Repository navigation
feat(tui): unify modal interaction and visual behavior #36302
Copy link
Copy link
Closed
Description
Context
A visual and source audit of the V2 TUI reviewed 37 dialog-related components and reproduced 20 normal, empty, nested, and loading states. This issue is the temporary decision surface for the related fixes. Split it into focused implementation issues after the interaction model and visual conventions are decided.
@kitlangton is the design owner for these decisions.
Design exploration first
- Design the progressive nested-dialog experience for integration authentication, provider methods, workspace selection, MCP details, and similar stacked workflows.
- Compare a true
push/backstack, breadcrumb or path treatment, progressive disclosure, and any hybrid that makes depth and Escape behavior obvious without making terminal dialogs feel browser-like. - Decide whether Escape means Back when a parent exists and Close only at the root.
- Define the shared visual language for keyboard focus, persisted current value, connected status, pending state, disabled state, and destructive state. Today the orange row means focus, a dot means current, and a check means connected, but the distinctions are unexplained.
Safety and correctness
- Default destructive confirmations to the safe option, especially Delete working copy.
- Make Export keyboard focus visible and make submission single-flight while export work is pending.
- Define cancellation semantics for pending Fork requests; dismissing the loading dialog must not later navigate unexpectedly.
- Make async
DialogSelectactions single-flight and route rejected promises through the dialog or toast error surface. - Preserve valid falsey generic selections by checking
current !== undefinedrather than truthiness. - Unify destructive-action safeguards by severity instead of mixing immediate actions, double-press confirmation, and Yes/No prompts.
Loading, empty, error, and unavailable states
- Give Skills, Sessions search, Organizations, workspace selection, Integrations, Pair, and Move explicit mutually exclusive state treatments.
- Do not render loading or empty messages as selectable options.
- Keep stale Sessions results or expose remote-search progress instead of flashing a finished empty state.
- Fix Pair so an error cannot appear alongside a permanent Loading server information message.
- Show environment-only integrations as disabled with an explanation instead of filtering them out.
Action availability and discoverability
- Hide or disable actions that cannot operate on the current selection or empty state, including Sessions, Plugins, and Move.
- Define compact shared navigation and selection hints for
DialogSelectwithout overcrowding narrow dialogs.
Presentation
- Replace hard mid-word clipping in Agent and Skill descriptions with a deliberate readable treatment.
- Preserve meaningful path suffixes in Move instead of clipping both ends indiscriminately.
- Standardize modal sizes by content role and give unbounded diagnostic content an internal scroll region.
- Standardize sentence casing, action labels, and product-name presentation.
Cleanup
- Remove or explicitly reconnect dead dialog families:
DialogTimeline,DialogSubagent,DialogTag,DialogProvider,DialogSessionDeleteFailed, andDialogRetryAction. - Remove the duplicate legacy provider-authentication flow if
DialogProviderremains unreachable.
Relevant source areas
packages/tui/src/ui/dialog.tsxpackages/tui/src/ui/dialog-select.tsxpackages/tui/src/ui/dialog-export-options.tsxpackages/tui/src/component/dialog-integration.tsxpackages/tui/src/component/dialog-agent.tsxpackages/tui/src/component/dialog-skill.tsxpackages/tui/src/component/dialog-session-list.tsxpackages/tui/src/component/dialog-move-session.tsxpackages/tui/src/component/dialog-pair.tsxpackages/tui/src/component/dialog-console-org.tsxpackages/tui/src/component/dialog-workspace-create.tsxpackages/tui/src/routes/session/dialog-fork.tsx
Validation direction
Each resulting behavior should have a deterministic opencode-drive reproduction for normal, loading, empty, error, disabled, and nested states where applicable. The current audit exposed that transient API states need deterministic barriers or latency control rather than screenshot sleeps.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
kitlangton commentedon Jul 10, 2026
The long-description treatment needs design exploration, not just a blanket ellipsis rule. Agents, Skills, and any other picker with meaningful secondary text should let users understand an option before selecting it without turning every row into a wall of wrapped text.
The same exploration should cover richer model details for the current or focused model. Dustin may already have model-picker work in this direction, so we should find and coordinate with that before choosing the shared picker pattern.
kitlangton commentedon Jul 10, 2026
More design inputs from the visual pass:
DialogSelect, with dimmed rendering and blocked execution. Hiding may still be right when an action is irrelevant, but temporarily unavailable actions should remain legible as disabled./helpneeds product-level rethinking. If it only redirects users to commands, it should either open Commands directly or offer genuinely useful help content. Bring this decision to the team.kitlangton commentedon Jul 10, 2026
Additional follow-ups:
kitlangton commentedon Jul 10, 2026
Truncation research points to a combined pattern rather than one global truncation rule:
Localehelpers count UTF-16 units, andtruncateMiddlehas a width 1-2 bug.Relevant work:
truncateMiddlewidths 1-2short_descriptionfield for agents and agent details dialog #5117 expandable agent detailsNo public model-picker PR or branch attributable to Dustin was found, so any additional work there still needs direct coordination before implementation.
kitlangton commentedon Jul 10, 2026
The diff command crash found during this audit is now fixed in the audit worktree and verified through
opencode-drive: the viewer opens the V2 working-tree source and rendersNo diff!instead of crashing. The fix migrates the TUI from removedclient.vcs.diff/mode: gittoclient.v2.vcs.diff/mode: working, preserving session workspace location. Related API/client migration issues: #34433 and #34359.neriousy commentedon Oct 21, 2026
Partly done: dialogs now share DialogHeader/Footer/Layout (#55501, #55513, #55519, #55523, #55799), with label-first hints (#55745), the fork pending view (#55751) and export keyboard focus (#57154). Nested-dialog Esc behavior, safe destructive defaults, single-flight async and loading/empty/error states haven't landed. As the issue suggested, those are better as focused issues. Closing this tracker.