Skip to content

feat(tui): unify modal interaction and visual behavior #36302

Closed

Description

@kitlangton

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 / back stack, 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 DialogSelect actions single-flight and route rejected promises through the dialog or toast error surface.
  • Preserve valid falsey generic selections by checking current !== undefined rather 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 DialogSelect without 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, and DialogRetryAction.
  • Remove the duplicate legacy provider-authentication flow if DialogProvider remains unreachable.

Relevant source areas

  • packages/tui/src/ui/dialog.tsx
  • packages/tui/src/ui/dialog-select.tsx
  • packages/tui/src/ui/dialog-export-options.tsx
  • packages/tui/src/component/dialog-integration.tsx
  • packages/tui/src/component/dialog-agent.tsx
  • packages/tui/src/component/dialog-skill.tsx
  • packages/tui/src/component/dialog-session-list.tsx
  • packages/tui/src/component/dialog-move-session.tsx
  • packages/tui/src/component/dialog-pair.tsx
  • packages/tui/src/component/dialog-console-org.tsx
  • packages/tui/src/component/dialog-workspace-create.tsx
  • packages/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.

Activity

  1. self-assigned this
    on Jul 10, 2026
  2. kitlangton commented on Jul 10, 2026

    @kitlangton
    ContributorAuthor

    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.

  3. kitlangton commented on Jul 10, 2026

    @kitlangton
    ContributorAuthor

    More design inputs from the visual pass:

    • Sessions needs an explicit pending-search state. Do not present a finished empty state while search is unresolved.
    • Footer commands need a real disabled state in 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.
    • Session pin/unpin should derive its label from the focused session. Revisit the grouping and selection behavior when pinned and unpinned sessions coexist.
    • Plugin toggle should be disabled when no plugin is selected.
    • The Install plugin Tab/toggle interaction and scope presentation need redesign; the current treatment is visually awkward and unclear.
    • Status needs its information architecture reconsidered rather than only a sizing fix. Debug is fine as-is.
    • /help needs 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.
  4. kitlangton commented on Jul 10, 2026

    @kitlangton
    ContributorAuthor

    Additional follow-ups:

    • Fork needs a broader interaction review beyond cancellation. The chooser, pending state, dismissal, and destination transition currently feel disconnected.
    • Move should not expand edge-to-edge for a single path row. Its sizing needs to follow the shared modal role rather than its custom implementation.
    • File and folder paths need a shared TUI presentation component. It should preserve the meaningful basename and nearest parent, support left/middle truncation deliberately, and avoid each dialog inventing its own clipping behavior.
    • The diff viewer/command is currently broken and needs a correctness fix, not just visual polish. We should link the existing diff issue here once confirmed.
  5. kitlangton commented on Jul 10, 2026

    @kitlangton
    ContributorAuthor

    Truncation research points to a combined pattern rather than one global truncation rule:

    • Keep semantic one-line rows for scan density and stable keyboard navigation.
    • Add an optional fixed preview pane for the focused option. Agents and Skills can show wrapped prose, Models can show structured provider/capability metadata, and Move can show the full normalized path and checkout relationship.
    • Use a shared row primitive that reserves gutter/trailing metadata before allocating primary and secondary text.
    • Use a separate shared path primitive: files preserve the basename with left-truncated parents; folders preserve both anchor and leaf with middle truncation.
    • Make low-level truncation terminal-column and grapheme aware. Current Locale helpers count UTF-16 units, and truncateMiddle has a width 1-2 bug.

    Relevant work:

    No public model-picker PR or branch attributable to Dustin was found, so any additional work there still needs direct coordination before implementation.

  6. kitlangton commented on Jul 10, 2026

    @kitlangton
    ContributorAuthor

    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 renders No diff! instead of crashing. The fix migrates the TUI from removed client.vcs.diff / mode: git to client.v2.vcs.diff / mode: working, preserving session workspace location. Related API/client migration issues: #34433 and #34359.

  7. neriousy commented on Oct 21, 2026

    @neriousy
    Member

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions