Files
yellowjacket/.planning/milestones/v1.0-phases/08-frontend-performance-ux/08-01-PLAN.md
T
yonlu 6ce0661fca chore: complete v1.0 Consolidation milestone
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
2026-03-05 09:34:43 -05:00

9.0 KiB
Raw Blame History

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 01 execute 1
frontend/src/store/library-store.ts
frontend/src/components/search-bar/search-bar.ts
frontend/src/styles/tokens.css.ts
true
PERF-05
UX-01
truths artifacts key_links
Library store notifications during rapid updates (scan, invalidation) are coalesced into a single subscriber notification per microtask tick
CSS custom properties for icon sizing (--yj-icon-sm, --yj-icon-md, --yj-icon-lg) and type scale (--yj-text-xs through --yj-text-lg) are defined and available to all components
Search input is debounced ~150ms before triggering filter/rank computation
path provides contains
frontend/src/store/library-store.ts queueMicrotask-based notification coalescing queueMicrotask
path provides contains
frontend/src/styles/tokens.css.ts Design token definitions for icon sizes and type scale --yj-icon-sm
path provides contains
frontend/src/components/search-bar/search-bar.ts Debounced search input debounce
from to via pattern
frontend/src/store/library-store.ts subscribers queueMicrotask coalescing in notify() queueMicrotask
from to via pattern
frontend/src/styles/tokens.css.ts all components CSS custom property inheritance from :host or adopted stylesheets --yj-icon-sm|--yj-text-xs
Add performance plumbing (store debouncing, search debounce) and define the design token foundation (icon sizes, type scale) that all subsequent plans depend on.

Purpose: Library store fires 8+ notifications during scan invalidation (4 parallel fetches × 2 notifications each). Coalescing via queueMicrotask prevents layout thrashing. Design tokens establish the visual vocabulary that Plan 04 will systematically apply. Output: Debounced store, debounced search, design token CSS file.

<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

@frontend/src/store/library-store.ts @frontend/src/components/search-bar/search-bar.ts

From frontend/src/store/library-store.ts:

type Subscriber = () => void;

class LibraryStore {
    private subscribers = new Set<Subscriber>();

    // Current notify — called ~12 times during invalidate→eagerFetch cycle:
    private notify(): void {
        this.subscribers.forEach((callback) => callback());
    }

    // Called from: getTracks/getAlbums/getArtists/getGenres (loading start + end),
    // invalidate(), setCoverSize()

    subscribe(callback: Subscriber): () => void {
        this.subscribers.add(callback);
        return () => this.subscribers.delete(callback);
    }
}

export const libraryStore = new LibraryStore();

From frontend/src/store/search-store.ts:

class SearchStore {
    private term = '';
    setTerm(term: string): void {
        if (term === this.term) return;
        this.term = term;
        this.notify();
    }
}
export const searchStore = new SearchStore();

From frontend/src/components/search-bar/search-bar.ts:

// Current: directly sets search term on every input event
// searchCtrl is a SearchController with a `term` setter
this.searchCtrl.term = input.value;

Existing CSS custom properties (already defined, DO NOT redefine):

  • --yj-text-primary, --yj-text-secondary, --yj-text-tertiary
  • --yj-bg-surface, --yj-bg-elevated, --yj-bg-overlay, --yj-bg-base
  • --yj-border, --yj-border-subtle
  • --yj-accent, --yj-accent-bg
  • --yj-hover-overlay, --yj-selection-bg, --yj-error
Task 1: Add queueMicrotask debouncing to library store and search input debounce frontend/src/store/library-store.ts, frontend/src/components/search-bar/search-bar.ts **Library store debouncing (library-store.ts):**

Replace the current notify() method with a queueMicrotask-based coalescing pattern:

  1. Add a private boolean field private notifyScheduled = false;
  2. Replace notify() implementation:
    private notify(): void {
        if (this.notifyScheduled) return;
        this.notifyScheduled = true;
        queueMicrotask(() => {
            this.notifyScheduled = false;
            this.subscribers.forEach((callback) => callback());
        });
    }
    

