Files
hqt/docs/superpowers/specs/2026-06-11-new-session-dialog-ux-design.md
T
2026-06-11 10:26:37 -04:00

3.6 KiB

New Session Dialog UX Refinements — Design

Goal: Three focused UX improvements to the New Session modal: hide sandbox sub-options until sandboxing is enabled, unify the focus highlight to the dialog's Peach accent, and add context-aware j/k field navigation.

Scope: src/hqt/tui/screens/new_session.py and src/hqt/tui/styles.tcss. No service, DB, or sandbox-policy changes.


1. Hide sandbox sub-options until the toggle is on

The "Filesystem access" and "Network" rows are sub-options of the sandbox switch and should only appear when sandboxing is enabled — mirroring the existing worktree-checkbox → branch-input reveal pattern already in this screen.

  • Wrap the two label/widget pairs (Filesystem access: + #fs-select, Network: + #net-switch) in a Vertical(id="sandbox-options") container so they toggle as a single unit.
  • Set display = False on the container at compose time.
  • Add on_switch_changed(self, event: Switch.Changed): when event.switch.id == "sandbox-switch", set #sandbox-options display to event.value.
  • The sandbox switch starts off — and is disabled when bwrap is absent — so the sub-options start hidden in every case. No separate handling for the unavailable case is needed.
  • _submit() still reads #fs-select / #net-switch unconditionally. Their values are valid defaults (rw / net on) even while hidden, and the policy is only built when the sandbox switch is on, so hidden values are never used.

2. Unify the focus highlight to Peach ($accent)

Buttons already get a Peach focus highlight from styles.tcss, but Switch / Checkbox / Select / Input fall back to Textual's default Lavender ($border) focus border. That inconsistency is the "off-palette" highlight. Add scoped rules so every focusable control in the dialog shares the Peach accent:

#new-session-dialog Switch:focus,
#new-session-dialog Checkbox:focus,
#new-session-dialog Select:focus,
#new-session-dialog Input:focus {
    border: tall $accent;
}

Switch / Checkbox / Input use a tall border by default, so matching tall keeps layout height identical — only the color changes.

3. Soft context-aware j/k navigation

Add screen-level bindings that walk the dialog's focus chain:

BINDINGS = [
    Binding("j", "focus_next", "Next field", show=False),
    Binding("k", "focus_previous", "Prev field", show=False),
]

Input consumes printable keys before screen bindings fire, so j/k type literally while editing a text field and only move focus when the focused widget is a toggle / select / button — the "soft" (context-aware) model the user chose. focus_next / focus_previous are built-in Textual screen actions. Tab / Shift+Tab / arrows keep working unchanged.


Testing

All three are testable against the screen in isolation via Textual's run_test() pilot:

  • Sub-options reveal: mount with sandbox_available=True; assert #sandbox-options .display is False initially, flip the sandbox switch, assert it becomes True, flip back, assert False.
  • j/k navigation: assert the j / k bindings map to focus_next / focus_previous; drive the pilot to confirm focus moves between toggles and that j/k typed into a focused Input land as literal text rather than moving focus.
  • Focus highlight: the color is visual, but the presence of the scoped :focus rule is assertable by reading the compiled CSS / styles.tcss.

Out of scope

  • No modal (normal/insert) vim mode — the soft model was chosen deliberately.
  • No change to Enter-to-submit behavior.
  • No restyling of controls outside the New Session dialog.