@lucaismyname/ginger 0.0.26 → 0.0.29

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.
Files changed (76) hide show
  1. package/README.md +40 -68
  2. package/dist/analyzer/liveAudioGraph.test.d.ts +2 -0
  3. package/dist/analyzer/liveAudioGraph.test.d.ts.map +1 -0
  4. package/dist/analyzer/useGingerLiveAnalyzer.test.d.ts +2 -0
  5. package/dist/analyzer/useGingerLiveAnalyzer.test.d.ts.map +1 -0
  6. package/dist/audio/GingerPlayer.d.ts.map +1 -1
  7. package/dist/client.cjs +1 -1
  8. package/dist/client.js +37 -28
  9. package/dist/components/controls/Controls.d.ts +1 -1
  10. package/dist/components/controls/Controls.d.ts.map +1 -1
  11. package/dist/components/current/Artwork.d.ts.map +1 -1
  12. package/dist/components/current/Chapters.d.ts.map +1 -1
  13. package/dist/components/current/Lyrics.d.ts +3 -1
  14. package/dist/components/current/Lyrics.d.ts.map +1 -1
  15. package/dist/components/current/LyricsSynced.d.ts.map +1 -1
  16. package/dist/components/current/Playback.d.ts +1 -1
  17. package/dist/components/current/Playback.d.ts.map +1 -1
  18. package/dist/components/current/QueueMeta.d.ts +1 -1
  19. package/dist/components/current/QueueMeta.d.ts.map +1 -1
  20. package/dist/components/current/Time.d.ts +2 -2
  21. package/dist/components/current/Time.d.ts.map +1 -1
  22. package/dist/components/current/Year.d.ts +1 -1
  23. package/dist/components/current/Year.d.ts.map +1 -1
  24. package/dist/components/icons/Pause.d.ts +2 -0
  25. package/dist/components/icons/Pause.d.ts.map +1 -0
  26. package/dist/components/icons/Play.d.ts +2 -0
  27. package/dist/components/icons/Play.d.ts.map +1 -0
  28. package/dist/components/icons/RepeatGlyph.d.ts +5 -0
  29. package/dist/components/icons/RepeatGlyph.d.ts.map +1 -0
  30. package/dist/components/icons/ShuffleIcon.d.ts +2 -0
  31. package/dist/components/icons/ShuffleIcon.d.ts.map +1 -0
  32. package/dist/components/icons/SkipBack.d.ts +2 -0
  33. package/dist/components/icons/SkipBack.d.ts.map +1 -0
  34. package/dist/components/icons/SkipForward.d.ts +2 -0
  35. package/dist/components/icons/SkipForward.d.ts.map +1 -0
  36. package/dist/components/icons/Volume2.d.ts +2 -0
  37. package/dist/components/icons/Volume2.d.ts.map +1 -0
  38. package/dist/components/icons/VolumeX.d.ts +2 -0
  39. package/dist/components/icons/VolumeX.d.ts.map +1 -0
  40. package/dist/components/icons/Wrapper.d.ts +14 -0
  41. package/dist/components/icons/Wrapper.d.ts.map +1 -0
  42. package/dist/components/icons/index.d.ts +11 -0
  43. package/dist/components/icons/index.d.ts.map +1 -0
  44. package/dist/components/queue/QueueDisplay.d.ts.map +1 -1
  45. package/dist/context/GingerLocaleContext.d.ts.map +1 -1
  46. package/dist/context/GingerProvider.d.ts +1 -1
  47. package/dist/context/GingerProvider.d.ts.map +1 -1
  48. package/dist/ginger-B26HM2Ja.cjs +2 -0
  49. package/dist/ginger-B26HM2Ja.cjs.map +1 -0
  50. package/dist/ginger-DlNYfHbV.js +2278 -0
  51. package/dist/ginger-DlNYfHbV.js.map +1 -0
  52. package/dist/ginger.d.ts +12 -0
  53. package/dist/ginger.d.ts.map +1 -1
  54. package/dist/index.cjs +1 -1
  55. package/dist/index.d.ts +2 -0
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +37 -28
  58. package/dist/testing/helpers.d.ts +1 -1
  59. package/dist/testing/helpers.d.ts.map +1 -1
  60. package/dist/testing/index.cjs +1 -1
  61. package/dist/testing/index.cjs.map +1 -1
  62. package/dist/testing/index.js +1 -1
  63. package/dist/testing/index.js.map +1 -1
  64. package/dist/testing/mockWebAudio.d.ts +47 -0
  65. package/dist/testing/mockWebAudio.d.ts.map +1 -0
  66. package/dist/types.d.ts +9 -0
  67. package/dist/types.d.ts.map +1 -1
  68. package/dist/{useNextTrackPrefetch-eR_p__O2.js → useNextTrackPrefetch-Y_fs2JEx.js} +51 -51
  69. package/dist/{useNextTrackPrefetch-eR_p__O2.js.map → useNextTrackPrefetch-Y_fs2JEx.js.map} +1 -1
  70. package/dist/{useNextTrackPrefetch-Cs4A1oFv.cjs → useNextTrackPrefetch-wSILz6TL.cjs} +2 -2
  71. package/dist/{useNextTrackPrefetch-Cs4A1oFv.cjs.map → useNextTrackPrefetch-wSILz6TL.cjs.map} +1 -1
  72. package/package.json +2 -2
  73. package/dist/ginger-DE4TKdCR.js +0 -1877
  74. package/dist/ginger-DE4TKdCR.js.map +0 -1
  75. package/dist/ginger-yY4Y5liv.cjs +0 -2
  76. package/dist/ginger-yY4Y5liv.cjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"useNextTrackPrefetch-Cs4A1oFv.cjs","sources":["../src/hooks/useGinger.ts","../src/analyzer/liveAudioGraph.ts","../src/analyzer/useGingerLiveAnalyzer.ts","../src/hooks/useGingerKeyboardShortcuts.ts","../src/hooks/useGingerSleepTimer.ts","../src/hooks/useGingerDebugLog.ts","../src/hooks/useSeekDrag.ts","../src/hooks/useNextTrackPrefetch.ts"],"sourcesContent":["import { useMemo } from \"react\";\nimport {\n gingerStateFromContextValues,\n useGingerMedia,\n useGingerPlayback,\n} from \"../context/GingerSplitContexts\";\nimport {\n derivePlaybackUiState,\n effectiveDuration,\n effectiveRemaining,\n getCurrentTrack,\n progressFraction,\n resolvedAlbumLine,\n resolvedArtwork,\n} from \"../internal/selectors\";\n\nexport function useGinger() {\n const pb = useGingerPlayback();\n const md = useGingerMedia();\n\n return useMemo(() => {\n const state = gingerStateFromContextValues(pb, md);\n return {\n state,\n currentTrack: getCurrentTrack(state),\n playbackUi: derivePlaybackUiState(state),\n duration: effectiveDuration(state),\n remaining: effectiveRemaining(state),\n progress: progressFraction(state),\n artworkUrl: resolvedArtwork(state),\n albumLine: resolvedAlbumLine(state),\n play: pb.play,\n pause: pb.pause,\n togglePlayPause: pb.togglePlayPause,\n seek: md.seek,\n setVolume: md.setVolume,\n setMuted: md.setMuted,\n toggleMute: md.toggleMute,\n setPlaybackRate: md.setPlaybackRate,\n next: pb.next,\n prev: pb.prev,\n setRepeatMode: pb.setRepeatMode,\n cycleRepeat: pb.cycleRepeat,\n toggleShuffle: pb.toggleShuffle,\n setQueue: pb.setQueue,\n insertTrackAt: pb.insertTrackAt,\n removeTrackAt: pb.removeTrackAt,\n moveTrack: pb.moveTrack,\n enqueueNext: pb.enqueueNext,\n playTrackAt: pb.playTrackAt,\n selectTrackAt: pb.selectTrackAt,\n setPlaylistMeta: pb.setPlaylistMeta,\n setPlaybackMode: pb.setPlaybackMode,\n init: pb.init,\n audioRef: md.audioRef,\n dispatch: pb.dispatch,\n };\n }, [pb, md]);\n}\n","/** One MediaElementAudioSourceNode per HTMLAudioElement; multiple AnalyserNodes may tap the source. */\n\nexport type LiveAnalyserOptions = {\n fftSize: number;\n smoothingTimeConstant: number;\n minDecibels: number;\n maxDecibels: number;\n};\n\ntype Consumer = {\n analyser: AnalyserNode;\n /** This analyser is wired to `audioContext.destination` so the graph is audible. */\n isPlaybackSink: boolean;\n};\n\ntype ElementEntry = {\n context: AudioContext;\n source: MediaElementAudioSourceNode;\n consumers: Map<number, Consumer>;\n nextId: number;\n};\n\nconst entries = new WeakMap<HTMLAudioElement, ElementEntry>();\n\nfunction clampFftSize(n: number): number {\n const p = 2 ** Math.round(Math.log2(n));\n return Math.min(32768, Math.max(32, p));\n}\n\nexport function attachLiveAnalyser(\n element: HTMLAudioElement,\n options: LiveAnalyserOptions,\n): { id: number; context: AudioContext; analyser: AnalyserNode } {\n let entry = entries.get(element);\n if (!entry) {\n const Context =\n window.AudioContext ??\n (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!Context) {\n throw new Error(\"Web Audio API is not available\");\n }\n const context = new Context();\n const source = context.createMediaElementSource(element);\n entry = { context, source, consumers: new Map(), nextId: 0 };\n entries.set(element, entry);\n }\n\n const { context, source } = entry;\n const analyser = context.createAnalyser();\n analyser.fftSize = clampFftSize(options.fftSize);\n analyser.smoothingTimeConstant = options.smoothingTimeConstant;\n analyser.minDecibels = options.minDecibels;\n analyser.maxDecibels = options.maxDecibels;\n\n source.connect(analyser);\n\n const isFirst = entry.consumers.size === 0;\n if (isFirst) {\n analyser.connect(context.destination);\n }\n\n const id = entry.nextId;\n entry.nextId += 1;\n entry.consumers.set(id, { analyser, isPlaybackSink: isFirst });\n\n return { id, context, analyser };\n}\n\nexport function detachLiveAnalyser(element: HTMLAudioElement, id: number): void {\n const entry = entries.get(element);\n if (!entry) return;\n\n const consumer = entry.consumers.get(id);\n if (!consumer) return;\n\n const { analyser, isPlaybackSink } = consumer;\n analyser.disconnect();\n entry.consumers.delete(id);\n\n if (entry.consumers.size === 0) {\n try {\n entry.source.disconnect();\n } catch {\n // ignore\n }\n void entry.context.close();\n entries.delete(element);\n return;\n }\n\n if (isPlaybackSink) {\n const first = entry.consumers.values().next().value as Consumer | undefined;\n if (first) {\n first.analyser.connect(entry.context.destination);\n first.isPlaybackSink = true;\n }\n }\n}\n","import { useCallback, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport { useGinger } from \"../hooks/useGinger\";\nimport { type LiveAnalyserOptions, attachLiveAnalyser, detachLiveAnalyser } from \"./liveAudioGraph\";\n\nexport type UseGingerLiveAnalyzerOptions = {\n /** When false, the analyser is detached and no frames are read. Default true. */\n enabled?: boolean;\n fftSize?: number;\n smoothingTimeConstant?: number;\n minDecibels?: number;\n maxDecibels?: number;\n};\n\nexport type UseGingerLiveAnalyzerResult = {\n /** Byte frequency data (0–255); length equals `frequencyBinCount`. Updated each animation frame while enabled. */\n frequencyData: Uint8Array;\n /** Byte time-domain data (0–255); length equals `fftSize`. */\n timeDomainData: Uint8Array;\n frequencyBinCount: number;\n sampleRate: number;\n isSuspended: boolean;\n error: string | null;\n resume: () => Promise<void>;\n};\n\nconst emptyFreq = new Uint8Array(0);\nconst emptyTime = new Uint8Array(0);\n\nexport function useGingerLiveAnalyzer(\n options: UseGingerLiveAnalyzerOptions = {},\n): UseGingerLiveAnalyzerResult {\n const {\n enabled = true,\n fftSize = 2048,\n smoothingTimeConstant = 0.8,\n minDecibels = -100,\n maxDecibels = -30,\n } = options;\n\n const { audioRef, state } = useGinger();\n const opts = useMemo<LiveAnalyserOptions>(\n () => ({\n fftSize,\n smoothingTimeConstant,\n minDecibels,\n maxDecibels,\n }),\n [fftSize, smoothingTimeConstant, minDecibels, maxDecibels],\n );\n\n const [frame, setFrame] = useState(0);\n const [error, setError] = useState<string | null>(null);\n const [isSuspended, setIsSuspended] = useState(false);\n const [meta, setMeta] = useState({ frequencyBinCount: 0, sampleRate: 0 });\n\n const freqRef = useRef<Uint8Array>(emptyFreq);\n const timeRef = useRef<Uint8Array>(emptyTime);\n\n const resume = useCallback(async () => {\n const ctx = contextHolderRef.current;\n if (ctx && ctx.state === \"suspended\") {\n await ctx.resume();\n }\n }, []);\n\n const contextHolderRef = useRef<AudioContext | null>(null);\n const analyserHolderRef = useRef<AnalyserNode | null>(null);\n\n useLayoutEffect(() => {\n if (!enabled || typeof window === \"undefined\") {\n return;\n }\n\n let cancelled = false;\n let consumerId: number | null = null;\n let element: HTMLAudioElement | null = null;\n let rafId = 0;\n\n const onStateChange = () => {\n const ctx = contextHolderRef.current;\n if (ctx) setIsSuspended(ctx.state === \"suspended\");\n };\n\n const runLoop = () => {\n if (cancelled) return;\n const a = analyserHolderRef.current;\n const fq = freqRef.current;\n const td = timeRef.current;\n if (a && fq.length > 0 && td.length > 0) {\n a.getByteFrequencyData(fq as Uint8Array<ArrayBuffer>);\n a.getByteTimeDomainData(td as Uint8Array<ArrayBuffer>);\n setFrame((n) => n + 1);\n }\n rafId = requestAnimationFrame(runLoop);\n };\n\n type AttachOutcome = \"ok\" | \"no-element\" | \"error\";\n\n const attach = (): AttachOutcome => {\n const el = audioRef.current;\n if (!el || cancelled) return \"no-element\";\n try {\n const { id, context, analyser } = attachLiveAnalyser(el, opts);\n consumerId = id;\n element = el;\n contextHolderRef.current = context;\n analyserHolderRef.current = analyser;\n setIsSuspended(context.state === \"suspended\");\n setError(null);\n\n context.addEventListener(\"statechange\", onStateChange);\n\n const n = analyser.frequencyBinCount;\n const fft = analyser.fftSize;\n freqRef.current = new Uint8Array(n);\n timeRef.current = new Uint8Array(fft);\n setMeta({ frequencyBinCount: n, sampleRate: context.sampleRate });\n\n rafId = requestAnimationFrame(runLoop);\n return \"ok\";\n } catch (e) {\n const msg = e instanceof Error ? e.message : \"Failed to attach live analyser\";\n setError(msg);\n contextHolderRef.current = null;\n analyserHolderRef.current = null;\n freqRef.current = emptyFreq;\n timeRef.current = emptyTime;\n setMeta({ frequencyBinCount: 0, sampleRate: 0 });\n return \"error\";\n }\n };\n\n const first = attach();\n if (first !== \"ok\") {\n let retryRaf = 0;\n const maxAttempts = 120;\n let attempts = 0;\n\n const retryLoop = () => {\n if (cancelled) return;\n const out = attach();\n if (out === \"ok\" || out === \"error\") return;\n attempts += 1;\n if (attempts >= maxAttempts) return;\n retryRaf = requestAnimationFrame(retryLoop);\n };\n\n if (first === \"no-element\") {\n retryRaf = requestAnimationFrame(retryLoop);\n }\n\n return () => {\n cancelled = true;\n cancelAnimationFrame(retryRaf);\n cancelAnimationFrame(rafId);\n if (consumerId != null && element) {\n detachLiveAnalyser(element, consumerId);\n }\n contextHolderRef.current?.removeEventListener(\"statechange\", onStateChange);\n contextHolderRef.current = null;\n analyserHolderRef.current = null;\n freqRef.current = emptyFreq;\n timeRef.current = emptyTime;\n };\n }\n\n return () => {\n cancelled = true;\n cancelAnimationFrame(rafId);\n if (consumerId != null && element) {\n detachLiveAnalyser(element, consumerId);\n }\n contextHolderRef.current?.removeEventListener(\"statechange\", onStateChange);\n contextHolderRef.current = null;\n analyserHolderRef.current = null;\n freqRef.current = emptyFreq;\n timeRef.current = emptyTime;\n setMeta({ frequencyBinCount: 0, sampleRate: 0 });\n };\n }, [enabled, audioRef, opts, state.currentIndex]);\n\n void frame;\n\n return {\n frequencyData: freqRef.current,\n timeDomainData: timeRef.current,\n frequencyBinCount: meta.frequencyBinCount,\n sampleRate: meta.sampleRate,\n isSuspended,\n error,\n resume,\n };\n}\n","import { useEffect } from \"react\";\nimport { useGingerMedia, useGingerPlayback } from \"../context/GingerSplitContexts\";\n\nexport type GingerKeyboardShortcutBindings = {\n playPause?: string;\n next?: string;\n previous?: string;\n mute?: string;\n};\n\nexport function useGingerKeyboardShortcuts(\n enabled = true,\n bindings: GingerKeyboardShortcutBindings = {},\n): void {\n const { togglePlayPause, next, prev } = useGingerPlayback();\n const { toggleMute } = useGingerMedia();\n\n const muteBinding = bindings.mute;\n\n useEffect(() => {\n if (!enabled || typeof window === \"undefined\") return;\n const playPause = (bindings.playPause ?? \" \").toLowerCase();\n const nextKey = (bindings.next ?? \"ArrowRight\").toLowerCase();\n const prevKey = (bindings.previous ?? \"ArrowLeft\").toLowerCase();\n const muteKey = muteBinding?.toLowerCase();\n\n const onKeyDown = (event: KeyboardEvent) => {\n const target = event.target as HTMLElement | null;\n if (\n target &&\n ([\"INPUT\", \"TEXTAREA\", \"SELECT\"].includes(target.tagName) || target.isContentEditable)\n )\n return;\n const key = event.key.toLowerCase();\n if (key === playPause) {\n event.preventDefault();\n togglePlayPause();\n } else if (key === nextKey) {\n event.preventDefault();\n next();\n } else if (key === prevKey) {\n event.preventDefault();\n prev();\n } else if (muteKey && key === muteKey) {\n event.preventDefault();\n toggleMute();\n }\n };\n window.addEventListener(\"keydown\", onKeyDown);\n return () => window.removeEventListener(\"keydown\", onKeyDown);\n }, [\n bindings.next,\n bindings.playPause,\n bindings.previous,\n enabled,\n muteBinding,\n next,\n prev,\n toggleMute,\n togglePlayPause,\n ]);\n}\n","import { useEffect, useRef } from \"react\";\nimport { useGingerPlayback } from \"../context/GingerSplitContexts\";\n\nexport type GingerSleepTimerOptions = {\n durationMs?: number;\n stopAfterTracks?: number;\n respectPause?: boolean;\n enabled?: boolean;\n onFire?: () => void;\n};\n\nexport function useGingerSleepTimer(options: GingerSleepTimerOptions): void {\n const { durationMs, stopAfterTracks, respectPause = true, enabled = true, onFire } = options;\n const { currentIndex, pause, isPaused } = useGingerPlayback();\n const remainingTracksRef = useRef(stopAfterTracks ?? 0);\n const prevIndexRef = useRef(currentIndex);\n\n useEffect(() => {\n remainingTracksRef.current = stopAfterTracks ?? 0;\n }, [stopAfterTracks]);\n\n useEffect(() => {\n if (!enabled || !durationMs || durationMs <= 0) return;\n if (respectPause && isPaused) return;\n const id = setTimeout(() => {\n pause();\n onFire?.();\n }, durationMs);\n return () => clearTimeout(id);\n }, [durationMs, enabled, isPaused, onFire, pause, respectPause]);\n\n useEffect(() => {\n if (!enabled || !stopAfterTracks || stopAfterTracks <= 0) return;\n const prev = prevIndexRef.current;\n prevIndexRef.current = currentIndex;\n if (currentIndex === prev) return;\n remainingTracksRef.current -= 1;\n if (remainingTracksRef.current <= 0) {\n pause();\n onFire?.();\n }\n }, [currentIndex, enabled, onFire, pause, stopAfterTracks]);\n}\n","import { useEffect, useRef } from \"react\";\nimport { useGingerState } from \"../context/GingerSplitContexts\";\n\nexport function useGingerDebugLog(enabled = false): void {\n const state = useGingerState();\n const prevRef = useRef(state);\n\n useEffect(() => {\n if (!enabled || typeof console === \"undefined\") return;\n const prev = prevRef.current;\n if (prev !== state) {\n console.debug(\"[ginger]\", {\n from: {\n currentIndex: prev.currentIndex,\n isPaused: prev.isPaused,\n currentTime: prev.currentTime,\n repeatMode: prev.repeatMode,\n },\n to: {\n currentIndex: state.currentIndex,\n isPaused: state.isPaused,\n currentTime: state.currentTime,\n repeatMode: state.repeatMode,\n },\n });\n }\n prevRef.current = state;\n }, [enabled, state]);\n}\n","import { useCallback, useState } from \"react\";\nimport type { PointerEvent as ReactPointerEvent } from \"react\";\nimport {\n gingerStateFromContextValues,\n useGingerMedia,\n useGingerPlayback,\n} from \"../context/GingerSplitContexts\";\nimport { progressFraction } from \"../internal/selectors\";\n\nexport type SeekDragState = {\n /** Raw drag fraction — only updated during an active drag gesture. */\n fraction: number;\n /** Blended fraction: follows live playback when idle, drag position when dragging. */\n displayFraction: number;\n isDragging: boolean;\n onPointerDown: (event: ReactPointerEvent<HTMLElement>) => void;\n};\n\nfunction clamp01(value: number): number {\n return Math.max(0, Math.min(1, value));\n}\n\nexport function useSeekDrag(duration: number): SeekDragState {\n const media = useGingerMedia();\n const playback = useGingerPlayback();\n const { seek } = media;\n const [fraction, setFraction] = useState(0);\n const [isDragging, setIsDragging] = useState(false);\n\n const liveFraction = progressFraction(gingerStateFromContextValues(playback, media));\n const displayFraction = isDragging ? fraction : liveFraction;\n\n const onPointerDown = useCallback(\n (event: ReactPointerEvent<HTMLElement>) => {\n if (!(duration > 0)) return;\n const target = event.currentTarget;\n const rect = target.getBoundingClientRect();\n const update = (clientX: number) => {\n const ratio = clamp01((clientX - rect.left) / rect.width);\n setFraction(ratio);\n seek(ratio * duration);\n };\n setIsDragging(true);\n target.setPointerCapture(event.pointerId);\n update(event.clientX);\n const onMove = (moveEvent: PointerEvent) => update(moveEvent.clientX);\n const onUp = (upEvent: PointerEvent) => {\n update(upEvent.clientX);\n setIsDragging(false);\n target.releasePointerCapture(event.pointerId);\n target.removeEventListener(\"pointermove\", onMove);\n target.removeEventListener(\"pointerup\", onUp);\n target.removeEventListener(\"pointercancel\", onUp);\n };\n target.addEventListener(\"pointermove\", onMove);\n target.addEventListener(\"pointerup\", onUp);\n target.addEventListener(\"pointercancel\", onUp);\n },\n [duration, seek],\n );\n\n return { fraction, displayFraction, isDragging, onPointerDown };\n}\n","import { useEffect } from \"react\";\nimport { useGingerPlayback } from \"../context/GingerSplitContexts\";\nimport { computeNextIndex } from \"../core/transitions\";\n\nexport type UseNextTrackPrefetchOptions = {\n /** When false, no prefetch runs. Default true. */\n enabled?: boolean;\n /**\n * Match `crossOrigin` on `Ginger.Player` when `fileUrl` is cross-origin so the browser\n * can reuse cached media consistently.\n */\n crossOrigin?: \"\" | \"anonymous\" | \"use-credentials\" | undefined;\n};\n\n/**\n * Warms the browser cache for the **logical** next track (same rules as the Next control:\n * `computeNextIndex` from queue, repeat, and playback mode) using a detached `HTMLAudioElement`\n * with `preload=\"auto\"`. Safe to call alongside `Ginger.Player`; it does not replace main playback.\n */\nexport function useNextTrackPrefetch(options: UseNextTrackPrefetchOptions = {}): void {\n const { enabled = true, crossOrigin } = options;\n const { tracks, currentIndex, repeatMode, playbackMode } = useGingerPlayback();\n\n useEffect(() => {\n if (!enabled || typeof document === \"undefined\") return;\n const nextIndex = computeNextIndex({ tracks, currentIndex, repeatMode, playbackMode });\n if (nextIndex === currentIndex) return;\n const nextUrl = tracks[nextIndex]?.fileUrl ?? \"\";\n if (!nextUrl) return;\n\n const audio = document.createElement(\"audio\");\n audio.preload = \"auto\";\n if (crossOrigin) audio.crossOrigin = crossOrigin;\n audio.src = nextUrl;\n audio.load();\n\n return () => {\n audio.removeAttribute(\"src\");\n audio.load();\n };\n }, [enabled, crossOrigin, tracks, currentIndex, repeatMode, playbackMode]);\n}\n"],"names":["useGinger","pb","useGingerPlayback","md","useGingerMedia","useMemo","state","gingerStateFromContextValues","getCurrentTrack","derivePlaybackUiState","effectiveDuration","effectiveRemaining","progressFraction","resolvedArtwork","resolvedAlbumLine","entries","clampFftSize","n","p","attachLiveAnalyser","element","options","entry","Context","context","source","analyser","isFirst","id","detachLiveAnalyser","consumer","isPlaybackSink","first","emptyFreq","emptyTime","useGingerLiveAnalyzer","enabled","fftSize","smoothingTimeConstant","minDecibels","maxDecibels","audioRef","opts","frame","setFrame","useState","error","setError","isSuspended","setIsSuspended","meta","setMeta","freqRef","useRef","timeRef","resume","useCallback","ctx","contextHolderRef","analyserHolderRef","useLayoutEffect","cancelled","consumerId","rafId","onStateChange","runLoop","a","fq","td","attach","el","fft","e","msg","retryRaf","maxAttempts","attempts","retryLoop","out","_a","useGingerKeyboardShortcuts","bindings","togglePlayPause","next","prev","toggleMute","muteBinding","useEffect","playPause","nextKey","prevKey","muteKey","onKeyDown","event","target","key","useGingerSleepTimer","durationMs","stopAfterTracks","respectPause","onFire","currentIndex","pause","isPaused","remainingTracksRef","prevIndexRef","useGingerDebugLog","useGingerState","prevRef","clamp01","value","useSeekDrag","duration","media","playback","seek","fraction","setFraction","isDragging","setIsDragging","liveFraction","displayFraction","onPointerDown","rect","update","clientX","ratio","onMove","moveEvent","onUp","upEvent","useNextTrackPrefetch","crossOrigin","tracks","repeatMode","playbackMode","nextIndex","computeNextIndex","nextUrl","audio"],"mappings":"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"}
