Skip to content

feat(tui): check enhancements, command palette, session restore#31

Merged
ako merged 6 commits intomendixlabs:mainfrom
engalar:feat/tui-check-palette
Mar 26, 2026
Merged

feat(tui): check enhancements, command palette, session restore#31
ako merged 6 commits intomendixlabs:mainfrom
engalar:feat/tui-check-palette

Conversation

@engalar
Copy link
Contributor

@engalar engalar commented Mar 26, 2026

Summary

  • Check overlay enhancements: error grouping by code + element-id dedup, warning/deprecation support with Tab filtering, ]e/[e error navigation with tree jump, LLM anchor structured output
  • Command palette: VS Code-style : command palette with fuzzy search, category grouping, simplified hint bar
  • Session restore: -c/--continue flag saves/restores TUI state (project, navigation, preview mode)
  • Mouse fixes: Y-coordinate offset for LLM anchor line, remove broken mouse reset on resize
  • Compare view: default to NDSL|MDL mode, load both sides simultaneously
  • PR feat(tui): interactive diff view with unified/side-by-side/plain modes #27 follow-ups: handledCmd goroutine elimination, scroll step magic number extraction

Test plan

  • 40+ new tests for checker grouping, command palette, session roundtrip
  • go test ./cmd/mxcli/tui/... passes (47 tests)
  • Manual: ! check overlay shows grouped errors with Tab filtering
  • Manual: ]e/[e navigates between error documents
  • Manual: : opens command palette, fuzzy search works
  • Manual: tui -c restores previous session state
  • Manual: mouse clicks work correctly (tab bar, miller, status bar)
  • Manual: compare view opens with NDSL|MDL side-by-side

🤖 Generated with Claude Code

engalar added 6 commits March 26, 2026 10:42
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)
Copy link
Collaborator

@ako ako left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) == 0len(nil) == 0 in Go.
  • Inconsistent filter call: extractCheckNavLocations called via filterCheckErrors(..., "all") in one place and raw a.checkErrors in another.
  • Magic numbers in commandpalette.go (30, 56, 14, 6, 10, "245") should be named constants.
  • LLM anchor line -1 offset repeated in 4+ places — should be part of chromeHeight or a named constant.
  • LLM anchor element= values unescaped (spaces/quotes), making machine parsing ambiguous.

What looks good

  • Error grouping/dedup logic in checker.go is well-structured
  • handledNoop pattern replacing the goroutine-based handledCmd is 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

@ako ako merged commit 8ab8a41 into mendixlabs:main Mar 26, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants