Skip to content

feat(ui): D.2 migrate SamplePlayback to ui/ primitives - #66

Merged
slittycode merged 2 commits into
mainfrom
claude/ui-restyle-pr3-sampleplayback
May 18, 2026
Merged

feat(ui): D.2 migrate SamplePlayback to ui/ primitives#66
slittycode merged 2 commits into
mainfrom
claude/ui-restyle-pr3-sampleplayback

Conversation

@slittycode

@slittycode slittycode commented May 18, 2026

Copy link
Copy Markdown
Owner

Summary

PR 3 of 16 in the UI consistency overhaul / Live 12 restyle stack. Worst-drift fix: kills every raw zinc-* / amber-* / emerald-* / rounded-lg / rounded-md instance in SamplePlayback.tsx (was 13 occurrences, now 0).

Rebased onto main after #65 merged. Two commits:

  1. 1a7ab3f — SamplePlayback migration.
  2. 28a9ac4 — Bundled fix from feat(ui): D.1 primitives complete — DeviceRack, SignalChain, MetricTile et al. #65 review: DeviceRack signal rail uses 3 explicit slots (left/center/right) so signalIn + railContent + signalOut lay out correctly when all three are present. SamplePlayback doesn't exercise this combo, but D.4 (AnalysisStatusPanel) will.

Migration

  • Outer <section><DeviceRack name="AUDITION SAMPLES" subtitle="· Phase 3 heuristic" status={...}>. Rack status reflects panel state — error tone on failure, active while generating, success 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.

DeviceRack rail fix (commit 2)

Previously, when signalIn + railContent + signalOut were 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. Restructured 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.

Non-goals (preserved verbatim)

  • 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 honest-uncertainty framing preserved in the description copy.
  • Same backend interface, same services/sampleGenerationClient.ts calls.

Test plan

  • npm run lint — clean
  • npm run test:unit — 605/605 passing
  • npm run build — clean
  • npm run test:smoke — sandbox network blocks Playwright browser; should pass in CI as SamplePlayback text content + aria-label are preserved
  • Manual: drag-drop a track, click "Generate audition samples", confirm rack lights up active during generation and turns success when manifest renders; verify confidence pills show success / neutral / warning tones; trigger an error and confirm the rack flips to error tone

Verification: drift count

$ grep -c "zinc-\|amber-\|emerald-\|rounded-lg\|rounded-md" apps/ui/src/components/SamplePlayback.tsx
0

Up next (PR 4)

D.3 AnalysisStatusPanel → SignalChain. Marquee transformation: the analysis pipeline literally renders as a horizontal Live 12 device chain.

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe

Base automatically changed from claude/ui-consistency-overhaul-sHJ73 to main May 18, 2026 22:34
claude added 2 commits May 18, 2026 22:35
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

@slittycode slittycode left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE

Summary

Pure visual restyle of SamplePlayback.tsx — raw zinc-*/amber-*/emerald-*/rounded-* Tailwind classes replaced with DeviceRack, Button, Panel, Pill, and SectionHeader primitives from the ui/ barrel. No behavior changes, no backend or type-contract changes. The PR's stated drift count (0 occurrences of the banned class patterns) checks out against the file. Chain-of-custody fields (sample.cites.rationale, sample.cites.phase1Fields) are rendered identically to the original.

Findings

Worth considering — rackStatusFor has no unit test. It's a new pure function with four branches encoding a state machine (error → active → success → idle). The codebase doesn't unit-test component helpers as a rule (no tests/services/samplePlayback.test.ts exists, and the pattern is service-layer unit tests + Playwright smoke for UI behavior), so this doesn't block — but if the smoke suite misses the error-state path, the only coverage for 'error' → rack status mapping is code review. Worth a smoke assertion on failure-path if the suite grows.

Not flagging: confidenceTone is a rename of confidenceClassFor with return type stringTone. Logic is identical; the old function had no tests either.

