From 273c360804bd1893635e091e1b5be38d1f95ad20 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 19 Jun 2026 04:21:57 +0000 Subject: [PATCH] UI overhaul Phase 2c: migrate MeasurementDashboard badges to ui/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Third slice of retiring MeasurementPrimitives.tsx — the dashboard now imports nothing from it. - StatusBadge (17 compact chips): introduce a tiny local adapter over ui/Pill that maps the legacy off-palette tones (muted/info/violet) onto the token palette in one place, so the call sites and chordToneForLabel stay untouched ahead of the Phase 5 split. Minor-chord violet collapses to neutral. Not a competing primitive — local sugar over Pill. - OutlinePillButton (sample-generate control) -> ui/Button variant=secondary (it already rendered muted/inactive, so secondary matches). - DeltaBadge, TokenBadgeList -> ui/DeltaBadge, ui/TokenBadgeList; the genre secondary tone remaps muted -> neutral. - Add the missing ui/DeltaBadge and ui/TokenBadgeList Storybook stories, restoring the "every ui/ primitive ships a story" convention (Chromatic picks them up). Only the four DAW-lane primitives remain in MeasurementPrimitives.tsx; Phase 2d moves them into ui/ and deletes the file. Verified: lint:style, tsc --noEmit, 830 unit tests, and the production build all green. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_016to3B8Hf9eAGxAbzpQNvku --- .../src/components/MeasurementDashboard.tsx | 59 ++++++++++++++++--- .../src/components/ui/DeltaBadge.stories.tsx | 26 ++++++++ .../components/ui/TokenBadgeList.stories.tsx | 34 +++++++++++ 3 files changed, 110 insertions(+), 9 deletions(-) create mode 100644 apps/ui/src/components/ui/DeltaBadge.stories.tsx create mode 100644 apps/ui/src/components/ui/TokenBadgeList.stories.tsx diff --git a/apps/ui/src/components/MeasurementDashboard.tsx b/apps/ui/src/components/MeasurementDashboard.tsx index 8a6ac0f4..f226210e 100644 --- a/apps/ui/src/components/MeasurementDashboard.tsx +++ b/apps/ui/src/components/MeasurementDashboard.tsx @@ -25,18 +25,57 @@ import { MiniHeatmap } from './MiniHeatmap'; import { ConfidenceBandBadge } from './sessionMusician/ConfidenceBandBadge'; import { MixDoctorPanel } from './MixDoctorPanel'; import { + Button, + DataTable, DeltaBadge, - OutlinePillButton, - StatusBadge, + DeviceRack, + EmptyState, + MetricBar, + MetricBarRow, + MetricTile, + Pill, TokenBadgeList, -} from './MeasurementPrimitives'; -import { DataTable, DeviceRack, EmptyState, MetricBar, MetricBarRow, MetricTile } from './ui'; + type Tone, +} from './ui'; import { Sparkline } from './Sparkline'; import { SpectralCursorProvider } from '../hooks/useSpectralCursorBus'; import { formatDisplayText, getTextRoleClassName } from '../utils/displayText'; import { HarmonyLanes } from './HarmonyLanes'; import { StructureLanes } from './StructureLanes'; +// Local adapter over the canonical ui/Pill. The dashboard's detail rows use a +// compact label/tone/compact status chip in ~17 places (and chordToneForLabel +// still emits the legacy off-palette tones); this maps those onto the token +// palette in one spot so the call sites stay untouched ahead of the Phase 5 +// split. Not a competing primitive — just local sugar over Pill. +type LegacyBadgeTone = Tone | 'muted' | 'info' | 'violet'; +const PILL_TONE_FOR_LEGACY: Record = { + accent: 'accent', + success: 'success', + warning: 'warning', + error: 'error', + neutral: 'neutral', + muted: 'neutral', + info: 'neutral', + violet: 'neutral', +}; + +function StatusBadge({ + label, + tone = 'neutral', + compact = false, +}: { + label: React.ReactNode; + tone?: LegacyBadgeTone; + compact?: boolean; +}) { + return ( + + {label} + + ); +} + interface MeasurementDashboardProps { phase1: Phase1Result; spectralArtifacts?: SpectralArtifacts | null; @@ -1457,7 +1496,7 @@ export function MeasurementDashboard({ items={[ { label: phase1.genreDetail.genreFamily, tone: 'accent' }, ...(phase1.genreDetail.secondaryGenre - ? [{ label: phase1.genreDetail.secondaryGenre, tone: 'muted' as const }] + ? [{ label: phase1.genreDetail.secondaryGenre, tone: 'neutral' as const }] : []), ]} /> @@ -2026,12 +2065,14 @@ export function MeasurementDashboard({ ) : ( - handleGenerate(kind)} disabled={generating.has(kind)} - tone="accent" - label={generating.has(kind) ? `${label}...` : `Generate ${label}`} - /> + > + {generating.has(kind) ? `${label}...` : `Generate ${label}`} + ), )} diff --git a/apps/ui/src/components/ui/DeltaBadge.stories.tsx b/apps/ui/src/components/ui/DeltaBadge.stories.tsx new file mode 100644 index 00000000..98eb5aab --- /dev/null +++ b/apps/ui/src/components/ui/DeltaBadge.stories.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import type { Meta, StoryObj } from '@storybook/react-vite'; + +import { DeltaBadge } from './DeltaBadge'; + +const meta: Meta = { + title: 'UI/DeltaBadge', + component: DeltaBadge, + args: { value: -1.8, unit: 'dB', okThreshold: 0.5, warnThreshold: 1.5 }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; + +export const ThresholdLadder: Story = { + render: () => ( +
+ + + + +
+ ), +}; diff --git a/apps/ui/src/components/ui/TokenBadgeList.stories.tsx b/apps/ui/src/components/ui/TokenBadgeList.stories.tsx new file mode 100644 index 00000000..728e1df7 --- /dev/null +++ b/apps/ui/src/components/ui/TokenBadgeList.stories.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import type { Meta, StoryObj } from '@storybook/react-vite'; + +import { TokenBadgeList } from './TokenBadgeList'; + +const meta: Meta = { + title: 'UI/TokenBadgeList', + component: TokenBadgeList, + args: { + items: [ + { label: 'house', tone: 'accent' }, + { label: 'techno', tone: 'neutral' }, + ], + }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; + +export const Tones: Story = { + render: () => ( + + ), +};