Journal — 2026-03-19
2026-03-19 — TUI Glamour Timeout Fix
A user reported the TUI taking ~60 seconds to render content, stuck on "Loading..." while the CLI responded instantly. Mouse scroll would unstick it.
Root cause: glamour.WithAutoStyle() queries the terminal background via OSC 11, which reads from stdin. Bubbletea also owns stdin, so the two raced. The renderer was recreated on every window resize, compounding the timeout.
Fix: detect dark/light once in main() before Bubbletea starts, cache the result, use glamour.WithStandardStyle() for all subsequent renderers. Added -style flag (dark/light/auto) so users can bypass detection entirely. Guard checks both stdin and stdout are terminals before probing.
Key lesson: never do terminal escape sequence queries inside a Bubbletea event loop. Anything that reads stdin will race with Bubbletea's input goroutine.
2026-03-19 — TUI Link Highlighting & Clickable Links
Added interactive link support to the TUI:
- Highlight on Tab: when tabbing through links, the selected link text and URL are highlighted with ANSI reverse video in the viewport. Viewport auto-scrolls to keep the selected link visible.
- Mouse hover: moving the mouse over a link highlights it. Required switching from
WithMouseCellMotiontoWithMouseAllMotion(cell motion only fires with button held). - Click to navigate: clicking on a link's text or URL navigates to that document.
Implementation: Marker injection approach. links.ExtractWithPositions walks the goldmark AST to get link byte offsets. injectLinkMarkers inserts Unicode PUA codepoints around each link's text before glamour renders. processMarkers scans the ANSI output, extracts linkRegion coordinates, strips markers, and injects highlight codes. The marker-injected glamour output is cached (markedRendered) so Tab and hover just re-run the fast processMarkers pass without re-rendering glamour.
Goldmark AST quirks discovered: Link nodes don't expose bracket positions directly. Text segments don't cover inline formatting markers. link.Text() is deprecated. Links with no text nodes need special handling. All documented in debugging.md.
Copilot review catches: marker range overflow for 4096+ links, stale marker state on error views, insertAt O(n*m) rewritten to single-pass builder, processMarkers per-line reset losing state on wrapped links (tracked as debt), stale comment on reverse-order processing, cyclomatic complexity in handleMouse (extracted handleMouseHover).