Skip to content

perf: redraw on change, cache catalogue reads, keep album art alive in tmux - #3

Merged
odiumuniverse merged 1 commit into
mainfrom
perf/redraw-cache-tmux-art
Jul 28, 2026
Merged

perf: redraw on change, cache catalogue reads, keep album art alive in tmux#3
odiumuniverse merged 1 commit into
mainfrom
perf/redraw-cache-tmux-art

Conversation

@odiumuniverse

Copy link
Copy Markdown
Owner

Chasing a torn-looking scroll turned up three separate things.

Redraws ran on a timer, not on changes. A sample of the running process spent 419 of 6066 samples on the main thread, almost all of it drawing frames identical to the previous one. Holding an arrow key waited up to 100ms per step, because a keypress didn't force a frame — only the mouse and resize did. Now input redraws within one 16ms refresh, animation runs at 30fps, and an untouched screen at 2fps. The visualizer only counts as animating while Now Playing is on screen, and the queue refresh + state save moved off the frame counter onto a timer — at 60fps they were firing every four seconds.

Every artist page went back to the network. Catalogue reads and album art are cached under ~/.cache/myx/api, and a stale entry is served when a request fails — a spent development-mode quota is exactly the moment an artist page would otherwise come up empty. Failed cover requests are never cached (image entries don't expire, so an error body would mean a permanently broken cover), writes go through temp + rename, and entries older than 30 days are swept once per run.

Album art vanished on a tmux window switch. Not a redraw problem: kitty and iTerm2 images travel as tmux passthrough, which tmux doesn't parse or store, so when it repaints a pane from its own buffer the image was never there. Sixel is the one protocol tmux keeps and repaints itself, so where tmux reports sixel support that's what gets drawn — unwrapped, since ratatui-image's passthrough wrapper is what would defeat it. WezTerm and Warp get iTerm2 instead of kitty: both answer the kitty query but place no unicode placeholders, which is how kitty is drawn here, so the cover came out as a see-through hole.

Also: ~/.config/myx/config.toml is written on first run with every key commented out, plus a protocol key for when detection picks wrong.

54 tests pass, clippy and fmt are clean. An ignored encode_cost benchmark records what a cover re-encode costs: kitty 1.5ms, iTerm2 1.9ms, halfblocks 2.6ms, sixel 8.7ms.

…n tmux

Three things that turned up while chasing a jerky scroll: the UI redrew on a
fixed schedule no matter what changed, every artist page went back to the
network, and inline album art vanished the moment you switched tmux windows.
A sample of the running process spent 419 of 6066 samples on the main thread —
all of it redrawing frames identical to the last one.

Redraws

- Draw when something changed rather than on a timer: input redraws within one
  16ms refresh, animation runs at 30fps, an untouched screen at 2fps instead of
  60. Holding an arrow key waited up to 100ms per step before, which is what
  made scrolling look torn
- Count the visualizer as animating only while Now Playing is on screen
- Move the queue refresh and state save off the frame counter onto a timer; at
  60fps they were firing every four seconds, burning API quota and disk writes

Cache

- Cache catalogue reads and album art under ~/.cache/myx/api, and serve a stale
  entry when a request fails — a spent development-mode quota is exactly when
  an artist page would otherwise come up empty
- Never cache a failed cover request: image entries never expire, so an error
  body would mean a permanently broken cover
- Write through a temp file and rename, and sweep entries older than 30 days

Album art

- Draw sixel, unwrapped, where tmux reports sixel support. It is the only image
  protocol tmux parses and repaints from its own buffer; kitty and iTerm2 ride
  through as passthrough, untracked, and are gone on the next repaint
- Give WezTerm and Warp iTerm2 images. Both answer the kitty query but neither
  places unicode placeholders, which is how ratatui-image draws kitty, so the
  cover came out as a see-through hole
- Re-send the art on a view switch, and clear the box when there is no cover,
  so another view can't show through cells the image widget marks as skipped

Config

- Write ~/.config/myx/config.toml on first run with every key commented out
- Add a protocol key (kitty, iterm2, sixel, halfblocks) for when detection
  picks wrong; MYX_PROTOCOL still overrides it

54 tests, plus an ignored encode_cost benchmark: kitty 1.5ms, iTerm2 1.9ms,
halfblocks 2.6ms, sixel 8.7ms per re-encode.
@odiumuniverse
odiumuniverse merged commit d765f0a into main Jul 28, 2026
11 checks passed
@odiumuniverse
odiumuniverse deleted the perf/redraw-cache-tmux-art branch July 28, 2026 10:19
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