import { useState, type CSSProperties, type JSX, type ReactNode } from 'react' import type { ScopeKind } from '../../types/scope' import { SCOPE_LABELS, isTransformableScopeKind } from '../../types/scope' import type { ScopeSettings } from '../../types/settings' import { SCOPE_DISPLAY_ROTATIONS, type ScopeDisplayRotation } from '../../types/scopeTransform' import { MAX_SPECTROGRAM_CONTRAST, MAX_SPECTROGRAM_TILT_DB_PER_OCTAVE, MIN_SPECTROGRAM_CONTRAST, MIN_SPECTROGRAM_TILT_DB_PER_OCTAVE, SPECTROGRAM_CONTRAST_STEP, SPECTROGRAM_TILT_STEP, } from '../../types/spectrogram' import { DEFAULT_VU_REFERENCE_DBFS, VU_REFERENCE_MAX_DBFS, VU_REFERENCE_MIN_DBFS, VU_REFERENCE_PRESETS, findVUReferencePreset, sanitizeVUReferenceDbfs, } from '../../types/vumeter' import { MAX_WAVEFORM_SCROLL_SPEED, MIN_WAVEFORM_SCROLL_SPEED, WAVEFORM_SCROLL_SPEED_STEP, } from '../../types/waveform' import ThemedSelect from './ThemedSelect' function vectorscopeModeLabel(mode: ScopeSettings['vectorscope']['mode']): string { switch (mode) { case 'lissajous': return 'Lissajous' case 'polar-unipolar': return 'Polar Uni' case 'polar-bipolar': return 'Polar Bi' case 'linear-unipolar': return 'Linear Uni' case 'linear-bipolar': return 'Linear Bi' } } function lufsReadoutLabel(readout: ScopeSettings['lufsmeter']['readout']): string { switch (readout) { case 'integrated': return 'Integrated' case 'shortTerm': return 'Short-term' case 'momentary': return 'Momentary' } } function nowPlayingVisibleLabels(settings: ScopeSettings['nowPlaying']): string[] { const labels: string[] = [] if (settings.showCoverArt) labels.push('Cover') if (settings.showTitle) labels.push('Title') if (settings.showArtist) labels.push('Artist') if (settings.showProgress) labels.push('Bar') if (settings.showTime) labels.push('Time') if (settings.showControls) labels.push('Controls') return labels } function appendTransformSummary( parts: string[], settings: { rotation: ScopeDisplayRotation; mirrorHorizontal: boolean }, ): string { if (settings.rotation !== 0) parts.push(`R${settings.rotation}°`) if (settings.mirrorHorizontal) parts.push('Mirror') return parts.join(' · ') } export function scopeSummary(kind: ScopeKind, settings: ScopeSettings[ScopeKind]): string { switch (kind) { case 'spectrum': { const scopeSettings = settings as ScopeSettings['spectrum'] const summary = `${scopeSettings.heatmap ? 'Heat' : 'Fill'} · FFT ${scopeSettings.fftSize}` const parts = [summary] if (scopeSettings.showSideLine) { parts.push('Side') } if (scopeSettings.peakInfoMode === 'on') { parts.push('Peak') } else if (scopeSettings.peakInfoMode === 'following') { parts.push('Peak Follow') } return appendTransformSummary(parts, scopeSettings) } case 'oscilloscope': { const scopeSettings = settings as ScopeSettings['oscilloscope'] const mode = scopeSettings.pitchLock ? 'Pitch Lock' : 'Free Run' return appendTransformSummary( scopeSettings.underfillEnabled ? [mode, 'Fill'] : [mode], scopeSettings, ) } case 'vectorscope': { const scopeSettings = settings as ScopeSettings['vectorscope'] return scopeSettings.multiband ? `${vectorscopeModeLabel(scopeSettings.mode)} · RGB` : vectorscopeModeLabel(scopeSettings.mode) } case 'spectrogram': { const scopeSettings = settings as ScopeSettings['spectrogram'] return [ `${scopeSettings.rotation}°`, scopeSettings.scaleMode.toUpperCase(), scopeSettings.clarityMode, ...(scopeSettings.mirrorHorizontal ? ['Mirror'] : []), ].join(' · ') } case 'vumeter': { const scopeSettings = settings as ScopeSettings['vumeter'] const matchedPreset = findVUReferencePreset(scopeSettings.referenceDb) const refLabel = matchedPreset ? matchedPreset.label : `${scopeSettings.referenceDb.toFixed(1)} dBFS` const base = scopeSettings.mode === 'needle' ? `${scopeSettings.mode.toUpperCase()} · ${scopeSettings.needleChannels.toUpperCase()}` : `${scopeSettings.mode.toUpperCase()} · ${scopeSettings.orientation.toUpperCase()}` return `${base} · ${refLabel}` } case 'lufsmeter': return `${lufsReadoutLabel((settings as ScopeSettings['lufsmeter']).readout)} LUFS` case 'waveform': { const scopeSettings = settings as ScopeSettings['waveform'] const summary = [scopeSettings.mode === 'stereo' ? 'Stereo' : 'Mono'] if (scopeSettings.multiband) { summary.push('RGB') } return appendTransformSummary(summary, scopeSettings) } case 'nowPlaying': { const visible = nowPlayingVisibleLabels(settings as ScopeSettings['nowPlaying']) return visible.length > 0 ? visible.join(' · ') : 'Hidden' } } } function ToggleChip({ label, active, onClick, }: { label: string active: boolean onClick: () => void }): JSX.Element { return ( ) } function ToggleGroup({ label, children, }: { label: string children: ReactNode }): JSX.Element { return (