Files
yellowjacket/.planning/phases/14-performance-optimization/14-04-PLAN.md
T

14 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
14-performance-optimization 04 execute 2
14-01
14-02
frontend/src/components/cover-grid/scroll-manager.ts
frontend/src/components/queue-panel/queue-panel.ts
docs/PROFILING.md
false
PERF-SCROLL-03
PERF-DIAG-01
truths artifacts key_links
Cover grid scroll manager uses RAF-throttled scroll events instead of debounced saves
Queue panel scroll correction monkey-patch is replaced with a cleaner CSS/layout solution
A profiling guide documents how to diagnose frontend and backend performance issues
User verifies scrolling smoothness across all views
path provides contains
frontend/src/components/cover-grid/scroll-manager.ts RAF-throttled scroll position saves requestAnimationFrame
path provides contains
docs/PROFILING.md Performance diagnosis guide pprof
from to via pattern
docs/PROFILING.md scripts/profile.sh References profiling script usage profile.sh
Polish scroll performance with targeted fixes to the cover grid scroll manager and queue panel, then create a performance profiling guide and verify all optimizations with user.

Purpose: The cover grid scroll manager uses a 100ms debounced scroll save (fires after scrolling stops, not ideal for position tracking during rapid scrolling). The queue panel has a monkey-patched _correctScrollError which is a band-aid for lit-virtualizer's scroll correction fighting the native scrollbar. Both need cleaner solutions. Additionally, the user wants guidance on diagnosing performance issues using the existing pprof infrastructure.

Output: Cleaner scroll handling, profiling documentation, and user-verified scroll smoothness.

<execution_context> @/home/caleb/.config/opencode/get-shit-done/workflows/execute-plan.md @/home/caleb/.config/opencode/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @frontend/src/components/cover-grid/scroll-manager.ts @frontend/src/components/queue-panel/queue-panel.ts @scripts/profile.sh @backend/profiling/profiling.go Task 1: Optimize scroll event handling and clean up queue panel scroll hack frontend/src/components/cover-grid/scroll-manager.ts frontend/src/components/queue-panel/queue-panel.ts **scroll-manager.ts — RAF-throttled scroll position saving:**

The current scroll position save uses a 100ms debounce timer. This is suboptimal because:

  1. During continuous scrolling, position is never saved (debounce resets on each scroll event)
  2. When scrolling stops, there's a 100ms delay before the position is recorded
  3. If the user navigates away during scrolling (before debounce fires), position is lost

Replace with requestAnimationFrame throttling: save the position once per animation frame. This fires at most once per ~16ms (60fps), captures position during scrolling (not just after), and naturally aligns with the browser's paint cycle.

Pattern:

private scrollRAFId: number | null = null;

private onScroll = () => {
    if (this.scrollRAFId !== null) return;
    this.scrollRAFId = requestAnimationFrame(() => {
        this.scrollRAFId = null;
        // save current scroll position
        this.saveScrollPosition();
    });
};

Find the existing debounced scroll handler in scroll-manager.ts and replace it with this RAF-throttled version. Make sure to:

  • Cancel any pending RAF in destroy() or cleanup method
  • Keep the same saveScrollPosition() logic (storing to library store)

queue-panel.ts — Replace _correctScrollError monkey-patch:

The queue panel currently monkey-patches lit-virtualizer's internal _correctScrollError method to prevent it from fighting the native scrollbar during drag scrolling. This was documented as a fix for the "scroll bar not following" issue.

Instead of monkey-patching an internal API (which could break on lit-virtualizer updates), use CSS overflow-anchor: none on the virtualizer's scroll container. This CSS property tells the browser NOT to automatically adjust scroll position when content changes above the viewport — which is the same thing _correctScrollError does but from the browser side.

Add to the queue panel's lit-virtualizer CSS:

lit-virtualizer {
    overflow-anchor: none;
}

Then check if the monkey-patch can be removed. If overflow-anchor: none alone resolves the scrollbar desync, remove the monkey-patch code entirely. If the monkey-patch is still needed for a specific scenario (like the gutter click detection for native scrollbar drag), keep only the gutter detection part and remove the scroll error correction override.

