@lucaismyname/ginger 0.0.10 → 0.0.12

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 (59) hide show
  1. package/README.md +42 -6
  2. package/dist/client.cjs +1 -1
  3. package/dist/client.js +2 -2
  4. package/dist/components/controls/Controls.d.ts +12 -8
  5. package/dist/components/controls/Controls.d.ts.map +1 -1
  6. package/dist/components/controls/Controls.test.d.ts +2 -0
  7. package/dist/components/controls/Controls.test.d.ts.map +1 -0
  8. package/dist/components/current/Artwork.d.ts +3 -1
  9. package/dist/components/current/Artwork.d.ts.map +1 -1
  10. package/dist/components/current/Time.d.ts +6 -2
  11. package/dist/components/current/Time.d.ts.map +1 -1
  12. package/dist/components/current/texts.test.d.ts +2 -0
  13. package/dist/components/current/texts.test.d.ts.map +1 -0
  14. package/dist/components/playlist/GingerPlaylist.d.ts +6 -2
  15. package/dist/components/playlist/GingerPlaylist.d.ts.map +1 -1
  16. package/dist/components/queue/QueueDisplay.d.ts +3 -1
  17. package/dist/components/queue/QueueDisplay.d.ts.map +1 -1
  18. package/dist/context/GingerProvider.d.ts +1 -1
  19. package/dist/context/GingerProvider.d.ts.map +1 -1
  20. package/dist/context/GingerProvider.test.d.ts +2 -0
  21. package/dist/context/GingerProvider.test.d.ts.map +1 -0
  22. package/dist/core/playbackReducer.d.ts.map +1 -1
  23. package/dist/ginger-DYoHDn8T.cjs +2 -0
  24. package/dist/ginger-DYoHDn8T.cjs.map +1 -0
  25. package/dist/ginger-Dntdd6zH.js +1599 -0
  26. package/dist/ginger-Dntdd6zH.js.map +1 -0
  27. package/dist/hooks/useSeekDrag.d.ts +3 -0
  28. package/dist/hooks/useSeekDrag.d.ts.map +1 -1
  29. package/dist/index.cjs +1 -1
  30. package/dist/index.js +2 -2
  31. package/dist/internal/formatTime.test.d.ts +2 -0
  32. package/dist/internal/formatTime.test.d.ts.map +1 -0
  33. package/dist/internal/selectors.test.d.ts +2 -0
  34. package/dist/internal/selectors.test.d.ts.map +1 -0
  35. package/dist/media/useMediaSession.d.ts.map +1 -1
  36. package/dist/testing/index.cjs +1 -1
  37. package/dist/testing/index.js +1 -1
  38. package/dist/testing/setup.d.ts +2 -0
  39. package/dist/testing/setup.d.ts.map +1 -0
  40. package/dist/types.d.ts +2 -0
  41. package/dist/types.d.ts.map +1 -1
  42. package/dist/useSeekDrag-DBzoym0-.cjs +2 -0
  43. package/dist/useSeekDrag-DBzoym0-.cjs.map +1 -0
  44. package/dist/useSeekDrag-jLsYA-uG.js +174 -0
  45. package/dist/useSeekDrag-jLsYA-uG.js.map +1 -0
  46. package/dist/waveform/index.cjs +1 -1
  47. package/dist/waveform/index.cjs.map +1 -1
  48. package/dist/waveform/index.js +15 -13
  49. package/dist/waveform/index.js.map +1 -1
  50. package/dist/waveform/useAudioPeaks.d.ts.map +1 -1
  51. package/package.json +2 -1
  52. package/dist/ginger-B5wfhrC8.cjs +0 -2
  53. package/dist/ginger-B5wfhrC8.cjs.map +0 -1
  54. package/dist/ginger-Cg2cLsA6.js +0 -1547
  55. package/dist/ginger-Cg2cLsA6.js.map +0 -1
  56. package/dist/useSeekDrag-B0l3F1fL.js +0 -174
  57. package/dist/useSeekDrag-B0l3F1fL.js.map +0 -1
  58. package/dist/useSeekDrag-DmVvk2Pc.cjs +0 -2
  59. package/dist/useSeekDrag-DmVvk2Pc.cjs.map +0 -1