Note: AnalysisResults chunk grew from 256.79 KB → 311.86 KB gz. The barrel-amortization explanation is plausible — worth a checkpoint at PR 16 to confirm the per-component cost actually normalizes.

Test results

PR reports 605/605 unit tests passing. No new tests added (test count unchanged). Smoke tests blocked by sandbox network; should run in CI. All verified prop shapes match component definitions: Button has primary/link variants; Pill has xs size; Panel has surface variant + md padding; DeviceRack spreads ...rest so aria-label propagates correctly; SectionHeader accepts size="sm" and ledTone.

Phase boundary check

Clean. No Phase 1 values re-derived or overwritten. No new fields read from or written to the measurement layer. sample.cites.phase1Fields rendering is unchanged. role="alert" preserved on error state.


Generated by Claude Code

@slittycode
slittycode merged commit 1d20ea6 into main May 18, 2026
3 checks passed
@slittycode
slittycode deleted the claude/ui-restyle-pr3-sampleplayback branch May 18, 2026 22:45
slittycode added a commit that referenced this pull request May 18, 2026
…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>
slittycode pushed a commit that referenced this pull request May 18, 2026
PR 5 of 16 in the UI consistency overhaul. The Input Source panel
becomes a <DeviceRack name="Input Source">; the analysis-option
toggles become Radix Checkboxes; the Run Analysis CTA becomes a
<Button variant="primary" ledIndicator>; the loaded-file card becomes
a <Panel variant="surface">.

Stacked on PR #67 (D.3 SignalChain). Branch will be rebased onto main
once #66#67 → PR 5 merge in order.

FileUpload.tsx:
- Loaded file card: replaced the hand-rolled bg-bg-card border div
  with <Panel variant="surface" padding="md">. Accent stripe + file
  icon + name + ready-dot + size-warning preserved verbatim.
- Format chips (MP3/WAV/FLAC/AIFF): replaced inline span chips with
  <Pill tone="neutral" variant="outline" size="xs">. Visual parity.
- "Load Demo Track" <button>: replaced with <Button variant="secondary"
  size="md">. Behavior + text preserved verbatim for the smoke test
  selector (tests/smoke/responsive-layout.spec.ts asserts the text).
- Remove File icon button: replaced with <Button variant="ghost"
  iconOnly size="sm">.
- Dropzone container kept as raw div — its drag-event handlers and
  conditional background-tint state are denser than Panel currently
  supports, and shimming Panel to accept onDragOver/Leave/Drop +
  three-way tint logic just to wrap one site would add API surface
  for one consumer.

App.tsx — Input Source panel:
- Outer wrapper at :1018-1027: hand-rolled bg-bg-surface-dark header
  + bg-bg-card body → <DeviceRack name="Input Source"
  status={audioFile ? (isAnalyzing ? 'active' : 'success') : 'idle'}>.
  Preserved data-testid="input-panel".
- Collapsed-summary buttons (Analyze new file + Adjust settings):
  swapped from inline <button> bg-bg-panel borders to <Button
  variant="secondary">.
- "Editing analysis settings" Hide button: <Button variant="secondary"
  size="sm">.
- STEM PITCH/NOTE TRANSLATION toggle: replaced <input type="checkbox">
  with Radix <Checkbox> (via the ui/ wrapper). Outer card label kept
  for the click-anywhere-on-card UX; htmlFor binds the label to the
  checkbox id. Preserved aria-label="PITCH/NOTE TRANSLATION" so the
  smoke selectors continue to resolve.
- AI INTERPRETATION toggle: same treatment. Preserved
  data-testid="phase2-status-inline" and aria-label="AI INTERPRETATION".
- Run Analysis CTA: replaced the motion.button with <Button
  variant="primary" size="lg" ledIndicator leadingIcon={<Play>}>.
  The Button primitive already encodes the bg-bg-panel +
  border-accent/60 + glow hover + ledIndicator pulse that the
  inline motion.button was hand-rolling. Text "Run Analysis"
  preserved verbatim for tests/smoke/upload-phase1.spec.ts.
