@waveform-playlist/ui-components 5.0.0-alpha.6 → 5.0.0-alpha.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.tsx","../src/components/AudioPosition.tsx","../src/styled/BaseButton.tsx","../src/styled/BaseCheckbox.tsx","../src/styled/BaseControlButton.tsx","../src/styled/BaseInput.tsx","../src/styled/BaseLabel.tsx","../src/styled/BaseSelect.tsx","../src/styled/BaseSlider.tsx","../src/components/AutomaticScrollCheckbox.tsx","../src/components/Channel.tsx","../src/wfpl-theme.ts","../src/components/Clip.tsx","../src/components/ClipHeader.tsx","../src/components/ClipBoundary.tsx","../src/components/FadeOverlay.tsx","../src/components/MasterVolumeControl.tsx","../src/components/Playhead.tsx","../src/components/Playlist.tsx","../src/components/Selection.tsx","../src/components/SelectionTimeInputs.tsx","../src/components/TimeInput.tsx","../src/utils/timeFormat.ts","../src/contexts/DevicePixelRatio.tsx","../src/contexts/PlaylistInfo.tsx","../src/contexts/Theme.tsx","../src/contexts/TrackControls.tsx","../src/contexts/Playout.tsx","../src/components/SmartChannel.tsx","../src/components/SmartScale.tsx","../src/components/TimeScale.tsx","../src/utils/conversions.ts","../src/components/TimeFormatSelect.tsx","../src/components/Track.tsx","../src/components/TrackControls/Button.tsx","../src/components/TrackControls/ButtonGroup.tsx","../src/components/TrackControls/Controls.tsx","../src/components/TrackControls/Header.tsx","../src/components/TrackControls/VolumeDownIcon.tsx","../src/components/TrackControls/VolumeUpIcon.tsx","../src/components/TrackControls/TrashIcon.tsx","../src/components/TrackControls/Slider.tsx","../src/components/TrackControls/SliderWrapper.tsx","../src/components/TrackControlsWithDelete.tsx"],"sourcesContent":["export * from './components';\nexport * from './contexts';\nexport * from './utils/conversions';\nexport * from './utils/timeFormat';\nexport * from './styled/index';\nexport * from './wfpl-theme';\n","import React from 'react';\nimport styled from 'styled-components';\n\nconst PositionDisplay = styled.span`\n font-family: 'Courier New', Monaco, monospace;\n font-size: 1rem;\n font-weight: 600;\n color: ${props => props.theme?.textColor || '#333'};\n user-select: none;\n`;\n\nexport interface AudioPositionProps {\n formattedTime: string;\n className?: string;\n}\n\n/**\n * Displays the current audio playback position\n */\nexport const AudioPosition: React.FC<AudioPositionProps> = ({\n formattedTime,\n className,\n}) => {\n return (\n <PositionDisplay className={className} aria-label=\"Audio position\">\n {formattedTime}\n </PositionDisplay>\n );\n};\n","import styled from 'styled-components';\n\n/**\n * BaseButton - A styled button component that uses theme values\n *\n * This provides consistent styling across all button elements in the waveform playlist.\n */\nexport const BaseButton = styled.button`\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 0.5rem 1rem;\n font-family: ${(props) => props.theme.fontFamily};\n font-size: ${(props) => props.theme.fontSize};\n font-weight: 500;\n color: ${(props) => props.theme.buttonText};\n background-color: ${(props) => props.theme.buttonBackground};\n border: 1px solid ${(props) => props.theme.buttonBorder};\n border-radius: ${(props) => props.theme.borderRadius};\n cursor: pointer;\n outline: none;\n transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out,\n box-shadow 0.15s ease-in-out;\n\n &:hover:not(:disabled) {\n background-color: ${(props) => props.theme.buttonHoverBackground};\n }\n\n &:focus {\n box-shadow: 0 0 0 2px ${(props) => props.theme.inputFocusBorder}33;\n }\n\n &:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n`;\n\n/**\n * BaseButtonSmall - A smaller variant for compact layouts\n */\nexport const BaseButtonSmall = styled(BaseButton)`\n padding: 0.25rem 0.5rem;\n font-size: ${(props) => props.theme.fontSizeSmall};\n`;\n\n/**\n * IconButton - A square button for icons\n */\nexport const IconButton = styled(BaseButton)`\n padding: 0.5rem;\n min-width: 2.25rem;\n min-height: 2.25rem;\n`;\n\n/**\n * IconButtonSmall - A smaller square button for icons\n */\nexport const IconButtonSmall = styled(BaseButton)`\n padding: 0.25rem;\n min-width: 1.75rem;\n min-height: 1.75rem;\n font-size: ${(props) => props.theme.fontSizeSmall};\n`;\n","import styled from 'styled-components';\n\n/**\n * BaseCheckboxWrapper - Container for checkbox + label\n */\nexport const BaseCheckboxWrapper = styled.div`\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n`;\n\n/**\n * BaseCheckbox - A styled checkbox input\n */\nexport const BaseCheckbox = styled.input`\n cursor: pointer;\n accent-color: ${(props) => props.theme.inputFocusBorder};\n\n &:disabled {\n cursor: not-allowed;\n }\n`;\n\n/**\n * BaseCheckboxLabel - Label for checkboxes\n */\nexport const BaseCheckboxLabel = styled.label`\n margin: 0;\n cursor: pointer;\n user-select: none;\n font-family: ${(props) => props.theme.fontFamily};\n font-size: ${(props) => props.theme.fontSize};\n color: ${(props) => props.theme.textColor};\n`;\n","import styled from 'styled-components';\n\nexport interface ControlButtonProps {\n variant?: 'primary' | 'success' | 'info';\n}\n\n/**\n * ControlButton - A colored action button (primary/success/info variants)\n *\n * This is used for prominent actions like Play, Pause, Record.\n * For neutral buttons, use BaseButton from the styled primitives.\n *\n * Uses theme colors when available, with fallbacks for standalone use.\n */\nexport const BaseControlButton = styled.button<ControlButtonProps>`\n padding: 0.5rem 1rem;\n background: ${(props) => props.theme.buttonBackground || '#007bff'};\n color: ${(props) => props.theme.buttonText || 'white'};\n border: none;\n border-radius: ${(props) => props.theme.borderRadius};\n cursor: pointer;\n font-family: ${(props) => props.theme.fontFamily};\n font-size: ${(props) => props.theme.fontSize};\n font-weight: 500;\n transition: background-color 0.15s ease-in-out;\n\n &:hover:not(:disabled) {\n background: ${(props) => props.theme.buttonHoverBackground || '#0056b3'};\n }\n\n &:focus {\n outline: none;\n box-shadow: 0 0 0 2px ${(props) => props.theme.buttonBackground || '#007bff'}66;\n }\n\n &:disabled {\n background: #6c757d;\n cursor: not-allowed;\n opacity: 0.6;\n }\n`;\n","import styled from 'styled-components';\n\n/**\n * BaseInput - A styled input component that uses theme values\n *\n * This provides consistent styling across all input elements in the waveform playlist.\n * Styling is controlled via the theme, making it easy to adapt to different environments.\n */\nexport const BaseInput = styled.input`\n padding: 0.5rem 0.75rem;\n font-family: ${(props) => props.theme.fontFamily};\n font-size: ${(props) => props.theme.fontSize};\n color: ${(props) => props.theme.inputText};\n background-color: ${(props) => props.theme.inputBackground};\n border: 1px solid ${(props) => props.theme.inputBorder};\n border-radius: ${(props) => props.theme.borderRadius};\n outline: none;\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n\n &::placeholder {\n color: ${(props) => props.theme.inputPlaceholder};\n }\n\n &:focus {\n border-color: ${(props) => props.theme.inputFocusBorder};\n box-shadow: 0 0 0 2px ${(props) => props.theme.inputFocusBorder}33;\n }\n\n &:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n`;\n\n/**\n * BaseInputSmall - A smaller variant for compact layouts\n */\nexport const BaseInputSmall = styled(BaseInput)`\n padding: 0.25rem 0.5rem;\n font-size: ${(props) => props.theme.fontSizeSmall};\n`;\n","import styled from 'styled-components';\n\n/**\n * BaseLabel - A styled label component that uses theme values\n */\nexport const BaseLabel = styled.label`\n font-family: ${(props) => props.theme.fontFamily};\n font-size: ${(props) => props.theme.fontSizeSmall};\n font-weight: 500;\n color: ${(props) => props.theme.textColorMuted};\n margin-bottom: 0.25rem;\n display: block;\n`;\n\n/**\n * InlineLabel - A label that displays inline with its input\n */\nexport const InlineLabel = styled.label`\n font-family: ${(props) => props.theme.fontFamily};\n font-size: ${(props) => props.theme.fontSize};\n color: ${(props) => props.theme.textColor};\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n cursor: pointer;\n`;\n\n/**\n * ScreenReaderOnly - Visually hidden but accessible to screen readers\n */\nexport const ScreenReaderOnly = styled.span`\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n`;\n","import styled from 'styled-components';\n\n/**\n * BaseSelect - A styled select component that uses theme values\n *\n * This provides consistent styling across all select elements in the waveform playlist.\n */\nexport const BaseSelect = styled.select`\n padding: 0.5rem 2rem 0.5rem 0.75rem;\n font-family: ${(props) => props.theme.fontFamily};\n font-size: ${(props) => props.theme.fontSize};\n color: ${(props) => props.theme.inputText};\n background-color: ${(props) => props.theme.inputBackground};\n border: 1px solid ${(props) => props.theme.inputBorder};\n border-radius: ${(props) => props.theme.borderRadius};\n outline: none;\n cursor: pointer;\n appearance: none;\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23666' d='M6 8L1 3h10z'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 0.75rem center;\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n\n &:focus {\n border-color: ${(props) => props.theme.inputFocusBorder};\n box-shadow: 0 0 0 2px ${(props) => props.theme.inputFocusBorder}33;\n }\n\n &:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n /* Style native option elements for dark mode support */\n option {\n color: ${(props) => props.theme.inputText};\n background-color: ${(props) => props.theme.inputBackground};\n }\n`;\n\n/**\n * BaseSelectSmall - A smaller variant for compact layouts\n */\nexport const BaseSelectSmall = styled(BaseSelect)`\n padding: 0.25rem 1.75rem 0.25rem 0.5rem;\n font-size: ${(props) => props.theme.fontSizeSmall};\n`;\n","import styled from 'styled-components';\n\n/**\n * BaseSlider - Themed range input for volume controls, etc.\n *\n * Uses theme values for consistent styling across light/dark modes.\n * Provides custom styling for the track and thumb.\n */\nexport const BaseSlider = styled.input.attrs({ type: 'range' })`\n -webkit-appearance: none;\n appearance: none;\n width: 100%;\n height: 6px;\n background: ${(props) => props.theme.sliderTrackColor};\n border-radius: 3px;\n cursor: pointer;\n outline: none;\n\n /* WebKit (Chrome, Safari) */\n &::-webkit-slider-thumb {\n -webkit-appearance: none;\n appearance: none;\n width: 16px;\n height: 16px;\n background: ${(props) => props.theme.sliderThumbColor};\n border: 2px solid ${(props) => props.theme.inputBackground};\n border-radius: 50%;\n cursor: pointer;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n transition: transform 0.15s ease, box-shadow 0.15s ease;\n }\n\n &::-webkit-slider-thumb:hover {\n transform: scale(1.1);\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n }\n\n /* Firefox */\n &::-moz-range-thumb {\n width: 16px;\n height: 16px;\n background: ${(props) => props.theme.sliderThumbColor};\n border: 2px solid ${(props) => props.theme.inputBackground};\n border-radius: 50%;\n cursor: pointer;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n transition: transform 0.15s ease, box-shadow 0.15s ease;\n }\n\n &::-moz-range-thumb:hover {\n transform: scale(1.1);\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n }\n\n &::-moz-range-track {\n background: ${(props) => props.theme.sliderTrackColor};\n border-radius: 3px;\n height: 6px;\n }\n\n &:focus {\n outline: none;\n }\n\n &:focus::-webkit-slider-thumb {\n box-shadow: 0 0 0 3px ${(props) => props.theme.inputFocusBorder}33;\n }\n\n &:focus::-moz-range-thumb {\n box-shadow: 0 0 0 3px ${(props) => props.theme.inputFocusBorder}33;\n }\n\n &:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n }\n\n &:disabled::-webkit-slider-thumb {\n cursor: not-allowed;\n }\n\n &:disabled::-moz-range-thumb {\n cursor: not-allowed;\n }\n`;\n","import React from 'react';\nimport { BaseCheckboxWrapper, BaseCheckbox, BaseCheckboxLabel } from '../styled/index';\n\nexport interface AutomaticScrollCheckboxProps {\n checked: boolean;\n onChange: (checked: boolean) => void;\n disabled?: boolean;\n className?: string;\n}\n\n/**\n * Checkbox control for enabling/disabling automatic scroll during playback\n */\nexport const AutomaticScrollCheckbox: React.FC<AutomaticScrollCheckboxProps> = ({\n checked,\n onChange,\n disabled = false,\n className,\n}) => {\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n onChange(e.target.checked);\n };\n\n return (\n <BaseCheckboxWrapper className={className}>\n <BaseCheckbox\n type=\"checkbox\"\n id=\"automatic-scroll\"\n className=\"automatic-scroll\"\n checked={checked}\n onChange={handleChange}\n disabled={disabled}\n />\n <BaseCheckboxLabel htmlFor=\"automatic-scroll\">Automatic Scroll</BaseCheckboxLabel>\n </BaseCheckboxWrapper>\n );\n};\n","import React, { FunctionComponent, useLayoutEffect, useCallback, useRef } from 'react';\nimport styled from 'styled-components';\nimport { Peaks, Bits } from '@waveform-playlist/webaudio-peaks';\nimport { WaveformColor, WaveformDrawMode, isWaveformGradient, waveformColorToCss } from '../wfpl-theme';\n\n// Re-export WaveformColor for consumers\nexport type { WaveformColor } from '../wfpl-theme';\nexport type { WaveformDrawMode } from '../wfpl-theme';\n\nconst MAX_CANVAS_WIDTH = 1000;\n\n/**\n * Creates a Canvas gradient from a WaveformColor configuration\n */\nfunction createCanvasFillStyle(\n ctx: CanvasRenderingContext2D,\n color: WaveformColor,\n width: number,\n height: number\n): string | CanvasGradient {\n if (!isWaveformGradient(color)) {\n return color;\n }\n\n let gradient: CanvasGradient;\n if (color.direction === 'vertical') {\n gradient = ctx.createLinearGradient(0, 0, 0, height);\n } else {\n gradient = ctx.createLinearGradient(0, 0, width, 0);\n }\n\n for (const stop of color.stops) {\n gradient.addColorStop(stop.offset, stop.color);\n }\n\n return gradient;\n}\n\ninterface WaveformProps {\n readonly $cssWidth: number;\n readonly $waveHeight: number;\n}\n\nconst Waveform = styled.canvas.attrs<WaveformProps>((props) => ({\n style: {\n width: `${props.$cssWidth}px`,\n height: `${props.$waveHeight}px`,\n },\n}))<WaveformProps>`\n float: left;\n position: relative;\n /* Promote to own compositing layer for smoother scrolling */\n will-change: transform;\n /* Disable image rendering interpolation */\n image-rendering: pixelated;\n image-rendering: crisp-edges;\n`;\n\ninterface WrapperProps {\n readonly $index: number;\n readonly $cssWidth: number;\n readonly $waveHeight: number;\n readonly $waveFillColor: string; // CSS background value (solid or gradient)\n}\n\nconst Wrapper = styled.div.attrs<WrapperProps>((props) => ({\n style: {\n top: `${props.$waveHeight * props.$index}px`,\n width: `${props.$cssWidth}px`,\n height: `${props.$waveHeight}px`,\n },\n}))<WrapperProps>`\n position: absolute;\n background: ${(props) => props.$waveFillColor};\n /* Force GPU compositing layer to reduce scroll flickering */\n transform: translateZ(0);\n backface-visibility: hidden;\n`;\n\nexport interface ChannelProps {\n className?: string;\n index: number;\n data: Peaks;\n bits: Bits;\n length: number;\n devicePixelRatio?: number;\n waveHeight?: number;\n /** Waveform bar color - can be a solid color string or gradient config */\n waveOutlineColor?: WaveformColor;\n /** Waveform background color - can be a solid color string or gradient config */\n waveFillColor?: WaveformColor;\n /** Width in pixels of waveform bars. Default: 1 */\n barWidth?: number;\n /** Spacing in pixels between waveform bars. Default: 0 */\n barGap?: number;\n /** If true, background is transparent (for use with external progress overlay) */\n transparentBackground?: boolean;\n /**\n * Drawing mode:\n * - 'inverted': Draw waveOutlineColor where there's NO audio (current default). Good for gradient bars.\n * - 'normal': Draw waveFillColor where there IS audio. Good for gradient backgrounds.\n */\n drawMode?: WaveformDrawMode;\n}\n\nexport const Channel: FunctionComponent<ChannelProps> = (props) => {\n const {\n data,\n bits,\n length,\n index,\n className,\n devicePixelRatio = 1,\n waveHeight = 80,\n waveOutlineColor = '#E0EFF1',\n waveFillColor = 'grey',\n barWidth = 1,\n barGap = 0,\n transparentBackground = false,\n drawMode = 'inverted',\n } = props;\n const canvasesRef = useRef<HTMLCanvasElement[]>([]);\n\n const canvasRef = useCallback(\n (canvas: HTMLCanvasElement | null) => {\n if (canvas !== null) {\n const index: number = parseInt(canvas.dataset.index!, 10);\n canvasesRef.current[index] = canvas;\n }\n },\n []\n );\n\n useLayoutEffect(() => {\n const canvases = canvasesRef.current;\n const step = barWidth + barGap;\n let globalPixelOffset = 0; // Track global pixel position across all canvases\n\n for (let i = 0; i < canvases.length; i++) {\n const canvas = canvases[i];\n const ctx = canvas.getContext('2d');\n const h2 = Math.floor(waveHeight / 2);\n const maxValue = 2 ** (bits - 1);\n\n if (ctx) {\n ctx.resetTransform();\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.imageSmoothingEnabled = false;\n ctx.scale(devicePixelRatio, devicePixelRatio);\n\n // Create gradient using CSS pixel coordinates (after scaling)\n // This ensures the gradient aligns with the drawing coordinates\n const canvasWidth = canvas.width / devicePixelRatio;\n\n // Choose canvas fill color based on draw mode:\n let fillColor: WaveformColor;\n if (drawMode === 'normal') {\n // Normal: canvas draws the bars directly\n fillColor = waveFillColor;\n } else {\n // Inverted: canvas masks non-audio areas, background shows as bars\n fillColor = waveOutlineColor;\n }\n ctx.fillStyle = createCanvasFillStyle(\n ctx,\n fillColor,\n canvasWidth,\n waveHeight\n );\n\n // Calculate where bars should be drawn in this canvas\n // by finding where in the global bar pattern this canvas starts\n const canvasStartGlobal = globalPixelOffset;\n const canvasEndGlobal = globalPixelOffset + canvasWidth;\n\n // Find the first bar that could affect this canvas\n // A bar at position X extends from X to X+barWidth-1\n // So we need bars where barStart + barWidth > canvasStartGlobal\n // Which means barStart > canvasStartGlobal - barWidth\n const firstBarGlobal = Math.floor((canvasStartGlobal - barWidth + step) / step) * step;\n\n // Draw bars at the correct positions\n for (let barGlobal = Math.max(0, firstBarGlobal); barGlobal < canvasEndGlobal; barGlobal += step) {\n const x = barGlobal - canvasStartGlobal; // Local x position in this canvas\n\n // Skip if the entire bar would be before this canvas\n if (x + barWidth <= 0) continue;\n\n const peakIndex = barGlobal; // Each pixel position corresponds to a peak\n\n if (peakIndex * 2 + 1 < data.length) {\n const minPeak = data[peakIndex * 2] / maxValue;\n const maxPeak = data[peakIndex * 2 + 1] / maxValue;\n\n const min = Math.abs(minPeak * h2);\n const max = Math.abs(maxPeak * h2);\n\n if (drawMode === 'normal') {\n // Normal mode: draw the actual peak bars\n // Draw from h2-max to h2+min (the actual waveform shape)\n ctx.fillRect(x, h2 - max, barWidth, max + min);\n } else {\n // Inverted mode (default): draw areas WITHOUT audio\n // This masks the background color to reveal the peaks\n // draw top region (above max peak)\n ctx.fillRect(x, 0, barWidth, h2 - max);\n // draw bottom region (below min peak)\n ctx.fillRect(x, h2 + min, barWidth, h2 - min);\n }\n }\n }\n }\n\n globalPixelOffset += canvas.width / devicePixelRatio;\n }\n }, [\n data,\n bits,\n waveHeight,\n waveOutlineColor,\n waveFillColor,\n devicePixelRatio,\n length,\n barWidth,\n barGap,\n drawMode,\n ]);\n\n let totalWidth = length;\n let waveformCount = 0;\n const waveforms = [];\n while (totalWidth > 0) {\n const currentWidth = Math.min(totalWidth, MAX_CANVAS_WIDTH);\n const waveform = (\n <Waveform\n key={`${length}-${waveformCount}`}\n $cssWidth={currentWidth}\n width={currentWidth * devicePixelRatio}\n height={waveHeight * devicePixelRatio}\n $waveHeight={waveHeight}\n data-index={waveformCount}\n ref={canvasRef}\n />\n );\n\n waveforms.push(waveform);\n totalWidth -= currentWidth;\n waveformCount += 1;\n }\n\n // Background color depends on draw mode:\n // Visual result is always: waveOutlineColor = bars, waveFillColor = background\n // - normal: waveFillColor is background, canvas draws waveOutlineColor bars on top\n // - inverted: waveFillColor is background, canvas masks with it to reveal waveOutlineColor (bars)\n const bgColor = waveFillColor;\n const backgroundCss = transparentBackground ? 'transparent' : waveformColorToCss(bgColor);\n\n return (\n <Wrapper\n $index={index}\n $cssWidth={length}\n className={className}\n $waveHeight={waveHeight}\n $waveFillColor={backgroundCss}\n >\n {waveforms}\n </Wrapper>\n );\n};\n","/**\n * Waveform Playlist Theme\n *\n * This file defines the theme interface and default values for the waveform playlist components.\n */\n\n/**\n * Gradient color stop for waveform gradients\n */\nexport interface GradientStop {\n offset: number; // 0 to 1\n color: string;\n}\n\n/**\n * Gradient configuration for waveforms\n * Can be applied vertically (top to bottom) or horizontally (left to right)\n */\nexport interface WaveformGradient {\n type: 'linear';\n direction: 'vertical' | 'horizontal';\n stops: GradientStop[];\n}\n\n/**\n * Waveform color can be a simple string or a gradient configuration\n */\nexport type WaveformColor = string | WaveformGradient;\n\n/**\n * Type guard to check if a WaveformColor is a gradient\n */\nexport function isWaveformGradient(color: WaveformColor): color is WaveformGradient {\n return typeof color === 'object' && color !== null && 'type' in color;\n}\n\n/**\n * Converts WaveformColor to a CSS background value\n */\nexport function waveformColorToCss(color: WaveformColor): string {\n if (!isWaveformGradient(color)) {\n return color;\n }\n\n const direction = color.direction === 'vertical' ? 'to bottom' : 'to right';\n const stops = color.stops\n .map((stop) => `${stop.color} ${stop.offset * 100}%`)\n .join(', ');\n\n return `linear-gradient(${direction}, ${stops})`;\n}\n\n/**\n * Waveform drawing mode determines how colors are applied:\n * - 'inverted': Canvas draws waveOutlineColor in areas WITHOUT audio (current default).\n * waveFillColor shows through where audio peaks are. Good for gradient bars.\n * - 'normal': Canvas draws waveFillColor bars where audio peaks ARE.\n * waveOutlineColor is used as background. Good for gradient backgrounds.\n */\nexport type WaveformDrawMode = 'inverted' | 'normal';\n\nexport interface WaveformPlaylistTheme {\n // Waveform drawing mode - controls how colors are applied\n waveformDrawMode?: WaveformDrawMode;\n\n // Waveform colors - can be solid colors or gradients\n waveOutlineColor: WaveformColor;\n waveFillColor: WaveformColor;\n waveProgressColor: string; // Progress stays solid for simplicity\n\n // Selected track colors - can also be gradients\n selectedWaveOutlineColor: WaveformColor;\n selectedWaveFillColor: WaveformColor;\n selectedTrackControlsBackground: string;\n\n // Timescale colors\n timeColor: string;\n timescaleBackgroundColor: string;\n\n // Playback UI colors\n playheadColor: string;\n selectionColor: string;\n\n // Clip header colors (for multi-clip editing)\n clipHeaderBackgroundColor: string;\n clipHeaderBorderColor: string;\n clipHeaderTextColor: string;\n clipHeaderFontFamily: string;\n\n // Selected clip header colors\n selectedClipHeaderBackgroundColor: string;\n\n // Fade overlay colors\n fadeOverlayColor: string;\n\n // UI component colors\n backgroundColor: string;\n surfaceColor: string;\n borderColor: string;\n textColor: string;\n textColorMuted: string;\n\n // Interactive element colors\n inputBackground: string;\n inputBorder: string;\n inputText: string;\n inputPlaceholder: string;\n inputFocusBorder: string;\n\n // Button colors\n buttonBackground: string;\n buttonText: string;\n buttonBorder: string;\n buttonHoverBackground: string;\n\n // Slider colors\n sliderTrackColor: string;\n sliderThumbColor: string;\n\n // Annotation colors\n annotationBoxBackground: string;\n annotationBoxActiveBackground: string;\n annotationBoxHoverBackground: string;\n annotationBoxBorder: string;\n annotationBoxActiveBorder: string;\n annotationLabelColor: string;\n annotationResizeHandleColor: string;\n annotationResizeHandleActiveColor: string;\n annotationTextItemHoverBackground: string;\n\n // Spacing and sizing\n borderRadius: string;\n fontFamily: string;\n fontSize: string;\n fontSizeSmall: string;\n}\n\nexport const defaultTheme: WaveformPlaylistTheme = {\n waveformDrawMode: 'inverted',\n waveOutlineColor: '#ffffff',\n waveFillColor: '#1a7f8e', // White background for crisp look\n waveProgressColor: 'rgba(0, 0, 0, 0.10)', // Subtle dark overlay for light mode\n\n selectedWaveOutlineColor: '#ffffff',\n selectedWaveFillColor: '#00b4d8', // Selected: brighter cyan\n selectedTrackControlsBackground: '#d9e9ff', // Light blue background for selected track controls\n timeColor: '#000',\n timescaleBackgroundColor: '#fff',\n playheadColor: '#f00',\n selectionColor: 'rgba(255, 105, 180, 0.7)', // hot pink - high contrast on light backgrounds\n clipHeaderBackgroundColor: 'rgba(0, 0, 0, 0.1)',\n clipHeaderBorderColor: 'rgba(0, 0, 0, 0.2)',\n clipHeaderTextColor: '#333',\n clipHeaderFontFamily: 'inherit',\n selectedClipHeaderBackgroundColor: '#b3d9ff', // Brighter blue for selected track clip headers\n\n // Fade overlay colors\n fadeOverlayColor: 'rgba(0, 0, 0, 0.4)', // Semi-transparent overlay for fade regions\n\n // UI component colors\n backgroundColor: '#ffffff',\n surfaceColor: '#f5f5f5',\n borderColor: '#ddd',\n textColor: '#333',\n textColorMuted: '#666',\n\n // Interactive element colors\n inputBackground: '#ffffff',\n inputBorder: '#ccc',\n inputText: '#333',\n inputPlaceholder: '#999',\n inputFocusBorder: '#0066cc',\n\n // Button colors - blue to match common UI patterns\n buttonBackground: '#0091ff',\n buttonText: '#ffffff',\n buttonBorder: '#0081e6',\n buttonHoverBackground: '#0081e6',\n\n // Slider colors\n sliderTrackColor: '#ddd',\n sliderThumbColor: '#daa520', // goldenrod\n\n // Annotation colors\n annotationBoxBackground: 'rgba(255, 255, 255, 0.85)',\n annotationBoxActiveBackground: 'rgba(255, 255, 255, 0.95)',\n annotationBoxHoverBackground: 'rgba(255, 255, 255, 0.98)',\n annotationBoxBorder: '#ff9800',\n annotationBoxActiveBorder: '#d67600',\n annotationLabelColor: '#2a2a2a',\n annotationResizeHandleColor: 'rgba(0, 0, 0, 0.4)',\n annotationResizeHandleActiveColor: 'rgba(0, 0, 0, 0.8)',\n annotationTextItemHoverBackground: 'rgba(0, 0, 0, 0.03)',\n\n // Spacing and sizing\n borderRadius: '4px',\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen, Ubuntu, sans-serif',\n fontSize: '14px',\n fontSizeSmall: '12px',\n};\n\nexport const darkTheme: WaveformPlaylistTheme = {\n // Normal mode: waveOutlineColor = bars, waveFillColor = background\n waveformDrawMode: 'inverted',\n // Dark bars on warm amber background\n waveOutlineColor: '#c49a6c', // Solid warm amber for background\n waveFillColor: '#1a1612', // Very dark warm brown for bars\n waveProgressColor: 'rgba(100, 70, 40, 0.6)', // Warm brown progress overlay\n // Selected: slightly lighter bars on brighter amber background\n selectedWaveFillColor: '#241c14', // Slightly lighter warm brown bars when selected\n selectedWaveOutlineColor: '#e8c090', // Brighter amber background when selected\n selectedTrackControlsBackground: '#2a2218', // Dark warm brown for selected track controls\n timeColor: '#d8c0a8', // Warm amber for timescale text\n timescaleBackgroundColor: '#1a1612', // Dark warm brown background\n playheadColor: '#3a8838', // Darker Ampelmännchen green playhead\n selectionColor: 'rgba(60, 140, 58, 0.6)', // Darker Ampelmännchen green selection - visible on dark backgrounds\n clipHeaderBackgroundColor: 'rgba(20, 16, 12, 0.85)', // Dark background for clip headers\n clipHeaderBorderColor: 'rgba(200, 160, 120, 0.25)',\n clipHeaderTextColor: '#d8c0a8', // Warm amber text\n clipHeaderFontFamily: 'inherit',\n selectedClipHeaderBackgroundColor: '#3a2c20', // Darker warm brown for selected clip headers\n\n // Fade overlay colors\n fadeOverlayColor: 'rgba(200, 100, 80, 0.5)', // Warm red-orange overlay visible on dark backgrounds\n\n // UI component colors\n backgroundColor: '#1e1e1e',\n surfaceColor: '#2d2d2d',\n borderColor: '#444',\n textColor: '#e0e0e0',\n textColorMuted: '#999',\n\n // Interactive element colors\n inputBackground: '#2d2d2d',\n inputBorder: '#555',\n inputText: '#e0e0e0',\n inputPlaceholder: '#777',\n inputFocusBorder: '#4A9EFF',\n\n // Button colors - Ampelmännchen green (#63C75F) with black text\n buttonBackground: '#63C75F',\n buttonText: '#0a0a0f',\n buttonBorder: '#52b84e',\n buttonHoverBackground: '#78d074',\n\n // Slider colors\n sliderTrackColor: '#555',\n sliderThumbColor: '#f0c040', // brighter goldenrod for dark mode\n\n // Annotation colors (dark mode - warm amber theme)\n annotationBoxBackground: 'rgba(40, 32, 24, 0.9)',\n annotationBoxActiveBackground: 'rgba(50, 40, 30, 0.95)',\n annotationBoxHoverBackground: 'rgba(60, 48, 36, 0.98)',\n annotationBoxBorder: '#c49a6c',\n annotationBoxActiveBorder: '#d4a87c',\n annotationLabelColor: '#d8c0a8',\n annotationResizeHandleColor: 'rgba(200, 160, 120, 0.5)',\n annotationResizeHandleActiveColor: 'rgba(220, 180, 140, 0.8)',\n annotationTextItemHoverBackground: 'rgba(200, 160, 120, 0.08)',\n\n // Spacing and sizing\n borderRadius: '4px',\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen, Ubuntu, sans-serif',\n fontSize: '14px',\n fontSizeSmall: '12px',\n};\n","import React, { FunctionComponent, ReactNode } from 'react';\nimport styled from 'styled-components';\nimport { useDraggable } from '@dnd-kit/core';\nimport { CSS } from '@dnd-kit/utilities';\nimport { ClipHeader, CLIP_HEADER_HEIGHT } from './ClipHeader';\nimport { ClipBoundary, CLIP_BOUNDARY_WIDTH } from './ClipBoundary';\nimport { FadeOverlay } from './FadeOverlay';\nimport type { Fade } from '@waveform-playlist/core';\n\ninterface ClipContainerProps {\n readonly $left?: number; // Horizontal position in pixels (optional for DragOverlay)\n readonly $width?: number; // Width in pixels (optional for DragOverlay)\n readonly $isOverlay?: boolean; // Whether this is rendering in DragOverlay\n readonly $isDragging?: boolean; // Whether this clip is being dragged\n}\n\nconst ClipContainer = styled.div.attrs<ClipContainerProps>((props) => ({\n style: props.$isOverlay ? {} : {\n left: `${props.$left}px`,\n width: `${props.$width}px`,\n },\n}))<ClipContainerProps>`\n position: ${props => props.$isOverlay ? 'relative' : 'absolute'};\n top: 0;\n height: ${props => props.$isOverlay ? 'auto' : '100%'};\n width: ${props => props.$isOverlay ? `${props.$width}px` : 'auto'};\n display: flex;\n flex-direction: column;\n background: rgba(255, 255, 255, 0.05);\n z-index: 10; /* Above progress overlay (z-index: 2) but below controls/playhead */\n pointer-events: none; /* Let clicks pass through to ClickOverlay for playhead positioning */\n\n &:hover {\n background: rgba(255, 255, 255, 0.08);\n }\n`;\n\ninterface ChannelsWrapperProps {\n readonly $isOverlay?: boolean;\n}\n\nconst ChannelsWrapper = styled.div<ChannelsWrapperProps>`\n flex: 1;\n position: relative;\n overflow: ${props => props.$isOverlay ? 'visible' : 'hidden'};\n`;\n\nexport interface ClipProps {\n className?: string;\n children?: ReactNode;\n clipId: string; // Unique clip ID\n trackIndex: number; // Track index (for drag operations)\n clipIndex: number; // Clip index within track (for drag operations)\n trackName: string; // Track name (shown in header)\n startSample: number; // Start position in samples\n durationSamples: number; // Duration in samples\n samplesPerPixel: number;\n // Optional header (for multi-clip editing with drag-to-move)\n showHeader?: boolean;\n disableHeaderDrag?: boolean; // Disable drag on header (for presentation-only rendering)\n isOverlay?: boolean; // Rendering in DragOverlay (disables absolute positioning)\n // Track selection\n isSelected?: boolean; // Whether the track is selected\n onMouseDown?: (e: React.MouseEvent<HTMLDivElement>) => void; // Called when clip is pressed (for track selection - fires before drag)\n trackId?: string; // Track ID for identifying which track this clip belongs to\n // Fade configuration\n fadeIn?: Fade; // Fade in effect\n fadeOut?: Fade; // Fade out effect\n sampleRate?: number; // Sample rate for converting fade duration to pixels\n showFades?: boolean; // Show fade in/out overlays\n // Mobile optimization\n touchOptimized?: boolean; // Enable larger touch targets for mobile devices\n}\n\n/**\n * Clip component for rendering individual audio clips within a track\n *\n * Each clip is positioned based on its startTime and has a width based on its duration.\n * This allows multiple clips to be arranged on a single track with gaps or overlaps.\n *\n * Includes a draggable ClipHeader at the top for repositioning clips on the timeline.\n */\nexport const Clip: FunctionComponent<ClipProps> = ({\n children,\n className,\n clipId,\n trackIndex,\n clipIndex,\n trackName,\n startSample,\n durationSamples,\n samplesPerPixel,\n showHeader = false,\n disableHeaderDrag = false,\n isOverlay = false,\n isSelected = false,\n onMouseDown,\n trackId,\n fadeIn,\n fadeOut,\n sampleRate = 44100,\n showFades = false,\n touchOptimized = false,\n}) => {\n // Calculate horizontal position based on start sample\n // Use Math.floor to always snap to pixel boundaries\n const left = Math.floor(startSample / samplesPerPixel);\n\n // Calculate width as the difference between end and start pixel positions\n // This ensures clips are perfectly adjacent with no gaps\n const endPixel = Math.floor((startSample + durationSamples) / samplesPerPixel);\n const width = endPixel - left;\n\n // Use draggable only if header is shown and drag is enabled\n const enableDrag = showHeader && !disableHeaderDrag && !isOverlay;\n\n // Main clip draggable (for moving entire clip)\n const draggableId = `clip-${trackIndex}-${clipIndex}`;\n const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, isDragging } = useDraggable({\n id: draggableId,\n data: { clipId, trackIndex, clipIndex },\n disabled: !enableDrag,\n });\n\n // Left boundary draggable (for trimming start)\n const leftBoundaryId = `clip-boundary-left-${trackIndex}-${clipIndex}`;\n const {\n attributes: leftBoundaryAttributes,\n listeners: leftBoundaryListeners,\n setActivatorNodeRef: setLeftBoundaryActivatorRef,\n isDragging: isLeftBoundaryDragging,\n } = useDraggable({\n id: leftBoundaryId,\n data: { clipId, trackIndex, clipIndex, boundary: 'left' },\n disabled: !enableDrag,\n });\n\n // Right boundary draggable (for trimming end)\n const rightBoundaryId = `clip-boundary-right-${trackIndex}-${clipIndex}`;\n const {\n attributes: rightBoundaryAttributes,\n listeners: rightBoundaryListeners,\n setActivatorNodeRef: setRightBoundaryActivatorRef,\n isDragging: isRightBoundaryDragging,\n } = useDraggable({\n id: rightBoundaryId,\n data: { clipId, trackIndex, clipIndex, boundary: 'right' },\n disabled: !enableDrag,\n });\n\n // Apply transform for dragging\n const style = transform ? {\n transform: CSS.Translate.toString(transform),\n zIndex: isDragging ? 100 : undefined, // Below controls (z-index: 999) but above other clips\n } : undefined;\n\n return (\n <ClipContainer\n ref={setNodeRef}\n style={style}\n className={className}\n $left={left}\n $width={width}\n $isOverlay={isOverlay}\n data-clip-container=\"true\"\n data-track-id={trackId}\n onMouseDown={onMouseDown}\n >\n {showHeader && (\n <ClipHeader\n clipId={clipId}\n trackIndex={trackIndex}\n clipIndex={clipIndex}\n trackName={trackName}\n isSelected={isSelected}\n disableDrag={disableHeaderDrag}\n dragHandleProps={enableDrag ? { attributes, listeners, setActivatorNodeRef } : undefined}\n />\n )}\n <ChannelsWrapper $isOverlay={isOverlay}>\n {children}\n {/* Fade overlays */}\n {showFades && fadeIn && fadeIn.duration > 0 && (\n <FadeOverlay\n left={0}\n width={Math.floor((fadeIn.duration * sampleRate) / samplesPerPixel)}\n type=\"fadeIn\"\n curveType={fadeIn.type}\n />\n )}\n {showFades && fadeOut && fadeOut.duration > 0 && (\n <FadeOverlay\n left={width - Math.floor((fadeOut.duration * sampleRate) / samplesPerPixel)}\n width={Math.floor((fadeOut.duration * sampleRate) / samplesPerPixel)}\n type=\"fadeOut\"\n curveType={fadeOut.type}\n />\n )}\n </ChannelsWrapper>\n {/* Clip boundaries - outside ChannelsWrapper to avoid overflow:hidden clipping */}\n {showHeader && !disableHeaderDrag && !isOverlay && (\n <>\n <ClipBoundary\n clipId={clipId}\n trackIndex={trackIndex}\n clipIndex={clipIndex}\n edge=\"left\"\n touchOptimized={touchOptimized}\n dragHandleProps={{\n attributes: leftBoundaryAttributes,\n listeners: leftBoundaryListeners,\n setActivatorNodeRef: setLeftBoundaryActivatorRef,\n isDragging: isLeftBoundaryDragging,\n }}\n />\n <ClipBoundary\n clipId={clipId}\n trackIndex={trackIndex}\n clipIndex={clipIndex}\n edge=\"right\"\n touchOptimized={touchOptimized}\n dragHandleProps={{\n attributes: rightBoundaryAttributes,\n listeners: rightBoundaryListeners,\n setActivatorNodeRef: setRightBoundaryActivatorRef,\n isDragging: isRightBoundaryDragging,\n }}\n />\n </>\n )}\n </ClipContainer>\n );\n};\n","import React, { FunctionComponent } from 'react';\nimport styled from 'styled-components';\nimport type { DraggableAttributes } from '@dnd-kit/core';\nimport type { SyntheticListenerMap } from '@dnd-kit/core/dist/hooks/utilities';\n\nexport const CLIP_HEADER_HEIGHT = 22; // Height of the clip header in pixels\n\ninterface HeaderContainerProps {\n readonly $isDragging?: boolean;\n readonly $interactive?: boolean; // Whether it's draggable or just presentational\n readonly $isSelected?: boolean; // Whether the track is selected\n}\n\nconst HeaderContainer = styled.div<HeaderContainerProps>`\n position: relative;\n height: ${CLIP_HEADER_HEIGHT}px;\n background: ${props => props.$isSelected\n ? props.theme.selectedClipHeaderBackgroundColor\n : props.theme.clipHeaderBackgroundColor};\n border-bottom: 1px solid ${props => props.theme.clipHeaderBorderColor};\n display: flex;\n align-items: center;\n padding: 0 8px;\n cursor: ${props => props.$interactive ? (props.$isDragging ? 'grabbing' : 'grab') : 'default'};\n user-select: none;\n z-index: 110;\n flex-shrink: 0;\n pointer-events: auto; /* Re-enable pointer events (parent ClipContainer has pointer-events: none) */\n touch-action: ${props => props.$interactive ? 'none' : 'auto'}; /* Prevent browser scroll during drag on touch devices */\n\n ${props => props.$interactive && `\n &:hover {\n background: ${props.theme.clipHeaderBackgroundColor}dd;\n }\n\n &:active {\n cursor: grabbing;\n }\n `}\n`;\n\nconst TrackName = styled.span`\n font-size: 11px;\n font-weight: 600;\n font-family: ${props => props.theme.clipHeaderFontFamily};\n color: ${props => props.theme.clipHeaderTextColor};\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n`;\n\n// Presentational-only header (no drag behavior)\nexport interface ClipHeaderPresentationalProps {\n trackName: string;\n isSelected?: boolean; // Whether the track is selected\n}\n\nexport const ClipHeaderPresentational: FunctionComponent<ClipHeaderPresentationalProps> = ({\n trackName,\n isSelected = false,\n}) => {\n return (\n <HeaderContainer\n $isDragging={false}\n $interactive={false}\n $isSelected={isSelected}\n >\n <TrackName>{trackName}</TrackName>\n </HeaderContainer>\n );\n};\n\nexport interface DragHandleProps {\n attributes: DraggableAttributes;\n listeners: SyntheticListenerMap | undefined;\n setActivatorNodeRef: (element: HTMLElement | null) => void;\n}\n\nexport interface ClipHeaderProps {\n clipId: string;\n trackIndex: number;\n clipIndex: number;\n trackName: string;\n isSelected?: boolean; // Whether the track is selected\n disableDrag?: boolean; // Disable drag behavior (for presentation-only rendering in overlays)\n dragHandleProps?: DragHandleProps; // Props for drag handle functionality\n}\n\n/**\n * ClipHeader component - Draggable title bar for audio clips\n *\n * Renders at the top of each clip (above all channels).\n * Drag the header to move the clip along the timeline.\n * Shows the track name (not clip-specific info).\n *\n * Theme colors (from useTheme):\n * - clipHeaderBackgroundColor / selectedClipHeaderBackgroundColor\n * - clipHeaderBorderColor\n * - clipHeaderTextColor\n */\nexport const ClipHeader: FunctionComponent<ClipHeaderProps> = ({\n clipId,\n trackIndex,\n clipIndex,\n trackName,\n isSelected = false,\n disableDrag = false,\n dragHandleProps,\n}) => {\n // Use purely presentational version when drag is disabled or no drag handle props\n if (disableDrag || !dragHandleProps) {\n return (\n <ClipHeaderPresentational\n trackName={trackName}\n isSelected={isSelected}\n />\n );\n }\n\n const { attributes, listeners, setActivatorNodeRef } = dragHandleProps;\n\n return (\n <HeaderContainer\n ref={setActivatorNodeRef}\n data-clip-id={clipId}\n $interactive={true}\n $isSelected={isSelected}\n {...listeners}\n {...attributes}\n >\n <TrackName>{trackName}</TrackName>\n </HeaderContainer>\n );\n};\n","import React, { FunctionComponent } from 'react';\nimport styled from 'styled-components';\nimport type { DraggableAttributes } from '@dnd-kit/core';\nimport type { SyntheticListenerMap } from '@dnd-kit/core/dist/hooks/utilities';\nimport type { DragHandleProps as BaseDragHandleProps } from './ClipHeader';\n\nexport const CLIP_BOUNDARY_WIDTH = 8; // Width of the draggable boundary in pixels\nexport const CLIP_BOUNDARY_WIDTH_TOUCH = 24; // Larger touch target for mobile (minimum 44px recommended, but 24px works well for trim handles)\n\ntype BoundaryEdge = 'left' | 'right';\n\ninterface BoundaryContainerProps {\n readonly $edge: BoundaryEdge;\n readonly $isDragging?: boolean;\n readonly $isHovered?: boolean;\n readonly $touchOptimized?: boolean;\n}\n\nconst BoundaryContainer = styled.div<BoundaryContainerProps>`\n position: absolute;\n ${props => props.$edge === 'left' ? 'left: 0;' : 'right: 0;'}\n top: 0;\n bottom: 0;\n width: ${props => props.$touchOptimized ? CLIP_BOUNDARY_WIDTH_TOUCH : CLIP_BOUNDARY_WIDTH}px;\n cursor: col-resize;\n user-select: none;\n z-index: 105; /* Above waveform, below header */\n pointer-events: auto; /* Re-enable pointer events (parent ClipContainer has pointer-events: none) */\n touch-action: none; /* Prevent browser scroll during drag on touch devices */\n\n /* Invisible by default, visible on hover */\n background: ${props =>\n props.$isDragging\n ? 'rgba(255, 255, 255, 0.4)'\n : props.$isHovered\n ? 'rgba(255, 255, 255, 0.2)'\n : 'transparent'\n };\n\n ${props => props.$edge === 'left'\n ? `border-left: 2px solid ${\n props.$isDragging\n ? 'rgba(255, 255, 255, 0.8)'\n : props.$isHovered\n ? 'rgba(255, 255, 255, 0.5)'\n : 'transparent'\n };`\n : `border-right: 2px solid ${\n props.$isDragging\n ? 'rgba(255, 255, 255, 0.8)'\n : props.$isHovered\n ? 'rgba(255, 255, 255, 0.5)'\n : 'transparent'\n };`\n }\n\n transition: background 0.15s ease, border-color 0.15s ease;\n\n &:hover {\n background: rgba(255, 255, 255, 0.2);\n ${props => props.$edge === 'left'\n ? 'border-left: 2px solid rgba(255, 255, 255, 0.5);'\n : 'border-right: 2px solid rgba(255, 255, 255, 0.5);'\n }\n }\n\n &:active {\n background: rgba(255, 255, 255, 0.4);\n ${props => props.$edge === 'left'\n ? 'border-left: 2px solid rgba(255, 255, 255, 0.8);'\n : 'border-right: 2px solid rgba(255, 255, 255, 0.8);'\n }\n }\n`;\n\n// Extend the base DragHandleProps to add isDragging\ninterface DragHandleProps extends BaseDragHandleProps {\n isDragging?: boolean;\n}\n\nexport interface ClipBoundaryProps {\n clipId: string;\n trackIndex: number;\n clipIndex: number;\n edge: BoundaryEdge;\n dragHandleProps?: DragHandleProps;\n /**\n * Enable larger touch targets for mobile devices.\n * When true, boundary width increases from 8px to 24px for easier touch targeting.\n */\n touchOptimized?: boolean;\n}\n\n/**\n * ClipBoundary component - Draggable edge for trimming clips\n *\n * Renders at the left or right edge of a clip.\n * Drag to trim the clip (adjust offset and duration).\n * Supports bidirectional trimming (trim in and out).\n *\n * On mobile (touchOptimized=true), boundaries are wider for easier targeting.\n */\nexport const ClipBoundary: FunctionComponent<ClipBoundaryProps> = ({\n clipId,\n trackIndex,\n clipIndex,\n edge,\n dragHandleProps,\n touchOptimized = false,\n}) => {\n const [isHovered, setIsHovered] = React.useState(false);\n\n if (!dragHandleProps) {\n // No drag handle props provided, render non-interactive boundary\n return null;\n }\n\n const { attributes, listeners, setActivatorNodeRef, isDragging } = dragHandleProps;\n\n return (\n <BoundaryContainer\n ref={setActivatorNodeRef}\n data-clip-id={clipId}\n data-boundary-edge={edge}\n $edge={edge}\n $isDragging={isDragging}\n $isHovered={isHovered}\n $touchOptimized={touchOptimized}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n {...listeners}\n {...attributes}\n />\n );\n};\n","import React, { FunctionComponent } from 'react';\nimport styled, { useTheme } from 'styled-components';\nimport type { FadeType } from '@waveform-playlist/core';\nimport type { WaveformPlaylistTheme } from '../wfpl-theme';\n\ninterface FadeContainerProps {\n readonly $left: number;\n readonly $width: number;\n readonly $type: 'fadeIn' | 'fadeOut';\n}\n\n// Use .attrs() for left/width to avoid generating new CSS classes on every render\nconst FadeContainer = styled.div.attrs<FadeContainerProps>((props) => ({\n style: {\n left: `${props.$left}px`,\n width: `${props.$width}px`,\n },\n}))<FadeContainerProps>`\n position: absolute;\n top: 0;\n bottom: 0;\n pointer-events: none;\n z-index: 50;\n`;\n\ninterface FadeSvgProps {\n readonly $type: 'fadeIn' | 'fadeOut';\n}\n\nconst FadeSvg = styled.svg<FadeSvgProps>`\n width: 100%;\n height: 100%;\n display: block;\n /* Flip horizontally for fadeOut - makes it mirror of fadeIn */\n transform: ${props => props.$type === 'fadeOut' ? 'scaleX(-1)' : 'none'};\n`;\n\nexport interface FadeOverlayProps {\n /** Position in pixels from the start of the clip */\n left: number;\n /** Width of the fade region in pixels */\n width: number;\n /** Type of fade: fadeIn or fadeOut */\n type: 'fadeIn' | 'fadeOut';\n /** Fade curve type */\n curveType?: FadeType;\n /** Custom fill color (defaults to theme.fadeOverlayColor) */\n color?: string;\n}\n\n/**\n * Generates an SVG path for a fade in curve\n * Always generates fadeIn shape - fadeOut is achieved by CSS transform scaleX(-1)\n *\n * The curve shows: more overlay at start (audio quiet), less overlay at end (audio full)\n */\nfunction generateFadePath(\n width: number,\n height: number,\n curveType: FadeType = 'logarithmic'\n): string {\n const points: string[] = [];\n const numPoints = Math.max(20, Math.min(width, 100)); // More points for smoother curves\n\n for (let i = 0; i <= numPoints; i++) {\n const x = (i / numPoints) * width;\n const progress = i / numPoints; // 0 to 1\n\n // Apply curve transformation based on type\n let curvedProgress: number;\n switch (curveType) {\n case 'linear':\n curvedProgress = progress;\n break;\n case 'exponential':\n curvedProgress = progress * progress;\n break;\n case 'sCurve':\n // S-curve using sine\n curvedProgress = (1 - Math.cos(progress * Math.PI)) / 2;\n break;\n case 'logarithmic':\n default:\n // Logarithmic curve (more natural for audio)\n curvedProgress = Math.log10(1 + progress * 9) / Math.log10(10);\n break;\n }\n\n // fadeIn: starts covered (y near 0), ends uncovered (y near height)\n // Y=0 is top of SVG, Y=height is bottom\n // We draw the curve edge, then fill above it\n const y = (1 - curvedProgress) * height;\n points.push(`${x},${y}`);\n }\n\n // Path: start at bottom-left, draw curve, go to top-right, top-left, close\n return `M 0,${height} L ${points.join(' L ')} L ${width},0 L 0,0 Z`;\n}\n\n/**\n * FadeOverlay component - Visual indicator for fade in/out regions on clips\n *\n * Renders a semi-transparent overlay with a curved shape indicating\n * the fade envelope. The shape follows the selected fade curve type.\n */\nexport const FadeOverlay: FunctionComponent<FadeOverlayProps> = ({\n left,\n width,\n type,\n curveType = 'logarithmic',\n color,\n}) => {\n const theme = useTheme() as WaveformPlaylistTheme;\n\n // Don't render if width is too small\n if (width < 1) return null;\n\n // Use color prop, then theme color, then fallback\n const fillColor = color || theme?.fadeOverlayColor || 'rgba(0, 0, 0, 0.4)';\n\n return (\n <FadeContainer $left={left} $width={width} $type={type}>\n <FadeSvg $type={type} viewBox={`0 0 ${width} 100`} preserveAspectRatio=\"none\">\n <path\n d={generateFadePath(width, 100, curveType)}\n fill={fillColor}\n />\n </FadeSvg>\n </FadeContainer>\n );\n};\n","import React from 'react';\nimport styled from 'styled-components';\nimport { BaseSlider, BaseLabel } from '../styled/index';\n\nconst VolumeContainer = styled.div`\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n`;\n\nconst VolumeLabel = styled(BaseLabel)`\n margin: 0;\n white-space: nowrap;\n`;\n\nconst VolumeSlider = styled(BaseSlider)`\n width: 120px;\n`;\n\nexport interface MasterVolumeControlProps {\n volume: number; // 0-1.0 (linear gain, consistent with Web Audio API)\n onChange: (volume: number) => void;\n disabled?: boolean;\n className?: string;\n}\n\n/**\n * Master volume control slider component\n * Accepts volume as 0-1.0 range (linear gain) and displays as percentage\n */\nexport const MasterVolumeControl: React.FC<MasterVolumeControlProps> = ({\n volume,\n onChange,\n disabled = false,\n className,\n}) => {\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n // Convert percentage (0-100) to linear gain (0-1.0)\n onChange(parseFloat(e.target.value) / 100);\n };\n\n return (\n <VolumeContainer className={className}>\n <VolumeLabel htmlFor=\"master-gain\">Master Volume</VolumeLabel>\n <VolumeSlider\n min=\"0\"\n max=\"100\"\n value={volume * 100}\n onChange={handleChange}\n disabled={disabled}\n id=\"master-gain\"\n />\n </VolumeContainer>\n );\n};\n","import React, { useRef, useEffect } from 'react';\nimport styled from 'styled-components';\n\ninterface PlayheadLineProps {\n readonly $position: number;\n readonly $color: string;\n}\n\nconst PlayheadLine = styled.div.attrs<PlayheadLineProps>((props) => ({\n style: {\n transform: `translate3d(${props.$position}px, 0, 0)`,\n },\n}))<PlayheadLineProps>`\n position: absolute;\n top: 0;\n left: 0;\n width: 2px;\n background: ${(props) => props.$color};\n height: 100%;\n z-index: 100; /* Below sticky controls (z-index: 101) so playhead is hidden when scrolled behind controls */\n pointer-events: none;\n will-change: transform;\n`;\n\n/**\n * Props passed to the default playhead component or custom render function.\n */\nexport interface PlayheadProps {\n /** Position in pixels from left edge (only valid when not playing) */\n position: number;\n /** Playhead color (default: #ff0000) */\n color?: string;\n /** Whether audio is currently playing */\n isPlaying: boolean;\n /** Ref to current time in seconds - use for smooth animation during playback */\n currentTimeRef: React.RefObject<number>;\n /** Audio context start time when playback began - for calculating elapsed time */\n playbackStartTimeRef: React.RefObject<number>;\n /** Audio position when playback started - for calculating current position */\n audioStartPositionRef: React.RefObject<number>;\n /** Samples per pixel - for converting time to pixels */\n samplesPerPixel: number;\n /** Sample rate - for converting time to pixels */\n sampleRate: number;\n /** Controls offset in pixels */\n controlsOffset: number;\n /** Function to get current audio context time - required for smooth animation */\n getAudioContextTime?: () => number;\n}\n\n/**\n * Type for custom playhead render functions.\n * Receives position, color, and animation refs for smooth 60fps animation.\n * Custom playheads should use requestAnimationFrame with the refs during playback.\n */\nexport type RenderPlayheadFunction = (props: PlayheadProps) => React.ReactNode;\n\n/**\n * Default playhead component - a simple vertical line.\n * Uses GPU-accelerated transform for smooth animation.\n */\nexport const Playhead: React.FC<PlayheadProps> = ({ position, color = '#ff0000' }) => {\n return <PlayheadLine $position={position} $color={color} />;\n};\n\n// === Custom Playhead Variants ===\n\nconst PlayheadWithMarkerContainer = styled.div<{ $color: string }>`\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n z-index: 100; /* Below sticky controls (z-index: 101) so playhead is hidden when scrolled behind controls */\n pointer-events: none;\n will-change: transform;\n`;\n\nconst MarkerTriangle = styled.div<{ $color: string }>`\n position: absolute;\n top: -10px;\n left: -6px;\n width: 0;\n height: 0;\n border-left: 7px solid transparent;\n border-right: 7px solid transparent;\n border-top: 10px solid ${(props) => props.$color};\n`;\n\nconst MarkerLine = styled.div<{ $color: string }>`\n position: absolute;\n top: 0;\n left: 0;\n width: 2px;\n height: 100%;\n background: ${(props) => props.$color};\n`;\n\n/**\n * Playhead with a triangle marker at the top.\n * Provides better visual indication of the current position.\n * Uses requestAnimationFrame for smooth 60fps animation during playback.\n */\nexport const PlayheadWithMarker: React.FC<PlayheadProps> = ({\n color = '#ff0000',\n isPlaying,\n currentTimeRef,\n playbackStartTimeRef,\n audioStartPositionRef,\n samplesPerPixel,\n sampleRate,\n controlsOffset,\n getAudioContextTime,\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const animationFrameRef = useRef<number | null>(null);\n\n useEffect(() => {\n const updatePosition = () => {\n if (containerRef.current) {\n let time: number;\n if (isPlaying && getAudioContextTime) {\n const elapsed = getAudioContextTime() - (playbackStartTimeRef.current ?? 0);\n time = (audioStartPositionRef.current ?? 0) + elapsed;\n } else {\n time = currentTimeRef.current ?? 0;\n }\n const pos = (time * sampleRate) / samplesPerPixel + controlsOffset;\n containerRef.current.style.transform = `translate3d(${pos}px, 0, 0)`;\n }\n\n if (isPlaying) {\n animationFrameRef.current = requestAnimationFrame(updatePosition);\n }\n };\n\n if (isPlaying) {\n animationFrameRef.current = requestAnimationFrame(updatePosition);\n } else {\n updatePosition();\n }\n\n return () => {\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n animationFrameRef.current = null;\n }\n };\n }, [isPlaying, sampleRate, samplesPerPixel, controlsOffset, currentTimeRef, playbackStartTimeRef, audioStartPositionRef, getAudioContextTime]);\n\n // Update position when stopped (for seeks)\n useEffect(() => {\n if (!isPlaying && containerRef.current) {\n const time = currentTimeRef.current ?? 0;\n const pos = (time * sampleRate) / samplesPerPixel + controlsOffset;\n containerRef.current.style.transform = `translate3d(${pos}px, 0, 0)`;\n }\n });\n\n return (\n <PlayheadWithMarkerContainer ref={containerRef} $color={color}>\n <MarkerTriangle $color={color} />\n <MarkerLine $color={color} />\n </PlayheadWithMarkerContainer>\n );\n};\n","import styled, { DefaultTheme, withTheme } from 'styled-components';\nimport React, { FunctionComponent } from 'react';\n\nconst Wrapper = styled.div`\n overflow-y: hidden;\n overflow-x: auto;\n position: relative;\n`;\n\ninterface ScrollContainerProps {\n readonly $backgroundColor?: string;\n readonly $width?: number;\n}\n\n// Use .attrs() for width to avoid generating new CSS classes on every render\nconst ScrollContainer = styled.div.attrs<ScrollContainerProps>((props) => ({\n style: props.$width !== undefined ? { width: `${props.$width}px` } : {},\n}))<ScrollContainerProps>`\n position: relative;\n background: ${(props) => props.$backgroundColor || 'transparent'};\n`;\n\ninterface TimescaleWrapperProps {\n readonly $width?: number;\n readonly $backgroundColor?: string;\n}\n\n// Use .attrs() for width to avoid generating new CSS classes on every render\nconst TimescaleWrapper = styled.div.attrs<TimescaleWrapperProps>((props) => ({\n style: props.$width ? { minWidth: `${props.$width}px` } : {},\n}))<TimescaleWrapperProps>`\n background: ${(props) => props.$backgroundColor || 'white'};\n width: 100%;\n overflow: visible;\n`;\n\ninterface TracksContainerProps {\n readonly $width?: number;\n readonly $backgroundColor?: string;\n}\n\n// Use .attrs() for width to avoid generating new CSS classes on every render\nconst TracksContainer = styled.div.attrs<TracksContainerProps>((props) => ({\n style: props.$width !== undefined ? { minWidth: `${props.$width}px` } : {},\n}))<TracksContainerProps>`\n position: relative;\n background: ${(props) => props.$backgroundColor || 'transparent'};\n width: 100%;\n`;\n\ninterface ClickOverlayProps {\n readonly $controlsWidth?: number;\n}\n\nconst ClickOverlay = styled.div<ClickOverlayProps>`\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n cursor: crosshair;\n z-index: 1; /* Low z-index - clip headers and boundaries have higher z-index */\n`;\n\nexport interface PlaylistProps {\n readonly theme: DefaultTheme;\n readonly children?: JSX.Element | JSX.Element[];\n readonly backgroundColor?: string;\n readonly timescaleBackgroundColor?: string;\n readonly timescale?: JSX.Element;\n readonly timescaleWidth?: number;\n readonly tracksWidth?: number;\n readonly scrollContainerWidth?: number;\n readonly controlsWidth?: number;\n readonly onTracksClick?: (e: React.MouseEvent<HTMLDivElement>) => void;\n readonly onTracksMouseDown?: (e: React.MouseEvent<HTMLDivElement>) => void;\n readonly onTracksMouseMove?: (e: React.MouseEvent<HTMLDivElement>) => void;\n readonly onTracksMouseUp?: (e: React.MouseEvent<HTMLDivElement>) => void;\n readonly scrollContainerRef?: (el: HTMLDivElement | null) => void;\n}\nexport const Playlist: FunctionComponent<PlaylistProps> = ({\n children,\n backgroundColor,\n timescaleBackgroundColor,\n timescale,\n timescaleWidth,\n tracksWidth,\n scrollContainerWidth,\n controlsWidth,\n onTracksClick,\n onTracksMouseDown,\n onTracksMouseMove,\n onTracksMouseUp,\n scrollContainerRef\n}) => {\n return (\n <Wrapper data-scroll-container=\"true\" ref={scrollContainerRef}>\n <ScrollContainer\n $backgroundColor={backgroundColor}\n $width={scrollContainerWidth}\n >\n {timescale && <TimescaleWrapper $width={timescaleWidth} $backgroundColor={timescaleBackgroundColor}>{timescale}</TimescaleWrapper>}\n <TracksContainer $width={tracksWidth} $backgroundColor={backgroundColor}>\n {children}\n {(onTracksClick || onTracksMouseDown) && (\n <ClickOverlay\n $controlsWidth={controlsWidth}\n onClick={onTracksClick}\n onMouseDown={onTracksMouseDown}\n onMouseMove={onTracksMouseMove}\n onMouseUp={onTracksMouseUp}\n />\n )}\n </TracksContainer>\n </ScrollContainer>\n </Wrapper>\n );\n};\n\nexport const StyledPlaylist = withTheme(Playlist);\n","import React from 'react';\nimport styled from 'styled-components';\n\ninterface SelectionOverlayProps {\n readonly $left: number;\n readonly $width: number;\n readonly $color: string;\n}\n\nconst SelectionOverlay = styled.div.attrs<SelectionOverlayProps>((props) => ({\n style: {\n left: `${props.$left}px`,\n width: `${props.$width}px`,\n },\n}))<SelectionOverlayProps>`\n position: absolute;\n top: 0;\n background: ${(props) => props.$color};\n height: 100%;\n z-index: 60; /* Above clips (z-index: 10) and fades (z-index: 50), below playhead (z-index: 100) */\n pointer-events: none;\n opacity: 0.3;\n`;\n\nexport interface SelectionProps {\n startPosition: number; // Start position in pixels\n endPosition: number; // End position in pixels\n color?: string;\n}\n\nexport const Selection: React.FC<SelectionProps> = ({\n startPosition,\n endPosition,\n color = '#00ff00'\n}) => {\n const width = Math.max(0, endPosition - startPosition);\n\n if (width <= 0) {\n return null;\n }\n\n return <SelectionOverlay $left={startPosition} $width={width} $color={color} data-selection />;\n};\n","import React, { useEffect, useState } from 'react';\nimport { TimeInput } from './TimeInput';\nimport { type TimeFormat } from '../utils/timeFormat';\n\nexport interface SelectionTimeInputsProps {\n selectionStart: number; // Time in seconds\n selectionEnd: number; // Time in seconds\n onSelectionChange?: (start: number, end: number) => void;\n className?: string;\n}\n\nexport const SelectionTimeInputs: React.FC<SelectionTimeInputsProps> = ({\n selectionStart,\n selectionEnd,\n onSelectionChange,\n className,\n}) => {\n const [timeFormat, setTimeFormat] = useState<TimeFormat>('hh:mm:ss.uuu');\n\n // Listen to the external time-format dropdown\n useEffect(() => {\n const timeFormatSelect = document.querySelector('.time-format') as HTMLSelectElement;\n\n const handleFormatChange = () => {\n if (timeFormatSelect) {\n setTimeFormat(timeFormatSelect.value as TimeFormat);\n }\n };\n\n // Set initial value\n if (timeFormatSelect) {\n setTimeFormat(timeFormatSelect.value as TimeFormat);\n timeFormatSelect.addEventListener('change', handleFormatChange);\n }\n\n return () => {\n timeFormatSelect?.removeEventListener('change', handleFormatChange);\n };\n }, []);\n\n const handleStartChange = (value: number) => {\n if (onSelectionChange) {\n onSelectionChange(value, selectionEnd);\n }\n };\n\n const handleEndChange = (value: number) => {\n if (onSelectionChange) {\n onSelectionChange(selectionStart, value);\n }\n };\n\n return (\n <>\n <TimeInput\n id=\"audio_start\"\n label=\"Start of audio selection\"\n value={selectionStart}\n format={timeFormat}\n className=\"audio-start form-control mr-sm-2\"\n onChange={handleStartChange}\n />\n <TimeInput\n id=\"audio_end\"\n label=\"End of audio selection\"\n value={selectionEnd}\n format={timeFormat}\n className=\"audio-end form-control mr-sm-2\"\n onChange={handleEndChange}\n />\n </>\n );\n};\n","import React, { useEffect, useState } from 'react';\nimport { formatTime, parseTime, type TimeFormat } from '../utils/timeFormat';\nimport { BaseInput, ScreenReaderOnly } from '../styled/index';\n\nexport interface TimeInputProps {\n id: string;\n label: string;\n value: number; // Time in seconds\n format: TimeFormat;\n className?: string;\n onChange?: (value: number) => void;\n readOnly?: boolean;\n}\n\n/**\n * TimeInput - A styled input for time values with format support\n *\n * Uses BaseInput for consistent theming. Displays time in the specified\n * format and parses user input on blur.\n */\nexport const TimeInput: React.FC<TimeInputProps> = ({\n id,\n label,\n value,\n format,\n className,\n onChange,\n readOnly = false,\n}) => {\n const [displayValue, setDisplayValue] = useState('');\n\n // Update display value when value or format changes\n useEffect(() => {\n const formatted = formatTime(value, format);\n setDisplayValue(formatted);\n }, [value, format, id]);\n\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const newDisplayValue = e.target.value;\n setDisplayValue(newDisplayValue);\n };\n\n const handleBlur = () => {\n // Parse the display value and notify parent\n if (onChange) {\n const parsedValue = parseTime(displayValue, format);\n onChange(parsedValue);\n }\n // Re-format to ensure consistent display\n setDisplayValue(formatTime(value, format));\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter') {\n e.currentTarget.blur();\n }\n };\n\n return (\n <>\n <ScreenReaderOnly as=\"label\" htmlFor={id}>\n {label}\n </ScreenReaderOnly>\n <BaseInput\n type=\"text\"\n className={className}\n id={id}\n value={displayValue}\n onChange={handleChange}\n onBlur={handleBlur}\n onKeyDown={handleKeyDown}\n readOnly={readOnly}\n />\n </>\n );\n};\n","/**\n * Time format utilities for displaying and parsing audio timestamps\n */\n\nexport type TimeFormat = 'seconds' | 'thousandths' | 'hh:mm:ss' | 'hh:mm:ss.u' | 'hh:mm:ss.uu' | 'hh:mm:ss.uuu';\n\n/**\n * Format time in clock format (hh:mm:ss with optional decimals)\n */\nfunction clockFormat(seconds: number, decimals: number): string {\n const hours = Math.floor(seconds / 3600) % 24;\n const minutes = Math.floor(seconds / 60) % 60;\n const secs = (seconds % 60).toFixed(decimals);\n\n return (\n String(hours).padStart(2, '0') +\n ':' +\n String(minutes).padStart(2, '0') +\n ':' +\n secs.padStart(decimals + 3, '0')\n );\n}\n\n/**\n * Format seconds according to the specified format\n */\nexport function formatTime(seconds: number, format: TimeFormat): string {\n switch (format) {\n case 'seconds':\n return seconds.toFixed(0);\n case 'thousandths':\n return seconds.toFixed(3);\n case 'hh:mm:ss':\n return clockFormat(seconds, 0);\n case 'hh:mm:ss.u':\n return clockFormat(seconds, 1);\n case 'hh:mm:ss.uu':\n return clockFormat(seconds, 2);\n case 'hh:mm:ss.uuu':\n return clockFormat(seconds, 3);\n default:\n return clockFormat(seconds, 3);\n }\n}\n\n/**\n * Parse a formatted time string back to seconds\n */\nexport function parseTime(timeStr: string, format: TimeFormat): number {\n if (!timeStr) return 0;\n\n switch (format) {\n case 'seconds':\n case 'thousandths':\n return parseFloat(timeStr) || 0;\n\n case 'hh:mm:ss':\n case 'hh:mm:ss.u':\n case 'hh:mm:ss.uu':\n case 'hh:mm:ss.uuu': {\n // Parse hh:mm:ss format\n const parts = timeStr.split(':');\n if (parts.length !== 3) return 0;\n\n const hours = parseInt(parts[0], 10) || 0;\n const minutes = parseInt(parts[1], 10) || 0;\n const seconds = parseFloat(parts[2]) || 0;\n\n return hours * 3600 + minutes * 60 + seconds;\n }\n\n default:\n return 0;\n }\n}\n","import React, { useState, createContext, useContext, ReactNode } from 'react';\n\nfunction getScale() {\n return window.devicePixelRatio;\n}\n\nconst DevicePixelRatioContext = createContext(getScale());\n\ntype Props = {\n children: ReactNode;\n};\nexport const DevicePixelRatioProvider = ({ children }: Props) => {\n const [scale, setScale] = useState(getScale());\n\n matchMedia(`(resolution: ${getScale()}dppx)`).addEventListener(\n 'change',\n () => {\n setScale(getScale());\n },\n { once: true }\n );\n\n return (\n <DevicePixelRatioContext.Provider value={Math.ceil(scale)}>\n {children}\n </DevicePixelRatioContext.Provider>\n );\n};\n\nexport const useDevicePixelRatio = () => useContext(DevicePixelRatioContext);\n","import { createContext, useContext } from 'react';\n\ntype Controls = {\n show: boolean;\n width: number;\n};\n\ntype PlaylistInfo = {\n sampleRate: number;\n samplesPerPixel: number;\n zoomLevels: Array<number>;\n waveHeight: number;\n timeScaleHeight: number;\n duration: number;\n controls: Controls;\n /** Width in pixels of waveform bars. Default: 1 */\n barWidth: number;\n /** Spacing in pixels between waveform bars. Default: 0 */\n barGap: number;\n /** Width in pixels of progress bars. Default: barWidth + barGap (fills gaps). Set to barWidth for no gap fill. */\n progressBarWidth?: number;\n};\n\nexport const PlaylistInfoContext = createContext<PlaylistInfo>({\n sampleRate: 48000,\n samplesPerPixel: 1000,\n zoomLevels: [1000, 1500, 2000, 2500],\n waveHeight: 80,\n timeScaleHeight: 15,\n controls: {\n show: false,\n width: 150,\n },\n duration: 30000,\n barWidth: 1,\n barGap: 0,\n});\n\nexport const usePlaylistInfo = () => useContext(PlaylistInfoContext);\n","import { useContext } from 'react';\nimport { ThemeContext } from 'styled-components';\n\nexport const useTheme = () => useContext(ThemeContext);\n","import React, { createContext, useContext, Fragment } from 'react';\n\nexport const TrackControlsContext = createContext<React.ReactNode>(<Fragment />);\n\nexport const useTrackControls = () => useContext(TrackControlsContext);\n","import React, {\n useState,\n createContext,\n useContext,\n ReactNode,\n Dispatch,\n SetStateAction,\n} from 'react';\n\nconst defaultProgress = 0;\nconst defaultIsPlaying = false;\nconst defaultSelectionStart = 0;\nconst defaultSelectionEnd = 0;\n\nconst defaultPlayout = {\n progress: defaultProgress,\n isPlaying: defaultIsPlaying,\n selectionStart: defaultSelectionStart,\n selectionEnd: defaultSelectionEnd,\n};\n\nconst PlayoutStatusContext = createContext(defaultPlayout);\n\ntype PlayoutStatusUpdate = {\n setIsPlaying: Dispatch<SetStateAction<boolean>>;\n setProgress: Dispatch<SetStateAction<number>>;\n setSelection: (start: number, end: number) => void;\n};\nconst PlayoutStatusUpdateContext = createContext<PlayoutStatusUpdate>({\n setIsPlaying: () => {},\n setProgress: () => {},\n setSelection: () => {},\n});\n\ntype Props = {\n children: ReactNode;\n};\nexport const PlayoutProvider = ({ children }: Props) => {\n const [isPlaying, setIsPlaying] = useState(defaultIsPlaying);\n const [progress, setProgress] = useState(defaultProgress);\n const [selectionStart, setSelectionStart] = useState(defaultSelectionStart);\n const [selectionEnd, setSelectionEnd] = useState(defaultSelectionEnd);\n\n const setSelection = (start: number, end: number) => {\n setSelectionStart(start);\n setSelectionEnd(end);\n };\n\n return (\n <PlayoutStatusUpdateContext.Provider value={{ setIsPlaying, setProgress, setSelection }}>\n <PlayoutStatusContext.Provider value={{ isPlaying, progress, selectionStart, selectionEnd }}>\n {children}\n </PlayoutStatusContext.Provider>\n </PlayoutStatusUpdateContext.Provider>\n );\n};\n\nexport const usePlayoutStatus = () => useContext(PlayoutStatusContext);\nexport const usePlayoutStatusUpdate = () =>\n useContext(PlayoutStatusUpdateContext);\n","import React, { FunctionComponent } from 'react';\nimport { useDevicePixelRatio, usePlaylistInfo, useTheme } from '../contexts';\nimport { Channel } from './Channel';\n\nexport interface SmartChannelProps {\n className?: string;\n index: number;\n data: Int8Array | Int16Array;\n bits: 8 | 16;\n length: number;\n isSelected?: boolean; // Whether this channel's track is selected\n /** If true, background is transparent (for use with external progress overlay) */\n transparentBackground?: boolean;\n}\n\nexport const SmartChannel: FunctionComponent<SmartChannelProps> = ({ isSelected, transparentBackground, ...props }) => {\n const theme = useTheme();\n const { waveHeight, barWidth, barGap } = usePlaylistInfo();\n const devicePixelRatio = useDevicePixelRatio();\n\n // Use selected colors if track is selected\n const waveOutlineColor = isSelected && theme\n ? theme.selectedWaveOutlineColor\n : theme?.waveOutlineColor;\n\n const waveFillColor = isSelected && theme\n ? theme.selectedWaveFillColor\n : theme?.waveFillColor;\n\n // Get draw mode from theme (defaults to 'inverted' for backwards compatibility)\n const drawMode = theme?.waveformDrawMode || 'inverted';\n\n return (\n <Channel\n {...props}\n {...theme}\n waveOutlineColor={waveOutlineColor}\n waveFillColor={waveFillColor}\n waveHeight={waveHeight}\n devicePixelRatio={devicePixelRatio}\n barWidth={barWidth}\n barGap={barGap}\n transparentBackground={transparentBackground}\n drawMode={drawMode}\n ></Channel>\n );\n};\n","import React, { FunctionComponent, useContext } from 'react';\nimport { PlaylistInfoContext } from '../contexts/PlaylistInfo';\nimport { StyledTimeScale } from './TimeScale';\n\nconst timeinfo = new Map([\n [\n 700,\n {\n marker: 1000,\n bigStep: 500,\n smallStep: 100,\n },\n ],\n [\n 1500,\n {\n marker: 2000,\n bigStep: 1000,\n smallStep: 200,\n },\n ],\n [\n 2500,\n {\n marker: 2000,\n bigStep: 1000,\n smallStep: 500,\n },\n ],\n [\n 5000,\n {\n marker: 5000,\n bigStep: 1000,\n smallStep: 500,\n },\n ],\n [\n 10000,\n {\n marker: 10000,\n bigStep: 5000,\n smallStep: 1000,\n },\n ],\n [\n 12000,\n {\n marker: 15000,\n bigStep: 5000,\n smallStep: 1000,\n },\n ],\n [\n Infinity,\n {\n marker: 30000,\n bigStep: 10000,\n smallStep: 5000,\n },\n ],\n]);\n\nfunction getScaleInfo(samplesPerPixel: number) {\n const keys = timeinfo.keys();\n let config;\n\n for (const resolution of keys) {\n if (samplesPerPixel < resolution) {\n config = timeinfo.get(resolution);\n break;\n }\n }\n\n if (config === undefined) {\n config = { marker: 30000, bigStep: 10000, smallStep: 5000 };\n }\n return config;\n}\n\nexport const SmartScale: FunctionComponent = () => {\n const { samplesPerPixel, duration } = useContext(PlaylistInfoContext);\n let config = getScaleInfo(samplesPerPixel);\n\n return (\n <StyledTimeScale\n marker={config.marker}\n bigStep={config.bigStep}\n secondStep={config.smallStep}\n duration={duration}\n />\n );\n};\n","import React, { FunctionComponent, useRef, useEffect, useContext } from 'react';\nimport styled, { withTheme, DefaultTheme } from 'styled-components';\nimport { PlaylistInfoContext } from '../contexts/PlaylistInfo';\nimport { useDevicePixelRatio } from '../contexts/DevicePixelRatio';\nimport { secondsToPixels } from '../utils/conversions';\n\nfunction formatTime(milliseconds: number) {\n const seconds = Math.floor(milliseconds / 1000);\n const s = seconds % 60;\n const m = (seconds - s) / 60;\n\n return `${m}:${String(s).padStart(2, '0')}`;\n}\n\ninterface PlaylistTimeScaleScroll {\n readonly $cssWidth: number;\n readonly $controlWidth: number;\n readonly $timeScaleHeight: number;\n}\nconst PlaylistTimeScaleScroll = styled.div.attrs<PlaylistTimeScaleScroll>((props) => ({\n style: {\n width: `${props.$cssWidth}px`,\n marginLeft: `${props.$controlWidth}px`,\n height: `${props.$timeScaleHeight}px`,\n },\n}))<PlaylistTimeScaleScroll>`\n position: relative;\n overflow: visible; /* Allow time labels to render above the container */\n border-bottom: 1px solid ${props => props.theme.timeColor};\n box-sizing: border-box;\n`;\n\ninterface TimeTicks {\n readonly $cssWidth: number;\n readonly $timeScaleHeight: number;\n}\nconst TimeTicks = styled.canvas.attrs<TimeTicks>((props) => ({\n style: {\n width: `${props.$cssWidth}px`,\n height: `${props.$timeScaleHeight}px`,\n },\n}))<TimeTicks>`\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n`;\n\ninterface TimeStamp {\n readonly $left: number;\n}\nconst TimeStamp = styled.div.attrs<TimeStamp>((props) => ({\n style: {\n left: `${props.$left + 4}px`, // Offset 4px to the right of the tick\n },\n}))<TimeStamp>`\n position: absolute;\n font-size: 0.75rem; /* Smaller font to prevent overflow */\n white-space: nowrap; /* Prevent text wrapping */\n color: ${props => props.theme.timeColor}; /* Use theme color instead of inheriting */\n`;\n\nexport interface TimeScaleProps {\n readonly theme?: DefaultTheme;\n readonly duration: number;\n readonly marker: number;\n readonly bigStep: number;\n readonly secondStep: number;\n readonly renderTimestamp?: (timeMs: number, pixelPosition: number) => React.ReactNode;\n}\n\ninterface TimeScalePropsWithTheme extends TimeScaleProps {\n readonly theme: DefaultTheme;\n}\n\nexport const TimeScale: FunctionComponent<TimeScalePropsWithTheme> = (props) => {\n const {\n theme: { timeColor },\n duration,\n marker,\n bigStep,\n secondStep,\n renderTimestamp,\n } = props;\n const canvasInfo = new Map();\n const timeMarkers = [];\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const {\n sampleRate,\n samplesPerPixel,\n timeScaleHeight,\n controls: { show: showControls, width: controlWidth },\n } = useContext(PlaylistInfoContext);\n const devicePixelRatio = useDevicePixelRatio();\n\n useEffect(() => {\n if (canvasRef.current !== null) {\n const canvas = canvasRef.current;\n const ctx = canvas.getContext('2d');\n\n if (ctx) {\n ctx.resetTransform();\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.imageSmoothingEnabled = false;\n ctx.fillStyle = timeColor;\n ctx.scale(devicePixelRatio, devicePixelRatio);\n\n for (const [pixLeft, scaleHeight] of canvasInfo.entries()) {\n const scaleY = timeScaleHeight - scaleHeight;\n ctx.fillRect(pixLeft, scaleY, 1, scaleHeight);\n }\n }\n }\n }, [\n duration,\n devicePixelRatio,\n timeColor,\n timeScaleHeight,\n bigStep,\n secondStep,\n marker,\n canvasInfo,\n ]);\n\n const widthX = secondsToPixels(duration / 1000, samplesPerPixel, sampleRate);\n const pixPerSec = sampleRate / samplesPerPixel;\n let counter = 0;\n\n for (let i = 0; i < widthX; i += (pixPerSec * secondStep) / 1000) {\n const pix = Math.floor(i);\n\n // create three levels of time markers - at marker point also place a timestamp.\n if (counter % marker === 0) {\n const timeMs = counter;\n const timestamp = formatTime(timeMs);\n\n // Use custom renderer if provided, otherwise use default\n const timestampContent = renderTimestamp ? (\n <React.Fragment key={`timestamp-${counter}`}>\n {renderTimestamp(timeMs, pix)}\n </React.Fragment>\n ) : (\n <TimeStamp key={timestamp} $left={pix}>\n {timestamp}\n </TimeStamp>\n );\n\n timeMarkers.push(timestampContent);\n canvasInfo.set(pix, timeScaleHeight);\n } else if (counter % bigStep === 0) {\n canvasInfo.set(pix, Math.floor(timeScaleHeight / 2));\n } else if (counter % secondStep === 0) {\n canvasInfo.set(pix, Math.floor(timeScaleHeight / 5));\n }\n\n counter += secondStep;\n }\n\n return (\n <PlaylistTimeScaleScroll\n $cssWidth={widthX}\n $controlWidth={showControls ? controlWidth : 0}\n $timeScaleHeight={timeScaleHeight}\n >\n {timeMarkers}\n <TimeTicks\n $cssWidth={widthX}\n $timeScaleHeight={timeScaleHeight}\n width={widthX * devicePixelRatio}\n height={timeScaleHeight * devicePixelRatio}\n ref={canvasRef}\n />\n </PlaylistTimeScaleScroll>\n );\n};\n\nexport const StyledTimeScale = withTheme(TimeScale) as FunctionComponent<TimeScaleProps>;\n","export function samplesToSeconds(samples: number, sampleRate: number) {\n return samples / sampleRate;\n}\n\nexport function secondsToSamples(seconds: number, sampleRate: number) {\n return Math.ceil(seconds * sampleRate);\n}\n\nexport function samplesToPixels(samples: number, samplesPerPixel: number) {\n return Math.floor(samples / samplesPerPixel);\n}\n\nexport function pixelsToSamples(pixels: number, samplesPerPixel: number) {\n return Math.floor(pixels * samplesPerPixel);\n}\n\nexport function pixelsToSeconds(\n pixels: number,\n samplesPerPixel: number,\n sampleRate: number\n) {\n return (pixels * samplesPerPixel) / sampleRate;\n}\n\nexport function secondsToPixels(\n seconds: number,\n samplesPerPixel: number,\n sampleRate: number\n) {\n return Math.ceil((seconds * sampleRate) / samplesPerPixel);\n}\n","import React from 'react';\nimport styled from 'styled-components';\nimport { type TimeFormat } from '../utils/timeFormat';\nimport { BaseSelect } from '../styled/index';\n\nconst SelectWrapper = styled.div`\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n`;\n\nexport interface TimeFormatSelectProps {\n value: TimeFormat;\n onChange: (format: TimeFormat) => void;\n disabled?: boolean;\n className?: string;\n}\n\nconst TIME_FORMAT_OPTIONS: { value: TimeFormat; label: string }[] = [\n { value: 'seconds', label: 'seconds' },\n { value: 'thousandths', label: 'thousandths' },\n { value: 'hh:mm:ss', label: 'hh:mm:ss' },\n { value: 'hh:mm:ss.u', label: 'hh:mm:ss + tenths' },\n { value: 'hh:mm:ss.uu', label: 'hh:mm:ss + hundredths' },\n { value: 'hh:mm:ss.uuu', label: 'hh:mm:ss + milliseconds' },\n];\n\n/**\n * Dropdown select for choosing time display format\n */\nexport const TimeFormatSelect: React.FC<TimeFormatSelectProps> = ({\n value,\n onChange,\n disabled = false,\n className,\n}) => {\n const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => {\n onChange(e.target.value as TimeFormat);\n };\n\n return (\n <SelectWrapper className={className}>\n <BaseSelect\n className=\"time-format\"\n value={value}\n onChange={handleChange}\n disabled={disabled}\n aria-label=\"Time format selection\"\n >\n {TIME_FORMAT_OPTIONS.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </BaseSelect>\n </SelectWrapper>\n );\n};\n","import React, { FunctionComponent, ReactNode } from 'react';\nimport styled from 'styled-components';\nimport { usePlaylistInfo } from '../contexts/PlaylistInfo';\nimport { useTrackControls } from '../contexts/TrackControls';\nimport { CLIP_HEADER_HEIGHT } from './ClipHeader';\n\ninterface ContainerProps {\n readonly $numChannels: number;\n readonly $waveHeight: number;\n readonly $controlWidth: number;\n readonly $width?: number;\n readonly $isSelected?: boolean;\n}\n\ninterface ContainerWithHeaderProps extends ContainerProps {\n readonly $hasClipHeaders: boolean;\n}\n\nconst Container = styled.div.attrs<ContainerWithHeaderProps>((props) => ({\n style: {\n height: `${props.$waveHeight * props.$numChannels + (props.$hasClipHeaders ? CLIP_HEADER_HEIGHT : 0)}px`,\n },\n}))<ContainerWithHeaderProps>`\n position: relative;\n display: flex;\n ${(props) => props.$width !== undefined && `width: ${props.$width}px;`}\n`;\n\ninterface ChannelContainerProps {\n readonly $controlWidth: number;\n readonly $backgroundColor?: string;\n readonly $offset?: number;\n}\nconst ChannelContainer = styled.div.attrs<ChannelContainerProps>((props) => ({\n style: {\n paddingLeft: `${props.$offset || 0}px`,\n },\n}))<ChannelContainerProps>`\n position: relative;\n background: ${(props) => props.$backgroundColor || 'transparent'};\n flex: 1;\n`;\n\nexport interface ControlsWrapperProps {\n readonly $controlWidth: number;\n readonly $isSelected?: boolean;\n}\nconst ControlsWrapper = styled.div.attrs<ControlsWrapperProps>((props) => ({\n style: {\n width: `${props.$controlWidth}px`,\n },\n}))<ControlsWrapperProps>`\n position: sticky;\n z-index: 101; /* Above waveform content, below Docusaurus navbar (z-index: 200) */\n left: 0;\n height: 100%;\n flex-shrink: 0;\n pointer-events: auto;\n background: ${(props) => props.theme.surfaceColor};\n transition: background 0.15s ease-in-out;\n\n /* Selected track: highlighted background */\n ${(props) => props.$isSelected && `\n background: ${props.theme.selectedTrackControlsBackground};\n `}\n`;\n\nexport interface TrackProps {\n className?: string;\n children?: ReactNode;\n numChannels: number;\n backgroundColor?: string;\n offset?: number; // Offset in pixels to shift the waveform right\n width?: number; // Total width of the track (for consistent backgrounds across tracks)\n hasClipHeaders?: boolean; // Whether clips have headers (for multi-clip editing)\n onClick?: () => void; // Called when track is clicked (for track selection)\n trackId?: string; // Track ID for identifying which track was clicked\n isSelected?: boolean; // Whether this track is currently selected (for visual feedback)\n}\n\nexport const Track: FunctionComponent<TrackProps> = ({\n numChannels,\n children,\n className,\n backgroundColor,\n offset = 0,\n width,\n hasClipHeaders = false,\n onClick,\n trackId,\n isSelected = false,\n}) => {\n const {\n waveHeight,\n controls: { show, width: controlWidth },\n } = usePlaylistInfo();\n const controls = useTrackControls();\n return (\n <Container\n $numChannels={numChannels}\n className={className}\n $waveHeight={waveHeight}\n $controlWidth={show ? controlWidth : 0}\n $width={width}\n $hasClipHeaders={hasClipHeaders}\n $isSelected={isSelected}\n >\n <ControlsWrapper\n $controlWidth={show ? controlWidth : 0}\n $isSelected={isSelected}\n >\n {controls}\n </ControlsWrapper>\n <ChannelContainer\n $controlWidth={show ? controlWidth : 0}\n $backgroundColor={backgroundColor}\n $offset={offset}\n onClick={onClick}\n data-track-id={trackId}\n >\n {children}\n </ChannelContainer>\n </Container>\n );\n};\n","import styled from 'styled-components';\n\n/**\n * TrackControls Button - Small button for track controls (Mute, Solo, etc.)\n *\n * Supports variants: outline (default), danger, info\n * Uses theme values for consistent styling.\n */\nexport const Button = styled.button.attrs({\n type: 'button',\n})<{ $variant?: 'outline' | 'danger' | 'info' }>`\n display: inline-block;\n font-family: ${(props) => props.theme.fontFamily};\n font-weight: 500;\n text-align: center;\n vertical-align: middle;\n user-select: none;\n padding: 0.25rem 0.4rem;\n font-size: ${(props) => props.theme.fontSizeSmall};\n line-height: 1;\n border-radius: ${(props) => props.theme.borderRadius};\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out,\n border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n cursor: pointer;\n\n ${(props) => {\n if (props.$variant === 'danger') {\n return `\n color: #fff;\n background-color: #dc3545;\n border: 1px solid #dc3545;\n\n &:hover {\n background-color: #c82333;\n border-color: #bd2130;\n }\n\n &:focus {\n outline: none;\n box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5);\n }\n `;\n } else if (props.$variant === 'info') {\n return `\n color: #fff;\n background-color: #17a2b8;\n border: 1px solid #17a2b8;\n\n &:hover {\n background-color: #138496;\n border-color: #117a8b;\n }\n\n &:focus {\n outline: none;\n box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5);\n }\n `;\n } else {\n // outline variant (default) - uses theme colors\n return `\n color: ${props.theme.textColor};\n background-color: transparent;\n border: 1px solid ${props.theme.borderColor};\n\n &:hover {\n color: #fff;\n background-color: ${props.theme.textColor};\n border-color: ${props.theme.textColor};\n }\n\n &:focus {\n outline: none;\n box-shadow: 0 0 0 0.2rem ${props.theme.inputFocusBorder}33;\n }\n `;\n }\n }}\n`;\n","import styled from 'styled-components';\n\nexport const ButtonGroup = styled.div`\n margin-bottom: 0.3rem;\n\n button:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n }\n\n button:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n }\n`;\n","import styled from 'styled-components';\n\nexport const Controls = styled.div`\n background: transparent;\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: flex-start;\n overflow: hidden;\n box-sizing: border-box;\n text-align: center;\n border: 1px solid ${(props) => props.theme.borderColor};\n border-radius: ${(props) => props.theme.borderRadius};\n`;\n","import styled from 'styled-components';\n\nexport const Header = styled.header`\n overflow: hidden;\n height: 26px;\n width: 100%;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0 0.2rem;\n font-size: ${(props) => props.theme.fontSizeSmall};\n color: ${(props) => props.theme.textColor};\n background-color: transparent;\n`;\n","import React from 'react';\nimport { SpeakerLowIcon, type IconProps } from '@phosphor-icons/react';\n\nexport const VolumeDownIcon: React.FC<IconProps> = (props) => (\n <SpeakerLowIcon weight=\"light\" {...props} />\n);\n","import React from 'react';\nimport { SpeakerHighIcon, type IconProps } from '@phosphor-icons/react';\n\nexport const VolumeUpIcon: React.FC<IconProps> = (props) => (\n <SpeakerHighIcon weight=\"light\" {...props} />\n);\n","import React from 'react';\nimport { TrashIcon as PhosphorTrashIcon, type IconProps } from '@phosphor-icons/react';\n\nexport const TrashIcon: React.FC<IconProps> = (props) => (\n <PhosphorTrashIcon weight=\"light\" {...props} />\n);\n","import styled from 'styled-components';\nimport { BaseSlider } from '../../styled/index';\n\n/**\n * TrackControls Slider - Compact slider for volume/pan controls\n *\n * Extends BaseSlider with track-specific styling:\n * - Smaller thumb and track for compact layout\n * - Uses theme's sliderThumbColor (goldenrod by default)\n */\nexport const Slider = styled(BaseSlider)`\n width: 75%;\n height: 5px;\n background: ${(props) => props.theme.sliderTrackColor};\n\n &::-webkit-slider-thumb {\n width: 12px;\n height: 12px;\n background: ${(props) => props.theme.sliderThumbColor};\n border: none;\n margin-top: -4px;\n cursor: ew-resize;\n }\n\n &::-moz-range-thumb {\n width: 12px;\n height: 12px;\n background: ${(props) => props.theme.sliderThumbColor};\n border: none;\n cursor: ew-resize;\n }\n\n &::-webkit-slider-runnable-track {\n height: 5px;\n background: ${(props) => props.theme.sliderTrackColor};\n border-radius: 3px;\n }\n\n &::-moz-range-track {\n height: 5px;\n background: ${(props) => props.theme.sliderTrackColor};\n border-radius: 3px;\n }\n\n &:focus::-webkit-slider-runnable-track {\n background: ${(props) => props.theme.inputBorder};\n }\n\n &:focus::-moz-range-track {\n background: ${(props) => props.theme.inputBorder};\n }\n\n &:focus::-webkit-slider-thumb {\n border: 2px solid ${(props) => props.theme.textColor};\n }\n\n &:focus::-moz-range-thumb {\n border: 2px solid ${(props) => props.theme.textColor};\n }\n`;\n","import styled from 'styled-components';\n\nexport const SliderWrapper = styled.label`\n width: 100%;\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0 1rem;\n margin-bottom: 0.2rem;\n font-size: 14px;\n`;\n","/**\n * Track Controls with Delete Button\n *\n * Reusable track controls component that includes standard controls\n * (mute, solo, volume, pan) plus a delete button\n */\n\nimport React from 'react';\nimport styled from 'styled-components';\nimport {\n Controls,\n SliderWrapper,\n Slider,\n VolumeDownIcon,\n VolumeUpIcon,\n Button,\n ButtonGroup,\n TrashIcon,\n} from './TrackControls';\n\nexport interface TrackControlsWithDeleteProps {\n trackIndex: number;\n trackName: string;\n muted: boolean;\n soloed: boolean;\n volume: number;\n pan: number;\n onMuteChange: (muted: boolean) => void;\n onSoloChange: (soloed: boolean) => void;\n onVolumeChange: (volume: number) => void;\n onPanChange: (pan: number) => void;\n onDelete: () => void;\n}\n\nconst HeaderContainer = styled.div`\n display: flex;\n align-items: center;\n gap: 0.25rem;\n padding: 0.5rem 0.5rem 0.25rem 0.5rem;\n`;\n\nconst TrackNameSpan = styled.span`\n flex: 1;\n font-weight: 600;\n font-size: 0.875rem;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n margin: 0 0.25rem;\n`;\n\nconst DeleteIconButton = styled.button`\n display: flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n padding: 0;\n border: none;\n background: transparent;\n color: #999;\n cursor: pointer;\n font-size: 16px;\n line-height: 1;\n border-radius: 3px;\n transition: all 0.2s ease-in-out;\n flex-shrink: 0;\n\n &:hover {\n background: #dc3545;\n color: white;\n }\n\n &:active {\n transform: scale(0.9);\n }\n`;\n\n/**\n * Track controls with delete button\n *\n * @example\n * ```tsx\n * <TrackControlsWithDelete\n * trackIndex={0}\n * trackName=\"Track 1\"\n * muted={false}\n * soloed={false}\n * volume={1.0}\n * pan={0}\n * onMuteChange={(muted) => console.log('mute:', muted)}\n * onSoloChange={(soloed) => console.log('solo:', soloed)}\n * onVolumeChange={(volume) => console.log('volume:', volume)}\n * onPanChange={(pan) => console.log('pan:', pan)}\n * onDelete={() => console.log('delete')}\n * />\n * ```\n */\nexport const TrackControlsWithDelete: React.FC<TrackControlsWithDeleteProps> = ({\n trackName,\n muted,\n soloed,\n volume,\n pan,\n onMuteChange,\n onSoloChange,\n onVolumeChange,\n onPanChange,\n onDelete,\n}) => {\n return (\n <Controls>\n <HeaderContainer>\n <DeleteIconButton onClick={onDelete} title=\"Delete track\">\n <TrashIcon />\n </DeleteIconButton>\n <TrackNameSpan>{trackName}</TrackNameSpan>\n </HeaderContainer>\n <ButtonGroup>\n <Button\n $variant={muted ? 'danger' : 'outline'}\n onClick={() => onMuteChange(!muted)}\n >\n Mute\n </Button>\n <Button\n $variant={soloed ? 'info' : 'outline'}\n onClick={() => onSoloChange(!soloed)}\n >\n Solo\n </Button>\n </ButtonGroup>\n <SliderWrapper>\n <VolumeDownIcon />\n <Slider\n min=\"0\"\n max=\"1\"\n step=\"0.01\"\n value={volume}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => onVolumeChange(parseFloat(e.target.value))}\n />\n <VolumeUpIcon />\n </SliderWrapper>\n <SliderWrapper>\n <span>L</span>\n <Slider\n min=\"-1\"\n max=\"1\"\n step=\"0.01\"\n value={pan}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => onPanChange(parseFloat(e.target.value))}\n />\n <span>R</span>\n </SliderWrapper>\n </Controls>\n );\n};\n"],"mappings":"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","names":["useTheme","styled","import_styled_components","styled","import_styled_components","styled","import_styled_components","styled","import_styled_components","styled","import_styled_components","styled","import_styled_components","styled","import_styled_components","styled","import_jsx_runtime","import_styled_components","import_jsx_runtime","styled","index","import_styled_components","import_styled_components","import_jsx_runtime","styled","import_react","import_styled_components","import_jsx_runtime","styled","React","import_styled_components","import_jsx_runtime","styled","import_jsx_runtime","styled","import_styled_components","import_jsx_runtime","styled","import_react","import_styled_components","import_jsx_runtime","styled","import_styled_components","import_jsx_runtime","Wrapper","styled","import_styled_components","import_jsx_runtime","styled","import_react","import_react","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_react","import_styled_components","useTheme","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_jsx_runtime","useTheme","import_react","import_react","import_styled_components","import_jsx_runtime","formatTime","styled","React","import_jsx_runtime","import_styled_components","import_jsx_runtime","styled","import_styled_components","import_jsx_runtime","styled","import_styled_components","styled","import_styled_components","styled","import_styled_components","styled","import_styled_components","styled","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime","PhosphorTrashIcon","import_styled_components","styled","import_styled_components","styled","import_styled_components","import_jsx_runtime","HeaderContainer","styled"]}
1
+ {"version":3,"sources":["../src/index.tsx","../src/components/AudioPosition.tsx","../src/styled/BaseButton.tsx","../src/styled/BaseCheckbox.tsx","../src/styled/BaseControlButton.tsx","../src/styled/BaseInput.tsx","../src/styled/BaseLabel.tsx","../src/styled/BaseSelect.tsx","../src/styled/BaseSlider.tsx","../src/components/AutomaticScrollCheckbox.tsx","../src/components/Channel.tsx","../src/wfpl-theme.ts","../src/components/Clip.tsx","../src/components/ClipHeader.tsx","../src/components/ClipBoundary.tsx","../src/components/FadeOverlay.tsx","../src/components/MasterVolumeControl.tsx","../src/components/Playhead.tsx","../src/components/Playlist.tsx","../src/components/Selection.tsx","../src/components/LoopRegion.tsx","../src/components/SelectionTimeInputs.tsx","../src/components/TimeInput.tsx","../src/utils/timeFormat.ts","../src/contexts/DevicePixelRatio.tsx","../src/contexts/PlaylistInfo.tsx","../src/contexts/Theme.tsx","../src/contexts/TrackControls.tsx","../src/contexts/Playout.tsx","../src/components/SmartChannel.tsx","../src/components/SmartScale.tsx","../src/components/TimeScale.tsx","../src/utils/conversions.ts","../src/components/TimeFormatSelect.tsx","../src/components/Track.tsx","../src/components/TrackControls/Button.tsx","../src/components/TrackControls/ButtonGroup.tsx","../src/components/TrackControls/Controls.tsx","../src/components/TrackControls/Header.tsx","../src/components/TrackControls/VolumeDownIcon.tsx","../src/components/TrackControls/VolumeUpIcon.tsx","../src/components/TrackControls/TrashIcon.tsx","../src/components/TrackControls/Slider.tsx","../src/components/TrackControls/SliderWrapper.tsx","../src/components/TrackControlsWithDelete.tsx"],"sourcesContent":["export * from './components';\nexport * from './contexts';\nexport * from './utils/conversions';\nexport * from './utils/timeFormat';\nexport * from './styled/index';\nexport * from './wfpl-theme';\n","import React from 'react';\nimport styled from 'styled-components';\n\nconst PositionDisplay = styled.span`\n font-family: 'Courier New', Monaco, monospace;\n font-size: 1rem;\n font-weight: 600;\n color: ${props => props.theme?.textColor || '#333'};\n user-select: none;\n`;\n\nexport interface AudioPositionProps {\n formattedTime: string;\n className?: string;\n}\n\n/**\n * Displays the current audio playback position\n */\nexport const AudioPosition: React.FC<AudioPositionProps> = ({\n formattedTime,\n className,\n}) => {\n return (\n <PositionDisplay className={className} aria-label=\"Audio position\">\n {formattedTime}\n </PositionDisplay>\n );\n};\n","import styled from 'styled-components';\n\n/**\n * BaseButton - A styled button component that uses theme values\n *\n * This provides consistent styling across all button elements in the waveform playlist.\n */\nexport const BaseButton = styled.button`\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 0.5rem 1rem;\n font-family: ${(props) => props.theme.fontFamily};\n font-size: ${(props) => props.theme.fontSize};\n font-weight: 500;\n color: ${(props) => props.theme.buttonText};\n background-color: ${(props) => props.theme.buttonBackground};\n border: 1px solid ${(props) => props.theme.buttonBorder};\n border-radius: ${(props) => props.theme.borderRadius};\n cursor: pointer;\n outline: none;\n transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out,\n box-shadow 0.15s ease-in-out;\n\n &:hover:not(:disabled) {\n background-color: ${(props) => props.theme.buttonHoverBackground};\n }\n\n &:focus {\n box-shadow: 0 0 0 2px ${(props) => props.theme.inputFocusBorder}33;\n }\n\n &:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n`;\n\n/**\n * BaseButtonSmall - A smaller variant for compact layouts\n */\nexport const BaseButtonSmall = styled(BaseButton)`\n padding: 0.25rem 0.5rem;\n font-size: ${(props) => props.theme.fontSizeSmall};\n`;\n\n/**\n * IconButton - A square button for icons\n */\nexport const IconButton = styled(BaseButton)`\n padding: 0.5rem;\n min-width: 2.25rem;\n min-height: 2.25rem;\n`;\n\n/**\n * IconButtonSmall - A smaller square button for icons\n */\nexport const IconButtonSmall = styled(BaseButton)`\n padding: 0.25rem;\n min-width: 1.75rem;\n min-height: 1.75rem;\n font-size: ${(props) => props.theme.fontSizeSmall};\n`;\n","import styled from 'styled-components';\n\n/**\n * BaseCheckboxWrapper - Container for checkbox + label\n */\nexport const BaseCheckboxWrapper = styled.div`\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n`;\n\n/**\n * BaseCheckbox - A styled checkbox input\n */\nexport const BaseCheckbox = styled.input`\n cursor: pointer;\n accent-color: ${(props) => props.theme.inputFocusBorder};\n\n &:disabled {\n cursor: not-allowed;\n }\n`;\n\n/**\n * BaseCheckboxLabel - Label for checkboxes\n */\nexport const BaseCheckboxLabel = styled.label`\n margin: 0;\n cursor: pointer;\n user-select: none;\n font-family: ${(props) => props.theme.fontFamily};\n font-size: ${(props) => props.theme.fontSize};\n color: ${(props) => props.theme.textColor};\n`;\n","import styled from 'styled-components';\n\nexport interface ControlButtonProps {\n variant?: 'primary' | 'success' | 'info';\n}\n\n/**\n * ControlButton - A colored action button (primary/success/info variants)\n *\n * This is used for prominent actions like Play, Pause, Record.\n * For neutral buttons, use BaseButton from the styled primitives.\n *\n * Uses theme colors when available, with fallbacks for standalone use.\n */\nexport const BaseControlButton = styled.button<ControlButtonProps>`\n padding: 0.5rem 1rem;\n background: ${(props) => props.theme.buttonBackground || '#007bff'};\n color: ${(props) => props.theme.buttonText || 'white'};\n border: none;\n border-radius: ${(props) => props.theme.borderRadius};\n cursor: pointer;\n font-family: ${(props) => props.theme.fontFamily};\n font-size: ${(props) => props.theme.fontSize};\n font-weight: 500;\n transition: background-color 0.15s ease-in-out;\n\n &:hover:not(:disabled) {\n background: ${(props) => props.theme.buttonHoverBackground || '#0056b3'};\n }\n\n &:focus {\n outline: none;\n box-shadow: 0 0 0 2px ${(props) => props.theme.buttonBackground || '#007bff'}66;\n }\n\n &:disabled {\n background: #6c757d;\n cursor: not-allowed;\n opacity: 0.6;\n }\n`;\n","import styled from 'styled-components';\n\n/**\n * BaseInput - A styled input component that uses theme values\n *\n * This provides consistent styling across all input elements in the waveform playlist.\n * Styling is controlled via the theme, making it easy to adapt to different environments.\n */\nexport const BaseInput = styled.input`\n padding: 0.5rem 0.75rem;\n font-family: ${(props) => props.theme.fontFamily};\n font-size: ${(props) => props.theme.fontSize};\n color: ${(props) => props.theme.inputText};\n background-color: ${(props) => props.theme.inputBackground};\n border: 1px solid ${(props) => props.theme.inputBorder};\n border-radius: ${(props) => props.theme.borderRadius};\n outline: none;\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n\n &::placeholder {\n color: ${(props) => props.theme.inputPlaceholder};\n }\n\n &:focus {\n border-color: ${(props) => props.theme.inputFocusBorder};\n box-shadow: 0 0 0 2px ${(props) => props.theme.inputFocusBorder}33;\n }\n\n &:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n`;\n\n/**\n * BaseInputSmall - A smaller variant for compact layouts\n */\nexport const BaseInputSmall = styled(BaseInput)`\n padding: 0.25rem 0.5rem;\n font-size: ${(props) => props.theme.fontSizeSmall};\n`;\n","import styled from 'styled-components';\n\n/**\n * BaseLabel - A styled label component that uses theme values\n */\nexport const BaseLabel = styled.label`\n font-family: ${(props) => props.theme.fontFamily};\n font-size: ${(props) => props.theme.fontSizeSmall};\n font-weight: 500;\n color: ${(props) => props.theme.textColorMuted};\n margin-bottom: 0.25rem;\n display: block;\n`;\n\n/**\n * InlineLabel - A label that displays inline with its input\n */\nexport const InlineLabel = styled.label`\n font-family: ${(props) => props.theme.fontFamily};\n font-size: ${(props) => props.theme.fontSize};\n color: ${(props) => props.theme.textColor};\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n cursor: pointer;\n`;\n\n/**\n * ScreenReaderOnly - Visually hidden but accessible to screen readers\n */\nexport const ScreenReaderOnly = styled.span`\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n`;\n","import styled from 'styled-components';\n\n/**\n * BaseSelect - A styled select component that uses theme values\n *\n * This provides consistent styling across all select elements in the waveform playlist.\n */\nexport const BaseSelect = styled.select`\n padding: 0.5rem 2rem 0.5rem 0.75rem;\n font-family: ${(props) => props.theme.fontFamily};\n font-size: ${(props) => props.theme.fontSize};\n color: ${(props) => props.theme.inputText};\n background-color: ${(props) => props.theme.inputBackground};\n border: 1px solid ${(props) => props.theme.inputBorder};\n border-radius: ${(props) => props.theme.borderRadius};\n outline: none;\n cursor: pointer;\n appearance: none;\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23666' d='M6 8L1 3h10z'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 0.75rem center;\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n\n &:focus {\n border-color: ${(props) => props.theme.inputFocusBorder};\n box-shadow: 0 0 0 2px ${(props) => props.theme.inputFocusBorder}33;\n }\n\n &:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n /* Style native option elements for dark mode support */\n option {\n color: ${(props) => props.theme.inputText};\n background-color: ${(props) => props.theme.inputBackground};\n }\n`;\n\n/**\n * BaseSelectSmall - A smaller variant for compact layouts\n */\nexport const BaseSelectSmall = styled(BaseSelect)`\n padding: 0.25rem 1.75rem 0.25rem 0.5rem;\n font-size: ${(props) => props.theme.fontSizeSmall};\n`;\n","import styled from 'styled-components';\n\n/**\n * BaseSlider - Themed range input for volume controls, etc.\n *\n * Uses theme values for consistent styling across light/dark modes.\n * Provides custom styling for the track and thumb.\n */\nexport const BaseSlider = styled.input.attrs({ type: 'range' })`\n -webkit-appearance: none;\n appearance: none;\n width: 100%;\n height: 6px;\n background: ${(props) => props.theme.sliderTrackColor};\n border-radius: 3px;\n cursor: pointer;\n outline: none;\n\n /* WebKit (Chrome, Safari) */\n &::-webkit-slider-thumb {\n -webkit-appearance: none;\n appearance: none;\n width: 16px;\n height: 16px;\n background: ${(props) => props.theme.sliderThumbColor};\n border: 2px solid ${(props) => props.theme.inputBackground};\n border-radius: 50%;\n cursor: pointer;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n transition: transform 0.15s ease, box-shadow 0.15s ease;\n }\n\n &::-webkit-slider-thumb:hover {\n transform: scale(1.1);\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n }\n\n /* Firefox */\n &::-moz-range-thumb {\n width: 16px;\n height: 16px;\n background: ${(props) => props.theme.sliderThumbColor};\n border: 2px solid ${(props) => props.theme.inputBackground};\n border-radius: 50%;\n cursor: pointer;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n transition: transform 0.15s ease, box-shadow 0.15s ease;\n }\n\n &::-moz-range-thumb:hover {\n transform: scale(1.1);\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n }\n\n &::-moz-range-track {\n background: ${(props) => props.theme.sliderTrackColor};\n border-radius: 3px;\n height: 6px;\n }\n\n &:focus {\n outline: none;\n }\n\n &:focus::-webkit-slider-thumb {\n box-shadow: 0 0 0 3px ${(props) => props.theme.inputFocusBorder}33;\n }\n\n &:focus::-moz-range-thumb {\n box-shadow: 0 0 0 3px ${(props) => props.theme.inputFocusBorder}33;\n }\n\n &:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n }\n\n &:disabled::-webkit-slider-thumb {\n cursor: not-allowed;\n }\n\n &:disabled::-moz-range-thumb {\n cursor: not-allowed;\n }\n`;\n","import React from 'react';\nimport { BaseCheckboxWrapper, BaseCheckbox, BaseCheckboxLabel } from '../styled/index';\n\nexport interface AutomaticScrollCheckboxProps {\n checked: boolean;\n onChange: (checked: boolean) => void;\n disabled?: boolean;\n className?: string;\n}\n\n/**\n * Checkbox control for enabling/disabling automatic scroll during playback\n */\nexport const AutomaticScrollCheckbox: React.FC<AutomaticScrollCheckboxProps> = ({\n checked,\n onChange,\n disabled = false,\n className,\n}) => {\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n onChange(e.target.checked);\n };\n\n return (\n <BaseCheckboxWrapper className={className}>\n <BaseCheckbox\n type=\"checkbox\"\n id=\"automatic-scroll\"\n className=\"automatic-scroll\"\n checked={checked}\n onChange={handleChange}\n disabled={disabled}\n />\n <BaseCheckboxLabel htmlFor=\"automatic-scroll\">Automatic Scroll</BaseCheckboxLabel>\n </BaseCheckboxWrapper>\n );\n};\n","import React, { FunctionComponent, useLayoutEffect, useCallback, useRef } from 'react';\nimport styled from 'styled-components';\nimport { Peaks, Bits } from '@waveform-playlist/webaudio-peaks';\nimport { WaveformColor, WaveformDrawMode, isWaveformGradient, waveformColorToCss } from '../wfpl-theme';\n\n// Re-export WaveformColor for consumers\nexport type { WaveformColor } from '../wfpl-theme';\nexport type { WaveformDrawMode } from '../wfpl-theme';\n\nconst MAX_CANVAS_WIDTH = 1000;\n\n/**\n * Creates a Canvas gradient from a WaveformColor configuration\n */\nfunction createCanvasFillStyle(\n ctx: CanvasRenderingContext2D,\n color: WaveformColor,\n width: number,\n height: number\n): string | CanvasGradient {\n if (!isWaveformGradient(color)) {\n return color;\n }\n\n let gradient: CanvasGradient;\n if (color.direction === 'vertical') {\n gradient = ctx.createLinearGradient(0, 0, 0, height);\n } else {\n gradient = ctx.createLinearGradient(0, 0, width, 0);\n }\n\n for (const stop of color.stops) {\n gradient.addColorStop(stop.offset, stop.color);\n }\n\n return gradient;\n}\n\ninterface WaveformProps {\n readonly $cssWidth: number;\n readonly $waveHeight: number;\n}\n\nconst Waveform = styled.canvas.attrs<WaveformProps>((props) => ({\n style: {\n width: `${props.$cssWidth}px`,\n height: `${props.$waveHeight}px`,\n },\n}))<WaveformProps>`\n float: left;\n position: relative;\n /* Promote to own compositing layer for smoother scrolling */\n will-change: transform;\n /* Disable image rendering interpolation */\n image-rendering: pixelated;\n image-rendering: crisp-edges;\n`;\n\ninterface WrapperProps {\n readonly $index: number;\n readonly $cssWidth: number;\n readonly $waveHeight: number;\n readonly $waveFillColor: string; // CSS background value (solid or gradient)\n}\n\nconst Wrapper = styled.div.attrs<WrapperProps>((props) => ({\n style: {\n top: `${props.$waveHeight * props.$index}px`,\n width: `${props.$cssWidth}px`,\n height: `${props.$waveHeight}px`,\n },\n}))<WrapperProps>`\n position: absolute;\n background: ${(props) => props.$waveFillColor};\n /* Force GPU compositing layer to reduce scroll flickering */\n transform: translateZ(0);\n backface-visibility: hidden;\n`;\n\nexport interface ChannelProps {\n className?: string;\n index: number;\n data: Peaks;\n bits: Bits;\n length: number;\n devicePixelRatio?: number;\n waveHeight?: number;\n /** Waveform bar color - can be a solid color string or gradient config */\n waveOutlineColor?: WaveformColor;\n /** Waveform background color - can be a solid color string or gradient config */\n waveFillColor?: WaveformColor;\n /** Width in pixels of waveform bars. Default: 1 */\n barWidth?: number;\n /** Spacing in pixels between waveform bars. Default: 0 */\n barGap?: number;\n /** If true, background is transparent (for use with external progress overlay) */\n transparentBackground?: boolean;\n /**\n * Drawing mode:\n * - 'inverted': Draw waveOutlineColor where there's NO audio (current default). Good for gradient bars.\n * - 'normal': Draw waveFillColor where there IS audio. Good for gradient backgrounds.\n */\n drawMode?: WaveformDrawMode;\n}\n\nexport const Channel: FunctionComponent<ChannelProps> = (props) => {\n const {\n data,\n bits,\n length,\n index,\n className,\n devicePixelRatio = 1,\n waveHeight = 80,\n waveOutlineColor = '#E0EFF1',\n waveFillColor = 'grey',\n barWidth = 1,\n barGap = 0,\n transparentBackground = false,\n drawMode = 'inverted',\n } = props;\n const canvasesRef = useRef<HTMLCanvasElement[]>([]);\n\n const canvasRef = useCallback(\n (canvas: HTMLCanvasElement | null) => {\n if (canvas !== null) {\n const index: number = parseInt(canvas.dataset.index!, 10);\n canvasesRef.current[index] = canvas;\n }\n },\n []\n );\n\n useLayoutEffect(() => {\n const canvases = canvasesRef.current;\n const step = barWidth + barGap;\n let globalPixelOffset = 0; // Track global pixel position across all canvases\n\n for (let i = 0; i < canvases.length; i++) {\n const canvas = canvases[i];\n const ctx = canvas.getContext('2d');\n const h2 = Math.floor(waveHeight / 2);\n const maxValue = 2 ** (bits - 1);\n\n if (ctx) {\n ctx.resetTransform();\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.imageSmoothingEnabled = false;\n ctx.scale(devicePixelRatio, devicePixelRatio);\n\n // Create gradient using CSS pixel coordinates (after scaling)\n // This ensures the gradient aligns with the drawing coordinates\n const canvasWidth = canvas.width / devicePixelRatio;\n\n // Choose canvas fill color based on draw mode:\n let fillColor: WaveformColor;\n if (drawMode === 'normal') {\n // Normal: canvas draws the bars directly\n fillColor = waveFillColor;\n } else {\n // Inverted: canvas masks non-audio areas, background shows as bars\n fillColor = waveOutlineColor;\n }\n ctx.fillStyle = createCanvasFillStyle(\n ctx,\n fillColor,\n canvasWidth,\n waveHeight\n );\n\n // Calculate where bars should be drawn in this canvas\n // by finding where in the global bar pattern this canvas starts\n const canvasStartGlobal = globalPixelOffset;\n const canvasEndGlobal = globalPixelOffset + canvasWidth;\n\n // Find the first bar that could affect this canvas\n // A bar at position X extends from X to X+barWidth-1\n // So we need bars where barStart + barWidth > canvasStartGlobal\n // Which means barStart > canvasStartGlobal - barWidth\n const firstBarGlobal = Math.floor((canvasStartGlobal - barWidth + step) / step) * step;\n\n // Draw bars at the correct positions\n for (let barGlobal = Math.max(0, firstBarGlobal); barGlobal < canvasEndGlobal; barGlobal += step) {\n const x = barGlobal - canvasStartGlobal; // Local x position in this canvas\n\n // Skip if the entire bar would be before this canvas\n if (x + barWidth <= 0) continue;\n\n const peakIndex = barGlobal; // Each pixel position corresponds to a peak\n\n if (peakIndex * 2 + 1 < data.length) {\n const minPeak = data[peakIndex * 2] / maxValue;\n const maxPeak = data[peakIndex * 2 + 1] / maxValue;\n\n const min = Math.abs(minPeak * h2);\n const max = Math.abs(maxPeak * h2);\n\n if (drawMode === 'normal') {\n // Normal mode: draw the actual peak bars\n // Draw from h2-max to h2+min (the actual waveform shape)\n ctx.fillRect(x, h2 - max, barWidth, max + min);\n } else {\n // Inverted mode (default): draw areas WITHOUT audio\n // This masks the background color to reveal the peaks\n // draw top region (above max peak)\n ctx.fillRect(x, 0, barWidth, h2 - max);\n // draw bottom region (below min peak)\n ctx.fillRect(x, h2 + min, barWidth, h2 - min);\n }\n }\n }\n }\n\n globalPixelOffset += canvas.width / devicePixelRatio;\n }\n }, [\n data,\n bits,\n waveHeight,\n waveOutlineColor,\n waveFillColor,\n devicePixelRatio,\n length,\n barWidth,\n barGap,\n drawMode,\n ]);\n\n let totalWidth = length;\n let waveformCount = 0;\n const waveforms = [];\n while (totalWidth > 0) {\n const currentWidth = Math.min(totalWidth, MAX_CANVAS_WIDTH);\n const waveform = (\n <Waveform\n key={`${length}-${waveformCount}`}\n $cssWidth={currentWidth}\n width={currentWidth * devicePixelRatio}\n height={waveHeight * devicePixelRatio}\n $waveHeight={waveHeight}\n data-index={waveformCount}\n ref={canvasRef}\n />\n );\n\n waveforms.push(waveform);\n totalWidth -= currentWidth;\n waveformCount += 1;\n }\n\n // Background color depends on draw mode:\n // Visual result is always: waveOutlineColor = bars, waveFillColor = background\n // - normal: waveFillColor is background, canvas draws waveOutlineColor bars on top\n // - inverted: waveFillColor is background, canvas masks with it to reveal waveOutlineColor (bars)\n const bgColor = waveFillColor;\n const backgroundCss = transparentBackground ? 'transparent' : waveformColorToCss(bgColor);\n\n return (\n <Wrapper\n $index={index}\n $cssWidth={length}\n className={className}\n $waveHeight={waveHeight}\n $waveFillColor={backgroundCss}\n >\n {waveforms}\n </Wrapper>\n );\n};\n","/**\n * Waveform Playlist Theme\n *\n * This file defines the theme interface and default values for the waveform playlist components.\n */\n\n/**\n * Gradient color stop for waveform gradients\n */\nexport interface GradientStop {\n offset: number; // 0 to 1\n color: string;\n}\n\n/**\n * Gradient configuration for waveforms\n * Can be applied vertically (top to bottom) or horizontally (left to right)\n */\nexport interface WaveformGradient {\n type: 'linear';\n direction: 'vertical' | 'horizontal';\n stops: GradientStop[];\n}\n\n/**\n * Waveform color can be a simple string or a gradient configuration\n */\nexport type WaveformColor = string | WaveformGradient;\n\n/**\n * Type guard to check if a WaveformColor is a gradient\n */\nexport function isWaveformGradient(color: WaveformColor): color is WaveformGradient {\n return typeof color === 'object' && color !== null && 'type' in color;\n}\n\n/**\n * Converts WaveformColor to a CSS background value\n */\nexport function waveformColorToCss(color: WaveformColor): string {\n if (!isWaveformGradient(color)) {\n return color;\n }\n\n const direction = color.direction === 'vertical' ? 'to bottom' : 'to right';\n const stops = color.stops\n .map((stop) => `${stop.color} ${stop.offset * 100}%`)\n .join(', ');\n\n return `linear-gradient(${direction}, ${stops})`;\n}\n\n/**\n * Waveform drawing mode determines how colors are applied:\n * - 'inverted': Canvas draws waveOutlineColor in areas WITHOUT audio (current default).\n * waveFillColor shows through where audio peaks are. Good for gradient bars.\n * - 'normal': Canvas draws waveFillColor bars where audio peaks ARE.\n * waveOutlineColor is used as background. Good for gradient backgrounds.\n */\nexport type WaveformDrawMode = 'inverted' | 'normal';\n\nexport interface WaveformPlaylistTheme {\n // Waveform drawing mode - controls how colors are applied\n waveformDrawMode?: WaveformDrawMode;\n\n // Waveform colors - can be solid colors or gradients\n waveOutlineColor: WaveformColor;\n waveFillColor: WaveformColor;\n waveProgressColor: string; // Progress stays solid for simplicity\n\n // Selected track colors - can also be gradients\n selectedWaveOutlineColor: WaveformColor;\n selectedWaveFillColor: WaveformColor;\n selectedTrackControlsBackground: string;\n\n // Timescale colors\n timeColor: string;\n timescaleBackgroundColor: string;\n\n // Playback UI colors\n playheadColor: string;\n selectionColor: string;\n\n // Loop region colors (Audacity-style loop markers)\n loopRegionColor: string;\n loopMarkerColor: string;\n\n // Clip header colors (for multi-clip editing)\n clipHeaderBackgroundColor: string;\n clipHeaderBorderColor: string;\n clipHeaderTextColor: string;\n clipHeaderFontFamily: string;\n\n // Selected clip header colors\n selectedClipHeaderBackgroundColor: string;\n\n // Fade overlay colors\n fadeOverlayColor: string;\n\n // UI component colors\n backgroundColor: string;\n surfaceColor: string;\n borderColor: string;\n textColor: string;\n textColorMuted: string;\n\n // Interactive element colors\n inputBackground: string;\n inputBorder: string;\n inputText: string;\n inputPlaceholder: string;\n inputFocusBorder: string;\n\n // Button colors\n buttonBackground: string;\n buttonText: string;\n buttonBorder: string;\n buttonHoverBackground: string;\n\n // Slider colors\n sliderTrackColor: string;\n sliderThumbColor: string;\n\n // Annotation colors\n annotationBoxBackground: string;\n annotationBoxActiveBackground: string;\n annotationBoxHoverBackground: string;\n annotationBoxBorder: string;\n annotationBoxActiveBorder: string;\n annotationLabelColor: string;\n annotationResizeHandleColor: string;\n annotationResizeHandleActiveColor: string;\n annotationTextItemHoverBackground: string;\n\n // Spacing and sizing\n borderRadius: string;\n fontFamily: string;\n fontSize: string;\n fontSizeSmall: string;\n}\n\nexport const defaultTheme: WaveformPlaylistTheme = {\n waveformDrawMode: 'inverted',\n waveOutlineColor: '#ffffff',\n waveFillColor: '#1a7f8e', // White background for crisp look\n waveProgressColor: 'rgba(0, 0, 0, 0.10)', // Subtle dark overlay for light mode\n\n selectedWaveOutlineColor: '#ffffff',\n selectedWaveFillColor: '#00b4d8', // Selected: brighter cyan\n selectedTrackControlsBackground: '#d9e9ff', // Light blue background for selected track controls\n timeColor: '#000',\n timescaleBackgroundColor: '#fff',\n playheadColor: '#f00',\n selectionColor: 'rgba(255, 105, 180, 0.7)', // hot pink - high contrast on light backgrounds\n loopRegionColor: 'rgba(59, 130, 246, 0.3)', // Blue - distinct from pink selection\n loopMarkerColor: '#3b82f6', // Blue marker triangles\n clipHeaderBackgroundColor: 'rgba(0, 0, 0, 0.1)',\n clipHeaderBorderColor: 'rgba(0, 0, 0, 0.2)',\n clipHeaderTextColor: '#333',\n clipHeaderFontFamily: 'inherit',\n selectedClipHeaderBackgroundColor: '#b3d9ff', // Brighter blue for selected track clip headers\n\n // Fade overlay colors\n fadeOverlayColor: 'rgba(0, 0, 0, 0.4)', // Semi-transparent overlay for fade regions\n\n // UI component colors\n backgroundColor: '#ffffff',\n surfaceColor: '#f5f5f5',\n borderColor: '#ddd',\n textColor: '#333',\n textColorMuted: '#666',\n\n // Interactive element colors\n inputBackground: '#ffffff',\n inputBorder: '#ccc',\n inputText: '#333',\n inputPlaceholder: '#999',\n inputFocusBorder: '#0066cc',\n\n // Button colors - blue to match common UI patterns\n buttonBackground: '#0091ff',\n buttonText: '#ffffff',\n buttonBorder: '#0081e6',\n buttonHoverBackground: '#0081e6',\n\n // Slider colors\n sliderTrackColor: '#ddd',\n sliderThumbColor: '#daa520', // goldenrod\n\n // Annotation colors\n annotationBoxBackground: 'rgba(255, 255, 255, 0.85)',\n annotationBoxActiveBackground: 'rgba(255, 255, 255, 0.95)',\n annotationBoxHoverBackground: 'rgba(255, 255, 255, 0.98)',\n annotationBoxBorder: '#ff9800',\n annotationBoxActiveBorder: '#d67600',\n annotationLabelColor: '#2a2a2a',\n annotationResizeHandleColor: 'rgba(0, 0, 0, 0.4)',\n annotationResizeHandleActiveColor: 'rgba(0, 0, 0, 0.8)',\n annotationTextItemHoverBackground: 'rgba(0, 0, 0, 0.03)',\n\n // Spacing and sizing\n borderRadius: '4px',\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen, Ubuntu, sans-serif',\n fontSize: '14px',\n fontSizeSmall: '12px',\n};\n\nexport const darkTheme: WaveformPlaylistTheme = {\n // Normal mode: waveOutlineColor = bars, waveFillColor = background\n waveformDrawMode: 'inverted',\n // Dark bars on warm amber background\n waveOutlineColor: '#c49a6c', // Solid warm amber for background\n waveFillColor: '#1a1612', // Very dark warm brown for bars\n waveProgressColor: 'rgba(100, 70, 40, 0.6)', // Warm brown progress overlay\n // Selected: slightly lighter bars on brighter amber background\n selectedWaveFillColor: '#241c14', // Slightly lighter warm brown bars when selected\n selectedWaveOutlineColor: '#e8c090', // Brighter amber background when selected\n selectedTrackControlsBackground: '#2a2218', // Dark warm brown for selected track controls\n timeColor: '#d8c0a8', // Warm amber for timescale text\n timescaleBackgroundColor: '#1a1612', // Dark warm brown background\n playheadColor: '#3a8838', // Darker Ampelmännchen green playhead\n selectionColor: 'rgba(60, 140, 58, 0.6)', // Darker Ampelmännchen green selection - visible on dark backgrounds\n loopRegionColor: 'rgba(96, 165, 250, 0.35)', // Light blue - distinct from green selection\n loopMarkerColor: '#60a5fa', // Light blue marker triangles\n clipHeaderBackgroundColor: 'rgba(20, 16, 12, 0.85)', // Dark background for clip headers\n clipHeaderBorderColor: 'rgba(200, 160, 120, 0.25)',\n clipHeaderTextColor: '#d8c0a8', // Warm amber text\n clipHeaderFontFamily: 'inherit',\n selectedClipHeaderBackgroundColor: '#3a2c20', // Darker warm brown for selected clip headers\n\n // Fade overlay colors\n fadeOverlayColor: 'rgba(200, 100, 80, 0.5)', // Warm red-orange overlay visible on dark backgrounds\n\n // UI component colors\n backgroundColor: '#1e1e1e',\n surfaceColor: '#2d2d2d',\n borderColor: '#444',\n textColor: '#e0e0e0',\n textColorMuted: '#999',\n\n // Interactive element colors\n inputBackground: '#2d2d2d',\n inputBorder: '#555',\n inputText: '#e0e0e0',\n inputPlaceholder: '#777',\n inputFocusBorder: '#4A9EFF',\n\n // Button colors - Ampelmännchen green (#63C75F) with black text\n buttonBackground: '#63C75F',\n buttonText: '#0a0a0f',\n buttonBorder: '#52b84e',\n buttonHoverBackground: '#78d074',\n\n // Slider colors\n sliderTrackColor: '#555',\n sliderThumbColor: '#f0c040', // brighter goldenrod for dark mode\n\n // Annotation colors (dark mode - warm amber theme)\n annotationBoxBackground: 'rgba(40, 32, 24, 0.9)',\n annotationBoxActiveBackground: 'rgba(50, 40, 30, 0.95)',\n annotationBoxHoverBackground: 'rgba(60, 48, 36, 0.98)',\n annotationBoxBorder: '#c49a6c',\n annotationBoxActiveBorder: '#d4a87c',\n annotationLabelColor: '#d8c0a8',\n annotationResizeHandleColor: 'rgba(200, 160, 120, 0.5)',\n annotationResizeHandleActiveColor: 'rgba(220, 180, 140, 0.8)',\n annotationTextItemHoverBackground: 'rgba(200, 160, 120, 0.08)',\n\n // Spacing and sizing\n borderRadius: '4px',\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen, Ubuntu, sans-serif',\n fontSize: '14px',\n fontSizeSmall: '12px',\n};\n","import React, { FunctionComponent, ReactNode } from 'react';\nimport styled from 'styled-components';\nimport { useDraggable } from '@dnd-kit/core';\nimport { CSS } from '@dnd-kit/utilities';\nimport { ClipHeader, CLIP_HEADER_HEIGHT } from './ClipHeader';\nimport { ClipBoundary, CLIP_BOUNDARY_WIDTH } from './ClipBoundary';\nimport { FadeOverlay } from './FadeOverlay';\nimport type { Fade } from '@waveform-playlist/core';\n\ninterface ClipContainerProps {\n readonly $left?: number; // Horizontal position in pixels (optional for DragOverlay)\n readonly $width?: number; // Width in pixels (optional for DragOverlay)\n readonly $isOverlay?: boolean; // Whether this is rendering in DragOverlay\n readonly $isDragging?: boolean; // Whether this clip is being dragged\n}\n\nconst ClipContainer = styled.div.attrs<ClipContainerProps>((props) => ({\n style: props.$isOverlay ? {} : {\n left: `${props.$left}px`,\n width: `${props.$width}px`,\n },\n}))<ClipContainerProps>`\n position: ${props => props.$isOverlay ? 'relative' : 'absolute'};\n top: 0;\n height: ${props => props.$isOverlay ? 'auto' : '100%'};\n width: ${props => props.$isOverlay ? `${props.$width}px` : 'auto'};\n display: flex;\n flex-direction: column;\n background: rgba(255, 255, 255, 0.05);\n z-index: 10; /* Above progress overlay (z-index: 2) but below controls/playhead */\n pointer-events: none; /* Let clicks pass through to ClickOverlay for playhead positioning */\n\n &:hover {\n background: rgba(255, 255, 255, 0.08);\n }\n`;\n\ninterface ChannelsWrapperProps {\n readonly $isOverlay?: boolean;\n}\n\nconst ChannelsWrapper = styled.div<ChannelsWrapperProps>`\n flex: 1;\n position: relative;\n overflow: ${props => props.$isOverlay ? 'visible' : 'hidden'};\n`;\n\nexport interface ClipProps {\n className?: string;\n children?: ReactNode;\n clipId: string; // Unique clip ID\n trackIndex: number; // Track index (for drag operations)\n clipIndex: number; // Clip index within track (for drag operations)\n trackName: string; // Track name (shown in header)\n startSample: number; // Start position in samples\n durationSamples: number; // Duration in samples\n samplesPerPixel: number;\n // Optional header (for multi-clip editing with drag-to-move)\n showHeader?: boolean;\n disableHeaderDrag?: boolean; // Disable drag on header (for presentation-only rendering)\n isOverlay?: boolean; // Rendering in DragOverlay (disables absolute positioning)\n // Track selection\n isSelected?: boolean; // Whether the track is selected\n onMouseDown?: (e: React.MouseEvent<HTMLDivElement>) => void; // Called when clip is pressed (for track selection - fires before drag)\n trackId?: string; // Track ID for identifying which track this clip belongs to\n // Fade configuration\n fadeIn?: Fade; // Fade in effect\n fadeOut?: Fade; // Fade out effect\n sampleRate?: number; // Sample rate for converting fade duration to pixels\n showFades?: boolean; // Show fade in/out overlays\n // Mobile optimization\n touchOptimized?: boolean; // Enable larger touch targets for mobile devices\n}\n\n/**\n * Clip component for rendering individual audio clips within a track\n *\n * Each clip is positioned based on its startTime and has a width based on its duration.\n * This allows multiple clips to be arranged on a single track with gaps or overlaps.\n *\n * Includes a draggable ClipHeader at the top for repositioning clips on the timeline.\n */\nexport const Clip: FunctionComponent<ClipProps> = ({\n children,\n className,\n clipId,\n trackIndex,\n clipIndex,\n trackName,\n startSample,\n durationSamples,\n samplesPerPixel,\n showHeader = false,\n disableHeaderDrag = false,\n isOverlay = false,\n isSelected = false,\n onMouseDown,\n trackId,\n fadeIn,\n fadeOut,\n sampleRate = 44100,\n showFades = false,\n touchOptimized = false,\n}) => {\n // Calculate horizontal position based on start sample\n // Use Math.floor to always snap to pixel boundaries\n const left = Math.floor(startSample / samplesPerPixel);\n\n // Calculate width as the difference between end and start pixel positions\n // This ensures clips are perfectly adjacent with no gaps\n const endPixel = Math.floor((startSample + durationSamples) / samplesPerPixel);\n const width = endPixel - left;\n\n // Use draggable only if header is shown and drag is enabled\n const enableDrag = showHeader && !disableHeaderDrag && !isOverlay;\n\n // Main clip draggable (for moving entire clip)\n const draggableId = `clip-${trackIndex}-${clipIndex}`;\n const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, isDragging } = useDraggable({\n id: draggableId,\n data: { clipId, trackIndex, clipIndex },\n disabled: !enableDrag,\n });\n\n // Left boundary draggable (for trimming start)\n const leftBoundaryId = `clip-boundary-left-${trackIndex}-${clipIndex}`;\n const {\n attributes: leftBoundaryAttributes,\n listeners: leftBoundaryListeners,\n setActivatorNodeRef: setLeftBoundaryActivatorRef,\n isDragging: isLeftBoundaryDragging,\n } = useDraggable({\n id: leftBoundaryId,\n data: { clipId, trackIndex, clipIndex, boundary: 'left' },\n disabled: !enableDrag,\n });\n\n // Right boundary draggable (for trimming end)\n const rightBoundaryId = `clip-boundary-right-${trackIndex}-${clipIndex}`;\n const {\n attributes: rightBoundaryAttributes,\n listeners: rightBoundaryListeners,\n setActivatorNodeRef: setRightBoundaryActivatorRef,\n isDragging: isRightBoundaryDragging,\n } = useDraggable({\n id: rightBoundaryId,\n data: { clipId, trackIndex, clipIndex, boundary: 'right' },\n disabled: !enableDrag,\n });\n\n // Apply transform for dragging\n const style = transform ? {\n transform: CSS.Translate.toString(transform),\n zIndex: isDragging ? 100 : undefined, // Below controls (z-index: 999) but above other clips\n } : undefined;\n\n return (\n <ClipContainer\n ref={setNodeRef}\n style={style}\n className={className}\n $left={left}\n $width={width}\n $isOverlay={isOverlay}\n data-clip-container=\"true\"\n data-track-id={trackId}\n onMouseDown={onMouseDown}\n >\n {showHeader && (\n <ClipHeader\n clipId={clipId}\n trackIndex={trackIndex}\n clipIndex={clipIndex}\n trackName={trackName}\n isSelected={isSelected}\n disableDrag={disableHeaderDrag}\n dragHandleProps={enableDrag ? { attributes, listeners, setActivatorNodeRef } : undefined}\n />\n )}\n <ChannelsWrapper $isOverlay={isOverlay}>\n {children}\n {/* Fade overlays */}\n {showFades && fadeIn && fadeIn.duration > 0 && (\n <FadeOverlay\n left={0}\n width={Math.floor((fadeIn.duration * sampleRate) / samplesPerPixel)}\n type=\"fadeIn\"\n curveType={fadeIn.type}\n />\n )}\n {showFades && fadeOut && fadeOut.duration > 0 && (\n <FadeOverlay\n left={width - Math.floor((fadeOut.duration * sampleRate) / samplesPerPixel)}\n width={Math.floor((fadeOut.duration * sampleRate) / samplesPerPixel)}\n type=\"fadeOut\"\n curveType={fadeOut.type}\n />\n )}\n </ChannelsWrapper>\n {/* Clip boundaries - outside ChannelsWrapper to avoid overflow:hidden clipping */}\n {showHeader && !disableHeaderDrag && !isOverlay && (\n <>\n <ClipBoundary\n clipId={clipId}\n trackIndex={trackIndex}\n clipIndex={clipIndex}\n edge=\"left\"\n touchOptimized={touchOptimized}\n dragHandleProps={{\n attributes: leftBoundaryAttributes,\n listeners: leftBoundaryListeners,\n setActivatorNodeRef: setLeftBoundaryActivatorRef,\n isDragging: isLeftBoundaryDragging,\n }}\n />\n <ClipBoundary\n clipId={clipId}\n trackIndex={trackIndex}\n clipIndex={clipIndex}\n edge=\"right\"\n touchOptimized={touchOptimized}\n dragHandleProps={{\n attributes: rightBoundaryAttributes,\n listeners: rightBoundaryListeners,\n setActivatorNodeRef: setRightBoundaryActivatorRef,\n isDragging: isRightBoundaryDragging,\n }}\n />\n </>\n )}\n </ClipContainer>\n );\n};\n","import React, { FunctionComponent } from 'react';\nimport styled from 'styled-components';\nimport type { DraggableAttributes } from '@dnd-kit/core';\nimport type { SyntheticListenerMap } from '@dnd-kit/core/dist/hooks/utilities';\n\nexport const CLIP_HEADER_HEIGHT = 22; // Height of the clip header in pixels\n\ninterface HeaderContainerProps {\n readonly $isDragging?: boolean;\n readonly $interactive?: boolean; // Whether it's draggable or just presentational\n readonly $isSelected?: boolean; // Whether the track is selected\n}\n\nconst HeaderContainer = styled.div<HeaderContainerProps>`\n position: relative;\n height: ${CLIP_HEADER_HEIGHT}px;\n background: ${props => props.$isSelected\n ? props.theme.selectedClipHeaderBackgroundColor\n : props.theme.clipHeaderBackgroundColor};\n border-bottom: 1px solid ${props => props.theme.clipHeaderBorderColor};\n display: flex;\n align-items: center;\n padding: 0 8px;\n cursor: ${props => props.$interactive ? (props.$isDragging ? 'grabbing' : 'grab') : 'default'};\n user-select: none;\n z-index: 110;\n flex-shrink: 0;\n pointer-events: auto; /* Re-enable pointer events (parent ClipContainer has pointer-events: none) */\n touch-action: ${props => props.$interactive ? 'none' : 'auto'}; /* Prevent browser scroll during drag on touch devices */\n\n ${props => props.$interactive && `\n &:hover {\n background: ${props.theme.clipHeaderBackgroundColor}dd;\n }\n\n &:active {\n cursor: grabbing;\n }\n `}\n`;\n\nconst TrackName = styled.span`\n font-size: 11px;\n font-weight: 600;\n font-family: ${props => props.theme.clipHeaderFontFamily};\n color: ${props => props.theme.clipHeaderTextColor};\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n`;\n\n// Presentational-only header (no drag behavior)\nexport interface ClipHeaderPresentationalProps {\n trackName: string;\n isSelected?: boolean; // Whether the track is selected\n}\n\nexport const ClipHeaderPresentational: FunctionComponent<ClipHeaderPresentationalProps> = ({\n trackName,\n isSelected = false,\n}) => {\n return (\n <HeaderContainer\n $isDragging={false}\n $interactive={false}\n $isSelected={isSelected}\n >\n <TrackName>{trackName}</TrackName>\n </HeaderContainer>\n );\n};\n\nexport interface DragHandleProps {\n attributes: DraggableAttributes;\n listeners: SyntheticListenerMap | undefined;\n setActivatorNodeRef: (element: HTMLElement | null) => void;\n}\n\nexport interface ClipHeaderProps {\n clipId: string;\n trackIndex: number;\n clipIndex: number;\n trackName: string;\n isSelected?: boolean; // Whether the track is selected\n disableDrag?: boolean; // Disable drag behavior (for presentation-only rendering in overlays)\n dragHandleProps?: DragHandleProps; // Props for drag handle functionality\n}\n\n/**\n * ClipHeader component - Draggable title bar for audio clips\n *\n * Renders at the top of each clip (above all channels).\n * Drag the header to move the clip along the timeline.\n * Shows the track name (not clip-specific info).\n *\n * Theme colors (from useTheme):\n * - clipHeaderBackgroundColor / selectedClipHeaderBackgroundColor\n * - clipHeaderBorderColor\n * - clipHeaderTextColor\n */\nexport const ClipHeader: FunctionComponent<ClipHeaderProps> = ({\n clipId,\n trackIndex,\n clipIndex,\n trackName,\n isSelected = false,\n disableDrag = false,\n dragHandleProps,\n}) => {\n // Use purely presentational version when drag is disabled or no drag handle props\n if (disableDrag || !dragHandleProps) {\n return (\n <ClipHeaderPresentational\n trackName={trackName}\n isSelected={isSelected}\n />\n );\n }\n\n const { attributes, listeners, setActivatorNodeRef } = dragHandleProps;\n\n return (\n <HeaderContainer\n ref={setActivatorNodeRef}\n data-clip-id={clipId}\n $interactive={true}\n $isSelected={isSelected}\n {...listeners}\n {...attributes}\n >\n <TrackName>{trackName}</TrackName>\n </HeaderContainer>\n );\n};\n","import React, { FunctionComponent } from 'react';\nimport styled from 'styled-components';\nimport type { DraggableAttributes } from '@dnd-kit/core';\nimport type { SyntheticListenerMap } from '@dnd-kit/core/dist/hooks/utilities';\nimport type { DragHandleProps as BaseDragHandleProps } from './ClipHeader';\n\nexport const CLIP_BOUNDARY_WIDTH = 8; // Width of the draggable boundary in pixels\nexport const CLIP_BOUNDARY_WIDTH_TOUCH = 24; // Larger touch target for mobile (minimum 44px recommended, but 24px works well for trim handles)\n\ntype BoundaryEdge = 'left' | 'right';\n\ninterface BoundaryContainerProps {\n readonly $edge: BoundaryEdge;\n readonly $isDragging?: boolean;\n readonly $isHovered?: boolean;\n readonly $touchOptimized?: boolean;\n}\n\nconst BoundaryContainer = styled.div<BoundaryContainerProps>`\n position: absolute;\n ${props => props.$edge === 'left' ? 'left: 0;' : 'right: 0;'}\n top: 0;\n bottom: 0;\n width: ${props => props.$touchOptimized ? CLIP_BOUNDARY_WIDTH_TOUCH : CLIP_BOUNDARY_WIDTH}px;\n cursor: col-resize;\n user-select: none;\n z-index: 105; /* Above waveform, below header */\n pointer-events: auto; /* Re-enable pointer events (parent ClipContainer has pointer-events: none) */\n touch-action: none; /* Prevent browser scroll during drag on touch devices */\n\n /* Invisible by default, visible on hover */\n background: ${props =>\n props.$isDragging\n ? 'rgba(255, 255, 255, 0.4)'\n : props.$isHovered\n ? 'rgba(255, 255, 255, 0.2)'\n : 'transparent'\n };\n\n ${props => props.$edge === 'left'\n ? `border-left: 2px solid ${\n props.$isDragging\n ? 'rgba(255, 255, 255, 0.8)'\n : props.$isHovered\n ? 'rgba(255, 255, 255, 0.5)'\n : 'transparent'\n };`\n : `border-right: 2px solid ${\n props.$isDragging\n ? 'rgba(255, 255, 255, 0.8)'\n : props.$isHovered\n ? 'rgba(255, 255, 255, 0.5)'\n : 'transparent'\n };`\n }\n\n transition: background 0.15s ease, border-color 0.15s ease;\n\n &:hover {\n background: rgba(255, 255, 255, 0.2);\n ${props => props.$edge === 'left'\n ? 'border-left: 2px solid rgba(255, 255, 255, 0.5);'\n : 'border-right: 2px solid rgba(255, 255, 255, 0.5);'\n }\n }\n\n &:active {\n background: rgba(255, 255, 255, 0.4);\n ${props => props.$edge === 'left'\n ? 'border-left: 2px solid rgba(255, 255, 255, 0.8);'\n : 'border-right: 2px solid rgba(255, 255, 255, 0.8);'\n }\n }\n`;\n\n// Extend the base DragHandleProps to add isDragging\ninterface DragHandleProps extends BaseDragHandleProps {\n isDragging?: boolean;\n}\n\nexport interface ClipBoundaryProps {\n clipId: string;\n trackIndex: number;\n clipIndex: number;\n edge: BoundaryEdge;\n dragHandleProps?: DragHandleProps;\n /**\n * Enable larger touch targets for mobile devices.\n * When true, boundary width increases from 8px to 24px for easier touch targeting.\n */\n touchOptimized?: boolean;\n}\n\n/**\n * ClipBoundary component - Draggable edge for trimming clips\n *\n * Renders at the left or right edge of a clip.\n * Drag to trim the clip (adjust offset and duration).\n * Supports bidirectional trimming (trim in and out).\n *\n * On mobile (touchOptimized=true), boundaries are wider for easier targeting.\n */\nexport const ClipBoundary: FunctionComponent<ClipBoundaryProps> = ({\n clipId,\n trackIndex,\n clipIndex,\n edge,\n dragHandleProps,\n touchOptimized = false,\n}) => {\n const [isHovered, setIsHovered] = React.useState(false);\n\n if (!dragHandleProps) {\n // No drag handle props provided, render non-interactive boundary\n return null;\n }\n\n const { attributes, listeners, setActivatorNodeRef, isDragging } = dragHandleProps;\n\n return (\n <BoundaryContainer\n ref={setActivatorNodeRef}\n data-clip-id={clipId}\n data-boundary-edge={edge}\n $edge={edge}\n $isDragging={isDragging}\n $isHovered={isHovered}\n $touchOptimized={touchOptimized}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n {...listeners}\n {...attributes}\n />\n );\n};\n","import React, { FunctionComponent } from 'react';\nimport styled, { useTheme } from 'styled-components';\nimport type { FadeType } from '@waveform-playlist/core';\nimport type { WaveformPlaylistTheme } from '../wfpl-theme';\n\ninterface FadeContainerProps {\n readonly $left: number;\n readonly $width: number;\n readonly $type: 'fadeIn' | 'fadeOut';\n}\n\n// Use .attrs() for left/width to avoid generating new CSS classes on every render\nconst FadeContainer = styled.div.attrs<FadeContainerProps>((props) => ({\n style: {\n left: `${props.$left}px`,\n width: `${props.$width}px`,\n },\n}))<FadeContainerProps>`\n position: absolute;\n top: 0;\n bottom: 0;\n pointer-events: none;\n z-index: 50;\n`;\n\ninterface FadeSvgProps {\n readonly $type: 'fadeIn' | 'fadeOut';\n}\n\nconst FadeSvg = styled.svg<FadeSvgProps>`\n width: 100%;\n height: 100%;\n display: block;\n /* Flip horizontally for fadeOut - makes it mirror of fadeIn */\n transform: ${props => props.$type === 'fadeOut' ? 'scaleX(-1)' : 'none'};\n`;\n\nexport interface FadeOverlayProps {\n /** Position in pixels from the start of the clip */\n left: number;\n /** Width of the fade region in pixels */\n width: number;\n /** Type of fade: fadeIn or fadeOut */\n type: 'fadeIn' | 'fadeOut';\n /** Fade curve type */\n curveType?: FadeType;\n /** Custom fill color (defaults to theme.fadeOverlayColor) */\n color?: string;\n}\n\n/**\n * Generates an SVG path for a fade in curve\n * Always generates fadeIn shape - fadeOut is achieved by CSS transform scaleX(-1)\n *\n * The curve shows: more overlay at start (audio quiet), less overlay at end (audio full)\n */\nfunction generateFadePath(\n width: number,\n height: number,\n curveType: FadeType = 'logarithmic'\n): string {\n const points: string[] = [];\n const numPoints = Math.max(20, Math.min(width, 100)); // More points for smoother curves\n\n for (let i = 0; i <= numPoints; i++) {\n const x = (i / numPoints) * width;\n const progress = i / numPoints; // 0 to 1\n\n // Apply curve transformation based on type\n let curvedProgress: number;\n switch (curveType) {\n case 'linear':\n curvedProgress = progress;\n break;\n case 'exponential':\n curvedProgress = progress * progress;\n break;\n case 'sCurve':\n // S-curve using sine\n curvedProgress = (1 - Math.cos(progress * Math.PI)) / 2;\n break;\n case 'logarithmic':\n default:\n // Logarithmic curve (more natural for audio)\n curvedProgress = Math.log10(1 + progress * 9) / Math.log10(10);\n break;\n }\n\n // fadeIn: starts covered (y near 0), ends uncovered (y near height)\n // Y=0 is top of SVG, Y=height is bottom\n // We draw the curve edge, then fill above it\n const y = (1 - curvedProgress) * height;\n points.push(`${x},${y}`);\n }\n\n // Path: start at bottom-left, draw curve, go to top-right, top-left, close\n return `M 0,${height} L ${points.join(' L ')} L ${width},0 L 0,0 Z`;\n}\n\n/**\n * FadeOverlay component - Visual indicator for fade in/out regions on clips\n *\n * Renders a semi-transparent overlay with a curved shape indicating\n * the fade envelope. The shape follows the selected fade curve type.\n */\nexport const FadeOverlay: FunctionComponent<FadeOverlayProps> = ({\n left,\n width,\n type,\n curveType = 'logarithmic',\n color,\n}) => {\n const theme = useTheme() as WaveformPlaylistTheme;\n\n // Don't render if width is too small\n if (width < 1) return null;\n\n // Use color prop, then theme color, then fallback\n const fillColor = color || theme?.fadeOverlayColor || 'rgba(0, 0, 0, 0.4)';\n\n return (\n <FadeContainer $left={left} $width={width} $type={type}>\n <FadeSvg $type={type} viewBox={`0 0 ${width} 100`} preserveAspectRatio=\"none\">\n <path\n d={generateFadePath(width, 100, curveType)}\n fill={fillColor}\n />\n </FadeSvg>\n </FadeContainer>\n );\n};\n","import React from 'react';\nimport styled from 'styled-components';\nimport { BaseSlider, BaseLabel } from '../styled/index';\n\nconst VolumeContainer = styled.div`\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n`;\n\nconst VolumeLabel = styled(BaseLabel)`\n margin: 0;\n white-space: nowrap;\n`;\n\nconst VolumeSlider = styled(BaseSlider)`\n width: 120px;\n`;\n\nexport interface MasterVolumeControlProps {\n volume: number; // 0-1.0 (linear gain, consistent with Web Audio API)\n onChange: (volume: number) => void;\n disabled?: boolean;\n className?: string;\n}\n\n/**\n * Master volume control slider component\n * Accepts volume as 0-1.0 range (linear gain) and displays as percentage\n */\nexport const MasterVolumeControl: React.FC<MasterVolumeControlProps> = ({\n volume,\n onChange,\n disabled = false,\n className,\n}) => {\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n // Convert percentage (0-100) to linear gain (0-1.0)\n onChange(parseFloat(e.target.value) / 100);\n };\n\n return (\n <VolumeContainer className={className}>\n <VolumeLabel htmlFor=\"master-gain\">Master Volume</VolumeLabel>\n <VolumeSlider\n min=\"0\"\n max=\"100\"\n value={volume * 100}\n onChange={handleChange}\n disabled={disabled}\n id=\"master-gain\"\n />\n </VolumeContainer>\n );\n};\n","import React, { useRef, useEffect } from 'react';\nimport styled from 'styled-components';\n\ninterface PlayheadLineProps {\n readonly $position: number;\n readonly $color: string;\n}\n\nconst PlayheadLine = styled.div.attrs<PlayheadLineProps>((props) => ({\n style: {\n transform: `translate3d(${props.$position}px, 0, 0)`,\n },\n}))<PlayheadLineProps>`\n position: absolute;\n top: 0;\n left: 0;\n width: 2px;\n background: ${(props) => props.$color};\n height: 100%;\n z-index: 100; /* Below sticky controls (z-index: 101) so playhead is hidden when scrolled behind controls */\n pointer-events: none;\n will-change: transform;\n`;\n\n/**\n * Props passed to the default playhead component or custom render function.\n */\nexport interface PlayheadProps {\n /** Position in pixels from left edge (only valid when not playing) */\n position: number;\n /** Playhead color (default: #ff0000) */\n color?: string;\n /** Whether audio is currently playing */\n isPlaying: boolean;\n /** Ref to current time in seconds - use for smooth animation during playback */\n currentTimeRef: React.RefObject<number>;\n /** Audio context start time when playback began - for calculating elapsed time */\n playbackStartTimeRef: React.RefObject<number>;\n /** Audio position when playback started - for calculating current position */\n audioStartPositionRef: React.RefObject<number>;\n /** Samples per pixel - for converting time to pixels */\n samplesPerPixel: number;\n /** Sample rate - for converting time to pixels */\n sampleRate: number;\n /** Controls offset in pixels */\n controlsOffset: number;\n /** Function to get current audio context time - required for smooth animation */\n getAudioContextTime?: () => number;\n}\n\n/**\n * Type for custom playhead render functions.\n * Receives position, color, and animation refs for smooth 60fps animation.\n * Custom playheads should use requestAnimationFrame with the refs during playback.\n */\nexport type RenderPlayheadFunction = (props: PlayheadProps) => React.ReactNode;\n\n/**\n * Default playhead component - a simple vertical line.\n * Uses GPU-accelerated transform for smooth animation.\n */\nexport const Playhead: React.FC<PlayheadProps> = ({ position, color = '#ff0000' }) => {\n return <PlayheadLine $position={position} $color={color} />;\n};\n\n// === Custom Playhead Variants ===\n\nconst PlayheadWithMarkerContainer = styled.div<{ $color: string }>`\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n z-index: 100; /* Below sticky controls (z-index: 101) so playhead is hidden when scrolled behind controls */\n pointer-events: none;\n will-change: transform;\n`;\n\nconst MarkerTriangle = styled.div<{ $color: string }>`\n position: absolute;\n top: -10px;\n left: -6px;\n width: 0;\n height: 0;\n border-left: 7px solid transparent;\n border-right: 7px solid transparent;\n border-top: 10px solid ${(props) => props.$color};\n`;\n\nconst MarkerLine = styled.div<{ $color: string }>`\n position: absolute;\n top: 0;\n left: 0;\n width: 2px;\n height: 100%;\n background: ${(props) => props.$color};\n`;\n\n/**\n * Playhead with a triangle marker at the top.\n * Provides better visual indication of the current position.\n * Uses requestAnimationFrame for smooth 60fps animation during playback.\n */\nexport const PlayheadWithMarker: React.FC<PlayheadProps> = ({\n color = '#ff0000',\n isPlaying,\n currentTimeRef,\n playbackStartTimeRef,\n audioStartPositionRef,\n samplesPerPixel,\n sampleRate,\n controlsOffset,\n getAudioContextTime,\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const animationFrameRef = useRef<number | null>(null);\n\n useEffect(() => {\n const updatePosition = () => {\n if (containerRef.current) {\n let time: number;\n if (isPlaying && getAudioContextTime) {\n const elapsed = getAudioContextTime() - (playbackStartTimeRef.current ?? 0);\n time = (audioStartPositionRef.current ?? 0) + elapsed;\n } else {\n time = currentTimeRef.current ?? 0;\n }\n const pos = (time * sampleRate) / samplesPerPixel + controlsOffset;\n containerRef.current.style.transform = `translate3d(${pos}px, 0, 0)`;\n }\n\n if (isPlaying) {\n animationFrameRef.current = requestAnimationFrame(updatePosition);\n }\n };\n\n if (isPlaying) {\n animationFrameRef.current = requestAnimationFrame(updatePosition);\n } else {\n updatePosition();\n }\n\n return () => {\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n animationFrameRef.current = null;\n }\n };\n }, [isPlaying, sampleRate, samplesPerPixel, controlsOffset, currentTimeRef, playbackStartTimeRef, audioStartPositionRef, getAudioContextTime]);\n\n // Update position when stopped (for seeks)\n useEffect(() => {\n if (!isPlaying && containerRef.current) {\n const time = currentTimeRef.current ?? 0;\n const pos = (time * sampleRate) / samplesPerPixel + controlsOffset;\n containerRef.current.style.transform = `translate3d(${pos}px, 0, 0)`;\n }\n });\n\n return (\n <PlayheadWithMarkerContainer ref={containerRef} $color={color}>\n <MarkerTriangle $color={color} />\n <MarkerLine $color={color} />\n </PlayheadWithMarkerContainer>\n );\n};\n","import styled, { DefaultTheme, withTheme } from 'styled-components';\nimport React, { FunctionComponent } from 'react';\n\nconst Wrapper = styled.div`\n overflow-y: hidden;\n overflow-x: auto;\n position: relative;\n`;\n\ninterface ScrollContainerProps {\n readonly $backgroundColor?: string;\n readonly $width?: number;\n}\n\n// Use .attrs() for width to avoid generating new CSS classes on every render\nconst ScrollContainer = styled.div.attrs<ScrollContainerProps>((props) => ({\n style: props.$width !== undefined ? { width: `${props.$width}px` } : {},\n}))<ScrollContainerProps>`\n position: relative;\n background: ${(props) => props.$backgroundColor || 'transparent'};\n`;\n\ninterface TimescaleWrapperProps {\n readonly $width?: number;\n readonly $backgroundColor?: string;\n}\n\n// Use .attrs() for width to avoid generating new CSS classes on every render\nconst TimescaleWrapper = styled.div.attrs<TimescaleWrapperProps>((props) => ({\n style: props.$width ? { minWidth: `${props.$width}px` } : {},\n}))<TimescaleWrapperProps>`\n background: ${(props) => props.$backgroundColor || 'white'};\n width: 100%;\n position: relative;\n overflow: hidden; /* Constrain loop region to timescale area */\n`;\n\ninterface TracksContainerProps {\n readonly $width?: number;\n readonly $backgroundColor?: string;\n}\n\n// Use .attrs() for width to avoid generating new CSS classes on every render\nconst TracksContainer = styled.div.attrs<TracksContainerProps>((props) => ({\n style: props.$width !== undefined ? { minWidth: `${props.$width}px` } : {},\n}))<TracksContainerProps>`\n position: relative;\n background: ${(props) => props.$backgroundColor || 'transparent'};\n width: 100%;\n`;\n\ninterface ClickOverlayProps {\n readonly $controlsWidth?: number;\n readonly $isSelecting?: boolean;\n}\n\nconst ClickOverlay = styled.div<ClickOverlayProps>`\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n cursor: crosshair;\n /* When selecting, raise z-index above clip boundaries (z-index: 105) to prevent interference */\n z-index: ${props => props.$isSelecting ? 110 : 1};\n`;\n\nexport interface PlaylistProps {\n readonly theme: DefaultTheme;\n readonly children?: JSX.Element | JSX.Element[];\n readonly backgroundColor?: string;\n readonly timescaleBackgroundColor?: string;\n readonly timescale?: JSX.Element;\n readonly timescaleWidth?: number;\n readonly tracksWidth?: number;\n readonly scrollContainerWidth?: number;\n readonly controlsWidth?: number;\n readonly onTracksClick?: (e: React.MouseEvent<HTMLDivElement>) => void;\n readonly onTracksMouseDown?: (e: React.MouseEvent<HTMLDivElement>) => void;\n readonly onTracksMouseMove?: (e: React.MouseEvent<HTMLDivElement>) => void;\n readonly onTracksMouseUp?: (e: React.MouseEvent<HTMLDivElement>) => void;\n readonly scrollContainerRef?: (el: HTMLDivElement | null) => void;\n /** When true, selection is in progress - raises z-index to prevent clip boundary interference */\n readonly isSelecting?: boolean;\n}\nexport const Playlist: FunctionComponent<PlaylistProps> = ({\n children,\n backgroundColor,\n timescaleBackgroundColor,\n timescale,\n timescaleWidth,\n tracksWidth,\n scrollContainerWidth,\n controlsWidth,\n onTracksClick,\n onTracksMouseDown,\n onTracksMouseMove,\n onTracksMouseUp,\n scrollContainerRef,\n isSelecting,\n}) => {\n return (\n <Wrapper data-scroll-container=\"true\" ref={scrollContainerRef}>\n <ScrollContainer\n $backgroundColor={backgroundColor}\n $width={scrollContainerWidth}\n >\n {timescale && <TimescaleWrapper $width={timescaleWidth} $backgroundColor={timescaleBackgroundColor}>{timescale}</TimescaleWrapper>}\n <TracksContainer $width={tracksWidth} $backgroundColor={backgroundColor}>\n {children}\n {(onTracksClick || onTracksMouseDown) && (\n <ClickOverlay\n $controlsWidth={controlsWidth}\n $isSelecting={isSelecting}\n onClick={onTracksClick}\n onMouseDown={onTracksMouseDown}\n onMouseMove={onTracksMouseMove}\n onMouseUp={onTracksMouseUp}\n />\n )}\n </TracksContainer>\n </ScrollContainer>\n </Wrapper>\n );\n};\n\nexport const StyledPlaylist = withTheme(Playlist);\n","import React from 'react';\nimport styled from 'styled-components';\n\ninterface SelectionOverlayProps {\n readonly $left: number;\n readonly $width: number;\n readonly $color: string;\n}\n\nconst SelectionOverlay = styled.div.attrs<SelectionOverlayProps>((props) => ({\n style: {\n left: `${props.$left}px`,\n width: `${props.$width}px`,\n },\n}))<SelectionOverlayProps>`\n position: absolute;\n top: 0;\n background: ${(props) => props.$color};\n height: 100%;\n z-index: 60; /* Above clips (z-index: 10) and fades (z-index: 50), below playhead (z-index: 100) */\n pointer-events: none;\n opacity: 0.3;\n`;\n\nexport interface SelectionProps {\n startPosition: number; // Start position in pixels\n endPosition: number; // End position in pixels\n color?: string;\n}\n\nexport const Selection: React.FC<SelectionProps> = ({\n startPosition,\n endPosition,\n color = '#00ff00'\n}) => {\n const width = Math.max(0, endPosition - startPosition);\n\n if (width <= 0) {\n return null;\n }\n\n return <SelectionOverlay $left={startPosition} $width={width} $color={color} data-selection />;\n};\n","import React, { useCallback, useRef, useState } from 'react';\nimport styled from 'styled-components';\n\ninterface LoopRegionOverlayProps {\n readonly $left: number;\n readonly $width: number;\n readonly $color: string;\n}\n\nconst LoopRegionOverlayDiv = styled.div.attrs<LoopRegionOverlayProps>((props) => ({\n style: {\n left: `${props.$left}px`,\n width: `${props.$width}px`,\n },\n}))<LoopRegionOverlayProps>`\n position: absolute;\n top: 0;\n background: ${(props) => props.$color};\n height: 100%;\n z-index: 55; /* Between clips (z-index: 50) and selection (z-index: 60) */\n pointer-events: none;\n`;\n\ninterface LoopMarkerProps {\n readonly $left: number;\n readonly $color: string;\n readonly $isStart: boolean;\n readonly $isDragging?: boolean;\n}\n\nconst LoopMarker = styled.div.attrs<LoopMarkerProps>((props) => ({\n style: {\n left: `${props.$left}px`,\n },\n}))<LoopMarkerProps>`\n position: absolute;\n top: 0;\n width: 2px;\n height: 100%;\n background: ${(props) => props.$color};\n z-index: 90; /* Below playhead (z-index: 100) */\n pointer-events: none;\n\n /* Triangle marker at top */\n &::before {\n content: '';\n position: absolute;\n top: 0;\n ${(props) => props.$isStart ? 'left: 0' : 'right: 0'};\n width: 0;\n height: 0;\n border-top: 8px solid ${(props) => props.$color};\n ${(props) => props.$isStart\n ? 'border-right: 8px solid transparent;'\n : 'border-left: 8px solid transparent;'\n }\n }\n`;\n\nexport interface LoopRegionProps {\n startPosition: number; // Start position in pixels\n endPosition: number; // End position in pixels\n regionColor?: string;\n markerColor?: string;\n}\n\n/**\n * Loop region overlay with non-interactive markers.\n * This renders over the tracks area - markers are visual only here.\n */\nexport const LoopRegion: React.FC<LoopRegionProps> = ({\n startPosition,\n endPosition,\n regionColor = 'rgba(59, 130, 246, 0.3)',\n markerColor = '#3b82f6'\n}) => {\n const width = Math.max(0, endPosition - startPosition);\n\n if (width <= 0) {\n return null;\n }\n\n return (\n <>\n <LoopRegionOverlayDiv\n $left={startPosition}\n $width={width}\n $color={regionColor}\n data-loop-region\n />\n <LoopMarker\n $left={startPosition}\n $color={markerColor}\n $isStart={true}\n data-loop-marker=\"start\"\n />\n <LoopMarker\n $left={endPosition - 2}\n $color={markerColor}\n $isStart={false}\n data-loop-marker=\"end\"\n />\n </>\n );\n};\n\n// Draggable marker handle for timescale area\ninterface DraggableMarkerHandleProps {\n readonly $left: number;\n readonly $color: string;\n readonly $isStart: boolean;\n readonly $isDragging?: boolean;\n}\n\nconst DraggableMarkerHandle = styled.div.attrs<DraggableMarkerHandleProps>((props) => ({\n style: {\n left: `${props.$left}px`,\n },\n}))<DraggableMarkerHandleProps>`\n position: absolute;\n top: 0;\n width: 12px;\n height: 100%;\n cursor: ew-resize;\n z-index: 100;\n /* Center the handle on the marker position */\n transform: translateX(-5px);\n\n /* Visual marker line */\n &::before {\n content: '';\n position: absolute;\n top: 0;\n left: 5px;\n width: 2px;\n height: 100%;\n background: ${(props) => props.$color};\n opacity: ${(props) => props.$isDragging ? 1 : 0.8};\n }\n\n /* Triangle marker at top */\n &::after {\n content: '';\n position: absolute;\n top: 0;\n ${(props) => props.$isStart ? 'left: 5px' : 'left: -1px'};\n width: 0;\n height: 0;\n border-top: 10px solid ${(props) => props.$color};\n ${(props) => props.$isStart\n ? 'border-right: 10px solid transparent;'\n : 'border-left: 10px solid transparent;'\n }\n }\n\n &:hover::before {\n opacity: 1;\n }\n`;\n\n// Background shading in timescale - draggable to move entire region\ninterface TimescaleLoopShadeProps {\n readonly $left: number;\n readonly $width: number;\n readonly $color: string;\n readonly $isDragging?: boolean;\n}\n\nconst TimescaleLoopShade = styled.div.attrs<TimescaleLoopShadeProps>((props) => ({\n style: {\n left: `${props.$left}px`,\n width: `${props.$width}px`,\n },\n}))<TimescaleLoopShadeProps>`\n position: absolute;\n top: 0;\n height: 100%;\n background: ${(props) => props.$color};\n z-index: 50;\n cursor: grab;\n\n &:active {\n cursor: grabbing;\n }\n`;\n\nexport interface LoopRegionMarkersProps {\n startPosition: number; // Start position in pixels\n endPosition: number; // End position in pixels\n markerColor?: string;\n regionColor?: string;\n onLoopStartChange?: (newPositionPixels: number) => void;\n onLoopEndChange?: (newPositionPixels: number) => void;\n /** Called when the entire region is moved */\n onLoopRegionMove?: (newStartPixels: number, newEndPixels: number) => void;\n /** Minimum position in pixels (usually controls width offset) */\n minPosition?: number;\n /** Maximum position in pixels */\n maxPosition?: number;\n}\n\n/**\n * Draggable loop region markers for the timescale area.\n * These are interactive and can be dragged to adjust loop boundaries.\n * The shaded region between markers can be dragged to move the entire loop.\n */\nexport const LoopRegionMarkers: React.FC<LoopRegionMarkersProps> = ({\n startPosition,\n endPosition,\n markerColor = '#3b82f6',\n regionColor = 'rgba(59, 130, 246, 0.3)',\n onLoopStartChange,\n onLoopEndChange,\n onLoopRegionMove,\n minPosition = 0,\n maxPosition = Infinity,\n}) => {\n const [draggingMarker, setDraggingMarker] = useState<'start' | 'end' | 'region' | null>(null);\n const dragStartX = useRef<number>(0);\n const dragStartPosition = useRef<number>(0);\n const dragStartEnd = useRef<number>(0);\n\n const width = Math.max(0, endPosition - startPosition);\n\n // Handle dragging individual markers\n const handleMarkerMouseDown = useCallback((\n e: React.MouseEvent,\n marker: 'start' | 'end'\n ) => {\n e.preventDefault();\n e.stopPropagation();\n setDraggingMarker(marker);\n dragStartX.current = e.clientX;\n dragStartPosition.current = marker === 'start' ? startPosition : endPosition;\n\n const handleMouseMove = (moveEvent: MouseEvent) => {\n const delta = moveEvent.clientX - dragStartX.current;\n const newPosition = dragStartPosition.current + delta;\n\n if (marker === 'start') {\n // Start marker can't go past end marker or outside bounds\n const clampedPosition = Math.max(minPosition, Math.min(endPosition - 10, newPosition));\n onLoopStartChange?.(clampedPosition);\n } else {\n // End marker can't go before start marker or outside bounds\n const clampedPosition = Math.max(startPosition + 10, Math.min(maxPosition, newPosition));\n onLoopEndChange?.(clampedPosition);\n }\n };\n\n const handleMouseUp = () => {\n setDraggingMarker(null);\n document.removeEventListener('mousemove', handleMouseMove);\n document.removeEventListener('mouseup', handleMouseUp);\n };\n\n document.addEventListener('mousemove', handleMouseMove);\n document.addEventListener('mouseup', handleMouseUp);\n }, [startPosition, endPosition, minPosition, maxPosition, onLoopStartChange, onLoopEndChange]);\n\n // Handle dragging the entire region\n const handleRegionMouseDown = useCallback((e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n setDraggingMarker('region');\n dragStartX.current = e.clientX;\n dragStartPosition.current = startPosition;\n dragStartEnd.current = endPosition;\n\n const regionWidth = endPosition - startPosition;\n\n const handleMouseMove = (moveEvent: MouseEvent) => {\n const delta = moveEvent.clientX - dragStartX.current;\n let newStart = dragStartPosition.current + delta;\n let newEnd = dragStartEnd.current + delta;\n\n // Clamp to bounds while maintaining region width\n if (newStart < minPosition) {\n newStart = minPosition;\n newEnd = minPosition + regionWidth;\n }\n if (newEnd > maxPosition) {\n newEnd = maxPosition;\n newStart = maxPosition - regionWidth;\n }\n\n onLoopRegionMove?.(newStart, newEnd);\n };\n\n const handleMouseUp = () => {\n setDraggingMarker(null);\n document.removeEventListener('mousemove', handleMouseMove);\n document.removeEventListener('mouseup', handleMouseUp);\n };\n\n document.addEventListener('mousemove', handleMouseMove);\n document.addEventListener('mouseup', handleMouseUp);\n }, [startPosition, endPosition, minPosition, maxPosition, onLoopRegionMove]);\n\n if (width <= 0) {\n return null;\n }\n\n return (\n <>\n <TimescaleLoopShade\n $left={startPosition}\n $width={width}\n $color={regionColor}\n $isDragging={draggingMarker === 'region'}\n onMouseDown={handleRegionMouseDown}\n data-loop-region-timescale\n />\n <DraggableMarkerHandle\n $left={startPosition}\n $color={markerColor}\n $isStart={true}\n $isDragging={draggingMarker === 'start'}\n onMouseDown={(e) => handleMarkerMouseDown(e, 'start')}\n data-loop-marker-handle=\"start\"\n />\n <DraggableMarkerHandle\n $left={endPosition}\n $color={markerColor}\n $isStart={false}\n $isDragging={draggingMarker === 'end'}\n onMouseDown={(e) => handleMarkerMouseDown(e, 'end')}\n data-loop-marker-handle=\"end\"\n />\n </>\n );\n};\n\n// Click-to-create wrapper for timescale area\ninterface TimescaleLoopCreatorProps {\n readonly $leftOffset?: number;\n}\n\nconst TimescaleLoopCreator = styled.div.attrs<TimescaleLoopCreatorProps>((props) => ({\n style: {\n left: `${props.$leftOffset || 0}px`,\n },\n}))<TimescaleLoopCreatorProps>`\n position: absolute;\n top: 0;\n right: 0;\n height: 100%; /* Stay within timescale bounds, don't extend into tracks */\n cursor: crosshair;\n z-index: 40; /* Below markers and shading */\n`;\n\nexport interface TimescaleLoopRegionProps {\n /** Current loop start position in pixels */\n startPosition: number;\n /** Current loop end position in pixels */\n endPosition: number;\n markerColor?: string;\n regionColor?: string;\n /** Called when loop region changes (start pixels, end pixels) */\n onLoopRegionChange?: (startPixels: number, endPixels: number) => void;\n /** Minimum position in pixels */\n minPosition?: number;\n /** Maximum position in pixels */\n maxPosition?: number;\n /** Offset for controls area (left margin) */\n controlsOffset?: number;\n}\n\n/**\n * Complete timescale loop region component with:\n * - Click and drag to create a new loop region\n * - Drag markers to resize existing loop region\n * - Drag the shaded region to move the entire loop\n */\nexport const TimescaleLoopRegion: React.FC<TimescaleLoopRegionProps> = ({\n startPosition,\n endPosition,\n markerColor = '#3b82f6',\n regionColor = 'rgba(59, 130, 246, 0.3)',\n onLoopRegionChange,\n minPosition = 0,\n maxPosition = Infinity,\n controlsOffset = 0,\n}) => {\n const [isCreating, setIsCreating] = useState(false);\n const createStartX = useRef<number>(0);\n const containerRef = useRef<HTMLDivElement>(null);\n\n const hasLoopRegion = endPosition > startPosition;\n\n // Handle creating a new loop region by clicking and dragging\n const handleBackgroundMouseDown = useCallback((e: React.MouseEvent) => {\n // Only create new region if clicking on the background (not on markers or region)\n const target = e.target as HTMLElement;\n if (target.closest('[data-loop-marker-handle]') || target.closest('[data-loop-region-timescale]')) {\n return;\n }\n\n e.preventDefault();\n setIsCreating(true);\n\n const rect = containerRef.current?.getBoundingClientRect();\n if (!rect) return;\n\n const clickX = e.clientX - rect.left;\n const clampedX = Math.max(minPosition, Math.min(maxPosition, clickX));\n createStartX.current = clampedX;\n\n // Set initial position (will be a point until dragged)\n onLoopRegionChange?.(clampedX, clampedX);\n\n const handleMouseMove = (moveEvent: MouseEvent) => {\n const currentX = moveEvent.clientX - rect.left;\n const clampedCurrentX = Math.max(minPosition, Math.min(maxPosition, currentX));\n\n const newStart = Math.min(createStartX.current, clampedCurrentX);\n const newEnd = Math.max(createStartX.current, clampedCurrentX);\n\n onLoopRegionChange?.(newStart, newEnd);\n };\n\n const handleMouseUp = () => {\n setIsCreating(false);\n document.removeEventListener('mousemove', handleMouseMove);\n document.removeEventListener('mouseup', handleMouseUp);\n };\n\n document.addEventListener('mousemove', handleMouseMove);\n document.addEventListener('mouseup', handleMouseUp);\n }, [minPosition, maxPosition, onLoopRegionChange]);\n\n return (\n <TimescaleLoopCreator\n ref={containerRef}\n $leftOffset={controlsOffset}\n onMouseDown={handleBackgroundMouseDown}\n data-timescale-loop-creator\n >\n {hasLoopRegion && (\n <LoopRegionMarkers\n startPosition={startPosition}\n endPosition={endPosition}\n markerColor={markerColor}\n regionColor={regionColor}\n minPosition={minPosition}\n maxPosition={maxPosition}\n onLoopStartChange={(newStart) => onLoopRegionChange?.(newStart, endPosition)}\n onLoopEndChange={(newEnd) => onLoopRegionChange?.(startPosition, newEnd)}\n onLoopRegionMove={(newStart, newEnd) => onLoopRegionChange?.(newStart, newEnd)}\n />\n )}\n </TimescaleLoopCreator>\n );\n};\n","import React, { useEffect, useState } from 'react';\nimport { TimeInput } from './TimeInput';\nimport { type TimeFormat } from '../utils/timeFormat';\n\nexport interface SelectionTimeInputsProps {\n selectionStart: number; // Time in seconds\n selectionEnd: number; // Time in seconds\n onSelectionChange?: (start: number, end: number) => void;\n className?: string;\n}\n\nexport const SelectionTimeInputs: React.FC<SelectionTimeInputsProps> = ({\n selectionStart,\n selectionEnd,\n onSelectionChange,\n className,\n}) => {\n const [timeFormat, setTimeFormat] = useState<TimeFormat>('hh:mm:ss.uuu');\n\n // Listen to the external time-format dropdown\n useEffect(() => {\n const timeFormatSelect = document.querySelector('.time-format') as HTMLSelectElement;\n\n const handleFormatChange = () => {\n if (timeFormatSelect) {\n setTimeFormat(timeFormatSelect.value as TimeFormat);\n }\n };\n\n // Set initial value\n if (timeFormatSelect) {\n setTimeFormat(timeFormatSelect.value as TimeFormat);\n timeFormatSelect.addEventListener('change', handleFormatChange);\n }\n\n return () => {\n timeFormatSelect?.removeEventListener('change', handleFormatChange);\n };\n }, []);\n\n const handleStartChange = (value: number) => {\n if (onSelectionChange) {\n onSelectionChange(value, selectionEnd);\n }\n };\n\n const handleEndChange = (value: number) => {\n if (onSelectionChange) {\n onSelectionChange(selectionStart, value);\n }\n };\n\n return (\n <>\n <TimeInput\n id=\"audio_start\"\n label=\"Start of audio selection\"\n value={selectionStart}\n format={timeFormat}\n className=\"audio-start form-control mr-sm-2\"\n onChange={handleStartChange}\n />\n <TimeInput\n id=\"audio_end\"\n label=\"End of audio selection\"\n value={selectionEnd}\n format={timeFormat}\n className=\"audio-end form-control mr-sm-2\"\n onChange={handleEndChange}\n />\n </>\n );\n};\n","import React, { useEffect, useState } from 'react';\nimport { formatTime, parseTime, type TimeFormat } from '../utils/timeFormat';\nimport { BaseInput, ScreenReaderOnly } from '../styled/index';\n\nexport interface TimeInputProps {\n id: string;\n label: string;\n value: number; // Time in seconds\n format: TimeFormat;\n className?: string;\n onChange?: (value: number) => void;\n readOnly?: boolean;\n}\n\n/**\n * TimeInput - A styled input for time values with format support\n *\n * Uses BaseInput for consistent theming. Displays time in the specified\n * format and parses user input on blur.\n */\nexport const TimeInput: React.FC<TimeInputProps> = ({\n id,\n label,\n value,\n format,\n className,\n onChange,\n readOnly = false,\n}) => {\n const [displayValue, setDisplayValue] = useState('');\n\n // Update display value when value or format changes\n useEffect(() => {\n const formatted = formatTime(value, format);\n setDisplayValue(formatted);\n }, [value, format, id]);\n\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const newDisplayValue = e.target.value;\n setDisplayValue(newDisplayValue);\n };\n\n const handleBlur = () => {\n // Parse the display value and notify parent\n if (onChange) {\n const parsedValue = parseTime(displayValue, format);\n onChange(parsedValue);\n }\n // Re-format to ensure consistent display\n setDisplayValue(formatTime(value, format));\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter') {\n e.currentTarget.blur();\n }\n };\n\n return (\n <>\n <ScreenReaderOnly as=\"label\" htmlFor={id}>\n {label}\n </ScreenReaderOnly>\n <BaseInput\n type=\"text\"\n className={className}\n id={id}\n value={displayValue}\n onChange={handleChange}\n onBlur={handleBlur}\n onKeyDown={handleKeyDown}\n readOnly={readOnly}\n />\n </>\n );\n};\n","/**\n * Time format utilities for displaying and parsing audio timestamps\n */\n\nexport type TimeFormat = 'seconds' | 'thousandths' | 'hh:mm:ss' | 'hh:mm:ss.u' | 'hh:mm:ss.uu' | 'hh:mm:ss.uuu';\n\n/**\n * Format time in clock format (hh:mm:ss with optional decimals)\n */\nfunction clockFormat(seconds: number, decimals: number): string {\n const hours = Math.floor(seconds / 3600) % 24;\n const minutes = Math.floor(seconds / 60) % 60;\n const secs = (seconds % 60).toFixed(decimals);\n\n return (\n String(hours).padStart(2, '0') +\n ':' +\n String(minutes).padStart(2, '0') +\n ':' +\n secs.padStart(decimals + 3, '0')\n );\n}\n\n/**\n * Format seconds according to the specified format\n */\nexport function formatTime(seconds: number, format: TimeFormat): string {\n switch (format) {\n case 'seconds':\n return seconds.toFixed(0);\n case 'thousandths':\n return seconds.toFixed(3);\n case 'hh:mm:ss':\n return clockFormat(seconds, 0);\n case 'hh:mm:ss.u':\n return clockFormat(seconds, 1);\n case 'hh:mm:ss.uu':\n return clockFormat(seconds, 2);\n case 'hh:mm:ss.uuu':\n return clockFormat(seconds, 3);\n default:\n return clockFormat(seconds, 3);\n }\n}\n\n/**\n * Parse a formatted time string back to seconds\n */\nexport function parseTime(timeStr: string, format: TimeFormat): number {\n if (!timeStr) return 0;\n\n switch (format) {\n case 'seconds':\n case 'thousandths':\n return parseFloat(timeStr) || 0;\n\n case 'hh:mm:ss':\n case 'hh:mm:ss.u':\n case 'hh:mm:ss.uu':\n case 'hh:mm:ss.uuu': {\n // Parse hh:mm:ss format\n const parts = timeStr.split(':');\n if (parts.length !== 3) return 0;\n\n const hours = parseInt(parts[0], 10) || 0;\n const minutes = parseInt(parts[1], 10) || 0;\n const seconds = parseFloat(parts[2]) || 0;\n\n return hours * 3600 + minutes * 60 + seconds;\n }\n\n default:\n return 0;\n }\n}\n","import React, { useState, createContext, useContext, ReactNode } from 'react';\n\nfunction getScale() {\n return window.devicePixelRatio;\n}\n\nconst DevicePixelRatioContext = createContext(getScale());\n\ntype Props = {\n children: ReactNode;\n};\nexport const DevicePixelRatioProvider = ({ children }: Props) => {\n const [scale, setScale] = useState(getScale());\n\n matchMedia(`(resolution: ${getScale()}dppx)`).addEventListener(\n 'change',\n () => {\n setScale(getScale());\n },\n { once: true }\n );\n\n return (\n <DevicePixelRatioContext.Provider value={Math.ceil(scale)}>\n {children}\n </DevicePixelRatioContext.Provider>\n );\n};\n\nexport const useDevicePixelRatio = () => useContext(DevicePixelRatioContext);\n","import { createContext, useContext } from 'react';\n\ntype Controls = {\n show: boolean;\n width: number;\n};\n\ntype PlaylistInfo = {\n sampleRate: number;\n samplesPerPixel: number;\n zoomLevels: Array<number>;\n waveHeight: number;\n timeScaleHeight: number;\n duration: number;\n controls: Controls;\n /** Width in pixels of waveform bars. Default: 1 */\n barWidth: number;\n /** Spacing in pixels between waveform bars. Default: 0 */\n barGap: number;\n /** Width in pixels of progress bars. Default: barWidth + barGap (fills gaps). Set to barWidth for no gap fill. */\n progressBarWidth?: number;\n};\n\nexport const PlaylistInfoContext = createContext<PlaylistInfo>({\n sampleRate: 48000,\n samplesPerPixel: 1000,\n zoomLevels: [1000, 1500, 2000, 2500],\n waveHeight: 80,\n timeScaleHeight: 15,\n controls: {\n show: false,\n width: 150,\n },\n duration: 30000,\n barWidth: 1,\n barGap: 0,\n});\n\nexport const usePlaylistInfo = () => useContext(PlaylistInfoContext);\n","import { useContext } from 'react';\nimport { ThemeContext } from 'styled-components';\n\nexport const useTheme = () => useContext(ThemeContext);\n","import React, { createContext, useContext, Fragment } from 'react';\n\nexport const TrackControlsContext = createContext<React.ReactNode>(<Fragment />);\n\nexport const useTrackControls = () => useContext(TrackControlsContext);\n","import React, {\n useState,\n createContext,\n useContext,\n ReactNode,\n Dispatch,\n SetStateAction,\n} from 'react';\n\nconst defaultProgress = 0;\nconst defaultIsPlaying = false;\nconst defaultSelectionStart = 0;\nconst defaultSelectionEnd = 0;\n\nconst defaultPlayout = {\n progress: defaultProgress,\n isPlaying: defaultIsPlaying,\n selectionStart: defaultSelectionStart,\n selectionEnd: defaultSelectionEnd,\n};\n\nconst PlayoutStatusContext = createContext(defaultPlayout);\n\ntype PlayoutStatusUpdate = {\n setIsPlaying: Dispatch<SetStateAction<boolean>>;\n setProgress: Dispatch<SetStateAction<number>>;\n setSelection: (start: number, end: number) => void;\n};\nconst PlayoutStatusUpdateContext = createContext<PlayoutStatusUpdate>({\n setIsPlaying: () => {},\n setProgress: () => {},\n setSelection: () => {},\n});\n\ntype Props = {\n children: ReactNode;\n};\nexport const PlayoutProvider = ({ children }: Props) => {\n const [isPlaying, setIsPlaying] = useState(defaultIsPlaying);\n const [progress, setProgress] = useState(defaultProgress);\n const [selectionStart, setSelectionStart] = useState(defaultSelectionStart);\n const [selectionEnd, setSelectionEnd] = useState(defaultSelectionEnd);\n\n const setSelection = (start: number, end: number) => {\n setSelectionStart(start);\n setSelectionEnd(end);\n };\n\n return (\n <PlayoutStatusUpdateContext.Provider value={{ setIsPlaying, setProgress, setSelection }}>\n <PlayoutStatusContext.Provider value={{ isPlaying, progress, selectionStart, selectionEnd }}>\n {children}\n </PlayoutStatusContext.Provider>\n </PlayoutStatusUpdateContext.Provider>\n );\n};\n\nexport const usePlayoutStatus = () => useContext(PlayoutStatusContext);\nexport const usePlayoutStatusUpdate = () =>\n useContext(PlayoutStatusUpdateContext);\n","import React, { FunctionComponent } from 'react';\nimport { useDevicePixelRatio, usePlaylistInfo, useTheme } from '../contexts';\nimport { Channel } from './Channel';\n\nexport interface SmartChannelProps {\n className?: string;\n index: number;\n data: Int8Array | Int16Array;\n bits: 8 | 16;\n length: number;\n isSelected?: boolean; // Whether this channel's track is selected\n /** If true, background is transparent (for use with external progress overlay) */\n transparentBackground?: boolean;\n}\n\nexport const SmartChannel: FunctionComponent<SmartChannelProps> = ({ isSelected, transparentBackground, ...props }) => {\n const theme = useTheme();\n const { waveHeight, barWidth, barGap } = usePlaylistInfo();\n const devicePixelRatio = useDevicePixelRatio();\n\n // Use selected colors if track is selected\n const waveOutlineColor = isSelected && theme\n ? theme.selectedWaveOutlineColor\n : theme?.waveOutlineColor;\n\n const waveFillColor = isSelected && theme\n ? theme.selectedWaveFillColor\n : theme?.waveFillColor;\n\n // Get draw mode from theme (defaults to 'inverted' for backwards compatibility)\n const drawMode = theme?.waveformDrawMode || 'inverted';\n\n return (\n <Channel\n {...props}\n {...theme}\n waveOutlineColor={waveOutlineColor}\n waveFillColor={waveFillColor}\n waveHeight={waveHeight}\n devicePixelRatio={devicePixelRatio}\n barWidth={barWidth}\n barGap={barGap}\n transparentBackground={transparentBackground}\n drawMode={drawMode}\n ></Channel>\n );\n};\n","import React, { FunctionComponent, useContext } from 'react';\nimport { PlaylistInfoContext } from '../contexts/PlaylistInfo';\nimport { StyledTimeScale } from './TimeScale';\n\nconst timeinfo = new Map([\n [\n 700,\n {\n marker: 1000,\n bigStep: 500,\n smallStep: 100,\n },\n ],\n [\n 1500,\n {\n marker: 2000,\n bigStep: 1000,\n smallStep: 200,\n },\n ],\n [\n 2500,\n {\n marker: 2000,\n bigStep: 1000,\n smallStep: 500,\n },\n ],\n [\n 5000,\n {\n marker: 5000,\n bigStep: 1000,\n smallStep: 500,\n },\n ],\n [\n 10000,\n {\n marker: 10000,\n bigStep: 5000,\n smallStep: 1000,\n },\n ],\n [\n 12000,\n {\n marker: 15000,\n bigStep: 5000,\n smallStep: 1000,\n },\n ],\n [\n Infinity,\n {\n marker: 30000,\n bigStep: 10000,\n smallStep: 5000,\n },\n ],\n]);\n\nfunction getScaleInfo(samplesPerPixel: number) {\n const keys = timeinfo.keys();\n let config;\n\n for (const resolution of keys) {\n if (samplesPerPixel < resolution) {\n config = timeinfo.get(resolution);\n break;\n }\n }\n\n if (config === undefined) {\n config = { marker: 30000, bigStep: 10000, smallStep: 5000 };\n }\n return config;\n}\n\nexport const SmartScale: FunctionComponent = () => {\n const { samplesPerPixel, duration } = useContext(PlaylistInfoContext);\n let config = getScaleInfo(samplesPerPixel);\n\n return (\n <StyledTimeScale\n marker={config.marker}\n bigStep={config.bigStep}\n secondStep={config.smallStep}\n duration={duration}\n />\n );\n};\n","import React, { FunctionComponent, useRef, useEffect, useContext } from 'react';\nimport styled, { withTheme, DefaultTheme } from 'styled-components';\nimport { PlaylistInfoContext } from '../contexts/PlaylistInfo';\nimport { useDevicePixelRatio } from '../contexts/DevicePixelRatio';\nimport { secondsToPixels } from '../utils/conversions';\n\nfunction formatTime(milliseconds: number) {\n const seconds = Math.floor(milliseconds / 1000);\n const s = seconds % 60;\n const m = (seconds - s) / 60;\n\n return `${m}:${String(s).padStart(2, '0')}`;\n}\n\ninterface PlaylistTimeScaleScroll {\n readonly $cssWidth: number;\n readonly $controlWidth: number;\n readonly $timeScaleHeight: number;\n}\nconst PlaylistTimeScaleScroll = styled.div.attrs<PlaylistTimeScaleScroll>((props) => ({\n style: {\n width: `${props.$cssWidth}px`,\n marginLeft: `${props.$controlWidth}px`,\n height: `${props.$timeScaleHeight}px`,\n },\n}))<PlaylistTimeScaleScroll>`\n position: relative;\n overflow: visible; /* Allow time labels to render above the container */\n border-bottom: 1px solid ${props => props.theme.timeColor};\n box-sizing: border-box;\n`;\n\ninterface TimeTicks {\n readonly $cssWidth: number;\n readonly $timeScaleHeight: number;\n}\nconst TimeTicks = styled.canvas.attrs<TimeTicks>((props) => ({\n style: {\n width: `${props.$cssWidth}px`,\n height: `${props.$timeScaleHeight}px`,\n },\n}))<TimeTicks>`\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n`;\n\ninterface TimeStamp {\n readonly $left: number;\n}\nconst TimeStamp = styled.div.attrs<TimeStamp>((props) => ({\n style: {\n left: `${props.$left + 4}px`, // Offset 4px to the right of the tick\n },\n}))<TimeStamp>`\n position: absolute;\n font-size: 0.75rem; /* Smaller font to prevent overflow */\n white-space: nowrap; /* Prevent text wrapping */\n color: ${props => props.theme.timeColor}; /* Use theme color instead of inheriting */\n`;\n\nexport interface TimeScaleProps {\n readonly theme?: DefaultTheme;\n readonly duration: number;\n readonly marker: number;\n readonly bigStep: number;\n readonly secondStep: number;\n readonly renderTimestamp?: (timeMs: number, pixelPosition: number) => React.ReactNode;\n}\n\ninterface TimeScalePropsWithTheme extends TimeScaleProps {\n readonly theme: DefaultTheme;\n}\n\nexport const TimeScale: FunctionComponent<TimeScalePropsWithTheme> = (props) => {\n const {\n theme: { timeColor },\n duration,\n marker,\n bigStep,\n secondStep,\n renderTimestamp,\n } = props;\n const canvasInfo = new Map();\n const timeMarkers = [];\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const {\n sampleRate,\n samplesPerPixel,\n timeScaleHeight,\n controls: { show: showControls, width: controlWidth },\n } = useContext(PlaylistInfoContext);\n const devicePixelRatio = useDevicePixelRatio();\n\n useEffect(() => {\n if (canvasRef.current !== null) {\n const canvas = canvasRef.current;\n const ctx = canvas.getContext('2d');\n\n if (ctx) {\n ctx.resetTransform();\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.imageSmoothingEnabled = false;\n ctx.fillStyle = timeColor;\n ctx.scale(devicePixelRatio, devicePixelRatio);\n\n for (const [pixLeft, scaleHeight] of canvasInfo.entries()) {\n const scaleY = timeScaleHeight - scaleHeight;\n ctx.fillRect(pixLeft, scaleY, 1, scaleHeight);\n }\n }\n }\n }, [\n duration,\n devicePixelRatio,\n timeColor,\n timeScaleHeight,\n bigStep,\n secondStep,\n marker,\n canvasInfo,\n ]);\n\n const widthX = secondsToPixels(duration / 1000, samplesPerPixel, sampleRate);\n const pixPerSec = sampleRate / samplesPerPixel;\n let counter = 0;\n\n for (let i = 0; i < widthX; i += (pixPerSec * secondStep) / 1000) {\n const pix = Math.floor(i);\n\n // create three levels of time markers - at marker point also place a timestamp.\n if (counter % marker === 0) {\n const timeMs = counter;\n const timestamp = formatTime(timeMs);\n\n // Use custom renderer if provided, otherwise use default\n const timestampContent = renderTimestamp ? (\n <React.Fragment key={`timestamp-${counter}`}>\n {renderTimestamp(timeMs, pix)}\n </React.Fragment>\n ) : (\n <TimeStamp key={timestamp} $left={pix}>\n {timestamp}\n </TimeStamp>\n );\n\n timeMarkers.push(timestampContent);\n canvasInfo.set(pix, timeScaleHeight);\n } else if (counter % bigStep === 0) {\n canvasInfo.set(pix, Math.floor(timeScaleHeight / 2));\n } else if (counter % secondStep === 0) {\n canvasInfo.set(pix, Math.floor(timeScaleHeight / 5));\n }\n\n counter += secondStep;\n }\n\n return (\n <PlaylistTimeScaleScroll\n $cssWidth={widthX}\n $controlWidth={showControls ? controlWidth : 0}\n $timeScaleHeight={timeScaleHeight}\n >\n {timeMarkers}\n <TimeTicks\n $cssWidth={widthX}\n $timeScaleHeight={timeScaleHeight}\n width={widthX * devicePixelRatio}\n height={timeScaleHeight * devicePixelRatio}\n ref={canvasRef}\n />\n </PlaylistTimeScaleScroll>\n );\n};\n\nexport const StyledTimeScale = withTheme(TimeScale) as FunctionComponent<TimeScaleProps>;\n","export function samplesToSeconds(samples: number, sampleRate: number) {\n return samples / sampleRate;\n}\n\nexport function secondsToSamples(seconds: number, sampleRate: number) {\n return Math.ceil(seconds * sampleRate);\n}\n\nexport function samplesToPixels(samples: number, samplesPerPixel: number) {\n return Math.floor(samples / samplesPerPixel);\n}\n\nexport function pixelsToSamples(pixels: number, samplesPerPixel: number) {\n return Math.floor(pixels * samplesPerPixel);\n}\n\nexport function pixelsToSeconds(\n pixels: number,\n samplesPerPixel: number,\n sampleRate: number\n) {\n return (pixels * samplesPerPixel) / sampleRate;\n}\n\nexport function secondsToPixels(\n seconds: number,\n samplesPerPixel: number,\n sampleRate: number\n) {\n return Math.ceil((seconds * sampleRate) / samplesPerPixel);\n}\n","import React from 'react';\nimport styled from 'styled-components';\nimport { type TimeFormat } from '../utils/timeFormat';\nimport { BaseSelect } from '../styled/index';\n\nconst SelectWrapper = styled.div`\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n`;\n\nexport interface TimeFormatSelectProps {\n value: TimeFormat;\n onChange: (format: TimeFormat) => void;\n disabled?: boolean;\n className?: string;\n}\n\nconst TIME_FORMAT_OPTIONS: { value: TimeFormat; label: string }[] = [\n { value: 'seconds', label: 'seconds' },\n { value: 'thousandths', label: 'thousandths' },\n { value: 'hh:mm:ss', label: 'hh:mm:ss' },\n { value: 'hh:mm:ss.u', label: 'hh:mm:ss + tenths' },\n { value: 'hh:mm:ss.uu', label: 'hh:mm:ss + hundredths' },\n { value: 'hh:mm:ss.uuu', label: 'hh:mm:ss + milliseconds' },\n];\n\n/**\n * Dropdown select for choosing time display format\n */\nexport const TimeFormatSelect: React.FC<TimeFormatSelectProps> = ({\n value,\n onChange,\n disabled = false,\n className,\n}) => {\n const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => {\n onChange(e.target.value as TimeFormat);\n };\n\n return (\n <SelectWrapper className={className}>\n <BaseSelect\n className=\"time-format\"\n value={value}\n onChange={handleChange}\n disabled={disabled}\n aria-label=\"Time format selection\"\n >\n {TIME_FORMAT_OPTIONS.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </BaseSelect>\n </SelectWrapper>\n );\n};\n","import React, { FunctionComponent, ReactNode } from 'react';\nimport styled from 'styled-components';\nimport { usePlaylistInfo } from '../contexts/PlaylistInfo';\nimport { useTrackControls } from '../contexts/TrackControls';\nimport { CLIP_HEADER_HEIGHT } from './ClipHeader';\n\ninterface ContainerProps {\n readonly $numChannels: number;\n readonly $waveHeight: number;\n readonly $controlWidth: number;\n readonly $width?: number;\n readonly $isSelected?: boolean;\n}\n\ninterface ContainerWithHeaderProps extends ContainerProps {\n readonly $hasClipHeaders: boolean;\n}\n\nconst Container = styled.div.attrs<ContainerWithHeaderProps>((props) => ({\n style: {\n height: `${props.$waveHeight * props.$numChannels + (props.$hasClipHeaders ? CLIP_HEADER_HEIGHT : 0)}px`,\n },\n}))<ContainerWithHeaderProps>`\n position: relative;\n display: flex;\n ${(props) => props.$width !== undefined && `width: ${props.$width}px;`}\n`;\n\ninterface ChannelContainerProps {\n readonly $controlWidth: number;\n readonly $backgroundColor?: string;\n readonly $offset?: number;\n}\nconst ChannelContainer = styled.div.attrs<ChannelContainerProps>((props) => ({\n style: {\n paddingLeft: `${props.$offset || 0}px`,\n },\n}))<ChannelContainerProps>`\n position: relative;\n background: ${(props) => props.$backgroundColor || 'transparent'};\n flex: 1;\n`;\n\nexport interface ControlsWrapperProps {\n readonly $controlWidth: number;\n readonly $isSelected?: boolean;\n}\nconst ControlsWrapper = styled.div.attrs<ControlsWrapperProps>((props) => ({\n style: {\n width: `${props.$controlWidth}px`,\n },\n}))<ControlsWrapperProps>`\n position: sticky;\n z-index: 101; /* Above waveform content, below Docusaurus navbar (z-index: 200) */\n left: 0;\n height: 100%;\n flex-shrink: 0;\n pointer-events: auto;\n background: ${(props) => props.theme.surfaceColor};\n transition: background 0.15s ease-in-out;\n\n /* Selected track: highlighted background */\n ${(props) => props.$isSelected && `\n background: ${props.theme.selectedTrackControlsBackground};\n `}\n`;\n\nexport interface TrackProps {\n className?: string;\n children?: ReactNode;\n numChannels: number;\n backgroundColor?: string;\n offset?: number; // Offset in pixels to shift the waveform right\n width?: number; // Total width of the track (for consistent backgrounds across tracks)\n hasClipHeaders?: boolean; // Whether clips have headers (for multi-clip editing)\n onClick?: () => void; // Called when track is clicked (for track selection)\n trackId?: string; // Track ID for identifying which track was clicked\n isSelected?: boolean; // Whether this track is currently selected (for visual feedback)\n}\n\nexport const Track: FunctionComponent<TrackProps> = ({\n numChannels,\n children,\n className,\n backgroundColor,\n offset = 0,\n width,\n hasClipHeaders = false,\n onClick,\n trackId,\n isSelected = false,\n}) => {\n const {\n waveHeight,\n controls: { show, width: controlWidth },\n } = usePlaylistInfo();\n const controls = useTrackControls();\n return (\n <Container\n $numChannels={numChannels}\n className={className}\n $waveHeight={waveHeight}\n $controlWidth={show ? controlWidth : 0}\n $width={width}\n $hasClipHeaders={hasClipHeaders}\n $isSelected={isSelected}\n >\n <ControlsWrapper\n $controlWidth={show ? controlWidth : 0}\n $isSelected={isSelected}\n >\n {controls}\n </ControlsWrapper>\n <ChannelContainer\n $controlWidth={show ? controlWidth : 0}\n $backgroundColor={backgroundColor}\n $offset={offset}\n onClick={onClick}\n data-track-id={trackId}\n >\n {children}\n </ChannelContainer>\n </Container>\n );\n};\n","import styled from 'styled-components';\n\n/**\n * TrackControls Button - Small button for track controls (Mute, Solo, etc.)\n *\n * Supports variants: outline (default), danger, info\n * Uses theme values for consistent styling.\n */\nexport const Button = styled.button.attrs({\n type: 'button',\n})<{ $variant?: 'outline' | 'danger' | 'info' }>`\n display: inline-block;\n font-family: ${(props) => props.theme.fontFamily};\n font-weight: 500;\n text-align: center;\n vertical-align: middle;\n user-select: none;\n padding: 0.25rem 0.4rem;\n font-size: ${(props) => props.theme.fontSizeSmall};\n line-height: 1;\n border-radius: ${(props) => props.theme.borderRadius};\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out,\n border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n cursor: pointer;\n\n ${(props) => {\n if (props.$variant === 'danger') {\n return `\n color: #fff;\n background-color: #dc3545;\n border: 1px solid #dc3545;\n\n &:hover {\n background-color: #c82333;\n border-color: #bd2130;\n }\n\n &:focus {\n outline: none;\n box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5);\n }\n `;\n } else if (props.$variant === 'info') {\n return `\n color: #fff;\n background-color: #17a2b8;\n border: 1px solid #17a2b8;\n\n &:hover {\n background-color: #138496;\n border-color: #117a8b;\n }\n\n &:focus {\n outline: none;\n box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5);\n }\n `;\n } else {\n // outline variant (default) - uses theme colors\n return `\n color: ${props.theme.textColor};\n background-color: transparent;\n border: 1px solid ${props.theme.borderColor};\n\n &:hover {\n color: #fff;\n background-color: ${props.theme.textColor};\n border-color: ${props.theme.textColor};\n }\n\n &:focus {\n outline: none;\n box-shadow: 0 0 0 0.2rem ${props.theme.inputFocusBorder}33;\n }\n `;\n }\n }}\n`;\n","import styled from 'styled-components';\n\nexport const ButtonGroup = styled.div`\n margin-bottom: 0.3rem;\n\n button:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n }\n\n button:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n }\n`;\n","import styled from 'styled-components';\n\nexport const Controls = styled.div`\n background: transparent;\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: flex-start;\n overflow: hidden;\n box-sizing: border-box;\n text-align: center;\n border: 1px solid ${(props) => props.theme.borderColor};\n border-radius: ${(props) => props.theme.borderRadius};\n`;\n","import styled from 'styled-components';\n\nexport const Header = styled.header`\n overflow: hidden;\n height: 26px;\n width: 100%;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0 0.2rem;\n font-size: ${(props) => props.theme.fontSizeSmall};\n color: ${(props) => props.theme.textColor};\n background-color: transparent;\n`;\n","import React from 'react';\nimport { SpeakerLowIcon, type IconProps } from '@phosphor-icons/react';\n\nexport const VolumeDownIcon: React.FC<IconProps> = (props) => (\n <SpeakerLowIcon weight=\"light\" {...props} />\n);\n","import React from 'react';\nimport { SpeakerHighIcon, type IconProps } from '@phosphor-icons/react';\n\nexport const VolumeUpIcon: React.FC<IconProps> = (props) => (\n <SpeakerHighIcon weight=\"light\" {...props} />\n);\n","import React from 'react';\nimport { TrashIcon as PhosphorTrashIcon, type IconProps } from '@phosphor-icons/react';\n\nexport const TrashIcon: React.FC<IconProps> = (props) => (\n <PhosphorTrashIcon weight=\"light\" {...props} />\n);\n","import styled from 'styled-components';\nimport { BaseSlider } from '../../styled/index';\n\n/**\n * TrackControls Slider - Compact slider for volume/pan controls\n *\n * Extends BaseSlider with track-specific styling:\n * - Smaller thumb and track for compact layout\n * - Uses theme's sliderThumbColor (goldenrod by default)\n */\nexport const Slider = styled(BaseSlider)`\n width: 75%;\n height: 5px;\n background: ${(props) => props.theme.sliderTrackColor};\n\n &::-webkit-slider-thumb {\n width: 12px;\n height: 12px;\n background: ${(props) => props.theme.sliderThumbColor};\n border: none;\n margin-top: -4px;\n cursor: ew-resize;\n }\n\n &::-moz-range-thumb {\n width: 12px;\n height: 12px;\n background: ${(props) => props.theme.sliderThumbColor};\n border: none;\n cursor: ew-resize;\n }\n\n &::-webkit-slider-runnable-track {\n height: 5px;\n background: ${(props) => props.theme.sliderTrackColor};\n border-radius: 3px;\n }\n\n &::-moz-range-track {\n height: 5px;\n background: ${(props) => props.theme.sliderTrackColor};\n border-radius: 3px;\n }\n\n &:focus::-webkit-slider-runnable-track {\n background: ${(props) => props.theme.inputBorder};\n }\n\n &:focus::-moz-range-track {\n background: ${(props) => props.theme.inputBorder};\n }\n\n &:focus::-webkit-slider-thumb {\n border: 2px solid ${(props) => props.theme.textColor};\n }\n\n &:focus::-moz-range-thumb {\n border: 2px solid ${(props) => props.theme.textColor};\n }\n`;\n","import styled from 'styled-components';\n\nexport const SliderWrapper = styled.label`\n width: 100%;\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0 1rem;\n margin-bottom: 0.2rem;\n font-size: 14px;\n`;\n","/**\n * Track Controls with Delete Button\n *\n * Reusable track controls component that includes standard controls\n * (mute, solo, volume, pan) plus a delete button\n */\n\nimport React from 'react';\nimport styled from 'styled-components';\nimport {\n Controls,\n SliderWrapper,\n Slider,\n VolumeDownIcon,\n VolumeUpIcon,\n Button,\n ButtonGroup,\n TrashIcon,\n} from './TrackControls';\n\nexport interface TrackControlsWithDeleteProps {\n trackIndex: number;\n trackName: string;\n muted: boolean;\n soloed: boolean;\n volume: number;\n pan: number;\n onMuteChange: (muted: boolean) => void;\n onSoloChange: (soloed: boolean) => void;\n onVolumeChange: (volume: number) => void;\n onPanChange: (pan: number) => void;\n onDelete: () => void;\n}\n\nconst HeaderContainer = styled.div`\n display: flex;\n align-items: center;\n gap: 0.25rem;\n padding: 0.5rem 0.5rem 0.25rem 0.5rem;\n`;\n\nconst TrackNameSpan = styled.span`\n flex: 1;\n font-weight: 600;\n font-size: 0.875rem;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n margin: 0 0.25rem;\n`;\n\nconst DeleteIconButton = styled.button`\n display: flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n padding: 0;\n border: none;\n background: transparent;\n color: #999;\n cursor: pointer;\n font-size: 16px;\n line-height: 1;\n border-radius: 3px;\n transition: all 0.2s ease-in-out;\n flex-shrink: 0;\n\n &:hover {\n background: #dc3545;\n color: white;\n }\n\n &:active {\n transform: scale(0.9);\n }\n`;\n\n/**\n * Track controls with delete button\n *\n * @example\n * ```tsx\n * <TrackControlsWithDelete\n * trackIndex={0}\n * trackName=\"Track 1\"\n * muted={false}\n * soloed={false}\n * volume={1.0}\n * pan={0}\n * onMuteChange={(muted) => console.log('mute:', muted)}\n * onSoloChange={(soloed) => console.log('solo:', soloed)}\n * onVolumeChange={(volume) => console.log('volume:', volume)}\n * onPanChange={(pan) => console.log('pan:', pan)}\n * onDelete={() => console.log('delete')}\n * />\n * ```\n */\nexport const TrackControlsWithDelete: React.FC<TrackControlsWithDeleteProps> = ({\n trackName,\n muted,\n soloed,\n volume,\n pan,\n onMuteChange,\n onSoloChange,\n onVolumeChange,\n onPanChange,\n onDelete,\n}) => {\n return (\n <Controls>\n <HeaderContainer>\n <DeleteIconButton onClick={onDelete} title=\"Delete track\">\n <TrashIcon />\n </DeleteIconButton>\n <TrackNameSpan>{trackName}</TrackNameSpan>\n </HeaderContainer>\n <ButtonGroup>\n <Button\n $variant={muted ? 'danger' : 'outline'}\n onClick={() => onMuteChange(!muted)}\n >\n Mute\n </Button>\n <Button\n $variant={soloed ? 'info' : 'outline'}\n onClick={() => onSoloChange(!soloed)}\n >\n Solo\n </Button>\n </ButtonGroup>\n <SliderWrapper>\n <VolumeDownIcon />\n <Slider\n min=\"0\"\n max=\"1\"\n step=\"0.01\"\n value={volume}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => onVolumeChange(parseFloat(e.target.value))}\n />\n <VolumeUpIcon />\n </SliderWrapper>\n <SliderWrapper>\n <span>L</span>\n <Slider\n min=\"-1\"\n max=\"1\"\n step=\"0.01\"\n value={pan}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => onPanChange(parseFloat(e.target.value))}\n />\n <span>R</span>\n </SliderWrapper>\n </Controls>\n );\n};\n"],"mappings":"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","names":["useTheme","styled","import_styled_components","styled","import_styled_components","styled","import_styled_components","styled","import_styled_components","styled","import_styled_components","styled","import_styled_components","styled","import_styled_components","styled","import_jsx_runtime","import_styled_components","import_jsx_runtime","styled","index","import_styled_components","import_styled_components","import_jsx_runtime","styled","import_react","import_styled_components","import_jsx_runtime","styled","React","import_styled_components","import_jsx_runtime","styled","import_jsx_runtime","styled","import_styled_components","import_jsx_runtime","styled","import_react","import_styled_components","import_jsx_runtime","styled","import_styled_components","import_jsx_runtime","Wrapper","styled","import_styled_components","import_jsx_runtime","styled","import_react","import_styled_components","import_jsx_runtime","styled","import_react","import_react","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_react","import_styled_components","useTheme","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_jsx_runtime","useTheme","import_react","import_react","import_styled_components","import_jsx_runtime","formatTime","styled","React","import_jsx_runtime","import_styled_components","import_jsx_runtime","styled","import_styled_components","import_jsx_runtime","styled","import_styled_components","styled","import_styled_components","styled","import_styled_components","styled","import_styled_components","styled","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime","PhosphorTrashIcon","import_styled_components","styled","import_styled_components","styled","import_styled_components","import_jsx_runtime","HeaderContainer","styled"]}