Skip to content

fix(tui): Escape in Import Preview, and menus that forget their row after a sub-screen - #780

Merged
sirmalloc merged 9 commits into
sirmalloc:mainfrom
eric-engberg:fix/import-preview-navigation
Nov 5, 2026
Merged

sirmalloc merged 9 commits into
sirmalloc:mainfrom
eric-engberg:fix/import-preview-navigation

Conversation

@eric-engberg

Copy link
Copy Markdown
Contributor

What

Import Preview ignored Escape, and three menus lost the highlighted row after coming back from a sub-screen:

  • Import Preview: Escape now steps back one screen. From the "Apply this import? It adds a shell command…" confirmation it returns to the preview. From the preview it leaves the import, as Cancel does.
  • Import Preview: after Merge, then Cancel on that confirmation, the preview comes back on Merge with the Merge diff instead of on Replace All.
  • Configure Status Line: Escape on a Refresh Interval or cache TTL prompt brings the list back on the row that opened it.
  • Install to Claude Code: going back from the package manager step (Escape or "← Back") keeps Auto-update highlighted when that was the choice.

Why (evidence)

  • Escape did nothing on either Import Preview screen. The only way out was to arrow down to Cancel and press Enter. docs/USAGE.md already says "Cancel/Escape to return to the preview", and the Import Config path dialog one step earlier goes back on Escape. The dialog had no input handler of its own, and List only handles the arrows and Enter.
  • Merge, Enter, then Cancel ("Go back to the preview without importing") moved the highlight to Replace All and switched the diff to Replace. The confirmation doesn't name the mode, so pressing Enter again and confirming applied Replace All. That resets every setting the file leaves out to its default in the working config. In a component test, Enter, Down, Enter after the Cancel called onApply('replace').
  • Configure Status Line: after cancelling Custom Command Cache TTL, the next Enter opened "Enter refresh interval in seconds (1-60):". Saving returns to the Main Menu, so only cancelling showed the problem.
  • Install: after picking Auto-update and pressing Escape, the highlight and description were Pinned global install, and Enter led to npm install -g / bun add -g. A confirmation still comes before anything installs.

The last three share one cause. A sub-screen (a confirmation, a prompt or the next step) is drawn in place of the menu's List. The List unmounts, and when it mounts again it starts on its first row, because initialSelection defaults to 0 or was hard-coded to 0. Powerline Setup and Line Selector already carry their selection across that remount.

How

  • ImportPreviewDialog: a useInput for Escape that goes back to the preview while the confirmation is open and calls onCancel otherwise. The main List gets initialSelection from previewMode, which already tracks the highlighted mode for the diff.
  • RefreshIntervalMenu: remembers the highlighted row through onSelectionChange and passes it back as initialSelection, as PowerlineSetup does. The index counts selectable rows only, so it stays right when Refresh Interval is disabled.
  • InstallMenu: the style List starts on the updateStyle still held from the first pick instead of a hard-coded 0.

Demo

Escape on Import Preview. Before the fix, two Escapes on the shell-command confirmation do nothing. After it, the first returns to the preview and the second to the Main Menu.

Powerline

Before:

Escape on Import Preview before the fix, Powerline

After:

Escape on Import Preview after the fix, Powerline

Plain

Before:

Escape on Import Preview before the fix, Plain

After:

Escape on Import Preview after the fix, Plain

Merge, then Cancel on the confirmation. Before the fix, the preview comes back on Replace All with the Replace diff. After it, the preview stays on Merge with the Merge diff.

Powerline

Before:

Merge then Cancel before the fix, Powerline

After:

Merge then Cancel after the fix, Powerline

Plain

Before:

Merge then Cancel before the fix, Plain

After:

Merge then Cancel after the fix, Plain

Configure Status Line after Escape on a TTL prompt. Before the fix, the highlight returns to Refresh Interval and Enter opens its prompt. After it, Custom Command Cache TTL stays highlighted.

Powerline

Before:

Escape on a TTL prompt before the fix, Powerline

After:

Escape on a TTL prompt after the fix, Powerline

Plain

Before:

Escape on a TTL prompt before the fix, Plain

After:

