Skip to content

perf: precompute session-scoped style set to eliminate per-frame allocations - #97

Merged
deveshctl merged 3 commits into
mainfrom
feat/tui-style-cache
Aug 6, 2026
Merged

perf: precompute session-scoped style set to eliminate per-frame allocations#97
deveshctl merged 3 commits into
mainfrom
feat/tui-style-cache

Conversation

@deveshctl

Copy link
Copy Markdown
Owner

Summary

  • All theme-derived lipgloss.Style values are now built once at startup into a themeStyles struct (tui/styles.go) and reused for every frame. The theme is immutable for the session, so the ~300–400 per-frame style allocations that were rebuilding identical structs on every View() call are eliminated.
  • The pre-built style set is stored on the model as m.styles and threaded through the internal render structs (treePaneInput, splitTreeInput, viewerParams) so the hot per-row renderers read a field rather than calling lipgloss.NewStyle().
  • Dynamic styles — those whose color depends on per-row state (diff type, delta sign, cursor selection, search match position) — remain as inline allocations since their inputs vary per call.
  • All test call sites updated to pass themeStyles{} (zero value is correct for structural tests that check rendered content, not color).

Changes

  • tui/styles.go — new themeStyles struct (26 fields) and newThemeStyles(Theme) constructor
  • tui/model.gostyles themeStyles field on model; static calls replaced in renderHeader, renderStatusBar, renderViewerStatusBar, viewLoading, viewError, separator line
  • tui/filetree.gotreePaneInput/splitTreeInput carry styles; static calls replaced in renderTreeHeader, renderFilterBar, renderSplitDivider, formatFileNodeLine, renderNameWithHighlight
  • tui/layers.go — static calls replaced in renderCommandBar, highlightInstruction, formatLayerLine, renderSizeColumn
  • tui/fileview.goviewerParams carries styles; static calls replaced in renderViewerLine, renderViewerSearchBar, overlayCursor, gutter, and binary/empty/truncation notices

Test plan

  • go build ./... and go vet ./... clean
  • CI passes (go test ./...)
  • On a real image: navigate the file tree, toggle sort, open the viewer, activate search — visual output identical to before; held-key scroll and search responsiveness improved on large layers

… allocations

The theme is fixed at startup and never changes, yet every frame built
~300-400 fresh lipgloss.Style structs via styleWithFg / lipgloss.NewStyle()
calls scattered across the hot render paths.

Added a themeStyles struct (tui/styles.go) with 26 pre-built Style fields,
populated once in NewModel via newThemeStyles(theme) and stored as m.styles.
Threaded through internal structs (treePaneInput, splitTreeInput,
viewerParams) so the hot per-row renderers read a field instead of
allocating.

Replaced all static styleWithFg / lipgloss.NewStyle() calls in:
- tui/model.go   — renderHeader, renderStatusBar, renderViewerStatusBar,
                   viewLoading, viewError, separator line
- tui/filetree.go — renderTreeHeader, renderFilterBar, renderSplitDivider,
                   formatFileNodeLine, renderNameWithHighlight
- tui/layers.go  — renderCommandBar, highlightInstruction, formatLayerLine,
                   renderSizeColumn
- tui/fileview.go — renderViewerLine, renderViewerSearchBar, overlayCursor,
                   gutter, truncation/binary/empty notices

Dynamic styles (per-row diff color, delta color, selected-row bg,
search-current highlight) remain inline as their color varies per call.

Updated all test call sites to pass themeStyles{} (zero value is correct
for structural tests that check content not color).
The sb.WriteString(pad) line in renderTreeBody's empty-tree placeholder
was indented one tab short of its sibling statement, a whitespace glitch
introduced alongside the style-cache change. Pure formatting; no behaviour
change.
The botBody treePaneInput literal in renderSplitFileTree was missing
styles: in.styles, so the Cumulative pane in aggregated split mode
rendered with a zero-value themeStyles — no colors, no diff highlighting,
no selection background, no search styling. topBody was correctly
threaded; this brings botBody in line.
@deveshctl
deveshctl merged commit 54dff80 into main Aug 6, 2026
24 checks passed
@deveshctl
deveshctl deleted the feat/tui-style-cache branch August 7, 2026 14:23
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