Context
Activity-bar keyboard navigation (the j/k selection ring + l/Enter activate + h/Esc dismiss) is being lifted out of vimcode and into quadraui's AppShell — see quadraui#386. vimcode currently owns this behavior with hand-rolled state because the AppShell API didn't exist when it was written. Once #386 lands, vimcode should consume the shared implementation so the logic lives in one place.
What vimcode hand-rolls today
kbd_sel + manual ActivityBar primitive construction — src/tui_main/panels.rs:32, :93 (bottom Settings), :101.
- Toolbar nav state —
toolbar_focused: bool, toolbar_selected: u16 in TuiSidebar (src/tui_main/mod.rs:333-335).
- The cursor arithmetic — the bespoke
0→1…→6→8…→max_ext→7(settings) up/down sequencing in src/tui_main/mod.rs:1770-1804, plus the l/Enter/Esc activate-dismiss handling at :1805-1865.
That index arithmetic (settings pinned last, extension panels spliced at 8+) is exactly the "cursor spanning top_items then bottom_items as one sequence" that quadraui#386 standardizes.
Goal
Retire vimcode's bespoke activity-bar nav state/arithmetic by delegating to AppShell's cursor from quadraui#386, while keeping vimcode's manual activity-bar rendering (vimcode is a manual-layout app — it does NOT use the ShellApp chrome renderer, so this is not a request to adopt that).
Concretely after #386:
- Replace
toolbar_selected index math (mod.rs:1770-1804) with app_shell.activity_select_next() / activity_select_prev().
- Replace
kbd_sel(idx) (panels.rs:32) with app_shell.activity_selected_id() when building each ActivityItem's is_keyboard_selected — vimcode still constructs the primitive, but reads the selected id from the shared cursor.
- Route the
l/Enter activation through activity_activate_selected() (emits the same panel-change vimcode already handles).
- Keep
toolbar_focused as the local "is the bar focused" gate, or fold into set_activity_keyboard_focused(true) if vimcode wants the backend ActivityBarEvent::KeyPressed routing too.
Open scope question
vimcode's activity bar includes dynamic extension panels (indices 8+) interleaved before Settings. quadraui#386's cursor must handle a variable panel count (top items + dynamic + bottom-pinned Settings) for vimcode to fully delegate; if #386 only covers static panels, vimcode keeps local handling for the ext-panel range. Confirm #386's cursor supports dynamic panels, or scope this migration to the static panels only.
Acceptance
- Behavior unchanged:
j/k move the ring (incl. settings-last ordering and ext-panel splice), l/Enter activate, h/Esc dismiss — verified against current behavior.
- The bespoke index arithmetic in
mod.rs:1770-1804 and kbd_sel are gone (or reduced to the ext-panel range only, per the scope question).
- Existing toolbar-nav tests still pass.
Blocked by
- quadraui#386 (lift activity-bar keyboard nav into
AppShell). File/land that first.
Unblocked 2026-08-26 — quadraui#386 has LANDED
The "Blocked by" section above is stale. quadraui#386 (AppShell: keyboard-selectable
activity bar) is CLOSED. This is now a pure vimcode-side adoption — the exact
"infra landed but the adoption issue never got picked up" shape GOALS.md exists to catch.
The open scope question above still stands and is the first thing to settle in-session:
confirm whether #386s cursor handles a variable panel count (top items + dynamic
ext panels at 8+ + bottom-pinned Settings). If it only covers static panels, scope this to
the static range and say so in the PR rather than half-migrating.
Note the TUI has since moved onto run_with_shell (#634), so the line references below
point into the pre-cutover layout — re-locate toolbar_selected / kbd_sel in the current
src/tui_main/shell_app.rs + panels.rs rather than trusting the old mod.rs:1770-1804.
Files
src/tui_main/shell_app.rs
src/tui_main/panels.rs
src/tui_main/mod.rs
Out of scope
- Adopting the
ShellApp chrome renderer for the activity bar. vimcode still constructs
the primitive itself; this issue moves only the nav state/arithmetic.
Context
Activity-bar keyboard navigation (the
j/kselection ring +l/Enteractivate +h/Escdismiss) is being lifted out of vimcode and into quadraui'sAppShell— see quadraui#386. vimcode currently owns this behavior with hand-rolled state because theAppShellAPI didn't exist when it was written. Once #386 lands, vimcode should consume the shared implementation so the logic lives in one place.What vimcode hand-rolls today
kbd_sel+ manualActivityBarprimitive construction —src/tui_main/panels.rs:32,:93(bottom Settings),:101.toolbar_focused: bool,toolbar_selected: u16inTuiSidebar(src/tui_main/mod.rs:333-335).0→1…→6→8…→max_ext→7(settings)up/down sequencing insrc/tui_main/mod.rs:1770-1804, plus thel/Enter/Escactivate-dismiss handling at:1805-1865.That index arithmetic (settings pinned last, extension panels spliced at 8+) is exactly the "cursor spanning top_items then bottom_items as one sequence" that quadraui#386 standardizes.
Goal
Retire vimcode's bespoke activity-bar nav state/arithmetic by delegating to
AppShell's cursor from quadraui#386, while keeping vimcode's manual activity-bar rendering (vimcode is a manual-layout app — it does NOT use theShellAppchrome renderer, so this is not a request to adopt that).Concretely after #386:
toolbar_selectedindex math (mod.rs:1770-1804) withapp_shell.activity_select_next()/activity_select_prev().kbd_sel(idx)(panels.rs:32) withapp_shell.activity_selected_id()when building eachActivityItem'sis_keyboard_selected— vimcode still constructs the primitive, but reads the selected id from the shared cursor.l/Enteractivation throughactivity_activate_selected()(emits the same panel-change vimcode already handles).toolbar_focusedas the local "is the bar focused" gate, or fold intoset_activity_keyboard_focused(true)if vimcode wants the backendActivityBarEvent::KeyPressedrouting too.Open scope question
vimcode's activity bar includes dynamic extension panels (indices 8+) interleaved before Settings. quadraui#386's cursor must handle a variable panel count (top items + dynamic + bottom-pinned Settings) for vimcode to fully delegate; if #386 only covers static panels, vimcode keeps local handling for the ext-panel range. Confirm #386's cursor supports dynamic panels, or scope this migration to the static panels only.
Acceptance
j/kmove the ring (incl. settings-last ordering and ext-panel splice),l/Enteractivate,h/Escdismiss — verified against current behavior.mod.rs:1770-1804andkbd_selare gone (or reduced to the ext-panel range only, per the scope question).Blocked by
AppShell). File/land that first.Unblocked 2026-08-26 — quadraui#386 has LANDED
The "Blocked by" section above is stale.
quadraui#386(AppShell: keyboard-selectableactivity bar) is CLOSED. This is now a pure vimcode-side adoption — the exact
"infra landed but the adoption issue never got picked up" shape
GOALS.mdexists to catch.The open scope question above still stands and is the first thing to settle in-session:
confirm whether #386s cursor handles a variable panel count (top items + dynamic
ext panels at 8+ + bottom-pinned Settings). If it only covers static panels, scope this to
the static range and say so in the PR rather than half-migrating.
Note the TUI has since moved onto
run_with_shell(#634), so the line references belowpoint into the pre-cutover layout — re-locate
toolbar_selected/kbd_selin the currentsrc/tui_main/shell_app.rs+panels.rsrather than trusting the oldmod.rs:1770-1804.Files
src/tui_main/shell_app.rssrc/tui_main/panels.rssrc/tui_main/mod.rsOut of scope
ShellAppchrome renderer for the activity bar. vimcode still constructsthe primitive itself; this issue moves only the nav state/arithmetic.