feat(cli): unify TUI dialog interaction and visuals#363
Conversation
🦋 Changeset detectedLatest commit: 51e9a5d The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
Align every list dialog and selector to a single spec: shared selection pointer and current-item marker, single-border header with a (type to search) title suffix, consistent search line, and a uniform keyboard-hint vocabulary. Replace ad-hoc exit wording (close/back/exit/dismiss) with cancel, hardcoded pointers with the shared constant, and ▲▼ with ↑↓. Also add fuzzy search to /provider, restyle the /model provider tabs, and require a token with multi-field navigation in the custom-registry import. Introduce the write-tui skill (carrying the DESIGN.md spec) and refresh the apps/kimi-code development guide to match the current TUI architecture.
bb88e2e to
defea26
Compare
The plugins overview and its marketplace/MCP sub-views used Left/Right to enter and exit details. Remove that hierarchy navigation: Enter opens a detail, Esc returns, and the arrow keys no longer jump between levels. Update the sub-view hints from `←/Esc cancel` to `Esc cancel`.
The marketplace install action also fired on Space, which collides with the Space-toggle convention used elsewhere. Bind install to Enter only and update the hint to `Enter install/update`.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7d3e34a3f9
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (tokenValue.length === 0) { | ||
| this.hint = 'token-empty'; | ||
| this.activeField = 'token'; |
There was a problem hiding this comment.
Allow empty registry tokens for public imports
When the user imports a public api.json registry that does not require a Bearer token, this validation blocks the TUI flow before fetchCustomRegistry can run. The registry fetcher explicitly supports an empty apiKey by omitting the Authorization header (packages/oauth/src/custom-registry.ts), and the previous dialog submitted an empty token, so /provider can no longer add public custom registries from the TUI even though the backend still supports them.
Useful? React with 👍 / 👎.
The inline badge shown on a changed plugin row read `pending /new`, which was cryptic. Reword it to `require run /new to apply`. Also switch the marketplace-install message-flow tests to Enter, matching the Enter-only install binding.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f5a157d4eb
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Each plugin picker's onSelect unconditionally restored the editor before the handler re-mounted the refreshed picker, so an in-place action like Space-toggling a plugin flashed: picker → editor → picker. Drop the pre-restore from the overview/marketplace/MCP onSelect callbacks and let each handler branch mount its next view; the two branches that close to the editor (show-list, info) restore it themselves.
Remove the fuzzy filter from the provider manager: no query state, search line, or type-to-search title suffix, and Esc closes directly. With no type-to-search to clash with, bind delete to the D key (matching /plugins) instead of Del/Ctrl+D. Update the write-tui spec to specify D as the delete shortcut.
The model selector seeded a single thinking draft from the global thinking flag, so highlighting a thinking-capable model showed Off whenever the active model had thinking off (e.g. a non-thinking current model). Compute the draft per model instead: the active model keeps its live state, any other thinking-capable model defaults to On, and a ←/→ toggle is remembered per model.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 51e9a5d9ea
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const totalSegmentWidth = segments.reduce((sum, s) => sum + visibleWidth(s), 0); | ||
| if (1 + totalSegmentWidth <= width) { | ||
| const hint = chalk.hex(colors.textMuted)('Tab / Shift+Tab provider'); | ||
| let strip = ' ' + segments.join(''); | ||
| const available = width - visibleWidth(strip) - 1; | ||
| if (available >= visibleWidth(hint) + 1) { | ||
| const pad = ' '.repeat(available - visibleWidth(hint)); | ||
| strip += pad + hint; | ||
| } | ||
| return strip; | ||
| return ' ' + segments.join(' '); |
There was a problem hiding this comment.
Include tab separators in width checks
When there are multiple provider tabs and the terminal width is close to the strip width, this branch decides that all tabs fit using only the sum of the segment widths, but the returned string now inserts spaces between segments with segments.join(' '). For n tabs that adds n - 1 extra columns after the fit check, so the outer render truncates the tab strip and can cut off a visible tab instead of falling back to the scrolling path. Account for the separator widths in both the fit calculation and the scrolling window sizing.
Useful? React with 👍 / 👎.
Related Issue
No prior issue — the problem is described below.
Problem
TUI dialogs and selectors had drifted apart: each picker hardcoded its own selection pointer (
>/▶/❯), current-item marker (●/(current)), header layout, and keyboard-hint wording (close/back/exit/dismissmixed withcancel,▲▼vs↑↓, inconsistent capitalization)./modelpacked too many controls into one view, and the custom-registry import had an awkward two-field flow with an optional token. There was no single source of truth for how a list dialog should look or behave.What changed
(type to search)title suffix on searchable lists, a hint line directly under the title, the sharedSELECT_POINTER(❯) andCURRENT_MARK(← current), and a uniform hint vocabulary (capitalized keys, lowercase descriptions,↑↓/←→,·separator)./model,/provider,/plugins, sessions, tasks, help, approval/output viewers, and the queue pane: exit wording standardized tocancel, hardcoded pointers replaced with the shared constant, and▲▼replaced with↑↓.require run /new to apply, and stop the editor flash when toggling a plugin.Dkey, matching/plugins.write-tuiskill carrying theDESIGN.mdinteraction/visual spec, and refreshapps/kimi-code/AGENTS.md(plus a root pointer) to match the current TUI architecture.Checklist