- Removed the explicit `<X />` import from lucide-react remains used
  by other call sites in App.tsx; left intact.
- Removed unused motion.button (motion is still used elsewhere in the
  file; the import stays).

Pre-flight decisions honored:
- Two native <select> at :984-998 (phase2-model-desktop) and
  :1191-1204 (phase2-model-mobile) stayed native — they were already
  restyled with appearance-none and don't need migration.
- Radix Checkbox, not Switch, for the analysis options (selection
  metaphor, not on/off feature flag).
- TooltipProvider not mounted yet — no Tooltip consumers in this PR.

Preserved verbatim:
- All data-testid attributes (input-panel, input-panel-collapsed,
  phase2-status-badge, phase2-status-inline).
- All visible text strings: "Input Source", "Drop Audio Here",
  "Load Demo Track", "Run Analysis", "↺ Analyze new file",
  "Adjust settings", "Hide", "STEM PITCH/NOTE TRANSLATION",
  "AI INTERPRETATION", "Ready", "Estimated local analysis".
- All ARIA labels (PITCH/NOTE TRANSLATION, AI INTERPRETATION,
  ANALYSIS MODE, Interpretation model).
- Behavior: onCheckedChange wraps Radix's CheckedState ('true' |
  'false' | 'indeterminate') with a strict `=== true` check before
  setting the boolean state, preserving the existing semantics.
- Audit findings (N9 collapse, N9 re-expand banner, #11 no CPU
  meter, #6 readout placement) untouched.

Verified: lint clean, 605/605 unit tests passing, build clean
(index chunk 323 KB / 97 KB gz; comparable to PR 4 — primitive
imports are now amortized across consumers).

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe
slittycode added a commit that referenced this pull request May 18, 2026
* 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>
slittycode added a commit that referenced this pull request May 18, 2026
* feat(ui): D.4 migrate FileUpload + Input Source to ui/ primitives

PR 5 of 16 in the UI consistency overhaul. The Input Source panel
becomes a <DeviceRack name="Input Source">; the analysis-option
toggles become Radix Checkboxes; the Run Analysis CTA becomes a
<Button variant="primary" ledIndicator>; the loaded-file card becomes
a <Panel variant="surface">.

Stacked on PR #67 (D.3 SignalChain). Branch will be rebased onto main
once #66#67 → PR 5 merge in order.

FileUpload.tsx:
- Loaded file card: replaced the hand-rolled bg-bg-card border div
  with <Panel variant="surface" padding="md">. Accent stripe + file
  icon + name + ready-dot + size-warning preserved verbatim.
- Format chips (MP3/WAV/FLAC/AIFF): replaced inline span chips with
  <Pill tone="neutral" variant="outline" size="xs">. Visual parity.
- "Load Demo Track" <button>: replaced with <Button variant="secondary"
  size="md">. Behavior + text preserved verbatim for the smoke test
  selector (tests/smoke/responsive-layout.spec.ts asserts the text).
- Remove File icon button: replaced with <Button variant="ghost"
  iconOnly size="sm">.
- Dropzone container kept as raw div — its drag-event handlers and
  conditional background-tint state are denser than Panel currently
  supports, and shimming Panel to accept onDragOver/Leave/Drop +
  three-way tint logic just to wrap one site would add API surface
  for one consumer.

App.tsx — Input Source panel:
- Outer wrapper at :1018-1027: hand-rolled bg-bg-surface-dark header
  + bg-bg-card body → <DeviceRack name="Input Source"
  status={audioFile ? (isAnalyzing ? 'active' : 'success') : 'idle'}>.
  Preserved data-testid="input-panel".
- Collapsed-summary buttons (Analyze new file + Adjust settings):
  swapped from inline <button> bg-bg-panel borders to <Button
  variant="secondary">.
- "Editing analysis settings" Hide button: <Button variant="secondary"
  size="sm">.
- STEM PITCH/NOTE TRANSLATION toggle: replaced <input type="checkbox">
  with Radix <Checkbox> (via the ui/ wrapper). Outer card label kept
  for the click-anywhere-on-card UX; htmlFor binds the label to the
  checkbox id. Preserved aria-label="PITCH/NOTE TRANSLATION" so the
  smoke selectors continue to resolve.
- AI INTERPRETATION toggle: same treatment. Preserved
  data-testid="phase2-status-inline" and aria-label="AI INTERPRETATION".
- Run Analysis CTA: replaced the motion.button with <Button
  variant="primary" size="lg" ledIndicator leadingIcon={<Play>}>.
  The Button primitive already encodes the bg-bg-panel +
  border-accent/60 + glow hover + ledIndicator pulse that the
  inline motion.button was hand-rolling. Text "Run Analysis"
  preserved verbatim for tests/smoke/upload-phase1.spec.ts.
- Removed the explicit `<X />` import from lucide-react remains used
  by other call sites in App.tsx; left intact.
- Removed unused motion.button (motion is still used elsewhere in the
  file; the import stays).

Pre-flight decisions honored:
- Two native <select> at :984-998 (phase2-model-desktop) and
  :1191-1204 (phase2-model-mobile) stayed native — they were already
  restyled with appearance-none and don't need migration.
- Radix Checkbox, not Switch, for the analysis options (selection
  metaphor, not on/off feature flag).
- TooltipProvider not mounted yet — no Tooltip consumers in this PR.

Preserved verbatim:
- All data-testid attributes (input-panel, input-panel-collapsed,
  phase2-status-badge, phase2-status-inline).
- All visible text strings: "Input Source", "Drop Audio Here",
  "Load Demo Track", "Run Analysis", "↺ Analyze new file",
  "Adjust settings", "Hide", "STEM PITCH/NOTE TRANSLATION",
  "AI INTERPRETATION", "Ready", "Estimated local analysis".
- All ARIA labels (PITCH/NOTE TRANSLATION, AI INTERPRETATION,
  ANALYSIS MODE, Interpretation model).
- Behavior: onCheckedChange wraps Radix's CheckedState ('true' |
  'false' | 'indeterminate') with a strict `=== true` check before
  setting the boolean state, preserving the existing semantics.