@@ -0,0 +1,174 @@
1
+ import { useMemo as v, useEffect as k, useRef as h, useState as w, useCallback as b } from "react";
2
+ import { b as g, u as T, g as A, c as R } from "./GingerSplitContexts-4YZ-OJ9V.js";
3
+ import { r as E, h as C, p as S, i as I, j as D, b as N, k as G } from "./ginger-Dntdd6zH.js";
4
+ function q() {
5
+ const e = g(), t = T();
6
+ return v(
7
+ () => {
8
+ const r = A(e, t);
9
+ return {
10
+ state: r,
11
+ currentTrack: G(r),
12
+ playbackUi: N(r),
13
+ duration: D(r),
14
+ remaining: I(r),
15
+ progress: S(r),
16
+ artworkUrl: C(r),
17
+ albumLine: E(r),
18
+ play: e.play,
19
+ pause: e.pause,
20
+ togglePlayPause: e.togglePlayPause,
21
+ seek: t.seek,
22
+ setVolume: t.setVolume,
23
+ setMuted: t.setMuted,
24
+ toggleMute: t.toggleMute,
25
+ setPlaybackRate: t.setPlaybackRate,
26
+ next: e.next,
27
+ prev: e.prev,
28
+ setRepeatMode: e.setRepeatMode,
29
+ cycleRepeat: e.cycleRepeat,
30
+ toggleShuffle: e.toggleShuffle,
31
+ setQueue: e.setQueue,
32
+ insertTrackAt: e.insertTrackAt,
33
+ removeTrackAt: e.removeTrackAt,
34
+ moveTrack: e.moveTrack,
35
+ enqueueNext: e.enqueueNext,
36
+ playTrackAt: e.playTrackAt,
37
+ selectTrackAt: e.selectTrackAt,
38
+ setPlaylistMeta: e.setPlaylistMeta,
39
+ init: e.init,
40
+ audioRef: t.audioRef,
41
+ dispatch: e.dispatch
42
+ };
43
+ },
44
+ [e, t]
45
+ );
46
+ }
47
+ function Q(e = !0, t = {}) {
48
+ const { togglePlayPause: r, next: s, prev: o } = g(), { toggleMute: a } = T(), n = t.mute;
49
+ k(() => {
50
+ if (!e || typeof window > "u") return;
51
+ const c = (t.playPause ?? " ").toLowerCase(), u = (t.next ?? "ArrowRight").toLowerCase(), l = (t.previous ?? "ArrowLeft").toLowerCase(), d = n == null ? void 0 : n.toLowerCase(), f = (i) => {
52
+ const m = i.target;
53
+ if (m && (["INPUT", "TEXTAREA", "SELECT"].includes(m.tagName) || m.isContentEditable)) return;
54
+ const p = i.key.toLowerCase();
55
+ p === c ? (i.preventDefault(), r()) : p === u ? (i.preventDefault(), s()) : p === l ? (i.preventDefault(), o()) : d && p === d && (i.preventDefault(), a());
56
+ };
57
+ return window.addEventListener("keydown", f), () => window.removeEventListener("keydown", f);
58
+ }, [t.next, t.playPause, t.previous, e, n, s, o, a, r]);
59
+ }
60
+ function j() {
61
+ const { tracks: e, currentIndex: t } = g(), { currentTime: r, seek: s } = T(), o = v(() => {
62
+ var c;
63
+ return [...((c = e[t]) == null ? void 0 : c.chapters) ?? []].filter((u) => u && Number.isFinite(u.startSeconds) && u.startSeconds >= 0).sort((u, l) => u.startSeconds - l.startSeconds);
64
+ }, [t, e]), a = v(() => {
65
+ if (o.length === 0) return -1;
66
+ for (let n = o.length - 1; n >= 0; n -= 1)
67
+ if (r >= o[n].startSeconds) return n;
68
+ return -1;
69
+ }, [r, o]);
70
+ return {
71
+ list: o,
72
+ activeIndex: a,
73
+ active: a >= 0 ? o[a] ?? null : null,
74
+ seekTo: (n) => {
75
+ const c = o[n];
76
+ c && s(c.startSeconds);
77
+ }
78
+ };
79
+ }
80
+ const M = /\[(\d{1,2}):(\d{2})(?:\.(\d{1,3}))?\]/g;
81
+ function F(e) {
82
+ const t = [];
83
+ for (const r of e.split(/\r?\n/)) {
84
+ const s = [...r.matchAll(M)];
85
+ if (s.length === 0) continue;
86
+ const o = r.replace(M, "").trim();
87
+ for (const a of s) {
88
+ const n = Number(a[1] ?? 0), c = Number(a[2] ?? 0), u = Number((a[3] ?? "0").padEnd(3, "0")), l = n * 60 + c + u / 1e3;
89
+ Number.isFinite(l) && l >= 0 && t.push({ time: l, text: o });
90
+ }
91
+ }
92
+ return t.sort((r, s) => r.time - s.time);
93
+ }
94
+ function z() {
95
+ const { tracks: e, currentIndex: t } = g(), { currentTime: r } = T(), s = e[t], o = v(() => s ? Array.isArray(s.lyricsTimed) && s.lyricsTimed.length > 0 ? [...s.lyricsTimed].filter((n) => Number.isFinite(n.time) && n.time >= 0).sort((n, c) => n.time - c.time) : typeof s.lyrics == "string" ? F(s.lyrics) : [] : [], [s]), a = v(() => {
96
+ for (let n = o.length - 1; n >= 0; n -= 1)
97
+ if (r >= o[n].time) return n;
98
+ return -1;
99
+ }, [r, o]);
100
+ return {
101
+ lines: o,
102
+ activeIndex: a,
103
+ activeLine: a >= 0 ? o[a] ?? null : null
104
+ };
105
+ }
106
+ function B(e) {
107
+ const { durationMs: t, stopAfterTracks: r, respectPause: s = !0, enabled: o = !0, onFire: a } = e, { currentIndex: n, pause: c, isPaused: u } = g(), l = h(r ?? 0), d = h(n);
108
+ k(() => {
109
+ l.current = r ?? 0;
110
+ }, [r]), k(() => {
111
+ if (!o || !t || t <= 0 || s && u) return;
112
+ const f = setTimeout(() => {
113
+ c(), a == null || a();
114
+ }, t);
115
+ return () => clearTimeout(f);
116
+ }, [t, o, u, a, c, s]), k(() => {
117
+ if (!o || !r || r <= 0) return;
118
+ const f = d.current;
119
+ d.current = n, n !== f && (l.current -= 1, l.current <= 0 && (c(), a == null || a()));
120
+ }, [n, o, a, c, r]);
121
+ }
122
+ function H(e = !1) {
123
+ const t = R(), r = h(t);
124
+ k(() => {
125
+ if (!e || typeof console > "u") return;
126
+ const s = r.current;
127
+ s !== t && console.debug("[ginger]", {
128
+ from: {
129
+ currentIndex: s.currentIndex,
130
+ isPaused: s.isPaused,
131
+ currentTime: s.currentTime,
132
+ repeatMode: s.repeatMode
133
+ },
134
+ to: {
135
+ currentIndex: t.currentIndex,
136
+ isPaused: t.isPaused,
137
+ currentTime: t.currentTime,
138
+ repeatMode: t.repeatMode
139
+ }
140
+ }), r.current = t;
141
+ }, [e, t]);
142
+ }
143
+ function K(e) {
144
+ return Math.max(0, Math.min(1, e));
145
+ }
146
+ function J(e) {
147
+ const t = T(), r = g(), { seek: s } = t, [o, a] = w(0), [n, c] = w(!1), u = S(A(r, t)), l = n ? o : u, d = b(
148
+ (f) => {
149
+ if (!(e > 0)) return;
150
+ const i = f.currentTarget, m = i.getBoundingClientRect(), p = (y) => {
151
+ const L = K((y - m.left) / m.width);
152
+ a(L), s(L * e);
153
+ };
154
+ c(!0), i.setPointerCapture(f.pointerId), p(f.clientX);
155
+ const x = (y) => p(y.clientX), P = (y) => {
156
+ p(y.clientX), c(!1), i.releasePointerCapture(f.pointerId), i.removeEventListener("pointermove", x), i.removeEventListener("pointerup", P), i.removeEventListener("pointercancel", P);
157
+ };
158
+ i.addEventListener("pointermove", x), i.addEventListener("pointerup", P), i.addEventListener("pointercancel", P);
159
+ },
160
+ [e, s]
161
+ );
162
+ return { fraction: o, displayFraction: l, isDragging: n, onPointerDown: d };
163
+ }
164
+ export {
165
+ j as a,
166
+ H as b,
167
+ Q as c,
168
+ z as d,
169
+ B as e,
170
+ J as f,
171
+ F as p,
172
+ q as u
173
+ };
174
+ //# sourceMappingURL=useSeekDrag-jLsYA-uG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSeekDrag-jLsYA-uG.js","sources":["../src/hooks/useGinger.ts","../src/hooks/useGingerKeyboardShortcuts.ts","../src/hooks/useGingerChapters.ts","../src/internal/lyrics.ts","../src/hooks/useGingerLyricsSync.ts","../src/hooks/useGingerSleepTimer.ts","../src/hooks/useGingerDebugLog.ts","../src/hooks/useSeekDrag.ts"],"sourcesContent":["import { useMemo } from \"react\";\nimport { gingerStateFromContextValues, useGingerMedia, useGingerPlayback } 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 () => {\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 init: pb.init,\n audioRef: md.audioRef,\n dispatch: pb.dispatch,\n };\n },\n [pb, md],\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 (target && ([\"INPUT\", \"TEXTAREA\", \"SELECT\"].includes(target.tagName) || target.isContentEditable)) 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 }, [bindings.next, bindings.playPause, bindings.previous, enabled, muteBinding, next, prev, toggleMute, togglePlayPause]);\n}\n","import { useMemo } from \"react\";\nimport { useGingerMedia, useGingerPlayback } from \"../context/GingerSplitContexts\";\n\nexport type GingerChapter = {\n title: string;\n startSeconds: number;\n};\n\nexport type GingerChapterState = {\n list: GingerChapter[];\n activeIndex: number;\n active: GingerChapter | null;\n seekTo: (index: number) => void;\n};\n\nexport function useGingerChapters(): GingerChapterState {\n const { tracks, currentIndex } = useGingerPlayback();\n const { currentTime, seek } = useGingerMedia();\n\n const list = useMemo(() => {\n const chapters = tracks[currentIndex]?.chapters ?? [];\n return [...chapters]\n .filter((item) => item && Number.isFinite(item.startSeconds) && item.startSeconds >= 0)\n .sort((a, b) => a.startSeconds - b.startSeconds);\n }, [currentIndex, tracks]);\n\n const activeIndex = useMemo(() => {\n if (list.length === 0) return -1;\n for (let i = list.length - 1; i >= 0; i -= 1) {\n if (currentTime >= list[i]!.startSeconds) return i;\n }\n return -1;\n }, [currentTime, list]);\n\n return {\n list,\n activeIndex,\n active: activeIndex >= 0 ? list[activeIndex] ?? null : null,\n seekTo: (index: number) => {\n const chapter = list[index];\n if (chapter) seek(chapter.startSeconds);\n },\n };\n}\n","export type TimedLyricLine = {\n time: number;\n text: string;\n};\n\nconst lrcTag = /\\[(\\d{1,2}):(\\d{2})(?:\\.(\\d{1,3}))?\\]/g;\n\nexport function parseLrc(lrc: string): TimedLyricLine[] {\n const lines: TimedLyricLine[] = [];\n for (const rawLine of lrc.split(/\\r?\\n/)) {\n const matches = [...rawLine.matchAll(lrcTag)];\n if (matches.length === 0) continue;\n const text = rawLine.replace(lrcTag, \"\").trim();\n for (const m of matches) {\n const minutes = Number(m[1] ?? 0);\n const seconds = Number(m[2] ?? 0);\n const millis = Number((m[3] ?? \"0\").padEnd(3, \"0\"));\n const time = minutes * 60 + seconds + millis / 1000;\n if (Number.isFinite(time) && time >= 0) {\n lines.push({ time, text });\n }\n }\n }\n return lines.sort((a, b) => a.time - b.time);\n}\n","import { useMemo } from \"react\";\nimport { useGingerMedia, useGingerPlayback } from \"../context/GingerSplitContexts\";\nimport { parseLrc, type TimedLyricLine } from \"../internal/lyrics\";\n\nexport type GingerLyricsSyncState = {\n lines: TimedLyricLine[];\n activeIndex: number;\n activeLine: TimedLyricLine | null;\n};\n\nexport function useGingerLyricsSync(): GingerLyricsSyncState {\n const { tracks, currentIndex } = useGingerPlayback();\n const { currentTime } = useGingerMedia();\n const currentTrack = tracks[currentIndex];\n\n const lines = useMemo(() => {\n if (!currentTrack) return [];\n if (Array.isArray(currentTrack.lyricsTimed) && currentTrack.lyricsTimed.length > 0) {\n return [...currentTrack.lyricsTimed]\n .filter((line) => Number.isFinite(line.time) && line.time >= 0)\n .sort((a, b) => a.time - b.time);\n }\n if (typeof currentTrack.lyrics === \"string\") {\n return parseLrc(currentTrack.lyrics);\n }\n return [];\n }, [currentTrack]);\n\n const activeIndex = useMemo(() => {\n for (let i = lines.length - 1; i >= 0; i -= 1) {\n if (currentTime >= lines[i]!.time) return i;\n }\n return -1;\n }, [currentTime, lines]);\n\n return {\n lines,\n activeIndex,\n activeLine: activeIndex >= 0 ? lines[activeIndex] ?? null : null,\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 { useGingerMedia, useGingerPlayback, gingerStateFromContextValues } 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"],"names":["useGinger","pb","useGingerPlayback","md","useGingerMedia","useMemo","state","gingerStateFromContextValues","getCurrentTrack","derivePlaybackUiState","effectiveDuration","effectiveRemaining","progressFraction","resolvedArtwork","resolvedAlbumLine","useGingerKeyboardShortcuts","enabled","bindings","togglePlayPause","next","prev","toggleMute","muteBinding","useEffect","playPause","nextKey","prevKey","muteKey","onKeyDown","event","target","key","useGingerChapters","tracks","currentIndex","currentTime","seek","list","_a","item","a","b","activeIndex","i","index","chapter","lrcTag","parseLrc","lrc","lines","rawLine","matches","text","m","minutes","seconds","millis","time","useGingerLyricsSync","currentTrack","line","useGingerSleepTimer","options","durationMs","stopAfterTracks","respectPause","onFire","pause","isPaused","remainingTracksRef","useRef","prevIndexRef","id","useGingerDebugLog","useGingerState","prevRef","clamp01","value","useSeekDrag","duration","media","playback","fraction","setFraction","useState","isDragging","setIsDragging","liveFraction","displayFraction","onPointerDown","useCallback","rect","update","clientX","ratio","onMove","moveEvent","onUp","upEvent"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const h=require("react");function M(a,o=64){const[g,t]=h.useState({peaks:[],isLoading:!1,error:null});return h.useEffect(()=>{if(!a){t({peaks:[],isLoading:!1,error:null});return}let s=!1;return t(e=>({...e,isLoading:!0,error:null})),(async()=>{try{const p=await(await fetch(a)).arrayBuffer(),c=new AudioContext,r=(await c.decodeAudioData(p)).getChannelData(0),l=Math.max(1,Math.floor(r.length/o)),f=[];for(let n=0;n<o;n+=1){let i=0;const d=n*l,m=Math.min(r.length,d+l);for(let u=d;u<m;u+=1)i=Math.max(i,Math.abs(r[u]??0));f.push(i)}await c.close(),s||t({peaks:f,isLoading:!1,error:null})}catch(e){s||t({peaks:[],isLoading:!1,error:e instanceof Error?e.message:"Failed to decode peaks"})}})(),()=>{s=!0}},[o,a]),g}exports.useAudioPeaks=M;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const h=require("react");function k(a,o=64){const[g,t]=h.useState({peaks:[],isLoading:!1,error:null});return h.useEffect(()=>{if(!a){t({peaks:[],isLoading:!1,error:null});return}let r=!1;return t(e=>({...e,isLoading:!0,error:null})),(async()=>{try{const e=await fetch(a);if(!e.ok)throw new Error(`Fetch failed: ${e.status} ${e.statusText}`);const p=await e.arrayBuffer(),c=new AudioContext,s=(await c.decodeAudioData(p)).getChannelData(0),l=Math.max(1,Math.floor(s.length/o)),f=[];for(let n=0;n<o;n+=1){let i=0;const d=n*l,w=Math.min(s.length,d+l);for(let u=d;u<w;u+=1)i=Math.max(i,Math.abs(s[u]??0));f.push(i)}await c.close(),r||t({peaks:f,isLoading:!1,error:null})}catch(e){r||t({peaks:[],isLoading:!1,error:e instanceof Error?e.message:"Failed to decode peaks"})}})(),()=>{r=!0}},[o,a]),g}exports.useAudioPeaks=k;
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../src/waveform/useAudioPeaks.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\nexport type UseAudioPeaksState = {\n peaks: number[];\n isLoading: boolean;\n error: string | null;\n};\n\nexport function useAudioPeaks(fileUrl: string | null | undefined, buckets = 64): UseAudioPeaksState {\n const [state, setState] = useState<UseAudioPeaksState>({\n peaks: [],\n isLoading: false,\n error: null,\n });\n\n useEffect(() => {\n if (!fileUrl) {\n setState({ peaks: [], isLoading: false, error: null });\n return;\n }\n let cancelled = false;\n setState((prev) => ({ ...prev, isLoading: true, error: null }));\n void (async () => {\n try {\n const response = await fetch(fileUrl);\n const buffer = await response.arrayBuffer();\n const audioContext = new AudioContext();\n const audioBuffer = await audioContext.decodeAudioData(buffer);\n const channel = audioBuffer.getChannelData(0);\n const step = Math.max(1, Math.floor(channel.length / buckets));\n const peaks: number[] = [];\n for (let i = 0; i < buckets; i += 1) {\n let max = 0;\n const start = i * step;\n const end = Math.min(channel.length, start + step);\n for (let j = start; j < end; j += 1) {\n max = Math.max(max, Math.abs(channel[j] ?? 0));\n }\n peaks.push(max);\n }\n await audioContext.close();\n if (!cancelled) {\n setState({ peaks, isLoading: false, error: null });\n }\n } catch (error) {\n if (!cancelled) {\n setState({\n peaks: [],\n isLoading: false,\n error: error instanceof Error ? error.message : \"Failed to decode peaks\",\n });\n }\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [buckets, fileUrl]);\n\n return state;\n}\n"],"names":["useAudioPeaks","fileUrl","buckets","state","setState","useState","useEffect","cancelled","prev","buffer","audioContext","channel","step","peaks","i","max","start","end","j","error"],"mappings":"yGAQO,SAASA,EAAcC,EAAoCC,EAAU,GAAwB,CAClG,KAAM,CAACC,EAAOC,CAAQ,EAAIC,WAA6B,CACrD,MAAO,CAAA,EACP,UAAW,GACX,MAAO,IAAA,CACR,EAEDC,OAAAA,EAAAA,UAAU,IAAM,CACd,GAAI,CAACL,EAAS,CACZG,EAAS,CAAE,MAAO,CAAA,EAAI,UAAW,GAAO,MAAO,KAAM,EACrD,MACF,CACA,IAAIG,EAAY,GAChB,OAAAH,EAAUI,IAAU,CAAE,GAAGA,EAAM,UAAW,GAAM,MAAO,IAAA,EAAO,GACxD,SAAY,CAChB,GAAI,CAEF,MAAMC,EAAS,MADE,MAAM,MAAMR,CAAO,GACN,YAAA,EACxBS,EAAe,IAAI,aAEnBC,GADc,MAAMD,EAAa,gBAAgBD,CAAM,GACjC,eAAe,CAAC,EACtCG,EAAO,KAAK,IAAI,EAAG,KAAK,MAAMD,EAAQ,OAAST,CAAO,CAAC,EACvDW,EAAkB,CAAA,EACxB,QAASC,EAAI,EAAGA,EAAIZ,EAASY,GAAK,EAAG,CACnC,IAAIC,EAAM,EACV,MAAMC,EAAQF,EAAIF,EACZK,EAAM,KAAK,IAAIN,EAAQ,OAAQK,EAAQJ,CAAI,EACjD,QAASM,EAAIF,EAAOE,EAAID,EAAKC,GAAK,EAChCH,EAAM,KAAK,IAAIA,EAAK,KAAK,IAAIJ,EAAQO,CAAC,GAAK,CAAC,CAAC,EAE/CL,EAAM,KAAKE,CAAG,CAChB,CACA,MAAML,EAAa,MAAA,EACdH,GACHH,EAAS,CAAE,MAAAS,EAAO,UAAW,GAAO,MAAO,KAAM,CAErD,OAASM,EAAO,CACTZ,GACHH,EAAS,CACP,MAAO,CAAA,EACP,UAAW,GACX,MAAOe,aAAiB,MAAQA,EAAM,QAAU,wBAAA,CACjD,CAEL,CACF,GAAA,EAEO,IAAM,CACXZ,EAAY,EACd,CACF,EAAG,CAACL,EAASD,CAAO,CAAC,EAEdE,CACT"}
1
+ {"version":3,"file":"index.cjs","sources":["../../src/waveform/useAudioPeaks.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\nexport type UseAudioPeaksState = {\n peaks: number[];\n isLoading: boolean;\n error: string | null;\n};\n\nexport function useAudioPeaks(fileUrl: string | null | undefined, buckets = 64): UseAudioPeaksState {\n const [state, setState] = useState<UseAudioPeaksState>({\n peaks: [],\n isLoading: false,\n error: null,\n });\n\n useEffect(() => {\n if (!fileUrl) {\n setState({ peaks: [], isLoading: false, error: null });\n return;\n }\n let cancelled = false;\n setState((prev) => ({ ...prev, isLoading: true, error: null }));\n void (async () => {\n try {\n const response = await fetch(fileUrl);\n if (!response.ok) throw new Error(`Fetch failed: ${response.status} ${response.statusText}`);\n const buffer = await response.arrayBuffer();\n const audioContext = new AudioContext();\n const audioBuffer = await audioContext.decodeAudioData(buffer);\n const channel = audioBuffer.getChannelData(0);\n const step = Math.max(1, Math.floor(channel.length / buckets));\n const peaks: number[] = [];\n for (let i = 0; i < buckets; i += 1) {\n let max = 0;\n const start = i * step;\n const end = Math.min(channel.length, start + step);\n for (let j = start; j < end; j += 1) {\n max = Math.max(max, Math.abs(channel[j] ?? 0));\n }\n peaks.push(max);\n }\n await audioContext.close();\n if (!cancelled) {\n setState({ peaks, isLoading: false, error: null });\n }\n } catch (error) {\n if (!cancelled) {\n setState({\n peaks: [],\n isLoading: false,\n error: error instanceof Error ? error.message : \"Failed to decode peaks\",\n });\n }\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [buckets, fileUrl]);\n\n return state;\n}\n"],"names":["useAudioPeaks","fileUrl","buckets","state","setState","useState","useEffect","cancelled","prev","response","buffer","audioContext","channel","step","peaks","i","max","start","end","j","error"],"mappings":"yGAQO,SAASA,EAAcC,EAAoCC,EAAU,GAAwB,CAClG,KAAM,CAACC,EAAOC,CAAQ,EAAIC,WAA6B,CACrD,MAAO,CAAA,EACP,UAAW,GACX,MAAO,IAAA,CACR,EAEDC,OAAAA,EAAAA,UAAU,IAAM,CACd,GAAI,CAACL,EAAS,CACZG,EAAS,CAAE,MAAO,CAAA,EAAI,UAAW,GAAO,MAAO,KAAM,EACrD,MACF,CACA,IAAIG,EAAY,GAChB,OAAAH,EAAUI,IAAU,CAAE,GAAGA,EAAM,UAAW,GAAM,MAAO,IAAA,EAAO,GACxD,SAAY,CAChB,GAAI,CACF,MAAMC,EAAW,MAAM,MAAMR,CAAO,EACpC,GAAI,CAACQ,EAAS,GAAI,MAAM,IAAI,MAAM,iBAAiBA,EAAS,MAAM,IAAIA,EAAS,UAAU,EAAE,EAC3F,MAAMC,EAAS,MAAMD,EAAS,YAAA,EACxBE,EAAe,IAAI,aAEnBC,GADc,MAAMD,EAAa,gBAAgBD,CAAM,GACjC,eAAe,CAAC,EACtCG,EAAO,KAAK,IAAI,EAAG,KAAK,MAAMD,EAAQ,OAASV,CAAO,CAAC,EACvDY,EAAkB,CAAA,EACxB,QAASC,EAAI,EAAGA,EAAIb,EAASa,GAAK,EAAG,CACnC,IAAIC,EAAM,EACV,MAAMC,EAAQF,EAAIF,EACZK,EAAM,KAAK,IAAIN,EAAQ,OAAQK,EAAQJ,CAAI,EACjD,QAASM,EAAIF,EAAOE,EAAID,EAAKC,GAAK,EAChCH,EAAM,KAAK,IAAIA,EAAK,KAAK,IAAIJ,EAAQO,CAAC,GAAK,CAAC,CAAC,EAE/CL,EAAM,KAAKE,CAAG,CAChB,CACA,MAAML,EAAa,MAAA,EACdJ,GACHH,EAAS,CAAE,MAAAU,EAAO,UAAW,GAAO,MAAO,KAAM,CAErD,OAASM,EAAO,CACTb,GACHH,EAAS,CACP,MAAO,CAAA,EACP,UAAW,GACX,MAAOgB,aAAiB,MAAQA,EAAM,QAAU,wBAAA,CACjD,CAEL,CACF,GAAA,EAEO,IAAM,CACXb,EAAY,EACd,CACF,EAAG,CAACL,EAASD,CAAO,CAAC,EAEdE,CACT"}
@@ -1,29 +1,31 @@
1
- import { useState as m, useEffect as x } from "react";
2
- function M(t, o = 64) {
3
- const [h, a] = m({
1
+ import { useState as m, useEffect as w } from "react";
2
+ function L(a, o = 64) {
3
+ const [h, t] = m({
4
4
  peaks: [],
5
5
  isLoading: !1,
6
6
  error: null
7
7
  });
8
- return x(() => {
9
- if (!t) {
10
- a({ peaks: [], isLoading: !1, error: null });
8
+ return w(() => {
9
+ if (!a) {
10
+ t({ peaks: [], isLoading: !1, error: null });
11
11
  return;
12
12
  }
13
13
  let r = !1;
14
- return a((e) => ({ ...e, isLoading: !0, error: null })), (async () => {
14
+ return t((e) => ({ ...e, isLoading: !0, error: null })), (async () => {
15
15
  try {
16
- const p = await (await fetch(t)).arrayBuffer(), l = new AudioContext(), s = (await l.decodeAudioData(p)).getChannelData(0), u = Math.max(1, Math.floor(s.length / o)), c = [];
16
+ const e = await fetch(a);
17
+ if (!e.ok) throw new Error(`Fetch failed: ${e.status} ${e.statusText}`);
18
+ const p = await e.arrayBuffer(), u = new AudioContext(), s = (await u.decodeAudioData(p)).getChannelData(0), l = Math.max(1, Math.floor(s.length / o)), c = [];
17
19
  for (let n = 0; n < o; n += 1) {
18
20
  let i = 0;
19
- const d = n * u, g = Math.min(s.length, d + u);
21
+ const d = n * l, g = Math.min(s.length, d + l);
20
22
  for (let f = d; f < g; f += 1)
21
23
  i = Math.max(i, Math.abs(s[f] ?? 0));
22
24
  c.push(i);
23
25
  }
24
- await l.close(), r || a({ peaks: c, isLoading: !1, error: null });
26
+ await u.close(), r || t({ peaks: c, isLoading: !1, error: null });
25
27
  } catch (e) {
26
- r || a({
28
+ r || t({
27
29
  peaks: [],
28
30
  isLoading: !1,
29
31
  error: e instanceof Error ? e.message : "Failed to decode peaks"
@@ -32,9 +34,9 @@ function M(t, o = 64) {
32
34
  })(), () => {
33
35
  r = !0;
34
36
  };
35
- }, [o, t]), h;
37
+ }, [o, a]), h;
36
38
  }
37
39
  export {
38
- M as useAudioPeaks
40
+ L as useAudioPeaks
39
41
  };
40
42
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/waveform/useAudioPeaks.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\nexport type UseAudioPeaksState = {\n peaks: number[];\n isLoading: boolean;\n error: string | null;\n};\n\nexport function useAudioPeaks(fileUrl: string | null | undefined, buckets = 64): UseAudioPeaksState {\n const [state, setState] = useState<UseAudioPeaksState>({\n peaks: [],\n isLoading: false,\n error: null,\n });\n\n useEffect(() => {\n if (!fileUrl) {\n setState({ peaks: [], isLoading: false, error: null });\n return;\n }\n let cancelled = false;\n setState((prev) => ({ ...prev, isLoading: true, error: null }));\n void (async () => {\n try {\n const response = await fetch(fileUrl);\n const buffer = await response.arrayBuffer();\n const audioContext = new AudioContext();\n const audioBuffer = await audioContext.decodeAudioData(buffer);\n const channel = audioBuffer.getChannelData(0);\n const step = Math.max(1, Math.floor(channel.length / buckets));\n const peaks: number[] = [];\n for (let i = 0; i < buckets; i += 1) {\n let max = 0;\n const start = i * step;\n const end = Math.min(channel.length, start + step);\n for (let j = start; j < end; j += 1) {\n max = Math.max(max, Math.abs(channel[j] ?? 0));\n }\n peaks.push(max);\n }\n await audioContext.close();\n if (!cancelled) {\n setState({ peaks, isLoading: false, error: null });\n }\n } catch (error) {\n if (!cancelled) {\n setState({\n peaks: [],\n isLoading: false,\n error: error instanceof Error ? error.message : \"Failed to decode peaks\",\n });\n }\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [buckets, fileUrl]);\n\n return state;\n}\n"],"names":["useAudioPeaks","fileUrl","buckets","state","setState","useState","useEffect","cancelled","prev","buffer","audioContext","channel","step","peaks","i","max","start","end","j","error"],"mappings":";AAQO,SAASA,EAAcC,GAAoCC,IAAU,IAAwB;AAClG,QAAM,CAACC,GAAOC,CAAQ,IAAIC,EAA6B;AAAA,IACrD,OAAO,CAAA;AAAA,IACP,WAAW;AAAA,IACX,OAAO;AAAA,EAAA,CACR;AAED,SAAAC,EAAU,MAAM;AACd,QAAI,CAACL,GAAS;AACZ,MAAAG,EAAS,EAAE,OAAO,CAAA,GAAI,WAAW,IAAO,OAAO,MAAM;AACrD;AAAA,IACF;AACA,QAAIG,IAAY;AAChB,WAAAH,EAAS,CAACI,OAAU,EAAE,GAAGA,GAAM,WAAW,IAAM,OAAO,KAAA,EAAO,IACxD,YAAY;AAChB,UAAI;AAEF,cAAMC,IAAS,OADE,MAAM,MAAMR,CAAO,GACN,YAAA,GACxBS,IAAe,IAAI,aAAA,GAEnBC,KADc,MAAMD,EAAa,gBAAgBD,CAAM,GACjC,eAAe,CAAC,GACtCG,IAAO,KAAK,IAAI,GAAG,KAAK,MAAMD,EAAQ,SAAST,CAAO,CAAC,GACvDW,IAAkB,CAAA;AACxB,iBAASC,IAAI,GAAGA,IAAIZ,GAASY,KAAK,GAAG;AACnC,cAAIC,IAAM;AACV,gBAAMC,IAAQF,IAAIF,GACZK,IAAM,KAAK,IAAIN,EAAQ,QAAQK,IAAQJ,CAAI;AACjD,mBAASM,IAAIF,GAAOE,IAAID,GAAKC,KAAK;AAChC,YAAAH,IAAM,KAAK,IAAIA,GAAK,KAAK,IAAIJ,EAAQO,CAAC,KAAK,CAAC,CAAC;AAE/C,UAAAL,EAAM,KAAKE,CAAG;AAAA,QAChB;AACA,cAAML,EAAa,MAAA,GACdH,KACHH,EAAS,EAAE,OAAAS,GAAO,WAAW,IAAO,OAAO,MAAM;AAAA,MAErD,SAASM,GAAO;AACd,QAAKZ,KACHH,EAAS;AAAA,UACP,OAAO,CAAA;AAAA,UACP,WAAW;AAAA,UACX,OAAOe,aAAiB,QAAQA,EAAM,UAAU;AAAA,QAAA,CACjD;AAAA,MAEL;AAAA,IACF,GAAA,GAEO,MAAM;AACX,MAAAZ,IAAY;AAAA,IACd;AAAA,EACF,GAAG,CAACL,GAASD,CAAO,CAAC,GAEdE;AACT;"}
1
+ {"version":3,"file":"index.js","sources":["../../src/waveform/useAudioPeaks.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\nexport type UseAudioPeaksState = {\n peaks: number[];\n isLoading: boolean;\n error: string | null;\n};\n\nexport function useAudioPeaks(fileUrl: string | null | undefined, buckets = 64): UseAudioPeaksState {\n const [state, setState] = useState<UseAudioPeaksState>({\n peaks: [],\n isLoading: false,\n error: null,\n });\n\n useEffect(() => {\n if (!fileUrl) {\n setState({ peaks: [], isLoading: false, error: null });\n return;\n }\n let cancelled = false;\n setState((prev) => ({ ...prev, isLoading: true, error: null }));\n void (async () => {\n try {\n const response = await fetch(fileUrl);\n if (!response.ok) throw new Error(`Fetch failed: ${response.status} ${response.statusText}`);\n const buffer = await response.arrayBuffer();\n const audioContext = new AudioContext();\n const audioBuffer = await audioContext.decodeAudioData(buffer);\n const channel = audioBuffer.getChannelData(0);\n const step = Math.max(1, Math.floor(channel.length / buckets));\n const peaks: number[] = [];\n for (let i = 0; i < buckets; i += 1) {\n let max = 0;\n const start = i * step;\n const end = Math.min(channel.length, start + step);\n for (let j = start; j < end; j += 1) {\n max = Math.max(max, Math.abs(channel[j] ?? 0));\n }\n peaks.push(max);\n }\n await audioContext.close();\n if (!cancelled) {\n setState({ peaks, isLoading: false, error: null });\n }\n } catch (error) {\n if (!cancelled) {\n setState({\n peaks: [],\n isLoading: false,\n error: error instanceof Error ? error.message : \"Failed to decode peaks\",\n });\n }\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [buckets, fileUrl]);\n\n return state;\n}\n"],"names":["useAudioPeaks","fileUrl","buckets","state","setState","useState","useEffect","cancelled","prev","response","buffer","audioContext","channel","step","peaks","i","max","start","end","j","error"],"mappings":";AAQO,SAASA,EAAcC,GAAoCC,IAAU,IAAwB;AAClG,QAAM,CAACC,GAAOC,CAAQ,IAAIC,EAA6B;AAAA,IACrD,OAAO,CAAA;AAAA,IACP,WAAW;AAAA,IACX,OAAO;AAAA,EAAA,CACR;AAED,SAAAC,EAAU,MAAM;AACd,QAAI,CAACL,GAAS;AACZ,MAAAG,EAAS,EAAE,OAAO,CAAA,GAAI,WAAW,IAAO,OAAO,MAAM;AACrD;AAAA,IACF;AACA,QAAIG,IAAY;AAChB,WAAAH,EAAS,CAACI,OAAU,EAAE,GAAGA,GAAM,WAAW,IAAM,OAAO,KAAA,EAAO,IACxD,YAAY;AAChB,UAAI;AACF,cAAMC,IAAW,MAAM,MAAMR,CAAO;AACpC,YAAI,CAACQ,EAAS,GAAI,OAAM,IAAI,MAAM,iBAAiBA,EAAS,MAAM,IAAIA,EAAS,UAAU,EAAE;AAC3F,cAAMC,IAAS,MAAMD,EAAS,YAAA,GACxBE,IAAe,IAAI,aAAA,GAEnBC,KADc,MAAMD,EAAa,gBAAgBD,CAAM,GACjC,eAAe,CAAC,GACtCG,IAAO,KAAK,IAAI,GAAG,KAAK,MAAMD,EAAQ,SAASV,CAAO,CAAC,GACvDY,IAAkB,CAAA;AACxB,iBAASC,IAAI,GAAGA,IAAIb,GAASa,KAAK,GAAG;AACnC,cAAIC,IAAM;AACV,gBAAMC,IAAQF,IAAIF,GACZK,IAAM,KAAK,IAAIN,EAAQ,QAAQK,IAAQJ,CAAI;AACjD,mBAASM,IAAIF,GAAOE,IAAID,GAAKC,KAAK;AAChC,YAAAH,IAAM,KAAK,IAAIA,GAAK,KAAK,IAAIJ,EAAQO,CAAC,KAAK,CAAC,CAAC;AAE/C,UAAAL,EAAM,KAAKE,CAAG;AAAA,QAChB;AACA,cAAML,EAAa,MAAA,GACdJ,KACHH,EAAS,EAAE,OAAAU,GAAO,WAAW,IAAO,OAAO,MAAM;AAAA,MAErD,SAASM,GAAO;AACd,QAAKb,KACHH,EAAS;AAAA,UACP,OAAO,CAAA;AAAA,UACP,WAAW;AAAA,UACX,OAAOgB,aAAiB,QAAQA,EAAM,UAAU;AAAA,QAAA,CACjD;AAAA,MAEL;AAAA,IACF,GAAA,GAEO,MAAM;AACX,MAAAb,IAAY;AAAA,IACd;AAAA,EACF,GAAG,CAACL,GAASD,CAAO,CAAC,GAEdE;AACT;"}
@@ -1 +1 @@
1
- {"version":3,"file":"useAudioPeaks.d.ts","sourceRoot":"","sources":["../../src/waveform/useAudioPeaks.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,CAAC;AAEF,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,OAAO,SAAK,GAAG,kBAAkB,CAqDlG"}
1
+ {"version":3,"file":"useAudioPeaks.d.ts","sourceRoot":"","sources":["../../src/waveform/useAudioPeaks.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,CAAC;AAEF,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,OAAO,SAAK,GAAG,kBAAkB,CAsDlG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lucaismyname/ginger",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "description": "A headless React audio player",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -58,6 +58,7 @@
58
58
  "@types/react": "^18.3.12",
59
59
  "@types/react-dom": "^18.3.1",
60
60
  "@vitejs/plugin-react": "^4.3.4",
61
+ "jsdom": "^29.0.2",
61
62
  "react": "^18.3.1",
62
63
  "react-dom": "^18.3.1",
63
64
  "typescript": "^5.6.3",
@@ -1,2 +0,0 @@
1
- "use strict";const o=require("react/jsx-runtime"),d=require("react"),k=require("./GingerSplitContexts-Bze1Bqe2.cjs"),Ce=d.createContext(null);function vr(){const e=d.useContext(Ce);if(!e)throw new Error("Ginger components must be used within <Ginger.Provider>");return e}function Re(e){const{buffered:r,duration:n}=e;return!(n>0)||r.length===0?0:Math.min(1,r.end(r.length-1)/n)}function Tr({className:e,style:r,preload:n="metadata",crossOrigin:t,respectReducedMotion:a=!1}){var E;const{audioRef:u,dispatch:s,state:l,notifyEnded:f}=vr(),p=((E=l.tracks[l.currentIndex])==null?void 0:E.fileUrl)??"",y=d.useRef({currentTime:-1,duration:-1,bufferedFraction:-1}),[h,x]=d.useState(!1);d.useEffect(()=>{if(!a||typeof window>"u")return;const g=window.matchMedia("(prefers-reduced-motion: reduce)"),m=()=>x(g.matches);return m(),g.addEventListener("change",m),()=>g.removeEventListener("change",m)},[a]);const T=(g,m=!1)=>{const R={currentTime:g.currentTime,duration:g.duration,bufferedFraction:Re(g)},P=y.current,he=h?.5:.25,H=Math.abs(R.currentTime-P.currentTime)>=he||Math.abs(R.duration-P.duration)>=.01||Math.abs(R.bufferedFraction-P.bufferedFraction)>=.01;!m&&!H||(y.current=R,s({type:"MEDIA_TIME_UPDATE",payload:R}))};return d.useEffect(()=>{const g=u.current;g&&(g.volume=l.volume,g.muted=l.muted,g.playbackRate=l.playbackRate)},[u,l.volume,l.muted,l.playbackRate]),d.useEffect(()=>{const g=u.current;if(g){if(!p){g.removeAttribute("src"),y.current={currentTime:-1,duration:-1,bufferedFraction:-1};return}g.getAttribute("src")!==p&&(g.src=p,g.load(),y.current={currentTime:-1,duration:-1,bufferedFraction:-1})}},[u,l.currentIndex,l.tracks,p]),o.jsx("audio",{ref:u,className:e,style:r,preload:n,crossOrigin:t,controls:!1,playsInline:!0,onTimeUpdate:g=>{T(g.currentTarget)},onLoadedMetadata:g=>{const m=g.currentTarget;y.current={currentTime:-1,duration:-1,bufferedFraction:-1},s({type:"MEDIA_LOADED_METADATA",payload:{duration:m.duration,bufferedFraction:Re(m)}})},onSeeking:g=>T(g.currentTarget,!0),onSeeked:g=>T(g.currentTarget,!0),onEnded:()=>f(),onPlay:()=>s({type:"MEDIA_PLAY"}),onPause:()=>s({type:"MEDIA_PAUSE"}),onWaiting:()=>s({type:"MEDIA_WAITING"}),onCanPlay:()=>s({type:"MEDIA_CANPLAY"}),onProgress:g=>T(g.currentTarget,!0),onVolumeChange:g=>{const m=g.currentTarget;s({type:"MEDIA_VOLUME_SYNC",payload:{volume:m.volume,muted:m.muted}})},onError:()=>{var P;const g=u.current,m=(P=g==null?void 0:g.error)==null?void 0:P.code;s({type:"MEDIA_ERROR",payload:{message:m===1?"MEDIA_ERR_ABORTED":m===2?"MEDIA_ERR_NETWORK":m===3?"MEDIA_ERR_DECODE":m===4?"MEDIA_ERR_SRC_NOT_SUPPORTED":"MEDIA_ERR_UNKNOWN"}})}})}function I(e,r){return r<=0?0:Math.max(0,Math.min(r-1,e))}function Ge(e,r){if(e.length<=1)return[...e];const n=e[r];if(!n)return[...e];const t=e.filter((a,u)=>u!==r);for(let a=t.length-1;a>0;a--){const u=Math.floor(Math.random()*(a+1));[t[a],t[u]]=[t[u],t[a]]}return[n,...t]}function Y(e){return e?e.id!=null&&e.id!==""?`id:${e.id}`:`file:${e.fileUrl}`:""}function Ir(e,r){const n=Y(r);if(!n)return 0;const t=e.findIndex(a=>Y(a)===n);return t===-1?0:t}function je(e,r,n){const t=[...e],a=Math.max(0,Math.min(t.length,n??t.length));return t.splice(a,0,r),t}function Er(e,r){if(r<0||r>=e.length)return[...e];const n=[...e];return n.splice(r,1),n}function Pr(e,r,n){if(r===n||r<0||r>=e.length||n<0||n>=e.length)return[...e];const t=[...e],[a]=t.splice(r,1);return a?(t.splice(n,0,a),t):[...e]}function Sr(e,r,n){return je(e,n,Math.max(0,Math.min(e.length,r+1)))}function Ar(e){const{tracks:r,currentIndex:n,repeatMode:t,playbackMode:a}=e,u=r.length;return u===0?{kind:"stop",nextIndex:0}:t==="one"?{kind:"replay_same"}:a==="single"?{kind:"stop",nextIndex:I(n,u)}:n<u-1?{kind:"advance",nextIndex:n+1}:t==="all"?{kind:"wrap",nextIndex:0}:{kind:"stop",nextIndex:I(n,u)}}function Rr(e){const{tracks:r,currentIndex:n,repeatMode:t,playbackMode:a}=e,u=r.length;return u===0?0:a==="single"?I(n,u):n<u-1?n+1:t==="all"?0:I(n,u)}function Cr(e){const{tracks:r,currentIndex:n,repeatMode:t,playbackMode:a}=e,u=r.length;return u===0?0:a==="single"?I(n,u):n>0?n-1:t==="all"?u-1:0}function Gr(e){return e==="off"?"all":e==="all"?"one":"off"}function jr(e,r){return(e==null?void 0:e.artworkUrl)??r??void 0}function _r(e,r){return(e==null?void 0:e.album)??r??void 0}function G(e){return e.tracks[e.currentIndex]??null}function Te(e){return e.errorMessage?"error":e.tracks.length===0?"idle":e.isBuffering?"loading":e.isPaused?Number.isFinite(e.duration)&&e.duration>0&&e.currentTime>=e.duration-.05?"ended":"paused":"playing"}function B(e){var t;const r=e.duration;if(Number.isFinite(r)&&r>0)return r;const n=(t=e.tracks[e.currentIndex])==null?void 0:t.durationSeconds;return typeof n=="number"&&Number.isFinite(n)&&n>0?n:0}function _e(e){const n=B(e)-e.currentTime;return Number.isFinite(n)?Math.max(0,n):0}function Ie(e){const r=B(e);return r>0?Math.min(1,Math.max(0,e.currentTime/r)):0}function Ne(e){var n;const r=G(e);return jr(r,(n=e.playlistMeta)==null?void 0:n.artworkUrl)}function we(e){var n;const r=G(e);return _r(r,(n=e.playlistMeta)==null?void 0:n.subtitle)}function L(e,r){function n(t){const a=k.useGingerState(),s=(r(a)??"").trim(),{className:l,style:f,fallback:p,empty:y,children:h}=t;if(!s){const x=y??p??null;return x?o.jsx("span",{className:l,style:f,children:x}):null}return h?o.jsx("span",{className:l,style:f,children:h(s,a)}):o.jsx("span",{className:l,style:f,children:s})}return n.displayName=e,n}function F(e,r){return L(e,n=>r(G(n)))}const Nr=F("Ginger.Current.Title",e=>e==null?void 0:e.title),wr=F("Ginger.Current.Artist",e=>e==null?void 0:e.artist),Dr=L("Ginger.Current.Album",e=>we(e)),Lr=F("Ginger.Current.Description",e=>e==null?void 0:e.description),Fr=L("Ginger.Current.Copyright",e=>{var n;const r=G(e);return(r==null?void 0:r.copyright)??((n=e.playlistMeta)==null?void 0:n.copyright)}),Ur=F("Ginger.Current.Genre",e=>e==null?void 0:e.genre),Vr=F("Ginger.Current.Label",e=>e==null?void 0:e.label),Br=F("Ginger.Current.Isrc",e=>e==null?void 0:e.isrc),$r=F("Ginger.Current.TrackNumber",e=>(e==null?void 0:e.trackNumber)!=null?String(e.trackNumber):void 0);function De({className:e,style:r,fallback:n,empty:t,children:a,format:u}){var p;const s=k.useGingerState(),l=(p=G(s))==null?void 0:p.year;if(typeof l!="number"||!Number.isFinite(l)){const y=t??n??null;return y?o.jsx("span",{className:e,style:r,children:y}):null}const f=u?u(l):String(l);return a?o.jsx("span",{className:e,style:r,children:a(f,s)}):o.jsx("span",{className:e,style:r,children:f})}De.displayName="Ginger.Current.Year";function Le({className:e,style:r,fallback:n,empty:t,children:a,preserveWhitespace:u=!0}){var y;const s=k.useGingerState(),l=((y=G(s))==null?void 0:y.lyrics)??"",f=u?l.replace(/^\s+|\s+$/g,""):l.trim();if(!f){const h=t??n??null;return h?o.jsx("span",{className:e,style:r,children:h}):null}const p=u?{whiteSpace:"pre-wrap"}:void 0;return a?o.jsx("span",{className:e,style:{...p,...r},children:a(f,s)}):o.jsx("span",{className:e,style:{...p,...r},children:f})}Le.displayName="Ginger.Current.Lyrics";function Fe({visible:e=!1,className:r,style:n,fallback:t,empty:a,children:u}){var f;const s=k.useGingerState();if(!e)return null;const l=((f=G(s))==null?void 0:f.fileUrl)??"";if(!l){const p=a??t??null;return p?o.jsx("span",{className:r,style:n,children:p}):null}return u?o.jsx("span",{className:r,style:n,children:u(l,s)}):o.jsx("span",{className:r,style:n,children:l})}Fe.displayName="Ginger.Current.FileUrl";function Ue({className:e,style:r,fallback:n,empty:t,sizes:a,loading:u,onError:s,decoding:l,imgStyle:f}){const p=k.useGingerState(),y=G(p),h=Ne(p);if(!h){const T=t??n??null;return T?o.jsx("span",{className:e,style:r,children:T}):null}const x=[y==null?void 0:y.title,y==null?void 0:y.artist].filter(Boolean).join(" — ")||"Artwork";return o.jsx("div",{className:e,style:{background:"var(--ginger-artwork-bg, transparent)",borderRadius:"var(--ginger-artwork-radius, 0)",overflow:"hidden",...r},children:o.jsx("img",{src:h,alt:x,sizes:a,loading:u,decoding:l,onError:s,style:{display:"block",width:"100%",height:"100%",objectFit:"cover",...f}})})}Ue.displayName="Ginger.Current.Artwork";function Ve({base:e=0,className:r,style:n,fallback:t,empty:a,children:u}){const s=k.useGingerState();if(s.tracks.length===0){const p=a??t??null;return p?o.jsx("span",{className:r,style:n,children:p}):null}const f=String(s.currentIndex+e);return u?o.jsx("span",{className:r,style:n,children:u(f,s)}):o.jsx("span",{className:r,style:n,children:f})}Ve.displayName="Ginger.Current.QueueIndex";function Be({className:e,style:r,fallback:n,empty:t,children:a}){const u=k.useGingerState(),s=String(u.tracks.length);if(u.tracks.length===0){const l=t??n??null;return l?o.jsx("span",{className:e,style:r,children:l}):null}return a?o.jsx("span",{className:e,style:r,children:a(s,u)}):o.jsx("span",{className:e,style:r,children:s})}Be.displayName="Ginger.Current.QueueLength";function $e({base:e=0,separator:r=" / ",className:n,style:t,fallback:a,empty:u,children:s}){const l=k.useGingerState(),f=l.tracks.length;if(f===0){const x=u??a??null;return x?o.jsx("span",{className:n,style:t,children:x}):null}const p=String(l.currentIndex+e),y=String(f),h=`${p}${r}${y}`;return s?o.jsx("span",{className:n,style:t,children:s({index:p,length:y,label:h},l)}):o.jsx("span",{className:n,style:t,children:h})}$e.displayName="Ginger.Current.QueuePosition";function ke(e){if(!Number.isFinite(e)||e<0)return"0:00";const r=Math.floor(e%60);return`${Math.floor(e/60)}:${r.toString().padStart(2,"0")}`}function Ee(e,r,n){const{className:t,style:a,fallback:u,empty:s,children:l,format:f=ke}=n;if(!(e>=0)||!Number.isFinite(e)){const y=s??u??null;return y?o.jsx("span",{className:t,style:a,children:y}):null}const p=f(e);return l?o.jsx("span",{className:t,style:a,children:l(p,r)}):o.jsx("span",{className:t,style:a,children:p})}function Oe(e){const r=k.useGingerState();return Ee(r.currentTime,r,e)}Oe.displayName="Ginger.Current.Elapsed";function Ye(e){const r=k.useGingerState();return Ee(B(r),r,e)}Ye.displayName="Ginger.Current.Duration";function Qe(e){const r=k.useGingerState();return Ee(_e(r),r,e)}Qe.displayName="Ginger.Current.Remaining";function He({className:e,style:r,fallback:n,empty:t,children:a}){const u=k.useGingerState(),s=B(u),l=Ie(u);if(!(s>0)){const f=t??n??null;return f?o.jsx("span",{className:e,style:r,children:f}):null}return a?o.jsx("span",{className:e,style:r,children:a({fraction:l,currentTime:u.currentTime,duration:s},u)}):o.jsx("span",{className:e,style:r,children:`${Math.round(l*100)}%`})}He.displayName="Ginger.Current.Progress";function Xe({className:e,style:r,height:n=4,showBuffered:t=!1}){const a=k.useGingerState(),u=`${Math.round(Ie(a)*100)}%`,s=`${Math.round(Math.min(1,Math.max(0,a.bufferedFraction))*100)}%`;return o.jsxs("div",{className:e,style:{width:"100%",height:n,background:"var(--ginger-muted-color, #e5e7eb)",borderRadius:999,overflow:"hidden",position:"relative",...r},"aria-hidden":!0,children:[t?o.jsx("div",{style:{position:"absolute",left:0,top:0,height:"100%",width:s,background:"var(--ginger-buffer-color, rgba(107, 114, 128, 0.35))"}}):null,o.jsx("div",{style:{position:"relative",width:u,height:"100%",background:"var(--ginger-primary-color, #111827)"}})]})}Xe.displayName="Ginger.Current.TimeRail";function Ke({className:e,style:r,height:n=4}){const t=k.useGingerState(),a=`${Math.round(Math.min(1,Math.max(0,t.bufferedFraction))*100)}%`;return o.jsx("div",{className:e,style:{width:"100%",height:n,background:"var(--ginger-muted-color, #e5e7eb)",borderRadius:999,overflow:"hidden",...r},"aria-hidden":!0,children:o.jsx("div",{style:{width:a,height:"100%",background:"var(--ginger-buffer-color, rgba(107, 114, 128, 0.35))"}})})}Ke.displayName="Ginger.Current.BufferRail";function qe({className:e,style:r,fallback:n,empty:t,children:a}){const u=k.useGingerState(),s=Te(u);return a?o.jsx("span",{className:e,style:r,children:a(s,u)}):o.jsx("span",{className:e,style:r,children:s})}qe.displayName="Ginger.Current.PlaybackState";function We({className:e,style:r,fallback:n,empty:t,live:a="polite",children:u}){const s=k.useGingerState(),l=s.errorMessage??"";if(!l){const f=t??n??null;return f?o.jsx("span",{className:e,style:r,children:f}):null}return u?o.jsx("span",{className:e,style:r,"aria-live":a,children:u(l,s)}):o.jsx("span",{className:e,style:r,"aria-live":a,children:l})}We.displayName="Ginger.Current.ErrorMessage";const O={seek:"Seek",volume:"Volume",playbackSpeed:"Playback speed",nextTrack:"Next track",previousTrack:"Previous track",shuffle:"Shuffle",mute:"Mute",unmute:"Unmute",play:"Play",pause:"Pause",repeat:{off:"Repeat off",all:"Repeat all",one:"Repeat one"},playbackRateNormal:"1× normal",playbackRateTimes:e=>`${e}×`};function Or(e){return e?{...O,...e,repeat:{...O.repeat,...e.repeat}}:O}const ze=d.createContext(O);function Yr({locale:e,children:r}){const n=Or(e);return o.jsx(ze.Provider,{value:n,children:r})}function S(){return d.useContext(ze)}function Je(){const e=k.useGingerPlayback(),r=k.useGingerMedia(),n=S(),t=d.useMemo(()=>k.gingerStateFromContextValues(e,r),[e,r]),a=B(t),u=a>0?t.currentTime:0,s=Number.isFinite(u)?u:0,l=a>0?`${ke(s)} of ${ke(a)}`:ke(s),f=p=>{r.seek(Number(p.currentTarget.value))};return{state:t,value:s,min:0,max:a>0?a:1,step:"any",ariaValueText:l,ariaLabel:n.seek,onSeekInput:f,onSeekChange:f}}function Ze(){const e=k.useGingerPlayback(),r=k.useGingerMedia(),n=S(),t=d.useMemo(()=>k.gingerStateFromContextValues(e,r),[e,r]),a=u=>{r.setVolume(Number(u.currentTarget.value))};return{state:t,value:t.volume,min:0,max:1,step:"any",ariaValueText:`${Math.round(t.volume*100)}%`,ariaLabel:n.volume,onVolumeInput:a,onVolumeChange:a}}function er(e){const r=k.useGingerPlayback(),n=S(),t=(e==null?void 0:e.playAriaLabel)??n.play,a=(e==null?void 0:e.pauseAriaLabel)??n.pause;return{isPaused:r.isPaused,toggle:r.togglePlayPause,ariaLabel:r.isPaused?t:a}}function rr({playLabel:e="Play",pauseLabel:r="Pause",playAriaLabel:n,pauseAriaLabel:t,type:a="button",...u}){const s=S(),l=typeof e=="string"?e:s.play,f=typeof r=="string"?r:s.pause,p=er({playAriaLabel:n??l,pauseAriaLabel:t??f});return o.jsx("button",{type:a,"aria-label":p.ariaLabel,onClick:p.toggle,...u,children:p.isPaused?e:r})}rr.displayName="Ginger.Control.PlayPause";function nr({type:e="button",...r}){const{repeatMode:n,cycleRepeat:t}=k.useGingerPlayback(),u=S().repeat[n];return o.jsx("button",{type:e,"aria-label":u,onClick:t,...r,children:u})}nr.displayName="Ginger.Control.Repeat";function tr({type:e="button",children:r="Next",...n}){const{next:t}=k.useGingerPlayback(),a=S();return o.jsx("button",{type:e,"aria-label":a.nextTrack,onClick:t,...n,children:r})}tr.displayName="Ginger.Control.Next";function ar({type:e="button",children:r="Previous",...n}){const{prev:t}=k.useGingerPlayback(),a=S();return o.jsx("button",{type:e,"aria-label":a.previousTrack,onClick:t,...n,children:r})}ar.displayName="Ginger.Control.Previous";function ur({type:e="button",children:r="Shuffle",...n}){const{isShuffled:t,toggleShuffle:a}=k.useGingerPlayback(),u=S();return o.jsx("button",{type:e,"aria-pressed":t,"aria-label":u.shuffle,onClick:a,...n,children:r})}ur.displayName="Ginger.Control.Shuffle";function ir({inputStyle:e,style:r,...n}){const t=Je();return o.jsx("input",{...n,type:"range",min:t.min,max:t.max,step:t.step,value:t.value,"aria-label":t.ariaLabel,"aria-valuetext":t.ariaValueText,onInput:t.onSeekInput,onChange:t.onSeekChange,style:{width:"100%",...r,...e}})}ir.displayName="Ginger.Control.SeekBar";function sr({inputStyle:e,style:r,...n}){const t=Ze();return o.jsx("input",{...n,type:"range",min:t.min,max:t.max,step:t.step,value:t.value,"aria-label":t.ariaLabel,"aria-valuetext":t.ariaValueText,onInput:t.onVolumeInput,onChange:t.onVolumeChange,style:{width:"100%",...r,...e}})}sr.displayName="Ginger.Control.Volume";function lr({muteLabel:e,unmuteLabel:r,type:n="button",...t}){const{muted:a,toggleMute:u}=k.useGingerMedia(),s=S(),l=e??s.mute,f=r??s.unmute;return o.jsx("button",{type:n,"aria-pressed":a,"aria-label":a?s.unmute:s.mute,onClick:u,...t,children:a?f:l})}lr.displayName="Ginger.Control.Mute";const Qr=[.5,.75,1,1.25,1.5,2];function or({rates:e=Qr,style:r,...n}){const{playbackRate:t,setPlaybackRate:a}=k.useGingerMedia(),u=S(),s=d.useMemo(()=>Array.from(new Set([...e,t])).sort((l,f)=>l-f),[e,t]);return o.jsx("select",{...n,"aria-label":u.playbackSpeed,value:String(t),style:r,onChange:l=>a(Number(l.currentTarget.value)),children:s.map(l=>o.jsx("option",{value:String(l),children:l===1?u.playbackRateNormal:u.playbackRateTimes(l)},l))})}or.displayName="Ginger.Control.PlaybackRate";const ve=d.createContext(null);function Hr(){const e=d.useContext(ve);if(!e)throw new Error("Ginger.Playlist.Track must be used inside <Ginger.Playlist>");return e}function cr({children:e,rowStyle:r,renderTrack:n,playOnSelect:t=!0,style:a,...u}){const{tracks:s,currentIndex:l,playTrackAt:f,selectTrackAt:p}=k.useGingerPlayback(),y={listStyle:"none",margin:0,padding:0,fontFamily:"var(--ginger-font-family, system-ui, sans-serif)",fontSize:"var(--ginger-font-size, 14px)",color:"var(--ginger-primary-color, #111827)",...a};return e!==void 0?o.jsx(ve.Provider,{value:{playOnSelect:t},children:o.jsx("ul",{style:y,...u,children:e})}):o.jsx(ve.Provider,{value:{playOnSelect:t},children:o.jsx("ul",{style:y,...u,children:s.map((x,T)=>{const E=T===l;return o.jsx("li",{children:o.jsx("button",{type:"button",onClick:()=>{t?f(T):p(T)},style:{width:"100%",textAlign:"left",border:"none",background:E?"var(--ginger-playlist-active-bg, rgba(17, 24, 39, 0.06))":"transparent",color:"inherit",font:"inherit",cursor:"pointer",padding:"var(--ginger-playlist-row-padding, 6px 8px)",...r},children:n?n(x,T,E):o.jsxs("span",{children:[x.title,x.artist?` — ${x.artist}`:""]})})},`${T}-${Y(x)}`)})})})}cr.displayName="Ginger.Playlist";function dr({index:e,className:r,style:n,children:t,liProps:a,onClick:u,...s}){const{playOnSelect:l}=Hr(),{tracks:f,currentIndex:p,playTrackAt:y,selectTrackAt:h}=k.useGingerPlayback(),x=e===p,T=f[e],E=T!=null?o.jsxs("span",{children:[T.title,T.artist?` — ${T.artist}`:""]}):null;return o.jsx("li",{...a,children:o.jsx("button",{type:"button","aria-current":x?"true":void 0,"data-ginger-active":x||void 0,className:r,style:{width:"100%",textAlign:"left",border:"none",background:x?"var(--ginger-playlist-active-bg, rgba(17, 24, 39, 0.06))":"transparent",color:"inherit",font:"inherit",cursor:"pointer",padding:"var(--ginger-playlist-row-padding, 6px 8px)",...n},...s,onClick:g=>{u==null||u(g),!g.defaultPrevented&&(l?y(e):h(e))},children:t??E})})}dr.displayName="Ginger.Playlist.Track";const Xr=Object.assign(cr,{Track:dr}),Kr=L("Ginger.Queue.Title",e=>{var r;return(r=e.playlistMeta)==null?void 0:r.title}),qr=L("Ginger.Queue.Subtitle",e=>{var r;return(r=e.playlistMeta)==null?void 0:r.subtitle}),Wr=L("Ginger.Queue.Description",e=>{var r;return(r=e.playlistMeta)==null?void 0:r.description}),zr=L("Ginger.Queue.Copyright",e=>{var r;return(r=e.playlistMeta)==null?void 0:r.copyright});function fr({className:e,style:r,fallback:n,empty:t,imgStyle:a}){var f,p;const u=k.useGingerState(),s=(f=u.playlistMeta)==null?void 0:f.artworkUrl;if(!s){const y=t??n??null;return y?o.jsx("span",{className:e,style:r,children:y}):null}const l=((p=u.playlistMeta)==null?void 0:p.title)??"Playlist artwork";return o.jsx("span",{className:e,style:{display:"inline-block",background:"var(--ginger-artwork-bg, #f3f4f6)",borderRadius:"var(--ginger-artwork-radius, 6px)",overflow:"hidden",...r},children:o.jsx("img",{src:s,alt:l,style:{display:"block",width:"100%",height:"100%",objectFit:"cover",...a}})})}fr.displayName="Ginger.Queue.Artwork";function Q(e){return Number.isFinite(e)?Math.min(1,Math.max(0,e)):1}function xe(e){return Number.isFinite(e)?Math.min(4,Math.max(.25,e)):1}const D={currentTime:0,duration:0,bufferedFraction:0,isBuffering:!1,errorMessage:null},Jr={...D,volume:1,muted:!1,playbackRate:1};function pr(e){const r=[...e.tracks];let n=I(e.currentIndex??0,r.length),t=null,a=r;return e.isShuffled&&r.length>1&&(t=[...r],a=Ge(r,n),n=0),{tracks:a,currentIndex:n,playbackMode:e.playbackMode??"playlist",isPaused:e.isPaused??!0,isShuffled:!!(e.isShuffled&&a.length>1),repeatMode:e.repeatMode??"off",originalTracks:t,playlistMeta:e.playlistMeta??null,...Jr,volume:Q(e.volume??1),muted:e.muted??!1,playbackRate:xe(e.playbackRate??1)}}function Zr(e,r){switch(r.type){case"INIT":{const{tracks:n,currentIndex:t,playlistMeta:a,isPaused:u,isShuffled:s,repeatMode:l,playbackMode:f,volume:p,muted:y,playbackRate:h}=r.payload;return pr({tracks:n,currentIndex:t,playlistMeta:a??null,isPaused:u??!0,isShuffled:s??!1,repeatMode:l??"off",playbackMode:f??"playlist",volume:p,muted:y,playbackRate:h})}case"SET_QUEUE":{const{tracks:n,currentIndex:t}=r.payload,a=[...n],u=I(t??e.currentIndex,a.length);return{...e,tracks:a,currentIndex:u,isShuffled:!1,originalTracks:null,...D}}case"INSERT_TRACK":{const n=r.payload.index??e.tracks.length,t=je(e.tracks,r.payload.track,n);if(r.payload.autoPlay){const u=I(n,t.length);return{...e,tracks:t,currentIndex:u,isShuffled:!1,originalTracks:null,isPaused:!1,...D}}const a=n<=e.currentIndex?e.currentIndex+1:e.currentIndex;return{...e,tracks:t,isShuffled:!1,originalTracks:null,currentIndex:I(a,t.length)}}case"REMOVE_TRACK":{const n=r.payload.index,t=Er(e.tracks,n),a=n<e.currentIndex?e.currentIndex-1:n===e.currentIndex?Math.min(e.currentIndex,Math.max(0,t.length-1)):e.currentIndex;return{...e,tracks:t,isShuffled:!1,originalTracks:null,currentIndex:I(a,t.length),...n===e.currentIndex?D:{}}}case"MOVE_TRACK":{const{fromIndex:n,toIndex:t}=r.payload,a=Pr(e.tracks,n,t);let u=e.currentIndex;return e.currentIndex===n?u=t:n<e.currentIndex&&t>=e.currentIndex?u-=1:n>e.currentIndex&&t<=e.currentIndex&&(u+=1),{...e,tracks:a,isShuffled:!1,originalTracks:null,currentIndex:I(u,a.length)}}case"ADD_NEXT":{const n=Sr(e.tracks,e.currentIndex,r.payload.track);return{...e,tracks:n,isShuffled:!1,originalTracks:null}}case"SET_INDEX":{const n=I(r.payload.index,e.tracks.length),t=r.payload.autoPlay,a=t===!0?!1:t===!1?!0:e.isPaused;return{...e,currentIndex:n,...D,isPaused:a}}case"PLAY":return{...e,isPaused:!1};case"PAUSE":return{...e,isPaused:!0};case"TOGGLE_PAUSE":return{...e,isPaused:!e.isPaused};case"SET_REPEAT":return{...e,repeatMode:r.payload};case"CYCLE_REPEAT":return{...e,repeatMode:Gr(e.repeatMode)};case"TOGGLE_SHUFFLE":{if(e.tracks.length<=1)return{...e,isShuffled:!1,originalTracks:null};if(!e.isShuffled){const u=[...e.tracks],s=Ge(u,e.currentIndex);return{...e,isShuffled:!0,originalTracks:u,tracks:s,currentIndex:0}}const n=e.originalTracks?[...e.originalTracks]:[...e.tracks],t=e.tracks[e.currentIndex],a=Ir(n,t);return{...e,isShuffled:!1,originalTracks:null,tracks:n,currentIndex:I(a,n.length)}}case"NEXT":{const n=Rr(e),t=n===e.currentIndex;return{...e,currentIndex:n,...t?{}:D,isPaused:t?e.isPaused:!1}}case"PREV":{const n=Cr(e),t=n===e.currentIndex;return{...e,currentIndex:n,...t?{}:D,isPaused:t?e.isPaused:!1}}case"MEDIA_TIME_UPDATE":return{...e,currentTime:r.payload.currentTime,duration:Number.isFinite(r.payload.duration)?r.payload.duration:e.duration,bufferedFraction:r.payload.bufferedFraction,isBuffering:!1};case"MEDIA_LOADED_METADATA":return{...e,duration:Number.isFinite(r.payload.duration)?r.payload.duration:e.duration,bufferedFraction:r.payload.bufferedFraction,errorMessage:null};case"SET_PLAYLIST_META":return{...e,playlistMeta:r.payload};case"MEDIA_ERROR":return{...e,errorMessage:r.payload.message,isPaused:!0,isBuffering:!1};case"MEDIA_WAITING":return{...e,isBuffering:!0};case"MEDIA_CANPLAY":return{...e,isBuffering:!1,errorMessage:null};case"MEDIA_PLAY":return{...e,isPaused:!1,isBuffering:!1};case"MEDIA_PAUSE":return{...e,isPaused:!0};case"RESET_MEDIA_TIMES":return{...e,currentTime:0,duration:0,bufferedFraction:0};case"SET_VOLUME":return{...e,volume:Q(r.payload)};case"SET_MUTED":return{...e,muted:r.payload};case"TOGGLE_MUTE":return{...e,muted:!e.muted};case"SET_PLAYBACK_RATE":return{...e,playbackRate:xe(r.payload)};case"MEDIA_VOLUME_SYNC":{const{volume:n,muted:t}=r.payload,a=Q(n);return a===e.volume&&t===e.muted?e:{...e,volume:a,muted:t}}default:return e}}function en(e){const r=e.tracks[e.currentIndex];return r?{title:r.title,artist:r.artist,album:r.album,artwork:r.artworkUrl?[{src:r.artworkUrl}]:void 0}:{title:"Unknown track"}}function rn(e,r,n){d.useEffect(()=>{if(!e||typeof navigator>"u"||!("mediaSession"in navigator))return;const t=navigator.mediaSession;t.metadata=new MediaMetadata(en(r)),t.playbackState=r.isPaused?"paused":"playing";try{t.setActionHandler("play",n.play),t.setActionHandler("pause",n.pause),t.setActionHandler("nexttrack",n.next),t.setActionHandler("previoustrack",n.prev),t.setActionHandler("seekto",a=>{typeof a.seekTime=="number"&&Number.isFinite(a.seekTime)&&n.seek(a.seekTime)})}catch{}return()=>{try{t.setActionHandler("play",null),t.setActionHandler("pause",null),t.setActionHandler("nexttrack",null),t.setActionHandler("previoustrack",null),t.setActionHandler("seekto",null)}catch{}}},[e,r,n])}const nn={"--ginger-primary-color":"#111827","--ginger-muted-color":"#6b7280","--ginger-font-size":"14px","--ginger-font-family":"system-ui, sans-serif","--ginger-playlist-row-padding":"6px 8px","--ginger-artwork-radius":"6px","--ginger-artwork-bg":"#f3f4f6","--ginger-playlist-active-bg":"rgba(17, 24, 39, 0.06)","--ginger-buffer-color":"rgba(107, 114, 128, 0.35)","--ginger-focus-ring":"0 0 0 2px rgba(59, 130, 246, 0.45)"};function tn({children:e,initialTracks:r=[],initialIndex:n=0,initialPlaylistMeta:t=null,initialShuffle:a=!1,initialRepeatMode:u="off",initialPlaybackMode:s="playlist",initialPaused:l=!0,initialVolume:f=1,initialMuted:p=!1,initialPlaybackRate:y=1,initialStateKey:h,locale:x,mediaSession:T=!1,beforePlay:E,onPlayBlocked:g,persistence:m,hydrateOnMount:R=!1,resumeOnTrackChange:P=!1,className:he,style:H,onTrackChange:X,onPlay:K,onPause:q,onQueueEnd:W,onError:z}){var Ae;const w=d.useRef(null),[i,b]=d.useReducer(Zr,void 0,()=>pr({tracks:r,currentIndex:n,playlistMeta:t,isPaused:l,isShuffled:a,repeatMode:u,playbackMode:s,volume:f,muted:p,playbackRate:y})),Pe=d.useRef(i),Me=d.useRef({tracks:r,currentIndex:n,playlistMeta:t,isPaused:l,isShuffled:a,repeatMode:u,playbackMode:s,volume:f,muted:p,playbackRate:y});Me.current={tracks:r,currentIndex:n,playlistMeta:t,isPaused:l,isShuffled:a,repeatMode:u,playbackMode:s,volume:f,muted:p,playbackRate:y};const $=d.useRef(void 0);d.useEffect(()=>{if(h===void 0){$.current=void 0;return}if($.current===void 0){$.current=h;return}if($.current===h)return;$.current=h;const c=Me.current;b({type:"INIT",payload:{tracks:c.tracks,currentIndex:c.currentIndex,playlistMeta:c.playlistMeta,isPaused:c.isPaused,isShuffled:c.isShuffled,repeatMode:c.repeatMode,playbackMode:c.playbackMode,volume:c.volume,muted:c.muted,playbackRate:c.playbackRate}})},[h,b]),d.useEffect(()=>{Pe.current=i},[i]);const Se=i.tracks[i.currentIndex]??null;d.useEffect(()=>{X==null||X(Se,i.currentIndex)},[Se,i.currentIndex,X]),d.useEffect(()=>{i.errorMessage&&(z==null||z(i.errorMessage))},[i.errorMessage,z]);const J=d.useRef(void 0);d.useEffect(()=>{if(J.current===void 0){J.current=i.isPaused;return}J.current!==i.isPaused&&(i.isPaused?q==null||q():K==null||K()),J.current=i.isPaused},[i.isPaused,q,K]);const j=d.useCallback(()=>{b({type:"PLAY"})},[]),_=d.useCallback(()=>{var c;b({type:"PAUSE"}),(c=w.current)==null||c.pause()},[]),Z=d.useCallback(()=>{i.isPaused?j():_()},[_,j,i.isPaused]),N=d.useCallback(c=>{const M=w.current;M&&Number.isFinite(c)&&(M.currentTime=Math.max(0,c))},[]),ee=d.useCallback(c=>{b({type:"SET_VOLUME",payload:Q(c)})},[]),re=d.useCallback(c=>{b({type:"SET_MUTED",payload:c})},[]),ne=d.useCallback(()=>{b({type:"TOGGLE_MUTE"})},[]),te=d.useCallback(c=>{b({type:"SET_PLAYBACK_RATE",payload:xe(c)})},[]),U=d.useCallback(()=>{b({type:"NEXT"})},[]),V=d.useCallback(()=>{b({type:"PREV"})},[]),ae=d.useCallback(c=>{b({type:"SET_REPEAT",payload:c})},[]),ue=d.useCallback(()=>{b({type:"CYCLE_REPEAT"})},[]),ie=d.useCallback(()=>{b({type:"TOGGLE_SHUFFLE"})},[]),se=d.useCallback((c,M)=>{b({type:"SET_QUEUE",payload:{tracks:c,currentIndex:M}})},[]),le=d.useCallback((c,M,v)=>{b({type:"INSERT_TRACK",payload:{track:c,index:M,autoPlay:v}})},[]),oe=d.useCallback(c=>{b({type:"REMOVE_TRACK",payload:{index:c}})},[]),ce=d.useCallback((c,M)=>{b({type:"MOVE_TRACK",payload:{fromIndex:c,toIndex:M}})},[]),de=d.useCallback(c=>{b({type:"ADD_NEXT",payload:{track:c}})},[]),fe=d.useCallback(c=>{b({type:"SET_INDEX",payload:{index:c,autoPlay:!0}})},[]),pe=d.useCallback(c=>{b({type:"SET_INDEX",payload:{index:c,autoPlay:!1}})},[]),ge=d.useCallback(c=>{b({type:"SET_PLAYLIST_META",payload:c})},[]),ye=d.useCallback(c=>{b({type:"INIT",payload:c})},[]);d.useEffect(()=>{if(!m||!R)return;const c=m.get("ginger:volume"),M=m.get("ginger:muted"),v=m.get("ginger:playbackRate"),C=m.get("ginger:repeatMode"),be=m.get("ginger:currentIndex"),A=Me.current;b({type:"INIT",payload:{tracks:A.tracks,playlistMeta:A.playlistMeta,isPaused:A.isPaused,isShuffled:A.isShuffled,playbackMode:A.playbackMode,currentIndex:typeof be=="number"?be:A.currentIndex,repeatMode:C==="off"||C==="all"||C==="one"?C:A.repeatMode,volume:typeof c=="number"?c:A.volume,muted:typeof M=="boolean"?M:A.muted,playbackRate:typeof v=="number"?v:A.playbackRate}})},[R,m]),d.useEffect(()=>{m&&(m.set("ginger:volume",i.volume),m.set("ginger:muted",i.muted),m.set("ginger:playbackRate",i.playbackRate),m.set("ginger:repeatMode",i.repeatMode),m.set("ginger:currentIndex",i.currentIndex))},[m,i.volume,i.muted,i.playbackRate,i.repeatMode,i.currentIndex]),d.useEffect(()=>{if(!m||!P)return;const c=i.tracks[i.currentIndex];if(!c)return;const M=`ginger:resume:${Y(c)}`,v=m.get(M);typeof v=="number"&&Number.isFinite(v)&&N(v)},[m,P,i.currentIndex,i.tracks,N]),d.useEffect(()=>{if(!m||!P)return;const c=i.tracks[i.currentIndex];if(!c||!(i.currentTime>=0))return;const M=`ginger:resume:${Y(c)}`,v=setTimeout(()=>m.set(M,i.currentTime),250);return()=>clearTimeout(v)},[m,P,i.currentIndex,i.tracks,i.currentTime]);const gr=(Ae=i.tracks[i.currentIndex])==null?void 0:Ae.fileUrl;d.useEffect(()=>{const c=w.current;if(!c)return;if(i.isPaused){c.pause();return}let M=!1;return(async()=>{if(E){let v=!1;try{v=await E()}catch(C){const be=C instanceof Error?C.message:"beforePlay rejected";b({type:"MEDIA_ERROR",payload:{message:be}});return}if(!v){M||(b({type:"PAUSE"}),g==null||g());return}}M||c.play().catch(v=>{const C=v instanceof Error?v.message:typeof v=="string"?v:"Playback failed (e.g. autoplay blocked or unavailable source)";b({type:"MEDIA_ERROR",payload:{message:C}})})})(),()=>{M=!0}},[E,gr,g,i.isPaused]);const me=d.useCallback(()=>{const c=Ar(Pe.current);if(c.kind==="replay_same"){const v=w.current;v&&(v.currentTime=0),b({type:"PLAY"});return}if(c.kind==="stop"){b({type:"PAUSE"}),W==null||W();return}const M=c.nextIndex;b({type:"SET_INDEX",payload:{index:M,autoPlay:!0}})},[W]),yr=d.useMemo(()=>({play:j,pause:_,next:U,prev:V,seek:N}),[j,_,U,V,N]);rn(!!T,i,yr);const mr=x!=null&&x.seek&&/[\u0590-\u08FF]/.test(x.seek)?"rtl":"ltr",br=d.useMemo(()=>({state:i,dispatch:b,audioRef:w,notifyEnded:me,init:ye,play:j,pause:_,togglePlayPause:Z,seek:N,setVolume:ee,setMuted:re,toggleMute:ne,setPlaybackRate:te,next:U,prev:V,setRepeatMode:ae,cycleRepeat:ue,toggleShuffle:ie,setQueue:se,insertTrackAt:le,removeTrackAt:oe,moveTrack:ce,enqueueNext:de,playTrackAt:fe,selectTrackAt:pe,setPlaylistMeta:ge}),[ue,b,ye,U,me,_,j,fe,le,oe,ce,de,pe,V,N,re,te,se,ae,ge,ee,i,ne,Z,ie]),kr=d.useMemo(()=>({tracks:i.tracks,currentIndex:i.currentIndex,isPaused:i.isPaused,isShuffled:i.isShuffled,repeatMode:i.repeatMode,originalTracks:i.originalTracks,playlistMeta:i.playlistMeta,init:ye,play:j,pause:_,togglePlayPause:Z,next:U,prev:V,setRepeatMode:ae,cycleRepeat:ue,toggleShuffle:ie,playbackMode:i.playbackMode,setQueue:se,insertTrackAt:le,removeTrackAt:oe,moveTrack:ce,enqueueNext:de,playTrackAt:fe,selectTrackAt:pe,setPlaylistMeta:ge,dispatch:b}),[i.tracks,i.currentIndex,i.isPaused,i.isShuffled,i.repeatMode,i.playbackMode,i.originalTracks,i.playlistMeta,ye,j,_,Z,U,V,ae,ue,ie,se,le,oe,ce,de,fe,pe,ge,b]),xr=d.useMemo(()=>({currentTime:i.currentTime,duration:i.duration,bufferedFraction:i.bufferedFraction,isBuffering:i.isBuffering,errorMessage:i.errorMessage,volume:i.volume,muted:i.muted,playbackRate:i.playbackRate,seek:N,setVolume:ee,setMuted:re,toggleMute:ne,setPlaybackRate:te,audioRef:w,notifyEnded:me,dispatch:b}),[i.currentTime,i.duration,i.bufferedFraction,i.isBuffering,i.errorMessage,i.volume,i.muted,i.playbackRate,N,ee,re,ne,te,w,me,b]),hr=Te(i),Mr=d.useMemo(()=>({...nn,...H}),[H]);return o.jsx(Yr,{locale:x,children:o.jsx(k.GingerPlaybackContext.Provider,{value:kr,children:o.jsx(k.GingerMediaContext.Provider,{value:xr,children:o.jsx(Ce.Provider,{value:br,children:o.jsx("div",{className:he,style:Mr,"data-ginger-playback":hr,dir:mr,children:e})})})})})}const an={Provider:tn,Player:Tr,Current:{Title:Nr,Artist:wr,Album:Dr,Description:Lr,Copyright:Fr,Genre:Ur,Label:Vr,Isrc:Br,TrackNumber:$r,Year:De,Lyrics:Le,FileUrl:Fe,Artwork:Ue,QueueIndex:Ve,QueueLength:Be,QueuePosition:$e,Elapsed:Oe,Duration:Ye,Remaining:Qe,Progress:He,TimeRail:Xe,BufferRail:Ke,PlaybackState:qe,ErrorMessage:We},Queue:{Title:Kr,Subtitle:qr,Description:Wr,Copyright:zr,Artwork:fr},Control:{PlayPause:rr,Repeat:nr,Next:tr,Previous:ar,Shuffle:ur,SeekBar:ir,Volume:sr,Mute:lr,PlaybackRate:or},Playlist:Xr};exports.Ginger=an;exports.clampPlaybackRate=xe;exports.clampVolume=Q;exports.defaultGingerLocale=O;exports.derivePlaybackUiState=Te;exports.effectiveDuration=B;exports.effectiveRemaining=_e;exports.getCurrentTrack=G;exports.progressFraction=Ie;exports.resolvedAlbumLine=we;exports.resolvedArtwork=Ne;exports.useGingerLocale=S;exports.usePlayPauseBinding=er;exports.useSeekBarBinding=Je;exports.useVolumeSlider=Ze;
2
- //# sourceMappingURL=ginger-B5wfhrC8.cjs.map