feat(tui): check enhancements, command palette, session restore#31
feat(tui): check enhancements, command palette, session restore#31ako merged 6 commits intomendixlabs:mainfrom
Conversation
Four-part enhancement plan: 1. Error grouping by code + dedup by element-id 2. Error navigation with ]e/[e and tree jump 3. Warning/deprecation support with Tab filtering 4. LLM anchor structured output in check overlay
…chors Part 1: Error grouping + deduplication - Group errors by code, deduplicate by element-id with (xN) count - New CheckGroup/CheckGroupItem types with groupCheckErrors() Part 2: Error navigation - Enter in check overlay jumps to document in tree - ]e/[e navigation in both browser and overlay modes - Check nav mode with status bar indicator [n/N] - Lazy init: ]e works directly without entering overlay first Part 3: Warning + deprecation support - Run mx check with -w -d flags for full diagnostics - Tab cycles severity filter in check overlay (all/error/warn/depr) - Badge shows all severity counts: ✗ 8E 2W 1D Part 4: LLM anchor structured output - [mxcli:check] summary + per-item anchors with Faint styling - Replaces generic [mxcli:overlay] anchor in check overlays
- New CommandPaletteView: centered modal with fuzzy search, category grouping, and keyboard navigation (j/k, Enter, Esc) - 21 browser-mode commands across 6 categories (Navigation, View, Action, Check, Tab Management, Other) - Simplify hint bar from 15 items to 6 core hints + : commands entry - Add LLM anchor line [mxcli:commands] with all available operations in Faint styling for AI agent consumption - PaletteExecMsg dispatches selected command key back through Update (supports special keys: Space, Tab, multi-char sequences like ]e)
Part 1: Reset mouse tracking on WindowSizeMsg to fix coordinate drift Part 2: -c flag to save/restore TUI session state (tabs, navigation, views) with edge case handling for deleted nodes and stale data
… layout Session restore: - Save TUI state to ~/.mxcli/tui-session.json on quit - -c/--continue flag restores project, navigation path, preview mode - Edge cases: deleted nodes fall back to miller path Mouse coordinate fix: - Fix tab bar click Y-offset for LLM anchor line (row 0 → 1) - Fix content area Y-offset (−1 → −2 for anchor + tab bar) - Remove broken DisableMouse/EnableMouseCellMotion on resize (these return Msg not Cmd, sending them broke mouse state) Compare view: - Default to NDSL|MDL mode, load both sides simultaneously Command palette layout: - Add scroll window to prevent overflow - Fix shortcut key alignment with plain string padding
- Replace handledCmd nil-returning goroutine with pre-allocated handledNoop Msg to avoid per-call goroutine allocation - Extract mouse scroll step magic number 3 to mouseScrollStep constant (reuse existing definition from column.go)
ako
left a comment
There was a problem hiding this comment.
Code Review
+2643 / -104 across 16 files. 6 commits adding check overlay enhancements, a command palette, session restore, mouse fixes, and PR #27 follow-ups.
Bugs
1. containsPlainText test helper is a no-op (commandpalette_test.go)
func containsPlainText(s, substr string) bool {
return len(s) > 0 && len(substr) > 0
}Never checks if substr exists in s, so TestCommandPalette_Render verifies nothing. Should use strings.Contains.
2. ]e/[e palette commands are non-functional (app.go dispatchPaletteKey)
Converts the key string to a single KeyMsg. For multi-char strings like ]e, this produces a KeyRunes with [']','e'] which won't trigger the two-key pendingKey sequence. These palette entries silently do nothing.
3. Potential panic on negative slice index in palette render (commandpalette.go)
nameMaxWidth := contentWidth - 6 - shortcutWidth. If shortcutWidth is large enough, nameMaxWidth goes negative, and name[:nameMaxWidth-1] panics. Needs a max(nameMaxWidth, 1) guard.
4. j/k dual behavior in check overlay (overlayview.go)
New j/k handling for check nav cursor movement doesn't return early, so the keypress also reaches ov.overlay.Update(msg) which scrolls the content. Pressing j/k will both move the cursor AND scroll simultaneously.
5. checkNavIndex bounds not fully validated (app.go)
checkNavIndex is initialized to -1 and the guard only checks len(a.checkNavLocations) > 0. If the locations slice is replaced after a rerun with a shorter slice, the index can exceed bounds. Should add a.checkNavIndex >= 0 && a.checkNavIndex < len(a.checkNavLocations).
6. applySessionRestore discards returned tea.Cmd (app.go)
bv.navigateToNode(ts.SelectedNode) return value is ignored, so async side effects (like loading preview content) won't execute.
Design Issues
7. [/] keys hijacked when check errors exist (app.go)
When len(a.checkErrors) > 0, pressing [ or ] always enters pendingKey mode and returns handledCmd, completely blocking tab switching until errors are cleared.
8. pendingKey has no timeout (overlayview.go)
If a user presses ] then walks away, pressing e later still triggers the jump. Also, a bare e keypress when no pending key exists gets silently swallowed.
9. Check summary shows unfiltered counts (checker.go renderCheckResults)
The summary header calls countBySeverity(errors) on the unfiltered set, so when filtering by severity the header still shows all counts.
10. Session tests don't actually test SaveSession/LoadSession (session_test.go)
The round-trip test manually does json.Marshal/os.WriteFile/os.ReadFile/json.Unmarshal instead of calling the actual functions.
Security / Hardening
11. Session file permissions too open (session.go)
Written with 0644. Contains absolute file paths. Should use 0600.
Minor / Style
- Redundant nil check in
renderCheckFilterTitle:errors == nil || len(errors) == 0—len(nil) == 0in Go. - Inconsistent filter call:
extractCheckNavLocationscalled viafilterCheckErrors(..., "all")in one place and rawa.checkErrorsin another. - Magic numbers in
commandpalette.go(30,56,14,6,10,"245") should be named constants. - LLM anchor line
-1offset repeated in 4+ places — should be part ofchromeHeightor a named constant. - LLM anchor
element=values unescaped (spaces/quotes), making machine parsing ambiguous.
What looks good
- Error grouping/dedup logic in
checker.gois well-structured handledNooppattern replacing the goroutine-basedhandledCmdis a solid improvement- Test coverage for session edge cases (deleted nodes, corrupt files) is thorough in intent
- Command palette UX with fuzzy search and category grouping is a nice addition
🤖 Generated with Claude Code
Summary
]e/[eerror navigation with tree jump, LLM anchor structured output:command palette with fuzzy search, category grouping, simplified hint bar-c/--continueflag saves/restores TUI state (project, navigation, preview mode)handledCmdgoroutine elimination, scroll step magic number extractionTest plan
go test ./cmd/mxcli/tui/...passes (47 tests)!check overlay shows grouped errors with Tab filtering]e/[enavigates between error documents:opens command palette, fuzzy search workstui -crestores previous session state🤖 Generated with Claude Code