docs: spec for New Session dialog UX refinements

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 10:26:37 -04:00
parent 5b4e800883
commit dbaf8ab1de
@@ -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.