1
+ {"version":3,"file":"useNextTrackPrefetch-wSILz6TL.cjs","sources":["../src/hooks/useGinger.ts","../src/analyzer/liveAudioGraph.ts","../src/analyzer/useGingerLiveAnalyzer.ts","../src/hooks/useGingerKeyboardShortcuts.ts","../src/hooks/useGingerSleepTimer.ts","../src/hooks/useGingerDebugLog.ts","../src/hooks/useSeekDrag.ts","../src/hooks/useNextTrackPrefetch.ts"],"sourcesContent":["import { useMemo } from \"react\";\nimport {\n gingerStateFromContextValues,\n useGingerMedia,\n useGingerPlayback,\n} from \"../context/GingerSplitContexts\";\nimport {\n derivePlaybackUiState,\n effectiveDuration,\n effectiveRemaining,\n getCurrentTrack,\n progressFraction,\n resolvedAlbumLine,\n resolvedArtwork,\n} from \"../internal/selectors\";\n\nexport function useGinger() {\n const pb = useGingerPlayback();\n const md = useGingerMedia();\n\n return useMemo(() => {\n const state = gingerStateFromContextValues(pb, md);\n return {\n state,\n currentTrack: getCurrentTrack(state),\n playbackUi: derivePlaybackUiState(state),\n duration: effectiveDuration(state),\n remaining: effectiveRemaining(state),\n progress: progressFraction(state),\n artworkUrl: resolvedArtwork(state),\n albumLine: resolvedAlbumLine(state),\n play: pb.play,\n pause: pb.pause,\n togglePlayPause: pb.togglePlayPause,\n seek: md.seek,\n setVolume: md.setVolume,\n setMuted: md.setMuted,\n toggleMute: md.toggleMute,\n setPlaybackRate: md.setPlaybackRate,\n next: pb.next,\n prev: pb.prev,\n setRepeatMode: pb.setRepeatMode,\n cycleRepeat: pb.cycleRepeat,\n toggleShuffle: pb.toggleShuffle,\n setQueue: pb.setQueue,\n insertTrackAt: pb.insertTrackAt,\n removeTrackAt: pb.removeTrackAt,\n moveTrack: pb.moveTrack,\n enqueueNext: pb.enqueueNext,\n playTrackAt: pb.playTrackAt,\n selectTrackAt: pb.selectTrackAt,\n setPlaylistMeta: pb.setPlaylistMeta,\n setPlaybackMode: pb.setPlaybackMode,\n init: pb.init,\n audioRef: md.audioRef,\n dispatch: pb.dispatch,\n };\n }, [pb, md]);\n}\n","/** One MediaElementAudioSourceNode per HTMLAudioElement; multiple AnalyserNodes may tap the source. */\n\nexport type LiveAnalyserOptions = {\n fftSize: number;\n smoothingTimeConstant: number;\n minDecibels: number;\n maxDecibels: number;\n};\n\ntype Consumer = {\n analyser: AnalyserNode;\n /** This analyser is wired to `audioContext.destination` so the graph is audible. */\n isPlaybackSink: boolean;\n};\n\ntype ElementEntry = {\n context: AudioContext;\n source: MediaElementAudioSourceNode;\n consumers: Map<number, Consumer>;\n nextId: number;\n};\n\nconst entries = new WeakMap<HTMLAudioElement, ElementEntry>();\n\nfunction clampFftSize(n: number): number {\n const p = 2 ** Math.round(Math.log2(n));\n return Math.min(32768, Math.max(32, p));\n}\n\nexport function attachLiveAnalyser(\n element: HTMLAudioElement,\n options: LiveAnalyserOptions,\n): { id: number; context: AudioContext; analyser: AnalyserNode } {\n let entry = entries.get(element);\n if (!entry) {\n const Context =\n window.AudioContext ??\n (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!Context) {\n throw new Error(\"Web Audio API is not available\");\n }\n const context = new Context();\n const source = context.createMediaElementSource(element);\n entry = { context, source, consumers: new Map(), nextId: 0 };\n entries.set(element, entry);\n }\n\n const { context, source } = entry;\n const analyser = context.createAnalyser();\n analyser.fftSize = clampFftSize(options.fftSize);\n analyser.smoothingTimeConstant = options.smoothingTimeConstant;\n analyser.minDecibels = options.minDecibels;\n analyser.maxDecibels = options.maxDecibels;\n\n source.connect(analyser);\n\n const isFirst = entry.consumers.size === 0;\n if (isFirst) {\n analyser.connect(context.destination);\n }\n\n const id = entry.nextId;\n entry.nextId += 1;\n entry.consumers.set(id, { analyser, isPlaybackSink: isFirst });\n\n return { id, context, analyser };\n}\n\nexport function detachLiveAnalyser(element: HTMLAudioElement, id: number): void {\n const entry = entries.get(element);\n if (!entry) return;\n\n const consumer = entry.consumers.get(id);\n if (!consumer) return;\n\n const { analyser, isPlaybackSink } = consumer;\n analyser.disconnect();\n entry.consumers.delete(id);\n\n if (entry.consumers.size === 0) {\n try {\n entry.source.disconnect();\n } catch {\n // ignore\n }\n void entry.context.close();\n entries.delete(element);\n return;\n }\n\n if (isPlaybackSink) {\n const first = entry.consumers.values().next().value as Consumer | undefined;\n if (first) {\n first.analyser.connect(entry.context.destination);\n first.isPlaybackSink = true;\n }\n }\n}\n","import { useCallback, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport { useGinger } from \"../hooks/useGinger\";\nimport { type LiveAnalyserOptions, attachLiveAnalyser, detachLiveAnalyser } from \"./liveAudioGraph\";\n\nexport type UseGingerLiveAnalyzerOptions = {\n /** When false, the analyser is detached and no frames are read. Default true. */\n enabled?: boolean;\n fftSize?: number;\n smoothingTimeConstant?: number;\n minDecibels?: number;\n maxDecibels?: number;\n};\n\nexport type UseGingerLiveAnalyzerResult = {\n /** Byte frequency data (0–255); length equals `frequencyBinCount`. Updated each animation frame while enabled. */\n frequencyData: Uint8Array;\n /** Byte time-domain data (0–255); length equals `fftSize`. */\n timeDomainData: Uint8Array;\n frequencyBinCount: number;\n sampleRate: number;\n isSuspended: boolean;\n error: string | null;\n resume: () => Promise<void>;\n};\n\nconst emptyFreq = new Uint8Array(0);\nconst emptyTime = new Uint8Array(0);\n\nexport function useGingerLiveAnalyzer(\n options: UseGingerLiveAnalyzerOptions = {},\n): UseGingerLiveAnalyzerResult {\n const {\n enabled = true,\n fftSize = 2048,\n smoothingTimeConstant = 0.8,\n minDecibels = -100,\n maxDecibels = -30,\n } = options;\n\n const { audioRef, state } = useGinger();\n const opts = useMemo<LiveAnalyserOptions>(\n () => ({\n fftSize,\n smoothingTimeConstant,\n minDecibels,\n maxDecibels,\n }),\n [fftSize, smoothingTimeConstant, minDecibels, maxDecibels],\n );\n\n const [frame, setFrame] = useState(0);\n const [error, setError] = useState<string | null>(null);\n const [isSuspended, setIsSuspended] = useState(false);\n const [meta, setMeta] = useState({ frequencyBinCount: 0, sampleRate: 0 });\n\n const freqRef = useRef<Uint8Array>(emptyFreq);\n const timeRef = useRef<Uint8Array>(emptyTime);\n\n const resume = useCallback(async () => {\n const ctx = contextHolderRef.current;\n if (ctx && ctx.state === \"suspended\") {\n await ctx.resume();\n }\n }, []);\n\n const contextHolderRef = useRef<AudioContext | null>(null);\n const analyserHolderRef = useRef<AnalyserNode | null>(null);\n\n useLayoutEffect(() => {\n if (!enabled || typeof window === \"undefined\") {\n return;\n }\n\n let cancelled = false;\n let consumerId: number | null = null;\n let element: HTMLAudioElement | null = null;\n let rafId = 0;\n\n const onStateChange = () => {\n const ctx = contextHolderRef.current;\n if (ctx) setIsSuspended(ctx.state === \"suspended\");\n };\n\n const runLoop = () => {\n if (cancelled) return;\n const a = analyserHolderRef.current;\n const fq = freqRef.current;\n const td = timeRef.current;\n if (a && fq.length > 0 && td.length > 0) {\n a.getByteFrequencyData(fq as Uint8Array<ArrayBuffer>);\n a.getByteTimeDomainData(td as Uint8Array<ArrayBuffer>);\n setFrame((n) => n + 1);\n }\n rafId = requestAnimationFrame(runLoop);\n };\n\n type AttachOutcome = \"ok\" | \"no-element\" | \"error\";\n\n const attach = (): AttachOutcome => {\n const el = audioRef.current;\n if (!el || cancelled) return \"no-element\";\n try {\n const { id, context, analyser } = attachLiveAnalyser(el, opts);\n consumerId = id;\n element = el;\n contextHolderRef.current = context;\n analyserHolderRef.current = analyser;\n setIsSuspended(context.state === \"suspended\");\n setError(null);\n\n context.addEventListener(\"statechange\", onStateChange);\n\n const n = analyser.frequencyBinCount;\n const fft = analyser.fftSize;\n freqRef.current = new Uint8Array(n);\n timeRef.current = new Uint8Array(fft);\n setMeta({ frequencyBinCount: n, sampleRate: context.sampleRate });\n\n rafId = requestAnimationFrame(runLoop);\n return \"ok\";\n } catch (e) {\n const msg = e instanceof Error ? e.message : \"Failed to attach live analyser\";\n setError(msg);\n contextHolderRef.current = null;\n analyserHolderRef.current = null;\n freqRef.current = emptyFreq;\n timeRef.current = emptyTime;\n setMeta({ frequencyBinCount: 0, sampleRate: 0 });\n return \"error\";\n }\n };\n\n const first = attach();\n if (first !== \"ok\") {\n let retryRaf = 0;\n const maxAttempts = 120;\n let attempts = 0;\n\n const retryLoop = () => {\n if (cancelled) return;\n const out = attach();\n if (out === \"ok\" || out === \"error\") return;\n attempts += 1;\n if (attempts >= maxAttempts) return;\n retryRaf = requestAnimationFrame(retryLoop);\n };\n\n if (first === \"no-element\") {\n retryRaf = requestAnimationFrame(retryLoop);\n }\n\n return () => {\n cancelled = true;\n cancelAnimationFrame(retryRaf);\n cancelAnimationFrame(rafId);\n if (consumerId != null && element) {\n detachLiveAnalyser(element, consumerId);\n }\n contextHolderRef.current?.removeEventListener(\"statechange\", onStateChange);\n contextHolderRef.current = null;\n analyserHolderRef.current = null;\n freqRef.current = emptyFreq;\n timeRef.current = emptyTime;\n };\n }\n\n return () => {\n cancelled = true;\n cancelAnimationFrame(rafId);\n if (consumerId != null && element) {\n detachLiveAnalyser(element, consumerId);\n }\n contextHolderRef.current?.removeEventListener(\"statechange\", onStateChange);\n contextHolderRef.current = null;\n analyserHolderRef.current = null;\n freqRef.current = emptyFreq;\n timeRef.current = emptyTime;\n setMeta({ frequencyBinCount: 0, sampleRate: 0 });\n };\n }, [enabled, audioRef, opts, state.currentIndex]);\n\n void frame;\n\n return {\n frequencyData: freqRef.current,\n timeDomainData: timeRef.current,\n frequencyBinCount: meta.frequencyBinCount,\n sampleRate: meta.sampleRate,\n isSuspended,\n error,\n resume,\n };\n}\n","import { useEffect } from \"react\";\nimport { useGingerMedia, useGingerPlayback } from \"../context/GingerSplitContexts\";\n\nexport type GingerKeyboardShortcutBindings = {\n playPause?: string;\n next?: string;\n previous?: string;\n mute?: string;\n};\n\nexport function useGingerKeyboardShortcuts(\n enabled = true,\n bindings: GingerKeyboardShortcutBindings = {},\n): void {\n const { togglePlayPause, next, prev } = useGingerPlayback();\n const { toggleMute } = useGingerMedia();\n\n const muteBinding = bindings.mute;\n\n useEffect(() => {\n if (!enabled || typeof window === \"undefined\") return;\n const playPause = (bindings.playPause ?? \" \").toLowerCase();\n const nextKey = (bindings.next ?? \"ArrowRight\").toLowerCase();\n const prevKey = (bindings.previous ?? \"ArrowLeft\").toLowerCase();\n const muteKey = muteBinding?.toLowerCase();\n\n const onKeyDown = (event: KeyboardEvent) => {\n const target = event.target as HTMLElement | null;\n if (\n target &&\n ([\"INPUT\", \"TEXTAREA\", \"SELECT\"].includes(target.tagName) || target.isContentEditable)\n )\n return;\n const key = event.key.toLowerCase();\n if (key === playPause) {\n event.preventDefault();\n togglePlayPause();\n } else if (key === nextKey) {\n event.preventDefault();\n next();\n } else if (key === prevKey) {\n event.preventDefault();\n prev();\n } else if (muteKey && key === muteKey) {\n event.preventDefault();\n toggleMute();\n }\n };\n window.addEventListener(\"keydown\", onKeyDown);\n return () => window.removeEventListener(\"keydown\", onKeyDown);\n }, [\n bindings.next,\n bindings.playPause,\n bindings.previous,\n enabled,\n muteBinding,\n next,\n prev,\n toggleMute,\n togglePlayPause,\n ]);\n}\n","import { useEffect, useRef } from \"react\";\nimport { useGingerPlayback } from \"../context/GingerSplitContexts\";\n\nexport type GingerSleepTimerOptions = {\n durationMs?: number;\n stopAfterTracks?: number;\n respectPause?: boolean;\n enabled?: boolean;\n onFire?: () => void;\n};\n\nexport function useGingerSleepTimer(options: GingerSleepTimerOptions): void {\n const { durationMs, stopAfterTracks, respectPause = true, enabled = true, onFire } = options;\n const { currentIndex, pause, isPaused } = useGingerPlayback();\n const remainingTracksRef = useRef(stopAfterTracks ?? 0);\n const prevIndexRef = useRef(currentIndex);\n\n useEffect(() => {\n remainingTracksRef.current = stopAfterTracks ?? 0;\n }, [stopAfterTracks]);\n\n useEffect(() => {\n if (!enabled || !durationMs || durationMs <= 0) return;\n if (respectPause && isPaused) return;\n const id = setTimeout(() => {\n pause();\n onFire?.();\n }, durationMs);\n return () => clearTimeout(id);\n }, [durationMs, enabled, isPaused, onFire, pause, respectPause]);\n\n useEffect(() => {\n if (!enabled || !stopAfterTracks || stopAfterTracks <= 0) return;\n const prev = prevIndexRef.current;\n prevIndexRef.current = currentIndex;\n if (currentIndex === prev) return;\n remainingTracksRef.current -= 1;\n if (remainingTracksRef.current <= 0) {\n pause();\n onFire?.();\n }\n }, [currentIndex, enabled, onFire, pause, stopAfterTracks]);\n}\n","import { useEffect, useRef } from \"react\";\nimport { useGingerState } from \"../context/GingerSplitContexts\";\n\nexport function useGingerDebugLog(enabled = false): void {\n const state = useGingerState();\n const prevRef = useRef(state);\n\n useEffect(() => {\n if (!enabled || typeof console === \"undefined\") return;\n const prev = prevRef.current;\n if (prev !== state) {\n console.debug(\"[ginger]\", {\n from: {\n currentIndex: prev.currentIndex,\n isPaused: prev.isPaused,\n currentTime: prev.currentTime,\n repeatMode: prev.repeatMode,\n },\n to: {\n currentIndex: state.currentIndex,\n isPaused: state.isPaused,\n currentTime: state.currentTime,\n repeatMode: state.repeatMode,\n },\n });\n }\n prevRef.current = state;\n }, [enabled, state]);\n}\n","import { useCallback, useState } from \"react\";\nimport type { PointerEvent as ReactPointerEvent } from \"react\";\nimport {\n gingerStateFromContextValues,\n useGingerMedia,\n useGingerPlayback,\n} from \"../context/GingerSplitContexts\";\nimport { progressFraction } from \"../internal/selectors\";\n\nexport type SeekDragState = {\n /** Raw drag fraction — only updated during an active drag gesture. */\n fraction: number;\n /** Blended fraction: follows live playback when idle, drag position when dragging. */\n displayFraction: number;\n isDragging: boolean;\n onPointerDown: (event: ReactPointerEvent<HTMLElement>) => void;\n};\n\nfunction clamp01(value: number): number {\n return Math.max(0, Math.min(1, value));\n}\n\nexport function useSeekDrag(duration: number): SeekDragState {\n const media = useGingerMedia();\n const playback = useGingerPlayback();\n const { seek } = media;\n const [fraction, setFraction] = useState(0);\n const [isDragging, setIsDragging] = useState(false);\n\n const liveFraction = progressFraction(gingerStateFromContextValues(playback, media));\n const displayFraction = isDragging ? fraction : liveFraction;\n\n const onPointerDown = useCallback(\n (event: ReactPointerEvent<HTMLElement>) => {\n if (!(duration > 0)) return;\n const target = event.currentTarget;\n const rect = target.getBoundingClientRect();\n const update = (clientX: number) => {\n const ratio = clamp01((clientX - rect.left) / rect.width);\n setFraction(ratio);\n seek(ratio * duration);\n };\n setIsDragging(true);\n target.setPointerCapture(event.pointerId);\n update(event.clientX);\n const onMove = (moveEvent: PointerEvent) => update(moveEvent.clientX);\n const onUp = (upEvent: PointerEvent) => {\n update(upEvent.clientX);\n setIsDragging(false);\n target.releasePointerCapture(event.pointerId);\n target.removeEventListener(\"pointermove\", onMove);\n target.removeEventListener(\"pointerup\", onUp);\n target.removeEventListener(\"pointercancel\", onUp);\n };\n target.addEventListener(\"pointermove\", onMove);\n target.addEventListener(\"pointerup\", onUp);\n target.addEventListener(\"pointercancel\", onUp);\n },\n [duration, seek],\n );\n\n return { fraction, displayFraction, isDragging, onPointerDown };\n}\n","import { useEffect } from \"react\";\nimport { useGingerPlayback } from \"../context/GingerSplitContexts\";\nimport { computeNextIndex } from \"../core/transitions\";\n\nexport type UseNextTrackPrefetchOptions = {\n /** When false, no prefetch runs. Default true. */\n enabled?: boolean;\n /**\n * Match `crossOrigin` on `Ginger.Player` when `fileUrl` is cross-origin so the browser\n * can reuse cached media consistently.\n */\n crossOrigin?: \"\" | \"anonymous\" | \"use-credentials\" | undefined;\n};\n\n/**\n * Warms the browser cache for the **logical** next track (same rules as the Next control:\n * `computeNextIndex` from queue, repeat, and playback mode) using a detached `HTMLAudioElement`\n * with `preload=\"auto\"`. Safe to call alongside `Ginger.Player`; it does not replace main playback.\n */\nexport function useNextTrackPrefetch(options: UseNextTrackPrefetchOptions = {}): void {\n const { enabled = true, crossOrigin } = options;\n const { tracks, currentIndex, repeatMode, playbackMode } = useGingerPlayback();\n\n useEffect(() => {\n if (!enabled || typeof document === \"undefined\") return;\n const nextIndex = computeNextIndex({ tracks, currentIndex, repeatMode, playbackMode });\n if (nextIndex === currentIndex) return;\n const nextUrl = tracks[nextIndex]?.fileUrl ?? \"\";\n if (!nextUrl) return;\n\n const audio = document.createElement(\"audio\");\n audio.preload = \"auto\";\n if (crossOrigin) audio.crossOrigin = crossOrigin;\n audio.src = nextUrl;\n audio.load();\n\n return () => {\n audio.removeAttribute(\"src\");\n audio.load();\n };\n }, [enabled, crossOrigin, tracks, currentIndex, repeatMode, playbackMode]);\n}\n"],"names":["useGinger","pb","useGingerPlayback","md","useGingerMedia","useMemo","state","gingerStateFromContextValues","getCurrentTrack","derivePlaybackUiState","effectiveDuration","effectiveRemaining","progressFraction","resolvedArtwork","resolvedAlbumLine","entries","clampFftSize","n","p","attachLiveAnalyser","element","options","entry","Context","context","source","analyser","isFirst","id","detachLiveAnalyser","consumer","isPlaybackSink","first","emptyFreq","emptyTime","useGingerLiveAnalyzer","enabled","fftSize","smoothingTimeConstant","minDecibels","maxDecibels","audioRef","opts","frame","setFrame","useState","error","setError","isSuspended","setIsSuspended","meta","setMeta","freqRef","useRef","timeRef","resume","useCallback","ctx","contextHolderRef","analyserHolderRef","useLayoutEffect","cancelled","consumerId","rafId","onStateChange","runLoop","a","fq","td","attach","el","fft","e","msg","retryRaf","maxAttempts","attempts","retryLoop","out","_a","useGingerKeyboardShortcuts","bindings","togglePlayPause","next","prev","toggleMute","muteBinding","useEffect","playPause","nextKey","prevKey","muteKey","onKeyDown","event","target","key","useGingerSleepTimer","durationMs","stopAfterTracks","respectPause","onFire","currentIndex","pause","isPaused","remainingTracksRef","prevIndexRef","useGingerDebugLog","useGingerState","prevRef","clamp01","value","useSeekDrag","duration","media","playback","seek","fraction","setFraction","isDragging","setIsDragging","liveFraction","displayFraction","onPointerDown","rect","update","clientX","ratio","onMove","moveEvent","onUp","upEvent","useNextTrackPrefetch","crossOrigin","tracks","repeatMode","playbackMode","nextIndex","computeNextIndex","nextUrl","audio"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lucaismyname/ginger",
3
- "version": "0.0.26",
4
- "description": "A headless React audio player",
3
+ "version": "0.0.29",
4
+ "description": "A Headless & batteries-included React audio-player primitive",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "files": [