Archive milestone artifacts: - milestones/v1.0-ROADMAP.md (full roadmap archive) - milestones/v1.0-REQUIREMENTS.md (26/26 requirements complete) - milestones/v1.0-phases/ (8 phase directories with plans, summaries, verifications) Updated: - PROJECT.md: full evolution review, all consolidation requirements validated - ROADMAP.md: collapsed to milestone summary with archive link - STATE.md: reset for next milestone - MILESTONES.md: created with stats and accomplishments - RETROSPECTIVE.md: created with lessons learned Deleted: - REQUIREMENTS.md (archived, fresh for next milestone) 8 phases, 17 plans, 34 tasks, 84 tests added, 6 days
8.0 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 | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 08-frontend-performance-ux | 03 | execute | 2 |
|
|
true |
|
|
Purpose: renderTrackRow is the hot path for the largest list component. It's called for every visible row on every scroll event. Current implementation builds CSS class strings via array filter/join and computes column values per-cell on every call. With 10k+ tracks, reducing per-row work directly impacts scroll smoothness. Output: Optimized renderTrackRow with cached class strings and efficient column rendering.
<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 @.planning/phases/08-frontend-performance-ux/08-CONTEXT.md @.planning/phases/08-frontend-performance-ux/08-01-SUMMARY.md @.planning/phases/08-frontend-performance-ux/08-02-SUMMARY.md@frontend/src/components/track-list/track-list.ts
Current renderTrackRow pattern (approximate):
private renderTrackRow = (track: library.Track, index: number) => {
// 1. Class string built via array filter/join on EVERY render:
const classes = [
'track-row',
this.isSelected(track) ? 'selected' : '',
this.isCurrentTrack(track) ? 'playing' : '',
// ... more conditions
].filter(Boolean).join(' ');
// 2. Column values computed per-cell via accessor:
// col.accessor(track) called for each column on each row
// 3. Search highlighting applied per-cell
};
Optimization targets:
- Replace array filter/join class construction with Lit's classMap directive
- Pre-compute or cache column accessor results where possible
- Avoid object/array allocations in the render hot path
Replace with Lit's classMap directive which is purpose-built for conditional classes and avoids these allocations:
- Add import:
import { classMap } from 'lit/directives/class-map.js';(if not already imported) - In renderTrackRow, find every pattern like:
const classes = ['base-class', condition ? 'class-a' : '', ...].filter(Boolean).join(' '); // Used as: class="${classes}" - Replace with:
// Used as: class=${classMap({ 'base-class': true, 'class-a': condition, ... })}
Read the full renderTrackRow method carefully — there may be multiple class string constructions (row-level and cell-level). Convert ALL of them.
The classMap object literal is still allocated per-call, but classMap internally compares with previous values and only updates changed classes — it's significantly faster than string concatenation for Lit's update cycle.
Also check renderTrackItem in queue-panel.ts for the same pattern — if it uses array filter/join for classes, apply the same classMap conversion there too. (Queue panel was listed in CONTEXT.md as having this pattern.)
cd frontend && npx tsc --noEmit 2>&1 | head -30
All class string construction in renderTrackRow uses classMap directive instead of array filter/join. No .filter(Boolean).join(' ') patterns remain in track-list render methods. TypeScript compiles.
Read the full renderTrackRow method to understand how column values are computed. The current pattern calls col.accessor(track) for each visible column on each row during render.
Optimization approach — evaluate what's actually expensive:
- If
col.accessoris a simple property lookup (e.g.,track.Title,track.Artist), it's already fast — no caching needed - If any accessor does computation (string formatting, duration conversion, etc.), consider whether it can be memoized or moved outside the per-cell loop
- If search highlighting is applied per-cell, check if the highlight computation can be short-circuited when there's no active search term (skip the regex/string manipulation entirely when term is empty)
Focus on the highest-impact optimizations:
- Search highlight short-circuit: When searchTerm is empty, skip all highlight logic entirely — just render the raw column value. This eliminates regex creation and string splitting for every cell in the common case.
- Duration formatting: If a time/duration column reformats on every render, cache the formatted string on the track object or in a WeakMap.
Do NOT over-optimize — if accessor is just track.Title, a cache would be slower than the direct access. Only optimize where measurement or code inspection shows actual waste.
Queue-panel classMap (queue-panel.ts):
Apply the same classMap directive conversion to renderTrackItem in queue-panel.ts:
- Add import:
import { classMap } from 'lit/directives/class-map.js'; - Find the class string construction pattern (array filter/join) in renderTrackItem
- Convert to classMap directive (same pattern as Task 1) cd frontend && npx tsc --noEmit 2>&1 | head -30 Track-list search highlighting is short-circuited when search term is empty. Queue-panel renderTrackItem uses classMap. No unnecessary per-row allocations in render hot paths. TypeScript compiles.
<success_criteria>
- renderTrackRow uses classMap for all conditional CSS classes
- renderTrackItem (queue) uses classMap for all conditional CSS classes
- Search highlighting skips computation when search term is empty
- No array allocations (filter/join) in render hot paths
- TypeScript compiles without errors </success_criteria>