- Audit findings (N9 collapse, N9 re-expand banner, #11 no CPU
  meter, #6 readout placement) untouched.

Verified: lint clean, 605/605 unit tests passing, build clean
(index chunk 323 KB / 97 KB gz; comparable to PR 4 — primitive
imports are now amortized across consumers).

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe

* fix(ui): drop unused Panel import from App.tsx

PR #69 review flagged App.tsx:9 imports Panel but never uses it
(Panel is only consumed in FileUpload.tsx). Drop it.

This is also a low-cost iteration to get CI logs against a fresh
commit — the Frontend job failed on the previous push but the
auth-gated logs prevented diagnosis from this sandbox. Pushing this
clean fix to re-trigger the smoke suite.

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe

* fix(ui): revert App.tsx analysis-option toggles to native <input>

PR #69's Frontend CI failed twice on smoke tests without accessible
logs from this sandbox. Best hypothesis: the smoke spec at
tests/smoke/upload-estimate-phase1.spec.ts:242-246 uses
page.getByLabel("PITCH/NOTE TRANSLATION") plus .toBeChecked() and
.uncheck(). Playwright's getByLabel documents itself for "input
elements", and .uncheck() expects native checkboxes (or, in some
versions, also role=checkbox). The Radix Checkbox renders as a
<button role="checkbox"> internally; that's a less-consistently-
supported target for these methods than a real <input>.

Revert the two toggles in App.tsx (PITCH/NOTE TRANSLATION and AI
INTERPRETATION) to native <input type="checkbox"> with the same
appearance-none-style border-card UX. The Checkbox primitive in
components/ui/ is unchanged and remains available for surfaces where
smoke selectors aren't load-bearing. Inline comments explain the
rationale at each call site so future migrations don't naively
re-introduce Radix here.

