Skip to content

Lipgloss Layers: Proper Styles, Proper Keybindings#13

Merged
JasonWarrenUK merged 9 commits intomainfrom
styles/lipgloss-layers
Mar 25, 2026
Merged

Lipgloss Layers: Proper Styles, Proper Keybindings#13
JasonWarrenUK merged 9 commits intomainfrom
styles/lipgloss-layers

Conversation

@JasonWarrenUK
Copy link
Owner

Overview

Replaces the TUI's ad-hoc styling and vim-style keybinding system with idiomatic Lipgloss v2 patterns and the standard bubbles/key library. View palettes use typed color.Color instead of raw hex strings, the command palette overlay uses Lipgloss Compositor instead of a brittle line-replacement hack, the Glamour markdown renderer is cached for performance, and all single-char vim bindings (j/k/q/:) are replaced with modifier-based keys (ctrl+c, ctrl+n, ctrl+p, arrows) to eliminate collisions with terminal escape sequences.

Summary

Imagine you've been navigating your house by shouting room names through a megaphone while wearing mismatched shoes. This PR replaces the megaphone with actual doors, gives you matching shoes, and stops the neighbours complaining about the noise. The house is the same; you just look less unhinged walking through it.

Tip

No manual steps required. Keybindings have changed — q no longer quits (use ctrl+c), : no longer opens palette (use ctrl+p), i no longer captures (use ctrl+n). Arrow keys replace j/k/h/l throughout.


Changes

Keybinding system rewritekeyboard.go, app.go, app_test.go
  • Replaced custom KeyAction enum, keyRule table, and KeyMap struct with idiomatic bubbles/key.Binding declarations (AppKeyMap)
  • App-level key dispatch now uses key.Matches() switch instead of handleAction() indirection
  • Removed jumpMsg type — jump-to-top/bottom handled directly by list/viewport KeyMap configuration
  • All single-char bindings removed: qctrl+c, :ctrl+p, ictrl+n, j/k → arrows
  • Tests updated to use modifier key presses; new tests for spurious byte rejection (OSC 11 responses) and ctrl+c-before-ready
Pane keybinding configurationnode_list_pane.go, pane.go, capture.go
  • nodeListPane: reconfigures bubbles/list KeyMap directly — removes j/k/g/G, keeps arrows/pgup/pgdn/home/end
  • viewportPane: reconfigures bubbles/viewport KeyMap — removes j/k/d/u/f/b/space, keeps arrows/pgup/pgdn/ctrl+d/ctrl+u
  • Capture key changed from i to ctrl+n; placeholder text updated
  • KeyBindings() return values updated across both pane types
Lipgloss v2 Compositor overlayapp.go, palette.go
  • Command palette overlay now uses lipgloss.NewCompositor() with NewLayer().X().Y().Z() instead of splitLines/joinLines replacement
  • Palette View() no longer pads itself horizontally — centring delegated to Compositor via Layer.X()
  • Removed splitLines and joinLines helper functions from app.go
  • Selected palette row uses Selection() background (VS.7)
  • Border background set explicitly on palette box to prevent bleed
Status bar polishstatusbar.go, layout.go
  • Status bar now 2 lines: separator () + content bar
  • Added SetKeyHints() — focused pane's bindings shown as compact hints
  • Layout statusBarHeight updated from 1 → 2; border calculation adjusted for Lipgloss v2 outer dimensions
  • Spacer() used for all internal gaps (no more strings.Repeat)
  • syncKeyHints() called on focus change, form open/close, and pane mount
View palette type safetyviews/budget.go, views/list.go, views/prose.go, views/schedule.go, views/timeline.go
  • All palette struct fields changed from string to color.Color (VS.1)
  • Default*Palette() functions return lipgloss.Color(...) typed values
  • Removed all lipgloss.Color(stringField) wrapper calls at render sites — fields used directly
Glamour renderer cachingdetail.go, app.go
  • DetailRenderer now caches its glamour.TermRenderer instance, only recreating when Width changes
  • Model holds a long-lived detailRenderer field instead of creating one per selection
  • Detail rendering moved to async via renderDetailAsync() / detailReadyMsg to keep the event loop responsive