This coalesces ALL notify() calls within the same microtask tick into a single subscriber notification round. During invalidate() → eagerFetch() → 4 parallel fetches × 2 notifications each = 8+ calls → 1 actual notification.

The subscribe() API is unchanged — this is transparent to subscribers.

Search input debounce (search-bar.ts):

Add a ~150ms debounce to the search input handler so that rapid typing doesn't trigger expensive filter/rank computation on every keystroke.

  1. Add a private timer field: private searchDebounceTimer: ReturnType<typeof setTimeout> | null = null;
  2. In the input handler, instead of immediately setting this.searchCtrl.term = input.value:
    • Clear any existing timer
    • If the input is empty, set term immediately (instant clear feedback)
    • Otherwise, set a 150ms timeout that sets this.searchCtrl.term

Do NOT debounce the visual update of the input field itself — only debounce the propagation to the search store. The input should still show characters as typed. cd frontend && npx tsc --noEmit 2>&1 | head -30 Library store notify() uses queueMicrotask to coalesce multiple calls per tick. Search input debounces store propagation by 150ms while maintaining instant visual feedback on the input element.

Task 2: Define design token CSS custom properties for icon sizes and type scale frontend/src/styles/tokens.css.ts Create a new file `frontend/src/styles/tokens.css.ts` that exports a Lit `css` tagged template with design token definitions.

Use the same pattern as other style files in the project — export a css tagged template literal from lit.

import { css } from 'lit';

/**
 * Design tokens for consistent sizing across all components.
 * Import and include in a component's static styles array:
 *
 *   import { designTokens } from '../../styles/tokens.css';
 *   static styles = [designTokens, css`...`];
 */
export const designTokens = css`
    :host {
        /* ── Icon sizes ── */
        --yj-icon-sm: 14px;
        --yj-icon-md: 18px;
        --yj-icon-lg: 24px;

        /* ── Type scale ── */
        --yj-text-xs: 11px;
        --yj-text-sm: 12px;
        --yj-text-md: 13px;
        --yj-text-lg: 15px;
        --yj-text-xl: 18px;
    }
`;

Design rationale:

  • Icon sizes: sm=14px covers small inline icons (favorites, sort indicators), md=18px covers standard toolbar/sidebar icons, lg=24px covers feature icons (now-playing placeholder, large action icons)
  • Type scale: xs=11px for smallest text (cover-grid small cards), sm=12px for secondary info and labels, md=13px for body text and inputs, lg=15px for headings and emphasis, xl=18px for large titles
  • These values are derived from the actual pixel values already scattered across the codebase — this consolidates them rather than inventing new sizes
  • :host scope means tokens are available within each component that imports the stylesheet

Verify the file path exists: check for a frontend/src/styles/ directory. If it doesn't exist, create it. cd frontend && npx tsc --noEmit 2>&1 | head -30 Design token file exists at frontend/src/styles/tokens.css.ts, exports designTokens css template with --yj-icon-sm/md/lg and --yj-text-xs/sm/md/lg/xl custom properties on :host.

1. `cd frontend && npx tsc --noEmit` compiles without errors 2. library-store.ts contains `queueMicrotask` in the notify method 3. search-bar.ts has debounce logic with ~150ms delay 4. frontend/src/styles/tokens.css.ts exists and exports designTokens 5. No behavioral regressions — subscribe() API is unchanged, search still works

<success_criteria>

  • Library store notify() coalesces multiple calls within a microtask tick into one notification round
  • Search input propagation to store is debounced by ~150ms (empty input clears immediately)
  • Design token file defines --yj-icon-sm/md/lg and --yj-text-xs/sm/md/lg/xl
  • TypeScript compiles without errors </success_criteria>
After completion, create `.planning/phases/08-frontend-performance-ux/08-01-SUMMARY.md`