295 lines
14 KiB
Markdown
295 lines
14 KiB
Markdown
---
|
|
phase: 14-performance-optimization
|
|
plan: 04
|
|
type: execute
|
|
wave: 2
|
|
depends_on: ["14-01", "14-02"]
|
|
files_modified:
|
|
- frontend/src/components/cover-grid/scroll-manager.ts
|
|
- frontend/src/components/queue-panel/queue-panel.ts
|
|
- docs/PROFILING.md
|
|
autonomous: false
|
|
requirements: [PERF-SCROLL-03, PERF-DIAG-01]
|
|
|
|
must_haves:
|
|
truths:
|
|
- "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"
|
|
artifacts:
|
|
- path: "frontend/src/components/cover-grid/scroll-manager.ts"
|
|
provides: "RAF-throttled scroll position saves"
|
|
contains: "requestAnimationFrame"
|
|
- path: "docs/PROFILING.md"
|
|
provides: "Performance diagnosis guide"
|
|
contains: "pprof"
|
|
key_links:
|
|
- from: "docs/PROFILING.md"
|
|
to: "scripts/profile.sh"
|
|
via: "References profiling script usage"
|
|
pattern: "profile.sh"
|
|
---
|
|
|
|
<objective>
|
|
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.
|
|
</objective>
|
|
|
|
<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>
|
|
|
|
<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
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Optimize scroll event handling and clean up queue panel scroll hack</name>
|
|
<files>
|
|
frontend/src/components/cover-grid/scroll-manager.ts
|
|
frontend/src/components/queue-panel/queue-panel.ts
|
|
</files>
|
|
<action>
|
|
**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:
|
|
```typescript
|
|
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:
|
|
```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.
|
|
</action>
|
|
<verify>
|
|
`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.
|
|
</verify>
|
|
<done>Cover grid scroll position saves are RAF-throttled (once per frame). Queue panel scroll handling is cleaned up (overflow-anchor or documented monkey-patch).</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: Create performance profiling guide</name>
|
|
<files>docs/PROFILING.md</files>
|
|
<action>
|
|
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
|
|
</action>
|
|
<verify>
|
|
`test -f docs/PROFILING.md && echo "File exists"` outputs "File exists".
|
|
The file contains sections on Backend Profiling, Frontend Profiling, and Profiling Workflow.
|
|
</verify>
|
|
<done>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.</done>
|
|
</task>
|
|
|
|
<task type="checkpoint:human-verify" gate="blocking">
|
|
<name>Task 3: Verify performance improvements</name>
|
|
<files>none</files>
|
|
<action>
|
|
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?
|
|
</action>
|
|
<verify>User approves scrolling smoothness and navigation speed</verify>
|
|
<done>User has verified that scrolling is smooth, navigation is instant, and no interactions are broken. Type "approved" or describe issues found.</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
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
|
|
</verification>
|
|
|
|
<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>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/14-performance-optimization/14-04-SUMMARY.md`
|
|
</output>
|