Roadmap updatesdocs/roadmaps/tui.md, docs/diagrams/roadmap-tui.html
  • VS.1, VS.2, VS.6, VS.7, VS.11 marked complete
  • VS.3–VS.5, VS.9, VS.10 unblocked (moved from Blocked → To Do)
  • Mermaid dependency graph pruned: completed nodes removed, edges updated
  • RT.1, CL.1, CL.2 dependency annotations updated (CP.1 marked done)
  • LG.7 duplicate entry consolidated

🤖 Generated with Claude Code

JasonWarrenUK and others added 9 commits March 24, 2026 15:54
All five view renderers (schedule, timeline, prose, budget, list) had
palette structs with `string` fields for hex colours. Change them to
`color.Color` so callers no longer need to wrap values in
`lipgloss.Color()`. `Default*Palette()` functions now return typed
values. Render sites updated to use palette fields directly.

This establishes the correct type boundary — when view pane models are
wired into the `tui` layer, construction sites will override palette
fields from `ActiveTheme`, following the same pattern as `detail.go`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The command palette selected row previously only changed the foreground
colour and added bold — the background remained unchanged, making the
selection invisible at a glance. Now all three style elements
(cursor prefix, name, hint) use `theme.Selection()` as the background
when the row is active.

Also documents that the manual left-pad in View() is intentionally
unstyled — those spaces sit outside the modal border and should be
transparent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…bar (VS.6)

Three improvements to the status bar:
- Separator: a full-width horizontal rule in the theme border colour is
  prepended above the bar, giving a clear visual boundary.
- Keybind hints: a compact hints section (up to 4 bindings) is shown
  between the capture text and the centre node info, using FgMuted().
  Updated by the root model via SetKeyHints() on every focus change.
- Spacer fix: replace spacerStyle.Render(strings.Repeat(...)) with the
  Spacer() utility from render.go, preventing background bleed.

statusBarHeight in layout.go updated to 2 to account for the new
separator line.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The palette overlay previously worked by splitting the frame into lines
and replacing them starting at index 2 — brittle and hard to reason
about.

Replace with lipgloss v2's Compositor API:
- Frame becomes Layer.Z(0) at position (0, 0)
- Palette overlay becomes Layer.Z(1) at (centreX, 2)
- NewCompositor composites them cell-by-cell

Palette.View() no longer manually centres the modal box — horizontal
position is now handled by Layer.X(). The splitLines/joinLines helpers
are removed as they are no longer used.

Also wires syncKeyHints() calls: every focus change and pane mount now
updates the status bar's keybind hints for the newly focused pane.
Adds lipgloss import to app.go (previously unused there).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
VS.3, VS.4, VS.5, VS.9 unblocked (VS.1 dependency resolved).
VS.10 unblocked similarly. VS.8 remains blocked (needs CP.1).
Prune completed VS nodes from Mermaid progress graph.
Update status summary table.
Regenerate roadmap-tui.html dashboard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove custom KeyAction enum, keyRule table, and KeyMap struct. Replace
with idiomatic bubbles/key.Binding declarations (AppKeyMap) that integrate
directly with Bubble Tea's key handling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ender

glamour.NewTermRenderer() probes terminal capabilities and builds a full
goldmark pipeline — far too expensive to call on every node selection.
Cache the renderer instance and only recreate when Width changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Store a long-lived DetailRenderer on the Model instead of recreating it
per selection. Add detailReadyMsg for async detail rendering. Switch
keyMap field from *KeyMap to AppKeyMap to match the new bubbles/key
bindings. Remove unused jumpMsg type.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…binding system

Align PaneModel interface and all pane implementations (node list,
capture bar, palette, layout) with the bubbles/key dispatch pattern.
Remove vim-style jump handlers in favour of standard key bindings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@JasonWarrenUK JasonWarrenUK merged commit ac14498 into main Mar 25, 2026
@JasonWarrenUK JasonWarrenUK deleted the styles/lipgloss-layers branch March 25, 2026 14:50
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.

1 participant