Local 628/628 unit tests still pass; lint clean. If smoke CI passes
on this commit, the hypothesis is confirmed and we can write a real
test against the Radix Checkbox primitive separately to decide
whether it's worth keeping in the canonical toolkit.

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe

* fix(ui): restore bg-bg-card on input-panel to satisfy theme-shell smoke

PR #69's Frontend CI failed three times. tests/smoke/theme-shell.spec.ts:41
asserts the computed background-color of [data-testid="input-panel"]
is exactly rgb(68, 68, 68) — that's --color-bg-card (#444444). The
PR 5 migration removed `bg-bg-card` from that div because the
DeviceRack wrapper was meant to own the surface chrome. But the
DeviceRack's body is transparent by default (so the rack's gradient
face shows through); the computed background becomes rgba(0,0,0,0)
instead of #444444, failing the test.

Add `bg-bg-card` back to the inner div explicitly. The DeviceRack
still owns the title-strip gradient, border, and box-shadow; the body
is now flat #444444 matching the previous palette contract — and
also matching how Live 12 devices visually compose (gradient header,
relatively flat body).

Also restore the `p-4` padding inside the input-panel div — the
DeviceRack body's --space-device-pad-* is smaller than the original
p-4 the test scenarios were calibrated against.

The other smoke selectors (getByLabel for the toggles, getByTitle on
Remove File, getByRole on Run Analysis) were never the issue —
they're all preserved through prop spreading on the new primitives.

Verified: lint clean, 628/628 unit tests passing, build clean.

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe

---------

Co-authored-by: Claude <noreply@anthropic.com>
slittycode added a commit that referenced this pull request May 18, 2026
PR 7 of 16 in the UI consistency overhaul. Mix & Master Chain groups
(DRUM PROCESSING, BASS PROCESSING, HIGH-END DETAIL, MASTER BUS) now
each render as a <DeviceRack> with the group emoji + name on the
title strip.

