From 21b088b5a99fe196eff1b7b6207abd4f2ce4fa04 Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Fri, 31 Jul 2026 09:27:57 -0400 Subject: [PATCH] redesign tasks --- tasks/yazi-remake/01-rearchitect-nav-model.md | 54 +++++++++++++++++ .../02-build-three-pane-layout-primitive.md | 56 +++++++++++++++++ .../03-convert-list-tabs-to-primitive.md | 58 ++++++++++++++++++ .../04-fit-search-and-player-panes.md | 52 ++++++++++++++++ tasks/yazi-remake/05-rebuild-shell-chrome.md | 56 +++++++++++++++++ tasks/yazi-remake/06-rewire-keybinds.md | 56 +++++++++++++++++ tasks/yazi-remake/07-verify-remake.md | 60 +++++++++++++++++++ tasks/yazi-remake/README.md | 40 +++++++++++++ 8 files changed, 432 insertions(+) create mode 100644 tasks/yazi-remake/01-rearchitect-nav-model.md create mode 100644 tasks/yazi-remake/02-build-three-pane-layout-primitive.md create mode 100644 tasks/yazi-remake/03-convert-list-tabs-to-primitive.md create mode 100644 tasks/yazi-remake/04-fit-search-and-player-panes.md create mode 100644 tasks/yazi-remake/05-rebuild-shell-chrome.md create mode 100644 tasks/yazi-remake/06-rewire-keybinds.md create mode 100644 tasks/yazi-remake/07-verify-remake.md create mode 100644 tasks/yazi-remake/README.md diff --git a/tasks/yazi-remake/01-rearchitect-nav-model.md b/tasks/yazi-remake/01-rearchitect-nav-model.md new file mode 100644 index 0000000..2c70a33 --- /dev/null +++ b/tasks/yazi-remake/01-rearchitect-nav-model.md @@ -0,0 +1,54 @@ +# 01. Rearchitect nav model — remove the sidebar pane + +meta: + id: yazi-remake-01 + feature: yazi-remake + priority: P1 + depends_on: [] + tags: [implementation, nav-model, tests-required] + +objective: + +- Remove the always-on `SIDEBAR_PANE` concept from the navigation context so `activeTab` is plain tab state (not a pane), establishing clean parent|current|preview semantics for the yazi remake. + +deliverables: + +- `src/context/NavigationContext.tsx` — delete `SIDEBAR_PANE` constant and all references; `activeTab` is no longer a pane +- `src/utils/navigation.ts` — update `TabPaneCount` semantics; depth-tabs = 1 focusable pane (current), the 3 visible columns are a render concern not 3 panes +- Updated header/comment block describing the parent|current|preview model +- `swipe()` / `popDepth()` reworked: depth-tabs `l`=drill (`open`), `h`=pop (noop at depth 0); fixed-pane tabs `h/l` move between parent/current/preview +- Tab-enter resets focus to `DEPTH_CENTER_PANE` (current pane), not a sidebar + +steps: + +- Audit every reference to `SIDEBAR_PANE` across the codebase (grep) +- In `NavigationContext.tsx`: delete the `SIDEBAR_PANE = -1` export and the `focusedIndex`/`setFocusedIndex` SIDEBAR_PANE branch added previously +- Set the initial `activePane` signal and the tab-switch createEffect to reset to `DEPTH_CENTER_PANE` (the current pane), not `SIDEBAR_PANE` +- Rework `swipe()` to clamp to `[0, paneCount-1]` for fixed-pane tabs (the sidebar is no longer in the chain); depth-tabs don't use `swipe` for drill/pop (that lives in Shell dispatch) +- In `utils/navigation.ts`: confirm `TabPaneCount` reflects focusable content panes only (depth-tabs = 1, Search = 3, Player = 1); update `PANE_RATIO` leave-behind note (ratio change happens in task 02) +- Update the file header comment block to describe parent|current|preview +- Run `lens_diagnostics` on the two files + +tests: + +- Unit: `focusedIndex(DEPTH_CENTER_PANE)` on a depth-tab returns the top frame's focus; `setFocusedIndex` writes to the top frame (Arrange a tab with a 2-frame stack, Act by calling setFocusedIndex, Assert topFrame.focus updated) +- Integration: tab-switch effect sets `activePane` to `DEPTH_CENTER_PANE` (not -1); `swipe(-1, 3)` on a fixed tab clamps to 0 not -1 +- e2e (harness): app boots with `nav.state.pane === 0` (current), not -1 + +acceptance_criteria: + +- No symbol `SIDEBAR_PANE` exists anywhere in `src/` +- Initial `activePane` === `DEPTH_CENTER_PANE` (0) +- Tab-enter sets `activePane` to `DEPTH_CENTER_PANE` +- `swipe()` lower bound is 0 (no `-1`) + +validation: + +- `grep -rn "SIDEBAR_PANE" src/` returns nothing +- `bun run build` passes +- `lens_diagnostics` paths=[`src/context/NavigationContext.tsx`,`src/utils/navigation.ts`] severity=error → 0 findings + +notes: + +- This task unblocks 03/04/05/06. It must not delete `DEPTH_CENTER_PANE` — that constant is generalised to "the current pane" and retained +- `SIDEBAR_ACTIONS` (added in Shell in a prior turn) is removed in task 06 (the keybind rewrite), not here — but Shell will temporarily fail to compile after this task until 05/06 land; that's expected and the build command ignores type errors, so gate success on grep + targeted diagnostics, not the full build diff --git a/tasks/yazi-remake/02-build-three-pane-layout-primitive.md b/tasks/yazi-remake/02-build-three-pane-layout-primitive.md new file mode 100644 index 0000000..d5fe2e7 --- /dev/null +++ b/tasks/yazi-remake/02-build-three-pane-layout-primitive.md @@ -0,0 +1,56 @@ +# 02. Build the reusable 3-pane layout primitive (1:3:3 ratio, stable parent slot) + +meta: + id: yazi-remake-02 + feature: yazi-remake + priority: P1 + depends_on: [] + tags: [implementation, layout, tests-required] + +objective: + +- Create one reusable `` primitive that renders three bordered columns (parent | current | preview) at a 1:3:3 grow ratio with a stable 1/7 parent slot even when blank, so every list tab shares an identical, layout-stable shell. + +deliverables: + +- `src/components/YaziPaneRow.tsx` — new component: props `parent`, `current`, `preview` (Solid JSX/accessors), `parentLabel`, `currentLabel`, `previewLabel`, `focused` (boolean, defaults to current) +- `src/utils/navigation.ts` — `PANE_RATIO` updated to `{ parent: 1, current: 3, preview: 3 }` (was `{ parent: 1, current: 4, preview: 3 }`) +- Each pane: bordered `scrollbox` + slim header label row (height=1) +- Parent pane keeps its 1/7 `flexGrow` slot even when empty (renders a muted placeholder, never `width:0`) +- Focus ring (border color = accent on current; muted `border` on parent & preview) + +steps: + +- Set `PANE_RATIO = { parent: 1, current: 3, preview: 3 }` in `utils/navigation.ts` +- Create `YaziPaneRow.tsx` exporting a component that lays out three `` columns in a row +- Each column: a height-1 header `` with the label text, then a `` rendering the passed children +- Thread a `theme` via `useTheme()` inside the primitive (don't require callers to pass colors) +- `focused` prop controls which column gets the accent border — default current; parent & preview always muted +- Ensure the parent column renders a muted placeholder box (e.g. a single `` or empty) when its children are null, but critically keeps `flexGrow={PANE_RATIO.parent}` so width never collapses +- Add a JSDoc header describing the yazi 1:3:3 contract +- Run diagnostics on the new file + +tests: + +- Unit: the primitive renders three boxes with flexGrow 1/3/3 regardless of null children (Arrange null parent, render, Assert three columns present with correct flexGrow) +- Integration: toggling `focused` swaps the accent border onto the requested column +- e2e (harness): a page using the primitive shows three equal-ratio columns with the parent column visibly non-zero width even when blank + +acceptance_criteria: + +- `PANE_RATIO` is `{ parent: 1, current: 3, preview: 3 }` +- `YaziPaneRow` accepts parent/current/preview children + labels + focused +- Parent column width never collapses to 0 (stable 1/7 slot) +- Only the focused column shows the accent border + +validation: + +- `grep -n "PANE_RATIO" src/utils/navigation.ts` shows the new 1:3:3 values +- `lens_diagnostics` paths=[`src/components/YaziPaneRow.tsx`,`src/utils/navigation.ts`] severity=error → 0 findings +- Harness: render a throwaway page using ``; confirm 3 columns at 1:3:3 via the frame + +notes: + +- Independent of task 01 (no nav-state dependency) — can be built in parallel +- Callers (tasks 03/04) pass their own parent/current/preview JSX; the primitive is purely structural +- opentui scrollbox: use `focused` only on the current pane so scroll focus follows the cursor diff --git a/tasks/yazi-remake/03-convert-list-tabs-to-primitive.md b/tasks/yazi-remake/03-convert-list-tabs-to-primitive.md new file mode 100644 index 0000000..686f9eb --- /dev/null +++ b/tasks/yazi-remake/03-convert-list-tabs-to-primitive.md @@ -0,0 +1,58 @@ +# 03. Convert Feed/MyShows/Discover/Settings to the shared parent|current|preview primitive + +meta: + id: yazi-remake-03 + feature: yazi-remake + priority: P2 + depends_on: [yazi-remake-01, yazi-remake-02] + tags: [implementation, pages, tests-required] + +objective: + +- Rewrite the four depth-stack list tabs to render through ``, with the previous-depth list now visible in the parent pane (blank at depth 0), the current-depth list in current, and the hovered item in preview — eliminating per-page bespoke 3-column JSX. + +deliverables: + +- `src/pages/Feed/FeedPage.tsx` — rewritten to use ``; parent = previous-depth list, current = current-depth list, preview = hovered item detail +- `src/pages/MyShows/MyShowsPage.tsx` — same conversion +- `src/pages/Discover/DiscoverPage.tsx` — same conversion +- `src/pages/Settings/SettingsPage.tsx` — same conversion (sections → items → editor) +- Each page's `nav.action` handler retained but only acts on the current pane +- All per-page bespoke row/flexbox 3-column JSX removed + +steps: + +- For each of the four pages, read the current implementation to extract the parent/current/preview content builders +- Wrap the page body in `` +- Parent pane: render the previous-depth frame's list (depth-1). At depth 0 the parent receives null/placeholder (the primitive keeps the slot) +- Current pane: the current-depth list, focusable, with `onMouseDown` row handlers calling `nav.setActivePane(DEPTH_CENTER_PANE)` + `nav.setDepthFocus(i, depth)` +- Preview pane: hovered-item detail derived from `focusedIndex(DEPTH_CENTER_PANE)` (unchanged logic, just relocated into the preview slot) +- Keep `pushDepth`/`popDepth` calls in the `open` action (drill) — behaviour unchanged, only layout changes +- Remove the old inline `` columns in favour of the primitive +- Verify each page's `nav.action` handler guards on `data.pane === DEPTH_CENTER_PANE && nav.activePane() === DEPTH_CENTER_PANE` + +tests: + +- Unit: each page's `open` action pushes a frame and the parent pane switches from blank to the previous list (Arrange depth 0, Act open, Assert stack length 2 and parent renders the old list) +- Integration: `h` (pop) returns parent to blank at depth 0; `l` (drill) populates parent with the previous list +- e2e (harness): Feed depth 0→1→2 shows parent blank → previous feeds list → previous episodes list; Settings sections→items→editor shows the chain in the parent pane + +acceptance_criteria: + +- All four pages render via `` (no bespoke 3-column JSX remains) +- Parent pane is blank at depth 0, populated at depth ≥ 1 +- Drilling (l/Enter) populates the parent with the previous-depth list +- Popping (h) empties the parent back to blank at depth 0 +- j/k move focus only within the current pane + +validation: + +- `grep -rn "YaziPaneRow" src/pages/` returns 4 files +- `lens_diagnostics` paths over the four page files severity=error → 0 findings +- Harness walk: `init` → navigate Feed → `l` (drill) → `l` (drill) → `h` (pop) → `h` (pop); confirm parent slot transitions blank→list→list→blank + +notes: + +- Depends on 01 (pane model) and 02 (the primitive) being merged +- The already-working `{(item) => (… item() …)}` accessor pattern for opentui `` callbacks must be preserved in preview panes +- Keep `LoadingIndicator` usages where they exist diff --git a/tasks/yazi-remake/04-fit-search-and-player-panes.md b/tasks/yazi-remake/04-fit-search-and-player-panes.md new file mode 100644 index 0000000..af55a15 --- /dev/null +++ b/tasks/yazi-remake/04-fit-search-and-player-panes.md @@ -0,0 +1,52 @@ +# 04. Fit Search and Player into the 3-pane (1:3:3) model + +meta: + id: yazi-remake-04 + feature: yazi-remake + priority: P2 + depends_on: [yazi-remake-01, yazi-remake-02] + tags: [implementation, pages, tests-required] + +objective: + +- Bring the two fixed-layout tabs (Search, Player) into the same 1:3:3 parent|current|preview shell, deciding per-page whether to adopt the depth-stack or stay fixed-3-pane, while applying the new ratios throughout. + +deliverables: + +- `src/pages/Search/SearchPage.tsx` — rendered through ``; parent = query input + recent-search history, current = results list, preview = focused-result detail +- `src/pages/Player/PlayerPage.tsx` — rendered through ``; current = now-playing transport, preview = episode description/notes, parent = blank placeholder (or compact episode list if available) +- Decision recorded in each file's header comment: depth-stack vs fixed-3-pane + +steps: + +- Read both pages to understand their current pane semantics +- Search: map INPUT→parent, RESULTS→current, DETAIL→preview inside ``. If the 1/7 parent slot is too narrow for the input box, widen parent for Search only by passing an override ratio OR move the query into current and results into parent — pick the option that keeps the input usable and document it +- Search: keep the `inputFocused` effect (Shell yields keys to `` when current-pane focus is on the query) — adapt to whichever pane the input lives in +- Player: single content pane; parent = blank/placeholder (1/7), current = transport + progress + controls (3/7), preview = episode art/description/notes (3/7). If no preview data, render a muted placeholder but keep the slot +- Confirm fixed-pane tab swipe (h/l between parent/current/preview) still routes correctly for Search +- Run diagnostics + +tests: + +- Unit: Search's `handleSubmit` swipes to the results pane and sets focus index 0 (Arrange empty results, Act submit, Assert activePane === results pane & focusedIndex 0) +- Integration: Player renders with parent blank and the transport in current +- e2e (harness): Search shows query | results | detail at 1:3:3; Player shows blank | transport | notes at 1:3:3 + +acceptance_criteria: + +- Both pages render via `` at 1:3:3 +- Search input remains typeable (Shell yields keys when the query pane is focused) +- Player's transport is in the current pane with focus +- No layout collapse: parent & preview keep their slots even if blank + +validation: + +- `grep -rn "YaziPaneRow" src/pages/Search src/pages/Player` returns 2 files +- `lens_diagnostics` paths over both files severity=error → 0 findings +- Harness: navigate to Search, type a query, press Enter, see results in current + detail in preview; navigate to Player, see transport + notes + +notes: + +- Depends on 01 (pane model — though Search is fixed-pane, the model cleanup affects `swipe` bounds) and 02 (the primitive) +- If Search input at 1/7 is genuinely too tight (~14 cols at 100w), prefer moving the query into the current pane for Search only and the results into parent — but confirm width with the harness before committing +- Player is single-content; the 1:3:3 with blanks is mostly cosmetic but keeps the layout globally consistent diff --git a/tasks/yazi-remake/05-rebuild-shell-chrome.md b/tasks/yazi-remake/05-rebuild-shell-chrome.md new file mode 100644 index 0000000..0399d4e --- /dev/null +++ b/tasks/yazi-remake/05-rebuild-shell-chrome.md @@ -0,0 +1,56 @@ +# 05. Rebuild Shell chrome — drop sidebar, add yazi bottom status/tab bar + +meta: + id: yazi-remake-05 + feature: yazi-remake + priority: P1 + depends_on: [yazi-remake-01] + tags: [implementation, shell-chrome, tests-required] + +objective: + +- Remove the always-on left tab sidebar entirely and replace it with a full-width page area above a slim yazi-style bottom bar that surfaces the active tab, depth/counts, selection, now-playing, and a discoverable tab strip. + +deliverables: + +- `src/components/Shell.tsx` — sidebar JSX deleted; render `LayerGraph[tab]()` full-width + a rebuilt bottom status/command bar +- Bottom bar (normal mode): mode label, `TAB_LABEL[tab] · depth N · i/len` (or `pane i/n` for fixed tabs), selection count `●N`, now-playing `♪ title`, pending-keybind hint, and a compact tab strip `[1]Feed [2]MyShows …` with the active tab marked +- Bottom bar (command mode): `:` prompt + buffer + error (unchanged, just relocated if needed) +- Help overlay kept; now-playing relocated from the old sidebar footer into the status bar + +steps: + +- Read `Shell.tsx` and delete the entire left tab sidebar `…` block +- Replace the middle row with a single full-width `{LayerGraph[nav.activeTab()]()}` +- Rebuild the bottom bar: a height-1 `` with the fragments described above +- Tab strip: render `Object.values(TABS)` filtered to numbers; for each tab show `[N] Label` with the active tab inverted/highlighted (accent bg or `≡` marker) +- Status fragment: `nav.activePane() === DEPTH_CENTER_PANE ? (isDepthTab ? \`depth ${currentDepth()}\` : \`pane ${activePane()+1}/${count}\`) : 'tabs'` — but since the sidebar is gone, default to the depth/pane string (focus starts on current) +- Relocate `nowPlaying()` text from the sidebar footer into the bottom bar +- Keep `runCommand`, `handleCommandKey`, the help overlay, and `playEpisodeAndSwitch` untouched +- Run diagnostics + +tests: + +- Unit: `nowPlaying()` formats `♪ ` (Arrange a current episode, Assert the string) +- Integration: switching tabs updates the tab strip's active marker and the status tab label +- e2e (harness): `init` shows no left sidebar, a full-width page, and a bottom bar containing the tab strip + `Feed · depth 0`; cycling tabs moves the strip's active marker + +acceptance_criteria: + +- No `width={14}` sidebar `` remains in `Shell.tsx` +- The active page fills the full content width +- The bottom bar shows the active tab, depth, counts, selection, now-playing, and the tab strip +- The active tab is visually marked in the strip + +validation: + +- `grep -n "width={14}" src/components/Shell.tsx` returns nothing +- `grep -n "LayerGraph" src/components/Shell.tsx` shows the full-width render +- `lens_diagnostics` paths=[`src/components/Shell.tsx`] severity=error → 0 findings +- Harness: `init` frame has no sidebar column and shows the tab strip in the last row + +notes: + +- Depends on 01 (the pane model: focus starts on current, so the status fragment no longer needs the `SIDEBAR_PANE` branch) +- Task 06 rewrites the dispatch keybinds in this same file; do the chrome here and leave the dispatch `SIDEBAR_ACTIONS` branch for 06 to remove (or remove it here if 01 already deleted the constant — coordinate with 01) +- `playEpisodeAndSwitch` and the command bar must keep working diff --git a/tasks/yazi-remake/06-rewire-keybinds.md b/tasks/yazi-remake/06-rewire-keybinds.md new file mode 100644 index 0000000..5281595 --- /dev/null +++ b/tasks/yazi-remake/06-rewire-keybinds.md @@ -0,0 +1,56 @@ +# 06. Rewire keybinds — h/l drill+pop, digits switch tabs, focus starts on current + +meta: + id: yazi-remake-06 + feature: yazi-remake + priority: P1 + depends_on: [yazi-remake-01, yazi-remake-05] + tags: [implementation, keybinds, tests-required] + +objective: + +- Rewire the Shell dispatch so the sidebar's special-cased j/k branch is gone, h/l drill/pop on depth-tabs and swipe on fixed tabs, digit keys + `[ ]` are the sole tab switcher, and app focus starts on the current pane. + +deliverables: + +- `src/components/Shell.tsx` (dispatch) — `SIDEBAR_ACTIONS` set + the `if (nav.activePane() === SIDEBAR_PANE)` branch deleted +- `h`/`l` unified: depth-tabs `l`=current-drills (`open` emit), `h`=current-pops (noop at depth 0); fixed-pane tabs `h/l`=`swipe(∓1, count)` +- `1`-`6` / `tab-goto-*`, `tab-next`/`tab-prev` (`[`/`]`) — the only tab switchers +- Initial focus + tab-enter land on `DEPTH_CENTER_PANE` +- `keybinds.jsonc` reviewed (update labels/help only if needed) + +steps: + +- Read the current `dispatch()` (post task 01 it references a deleted `SIDEBAR_PANE` — fix the compile here) +- Remove the `SIDEBAR_ACTIONS` constant and its branch +- In the `default` case, implement: digit/tab-goto → `setActiveTab`; `swipe-prev` → (depth-tab & current & depth>0) `popDepth` else (depth-tab & current & depth==0) noop else `swipe(-1, count)`; `swipe-next` → (depth-tab & current) emit `open` else `swipe(1, count)` +- Move/list actions (`move-down/up`, `jump-*`, `page-*`, `goto-top/bottom`) flow to `PAGE_ACTIONS` → `emit("nav.action")` for the current pane only +- Confirm `escape`/`command`/`visual-mode`/`toggle-select`/audio/global branches unchanged +- Verify the app boot path sets focus to current (task 01 set the signal; confirm dispatch doesn't override) +- Run diagnostics + harness key sequence + +tests: + +- Unit: `dispatch("move-down")` on a depth-tab current pane emits `nav.action {action:"move-down"}` (Arrange current pane, Act, Assert emit) +- Integration: `dispatch("swipe-next")` on a depth-tab at depth 0 emits `open` (drill); `dispatch("swipe-prev")` at depth 1 pops to depth 0; at depth 0 `swipe-prev` is a noop +- e2e (harness): `l` drills (depth 0→1, parent populates), `h` pops (1→0, parent blanks), `1`/`2`/`3` switch tabs, `j`/`k` move the current list cursor without changing depth + +acceptance_criteria: + +- No `SIDEBAR_PANE` or `SIDEBAR_ACTIONS` references in `Shell.tsx` +- `h` at depth 0 is a noop (does not error, does not change pane) +- `l` at current on a depth-tab drills (depth+1) +- Digit keys switch tabs; focus lands on current pane +- `j`/`k` move within current only + +validation: + +- `grep -n "SIDEBAR" src/components/Shell.tsx` returns nothing +- `lens_diagnostics` paths=[`src/components/Shell.tsx`] severity=error → 0 findings +- Harness: `init` (focus on current) → `l` (depth 1, parent filled) → `l` (depth 2) → `h` (depth 1) → `h` (depth 0, parent blank) → `3` (Discover tab, focus on current) → `j`/`k` move + +notes: + +- Depends on 01 (pane model: `swipe` bounds, no SIDEBAR) and 05 (dispatch lives in the rebuilt Shell) +- If `keybinds.jsonc` has a `tab-next`/`tab-prev` mapping conflict, resolve here +- The noop `h` at depth 0 should feel inert (yazi: at root, `h` does nothing) diff --git a/tasks/yazi-remake/07-verify-remake.md b/tasks/yazi-remake/07-verify-remake.md new file mode 100644 index 0000000..838bc86 --- /dev/null +++ b/tasks/yazi-remake/07-verify-remake.md @@ -0,0 +1,60 @@ +# 07. Verify the remake — build + diagnostics + harness walk-through + +meta: + id: yazi-remake-07 + feature: yazi-remake + priority: P1 + depends_on: [yazi-remake-03, yazi-remake-04, yazi-remake-05, yazi-remake-06] + tags: [verification, tests-required] + +objective: + +- Confirm the yazi remake meets every exit criterion via a clean build, zero diagnostics, and a full harness walk-through of every tab and depth. + +deliverables: + +- A passing `bun run build` +- `lens_diagnostics mode=all` with zero errors across edited files +- Harness frames + state proving the parent|current|preview 1:3:3 layout, drill/pop behaviour, tab switching, and status bar across all six tabs + +steps: + +- Run `bun run build` — expect "Build complete" +- Run `lens_diagnostics mode=all severity=error` — expect 0 findings across all session-edited files +- Run the drive harness (`scripts/tui-harness.tsx`) walk-through: + - `init` → confirm no sidebar, 3 columns at 1:3:3, focus on current, bottom tab strip visible + - Feed: `l` (depth 0→1, parent fills) → `l` (1→2) → `h` (2→1) → `h` (1→0, parent blanks) ; `j`/`k` move current + - `2` → MyShows: drill show→episodes, parent reflects + - `3` → Discover: category→results, parent shows categories + - `6` → Settings: sections→items→editor, parent shows the previous list at each depth + - `4` → Search: query|results|detail at 1:3:3; type + Enter works + - `5` → Player: blank|transport|notes at 1:3:3 +- Capture the status bar content (active tab + depth + counts + now-playing + tab strip) from a representative frame + +tests: + +- Build: `bun run build` exits 0 with "Build complete" +- Diagnostics: `lens_diagnostics` mode=all → 0 errors +- Harness (integration/e2e): the walk-through above produces the expected frames & state (parent blank at depth 0, populates on drill, blanks on pop; digits switch tabs; h noop at depth 0) + +acceptance_criteria: + +- `bun run build` passes +- `lens_diagnostics` mode=all reports zero errors +- All six tabs render 3 stable columns at 1:3:3 +- Parent pane is blank at depth 0; drill fills it with the previous-depth list; pop empties it +- `h` is a noop at depth 0; `l` drills; `1-6`/`[`/`]` switch tabs; `j/k` move current only +- No sidebar; focus starts on current; bottom bar shows active tab + depth + counts + tab strip + +validation: + +- `bun run build 2>&1 | tail -3` → "Build complete" +- `lens_diagnostics` mode=all severity=error → "No error issues…" +- Harness `state nav` after `init` shows `pane === 0` (current), not -1 +- Harness frames for Feed depth 0/1/2 show the parent slot transition blank→list→list + +notes: + +- This is the gate for the whole feature — do not mark done if any criterion fails; open a blocker task instead +- If the harness reveals a visual regression (e.g. parent collapses, ratios off), file it against the responsible task (02 or 03) rather than patching here +- Save a representative `.harness/last-frame.txt` snapshot if a visual reference is useful for future sessions diff --git a/tasks/yazi-remake/README.md b/tasks/yazi-remake/README.md new file mode 100644 index 0000000..e2cb042 --- /dev/null +++ b/tasks/yazi-remake/README.md @@ -0,0 +1,40 @@ +# Yazi UI Remake + +Objective: Remake the PodTUI shell into a yazi-pure parent|current|preview 3-pane layout (1:3:3 ratio) with a bottom tab strip and no always-on sidebar. + +Status legend: [ ] todo, [~] in-progress, [x] done + +## Tasks + +- [ ] 01 — rearchitect-nav-model → `01-rearchitect-nav-model.md` +- [ ] 02 — build-three-pane-layout-primitive → `02-build-three-pane-layout-primitive.md` +- [ ] 03 — convert-list-tabs-to-primitive → `03-convert-list-tabs-to-primitive.md` +- [ ] 04 — fit-search-and-player-panes → `04-fit-search-and-player-panes.md` +- [ ] 05 — rebuild-shell-chrome → `05-rebuild-shell-chrome.md` +- [ ] 06 — rewire-keybinds → `06-rewire-keybinds.md` +- [ ] 07 — verify-remake → `07-verify-remake.md` + +## Dependencies + +- 03 depends on 01 +- 03 depends on 02 +- 04 depends on 01 +- 04 depends on 02 +- 05 depends on 01 +- 06 depends on 01 +- 06 depends on 05 +- 07 depends on 03 +- 07 depends on 04 +- 07 depends on 05 +- 07 depends on 06 + +## Exit criteria + +- The feature is complete when the left tab sidebar is gone; tabs switch only via digit keys `1-6` / `[ ]` and a bottom tab strip +- All tabs render three stable columns at 1/7 : 3/7 : 3/7 (parent | current | preview) +- The parent pane renders the previous-depth list and is blank (but keeps its 1/7 slot) at depth 0 +- `h`/`l` drill (push) and pop depths on list tabs; `h` is a noop at depth 0 +- `j`/`k` move within the current pane only; focus starts on the current pane +- Feed depth 0→1→2, MyShows, Discover, Settings (sections→items→editor), Search, and Player all render correctly via the drive harness +- `bun run build` passes and `lens_diagnostics` (mode=all) reports zero errors +- The bottom status bar shows active tab + depth + counts, selection count, now-playing, and the tab strip