From dbaf8ab1de474bd8047d4ff415ecaf486d628f8e Mon Sep 17 00:00:00 2001 From: Felix Martin Date: Thu, 11 Jun 2026 10:26:37 -0400 Subject: [PATCH] docs: spec for New Session dialog UX refinements Co-Authored-By: Claude Opus 4.8 --- ...2026-06-11-new-session-dialog-ux-design.md | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 docs/superpowers/specs/2026-06-11-new-session-dialog-ux-design.md diff --git a/docs/superpowers/specs/2026-06-11-new-session-dialog-ux-design.md b/docs/superpowers/specs/2026-06-11-new-session-dialog-ux-design.md new file mode 100644 index 0000000..dbe97a2 --- /dev/null +++ b/docs/superpowers/specs/2026-06-11-new-session-dialog-ux-design.md @@ -0,0 +1,90 @@ +# 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: + +```css +#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: + +```python +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.