Important: Test the queue panel with a large queue (5000+ tracks) and verify:

  1. Native scrollbar drag works smoothly (no jumping/fighting)
  2. Keyboard navigation (arrow keys) doesn't cause scroll jumps
  3. Auto-scroll to current track works
  4. The queue panel scrolls smoothly when dragging tracks to reorder

If overflow-anchor: none doesn't fully replace the monkey-patch, keep the monkey-patch but add a comment explaining WHY it's needed and what the CSS alone doesn't handle. cd frontend && npx vite build --mode development 2>&1 | tail -5 builds without errors. Cover grid: scroll position saves continuously during scrolling (not just after stopping). Queue panel: scrollbar drag on 5000+ item queue works without scroll fighting. Cover grid scroll position saves are RAF-throttled (once per frame). Queue panel scroll handling is cleaned up (overflow-anchor or documented monkey-patch).

Task 2: Create performance profiling guide docs/PROFILING.md Create a practical profiling guide at `docs/PROFILING.md` that documents how to diagnose performance issues in YellowJacket. This should be a concise, actionable reference — not a textbook.

Structure:

1. Backend Profiling (Go / pprof)

Setup: make dev starts the app with pprof server on :6060.

Quick Start:

  • ./scripts/profile.sh — interactive menu
  • ./scripts/profile.sh cpu — 30s CPU profile (flame graph in browser)
  • ./scripts/profile.sh heap — current memory usage
  • ./scripts/profile.sh health — goroutine count, heap, GC stats

When to use each profile type:

Profile Use When What It Shows
CPU Something is slow Time spent in each function (flame graph)
Heap Memory growing Current allocations by location
Allocs GC pressure Where allocations happen (even freed)
Goroutine Hangs/deadlocks All goroutines and their stack traces
Block Lock contention Where goroutines block on mutexes/channels
Mutex Mutex bottleneck Mutex contention hotspots
Trace Scheduling issues Timeline of goroutine scheduling, GC pauses, syscalls

Reading flame graphs:

  • Wide bars = more time spent
  • Look for unexpected width (functions taking more time than expected)
  • Bottom of stack = entry points, top = leaf functions where time is actually spent
  • Use the search box to find specific packages (e.g., "library", "queue", "database")

Common YellowJacket hotspots:

  • database.GetAllTracks — large library, check SQL query time
  • library.extractMetadata — scan performance, check per-format timing in scan metrics
  • queue.SetQueue — Phase 1/2 dedup, check with large queues
  • coverart.Generate* — thumbnail generation, check per-tier timing

2. Frontend Profiling (Chrome DevTools)

Since YellowJacket uses Wails (WebView2/WebKit), you can use Chrome DevTools for frontend profiling.

Opening DevTools:

  • On Wails dev builds, press Ctrl+Shift+I (or right-click → Inspect)

Performance Panel (scrolling/rendering):

  1. Open Performance panel
  2. Click Record
  3. Perform the action (scroll, navigate, etc.)
  4. Stop recording
  5. Look at the Main thread timeline:
    • Long yellow bars = JavaScript execution (too long = jank)
    • Purple bars = rendering/layout
    • Green bars = painting
    • Grey bars = idle
  6. Target: each frame should be <16ms for 60fps scrolling

Key metrics for scroll smoothness:

  • Frame time: Should be consistently <16ms
  • Layout recalculation: Should not happen during scrolling (if it does, contain CSS isn't working)
  • Paint: Should be minimal and composited (green bars should be thin)
  • JS execution during scroll: Should be minimal — lit-virtualizer does most work, but renderItem callbacks add up

Memory Panel:

  1. Take heap snapshot before/after an action
  2. Compare snapshots to find leaks
  3. Look for growing arrays of detached DOM nodes (sign of view not cleaning up)

What to look for in YellowJacket:

Symptom Likely Cause Check
Scroll jank Layout thrashing Performance panel → check for "Layout" bars during scroll
Slow navigation View recreation Performance panel → look for long constructors after navigate
Memory growth Listener leaks Memory panel → compare snapshots, filter "Detached"
Slow initial load Blocking JS Performance panel → check DOMContentLoaded to first paint

3. Profiling Workflow for Specific Issues

"Scrolling feels janky":

  1. Open DevTools Performance panel
  2. Record while scrolling the problematic view
  3. Look at frame times — are any >16ms?
  4. If JS is the bottleneck: check renderItem callback time
  5. If Layout is the bottleneck: check if contain CSS is present
  6. If Paint is the bottleneck: check if will-change: transform is on the scroll container

"Navigation is slow":

  1. Open DevTools Performance panel
  2. Record while navigating between views
  3. Look for long JS tasks between navigate event and first paint
  4. Check if the view is being destroyed/recreated (look for constructor calls)
  5. After Phase 14 view caching: navigation between cached views should show almost no activity

"Library operations feel slow":

  1. ./scripts/profile.sh cpu — capture during the operation
  2. Check the flame graph for the specific Go function
  3. For database operations: check if SQL queries are optimal
  4. For scan operations: check scan metrics (they're already logged)
  5. ./scripts/profile.sh trace — for detailed timing of goroutine scheduling test -f docs/PROFILING.md && echo "File exists" outputs "File exists". The file contains sections on Backend Profiling, Frontend Profiling, and Profiling Workflow. docs/PROFILING.md exists with practical guidance on using pprof, Chrome DevTools Performance panel, and specific diagnostic workflows for scrolling, navigation, and library operation performance issues.
Task 3: Verify performance improvements none Phase 14 performance optimizations across 4 plans: - CSS containment + GPU layer promotion on all scroll containers (Plan 01) - View caching navigation (no more innerHTML destruction) (Plan 02) - Render closure elimination + store notification optimization (Plan 03) - Scroll event handling cleanup + profiling guide (Plan 04)

Verification steps:

  1. Run make dev to start the app
  2. Test scrolling smoothness:
    • Open the track list view → scroll rapidly up and down → should feel smooth, no jank or stuttering
    • Open the album grid → scroll rapidly → should be smooth, no blank areas appearing
    • Open the queue panel → add 1000+ tracks → scroll rapidly → should be smooth
    • Open artists view → scroll → smooth
    • Open genres view → scroll → smooth
  3. Test navigation speed:
    • Click Tracks → Albums → Tracks rapidly → should feel instant (no flash of loading)
    • Click Albums → Artists → Genres → Playlists → Settings → Tracks → each transition should be near-instant
    • Navigate to an artist detail → back to artists → artists view should still have its scroll position
  4. Test that nothing broke:
    • Play a track from track list (double-click)
    • Right-click → context menu works
    • Drag tracks to queue
    • Multi-select with Shift/Ctrl+click
    • Search filters correctly
    • Album dropdown (click album in grid → tracks show)
    • Queue reorder via drag
  5. Read profiling guide:
    • Open docs/PROFILING.md
    • Does it make sense? Any confusing parts?
    • Try ./scripts/profile.sh health — does it connect? User approves scrolling smoothness and navigation speed User has verified that scrolling is smooth, navigation is instant, and no interactions are broken. Type "approved" or describe issues found.
Full Phase 14 verification: 1. Scrolling is measurably smoother across all views 2. Navigation between views is near-instant (cached views) 3. No visual regressions (context menus, popups, dropdowns, drag-drop) 4. Build succeeds: `cd frontend && npx vite build --mode development` 5. Go backend builds: `go build -tags webkit2_41 ./...` 6. PROFILING.md provides actionable guidance

<success_criteria>

  • Cover grid scroll position saves use RAF throttling
  • Queue panel scroll handling is cleaner (overflow-anchor or documented hack)
  • docs/PROFILING.md exists with Backend, Frontend, and Workflow sections
  • User approves scrolling smoothness in the human-verify checkpoint </success_criteria>
After completion, create `.planning/phases/14-performance-optimization/14-04-SUMMARY.md`