Branched off main (PRs #66/#67/#69/#71 squash-merged). Single-file
PR — AnalysisResults.tsx.

Migration:
- Per-group <section> + <h3> heading → <DeviceRack name={emoji +
  group.name}>. The DeviceRack title strip displays the emoji and
  uppercase group name; the DOM text content is preserved exactly so
  analysisResultsUi.test.ts:441-450 (toContain('🥁 DRUM PROCESSING')
  etc.) continue to pass.
- group.annotation <p> kept inside the rack body with
  data-text-role="meta", margin-bottom restored.
- Cards grid div retains its `grid gap-4 grid-cols-1 sm:grid-cols-2`
  className verbatim — analysisResultsUi.test.ts:440 expects ≥2
  occurrences of this exact string (Mix Chain + Patches).
- mix-chain-applied-progress badge: hand-rolled span → <Pill
  tone="success" size="sm">. Preserved data-testid="mix-chain-
  applied-progress". The Pill emits the canonical
  `bg-success/20 text-success border-success/30` class string the
  test at :507 already asserts is present in the rendered HTML.
- SourcesToggle <button> → <Button variant="link">. Used by both Mix
  Chain and other sections; small ergonomic upgrade.

Deferred (out of D.5b scope):
- Per-card <div> outer wrapper NOT migrated to <Panel variant=
  "surface">. The card carries rich state-dependent styling
  (bg-bg-card with hover transitions, conditional border-l-2 for
  applied state, overflow-hidden for collapsible) that would
  require either extensive className overrides on Panel or
  extending Panel with new variants. Per-card migration deferred
  to a focused follow-up if/when Panel needs the additional
  variants.
- AppliedCheckbox NOT migrated to the Checkbox primitive. The
  existing implementation is already a <button role="checkbox">
  with aria-checked + aria-label + data-testid + green-tinted
  applied state. Migrating to Radix Checkbox would change the
  styling palette (success-green → accent-orange) and require
  Checkbox primitive extensions (tone prop). Existing
  implementation has equivalent a11y semantics and passes the
  applied-checkbox smoke test.

Preserved verbatim:
- All data-testid attributes (mix-chain-applied-progress,
  applied-checkbox, mix-chain-citation-*, mix-chain-headline-*).
- All visible text strings (group names + emojis, "applied",
  "Sources", PRO TIP, etc.).
- The exact `grid gap-4 grid-cols-1 sm:grid-cols-2` className the
  brittle assertion locks.
- All data-text-role attributes on h2/h3/h4/p elements (section-
  title, item-title, meta, body).
- The characteristicPillClass function which produces the
  `bg-{tone}/20 text-{tone} border-{tone}/30` strings asserted at
  :507-509 — untouched.
- CitationBlock + CitationHeadline internals — untouched.
- AppliedCheckbox tracker + audioContentHash gating — untouched.

Verified: lint clean, 628/628 unit tests passing (including
analysisResultsUi.test.ts which directly asserts the preserved
class strings + text + data-text-role attributes), build clean
(AnalysisResults chunk 254 KB / 63 KB gz — slightly smaller than
PR 6 due to removed hand-rolled span styling).

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe

Co-authored-by: Claude <noreply@anthropic.com>
slittycode added a commit that referenced this pull request May 18, 2026
#75)

PR 9 of 16 in the UI consistency overhaul. Promotes every section
header in AnalysisResults to the SectionHeader primitive via a single
internal change — the local ResultsSectionHeader helper is now a thin
wrapper that delegates to <SectionHeader variant="underline">.

Affected sections (every ResultsSectionHeader call site):
- Interpretation panel (:1024)
- Idle / "waiting on interpretation" panel (:1214)
- Style Profile (:1227)
- Audio Observations (:1369)
- Project Setup (:1436)
- Track Layout (:1466)
- Routing Blueprint (:1529)
- Warp Guide (:1601)
- (second warp section :1643)
- Arrangement (:1689)
- Sonic Elements & Reconstruction (:1891)
- Mix & Master Chain (already migrated in D.5b but still uses the
  helper)
- Patch Framework (already migrated in D.5c but still uses the helper)

What changes visually:
- The static accent dot in each section header
  (<span class="w-2 h-2 bg-accent rounded-full">) becomes the
  pulsing `.led-indicator--active` glyph. This is the same LED
  affordance used on every DeviceRack title strip in PRs #66-#74,
  so all section landmarks now share one vocabulary.
- The outer underline + flex layout is preserved verbatim
  (border-b border-border pb-2 + flex items-center justify-between
  gap-3), so spacing doesn't shift.

What's preserved verbatim:
- The ResultsSectionHeader prop API (title, rightSlot, titleRole,
  titleClassName, className) — every existing call site continues
  to work without changes.
- All <h2> elements with data-text-role={titleRole} —
  analysisResultsUi.test.ts:465-466 assertions
  (data-text-role="section-title" + ">Mix &amp; Master Chain</h2>)
  continue to match.
- data-text-role="item-title" assertions inside each section.
- All data-testid hooks (interpretation-panel, interpretation-
  warnings, mix-chain-applied-progress, patches-applied-progress,
  measurements-section, etc.).
- The `border-b border-border pb-2` outer container class so
  spacing is identical.

Deferred (not in D.5d scope):
- Per-section DeviceRack wraps were considered (per the plan's
  "Section wrappers → <DeviceRack>" item) but would have created
  duplicate title strips wherever a section also has a
  ResultsSectionHeader — the rack title strip + the SectionHeader
  h2 would both render section identity. Each affected section
  would need a custom "drop the SectionHeader, rely only on the
  rack name" call which doesn't fit a single mechanical change.
  Save for a focused follow-up if/when the rack chrome is wanted
  on these surfaces.

Verified: lint clean, 628/628 unit tests passing, build clean
(AnalysisResults chunk 254 KB / 63 KB gz — marginally smaller than
PR 8 due to less inline JSX in the section header).

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe

Co-authored-by: Claude <noreply@anthropic.com>
slittycode pushed a commit that referenced this pull request May 18, 2026
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
slittycode added a commit that referenced this pull request May 19, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants