diff --git a/ui-opentui/src/logic/completionMenu.ts b/ui-opentui/src/logic/completionMenu.ts index a2f65567417..9b92a27d65c 100644 --- a/ui-opentui/src/logic/completionMenu.ts +++ b/ui-opentui/src/logic/completionMenu.ts @@ -47,12 +47,17 @@ function clampSelected(ctx: MenuKeyContext): number { * Route one key press against the open menu. `modified` is Ctrl/Alt/Option — * modified arrows/Enter never belong to the menu (Tab/Esc keep their * pre-existing modifier-blind accept/dismiss semantics). + * + * ANY open menu owns plain arrows/Enter (glitch, 2026-06-10): @-path and + * arg menus navigate exactly like the slash menu — standard editor- + * autocomplete behavior; Esc dismisses to hand the cursor keys back. + * (`ctx.slashMenu` still feeds the hint text + suggestion rows.) */ export function routeMenuKey(name: string, modified: boolean, ctx: MenuKeyContext): MenuKeyAction { if (ctx.count <= 0) return PASS if (name === 'tab') return { index: clampSelected(ctx), kind: 'accept' } if (name === 'escape') return { kind: 'dismiss' } - if (modified || !ctx.slashMenu) return PASS + if (modified) return PASS const sel = clampSelected(ctx) if (name === 'up') return { kind: 'move', selected: (sel - 1 + ctx.count) % ctx.count } if (name === 'down') return { kind: 'move', selected: (sel + 1) % ctx.count } diff --git a/ui-opentui/src/test/slashMenu.test.tsx b/ui-opentui/src/test/slashMenu.test.tsx index 2289ee6a7aa..64d9ea523b8 100644 --- a/ui-opentui/src/test/slashMenu.test.tsx +++ b/ui-opentui/src/test/slashMenu.test.tsx @@ -47,9 +47,17 @@ describe('routeMenuKey — key-routing precedence table', () => { ['Esc dismisses', 'escape', false, ctx({ selected: 2 }), { kind: 'dismiss' }], // NOT the slash menu (path/@-mention dropdown): arrows + Enter keep their // existing meanings (history / cursor / textarea submit) … - ['Down on a path menu passes', 'down', false, ctx({ slashMenu: false }), { kind: 'pass' }], - ['Up on a path menu passes', 'up', false, ctx({ slashMenu: false }), { kind: 'pass' }], - ['Enter on a path menu passes (submits as today)', 'return', false, ctx({ slashMenu: false }), { kind: 'pass' }], + // glitch 2026-06-10: ANY open menu owns plain arrows/Enter (path/arg menus + // navigate like the slash menu; Esc hands the cursor keys back). + ['Down on a path menu moves', 'down', false, ctx({ slashMenu: false }), { kind: 'move', selected: 1 }], + ['Up on a path menu moves (wraps)', 'up', false, ctx({ slashMenu: false }), { kind: 'move', selected: 2 }], + [ + 'Enter on a path menu accepts the highlighted item', + 'return', + false, + ctx({ slashMenu: false }), + { index: 0, kind: 'accept' } + ], // … but Tab/Esc keep working on ANY menu (pre-Epic-8 semantics) ['Tab on a path menu still accepts', 'tab', false, ctx({ slashMenu: false }), { index: 0, kind: 'accept' }], ['Esc on a path menu still dismisses', 'escape', false, ctx({ slashMenu: false }), { kind: 'dismiss' }], diff --git a/ui-opentui/src/view/composer.tsx b/ui-opentui/src/view/composer.tsx index fc5b2cc4dbd..32d7f889e8b 100644 --- a/ui-opentui/src/view/composer.tsx +++ b/ui-opentui/src/view/composer.tsx @@ -234,7 +234,6 @@ export function Composer(props: { // Whether the composer text starts with `/` (slash menu vs path menu) — a // signal so the dropdown hint stays reactive; the key handler re-checks // `ta.plainText` directly. - const [slashText, setSlashText] = createSignal(false) /** Replace the textarea content and park the cursor at the end (history recall). */ const setBuffer = (text: string) => { @@ -416,7 +415,7 @@ export function Composer(props: { )} - {slashText() ? '↑/↓ select · Enter/Tab accept · Esc dismiss' : 'Tab complete · Esc dismiss'} + {'↑/↓ select · Enter/Tab accept · Esc dismiss'} @@ -460,7 +459,6 @@ export function Composer(props: { }} onContentChange={() => { const text = ta?.plainText ?? '' - setSlashText(text.startsWith('/')) setBufText(text) // drives the token analysis (highlight + suggestion) props.onType?.(text) }}