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: () => ( + + ), +};