ui cleanup

This commit is contained in:
2026-08-07 13:38:32 -04:00
parent c8d29ed59d
commit 85cb9fba26
24 changed files with 1839 additions and 1375 deletions

View File

@@ -78,7 +78,7 @@ function mpvSocketPath(): string {
// ── mpv Backend ──────────────────────────────────────────────────────
// Uses JSON IPC over a Unix socket for full bidirectional control.
class MpvBackend implements AudioBackend {
export class MpvBackend implements AudioBackend {
readonly name: BackendName = "mpv";
private proc: ReturnType<typeof Bun.spawn> | null = null;
private socketPath = mpvSocketPath();

View File

@@ -22,12 +22,10 @@
* • digit keys `1`-`6` / `tab-goto-*`, `tab-next` (`]`), `tab-prev` (`[`)
* switch tabs; focus keeps its context (root iff already at the root,
* otherwise the content `DEPTH_CENTER_PANE`).
* • `h`/`l` are `swipe-prev`/`swipe-next` in content:
* - depth-tabs, current pane: `l` drills (`open` emit), `h` pops a depth
* when depth > 0; at depth 0 `h` returns to the tab root (`backToTabRoot`),
* where the tab becomes CURRENT again.
* - fixed-pane tabs (Search/Player, special): `swipe(±1, count)` clamped to
* [1, paneCount]; `h` on the first content pane stays (no tab overflow).
* • `h`/`l` are `swipe-prev`/`swipe-next` in content (every tab is a
* depth-tab): `l` at the current pane drills in (emits `open`); `h` pops
* a depth when depth > 0; at depth 0 `h` returns to the tab root
* (`backToTabRoot`), where the tab becomes CURRENT again.
* • list/pane actions (`j`/`k`, `gg`/`G`, page-up/down, …) flow to
* `PAGE_ACTIONS` → `emit("nav.action")` for the current active content pane.
* • `escape`/`command`/`visual-mode`/`toggle-select`/audio/global branches
@@ -36,7 +34,7 @@
import type { KeybindActionName } from "@/context/KeybindContext";
import type { NavigationState, DepthFrame } from "@/context/navigation-store";
import { NavMode, DEPTH_CENTER_PANE } from "@/context/navigation-store";
import { TABS, TabsCount, TabPaneCount } from "@/utils/navigation";
import { TABS, TabsCount } from "@/utils/navigation";
import { emit } from "@/utils/event-bus";
// Re-export NavMode + DEPTH_CENTER_PANE so Shell keeps importing them from here.
@@ -182,33 +180,23 @@ export function createDispatcher(deps: DispatcherDeps) {
if (action === "swipe-prev") break;
}
// ── pane swipe / depth nav ──
// Depth-tabs: l at the center drills in (emits `open`); h at the
// center pops a depth; at depth 0 h returns to the tab root (the tab
// becomes CURRENT again). Fixed-pane tabs (Search/Player, special):
// h/l swipe across [1, paneCount]; h on the first pane stays.
// Every tab is a depth-tab: `l` at the center drills in (emits `open`);
// `h` at the center pops a depth when depth > 0, and at depth 0 returns
// to the tab root (the tab becomes CURRENT again).
if (action === "swipe-prev") {
evt.preventDefault();
if (nav.isDepthTab() && nav.activePane() === DEPTH_CENTER_PANE) {
if (nav.currentDepth() > 0) nav.popDepth();
else nav.backToTabRoot(); // depth 0 → tab root
} else if (nav.activePane() > DEPTH_CENTER_PANE) {
nav.swipe(-1, TabPaneCount[tab]); // content 1..n
}
// fixed first content pane (1): no-op, stays (special tabs)
if (nav.currentDepth() > 0) nav.popDepth();
else nav.backToTabRoot(); // depth 0 → tab root
break;
}
if (action === "swipe-next") {
evt.preventDefault();
if (nav.isDepthTab() && nav.activePane() === DEPTH_CENTER_PANE) {
emit("nav.action", {
action: "open",
tab,
pane: DEPTH_CENTER_PANE,
mode: nav.mode(),
});
} else {
nav.swipe(1, TabPaneCount[tab]);
}
emit("nav.action", {
action: "open",
tab,
pane: DEPTH_CENTER_PANE,
mode: nav.mode(),
});
break;
}
// ── audio transport (global) ──

View File

@@ -14,12 +14,15 @@ export enum TABS {
export const TabsCount = 6;
/** Tabs that use the yazi depth-stack model (prev | current | preview
* columns, infinite drill via push/pop). Search and Player keep the legacy
* fixed-pane model. */
* columns, infinite drill via push/pop). Search drills query→results, and
* Player drills into its single now-playing pane under the tab list (the
* parent=/tabs, current=player, preview hidden). */
export const DEPTH_TABS: ReadonlySet<TABS> = new Set([
TABS.FEED,
TABS.MYSHOWS,
TABS.DISCOVER,
TABS.SEARCH,
TABS.PLAYER,
TABS.SETTINGS,
]);
@@ -35,6 +38,10 @@ export function rootFrameFor(
return { kind: "shows", focus: 0 };
case TABS.DISCOVER:
return { kind: "discover:categories", focus: 0 };
case TABS.SEARCH:
return { kind: "search:query", focus: 0 };
case TABS.PLAYER:
return { kind: "player:nowplaying", focus: 0 };
case TABS.SETTINGS:
return { kind: "settings:sections", focus: 0 };
default:
@@ -62,16 +69,15 @@ export const PANE_RATIO = {
// Number of *focusable* content panes per tab. The three visible columns
// (parent | current | preview) are a *render* concern, NOT three panes — for
// depth-tabs only the current column (index 0) is focusable, so this is 1.
// Depth-tabs (Feed/MyShows/Discover/Settings) drill with `l` (push) and pop
// with `h` (noop at depth 0) via the Shell dispatch — they never call swipe.
// Search keeps its 3 fixed focusable panes; Player is single-pane. Defined
// here (after TABS) to avoid re-introducing the old NavigationContext
// top-level-init circular deadlock.
// Every tab is now a depth-tab: each drills with `l` (push) and pops with `h`
// (returns to the tab root at depth 0) via the Shell dispatch. Defined here
// (after TABS) to avoid re-introducing the old NavigationContext top-level-
// init circular deadlock.
export const TabPaneCount: Record<TABS, number> = {
[TABS.FEED]: 1, // depth: feeds → episodes → preview
[TABS.MYSHOWS]: 1, // depth: shows → episodes → preview
[TABS.DISCOVER]: 1, // depth: categories → results → preview
[TABS.SEARCH]: 3, // fixed: query | results | detail
[TABS.PLAYER]: 1, // single pane
[TABS.SEARCH]: 1, // depth: query results, preview=detail
[TABS.PLAYER]: 1, // depth: now-playing (2-pane, no preview)
[TABS.SETTINGS]: 1, // depth: sections → items → editor
};