docs/internal/settings-ui-improvement-plan.md
This plan tracks systematic UX testing of the Settings UI dialogs against UI design principles and NNGroup best practices. Three rounds of testing completed; this document reflects the current state.
Key files:
| File | Purpose |
|---|---|
crates/fresh-editor/src/view/settings/input.rs | Input routing: text editing, dropdown, navigation, entry dialog |
crates/fresh-editor/src/view/settings/entry_dialog.rs | EntryDialogState: focus management, items, buttons |
crates/fresh-editor/src/view/settings/render.rs | All rendering including entry dialog, buttons, help text |
crates/fresh-editor/src/view/settings/items.rs | SettingControl enum, build_item_from_value |
crates/fresh-editor/src/view/settings/state.rs | SettingsState, dialog stack, config layer management |
crates/fresh-editor/src/view/controls/map_input/mod.rs | MapState, get_display_value() |
crates/fresh-editor/src/types.rs | LspServerConfig, LspLanguageConfig |
| Round | Rebased On | Agents | Key Changes Found |
|---|---|---|---|
| 1 | Initial | 4 agents | Identified 20 bugs across all categories |
| 2 | master (ced0969) | 3 agents | C1, H3, H4, M1, M3, M4 fixed. C2 behavior changed (no longer closes dialog, but Esc now does nothing) |
| 3 | master (efbf75f) | 3 agents | C3 fixed, M8 fixed (sticky headers), "Advanced" section divider added. Composite control navigation improved. |
| 4 | master (efbf75f) | 3 agents + tmux | H1 fixed (Tab sequential), M7 fixed (PageDown), composite highlight per-row, add-server persistence, TextList auto-accept |
| ID | Description | Fixed In |
|---|---|---|
| C1 | Text input not rendering in Edit Item dialog | Round 2 |
| C3 | Enter on array item in Edit Value closes dialog | Round 3 |
| H1 | Tab only toggles between fields and Save button | Round 4 — Tab now cycles sequentially through all fields, sub-fields, and buttons |
| H3 | Down-arrow skips Command field | Round 2 |
| H4 | Ctrl+S doesn't work in entry dialogs | Round 2 |
| H5 | Individual TextList items not keyboard-focusable | Round 3/4 — composite navigation visits sub-items |
| M1 | Name field opens wrong dialog type | Round 2 |
| M3 | LSP entries display [1 items] instead of command | Round 2 |
| M4 | Parent dialog not dimmed when child opens | Round 2 |
| M7 | No Page Up/Down in long lists | Round 4 — PageDown/PageUp jump by viewport height |
| M8 | Section header scrolls away, losing context | Round 3 (sticky headers) |
| NEW | Composite highlight covers entire section | Round 4 — highlight now only on focused sub-row, > on sub-item not header |
| NEW | Adding new LSP server doesn't persist | Round 4 — fixed path computation in save_array_item_dialog_inner |
| NEW | TextList edits lost on Down/Up/Tab | Round 4 — auto-accept pending text before navigating away |
Round 3 status: NOT FIXED
Behavior: Text fields (Command, Name, Comment Prefix, etc.) auto-enter edit mode when navigated to. The inverse video cursor ([7m]) appears and the label turns blue (38;5;25) immediately on focus — no Enter press required. Escape has zero visible effect: cursor stays, label stays blue, characters can still be typed. The status bar says "Enter:Edit" but Enter is not required.
Impact: Users cannot distinguish "navigating fields" from "editing a field value." Accidental keystrokes modify field values. The only way to leave the dialog is Ctrl+S (save+close) or multiple Escapes (which eventually close the dialog).
ANSI evidence:
Navigating to field: [38;5;25mCommand : [astro-ls[7m ] <- blue label, cursor active
After pressing Esc: [38;5;25mCommand : [astro-ls[7m ] <- IDENTICAL
After pressing Down: [38;5;15mCommand : [astro-ls] <- normal white (left field)
Root cause: editing_text flag may not be properly toggled, or text fields use a different edit-mode mechanism (always-inline) that bypasses the start_editing()/stop_editing() flow.
NNGroup violations:
Round 4 status: FIXED
Fix: Tab now calls focus_next() (same as Down), cycling sequentially through all fields, sub-fields, and buttons. Shift+Tab calls focus_prev(). Verified with tmux: 20 Tab stops including all 3 buttons.
Round 3 status: PARTIALLY FIXED Behavior: Down arrow navigation visits composite control items (e.g., Args → "--stdio" item) but still skips [+] Add new buttons. Workaround: Enter on section headers (Args, Root Markers) opens inline text input with [+]. Enter on map sections (Env, Language Id Overrides) opens "Add Value" dialog. Adding items IS possible, just not through the visible [+] buttons. NNGroup violation: WCAG 2.1 Level A — visible interactive elements must be keyboard-reachable.
Round 4 status: FIXED
Fix: Composite control navigation now visits individual TextList items. Down arrow enters the section and visits each item. E2e test test_textlist_items_keyboard_accessible passes.
Round 3 status: NOT FIXED (see C2 — same root cause) Behavior: Navigating to any text field with Down/Up immediately activates edit mode without pressing Enter. This makes it impossible to "select" a text field without editing it.
Round 3 status: NOT FIXED
Behavior: Footer always shows ↑↓:Navigate Tab:Fields/Buttons Enter:Edit Ctrl+S:Save Esc:Cancel regardless of:
Status: Not re-tested in Round 3. Likely still present.
Round 3 status: NOT FIXED
Tested: Scrollbar IS present with proportional thumb size. Colors: main panel thumb 48;5;3 (olive), track 48;5;15 (white). Dialog thumb 38;5;70 (green), track 48;5;239 (dark gray). But no numeric "X of Y" indicator anywhere.
Round 4 status: FIXED
Fix: Added select_next_page()/select_prev_page() to SettingsState. PageDown/PageUp jump by viewport height. Home/End not yet implemented.
Status: Not re-tested in Round 3. Likely still present.
Status: Not re-tested in Round 3.
Round 3 finding: Map entries (Languages, LSP) use only text color change (cyan 38;5;14 → white 38;5;231) for focus. No background highlight, no > arrow. This is significantly weaker than:
48;5;25 + > arrow48;5;16 + >● indicator[1;7m] + > arrow
NNGroup violation: Consistency — focus indication strength should not vary by context.Round 3 finding: Main panel scrollbar (olive 48;5;3 on white 48;5;15) vs dialog scrollbar (green 38;5;70 on dark gray 48;5;239). Minor but noticeable inconsistency.
Process Limits, Except/Only Features, Initialization Options shown as raw JSON textareas.
An "Advanced" section divider (── Advanced ── in 38;5;244 gray, bold) now separates basic from advanced fields in the Edit Item dialog. However it's not collapsible — all fields remain visible.
Can enter nonexistent commands with no feedback.
> prefix with >● markers. Consistent 48;5;16 dark bg + 38;5;231 bright white[1;7m]) with > arrow. Delete button uses red (38;5;160)38;5;14. Parent: gray 38;5;59, dark teal 38;5;29> indicator, not the entire sectionThe most impactful fix — makes the entire Edit Item dialog usable for text fields.
Problem: Text fields behave as "always-inline-editing" rather than using the explicit start_editing()/stop_editing() pattern that JSON controls use.
Fix approach:
entry_dialog.rs, ensure text fields do NOT auto-enter edit mode on focus. The editing_text flag should only be set when the user presses Enter/Space.input.rs handle_entry_dialog_text_editing(), ensure Escape calls dialog.stop_editing() and returns InputResult::Consumed, properly clearing the edit state.[Enter to edit] hint should appear.Tab now calls focus_next(), identical to Down. Cycles through all fields, sub-fields, and buttons sequentially. Shift+Tab mirrors in reverse.
Fix: In render.rs, make the help text dynamic based on current state:
Normal: ↑↓:Navigate Tab:Fields/Buttons Enter:Edit Ctrl+S:Save Esc:Cancel
Text editing: Type to edit Tab/Esc:Stop editing Ctrl+S:Save
Dropdown: ↑↓:Select Enter:Confirm Esc:Cancel
On buttons: ←→:Navigate Enter:Activate Tab:Back to fields Esc:Cancel
> indicator to focused map table entries