feat(ui): D.1 primitives complete — DeviceRack, SignalChain, MetricTile et al. - #65
Conversation
…le et al. PR 2 of 16 in the UI consistency overhaul. Lands the rest of the primitive library on top of the D.0 foundation. No consumer migrations in this PR. New primitives (apps/ui/src/components/ui/): - DeviceRack: the Live 12 device chrome (title strip + LED + body + optional signal rail). Status × density variants. The centerpiece the rest of the migration will build on. - SignalChain: composes DeviceRack + ChainSeparator into the horizontal stage pipeline (queued → active → success cables animate). AnalysisStatusPanel becomes this in PR 4. - ChainSeparator: cable + arrow between racked devices. Tones idle/active/success; animated variant. - MetricTile: Live compressor/EQ-style parameter display. Label eyebrow + tabular-nums value + optional unit, status LED, accent stripe. Replaces AccentMetricCard. - MetricBar / MetricBarRow: API-parity lift of the existing helpers from MeasurementPrimitives so the consumer migration is mechanical. - DataTable: API-parity lift of StyledDataTable. - EmptyState: unifies the ad-hoc "no data" / "feature off" / "failed" banners (UnavailableMeasurementCard, session-musician off-banner, etc.). - TimeReadout: tabular-nums elapsed + estimate readout for AnalysisStatusPanel and similar. Storybook coverage: - Per-primitive default + variant matrix. - DeviceRack stories named 01-04 as the "Live 12 Vocabulary" anchors (EmptyShell, WithParameterGrid, InChain, ActiveStateProgression) — exist so future contributors don't reintroduce drift. - SignalChain stories cover every stage-status combo plus vertical orientation. Tooltip migration: - components/Tooltip.tsx becomes a re-export shim for ui/Tooltip.tsx. Old custom + Framer Motion implementation removed. No current consumers, so no provider mounting needed yet — that lands when the first consumer (D.4+) imports it. Verified: lint clean, 605/605 unit tests passing, build clean (90.66 KB CSS / 14.75 KB gzipped), build-storybook clean. MeasurementPrimitives.tsx → new-primitive mapping table (for future consumer-migration PRs): StatusBadge → Pill (tone) DeltaBadge → keep specialized (signed-delta number) MetricBar → MetricBar (drop-in) MetricBarRow → MetricBarRow (drop-in) AccentMetricCard → MetricTile (accent + status props) TokenBadgeList → map() of Pill StyledDataTable → DataTable (drop-in, rename column types) OutlinePillButton → Button variant="secondary" or "ghost" UnavailableMetricCrd → EmptyState LaneContainer/Row/ → leave in MeasurementPrimitives — these are TimeRuler/StatsBar timeline-lane primitives, not generic chrome. BADGE_TONE_CLASSES → drop (Pill owns tone styling) ACCENT_CARD_CLASSES → drop (MetricTile owns accent stripe styling) https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe
slittycode
left a comment
There was a problem hiding this comment.
Verdict: APPROVE
Summary
Adds 8 new UI primitives to apps/ui/src/components/ui/ and converts Tooltip.tsx to a Radix-backed re-export shim. Pure UI layer — no Phase 1/2/3 code, no backend files, no measurement schema touched. Chromatic passes; Frontend/Backend CI still in-progress at review time but neither is reachable from these changes. PR description reports 605/605 unit tests passing, consistent with no service-layer changes.
Findings
Worth considering
-
DeviceRack.tsxsignal rail — doubleml-auto(signal rail block, ~L97–L107): whensignalIn,railContent, andsignalOutare all set simultaneously, bothrailContentandsignalOutcarryml-auto. In a flex row, only the firstml-autoelement claims available space;signalOutends up adjacent torailContentrather than at the far right. None of the stories exercise all three slots together, so it hasn't been caught. Low priority until a consumer needs it, but worth fixing before D.4 whenAnalysisStatusPanelmigrates to this. -
Tooltip
TooltipProviderlatent footgun (Tooltip.tsxre-export shim): the shim correctly documents the Provider requirement in its comment, but there's no runtime guard — a future consumer importing<Tooltip>without<TooltipProvider>will silently render nothing. Zero current consumers (verified), so not a blocker now. Worth aconsole.warncheck in development when the first consumer lands in D.4+. -
Helper functions without Vitest coverage (
MetricBar.tsx:resolvePercent,TimeReadout.tsx:formatMs/formatRange,SignalChain.tsx:cableToneForTransition): these carry real edge-case logic (null/NaN guards,max === mindivision-by-zero, state table). Project norm forui/components is Storybook rather than Vitest unit tests, and all helpers are unexported, so this is consistent with the existing primitives. Flagging only becauseresolvePercent'smax === minguard is the kind of thing that quietly regresses.
Test results
605/605 unit tests passing (per PR description); Chromatic ✅; Frontend + Backend CI in-progress at review time.
Phase boundary check
Clean. No files outside apps/ui/src/components/ were modified. No Phase 1 output schema touched. No Phase 2/3 code changed.
Generated by Claude Code
PR 3 of 16 in the UI consistency overhaul. Worst-drift fix: kills every raw zinc-* / amber-* / emerald-* / rounded-lg instance in SamplePlayback.tsx (was 13 occurrences, now 0). Stacked on top of PR #65 (D.1 primitives). Will be rebased onto main once PR #65 merges. Migration: - Outer <section> → <DeviceRack name="AUDITION SAMPLES" subtitle="· Phase 3 heuristic" status={rackStatusFor(...)}>. Rack status reflects panel state — error tone on failure, active tone while generating, success tone once a manifest is rendered. - Original <h3> id-based labelling replaced by aria-label on the DeviceRack; the title strip carries the visible name. - Regenerate <button> → <Button variant="link" size="sm"> in the title-strip action slot. - Generate <button> → <Button variant="primary" size="md" ledIndicator> in the body. - ManifestMeta pill spans → <Pill tone="neutral" leadingDot>. - SampleGroup <h4> → <SectionHeader size="sm" eyebrow="Group" title={CATEGORY_LABELS[category]}>. - SampleCard <li> rounded-md border → <Panel variant="surface" padding="md"> (wrapped in a <li> for list semantics). - confidenceClassFor() className helper → confidenceTone() returning a Pill tone (warning for LOW / low-confidence, neutral for MED, success for HIGH). - Code spans for cited Phase 1 field paths now use design-token colors (bg-bg-app, border-border, text-text-primary) instead of raw zinc-300/zinc-800. - MIDI download link uses text-accent + underline instead of zinc. No behavior changes — same callbacks, same conditional rendering, same content. Chain-of-custody fields (sample.cites.rationale, sample.cites.phase1Fields) are rendered identically; PURPOSE.md invariant preserved. Verified: lint clean, 605/605 unit tests passing, build clean. Bundle: AnalysisResults chunk grew from 256.79 KB to 311.86 KB (larger import surface from the ui/ barrel; tree-shaking will tighten this as more components migrate and the import is shared). https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe
PR #65 review flagged that when signalIn, railContent, and signalOut are all set together, both railContent and signalOut carried ml-auto. In a flex row only the first ml-auto sibling claims the available space, so signalOut collapsed against railContent instead of anchoring to the far right. Restructure the rail as three fixed slots: a left container for signalIn, a flex-1 center for railContent, and a right-justified container for signalOut. Each slot always renders; empty when its content is absent. Layout is now consistent across all combinations of the three slot props. PR #66 doesn't exercise the all-three-slots case (SamplePlayback only sets status, not the rail), but D.4 (AnalysisStatusPanel migration) will, so worth fixing now while the primitive has no production consumers. Verified: lint clean, 605/605 unit tests passing. https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe
* feat(ui): D.2 migrate SamplePlayback to ui/ primitives PR 3 of 16 in the UI consistency overhaul. Worst-drift fix: kills every raw zinc-* / amber-* / emerald-* / rounded-lg instance in SamplePlayback.tsx (was 13 occurrences, now 0). Stacked on top of PR #65 (D.1 primitives). Will be rebased onto main once PR #65 merges. Migration: - Outer <section> → <DeviceRack name="AUDITION SAMPLES" subtitle="· Phase 3 heuristic" status={rackStatusFor(...)}>. Rack status reflects panel state — error tone on failure, active tone while generating, success tone once a manifest is rendered. - Original <h3> id-based labelling replaced by aria-label on the DeviceRack; the title strip carries the visible name. - Regenerate <button> → <Button variant="link" size="sm"> in the title-strip action slot. - Generate <button> → <Button variant="primary" size="md" ledIndicator> in the body. - ManifestMeta pill spans → <Pill tone="neutral" leadingDot>. - SampleGroup <h4> → <SectionHeader size="sm" eyebrow="Group" title={CATEGORY_LABELS[category]}>. - SampleCard <li> rounded-md border → <Panel variant="surface" padding="md"> (wrapped in a <li> for list semantics). - confidenceClassFor() className helper → confidenceTone() returning a Pill tone (warning for LOW / low-confidence, neutral for MED, success for HIGH). - Code spans for cited Phase 1 field paths now use design-token colors (bg-bg-app, border-border, text-text-primary) instead of raw zinc-300/zinc-800. - MIDI download link uses text-accent + underline instead of zinc. No behavior changes — same callbacks, same conditional rendering, same content. Chain-of-custody fields (sample.cites.rationale, sample.cites.phase1Fields) are rendered identically; PURPOSE.md invariant preserved. Verified: lint clean, 605/605 unit tests passing, build clean. Bundle: AnalysisResults chunk grew from 256.79 KB to 311.86 KB (larger import surface from the ui/ barrel; tree-shaking will tighten this as more components migrate and the import is shared). https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe * fix(ui): DeviceRack signal rail uses 3 explicit slots PR #65 review flagged that when signalIn, railContent, and signalOut are all set together, both railContent and signalOut carried ml-auto. In a flex row only the first ml-auto sibling claims the available space, so signalOut collapsed against railContent instead of anchoring to the far right. Restructure the rail as three fixed slots: a left container for signalIn, a flex-1 center for railContent, and a right-justified container for signalOut. Each slot always renders; empty when its content is absent. Layout is now consistent across all combinations of the three slot props. PR #66 doesn't exercise the all-three-slots case (SamplePlayback only sets status, not the rail), but D.4 (AnalysisStatusPanel migration) will, so worth fixing now while the primitive has no production consumers. Verified: lint clean, 605/605 unit tests passing. https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe --------- Co-authored-by: Claude <noreply@anthropic.com>
…on) (#67) * feat(ui): D.2 migrate SamplePlayback to ui/ primitives PR 3 of 16 in the UI consistency overhaul. Worst-drift fix: kills every raw zinc-* / amber-* / emerald-* / rounded-lg instance in SamplePlayback.tsx (was 13 occurrences, now 0). Stacked on top of PR #65 (D.1 primitives). Will be rebased onto main once PR #65 merges. Migration: - Outer <section> → <DeviceRack name="AUDITION SAMPLES" subtitle="· Phase 3 heuristic" status={rackStatusFor(...)}>. Rack status reflects panel state — error tone on failure, active tone while generating, success tone once a manifest is rendered. - Original <h3> id-based labelling replaced by aria-label on the DeviceRack; the title strip carries the visible name. - Regenerate <button> → <Button variant="link" size="sm"> in the title-strip action slot. - Generate <button> → <Button variant="primary" size="md" ledIndicator> in the body. - ManifestMeta pill spans → <Pill tone="neutral" leadingDot>. - SampleGroup <h4> → <SectionHeader size="sm" eyebrow="Group" title={CATEGORY_LABELS[category]}>. - SampleCard <li> rounded-md border → <Panel variant="surface" padding="md"> (wrapped in a <li> for list semantics). - confidenceClassFor() className helper → confidenceTone() returning a Pill tone (warning for LOW / low-confidence, neutral for MED, success for HIGH). - Code spans for cited Phase 1 field paths now use design-token colors (bg-bg-app, border-border, text-text-primary) instead of raw zinc-300/zinc-800. - MIDI download link uses text-accent + underline instead of zinc. No behavior changes — same callbacks, same conditional rendering, same content. Chain-of-custody fields (sample.cites.rationale, sample.cites.phase1Fields) are rendered identically; PURPOSE.md invariant preserved. Verified: lint clean, 605/605 unit tests passing, build clean. Bundle: AnalysisResults chunk grew from 256.79 KB to 311.86 KB (larger import surface from the ui/ barrel; tree-shaking will tighten this as more components migrate and the import is shared). https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe * fix(ui): DeviceRack signal rail uses 3 explicit slots PR #65 review flagged that when signalIn, railContent, and signalOut are all set together, both railContent and signalOut carried ml-auto. In a flex row only the first ml-auto sibling claims the available space, so signalOut collapsed against railContent instead of anchoring to the far right. Restructure the rail as three fixed slots: a left container for signalIn, a flex-1 center for railContent, and a right-justified container for signalOut. Each slot always renders; empty when its content is absent. Layout is now consistent across all combinations of the three slot props. PR #66 doesn't exercise the all-three-slots case (SamplePlayback only sets status, not the rail), but D.4 (AnalysisStatusPanel migration) will, so worth fixing now while the primitive has no production consumers. Verified: lint clean, 605/605 unit tests passing. https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe * feat(ui): D.3 AnalysisStatusPanel → SignalChain (marquee transformation) PR 4 of 16 in the UI consistency overhaul. The visual hook reviewers will remember: the analysis pipeline literally renders as a horizontal Live 12 device chain. Stacked on PR #66 (D.2 SamplePlayback + DeviceRack rail fix). Branch already includes the rail fix needed for the all-3-slots layout this PR exercises. Migration: - Outer <div> → <DeviceRack name="ANALYSIS RUN" subtitle={'· ' + runId.slice(-8)} status={overallStatus}>. Status maps progress tone + isActive to idle/active/success/error. - Stop button → <Button variant="danger" size="sm" leadingIcon=Square> in the title-strip action slot. - The three stage tiles (measurement / pitchNoteTranslation / interpretation) → <SignalChain orientation="horizontal" animated>. The animated cables convey "data is flowing left to right" between active stages — the literal Live 12 device chain metaphor. - Stage statuses (running/queued/completed/failed/etc.) map onto the SignalChain primitive's smaller vocabulary (idle/queued/active/success/error) via toSignalStatus(). - Per-stage retry buttons rendered via Button variant="ghost" in the stage device's action slot. - Non-retryable error codes (e.g. GEMINI_NOT_CONFIGURED) surface as a truncated mono span in the action slot with the full message in the title attribute — Audit N1 sibling preserved. - Primary readout (data-testid="status-panel-primary-readout") wraps in <Panel variant="inset" padding="sm"> instead of the hand-rolled rounded-sm border bg-bg-card div. Test ID preserved verbatim. - Progress bar disappears as a standalone element — its information (elapsed time, estimate range, percent) folds into the DeviceRack's bottom signal-flow rail via railContent. Animated SignalChain cables + LED-pulse on the active stage + percent in the rail collectively carry the "still running" signal. Preserved verbatim: - AnalysisStatusPanelProps (no API change for App.tsx). - computeLiveProgress() pure function and its export — the only consumer (tests/services/analysisStatusProgress.test.ts) was driven exclusively by this export and continues to pass without change. - STAGE_LABELS, statusLabel, all stage-status mapping logic. - The Audit N1 retry-button gating (errorMarkedNonRetryable check). - The Audit Finding #6 primary-readout placement at the top of the body. Bundle: index chunk grew from 271 KB → 325 KB gz (97.5 KB compressed) because AnalysisStatusPanel now imports more from the ui/ barrel. AnalysisResults shrank slightly. Will tighten as more files share the import. Verified: lint clean, 605/605 unit tests passing (computeLiveProgress test suite unaffected), build clean. https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe * test(ui): unit-cover AnalysisStatusPanel helpers + tighten ready mapping PR #67 review (REQUEST CHANGES) called out that toSignalStatus() and rackStatusFromProgress() were new logic without tests, and that 'ready' mapping to 'queued' was a judgment-call regression (the user already sees a Retry button in the action slot — the tile reading 'queued' implies the chain will auto-resume when it actually won't). Changes: - Export toSignalStatus, statusLabel, and rackStatusFromProgress from AnalysisStatusPanel.tsx so the test suite can import them. Add doc comments to each explaining the intent + the per-case mapping rationale. - Remap 'ready' from 'queued' to 'idle'. statusLabel('ready') still returns 'READY' so the user sees the correct label inside the tile; the LED + cable + box-shadow no longer fake imminent execution. - New test file tests/services/analysisStatusPanelHelpers.test.ts with 23 tests covering every AnalysisStageStatus value, the unknown fallback for both helpers, the dominance of failed/success tones in rackStatusFromProgress, the running+isActive interaction, and the ready-vs-queued separation explicitly. Test count: 605 → 628. Verified: lint clean, full unit suite 628/628 passing. https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe --------- Co-authored-by: Claude <noreply@anthropic.com>
* feat(ui): D.2 migrate SamplePlayback to ui/ primitives PR 3 of 16 in the UI consistency overhaul. Worst-drift fix: kills every raw zinc-* / amber-* / emerald-* / rounded-lg instance in SamplePlayback.tsx (was 13 occurrences, now 0). Stacked on top of PR #65 (D.1 primitives). Will be rebased onto main once PR #65 merges. Migration: - Outer <section> → <DeviceRack name="AUDITION SAMPLES" subtitle="· Phase 3 heuristic" status={rackStatusFor(...)}>. Rack status reflects panel state — error tone on failure, active tone while generating, success tone once a manifest is rendered. - Original <h3> id-based labelling replaced by aria-label on the DeviceRack; the title strip carries the visible name. - Regenerate <button> → <Button variant="link" size="sm"> in the title-strip action slot. - Generate <button> → <Button variant="primary" size="md" ledIndicator> in the body. - ManifestMeta pill spans → <Pill tone="neutral" leadingDot>. - SampleGroup <h4> → <SectionHeader size="sm" eyebrow="Group" title={CATEGORY_LABELS[category]}>. - SampleCard <li> rounded-md border → <Panel variant="surface" padding="md"> (wrapped in a <li> for list semantics). - confidenceClassFor() className helper → confidenceTone() returning a Pill tone (warning for LOW / low-confidence, neutral for MED, success for HIGH). - Code spans for cited Phase 1 field paths now use design-token colors (bg-bg-app, border-border, text-text-primary) instead of raw zinc-300/zinc-800. - MIDI download link uses text-accent + underline instead of zinc. No behavior changes — same callbacks, same conditional rendering, same content. Chain-of-custody fields (sample.cites.rationale, sample.cites.phase1Fields) are rendered identically; PURPOSE.md invariant preserved. Verified: lint clean, 605/605 unit tests passing, build clean. Bundle: AnalysisResults chunk grew from 256.79 KB to 311.86 KB (larger import surface from the ui/ barrel; tree-shaking will tighten this as more components migrate and the import is shared). https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe * fix(ui): DeviceRack signal rail uses 3 explicit slots PR #65 review flagged that when signalIn, railContent, and signalOut are all set together, both railContent and signalOut carried ml-auto. In a flex row only the first ml-auto sibling claims the available space, so signalOut collapsed against railContent instead of anchoring to the far right. Restructure the rail as three fixed slots: a left container for signalIn, a flex-1 center for railContent, and a right-justified container for signalOut. Each slot always renders; empty when its content is absent. Layout is now consistent across all combinations of the three slot props. PR #66 doesn't exercise the all-three-slots case (SamplePlayback only sets status, not the rail), but D.4 (AnalysisStatusPanel migration) will, so worth fixing now while the primitive has no production consumers. Verified: lint clean, 605/605 unit tests passing. https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe * feat(ui): D.3 AnalysisStatusPanel → SignalChain (marquee transformation) PR 4 of 16 in the UI consistency overhaul. The visual hook reviewers will remember: the analysis pipeline literally renders as a horizontal Live 12 device chain. Stacked on PR #66 (D.2 SamplePlayback + DeviceRack rail fix). Branch already includes the rail fix needed for the all-3-slots layout this PR exercises. Migration: - Outer <div> → <DeviceRack name="ANALYSIS RUN" subtitle={'· ' + runId.slice(-8)} status={overallStatus}>. Status maps progress tone + isActive to idle/active/success/error. - Stop button → <Button variant="danger" size="sm" leadingIcon=Square> in the title-strip action slot. - The three stage tiles (measurement / pitchNoteTranslation / interpretation) → <SignalChain orientation="horizontal" animated>. The animated cables convey "data is flowing left to right" between active stages — the literal Live 12 device chain metaphor. - Stage statuses (running/queued/completed/failed/etc.) map onto the SignalChain primitive's smaller vocabulary (idle/queued/active/success/error) via toSignalStatus(). - Per-stage retry buttons rendered via Button variant="ghost" in the stage device's action slot. - Non-retryable error codes (e.g. GEMINI_NOT_CONFIGURED) surface as a truncated mono span in the action slot with the full message in the title attribute — Audit N1 sibling preserved. - Primary readout (data-testid="status-panel-primary-readout") wraps in <Panel variant="inset" padding="sm"> instead of the hand-rolled rounded-sm border bg-bg-card div. Test ID preserved verbatim. - Progress bar disappears as a standalone element — its information (elapsed time, estimate range, percent) folds into the DeviceRack's bottom signal-flow rail via railContent. Animated SignalChain cables + LED-pulse on the active stage + percent in the rail collectively carry the "still running" signal. Preserved verbatim: - AnalysisStatusPanelProps (no API change for App.tsx). - computeLiveProgress() pure function and its export — the only consumer (tests/services/analysisStatusProgress.test.ts) was driven exclusively by this export and continues to pass without change. - STAGE_LABELS, statusLabel, all stage-status mapping logic. - The Audit N1 retry-button gating (errorMarkedNonRetryable check). - The Audit Finding #6 primary-readout placement at the top of the body. Bundle: index chunk grew from 271 KB → 325 KB gz (97.5 KB compressed) because AnalysisStatusPanel now imports more from the ui/ barrel. AnalysisResults shrank slightly. Will tighten as more files share the import. Verified: lint clean, 605/605 unit tests passing (computeLiveProgress test suite unaffected), build clean. https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe * chore(ui): normalize package-lock.json optional dep dev flags npm install during PR review pass dropped "dev": true from optional rollup platform-binary entries — cosmetic lockfile normalization, no functional change. https://claude.ai/code/session_01MWRh46jPZ7YbFq4SkPFEwU --------- Co-authored-by: Claude <noreply@anthropic.com>
PR 14 of 16 in the UI consistency overhaul. Final cleanup pass. Removes three CSS utility classes from index.css: - .ableton-panel - .ableton-device - .ableton-header All three were retired by D.* migrations — every former consumer now uses the canonical primitives from components/ui/ (Panel, DeviceRack, SectionHeader). `grep -rn` against apps/ui/src/ confirms zero remaining usages. .ableton-shell and .ableton-toolbar STAY because App.tsx:952,956 still consumes them for the outer app chrome (the load-bearing section-grid shell). A focused follow-up could migrate those two to DeviceRack / a future AppShell primitive; out of scope here. Deferred (preserved as-is): - components/MeasurementPrimitives.tsx cannot be fully deleted yet — 5 consumers still depend on timeline/lane primitives (LaneContainer, LaneRow, StatsBar, TimeRuler) that the plan explicitly preserved. The shipped primitives (StatusBadge, DeltaBadge, MetricBar, MetricBarRow, OutlinePillButton, TokenBadgeList, StyledDataTable, AccentMetricCard) also still have consumers in MeasurementDashboard and AnalysisResults sections that weren't migrated (StructureLanes, HarmonyLanes, AnalysisResults remaining sections). Keep the file intact. - components/Tooltip.tsx stays as a re-export shim; the file isn't causing any drift and external imports may still target it. Verified: lint clean, 628/628 unit tests passing, build clean. D.* migrations summary (all merged or open at time of this PR): - D.0 Foundation (#64) ✓ - D.1 Primitives complete (#65) ✓ - D.2 SamplePlayback (#66) ✓ - D.3 SignalChain marquee (#67) ✓ - D.4 FileUpload + Input Source (#69) ✓ - D.5a Results header + metric strip (#71) ✓ - D.5b Mix Chain (#73) ✓ - D.5c Patches (#74) ✓ - D.5d Section headers (#75) ✓ - D.6a Dashboard structure (#76) ✓ - D.6b Dashboard sections (#77) ✓ - D.6c Dashboard headers (#78) ✓ - D.7 SessionMusicianPanel + MixDoctorPanel (#79) — open - D.8 Signal Monitor + SpectrogramViewer drift (#80) — open - D.9 DenseDawConcept drift (#81) — open - D.10 Cleanup (this PR) ✓ https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe
…82) PR 14 of 16 in the UI consistency overhaul. Final cleanup pass. Removes three CSS utility classes from index.css: - .ableton-panel - .ableton-device - .ableton-header All three were retired by D.* migrations — every former consumer now uses the canonical primitives from components/ui/ (Panel, DeviceRack, SectionHeader). `grep -rn` against apps/ui/src/ confirms zero remaining usages. .ableton-shell and .ableton-toolbar STAY because App.tsx:952,956 still consumes them for the outer app chrome (the load-bearing section-grid shell). A focused follow-up could migrate those two to DeviceRack / a future AppShell primitive; out of scope here. Deferred (preserved as-is): - components/MeasurementPrimitives.tsx cannot be fully deleted yet — 5 consumers still depend on timeline/lane primitives (LaneContainer, LaneRow, StatsBar, TimeRuler) that the plan explicitly preserved. The shipped primitives (StatusBadge, DeltaBadge, MetricBar, MetricBarRow, OutlinePillButton, TokenBadgeList, StyledDataTable, AccentMetricCard) also still have consumers in MeasurementDashboard and AnalysisResults sections that weren't migrated (StructureLanes, HarmonyLanes, AnalysisResults remaining sections). Keep the file intact. - components/Tooltip.tsx stays as a re-export shim; the file isn't causing any drift and external imports may still target it. Verified: lint clean, 628/628 unit tests passing, build clean. D.* migrations summary (all merged or open at time of this PR): - D.0 Foundation (#64) ✓ - D.1 Primitives complete (#65) ✓ - D.2 SamplePlayback (#66) ✓ - D.3 SignalChain marquee (#67) ✓ - D.4 FileUpload + Input Source (#69) ✓ - D.5a Results header + metric strip (#71) ✓ - D.5b Mix Chain (#73) ✓ - D.5c Patches (#74) ✓ - D.5d Section headers (#75) ✓ - D.6a Dashboard structure (#76) ✓ - D.6b Dashboard sections (#77) ✓ - D.6c Dashboard headers (#78) ✓ - D.7 SessionMusicianPanel + MixDoctorPanel (#79) — open - D.8 Signal Monitor + SpectrogramViewer drift (#80) — open - D.9 DenseDawConcept drift (#81) — open - D.10 Cleanup (this PR) ✓ https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe Co-authored-by: Claude <noreply@anthropic.com>
Summary
PR 2 of 16 in the UI consistency overhaul / Live 12 restyle stack. Lands the rest of the primitive library on top of the D.0 foundation. No consumer migrations in this PR — every existing component is untouched.
New primitives (
apps/ui/src/components/ui/)DeviceRackstatus × densityvariants. The centerpiece the rest of the migration will build on.SignalChainDeviceRack+ChainSeparatorinto the horizontal stage pipeline. Cables animate when a stage isactive.AnalysisStatusPanel.tsx:318-470becomes this in PR 4.ChainSeparatoridle/active/success; animated variant. Horizontal + vertical orientations.MetricTileAccentMetricCard.MetricBar/MetricBarRowMeasurementPrimitivesso the consumer migration is mechanical.DataTableStyledDataTable(column types renamedDataTableColumn/DataTableProps).EmptyStateUnavailableMeasurementCard, session-musician off-banner, etc.).TimeReadoutAnalysisStatusPaneland similar.Storybook coverage
DeviceRackstories named01-04as the "Live 12 Vocabulary" anchors —EmptyShell,WithParameterGrid,InChain,ActiveStateProgression. These exist so future contributors don't reintroduce drift.SignalChainstories cover every stage-status combo (idle / measuring / pitch-active / interpret-active / all-complete / pitch-failed) plus vertical orientation.Tooltip migration
apps/ui/src/components/Tooltip.tsxis now a re-export shim forui/Tooltip.tsx. The old custom + Framer Motion implementation has been removed. No current consumers, so no provider mounting needed yet — that lands when the first consumer (D.4+) imports it.MeasurementPrimitives.tsx→ new-primitive mapping tableFor future consumer-migration PRs (D.5/D.6):
StatusBadgePill(tone)DeltaBadgeMetricBarMetricBar(drop-in)MetricBarRowMetricBarRow(drop-in)AccentMetricCardMetricTile(accent+statusprops)TokenBadgeListmap()of<Pill>StyledDataTableDataTable(drop-in, rename column types)OutlinePillButtonButton variant="secondary"or"ghost"UnavailableMeasurementCardEmptyStateLaneContainer/LaneRow/TimeRuler/StatsBarMeasurementPrimitives— these are timeline-lane primitives, not generic chromeBADGE_TONE_CLASSESPillowns tone styling)ACCENT_CARD_CLASSESMetricTileowns accent stripe styling)Test plan
npm run lint— cleannpm run test:unit— 605/605 passingnpm run build— clean (90.66 KB CSS, 14.75 KB gzipped)npm run build-storybook— cleannpm run test:smoke— not run locally (sandbox network blocks Playwright browser download); existing components untouched so smoke tests should pass in CInpm run storybook— boot locally, walk through the 4DeviceRackanchor stories + theSignalChainstatus combos, confirm cables animate when activeUp next (PR 3)
D.2 SamplePlayback — kill the 13 raw
zinc-*/amber-*/emerald-*instances; outer<section>→<DeviceRack name="AUDITION SAMPLES">, generate button →<Button variant="primary" ledIndicator>, manifest meta →<Pill tone="neutral">, sample cards →<Panel variant="surface">. ~80 lines changed.https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe
Generated by Claude Code