Escape on a TTL prompt after the fix, Plain

Install, back from the package manager step. Before the fix, Pinned global install is highlighted again. After it, Auto-update stays highlighted.

Powerline

Before:

Back from the package manager step before the fix, Powerline

After:

Back from the package manager step after the fix, Powerline

Plain

Before:

Back from the package manager step before the fix, Plain

After:

Back from the package manager step after the fix, Plain

Testing

  • On main as of fix: use env override and settings for usable context ratio calculation #407 (71e8b42): merges cleanly, bun run lint is clean, and bun test gives 3943 pass, 0 fail. The built CLI under Bun 1.4.4 and Node 26.13.0 matches main in plain and Powerline mode with the baseline configs, and the TUI opens and exits cleanly. It adds no JavaScript or Node API newer than Node 14, the build's target.
  • 5 new Ink tests. They render the real components with simulated keys, and each key waits for its visible effect. There are 3 for Import Preview and 1 each for Configure Status Line and Install. All 5 fail on main under both Bun and Node, and all pass here.
  • bun test: 3175 pass, 0 fail, 178 files.
  • Changed test files under Node 26.10.0 (vitest): 52/52 pass. The same files on main: 45/45.
  • Stability: the four changed test files ran 8 times in a row and 6 times in parallel, 52/52 every run.
  • bun run lint: clean.
  • Built dist/ under Bun 1.4.2 and Node 26.10.0: plain and Powerline piped renders are identical across runtimes and match main. The TUI opens and exits cleanly on both.

…onfirmation

When an import adds a Custom Command, choosing Replace All or Merge
first asks "Apply this import? It adds a shell command...". Cancel says
"Go back to the preview without importing", but coming back from Merge
put the highlight on Replace All and switched the preview to the
Replace diff. The confirmation doesn't name the mode, so pressing Enter
again and confirming applied Replace All, which resets every setting
the file leaves out to its default.

The confirmation replaces the preview's List, and the List mounted
again at its first row, then reported that row as the new selection.
Mount it on the mode the preview was showing instead.
…rmation

Escape did nothing on Import Preview, on both the diff and the "Apply
this import? It adds a shell command..." confirmation. The only way out
was to arrow down to Cancel and press Enter. The Import Config path
dialog one step earlier, and the other menus, go back on Escape, and
docs/USAGE.md already says Escape returns from the confirmation to the
preview.

The dialog had no input handler of its own, and its List only handles
the arrows and Enter. Escape now steps back one screen: from the
confirmation to the preview, where the mode stays highlighted, and from
the preview out of the import, as Cancel does.
In Terminal Options you press Enter on Color Level to cycle through the
levels. With an ansi256 or hex widget color, leaving 256 Color or
Truecolor first warns "Custom colors detected!". After Yes, No or
Escape the highlight was back on Terminal Width, so the next Enter
opened the Terminal Width screen instead of cycling on.

The warning replaces the List, and the List mounted again at its first
row. Remember the highlighted row, as Powerline Setup does around its
confirmations, and mount the List on it.
…Line

Pressing Escape on a Refresh Interval or cache TTL prompt brought the
list back with the highlight on the first row, Refresh Interval (or Git
Cache TTL when Refresh Interval needs a newer Claude Code). Pressing
Enter again opened a different setting from the one just left. Saving
returns to the Main Menu, so only cancelling showed it.

Each prompt replaces the List, and the List mounted again at its first
row. Remember the highlighted row and mount the List on it.
… Install

In Install to Claude Code, going back from the package manager step
(Escape or "← Back") highlighted Pinned global install again, with its
description, even after choosing Auto-update. Pressing Enter then led
into the pinned global install commands instead of the npx/bunx ones.

The style List mounts again on the way back, and it always started on
its first row. Start it on the style still held from the first pick.
… of flexMode

flexMode is going away with the Terminal Width options; any non-default key
the import carries serves this fixture.
…he first row

With sirmalloc#765 Terminal Options lists only Color Level, so the two tests start
there instead of stepping down from Terminal Width.
…ade moot

With sirmalloc#765, Color Level is Terminal Options' only setting and the
custom-colors warning opens only from it, so the menu comes back on that
row without help.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants