@waveform-playlist/browser 7.1.1 → 7.1.3
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.d.ts +34 -0
- package/dist/index.js +104 -89
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3755 -3058
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -8
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../playout/dist/index.mjs","../../../node_modules/.pnpm/@dnd-kit+utilities@3.2.2_react@18.3.1/node_modules/@dnd-kit/utilities/dist/utilities.esm.js","../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@18.3.1_react@18.3.1/node_modules/@phosphor-icons/react/dist/defs/DotsThree.es.js","../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@18.3.1_react@18.3.1/node_modules/@phosphor-icons/react/dist/defs/SpeakerHigh.es.js","../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@18.3.1_react@18.3.1/node_modules/@phosphor-icons/react/dist/defs/SpeakerLow.es.js","../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@18.3.1_react@18.3.1/node_modules/@phosphor-icons/react/dist/defs/X.es.js","../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@18.3.1_react@18.3.1/node_modules/@phosphor-icons/react/dist/lib/context.es.js","../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@18.3.1_react@18.3.1/node_modules/@phosphor-icons/react/dist/lib/IconBase.es.js","../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@18.3.1_react@18.3.1/node_modules/@phosphor-icons/react/dist/csr/DotsThree.es.js","../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@18.3.1_react@18.3.1/node_modules/@phosphor-icons/react/dist/csr/SpeakerHigh.es.js","../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@18.3.1_react@18.3.1/node_modules/@phosphor-icons/react/dist/csr/SpeakerLow.es.js","../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@18.3.1_react@18.3.1/node_modules/@phosphor-icons/react/dist/csr/X.es.js","../../ui-components/dist/index.mjs","../../../node_modules/.pnpm/waveform-data@4.5.2/node_modules/waveform-data/dist/waveform-data.esm.js","../src/waveformDataLoader.ts","../src/hooks/useTimeFormat.ts","../src/hooks/useZoomControls.ts","../src/hooks/useMasterVolume.ts","../src/hooks/useAudioEffects.ts","../../core/dist/index.mjs","../src/hooks/useAudioTracks.ts","../src/hooks/useClipDragHandlers.ts","../src/hooks/useAnnotationDragHandlers.ts","../src/hooks/useDragSensors.ts","../src/hooks/useClipSplitting.ts","../src/hooks/useKeyboardShortcuts.ts","../src/hooks/usePlaybackShortcuts.ts","../src/hooks/useAnnotationKeyboardControls.ts","../src/effects/effectDefinitions.ts","../src/effects/effectFactory.ts","../src/hooks/useDynamicEffects.ts","../src/hooks/useTrackDynamicEffects.ts","../src/utils/wavEncoder.ts","../src/hooks/useExportWav.ts","../src/hooks/useAnimationFrameLoop.ts","../src/workers/peaksWorker.ts","../src/hooks/useWaveformDataCache.ts","../src/WaveformPlaylistContext.tsx","../../media-element-playout/dist/index.mjs","../src/MediaElementPlaylistContext.tsx","../src/components/PlaybackControls.tsx","../src/components/ZoomControls.tsx","../src/components/ContextualControls.tsx","../src/AnnotationIntegrationContext.tsx","../src/components/AnnotationControls.tsx","../src/components/ExportControls.tsx","../src/components/AnimatedPlayhead.tsx","../src/components/ChannelWithProgress.tsx","../src/SpectrogramIntegrationContext.tsx","../src/components/PlaylistVisualization.tsx","../src/components/PlaylistAnnotationList.tsx","../src/components/Waveform.tsx","../src/components/AnimatedMediaElementPlayhead.tsx","../src/components/ChannelWithMediaElementProgress.tsx","../src/components/MediaElementPlaylist.tsx","../src/components/MediaElementAnnotationList.tsx","../src/components/MediaElementWaveform.tsx"],"sourcesContent":["// src/TonePlayout.ts\nimport {\n Volume as Volume2,\n getDestination as getDestination2,\n start,\n now as now2,\n getTransport,\n getContext\n} from \"tone\";\n\n// src/ToneTrack.ts\nimport {\n Player,\n Volume,\n Gain,\n Panner,\n getDestination,\n now\n} from \"tone\";\n\n// src/fades.ts\nvar hasWarned = false;\nfunction getUnderlyingAudioParam(signal) {\n const param = signal._param;\n if (!param && !hasWarned) {\n hasWarned = true;\n console.warn(\n \"[waveform-playlist] Unable to access Tone.js internal _param. This likely means the Tone.js version is incompatible. Fades and mute scheduling may not work correctly.\"\n );\n }\n return param;\n}\nfunction linearCurve(length, fadeIn) {\n const curve = new Float32Array(length);\n const scale = length - 1;\n for (let i = 0; i < length; i++) {\n const x = i / scale;\n curve[i] = fadeIn ? x : 1 - x;\n }\n return curve;\n}\nfunction exponentialCurve(length, fadeIn) {\n const curve = new Float32Array(length);\n const scale = length - 1;\n for (let i = 0; i < length; i++) {\n const x = i / scale;\n const index = fadeIn ? i : length - 1 - i;\n curve[index] = Math.exp(2 * x - 1) / Math.E;\n }\n return curve;\n}\nfunction sCurveCurve(length, fadeIn) {\n const curve = new Float32Array(length);\n const phase = fadeIn ? Math.PI / 2 : -Math.PI / 2;\n for (let i = 0; i < length; i++) {\n curve[i] = Math.sin(Math.PI * i / length - phase) / 2 + 0.5;\n }\n return curve;\n}\nfunction logarithmicCurve(length, fadeIn, base = 10) {\n const curve = new Float32Array(length);\n for (let i = 0; i < length; i++) {\n const index = fadeIn ? i : length - 1 - i;\n const x = i / length;\n curve[index] = Math.log(1 + base * x) / Math.log(1 + base);\n }\n return curve;\n}\nfunction generateCurve(type, length, fadeIn) {\n switch (type) {\n case \"linear\":\n return linearCurve(length, fadeIn);\n case \"exponential\":\n return exponentialCurve(length, fadeIn);\n case \"sCurve\":\n return sCurveCurve(length, fadeIn);\n case \"logarithmic\":\n return logarithmicCurve(length, fadeIn);\n default:\n return linearCurve(length, fadeIn);\n }\n}\nfunction applyFadeIn(param, startTime, duration, type = \"linear\", startValue = 0, endValue = 1) {\n if (duration <= 0) return;\n if (type === \"linear\") {\n param.setValueAtTime(startValue, startTime);\n param.linearRampToValueAtTime(endValue, startTime + duration);\n } else if (type === \"exponential\") {\n param.setValueAtTime(Math.max(startValue, 1e-3), startTime);\n param.exponentialRampToValueAtTime(Math.max(endValue, 1e-3), startTime + duration);\n } else {\n const curve = generateCurve(type, 1e4, true);\n const scaledCurve = new Float32Array(curve.length);\n const range = endValue - startValue;\n for (let i = 0; i < curve.length; i++) {\n scaledCurve[i] = startValue + curve[i] * range;\n }\n param.setValueCurveAtTime(scaledCurve, startTime, duration);\n }\n}\nfunction applyFadeOut(param, startTime, duration, type = \"linear\", startValue = 1, endValue = 0) {\n if (duration <= 0) return;\n if (type === \"linear\") {\n param.setValueAtTime(startValue, startTime);\n param.linearRampToValueAtTime(endValue, startTime + duration);\n } else if (type === \"exponential\") {\n param.setValueAtTime(Math.max(startValue, 1e-3), startTime);\n param.exponentialRampToValueAtTime(Math.max(endValue, 1e-3), startTime + duration);\n } else {\n const curve = generateCurve(type, 1e4, false);\n const scaledCurve = new Float32Array(curve.length);\n const range = startValue - endValue;\n for (let i = 0; i < curve.length; i++) {\n scaledCurve[i] = endValue + curve[i] * range;\n }\n param.setValueCurveAtTime(scaledCurve, startTime, duration);\n }\n}\n\n// src/ToneTrack.ts\nvar ToneTrack = class {\n // Count of currently playing clips\n constructor(options) {\n this.activePlayers = 0;\n this.track = options.track;\n this.volumeNode = new Volume(this.gainToDb(options.track.gain));\n this.panNode = new Panner(options.track.stereoPan);\n this.muteGain = new Gain(options.track.muted ? 0 : 1);\n const destination = options.destination || getDestination();\n if (options.effects) {\n const cleanup = options.effects(this.muteGain, destination, false);\n if (cleanup) {\n this.effectsCleanup = cleanup;\n }\n } else {\n this.muteGain.connect(destination);\n }\n const clipInfos = options.clips || (options.buffer ? [{\n buffer: options.buffer,\n startTime: 0,\n // Legacy: single buffer starts at timeline position 0\n duration: options.buffer.duration,\n // Legacy: play full buffer duration\n offset: 0,\n fadeIn: options.track.fadeIn,\n fadeOut: options.track.fadeOut,\n gain: 1\n }] : []);\n this.clips = clipInfos.map((clipInfo) => {\n const player = new Player({\n url: clipInfo.buffer,\n loop: false,\n onstop: () => {\n this.activePlayers--;\n if (this.activePlayers === 0 && this.onStopCallback) {\n this.onStopCallback();\n }\n }\n });\n const fadeGain = new Gain(clipInfo.gain);\n player.connect(fadeGain);\n fadeGain.chain(this.volumeNode, this.panNode, this.muteGain);\n return {\n player,\n clipInfo,\n fadeGain,\n pausedPosition: 0,\n playStartTime: 0\n };\n });\n }\n /**\n * Schedule fade envelopes for a clip at the given start time\n */\n scheduleFades(clipPlayer, clipStartTime, clipOffset = 0) {\n const { clipInfo, fadeGain } = clipPlayer;\n const audioParam = getUnderlyingAudioParam(fadeGain.gain);\n if (!audioParam) return;\n audioParam.cancelScheduledValues(0);\n const skipTime = clipOffset - clipInfo.offset;\n if (clipInfo.fadeIn && skipTime < clipInfo.fadeIn.duration) {\n const fadeInDuration = clipInfo.fadeIn.duration;\n if (skipTime <= 0) {\n applyFadeIn(\n audioParam,\n clipStartTime,\n fadeInDuration,\n clipInfo.fadeIn.type || \"linear\",\n 0,\n clipInfo.gain\n );\n } else {\n const remainingFadeDuration = fadeInDuration - skipTime;\n const fadeProgress = skipTime / fadeInDuration;\n const startValue = clipInfo.gain * fadeProgress;\n applyFadeIn(\n audioParam,\n clipStartTime,\n remainingFadeDuration,\n clipInfo.fadeIn.type || \"linear\",\n startValue,\n clipInfo.gain\n );\n }\n } else {\n audioParam.setValueAtTime(clipInfo.gain, clipStartTime);\n }\n if (clipInfo.fadeOut) {\n const fadeOutStart = clipInfo.duration - clipInfo.fadeOut.duration;\n const fadeOutStartInClip = fadeOutStart - skipTime;\n if (fadeOutStartInClip > 0) {\n const absoluteFadeOutStart = clipStartTime + fadeOutStartInClip;\n applyFadeOut(\n audioParam,\n absoluteFadeOutStart,\n clipInfo.fadeOut.duration,\n clipInfo.fadeOut.type || \"linear\",\n clipInfo.gain,\n 0\n );\n } else if (fadeOutStartInClip > -clipInfo.fadeOut.duration) {\n const elapsedFadeOut = -fadeOutStartInClip;\n const remainingFadeDuration = clipInfo.fadeOut.duration - elapsedFadeOut;\n const fadeProgress = elapsedFadeOut / clipInfo.fadeOut.duration;\n const startValue = clipInfo.gain * (1 - fadeProgress);\n applyFadeOut(\n audioParam,\n clipStartTime,\n remainingFadeDuration,\n clipInfo.fadeOut.type || \"linear\",\n startValue,\n 0\n );\n }\n }\n }\n gainToDb(gain) {\n return 20 * Math.log10(gain);\n }\n setVolume(gain) {\n this.track.gain = gain;\n this.volumeNode.volume.value = this.gainToDb(gain);\n }\n setPan(pan) {\n this.track.stereoPan = pan;\n this.panNode.pan.value = pan;\n }\n setMute(muted) {\n this.track.muted = muted;\n const value = muted ? 0 : 1;\n const audioParam = getUnderlyingAudioParam(this.muteGain.gain);\n audioParam?.setValueAtTime(value, 0);\n this.muteGain.gain.value = value;\n }\n setSolo(soloed) {\n this.track.soloed = soloed;\n }\n play(when, offset = 0, duration) {\n this.clips.forEach((clipPlayer) => {\n clipPlayer.player.stop();\n clipPlayer.player.disconnect();\n clipPlayer.player.dispose();\n const newPlayer = new Player({\n url: clipPlayer.clipInfo.buffer,\n loop: false,\n onstop: () => {\n this.activePlayers--;\n if (this.activePlayers === 0 && this.onStopCallback) {\n this.onStopCallback();\n }\n }\n });\n newPlayer.connect(clipPlayer.fadeGain);\n clipPlayer.player = newPlayer;\n clipPlayer.pausedPosition = 0;\n });\n this.activePlayers = 0;\n this.clips.forEach((clipPlayer) => {\n const { player, clipInfo } = clipPlayer;\n const playbackPosition = offset;\n const clipStart = clipInfo.startTime;\n const clipEnd = clipInfo.startTime + clipInfo.duration;\n if (playbackPosition < clipEnd) {\n this.activePlayers++;\n const currentTime = when ?? now();\n clipPlayer.playStartTime = currentTime;\n if (playbackPosition >= clipStart) {\n const clipOffset = playbackPosition - clipStart + clipInfo.offset;\n const remainingDuration = clipInfo.duration - (playbackPosition - clipStart);\n const clipDuration = duration ? Math.min(duration, remainingDuration) : remainingDuration;\n clipPlayer.pausedPosition = clipOffset;\n this.scheduleFades(clipPlayer, currentTime, clipOffset);\n player.start(currentTime, clipOffset, clipDuration);\n } else {\n const delay = clipStart - playbackPosition;\n const clipDuration = duration ? Math.min(duration - delay, clipInfo.duration) : clipInfo.duration;\n if (delay < (duration ?? Infinity)) {\n clipPlayer.pausedPosition = clipInfo.offset;\n this.scheduleFades(clipPlayer, currentTime + delay, clipInfo.offset);\n player.start(currentTime + delay, clipInfo.offset, clipDuration);\n } else {\n this.activePlayers--;\n }\n }\n }\n });\n }\n pause() {\n this.clips.forEach((clipPlayer) => {\n if (clipPlayer.player.state === \"started\") {\n const elapsed = (now() - clipPlayer.playStartTime) * clipPlayer.player.playbackRate;\n clipPlayer.pausedPosition = clipPlayer.pausedPosition + elapsed;\n }\n clipPlayer.player.stop();\n });\n this.activePlayers = 0;\n }\n stop(when) {\n const stopWhen = when ?? now();\n this.clips.forEach((clipPlayer) => {\n clipPlayer.player.stop(stopWhen);\n clipPlayer.pausedPosition = 0;\n });\n this.activePlayers = 0;\n }\n dispose() {\n if (this.effectsCleanup) {\n this.effectsCleanup();\n }\n this.clips.forEach((clipPlayer) => {\n clipPlayer.player.dispose();\n clipPlayer.fadeGain.dispose();\n });\n this.volumeNode.dispose();\n this.panNode.dispose();\n this.muteGain.dispose();\n }\n get id() {\n return this.track.id;\n }\n get duration() {\n if (this.clips.length === 0) return 0;\n const lastClip = this.clips[this.clips.length - 1];\n return lastClip.clipInfo.startTime + lastClip.clipInfo.duration;\n }\n get buffer() {\n return this.clips[0]?.clipInfo.buffer;\n }\n get isPlaying() {\n return this.clips.some((clipPlayer) => clipPlayer.player.state === \"started\");\n }\n get muted() {\n return this.track.muted;\n }\n get startTime() {\n return this.track.startTime;\n }\n setOnStopCallback(callback) {\n this.onStopCallback = callback;\n }\n};\n\n// src/TonePlayout.ts\nvar TonePlayout = class {\n constructor(options = {}) {\n this.tracks = /* @__PURE__ */ new Map();\n this.isInitialized = false;\n this.soloedTracks = /* @__PURE__ */ new Set();\n this.manualMuteState = /* @__PURE__ */ new Map();\n this.activeTracks = /* @__PURE__ */ new Map();\n // Map track ID to session ID\n this.playbackSessionId = 0;\n this.masterVolume = new Volume2(this.gainToDb(options.masterGain ?? 1));\n if (options.effects) {\n const cleanup = options.effects(this.masterVolume, getDestination2(), false);\n if (cleanup) {\n this.effectsCleanup = cleanup;\n }\n } else {\n this.masterVolume.toDestination();\n }\n if (options.tracks) {\n options.tracks.forEach((track) => {\n this.tracks.set(track.id, track);\n this.manualMuteState.set(track.id, track.muted);\n });\n }\n }\n gainToDb(gain) {\n return 20 * Math.log10(gain);\n }\n async init() {\n if (this.isInitialized) return;\n await start();\n this.isInitialized = true;\n }\n addTrack(trackOptions) {\n const optionsWithDestination = {\n ...trackOptions,\n destination: this.masterVolume\n };\n const toneTrack = new ToneTrack(optionsWithDestination);\n this.tracks.set(toneTrack.id, toneTrack);\n this.manualMuteState.set(toneTrack.id, trackOptions.track.muted ?? false);\n if (trackOptions.track.soloed) {\n this.soloedTracks.add(toneTrack.id);\n }\n return toneTrack;\n }\n /**\n * Apply solo muting after all tracks have been added.\n * Call this after adding all tracks to ensure solo logic is applied correctly.\n */\n applyInitialSoloState() {\n this.updateSoloMuting();\n }\n removeTrack(trackId) {\n const track = this.tracks.get(trackId);\n if (track) {\n track.dispose();\n this.tracks.delete(trackId);\n this.manualMuteState.delete(trackId);\n this.soloedTracks.delete(trackId);\n }\n }\n getTrack(trackId) {\n return this.tracks.get(trackId);\n }\n play(when, offset, duration) {\n if (!this.isInitialized) {\n console.warn(\"TonePlayout not initialized. Call init() first.\");\n return;\n }\n const startTime = when ?? now2();\n const playbackPosition = offset ?? 0;\n this.playbackSessionId++;\n const currentSessionId = this.playbackSessionId;\n this.activeTracks.clear();\n this.tracks.forEach((toneTrack) => {\n const trackStartTime = toneTrack.startTime;\n if (playbackPosition >= trackStartTime) {\n const bufferOffset = playbackPosition - trackStartTime;\n if (duration !== void 0) {\n this.activeTracks.set(toneTrack.id, currentSessionId);\n toneTrack.setOnStopCallback(() => {\n if (this.activeTracks.get(toneTrack.id) === currentSessionId) {\n this.activeTracks.delete(toneTrack.id);\n if (this.activeTracks.size === 0 && this.onPlaybackCompleteCallback) {\n this.onPlaybackCompleteCallback();\n }\n }\n });\n }\n toneTrack.play(startTime, bufferOffset, duration);\n } else {\n const delay = trackStartTime - playbackPosition;\n if (duration !== void 0) {\n this.activeTracks.set(toneTrack.id, currentSessionId);\n toneTrack.setOnStopCallback(() => {\n if (this.activeTracks.get(toneTrack.id) === currentSessionId) {\n this.activeTracks.delete(toneTrack.id);\n if (this.activeTracks.size === 0 && this.onPlaybackCompleteCallback) {\n this.onPlaybackCompleteCallback();\n }\n }\n });\n }\n toneTrack.play(startTime + delay, 0, duration);\n }\n });\n if (offset !== void 0) {\n getTransport().start(startTime, offset);\n } else {\n getTransport().start(startTime);\n }\n }\n pause() {\n getTransport().pause();\n this.tracks.forEach((track) => {\n track.pause();\n });\n }\n stop() {\n getTransport().stop();\n this.tracks.forEach((track) => {\n track.stop();\n });\n }\n setMasterGain(gain) {\n this.masterVolume.volume.value = this.gainToDb(gain);\n }\n setSolo(trackId, soloed) {\n const track = this.tracks.get(trackId);\n if (track) {\n track.setSolo(soloed);\n if (soloed) {\n this.soloedTracks.add(trackId);\n } else {\n this.soloedTracks.delete(trackId);\n }\n this.updateSoloMuting();\n }\n }\n updateSoloMuting() {\n const hasSoloedTracks = this.soloedTracks.size > 0;\n this.tracks.forEach((track, id) => {\n if (hasSoloedTracks) {\n if (!this.soloedTracks.has(id)) {\n track.setMute(true);\n } else {\n const manuallyMuted = this.manualMuteState.get(id) ?? false;\n track.setMute(manuallyMuted);\n }\n } else {\n const manuallyMuted = this.manualMuteState.get(id) ?? false;\n track.setMute(manuallyMuted);\n }\n });\n }\n setMute(trackId, muted) {\n const track = this.tracks.get(trackId);\n if (track) {\n this.manualMuteState.set(trackId, muted);\n track.setMute(muted);\n }\n }\n getCurrentTime() {\n return getTransport().seconds;\n }\n seekTo(time) {\n getTransport().seconds = time;\n }\n dispose() {\n this.tracks.forEach((track) => {\n track.dispose();\n });\n this.tracks.clear();\n if (this.effectsCleanup) {\n this.effectsCleanup();\n }\n this.masterVolume.dispose();\n }\n get context() {\n return getContext();\n }\n get sampleRate() {\n return getContext().sampleRate;\n }\n setOnPlaybackComplete(callback) {\n this.onPlaybackCompleteCallback = callback;\n }\n};\n\n// src/audioContext.ts\nimport { Context, setContext } from \"tone\";\nvar globalToneContext = null;\nfunction getGlobalContext() {\n if (!globalToneContext) {\n globalToneContext = new Context();\n setContext(globalToneContext);\n }\n return globalToneContext;\n}\nfunction getGlobalAudioContext() {\n return getGlobalContext().rawContext;\n}\nfunction getGlobalToneContext() {\n return getGlobalContext();\n}\nasync function resumeGlobalAudioContext() {\n const context = getGlobalContext();\n if (context.state !== \"running\") {\n await context.resume();\n }\n}\nfunction getGlobalAudioContextState() {\n return globalToneContext?.rawContext.state || \"suspended\";\n}\nasync function closeGlobalAudioContext() {\n if (globalToneContext && globalToneContext.rawContext.state !== \"closed\") {\n await globalToneContext.close();\n globalToneContext = null;\n }\n}\n\n// src/mediaStreamSourceManager.ts\nimport { getContext as getContext2 } from \"tone\";\nvar streamSources = /* @__PURE__ */ new Map();\nvar streamCleanupHandlers = /* @__PURE__ */ new Map();\nfunction getMediaStreamSource(stream) {\n if (streamSources.has(stream)) {\n return streamSources.get(stream);\n }\n const context = getContext2();\n const source = context.createMediaStreamSource(stream);\n streamSources.set(stream, source);\n const cleanup = () => {\n source.disconnect();\n streamSources.delete(stream);\n streamCleanupHandlers.delete(stream);\n stream.removeEventListener(\"ended\", cleanup);\n stream.removeEventListener(\"inactive\", cleanup);\n };\n streamCleanupHandlers.set(stream, cleanup);\n stream.addEventListener(\"ended\", cleanup);\n stream.addEventListener(\"inactive\", cleanup);\n return source;\n}\nfunction releaseMediaStreamSource(stream) {\n const cleanup = streamCleanupHandlers.get(stream);\n if (cleanup) {\n cleanup();\n }\n}\nfunction hasMediaStreamSource(stream) {\n return streamSources.has(stream);\n}\nexport {\n TonePlayout,\n ToneTrack,\n applyFadeIn,\n applyFadeOut,\n closeGlobalAudioContext,\n getGlobalAudioContext,\n getGlobalAudioContextState,\n getGlobalContext,\n getGlobalToneContext,\n getMediaStreamSource,\n getUnderlyingAudioParam,\n hasMediaStreamSource,\n releaseMediaStreamSource,\n resumeGlobalAudioContext\n};\n//# sourceMappingURL=index.mjs.map","import { useMemo, useLayoutEffect, useEffect, useRef, useCallback } from 'react';\n\nfunction useCombinedRefs() {\n for (var _len = arguments.length, refs = new Array(_len), _key = 0; _key < _len; _key++) {\n refs[_key] = arguments[_key];\n }\n\n return useMemo(() => node => {\n refs.forEach(ref => ref(node));\n }, // eslint-disable-next-line react-hooks/exhaustive-deps\n refs);\n}\n\n// https://github.com/facebook/react/blob/master/packages/shared/ExecutionEnvironment.js\nconst canUseDOM = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';\n\nfunction isWindow(element) {\n const elementString = Object.prototype.toString.call(element);\n return elementString === '[object Window]' || // In Electron context the Window object serializes to [object global]\n elementString === '[object global]';\n}\n\nfunction isNode(node) {\n return 'nodeType' in node;\n}\n\nfunction getWindow(target) {\n var _target$ownerDocument, _target$ownerDocument2;\n\n if (!target) {\n return window;\n }\n\n if (isWindow(target)) {\n return target;\n }\n\n if (!isNode(target)) {\n return window;\n }\n\n return (_target$ownerDocument = (_target$ownerDocument2 = target.ownerDocument) == null ? void 0 : _target$ownerDocument2.defaultView) != null ? _target$ownerDocument : window;\n}\n\nfunction isDocument(node) {\n const {\n Document\n } = getWindow(node);\n return node instanceof Document;\n}\n\nfunction isHTMLElement(node) {\n if (isWindow(node)) {\n return false;\n }\n\n return node instanceof getWindow(node).HTMLElement;\n}\n\nfunction isSVGElement(node) {\n return node instanceof getWindow(node).SVGElement;\n}\n\nfunction getOwnerDocument(target) {\n if (!target) {\n return document;\n }\n\n if (isWindow(target)) {\n return target.document;\n }\n\n if (!isNode(target)) {\n return document;\n }\n\n if (isDocument(target)) {\n return target;\n }\n\n if (isHTMLElement(target) || isSVGElement(target)) {\n return target.ownerDocument;\n }\n\n return document;\n}\n\n/**\r\n * A hook that resolves to useEffect on the server and useLayoutEffect on the client\r\n * @param callback {function} Callback function that is invoked when the dependencies of the hook change\r\n */\n\nconst useIsomorphicLayoutEffect = canUseDOM ? useLayoutEffect : useEffect;\n\nfunction useEvent(handler) {\n const handlerRef = useRef(handler);\n useIsomorphicLayoutEffect(() => {\n handlerRef.current = handler;\n });\n return useCallback(function () {\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n return handlerRef.current == null ? void 0 : handlerRef.current(...args);\n }, []);\n}\n\nfunction useInterval() {\n const intervalRef = useRef(null);\n const set = useCallback((listener, duration) => {\n intervalRef.current = setInterval(listener, duration);\n }, []);\n const clear = useCallback(() => {\n if (intervalRef.current !== null) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n }, []);\n return [set, clear];\n}\n\nfunction useLatestValue(value, dependencies) {\n if (dependencies === void 0) {\n dependencies = [value];\n }\n\n const valueRef = useRef(value);\n useIsomorphicLayoutEffect(() => {\n if (valueRef.current !== value) {\n valueRef.current = value;\n }\n }, dependencies);\n return valueRef;\n}\n\nfunction useLazyMemo(callback, dependencies) {\n const valueRef = useRef();\n return useMemo(() => {\n const newValue = callback(valueRef.current);\n valueRef.current = newValue;\n return newValue;\n }, // eslint-disable-next-line react-hooks/exhaustive-deps\n [...dependencies]);\n}\n\nfunction useNodeRef(onChange) {\n const onChangeHandler = useEvent(onChange);\n const node = useRef(null);\n const setNodeRef = useCallback(element => {\n if (element !== node.current) {\n onChangeHandler == null ? void 0 : onChangeHandler(element, node.current);\n }\n\n node.current = element;\n }, //eslint-disable-next-line\n []);\n return [node, setNodeRef];\n}\n\nfunction usePrevious(value) {\n const ref = useRef();\n useEffect(() => {\n ref.current = value;\n }, [value]);\n return ref.current;\n}\n\nlet ids = {};\nfunction useUniqueId(prefix, value) {\n return useMemo(() => {\n if (value) {\n return value;\n }\n\n const id = ids[prefix] == null ? 0 : ids[prefix] + 1;\n ids[prefix] = id;\n return prefix + \"-\" + id;\n }, [prefix, value]);\n}\n\nfunction createAdjustmentFn(modifier) {\n return function (object) {\n for (var _len = arguments.length, adjustments = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n adjustments[_key - 1] = arguments[_key];\n }\n\n return adjustments.reduce((accumulator, adjustment) => {\n const entries = Object.entries(adjustment);\n\n for (const [key, valueAdjustment] of entries) {\n const value = accumulator[key];\n\n if (value != null) {\n accumulator[key] = value + modifier * valueAdjustment;\n }\n }\n\n return accumulator;\n }, { ...object\n });\n };\n}\n\nconst add = /*#__PURE__*/createAdjustmentFn(1);\nconst subtract = /*#__PURE__*/createAdjustmentFn(-1);\n\nfunction hasViewportRelativeCoordinates(event) {\n return 'clientX' in event && 'clientY' in event;\n}\n\nfunction isKeyboardEvent(event) {\n if (!event) {\n return false;\n }\n\n const {\n KeyboardEvent\n } = getWindow(event.target);\n return KeyboardEvent && event instanceof KeyboardEvent;\n}\n\nfunction isTouchEvent(event) {\n if (!event) {\n return false;\n }\n\n const {\n TouchEvent\n } = getWindow(event.target);\n return TouchEvent && event instanceof TouchEvent;\n}\n\n/**\r\n * Returns the normalized x and y coordinates for mouse and touch events.\r\n */\n\nfunction getEventCoordinates(event) {\n if (isTouchEvent(event)) {\n if (event.touches && event.touches.length) {\n const {\n clientX: x,\n clientY: y\n } = event.touches[0];\n return {\n x,\n y\n };\n } else if (event.changedTouches && event.changedTouches.length) {\n const {\n clientX: x,\n clientY: y\n } = event.changedTouches[0];\n return {\n x,\n y\n };\n }\n }\n\n if (hasViewportRelativeCoordinates(event)) {\n return {\n x: event.clientX,\n y: event.clientY\n };\n }\n\n return null;\n}\n\nconst CSS = /*#__PURE__*/Object.freeze({\n Translate: {\n toString(transform) {\n if (!transform) {\n return;\n }\n\n const {\n x,\n y\n } = transform;\n return \"translate3d(\" + (x ? Math.round(x) : 0) + \"px, \" + (y ? Math.round(y) : 0) + \"px, 0)\";\n }\n\n },\n Scale: {\n toString(transform) {\n if (!transform) {\n return;\n }\n\n const {\n scaleX,\n scaleY\n } = transform;\n return \"scaleX(\" + scaleX + \") scaleY(\" + scaleY + \")\";\n }\n\n },\n Transform: {\n toString(transform) {\n if (!transform) {\n return;\n }\n\n return [CSS.Translate.toString(transform), CSS.Scale.toString(transform)].join(' ');\n }\n\n },\n Transition: {\n toString(_ref) {\n let {\n property,\n duration,\n easing\n } = _ref;\n return property + \" \" + duration + \"ms \" + easing;\n }\n\n }\n});\n\nconst SELECTOR = 'a,frame,iframe,input:not([type=hidden]):not(:disabled),select:not(:disabled),textarea:not(:disabled),button:not(:disabled),*[tabindex]';\nfunction findFirstFocusableNode(element) {\n if (element.matches(SELECTOR)) {\n return element;\n }\n\n return element.querySelector(SELECTOR);\n}\n\nexport { CSS, add, canUseDOM, findFirstFocusableNode, getEventCoordinates, getOwnerDocument, getWindow, hasViewportRelativeCoordinates, isDocument, isHTMLElement, isKeyboardEvent, isNode, isSVGElement, isTouchEvent, isWindow, subtract, useCombinedRefs, useEvent, useInterval, useIsomorphicLayoutEffect, useLatestValue, useLazyMemo, useNodeRef, usePrevious, useUniqueId };\n//# sourceMappingURL=utilities.esm.js.map\n","import * as e from \"react\";\nconst a = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M144,128a16,16,0,1,1-16-16A16,16,0,0,1,144,128ZM60,112a16,16,0,1,0,16,16A16,16,0,0,0,60,112Zm136,0a16,16,0,1,0,16,16A16,16,0,0,0,196,112Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\n \"path\",\n {\n d: \"M240,96v64a16,16,0,0,1-16,16H32a16,16,0,0,1-16-16V96A16,16,0,0,1,32,80H224A16,16,0,0,1,240,96Z\",\n opacity: \"0.2\"\n }\n ), /* @__PURE__ */ e.createElement(\"path\", { d: \"M140,128a12,12,0,1,1-12-12A12,12,0,0,1,140,128Zm56-12a12,12,0,1,0,12,12A12,12,0,0,0,196,116ZM60,116a12,12,0,1,0,12,12A12,12,0,0,0,60,116Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M224,80H32A16,16,0,0,0,16,96v64a16,16,0,0,0,16,16H224a16,16,0,0,0,16-16V96A16,16,0,0,0,224,80ZM60,140a12,12,0,1,1,12-12A12,12,0,0,1,60,140Zm68,0a12,12,0,1,1,12-12A12,12,0,0,1,128,140Zm68,0a12,12,0,1,1,12-12A12,12,0,0,1,196,140Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M138,128a10,10,0,1,1-10-10A10,10,0,0,1,138,128ZM60,118a10,10,0,1,0,10,10A10,10,0,0,0,60,118Zm136,0a10,10,0,1,0,10,10A10,10,0,0,0,196,118Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M140,128a12,12,0,1,1-12-12A12,12,0,0,1,140,128Zm56-12a12,12,0,1,0,12,12A12,12,0,0,0,196,116ZM60,116a12,12,0,1,0,12,12A12,12,0,0,0,60,116Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M136,128a8,8,0,1,1-8-8A8,8,0,0,1,136,128Zm-76-8a8,8,0,1,0,8,8A8,8,0,0,0,60,120Zm136,0a8,8,0,1,0,8,8A8,8,0,0,0,196,120Z\" }))\n ]\n]);\nexport {\n a as default\n};\n","import * as a from \"react\";\nconst e = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M157.27,21.22a12,12,0,0,0-12.64,1.31L75.88,76H32A20,20,0,0,0,12,96v64a20,20,0,0,0,20,20H75.88l68.75,53.47A12,12,0,0,0,164,224V32A12,12,0,0,0,157.27,21.22ZM36,100H68v56H36Zm104,99.46L92,162.13V93.87l48-37.33ZM212,128a44,44,0,0,1-11,29.11,12,12,0,1,1-18-15.88,20,20,0,0,0,0-26.43,12,12,0,0,1,18-15.86A43.94,43.94,0,0,1,212,128Zm40,0a83.87,83.87,0,0,1-21.39,56,12,12,0,0,1-17.89-16,60,60,0,0,0,0-80,12,12,0,1,1,17.88-16A83.87,83.87,0,0,1,252,128Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M80,88v80H32a8,8,0,0,1-8-8V96a8,8,0,0,1,8-8Z\", opacity: \"0.2\" }), /* @__PURE__ */ a.createElement(\"path\", { d: \"M155.51,24.81a8,8,0,0,0-8.42.88L77.25,80H32A16,16,0,0,0,16,96v64a16,16,0,0,0,16,16H77.25l69.84,54.31A8,8,0,0,0,160,224V32A8,8,0,0,0,155.51,24.81ZM32,96H72v64H32ZM144,207.64,88,164.09V91.91l56-43.55Zm54-106.08a40,40,0,0,1,0,52.88,8,8,0,0,1-12-10.58,24,24,0,0,0,0-31.72,8,8,0,0,1,12-10.58ZM248,128a79.9,79.9,0,0,1-20.37,53.34,8,8,0,0,1-11.92-10.67,64,64,0,0,0,0-85.33,8,8,0,1,1,11.92-10.67A79.83,79.83,0,0,1,248,128Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M160,32.25V223.69a8.29,8.29,0,0,1-3.91,7.18,8,8,0,0,1-9-.56l-65.57-51A4,4,0,0,1,80,176.16V79.84a4,4,0,0,1,1.55-3.15l65.57-51a8,8,0,0,1,10,.16A8.27,8.27,0,0,1,160,32.25ZM60,80H32A16,16,0,0,0,16,96v64a16,16,0,0,0,16,16H60a4,4,0,0,0,4-4V84A4,4,0,0,0,60,80Zm126.77,20.84a8,8,0,0,0-.72,11.3,24,24,0,0,1,0,31.72,8,8,0,1,0,12,10.58,40,40,0,0,0,0-52.88A8,8,0,0,0,186.74,100.84Zm40.89-26.17a8,8,0,1,0-11.92,10.66,64,64,0,0,1,0,85.34,8,8,0,1,0,11.92,10.66,80,80,0,0,0,0-106.66Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M154.64,26.61a6,6,0,0,0-6.32.65L77.94,82H32A14,14,0,0,0,18,96v64a14,14,0,0,0,14,14H77.94l70.38,54.74A6,6,0,0,0,158,224V32A6,6,0,0,0,154.64,26.61ZM30,160V96a2,2,0,0,1,2-2H74v68H32A2,2,0,0,1,30,160Zm116,51.73L86,165.07V90.93l60-46.66Zm50.53-108.85a38,38,0,0,1,0,50.24,6,6,0,1,1-9-7.94,26,26,0,0,0,0-34.37,6,6,0,0,1,9-7.93ZM246,128a77.86,77.86,0,0,1-19.86,52,6,6,0,1,1-8.94-8,66,66,0,0,0,0-88,6,6,0,1,1,8.94-8A77.86,77.86,0,0,1,246,128Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M155.51,24.81a8,8,0,0,0-8.42.88L77.25,80H32A16,16,0,0,0,16,96v64a16,16,0,0,0,16,16H77.25l69.84,54.31A8,8,0,0,0,160,224V32A8,8,0,0,0,155.51,24.81ZM32,96H72v64H32ZM144,207.64,88,164.09V91.91l56-43.55Zm54-106.08a40,40,0,0,1,0,52.88,8,8,0,0,1-12-10.58,24,24,0,0,0,0-31.72,8,8,0,0,1,12-10.58ZM248,128a79.9,79.9,0,0,1-20.37,53.34,8,8,0,0,1-11.92-10.67,64,64,0,0,0,0-85.33,8,8,0,1,1,11.92-10.67A79.83,79.83,0,0,1,248,128Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M153.76,28.41a4,4,0,0,0-4.22.43L78.63,84H32A12,12,0,0,0,20,96v64a12,12,0,0,0,12,12H78.63l70.91,55.16A4.07,4.07,0,0,0,152,228a3.92,3.92,0,0,0,1.76-.41A4,4,0,0,0,156,224V32A4,4,0,0,0,153.76,28.41ZM28,160V96a4,4,0,0,1,4-4H76v72H32A4,4,0,0,1,28,160Zm120,55.82L84,166V90l64-49.78Zm47-111.61a36,36,0,0,1,0,47.59,4,4,0,1,1-6-5.3,28,28,0,0,0,0-37,4,4,0,0,1,6-5.28ZM244,128a75.88,75.88,0,0,1-19.35,50.67,4,4,0,0,1-6-5.34,68,68,0,0,0,0-90.66,4,4,0,0,1,6-5.34A75.88,75.88,0,0,1,244,128Z\" }))\n ]\n]);\nexport {\n e as default\n};\n","import * as e from \"react\";\nconst a = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M157.27,21.22a12,12,0,0,0-12.64,1.31L75.88,76H32A20,20,0,0,0,12,96v64a20,20,0,0,0,20,20H75.88l68.75,53.47A12,12,0,0,0,164,224V32A12,12,0,0,0,157.27,21.22ZM36,100H68v56H36Zm104,99.46L92,162.13V93.87l48-37.33ZM212,128a44,44,0,0,1-11,29.11,12,12,0,0,1-18-15.88,20,20,0,0,0,0-26.44,12,12,0,0,1,18-15.85A43.94,43.94,0,0,1,212,128Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M80,88v80H32a8,8,0,0,1-8-8V96a8,8,0,0,1,8-8Z\", opacity: \"0.2\" }), /* @__PURE__ */ e.createElement(\"path\", { d: \"M155.51,24.81a8,8,0,0,0-8.42.88L77.25,80H32A16,16,0,0,0,16,96v64a16,16,0,0,0,16,16H77.25l69.84,54.31A8,8,0,0,0,160,224V32A8,8,0,0,0,155.51,24.81ZM32,96H72v64H32ZM144,207.64,88,164.09V91.91l56-43.55ZM208,128a39.93,39.93,0,0,1-10,26.46,8,8,0,0,1-12-10.58,24,24,0,0,0,0-31.72,8,8,0,1,1,12-10.58A40,40,0,0,1,208,128Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M160,32.25V223.69a8.29,8.29,0,0,1-3.91,7.18,8,8,0,0,1-9-.56l-65.57-51A4,4,0,0,1,80,176.16V79.84a4,4,0,0,1,1.55-3.15l65.57-51a8,8,0,0,1,10,.16A8.27,8.27,0,0,1,160,32.25ZM60,80H32A16,16,0,0,0,16,96v64a16,16,0,0,0,16,16H60a4,4,0,0,0,4-4V84A4,4,0,0,0,60,80ZM198,101.56a8,8,0,1,0-12,10.58,24,24,0,0,1,0,31.72,8,8,0,1,0,12,10.58,40,40,0,0,0,0-52.88Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M154.64,26.61a6,6,0,0,0-6.32.65L77.94,82H32A14,14,0,0,0,18,96v64a14,14,0,0,0,14,14H77.94l70.38,54.74A6,6,0,0,0,158,224V32A6,6,0,0,0,154.64,26.61ZM30,160V96a2,2,0,0,1,2-2H74v68H32A2,2,0,0,1,30,160Zm116,51.73L86,165.07V90.93l60-46.66ZM206,128a37.94,37.94,0,0,1-9.5,25.14,6,6,0,1,1-9-7.94,26,26,0,0,0,0-34.37,6,6,0,0,1,9-7.93A38,38,0,0,1,206,128Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M155.51,24.81a8,8,0,0,0-8.42.88L77.25,80H32A16,16,0,0,0,16,96v64a16,16,0,0,0,16,16H77.25l69.84,54.31A8,8,0,0,0,160,224V32A8,8,0,0,0,155.51,24.81ZM32,96H72v64H32ZM144,207.64,88,164.09V91.91l56-43.55ZM208,128a39.93,39.93,0,0,1-10,26.46,8,8,0,0,1-12-10.58,24,24,0,0,0,0-31.72,8,8,0,1,1,12-10.58A40,40,0,0,1,208,128Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M153.76,28.41a4,4,0,0,0-4.22.43L78.63,84H32A12,12,0,0,0,20,96v64a12,12,0,0,0,12,12H78.63l70.91,55.16A4.07,4.07,0,0,0,152,228a3.92,3.92,0,0,0,1.76-.41A4,4,0,0,0,156,224V32A4,4,0,0,0,153.76,28.41ZM28,160V96a4,4,0,0,1,4-4H76v72H32A4,4,0,0,1,28,160Zm120,55.82L84,166V90l64-49.78ZM204,128a36,36,0,0,1-9,23.82,4,4,0,1,1-6-5.3,28,28,0,0,0,0-37,4,4,0,0,1,6-5.28A36,36,0,0,1,204,128Z\" }))\n ]\n]);\nexport {\n a as default\n};\n","import * as e from \"react\";\nconst a = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M208.49,191.51a12,12,0,0,1-17,17L128,145,64.49,208.49a12,12,0,0,1-17-17L111,128,47.51,64.49a12,12,0,0,1,17-17L128,111l63.51-63.52a12,12,0,0,1,17,17L145,128Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\n \"path\",\n {\n d: \"M216,56V200a16,16,0,0,1-16,16H56a16,16,0,0,1-16-16V56A16,16,0,0,1,56,40H200A16,16,0,0,1,216,56Z\",\n opacity: \"0.2\"\n }\n ), /* @__PURE__ */ e.createElement(\"path\", { d: \"M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M208,32H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM181.66,170.34a8,8,0,0,1-11.32,11.32L128,139.31,85.66,181.66a8,8,0,0,1-11.32-11.32L116.69,128,74.34,85.66A8,8,0,0,1,85.66,74.34L128,116.69l42.34-42.35a8,8,0,0,1,11.32,11.32L139.31,128Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M204.24,195.76a6,6,0,1,1-8.48,8.48L128,136.49,60.24,204.24a6,6,0,0,1-8.48-8.48L119.51,128,51.76,60.24a6,6,0,0,1,8.48-8.48L128,119.51l67.76-67.75a6,6,0,0,1,8.48,8.48L136.49,128Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M202.83,197.17a4,4,0,0,1-5.66,5.66L128,133.66,58.83,202.83a4,4,0,0,1-5.66-5.66L122.34,128,53.17,58.83a4,4,0,0,1,5.66-5.66L128,122.34l69.17-69.17a4,4,0,1,1,5.66,5.66L133.66,128Z\" }))\n ]\n]);\nexport {\n a as default\n};\n","import { createContext as r } from \"react\";\nconst o = r({\n color: \"currentColor\",\n size: \"1em\",\n weight: \"regular\",\n mirrored: !1\n});\nexport {\n o as IconContext\n};\n","import * as e from \"react\";\nimport { IconContext as h } from \"./context.es.js\";\nconst p = e.forwardRef(\n (s, a) => {\n const {\n alt: n,\n color: r,\n size: t,\n weight: o,\n mirrored: c,\n children: i,\n weights: m,\n ...x\n } = s, {\n color: d = \"currentColor\",\n size: l,\n weight: f = \"regular\",\n mirrored: g = !1,\n ...w\n } = e.useContext(h);\n return /* @__PURE__ */ e.createElement(\n \"svg\",\n {\n ref: a,\n xmlns: \"http://www.w3.org/2000/svg\",\n width: t != null ? t : l,\n height: t != null ? t : l,\n fill: r != null ? r : d,\n viewBox: \"0 0 256 256\",\n transform: c || g ? \"scale(-1, 1)\" : void 0,\n ...w,\n ...x\n },\n !!n && /* @__PURE__ */ e.createElement(\"title\", null, n),\n i,\n m.get(o != null ? o : f)\n );\n }\n);\np.displayName = \"IconBase\";\nexport {\n p as default\n};\n","import * as e from \"react\";\nimport s from \"../lib/IconBase.es.js\";\nimport a from \"../defs/DotsThree.es.js\";\nconst o = e.forwardRef((r, t) => /* @__PURE__ */ e.createElement(s, { ref: t, ...r, weights: a }));\no.displayName = \"DotsThreeIcon\";\nconst n = o;\nexport {\n n as DotsThree,\n o as DotsThreeIcon\n};\n","import * as e from \"react\";\nimport t from \"../lib/IconBase.es.js\";\nimport i from \"../defs/SpeakerHigh.es.js\";\nconst o = e.forwardRef((r, a) => /* @__PURE__ */ e.createElement(t, { ref: a, ...r, weights: i }));\no.displayName = \"SpeakerHighIcon\";\nconst c = o;\nexport {\n c as SpeakerHigh,\n o as SpeakerHighIcon\n};\n","import * as e from \"react\";\nimport t from \"../lib/IconBase.es.js\";\nimport m from \"../defs/SpeakerLow.es.js\";\nconst o = e.forwardRef((r, a) => /* @__PURE__ */ e.createElement(t, { ref: a, ...r, weights: m }));\no.displayName = \"SpeakerLowIcon\";\nconst s = o;\nexport {\n s as SpeakerLow,\n o as SpeakerLowIcon\n};\n","import * as o from \"react\";\nimport a from \"../lib/IconBase.es.js\";\nimport m from \"../defs/X.es.js\";\nconst e = o.forwardRef((r, t) => /* @__PURE__ */ o.createElement(a, { ref: t, ...r, weights: m }));\ne.displayName = \"XIcon\";\nconst n = e;\nexport {\n n as X,\n e as XIcon\n};\n","// src/components/AudioPosition.tsx\nimport styled from \"styled-components\";\nimport { jsx } from \"react/jsx-runtime\";\nvar 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`;\nvar AudioPosition = ({\n formattedTime,\n className\n}) => {\n return /* @__PURE__ */ jsx(PositionDisplay, { className, \"aria-label\": \"Audio position\", children: formattedTime });\n};\n\n// src/styled/BaseButton.tsx\nimport styled2 from \"styled-components\";\nvar BaseButton = styled2.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`;\nvar BaseButtonSmall = styled2(BaseButton)`\n padding: 0.25rem 0.5rem;\n font-size: ${(props) => props.theme.fontSizeSmall};\n`;\nvar IconButton = styled2(BaseButton)`\n padding: 0.5rem;\n min-width: 2.25rem;\n min-height: 2.25rem;\n`;\nvar IconButtonSmall = styled2(BaseButton)`\n padding: 0.25rem;\n min-width: 1.75rem;\n min-height: 1.75rem;\n font-size: ${(props) => props.theme.fontSizeSmall};\n`;\n\n// src/styled/BaseCheckbox.tsx\nimport styled3 from \"styled-components\";\nvar BaseCheckboxWrapper = styled3.div`\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n`;\nvar BaseCheckbox = styled3.input`\n cursor: pointer;\n accent-color: ${(props) => props.theme.inputFocusBorder};\n\n &:disabled {\n cursor: not-allowed;\n }\n`;\nvar BaseCheckboxLabel = styled3.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\n// src/styled/BaseControlButton.tsx\nimport styled4 from \"styled-components\";\nvar BaseControlButton = styled4.button`\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\n// src/styled/BaseInput.tsx\nimport styled5 from \"styled-components\";\nvar BaseInput = styled5.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`;\nvar BaseInputSmall = styled5(BaseInput)`\n padding: 0.25rem 0.5rem;\n font-size: ${(props) => props.theme.fontSizeSmall};\n`;\n\n// src/styled/BaseLabel.tsx\nimport styled6 from \"styled-components\";\nvar BaseLabel = styled6.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`;\nvar InlineLabel = styled6.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`;\nvar ScreenReaderOnly = styled6.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\n// src/styled/BaseSelect.tsx\nimport styled7 from \"styled-components\";\nvar BaseSelect = styled7.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`;\nvar BaseSelectSmall = styled7(BaseSelect)`\n padding: 0.25rem 1.75rem 0.25rem 0.5rem;\n font-size: ${(props) => props.theme.fontSizeSmall};\n`;\n\n// src/styled/BaseSlider.tsx\nimport styled8 from \"styled-components\";\nvar BaseSlider = styled8.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\n// src/components/AutomaticScrollCheckbox.tsx\nimport { jsx as jsx2, jsxs } from \"react/jsx-runtime\";\nvar AutomaticScrollCheckbox = ({\n checked,\n onChange,\n disabled = false,\n className\n}) => {\n const handleChange = (e) => {\n onChange(e.target.checked);\n };\n return /* @__PURE__ */ jsxs(BaseCheckboxWrapper, { className, children: [\n /* @__PURE__ */ jsx2(\n BaseCheckbox,\n {\n type: \"checkbox\",\n id: \"automatic-scroll\",\n className: \"automatic-scroll\",\n checked,\n onChange: handleChange,\n disabled\n }\n ),\n /* @__PURE__ */ jsx2(BaseCheckboxLabel, { htmlFor: \"automatic-scroll\", children: \"Automatic Scroll\" })\n ] });\n};\n\n// src/components/Channel.tsx\nimport { useLayoutEffect, useEffect as useEffect2, useCallback as useCallback2, useRef as useRef2 } from \"react\";\nimport styled9 from \"styled-components\";\n\n// src/wfpl-theme.ts\nfunction isWaveformGradient(color) {\n return typeof color === \"object\" && color !== null && \"type\" in color;\n}\nfunction waveformColorToCss(color) {\n if (!isWaveformGradient(color)) {\n return color;\n }\n const direction = color.direction === \"vertical\" ? \"to bottom\" : \"to right\";\n const stops = color.stops.map((stop) => `${stop.color} ${stop.offset * 100}%`).join(\", \");\n return `linear-gradient(${direction}, ${stops})`;\n}\nvar defaultTheme = {\n waveformDrawMode: \"inverted\",\n waveOutlineColor: \"#ffffff\",\n waveFillColor: \"#1a7f8e\",\n // White background for crisp look\n waveProgressColor: \"rgba(0, 0, 0, 0.10)\",\n // Subtle dark overlay for light mode\n selectedWaveOutlineColor: \"#ffffff\",\n selectedWaveFillColor: \"#00b4d8\",\n // Selected: brighter cyan\n selectedTrackControlsBackground: \"#d9e9ff\",\n // Light blue background for selected track controls\n timeColor: \"#000\",\n timescaleBackgroundColor: \"#fff\",\n playheadColor: \"#f00\",\n selectionColor: \"rgba(255, 105, 180, 0.7)\",\n // hot pink - high contrast on light backgrounds\n loopRegionColor: \"rgba(59, 130, 246, 0.3)\",\n // Blue - distinct from pink selection\n loopMarkerColor: \"#3b82f6\",\n // 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\",\n // Brighter blue for selected track clip headers\n // Fade overlay colors\n fadeOverlayColor: \"rgba(0, 0, 0, 0.4)\",\n // Semi-transparent overlay for fade regions\n // UI component colors\n backgroundColor: \"#ffffff\",\n surfaceColor: \"#f5f5f5\",\n borderColor: \"#ddd\",\n textColor: \"#333\",\n textColorMuted: \"#666\",\n // Interactive element colors\n inputBackground: \"#ffffff\",\n inputBorder: \"#ccc\",\n inputText: \"#333\",\n inputPlaceholder: \"#999\",\n inputFocusBorder: \"#0066cc\",\n // Button colors - blue to match common UI patterns\n buttonBackground: \"#0091ff\",\n buttonText: \"#ffffff\",\n buttonBorder: \"#0081e6\",\n buttonHoverBackground: \"#0081e6\",\n // Slider colors\n sliderTrackColor: \"#ddd\",\n sliderThumbColor: \"#daa520\",\n // goldenrod\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 // 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};\nvar darkTheme = {\n // Normal mode: waveOutlineColor = bars, waveFillColor = background\n waveformDrawMode: \"inverted\",\n // Dark bars on warm amber background\n waveOutlineColor: \"#c49a6c\",\n // Solid warm amber for background\n waveFillColor: \"#1a1612\",\n // Very dark warm brown for bars\n waveProgressColor: \"rgba(100, 70, 40, 0.6)\",\n // Warm brown progress overlay\n // Selected: slightly lighter bars on brighter amber background\n selectedWaveFillColor: \"#241c14\",\n // Slightly lighter warm brown bars when selected\n selectedWaveOutlineColor: \"#e8c090\",\n // Brighter amber background when selected\n selectedTrackControlsBackground: \"#2a2218\",\n // Dark warm brown for selected track controls\n timeColor: \"#d8c0a8\",\n // Warm amber for timescale text\n timescaleBackgroundColor: \"#1a1612\",\n // Dark warm brown background\n playheadColor: \"#3a8838\",\n // Darker Ampelmännchen green playhead\n selectionColor: \"rgba(60, 140, 58, 0.6)\",\n // Darker Ampelmännchen green selection - visible on dark backgrounds\n loopRegionColor: \"rgba(96, 165, 250, 0.35)\",\n // Light blue - distinct from green selection\n loopMarkerColor: \"#60a5fa\",\n // Light blue marker triangles\n clipHeaderBackgroundColor: \"rgba(20, 16, 12, 0.85)\",\n // Dark background for clip headers\n clipHeaderBorderColor: \"rgba(200, 160, 120, 0.25)\",\n clipHeaderTextColor: \"#d8c0a8\",\n // Warm amber text\n clipHeaderFontFamily: \"inherit\",\n selectedClipHeaderBackgroundColor: \"#3a2c20\",\n // Darker warm brown for selected clip headers\n // Fade overlay colors\n fadeOverlayColor: \"rgba(200, 100, 80, 0.5)\",\n // Warm red-orange overlay visible on dark backgrounds\n // UI component colors\n backgroundColor: \"#1e1e1e\",\n surfaceColor: \"#2d2d2d\",\n borderColor: \"#444\",\n textColor: \"#e0e0e0\",\n textColorMuted: \"#999\",\n // Interactive element colors\n inputBackground: \"#2d2d2d\",\n inputBorder: \"#555\",\n inputText: \"#e0e0e0\",\n inputPlaceholder: \"#777\",\n inputFocusBorder: \"#4A9EFF\",\n // Button colors - Ampelmännchen green (#63C75F) with black text\n buttonBackground: \"#63C75F\",\n buttonText: \"#0a0a0f\",\n buttonBorder: \"#52b84e\",\n buttonHoverBackground: \"#78d074\",\n // Slider colors\n sliderTrackColor: \"#555\",\n sliderThumbColor: \"#f0c040\",\n // brighter goldenrod for dark mode\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 // 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\n// src/contexts/ScrollViewport.tsx\nimport {\n createContext,\n useContext,\n useEffect,\n useCallback,\n useRef,\n useSyncExternalStore\n} from \"react\";\nimport { jsx as jsx3 } from \"react/jsx-runtime\";\nvar ViewportStore = class {\n constructor() {\n this._state = null;\n this._listeners = /* @__PURE__ */ new Set();\n this.subscribe = (callback) => {\n this._listeners.add(callback);\n return () => this._listeners.delete(callback);\n };\n this.getSnapshot = () => this._state;\n }\n /**\n * Update viewport state. Applies a 100px scroll threshold to skip updates\n * that don't affect chunk visibility (1000px chunks with 1.5× overscan buffer).\n * Only notifies listeners when the state actually changes.\n */\n update(scrollLeft, containerWidth) {\n const buffer = containerWidth * 1.5;\n const visibleStart = Math.max(0, scrollLeft - buffer);\n const visibleEnd = scrollLeft + containerWidth + buffer;\n if (this._state && this._state.containerWidth === containerWidth && Math.abs(this._state.scrollLeft - scrollLeft) < 100) {\n return;\n }\n this._state = { scrollLeft, containerWidth, visibleStart, visibleEnd };\n for (const listener of this._listeners) {\n listener();\n }\n }\n};\nvar ViewportStoreContext = createContext(null);\nvar EMPTY_SUBSCRIBE = () => () => {\n};\nvar NULL_SNAPSHOT = () => null;\nvar ScrollViewportProvider = ({\n containerRef,\n children\n}) => {\n const storeRef = useRef(null);\n if (storeRef.current === null) {\n storeRef.current = new ViewportStore();\n }\n const store = storeRef.current;\n const rafIdRef = useRef(null);\n const measure = useCallback(() => {\n const el = containerRef.current;\n if (!el) return;\n store.update(el.scrollLeft, el.clientWidth);\n }, [containerRef, store]);\n const scheduleUpdate = useCallback(() => {\n if (rafIdRef.current !== null) return;\n rafIdRef.current = requestAnimationFrame(() => {\n rafIdRef.current = null;\n measure();\n });\n }, [measure]);\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n measure();\n el.addEventListener(\"scroll\", scheduleUpdate, { passive: true });\n const resizeObserver = new ResizeObserver(() => {\n scheduleUpdate();\n });\n resizeObserver.observe(el);\n return () => {\n el.removeEventListener(\"scroll\", scheduleUpdate);\n resizeObserver.disconnect();\n if (rafIdRef.current !== null) {\n cancelAnimationFrame(rafIdRef.current);\n rafIdRef.current = null;\n }\n };\n }, [containerRef, measure, scheduleUpdate]);\n return /* @__PURE__ */ jsx3(ViewportStoreContext.Provider, { value: store, children });\n};\nvar useScrollViewport = () => {\n const store = useContext(ViewportStoreContext);\n return useSyncExternalStore(\n store ? store.subscribe : EMPTY_SUBSCRIBE,\n store ? store.getSnapshot : NULL_SNAPSHOT,\n NULL_SNAPSHOT\n );\n};\nfunction useScrollViewportSelector(selector) {\n const store = useContext(ViewportStoreContext);\n return useSyncExternalStore(\n store ? store.subscribe : EMPTY_SUBSCRIBE,\n () => selector(store ? store.getSnapshot() : null),\n () => selector(null)\n );\n}\n\n// src/constants.ts\nvar MAX_CANVAS_WIDTH = 1e3;\n\n// src/components/Channel.tsx\nimport { jsx as jsx4 } from \"react/jsx-runtime\";\nfunction createCanvasFillStyle(ctx, color, width, height) {\n if (!isWaveformGradient(color)) {\n return color;\n }\n let gradient;\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 for (const stop of color.stops) {\n gradient.addColorStop(stop.offset, stop.color);\n }\n return gradient;\n}\nvar Waveform = styled9.canvas.attrs((props) => ({\n style: {\n width: `${props.$cssWidth}px`,\n height: `${props.$waveHeight}px`,\n left: `${props.$left}px`\n }\n}))`\n position: absolute;\n top: 0;\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`;\nvar Wrapper = styled9.div.attrs((props) => ({\n style: {\n top: `${props.$waveHeight * props.$index}px`,\n width: `${props.$cssWidth}px`,\n height: `${props.$waveHeight}px`\n }\n}))`\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`;\nvar Channel = (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 = useRef2([]);\n const visibleChunkKey = useScrollViewportSelector((viewport) => {\n const totalChunks = Math.ceil(length / MAX_CANVAS_WIDTH);\n const indices = [];\n for (let i = 0; i < totalChunks; i++) {\n const chunkLeft = i * MAX_CANVAS_WIDTH;\n const chunkWidth = Math.min(length - chunkLeft, MAX_CANVAS_WIDTH);\n if (viewport) {\n const chunkEnd = chunkLeft + chunkWidth;\n if (chunkEnd <= viewport.visibleStart || chunkLeft >= viewport.visibleEnd) {\n continue;\n }\n }\n indices.push(i);\n }\n return indices.join(\",\");\n });\n const visibleChunkIndices = visibleChunkKey ? visibleChunkKey.split(\",\").map(Number) : [];\n const canvasRef = useCallback2(\n (canvas) => {\n if (canvas !== null) {\n const index2 = parseInt(canvas.dataset.index, 10);\n canvasesRef.current[index2] = canvas;\n }\n },\n []\n );\n useEffect2(() => {\n const canvases = canvasesRef.current;\n for (let i = canvases.length - 1; i >= 0; i--) {\n if (canvases[i] && !canvases[i].isConnected) {\n delete canvases[i];\n }\n }\n });\n useLayoutEffect(() => {\n const canvases = canvasesRef.current;\n const step = barWidth + barGap;\n for (let i = 0; i < canvases.length; i++) {\n const canvas = canvases[i];\n if (!canvas) continue;\n const canvasIdx = parseInt(canvas.dataset.index, 10);\n const globalPixelOffset = canvasIdx * MAX_CANVAS_WIDTH;\n const ctx = canvas.getContext(\"2d\");\n const h2 = Math.floor(waveHeight / 2);\n const maxValue = 2 ** (bits - 1);\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 const canvasWidth = canvas.width / devicePixelRatio;\n let fillColor;\n if (drawMode === \"normal\") {\n fillColor = waveFillColor;\n } else {\n fillColor = waveOutlineColor;\n }\n ctx.fillStyle = createCanvasFillStyle(\n ctx,\n fillColor,\n canvasWidth,\n waveHeight\n );\n const canvasStartGlobal = globalPixelOffset;\n const canvasEndGlobal = globalPixelOffset + canvasWidth;\n const firstBarGlobal = Math.floor((canvasStartGlobal - barWidth + step) / step) * step;\n for (let barGlobal = Math.max(0, firstBarGlobal); barGlobal < canvasEndGlobal; barGlobal += step) {\n const x = barGlobal - canvasStartGlobal;\n if (x + barWidth <= 0) continue;\n const peakIndex = barGlobal;\n if (peakIndex * 2 + 1 < data.length) {\n const minPeak = data[peakIndex * 2] / maxValue;\n const maxPeak = data[peakIndex * 2 + 1] / maxValue;\n const min = Math.abs(minPeak * h2);\n const max = Math.abs(maxPeak * h2);\n if (drawMode === \"normal\") {\n ctx.fillRect(x, h2 - max, barWidth, max + min);\n } else {\n ctx.fillRect(x, 0, barWidth, h2 - max);\n ctx.fillRect(x, h2 + min, barWidth, h2 - min);\n }\n }\n }\n }\n }\n }, [\n data,\n bits,\n waveHeight,\n waveOutlineColor,\n waveFillColor,\n devicePixelRatio,\n length,\n barWidth,\n barGap,\n drawMode,\n visibleChunkKey\n ]);\n const waveforms = visibleChunkIndices.map((i) => {\n const chunkLeft = i * MAX_CANVAS_WIDTH;\n const currentWidth = Math.min(length - chunkLeft, MAX_CANVAS_WIDTH);\n return /* @__PURE__ */ jsx4(\n Waveform,\n {\n $cssWidth: currentWidth,\n $left: chunkLeft,\n width: currentWidth * devicePixelRatio,\n height: waveHeight * devicePixelRatio,\n $waveHeight: waveHeight,\n \"data-index\": i,\n ref: canvasRef\n },\n `${length}-${i}`\n );\n });\n const bgColor = waveFillColor;\n const backgroundCss = transparentBackground ? \"transparent\" : waveformColorToCss(bgColor);\n return /* @__PURE__ */ jsx4(\n Wrapper,\n {\n $index: index,\n $cssWidth: length,\n className,\n $waveHeight: waveHeight,\n $waveFillColor: backgroundCss,\n children: waveforms\n }\n );\n};\n\n// src/components/ErrorBoundary.tsx\nimport React3 from \"react\";\nimport { jsx as jsx5 } from \"react/jsx-runtime\";\nvar errorContainerStyle = {\n padding: \"16px\",\n background: \"#1a1a2e\",\n color: \"#e0e0e0\",\n border: \"1px solid #d08070\",\n borderRadius: \"4px\",\n fontFamily: \"monospace\",\n fontSize: \"13px\",\n minHeight: \"60px\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\"\n};\nvar PlaylistErrorBoundary = class extends React3.Component {\n constructor(props) {\n super(props);\n this.state = { hasError: false, error: null };\n }\n static getDerivedStateFromError(error) {\n return { hasError: true, error };\n }\n componentDidCatch(error, errorInfo) {\n console.error(\"[waveform-playlist] Render error:\", error, errorInfo.componentStack);\n }\n render() {\n if (this.state.hasError) {\n if (this.props.fallback) {\n return this.props.fallback;\n }\n return /* @__PURE__ */ jsx5(\"div\", { style: errorContainerStyle, children: \"Waveform playlist encountered an error. Check console for details.\" });\n }\n return this.props.children;\n }\n};\n\n// src/components/Clip.tsx\nimport styled13 from \"styled-components\";\nimport { useDraggable } from \"@dnd-kit/core\";\nimport { CSS } from \"@dnd-kit/utilities\";\n\n// src/components/ClipHeader.tsx\nimport styled10 from \"styled-components\";\nimport { jsx as jsx6 } from \"react/jsx-runtime\";\nvar CLIP_HEADER_HEIGHT = 22;\nvar HeaderContainer = styled10.div`\n position: relative;\n height: ${CLIP_HEADER_HEIGHT}px;\n background: ${(props) => props.$isSelected ? props.theme.selectedClipHeaderBackgroundColor : 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`;\nvar TrackName = styled10.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`;\nvar ClipHeaderPresentational = ({\n trackName,\n isSelected = false\n}) => {\n return /* @__PURE__ */ jsx6(\n HeaderContainer,\n {\n $isDragging: false,\n $interactive: false,\n $isSelected: isSelected,\n children: /* @__PURE__ */ jsx6(TrackName, { children: trackName })\n }\n );\n};\nvar ClipHeader = ({\n clipId,\n trackIndex: _trackIndex,\n clipIndex: _clipIndex,\n trackName,\n isSelected = false,\n disableDrag = false,\n dragHandleProps\n}) => {\n if (disableDrag || !dragHandleProps) {\n return /* @__PURE__ */ jsx6(\n ClipHeaderPresentational,\n {\n trackName,\n isSelected\n }\n );\n }\n const { attributes, listeners, setActivatorNodeRef } = dragHandleProps;\n return /* @__PURE__ */ jsx6(\n HeaderContainer,\n {\n ref: setActivatorNodeRef,\n \"data-clip-id\": clipId,\n $interactive: true,\n $isSelected: isSelected,\n ...listeners,\n ...attributes,\n children: /* @__PURE__ */ jsx6(TrackName, { children: trackName })\n }\n );\n};\n\n// src/components/ClipBoundary.tsx\nimport React4 from \"react\";\nimport styled11 from \"styled-components\";\nimport { jsx as jsx7 } from \"react/jsx-runtime\";\nvar CLIP_BOUNDARY_WIDTH = 8;\nvar CLIP_BOUNDARY_WIDTH_TOUCH = 24;\nvar BoundaryContainer = styled11.div`\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) => props.$isDragging ? \"rgba(255, 255, 255, 0.4)\" : props.$isHovered ? \"rgba(255, 255, 255, 0.2)\" : \"transparent\"};\n\n ${(props) => props.$edge === \"left\" ? `border-left: 2px solid ${props.$isDragging ? \"rgba(255, 255, 255, 0.8)\" : props.$isHovered ? \"rgba(255, 255, 255, 0.5)\" : \"transparent\"};` : `border-right: 2px solid ${props.$isDragging ? \"rgba(255, 255, 255, 0.8)\" : props.$isHovered ? \"rgba(255, 255, 255, 0.5)\" : \"transparent\"};`}\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\" ? \"border-left: 2px solid rgba(255, 255, 255, 0.5);\" : \"border-right: 2px solid rgba(255, 255, 255, 0.5);\"}\n }\n\n &:active {\n background: rgba(255, 255, 255, 0.4);\n ${(props) => props.$edge === \"left\" ? \"border-left: 2px solid rgba(255, 255, 255, 0.8);\" : \"border-right: 2px solid rgba(255, 255, 255, 0.8);\"}\n }\n`;\nvar ClipBoundary = ({\n clipId,\n trackIndex: _trackIndex,\n clipIndex: _clipIndex,\n edge,\n dragHandleProps,\n touchOptimized = false\n}) => {\n const [isHovered, setIsHovered] = React4.useState(false);\n if (!dragHandleProps) {\n return null;\n }\n const { attributes, listeners, setActivatorNodeRef, isDragging } = dragHandleProps;\n return /* @__PURE__ */ jsx7(\n BoundaryContainer,\n {\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\n// src/components/FadeOverlay.tsx\nimport styled12, { useTheme } from \"styled-components\";\nimport { jsx as jsx8 } from \"react/jsx-runtime\";\nvar FadeContainer = styled12.div.attrs((props) => ({\n style: {\n left: `${props.$left}px`,\n width: `${props.$width}px`\n }\n}))`\n position: absolute;\n top: 0;\n bottom: 0;\n pointer-events: none;\n z-index: 50;\n`;\nvar FadeSvg = styled12.svg`\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`;\nfunction generateFadePath(width, height, curveType = \"logarithmic\") {\n const points = [];\n const numPoints = Math.max(20, Math.min(width, 100));\n for (let i = 0; i <= numPoints; i++) {\n const x = i / numPoints * width;\n const progress = i / numPoints;\n let curvedProgress;\n switch (curveType) {\n case \"linear\":\n curvedProgress = progress;\n break;\n case \"exponential\":\n curvedProgress = progress * progress;\n break;\n case \"sCurve\":\n curvedProgress = (1 - Math.cos(progress * Math.PI)) / 2;\n break;\n case \"logarithmic\":\n default:\n curvedProgress = Math.log10(1 + progress * 9) / Math.log10(10);\n break;\n }\n const y = (1 - curvedProgress) * height;\n points.push(`${x},${y}`);\n }\n return `M 0,${height} L ${points.join(\" L \")} L ${width},0 L 0,0 Z`;\n}\nvar FadeOverlay = ({\n left,\n width,\n type,\n curveType = \"logarithmic\",\n color\n}) => {\n const theme = useTheme();\n if (width < 1) return null;\n const fillColor = color || theme?.fadeOverlayColor || \"rgba(0, 0, 0, 0.4)\";\n return /* @__PURE__ */ jsx8(FadeContainer, { $left: left, $width: width, $type: type, children: /* @__PURE__ */ jsx8(FadeSvg, { $type: type, viewBox: `0 0 ${width} 100`, preserveAspectRatio: \"none\", children: /* @__PURE__ */ jsx8(\n \"path\",\n {\n d: generateFadePath(width, 100, curveType),\n fill: fillColor\n }\n ) }) });\n};\n\n// src/components/Clip.tsx\nimport { Fragment, jsx as jsx9, jsxs as jsxs2 } from \"react/jsx-runtime\";\nvar ClipContainer = styled13.div.attrs((props) => ({\n style: props.$isOverlay ? {} : {\n left: `${props.$left}px`,\n width: `${props.$width}px`\n }\n}))`\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`;\nvar ChannelsWrapper = styled13.div`\n flex: 1;\n position: relative;\n overflow: ${(props) => props.$isOverlay ? \"visible\" : \"hidden\"};\n`;\nvar Clip = ({\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 const left = Math.floor(startSample / samplesPerPixel);\n const endPixel = Math.floor((startSample + durationSamples) / samplesPerPixel);\n const width = endPixel - left;\n const enableDrag = showHeader && !disableHeaderDrag && !isOverlay;\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 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 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 const style = transform ? {\n transform: CSS.Translate.toString(transform),\n zIndex: isDragging ? 100 : void 0\n // Below controls (z-index: 999) but above other clips\n } : void 0;\n return /* @__PURE__ */ jsxs2(\n ClipContainer,\n {\n ref: setNodeRef,\n style,\n className,\n $left: left,\n $width: width,\n $isOverlay: isOverlay,\n \"data-clip-container\": \"true\",\n \"data-track-id\": trackId,\n onMouseDown,\n children: [\n showHeader && /* @__PURE__ */ jsx9(\n ClipHeader,\n {\n clipId,\n trackIndex,\n clipIndex,\n trackName,\n isSelected,\n disableDrag: disableHeaderDrag,\n dragHandleProps: enableDrag ? { attributes, listeners, setActivatorNodeRef } : void 0\n }\n ),\n /* @__PURE__ */ jsxs2(ChannelsWrapper, { $isOverlay: isOverlay, children: [\n children,\n showFades && fadeIn && fadeIn.duration > 0 && /* @__PURE__ */ jsx9(\n FadeOverlay,\n {\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 && /* @__PURE__ */ jsx9(\n FadeOverlay,\n {\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 ] }),\n showHeader && !disableHeaderDrag && !isOverlay && /* @__PURE__ */ jsxs2(Fragment, { children: [\n /* @__PURE__ */ jsx9(\n ClipBoundary,\n {\n clipId,\n trackIndex,\n clipIndex,\n edge: \"left\",\n touchOptimized,\n dragHandleProps: {\n attributes: leftBoundaryAttributes,\n listeners: leftBoundaryListeners,\n setActivatorNodeRef: setLeftBoundaryActivatorRef,\n isDragging: isLeftBoundaryDragging\n }\n }\n ),\n /* @__PURE__ */ jsx9(\n ClipBoundary,\n {\n clipId,\n trackIndex,\n clipIndex,\n edge: \"right\",\n touchOptimized,\n dragHandleProps: {\n attributes: rightBoundaryAttributes,\n listeners: rightBoundaryListeners,\n setActivatorNodeRef: setRightBoundaryActivatorRef,\n isDragging: isRightBoundaryDragging\n }\n }\n )\n ] })\n ]\n }\n );\n};\n\n// src/components/MasterVolumeControl.tsx\nimport styled14 from \"styled-components\";\nimport { jsx as jsx10, jsxs as jsxs3 } from \"react/jsx-runtime\";\nvar VolumeContainer = styled14.div`\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n`;\nvar VolumeLabel = styled14(BaseLabel)`\n margin: 0;\n white-space: nowrap;\n`;\nvar VolumeSlider = styled14(BaseSlider)`\n width: 120px;\n`;\nvar MasterVolumeControl = ({\n volume,\n onChange,\n disabled = false,\n className\n}) => {\n const handleChange = (e) => {\n onChange(parseFloat(e.target.value) / 100);\n };\n return /* @__PURE__ */ jsxs3(VolumeContainer, { className, children: [\n /* @__PURE__ */ jsx10(VolumeLabel, { htmlFor: \"master-gain\", children: \"Master Volume\" }),\n /* @__PURE__ */ jsx10(\n VolumeSlider,\n {\n min: \"0\",\n max: \"100\",\n value: volume * 100,\n onChange: handleChange,\n disabled,\n id: \"master-gain\"\n }\n )\n ] });\n};\n\n// src/components/Playhead.tsx\nimport { useRef as useRef3, useEffect as useEffect3 } from \"react\";\nimport styled15 from \"styled-components\";\nimport { jsx as jsx11, jsxs as jsxs4 } from \"react/jsx-runtime\";\nvar PlayheadLine = styled15.div.attrs((props) => ({\n style: {\n transform: `translate3d(${props.$position}px, 0, 0)`\n }\n}))`\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`;\nvar Playhead = ({ position, color = \"#ff0000\" }) => {\n return /* @__PURE__ */ jsx11(PlayheadLine, { $position: position, $color: color });\n};\nvar PlayheadWithMarkerContainer = styled15.div`\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`;\nvar MarkerTriangle = styled15.div`\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`;\nvar MarkerLine = styled15.div`\n position: absolute;\n top: 0;\n left: 0;\n width: 2px;\n height: 100%;\n background: ${(props) => props.$color};\n`;\nvar PlayheadWithMarker = ({\n color = \"#ff0000\",\n isPlaying,\n currentTimeRef,\n playbackStartTimeRef,\n audioStartPositionRef,\n samplesPerPixel,\n sampleRate,\n controlsOffset,\n getAudioContextTime\n}) => {\n const containerRef = useRef3(null);\n const animationFrameRef = useRef3(null);\n useEffect3(() => {\n const updatePosition = () => {\n if (containerRef.current) {\n let time;\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 if (isPlaying) {\n animationFrameRef.current = requestAnimationFrame(updatePosition);\n }\n };\n if (isPlaying) {\n animationFrameRef.current = requestAnimationFrame(updatePosition);\n } else {\n updatePosition();\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 useEffect3(() => {\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 return /* @__PURE__ */ jsxs4(PlayheadWithMarkerContainer, { ref: containerRef, $color: color, children: [\n /* @__PURE__ */ jsx11(MarkerTriangle, { $color: color }),\n /* @__PURE__ */ jsx11(MarkerLine, { $color: color })\n ] });\n};\n\n// src/components/Playlist.tsx\nimport styled16, { withTheme } from \"styled-components\";\nimport { useRef as useRef4, useCallback as useCallback3 } from \"react\";\nimport { jsx as jsx12, jsxs as jsxs5 } from \"react/jsx-runtime\";\nvar Wrapper2 = styled16.div`\n overflow-y: hidden;\n overflow-x: auto;\n position: relative;\n`;\nvar ScrollContainer = styled16.div.attrs((props) => ({\n style: props.$width !== void 0 ? { width: `${props.$width}px` } : {}\n}))`\n position: relative;\n background: ${(props) => props.$backgroundColor || \"transparent\"};\n`;\nvar TimescaleWrapper = styled16.div.attrs((props) => ({\n style: props.$width ? { minWidth: `${props.$width}px` } : {}\n}))`\n background: ${(props) => props.$backgroundColor || \"white\"};\n width: 100%;\n position: relative;\n overflow: hidden; /* Constrain loop region to timescale area */\n`;\nvar TracksContainer = styled16.div.attrs((props) => ({\n style: props.$width !== void 0 ? { minWidth: `${props.$width}px` } : {}\n}))`\n position: relative;\n background: ${(props) => props.$backgroundColor || \"transparent\"};\n width: 100%;\n`;\nvar ClickOverlay = styled16.div`\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`;\nvar Playlist = ({\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 \"data-playlist-state\": playlistState\n}) => {\n const wrapperRef = useRef4(null);\n const handleRef = useCallback3((el) => {\n wrapperRef.current = el;\n scrollContainerRef?.(el);\n }, [scrollContainerRef]);\n return /* @__PURE__ */ jsx12(Wrapper2, { \"data-scroll-container\": \"true\", \"data-playlist-state\": playlistState, ref: handleRef, children: /* @__PURE__ */ jsx12(ScrollViewportProvider, { containerRef: wrapperRef, children: /* @__PURE__ */ jsxs5(\n ScrollContainer,\n {\n $backgroundColor: backgroundColor,\n $width: scrollContainerWidth,\n children: [\n timescale && /* @__PURE__ */ jsx12(TimescaleWrapper, { $width: timescaleWidth, $backgroundColor: timescaleBackgroundColor, children: timescale }),\n /* @__PURE__ */ jsxs5(TracksContainer, { $width: tracksWidth, $backgroundColor: backgroundColor, children: [\n children,\n (onTracksClick || onTracksMouseDown) && /* @__PURE__ */ jsx12(\n ClickOverlay,\n {\n $controlsWidth: controlsWidth,\n $isSelecting: isSelecting,\n onClick: onTracksClick,\n onMouseDown: onTracksMouseDown,\n onMouseMove: onTracksMouseMove,\n onMouseUp: onTracksMouseUp\n }\n )\n ] })\n ]\n }\n ) }) });\n};\nvar StyledPlaylist = withTheme(Playlist);\n\n// src/components/Selection.tsx\nimport styled17 from \"styled-components\";\nimport { jsx as jsx13 } from \"react/jsx-runtime\";\nvar SelectionOverlay = styled17.div.attrs((props) => ({\n style: {\n left: `${props.$left}px`,\n width: `${props.$width}px`\n }\n}))`\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`;\nvar Selection = ({\n startPosition,\n endPosition,\n color = \"#00ff00\"\n}) => {\n const width = Math.max(0, endPosition - startPosition);\n if (width <= 0) {\n return null;\n }\n return /* @__PURE__ */ jsx13(SelectionOverlay, { $left: startPosition, $width: width, $color: color, \"data-selection\": true });\n};\n\n// src/components/LoopRegion.tsx\nimport { useCallback as useCallback4, useRef as useRef5, useState } from \"react\";\nimport styled18 from \"styled-components\";\nimport { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs6 } from \"react/jsx-runtime\";\nvar LoopRegionOverlayDiv = styled18.div.attrs((props) => ({\n style: {\n left: `${props.$left}px`,\n width: `${props.$width}px`\n }\n}))`\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`;\nvar LoopMarker = styled18.div.attrs((props) => ({\n style: {\n left: `${props.$left}px`\n }\n}))`\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 ? \"border-right: 8px solid transparent;\" : \"border-left: 8px solid transparent;\"}\n }\n`;\nvar LoopRegion = ({\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 if (width <= 0) {\n return null;\n }\n return /* @__PURE__ */ jsxs6(Fragment2, { children: [\n /* @__PURE__ */ jsx14(\n LoopRegionOverlayDiv,\n {\n $left: startPosition,\n $width: width,\n $color: regionColor,\n \"data-loop-region\": true\n }\n ),\n /* @__PURE__ */ jsx14(\n LoopMarker,\n {\n $left: startPosition,\n $color: markerColor,\n $isStart: true,\n \"data-loop-marker\": \"start\"\n }\n ),\n /* @__PURE__ */ jsx14(\n LoopMarker,\n {\n $left: endPosition - 2,\n $color: markerColor,\n $isStart: false,\n \"data-loop-marker\": \"end\"\n }\n )\n ] });\n};\nvar DraggableMarkerHandle = styled18.div.attrs((props) => ({\n style: {\n left: `${props.$left}px`\n }\n}))`\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 ? \"border-right: 10px solid transparent;\" : \"border-left: 10px solid transparent;\"}\n }\n\n &:hover::before {\n opacity: 1;\n }\n`;\nvar TimescaleLoopShade = styled18.div.attrs((props) => ({\n style: {\n left: `${props.$left}px`,\n width: `${props.$width}px`\n }\n}))`\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`;\nvar LoopRegionMarkers = ({\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(null);\n const dragStartX = useRef5(0);\n const dragStartPosition = useRef5(0);\n const dragStartEnd = useRef5(0);\n const width = Math.max(0, endPosition - startPosition);\n const handleMarkerMouseDown = useCallback4((e, marker) => {\n e.preventDefault();\n e.stopPropagation();\n setDraggingMarker(marker);\n dragStartX.current = e.clientX;\n dragStartPosition.current = marker === \"start\" ? startPosition : endPosition;\n const handleMouseMove = (moveEvent) => {\n const delta = moveEvent.clientX - dragStartX.current;\n const newPosition = dragStartPosition.current + delta;\n if (marker === \"start\") {\n const clampedPosition = Math.max(minPosition, Math.min(endPosition - 10, newPosition));\n onLoopStartChange?.(clampedPosition);\n } else {\n const clampedPosition = Math.max(startPosition + 10, Math.min(maxPosition, newPosition));\n onLoopEndChange?.(clampedPosition);\n }\n };\n const handleMouseUp = () => {\n setDraggingMarker(null);\n document.removeEventListener(\"mousemove\", handleMouseMove);\n document.removeEventListener(\"mouseup\", handleMouseUp);\n };\n document.addEventListener(\"mousemove\", handleMouseMove);\n document.addEventListener(\"mouseup\", handleMouseUp);\n }, [startPosition, endPosition, minPosition, maxPosition, onLoopStartChange, onLoopEndChange]);\n const handleRegionMouseDown = useCallback4((e) => {\n e.preventDefault();\n e.stopPropagation();\n setDraggingMarker(\"region\");\n dragStartX.current = e.clientX;\n dragStartPosition.current = startPosition;\n dragStartEnd.current = endPosition;\n const regionWidth = endPosition - startPosition;\n const handleMouseMove = (moveEvent) => {\n const delta = moveEvent.clientX - dragStartX.current;\n let newStart = dragStartPosition.current + delta;\n let newEnd = dragStartEnd.current + delta;\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 onLoopRegionMove?.(newStart, newEnd);\n };\n const handleMouseUp = () => {\n setDraggingMarker(null);\n document.removeEventListener(\"mousemove\", handleMouseMove);\n document.removeEventListener(\"mouseup\", handleMouseUp);\n };\n document.addEventListener(\"mousemove\", handleMouseMove);\n document.addEventListener(\"mouseup\", handleMouseUp);\n }, [startPosition, endPosition, minPosition, maxPosition, onLoopRegionMove]);\n if (width <= 0) {\n return null;\n }\n return /* @__PURE__ */ jsxs6(Fragment2, { children: [\n /* @__PURE__ */ jsx14(\n TimescaleLoopShade,\n {\n $left: startPosition,\n $width: width,\n $color: regionColor,\n $isDragging: draggingMarker === \"region\",\n onMouseDown: handleRegionMouseDown,\n \"data-loop-region-timescale\": true\n }\n ),\n /* @__PURE__ */ jsx14(\n DraggableMarkerHandle,\n {\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 ),\n /* @__PURE__ */ jsx14(\n DraggableMarkerHandle,\n {\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};\nvar TimescaleLoopCreator = styled18.div.attrs((props) => ({\n style: {\n left: `${props.$leftOffset || 0}px`\n }\n}))`\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`;\nvar TimescaleLoopRegion = ({\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 [, setIsCreating] = useState(false);\n const createStartX = useRef5(0);\n const containerRef = useRef5(null);\n const hasLoopRegion = endPosition > startPosition;\n const handleBackgroundMouseDown = useCallback4((e) => {\n const target = e.target;\n if (target.closest(\"[data-loop-marker-handle]\") || target.closest(\"[data-loop-region-timescale]\")) {\n return;\n }\n e.preventDefault();\n setIsCreating(true);\n const rect = containerRef.current?.getBoundingClientRect();\n if (!rect) return;\n const clickX = e.clientX - rect.left;\n const clampedX = Math.max(minPosition, Math.min(maxPosition, clickX));\n createStartX.current = clampedX;\n onLoopRegionChange?.(clampedX, clampedX);\n const handleMouseMove = (moveEvent) => {\n const currentX = moveEvent.clientX - rect.left;\n const clampedCurrentX = Math.max(minPosition, Math.min(maxPosition, currentX));\n const newStart = Math.min(createStartX.current, clampedCurrentX);\n const newEnd = Math.max(createStartX.current, clampedCurrentX);\n onLoopRegionChange?.(newStart, newEnd);\n };\n const handleMouseUp = () => {\n setIsCreating(false);\n document.removeEventListener(\"mousemove\", handleMouseMove);\n document.removeEventListener(\"mouseup\", handleMouseUp);\n };\n document.addEventListener(\"mousemove\", handleMouseMove);\n document.addEventListener(\"mouseup\", handleMouseUp);\n }, [minPosition, maxPosition, onLoopRegionChange]);\n return /* @__PURE__ */ jsx14(\n TimescaleLoopCreator,\n {\n ref: containerRef,\n $leftOffset: controlsOffset,\n onMouseDown: handleBackgroundMouseDown,\n \"data-timescale-loop-creator\": true,\n children: hasLoopRegion && /* @__PURE__ */ jsx14(\n LoopRegionMarkers,\n {\n startPosition,\n endPosition,\n markerColor,\n regionColor,\n minPosition,\n maxPosition,\n onLoopStartChange: (newStart) => onLoopRegionChange?.(newStart, endPosition),\n onLoopEndChange: (newEnd) => onLoopRegionChange?.(startPosition, newEnd),\n onLoopRegionMove: (newStart, newEnd) => onLoopRegionChange?.(newStart, newEnd)\n }\n )\n }\n );\n};\n\n// src/components/SelectionTimeInputs.tsx\nimport { useEffect as useEffect5, useState as useState3 } from \"react\";\n\n// src/components/TimeInput.tsx\nimport { useEffect as useEffect4, useState as useState2 } from \"react\";\n\n// src/utils/timeFormat.ts\nfunction clockFormat(seconds, decimals) {\n const hours = Math.floor(seconds / 3600) % 24;\n const minutes = Math.floor(seconds / 60) % 60;\n const secs = (seconds % 60).toFixed(decimals);\n return String(hours).padStart(2, \"0\") + \":\" + String(minutes).padStart(2, \"0\") + \":\" + secs.padStart(decimals + 3, \"0\");\n}\nfunction formatTime(seconds, format) {\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}\nfunction parseTime(timeStr, format) {\n if (!timeStr) return 0;\n switch (format) {\n case \"seconds\":\n case \"thousandths\":\n return parseFloat(timeStr) || 0;\n case \"hh:mm:ss\":\n case \"hh:mm:ss.u\":\n case \"hh:mm:ss.uu\":\n case \"hh:mm:ss.uuu\": {\n const parts = timeStr.split(\":\");\n if (parts.length !== 3) return 0;\n const hours = parseInt(parts[0], 10) || 0;\n const minutes = parseInt(parts[1], 10) || 0;\n const seconds = parseFloat(parts[2]) || 0;\n return hours * 3600 + minutes * 60 + seconds;\n }\n default:\n return 0;\n }\n}\n\n// src/components/TimeInput.tsx\nimport { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs7 } from \"react/jsx-runtime\";\nvar TimeInput = ({\n id,\n label,\n value,\n format,\n className,\n onChange,\n readOnly = false\n}) => {\n const [displayValue, setDisplayValue] = useState2(\"\");\n useEffect4(() => {\n const formatted = formatTime(value, format);\n setDisplayValue(formatted);\n }, [value, format, id]);\n const handleChange = (e) => {\n const newDisplayValue = e.target.value;\n setDisplayValue(newDisplayValue);\n };\n const handleBlur = () => {\n if (onChange) {\n const parsedValue = parseTime(displayValue, format);\n onChange(parsedValue);\n }\n setDisplayValue(formatTime(value, format));\n };\n const handleKeyDown = (e) => {\n if (e.key === \"Enter\") {\n e.currentTarget.blur();\n }\n };\n return /* @__PURE__ */ jsxs7(Fragment3, { children: [\n /* @__PURE__ */ jsx15(ScreenReaderOnly, { as: \"label\", htmlFor: id, children: label }),\n /* @__PURE__ */ jsx15(\n BaseInput,\n {\n type: \"text\",\n className,\n id,\n value: displayValue,\n onChange: handleChange,\n onBlur: handleBlur,\n onKeyDown: handleKeyDown,\n readOnly\n }\n )\n ] });\n};\n\n// src/components/SelectionTimeInputs.tsx\nimport { jsx as jsx16, jsxs as jsxs8 } from \"react/jsx-runtime\";\nvar SelectionTimeInputs = ({\n selectionStart,\n selectionEnd,\n onSelectionChange,\n className\n}) => {\n const [timeFormat, setTimeFormat] = useState3(\"hh:mm:ss.uuu\");\n useEffect5(() => {\n const timeFormatSelect = document.querySelector(\".time-format\");\n const handleFormatChange = () => {\n if (timeFormatSelect) {\n setTimeFormat(timeFormatSelect.value);\n }\n };\n if (timeFormatSelect) {\n setTimeFormat(timeFormatSelect.value);\n timeFormatSelect.addEventListener(\"change\", handleFormatChange);\n }\n return () => {\n timeFormatSelect?.removeEventListener(\"change\", handleFormatChange);\n };\n }, []);\n const handleStartChange = (value) => {\n if (onSelectionChange) {\n onSelectionChange(value, selectionEnd);\n }\n };\n const handleEndChange = (value) => {\n if (onSelectionChange) {\n onSelectionChange(selectionStart, value);\n }\n };\n return /* @__PURE__ */ jsxs8(\"div\", { className, children: [\n /* @__PURE__ */ jsx16(\n TimeInput,\n {\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 ),\n /* @__PURE__ */ jsx16(\n TimeInput,\n {\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\n// src/contexts/DevicePixelRatio.tsx\nimport { useState as useState4, createContext as createContext2, useContext as useContext2 } from \"react\";\nimport { jsx as jsx17 } from \"react/jsx-runtime\";\nfunction getScale() {\n return window.devicePixelRatio;\n}\nvar DevicePixelRatioContext = createContext2(getScale());\nvar DevicePixelRatioProvider = ({ children }) => {\n const [scale, setScale] = useState4(getScale());\n matchMedia(`(resolution: ${getScale()}dppx)`).addEventListener(\n \"change\",\n () => {\n setScale(getScale());\n },\n { once: true }\n );\n return /* @__PURE__ */ jsx17(DevicePixelRatioContext.Provider, { value: Math.ceil(scale), children });\n};\nvar useDevicePixelRatio = () => useContext2(DevicePixelRatioContext);\n\n// src/contexts/PlaylistInfo.tsx\nimport { createContext as createContext3, useContext as useContext3 } from \"react\";\nvar PlaylistInfoContext = createContext3({\n sampleRate: 48e3,\n samplesPerPixel: 1e3,\n zoomLevels: [1e3, 1500, 2e3, 2500],\n waveHeight: 80,\n timeScaleHeight: 15,\n controls: {\n show: false,\n width: 150\n },\n duration: 3e4,\n barWidth: 1,\n barGap: 0\n});\nvar usePlaylistInfo = () => useContext3(PlaylistInfoContext);\n\n// src/contexts/Theme.tsx\nimport { useContext as useContext4 } from \"react\";\nimport { ThemeContext } from \"styled-components\";\nvar useTheme2 = () => useContext4(ThemeContext);\n\n// src/contexts/TrackControls.tsx\nimport { createContext as createContext4, useContext as useContext5, Fragment as Fragment4 } from \"react\";\nimport { jsx as jsx18 } from \"react/jsx-runtime\";\nvar TrackControlsContext = createContext4(/* @__PURE__ */ jsx18(Fragment4, {}));\nvar useTrackControls = () => useContext5(TrackControlsContext);\n\n// src/contexts/Playout.tsx\nimport {\n useState as useState5,\n createContext as createContext5,\n useContext as useContext6\n} from \"react\";\nimport { jsx as jsx19 } from \"react/jsx-runtime\";\nvar defaultProgress = 0;\nvar defaultIsPlaying = false;\nvar defaultSelectionStart = 0;\nvar defaultSelectionEnd = 0;\nvar defaultPlayout = {\n progress: defaultProgress,\n isPlaying: defaultIsPlaying,\n selectionStart: defaultSelectionStart,\n selectionEnd: defaultSelectionEnd\n};\nvar PlayoutStatusContext = createContext5(defaultPlayout);\nvar PlayoutStatusUpdateContext = createContext5({\n setIsPlaying: () => {\n },\n setProgress: () => {\n },\n setSelection: () => {\n }\n});\nvar PlayoutProvider = ({ children }) => {\n const [isPlaying, setIsPlaying] = useState5(defaultIsPlaying);\n const [progress, setProgress] = useState5(defaultProgress);\n const [selectionStart, setSelectionStart] = useState5(defaultSelectionStart);\n const [selectionEnd, setSelectionEnd] = useState5(defaultSelectionEnd);\n const setSelection = (start, end) => {\n setSelectionStart(start);\n setSelectionEnd(end);\n };\n return /* @__PURE__ */ jsx19(PlayoutStatusUpdateContext.Provider, { value: { setIsPlaying, setProgress, setSelection }, children: /* @__PURE__ */ jsx19(PlayoutStatusContext.Provider, { value: { isPlaying, progress, selectionStart, selectionEnd }, children }) });\n};\nvar usePlayoutStatus = () => useContext6(PlayoutStatusContext);\nvar usePlayoutStatusUpdate = () => useContext6(PlayoutStatusUpdateContext);\n\n// src/components/SpectrogramChannel.tsx\nimport { useLayoutEffect as useLayoutEffect2, useCallback as useCallback5, useRef as useRef6, useEffect as useEffect6 } from \"react\";\nimport styled19 from \"styled-components\";\nimport { jsx as jsx20 } from \"react/jsx-runtime\";\nvar LINEAR_FREQUENCY_SCALE = (f, minF, maxF) => (f - minF) / (maxF - minF);\nvar Wrapper3 = styled19.div.attrs((props) => ({\n style: {\n top: `${props.$waveHeight * props.$index}px`,\n width: `${props.$cssWidth}px`,\n height: `${props.$waveHeight}px`\n }\n}))`\n position: absolute;\n background: #000;\n transform: translateZ(0);\n backface-visibility: hidden;\n`;\nvar SpectrogramCanvas = styled19.canvas.attrs((props) => ({\n style: {\n width: `${props.$cssWidth}px`,\n height: `${props.$waveHeight}px`,\n left: `${props.$left}px`\n }\n}))`\n position: absolute;\n top: 0;\n /* Promote to own compositing layer for smoother scrolling */\n will-change: transform;\n image-rendering: pixelated;\n image-rendering: crisp-edges;\n`;\nfunction defaultGetColorMap() {\n const lut = new Uint8Array(256 * 3);\n for (let i = 0; i < 256; i++) {\n lut[i * 3] = lut[i * 3 + 1] = lut[i * 3 + 2] = i;\n }\n return lut;\n}\nvar DEFAULT_COLOR_LUT = defaultGetColorMap();\nvar SpectrogramChannel = ({\n index,\n channelIndex: channelIndexProp,\n data,\n length,\n waveHeight,\n devicePixelRatio = 1,\n samplesPerPixel,\n colorLUT,\n frequencyScaleFn,\n minFrequency = 0,\n maxFrequency,\n workerApi,\n clipId,\n onCanvasesReady\n}) => {\n const channelIndex = channelIndexProp ?? index;\n const canvasesRef = useRef6([]);\n const registeredIdsRef = useRef6([]);\n const transferredCanvasesRef = useRef6(/* @__PURE__ */ new WeakSet());\n const workerApiRef = useRef6(workerApi);\n const onCanvasesReadyRef = useRef6(onCanvasesReady);\n const isWorkerMode = !!(workerApi && clipId);\n const visibleChunkKey = useScrollViewportSelector((viewport) => {\n const totalChunks = Math.ceil(length / MAX_CANVAS_WIDTH);\n const indices = [];\n for (let i = 0; i < totalChunks; i++) {\n const chunkLeft = i * MAX_CANVAS_WIDTH;\n const chunkWidth = Math.min(length - chunkLeft, MAX_CANVAS_WIDTH);\n if (viewport) {\n const chunkEnd = chunkLeft + chunkWidth;\n if (chunkEnd <= viewport.visibleStart || chunkLeft >= viewport.visibleEnd) {\n continue;\n }\n }\n indices.push(i);\n }\n return indices.join(\",\");\n });\n const visibleChunkIndices = visibleChunkKey ? visibleChunkKey.split(\",\").map(Number) : [];\n const canvasRef = useCallback5(\n (canvas) => {\n if (canvas !== null) {\n const idx = parseInt(canvas.dataset.index, 10);\n canvasesRef.current[idx] = canvas;\n }\n },\n []\n );\n const lut = colorLUT ?? DEFAULT_COLOR_LUT;\n const maxF = maxFrequency ?? (data ? data.sampleRate / 2 : 22050);\n const scaleFn = frequencyScaleFn ?? LINEAR_FREQUENCY_SCALE;\n const hasCustomFrequencyScale = Boolean(frequencyScaleFn);\n useEffect6(() => {\n workerApiRef.current = workerApi;\n }, [workerApi]);\n useEffect6(() => {\n onCanvasesReadyRef.current = onCanvasesReady;\n }, [onCanvasesReady]);\n useEffect6(() => {\n if (!isWorkerMode) return;\n const currentWorkerApi = workerApiRef.current;\n if (!currentWorkerApi || !clipId) return;\n const canvases2 = canvasesRef.current;\n const newIds = [];\n const newWidths = [];\n for (let i = 0; i < canvases2.length; i++) {\n const canvas = canvases2[i];\n if (!canvas) continue;\n if (transferredCanvasesRef.current.has(canvas)) continue;\n const canvasIdx = parseInt(canvas.dataset.index, 10);\n const canvasId = `${clipId}-ch${channelIndex}-chunk${canvasIdx}`;\n let offscreen;\n try {\n offscreen = canvas.transferControlToOffscreen();\n } catch (err) {\n console.warn(`[spectrogram] transferControlToOffscreen failed for ${canvasId}:`, err);\n continue;\n }\n transferredCanvasesRef.current.add(canvas);\n try {\n currentWorkerApi.registerCanvas(canvasId, offscreen);\n newIds.push(canvasId);\n newWidths.push(Math.min(length - canvasIdx * MAX_CANVAS_WIDTH, MAX_CANVAS_WIDTH));\n } catch (err) {\n console.warn(`[spectrogram] registerCanvas failed for ${canvasId}:`, err);\n continue;\n }\n }\n if (newIds.length > 0) {\n registeredIdsRef.current = [...registeredIdsRef.current, ...newIds];\n onCanvasesReadyRef.current?.(newIds, newWidths);\n }\n }, [isWorkerMode, clipId, channelIndex, length, visibleChunkKey]);\n useEffect6(() => {\n if (!isWorkerMode) return;\n const currentWorkerApi = workerApiRef.current;\n if (!currentWorkerApi) return;\n const remaining = [];\n for (const id of registeredIdsRef.current) {\n const match = id.match(/chunk(\\d+)$/);\n if (!match) {\n remaining.push(id);\n continue;\n }\n const chunkIdx = parseInt(match[1], 10);\n const canvas = canvasesRef.current[chunkIdx];\n if (canvas && canvas.isConnected) {\n remaining.push(id);\n } else {\n try {\n currentWorkerApi.unregisterCanvas(id);\n } catch (err) {\n console.warn(`[spectrogram] unregisterCanvas failed for ${id}:`, err);\n }\n }\n }\n registeredIdsRef.current = remaining;\n });\n useEffect6(() => {\n return () => {\n const api = workerApiRef.current;\n if (!api) return;\n for (const id of registeredIdsRef.current) {\n try {\n api.unregisterCanvas(id);\n } catch (err) {\n console.warn(`[spectrogram] unregisterCanvas failed for ${id}:`, err);\n }\n }\n registeredIdsRef.current = [];\n };\n }, []);\n useLayoutEffect2(() => {\n if (isWorkerMode || !data) return;\n const canvases2 = canvasesRef.current;\n const { frequencyBinCount, frameCount, hopSize, sampleRate, gainDb, rangeDb: rawRangeDb } = data;\n const rangeDb = rawRangeDb === 0 ? 1 : rawRangeDb;\n const binToFreq = (bin) => bin / frequencyBinCount * (sampleRate / 2);\n for (let i = 0; i < canvases2.length; i++) {\n const canvas = canvases2[i];\n if (!canvas) continue;\n const canvasIdx = parseInt(canvas.dataset.index, 10);\n const globalPixelOffset = canvasIdx * MAX_CANVAS_WIDTH;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) continue;\n const canvasWidth = canvas.width / devicePixelRatio;\n const canvasHeight = waveHeight;\n ctx.resetTransform();\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.imageSmoothingEnabled = false;\n ctx.scale(devicePixelRatio, devicePixelRatio);\n const imgData = ctx.createImageData(canvasWidth, canvasHeight);\n const pixels = imgData.data;\n for (let x = 0; x < canvasWidth; x++) {\n const globalX = globalPixelOffset + x;\n const samplePos = globalX * samplesPerPixel;\n const frame = Math.floor(samplePos / hopSize);\n if (frame < 0 || frame >= frameCount) continue;\n const frameOffset = frame * frequencyBinCount;\n for (let y = 0; y < canvasHeight; y++) {\n const normalizedY = 1 - y / canvasHeight;\n let bin = Math.floor(normalizedY * frequencyBinCount);\n if (hasCustomFrequencyScale) {\n let lo = 0;\n let hi = frequencyBinCount - 1;\n while (lo < hi) {\n const mid = lo + hi >> 1;\n const freq = binToFreq(mid);\n const scaled = scaleFn(freq, minFrequency, maxF);\n if (scaled < normalizedY) {\n lo = mid + 1;\n } else {\n hi = mid;\n }\n }\n bin = lo;\n }\n if (bin < 0 || bin >= frequencyBinCount) continue;\n const db = data.data[frameOffset + bin];\n const normalized = Math.max(0, Math.min(1, (db + rangeDb + gainDb) / rangeDb));\n const colorIdx = Math.floor(normalized * 255);\n const pixelIdx = (y * canvasWidth + x) * 4;\n pixels[pixelIdx] = lut[colorIdx * 3];\n pixels[pixelIdx + 1] = lut[colorIdx * 3 + 1];\n pixels[pixelIdx + 2] = lut[colorIdx * 3 + 2];\n pixels[pixelIdx + 3] = 255;\n }\n }\n ctx.resetTransform();\n ctx.putImageData(imgData, 0, 0);\n if (devicePixelRatio !== 1) {\n const tmpCanvas = document.createElement(\"canvas\");\n tmpCanvas.width = canvasWidth;\n tmpCanvas.height = canvasHeight;\n const tmpCtx = tmpCanvas.getContext(\"2d\");\n if (!tmpCtx) continue;\n tmpCtx.putImageData(imgData, 0, 0);\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.imageSmoothingEnabled = false;\n ctx.drawImage(tmpCanvas, 0, 0, canvas.width, canvas.height);\n }\n }\n }, [isWorkerMode, data, length, waveHeight, devicePixelRatio, samplesPerPixel, lut, minFrequency, maxF, scaleFn, hasCustomFrequencyScale, visibleChunkKey]);\n const canvases = visibleChunkIndices.map((i) => {\n const chunkLeft = i * MAX_CANVAS_WIDTH;\n const currentWidth = Math.min(length - chunkLeft, MAX_CANVAS_WIDTH);\n return /* @__PURE__ */ jsx20(\n SpectrogramCanvas,\n {\n $cssWidth: currentWidth,\n $left: chunkLeft,\n width: currentWidth * devicePixelRatio,\n height: waveHeight * devicePixelRatio,\n $waveHeight: waveHeight,\n \"data-index\": i,\n ref: canvasRef\n },\n `${length}-${i}`\n );\n });\n return /* @__PURE__ */ jsx20(Wrapper3, { $index: index, $cssWidth: length, $waveHeight: waveHeight, children: canvases });\n};\n\n// src/components/SmartChannel.tsx\nimport { Fragment as Fragment5, jsx as jsx21, jsxs as jsxs9 } from \"react/jsx-runtime\";\nvar SmartChannel = ({\n isSelected,\n transparentBackground,\n renderMode = \"waveform\",\n spectrogramData,\n spectrogramColorLUT,\n samplesPerPixel: sppProp,\n spectrogramFrequencyScaleFn,\n spectrogramMinFrequency,\n spectrogramMaxFrequency,\n spectrogramWorkerApi,\n spectrogramClipId,\n spectrogramOnCanvasesReady,\n ...props\n}) => {\n const theme = useTheme2();\n const { waveHeight, barWidth, barGap, samplesPerPixel: contextSpp } = usePlaylistInfo();\n const devicePixelRatio = useDevicePixelRatio();\n const samplesPerPixel = sppProp ?? contextSpp;\n const waveOutlineColor = isSelected && theme ? theme.selectedWaveOutlineColor : theme?.waveOutlineColor;\n const waveFillColor = isSelected && theme ? theme.selectedWaveFillColor : theme?.waveFillColor;\n const drawMode = theme?.waveformDrawMode || \"inverted\";\n const hasSpectrogram = spectrogramData || spectrogramWorkerApi;\n if (renderMode === \"spectrogram\" && hasSpectrogram) {\n return /* @__PURE__ */ jsx21(\n SpectrogramChannel,\n {\n index: props.index,\n data: spectrogramData,\n length: props.length,\n waveHeight,\n devicePixelRatio,\n samplesPerPixel,\n colorLUT: spectrogramColorLUT,\n frequencyScaleFn: spectrogramFrequencyScaleFn,\n minFrequency: spectrogramMinFrequency,\n maxFrequency: spectrogramMaxFrequency,\n workerApi: spectrogramWorkerApi,\n clipId: spectrogramClipId,\n onCanvasesReady: spectrogramOnCanvasesReady\n }\n );\n }\n if (renderMode === \"both\" && hasSpectrogram) {\n const halfHeight = Math.floor(waveHeight / 2);\n return /* @__PURE__ */ jsxs9(Fragment5, { children: [\n /* @__PURE__ */ jsx21(\n SpectrogramChannel,\n {\n index: props.index * 2,\n channelIndex: props.index,\n data: spectrogramData,\n length: props.length,\n waveHeight: halfHeight,\n devicePixelRatio,\n samplesPerPixel,\n colorLUT: spectrogramColorLUT,\n frequencyScaleFn: spectrogramFrequencyScaleFn,\n minFrequency: spectrogramMinFrequency,\n maxFrequency: spectrogramMaxFrequency,\n workerApi: spectrogramWorkerApi,\n clipId: spectrogramClipId,\n onCanvasesReady: spectrogramOnCanvasesReady\n }\n ),\n /* @__PURE__ */ jsx21(\"div\", { style: { position: \"absolute\", top: (props.index * 2 + 1) * halfHeight, width: props.length, height: halfHeight }, children: /* @__PURE__ */ jsx21(\n Channel,\n {\n ...props,\n index: 0,\n waveOutlineColor,\n waveFillColor,\n waveHeight: halfHeight,\n devicePixelRatio,\n barWidth,\n barGap,\n transparentBackground,\n drawMode\n }\n ) })\n ] });\n }\n return /* @__PURE__ */ jsx21(\n Channel,\n {\n ...props,\n waveOutlineColor,\n waveFillColor,\n waveHeight,\n devicePixelRatio,\n barWidth,\n barGap,\n transparentBackground,\n drawMode\n }\n );\n};\n\n// src/components/SpectrogramLabels.tsx\nimport { useRef as useRef7, useLayoutEffect as useLayoutEffect3 } from \"react\";\nimport styled20 from \"styled-components\";\nimport { jsx as jsx22 } from \"react/jsx-runtime\";\nvar LABELS_WIDTH = 72;\nvar LabelsStickyWrapper = styled20.div`\n position: sticky;\n left: 0;\n z-index: 101;\n pointer-events: none;\n height: 0;\n width: 0;\n overflow: visible;\n`;\nfunction getFrequencyLabels(minF, maxF, height) {\n const allCandidates = [\n 20,\n 50,\n 100,\n 200,\n 500,\n 1e3,\n 2e3,\n 3e3,\n 4e3,\n 5e3,\n 8e3,\n 1e4,\n 12e3,\n 16e3,\n 2e4\n ];\n const inRange = allCandidates.filter((f) => f >= minF && f <= maxF);\n const maxLabels = Math.max(2, Math.floor(height / 20));\n if (inRange.length <= maxLabels) return inRange;\n const step = (inRange.length - 1) / (maxLabels - 1);\n const result = [];\n for (let i = 0; i < maxLabels; i++) {\n result.push(inRange[Math.round(i * step)]);\n }\n return result;\n}\nvar SpectrogramLabels = ({\n waveHeight,\n numChannels,\n frequencyScaleFn,\n minFrequency,\n maxFrequency,\n labelsColor = \"#ccc\",\n labelsBackground = \"rgba(0,0,0,0.6)\",\n renderMode = \"spectrogram\",\n hasClipHeaders = false\n}) => {\n const canvasRef = useRef7(null);\n const devicePixelRatio = useDevicePixelRatio();\n const spectrogramHeight = renderMode === \"both\" ? Math.floor(waveHeight / 2) : waveHeight;\n const totalHeight = numChannels * waveHeight;\n const clipHeaderOffset = hasClipHeaders ? 22 : 0;\n useLayoutEffect3(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n ctx.resetTransform();\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.scale(devicePixelRatio, devicePixelRatio);\n const labelFreqs = getFrequencyLabels(minFrequency, maxFrequency, spectrogramHeight);\n for (let ch = 0; ch < numChannels; ch++) {\n const channelTop = ch * waveHeight + clipHeaderOffset;\n ctx.font = \"11px monospace\";\n ctx.textBaseline = \"middle\";\n for (const freq of labelFreqs) {\n const normalized = frequencyScaleFn(freq, minFrequency, maxFrequency);\n if (normalized < 0 || normalized > 1) continue;\n const y = channelTop + spectrogramHeight * (1 - normalized);\n const text = freq >= 1e3 ? `${(freq / 1e3).toFixed(1)}k` : `${freq} Hz`;\n const metrics = ctx.measureText(text);\n const padding = 3;\n ctx.fillStyle = labelsBackground;\n ctx.fillRect(0, y - 7, metrics.width + padding * 2, 14);\n ctx.fillStyle = labelsColor;\n ctx.fillText(text, padding, y);\n }\n }\n }, [waveHeight, numChannels, frequencyScaleFn, minFrequency, maxFrequency, labelsColor, labelsBackground, devicePixelRatio, spectrogramHeight, clipHeaderOffset]);\n return /* @__PURE__ */ jsx22(LabelsStickyWrapper, { $height: totalHeight + clipHeaderOffset, children: /* @__PURE__ */ jsx22(\n \"canvas\",\n {\n ref: canvasRef,\n width: LABELS_WIDTH * devicePixelRatio,\n height: (totalHeight + clipHeaderOffset) * devicePixelRatio,\n style: {\n width: LABELS_WIDTH,\n height: totalHeight + clipHeaderOffset,\n pointerEvents: \"none\"\n }\n }\n ) });\n};\n\n// src/components/SmartScale.tsx\nimport { useContext as useContext8 } from \"react\";\n\n// src/components/TimeScale.tsx\nimport React15, { useRef as useRef8, useEffect as useEffect7, useLayoutEffect as useLayoutEffect4, useContext as useContext7, useMemo, useCallback as useCallback6 } from \"react\";\nimport styled21, { withTheme as withTheme2 } from \"styled-components\";\n\n// src/utils/conversions.ts\nfunction samplesToSeconds(samples, sampleRate) {\n return samples / sampleRate;\n}\nfunction secondsToSamples(seconds, sampleRate) {\n return Math.ceil(seconds * sampleRate);\n}\nfunction samplesToPixels(samples, samplesPerPixel) {\n return Math.floor(samples / samplesPerPixel);\n}\nfunction pixelsToSamples(pixels, samplesPerPixel) {\n return Math.floor(pixels * samplesPerPixel);\n}\nfunction pixelsToSeconds(pixels, samplesPerPixel, sampleRate) {\n return pixels * samplesPerPixel / sampleRate;\n}\nfunction secondsToPixels(seconds, samplesPerPixel, sampleRate) {\n return Math.ceil(seconds * sampleRate / samplesPerPixel);\n}\n\n// src/components/TimeScale.tsx\nimport { jsx as jsx23, jsxs as jsxs10 } from \"react/jsx-runtime\";\nfunction formatTime2(milliseconds) {\n const seconds = Math.floor(milliseconds / 1e3);\n const s = seconds % 60;\n const m = (seconds - s) / 60;\n return `${m}:${String(s).padStart(2, \"0\")}`;\n}\nvar PlaylistTimeScaleScroll = styled21.div.attrs((props) => ({\n style: {\n width: `${props.$cssWidth}px`,\n marginLeft: `${props.$controlWidth}px`,\n height: `${props.$timeScaleHeight}px`\n }\n}))`\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`;\nvar TimeTickChunk = styled21.canvas.attrs((props) => ({\n style: {\n width: `${props.$cssWidth}px`,\n height: `${props.$timeScaleHeight}px`,\n left: `${props.$left}px`\n }\n}))`\n position: absolute;\n bottom: 0;\n /* Promote to own compositing layer for smoother scrolling */\n will-change: transform;\n`;\nvar TimeStamp = styled21.div.attrs((props) => ({\n style: {\n left: `${props.$left + 4}px`\n // Offset 4px to the right of the tick\n }\n}))`\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`;\nvar TimeScale = (props) => {\n const {\n theme: { timeColor },\n duration,\n marker,\n bigStep,\n secondStep,\n renderTimestamp\n } = props;\n const canvasRefsMap = useRef8(/* @__PURE__ */ new Map());\n const {\n sampleRate,\n samplesPerPixel,\n timeScaleHeight,\n controls: { show: showControls, width: controlWidth }\n } = useContext7(PlaylistInfoContext);\n const devicePixelRatio = useDevicePixelRatio();\n const canvasRefCallback = useCallback6((canvas) => {\n if (canvas !== null) {\n const idx = parseInt(canvas.dataset.index, 10);\n canvasRefsMap.current.set(idx, canvas);\n }\n }, []);\n const { widthX, canvasInfo, timeMarkersWithPositions } = useMemo(() => {\n const nextCanvasInfo = /* @__PURE__ */ new Map();\n const nextMarkers = [];\n const nextWidthX = secondsToPixels(duration / 1e3, samplesPerPixel, sampleRate);\n const pixPerSec = sampleRate / samplesPerPixel;\n let counter = 0;\n for (let i = 0; i < nextWidthX; i += pixPerSec * secondStep / 1e3) {\n const pix = Math.floor(i);\n if (counter % marker === 0) {\n const timeMs = counter;\n const timestamp = formatTime2(timeMs);\n const element = renderTimestamp ? /* @__PURE__ */ jsx23(React15.Fragment, { children: renderTimestamp(timeMs, pix) }, `timestamp-${counter}`) : /* @__PURE__ */ jsx23(TimeStamp, { $left: pix, children: timestamp }, timestamp);\n nextMarkers.push({ pix, element });\n nextCanvasInfo.set(pix, timeScaleHeight);\n } else if (counter % bigStep === 0) {\n nextCanvasInfo.set(pix, Math.floor(timeScaleHeight / 2));\n } else if (counter % secondStep === 0) {\n nextCanvasInfo.set(pix, Math.floor(timeScaleHeight / 5));\n }\n counter += secondStep;\n }\n return {\n widthX: nextWidthX,\n canvasInfo: nextCanvasInfo,\n timeMarkersWithPositions: nextMarkers\n };\n }, [duration, samplesPerPixel, sampleRate, marker, bigStep, secondStep, renderTimestamp, timeScaleHeight]);\n const visibleChunkKey = useScrollViewportSelector((viewport) => {\n const totalChunks = Math.ceil(widthX / MAX_CANVAS_WIDTH);\n const indices = [];\n for (let i = 0; i < totalChunks; i++) {\n const chunkLeft = i * MAX_CANVAS_WIDTH;\n const chunkWidth = Math.min(widthX - chunkLeft, MAX_CANVAS_WIDTH);\n if (viewport) {\n const chunkEnd = chunkLeft + chunkWidth;\n if (chunkEnd <= viewport.visibleStart || chunkLeft >= viewport.visibleEnd) {\n continue;\n }\n }\n indices.push(i);\n }\n return indices.join(\",\");\n });\n const visibleChunkIndices = visibleChunkKey ? visibleChunkKey.split(\",\").map(Number) : [];\n const visibleChunks = visibleChunkIndices.map((i) => {\n const chunkLeft = i * MAX_CANVAS_WIDTH;\n const chunkWidth = Math.min(widthX - chunkLeft, MAX_CANVAS_WIDTH);\n return /* @__PURE__ */ jsx23(\n TimeTickChunk,\n {\n $cssWidth: chunkWidth,\n $left: chunkLeft,\n $timeScaleHeight: timeScaleHeight,\n width: chunkWidth * devicePixelRatio,\n height: timeScaleHeight * devicePixelRatio,\n \"data-index\": i,\n ref: canvasRefCallback\n },\n `timescale-${i}`\n );\n });\n const firstChunkLeft = visibleChunkIndices.length > 0 ? visibleChunkIndices[0] * MAX_CANVAS_WIDTH : 0;\n const lastChunkRight = visibleChunkIndices.length > 0 ? (visibleChunkIndices[visibleChunkIndices.length - 1] + 1) * MAX_CANVAS_WIDTH : Infinity;\n const visibleMarkers = visibleChunkIndices.length > 0 ? timeMarkersWithPositions.filter(({ pix }) => pix >= firstChunkLeft && pix < lastChunkRight).map(({ element }) => element) : timeMarkersWithPositions.map(({ element }) => element);\n useEffect7(() => {\n const currentMap = canvasRefsMap.current;\n for (const [idx, canvas] of currentMap.entries()) {\n if (!canvas.isConnected) {\n currentMap.delete(idx);\n }\n }\n });\n useLayoutEffect4(() => {\n for (const [chunkIdx, canvas] of canvasRefsMap.current.entries()) {\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) continue;\n const chunkLeft = chunkIdx * MAX_CANVAS_WIDTH;\n const chunkWidth = canvas.width / devicePixelRatio;\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 for (const [pixLeft, scaleHeight] of canvasInfo.entries()) {\n if (pixLeft < chunkLeft || pixLeft >= chunkLeft + chunkWidth) continue;\n const localX = pixLeft - chunkLeft;\n const scaleY = timeScaleHeight - scaleHeight;\n ctx.fillRect(localX, scaleY, 1, scaleHeight);\n }\n }\n }, [duration, devicePixelRatio, timeColor, timeScaleHeight, canvasInfo, visibleChunkKey]);\n return /* @__PURE__ */ jsxs10(\n PlaylistTimeScaleScroll,\n {\n $cssWidth: widthX,\n $controlWidth: showControls ? controlWidth : 0,\n $timeScaleHeight: timeScaleHeight,\n children: [\n visibleMarkers,\n visibleChunks\n ]\n }\n );\n};\nvar StyledTimeScale = withTheme2(TimeScale);\n\n// src/components/SmartScale.tsx\nimport { jsx as jsx24 } from \"react/jsx-runtime\";\nvar timeinfo = /* @__PURE__ */ new Map([\n [\n 700,\n {\n marker: 1e3,\n bigStep: 500,\n smallStep: 100\n }\n ],\n [\n 1500,\n {\n marker: 2e3,\n bigStep: 1e3,\n smallStep: 200\n }\n ],\n [\n 2500,\n {\n marker: 2e3,\n bigStep: 1e3,\n smallStep: 500\n }\n ],\n [\n 5e3,\n {\n marker: 5e3,\n bigStep: 1e3,\n smallStep: 500\n }\n ],\n [\n 1e4,\n {\n marker: 1e4,\n bigStep: 5e3,\n smallStep: 1e3\n }\n ],\n [\n 12e3,\n {\n marker: 15e3,\n bigStep: 5e3,\n smallStep: 1e3\n }\n ],\n [\n Infinity,\n {\n marker: 3e4,\n bigStep: 1e4,\n smallStep: 5e3\n }\n ]\n]);\nfunction getScaleInfo(samplesPerPixel) {\n const keys = timeinfo.keys();\n let config;\n for (const resolution of keys) {\n if (samplesPerPixel < resolution) {\n config = timeinfo.get(resolution);\n break;\n }\n }\n if (config === void 0) {\n config = { marker: 3e4, bigStep: 1e4, smallStep: 5e3 };\n }\n return config;\n}\nvar SmartScale = ({ renderTimestamp }) => {\n const { samplesPerPixel, duration } = useContext8(PlaylistInfoContext);\n let config = getScaleInfo(samplesPerPixel);\n return /* @__PURE__ */ jsx24(\n StyledTimeScale,\n {\n marker: config.marker,\n bigStep: config.bigStep,\n secondStep: config.smallStep,\n duration,\n renderTimestamp\n }\n );\n};\n\n// src/components/TimeFormatSelect.tsx\nimport styled22 from \"styled-components\";\nimport { jsx as jsx25 } from \"react/jsx-runtime\";\nvar SelectWrapper = styled22.div`\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n`;\nvar TIME_FORMAT_OPTIONS = [\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];\nvar TimeFormatSelect = ({\n value,\n onChange,\n disabled = false,\n className\n}) => {\n const handleChange = (e) => {\n onChange(e.target.value);\n };\n return /* @__PURE__ */ jsx25(SelectWrapper, { className, children: /* @__PURE__ */ jsx25(\n BaseSelect,\n {\n className: \"time-format\",\n value,\n onChange: handleChange,\n disabled,\n \"aria-label\": \"Time format selection\",\n children: TIME_FORMAT_OPTIONS.map((option) => /* @__PURE__ */ jsx25(\"option\", { value: option.value, children: option.label }, option.value))\n }\n ) });\n};\n\n// src/components/Track.tsx\nimport styled23 from \"styled-components\";\nimport { jsx as jsx26, jsxs as jsxs11 } from \"react/jsx-runtime\";\nvar Container = styled23.div.attrs((props) => ({\n style: {\n height: `${props.$waveHeight * props.$numChannels + (props.$hasClipHeaders ? CLIP_HEADER_HEIGHT : 0)}px`\n }\n}))`\n position: relative;\n display: flex;\n ${(props) => props.$width !== void 0 && `width: ${props.$width}px;`}\n`;\nvar ChannelContainer = styled23.div.attrs((props) => ({\n style: {\n paddingLeft: `${props.$offset || 0}px`\n }\n}))`\n position: relative;\n background: ${(props) => props.$backgroundColor || \"transparent\"};\n flex: 1;\n`;\nvar ControlsWrapper = styled23.div.attrs((props) => ({\n style: {\n width: `${props.$controlWidth}px`\n }\n}))`\n position: sticky;\n z-index: 102; /* Above waveform content and spectrogram labels (101), below Docusaurus navbar (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`;\nvar Track = ({\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 /* @__PURE__ */ jsxs11(\n Container,\n {\n $numChannels: numChannels,\n className,\n $waveHeight: waveHeight,\n $controlWidth: show ? controlWidth : 0,\n $width: width,\n $hasClipHeaders: hasClipHeaders,\n $isSelected: isSelected,\n children: [\n /* @__PURE__ */ jsx26(\n ControlsWrapper,\n {\n $controlWidth: show ? controlWidth : 0,\n $isSelected: isSelected,\n children: controls\n }\n ),\n /* @__PURE__ */ jsx26(\n ChannelContainer,\n {\n $controlWidth: show ? controlWidth : 0,\n $backgroundColor: backgroundColor,\n $offset: offset,\n onClick,\n \"data-track-id\": trackId,\n children\n }\n )\n ]\n }\n );\n};\n\n// src/components/TrackControls/Button.tsx\nimport styled24 from \"styled-components\";\nvar Button = styled24.button.attrs({\n type: \"button\"\n})`\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 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\n// src/components/TrackControls/ButtonGroup.tsx\nimport styled25 from \"styled-components\";\nvar ButtonGroup = styled25.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\n// src/components/TrackControls/CloseButton.tsx\nimport styled26 from \"styled-components\";\nimport { X as XIcon } from \"@phosphor-icons/react\";\nimport { jsx as jsx27 } from \"react/jsx-runtime\";\nvar StyledCloseButton = styled26.button`\n position: absolute;\n left: 0;\n top: 0;\n border: none;\n background: transparent;\n color: inherit;\n cursor: pointer;\n font-size: 16px;\n padding: 2px 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n opacity: 0.7;\n transition: opacity 0.15s, color 0.15s;\n\n &:hover {\n opacity: 1;\n color: #dc3545;\n }\n`;\nvar CloseButton = ({\n onClick,\n title = \"Remove track\"\n}) => /* @__PURE__ */ jsx27(StyledCloseButton, { onClick, title, children: /* @__PURE__ */ jsx27(XIcon, { size: 12, weight: \"bold\" }) });\n\n// src/components/TrackControls/Controls.tsx\nimport styled27 from \"styled-components\";\nvar Controls = styled27.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\n// src/components/TrackControls/Header.tsx\nimport styled28 from \"styled-components\";\nvar Header = styled28.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\n// src/components/TrackControls/VolumeDownIcon.tsx\nimport { SpeakerLowIcon } from \"@phosphor-icons/react\";\nimport { jsx as jsx28 } from \"react/jsx-runtime\";\nvar VolumeDownIcon = (props) => /* @__PURE__ */ jsx28(SpeakerLowIcon, { weight: \"light\", ...props });\n\n// src/components/TrackControls/VolumeUpIcon.tsx\nimport { SpeakerHighIcon } from \"@phosphor-icons/react\";\nimport { jsx as jsx29 } from \"react/jsx-runtime\";\nvar VolumeUpIcon = (props) => /* @__PURE__ */ jsx29(SpeakerHighIcon, { weight: \"light\", ...props });\n\n// src/components/TrackControls/TrashIcon.tsx\nimport { TrashIcon as PhosphorTrashIcon } from \"@phosphor-icons/react\";\nimport { jsx as jsx30 } from \"react/jsx-runtime\";\nvar TrashIcon = (props) => /* @__PURE__ */ jsx30(PhosphorTrashIcon, { weight: \"light\", ...props });\n\n// src/components/TrackControls/DotsIcon.tsx\nimport { DotsThreeIcon } from \"@phosphor-icons/react\";\nimport { jsx as jsx31 } from \"react/jsx-runtime\";\nvar DotsIcon = (props) => /* @__PURE__ */ jsx31(DotsThreeIcon, { weight: \"bold\", ...props });\n\n// src/components/TrackControls/Slider.tsx\nimport styled29 from \"styled-components\";\nvar Slider = styled29(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\n// src/components/TrackControls/SliderWrapper.tsx\nimport styled30 from \"styled-components\";\nvar SliderWrapper = styled30.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// src/components/TrackMenu.tsx\nimport React17, { useState as useState6, useEffect as useEffect8, useRef as useRef9 } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport styled31 from \"styled-components\";\nimport { jsx as jsx32, jsxs as jsxs12 } from \"react/jsx-runtime\";\nvar MenuContainer = styled31.div`\n position: relative;\n display: inline-block;\n`;\nvar MenuButton = styled31.button`\n background: none;\n border: none;\n cursor: pointer;\n padding: 2px 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n color: inherit;\n opacity: 0.7;\n\n &:hover {\n opacity: 1;\n }\n`;\nvar Dropdown = styled31.div`\n position: fixed;\n top: ${(p) => p.$top}px;\n left: ${(p) => p.$left}px;\n z-index: 10000;\n background: ${(p) => p.theme.timescaleBackgroundColor ?? \"#222\"};\n color: ${(p) => p.theme.textColor ?? \"inherit\"};\n border: 1px solid rgba(128, 128, 128, 0.4);\n border-radius: 6px;\n padding: 0.5rem 0;\n min-width: 180px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);\n`;\nvar Divider = styled31.hr`\n border: none;\n border-top: 1px solid rgba(128, 128, 128, 0.3);\n margin: 0.35rem 0;\n`;\nvar TrackMenu = ({\n items: itemsProp\n}) => {\n const [open, setOpen] = useState6(false);\n const close = () => setOpen(false);\n const items = typeof itemsProp === \"function\" ? itemsProp(close) : itemsProp;\n const [dropdownPos, setDropdownPos] = useState6({ top: 0, left: 0 });\n const buttonRef = useRef9(null);\n const dropdownRef = useRef9(null);\n useEffect8(() => {\n if (open && buttonRef.current) {\n const rect = buttonRef.current.getBoundingClientRect();\n setDropdownPos({\n top: rect.bottom + 2,\n left: Math.max(0, rect.right - 180)\n });\n }\n }, [open]);\n useEffect8(() => {\n if (!open) return;\n const handleClick = (e) => {\n const target = e.target;\n if (buttonRef.current && !buttonRef.current.contains(target) && dropdownRef.current && !dropdownRef.current.contains(target)) {\n setOpen(false);\n }\n };\n document.addEventListener(\"mousedown\", handleClick);\n return () => document.removeEventListener(\"mousedown\", handleClick);\n }, [open]);\n return /* @__PURE__ */ jsxs12(MenuContainer, { children: [\n /* @__PURE__ */ jsx32(\n MenuButton,\n {\n ref: buttonRef,\n onClick: (e) => {\n e.stopPropagation();\n setOpen((prev) => !prev);\n },\n onMouseDown: (e) => e.stopPropagation(),\n title: \"Track menu\",\n \"aria-label\": \"Track menu\",\n children: /* @__PURE__ */ jsx32(DotsIcon, { size: 16 })\n }\n ),\n open && typeof document !== \"undefined\" && createPortal(\n /* @__PURE__ */ jsx32(\n Dropdown,\n {\n ref: dropdownRef,\n $top: dropdownPos.top,\n $left: dropdownPos.left,\n onMouseDown: (e) => e.stopPropagation(),\n children: items.map((item, index) => /* @__PURE__ */ jsxs12(React17.Fragment, { children: [\n index > 0 && /* @__PURE__ */ jsx32(Divider, {}),\n item.content\n ] }, item.id))\n }\n ),\n document.body\n )\n ] });\n};\nexport {\n AudioPosition,\n AutomaticScrollCheckbox,\n BaseButton,\n BaseCheckbox,\n BaseCheckboxLabel,\n BaseCheckboxWrapper,\n BaseControlButton,\n BaseInput,\n BaseLabel,\n BaseSelect,\n BaseSlider,\n Button,\n ButtonGroup,\n CLIP_BOUNDARY_WIDTH,\n CLIP_BOUNDARY_WIDTH_TOUCH,\n CLIP_HEADER_HEIGHT,\n Channel,\n Clip,\n ClipBoundary,\n ClipHeader,\n ClipHeaderPresentational,\n CloseButton,\n Controls,\n DevicePixelRatioProvider,\n DotsIcon,\n FadeOverlay,\n Header,\n InlineLabel,\n LoopRegion,\n LoopRegionMarkers,\n MAX_CANVAS_WIDTH,\n MasterVolumeControl,\n Playhead,\n PlayheadWithMarker,\n Playlist,\n PlaylistErrorBoundary,\n PlaylistInfoContext,\n PlayoutProvider,\n ScreenReaderOnly,\n ScrollViewportProvider,\n Selection,\n SelectionTimeInputs,\n Slider,\n SliderWrapper,\n SmartChannel,\n SmartScale,\n SpectrogramChannel,\n SpectrogramLabels,\n StyledPlaylist,\n StyledTimeScale,\n TimeFormatSelect,\n TimeInput,\n TimeScale,\n TimescaleLoopRegion,\n Track,\n TrackControlsContext,\n TrackMenu,\n TrashIcon,\n VolumeDownIcon,\n VolumeUpIcon,\n darkTheme,\n defaultTheme,\n formatTime,\n isWaveformGradient,\n parseTime,\n pixelsToSamples,\n pixelsToSeconds,\n samplesToPixels,\n samplesToSeconds,\n secondsToPixels,\n secondsToSamples,\n useDevicePixelRatio,\n usePlaylistInfo,\n usePlayoutStatus,\n usePlayoutStatusUpdate,\n useScrollViewport,\n useScrollViewportSelector,\n useTheme2 as useTheme,\n useTrackControls,\n waveformColorToCss\n};\n//# sourceMappingURL=index.mjs.map","/**\n * Provides access to the waveform data for a single audio channel.\n */\n\nfunction WaveformDataChannel(waveformData, channelIndex) {\n this._waveformData = waveformData;\n this._channelIndex = channelIndex;\n}\n\n/**\n * Returns the waveform minimum at the given index position.\n */\n\nWaveformDataChannel.prototype.min_sample = function (index) {\n var offset = (index * this._waveformData.channels + this._channelIndex) * 2;\n return this._waveformData._at(offset);\n};\n\n/**\n * Returns the waveform maximum at the given index position.\n */\n\nWaveformDataChannel.prototype.max_sample = function (index) {\n var offset = (index * this._waveformData.channels + this._channelIndex) * 2 + 1;\n return this._waveformData._at(offset);\n};\n\n/**\n * Sets the waveform minimum at the given index position.\n */\n\nWaveformDataChannel.prototype.set_min_sample = function (index, sample) {\n var offset = (index * this._waveformData.channels + this._channelIndex) * 2;\n return this._waveformData._set_at(offset, sample);\n};\n\n/**\n * Sets the waveform maximum at the given index position.\n */\n\nWaveformDataChannel.prototype.set_max_sample = function (index, sample) {\n var offset = (index * this._waveformData.channels + this._channelIndex) * 2 + 1;\n return this._waveformData._set_at(offset, sample);\n};\n\n/**\n * Returns all the waveform minimum values as an array.\n */\n\nWaveformDataChannel.prototype.min_array = function () {\n var length = this._waveformData.length;\n var values = [];\n for (var i = 0; i < length; i++) {\n values.push(this.min_sample(i));\n }\n return values;\n};\n\n/**\n * Returns all the waveform maximum values as an array.\n */\n\nWaveformDataChannel.prototype.max_array = function () {\n var length = this._waveformData.length;\n var values = [];\n for (var i = 0; i < length; i++) {\n values.push(this.max_sample(i));\n }\n return values;\n};\n\n/**\n * AudioBuffer-based WaveformData generator\n *\n * Adapted from BlockFile::CalcSummary in Audacity, with permission.\n * See https://github.com/audacity/audacity/blob/\n * 1108c1376c09166162335fab4743008cba57c4ee/src/BlockFile.cpp#L198\n */\n\nvar INT8_MAX = 127;\nvar INT8_MIN = -128;\nvar INT16_MAX = 32767;\nvar INT16_MIN = -32768;\nfunction calculateWaveformDataLength(audio_sample_count, scale) {\n var data_length = Math.floor(audio_sample_count / scale);\n var samples_remaining = audio_sample_count - data_length * scale;\n if (samples_remaining > 0) {\n data_length++;\n }\n return data_length;\n}\nfunction generateWaveformData(options) {\n var scale = options.scale;\n var amplitude_scale = options.amplitude_scale;\n var split_channels = options.split_channels;\n var length = options.length;\n var sample_rate = options.sample_rate;\n var channels = options.channels.map(function (channel) {\n return new Float32Array(channel);\n });\n var output_channels = split_channels ? channels.length : 1;\n var header_size = 24;\n var data_length = calculateWaveformDataLength(length, scale);\n var bytes_per_sample = options.bits === 8 ? 1 : 2;\n var total_size = header_size + data_length * 2 * bytes_per_sample * output_channels;\n var buffer = new ArrayBuffer(total_size);\n var data_view = new DataView(buffer);\n var scale_counter = 0;\n var offset = header_size;\n var min_value = new Array(output_channels);\n var max_value = new Array(output_channels);\n for (var channel = 0; channel < output_channels; channel++) {\n min_value[channel] = Infinity;\n max_value[channel] = -Infinity;\n }\n var range_min = options.bits === 8 ? INT8_MIN : INT16_MIN;\n var range_max = options.bits === 8 ? INT8_MAX : INT16_MAX;\n data_view.setInt32(0, 2, true); // Version\n data_view.setUint32(4, options.bits === 8, true); // Is 8 bit?\n data_view.setInt32(8, sample_rate, true); // Sample rate\n data_view.setInt32(12, scale, true); // Scale\n data_view.setInt32(16, data_length, true); // Length\n data_view.setInt32(20, output_channels, true);\n for (var i = 0; i < length; i++) {\n var sample = 0;\n if (output_channels === 1) {\n for (var _channel = 0; _channel < channels.length; ++_channel) {\n sample += channels[_channel][i];\n }\n sample = Math.floor(range_max * sample * amplitude_scale / channels.length);\n if (sample < min_value[0]) {\n min_value[0] = sample;\n if (min_value[0] < range_min) {\n min_value[0] = range_min;\n }\n }\n if (sample > max_value[0]) {\n max_value[0] = sample;\n if (max_value[0] > range_max) {\n max_value[0] = range_max;\n }\n }\n } else {\n for (var _channel2 = 0; _channel2 < output_channels; ++_channel2) {\n sample = Math.floor(range_max * channels[_channel2][i] * amplitude_scale);\n if (sample < min_value[_channel2]) {\n min_value[_channel2] = sample;\n if (min_value[_channel2] < range_min) {\n min_value[_channel2] = range_min;\n }\n }\n if (sample > max_value[_channel2]) {\n max_value[_channel2] = sample;\n if (max_value[_channel2] > range_max) {\n max_value[_channel2] = range_max;\n }\n }\n }\n }\n if (++scale_counter === scale) {\n for (var _channel3 = 0; _channel3 < output_channels; _channel3++) {\n if (options.bits === 8) {\n data_view.setInt8(offset++, min_value[_channel3]);\n data_view.setInt8(offset++, max_value[_channel3]);\n } else {\n data_view.setInt16(offset, min_value[_channel3], true);\n data_view.setInt16(offset + 2, max_value[_channel3], true);\n offset += 4;\n }\n min_value[_channel3] = Infinity;\n max_value[_channel3] = -Infinity;\n }\n scale_counter = 0;\n }\n }\n if (scale_counter > 0) {\n for (var _channel4 = 0; _channel4 < output_channels; _channel4++) {\n if (options.bits === 8) {\n data_view.setInt8(offset++, min_value[_channel4]);\n data_view.setInt8(offset++, max_value[_channel4]);\n } else {\n data_view.setInt16(offset, min_value[_channel4], true);\n data_view.setInt16(offset + 2, max_value[_channel4], true);\n }\n }\n }\n return buffer;\n}\n\nfunction _typeof(o) {\n \"@babel/helpers - typeof\";\n\n return _typeof = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function (o) {\n return typeof o;\n } : function (o) {\n return o && \"function\" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? \"symbol\" : typeof o;\n }, _typeof(o);\n}\n\nfunction isJsonWaveformData(data) {\n return data && _typeof(data) === 'object' && 'sample_rate' in data && 'samples_per_pixel' in data && 'bits' in data && 'length' in data && 'data' in data;\n}\nfunction isBinaryWaveformData(data) {\n var isCompatible = data && _typeof(data) === 'object' && 'byteLength' in data;\n if (isCompatible) {\n var view = new DataView(data);\n var version = view.getInt32(0, true);\n if (version !== 1 && version !== 2) {\n throw new TypeError('WaveformData.create(): This waveform data version not supported');\n }\n }\n return isCompatible;\n}\nfunction convertJsonToBinary(data) {\n var waveformData = data.data;\n var channels = data.channels || 1;\n var header_size = 24; // version 2\n var bytes_per_sample = data.bits === 8 ? 1 : 2;\n var expected_length = data.length * 2 * channels;\n if (waveformData.length !== expected_length) {\n throw new Error('WaveformData.create(): Length mismatch in JSON waveform data');\n }\n var total_size = header_size + waveformData.length * bytes_per_sample;\n var array_buffer = new ArrayBuffer(total_size);\n var data_object = new DataView(array_buffer);\n data_object.setInt32(0, 2, true); // Version\n data_object.setUint32(4, data.bits === 8, true);\n data_object.setInt32(8, data.sample_rate, true);\n data_object.setInt32(12, data.samples_per_pixel, true);\n data_object.setInt32(16, data.length, true);\n data_object.setInt32(20, channels, true);\n var index = header_size;\n if (data.bits === 8) {\n for (var i = 0; i < waveformData.length; i++) {\n data_object.setInt8(index++, waveformData[i], true);\n }\n } else {\n for (var _i = 0; _i < waveformData.length; _i++) {\n data_object.setInt16(index, waveformData[_i], true);\n index += 2;\n }\n }\n return array_buffer;\n}\n\nfunction isNullOrUndefined(value) {\n return value === undefined || value === null;\n}\n\nfunction decodeBase64(base64, enableUnicode) {\n var binaryString = atob(base64);\n if (enableUnicode) {\n var binaryView = new Uint8Array(binaryString.length);\n for (var i = 0, n = binaryString.length; i < n; ++i) {\n binaryView[i] = binaryString.charCodeAt(i);\n }\n return String.fromCharCode.apply(null, new Uint16Array(binaryView.buffer));\n }\n return binaryString;\n}\n\nfunction createURL(base64, sourcemapArg, enableUnicodeArg) {\n var sourcemap = sourcemapArg === undefined ? null : sourcemapArg;\n var enableUnicode = enableUnicodeArg === undefined ? false : enableUnicodeArg;\n var source = decodeBase64(base64, enableUnicode);\n var start = source.indexOf('\\n', 10) + 1;\n var body = source.substring(start) + (sourcemap ? '\\/\\/# sourceMappingURL=' + sourcemap : '');\n var blob = new Blob([body], { type: 'application/javascript' });\n return URL.createObjectURL(blob);\n}\n\nfunction createBase64WorkerFactory(base64, sourcemapArg, enableUnicodeArg) {\n var url;\n return function WorkerFactory(options) {\n url = url || createURL(base64, sourcemapArg, enableUnicodeArg);\n return new Worker(url, options);\n };\n}\n\nvar WorkerFactory = /*#__PURE__*/createBase64WorkerFactory('Lyogcm9sbHVwLXBsdWdpbi13ZWItd29ya2VyLWxvYWRlciAqLwooZnVuY3Rpb24gKCkgewogICd1c2Ugc3RyaWN0JzsKCiAgLyoqCiAgICogQXVkaW9CdWZmZXItYmFzZWQgV2F2ZWZvcm1EYXRhIGdlbmVyYXRvcgogICAqCiAgICogQWRhcHRlZCBmcm9tIEJsb2NrRmlsZTo6Q2FsY1N1bW1hcnkgaW4gQXVkYWNpdHksIHdpdGggcGVybWlzc2lvbi4KICAgKiBTZWUgaHR0cHM6Ly9naXRodWIuY29tL2F1ZGFjaXR5L2F1ZGFjaXR5L2Jsb2IvCiAgICogICAxMTA4YzEzNzZjMDkxNjYxNjIzMzVmYWI0NzQzMDA4Y2JhNTdjNGVlL3NyYy9CbG9ja0ZpbGUuY3BwI0wxOTgKICAgKi8KCiAgdmFyIElOVDhfTUFYID0gMTI3OwogIHZhciBJTlQ4X01JTiA9IC0xMjg7CiAgdmFyIElOVDE2X01BWCA9IDMyNzY3OwogIHZhciBJTlQxNl9NSU4gPSAtMzI3Njg7CiAgZnVuY3Rpb24gY2FsY3VsYXRlV2F2ZWZvcm1EYXRhTGVuZ3RoKGF1ZGlvX3NhbXBsZV9jb3VudCwgc2NhbGUpIHsKICAgIHZhciBkYXRhX2xlbmd0aCA9IE1hdGguZmxvb3IoYXVkaW9fc2FtcGxlX2NvdW50IC8gc2NhbGUpOwogICAgdmFyIHNhbXBsZXNfcmVtYWluaW5nID0gYXVkaW9fc2FtcGxlX2NvdW50IC0gZGF0YV9sZW5ndGggKiBzY2FsZTsKICAgIGlmIChzYW1wbGVzX3JlbWFpbmluZyA+IDApIHsKICAgICAgZGF0YV9sZW5ndGgrKzsKICAgIH0KICAgIHJldHVybiBkYXRhX2xlbmd0aDsKICB9CiAgZnVuY3Rpb24gZ2VuZXJhdGVXYXZlZm9ybURhdGEob3B0aW9ucykgewogICAgdmFyIHNjYWxlID0gb3B0aW9ucy5zY2FsZTsKICAgIHZhciBhbXBsaXR1ZGVfc2NhbGUgPSBvcHRpb25zLmFtcGxpdHVkZV9zY2FsZTsKICAgIHZhciBzcGxpdF9jaGFubmVscyA9IG9wdGlvbnMuc3BsaXRfY2hhbm5lbHM7CiAgICB2YXIgbGVuZ3RoID0gb3B0aW9ucy5sZW5ndGg7CiAgICB2YXIgc2FtcGxlX3JhdGUgPSBvcHRpb25zLnNhbXBsZV9yYXRlOwogICAgdmFyIGNoYW5uZWxzID0gb3B0aW9ucy5jaGFubmVscy5tYXAoZnVuY3Rpb24gKGNoYW5uZWwpIHsKICAgICAgcmV0dXJuIG5ldyBGbG9hdDMyQXJyYXkoY2hhbm5lbCk7CiAgICB9KTsKICAgIHZhciBvdXRwdXRfY2hhbm5lbHMgPSBzcGxpdF9jaGFubmVscyA/IGNoYW5uZWxzLmxlbmd0aCA6IDE7CiAgICB2YXIgaGVhZGVyX3NpemUgPSAyNDsKICAgIHZhciBkYXRhX2xlbmd0aCA9IGNhbGN1bGF0ZVdhdmVmb3JtRGF0YUxlbmd0aChsZW5ndGgsIHNjYWxlKTsKICAgIHZhciBieXRlc19wZXJfc2FtcGxlID0gb3B0aW9ucy5iaXRzID09PSA4ID8gMSA6IDI7CiAgICB2YXIgdG90YWxfc2l6ZSA9IGhlYWRlcl9zaXplICsgZGF0YV9sZW5ndGggKiAyICogYnl0ZXNfcGVyX3NhbXBsZSAqIG91dHB1dF9jaGFubmVsczsKICAgIHZhciBidWZmZXIgPSBuZXcgQXJyYXlCdWZmZXIodG90YWxfc2l6ZSk7CiAgICB2YXIgZGF0YV92aWV3ID0gbmV3IERhdGFWaWV3KGJ1ZmZlcik7CiAgICB2YXIgc2NhbGVfY291bnRlciA9IDA7CiAgICB2YXIgb2Zmc2V0ID0gaGVhZGVyX3NpemU7CiAgICB2YXIgbWluX3ZhbHVlID0gbmV3IEFycmF5KG91dHB1dF9jaGFubmVscyk7CiAgICB2YXIgbWF4X3ZhbHVlID0gbmV3IEFycmF5KG91dHB1dF9jaGFubmVscyk7CiAgICBmb3IgKHZhciBjaGFubmVsID0gMDsgY2hhbm5lbCA8IG91dHB1dF9jaGFubmVsczsgY2hhbm5lbCsrKSB7CiAgICAgIG1pbl92YWx1ZVtjaGFubmVsXSA9IEluZmluaXR5OwogICAgICBtYXhfdmFsdWVbY2hhbm5lbF0gPSAtSW5maW5pdHk7CiAgICB9CiAgICB2YXIgcmFuZ2VfbWluID0gb3B0aW9ucy5iaXRzID09PSA4ID8gSU5UOF9NSU4gOiBJTlQxNl9NSU47CiAgICB2YXIgcmFuZ2VfbWF4ID0gb3B0aW9ucy5iaXRzID09PSA4ID8gSU5UOF9NQVggOiBJTlQxNl9NQVg7CiAgICBkYXRhX3ZpZXcuc2V0SW50MzIoMCwgMiwgdHJ1ZSk7IC8vIFZlcnNpb24KICAgIGRhdGFfdmlldy5zZXRVaW50MzIoNCwgb3B0aW9ucy5iaXRzID09PSA4LCB0cnVlKTsgLy8gSXMgOCBiaXQ/CiAgICBkYXRhX3ZpZXcuc2V0SW50MzIoOCwgc2FtcGxlX3JhdGUsIHRydWUpOyAvLyBTYW1wbGUgcmF0ZQogICAgZGF0YV92aWV3LnNldEludDMyKDEyLCBzY2FsZSwgdHJ1ZSk7IC8vIFNjYWxlCiAgICBkYXRhX3ZpZXcuc2V0SW50MzIoMTYsIGRhdGFfbGVuZ3RoLCB0cnVlKTsgLy8gTGVuZ3RoCiAgICBkYXRhX3ZpZXcuc2V0SW50MzIoMjAsIG91dHB1dF9jaGFubmVscywgdHJ1ZSk7CiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxlbmd0aDsgaSsrKSB7CiAgICAgIHZhciBzYW1wbGUgPSAwOwogICAgICBpZiAob3V0cHV0X2NoYW5uZWxzID09PSAxKSB7CiAgICAgICAgZm9yICh2YXIgX2NoYW5uZWwgPSAwOyBfY2hhbm5lbCA8IGNoYW5uZWxzLmxlbmd0aDsgKytfY2hhbm5lbCkgewogICAgICAgICAgc2FtcGxlICs9IGNoYW5uZWxzW19jaGFubmVsXVtpXTsKICAgICAgICB9CiAgICAgICAgc2FtcGxlID0gTWF0aC5mbG9vcihyYW5nZV9tYXggKiBzYW1wbGUgKiBhbXBsaXR1ZGVfc2NhbGUgLyBjaGFubmVscy5sZW5ndGgpOwogICAgICAgIGlmIChzYW1wbGUgPCBtaW5fdmFsdWVbMF0pIHsKICAgICAgICAgIG1pbl92YWx1ZVswXSA9IHNhbXBsZTsKICAgICAgICAgIGlmIChtaW5fdmFsdWVbMF0gPCByYW5nZV9taW4pIHsKICAgICAgICAgICAgbWluX3ZhbHVlWzBdID0gcmFuZ2VfbWluOwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgICBpZiAoc2FtcGxlID4gbWF4X3ZhbHVlWzBdKSB7CiAgICAgICAgICBtYXhfdmFsdWVbMF0gPSBzYW1wbGU7CiAgICAgICAgICBpZiAobWF4X3ZhbHVlWzBdID4gcmFuZ2VfbWF4KSB7CiAgICAgICAgICAgIG1heF92YWx1ZVswXSA9IHJhbmdlX21heDsKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIH0gZWxzZSB7CiAgICAgICAgZm9yICh2YXIgX2NoYW5uZWwyID0gMDsgX2NoYW5uZWwyIDwgb3V0cHV0X2NoYW5uZWxzOyArK19jaGFubmVsMikgewogICAgICAgICAgc2FtcGxlID0gTWF0aC5mbG9vcihyYW5nZV9tYXggKiBjaGFubmVsc1tfY2hhbm5lbDJdW2ldICogYW1wbGl0dWRlX3NjYWxlKTsKICAgICAgICAgIGlmIChzYW1wbGUgPCBtaW5fdmFsdWVbX2NoYW5uZWwyXSkgewogICAgICAgICAgICBtaW5fdmFsdWVbX2NoYW5uZWwyXSA9IHNhbXBsZTsKICAgICAgICAgICAgaWYgKG1pbl92YWx1ZVtfY2hhbm5lbDJdIDwgcmFuZ2VfbWluKSB7CiAgICAgICAgICAgICAgbWluX3ZhbHVlW19jaGFubmVsMl0gPSByYW5nZV9taW47CiAgICAgICAgICAgIH0KICAgICAgICAgIH0KICAgICAgICAgIGlmIChzYW1wbGUgPiBtYXhfdmFsdWVbX2NoYW5uZWwyXSkgewogICAgICAgICAgICBtYXhfdmFsdWVbX2NoYW5uZWwyXSA9IHNhbXBsZTsKICAgICAgICAgICAgaWYgKG1heF92YWx1ZVtfY2hhbm5lbDJdID4gcmFuZ2VfbWF4KSB7CiAgICAgICAgICAgICAgbWF4X3ZhbHVlW19jaGFubmVsMl0gPSByYW5nZV9tYXg7CiAgICAgICAgICAgIH0KICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIH0KICAgICAgaWYgKCsrc2NhbGVfY291bnRlciA9PT0gc2NhbGUpIHsKICAgICAgICBmb3IgKHZhciBfY2hhbm5lbDMgPSAwOyBfY2hhbm5lbDMgPCBvdXRwdXRfY2hhbm5lbHM7IF9jaGFubmVsMysrKSB7CiAgICAgICAgICBpZiAob3B0aW9ucy5iaXRzID09PSA4KSB7CiAgICAgICAgICAgIGRhdGFfdmlldy5zZXRJbnQ4KG9mZnNldCsrLCBtaW5fdmFsdWVbX2NoYW5uZWwzXSk7CiAgICAgICAgICAgIGRhdGFfdmlldy5zZXRJbnQ4KG9mZnNldCsrLCBtYXhfdmFsdWVbX2NoYW5uZWwzXSk7CiAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICBkYXRhX3ZpZXcuc2V0SW50MTYob2Zmc2V0LCBtaW5fdmFsdWVbX2NoYW5uZWwzXSwgdHJ1ZSk7CiAgICAgICAgICAgIGRhdGFfdmlldy5zZXRJbnQxNihvZmZzZXQgKyAyLCBtYXhfdmFsdWVbX2NoYW5uZWwzXSwgdHJ1ZSk7CiAgICAgICAgICAgIG9mZnNldCArPSA0OwogICAgICAgICAgfQogICAgICAgICAgbWluX3ZhbHVlW19jaGFubmVsM10gPSBJbmZpbml0eTsKICAgICAgICAgIG1heF92YWx1ZVtfY2hhbm5lbDNdID0gLUluZmluaXR5OwogICAgICAgIH0KICAgICAgICBzY2FsZV9jb3VudGVyID0gMDsKICAgICAgfQogICAgfQogICAgaWYgKHNjYWxlX2NvdW50ZXIgPiAwKSB7CiAgICAgIGZvciAodmFyIF9jaGFubmVsNCA9IDA7IF9jaGFubmVsNCA8IG91dHB1dF9jaGFubmVsczsgX2NoYW5uZWw0KyspIHsKICAgICAgICBpZiAob3B0aW9ucy5iaXRzID09PSA4KSB7CiAgICAgICAgICBkYXRhX3ZpZXcuc2V0SW50OChvZmZzZXQrKywgbWluX3ZhbHVlW19jaGFubmVsNF0pOwogICAgICAgICAgZGF0YV92aWV3LnNldEludDgob2Zmc2V0KyssIG1heF92YWx1ZVtfY2hhbm5lbDRdKTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgZGF0YV92aWV3LnNldEludDE2KG9mZnNldCwgbWluX3ZhbHVlW19jaGFubmVsNF0sIHRydWUpOwogICAgICAgICAgZGF0YV92aWV3LnNldEludDE2KG9mZnNldCArIDIsIG1heF92YWx1ZVtfY2hhbm5lbDRdLCB0cnVlKTsKICAgICAgICB9CiAgICAgIH0KICAgIH0KICAgIHJldHVybiBidWZmZXI7CiAgfQoKICBvbm1lc3NhZ2UgPSBmdW5jdGlvbiBvbm1lc3NhZ2UoZXZ0KSB7CiAgICB2YXIgYnVmZmVyID0gZ2VuZXJhdGVXYXZlZm9ybURhdGEoZXZ0LmRhdGEpOwoKICAgIC8vIFRyYW5zZmVyIGJ1ZmZlciB0byB0aGUgY2FsbGluZyB0aHJlYWQKICAgIHRoaXMucG9zdE1lc3NhZ2UoYnVmZmVyLCBbYnVmZmVyXSk7CiAgICB0aGlzLmNsb3NlKCk7CiAgfTsKCn0pKCk7Ci8vIyBzb3VyY2VNYXBwaW5nVVJMPXdhdmVmb3JtLWRhdGEtd29ya2VyLmpzLm1hcAoK', null, false);\n/* eslint-enable */\n\n/**\n * Provides access to waveform data.\n */\n\nfunction WaveformData(data) {\n if (isJsonWaveformData(data)) {\n data = convertJsonToBinary(data);\n }\n if (isBinaryWaveformData(data)) {\n this._data = new DataView(data);\n this._offset = this._version() === 2 ? 24 : 20;\n this._channels = [];\n for (var channel = 0; channel < this.channels; channel++) {\n this._channels[channel] = new WaveformDataChannel(this, channel);\n }\n } else {\n throw new TypeError('WaveformData.create(): Unknown data format');\n }\n}\nvar defaultOptions = {\n scale: 512,\n bits: 8,\n amplitude_scale: 1.0,\n split_channels: false,\n disable_worker: false\n};\nfunction getOptions(options) {\n var opts = {\n scale: options.scale || defaultOptions.scale,\n bits: options.bits || defaultOptions.bits,\n amplitude_scale: options.amplitude_scale || defaultOptions.amplitude_scale,\n split_channels: options.split_channels || defaultOptions.split_channels,\n disable_worker: options.disable_worker || defaultOptions.disable_worker\n };\n return opts;\n}\nfunction getChannelData(audio_buffer) {\n var channels = [];\n for (var i = 0; i < audio_buffer.numberOfChannels; ++i) {\n channels.push(audio_buffer.getChannelData(i).buffer);\n }\n return channels;\n}\nfunction createFromAudioBuffer(audio_buffer, options, callback) {\n var channels = getChannelData(audio_buffer);\n if (options.disable_worker) {\n var buffer = generateWaveformData({\n scale: options.scale,\n bits: options.bits,\n amplitude_scale: options.amplitude_scale,\n split_channels: options.split_channels,\n length: audio_buffer.length,\n sample_rate: audio_buffer.sampleRate,\n channels: channels\n });\n callback(undefined, new WaveformData(buffer), audio_buffer);\n } else {\n var worker = new WorkerFactory();\n worker.onmessage = function (evt) {\n callback(undefined, new WaveformData(evt.data), audio_buffer);\n };\n worker.postMessage({\n scale: options.scale,\n bits: options.bits,\n amplitude_scale: options.amplitude_scale,\n split_channels: options.split_channels,\n length: audio_buffer.length,\n sample_rate: audio_buffer.sampleRate,\n channels: channels\n }, channels);\n }\n}\nfunction createFromArrayBuffer(audioContext, audioData, options, callback) {\n // The following function is a workaround for a Webkit bug where decodeAudioData\n // invokes the errorCallback with null instead of a DOMException.\n // See https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-decodeaudiodata\n // and http://stackoverflow.com/q/10365335/103396\n\n function errorCallback(error) {\n if (!error) {\n error = new DOMException('EncodingError');\n }\n callback(error);\n // prevent double-calling the callback on errors:\n callback = function callback() {};\n }\n var promise = audioContext.decodeAudioData(audioData, function (audio_buffer) {\n createFromAudioBuffer(audio_buffer, options, callback);\n }, errorCallback);\n if (promise) {\n promise.catch(errorCallback);\n }\n}\n\n/**\n * Creates and returns a WaveformData instance from the given waveform data.\n */\n\nWaveformData.create = function create(data) {\n return new WaveformData(data);\n};\n\n/**\n * Creates a WaveformData instance from audio.\n */\n\nWaveformData.createFromAudio = function (options, callback) {\n var opts = getOptions(options);\n if (options.audio_context && options.array_buffer) {\n return createFromArrayBuffer(options.audio_context, options.array_buffer, opts, callback);\n } else if (options.audio_buffer) {\n return createFromAudioBuffer(options.audio_buffer, opts, callback);\n } else {\n throw new TypeError(\n // eslint-disable-next-line\n 'WaveformData.createFromAudio(): Pass either an AudioContext and ArrayBuffer, or an AudioBuffer object');\n }\n};\nfunction WaveformResampler(options) {\n this._inputData = options.waveformData;\n\n // Scale we want to reach\n this._output_samples_per_pixel = options.scale;\n this._scale = this._inputData.scale; // scale we are coming from\n\n // The amount of data we want to resample i.e. final zoom want to resample\n // all data but for intermediate zoom we want to resample subset\n this._input_buffer_size = this._inputData.length;\n var input_buffer_length_samples = this._input_buffer_size * this._inputData.scale;\n var output_buffer_length_samples = Math.ceil(input_buffer_length_samples / this._output_samples_per_pixel);\n var output_header_size = 24; // version 2\n var bytes_per_sample = this._inputData.bits === 8 ? 1 : 2;\n var total_size = output_header_size + output_buffer_length_samples * 2 * this._inputData.channels * bytes_per_sample;\n this._output_data = new ArrayBuffer(total_size);\n this.output_dataview = new DataView(this._output_data);\n this.output_dataview.setInt32(0, 2, true); // Version\n this.output_dataview.setUint32(4, this._inputData.bits === 8, true); // Is 8 bit?\n this.output_dataview.setInt32(8, this._inputData.sample_rate, true);\n this.output_dataview.setInt32(12, this._output_samples_per_pixel, true);\n this.output_dataview.setInt32(16, output_buffer_length_samples, true);\n this.output_dataview.setInt32(20, this._inputData.channels, true);\n this._outputWaveformData = new WaveformData(this._output_data);\n this._input_index = 0;\n this._output_index = 0;\n var channels = this._inputData.channels;\n this._min = new Array(channels);\n this._max = new Array(channels);\n for (var channel = 0; channel < channels; ++channel) {\n if (this._input_buffer_size > 0) {\n this._min[channel] = this._inputData.channel(channel).min_sample(this._input_index);\n this._max[channel] = this._inputData.channel(channel).max_sample(this._input_index);\n } else {\n this._min[channel] = 0;\n this._max[channel] = 0;\n }\n }\n this._min_value = this._inputData.bits === 8 ? -128 : -32768;\n this._max_value = this._inputData.bits === 8 ? 127 : 32767;\n this._where = 0;\n this._prev_where = 0;\n this._stop = 0;\n this._last_input_index = 0;\n}\nWaveformResampler.prototype.sample_at_pixel = function (x) {\n return Math.floor(x * this._output_samples_per_pixel);\n};\nWaveformResampler.prototype.next = function () {\n var count = 0;\n var total = 1000;\n var channels = this._inputData.channels;\n var channel;\n while (this._input_index < this._input_buffer_size && count < total) {\n while (Math.floor(this.sample_at_pixel(this._output_index) / this._scale) === this._input_index) {\n if (this._output_index > 0) {\n for (var i = 0; i < channels; ++i) {\n channel = this._outputWaveformData.channel(i);\n channel.set_min_sample(this._output_index - 1, this._min[i]);\n channel.set_max_sample(this._output_index - 1, this._max[i]);\n }\n }\n this._last_input_index = this._input_index;\n this._output_index++;\n this._where = this.sample_at_pixel(this._output_index);\n this._prev_where = this.sample_at_pixel(this._output_index - 1);\n if (this._where !== this._prev_where) {\n for (var _i = 0; _i < channels; ++_i) {\n this._min[_i] = this._max_value;\n this._max[_i] = this._min_value;\n }\n }\n }\n this._where = this.sample_at_pixel(this._output_index);\n this._stop = Math.floor(this._where / this._scale);\n if (this._stop > this._input_buffer_size) {\n this._stop = this._input_buffer_size;\n }\n while (this._input_index < this._stop) {\n for (var _i2 = 0; _i2 < channels; ++_i2) {\n channel = this._inputData.channel(_i2);\n var value = channel.min_sample(this._input_index);\n if (value < this._min[_i2]) {\n this._min[_i2] = value;\n }\n value = channel.max_sample(this._input_index);\n if (value > this._max[_i2]) {\n this._max[_i2] = value;\n }\n }\n this._input_index++;\n }\n count++;\n }\n if (this._input_index < this._input_buffer_size) {\n // More to do\n return false;\n } else {\n // Done\n if (this._input_index !== this._last_input_index) {\n for (var _i3 = 0; _i3 < channels; ++_i3) {\n channel = this._outputWaveformData.channel(_i3);\n channel.set_min_sample(this._output_index - 1, this._min[_i3]);\n channel.set_max_sample(this._output_index - 1, this._max[_i3]);\n }\n }\n return true;\n }\n};\nWaveformResampler.prototype.getOutputData = function () {\n return this._output_data;\n};\nWaveformData.prototype = {\n _getResampleOptions: function _getResampleOptions(options) {\n var opts = {};\n opts.scale = options.scale;\n opts.width = options.width;\n if (!isNullOrUndefined(opts.width) && (typeof opts.width !== 'number' || opts.width <= 0)) {\n throw new RangeError('WaveformData.resample(): width should be a positive integer value');\n }\n if (!isNullOrUndefined(opts.scale) && (typeof opts.scale !== 'number' || opts.scale <= 0)) {\n throw new RangeError('WaveformData.resample(): scale should be a positive integer value');\n }\n if (!opts.scale && !opts.width) {\n throw new Error('WaveformData.resample(): Missing scale or width option');\n }\n if (opts.width) {\n // Calculate the target scale for the resampled waveform\n opts.scale = Math.floor(this.duration * this.sample_rate / opts.width);\n }\n if (opts.scale < this.scale) {\n throw new Error('WaveformData.resample(): Zoom level ' + opts.scale + ' too low, minimum: ' + this.scale);\n }\n opts.abortSignal = options.abortSignal;\n return opts;\n },\n resample: function resample(options) {\n options = this._getResampleOptions(options);\n options.waveformData = this;\n var resampler = new WaveformResampler(options);\n while (!resampler.next()) {\n // nothing\n }\n return new WaveformData(resampler.getOutputData());\n },\n /**\n * Concatenates with one or more other waveforms, returning a new WaveformData object.\n */\n\n concat: function concat() {\n var self = this;\n var otherWaveforms = Array.prototype.slice.call(arguments);\n\n // Check that all the supplied waveforms are compatible\n otherWaveforms.forEach(function (otherWaveform) {\n if (self.channels !== otherWaveform.channels || self.sample_rate !== otherWaveform.sample_rate || self.bits !== otherWaveform.bits || self.scale !== otherWaveform.scale) {\n throw new Error('WaveformData.concat(): Waveforms are incompatible');\n }\n });\n var combinedBuffer = this._concatBuffers.apply(this, otherWaveforms);\n return WaveformData.create(combinedBuffer);\n },\n /**\n * Returns a new ArrayBuffer with the concatenated waveform.\n * All waveforms must have identical metadata (version, channels, etc)\n */\n\n _concatBuffers: function _concatBuffers() {\n var otherWaveforms = Array.prototype.slice.call(arguments);\n var headerSize = this._offset;\n var totalSize = headerSize;\n var totalDataLength = 0;\n var bufferCollection = [this].concat(otherWaveforms).map(function (w) {\n return w._data.buffer;\n });\n for (var i = 0; i < bufferCollection.length; i++) {\n var buffer = bufferCollection[i];\n var dataSize = new DataView(buffer).getInt32(16, true);\n totalSize += buffer.byteLength - headerSize;\n totalDataLength += dataSize;\n }\n var totalBuffer = new ArrayBuffer(totalSize);\n var sourceHeader = new DataView(bufferCollection[0]);\n var totalBufferView = new DataView(totalBuffer);\n\n // Copy the header from the first chunk\n for (var _i4 = 0; _i4 < headerSize; _i4++) {\n totalBufferView.setUint8(_i4, sourceHeader.getUint8(_i4));\n }\n\n // Rewrite the data-length header item to reflect all of the samples concatenated together\n totalBufferView.setInt32(16, totalDataLength, true);\n var offset = 0;\n var dataOfTotalBuffer = new Uint8Array(totalBuffer, headerSize);\n for (var _i5 = 0; _i5 < bufferCollection.length; _i5++) {\n var _buffer = bufferCollection[_i5];\n dataOfTotalBuffer.set(new Uint8Array(_buffer, headerSize), offset);\n offset += _buffer.byteLength - headerSize;\n }\n return totalBuffer;\n },\n slice: function slice(options) {\n var startIndex = 0;\n var endIndex = 0;\n if (!isNullOrUndefined(options.startIndex) && !isNullOrUndefined(options.endIndex)) {\n startIndex = options.startIndex;\n endIndex = options.endIndex;\n } else if (!isNullOrUndefined(options.startTime) && !isNullOrUndefined(options.endTime)) {\n startIndex = this.at_time(options.startTime);\n endIndex = this.at_time(options.endTime);\n }\n if (startIndex < 0) {\n throw new RangeError('startIndex or startTime must not be negative');\n }\n if (endIndex < 0) {\n throw new RangeError('endIndex or endTime must not be negative');\n }\n if (startIndex > this.length) {\n startIndex = this.length;\n }\n if (endIndex > this.length) {\n endIndex = this.length;\n }\n if (startIndex > endIndex) {\n startIndex = endIndex;\n }\n var length = endIndex - startIndex;\n var header_size = 24; // Version 2\n var bytes_per_sample = this.bits === 8 ? 1 : 2;\n var total_size = header_size + length * 2 * this.channels * bytes_per_sample;\n var output_data = new ArrayBuffer(total_size);\n var output_dataview = new DataView(output_data);\n output_dataview.setInt32(0, 2, true); // Version\n output_dataview.setUint32(4, this.bits === 8, true); // Is 8 bit?\n output_dataview.setInt32(8, this.sample_rate, true);\n output_dataview.setInt32(12, this.scale, true);\n output_dataview.setInt32(16, length, true);\n output_dataview.setInt32(20, this.channels, true);\n for (var i = 0; i < length * this.channels * 2; i++) {\n var sample = this._at(startIndex * this.channels * 2 + i);\n if (this.bits === 8) {\n output_dataview.setInt8(header_size + i, sample);\n } else {\n output_dataview.setInt16(header_size + i * 2, sample, true);\n }\n }\n return new WaveformData(output_data);\n },\n /**\n * Returns the data format version number.\n */\n\n _version: function _version() {\n return this._data.getInt32(0, true);\n },\n /**\n * Returns the length of the waveform, in pixels.\n */\n\n get length() {\n return this._data.getUint32(16, true);\n },\n /**\n * Returns the number of bits per sample, either 8 or 16.\n */\n\n get bits() {\n var bits = Boolean(this._data.getUint32(4, true));\n return bits ? 8 : 16;\n },\n /**\n * Returns the (approximate) duration of the audio file, in seconds.\n */\n\n get duration() {\n return this.length * this.scale / this.sample_rate;\n },\n /**\n * Returns the number of pixels per second.\n */\n\n get pixels_per_second() {\n return this.sample_rate / this.scale;\n },\n /**\n * Returns the amount of time represented by a single pixel, in seconds.\n */\n\n get seconds_per_pixel() {\n return this.scale / this.sample_rate;\n },\n /**\n * Returns the number of waveform channels.\n */\n\n get channels() {\n if (this._version() === 2) {\n return this._data.getInt32(20, true);\n } else {\n return 1;\n }\n },\n /**\n * Returns a waveform channel.\n */\n\n channel: function channel(index) {\n if (index >= 0 && index < this._channels.length) {\n return this._channels[index];\n } else {\n throw new RangeError('Invalid channel: ' + index);\n }\n },\n /**\n * Returns the number of audio samples per second.\n */\n\n get sample_rate() {\n return this._data.getInt32(8, true);\n },\n /**\n * Returns the number of audio samples per pixel.\n */\n\n get scale() {\n return this._data.getInt32(12, true);\n },\n /**\n * Returns a waveform data value at a specific offset.\n */\n\n _at: function at_sample(index) {\n if (this.bits === 8) {\n return this._data.getInt8(this._offset + index);\n } else {\n return this._data.getInt16(this._offset + index * 2, true);\n }\n },\n /**\n * Sets a waveform data value at a specific offset.\n */\n\n _set_at: function set_at(index, sample) {\n if (this.bits === 8) {\n return this._data.setInt8(this._offset + index, sample);\n } else {\n return this._data.setInt16(this._offset + index * 2, sample, true);\n }\n },\n /**\n * Returns the waveform data index position for a given time.\n */\n\n at_time: function at_time(time) {\n return Math.floor(time * this.sample_rate / this.scale);\n },\n /**\n * Returns the time in seconds for a given index.\n */\n\n time: function time(index) {\n return index * this.scale / this.sample_rate;\n },\n /**\n * Returns an object containing the waveform data.\n */\n\n toJSON: function toJSON() {\n var waveform = {\n version: 2,\n channels: this.channels,\n sample_rate: this.sample_rate,\n samples_per_pixel: this.scale,\n bits: this.bits,\n length: this.length,\n data: []\n };\n for (var i = 0; i < this.length; i++) {\n for (var channel = 0; channel < this.channels; channel++) {\n waveform.data.push(this.channel(channel).min_sample(i));\n waveform.data.push(this.channel(channel).max_sample(i));\n }\n }\n return waveform;\n },\n /**\n * Returns the waveform data in binary format as an ArrayBuffer.\n */\n\n toArrayBuffer: function toArrayBuffer() {\n return this._data.buffer;\n }\n};\n\nexport { WaveformData as default };\n","/**\n * Waveform Data Loader\n *\n * Utilities for loading pre-computed waveform data in waveform-data.js format.\n * Supports both binary (.dat) and JSON formats from BBC's audiowaveform tool.\n */\n\nimport WaveformData from 'waveform-data';\nimport type { PeakData, Peaks } from '@waveform-playlist/core';\n\n/**\n * Load waveform data from a .dat or .json file\n *\n * @param src - URL to waveform data file (.dat or .json)\n * @returns WaveformData instance\n */\nexport async function loadWaveformData(src: string): Promise<WaveformData> {\n const response = await fetch(src);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch waveform data: ${response.statusText}`);\n }\n\n // Check file extension to determine format\n const isBinary = src.endsWith('.dat');\n\n if (isBinary) {\n const arrayBuffer = await response.arrayBuffer();\n return WaveformData.create(arrayBuffer);\n } else {\n const json = await response.json();\n return WaveformData.create(json);\n }\n}\n\n/**\n * Convert WaveformData to our internal Peaks format\n *\n * @param waveformData - WaveformData instance from waveform-data.js\n * @param channelIndex - Channel index (0 for mono/left, 1 for right)\n * @returns Peaks data with alternating min/max values, preserving original bit depth\n */\nexport function waveformDataToPeaks(\n waveformData: WaveformData,\n channelIndex: number = 0\n): { data: Int8Array | Int16Array; bits: 8 | 16; length: number; sampleRate: number } {\n const channel = waveformData.channel(channelIndex);\n const bits = waveformData.bits as 8 | 16;\n\n // Get the min/max arrays to determine length\n const minArray = channel.min_array();\n const maxArray = channel.max_array();\n const length = minArray.length;\n\n // Use appropriate typed array based on source file bit depth\n // 8-bit: values range from -128 to 127\n // 16-bit: values range from -32768 to 32767\n const peaks = bits === 8\n ? new Int8Array(length * 2)\n : new Int16Array(length * 2);\n\n // Interleave min/max pairs\n for (let i = 0; i < length; i++) {\n peaks[i * 2] = minArray[i];\n peaks[i * 2 + 1] = maxArray[i];\n }\n\n return {\n data: peaks,\n bits,\n length,\n sampleRate: waveformData.sample_rate,\n };\n}\n\n/**\n * Load waveform data file and convert to Peaks format in one step\n *\n * @param src - URL to waveform data file (.dat or .json)\n * @param channelIndex - Channel index (default: 0)\n * @returns Peaks data ready for rendering\n */\nexport async function loadPeaksFromWaveformData(\n src: string,\n channelIndex: number = 0\n): Promise<{ data: Int8Array | Int16Array; bits: 8 | 16; length: number; sampleRate: number }> {\n const waveformData = await loadWaveformData(src);\n return waveformDataToPeaks(waveformData, channelIndex);\n}\n\n/**\n * Get metadata from waveform data file without converting to peaks\n *\n * @param src - URL to waveform data file\n * @returns Metadata (sample rate, channels, duration, bits, etc.)\n */\nexport async function getWaveformDataMetadata(src: string): Promise<{\n sampleRate: number;\n channels: number;\n duration: number;\n samplesPerPixel: number;\n length: number;\n bits: 8 | 16;\n}> {\n const waveformData = await loadWaveformData(src);\n\n return {\n sampleRate: waveformData.sample_rate,\n channels: waveformData.channels,\n duration: waveformData.duration,\n samplesPerPixel: waveformData.scale,\n length: waveformData.length,\n bits: waveformData.bits as 8 | 16,\n };\n}\n\n/**\n * Extract peaks from a WaveformData object at a specific scale (samplesPerPixel)\n * and optionally slice to a sample range.\n *\n * @param waveformData - WaveformData instance from waveform-data.js\n * @param samplesPerPixel - Target samples per pixel (will resample if different)\n * @param channelIndex - Channel index (default: 0)\n * @param offsetSamples - Optional start offset in samples (for clip trimming)\n * @param durationSamples - Optional duration in samples (for clip trimming)\n * @returns Peaks data ready for rendering\n */\nexport function extractPeaksFromWaveformData(\n waveformData: WaveformData,\n samplesPerPixel: number,\n channelIndex: number = 0,\n offsetSamples?: number,\n durationSamples?: number\n): { data: Int8Array | Int16Array; bits: 8 | 16; length: number } {\n let processedData = waveformData;\n\n // Slice if offset/duration specified (using index-based slicing for sample accuracy)\n if (offsetSamples !== undefined && durationSamples !== undefined) {\n // Convert samples to waveform data indices\n // waveformData.scale is the samples per pixel of the source data\n const sourceScale = waveformData.scale;\n const startIndex = Math.floor(offsetSamples / sourceScale);\n const endIndex = Math.ceil((offsetSamples + durationSamples) / sourceScale);\n processedData = processedData.slice({ startIndex, endIndex });\n }\n\n // Resample to target scale if different\n if (processedData.scale !== samplesPerPixel) {\n processedData = processedData.resample({ scale: samplesPerPixel });\n }\n\n // Convert to our peaks format\n const channel = processedData.channel(channelIndex);\n const bits = processedData.bits as 8 | 16;\n const minArray = channel.min_array();\n const maxArray = channel.max_array();\n const length = minArray.length;\n\n const peaks = bits === 8\n ? new Int8Array(length * 2)\n : new Int16Array(length * 2);\n\n for (let i = 0; i < length; i++) {\n peaks[i * 2] = minArray[i];\n peaks[i * 2 + 1] = maxArray[i];\n }\n\n return { data: peaks, bits, length };\n}\n\n/**\n * Extract peaks from a WaveformData object, handling ALL channels, mono merging,\n * slicing, and resampling.\n *\n * Bit depth is determined by the WaveformData source — all typed arrays match\n * the source's bit depth for consistent data/metadata.\n *\n * @param waveformData - WaveformData instance (should be generated with split_channels: true)\n * @param samplesPerPixel - Target samples per pixel\n * @param isMono - Whether to merge channels to mono\n * @param offsetSamples - Optional start offset in samples (for clip trimming)\n * @param durationSamples - Optional duration in samples (for clip trimming)\n * @returns PeakData matching the interface from @waveform-playlist/core\n */\nexport function extractPeaksFromWaveformDataFull(\n waveformData: WaveformData,\n samplesPerPixel: number,\n isMono: boolean,\n offsetSamples?: number,\n durationSamples?: number,\n): PeakData {\n let processedData = waveformData;\n\n // Slice if offset/duration specified\n if (offsetSamples !== undefined && durationSamples !== undefined) {\n const sourceScale = waveformData.scale;\n const startIndex = Math.floor(offsetSamples / sourceScale);\n const endIndex = Math.ceil((offsetSamples + durationSamples) / sourceScale);\n processedData = processedData.slice({ startIndex, endIndex });\n }\n\n // Resample to target scale if different\n if (processedData.scale !== samplesPerPixel) {\n processedData = processedData.resample({ scale: samplesPerPixel });\n }\n\n const numChannels = processedData.channels;\n const bits = processedData.bits as 8 | 16;\n\n // Extract peaks for all channels\n const channelPeaks: Peaks[] = [];\n for (let c = 0; c < numChannels; c++) {\n const channel = processedData.channel(c);\n const minArray = channel.min_array();\n const maxArray = channel.max_array();\n const len = minArray.length;\n\n const peaks: Peaks = bits === 8\n ? new Int8Array(len * 2)\n : new Int16Array(len * 2);\n\n for (let i = 0; i < len; i++) {\n peaks[i * 2] = minArray[i];\n peaks[i * 2 + 1] = maxArray[i];\n }\n channelPeaks.push(peaks);\n }\n\n // Handle mono merging (same algorithm as makeMono in webaudio-peaks)\n if (isMono && channelPeaks.length > 1) {\n const weight = 1 / channelPeaks.length;\n const numPeaks = channelPeaks[0].length / 2;\n const monoPeaks: Peaks = bits === 8\n ? new Int8Array(numPeaks * 2)\n : new Int16Array(numPeaks * 2);\n\n for (let i = 0; i < numPeaks; i++) {\n let min = 0;\n let max = 0;\n for (let c = 0; c < channelPeaks.length; c++) {\n min += weight * channelPeaks[c][i * 2];\n max += weight * channelPeaks[c][i * 2 + 1];\n }\n monoPeaks[i * 2] = min;\n monoPeaks[i * 2 + 1] = max;\n }\n\n return {\n length: numPeaks,\n data: [monoPeaks],\n bits,\n };\n }\n\n const peakLength = channelPeaks.length > 0 ? channelPeaks[0].length / 2 : 0;\n\n return {\n length: peakLength,\n data: channelPeaks,\n bits,\n };\n}\n","import { useState } from 'react';\nimport { formatTime as formatTimeUtil, parseTime as parseTimeUtil, type TimeFormat } from '@waveform-playlist/ui-components';\n\nexport interface TimeFormatControls {\n timeFormat: TimeFormat;\n setTimeFormat: (format: TimeFormat) => void;\n formatTime: (seconds: number) => string;\n parseTime: (timeString: string) => number;\n}\n\n/**\n * Hook to manage time format state\n *\n * @example\n * ```tsx\n * const { timeFormat, setTimeFormat, formatTime, parseTime } = useTimeFormat();\n *\n * <TimeFormatSelect\n * value={timeFormat}\n * onChange={setTimeFormat}\n * />\n * <span>{formatTime(currentTime)}</span>\n * <input onChange={(e) => seekTo(parseTime(e.target.value))} />\n * ```\n */\nexport function useTimeFormat(): TimeFormatControls {\n const [timeFormat, setTimeFormat] = useState<TimeFormat>('hh:mm:ss.uuu');\n\n const formatTime = (seconds: number) => {\n return formatTimeUtil(seconds, timeFormat);\n };\n\n const parseTime = (timeString: string) => {\n return parseTimeUtil(timeString, timeFormat);\n };\n\n return {\n timeFormat,\n setTimeFormat,\n formatTime,\n parseTime,\n };\n}\n","import { useState, useCallback, startTransition } from 'react';\n\nexport interface ZoomControls {\n samplesPerPixel: number;\n zoomIn: () => void;\n zoomOut: () => void;\n canZoomIn: boolean;\n canZoomOut: boolean;\n}\n\nexport interface UseZoomControlsProps {\n initialSamplesPerPixel: number;\n zoomLevels?: number[]; // Array of samples per pixel values (lower = more zoomed in)\n}\n\nconst DEFAULT_ZOOM_LEVELS = [256, 512, 1024, 2048, 4096, 8192];\n\nexport function useZoomControls({\n initialSamplesPerPixel,\n zoomLevels = DEFAULT_ZOOM_LEVELS,\n}: UseZoomControlsProps): ZoomControls {\n const [zoomIndex, setZoomIndex] = useState(() => {\n const index = zoomLevels.indexOf(initialSamplesPerPixel);\n return index !== -1 ? index : Math.floor(zoomLevels.length / 2);\n });\n\n const samplesPerPixel = zoomLevels[zoomIndex];\n const canZoomIn = zoomIndex > 0;\n const canZoomOut = zoomIndex < zoomLevels.length - 1;\n\n // Wrap zoom state changes in startTransition so React treats them as\n // non-urgent. During playback, this allows animation RAF callbacks to\n // interleave with the zoom re-render. Rapid consecutive zooms are\n // batched — only the final level triggers peak recalculation.\n const zoomIn = useCallback(() => {\n startTransition(() => {\n setZoomIndex((prev) => Math.max(0, prev - 1));\n });\n }, []);\n\n const zoomOut = useCallback(() => {\n startTransition(() => {\n setZoomIndex((prev) => Math.min(zoomLevels.length - 1, prev + 1));\n });\n }, [zoomLevels.length]);\n\n return {\n samplesPerPixel,\n zoomIn,\n zoomOut,\n canZoomIn,\n canZoomOut,\n };\n}\n","import { useState, useCallback, RefObject } from 'react';\nimport { TonePlayout } from '@waveform-playlist/playout';\n\nexport interface UseMasterVolumeProps {\n playoutRef: RefObject<TonePlayout | null>;\n initialVolume?: number; // 0-1.0 (linear gain, consistent with Web Audio API)\n onVolumeChange?: (volume: number) => void;\n}\n\nexport interface MasterVolumeControls {\n masterVolume: number;\n setMasterVolume: (volume: number) => void;\n}\n\n/**\n * Hook for managing master volume control\n *\n * @example\n * ```tsx\n * const { masterVolume, setMasterVolume } = useMasterVolume({\n * playoutRef,\n * initialVolume: 1.0,\n * });\n *\n * <MasterVolumeControl\n * volume={masterVolume}\n * onChange={setMasterVolume}\n * />\n * ```\n */\nexport function useMasterVolume({\n playoutRef,\n initialVolume = 1.0,\n onVolumeChange,\n}: UseMasterVolumeProps): MasterVolumeControls {\n const [masterVolume, setMasterVolumeState] = useState(initialVolume);\n\n const setMasterVolume = useCallback((volume: number) => {\n setMasterVolumeState(volume);\n\n // Update the playout with linear gain (0-1.0 range)\n if (playoutRef.current) {\n playoutRef.current.setMasterGain(volume);\n }\n\n // Call optional callback\n onVolumeChange?.(volume);\n }, [playoutRef, onVolumeChange]);\n\n return {\n masterVolume,\n setMasterVolume,\n };\n}\n","import { useRef, useCallback } from 'react';\nimport type { EffectsFunction } from '@waveform-playlist/playout';\n// Import Tone.js classes directly for tree-shaking\nimport { Analyser } from 'tone';\n\n/**\n * Hook for master effects with frequency analyzer\n * Returns the analyser ref and the effects function to pass to WaveformPlaylistProvider\n *\n * For more advanced effects (reverb, delay, filters, etc.), use useDynamicEffects instead.\n */\nexport const useMasterAnalyser = (fftSize: number = 256) => {\n const analyserRef = useRef<Analyser | null>(null);\n\n const masterEffects: EffectsFunction = useCallback((masterGainNode, destination, _isOffline) => {\n // Create analyser and connect it in parallel to monitor the output\n const analyserNode = new Analyser('fft', fftSize);\n masterGainNode.connect(analyserNode);\n\n // Connect master to destination as normal\n masterGainNode.connect(destination);\n\n // Store analyser for visualization\n analyserRef.current = analyserNode;\n\n return function cleanup() {\n // Cleanup when playlist is destroyed\n analyserNode.dispose();\n analyserRef.current = null;\n };\n }, [fftSize]);\n\n return { analyserRef, masterEffects };\n};\n","// src/types/clip.ts\nfunction createClip(options) {\n const {\n audioBuffer,\n startSample,\n offsetSamples = 0,\n gain = 1,\n name,\n color,\n fadeIn,\n fadeOut,\n waveformData\n } = options;\n const sampleRate = audioBuffer?.sampleRate ?? options.sampleRate ?? waveformData?.sample_rate;\n const sourceDurationSamples = audioBuffer?.length ?? options.sourceDurationSamples ?? (waveformData && sampleRate ? Math.ceil(waveformData.duration * sampleRate) : void 0);\n if (sampleRate === void 0) {\n throw new Error(\"createClip: sampleRate is required when audioBuffer is not provided (can use waveformData.sample_rate)\");\n }\n if (sourceDurationSamples === void 0) {\n throw new Error(\"createClip: sourceDurationSamples is required when audioBuffer is not provided (can use waveformData.duration)\");\n }\n if (audioBuffer && waveformData && audioBuffer.sampleRate !== waveformData.sample_rate) {\n console.warn(\n `Sample rate mismatch: audioBuffer (${audioBuffer.sampleRate}) vs waveformData (${waveformData.sample_rate}). Using audioBuffer sample rate. Waveform visualization may be slightly off.`\n );\n }\n const durationSamples = options.durationSamples ?? sourceDurationSamples;\n return {\n id: generateId(),\n audioBuffer,\n startSample,\n durationSamples,\n offsetSamples,\n sampleRate,\n sourceDurationSamples,\n gain,\n name,\n color,\n fadeIn,\n fadeOut,\n waveformData\n };\n}\nfunction createClipFromSeconds(options) {\n const {\n audioBuffer,\n startTime,\n offset = 0,\n gain = 1,\n name,\n color,\n fadeIn,\n fadeOut,\n waveformData\n } = options;\n const sampleRate = audioBuffer?.sampleRate ?? options.sampleRate ?? waveformData?.sample_rate;\n if (sampleRate === void 0) {\n throw new Error(\"createClipFromSeconds: sampleRate is required when audioBuffer is not provided (can use waveformData.sample_rate)\");\n }\n const sourceDuration = audioBuffer?.duration ?? options.sourceDuration ?? waveformData?.duration;\n if (sourceDuration === void 0) {\n throw new Error(\"createClipFromSeconds: sourceDuration is required when audioBuffer is not provided (can use waveformData.duration)\");\n }\n if (audioBuffer && waveformData && audioBuffer.sampleRate !== waveformData.sample_rate) {\n console.warn(\n `Sample rate mismatch: audioBuffer (${audioBuffer.sampleRate}) vs waveformData (${waveformData.sample_rate}). Using audioBuffer sample rate. Waveform visualization may be slightly off.`\n );\n }\n const duration = options.duration ?? sourceDuration;\n return createClip({\n audioBuffer,\n startSample: Math.round(startTime * sampleRate),\n durationSamples: Math.round(duration * sampleRate),\n offsetSamples: Math.round(offset * sampleRate),\n sampleRate,\n sourceDurationSamples: Math.ceil(sourceDuration * sampleRate),\n gain,\n name,\n color,\n fadeIn,\n fadeOut,\n waveformData\n });\n}\nfunction createTrack(options) {\n const {\n name,\n clips = [],\n muted = false,\n soloed = false,\n volume = 1,\n pan = 0,\n color,\n height,\n spectrogramConfig,\n spectrogramColorMap\n } = options;\n return {\n id: generateId(),\n name,\n clips,\n muted,\n soloed,\n volume,\n pan,\n color,\n height,\n spectrogramConfig,\n spectrogramColorMap\n };\n}\nfunction createTimeline(tracks, sampleRate = 44100, options) {\n const durationSamples = tracks.reduce((maxSamples, track) => {\n const trackSamples = track.clips.reduce((max, clip) => {\n return Math.max(max, clip.startSample + clip.durationSamples);\n }, 0);\n return Math.max(maxSamples, trackSamples);\n }, 0);\n const duration = durationSamples / sampleRate;\n return {\n tracks,\n duration,\n sampleRate,\n name: options?.name,\n tempo: options?.tempo,\n timeSignature: options?.timeSignature\n };\n}\nfunction generateId() {\n return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n}\nfunction getClipsInRange(track, startSample, endSample) {\n return track.clips.filter((clip) => {\n const clipEnd = clip.startSample + clip.durationSamples;\n return clip.startSample < endSample && clipEnd > startSample;\n });\n}\nfunction getClipsAtSample(track, sample) {\n return track.clips.filter((clip) => {\n const clipEnd = clip.startSample + clip.durationSamples;\n return sample >= clip.startSample && sample < clipEnd;\n });\n}\nfunction clipsOverlap(clip1, clip2) {\n const clip1End = clip1.startSample + clip1.durationSamples;\n const clip2End = clip2.startSample + clip2.durationSamples;\n return clip1.startSample < clip2End && clip1End > clip2.startSample;\n}\nfunction sortClipsByTime(clips) {\n return [...clips].sort((a, b) => a.startSample - b.startSample);\n}\nfunction findGaps(track) {\n if (track.clips.length === 0) return [];\n const sorted = sortClipsByTime(track.clips);\n const gaps = [];\n for (let i = 0; i < sorted.length - 1; i++) {\n const currentClipEnd = sorted[i].startSample + sorted[i].durationSamples;\n const nextClipStart = sorted[i + 1].startSample;\n if (nextClipStart > currentClipEnd) {\n gaps.push({\n startSample: currentClipEnd,\n endSample: nextClipStart,\n durationSamples: nextClipStart - currentClipEnd\n });\n }\n }\n return gaps;\n}\n\n// src/types/index.ts\nvar InteractionState = /* @__PURE__ */ ((InteractionState2) => {\n InteractionState2[\"Cursor\"] = \"cursor\";\n InteractionState2[\"Select\"] = \"select\";\n InteractionState2[\"Shift\"] = \"shift\";\n InteractionState2[\"FadeIn\"] = \"fadein\";\n InteractionState2[\"FadeOut\"] = \"fadeout\";\n return InteractionState2;\n})(InteractionState || {});\n\n// src/utils/conversions.ts\nfunction samplesToSeconds(samples, sampleRate) {\n return samples / sampleRate;\n}\nfunction secondsToSamples(seconds, sampleRate) {\n return Math.ceil(seconds * sampleRate);\n}\nfunction samplesToPixels(samples, samplesPerPixel) {\n return Math.floor(samples / samplesPerPixel);\n}\nfunction pixelsToSamples(pixels, samplesPerPixel) {\n return Math.floor(pixels * samplesPerPixel);\n}\nfunction pixelsToSeconds(pixels, samplesPerPixel, sampleRate) {\n return pixels * samplesPerPixel / sampleRate;\n}\nfunction secondsToPixels(seconds, samplesPerPixel, sampleRate) {\n return Math.ceil(seconds * sampleRate / samplesPerPixel);\n}\nexport {\n InteractionState,\n clipsOverlap,\n createClip,\n createClipFromSeconds,\n createTimeline,\n createTrack,\n findGaps,\n getClipsAtSample,\n getClipsInRange,\n pixelsToSamples,\n pixelsToSeconds,\n samplesToPixels,\n samplesToSeconds,\n secondsToPixels,\n secondsToSamples,\n sortClipsByTime\n};\n//# sourceMappingURL=index.mjs.map","import { useState, useEffect } from 'react';\nimport { ClipTrack, createTrack, createClipFromSeconds, type Fade, type TrackEffectsFunction, type WaveformDataObject, type RenderMode, type SpectrogramConfig, type ColorMapValue } from '@waveform-playlist/core';\nimport * as Tone from 'tone';\n\n/**\n * Configuration for a single audio track to load\n *\n * Audio can be provided in three ways:\n * 1. `src` - URL to fetch and decode (standard loading)\n * 2. `audioBuffer` - Pre-loaded AudioBuffer (skip fetch/decode)\n * 3. `waveformData` only - Peaks-first rendering (audio loads later)\n *\n * For peaks-first rendering, just provide `waveformData` - the sample rate\n * and duration are derived from the waveform data automatically.\n */\nexport interface AudioTrackConfig {\n /** URL to audio file - used if audioBuffer not provided */\n src?: string;\n /** Pre-loaded AudioBuffer - skips fetch/decode if provided */\n audioBuffer?: AudioBuffer;\n name?: string;\n muted?: boolean;\n soloed?: boolean;\n volume?: number;\n pan?: number;\n color?: string;\n effects?: TrackEffectsFunction;\n // Multi-clip support\n startTime?: number; // When the clip starts on the timeline (default: 0)\n duration?: number; // Duration of the clip (default: full audio duration)\n offset?: number; // Offset into the source audio file (default: 0)\n // Fade support\n fadeIn?: Fade; // Fade in configuration\n fadeOut?: Fade; // Fade out configuration\n // Pre-computed waveform data (BBC audiowaveform format)\n // For peaks-first rendering, provide this without audioBuffer/src\n // Sample rate and duration are derived from waveformData.sample_rate and waveformData.duration\n waveformData?: WaveformDataObject;\n /** Visualization render mode: 'waveform' | 'spectrogram' | 'both'. Default: 'waveform' */\n renderMode?: RenderMode;\n /** Spectrogram configuration (FFT size, window, frequency scale, etc.) */\n spectrogramConfig?: SpectrogramConfig;\n /** Spectrogram color map name or custom color array */\n spectrogramColorMap?: ColorMapValue;\n}\n\n/**\n * Options for useAudioTracks hook\n */\nexport interface UseAudioTracksOptions {\n /**\n * When true, tracks are added to the playlist progressively as they load,\n * rather than waiting for all tracks to finish loading.\n * Default: false (wait for all tracks)\n */\n progressive?: boolean;\n}\n\n/**\n * Hook to load audio from URLs and convert to ClipTrack format\n *\n * This hook fetches audio files, decodes them, and creates ClipTrack objects\n * with a single clip per track. Supports custom positioning for multi-clip arrangements.\n *\n * @param configs - Array of audio track configurations\n * @param options - Optional configuration for loading behavior\n * @returns Object with tracks array, loading state, and progress info\n *\n * @example\n * ```typescript\n * // Basic usage (clips positioned at start)\n * const { tracks, loading, error } = useAudioTracks([\n * { src: 'audio/vocals.mp3', name: 'Vocals' },\n * { src: 'audio/drums.mp3', name: 'Drums' },\n * ]);\n *\n * // Progressive loading (tracks appear as they load)\n * const { tracks, loading, loadedCount, totalCount } = useAudioTracks(\n * [{ src: 'audio/vocals.mp3' }, { src: 'audio/drums.mp3' }],\n * { progressive: true }\n * );\n *\n * // Pre-loaded AudioBuffer (skip fetch/decode)\n * const { tracks } = useAudioTracks([\n * { audioBuffer: myPreloadedBuffer, name: 'Pre-loaded' },\n * ]);\n *\n * // Peaks-first rendering (instant visual, audio loads later)\n * const { tracks } = useAudioTracks([\n * { waveformData: preloadedPeaks, name: 'Peaks Only' }, // Renders immediately\n * ]);\n *\n * if (loading) return <div>Loading {loadedCount}/{totalCount}...</div>;\n * if (error) return <div>Error: {error}</div>;\n *\n * return <WaveformPlaylistProvider tracks={tracks}>...</WaveformPlaylistProvider>;\n * ```\n */\nexport function useAudioTracks(\n configs: AudioTrackConfig[],\n options: UseAudioTracksOptions = {}\n) {\n const { progressive = false } = options;\n const [tracks, setTracks] = useState<ClipTrack[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [loadedCount, setLoadedCount] = useState(0);\n\n // Track which configs need audio loading vs already have data\n const totalCount = configs.length;\n\n useEffect(() => {\n if (configs.length === 0) {\n setTracks([]);\n setLoading(false);\n setLoadedCount(0);\n return;\n }\n\n let cancelled = false;\n const abortController = new AbortController();\n // Track loaded tracks by their config index for progressive mode\n const loadedTracksMap = new Map<number, ClipTrack>();\n\n const createTrackFromConfig = (\n config: AudioTrackConfig,\n index: number,\n audioBuffer?: AudioBuffer\n ): ClipTrack => {\n // Use provided audioBuffer, config's audioBuffer, or undefined for peaks-only\n const buffer = audioBuffer ?? config.audioBuffer;\n\n // For peaks-first rendering, we need waveformData if no buffer\n if (!buffer && !config.waveformData) {\n throw new Error(\n `Track ${index + 1}: Must provide src, audioBuffer, or waveformData`\n );\n }\n\n // Determine source duration for clip creation\n const sourceDuration = buffer?.duration ?? config.waveformData?.duration;\n\n // Create clip - createClipFromSeconds handles deriving sampleRate from waveformData\n const clip = createClipFromSeconds({\n audioBuffer: buffer,\n startTime: config.startTime ?? 0,\n duration: config.duration ?? sourceDuration,\n offset: config.offset ?? 0,\n name: config.name || `Track ${index + 1}`,\n fadeIn: config.fadeIn,\n fadeOut: config.fadeOut,\n waveformData: config.waveformData,\n });\n\n // Validate clip values\n if (isNaN(clip.startSample) || isNaN(clip.durationSamples) || isNaN(clip.offsetSamples)) {\n console.error('Invalid clip values:', clip);\n throw new Error(`Invalid clip values for track ${index + 1}`);\n }\n\n // Create the track with the single clip\n const track: ClipTrack = {\n ...createTrack({\n name: config.name || `Track ${index + 1}`,\n clips: [clip],\n muted: config.muted ?? false,\n soloed: config.soloed ?? false,\n volume: config.volume ?? 1.0,\n pan: config.pan ?? 0,\n color: config.color,\n }),\n effects: config.effects,\n renderMode: config.renderMode,\n spectrogramConfig: config.spectrogramConfig,\n spectrogramColorMap: config.spectrogramColorMap,\n };\n\n return track;\n };\n\n const loadTracks = async () => {\n try {\n setLoading(true);\n setError(null);\n setLoadedCount(0);\n\n const audioContext = Tone.getContext().rawContext as AudioContext;\n\n // Process each config\n const loadPromises = configs.map(async (config, index) => {\n // Case 1: Already have audioBuffer - no loading needed\n if (config.audioBuffer) {\n const track = createTrackFromConfig(config, index, config.audioBuffer);\n\n if (progressive && !cancelled) {\n loadedTracksMap.set(index, track);\n setLoadedCount(prev => prev + 1);\n // Update tracks maintaining order\n setTracks(\n Array.from({ length: configs.length }, (_, i) => loadedTracksMap.get(i))\n .filter((t): t is ClipTrack => t !== undefined)\n );\n }\n\n return track;\n }\n\n // Case 2: Have waveformData but no src - peaks-only (no audio to load)\n if (!config.src && config.waveformData) {\n const track = createTrackFromConfig(config, index);\n\n if (progressive && !cancelled) {\n loadedTracksMap.set(index, track);\n setLoadedCount(prev => prev + 1);\n setTracks(\n Array.from({ length: configs.length }, (_, i) => loadedTracksMap.get(i))\n .filter((t): t is ClipTrack => t !== undefined)\n );\n }\n\n return track;\n }\n\n // Case 3: Need to fetch and decode audio from src\n if (!config.src) {\n throw new Error(`Track ${index + 1}: Must provide src, audioBuffer, or waveformData`);\n }\n\n const response = await fetch(config.src, { signal: abortController.signal });\n if (!response.ok) {\n throw new Error(`Failed to fetch ${config.src}: ${response.statusText}`);\n }\n\n const arrayBuffer = await response.arrayBuffer();\n const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);\n\n // Validate audioBuffer\n if (!audioBuffer || !audioBuffer.sampleRate || !audioBuffer.duration) {\n throw new Error(`Invalid audio buffer for ${config.src}`);\n }\n\n const track = createTrackFromConfig(config, index, audioBuffer);\n\n if (progressive && !cancelled) {\n loadedTracksMap.set(index, track);\n setLoadedCount(prev => prev + 1);\n // Update tracks maintaining original config order\n setTracks(\n Array.from({ length: configs.length }, (_, i) => loadedTracksMap.get(i))\n .filter((t): t is ClipTrack => t !== undefined)\n );\n }\n\n return track;\n });\n\n const loadedTracks = await Promise.all(loadPromises);\n\n if (!cancelled) {\n // For non-progressive mode, set all tracks at once\n if (!progressive) {\n setTracks(loadedTracks);\n setLoadedCount(loadedTracks.length);\n }\n setLoading(false);\n }\n } catch (err) {\n if (!cancelled) {\n const errorMessage = err instanceof Error ? err.message : 'Unknown error loading audio';\n setError(errorMessage);\n setLoading(false);\n console.error('Error loading audio tracks:', err);\n }\n }\n };\n\n loadTracks();\n\n // Cleanup: prevent state updates and abort in-flight fetches on unmount\n return () => {\n cancelled = true;\n abortController.abort();\n };\n }, [configs, progressive]);\n\n return { tracks, loading, error, loadedCount, totalCount };\n}\n","import React from 'react';\nimport type { DragEndEvent, DragStartEvent, DragMoveEvent, Modifier } from '@dnd-kit/core';\nimport type { ClipTrack } from '@waveform-playlist/core';\n\ninterface UseClipDragHandlersOptions {\n tracks: ClipTrack[];\n onTracksChange: (tracks: ClipTrack[]) => void;\n samplesPerPixel: number;\n sampleRate: number;\n}\n\n/**\n * Custom hook for handling clip drag operations (movement and trimming)\n *\n * Provides drag handlers and collision modifier for use with @dnd-kit/core DndContext.\n * Handles both clip movement (dragging entire clips) and boundary trimming (adjusting clip edges).\n *\n * @example\n * ```tsx\n * const { onDragStart, onDragMove, onDragEnd, collisionModifier } = useClipDragHandlers({\n * tracks,\n * onTracksChange: setTracks,\n * samplesPerPixel,\n * sampleRate,\n * });\n *\n * return (\n * <DndContext\n * onDragStart={onDragStart}\n * onDragMove={onDragMove}\n * onDragEnd={onDragEnd}\n * modifiers={[restrictToHorizontalAxis, collisionModifier]}\n * >\n * <Waveform showClipHeaders={true} />\n * </DndContext>\n * );\n * ```\n */\nexport function useClipDragHandlers({\n tracks,\n onTracksChange,\n samplesPerPixel,\n sampleRate,\n}: UseClipDragHandlersOptions) {\n // Store original clip state when drag starts (for cumulative delta application)\n const originalClipStateRef = React.useRef<{\n offsetSamples: number;\n durationSamples: number;\n startSample: number;\n } | null>(null);\n\n // Custom modifier for real-time collision detection during clip movement\n const collisionModifier = React.useCallback(\n (args: Parameters<Modifier>[0]) => {\n const { transform, active } = args;\n\n if (!active?.data?.current) return { ...transform, scaleX: 1, scaleY: 1 };\n\n const { trackIndex, clipIndex, boundary } = active.data.current as {\n clipId: string;\n trackIndex: number;\n clipIndex: number;\n boundary?: 'left' | 'right';\n };\n\n // For boundary trimming, skip modifier - onDragMove handles constraints\n if (boundary) {\n return { ...transform, scaleX: 1, scaleY: 1 };\n }\n\n const track = tracks[trackIndex];\n if (!track) return { ...transform, scaleX: 1, scaleY: 1 };\n\n const clip = track.clips[clipIndex];\n if (!clip) return { ...transform, scaleX: 1, scaleY: 1 };\n\n // Convert sample-based properties to time for calculations\n const clipStartTime = clip.startSample / sampleRate;\n const clipDuration = clip.durationSamples / sampleRate;\n\n // Convert pixel delta to time delta\n const timeDelta = (transform.x * samplesPerPixel) / sampleRate;\n\n // Handle clip movement (not trimming)\n let newStartTime = clipStartTime + timeDelta;\n\n // Get sorted clips for collision detection\n const sortedClips = [...track.clips].sort((a, b) => (a.startSample - b.startSample));\n const sortedIndex = sortedClips.findIndex((c) => c === clip);\n\n // Constraint 1: Cannot go before time 0\n newStartTime = Math.max(0, newStartTime);\n\n // Constraint 2: Cannot overlap with previous clip\n const previousClip = sortedIndex > 0 ? sortedClips[sortedIndex - 1] : null;\n if (previousClip) {\n const previousEndTime = (previousClip.startSample + previousClip.durationSamples) / sampleRate;\n newStartTime = Math.max(newStartTime, previousEndTime);\n }\n\n // Constraint 3: Cannot overlap with next clip\n const nextClip = sortedIndex < sortedClips.length - 1 ? sortedClips[sortedIndex + 1] : null;\n if (nextClip) {\n const newEndTime = newStartTime + clipDuration;\n const nextClipStartTime = nextClip.startSample / sampleRate;\n if (newEndTime > nextClipStartTime) {\n newStartTime = nextClipStartTime - clipDuration;\n }\n }\n\n // Convert constrained time back to pixel delta\n const constrainedTimeDelta = newStartTime - clipStartTime;\n const constrainedX = (constrainedTimeDelta * sampleRate) / samplesPerPixel;\n\n return {\n ...transform,\n x: constrainedX,\n scaleX: 1,\n scaleY: 1,\n };\n },\n [tracks, samplesPerPixel, sampleRate]\n );\n\n const onDragStart = React.useCallback(\n (event: DragStartEvent) => {\n const { active } = event;\n const { boundary } = active.data.current as { boundary?: 'left' | 'right' };\n\n // Only store state for boundary trimming operations\n if (!boundary) {\n originalClipStateRef.current = null;\n return;\n }\n\n const { trackIndex, clipIndex } = active.data.current as {\n clipId: string;\n trackIndex: number;\n clipIndex: number;\n boundary: 'left' | 'right';\n };\n\n const track = tracks[trackIndex];\n const clip = track?.clips[clipIndex];\n\n if (clip) {\n // Store original clip state for cumulative delta application\n originalClipStateRef.current = {\n offsetSamples: clip.offsetSamples,\n durationSamples: clip.durationSamples,\n startSample: clip.startSample,\n };\n }\n },\n [tracks]\n );\n\n const onDragMove = React.useCallback(\n (event: DragMoveEvent) => {\n const { active, delta } = event;\n\n // Only update for boundary trimming operations (not clip movement)\n const { boundary } = active.data.current as { boundary?: 'left' | 'right' };\n if (!boundary) return;\n\n // Need original clip state to apply cumulative delta\n if (!originalClipStateRef.current) return;\n\n // Extract clip metadata\n const { trackIndex, clipIndex } = active.data.current as {\n clipId: string;\n trackIndex: number;\n clipIndex: number;\n boundary: 'left' | 'right';\n };\n\n const sampleDelta = delta.x * samplesPerPixel;\n const MIN_DURATION_SAMPLES = Math.floor(0.1 * sampleRate); // 0.1 seconds minimum\n\n // Get original clip state (stored on drag start)\n const originalClip = originalClipStateRef.current;\n\n // Update tracks in real-time during drag\n const newTracks = tracks.map((track, tIdx) => {\n if (tIdx !== trackIndex) return track;\n\n const sortedClips = [...track.clips].sort((a, b) => a.startSample - b.startSample);\n const sortedIndex = sortedClips.findIndex((clip) => clip === track.clips[clipIndex]);\n\n const newClips = track.clips.map((clip, cIdx) => {\n if (cIdx !== clipIndex) return clip;\n\n // Use sourceDurationSamples (works for both audio and peaks-only clips)\n const audioBufferDurationSamples = clip.sourceDurationSamples;\n\n if (boundary === 'left') {\n // Left boundary drag: moving left (negative delta) expands clip, moving right shrinks it\n // The RIGHT edge stays fixed. We're moving the LEFT edge.\n //\n // When dragging left (sampleDelta < 0):\n // - startSample decreases (moves left)\n // - durationSamples increases (clip gets longer)\n // - offsetSamples decreases (reveal earlier audio from buffer)\n //\n // When dragging right (sampleDelta > 0):\n // - startSample increases (moves right)\n // - durationSamples decreases (clip gets shorter)\n // - offsetSamples increases (hide earlier audio)\n\n // Calculate the constrained delta first, then apply it uniformly\n let constrainedDelta = Math.floor(sampleDelta);\n\n // Constraint 1: startSample cannot go below 0 (dragging left limit)\n // newStartSample = originalClip.startSample + delta >= 0\n // delta >= -originalClip.startSample\n const minDeltaForStart = -originalClip.startSample;\n if (constrainedDelta < minDeltaForStart) {\n constrainedDelta = minDeltaForStart;\n }\n\n // Constraint 2: offsetSamples cannot go below 0 (can't reveal audio before buffer start)\n // newOffsetSamples = originalClip.offsetSamples + delta >= 0\n // delta >= -originalClip.offsetSamples\n const minDeltaForOffset = -originalClip.offsetSamples;\n if (constrainedDelta < minDeltaForOffset) {\n constrainedDelta = minDeltaForOffset;\n }\n\n // Constraint 3: Cannot overlap with previous clip (dragging left limit)\n const previousClip = sortedIndex > 0 ? sortedClips[sortedIndex - 1] : null;\n if (previousClip) {\n const previousEndSample = previousClip.startSample + previousClip.durationSamples;\n // newStartSample = originalClip.startSample + delta >= previousEndSample\n // delta >= previousEndSample - originalClip.startSample\n const minDeltaForPrevious = previousEndSample - originalClip.startSample;\n if (constrainedDelta < minDeltaForPrevious) {\n constrainedDelta = minDeltaForPrevious;\n }\n }\n\n // Constraint 4: Minimum duration (dragging right limit)\n // newDurationSamples = originalClip.durationSamples - delta >= MIN_DURATION_SAMPLES\n // -delta >= MIN_DURATION_SAMPLES - originalClip.durationSamples\n // delta <= originalClip.durationSamples - MIN_DURATION_SAMPLES\n const maxDeltaForMinDuration = originalClip.durationSamples - MIN_DURATION_SAMPLES;\n if (constrainedDelta > maxDeltaForMinDuration) {\n constrainedDelta = maxDeltaForMinDuration;\n }\n\n // Constraint 5: Cannot exceed audio buffer length\n // newOffsetSamples + newDurationSamples <= audioBufferDurationSamples\n // (originalClip.offsetSamples + delta) + (originalClip.durationSamples - delta) <= audioBufferDurationSamples\n // This simplifies to: originalClip.offsetSamples + originalClip.durationSamples <= audioBufferDurationSamples\n // This is always true if the clip was valid to begin with, so no constraint needed here\n\n // Now apply the constrained delta\n const newOffsetSamples = originalClip.offsetSamples + constrainedDelta;\n const newDurationSamples = originalClip.durationSamples - constrainedDelta;\n const newStartSample = originalClip.startSample + constrainedDelta;\n\n return {\n ...clip,\n offsetSamples: newOffsetSamples,\n durationSamples: newDurationSamples,\n startSample: newStartSample\n };\n } else {\n // Right boundary - only update duration\n // Apply cumulative delta to ORIGINAL state (not current state)\n let newDurationSamples = Math.floor(originalClip.durationSamples + sampleDelta);\n newDurationSamples = Math.max(MIN_DURATION_SAMPLES, newDurationSamples);\n\n if (originalClip.offsetSamples + newDurationSamples > audioBufferDurationSamples) {\n newDurationSamples = audioBufferDurationSamples - originalClip.offsetSamples;\n }\n\n const nextClip = sortedIndex < sortedClips.length - 1 ? sortedClips[sortedIndex + 1] : null;\n if (nextClip) {\n const newEndSample = originalClip.startSample + newDurationSamples;\n if (newEndSample > nextClip.startSample) {\n newDurationSamples = nextClip.startSample - originalClip.startSample;\n newDurationSamples = Math.max(MIN_DURATION_SAMPLES, newDurationSamples);\n }\n }\n\n return { ...clip, durationSamples: newDurationSamples };\n }\n });\n\n return { ...track, clips: newClips };\n });\n\n onTracksChange(newTracks);\n },\n [tracks, onTracksChange, samplesPerPixel, sampleRate]\n );\n\n const onDragEnd = React.useCallback(\n (event: DragEndEvent) => {\n const { active, delta } = event;\n\n // Extract clip metadata from drag data\n const { trackIndex, clipIndex, boundary } = active.data.current as {\n clipId: string;\n trackIndex: number;\n clipIndex: number;\n boundary?: 'left' | 'right';\n };\n\n // Convert pixel delta to samples\n const sampleDelta = delta.x * samplesPerPixel;\n\n // Check if this is a boundary trim operation\n if (boundary) {\n // For boundary trimming, onDragMove already updated the tracks\n // onDragEnd doesn't need to do anything (state is already correct)\n // Just clear the original clip state ref\n originalClipStateRef.current = null;\n return;\n }\n\n // Handle clip movement (not trimming)\n const newTracks = tracks.map((track, tIdx) => {\n if (tIdx !== trackIndex) return track;\n\n // Get sorted clips for collision detection\n const sortedClips = [...track.clips].sort((a, b) => a.startSample - b.startSample);\n const sortedIndex = sortedClips.findIndex((clip) => clip === track.clips[clipIndex]);\n\n // Update the specific clip in this track\n const newClips = track.clips.map((clip, cIdx) => {\n if (cIdx !== clipIndex) return clip;\n\n // Calculate desired new start sample\n let newStartSample = Math.floor(clip.startSample + sampleDelta);\n\n // Collision detection constraints:\n // 1. Cannot go before sample 0\n newStartSample = Math.max(0, newStartSample);\n\n // 2. Cannot overlap with previous clip\n const previousClip = sortedIndex > 0 ? sortedClips[sortedIndex - 1] : null;\n if (previousClip) {\n const previousEndSample = previousClip.startSample + previousClip.durationSamples;\n newStartSample = Math.max(newStartSample, previousEndSample);\n }\n\n // 3. Cannot overlap with next clip\n const nextClip = sortedIndex < sortedClips.length - 1 ? sortedClips[sortedIndex + 1] : null;\n if (nextClip) {\n const newEndSample = newStartSample + clip.durationSamples;\n if (newEndSample > nextClip.startSample) {\n // Push back to be adjacent to next clip\n newStartSample = nextClip.startSample - clip.durationSamples;\n }\n }\n\n return {\n ...clip,\n startSample: newStartSample,\n };\n });\n\n return {\n ...track,\n clips: newClips,\n };\n });\n\n onTracksChange(newTracks);\n },\n [tracks, onTracksChange, samplesPerPixel]\n );\n\n return {\n onDragStart,\n onDragMove,\n onDragEnd,\n collisionModifier,\n };\n}\n","import React from 'react';\nimport type { DragStartEvent, DragMoveEvent } from '@dnd-kit/core';\nimport type { AnnotationData } from '@waveform-playlist/core';\n\nconst LINK_THRESHOLD = 0.01; // Consider edges \"linked\" if within 10ms\n\ninterface UseAnnotationDragHandlersOptions {\n annotations: AnnotationData[];\n onAnnotationsChange: (annotations: AnnotationData[]) => void;\n samplesPerPixel: number;\n sampleRate: number;\n duration: number;\n linkEndpoints: boolean;\n}\n\n/**\n * Custom hook for handling annotation drag operations (boundary trimming)\n *\n * Provides drag handlers for use with @dnd-kit/core DndContext.\n * Handles annotation boundary resizing with linked endpoints support.\n *\n * @example\n * ```tsx\n * const { onDragStart, onDragMove, onDragEnd } = useAnnotationDragHandlers({\n * annotations,\n * onAnnotationsChange: setAnnotations,\n * samplesPerPixel,\n * sampleRate,\n * duration,\n * linkEndpoints,\n * });\n *\n * return (\n * <DndContext\n * onDragStart={onDragStart}\n * onDragMove={onDragMove}\n * onDragEnd={onDragEnd}\n * modifiers={[restrictToHorizontalAxis]}\n * >\n * {renderAnnotations()}\n * </DndContext>\n * );\n * ```\n */\nexport function useAnnotationDragHandlers({\n annotations,\n onAnnotationsChange,\n samplesPerPixel,\n sampleRate,\n duration,\n linkEndpoints,\n}: UseAnnotationDragHandlersOptions) {\n // Store original annotation state when drag starts (for cumulative delta application)\n const originalAnnotationStateRef = React.useRef<{\n start: number;\n end: number;\n annotationIndex: number;\n } | null>(null);\n\n const onDragStart = React.useCallback(\n (event: DragStartEvent) => {\n const { active } = event;\n const data = active.data.current as {\n annotationId: string;\n annotationIndex: number;\n edge: 'start' | 'end';\n };\n\n if (!data || data.annotationIndex === undefined) {\n originalAnnotationStateRef.current = null;\n return;\n }\n\n const annotation = annotations[data.annotationIndex];\n if (annotation) {\n originalAnnotationStateRef.current = {\n start: annotation.start,\n end: annotation.end,\n annotationIndex: data.annotationIndex,\n };\n }\n },\n [annotations]\n );\n\n const onDragMove = React.useCallback(\n (event: DragMoveEvent) => {\n const { active, delta } = event;\n\n if (!originalAnnotationStateRef.current) {\n return;\n }\n\n const data = active.data.current as {\n annotationId: string;\n annotationIndex: number;\n edge: 'start' | 'end';\n };\n\n if (!data) return;\n\n const { edge, annotationIndex } = data;\n const originalState = originalAnnotationStateRef.current;\n\n // Convert pixel delta to time delta\n const timeDelta = (delta.x * samplesPerPixel) / sampleRate;\n\n // Apply delta to original state\n const newTime = edge === 'start'\n ? originalState.start + timeDelta\n : originalState.end + timeDelta;\n\n // Update annotations using the boundary logic\n const updatedAnnotations = updateAnnotationBoundaries({\n annotationIndex,\n newTime,\n isDraggingStart: edge === 'start',\n annotations,\n duration,\n linkEndpoints,\n });\n\n onAnnotationsChange(updatedAnnotations);\n },\n [annotations, onAnnotationsChange, samplesPerPixel, sampleRate, duration, linkEndpoints]\n );\n\n const onDragEnd = React.useCallback(() => {\n originalAnnotationStateRef.current = null;\n }, []);\n\n return {\n onDragStart,\n onDragMove,\n onDragEnd,\n };\n}\n\n/**\n * Updates annotation boundaries based on drag operations.\n * Handles linked endpoints and collision detection.\n */\nfunction updateAnnotationBoundaries({\n annotationIndex,\n newTime,\n isDraggingStart,\n annotations,\n duration,\n linkEndpoints: shouldLinkEndpoints,\n}: {\n annotationIndex: number;\n newTime: number;\n isDraggingStart: boolean;\n annotations: AnnotationData[];\n duration: number;\n linkEndpoints: boolean;\n}): AnnotationData[] {\n const updatedAnnotations = [...annotations];\n const annotation = annotations[annotationIndex];\n\n if (isDraggingStart) {\n // Dragging start edge\n const constrainedStart = Math.min(annotation.end - 0.1, Math.max(0, newTime));\n const delta = constrainedStart - annotation.start;\n\n updatedAnnotations[annotationIndex] = {\n ...annotation,\n start: constrainedStart,\n };\n\n if (shouldLinkEndpoints && annotationIndex > 0) {\n // Link Endpoints mode: handle both already-linked and collision scenarios\n const prevAnnotation = updatedAnnotations[annotationIndex - 1];\n\n if (Math.abs(prevAnnotation.end - annotation.start) < LINK_THRESHOLD) {\n // Already linked: move previous annotation's end together with this start\n updatedAnnotations[annotationIndex - 1] = {\n ...prevAnnotation,\n end: Math.max(prevAnnotation.start + 0.1, prevAnnotation.end + delta),\n };\n } else if (constrainedStart <= prevAnnotation.end) {\n // Dragged past previous annotation: snap to link them together\n updatedAnnotations[annotationIndex] = {\n ...updatedAnnotations[annotationIndex],\n start: prevAnnotation.end,\n };\n }\n } else if (!shouldLinkEndpoints && annotationIndex > 0 && constrainedStart < updatedAnnotations[annotationIndex - 1].end) {\n // Collision detection: push previous annotation's end back\n updatedAnnotations[annotationIndex - 1] = {\n ...updatedAnnotations[annotationIndex - 1],\n end: constrainedStart,\n };\n }\n } else {\n // Dragging end edge\n const constrainedEnd = Math.max(annotation.start + 0.1, Math.min(newTime, duration));\n const delta = constrainedEnd - annotation.end;\n\n updatedAnnotations[annotationIndex] = {\n ...annotation,\n end: constrainedEnd,\n };\n\n if (shouldLinkEndpoints && annotationIndex < updatedAnnotations.length - 1) {\n // Link Endpoints mode: handle both already-linked and collision scenarios\n const nextAnnotation = updatedAnnotations[annotationIndex + 1];\n\n if (Math.abs(nextAnnotation.start - annotation.end) < LINK_THRESHOLD) {\n // Already linked: move next annotation's start together with this end\n const newStart = nextAnnotation.start + delta;\n updatedAnnotations[annotationIndex + 1] = {\n ...nextAnnotation,\n start: Math.min(nextAnnotation.end - 0.1, newStart),\n };\n\n // Cascade linked endpoints\n let currentIndex = annotationIndex + 1;\n while (currentIndex < updatedAnnotations.length - 1) {\n const current = updatedAnnotations[currentIndex];\n const next = updatedAnnotations[currentIndex + 1];\n\n if (Math.abs(next.start - current.end) < LINK_THRESHOLD) {\n const nextDelta = current.end - annotations[currentIndex].end;\n updatedAnnotations[currentIndex + 1] = {\n ...next,\n start: Math.min(next.end - 0.1, next.start + nextDelta),\n };\n currentIndex++;\n } else {\n break; // No more linked endpoints\n }\n }\n } else if (constrainedEnd >= nextAnnotation.start) {\n // Dragged past next annotation: snap to link them together\n updatedAnnotations[annotationIndex] = {\n ...updatedAnnotations[annotationIndex],\n end: nextAnnotation.start,\n };\n }\n } else if (!shouldLinkEndpoints && annotationIndex < updatedAnnotations.length - 1 && constrainedEnd > updatedAnnotations[annotationIndex + 1].start) {\n // Collision detection: push next annotation's start forward\n const nextAnnotation = updatedAnnotations[annotationIndex + 1];\n\n updatedAnnotations[annotationIndex + 1] = {\n ...nextAnnotation,\n start: constrainedEnd,\n };\n\n // Cascade collisions\n let currentIndex = annotationIndex + 1;\n while (currentIndex < updatedAnnotations.length - 1) {\n const current = updatedAnnotations[currentIndex];\n const next = updatedAnnotations[currentIndex + 1];\n\n if (current.end > next.start) {\n updatedAnnotations[currentIndex + 1] = {\n ...next,\n start: current.end,\n };\n currentIndex++;\n } else {\n break; // No more collisions\n }\n }\n }\n }\n\n return updatedAnnotations;\n}\n","/**\n * Hook for configuring @dnd-kit sensors for clip dragging\n *\n * Provides consistent drag activation behavior across all examples.\n * Supports both desktop (immediate feedback) and mobile (delay-based) interactions.\n */\n\nimport { useSensor, useSensors, PointerSensor, TouchSensor, MouseSensor } from '@dnd-kit/core';\n\nexport interface DragSensorOptions {\n /**\n * Enable mobile-optimized touch handling with delay-based activation.\n * When true, uses TouchSensor with 250ms delay to distinguish drag from scroll.\n * When false (default), uses PointerSensor with 1px activation for immediate feedback.\n */\n touchOptimized?: boolean;\n /**\n * Delay in milliseconds before touch drag activates (only when touchOptimized is true).\n * Default: 250ms - long enough to distinguish from scroll intent\n */\n touchDelay?: number;\n /**\n * Distance tolerance during touch delay (only when touchOptimized is true).\n * If finger moves more than this during delay, drag is cancelled.\n * Default: 5px - allows slight finger movement\n */\n touchTolerance?: number;\n /**\n * Distance in pixels before mouse drag activates.\n * Default: 1px for immediate feedback on desktop\n */\n mouseDistance?: number;\n}\n\n/**\n * Returns configured sensors for @dnd-kit drag operations\n *\n * @param options - Configuration options for drag sensors\n * @returns Configured sensors appropriate for the interaction mode\n *\n * @example\n * // Desktop-optimized (default)\n * const sensors = useDragSensors();\n *\n * @example\n * // Mobile-optimized with touch delay\n * const sensors = useDragSensors({ touchOptimized: true });\n *\n * @example\n * // Custom touch settings\n * const sensors = useDragSensors({\n * touchOptimized: true,\n * touchDelay: 300,\n * touchTolerance: 8\n * });\n */\nexport function useDragSensors(options: DragSensorOptions = {}) {\n const {\n touchOptimized = false,\n touchDelay = 250,\n touchTolerance = 5,\n mouseDistance = 1,\n } = options;\n\n // Touch-optimized: Use separate MouseSensor and TouchSensor\n // This allows different activation constraints for each input type\n const mouseSensor = useSensor(MouseSensor, {\n activationConstraint: {\n distance: mouseDistance,\n },\n });\n\n const touchSensor = useSensor(TouchSensor, {\n activationConstraint: touchOptimized\n ? {\n // Delay-based activation for mobile - wait before starting drag\n // This allows users to scroll without accidentally triggering drag\n delay: touchDelay,\n tolerance: touchTolerance,\n }\n : {\n // Distance-based activation for non-optimized mode\n distance: mouseDistance,\n },\n });\n\n // Non-optimized: Use PointerSensor for unified handling (original behavior)\n const pointerSensor = useSensor(PointerSensor, {\n activationConstraint: {\n distance: mouseDistance,\n },\n });\n\n // When touch-optimized, use separate sensors for better control\n // Otherwise, use unified PointerSensor for backwards compatibility\n return useSensors(\n ...(touchOptimized ? [mouseSensor, touchSensor] : [pointerSensor])\n );\n}\n","import { useCallback } from 'react';\nimport { type ClipTrack, createClip } from '@waveform-playlist/core';\nimport { usePlaybackAnimation, usePlaylistState } from '../WaveformPlaylistContext';\n\nexport interface UseClipSplittingOptions {\n tracks: ClipTrack[];\n onTracksChange: (tracks: ClipTrack[]) => void;\n sampleRate: number;\n samplesPerPixel: number;\n}\n\nexport interface UseClipSplittingResult {\n splitClipAtPlayhead: () => boolean;\n splitClipAt: (trackIndex: number, clipIndex: number, splitTime: number) => boolean;\n}\n\n/**\n * Hook for splitting clips at the playhead or at a specific time\n *\n * @param options - Configuration options\n * @returns Object with split functions\n *\n * @example\n * ```tsx\n * const { splitClipAtPlayhead } = useClipSplitting({\n * tracks,\n * onTracksChange: setTracks,\n * currentTime,\n * });\n *\n * // In keyboard handler\n * const handleKeyPress = (e: KeyboardEvent) => {\n * if (e.key === 's' || e.key === 'S') {\n * splitClipAtPlayhead();\n * }\n * };\n * ```\n */\nexport const useClipSplitting = (options: UseClipSplittingOptions): UseClipSplittingResult => {\n const { tracks, onTracksChange, sampleRate } = options;\n const { currentTimeRef } = usePlaybackAnimation();\n const { selectedTrackId } = usePlaylistState();\n\n /**\n * Split a specific clip at a given time\n *\n * @param trackIndex - Index of the track containing the clip\n * @param clipIndex - Index of the clip within the track\n * @param splitTime - Timeline position where to split (in seconds)\n * @returns true if split was successful, false otherwise\n */\n const splitClipAt = useCallback(\n (trackIndex: number, clipIndex: number, splitTime: number): boolean => {\n // Work with samples and pixels (all integers!) to avoid floating-point precision issues\n // Key insight: A pixel represents a RANGE of samples (samplesPerPixel samples)\n // By working in samples, we eliminate all floating-point errors\n const { sampleRate, samplesPerPixel } = options;\n\n const track = tracks[trackIndex];\n if (!track) return false;\n\n const clip = track.clips[clipIndex];\n if (!clip) return false;\n\n // Convert clip positions from samples to seconds for bounds checking\n const clipStartTime = clip.startSample / sampleRate;\n const clipEndTime = (clip.startSample + clip.durationSamples) / sampleRate;\n\n // Validate that split time is within clip bounds\n if (splitTime <= clipStartTime || splitTime >= clipEndTime) {\n console.warn('Split time is outside clip bounds');\n return false;\n }\n\n // Convert split time from seconds to samples (round to nearest sample)\n const splitSample = Math.round(splitTime * sampleRate);\n\n // Calculate pixel positions from sample positions using integer division\n const splitPixel = Math.floor(splitSample / samplesPerPixel);\n const clipEndSample = clip.startSample + clip.durationSamples;\n\n // Calculate sample positions from exact pixel boundaries\n // Both clips share the same boundary: the start of the split pixel\n const snappedSplitSample = splitPixel * samplesPerPixel;\n\n // First clip: starts at clip's original start, ends at split pixel boundary\n const firstClipStartSample = clip.startSample;\n const firstClipDurationSamples = snappedSplitSample - firstClipStartSample;\n\n // Second clip: starts at split pixel boundary, ends at clip's original end\n const secondClipStartSample = snappedSplitSample;\n const secondClipDurationSamples = clipEndSample - secondClipStartSample;\n\n // Calculate offset increment for second clip (in samples)\n const offsetIncrement = snappedSplitSample - clip.startSample;\n\n // Create first clip (from start to split point)\n const firstClip = createClip({\n audioBuffer: clip.audioBuffer,\n startSample: firstClipStartSample,\n durationSamples: firstClipDurationSamples,\n offsetSamples: clip.offsetSamples,\n sampleRate: clip.sampleRate,\n sourceDurationSamples: clip.sourceDurationSamples,\n gain: clip.gain,\n name: clip.name ? `${clip.name} (1)` : undefined,\n color: clip.color,\n fadeIn: clip.fadeIn,\n waveformData: clip.waveformData, // Share waveformData - slicing happens at render time\n // Note: fadeOut removed for first clip since it's cut\n });\n\n // Create second clip (from split point to end)\n const secondClip = createClip({\n audioBuffer: clip.audioBuffer,\n startSample: secondClipStartSample,\n durationSamples: secondClipDurationSamples,\n offsetSamples: clip.offsetSamples + offsetIncrement,\n sampleRate: clip.sampleRate,\n sourceDurationSamples: clip.sourceDurationSamples,\n gain: clip.gain,\n name: clip.name ? `${clip.name} (2)` : undefined,\n color: clip.color,\n waveformData: clip.waveformData, // Share waveformData - slicing happens at render time\n // Note: fadeIn removed for second clip since it's cut\n fadeOut: clip.fadeOut,\n });\n\n // Create new clips array with the split clips\n const newClips = [...track.clips];\n newClips.splice(clipIndex, 1, firstClip, secondClip);\n\n // Update the track with new clips\n const newTracks = [...tracks];\n newTracks[trackIndex] = {\n ...track,\n clips: newClips,\n };\n\n onTracksChange(newTracks);\n return true;\n },\n [tracks, onTracksChange, options]\n );\n\n /**\n * Split clip at the current playhead position on the selected track\n * If no track is selected, does nothing\n *\n * @returns true if a clip was split, false otherwise\n */\n const splitClipAtPlayhead = useCallback((): boolean => {\n // If no track is selected, cannot split\n if (!selectedTrackId) {\n console.log('No track selected - click a clip to select a track first');\n return false;\n }\n\n // Find the selected track\n const trackIndex = tracks.findIndex(track => track.id === selectedTrackId);\n if (trackIndex === -1) {\n console.warn('Selected track not found');\n return false;\n }\n\n const track = tracks[trackIndex];\n\n // Use ref for real-time position during playback (state updates are throttled)\n const currentTime = currentTimeRef.current ?? 0;\n\n // Find clip at current time on the selected track\n for (let clipIndex = 0; clipIndex < track.clips.length; clipIndex++) {\n const clip = track.clips[clipIndex];\n const clipStartTime = clip.startSample / sampleRate;\n const clipEndTime = (clip.startSample + clip.durationSamples) / sampleRate;\n\n // Check if currentTime is within this clip (not at boundaries)\n if (currentTime > clipStartTime && currentTime < clipEndTime) {\n // Found a clip! Split it\n console.log(`Splitting clip on track \"${track.name}\" at ${currentTime}s`);\n return splitClipAt(trackIndex, clipIndex, currentTime);\n }\n }\n\n console.log(`No clip found at playhead position on track \"${track.name}\"`);\n return false;\n }, [tracks, currentTimeRef, selectedTrackId, splitClipAt, sampleRate]);\n\n return {\n splitClipAtPlayhead,\n splitClipAt,\n };\n};\n","import { useEffect, useCallback } from 'react';\n\nexport interface KeyboardShortcut {\n key: string;\n ctrlKey?: boolean;\n shiftKey?: boolean;\n metaKey?: boolean;\n altKey?: boolean;\n action: () => void;\n description?: string;\n preventDefault?: boolean;\n}\n\nexport interface UseKeyboardShortcutsOptions {\n shortcuts: KeyboardShortcut[];\n enabled?: boolean;\n}\n\n/**\n * Hook for managing keyboard shortcuts\n *\n * @param options - Configuration options\n *\n * @example\n * ```tsx\n * const { splitClipAtPlayhead } = useClipSplitting({ ... });\n *\n * useKeyboardShortcuts({\n * shortcuts: [\n * {\n * key: 's',\n * action: splitClipAtPlayhead,\n * description: 'Split clip at playhead',\n * preventDefault: true,\n * },\n * {\n * key: 'S',\n * shiftKey: true,\n * action: () => splitAtSelection(),\n * description: 'Split at selection boundaries',\n * preventDefault: true,\n * },\n * ],\n * });\n * ```\n */\nexport const useKeyboardShortcuts = (options: UseKeyboardShortcutsOptions): void => {\n const { shortcuts, enabled = true } = options;\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent) => {\n if (!enabled) return;\n\n // Check if we're in an input/textarea element\n const target = event.target as HTMLElement;\n if (\n target.tagName === 'INPUT' ||\n target.tagName === 'TEXTAREA' ||\n target.isContentEditable\n ) {\n // Don't trigger shortcuts when typing in input fields\n return;\n }\n\n // Find matching shortcut\n const matchingShortcut = shortcuts.find((shortcut) => {\n const keyMatch =\n event.key.toLowerCase() === shortcut.key.toLowerCase() ||\n event.key === shortcut.key;\n\n const ctrlMatch = shortcut.ctrlKey === undefined || event.ctrlKey === shortcut.ctrlKey;\n const shiftMatch =\n shortcut.shiftKey === undefined || event.shiftKey === shortcut.shiftKey;\n const metaMatch = shortcut.metaKey === undefined || event.metaKey === shortcut.metaKey;\n const altMatch = shortcut.altKey === undefined || event.altKey === shortcut.altKey;\n\n return keyMatch && ctrlMatch && shiftMatch && metaMatch && altMatch;\n });\n\n if (matchingShortcut) {\n if (matchingShortcut.preventDefault !== false) {\n event.preventDefault();\n }\n matchingShortcut.action();\n }\n },\n [shortcuts, enabled]\n );\n\n useEffect(() => {\n if (!enabled) return;\n\n window.addEventListener('keydown', handleKeyDown);\n\n return () => {\n window.removeEventListener('keydown', handleKeyDown);\n };\n }, [handleKeyDown, enabled]);\n};\n\n/**\n * Get a human-readable string representation of a keyboard shortcut\n *\n * @param shortcut - The keyboard shortcut\n * @returns Human-readable string (e.g., \"Cmd+Shift+S\")\n */\nexport const getShortcutLabel = (shortcut: KeyboardShortcut): string => {\n const parts: string[] = [];\n\n // Use Cmd on Mac, Ctrl on other platforms\n const isMac = typeof navigator !== 'undefined' && navigator.platform.includes('Mac');\n\n if (shortcut.metaKey) {\n parts.push(isMac ? 'Cmd' : 'Ctrl');\n }\n\n if (shortcut.ctrlKey && !shortcut.metaKey) {\n parts.push('Ctrl');\n }\n\n if (shortcut.altKey) {\n parts.push(isMac ? 'Option' : 'Alt');\n }\n\n if (shortcut.shiftKey) {\n parts.push('Shift');\n }\n\n parts.push(shortcut.key.toUpperCase());\n\n return parts.join('+');\n};\n","import { useCallback } from 'react';\nimport { usePlaybackAnimation, usePlaylistControls, usePlaylistData } from '../WaveformPlaylistContext';\nimport { useKeyboardShortcuts, type KeyboardShortcut } from './useKeyboardShortcuts';\n\nexport interface UsePlaybackShortcutsOptions {\n /**\n * Enable the shortcuts. Defaults to true.\n */\n enabled?: boolean;\n /**\n * Additional shortcuts to include alongside the default playback shortcuts.\n */\n additionalShortcuts?: KeyboardShortcut[];\n /**\n * Override default shortcuts. If provided, only these shortcuts will be used.\n */\n shortcuts?: KeyboardShortcut[];\n}\n\nexport interface UsePlaybackShortcutsReturn {\n /** Rewind to the beginning (time = 0) */\n rewindToStart: () => void;\n /** Toggle play/pause */\n togglePlayPause: () => void;\n /** Stop playback and return to start position */\n stopPlayback: () => void;\n /** The list of active keyboard shortcuts */\n shortcuts: KeyboardShortcut[];\n}\n\n/**\n * Hook that provides common playback keyboard shortcuts for the playlist.\n *\n * Default shortcuts:\n * - `Space` - Toggle play/pause\n * - `Escape` - Stop playback\n * - `0` - Rewind to start (seek to time 0)\n *\n * @example\n * ```tsx\n * // Basic usage - enables default shortcuts\n * usePlaybackShortcuts();\n *\n * // With additional custom shortcuts\n * usePlaybackShortcuts({\n * additionalShortcuts: [\n * { key: 's', action: splitClipAtPlayhead, description: 'Split clip' },\n * ],\n * });\n *\n * // Completely override shortcuts\n * usePlaybackShortcuts({\n * shortcuts: [\n * { key: 'Home', action: rewindToStart, description: 'Go to start' },\n * ],\n * });\n * ```\n */\nexport const usePlaybackShortcuts = (\n options: UsePlaybackShortcutsOptions = {}\n): UsePlaybackShortcutsReturn => {\n const { enabled = true, additionalShortcuts = [], shortcuts: overrideShortcuts } = options;\n\n const { isPlaying } = usePlaybackAnimation();\n const { setCurrentTime, play, pause, stop } = usePlaylistControls();\n const { playoutRef } = usePlaylistData();\n\n /**\n * Toggle between play and pause.\n */\n const togglePlayPause = useCallback(() => {\n if (isPlaying) {\n pause();\n } else {\n play();\n }\n }, [isPlaying, play, pause]);\n\n /**\n * Stop playback and return to start position.\n */\n const stopPlayback = useCallback(() => {\n stop();\n }, [stop]);\n\n /**\n * Rewind to the beginning of the timeline.\n * If playing, stops and restarts playback from the beginning.\n */\n const rewindToStart = useCallback(() => {\n if (isPlaying && playoutRef.current) {\n playoutRef.current.stop();\n setCurrentTime(0);\n play(0);\n } else {\n setCurrentTime(0);\n }\n }, [isPlaying, playoutRef, setCurrentTime, play]);\n\n // Default playback shortcuts\n const defaultShortcuts: KeyboardShortcut[] = [\n {\n key: ' ',\n action: togglePlayPause,\n description: 'Play/Pause',\n preventDefault: true,\n },\n {\n key: 'Escape',\n action: stopPlayback,\n description: 'Stop',\n preventDefault: true,\n },\n {\n key: '0',\n action: rewindToStart,\n description: 'Rewind to start',\n preventDefault: true,\n },\n ];\n\n // Use override shortcuts if provided, otherwise combine defaults with additional\n const activeShortcuts = overrideShortcuts ?? [...defaultShortcuts, ...additionalShortcuts];\n\n // Register the keyboard shortcuts\n useKeyboardShortcuts({\n shortcuts: activeShortcuts,\n enabled,\n });\n\n return {\n rewindToStart,\n togglePlayPause,\n stopPlayback,\n shortcuts: activeShortcuts,\n };\n};\n","import { useCallback, useMemo, useEffect } from 'react';\nimport type { AnnotationData } from '@waveform-playlist/core';\nimport { useKeyboardShortcuts } from './useKeyboardShortcuts';\n\nconst LINK_THRESHOLD = 0.01; // Consider edges \"linked\" if within 10ms\nconst TIME_DELTA = 0.01; // 10ms adjustment per keypress\n\ninterface UseAnnotationKeyboardControlsOptions {\n annotations: AnnotationData[];\n activeAnnotationId: string | null;\n onAnnotationsChange: (annotations: AnnotationData[]) => void;\n /** Callback to set the active annotation ID for selection */\n onActiveAnnotationChange?: (id: string | null) => void;\n duration: number;\n linkEndpoints: boolean;\n /** Whether continuous play is enabled (affects playback duration) */\n continuousPlay?: boolean;\n enabled?: boolean;\n /** Optional: scroll container ref for auto-scrolling to annotation */\n scrollContainerRef?: React.RefObject<HTMLDivElement | null>;\n /** Optional: samples per pixel for scroll position calculation */\n samplesPerPixel?: number;\n /** Optional: sample rate for scroll position calculation */\n sampleRate?: number;\n /** Optional: controls width offset for scroll position calculation */\n controlsWidth?: number;\n /** Optional: callback to start playback at a time with optional duration */\n onPlay?: (startTime: number, duration?: number) => void;\n}\n\n/**\n * Hook for keyboard-based annotation navigation and boundary editing\n *\n * Navigation Shortcuts:\n * - ArrowUp / ArrowLeft = Select previous annotation\n * - ArrowDown / ArrowRight = Select next annotation\n * - Home = Select first annotation\n * - End = Select last annotation\n * - Escape = Deselect annotation\n * - Enter = Play selected annotation\n *\n * Boundary Editing Shortcuts (requires active annotation):\n * - [ = Move start boundary earlier (left)\n * - ] = Move start boundary later (right)\n * - Shift+[ = Move end boundary earlier (left)\n * - Shift+] = Move end boundary later (right)\n *\n * Respects linkEndpoints and continuousPlay settings.\n *\n * @example\n * ```tsx\n * useAnnotationKeyboardControls({\n * annotations,\n * activeAnnotationId,\n * onAnnotationsChange: setAnnotations,\n * onActiveAnnotationChange: setActiveAnnotationId,\n * duration,\n * linkEndpoints,\n * });\n * ```\n */\nexport function useAnnotationKeyboardControls({\n annotations,\n activeAnnotationId,\n onAnnotationsChange,\n onActiveAnnotationChange,\n duration,\n linkEndpoints,\n continuousPlay = false,\n enabled = true,\n scrollContainerRef,\n samplesPerPixel,\n sampleRate,\n controlsWidth = 0,\n onPlay,\n}: UseAnnotationKeyboardControlsOptions) {\n const activeIndex = useMemo(() => {\n if (!activeAnnotationId) return -1;\n return annotations.findIndex((a) => a.id === activeAnnotationId);\n }, [annotations, activeAnnotationId]);\n\n // Scroll waveform to show a specific annotation\n const scrollToAnnotation = useCallback(\n (annotationId: string) => {\n if (!scrollContainerRef?.current || !samplesPerPixel || !sampleRate) return;\n\n const annotation = annotations.find((a) => a.id === annotationId);\n if (!annotation) return;\n\n const container = scrollContainerRef.current;\n const containerWidth = container.clientWidth;\n\n // Calculate pixel positions for annotation start and center\n const startPixel = (annotation.start * sampleRate) / samplesPerPixel + controlsWidth;\n const endPixel = (annotation.end * sampleRate) / samplesPerPixel + controlsWidth;\n const annotationCenter = (startPixel + endPixel) / 2;\n\n // Check if annotation is currently visible\n const scrollLeft = container.scrollLeft;\n const visibleStart = scrollLeft;\n const visibleEnd = scrollLeft + containerWidth;\n\n // If annotation is not fully visible, scroll to center it\n if (startPixel < visibleStart || endPixel > visibleEnd) {\n const targetScrollLeft = Math.max(0, annotationCenter - containerWidth / 2);\n container.scrollTo({\n left: targetScrollLeft,\n behavior: 'smooth',\n });\n }\n },\n [annotations, scrollContainerRef, samplesPerPixel, sampleRate, controlsWidth]\n );\n\n // Auto-scroll when active annotation changes via keyboard navigation\n useEffect(() => {\n if (activeAnnotationId && scrollContainerRef?.current && samplesPerPixel && sampleRate) {\n scrollToAnnotation(activeAnnotationId);\n }\n }, [activeAnnotationId, scrollToAnnotation, scrollContainerRef, samplesPerPixel, sampleRate]);\n\n const moveStartBoundary = useCallback(\n (delta: number) => {\n if (activeIndex < 0) return;\n\n const annotation = annotations[activeIndex];\n const newStart = Math.max(0, Math.min(annotation.end - 0.1, annotation.start + delta));\n const actualDelta = newStart - annotation.start;\n\n const updatedAnnotations = [...annotations];\n updatedAnnotations[activeIndex] = {\n ...annotation,\n start: newStart,\n };\n\n // Handle linked endpoints\n if (linkEndpoints && activeIndex > 0) {\n const prevAnnotation = updatedAnnotations[activeIndex - 1];\n if (Math.abs(prevAnnotation.end - annotation.start) < LINK_THRESHOLD) {\n // Already linked: move previous annotation's end together\n updatedAnnotations[activeIndex - 1] = {\n ...prevAnnotation,\n end: Math.max(prevAnnotation.start + 0.1, prevAnnotation.end + actualDelta),\n };\n }\n } else if (!linkEndpoints && activeIndex > 0) {\n // Non-linked mode: don't overlap previous annotation\n const prevAnnotation = updatedAnnotations[activeIndex - 1];\n if (newStart < prevAnnotation.end) {\n // Push back previous annotation's end\n updatedAnnotations[activeIndex - 1] = {\n ...prevAnnotation,\n end: newStart,\n };\n }\n }\n\n onAnnotationsChange(updatedAnnotations);\n },\n [annotations, activeIndex, linkEndpoints, onAnnotationsChange]\n );\n\n const moveEndBoundary = useCallback(\n (delta: number) => {\n if (activeIndex < 0) return;\n\n const annotation = annotations[activeIndex];\n const newEnd = Math.max(annotation.start + 0.1, Math.min(duration, annotation.end + delta));\n const actualDelta = newEnd - annotation.end;\n\n const updatedAnnotations = [...annotations];\n updatedAnnotations[activeIndex] = {\n ...annotation,\n end: newEnd,\n };\n\n // Handle linked endpoints\n if (linkEndpoints && activeIndex < annotations.length - 1) {\n const nextAnnotation = updatedAnnotations[activeIndex + 1];\n if (Math.abs(nextAnnotation.start - annotation.end) < LINK_THRESHOLD) {\n // Already linked: move next annotation's start together\n const newNextStart = Math.min(nextAnnotation.end - 0.1, nextAnnotation.start + actualDelta);\n updatedAnnotations[activeIndex + 1] = {\n ...nextAnnotation,\n start: newNextStart,\n };\n\n // Cascade linked endpoints\n let currentIndex = activeIndex + 1;\n while (currentIndex < updatedAnnotations.length - 1) {\n const current = updatedAnnotations[currentIndex];\n const next = updatedAnnotations[currentIndex + 1];\n\n if (Math.abs(next.start - annotations[currentIndex].end) < LINK_THRESHOLD) {\n const nextDelta = current.end - annotations[currentIndex].end;\n updatedAnnotations[currentIndex + 1] = {\n ...next,\n start: Math.min(next.end - 0.1, next.start + nextDelta),\n };\n currentIndex++;\n } else {\n break;\n }\n }\n }\n } else if (!linkEndpoints && activeIndex < annotations.length - 1) {\n // Non-linked mode: don't overlap next annotation\n const nextAnnotation = updatedAnnotations[activeIndex + 1];\n if (newEnd > nextAnnotation.start) {\n // Push forward next annotation's start\n updatedAnnotations[activeIndex + 1] = {\n ...nextAnnotation,\n start: newEnd,\n };\n\n // Cascade collisions\n let currentIndex = activeIndex + 1;\n while (currentIndex < updatedAnnotations.length - 1) {\n const current = updatedAnnotations[currentIndex];\n const next = updatedAnnotations[currentIndex + 1];\n\n if (current.end > next.start) {\n updatedAnnotations[currentIndex + 1] = {\n ...next,\n start: current.end,\n };\n currentIndex++;\n } else {\n break;\n }\n }\n }\n }\n\n onAnnotationsChange(updatedAnnotations);\n },\n [annotations, activeIndex, duration, linkEndpoints, onAnnotationsChange]\n );\n\n // Navigation functions\n const selectPrevious = useCallback(() => {\n if (!onActiveAnnotationChange || annotations.length === 0) return;\n\n if (activeIndex <= 0) {\n // If no selection or at first, select last annotation\n onActiveAnnotationChange(annotations[annotations.length - 1].id);\n } else {\n onActiveAnnotationChange(annotations[activeIndex - 1].id);\n }\n }, [annotations, activeIndex, onActiveAnnotationChange]);\n\n const selectNext = useCallback(() => {\n if (!onActiveAnnotationChange || annotations.length === 0) return;\n\n if (activeIndex < 0 || activeIndex >= annotations.length - 1) {\n // If no selection or at last, select first annotation\n onActiveAnnotationChange(annotations[0].id);\n } else {\n onActiveAnnotationChange(annotations[activeIndex + 1].id);\n }\n }, [annotations, activeIndex, onActiveAnnotationChange]);\n\n const selectFirst = useCallback(() => {\n if (!onActiveAnnotationChange || annotations.length === 0) return;\n onActiveAnnotationChange(annotations[0].id);\n }, [annotations, onActiveAnnotationChange]);\n\n const selectLast = useCallback(() => {\n if (!onActiveAnnotationChange || annotations.length === 0) return;\n onActiveAnnotationChange(annotations[annotations.length - 1].id);\n }, [annotations, onActiveAnnotationChange]);\n\n const clearSelection = useCallback(() => {\n if (!onActiveAnnotationChange) return;\n onActiveAnnotationChange(null);\n }, [onActiveAnnotationChange]);\n\n // Play the currently selected annotation\n const playActiveAnnotation = useCallback(() => {\n if (activeIndex < 0 || !onPlay) return;\n\n const annotation = annotations[activeIndex];\n // If continuous play is off, play just this annotation's duration\n const playDuration = !continuousPlay ? annotation.end - annotation.start : undefined;\n onPlay(annotation.start, playDuration);\n }, [annotations, activeIndex, continuousPlay, onPlay]);\n\n // Shortcuts that require an active annotation (boundary editing + playback)\n const activeAnnotationShortcuts = useMemo(\n () => [\n {\n key: '[',\n action: () => moveStartBoundary(-TIME_DELTA),\n description: 'Move annotation start earlier',\n preventDefault: true,\n },\n {\n key: ']',\n action: () => moveStartBoundary(TIME_DELTA),\n description: 'Move annotation start later',\n preventDefault: true,\n },\n {\n key: '{',\n shiftKey: true,\n action: () => moveEndBoundary(-TIME_DELTA),\n description: 'Move annotation end earlier',\n preventDefault: true,\n },\n {\n key: '}',\n shiftKey: true,\n action: () => moveEndBoundary(TIME_DELTA),\n description: 'Move annotation end later',\n preventDefault: true,\n },\n {\n key: 'Enter',\n action: playActiveAnnotation,\n description: 'Play selected annotation',\n preventDefault: true,\n },\n ],\n [moveStartBoundary, moveEndBoundary, playActiveAnnotation]\n );\n\n // Navigation shortcuts (always active when enabled and there are annotations)\n const navigationShortcuts = useMemo(\n () => [\n {\n key: 'ArrowUp',\n action: selectPrevious,\n description: 'Select previous annotation',\n preventDefault: true,\n },\n {\n key: 'ArrowLeft',\n action: selectPrevious,\n description: 'Select previous annotation',\n preventDefault: true,\n },\n {\n key: 'ArrowDown',\n action: selectNext,\n description: 'Select next annotation',\n preventDefault: true,\n },\n {\n key: 'ArrowRight',\n action: selectNext,\n description: 'Select next annotation',\n preventDefault: true,\n },\n {\n key: 'Home',\n action: selectFirst,\n description: 'Select first annotation',\n preventDefault: true,\n },\n {\n key: 'End',\n action: selectLast,\n description: 'Select last annotation',\n preventDefault: true,\n },\n {\n key: 'Escape',\n action: clearSelection,\n description: 'Deselect annotation',\n preventDefault: true,\n },\n ],\n [selectPrevious, selectNext, selectFirst, selectLast, clearSelection]\n );\n\n // Active annotation shortcuts only work when an annotation is selected\n useKeyboardShortcuts({\n shortcuts: activeAnnotationShortcuts,\n enabled: enabled && activeIndex >= 0,\n });\n\n // Navigation shortcuts work whenever there are annotations\n useKeyboardShortcuts({\n shortcuts: navigationShortcuts,\n enabled: enabled && annotations.length > 0 && !!onActiveAnnotationChange,\n });\n\n return {\n moveStartBoundary,\n moveEndBoundary,\n selectPrevious,\n selectNext,\n selectFirst,\n selectLast,\n clearSelection,\n scrollToAnnotation,\n playActiveAnnotation,\n };\n}\n","/**\n * Effect definitions for all available Tone.js effects\n * Each effect has parameters with min/max/default values for UI controls\n */\n\nexport type ParameterType = 'number' | 'select' | 'boolean';\n\nexport interface EffectParameter {\n name: string;\n label: string;\n type: ParameterType;\n min?: number;\n max?: number;\n step?: number;\n default: number | string | boolean;\n unit?: string;\n options?: { value: string | number; label: string }[];\n}\n\nexport interface EffectDefinition {\n id: string;\n name: string;\n category: 'delay' | 'reverb' | 'modulation' | 'distortion' | 'filter' | 'dynamics' | 'spatial';\n description: string;\n parameters: EffectParameter[];\n}\n\nexport const effectDefinitions: EffectDefinition[] = [\n // === REVERB EFFECTS ===\n {\n id: 'reverb',\n name: 'Reverb',\n category: 'reverb',\n description: 'Simple convolution reverb with adjustable decay time',\n parameters: [\n { name: 'decay', label: 'Decay', type: 'number', min: 0.1, max: 10, step: 0.1, default: 1.5, unit: 's' },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 0.5 },\n ],\n },\n {\n id: 'freeverb',\n name: 'Freeverb',\n category: 'reverb',\n description: 'Classic Schroeder/Moorer reverb with room size and dampening',\n parameters: [\n { name: 'roomSize', label: 'Room Size', type: 'number', min: 0, max: 1, step: 0.01, default: 0.7 },\n { name: 'dampening', label: 'Dampening', type: 'number', min: 0, max: 10000, step: 100, default: 3000, unit: 'Hz' },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 0.5 },\n ],\n },\n {\n id: 'jcReverb',\n name: 'JC Reverb',\n category: 'reverb',\n description: 'Attempt at Roland JC-120 chorus reverb emulation',\n parameters: [\n { name: 'roomSize', label: 'Room Size', type: 'number', min: 0, max: 1, step: 0.01, default: 0.5 },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 0.5 },\n ],\n },\n\n // === DELAY EFFECTS ===\n {\n id: 'feedbackDelay',\n name: 'Feedback Delay',\n category: 'delay',\n description: 'Delay line with feedback for echo effects',\n parameters: [\n { name: 'delayTime', label: 'Delay Time', type: 'number', min: 0, max: 1, step: 0.01, default: 0.25, unit: 's' },\n { name: 'feedback', label: 'Feedback', type: 'number', min: 0, max: 0.95, step: 0.01, default: 0.5 },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 0.5 },\n ],\n },\n {\n id: 'pingPongDelay',\n name: 'Ping Pong Delay',\n category: 'delay',\n description: 'Stereo delay bouncing between left and right channels',\n parameters: [\n { name: 'delayTime', label: 'Delay Time', type: 'number', min: 0, max: 1, step: 0.01, default: 0.25, unit: 's' },\n { name: 'feedback', label: 'Feedback', type: 'number', min: 0, max: 0.95, step: 0.01, default: 0.5 },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 0.5 },\n ],\n },\n\n // === MODULATION EFFECTS ===\n {\n id: 'chorus',\n name: 'Chorus',\n category: 'modulation',\n description: 'Creates thickness by layering detuned copies of the signal',\n parameters: [\n { name: 'frequency', label: 'Rate', type: 'number', min: 0.1, max: 10, step: 0.1, default: 1.5, unit: 'Hz' },\n { name: 'delayTime', label: 'Delay', type: 'number', min: 0, max: 20, step: 0.5, default: 3.5, unit: 'ms' },\n { name: 'depth', label: 'Depth', type: 'number', min: 0, max: 1, step: 0.01, default: 0.7 },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 0.5 },\n ],\n },\n {\n id: 'phaser',\n name: 'Phaser',\n category: 'modulation',\n description: 'Classic phaser effect using allpass filters',\n parameters: [\n { name: 'frequency', label: 'Rate', type: 'number', min: 0.1, max: 10, step: 0.1, default: 0.5, unit: 'Hz' },\n { name: 'octaves', label: 'Octaves', type: 'number', min: 1, max: 6, step: 1, default: 3 },\n { name: 'baseFrequency', label: 'Base Freq', type: 'number', min: 100, max: 2000, step: 10, default: 350, unit: 'Hz' },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 0.5 },\n ],\n },\n {\n id: 'tremolo',\n name: 'Tremolo',\n category: 'modulation',\n description: 'Rhythmic volume modulation',\n parameters: [\n { name: 'frequency', label: 'Rate', type: 'number', min: 0.1, max: 20, step: 0.1, default: 4, unit: 'Hz' },\n { name: 'depth', label: 'Depth', type: 'number', min: 0, max: 1, step: 0.01, default: 0.5 },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 1 },\n ],\n },\n {\n id: 'vibrato',\n name: 'Vibrato',\n category: 'modulation',\n description: 'Pitch modulation effect',\n parameters: [\n { name: 'frequency', label: 'Rate', type: 'number', min: 0.1, max: 20, step: 0.1, default: 5, unit: 'Hz' },\n { name: 'depth', label: 'Depth', type: 'number', min: 0, max: 1, step: 0.01, default: 0.1 },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 1 },\n ],\n },\n {\n id: 'autoPanner',\n name: 'Auto Panner',\n category: 'modulation',\n description: 'Automatic left-right panning',\n parameters: [\n { name: 'frequency', label: 'Rate', type: 'number', min: 0.1, max: 10, step: 0.1, default: 1, unit: 'Hz' },\n { name: 'depth', label: 'Depth', type: 'number', min: 0, max: 1, step: 0.01, default: 1 },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 1 },\n ],\n },\n\n // === FILTER EFFECTS ===\n {\n id: 'autoFilter',\n name: 'Auto Filter',\n category: 'filter',\n description: 'Automated filter sweep with LFO',\n parameters: [\n { name: 'frequency', label: 'Rate', type: 'number', min: 0.1, max: 10, step: 0.1, default: 1, unit: 'Hz' },\n { name: 'baseFrequency', label: 'Base Freq', type: 'number', min: 20, max: 2000, step: 10, default: 200, unit: 'Hz' },\n { name: 'octaves', label: 'Octaves', type: 'number', min: 0.5, max: 8, step: 0.5, default: 2.6 },\n { name: 'depth', label: 'Depth', type: 'number', min: 0, max: 1, step: 0.01, default: 1 },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 1 },\n ],\n },\n {\n id: 'autoWah',\n name: 'Auto Wah',\n category: 'filter',\n description: 'Envelope follower filter effect',\n parameters: [\n { name: 'baseFrequency', label: 'Base Freq', type: 'number', min: 20, max: 500, step: 10, default: 100, unit: 'Hz' },\n { name: 'octaves', label: 'Octaves', type: 'number', min: 1, max: 8, step: 1, default: 6 },\n { name: 'sensitivity', label: 'Sensitivity', type: 'number', min: -40, max: 0, step: 1, default: 0, unit: 'dB' },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 1 },\n ],\n },\n {\n id: 'eq3',\n name: '3-Band EQ',\n category: 'filter',\n description: 'Three band equalizer with low, mid, and high controls',\n parameters: [\n { name: 'low', label: 'Low', type: 'number', min: -24, max: 24, step: 0.5, default: 0, unit: 'dB' },\n { name: 'mid', label: 'Mid', type: 'number', min: -24, max: 24, step: 0.5, default: 0, unit: 'dB' },\n { name: 'high', label: 'High', type: 'number', min: -24, max: 24, step: 0.5, default: 0, unit: 'dB' },\n { name: 'lowFrequency', label: 'Low Freq', type: 'number', min: 20, max: 500, step: 10, default: 400, unit: 'Hz' },\n { name: 'highFrequency', label: 'High Freq', type: 'number', min: 1000, max: 10000, step: 100, default: 2500, unit: 'Hz' },\n ],\n },\n\n // === DISTORTION EFFECTS ===\n {\n id: 'distortion',\n name: 'Distortion',\n category: 'distortion',\n description: 'Wave shaping distortion effect',\n parameters: [\n { name: 'distortion', label: 'Drive', type: 'number', min: 0, max: 1, step: 0.01, default: 0.4 },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 1 },\n ],\n },\n {\n id: 'bitCrusher',\n name: 'Bit Crusher',\n category: 'distortion',\n description: 'Reduces bit depth for lo-fi digital texture',\n parameters: [\n { name: 'bits', label: 'Bits', type: 'number', min: 1, max: 16, step: 1, default: 4 },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 1 },\n ],\n },\n {\n id: 'chebyshev',\n name: 'Chebyshev',\n category: 'distortion',\n description: 'Waveshaping distortion using Chebyshev polynomials',\n parameters: [\n { name: 'order', label: 'Order', type: 'number', min: 1, max: 100, step: 1, default: 50 },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 1 },\n ],\n },\n\n // === DYNAMICS EFFECTS ===\n {\n id: 'compressor',\n name: 'Compressor',\n category: 'dynamics',\n description: 'Dynamic range compressor',\n parameters: [\n { name: 'threshold', label: 'Threshold', type: 'number', min: -60, max: 0, step: 1, default: -24, unit: 'dB' },\n { name: 'ratio', label: 'Ratio', type: 'number', min: 1, max: 20, step: 0.5, default: 4 },\n { name: 'attack', label: 'Attack', type: 'number', min: 0, max: 1, step: 0.001, default: 0.003, unit: 's' },\n { name: 'release', label: 'Release', type: 'number', min: 0, max: 1, step: 0.01, default: 0.25, unit: 's' },\n { name: 'knee', label: 'Knee', type: 'number', min: 0, max: 40, step: 1, default: 30, unit: 'dB' },\n ],\n },\n {\n id: 'limiter',\n name: 'Limiter',\n category: 'dynamics',\n description: 'Hard limiter to prevent clipping',\n parameters: [\n { name: 'threshold', label: 'Threshold', type: 'number', min: -12, max: 0, step: 0.5, default: -6, unit: 'dB' },\n ],\n },\n {\n id: 'gate',\n name: 'Gate',\n category: 'dynamics',\n description: 'Noise gate to silence signal below threshold',\n parameters: [\n { name: 'threshold', label: 'Threshold', type: 'number', min: -100, max: 0, step: 1, default: -40, unit: 'dB' },\n { name: 'attack', label: 'Attack', type: 'number', min: 0, max: 0.3, step: 0.001, default: 0.001, unit: 's' },\n { name: 'release', label: 'Release', type: 'number', min: 0, max: 0.5, step: 0.01, default: 0.1, unit: 's' },\n ],\n },\n\n // === SPATIAL EFFECTS ===\n {\n id: 'stereoWidener',\n name: 'Stereo Widener',\n category: 'spatial',\n description: 'Expands or narrows the stereo image',\n parameters: [\n { name: 'width', label: 'Width', type: 'number', min: 0, max: 1, step: 0.01, default: 0.5 },\n ],\n },\n];\n\n// Helper to get effect definition by ID\nexport const getEffectDefinition = (id: string): EffectDefinition | undefined => {\n return effectDefinitions.find((def) => def.id === id);\n};\n\n// Helper to get effects by category\nexport const getEffectsByCategory = (category: EffectDefinition['category']): EffectDefinition[] => {\n return effectDefinitions.filter((def) => def.category === category);\n};\n\n// All categories with their display names\nexport const effectCategories: { id: EffectDefinition['category']; name: string }[] = [\n { id: 'reverb', name: 'Reverb' },\n { id: 'delay', name: 'Delay' },\n { id: 'modulation', name: 'Modulation' },\n { id: 'filter', name: 'Filter' },\n { id: 'distortion', name: 'Distortion' },\n { id: 'dynamics', name: 'Dynamics' },\n { id: 'spatial', name: 'Spatial' },\n];\n","/**\n * Factory for creating Tone.js effect instances from effect definitions\n */\nimport {\n Reverb,\n Freeverb,\n JCReverb,\n FeedbackDelay,\n PingPongDelay,\n Chorus,\n Phaser,\n Tremolo,\n Vibrato,\n AutoPanner,\n AutoFilter,\n AutoWah,\n EQ3,\n Distortion,\n BitCrusher,\n Chebyshev,\n Compressor,\n Limiter,\n Gate,\n StereoWidener,\n ToneAudioNode,\n} from 'tone';\nimport type { InputNode } from 'tone';\nimport type { EffectDefinition } from './effectDefinitions';\n\n// Type for effect instance with common methods\nexport interface EffectInstance {\n effect: ToneAudioNode; // Tone.js effect instance\n id: string;\n instanceId: string;\n dispose: () => void;\n setParameter: (name: string, value: number | string | boolean) => void;\n getParameter: (name: string) => number | string | boolean | undefined;\n connect: (destination: InputNode) => void;\n disconnect: () => void;\n}\n\n// Each Tone.js effect constructor accepts different option types (ReverbOptions,\n// ChorusOptions, etc.) but all produce ToneAudioNode subclasses. We use a\n// permissive constructor signature to unify them in a single lookup map.\ntype EffectConstructor = new (options?: Record<string, number | string | boolean>) => ToneAudioNode;\n\n/** Centralizes the single unavoidable cast from a specific Tone.js effect constructor to EffectConstructor. */\nfunction asEffectConstructor(ctor: new (...args: never[]) => ToneAudioNode): EffectConstructor {\n return ctor as unknown as EffectConstructor;\n}\n\n// Map of effect IDs to their Tone.js constructors\nconst effectConstructors: Record<string, EffectConstructor> = {\n reverb: asEffectConstructor(Reverb),\n freeverb: asEffectConstructor(Freeverb),\n jcReverb: asEffectConstructor(JCReverb),\n feedbackDelay: asEffectConstructor(FeedbackDelay),\n pingPongDelay: asEffectConstructor(PingPongDelay),\n chorus: asEffectConstructor(Chorus),\n phaser: asEffectConstructor(Phaser),\n tremolo: asEffectConstructor(Tremolo),\n vibrato: asEffectConstructor(Vibrato),\n autoPanner: asEffectConstructor(AutoPanner),\n autoFilter: asEffectConstructor(AutoFilter),\n autoWah: asEffectConstructor(AutoWah),\n eq3: asEffectConstructor(EQ3),\n distortion: asEffectConstructor(Distortion),\n bitCrusher: asEffectConstructor(BitCrusher),\n chebyshev: asEffectConstructor(Chebyshev),\n compressor: asEffectConstructor(Compressor),\n limiter: asEffectConstructor(Limiter),\n gate: asEffectConstructor(Gate),\n stereoWidener: asEffectConstructor(StereoWidener),\n};\n\n// Generate unique instance ID\nlet instanceCounter = 0;\nconst generateInstanceId = (): string => {\n return `effect_${Date.now()}_${++instanceCounter}`;\n};\n\n/**\n * Create an effect instance from a definition with initial parameter values\n */\nexport function createEffectInstance(\n definition: EffectDefinition,\n initialParams?: Record<string, number | string | boolean>\n): EffectInstance {\n const Constructor = effectConstructors[definition.id];\n if (!Constructor) {\n throw new Error(`Unknown effect type: ${definition.id}`);\n }\n\n // Build initial options from definition defaults and any overrides\n const options: Record<string, number | string | boolean> = {};\n definition.parameters.forEach((param) => {\n const value = initialParams?.[param.name] ?? param.default;\n options[param.name] = value;\n });\n\n // Create the effect instance\n const effect = new Constructor(options);\n const instanceId = generateInstanceId();\n\n // Dynamic property access on Tone.js effects for parameter get/set.\n // Each effect type (Reverb, Chorus, EQ3, etc.) exposes different parameters as\n // properties or Signals. We cast to a record type for safe dynamic access.\n const effectRecord = effect as unknown as Record<string, { value?: unknown } | unknown>;\n\n return {\n effect,\n id: definition.id,\n instanceId,\n\n dispose() {\n try {\n effect.disconnect();\n effect.dispose();\n } catch (e) {\n console.warn(`[waveform-playlist] Error disposing effect \"${definition.id}\" (${instanceId}):`, e);\n }\n },\n\n setParameter(name: string, value: number | string | boolean) {\n // Handle special cases for different effect types\n const prop = effectRecord[name];\n if (name === 'wet') {\n const wetProp = effectRecord['wet'] as { value?: number } | undefined;\n if (wetProp && typeof wetProp === 'object' && 'value' in wetProp) {\n wetProp.value = value as number;\n return;\n }\n }\n if (prop !== undefined) {\n // Check if it's a Tone.js Signal (has .value property)\n if (prop && typeof prop === 'object' && 'value' in prop) {\n (prop as { value: unknown }).value = value;\n } else {\n effectRecord[name] = value;\n }\n }\n },\n\n getParameter(name: string): number | string | boolean | undefined {\n if (name === 'wet') {\n const wetProp = effectRecord['wet'] as { value?: number } | undefined;\n if (wetProp && typeof wetProp === 'object' && 'value' in wetProp) {\n return wetProp.value;\n }\n }\n const prop = effectRecord[name];\n if (prop !== undefined) {\n if (prop && typeof prop === 'object' && 'value' in prop) {\n return (prop as { value: unknown }).value as number | string | boolean;\n }\n return prop as number | string | boolean;\n }\n return undefined;\n },\n\n connect(destination: InputNode) {\n effect.connect(destination);\n },\n\n disconnect() {\n try {\n effect.disconnect();\n } catch (e) {\n console.warn(`[waveform-playlist] Error disconnecting effect \"${definition.id}\" (${instanceId}):`, e);\n }\n },\n };\n}\n\n/**\n * Create a chain of effects connected in series\n */\nexport function createEffectChain(\n effects: EffectInstance[]\n): {\n input: ToneAudioNode;\n output: ToneAudioNode;\n dispose: () => void;\n} {\n if (effects.length === 0) {\n throw new Error('Cannot create effect chain with no effects');\n }\n\n // Connect effects in series\n for (let i = 0; i < effects.length - 1; i++) {\n effects[i].effect.connect(effects[i + 1].effect);\n }\n\n return {\n input: effects[0].effect,\n output: effects[effects.length - 1].effect,\n dispose() {\n effects.forEach((e) => e.dispose());\n },\n };\n}\n","import { useState, useCallback, useRef, useEffect } from 'react';\nimport type { EffectsFunction } from '@waveform-playlist/playout';\nimport {\n effectDefinitions,\n getEffectDefinition,\n type EffectDefinition,\n} from '../effects/effectDefinitions';\nimport {\n createEffectInstance,\n type EffectInstance,\n} from '../effects/effectFactory';\nimport { Analyser, Volume, ToneAudioNode } from 'tone';\n\nexport interface ActiveEffect {\n instanceId: string;\n effectId: string;\n definition: EffectDefinition;\n params: Record<string, number | string | boolean>;\n bypassed: boolean;\n}\n\nexport interface UseDynamicEffectsReturn {\n // State\n activeEffects: ActiveEffect[];\n availableEffects: EffectDefinition[];\n\n // Actions\n addEffect: (effectId: string) => void;\n removeEffect: (instanceId: string) => void;\n updateParameter: (instanceId: string, paramName: string, value: number | string | boolean) => void;\n toggleBypass: (instanceId: string) => void;\n reorderEffects: (fromIndex: number, toIndex: number) => void;\n clearAllEffects: () => void;\n\n // For connecting to audio graph\n masterEffects: EffectsFunction;\n\n /**\n * Creates a fresh effects function for offline rendering.\n * This creates new effect instances that work in the offline AudioContext.\n */\n createOfflineEffectsFunction: () => EffectsFunction | undefined;\n\n // Analyser for visualization\n analyserRef: React.RefObject<Analyser | null>;\n}\n\n/**\n * Hook for managing a dynamic chain of audio effects with real-time parameter updates\n */\nexport function useDynamicEffects(fftSize: number = 256): UseDynamicEffectsReturn {\n // Track active effects in state (for UI)\n const [activeEffects, setActiveEffects] = useState<ActiveEffect[]>([]);\n\n // Ref to store current activeEffects for reading in callbacks (avoids stale closures)\n const activeEffectsRef = useRef<ActiveEffect[]>(activeEffects);\n activeEffectsRef.current = activeEffects;\n\n // Track effect instances (for audio processing)\n const effectInstancesRef = useRef<Map<string, EffectInstance>>(new Map());\n\n // Analyser for visualization\n const analyserRef = useRef<Analyser | null>(null);\n\n // Reference to the current audio graph nodes\n const graphNodesRef = useRef<{\n masterGainNode: Volume;\n destination: ToneAudioNode;\n analyserNode: Analyser;\n } | null>(null);\n\n // Rebuild the effect chain when effects change\n // Note: effects is passed as parameter to avoid stale closure issues\n const rebuildChain = useCallback((effects: ActiveEffect[]) => {\n const nodes = graphNodesRef.current;\n if (!nodes) return;\n\n const { masterGainNode, destination, analyserNode } = nodes;\n\n // Disconnect everything first\n try {\n masterGainNode.disconnect();\n } catch (e) {\n console.warn('[waveform-playlist] Error disconnecting master effects chain:', e);\n }\n\n // Get effect instances in order\n const instances = effects\n .map((ae) => effectInstancesRef.current.get(ae.instanceId))\n .filter((inst): inst is EffectInstance => inst !== undefined);\n\n if (instances.length === 0) {\n // No effects - connect directly to analyser -> destination\n masterGainNode.connect(analyserNode);\n analyserNode.connect(destination);\n } else {\n // Connect: masterGain -> effect1 -> effect2 -> ... -> analyser -> destination\n let currentNode: ToneAudioNode = masterGainNode;\n\n instances.forEach((inst) => {\n try {\n inst.disconnect();\n } catch (e) {\n console.warn(`[waveform-playlist] Error disconnecting effect \"${inst.id}\":`, e);\n }\n currentNode.connect(inst.effect);\n currentNode = inst.effect;\n });\n\n // Connect last effect to analyser\n currentNode.connect(analyserNode);\n analyserNode.connect(destination);\n }\n }, []);\n\n // Add a new effect\n const addEffect = useCallback((effectId: string) => {\n const definition = getEffectDefinition(effectId);\n if (!definition) {\n console.error(`Unknown effect: ${effectId}`);\n return;\n }\n\n // Build default params\n const params: Record<string, number | string | boolean> = {};\n definition.parameters.forEach((p) => {\n params[p.name] = p.default;\n });\n\n // Create the effect instance\n const instance = createEffectInstance(definition, params);\n effectInstancesRef.current.set(instance.instanceId, instance);\n\n // Add to state\n const newActiveEffect: ActiveEffect = {\n instanceId: instance.instanceId,\n effectId: definition.id,\n definition,\n params,\n bypassed: false,\n };\n\n setActiveEffects((prev) => [...prev, newActiveEffect]);\n }, []);\n\n // Remove an effect\n const removeEffect = useCallback((instanceId: string) => {\n const instance = effectInstancesRef.current.get(instanceId);\n if (instance) {\n instance.dispose();\n effectInstancesRef.current.delete(instanceId);\n }\n\n setActiveEffects((prev) => prev.filter((e) => e.instanceId !== instanceId));\n }, []);\n\n // Update a parameter in real-time\n const updateParameter = useCallback(\n (instanceId: string, paramName: string, value: number | string | boolean) => {\n // Update the actual effect instance\n const instance = effectInstancesRef.current.get(instanceId);\n if (instance) {\n instance.setParameter(paramName, value);\n }\n\n // Update state for UI\n setActiveEffects((prev) =>\n prev.map((e) =>\n e.instanceId === instanceId\n ? { ...e, params: { ...e.params, [paramName]: value } }\n : e\n )\n );\n },\n []\n );\n\n // Toggle bypass for an effect (uses wet parameter - 0 = bypass, restore original for active)\n const toggleBypass = useCallback(\n (instanceId: string) => {\n // Get current state from ref to determine new bypassed value (avoids stale closure)\n const effect = activeEffectsRef.current.find((e) => e.instanceId === instanceId);\n if (!effect) return;\n\n const newBypassed = !effect.bypassed;\n\n // Update the actual effect instance\n // When bypassing: set wet to 0\n // When un-bypassing: restore the original wet value from params\n const instance = effectInstancesRef.current.get(instanceId);\n if (instance) {\n const originalWet = effect.params.wet as number ?? 1;\n instance.setParameter('wet', newBypassed ? 0 : originalWet);\n }\n\n // Update state for UI\n setActiveEffects((prev) =>\n prev.map((e) =>\n e.instanceId === instanceId ? { ...e, bypassed: newBypassed } : e\n )\n );\n },\n []\n );\n\n // Reorder effects in the chain\n const reorderEffects = useCallback((fromIndex: number, toIndex: number) => {\n setActiveEffects((prev) => {\n const newEffects = [...prev];\n const [removed] = newEffects.splice(fromIndex, 1);\n newEffects.splice(toIndex, 0, removed);\n return newEffects;\n });\n }, []);\n\n // Clear all effects\n const clearAllEffects = useCallback(() => {\n // Dispose all instances\n effectInstancesRef.current.forEach((inst) => inst.dispose());\n effectInstancesRef.current.clear();\n\n setActiveEffects([]);\n }, []);\n\n // Rebuild chain when effects change\n useEffect(() => {\n rebuildChain(activeEffects);\n }, [activeEffects, rebuildChain]);\n\n // The effects function that gets passed to WaveformPlaylistProvider\n // This function is stable - it reads from refs at call time to avoid stale closures\n const masterEffects: EffectsFunction = useCallback(\n (masterGainNode, destination, _isOffline) => {\n // Create analyser for visualization\n const analyserNode = new Analyser('fft', fftSize);\n analyserRef.current = analyserNode;\n\n // Store references for rebuilding chain\n graphNodesRef.current = {\n masterGainNode,\n destination,\n analyserNode,\n };\n\n // Build initial chain - read from ref to get current state\n const effects = activeEffectsRef.current;\n const instances = effects\n .map((ae) => effectInstancesRef.current.get(ae.instanceId))\n .filter((inst): inst is EffectInstance => inst !== undefined);\n\n if (instances.length === 0) {\n // No effects - connect directly to analyser -> destination\n masterGainNode.connect(analyserNode);\n analyserNode.connect(destination);\n } else {\n // Connect: masterGain -> effect1 -> effect2 -> ... -> analyser -> destination\n let currentNode: ToneAudioNode = masterGainNode;\n\n instances.forEach((inst) => {\n currentNode.connect(inst.effect);\n currentNode = inst.effect;\n });\n\n // Connect last effect to analyser\n currentNode.connect(analyserNode);\n analyserNode.connect(destination);\n }\n\n return function cleanup() {\n analyserNode.dispose();\n analyserRef.current = null;\n graphNodesRef.current = null;\n };\n },\n [fftSize] // Only fftSize - reads effects from ref\n );\n\n // Cleanup on unmount\n useEffect(() => {\n const effectInstances = effectInstancesRef.current;\n return () => {\n effectInstances.forEach((inst) => inst.dispose());\n effectInstances.clear();\n };\n }, []);\n\n /**\n * Creates a fresh effects function for offline rendering.\n * This creates new effect instances in the offline context, avoiding the\n * AudioContext mismatch issue that occurs when reusing real-time effects.\n */\n const createOfflineEffectsFunction = useCallback((): EffectsFunction | undefined => {\n // Get non-bypassed effects\n const nonBypassedEffects = activeEffects.filter((e) => !e.bypassed);\n\n if (nonBypassedEffects.length === 0) {\n return undefined;\n }\n\n // Return a function that creates fresh effect instances\n return (masterGainNode: Volume, destination: ToneAudioNode, _isOffline: boolean) => {\n // Create fresh effect instances for offline context\n const offlineInstances: EffectInstance[] = [];\n\n for (const activeEffect of nonBypassedEffects) {\n const instance = createEffectInstance(activeEffect.definition, activeEffect.params);\n offlineInstances.push(instance);\n }\n\n if (offlineInstances.length === 0) {\n // No effects - connect directly\n masterGainNode.connect(destination);\n } else {\n // Connect: masterGain -> effect1 -> effect2 -> ... -> destination\n let currentNode: ToneAudioNode = masterGainNode;\n\n offlineInstances.forEach((inst) => {\n currentNode.connect(inst.effect);\n currentNode = inst.effect;\n });\n\n // Connect last effect to destination\n currentNode.connect(destination);\n }\n\n return function cleanup() {\n offlineInstances.forEach((inst) => inst.dispose());\n };\n };\n }, [activeEffects]);\n\n return {\n activeEffects,\n availableEffects: effectDefinitions,\n addEffect,\n removeEffect,\n updateParameter,\n toggleBypass,\n reorderEffects,\n clearAllEffects,\n masterEffects,\n createOfflineEffectsFunction,\n analyserRef,\n };\n}\n","import { useState, useCallback, useRef, useEffect } from 'react';\nimport type { TrackEffectsFunction } from '../index';\nimport {\n effectDefinitions,\n getEffectDefinition,\n type EffectDefinition,\n} from '../effects/effectDefinitions';\nimport {\n createEffectInstance,\n type EffectInstance,\n} from '../effects/effectFactory';\nimport { Gain, ToneAudioNode } from 'tone';\n\nexport interface TrackActiveEffect {\n instanceId: string;\n effectId: string;\n definition: EffectDefinition;\n params: Record<string, number | string | boolean>;\n bypassed: boolean;\n}\n\nexport interface TrackEffectsState {\n trackId: string;\n activeEffects: TrackActiveEffect[];\n}\n\nexport interface UseTrackDynamicEffectsReturn {\n // State per track\n trackEffectsState: Map<string, TrackActiveEffect[]>;\n\n // Actions\n addEffectToTrack: (trackId: string, effectId: string) => void;\n removeEffectFromTrack: (trackId: string, instanceId: string) => void;\n updateTrackEffectParameter: (\n trackId: string,\n instanceId: string,\n paramName: string,\n value: number | string | boolean\n ) => void;\n toggleBypass: (trackId: string, instanceId: string) => void;\n clearTrackEffects: (trackId: string) => void;\n getTrackEffectsFunction: (trackId: string) => TrackEffectsFunction | undefined;\n\n /**\n * Creates a fresh effects function for a track for offline rendering.\n * This creates new effect instances that work in the offline AudioContext.\n */\n createOfflineTrackEffectsFunction: (trackId: string) => TrackEffectsFunction | undefined;\n\n // Available effects\n availableEffects: EffectDefinition[];\n}\n\n/**\n * Hook for managing dynamic effects per track with real-time parameter updates\n */\nexport function useTrackDynamicEffects(): UseTrackDynamicEffectsReturn {\n // Track effects state per track (for UI)\n const [trackEffectsState, setTrackEffectsState] = useState<Map<string, TrackActiveEffect[]>>(\n new Map()\n );\n\n // Track effect instances per track (for audio processing)\n const trackEffectInstancesRef = useRef<Map<string, Map<string, EffectInstance>>>(new Map());\n\n // Track graph nodes per track for rebuilding chains\n const trackGraphNodesRef = useRef<\n Map<\n string,\n {\n graphEnd: Gain;\n masterGainNode: ToneAudioNode;\n }\n >\n >(new Map());\n\n // Rebuild the effect chain for a specific track\n // Note: trackEffects is passed as parameter to avoid stale closure issues\n const rebuildTrackChain = useCallback((trackId: string, trackEffects: TrackActiveEffect[]) => {\n const nodes = trackGraphNodesRef.current.get(trackId);\n if (!nodes) return;\n\n const { graphEnd, masterGainNode } = nodes;\n const instancesMap = trackEffectInstancesRef.current.get(trackId);\n\n // Disconnect everything first\n try {\n graphEnd.disconnect();\n } catch (e) {\n console.warn(`[waveform-playlist] Error disconnecting track \"${trackId}\" effect chain:`, e);\n }\n\n // Get effect instances in order\n const instances = trackEffects\n .map((ae) => instancesMap?.get(ae.instanceId))\n .filter((inst): inst is EffectInstance => inst !== undefined);\n\n if (instances.length === 0) {\n // No effects - connect directly\n graphEnd.connect(masterGainNode);\n } else {\n // Connect: graphEnd -> effect1 -> effect2 -> ... -> masterGainNode\n let currentNode: ToneAudioNode = graphEnd;\n\n instances.forEach((inst) => {\n try {\n inst.disconnect();\n } catch (e) {\n console.warn(`[waveform-playlist] Error disconnecting effect \"${inst.id}\" on track \"${trackId}\":`, e);\n }\n currentNode.connect(inst.effect);\n currentNode = inst.effect;\n });\n\n // Connect last effect to master\n currentNode.connect(masterGainNode);\n }\n }, []);\n\n // Add a new effect to a track\n const addEffectToTrack = useCallback((trackId: string, effectId: string) => {\n const definition = getEffectDefinition(effectId);\n if (!definition) {\n console.error(`Unknown effect: ${effectId}`);\n return;\n }\n\n // Build default params\n const params: Record<string, number | string | boolean> = {};\n definition.parameters.forEach((p) => {\n params[p.name] = p.default;\n });\n\n // Create the effect instance\n const instance = createEffectInstance(definition, params);\n\n // Initialize maps if needed\n if (!trackEffectInstancesRef.current.has(trackId)) {\n trackEffectInstancesRef.current.set(trackId, new Map());\n }\n trackEffectInstancesRef.current.get(trackId)!.set(instance.instanceId, instance);\n\n // Add to state\n const newActiveEffect: TrackActiveEffect = {\n instanceId: instance.instanceId,\n effectId: definition.id,\n definition,\n params,\n bypassed: false,\n };\n\n setTrackEffectsState((prev) => {\n const newState = new Map(prev);\n const existing = newState.get(trackId) || [];\n newState.set(trackId, [...existing, newActiveEffect]);\n return newState;\n });\n }, []);\n\n // Remove an effect from a track\n const removeEffectFromTrack = useCallback((trackId: string, instanceId: string) => {\n const instancesMap = trackEffectInstancesRef.current.get(trackId);\n const instance = instancesMap?.get(instanceId);\n if (instance) {\n instance.dispose();\n instancesMap?.delete(instanceId);\n }\n\n setTrackEffectsState((prev) => {\n const newState = new Map(prev);\n const existing = newState.get(trackId) || [];\n newState.set(trackId, existing.filter((e) => e.instanceId !== instanceId));\n return newState;\n });\n }, []);\n\n // Update a parameter in real-time\n const updateTrackEffectParameter = useCallback(\n (trackId: string, instanceId: string, paramName: string, value: number | string | boolean) => {\n // Update the actual effect instance\n const instancesMap = trackEffectInstancesRef.current.get(trackId);\n const instance = instancesMap?.get(instanceId);\n if (instance) {\n instance.setParameter(paramName, value);\n }\n\n // Update state for UI\n setTrackEffectsState((prev) => {\n const newState = new Map(prev);\n const existing = newState.get(trackId) || [];\n newState.set(\n trackId,\n existing.map((e) =>\n e.instanceId === instanceId\n ? { ...e, params: { ...e.params, [paramName]: value } }\n : e\n )\n );\n return newState;\n });\n },\n []\n );\n\n // Toggle bypass for an effect (uses wet parameter - 0 = bypass, restore original for active)\n const toggleBypass = useCallback(\n (trackId: string, instanceId: string) => {\n // Get current state from ref to determine new bypassed value (avoids stale closure)\n const trackEffects = trackEffectsStateRef.current.get(trackId) || [];\n const effect = trackEffects.find((e) => e.instanceId === instanceId);\n if (!effect) return;\n\n const newBypassed = !effect.bypassed;\n\n // Update the actual effect instance\n // When bypassing: set wet to 0\n // When un-bypassing: restore the original wet value from params\n const instancesMap = trackEffectInstancesRef.current.get(trackId);\n const instance = instancesMap?.get(instanceId);\n if (instance) {\n const originalWet = effect.params.wet as number ?? 1;\n instance.setParameter('wet', newBypassed ? 0 : originalWet);\n }\n\n // Update state for UI\n setTrackEffectsState((prev) => {\n const newState = new Map(prev);\n const existing = newState.get(trackId) || [];\n newState.set(\n trackId,\n existing.map((e) =>\n e.instanceId === instanceId ? { ...e, bypassed: newBypassed } : e\n )\n );\n return newState;\n });\n },\n []\n );\n\n // Clear all effects from a track\n const clearTrackEffects = useCallback((trackId: string) => {\n // Dispose all instances for this track\n const instancesMap = trackEffectInstancesRef.current.get(trackId);\n if (instancesMap) {\n instancesMap.forEach((inst) => inst.dispose());\n instancesMap.clear();\n }\n\n setTrackEffectsState((prev) => {\n const newState = new Map(prev);\n newState.set(trackId, []);\n return newState;\n });\n }, []);\n\n // Ref to store the current trackEffectsState for reading in effects function\n // This avoids stale closure issues when the effects function is called later\n const trackEffectsStateRef = useRef<Map<string, TrackActiveEffect[]>>(trackEffectsState);\n trackEffectsStateRef.current = trackEffectsState;\n\n // Get the effects function for a track to pass to useAudioTracks\n // This function is stable (no dependencies) - it reads from refs at call time\n const getTrackEffectsFunction = useCallback(\n (trackId: string): TrackEffectsFunction | undefined => {\n // Return a function that connects effects when the track is loaded\n return (graphEnd, masterGainNode, _isOffline) => {\n // Store references for rebuilding chain\n trackGraphNodesRef.current.set(trackId, {\n graphEnd,\n masterGainNode,\n });\n\n // Read current state from ref (not stale closure)\n const trackEffects = trackEffectsStateRef.current.get(trackId) || [];\n const instancesMap = trackEffectInstancesRef.current.get(trackId);\n\n // Get effect instances in order\n const instances = trackEffects\n .map((ae) => instancesMap?.get(ae.instanceId))\n .filter((inst): inst is EffectInstance => inst !== undefined);\n\n if (instances.length === 0) {\n // No effects - connect directly\n graphEnd.connect(masterGainNode);\n } else {\n // Connect: graphEnd -> effect1 -> effect2 -> ... -> masterGainNode\n let currentNode: ToneAudioNode = graphEnd;\n\n instances.forEach((inst) => {\n currentNode.connect(inst.effect);\n currentNode = inst.effect;\n });\n\n // Connect last effect to master\n currentNode.connect(masterGainNode);\n }\n\n return function cleanup() {\n trackGraphNodesRef.current.delete(trackId);\n };\n };\n },\n [] // No dependencies - stable function that reads from refs\n );\n\n // Rebuild chains when effects change\n useEffect(() => {\n trackEffectsState.forEach((effects, trackId) => {\n rebuildTrackChain(trackId, effects);\n });\n }, [trackEffectsState, rebuildTrackChain]);\n\n // Cleanup on unmount\n useEffect(() => {\n const trackEffectInstances = trackEffectInstancesRef.current;\n return () => {\n trackEffectInstances.forEach((instancesMap) => {\n instancesMap.forEach((inst) => inst.dispose());\n instancesMap.clear();\n });\n trackEffectInstances.clear();\n };\n }, []);\n\n /**\n * Creates a fresh effects function for a track for offline rendering.\n * This creates new effect instances in the offline context, avoiding the\n * AudioContext mismatch issue that occurs when reusing real-time effects.\n */\n const createOfflineTrackEffectsFunction = useCallback(\n (trackId: string): TrackEffectsFunction | undefined => {\n const trackEffects = trackEffectsState.get(trackId) || [];\n // Get non-bypassed effects\n const nonBypassedEffects = trackEffects.filter((e) => !e.bypassed);\n\n if (nonBypassedEffects.length === 0) {\n return undefined;\n }\n\n // Return a function that creates fresh effect instances\n return (graphEnd: Gain, masterGainNode: ToneAudioNode, _isOffline: boolean) => {\n // Create fresh effect instances for offline context\n const offlineInstances: EffectInstance[] = [];\n\n for (const activeEffect of nonBypassedEffects) {\n const instance = createEffectInstance(activeEffect.definition, activeEffect.params);\n offlineInstances.push(instance);\n }\n\n if (offlineInstances.length === 0) {\n // No effects - connect directly\n graphEnd.connect(masterGainNode);\n } else {\n // Connect: graphEnd -> effect1 -> effect2 -> ... -> masterGainNode\n let currentNode: ToneAudioNode = graphEnd;\n\n offlineInstances.forEach((inst) => {\n currentNode.connect(inst.effect);\n currentNode = inst.effect;\n });\n\n // Connect last effect to master\n currentNode.connect(masterGainNode);\n }\n\n return function cleanup() {\n offlineInstances.forEach((inst) => inst.dispose());\n };\n };\n },\n [trackEffectsState]\n );\n\n return {\n trackEffectsState,\n addEffectToTrack,\n removeEffectFromTrack,\n updateTrackEffectParameter,\n toggleBypass,\n clearTrackEffects,\n getTrackEffectsFunction,\n createOfflineTrackEffectsFunction,\n availableEffects: effectDefinitions,\n };\n}\n","/**\n * WAV file encoder\n * Converts AudioBuffer to WAV format Blob\n */\n\nexport interface WavEncoderOptions {\n /** Bit depth: 16 or 32. Default: 16 */\n bitDepth?: 16 | 32;\n}\n\n/**\n * Encode an AudioBuffer to WAV format\n * @param audioBuffer - The AudioBuffer to encode\n * @param options - Encoding options\n * @returns WAV file as Blob\n */\nexport function encodeWav(\n audioBuffer: AudioBuffer,\n options: WavEncoderOptions = {}\n): Blob {\n const { bitDepth = 16 } = options;\n\n const numChannels = audioBuffer.numberOfChannels;\n const sampleRate = audioBuffer.sampleRate;\n const numSamples = audioBuffer.length;\n const bytesPerSample = bitDepth / 8;\n const blockAlign = numChannels * bytesPerSample;\n const byteRate = sampleRate * blockAlign;\n const dataSize = numSamples * blockAlign;\n\n // WAV header is 44 bytes\n const headerSize = 44;\n const totalSize = headerSize + dataSize;\n\n const buffer = new ArrayBuffer(totalSize);\n const view = new DataView(buffer);\n\n // Write WAV header\n // RIFF chunk descriptor\n writeString(view, 0, 'RIFF');\n view.setUint32(4, totalSize - 8, true); // File size minus RIFF header\n writeString(view, 8, 'WAVE');\n\n // fmt sub-chunk\n writeString(view, 12, 'fmt ');\n view.setUint32(16, 16, true); // Subchunk1Size (16 for PCM)\n view.setUint16(20, bitDepth === 32 ? 3 : 1, true); // AudioFormat (1=PCM, 3=IEEE float)\n view.setUint16(22, numChannels, true);\n view.setUint32(24, sampleRate, true);\n view.setUint32(28, byteRate, true);\n view.setUint16(32, blockAlign, true);\n view.setUint16(34, bitDepth, true);\n\n // data sub-chunk\n writeString(view, 36, 'data');\n view.setUint32(40, dataSize, true);\n\n // Write interleaved audio data\n const channelData: Float32Array[] = [];\n for (let ch = 0; ch < numChannels; ch++) {\n channelData.push(audioBuffer.getChannelData(ch));\n }\n\n let offset = headerSize;\n\n if (bitDepth === 16) {\n // 16-bit PCM\n for (let i = 0; i < numSamples; i++) {\n for (let ch = 0; ch < numChannels; ch++) {\n const sample = channelData[ch][i];\n // Clamp to [-1, 1] and convert to 16-bit signed integer\n const clampedSample = Math.max(-1, Math.min(1, sample));\n const intSample = clampedSample < 0\n ? clampedSample * 0x8000\n : clampedSample * 0x7FFF;\n view.setInt16(offset, intSample, true);\n offset += 2;\n }\n }\n } else {\n // 32-bit IEEE float\n for (let i = 0; i < numSamples; i++) {\n for (let ch = 0; ch < numChannels; ch++) {\n view.setFloat32(offset, channelData[ch][i], true);\n offset += 4;\n }\n }\n }\n\n return new Blob([buffer], { type: 'audio/wav' });\n}\n\n/**\n * Write a string to a DataView at the specified offset\n */\nfunction writeString(view: DataView, offset: number, str: string): void {\n for (let i = 0; i < str.length; i++) {\n view.setUint8(offset + i, str.charCodeAt(i));\n }\n}\n\n/**\n * Trigger a download of a Blob with the specified filename\n */\nexport function downloadBlob(blob: Blob, filename: string): void {\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = filename;\n a.style.display = 'none';\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n}\n","import { useState, useCallback } from 'react';\nimport type { ClipTrack, AudioClip, FadeType } from '@waveform-playlist/core';\nimport { type EffectsFunction, getUnderlyingAudioParam } from '@waveform-playlist/playout';\nimport { encodeWav, downloadBlob, type WavEncoderOptions } from '../utils/wavEncoder';\n\n/** Function type for per-track effects (same as in @waveform-playlist/core) */\nexport type TrackEffectsFunction = (graphEnd: unknown, destination: unknown, isOffline: boolean) => void | (() => void);\n\nexport interface ExportOptions extends WavEncoderOptions {\n /** Filename for download (without extension) */\n filename?: string;\n /** Export mode: 'master' for stereo mix, 'individual' for single track */\n mode?: 'master' | 'individual';\n /** Track index for individual export (only used when mode is 'individual') */\n trackIndex?: number;\n /** Whether to trigger automatic download */\n autoDownload?: boolean;\n /** Whether to apply effects (fades, etc.) - defaults to true */\n applyEffects?: boolean;\n /**\n * Optional Tone.js effects function for master effects. When provided, export will use Tone.Offline\n * to render through the effects chain. The function receives isOffline=true.\n */\n effectsFunction?: EffectsFunction;\n /**\n * Optional function to create offline track effects.\n * Takes a trackId and returns a TrackEffectsFunction for offline rendering.\n * This is used instead of track.effects to avoid AudioContext mismatch issues.\n */\n createOfflineTrackEffects?: (trackId: string) => TrackEffectsFunction | undefined;\n /** Progress callback (0-1) */\n onProgress?: (progress: number) => void;\n}\n\nexport interface ExportResult {\n /** The rendered audio buffer */\n audioBuffer: AudioBuffer;\n /** The WAV file as a Blob */\n blob: Blob;\n /** Duration in seconds */\n duration: number;\n}\n\nexport interface UseExportWavReturn {\n /** Export the playlist to WAV */\n exportWav: (tracks: ClipTrack[], trackStates: TrackState[], options?: ExportOptions) => Promise<ExportResult>;\n /** Whether export is in progress */\n isExporting: boolean;\n /** Export progress (0-1) */\n progress: number;\n /** Error message if export failed */\n error: string | null;\n}\n\ninterface TrackState {\n muted: boolean;\n soloed: boolean;\n volume: number;\n pan: number;\n}\n\n/**\n * Hook for exporting the waveform playlist to WAV format\n * Uses OfflineAudioContext for fast, non-real-time rendering\n */\nexport function useExportWav(): UseExportWavReturn {\n const [isExporting, setIsExporting] = useState(false);\n const [progress, setProgress] = useState(0);\n const [error, setError] = useState<string | null>(null);\n\n const exportWav = useCallback(async (\n tracks: ClipTrack[],\n trackStates: TrackState[],\n options: ExportOptions = {}\n ): Promise<ExportResult> => {\n const {\n filename = 'export',\n mode = 'master',\n trackIndex,\n autoDownload = true,\n applyEffects = true,\n effectsFunction,\n createOfflineTrackEffects,\n bitDepth = 16,\n onProgress,\n } = options;\n\n setIsExporting(true);\n setProgress(0);\n setError(null);\n\n try {\n // Validate inputs\n if (tracks.length === 0) {\n throw new Error('No tracks to export');\n }\n\n if (mode === 'individual' && (trackIndex === undefined || trackIndex < 0 || trackIndex >= tracks.length)) {\n throw new Error('Invalid track index for individual export');\n }\n\n // Get sample rate from first clip (use clip.sampleRate which is always defined)\n const sampleRate = tracks[0].clips[0]?.sampleRate || 44100;\n\n // Calculate total duration from all clips (in samples)\n let totalDurationSamples = 0;\n for (const track of tracks) {\n for (const clip of track.clips) {\n const clipEndSample = clip.startSample + clip.durationSamples;\n totalDurationSamples = Math.max(totalDurationSamples, clipEndSample);\n }\n }\n\n // Add a small buffer at the end (0.1 seconds) to avoid cutting off\n totalDurationSamples += Math.round(sampleRate * 0.1);\n\n const duration = totalDurationSamples / sampleRate;\n\n // Determine which tracks to render\n const tracksToRender = mode === 'individual'\n ? [{ track: tracks[trackIndex!], state: trackStates[trackIndex!], index: trackIndex! }]\n : tracks.map((track, index) => ({ track, state: trackStates[index], index }));\n\n // Check for solo - if any track is soloed, only play soloed tracks\n const hasSolo = trackStates.some(state => state.soloed);\n\n // Check if per-track effects are provided via the offline creator function\n // Note: We don't use track.effects directly for offline rendering to avoid AudioContext mismatch\n const hasOfflineTrackEffects = !!createOfflineTrackEffects;\n\n let renderedBuffer: AudioBuffer;\n\n if ((effectsFunction || hasOfflineTrackEffects) && applyEffects) {\n // Use Tone.Offline for rendering with effects (master and/or per-track)\n renderedBuffer = await renderWithToneEffects(\n tracksToRender,\n trackStates,\n hasSolo,\n duration,\n sampleRate,\n effectsFunction,\n createOfflineTrackEffects,\n (p) => {\n setProgress(p);\n onProgress?.(p);\n }\n );\n } else {\n // Use standard OfflineAudioContext rendering\n const offlineCtx = new OfflineAudioContext(2, totalDurationSamples, sampleRate);\n\n // Schedule all clips for rendering\n let scheduledClips = 0;\n const totalClips = tracksToRender.reduce((sum, { track }) => sum + track.clips.length, 0);\n\n for (const { track, state } of tracksToRender) {\n // Skip muted tracks (unless soloed)\n if (state.muted && !state.soloed) continue;\n // If there's a solo and this track isn't soloed, skip it\n if (hasSolo && !state.soloed) continue;\n\n for (const clip of track.clips) {\n await scheduleClip(offlineCtx, clip, state, sampleRate, applyEffects);\n scheduledClips++;\n const currentProgress = scheduledClips / totalClips * 0.5; // First 50% is scheduling\n setProgress(currentProgress);\n onProgress?.(currentProgress);\n }\n }\n\n // Render the audio\n setProgress(0.5);\n onProgress?.(0.5);\n\n renderedBuffer = await offlineCtx.startRendering();\n }\n\n setProgress(0.9);\n onProgress?.(0.9);\n\n // Encode to WAV\n const blob = encodeWav(renderedBuffer, { bitDepth });\n\n setProgress(1);\n onProgress?.(1);\n\n // Auto download if requested\n if (autoDownload) {\n const exportFilename = mode === 'individual'\n ? `${filename}_${tracks[trackIndex!].name}`\n : filename;\n downloadBlob(blob, `${exportFilename}.wav`);\n }\n\n return {\n audioBuffer: renderedBuffer,\n blob,\n duration,\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Export failed';\n setError(message);\n throw err;\n } finally {\n setIsExporting(false);\n }\n }, []);\n\n return {\n exportWav,\n isExporting,\n progress,\n error,\n };\n}\n\n/**\n * Render using Tone.Offline with effects chain (master and/or per-track)\n */\nasync function renderWithToneEffects(\n tracksToRender: { track: ClipTrack; state: TrackState; index: number }[],\n _trackStates: TrackState[],\n hasSolo: boolean,\n duration: number,\n sampleRate: number,\n effectsFunction: EffectsFunction | undefined,\n createOfflineTrackEffects: ((trackId: string) => TrackEffectsFunction | undefined) | undefined,\n onProgress: (progress: number) => void\n): Promise<AudioBuffer> {\n // Dynamically import Tone.js modules\n const { Offline, Volume, Gain, Panner, Player, ToneAudioBuffer } = await import('tone');\n\n onProgress(0.1);\n\n // Use Tone.Offline to render with effects\n let buffer;\n try {\n buffer = await Offline(\n async ({ transport, destination }) => {\n // Create master volume node\n const masterVolume = new Volume(0); // 0 dB = unity gain\n\n // Apply master effects chain if provided, otherwise connect directly to destination\n let cleanup: void | (() => void) = undefined;\n if (effectsFunction) {\n cleanup = effectsFunction(masterVolume, destination, true);\n } else {\n masterVolume.connect(destination);\n }\n\n // Schedule all clips\n for (const { track, state } of tracksToRender) {\n // Skip muted tracks (unless soloed)\n if (state.muted && !state.soloed) continue;\n // If there's a solo and this track isn't soloed, skip it\n if (hasSolo && !state.soloed) continue;\n\n // Create track-level nodes\n const trackVolume = new Volume(gainToDb(state.volume));\n const trackPan = new Panner(state.pan);\n const trackMute = new Gain(state.muted ? 0 : 1);\n\n // Get offline track effects using the creator function\n // Note: We use createOfflineTrackEffects instead of track.effects to avoid AudioContext mismatch\n const trackEffects = createOfflineTrackEffects?.(track.id);\n\n if (trackEffects) {\n // Apply per-track effects chain: trackMute -> effects -> masterVolume\n trackEffects(trackMute, masterVolume, true);\n } else {\n // No per-track effects: connect directly to master\n trackMute.connect(masterVolume);\n }\n\n // Connect track chain: clips -> trackVolume -> trackPan -> trackMute\n trackPan.connect(trackMute);\n trackVolume.connect(trackPan);\n\n // Schedule each clip\n for (const clip of track.clips) {\n const { audioBuffer, startSample, durationSamples, offsetSamples, gain: clipGain, fadeIn, fadeOut } = clip;\n\n // Convert samples to seconds\n const startTime = startSample / sampleRate;\n const clipDuration = durationSamples / sampleRate;\n const offset = offsetSamples / sampleRate;\n\n // Create a ToneAudioBuffer from the existing AudioBuffer\n const toneBuffer = new ToneAudioBuffer(audioBuffer);\n\n // Create player for this clip\n const player = new Player(toneBuffer);\n\n // Create fade gain for clip-level effects\n const fadeGain = new Gain(clipGain);\n\n // Connect player -> fadeGain -> trackVolume\n player.connect(fadeGain);\n fadeGain.connect(trackVolume);\n\n // Apply fades using gain automation\n // New simple API: fadeIn starts at clip start, fadeOut ends at clip end\n if (fadeIn) {\n const fadeInStart = startTime;\n const fadeInEnd = startTime + fadeIn.duration;\n const audioParam = getUnderlyingAudioParam(fadeGain.gain);\n if (audioParam) {\n // Set initial value to 0\n audioParam.setValueAtTime(0, fadeInStart);\n audioParam.linearRampToValueAtTime(clipGain, fadeInEnd);\n }\n }\n\n if (fadeOut) {\n const fadeOutStart = startTime + clipDuration - fadeOut.duration;\n const fadeOutEnd = startTime + clipDuration;\n const audioParam = getUnderlyingAudioParam(fadeGain.gain);\n if (audioParam) {\n audioParam.setValueAtTime(clipGain, fadeOutStart);\n audioParam.linearRampToValueAtTime(0, fadeOutEnd);\n }\n }\n\n // Schedule the player to start\n player.start(startTime, offset, clipDuration);\n }\n }\n\n // Start the transport\n transport.start(0);\n\n // Clean up effects if cleanup function was provided\n if (cleanup) {\n // Note: cleanup will be called after rendering completes\n }\n },\n duration,\n 2, // stereo\n sampleRate\n );\n } catch (err) {\n // Re-throw with a proper Error object if needed\n if (err instanceof Error) {\n throw err;\n } else {\n throw new Error(`Tone.Offline rendering failed: ${String(err)}`);\n }\n }\n\n onProgress(0.9);\n\n // Convert ToneAudioBuffer to standard AudioBuffer\n return buffer.get() as AudioBuffer;\n}\n\n/**\n * Convert linear gain to decibels\n */\nfunction gainToDb(gain: number): number {\n return 20 * Math.log10(Math.max(gain, 0.0001));\n}\n\n/**\n * Schedule a single clip in the offline context\n */\nasync function scheduleClip(\n ctx: OfflineAudioContext,\n clip: AudioClip,\n trackState: TrackState,\n sampleRate: number,\n applyEffects: boolean\n): Promise<void> {\n const { audioBuffer, startSample, durationSamples, offsetSamples, gain: clipGain, fadeIn, fadeOut } = clip;\n\n // Skip clips without audioBuffer (peaks-only clips can't be exported)\n if (!audioBuffer) {\n console.warn(`Skipping clip \"${clip.name || clip.id}\" - no audioBuffer for export`);\n return;\n }\n\n // Convert samples to seconds for Web Audio API\n const startTime = startSample / sampleRate;\n const duration = durationSamples / sampleRate;\n const offset = offsetSamples / sampleRate;\n\n // Create buffer source\n const source = ctx.createBufferSource();\n source.buffer = audioBuffer;\n\n // Create gain node for clip + track volume\n const gainNode = ctx.createGain();\n const baseGain = clipGain * trackState.volume;\n\n // Create stereo panner for track pan\n const pannerNode = ctx.createStereoPanner();\n pannerNode.pan.value = trackState.pan;\n\n // Connect: source -> gain -> panner -> destination\n source.connect(gainNode);\n gainNode.connect(pannerNode);\n pannerNode.connect(ctx.destination);\n\n // Apply effects (fades) if enabled\n // New simple API: fadeIn starts at clip start, fadeOut ends at clip end\n if (applyEffects) {\n // Set initial gain (may be 0 if fade in exists)\n if (fadeIn) {\n gainNode.gain.setValueAtTime(0, startTime);\n } else {\n gainNode.gain.setValueAtTime(baseGain, startTime);\n }\n\n // Apply fade in\n if (fadeIn) {\n const fadeInStart = startTime;\n const fadeInEnd = startTime + fadeIn.duration;\n applyFadeEnvelope(gainNode.gain, fadeInStart, fadeInEnd, 0, baseGain, fadeIn.type || 'linear');\n }\n\n // Apply fade out\n if (fadeOut) {\n const fadeOutStart = startTime + duration - fadeOut.duration;\n const fadeOutEnd = startTime + duration;\n // Ensure we're at baseGain before fade out starts\n if (!fadeIn || fadeIn.duration < (duration - fadeOut.duration)) {\n gainNode.gain.setValueAtTime(baseGain, fadeOutStart);\n }\n applyFadeEnvelope(gainNode.gain, fadeOutStart, fadeOutEnd, baseGain, 0, fadeOut.type || 'linear');\n }\n } else {\n // No effects - just set constant gain\n gainNode.gain.setValueAtTime(baseGain, startTime);\n }\n\n // Schedule playback\n source.start(startTime, offset, duration);\n}\n\n/**\n * Apply a fade envelope to a gain parameter using Web Audio automation\n */\nfunction applyFadeEnvelope(\n gainParam: AudioParam,\n startTime: number,\n endTime: number,\n startValue: number,\n endValue: number,\n fadeType: FadeType\n): void {\n const duration = endTime - startTime;\n if (duration <= 0) return;\n\n switch (fadeType) {\n case 'linear':\n gainParam.setValueAtTime(startValue, startTime);\n gainParam.linearRampToValueAtTime(endValue, endTime);\n break;\n\n case 'exponential':\n {\n // Exponential can't handle 0 values, use small value instead\n const expStart = Math.max(startValue, 0.0001);\n const expEnd = Math.max(endValue, 0.0001);\n gainParam.setValueAtTime(expStart, startTime);\n gainParam.exponentialRampToValueAtTime(expEnd, endTime);\n // Set to actual 0 if needed\n if (endValue === 0) {\n gainParam.setValueAtTime(0, endTime);\n }\n break;\n }\n\n case 'logarithmic':\n {\n // Logarithmic fade - more aggressive at start, gentler at end\n // Implemented using setValueCurveAtTime with calculated curve\n const logCurve = generateFadeCurve(startValue, endValue, 256, 'logarithmic');\n gainParam.setValueCurveAtTime(logCurve, startTime, duration);\n break;\n }\n\n case 'sCurve':\n {\n // S-curve (ease-in-out) - smooth start and end\n const sCurve = generateFadeCurve(startValue, endValue, 256, 'sCurve');\n gainParam.setValueCurveAtTime(sCurve, startTime, duration);\n break;\n }\n\n default:\n // Default to linear\n gainParam.setValueAtTime(startValue, startTime);\n gainParam.linearRampToValueAtTime(endValue, endTime);\n }\n}\n\n/**\n * Generate a fade curve for setValueCurveAtTime\n */\nfunction generateFadeCurve(\n startValue: number,\n endValue: number,\n numPoints: number,\n curveType: 'logarithmic' | 'sCurve'\n): Float32Array {\n const curve = new Float32Array(numPoints);\n const range = endValue - startValue;\n\n for (let i = 0; i < numPoints; i++) {\n const t = i / (numPoints - 1); // 0 to 1\n\n let curveValue: number;\n if (curveType === 'logarithmic') {\n // Logarithmic: fast at start, slow at end (for fade out)\n // or slow at start, fast at end (for fade in)\n if (range > 0) {\n // Fade in: use log curve\n curveValue = Math.log10(1 + t * 9) / Math.log10(10);\n } else {\n // Fade out: use inverse log curve\n curveValue = 1 - Math.log10(1 + (1 - t) * 9) / Math.log10(10);\n }\n } else {\n // S-curve (smoothstep)\n curveValue = t * t * (3 - 2 * t);\n }\n\n curve[i] = startValue + range * curveValue;\n }\n\n return curve;\n}\n\n/**\n * Export types\n */\nexport type { WavEncoderOptions };\n","import { useCallback, useEffect, useRef } from 'react';\n\nexport interface AnimationFrameLoopControls {\n animationFrameRef: React.MutableRefObject<number | null>;\n startAnimationFrameLoop: (callback: () => void) => void;\n stopAnimationFrameLoop: () => void;\n}\n\n/**\n * Shared RAF loop controller used by playlist providers.\n * Always guarantees a single in-flight animation frame.\n */\nexport const useAnimationFrameLoop = (): AnimationFrameLoopControls => {\n const animationFrameRef = useRef<number | null>(null);\n\n const stopAnimationFrameLoop = useCallback(() => {\n if (animationFrameRef.current !== null) {\n cancelAnimationFrame(animationFrameRef.current);\n animationFrameRef.current = null;\n }\n }, []);\n\n const startAnimationFrameLoop = useCallback((callback: () => void) => {\n stopAnimationFrameLoop();\n animationFrameRef.current = requestAnimationFrame(callback);\n }, [stopAnimationFrameLoop]);\n\n useEffect(() => {\n return () => {\n stopAnimationFrameLoop();\n };\n }, [stopAnimationFrameLoop]);\n\n return {\n animationFrameRef,\n startAnimationFrameLoop,\n stopAnimationFrameLoop,\n };\n};\n","/**\n * Inline Web Worker for generating WaveformData binary format from AudioBuffer channels.\n *\n * Uses a Blob URL approach (portable across all bundlers) with the generateWaveformData\n * algorithm adapted from BBC's waveform-data.js (MIT licensed, adapted from Audacity).\n *\n * The worker generates peaks at a base scale (finest zoom level). The main thread\n * then uses WaveformData.resample() for near-instant zoom changes.\n */\n\nimport WaveformData from 'waveform-data';\n\n// ────────────────────────────────────────────────────────────────────────────\n// Worker code (runs inside the Blob URL worker)\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Self-contained worker source.\n * Contains the generateWaveformData function from waveform-data.js/src/waveform-generator.js\n * (MIT License, BBC). Pure JS, zero dependencies.\n */\nconst workerSource = `\n\"use strict\";\n\nvar INT8_MAX = 127;\nvar INT8_MIN = -128;\nvar INT16_MAX = 32767;\nvar INT16_MIN = -32768;\n\nfunction calculateWaveformDataLength(audio_sample_count, scale) {\n var data_length = Math.floor(audio_sample_count / scale);\n var samples_remaining = audio_sample_count - (data_length * scale);\n if (samples_remaining > 0) {\n data_length++;\n }\n return data_length;\n}\n\nfunction generateWaveformData(options) {\n var scale = options.scale;\n var amplitude_scale = options.amplitude_scale;\n var split_channels = options.split_channels;\n var length = options.length;\n var sample_rate = options.sample_rate;\n var channels = options.channels.map(function(channel) {\n return new Float32Array(channel);\n });\n var output_channels = split_channels ? channels.length : 1;\n var header_size = 24;\n var data_length = calculateWaveformDataLength(length, scale);\n var bytes_per_sample = options.bits === 8 ? 1 : 2;\n var total_size = header_size + data_length * 2 * bytes_per_sample * output_channels;\n var buffer = new ArrayBuffer(total_size);\n var data_view = new DataView(buffer);\n\n var scale_counter = 0;\n var offset = header_size;\n\n var min_value = new Array(output_channels);\n var max_value = new Array(output_channels);\n\n for (var channel = 0; channel < output_channels; channel++) {\n min_value[channel] = Infinity;\n max_value[channel] = -Infinity;\n }\n\n var range_min = options.bits === 8 ? INT8_MIN : INT16_MIN;\n var range_max = options.bits === 8 ? INT8_MAX : INT16_MAX;\n\n data_view.setInt32(0, 2, true);\n data_view.setUint32(4, options.bits === 8, true);\n data_view.setInt32(8, sample_rate, true);\n data_view.setInt32(12, scale, true);\n data_view.setInt32(16, data_length, true);\n data_view.setInt32(20, output_channels, true);\n\n for (var i = 0; i < length; i++) {\n var sample = 0;\n\n if (output_channels === 1) {\n for (var ch = 0; ch < channels.length; ++ch) {\n sample += channels[ch][i];\n }\n sample = Math.floor(range_max * sample * amplitude_scale / channels.length);\n\n if (sample < min_value[0]) {\n min_value[0] = sample;\n if (min_value[0] < range_min) {\n min_value[0] = range_min;\n }\n }\n if (sample > max_value[0]) {\n max_value[0] = sample;\n if (max_value[0] > range_max) {\n max_value[0] = range_max;\n }\n }\n }\n else {\n for (var ch2 = 0; ch2 < output_channels; ++ch2) {\n sample = Math.floor(range_max * channels[ch2][i] * amplitude_scale);\n\n if (sample < min_value[ch2]) {\n min_value[ch2] = sample;\n if (min_value[ch2] < range_min) {\n min_value[ch2] = range_min;\n }\n }\n if (sample > max_value[ch2]) {\n max_value[ch2] = sample;\n if (max_value[ch2] > range_max) {\n max_value[ch2] = range_max;\n }\n }\n }\n }\n\n if (++scale_counter === scale) {\n for (var ch3 = 0; ch3 < output_channels; ch3++) {\n if (options.bits === 8) {\n data_view.setInt8(offset++, min_value[ch3]);\n data_view.setInt8(offset++, max_value[ch3]);\n }\n else {\n data_view.setInt16(offset, min_value[ch3], true);\n data_view.setInt16(offset + 2, max_value[ch3], true);\n offset += 4;\n }\n min_value[ch3] = Infinity;\n max_value[ch3] = -Infinity;\n }\n scale_counter = 0;\n }\n }\n\n if (scale_counter > 0) {\n for (var ch4 = 0; ch4 < output_channels; ch4++) {\n if (options.bits === 8) {\n data_view.setInt8(offset++, min_value[ch4]);\n data_view.setInt8(offset++, max_value[ch4]);\n }\n else {\n data_view.setInt16(offset, min_value[ch4], true);\n data_view.setInt16(offset + 2, max_value[ch4], true);\n }\n }\n }\n\n return buffer;\n}\n\nself.onmessage = function(e) {\n var msg = e.data;\n try {\n var result = generateWaveformData({\n scale: msg.scale,\n bits: msg.bits,\n amplitude_scale: msg.amplitude_scale,\n split_channels: msg.split_channels,\n length: msg.length,\n sample_rate: msg.sample_rate,\n channels: msg.channels\n });\n self.postMessage({ id: msg.id, buffer: result }, [result]);\n } catch (err) {\n self.postMessage({ id: msg.id, error: err.message || String(err) });\n }\n};\n`;\n\n// ────────────────────────────────────────────────────────────────────────────\n// Promise-based worker API (runs on the main thread)\n// ────────────────────────────────────────────────────────────────────────────\n\nexport interface PeaksWorkerApi {\n generate(params: {\n id: string;\n channels: ArrayBuffer[];\n length: number;\n sampleRate: number;\n scale: number;\n bits: 8 | 16;\n splitChannels: boolean;\n }): Promise<WaveformData>;\n terminate(): void;\n}\n\ninterface PendingEntry {\n resolve: (value: WaveformData) => void;\n reject: (reason: unknown) => void;\n}\n\nlet idCounter = 0;\n\nexport function createPeaksWorker(): PeaksWorkerApi {\n let worker: Worker;\n try {\n const blob = new Blob([workerSource], { type: 'application/javascript' });\n const url = URL.createObjectURL(blob);\n worker = new Worker(url);\n URL.revokeObjectURL(url);\n } catch (err) {\n // Worker creation can fail in CSP-restricted environments that block blob: URLs.\n // Return a no-op API that rejects all generate calls gracefully.\n console.warn('[waveform-playlist] Failed to create peaks worker (CSP restriction?):', err);\n return {\n generate() {\n return Promise.reject(new Error('Worker creation failed'));\n },\n terminate() { /* no-op */ },\n };\n }\n\n const pending = new Map<string, PendingEntry>();\n let terminated = false;\n\n worker.onmessage = (e: MessageEvent) => {\n const msg = e.data;\n const entry = pending.get(msg.id);\n if (!entry) return;\n pending.delete(msg.id);\n\n if (msg.error) {\n entry.reject(new Error(msg.error));\n } else {\n try {\n const waveformData = WaveformData.create(msg.buffer);\n entry.resolve(waveformData);\n } catch (err) {\n entry.reject(err);\n }\n }\n };\n\n worker.onerror = (e: ErrorEvent) => {\n terminated = true;\n worker.terminate();\n for (const [, entry] of pending) {\n entry.reject(e.error ?? new Error(e.message));\n }\n pending.clear();\n };\n\n return {\n generate(params) {\n if (terminated) return Promise.reject(new Error('Worker terminated'));\n const messageId = String(++idCounter);\n\n return new Promise<WaveformData>((resolve, reject) => {\n pending.set(messageId, { resolve, reject });\n\n worker.postMessage(\n {\n id: messageId,\n scale: params.scale,\n bits: params.bits,\n amplitude_scale: 1.0,\n split_channels: params.splitChannels,\n length: params.length,\n sample_rate: params.sampleRate,\n channels: params.channels,\n },\n params.channels, // Transfer ownership\n );\n });\n },\n\n terminate() {\n terminated = true;\n worker.terminate();\n for (const [, entry] of pending) {\n entry.reject(new Error('Worker terminated'));\n }\n pending.clear();\n },\n };\n}\n","/**\n * useWaveformDataCache\n *\n * Manages a Map<clipId, WaveformData> that stores pre-computed WaveformData\n * objects generated by a web worker. These enable near-instant zoom changes\n * via WaveformData.resample() instead of re-scanning raw audio samples.\n *\n * Behavior:\n * - Creates the worker lazily on first clip needing generation\n * - Tracks submitted clip IDs via ref to avoid duplicate worker jobs\n * - Uses a cancelled flag to ignore responses after effect cleanup\n * - Terminates the worker on unmount\n */\n\nimport { useState, useEffect, useRef, useCallback } from 'react';\nimport type { ClipTrack } from '@waveform-playlist/core';\nimport type WaveformData from 'waveform-data';\nimport { createPeaksWorker, type PeaksWorkerApi } from '../workers/peaksWorker';\n\nexport interface UseWaveformDataCacheReturn {\n cache: ReadonlyMap<string, WaveformData>;\n isGenerating: boolean;\n}\n\nexport function useWaveformDataCache(\n tracks: ClipTrack[],\n baseScale: number,\n): UseWaveformDataCacheReturn {\n const [cache, setCache] = useState<Map<string, WaveformData>>(() => new Map());\n const [isGenerating, setIsGenerating] = useState(false);\n\n const workerRef = useRef<PeaksWorkerApi | null>(null);\n const submittedRef = useRef<Set<string>>(new Set());\n const pendingCountRef = useRef(0);\n\n // Stable callback to get or create the worker\n const getWorker = useCallback(() => {\n if (!workerRef.current) {\n workerRef.current = createPeaksWorker();\n }\n return workerRef.current;\n }, []);\n\n useEffect(() => {\n let cancelled = false;\n const submitted = submittedRef.current;\n\n // Find clips that have audioBuffer but no waveformData and haven't been submitted\n const clipsToProcess: { clipId: string; audioBuffer: AudioBuffer }[] = [];\n\n for (const track of tracks) {\n for (const clip of track.clips) {\n if (\n clip.audioBuffer &&\n !clip.waveformData &&\n !submitted.has(clip.id)\n ) {\n clipsToProcess.push({\n clipId: clip.id,\n audioBuffer: clip.audioBuffer,\n });\n }\n }\n }\n\n if (clipsToProcess.length === 0) return;\n\n // Mark all as submitted before starting async work\n const submittedThisRun = new Set<string>();\n for (const { clipId } of clipsToProcess) {\n submitted.add(clipId);\n submittedThisRun.add(clipId);\n }\n\n pendingCountRef.current += clipsToProcess.length;\n setIsGenerating(true);\n\n const worker = getWorker();\n\n for (const { clipId, audioBuffer } of clipsToProcess) {\n // .slice() channel buffers to avoid detaching the original AudioBuffer views\n const channels: ArrayBuffer[] = [];\n for (let c = 0; c < audioBuffer.numberOfChannels; c++) {\n channels.push(audioBuffer.getChannelData(c).slice().buffer);\n }\n\n worker\n .generate({\n id: clipId,\n channels,\n length: audioBuffer.length,\n sampleRate: audioBuffer.sampleRate,\n scale: baseScale,\n bits: 16,\n splitChannels: true,\n })\n .then((waveformData) => {\n if (cancelled) return;\n\n setCache((prev) => {\n const next = new Map(prev);\n next.set(clipId, waveformData);\n return next;\n });\n\n pendingCountRef.current--;\n if (pendingCountRef.current <= 0) {\n pendingCountRef.current = 0;\n setIsGenerating(false);\n }\n })\n .catch((err) => {\n if (cancelled) return;\n console.warn('[waveform-playlist] Worker peak generation failed:', err);\n // Remove from submitted so it can be retried on next effect run\n submitted.delete(clipId);\n pendingCountRef.current--;\n if (pendingCountRef.current <= 0) {\n pendingCountRef.current = 0;\n setIsGenerating(false);\n }\n });\n }\n\n return () => {\n cancelled = true;\n // Clear IDs submitted by this effect run so they can be resubmitted\n // if the effect re-runs with new deps (e.g., tracks changed)\n for (const clipId of submittedThisRun) {\n submitted.delete(clipId);\n }\n };\n // submittedRef guards against duplicate submissions — no need for cache in deps\n }, [tracks, baseScale, getWorker]);\n\n // Terminate worker on unmount\n useEffect(() => {\n return () => {\n workerRef.current?.terminate();\n workerRef.current = null;\n };\n }, []);\n\n return { cache, isGenerating };\n}\n","import React, { createContext, useContext, useState, useEffect, useRef, useCallback, useMemo, type ReactNode } from 'react';\nimport { ThemeProvider } from 'styled-components';\nimport { TonePlayout, type EffectsFunction, type TrackEffectsFunction } from '@waveform-playlist/playout';\nimport { type Track, type ClipTrack, type Fade, type AnnotationAction } from '@waveform-playlist/core';\nimport { type TimeFormat, type WaveformPlaylistTheme, defaultTheme } from '@waveform-playlist/ui-components';\nimport { getContext } from 'tone';\nimport { extractPeaksFromWaveformDataFull } from './waveformDataLoader';\nimport type WaveformData from 'waveform-data';\nimport type { PeakData } from '@waveform-playlist/core';\nimport type { AnnotationData } from '@waveform-playlist/core';\nimport { useTimeFormat, useZoomControls, useMasterVolume, useAnimationFrameLoop, useWaveformDataCache } from './hooks';\n\n// Types\nexport interface ClipPeaks {\n clipId: string;\n trackName: string;\n peaks: PeakData;\n startSample: number;\n durationSamples: number;\n fadeIn?: Fade;\n fadeOut?: Fade;\n}\n\nexport type TrackClipPeaks = ClipPeaks[];\n\n// Legacy WaveformTrack type - kept for reference but deprecated\n// @deprecated Use ClipTrack from @waveform-playlist/core instead\nexport interface WaveformTrack {\n src: string | AudioBuffer;\n name?: string;\n effects?: TrackEffectsFunction;\n}\n\nexport interface TrackState {\n name: string;\n muted: boolean;\n soloed: boolean;\n volume: number;\n pan: number;\n}\n\n// Split contexts for performance optimization\n// Animation context contains playback state and timing refs — no per-frame state updates\n\nexport interface PlaybackAnimationContextValue {\n isPlaying: boolean;\n currentTime: number;\n currentTimeRef: React.RefObject<number>;\n // Refs for direct time calculation in animated components (avoids timing drift)\n playbackStartTimeRef: React.RefObject<number>; // context.currentTime when playback started\n audioStartPositionRef: React.RefObject<number>; // Audio position when playback started\n}\n\nexport interface PlaylistStateContextValue {\n continuousPlay: boolean;\n linkEndpoints: boolean;\n annotationsEditable: boolean;\n isAutomaticScroll: boolean;\n isLoopEnabled: boolean;\n annotations: AnnotationData[];\n activeAnnotationId: string | null;\n selectionStart: number;\n selectionEnd: number;\n selectedTrackId: string | null; // ID of currently selected track for editing operations\n // Loop region (separate from selection) - Audacity-style loop points\n loopStart: number;\n loopEnd: number;\n}\n\nexport interface PlaylistControlsContextValue {\n // Playback controls\n play: (startTime?: number, playDuration?: number) => Promise<void>;\n pause: () => void;\n stop: () => void;\n seekTo: (time: number) => void;\n setCurrentTime: (time: number) => void;\n\n // Track controls\n setTrackMute: (trackIndex: number, muted: boolean) => void;\n setTrackSolo: (trackIndex: number, soloed: boolean) => void;\n setTrackVolume: (trackIndex: number, volume: number) => void;\n setTrackPan: (trackIndex: number, pan: number) => void;\n\n // Selection\n setSelection: (start: number, end: number) => void;\n setSelectedTrackId: (trackId: string | null) => void;\n\n // Time format\n setTimeFormat: (format: TimeFormat) => void;\n formatTime: (seconds: number) => string;\n\n // Zoom\n zoomIn: () => void;\n zoomOut: () => void;\n\n // Master volume\n setMasterVolume: (volume: number) => void;\n\n // Automatic scroll\n setAutomaticScroll: (enabled: boolean) => void;\n setScrollContainer: (element: HTMLDivElement | null) => void;\n scrollContainerRef: React.RefObject<HTMLDivElement | null>;\n\n // Annotation controls\n setContinuousPlay: (enabled: boolean) => void;\n setLinkEndpoints: (enabled: boolean) => void;\n setAnnotationsEditable: (enabled: boolean) => void;\n setAnnotations: React.Dispatch<React.SetStateAction<AnnotationData[]>>;\n setActiveAnnotationId: (id: string | null) => void;\n\n // Loop controls\n setLoopEnabled: (enabled: boolean) => void;\n setLoopRegion: (start: number, end: number) => void;\n setLoopRegionFromSelection: () => void;\n clearLoopRegion: () => void;\n\n}\n\nexport interface PlaylistDataContextValue {\n duration: number;\n audioBuffers: AudioBuffer[];\n peaksDataArray: TrackClipPeaks[]; // Array of tracks, each containing array of clip peaks\n trackStates: TrackState[];\n tracks: ClipTrack[]; // Original tracks array with IDs\n sampleRate: number;\n waveHeight: number;\n timeScaleHeight: number;\n minimumPlaylistHeight: number;\n controls: { show: boolean; width: number };\n playoutRef: React.RefObject<TonePlayout | null>;\n samplesPerPixel: number;\n timeFormat: TimeFormat;\n masterVolume: number;\n canZoomIn: boolean;\n canZoomOut: boolean;\n barWidth: number;\n barGap: number;\n /** Width in pixels of progress bars. Defaults to barWidth + barGap (fills gaps). */\n progressBarWidth: number;\n /** Whether the playlist has finished loading all tracks */\n isReady: boolean;\n /** Whether tracks are rendered in mono mode */\n mono: boolean;\n}\n\n// Create the 4 separate contexts\nconst PlaybackAnimationContext = createContext<PlaybackAnimationContextValue | null>(null);\nconst PlaylistStateContext = createContext<PlaylistStateContextValue | null>(null);\nconst PlaylistControlsContext = createContext<PlaylistControlsContextValue | null>(null);\nconst PlaylistDataContext = createContext<PlaylistDataContextValue | null>(null);\n\nexport interface WaveformPlaylistProviderProps {\n tracks: ClipTrack[]; // Updated to use clip-based model\n timescale?: boolean;\n mono?: boolean;\n waveHeight?: number;\n samplesPerPixel?: number;\n zoomLevels?: number[]; // Array of zoom levels in samples per pixel (lower = more zoomed in)\n automaticScroll?: boolean;\n theme?: Partial<WaveformPlaylistTheme>;\n controls?: {\n show: boolean;\n width: number;\n };\n annotationList?: {\n annotations?: AnnotationData[];\n editable?: boolean;\n isContinuousPlay?: boolean;\n linkEndpoints?: boolean;\n controls?: AnnotationAction[];\n };\n effects?: EffectsFunction;\n onReady?: () => void;\n /** @deprecated Use onAnnotationsChange instead */\n onAnnotationUpdate?: (annotations: AnnotationData[]) => void;\n /** Callback when annotations are changed (drag, edit, etc.) */\n onAnnotationsChange?: (annotations: AnnotationData[]) => void;\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). */\n progressBarWidth?: number;\n children: ReactNode;\n}\n\nexport const WaveformPlaylistProvider: React.FC<WaveformPlaylistProviderProps> = ({\n tracks,\n timescale = false,\n mono = false,\n waveHeight = 80,\n samplesPerPixel: initialSamplesPerPixel = 1024,\n zoomLevels,\n automaticScroll = false,\n theme: userTheme,\n controls = { show: false, width: 0 },\n annotationList,\n effects,\n onReady,\n onAnnotationUpdate: _onAnnotationUpdate,\n onAnnotationsChange,\n barWidth = 1,\n barGap = 0,\n progressBarWidth: progressBarWidthProp,\n children,\n}) => {\n // Default progressBarWidth to barWidth + barGap (fills gaps)\n const progressBarWidth = progressBarWidthProp ?? (barWidth + barGap);\n // Annotations are derived from prop (single source of truth in parent)\n // In v6, annotations must be pre-parsed (numeric start/end). Use parseAeneas() from @waveform-playlist/annotations before passing.\n const annotations = useMemo(() => {\n if (!annotationList?.annotations) return [];\n if (process.env.NODE_ENV !== 'production' && annotationList.annotations.length > 0) {\n const first = annotationList.annotations[0] as unknown as Record<string, unknown>;\n if (typeof first.start !== 'number' || typeof first.end !== 'number') {\n console.error(\n '[waveform-playlist] Annotations must have numeric start/end values. ' +\n 'In v6, use parseAeneas() from @waveform-playlist/annotations before passing annotations. ' +\n 'Received start type: ' + typeof first.start\n );\n return [];\n }\n }\n return annotationList.annotations;\n }, [annotationList?.annotations]);\n\n // Ref for animation loop (avoids restarting loop on annotation change)\n const annotationsRef = useRef<AnnotationData[]>(annotations);\n annotationsRef.current = annotations;\n\n // State\n const [activeAnnotationId, setActiveAnnotationIdState] = useState<string | null>(null);\n const [isPlaying, setIsPlaying] = useState(false);\n const [currentTime, setCurrentTime] = useState(0);\n const [duration, setDuration] = useState(0);\n const [audioBuffers, setAudioBuffers] = useState<AudioBuffer[]>([]);\n const [peaksDataArray, setPeaksDataArray] = useState<TrackClipPeaks[]>([]); // Updated for clip-based peaks\n const [trackStates, setTrackStates] = useState<TrackState[]>([]);\n const [selectionStart, setSelectionStart] = useState(0);\n const [selectionEnd, setSelectionEnd] = useState(0);\n const [selectedTrackId, setSelectedTrackId] = useState<string | null>(null);\n const [isAutomaticScroll, setIsAutomaticScroll] = useState(automaticScroll);\n const [continuousPlay, setContinuousPlayState] = useState(annotationList?.isContinuousPlay ?? false);\n const [linkEndpoints, setLinkEndpoints] = useState(annotationList?.linkEndpoints ?? false);\n const [annotationsEditable, setAnnotationsEditable] = useState(annotationList?.editable ?? false);\n const [isLoopEnabled, setIsLoopEnabledState] = useState(false);\n const [loopStart, setLoopStartState] = useState(0);\n const [loopEnd, setLoopEndState] = useState(0);\n const [isReady, setIsReady] = useState(false);\n\n // Refs\n const playoutRef = useRef<TonePlayout | null>(null);\n const playStartPositionRef = useRef<number>(0);\n const currentTimeRef = useRef<number>(0);\n const trackStatesRef = useRef<TrackState[]>(trackStates);\n const playbackStartTimeRef = useRef<number>(0); // context.currentTime when playback started\n const audioStartPositionRef = useRef<number>(0); // Audio position when playback started\n const playbackEndTimeRef = useRef<number | null>(null); // Audio position where playback should stop (for selections)\n const scrollContainerRef = useRef<HTMLDivElement | null>(null);\n const isAutomaticScrollRef = useRef<boolean>(false);\n const continuousPlayRef = useRef<boolean>(annotationList?.isContinuousPlay ?? false);\n const activeAnnotationIdRef = useRef<string | null>(null);\n const samplesPerPixelRef = useRef<number>(initialSamplesPerPixel);\n const isLoopEnabledRef = useRef<boolean>(false);\n const selectionStartRef = useRef<number>(0);\n const selectionEndRef = useRef<number>(0);\n const loopStartRef = useRef<number>(0);\n const loopEndRef = useRef<number>(0);\n\n // Custom hooks\n const { timeFormat, setTimeFormat, formatTime } = useTimeFormat();\n const zoom = useZoomControls({ initialSamplesPerPixel, zoomLevels });\n const samplesPerPixel = zoom.samplesPerPixel;\n const { masterVolume, setMasterVolume } = useMasterVolume({ playoutRef, initialVolume: 1.0 });\n const { animationFrameRef, startAnimationFrameLoop, stopAnimationFrameLoop } = useAnimationFrameLoop();\n\n // Worker-based WaveformData cache for fast zoom resampling\n const baseScale = useMemo(\n () => Math.min(...(zoomLevels ?? [256, 512, 1024, 2048, 4096, 8192])),\n [zoomLevels],\n );\n const { cache: waveformDataCache } = useWaveformDataCache(tracks, baseScale);\n\n // Custom setter for continuousPlay that updates BOTH state and ref synchronously\n // This ensures the ref is updated immediately, before the animation loop can read it\n const setContinuousPlay = useCallback((value: boolean) => {\n continuousPlayRef.current = value; // Update ref synchronously\n setContinuousPlayState(value); // Update state (triggers re-render)\n }, []);\n\n // Custom setter for activeAnnotationId that updates BOTH state and ref synchronously\n const setActiveAnnotationId = useCallback((value: string | null) => {\n activeAnnotationIdRef.current = value; // Update ref synchronously\n setActiveAnnotationIdState(value); // Update state (triggers re-render)\n }, []);\n\n // Custom setter for isLoopEnabled that updates BOTH state and ref synchronously\n const setLoopEnabled = useCallback((value: boolean) => {\n isLoopEnabledRef.current = value; // Update ref synchronously\n setIsLoopEnabledState(value); // Update state (triggers re-render)\n }, []);\n\n // Loop region setters - Audacity-style separate loop points\n const setLoopRegion = useCallback((start: number, end: number) => {\n loopStartRef.current = start;\n loopEndRef.current = end;\n setLoopStartState(start);\n setLoopEndState(end);\n }, []);\n\n const setLoopRegionFromSelection = useCallback(() => {\n const start = selectionStartRef.current;\n const end = selectionEndRef.current;\n if (start !== end && end > start) {\n setLoopRegion(start, end);\n }\n }, [setLoopRegion]);\n\n const clearLoopRegion = useCallback(() => {\n setLoopRegion(0, 0);\n }, [setLoopRegion]);\n\n // Keep refs in sync with state\n useEffect(() => {\n isAutomaticScrollRef.current = isAutomaticScroll;\n }, [isAutomaticScroll]);\n\n useEffect(() => {\n trackStatesRef.current = trackStates;\n }, [trackStates]);\n\n // Keep selection refs in sync for animation loop access\n useEffect(() => {\n selectionStartRef.current = selectionStart;\n selectionEndRef.current = selectionEnd;\n }, [selectionStart, selectionEnd]);\n\n // Adjust scroll position proportionally when zoom changes\n useEffect(() => {\n if (!scrollContainerRef.current || !audioBuffers.length) return;\n\n const container = scrollContainerRef.current;\n const oldSamplesPerPixel = samplesPerPixelRef.current;\n const newSamplesPerPixel = samplesPerPixel;\n\n if (oldSamplesPerPixel === newSamplesPerPixel) return;\n\n // Calculate the current center time in the viewport\n const controlWidth = controls.show ? controls.width : 0;\n const containerWidth = container.clientWidth;\n const currentScrollLeft = container.scrollLeft;\n const centerPixel = currentScrollLeft + containerWidth / 2 - controlWidth;\n const sr = audioBuffers[0].sampleRate;\n const centerTime = (centerPixel * oldSamplesPerPixel) / sr;\n\n // Calculate new scroll position to keep the same center time\n const newCenterPixel = (centerTime * sr) / newSamplesPerPixel;\n const newScrollLeft = Math.max(0, newCenterPixel + controlWidth - containerWidth / 2);\n\n container.scrollLeft = newScrollLeft;\n samplesPerPixelRef.current = newSamplesPerPixel;\n }, [samplesPerPixel, audioBuffers, controls]);\n\n // Track pending playback resume after tracks change\n const pendingResumeRef = useRef<{ position: number } | null>(null);\n\n // Load audio from clips (only when tracks change)\n useEffect(() => {\n // Reset ready state when tracks change\n setIsReady(false);\n\n if (tracks.length === 0) {\n // Clear state when all tracks are removed\n setAudioBuffers([]);\n setDuration(0);\n setTrackStates([]);\n setPeaksDataArray([]);\n if (playoutRef.current) {\n playoutRef.current.dispose();\n playoutRef.current = null;\n }\n return;\n }\n\n // Capture playback state before rebuilding playout\n const wasPlaying = isPlaying;\n const resumePosition = currentTimeRef.current;\n\n // Stop current playback and animation before disposing\n if (playoutRef.current && wasPlaying) {\n playoutRef.current.stop();\n stopAnimationFrameLoop();\n // Mark that we need to resume playback after playout is rebuilt\n pendingResumeRef.current = { position: resumePosition };\n }\n\n const loadAudio = async () => {\n try {\n // Extract all audio buffers from clips (only those that have audioBuffer)\n // For now, collect the first clip's buffer from each track\n const buffers: AudioBuffer[] = [];\n\n tracks.forEach((track) => {\n if (track.clips.length > 0 && track.clips[0].audioBuffer) {\n // Use first clip's buffer for now (full multi-clip support comes in next phase)\n buffers.push(track.clips[0].audioBuffer);\n }\n });\n\n // Calculate total timeline duration from all clips across all tracks\n // Use clip.sampleRate which is always defined (works for peaks-only clips too)\n let maxDuration = 0;\n tracks.forEach((track) => {\n track.clips.forEach((clip) => {\n const sampleRate = clip.sampleRate;\n const clipEndSample = clip.startSample + clip.durationSamples;\n const clipEnd = clipEndSample / sampleRate;\n maxDuration = Math.max(maxDuration, clipEnd);\n });\n });\n\n setAudioBuffers(buffers);\n setDuration(maxDuration);\n\n // Initialize or update track states, preserving existing UI state (mute/solo/volume/pan)\n // Only initialize from ClipTrack props when trackStates is empty or track count changes\n setTrackStates(prevStates => {\n if (prevStates.length === tracks.length) {\n // Same number of tracks - preserve existing UI state, just update names\n return prevStates.map((state, i) => ({\n ...state,\n name: tracks[i].name,\n }));\n }\n // Track count changed - reinitialize from ClipTrack properties\n return tracks.map((track) => ({\n name: track.name,\n muted: track.muted,\n soloed: track.soloed,\n volume: track.volume,\n pan: track.pan,\n }));\n });\n\n // Dispose old playout before creating new one\n if (playoutRef.current) {\n playoutRef.current.dispose();\n }\n\n // Create playout with clips\n const playout = new TonePlayout({\n effects,\n });\n\n // For each track, create a ToneTrack with all clips\n // Use trackStatesRef for current UI state (mute/solo/volume/pan) instead of track props\n const currentTrackStates = trackStatesRef.current;\n tracks.forEach((track, index) => {\n // Filter to only clips with audioBuffer (peaks-only clips can't be played)\n const playableClips = track.clips.filter(clip => clip.audioBuffer);\n\n if (playableClips.length > 0) {\n // Calculate track start and end times from clips (converting samples to seconds)\n // Use clip.sampleRate which is always defined\n const sampleRate = playableClips[0].sampleRate;\n const startTime = Math.min(...playableClips.map(c => c.startSample / sampleRate));\n const endTime = Math.max(...playableClips.map(c => (c.startSample + c.durationSamples) / sampleRate));\n\n // Use current UI state if available, otherwise fall back to track props\n const trackState = currentTrackStates[index];\n const trackObj: Track = {\n id: `track-${index}`, // Use consistent index-based ID for track controls\n name: track.name,\n gain: trackState?.volume ?? track.volume,\n muted: trackState?.muted ?? track.muted,\n soloed: trackState?.soloed ?? track.soloed,\n stereoPan: trackState?.pan ?? track.pan,\n startTime,\n endTime,\n };\n\n // Convert ClipTrack clips to ToneTrack ClipInfo format\n // Note: ClipInfo.startTime is relative to track start, not absolute timeline\n const clipInfos = playableClips.map(clip => {\n const clipSampleRate = clip.sampleRate;\n return {\n buffer: clip.audioBuffer!, // We filtered for audioBuffer above\n startTime: (clip.startSample / clipSampleRate) - startTime, // Make relative to track start\n duration: clip.durationSamples / clipSampleRate,\n offset: clip.offsetSamples / clipSampleRate,\n fadeIn: clip.fadeIn,\n fadeOut: clip.fadeOut,\n gain: clip.gain,\n };\n });\n\n playout.addTrack({\n clips: clipInfos,\n track: trackObj,\n effects: track.effects, // Pass track effects\n });\n }\n });\n\n // Apply solo muting after all tracks are added\n playout.applyInitialSoloState();\n\n playoutRef.current = playout;\n setIsReady(true);\n\n // Dispatch custom event for external listeners\n const event = new CustomEvent('waveform-playlist:ready', {\n detail: {\n trackCount: tracks.length,\n duration: maxDuration,\n },\n });\n window.dispatchEvent(event);\n\n onReady?.();\n } catch (error) {\n console.error('Error loading audio:', error);\n }\n };\n\n loadAudio();\n\n return () => {\n stopAnimationFrameLoop();\n if (playoutRef.current) {\n playoutRef.current.dispose();\n }\n };\n }, [tracks, onReady, isPlaying, effects, stopAnimationFrameLoop]);\n\n // Regenerate peaks when zoom, mono, or waveformDataCache changes (without reloading audio)\n // Peak sources in priority order:\n // A) clip.waveformData — external pre-computed peaks (e.g. from BBC audiowaveform)\n // B) waveformDataCache — worker-generated WaveformData (fast resample on zoom)\n // C) empty peaks — clip is still loading or has no audio data\n useEffect(() => {\n if (tracks.length === 0) return;\n\n const allTrackPeaks: TrackClipPeaks[] = tracks.map((track) => {\n const clipPeaks: ClipPeaks[] = track.clips.map((clip) => {\n let peaks: PeakData | undefined;\n\n // Path A: External pre-computed waveform data (e.g. from audiowaveform .dat file)\n if (clip.waveformData) {\n try {\n peaks = extractPeaksFromWaveformDataFull(\n clip.waveformData as WaveformData,\n samplesPerPixel,\n mono,\n clip.offsetSamples,\n clip.durationSamples,\n );\n } catch (err) {\n console.warn('[waveform-playlist] Failed to extract peaks from waveformData:', err);\n }\n }\n\n // Path B: Worker-generated WaveformData cache (fast resample on zoom)\n if (!peaks) {\n const cached = waveformDataCache.get(clip.id);\n if (cached) {\n try {\n peaks = extractPeaksFromWaveformDataFull(\n cached,\n samplesPerPixel,\n mono,\n clip.offsetSamples,\n clip.durationSamples,\n );\n } catch (err) {\n console.warn('[waveform-playlist] Failed to extract peaks from cache:', err);\n }\n }\n }\n\n // Path C: No peaks data available yet — render empty while worker processes\n if (!peaks) {\n if (!clip.audioBuffer && !clip.waveformData) {\n console.warn(`[waveform-playlist] Clip \"${clip.id}\" has no audio data or waveform data`);\n }\n peaks = { length: 0, data: [], bits: 16 };\n }\n\n return {\n clipId: clip.id,\n trackName: track.name,\n peaks,\n startSample: clip.startSample,\n durationSamples: clip.durationSamples,\n fadeIn: clip.fadeIn,\n fadeOut: clip.fadeOut,\n };\n });\n\n return clipPeaks;\n });\n\n setPeaksDataArray(allTrackPeaks);\n }, [tracks, samplesPerPixel, mono, waveformDataCache]);\n\n\n // Animation loop\n const startAnimationLoop = useCallback(() => {\n const updateTime = () => {\n // Calculate current position based on context.currentTime timing\n const elapsed = getContext().currentTime - playbackStartTimeRef.current;\n const time = audioStartPositionRef.current + elapsed;\n currentTimeRef.current = time;\n\n // Handle annotation playback based on continuous play mode\n const currentAnnotations = annotationsRef.current;\n if (currentAnnotations.length > 0) {\n const currentAnnotation = currentAnnotations.find(\n (ann) => time >= ann.start && time < ann.end\n );\n\n if (continuousPlayRef.current) {\n // Continuous play ON: update active annotation, let audio play to the end\n if (currentAnnotation && currentAnnotation.id !== activeAnnotationIdRef.current) {\n setActiveAnnotationId(currentAnnotation.id);\n } else if (!currentAnnotation && activeAnnotationIdRef.current !== null) {\n // Clear the active annotation when we're past it, but don't stop playback\n // Let playback continue until the audio actually ends (handled by duration check)\n setActiveAnnotationId(null);\n }\n } else {\n // Continuous play OFF: stop at end of current annotation\n if (activeAnnotationIdRef.current) {\n const activeAnnotation = currentAnnotations.find(ann => ann.id === activeAnnotationIdRef.current);\n if (activeAnnotation && time >= activeAnnotation.end) {\n // Stop playback at end of current annotation\n if (playoutRef.current) {\n playoutRef.current.stop();\n }\n setIsPlaying(false);\n currentTimeRef.current = playStartPositionRef.current;\n setCurrentTime(playStartPositionRef.current);\n return;\n }\n } else {\n // If no active annotation ID is set, use the current annotation\n if (currentAnnotation) {\n setActiveAnnotationId(currentAnnotation.id);\n }\n }\n }\n }\n\n // Handle automatic scroll - continuously center the playhead\n if (isAutomaticScrollRef.current && scrollContainerRef.current && audioBuffers.length > 0) {\n const container = scrollContainerRef.current;\n const sr = audioBuffers[0].sampleRate;\n const pixelPosition = (time * sr) / samplesPerPixelRef.current;\n const containerWidth = container.clientWidth;\n\n // Calculate visual position of playhead (includes controls offset)\n const controlWidth = controls.show ? controls.width : 0;\n const visualPosition = pixelPosition + controlWidth;\n\n // Continuously scroll to keep playhead centered\n const targetScrollLeft = Math.max(0, visualPosition - containerWidth / 2);\n container.scrollLeft = targetScrollLeft;\n }\n\n // Check if we've reached the playback end time (for selection playback)\n if (playbackEndTimeRef.current !== null && time >= playbackEndTimeRef.current) {\n // Stop playback at selection end (selection playback is separate from looping)\n if (playoutRef.current) {\n playoutRef.current.stop();\n }\n setIsPlaying(false);\n currentTimeRef.current = playbackEndTimeRef.current;\n setCurrentTime(playbackEndTimeRef.current);\n playbackEndTimeRef.current = null; // Clear the end time\n return;\n }\n\n // Audacity-style loop region: loop when cursor enters and reaches end of loop region\n const hasValidLoopRegion = loopStartRef.current !== loopEndRef.current &&\n loopEndRef.current > loopStartRef.current;\n\n if (isLoopEnabledRef.current && hasValidLoopRegion) {\n // Check if we've reached or passed the loop end point\n if (time >= loopEndRef.current) {\n // Loop: restart from loop start\n playoutRef.current?.stop();\n\n const context = getContext();\n const timeNow = context.currentTime;\n playbackStartTimeRef.current = timeNow;\n audioStartPositionRef.current = loopStartRef.current;\n currentTimeRef.current = loopStartRef.current;\n\n // Restart playback from loop start (no duration limit - will loop again when reaching loop end)\n playoutRef.current?.play(timeNow, loopStartRef.current);\n\n // Continue animation loop\n startAnimationFrameLoop(updateTime);\n return;\n }\n }\n\n if (time >= duration) {\n // Stop playback - inline to avoid circular dependency\n if (playoutRef.current) {\n playoutRef.current.stop();\n }\n setIsPlaying(false);\n currentTimeRef.current = playStartPositionRef.current;\n setCurrentTime(playStartPositionRef.current);\n setActiveAnnotationId(null);\n return;\n }\n startAnimationFrameLoop(updateTime);\n };\n startAnimationFrameLoop(updateTime);\n }, [duration, audioBuffers, controls.show, controls.width, setActiveAnnotationId, startAnimationFrameLoop]);\n\n const stopAnimationLoop = stopAnimationFrameLoop;\n\n // Restart animation loop and reschedule playout when continuousPlay changes during playback\n // This ensures the loop always has the current continuousPlay value\n // and removes duration limits when switching to continuous play\n useEffect(() => {\n const reschedulePlayback = async () => {\n if (isPlaying && animationFrameRef.current && playoutRef.current) {\n // When toggling continuous play ON, reschedule playout without duration limit\n // so audio continues past the current annotation boundary\n if (continuousPlay) {\n const currentPos = currentTimeRef.current;\n\n // Stop current playout (which may have duration limit + pause callback)\n playoutRef.current.stop();\n stopAnimationLoop();\n\n // Initialize and restart from current position without duration limit\n await playoutRef.current.init();\n\n // Clear any existing playback complete callback\n playoutRef.current.setOnPlaybackComplete(() => {});\n\n const context = getContext();\n const timeNow = context.currentTime;\n playbackStartTimeRef.current = timeNow;\n audioStartPositionRef.current = currentPos;\n\n // Play without duration - will play to end of track\n playoutRef.current.play(timeNow, currentPos);\n startAnimationLoop();\n } else {\n // Just restart animation loop for continuous play OFF\n stopAnimationLoop();\n startAnimationLoop();\n }\n }\n };\n\n reschedulePlayback();\n }, [continuousPlay, isPlaying, startAnimationLoop, stopAnimationLoop, animationFrameRef]);\n\n // Resume playback after tracks change (e.g., after splitting a clip during playback)\n useEffect(() => {\n const resumePlayback = async () => {\n if (pendingResumeRef.current && playoutRef.current) {\n const { position } = pendingResumeRef.current;\n pendingResumeRef.current = null;\n\n await playoutRef.current.init();\n playoutRef.current.setOnPlaybackComplete(() => {});\n\n const context = getContext();\n const timeNow = context.currentTime;\n playbackStartTimeRef.current = timeNow;\n audioStartPositionRef.current = position;\n\n playoutRef.current.play(timeNow, position);\n setIsPlaying(true);\n startAnimationLoop();\n }\n };\n\n resumePlayback();\n }, [tracks, startAnimationLoop]);\n\n // Playback controls\n const play = useCallback(async (startTime?: number, playDuration?: number) => {\n if (!playoutRef.current || audioBuffers.length === 0) return;\n\n await playoutRef.current.init();\n\n const actualStartTime = startTime ?? currentTimeRef.current;\n playStartPositionRef.current = actualStartTime;\n\n // Update currentTimeRef to match the actual start position\n // This ensures the animation loop starts from the correct position\n currentTimeRef.current = actualStartTime;\n\n // Clear any existing playback complete callback before stopping\n // Otherwise stopping will trigger the old callback and interfere with new playback\n playoutRef.current.setOnPlaybackComplete(() => {});\n\n // Stop any existing playback and animation loop before starting\n playoutRef.current.stop();\n stopAnimationLoop();\n\n // Record timing for accurate position tracking using Tone.js context\n const context = getContext();\n // Tone.js context wraps Web Audio - need to use .currentTime from wrapped context\n const startTimeNow = context.currentTime;\n playbackStartTimeRef.current = startTimeNow;\n audioStartPositionRef.current = actualStartTime;\n\n // Set playback end time if playing with duration (e.g., selection playback)\n playbackEndTimeRef.current = playDuration !== undefined ? actualStartTime + playDuration : null;\n\n // Don't set up playback complete callback for annotations\n // The animation loop handles stopping at annotation boundaries\n // This avoids callback timing issues when switching between annotations\n\n playoutRef.current.play(startTimeNow, actualStartTime, playDuration);\n setIsPlaying(true);\n startAnimationLoop();\n }, [audioBuffers.length, startAnimationLoop, stopAnimationLoop]);\n\n const pause = useCallback(() => {\n if (!playoutRef.current) return;\n\n // Calculate exact pause position using context.currentTime timing\n const elapsed = getContext().currentTime - playbackStartTimeRef.current;\n const pauseTime = audioStartPositionRef.current + elapsed;\n\n playoutRef.current.pause();\n setIsPlaying(false);\n stopAnimationLoop();\n\n // Update to the calculated pause position\n currentTimeRef.current = pauseTime;\n setCurrentTime(pauseTime);\n }, [stopAnimationLoop]);\n\n const stop = useCallback(() => {\n if (!playoutRef.current) return;\n\n playoutRef.current.stop();\n setIsPlaying(false);\n stopAnimationLoop();\n\n currentTimeRef.current = playStartPositionRef.current;\n setCurrentTime(playStartPositionRef.current);\n setActiveAnnotationId(null);\n }, [stopAnimationLoop, setActiveAnnotationId]);\n\n // Seek to a specific time - works whether playing or stopped\n const seekTo = useCallback((time: number) => {\n // Clamp time to valid range\n const clampedTime = Math.max(0, Math.min(time, duration));\n\n // Update the current time state\n currentTimeRef.current = clampedTime;\n setCurrentTime(clampedTime);\n\n // If currently playing, stop and restart at the new position\n if (isPlaying && playoutRef.current) {\n playoutRef.current.stop();\n stopAnimationLoop();\n // Use play() which handles all the timing setup\n play(clampedTime);\n }\n }, [duration, isPlaying, play, stopAnimationLoop]);\n\n // Track controls\n const setTrackMute = useCallback((trackIndex: number, muted: boolean) => {\n const newStates = [...trackStates];\n newStates[trackIndex] = { ...newStates[trackIndex], muted };\n setTrackStates(newStates);\n\n if (playoutRef.current) {\n const trackId = `track-${trackIndex}`;\n playoutRef.current.setMute(trackId, muted);\n }\n }, [trackStates]);\n\n const setTrackSolo = useCallback((trackIndex: number, soloed: boolean) => {\n const newStates = [...trackStates];\n newStates[trackIndex] = { ...newStates[trackIndex], soloed };\n setTrackStates(newStates);\n\n if (playoutRef.current) {\n const trackId = `track-${trackIndex}`;\n playoutRef.current.setSolo(trackId, soloed);\n }\n }, [trackStates]);\n\n const setTrackVolume = useCallback((trackIndex: number, volume: number) => {\n const newStates = [...trackStates];\n newStates[trackIndex] = { ...newStates[trackIndex], volume };\n setTrackStates(newStates);\n\n if (playoutRef.current) {\n const trackId = `track-${trackIndex}`;\n const track = playoutRef.current.getTrack(trackId);\n if (track) {\n track.setVolume(volume);\n }\n }\n }, [trackStates]);\n\n const setTrackPan = useCallback((trackIndex: number, pan: number) => {\n const newStates = [...trackStates];\n newStates[trackIndex] = { ...newStates[trackIndex], pan };\n setTrackStates(newStates);\n\n if (playoutRef.current) {\n const trackId = `track-${trackIndex}`;\n const track = playoutRef.current.getTrack(trackId);\n if (track) {\n track.setPan(pan);\n }\n }\n }, [trackStates]);\n\n // Selection\n const setSelection = useCallback((start: number, end: number) => {\n setSelectionStart(start);\n setSelectionEnd(end);\n currentTimeRef.current = start;\n setCurrentTime(start);\n\n if (isPlaying && playoutRef.current) {\n playoutRef.current.stop();\n playoutRef.current.play(getContext().currentTime, start);\n }\n }, [isPlaying]);\n\n // Memoize setScrollContainer callback\n const setScrollContainer = useCallback((element: HTMLDivElement | null) => {\n scrollContainerRef.current = element;\n }, []);\n\n // Stable callback ref for onAnnotationsChange to avoid re-creating controls context\n const onAnnotationsChangeRef = useRef(onAnnotationsChange);\n onAnnotationsChangeRef.current = onAnnotationsChange;\n\n const setAnnotations: React.Dispatch<React.SetStateAction<AnnotationData[]>> = useCallback(\n (action) => {\n const updated = typeof action === 'function'\n ? action(annotationsRef.current)\n : action;\n if (!onAnnotationsChangeRef.current) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n 'waveform-playlist: setAnnotations was called but no onAnnotationsChange callback is provided. ' +\n 'Annotation edits will not persist. Pass onAnnotationsChange to WaveformPlaylistProvider to handle annotation updates.'\n );\n }\n return;\n }\n onAnnotationsChangeRef.current(updated);\n },\n []\n );\n\n const sampleRate = audioBuffers[0]?.sampleRate || 44100;\n const timeScaleHeight = timescale ? 30 : 0;\n const minimumPlaylistHeight = (tracks.length * waveHeight) + timeScaleHeight;\n\n // Split context values for performance optimization\n // Animation context only re-renders consumers on discrete events\n // (play/pause/stop/seek), never during the animation loop itself\n\n const animationValue: PlaybackAnimationContextValue = useMemo(() => ({\n isPlaying,\n currentTime,\n currentTimeRef,\n playbackStartTimeRef,\n audioStartPositionRef,\n }), [isPlaying, currentTime, currentTimeRef, playbackStartTimeRef, audioStartPositionRef]);\n\n const stateValue: PlaylistStateContextValue = useMemo(() => ({\n continuousPlay,\n linkEndpoints,\n annotationsEditable,\n isAutomaticScroll,\n isLoopEnabled,\n annotations,\n activeAnnotationId,\n selectionStart,\n selectionEnd,\n selectedTrackId,\n loopStart,\n loopEnd,\n }), [continuousPlay, linkEndpoints, annotationsEditable, isAutomaticScroll, isLoopEnabled, annotations, activeAnnotationId, selectionStart, selectionEnd, selectedTrackId, loopStart, loopEnd]);\n\n const setCurrentTimeControl = useCallback((time: number) => {\n currentTimeRef.current = time;\n setCurrentTime(time);\n }, [currentTimeRef]);\n\n const setAutomaticScrollControl = useCallback((enabled: boolean) => {\n setIsAutomaticScroll(enabled);\n }, []);\n\n const controlsValue: PlaylistControlsContextValue = useMemo(() => ({\n // Playback controls\n play,\n pause,\n stop,\n seekTo,\n setCurrentTime: setCurrentTimeControl,\n\n // Track controls\n setTrackMute,\n setTrackSolo,\n setTrackVolume,\n setTrackPan,\n\n // Selection\n setSelection,\n setSelectedTrackId,\n\n // Time format\n setTimeFormat,\n formatTime,\n\n // Zoom\n zoomIn: zoom.zoomIn,\n zoomOut: zoom.zoomOut,\n\n // Master volume\n setMasterVolume,\n\n // Automatic scroll\n setAutomaticScroll: setAutomaticScrollControl,\n setScrollContainer,\n scrollContainerRef,\n\n // Annotation controls\n setContinuousPlay,\n setLinkEndpoints,\n setAnnotationsEditable,\n setAnnotations,\n setActiveAnnotationId,\n\n // Loop controls\n setLoopEnabled,\n setLoopRegion,\n setLoopRegionFromSelection,\n clearLoopRegion,\n\n }), [play, pause, stop, seekTo, setCurrentTimeControl, setTrackMute, setTrackSolo, setTrackVolume, setTrackPan, setSelection, setSelectedTrackId, setTimeFormat, formatTime, zoom.zoomIn, zoom.zoomOut, setMasterVolume, setAutomaticScrollControl, setScrollContainer, scrollContainerRef, setContinuousPlay, setLinkEndpoints, setAnnotationsEditable, setAnnotations, setActiveAnnotationId, setLoopEnabled, setLoopRegion, setLoopRegionFromSelection, clearLoopRegion]);\n\n const dataValue: PlaylistDataContextValue = useMemo(() => ({\n duration,\n audioBuffers,\n peaksDataArray,\n trackStates,\n tracks,\n sampleRate,\n waveHeight,\n timeScaleHeight,\n minimumPlaylistHeight,\n controls,\n playoutRef,\n samplesPerPixel,\n timeFormat,\n masterVolume,\n canZoomIn: zoom.canZoomIn,\n canZoomOut: zoom.canZoomOut,\n barWidth,\n barGap,\n progressBarWidth,\n isReady,\n mono,\n }), [duration, audioBuffers, peaksDataArray, trackStates, tracks, sampleRate, waveHeight, timeScaleHeight, minimumPlaylistHeight, controls, playoutRef, samplesPerPixel, timeFormat, masterVolume, zoom.canZoomIn, zoom.canZoomOut, barWidth, barGap, progressBarWidth, isReady, mono]);\n\n // Merge user theme with default theme\n const mergedTheme = { ...defaultTheme, ...userTheme };\n\n return (\n <ThemeProvider theme={mergedTheme}>\n <PlaybackAnimationContext.Provider value={animationValue}>\n <PlaylistStateContext.Provider value={stateValue}>\n <PlaylistControlsContext.Provider value={controlsValue}>\n <PlaylistDataContext.Provider value={dataValue}>\n {children}\n </PlaylistDataContext.Provider>\n </PlaylistControlsContext.Provider>\n </PlaylistStateContext.Provider>\n </PlaybackAnimationContext.Provider>\n </ThemeProvider>\n );\n};\n\n// Individual hooks for each context - use these for optimal performance\n// Components only re-render when their specific context data changes\n\nexport const usePlaybackAnimation = () => {\n const context = useContext(PlaybackAnimationContext);\n if (!context) {\n throw new Error('usePlaybackAnimation must be used within WaveformPlaylistProvider');\n }\n return context;\n};\n\nexport const usePlaylistState = () => {\n const context = useContext(PlaylistStateContext);\n if (!context) {\n throw new Error('usePlaylistState must be used within WaveformPlaylistProvider');\n }\n return context;\n};\n\nexport const usePlaylistControls = () => {\n const context = useContext(PlaylistControlsContext);\n if (!context) {\n throw new Error('usePlaylistControls must be used within WaveformPlaylistProvider');\n }\n return context;\n};\n\nexport const usePlaylistData = () => {\n const context = useContext(PlaylistDataContext);\n if (!context) {\n throw new Error('usePlaylistData must be used within WaveformPlaylistProvider');\n }\n return context;\n};\n","// src/MediaElementTrack.ts\nvar MediaElementTrack = class {\n constructor(options) {\n this._playbackRate = 1;\n this.handleEnded = () => {\n if (this.onStopCallback) {\n this.onStopCallback();\n }\n };\n this.handleTimeUpdate = () => {\n if (this.onTimeUpdateCallback) {\n this.onTimeUpdateCallback(this.audioElement.currentTime);\n }\n };\n this._peaks = options.peaks;\n this._id = options.id ?? `track-${Date.now()}`;\n this._name = options.name ?? \"Track\";\n this._playbackRate = options.playbackRate ?? 1;\n if (typeof options.source === \"string\") {\n this.audioElement = new Audio(options.source);\n this.ownsElement = true;\n } else {\n this.audioElement = options.source;\n this.ownsElement = false;\n }\n this.audioElement.preload = \"auto\";\n this.audioElement.volume = options.volume ?? 1;\n this.audioElement.playbackRate = this._playbackRate;\n const audio = this.audioElement;\n if (\"preservesPitch\" in this.audioElement) {\n audio.preservesPitch = true;\n } else if (\"mozPreservesPitch\" in this.audioElement) {\n audio.mozPreservesPitch = true;\n } else if (\"webkitPreservesPitch\" in this.audioElement) {\n audio.webkitPreservesPitch = true;\n }\n this.audioElement.addEventListener(\"ended\", this.handleEnded);\n this.audioElement.addEventListener(\"timeupdate\", this.handleTimeUpdate);\n }\n /**\n * Start playback from a specific time\n */\n play(offset = 0) {\n this.audioElement.currentTime = offset;\n this.audioElement.play().catch((err) => {\n console.warn(\"MediaElementTrack: play() failed:\", err);\n });\n }\n /**\n * Pause playback\n */\n pause() {\n this.audioElement.pause();\n }\n /**\n * Stop playback and reset to beginning\n */\n stop() {\n this.audioElement.pause();\n this.audioElement.currentTime = 0;\n }\n /**\n * Seek to a specific time\n */\n seekTo(time) {\n this.audioElement.currentTime = Math.max(0, Math.min(time, this.duration));\n }\n /**\n * Set volume (0.0 to 1.0)\n */\n setVolume(volume) {\n this.audioElement.volume = Math.max(0, Math.min(1, volume));\n }\n /**\n * Set playback rate (0.5 to 2.0, pitch preserved)\n */\n setPlaybackRate(rate) {\n const clampedRate = Math.max(0.5, Math.min(2, rate));\n this._playbackRate = clampedRate;\n this.audioElement.playbackRate = clampedRate;\n }\n /**\n * Set muted state\n */\n setMuted(muted) {\n this.audioElement.muted = muted;\n }\n /**\n * Set callback for when playback ends\n */\n setOnStopCallback(callback) {\n this.onStopCallback = callback;\n }\n /**\n * Set callback for time updates\n */\n setOnTimeUpdateCallback(callback) {\n this.onTimeUpdateCallback = callback;\n }\n /**\n * Clean up resources\n */\n dispose() {\n this.audioElement.removeEventListener(\"ended\", this.handleEnded);\n this.audioElement.removeEventListener(\"timeupdate\", this.handleTimeUpdate);\n this.audioElement.pause();\n if (this.ownsElement) {\n this.audioElement.src = \"\";\n this.audioElement.load();\n }\n }\n // Getters\n get id() {\n return this._id;\n }\n get name() {\n return this._name;\n }\n get peaks() {\n return this._peaks;\n }\n get currentTime() {\n return this.audioElement.currentTime;\n }\n get duration() {\n return this.audioElement.duration || this._peaks.duration;\n }\n get isPlaying() {\n return !this.audioElement.paused && !this.audioElement.ended;\n }\n get volume() {\n return this.audioElement.volume;\n }\n get playbackRate() {\n return this._playbackRate;\n }\n get muted() {\n return this.audioElement.muted;\n }\n /**\n * Get the underlying audio element (for advanced use cases)\n */\n get element() {\n return this.audioElement;\n }\n};\n\n// src/MediaElementPlayout.ts\nvar MediaElementPlayout = class {\n constructor(options = {}) {\n this.track = null;\n this._isPlaying = false;\n this._masterVolume = options.masterVolume ?? 1;\n this._playbackRate = options.playbackRate ?? 1;\n }\n /**\n * Initialize the playout engine.\n * For MediaElementPlayout this is a no-op (no AudioContext to start).\n */\n async init() {\n }\n /**\n * Add a track to the playout.\n * Note: Only one track is supported. Adding a second track will dispose the first.\n */\n addTrack(options) {\n if (this.track) {\n console.warn(\n \"MediaElementPlayout: Only one track is supported. Disposing previous track. For multi-track, use TonePlayout.\"\n );\n this.track.dispose();\n }\n this.track = new MediaElementTrack({\n ...options,\n volume: this._masterVolume * (options.volume ?? 1),\n playbackRate: this._playbackRate\n });\n this.track.setOnStopCallback(() => {\n this._isPlaying = false;\n if (this.onPlaybackCompleteCallback) {\n this.onPlaybackCompleteCallback();\n }\n });\n return this.track;\n }\n /**\n * Remove a track by ID.\n */\n removeTrack(trackId) {\n if (this.track && this.track.id === trackId) {\n this.track.dispose();\n this.track = null;\n }\n }\n /**\n * Get a track by ID.\n */\n getTrack(trackId) {\n if (this.track && this.track.id === trackId) {\n return this.track;\n }\n return void 0;\n }\n /**\n * Start playback.\n * @param _when - Ignored (HTMLAudioElement doesn't support scheduled start)\n * @param offset - Start position in seconds\n * @param duration - Duration to play in seconds (optional)\n */\n play(_when, offset, duration) {\n if (!this.track) {\n console.warn(\"MediaElementPlayout: No track to play\");\n return;\n }\n const startPosition = offset ?? 0;\n this._isPlaying = true;\n this.track.play(startPosition);\n if (duration !== void 0) {\n const adjustedDuration = duration / this._playbackRate;\n setTimeout(() => {\n if (this._isPlaying) {\n this.pause();\n if (this.onPlaybackCompleteCallback) {\n this.onPlaybackCompleteCallback();\n }\n }\n }, adjustedDuration * 1e3);\n }\n }\n /**\n * Pause playback.\n */\n pause() {\n if (this.track) {\n this.track.pause();\n }\n this._isPlaying = false;\n }\n /**\n * Stop playback and reset to start.\n */\n stop() {\n if (this.track) {\n this.track.stop();\n }\n this._isPlaying = false;\n }\n /**\n * Seek to a specific time.\n */\n seekTo(time) {\n if (this.track) {\n this.track.seekTo(time);\n }\n }\n /**\n * Get current playback time.\n */\n getCurrentTime() {\n if (this.track) {\n return this.track.currentTime;\n }\n return 0;\n }\n /**\n * Set master volume.\n */\n setMasterVolume(volume) {\n this._masterVolume = Math.max(0, Math.min(1, volume));\n if (this.track) {\n this.track.setVolume(this._masterVolume);\n }\n }\n /**\n * Set playback rate (0.5 to 2.0, pitch preserved).\n */\n setPlaybackRate(rate) {\n this._playbackRate = Math.max(0.5, Math.min(2, rate));\n if (this.track) {\n this.track.setPlaybackRate(this._playbackRate);\n }\n }\n /**\n * Set mute state for a track.\n */\n setMute(trackId, muted) {\n const track = this.getTrack(trackId);\n if (track) {\n track.setMuted(muted);\n }\n }\n /**\n * Set solo state for a track.\n * Note: With single track, solo is effectively the same as unmute.\n */\n setSolo(_trackId, _soloed) {\n console.warn(\"MediaElementPlayout: Solo is not applicable for single-track playback\");\n }\n /**\n * Set callback for when playback completes.\n */\n setOnPlaybackComplete(callback) {\n this.onPlaybackCompleteCallback = callback;\n }\n /**\n * Clean up resources.\n */\n dispose() {\n if (this.track) {\n this.track.dispose();\n this.track = null;\n }\n }\n // Getters\n get isPlaying() {\n return this._isPlaying;\n }\n get masterVolume() {\n return this._masterVolume;\n }\n get playbackRate() {\n return this._playbackRate;\n }\n get duration() {\n return this.track?.duration ?? 0;\n }\n get sampleRate() {\n return this.track?.peaks.sample_rate ?? 44100;\n }\n};\n\n// src/types.ts\nfunction supportsPlaybackRate(engine) {\n return \"setPlaybackRate\" in engine && typeof engine.setPlaybackRate === \"function\";\n}\nexport {\n MediaElementPlayout,\n MediaElementTrack,\n supportsPlaybackRate\n};\n//# sourceMappingURL=index.mjs.map","import React, {\n createContext,\n useContext,\n useState,\n useEffect,\n useRef,\n useCallback,\n useMemo,\n type ReactNode,\n} from 'react';\nimport { ThemeProvider } from 'styled-components';\nimport {\n MediaElementPlayout,\n} from '@waveform-playlist/media-element-playout';\nimport { type WaveformDataObject } from '@waveform-playlist/core';\nimport {\n type WaveformPlaylistTheme,\n defaultTheme,\n} from '@waveform-playlist/ui-components';\nimport type { AnnotationData } from '@waveform-playlist/core';\nimport { extractPeaksFromWaveformData } from './waveformDataLoader';\nimport type WaveformData from 'waveform-data';\nimport type { PeakData } from '@waveform-playlist/core';\nimport type { ClipPeaks, TrackClipPeaks } from './WaveformPlaylistContext';\nimport { useAnimationFrameLoop } from './hooks/useAnimationFrameLoop';\n\n// Configuration for a single media element track\nexport interface MediaElementTrackConfig {\n /** Audio source URL or Blob URL */\n source: string;\n /** Pre-computed waveform data (required for visualization) */\n waveformData: WaveformDataObject;\n /** Track name for display */\n name?: string;\n}\n\n// Context values for animation (high-frequency updates)\nexport interface MediaElementAnimationContextValue {\n isPlaying: boolean;\n currentTime: number;\n currentTimeRef: React.RefObject<number>;\n}\n\n// Context values for playlist state\nexport interface MediaElementStateContextValue {\n continuousPlay: boolean;\n annotations: AnnotationData[];\n activeAnnotationId: string | null;\n playbackRate: number;\n isAutomaticScroll: boolean;\n}\n\n// Context values for controls\nexport interface MediaElementControlsContextValue {\n play: (startTime?: number) => void;\n pause: () => void;\n stop: () => void;\n seekTo: (time: number) => void;\n setPlaybackRate: (rate: number) => void;\n setContinuousPlay: (enabled: boolean) => void;\n setAnnotations: React.Dispatch<React.SetStateAction<AnnotationData[]>>;\n setActiveAnnotationId: (id: string | null) => void;\n setAutomaticScroll: (enabled: boolean) => void;\n setScrollContainer: (element: HTMLDivElement | null) => void;\n scrollContainerRef: React.RefObject<HTMLDivElement | null>;\n}\n\n// Context values for playlist data\nexport interface MediaElementDataContextValue {\n duration: number;\n peaksDataArray: TrackClipPeaks[];\n sampleRate: number;\n waveHeight: number;\n timeScaleHeight: number;\n samplesPerPixel: number;\n playoutRef: React.RefObject<MediaElementPlayout | null>;\n controls: { show: boolean; width: number };\n barWidth: number;\n barGap: number;\n progressBarWidth: number;\n}\n\n// Create contexts\nconst MediaElementAnimationContext =\n createContext<MediaElementAnimationContextValue | null>(null);\nconst MediaElementStateContext =\n createContext<MediaElementStateContextValue | null>(null);\nconst MediaElementControlsContext =\n createContext<MediaElementControlsContextValue | null>(null);\nconst MediaElementDataContext =\n createContext<MediaElementDataContextValue | null>(null);\n\nexport interface MediaElementPlaylistProviderProps {\n /** Single track configuration with source URL and waveform data */\n track: MediaElementTrackConfig;\n /** Initial samples per pixel (zoom level) */\n samplesPerPixel?: number;\n /** Height of each waveform track */\n waveHeight?: number;\n /** Show timescale */\n timescale?: boolean;\n /** Initial playback rate (0.5 to 2.0) */\n playbackRate?: number;\n /** Enable automatic scroll to keep playhead centered */\n automaticScroll?: boolean;\n /** Theme configuration */\n theme?: Partial<WaveformPlaylistTheme>;\n /** Track controls configuration */\n controls?: { show: boolean; width: number };\n /** Annotations */\n annotationList?: {\n annotations?: AnnotationData[];\n isContinuousPlay?: boolean;\n };\n /** Width of waveform bars */\n barWidth?: number;\n /** Gap between waveform bars */\n barGap?: number;\n /** Width of progress bars */\n progressBarWidth?: number;\n /** Callback when annotations are changed (drag, edit, etc.) */\n onAnnotationsChange?: (annotations: AnnotationData[]) => void;\n /** Callback when audio is ready */\n onReady?: () => void;\n children: ReactNode;\n}\n\n/**\n * MediaElementPlaylistProvider\n *\n * A simplified playlist provider for single-track playback using HTMLAudioElement.\n * Key features:\n * - Pitch-preserving playback rate (0.5x - 2.0x)\n * - Pre-computed peaks visualization (no AudioBuffer needed)\n * - Simpler API than full WaveformPlaylistProvider\n *\n * Use this for:\n * - Language learning apps (speed control)\n * - Podcast players\n * - Single-track audio viewers\n *\n * For multi-track editing, use WaveformPlaylistProvider instead.\n */\nexport const MediaElementPlaylistProvider: React.FC<\n MediaElementPlaylistProviderProps\n> = ({\n track,\n samplesPerPixel: initialSamplesPerPixel = 1024,\n waveHeight = 100,\n timescale = false,\n playbackRate: initialPlaybackRate = 1,\n automaticScroll = false,\n theme: userTheme,\n controls = { show: false, width: 0 },\n annotationList,\n barWidth = 1,\n barGap = 0,\n progressBarWidth: progressBarWidthProp,\n onAnnotationsChange,\n onReady,\n children,\n}) => {\n const progressBarWidth = progressBarWidthProp ?? barWidth + barGap;\n\n // State\n const [isPlaying, setIsPlaying] = useState(false);\n const [currentTime, setCurrentTime] = useState(0);\n const [duration, setDuration] = useState(0);\n const [peaksDataArray, setPeaksDataArray] = useState<TrackClipPeaks[]>([]);\n const [playbackRate, setPlaybackRateState] = useState(initialPlaybackRate);\n // Annotations are derived from prop (single source of truth in parent)\n // In v6, annotations must be pre-parsed (numeric start/end). Use parseAeneas() from @waveform-playlist/annotations before passing.\n const annotations = useMemo(() => {\n if (!annotationList?.annotations) return [];\n if (process.env.NODE_ENV !== 'production' && annotationList.annotations.length > 0) {\n const first = annotationList.annotations[0] as unknown as Record<string, unknown>;\n if (typeof first.start !== 'number' || typeof first.end !== 'number') {\n console.error(\n '[waveform-playlist] Annotations must have numeric start/end values. ' +\n 'In v6, use parseAeneas() from @waveform-playlist/annotations before passing annotations. ' +\n 'Received start type: ' + typeof first.start\n );\n return [];\n }\n }\n return annotationList.annotations;\n }, [annotationList?.annotations]);\n\n // Ref for animation loop (avoids restarting loop on annotation change)\n const annotationsRef = useRef<AnnotationData[]>(annotations);\n annotationsRef.current = annotations;\n\n const [activeAnnotationId, setActiveAnnotationIdState] = useState<\n string | null\n >(null);\n const [continuousPlay, setContinuousPlayState] = useState(\n annotationList?.isContinuousPlay ?? false\n );\n const [samplesPerPixel] = useState(initialSamplesPerPixel);\n const [isAutomaticScroll, setIsAutomaticScroll] = useState(automaticScroll);\n\n // Refs\n const playoutRef = useRef<MediaElementPlayout | null>(null);\n const currentTimeRef = useRef<number>(0);\n const continuousPlayRef = useRef<boolean>(continuousPlay);\n const activeAnnotationIdRef = useRef<string | null>(null);\n const scrollContainerRef = useRef<HTMLDivElement | null>(null);\n const isAutomaticScrollRef = useRef<boolean>(automaticScroll);\n const samplesPerPixelRef = useRef<number>(initialSamplesPerPixel);\n const { startAnimationFrameLoop, stopAnimationFrameLoop } = useAnimationFrameLoop();\n\n // Sync refs\n useEffect(() => {\n continuousPlayRef.current = continuousPlay;\n }, [continuousPlay]);\n\n useEffect(() => {\n isAutomaticScrollRef.current = isAutomaticScroll;\n }, [isAutomaticScroll]);\n\n // Custom setter for activeAnnotationId\n const setActiveAnnotationId = useCallback((value: string | null) => {\n activeAnnotationIdRef.current = value;\n setActiveAnnotationIdState(value);\n }, []);\n\n const setContinuousPlay = useCallback((value: boolean) => {\n continuousPlayRef.current = value;\n setContinuousPlayState(value);\n }, []);\n\n // Memoize setScrollContainer callback\n const setScrollContainer = useCallback((element: HTMLDivElement | null) => {\n scrollContainerRef.current = element;\n }, []);\n\n // Get sample rate from waveform data\n const sampleRate = track.waveformData.sample_rate;\n\n // Initialize playout and load track\n useEffect(() => {\n const playout = new MediaElementPlayout({\n playbackRate: initialPlaybackRate,\n });\n\n playout.addTrack({\n source: track.source,\n peaks: track.waveformData,\n name: track.name,\n });\n\n // Set up time update callback\n const mediaTrack = playout.getTrack(playout['track']?.id ?? '');\n if (mediaTrack) {\n mediaTrack.setOnTimeUpdateCallback((time) => {\n currentTimeRef.current = time;\n });\n }\n\n // Set up playback complete callback\n playout.setOnPlaybackComplete(() => {\n stopAnimationFrameLoop();\n setIsPlaying(false);\n setActiveAnnotationId(null);\n currentTimeRef.current = 0;\n setCurrentTime(0);\n });\n\n playoutRef.current = playout;\n setDuration(track.waveformData.duration);\n onReady?.();\n\n return () => {\n stopAnimationFrameLoop();\n playout.dispose();\n };\n }, [track.source, track.waveformData, track.name, initialPlaybackRate, onReady, stopAnimationFrameLoop, setActiveAnnotationId]);\n\n // Generate peaks from waveform data\n useEffect(() => {\n const extractedPeaks = extractPeaksFromWaveformData(\n track.waveformData as WaveformData,\n samplesPerPixel,\n 0, // channel index\n 0, // offset\n Math.ceil(track.waveformData.duration * sampleRate) // duration in samples\n );\n\n const clipPeaks: ClipPeaks = {\n clipId: 'media-element-clip',\n trackName: track.name ?? 'Track',\n peaks: {\n length: extractedPeaks.length,\n data: [extractedPeaks.data],\n bits: extractedPeaks.bits,\n } as PeakData,\n startSample: 0,\n durationSamples: Math.ceil(track.waveformData.duration * sampleRate),\n };\n\n setPeaksDataArray([[clipPeaks]]);\n }, [track.waveformData, track.name, samplesPerPixel, sampleRate]);\n\n // Animation loop\n const startAnimationLoop = useCallback(() => {\n const updateTime = () => {\n const time = playoutRef.current?.getCurrentTime() ?? 0;\n currentTimeRef.current = time;\n\n\n // Handle annotation playback\n const currentAnnotations = annotationsRef.current;\n if (currentAnnotations.length > 0) {\n const currentAnnotation = currentAnnotations.find(\n (ann) => time >= ann.start && time < ann.end\n );\n\n if (continuousPlayRef.current) {\n if (\n currentAnnotation &&\n currentAnnotation.id !== activeAnnotationIdRef.current\n ) {\n setActiveAnnotationId(currentAnnotation.id);\n } else if (!currentAnnotation && activeAnnotationIdRef.current !== null) {\n // Clear the active annotation when we're past it, but don't stop playback\n // Let playback continue until the audio element fires its 'ended' event\n setActiveAnnotationId(null);\n }\n } else {\n if (activeAnnotationIdRef.current) {\n const activeAnnotation = currentAnnotations.find(\n (ann) => ann.id === activeAnnotationIdRef.current\n );\n if (activeAnnotation && time >= activeAnnotation.end) {\n\n playoutRef.current?.stop();\n setIsPlaying(false);\n return;\n }\n } else if (currentAnnotation) {\n setActiveAnnotationId(currentAnnotation.id);\n }\n }\n }\n\n // Handle automatic scroll - continuously center the playhead\n if (isAutomaticScrollRef.current && scrollContainerRef.current) {\n const container = scrollContainerRef.current;\n const pixelPosition = (time * sampleRate) / samplesPerPixelRef.current;\n const containerWidth = container.clientWidth;\n\n // Calculate visual position of playhead (includes controls offset)\n const controlWidth = controls.show ? controls.width : 0;\n const visualPosition = pixelPosition + controlWidth;\n\n // Continuously scroll to keep playhead centered\n const targetScrollLeft = Math.max(0, visualPosition - containerWidth / 2);\n container.scrollLeft = targetScrollLeft;\n }\n\n startAnimationFrameLoop(updateTime);\n };\n\n startAnimationFrameLoop(updateTime);\n }, [setActiveAnnotationId, sampleRate, controls, startAnimationFrameLoop]);\n\n const stopAnimationLoop = stopAnimationFrameLoop;\n\n // Playback controls\n const play = useCallback(\n (startTime?: number) => {\n if (!playoutRef.current) return;\n\n const actualStartTime = startTime ?? currentTimeRef.current;\n playoutRef.current.play(undefined, actualStartTime);\n setIsPlaying(true);\n startAnimationLoop();\n },\n [startAnimationLoop]\n );\n\n const pause = useCallback(() => {\n if (!playoutRef.current) return;\n\n playoutRef.current.pause();\n setIsPlaying(false);\n stopAnimationLoop();\n setCurrentTime(playoutRef.current.getCurrentTime());\n }, [stopAnimationLoop]);\n\n const stop = useCallback(() => {\n if (!playoutRef.current) return;\n\n playoutRef.current.stop();\n setIsPlaying(false);\n stopAnimationLoop();\n currentTimeRef.current = 0;\n setCurrentTime(0);\n setActiveAnnotationId(null);\n }, [stopAnimationLoop, setActiveAnnotationId]);\n\n const seekTo = useCallback(\n (time: number) => {\n const clampedTime = Math.max(0, Math.min(time, duration));\n currentTimeRef.current = clampedTime;\n setCurrentTime(clampedTime);\n\n if (playoutRef.current) {\n playoutRef.current.seekTo(clampedTime);\n }\n },\n [duration]\n );\n\n const setPlaybackRate = useCallback((rate: number) => {\n const clampedRate = Math.max(0.5, Math.min(2.0, rate));\n setPlaybackRateState(clampedRate);\n if (playoutRef.current) {\n playoutRef.current.setPlaybackRate(clampedRate);\n }\n }, []);\n\n const timeScaleHeight = timescale ? 30 : 0;\n\n // Context values\n const animationValue: MediaElementAnimationContextValue = useMemo(\n () => ({\n isPlaying,\n currentTime,\n currentTimeRef,\n }),\n [isPlaying, currentTime]\n );\n\n const stateValue: MediaElementStateContextValue = useMemo(\n () => ({\n continuousPlay,\n annotations,\n activeAnnotationId,\n playbackRate,\n isAutomaticScroll,\n }),\n [continuousPlay, annotations, activeAnnotationId, playbackRate, isAutomaticScroll]\n );\n\n // Stable callback ref for onAnnotationsChange to avoid re-creating controls context\n const onAnnotationsChangeRef = useRef(onAnnotationsChange);\n onAnnotationsChangeRef.current = onAnnotationsChange;\n\n const setAnnotations: React.Dispatch<React.SetStateAction<AnnotationData[]>> = useCallback(\n (action) => {\n const updated = typeof action === 'function'\n ? action(annotationsRef.current)\n : action;\n if (!onAnnotationsChangeRef.current) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n 'waveform-playlist: setAnnotations was called but no onAnnotationsChange callback is provided. ' +\n 'Annotation edits will not persist. Pass onAnnotationsChange to MediaElementPlaylistProvider to handle annotation updates.'\n );\n }\n return;\n }\n onAnnotationsChangeRef.current(updated);\n },\n []\n );\n\n const controlsValue: MediaElementControlsContextValue = useMemo(\n () => ({\n play,\n pause,\n stop,\n seekTo,\n setPlaybackRate,\n setContinuousPlay,\n setAnnotations,\n setActiveAnnotationId,\n setAutomaticScroll: (enabled: boolean) => {\n setIsAutomaticScroll(enabled);\n },\n setScrollContainer,\n scrollContainerRef,\n }),\n [play, pause, stop, seekTo, setPlaybackRate, setContinuousPlay, setAnnotations, setActiveAnnotationId, setScrollContainer]\n );\n\n const dataValue: MediaElementDataContextValue = useMemo(\n () => ({\n duration,\n peaksDataArray,\n sampleRate,\n waveHeight,\n timeScaleHeight,\n samplesPerPixel,\n playoutRef,\n controls,\n barWidth,\n barGap,\n progressBarWidth,\n }),\n [\n duration,\n peaksDataArray,\n sampleRate,\n waveHeight,\n timeScaleHeight,\n samplesPerPixel,\n controls,\n barWidth,\n barGap,\n progressBarWidth,\n ]\n );\n\n const mergedTheme = { ...defaultTheme, ...userTheme };\n\n return (\n <ThemeProvider theme={mergedTheme}>\n <MediaElementAnimationContext.Provider value={animationValue}>\n <MediaElementStateContext.Provider value={stateValue}>\n <MediaElementControlsContext.Provider value={controlsValue}>\n <MediaElementDataContext.Provider value={dataValue}>\n {children}\n </MediaElementDataContext.Provider>\n </MediaElementControlsContext.Provider>\n </MediaElementStateContext.Provider>\n </MediaElementAnimationContext.Provider>\n </ThemeProvider>\n );\n};\n\n// Hooks\nexport const useMediaElementAnimation = () => {\n const context = useContext(MediaElementAnimationContext);\n if (!context) {\n throw new Error(\n 'useMediaElementAnimation must be used within MediaElementPlaylistProvider'\n );\n }\n return context;\n};\n\nexport const useMediaElementState = () => {\n const context = useContext(MediaElementStateContext);\n if (!context) {\n throw new Error(\n 'useMediaElementState must be used within MediaElementPlaylistProvider'\n );\n }\n return context;\n};\n\nexport const useMediaElementControls = () => {\n const context = useContext(MediaElementControlsContext);\n if (!context) {\n throw new Error(\n 'useMediaElementControls must be used within MediaElementPlaylistProvider'\n );\n }\n return context;\n};\n\nexport const useMediaElementData = () => {\n const context = useContext(MediaElementDataContext);\n if (!context) {\n throw new Error(\n 'useMediaElementData must be used within MediaElementPlaylistProvider'\n );\n }\n return context;\n};\n","import React from 'react';\nimport { BaseControlButton } from '@waveform-playlist/ui-components';\nimport { usePlaybackAnimation, usePlaylistState, usePlaylistControls, usePlaylistData } from '../WaveformPlaylistContext';\n\nexport const PlayButton: React.FC<{ className?: string }> = ({ className }) => {\n const { isPlaying, currentTimeRef } = usePlaybackAnimation();\n const { selectionStart, selectionEnd, isLoopEnabled } = usePlaylistState();\n const { play } = usePlaylistControls();\n\n const handleClick = async () => {\n const hasSelection = selectionStart !== selectionEnd && selectionEnd > selectionStart;\n\n if (hasSelection) {\n if (isLoopEnabled) {\n // With loop: Start from selection start, let loop logic handle boundaries\n // Playback continues until it gets trapped in loop or reaches end\n await play(selectionStart);\n } else {\n // Without loop: Play selection region only, then stop\n const duration = selectionEnd - selectionStart;\n await play(selectionStart, duration);\n }\n } else {\n // No selection: Play from current position to the end\n await play(currentTimeRef.current ?? 0);\n }\n };\n\n return (\n <BaseControlButton onClick={handleClick} disabled={isPlaying} className={className}>\n Play\n </BaseControlButton>\n );\n};\n\nexport const PauseButton: React.FC<{ className?: string }> = ({ className }) => {\n const { isPlaying } = usePlaybackAnimation();\n const { pause } = usePlaylistControls();\n\n return (\n <BaseControlButton onClick={pause} disabled={!isPlaying} className={className}>\n Pause\n </BaseControlButton>\n );\n};\n\nexport const StopButton: React.FC<{ className?: string }> = ({ className }) => {\n const { isPlaying } = usePlaybackAnimation();\n const { stop } = usePlaylistControls();\n\n return (\n <BaseControlButton onClick={stop} disabled={!isPlaying} className={className}>\n Stop\n </BaseControlButton>\n );\n};\n\nexport const RewindButton: React.FC<{ className?: string }> = ({ className }) => {\n const { isPlaying } = usePlaybackAnimation();\n const { play, setCurrentTime } = usePlaylistControls();\n const { playoutRef } = usePlaylistData();\n\n const handleClick = () => {\n setCurrentTime(0);\n\n if (isPlaying && playoutRef.current) {\n playoutRef.current.stop();\n play(0);\n }\n };\n\n return (\n <BaseControlButton onClick={handleClick} className={className}>\n Rewind\n </BaseControlButton>\n );\n};\n\nexport const FastForwardButton: React.FC<{ className?: string }> = ({ className }) => {\n const { isPlaying } = usePlaybackAnimation();\n const { play, setCurrentTime } = usePlaylistControls();\n const { duration, playoutRef } = usePlaylistData();\n\n const handleClick = () => {\n setCurrentTime(duration);\n\n if (isPlaying && playoutRef.current) {\n playoutRef.current.stop();\n play(duration);\n }\n };\n\n return (\n <BaseControlButton onClick={handleClick} className={className}>\n Fast Forward\n </BaseControlButton>\n );\n};\n\nexport const SkipBackwardButton: React.FC<{ skipAmount?: number; className?: string }> = ({\n skipAmount = 5,\n className\n}) => {\n const { currentTimeRef, isPlaying } = usePlaybackAnimation();\n const { play, setCurrentTime } = usePlaylistControls();\n const { playoutRef } = usePlaylistData();\n\n const handleClick = () => {\n const newTime = Math.max(0, (currentTimeRef.current ?? 0) - skipAmount);\n setCurrentTime(newTime);\n\n if (isPlaying && playoutRef.current) {\n playoutRef.current.stop();\n play(newTime);\n }\n };\n\n return (\n <BaseControlButton onClick={handleClick} className={className}>\n Skip Backward\n </BaseControlButton>\n );\n};\n\nexport const SkipForwardButton: React.FC<{ skipAmount?: number; className?: string }> = ({\n skipAmount = 5,\n className\n}) => {\n const { currentTimeRef, isPlaying } = usePlaybackAnimation();\n const { play, setCurrentTime } = usePlaylistControls();\n const { duration, playoutRef } = usePlaylistData();\n\n const handleClick = () => {\n const newTime = Math.min(duration, (currentTimeRef.current ?? 0) + skipAmount);\n setCurrentTime(newTime);\n\n if (isPlaying && playoutRef.current) {\n playoutRef.current.stop();\n play(newTime);\n }\n };\n\n return (\n <BaseControlButton onClick={handleClick} className={className}>\n Skip Forward\n </BaseControlButton>\n );\n};\n\nexport const LoopButton: React.FC<{ className?: string }> = ({ className }) => {\n const { isLoopEnabled, loopStart, loopEnd } = usePlaylistState();\n const { setLoopEnabled, setLoopRegion } = usePlaylistControls();\n const { duration } = usePlaylistData();\n\n const hasValidLoopRegion = loopStart !== loopEnd && loopEnd > loopStart;\n\n const handleClick = () => {\n if (!isLoopEnabled && !hasValidLoopRegion) {\n // Create a default loop region when enabling loop without one\n // Default to first 10 seconds or 25% of duration, whichever is smaller\n const defaultEnd = Math.min(10, duration * 0.25);\n setLoopRegion(0, Math.max(1, defaultEnd)); // At least 1 second\n }\n setLoopEnabled(!isLoopEnabled);\n };\n\n return (\n <BaseControlButton\n onClick={handleClick}\n className={className}\n title={isLoopEnabled ? 'Disable loop' : 'Enable loop'}\n >\n {isLoopEnabled ? 'Loop On' : 'Loop Off'}\n </BaseControlButton>\n );\n};\n\nexport const SetLoopRegionButton: React.FC<{ className?: string }> = ({ className }) => {\n const { selectionStart, selectionEnd, loopStart, loopEnd } = usePlaylistState();\n const { setLoopRegionFromSelection, clearLoopRegion } = usePlaylistControls();\n\n const hasValidSelection = selectionStart !== selectionEnd && selectionEnd > selectionStart;\n const hasLoopRegion = loopStart !== loopEnd && loopEnd > loopStart;\n\n const handleClick = () => {\n if (hasLoopRegion) {\n clearLoopRegion();\n } else {\n setLoopRegionFromSelection();\n }\n };\n\n return (\n <BaseControlButton\n onClick={handleClick}\n disabled={!hasValidSelection && !hasLoopRegion}\n className={className}\n title={hasLoopRegion ? 'Clear loop region' : (hasValidSelection ? 'Set loop region from selection' : 'Create a selection first')}\n >\n {hasLoopRegion ? 'Clear Loop' : 'Set Loop'}\n </BaseControlButton>\n );\n};\n","import React from 'react';\nimport { BaseControlButton } from '@waveform-playlist/ui-components';\nimport { usePlaylistControls, usePlaylistData } from '../WaveformPlaylistContext';\n\nexport const ZoomInButton: React.FC<{ className?: string; disabled?: boolean }> = ({ className, disabled }) => {\n const { zoomIn } = usePlaylistControls();\n const { canZoomIn } = usePlaylistData();\n\n return (\n <BaseControlButton onClick={zoomIn} disabled={disabled || !canZoomIn} className={className}>\n Zoom In\n </BaseControlButton>\n );\n};\n\nexport const ZoomOutButton: React.FC<{ className?: string; disabled?: boolean }> = ({ className, disabled }) => {\n const { zoomOut } = usePlaylistControls();\n const { canZoomOut } = usePlaylistData();\n\n return (\n <BaseControlButton onClick={zoomOut} disabled={disabled || !canZoomOut} className={className}>\n Zoom Out\n </BaseControlButton>\n );\n};\n","import React, { useRef, useEffect } from 'react';\nimport { getContext } from 'tone';\nimport {\n MasterVolumeControl as BaseMasterVolumeControl,\n TimeFormatSelect as BaseTimeFormatSelect,\n AutomaticScrollCheckbox as BaseAutomaticScrollCheckbox,\n SelectionTimeInputs as BaseSelectionTimeInputs,\n formatTime,\n} from '@waveform-playlist/ui-components';\nimport styled from 'styled-components';\nimport { usePlaybackAnimation, usePlaylistState, usePlaylistControls, usePlaylistData } from '../WaveformPlaylistContext';\n\n/**\n * Master volume control that uses the playlist context\n */\nexport const MasterVolumeControl: React.FC<{ className?: string }> = ({ className }) => {\n const { masterVolume } = usePlaylistData();\n const { setMasterVolume } = usePlaylistControls();\n\n return (\n <BaseMasterVolumeControl\n volume={masterVolume}\n onChange={setMasterVolume}\n className={className}\n />\n );\n};\n\n/**\n * Time format selector that uses the playlist context\n */\nexport const TimeFormatSelect: React.FC<{ className?: string }> = ({ className }) => {\n const { timeFormat } = usePlaylistData();\n const { setTimeFormat } = usePlaylistControls();\n\n return (\n <BaseTimeFormatSelect\n value={timeFormat}\n onChange={setTimeFormat}\n className={className}\n />\n );\n};\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\n/**\n * Audio position display that uses the playlist context.\n * Uses requestAnimationFrame for smooth 60fps updates during playback.\n * Direct DOM manipulation avoids React re-renders.\n */\nexport const AudioPosition: React.FC<{ className?: string }> = ({ className }) => {\n const timeRef = useRef<HTMLSpanElement>(null);\n const animationFrameRef = useRef<number | null>(null);\n const { isPlaying, currentTimeRef, playbackStartTimeRef, audioStartPositionRef } = usePlaybackAnimation();\n const { timeFormat: format } = usePlaylistData();\n\n useEffect(() => {\n const updateTime = () => {\n if (timeRef.current) {\n let time: number;\n if (isPlaying) {\n const elapsed = getContext().currentTime - (playbackStartTimeRef.current ?? 0);\n time = (audioStartPositionRef.current ?? 0) + elapsed;\n } else {\n time = currentTimeRef.current ?? 0;\n }\n timeRef.current.textContent = formatTime(time, format);\n }\n\n if (isPlaying) {\n animationFrameRef.current = requestAnimationFrame(updateTime);\n }\n };\n\n if (isPlaying) {\n animationFrameRef.current = requestAnimationFrame(updateTime);\n } else {\n updateTime();\n }\n\n return () => {\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n animationFrameRef.current = null;\n }\n };\n }, [isPlaying, format, currentTimeRef, playbackStartTimeRef, audioStartPositionRef]);\n\n // Update when stopped (for seeks)\n useEffect(() => {\n if (!isPlaying && timeRef.current) {\n timeRef.current.textContent = formatTime(currentTimeRef.current ?? 0, format);\n }\n });\n\n return (\n <PositionDisplay ref={timeRef} className={className} aria-label=\"Audio position\">\n {formatTime(currentTimeRef.current ?? 0, format)}\n </PositionDisplay>\n );\n};\n\n/**\n * Selection time inputs that use the playlist context\n */\nexport const SelectionTimeInputs: React.FC<{ className?: string }> = ({ className }) => {\n const { selectionStart, selectionEnd } = usePlaylistState();\n const { setSelection } = usePlaylistControls();\n\n return (\n <BaseSelectionTimeInputs\n selectionStart={selectionStart}\n selectionEnd={selectionEnd}\n onSelectionChange={setSelection}\n className={className}\n />\n );\n};\n\n/**\n * Automatic scroll checkbox that uses the playlist context\n * Uses split contexts to avoid re-rendering during animation\n */\nexport const AutomaticScrollCheckbox: React.FC<{ className?: string }> = ({ className }) => {\n const { isAutomaticScroll } = usePlaylistState();\n const { setAutomaticScroll } = usePlaylistControls();\n\n return (\n <BaseAutomaticScrollCheckbox\n checked={isAutomaticScroll}\n onChange={setAutomaticScroll}\n className={className}\n />\n );\n};\n","import { createContext, useContext } from 'react';\nimport type {\n AnnotationData,\n AnnotationAction,\n AnnotationActionOptions,\n RenderAnnotationItemProps,\n} from '@waveform-playlist/core';\n\n/**\n * Props the browser package passes to the AnnotationText component.\n * Mirrors what PlaylistAnnotationList and MediaElementAnnotationList actually use.\n */\nexport interface AnnotationTextIntegrationProps {\n annotations: AnnotationData[];\n activeAnnotationId?: string;\n shouldScrollToActive?: boolean;\n scrollActivePosition?: ScrollLogicalPosition;\n scrollActiveContainer?: 'nearest' | 'all';\n editable?: boolean;\n controls?: AnnotationAction[];\n annotationListConfig?: AnnotationActionOptions;\n height?: number;\n onAnnotationUpdate?: (updatedAnnotations: AnnotationData[]) => void;\n renderAnnotationItem?: (props: RenderAnnotationItemProps) => React.ReactNode;\n}\n\n/**\n * Props the browser package passes to the AnnotationBox component.\n * Mirrors what PlaylistVisualization and MediaElementPlaylist actually use.\n */\nexport interface AnnotationBoxIntegrationProps {\n annotationId: string;\n annotationIndex: number;\n startPosition: number;\n endPosition: number;\n label?: string;\n color?: string;\n isActive?: boolean;\n onClick?: () => void;\n editable?: boolean;\n}\n\n/**\n * Props the browser package passes to the AnnotationBoxesWrapper component.\n * Mirrors what PlaylistVisualization and MediaElementPlaylist actually use.\n */\nexport interface AnnotationBoxesWrapperIntegrationProps {\n children?: React.ReactNode;\n height?: number;\n width?: number;\n}\n\n/**\n * Interface for annotation integration provided by @waveform-playlist/annotations.\n *\n * The browser package defines what it needs, and the optional annotations package\n * provides it via <AnnotationProvider>.\n */\nexport interface AnnotationIntegration {\n // Parser functions\n parseAeneas: (data: unknown) => AnnotationData;\n serializeAeneas: (annotation: AnnotationData) => unknown;\n\n // Visualization components (typed with the props the browser package actually passes)\n AnnotationText: React.ComponentType<AnnotationTextIntegrationProps>;\n AnnotationBox: React.ComponentType<AnnotationBoxIntegrationProps>;\n AnnotationBoxesWrapper: React.ComponentType<AnnotationBoxesWrapperIntegrationProps>;\n\n // Control components\n ContinuousPlayCheckbox: React.ComponentType<{ checked: boolean; onChange: (checked: boolean) => void; className?: string }>;\n LinkEndpointsCheckbox: React.ComponentType<{ checked: boolean; onChange: (checked: boolean) => void; className?: string }>;\n EditableCheckbox: React.ComponentType<{ checked: boolean; onChange: (checked: boolean) => void; className?: string }>;\n DownloadAnnotationsButton: React.ComponentType<{ annotations: AnnotationData[]; filename?: string; className?: string }>;\n}\n\nexport const AnnotationIntegrationContext = createContext<AnnotationIntegration | null>(null);\n\nexport const AnnotationIntegrationProvider = AnnotationIntegrationContext.Provider;\n\n/**\n * Hook to access annotation integration provided by @waveform-playlist/annotations.\n * Throws if used without <AnnotationProvider> wrapping the component tree.\n *\n * Follows the Kent C. Dodds pattern:\n * https://kentcdodds.com/blog/how-to-use-react-context-effectively\n */\nexport function useAnnotationIntegration(): AnnotationIntegration {\n const context = useContext(AnnotationIntegrationContext);\n if (!context) {\n throw new Error(\n 'useAnnotationIntegration must be used within <AnnotationProvider>. ' +\n 'Install @waveform-playlist/annotations and wrap your app with <AnnotationProvider>. ' +\n 'See: https://waveform-playlist.naomiaro.com/docs/guides/annotations'\n );\n }\n return context;\n}\n","import React from 'react';\nimport { useAnnotationIntegration } from '../AnnotationIntegrationContext';\nimport { usePlaylistState, usePlaylistControls } from '../WaveformPlaylistContext';\n\n/**\n * Continuous play checkbox that uses the playlist context.\n * Must be used within <AnnotationProvider>.\n */\nexport const ContinuousPlayCheckbox: React.FC<{ className?: string }> = ({ className }) => {\n const { ContinuousPlayCheckbox: Base } = useAnnotationIntegration();\n const { continuousPlay } = usePlaylistState();\n const { setContinuousPlay } = usePlaylistControls();\n\n return <Base checked={continuousPlay} onChange={setContinuousPlay} className={className} />;\n};\n\n/**\n * Link endpoints checkbox that uses the playlist context.\n * Must be used within <AnnotationProvider>.\n */\nexport const LinkEndpointsCheckbox: React.FC<{ className?: string }> = ({ className }) => {\n const { LinkEndpointsCheckbox: Base } = useAnnotationIntegration();\n const { linkEndpoints } = usePlaylistState();\n const { setLinkEndpoints } = usePlaylistControls();\n\n return <Base checked={linkEndpoints} onChange={setLinkEndpoints} className={className} />;\n};\n\n/**\n * Editable annotations checkbox that uses the playlist context.\n * Must be used within <AnnotationProvider>.\n */\nexport const EditableCheckbox: React.FC<{ className?: string }> = ({ className }) => {\n const { EditableCheckbox: Base } = useAnnotationIntegration();\n const { annotationsEditable } = usePlaylistState();\n const { setAnnotationsEditable } = usePlaylistControls();\n\n return <Base checked={annotationsEditable} onChange={setAnnotationsEditable} className={className} />;\n};\n\n/**\n * Download annotations button that uses the playlist context.\n * Must be used within <AnnotationProvider>.\n */\nexport const DownloadAnnotationsButton: React.FC<{ filename?: string; className?: string }> = ({\n filename,\n className,\n}) => {\n const { DownloadAnnotationsButton: Base } = useAnnotationIntegration();\n const { annotations } = usePlaylistState();\n\n return <Base annotations={annotations} filename={filename} className={className} />;\n};\n","import React from 'react';\nimport { BaseControlButton } from '@waveform-playlist/ui-components';\nimport type { EffectsFunction } from '@waveform-playlist/playout';\nimport { usePlaylistData } from '../WaveformPlaylistContext';\nimport { useExportWav, type TrackEffectsFunction } from '../hooks/useExportWav';\n\nexport interface ExportWavButtonProps {\n /** Button label */\n label?: string;\n /** Filename for the downloaded file (without extension) */\n filename?: string;\n /** Export mode: 'master' for stereo mix, 'individual' for single track */\n mode?: 'master' | 'individual';\n /** Track index for individual export */\n trackIndex?: number;\n /** Bit depth: 16 or 32 */\n bitDepth?: 16 | 32;\n /** Whether to apply effects (fades, etc.) - defaults to true */\n applyEffects?: boolean;\n /**\n * Optional Tone.js effects function for master effects. When provided, export will use Tone.Offline\n * to render through the effects chain. The function receives isOffline=true.\n */\n effectsFunction?: EffectsFunction;\n /**\n * Optional function to create offline track effects.\n * Takes a trackId and returns a TrackEffectsFunction for offline rendering.\n */\n createOfflineTrackEffects?: (trackId: string) => TrackEffectsFunction | undefined;\n /** CSS class name */\n className?: string;\n /** Callback when export completes */\n onExportComplete?: (blob: Blob) => void;\n /** Callback when export fails */\n onExportError?: (error: Error) => void;\n}\n\nexport const ExportWavButton: React.FC<ExportWavButtonProps> = ({\n label = 'Export WAV',\n filename = 'export',\n mode = 'master',\n trackIndex,\n bitDepth = 16,\n applyEffects = true,\n effectsFunction,\n createOfflineTrackEffects,\n className,\n onExportComplete,\n onExportError,\n}) => {\n const { tracks, trackStates } = usePlaylistData();\n const { exportWav, isExporting, progress } = useExportWav();\n\n const handleExport = async () => {\n try {\n const result = await exportWav(tracks, trackStates, {\n filename,\n mode,\n trackIndex,\n bitDepth,\n applyEffects,\n effectsFunction,\n createOfflineTrackEffects,\n autoDownload: true,\n });\n onExportComplete?.(result.blob);\n } catch (error) {\n onExportError?.(error instanceof Error ? error : new Error('Export failed'));\n }\n };\n\n const buttonLabel = isExporting\n ? `Exporting ${Math.round(progress * 100)}%`\n : label;\n\n return (\n <BaseControlButton\n onClick={handleExport}\n disabled={isExporting || tracks.length === 0}\n className={className}\n >\n {buttonLabel}\n </BaseControlButton>\n );\n};\n","import React, { useRef, useEffect } from 'react';\nimport styled from 'styled-components';\nimport { getContext } from 'tone';\nimport { usePlaybackAnimation, usePlaylistData } from '../WaveformPlaylistContext';\n\nconst PlayheadLine = styled.div.attrs<{ $color: string; $width: number }>((props) => ({\n style: {\n width: `${props.$width}px`,\n background: props.$color,\n },\n}))<{ $color: string; $width: number }>`\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\ninterface AnimatedPlayheadProps {\n color?: string;\n controlsOffset?: number;\n}\n\n/**\n * Animated playhead that updates position via direct DOM manipulation.\n * Calculates time directly from audio context for perfect synchronization.\n * Uses requestAnimationFrame for smooth 60fps animation without React re-renders.\n */\nexport const AnimatedPlayhead: React.FC<AnimatedPlayheadProps> = ({\n color = '#ff0000',\n controlsOffset = 0,\n}) => {\n const playheadRef = useRef<HTMLDivElement>(null);\n const animationFrameRef = useRef<number | null>(null);\n\n const { isPlaying, currentTimeRef, playbackStartTimeRef, audioStartPositionRef } = usePlaybackAnimation();\n const { samplesPerPixel, sampleRate, progressBarWidth } = usePlaylistData();\n\n useEffect(() => {\n const updatePosition = () => {\n if (playheadRef.current) {\n // Calculate time directly from audio context for perfect sync\n let time: number;\n if (isPlaying) {\n const elapsed = getContext().currentTime - (playbackStartTimeRef.current ?? 0);\n time = (audioStartPositionRef.current ?? 0) + elapsed;\n } else {\n time = currentTimeRef.current ?? 0;\n }\n const position = (time * sampleRate) / samplesPerPixel + controlsOffset;\n playheadRef.current.style.transform = `translate3d(${position}px, 0, 0)`;\n }\n\n if (isPlaying) {\n animationFrameRef.current = requestAnimationFrame(updatePosition);\n }\n };\n\n if (isPlaying) {\n // Start animation loop\n animationFrameRef.current = requestAnimationFrame(updatePosition);\n } else {\n // When stopped, update once to show final position\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]);\n\n // Also update position when not playing (for seeks, stops, etc.)\n useEffect(() => {\n if (!isPlaying && playheadRef.current) {\n const time = currentTimeRef.current ?? 0;\n const position = (time * sampleRate) / samplesPerPixel + controlsOffset;\n playheadRef.current.style.transform = `translate3d(${position}px, 0, 0)`;\n }\n });\n\n return <PlayheadLine ref={playheadRef} $color={color} $width={progressBarWidth} data-playhead />;\n};\n","import React, { useRef, useEffect } from 'react';\nimport styled from 'styled-components';\nimport { getContext } from 'tone';\nimport { SmartChannel, type SmartChannelProps, useTheme, usePlaylistInfo, type WaveformPlaylistTheme, waveformColorToCss } from '@waveform-playlist/ui-components';\nimport { usePlaybackAnimation, usePlaylistData } from '../WaveformPlaylistContext';\n\nconst ChannelWrapper = styled.div`\n position: relative;\n`;\n\ninterface BackgroundProps {\n readonly $color: string;\n readonly $height: number;\n readonly $top: number;\n readonly $width: number;\n}\n\nconst Background = styled.div.attrs<BackgroundProps>((props) => ({\n style: {\n top: `${props.$top}px`,\n width: `${props.$width}px`,\n height: `${props.$height}px`,\n background: props.$color,\n },\n}))<BackgroundProps>`\n position: absolute;\n left: 0;\n z-index: 0;\n /* Force GPU compositing layer to prevent gradient flickering during scroll */\n transform: translateZ(0);\n backface-visibility: hidden;\n will-change: transform;\n`;\n\ninterface ProgressOverlayProps {\n readonly $color: string;\n readonly $height: number;\n readonly $top: number;\n readonly $width: number;\n}\n\nconst ProgressOverlay = styled.div.attrs<ProgressOverlayProps>((props) => ({\n style: {\n top: `${props.$top}px`,\n height: `${props.$height}px`,\n width: `${props.$width}px`,\n background: props.$color,\n transform: 'scaleX(0)',\n },\n}))<ProgressOverlayProps>`\n position: absolute;\n left: 0;\n pointer-events: none;\n z-index: 1;\n transform-origin: left;\n /* scaleX changes are composite-only (GPU) — no layout reflow per frame */\n will-change: transform;\n`;\n\nconst ChannelContainer = styled.div`\n position: relative;\n z-index: 2;\n`;\n\nexport interface ChannelWithProgressProps extends SmartChannelProps {\n /** Start sample of the clip containing this channel (for progress calculation) */\n clipStartSample: number;\n /** Duration in samples of the clip */\n clipDurationSamples: number;\n}\n\n/**\n * SmartChannel wrapper that adds an animated progress overlay.\n * The progress overlay shows the \"played\" portion of the waveform.\n * Uses requestAnimationFrame for smooth 60fps animation without React re-renders.\n */\nexport const ChannelWithProgress: React.FC<ChannelWithProgressProps> = ({\n clipStartSample,\n clipDurationSamples,\n ...smartChannelProps\n}) => {\n const progressRef = useRef<HTMLDivElement>(null);\n const animationFrameRef = useRef<number | null>(null);\n const theme = useTheme() as WaveformPlaylistTheme;\n const { waveHeight } = usePlaylistInfo();\n\n const { isPlaying, currentTimeRef, playbackStartTimeRef, audioStartPositionRef } = usePlaybackAnimation();\n const { samplesPerPixel, sampleRate } = usePlaylistData();\n\n const progressColor = theme?.waveProgressColor || 'rgba(0, 0, 0, 0.1)';\n\n useEffect(() => {\n const updateProgress = () => {\n if (progressRef.current) {\n // Calculate current time from audio context\n let currentTime: number;\n if (isPlaying) {\n const elapsed = getContext().currentTime - (playbackStartTimeRef.current ?? 0);\n currentTime = (audioStartPositionRef.current ?? 0) + elapsed;\n } else {\n currentTime = currentTimeRef.current ?? 0;\n }\n\n // Convert current time to samples\n const currentSample = currentTime * sampleRate;\n\n // Calculate clip bounds in samples\n const clipEndSample = clipStartSample + clipDurationSamples;\n\n // Calculate progress ratio (0 to 1) for scaleX transform\n let ratio = 0;\n\n if (currentSample <= clipStartSample) {\n ratio = 0;\n } else if (currentSample >= clipEndSample) {\n ratio = 1;\n } else {\n const playedSamples = currentSample - clipStartSample;\n ratio = playedSamples / clipDurationSamples;\n }\n\n // scaleX is composite-only — no layout reflow, GPU-accelerated\n progressRef.current.style.transform = `scaleX(${ratio})`;\n }\n\n if (isPlaying) {\n animationFrameRef.current = requestAnimationFrame(updateProgress);\n }\n };\n\n if (isPlaying) {\n animationFrameRef.current = requestAnimationFrame(updateProgress);\n } else {\n // When stopped, update once to show final position\n updateProgress();\n }\n\n return () => {\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n animationFrameRef.current = null;\n }\n };\n }, [isPlaying, sampleRate, samplesPerPixel, clipStartSample, clipDurationSamples, smartChannelProps.length, currentTimeRef, playbackStartTimeRef, audioStartPositionRef]);\n\n // Also update when not playing (for seeks, stops, etc.)\n useEffect(() => {\n if (!isPlaying && progressRef.current) {\n const currentTime = currentTimeRef.current ?? 0;\n const currentSample = currentTime * sampleRate;\n const clipEndSample = clipStartSample + clipDurationSamples;\n\n let ratio = 0;\n if (currentSample <= clipStartSample) {\n ratio = 0;\n } else if (currentSample >= clipEndSample) {\n ratio = 1;\n } else {\n const playedSamples = currentSample - clipStartSample;\n ratio = playedSamples / clipDurationSamples;\n }\n\n progressRef.current.style.transform = `scaleX(${ratio})`;\n }\n });\n\n // Get the draw mode from theme (defaults to 'inverted')\n const drawMode = theme?.waveformDrawMode || 'inverted';\n\n let backgroundColor;\n if (drawMode === 'inverted') {\n\n backgroundColor = smartChannelProps.isSelected && theme\n ? theme.selectedWaveFillColor\n : theme?.waveFillColor || 'white';\n\n } else {\n backgroundColor = smartChannelProps.isSelected && theme\n ? theme.selectedWaveOutlineColor\n : theme?.waveOutlineColor || 'grey';\n }\n\n // Use black background for spectrogram mode\n const isSpectrogramMode = smartChannelProps.renderMode === 'spectrogram' || smartChannelProps.renderMode === 'both';\n const isBothMode = smartChannelProps.renderMode === 'both';\n const backgroundCss = isSpectrogramMode ? '#000' : waveformColorToCss(backgroundColor);\n\n // In \"both\" mode each half (spectrogram + waveform) is waveHeight/2 so the track\n // container stays the same height as a single-mode track.\n const halfHeight = Math.floor(waveHeight / 2);\n const effectiveHeight = waveHeight;\n const effectiveTop = isBothMode\n ? smartChannelProps.index * waveHeight\n : smartChannelProps.index * waveHeight;\n\n // In \"both\" mode, the waveform portion needs its own (non-black) background\n const waveformBackgroundCss = waveformColorToCss(backgroundColor);\n\n return (\n <ChannelWrapper>\n {/* Background layer - color depends on draw mode */}\n {isBothMode ? (\n <>\n {/* Spectrogram portion: black background */}\n <Background\n $color=\"#000\"\n $height={halfHeight}\n $top={effectiveTop}\n $width={smartChannelProps.length}\n />\n {/* Waveform portion: themed background */}\n <Background\n $color={waveformBackgroundCss}\n $height={halfHeight}\n $top={effectiveTop + halfHeight}\n $width={smartChannelProps.length}\n />\n </>\n ) : (\n <Background\n $color={backgroundCss}\n $height={effectiveHeight}\n $top={effectiveTop}\n $width={smartChannelProps.length}\n />\n )}\n {/* Progress overlay - shows played portion with progress color */}\n <ProgressOverlay\n ref={progressRef}\n $color={progressColor}\n $height={effectiveHeight}\n $top={effectiveTop}\n $width={smartChannelProps.length}\n />\n {/* Waveform canvas with transparent background */}\n <ChannelContainer>\n <SmartChannel {...smartChannelProps} transparentBackground />\n </ChannelContainer>\n </ChannelWrapper>\n );\n};\n","import { createContext, useContext } from 'react';\nimport type { SpectrogramData, SpectrogramConfig, ColorMapValue, RenderMode, TrackSpectrogramOverrides } from '@waveform-playlist/core';\nimport type { TrackMenuItem } from '@waveform-playlist/ui-components';\n\nexport interface SpectrogramIntegration {\n spectrogramDataMap: Map<string, SpectrogramData[]>;\n trackSpectrogramOverrides: Map<string, TrackSpectrogramOverrides>;\n spectrogramWorkerApi: SpectrogramWorkerApi | null;\n spectrogramConfig?: SpectrogramConfig;\n spectrogramColorMap?: ColorMapValue;\n setTrackRenderMode: (trackId: string, mode: RenderMode) => void;\n setTrackSpectrogramConfig: (trackId: string, config: SpectrogramConfig, colorMap?: ColorMapValue) => void;\n registerSpectrogramCanvases: (clipId: string, channelIndex: number, canvasIds: string[], canvasWidths: number[]) => void;\n unregisterSpectrogramCanvases: (clipId: string, channelIndex: number) => void;\n /** Render spectrogram menu items for a track's context menu */\n renderMenuItems?: (props: { renderMode: string; onRenderModeChange: (mode: RenderMode) => void; onOpenSettings: () => void; onClose?: () => void }) => TrackMenuItem[];\n /** Settings modal component provided by the spectrogram package */\n SettingsModal?: React.ComponentType<{\n open: boolean;\n onClose: () => void;\n config: SpectrogramConfig;\n colorMap: ColorMapValue;\n onApply: (config: SpectrogramConfig, colorMap: ColorMapValue) => void;\n }>;\n /** Get color lookup table for a color map name */\n getColorMap: (name: ColorMapValue) => Uint8Array;\n /** Get frequency scale function for a scale name */\n getFrequencyScale: (name: string) => (f: number, minF: number, maxF: number) => number;\n}\n\n/** Minimal type for the worker API surface used by browser components */\nexport interface SpectrogramWorkerApi {\n registerCanvas: (canvasId: string, canvas: OffscreenCanvas) => void;\n unregisterCanvas: (canvasId: string) => void;\n}\n\nexport const SpectrogramIntegrationContext = createContext<SpectrogramIntegration | null>(null);\n\nexport const SpectrogramIntegrationProvider = SpectrogramIntegrationContext.Provider;\n\n/**\n * Hook to access spectrogram integration provided by @waveform-playlist/spectrogram.\n * Throws if used without <SpectrogramProvider> wrapping the component tree.\n *\n * Follows the Kent C. Dodds pattern:\n * https://kentcdodds.com/blog/how-to-use-react-context-effectively\n */\nexport function useSpectrogramIntegration(): SpectrogramIntegration {\n const context = useContext(SpectrogramIntegrationContext);\n if (!context) {\n throw new Error(\n 'useSpectrogramIntegration must be used within <SpectrogramProvider>. ' +\n 'Install @waveform-playlist/spectrogram and wrap your app with <SpectrogramProvider>.'\n );\n }\n return context;\n}\n","import React, { useContext, useRef, useState, useMemo, type ReactNode, useCallback } from 'react';\nimport { createPortal } from 'react-dom';\nimport { getContext } from 'tone';\nimport {\n Playlist,\n Track as TrackComponent,\n Clip,\n Selection,\n TimescaleLoopRegion,\n PlaylistInfoContext,\n TrackControlsContext,\n DevicePixelRatioProvider,\n SmartScale,\n CloseButton,\n Controls,\n Header,\n Button,\n ButtonGroup,\n Slider,\n SliderWrapper,\n VolumeDownIcon,\n VolumeUpIcon,\n TrackMenu,\n useTheme,\n waveformColorToCss,\n type RenderPlayheadFunction,\n SpectrogramLabels,\n} from '@waveform-playlist/ui-components';\nimport { AnnotationIntegrationContext } from '../AnnotationIntegrationContext';\nimport { usePlaybackAnimation, usePlaylistState, usePlaylistControls, usePlaylistData } from '../WaveformPlaylistContext';\nimport type { Peaks } from '@waveform-playlist/core';\nimport { AnimatedPlayhead } from './AnimatedPlayhead';\nimport { ChannelWithProgress } from './ChannelWithProgress';\nimport type { SpectrogramConfig } from '@waveform-playlist/core';\nimport type { AnnotationAction } from '@waveform-playlist/core';\nimport type { AnnotationData, GetAnnotationBoxLabelFn } from '../types/annotations';\nimport { SpectrogramIntegrationContext } from '../SpectrogramIntegrationContext';\n\n// Default duration in seconds for empty tracks (used for recording workflow)\nconst DEFAULT_EMPTY_TRACK_DURATION = 60;\n\nexport interface PlaylistVisualizationProps {\n renderTrackControls?: (trackIndex: number) => ReactNode;\n renderTimestamp?: (timeMs: number, pixelPosition: number) => ReactNode;\n /** Custom playhead render function. Receives position (pixels) and color from theme. */\n renderPlayhead?: RenderPlayheadFunction;\n annotationControls?: AnnotationAction[];\n /**\n * Custom function to generate the label shown on annotation boxes in the waveform.\n * Receives the annotation data and its index, returns a string label.\n * Default: annotation.id\n */\n getAnnotationBoxLabel?: GetAnnotationBoxLabelFn;\n className?: string;\n showClipHeaders?: boolean;\n interactiveClips?: boolean;\n showFades?: boolean;\n /**\n * Enable mobile-optimized touch interactions.\n * When true, increases touch target sizes for clip boundaries.\n * Use with useDragSensors({ touchOptimized: true }) for best results.\n */\n touchOptimized?: boolean;\n /** Callback when a track's close button is clicked. Only renders close button when provided. */\n onRemoveTrack?: (trackIndex: number) => void;\n // Live recording state for real-time waveform preview\n recordingState?: {\n isRecording: boolean;\n trackId: string;\n startSample: number;\n durationSamples: number;\n peaks: Int8Array | Int16Array;\n };\n}\n\n/**\n * Standalone playlist visualization component (WebAudio version).\n *\n * Renders the waveform tracks, timescale, annotations boxes, selection,\n * playhead, loop regions, and track controls — everything that lives\n * inside <Playlist> plus wrapping providers.\n *\n * Does NOT render AnnotationText (the annotation list below the waveform).\n * Pair with PlaylistAnnotationList for a full annotation editing UI.\n */\nexport const PlaylistVisualization: React.FC<PlaylistVisualizationProps> = ({\n renderTrackControls,\n renderTimestamp,\n renderPlayhead,\n annotationControls: _annotationControls,\n getAnnotationBoxLabel,\n className,\n showClipHeaders = false,\n interactiveClips = false,\n showFades = false,\n touchOptimized = false,\n onRemoveTrack,\n recordingState,\n}) => {\n const theme = useTheme() as import('@waveform-playlist/ui-components').WaveformPlaylistTheme;\n\n const { isPlaying, currentTimeRef, playbackStartTimeRef, audioStartPositionRef } = usePlaybackAnimation();\n const {\n selectionStart,\n selectionEnd,\n annotations,\n activeAnnotationId,\n annotationsEditable,\n linkEndpoints: _linkEndpoints,\n continuousPlay,\n selectedTrackId,\n loopStart,\n loopEnd,\n isLoopEnabled,\n } = usePlaylistState();\n const annotationIntegration = useContext(AnnotationIntegrationContext);\n const {\n setAnnotations: _setAnnotations,\n setActiveAnnotationId,\n setTrackMute,\n setTrackSolo,\n setTrackVolume,\n setTrackPan,\n setSelection,\n play,\n setScrollContainer,\n setSelectedTrackId,\n setCurrentTime,\n setLoopRegion,\n } = usePlaylistControls();\n const {\n audioBuffers,\n peaksDataArray,\n trackStates,\n tracks,\n duration,\n samplesPerPixel,\n sampleRate,\n waveHeight,\n timeScaleHeight,\n controls,\n playoutRef,\n barWidth,\n barGap,\n isReady,\n } = usePlaylistData();\n\n // Optional spectrogram integration (only available when SpectrogramProvider is present)\n const spectrogram = useContext(SpectrogramIntegrationContext);\n\n // Per-track spectrogram rendering helpers (memoized) — only computed when spectrogram is available\n const perTrackSpectrogramHelpers = useMemo(() => {\n if (!spectrogram) return new Map<string, { colorLUT: Uint8Array; frequencyScaleFn: (f: number, minF: number, maxF: number) => number; config: SpectrogramConfig | undefined }>();\n const helpers = new Map<string, {\n colorLUT: Uint8Array;\n frequencyScaleFn: (f: number, minF: number, maxF: number) => number;\n config: SpectrogramConfig | undefined;\n }>();\n tracks.forEach((track) => {\n const mode = spectrogram.trackSpectrogramOverrides.get(track.id)?.renderMode ?? track.renderMode ?? 'waveform';\n if (mode === 'waveform') return;\n const overrides = spectrogram.trackSpectrogramOverrides.get(track.id);\n const cm = overrides?.colorMap ?? track.spectrogramColorMap ?? spectrogram.spectrogramColorMap ?? 'viridis';\n const cfg = overrides?.config ?? track.spectrogramConfig ?? spectrogram.spectrogramConfig;\n helpers.set(track.id, {\n colorLUT: spectrogram.getColorMap(cm),\n frequencyScaleFn: spectrogram.getFrequencyScale(cfg?.frequencyScale ?? 'mel'),\n config: cfg,\n });\n });\n return helpers;\n }, [tracks, spectrogram]);\n\n // Worker canvas API for SpectrogramChannel (stable reference)\n const workerCanvasApi = useMemo(() => {\n if (!spectrogram?.spectrogramWorkerApi) return undefined;\n return {\n registerCanvas: spectrogram.spectrogramWorkerApi.registerCanvas.bind(spectrogram.spectrogramWorkerApi),\n unregisterCanvas: spectrogram.spectrogramWorkerApi.unregisterCanvas.bind(spectrogram.spectrogramWorkerApi),\n };\n }, [spectrogram?.spectrogramWorkerApi]);\n\n // State for spectrogram settings modal\n const [settingsModalTrackId, setSettingsModalTrackId] = useState<string | null>(null);\n\n const [isSelecting, setIsSelecting] = useState(false);\n\n const scrollContainerRef = useRef<HTMLDivElement | null>(null);\n\n const handleScrollContainerRef = useCallback((element: HTMLDivElement | null) => {\n scrollContainerRef.current = element;\n setScrollContainer(element);\n }, [setScrollContainer]);\n\n // Calculate dimensions\n let displayDuration = audioBuffers.length > 0 ? duration : DEFAULT_EMPTY_TRACK_DURATION;\n\n if (recordingState?.isRecording) {\n const recordingEndSample = recordingState.startSample + recordingState.durationSamples;\n const recordingEndTime = recordingEndSample / sampleRate;\n displayDuration = Math.max(displayDuration, recordingEndTime + 10);\n }\n\n const tracksFullWidth = Math.floor((displayDuration * sampleRate) / samplesPerPixel);\n\n const handleAnnotationClick = async (annotation: AnnotationData) => {\n setActiveAnnotationId(annotation.id);\n const playDuration = !continuousPlay ? annotation.end - annotation.start : undefined;\n try {\n await play(annotation.start, playDuration);\n } catch (err) {\n console.error('waveform-playlist: Failed to start playback for annotation', annotation.id, err);\n }\n };\n\n const selectTrack = useCallback((trackIndex: number) => {\n if (trackIndex >= 0 && trackIndex < tracks.length) {\n const track = tracks[trackIndex];\n setSelectedTrackId(track.id);\n }\n }, [tracks, setSelectedTrackId]);\n\n const handleMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {\n const rect = (e.currentTarget as HTMLDivElement).getBoundingClientRect();\n const controlWidth = controls.show ? controls.width : 0;\n const x = e.clientX - rect.left - controlWidth;\n const clickTime = (x * samplesPerPixel) / sampleRate;\n\n const y = e.clientY - rect.top;\n const trackY = y;\n\n let cumulativeHeight = 0;\n let clickedTrackIndex = -1;\n\n for (let i = 0; i < peaksDataArray.length; i++) {\n const trackClipPeaks = peaksDataArray[i];\n const rawCh = trackClipPeaks.length > 0\n ? Math.max(...trackClipPeaks.map(clip => clip.peaks.data.length))\n : 1;\n const trackMode = spectrogram?.trackSpectrogramOverrides.get(tracks[i]?.id)?.renderMode ?? tracks[i]?.renderMode ?? 'waveform';\n const effectiveCh = trackMode === 'both' ? rawCh * 2 : rawCh;\n const trackHeight = effectiveCh * waveHeight + (showClipHeaders ? 22 : 0);\n\n if (trackY >= cumulativeHeight && trackY < cumulativeHeight + trackHeight) {\n clickedTrackIndex = i;\n break;\n }\n cumulativeHeight += trackHeight;\n }\n\n if (clickedTrackIndex !== -1) {\n selectTrack(clickedTrackIndex);\n }\n\n setIsSelecting(true);\n setCurrentTime(clickTime);\n setSelection(clickTime, clickTime);\n };\n\n const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {\n if (!isSelecting) return;\n\n const rect = (e.currentTarget as HTMLDivElement).getBoundingClientRect();\n const controlWidth = controls.show ? controls.width : 0;\n const x = e.clientX - rect.left - controlWidth;\n const moveTime = (x * samplesPerPixel) / sampleRate;\n\n const start = Math.min(selectionStart, moveTime);\n const end = Math.max(selectionStart, moveTime);\n setSelection(start, end);\n };\n\n const handleMouseUp = (e: React.MouseEvent<HTMLDivElement>) => {\n if (!isSelecting) return;\n\n setIsSelecting(false);\n\n const rect = (e.currentTarget as HTMLDivElement).getBoundingClientRect();\n const controlWidth = controls.show ? controls.width : 0;\n const x = e.clientX - rect.left - controlWidth;\n const endTime = (x * samplesPerPixel) / sampleRate;\n\n const start = Math.min(selectionStart, endTime);\n const end = Math.max(selectionStart, endTime);\n\n if (Math.abs(end - start) < 0.1) {\n setCurrentTime(start);\n\n if (isPlaying && playoutRef.current) {\n playoutRef.current.stop();\n play(start);\n } else if (playoutRef.current) {\n playoutRef.current.stop();\n }\n } else {\n setSelection(start, end);\n }\n };\n\n // Only show loading if we have tracks WITH clips but haven't loaded their data yet\n const hasClips = tracks.some(track => track.clips.length > 0);\n if (hasClips && (audioBuffers.length === 0 || peaksDataArray.length === 0)) {\n return <div className={className}>Loading waveform...</div>;\n }\n\n return (\n <DevicePixelRatioProvider>\n <PlaylistInfoContext.Provider\n value={{\n samplesPerPixel,\n sampleRate,\n zoomLevels: [samplesPerPixel],\n waveHeight,\n timeScaleHeight,\n duration: displayDuration * 1000,\n controls,\n barWidth,\n barGap,\n }}\n >\n <Playlist\n theme={theme}\n backgroundColor={waveformColorToCss(theme.waveOutlineColor)}\n timescaleBackgroundColor={theme.timescaleBackgroundColor}\n scrollContainerWidth={tracksFullWidth + (controls.show ? controls.width : 0)}\n timescaleWidth={tracksFullWidth}\n tracksWidth={tracksFullWidth}\n controlsWidth={controls.show ? controls.width : 0}\n onTracksMouseDown={handleMouseDown}\n onTracksMouseMove={handleMouseMove}\n onTracksMouseUp={handleMouseUp}\n scrollContainerRef={handleScrollContainerRef}\n isSelecting={isSelecting}\n data-playlist-state={isReady ? 'ready' : 'loading'}\n timescale={\n timeScaleHeight > 0 ? (\n <>\n <SmartScale renderTimestamp={renderTimestamp} />\n {isLoopEnabled && (\n <TimescaleLoopRegion\n startPosition={\n (Math.min(loopStart, loopEnd) * sampleRate) / samplesPerPixel\n }\n endPosition={\n (Math.max(loopStart, loopEnd) * sampleRate) / samplesPerPixel\n }\n markerColor={theme.loopMarkerColor}\n regionColor={theme.loopRegionColor}\n minPosition={0}\n maxPosition={tracksFullWidth}\n controlsOffset={controls.show ? controls.width : 0}\n onLoopRegionChange={(startPixels, endPixels) => {\n const startSeconds = (startPixels * samplesPerPixel) / sampleRate;\n const endSeconds = (endPixels * samplesPerPixel) / sampleRate;\n setLoopRegion(startSeconds, endSeconds);\n }}\n />\n )}\n </>\n ) : undefined\n }\n >\n <>\n {peaksDataArray.map((trackClipPeaks, trackIndex) => {\n const track = tracks[trackIndex];\n if (!track) return null;\n\n const trackState = trackStates[trackIndex] || {\n name: `Track ${trackIndex + 1}`,\n muted: false,\n soloed: false,\n volume: 1.0,\n pan: 0,\n };\n\n const effectiveRenderMode = spectrogram?.trackSpectrogramOverrides.get(track.id)?.renderMode ?? track.renderMode ?? 'waveform';\n\n const trackControls = renderTrackControls ? (\n renderTrackControls(trackIndex)\n ) : (\n <Controls onClick={() => selectTrack(trackIndex)}>\n <Header style={{ justifyContent: 'center', position: 'relative' }}>\n {onRemoveTrack && (\n <CloseButton onClick={(e) => { e.stopPropagation(); onRemoveTrack(trackIndex); }} />\n )}\n <span style={{\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n padding: '0 24px',\n display: 'block',\n }}>\n {trackState.name || `Track ${trackIndex + 1}`}\n </span>\n {spectrogram?.renderMenuItems && (\n <span style={{ position: 'absolute', right: 0, top: 0 }}>\n <TrackMenu\n items={(onClose) => spectrogram.renderMenuItems!({\n renderMode: effectiveRenderMode,\n onRenderModeChange: (mode) => spectrogram.setTrackRenderMode(track.id, mode),\n onOpenSettings: () => setSettingsModalTrackId(track.id),\n onClose,\n })}\n />\n </span>\n )}\n </Header>\n <ButtonGroup>\n <Button\n $variant={trackState.muted ? 'danger' : 'outline'}\n onClick={() => setTrackMute(trackIndex, !trackState.muted)}\n >\n Mute\n </Button>\n <Button\n $variant={trackState.soloed ? 'info' : 'outline'}\n onClick={() => setTrackSolo(trackIndex, !trackState.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={trackState.volume}\n onChange={(e) =>\n setTrackVolume(trackIndex, parseFloat(e.target.value))\n }\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={trackState.pan}\n onChange={(e) =>\n setTrackPan(trackIndex, parseFloat(e.target.value))\n }\n />\n <span>R</span>\n </SliderWrapper>\n </Controls>\n );\n\n const rawChannels = trackClipPeaks.length > 0\n ? Math.max(...trackClipPeaks.map(clip => clip.peaks.data.length))\n : 1;\n const maxChannels = rawChannels;\n\n return (\n <TrackControlsContext.Provider key={track.id} value={trackControls}>\n <TrackComponent\n numChannels={maxChannels}\n backgroundColor={waveformColorToCss(theme.waveOutlineColor)}\n offset={0}\n width={tracksFullWidth}\n hasClipHeaders={showClipHeaders}\n trackId={track.id}\n isSelected={track.id === selectedTrackId}\n >\n {effectiveRenderMode !== 'waveform' && (() => {\n const helpers = perTrackSpectrogramHelpers.get(track.id);\n const trackCfg = helpers?.config;\n if (!trackCfg?.labels || !helpers) return null;\n return (\n <SpectrogramLabels\n waveHeight={waveHeight}\n numChannels={maxChannels}\n frequencyScaleFn={helpers.frequencyScaleFn}\n minFrequency={trackCfg.minFrequency ?? 0}\n maxFrequency={trackCfg.maxFrequency ?? (sampleRate / 2)}\n labelsColor={trackCfg.labelsColor}\n labelsBackground={trackCfg.labelsBackground}\n renderMode={effectiveRenderMode as 'spectrogram' | 'both'}\n hasClipHeaders={showClipHeaders}\n />\n );\n })()}\n {trackClipPeaks.map((clip, clipIndex) => {\n const peaksData = clip.peaks;\n const width = peaksData.length;\n\n return (\n <Clip\n key={clip.clipId}\n clipId={clip.clipId}\n trackIndex={trackIndex}\n clipIndex={clipIndex}\n trackName={clip.trackName}\n startSample={clip.startSample}\n durationSamples={clip.durationSamples}\n samplesPerPixel={samplesPerPixel}\n showHeader={showClipHeaders}\n disableHeaderDrag={!interactiveClips}\n isSelected={track.id === selectedTrackId}\n trackId={track.id}\n fadeIn={clip.fadeIn}\n fadeOut={clip.fadeOut}\n sampleRate={sampleRate}\n showFades={showFades}\n touchOptimized={touchOptimized}\n onMouseDown={(e) => {\n const target = e.target as HTMLElement;\n const isDraggable = target.closest('[role=\"button\"][aria-roledescription=\"draggable\"]');\n if (isDraggable) {\n return;\n }\n selectTrack(trackIndex);\n }}\n >\n {peaksData.data.map((channelPeaks: Peaks, channelIndex: number) => {\n const clipSpectrograms = spectrogram?.spectrogramDataMap.get(clip.clipId);\n const channelSpectrogram = clipSpectrograms?.[channelIndex] ?? clipSpectrograms?.[0];\n const helpers = perTrackSpectrogramHelpers.get(track.id);\n const trackCfg = helpers?.config;\n\n return (\n <ChannelWithProgress\n key={`${clip.clipId}-${channelIndex}`}\n index={channelIndex}\n data={channelPeaks}\n bits={peaksData.bits}\n length={width}\n isSelected={track.id === selectedTrackId}\n clipStartSample={clip.startSample}\n clipDurationSamples={clip.durationSamples}\n renderMode={effectiveRenderMode}\n spectrogramData={channelSpectrogram}\n samplesPerPixel={samplesPerPixel}\n spectrogramColorLUT={helpers?.colorLUT}\n spectrogramFrequencyScaleFn={helpers?.frequencyScaleFn}\n spectrogramMinFrequency={trackCfg?.minFrequency}\n spectrogramMaxFrequency={trackCfg?.maxFrequency}\n spectrogramWorkerApi={workerCanvasApi}\n spectrogramClipId={clip.clipId}\n spectrogramOnCanvasesReady={spectrogram ? (canvasIds, canvasWidths) => {\n spectrogram.registerSpectrogramCanvases(clip.clipId, channelIndex, canvasIds, canvasWidths);\n } : undefined}\n />\n );\n })}\n </Clip>\n );\n })}\n {recordingState?.isRecording &&\n recordingState.trackId === track.id &&\n recordingState.peaks.length > 0 && (\n <Clip\n key={`${track.id}-recording`}\n clipId=\"recording-preview\"\n trackIndex={trackIndex}\n clipIndex={trackClipPeaks.length}\n trackName=\"Recording...\"\n startSample={recordingState.startSample}\n durationSamples={recordingState.durationSamples}\n samplesPerPixel={samplesPerPixel}\n showHeader={showClipHeaders}\n disableHeaderDrag={true}\n isSelected={track.id === selectedTrackId}\n trackId={track.id}\n >\n <ChannelWithProgress\n key={`${track.id}-recording-0`}\n index={0}\n data={recordingState.peaks}\n bits={16}\n length={Math.floor(recordingState.peaks.length / 2)}\n isSelected={track.id === selectedTrackId}\n clipStartSample={recordingState.startSample}\n clipDurationSamples={recordingState.durationSamples}\n />\n </Clip>\n )}\n </TrackComponent>\n </TrackControlsContext.Provider>\n );\n })}\n {annotations.length > 0 && annotationIntegration && (\n <annotationIntegration.AnnotationBoxesWrapper height={30} width={tracksFullWidth}>\n {annotations.map((annotation, index) => {\n const startPosition = (annotation.start * sampleRate) / samplesPerPixel;\n const endPosition = (annotation.end * sampleRate) / samplesPerPixel;\n const label = getAnnotationBoxLabel\n ? getAnnotationBoxLabel(annotation, index)\n : annotation.id;\n return (\n <annotationIntegration.AnnotationBox\n key={annotation.id}\n annotationId={annotation.id}\n annotationIndex={index}\n startPosition={startPosition}\n endPosition={endPosition}\n label={label}\n color=\"#ff9800\"\n isActive={annotation.id === activeAnnotationId}\n onClick={() => handleAnnotationClick(annotation)}\n editable={annotationsEditable}\n />\n );\n })}\n </annotationIntegration.AnnotationBoxesWrapper>\n )}\n {selectionStart !== selectionEnd && (\n <Selection\n startPosition={\n (Math.min(selectionStart, selectionEnd) * sampleRate) / samplesPerPixel +\n (controls.show ? controls.width : 0)\n }\n endPosition={\n (Math.max(selectionStart, selectionEnd) * sampleRate) / samplesPerPixel +\n (controls.show ? controls.width : 0)\n }\n color={theme.selectionColor}\n />\n )}\n {(isPlaying || selectionStart === selectionEnd) && (\n renderPlayhead ? (\n renderPlayhead({\n position: ((currentTimeRef.current ?? 0) * sampleRate) / samplesPerPixel +\n (controls.show ? controls.width : 0),\n color: theme.playheadColor,\n isPlaying,\n currentTimeRef,\n playbackStartTimeRef,\n audioStartPositionRef,\n samplesPerPixel,\n sampleRate,\n controlsOffset: controls.show ? controls.width : 0,\n getAudioContextTime: () => getContext().currentTime,\n })\n ) : (\n <AnimatedPlayhead\n color={theme.playheadColor}\n controlsOffset={controls.show ? controls.width : 0}\n />\n )\n )}\n </>\n </Playlist>\n </PlaylistInfoContext.Provider>\n {spectrogram?.SettingsModal && typeof document !== 'undefined' && createPortal(\n <spectrogram.SettingsModal\n open={settingsModalTrackId !== null}\n onClose={() => setSettingsModalTrackId(null)}\n config={\n settingsModalTrackId !== null\n ? (spectrogram.trackSpectrogramOverrides.get(settingsModalTrackId)?.config ?? tracks.find(t => t.id === settingsModalTrackId)?.spectrogramConfig ?? spectrogram.spectrogramConfig ?? {})\n : {}\n }\n colorMap={\n settingsModalTrackId !== null\n ? (spectrogram.trackSpectrogramOverrides.get(settingsModalTrackId)?.colorMap ?? tracks.find(t => t.id === settingsModalTrackId)?.spectrogramColorMap ?? spectrogram.spectrogramColorMap ?? 'viridis')\n : 'viridis'\n }\n onApply={(newConfig, newColorMap) => {\n if (settingsModalTrackId !== null) {\n spectrogram.setTrackSpectrogramConfig(settingsModalTrackId, newConfig, newColorMap);\n }\n }}\n />,\n document.body\n )}\n </DevicePixelRatioProvider>\n );\n};\n","import React, { useCallback } from 'react';\nimport type {\n AnnotationData,\n AnnotationAction,\n AnnotationActionOptions,\n RenderAnnotationItemProps,\n} from '@waveform-playlist/core';\nimport { useAnnotationIntegration } from '../AnnotationIntegrationContext';\nimport { usePlaylistState, usePlaylistControls } from '../WaveformPlaylistContext';\nimport type { OnAnnotationUpdateFn } from '../types/annotations';\n\nexport type { OnAnnotationUpdateFn } from '../types/annotations';\n\nexport interface PlaylistAnnotationListProps {\n /** Height in pixels for the annotation text list */\n height?: number;\n /**\n * Custom render function for annotation items in the text list.\n * When provided, completely replaces the default annotation item rendering.\n */\n renderAnnotationItem?: (props: RenderAnnotationItemProps) => React.ReactNode;\n /**\n * Callback when annotations are updated (e.g., text edited).\n * Called with the full updated annotations array.\n */\n onAnnotationUpdate?: OnAnnotationUpdateFn;\n /**\n * Action controls to show on each annotation item (e.g., delete, split).\n * Only rendered when `annotationsEditable` is true in context.\n */\n controls?: AnnotationAction[];\n /**\n * Override annotation list config. Falls back to context values\n * `{ linkEndpoints, continuousPlay }` if not provided.\n */\n annotationListConfig?: AnnotationActionOptions;\n /** Where to position the active annotation when auto-scrolling. Defaults to 'center'. */\n scrollActivePosition?: ScrollLogicalPosition;\n /** Which scrollable containers to scroll: 'nearest' or 'all'. Defaults to 'nearest'. */\n scrollActiveContainer?: 'nearest' | 'all';\n}\n\n/**\n * Standalone annotation text list component for WaveformPlaylistProvider (WebAudio).\n *\n * Requires @waveform-playlist/annotations with AnnotationProvider.\n * Throws if used without `<AnnotationProvider>` wrapping the component tree.\n */\nexport const PlaylistAnnotationList: React.FC<PlaylistAnnotationListProps> = ({\n height,\n renderAnnotationItem,\n onAnnotationUpdate,\n controls,\n annotationListConfig,\n scrollActivePosition = 'center',\n scrollActiveContainer = 'nearest',\n}) => {\n const {\n annotations,\n activeAnnotationId,\n annotationsEditable,\n linkEndpoints,\n continuousPlay,\n } = usePlaylistState();\n const integration = useAnnotationIntegration();\n const { setAnnotations } = usePlaylistControls();\n\n const resolvedConfig = annotationListConfig ?? { linkEndpoints, continuousPlay };\n\n const handleAnnotationUpdate = useCallback((updatedAnnotations: AnnotationData[]) => {\n setAnnotations(updatedAnnotations);\n onAnnotationUpdate?.(updatedAnnotations);\n }, [setAnnotations, onAnnotationUpdate]);\n\n const { AnnotationText } = integration;\n\n return (\n <AnnotationText\n annotations={annotations}\n activeAnnotationId={activeAnnotationId ?? undefined}\n shouldScrollToActive={true}\n scrollActivePosition={scrollActivePosition}\n scrollActiveContainer={scrollActiveContainer}\n editable={annotationsEditable}\n controls={annotationsEditable ? controls : undefined}\n annotationListConfig={resolvedConfig}\n height={height}\n onAnnotationUpdate={handleAnnotationUpdate}\n renderAnnotationItem={renderAnnotationItem}\n />\n );\n};\n","import React, { ReactNode } from 'react';\nimport type { RenderPlayheadFunction } from '@waveform-playlist/ui-components';\nimport type { AnnotationAction, AnnotationActionOptions, RenderAnnotationItemProps } from '@waveform-playlist/core';\nimport { usePlaylistState } from '../WaveformPlaylistContext';\nimport type { GetAnnotationBoxLabelFn } from '../types/annotations';\nimport { PlaylistVisualization } from './PlaylistVisualization';\nimport { PlaylistAnnotationList } from './PlaylistAnnotationList';\n\nexport interface WaveformProps {\n renderTrackControls?: (trackIndex: number) => ReactNode;\n renderTimestamp?: (timeMs: number, pixelPosition: number) => ReactNode;\n /** Custom playhead render function. Receives position (pixels) and color from theme. */\n renderPlayhead?: RenderPlayheadFunction;\n annotationControls?: AnnotationAction[];\n annotationListConfig?: AnnotationActionOptions;\n annotationTextHeight?: number; // Height in pixels for the annotation text list\n /**\n * Custom render function for annotation items in the text list.\n * Use this to completely customize how each annotation is displayed.\n */\n renderAnnotationItem?: (props: RenderAnnotationItemProps) => ReactNode;\n /**\n * Custom function to generate the label shown on annotation boxes in the waveform.\n * Receives the annotation data and its index, returns a string label.\n * Default: annotation.id\n */\n getAnnotationBoxLabel?: GetAnnotationBoxLabelFn;\n /** Where to position the active annotation when auto-scrolling: 'center', 'start', 'end', or 'nearest'. Defaults to 'center'. */\n scrollActivePosition?: ScrollLogicalPosition;\n /** Which scrollable containers to scroll: 'nearest' (only the annotation list) or 'all' (including viewport). Defaults to 'nearest'. */\n scrollActiveContainer?: 'nearest' | 'all';\n className?: string;\n showClipHeaders?: boolean; // Show headers on clips for visual organization\n interactiveClips?: boolean; // Enable dragging/trimming interactions on clips (requires @dnd-kit setup)\n showFades?: boolean; // Show fade in/out overlays on clips\n /**\n * Enable mobile-optimized touch interactions.\n * When true, increases touch target sizes for clip boundaries.\n * Use with useDragSensors({ touchOptimized: true }) for best results.\n */\n touchOptimized?: boolean;\n /** Callback when a track's close button is clicked. Only renders close button when provided. */\n onRemoveTrack?: (trackIndex: number) => void;\n // Live recording state for real-time waveform preview\n recordingState?: {\n isRecording: boolean;\n trackId: string; // Which track is being recorded into\n startSample: number; // Where recording started\n durationSamples: number; // Current recording length\n peaks: Int8Array | Int16Array; // Live peaks data\n };\n}\n\n/**\n * Waveform visualization component that uses the playlist context.\n *\n * Composes PlaylistVisualization (waveform + tracks) and\n * PlaylistAnnotationList (annotation text list below the waveform).\n */\nexport const Waveform: React.FC<WaveformProps> = ({\n renderTrackControls,\n renderTimestamp,\n renderPlayhead,\n annotationControls,\n annotationListConfig,\n annotationTextHeight,\n renderAnnotationItem,\n getAnnotationBoxLabel,\n scrollActivePosition = 'center',\n scrollActiveContainer = 'nearest',\n className,\n showClipHeaders = false,\n interactiveClips = false,\n showFades = false,\n touchOptimized = false,\n onRemoveTrack,\n recordingState,\n}) => {\n const { annotations } = usePlaylistState();\n\n return (\n <>\n <PlaylistVisualization\n renderTrackControls={renderTrackControls}\n renderTimestamp={renderTimestamp}\n renderPlayhead={renderPlayhead}\n annotationControls={annotationControls}\n getAnnotationBoxLabel={getAnnotationBoxLabel}\n className={className}\n showClipHeaders={showClipHeaders}\n interactiveClips={interactiveClips}\n showFades={showFades}\n touchOptimized={touchOptimized}\n onRemoveTrack={onRemoveTrack}\n recordingState={recordingState}\n />\n {annotations.length > 0 && (\n <PlaylistAnnotationList\n height={annotationTextHeight}\n renderAnnotationItem={renderAnnotationItem}\n controls={annotationControls}\n annotationListConfig={annotationListConfig}\n scrollActivePosition={scrollActivePosition}\n scrollActiveContainer={scrollActiveContainer}\n />\n )}\n </>\n );\n};\n","import React, { useRef, useEffect } from 'react';\nimport styled from 'styled-components';\nimport { useMediaElementAnimation, useMediaElementData } from '../MediaElementPlaylistContext';\n\nconst PlayheadLine = styled.div<{ $color: string; $width: number }>`\n position: absolute;\n top: 0;\n left: 0;\n width: ${(props) => props.$width}px;\n background: ${(props) => props.$color};\n height: 100%;\n z-index: 100;\n pointer-events: none;\n will-change: transform;\n`;\n\ninterface AnimatedMediaElementPlayheadProps {\n color?: string;\n controlsOffset?: number;\n}\n\n/**\n * Animated playhead for MediaElementPlaylistProvider.\n * Uses the MediaElement context for time tracking instead of Tone.js audio context.\n * Updates position via direct DOM manipulation for smooth 60fps animation.\n */\nexport const AnimatedMediaElementPlayhead: React.FC<AnimatedMediaElementPlayheadProps> = ({\n color = '#ff0000',\n controlsOffset = 0,\n}) => {\n const playheadRef = useRef<HTMLDivElement>(null);\n const animationFrameRef = useRef<number | null>(null);\n\n const { isPlaying, currentTimeRef } = useMediaElementAnimation();\n const { samplesPerPixel, sampleRate, progressBarWidth } = useMediaElementData();\n\n useEffect(() => {\n const updatePosition = () => {\n if (playheadRef.current) {\n // Get current time from the ref (updated by animation loop in context)\n const time = currentTimeRef.current ?? 0;\n const position = (time * sampleRate) / samplesPerPixel + controlsOffset;\n playheadRef.current.style.transform = `translate3d(${position}px, 0, 0)`;\n }\n\n if (isPlaying) {\n animationFrameRef.current = requestAnimationFrame(updatePosition);\n }\n };\n\n if (isPlaying) {\n // Start animation loop\n animationFrameRef.current = requestAnimationFrame(updatePosition);\n } else {\n // When stopped, update once to show final position\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]);\n\n // Also update position when not playing (for seeks, stops, etc.)\n useEffect(() => {\n if (!isPlaying && playheadRef.current) {\n const time = currentTimeRef.current ?? 0;\n const position = (time * sampleRate) / samplesPerPixel + controlsOffset;\n playheadRef.current.style.transform = `translate3d(${position}px, 0, 0)`;\n }\n });\n\n return <PlayheadLine ref={playheadRef} $color={color} $width={progressBarWidth} data-playhead />;\n};\n","import React, { useRef, useEffect } from 'react';\nimport styled from 'styled-components';\nimport { SmartChannel, type SmartChannelProps, useTheme, usePlaylistInfo, type WaveformPlaylistTheme, waveformColorToCss } from '@waveform-playlist/ui-components';\nimport { useMediaElementAnimation, useMediaElementData } from '../MediaElementPlaylistContext';\n\nconst ChannelWrapper = styled.div`\n position: relative;\n`;\n\ninterface BackgroundProps {\n readonly $color: string;\n readonly $height: number;\n readonly $top: number;\n readonly $width: number;\n}\n\nconst Background = styled.div<BackgroundProps>`\n position: absolute;\n top: ${(props) => props.$top}px;\n left: 0;\n width: ${(props) => props.$width}px;\n height: ${(props) => props.$height}px;\n background: ${(props) => props.$color};\n z-index: 0;\n transform: translateZ(0);\n backface-visibility: hidden;\n will-change: transform;\n`;\n\ninterface ProgressOverlayProps {\n readonly $color: string;\n readonly $height: number;\n readonly $top: number;\n}\n\nconst ProgressOverlay = styled.div<ProgressOverlayProps>`\n position: absolute;\n top: ${(props) => props.$top}px;\n left: 0;\n height: ${(props) => props.$height}px;\n background: ${(props) => props.$color};\n pointer-events: none;\n z-index: 1;\n will-change: width;\n`;\n\nconst ChannelContainer = styled.div`\n position: relative;\n z-index: 2;\n`;\n\nexport interface ChannelWithMediaElementProgressProps extends Omit<SmartChannelProps, 'isSelected'> {\n /** Start sample of the clip containing this channel (for progress calculation) */\n clipStartSample: number;\n /** Duration in samples of the clip */\n clipDurationSamples: number;\n}\n\n/**\n * SmartChannel wrapper for MediaElementPlaylistProvider with animated progress overlay.\n * Uses MediaElement context for time tracking instead of Tone.js audio context.\n */\nexport const ChannelWithMediaElementProgress: React.FC<ChannelWithMediaElementProgressProps> = ({\n clipStartSample,\n clipDurationSamples,\n ...smartChannelProps\n}) => {\n const progressRef = useRef<HTMLDivElement>(null);\n const animationFrameRef = useRef<number | null>(null);\n const theme = useTheme() as WaveformPlaylistTheme;\n const { waveHeight } = usePlaylistInfo();\n\n const { isPlaying, currentTimeRef } = useMediaElementAnimation();\n const { samplesPerPixel, sampleRate } = useMediaElementData();\n\n const progressColor = theme?.waveProgressColor || 'rgba(0, 0, 0, 0.1)';\n\n useEffect(() => {\n const updateProgress = () => {\n if (progressRef.current) {\n // Get current time from the ref\n const currentTime = currentTimeRef.current ?? 0;\n\n // Convert current time to samples\n const currentSample = currentTime * sampleRate;\n\n // Calculate clip bounds in samples\n const clipEndSample = clipStartSample + clipDurationSamples;\n\n // Calculate how much of this clip has been played\n let progressWidth = 0;\n\n if (currentSample <= clipStartSample) {\n // Playhead is before this clip - no progress\n progressWidth = 0;\n } else if (currentSample >= clipEndSample) {\n // Playhead is past this clip - full progress\n progressWidth = smartChannelProps.length;\n } else {\n // Playhead is within this clip - partial progress\n const playedSamples = currentSample - clipStartSample;\n progressWidth = Math.floor(playedSamples / samplesPerPixel);\n }\n\n progressRef.current.style.width = `${progressWidth}px`;\n }\n\n if (isPlaying) {\n animationFrameRef.current = requestAnimationFrame(updateProgress);\n }\n };\n\n if (isPlaying) {\n animationFrameRef.current = requestAnimationFrame(updateProgress);\n } else {\n // When stopped, update once to show final position\n updateProgress();\n }\n\n return () => {\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n animationFrameRef.current = null;\n }\n };\n }, [isPlaying, sampleRate, samplesPerPixel, clipStartSample, clipDurationSamples, smartChannelProps.length, currentTimeRef]);\n\n // Also update when not playing (for seeks, stops, etc.)\n useEffect(() => {\n if (!isPlaying && progressRef.current) {\n const currentTime = currentTimeRef.current ?? 0;\n const currentSample = currentTime * sampleRate;\n const clipEndSample = clipStartSample + clipDurationSamples;\n\n let progressWidth = 0;\n if (currentSample <= clipStartSample) {\n progressWidth = 0;\n } else if (currentSample >= clipEndSample) {\n progressWidth = smartChannelProps.length;\n } else {\n const playedSamples = currentSample - clipStartSample;\n progressWidth = Math.floor(playedSamples / samplesPerPixel);\n }\n\n progressRef.current.style.width = `${progressWidth}px`;\n }\n });\n\n // Get the draw mode from theme (defaults to 'inverted')\n const drawMode = theme?.waveformDrawMode || 'inverted';\n\n let backgroundColor;\n if (drawMode === 'inverted') {\n // For MediaElement, always treat as selected (single track)\n backgroundColor = theme?.selectedWaveFillColor || theme?.waveFillColor || 'white';\n } else {\n backgroundColor = theme?.selectedWaveOutlineColor || theme?.waveOutlineColor || 'grey';\n }\n\n const backgroundCss = waveformColorToCss(backgroundColor);\n\n return (\n <ChannelWrapper>\n <Background\n $color={backgroundCss}\n $height={waveHeight}\n $top={smartChannelProps.index * waveHeight}\n $width={smartChannelProps.length}\n />\n <ProgressOverlay\n ref={progressRef}\n $color={progressColor}\n $height={waveHeight}\n $top={smartChannelProps.index * waveHeight}\n />\n <ChannelContainer>\n <SmartChannel {...smartChannelProps} isSelected={true} transparentBackground />\n </ChannelContainer>\n </ChannelWrapper>\n );\n};\n","import React, { useContext, useRef, useState, useCallback } from 'react';\nimport { DndContext } from '@dnd-kit/core';\nimport { restrictToHorizontalAxis } from '@dnd-kit/modifiers';\nimport {\n Playlist,\n Track as TrackComponent,\n Clip,\n Selection,\n PlaylistInfoContext,\n TrackControlsContext,\n DevicePixelRatioProvider,\n SmartScale,\n useTheme,\n waveformColorToCss,\n} from '@waveform-playlist/ui-components';\nimport { AnnotationIntegrationContext } from '../AnnotationIntegrationContext';\nimport type { Peaks } from '@waveform-playlist/core';\nimport {\n useMediaElementAnimation,\n useMediaElementState,\n useMediaElementControls,\n useMediaElementData,\n} from '../MediaElementPlaylistContext';\nimport { useAnnotationDragHandlers } from '../hooks/useAnnotationDragHandlers';\nimport { AnimatedMediaElementPlayhead } from './AnimatedMediaElementPlayhead';\nimport { ChannelWithMediaElementProgress } from './ChannelWithMediaElementProgress';\nimport type { AnnotationData, GetAnnotationBoxLabelFn, OnAnnotationUpdateFn } from '../types/annotations';\n\nexport interface MediaElementPlaylistProps {\n /** Custom function to generate the label shown on annotation boxes */\n getAnnotationBoxLabel?: GetAnnotationBoxLabelFn;\n /** Whether annotation boundaries can be edited by dragging. Defaults to false. */\n editable?: boolean;\n /** Whether dragging one annotation boundary also moves the adjacent annotation's boundary. Defaults to false. */\n linkEndpoints?: boolean;\n /**\n * Callback when annotations are updated (e.g., boundaries dragged).\n * Called with the full updated annotations array.\n */\n onAnnotationUpdate?: OnAnnotationUpdateFn;\n className?: string;\n}\n\n/**\n * Standalone waveform + annotation boxes component for MediaElementPlaylistProvider.\n *\n * Renders the waveform visualization, annotation boxes, selection, and playhead.\n * Does NOT render the annotation text list — use `MediaElementAnnotationList` for that.\n *\n * Must be used inside a `MediaElementPlaylistProvider`.\n *\n * This component can be placed independently in consumer layouts, allowing the\n * waveform and annotation list to be positioned separately (e.g., in different\n * panels or with custom elements between them).\n */\nexport const MediaElementPlaylist: React.FC<MediaElementPlaylistProps> = ({\n getAnnotationBoxLabel,\n editable = false,\n linkEndpoints: linkEndpointsProp = false,\n onAnnotationUpdate,\n className,\n}) => {\n const theme = useTheme() as import('@waveform-playlist/ui-components').WaveformPlaylistTheme;\n\n // MediaElement context hooks\n const { isPlaying } = useMediaElementAnimation();\n const { annotations, activeAnnotationId } = useMediaElementState();\n const annotationIntegration = useContext(AnnotationIntegrationContext);\n const { play, seekTo, setActiveAnnotationId, setAnnotations, setScrollContainer } = useMediaElementControls();\n const {\n duration,\n peaksDataArray,\n sampleRate,\n waveHeight,\n timeScaleHeight,\n samplesPerPixel,\n controls,\n playoutRef,\n barWidth,\n barGap,\n } = useMediaElementData();\n\n const [selectionStart, setSelectionStart] = useState(0);\n const [selectionEnd, setSelectionEnd] = useState(0);\n const [isSelecting, setIsSelecting] = useState(false);\n\n // Local ref for scroll container - also register with context for auto-scroll\n const scrollContainerRef = useRef<HTMLDivElement | null>(null);\n\n // Callback to register scroll container with context\n const handleScrollContainerRef = useCallback((el: HTMLDivElement | null) => {\n scrollContainerRef.current = el;\n setScrollContainer(el);\n }, [setScrollContainer]);\n\n // Calculate dimensions\n const tracksFullWidth = Math.floor((duration * sampleRate) / samplesPerPixel);\n\n // Annotation click handler\n const handleAnnotationClick = useCallback(async (annotation: AnnotationData) => {\n setActiveAnnotationId(annotation.id);\n try {\n await play(annotation.start);\n } catch (err) {\n console.error('waveform-playlist: Failed to start playback for annotation', annotation.id, err);\n }\n }, [setActiveAnnotationId, play]);\n\n // Handle annotation boundary updates\n const handleAnnotationUpdate = useCallback((updatedAnnotations: AnnotationData[]) => {\n setAnnotations(updatedAnnotations);\n onAnnotationUpdate?.(updatedAnnotations);\n }, [setAnnotations, onAnnotationUpdate]);\n\n // Drag handlers for annotation boundary editing\n const { onDragStart, onDragMove, onDragEnd } = useAnnotationDragHandlers({\n annotations,\n onAnnotationsChange: handleAnnotationUpdate,\n samplesPerPixel,\n sampleRate,\n duration,\n linkEndpoints: linkEndpointsProp,\n });\n\n // Mouse handlers for click-to-seek\n const handleMouseDown = useCallback((e: React.MouseEvent<HTMLDivElement>) => {\n const rect = (e.currentTarget as HTMLDivElement).getBoundingClientRect();\n const controlWidth = controls.show ? controls.width : 0;\n const x = e.clientX - rect.left - controlWidth;\n const clickTime = (x * samplesPerPixel) / sampleRate;\n\n setIsSelecting(true);\n setSelectionStart(clickTime);\n setSelectionEnd(clickTime);\n }, [controls, samplesPerPixel, sampleRate]);\n\n const handleMouseMove = useCallback((e: React.MouseEvent<HTMLDivElement>) => {\n if (!isSelecting) return;\n\n const rect = (e.currentTarget as HTMLDivElement).getBoundingClientRect();\n const controlWidth = controls.show ? controls.width : 0;\n const x = e.clientX - rect.left - controlWidth;\n const moveTime = (x * samplesPerPixel) / sampleRate;\n\n setSelectionEnd(moveTime);\n }, [isSelecting, controls, samplesPerPixel, sampleRate]);\n\n const handleMouseUp = useCallback((e: React.MouseEvent<HTMLDivElement>) => {\n if (!isSelecting) return;\n\n setIsSelecting(false);\n\n const rect = (e.currentTarget as HTMLDivElement).getBoundingClientRect();\n const controlWidth = controls.show ? controls.width : 0;\n const x = e.clientX - rect.left - controlWidth;\n const endTime = (x * samplesPerPixel) / sampleRate;\n\n const start = Math.min(selectionStart, endTime);\n const end = Math.max(selectionStart, endTime);\n\n // If it's just a click (not a drag), seek to that position\n if (Math.abs(end - start) < 0.1) {\n seekTo(start);\n setSelectionStart(start);\n setSelectionEnd(start);\n\n if (isPlaying && playoutRef.current) {\n playoutRef.current.stop();\n play(start);\n }\n } else {\n // It was a drag - finalize the selection\n setSelectionStart(start);\n setSelectionEnd(end);\n }\n }, [isSelecting, selectionStart, samplesPerPixel, sampleRate, controls, seekTo, isPlaying, playoutRef, play]);\n\n // Show loading if peaks not ready\n if (peaksDataArray.length === 0) {\n return <div className={className}>Loading waveform...</div>;\n }\n\n // Empty track controls (MediaElement is single-track, no mute/solo needed)\n const emptyControls = null;\n\n return (\n <DevicePixelRatioProvider>\n <PlaylistInfoContext.Provider\n value={{\n samplesPerPixel,\n sampleRate,\n zoomLevels: [samplesPerPixel],\n waveHeight,\n timeScaleHeight,\n duration: duration * 1000,\n controls,\n barWidth,\n barGap,\n }}\n >\n <Playlist\n theme={theme}\n backgroundColor={waveformColorToCss(theme.waveOutlineColor)}\n timescaleBackgroundColor={theme.timescaleBackgroundColor}\n scrollContainerWidth={tracksFullWidth + (controls.show ? controls.width : 0)}\n timescaleWidth={tracksFullWidth}\n tracksWidth={tracksFullWidth}\n controlsWidth={controls.show ? controls.width : 0}\n onTracksMouseDown={handleMouseDown}\n onTracksMouseMove={handleMouseMove}\n onTracksMouseUp={handleMouseUp}\n scrollContainerRef={handleScrollContainerRef}\n isSelecting={isSelecting}\n timescale={\n timeScaleHeight > 0 ? (\n <SmartScale />\n ) : undefined\n }\n >\n <>\n {peaksDataArray.map((trackClipPeaks, trackIndex) => {\n // For MediaElement, we have a single track with a single clip\n const maxChannels = trackClipPeaks.length > 0\n ? Math.max(...trackClipPeaks.map(clip => clip.peaks.data.length))\n : 1;\n\n return (\n <TrackControlsContext.Provider key={trackIndex} value={emptyControls}>\n <TrackComponent\n numChannels={maxChannels}\n backgroundColor={waveformColorToCss(theme.waveOutlineColor)}\n offset={0}\n width={tracksFullWidth}\n hasClipHeaders={false}\n trackId={`media-element-track-${trackIndex}`}\n isSelected={true}\n >\n {trackClipPeaks.map((clip, clipIndex) => {\n const peaksData = clip.peaks;\n const width = peaksData.length;\n\n return (\n <Clip\n key={`${trackIndex}-${clipIndex}`}\n clipId={clip.clipId}\n trackIndex={trackIndex}\n clipIndex={clipIndex}\n trackName={clip.trackName}\n startSample={clip.startSample}\n durationSamples={clip.durationSamples}\n samplesPerPixel={samplesPerPixel}\n showHeader={false}\n disableHeaderDrag={true}\n isSelected={true}\n trackId={`media-element-track-${trackIndex}`}\n >\n {peaksData.data.map((channelPeaks: Peaks, channelIndex: number) => (\n <ChannelWithMediaElementProgress\n key={`${trackIndex}-${clipIndex}-${channelIndex}`}\n index={channelIndex}\n data={channelPeaks}\n bits={peaksData.bits}\n length={width}\n clipStartSample={clip.startSample}\n clipDurationSamples={clip.durationSamples}\n />\n ))}\n </Clip>\n );\n })}\n </TrackComponent>\n </TrackControlsContext.Provider>\n );\n })}\n {annotations.length > 0 && annotationIntegration && (\n <DndContext\n onDragStart={onDragStart}\n onDragMove={onDragMove}\n onDragEnd={onDragEnd}\n modifiers={editable ? [restrictToHorizontalAxis] : []}\n >\n <annotationIntegration.AnnotationBoxesWrapper height={30} width={tracksFullWidth}>\n {annotations.map((annotation, index) => {\n const startPosition = (annotation.start * sampleRate) / samplesPerPixel;\n const endPosition = (annotation.end * sampleRate) / samplesPerPixel;\n const label = getAnnotationBoxLabel\n ? getAnnotationBoxLabel(annotation, index)\n : annotation.id;\n return (\n <annotationIntegration.AnnotationBox\n key={annotation.id}\n annotationId={annotation.id}\n annotationIndex={index}\n startPosition={startPosition}\n endPosition={endPosition}\n label={label}\n color=\"#ff9800\"\n isActive={annotation.id === activeAnnotationId}\n onClick={() => handleAnnotationClick(annotation)}\n editable={editable}\n />\n );\n })}\n </annotationIntegration.AnnotationBoxesWrapper>\n </DndContext>\n )}\n {selectionStart !== selectionEnd && (\n <Selection\n startPosition={\n (Math.min(selectionStart, selectionEnd) * sampleRate) / samplesPerPixel +\n (controls.show ? controls.width : 0)\n }\n endPosition={\n (Math.max(selectionStart, selectionEnd) * sampleRate) / samplesPerPixel +\n (controls.show ? controls.width : 0)\n }\n color={theme.selectionColor}\n />\n )}\n <AnimatedMediaElementPlayhead\n color={theme.playheadColor}\n controlsOffset={controls.show ? controls.width : 0}\n />\n </>\n </Playlist>\n </PlaylistInfoContext.Provider>\n </DevicePixelRatioProvider>\n );\n};\n","import React, { useCallback } from 'react';\nimport type {\n AnnotationData,\n AnnotationAction,\n AnnotationActionOptions,\n RenderAnnotationItemProps,\n} from '@waveform-playlist/core';\nimport { useAnnotationIntegration } from '../AnnotationIntegrationContext';\nimport { useMediaElementState, useMediaElementControls } from '../MediaElementPlaylistContext';\nimport type { OnAnnotationUpdateFn } from '../types/annotations';\n\nexport type { OnAnnotationUpdateFn } from '../types/annotations';\n\nexport interface MediaElementAnnotationListProps {\n /** Height in pixels for the annotation text list */\n height?: number;\n /**\n * Custom render function for annotation items in the text list.\n * When provided, completely replaces the default annotation item rendering.\n */\n renderAnnotationItem?: (props: RenderAnnotationItemProps) => React.ReactNode;\n /**\n * Callback when annotations are updated (e.g., text edited).\n * Called with the full updated annotations array.\n */\n onAnnotationUpdate?: OnAnnotationUpdateFn;\n /** Whether annotation text can be edited. Defaults to false. */\n editable?: boolean;\n /**\n * Action controls to show on each annotation item (e.g., delete, split).\n * Only rendered when `editable` is true.\n */\n controls?: AnnotationAction[];\n /**\n * Override annotation list config. Falls back to context values\n * `{ linkEndpoints: false, continuousPlay }` if not provided.\n */\n annotationListConfig?: AnnotationActionOptions;\n /** Where to position the active annotation when auto-scrolling. Defaults to 'center'. */\n scrollActivePosition?: ScrollLogicalPosition;\n /** Which scrollable containers to scroll: 'nearest' or 'all'. Defaults to 'nearest'. */\n scrollActiveContainer?: 'nearest' | 'all';\n}\n\n/**\n * Standalone annotation text list component for MediaElementPlaylistProvider.\n *\n * Requires @waveform-playlist/annotations with AnnotationProvider.\n * Throws if used without `<AnnotationProvider>` wrapping the component tree.\n */\nexport const MediaElementAnnotationList: React.FC<MediaElementAnnotationListProps> = ({\n height,\n renderAnnotationItem,\n onAnnotationUpdate,\n editable = false,\n controls,\n annotationListConfig,\n scrollActivePosition = 'center',\n scrollActiveContainer = 'nearest',\n}) => {\n const { annotations, activeAnnotationId, continuousPlay } = useMediaElementState();\n const integration = useAnnotationIntegration();\n const { setAnnotations } = useMediaElementControls();\n\n const resolvedConfig = annotationListConfig ?? { linkEndpoints: false, continuousPlay };\n\n const handleAnnotationUpdate = useCallback((updatedAnnotations: AnnotationData[]) => {\n setAnnotations(updatedAnnotations);\n onAnnotationUpdate?.(updatedAnnotations);\n }, [setAnnotations, onAnnotationUpdate]);\n\n const { AnnotationText } = integration;\n\n return (\n <AnnotationText\n annotations={annotations}\n activeAnnotationId={activeAnnotationId ?? undefined}\n shouldScrollToActive={true}\n scrollActivePosition={scrollActivePosition}\n scrollActiveContainer={scrollActiveContainer}\n editable={editable}\n controls={editable ? controls : undefined}\n annotationListConfig={resolvedConfig}\n height={height}\n onAnnotationUpdate={handleAnnotationUpdate}\n renderAnnotationItem={renderAnnotationItem}\n />\n );\n};\n","import React from 'react';\nimport type { RenderAnnotationItemProps } from '@waveform-playlist/core';\nimport { useMediaElementState } from '../MediaElementPlaylistContext';\nimport type { GetAnnotationBoxLabelFn, OnAnnotationUpdateFn } from '../types/annotations';\nimport { MediaElementPlaylist } from './MediaElementPlaylist';\nimport { MediaElementAnnotationList } from './MediaElementAnnotationList';\n\n// Re-export annotation types for convenience\nexport type { GetAnnotationBoxLabelFn, OnAnnotationUpdateFn } from '../types/annotations';\n\nexport interface MediaElementWaveformProps {\n /** Height in pixels for the annotation text list */\n annotationTextHeight?: number;\n /** Custom function to generate the label shown on annotation boxes */\n getAnnotationBoxLabel?: GetAnnotationBoxLabelFn;\n /**\n * Custom render function for annotation items in the text list.\n * When provided, completely replaces the default annotation item rendering.\n * Use this to customize the appearance of each annotation (e.g., add furigana).\n */\n renderAnnotationItem?: (props: RenderAnnotationItemProps) => React.ReactNode;\n /** Whether annotation boundaries can be edited by dragging. Defaults to false. */\n editable?: boolean;\n /** Whether dragging one annotation boundary also moves the adjacent annotation's boundary. Defaults to false. */\n linkEndpoints?: boolean;\n /**\n * Callback when annotations are updated (e.g., boundaries dragged).\n * Called with the full updated annotations array.\n */\n onAnnotationUpdate?: OnAnnotationUpdateFn;\n /** Where to position the active annotation when auto-scrolling: 'center', 'start', 'end', or 'nearest'. Defaults to 'center'. */\n scrollActivePosition?: ScrollLogicalPosition;\n /** Which scrollable containers to scroll: 'nearest' (only the annotation list) or 'all' (including viewport). Defaults to 'nearest'. */\n scrollActiveContainer?: 'nearest' | 'all';\n className?: string;\n}\n\n/**\n * Simplified Waveform component for MediaElementPlaylistProvider\n *\n * This is a stripped-down version of Waveform that works with the\n * MediaElement context. It supports:\n * - Single track visualization\n * - Click to seek\n * - Annotation display and click-to-play\n * - Playhead animation\n *\n * For multi-track editing, use the full Waveform with WaveformPlaylistProvider.\n */\nexport const MediaElementWaveform: React.FC<MediaElementWaveformProps> = ({\n annotationTextHeight,\n getAnnotationBoxLabel,\n renderAnnotationItem,\n editable = false,\n linkEndpoints = false,\n onAnnotationUpdate,\n scrollActivePosition = 'center',\n scrollActiveContainer = 'nearest',\n className,\n}) => {\n const { annotations } = useMediaElementState();\n\n return (\n <>\n <MediaElementPlaylist\n getAnnotationBoxLabel={getAnnotationBoxLabel}\n editable={editable}\n linkEndpoints={linkEndpoints}\n onAnnotationUpdate={onAnnotationUpdate}\n className={className}\n />\n {annotations.length > 0 && (\n <MediaElementAnnotationList\n height={annotationTextHeight}\n renderAnnotationItem={renderAnnotationItem}\n onAnnotationUpdate={onAnnotationUpdate}\n editable={editable}\n annotationListConfig={{ linkEndpoints, continuousPlay: false }}\n scrollActivePosition={scrollActivePosition}\n scrollActiveContainer={scrollActiveContainer}\n />\n )}\n </>\n );\n};\n"],"names":["hasWarned","getUnderlyingAudioParam","signal","param","linearCurve","length","fadeIn","curve","scale","i","x","exponentialCurve","index","sCurveCurve","phase","logarithmicCurve","base","generateCurve","type","applyFadeIn","startTime","duration","startValue","endValue","scaledCurve","range","applyFadeOut","ToneTrack","options","Volume","Panner","Gain","destination","getDestination","cleanup","clipInfos","clipInfo","player","Player","fadeGain","clipPlayer","clipStartTime","clipOffset","audioParam","skipTime","fadeInDuration","remainingFadeDuration","fadeProgress","fadeOutStartInClip","absoluteFadeOutStart","elapsedFadeOut","gain","pan","muted","value","soloed","when","offset","newPlayer","playbackPosition","clipStart","clipEnd","currentTime","now","remainingDuration","clipDuration","delay","elapsed","stopWhen","lastClip","callback","TonePlayout","Volume2","getDestination2","track","start","trackOptions","optionsWithDestination","toneTrack","trackId","now2","currentSessionId","trackStartTime","bufferOffset","getTransport","hasSoloedTracks","id","manuallyMuted","time","getContext","CSS","transform","y","scaleX","scaleY","_ref","property","easing","a","e","o","r","p","s","t","c","m","d","l","f","g","w","h","n","styled","props","BaseButton","styled2","BaseCheckboxWrapper","styled3","BaseCheckbox","BaseCheckboxLabel","BaseControlButton","styled4","BaseInput","styled5","BaseLabel","styled6","ScreenReaderOnly","BaseSelect","styled7","BaseSlider","styled8","AutomaticScrollCheckbox","checked","onChange","disabled","className","handleChange","jsxs","jsx2","isWaveformGradient","color","waveformColorToCss","direction","stops","stop","defaultTheme","ViewportStore","scrollLeft","containerWidth","buffer","visibleStart","visibleEnd","listener","ViewportStoreContext","createContext","EMPTY_SUBSCRIBE","ScrollViewportProvider","containerRef","children","storeRef","useRef","store","rafIdRef","measure","useCallback","el","scheduleUpdate","useEffect","resizeObserver","jsx3","useScrollViewportSelector","selector","useContext","useSyncExternalStore","MAX_CANVAS_WIDTH","createCanvasFillStyle","ctx","width","height","gradient","Waveform","styled9","Wrapper","Channel","data","bits","devicePixelRatio","waveHeight","waveOutlineColor","waveFillColor","barWidth","barGap","transparentBackground","drawMode","canvasesRef","useRef2","visibleChunkKey","viewport","totalChunks","indices","chunkLeft","chunkWidth","visibleChunkIndices","canvasRef","useCallback2","canvas","index2","useEffect2","canvases","useLayoutEffect","step","globalPixelOffset","h2","maxValue","canvasWidth","fillColor","canvasStartGlobal","canvasEndGlobal","firstBarGlobal","barGlobal","peakIndex","minPeak","maxPeak","min","max","waveforms","currentWidth","jsx4","backgroundCss","errorContainerStyle","React3","error","errorInfo","jsx5","CLIP_HEADER_HEIGHT","HeaderContainer","styled10","TrackName","ClipHeaderPresentational","trackName","isSelected","jsx6","ClipHeader","clipId","_trackIndex","_clipIndex","disableDrag","dragHandleProps","attributes","listeners","setActivatorNodeRef","CLIP_BOUNDARY_WIDTH","CLIP_BOUNDARY_WIDTH_TOUCH","BoundaryContainer","styled11","ClipBoundary","edge","touchOptimized","isHovered","setIsHovered","React4","isDragging","jsx7","FadeContainer","styled12","FadeSvg","generateFadePath","curveType","points","numPoints","progress","curvedProgress","FadeOverlay","left","theme","useTheme","jsx8","ClipContainer","styled13","ChannelsWrapper","Clip","trackIndex","clipIndex","startSample","durationSamples","samplesPerPixel","showHeader","disableHeaderDrag","isOverlay","onMouseDown","fadeOut","sampleRate","showFades","enableDrag","draggableId","setNodeRef","useDraggable","leftBoundaryId","leftBoundaryAttributes","leftBoundaryListeners","setLeftBoundaryActivatorRef","isLeftBoundaryDragging","rightBoundaryId","rightBoundaryAttributes","rightBoundaryListeners","setRightBoundaryActivatorRef","isRightBoundaryDragging","style","jsxs2","jsx9","Fragment","VolumeContainer","styled14","VolumeLabel","VolumeSlider","MasterVolumeControl","volume","jsxs3","jsx10","styled15","Wrapper2","styled16","ScrollContainer","TimescaleWrapper","TracksContainer","ClickOverlay","Playlist","backgroundColor","timescaleBackgroundColor","timescale","timescaleWidth","tracksWidth","scrollContainerWidth","controlsWidth","onTracksClick","onTracksMouseDown","onTracksMouseMove","onTracksMouseUp","scrollContainerRef","isSelecting","playlistState","wrapperRef","useRef4","handleRef","useCallback3","jsx12","jsxs5","withTheme","SelectionOverlay","styled17","Selection","startPosition","endPosition","jsx13","styled18","DraggableMarkerHandle","TimescaleLoopShade","LoopRegionMarkers","markerColor","regionColor","onLoopStartChange","onLoopEndChange","onLoopRegionMove","minPosition","maxPosition","draggingMarker","setDraggingMarker","useState","dragStartX","useRef5","dragStartPosition","dragStartEnd","handleMarkerMouseDown","useCallback4","marker","handleMouseMove","moveEvent","delta","newPosition","clampedPosition","handleMouseUp","handleRegionMouseDown","regionWidth","newStart","newEnd","jsxs6","Fragment2","jsx14","TimescaleLoopCreator","TimescaleLoopRegion","onLoopRegionChange","controlsOffset","setIsCreating","createStartX","hasLoopRegion","handleBackgroundMouseDown","target","rect","clickX","clampedX","currentX","clampedCurrentX","clockFormat","seconds","decimals","hours","minutes","secs","formatTime","format","parseTime","timeStr","parts","TimeInput","label","readOnly","displayValue","setDisplayValue","useState2","useEffect4","formatted","newDisplayValue","handleBlur","parsedValue","handleKeyDown","jsxs7","Fragment3","jsx15","SelectionTimeInputs","selectionStart","selectionEnd","onSelectionChange","timeFormat","setTimeFormat","useState3","useEffect5","timeFormatSelect","handleFormatChange","handleStartChange","handleEndChange","jsxs8","jsx16","getScale","DevicePixelRatioContext","createContext2","DevicePixelRatioProvider","setScale","useState4","jsx17","useDevicePixelRatio","useContext2","PlaylistInfoContext","createContext3","usePlaylistInfo","useContext3","useTheme2","useContext4","ThemeContext","TrackControlsContext","createContext4","jsx18","Fragment4","useTrackControls","useContext5","defaultProgress","defaultIsPlaying","defaultSelectionStart","defaultSelectionEnd","defaultPlayout","createContext5","LINEAR_FREQUENCY_SCALE","minF","maxF","Wrapper3","styled19","SpectrogramCanvas","defaultGetColorMap","lut","DEFAULT_COLOR_LUT","SpectrogramChannel","channelIndexProp","colorLUT","frequencyScaleFn","minFrequency","maxFrequency","workerApi","onCanvasesReady","channelIndex","useRef6","registeredIdsRef","transferredCanvasesRef","workerApiRef","onCanvasesReadyRef","isWorkerMode","useCallback5","idx","scaleFn","hasCustomFrequencyScale","useEffect6","currentWorkerApi","canvases2","newIds","newWidths","canvasIdx","canvasId","offscreen","err","remaining","match","chunkIdx","api","useLayoutEffect2","frequencyBinCount","frameCount","hopSize","gainDb","rawRangeDb","rangeDb","binToFreq","bin","canvasHeight","imgData","pixels","samplePos","frame","frameOffset","normalizedY","lo","hi","mid","freq","db","normalized","colorIdx","pixelIdx","tmpCanvas","tmpCtx","jsx20","SmartChannel","renderMode","spectrogramData","spectrogramColorLUT","sppProp","spectrogramFrequencyScaleFn","spectrogramMinFrequency","spectrogramMaxFrequency","spectrogramWorkerApi","spectrogramClipId","spectrogramOnCanvasesReady","contextSpp","hasSpectrogram","jsx21","halfHeight","jsxs9","Fragment5","LABELS_WIDTH","LabelsStickyWrapper","styled20","getFrequencyLabels","inRange","maxLabels","result","SpectrogramLabels","numChannels","labelsColor","labelsBackground","hasClipHeaders","useRef7","spectrogramHeight","totalHeight","clipHeaderOffset","useLayoutEffect3","labelFreqs","ch","channelTop","text","metrics","padding","jsx22","secondsToPixels","formatTime2","milliseconds","PlaylistTimeScaleScroll","styled21","TimeTickChunk","TimeStamp","TimeScale","timeColor","bigStep","secondStep","renderTimestamp","canvasRefsMap","useRef8","timeScaleHeight","showControls","controlWidth","useContext7","canvasRefCallback","useCallback6","widthX","canvasInfo","timeMarkersWithPositions","useMemo","nextCanvasInfo","nextMarkers","nextWidthX","pixPerSec","counter","pix","timeMs","timestamp","element","jsx23","React15","visibleChunks","firstChunkLeft","lastChunkRight","visibleMarkers","useEffect7","currentMap","useLayoutEffect4","pixLeft","scaleHeight","localX","jsxs10","StyledTimeScale","withTheme2","timeinfo","getScaleInfo","keys","config","resolution","SmartScale","useContext8","jsx24","SelectWrapper","styled22","TIME_FORMAT_OPTIONS","TimeFormatSelect","jsx25","option","Container","styled23","ChannelContainer","ControlsWrapper","Track","onClick","show","controls","jsxs11","jsx26","Button","styled24","ButtonGroup","styled25","StyledCloseButton","styled26","CloseButton","title","jsx27","XIcon","Controls","styled27","Header","styled28","VolumeDownIcon","jsx28","SpeakerLowIcon","VolumeUpIcon","jsx29","SpeakerHighIcon","DotsIcon","jsx31","DotsThreeIcon","Slider","styled29","SliderWrapper","styled30","MenuContainer","styled31","MenuButton","Dropdown","Divider","TrackMenu","itemsProp","open","setOpen","useState6","items","dropdownPos","setDropdownPos","buttonRef","useRef9","dropdownRef","useEffect8","handleClick","jsxs12","jsx32","prev","createPortal","item","React17","WaveformDataChannel","waveformData","sample","values","INT8_MAX","INT8_MIN","INT16_MAX","INT16_MIN","calculateWaveformDataLength","audio_sample_count","data_length","samples_remaining","generateWaveformData","amplitude_scale","split_channels","sample_rate","channels","channel","output_channels","header_size","bytes_per_sample","total_size","data_view","scale_counter","min_value","max_value","range_min","range_max","_channel","_channel2","_channel3","_channel4","_typeof","isJsonWaveformData","isBinaryWaveformData","isCompatible","view","version","convertJsonToBinary","expected_length","array_buffer","data_object","_i","isNullOrUndefined","decodeBase64","base64","enableUnicode","binaryString","createURL","sourcemapArg","enableUnicodeArg","source","body","blob","createBase64WorkerFactory","url","WorkerFactory","WaveformData","defaultOptions","getOptions","opts","getChannelData","audio_buffer","createFromAudioBuffer","worker","evt","createFromArrayBuffer","audioContext","audioData","errorCallback","promise","WaveformResampler","input_buffer_length_samples","output_buffer_length_samples","output_header_size","count","total","_i2","_i3","resampler","self","otherWaveforms","otherWaveform","combinedBuffer","headerSize","totalSize","totalDataLength","bufferCollection","dataSize","totalBuffer","sourceHeader","totalBufferView","_i4","dataOfTotalBuffer","_i5","_buffer","startIndex","endIndex","output_data","output_dataview","waveform","loadWaveformData","src","response","arrayBuffer","json","waveformDataToPeaks","minArray","maxArray","peaks","loadPeaksFromWaveformData","getWaveformDataMetadata","extractPeaksFromWaveformData","offsetSamples","processedData","sourceScale","extractPeaksFromWaveformDataFull","isMono","channelPeaks","len","weight","numPeaks","monoPeaks","useTimeFormat","formatTimeUtil","timeString","parseTimeUtil","DEFAULT_ZOOM_LEVELS","useZoomControls","initialSamplesPerPixel","zoomLevels","zoomIndex","setZoomIndex","canZoomIn","canZoomOut","zoomIn","startTransition","zoomOut","useMasterVolume","playoutRef","initialVolume","onVolumeChange","masterVolume","setMasterVolumeState","setMasterVolume","useMasterAnalyser","fftSize","analyserRef","masterEffects","masterGainNode","_isOffline","analyserNode","Analyser","createClip","audioBuffer","name","sourceDurationSamples","generateId","createClipFromSeconds","sourceDuration","createTrack","clips","spectrogramConfig","spectrogramColorMap","useAudioTracks","configs","progressive","tracks","setTracks","loading","setLoading","setError","loadedCount","setLoadedCount","totalCount","cancelled","abortController","loadedTracksMap","createTrackFromConfig","clip","Tone","loadPromises","_","loadedTracks","errorMessage","useClipDragHandlers","onTracksChange","originalClipStateRef","React","collisionModifier","args","active","boundary","timeDelta","newStartTime","sortedClips","b","sortedIndex","previousClip","previousEndTime","nextClip","newEndTime","nextClipStartTime","constrainedX","onDragStart","event","onDragMove","sampleDelta","MIN_DURATION_SAMPLES","originalClip","newTracks","tIdx","newClips","cIdx","audioBufferDurationSamples","constrainedDelta","minDeltaForStart","minDeltaForOffset","minDeltaForPrevious","maxDeltaForMinDuration","newOffsetSamples","newDurationSamples","newStartSample","onDragEnd","previousEndSample","LINK_THRESHOLD","useAnnotationDragHandlers","annotations","onAnnotationsChange","linkEndpoints","originalAnnotationStateRef","annotation","annotationIndex","originalState","newTime","updatedAnnotations","updateAnnotationBoundaries","isDraggingStart","shouldLinkEndpoints","constrainedStart","prevAnnotation","constrainedEnd","nextAnnotation","currentIndex","current","next","nextDelta","useDragSensors","touchDelay","touchTolerance","mouseDistance","mouseSensor","useSensor","MouseSensor","touchSensor","TouchSensor","pointerSensor","PointerSensor","useSensors","useClipSplitting","currentTimeRef","usePlaybackAnimation","selectedTrackId","usePlaylistState","splitClipAt","splitTime","clipEndTime","splitSample","splitPixel","clipEndSample","snappedSplitSample","firstClipStartSample","firstClipDurationSamples","secondClipStartSample","secondClipDurationSamples","offsetIncrement","firstClip","secondClip","useKeyboardShortcuts","shortcuts","enabled","matchingShortcut","shortcut","keyMatch","ctrlMatch","shiftMatch","metaMatch","altMatch","getShortcutLabel","isMac","usePlaybackShortcuts","additionalShortcuts","overrideShortcuts","isPlaying","setCurrentTime","play","pause","usePlaylistControls","usePlaylistData","togglePlayPause","stopPlayback","rewindToStart","activeShortcuts","TIME_DELTA","useAnnotationKeyboardControls","activeAnnotationId","onActiveAnnotationChange","continuousPlay","onPlay","activeIndex","scrollToAnnotation","annotationId","container","startPixel","endPixel","annotationCenter","targetScrollLeft","moveStartBoundary","actualDelta","moveEndBoundary","newNextStart","selectPrevious","selectNext","selectFirst","selectLast","clearSelection","playActiveAnnotation","playDuration","activeAnnotationShortcuts","navigationShortcuts","effectDefinitions","getEffectDefinition","def","getEffectsByCategory","category","effectCategories","effectConstructors","Reverb","Freeverb","JCReverb","FeedbackDelay","PingPongDelay","Chorus","Phaser","Tremolo","Vibrato","AutoPanner","AutoFilter","AutoWah","EQ3","Distortion","BitCrusher","Chebyshev","Compressor","Limiter","Gate","StereoWidener","instanceCounter","generateInstanceId","createEffectInstance","definition","initialParams","Constructor","effect","instanceId","effectRecord","prop","wetProp","createEffectChain","effects","useDynamicEffects","activeEffects","setActiveEffects","activeEffectsRef","effectInstancesRef","graphNodesRef","rebuildChain","nodes","instances","ae","inst","currentNode","addEffect","effectId","params","instance","newActiveEffect","removeEffect","updateParameter","paramName","toggleBypass","newBypassed","originalWet","reorderEffects","fromIndex","toIndex","newEffects","removed","clearAllEffects","effectInstances","createOfflineEffectsFunction","nonBypassedEffects","offlineInstances","activeEffect","useTrackDynamicEffects","trackEffectsState","setTrackEffectsState","trackEffectInstancesRef","trackGraphNodesRef","rebuildTrackChain","trackEffects","graphEnd","instancesMap","addEffectToTrack","newState","existing","removeEffectFromTrack","updateTrackEffectParameter","trackEffectsStateRef","clearTrackEffects","getTrackEffectsFunction","trackEffectInstances","createOfflineTrackEffectsFunction","encodeWav","bitDepth","numSamples","bytesPerSample","blockAlign","byteRate","writeString","channelData","clampedSample","intSample","str","downloadBlob","filename","useExportWav","isExporting","setIsExporting","setProgress","trackStates","mode","autoDownload","applyEffects","effectsFunction","createOfflineTrackEffects","onProgress","totalDurationSamples","tracksToRender","hasSolo","state","hasOfflineTrackEffects","renderedBuffer","renderWithToneEffects","offlineCtx","scheduledClips","totalClips","sum","scheduleClip","currentProgress","exportFilename","message","_trackStates","Offline","ToneAudioBuffer","transport","trackVolume","gainToDb","trackPan","trackMute","clipGain","toneBuffer","fadeInStart","fadeInEnd","fadeOutStart","fadeOutEnd","trackState","gainNode","baseGain","pannerNode","applyFadeEnvelope","gainParam","endTime","fadeType","expStart","expEnd","logCurve","generateFadeCurve","sCurve","curveValue","useAnimationFrameLoop","animationFrameRef","stopAnimationFrameLoop","startAnimationFrameLoop","workerSource","idCounter","createPeaksWorker","pending","terminated","msg","entry","messageId","resolve","reject","useWaveformDataCache","baseScale","cache","setCache","isGenerating","setIsGenerating","workerRef","submittedRef","pendingCountRef","getWorker","submitted","clipsToProcess","submittedThisRun","PlaybackAnimationContext","PlaylistStateContext","PlaylistControlsContext","PlaylistDataContext","WaveformPlaylistProvider","mono","automaticScroll","userTheme","annotationList","onReady","_onAnnotationUpdate","progressBarWidthProp","progressBarWidth","first","annotationsRef","setActiveAnnotationIdState","setIsPlaying","setDuration","audioBuffers","setAudioBuffers","peaksDataArray","setPeaksDataArray","setTrackStates","setSelectionStart","setSelectionEnd","setSelectedTrackId","isAutomaticScroll","setIsAutomaticScroll","setContinuousPlayState","setLinkEndpoints","annotationsEditable","setAnnotationsEditable","isLoopEnabled","setIsLoopEnabledState","loopStart","setLoopStartState","loopEnd","setLoopEndState","isReady","setIsReady","playStartPositionRef","trackStatesRef","playbackStartTimeRef","audioStartPositionRef","playbackEndTimeRef","isAutomaticScrollRef","continuousPlayRef","activeAnnotationIdRef","samplesPerPixelRef","isLoopEnabledRef","selectionStartRef","selectionEndRef","loopStartRef","loopEndRef","zoom","waveformDataCache","setContinuousPlay","setActiveAnnotationId","setLoopEnabled","setLoopRegion","end","setLoopRegionFromSelection","clearLoopRegion","oldSamplesPerPixel","newSamplesPerPixel","centerPixel","sr","newCenterPixel","newScrollLeft","pendingResumeRef","wasPlaying","resumePosition","buffers","maxDuration","prevStates","playout","currentTrackStates","playableClips","trackObj","clipSampleRate","allTrackPeaks","cached","startAnimationLoop","updateTime","currentAnnotations","currentAnnotation","ann","activeAnnotation","pixelPosition","visualPosition","hasValidLoopRegion","timeNow","stopAnimationLoop","currentPos","position","actualStartTime","startTimeNow","pauseTime","seekTo","clampedTime","setTrackMute","newStates","setTrackSolo","setTrackVolume","setTrackPan","setSelection","setScrollContainer","onAnnotationsChangeRef","setAnnotations","action","updated","minimumPlaylistHeight","animationValue","stateValue","setCurrentTimeControl","setAutomaticScrollControl","controlsValue","dataValue","mergedTheme","jsx","ThemeProvider","context","MediaElementTrack","audio","rate","clampedRate","MediaElementPlayout","_when","adjustedDuration","_trackId","_soloed","MediaElementAnimationContext","MediaElementStateContext","MediaElementControlsContext","MediaElementDataContext","MediaElementPlaylistProvider","initialPlaybackRate","playbackRate","setPlaybackRateState","mediaTrack","extractedPeaks","clipPeaks","setPlaybackRate","useMediaElementAnimation","useMediaElementState","useMediaElementControls","useMediaElementData","PlayButton","PauseButton","StopButton","RewindButton","FastForwardButton","SkipBackwardButton","skipAmount","SkipForwardButton","LoopButton","defaultEnd","SetLoopRegionButton","hasValidSelection","ZoomInButton","ZoomOutButton","BaseMasterVolumeControl","BaseTimeFormatSelect","PositionDisplay","AudioPosition","timeRef","BaseSelectionTimeInputs","setAutomaticScroll","BaseAutomaticScrollCheckbox","AnnotationIntegrationContext","AnnotationIntegrationProvider","useAnnotationIntegration","ContinuousPlayCheckbox","Base","LinkEndpointsCheckbox","EditableCheckbox","DownloadAnnotationsButton","ExportWavButton","onExportComplete","onExportError","exportWav","handleExport","buttonLabel","PlayheadLine","AnimatedPlayhead","playheadRef","updatePosition","ChannelWrapper","Background","ProgressOverlay","ChannelWithProgress","clipStartSample","clipDurationSamples","smartChannelProps","progressRef","progressColor","updateProgress","currentSample","ratio","isSpectrogramMode","isBothMode","effectiveHeight","effectiveTop","waveformBackgroundCss","SpectrogramIntegrationContext","SpectrogramIntegrationProvider","useSpectrogramIntegration","DEFAULT_EMPTY_TRACK_DURATION","PlaylistVisualization","renderTrackControls","renderPlayhead","_annotationControls","getAnnotationBoxLabel","showClipHeaders","interactiveClips","onRemoveTrack","recordingState","_linkEndpoints","annotationIntegration","_setAnnotations","spectrogram","perTrackSpectrogramHelpers","helpers","overrides","cm","cfg","workerCanvasApi","settingsModalTrackId","setSettingsModalTrackId","setIsSelecting","handleScrollContainerRef","displayDuration","recordingEndTime","tracksFullWidth","handleAnnotationClick","selectTrack","handleMouseDown","clickTime","trackY","cumulativeHeight","clickedTrackIndex","trackClipPeaks","rawCh","trackHeight","moveTime","startPixels","endPixels","startSeconds","endSeconds","effectiveRenderMode","trackControls","onClose","maxChannels","TrackComponent","trackCfg","peaksData","clipSpectrograms","channelSpectrogram","canvasIds","canvasWidths","newConfig","newColorMap","PlaylistAnnotationList","renderAnnotationItem","onAnnotationUpdate","annotationListConfig","scrollActivePosition","scrollActiveContainer","integration","resolvedConfig","handleAnnotationUpdate","AnnotationText","annotationControls","annotationTextHeight","AnimatedMediaElementPlayhead","ChannelWithMediaElementProgress","progressWidth","playedSamples","MediaElementPlaylist","editable","linkEndpointsProp","emptyControls","DndContext","restrictToHorizontalAxis","MediaElementAnnotationList","MediaElementWaveform"],"mappings":"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","x_google_ignoreList":[1,2,3,4,5,6,7,8,9,10,11,13]}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../core/dist/index.mjs","../../playout/dist/index.mjs","../../../node_modules/.pnpm/@dnd-kit+utilities@3.2.2_react@18.3.1/node_modules/@dnd-kit/utilities/dist/utilities.esm.js","../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@18.3.1_react@18.3.1/node_modules/@phosphor-icons/react/dist/defs/DotsThree.es.js","../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@18.3.1_react@18.3.1/node_modules/@phosphor-icons/react/dist/defs/SpeakerHigh.es.js","../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@18.3.1_react@18.3.1/node_modules/@phosphor-icons/react/dist/defs/SpeakerLow.es.js","../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@18.3.1_react@18.3.1/node_modules/@phosphor-icons/react/dist/defs/X.es.js","../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@18.3.1_react@18.3.1/node_modules/@phosphor-icons/react/dist/lib/context.es.js","../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@18.3.1_react@18.3.1/node_modules/@phosphor-icons/react/dist/lib/IconBase.es.js","../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@18.3.1_react@18.3.1/node_modules/@phosphor-icons/react/dist/csr/DotsThree.es.js","../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@18.3.1_react@18.3.1/node_modules/@phosphor-icons/react/dist/csr/SpeakerHigh.es.js","../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@18.3.1_react@18.3.1/node_modules/@phosphor-icons/react/dist/csr/SpeakerLow.es.js","../../../node_modules/.pnpm/@phosphor-icons+react@2.1.10_react-dom@18.3.1_react@18.3.1/node_modules/@phosphor-icons/react/dist/csr/X.es.js","../../ui-components/dist/index.mjs","../../../node_modules/.pnpm/waveform-data@4.5.2/node_modules/waveform-data/dist/waveform-data.esm.js","../src/waveformDataLoader.ts","../src/hooks/useTimeFormat.ts","../src/hooks/useZoomControls.ts","../src/hooks/useMasterVolume.ts","../src/hooks/useAudioEffects.ts","../src/hooks/useAudioTracks.ts","../src/hooks/useClipDragHandlers.ts","../src/hooks/useAnnotationDragHandlers.ts","../src/hooks/useDragSensors.ts","../src/hooks/useClipSplitting.ts","../src/hooks/useKeyboardShortcuts.ts","../src/hooks/usePlaybackShortcuts.ts","../src/hooks/useAnnotationKeyboardControls.ts","../src/effects/effectDefinitions.ts","../src/effects/effectFactory.ts","../src/hooks/useDynamicEffects.ts","../src/hooks/useTrackDynamicEffects.ts","../src/utils/wavEncoder.ts","../src/hooks/useExportWav.ts","../src/hooks/useAnimationFrameLoop.ts","../src/workers/peaksWorker.ts","../src/hooks/useWaveformDataCache.ts","../src/hooks/useDynamicTracks.ts","../src/WaveformPlaylistContext.tsx","../../media-element-playout/dist/index.mjs","../src/MediaElementPlaylistContext.tsx","../src/components/PlaybackControls.tsx","../src/components/ZoomControls.tsx","../src/components/ContextualControls.tsx","../src/AnnotationIntegrationContext.tsx","../src/components/AnnotationControls.tsx","../src/components/ExportControls.tsx","../src/components/AnimatedPlayhead.tsx","../src/components/ChannelWithProgress.tsx","../src/SpectrogramIntegrationContext.tsx","../src/components/PlaylistVisualization.tsx","../src/components/PlaylistAnnotationList.tsx","../src/components/Waveform.tsx","../src/components/AnimatedMediaElementPlayhead.tsx","../src/components/ChannelWithMediaElementProgress.tsx","../src/components/MediaElementPlaylist.tsx","../src/components/MediaElementAnnotationList.tsx","../src/components/MediaElementWaveform.tsx"],"sourcesContent":["// src/constants.ts\nvar MAX_CANVAS_WIDTH = 1e3;\n\n// src/types/clip.ts\nfunction createClip(options) {\n const {\n audioBuffer,\n startSample,\n offsetSamples = 0,\n gain = 1,\n name,\n color,\n fadeIn,\n fadeOut,\n waveformData\n } = options;\n const sampleRate = audioBuffer?.sampleRate ?? options.sampleRate ?? waveformData?.sample_rate;\n const sourceDurationSamples = audioBuffer?.length ?? options.sourceDurationSamples ?? (waveformData && sampleRate ? Math.ceil(waveformData.duration * sampleRate) : void 0);\n if (sampleRate === void 0) {\n throw new Error(\n \"createClip: sampleRate is required when audioBuffer is not provided (can use waveformData.sample_rate)\"\n );\n }\n if (sourceDurationSamples === void 0) {\n throw new Error(\n \"createClip: sourceDurationSamples is required when audioBuffer is not provided (can use waveformData.duration)\"\n );\n }\n if (audioBuffer && waveformData && audioBuffer.sampleRate !== waveformData.sample_rate) {\n console.warn(\n `Sample rate mismatch: audioBuffer (${audioBuffer.sampleRate}) vs waveformData (${waveformData.sample_rate}). Using audioBuffer sample rate. Waveform visualization may be slightly off.`\n );\n }\n const durationSamples = options.durationSamples ?? sourceDurationSamples;\n return {\n id: generateId(),\n audioBuffer,\n startSample,\n durationSamples,\n offsetSamples,\n sampleRate,\n sourceDurationSamples,\n gain,\n name,\n color,\n fadeIn,\n fadeOut,\n waveformData\n };\n}\nfunction createClipFromSeconds(options) {\n const {\n audioBuffer,\n startTime,\n offset = 0,\n gain = 1,\n name,\n color,\n fadeIn,\n fadeOut,\n waveformData\n } = options;\n const sampleRate = audioBuffer?.sampleRate ?? options.sampleRate ?? waveformData?.sample_rate;\n if (sampleRate === void 0) {\n throw new Error(\n \"createClipFromSeconds: sampleRate is required when audioBuffer is not provided (can use waveformData.sample_rate)\"\n );\n }\n const sourceDuration = audioBuffer?.duration ?? options.sourceDuration ?? waveformData?.duration;\n if (sourceDuration === void 0) {\n throw new Error(\n \"createClipFromSeconds: sourceDuration is required when audioBuffer is not provided (can use waveformData.duration)\"\n );\n }\n if (audioBuffer && waveformData && audioBuffer.sampleRate !== waveformData.sample_rate) {\n console.warn(\n `Sample rate mismatch: audioBuffer (${audioBuffer.sampleRate}) vs waveformData (${waveformData.sample_rate}). Using audioBuffer sample rate. Waveform visualization may be slightly off.`\n );\n }\n const duration = options.duration ?? sourceDuration;\n return createClip({\n audioBuffer,\n startSample: Math.round(startTime * sampleRate),\n durationSamples: Math.round(duration * sampleRate),\n offsetSamples: Math.round(offset * sampleRate),\n sampleRate,\n sourceDurationSamples: Math.ceil(sourceDuration * sampleRate),\n gain,\n name,\n color,\n fadeIn,\n fadeOut,\n waveformData\n });\n}\nfunction createTrack(options) {\n const {\n name,\n clips = [],\n muted = false,\n soloed = false,\n volume = 1,\n pan = 0,\n color,\n height,\n spectrogramConfig,\n spectrogramColorMap\n } = options;\n return {\n id: generateId(),\n name,\n clips,\n muted,\n soloed,\n volume,\n pan,\n color,\n height,\n spectrogramConfig,\n spectrogramColorMap\n };\n}\nfunction createTimeline(tracks, sampleRate = 44100, options) {\n const durationSamples = tracks.reduce((maxSamples, track) => {\n const trackSamples = track.clips.reduce((max, clip) => {\n return Math.max(max, clip.startSample + clip.durationSamples);\n }, 0);\n return Math.max(maxSamples, trackSamples);\n }, 0);\n const duration = durationSamples / sampleRate;\n return {\n tracks,\n duration,\n sampleRate,\n name: options?.name,\n tempo: options?.tempo,\n timeSignature: options?.timeSignature\n };\n}\nfunction generateId() {\n return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n}\nfunction getClipsInRange(track, startSample, endSample) {\n return track.clips.filter((clip) => {\n const clipEnd = clip.startSample + clip.durationSamples;\n return clip.startSample < endSample && clipEnd > startSample;\n });\n}\nfunction getClipsAtSample(track, sample) {\n return track.clips.filter((clip) => {\n const clipEnd = clip.startSample + clip.durationSamples;\n return sample >= clip.startSample && sample < clipEnd;\n });\n}\nfunction clipsOverlap(clip1, clip2) {\n const clip1End = clip1.startSample + clip1.durationSamples;\n const clip2End = clip2.startSample + clip2.durationSamples;\n return clip1.startSample < clip2End && clip1End > clip2.startSample;\n}\nfunction sortClipsByTime(clips) {\n return [...clips].sort((a, b) => a.startSample - b.startSample);\n}\nfunction findGaps(track) {\n if (track.clips.length === 0) return [];\n const sorted = sortClipsByTime(track.clips);\n const gaps = [];\n for (let i = 0; i < sorted.length - 1; i++) {\n const currentClipEnd = sorted[i].startSample + sorted[i].durationSamples;\n const nextClipStart = sorted[i + 1].startSample;\n if (nextClipStart > currentClipEnd) {\n gaps.push({\n startSample: currentClipEnd,\n endSample: nextClipStart,\n durationSamples: nextClipStart - currentClipEnd\n });\n }\n }\n return gaps;\n}\n\n// src/types/index.ts\nvar InteractionState = /* @__PURE__ */ ((InteractionState2) => {\n InteractionState2[\"Cursor\"] = \"cursor\";\n InteractionState2[\"Select\"] = \"select\";\n InteractionState2[\"Shift\"] = \"shift\";\n InteractionState2[\"FadeIn\"] = \"fadein\";\n InteractionState2[\"FadeOut\"] = \"fadeout\";\n return InteractionState2;\n})(InteractionState || {});\n\n// src/utils/conversions.ts\nfunction samplesToSeconds(samples, sampleRate) {\n return samples / sampleRate;\n}\nfunction secondsToSamples(seconds, sampleRate) {\n return Math.ceil(seconds * sampleRate);\n}\nfunction samplesToPixels(samples, samplesPerPixel) {\n return Math.floor(samples / samplesPerPixel);\n}\nfunction pixelsToSamples(pixels, samplesPerPixel) {\n return Math.floor(pixels * samplesPerPixel);\n}\nfunction pixelsToSeconds(pixels, samplesPerPixel, sampleRate) {\n return pixels * samplesPerPixel / sampleRate;\n}\nfunction secondsToPixels(seconds, samplesPerPixel, sampleRate) {\n return Math.ceil(seconds * sampleRate / samplesPerPixel);\n}\n\n// src/clipTimeHelpers.ts\nfunction clipStartTime(clip) {\n return clip.startSample / clip.sampleRate;\n}\nfunction clipEndTime(clip) {\n return (clip.startSample + clip.durationSamples) / clip.sampleRate;\n}\nfunction clipOffsetTime(clip) {\n return clip.offsetSamples / clip.sampleRate;\n}\nfunction clipDurationTime(clip) {\n return clip.durationSamples / clip.sampleRate;\n}\nexport {\n InteractionState,\n MAX_CANVAS_WIDTH,\n clipDurationTime,\n clipEndTime,\n clipOffsetTime,\n clipStartTime,\n clipsOverlap,\n createClip,\n createClipFromSeconds,\n createTimeline,\n createTrack,\n findGaps,\n getClipsAtSample,\n getClipsInRange,\n pixelsToSamples,\n pixelsToSeconds,\n samplesToPixels,\n samplesToSeconds,\n secondsToPixels,\n secondsToSamples,\n sortClipsByTime\n};\n//# sourceMappingURL=index.mjs.map","// src/TonePlayout.ts\nimport {\n Volume as Volume2,\n getDestination as getDestination2,\n start,\n now as now2,\n getTransport,\n getContext\n} from \"tone\";\n\n// src/ToneTrack.ts\nimport { Player, Volume, Gain, Panner, getDestination, now } from \"tone\";\n\n// src/fades.ts\nvar hasWarned = false;\nfunction getUnderlyingAudioParam(signal) {\n const param = signal._param;\n if (!param && !hasWarned) {\n hasWarned = true;\n console.warn(\n \"[waveform-playlist] Unable to access Tone.js internal _param. This likely means the Tone.js version is incompatible. Fades and mute scheduling may not work correctly.\"\n );\n }\n return param;\n}\nfunction linearCurve(length, fadeIn) {\n const curve = new Float32Array(length);\n const scale = length - 1;\n for (let i = 0; i < length; i++) {\n const x = i / scale;\n curve[i] = fadeIn ? x : 1 - x;\n }\n return curve;\n}\nfunction exponentialCurve(length, fadeIn) {\n const curve = new Float32Array(length);\n const scale = length - 1;\n for (let i = 0; i < length; i++) {\n const x = i / scale;\n const index = fadeIn ? i : length - 1 - i;\n curve[index] = Math.exp(2 * x - 1) / Math.E;\n }\n return curve;\n}\nfunction sCurveCurve(length, fadeIn) {\n const curve = new Float32Array(length);\n const phase = fadeIn ? Math.PI / 2 : -Math.PI / 2;\n for (let i = 0; i < length; i++) {\n curve[i] = Math.sin(Math.PI * i / length - phase) / 2 + 0.5;\n }\n return curve;\n}\nfunction logarithmicCurve(length, fadeIn, base = 10) {\n const curve = new Float32Array(length);\n for (let i = 0; i < length; i++) {\n const index = fadeIn ? i : length - 1 - i;\n const x = i / length;\n curve[index] = Math.log(1 + base * x) / Math.log(1 + base);\n }\n return curve;\n}\nfunction generateCurve(type, length, fadeIn) {\n switch (type) {\n case \"linear\":\n return linearCurve(length, fadeIn);\n case \"exponential\":\n return exponentialCurve(length, fadeIn);\n case \"sCurve\":\n return sCurveCurve(length, fadeIn);\n case \"logarithmic\":\n return logarithmicCurve(length, fadeIn);\n default:\n return linearCurve(length, fadeIn);\n }\n}\nfunction applyFadeIn(param, startTime, duration, type = \"linear\", startValue = 0, endValue = 1) {\n if (duration <= 0) return;\n if (type === \"linear\") {\n param.setValueAtTime(startValue, startTime);\n param.linearRampToValueAtTime(endValue, startTime + duration);\n } else if (type === \"exponential\") {\n param.setValueAtTime(Math.max(startValue, 1e-3), startTime);\n param.exponentialRampToValueAtTime(Math.max(endValue, 1e-3), startTime + duration);\n } else {\n const curve = generateCurve(type, 1e4, true);\n const scaledCurve = new Float32Array(curve.length);\n const range = endValue - startValue;\n for (let i = 0; i < curve.length; i++) {\n scaledCurve[i] = startValue + curve[i] * range;\n }\n param.setValueCurveAtTime(scaledCurve, startTime, duration);\n }\n}\nfunction applyFadeOut(param, startTime, duration, type = \"linear\", startValue = 1, endValue = 0) {\n if (duration <= 0) return;\n if (type === \"linear\") {\n param.setValueAtTime(startValue, startTime);\n param.linearRampToValueAtTime(endValue, startTime + duration);\n } else if (type === \"exponential\") {\n param.setValueAtTime(Math.max(startValue, 1e-3), startTime);\n param.exponentialRampToValueAtTime(Math.max(endValue, 1e-3), startTime + duration);\n } else {\n const curve = generateCurve(type, 1e4, false);\n const scaledCurve = new Float32Array(curve.length);\n const range = startValue - endValue;\n for (let i = 0; i < curve.length; i++) {\n scaledCurve[i] = endValue + curve[i] * range;\n }\n param.setValueCurveAtTime(scaledCurve, startTime, duration);\n }\n}\n\n// src/ToneTrack.ts\nvar ToneTrack = class {\n // Count of currently playing clips\n constructor(options) {\n this.activePlayers = 0;\n this.track = options.track;\n this.volumeNode = new Volume(this.gainToDb(options.track.gain));\n this.panNode = new Panner(options.track.stereoPan);\n this.muteGain = new Gain(options.track.muted ? 0 : 1);\n const destination = options.destination || getDestination();\n if (options.effects) {\n const cleanup = options.effects(this.muteGain, destination, false);\n if (cleanup) {\n this.effectsCleanup = cleanup;\n }\n } else {\n this.muteGain.connect(destination);\n }\n const clipInfos = options.clips || (options.buffer ? [\n {\n buffer: options.buffer,\n startTime: 0,\n // Legacy: single buffer starts at timeline position 0\n duration: options.buffer.duration,\n // Legacy: play full buffer duration\n offset: 0,\n fadeIn: options.track.fadeIn,\n fadeOut: options.track.fadeOut,\n gain: 1\n }\n ] : []);\n this.clips = clipInfos.map((clipInfo) => {\n const player = new Player({\n url: clipInfo.buffer,\n loop: false,\n onstop: () => {\n this.activePlayers--;\n if (this.activePlayers === 0 && this.onStopCallback) {\n this.onStopCallback();\n }\n }\n });\n const fadeGain = new Gain(clipInfo.gain);\n player.connect(fadeGain);\n fadeGain.chain(this.volumeNode, this.panNode, this.muteGain);\n return {\n player,\n clipInfo,\n fadeGain,\n pausedPosition: 0,\n playStartTime: 0\n };\n });\n }\n /**\n * Schedule fade envelopes for a clip at the given start time\n */\n scheduleFades(clipPlayer, clipStartTime2, clipOffset = 0) {\n const { clipInfo, fadeGain } = clipPlayer;\n const audioParam = getUnderlyingAudioParam(fadeGain.gain);\n if (!audioParam) return;\n audioParam.cancelScheduledValues(0);\n const skipTime = clipOffset - clipInfo.offset;\n if (clipInfo.fadeIn && skipTime < clipInfo.fadeIn.duration) {\n const fadeInDuration = clipInfo.fadeIn.duration;\n if (skipTime <= 0) {\n applyFadeIn(\n audioParam,\n clipStartTime2,\n fadeInDuration,\n clipInfo.fadeIn.type || \"linear\",\n 0,\n clipInfo.gain\n );\n } else {\n const remainingFadeDuration = fadeInDuration - skipTime;\n const fadeProgress = skipTime / fadeInDuration;\n const startValue = clipInfo.gain * fadeProgress;\n applyFadeIn(\n audioParam,\n clipStartTime2,\n remainingFadeDuration,\n clipInfo.fadeIn.type || \"linear\",\n startValue,\n clipInfo.gain\n );\n }\n } else {\n audioParam.setValueAtTime(clipInfo.gain, clipStartTime2);\n }\n if (clipInfo.fadeOut) {\n const fadeOutStart = clipInfo.duration - clipInfo.fadeOut.duration;\n const fadeOutStartInClip = fadeOutStart - skipTime;\n if (fadeOutStartInClip > 0) {\n const absoluteFadeOutStart = clipStartTime2 + fadeOutStartInClip;\n applyFadeOut(\n audioParam,\n absoluteFadeOutStart,\n clipInfo.fadeOut.duration,\n clipInfo.fadeOut.type || \"linear\",\n clipInfo.gain,\n 0\n );\n } else if (fadeOutStartInClip > -clipInfo.fadeOut.duration) {\n const elapsedFadeOut = -fadeOutStartInClip;\n const remainingFadeDuration = clipInfo.fadeOut.duration - elapsedFadeOut;\n const fadeProgress = elapsedFadeOut / clipInfo.fadeOut.duration;\n const startValue = clipInfo.gain * (1 - fadeProgress);\n applyFadeOut(\n audioParam,\n clipStartTime2,\n remainingFadeDuration,\n clipInfo.fadeOut.type || \"linear\",\n startValue,\n 0\n );\n }\n }\n }\n gainToDb(gain) {\n return 20 * Math.log10(gain);\n }\n setVolume(gain) {\n this.track.gain = gain;\n this.volumeNode.volume.value = this.gainToDb(gain);\n }\n setPan(pan) {\n this.track.stereoPan = pan;\n this.panNode.pan.value = pan;\n }\n setMute(muted) {\n this.track.muted = muted;\n const value = muted ? 0 : 1;\n const audioParam = getUnderlyingAudioParam(this.muteGain.gain);\n audioParam?.setValueAtTime(value, 0);\n this.muteGain.gain.value = value;\n }\n setSolo(soloed) {\n this.track.soloed = soloed;\n }\n play(when, offset = 0, duration) {\n this.clips.forEach((clipPlayer) => {\n clipPlayer.player.stop();\n clipPlayer.player.disconnect();\n clipPlayer.player.dispose();\n const newPlayer = new Player({\n url: clipPlayer.clipInfo.buffer,\n loop: false,\n onstop: () => {\n this.activePlayers--;\n if (this.activePlayers === 0 && this.onStopCallback) {\n this.onStopCallback();\n }\n }\n });\n newPlayer.connect(clipPlayer.fadeGain);\n clipPlayer.player = newPlayer;\n clipPlayer.pausedPosition = 0;\n });\n this.activePlayers = 0;\n this.clips.forEach((clipPlayer) => {\n const { player, clipInfo } = clipPlayer;\n const playbackPosition = offset;\n const clipStart = clipInfo.startTime;\n const clipEnd = clipInfo.startTime + clipInfo.duration;\n if (playbackPosition < clipEnd) {\n this.activePlayers++;\n const currentTime = when ?? now();\n clipPlayer.playStartTime = currentTime;\n if (playbackPosition >= clipStart) {\n const clipOffset = playbackPosition - clipStart + clipInfo.offset;\n const remainingDuration = clipInfo.duration - (playbackPosition - clipStart);\n const clipDuration = duration ? Math.min(duration, remainingDuration) : remainingDuration;\n clipPlayer.pausedPosition = clipOffset;\n this.scheduleFades(clipPlayer, currentTime, clipOffset);\n player.start(currentTime, clipOffset, clipDuration);\n } else {\n const delay = clipStart - playbackPosition;\n const clipDuration = duration ? Math.min(duration - delay, clipInfo.duration) : clipInfo.duration;\n if (delay < (duration ?? Infinity)) {\n clipPlayer.pausedPosition = clipInfo.offset;\n this.scheduleFades(clipPlayer, currentTime + delay, clipInfo.offset);\n player.start(currentTime + delay, clipInfo.offset, clipDuration);\n } else {\n this.activePlayers--;\n }\n }\n }\n });\n }\n pause() {\n this.clips.forEach((clipPlayer) => {\n if (clipPlayer.player.state === \"started\") {\n const elapsed = (now() - clipPlayer.playStartTime) * clipPlayer.player.playbackRate;\n clipPlayer.pausedPosition = clipPlayer.pausedPosition + elapsed;\n }\n clipPlayer.player.stop();\n });\n this.activePlayers = 0;\n }\n stop(when) {\n const stopWhen = when ?? now();\n this.clips.forEach((clipPlayer) => {\n clipPlayer.player.stop(stopWhen);\n clipPlayer.pausedPosition = 0;\n });\n this.activePlayers = 0;\n }\n dispose() {\n if (this.effectsCleanup) {\n this.effectsCleanup();\n }\n this.clips.forEach((clipPlayer) => {\n clipPlayer.player.dispose();\n clipPlayer.fadeGain.dispose();\n });\n this.volumeNode.dispose();\n this.panNode.dispose();\n this.muteGain.dispose();\n }\n get id() {\n return this.track.id;\n }\n get duration() {\n if (this.clips.length === 0) return 0;\n const lastClip = this.clips[this.clips.length - 1];\n return lastClip.clipInfo.startTime + lastClip.clipInfo.duration;\n }\n get buffer() {\n return this.clips[0]?.clipInfo.buffer;\n }\n get isPlaying() {\n return this.clips.some((clipPlayer) => clipPlayer.player.state === \"started\");\n }\n get muted() {\n return this.track.muted;\n }\n get startTime() {\n return this.track.startTime;\n }\n setOnStopCallback(callback) {\n this.onStopCallback = callback;\n }\n};\n\n// src/TonePlayout.ts\nvar TonePlayout = class {\n constructor(options = {}) {\n this.tracks = /* @__PURE__ */ new Map();\n this.isInitialized = false;\n this.soloedTracks = /* @__PURE__ */ new Set();\n this.manualMuteState = /* @__PURE__ */ new Map();\n this.activeTracks = /* @__PURE__ */ new Map();\n // Map track ID to session ID\n this.playbackSessionId = 0;\n this.masterVolume = new Volume2(this.gainToDb(options.masterGain ?? 1));\n if (options.effects) {\n const cleanup = options.effects(this.masterVolume, getDestination2(), false);\n if (cleanup) {\n this.effectsCleanup = cleanup;\n }\n } else {\n this.masterVolume.toDestination();\n }\n if (options.tracks) {\n options.tracks.forEach((track) => {\n this.tracks.set(track.id, track);\n this.manualMuteState.set(track.id, track.muted);\n });\n }\n }\n gainToDb(gain) {\n return 20 * Math.log10(gain);\n }\n async init() {\n if (this.isInitialized) return;\n await start();\n this.isInitialized = true;\n }\n addTrack(trackOptions) {\n const optionsWithDestination = {\n ...trackOptions,\n destination: this.masterVolume\n };\n const toneTrack = new ToneTrack(optionsWithDestination);\n this.tracks.set(toneTrack.id, toneTrack);\n this.manualMuteState.set(toneTrack.id, trackOptions.track.muted ?? false);\n if (trackOptions.track.soloed) {\n this.soloedTracks.add(toneTrack.id);\n }\n return toneTrack;\n }\n /**\n * Apply solo muting after all tracks have been added.\n * Call this after adding all tracks to ensure solo logic is applied correctly.\n */\n applyInitialSoloState() {\n this.updateSoloMuting();\n }\n removeTrack(trackId) {\n const track = this.tracks.get(trackId);\n if (track) {\n track.dispose();\n this.tracks.delete(trackId);\n this.manualMuteState.delete(trackId);\n this.soloedTracks.delete(trackId);\n }\n }\n getTrack(trackId) {\n return this.tracks.get(trackId);\n }\n play(when, offset, duration) {\n if (!this.isInitialized) {\n console.warn(\"TonePlayout not initialized. Call init() first.\");\n return;\n }\n const startTime = when ?? now2();\n const playbackPosition = offset ?? 0;\n this.playbackSessionId++;\n const currentSessionId = this.playbackSessionId;\n this.activeTracks.clear();\n this.tracks.forEach((toneTrack) => {\n const trackStartTime = toneTrack.startTime;\n if (playbackPosition >= trackStartTime) {\n const bufferOffset = playbackPosition - trackStartTime;\n if (duration !== void 0) {\n this.activeTracks.set(toneTrack.id, currentSessionId);\n toneTrack.setOnStopCallback(() => {\n if (this.activeTracks.get(toneTrack.id) === currentSessionId) {\n this.activeTracks.delete(toneTrack.id);\n if (this.activeTracks.size === 0 && this.onPlaybackCompleteCallback) {\n this.onPlaybackCompleteCallback();\n }\n }\n });\n }\n toneTrack.play(startTime, bufferOffset, duration);\n } else {\n const delay = trackStartTime - playbackPosition;\n if (duration !== void 0) {\n this.activeTracks.set(toneTrack.id, currentSessionId);\n toneTrack.setOnStopCallback(() => {\n if (this.activeTracks.get(toneTrack.id) === currentSessionId) {\n this.activeTracks.delete(toneTrack.id);\n if (this.activeTracks.size === 0 && this.onPlaybackCompleteCallback) {\n this.onPlaybackCompleteCallback();\n }\n }\n });\n }\n toneTrack.play(startTime + delay, 0, duration);\n }\n });\n if (offset !== void 0) {\n getTransport().start(startTime, offset);\n } else {\n getTransport().start(startTime);\n }\n }\n pause() {\n getTransport().pause();\n this.tracks.forEach((track) => {\n track.pause();\n });\n }\n stop() {\n getTransport().stop();\n this.tracks.forEach((track) => {\n track.stop();\n });\n }\n setMasterGain(gain) {\n this.masterVolume.volume.value = this.gainToDb(gain);\n }\n setSolo(trackId, soloed) {\n const track = this.tracks.get(trackId);\n if (track) {\n track.setSolo(soloed);\n if (soloed) {\n this.soloedTracks.add(trackId);\n } else {\n this.soloedTracks.delete(trackId);\n }\n this.updateSoloMuting();\n }\n }\n updateSoloMuting() {\n const hasSoloedTracks = this.soloedTracks.size > 0;\n this.tracks.forEach((track, id) => {\n if (hasSoloedTracks) {\n if (!this.soloedTracks.has(id)) {\n track.setMute(true);\n } else {\n const manuallyMuted = this.manualMuteState.get(id) ?? false;\n track.setMute(manuallyMuted);\n }\n } else {\n const manuallyMuted = this.manualMuteState.get(id) ?? false;\n track.setMute(manuallyMuted);\n }\n });\n }\n setMute(trackId, muted) {\n const track = this.tracks.get(trackId);\n if (track) {\n this.manualMuteState.set(trackId, muted);\n track.setMute(muted);\n }\n }\n getCurrentTime() {\n return getTransport().seconds;\n }\n seekTo(time) {\n getTransport().seconds = time;\n }\n dispose() {\n this.tracks.forEach((track) => {\n track.dispose();\n });\n this.tracks.clear();\n if (this.effectsCleanup) {\n this.effectsCleanup();\n }\n this.masterVolume.dispose();\n }\n get context() {\n return getContext();\n }\n get sampleRate() {\n return getContext().sampleRate;\n }\n setOnPlaybackComplete(callback) {\n this.onPlaybackCompleteCallback = callback;\n }\n};\n\n// src/audioContext.ts\nimport { Context, setContext } from \"tone\";\nvar globalToneContext = null;\nfunction getGlobalContext() {\n if (!globalToneContext) {\n globalToneContext = new Context();\n setContext(globalToneContext);\n }\n return globalToneContext;\n}\nfunction getGlobalAudioContext() {\n return getGlobalContext().rawContext;\n}\nfunction getGlobalToneContext() {\n return getGlobalContext();\n}\nasync function resumeGlobalAudioContext() {\n const context = getGlobalContext();\n if (context.state !== \"running\") {\n await context.resume();\n }\n}\nfunction getGlobalAudioContextState() {\n return globalToneContext?.rawContext.state || \"suspended\";\n}\nasync function closeGlobalAudioContext() {\n if (globalToneContext && globalToneContext.rawContext.state !== \"closed\") {\n await globalToneContext.close();\n globalToneContext = null;\n }\n}\n\n// src/mediaStreamSourceManager.ts\nimport { getContext as getContext2 } from \"tone\";\nvar streamSources = /* @__PURE__ */ new Map();\nvar streamCleanupHandlers = /* @__PURE__ */ new Map();\nfunction getMediaStreamSource(stream) {\n if (streamSources.has(stream)) {\n return streamSources.get(stream);\n }\n const context = getContext2();\n const source = context.createMediaStreamSource(stream);\n streamSources.set(stream, source);\n const cleanup = () => {\n source.disconnect();\n streamSources.delete(stream);\n streamCleanupHandlers.delete(stream);\n stream.removeEventListener(\"ended\", cleanup);\n stream.removeEventListener(\"inactive\", cleanup);\n };\n streamCleanupHandlers.set(stream, cleanup);\n stream.addEventListener(\"ended\", cleanup);\n stream.addEventListener(\"inactive\", cleanup);\n return source;\n}\nfunction releaseMediaStreamSource(stream) {\n const cleanup = streamCleanupHandlers.get(stream);\n if (cleanup) {\n cleanup();\n }\n}\nfunction hasMediaStreamSource(stream) {\n return streamSources.has(stream);\n}\n\n// src/TonePlayoutAdapter.ts\nimport {\n clipStartTime,\n clipEndTime,\n clipOffsetTime,\n clipDurationTime\n} from \"@waveform-playlist/core\";\nimport { now as now3 } from \"tone\";\nfunction createToneAdapter(options) {\n let playout = null;\n let _isPlaying = false;\n let _playoutGeneration = 0;\n function buildPlayout(tracks) {\n if (playout) {\n playout.dispose();\n }\n _playoutGeneration++;\n const generation = _playoutGeneration;\n playout = new TonePlayout({\n effects: options?.effects\n });\n for (const track of tracks) {\n const playableClips = track.clips.filter((c) => c.audioBuffer);\n if (playableClips.length === 0) continue;\n const startTime = Math.min(...playableClips.map(clipStartTime));\n const endTime = Math.max(...playableClips.map(clipEndTime));\n const trackObj = {\n id: track.id,\n name: track.name,\n gain: track.volume,\n muted: track.muted,\n soloed: track.soloed,\n stereoPan: track.pan,\n startTime,\n endTime\n };\n const clipInfos = playableClips.map((clip) => ({\n buffer: clip.audioBuffer,\n startTime: clipStartTime(clip) - startTime,\n duration: clipDurationTime(clip),\n offset: clipOffsetTime(clip),\n fadeIn: clip.fadeIn,\n fadeOut: clip.fadeOut,\n gain: clip.gain\n }));\n playout.addTrack({\n clips: clipInfos,\n track: trackObj,\n effects: track.effects\n });\n }\n playout.applyInitialSoloState();\n playout.setOnPlaybackComplete(() => {\n if (generation === _playoutGeneration) {\n _isPlaying = false;\n }\n });\n }\n return {\n async init() {\n if (playout) {\n await playout.init();\n }\n },\n setTracks(tracks) {\n buildPlayout(tracks);\n },\n async play(startTime, endTime) {\n if (!playout) return;\n await playout.init();\n const duration = endTime !== void 0 ? endTime - startTime : void 0;\n playout.play(now3(), startTime, duration);\n _isPlaying = true;\n },\n pause() {\n playout?.pause();\n _isPlaying = false;\n },\n stop() {\n playout?.stop();\n _isPlaying = false;\n },\n seek(time) {\n playout?.seekTo(time);\n },\n getCurrentTime() {\n return playout?.getCurrentTime() ?? 0;\n },\n isPlaying() {\n return _isPlaying;\n },\n setMasterVolume(volume) {\n playout?.setMasterGain(volume);\n },\n setTrackVolume(trackId, volume) {\n playout?.getTrack(trackId)?.setVolume(volume);\n },\n setTrackMute(trackId, muted) {\n playout?.setMute(trackId, muted);\n },\n setTrackSolo(trackId, soloed) {\n playout?.setSolo(trackId, soloed);\n },\n setTrackPan(trackId, pan) {\n playout?.getTrack(trackId)?.setPan(pan);\n },\n dispose() {\n playout?.dispose();\n playout = null;\n _isPlaying = false;\n }\n };\n}\nexport {\n TonePlayout,\n ToneTrack,\n applyFadeIn,\n applyFadeOut,\n closeGlobalAudioContext,\n createToneAdapter,\n getGlobalAudioContext,\n getGlobalAudioContextState,\n getGlobalContext,\n getGlobalToneContext,\n getMediaStreamSource,\n getUnderlyingAudioParam,\n hasMediaStreamSource,\n releaseMediaStreamSource,\n resumeGlobalAudioContext\n};\n//# sourceMappingURL=index.mjs.map","import { useMemo, useLayoutEffect, useEffect, useRef, useCallback } from 'react';\n\nfunction useCombinedRefs() {\n for (var _len = arguments.length, refs = new Array(_len), _key = 0; _key < _len; _key++) {\n refs[_key] = arguments[_key];\n }\n\n return useMemo(() => node => {\n refs.forEach(ref => ref(node));\n }, // eslint-disable-next-line react-hooks/exhaustive-deps\n refs);\n}\n\n// https://github.com/facebook/react/blob/master/packages/shared/ExecutionEnvironment.js\nconst canUseDOM = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';\n\nfunction isWindow(element) {\n const elementString = Object.prototype.toString.call(element);\n return elementString === '[object Window]' || // In Electron context the Window object serializes to [object global]\n elementString === '[object global]';\n}\n\nfunction isNode(node) {\n return 'nodeType' in node;\n}\n\nfunction getWindow(target) {\n var _target$ownerDocument, _target$ownerDocument2;\n\n if (!target) {\n return window;\n }\n\n if (isWindow(target)) {\n return target;\n }\n\n if (!isNode(target)) {\n return window;\n }\n\n return (_target$ownerDocument = (_target$ownerDocument2 = target.ownerDocument) == null ? void 0 : _target$ownerDocument2.defaultView) != null ? _target$ownerDocument : window;\n}\n\nfunction isDocument(node) {\n const {\n Document\n } = getWindow(node);\n return node instanceof Document;\n}\n\nfunction isHTMLElement(node) {\n if (isWindow(node)) {\n return false;\n }\n\n return node instanceof getWindow(node).HTMLElement;\n}\n\nfunction isSVGElement(node) {\n return node instanceof getWindow(node).SVGElement;\n}\n\nfunction getOwnerDocument(target) {\n if (!target) {\n return document;\n }\n\n if (isWindow(target)) {\n return target.document;\n }\n\n if (!isNode(target)) {\n return document;\n }\n\n if (isDocument(target)) {\n return target;\n }\n\n if (isHTMLElement(target) || isSVGElement(target)) {\n return target.ownerDocument;\n }\n\n return document;\n}\n\n/**\r\n * A hook that resolves to useEffect on the server and useLayoutEffect on the client\r\n * @param callback {function} Callback function that is invoked when the dependencies of the hook change\r\n */\n\nconst useIsomorphicLayoutEffect = canUseDOM ? useLayoutEffect : useEffect;\n\nfunction useEvent(handler) {\n const handlerRef = useRef(handler);\n useIsomorphicLayoutEffect(() => {\n handlerRef.current = handler;\n });\n return useCallback(function () {\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n return handlerRef.current == null ? void 0 : handlerRef.current(...args);\n }, []);\n}\n\nfunction useInterval() {\n const intervalRef = useRef(null);\n const set = useCallback((listener, duration) => {\n intervalRef.current = setInterval(listener, duration);\n }, []);\n const clear = useCallback(() => {\n if (intervalRef.current !== null) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n }, []);\n return [set, clear];\n}\n\nfunction useLatestValue(value, dependencies) {\n if (dependencies === void 0) {\n dependencies = [value];\n }\n\n const valueRef = useRef(value);\n useIsomorphicLayoutEffect(() => {\n if (valueRef.current !== value) {\n valueRef.current = value;\n }\n }, dependencies);\n return valueRef;\n}\n\nfunction useLazyMemo(callback, dependencies) {\n const valueRef = useRef();\n return useMemo(() => {\n const newValue = callback(valueRef.current);\n valueRef.current = newValue;\n return newValue;\n }, // eslint-disable-next-line react-hooks/exhaustive-deps\n [...dependencies]);\n}\n\nfunction useNodeRef(onChange) {\n const onChangeHandler = useEvent(onChange);\n const node = useRef(null);\n const setNodeRef = useCallback(element => {\n if (element !== node.current) {\n onChangeHandler == null ? void 0 : onChangeHandler(element, node.current);\n }\n\n node.current = element;\n }, //eslint-disable-next-line\n []);\n return [node, setNodeRef];\n}\n\nfunction usePrevious(value) {\n const ref = useRef();\n useEffect(() => {\n ref.current = value;\n }, [value]);\n return ref.current;\n}\n\nlet ids = {};\nfunction useUniqueId(prefix, value) {\n return useMemo(() => {\n if (value) {\n return value;\n }\n\n const id = ids[prefix] == null ? 0 : ids[prefix] + 1;\n ids[prefix] = id;\n return prefix + \"-\" + id;\n }, [prefix, value]);\n}\n\nfunction createAdjustmentFn(modifier) {\n return function (object) {\n for (var _len = arguments.length, adjustments = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n adjustments[_key - 1] = arguments[_key];\n }\n\n return adjustments.reduce((accumulator, adjustment) => {\n const entries = Object.entries(adjustment);\n\n for (const [key, valueAdjustment] of entries) {\n const value = accumulator[key];\n\n if (value != null) {\n accumulator[key] = value + modifier * valueAdjustment;\n }\n }\n\n return accumulator;\n }, { ...object\n });\n };\n}\n\nconst add = /*#__PURE__*/createAdjustmentFn(1);\nconst subtract = /*#__PURE__*/createAdjustmentFn(-1);\n\nfunction hasViewportRelativeCoordinates(event) {\n return 'clientX' in event && 'clientY' in event;\n}\n\nfunction isKeyboardEvent(event) {\n if (!event) {\n return false;\n }\n\n const {\n KeyboardEvent\n } = getWindow(event.target);\n return KeyboardEvent && event instanceof KeyboardEvent;\n}\n\nfunction isTouchEvent(event) {\n if (!event) {\n return false;\n }\n\n const {\n TouchEvent\n } = getWindow(event.target);\n return TouchEvent && event instanceof TouchEvent;\n}\n\n/**\r\n * Returns the normalized x and y coordinates for mouse and touch events.\r\n */\n\nfunction getEventCoordinates(event) {\n if (isTouchEvent(event)) {\n if (event.touches && event.touches.length) {\n const {\n clientX: x,\n clientY: y\n } = event.touches[0];\n return {\n x,\n y\n };\n } else if (event.changedTouches && event.changedTouches.length) {\n const {\n clientX: x,\n clientY: y\n } = event.changedTouches[0];\n return {\n x,\n y\n };\n }\n }\n\n if (hasViewportRelativeCoordinates(event)) {\n return {\n x: event.clientX,\n y: event.clientY\n };\n }\n\n return null;\n}\n\nconst CSS = /*#__PURE__*/Object.freeze({\n Translate: {\n toString(transform) {\n if (!transform) {\n return;\n }\n\n const {\n x,\n y\n } = transform;\n return \"translate3d(\" + (x ? Math.round(x) : 0) + \"px, \" + (y ? Math.round(y) : 0) + \"px, 0)\";\n }\n\n },\n Scale: {\n toString(transform) {\n if (!transform) {\n return;\n }\n\n const {\n scaleX,\n scaleY\n } = transform;\n return \"scaleX(\" + scaleX + \") scaleY(\" + scaleY + \")\";\n }\n\n },\n Transform: {\n toString(transform) {\n if (!transform) {\n return;\n }\n\n return [CSS.Translate.toString(transform), CSS.Scale.toString(transform)].join(' ');\n }\n\n },\n Transition: {\n toString(_ref) {\n let {\n property,\n duration,\n easing\n } = _ref;\n return property + \" \" + duration + \"ms \" + easing;\n }\n\n }\n});\n\nconst SELECTOR = 'a,frame,iframe,input:not([type=hidden]):not(:disabled),select:not(:disabled),textarea:not(:disabled),button:not(:disabled),*[tabindex]';\nfunction findFirstFocusableNode(element) {\n if (element.matches(SELECTOR)) {\n return element;\n }\n\n return element.querySelector(SELECTOR);\n}\n\nexport { CSS, add, canUseDOM, findFirstFocusableNode, getEventCoordinates, getOwnerDocument, getWindow, hasViewportRelativeCoordinates, isDocument, isHTMLElement, isKeyboardEvent, isNode, isSVGElement, isTouchEvent, isWindow, subtract, useCombinedRefs, useEvent, useInterval, useIsomorphicLayoutEffect, useLatestValue, useLazyMemo, useNodeRef, usePrevious, useUniqueId };\n//# sourceMappingURL=utilities.esm.js.map\n","import * as e from \"react\";\nconst a = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M144,128a16,16,0,1,1-16-16A16,16,0,0,1,144,128ZM60,112a16,16,0,1,0,16,16A16,16,0,0,0,60,112Zm136,0a16,16,0,1,0,16,16A16,16,0,0,0,196,112Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\n \"path\",\n {\n d: \"M240,96v64a16,16,0,0,1-16,16H32a16,16,0,0,1-16-16V96A16,16,0,0,1,32,80H224A16,16,0,0,1,240,96Z\",\n opacity: \"0.2\"\n }\n ), /* @__PURE__ */ e.createElement(\"path\", { d: \"M140,128a12,12,0,1,1-12-12A12,12,0,0,1,140,128Zm56-12a12,12,0,1,0,12,12A12,12,0,0,0,196,116ZM60,116a12,12,0,1,0,12,12A12,12,0,0,0,60,116Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M224,80H32A16,16,0,0,0,16,96v64a16,16,0,0,0,16,16H224a16,16,0,0,0,16-16V96A16,16,0,0,0,224,80ZM60,140a12,12,0,1,1,12-12A12,12,0,0,1,60,140Zm68,0a12,12,0,1,1,12-12A12,12,0,0,1,128,140Zm68,0a12,12,0,1,1,12-12A12,12,0,0,1,196,140Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M138,128a10,10,0,1,1-10-10A10,10,0,0,1,138,128ZM60,118a10,10,0,1,0,10,10A10,10,0,0,0,60,118Zm136,0a10,10,0,1,0,10,10A10,10,0,0,0,196,118Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M140,128a12,12,0,1,1-12-12A12,12,0,0,1,140,128Zm56-12a12,12,0,1,0,12,12A12,12,0,0,0,196,116ZM60,116a12,12,0,1,0,12,12A12,12,0,0,0,60,116Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M136,128a8,8,0,1,1-8-8A8,8,0,0,1,136,128Zm-76-8a8,8,0,1,0,8,8A8,8,0,0,0,60,120Zm136,0a8,8,0,1,0,8,8A8,8,0,0,0,196,120Z\" }))\n ]\n]);\nexport {\n a as default\n};\n","import * as a from \"react\";\nconst e = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M157.27,21.22a12,12,0,0,0-12.64,1.31L75.88,76H32A20,20,0,0,0,12,96v64a20,20,0,0,0,20,20H75.88l68.75,53.47A12,12,0,0,0,164,224V32A12,12,0,0,0,157.27,21.22ZM36,100H68v56H36Zm104,99.46L92,162.13V93.87l48-37.33ZM212,128a44,44,0,0,1-11,29.11,12,12,0,1,1-18-15.88,20,20,0,0,0,0-26.43,12,12,0,0,1,18-15.86A43.94,43.94,0,0,1,212,128Zm40,0a83.87,83.87,0,0,1-21.39,56,12,12,0,0,1-17.89-16,60,60,0,0,0,0-80,12,12,0,1,1,17.88-16A83.87,83.87,0,0,1,252,128Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M80,88v80H32a8,8,0,0,1-8-8V96a8,8,0,0,1,8-8Z\", opacity: \"0.2\" }), /* @__PURE__ */ a.createElement(\"path\", { d: \"M155.51,24.81a8,8,0,0,0-8.42.88L77.25,80H32A16,16,0,0,0,16,96v64a16,16,0,0,0,16,16H77.25l69.84,54.31A8,8,0,0,0,160,224V32A8,8,0,0,0,155.51,24.81ZM32,96H72v64H32ZM144,207.64,88,164.09V91.91l56-43.55Zm54-106.08a40,40,0,0,1,0,52.88,8,8,0,0,1-12-10.58,24,24,0,0,0,0-31.72,8,8,0,0,1,12-10.58ZM248,128a79.9,79.9,0,0,1-20.37,53.34,8,8,0,0,1-11.92-10.67,64,64,0,0,0,0-85.33,8,8,0,1,1,11.92-10.67A79.83,79.83,0,0,1,248,128Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M160,32.25V223.69a8.29,8.29,0,0,1-3.91,7.18,8,8,0,0,1-9-.56l-65.57-51A4,4,0,0,1,80,176.16V79.84a4,4,0,0,1,1.55-3.15l65.57-51a8,8,0,0,1,10,.16A8.27,8.27,0,0,1,160,32.25ZM60,80H32A16,16,0,0,0,16,96v64a16,16,0,0,0,16,16H60a4,4,0,0,0,4-4V84A4,4,0,0,0,60,80Zm126.77,20.84a8,8,0,0,0-.72,11.3,24,24,0,0,1,0,31.72,8,8,0,1,0,12,10.58,40,40,0,0,0,0-52.88A8,8,0,0,0,186.74,100.84Zm40.89-26.17a8,8,0,1,0-11.92,10.66,64,64,0,0,1,0,85.34,8,8,0,1,0,11.92,10.66,80,80,0,0,0,0-106.66Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M154.64,26.61a6,6,0,0,0-6.32.65L77.94,82H32A14,14,0,0,0,18,96v64a14,14,0,0,0,14,14H77.94l70.38,54.74A6,6,0,0,0,158,224V32A6,6,0,0,0,154.64,26.61ZM30,160V96a2,2,0,0,1,2-2H74v68H32A2,2,0,0,1,30,160Zm116,51.73L86,165.07V90.93l60-46.66Zm50.53-108.85a38,38,0,0,1,0,50.24,6,6,0,1,1-9-7.94,26,26,0,0,0,0-34.37,6,6,0,0,1,9-7.93ZM246,128a77.86,77.86,0,0,1-19.86,52,6,6,0,1,1-8.94-8,66,66,0,0,0,0-88,6,6,0,1,1,8.94-8A77.86,77.86,0,0,1,246,128Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M155.51,24.81a8,8,0,0,0-8.42.88L77.25,80H32A16,16,0,0,0,16,96v64a16,16,0,0,0,16,16H77.25l69.84,54.31A8,8,0,0,0,160,224V32A8,8,0,0,0,155.51,24.81ZM32,96H72v64H32ZM144,207.64,88,164.09V91.91l56-43.55Zm54-106.08a40,40,0,0,1,0,52.88,8,8,0,0,1-12-10.58,24,24,0,0,0,0-31.72,8,8,0,0,1,12-10.58ZM248,128a79.9,79.9,0,0,1-20.37,53.34,8,8,0,0,1-11.92-10.67,64,64,0,0,0,0-85.33,8,8,0,1,1,11.92-10.67A79.83,79.83,0,0,1,248,128Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M153.76,28.41a4,4,0,0,0-4.22.43L78.63,84H32A12,12,0,0,0,20,96v64a12,12,0,0,0,12,12H78.63l70.91,55.16A4.07,4.07,0,0,0,152,228a3.92,3.92,0,0,0,1.76-.41A4,4,0,0,0,156,224V32A4,4,0,0,0,153.76,28.41ZM28,160V96a4,4,0,0,1,4-4H76v72H32A4,4,0,0,1,28,160Zm120,55.82L84,166V90l64-49.78Zm47-111.61a36,36,0,0,1,0,47.59,4,4,0,1,1-6-5.3,28,28,0,0,0,0-37,4,4,0,0,1,6-5.28ZM244,128a75.88,75.88,0,0,1-19.35,50.67,4,4,0,0,1-6-5.34,68,68,0,0,0,0-90.66,4,4,0,0,1,6-5.34A75.88,75.88,0,0,1,244,128Z\" }))\n ]\n]);\nexport {\n e as default\n};\n","import * as e from \"react\";\nconst a = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M157.27,21.22a12,12,0,0,0-12.64,1.31L75.88,76H32A20,20,0,0,0,12,96v64a20,20,0,0,0,20,20H75.88l68.75,53.47A12,12,0,0,0,164,224V32A12,12,0,0,0,157.27,21.22ZM36,100H68v56H36Zm104,99.46L92,162.13V93.87l48-37.33ZM212,128a44,44,0,0,1-11,29.11,12,12,0,0,1-18-15.88,20,20,0,0,0,0-26.44,12,12,0,0,1,18-15.85A43.94,43.94,0,0,1,212,128Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M80,88v80H32a8,8,0,0,1-8-8V96a8,8,0,0,1,8-8Z\", opacity: \"0.2\" }), /* @__PURE__ */ e.createElement(\"path\", { d: \"M155.51,24.81a8,8,0,0,0-8.42.88L77.25,80H32A16,16,0,0,0,16,96v64a16,16,0,0,0,16,16H77.25l69.84,54.31A8,8,0,0,0,160,224V32A8,8,0,0,0,155.51,24.81ZM32,96H72v64H32ZM144,207.64,88,164.09V91.91l56-43.55ZM208,128a39.93,39.93,0,0,1-10,26.46,8,8,0,0,1-12-10.58,24,24,0,0,0,0-31.72,8,8,0,1,1,12-10.58A40,40,0,0,1,208,128Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M160,32.25V223.69a8.29,8.29,0,0,1-3.91,7.18,8,8,0,0,1-9-.56l-65.57-51A4,4,0,0,1,80,176.16V79.84a4,4,0,0,1,1.55-3.15l65.57-51a8,8,0,0,1,10,.16A8.27,8.27,0,0,1,160,32.25ZM60,80H32A16,16,0,0,0,16,96v64a16,16,0,0,0,16,16H60a4,4,0,0,0,4-4V84A4,4,0,0,0,60,80ZM198,101.56a8,8,0,1,0-12,10.58,24,24,0,0,1,0,31.72,8,8,0,1,0,12,10.58,40,40,0,0,0,0-52.88Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M154.64,26.61a6,6,0,0,0-6.32.65L77.94,82H32A14,14,0,0,0,18,96v64a14,14,0,0,0,14,14H77.94l70.38,54.74A6,6,0,0,0,158,224V32A6,6,0,0,0,154.64,26.61ZM30,160V96a2,2,0,0,1,2-2H74v68H32A2,2,0,0,1,30,160Zm116,51.73L86,165.07V90.93l60-46.66ZM206,128a37.94,37.94,0,0,1-9.5,25.14,6,6,0,1,1-9-7.94,26,26,0,0,0,0-34.37,6,6,0,0,1,9-7.93A38,38,0,0,1,206,128Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M155.51,24.81a8,8,0,0,0-8.42.88L77.25,80H32A16,16,0,0,0,16,96v64a16,16,0,0,0,16,16H77.25l69.84,54.31A8,8,0,0,0,160,224V32A8,8,0,0,0,155.51,24.81ZM32,96H72v64H32ZM144,207.64,88,164.09V91.91l56-43.55ZM208,128a39.93,39.93,0,0,1-10,26.46,8,8,0,0,1-12-10.58,24,24,0,0,0,0-31.72,8,8,0,1,1,12-10.58A40,40,0,0,1,208,128Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M153.76,28.41a4,4,0,0,0-4.22.43L78.63,84H32A12,12,0,0,0,20,96v64a12,12,0,0,0,12,12H78.63l70.91,55.16A4.07,4.07,0,0,0,152,228a3.92,3.92,0,0,0,1.76-.41A4,4,0,0,0,156,224V32A4,4,0,0,0,153.76,28.41ZM28,160V96a4,4,0,0,1,4-4H76v72H32A4,4,0,0,1,28,160Zm120,55.82L84,166V90l64-49.78ZM204,128a36,36,0,0,1-9,23.82,4,4,0,1,1-6-5.3,28,28,0,0,0,0-37,4,4,0,0,1,6-5.28A36,36,0,0,1,204,128Z\" }))\n ]\n]);\nexport {\n a as default\n};\n","import * as e from \"react\";\nconst a = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M208.49,191.51a12,12,0,0,1-17,17L128,145,64.49,208.49a12,12,0,0,1-17-17L111,128,47.51,64.49a12,12,0,0,1,17-17L128,111l63.51-63.52a12,12,0,0,1,17,17L145,128Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\n \"path\",\n {\n d: \"M216,56V200a16,16,0,0,1-16,16H56a16,16,0,0,1-16-16V56A16,16,0,0,1,56,40H200A16,16,0,0,1,216,56Z\",\n opacity: \"0.2\"\n }\n ), /* @__PURE__ */ e.createElement(\"path\", { d: \"M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M208,32H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM181.66,170.34a8,8,0,0,1-11.32,11.32L128,139.31,85.66,181.66a8,8,0,0,1-11.32-11.32L116.69,128,74.34,85.66A8,8,0,0,1,85.66,74.34L128,116.69l42.34-42.35a8,8,0,0,1,11.32,11.32L139.31,128Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M204.24,195.76a6,6,0,1,1-8.48,8.48L128,136.49,60.24,204.24a6,6,0,0,1-8.48-8.48L119.51,128,51.76,60.24a6,6,0,0,1,8.48-8.48L128,119.51l67.76-67.75a6,6,0,0,1,8.48,8.48L136.49,128Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M202.83,197.17a4,4,0,0,1-5.66,5.66L128,133.66,58.83,202.83a4,4,0,0,1-5.66-5.66L122.34,128,53.17,58.83a4,4,0,0,1,5.66-5.66L128,122.34l69.17-69.17a4,4,0,1,1,5.66,5.66L133.66,128Z\" }))\n ]\n]);\nexport {\n a as default\n};\n","import { createContext as r } from \"react\";\nconst o = r({\n color: \"currentColor\",\n size: \"1em\",\n weight: \"regular\",\n mirrored: !1\n});\nexport {\n o as IconContext\n};\n","import * as e from \"react\";\nimport { IconContext as h } from \"./context.es.js\";\nconst p = e.forwardRef(\n (s, a) => {\n const {\n alt: n,\n color: r,\n size: t,\n weight: o,\n mirrored: c,\n children: i,\n weights: m,\n ...x\n } = s, {\n color: d = \"currentColor\",\n size: l,\n weight: f = \"regular\",\n mirrored: g = !1,\n ...w\n } = e.useContext(h);\n return /* @__PURE__ */ e.createElement(\n \"svg\",\n {\n ref: a,\n xmlns: \"http://www.w3.org/2000/svg\",\n width: t != null ? t : l,\n height: t != null ? t : l,\n fill: r != null ? r : d,\n viewBox: \"0 0 256 256\",\n transform: c || g ? \"scale(-1, 1)\" : void 0,\n ...w,\n ...x\n },\n !!n && /* @__PURE__ */ e.createElement(\"title\", null, n),\n i,\n m.get(o != null ? o : f)\n );\n }\n);\np.displayName = \"IconBase\";\nexport {\n p as default\n};\n","import * as e from \"react\";\nimport s from \"../lib/IconBase.es.js\";\nimport a from \"../defs/DotsThree.es.js\";\nconst o = e.forwardRef((r, t) => /* @__PURE__ */ e.createElement(s, { ref: t, ...r, weights: a }));\no.displayName = \"DotsThreeIcon\";\nconst n = o;\nexport {\n n as DotsThree,\n o as DotsThreeIcon\n};\n","import * as e from \"react\";\nimport t from \"../lib/IconBase.es.js\";\nimport i from \"../defs/SpeakerHigh.es.js\";\nconst o = e.forwardRef((r, a) => /* @__PURE__ */ e.createElement(t, { ref: a, ...r, weights: i }));\no.displayName = \"SpeakerHighIcon\";\nconst c = o;\nexport {\n c as SpeakerHigh,\n o as SpeakerHighIcon\n};\n","import * as e from \"react\";\nimport t from \"../lib/IconBase.es.js\";\nimport m from \"../defs/SpeakerLow.es.js\";\nconst o = e.forwardRef((r, a) => /* @__PURE__ */ e.createElement(t, { ref: a, ...r, weights: m }));\no.displayName = \"SpeakerLowIcon\";\nconst s = o;\nexport {\n s as SpeakerLow,\n o as SpeakerLowIcon\n};\n","import * as o from \"react\";\nimport a from \"../lib/IconBase.es.js\";\nimport m from \"../defs/X.es.js\";\nconst e = o.forwardRef((r, t) => /* @__PURE__ */ o.createElement(a, { ref: t, ...r, weights: m }));\ne.displayName = \"XIcon\";\nconst n = e;\nexport {\n n as X,\n e as XIcon\n};\n","// src/components/AudioPosition.tsx\nimport styled from \"styled-components\";\nimport { jsx } from \"react/jsx-runtime\";\nvar 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`;\nvar AudioPosition = ({ formattedTime, className }) => {\n return /* @__PURE__ */ jsx(PositionDisplay, { className, \"aria-label\": \"Audio position\", children: formattedTime });\n};\n\n// src/styled/BaseButton.tsx\nimport styled2 from \"styled-components\";\nvar BaseButton = styled2.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:\n background-color 0.15s ease-in-out,\n 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`;\nvar BaseButtonSmall = styled2(BaseButton)`\n padding: 0.25rem 0.5rem;\n font-size: ${(props) => props.theme.fontSizeSmall};\n`;\nvar IconButton = styled2(BaseButton)`\n padding: 0.5rem;\n min-width: 2.25rem;\n min-height: 2.25rem;\n`;\nvar IconButtonSmall = styled2(BaseButton)`\n padding: 0.25rem;\n min-width: 1.75rem;\n min-height: 1.75rem;\n font-size: ${(props) => props.theme.fontSizeSmall};\n`;\n\n// src/styled/BaseCheckbox.tsx\nimport styled3 from \"styled-components\";\nvar BaseCheckboxWrapper = styled3.div`\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n`;\nvar BaseCheckbox = styled3.input`\n cursor: pointer;\n accent-color: ${(props) => props.theme.inputFocusBorder};\n\n &:disabled {\n cursor: not-allowed;\n }\n`;\nvar BaseCheckboxLabel = styled3.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\n// src/styled/BaseControlButton.tsx\nimport styled4 from \"styled-components\";\nvar BaseControlButton = styled4.button`\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\n// src/styled/BaseInput.tsx\nimport styled5 from \"styled-components\";\nvar BaseInput = styled5.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:\n border-color 0.15s ease-in-out,\n 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`;\nvar BaseInputSmall = styled5(BaseInput)`\n padding: 0.25rem 0.5rem;\n font-size: ${(props) => props.theme.fontSizeSmall};\n`;\n\n// src/styled/BaseLabel.tsx\nimport styled6 from \"styled-components\";\nvar BaseLabel = styled6.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`;\nvar InlineLabel = styled6.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`;\nvar ScreenReaderOnly = styled6.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\n// src/styled/BaseSelect.tsx\nimport styled7 from \"styled-components\";\nvar BaseSelect = styled7.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:\n border-color 0.15s ease-in-out,\n 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`;\nvar BaseSelectSmall = styled7(BaseSelect)`\n padding: 0.25rem 1.75rem 0.25rem 0.5rem;\n font-size: ${(props) => props.theme.fontSizeSmall};\n`;\n\n// src/styled/BaseSlider.tsx\nimport styled8 from \"styled-components\";\nvar BaseSlider = styled8.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:\n transform 0.15s ease,\n 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:\n transform 0.15s ease,\n 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\n// src/components/AutomaticScrollCheckbox.tsx\nimport { jsx as jsx2, jsxs } from \"react/jsx-runtime\";\nvar AutomaticScrollCheckbox = ({\n checked,\n onChange,\n disabled = false,\n className\n}) => {\n const handleChange = (e) => {\n onChange(e.target.checked);\n };\n return /* @__PURE__ */ jsxs(BaseCheckboxWrapper, { className, children: [\n /* @__PURE__ */ jsx2(\n BaseCheckbox,\n {\n type: \"checkbox\",\n id: \"automatic-scroll\",\n className: \"automatic-scroll\",\n checked,\n onChange: handleChange,\n disabled\n }\n ),\n /* @__PURE__ */ jsx2(BaseCheckboxLabel, { htmlFor: \"automatic-scroll\", children: \"Automatic Scroll\" })\n ] });\n};\n\n// src/components/Channel.tsx\nimport { useLayoutEffect } from \"react\";\nimport styled9 from \"styled-components\";\n\n// src/wfpl-theme.ts\nfunction isWaveformGradient(color) {\n return typeof color === \"object\" && color !== null && \"type\" in color;\n}\nfunction waveformColorToCss(color) {\n if (!isWaveformGradient(color)) {\n return color;\n }\n const direction = color.direction === \"vertical\" ? \"to bottom\" : \"to right\";\n const stops = color.stops.map((stop) => `${stop.color} ${stop.offset * 100}%`).join(\", \");\n return `linear-gradient(${direction}, ${stops})`;\n}\nvar defaultTheme = {\n waveformDrawMode: \"inverted\",\n waveOutlineColor: \"#ffffff\",\n waveFillColor: \"#1a7f8e\",\n // White background for crisp look\n waveProgressColor: \"rgba(0, 0, 0, 0.10)\",\n // Subtle dark overlay for light mode\n selectedWaveOutlineColor: \"#ffffff\",\n selectedWaveFillColor: \"#00b4d8\",\n // Selected: brighter cyan\n selectedTrackControlsBackground: \"#d9e9ff\",\n // Light blue background for selected track controls\n timeColor: \"#000\",\n timescaleBackgroundColor: \"#fff\",\n playheadColor: \"#f00\",\n selectionColor: \"rgba(255, 105, 180, 0.7)\",\n // hot pink - high contrast on light backgrounds\n loopRegionColor: \"rgba(59, 130, 246, 0.3)\",\n // Blue - distinct from pink selection\n loopMarkerColor: \"#3b82f6\",\n // 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\",\n // Brighter blue for selected track clip headers\n // Fade overlay colors\n fadeOverlayColor: \"rgba(0, 0, 0, 0.4)\",\n // Semi-transparent overlay for fade regions\n // UI component colors\n backgroundColor: \"#ffffff\",\n surfaceColor: \"#f5f5f5\",\n borderColor: \"#ddd\",\n textColor: \"#333\",\n textColorMuted: \"#666\",\n // Interactive element colors\n inputBackground: \"#ffffff\",\n inputBorder: \"#ccc\",\n inputText: \"#333\",\n inputPlaceholder: \"#999\",\n inputFocusBorder: \"#0066cc\",\n // Button colors - blue to match common UI patterns\n buttonBackground: \"#0091ff\",\n buttonText: \"#ffffff\",\n buttonBorder: \"#0081e6\",\n buttonHoverBackground: \"#0081e6\",\n // Slider colors\n sliderTrackColor: \"#ddd\",\n sliderThumbColor: \"#daa520\",\n // goldenrod\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 // 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};\nvar darkTheme = {\n // Normal mode: waveOutlineColor = bars, waveFillColor = background\n waveformDrawMode: \"inverted\",\n // Dark bars on warm amber background\n waveOutlineColor: \"#c49a6c\",\n // Solid warm amber for background\n waveFillColor: \"#1a1612\",\n // Very dark warm brown for bars\n waveProgressColor: \"rgba(100, 70, 40, 0.6)\",\n // Warm brown progress overlay\n // Selected: slightly lighter bars on brighter amber background\n selectedWaveFillColor: \"#241c14\",\n // Slightly lighter warm brown bars when selected\n selectedWaveOutlineColor: \"#e8c090\",\n // Brighter amber background when selected\n selectedTrackControlsBackground: \"#2a2218\",\n // Dark warm brown for selected track controls\n timeColor: \"#d8c0a8\",\n // Warm amber for timescale text\n timescaleBackgroundColor: \"#1a1612\",\n // Dark warm brown background\n playheadColor: \"#3a8838\",\n // Darker Ampelmännchen green playhead\n selectionColor: \"rgba(60, 140, 58, 0.6)\",\n // Darker Ampelmännchen green selection - visible on dark backgrounds\n loopRegionColor: \"rgba(96, 165, 250, 0.35)\",\n // Light blue - distinct from green selection\n loopMarkerColor: \"#60a5fa\",\n // Light blue marker triangles\n clipHeaderBackgroundColor: \"rgba(20, 16, 12, 0.85)\",\n // Dark background for clip headers\n clipHeaderBorderColor: \"rgba(200, 160, 120, 0.25)\",\n clipHeaderTextColor: \"#d8c0a8\",\n // Warm amber text\n clipHeaderFontFamily: \"inherit\",\n selectedClipHeaderBackgroundColor: \"#3a2c20\",\n // Darker warm brown for selected clip headers\n // Fade overlay colors\n fadeOverlayColor: \"rgba(200, 100, 80, 0.5)\",\n // Warm red-orange overlay visible on dark backgrounds\n // UI component colors\n backgroundColor: \"#1e1e1e\",\n surfaceColor: \"#2d2d2d\",\n borderColor: \"#444\",\n textColor: \"#e0e0e0\",\n textColorMuted: \"#999\",\n // Interactive element colors\n inputBackground: \"#2d2d2d\",\n inputBorder: \"#555\",\n inputText: \"#e0e0e0\",\n inputPlaceholder: \"#777\",\n inputFocusBorder: \"#4A9EFF\",\n // Button colors - Ampelmännchen green (#63C75F) with black text\n buttonBackground: \"#63C75F\",\n buttonText: \"#0a0a0f\",\n buttonBorder: \"#52b84e\",\n buttonHoverBackground: \"#78d074\",\n // Slider colors\n sliderTrackColor: \"#555\",\n sliderThumbColor: \"#f0c040\",\n // brighter goldenrod for dark mode\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 // 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\n// src/contexts/ScrollViewport.tsx\nimport {\n createContext,\n useContext,\n useEffect,\n useCallback,\n useMemo,\n useRef,\n useSyncExternalStore\n} from \"react\";\nimport { jsx as jsx3 } from \"react/jsx-runtime\";\nvar ViewportStore = class {\n constructor() {\n this._state = null;\n this._listeners = /* @__PURE__ */ new Set();\n this.subscribe = (callback) => {\n this._listeners.add(callback);\n return () => this._listeners.delete(callback);\n };\n this.getSnapshot = () => this._state;\n }\n /**\n * Update viewport state. Applies a 100px scroll threshold to skip updates\n * that don't affect chunk visibility (1000px chunks with 1.5× overscan buffer).\n * Only notifies listeners when the state actually changes.\n */\n update(scrollLeft, containerWidth) {\n const buffer = containerWidth * 1.5;\n const visibleStart = Math.max(0, scrollLeft - buffer);\n const visibleEnd = scrollLeft + containerWidth + buffer;\n if (this._state && this._state.containerWidth === containerWidth && Math.abs(this._state.scrollLeft - scrollLeft) < 100) {\n return;\n }\n this._state = { scrollLeft, containerWidth, visibleStart, visibleEnd };\n for (const listener of this._listeners) {\n listener();\n }\n }\n};\nvar ViewportStoreContext = createContext(null);\nvar EMPTY_SUBSCRIBE = () => () => {\n};\nvar NULL_SNAPSHOT = () => null;\nvar ScrollViewportProvider = ({ containerRef, children }) => {\n const storeRef = useRef(null);\n if (storeRef.current === null) {\n storeRef.current = new ViewportStore();\n }\n const store = storeRef.current;\n const rafIdRef = useRef(null);\n const measure = useCallback(() => {\n const el = containerRef.current;\n if (!el) return;\n store.update(el.scrollLeft, el.clientWidth);\n }, [containerRef, store]);\n const scheduleUpdate = useCallback(() => {\n if (rafIdRef.current !== null) return;\n rafIdRef.current = requestAnimationFrame(() => {\n rafIdRef.current = null;\n measure();\n });\n }, [measure]);\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n measure();\n el.addEventListener(\"scroll\", scheduleUpdate, { passive: true });\n const resizeObserver = new ResizeObserver(() => {\n scheduleUpdate();\n });\n resizeObserver.observe(el);\n return () => {\n el.removeEventListener(\"scroll\", scheduleUpdate);\n resizeObserver.disconnect();\n if (rafIdRef.current !== null) {\n cancelAnimationFrame(rafIdRef.current);\n rafIdRef.current = null;\n }\n };\n }, [containerRef, measure, scheduleUpdate]);\n return /* @__PURE__ */ jsx3(ViewportStoreContext.Provider, { value: store, children });\n};\nvar useScrollViewport = () => {\n const store = useContext(ViewportStoreContext);\n return useSyncExternalStore(\n store ? store.subscribe : EMPTY_SUBSCRIBE,\n store ? store.getSnapshot : NULL_SNAPSHOT,\n NULL_SNAPSHOT\n );\n};\nfunction useScrollViewportSelector(selector) {\n const store = useContext(ViewportStoreContext);\n return useSyncExternalStore(\n store ? store.subscribe : EMPTY_SUBSCRIBE,\n () => selector(store ? store.getSnapshot() : null),\n () => selector(null)\n );\n}\nfunction useVisibleChunkIndices(totalWidth, chunkWidth, originX = 0) {\n const visibleChunkKey = useScrollViewportSelector((viewport) => {\n const totalChunks = Math.ceil(totalWidth / chunkWidth);\n const indices = [];\n for (let i = 0; i < totalChunks; i++) {\n const chunkLeft = i * chunkWidth;\n const thisChunkWidth = Math.min(totalWidth - chunkLeft, chunkWidth);\n if (viewport) {\n const chunkLeftGlobal = originX + chunkLeft;\n const chunkEndGlobal = chunkLeftGlobal + thisChunkWidth;\n if (chunkEndGlobal <= viewport.visibleStart || chunkLeftGlobal >= viewport.visibleEnd) {\n continue;\n }\n }\n indices.push(i);\n }\n return indices.join(\",\");\n });\n return useMemo(\n () => visibleChunkKey ? visibleChunkKey.split(\",\").map(Number) : [],\n [visibleChunkKey]\n );\n}\n\n// src/contexts/ClipViewportOrigin.tsx\nimport { createContext as createContext2, useContext as useContext2 } from \"react\";\nimport { jsx as jsx4 } from \"react/jsx-runtime\";\nvar ClipViewportOriginContext = createContext2(0);\nvar ClipViewportOriginProvider = ({\n originX,\n children\n}) => /* @__PURE__ */ jsx4(ClipViewportOriginContext.Provider, { value: originX, children });\nvar useClipViewportOrigin = () => useContext2(ClipViewportOriginContext);\n\n// src/hooks/useChunkedCanvasRefs.ts\nimport { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2 } from \"react\";\nfunction useChunkedCanvasRefs() {\n const canvasMapRef = useRef2(/* @__PURE__ */ new Map());\n const canvasRef = useCallback2((canvas) => {\n if (canvas !== null) {\n const idx = parseInt(canvas.dataset.index, 10);\n canvasMapRef.current.set(idx, canvas);\n }\n }, []);\n useEffect2(() => {\n const map = canvasMapRef.current;\n for (const [idx, canvas] of map.entries()) {\n if (!canvas.isConnected) {\n map.delete(idx);\n }\n }\n });\n return { canvasRef, canvasMapRef };\n}\n\n// src/components/Channel.tsx\nimport { MAX_CANVAS_WIDTH } from \"@waveform-playlist/core\";\nimport { jsx as jsx5 } from \"react/jsx-runtime\";\nfunction createCanvasFillStyle(ctx, color, width, height) {\n if (!isWaveformGradient(color)) {\n return color;\n }\n let gradient;\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 for (const stop of color.stops) {\n gradient.addColorStop(stop.offset, stop.color);\n }\n return gradient;\n}\nvar Waveform = styled9.canvas.attrs((props) => ({\n style: {\n width: `${props.$cssWidth}px`,\n height: `${props.$waveHeight}px`,\n left: `${props.$left}px`\n }\n}))`\n position: absolute;\n top: 0;\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`;\nvar Wrapper = styled9.div.attrs((props) => ({\n style: {\n top: `${props.$waveHeight * props.$index}px`,\n width: `${props.$cssWidth}px`,\n height: `${props.$waveHeight}px`\n }\n}))`\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`;\nvar Channel = (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 { canvasRef, canvasMapRef } = useChunkedCanvasRefs();\n const clipOriginX = useClipViewportOrigin();\n const visibleChunkIndices = useVisibleChunkIndices(length, MAX_CANVAS_WIDTH, clipOriginX);\n useLayoutEffect(() => {\n const step = barWidth + barGap;\n for (const [canvasIdx, canvas] of canvasMapRef.current.entries()) {\n const globalPixelOffset = canvasIdx * MAX_CANVAS_WIDTH;\n const ctx = canvas.getContext(\"2d\");\n const h2 = Math.floor(waveHeight / 2);\n const maxValue = 2 ** (bits - 1);\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 const canvasWidth = canvas.width / devicePixelRatio;\n let fillColor;\n if (drawMode === \"normal\") {\n fillColor = waveFillColor;\n } else {\n fillColor = waveOutlineColor;\n }\n ctx.fillStyle = createCanvasFillStyle(ctx, fillColor, canvasWidth, waveHeight);\n const canvasStartGlobal = globalPixelOffset;\n const canvasEndGlobal = globalPixelOffset + canvasWidth;\n const firstBarGlobal = Math.floor((canvasStartGlobal - barWidth + step) / step) * step;\n for (let barGlobal = Math.max(0, firstBarGlobal); barGlobal < canvasEndGlobal; barGlobal += step) {\n const x = barGlobal - canvasStartGlobal;\n if (x + barWidth <= 0) continue;\n const peakIndex = barGlobal;\n if (peakIndex * 2 + 1 < data.length) {\n const minPeak = data[peakIndex * 2] / maxValue;\n const maxPeak = data[peakIndex * 2 + 1] / maxValue;\n const min = Math.abs(minPeak * h2);\n const max = Math.abs(maxPeak * h2);\n if (drawMode === \"normal\") {\n ctx.fillRect(x, h2 - max, barWidth, max + min);\n } else {\n ctx.fillRect(x, 0, barWidth, h2 - max);\n ctx.fillRect(x, h2 + min, barWidth, h2 - min);\n }\n }\n }\n }\n }\n }, [\n canvasMapRef,\n data,\n bits,\n waveHeight,\n waveOutlineColor,\n waveFillColor,\n devicePixelRatio,\n length,\n barWidth,\n barGap,\n drawMode,\n visibleChunkIndices\n ]);\n const waveforms = visibleChunkIndices.map((i) => {\n const chunkLeft = i * MAX_CANVAS_WIDTH;\n const currentWidth = Math.min(length - chunkLeft, MAX_CANVAS_WIDTH);\n return /* @__PURE__ */ jsx5(\n Waveform,\n {\n $cssWidth: currentWidth,\n $left: chunkLeft,\n width: currentWidth * devicePixelRatio,\n height: waveHeight * devicePixelRatio,\n $waveHeight: waveHeight,\n \"data-index\": i,\n ref: canvasRef\n },\n `${length}-${i}`\n );\n });\n const bgColor = waveFillColor;\n const backgroundCss = transparentBackground ? \"transparent\" : waveformColorToCss(bgColor);\n return /* @__PURE__ */ jsx5(\n Wrapper,\n {\n $index: index,\n $cssWidth: length,\n className,\n $waveHeight: waveHeight,\n $waveFillColor: backgroundCss,\n children: waveforms\n }\n );\n};\n\n// src/components/ErrorBoundary.tsx\nimport React4 from \"react\";\nimport { jsx as jsx6 } from \"react/jsx-runtime\";\nvar errorContainerStyle = {\n padding: \"16px\",\n background: \"#1a1a2e\",\n color: \"#e0e0e0\",\n border: \"1px solid #d08070\",\n borderRadius: \"4px\",\n fontFamily: \"monospace\",\n fontSize: \"13px\",\n minHeight: \"60px\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\"\n};\nvar PlaylistErrorBoundary = class extends React4.Component {\n constructor(props) {\n super(props);\n this.state = { hasError: false, error: null };\n }\n static getDerivedStateFromError(error) {\n return { hasError: true, error };\n }\n componentDidCatch(error, errorInfo) {\n console.error(\"[waveform-playlist] Render error:\", error, errorInfo.componentStack);\n }\n render() {\n if (this.state.hasError) {\n if (this.props.fallback) {\n return this.props.fallback;\n }\n return /* @__PURE__ */ jsx6(\"div\", { style: errorContainerStyle, children: \"Waveform playlist encountered an error. Check console for details.\" });\n }\n return this.props.children;\n }\n};\n\n// src/components/Clip.tsx\nimport styled13 from \"styled-components\";\nimport { useDraggable } from \"@dnd-kit/core\";\nimport { CSS } from \"@dnd-kit/utilities\";\n\n// src/components/ClipHeader.tsx\nimport styled10 from \"styled-components\";\nimport { jsx as jsx7 } from \"react/jsx-runtime\";\nvar CLIP_HEADER_HEIGHT = 22;\nvar HeaderContainer = styled10.div`\n position: relative;\n height: ${CLIP_HEADER_HEIGHT}px;\n background: ${(props) => props.$isSelected ? props.theme.selectedClipHeaderBackgroundColor : 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`;\nvar TrackName = styled10.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`;\nvar ClipHeaderPresentational = ({\n trackName,\n isSelected = false\n}) => {\n return /* @__PURE__ */ jsx7(HeaderContainer, { $isDragging: false, $interactive: false, $isSelected: isSelected, children: /* @__PURE__ */ jsx7(TrackName, { children: trackName }) });\n};\nvar ClipHeader = ({\n clipId,\n trackIndex: _trackIndex,\n clipIndex: _clipIndex,\n trackName,\n isSelected = false,\n disableDrag = false,\n dragHandleProps\n}) => {\n if (disableDrag || !dragHandleProps) {\n return /* @__PURE__ */ jsx7(ClipHeaderPresentational, { trackName, isSelected });\n }\n const { attributes, listeners, setActivatorNodeRef } = dragHandleProps;\n return /* @__PURE__ */ jsx7(\n HeaderContainer,\n {\n ref: setActivatorNodeRef,\n \"data-clip-id\": clipId,\n $interactive: true,\n $isSelected: isSelected,\n ...listeners,\n ...attributes,\n children: /* @__PURE__ */ jsx7(TrackName, { children: trackName })\n }\n );\n};\n\n// src/components/ClipBoundary.tsx\nimport React5 from \"react\";\nimport styled11 from \"styled-components\";\nimport { jsx as jsx8 } from \"react/jsx-runtime\";\nvar CLIP_BOUNDARY_WIDTH = 8;\nvar CLIP_BOUNDARY_WIDTH_TOUCH = 24;\nvar BoundaryContainer = styled11.div`\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) => props.$isDragging ? \"rgba(255, 255, 255, 0.4)\" : props.$isHovered ? \"rgba(255, 255, 255, 0.2)\" : \"transparent\"};\n\n ${(props) => props.$edge === \"left\" ? `border-left: 2px solid ${props.$isDragging ? \"rgba(255, 255, 255, 0.8)\" : props.$isHovered ? \"rgba(255, 255, 255, 0.5)\" : \"transparent\"};` : `border-right: 2px solid ${props.$isDragging ? \"rgba(255, 255, 255, 0.8)\" : props.$isHovered ? \"rgba(255, 255, 255, 0.5)\" : \"transparent\"};`}\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\" ? \"border-left: 2px solid rgba(255, 255, 255, 0.5);\" : \"border-right: 2px solid rgba(255, 255, 255, 0.5);\"}\n }\n\n &:active {\n background: rgba(255, 255, 255, 0.4);\n ${(props) => props.$edge === \"left\" ? \"border-left: 2px solid rgba(255, 255, 255, 0.8);\" : \"border-right: 2px solid rgba(255, 255, 255, 0.8);\"}\n }\n`;\nvar ClipBoundary = ({\n clipId,\n trackIndex: _trackIndex,\n clipIndex: _clipIndex,\n edge,\n dragHandleProps,\n touchOptimized = false\n}) => {\n const [isHovered, setIsHovered] = React5.useState(false);\n if (!dragHandleProps) {\n return null;\n }\n const { attributes, listeners, setActivatorNodeRef, isDragging } = dragHandleProps;\n return /* @__PURE__ */ jsx8(\n BoundaryContainer,\n {\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\n// src/components/FadeOverlay.tsx\nimport styled12, { useTheme } from \"styled-components\";\nimport { jsx as jsx9 } from \"react/jsx-runtime\";\nvar FadeContainer = styled12.div.attrs((props) => ({\n style: {\n left: `${props.$left}px`,\n width: `${props.$width}px`\n }\n}))`\n position: absolute;\n top: 0;\n bottom: 0;\n pointer-events: none;\n z-index: 50;\n`;\nvar FadeSvg = styled12.svg`\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`;\nfunction generateFadePath(width, height, curveType = \"logarithmic\") {\n const points = [];\n const numPoints = Math.max(20, Math.min(width, 100));\n for (let i = 0; i <= numPoints; i++) {\n const x = i / numPoints * width;\n const progress = i / numPoints;\n let curvedProgress;\n switch (curveType) {\n case \"linear\":\n curvedProgress = progress;\n break;\n case \"exponential\":\n curvedProgress = progress * progress;\n break;\n case \"sCurve\":\n curvedProgress = (1 - Math.cos(progress * Math.PI)) / 2;\n break;\n case \"logarithmic\":\n default:\n curvedProgress = Math.log10(1 + progress * 9) / Math.log10(10);\n break;\n }\n const y = (1 - curvedProgress) * height;\n points.push(`${x},${y}`);\n }\n return `M 0,${height} L ${points.join(\" L \")} L ${width},0 L 0,0 Z`;\n}\nvar FadeOverlay = ({\n left,\n width,\n type,\n curveType = \"logarithmic\",\n color\n}) => {\n const theme = useTheme();\n if (width < 1) return null;\n const fillColor = color || theme?.fadeOverlayColor || \"rgba(0, 0, 0, 0.4)\";\n return /* @__PURE__ */ jsx9(FadeContainer, { $left: left, $width: width, $type: type, children: /* @__PURE__ */ jsx9(FadeSvg, { $type: type, viewBox: `0 0 ${width} 100`, preserveAspectRatio: \"none\", children: /* @__PURE__ */ jsx9(\"path\", { d: generateFadePath(width, 100, curveType), fill: fillColor }) }) });\n};\n\n// src/components/Clip.tsx\nimport { Fragment, jsx as jsx10, jsxs as jsxs2 } from \"react/jsx-runtime\";\nvar ClipContainer = styled13.div.attrs((props) => ({\n style: props.$isOverlay ? {} : {\n left: `${props.$left}px`,\n width: `${props.$width}px`\n }\n}))`\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`;\nvar ChannelsWrapper = styled13.div`\n flex: 1;\n position: relative;\n overflow: ${(props) => props.$isOverlay ? \"visible\" : \"hidden\"};\n`;\nvar Clip = ({\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 const left = Math.floor(startSample / samplesPerPixel);\n const endPixel = Math.floor((startSample + durationSamples) / samplesPerPixel);\n const width = endPixel - left;\n const enableDrag = showHeader && !disableHeaderDrag && !isOverlay;\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 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 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 const style = transform ? {\n transform: CSS.Translate.toString(transform),\n zIndex: isDragging ? 100 : void 0\n // Below controls (z-index: 999) but above other clips\n } : void 0;\n return /* @__PURE__ */ jsxs2(\n ClipContainer,\n {\n ref: setNodeRef,\n style,\n className,\n $left: left,\n $width: width,\n $isOverlay: isOverlay,\n \"data-clip-container\": \"true\",\n \"data-track-id\": trackId,\n onMouseDown,\n children: [\n showHeader && /* @__PURE__ */ jsx10(\n ClipHeader,\n {\n clipId,\n trackIndex,\n clipIndex,\n trackName,\n isSelected,\n disableDrag: disableHeaderDrag,\n dragHandleProps: enableDrag ? { attributes, listeners, setActivatorNodeRef } : void 0\n }\n ),\n /* @__PURE__ */ jsx10(ClipViewportOriginProvider, { originX: left, children: /* @__PURE__ */ jsxs2(ChannelsWrapper, { $isOverlay: isOverlay, children: [\n children,\n showFades && fadeIn && fadeIn.duration > 0 && /* @__PURE__ */ jsx10(\n FadeOverlay,\n {\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 && /* @__PURE__ */ jsx10(\n FadeOverlay,\n {\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 ] }) }),\n showHeader && !disableHeaderDrag && !isOverlay && /* @__PURE__ */ jsxs2(Fragment, { children: [\n /* @__PURE__ */ jsx10(\n ClipBoundary,\n {\n clipId,\n trackIndex,\n clipIndex,\n edge: \"left\",\n touchOptimized,\n dragHandleProps: {\n attributes: leftBoundaryAttributes,\n listeners: leftBoundaryListeners,\n setActivatorNodeRef: setLeftBoundaryActivatorRef,\n isDragging: isLeftBoundaryDragging\n }\n }\n ),\n /* @__PURE__ */ jsx10(\n ClipBoundary,\n {\n clipId,\n trackIndex,\n clipIndex,\n edge: \"right\",\n touchOptimized,\n dragHandleProps: {\n attributes: rightBoundaryAttributes,\n listeners: rightBoundaryListeners,\n setActivatorNodeRef: setRightBoundaryActivatorRef,\n isDragging: isRightBoundaryDragging\n }\n }\n )\n ] })\n ]\n }\n );\n};\n\n// src/components/MasterVolumeControl.tsx\nimport styled14 from \"styled-components\";\nimport { jsx as jsx11, jsxs as jsxs3 } from \"react/jsx-runtime\";\nvar VolumeContainer = styled14.div`\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n`;\nvar VolumeLabel = styled14(BaseLabel)`\n margin: 0;\n white-space: nowrap;\n`;\nvar VolumeSlider = styled14(BaseSlider)`\n width: 120px;\n`;\nvar MasterVolumeControl = ({\n volume,\n onChange,\n disabled = false,\n className\n}) => {\n const handleChange = (e) => {\n onChange(parseFloat(e.target.value) / 100);\n };\n return /* @__PURE__ */ jsxs3(VolumeContainer, { className, children: [\n /* @__PURE__ */ jsx11(VolumeLabel, { htmlFor: \"master-gain\", children: \"Master Volume\" }),\n /* @__PURE__ */ jsx11(\n VolumeSlider,\n {\n min: \"0\",\n max: \"100\",\n value: volume * 100,\n onChange: handleChange,\n disabled,\n id: \"master-gain\"\n }\n )\n ] });\n};\n\n// src/components/Playhead.tsx\nimport { useRef as useRef3, useEffect as useEffect3 } from \"react\";\nimport styled15 from \"styled-components\";\nimport { jsx as jsx12, jsxs as jsxs4 } from \"react/jsx-runtime\";\nvar PlayheadLine = styled15.div.attrs((props) => ({\n style: {\n transform: `translate3d(${props.$position}px, 0, 0)`\n }\n}))`\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`;\nvar Playhead = ({ position, color = \"#ff0000\" }) => {\n return /* @__PURE__ */ jsx12(PlayheadLine, { $position: position, $color: color });\n};\nvar PlayheadWithMarkerContainer = styled15.div`\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`;\nvar MarkerTriangle = styled15.div`\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`;\nvar MarkerLine = styled15.div`\n position: absolute;\n top: 0;\n left: 0;\n width: 2px;\n height: 100%;\n background: ${(props) => props.$color};\n`;\nvar PlayheadWithMarker = ({\n color = \"#ff0000\",\n isPlaying,\n currentTimeRef,\n playbackStartTimeRef,\n audioStartPositionRef,\n samplesPerPixel,\n sampleRate,\n controlsOffset,\n getAudioContextTime\n}) => {\n const containerRef = useRef3(null);\n const animationFrameRef = useRef3(null);\n useEffect3(() => {\n const updatePosition = () => {\n if (containerRef.current) {\n let time;\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 if (isPlaying) {\n animationFrameRef.current = requestAnimationFrame(updatePosition);\n }\n };\n if (isPlaying) {\n animationFrameRef.current = requestAnimationFrame(updatePosition);\n } else {\n updatePosition();\n }\n return () => {\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n animationFrameRef.current = null;\n }\n };\n }, [\n isPlaying,\n sampleRate,\n samplesPerPixel,\n controlsOffset,\n currentTimeRef,\n playbackStartTimeRef,\n audioStartPositionRef,\n getAudioContextTime\n ]);\n useEffect3(() => {\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 return /* @__PURE__ */ jsxs4(PlayheadWithMarkerContainer, { ref: containerRef, $color: color, children: [\n /* @__PURE__ */ jsx12(MarkerTriangle, { $color: color }),\n /* @__PURE__ */ jsx12(MarkerLine, { $color: color })\n ] });\n};\n\n// src/components/Playlist.tsx\nimport styled16, { withTheme } from \"styled-components\";\nimport { useRef as useRef4, useCallback as useCallback3 } from \"react\";\nimport { jsx as jsx13, jsxs as jsxs5 } from \"react/jsx-runtime\";\nvar Wrapper2 = styled16.div`\n overflow-y: hidden;\n overflow-x: auto;\n position: relative;\n`;\nvar ScrollContainer = styled16.div.attrs((props) => ({\n style: props.$width !== void 0 ? { width: `${props.$width}px` } : {}\n}))`\n position: relative;\n background: ${(props) => props.$backgroundColor || \"transparent\"};\n`;\nvar TimescaleWrapper = styled16.div.attrs((props) => ({\n style: props.$width ? { minWidth: `${props.$width}px` } : {}\n}))`\n background: ${(props) => props.$backgroundColor || \"white\"};\n width: 100%;\n position: relative;\n overflow: hidden; /* Constrain loop region to timescale area */\n`;\nvar TracksContainer = styled16.div.attrs((props) => ({\n style: props.$width !== void 0 ? { minWidth: `${props.$width}px` } : {}\n}))`\n position: relative;\n background: ${(props) => props.$backgroundColor || \"transparent\"};\n width: 100%;\n`;\nvar ClickOverlay = styled16.div`\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`;\nvar Playlist = ({\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 \"data-playlist-state\": playlistState\n}) => {\n const wrapperRef = useRef4(null);\n const handleRef = useCallback3(\n (el) => {\n wrapperRef.current = el;\n scrollContainerRef?.(el);\n },\n [scrollContainerRef]\n );\n return /* @__PURE__ */ jsx13(Wrapper2, { \"data-scroll-container\": \"true\", \"data-playlist-state\": playlistState, ref: handleRef, children: /* @__PURE__ */ jsx13(ScrollViewportProvider, { containerRef: wrapperRef, children: /* @__PURE__ */ jsxs5(ScrollContainer, { $backgroundColor: backgroundColor, $width: scrollContainerWidth, children: [\n timescale && /* @__PURE__ */ jsx13(TimescaleWrapper, { $width: timescaleWidth, $backgroundColor: timescaleBackgroundColor, children: timescale }),\n /* @__PURE__ */ jsxs5(TracksContainer, { $width: tracksWidth, $backgroundColor: backgroundColor, children: [\n children,\n (onTracksClick || onTracksMouseDown) && /* @__PURE__ */ jsx13(\n ClickOverlay,\n {\n $controlsWidth: controlsWidth,\n $isSelecting: isSelecting,\n onClick: onTracksClick,\n onMouseDown: onTracksMouseDown,\n onMouseMove: onTracksMouseMove,\n onMouseUp: onTracksMouseUp\n }\n )\n ] })\n ] }) }) });\n};\nvar StyledPlaylist = withTheme(Playlist);\n\n// src/components/Selection.tsx\nimport styled17 from \"styled-components\";\nimport { jsx as jsx14 } from \"react/jsx-runtime\";\nvar SelectionOverlay = styled17.div.attrs((props) => ({\n style: {\n left: `${props.$left}px`,\n width: `${props.$width}px`\n }\n}))`\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`;\nvar Selection = ({\n startPosition,\n endPosition,\n color = \"#00ff00\"\n}) => {\n const width = Math.max(0, endPosition - startPosition);\n if (width <= 0) {\n return null;\n }\n return /* @__PURE__ */ jsx14(SelectionOverlay, { $left: startPosition, $width: width, $color: color, \"data-selection\": true });\n};\n\n// src/components/LoopRegion.tsx\nimport { useCallback as useCallback4, useRef as useRef5, useState } from \"react\";\nimport styled18 from \"styled-components\";\nimport { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs6 } from \"react/jsx-runtime\";\nvar LoopRegionOverlayDiv = styled18.div.attrs((props) => ({\n style: {\n left: `${props.$left}px`,\n width: `${props.$width}px`\n }\n}))`\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`;\nvar LoopMarker = styled18.div.attrs((props) => ({\n style: {\n left: `${props.$left}px`\n }\n}))`\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 ? \"border-right: 8px solid transparent;\" : \"border-left: 8px solid transparent;\"}\n }\n`;\nvar LoopRegion = ({\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 if (width <= 0) {\n return null;\n }\n return /* @__PURE__ */ jsxs6(Fragment2, { children: [\n /* @__PURE__ */ jsx15(\n LoopRegionOverlayDiv,\n {\n $left: startPosition,\n $width: width,\n $color: regionColor,\n \"data-loop-region\": true\n }\n ),\n /* @__PURE__ */ jsx15(\n LoopMarker,\n {\n $left: startPosition,\n $color: markerColor,\n $isStart: true,\n \"data-loop-marker\": \"start\"\n }\n ),\n /* @__PURE__ */ jsx15(\n LoopMarker,\n {\n $left: endPosition - 2,\n $color: markerColor,\n $isStart: false,\n \"data-loop-marker\": \"end\"\n }\n )\n ] });\n};\nvar DraggableMarkerHandle = styled18.div.attrs((props) => ({\n style: {\n left: `${props.$left}px`\n }\n}))`\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 ? \"border-right: 10px solid transparent;\" : \"border-left: 10px solid transparent;\"}\n }\n\n &:hover::before {\n opacity: 1;\n }\n`;\nvar TimescaleLoopShade = styled18.div.attrs((props) => ({\n style: {\n left: `${props.$left}px`,\n width: `${props.$width}px`\n }\n}))`\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`;\nvar LoopRegionMarkers = ({\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(null);\n const dragStartX = useRef5(0);\n const dragStartPosition = useRef5(0);\n const dragStartEnd = useRef5(0);\n const width = Math.max(0, endPosition - startPosition);\n const handleMarkerMouseDown = useCallback4(\n (e, marker) => {\n e.preventDefault();\n e.stopPropagation();\n setDraggingMarker(marker);\n dragStartX.current = e.clientX;\n dragStartPosition.current = marker === \"start\" ? startPosition : endPosition;\n const handleMouseMove = (moveEvent) => {\n const delta = moveEvent.clientX - dragStartX.current;\n const newPosition = dragStartPosition.current + delta;\n if (marker === \"start\") {\n const clampedPosition = Math.max(minPosition, Math.min(endPosition - 10, newPosition));\n onLoopStartChange?.(clampedPosition);\n } else {\n const clampedPosition = Math.max(startPosition + 10, Math.min(maxPosition, newPosition));\n onLoopEndChange?.(clampedPosition);\n }\n };\n const handleMouseUp = () => {\n setDraggingMarker(null);\n document.removeEventListener(\"mousemove\", handleMouseMove);\n document.removeEventListener(\"mouseup\", handleMouseUp);\n };\n document.addEventListener(\"mousemove\", handleMouseMove);\n document.addEventListener(\"mouseup\", handleMouseUp);\n },\n [startPosition, endPosition, minPosition, maxPosition, onLoopStartChange, onLoopEndChange]\n );\n const handleRegionMouseDown = useCallback4(\n (e) => {\n e.preventDefault();\n e.stopPropagation();\n setDraggingMarker(\"region\");\n dragStartX.current = e.clientX;\n dragStartPosition.current = startPosition;\n dragStartEnd.current = endPosition;\n const regionWidth = endPosition - startPosition;\n const handleMouseMove = (moveEvent) => {\n const delta = moveEvent.clientX - dragStartX.current;\n let newStart = dragStartPosition.current + delta;\n let newEnd = dragStartEnd.current + delta;\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 onLoopRegionMove?.(newStart, newEnd);\n };\n const handleMouseUp = () => {\n setDraggingMarker(null);\n document.removeEventListener(\"mousemove\", handleMouseMove);\n document.removeEventListener(\"mouseup\", handleMouseUp);\n };\n document.addEventListener(\"mousemove\", handleMouseMove);\n document.addEventListener(\"mouseup\", handleMouseUp);\n },\n [startPosition, endPosition, minPosition, maxPosition, onLoopRegionMove]\n );\n if (width <= 0) {\n return null;\n }\n return /* @__PURE__ */ jsxs6(Fragment2, { children: [\n /* @__PURE__ */ jsx15(\n TimescaleLoopShade,\n {\n $left: startPosition,\n $width: width,\n $color: regionColor,\n $isDragging: draggingMarker === \"region\",\n onMouseDown: handleRegionMouseDown,\n \"data-loop-region-timescale\": true\n }\n ),\n /* @__PURE__ */ jsx15(\n DraggableMarkerHandle,\n {\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 ),\n /* @__PURE__ */ jsx15(\n DraggableMarkerHandle,\n {\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};\nvar TimescaleLoopCreator = styled18.div.attrs((props) => ({\n style: {\n left: `${props.$leftOffset || 0}px`\n }\n}))`\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`;\nvar TimescaleLoopRegion = ({\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 [, setIsCreating] = useState(false);\n const createStartX = useRef5(0);\n const containerRef = useRef5(null);\n const hasLoopRegion = endPosition > startPosition;\n const handleBackgroundMouseDown = useCallback4(\n (e) => {\n const target = e.target;\n if (target.closest(\"[data-loop-marker-handle]\") || target.closest(\"[data-loop-region-timescale]\")) {\n return;\n }\n e.preventDefault();\n setIsCreating(true);\n const rect = containerRef.current?.getBoundingClientRect();\n if (!rect) return;\n const clickX = e.clientX - rect.left;\n const clampedX = Math.max(minPosition, Math.min(maxPosition, clickX));\n createStartX.current = clampedX;\n onLoopRegionChange?.(clampedX, clampedX);\n const handleMouseMove = (moveEvent) => {\n const currentX = moveEvent.clientX - rect.left;\n const clampedCurrentX = Math.max(minPosition, Math.min(maxPosition, currentX));\n const newStart = Math.min(createStartX.current, clampedCurrentX);\n const newEnd = Math.max(createStartX.current, clampedCurrentX);\n onLoopRegionChange?.(newStart, newEnd);\n };\n const handleMouseUp = () => {\n setIsCreating(false);\n document.removeEventListener(\"mousemove\", handleMouseMove);\n document.removeEventListener(\"mouseup\", handleMouseUp);\n };\n document.addEventListener(\"mousemove\", handleMouseMove);\n document.addEventListener(\"mouseup\", handleMouseUp);\n },\n [minPosition, maxPosition, onLoopRegionChange]\n );\n return /* @__PURE__ */ jsx15(\n TimescaleLoopCreator,\n {\n ref: containerRef,\n $leftOffset: controlsOffset,\n onMouseDown: handleBackgroundMouseDown,\n \"data-timescale-loop-creator\": true,\n children: hasLoopRegion && /* @__PURE__ */ jsx15(\n LoopRegionMarkers,\n {\n startPosition,\n endPosition,\n markerColor,\n regionColor,\n minPosition,\n maxPosition,\n onLoopStartChange: (newStart) => onLoopRegionChange?.(newStart, endPosition),\n onLoopEndChange: (newEnd) => onLoopRegionChange?.(startPosition, newEnd),\n onLoopRegionMove: (newStart, newEnd) => onLoopRegionChange?.(newStart, newEnd)\n }\n )\n }\n );\n};\n\n// src/components/SelectionTimeInputs.tsx\nimport { useEffect as useEffect5, useState as useState3 } from \"react\";\n\n// src/components/TimeInput.tsx\nimport { useEffect as useEffect4, useState as useState2 } from \"react\";\n\n// src/utils/timeFormat.ts\nfunction clockFormat(seconds, decimals) {\n const hours = Math.floor(seconds / 3600) % 24;\n const minutes = Math.floor(seconds / 60) % 60;\n const secs = (seconds % 60).toFixed(decimals);\n return String(hours).padStart(2, \"0\") + \":\" + String(minutes).padStart(2, \"0\") + \":\" + secs.padStart(decimals + 3, \"0\");\n}\nfunction formatTime(seconds, format) {\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}\nfunction parseTime(timeStr, format) {\n if (!timeStr) return 0;\n switch (format) {\n case \"seconds\":\n case \"thousandths\":\n return parseFloat(timeStr) || 0;\n case \"hh:mm:ss\":\n case \"hh:mm:ss.u\":\n case \"hh:mm:ss.uu\":\n case \"hh:mm:ss.uuu\": {\n const parts = timeStr.split(\":\");\n if (parts.length !== 3) return 0;\n const hours = parseInt(parts[0], 10) || 0;\n const minutes = parseInt(parts[1], 10) || 0;\n const seconds = parseFloat(parts[2]) || 0;\n return hours * 3600 + minutes * 60 + seconds;\n }\n default:\n return 0;\n }\n}\n\n// src/components/TimeInput.tsx\nimport { Fragment as Fragment3, jsx as jsx16, jsxs as jsxs7 } from \"react/jsx-runtime\";\nvar TimeInput = ({\n id,\n label,\n value,\n format,\n className,\n onChange,\n readOnly = false\n}) => {\n const [displayValue, setDisplayValue] = useState2(\"\");\n useEffect4(() => {\n const formatted = formatTime(value, format);\n setDisplayValue(formatted);\n }, [value, format, id]);\n const handleChange = (e) => {\n const newDisplayValue = e.target.value;\n setDisplayValue(newDisplayValue);\n };\n const handleBlur = () => {\n if (onChange) {\n const parsedValue = parseTime(displayValue, format);\n onChange(parsedValue);\n }\n setDisplayValue(formatTime(value, format));\n };\n const handleKeyDown = (e) => {\n if (e.key === \"Enter\") {\n e.currentTarget.blur();\n }\n };\n return /* @__PURE__ */ jsxs7(Fragment3, { children: [\n /* @__PURE__ */ jsx16(ScreenReaderOnly, { as: \"label\", htmlFor: id, children: label }),\n /* @__PURE__ */ jsx16(\n BaseInput,\n {\n type: \"text\",\n className,\n id,\n value: displayValue,\n onChange: handleChange,\n onBlur: handleBlur,\n onKeyDown: handleKeyDown,\n readOnly\n }\n )\n ] });\n};\n\n// src/components/SelectionTimeInputs.tsx\nimport { jsx as jsx17, jsxs as jsxs8 } from \"react/jsx-runtime\";\nvar SelectionTimeInputs = ({\n selectionStart,\n selectionEnd,\n onSelectionChange,\n className\n}) => {\n const [timeFormat, setTimeFormat] = useState3(\"hh:mm:ss.uuu\");\n useEffect5(() => {\n const timeFormatSelect = document.querySelector(\".time-format\");\n const handleFormatChange = () => {\n if (timeFormatSelect) {\n setTimeFormat(timeFormatSelect.value);\n }\n };\n if (timeFormatSelect) {\n setTimeFormat(timeFormatSelect.value);\n timeFormatSelect.addEventListener(\"change\", handleFormatChange);\n }\n return () => {\n timeFormatSelect?.removeEventListener(\"change\", handleFormatChange);\n };\n }, []);\n const handleStartChange = (value) => {\n if (onSelectionChange) {\n onSelectionChange(value, selectionEnd);\n }\n };\n const handleEndChange = (value) => {\n if (onSelectionChange) {\n onSelectionChange(selectionStart, value);\n }\n };\n return /* @__PURE__ */ jsxs8(\"div\", { className, children: [\n /* @__PURE__ */ jsx17(\n TimeInput,\n {\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 ),\n /* @__PURE__ */ jsx17(\n TimeInput,\n {\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\n// src/contexts/DevicePixelRatio.tsx\nimport { useState as useState4, createContext as createContext3, useContext as useContext3 } from \"react\";\nimport { jsx as jsx18 } from \"react/jsx-runtime\";\nfunction getScale() {\n return window.devicePixelRatio;\n}\nvar DevicePixelRatioContext = createContext3(getScale());\nvar DevicePixelRatioProvider = ({ children }) => {\n const [scale, setScale] = useState4(getScale());\n matchMedia(`(resolution: ${getScale()}dppx)`).addEventListener(\n \"change\",\n () => {\n setScale(getScale());\n },\n { once: true }\n );\n return /* @__PURE__ */ jsx18(DevicePixelRatioContext.Provider, { value: Math.ceil(scale), children });\n};\nvar useDevicePixelRatio = () => useContext3(DevicePixelRatioContext);\n\n// src/contexts/PlaylistInfo.tsx\nimport { createContext as createContext4, useContext as useContext4 } from \"react\";\nvar PlaylistInfoContext = createContext4({\n sampleRate: 48e3,\n samplesPerPixel: 1e3,\n zoomLevels: [1e3, 1500, 2e3, 2500],\n waveHeight: 80,\n timeScaleHeight: 15,\n controls: {\n show: false,\n width: 150\n },\n duration: 3e4,\n barWidth: 1,\n barGap: 0\n});\nvar usePlaylistInfo = () => useContext4(PlaylistInfoContext);\n\n// src/contexts/Theme.tsx\nimport { useContext as useContext5 } from \"react\";\nimport { ThemeContext } from \"styled-components\";\nvar useTheme2 = () => useContext5(ThemeContext);\n\n// src/contexts/TrackControls.tsx\nimport { createContext as createContext5, useContext as useContext6, Fragment as Fragment4 } from \"react\";\nimport { jsx as jsx19 } from \"react/jsx-runtime\";\nvar TrackControlsContext = createContext5(/* @__PURE__ */ jsx19(Fragment4, {}));\nvar useTrackControls = () => useContext6(TrackControlsContext);\n\n// src/contexts/Playout.tsx\nimport {\n useState as useState5,\n createContext as createContext6,\n useContext as useContext7\n} from \"react\";\nimport { jsx as jsx20 } from \"react/jsx-runtime\";\nvar defaultProgress = 0;\nvar defaultIsPlaying = false;\nvar defaultSelectionStart = 0;\nvar defaultSelectionEnd = 0;\nvar defaultPlayout = {\n progress: defaultProgress,\n isPlaying: defaultIsPlaying,\n selectionStart: defaultSelectionStart,\n selectionEnd: defaultSelectionEnd\n};\nvar PlayoutStatusContext = createContext6(defaultPlayout);\nvar PlayoutStatusUpdateContext = createContext6({\n setIsPlaying: () => {\n },\n setProgress: () => {\n },\n setSelection: () => {\n }\n});\nvar PlayoutProvider = ({ children }) => {\n const [isPlaying, setIsPlaying] = useState5(defaultIsPlaying);\n const [progress, setProgress] = useState5(defaultProgress);\n const [selectionStart, setSelectionStart] = useState5(defaultSelectionStart);\n const [selectionEnd, setSelectionEnd] = useState5(defaultSelectionEnd);\n const setSelection = (start, end) => {\n setSelectionStart(start);\n setSelectionEnd(end);\n };\n return /* @__PURE__ */ jsx20(PlayoutStatusUpdateContext.Provider, { value: { setIsPlaying, setProgress, setSelection }, children: /* @__PURE__ */ jsx20(PlayoutStatusContext.Provider, { value: { isPlaying, progress, selectionStart, selectionEnd }, children }) });\n};\nvar usePlayoutStatus = () => useContext7(PlayoutStatusContext);\nvar usePlayoutStatusUpdate = () => useContext7(PlayoutStatusUpdateContext);\n\n// src/components/SpectrogramChannel.tsx\nimport { useLayoutEffect as useLayoutEffect2, useRef as useRef6, useEffect as useEffect6 } from \"react\";\nimport styled19 from \"styled-components\";\nimport { MAX_CANVAS_WIDTH as MAX_CANVAS_WIDTH2 } from \"@waveform-playlist/core\";\nimport { jsx as jsx21 } from \"react/jsx-runtime\";\nvar LINEAR_FREQUENCY_SCALE = (f, minF, maxF) => (f - minF) / (maxF - minF);\nvar Wrapper3 = styled19.div.attrs((props) => ({\n style: {\n top: `${props.$waveHeight * props.$index}px`,\n width: `${props.$cssWidth}px`,\n height: `${props.$waveHeight}px`\n }\n}))`\n position: absolute;\n background: #000;\n transform: translateZ(0);\n backface-visibility: hidden;\n`;\nvar SpectrogramCanvas = styled19.canvas.attrs((props) => ({\n style: {\n width: `${props.$cssWidth}px`,\n height: `${props.$waveHeight}px`,\n left: `${props.$left}px`\n }\n}))`\n position: absolute;\n top: 0;\n /* Promote to own compositing layer for smoother scrolling */\n will-change: transform;\n image-rendering: pixelated;\n image-rendering: crisp-edges;\n`;\nfunction defaultGetColorMap() {\n const lut = new Uint8Array(256 * 3);\n for (let i = 0; i < 256; i++) {\n lut[i * 3] = lut[i * 3 + 1] = lut[i * 3 + 2] = i;\n }\n return lut;\n}\nvar DEFAULT_COLOR_LUT = defaultGetColorMap();\nvar SpectrogramChannel = ({\n index,\n channelIndex: channelIndexProp,\n data,\n length,\n waveHeight,\n devicePixelRatio = 1,\n samplesPerPixel,\n colorLUT,\n frequencyScaleFn,\n minFrequency = 0,\n maxFrequency,\n workerApi,\n clipId,\n onCanvasesReady\n}) => {\n const channelIndex = channelIndexProp ?? index;\n const { canvasRef, canvasMapRef } = useChunkedCanvasRefs();\n const registeredIdsRef = useRef6([]);\n const transferredCanvasesRef = useRef6(/* @__PURE__ */ new WeakSet());\n const workerApiRef = useRef6(workerApi);\n const onCanvasesReadyRef = useRef6(onCanvasesReady);\n const isWorkerMode = !!(workerApi && clipId);\n const clipOriginX = useClipViewportOrigin();\n const visibleChunkIndices = useVisibleChunkIndices(length, MAX_CANVAS_WIDTH2, clipOriginX);\n const lut = colorLUT ?? DEFAULT_COLOR_LUT;\n const maxF = maxFrequency ?? (data ? data.sampleRate / 2 : 22050);\n const scaleFn = frequencyScaleFn ?? LINEAR_FREQUENCY_SCALE;\n const hasCustomFrequencyScale = Boolean(frequencyScaleFn);\n useEffect6(() => {\n workerApiRef.current = workerApi;\n }, [workerApi]);\n useEffect6(() => {\n onCanvasesReadyRef.current = onCanvasesReady;\n }, [onCanvasesReady]);\n useEffect6(() => {\n if (!isWorkerMode) return;\n const currentWorkerApi = workerApiRef.current;\n if (!currentWorkerApi || !clipId) return;\n const previousCount = registeredIdsRef.current.length;\n const remaining = [];\n for (const id of registeredIdsRef.current) {\n const match = id.match(/chunk(\\d+)$/);\n if (!match) {\n remaining.push(id);\n continue;\n }\n const chunkIdx = parseInt(match[1], 10);\n const canvas = canvasMapRef.current.get(chunkIdx);\n if (canvas && canvas.isConnected) {\n remaining.push(id);\n } else {\n try {\n currentWorkerApi.unregisterCanvas(id);\n } catch (err) {\n console.warn(`[spectrogram] unregisterCanvas failed for ${id}:`, err);\n }\n }\n }\n registeredIdsRef.current = remaining;\n const newIds = [];\n for (const [canvasIdx, canvas] of canvasMapRef.current.entries()) {\n if (transferredCanvasesRef.current.has(canvas)) continue;\n const canvasId = `${clipId}-ch${channelIndex}-chunk${canvasIdx}`;\n let offscreen;\n try {\n offscreen = canvas.transferControlToOffscreen();\n } catch (err) {\n console.warn(`[spectrogram] transferControlToOffscreen failed for ${canvasId}:`, err);\n continue;\n }\n transferredCanvasesRef.current.add(canvas);\n try {\n currentWorkerApi.registerCanvas(canvasId, offscreen);\n newIds.push(canvasId);\n } catch (err) {\n console.warn(`[spectrogram] registerCanvas failed for ${canvasId}:`, err);\n continue;\n }\n }\n if (newIds.length > 0) {\n registeredIdsRef.current = [...registeredIdsRef.current, ...newIds];\n }\n const canvasSetChanged = newIds.length > 0 || remaining.length < previousCount;\n if (canvasSetChanged) {\n const allIds = registeredIdsRef.current;\n const allWidths = allIds.map((id) => {\n const match = id.match(/chunk(\\d+)$/);\n if (!match) {\n console.warn(`[spectrogram] Unexpected canvas ID format: ${id}`);\n return MAX_CANVAS_WIDTH2;\n }\n const chunkIdx = parseInt(match[1], 10);\n return Math.min(length - chunkIdx * MAX_CANVAS_WIDTH2, MAX_CANVAS_WIDTH2);\n });\n onCanvasesReadyRef.current?.(allIds, allWidths);\n }\n }, [canvasMapRef, isWorkerMode, clipId, channelIndex, length, visibleChunkIndices]);\n useEffect6(() => {\n return () => {\n const api = workerApiRef.current;\n if (!api) return;\n for (const id of registeredIdsRef.current) {\n try {\n api.unregisterCanvas(id);\n } catch (err) {\n console.warn(`[spectrogram] unregisterCanvas failed for ${id}:`, err);\n }\n }\n registeredIdsRef.current = [];\n };\n }, []);\n useLayoutEffect2(() => {\n if (isWorkerMode || !data) return;\n const {\n frequencyBinCount,\n frameCount,\n hopSize,\n sampleRate,\n gainDb,\n rangeDb: rawRangeDb\n } = data;\n const rangeDb = rawRangeDb === 0 ? 1 : rawRangeDb;\n const binToFreq = (bin) => bin / frequencyBinCount * (sampleRate / 2);\n for (const [canvasIdx, canvas] of canvasMapRef.current.entries()) {\n const globalPixelOffset = canvasIdx * MAX_CANVAS_WIDTH2;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) continue;\n const canvasWidth = canvas.width / devicePixelRatio;\n const canvasHeight = waveHeight;\n ctx.resetTransform();\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.imageSmoothingEnabled = false;\n ctx.scale(devicePixelRatio, devicePixelRatio);\n const imgData = ctx.createImageData(canvasWidth, canvasHeight);\n const pixels = imgData.data;\n for (let x = 0; x < canvasWidth; x++) {\n const globalX = globalPixelOffset + x;\n const samplePos = globalX * samplesPerPixel;\n const frame = Math.floor(samplePos / hopSize);\n if (frame < 0 || frame >= frameCount) continue;\n const frameOffset = frame * frequencyBinCount;\n for (let y = 0; y < canvasHeight; y++) {\n const normalizedY = 1 - y / canvasHeight;\n let bin = Math.floor(normalizedY * frequencyBinCount);\n if (hasCustomFrequencyScale) {\n let lo = 0;\n let hi = frequencyBinCount - 1;\n while (lo < hi) {\n const mid = lo + hi >> 1;\n const freq = binToFreq(mid);\n const scaled = scaleFn(freq, minFrequency, maxF);\n if (scaled < normalizedY) {\n lo = mid + 1;\n } else {\n hi = mid;\n }\n }\n bin = lo;\n }\n if (bin < 0 || bin >= frequencyBinCount) continue;\n const db = data.data[frameOffset + bin];\n const normalized = Math.max(0, Math.min(1, (db + rangeDb + gainDb) / rangeDb));\n const colorIdx = Math.floor(normalized * 255);\n const pixelIdx = (y * canvasWidth + x) * 4;\n pixels[pixelIdx] = lut[colorIdx * 3];\n pixels[pixelIdx + 1] = lut[colorIdx * 3 + 1];\n pixels[pixelIdx + 2] = lut[colorIdx * 3 + 2];\n pixels[pixelIdx + 3] = 255;\n }\n }\n ctx.resetTransform();\n ctx.putImageData(imgData, 0, 0);\n if (devicePixelRatio !== 1) {\n const tmpCanvas = document.createElement(\"canvas\");\n tmpCanvas.width = canvasWidth;\n tmpCanvas.height = canvasHeight;\n const tmpCtx = tmpCanvas.getContext(\"2d\");\n if (!tmpCtx) continue;\n tmpCtx.putImageData(imgData, 0, 0);\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.imageSmoothingEnabled = false;\n ctx.drawImage(tmpCanvas, 0, 0, canvas.width, canvas.height);\n }\n }\n }, [\n canvasMapRef,\n isWorkerMode,\n data,\n length,\n waveHeight,\n devicePixelRatio,\n samplesPerPixel,\n lut,\n minFrequency,\n maxF,\n scaleFn,\n hasCustomFrequencyScale,\n visibleChunkIndices\n ]);\n const canvases = visibleChunkIndices.map((i) => {\n const chunkLeft = i * MAX_CANVAS_WIDTH2;\n const currentWidth = Math.min(length - chunkLeft, MAX_CANVAS_WIDTH2);\n return /* @__PURE__ */ jsx21(\n SpectrogramCanvas,\n {\n $cssWidth: currentWidth,\n $left: chunkLeft,\n width: currentWidth * devicePixelRatio,\n height: waveHeight * devicePixelRatio,\n $waveHeight: waveHeight,\n \"data-index\": i,\n ref: canvasRef\n },\n `${length}-${i}`\n );\n });\n return /* @__PURE__ */ jsx21(Wrapper3, { $index: index, $cssWidth: length, $waveHeight: waveHeight, children: canvases });\n};\n\n// src/components/SmartChannel.tsx\nimport { Fragment as Fragment5, jsx as jsx22, jsxs as jsxs9 } from \"react/jsx-runtime\";\nvar SmartChannel = ({\n isSelected,\n transparentBackground,\n renderMode = \"waveform\",\n spectrogramData,\n spectrogramColorLUT,\n samplesPerPixel: sppProp,\n spectrogramFrequencyScaleFn,\n spectrogramMinFrequency,\n spectrogramMaxFrequency,\n spectrogramWorkerApi,\n spectrogramClipId,\n spectrogramOnCanvasesReady,\n ...props\n}) => {\n const theme = useTheme2();\n const { waveHeight, barWidth, barGap, samplesPerPixel: contextSpp } = usePlaylistInfo();\n const devicePixelRatio = useDevicePixelRatio();\n const samplesPerPixel = sppProp ?? contextSpp;\n const waveOutlineColor = isSelected && theme ? theme.selectedWaveOutlineColor : theme?.waveOutlineColor;\n const waveFillColor = isSelected && theme ? theme.selectedWaveFillColor : theme?.waveFillColor;\n const drawMode = theme?.waveformDrawMode || \"inverted\";\n const hasSpectrogram = spectrogramData || spectrogramWorkerApi;\n if (renderMode === \"spectrogram\" && hasSpectrogram) {\n return /* @__PURE__ */ jsx22(\n SpectrogramChannel,\n {\n index: props.index,\n data: spectrogramData,\n length: props.length,\n waveHeight,\n devicePixelRatio,\n samplesPerPixel,\n colorLUT: spectrogramColorLUT,\n frequencyScaleFn: spectrogramFrequencyScaleFn,\n minFrequency: spectrogramMinFrequency,\n maxFrequency: spectrogramMaxFrequency,\n workerApi: spectrogramWorkerApi,\n clipId: spectrogramClipId,\n onCanvasesReady: spectrogramOnCanvasesReady\n }\n );\n }\n if (renderMode === \"both\" && hasSpectrogram) {\n const halfHeight = Math.floor(waveHeight / 2);\n return /* @__PURE__ */ jsxs9(Fragment5, { children: [\n /* @__PURE__ */ jsx22(\n SpectrogramChannel,\n {\n index: props.index * 2,\n channelIndex: props.index,\n data: spectrogramData,\n length: props.length,\n waveHeight: halfHeight,\n devicePixelRatio,\n samplesPerPixel,\n colorLUT: spectrogramColorLUT,\n frequencyScaleFn: spectrogramFrequencyScaleFn,\n minFrequency: spectrogramMinFrequency,\n maxFrequency: spectrogramMaxFrequency,\n workerApi: spectrogramWorkerApi,\n clipId: spectrogramClipId,\n onCanvasesReady: spectrogramOnCanvasesReady\n }\n ),\n /* @__PURE__ */ jsx22(\n \"div\",\n {\n style: {\n position: \"absolute\",\n top: (props.index * 2 + 1) * halfHeight,\n width: props.length,\n height: halfHeight\n },\n children: /* @__PURE__ */ jsx22(\n Channel,\n {\n ...props,\n index: 0,\n waveOutlineColor,\n waveFillColor,\n waveHeight: halfHeight,\n devicePixelRatio,\n barWidth,\n barGap,\n transparentBackground,\n drawMode\n }\n )\n }\n )\n ] });\n }\n return /* @__PURE__ */ jsx22(\n Channel,\n {\n ...props,\n waveOutlineColor,\n waveFillColor,\n waveHeight,\n devicePixelRatio,\n barWidth,\n barGap,\n transparentBackground,\n drawMode\n }\n );\n};\n\n// src/components/SpectrogramLabels.tsx\nimport { useRef as useRef7, useLayoutEffect as useLayoutEffect3 } from \"react\";\nimport styled20 from \"styled-components\";\nimport { jsx as jsx23 } from \"react/jsx-runtime\";\nvar LABELS_WIDTH = 72;\nvar LabelsStickyWrapper = styled20.div`\n position: sticky;\n left: 0;\n z-index: 101;\n pointer-events: none;\n height: 0;\n width: 0;\n overflow: visible;\n`;\nfunction getFrequencyLabels(minF, maxF, height) {\n const allCandidates = [\n 20,\n 50,\n 100,\n 200,\n 500,\n 1e3,\n 2e3,\n 3e3,\n 4e3,\n 5e3,\n 8e3,\n 1e4,\n 12e3,\n 16e3,\n 2e4\n ];\n const inRange = allCandidates.filter((f) => f >= minF && f <= maxF);\n const maxLabels = Math.max(2, Math.floor(height / 20));\n if (inRange.length <= maxLabels) return inRange;\n const step = (inRange.length - 1) / (maxLabels - 1);\n const result = [];\n for (let i = 0; i < maxLabels; i++) {\n result.push(inRange[Math.round(i * step)]);\n }\n return result;\n}\nvar SpectrogramLabels = ({\n waveHeight,\n numChannels,\n frequencyScaleFn,\n minFrequency,\n maxFrequency,\n labelsColor = \"#ccc\",\n labelsBackground = \"rgba(0,0,0,0.6)\",\n renderMode = \"spectrogram\",\n hasClipHeaders = false\n}) => {\n const canvasRef = useRef7(null);\n const devicePixelRatio = useDevicePixelRatio();\n const spectrogramHeight = renderMode === \"both\" ? Math.floor(waveHeight / 2) : waveHeight;\n const totalHeight = numChannels * waveHeight;\n const clipHeaderOffset = hasClipHeaders ? 22 : 0;\n useLayoutEffect3(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n ctx.resetTransform();\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.scale(devicePixelRatio, devicePixelRatio);\n const labelFreqs = getFrequencyLabels(minFrequency, maxFrequency, spectrogramHeight);\n for (let ch = 0; ch < numChannels; ch++) {\n const channelTop = ch * waveHeight + clipHeaderOffset;\n ctx.font = \"11px monospace\";\n ctx.textBaseline = \"middle\";\n for (const freq of labelFreqs) {\n const normalized = frequencyScaleFn(freq, minFrequency, maxFrequency);\n if (normalized < 0 || normalized > 1) continue;\n const y = channelTop + spectrogramHeight * (1 - normalized);\n const text = freq >= 1e3 ? `${(freq / 1e3).toFixed(1)}k` : `${freq} Hz`;\n const metrics = ctx.measureText(text);\n const padding = 3;\n ctx.fillStyle = labelsBackground;\n ctx.fillRect(0, y - 7, metrics.width + padding * 2, 14);\n ctx.fillStyle = labelsColor;\n ctx.fillText(text, padding, y);\n }\n }\n }, [\n waveHeight,\n numChannels,\n frequencyScaleFn,\n minFrequency,\n maxFrequency,\n labelsColor,\n labelsBackground,\n devicePixelRatio,\n spectrogramHeight,\n clipHeaderOffset\n ]);\n return /* @__PURE__ */ jsx23(LabelsStickyWrapper, { $height: totalHeight + clipHeaderOffset, children: /* @__PURE__ */ jsx23(\n \"canvas\",\n {\n ref: canvasRef,\n width: LABELS_WIDTH * devicePixelRatio,\n height: (totalHeight + clipHeaderOffset) * devicePixelRatio,\n style: {\n width: LABELS_WIDTH,\n height: totalHeight + clipHeaderOffset,\n pointerEvents: \"none\"\n }\n }\n ) });\n};\n\n// src/components/SmartScale.tsx\nimport { useContext as useContext9 } from \"react\";\n\n// src/components/TimeScale.tsx\nimport React16, { useLayoutEffect as useLayoutEffect4, useContext as useContext8, useMemo as useMemo2 } from \"react\";\nimport styled21, { withTheme as withTheme2 } from \"styled-components\";\n\n// src/utils/conversions.ts\nfunction samplesToSeconds(samples, sampleRate) {\n return samples / sampleRate;\n}\nfunction secondsToSamples(seconds, sampleRate) {\n return Math.ceil(seconds * sampleRate);\n}\nfunction samplesToPixels(samples, samplesPerPixel) {\n return Math.floor(samples / samplesPerPixel);\n}\nfunction pixelsToSamples(pixels, samplesPerPixel) {\n return Math.floor(pixels * samplesPerPixel);\n}\nfunction pixelsToSeconds(pixels, samplesPerPixel, sampleRate) {\n return pixels * samplesPerPixel / sampleRate;\n}\nfunction secondsToPixels(seconds, samplesPerPixel, sampleRate) {\n return Math.ceil(seconds * sampleRate / samplesPerPixel);\n}\n\n// src/components/TimeScale.tsx\nimport { MAX_CANVAS_WIDTH as MAX_CANVAS_WIDTH3 } from \"@waveform-playlist/core\";\nimport { jsx as jsx24, jsxs as jsxs10 } from \"react/jsx-runtime\";\nfunction formatTime2(milliseconds) {\n const seconds = Math.floor(milliseconds / 1e3);\n const s = seconds % 60;\n const m = (seconds - s) / 60;\n return `${m}:${String(s).padStart(2, \"0\")}`;\n}\nvar PlaylistTimeScaleScroll = styled21.div.attrs((props) => ({\n style: {\n width: `${props.$cssWidth}px`,\n marginLeft: `${props.$controlWidth}px`,\n height: `${props.$timeScaleHeight}px`\n }\n}))`\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`;\nvar TimeTickChunk = styled21.canvas.attrs((props) => ({\n style: {\n width: `${props.$cssWidth}px`,\n height: `${props.$timeScaleHeight}px`,\n left: `${props.$left}px`\n }\n}))`\n position: absolute;\n bottom: 0;\n /* Promote to own compositing layer for smoother scrolling */\n will-change: transform;\n`;\nvar TimeStamp = styled21.div.attrs((props) => ({\n style: {\n left: `${props.$left + 4}px`\n // Offset 4px to the right of the tick\n }\n}))`\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`;\nvar TimeScale = (props) => {\n const {\n theme: { timeColor },\n duration,\n marker,\n bigStep,\n secondStep,\n renderTimestamp\n } = props;\n const { canvasRef, canvasMapRef } = useChunkedCanvasRefs();\n const {\n sampleRate,\n samplesPerPixel,\n timeScaleHeight,\n controls: { show: showControls, width: controlWidth }\n } = useContext8(PlaylistInfoContext);\n const devicePixelRatio = useDevicePixelRatio();\n const { widthX, canvasInfo, timeMarkersWithPositions } = useMemo2(() => {\n const nextCanvasInfo = /* @__PURE__ */ new Map();\n const nextMarkers = [];\n const nextWidthX = secondsToPixels(duration / 1e3, samplesPerPixel, sampleRate);\n const pixPerSec = sampleRate / samplesPerPixel;\n let counter = 0;\n for (let i = 0; i < nextWidthX; i += pixPerSec * secondStep / 1e3) {\n const pix = Math.floor(i);\n if (counter % marker === 0) {\n const timeMs = counter;\n const timestamp = formatTime2(timeMs);\n const element = renderTimestamp ? /* @__PURE__ */ jsx24(React16.Fragment, { children: renderTimestamp(timeMs, pix) }, `timestamp-${counter}`) : /* @__PURE__ */ jsx24(TimeStamp, { $left: pix, children: timestamp }, timestamp);\n nextMarkers.push({ pix, element });\n nextCanvasInfo.set(pix, timeScaleHeight);\n } else if (counter % bigStep === 0) {\n nextCanvasInfo.set(pix, Math.floor(timeScaleHeight / 2));\n } else if (counter % secondStep === 0) {\n nextCanvasInfo.set(pix, Math.floor(timeScaleHeight / 5));\n }\n counter += secondStep;\n }\n return {\n widthX: nextWidthX,\n canvasInfo: nextCanvasInfo,\n timeMarkersWithPositions: nextMarkers\n };\n }, [\n duration,\n samplesPerPixel,\n sampleRate,\n marker,\n bigStep,\n secondStep,\n renderTimestamp,\n timeScaleHeight\n ]);\n const visibleChunkIndices = useVisibleChunkIndices(widthX, MAX_CANVAS_WIDTH3);\n const visibleChunks = visibleChunkIndices.map((i) => {\n const chunkLeft = i * MAX_CANVAS_WIDTH3;\n const chunkWidth = Math.min(widthX - chunkLeft, MAX_CANVAS_WIDTH3);\n return /* @__PURE__ */ jsx24(\n TimeTickChunk,\n {\n $cssWidth: chunkWidth,\n $left: chunkLeft,\n $timeScaleHeight: timeScaleHeight,\n width: chunkWidth * devicePixelRatio,\n height: timeScaleHeight * devicePixelRatio,\n \"data-index\": i,\n ref: canvasRef\n },\n `timescale-${i}`\n );\n });\n const firstChunkLeft = visibleChunkIndices.length > 0 ? visibleChunkIndices[0] * MAX_CANVAS_WIDTH3 : 0;\n const lastChunkRight = visibleChunkIndices.length > 0 ? (visibleChunkIndices[visibleChunkIndices.length - 1] + 1) * MAX_CANVAS_WIDTH3 : Infinity;\n const visibleMarkers = visibleChunkIndices.length > 0 ? timeMarkersWithPositions.filter(({ pix }) => pix >= firstChunkLeft && pix < lastChunkRight).map(({ element }) => element) : timeMarkersWithPositions.map(({ element }) => element);\n useLayoutEffect4(() => {\n for (const [chunkIdx, canvas] of canvasMapRef.current.entries()) {\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) continue;\n const chunkLeft = chunkIdx * MAX_CANVAS_WIDTH3;\n const chunkWidth = canvas.width / devicePixelRatio;\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 for (const [pixLeft, scaleHeight] of canvasInfo.entries()) {\n if (pixLeft < chunkLeft || pixLeft >= chunkLeft + chunkWidth) continue;\n const localX = pixLeft - chunkLeft;\n const scaleY = timeScaleHeight - scaleHeight;\n ctx.fillRect(localX, scaleY, 1, scaleHeight);\n }\n }\n }, [\n canvasMapRef,\n duration,\n devicePixelRatio,\n timeColor,\n timeScaleHeight,\n canvasInfo,\n visibleChunkIndices\n ]);\n return /* @__PURE__ */ jsxs10(\n PlaylistTimeScaleScroll,\n {\n $cssWidth: widthX,\n $controlWidth: showControls ? controlWidth : 0,\n $timeScaleHeight: timeScaleHeight,\n children: [\n visibleMarkers,\n visibleChunks\n ]\n }\n );\n};\nvar StyledTimeScale = withTheme2(TimeScale);\n\n// src/components/SmartScale.tsx\nimport { jsx as jsx25 } from \"react/jsx-runtime\";\nvar timeinfo = /* @__PURE__ */ new Map([\n [\n 700,\n {\n marker: 1e3,\n bigStep: 500,\n smallStep: 100\n }\n ],\n [\n 1500,\n {\n marker: 2e3,\n bigStep: 1e3,\n smallStep: 200\n }\n ],\n [\n 2500,\n {\n marker: 2e3,\n bigStep: 1e3,\n smallStep: 500\n }\n ],\n [\n 5e3,\n {\n marker: 5e3,\n bigStep: 1e3,\n smallStep: 500\n }\n ],\n [\n 1e4,\n {\n marker: 1e4,\n bigStep: 5e3,\n smallStep: 1e3\n }\n ],\n [\n 12e3,\n {\n marker: 15e3,\n bigStep: 5e3,\n smallStep: 1e3\n }\n ],\n [\n Infinity,\n {\n marker: 3e4,\n bigStep: 1e4,\n smallStep: 5e3\n }\n ]\n]);\nfunction getScaleInfo(samplesPerPixel) {\n const keys = timeinfo.keys();\n let config;\n for (const resolution of keys) {\n if (samplesPerPixel < resolution) {\n config = timeinfo.get(resolution);\n break;\n }\n }\n if (config === void 0) {\n config = { marker: 3e4, bigStep: 1e4, smallStep: 5e3 };\n }\n return config;\n}\nvar SmartScale = ({ renderTimestamp }) => {\n const { samplesPerPixel, duration } = useContext9(PlaylistInfoContext);\n let config = getScaleInfo(samplesPerPixel);\n return /* @__PURE__ */ jsx25(\n StyledTimeScale,\n {\n marker: config.marker,\n bigStep: config.bigStep,\n secondStep: config.smallStep,\n duration,\n renderTimestamp\n }\n );\n};\n\n// src/components/TimeFormatSelect.tsx\nimport styled22 from \"styled-components\";\nimport { jsx as jsx26 } from \"react/jsx-runtime\";\nvar SelectWrapper = styled22.div`\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n`;\nvar TIME_FORMAT_OPTIONS = [\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];\nvar TimeFormatSelect = ({\n value,\n onChange,\n disabled = false,\n className\n}) => {\n const handleChange = (e) => {\n onChange(e.target.value);\n };\n return /* @__PURE__ */ jsx26(SelectWrapper, { className, children: /* @__PURE__ */ jsx26(\n BaseSelect,\n {\n className: \"time-format\",\n value,\n onChange: handleChange,\n disabled,\n \"aria-label\": \"Time format selection\",\n children: TIME_FORMAT_OPTIONS.map((option) => /* @__PURE__ */ jsx26(\"option\", { value: option.value, children: option.label }, option.value))\n }\n ) });\n};\n\n// src/components/Track.tsx\nimport styled23 from \"styled-components\";\nimport { jsx as jsx27, jsxs as jsxs11 } from \"react/jsx-runtime\";\nvar Container = styled23.div.attrs((props) => ({\n style: {\n height: `${props.$waveHeight * props.$numChannels + (props.$hasClipHeaders ? CLIP_HEADER_HEIGHT : 0)}px`\n }\n}))`\n position: relative;\n display: flex;\n ${(props) => props.$width !== void 0 && `width: ${props.$width}px;`}\n`;\nvar ChannelContainer = styled23.div.attrs((props) => ({\n style: {\n paddingLeft: `${props.$offset || 0}px`\n }\n}))`\n position: relative;\n background: ${(props) => props.$backgroundColor || \"transparent\"};\n flex: 1;\n`;\nvar ControlsWrapper = styled23.div.attrs((props) => ({\n style: {\n width: `${props.$controlWidth}px`\n }\n}))`\n position: sticky;\n z-index: 102; /* Above waveform content and spectrogram labels (101), below Docusaurus navbar (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`;\nvar Track = ({\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 /* @__PURE__ */ jsxs11(\n Container,\n {\n $numChannels: numChannels,\n className,\n $waveHeight: waveHeight,\n $controlWidth: show ? controlWidth : 0,\n $width: width,\n $hasClipHeaders: hasClipHeaders,\n $isSelected: isSelected,\n children: [\n /* @__PURE__ */ jsx27(ControlsWrapper, { $controlWidth: show ? controlWidth : 0, $isSelected: isSelected, children: controls }),\n /* @__PURE__ */ jsx27(\n ChannelContainer,\n {\n $controlWidth: show ? controlWidth : 0,\n $backgroundColor: backgroundColor,\n $offset: offset,\n onClick,\n \"data-track-id\": trackId,\n children\n }\n )\n ]\n }\n );\n};\n\n// src/components/TrackControls/Button.tsx\nimport styled24 from \"styled-components\";\nvar Button = styled24.button.attrs({\n type: \"button\"\n})`\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:\n color 0.15s ease-in-out,\n background-color 0.15s ease-in-out,\n border-color 0.15s ease-in-out,\n 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 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\n// src/components/TrackControls/ButtonGroup.tsx\nimport styled25 from \"styled-components\";\nvar ButtonGroup = styled25.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\n// src/components/TrackControls/CloseButton.tsx\nimport styled26 from \"styled-components\";\nimport { X as XIcon } from \"@phosphor-icons/react\";\nimport { jsx as jsx28 } from \"react/jsx-runtime\";\nvar StyledCloseButton = styled26.button`\n position: absolute;\n left: 0;\n top: 0;\n border: none;\n background: transparent;\n color: inherit;\n cursor: pointer;\n font-size: 16px;\n padding: 2px 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n opacity: 0.7;\n transition:\n opacity 0.15s,\n color 0.15s;\n\n &:hover {\n opacity: 1;\n color: #dc3545;\n }\n`;\nvar CloseButton = ({ onClick, title = \"Remove track\" }) => /* @__PURE__ */ jsx28(StyledCloseButton, { onClick, title, children: /* @__PURE__ */ jsx28(XIcon, { size: 12, weight: \"bold\" }) });\n\n// src/components/TrackControls/Controls.tsx\nimport styled27 from \"styled-components\";\nvar Controls = styled27.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\n// src/components/TrackControls/Header.tsx\nimport styled28 from \"styled-components\";\nvar Header = styled28.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\n// src/components/TrackControls/VolumeDownIcon.tsx\nimport { SpeakerLowIcon } from \"@phosphor-icons/react\";\nimport { jsx as jsx29 } from \"react/jsx-runtime\";\nvar VolumeDownIcon = (props) => /* @__PURE__ */ jsx29(SpeakerLowIcon, { weight: \"light\", ...props });\n\n// src/components/TrackControls/VolumeUpIcon.tsx\nimport { SpeakerHighIcon } from \"@phosphor-icons/react\";\nimport { jsx as jsx30 } from \"react/jsx-runtime\";\nvar VolumeUpIcon = (props) => /* @__PURE__ */ jsx30(SpeakerHighIcon, { weight: \"light\", ...props });\n\n// src/components/TrackControls/TrashIcon.tsx\nimport { TrashIcon as PhosphorTrashIcon } from \"@phosphor-icons/react\";\nimport { jsx as jsx31 } from \"react/jsx-runtime\";\nvar TrashIcon = (props) => /* @__PURE__ */ jsx31(PhosphorTrashIcon, { weight: \"light\", ...props });\n\n// src/components/TrackControls/DotsIcon.tsx\nimport { DotsThreeIcon } from \"@phosphor-icons/react\";\nimport { jsx as jsx32 } from \"react/jsx-runtime\";\nvar DotsIcon = (props) => /* @__PURE__ */ jsx32(DotsThreeIcon, { weight: \"bold\", ...props });\n\n// src/components/TrackControls/Slider.tsx\nimport styled29 from \"styled-components\";\nvar Slider = styled29(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\n// src/components/TrackControls/SliderWrapper.tsx\nimport styled30 from \"styled-components\";\nvar SliderWrapper = styled30.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// src/components/TrackMenu.tsx\nimport React18, { useState as useState6, useEffect as useEffect7, useRef as useRef8 } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport styled31 from \"styled-components\";\nimport { jsx as jsx33, jsxs as jsxs12 } from \"react/jsx-runtime\";\nvar MenuContainer = styled31.div`\n position: relative;\n display: inline-block;\n`;\nvar MenuButton = styled31.button`\n background: none;\n border: none;\n cursor: pointer;\n padding: 2px 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n color: inherit;\n opacity: 0.7;\n\n &:hover {\n opacity: 1;\n }\n`;\nvar Dropdown = styled31.div`\n position: fixed;\n top: ${(p) => p.$top}px;\n left: ${(p) => p.$left}px;\n z-index: 10000;\n background: ${(p) => p.theme.timescaleBackgroundColor ?? \"#222\"};\n color: ${(p) => p.theme.textColor ?? \"inherit\"};\n border: 1px solid rgba(128, 128, 128, 0.4);\n border-radius: 6px;\n padding: 0.5rem 0;\n min-width: 180px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);\n`;\nvar Divider = styled31.hr`\n border: none;\n border-top: 1px solid rgba(128, 128, 128, 0.3);\n margin: 0.35rem 0;\n`;\nvar TrackMenu = ({ items: itemsProp }) => {\n const [open, setOpen] = useState6(false);\n const close = () => setOpen(false);\n const items = typeof itemsProp === \"function\" ? itemsProp(close) : itemsProp;\n const [dropdownPos, setDropdownPos] = useState6({ top: 0, left: 0 });\n const buttonRef = useRef8(null);\n const dropdownRef = useRef8(null);\n useEffect7(() => {\n if (open && buttonRef.current) {\n const rect = buttonRef.current.getBoundingClientRect();\n setDropdownPos({\n top: rect.bottom + 2,\n left: Math.max(0, rect.right - 180)\n });\n }\n }, [open]);\n useEffect7(() => {\n if (!open) return;\n const handleClick = (e) => {\n const target = e.target;\n if (buttonRef.current && !buttonRef.current.contains(target) && dropdownRef.current && !dropdownRef.current.contains(target)) {\n setOpen(false);\n }\n };\n document.addEventListener(\"mousedown\", handleClick);\n return () => document.removeEventListener(\"mousedown\", handleClick);\n }, [open]);\n return /* @__PURE__ */ jsxs12(MenuContainer, { children: [\n /* @__PURE__ */ jsx33(\n MenuButton,\n {\n ref: buttonRef,\n onClick: (e) => {\n e.stopPropagation();\n setOpen((prev) => !prev);\n },\n onMouseDown: (e) => e.stopPropagation(),\n title: \"Track menu\",\n \"aria-label\": \"Track menu\",\n children: /* @__PURE__ */ jsx33(DotsIcon, { size: 16 })\n }\n ),\n open && typeof document !== \"undefined\" && createPortal(\n /* @__PURE__ */ jsx33(\n Dropdown,\n {\n ref: dropdownRef,\n $top: dropdownPos.top,\n $left: dropdownPos.left,\n onMouseDown: (e) => e.stopPropagation(),\n children: items.map((item, index) => /* @__PURE__ */ jsxs12(React18.Fragment, { children: [\n index > 0 && /* @__PURE__ */ jsx33(Divider, {}),\n item.content\n ] }, item.id))\n }\n ),\n document.body\n )\n ] });\n};\nexport {\n AudioPosition,\n AutomaticScrollCheckbox,\n BaseButton,\n BaseCheckbox,\n BaseCheckboxLabel,\n BaseCheckboxWrapper,\n BaseControlButton,\n BaseInput,\n BaseLabel,\n BaseSelect,\n BaseSlider,\n Button,\n ButtonGroup,\n CLIP_BOUNDARY_WIDTH,\n CLIP_BOUNDARY_WIDTH_TOUCH,\n CLIP_HEADER_HEIGHT,\n Channel,\n Clip,\n ClipBoundary,\n ClipHeader,\n ClipHeaderPresentational,\n ClipViewportOriginProvider,\n CloseButton,\n Controls,\n DevicePixelRatioProvider,\n DotsIcon,\n FadeOverlay,\n Header,\n InlineLabel,\n LoopRegion,\n LoopRegionMarkers,\n MasterVolumeControl,\n Playhead,\n PlayheadWithMarker,\n Playlist,\n PlaylistErrorBoundary,\n PlaylistInfoContext,\n PlayoutProvider,\n ScreenReaderOnly,\n ScrollViewportProvider,\n Selection,\n SelectionTimeInputs,\n Slider,\n SliderWrapper,\n SmartChannel,\n SmartScale,\n SpectrogramChannel,\n SpectrogramLabels,\n StyledPlaylist,\n StyledTimeScale,\n TimeFormatSelect,\n TimeInput,\n TimeScale,\n TimescaleLoopRegion,\n Track,\n TrackControlsContext,\n TrackMenu,\n TrashIcon,\n VolumeDownIcon,\n VolumeUpIcon,\n darkTheme,\n defaultTheme,\n formatTime,\n isWaveformGradient,\n parseTime,\n pixelsToSamples,\n pixelsToSeconds,\n samplesToPixels,\n samplesToSeconds,\n secondsToPixels,\n secondsToSamples,\n useClipViewportOrigin,\n useDevicePixelRatio,\n usePlaylistInfo,\n usePlayoutStatus,\n usePlayoutStatusUpdate,\n useScrollViewport,\n useScrollViewportSelector,\n useTheme2 as useTheme,\n useTrackControls,\n useVisibleChunkIndices,\n waveformColorToCss\n};\n//# sourceMappingURL=index.mjs.map","/**\n * Provides access to the waveform data for a single audio channel.\n */\n\nfunction WaveformDataChannel(waveformData, channelIndex) {\n this._waveformData = waveformData;\n this._channelIndex = channelIndex;\n}\n\n/**\n * Returns the waveform minimum at the given index position.\n */\n\nWaveformDataChannel.prototype.min_sample = function (index) {\n var offset = (index * this._waveformData.channels + this._channelIndex) * 2;\n return this._waveformData._at(offset);\n};\n\n/**\n * Returns the waveform maximum at the given index position.\n */\n\nWaveformDataChannel.prototype.max_sample = function (index) {\n var offset = (index * this._waveformData.channels + this._channelIndex) * 2 + 1;\n return this._waveformData._at(offset);\n};\n\n/**\n * Sets the waveform minimum at the given index position.\n */\n\nWaveformDataChannel.prototype.set_min_sample = function (index, sample) {\n var offset = (index * this._waveformData.channels + this._channelIndex) * 2;\n return this._waveformData._set_at(offset, sample);\n};\n\n/**\n * Sets the waveform maximum at the given index position.\n */\n\nWaveformDataChannel.prototype.set_max_sample = function (index, sample) {\n var offset = (index * this._waveformData.channels + this._channelIndex) * 2 + 1;\n return this._waveformData._set_at(offset, sample);\n};\n\n/**\n * Returns all the waveform minimum values as an array.\n */\n\nWaveformDataChannel.prototype.min_array = function () {\n var length = this._waveformData.length;\n var values = [];\n for (var i = 0; i < length; i++) {\n values.push(this.min_sample(i));\n }\n return values;\n};\n\n/**\n * Returns all the waveform maximum values as an array.\n */\n\nWaveformDataChannel.prototype.max_array = function () {\n var length = this._waveformData.length;\n var values = [];\n for (var i = 0; i < length; i++) {\n values.push(this.max_sample(i));\n }\n return values;\n};\n\n/**\n * AudioBuffer-based WaveformData generator\n *\n * Adapted from BlockFile::CalcSummary in Audacity, with permission.\n * See https://github.com/audacity/audacity/blob/\n * 1108c1376c09166162335fab4743008cba57c4ee/src/BlockFile.cpp#L198\n */\n\nvar INT8_MAX = 127;\nvar INT8_MIN = -128;\nvar INT16_MAX = 32767;\nvar INT16_MIN = -32768;\nfunction calculateWaveformDataLength(audio_sample_count, scale) {\n var data_length = Math.floor(audio_sample_count / scale);\n var samples_remaining = audio_sample_count - data_length * scale;\n if (samples_remaining > 0) {\n data_length++;\n }\n return data_length;\n}\nfunction generateWaveformData(options) {\n var scale = options.scale;\n var amplitude_scale = options.amplitude_scale;\n var split_channels = options.split_channels;\n var length = options.length;\n var sample_rate = options.sample_rate;\n var channels = options.channels.map(function (channel) {\n return new Float32Array(channel);\n });\n var output_channels = split_channels ? channels.length : 1;\n var header_size = 24;\n var data_length = calculateWaveformDataLength(length, scale);\n var bytes_per_sample = options.bits === 8 ? 1 : 2;\n var total_size = header_size + data_length * 2 * bytes_per_sample * output_channels;\n var buffer = new ArrayBuffer(total_size);\n var data_view = new DataView(buffer);\n var scale_counter = 0;\n var offset = header_size;\n var min_value = new Array(output_channels);\n var max_value = new Array(output_channels);\n for (var channel = 0; channel < output_channels; channel++) {\n min_value[channel] = Infinity;\n max_value[channel] = -Infinity;\n }\n var range_min = options.bits === 8 ? INT8_MIN : INT16_MIN;\n var range_max = options.bits === 8 ? INT8_MAX : INT16_MAX;\n data_view.setInt32(0, 2, true); // Version\n data_view.setUint32(4, options.bits === 8, true); // Is 8 bit?\n data_view.setInt32(8, sample_rate, true); // Sample rate\n data_view.setInt32(12, scale, true); // Scale\n data_view.setInt32(16, data_length, true); // Length\n data_view.setInt32(20, output_channels, true);\n for (var i = 0; i < length; i++) {\n var sample = 0;\n if (output_channels === 1) {\n for (var _channel = 0; _channel < channels.length; ++_channel) {\n sample += channels[_channel][i];\n }\n sample = Math.floor(range_max * sample * amplitude_scale / channels.length);\n if (sample < min_value[0]) {\n min_value[0] = sample;\n if (min_value[0] < range_min) {\n min_value[0] = range_min;\n }\n }\n if (sample > max_value[0]) {\n max_value[0] = sample;\n if (max_value[0] > range_max) {\n max_value[0] = range_max;\n }\n }\n } else {\n for (var _channel2 = 0; _channel2 < output_channels; ++_channel2) {\n sample = Math.floor(range_max * channels[_channel2][i] * amplitude_scale);\n if (sample < min_value[_channel2]) {\n min_value[_channel2] = sample;\n if (min_value[_channel2] < range_min) {\n min_value[_channel2] = range_min;\n }\n }\n if (sample > max_value[_channel2]) {\n max_value[_channel2] = sample;\n if (max_value[_channel2] > range_max) {\n max_value[_channel2] = range_max;\n }\n }\n }\n }\n if (++scale_counter === scale) {\n for (var _channel3 = 0; _channel3 < output_channels; _channel3++) {\n if (options.bits === 8) {\n data_view.setInt8(offset++, min_value[_channel3]);\n data_view.setInt8(offset++, max_value[_channel3]);\n } else {\n data_view.setInt16(offset, min_value[_channel3], true);\n data_view.setInt16(offset + 2, max_value[_channel3], true);\n offset += 4;\n }\n min_value[_channel3] = Infinity;\n max_value[_channel3] = -Infinity;\n }\n scale_counter = 0;\n }\n }\n if (scale_counter > 0) {\n for (var _channel4 = 0; _channel4 < output_channels; _channel4++) {\n if (options.bits === 8) {\n data_view.setInt8(offset++, min_value[_channel4]);\n data_view.setInt8(offset++, max_value[_channel4]);\n } else {\n data_view.setInt16(offset, min_value[_channel4], true);\n data_view.setInt16(offset + 2, max_value[_channel4], true);\n }\n }\n }\n return buffer;\n}\n\nfunction _typeof(o) {\n \"@babel/helpers - typeof\";\n\n return _typeof = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function (o) {\n return typeof o;\n } : function (o) {\n return o && \"function\" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? \"symbol\" : typeof o;\n }, _typeof(o);\n}\n\nfunction isJsonWaveformData(data) {\n return data && _typeof(data) === 'object' && 'sample_rate' in data && 'samples_per_pixel' in data && 'bits' in data && 'length' in data && 'data' in data;\n}\nfunction isBinaryWaveformData(data) {\n var isCompatible = data && _typeof(data) === 'object' && 'byteLength' in data;\n if (isCompatible) {\n var view = new DataView(data);\n var version = view.getInt32(0, true);\n if (version !== 1 && version !== 2) {\n throw new TypeError('WaveformData.create(): This waveform data version not supported');\n }\n }\n return isCompatible;\n}\nfunction convertJsonToBinary(data) {\n var waveformData = data.data;\n var channels = data.channels || 1;\n var header_size = 24; // version 2\n var bytes_per_sample = data.bits === 8 ? 1 : 2;\n var expected_length = data.length * 2 * channels;\n if (waveformData.length !== expected_length) {\n throw new Error('WaveformData.create(): Length mismatch in JSON waveform data');\n }\n var total_size = header_size + waveformData.length * bytes_per_sample;\n var array_buffer = new ArrayBuffer(total_size);\n var data_object = new DataView(array_buffer);\n data_object.setInt32(0, 2, true); // Version\n data_object.setUint32(4, data.bits === 8, true);\n data_object.setInt32(8, data.sample_rate, true);\n data_object.setInt32(12, data.samples_per_pixel, true);\n data_object.setInt32(16, data.length, true);\n data_object.setInt32(20, channels, true);\n var index = header_size;\n if (data.bits === 8) {\n for (var i = 0; i < waveformData.length; i++) {\n data_object.setInt8(index++, waveformData[i], true);\n }\n } else {\n for (var _i = 0; _i < waveformData.length; _i++) {\n data_object.setInt16(index, waveformData[_i], true);\n index += 2;\n }\n }\n return array_buffer;\n}\n\nfunction isNullOrUndefined(value) {\n return value === undefined || value === null;\n}\n\nfunction decodeBase64(base64, enableUnicode) {\n var binaryString = atob(base64);\n if (enableUnicode) {\n var binaryView = new Uint8Array(binaryString.length);\n for (var i = 0, n = binaryString.length; i < n; ++i) {\n binaryView[i] = binaryString.charCodeAt(i);\n }\n return String.fromCharCode.apply(null, new Uint16Array(binaryView.buffer));\n }\n return binaryString;\n}\n\nfunction createURL(base64, sourcemapArg, enableUnicodeArg) {\n var sourcemap = sourcemapArg === undefined ? null : sourcemapArg;\n var enableUnicode = enableUnicodeArg === undefined ? false : enableUnicodeArg;\n var source = decodeBase64(base64, enableUnicode);\n var start = source.indexOf('\\n', 10) + 1;\n var body = source.substring(start) + (sourcemap ? '\\/\\/# sourceMappingURL=' + sourcemap : '');\n var blob = new Blob([body], { type: 'application/javascript' });\n return URL.createObjectURL(blob);\n}\n\nfunction createBase64WorkerFactory(base64, sourcemapArg, enableUnicodeArg) {\n var url;\n return function WorkerFactory(options) {\n url = url || createURL(base64, sourcemapArg, enableUnicodeArg);\n return new Worker(url, options);\n };\n}\n\nvar WorkerFactory = /*#__PURE__*/createBase64WorkerFactory('Lyogcm9sbHVwLXBsdWdpbi13ZWItd29ya2VyLWxvYWRlciAqLwooZnVuY3Rpb24gKCkgewogICd1c2Ugc3RyaWN0JzsKCiAgLyoqCiAgICogQXVkaW9CdWZmZXItYmFzZWQgV2F2ZWZvcm1EYXRhIGdlbmVyYXRvcgogICAqCiAgICogQWRhcHRlZCBmcm9tIEJsb2NrRmlsZTo6Q2FsY1N1bW1hcnkgaW4gQXVkYWNpdHksIHdpdGggcGVybWlzc2lvbi4KICAgKiBTZWUgaHR0cHM6Ly9naXRodWIuY29tL2F1ZGFjaXR5L2F1ZGFjaXR5L2Jsb2IvCiAgICogICAxMTA4YzEzNzZjMDkxNjYxNjIzMzVmYWI0NzQzMDA4Y2JhNTdjNGVlL3NyYy9CbG9ja0ZpbGUuY3BwI0wxOTgKICAgKi8KCiAgdmFyIElOVDhfTUFYID0gMTI3OwogIHZhciBJTlQ4X01JTiA9IC0xMjg7CiAgdmFyIElOVDE2X01BWCA9IDMyNzY3OwogIHZhciBJTlQxNl9NSU4gPSAtMzI3Njg7CiAgZnVuY3Rpb24gY2FsY3VsYXRlV2F2ZWZvcm1EYXRhTGVuZ3RoKGF1ZGlvX3NhbXBsZV9jb3VudCwgc2NhbGUpIHsKICAgIHZhciBkYXRhX2xlbmd0aCA9IE1hdGguZmxvb3IoYXVkaW9fc2FtcGxlX2NvdW50IC8gc2NhbGUpOwogICAgdmFyIHNhbXBsZXNfcmVtYWluaW5nID0gYXVkaW9fc2FtcGxlX2NvdW50IC0gZGF0YV9sZW5ndGggKiBzY2FsZTsKICAgIGlmIChzYW1wbGVzX3JlbWFpbmluZyA+IDApIHsKICAgICAgZGF0YV9sZW5ndGgrKzsKICAgIH0KICAgIHJldHVybiBkYXRhX2xlbmd0aDsKICB9CiAgZnVuY3Rpb24gZ2VuZXJhdGVXYXZlZm9ybURhdGEob3B0aW9ucykgewogICAgdmFyIHNjYWxlID0gb3B0aW9ucy5zY2FsZTsKICAgIHZhciBhbXBsaXR1ZGVfc2NhbGUgPSBvcHRpb25zLmFtcGxpdHVkZV9zY2FsZTsKICAgIHZhciBzcGxpdF9jaGFubmVscyA9IG9wdGlvbnMuc3BsaXRfY2hhbm5lbHM7CiAgICB2YXIgbGVuZ3RoID0gb3B0aW9ucy5sZW5ndGg7CiAgICB2YXIgc2FtcGxlX3JhdGUgPSBvcHRpb25zLnNhbXBsZV9yYXRlOwogICAgdmFyIGNoYW5uZWxzID0gb3B0aW9ucy5jaGFubmVscy5tYXAoZnVuY3Rpb24gKGNoYW5uZWwpIHsKICAgICAgcmV0dXJuIG5ldyBGbG9hdDMyQXJyYXkoY2hhbm5lbCk7CiAgICB9KTsKICAgIHZhciBvdXRwdXRfY2hhbm5lbHMgPSBzcGxpdF9jaGFubmVscyA/IGNoYW5uZWxzLmxlbmd0aCA6IDE7CiAgICB2YXIgaGVhZGVyX3NpemUgPSAyNDsKICAgIHZhciBkYXRhX2xlbmd0aCA9IGNhbGN1bGF0ZVdhdmVmb3JtRGF0YUxlbmd0aChsZW5ndGgsIHNjYWxlKTsKICAgIHZhciBieXRlc19wZXJfc2FtcGxlID0gb3B0aW9ucy5iaXRzID09PSA4ID8gMSA6IDI7CiAgICB2YXIgdG90YWxfc2l6ZSA9IGhlYWRlcl9zaXplICsgZGF0YV9sZW5ndGggKiAyICogYnl0ZXNfcGVyX3NhbXBsZSAqIG91dHB1dF9jaGFubmVsczsKICAgIHZhciBidWZmZXIgPSBuZXcgQXJyYXlCdWZmZXIodG90YWxfc2l6ZSk7CiAgICB2YXIgZGF0YV92aWV3ID0gbmV3IERhdGFWaWV3KGJ1ZmZlcik7CiAgICB2YXIgc2NhbGVfY291bnRlciA9IDA7CiAgICB2YXIgb2Zmc2V0ID0gaGVhZGVyX3NpemU7CiAgICB2YXIgbWluX3ZhbHVlID0gbmV3IEFycmF5KG91dHB1dF9jaGFubmVscyk7CiAgICB2YXIgbWF4X3ZhbHVlID0gbmV3IEFycmF5KG91dHB1dF9jaGFubmVscyk7CiAgICBmb3IgKHZhciBjaGFubmVsID0gMDsgY2hhbm5lbCA8IG91dHB1dF9jaGFubmVsczsgY2hhbm5lbCsrKSB7CiAgICAgIG1pbl92YWx1ZVtjaGFubmVsXSA9IEluZmluaXR5OwogICAgICBtYXhfdmFsdWVbY2hhbm5lbF0gPSAtSW5maW5pdHk7CiAgICB9CiAgICB2YXIgcmFuZ2VfbWluID0gb3B0aW9ucy5iaXRzID09PSA4ID8gSU5UOF9NSU4gOiBJTlQxNl9NSU47CiAgICB2YXIgcmFuZ2VfbWF4ID0gb3B0aW9ucy5iaXRzID09PSA4ID8gSU5UOF9NQVggOiBJTlQxNl9NQVg7CiAgICBkYXRhX3ZpZXcuc2V0SW50MzIoMCwgMiwgdHJ1ZSk7IC8vIFZlcnNpb24KICAgIGRhdGFfdmlldy5zZXRVaW50MzIoNCwgb3B0aW9ucy5iaXRzID09PSA4LCB0cnVlKTsgLy8gSXMgOCBiaXQ/CiAgICBkYXRhX3ZpZXcuc2V0SW50MzIoOCwgc2FtcGxlX3JhdGUsIHRydWUpOyAvLyBTYW1wbGUgcmF0ZQogICAgZGF0YV92aWV3LnNldEludDMyKDEyLCBzY2FsZSwgdHJ1ZSk7IC8vIFNjYWxlCiAgICBkYXRhX3ZpZXcuc2V0SW50MzIoMTYsIGRhdGFfbGVuZ3RoLCB0cnVlKTsgLy8gTGVuZ3RoCiAgICBkYXRhX3ZpZXcuc2V0SW50MzIoMjAsIG91dHB1dF9jaGFubmVscywgdHJ1ZSk7CiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxlbmd0aDsgaSsrKSB7CiAgICAgIHZhciBzYW1wbGUgPSAwOwogICAgICBpZiAob3V0cHV0X2NoYW5uZWxzID09PSAxKSB7CiAgICAgICAgZm9yICh2YXIgX2NoYW5uZWwgPSAwOyBfY2hhbm5lbCA8IGNoYW5uZWxzLmxlbmd0aDsgKytfY2hhbm5lbCkgewogICAgICAgICAgc2FtcGxlICs9IGNoYW5uZWxzW19jaGFubmVsXVtpXTsKICAgICAgICB9CiAgICAgICAgc2FtcGxlID0gTWF0aC5mbG9vcihyYW5nZV9tYXggKiBzYW1wbGUgKiBhbXBsaXR1ZGVfc2NhbGUgLyBjaGFubmVscy5sZW5ndGgpOwogICAgICAgIGlmIChzYW1wbGUgPCBtaW5fdmFsdWVbMF0pIHsKICAgICAgICAgIG1pbl92YWx1ZVswXSA9IHNhbXBsZTsKICAgICAgICAgIGlmIChtaW5fdmFsdWVbMF0gPCByYW5nZV9taW4pIHsKICAgICAgICAgICAgbWluX3ZhbHVlWzBdID0gcmFuZ2VfbWluOwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgICBpZiAoc2FtcGxlID4gbWF4X3ZhbHVlWzBdKSB7CiAgICAgICAgICBtYXhfdmFsdWVbMF0gPSBzYW1wbGU7CiAgICAgICAgICBpZiAobWF4X3ZhbHVlWzBdID4gcmFuZ2VfbWF4KSB7CiAgICAgICAgICAgIG1heF92YWx1ZVswXSA9IHJhbmdlX21heDsKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIH0gZWxzZSB7CiAgICAgICAgZm9yICh2YXIgX2NoYW5uZWwyID0gMDsgX2NoYW5uZWwyIDwgb3V0cHV0X2NoYW5uZWxzOyArK19jaGFubmVsMikgewogICAgICAgICAgc2FtcGxlID0gTWF0aC5mbG9vcihyYW5nZV9tYXggKiBjaGFubmVsc1tfY2hhbm5lbDJdW2ldICogYW1wbGl0dWRlX3NjYWxlKTsKICAgICAgICAgIGlmIChzYW1wbGUgPCBtaW5fdmFsdWVbX2NoYW5uZWwyXSkgewogICAgICAgICAgICBtaW5fdmFsdWVbX2NoYW5uZWwyXSA9IHNhbXBsZTsKICAgICAgICAgICAgaWYgKG1pbl92YWx1ZVtfY2hhbm5lbDJdIDwgcmFuZ2VfbWluKSB7CiAgICAgICAgICAgICAgbWluX3ZhbHVlW19jaGFubmVsMl0gPSByYW5nZV9taW47CiAgICAgICAgICAgIH0KICAgICAgICAgIH0KICAgICAgICAgIGlmIChzYW1wbGUgPiBtYXhfdmFsdWVbX2NoYW5uZWwyXSkgewogICAgICAgICAgICBtYXhfdmFsdWVbX2NoYW5uZWwyXSA9IHNhbXBsZTsKICAgICAgICAgICAgaWYgKG1heF92YWx1ZVtfY2hhbm5lbDJdID4gcmFuZ2VfbWF4KSB7CiAgICAgICAgICAgICAgbWF4X3ZhbHVlW19jaGFubmVsMl0gPSByYW5nZV9tYXg7CiAgICAgICAgICAgIH0KICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIH0KICAgICAgaWYgKCsrc2NhbGVfY291bnRlciA9PT0gc2NhbGUpIHsKICAgICAgICBmb3IgKHZhciBfY2hhbm5lbDMgPSAwOyBfY2hhbm5lbDMgPCBvdXRwdXRfY2hhbm5lbHM7IF9jaGFubmVsMysrKSB7CiAgICAgICAgICBpZiAob3B0aW9ucy5iaXRzID09PSA4KSB7CiAgICAgICAgICAgIGRhdGFfdmlldy5zZXRJbnQ4KG9mZnNldCsrLCBtaW5fdmFsdWVbX2NoYW5uZWwzXSk7CiAgICAgICAgICAgIGRhdGFfdmlldy5zZXRJbnQ4KG9mZnNldCsrLCBtYXhfdmFsdWVbX2NoYW5uZWwzXSk7CiAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICBkYXRhX3ZpZXcuc2V0SW50MTYob2Zmc2V0LCBtaW5fdmFsdWVbX2NoYW5uZWwzXSwgdHJ1ZSk7CiAgICAgICAgICAgIGRhdGFfdmlldy5zZXRJbnQxNihvZmZzZXQgKyAyLCBtYXhfdmFsdWVbX2NoYW5uZWwzXSwgdHJ1ZSk7CiAgICAgICAgICAgIG9mZnNldCArPSA0OwogICAgICAgICAgfQogICAgICAgICAgbWluX3ZhbHVlW19jaGFubmVsM10gPSBJbmZpbml0eTsKICAgICAgICAgIG1heF92YWx1ZVtfY2hhbm5lbDNdID0gLUluZmluaXR5OwogICAgICAgIH0KICAgICAgICBzY2FsZV9jb3VudGVyID0gMDsKICAgICAgfQogICAgfQogICAgaWYgKHNjYWxlX2NvdW50ZXIgPiAwKSB7CiAgICAgIGZvciAodmFyIF9jaGFubmVsNCA9IDA7IF9jaGFubmVsNCA8IG91dHB1dF9jaGFubmVsczsgX2NoYW5uZWw0KyspIHsKICAgICAgICBpZiAob3B0aW9ucy5iaXRzID09PSA4KSB7CiAgICAgICAgICBkYXRhX3ZpZXcuc2V0SW50OChvZmZzZXQrKywgbWluX3ZhbHVlW19jaGFubmVsNF0pOwogICAgICAgICAgZGF0YV92aWV3LnNldEludDgob2Zmc2V0KyssIG1heF92YWx1ZVtfY2hhbm5lbDRdKTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgZGF0YV92aWV3LnNldEludDE2KG9mZnNldCwgbWluX3ZhbHVlW19jaGFubmVsNF0sIHRydWUpOwogICAgICAgICAgZGF0YV92aWV3LnNldEludDE2KG9mZnNldCArIDIsIG1heF92YWx1ZVtfY2hhbm5lbDRdLCB0cnVlKTsKICAgICAgICB9CiAgICAgIH0KICAgIH0KICAgIHJldHVybiBidWZmZXI7CiAgfQoKICBvbm1lc3NhZ2UgPSBmdW5jdGlvbiBvbm1lc3NhZ2UoZXZ0KSB7CiAgICB2YXIgYnVmZmVyID0gZ2VuZXJhdGVXYXZlZm9ybURhdGEoZXZ0LmRhdGEpOwoKICAgIC8vIFRyYW5zZmVyIGJ1ZmZlciB0byB0aGUgY2FsbGluZyB0aHJlYWQKICAgIHRoaXMucG9zdE1lc3NhZ2UoYnVmZmVyLCBbYnVmZmVyXSk7CiAgICB0aGlzLmNsb3NlKCk7CiAgfTsKCn0pKCk7Ci8vIyBzb3VyY2VNYXBwaW5nVVJMPXdhdmVmb3JtLWRhdGEtd29ya2VyLmpzLm1hcAoK', null, false);\n/* eslint-enable */\n\n/**\n * Provides access to waveform data.\n */\n\nfunction WaveformData(data) {\n if (isJsonWaveformData(data)) {\n data = convertJsonToBinary(data);\n }\n if (isBinaryWaveformData(data)) {\n this._data = new DataView(data);\n this._offset = this._version() === 2 ? 24 : 20;\n this._channels = [];\n for (var channel = 0; channel < this.channels; channel++) {\n this._channels[channel] = new WaveformDataChannel(this, channel);\n }\n } else {\n throw new TypeError('WaveformData.create(): Unknown data format');\n }\n}\nvar defaultOptions = {\n scale: 512,\n bits: 8,\n amplitude_scale: 1.0,\n split_channels: false,\n disable_worker: false\n};\nfunction getOptions(options) {\n var opts = {\n scale: options.scale || defaultOptions.scale,\n bits: options.bits || defaultOptions.bits,\n amplitude_scale: options.amplitude_scale || defaultOptions.amplitude_scale,\n split_channels: options.split_channels || defaultOptions.split_channels,\n disable_worker: options.disable_worker || defaultOptions.disable_worker\n };\n return opts;\n}\nfunction getChannelData(audio_buffer) {\n var channels = [];\n for (var i = 0; i < audio_buffer.numberOfChannels; ++i) {\n channels.push(audio_buffer.getChannelData(i).buffer);\n }\n return channels;\n}\nfunction createFromAudioBuffer(audio_buffer, options, callback) {\n var channels = getChannelData(audio_buffer);\n if (options.disable_worker) {\n var buffer = generateWaveformData({\n scale: options.scale,\n bits: options.bits,\n amplitude_scale: options.amplitude_scale,\n split_channels: options.split_channels,\n length: audio_buffer.length,\n sample_rate: audio_buffer.sampleRate,\n channels: channels\n });\n callback(undefined, new WaveformData(buffer), audio_buffer);\n } else {\n var worker = new WorkerFactory();\n worker.onmessage = function (evt) {\n callback(undefined, new WaveformData(evt.data), audio_buffer);\n };\n worker.postMessage({\n scale: options.scale,\n bits: options.bits,\n amplitude_scale: options.amplitude_scale,\n split_channels: options.split_channels,\n length: audio_buffer.length,\n sample_rate: audio_buffer.sampleRate,\n channels: channels\n }, channels);\n }\n}\nfunction createFromArrayBuffer(audioContext, audioData, options, callback) {\n // The following function is a workaround for a Webkit bug where decodeAudioData\n // invokes the errorCallback with null instead of a DOMException.\n // See https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-decodeaudiodata\n // and http://stackoverflow.com/q/10365335/103396\n\n function errorCallback(error) {\n if (!error) {\n error = new DOMException('EncodingError');\n }\n callback(error);\n // prevent double-calling the callback on errors:\n callback = function callback() {};\n }\n var promise = audioContext.decodeAudioData(audioData, function (audio_buffer) {\n createFromAudioBuffer(audio_buffer, options, callback);\n }, errorCallback);\n if (promise) {\n promise.catch(errorCallback);\n }\n}\n\n/**\n * Creates and returns a WaveformData instance from the given waveform data.\n */\n\nWaveformData.create = function create(data) {\n return new WaveformData(data);\n};\n\n/**\n * Creates a WaveformData instance from audio.\n */\n\nWaveformData.createFromAudio = function (options, callback) {\n var opts = getOptions(options);\n if (options.audio_context && options.array_buffer) {\n return createFromArrayBuffer(options.audio_context, options.array_buffer, opts, callback);\n } else if (options.audio_buffer) {\n return createFromAudioBuffer(options.audio_buffer, opts, callback);\n } else {\n throw new TypeError(\n // eslint-disable-next-line\n 'WaveformData.createFromAudio(): Pass either an AudioContext and ArrayBuffer, or an AudioBuffer object');\n }\n};\nfunction WaveformResampler(options) {\n this._inputData = options.waveformData;\n\n // Scale we want to reach\n this._output_samples_per_pixel = options.scale;\n this._scale = this._inputData.scale; // scale we are coming from\n\n // The amount of data we want to resample i.e. final zoom want to resample\n // all data but for intermediate zoom we want to resample subset\n this._input_buffer_size = this._inputData.length;\n var input_buffer_length_samples = this._input_buffer_size * this._inputData.scale;\n var output_buffer_length_samples = Math.ceil(input_buffer_length_samples / this._output_samples_per_pixel);\n var output_header_size = 24; // version 2\n var bytes_per_sample = this._inputData.bits === 8 ? 1 : 2;\n var total_size = output_header_size + output_buffer_length_samples * 2 * this._inputData.channels * bytes_per_sample;\n this._output_data = new ArrayBuffer(total_size);\n this.output_dataview = new DataView(this._output_data);\n this.output_dataview.setInt32(0, 2, true); // Version\n this.output_dataview.setUint32(4, this._inputData.bits === 8, true); // Is 8 bit?\n this.output_dataview.setInt32(8, this._inputData.sample_rate, true);\n this.output_dataview.setInt32(12, this._output_samples_per_pixel, true);\n this.output_dataview.setInt32(16, output_buffer_length_samples, true);\n this.output_dataview.setInt32(20, this._inputData.channels, true);\n this._outputWaveformData = new WaveformData(this._output_data);\n this._input_index = 0;\n this._output_index = 0;\n var channels = this._inputData.channels;\n this._min = new Array(channels);\n this._max = new Array(channels);\n for (var channel = 0; channel < channels; ++channel) {\n if (this._input_buffer_size > 0) {\n this._min[channel] = this._inputData.channel(channel).min_sample(this._input_index);\n this._max[channel] = this._inputData.channel(channel).max_sample(this._input_index);\n } else {\n this._min[channel] = 0;\n this._max[channel] = 0;\n }\n }\n this._min_value = this._inputData.bits === 8 ? -128 : -32768;\n this._max_value = this._inputData.bits === 8 ? 127 : 32767;\n this._where = 0;\n this._prev_where = 0;\n this._stop = 0;\n this._last_input_index = 0;\n}\nWaveformResampler.prototype.sample_at_pixel = function (x) {\n return Math.floor(x * this._output_samples_per_pixel);\n};\nWaveformResampler.prototype.next = function () {\n var count = 0;\n var total = 1000;\n var channels = this._inputData.channels;\n var channel;\n while (this._input_index < this._input_buffer_size && count < total) {\n while (Math.floor(this.sample_at_pixel(this._output_index) / this._scale) === this._input_index) {\n if (this._output_index > 0) {\n for (var i = 0; i < channels; ++i) {\n channel = this._outputWaveformData.channel(i);\n channel.set_min_sample(this._output_index - 1, this._min[i]);\n channel.set_max_sample(this._output_index - 1, this._max[i]);\n }\n }\n this._last_input_index = this._input_index;\n this._output_index++;\n this._where = this.sample_at_pixel(this._output_index);\n this._prev_where = this.sample_at_pixel(this._output_index - 1);\n if (this._where !== this._prev_where) {\n for (var _i = 0; _i < channels; ++_i) {\n this._min[_i] = this._max_value;\n this._max[_i] = this._min_value;\n }\n }\n }\n this._where = this.sample_at_pixel(this._output_index);\n this._stop = Math.floor(this._where / this._scale);\n if (this._stop > this._input_buffer_size) {\n this._stop = this._input_buffer_size;\n }\n while (this._input_index < this._stop) {\n for (var _i2 = 0; _i2 < channels; ++_i2) {\n channel = this._inputData.channel(_i2);\n var value = channel.min_sample(this._input_index);\n if (value < this._min[_i2]) {\n this._min[_i2] = value;\n }\n value = channel.max_sample(this._input_index);\n if (value > this._max[_i2]) {\n this._max[_i2] = value;\n }\n }\n this._input_index++;\n }\n count++;\n }\n if (this._input_index < this._input_buffer_size) {\n // More to do\n return false;\n } else {\n // Done\n if (this._input_index !== this._last_input_index) {\n for (var _i3 = 0; _i3 < channels; ++_i3) {\n channel = this._outputWaveformData.channel(_i3);\n channel.set_min_sample(this._output_index - 1, this._min[_i3]);\n channel.set_max_sample(this._output_index - 1, this._max[_i3]);\n }\n }\n return true;\n }\n};\nWaveformResampler.prototype.getOutputData = function () {\n return this._output_data;\n};\nWaveformData.prototype = {\n _getResampleOptions: function _getResampleOptions(options) {\n var opts = {};\n opts.scale = options.scale;\n opts.width = options.width;\n if (!isNullOrUndefined(opts.width) && (typeof opts.width !== 'number' || opts.width <= 0)) {\n throw new RangeError('WaveformData.resample(): width should be a positive integer value');\n }\n if (!isNullOrUndefined(opts.scale) && (typeof opts.scale !== 'number' || opts.scale <= 0)) {\n throw new RangeError('WaveformData.resample(): scale should be a positive integer value');\n }\n if (!opts.scale && !opts.width) {\n throw new Error('WaveformData.resample(): Missing scale or width option');\n }\n if (opts.width) {\n // Calculate the target scale for the resampled waveform\n opts.scale = Math.floor(this.duration * this.sample_rate / opts.width);\n }\n if (opts.scale < this.scale) {\n throw new Error('WaveformData.resample(): Zoom level ' + opts.scale + ' too low, minimum: ' + this.scale);\n }\n opts.abortSignal = options.abortSignal;\n return opts;\n },\n resample: function resample(options) {\n options = this._getResampleOptions(options);\n options.waveformData = this;\n var resampler = new WaveformResampler(options);\n while (!resampler.next()) {\n // nothing\n }\n return new WaveformData(resampler.getOutputData());\n },\n /**\n * Concatenates with one or more other waveforms, returning a new WaveformData object.\n */\n\n concat: function concat() {\n var self = this;\n var otherWaveforms = Array.prototype.slice.call(arguments);\n\n // Check that all the supplied waveforms are compatible\n otherWaveforms.forEach(function (otherWaveform) {\n if (self.channels !== otherWaveform.channels || self.sample_rate !== otherWaveform.sample_rate || self.bits !== otherWaveform.bits || self.scale !== otherWaveform.scale) {\n throw new Error('WaveformData.concat(): Waveforms are incompatible');\n }\n });\n var combinedBuffer = this._concatBuffers.apply(this, otherWaveforms);\n return WaveformData.create(combinedBuffer);\n },\n /**\n * Returns a new ArrayBuffer with the concatenated waveform.\n * All waveforms must have identical metadata (version, channels, etc)\n */\n\n _concatBuffers: function _concatBuffers() {\n var otherWaveforms = Array.prototype.slice.call(arguments);\n var headerSize = this._offset;\n var totalSize = headerSize;\n var totalDataLength = 0;\n var bufferCollection = [this].concat(otherWaveforms).map(function (w) {\n return w._data.buffer;\n });\n for (var i = 0; i < bufferCollection.length; i++) {\n var buffer = bufferCollection[i];\n var dataSize = new DataView(buffer).getInt32(16, true);\n totalSize += buffer.byteLength - headerSize;\n totalDataLength += dataSize;\n }\n var totalBuffer = new ArrayBuffer(totalSize);\n var sourceHeader = new DataView(bufferCollection[0]);\n var totalBufferView = new DataView(totalBuffer);\n\n // Copy the header from the first chunk\n for (var _i4 = 0; _i4 < headerSize; _i4++) {\n totalBufferView.setUint8(_i4, sourceHeader.getUint8(_i4));\n }\n\n // Rewrite the data-length header item to reflect all of the samples concatenated together\n totalBufferView.setInt32(16, totalDataLength, true);\n var offset = 0;\n var dataOfTotalBuffer = new Uint8Array(totalBuffer, headerSize);\n for (var _i5 = 0; _i5 < bufferCollection.length; _i5++) {\n var _buffer = bufferCollection[_i5];\n dataOfTotalBuffer.set(new Uint8Array(_buffer, headerSize), offset);\n offset += _buffer.byteLength - headerSize;\n }\n return totalBuffer;\n },\n slice: function slice(options) {\n var startIndex = 0;\n var endIndex = 0;\n if (!isNullOrUndefined(options.startIndex) && !isNullOrUndefined(options.endIndex)) {\n startIndex = options.startIndex;\n endIndex = options.endIndex;\n } else if (!isNullOrUndefined(options.startTime) && !isNullOrUndefined(options.endTime)) {\n startIndex = this.at_time(options.startTime);\n endIndex = this.at_time(options.endTime);\n }\n if (startIndex < 0) {\n throw new RangeError('startIndex or startTime must not be negative');\n }\n if (endIndex < 0) {\n throw new RangeError('endIndex or endTime must not be negative');\n }\n if (startIndex > this.length) {\n startIndex = this.length;\n }\n if (endIndex > this.length) {\n endIndex = this.length;\n }\n if (startIndex > endIndex) {\n startIndex = endIndex;\n }\n var length = endIndex - startIndex;\n var header_size = 24; // Version 2\n var bytes_per_sample = this.bits === 8 ? 1 : 2;\n var total_size = header_size + length * 2 * this.channels * bytes_per_sample;\n var output_data = new ArrayBuffer(total_size);\n var output_dataview = new DataView(output_data);\n output_dataview.setInt32(0, 2, true); // Version\n output_dataview.setUint32(4, this.bits === 8, true); // Is 8 bit?\n output_dataview.setInt32(8, this.sample_rate, true);\n output_dataview.setInt32(12, this.scale, true);\n output_dataview.setInt32(16, length, true);\n output_dataview.setInt32(20, this.channels, true);\n for (var i = 0; i < length * this.channels * 2; i++) {\n var sample = this._at(startIndex * this.channels * 2 + i);\n if (this.bits === 8) {\n output_dataview.setInt8(header_size + i, sample);\n } else {\n output_dataview.setInt16(header_size + i * 2, sample, true);\n }\n }\n return new WaveformData(output_data);\n },\n /**\n * Returns the data format version number.\n */\n\n _version: function _version() {\n return this._data.getInt32(0, true);\n },\n /**\n * Returns the length of the waveform, in pixels.\n */\n\n get length() {\n return this._data.getUint32(16, true);\n },\n /**\n * Returns the number of bits per sample, either 8 or 16.\n */\n\n get bits() {\n var bits = Boolean(this._data.getUint32(4, true));\n return bits ? 8 : 16;\n },\n /**\n * Returns the (approximate) duration of the audio file, in seconds.\n */\n\n get duration() {\n return this.length * this.scale / this.sample_rate;\n },\n /**\n * Returns the number of pixels per second.\n */\n\n get pixels_per_second() {\n return this.sample_rate / this.scale;\n },\n /**\n * Returns the amount of time represented by a single pixel, in seconds.\n */\n\n get seconds_per_pixel() {\n return this.scale / this.sample_rate;\n },\n /**\n * Returns the number of waveform channels.\n */\n\n get channels() {\n if (this._version() === 2) {\n return this._data.getInt32(20, true);\n } else {\n return 1;\n }\n },\n /**\n * Returns a waveform channel.\n */\n\n channel: function channel(index) {\n if (index >= 0 && index < this._channels.length) {\n return this._channels[index];\n } else {\n throw new RangeError('Invalid channel: ' + index);\n }\n },\n /**\n * Returns the number of audio samples per second.\n */\n\n get sample_rate() {\n return this._data.getInt32(8, true);\n },\n /**\n * Returns the number of audio samples per pixel.\n */\n\n get scale() {\n return this._data.getInt32(12, true);\n },\n /**\n * Returns a waveform data value at a specific offset.\n */\n\n _at: function at_sample(index) {\n if (this.bits === 8) {\n return this._data.getInt8(this._offset + index);\n } else {\n return this._data.getInt16(this._offset + index * 2, true);\n }\n },\n /**\n * Sets a waveform data value at a specific offset.\n */\n\n _set_at: function set_at(index, sample) {\n if (this.bits === 8) {\n return this._data.setInt8(this._offset + index, sample);\n } else {\n return this._data.setInt16(this._offset + index * 2, sample, true);\n }\n },\n /**\n * Returns the waveform data index position for a given time.\n */\n\n at_time: function at_time(time) {\n return Math.floor(time * this.sample_rate / this.scale);\n },\n /**\n * Returns the time in seconds for a given index.\n */\n\n time: function time(index) {\n return index * this.scale / this.sample_rate;\n },\n /**\n * Returns an object containing the waveform data.\n */\n\n toJSON: function toJSON() {\n var waveform = {\n version: 2,\n channels: this.channels,\n sample_rate: this.sample_rate,\n samples_per_pixel: this.scale,\n bits: this.bits,\n length: this.length,\n data: []\n };\n for (var i = 0; i < this.length; i++) {\n for (var channel = 0; channel < this.channels; channel++) {\n waveform.data.push(this.channel(channel).min_sample(i));\n waveform.data.push(this.channel(channel).max_sample(i));\n }\n }\n return waveform;\n },\n /**\n * Returns the waveform data in binary format as an ArrayBuffer.\n */\n\n toArrayBuffer: function toArrayBuffer() {\n return this._data.buffer;\n }\n};\n\nexport { WaveformData as default };\n","/**\n * Waveform Data Loader\n *\n * Utilities for loading pre-computed waveform data in waveform-data.js format.\n * Supports both binary (.dat) and JSON formats from BBC's audiowaveform tool.\n */\n\nimport WaveformData from 'waveform-data';\nimport type { PeakData, Peaks } from '@waveform-playlist/core';\n\n/**\n * Load waveform data from a .dat or .json file\n *\n * @param src - URL to waveform data file (.dat or .json)\n * @returns WaveformData instance\n */\nexport async function loadWaveformData(src: string): Promise<WaveformData> {\n const response = await fetch(src);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch waveform data: ${response.statusText}`);\n }\n\n // Check file extension to determine format\n const isBinary = src.endsWith('.dat');\n\n if (isBinary) {\n const arrayBuffer = await response.arrayBuffer();\n return WaveformData.create(arrayBuffer);\n } else {\n const json = await response.json();\n return WaveformData.create(json);\n }\n}\n\n/**\n * Convert WaveformData to our internal Peaks format\n *\n * @param waveformData - WaveformData instance from waveform-data.js\n * @param channelIndex - Channel index (0 for mono/left, 1 for right)\n * @returns Peaks data with alternating min/max values, preserving original bit depth\n */\nexport function waveformDataToPeaks(\n waveformData: WaveformData,\n channelIndex: number = 0\n): { data: Int8Array | Int16Array; bits: 8 | 16; length: number; sampleRate: number } {\n const channel = waveformData.channel(channelIndex);\n const bits = waveformData.bits as 8 | 16;\n\n // Get the min/max arrays to determine length\n const minArray = channel.min_array();\n const maxArray = channel.max_array();\n const length = minArray.length;\n\n // Use appropriate typed array based on source file bit depth\n // 8-bit: values range from -128 to 127\n // 16-bit: values range from -32768 to 32767\n const peaks = bits === 8 ? new Int8Array(length * 2) : new Int16Array(length * 2);\n\n // Interleave min/max pairs\n for (let i = 0; i < length; i++) {\n peaks[i * 2] = minArray[i];\n peaks[i * 2 + 1] = maxArray[i];\n }\n\n return {\n data: peaks,\n bits,\n length,\n sampleRate: waveformData.sample_rate,\n };\n}\n\n/**\n * Load waveform data file and convert to Peaks format in one step\n *\n * @param src - URL to waveform data file (.dat or .json)\n * @param channelIndex - Channel index (default: 0)\n * @returns Peaks data ready for rendering\n */\nexport async function loadPeaksFromWaveformData(\n src: string,\n channelIndex: number = 0\n): Promise<{ data: Int8Array | Int16Array; bits: 8 | 16; length: number; sampleRate: number }> {\n const waveformData = await loadWaveformData(src);\n return waveformDataToPeaks(waveformData, channelIndex);\n}\n\n/**\n * Get metadata from waveform data file without converting to peaks\n *\n * @param src - URL to waveform data file\n * @returns Metadata (sample rate, channels, duration, bits, etc.)\n */\nexport async function getWaveformDataMetadata(src: string): Promise<{\n sampleRate: number;\n channels: number;\n duration: number;\n samplesPerPixel: number;\n length: number;\n bits: 8 | 16;\n}> {\n const waveformData = await loadWaveformData(src);\n\n return {\n sampleRate: waveformData.sample_rate,\n channels: waveformData.channels,\n duration: waveformData.duration,\n samplesPerPixel: waveformData.scale,\n length: waveformData.length,\n bits: waveformData.bits as 8 | 16,\n };\n}\n\n/**\n * Extract peaks from a WaveformData object at a specific scale (samplesPerPixel)\n * and optionally slice to a sample range.\n *\n * @param waveformData - WaveformData instance from waveform-data.js\n * @param samplesPerPixel - Target samples per pixel (will resample if different)\n * @param channelIndex - Channel index (default: 0)\n * @param offsetSamples - Optional start offset in samples (for clip trimming)\n * @param durationSamples - Optional duration in samples (for clip trimming)\n * @returns Peaks data ready for rendering\n */\nexport function extractPeaksFromWaveformData(\n waveformData: WaveformData,\n samplesPerPixel: number,\n channelIndex: number = 0,\n offsetSamples?: number,\n durationSamples?: number\n): { data: Int8Array | Int16Array; bits: 8 | 16; length: number } {\n let processedData = waveformData;\n\n // Slice if offset/duration specified (using index-based slicing for sample accuracy)\n if (offsetSamples !== undefined && durationSamples !== undefined) {\n // Convert samples to waveform data indices\n // waveformData.scale is the samples per pixel of the source data\n const sourceScale = waveformData.scale;\n const startIndex = Math.floor(offsetSamples / sourceScale);\n const endIndex = Math.ceil((offsetSamples + durationSamples) / sourceScale);\n processedData = processedData.slice({ startIndex, endIndex });\n }\n\n // Resample to target scale if different\n if (processedData.scale !== samplesPerPixel) {\n processedData = processedData.resample({ scale: samplesPerPixel });\n }\n\n // Convert to our peaks format\n const channel = processedData.channel(channelIndex);\n const bits = processedData.bits as 8 | 16;\n const minArray = channel.min_array();\n const maxArray = channel.max_array();\n const length = minArray.length;\n\n const peaks = bits === 8 ? new Int8Array(length * 2) : new Int16Array(length * 2);\n\n for (let i = 0; i < length; i++) {\n peaks[i * 2] = minArray[i];\n peaks[i * 2 + 1] = maxArray[i];\n }\n\n return { data: peaks, bits, length };\n}\n\n/**\n * Extract peaks from a WaveformData object, handling ALL channels, mono merging,\n * slicing, and resampling.\n *\n * Bit depth is determined by the WaveformData source — all typed arrays match\n * the source's bit depth for consistent data/metadata.\n *\n * @param waveformData - WaveformData instance (should be generated with split_channels: true)\n * @param samplesPerPixel - Target samples per pixel\n * @param isMono - Whether to merge channels to mono\n * @param offsetSamples - Optional start offset in samples (for clip trimming)\n * @param durationSamples - Optional duration in samples (for clip trimming)\n * @returns PeakData matching the interface from @waveform-playlist/core\n */\nexport function extractPeaksFromWaveformDataFull(\n waveformData: WaveformData,\n samplesPerPixel: number,\n isMono: boolean,\n offsetSamples?: number,\n durationSamples?: number\n): PeakData {\n let processedData = waveformData;\n\n // Slice if offset/duration specified\n if (offsetSamples !== undefined && durationSamples !== undefined) {\n const sourceScale = waveformData.scale;\n const startIndex = Math.floor(offsetSamples / sourceScale);\n const endIndex = Math.ceil((offsetSamples + durationSamples) / sourceScale);\n processedData = processedData.slice({ startIndex, endIndex });\n }\n\n // Resample to target scale if different\n if (processedData.scale !== samplesPerPixel) {\n processedData = processedData.resample({ scale: samplesPerPixel });\n }\n\n const numChannels = processedData.channels;\n const bits = processedData.bits as 8 | 16;\n\n // Extract peaks for all channels\n const channelPeaks: Peaks[] = [];\n for (let c = 0; c < numChannels; c++) {\n const channel = processedData.channel(c);\n const minArray = channel.min_array();\n const maxArray = channel.max_array();\n const len = minArray.length;\n\n const peaks: Peaks = bits === 8 ? new Int8Array(len * 2) : new Int16Array(len * 2);\n\n for (let i = 0; i < len; i++) {\n peaks[i * 2] = minArray[i];\n peaks[i * 2 + 1] = maxArray[i];\n }\n channelPeaks.push(peaks);\n }\n\n // Handle mono merging (same algorithm as makeMono in webaudio-peaks)\n if (isMono && channelPeaks.length > 1) {\n const weight = 1 / channelPeaks.length;\n const numPeaks = channelPeaks[0].length / 2;\n const monoPeaks: Peaks =\n bits === 8 ? new Int8Array(numPeaks * 2) : new Int16Array(numPeaks * 2);\n\n for (let i = 0; i < numPeaks; i++) {\n let min = 0;\n let max = 0;\n for (let c = 0; c < channelPeaks.length; c++) {\n min += weight * channelPeaks[c][i * 2];\n max += weight * channelPeaks[c][i * 2 + 1];\n }\n monoPeaks[i * 2] = min;\n monoPeaks[i * 2 + 1] = max;\n }\n\n return {\n length: numPeaks,\n data: [monoPeaks],\n bits,\n };\n }\n\n const peakLength = channelPeaks.length > 0 ? channelPeaks[0].length / 2 : 0;\n\n return {\n length: peakLength,\n data: channelPeaks,\n bits,\n };\n}\n","import { useState } from 'react';\nimport {\n formatTime as formatTimeUtil,\n parseTime as parseTimeUtil,\n type TimeFormat,\n} from '@waveform-playlist/ui-components';\n\nexport interface TimeFormatControls {\n timeFormat: TimeFormat;\n setTimeFormat: (format: TimeFormat) => void;\n formatTime: (seconds: number) => string;\n parseTime: (timeString: string) => number;\n}\n\n/**\n * Hook to manage time format state\n *\n * @example\n * ```tsx\n * const { timeFormat, setTimeFormat, formatTime, parseTime } = useTimeFormat();\n *\n * <TimeFormatSelect\n * value={timeFormat}\n * onChange={setTimeFormat}\n * />\n * <span>{formatTime(currentTime)}</span>\n * <input onChange={(e) => seekTo(parseTime(e.target.value))} />\n * ```\n */\nexport function useTimeFormat(): TimeFormatControls {\n const [timeFormat, setTimeFormat] = useState<TimeFormat>('hh:mm:ss.uuu');\n\n const formatTime = (seconds: number) => {\n return formatTimeUtil(seconds, timeFormat);\n };\n\n const parseTime = (timeString: string) => {\n return parseTimeUtil(timeString, timeFormat);\n };\n\n return {\n timeFormat,\n setTimeFormat,\n formatTime,\n parseTime,\n };\n}\n","import { useState, useCallback, startTransition } from 'react';\n\nexport interface ZoomControls {\n samplesPerPixel: number;\n zoomIn: () => void;\n zoomOut: () => void;\n canZoomIn: boolean;\n canZoomOut: boolean;\n}\n\nexport interface UseZoomControlsProps {\n initialSamplesPerPixel: number;\n zoomLevels?: number[]; // Array of samples per pixel values (lower = more zoomed in)\n}\n\nconst DEFAULT_ZOOM_LEVELS = [256, 512, 1024, 2048, 4096, 8192];\n\nexport function useZoomControls({\n initialSamplesPerPixel,\n zoomLevels = DEFAULT_ZOOM_LEVELS,\n}: UseZoomControlsProps): ZoomControls {\n const [zoomIndex, setZoomIndex] = useState(() => {\n const index = zoomLevels.indexOf(initialSamplesPerPixel);\n return index !== -1 ? index : Math.floor(zoomLevels.length / 2);\n });\n\n const samplesPerPixel = zoomLevels[zoomIndex];\n const canZoomIn = zoomIndex > 0;\n const canZoomOut = zoomIndex < zoomLevels.length - 1;\n\n // Wrap zoom state changes in startTransition so React treats them as\n // non-urgent. During playback, this allows animation RAF callbacks to\n // interleave with the zoom re-render. Rapid consecutive zooms are\n // batched — only the final level triggers peak recalculation.\n const zoomIn = useCallback(() => {\n startTransition(() => {\n setZoomIndex((prev) => Math.max(0, prev - 1));\n });\n }, []);\n\n const zoomOut = useCallback(() => {\n startTransition(() => {\n setZoomIndex((prev) => Math.min(zoomLevels.length - 1, prev + 1));\n });\n }, [zoomLevels.length]);\n\n return {\n samplesPerPixel,\n zoomIn,\n zoomOut,\n canZoomIn,\n canZoomOut,\n };\n}\n","import { useState, useCallback, RefObject } from 'react';\nimport { TonePlayout } from '@waveform-playlist/playout';\n\nexport interface UseMasterVolumeProps {\n playoutRef: RefObject<TonePlayout | null>;\n initialVolume?: number; // 0-1.0 (linear gain, consistent with Web Audio API)\n onVolumeChange?: (volume: number) => void;\n}\n\nexport interface MasterVolumeControls {\n masterVolume: number;\n setMasterVolume: (volume: number) => void;\n}\n\n/**\n * Hook for managing master volume control\n *\n * @example\n * ```tsx\n * const { masterVolume, setMasterVolume } = useMasterVolume({\n * playoutRef,\n * initialVolume: 1.0,\n * });\n *\n * <MasterVolumeControl\n * volume={masterVolume}\n * onChange={setMasterVolume}\n * />\n * ```\n */\nexport function useMasterVolume({\n playoutRef,\n initialVolume = 1.0,\n onVolumeChange,\n}: UseMasterVolumeProps): MasterVolumeControls {\n const [masterVolume, setMasterVolumeState] = useState(initialVolume);\n\n const setMasterVolume = useCallback(\n (volume: number) => {\n setMasterVolumeState(volume);\n\n // Update the playout with linear gain (0-1.0 range)\n if (playoutRef.current) {\n playoutRef.current.setMasterGain(volume);\n }\n\n // Call optional callback\n onVolumeChange?.(volume);\n },\n [playoutRef, onVolumeChange]\n );\n\n return {\n masterVolume,\n setMasterVolume,\n };\n}\n","import { useRef, useCallback } from 'react';\nimport type { EffectsFunction } from '@waveform-playlist/playout';\n// Import Tone.js classes directly for tree-shaking\nimport { Analyser } from 'tone';\n\n/**\n * Hook for master effects with frequency analyzer\n * Returns the analyser ref and the effects function to pass to WaveformPlaylistProvider\n *\n * For more advanced effects (reverb, delay, filters, etc.), use useDynamicEffects instead.\n */\nexport const useMasterAnalyser = (fftSize: number = 256) => {\n const analyserRef = useRef<Analyser | null>(null);\n\n const masterEffects: EffectsFunction = useCallback(\n (masterGainNode, destination, _isOffline) => {\n // Create analyser and connect it in parallel to monitor the output\n const analyserNode = new Analyser('fft', fftSize);\n masterGainNode.connect(analyserNode);\n\n // Connect master to destination as normal\n masterGainNode.connect(destination);\n\n // Store analyser for visualization\n analyserRef.current = analyserNode;\n\n return function cleanup() {\n // Cleanup when playlist is destroyed\n analyserNode.dispose();\n analyserRef.current = null;\n };\n },\n [fftSize]\n );\n\n return { analyserRef, masterEffects };\n};\n","import { useState, useEffect } from 'react';\nimport {\n ClipTrack,\n createTrack,\n createClipFromSeconds,\n type Fade,\n type TrackEffectsFunction,\n type WaveformDataObject,\n type RenderMode,\n type SpectrogramConfig,\n type ColorMapValue,\n} from '@waveform-playlist/core';\nimport * as Tone from 'tone';\n\n/**\n * Configuration for a single audio track to load\n *\n * Audio can be provided in three ways:\n * 1. `src` - URL to fetch and decode (standard loading)\n * 2. `audioBuffer` - Pre-loaded AudioBuffer (skip fetch/decode)\n * 3. `waveformData` only - Peaks-first rendering (audio loads later)\n *\n * For peaks-first rendering, just provide `waveformData` - the sample rate\n * and duration are derived from the waveform data automatically.\n */\nexport interface AudioTrackConfig {\n /** URL to audio file - used if audioBuffer not provided */\n src?: string;\n /** Pre-loaded AudioBuffer - skips fetch/decode if provided */\n audioBuffer?: AudioBuffer;\n name?: string;\n muted?: boolean;\n soloed?: boolean;\n volume?: number;\n pan?: number;\n color?: string;\n effects?: TrackEffectsFunction;\n // Multi-clip support\n startTime?: number; // When the clip starts on the timeline (default: 0)\n duration?: number; // Duration of the clip (default: full audio duration)\n offset?: number; // Offset into the source audio file (default: 0)\n // Fade support\n fadeIn?: Fade; // Fade in configuration\n fadeOut?: Fade; // Fade out configuration\n // Pre-computed waveform data (BBC audiowaveform format)\n // For peaks-first rendering, provide this without audioBuffer/src\n // Sample rate and duration are derived from waveformData.sample_rate and waveformData.duration\n waveformData?: WaveformDataObject;\n /** Visualization render mode: 'waveform' | 'spectrogram' | 'both'. Default: 'waveform' */\n renderMode?: RenderMode;\n /** Spectrogram configuration (FFT size, window, frequency scale, etc.) */\n spectrogramConfig?: SpectrogramConfig;\n /** Spectrogram color map name or custom color array */\n spectrogramColorMap?: ColorMapValue;\n}\n\n/**\n * Options for useAudioTracks hook\n */\nexport interface UseAudioTracksOptions {\n /**\n * When true, tracks are added to the playlist progressively as they load,\n * rather than waiting for all tracks to finish loading.\n * Default: false (wait for all tracks)\n */\n progressive?: boolean;\n}\n\n/**\n * Hook to load audio from URLs and convert to ClipTrack format\n *\n * This hook fetches audio files, decodes them, and creates ClipTrack objects\n * with a single clip per track. Supports custom positioning for multi-clip arrangements.\n *\n * @param configs - Array of audio track configurations\n * @param options - Optional configuration for loading behavior\n * @returns Object with tracks array, loading state, and progress info\n *\n * @example\n * ```typescript\n * // Basic usage (clips positioned at start)\n * const { tracks, loading, error } = useAudioTracks([\n * { src: 'audio/vocals.mp3', name: 'Vocals' },\n * { src: 'audio/drums.mp3', name: 'Drums' },\n * ]);\n *\n * // Progressive loading (tracks appear as they load)\n * const { tracks, loading, loadedCount, totalCount } = useAudioTracks(\n * [{ src: 'audio/vocals.mp3' }, { src: 'audio/drums.mp3' }],\n * { progressive: true }\n * );\n *\n * // Pre-loaded AudioBuffer (skip fetch/decode)\n * const { tracks } = useAudioTracks([\n * { audioBuffer: myPreloadedBuffer, name: 'Pre-loaded' },\n * ]);\n *\n * // Peaks-first rendering (instant visual, audio loads later)\n * const { tracks } = useAudioTracks([\n * { waveformData: preloadedPeaks, name: 'Peaks Only' }, // Renders immediately\n * ]);\n *\n * if (loading) return <div>Loading {loadedCount}/{totalCount}...</div>;\n * if (error) return <div>Error: {error}</div>;\n *\n * return <WaveformPlaylistProvider tracks={tracks}>...</WaveformPlaylistProvider>;\n * ```\n */\nexport function useAudioTracks(configs: AudioTrackConfig[], options: UseAudioTracksOptions = {}) {\n const { progressive = false } = options;\n const [tracks, setTracks] = useState<ClipTrack[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [loadedCount, setLoadedCount] = useState(0);\n\n // Track which configs need audio loading vs already have data\n const totalCount = configs.length;\n\n useEffect(() => {\n if (configs.length === 0) {\n setTracks([]);\n setLoading(false);\n setLoadedCount(0);\n return;\n }\n\n let cancelled = false;\n const abortController = new AbortController();\n // Track loaded tracks by their config index for progressive mode\n const loadedTracksMap = new Map<number, ClipTrack>();\n\n const createTrackFromConfig = (\n config: AudioTrackConfig,\n index: number,\n audioBuffer?: AudioBuffer\n ): ClipTrack => {\n // Use provided audioBuffer, config's audioBuffer, or undefined for peaks-only\n const buffer = audioBuffer ?? config.audioBuffer;\n\n // For peaks-first rendering, we need waveformData if no buffer\n if (!buffer && !config.waveformData) {\n throw new Error(`Track ${index + 1}: Must provide src, audioBuffer, or waveformData`);\n }\n\n // Determine source duration for clip creation\n const sourceDuration = buffer?.duration ?? config.waveformData?.duration;\n\n // Create clip - createClipFromSeconds handles deriving sampleRate from waveformData\n const clip = createClipFromSeconds({\n audioBuffer: buffer,\n startTime: config.startTime ?? 0,\n duration: config.duration ?? sourceDuration,\n offset: config.offset ?? 0,\n name: config.name || `Track ${index + 1}`,\n fadeIn: config.fadeIn,\n fadeOut: config.fadeOut,\n waveformData: config.waveformData,\n });\n\n // Validate clip values\n if (isNaN(clip.startSample) || isNaN(clip.durationSamples) || isNaN(clip.offsetSamples)) {\n console.error('Invalid clip values:', clip);\n throw new Error(`Invalid clip values for track ${index + 1}`);\n }\n\n // Create the track with the single clip\n const track: ClipTrack = {\n ...createTrack({\n name: config.name || `Track ${index + 1}`,\n clips: [clip],\n muted: config.muted ?? false,\n soloed: config.soloed ?? false,\n volume: config.volume ?? 1.0,\n pan: config.pan ?? 0,\n color: config.color,\n }),\n effects: config.effects,\n renderMode: config.renderMode,\n spectrogramConfig: config.spectrogramConfig,\n spectrogramColorMap: config.spectrogramColorMap,\n };\n\n return track;\n };\n\n const loadTracks = async () => {\n try {\n setLoading(true);\n setError(null);\n setLoadedCount(0);\n\n const audioContext = Tone.getContext().rawContext as AudioContext;\n\n // Process each config\n const loadPromises = configs.map(async (config, index) => {\n // Case 1: Already have audioBuffer - no loading needed\n if (config.audioBuffer) {\n const track = createTrackFromConfig(config, index, config.audioBuffer);\n\n if (progressive && !cancelled) {\n loadedTracksMap.set(index, track);\n setLoadedCount((prev) => prev + 1);\n // Update tracks maintaining order\n setTracks(\n Array.from({ length: configs.length }, (_, i) => loadedTracksMap.get(i)).filter(\n (t): t is ClipTrack => t !== undefined\n )\n );\n }\n\n return track;\n }\n\n // Case 2: Have waveformData but no src - peaks-only (no audio to load)\n if (!config.src && config.waveformData) {\n const track = createTrackFromConfig(config, index);\n\n if (progressive && !cancelled) {\n loadedTracksMap.set(index, track);\n setLoadedCount((prev) => prev + 1);\n setTracks(\n Array.from({ length: configs.length }, (_, i) => loadedTracksMap.get(i)).filter(\n (t): t is ClipTrack => t !== undefined\n )\n );\n }\n\n return track;\n }\n\n // Case 3: Need to fetch and decode audio from src\n if (!config.src) {\n throw new Error(`Track ${index + 1}: Must provide src, audioBuffer, or waveformData`);\n }\n\n const response = await fetch(config.src, { signal: abortController.signal });\n if (!response.ok) {\n throw new Error(`Failed to fetch ${config.src}: ${response.statusText}`);\n }\n\n const arrayBuffer = await response.arrayBuffer();\n const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);\n\n // Validate audioBuffer\n if (!audioBuffer || !audioBuffer.sampleRate || !audioBuffer.duration) {\n throw new Error(`Invalid audio buffer for ${config.src}`);\n }\n\n const track = createTrackFromConfig(config, index, audioBuffer);\n\n if (progressive && !cancelled) {\n loadedTracksMap.set(index, track);\n setLoadedCount((prev) => prev + 1);\n // Update tracks maintaining original config order\n setTracks(\n Array.from({ length: configs.length }, (_, i) => loadedTracksMap.get(i)).filter(\n (t): t is ClipTrack => t !== undefined\n )\n );\n }\n\n return track;\n });\n\n const loadedTracks = await Promise.all(loadPromises);\n\n if (!cancelled) {\n // For non-progressive mode, set all tracks at once\n if (!progressive) {\n setTracks(loadedTracks);\n setLoadedCount(loadedTracks.length);\n }\n setLoading(false);\n }\n } catch (err) {\n if (!cancelled) {\n const errorMessage = err instanceof Error ? err.message : 'Unknown error loading audio';\n setError(errorMessage);\n setLoading(false);\n console.error('Error loading audio tracks:', err);\n }\n }\n };\n\n loadTracks();\n\n // Cleanup: prevent state updates and abort in-flight fetches on unmount\n return () => {\n cancelled = true;\n abortController.abort();\n };\n }, [configs, progressive]);\n\n return { tracks, loading, error, loadedCount, totalCount };\n}\n","import React from 'react';\nimport type { DragEndEvent, DragStartEvent, DragMoveEvent, Modifier } from '@dnd-kit/core';\nimport type { ClipTrack } from '@waveform-playlist/core';\n\ninterface UseClipDragHandlersOptions {\n tracks: ClipTrack[];\n onTracksChange: (tracks: ClipTrack[]) => void;\n samplesPerPixel: number;\n sampleRate: number;\n}\n\n/**\n * Custom hook for handling clip drag operations (movement and trimming)\n *\n * Provides drag handlers and collision modifier for use with @dnd-kit/core DndContext.\n * Handles both clip movement (dragging entire clips) and boundary trimming (adjusting clip edges).\n *\n * @example\n * ```tsx\n * const { onDragStart, onDragMove, onDragEnd, collisionModifier } = useClipDragHandlers({\n * tracks,\n * onTracksChange: setTracks,\n * samplesPerPixel,\n * sampleRate,\n * });\n *\n * return (\n * <DndContext\n * onDragStart={onDragStart}\n * onDragMove={onDragMove}\n * onDragEnd={onDragEnd}\n * modifiers={[restrictToHorizontalAxis, collisionModifier]}\n * >\n * <Waveform showClipHeaders={true} />\n * </DndContext>\n * );\n * ```\n */\nexport function useClipDragHandlers({\n tracks,\n onTracksChange,\n samplesPerPixel,\n sampleRate,\n}: UseClipDragHandlersOptions) {\n // Store original clip state when drag starts (for cumulative delta application)\n const originalClipStateRef = React.useRef<{\n offsetSamples: number;\n durationSamples: number;\n startSample: number;\n } | null>(null);\n\n // Custom modifier for real-time collision detection during clip movement\n const collisionModifier = React.useCallback(\n (args: Parameters<Modifier>[0]) => {\n const { transform, active } = args;\n\n if (!active?.data?.current) return { ...transform, scaleX: 1, scaleY: 1 };\n\n const { trackIndex, clipIndex, boundary } = active.data.current as {\n clipId: string;\n trackIndex: number;\n clipIndex: number;\n boundary?: 'left' | 'right';\n };\n\n // For boundary trimming, skip modifier - onDragMove handles constraints\n if (boundary) {\n return { ...transform, scaleX: 1, scaleY: 1 };\n }\n\n const track = tracks[trackIndex];\n if (!track) return { ...transform, scaleX: 1, scaleY: 1 };\n\n const clip = track.clips[clipIndex];\n if (!clip) return { ...transform, scaleX: 1, scaleY: 1 };\n\n // Convert sample-based properties to time for calculations\n const clipStartTime = clip.startSample / sampleRate;\n const clipDuration = clip.durationSamples / sampleRate;\n\n // Convert pixel delta to time delta\n const timeDelta = (transform.x * samplesPerPixel) / sampleRate;\n\n // Handle clip movement (not trimming)\n let newStartTime = clipStartTime + timeDelta;\n\n // Get sorted clips for collision detection\n const sortedClips = [...track.clips].sort((a, b) => a.startSample - b.startSample);\n const sortedIndex = sortedClips.findIndex((c) => c === clip);\n\n // Constraint 1: Cannot go before time 0\n newStartTime = Math.max(0, newStartTime);\n\n // Constraint 2: Cannot overlap with previous clip\n const previousClip = sortedIndex > 0 ? sortedClips[sortedIndex - 1] : null;\n if (previousClip) {\n const previousEndTime =\n (previousClip.startSample + previousClip.durationSamples) / sampleRate;\n newStartTime = Math.max(newStartTime, previousEndTime);\n }\n\n // Constraint 3: Cannot overlap with next clip\n const nextClip = sortedIndex < sortedClips.length - 1 ? sortedClips[sortedIndex + 1] : null;\n if (nextClip) {\n const newEndTime = newStartTime + clipDuration;\n const nextClipStartTime = nextClip.startSample / sampleRate;\n if (newEndTime > nextClipStartTime) {\n newStartTime = nextClipStartTime - clipDuration;\n }\n }\n\n // Convert constrained time back to pixel delta\n const constrainedTimeDelta = newStartTime - clipStartTime;\n const constrainedX = (constrainedTimeDelta * sampleRate) / samplesPerPixel;\n\n return {\n ...transform,\n x: constrainedX,\n scaleX: 1,\n scaleY: 1,\n };\n },\n [tracks, samplesPerPixel, sampleRate]\n );\n\n const onDragStart = React.useCallback(\n (event: DragStartEvent) => {\n const { active } = event;\n const { boundary } = active.data.current as { boundary?: 'left' | 'right' };\n\n // Only store state for boundary trimming operations\n if (!boundary) {\n originalClipStateRef.current = null;\n return;\n }\n\n const { trackIndex, clipIndex } = active.data.current as {\n clipId: string;\n trackIndex: number;\n clipIndex: number;\n boundary: 'left' | 'right';\n };\n\n const track = tracks[trackIndex];\n const clip = track?.clips[clipIndex];\n\n if (clip) {\n // Store original clip state for cumulative delta application\n originalClipStateRef.current = {\n offsetSamples: clip.offsetSamples,\n durationSamples: clip.durationSamples,\n startSample: clip.startSample,\n };\n }\n },\n [tracks]\n );\n\n const onDragMove = React.useCallback(\n (event: DragMoveEvent) => {\n const { active, delta } = event;\n\n // Only update for boundary trimming operations (not clip movement)\n const { boundary } = active.data.current as { boundary?: 'left' | 'right' };\n if (!boundary) return;\n\n // Need original clip state to apply cumulative delta\n if (!originalClipStateRef.current) return;\n\n // Extract clip metadata\n const { trackIndex, clipIndex } = active.data.current as {\n clipId: string;\n trackIndex: number;\n clipIndex: number;\n boundary: 'left' | 'right';\n };\n\n const sampleDelta = delta.x * samplesPerPixel;\n const MIN_DURATION_SAMPLES = Math.floor(0.1 * sampleRate); // 0.1 seconds minimum\n\n // Get original clip state (stored on drag start)\n const originalClip = originalClipStateRef.current;\n\n // Update tracks in real-time during drag\n const newTracks = tracks.map((track, tIdx) => {\n if (tIdx !== trackIndex) return track;\n\n const sortedClips = [...track.clips].sort((a, b) => a.startSample - b.startSample);\n const sortedIndex = sortedClips.findIndex((clip) => clip === track.clips[clipIndex]);\n\n const newClips = track.clips.map((clip, cIdx) => {\n if (cIdx !== clipIndex) return clip;\n\n // Use sourceDurationSamples (works for both audio and peaks-only clips)\n const audioBufferDurationSamples = clip.sourceDurationSamples;\n\n if (boundary === 'left') {\n // Left boundary drag: moving left (negative delta) expands clip, moving right shrinks it\n // The RIGHT edge stays fixed. We're moving the LEFT edge.\n //\n // When dragging left (sampleDelta < 0):\n // - startSample decreases (moves left)\n // - durationSamples increases (clip gets longer)\n // - offsetSamples decreases (reveal earlier audio from buffer)\n //\n // When dragging right (sampleDelta > 0):\n // - startSample increases (moves right)\n // - durationSamples decreases (clip gets shorter)\n // - offsetSamples increases (hide earlier audio)\n\n // Calculate the constrained delta first, then apply it uniformly\n let constrainedDelta = Math.floor(sampleDelta);\n\n // Constraint 1: startSample cannot go below 0 (dragging left limit)\n // newStartSample = originalClip.startSample + delta >= 0\n // delta >= -originalClip.startSample\n const minDeltaForStart = -originalClip.startSample;\n if (constrainedDelta < minDeltaForStart) {\n constrainedDelta = minDeltaForStart;\n }\n\n // Constraint 2: offsetSamples cannot go below 0 (can't reveal audio before buffer start)\n // newOffsetSamples = originalClip.offsetSamples + delta >= 0\n // delta >= -originalClip.offsetSamples\n const minDeltaForOffset = -originalClip.offsetSamples;\n if (constrainedDelta < minDeltaForOffset) {\n constrainedDelta = minDeltaForOffset;\n }\n\n // Constraint 3: Cannot overlap with previous clip (dragging left limit)\n const previousClip = sortedIndex > 0 ? sortedClips[sortedIndex - 1] : null;\n if (previousClip) {\n const previousEndSample = previousClip.startSample + previousClip.durationSamples;\n // newStartSample = originalClip.startSample + delta >= previousEndSample\n // delta >= previousEndSample - originalClip.startSample\n const minDeltaForPrevious = previousEndSample - originalClip.startSample;\n if (constrainedDelta < minDeltaForPrevious) {\n constrainedDelta = minDeltaForPrevious;\n }\n }\n\n // Constraint 4: Minimum duration (dragging right limit)\n // newDurationSamples = originalClip.durationSamples - delta >= MIN_DURATION_SAMPLES\n // -delta >= MIN_DURATION_SAMPLES - originalClip.durationSamples\n // delta <= originalClip.durationSamples - MIN_DURATION_SAMPLES\n const maxDeltaForMinDuration = originalClip.durationSamples - MIN_DURATION_SAMPLES;\n if (constrainedDelta > maxDeltaForMinDuration) {\n constrainedDelta = maxDeltaForMinDuration;\n }\n\n // Constraint 5: Cannot exceed audio buffer length\n // newOffsetSamples + newDurationSamples <= audioBufferDurationSamples\n // (originalClip.offsetSamples + delta) + (originalClip.durationSamples - delta) <= audioBufferDurationSamples\n // This simplifies to: originalClip.offsetSamples + originalClip.durationSamples <= audioBufferDurationSamples\n // This is always true if the clip was valid to begin with, so no constraint needed here\n\n // Now apply the constrained delta\n const newOffsetSamples = originalClip.offsetSamples + constrainedDelta;\n const newDurationSamples = originalClip.durationSamples - constrainedDelta;\n const newStartSample = originalClip.startSample + constrainedDelta;\n\n return {\n ...clip,\n offsetSamples: newOffsetSamples,\n durationSamples: newDurationSamples,\n startSample: newStartSample,\n };\n } else {\n // Right boundary - only update duration\n // Apply cumulative delta to ORIGINAL state (not current state)\n let newDurationSamples = Math.floor(originalClip.durationSamples + sampleDelta);\n newDurationSamples = Math.max(MIN_DURATION_SAMPLES, newDurationSamples);\n\n if (originalClip.offsetSamples + newDurationSamples > audioBufferDurationSamples) {\n newDurationSamples = audioBufferDurationSamples - originalClip.offsetSamples;\n }\n\n const nextClip =\n sortedIndex < sortedClips.length - 1 ? sortedClips[sortedIndex + 1] : null;\n if (nextClip) {\n const newEndSample = originalClip.startSample + newDurationSamples;\n if (newEndSample > nextClip.startSample) {\n newDurationSamples = nextClip.startSample - originalClip.startSample;\n newDurationSamples = Math.max(MIN_DURATION_SAMPLES, newDurationSamples);\n }\n }\n\n return { ...clip, durationSamples: newDurationSamples };\n }\n });\n\n return { ...track, clips: newClips };\n });\n\n onTracksChange(newTracks);\n },\n [tracks, onTracksChange, samplesPerPixel, sampleRate]\n );\n\n const onDragEnd = React.useCallback(\n (event: DragEndEvent) => {\n const { active, delta } = event;\n\n // Extract clip metadata from drag data\n const { trackIndex, clipIndex, boundary } = active.data.current as {\n clipId: string;\n trackIndex: number;\n clipIndex: number;\n boundary?: 'left' | 'right';\n };\n\n // Convert pixel delta to samples\n const sampleDelta = delta.x * samplesPerPixel;\n\n // Check if this is a boundary trim operation\n if (boundary) {\n // For boundary trimming, onDragMove already updated the tracks\n // onDragEnd doesn't need to do anything (state is already correct)\n // Just clear the original clip state ref\n originalClipStateRef.current = null;\n return;\n }\n\n // Handle clip movement (not trimming)\n const newTracks = tracks.map((track, tIdx) => {\n if (tIdx !== trackIndex) return track;\n\n // Get sorted clips for collision detection\n const sortedClips = [...track.clips].sort((a, b) => a.startSample - b.startSample);\n const sortedIndex = sortedClips.findIndex((clip) => clip === track.clips[clipIndex]);\n\n // Update the specific clip in this track\n const newClips = track.clips.map((clip, cIdx) => {\n if (cIdx !== clipIndex) return clip;\n\n // Calculate desired new start sample\n let newStartSample = Math.floor(clip.startSample + sampleDelta);\n\n // Collision detection constraints:\n // 1. Cannot go before sample 0\n newStartSample = Math.max(0, newStartSample);\n\n // 2. Cannot overlap with previous clip\n const previousClip = sortedIndex > 0 ? sortedClips[sortedIndex - 1] : null;\n if (previousClip) {\n const previousEndSample = previousClip.startSample + previousClip.durationSamples;\n newStartSample = Math.max(newStartSample, previousEndSample);\n }\n\n // 3. Cannot overlap with next clip\n const nextClip =\n sortedIndex < sortedClips.length - 1 ? sortedClips[sortedIndex + 1] : null;\n if (nextClip) {\n const newEndSample = newStartSample + clip.durationSamples;\n if (newEndSample > nextClip.startSample) {\n // Push back to be adjacent to next clip\n newStartSample = nextClip.startSample - clip.durationSamples;\n }\n }\n\n return {\n ...clip,\n startSample: newStartSample,\n };\n });\n\n return {\n ...track,\n clips: newClips,\n };\n });\n\n onTracksChange(newTracks);\n },\n [tracks, onTracksChange, samplesPerPixel]\n );\n\n return {\n onDragStart,\n onDragMove,\n onDragEnd,\n collisionModifier,\n };\n}\n","import React from 'react';\nimport type { DragStartEvent, DragMoveEvent } from '@dnd-kit/core';\nimport type { AnnotationData } from '@waveform-playlist/core';\n\nconst LINK_THRESHOLD = 0.01; // Consider edges \"linked\" if within 10ms\n\ninterface UseAnnotationDragHandlersOptions {\n annotations: AnnotationData[];\n onAnnotationsChange: (annotations: AnnotationData[]) => void;\n samplesPerPixel: number;\n sampleRate: number;\n duration: number;\n linkEndpoints: boolean;\n}\n\n/**\n * Custom hook for handling annotation drag operations (boundary trimming)\n *\n * Provides drag handlers for use with @dnd-kit/core DndContext.\n * Handles annotation boundary resizing with linked endpoints support.\n *\n * @example\n * ```tsx\n * const { onDragStart, onDragMove, onDragEnd } = useAnnotationDragHandlers({\n * annotations,\n * onAnnotationsChange: setAnnotations,\n * samplesPerPixel,\n * sampleRate,\n * duration,\n * linkEndpoints,\n * });\n *\n * return (\n * <DndContext\n * onDragStart={onDragStart}\n * onDragMove={onDragMove}\n * onDragEnd={onDragEnd}\n * modifiers={[restrictToHorizontalAxis]}\n * >\n * {renderAnnotations()}\n * </DndContext>\n * );\n * ```\n */\nexport function useAnnotationDragHandlers({\n annotations,\n onAnnotationsChange,\n samplesPerPixel,\n sampleRate,\n duration,\n linkEndpoints,\n}: UseAnnotationDragHandlersOptions) {\n // Store original annotation state when drag starts (for cumulative delta application)\n const originalAnnotationStateRef = React.useRef<{\n start: number;\n end: number;\n annotationIndex: number;\n } | null>(null);\n\n const onDragStart = React.useCallback(\n (event: DragStartEvent) => {\n const { active } = event;\n const data = active.data.current as {\n annotationId: string;\n annotationIndex: number;\n edge: 'start' | 'end';\n };\n\n if (!data || data.annotationIndex === undefined) {\n originalAnnotationStateRef.current = null;\n return;\n }\n\n const annotation = annotations[data.annotationIndex];\n if (annotation) {\n originalAnnotationStateRef.current = {\n start: annotation.start,\n end: annotation.end,\n annotationIndex: data.annotationIndex,\n };\n }\n },\n [annotations]\n );\n\n const onDragMove = React.useCallback(\n (event: DragMoveEvent) => {\n const { active, delta } = event;\n\n if (!originalAnnotationStateRef.current) {\n return;\n }\n\n const data = active.data.current as {\n annotationId: string;\n annotationIndex: number;\n edge: 'start' | 'end';\n };\n\n if (!data) return;\n\n const { edge, annotationIndex } = data;\n const originalState = originalAnnotationStateRef.current;\n\n // Convert pixel delta to time delta\n const timeDelta = (delta.x * samplesPerPixel) / sampleRate;\n\n // Apply delta to original state\n const newTime =\n edge === 'start' ? originalState.start + timeDelta : originalState.end + timeDelta;\n\n // Update annotations using the boundary logic\n const updatedAnnotations = updateAnnotationBoundaries({\n annotationIndex,\n newTime,\n isDraggingStart: edge === 'start',\n annotations,\n duration,\n linkEndpoints,\n });\n\n onAnnotationsChange(updatedAnnotations);\n },\n [annotations, onAnnotationsChange, samplesPerPixel, sampleRate, duration, linkEndpoints]\n );\n\n const onDragEnd = React.useCallback(() => {\n originalAnnotationStateRef.current = null;\n }, []);\n\n return {\n onDragStart,\n onDragMove,\n onDragEnd,\n };\n}\n\n/**\n * Updates annotation boundaries based on drag operations.\n * Handles linked endpoints and collision detection.\n */\nfunction updateAnnotationBoundaries({\n annotationIndex,\n newTime,\n isDraggingStart,\n annotations,\n duration,\n linkEndpoints: shouldLinkEndpoints,\n}: {\n annotationIndex: number;\n newTime: number;\n isDraggingStart: boolean;\n annotations: AnnotationData[];\n duration: number;\n linkEndpoints: boolean;\n}): AnnotationData[] {\n const updatedAnnotations = [...annotations];\n const annotation = annotations[annotationIndex];\n\n if (isDraggingStart) {\n // Dragging start edge\n const constrainedStart = Math.min(annotation.end - 0.1, Math.max(0, newTime));\n const delta = constrainedStart - annotation.start;\n\n updatedAnnotations[annotationIndex] = {\n ...annotation,\n start: constrainedStart,\n };\n\n if (shouldLinkEndpoints && annotationIndex > 0) {\n // Link Endpoints mode: handle both already-linked and collision scenarios\n const prevAnnotation = updatedAnnotations[annotationIndex - 1];\n\n if (Math.abs(prevAnnotation.end - annotation.start) < LINK_THRESHOLD) {\n // Already linked: move previous annotation's end together with this start\n updatedAnnotations[annotationIndex - 1] = {\n ...prevAnnotation,\n end: Math.max(prevAnnotation.start + 0.1, prevAnnotation.end + delta),\n };\n } else if (constrainedStart <= prevAnnotation.end) {\n // Dragged past previous annotation: snap to link them together\n updatedAnnotations[annotationIndex] = {\n ...updatedAnnotations[annotationIndex],\n start: prevAnnotation.end,\n };\n }\n } else if (\n !shouldLinkEndpoints &&\n annotationIndex > 0 &&\n constrainedStart < updatedAnnotations[annotationIndex - 1].end\n ) {\n // Collision detection: push previous annotation's end back\n updatedAnnotations[annotationIndex - 1] = {\n ...updatedAnnotations[annotationIndex - 1],\n end: constrainedStart,\n };\n }\n } else {\n // Dragging end edge\n const constrainedEnd = Math.max(annotation.start + 0.1, Math.min(newTime, duration));\n const delta = constrainedEnd - annotation.end;\n\n updatedAnnotations[annotationIndex] = {\n ...annotation,\n end: constrainedEnd,\n };\n\n if (shouldLinkEndpoints && annotationIndex < updatedAnnotations.length - 1) {\n // Link Endpoints mode: handle both already-linked and collision scenarios\n const nextAnnotation = updatedAnnotations[annotationIndex + 1];\n\n if (Math.abs(nextAnnotation.start - annotation.end) < LINK_THRESHOLD) {\n // Already linked: move next annotation's start together with this end\n const newStart = nextAnnotation.start + delta;\n updatedAnnotations[annotationIndex + 1] = {\n ...nextAnnotation,\n start: Math.min(nextAnnotation.end - 0.1, newStart),\n };\n\n // Cascade linked endpoints\n let currentIndex = annotationIndex + 1;\n while (currentIndex < updatedAnnotations.length - 1) {\n const current = updatedAnnotations[currentIndex];\n const next = updatedAnnotations[currentIndex + 1];\n\n if (Math.abs(next.start - current.end) < LINK_THRESHOLD) {\n const nextDelta = current.end - annotations[currentIndex].end;\n updatedAnnotations[currentIndex + 1] = {\n ...next,\n start: Math.min(next.end - 0.1, next.start + nextDelta),\n };\n currentIndex++;\n } else {\n break; // No more linked endpoints\n }\n }\n } else if (constrainedEnd >= nextAnnotation.start) {\n // Dragged past next annotation: snap to link them together\n updatedAnnotations[annotationIndex] = {\n ...updatedAnnotations[annotationIndex],\n end: nextAnnotation.start,\n };\n }\n } else if (\n !shouldLinkEndpoints &&\n annotationIndex < updatedAnnotations.length - 1 &&\n constrainedEnd > updatedAnnotations[annotationIndex + 1].start\n ) {\n // Collision detection: push next annotation's start forward\n const nextAnnotation = updatedAnnotations[annotationIndex + 1];\n\n updatedAnnotations[annotationIndex + 1] = {\n ...nextAnnotation,\n start: constrainedEnd,\n };\n\n // Cascade collisions\n let currentIndex = annotationIndex + 1;\n while (currentIndex < updatedAnnotations.length - 1) {\n const current = updatedAnnotations[currentIndex];\n const next = updatedAnnotations[currentIndex + 1];\n\n if (current.end > next.start) {\n updatedAnnotations[currentIndex + 1] = {\n ...next,\n start: current.end,\n };\n currentIndex++;\n } else {\n break; // No more collisions\n }\n }\n }\n }\n\n return updatedAnnotations;\n}\n","/**\n * Hook for configuring @dnd-kit sensors for clip dragging\n *\n * Provides consistent drag activation behavior across all examples.\n * Supports both desktop (immediate feedback) and mobile (delay-based) interactions.\n */\n\nimport { useSensor, useSensors, PointerSensor, TouchSensor, MouseSensor } from '@dnd-kit/core';\n\nexport interface DragSensorOptions {\n /**\n * Enable mobile-optimized touch handling with delay-based activation.\n * When true, uses TouchSensor with 250ms delay to distinguish drag from scroll.\n * When false (default), uses PointerSensor with 1px activation for immediate feedback.\n */\n touchOptimized?: boolean;\n /**\n * Delay in milliseconds before touch drag activates (only when touchOptimized is true).\n * Default: 250ms - long enough to distinguish from scroll intent\n */\n touchDelay?: number;\n /**\n * Distance tolerance during touch delay (only when touchOptimized is true).\n * If finger moves more than this during delay, drag is cancelled.\n * Default: 5px - allows slight finger movement\n */\n touchTolerance?: number;\n /**\n * Distance in pixels before mouse drag activates.\n * Default: 1px for immediate feedback on desktop\n */\n mouseDistance?: number;\n}\n\n/**\n * Returns configured sensors for @dnd-kit drag operations\n *\n * @param options - Configuration options for drag sensors\n * @returns Configured sensors appropriate for the interaction mode\n *\n * @example\n * // Desktop-optimized (default)\n * const sensors = useDragSensors();\n *\n * @example\n * // Mobile-optimized with touch delay\n * const sensors = useDragSensors({ touchOptimized: true });\n *\n * @example\n * // Custom touch settings\n * const sensors = useDragSensors({\n * touchOptimized: true,\n * touchDelay: 300,\n * touchTolerance: 8\n * });\n */\nexport function useDragSensors(options: DragSensorOptions = {}) {\n const {\n touchOptimized = false,\n touchDelay = 250,\n touchTolerance = 5,\n mouseDistance = 1,\n } = options;\n\n // Touch-optimized: Use separate MouseSensor and TouchSensor\n // This allows different activation constraints for each input type\n const mouseSensor = useSensor(MouseSensor, {\n activationConstraint: {\n distance: mouseDistance,\n },\n });\n\n const touchSensor = useSensor(TouchSensor, {\n activationConstraint: touchOptimized\n ? {\n // Delay-based activation for mobile - wait before starting drag\n // This allows users to scroll without accidentally triggering drag\n delay: touchDelay,\n tolerance: touchTolerance,\n }\n : {\n // Distance-based activation for non-optimized mode\n distance: mouseDistance,\n },\n });\n\n // Non-optimized: Use PointerSensor for unified handling (original behavior)\n const pointerSensor = useSensor(PointerSensor, {\n activationConstraint: {\n distance: mouseDistance,\n },\n });\n\n // When touch-optimized, use separate sensors for better control\n // Otherwise, use unified PointerSensor for backwards compatibility\n return useSensors(...(touchOptimized ? [mouseSensor, touchSensor] : [pointerSensor]));\n}\n","import { useCallback } from 'react';\nimport { type ClipTrack, createClip } from '@waveform-playlist/core';\nimport { usePlaybackAnimation, usePlaylistState } from '../WaveformPlaylistContext';\n\nexport interface UseClipSplittingOptions {\n tracks: ClipTrack[];\n onTracksChange: (tracks: ClipTrack[]) => void;\n sampleRate: number;\n samplesPerPixel: number;\n}\n\nexport interface UseClipSplittingResult {\n splitClipAtPlayhead: () => boolean;\n splitClipAt: (trackIndex: number, clipIndex: number, splitTime: number) => boolean;\n}\n\n/**\n * Hook for splitting clips at the playhead or at a specific time\n *\n * @param options - Configuration options\n * @returns Object with split functions\n *\n * @example\n * ```tsx\n * const { splitClipAtPlayhead } = useClipSplitting({\n * tracks,\n * onTracksChange: setTracks,\n * currentTime,\n * });\n *\n * // In keyboard handler\n * const handleKeyPress = (e: KeyboardEvent) => {\n * if (e.key === 's' || e.key === 'S') {\n * splitClipAtPlayhead();\n * }\n * };\n * ```\n */\nexport const useClipSplitting = (options: UseClipSplittingOptions): UseClipSplittingResult => {\n const { tracks, onTracksChange, sampleRate } = options;\n const { currentTimeRef } = usePlaybackAnimation();\n const { selectedTrackId } = usePlaylistState();\n\n /**\n * Split a specific clip at a given time\n *\n * @param trackIndex - Index of the track containing the clip\n * @param clipIndex - Index of the clip within the track\n * @param splitTime - Timeline position where to split (in seconds)\n * @returns true if split was successful, false otherwise\n */\n const splitClipAt = useCallback(\n (trackIndex: number, clipIndex: number, splitTime: number): boolean => {\n // Work with samples and pixels (all integers!) to avoid floating-point precision issues\n // Key insight: A pixel represents a RANGE of samples (samplesPerPixel samples)\n // By working in samples, we eliminate all floating-point errors\n const { sampleRate, samplesPerPixel } = options;\n\n const track = tracks[trackIndex];\n if (!track) return false;\n\n const clip = track.clips[clipIndex];\n if (!clip) return false;\n\n // Convert clip positions from samples to seconds for bounds checking\n const clipStartTime = clip.startSample / sampleRate;\n const clipEndTime = (clip.startSample + clip.durationSamples) / sampleRate;\n\n // Validate that split time is within clip bounds\n if (splitTime <= clipStartTime || splitTime >= clipEndTime) {\n console.warn('Split time is outside clip bounds');\n return false;\n }\n\n // Convert split time from seconds to samples (round to nearest sample)\n const splitSample = Math.round(splitTime * sampleRate);\n\n // Calculate pixel positions from sample positions using integer division\n const splitPixel = Math.floor(splitSample / samplesPerPixel);\n const clipEndSample = clip.startSample + clip.durationSamples;\n\n // Calculate sample positions from exact pixel boundaries\n // Both clips share the same boundary: the start of the split pixel\n const snappedSplitSample = splitPixel * samplesPerPixel;\n\n // First clip: starts at clip's original start, ends at split pixel boundary\n const firstClipStartSample = clip.startSample;\n const firstClipDurationSamples = snappedSplitSample - firstClipStartSample;\n\n // Second clip: starts at split pixel boundary, ends at clip's original end\n const secondClipStartSample = snappedSplitSample;\n const secondClipDurationSamples = clipEndSample - secondClipStartSample;\n\n // Calculate offset increment for second clip (in samples)\n const offsetIncrement = snappedSplitSample - clip.startSample;\n\n // Create first clip (from start to split point)\n const firstClip = createClip({\n audioBuffer: clip.audioBuffer,\n startSample: firstClipStartSample,\n durationSamples: firstClipDurationSamples,\n offsetSamples: clip.offsetSamples,\n sampleRate: clip.sampleRate,\n sourceDurationSamples: clip.sourceDurationSamples,\n gain: clip.gain,\n name: clip.name ? `${clip.name} (1)` : undefined,\n color: clip.color,\n fadeIn: clip.fadeIn,\n waveformData: clip.waveformData, // Share waveformData - slicing happens at render time\n // Note: fadeOut removed for first clip since it's cut\n });\n\n // Create second clip (from split point to end)\n const secondClip = createClip({\n audioBuffer: clip.audioBuffer,\n startSample: secondClipStartSample,\n durationSamples: secondClipDurationSamples,\n offsetSamples: clip.offsetSamples + offsetIncrement,\n sampleRate: clip.sampleRate,\n sourceDurationSamples: clip.sourceDurationSamples,\n gain: clip.gain,\n name: clip.name ? `${clip.name} (2)` : undefined,\n color: clip.color,\n waveformData: clip.waveformData, // Share waveformData - slicing happens at render time\n // Note: fadeIn removed for second clip since it's cut\n fadeOut: clip.fadeOut,\n });\n\n // Create new clips array with the split clips\n const newClips = [...track.clips];\n newClips.splice(clipIndex, 1, firstClip, secondClip);\n\n // Update the track with new clips\n const newTracks = [...tracks];\n newTracks[trackIndex] = {\n ...track,\n clips: newClips,\n };\n\n onTracksChange(newTracks);\n return true;\n },\n [tracks, onTracksChange, options]\n );\n\n /**\n * Split clip at the current playhead position on the selected track\n * If no track is selected, does nothing\n *\n * @returns true if a clip was split, false otherwise\n */\n const splitClipAtPlayhead = useCallback((): boolean => {\n // If no track is selected, cannot split\n if (!selectedTrackId) {\n console.log('No track selected - click a clip to select a track first');\n return false;\n }\n\n // Find the selected track\n const trackIndex = tracks.findIndex((track) => track.id === selectedTrackId);\n if (trackIndex === -1) {\n console.warn('Selected track not found');\n return false;\n }\n\n const track = tracks[trackIndex];\n\n // Use ref for real-time position during playback (state updates are throttled)\n const currentTime = currentTimeRef.current ?? 0;\n\n // Find clip at current time on the selected track\n for (let clipIndex = 0; clipIndex < track.clips.length; clipIndex++) {\n const clip = track.clips[clipIndex];\n const clipStartTime = clip.startSample / sampleRate;\n const clipEndTime = (clip.startSample + clip.durationSamples) / sampleRate;\n\n // Check if currentTime is within this clip (not at boundaries)\n if (currentTime > clipStartTime && currentTime < clipEndTime) {\n // Found a clip! Split it\n console.log(`Splitting clip on track \"${track.name}\" at ${currentTime}s`);\n return splitClipAt(trackIndex, clipIndex, currentTime);\n }\n }\n\n console.log(`No clip found at playhead position on track \"${track.name}\"`);\n return false;\n }, [tracks, currentTimeRef, selectedTrackId, splitClipAt, sampleRate]);\n\n return {\n splitClipAtPlayhead,\n splitClipAt,\n };\n};\n","import { useEffect, useCallback } from 'react';\n\nexport interface KeyboardShortcut {\n key: string;\n ctrlKey?: boolean;\n shiftKey?: boolean;\n metaKey?: boolean;\n altKey?: boolean;\n action: () => void;\n description?: string;\n preventDefault?: boolean;\n}\n\nexport interface UseKeyboardShortcutsOptions {\n shortcuts: KeyboardShortcut[];\n enabled?: boolean;\n}\n\n/**\n * Hook for managing keyboard shortcuts\n *\n * @param options - Configuration options\n *\n * @example\n * ```tsx\n * const { splitClipAtPlayhead } = useClipSplitting({ ... });\n *\n * useKeyboardShortcuts({\n * shortcuts: [\n * {\n * key: 's',\n * action: splitClipAtPlayhead,\n * description: 'Split clip at playhead',\n * preventDefault: true,\n * },\n * {\n * key: 'S',\n * shiftKey: true,\n * action: () => splitAtSelection(),\n * description: 'Split at selection boundaries',\n * preventDefault: true,\n * },\n * ],\n * });\n * ```\n */\nexport const useKeyboardShortcuts = (options: UseKeyboardShortcutsOptions): void => {\n const { shortcuts, enabled = true } = options;\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent) => {\n if (!enabled) return;\n\n // Check if we're in an input/textarea element\n const target = event.target as HTMLElement;\n if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable) {\n // Don't trigger shortcuts when typing in input fields\n return;\n }\n\n // Find matching shortcut\n const matchingShortcut = shortcuts.find((shortcut) => {\n const keyMatch =\n event.key.toLowerCase() === shortcut.key.toLowerCase() || event.key === shortcut.key;\n\n const ctrlMatch = shortcut.ctrlKey === undefined || event.ctrlKey === shortcut.ctrlKey;\n const shiftMatch = shortcut.shiftKey === undefined || event.shiftKey === shortcut.shiftKey;\n const metaMatch = shortcut.metaKey === undefined || event.metaKey === shortcut.metaKey;\n const altMatch = shortcut.altKey === undefined || event.altKey === shortcut.altKey;\n\n return keyMatch && ctrlMatch && shiftMatch && metaMatch && altMatch;\n });\n\n if (matchingShortcut) {\n if (matchingShortcut.preventDefault !== false) {\n event.preventDefault();\n }\n matchingShortcut.action();\n }\n },\n [shortcuts, enabled]\n );\n\n useEffect(() => {\n if (!enabled) return;\n\n window.addEventListener('keydown', handleKeyDown);\n\n return () => {\n window.removeEventListener('keydown', handleKeyDown);\n };\n }, [handleKeyDown, enabled]);\n};\n\n/**\n * Get a human-readable string representation of a keyboard shortcut\n *\n * @param shortcut - The keyboard shortcut\n * @returns Human-readable string (e.g., \"Cmd+Shift+S\")\n */\nexport const getShortcutLabel = (shortcut: KeyboardShortcut): string => {\n const parts: string[] = [];\n\n // Use Cmd on Mac, Ctrl on other platforms\n const isMac = typeof navigator !== 'undefined' && navigator.platform.includes('Mac');\n\n if (shortcut.metaKey) {\n parts.push(isMac ? 'Cmd' : 'Ctrl');\n }\n\n if (shortcut.ctrlKey && !shortcut.metaKey) {\n parts.push('Ctrl');\n }\n\n if (shortcut.altKey) {\n parts.push(isMac ? 'Option' : 'Alt');\n }\n\n if (shortcut.shiftKey) {\n parts.push('Shift');\n }\n\n parts.push(shortcut.key.toUpperCase());\n\n return parts.join('+');\n};\n","import { useCallback } from 'react';\nimport {\n usePlaybackAnimation,\n usePlaylistControls,\n usePlaylistData,\n} from '../WaveformPlaylistContext';\nimport { useKeyboardShortcuts, type KeyboardShortcut } from './useKeyboardShortcuts';\n\nexport interface UsePlaybackShortcutsOptions {\n /**\n * Enable the shortcuts. Defaults to true.\n */\n enabled?: boolean;\n /**\n * Additional shortcuts to include alongside the default playback shortcuts.\n */\n additionalShortcuts?: KeyboardShortcut[];\n /**\n * Override default shortcuts. If provided, only these shortcuts will be used.\n */\n shortcuts?: KeyboardShortcut[];\n}\n\nexport interface UsePlaybackShortcutsReturn {\n /** Rewind to the beginning (time = 0) */\n rewindToStart: () => void;\n /** Toggle play/pause */\n togglePlayPause: () => void;\n /** Stop playback and return to start position */\n stopPlayback: () => void;\n /** The list of active keyboard shortcuts */\n shortcuts: KeyboardShortcut[];\n}\n\n/**\n * Hook that provides common playback keyboard shortcuts for the playlist.\n *\n * Default shortcuts:\n * - `Space` - Toggle play/pause\n * - `Escape` - Stop playback\n * - `0` - Rewind to start (seek to time 0)\n *\n * @example\n * ```tsx\n * // Basic usage - enables default shortcuts\n * usePlaybackShortcuts();\n *\n * // With additional custom shortcuts\n * usePlaybackShortcuts({\n * additionalShortcuts: [\n * { key: 's', action: splitClipAtPlayhead, description: 'Split clip' },\n * ],\n * });\n *\n * // Completely override shortcuts\n * usePlaybackShortcuts({\n * shortcuts: [\n * { key: 'Home', action: rewindToStart, description: 'Go to start' },\n * ],\n * });\n * ```\n */\nexport const usePlaybackShortcuts = (\n options: UsePlaybackShortcutsOptions = {}\n): UsePlaybackShortcutsReturn => {\n const { enabled = true, additionalShortcuts = [], shortcuts: overrideShortcuts } = options;\n\n const { isPlaying } = usePlaybackAnimation();\n const { setCurrentTime, play, pause, stop } = usePlaylistControls();\n const { playoutRef } = usePlaylistData();\n\n /**\n * Toggle between play and pause.\n */\n const togglePlayPause = useCallback(() => {\n if (isPlaying) {\n pause();\n } else {\n play();\n }\n }, [isPlaying, play, pause]);\n\n /**\n * Stop playback and return to start position.\n */\n const stopPlayback = useCallback(() => {\n stop();\n }, [stop]);\n\n /**\n * Rewind to the beginning of the timeline.\n * If playing, stops and restarts playback from the beginning.\n */\n const rewindToStart = useCallback(() => {\n if (isPlaying && playoutRef.current) {\n playoutRef.current.stop();\n setCurrentTime(0);\n play(0);\n } else {\n setCurrentTime(0);\n }\n }, [isPlaying, playoutRef, setCurrentTime, play]);\n\n // Default playback shortcuts\n const defaultShortcuts: KeyboardShortcut[] = [\n {\n key: ' ',\n action: togglePlayPause,\n description: 'Play/Pause',\n preventDefault: true,\n },\n {\n key: 'Escape',\n action: stopPlayback,\n description: 'Stop',\n preventDefault: true,\n },\n {\n key: '0',\n action: rewindToStart,\n description: 'Rewind to start',\n preventDefault: true,\n },\n ];\n\n // Use override shortcuts if provided, otherwise combine defaults with additional\n const activeShortcuts = overrideShortcuts ?? [...defaultShortcuts, ...additionalShortcuts];\n\n // Register the keyboard shortcuts\n useKeyboardShortcuts({\n shortcuts: activeShortcuts,\n enabled,\n });\n\n return {\n rewindToStart,\n togglePlayPause,\n stopPlayback,\n shortcuts: activeShortcuts,\n };\n};\n","import { useCallback, useMemo, useEffect } from 'react';\nimport type { AnnotationData } from '@waveform-playlist/core';\nimport { useKeyboardShortcuts } from './useKeyboardShortcuts';\n\nconst LINK_THRESHOLD = 0.01; // Consider edges \"linked\" if within 10ms\nconst TIME_DELTA = 0.01; // 10ms adjustment per keypress\n\ninterface UseAnnotationKeyboardControlsOptions {\n annotations: AnnotationData[];\n activeAnnotationId: string | null;\n onAnnotationsChange: (annotations: AnnotationData[]) => void;\n /** Callback to set the active annotation ID for selection */\n onActiveAnnotationChange?: (id: string | null) => void;\n duration: number;\n linkEndpoints: boolean;\n /** Whether continuous play is enabled (affects playback duration) */\n continuousPlay?: boolean;\n enabled?: boolean;\n /** Optional: scroll container ref for auto-scrolling to annotation */\n scrollContainerRef?: React.RefObject<HTMLDivElement | null>;\n /** Optional: samples per pixel for scroll position calculation */\n samplesPerPixel?: number;\n /** Optional: sample rate for scroll position calculation */\n sampleRate?: number;\n /** Optional: controls width offset for scroll position calculation */\n controlsWidth?: number;\n /** Optional: callback to start playback at a time with optional duration */\n onPlay?: (startTime: number, duration?: number) => void;\n}\n\n/**\n * Hook for keyboard-based annotation navigation and boundary editing\n *\n * Navigation Shortcuts:\n * - ArrowUp / ArrowLeft = Select previous annotation\n * - ArrowDown / ArrowRight = Select next annotation\n * - Home = Select first annotation\n * - End = Select last annotation\n * - Escape = Deselect annotation\n * - Enter = Play selected annotation\n *\n * Boundary Editing Shortcuts (requires active annotation):\n * - [ = Move start boundary earlier (left)\n * - ] = Move start boundary later (right)\n * - Shift+[ = Move end boundary earlier (left)\n * - Shift+] = Move end boundary later (right)\n *\n * Respects linkEndpoints and continuousPlay settings.\n *\n * @example\n * ```tsx\n * useAnnotationKeyboardControls({\n * annotations,\n * activeAnnotationId,\n * onAnnotationsChange: setAnnotations,\n * onActiveAnnotationChange: setActiveAnnotationId,\n * duration,\n * linkEndpoints,\n * });\n * ```\n */\nexport function useAnnotationKeyboardControls({\n annotations,\n activeAnnotationId,\n onAnnotationsChange,\n onActiveAnnotationChange,\n duration,\n linkEndpoints,\n continuousPlay = false,\n enabled = true,\n scrollContainerRef,\n samplesPerPixel,\n sampleRate,\n controlsWidth = 0,\n onPlay,\n}: UseAnnotationKeyboardControlsOptions) {\n const activeIndex = useMemo(() => {\n if (!activeAnnotationId) return -1;\n return annotations.findIndex((a) => a.id === activeAnnotationId);\n }, [annotations, activeAnnotationId]);\n\n // Scroll waveform to show a specific annotation\n const scrollToAnnotation = useCallback(\n (annotationId: string) => {\n if (!scrollContainerRef?.current || !samplesPerPixel || !sampleRate) return;\n\n const annotation = annotations.find((a) => a.id === annotationId);\n if (!annotation) return;\n\n const container = scrollContainerRef.current;\n const containerWidth = container.clientWidth;\n\n // Calculate pixel positions for annotation start and center\n const startPixel = (annotation.start * sampleRate) / samplesPerPixel + controlsWidth;\n const endPixel = (annotation.end * sampleRate) / samplesPerPixel + controlsWidth;\n const annotationCenter = (startPixel + endPixel) / 2;\n\n // Check if annotation is currently visible\n const scrollLeft = container.scrollLeft;\n const visibleStart = scrollLeft;\n const visibleEnd = scrollLeft + containerWidth;\n\n // If annotation is not fully visible, scroll to center it\n if (startPixel < visibleStart || endPixel > visibleEnd) {\n const targetScrollLeft = Math.max(0, annotationCenter - containerWidth / 2);\n container.scrollTo({\n left: targetScrollLeft,\n behavior: 'smooth',\n });\n }\n },\n [annotations, scrollContainerRef, samplesPerPixel, sampleRate, controlsWidth]\n );\n\n // Auto-scroll when active annotation changes via keyboard navigation\n useEffect(() => {\n if (activeAnnotationId && scrollContainerRef?.current && samplesPerPixel && sampleRate) {\n scrollToAnnotation(activeAnnotationId);\n }\n }, [activeAnnotationId, scrollToAnnotation, scrollContainerRef, samplesPerPixel, sampleRate]);\n\n const moveStartBoundary = useCallback(\n (delta: number) => {\n if (activeIndex < 0) return;\n\n const annotation = annotations[activeIndex];\n const newStart = Math.max(0, Math.min(annotation.end - 0.1, annotation.start + delta));\n const actualDelta = newStart - annotation.start;\n\n const updatedAnnotations = [...annotations];\n updatedAnnotations[activeIndex] = {\n ...annotation,\n start: newStart,\n };\n\n // Handle linked endpoints\n if (linkEndpoints && activeIndex > 0) {\n const prevAnnotation = updatedAnnotations[activeIndex - 1];\n if (Math.abs(prevAnnotation.end - annotation.start) < LINK_THRESHOLD) {\n // Already linked: move previous annotation's end together\n updatedAnnotations[activeIndex - 1] = {\n ...prevAnnotation,\n end: Math.max(prevAnnotation.start + 0.1, prevAnnotation.end + actualDelta),\n };\n }\n } else if (!linkEndpoints && activeIndex > 0) {\n // Non-linked mode: don't overlap previous annotation\n const prevAnnotation = updatedAnnotations[activeIndex - 1];\n if (newStart < prevAnnotation.end) {\n // Push back previous annotation's end\n updatedAnnotations[activeIndex - 1] = {\n ...prevAnnotation,\n end: newStart,\n };\n }\n }\n\n onAnnotationsChange(updatedAnnotations);\n },\n [annotations, activeIndex, linkEndpoints, onAnnotationsChange]\n );\n\n const moveEndBoundary = useCallback(\n (delta: number) => {\n if (activeIndex < 0) return;\n\n const annotation = annotations[activeIndex];\n const newEnd = Math.max(annotation.start + 0.1, Math.min(duration, annotation.end + delta));\n const actualDelta = newEnd - annotation.end;\n\n const updatedAnnotations = [...annotations];\n updatedAnnotations[activeIndex] = {\n ...annotation,\n end: newEnd,\n };\n\n // Handle linked endpoints\n if (linkEndpoints && activeIndex < annotations.length - 1) {\n const nextAnnotation = updatedAnnotations[activeIndex + 1];\n if (Math.abs(nextAnnotation.start - annotation.end) < LINK_THRESHOLD) {\n // Already linked: move next annotation's start together\n const newNextStart = Math.min(\n nextAnnotation.end - 0.1,\n nextAnnotation.start + actualDelta\n );\n updatedAnnotations[activeIndex + 1] = {\n ...nextAnnotation,\n start: newNextStart,\n };\n\n // Cascade linked endpoints\n let currentIndex = activeIndex + 1;\n while (currentIndex < updatedAnnotations.length - 1) {\n const current = updatedAnnotations[currentIndex];\n const next = updatedAnnotations[currentIndex + 1];\n\n if (Math.abs(next.start - annotations[currentIndex].end) < LINK_THRESHOLD) {\n const nextDelta = current.end - annotations[currentIndex].end;\n updatedAnnotations[currentIndex + 1] = {\n ...next,\n start: Math.min(next.end - 0.1, next.start + nextDelta),\n };\n currentIndex++;\n } else {\n break;\n }\n }\n }\n } else if (!linkEndpoints && activeIndex < annotations.length - 1) {\n // Non-linked mode: don't overlap next annotation\n const nextAnnotation = updatedAnnotations[activeIndex + 1];\n if (newEnd > nextAnnotation.start) {\n // Push forward next annotation's start\n updatedAnnotations[activeIndex + 1] = {\n ...nextAnnotation,\n start: newEnd,\n };\n\n // Cascade collisions\n let currentIndex = activeIndex + 1;\n while (currentIndex < updatedAnnotations.length - 1) {\n const current = updatedAnnotations[currentIndex];\n const next = updatedAnnotations[currentIndex + 1];\n\n if (current.end > next.start) {\n updatedAnnotations[currentIndex + 1] = {\n ...next,\n start: current.end,\n };\n currentIndex++;\n } else {\n break;\n }\n }\n }\n }\n\n onAnnotationsChange(updatedAnnotations);\n },\n [annotations, activeIndex, duration, linkEndpoints, onAnnotationsChange]\n );\n\n // Navigation functions\n const selectPrevious = useCallback(() => {\n if (!onActiveAnnotationChange || annotations.length === 0) return;\n\n if (activeIndex <= 0) {\n // If no selection or at first, select last annotation\n onActiveAnnotationChange(annotations[annotations.length - 1].id);\n } else {\n onActiveAnnotationChange(annotations[activeIndex - 1].id);\n }\n }, [annotations, activeIndex, onActiveAnnotationChange]);\n\n const selectNext = useCallback(() => {\n if (!onActiveAnnotationChange || annotations.length === 0) return;\n\n if (activeIndex < 0 || activeIndex >= annotations.length - 1) {\n // If no selection or at last, select first annotation\n onActiveAnnotationChange(annotations[0].id);\n } else {\n onActiveAnnotationChange(annotations[activeIndex + 1].id);\n }\n }, [annotations, activeIndex, onActiveAnnotationChange]);\n\n const selectFirst = useCallback(() => {\n if (!onActiveAnnotationChange || annotations.length === 0) return;\n onActiveAnnotationChange(annotations[0].id);\n }, [annotations, onActiveAnnotationChange]);\n\n const selectLast = useCallback(() => {\n if (!onActiveAnnotationChange || annotations.length === 0) return;\n onActiveAnnotationChange(annotations[annotations.length - 1].id);\n }, [annotations, onActiveAnnotationChange]);\n\n const clearSelection = useCallback(() => {\n if (!onActiveAnnotationChange) return;\n onActiveAnnotationChange(null);\n }, [onActiveAnnotationChange]);\n\n // Play the currently selected annotation\n const playActiveAnnotation = useCallback(() => {\n if (activeIndex < 0 || !onPlay) return;\n\n const annotation = annotations[activeIndex];\n // If continuous play is off, play just this annotation's duration\n const playDuration = !continuousPlay ? annotation.end - annotation.start : undefined;\n onPlay(annotation.start, playDuration);\n }, [annotations, activeIndex, continuousPlay, onPlay]);\n\n // Shortcuts that require an active annotation (boundary editing + playback)\n const activeAnnotationShortcuts = useMemo(\n () => [\n {\n key: '[',\n action: () => moveStartBoundary(-TIME_DELTA),\n description: 'Move annotation start earlier',\n preventDefault: true,\n },\n {\n key: ']',\n action: () => moveStartBoundary(TIME_DELTA),\n description: 'Move annotation start later',\n preventDefault: true,\n },\n {\n key: '{',\n shiftKey: true,\n action: () => moveEndBoundary(-TIME_DELTA),\n description: 'Move annotation end earlier',\n preventDefault: true,\n },\n {\n key: '}',\n shiftKey: true,\n action: () => moveEndBoundary(TIME_DELTA),\n description: 'Move annotation end later',\n preventDefault: true,\n },\n {\n key: 'Enter',\n action: playActiveAnnotation,\n description: 'Play selected annotation',\n preventDefault: true,\n },\n ],\n [moveStartBoundary, moveEndBoundary, playActiveAnnotation]\n );\n\n // Navigation shortcuts (always active when enabled and there are annotations)\n const navigationShortcuts = useMemo(\n () => [\n {\n key: 'ArrowUp',\n action: selectPrevious,\n description: 'Select previous annotation',\n preventDefault: true,\n },\n {\n key: 'ArrowLeft',\n action: selectPrevious,\n description: 'Select previous annotation',\n preventDefault: true,\n },\n {\n key: 'ArrowDown',\n action: selectNext,\n description: 'Select next annotation',\n preventDefault: true,\n },\n {\n key: 'ArrowRight',\n action: selectNext,\n description: 'Select next annotation',\n preventDefault: true,\n },\n {\n key: 'Home',\n action: selectFirst,\n description: 'Select first annotation',\n preventDefault: true,\n },\n {\n key: 'End',\n action: selectLast,\n description: 'Select last annotation',\n preventDefault: true,\n },\n {\n key: 'Escape',\n action: clearSelection,\n description: 'Deselect annotation',\n preventDefault: true,\n },\n ],\n [selectPrevious, selectNext, selectFirst, selectLast, clearSelection]\n );\n\n // Active annotation shortcuts only work when an annotation is selected\n useKeyboardShortcuts({\n shortcuts: activeAnnotationShortcuts,\n enabled: enabled && activeIndex >= 0,\n });\n\n // Navigation shortcuts work whenever there are annotations\n useKeyboardShortcuts({\n shortcuts: navigationShortcuts,\n enabled: enabled && annotations.length > 0 && !!onActiveAnnotationChange,\n });\n\n return {\n moveStartBoundary,\n moveEndBoundary,\n selectPrevious,\n selectNext,\n selectFirst,\n selectLast,\n clearSelection,\n scrollToAnnotation,\n playActiveAnnotation,\n };\n}\n","/**\n * Effect definitions for all available Tone.js effects\n * Each effect has parameters with min/max/default values for UI controls\n */\n\nexport type ParameterType = 'number' | 'select' | 'boolean';\n\nexport interface EffectParameter {\n name: string;\n label: string;\n type: ParameterType;\n min?: number;\n max?: number;\n step?: number;\n default: number | string | boolean;\n unit?: string;\n options?: { value: string | number; label: string }[];\n}\n\nexport interface EffectDefinition {\n id: string;\n name: string;\n category: 'delay' | 'reverb' | 'modulation' | 'distortion' | 'filter' | 'dynamics' | 'spatial';\n description: string;\n parameters: EffectParameter[];\n}\n\nexport const effectDefinitions: EffectDefinition[] = [\n // === REVERB EFFECTS ===\n {\n id: 'reverb',\n name: 'Reverb',\n category: 'reverb',\n description: 'Simple convolution reverb with adjustable decay time',\n parameters: [\n {\n name: 'decay',\n label: 'Decay',\n type: 'number',\n min: 0.1,\n max: 10,\n step: 0.1,\n default: 1.5,\n unit: 's',\n },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 0.5 },\n ],\n },\n {\n id: 'freeverb',\n name: 'Freeverb',\n category: 'reverb',\n description: 'Classic Schroeder/Moorer reverb with room size and dampening',\n parameters: [\n {\n name: 'roomSize',\n label: 'Room Size',\n type: 'number',\n min: 0,\n max: 1,\n step: 0.01,\n default: 0.7,\n },\n {\n name: 'dampening',\n label: 'Dampening',\n type: 'number',\n min: 0,\n max: 10000,\n step: 100,\n default: 3000,\n unit: 'Hz',\n },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 0.5 },\n ],\n },\n {\n id: 'jcReverb',\n name: 'JC Reverb',\n category: 'reverb',\n description: 'Attempt at Roland JC-120 chorus reverb emulation',\n parameters: [\n {\n name: 'roomSize',\n label: 'Room Size',\n type: 'number',\n min: 0,\n max: 1,\n step: 0.01,\n default: 0.5,\n },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 0.5 },\n ],\n },\n\n // === DELAY EFFECTS ===\n {\n id: 'feedbackDelay',\n name: 'Feedback Delay',\n category: 'delay',\n description: 'Delay line with feedback for echo effects',\n parameters: [\n {\n name: 'delayTime',\n label: 'Delay Time',\n type: 'number',\n min: 0,\n max: 1,\n step: 0.01,\n default: 0.25,\n unit: 's',\n },\n {\n name: 'feedback',\n label: 'Feedback',\n type: 'number',\n min: 0,\n max: 0.95,\n step: 0.01,\n default: 0.5,\n },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 0.5 },\n ],\n },\n {\n id: 'pingPongDelay',\n name: 'Ping Pong Delay',\n category: 'delay',\n description: 'Stereo delay bouncing between left and right channels',\n parameters: [\n {\n name: 'delayTime',\n label: 'Delay Time',\n type: 'number',\n min: 0,\n max: 1,\n step: 0.01,\n default: 0.25,\n unit: 's',\n },\n {\n name: 'feedback',\n label: 'Feedback',\n type: 'number',\n min: 0,\n max: 0.95,\n step: 0.01,\n default: 0.5,\n },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 0.5 },\n ],\n },\n\n // === MODULATION EFFECTS ===\n {\n id: 'chorus',\n name: 'Chorus',\n category: 'modulation',\n description: 'Creates thickness by layering detuned copies of the signal',\n parameters: [\n {\n name: 'frequency',\n label: 'Rate',\n type: 'number',\n min: 0.1,\n max: 10,\n step: 0.1,\n default: 1.5,\n unit: 'Hz',\n },\n {\n name: 'delayTime',\n label: 'Delay',\n type: 'number',\n min: 0,\n max: 20,\n step: 0.5,\n default: 3.5,\n unit: 'ms',\n },\n { name: 'depth', label: 'Depth', type: 'number', min: 0, max: 1, step: 0.01, default: 0.7 },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 0.5 },\n ],\n },\n {\n id: 'phaser',\n name: 'Phaser',\n category: 'modulation',\n description: 'Classic phaser effect using allpass filters',\n parameters: [\n {\n name: 'frequency',\n label: 'Rate',\n type: 'number',\n min: 0.1,\n max: 10,\n step: 0.1,\n default: 0.5,\n unit: 'Hz',\n },\n { name: 'octaves', label: 'Octaves', type: 'number', min: 1, max: 6, step: 1, default: 3 },\n {\n name: 'baseFrequency',\n label: 'Base Freq',\n type: 'number',\n min: 100,\n max: 2000,\n step: 10,\n default: 350,\n unit: 'Hz',\n },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 0.5 },\n ],\n },\n {\n id: 'tremolo',\n name: 'Tremolo',\n category: 'modulation',\n description: 'Rhythmic volume modulation',\n parameters: [\n {\n name: 'frequency',\n label: 'Rate',\n type: 'number',\n min: 0.1,\n max: 20,\n step: 0.1,\n default: 4,\n unit: 'Hz',\n },\n { name: 'depth', label: 'Depth', type: 'number', min: 0, max: 1, step: 0.01, default: 0.5 },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 1 },\n ],\n },\n {\n id: 'vibrato',\n name: 'Vibrato',\n category: 'modulation',\n description: 'Pitch modulation effect',\n parameters: [\n {\n name: 'frequency',\n label: 'Rate',\n type: 'number',\n min: 0.1,\n max: 20,\n step: 0.1,\n default: 5,\n unit: 'Hz',\n },\n { name: 'depth', label: 'Depth', type: 'number', min: 0, max: 1, step: 0.01, default: 0.1 },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 1 },\n ],\n },\n {\n id: 'autoPanner',\n name: 'Auto Panner',\n category: 'modulation',\n description: 'Automatic left-right panning',\n parameters: [\n {\n name: 'frequency',\n label: 'Rate',\n type: 'number',\n min: 0.1,\n max: 10,\n step: 0.1,\n default: 1,\n unit: 'Hz',\n },\n { name: 'depth', label: 'Depth', type: 'number', min: 0, max: 1, step: 0.01, default: 1 },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 1 },\n ],\n },\n\n // === FILTER EFFECTS ===\n {\n id: 'autoFilter',\n name: 'Auto Filter',\n category: 'filter',\n description: 'Automated filter sweep with LFO',\n parameters: [\n {\n name: 'frequency',\n label: 'Rate',\n type: 'number',\n min: 0.1,\n max: 10,\n step: 0.1,\n default: 1,\n unit: 'Hz',\n },\n {\n name: 'baseFrequency',\n label: 'Base Freq',\n type: 'number',\n min: 20,\n max: 2000,\n step: 10,\n default: 200,\n unit: 'Hz',\n },\n {\n name: 'octaves',\n label: 'Octaves',\n type: 'number',\n min: 0.5,\n max: 8,\n step: 0.5,\n default: 2.6,\n },\n { name: 'depth', label: 'Depth', type: 'number', min: 0, max: 1, step: 0.01, default: 1 },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 1 },\n ],\n },\n {\n id: 'autoWah',\n name: 'Auto Wah',\n category: 'filter',\n description: 'Envelope follower filter effect',\n parameters: [\n {\n name: 'baseFrequency',\n label: 'Base Freq',\n type: 'number',\n min: 20,\n max: 500,\n step: 10,\n default: 100,\n unit: 'Hz',\n },\n { name: 'octaves', label: 'Octaves', type: 'number', min: 1, max: 8, step: 1, default: 6 },\n {\n name: 'sensitivity',\n label: 'Sensitivity',\n type: 'number',\n min: -40,\n max: 0,\n step: 1,\n default: 0,\n unit: 'dB',\n },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 1 },\n ],\n },\n {\n id: 'eq3',\n name: '3-Band EQ',\n category: 'filter',\n description: 'Three band equalizer with low, mid, and high controls',\n parameters: [\n {\n name: 'low',\n label: 'Low',\n type: 'number',\n min: -24,\n max: 24,\n step: 0.5,\n default: 0,\n unit: 'dB',\n },\n {\n name: 'mid',\n label: 'Mid',\n type: 'number',\n min: -24,\n max: 24,\n step: 0.5,\n default: 0,\n unit: 'dB',\n },\n {\n name: 'high',\n label: 'High',\n type: 'number',\n min: -24,\n max: 24,\n step: 0.5,\n default: 0,\n unit: 'dB',\n },\n {\n name: 'lowFrequency',\n label: 'Low Freq',\n type: 'number',\n min: 20,\n max: 500,\n step: 10,\n default: 400,\n unit: 'Hz',\n },\n {\n name: 'highFrequency',\n label: 'High Freq',\n type: 'number',\n min: 1000,\n max: 10000,\n step: 100,\n default: 2500,\n unit: 'Hz',\n },\n ],\n },\n\n // === DISTORTION EFFECTS ===\n {\n id: 'distortion',\n name: 'Distortion',\n category: 'distortion',\n description: 'Wave shaping distortion effect',\n parameters: [\n {\n name: 'distortion',\n label: 'Drive',\n type: 'number',\n min: 0,\n max: 1,\n step: 0.01,\n default: 0.4,\n },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 1 },\n ],\n },\n {\n id: 'bitCrusher',\n name: 'Bit Crusher',\n category: 'distortion',\n description: 'Reduces bit depth for lo-fi digital texture',\n parameters: [\n { name: 'bits', label: 'Bits', type: 'number', min: 1, max: 16, step: 1, default: 4 },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 1 },\n ],\n },\n {\n id: 'chebyshev',\n name: 'Chebyshev',\n category: 'distortion',\n description: 'Waveshaping distortion using Chebyshev polynomials',\n parameters: [\n { name: 'order', label: 'Order', type: 'number', min: 1, max: 100, step: 1, default: 50 },\n { name: 'wet', label: 'Mix', type: 'number', min: 0, max: 1, step: 0.01, default: 1 },\n ],\n },\n\n // === DYNAMICS EFFECTS ===\n {\n id: 'compressor',\n name: 'Compressor',\n category: 'dynamics',\n description: 'Dynamic range compressor',\n parameters: [\n {\n name: 'threshold',\n label: 'Threshold',\n type: 'number',\n min: -60,\n max: 0,\n step: 1,\n default: -24,\n unit: 'dB',\n },\n { name: 'ratio', label: 'Ratio', type: 'number', min: 1, max: 20, step: 0.5, default: 4 },\n {\n name: 'attack',\n label: 'Attack',\n type: 'number',\n min: 0,\n max: 1,\n step: 0.001,\n default: 0.003,\n unit: 's',\n },\n {\n name: 'release',\n label: 'Release',\n type: 'number',\n min: 0,\n max: 1,\n step: 0.01,\n default: 0.25,\n unit: 's',\n },\n {\n name: 'knee',\n label: 'Knee',\n type: 'number',\n min: 0,\n max: 40,\n step: 1,\n default: 30,\n unit: 'dB',\n },\n ],\n },\n {\n id: 'limiter',\n name: 'Limiter',\n category: 'dynamics',\n description: 'Hard limiter to prevent clipping',\n parameters: [\n {\n name: 'threshold',\n label: 'Threshold',\n type: 'number',\n min: -12,\n max: 0,\n step: 0.5,\n default: -6,\n unit: 'dB',\n },\n ],\n },\n {\n id: 'gate',\n name: 'Gate',\n category: 'dynamics',\n description: 'Noise gate to silence signal below threshold',\n parameters: [\n {\n name: 'threshold',\n label: 'Threshold',\n type: 'number',\n min: -100,\n max: 0,\n step: 1,\n default: -40,\n unit: 'dB',\n },\n {\n name: 'attack',\n label: 'Attack',\n type: 'number',\n min: 0,\n max: 0.3,\n step: 0.001,\n default: 0.001,\n unit: 's',\n },\n {\n name: 'release',\n label: 'Release',\n type: 'number',\n min: 0,\n max: 0.5,\n step: 0.01,\n default: 0.1,\n unit: 's',\n },\n ],\n },\n\n // === SPATIAL EFFECTS ===\n {\n id: 'stereoWidener',\n name: 'Stereo Widener',\n category: 'spatial',\n description: 'Expands or narrows the stereo image',\n parameters: [\n { name: 'width', label: 'Width', type: 'number', min: 0, max: 1, step: 0.01, default: 0.5 },\n ],\n },\n];\n\n// Helper to get effect definition by ID\nexport const getEffectDefinition = (id: string): EffectDefinition | undefined => {\n return effectDefinitions.find((def) => def.id === id);\n};\n\n// Helper to get effects by category\nexport const getEffectsByCategory = (\n category: EffectDefinition['category']\n): EffectDefinition[] => {\n return effectDefinitions.filter((def) => def.category === category);\n};\n\n// All categories with their display names\nexport const effectCategories: { id: EffectDefinition['category']; name: string }[] = [\n { id: 'reverb', name: 'Reverb' },\n { id: 'delay', name: 'Delay' },\n { id: 'modulation', name: 'Modulation' },\n { id: 'filter', name: 'Filter' },\n { id: 'distortion', name: 'Distortion' },\n { id: 'dynamics', name: 'Dynamics' },\n { id: 'spatial', name: 'Spatial' },\n];\n","/**\n * Factory for creating Tone.js effect instances from effect definitions\n */\nimport {\n Reverb,\n Freeverb,\n JCReverb,\n FeedbackDelay,\n PingPongDelay,\n Chorus,\n Phaser,\n Tremolo,\n Vibrato,\n AutoPanner,\n AutoFilter,\n AutoWah,\n EQ3,\n Distortion,\n BitCrusher,\n Chebyshev,\n Compressor,\n Limiter,\n Gate,\n StereoWidener,\n ToneAudioNode,\n} from 'tone';\nimport type { InputNode } from 'tone';\nimport type { EffectDefinition } from './effectDefinitions';\n\n// Type for effect instance with common methods\nexport interface EffectInstance {\n effect: ToneAudioNode; // Tone.js effect instance\n id: string;\n instanceId: string;\n dispose: () => void;\n setParameter: (name: string, value: number | string | boolean) => void;\n getParameter: (name: string) => number | string | boolean | undefined;\n connect: (destination: InputNode) => void;\n disconnect: () => void;\n}\n\n// Each Tone.js effect constructor accepts different option types (ReverbOptions,\n// ChorusOptions, etc.) but all produce ToneAudioNode subclasses. We use a\n// permissive constructor signature to unify them in a single lookup map.\ntype EffectConstructor = new (options?: Record<string, number | string | boolean>) => ToneAudioNode;\n\n/** Centralizes the single unavoidable cast from a specific Tone.js effect constructor to EffectConstructor. */\nfunction asEffectConstructor(ctor: new (...args: never[]) => ToneAudioNode): EffectConstructor {\n return ctor as unknown as EffectConstructor;\n}\n\n// Map of effect IDs to their Tone.js constructors\nconst effectConstructors: Record<string, EffectConstructor> = {\n reverb: asEffectConstructor(Reverb),\n freeverb: asEffectConstructor(Freeverb),\n jcReverb: asEffectConstructor(JCReverb),\n feedbackDelay: asEffectConstructor(FeedbackDelay),\n pingPongDelay: asEffectConstructor(PingPongDelay),\n chorus: asEffectConstructor(Chorus),\n phaser: asEffectConstructor(Phaser),\n tremolo: asEffectConstructor(Tremolo),\n vibrato: asEffectConstructor(Vibrato),\n autoPanner: asEffectConstructor(AutoPanner),\n autoFilter: asEffectConstructor(AutoFilter),\n autoWah: asEffectConstructor(AutoWah),\n eq3: asEffectConstructor(EQ3),\n distortion: asEffectConstructor(Distortion),\n bitCrusher: asEffectConstructor(BitCrusher),\n chebyshev: asEffectConstructor(Chebyshev),\n compressor: asEffectConstructor(Compressor),\n limiter: asEffectConstructor(Limiter),\n gate: asEffectConstructor(Gate),\n stereoWidener: asEffectConstructor(StereoWidener),\n};\n\n// Generate unique instance ID\nlet instanceCounter = 0;\nconst generateInstanceId = (): string => {\n return `effect_${Date.now()}_${++instanceCounter}`;\n};\n\n/**\n * Create an effect instance from a definition with initial parameter values\n */\nexport function createEffectInstance(\n definition: EffectDefinition,\n initialParams?: Record<string, number | string | boolean>\n): EffectInstance {\n const Constructor = effectConstructors[definition.id];\n if (!Constructor) {\n throw new Error(`Unknown effect type: ${definition.id}`);\n }\n\n // Build initial options from definition defaults and any overrides\n const options: Record<string, number | string | boolean> = {};\n definition.parameters.forEach((param) => {\n const value = initialParams?.[param.name] ?? param.default;\n options[param.name] = value;\n });\n\n // Create the effect instance\n const effect = new Constructor(options);\n const instanceId = generateInstanceId();\n\n // Dynamic property access on Tone.js effects for parameter get/set.\n // Each effect type (Reverb, Chorus, EQ3, etc.) exposes different parameters as\n // properties or Signals. We cast to a record type for safe dynamic access.\n const effectRecord = effect as unknown as Record<string, { value?: unknown } | unknown>;\n\n return {\n effect,\n id: definition.id,\n instanceId,\n\n dispose() {\n try {\n effect.disconnect();\n effect.dispose();\n } catch (e) {\n console.warn(\n `[waveform-playlist] Error disposing effect \"${definition.id}\" (${instanceId}):`,\n e\n );\n }\n },\n\n setParameter(name: string, value: number | string | boolean) {\n // Handle special cases for different effect types\n const prop = effectRecord[name];\n if (name === 'wet') {\n const wetProp = effectRecord['wet'] as { value?: number } | undefined;\n if (wetProp && typeof wetProp === 'object' && 'value' in wetProp) {\n wetProp.value = value as number;\n return;\n }\n }\n if (prop !== undefined) {\n // Check if it's a Tone.js Signal (has .value property)\n if (prop && typeof prop === 'object' && 'value' in prop) {\n (prop as { value: unknown }).value = value;\n } else {\n effectRecord[name] = value;\n }\n }\n },\n\n getParameter(name: string): number | string | boolean | undefined {\n if (name === 'wet') {\n const wetProp = effectRecord['wet'] as { value?: number } | undefined;\n if (wetProp && typeof wetProp === 'object' && 'value' in wetProp) {\n return wetProp.value;\n }\n }\n const prop = effectRecord[name];\n if (prop !== undefined) {\n if (prop && typeof prop === 'object' && 'value' in prop) {\n return (prop as { value: unknown }).value as number | string | boolean;\n }\n return prop as number | string | boolean;\n }\n return undefined;\n },\n\n connect(destination: InputNode) {\n effect.connect(destination);\n },\n\n disconnect() {\n try {\n effect.disconnect();\n } catch (e) {\n console.warn(\n `[waveform-playlist] Error disconnecting effect \"${definition.id}\" (${instanceId}):`,\n e\n );\n }\n },\n };\n}\n\n/**\n * Create a chain of effects connected in series\n */\nexport function createEffectChain(effects: EffectInstance[]): {\n input: ToneAudioNode;\n output: ToneAudioNode;\n dispose: () => void;\n} {\n if (effects.length === 0) {\n throw new Error('Cannot create effect chain with no effects');\n }\n\n // Connect effects in series\n for (let i = 0; i < effects.length - 1; i++) {\n effects[i].effect.connect(effects[i + 1].effect);\n }\n\n return {\n input: effects[0].effect,\n output: effects[effects.length - 1].effect,\n dispose() {\n effects.forEach((e) => e.dispose());\n },\n };\n}\n","import { useState, useCallback, useRef, useEffect } from 'react';\nimport type { EffectsFunction } from '@waveform-playlist/playout';\nimport {\n effectDefinitions,\n getEffectDefinition,\n type EffectDefinition,\n} from '../effects/effectDefinitions';\nimport { createEffectInstance, type EffectInstance } from '../effects/effectFactory';\nimport { Analyser, Volume, ToneAudioNode } from 'tone';\n\nexport interface ActiveEffect {\n instanceId: string;\n effectId: string;\n definition: EffectDefinition;\n params: Record<string, number | string | boolean>;\n bypassed: boolean;\n}\n\nexport interface UseDynamicEffectsReturn {\n // State\n activeEffects: ActiveEffect[];\n availableEffects: EffectDefinition[];\n\n // Actions\n addEffect: (effectId: string) => void;\n removeEffect: (instanceId: string) => void;\n updateParameter: (\n instanceId: string,\n paramName: string,\n value: number | string | boolean\n ) => void;\n toggleBypass: (instanceId: string) => void;\n reorderEffects: (fromIndex: number, toIndex: number) => void;\n clearAllEffects: () => void;\n\n // For connecting to audio graph\n masterEffects: EffectsFunction;\n\n /**\n * Creates a fresh effects function for offline rendering.\n * This creates new effect instances that work in the offline AudioContext.\n */\n createOfflineEffectsFunction: () => EffectsFunction | undefined;\n\n // Analyser for visualization\n analyserRef: React.RefObject<Analyser | null>;\n}\n\n/**\n * Hook for managing a dynamic chain of audio effects with real-time parameter updates\n */\nexport function useDynamicEffects(fftSize: number = 256): UseDynamicEffectsReturn {\n // Track active effects in state (for UI)\n const [activeEffects, setActiveEffects] = useState<ActiveEffect[]>([]);\n\n // Ref to store current activeEffects for reading in callbacks (avoids stale closures)\n const activeEffectsRef = useRef<ActiveEffect[]>(activeEffects);\n activeEffectsRef.current = activeEffects;\n\n // Track effect instances (for audio processing)\n const effectInstancesRef = useRef<Map<string, EffectInstance>>(new Map());\n\n // Analyser for visualization\n const analyserRef = useRef<Analyser | null>(null);\n\n // Reference to the current audio graph nodes\n const graphNodesRef = useRef<{\n masterGainNode: Volume;\n destination: ToneAudioNode;\n analyserNode: Analyser;\n } | null>(null);\n\n // Rebuild the effect chain when effects change\n // Note: effects is passed as parameter to avoid stale closure issues\n const rebuildChain = useCallback((effects: ActiveEffect[]) => {\n const nodes = graphNodesRef.current;\n if (!nodes) return;\n\n const { masterGainNode, destination, analyserNode } = nodes;\n\n // Disconnect everything first\n try {\n masterGainNode.disconnect();\n } catch (e) {\n console.warn('[waveform-playlist] Error disconnecting master effects chain:', e);\n }\n\n // Get effect instances in order\n const instances = effects\n .map((ae) => effectInstancesRef.current.get(ae.instanceId))\n .filter((inst): inst is EffectInstance => inst !== undefined);\n\n if (instances.length === 0) {\n // No effects - connect directly to analyser -> destination\n masterGainNode.connect(analyserNode);\n analyserNode.connect(destination);\n } else {\n // Connect: masterGain -> effect1 -> effect2 -> ... -> analyser -> destination\n let currentNode: ToneAudioNode = masterGainNode;\n\n instances.forEach((inst) => {\n try {\n inst.disconnect();\n } catch (e) {\n console.warn(`[waveform-playlist] Error disconnecting effect \"${inst.id}\":`, e);\n }\n currentNode.connect(inst.effect);\n currentNode = inst.effect;\n });\n\n // Connect last effect to analyser\n currentNode.connect(analyserNode);\n analyserNode.connect(destination);\n }\n }, []);\n\n // Add a new effect\n const addEffect = useCallback((effectId: string) => {\n const definition = getEffectDefinition(effectId);\n if (!definition) {\n console.error(`Unknown effect: ${effectId}`);\n return;\n }\n\n // Build default params\n const params: Record<string, number | string | boolean> = {};\n definition.parameters.forEach((p) => {\n params[p.name] = p.default;\n });\n\n // Create the effect instance\n const instance = createEffectInstance(definition, params);\n effectInstancesRef.current.set(instance.instanceId, instance);\n\n // Add to state\n const newActiveEffect: ActiveEffect = {\n instanceId: instance.instanceId,\n effectId: definition.id,\n definition,\n params,\n bypassed: false,\n };\n\n setActiveEffects((prev) => [...prev, newActiveEffect]);\n }, []);\n\n // Remove an effect\n const removeEffect = useCallback((instanceId: string) => {\n const instance = effectInstancesRef.current.get(instanceId);\n if (instance) {\n instance.dispose();\n effectInstancesRef.current.delete(instanceId);\n }\n\n setActiveEffects((prev) => prev.filter((e) => e.instanceId !== instanceId));\n }, []);\n\n // Update a parameter in real-time\n const updateParameter = useCallback(\n (instanceId: string, paramName: string, value: number | string | boolean) => {\n // Update the actual effect instance\n const instance = effectInstancesRef.current.get(instanceId);\n if (instance) {\n instance.setParameter(paramName, value);\n }\n\n // Update state for UI\n setActiveEffects((prev) =>\n prev.map((e) =>\n e.instanceId === instanceId ? { ...e, params: { ...e.params, [paramName]: value } } : e\n )\n );\n },\n []\n );\n\n // Toggle bypass for an effect (uses wet parameter - 0 = bypass, restore original for active)\n const toggleBypass = useCallback((instanceId: string) => {\n // Get current state from ref to determine new bypassed value (avoids stale closure)\n const effect = activeEffectsRef.current.find((e) => e.instanceId === instanceId);\n if (!effect) return;\n\n const newBypassed = !effect.bypassed;\n\n // Update the actual effect instance\n // When bypassing: set wet to 0\n // When un-bypassing: restore the original wet value from params\n const instance = effectInstancesRef.current.get(instanceId);\n if (instance) {\n const originalWet = (effect.params.wet as number) ?? 1;\n instance.setParameter('wet', newBypassed ? 0 : originalWet);\n }\n\n // Update state for UI\n setActiveEffects((prev) =>\n prev.map((e) => (e.instanceId === instanceId ? { ...e, bypassed: newBypassed } : e))\n );\n }, []);\n\n // Reorder effects in the chain\n const reorderEffects = useCallback((fromIndex: number, toIndex: number) => {\n setActiveEffects((prev) => {\n const newEffects = [...prev];\n const [removed] = newEffects.splice(fromIndex, 1);\n newEffects.splice(toIndex, 0, removed);\n return newEffects;\n });\n }, []);\n\n // Clear all effects\n const clearAllEffects = useCallback(() => {\n // Dispose all instances\n effectInstancesRef.current.forEach((inst) => inst.dispose());\n effectInstancesRef.current.clear();\n\n setActiveEffects([]);\n }, []);\n\n // Rebuild chain when effects change\n useEffect(() => {\n rebuildChain(activeEffects);\n }, [activeEffects, rebuildChain]);\n\n // The effects function that gets passed to WaveformPlaylistProvider\n // This function is stable - it reads from refs at call time to avoid stale closures\n const masterEffects: EffectsFunction = useCallback(\n (masterGainNode, destination, _isOffline) => {\n // Create analyser for visualization\n const analyserNode = new Analyser('fft', fftSize);\n analyserRef.current = analyserNode;\n\n // Store references for rebuilding chain\n graphNodesRef.current = {\n masterGainNode,\n destination,\n analyserNode,\n };\n\n // Build initial chain - read from ref to get current state\n const effects = activeEffectsRef.current;\n const instances = effects\n .map((ae) => effectInstancesRef.current.get(ae.instanceId))\n .filter((inst): inst is EffectInstance => inst !== undefined);\n\n if (instances.length === 0) {\n // No effects - connect directly to analyser -> destination\n masterGainNode.connect(analyserNode);\n analyserNode.connect(destination);\n } else {\n // Connect: masterGain -> effect1 -> effect2 -> ... -> analyser -> destination\n let currentNode: ToneAudioNode = masterGainNode;\n\n instances.forEach((inst) => {\n currentNode.connect(inst.effect);\n currentNode = inst.effect;\n });\n\n // Connect last effect to analyser\n currentNode.connect(analyserNode);\n analyserNode.connect(destination);\n }\n\n return function cleanup() {\n analyserNode.dispose();\n analyserRef.current = null;\n graphNodesRef.current = null;\n };\n },\n [fftSize] // Only fftSize - reads effects from ref\n );\n\n // Cleanup on unmount\n useEffect(() => {\n const effectInstances = effectInstancesRef.current;\n return () => {\n effectInstances.forEach((inst) => inst.dispose());\n effectInstances.clear();\n };\n }, []);\n\n /**\n * Creates a fresh effects function for offline rendering.\n * This creates new effect instances in the offline context, avoiding the\n * AudioContext mismatch issue that occurs when reusing real-time effects.\n */\n const createOfflineEffectsFunction = useCallback((): EffectsFunction | undefined => {\n // Get non-bypassed effects\n const nonBypassedEffects = activeEffects.filter((e) => !e.bypassed);\n\n if (nonBypassedEffects.length === 0) {\n return undefined;\n }\n\n // Return a function that creates fresh effect instances\n return (masterGainNode: Volume, destination: ToneAudioNode, _isOffline: boolean) => {\n // Create fresh effect instances for offline context\n const offlineInstances: EffectInstance[] = [];\n\n for (const activeEffect of nonBypassedEffects) {\n const instance = createEffectInstance(activeEffect.definition, activeEffect.params);\n offlineInstances.push(instance);\n }\n\n if (offlineInstances.length === 0) {\n // No effects - connect directly\n masterGainNode.connect(destination);\n } else {\n // Connect: masterGain -> effect1 -> effect2 -> ... -> destination\n let currentNode: ToneAudioNode = masterGainNode;\n\n offlineInstances.forEach((inst) => {\n currentNode.connect(inst.effect);\n currentNode = inst.effect;\n });\n\n // Connect last effect to destination\n currentNode.connect(destination);\n }\n\n return function cleanup() {\n offlineInstances.forEach((inst) => inst.dispose());\n };\n };\n }, [activeEffects]);\n\n return {\n activeEffects,\n availableEffects: effectDefinitions,\n addEffect,\n removeEffect,\n updateParameter,\n toggleBypass,\n reorderEffects,\n clearAllEffects,\n masterEffects,\n createOfflineEffectsFunction,\n analyserRef,\n };\n}\n","import { useState, useCallback, useRef, useEffect } from 'react';\nimport type { TrackEffectsFunction } from '../index';\nimport {\n effectDefinitions,\n getEffectDefinition,\n type EffectDefinition,\n} from '../effects/effectDefinitions';\nimport { createEffectInstance, type EffectInstance } from '../effects/effectFactory';\nimport { Gain, ToneAudioNode } from 'tone';\n\nexport interface TrackActiveEffect {\n instanceId: string;\n effectId: string;\n definition: EffectDefinition;\n params: Record<string, number | string | boolean>;\n bypassed: boolean;\n}\n\nexport interface TrackEffectsState {\n trackId: string;\n activeEffects: TrackActiveEffect[];\n}\n\nexport interface UseTrackDynamicEffectsReturn {\n // State per track\n trackEffectsState: Map<string, TrackActiveEffect[]>;\n\n // Actions\n addEffectToTrack: (trackId: string, effectId: string) => void;\n removeEffectFromTrack: (trackId: string, instanceId: string) => void;\n updateTrackEffectParameter: (\n trackId: string,\n instanceId: string,\n paramName: string,\n value: number | string | boolean\n ) => void;\n toggleBypass: (trackId: string, instanceId: string) => void;\n clearTrackEffects: (trackId: string) => void;\n getTrackEffectsFunction: (trackId: string) => TrackEffectsFunction | undefined;\n\n /**\n * Creates a fresh effects function for a track for offline rendering.\n * This creates new effect instances that work in the offline AudioContext.\n */\n createOfflineTrackEffectsFunction: (trackId: string) => TrackEffectsFunction | undefined;\n\n // Available effects\n availableEffects: EffectDefinition[];\n}\n\n/**\n * Hook for managing dynamic effects per track with real-time parameter updates\n */\nexport function useTrackDynamicEffects(): UseTrackDynamicEffectsReturn {\n // Track effects state per track (for UI)\n const [trackEffectsState, setTrackEffectsState] = useState<Map<string, TrackActiveEffect[]>>(\n new Map()\n );\n\n // Track effect instances per track (for audio processing)\n const trackEffectInstancesRef = useRef<Map<string, Map<string, EffectInstance>>>(new Map());\n\n // Track graph nodes per track for rebuilding chains\n const trackGraphNodesRef = useRef<\n Map<\n string,\n {\n graphEnd: Gain;\n masterGainNode: ToneAudioNode;\n }\n >\n >(new Map());\n\n // Rebuild the effect chain for a specific track\n // Note: trackEffects is passed as parameter to avoid stale closure issues\n const rebuildTrackChain = useCallback((trackId: string, trackEffects: TrackActiveEffect[]) => {\n const nodes = trackGraphNodesRef.current.get(trackId);\n if (!nodes) return;\n\n const { graphEnd, masterGainNode } = nodes;\n const instancesMap = trackEffectInstancesRef.current.get(trackId);\n\n // Disconnect everything first\n try {\n graphEnd.disconnect();\n } catch (e) {\n console.warn(`[waveform-playlist] Error disconnecting track \"${trackId}\" effect chain:`, e);\n }\n\n // Get effect instances in order\n const instances = trackEffects\n .map((ae) => instancesMap?.get(ae.instanceId))\n .filter((inst): inst is EffectInstance => inst !== undefined);\n\n if (instances.length === 0) {\n // No effects - connect directly\n graphEnd.connect(masterGainNode);\n } else {\n // Connect: graphEnd -> effect1 -> effect2 -> ... -> masterGainNode\n let currentNode: ToneAudioNode = graphEnd;\n\n instances.forEach((inst) => {\n try {\n inst.disconnect();\n } catch (e) {\n console.warn(\n `[waveform-playlist] Error disconnecting effect \"${inst.id}\" on track \"${trackId}\":`,\n e\n );\n }\n currentNode.connect(inst.effect);\n currentNode = inst.effect;\n });\n\n // Connect last effect to master\n currentNode.connect(masterGainNode);\n }\n }, []);\n\n // Add a new effect to a track\n const addEffectToTrack = useCallback((trackId: string, effectId: string) => {\n const definition = getEffectDefinition(effectId);\n if (!definition) {\n console.error(`Unknown effect: ${effectId}`);\n return;\n }\n\n // Build default params\n const params: Record<string, number | string | boolean> = {};\n definition.parameters.forEach((p) => {\n params[p.name] = p.default;\n });\n\n // Create the effect instance\n const instance = createEffectInstance(definition, params);\n\n // Initialize maps if needed\n if (!trackEffectInstancesRef.current.has(trackId)) {\n trackEffectInstancesRef.current.set(trackId, new Map());\n }\n trackEffectInstancesRef.current.get(trackId)!.set(instance.instanceId, instance);\n\n // Add to state\n const newActiveEffect: TrackActiveEffect = {\n instanceId: instance.instanceId,\n effectId: definition.id,\n definition,\n params,\n bypassed: false,\n };\n\n setTrackEffectsState((prev) => {\n const newState = new Map(prev);\n const existing = newState.get(trackId) || [];\n newState.set(trackId, [...existing, newActiveEffect]);\n return newState;\n });\n }, []);\n\n // Remove an effect from a track\n const removeEffectFromTrack = useCallback((trackId: string, instanceId: string) => {\n const instancesMap = trackEffectInstancesRef.current.get(trackId);\n const instance = instancesMap?.get(instanceId);\n if (instance) {\n instance.dispose();\n instancesMap?.delete(instanceId);\n }\n\n setTrackEffectsState((prev) => {\n const newState = new Map(prev);\n const existing = newState.get(trackId) || [];\n newState.set(\n trackId,\n existing.filter((e) => e.instanceId !== instanceId)\n );\n return newState;\n });\n }, []);\n\n // Update a parameter in real-time\n const updateTrackEffectParameter = useCallback(\n (trackId: string, instanceId: string, paramName: string, value: number | string | boolean) => {\n // Update the actual effect instance\n const instancesMap = trackEffectInstancesRef.current.get(trackId);\n const instance = instancesMap?.get(instanceId);\n if (instance) {\n instance.setParameter(paramName, value);\n }\n\n // Update state for UI\n setTrackEffectsState((prev) => {\n const newState = new Map(prev);\n const existing = newState.get(trackId) || [];\n newState.set(\n trackId,\n existing.map((e) =>\n e.instanceId === instanceId ? { ...e, params: { ...e.params, [paramName]: value } } : e\n )\n );\n return newState;\n });\n },\n []\n );\n\n // Toggle bypass for an effect (uses wet parameter - 0 = bypass, restore original for active)\n const toggleBypass = useCallback((trackId: string, instanceId: string) => {\n // Get current state from ref to determine new bypassed value (avoids stale closure)\n const trackEffects = trackEffectsStateRef.current.get(trackId) || [];\n const effect = trackEffects.find((e) => e.instanceId === instanceId);\n if (!effect) return;\n\n const newBypassed = !effect.bypassed;\n\n // Update the actual effect instance\n // When bypassing: set wet to 0\n // When un-bypassing: restore the original wet value from params\n const instancesMap = trackEffectInstancesRef.current.get(trackId);\n const instance = instancesMap?.get(instanceId);\n if (instance) {\n const originalWet = (effect.params.wet as number) ?? 1;\n instance.setParameter('wet', newBypassed ? 0 : originalWet);\n }\n\n // Update state for UI\n setTrackEffectsState((prev) => {\n const newState = new Map(prev);\n const existing = newState.get(trackId) || [];\n newState.set(\n trackId,\n existing.map((e) => (e.instanceId === instanceId ? { ...e, bypassed: newBypassed } : e))\n );\n return newState;\n });\n }, []);\n\n // Clear all effects from a track\n const clearTrackEffects = useCallback((trackId: string) => {\n // Dispose all instances for this track\n const instancesMap = trackEffectInstancesRef.current.get(trackId);\n if (instancesMap) {\n instancesMap.forEach((inst) => inst.dispose());\n instancesMap.clear();\n }\n\n setTrackEffectsState((prev) => {\n const newState = new Map(prev);\n newState.set(trackId, []);\n return newState;\n });\n }, []);\n\n // Ref to store the current trackEffectsState for reading in effects function\n // This avoids stale closure issues when the effects function is called later\n const trackEffectsStateRef = useRef<Map<string, TrackActiveEffect[]>>(trackEffectsState);\n trackEffectsStateRef.current = trackEffectsState;\n\n // Get the effects function for a track to pass to useAudioTracks\n // This function is stable (no dependencies) - it reads from refs at call time\n const getTrackEffectsFunction = useCallback(\n (trackId: string): TrackEffectsFunction | undefined => {\n // Return a function that connects effects when the track is loaded\n return (graphEnd, masterGainNode, _isOffline) => {\n // Store references for rebuilding chain\n trackGraphNodesRef.current.set(trackId, {\n graphEnd,\n masterGainNode,\n });\n\n // Read current state from ref (not stale closure)\n const trackEffects = trackEffectsStateRef.current.get(trackId) || [];\n const instancesMap = trackEffectInstancesRef.current.get(trackId);\n\n // Get effect instances in order\n const instances = trackEffects\n .map((ae) => instancesMap?.get(ae.instanceId))\n .filter((inst): inst is EffectInstance => inst !== undefined);\n\n if (instances.length === 0) {\n // No effects - connect directly\n graphEnd.connect(masterGainNode);\n } else {\n // Connect: graphEnd -> effect1 -> effect2 -> ... -> masterGainNode\n let currentNode: ToneAudioNode = graphEnd;\n\n instances.forEach((inst) => {\n currentNode.connect(inst.effect);\n currentNode = inst.effect;\n });\n\n // Connect last effect to master\n currentNode.connect(masterGainNode);\n }\n\n return function cleanup() {\n trackGraphNodesRef.current.delete(trackId);\n };\n };\n },\n [] // No dependencies - stable function that reads from refs\n );\n\n // Rebuild chains when effects change\n useEffect(() => {\n trackEffectsState.forEach((effects, trackId) => {\n rebuildTrackChain(trackId, effects);\n });\n }, [trackEffectsState, rebuildTrackChain]);\n\n // Cleanup on unmount\n useEffect(() => {\n const trackEffectInstances = trackEffectInstancesRef.current;\n return () => {\n trackEffectInstances.forEach((instancesMap) => {\n instancesMap.forEach((inst) => inst.dispose());\n instancesMap.clear();\n });\n trackEffectInstances.clear();\n };\n }, []);\n\n /**\n * Creates a fresh effects function for a track for offline rendering.\n * This creates new effect instances in the offline context, avoiding the\n * AudioContext mismatch issue that occurs when reusing real-time effects.\n */\n const createOfflineTrackEffectsFunction = useCallback(\n (trackId: string): TrackEffectsFunction | undefined => {\n const trackEffects = trackEffectsState.get(trackId) || [];\n // Get non-bypassed effects\n const nonBypassedEffects = trackEffects.filter((e) => !e.bypassed);\n\n if (nonBypassedEffects.length === 0) {\n return undefined;\n }\n\n // Return a function that creates fresh effect instances\n return (graphEnd: Gain, masterGainNode: ToneAudioNode, _isOffline: boolean) => {\n // Create fresh effect instances for offline context\n const offlineInstances: EffectInstance[] = [];\n\n for (const activeEffect of nonBypassedEffects) {\n const instance = createEffectInstance(activeEffect.definition, activeEffect.params);\n offlineInstances.push(instance);\n }\n\n if (offlineInstances.length === 0) {\n // No effects - connect directly\n graphEnd.connect(masterGainNode);\n } else {\n // Connect: graphEnd -> effect1 -> effect2 -> ... -> masterGainNode\n let currentNode: ToneAudioNode = graphEnd;\n\n offlineInstances.forEach((inst) => {\n currentNode.connect(inst.effect);\n currentNode = inst.effect;\n });\n\n // Connect last effect to master\n currentNode.connect(masterGainNode);\n }\n\n return function cleanup() {\n offlineInstances.forEach((inst) => inst.dispose());\n };\n };\n },\n [trackEffectsState]\n );\n\n return {\n trackEffectsState,\n addEffectToTrack,\n removeEffectFromTrack,\n updateTrackEffectParameter,\n toggleBypass,\n clearTrackEffects,\n getTrackEffectsFunction,\n createOfflineTrackEffectsFunction,\n availableEffects: effectDefinitions,\n };\n}\n","/**\n * WAV file encoder\n * Converts AudioBuffer to WAV format Blob\n */\n\nexport interface WavEncoderOptions {\n /** Bit depth: 16 or 32. Default: 16 */\n bitDepth?: 16 | 32;\n}\n\n/**\n * Encode an AudioBuffer to WAV format\n * @param audioBuffer - The AudioBuffer to encode\n * @param options - Encoding options\n * @returns WAV file as Blob\n */\nexport function encodeWav(audioBuffer: AudioBuffer, options: WavEncoderOptions = {}): Blob {\n const { bitDepth = 16 } = options;\n\n const numChannels = audioBuffer.numberOfChannels;\n const sampleRate = audioBuffer.sampleRate;\n const numSamples = audioBuffer.length;\n const bytesPerSample = bitDepth / 8;\n const blockAlign = numChannels * bytesPerSample;\n const byteRate = sampleRate * blockAlign;\n const dataSize = numSamples * blockAlign;\n\n // WAV header is 44 bytes\n const headerSize = 44;\n const totalSize = headerSize + dataSize;\n\n const buffer = new ArrayBuffer(totalSize);\n const view = new DataView(buffer);\n\n // Write WAV header\n // RIFF chunk descriptor\n writeString(view, 0, 'RIFF');\n view.setUint32(4, totalSize - 8, true); // File size minus RIFF header\n writeString(view, 8, 'WAVE');\n\n // fmt sub-chunk\n writeString(view, 12, 'fmt ');\n view.setUint32(16, 16, true); // Subchunk1Size (16 for PCM)\n view.setUint16(20, bitDepth === 32 ? 3 : 1, true); // AudioFormat (1=PCM, 3=IEEE float)\n view.setUint16(22, numChannels, true);\n view.setUint32(24, sampleRate, true);\n view.setUint32(28, byteRate, true);\n view.setUint16(32, blockAlign, true);\n view.setUint16(34, bitDepth, true);\n\n // data sub-chunk\n writeString(view, 36, 'data');\n view.setUint32(40, dataSize, true);\n\n // Write interleaved audio data\n const channelData: Float32Array[] = [];\n for (let ch = 0; ch < numChannels; ch++) {\n channelData.push(audioBuffer.getChannelData(ch));\n }\n\n let offset = headerSize;\n\n if (bitDepth === 16) {\n // 16-bit PCM\n for (let i = 0; i < numSamples; i++) {\n for (let ch = 0; ch < numChannels; ch++) {\n const sample = channelData[ch][i];\n // Clamp to [-1, 1] and convert to 16-bit signed integer\n const clampedSample = Math.max(-1, Math.min(1, sample));\n const intSample = clampedSample < 0 ? clampedSample * 0x8000 : clampedSample * 0x7fff;\n view.setInt16(offset, intSample, true);\n offset += 2;\n }\n }\n } else {\n // 32-bit IEEE float\n for (let i = 0; i < numSamples; i++) {\n for (let ch = 0; ch < numChannels; ch++) {\n view.setFloat32(offset, channelData[ch][i], true);\n offset += 4;\n }\n }\n }\n\n return new Blob([buffer], { type: 'audio/wav' });\n}\n\n/**\n * Write a string to a DataView at the specified offset\n */\nfunction writeString(view: DataView, offset: number, str: string): void {\n for (let i = 0; i < str.length; i++) {\n view.setUint8(offset + i, str.charCodeAt(i));\n }\n}\n\n/**\n * Trigger a download of a Blob with the specified filename\n */\nexport function downloadBlob(blob: Blob, filename: string): void {\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = filename;\n a.style.display = 'none';\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n}\n","import { useState, useCallback } from 'react';\nimport type { ClipTrack, AudioClip, FadeType } from '@waveform-playlist/core';\nimport { type EffectsFunction, getUnderlyingAudioParam } from '@waveform-playlist/playout';\nimport { encodeWav, downloadBlob, type WavEncoderOptions } from '../utils/wavEncoder';\n\n/** Function type for per-track effects (same as in @waveform-playlist/core) */\nexport type TrackEffectsFunction = (\n graphEnd: unknown,\n destination: unknown,\n isOffline: boolean\n) => void | (() => void);\n\nexport interface ExportOptions extends WavEncoderOptions {\n /** Filename for download (without extension) */\n filename?: string;\n /** Export mode: 'master' for stereo mix, 'individual' for single track */\n mode?: 'master' | 'individual';\n /** Track index for individual export (only used when mode is 'individual') */\n trackIndex?: number;\n /** Whether to trigger automatic download */\n autoDownload?: boolean;\n /** Whether to apply effects (fades, etc.) - defaults to true */\n applyEffects?: boolean;\n /**\n * Optional Tone.js effects function for master effects. When provided, export will use Tone.Offline\n * to render through the effects chain. The function receives isOffline=true.\n */\n effectsFunction?: EffectsFunction;\n /**\n * Optional function to create offline track effects.\n * Takes a trackId and returns a TrackEffectsFunction for offline rendering.\n * This is used instead of track.effects to avoid AudioContext mismatch issues.\n */\n createOfflineTrackEffects?: (trackId: string) => TrackEffectsFunction | undefined;\n /** Progress callback (0-1) */\n onProgress?: (progress: number) => void;\n}\n\nexport interface ExportResult {\n /** The rendered audio buffer */\n audioBuffer: AudioBuffer;\n /** The WAV file as a Blob */\n blob: Blob;\n /** Duration in seconds */\n duration: number;\n}\n\nexport interface UseExportWavReturn {\n /** Export the playlist to WAV */\n exportWav: (\n tracks: ClipTrack[],\n trackStates: TrackState[],\n options?: ExportOptions\n ) => Promise<ExportResult>;\n /** Whether export is in progress */\n isExporting: boolean;\n /** Export progress (0-1) */\n progress: number;\n /** Error message if export failed */\n error: string | null;\n}\n\ninterface TrackState {\n muted: boolean;\n soloed: boolean;\n volume: number;\n pan: number;\n}\n\n/**\n * Hook for exporting the waveform playlist to WAV format\n * Uses OfflineAudioContext for fast, non-real-time rendering\n */\nexport function useExportWav(): UseExportWavReturn {\n const [isExporting, setIsExporting] = useState(false);\n const [progress, setProgress] = useState(0);\n const [error, setError] = useState<string | null>(null);\n\n const exportWav = useCallback(\n async (\n tracks: ClipTrack[],\n trackStates: TrackState[],\n options: ExportOptions = {}\n ): Promise<ExportResult> => {\n const {\n filename = 'export',\n mode = 'master',\n trackIndex,\n autoDownload = true,\n applyEffects = true,\n effectsFunction,\n createOfflineTrackEffects,\n bitDepth = 16,\n onProgress,\n } = options;\n\n setIsExporting(true);\n setProgress(0);\n setError(null);\n\n try {\n // Validate inputs\n if (tracks.length === 0) {\n throw new Error('No tracks to export');\n }\n\n if (\n mode === 'individual' &&\n (trackIndex === undefined || trackIndex < 0 || trackIndex >= tracks.length)\n ) {\n throw new Error('Invalid track index for individual export');\n }\n\n // Get sample rate from first clip (use clip.sampleRate which is always defined)\n const sampleRate = tracks[0].clips[0]?.sampleRate || 44100;\n\n // Calculate total duration from all clips (in samples)\n let totalDurationSamples = 0;\n for (const track of tracks) {\n for (const clip of track.clips) {\n const clipEndSample = clip.startSample + clip.durationSamples;\n totalDurationSamples = Math.max(totalDurationSamples, clipEndSample);\n }\n }\n\n // Add a small buffer at the end (0.1 seconds) to avoid cutting off\n totalDurationSamples += Math.round(sampleRate * 0.1);\n\n const duration = totalDurationSamples / sampleRate;\n\n // Determine which tracks to render\n const tracksToRender =\n mode === 'individual'\n ? [{ track: tracks[trackIndex!], state: trackStates[trackIndex!], index: trackIndex! }]\n : tracks.map((track, index) => ({ track, state: trackStates[index], index }));\n\n // Check for solo - if any track is soloed, only play soloed tracks\n const hasSolo = trackStates.some((state) => state.soloed);\n\n // Check if per-track effects are provided via the offline creator function\n // Note: We don't use track.effects directly for offline rendering to avoid AudioContext mismatch\n const hasOfflineTrackEffects = !!createOfflineTrackEffects;\n\n let renderedBuffer: AudioBuffer;\n\n if ((effectsFunction || hasOfflineTrackEffects) && applyEffects) {\n // Use Tone.Offline for rendering with effects (master and/or per-track)\n renderedBuffer = await renderWithToneEffects(\n tracksToRender,\n trackStates,\n hasSolo,\n duration,\n sampleRate,\n effectsFunction,\n createOfflineTrackEffects,\n (p) => {\n setProgress(p);\n onProgress?.(p);\n }\n );\n } else {\n // Use standard OfflineAudioContext rendering\n const offlineCtx = new OfflineAudioContext(2, totalDurationSamples, sampleRate);\n\n // Schedule all clips for rendering\n let scheduledClips = 0;\n const totalClips = tracksToRender.reduce((sum, { track }) => sum + track.clips.length, 0);\n\n for (const { track, state } of tracksToRender) {\n // Skip muted tracks (unless soloed)\n if (state.muted && !state.soloed) continue;\n // If there's a solo and this track isn't soloed, skip it\n if (hasSolo && !state.soloed) continue;\n\n for (const clip of track.clips) {\n await scheduleClip(offlineCtx, clip, state, sampleRate, applyEffects);\n scheduledClips++;\n const currentProgress = (scheduledClips / totalClips) * 0.5; // First 50% is scheduling\n setProgress(currentProgress);\n onProgress?.(currentProgress);\n }\n }\n\n // Render the audio\n setProgress(0.5);\n onProgress?.(0.5);\n\n renderedBuffer = await offlineCtx.startRendering();\n }\n\n setProgress(0.9);\n onProgress?.(0.9);\n\n // Encode to WAV\n const blob = encodeWav(renderedBuffer, { bitDepth });\n\n setProgress(1);\n onProgress?.(1);\n\n // Auto download if requested\n if (autoDownload) {\n const exportFilename =\n mode === 'individual' ? `${filename}_${tracks[trackIndex!].name}` : filename;\n downloadBlob(blob, `${exportFilename}.wav`);\n }\n\n return {\n audioBuffer: renderedBuffer,\n blob,\n duration,\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Export failed';\n setError(message);\n throw err;\n } finally {\n setIsExporting(false);\n }\n },\n []\n );\n\n return {\n exportWav,\n isExporting,\n progress,\n error,\n };\n}\n\n/**\n * Render using Tone.Offline with effects chain (master and/or per-track)\n */\nasync function renderWithToneEffects(\n tracksToRender: { track: ClipTrack; state: TrackState; index: number }[],\n _trackStates: TrackState[],\n hasSolo: boolean,\n duration: number,\n sampleRate: number,\n effectsFunction: EffectsFunction | undefined,\n createOfflineTrackEffects: ((trackId: string) => TrackEffectsFunction | undefined) | undefined,\n onProgress: (progress: number) => void\n): Promise<AudioBuffer> {\n // Dynamically import Tone.js modules\n const { Offline, Volume, Gain, Panner, Player, ToneAudioBuffer } = await import('tone');\n\n onProgress(0.1);\n\n // Use Tone.Offline to render with effects\n let buffer;\n try {\n buffer = await Offline(\n async ({ transport, destination }) => {\n // Create master volume node\n const masterVolume = new Volume(0); // 0 dB = unity gain\n\n // Apply master effects chain if provided, otherwise connect directly to destination\n let cleanup: void | (() => void) = undefined;\n if (effectsFunction) {\n cleanup = effectsFunction(masterVolume, destination, true);\n } else {\n masterVolume.connect(destination);\n }\n\n // Schedule all clips\n for (const { track, state } of tracksToRender) {\n // Skip muted tracks (unless soloed)\n if (state.muted && !state.soloed) continue;\n // If there's a solo and this track isn't soloed, skip it\n if (hasSolo && !state.soloed) continue;\n\n // Create track-level nodes\n const trackVolume = new Volume(gainToDb(state.volume));\n const trackPan = new Panner(state.pan);\n const trackMute = new Gain(state.muted ? 0 : 1);\n\n // Get offline track effects using the creator function\n // Note: We use createOfflineTrackEffects instead of track.effects to avoid AudioContext mismatch\n const trackEffects = createOfflineTrackEffects?.(track.id);\n\n if (trackEffects) {\n // Apply per-track effects chain: trackMute -> effects -> masterVolume\n trackEffects(trackMute, masterVolume, true);\n } else {\n // No per-track effects: connect directly to master\n trackMute.connect(masterVolume);\n }\n\n // Connect track chain: clips -> trackVolume -> trackPan -> trackMute\n trackPan.connect(trackMute);\n trackVolume.connect(trackPan);\n\n // Schedule each clip\n for (const clip of track.clips) {\n const {\n audioBuffer,\n startSample,\n durationSamples,\n offsetSamples,\n gain: clipGain,\n fadeIn,\n fadeOut,\n } = clip;\n\n // Convert samples to seconds\n const startTime = startSample / sampleRate;\n const clipDuration = durationSamples / sampleRate;\n const offset = offsetSamples / sampleRate;\n\n // Create a ToneAudioBuffer from the existing AudioBuffer\n const toneBuffer = new ToneAudioBuffer(audioBuffer);\n\n // Create player for this clip\n const player = new Player(toneBuffer);\n\n // Create fade gain for clip-level effects\n const fadeGain = new Gain(clipGain);\n\n // Connect player -> fadeGain -> trackVolume\n player.connect(fadeGain);\n fadeGain.connect(trackVolume);\n\n // Apply fades using gain automation\n // New simple API: fadeIn starts at clip start, fadeOut ends at clip end\n if (fadeIn) {\n const fadeInStart = startTime;\n const fadeInEnd = startTime + fadeIn.duration;\n const audioParam = getUnderlyingAudioParam(fadeGain.gain);\n if (audioParam) {\n // Set initial value to 0\n audioParam.setValueAtTime(0, fadeInStart);\n audioParam.linearRampToValueAtTime(clipGain, fadeInEnd);\n }\n }\n\n if (fadeOut) {\n const fadeOutStart = startTime + clipDuration - fadeOut.duration;\n const fadeOutEnd = startTime + clipDuration;\n const audioParam = getUnderlyingAudioParam(fadeGain.gain);\n if (audioParam) {\n audioParam.setValueAtTime(clipGain, fadeOutStart);\n audioParam.linearRampToValueAtTime(0, fadeOutEnd);\n }\n }\n\n // Schedule the player to start\n player.start(startTime, offset, clipDuration);\n }\n }\n\n // Start the transport\n transport.start(0);\n\n // Clean up effects if cleanup function was provided\n if (cleanup) {\n // Note: cleanup will be called after rendering completes\n }\n },\n duration,\n 2, // stereo\n sampleRate\n );\n } catch (err) {\n // Re-throw with a proper Error object if needed\n if (err instanceof Error) {\n throw err;\n } else {\n throw new Error(`Tone.Offline rendering failed: ${String(err)}`);\n }\n }\n\n onProgress(0.9);\n\n // Convert ToneAudioBuffer to standard AudioBuffer\n return buffer.get() as AudioBuffer;\n}\n\n/**\n * Convert linear gain to decibels\n */\nfunction gainToDb(gain: number): number {\n return 20 * Math.log10(Math.max(gain, 0.0001));\n}\n\n/**\n * Schedule a single clip in the offline context\n */\nasync function scheduleClip(\n ctx: OfflineAudioContext,\n clip: AudioClip,\n trackState: TrackState,\n sampleRate: number,\n applyEffects: boolean\n): Promise<void> {\n const {\n audioBuffer,\n startSample,\n durationSamples,\n offsetSamples,\n gain: clipGain,\n fadeIn,\n fadeOut,\n } = clip;\n\n // Skip clips without audioBuffer (peaks-only clips can't be exported)\n if (!audioBuffer) {\n console.warn(`Skipping clip \"${clip.name || clip.id}\" - no audioBuffer for export`);\n return;\n }\n\n // Convert samples to seconds for Web Audio API\n const startTime = startSample / sampleRate;\n const duration = durationSamples / sampleRate;\n const offset = offsetSamples / sampleRate;\n\n // Create buffer source\n const source = ctx.createBufferSource();\n source.buffer = audioBuffer;\n\n // Create gain node for clip + track volume\n const gainNode = ctx.createGain();\n const baseGain = clipGain * trackState.volume;\n\n // Create stereo panner for track pan\n const pannerNode = ctx.createStereoPanner();\n pannerNode.pan.value = trackState.pan;\n\n // Connect: source -> gain -> panner -> destination\n source.connect(gainNode);\n gainNode.connect(pannerNode);\n pannerNode.connect(ctx.destination);\n\n // Apply effects (fades) if enabled\n // New simple API: fadeIn starts at clip start, fadeOut ends at clip end\n if (applyEffects) {\n // Set initial gain (may be 0 if fade in exists)\n if (fadeIn) {\n gainNode.gain.setValueAtTime(0, startTime);\n } else {\n gainNode.gain.setValueAtTime(baseGain, startTime);\n }\n\n // Apply fade in\n if (fadeIn) {\n const fadeInStart = startTime;\n const fadeInEnd = startTime + fadeIn.duration;\n applyFadeEnvelope(\n gainNode.gain,\n fadeInStart,\n fadeInEnd,\n 0,\n baseGain,\n fadeIn.type || 'linear'\n );\n }\n\n // Apply fade out\n if (fadeOut) {\n const fadeOutStart = startTime + duration - fadeOut.duration;\n const fadeOutEnd = startTime + duration;\n // Ensure we're at baseGain before fade out starts\n if (!fadeIn || fadeIn.duration < duration - fadeOut.duration) {\n gainNode.gain.setValueAtTime(baseGain, fadeOutStart);\n }\n applyFadeEnvelope(\n gainNode.gain,\n fadeOutStart,\n fadeOutEnd,\n baseGain,\n 0,\n fadeOut.type || 'linear'\n );\n }\n } else {\n // No effects - just set constant gain\n gainNode.gain.setValueAtTime(baseGain, startTime);\n }\n\n // Schedule playback\n source.start(startTime, offset, duration);\n}\n\n/**\n * Apply a fade envelope to a gain parameter using Web Audio automation\n */\nfunction applyFadeEnvelope(\n gainParam: AudioParam,\n startTime: number,\n endTime: number,\n startValue: number,\n endValue: number,\n fadeType: FadeType\n): void {\n const duration = endTime - startTime;\n if (duration <= 0) return;\n\n switch (fadeType) {\n case 'linear':\n gainParam.setValueAtTime(startValue, startTime);\n gainParam.linearRampToValueAtTime(endValue, endTime);\n break;\n\n case 'exponential': {\n // Exponential can't handle 0 values, use small value instead\n const expStart = Math.max(startValue, 0.0001);\n const expEnd = Math.max(endValue, 0.0001);\n gainParam.setValueAtTime(expStart, startTime);\n gainParam.exponentialRampToValueAtTime(expEnd, endTime);\n // Set to actual 0 if needed\n if (endValue === 0) {\n gainParam.setValueAtTime(0, endTime);\n }\n break;\n }\n\n case 'logarithmic': {\n // Logarithmic fade - more aggressive at start, gentler at end\n // Implemented using setValueCurveAtTime with calculated curve\n const logCurve = generateFadeCurve(startValue, endValue, 256, 'logarithmic');\n gainParam.setValueCurveAtTime(logCurve, startTime, duration);\n break;\n }\n\n case 'sCurve': {\n // S-curve (ease-in-out) - smooth start and end\n const sCurve = generateFadeCurve(startValue, endValue, 256, 'sCurve');\n gainParam.setValueCurveAtTime(sCurve, startTime, duration);\n break;\n }\n\n default:\n // Default to linear\n gainParam.setValueAtTime(startValue, startTime);\n gainParam.linearRampToValueAtTime(endValue, endTime);\n }\n}\n\n/**\n * Generate a fade curve for setValueCurveAtTime\n */\nfunction generateFadeCurve(\n startValue: number,\n endValue: number,\n numPoints: number,\n curveType: 'logarithmic' | 'sCurve'\n): Float32Array {\n const curve = new Float32Array(numPoints);\n const range = endValue - startValue;\n\n for (let i = 0; i < numPoints; i++) {\n const t = i / (numPoints - 1); // 0 to 1\n\n let curveValue: number;\n if (curveType === 'logarithmic') {\n // Logarithmic: fast at start, slow at end (for fade out)\n // or slow at start, fast at end (for fade in)\n if (range > 0) {\n // Fade in: use log curve\n curveValue = Math.log10(1 + t * 9) / Math.log10(10);\n } else {\n // Fade out: use inverse log curve\n curveValue = 1 - Math.log10(1 + (1 - t) * 9) / Math.log10(10);\n }\n } else {\n // S-curve (smoothstep)\n curveValue = t * t * (3 - 2 * t);\n }\n\n curve[i] = startValue + range * curveValue;\n }\n\n return curve;\n}\n\n/**\n * Export types\n */\nexport type { WavEncoderOptions };\n","import { useCallback, useEffect, useRef } from 'react';\n\nexport interface AnimationFrameLoopControls {\n animationFrameRef: React.MutableRefObject<number | null>;\n startAnimationFrameLoop: (callback: () => void) => void;\n stopAnimationFrameLoop: () => void;\n}\n\n/**\n * Shared RAF loop controller used by playlist providers.\n * Always guarantees a single in-flight animation frame.\n */\nexport const useAnimationFrameLoop = (): AnimationFrameLoopControls => {\n const animationFrameRef = useRef<number | null>(null);\n\n const stopAnimationFrameLoop = useCallback(() => {\n if (animationFrameRef.current !== null) {\n cancelAnimationFrame(animationFrameRef.current);\n animationFrameRef.current = null;\n }\n }, []);\n\n const startAnimationFrameLoop = useCallback(\n (callback: () => void) => {\n stopAnimationFrameLoop();\n animationFrameRef.current = requestAnimationFrame(callback);\n },\n [stopAnimationFrameLoop]\n );\n\n useEffect(() => {\n return () => {\n stopAnimationFrameLoop();\n };\n }, [stopAnimationFrameLoop]);\n\n return {\n animationFrameRef,\n startAnimationFrameLoop,\n stopAnimationFrameLoop,\n };\n};\n","/**\n * Inline Web Worker for generating WaveformData binary format from AudioBuffer channels.\n *\n * Uses a Blob URL approach (portable across all bundlers) with the generateWaveformData\n * algorithm adapted from BBC's waveform-data.js (MIT licensed, adapted from Audacity).\n *\n * The worker generates peaks at a base scale (finest zoom level). The main thread\n * then uses WaveformData.resample() for near-instant zoom changes.\n */\n\nimport WaveformData from 'waveform-data';\n\n// ────────────────────────────────────────────────────────────────────────────\n// Worker code (runs inside the Blob URL worker)\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Self-contained worker source.\n * Contains the generateWaveformData function from waveform-data.js/src/waveform-generator.js\n * (MIT License, BBC). Pure JS, zero dependencies.\n */\nconst workerSource = `\n\"use strict\";\n\nvar INT8_MAX = 127;\nvar INT8_MIN = -128;\nvar INT16_MAX = 32767;\nvar INT16_MIN = -32768;\n\nfunction calculateWaveformDataLength(audio_sample_count, scale) {\n var data_length = Math.floor(audio_sample_count / scale);\n var samples_remaining = audio_sample_count - (data_length * scale);\n if (samples_remaining > 0) {\n data_length++;\n }\n return data_length;\n}\n\nfunction generateWaveformData(options) {\n var scale = options.scale;\n var amplitude_scale = options.amplitude_scale;\n var split_channels = options.split_channels;\n var length = options.length;\n var sample_rate = options.sample_rate;\n var channels = options.channels.map(function(channel) {\n return new Float32Array(channel);\n });\n var output_channels = split_channels ? channels.length : 1;\n var header_size = 24;\n var data_length = calculateWaveformDataLength(length, scale);\n var bytes_per_sample = options.bits === 8 ? 1 : 2;\n var total_size = header_size + data_length * 2 * bytes_per_sample * output_channels;\n var buffer = new ArrayBuffer(total_size);\n var data_view = new DataView(buffer);\n\n var scale_counter = 0;\n var offset = header_size;\n\n var min_value = new Array(output_channels);\n var max_value = new Array(output_channels);\n\n for (var channel = 0; channel < output_channels; channel++) {\n min_value[channel] = Infinity;\n max_value[channel] = -Infinity;\n }\n\n var range_min = options.bits === 8 ? INT8_MIN : INT16_MIN;\n var range_max = options.bits === 8 ? INT8_MAX : INT16_MAX;\n\n data_view.setInt32(0, 2, true);\n data_view.setUint32(4, options.bits === 8, true);\n data_view.setInt32(8, sample_rate, true);\n data_view.setInt32(12, scale, true);\n data_view.setInt32(16, data_length, true);\n data_view.setInt32(20, output_channels, true);\n\n for (var i = 0; i < length; i++) {\n var sample = 0;\n\n if (output_channels === 1) {\n for (var ch = 0; ch < channels.length; ++ch) {\n sample += channels[ch][i];\n }\n sample = Math.floor(range_max * sample * amplitude_scale / channels.length);\n\n if (sample < min_value[0]) {\n min_value[0] = sample;\n if (min_value[0] < range_min) {\n min_value[0] = range_min;\n }\n }\n if (sample > max_value[0]) {\n max_value[0] = sample;\n if (max_value[0] > range_max) {\n max_value[0] = range_max;\n }\n }\n }\n else {\n for (var ch2 = 0; ch2 < output_channels; ++ch2) {\n sample = Math.floor(range_max * channels[ch2][i] * amplitude_scale);\n\n if (sample < min_value[ch2]) {\n min_value[ch2] = sample;\n if (min_value[ch2] < range_min) {\n min_value[ch2] = range_min;\n }\n }\n if (sample > max_value[ch2]) {\n max_value[ch2] = sample;\n if (max_value[ch2] > range_max) {\n max_value[ch2] = range_max;\n }\n }\n }\n }\n\n if (++scale_counter === scale) {\n for (var ch3 = 0; ch3 < output_channels; ch3++) {\n if (options.bits === 8) {\n data_view.setInt8(offset++, min_value[ch3]);\n data_view.setInt8(offset++, max_value[ch3]);\n }\n else {\n data_view.setInt16(offset, min_value[ch3], true);\n data_view.setInt16(offset + 2, max_value[ch3], true);\n offset += 4;\n }\n min_value[ch3] = Infinity;\n max_value[ch3] = -Infinity;\n }\n scale_counter = 0;\n }\n }\n\n if (scale_counter > 0) {\n for (var ch4 = 0; ch4 < output_channels; ch4++) {\n if (options.bits === 8) {\n data_view.setInt8(offset++, min_value[ch4]);\n data_view.setInt8(offset++, max_value[ch4]);\n }\n else {\n data_view.setInt16(offset, min_value[ch4], true);\n data_view.setInt16(offset + 2, max_value[ch4], true);\n }\n }\n }\n\n return buffer;\n}\n\nself.onmessage = function(e) {\n var msg = e.data;\n try {\n var result = generateWaveformData({\n scale: msg.scale,\n bits: msg.bits,\n amplitude_scale: msg.amplitude_scale,\n split_channels: msg.split_channels,\n length: msg.length,\n sample_rate: msg.sample_rate,\n channels: msg.channels\n });\n self.postMessage({ id: msg.id, buffer: result }, [result]);\n } catch (err) {\n self.postMessage({ id: msg.id, error: err.message || String(err) });\n }\n};\n`;\n\n// ────────────────────────────────────────────────────────────────────────────\n// Promise-based worker API (runs on the main thread)\n// ────────────────────────────────────────────────────────────────────────────\n\nexport interface PeaksWorkerApi {\n generate(params: {\n id: string;\n channels: ArrayBuffer[];\n length: number;\n sampleRate: number;\n scale: number;\n bits: 8 | 16;\n splitChannels: boolean;\n }): Promise<WaveformData>;\n terminate(): void;\n}\n\ninterface PendingEntry {\n resolve: (value: WaveformData) => void;\n reject: (reason: unknown) => void;\n}\n\nlet idCounter = 0;\n\nexport function createPeaksWorker(): PeaksWorkerApi {\n let worker: Worker;\n try {\n const blob = new Blob([workerSource], { type: 'application/javascript' });\n const url = URL.createObjectURL(blob);\n worker = new Worker(url);\n URL.revokeObjectURL(url);\n } catch (err) {\n // Worker creation can fail in CSP-restricted environments that block blob: URLs.\n // Return a no-op API that rejects all generate calls gracefully.\n console.warn('[waveform-playlist] Failed to create peaks worker (CSP restriction?):', err);\n return {\n generate() {\n return Promise.reject(new Error('Worker creation failed'));\n },\n terminate() {\n /* no-op */\n },\n };\n }\n\n const pending = new Map<string, PendingEntry>();\n let terminated = false;\n\n worker.onmessage = (e: MessageEvent) => {\n const msg = e.data;\n const entry = pending.get(msg.id);\n if (!entry) return;\n pending.delete(msg.id);\n\n if (msg.error) {\n entry.reject(new Error(msg.error));\n } else {\n try {\n const waveformData = WaveformData.create(msg.buffer);\n entry.resolve(waveformData);\n } catch (err) {\n entry.reject(err);\n }\n }\n };\n\n worker.onerror = (e: ErrorEvent) => {\n terminated = true;\n worker.terminate();\n for (const [, entry] of pending) {\n entry.reject(e.error ?? new Error(e.message));\n }\n pending.clear();\n };\n\n return {\n generate(params) {\n if (terminated) return Promise.reject(new Error('Worker terminated'));\n const messageId = String(++idCounter);\n\n return new Promise<WaveformData>((resolve, reject) => {\n pending.set(messageId, { resolve, reject });\n\n worker.postMessage(\n {\n id: messageId,\n scale: params.scale,\n bits: params.bits,\n amplitude_scale: 1.0,\n split_channels: params.splitChannels,\n length: params.length,\n sample_rate: params.sampleRate,\n channels: params.channels,\n },\n params.channels // Transfer ownership\n );\n });\n },\n\n terminate() {\n terminated = true;\n worker.terminate();\n for (const [, entry] of pending) {\n entry.reject(new Error('Worker terminated'));\n }\n pending.clear();\n },\n };\n}\n","/**\n * useWaveformDataCache\n *\n * Manages a Map<clipId, WaveformData> that stores pre-computed WaveformData\n * objects generated by a web worker. These enable near-instant zoom changes\n * via WaveformData.resample() instead of re-scanning raw audio samples.\n *\n * Behavior:\n * - Creates the worker lazily on first clip needing generation\n * - Tracks submitted clip IDs via ref to avoid duplicate worker jobs\n * - Uses a cancelled flag to ignore responses after effect cleanup\n * - Terminates the worker on unmount\n */\n\nimport { useState, useEffect, useRef, useCallback } from 'react';\nimport type { ClipTrack } from '@waveform-playlist/core';\nimport type WaveformData from 'waveform-data';\nimport { createPeaksWorker, type PeaksWorkerApi } from '../workers/peaksWorker';\n\nexport interface UseWaveformDataCacheReturn {\n cache: ReadonlyMap<string, WaveformData>;\n isGenerating: boolean;\n}\n\nexport function useWaveformDataCache(\n tracks: ClipTrack[],\n baseScale: number\n): UseWaveformDataCacheReturn {\n const [cache, setCache] = useState<Map<string, WaveformData>>(() => new Map());\n const [isGenerating, setIsGenerating] = useState(false);\n\n const workerRef = useRef<PeaksWorkerApi | null>(null);\n const submittedRef = useRef<Set<string>>(new Set());\n const pendingCountRef = useRef(0);\n\n // Stable callback to get or create the worker\n const getWorker = useCallback(() => {\n if (!workerRef.current) {\n workerRef.current = createPeaksWorker();\n }\n return workerRef.current;\n }, []);\n\n useEffect(() => {\n let cancelled = false;\n const submitted = submittedRef.current;\n\n // Find clips that have audioBuffer but no waveformData and haven't been submitted\n const clipsToProcess: { clipId: string; audioBuffer: AudioBuffer }[] = [];\n\n for (const track of tracks) {\n for (const clip of track.clips) {\n if (clip.audioBuffer && !clip.waveformData && !submitted.has(clip.id)) {\n clipsToProcess.push({\n clipId: clip.id,\n audioBuffer: clip.audioBuffer,\n });\n }\n }\n }\n\n if (clipsToProcess.length === 0) return;\n\n // Mark all as submitted before starting async work\n const submittedThisRun = new Set<string>();\n for (const { clipId } of clipsToProcess) {\n submitted.add(clipId);\n submittedThisRun.add(clipId);\n }\n\n pendingCountRef.current += clipsToProcess.length;\n setIsGenerating(true);\n\n const worker = getWorker();\n\n for (const { clipId, audioBuffer } of clipsToProcess) {\n // .slice() channel buffers to avoid detaching the original AudioBuffer views\n const channels: ArrayBuffer[] = [];\n for (let c = 0; c < audioBuffer.numberOfChannels; c++) {\n channels.push(audioBuffer.getChannelData(c).slice().buffer);\n }\n\n worker\n .generate({\n id: clipId,\n channels,\n length: audioBuffer.length,\n sampleRate: audioBuffer.sampleRate,\n scale: baseScale,\n bits: 16,\n splitChannels: true,\n })\n .then((waveformData) => {\n if (cancelled) return;\n\n setCache((prev) => {\n const next = new Map(prev);\n next.set(clipId, waveformData);\n return next;\n });\n\n pendingCountRef.current--;\n if (pendingCountRef.current <= 0) {\n pendingCountRef.current = 0;\n setIsGenerating(false);\n }\n })\n .catch((err) => {\n if (cancelled) return;\n console.warn('[waveform-playlist] Worker peak generation failed:', err);\n // Remove from submitted so it can be retried on next effect run\n submitted.delete(clipId);\n pendingCountRef.current--;\n if (pendingCountRef.current <= 0) {\n pendingCountRef.current = 0;\n setIsGenerating(false);\n }\n });\n }\n\n return () => {\n cancelled = true;\n // Clear IDs submitted by this effect run so they can be resubmitted\n // if the effect re-runs with new deps (e.g., tracks changed)\n for (const clipId of submittedThisRun) {\n submitted.delete(clipId);\n }\n };\n // submittedRef guards against duplicate submissions — no need for cache in deps\n }, [tracks, baseScale, getWorker]);\n\n // Terminate worker on unmount\n useEffect(() => {\n return () => {\n workerRef.current?.terminate();\n workerRef.current = null;\n };\n }, []);\n\n return { cache, isGenerating };\n}\n","/**\n * useDynamicTracks — imperative hook for runtime track additions.\n *\n * Complements `useAudioTracks` (declarative, configs-driven) with an\n * imperative `addTracks()` API for dynamic loading (drag-and-drop, file pickers).\n *\n * Placeholder tracks appear instantly while audio decodes in parallel.\n */\n\nimport { useState, useCallback, useRef, useEffect } from 'react';\nimport { ClipTrack, createTrack, createClipFromSeconds } from '@waveform-playlist/core';\nimport { getGlobalAudioContext } from '@waveform-playlist/playout';\n\n/** A source that can be decoded into a track. */\nexport type TrackSource = File | Blob | string | { src: string; name?: string };\n\n/** Info about a track that failed to load. */\nexport interface TrackLoadError {\n /** Display name of the source that failed. */\n name: string;\n /** The underlying error. */\n error: Error;\n}\n\nexport interface UseDynamicTracksReturn {\n /**\n * Current tracks array (placeholders + loaded). Pass to WaveformPlaylistProvider.\n * Placeholder tracks have `clips: []` and names ending with \" (loading...)\".\n */\n tracks: ClipTrack[];\n /** Add one or more sources — creates placeholder tracks immediately. */\n addTracks: (sources: TrackSource[]) => void;\n /** Remove a track by its id. Aborts in-flight fetch/decode if still loading. */\n removeTrack: (trackId: string) => void;\n /** Number of sources currently decoding. */\n loadingCount: number;\n /** True when any source is still decoding. */\n isLoading: boolean;\n /** Tracks that failed to load (removed from `tracks` automatically). */\n errors: TrackLoadError[];\n}\n\n/** Extract a display name from a TrackSource. */\nfunction getSourceName(source: TrackSource): string {\n if (source instanceof File) {\n return source.name.replace(/\\.[^/.]+$/, '');\n }\n if (source instanceof Blob) {\n return 'Untitled';\n }\n if (typeof source === 'string') {\n return (\n source\n .split('/')\n .pop()\n ?.replace(/\\.[^/.]+$/, '') ?? 'Untitled'\n );\n }\n return (\n source.name ??\n source.src\n .split('/')\n .pop()\n ?.replace(/\\.[^/.]+$/, '') ??\n 'Untitled'\n );\n}\n\n/** Decode a TrackSource into an AudioBuffer + clean name. */\nasync function decodeSource(\n source: TrackSource,\n audioContext: AudioContext,\n signal?: AbortSignal\n): Promise<{ audioBuffer: AudioBuffer; name: string }> {\n const name = getSourceName(source);\n\n // File and Blob: read arrayBuffer directly (not abortable, but we check signal after)\n if (source instanceof Blob) {\n const arrayBuffer = await source.arrayBuffer();\n signal?.throwIfAborted();\n const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);\n return { audioBuffer, name };\n }\n\n const url = typeof source === 'string' ? source : source.src;\n const response = await fetch(url, { signal });\n if (!response.ok) throw new Error(`Failed to fetch ${url}: ${response.statusText}`);\n const arrayBuffer = await response.arrayBuffer();\n signal?.throwIfAborted();\n const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);\n return { audioBuffer, name };\n}\n\nexport function useDynamicTracks(): UseDynamicTracksReturn {\n const [tracks, setTracks] = useState<ClipTrack[]>([]);\n const [loadingCount, setLoadingCount] = useState(0);\n const [errors, setErrors] = useState<TrackLoadError[]>([]);\n const cancelledRef = useRef(false);\n /** Track IDs currently decoding — for accurate loadingCount on removal. */\n const loadingIdsRef = useRef(new Set<string>());\n /** Per-track AbortControllers for in-flight fetches — keyed by track ID. */\n const abortControllersRef = useRef(new Map<string, AbortController>());\n\n useEffect(() => {\n const controllers = abortControllersRef.current;\n return () => {\n cancelledRef.current = true;\n for (const controller of controllers.values()) {\n controller.abort();\n }\n controllers.clear();\n };\n }, []);\n\n const addTracks = useCallback((sources: TrackSource[]) => {\n if (sources.length === 0) return;\n\n const audioContext = getGlobalAudioContext();\n\n // 1. Create placeholder tracks immediately\n const placeholders = sources.map((source) => ({\n track: createTrack({ name: `${getSourceName(source)} (loading...)`, clips: [] }),\n source,\n }));\n\n setTracks((prev) => [...prev, ...placeholders.map((p) => p.track)]);\n setLoadingCount((prev) => prev + sources.length);\n\n // 2. Decode each source in parallel (fire-and-forget per source)\n for (const { track, source } of placeholders) {\n loadingIdsRef.current.add(track.id);\n const controller = new AbortController();\n abortControllersRef.current.set(track.id, controller);\n\n (async () => {\n try {\n const { audioBuffer, name } = await decodeSource(source, audioContext, controller.signal);\n const clip = createClipFromSeconds({\n audioBuffer,\n startTime: 0,\n duration: audioBuffer.duration,\n offset: 0,\n name,\n });\n\n // Guard: skip state update if hook unmounted or track was removed while decoding\n if (!cancelledRef.current && loadingIdsRef.current.has(track.id)) {\n setTracks((prev) =>\n prev.map((t) => (t.id === track.id ? { ...t, name, clips: [clip] } : t))\n );\n }\n } catch (error) {\n if (error instanceof DOMException && error.name === 'AbortError') return;\n console.warn('[waveform-playlist] Error loading audio:', error);\n // Guard: skip state update if hook unmounted or track was removed while decoding\n if (!cancelledRef.current && loadingIdsRef.current.has(track.id)) {\n setTracks((prev) => prev.filter((t) => t.id !== track.id));\n setErrors((prev) => [\n ...prev,\n {\n name: getSourceName(source),\n error: error instanceof Error ? error : new Error(String(error)),\n },\n ]);\n }\n } finally {\n abortControllersRef.current.delete(track.id);\n if (!cancelledRef.current && loadingIdsRef.current.delete(track.id)) {\n setLoadingCount((prev) => prev - 1);\n }\n }\n })();\n }\n }, []);\n\n const removeTrack = useCallback((trackId: string) => {\n setTracks((prev) => prev.filter((t) => t.id !== trackId));\n // Abort in-flight fetch/decode and update loading state\n const controller = abortControllersRef.current.get(trackId);\n if (controller) {\n controller.abort();\n abortControllersRef.current.delete(trackId);\n }\n if (loadingIdsRef.current.delete(trackId)) {\n setLoadingCount((prev) => prev - 1);\n }\n }, []);\n\n return {\n tracks,\n addTracks,\n removeTrack,\n loadingCount,\n isLoading: loadingCount > 0,\n errors,\n };\n}\n","import React, {\n createContext,\n useContext,\n useState,\n useEffect,\n useRef,\n useCallback,\n useMemo,\n type ReactNode,\n} from 'react';\nimport { ThemeProvider } from 'styled-components';\nimport {\n TonePlayout,\n type EffectsFunction,\n type TrackEffectsFunction,\n} from '@waveform-playlist/playout';\nimport {\n type Track,\n type ClipTrack,\n type Fade,\n type AnnotationAction,\n} from '@waveform-playlist/core';\nimport {\n type TimeFormat,\n type WaveformPlaylistTheme,\n defaultTheme,\n} from '@waveform-playlist/ui-components';\nimport { getContext } from 'tone';\nimport { extractPeaksFromWaveformDataFull } from './waveformDataLoader';\nimport type WaveformData from 'waveform-data';\nimport type { PeakData } from '@waveform-playlist/core';\nimport type { AnnotationData } from '@waveform-playlist/core';\nimport {\n useTimeFormat,\n useZoomControls,\n useMasterVolume,\n useAnimationFrameLoop,\n useWaveformDataCache,\n} from './hooks';\n\n// Types\nexport interface ClipPeaks {\n clipId: string;\n trackName: string;\n peaks: PeakData;\n startSample: number;\n durationSamples: number;\n fadeIn?: Fade;\n fadeOut?: Fade;\n}\n\nexport type TrackClipPeaks = ClipPeaks[];\n\n// Legacy WaveformTrack type - kept for reference but deprecated\n// @deprecated Use ClipTrack from @waveform-playlist/core instead\nexport interface WaveformTrack {\n src: string | AudioBuffer;\n name?: string;\n effects?: TrackEffectsFunction;\n}\n\nexport interface TrackState {\n name: string;\n muted: boolean;\n soloed: boolean;\n volume: number;\n pan: number;\n}\n\n// Split contexts for performance optimization\n// Animation context contains playback state and timing refs — no per-frame state updates\n\nexport interface PlaybackAnimationContextValue {\n isPlaying: boolean;\n currentTime: number;\n currentTimeRef: React.RefObject<number>;\n // Refs for direct time calculation in animated components (avoids timing drift)\n playbackStartTimeRef: React.RefObject<number>; // context.currentTime when playback started\n audioStartPositionRef: React.RefObject<number>; // Audio position when playback started\n}\n\nexport interface PlaylistStateContextValue {\n continuousPlay: boolean;\n linkEndpoints: boolean;\n annotationsEditable: boolean;\n isAutomaticScroll: boolean;\n isLoopEnabled: boolean;\n annotations: AnnotationData[];\n activeAnnotationId: string | null;\n selectionStart: number;\n selectionEnd: number;\n selectedTrackId: string | null; // ID of currently selected track for editing operations\n // Loop region (separate from selection) - Audacity-style loop points\n loopStart: number;\n loopEnd: number;\n}\n\nexport interface PlaylistControlsContextValue {\n // Playback controls\n play: (startTime?: number, playDuration?: number) => Promise<void>;\n pause: () => void;\n stop: () => void;\n seekTo: (time: number) => void;\n setCurrentTime: (time: number) => void;\n\n // Track controls\n setTrackMute: (trackIndex: number, muted: boolean) => void;\n setTrackSolo: (trackIndex: number, soloed: boolean) => void;\n setTrackVolume: (trackIndex: number, volume: number) => void;\n setTrackPan: (trackIndex: number, pan: number) => void;\n\n // Selection\n setSelection: (start: number, end: number) => void;\n setSelectedTrackId: (trackId: string | null) => void;\n\n // Time format\n setTimeFormat: (format: TimeFormat) => void;\n formatTime: (seconds: number) => string;\n\n // Zoom\n zoomIn: () => void;\n zoomOut: () => void;\n\n // Master volume\n setMasterVolume: (volume: number) => void;\n\n // Automatic scroll\n setAutomaticScroll: (enabled: boolean) => void;\n setScrollContainer: (element: HTMLDivElement | null) => void;\n scrollContainerRef: React.RefObject<HTMLDivElement | null>;\n\n // Annotation controls\n setContinuousPlay: (enabled: boolean) => void;\n setLinkEndpoints: (enabled: boolean) => void;\n setAnnotationsEditable: (enabled: boolean) => void;\n setAnnotations: React.Dispatch<React.SetStateAction<AnnotationData[]>>;\n setActiveAnnotationId: (id: string | null) => void;\n\n // Loop controls\n setLoopEnabled: (enabled: boolean) => void;\n setLoopRegion: (start: number, end: number) => void;\n setLoopRegionFromSelection: () => void;\n clearLoopRegion: () => void;\n}\n\nexport interface PlaylistDataContextValue {\n duration: number;\n audioBuffers: AudioBuffer[];\n peaksDataArray: TrackClipPeaks[]; // Array of tracks, each containing array of clip peaks\n trackStates: TrackState[];\n tracks: ClipTrack[]; // Original tracks array with IDs\n sampleRate: number;\n waveHeight: number;\n timeScaleHeight: number;\n minimumPlaylistHeight: number;\n controls: { show: boolean; width: number };\n playoutRef: React.RefObject<TonePlayout | null>;\n samplesPerPixel: number;\n timeFormat: TimeFormat;\n masterVolume: number;\n canZoomIn: boolean;\n canZoomOut: boolean;\n barWidth: number;\n barGap: number;\n /** Width in pixels of progress bars. Defaults to barWidth + barGap (fills gaps). */\n progressBarWidth: number;\n /** Whether the playlist has finished loading all tracks */\n isReady: boolean;\n /** Whether tracks are rendered in mono mode */\n mono: boolean;\n}\n\n// Create the 4 separate contexts\nconst PlaybackAnimationContext = createContext<PlaybackAnimationContextValue | null>(null);\nconst PlaylistStateContext = createContext<PlaylistStateContextValue | null>(null);\nconst PlaylistControlsContext = createContext<PlaylistControlsContextValue | null>(null);\nconst PlaylistDataContext = createContext<PlaylistDataContextValue | null>(null);\n\nexport interface WaveformPlaylistProviderProps {\n tracks: ClipTrack[]; // Updated to use clip-based model\n timescale?: boolean;\n mono?: boolean;\n waveHeight?: number;\n samplesPerPixel?: number;\n zoomLevels?: number[]; // Array of zoom levels in samples per pixel (lower = more zoomed in)\n automaticScroll?: boolean;\n theme?: Partial<WaveformPlaylistTheme>;\n controls?: {\n show: boolean;\n width: number;\n };\n annotationList?: {\n annotations?: AnnotationData[];\n editable?: boolean;\n isContinuousPlay?: boolean;\n linkEndpoints?: boolean;\n controls?: AnnotationAction[];\n };\n effects?: EffectsFunction;\n onReady?: () => void;\n /** @deprecated Use onAnnotationsChange instead */\n onAnnotationUpdate?: (annotations: AnnotationData[]) => void;\n /** Callback when annotations are changed (drag, edit, etc.) */\n onAnnotationsChange?: (annotations: AnnotationData[]) => void;\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). */\n progressBarWidth?: number;\n children: ReactNode;\n}\n\nexport const WaveformPlaylistProvider: React.FC<WaveformPlaylistProviderProps> = ({\n tracks,\n timescale = false,\n mono = false,\n waveHeight = 80,\n samplesPerPixel: initialSamplesPerPixel = 1024,\n zoomLevels,\n automaticScroll = false,\n theme: userTheme,\n controls = { show: false, width: 0 },\n annotationList,\n effects,\n onReady,\n onAnnotationUpdate: _onAnnotationUpdate,\n onAnnotationsChange,\n barWidth = 1,\n barGap = 0,\n progressBarWidth: progressBarWidthProp,\n children,\n}) => {\n // Default progressBarWidth to barWidth + barGap (fills gaps)\n const progressBarWidth = progressBarWidthProp ?? barWidth + barGap;\n // Annotations are derived from prop (single source of truth in parent)\n // In v6, annotations must be pre-parsed (numeric start/end). Use parseAeneas() from @waveform-playlist/annotations before passing.\n const annotations = useMemo(() => {\n if (!annotationList?.annotations) return [];\n if (process.env.NODE_ENV !== 'production' && annotationList.annotations.length > 0) {\n const first = annotationList.annotations[0] as unknown as Record<string, unknown>;\n if (typeof first.start !== 'number' || typeof first.end !== 'number') {\n console.error(\n '[waveform-playlist] Annotations must have numeric start/end values. ' +\n 'In v6, use parseAeneas() from @waveform-playlist/annotations before passing annotations. ' +\n 'Received start type: ' +\n typeof first.start\n );\n return [];\n }\n }\n return annotationList.annotations;\n }, [annotationList?.annotations]);\n\n // Ref for animation loop (avoids restarting loop on annotation change)\n const annotationsRef = useRef<AnnotationData[]>(annotations);\n annotationsRef.current = annotations;\n\n // State\n const [activeAnnotationId, setActiveAnnotationIdState] = useState<string | null>(null);\n const [isPlaying, setIsPlaying] = useState(false);\n const [currentTime, setCurrentTime] = useState(0);\n const [duration, setDuration] = useState(0);\n const [audioBuffers, setAudioBuffers] = useState<AudioBuffer[]>([]);\n const [peaksDataArray, setPeaksDataArray] = useState<TrackClipPeaks[]>([]); // Updated for clip-based peaks\n const [trackStates, setTrackStates] = useState<TrackState[]>([]);\n const [selectionStart, setSelectionStart] = useState(0);\n const [selectionEnd, setSelectionEnd] = useState(0);\n const [selectedTrackId, setSelectedTrackId] = useState<string | null>(null);\n const [isAutomaticScroll, setIsAutomaticScroll] = useState(automaticScroll);\n const [continuousPlay, setContinuousPlayState] = useState(\n annotationList?.isContinuousPlay ?? false\n );\n const [linkEndpoints, setLinkEndpoints] = useState(annotationList?.linkEndpoints ?? false);\n const [annotationsEditable, setAnnotationsEditable] = useState(annotationList?.editable ?? false);\n const [isLoopEnabled, setIsLoopEnabledState] = useState(false);\n const [loopStart, setLoopStartState] = useState(0);\n const [loopEnd, setLoopEndState] = useState(0);\n const [isReady, setIsReady] = useState(false);\n\n // Refs\n const playoutRef = useRef<TonePlayout | null>(null);\n const playStartPositionRef = useRef<number>(0);\n const currentTimeRef = useRef<number>(0);\n const tracksRef = useRef<ClipTrack[]>(tracks);\n const trackStatesRef = useRef<TrackState[]>(trackStates);\n const playbackStartTimeRef = useRef<number>(0); // context.currentTime when playback started\n const audioStartPositionRef = useRef<number>(0); // Audio position when playback started\n const playbackEndTimeRef = useRef<number | null>(null); // Audio position where playback should stop (for selections)\n const scrollContainerRef = useRef<HTMLDivElement | null>(null);\n const isAutomaticScrollRef = useRef<boolean>(false);\n const continuousPlayRef = useRef<boolean>(annotationList?.isContinuousPlay ?? false);\n const activeAnnotationIdRef = useRef<string | null>(null);\n const samplesPerPixelRef = useRef<number>(initialSamplesPerPixel);\n const isLoopEnabledRef = useRef<boolean>(false);\n const selectionStartRef = useRef<number>(0);\n const selectionEndRef = useRef<number>(0);\n const loopStartRef = useRef<number>(0);\n const loopEndRef = useRef<number>(0);\n\n // Custom hooks\n const { timeFormat, setTimeFormat, formatTime } = useTimeFormat();\n const zoom = useZoomControls({ initialSamplesPerPixel, zoomLevels });\n const samplesPerPixel = zoom.samplesPerPixel;\n const { masterVolume, setMasterVolume } = useMasterVolume({ playoutRef, initialVolume: 1.0 });\n const { animationFrameRef, startAnimationFrameLoop, stopAnimationFrameLoop } =\n useAnimationFrameLoop();\n\n // Worker-based WaveformData cache for fast zoom resampling\n const baseScale = useMemo(\n () => Math.min(...(zoomLevels ?? [256, 512, 1024, 2048, 4096, 8192])),\n [zoomLevels]\n );\n const { cache: waveformDataCache } = useWaveformDataCache(tracks, baseScale);\n\n // Custom setter for continuousPlay that updates BOTH state and ref synchronously\n // This ensures the ref is updated immediately, before the animation loop can read it\n const setContinuousPlay = useCallback((value: boolean) => {\n continuousPlayRef.current = value; // Update ref synchronously\n setContinuousPlayState(value); // Update state (triggers re-render)\n }, []);\n\n // Custom setter for activeAnnotationId that updates BOTH state and ref synchronously\n const setActiveAnnotationId = useCallback((value: string | null) => {\n activeAnnotationIdRef.current = value; // Update ref synchronously\n setActiveAnnotationIdState(value); // Update state (triggers re-render)\n }, []);\n\n // Custom setter for isLoopEnabled that updates BOTH state and ref synchronously\n const setLoopEnabled = useCallback((value: boolean) => {\n isLoopEnabledRef.current = value; // Update ref synchronously\n setIsLoopEnabledState(value); // Update state (triggers re-render)\n }, []);\n\n // Loop region setters - Audacity-style separate loop points\n const setLoopRegion = useCallback((start: number, end: number) => {\n loopStartRef.current = start;\n loopEndRef.current = end;\n setLoopStartState(start);\n setLoopEndState(end);\n }, []);\n\n const setLoopRegionFromSelection = useCallback(() => {\n const start = selectionStartRef.current;\n const end = selectionEndRef.current;\n if (start !== end && end > start) {\n setLoopRegion(start, end);\n }\n }, [setLoopRegion]);\n\n const clearLoopRegion = useCallback(() => {\n setLoopRegion(0, 0);\n }, [setLoopRegion]);\n\n // Keep refs in sync with state\n useEffect(() => {\n isAutomaticScrollRef.current = isAutomaticScroll;\n }, [isAutomaticScroll]);\n\n useEffect(() => {\n trackStatesRef.current = trackStates;\n }, [trackStates]);\n\n tracksRef.current = tracks;\n\n // Keep selection refs in sync for animation loop access\n useEffect(() => {\n selectionStartRef.current = selectionStart;\n selectionEndRef.current = selectionEnd;\n }, [selectionStart, selectionEnd]);\n\n // Adjust scroll position proportionally when zoom changes\n useEffect(() => {\n if (!scrollContainerRef.current || !audioBuffers.length) return;\n\n const container = scrollContainerRef.current;\n const oldSamplesPerPixel = samplesPerPixelRef.current;\n const newSamplesPerPixel = samplesPerPixel;\n\n if (oldSamplesPerPixel === newSamplesPerPixel) return;\n\n // Calculate the current center time in the viewport\n const controlWidth = controls.show ? controls.width : 0;\n const containerWidth = container.clientWidth;\n const currentScrollLeft = container.scrollLeft;\n const centerPixel = currentScrollLeft + containerWidth / 2 - controlWidth;\n const sr = audioBuffers[0].sampleRate;\n const centerTime = (centerPixel * oldSamplesPerPixel) / sr;\n\n // Calculate new scroll position to keep the same center time\n const newCenterPixel = (centerTime * sr) / newSamplesPerPixel;\n const newScrollLeft = Math.max(0, newCenterPixel + controlWidth - containerWidth / 2);\n\n container.scrollLeft = newScrollLeft;\n samplesPerPixelRef.current = newSamplesPerPixel;\n }, [samplesPerPixel, audioBuffers, controls]);\n\n // Track pending playback resume after tracks change\n const pendingResumeRef = useRef<{ position: number } | null>(null);\n\n // Load audio from clips (only when tracks change)\n useEffect(() => {\n // Reset ready state when tracks change\n setIsReady(false);\n\n if (tracks.length === 0) {\n // Clear state when all tracks are removed\n setAudioBuffers([]);\n setDuration(0);\n setTrackStates([]);\n setPeaksDataArray([]);\n if (playoutRef.current) {\n playoutRef.current.dispose();\n playoutRef.current = null;\n }\n return;\n }\n\n // Capture playback state before rebuilding playout\n const wasPlaying = isPlaying;\n const resumePosition = currentTimeRef.current;\n\n // Stop current playback and animation before disposing\n if (playoutRef.current && wasPlaying) {\n playoutRef.current.stop();\n stopAnimationFrameLoop();\n // Mark that we need to resume playback after playout is rebuilt\n pendingResumeRef.current = { position: resumePosition };\n }\n\n const loadAudio = async () => {\n try {\n // Extract all audio buffers from clips (only those that have audioBuffer)\n // For now, collect the first clip's buffer from each track\n const buffers: AudioBuffer[] = [];\n\n tracks.forEach((track) => {\n if (track.clips.length > 0 && track.clips[0].audioBuffer) {\n // Use first clip's buffer for now (full multi-clip support comes in next phase)\n buffers.push(track.clips[0].audioBuffer);\n }\n });\n\n // Calculate total timeline duration from all clips across all tracks\n // Use clip.sampleRate which is always defined (works for peaks-only clips too)\n let maxDuration = 0;\n tracks.forEach((track) => {\n track.clips.forEach((clip) => {\n const sampleRate = clip.sampleRate;\n const clipEndSample = clip.startSample + clip.durationSamples;\n const clipEnd = clipEndSample / sampleRate;\n maxDuration = Math.max(maxDuration, clipEnd);\n });\n });\n\n setAudioBuffers(buffers);\n setDuration(maxDuration);\n\n // Initialize or update track states, preserving existing UI state (mute/solo/volume/pan)\n // Only initialize from ClipTrack props when trackStates is empty or track count changes\n setTrackStates((prevStates) => {\n if (prevStates.length === tracks.length) {\n // Same number of tracks - preserve existing UI state, just update names\n return prevStates.map((state, i) => ({\n ...state,\n name: tracks[i].name,\n }));\n }\n // Track count changed - reinitialize from ClipTrack properties\n return tracks.map((track) => ({\n name: track.name,\n muted: track.muted,\n soloed: track.soloed,\n volume: track.volume,\n pan: track.pan,\n }));\n });\n\n // Dispose old playout before creating new one\n if (playoutRef.current) {\n playoutRef.current.dispose();\n }\n\n // Create playout with clips\n const playout = new TonePlayout({\n effects,\n });\n\n // For each track, create a ToneTrack with all clips\n // Use trackStatesRef for current UI state (mute/solo/volume/pan) instead of track props\n const currentTrackStates = trackStatesRef.current;\n tracks.forEach((track, index) => {\n // Filter to only clips with audioBuffer (peaks-only clips can't be played)\n const playableClips = track.clips.filter((clip) => clip.audioBuffer);\n\n if (playableClips.length > 0) {\n // Calculate track start and end times from clips (converting samples to seconds)\n // Use clip.sampleRate which is always defined\n const sampleRate = playableClips[0].sampleRate;\n const startTime = Math.min(...playableClips.map((c) => c.startSample / sampleRate));\n const endTime = Math.max(\n ...playableClips.map((c) => (c.startSample + c.durationSamples) / sampleRate)\n );\n\n // Use current UI state if available, otherwise fall back to track props\n const trackState = currentTrackStates[index];\n const trackObj: Track = {\n id: track.id,\n name: track.name,\n gain: trackState?.volume ?? track.volume,\n muted: trackState?.muted ?? track.muted,\n soloed: trackState?.soloed ?? track.soloed,\n stereoPan: trackState?.pan ?? track.pan,\n startTime,\n endTime,\n };\n\n // Convert ClipTrack clips to ToneTrack ClipInfo format\n // Note: ClipInfo.startTime is relative to track start, not absolute timeline\n const clipInfos = playableClips.map((clip) => {\n const clipSampleRate = clip.sampleRate;\n return {\n buffer: clip.audioBuffer!, // We filtered for audioBuffer above\n startTime: clip.startSample / clipSampleRate - startTime, // Make relative to track start\n duration: clip.durationSamples / clipSampleRate,\n offset: clip.offsetSamples / clipSampleRate,\n fadeIn: clip.fadeIn,\n fadeOut: clip.fadeOut,\n gain: clip.gain,\n };\n });\n\n playout.addTrack({\n clips: clipInfos,\n track: trackObj,\n effects: track.effects, // Pass track effects\n });\n }\n });\n\n // Apply solo muting after all tracks are added\n playout.applyInitialSoloState();\n\n playoutRef.current = playout;\n setIsReady(true);\n\n // Dispatch custom event for external listeners\n const event = new CustomEvent('waveform-playlist:ready', {\n detail: {\n trackCount: tracks.length,\n duration: maxDuration,\n },\n });\n window.dispatchEvent(event);\n\n onReady?.();\n } catch (error) {\n console.error('Error loading audio:', error);\n }\n };\n\n loadAudio();\n\n return () => {\n stopAnimationFrameLoop();\n if (playoutRef.current) {\n playoutRef.current.dispose();\n }\n };\n }, [tracks, onReady, isPlaying, effects, stopAnimationFrameLoop]);\n\n // Regenerate peaks when zoom, mono, or waveformDataCache changes (without reloading audio)\n // Peak sources in priority order:\n // A) clip.waveformData — external pre-computed peaks (e.g. from BBC audiowaveform)\n // B) waveformDataCache — worker-generated WaveformData (fast resample on zoom)\n // C) empty peaks — clip is still loading or has no audio data\n useEffect(() => {\n if (tracks.length === 0) return;\n\n const allTrackPeaks: TrackClipPeaks[] = tracks.map((track) => {\n const clipPeaks: ClipPeaks[] = track.clips.map((clip) => {\n let peaks: PeakData | undefined;\n\n // Path A: External pre-computed waveform data (e.g. from audiowaveform .dat file)\n if (clip.waveformData) {\n try {\n peaks = extractPeaksFromWaveformDataFull(\n clip.waveformData as WaveformData,\n samplesPerPixel,\n mono,\n clip.offsetSamples,\n clip.durationSamples\n );\n } catch (err) {\n console.warn('[waveform-playlist] Failed to extract peaks from waveformData:', err);\n }\n }\n\n // Path B: Worker-generated WaveformData cache (fast resample on zoom)\n if (!peaks) {\n const cached = waveformDataCache.get(clip.id);\n if (cached) {\n try {\n peaks = extractPeaksFromWaveformDataFull(\n cached,\n samplesPerPixel,\n mono,\n clip.offsetSamples,\n clip.durationSamples\n );\n } catch (err) {\n console.warn('[waveform-playlist] Failed to extract peaks from cache:', err);\n }\n }\n }\n\n // Path C: No peaks data available yet — render empty while worker processes.\n // Use correct channel count from audioBuffer to prevent track height shift\n // when peaks arrive (mono mode collapses to 1 channel).\n if (!peaks) {\n if (!clip.audioBuffer && !clip.waveformData) {\n console.warn(\n `[waveform-playlist] Clip \"${clip.id}\" has no audio data or waveform data`\n );\n }\n const numChannels = mono ? 1 : (clip.audioBuffer?.numberOfChannels ?? 1);\n peaks = {\n length: 0,\n data: Array.from({ length: numChannels }, () => new Int16Array(0)),\n bits: 16,\n };\n }\n\n return {\n clipId: clip.id,\n trackName: track.name,\n peaks,\n startSample: clip.startSample,\n durationSamples: clip.durationSamples,\n fadeIn: clip.fadeIn,\n fadeOut: clip.fadeOut,\n };\n });\n\n return clipPeaks;\n });\n\n setPeaksDataArray(allTrackPeaks);\n }, [tracks, samplesPerPixel, mono, waveformDataCache]);\n\n // Animation loop\n const startAnimationLoop = useCallback(() => {\n const updateTime = () => {\n // Calculate current position based on context.currentTime timing\n const elapsed = getContext().currentTime - playbackStartTimeRef.current;\n const time = audioStartPositionRef.current + elapsed;\n currentTimeRef.current = time;\n\n // Handle annotation playback based on continuous play mode\n const currentAnnotations = annotationsRef.current;\n if (currentAnnotations.length > 0) {\n const currentAnnotation = currentAnnotations.find(\n (ann) => time >= ann.start && time < ann.end\n );\n\n if (continuousPlayRef.current) {\n // Continuous play ON: update active annotation, let audio play to the end\n if (currentAnnotation && currentAnnotation.id !== activeAnnotationIdRef.current) {\n setActiveAnnotationId(currentAnnotation.id);\n } else if (!currentAnnotation && activeAnnotationIdRef.current !== null) {\n // Clear the active annotation when we're past it, but don't stop playback\n // Let playback continue until the audio actually ends (handled by duration check)\n setActiveAnnotationId(null);\n }\n } else {\n // Continuous play OFF: stop at end of current annotation\n if (activeAnnotationIdRef.current) {\n const activeAnnotation = currentAnnotations.find(\n (ann) => ann.id === activeAnnotationIdRef.current\n );\n if (activeAnnotation && time >= activeAnnotation.end) {\n // Stop playback at end of current annotation\n if (playoutRef.current) {\n playoutRef.current.stop();\n }\n setIsPlaying(false);\n currentTimeRef.current = playStartPositionRef.current;\n setCurrentTime(playStartPositionRef.current);\n return;\n }\n } else {\n // If no active annotation ID is set, use the current annotation\n if (currentAnnotation) {\n setActiveAnnotationId(currentAnnotation.id);\n }\n }\n }\n }\n\n // Handle automatic scroll - continuously center the playhead\n if (isAutomaticScrollRef.current && scrollContainerRef.current && audioBuffers.length > 0) {\n const container = scrollContainerRef.current;\n const sr = audioBuffers[0].sampleRate;\n const pixelPosition = (time * sr) / samplesPerPixelRef.current;\n const containerWidth = container.clientWidth;\n\n // Calculate visual position of playhead (includes controls offset)\n const controlWidth = controls.show ? controls.width : 0;\n const visualPosition = pixelPosition + controlWidth;\n\n // Continuously scroll to keep playhead centered\n const targetScrollLeft = Math.max(0, visualPosition - containerWidth / 2);\n container.scrollLeft = targetScrollLeft;\n }\n\n // Check if we've reached the playback end time (for selection playback)\n if (playbackEndTimeRef.current !== null && time >= playbackEndTimeRef.current) {\n // Stop playback at selection end (selection playback is separate from looping)\n if (playoutRef.current) {\n playoutRef.current.stop();\n }\n setIsPlaying(false);\n currentTimeRef.current = playbackEndTimeRef.current;\n setCurrentTime(playbackEndTimeRef.current);\n playbackEndTimeRef.current = null; // Clear the end time\n return;\n }\n\n // Audacity-style loop region: loop when cursor enters and reaches end of loop region\n const hasValidLoopRegion =\n loopStartRef.current !== loopEndRef.current && loopEndRef.current > loopStartRef.current;\n\n if (isLoopEnabledRef.current && hasValidLoopRegion) {\n // Check if we've reached or passed the loop end point\n if (time >= loopEndRef.current) {\n // Loop: restart from loop start\n playoutRef.current?.stop();\n\n const context = getContext();\n const timeNow = context.currentTime;\n playbackStartTimeRef.current = timeNow;\n audioStartPositionRef.current = loopStartRef.current;\n currentTimeRef.current = loopStartRef.current;\n\n // Restart playback from loop start (no duration limit - will loop again when reaching loop end)\n playoutRef.current?.play(timeNow, loopStartRef.current);\n\n // Continue animation loop\n startAnimationFrameLoop(updateTime);\n return;\n }\n }\n\n if (time >= duration) {\n // Stop playback - inline to avoid circular dependency\n if (playoutRef.current) {\n playoutRef.current.stop();\n }\n setIsPlaying(false);\n currentTimeRef.current = playStartPositionRef.current;\n setCurrentTime(playStartPositionRef.current);\n setActiveAnnotationId(null);\n return;\n }\n startAnimationFrameLoop(updateTime);\n };\n startAnimationFrameLoop(updateTime);\n }, [\n duration,\n audioBuffers,\n controls.show,\n controls.width,\n setActiveAnnotationId,\n startAnimationFrameLoop,\n ]);\n\n const stopAnimationLoop = stopAnimationFrameLoop;\n\n // Restart animation loop and reschedule playout when continuousPlay changes during playback\n // This ensures the loop always has the current continuousPlay value\n // and removes duration limits when switching to continuous play\n useEffect(() => {\n const reschedulePlayback = async () => {\n if (isPlaying && animationFrameRef.current && playoutRef.current) {\n // When toggling continuous play ON, reschedule playout without duration limit\n // so audio continues past the current annotation boundary\n if (continuousPlay) {\n const currentPos = currentTimeRef.current;\n\n // Stop current playout (which may have duration limit + pause callback)\n playoutRef.current.stop();\n stopAnimationLoop();\n\n // Initialize and restart from current position without duration limit\n await playoutRef.current.init();\n\n // Clear any existing playback complete callback\n playoutRef.current.setOnPlaybackComplete(() => {});\n\n const context = getContext();\n const timeNow = context.currentTime;\n playbackStartTimeRef.current = timeNow;\n audioStartPositionRef.current = currentPos;\n\n // Play without duration - will play to end of track\n playoutRef.current.play(timeNow, currentPos);\n startAnimationLoop();\n } else {\n // Just restart animation loop for continuous play OFF\n stopAnimationLoop();\n startAnimationLoop();\n }\n }\n };\n\n reschedulePlayback();\n }, [continuousPlay, isPlaying, startAnimationLoop, stopAnimationLoop, animationFrameRef]);\n\n // Resume playback after tracks change (e.g., after splitting a clip during playback)\n useEffect(() => {\n const resumePlayback = async () => {\n if (pendingResumeRef.current && playoutRef.current) {\n const { position } = pendingResumeRef.current;\n pendingResumeRef.current = null;\n\n await playoutRef.current.init();\n playoutRef.current.setOnPlaybackComplete(() => {});\n\n const context = getContext();\n const timeNow = context.currentTime;\n playbackStartTimeRef.current = timeNow;\n audioStartPositionRef.current = position;\n\n playoutRef.current.play(timeNow, position);\n setIsPlaying(true);\n startAnimationLoop();\n }\n };\n\n resumePlayback();\n }, [tracks, startAnimationLoop]);\n\n // Playback controls\n const play = useCallback(\n async (startTime?: number, playDuration?: number) => {\n if (!playoutRef.current || audioBuffers.length === 0) return;\n\n await playoutRef.current.init();\n\n const actualStartTime = startTime ?? currentTimeRef.current;\n playStartPositionRef.current = actualStartTime;\n\n // Update currentTimeRef to match the actual start position\n // This ensures the animation loop starts from the correct position\n currentTimeRef.current = actualStartTime;\n\n // Clear any existing playback complete callback before stopping\n // Otherwise stopping will trigger the old callback and interfere with new playback\n playoutRef.current.setOnPlaybackComplete(() => {});\n\n // Stop any existing playback and animation loop before starting\n playoutRef.current.stop();\n stopAnimationLoop();\n\n // Record timing for accurate position tracking using Tone.js context\n const context = getContext();\n // Tone.js context wraps Web Audio - need to use .currentTime from wrapped context\n const startTimeNow = context.currentTime;\n playbackStartTimeRef.current = startTimeNow;\n audioStartPositionRef.current = actualStartTime;\n\n // Set playback end time if playing with duration (e.g., selection playback)\n playbackEndTimeRef.current =\n playDuration !== undefined ? actualStartTime + playDuration : null;\n\n // Don't set up playback complete callback for annotations\n // The animation loop handles stopping at annotation boundaries\n // This avoids callback timing issues when switching between annotations\n\n playoutRef.current.play(startTimeNow, actualStartTime, playDuration);\n setIsPlaying(true);\n startAnimationLoop();\n },\n [audioBuffers.length, startAnimationLoop, stopAnimationLoop]\n );\n\n const pause = useCallback(() => {\n if (!playoutRef.current) return;\n\n // Calculate exact pause position using context.currentTime timing\n const elapsed = getContext().currentTime - playbackStartTimeRef.current;\n const pauseTime = audioStartPositionRef.current + elapsed;\n\n playoutRef.current.pause();\n setIsPlaying(false);\n stopAnimationLoop();\n\n // Update to the calculated pause position\n currentTimeRef.current = pauseTime;\n setCurrentTime(pauseTime);\n }, [stopAnimationLoop]);\n\n const stop = useCallback(() => {\n if (!playoutRef.current) return;\n\n playoutRef.current.stop();\n setIsPlaying(false);\n stopAnimationLoop();\n\n currentTimeRef.current = playStartPositionRef.current;\n setCurrentTime(playStartPositionRef.current);\n setActiveAnnotationId(null);\n }, [stopAnimationLoop, setActiveAnnotationId]);\n\n // Seek to a specific time - works whether playing or stopped\n const seekTo = useCallback(\n (time: number) => {\n // Clamp time to valid range\n const clampedTime = Math.max(0, Math.min(time, duration));\n\n // Update the current time state\n currentTimeRef.current = clampedTime;\n setCurrentTime(clampedTime);\n\n // If currently playing, stop and restart at the new position\n if (isPlaying && playoutRef.current) {\n playoutRef.current.stop();\n stopAnimationLoop();\n // Use play() which handles all the timing setup\n play(clampedTime);\n }\n },\n [duration, isPlaying, play, stopAnimationLoop]\n );\n\n // Track controls\n const setTrackMute = useCallback(\n (trackIndex: number, muted: boolean) => {\n const trackId = tracksRef.current[trackIndex]?.id;\n if (!trackId) return;\n\n const newStates = [...trackStates];\n newStates[trackIndex] = { ...newStates[trackIndex], muted };\n setTrackStates(newStates);\n\n if (playoutRef.current) {\n playoutRef.current.setMute(trackId, muted);\n }\n },\n [trackStates]\n );\n\n const setTrackSolo = useCallback(\n (trackIndex: number, soloed: boolean) => {\n const trackId = tracksRef.current[trackIndex]?.id;\n if (!trackId) return;\n\n const newStates = [...trackStates];\n newStates[trackIndex] = { ...newStates[trackIndex], soloed };\n setTrackStates(newStates);\n\n if (playoutRef.current) {\n playoutRef.current.setSolo(trackId, soloed);\n }\n },\n [trackStates]\n );\n\n const setTrackVolume = useCallback(\n (trackIndex: number, volume: number) => {\n const trackId = tracksRef.current[trackIndex]?.id;\n if (!trackId) return;\n\n const newStates = [...trackStates];\n newStates[trackIndex] = { ...newStates[trackIndex], volume };\n setTrackStates(newStates);\n\n if (playoutRef.current) {\n const toneTrack = playoutRef.current.getTrack(trackId);\n if (toneTrack) {\n toneTrack.setVolume(volume);\n }\n }\n },\n [trackStates]\n );\n\n const setTrackPan = useCallback(\n (trackIndex: number, pan: number) => {\n const trackId = tracksRef.current[trackIndex]?.id;\n if (!trackId) return;\n\n const newStates = [...trackStates];\n newStates[trackIndex] = { ...newStates[trackIndex], pan };\n setTrackStates(newStates);\n\n if (playoutRef.current) {\n const toneTrack = playoutRef.current.getTrack(trackId);\n if (toneTrack) {\n toneTrack.setPan(pan);\n }\n }\n },\n [trackStates]\n );\n\n // Selection\n const setSelection = useCallback(\n (start: number, end: number) => {\n setSelectionStart(start);\n setSelectionEnd(end);\n currentTimeRef.current = start;\n setCurrentTime(start);\n\n if (isPlaying && playoutRef.current) {\n playoutRef.current.stop();\n playoutRef.current.play(getContext().currentTime, start);\n }\n },\n [isPlaying]\n );\n\n // Memoize setScrollContainer callback\n const setScrollContainer = useCallback((element: HTMLDivElement | null) => {\n scrollContainerRef.current = element;\n }, []);\n\n // Stable callback ref for onAnnotationsChange to avoid re-creating controls context\n const onAnnotationsChangeRef = useRef(onAnnotationsChange);\n onAnnotationsChangeRef.current = onAnnotationsChange;\n\n const setAnnotations: React.Dispatch<React.SetStateAction<AnnotationData[]>> = useCallback(\n (action) => {\n const updated = typeof action === 'function' ? action(annotationsRef.current) : action;\n if (!onAnnotationsChangeRef.current) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n 'waveform-playlist: setAnnotations was called but no onAnnotationsChange callback is provided. ' +\n 'Annotation edits will not persist. Pass onAnnotationsChange to WaveformPlaylistProvider to handle annotation updates.'\n );\n }\n return;\n }\n onAnnotationsChangeRef.current(updated);\n },\n []\n );\n\n const sampleRate = audioBuffers[0]?.sampleRate || 44100;\n const timeScaleHeight = timescale ? 30 : 0;\n const minimumPlaylistHeight = tracks.length * waveHeight + timeScaleHeight;\n\n // Split context values for performance optimization\n // Animation context only re-renders consumers on discrete events\n // (play/pause/stop/seek), never during the animation loop itself\n\n const animationValue: PlaybackAnimationContextValue = useMemo(\n () => ({\n isPlaying,\n currentTime,\n currentTimeRef,\n playbackStartTimeRef,\n audioStartPositionRef,\n }),\n [isPlaying, currentTime, currentTimeRef, playbackStartTimeRef, audioStartPositionRef]\n );\n\n const stateValue: PlaylistStateContextValue = useMemo(\n () => ({\n continuousPlay,\n linkEndpoints,\n annotationsEditable,\n isAutomaticScroll,\n isLoopEnabled,\n annotations,\n activeAnnotationId,\n selectionStart,\n selectionEnd,\n selectedTrackId,\n loopStart,\n loopEnd,\n }),\n [\n continuousPlay,\n linkEndpoints,\n annotationsEditable,\n isAutomaticScroll,\n isLoopEnabled,\n annotations,\n activeAnnotationId,\n selectionStart,\n selectionEnd,\n selectedTrackId,\n loopStart,\n loopEnd,\n ]\n );\n\n const setCurrentTimeControl = useCallback(\n (time: number) => {\n currentTimeRef.current = time;\n setCurrentTime(time);\n },\n [currentTimeRef]\n );\n\n const setAutomaticScrollControl = useCallback((enabled: boolean) => {\n setIsAutomaticScroll(enabled);\n }, []);\n\n const controlsValue: PlaylistControlsContextValue = useMemo(\n () => ({\n // Playback controls\n play,\n pause,\n stop,\n seekTo,\n setCurrentTime: setCurrentTimeControl,\n\n // Track controls\n setTrackMute,\n setTrackSolo,\n setTrackVolume,\n setTrackPan,\n\n // Selection\n setSelection,\n setSelectedTrackId,\n\n // Time format\n setTimeFormat,\n formatTime,\n\n // Zoom\n zoomIn: zoom.zoomIn,\n zoomOut: zoom.zoomOut,\n\n // Master volume\n setMasterVolume,\n\n // Automatic scroll\n setAutomaticScroll: setAutomaticScrollControl,\n setScrollContainer,\n scrollContainerRef,\n\n // Annotation controls\n setContinuousPlay,\n setLinkEndpoints,\n setAnnotationsEditable,\n setAnnotations,\n setActiveAnnotationId,\n\n // Loop controls\n setLoopEnabled,\n setLoopRegion,\n setLoopRegionFromSelection,\n clearLoopRegion,\n }),\n [\n play,\n pause,\n stop,\n seekTo,\n setCurrentTimeControl,\n setTrackMute,\n setTrackSolo,\n setTrackVolume,\n setTrackPan,\n setSelection,\n setSelectedTrackId,\n setTimeFormat,\n formatTime,\n zoom.zoomIn,\n zoom.zoomOut,\n setMasterVolume,\n setAutomaticScrollControl,\n setScrollContainer,\n scrollContainerRef,\n setContinuousPlay,\n setLinkEndpoints,\n setAnnotationsEditable,\n setAnnotations,\n setActiveAnnotationId,\n setLoopEnabled,\n setLoopRegion,\n setLoopRegionFromSelection,\n clearLoopRegion,\n ]\n );\n\n const dataValue: PlaylistDataContextValue = useMemo(\n () => ({\n duration,\n audioBuffers,\n peaksDataArray,\n trackStates,\n tracks,\n sampleRate,\n waveHeight,\n timeScaleHeight,\n minimumPlaylistHeight,\n controls,\n playoutRef,\n samplesPerPixel,\n timeFormat,\n masterVolume,\n canZoomIn: zoom.canZoomIn,\n canZoomOut: zoom.canZoomOut,\n barWidth,\n barGap,\n progressBarWidth,\n isReady,\n mono,\n }),\n [\n duration,\n audioBuffers,\n peaksDataArray,\n trackStates,\n tracks,\n sampleRate,\n waveHeight,\n timeScaleHeight,\n minimumPlaylistHeight,\n controls,\n playoutRef,\n samplesPerPixel,\n timeFormat,\n masterVolume,\n zoom.canZoomIn,\n zoom.canZoomOut,\n barWidth,\n barGap,\n progressBarWidth,\n isReady,\n mono,\n ]\n );\n\n // Merge user theme with default theme\n const mergedTheme = { ...defaultTheme, ...userTheme };\n\n return (\n <ThemeProvider theme={mergedTheme}>\n <PlaybackAnimationContext.Provider value={animationValue}>\n <PlaylistStateContext.Provider value={stateValue}>\n <PlaylistControlsContext.Provider value={controlsValue}>\n <PlaylistDataContext.Provider value={dataValue}>\n {children}\n </PlaylistDataContext.Provider>\n </PlaylistControlsContext.Provider>\n </PlaylistStateContext.Provider>\n </PlaybackAnimationContext.Provider>\n </ThemeProvider>\n );\n};\n\n// Individual hooks for each context - use these for optimal performance\n// Components only re-render when their specific context data changes\n\nexport const usePlaybackAnimation = () => {\n const context = useContext(PlaybackAnimationContext);\n if (!context) {\n throw new Error('usePlaybackAnimation must be used within WaveformPlaylistProvider');\n }\n return context;\n};\n\nexport const usePlaylistState = () => {\n const context = useContext(PlaylistStateContext);\n if (!context) {\n throw new Error('usePlaylistState must be used within WaveformPlaylistProvider');\n }\n return context;\n};\n\nexport const usePlaylistControls = () => {\n const context = useContext(PlaylistControlsContext);\n if (!context) {\n throw new Error('usePlaylistControls must be used within WaveformPlaylistProvider');\n }\n return context;\n};\n\nexport const usePlaylistData = () => {\n const context = useContext(PlaylistDataContext);\n if (!context) {\n throw new Error('usePlaylistData must be used within WaveformPlaylistProvider');\n }\n return context;\n};\n","// src/MediaElementTrack.ts\nvar MediaElementTrack = class {\n constructor(options) {\n this._playbackRate = 1;\n this.handleEnded = () => {\n if (this.onStopCallback) {\n this.onStopCallback();\n }\n };\n this.handleTimeUpdate = () => {\n if (this.onTimeUpdateCallback) {\n this.onTimeUpdateCallback(this.audioElement.currentTime);\n }\n };\n this._peaks = options.peaks;\n this._id = options.id ?? `track-${Date.now()}`;\n this._name = options.name ?? \"Track\";\n this._playbackRate = options.playbackRate ?? 1;\n if (typeof options.source === \"string\") {\n this.audioElement = new Audio(options.source);\n this.ownsElement = true;\n } else {\n this.audioElement = options.source;\n this.ownsElement = false;\n }\n this.audioElement.preload = \"auto\";\n this.audioElement.volume = options.volume ?? 1;\n this.audioElement.playbackRate = this._playbackRate;\n const audio = this.audioElement;\n if (\"preservesPitch\" in this.audioElement) {\n audio.preservesPitch = true;\n } else if (\"mozPreservesPitch\" in this.audioElement) {\n audio.mozPreservesPitch = true;\n } else if (\"webkitPreservesPitch\" in this.audioElement) {\n audio.webkitPreservesPitch = true;\n }\n this.audioElement.addEventListener(\"ended\", this.handleEnded);\n this.audioElement.addEventListener(\"timeupdate\", this.handleTimeUpdate);\n }\n /**\n * Start playback from a specific time\n */\n play(offset = 0) {\n this.audioElement.currentTime = offset;\n this.audioElement.play().catch((err) => {\n console.warn(\"MediaElementTrack: play() failed:\", err);\n });\n }\n /**\n * Pause playback\n */\n pause() {\n this.audioElement.pause();\n }\n /**\n * Stop playback and reset to beginning\n */\n stop() {\n this.audioElement.pause();\n this.audioElement.currentTime = 0;\n }\n /**\n * Seek to a specific time\n */\n seekTo(time) {\n this.audioElement.currentTime = Math.max(0, Math.min(time, this.duration));\n }\n /**\n * Set volume (0.0 to 1.0)\n */\n setVolume(volume) {\n this.audioElement.volume = Math.max(0, Math.min(1, volume));\n }\n /**\n * Set playback rate (0.5 to 2.0, pitch preserved)\n */\n setPlaybackRate(rate) {\n const clampedRate = Math.max(0.5, Math.min(2, rate));\n this._playbackRate = clampedRate;\n this.audioElement.playbackRate = clampedRate;\n }\n /**\n * Set muted state\n */\n setMuted(muted) {\n this.audioElement.muted = muted;\n }\n /**\n * Set callback for when playback ends\n */\n setOnStopCallback(callback) {\n this.onStopCallback = callback;\n }\n /**\n * Set callback for time updates\n */\n setOnTimeUpdateCallback(callback) {\n this.onTimeUpdateCallback = callback;\n }\n /**\n * Clean up resources\n */\n dispose() {\n this.audioElement.removeEventListener(\"ended\", this.handleEnded);\n this.audioElement.removeEventListener(\"timeupdate\", this.handleTimeUpdate);\n this.audioElement.pause();\n if (this.ownsElement) {\n this.audioElement.src = \"\";\n this.audioElement.load();\n }\n }\n // Getters\n get id() {\n return this._id;\n }\n get name() {\n return this._name;\n }\n get peaks() {\n return this._peaks;\n }\n get currentTime() {\n return this.audioElement.currentTime;\n }\n get duration() {\n return this.audioElement.duration || this._peaks.duration;\n }\n get isPlaying() {\n return !this.audioElement.paused && !this.audioElement.ended;\n }\n get volume() {\n return this.audioElement.volume;\n }\n get playbackRate() {\n return this._playbackRate;\n }\n get muted() {\n return this.audioElement.muted;\n }\n /**\n * Get the underlying audio element (for advanced use cases)\n */\n get element() {\n return this.audioElement;\n }\n};\n\n// src/MediaElementPlayout.ts\nvar MediaElementPlayout = class {\n constructor(options = {}) {\n this.track = null;\n this._isPlaying = false;\n this._masterVolume = options.masterVolume ?? 1;\n this._playbackRate = options.playbackRate ?? 1;\n }\n /**\n * Initialize the playout engine.\n * For MediaElementPlayout this is a no-op (no AudioContext to start).\n */\n async init() {\n }\n /**\n * Add a track to the playout.\n * Note: Only one track is supported. Adding a second track will dispose the first.\n */\n addTrack(options) {\n if (this.track) {\n console.warn(\n \"MediaElementPlayout: Only one track is supported. Disposing previous track. For multi-track, use TonePlayout.\"\n );\n this.track.dispose();\n }\n this.track = new MediaElementTrack({\n ...options,\n volume: this._masterVolume * (options.volume ?? 1),\n playbackRate: this._playbackRate\n });\n this.track.setOnStopCallback(() => {\n this._isPlaying = false;\n if (this.onPlaybackCompleteCallback) {\n this.onPlaybackCompleteCallback();\n }\n });\n return this.track;\n }\n /**\n * Remove a track by ID.\n */\n removeTrack(trackId) {\n if (this.track && this.track.id === trackId) {\n this.track.dispose();\n this.track = null;\n }\n }\n /**\n * Get a track by ID.\n */\n getTrack(trackId) {\n if (this.track && this.track.id === trackId) {\n return this.track;\n }\n return void 0;\n }\n /**\n * Start playback.\n * @param _when - Ignored (HTMLAudioElement doesn't support scheduled start)\n * @param offset - Start position in seconds\n * @param duration - Duration to play in seconds (optional)\n */\n play(_when, offset, duration) {\n if (!this.track) {\n console.warn(\"MediaElementPlayout: No track to play\");\n return;\n }\n const startPosition = offset ?? 0;\n this._isPlaying = true;\n this.track.play(startPosition);\n if (duration !== void 0) {\n const adjustedDuration = duration / this._playbackRate;\n setTimeout(() => {\n if (this._isPlaying) {\n this.pause();\n if (this.onPlaybackCompleteCallback) {\n this.onPlaybackCompleteCallback();\n }\n }\n }, adjustedDuration * 1e3);\n }\n }\n /**\n * Pause playback.\n */\n pause() {\n if (this.track) {\n this.track.pause();\n }\n this._isPlaying = false;\n }\n /**\n * Stop playback and reset to start.\n */\n stop() {\n if (this.track) {\n this.track.stop();\n }\n this._isPlaying = false;\n }\n /**\n * Seek to a specific time.\n */\n seekTo(time) {\n if (this.track) {\n this.track.seekTo(time);\n }\n }\n /**\n * Get current playback time.\n */\n getCurrentTime() {\n if (this.track) {\n return this.track.currentTime;\n }\n return 0;\n }\n /**\n * Set master volume.\n */\n setMasterVolume(volume) {\n this._masterVolume = Math.max(0, Math.min(1, volume));\n if (this.track) {\n this.track.setVolume(this._masterVolume);\n }\n }\n /**\n * Set playback rate (0.5 to 2.0, pitch preserved).\n */\n setPlaybackRate(rate) {\n this._playbackRate = Math.max(0.5, Math.min(2, rate));\n if (this.track) {\n this.track.setPlaybackRate(this._playbackRate);\n }\n }\n /**\n * Set mute state for a track.\n */\n setMute(trackId, muted) {\n const track = this.getTrack(trackId);\n if (track) {\n track.setMuted(muted);\n }\n }\n /**\n * Set solo state for a track.\n * Note: With single track, solo is effectively the same as unmute.\n */\n setSolo(_trackId, _soloed) {\n console.warn(\"MediaElementPlayout: Solo is not applicable for single-track playback\");\n }\n /**\n * Set callback for when playback completes.\n */\n setOnPlaybackComplete(callback) {\n this.onPlaybackCompleteCallback = callback;\n }\n /**\n * Clean up resources.\n */\n dispose() {\n if (this.track) {\n this.track.dispose();\n this.track = null;\n }\n }\n // Getters\n get isPlaying() {\n return this._isPlaying;\n }\n get masterVolume() {\n return this._masterVolume;\n }\n get playbackRate() {\n return this._playbackRate;\n }\n get duration() {\n return this.track?.duration ?? 0;\n }\n get sampleRate() {\n return this.track?.peaks.sample_rate ?? 44100;\n }\n};\n\n// src/types.ts\nfunction supportsPlaybackRate(engine) {\n return \"setPlaybackRate\" in engine && typeof engine.setPlaybackRate === \"function\";\n}\nexport {\n MediaElementPlayout,\n MediaElementTrack,\n supportsPlaybackRate\n};\n//# sourceMappingURL=index.mjs.map","import React, {\n createContext,\n useContext,\n useState,\n useEffect,\n useRef,\n useCallback,\n useMemo,\n type ReactNode,\n} from 'react';\nimport { ThemeProvider } from 'styled-components';\nimport { MediaElementPlayout } from '@waveform-playlist/media-element-playout';\nimport { type WaveformDataObject } from '@waveform-playlist/core';\nimport { type WaveformPlaylistTheme, defaultTheme } from '@waveform-playlist/ui-components';\nimport type { AnnotationData } from '@waveform-playlist/core';\nimport { extractPeaksFromWaveformData } from './waveformDataLoader';\nimport type WaveformData from 'waveform-data';\nimport type { PeakData } from '@waveform-playlist/core';\nimport type { ClipPeaks, TrackClipPeaks } from './WaveformPlaylistContext';\nimport { useAnimationFrameLoop } from './hooks/useAnimationFrameLoop';\n\n// Configuration for a single media element track\nexport interface MediaElementTrackConfig {\n /** Audio source URL or Blob URL */\n source: string;\n /** Pre-computed waveform data (required for visualization) */\n waveformData: WaveformDataObject;\n /** Track name for display */\n name?: string;\n}\n\n// Context values for animation (high-frequency updates)\nexport interface MediaElementAnimationContextValue {\n isPlaying: boolean;\n currentTime: number;\n currentTimeRef: React.RefObject<number>;\n}\n\n// Context values for playlist state\nexport interface MediaElementStateContextValue {\n continuousPlay: boolean;\n annotations: AnnotationData[];\n activeAnnotationId: string | null;\n playbackRate: number;\n isAutomaticScroll: boolean;\n}\n\n// Context values for controls\nexport interface MediaElementControlsContextValue {\n play: (startTime?: number) => void;\n pause: () => void;\n stop: () => void;\n seekTo: (time: number) => void;\n setPlaybackRate: (rate: number) => void;\n setContinuousPlay: (enabled: boolean) => void;\n setAnnotations: React.Dispatch<React.SetStateAction<AnnotationData[]>>;\n setActiveAnnotationId: (id: string | null) => void;\n setAutomaticScroll: (enabled: boolean) => void;\n setScrollContainer: (element: HTMLDivElement | null) => void;\n scrollContainerRef: React.RefObject<HTMLDivElement | null>;\n}\n\n// Context values for playlist data\nexport interface MediaElementDataContextValue {\n duration: number;\n peaksDataArray: TrackClipPeaks[];\n sampleRate: number;\n waveHeight: number;\n timeScaleHeight: number;\n samplesPerPixel: number;\n playoutRef: React.RefObject<MediaElementPlayout | null>;\n controls: { show: boolean; width: number };\n barWidth: number;\n barGap: number;\n progressBarWidth: number;\n}\n\n// Create contexts\nconst MediaElementAnimationContext = createContext<MediaElementAnimationContextValue | null>(null);\nconst MediaElementStateContext = createContext<MediaElementStateContextValue | null>(null);\nconst MediaElementControlsContext = createContext<MediaElementControlsContextValue | null>(null);\nconst MediaElementDataContext = createContext<MediaElementDataContextValue | null>(null);\n\nexport interface MediaElementPlaylistProviderProps {\n /** Single track configuration with source URL and waveform data */\n track: MediaElementTrackConfig;\n /** Initial samples per pixel (zoom level) */\n samplesPerPixel?: number;\n /** Height of each waveform track */\n waveHeight?: number;\n /** Show timescale */\n timescale?: boolean;\n /** Initial playback rate (0.5 to 2.0) */\n playbackRate?: number;\n /** Enable automatic scroll to keep playhead centered */\n automaticScroll?: boolean;\n /** Theme configuration */\n theme?: Partial<WaveformPlaylistTheme>;\n /** Track controls configuration */\n controls?: { show: boolean; width: number };\n /** Annotations */\n annotationList?: {\n annotations?: AnnotationData[];\n isContinuousPlay?: boolean;\n };\n /** Width of waveform bars */\n barWidth?: number;\n /** Gap between waveform bars */\n barGap?: number;\n /** Width of progress bars */\n progressBarWidth?: number;\n /** Callback when annotations are changed (drag, edit, etc.) */\n onAnnotationsChange?: (annotations: AnnotationData[]) => void;\n /** Callback when audio is ready */\n onReady?: () => void;\n children: ReactNode;\n}\n\n/**\n * MediaElementPlaylistProvider\n *\n * A simplified playlist provider for single-track playback using HTMLAudioElement.\n * Key features:\n * - Pitch-preserving playback rate (0.5x - 2.0x)\n * - Pre-computed peaks visualization (no AudioBuffer needed)\n * - Simpler API than full WaveformPlaylistProvider\n *\n * Use this for:\n * - Language learning apps (speed control)\n * - Podcast players\n * - Single-track audio viewers\n *\n * For multi-track editing, use WaveformPlaylistProvider instead.\n */\nexport const MediaElementPlaylistProvider: React.FC<MediaElementPlaylistProviderProps> = ({\n track,\n samplesPerPixel: initialSamplesPerPixel = 1024,\n waveHeight = 100,\n timescale = false,\n playbackRate: initialPlaybackRate = 1,\n automaticScroll = false,\n theme: userTheme,\n controls = { show: false, width: 0 },\n annotationList,\n barWidth = 1,\n barGap = 0,\n progressBarWidth: progressBarWidthProp,\n onAnnotationsChange,\n onReady,\n children,\n}) => {\n const progressBarWidth = progressBarWidthProp ?? barWidth + barGap;\n\n // State\n const [isPlaying, setIsPlaying] = useState(false);\n const [currentTime, setCurrentTime] = useState(0);\n const [duration, setDuration] = useState(0);\n const [peaksDataArray, setPeaksDataArray] = useState<TrackClipPeaks[]>([]);\n const [playbackRate, setPlaybackRateState] = useState(initialPlaybackRate);\n // Annotations are derived from prop (single source of truth in parent)\n // In v6, annotations must be pre-parsed (numeric start/end). Use parseAeneas() from @waveform-playlist/annotations before passing.\n const annotations = useMemo(() => {\n if (!annotationList?.annotations) return [];\n if (process.env.NODE_ENV !== 'production' && annotationList.annotations.length > 0) {\n const first = annotationList.annotations[0] as unknown as Record<string, unknown>;\n if (typeof first.start !== 'number' || typeof first.end !== 'number') {\n console.error(\n '[waveform-playlist] Annotations must have numeric start/end values. ' +\n 'In v6, use parseAeneas() from @waveform-playlist/annotations before passing annotations. ' +\n 'Received start type: ' +\n typeof first.start\n );\n return [];\n }\n }\n return annotationList.annotations;\n }, [annotationList?.annotations]);\n\n // Ref for animation loop (avoids restarting loop on annotation change)\n const annotationsRef = useRef<AnnotationData[]>(annotations);\n annotationsRef.current = annotations;\n\n const [activeAnnotationId, setActiveAnnotationIdState] = useState<string | null>(null);\n const [continuousPlay, setContinuousPlayState] = useState(\n annotationList?.isContinuousPlay ?? false\n );\n const [samplesPerPixel] = useState(initialSamplesPerPixel);\n const [isAutomaticScroll, setIsAutomaticScroll] = useState(automaticScroll);\n\n // Refs\n const playoutRef = useRef<MediaElementPlayout | null>(null);\n const currentTimeRef = useRef<number>(0);\n const continuousPlayRef = useRef<boolean>(continuousPlay);\n const activeAnnotationIdRef = useRef<string | null>(null);\n const scrollContainerRef = useRef<HTMLDivElement | null>(null);\n const isAutomaticScrollRef = useRef<boolean>(automaticScroll);\n const samplesPerPixelRef = useRef<number>(initialSamplesPerPixel);\n const { startAnimationFrameLoop, stopAnimationFrameLoop } = useAnimationFrameLoop();\n\n // Sync refs\n useEffect(() => {\n continuousPlayRef.current = continuousPlay;\n }, [continuousPlay]);\n\n useEffect(() => {\n isAutomaticScrollRef.current = isAutomaticScroll;\n }, [isAutomaticScroll]);\n\n // Custom setter for activeAnnotationId\n const setActiveAnnotationId = useCallback((value: string | null) => {\n activeAnnotationIdRef.current = value;\n setActiveAnnotationIdState(value);\n }, []);\n\n const setContinuousPlay = useCallback((value: boolean) => {\n continuousPlayRef.current = value;\n setContinuousPlayState(value);\n }, []);\n\n // Memoize setScrollContainer callback\n const setScrollContainer = useCallback((element: HTMLDivElement | null) => {\n scrollContainerRef.current = element;\n }, []);\n\n // Get sample rate from waveform data\n const sampleRate = track.waveformData.sample_rate;\n\n // Initialize playout and load track\n useEffect(() => {\n const playout = new MediaElementPlayout({\n playbackRate: initialPlaybackRate,\n });\n\n playout.addTrack({\n source: track.source,\n peaks: track.waveformData,\n name: track.name,\n });\n\n // Set up time update callback\n const mediaTrack = playout.getTrack(playout['track']?.id ?? '');\n if (mediaTrack) {\n mediaTrack.setOnTimeUpdateCallback((time) => {\n currentTimeRef.current = time;\n });\n }\n\n // Set up playback complete callback\n playout.setOnPlaybackComplete(() => {\n stopAnimationFrameLoop();\n setIsPlaying(false);\n setActiveAnnotationId(null);\n currentTimeRef.current = 0;\n setCurrentTime(0);\n });\n\n playoutRef.current = playout;\n setDuration(track.waveformData.duration);\n onReady?.();\n\n return () => {\n stopAnimationFrameLoop();\n playout.dispose();\n };\n }, [\n track.source,\n track.waveformData,\n track.name,\n initialPlaybackRate,\n onReady,\n stopAnimationFrameLoop,\n setActiveAnnotationId,\n ]);\n\n // Generate peaks from waveform data\n useEffect(() => {\n const extractedPeaks = extractPeaksFromWaveformData(\n track.waveformData as WaveformData,\n samplesPerPixel,\n 0, // channel index\n 0, // offset\n Math.ceil(track.waveformData.duration * sampleRate) // duration in samples\n );\n\n const clipPeaks: ClipPeaks = {\n clipId: 'media-element-clip',\n trackName: track.name ?? 'Track',\n peaks: {\n length: extractedPeaks.length,\n data: [extractedPeaks.data],\n bits: extractedPeaks.bits,\n } as PeakData,\n startSample: 0,\n durationSamples: Math.ceil(track.waveformData.duration * sampleRate),\n };\n\n setPeaksDataArray([[clipPeaks]]);\n }, [track.waveformData, track.name, samplesPerPixel, sampleRate]);\n\n // Animation loop\n const startAnimationLoop = useCallback(() => {\n const updateTime = () => {\n const time = playoutRef.current?.getCurrentTime() ?? 0;\n currentTimeRef.current = time;\n\n // Handle annotation playback\n const currentAnnotations = annotationsRef.current;\n if (currentAnnotations.length > 0) {\n const currentAnnotation = currentAnnotations.find(\n (ann) => time >= ann.start && time < ann.end\n );\n\n if (continuousPlayRef.current) {\n if (currentAnnotation && currentAnnotation.id !== activeAnnotationIdRef.current) {\n setActiveAnnotationId(currentAnnotation.id);\n } else if (!currentAnnotation && activeAnnotationIdRef.current !== null) {\n // Clear the active annotation when we're past it, but don't stop playback\n // Let playback continue until the audio element fires its 'ended' event\n setActiveAnnotationId(null);\n }\n } else {\n if (activeAnnotationIdRef.current) {\n const activeAnnotation = currentAnnotations.find(\n (ann) => ann.id === activeAnnotationIdRef.current\n );\n if (activeAnnotation && time >= activeAnnotation.end) {\n playoutRef.current?.stop();\n setIsPlaying(false);\n return;\n }\n } else if (currentAnnotation) {\n setActiveAnnotationId(currentAnnotation.id);\n }\n }\n }\n\n // Handle automatic scroll - continuously center the playhead\n if (isAutomaticScrollRef.current && scrollContainerRef.current) {\n const container = scrollContainerRef.current;\n const pixelPosition = (time * sampleRate) / samplesPerPixelRef.current;\n const containerWidth = container.clientWidth;\n\n // Calculate visual position of playhead (includes controls offset)\n const controlWidth = controls.show ? controls.width : 0;\n const visualPosition = pixelPosition + controlWidth;\n\n // Continuously scroll to keep playhead centered\n const targetScrollLeft = Math.max(0, visualPosition - containerWidth / 2);\n container.scrollLeft = targetScrollLeft;\n }\n\n startAnimationFrameLoop(updateTime);\n };\n\n startAnimationFrameLoop(updateTime);\n }, [setActiveAnnotationId, sampleRate, controls, startAnimationFrameLoop]);\n\n const stopAnimationLoop = stopAnimationFrameLoop;\n\n // Playback controls\n const play = useCallback(\n (startTime?: number) => {\n if (!playoutRef.current) return;\n\n const actualStartTime = startTime ?? currentTimeRef.current;\n playoutRef.current.play(undefined, actualStartTime);\n setIsPlaying(true);\n startAnimationLoop();\n },\n [startAnimationLoop]\n );\n\n const pause = useCallback(() => {\n if (!playoutRef.current) return;\n\n playoutRef.current.pause();\n setIsPlaying(false);\n stopAnimationLoop();\n setCurrentTime(playoutRef.current.getCurrentTime());\n }, [stopAnimationLoop]);\n\n const stop = useCallback(() => {\n if (!playoutRef.current) return;\n\n playoutRef.current.stop();\n setIsPlaying(false);\n stopAnimationLoop();\n currentTimeRef.current = 0;\n setCurrentTime(0);\n setActiveAnnotationId(null);\n }, [stopAnimationLoop, setActiveAnnotationId]);\n\n const seekTo = useCallback(\n (time: number) => {\n const clampedTime = Math.max(0, Math.min(time, duration));\n currentTimeRef.current = clampedTime;\n setCurrentTime(clampedTime);\n\n if (playoutRef.current) {\n playoutRef.current.seekTo(clampedTime);\n }\n },\n [duration]\n );\n\n const setPlaybackRate = useCallback((rate: number) => {\n const clampedRate = Math.max(0.5, Math.min(2.0, rate));\n setPlaybackRateState(clampedRate);\n if (playoutRef.current) {\n playoutRef.current.setPlaybackRate(clampedRate);\n }\n }, []);\n\n const timeScaleHeight = timescale ? 30 : 0;\n\n // Context values\n const animationValue: MediaElementAnimationContextValue = useMemo(\n () => ({\n isPlaying,\n currentTime,\n currentTimeRef,\n }),\n [isPlaying, currentTime]\n );\n\n const stateValue: MediaElementStateContextValue = useMemo(\n () => ({\n continuousPlay,\n annotations,\n activeAnnotationId,\n playbackRate,\n isAutomaticScroll,\n }),\n [continuousPlay, annotations, activeAnnotationId, playbackRate, isAutomaticScroll]\n );\n\n // Stable callback ref for onAnnotationsChange to avoid re-creating controls context\n const onAnnotationsChangeRef = useRef(onAnnotationsChange);\n onAnnotationsChangeRef.current = onAnnotationsChange;\n\n const setAnnotations: React.Dispatch<React.SetStateAction<AnnotationData[]>> = useCallback(\n (action) => {\n const updated = typeof action === 'function' ? action(annotationsRef.current) : action;\n if (!onAnnotationsChangeRef.current) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n 'waveform-playlist: setAnnotations was called but no onAnnotationsChange callback is provided. ' +\n 'Annotation edits will not persist. Pass onAnnotationsChange to MediaElementPlaylistProvider to handle annotation updates.'\n );\n }\n return;\n }\n onAnnotationsChangeRef.current(updated);\n },\n []\n );\n\n const controlsValue: MediaElementControlsContextValue = useMemo(\n () => ({\n play,\n pause,\n stop,\n seekTo,\n setPlaybackRate,\n setContinuousPlay,\n setAnnotations,\n setActiveAnnotationId,\n setAutomaticScroll: (enabled: boolean) => {\n setIsAutomaticScroll(enabled);\n },\n setScrollContainer,\n scrollContainerRef,\n }),\n [\n play,\n pause,\n stop,\n seekTo,\n setPlaybackRate,\n setContinuousPlay,\n setAnnotations,\n setActiveAnnotationId,\n setScrollContainer,\n ]\n );\n\n const dataValue: MediaElementDataContextValue = useMemo(\n () => ({\n duration,\n peaksDataArray,\n sampleRate,\n waveHeight,\n timeScaleHeight,\n samplesPerPixel,\n playoutRef,\n controls,\n barWidth,\n barGap,\n progressBarWidth,\n }),\n [\n duration,\n peaksDataArray,\n sampleRate,\n waveHeight,\n timeScaleHeight,\n samplesPerPixel,\n controls,\n barWidth,\n barGap,\n progressBarWidth,\n ]\n );\n\n const mergedTheme = { ...defaultTheme, ...userTheme };\n\n return (\n <ThemeProvider theme={mergedTheme}>\n <MediaElementAnimationContext.Provider value={animationValue}>\n <MediaElementStateContext.Provider value={stateValue}>\n <MediaElementControlsContext.Provider value={controlsValue}>\n <MediaElementDataContext.Provider value={dataValue}>\n {children}\n </MediaElementDataContext.Provider>\n </MediaElementControlsContext.Provider>\n </MediaElementStateContext.Provider>\n </MediaElementAnimationContext.Provider>\n </ThemeProvider>\n );\n};\n\n// Hooks\nexport const useMediaElementAnimation = () => {\n const context = useContext(MediaElementAnimationContext);\n if (!context) {\n throw new Error('useMediaElementAnimation must be used within MediaElementPlaylistProvider');\n }\n return context;\n};\n\nexport const useMediaElementState = () => {\n const context = useContext(MediaElementStateContext);\n if (!context) {\n throw new Error('useMediaElementState must be used within MediaElementPlaylistProvider');\n }\n return context;\n};\n\nexport const useMediaElementControls = () => {\n const context = useContext(MediaElementControlsContext);\n if (!context) {\n throw new Error('useMediaElementControls must be used within MediaElementPlaylistProvider');\n }\n return context;\n};\n\nexport const useMediaElementData = () => {\n const context = useContext(MediaElementDataContext);\n if (!context) {\n throw new Error('useMediaElementData must be used within MediaElementPlaylistProvider');\n }\n return context;\n};\n","import React from 'react';\nimport { BaseControlButton } from '@waveform-playlist/ui-components';\nimport {\n usePlaybackAnimation,\n usePlaylistState,\n usePlaylistControls,\n usePlaylistData,\n} from '../WaveformPlaylistContext';\n\nexport const PlayButton: React.FC<{ className?: string }> = ({ className }) => {\n const { isPlaying, currentTimeRef } = usePlaybackAnimation();\n const { selectionStart, selectionEnd, isLoopEnabled } = usePlaylistState();\n const { play } = usePlaylistControls();\n\n const handleClick = async () => {\n const hasSelection = selectionStart !== selectionEnd && selectionEnd > selectionStart;\n\n if (hasSelection) {\n if (isLoopEnabled) {\n // With loop: Start from selection start, let loop logic handle boundaries\n // Playback continues until it gets trapped in loop or reaches end\n await play(selectionStart);\n } else {\n // Without loop: Play selection region only, then stop\n const duration = selectionEnd - selectionStart;\n await play(selectionStart, duration);\n }\n } else {\n // No selection: Play from current position to the end\n await play(currentTimeRef.current ?? 0);\n }\n };\n\n return (\n <BaseControlButton onClick={handleClick} disabled={isPlaying} className={className}>\n Play\n </BaseControlButton>\n );\n};\n\nexport const PauseButton: React.FC<{ className?: string }> = ({ className }) => {\n const { isPlaying } = usePlaybackAnimation();\n const { pause } = usePlaylistControls();\n\n return (\n <BaseControlButton onClick={pause} disabled={!isPlaying} className={className}>\n Pause\n </BaseControlButton>\n );\n};\n\nexport const StopButton: React.FC<{ className?: string }> = ({ className }) => {\n const { isPlaying } = usePlaybackAnimation();\n const { stop } = usePlaylistControls();\n\n return (\n <BaseControlButton onClick={stop} disabled={!isPlaying} className={className}>\n Stop\n </BaseControlButton>\n );\n};\n\nexport const RewindButton: React.FC<{ className?: string }> = ({ className }) => {\n const { isPlaying } = usePlaybackAnimation();\n const { play, setCurrentTime } = usePlaylistControls();\n const { playoutRef } = usePlaylistData();\n\n const handleClick = () => {\n setCurrentTime(0);\n\n if (isPlaying && playoutRef.current) {\n playoutRef.current.stop();\n play(0);\n }\n };\n\n return (\n <BaseControlButton onClick={handleClick} className={className}>\n Rewind\n </BaseControlButton>\n );\n};\n\nexport const FastForwardButton: React.FC<{ className?: string }> = ({ className }) => {\n const { isPlaying } = usePlaybackAnimation();\n const { play, setCurrentTime } = usePlaylistControls();\n const { duration, playoutRef } = usePlaylistData();\n\n const handleClick = () => {\n setCurrentTime(duration);\n\n if (isPlaying && playoutRef.current) {\n playoutRef.current.stop();\n play(duration);\n }\n };\n\n return (\n <BaseControlButton onClick={handleClick} className={className}>\n Fast Forward\n </BaseControlButton>\n );\n};\n\nexport const SkipBackwardButton: React.FC<{ skipAmount?: number; className?: string }> = ({\n skipAmount = 5,\n className,\n}) => {\n const { currentTimeRef, isPlaying } = usePlaybackAnimation();\n const { play, setCurrentTime } = usePlaylistControls();\n const { playoutRef } = usePlaylistData();\n\n const handleClick = () => {\n const newTime = Math.max(0, (currentTimeRef.current ?? 0) - skipAmount);\n setCurrentTime(newTime);\n\n if (isPlaying && playoutRef.current) {\n playoutRef.current.stop();\n play(newTime);\n }\n };\n\n return (\n <BaseControlButton onClick={handleClick} className={className}>\n Skip Backward\n </BaseControlButton>\n );\n};\n\nexport const SkipForwardButton: React.FC<{ skipAmount?: number; className?: string }> = ({\n skipAmount = 5,\n className,\n}) => {\n const { currentTimeRef, isPlaying } = usePlaybackAnimation();\n const { play, setCurrentTime } = usePlaylistControls();\n const { duration, playoutRef } = usePlaylistData();\n\n const handleClick = () => {\n const newTime = Math.min(duration, (currentTimeRef.current ?? 0) + skipAmount);\n setCurrentTime(newTime);\n\n if (isPlaying && playoutRef.current) {\n playoutRef.current.stop();\n play(newTime);\n }\n };\n\n return (\n <BaseControlButton onClick={handleClick} className={className}>\n Skip Forward\n </BaseControlButton>\n );\n};\n\nexport const LoopButton: React.FC<{ className?: string }> = ({ className }) => {\n const { isLoopEnabled, loopStart, loopEnd } = usePlaylistState();\n const { setLoopEnabled, setLoopRegion } = usePlaylistControls();\n const { duration } = usePlaylistData();\n\n const hasValidLoopRegion = loopStart !== loopEnd && loopEnd > loopStart;\n\n const handleClick = () => {\n if (!isLoopEnabled && !hasValidLoopRegion) {\n // Create a default loop region when enabling loop without one\n // Default to first 10 seconds or 25% of duration, whichever is smaller\n const defaultEnd = Math.min(10, duration * 0.25);\n setLoopRegion(0, Math.max(1, defaultEnd)); // At least 1 second\n }\n setLoopEnabled(!isLoopEnabled);\n };\n\n return (\n <BaseControlButton\n onClick={handleClick}\n className={className}\n title={isLoopEnabled ? 'Disable loop' : 'Enable loop'}\n >\n {isLoopEnabled ? 'Loop On' : 'Loop Off'}\n </BaseControlButton>\n );\n};\n\nexport const SetLoopRegionButton: React.FC<{ className?: string }> = ({ className }) => {\n const { selectionStart, selectionEnd, loopStart, loopEnd } = usePlaylistState();\n const { setLoopRegionFromSelection, clearLoopRegion } = usePlaylistControls();\n\n const hasValidSelection = selectionStart !== selectionEnd && selectionEnd > selectionStart;\n const hasLoopRegion = loopStart !== loopEnd && loopEnd > loopStart;\n\n const handleClick = () => {\n if (hasLoopRegion) {\n clearLoopRegion();\n } else {\n setLoopRegionFromSelection();\n }\n };\n\n return (\n <BaseControlButton\n onClick={handleClick}\n disabled={!hasValidSelection && !hasLoopRegion}\n className={className}\n title={\n hasLoopRegion\n ? 'Clear loop region'\n : hasValidSelection\n ? 'Set loop region from selection'\n : 'Create a selection first'\n }\n >\n {hasLoopRegion ? 'Clear Loop' : 'Set Loop'}\n </BaseControlButton>\n );\n};\n","import React from 'react';\nimport { BaseControlButton } from '@waveform-playlist/ui-components';\nimport { usePlaylistControls, usePlaylistData } from '../WaveformPlaylistContext';\n\nexport const ZoomInButton: React.FC<{ className?: string; disabled?: boolean }> = ({\n className,\n disabled,\n}) => {\n const { zoomIn } = usePlaylistControls();\n const { canZoomIn } = usePlaylistData();\n\n return (\n <BaseControlButton onClick={zoomIn} disabled={disabled || !canZoomIn} className={className}>\n Zoom In\n </BaseControlButton>\n );\n};\n\nexport const ZoomOutButton: React.FC<{ className?: string; disabled?: boolean }> = ({\n className,\n disabled,\n}) => {\n const { zoomOut } = usePlaylistControls();\n const { canZoomOut } = usePlaylistData();\n\n return (\n <BaseControlButton onClick={zoomOut} disabled={disabled || !canZoomOut} className={className}>\n Zoom Out\n </BaseControlButton>\n );\n};\n","import React, { useRef, useEffect } from 'react';\nimport { getContext } from 'tone';\nimport {\n MasterVolumeControl as BaseMasterVolumeControl,\n TimeFormatSelect as BaseTimeFormatSelect,\n AutomaticScrollCheckbox as BaseAutomaticScrollCheckbox,\n SelectionTimeInputs as BaseSelectionTimeInputs,\n formatTime,\n} from '@waveform-playlist/ui-components';\nimport styled from 'styled-components';\nimport {\n usePlaybackAnimation,\n usePlaylistState,\n usePlaylistControls,\n usePlaylistData,\n} from '../WaveformPlaylistContext';\n\n/**\n * Master volume control that uses the playlist context\n */\nexport const MasterVolumeControl: React.FC<{ className?: string }> = ({ className }) => {\n const { masterVolume } = usePlaylistData();\n const { setMasterVolume } = usePlaylistControls();\n\n return (\n <BaseMasterVolumeControl\n volume={masterVolume}\n onChange={setMasterVolume}\n className={className}\n />\n );\n};\n\n/**\n * Time format selector that uses the playlist context\n */\nexport const TimeFormatSelect: React.FC<{ className?: string }> = ({ className }) => {\n const { timeFormat } = usePlaylistData();\n const { setTimeFormat } = usePlaylistControls();\n\n return <BaseTimeFormatSelect value={timeFormat} onChange={setTimeFormat} className={className} />;\n};\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\n/**\n * Audio position display that uses the playlist context.\n * Uses requestAnimationFrame for smooth 60fps updates during playback.\n * Direct DOM manipulation avoids React re-renders.\n */\nexport const AudioPosition: React.FC<{ className?: string }> = ({ className }) => {\n const timeRef = useRef<HTMLSpanElement>(null);\n const animationFrameRef = useRef<number | null>(null);\n const { isPlaying, currentTimeRef, playbackStartTimeRef, audioStartPositionRef } =\n usePlaybackAnimation();\n const { timeFormat: format } = usePlaylistData();\n\n useEffect(() => {\n const updateTime = () => {\n if (timeRef.current) {\n let time: number;\n if (isPlaying) {\n const elapsed = getContext().currentTime - (playbackStartTimeRef.current ?? 0);\n time = (audioStartPositionRef.current ?? 0) + elapsed;\n } else {\n time = currentTimeRef.current ?? 0;\n }\n timeRef.current.textContent = formatTime(time, format);\n }\n\n if (isPlaying) {\n animationFrameRef.current = requestAnimationFrame(updateTime);\n }\n };\n\n if (isPlaying) {\n animationFrameRef.current = requestAnimationFrame(updateTime);\n } else {\n updateTime();\n }\n\n return () => {\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n animationFrameRef.current = null;\n }\n };\n }, [isPlaying, format, currentTimeRef, playbackStartTimeRef, audioStartPositionRef]);\n\n // Update when stopped (for seeks)\n useEffect(() => {\n if (!isPlaying && timeRef.current) {\n timeRef.current.textContent = formatTime(currentTimeRef.current ?? 0, format);\n }\n });\n\n return (\n <PositionDisplay ref={timeRef} className={className} aria-label=\"Audio position\">\n {formatTime(currentTimeRef.current ?? 0, format)}\n </PositionDisplay>\n );\n};\n\n/**\n * Selection time inputs that use the playlist context\n */\nexport const SelectionTimeInputs: React.FC<{ className?: string }> = ({ className }) => {\n const { selectionStart, selectionEnd } = usePlaylistState();\n const { setSelection } = usePlaylistControls();\n\n return (\n <BaseSelectionTimeInputs\n selectionStart={selectionStart}\n selectionEnd={selectionEnd}\n onSelectionChange={setSelection}\n className={className}\n />\n );\n};\n\n/**\n * Automatic scroll checkbox that uses the playlist context\n * Uses split contexts to avoid re-rendering during animation\n */\nexport const AutomaticScrollCheckbox: React.FC<{ className?: string }> = ({ className }) => {\n const { isAutomaticScroll } = usePlaylistState();\n const { setAutomaticScroll } = usePlaylistControls();\n\n return (\n <BaseAutomaticScrollCheckbox\n checked={isAutomaticScroll}\n onChange={setAutomaticScroll}\n className={className}\n />\n );\n};\n","import { createContext, useContext } from 'react';\nimport type {\n AnnotationData,\n AnnotationAction,\n AnnotationActionOptions,\n RenderAnnotationItemProps,\n} from '@waveform-playlist/core';\n\n/**\n * Props the browser package passes to the AnnotationText component.\n * Mirrors what PlaylistAnnotationList and MediaElementAnnotationList actually use.\n */\nexport interface AnnotationTextIntegrationProps {\n annotations: AnnotationData[];\n activeAnnotationId?: string;\n shouldScrollToActive?: boolean;\n scrollActivePosition?: ScrollLogicalPosition;\n scrollActiveContainer?: 'nearest' | 'all';\n editable?: boolean;\n controls?: AnnotationAction[];\n annotationListConfig?: AnnotationActionOptions;\n height?: number;\n onAnnotationUpdate?: (updatedAnnotations: AnnotationData[]) => void;\n renderAnnotationItem?: (props: RenderAnnotationItemProps) => React.ReactNode;\n}\n\n/**\n * Props the browser package passes to the AnnotationBox component.\n * Mirrors what PlaylistVisualization and MediaElementPlaylist actually use.\n */\nexport interface AnnotationBoxIntegrationProps {\n annotationId: string;\n annotationIndex: number;\n startPosition: number;\n endPosition: number;\n label?: string;\n color?: string;\n isActive?: boolean;\n onClick?: () => void;\n editable?: boolean;\n}\n\n/**\n * Props the browser package passes to the AnnotationBoxesWrapper component.\n * Mirrors what PlaylistVisualization and MediaElementPlaylist actually use.\n */\nexport interface AnnotationBoxesWrapperIntegrationProps {\n children?: React.ReactNode;\n height?: number;\n width?: number;\n}\n\n/**\n * Interface for annotation integration provided by @waveform-playlist/annotations.\n *\n * The browser package defines what it needs, and the optional annotations package\n * provides it via <AnnotationProvider>.\n */\nexport interface AnnotationIntegration {\n // Parser functions\n parseAeneas: (data: unknown) => AnnotationData;\n serializeAeneas: (annotation: AnnotationData) => unknown;\n\n // Visualization components (typed with the props the browser package actually passes)\n AnnotationText: React.ComponentType<AnnotationTextIntegrationProps>;\n AnnotationBox: React.ComponentType<AnnotationBoxIntegrationProps>;\n AnnotationBoxesWrapper: React.ComponentType<AnnotationBoxesWrapperIntegrationProps>;\n\n // Control components\n ContinuousPlayCheckbox: React.ComponentType<{\n checked: boolean;\n onChange: (checked: boolean) => void;\n className?: string;\n }>;\n LinkEndpointsCheckbox: React.ComponentType<{\n checked: boolean;\n onChange: (checked: boolean) => void;\n className?: string;\n }>;\n EditableCheckbox: React.ComponentType<{\n checked: boolean;\n onChange: (checked: boolean) => void;\n className?: string;\n }>;\n DownloadAnnotationsButton: React.ComponentType<{\n annotations: AnnotationData[];\n filename?: string;\n className?: string;\n }>;\n}\n\nexport const AnnotationIntegrationContext = createContext<AnnotationIntegration | null>(null);\n\nexport const AnnotationIntegrationProvider = AnnotationIntegrationContext.Provider;\n\n/**\n * Hook to access annotation integration provided by @waveform-playlist/annotations.\n * Throws if used without <AnnotationProvider> wrapping the component tree.\n *\n * Follows the Kent C. Dodds pattern:\n * https://kentcdodds.com/blog/how-to-use-react-context-effectively\n */\nexport function useAnnotationIntegration(): AnnotationIntegration {\n const context = useContext(AnnotationIntegrationContext);\n if (!context) {\n throw new Error(\n 'useAnnotationIntegration must be used within <AnnotationProvider>. ' +\n 'Install @waveform-playlist/annotations and wrap your app with <AnnotationProvider>. ' +\n 'See: https://waveform-playlist.naomiaro.com/docs/guides/annotations'\n );\n }\n return context;\n}\n","import React from 'react';\nimport { useAnnotationIntegration } from '../AnnotationIntegrationContext';\nimport { usePlaylistState, usePlaylistControls } from '../WaveformPlaylistContext';\n\n/**\n * Continuous play checkbox that uses the playlist context.\n * Must be used within <AnnotationProvider>.\n */\nexport const ContinuousPlayCheckbox: React.FC<{ className?: string }> = ({ className }) => {\n const { ContinuousPlayCheckbox: Base } = useAnnotationIntegration();\n const { continuousPlay } = usePlaylistState();\n const { setContinuousPlay } = usePlaylistControls();\n\n return <Base checked={continuousPlay} onChange={setContinuousPlay} className={className} />;\n};\n\n/**\n * Link endpoints checkbox that uses the playlist context.\n * Must be used within <AnnotationProvider>.\n */\nexport const LinkEndpointsCheckbox: React.FC<{ className?: string }> = ({ className }) => {\n const { LinkEndpointsCheckbox: Base } = useAnnotationIntegration();\n const { linkEndpoints } = usePlaylistState();\n const { setLinkEndpoints } = usePlaylistControls();\n\n return <Base checked={linkEndpoints} onChange={setLinkEndpoints} className={className} />;\n};\n\n/**\n * Editable annotations checkbox that uses the playlist context.\n * Must be used within <AnnotationProvider>.\n */\nexport const EditableCheckbox: React.FC<{ className?: string }> = ({ className }) => {\n const { EditableCheckbox: Base } = useAnnotationIntegration();\n const { annotationsEditable } = usePlaylistState();\n const { setAnnotationsEditable } = usePlaylistControls();\n\n return (\n <Base checked={annotationsEditable} onChange={setAnnotationsEditable} className={className} />\n );\n};\n\n/**\n * Download annotations button that uses the playlist context.\n * Must be used within <AnnotationProvider>.\n */\nexport const DownloadAnnotationsButton: React.FC<{ filename?: string; className?: string }> = ({\n filename,\n className,\n}) => {\n const { DownloadAnnotationsButton: Base } = useAnnotationIntegration();\n const { annotations } = usePlaylistState();\n\n return <Base annotations={annotations} filename={filename} className={className} />;\n};\n","import React from 'react';\nimport { BaseControlButton } from '@waveform-playlist/ui-components';\nimport type { EffectsFunction } from '@waveform-playlist/playout';\nimport { usePlaylistData } from '../WaveformPlaylistContext';\nimport { useExportWav, type TrackEffectsFunction } from '../hooks/useExportWav';\n\nexport interface ExportWavButtonProps {\n /** Button label */\n label?: string;\n /** Filename for the downloaded file (without extension) */\n filename?: string;\n /** Export mode: 'master' for stereo mix, 'individual' for single track */\n mode?: 'master' | 'individual';\n /** Track index for individual export */\n trackIndex?: number;\n /** Bit depth: 16 or 32 */\n bitDepth?: 16 | 32;\n /** Whether to apply effects (fades, etc.) - defaults to true */\n applyEffects?: boolean;\n /**\n * Optional Tone.js effects function for master effects. When provided, export will use Tone.Offline\n * to render through the effects chain. The function receives isOffline=true.\n */\n effectsFunction?: EffectsFunction;\n /**\n * Optional function to create offline track effects.\n * Takes a trackId and returns a TrackEffectsFunction for offline rendering.\n */\n createOfflineTrackEffects?: (trackId: string) => TrackEffectsFunction | undefined;\n /** CSS class name */\n className?: string;\n /** Callback when export completes */\n onExportComplete?: (blob: Blob) => void;\n /** Callback when export fails */\n onExportError?: (error: Error) => void;\n}\n\nexport const ExportWavButton: React.FC<ExportWavButtonProps> = ({\n label = 'Export WAV',\n filename = 'export',\n mode = 'master',\n trackIndex,\n bitDepth = 16,\n applyEffects = true,\n effectsFunction,\n createOfflineTrackEffects,\n className,\n onExportComplete,\n onExportError,\n}) => {\n const { tracks, trackStates } = usePlaylistData();\n const { exportWav, isExporting, progress } = useExportWav();\n\n const handleExport = async () => {\n try {\n const result = await exportWav(tracks, trackStates, {\n filename,\n mode,\n trackIndex,\n bitDepth,\n applyEffects,\n effectsFunction,\n createOfflineTrackEffects,\n autoDownload: true,\n });\n onExportComplete?.(result.blob);\n } catch (error) {\n onExportError?.(error instanceof Error ? error : new Error('Export failed'));\n }\n };\n\n const buttonLabel = isExporting ? `Exporting ${Math.round(progress * 100)}%` : label;\n\n return (\n <BaseControlButton\n onClick={handleExport}\n disabled={isExporting || tracks.length === 0}\n className={className}\n >\n {buttonLabel}\n </BaseControlButton>\n );\n};\n","import React, { useRef, useEffect } from 'react';\nimport styled from 'styled-components';\nimport { getContext } from 'tone';\nimport { usePlaybackAnimation, usePlaylistData } from '../WaveformPlaylistContext';\n\nconst PlayheadLine = styled.div.attrs<{ $color: string; $width: number }>((props) => ({\n style: {\n width: `${props.$width}px`,\n background: props.$color,\n },\n}))<{ $color: string; $width: number }>`\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\ninterface AnimatedPlayheadProps {\n color?: string;\n controlsOffset?: number;\n}\n\n/**\n * Animated playhead that updates position via direct DOM manipulation.\n * Calculates time directly from audio context for perfect synchronization.\n * Uses requestAnimationFrame for smooth 60fps animation without React re-renders.\n */\nexport const AnimatedPlayhead: React.FC<AnimatedPlayheadProps> = ({\n color = '#ff0000',\n controlsOffset = 0,\n}) => {\n const playheadRef = useRef<HTMLDivElement>(null);\n const animationFrameRef = useRef<number | null>(null);\n\n const { isPlaying, currentTimeRef, playbackStartTimeRef, audioStartPositionRef } =\n usePlaybackAnimation();\n const { samplesPerPixel, sampleRate, progressBarWidth } = usePlaylistData();\n\n useEffect(() => {\n const updatePosition = () => {\n if (playheadRef.current) {\n // Calculate time directly from audio context for perfect sync\n let time: number;\n if (isPlaying) {\n const elapsed = getContext().currentTime - (playbackStartTimeRef.current ?? 0);\n time = (audioStartPositionRef.current ?? 0) + elapsed;\n } else {\n time = currentTimeRef.current ?? 0;\n }\n const position = (time * sampleRate) / samplesPerPixel + controlsOffset;\n playheadRef.current.style.transform = `translate3d(${position}px, 0, 0)`;\n }\n\n if (isPlaying) {\n animationFrameRef.current = requestAnimationFrame(updatePosition);\n }\n };\n\n if (isPlaying) {\n // Start animation loop\n animationFrameRef.current = requestAnimationFrame(updatePosition);\n } else {\n // When stopped, update once to show final position\n updatePosition();\n }\n\n return () => {\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n animationFrameRef.current = null;\n }\n };\n }, [\n isPlaying,\n sampleRate,\n samplesPerPixel,\n controlsOffset,\n currentTimeRef,\n playbackStartTimeRef,\n audioStartPositionRef,\n ]);\n\n // Also update position when not playing (for seeks, stops, etc.)\n useEffect(() => {\n if (!isPlaying && playheadRef.current) {\n const time = currentTimeRef.current ?? 0;\n const position = (time * sampleRate) / samplesPerPixel + controlsOffset;\n playheadRef.current.style.transform = `translate3d(${position}px, 0, 0)`;\n }\n });\n\n return <PlayheadLine ref={playheadRef} $color={color} $width={progressBarWidth} data-playhead />;\n};\n","import React, { useRef, useEffect } from 'react';\nimport styled from 'styled-components';\nimport { getContext } from 'tone';\nimport {\n SmartChannel,\n type SmartChannelProps,\n useTheme,\n usePlaylistInfo,\n type WaveformPlaylistTheme,\n waveformColorToCss,\n} from '@waveform-playlist/ui-components';\nimport { usePlaybackAnimation, usePlaylistData } from '../WaveformPlaylistContext';\n\nconst ChannelWrapper = styled.div`\n position: relative;\n`;\n\ninterface BackgroundProps {\n readonly $color: string;\n readonly $height: number;\n readonly $top: number;\n readonly $width: number;\n}\n\nconst Background = styled.div.attrs<BackgroundProps>((props) => ({\n style: {\n top: `${props.$top}px`,\n width: `${props.$width}px`,\n height: `${props.$height}px`,\n background: props.$color,\n },\n}))<BackgroundProps>`\n position: absolute;\n left: 0;\n z-index: 0;\n /* Force GPU compositing layer to prevent gradient flickering during scroll */\n transform: translateZ(0);\n backface-visibility: hidden;\n will-change: transform;\n`;\n\ninterface ProgressOverlayProps {\n readonly $color: string;\n readonly $height: number;\n readonly $top: number;\n readonly $width: number;\n}\n\nconst ProgressOverlay = styled.div.attrs<ProgressOverlayProps>((props) => ({\n style: {\n top: `${props.$top}px`,\n height: `${props.$height}px`,\n width: `${props.$width}px`,\n background: props.$color,\n transform: 'scaleX(0)',\n },\n}))<ProgressOverlayProps>`\n position: absolute;\n left: 0;\n pointer-events: none;\n z-index: 1;\n transform-origin: left;\n /* scaleX changes are composite-only (GPU) — no layout reflow per frame */\n will-change: transform;\n`;\n\nconst ChannelContainer = styled.div`\n position: relative;\n z-index: 2;\n`;\n\nexport interface ChannelWithProgressProps extends SmartChannelProps {\n /** Start sample of the clip containing this channel (for progress calculation) */\n clipStartSample: number;\n /** Duration in samples of the clip */\n clipDurationSamples: number;\n}\n\n/**\n * SmartChannel wrapper that adds an animated progress overlay.\n * The progress overlay shows the \"played\" portion of the waveform.\n * Uses requestAnimationFrame for smooth 60fps animation without React re-renders.\n */\nexport const ChannelWithProgress: React.FC<ChannelWithProgressProps> = ({\n clipStartSample,\n clipDurationSamples,\n ...smartChannelProps\n}) => {\n const progressRef = useRef<HTMLDivElement>(null);\n const animationFrameRef = useRef<number | null>(null);\n const theme = useTheme() as WaveformPlaylistTheme;\n const { waveHeight } = usePlaylistInfo();\n\n const { isPlaying, currentTimeRef, playbackStartTimeRef, audioStartPositionRef } =\n usePlaybackAnimation();\n const { samplesPerPixel, sampleRate } = usePlaylistData();\n\n const progressColor = theme?.waveProgressColor || 'rgba(0, 0, 0, 0.1)';\n\n useEffect(() => {\n const updateProgress = () => {\n if (progressRef.current) {\n // Calculate current time from audio context\n let currentTime: number;\n if (isPlaying) {\n const elapsed = getContext().currentTime - (playbackStartTimeRef.current ?? 0);\n currentTime = (audioStartPositionRef.current ?? 0) + elapsed;\n } else {\n currentTime = currentTimeRef.current ?? 0;\n }\n\n // Convert current time to samples\n const currentSample = currentTime * sampleRate;\n\n // Calculate clip bounds in samples\n const clipEndSample = clipStartSample + clipDurationSamples;\n\n // Calculate progress ratio (0 to 1) for scaleX transform\n let ratio = 0;\n\n if (currentSample <= clipStartSample) {\n ratio = 0;\n } else if (currentSample >= clipEndSample) {\n ratio = 1;\n } else {\n const playedSamples = currentSample - clipStartSample;\n ratio = playedSamples / clipDurationSamples;\n }\n\n // scaleX is composite-only — no layout reflow, GPU-accelerated\n progressRef.current.style.transform = `scaleX(${ratio})`;\n }\n\n if (isPlaying) {\n animationFrameRef.current = requestAnimationFrame(updateProgress);\n }\n };\n\n if (isPlaying) {\n animationFrameRef.current = requestAnimationFrame(updateProgress);\n } else {\n // When stopped, update once to show final position\n updateProgress();\n }\n\n return () => {\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n animationFrameRef.current = null;\n }\n };\n }, [\n isPlaying,\n sampleRate,\n samplesPerPixel,\n clipStartSample,\n clipDurationSamples,\n smartChannelProps.length,\n currentTimeRef,\n playbackStartTimeRef,\n audioStartPositionRef,\n ]);\n\n // Also update when not playing (for seeks, stops, etc.)\n useEffect(() => {\n if (!isPlaying && progressRef.current) {\n const currentTime = currentTimeRef.current ?? 0;\n const currentSample = currentTime * sampleRate;\n const clipEndSample = clipStartSample + clipDurationSamples;\n\n let ratio = 0;\n if (currentSample <= clipStartSample) {\n ratio = 0;\n } else if (currentSample >= clipEndSample) {\n ratio = 1;\n } else {\n const playedSamples = currentSample - clipStartSample;\n ratio = playedSamples / clipDurationSamples;\n }\n\n progressRef.current.style.transform = `scaleX(${ratio})`;\n }\n });\n\n // Get the draw mode from theme (defaults to 'inverted')\n const drawMode = theme?.waveformDrawMode || 'inverted';\n\n let backgroundColor;\n if (drawMode === 'inverted') {\n backgroundColor =\n smartChannelProps.isSelected && theme\n ? theme.selectedWaveFillColor\n : theme?.waveFillColor || 'white';\n } else {\n backgroundColor =\n smartChannelProps.isSelected && theme\n ? theme.selectedWaveOutlineColor\n : theme?.waveOutlineColor || 'grey';\n }\n\n // Use black background for spectrogram mode\n const isSpectrogramMode =\n smartChannelProps.renderMode === 'spectrogram' || smartChannelProps.renderMode === 'both';\n const isBothMode = smartChannelProps.renderMode === 'both';\n const backgroundCss = isSpectrogramMode ? '#000' : waveformColorToCss(backgroundColor);\n\n // In \"both\" mode each half (spectrogram + waveform) is waveHeight/2 so the track\n // container stays the same height as a single-mode track.\n const halfHeight = Math.floor(waveHeight / 2);\n const effectiveHeight = waveHeight;\n const effectiveTop = isBothMode\n ? smartChannelProps.index * waveHeight\n : smartChannelProps.index * waveHeight;\n\n // In \"both\" mode, the waveform portion needs its own (non-black) background\n const waveformBackgroundCss = waveformColorToCss(backgroundColor);\n\n return (\n <ChannelWrapper>\n {/* Background layer - color depends on draw mode */}\n {isBothMode ? (\n <>\n {/* Spectrogram portion: black background */}\n <Background\n $color=\"#000\"\n $height={halfHeight}\n $top={effectiveTop}\n $width={smartChannelProps.length}\n />\n {/* Waveform portion: themed background */}\n <Background\n $color={waveformBackgroundCss}\n $height={halfHeight}\n $top={effectiveTop + halfHeight}\n $width={smartChannelProps.length}\n />\n </>\n ) : (\n <Background\n $color={backgroundCss}\n $height={effectiveHeight}\n $top={effectiveTop}\n $width={smartChannelProps.length}\n />\n )}\n {/* Progress overlay - shows played portion with progress color */}\n <ProgressOverlay\n ref={progressRef}\n $color={progressColor}\n $height={effectiveHeight}\n $top={effectiveTop}\n $width={smartChannelProps.length}\n />\n {/* Waveform canvas with transparent background */}\n <ChannelContainer>\n <SmartChannel {...smartChannelProps} transparentBackground />\n </ChannelContainer>\n </ChannelWrapper>\n );\n};\n","import { createContext, useContext } from 'react';\nimport type {\n SpectrogramData,\n SpectrogramConfig,\n ColorMapValue,\n RenderMode,\n TrackSpectrogramOverrides,\n} from '@waveform-playlist/core';\nimport type { TrackMenuItem } from '@waveform-playlist/ui-components';\n\nexport interface SpectrogramIntegration {\n spectrogramDataMap: Map<string, SpectrogramData[]>;\n trackSpectrogramOverrides: Map<string, TrackSpectrogramOverrides>;\n spectrogramWorkerApi: SpectrogramWorkerApi | null;\n spectrogramConfig?: SpectrogramConfig;\n spectrogramColorMap?: ColorMapValue;\n setTrackRenderMode: (trackId: string, mode: RenderMode) => void;\n setTrackSpectrogramConfig: (\n trackId: string,\n config: SpectrogramConfig,\n colorMap?: ColorMapValue\n ) => void;\n registerSpectrogramCanvases: (\n clipId: string,\n channelIndex: number,\n canvasIds: string[],\n canvasWidths: number[]\n ) => void;\n unregisterSpectrogramCanvases: (clipId: string, channelIndex: number) => void;\n /** Render spectrogram menu items for a track's context menu */\n renderMenuItems?: (props: {\n renderMode: string;\n onRenderModeChange: (mode: RenderMode) => void;\n onOpenSettings: () => void;\n onClose?: () => void;\n }) => TrackMenuItem[];\n /** Settings modal component provided by the spectrogram package */\n SettingsModal?: React.ComponentType<{\n open: boolean;\n onClose: () => void;\n config: SpectrogramConfig;\n colorMap: ColorMapValue;\n onApply: (config: SpectrogramConfig, colorMap: ColorMapValue) => void;\n }>;\n /** Get color lookup table for a color map name */\n getColorMap: (name: ColorMapValue) => Uint8Array;\n /** Get frequency scale function for a scale name */\n getFrequencyScale: (name: string) => (f: number, minF: number, maxF: number) => number;\n}\n\n/** Minimal type for the worker API surface used by browser components */\nexport interface SpectrogramWorkerApi {\n registerCanvas: (canvasId: string, canvas: OffscreenCanvas) => void;\n unregisterCanvas: (canvasId: string) => void;\n}\n\nexport const SpectrogramIntegrationContext = createContext<SpectrogramIntegration | null>(null);\n\nexport const SpectrogramIntegrationProvider = SpectrogramIntegrationContext.Provider;\n\n/**\n * Hook to access spectrogram integration provided by @waveform-playlist/spectrogram.\n * Throws if used without <SpectrogramProvider> wrapping the component tree.\n *\n * Follows the Kent C. Dodds pattern:\n * https://kentcdodds.com/blog/how-to-use-react-context-effectively\n */\nexport function useSpectrogramIntegration(): SpectrogramIntegration {\n const context = useContext(SpectrogramIntegrationContext);\n if (!context) {\n throw new Error(\n 'useSpectrogramIntegration must be used within <SpectrogramProvider>. ' +\n 'Install @waveform-playlist/spectrogram and wrap your app with <SpectrogramProvider>.'\n );\n }\n return context;\n}\n","import React, { useContext, useRef, useState, useMemo, type ReactNode, useCallback } from 'react';\nimport { createPortal } from 'react-dom';\nimport { getContext } from 'tone';\nimport {\n Playlist,\n Track as TrackComponent,\n Clip,\n Selection,\n TimescaleLoopRegion,\n PlaylistInfoContext,\n TrackControlsContext,\n DevicePixelRatioProvider,\n SmartScale,\n CloseButton,\n Controls,\n Header,\n Button,\n ButtonGroup,\n Slider,\n SliderWrapper,\n VolumeDownIcon,\n VolumeUpIcon,\n TrackMenu,\n useTheme,\n waveformColorToCss,\n type RenderPlayheadFunction,\n SpectrogramLabels,\n} from '@waveform-playlist/ui-components';\nimport { AnnotationIntegrationContext } from '../AnnotationIntegrationContext';\nimport {\n usePlaybackAnimation,\n usePlaylistState,\n usePlaylistControls,\n usePlaylistData,\n} from '../WaveformPlaylistContext';\nimport type { Peaks } from '@waveform-playlist/core';\nimport { AnimatedPlayhead } from './AnimatedPlayhead';\nimport { ChannelWithProgress } from './ChannelWithProgress';\nimport type { SpectrogramConfig } from '@waveform-playlist/core';\nimport type { AnnotationAction } from '@waveform-playlist/core';\nimport type { AnnotationData, GetAnnotationBoxLabelFn } from '../types/annotations';\nimport { SpectrogramIntegrationContext } from '../SpectrogramIntegrationContext';\n\n// Default duration in seconds for empty tracks (used for recording workflow)\nconst DEFAULT_EMPTY_TRACK_DURATION = 60;\n\nexport interface PlaylistVisualizationProps {\n renderTrackControls?: (trackIndex: number) => ReactNode;\n renderTimestamp?: (timeMs: number, pixelPosition: number) => ReactNode;\n /** Custom playhead render function. Receives position (pixels) and color from theme. */\n renderPlayhead?: RenderPlayheadFunction;\n annotationControls?: AnnotationAction[];\n /**\n * Custom function to generate the label shown on annotation boxes in the waveform.\n * Receives the annotation data and its index, returns a string label.\n * Default: annotation.id\n */\n getAnnotationBoxLabel?: GetAnnotationBoxLabelFn;\n className?: string;\n showClipHeaders?: boolean;\n interactiveClips?: boolean;\n showFades?: boolean;\n /**\n * Enable mobile-optimized touch interactions.\n * When true, increases touch target sizes for clip boundaries.\n * Use with useDragSensors({ touchOptimized: true }) for best results.\n */\n touchOptimized?: boolean;\n /** Callback when a track's close button is clicked. Only renders close button when provided. */\n onRemoveTrack?: (trackIndex: number) => void;\n // Live recording state for real-time waveform preview\n recordingState?: {\n isRecording: boolean;\n trackId: string;\n startSample: number;\n durationSamples: number;\n peaks: Int8Array | Int16Array;\n };\n}\n\n/**\n * Standalone playlist visualization component (WebAudio version).\n *\n * Renders the waveform tracks, timescale, annotations boxes, selection,\n * playhead, loop regions, and track controls — everything that lives\n * inside <Playlist> plus wrapping providers.\n *\n * Does NOT render AnnotationText (the annotation list below the waveform).\n * Pair with PlaylistAnnotationList for a full annotation editing UI.\n */\nexport const PlaylistVisualization: React.FC<PlaylistVisualizationProps> = ({\n renderTrackControls,\n renderTimestamp,\n renderPlayhead,\n annotationControls: _annotationControls,\n getAnnotationBoxLabel,\n className,\n showClipHeaders = false,\n interactiveClips = false,\n showFades = false,\n touchOptimized = false,\n onRemoveTrack,\n recordingState,\n}) => {\n const theme = useTheme() as import('@waveform-playlist/ui-components').WaveformPlaylistTheme;\n\n const { isPlaying, currentTimeRef, playbackStartTimeRef, audioStartPositionRef } =\n usePlaybackAnimation();\n const {\n selectionStart,\n selectionEnd,\n annotations,\n activeAnnotationId,\n annotationsEditable,\n linkEndpoints: _linkEndpoints,\n continuousPlay,\n selectedTrackId,\n loopStart,\n loopEnd,\n isLoopEnabled,\n } = usePlaylistState();\n const annotationIntegration = useContext(AnnotationIntegrationContext);\n const {\n setAnnotations: _setAnnotations,\n setActiveAnnotationId,\n setTrackMute,\n setTrackSolo,\n setTrackVolume,\n setTrackPan,\n setSelection,\n play,\n setScrollContainer,\n setSelectedTrackId,\n setCurrentTime,\n setLoopRegion,\n } = usePlaylistControls();\n const {\n peaksDataArray,\n trackStates,\n tracks,\n duration,\n samplesPerPixel,\n sampleRate,\n waveHeight,\n timeScaleHeight,\n controls,\n playoutRef,\n barWidth,\n barGap,\n isReady,\n } = usePlaylistData();\n\n // Optional spectrogram integration (only available when SpectrogramProvider is present)\n const spectrogram = useContext(SpectrogramIntegrationContext);\n\n // Per-track spectrogram rendering helpers (memoized) — only computed when spectrogram is available\n const perTrackSpectrogramHelpers = useMemo(() => {\n if (!spectrogram)\n return new Map<\n string,\n {\n colorLUT: Uint8Array;\n frequencyScaleFn: (f: number, minF: number, maxF: number) => number;\n config: SpectrogramConfig | undefined;\n }\n >();\n const helpers = new Map<\n string,\n {\n colorLUT: Uint8Array;\n frequencyScaleFn: (f: number, minF: number, maxF: number) => number;\n config: SpectrogramConfig | undefined;\n }\n >();\n tracks.forEach((track) => {\n const mode =\n spectrogram.trackSpectrogramOverrides.get(track.id)?.renderMode ??\n track.renderMode ??\n 'waveform';\n if (mode === 'waveform') return;\n const overrides = spectrogram.trackSpectrogramOverrides.get(track.id);\n const cm =\n overrides?.colorMap ??\n track.spectrogramColorMap ??\n spectrogram.spectrogramColorMap ??\n 'viridis';\n const cfg = overrides?.config ?? track.spectrogramConfig ?? spectrogram.spectrogramConfig;\n helpers.set(track.id, {\n colorLUT: spectrogram.getColorMap(cm),\n frequencyScaleFn: spectrogram.getFrequencyScale(cfg?.frequencyScale ?? 'mel'),\n config: cfg,\n });\n });\n return helpers;\n }, [tracks, spectrogram]);\n\n // Worker canvas API for SpectrogramChannel (stable reference)\n const workerCanvasApi = useMemo(() => {\n if (!spectrogram?.spectrogramWorkerApi) return undefined;\n return {\n registerCanvas: spectrogram.spectrogramWorkerApi.registerCanvas.bind(\n spectrogram.spectrogramWorkerApi\n ),\n unregisterCanvas: spectrogram.spectrogramWorkerApi.unregisterCanvas.bind(\n spectrogram.spectrogramWorkerApi\n ),\n };\n }, [spectrogram?.spectrogramWorkerApi]);\n\n // State for spectrogram settings modal\n const [settingsModalTrackId, setSettingsModalTrackId] = useState<string | null>(null);\n\n const [isSelecting, setIsSelecting] = useState(false);\n\n const scrollContainerRef = useRef<HTMLDivElement | null>(null);\n\n const handleScrollContainerRef = useCallback(\n (element: HTMLDivElement | null) => {\n scrollContainerRef.current = element;\n setScrollContainer(element);\n },\n [setScrollContainer]\n );\n\n // Calculate dimensions — derive duration directly from tracks prop to prevent width\n // shift. The `duration` state is set in an effect and lags tracks by at least one render.\n const tracksMaxDuration = tracks.reduce((max, track) => {\n return track.clips.reduce((clipMax, clip) => {\n const end = (clip.startSample + clip.durationSamples) / clip.sampleRate;\n return Math.max(clipMax, end);\n }, max);\n }, 0);\n let displayDuration =\n tracksMaxDuration > 0\n ? tracksMaxDuration\n : duration > 0\n ? duration\n : DEFAULT_EMPTY_TRACK_DURATION;\n\n if (recordingState?.isRecording) {\n const recordingEndSample = recordingState.startSample + recordingState.durationSamples;\n const recordingEndTime = recordingEndSample / sampleRate;\n displayDuration = Math.max(displayDuration, recordingEndTime + 10);\n }\n\n const tracksFullWidth = Math.floor((displayDuration * sampleRate) / samplesPerPixel);\n\n const handleAnnotationClick = async (annotation: AnnotationData) => {\n setActiveAnnotationId(annotation.id);\n const playDuration = !continuousPlay ? annotation.end - annotation.start : undefined;\n try {\n await play(annotation.start, playDuration);\n } catch (err) {\n console.error(\n 'waveform-playlist: Failed to start playback for annotation',\n annotation.id,\n err\n );\n }\n };\n\n const selectTrack = useCallback(\n (trackIndex: number) => {\n if (trackIndex >= 0 && trackIndex < tracks.length) {\n const track = tracks[trackIndex];\n setSelectedTrackId(track.id);\n }\n },\n [tracks, setSelectedTrackId]\n );\n\n const handleMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {\n const rect = (e.currentTarget as HTMLDivElement).getBoundingClientRect();\n const controlWidth = controls.show ? controls.width : 0;\n const x = e.clientX - rect.left - controlWidth;\n const clickTime = (x * samplesPerPixel) / sampleRate;\n\n const y = e.clientY - rect.top;\n const trackY = y;\n\n let cumulativeHeight = 0;\n let clickedTrackIndex = -1;\n\n for (let i = 0; i < peaksDataArray.length; i++) {\n const trackClipPeaks = peaksDataArray[i];\n const rawCh =\n trackClipPeaks.length > 0\n ? Math.max(1, ...trackClipPeaks.map((clip) => clip.peaks.data.length))\n : 1;\n const trackMode =\n spectrogram?.trackSpectrogramOverrides.get(tracks[i]?.id)?.renderMode ??\n tracks[i]?.renderMode ??\n 'waveform';\n const effectiveCh = trackMode === 'both' ? rawCh * 2 : rawCh;\n const trackHeight = effectiveCh * waveHeight + (showClipHeaders ? 22 : 0);\n\n if (trackY >= cumulativeHeight && trackY < cumulativeHeight + trackHeight) {\n clickedTrackIndex = i;\n break;\n }\n cumulativeHeight += trackHeight;\n }\n\n if (clickedTrackIndex !== -1) {\n selectTrack(clickedTrackIndex);\n }\n\n setIsSelecting(true);\n setCurrentTime(clickTime);\n setSelection(clickTime, clickTime);\n };\n\n const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {\n if (!isSelecting) return;\n\n const rect = (e.currentTarget as HTMLDivElement).getBoundingClientRect();\n const controlWidth = controls.show ? controls.width : 0;\n const x = e.clientX - rect.left - controlWidth;\n const moveTime = (x * samplesPerPixel) / sampleRate;\n\n const start = Math.min(selectionStart, moveTime);\n const end = Math.max(selectionStart, moveTime);\n setSelection(start, end);\n };\n\n const handleMouseUp = (e: React.MouseEvent<HTMLDivElement>) => {\n if (!isSelecting) return;\n\n setIsSelecting(false);\n\n const rect = (e.currentTarget as HTMLDivElement).getBoundingClientRect();\n const controlWidth = controls.show ? controls.width : 0;\n const x = e.clientX - rect.left - controlWidth;\n const endTime = (x * samplesPerPixel) / sampleRate;\n\n const start = Math.min(selectionStart, endTime);\n const end = Math.max(selectionStart, endTime);\n\n if (Math.abs(end - start) < 0.1) {\n setCurrentTime(start);\n\n if (isPlaying && playoutRef.current) {\n playoutRef.current.stop();\n play(start);\n } else if (playoutRef.current) {\n playoutRef.current.stop();\n }\n } else {\n setSelection(start, end);\n }\n };\n\n // Only show loading if we have tracks WITH clips but peaks haven't been computed yet.\n // Don't check audioBuffers — it's set in an effect and can be stale for one or more\n // renders after tracks change, causing the playlist to unmount and remount (layout shift).\n // Placeholder tracks (clips: []) bypass this check intentionally.\n const hasClips = tracks.some((track) => track.clips.length > 0);\n if (hasClips && peaksDataArray.length === 0) {\n return <div className={className}>Loading waveform...</div>;\n }\n\n return (\n <DevicePixelRatioProvider>\n <PlaylistInfoContext.Provider\n value={{\n samplesPerPixel,\n sampleRate,\n zoomLevels: [samplesPerPixel],\n waveHeight,\n timeScaleHeight,\n duration: displayDuration * 1000,\n controls,\n barWidth,\n barGap,\n }}\n >\n <Playlist\n theme={theme}\n backgroundColor={waveformColorToCss(theme.waveOutlineColor)}\n timescaleBackgroundColor={theme.timescaleBackgroundColor}\n scrollContainerWidth={tracksFullWidth + (controls.show ? controls.width : 0)}\n timescaleWidth={tracksFullWidth}\n tracksWidth={tracksFullWidth}\n controlsWidth={controls.show ? controls.width : 0}\n onTracksMouseDown={handleMouseDown}\n onTracksMouseMove={handleMouseMove}\n onTracksMouseUp={handleMouseUp}\n scrollContainerRef={handleScrollContainerRef}\n isSelecting={isSelecting}\n data-playlist-state={isReady ? 'ready' : 'loading'}\n timescale={\n timeScaleHeight > 0 ? (\n <>\n <SmartScale renderTimestamp={renderTimestamp} />\n {isLoopEnabled && (\n <TimescaleLoopRegion\n startPosition={(Math.min(loopStart, loopEnd) * sampleRate) / samplesPerPixel}\n endPosition={(Math.max(loopStart, loopEnd) * sampleRate) / samplesPerPixel}\n markerColor={theme.loopMarkerColor}\n regionColor={theme.loopRegionColor}\n minPosition={0}\n maxPosition={tracksFullWidth}\n controlsOffset={controls.show ? controls.width : 0}\n onLoopRegionChange={(startPixels, endPixels) => {\n const startSeconds = (startPixels * samplesPerPixel) / sampleRate;\n const endSeconds = (endPixels * samplesPerPixel) / sampleRate;\n setLoopRegion(startSeconds, endSeconds);\n }}\n />\n )}\n </>\n ) : undefined\n }\n >\n <>\n {peaksDataArray.map((trackClipPeaks, trackIndex) => {\n const track = tracks[trackIndex];\n if (!track) return null;\n\n const trackState = trackStates[trackIndex] || {\n name: `Track ${trackIndex + 1}`,\n muted: false,\n soloed: false,\n volume: 1.0,\n pan: 0,\n };\n\n const effectiveRenderMode =\n spectrogram?.trackSpectrogramOverrides.get(track.id)?.renderMode ??\n track.renderMode ??\n 'waveform';\n\n const trackControls = renderTrackControls ? (\n renderTrackControls(trackIndex)\n ) : (\n <Controls onClick={() => selectTrack(trackIndex)}>\n <Header style={{ justifyContent: 'center', position: 'relative' }}>\n {onRemoveTrack && (\n <CloseButton\n onClick={(e) => {\n e.stopPropagation();\n onRemoveTrack(trackIndex);\n }}\n />\n )}\n <span\n style={{\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n padding: '0 24px',\n display: 'block',\n }}\n >\n {trackState.name || `Track ${trackIndex + 1}`}\n </span>\n {spectrogram?.renderMenuItems && (\n <span style={{ position: 'absolute', right: 0, top: 0 }}>\n <TrackMenu\n items={(onClose) =>\n spectrogram.renderMenuItems!({\n renderMode: effectiveRenderMode,\n onRenderModeChange: (mode) =>\n spectrogram.setTrackRenderMode(track.id, mode),\n onOpenSettings: () => setSettingsModalTrackId(track.id),\n onClose,\n })\n }\n />\n </span>\n )}\n </Header>\n <ButtonGroup>\n <Button\n $variant={trackState.muted ? 'danger' : 'outline'}\n onClick={() => setTrackMute(trackIndex, !trackState.muted)}\n >\n Mute\n </Button>\n <Button\n $variant={trackState.soloed ? 'info' : 'outline'}\n onClick={() => setTrackSolo(trackIndex, !trackState.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={trackState.volume}\n onChange={(e) => setTrackVolume(trackIndex, 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={trackState.pan}\n onChange={(e) => setTrackPan(trackIndex, parseFloat(e.target.value))}\n />\n <span>R</span>\n </SliderWrapper>\n </Controls>\n );\n\n const maxChannels =\n trackClipPeaks.length > 0\n ? Math.max(1, ...trackClipPeaks.map((clip) => clip.peaks.data.length))\n : 1;\n\n return (\n <TrackControlsContext.Provider key={track.id} value={trackControls}>\n <TrackComponent\n numChannels={maxChannels}\n backgroundColor={waveformColorToCss(theme.waveOutlineColor)}\n offset={0}\n width={tracksFullWidth}\n hasClipHeaders={showClipHeaders}\n trackId={track.id}\n isSelected={track.id === selectedTrackId}\n >\n {effectiveRenderMode !== 'waveform' &&\n (() => {\n const helpers = perTrackSpectrogramHelpers.get(track.id);\n const trackCfg = helpers?.config;\n if (!trackCfg?.labels || !helpers) return null;\n return (\n <SpectrogramLabels\n waveHeight={waveHeight}\n numChannels={maxChannels}\n frequencyScaleFn={helpers.frequencyScaleFn}\n minFrequency={trackCfg.minFrequency ?? 0}\n maxFrequency={trackCfg.maxFrequency ?? sampleRate / 2}\n labelsColor={trackCfg.labelsColor}\n labelsBackground={trackCfg.labelsBackground}\n renderMode={effectiveRenderMode as 'spectrogram' | 'both'}\n hasClipHeaders={showClipHeaders}\n />\n );\n })()}\n {trackClipPeaks.map((clip, clipIndex) => {\n const peaksData = clip.peaks;\n const width = peaksData.length;\n\n return (\n <Clip\n key={clip.clipId}\n clipId={clip.clipId}\n trackIndex={trackIndex}\n clipIndex={clipIndex}\n trackName={clip.trackName}\n startSample={clip.startSample}\n durationSamples={clip.durationSamples}\n samplesPerPixel={samplesPerPixel}\n showHeader={showClipHeaders}\n disableHeaderDrag={!interactiveClips}\n isSelected={track.id === selectedTrackId}\n trackId={track.id}\n fadeIn={clip.fadeIn}\n fadeOut={clip.fadeOut}\n sampleRate={sampleRate}\n showFades={showFades}\n touchOptimized={touchOptimized}\n onMouseDown={(e) => {\n const target = e.target as HTMLElement;\n const isDraggable = target.closest(\n '[role=\"button\"][aria-roledescription=\"draggable\"]'\n );\n if (isDraggable) {\n return;\n }\n selectTrack(trackIndex);\n }}\n >\n {peaksData.data.map((channelPeaks: Peaks, channelIndex: number) => {\n const clipSpectrograms = spectrogram?.spectrogramDataMap.get(\n clip.clipId\n );\n const channelSpectrogram =\n clipSpectrograms?.[channelIndex] ?? clipSpectrograms?.[0];\n const helpers = perTrackSpectrogramHelpers.get(track.id);\n const trackCfg = helpers?.config;\n\n return (\n <ChannelWithProgress\n key={`${clip.clipId}-${channelIndex}`}\n index={channelIndex}\n data={channelPeaks}\n bits={peaksData.bits}\n length={width}\n isSelected={track.id === selectedTrackId}\n clipStartSample={clip.startSample}\n clipDurationSamples={clip.durationSamples}\n renderMode={effectiveRenderMode}\n spectrogramData={channelSpectrogram}\n samplesPerPixel={samplesPerPixel}\n spectrogramColorLUT={helpers?.colorLUT}\n spectrogramFrequencyScaleFn={helpers?.frequencyScaleFn}\n spectrogramMinFrequency={trackCfg?.minFrequency}\n spectrogramMaxFrequency={trackCfg?.maxFrequency}\n spectrogramWorkerApi={workerCanvasApi}\n spectrogramClipId={clip.clipId}\n spectrogramOnCanvasesReady={\n spectrogram\n ? (canvasIds, canvasWidths) => {\n spectrogram.registerSpectrogramCanvases(\n clip.clipId,\n channelIndex,\n canvasIds,\n canvasWidths\n );\n }\n : undefined\n }\n />\n );\n })}\n </Clip>\n );\n })}\n {recordingState?.isRecording &&\n recordingState.trackId === track.id &&\n recordingState.peaks.length > 0 && (\n <Clip\n key={`${track.id}-recording`}\n clipId=\"recording-preview\"\n trackIndex={trackIndex}\n clipIndex={trackClipPeaks.length}\n trackName=\"Recording...\"\n startSample={recordingState.startSample}\n durationSamples={recordingState.durationSamples}\n samplesPerPixel={samplesPerPixel}\n showHeader={showClipHeaders}\n disableHeaderDrag={true}\n isSelected={track.id === selectedTrackId}\n trackId={track.id}\n >\n <ChannelWithProgress\n key={`${track.id}-recording-0`}\n index={0}\n data={recordingState.peaks}\n bits={16}\n length={Math.floor(recordingState.peaks.length / 2)}\n isSelected={track.id === selectedTrackId}\n clipStartSample={recordingState.startSample}\n clipDurationSamples={recordingState.durationSamples}\n />\n </Clip>\n )}\n </TrackComponent>\n </TrackControlsContext.Provider>\n );\n })}\n {annotations.length > 0 && annotationIntegration && (\n <annotationIntegration.AnnotationBoxesWrapper height={30} width={tracksFullWidth}>\n {annotations.map((annotation, index) => {\n const startPosition = (annotation.start * sampleRate) / samplesPerPixel;\n const endPosition = (annotation.end * sampleRate) / samplesPerPixel;\n const label = getAnnotationBoxLabel\n ? getAnnotationBoxLabel(annotation, index)\n : annotation.id;\n return (\n <annotationIntegration.AnnotationBox\n key={annotation.id}\n annotationId={annotation.id}\n annotationIndex={index}\n startPosition={startPosition}\n endPosition={endPosition}\n label={label}\n color=\"#ff9800\"\n isActive={annotation.id === activeAnnotationId}\n onClick={() => handleAnnotationClick(annotation)}\n editable={annotationsEditable}\n />\n );\n })}\n </annotationIntegration.AnnotationBoxesWrapper>\n )}\n {selectionStart !== selectionEnd && (\n <Selection\n startPosition={\n (Math.min(selectionStart, selectionEnd) * sampleRate) / samplesPerPixel +\n (controls.show ? controls.width : 0)\n }\n endPosition={\n (Math.max(selectionStart, selectionEnd) * sampleRate) / samplesPerPixel +\n (controls.show ? controls.width : 0)\n }\n color={theme.selectionColor}\n />\n )}\n {(isPlaying || selectionStart === selectionEnd) &&\n (renderPlayhead ? (\n renderPlayhead({\n position:\n ((currentTimeRef.current ?? 0) * sampleRate) / samplesPerPixel +\n (controls.show ? controls.width : 0),\n color: theme.playheadColor,\n isPlaying,\n currentTimeRef,\n playbackStartTimeRef,\n audioStartPositionRef,\n samplesPerPixel,\n sampleRate,\n controlsOffset: controls.show ? controls.width : 0,\n getAudioContextTime: () => getContext().currentTime,\n })\n ) : (\n <AnimatedPlayhead\n color={theme.playheadColor}\n controlsOffset={controls.show ? controls.width : 0}\n />\n ))}\n </>\n </Playlist>\n </PlaylistInfoContext.Provider>\n {spectrogram?.SettingsModal &&\n typeof document !== 'undefined' &&\n createPortal(\n <spectrogram.SettingsModal\n open={settingsModalTrackId !== null}\n onClose={() => setSettingsModalTrackId(null)}\n config={\n settingsModalTrackId !== null\n ? (spectrogram.trackSpectrogramOverrides.get(settingsModalTrackId)?.config ??\n tracks.find((t) => t.id === settingsModalTrackId)?.spectrogramConfig ??\n spectrogram.spectrogramConfig ??\n {})\n : {}\n }\n colorMap={\n settingsModalTrackId !== null\n ? (spectrogram.trackSpectrogramOverrides.get(settingsModalTrackId)?.colorMap ??\n tracks.find((t) => t.id === settingsModalTrackId)?.spectrogramColorMap ??\n spectrogram.spectrogramColorMap ??\n 'viridis')\n : 'viridis'\n }\n onApply={(newConfig, newColorMap) => {\n if (settingsModalTrackId !== null) {\n spectrogram.setTrackSpectrogramConfig(settingsModalTrackId, newConfig, newColorMap);\n }\n }}\n />,\n document.body\n )}\n </DevicePixelRatioProvider>\n );\n};\n","import React, { useCallback } from 'react';\nimport type {\n AnnotationData,\n AnnotationAction,\n AnnotationActionOptions,\n RenderAnnotationItemProps,\n} from '@waveform-playlist/core';\nimport { useAnnotationIntegration } from '../AnnotationIntegrationContext';\nimport { usePlaylistState, usePlaylistControls } from '../WaveformPlaylistContext';\nimport type { OnAnnotationUpdateFn } from '../types/annotations';\n\nexport type { OnAnnotationUpdateFn } from '../types/annotations';\n\nexport interface PlaylistAnnotationListProps {\n /** Height in pixels for the annotation text list */\n height?: number;\n /**\n * Custom render function for annotation items in the text list.\n * When provided, completely replaces the default annotation item rendering.\n */\n renderAnnotationItem?: (props: RenderAnnotationItemProps) => React.ReactNode;\n /**\n * Callback when annotations are updated (e.g., text edited).\n * Called with the full updated annotations array.\n */\n onAnnotationUpdate?: OnAnnotationUpdateFn;\n /**\n * Action controls to show on each annotation item (e.g., delete, split).\n * Only rendered when `annotationsEditable` is true in context.\n */\n controls?: AnnotationAction[];\n /**\n * Override annotation list config. Falls back to context values\n * `{ linkEndpoints, continuousPlay }` if not provided.\n */\n annotationListConfig?: AnnotationActionOptions;\n /** Where to position the active annotation when auto-scrolling. Defaults to 'center'. */\n scrollActivePosition?: ScrollLogicalPosition;\n /** Which scrollable containers to scroll: 'nearest' or 'all'. Defaults to 'nearest'. */\n scrollActiveContainer?: 'nearest' | 'all';\n}\n\n/**\n * Standalone annotation text list component for WaveformPlaylistProvider (WebAudio).\n *\n * Requires @waveform-playlist/annotations with AnnotationProvider.\n * Throws if used without `<AnnotationProvider>` wrapping the component tree.\n */\nexport const PlaylistAnnotationList: React.FC<PlaylistAnnotationListProps> = ({\n height,\n renderAnnotationItem,\n onAnnotationUpdate,\n controls,\n annotationListConfig,\n scrollActivePosition = 'center',\n scrollActiveContainer = 'nearest',\n}) => {\n const { annotations, activeAnnotationId, annotationsEditable, linkEndpoints, continuousPlay } =\n usePlaylistState();\n const integration = useAnnotationIntegration();\n const { setAnnotations } = usePlaylistControls();\n\n const resolvedConfig = annotationListConfig ?? { linkEndpoints, continuousPlay };\n\n const handleAnnotationUpdate = useCallback(\n (updatedAnnotations: AnnotationData[]) => {\n setAnnotations(updatedAnnotations);\n onAnnotationUpdate?.(updatedAnnotations);\n },\n [setAnnotations, onAnnotationUpdate]\n );\n\n const { AnnotationText } = integration;\n\n return (\n <AnnotationText\n annotations={annotations}\n activeAnnotationId={activeAnnotationId ?? undefined}\n shouldScrollToActive={true}\n scrollActivePosition={scrollActivePosition}\n scrollActiveContainer={scrollActiveContainer}\n editable={annotationsEditable}\n controls={annotationsEditable ? controls : undefined}\n annotationListConfig={resolvedConfig}\n height={height}\n onAnnotationUpdate={handleAnnotationUpdate}\n renderAnnotationItem={renderAnnotationItem}\n />\n );\n};\n","import React, { ReactNode } from 'react';\nimport type { RenderPlayheadFunction } from '@waveform-playlist/ui-components';\nimport type {\n AnnotationAction,\n AnnotationActionOptions,\n RenderAnnotationItemProps,\n} from '@waveform-playlist/core';\nimport { usePlaylistState } from '../WaveformPlaylistContext';\nimport type { GetAnnotationBoxLabelFn } from '../types/annotations';\nimport { PlaylistVisualization } from './PlaylistVisualization';\nimport { PlaylistAnnotationList } from './PlaylistAnnotationList';\n\nexport interface WaveformProps {\n renderTrackControls?: (trackIndex: number) => ReactNode;\n renderTimestamp?: (timeMs: number, pixelPosition: number) => ReactNode;\n /** Custom playhead render function. Receives position (pixels) and color from theme. */\n renderPlayhead?: RenderPlayheadFunction;\n annotationControls?: AnnotationAction[];\n annotationListConfig?: AnnotationActionOptions;\n annotationTextHeight?: number; // Height in pixels for the annotation text list\n /**\n * Custom render function for annotation items in the text list.\n * Use this to completely customize how each annotation is displayed.\n */\n renderAnnotationItem?: (props: RenderAnnotationItemProps) => ReactNode;\n /**\n * Custom function to generate the label shown on annotation boxes in the waveform.\n * Receives the annotation data and its index, returns a string label.\n * Default: annotation.id\n */\n getAnnotationBoxLabel?: GetAnnotationBoxLabelFn;\n /** Where to position the active annotation when auto-scrolling: 'center', 'start', 'end', or 'nearest'. Defaults to 'center'. */\n scrollActivePosition?: ScrollLogicalPosition;\n /** Which scrollable containers to scroll: 'nearest' (only the annotation list) or 'all' (including viewport). Defaults to 'nearest'. */\n scrollActiveContainer?: 'nearest' | 'all';\n className?: string;\n showClipHeaders?: boolean; // Show headers on clips for visual organization\n interactiveClips?: boolean; // Enable dragging/trimming interactions on clips (requires @dnd-kit setup)\n showFades?: boolean; // Show fade in/out overlays on clips\n /**\n * Enable mobile-optimized touch interactions.\n * When true, increases touch target sizes for clip boundaries.\n * Use with useDragSensors({ touchOptimized: true }) for best results.\n */\n touchOptimized?: boolean;\n /** Callback when a track's close button is clicked. Only renders close button when provided. */\n onRemoveTrack?: (trackIndex: number) => void;\n // Live recording state for real-time waveform preview\n recordingState?: {\n isRecording: boolean;\n trackId: string; // Which track is being recorded into\n startSample: number; // Where recording started\n durationSamples: number; // Current recording length\n peaks: Int8Array | Int16Array; // Live peaks data\n };\n}\n\n/**\n * Waveform visualization component that uses the playlist context.\n *\n * Composes PlaylistVisualization (waveform + tracks) and\n * PlaylistAnnotationList (annotation text list below the waveform).\n */\nexport const Waveform: React.FC<WaveformProps> = ({\n renderTrackControls,\n renderTimestamp,\n renderPlayhead,\n annotationControls,\n annotationListConfig,\n annotationTextHeight,\n renderAnnotationItem,\n getAnnotationBoxLabel,\n scrollActivePosition = 'center',\n scrollActiveContainer = 'nearest',\n className,\n showClipHeaders = false,\n interactiveClips = false,\n showFades = false,\n touchOptimized = false,\n onRemoveTrack,\n recordingState,\n}) => {\n const { annotations } = usePlaylistState();\n\n return (\n <>\n <PlaylistVisualization\n renderTrackControls={renderTrackControls}\n renderTimestamp={renderTimestamp}\n renderPlayhead={renderPlayhead}\n annotationControls={annotationControls}\n getAnnotationBoxLabel={getAnnotationBoxLabel}\n className={className}\n showClipHeaders={showClipHeaders}\n interactiveClips={interactiveClips}\n showFades={showFades}\n touchOptimized={touchOptimized}\n onRemoveTrack={onRemoveTrack}\n recordingState={recordingState}\n />\n {annotations.length > 0 && (\n <PlaylistAnnotationList\n height={annotationTextHeight}\n renderAnnotationItem={renderAnnotationItem}\n controls={annotationControls}\n annotationListConfig={annotationListConfig}\n scrollActivePosition={scrollActivePosition}\n scrollActiveContainer={scrollActiveContainer}\n />\n )}\n </>\n );\n};\n","import React, { useRef, useEffect } from 'react';\nimport styled from 'styled-components';\nimport { useMediaElementAnimation, useMediaElementData } from '../MediaElementPlaylistContext';\n\nconst PlayheadLine = styled.div<{ $color: string; $width: number }>`\n position: absolute;\n top: 0;\n left: 0;\n width: ${(props) => props.$width}px;\n background: ${(props) => props.$color};\n height: 100%;\n z-index: 100;\n pointer-events: none;\n will-change: transform;\n`;\n\ninterface AnimatedMediaElementPlayheadProps {\n color?: string;\n controlsOffset?: number;\n}\n\n/**\n * Animated playhead for MediaElementPlaylistProvider.\n * Uses the MediaElement context for time tracking instead of Tone.js audio context.\n * Updates position via direct DOM manipulation for smooth 60fps animation.\n */\nexport const AnimatedMediaElementPlayhead: React.FC<AnimatedMediaElementPlayheadProps> = ({\n color = '#ff0000',\n controlsOffset = 0,\n}) => {\n const playheadRef = useRef<HTMLDivElement>(null);\n const animationFrameRef = useRef<number | null>(null);\n\n const { isPlaying, currentTimeRef } = useMediaElementAnimation();\n const { samplesPerPixel, sampleRate, progressBarWidth } = useMediaElementData();\n\n useEffect(() => {\n const updatePosition = () => {\n if (playheadRef.current) {\n // Get current time from the ref (updated by animation loop in context)\n const time = currentTimeRef.current ?? 0;\n const position = (time * sampleRate) / samplesPerPixel + controlsOffset;\n playheadRef.current.style.transform = `translate3d(${position}px, 0, 0)`;\n }\n\n if (isPlaying) {\n animationFrameRef.current = requestAnimationFrame(updatePosition);\n }\n };\n\n if (isPlaying) {\n // Start animation loop\n animationFrameRef.current = requestAnimationFrame(updatePosition);\n } else {\n // When stopped, update once to show final position\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]);\n\n // Also update position when not playing (for seeks, stops, etc.)\n useEffect(() => {\n if (!isPlaying && playheadRef.current) {\n const time = currentTimeRef.current ?? 0;\n const position = (time * sampleRate) / samplesPerPixel + controlsOffset;\n playheadRef.current.style.transform = `translate3d(${position}px, 0, 0)`;\n }\n });\n\n return <PlayheadLine ref={playheadRef} $color={color} $width={progressBarWidth} data-playhead />;\n};\n","import React, { useRef, useEffect } from 'react';\nimport styled from 'styled-components';\nimport {\n SmartChannel,\n type SmartChannelProps,\n useTheme,\n usePlaylistInfo,\n type WaveformPlaylistTheme,\n waveformColorToCss,\n} from '@waveform-playlist/ui-components';\nimport { useMediaElementAnimation, useMediaElementData } from '../MediaElementPlaylistContext';\n\nconst ChannelWrapper = styled.div`\n position: relative;\n`;\n\ninterface BackgroundProps {\n readonly $color: string;\n readonly $height: number;\n readonly $top: number;\n readonly $width: number;\n}\n\nconst Background = styled.div<BackgroundProps>`\n position: absolute;\n top: ${(props) => props.$top}px;\n left: 0;\n width: ${(props) => props.$width}px;\n height: ${(props) => props.$height}px;\n background: ${(props) => props.$color};\n z-index: 0;\n transform: translateZ(0);\n backface-visibility: hidden;\n will-change: transform;\n`;\n\ninterface ProgressOverlayProps {\n readonly $color: string;\n readonly $height: number;\n readonly $top: number;\n}\n\nconst ProgressOverlay = styled.div<ProgressOverlayProps>`\n position: absolute;\n top: ${(props) => props.$top}px;\n left: 0;\n height: ${(props) => props.$height}px;\n background: ${(props) => props.$color};\n pointer-events: none;\n z-index: 1;\n will-change: width;\n`;\n\nconst ChannelContainer = styled.div`\n position: relative;\n z-index: 2;\n`;\n\nexport interface ChannelWithMediaElementProgressProps\n extends Omit<SmartChannelProps, 'isSelected'> {\n /** Start sample of the clip containing this channel (for progress calculation) */\n clipStartSample: number;\n /** Duration in samples of the clip */\n clipDurationSamples: number;\n}\n\n/**\n * SmartChannel wrapper for MediaElementPlaylistProvider with animated progress overlay.\n * Uses MediaElement context for time tracking instead of Tone.js audio context.\n */\nexport const ChannelWithMediaElementProgress: React.FC<ChannelWithMediaElementProgressProps> = ({\n clipStartSample,\n clipDurationSamples,\n ...smartChannelProps\n}) => {\n const progressRef = useRef<HTMLDivElement>(null);\n const animationFrameRef = useRef<number | null>(null);\n const theme = useTheme() as WaveformPlaylistTheme;\n const { waveHeight } = usePlaylistInfo();\n\n const { isPlaying, currentTimeRef } = useMediaElementAnimation();\n const { samplesPerPixel, sampleRate } = useMediaElementData();\n\n const progressColor = theme?.waveProgressColor || 'rgba(0, 0, 0, 0.1)';\n\n useEffect(() => {\n const updateProgress = () => {\n if (progressRef.current) {\n // Get current time from the ref\n const currentTime = currentTimeRef.current ?? 0;\n\n // Convert current time to samples\n const currentSample = currentTime * sampleRate;\n\n // Calculate clip bounds in samples\n const clipEndSample = clipStartSample + clipDurationSamples;\n\n // Calculate how much of this clip has been played\n let progressWidth = 0;\n\n if (currentSample <= clipStartSample) {\n // Playhead is before this clip - no progress\n progressWidth = 0;\n } else if (currentSample >= clipEndSample) {\n // Playhead is past this clip - full progress\n progressWidth = smartChannelProps.length;\n } else {\n // Playhead is within this clip - partial progress\n const playedSamples = currentSample - clipStartSample;\n progressWidth = Math.floor(playedSamples / samplesPerPixel);\n }\n\n progressRef.current.style.width = `${progressWidth}px`;\n }\n\n if (isPlaying) {\n animationFrameRef.current = requestAnimationFrame(updateProgress);\n }\n };\n\n if (isPlaying) {\n animationFrameRef.current = requestAnimationFrame(updateProgress);\n } else {\n // When stopped, update once to show final position\n updateProgress();\n }\n\n return () => {\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n animationFrameRef.current = null;\n }\n };\n }, [\n isPlaying,\n sampleRate,\n samplesPerPixel,\n clipStartSample,\n clipDurationSamples,\n smartChannelProps.length,\n currentTimeRef,\n ]);\n\n // Also update when not playing (for seeks, stops, etc.)\n useEffect(() => {\n if (!isPlaying && progressRef.current) {\n const currentTime = currentTimeRef.current ?? 0;\n const currentSample = currentTime * sampleRate;\n const clipEndSample = clipStartSample + clipDurationSamples;\n\n let progressWidth = 0;\n if (currentSample <= clipStartSample) {\n progressWidth = 0;\n } else if (currentSample >= clipEndSample) {\n progressWidth = smartChannelProps.length;\n } else {\n const playedSamples = currentSample - clipStartSample;\n progressWidth = Math.floor(playedSamples / samplesPerPixel);\n }\n\n progressRef.current.style.width = `${progressWidth}px`;\n }\n });\n\n // Get the draw mode from theme (defaults to 'inverted')\n const drawMode = theme?.waveformDrawMode || 'inverted';\n\n let backgroundColor;\n if (drawMode === 'inverted') {\n // For MediaElement, always treat as selected (single track)\n backgroundColor = theme?.selectedWaveFillColor || theme?.waveFillColor || 'white';\n } else {\n backgroundColor = theme?.selectedWaveOutlineColor || theme?.waveOutlineColor || 'grey';\n }\n\n const backgroundCss = waveformColorToCss(backgroundColor);\n\n return (\n <ChannelWrapper>\n <Background\n $color={backgroundCss}\n $height={waveHeight}\n $top={smartChannelProps.index * waveHeight}\n $width={smartChannelProps.length}\n />\n <ProgressOverlay\n ref={progressRef}\n $color={progressColor}\n $height={waveHeight}\n $top={smartChannelProps.index * waveHeight}\n />\n <ChannelContainer>\n <SmartChannel {...smartChannelProps} isSelected={true} transparentBackground />\n </ChannelContainer>\n </ChannelWrapper>\n );\n};\n","import React, { useContext, useRef, useState, useCallback } from 'react';\nimport { DndContext } from '@dnd-kit/core';\nimport { restrictToHorizontalAxis } from '@dnd-kit/modifiers';\nimport {\n Playlist,\n Track as TrackComponent,\n Clip,\n Selection,\n PlaylistInfoContext,\n TrackControlsContext,\n DevicePixelRatioProvider,\n SmartScale,\n useTheme,\n waveformColorToCss,\n} from '@waveform-playlist/ui-components';\nimport { AnnotationIntegrationContext } from '../AnnotationIntegrationContext';\nimport type { Peaks } from '@waveform-playlist/core';\nimport {\n useMediaElementAnimation,\n useMediaElementState,\n useMediaElementControls,\n useMediaElementData,\n} from '../MediaElementPlaylistContext';\nimport { useAnnotationDragHandlers } from '../hooks/useAnnotationDragHandlers';\nimport { AnimatedMediaElementPlayhead } from './AnimatedMediaElementPlayhead';\nimport { ChannelWithMediaElementProgress } from './ChannelWithMediaElementProgress';\nimport type {\n AnnotationData,\n GetAnnotationBoxLabelFn,\n OnAnnotationUpdateFn,\n} from '../types/annotations';\n\nexport interface MediaElementPlaylistProps {\n /** Custom function to generate the label shown on annotation boxes */\n getAnnotationBoxLabel?: GetAnnotationBoxLabelFn;\n /** Whether annotation boundaries can be edited by dragging. Defaults to false. */\n editable?: boolean;\n /** Whether dragging one annotation boundary also moves the adjacent annotation's boundary. Defaults to false. */\n linkEndpoints?: boolean;\n /**\n * Callback when annotations are updated (e.g., boundaries dragged).\n * Called with the full updated annotations array.\n */\n onAnnotationUpdate?: OnAnnotationUpdateFn;\n className?: string;\n}\n\n/**\n * Standalone waveform + annotation boxes component for MediaElementPlaylistProvider.\n *\n * Renders the waveform visualization, annotation boxes, selection, and playhead.\n * Does NOT render the annotation text list — use `MediaElementAnnotationList` for that.\n *\n * Must be used inside a `MediaElementPlaylistProvider`.\n *\n * This component can be placed independently in consumer layouts, allowing the\n * waveform and annotation list to be positioned separately (e.g., in different\n * panels or with custom elements between them).\n */\nexport const MediaElementPlaylist: React.FC<MediaElementPlaylistProps> = ({\n getAnnotationBoxLabel,\n editable = false,\n linkEndpoints: linkEndpointsProp = false,\n onAnnotationUpdate,\n className,\n}) => {\n const theme = useTheme() as import('@waveform-playlist/ui-components').WaveformPlaylistTheme;\n\n // MediaElement context hooks\n const { isPlaying } = useMediaElementAnimation();\n const { annotations, activeAnnotationId } = useMediaElementState();\n const annotationIntegration = useContext(AnnotationIntegrationContext);\n const { play, seekTo, setActiveAnnotationId, setAnnotations, setScrollContainer } =\n useMediaElementControls();\n const {\n duration,\n peaksDataArray,\n sampleRate,\n waveHeight,\n timeScaleHeight,\n samplesPerPixel,\n controls,\n playoutRef,\n barWidth,\n barGap,\n } = useMediaElementData();\n\n const [selectionStart, setSelectionStart] = useState(0);\n const [selectionEnd, setSelectionEnd] = useState(0);\n const [isSelecting, setIsSelecting] = useState(false);\n\n // Local ref for scroll container - also register with context for auto-scroll\n const scrollContainerRef = useRef<HTMLDivElement | null>(null);\n\n // Callback to register scroll container with context\n const handleScrollContainerRef = useCallback(\n (el: HTMLDivElement | null) => {\n scrollContainerRef.current = el;\n setScrollContainer(el);\n },\n [setScrollContainer]\n );\n\n // Calculate dimensions\n const tracksFullWidth = Math.floor((duration * sampleRate) / samplesPerPixel);\n\n // Annotation click handler\n const handleAnnotationClick = useCallback(\n async (annotation: AnnotationData) => {\n setActiveAnnotationId(annotation.id);\n try {\n await play(annotation.start);\n } catch (err) {\n console.error(\n 'waveform-playlist: Failed to start playback for annotation',\n annotation.id,\n err\n );\n }\n },\n [setActiveAnnotationId, play]\n );\n\n // Handle annotation boundary updates\n const handleAnnotationUpdate = useCallback(\n (updatedAnnotations: AnnotationData[]) => {\n setAnnotations(updatedAnnotations);\n onAnnotationUpdate?.(updatedAnnotations);\n },\n [setAnnotations, onAnnotationUpdate]\n );\n\n // Drag handlers for annotation boundary editing\n const { onDragStart, onDragMove, onDragEnd } = useAnnotationDragHandlers({\n annotations,\n onAnnotationsChange: handleAnnotationUpdate,\n samplesPerPixel,\n sampleRate,\n duration,\n linkEndpoints: linkEndpointsProp,\n });\n\n // Mouse handlers for click-to-seek\n const handleMouseDown = useCallback(\n (e: React.MouseEvent<HTMLDivElement>) => {\n const rect = (e.currentTarget as HTMLDivElement).getBoundingClientRect();\n const controlWidth = controls.show ? controls.width : 0;\n const x = e.clientX - rect.left - controlWidth;\n const clickTime = (x * samplesPerPixel) / sampleRate;\n\n setIsSelecting(true);\n setSelectionStart(clickTime);\n setSelectionEnd(clickTime);\n },\n [controls, samplesPerPixel, sampleRate]\n );\n\n const handleMouseMove = useCallback(\n (e: React.MouseEvent<HTMLDivElement>) => {\n if (!isSelecting) return;\n\n const rect = (e.currentTarget as HTMLDivElement).getBoundingClientRect();\n const controlWidth = controls.show ? controls.width : 0;\n const x = e.clientX - rect.left - controlWidth;\n const moveTime = (x * samplesPerPixel) / sampleRate;\n\n setSelectionEnd(moveTime);\n },\n [isSelecting, controls, samplesPerPixel, sampleRate]\n );\n\n const handleMouseUp = useCallback(\n (e: React.MouseEvent<HTMLDivElement>) => {\n if (!isSelecting) return;\n\n setIsSelecting(false);\n\n const rect = (e.currentTarget as HTMLDivElement).getBoundingClientRect();\n const controlWidth = controls.show ? controls.width : 0;\n const x = e.clientX - rect.left - controlWidth;\n const endTime = (x * samplesPerPixel) / sampleRate;\n\n const start = Math.min(selectionStart, endTime);\n const end = Math.max(selectionStart, endTime);\n\n // If it's just a click (not a drag), seek to that position\n if (Math.abs(end - start) < 0.1) {\n seekTo(start);\n setSelectionStart(start);\n setSelectionEnd(start);\n\n if (isPlaying && playoutRef.current) {\n playoutRef.current.stop();\n play(start);\n }\n } else {\n // It was a drag - finalize the selection\n setSelectionStart(start);\n setSelectionEnd(end);\n }\n },\n [\n isSelecting,\n selectionStart,\n samplesPerPixel,\n sampleRate,\n controls,\n seekTo,\n isPlaying,\n playoutRef,\n play,\n ]\n );\n\n // Show loading if peaks not ready\n if (peaksDataArray.length === 0) {\n return <div className={className}>Loading waveform...</div>;\n }\n\n // Empty track controls (MediaElement is single-track, no mute/solo needed)\n const emptyControls = null;\n\n return (\n <DevicePixelRatioProvider>\n <PlaylistInfoContext.Provider\n value={{\n samplesPerPixel,\n sampleRate,\n zoomLevels: [samplesPerPixel],\n waveHeight,\n timeScaleHeight,\n duration: duration * 1000,\n controls,\n barWidth,\n barGap,\n }}\n >\n <Playlist\n theme={theme}\n backgroundColor={waveformColorToCss(theme.waveOutlineColor)}\n timescaleBackgroundColor={theme.timescaleBackgroundColor}\n scrollContainerWidth={tracksFullWidth + (controls.show ? controls.width : 0)}\n timescaleWidth={tracksFullWidth}\n tracksWidth={tracksFullWidth}\n controlsWidth={controls.show ? controls.width : 0}\n onTracksMouseDown={handleMouseDown}\n onTracksMouseMove={handleMouseMove}\n onTracksMouseUp={handleMouseUp}\n scrollContainerRef={handleScrollContainerRef}\n isSelecting={isSelecting}\n timescale={timeScaleHeight > 0 ? <SmartScale /> : undefined}\n >\n <>\n {peaksDataArray.map((trackClipPeaks, trackIndex) => {\n // For MediaElement, we have a single track with a single clip\n const maxChannels =\n trackClipPeaks.length > 0\n ? Math.max(...trackClipPeaks.map((clip) => clip.peaks.data.length))\n : 1;\n\n return (\n <TrackControlsContext.Provider key={trackIndex} value={emptyControls}>\n <TrackComponent\n numChannels={maxChannels}\n backgroundColor={waveformColorToCss(theme.waveOutlineColor)}\n offset={0}\n width={tracksFullWidth}\n hasClipHeaders={false}\n trackId={`media-element-track-${trackIndex}`}\n isSelected={true}\n >\n {trackClipPeaks.map((clip, clipIndex) => {\n const peaksData = clip.peaks;\n const width = peaksData.length;\n\n return (\n <Clip\n key={`${trackIndex}-${clipIndex}`}\n clipId={clip.clipId}\n trackIndex={trackIndex}\n clipIndex={clipIndex}\n trackName={clip.trackName}\n startSample={clip.startSample}\n durationSamples={clip.durationSamples}\n samplesPerPixel={samplesPerPixel}\n showHeader={false}\n disableHeaderDrag={true}\n isSelected={true}\n trackId={`media-element-track-${trackIndex}`}\n >\n {peaksData.data.map((channelPeaks: Peaks, channelIndex: number) => (\n <ChannelWithMediaElementProgress\n key={`${trackIndex}-${clipIndex}-${channelIndex}`}\n index={channelIndex}\n data={channelPeaks}\n bits={peaksData.bits}\n length={width}\n clipStartSample={clip.startSample}\n clipDurationSamples={clip.durationSamples}\n />\n ))}\n </Clip>\n );\n })}\n </TrackComponent>\n </TrackControlsContext.Provider>\n );\n })}\n {annotations.length > 0 && annotationIntegration && (\n <DndContext\n onDragStart={onDragStart}\n onDragMove={onDragMove}\n onDragEnd={onDragEnd}\n modifiers={editable ? [restrictToHorizontalAxis] : []}\n >\n <annotationIntegration.AnnotationBoxesWrapper height={30} width={tracksFullWidth}>\n {annotations.map((annotation, index) => {\n const startPosition = (annotation.start * sampleRate) / samplesPerPixel;\n const endPosition = (annotation.end * sampleRate) / samplesPerPixel;\n const label = getAnnotationBoxLabel\n ? getAnnotationBoxLabel(annotation, index)\n : annotation.id;\n return (\n <annotationIntegration.AnnotationBox\n key={annotation.id}\n annotationId={annotation.id}\n annotationIndex={index}\n startPosition={startPosition}\n endPosition={endPosition}\n label={label}\n color=\"#ff9800\"\n isActive={annotation.id === activeAnnotationId}\n onClick={() => handleAnnotationClick(annotation)}\n editable={editable}\n />\n );\n })}\n </annotationIntegration.AnnotationBoxesWrapper>\n </DndContext>\n )}\n {selectionStart !== selectionEnd && (\n <Selection\n startPosition={\n (Math.min(selectionStart, selectionEnd) * sampleRate) / samplesPerPixel +\n (controls.show ? controls.width : 0)\n }\n endPosition={\n (Math.max(selectionStart, selectionEnd) * sampleRate) / samplesPerPixel +\n (controls.show ? controls.width : 0)\n }\n color={theme.selectionColor}\n />\n )}\n <AnimatedMediaElementPlayhead\n color={theme.playheadColor}\n controlsOffset={controls.show ? controls.width : 0}\n />\n </>\n </Playlist>\n </PlaylistInfoContext.Provider>\n </DevicePixelRatioProvider>\n );\n};\n","import React, { useCallback } from 'react';\nimport type {\n AnnotationData,\n AnnotationAction,\n AnnotationActionOptions,\n RenderAnnotationItemProps,\n} from '@waveform-playlist/core';\nimport { useAnnotationIntegration } from '../AnnotationIntegrationContext';\nimport { useMediaElementState, useMediaElementControls } from '../MediaElementPlaylistContext';\nimport type { OnAnnotationUpdateFn } from '../types/annotations';\n\nexport type { OnAnnotationUpdateFn } from '../types/annotations';\n\nexport interface MediaElementAnnotationListProps {\n /** Height in pixels for the annotation text list */\n height?: number;\n /**\n * Custom render function for annotation items in the text list.\n * When provided, completely replaces the default annotation item rendering.\n */\n renderAnnotationItem?: (props: RenderAnnotationItemProps) => React.ReactNode;\n /**\n * Callback when annotations are updated (e.g., text edited).\n * Called with the full updated annotations array.\n */\n onAnnotationUpdate?: OnAnnotationUpdateFn;\n /** Whether annotation text can be edited. Defaults to false. */\n editable?: boolean;\n /**\n * Action controls to show on each annotation item (e.g., delete, split).\n * Only rendered when `editable` is true.\n */\n controls?: AnnotationAction[];\n /**\n * Override annotation list config. Falls back to context values\n * `{ linkEndpoints: false, continuousPlay }` if not provided.\n */\n annotationListConfig?: AnnotationActionOptions;\n /** Where to position the active annotation when auto-scrolling. Defaults to 'center'. */\n scrollActivePosition?: ScrollLogicalPosition;\n /** Which scrollable containers to scroll: 'nearest' or 'all'. Defaults to 'nearest'. */\n scrollActiveContainer?: 'nearest' | 'all';\n}\n\n/**\n * Standalone annotation text list component for MediaElementPlaylistProvider.\n *\n * Requires @waveform-playlist/annotations with AnnotationProvider.\n * Throws if used without `<AnnotationProvider>` wrapping the component tree.\n */\nexport const MediaElementAnnotationList: React.FC<MediaElementAnnotationListProps> = ({\n height,\n renderAnnotationItem,\n onAnnotationUpdate,\n editable = false,\n controls,\n annotationListConfig,\n scrollActivePosition = 'center',\n scrollActiveContainer = 'nearest',\n}) => {\n const { annotations, activeAnnotationId, continuousPlay } = useMediaElementState();\n const integration = useAnnotationIntegration();\n const { setAnnotations } = useMediaElementControls();\n\n const resolvedConfig = annotationListConfig ?? { linkEndpoints: false, continuousPlay };\n\n const handleAnnotationUpdate = useCallback(\n (updatedAnnotations: AnnotationData[]) => {\n setAnnotations(updatedAnnotations);\n onAnnotationUpdate?.(updatedAnnotations);\n },\n [setAnnotations, onAnnotationUpdate]\n );\n\n const { AnnotationText } = integration;\n\n return (\n <AnnotationText\n annotations={annotations}\n activeAnnotationId={activeAnnotationId ?? undefined}\n shouldScrollToActive={true}\n scrollActivePosition={scrollActivePosition}\n scrollActiveContainer={scrollActiveContainer}\n editable={editable}\n controls={editable ? controls : undefined}\n annotationListConfig={resolvedConfig}\n height={height}\n onAnnotationUpdate={handleAnnotationUpdate}\n renderAnnotationItem={renderAnnotationItem}\n />\n );\n};\n","import React from 'react';\nimport type { RenderAnnotationItemProps } from '@waveform-playlist/core';\nimport { useMediaElementState } from '../MediaElementPlaylistContext';\nimport type { GetAnnotationBoxLabelFn, OnAnnotationUpdateFn } from '../types/annotations';\nimport { MediaElementPlaylist } from './MediaElementPlaylist';\nimport { MediaElementAnnotationList } from './MediaElementAnnotationList';\n\n// Re-export annotation types for convenience\nexport type { GetAnnotationBoxLabelFn, OnAnnotationUpdateFn } from '../types/annotations';\n\nexport interface MediaElementWaveformProps {\n /** Height in pixels for the annotation text list */\n annotationTextHeight?: number;\n /** Custom function to generate the label shown on annotation boxes */\n getAnnotationBoxLabel?: GetAnnotationBoxLabelFn;\n /**\n * Custom render function for annotation items in the text list.\n * When provided, completely replaces the default annotation item rendering.\n * Use this to customize the appearance of each annotation (e.g., add furigana).\n */\n renderAnnotationItem?: (props: RenderAnnotationItemProps) => React.ReactNode;\n /** Whether annotation boundaries can be edited by dragging. Defaults to false. */\n editable?: boolean;\n /** Whether dragging one annotation boundary also moves the adjacent annotation's boundary. Defaults to false. */\n linkEndpoints?: boolean;\n /**\n * Callback when annotations are updated (e.g., boundaries dragged).\n * Called with the full updated annotations array.\n */\n onAnnotationUpdate?: OnAnnotationUpdateFn;\n /** Where to position the active annotation when auto-scrolling: 'center', 'start', 'end', or 'nearest'. Defaults to 'center'. */\n scrollActivePosition?: ScrollLogicalPosition;\n /** Which scrollable containers to scroll: 'nearest' (only the annotation list) or 'all' (including viewport). Defaults to 'nearest'. */\n scrollActiveContainer?: 'nearest' | 'all';\n className?: string;\n}\n\n/**\n * Simplified Waveform component for MediaElementPlaylistProvider\n *\n * This is a stripped-down version of Waveform that works with the\n * MediaElement context. It supports:\n * - Single track visualization\n * - Click to seek\n * - Annotation display and click-to-play\n * - Playhead animation\n *\n * For multi-track editing, use the full Waveform with WaveformPlaylistProvider.\n */\nexport const MediaElementWaveform: React.FC<MediaElementWaveformProps> = ({\n annotationTextHeight,\n getAnnotationBoxLabel,\n renderAnnotationItem,\n editable = false,\n linkEndpoints = false,\n onAnnotationUpdate,\n scrollActivePosition = 'center',\n scrollActiveContainer = 'nearest',\n className,\n}) => {\n const { annotations } = useMediaElementState();\n\n return (\n <>\n <MediaElementPlaylist\n getAnnotationBoxLabel={getAnnotationBoxLabel}\n editable={editable}\n linkEndpoints={linkEndpoints}\n onAnnotationUpdate={onAnnotationUpdate}\n className={className}\n />\n {annotations.length > 0 && (\n <MediaElementAnnotationList\n height={annotationTextHeight}\n renderAnnotationItem={renderAnnotationItem}\n onAnnotationUpdate={onAnnotationUpdate}\n editable={editable}\n annotationListConfig={{ linkEndpoints, continuousPlay: false }}\n scrollActivePosition={scrollActivePosition}\n scrollActiveContainer={scrollActiveContainer}\n />\n )}\n </>\n );\n};\n"],"names":["MAX_CANVAS_WIDTH","createClip","options","audioBuffer","startSample","offsetSamples","gain","name","color","fadeIn","fadeOut","waveformData","sampleRate","sourceDurationSamples","durationSamples","generateId","createClipFromSeconds","startTime","offset","sourceDuration","duration","createTrack","clips","muted","soloed","volume","pan","height","spectrogramConfig","spectrogramColorMap","hasWarned","getUnderlyingAudioParam","signal","param","linearCurve","length","curve","scale","i","x","exponentialCurve","index","sCurveCurve","phase","logarithmicCurve","base","generateCurve","type","applyFadeIn","startValue","endValue","scaledCurve","range","applyFadeOut","ToneTrack","Volume","Panner","Gain","destination","getDestination","cleanup","clipInfos","clipInfo","player","Player","fadeGain","clipPlayer","clipStartTime2","clipOffset","audioParam","skipTime","fadeInDuration","remainingFadeDuration","fadeProgress","fadeOutStartInClip","absoluteFadeOutStart","elapsedFadeOut","value","when","newPlayer","playbackPosition","clipStart","clipEnd","currentTime","now","remainingDuration","clipDuration","delay","elapsed","stopWhen","lastClip","callback","TonePlayout","Volume2","getDestination2","track","start","trackOptions","optionsWithDestination","toneTrack","trackId","now2","currentSessionId","trackStartTime","bufferOffset","getTransport","hasSoloedTracks","id","manuallyMuted","time","getContext","globalToneContext","getGlobalContext","Context","setContext","getGlobalAudioContext","CSS","transform","y","scaleX","scaleY","_ref","property","easing","a","e","o","r","p","s","t","c","m","d","l","f","g","w","h","n","styled","props","BaseButton","styled2","BaseCheckboxWrapper","styled3","BaseCheckbox","BaseCheckboxLabel","BaseControlButton","styled4","BaseInput","styled5","BaseLabel","styled6","ScreenReaderOnly","BaseSelect","styled7","BaseSlider","styled8","AutomaticScrollCheckbox","checked","onChange","disabled","className","handleChange","jsxs","jsx2","isWaveformGradient","waveformColorToCss","direction","stops","stop","defaultTheme","ViewportStore","scrollLeft","containerWidth","buffer","visibleStart","visibleEnd","listener","ViewportStoreContext","createContext","EMPTY_SUBSCRIBE","ScrollViewportProvider","containerRef","children","storeRef","useRef","store","rafIdRef","measure","useCallback","el","scheduleUpdate","useEffect","resizeObserver","jsx3","useScrollViewportSelector","selector","useContext","useSyncExternalStore","useVisibleChunkIndices","totalWidth","chunkWidth","originX","visibleChunkKey","viewport","totalChunks","indices","chunkLeft","thisChunkWidth","chunkLeftGlobal","useMemo","ClipViewportOriginContext","createContext2","ClipViewportOriginProvider","jsx4","useClipViewportOrigin","useContext2","useChunkedCanvasRefs","canvasMapRef","useRef2","canvasRef","useCallback2","canvas","idx","useEffect2","map","createCanvasFillStyle","ctx","width","gradient","Waveform","styled9","Wrapper","Channel","data","bits","devicePixelRatio","waveHeight","waveOutlineColor","waveFillColor","barWidth","barGap","transparentBackground","drawMode","clipOriginX","visibleChunkIndices","useLayoutEffect","step","canvasIdx","globalPixelOffset","h2","maxValue","canvasWidth","fillColor","canvasStartGlobal","canvasEndGlobal","firstBarGlobal","barGlobal","peakIndex","minPeak","maxPeak","min","max","waveforms","currentWidth","jsx5","backgroundCss","errorContainerStyle","React4","error","errorInfo","jsx6","CLIP_HEADER_HEIGHT","HeaderContainer","styled10","TrackName","ClipHeaderPresentational","trackName","isSelected","jsx7","ClipHeader","clipId","_trackIndex","_clipIndex","disableDrag","dragHandleProps","attributes","listeners","setActivatorNodeRef","CLIP_BOUNDARY_WIDTH","CLIP_BOUNDARY_WIDTH_TOUCH","BoundaryContainer","styled11","ClipBoundary","edge","touchOptimized","isHovered","setIsHovered","React5","isDragging","jsx8","FadeContainer","styled12","FadeSvg","generateFadePath","curveType","points","numPoints","progress","curvedProgress","FadeOverlay","left","theme","useTheme","jsx9","ClipContainer","styled13","ChannelsWrapper","Clip","trackIndex","clipIndex","samplesPerPixel","showHeader","disableHeaderDrag","isOverlay","onMouseDown","showFades","enableDrag","draggableId","setNodeRef","useDraggable","leftBoundaryId","leftBoundaryAttributes","leftBoundaryListeners","setLeftBoundaryActivatorRef","isLeftBoundaryDragging","rightBoundaryId","rightBoundaryAttributes","rightBoundaryListeners","setRightBoundaryActivatorRef","isRightBoundaryDragging","style","jsxs2","jsx10","Fragment","VolumeContainer","styled14","VolumeLabel","VolumeSlider","MasterVolumeControl","jsxs3","jsx11","styled15","Wrapper2","styled16","ScrollContainer","TimescaleWrapper","TracksContainer","ClickOverlay","Playlist","backgroundColor","timescaleBackgroundColor","timescale","timescaleWidth","tracksWidth","scrollContainerWidth","controlsWidth","onTracksClick","onTracksMouseDown","onTracksMouseMove","onTracksMouseUp","scrollContainerRef","isSelecting","playlistState","wrapperRef","useRef4","handleRef","useCallback3","jsx13","jsxs5","withTheme","SelectionOverlay","styled17","Selection","startPosition","endPosition","jsx14","styled18","DraggableMarkerHandle","TimescaleLoopShade","LoopRegionMarkers","markerColor","regionColor","onLoopStartChange","onLoopEndChange","onLoopRegionMove","minPosition","maxPosition","draggingMarker","setDraggingMarker","useState","dragStartX","useRef5","dragStartPosition","dragStartEnd","handleMarkerMouseDown","useCallback4","marker","handleMouseMove","moveEvent","delta","newPosition","clampedPosition","handleMouseUp","handleRegionMouseDown","regionWidth","newStart","newEnd","jsxs6","Fragment2","jsx15","TimescaleLoopCreator","TimescaleLoopRegion","onLoopRegionChange","controlsOffset","setIsCreating","createStartX","hasLoopRegion","handleBackgroundMouseDown","target","rect","clickX","clampedX","currentX","clampedCurrentX","clockFormat","seconds","decimals","hours","minutes","secs","formatTime","format","parseTime","timeStr","parts","TimeInput","label","readOnly","displayValue","setDisplayValue","useState2","useEffect4","formatted","newDisplayValue","handleBlur","parsedValue","handleKeyDown","jsxs7","Fragment3","jsx16","SelectionTimeInputs","selectionStart","selectionEnd","onSelectionChange","timeFormat","setTimeFormat","useState3","useEffect5","timeFormatSelect","handleFormatChange","handleStartChange","handleEndChange","jsxs8","jsx17","getScale","DevicePixelRatioContext","createContext3","DevicePixelRatioProvider","setScale","useState4","jsx18","useDevicePixelRatio","useContext3","PlaylistInfoContext","createContext4","usePlaylistInfo","useContext4","useTheme2","useContext5","ThemeContext","TrackControlsContext","createContext5","jsx19","Fragment4","useTrackControls","useContext6","defaultProgress","defaultIsPlaying","defaultSelectionStart","defaultSelectionEnd","defaultPlayout","createContext6","LINEAR_FREQUENCY_SCALE","minF","maxF","Wrapper3","styled19","SpectrogramCanvas","defaultGetColorMap","lut","DEFAULT_COLOR_LUT","SpectrogramChannel","channelIndexProp","colorLUT","frequencyScaleFn","minFrequency","maxFrequency","workerApi","onCanvasesReady","channelIndex","registeredIdsRef","useRef6","transferredCanvasesRef","workerApiRef","onCanvasesReadyRef","isWorkerMode","MAX_CANVAS_WIDTH2","scaleFn","hasCustomFrequencyScale","useEffect6","currentWorkerApi","previousCount","remaining","match","chunkIdx","err","newIds","canvasId","offscreen","allIds","allWidths","api","useLayoutEffect2","frequencyBinCount","frameCount","hopSize","gainDb","rawRangeDb","rangeDb","binToFreq","bin","canvasHeight","imgData","pixels","samplePos","frame","frameOffset","normalizedY","lo","hi","mid","freq","db","normalized","colorIdx","pixelIdx","tmpCanvas","tmpCtx","canvases","jsx21","SmartChannel","renderMode","spectrogramData","spectrogramColorLUT","sppProp","spectrogramFrequencyScaleFn","spectrogramMinFrequency","spectrogramMaxFrequency","spectrogramWorkerApi","spectrogramClipId","spectrogramOnCanvasesReady","contextSpp","hasSpectrogram","jsx22","halfHeight","jsxs9","Fragment5","LABELS_WIDTH","LabelsStickyWrapper","styled20","getFrequencyLabels","inRange","maxLabels","result","SpectrogramLabels","numChannels","labelsColor","labelsBackground","hasClipHeaders","useRef7","spectrogramHeight","totalHeight","clipHeaderOffset","useLayoutEffect3","labelFreqs","ch","channelTop","text","metrics","padding","jsx23","secondsToPixels","formatTime2","milliseconds","PlaylistTimeScaleScroll","styled21","TimeTickChunk","TimeStamp","TimeScale","timeColor","bigStep","secondStep","renderTimestamp","timeScaleHeight","showControls","controlWidth","useContext8","widthX","canvasInfo","timeMarkersWithPositions","useMemo2","nextCanvasInfo","nextMarkers","nextWidthX","pixPerSec","counter","pix","timeMs","timestamp","element","jsx24","React16","MAX_CANVAS_WIDTH3","visibleChunks","firstChunkLeft","lastChunkRight","visibleMarkers","useLayoutEffect4","pixLeft","scaleHeight","localX","jsxs10","StyledTimeScale","withTheme2","timeinfo","getScaleInfo","keys","config","resolution","SmartScale","useContext9","jsx25","SelectWrapper","styled22","TIME_FORMAT_OPTIONS","TimeFormatSelect","jsx26","option","Container","styled23","ChannelContainer","ControlsWrapper","Track","onClick","show","controls","jsxs11","jsx27","Button","styled24","ButtonGroup","styled25","StyledCloseButton","styled26","CloseButton","title","jsx28","XIcon","Controls","styled27","Header","styled28","VolumeDownIcon","jsx29","SpeakerLowIcon","VolumeUpIcon","jsx30","SpeakerHighIcon","DotsIcon","jsx32","DotsThreeIcon","Slider","styled29","SliderWrapper","styled30","MenuContainer","styled31","MenuButton","Dropdown","Divider","TrackMenu","itemsProp","open","setOpen","useState6","items","dropdownPos","setDropdownPos","buttonRef","useRef8","dropdownRef","useEffect7","handleClick","jsxs12","jsx33","prev","createPortal","item","React18","WaveformDataChannel","sample","values","INT8_MAX","INT8_MIN","INT16_MAX","INT16_MIN","calculateWaveformDataLength","audio_sample_count","data_length","samples_remaining","generateWaveformData","amplitude_scale","split_channels","sample_rate","channels","channel","output_channels","header_size","bytes_per_sample","total_size","data_view","scale_counter","min_value","max_value","range_min","range_max","_channel","_channel2","_channel3","_channel4","_typeof","isJsonWaveformData","isBinaryWaveformData","isCompatible","view","version","convertJsonToBinary","expected_length","array_buffer","data_object","_i","isNullOrUndefined","decodeBase64","base64","enableUnicode","binaryString","createURL","sourcemapArg","enableUnicodeArg","source","body","blob","createBase64WorkerFactory","url","WorkerFactory","WaveformData","defaultOptions","getOptions","opts","getChannelData","audio_buffer","createFromAudioBuffer","worker","evt","createFromArrayBuffer","audioContext","audioData","errorCallback","promise","WaveformResampler","input_buffer_length_samples","output_buffer_length_samples","output_header_size","count","total","_i2","_i3","resampler","self","otherWaveforms","otherWaveform","combinedBuffer","headerSize","totalSize","totalDataLength","bufferCollection","dataSize","totalBuffer","sourceHeader","totalBufferView","_i4","dataOfTotalBuffer","_i5","_buffer","startIndex","endIndex","output_data","output_dataview","waveform","loadWaveformData","src","response","arrayBuffer","json","waveformDataToPeaks","minArray","maxArray","peaks","loadPeaksFromWaveformData","getWaveformDataMetadata","extractPeaksFromWaveformData","processedData","sourceScale","extractPeaksFromWaveformDataFull","isMono","channelPeaks","len","weight","numPeaks","monoPeaks","useTimeFormat","formatTimeUtil","timeString","parseTimeUtil","DEFAULT_ZOOM_LEVELS","useZoomControls","initialSamplesPerPixel","zoomLevels","zoomIndex","setZoomIndex","canZoomIn","canZoomOut","zoomIn","startTransition","zoomOut","useMasterVolume","playoutRef","initialVolume","onVolumeChange","masterVolume","setMasterVolumeState","setMasterVolume","useMasterAnalyser","fftSize","analyserRef","masterEffects","masterGainNode","_isOffline","analyserNode","Analyser","useAudioTracks","configs","progressive","tracks","setTracks","loading","setLoading","setError","loadedCount","setLoadedCount","totalCount","cancelled","abortController","loadedTracksMap","createTrackFromConfig","clip","Tone","loadPromises","_","loadedTracks","errorMessage","useClipDragHandlers","onTracksChange","originalClipStateRef","React","collisionModifier","args","active","boundary","clipStartTime","timeDelta","newStartTime","sortedClips","b","sortedIndex","previousClip","previousEndTime","nextClip","newEndTime","nextClipStartTime","constrainedX","onDragStart","event","onDragMove","sampleDelta","MIN_DURATION_SAMPLES","originalClip","newTracks","tIdx","newClips","cIdx","audioBufferDurationSamples","constrainedDelta","minDeltaForStart","minDeltaForOffset","minDeltaForPrevious","maxDeltaForMinDuration","newOffsetSamples","newDurationSamples","newStartSample","onDragEnd","previousEndSample","LINK_THRESHOLD","useAnnotationDragHandlers","annotations","onAnnotationsChange","linkEndpoints","originalAnnotationStateRef","annotation","annotationIndex","originalState","newTime","updatedAnnotations","updateAnnotationBoundaries","isDraggingStart","shouldLinkEndpoints","constrainedStart","prevAnnotation","constrainedEnd","nextAnnotation","currentIndex","current","next","nextDelta","useDragSensors","touchDelay","touchTolerance","mouseDistance","mouseSensor","useSensor","MouseSensor","touchSensor","TouchSensor","pointerSensor","PointerSensor","useSensors","useClipSplitting","currentTimeRef","usePlaybackAnimation","selectedTrackId","usePlaylistState","splitClipAt","splitTime","clipEndTime","splitSample","splitPixel","clipEndSample","snappedSplitSample","firstClipStartSample","firstClipDurationSamples","secondClipStartSample","secondClipDurationSamples","offsetIncrement","firstClip","secondClip","useKeyboardShortcuts","shortcuts","enabled","matchingShortcut","shortcut","keyMatch","ctrlMatch","shiftMatch","metaMatch","altMatch","getShortcutLabel","isMac","usePlaybackShortcuts","additionalShortcuts","overrideShortcuts","isPlaying","setCurrentTime","play","pause","usePlaylistControls","usePlaylistData","togglePlayPause","stopPlayback","rewindToStart","activeShortcuts","TIME_DELTA","useAnnotationKeyboardControls","activeAnnotationId","onActiveAnnotationChange","continuousPlay","onPlay","activeIndex","scrollToAnnotation","annotationId","container","startPixel","endPixel","annotationCenter","targetScrollLeft","moveStartBoundary","actualDelta","moveEndBoundary","newNextStart","selectPrevious","selectNext","selectFirst","selectLast","clearSelection","playActiveAnnotation","playDuration","activeAnnotationShortcuts","navigationShortcuts","effectDefinitions","getEffectDefinition","def","getEffectsByCategory","category","effectCategories","effectConstructors","Reverb","Freeverb","JCReverb","FeedbackDelay","PingPongDelay","Chorus","Phaser","Tremolo","Vibrato","AutoPanner","AutoFilter","AutoWah","EQ3","Distortion","BitCrusher","Chebyshev","Compressor","Limiter","Gate","StereoWidener","instanceCounter","generateInstanceId","createEffectInstance","definition","initialParams","Constructor","effect","instanceId","effectRecord","prop","wetProp","createEffectChain","effects","useDynamicEffects","activeEffects","setActiveEffects","activeEffectsRef","effectInstancesRef","graphNodesRef","rebuildChain","nodes","instances","ae","inst","currentNode","addEffect","effectId","params","instance","newActiveEffect","removeEffect","updateParameter","paramName","toggleBypass","newBypassed","originalWet","reorderEffects","fromIndex","toIndex","newEffects","removed","clearAllEffects","effectInstances","createOfflineEffectsFunction","nonBypassedEffects","offlineInstances","activeEffect","useTrackDynamicEffects","trackEffectsState","setTrackEffectsState","trackEffectInstancesRef","trackGraphNodesRef","rebuildTrackChain","trackEffects","graphEnd","instancesMap","addEffectToTrack","newState","existing","removeEffectFromTrack","updateTrackEffectParameter","trackEffectsStateRef","clearTrackEffects","getTrackEffectsFunction","trackEffectInstances","createOfflineTrackEffectsFunction","encodeWav","bitDepth","numSamples","bytesPerSample","blockAlign","byteRate","writeString","channelData","clampedSample","intSample","str","downloadBlob","filename","useExportWav","isExporting","setIsExporting","setProgress","trackStates","mode","autoDownload","applyEffects","effectsFunction","createOfflineTrackEffects","onProgress","totalDurationSamples","tracksToRender","hasSolo","state","hasOfflineTrackEffects","renderedBuffer","renderWithToneEffects","offlineCtx","scheduledClips","totalClips","sum","scheduleClip","currentProgress","exportFilename","message","_trackStates","Offline","ToneAudioBuffer","transport","trackVolume","gainToDb","trackPan","trackMute","clipGain","toneBuffer","fadeInStart","fadeInEnd","fadeOutStart","fadeOutEnd","trackState","gainNode","baseGain","pannerNode","applyFadeEnvelope","gainParam","endTime","fadeType","expStart","expEnd","logCurve","generateFadeCurve","sCurve","curveValue","useAnimationFrameLoop","animationFrameRef","stopAnimationFrameLoop","startAnimationFrameLoop","workerSource","idCounter","createPeaksWorker","pending","terminated","msg","entry","messageId","resolve","reject","useWaveformDataCache","baseScale","cache","setCache","isGenerating","setIsGenerating","workerRef","submittedRef","pendingCountRef","getWorker","submitted","clipsToProcess","submittedThisRun","getSourceName","decodeSource","useDynamicTracks","loadingCount","setLoadingCount","errors","setErrors","cancelledRef","loadingIdsRef","abortControllersRef","controllers","controller","addTracks","sources","placeholders","removeTrack","PlaybackAnimationContext","PlaylistStateContext","PlaylistControlsContext","PlaylistDataContext","WaveformPlaylistProvider","mono","automaticScroll","userTheme","annotationList","onReady","_onAnnotationUpdate","progressBarWidthProp","progressBarWidth","first","annotationsRef","setActiveAnnotationIdState","setIsPlaying","setDuration","audioBuffers","setAudioBuffers","peaksDataArray","setPeaksDataArray","setTrackStates","setSelectionStart","setSelectionEnd","setSelectedTrackId","isAutomaticScroll","setIsAutomaticScroll","setContinuousPlayState","setLinkEndpoints","annotationsEditable","setAnnotationsEditable","isLoopEnabled","setIsLoopEnabledState","loopStart","setLoopStartState","loopEnd","setLoopEndState","isReady","setIsReady","playStartPositionRef","tracksRef","trackStatesRef","playbackStartTimeRef","audioStartPositionRef","playbackEndTimeRef","isAutomaticScrollRef","continuousPlayRef","activeAnnotationIdRef","samplesPerPixelRef","isLoopEnabledRef","selectionStartRef","selectionEndRef","loopStartRef","loopEndRef","zoom","waveformDataCache","setContinuousPlay","setActiveAnnotationId","setLoopEnabled","setLoopRegion","end","setLoopRegionFromSelection","clearLoopRegion","oldSamplesPerPixel","newSamplesPerPixel","centerPixel","sr","newCenterPixel","newScrollLeft","pendingResumeRef","wasPlaying","resumePosition","buffers","maxDuration","prevStates","playout","currentTrackStates","playableClips","trackObj","clipSampleRate","allTrackPeaks","cached","startAnimationLoop","updateTime","currentAnnotations","currentAnnotation","ann","activeAnnotation","pixelPosition","visualPosition","hasValidLoopRegion","timeNow","stopAnimationLoop","currentPos","position","actualStartTime","startTimeNow","pauseTime","seekTo","clampedTime","setTrackMute","newStates","setTrackSolo","setTrackVolume","setTrackPan","setSelection","setScrollContainer","onAnnotationsChangeRef","setAnnotations","action","updated","minimumPlaylistHeight","animationValue","stateValue","setCurrentTimeControl","setAutomaticScrollControl","controlsValue","dataValue","mergedTheme","jsx","ThemeProvider","context","MediaElementTrack","audio","rate","clampedRate","MediaElementPlayout","_when","adjustedDuration","_trackId","_soloed","MediaElementAnimationContext","MediaElementStateContext","MediaElementControlsContext","MediaElementDataContext","MediaElementPlaylistProvider","initialPlaybackRate","playbackRate","setPlaybackRateState","mediaTrack","extractedPeaks","clipPeaks","setPlaybackRate","useMediaElementAnimation","useMediaElementState","useMediaElementControls","useMediaElementData","PlayButton","PauseButton","StopButton","RewindButton","FastForwardButton","SkipBackwardButton","skipAmount","SkipForwardButton","LoopButton","defaultEnd","SetLoopRegionButton","hasValidSelection","ZoomInButton","ZoomOutButton","BaseMasterVolumeControl","BaseTimeFormatSelect","PositionDisplay","AudioPosition","timeRef","BaseSelectionTimeInputs","setAutomaticScroll","BaseAutomaticScrollCheckbox","AnnotationIntegrationContext","AnnotationIntegrationProvider","useAnnotationIntegration","ContinuousPlayCheckbox","Base","LinkEndpointsCheckbox","EditableCheckbox","DownloadAnnotationsButton","ExportWavButton","onExportComplete","onExportError","exportWav","handleExport","buttonLabel","PlayheadLine","AnimatedPlayhead","playheadRef","updatePosition","ChannelWrapper","Background","ProgressOverlay","ChannelWithProgress","clipStartSample","clipDurationSamples","smartChannelProps","progressRef","progressColor","updateProgress","currentSample","ratio","isSpectrogramMode","isBothMode","effectiveHeight","effectiveTop","waveformBackgroundCss","SpectrogramIntegrationContext","SpectrogramIntegrationProvider","useSpectrogramIntegration","DEFAULT_EMPTY_TRACK_DURATION","PlaylistVisualization","renderTrackControls","renderPlayhead","_annotationControls","getAnnotationBoxLabel","showClipHeaders","interactiveClips","onRemoveTrack","recordingState","_linkEndpoints","annotationIntegration","_setAnnotations","spectrogram","perTrackSpectrogramHelpers","helpers","overrides","cm","cfg","workerCanvasApi","settingsModalTrackId","setSettingsModalTrackId","setIsSelecting","handleScrollContainerRef","tracksMaxDuration","clipMax","displayDuration","recordingEndTime","tracksFullWidth","handleAnnotationClick","selectTrack","handleMouseDown","clickTime","trackY","cumulativeHeight","clickedTrackIndex","trackClipPeaks","rawCh","trackHeight","moveTime","startPixels","endPixels","startSeconds","endSeconds","effectiveRenderMode","trackControls","onClose","maxChannels","TrackComponent","trackCfg","peaksData","clipSpectrograms","channelSpectrogram","canvasIds","canvasWidths","newConfig","newColorMap","PlaylistAnnotationList","renderAnnotationItem","onAnnotationUpdate","annotationListConfig","scrollActivePosition","scrollActiveContainer","integration","resolvedConfig","handleAnnotationUpdate","AnnotationText","annotationControls","annotationTextHeight","AnimatedMediaElementPlayhead","ChannelWithMediaElementProgress","progressWidth","playedSamples","MediaElementPlaylist","editable","linkEndpointsProp","emptyControls","DndContext","restrictToHorizontalAxis","MediaElementAnnotationList","MediaElementWaveform"],"mappings":"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","x_google_ignoreList":[2,3,4,5,6,7,8,9,10,11,12,14]}
|