From 804474fcbbf831fa5bae90fb5dd1ed34c31b483c Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Sat, 4 Apr 2026 12:02:20 +0200 Subject: [PATCH] fix(tui): Space to proceed, Enter to toggle options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Swap keyboard roles so Space confirms/advances and Enter toggles multi-select rows or moves focus on continue/cancel prompts—closer to common form UX for first-time wizard users. - PickerMenu, GroupedPickerMenu, ConfirmationInput - Labels and playground copy; e2e MCP step uses Space to confirm - Update ink-tui PRIMITIVES reference Made-with: Cursor --- .claude/skills/ink-tui/references/PRIMITIVES.md | 6 +++--- e2e-tests/utils/framework-test-utils.ts | 4 ++-- src/ui/tui/playground/demos/HealthCheckDemo.tsx | 2 +- src/ui/tui/playground/demos/ModalDemo.tsx | 2 +- src/ui/tui/playground/demos/WelcomeDemo.tsx | 6 +++--- src/ui/tui/primitives/ConfirmationInput.tsx | 8 ++++---- src/ui/tui/primitives/GroupedPickerMenu.tsx | 8 ++++---- src/ui/tui/primitives/PickerMenu.tsx | 10 +++++----- src/ui/tui/screens/PortConflictScreen.tsx | 2 +- src/ui/tui/screens/SettingsOverrideScreen.tsx | 2 +- src/ui/tui/screens/SkillsScreen.tsx | 2 +- src/ui/tui/screens/health/HealthCheckScreen.tsx | 2 +- 12 files changed, 27 insertions(+), 27 deletions(-) diff --git a/.claude/skills/ink-tui/references/PRIMITIVES.md b/.claude/skills/ink-tui/references/PRIMITIVES.md index eaa43e6e..83793dc3 100644 --- a/.claude/skills/ink-tui/references/PRIMITIVES.md +++ b/.claude/skills/ink-tui/references/PRIMITIVES.md @@ -39,13 +39,13 @@ Tabs array can be built conditionally — see `RunScreen.tsx` for an example of Single and multi select. Fully custom renderers — does NOT use `@inkjs/ui` Select/MultiSelect. -- **Single select**: `▸` triangle cursor on focused item, enter selects -- **Multi select** (`mode="multi"`): `◻`/`◼` toggles with space, enter submits +- **Single select**: `▸` triangle cursor on focused item, space selects +- **Multi select** (`mode="multi"`): `◻`/`◼` toggles with enter, space submits ### ConfirmationInput `src/ui/tui/primitives/ConfirmationInput.tsx` -Continue/cancel prompt with two bordered button boxes. Left/right arrows switch focus, enter activates focused, escape always cancels. +Continue/cancel prompt with two bordered button boxes. Left/right arrows or enter switch focus, space activates focused, escape always cancels. ### DissolveTransition `src/ui/tui/primitives/DissolveTransition.tsx` diff --git a/e2e-tests/utils/framework-test-utils.ts b/e2e-tests/utils/framework-test-utils.ts index 167568ad..44d57778 100644 --- a/e2e-tests/utils/framework-test-utils.ts +++ b/e2e-tests/utils/framework-test-utils.ts @@ -5,7 +5,7 @@ export const DEFAULT_WIZARD_STEPS: WizardStep[] = [ // { // name: 'uncommitted', // waitFor: 'You have uncommitted or untracked files in your repo:', - // response: [KEYS.DOWN, KEYS.ENTER], + // response: [KEYS.DOWN, KEYS.SPACE], // timeout: 2000, // optional: true, // }, @@ -13,7 +13,7 @@ export const DEFAULT_WIZARD_STEPS: WizardStep[] = [ name: 'mcp', waitFor: 'Would you like to install the PostHog MCP server to use PostHog in your editor?', - response: [KEYS.DOWN, KEYS.ENTER], + response: [KEYS.DOWN, KEYS.SPACE], responseWaitFor: 'No', }, { diff --git a/src/ui/tui/playground/demos/HealthCheckDemo.tsx b/src/ui/tui/playground/demos/HealthCheckDemo.tsx index 0ea96e34..5be0776d 100644 --- a/src/ui/tui/playground/demos/HealthCheckDemo.tsx +++ b/src/ui/tui/playground/demos/HealthCheckDemo.tsx @@ -77,7 +77,7 @@ export const HealthCheckDemo = () => { footer={ - Continue [Enter] / Exit [Esc] (disabled in playground) + Continue [Space] / Exit [Esc] (disabled in playground) } diff --git a/src/ui/tui/playground/demos/ModalDemo.tsx b/src/ui/tui/playground/demos/ModalDemo.tsx index 0ee16b99..4fb170cc 100644 --- a/src/ui/tui/playground/demos/ModalDemo.tsx +++ b/src/ui/tui/playground/demos/ModalDemo.tsx @@ -31,7 +31,7 @@ export const ModalDemo = () => { footer={ - Continue [Enter] / Exit [Esc] (disabled in playground) + Continue [Space] / Exit [Esc] (disabled in playground) } diff --git a/src/ui/tui/playground/demos/WelcomeDemo.tsx b/src/ui/tui/playground/demos/WelcomeDemo.tsx index 28d38932..2a742652 100644 --- a/src/ui/tui/playground/demos/WelcomeDemo.tsx +++ b/src/ui/tui/playground/demos/WelcomeDemo.tsx @@ -1,5 +1,5 @@ /** - * WelcomeDemo — Splash screen. Press enter to push the tabbed view. + * WelcomeDemo — Splash screen. Press space to push the tabbed view. */ import { Box, Text, useInput } from 'ink'; @@ -12,7 +12,7 @@ interface WelcomeDemoProps { export const WelcomeDemo = ({ store }: WelcomeDemoProps) => { useInput((_input, key) => { - if (key.return) { + if (_input === ' ') { store.completeSetup(); } }); @@ -34,7 +34,7 @@ export const WelcomeDemo = ({ store }: WelcomeDemoProps) => { - Press enter to continue {Icons.triangleRight} + Press space to continue {Icons.triangleRight} ); diff --git a/src/ui/tui/primitives/ConfirmationInput.tsx b/src/ui/tui/primitives/ConfirmationInput.tsx index 664c7fbf..355a3a18 100644 --- a/src/ui/tui/primitives/ConfirmationInput.tsx +++ b/src/ui/tui/primitives/ConfirmationInput.tsx @@ -1,6 +1,6 @@ /** * ConfirmationInput — Continue/cancel prompt. - * Enter confirms, escape cancels. Arrow keys toggle focus. + * Space confirms the focused action, Enter or arrow keys move focus. Escape cancels. */ import { Box, Text, useInput } from 'ink'; @@ -25,18 +25,18 @@ export const ConfirmationInput = ({ message, onConfirm, onCancel, - confirmLabel = 'Continue [Enter]', + confirmLabel = 'Continue [Space]', cancelLabel = 'Cancel [Esc]', }: ConfirmationInputProps) => { const [focused, setFocused] = useState(FocusTarget.Continue); useInput((_input, key) => { - if (key.leftArrow || key.rightArrow) { + if (key.leftArrow || key.rightArrow || key.return) { setFocused((f) => f === FocusTarget.Continue ? FocusTarget.Cancel : FocusTarget.Continue, ); } - if (key.return) { + if (_input === ' ') { if (focused === FocusTarget.Continue) { onConfirm(); } else { diff --git a/src/ui/tui/primitives/GroupedPickerMenu.tsx b/src/ui/tui/primitives/GroupedPickerMenu.tsx index d1030f98..6987307d 100644 --- a/src/ui/tui/primitives/GroupedPickerMenu.tsx +++ b/src/ui/tui/primitives/GroupedPickerMenu.tsx @@ -3,7 +3,7 @@ * * Renders groups of options with bold category labels. * Arrow keys navigate selectable items (headers are skipped), - * space toggles, "a" toggles all, enter submits. + * enter toggles, "a" toggles all, space submits. * * When content exceeds available terminal height, the list scrolls * to keep the focused item visible with ↑/↓ indicators. @@ -185,7 +185,7 @@ export const GroupedPickerMenu = ({ } } - if (input === ' ') { + if (key.return) { const targetRowIdx = selectableIndices[newFocused] ?? 0; const row = rows[targetRowIdx]; if (row?.kind === 'option') { @@ -208,7 +208,7 @@ export const GroupedPickerMenu = ({ return new Set(allValues); }); } - if (key.return) { + if (input === ' ') { onSelect([...selected]); } }); @@ -231,7 +231,7 @@ export const GroupedPickerMenu = ({ {' '} - (space to toggle, a to toggle all, enter to confirm) + (enter to toggle, a to toggle all, space to confirm) {needsScroll && ( diff --git a/src/ui/tui/primitives/PickerMenu.tsx b/src/ui/tui/primitives/PickerMenu.tsx index c47c73db..51ffb7dc 100644 --- a/src/ui/tui/primitives/PickerMenu.tsx +++ b/src/ui/tui/primitives/PickerMenu.tsx @@ -1,7 +1,7 @@ /** * PickerMenu — Single and multi select. * Single mode: custom renderer with small triangle indicator. - * Multi mode: checkbox glyphs with space to toggle. + * Multi mode: checkbox glyphs with Enter to toggle, Space to confirm. */ import { Box, Text, useInput } from 'ink'; @@ -99,7 +99,7 @@ const SinglePickerMenu = ({ const nextCol = col < columns - 1 ? col + 1 : 0; setFocused(Math.min(nextCol * rows + row, options.length - 1)); } - if (key.return) { + if (_input === ' ') { const selected = options[focused]; if (selected) { onSelect(selected.value); @@ -195,7 +195,7 @@ const MultiPickerMenu = ({ const nextCol = col < columns - 1 ? col + 1 : 0; setFocused(Math.min(nextCol * rows + row, options.length - 1)); } - if (_input === ' ') { + if (key.return) { setSelected((prev) => { const next = new Set(prev); if (next.has(focused)) { @@ -206,7 +206,7 @@ const MultiPickerMenu = ({ return next; }); } - if (key.return) { + if (_input === ' ') { if (selected.size === 0) { // Nothing toggled, select hovered const hovered = options[focused]; @@ -228,7 +228,7 @@ const MultiPickerMenu = ({ return ( - (space to multi-select, enter to confirm) + (enter to multi-select, space to confirm) { footer={ { try { diff --git a/src/ui/tui/screens/SettingsOverrideScreen.tsx b/src/ui/tui/screens/SettingsOverrideScreen.tsx index faf1ed0b..6f8cf776 100644 --- a/src/ui/tui/screens/SettingsOverrideScreen.tsx +++ b/src/ui/tui/screens/SettingsOverrideScreen.tsx @@ -44,7 +44,7 @@ export const SettingsOverrideScreen = ({ footer={ { const ok = store.backupAndFixSettingsOverride(); diff --git a/src/ui/tui/screens/SkillsScreen.tsx b/src/ui/tui/screens/SkillsScreen.tsx index 254de20c..2fd99064 100644 --- a/src/ui/tui/screens/SkillsScreen.tsx +++ b/src/ui/tui/screens/SkillsScreen.tsx @@ -124,7 +124,7 @@ export const SkillsScreen = ({ store }: SkillsScreenProps) => { void handleRemove()} diff --git a/src/ui/tui/screens/health/HealthCheckScreen.tsx b/src/ui/tui/screens/health/HealthCheckScreen.tsx index 4d63e588..71160e36 100644 --- a/src/ui/tui/screens/health/HealthCheckScreen.tsx +++ b/src/ui/tui/screens/health/HealthCheckScreen.tsx @@ -154,7 +154,7 @@ export const HealthCheckScreen = ({ store }: HealthCheckScreenProps) => { ) : ( store.dismissOutage()} onCancel={handleCancel}