@lucaismyname/ginger 0.0.8 → 0.0.9

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 (83) hide show
  1. package/README.md +99 -0
  2. package/dist/GingerSplitContexts-4YZ-OJ9V.js +63 -0
  3. package/dist/GingerSplitContexts-4YZ-OJ9V.js.map +1 -0
  4. package/dist/GingerSplitContexts-Bze1Bqe2.cjs +2 -0
  5. package/dist/GingerSplitContexts-Bze1Bqe2.cjs.map +1 -0
  6. package/dist/audio/GingerPlayer.d.ts +2 -1
  7. package/dist/audio/GingerPlayer.d.ts.map +1 -1
  8. package/dist/client.cjs +2 -0
  9. package/dist/client.cjs.map +1 -0
  10. package/dist/client.d.ts +2 -0
  11. package/dist/client.d.ts.map +1 -0
  12. package/dist/client.js +28 -0
  13. package/dist/client.js.map +1 -0
  14. package/dist/components/current/Playback.d.ts +2 -1
  15. package/dist/components/current/Playback.d.ts.map +1 -1
  16. package/dist/context/GingerContext.d.ts +4 -0
  17. package/dist/context/GingerContext.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/GingerSplitContexts.d.ts +4 -0
  21. package/dist/context/GingerSplitContexts.d.ts.map +1 -1
  22. package/dist/core/playbackReducer.d.ts +1 -0
  23. package/dist/core/playbackReducer.d.ts.map +1 -1
  24. package/dist/core/queue.d.ts +4 -0
  25. package/dist/core/queue.d.ts.map +1 -1
  26. package/dist/core/transitions.d.ts.map +1 -1
  27. package/dist/experimental-gapless/index.cjs +2 -0
  28. package/dist/experimental-gapless/index.cjs.map +1 -0
  29. package/dist/experimental-gapless/index.d.ts +11 -0
  30. package/dist/experimental-gapless/index.d.ts.map +1 -0
  31. package/dist/experimental-gapless/index.js +17 -0
  32. package/dist/experimental-gapless/index.js.map +1 -0
  33. package/dist/ginger-C1fV612c.cjs +2 -0
  34. package/dist/ginger-C1fV612c.cjs.map +1 -0
  35. package/dist/ginger-gb6OJ3eQ.js +1547 -0
  36. package/dist/ginger-gb6OJ3eQ.js.map +1 -0
  37. package/dist/hooks/useGinger.d.ts +4 -0
  38. package/dist/hooks/useGinger.d.ts.map +1 -1
  39. package/dist/hooks/useGingerChapters.d.ts +12 -0
  40. package/dist/hooks/useGingerChapters.d.ts.map +1 -0
  41. package/dist/hooks/useGingerDebugLog.d.ts +2 -0
  42. package/dist/hooks/useGingerDebugLog.d.ts.map +1 -0
  43. package/dist/hooks/useGingerKeyboardShortcuts.d.ts +8 -0
  44. package/dist/hooks/useGingerKeyboardShortcuts.d.ts.map +1 -0
  45. package/dist/hooks/useGingerLyricsSync.d.ts +8 -0
  46. package/dist/hooks/useGingerLyricsSync.d.ts.map +1 -0
  47. package/dist/hooks/useGingerSleepTimer.d.ts +9 -0
  48. package/dist/hooks/useGingerSleepTimer.d.ts.map +1 -0
  49. package/dist/hooks/useSeekDrag.d.ts +8 -0
  50. package/dist/hooks/useSeekDrag.d.ts.map +1 -0
  51. package/dist/index.cjs +1 -1
  52. package/dist/index.cjs.map +1 -1
  53. package/dist/index.d.ts +13 -1
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +25 -1414
  56. package/dist/index.js.map +1 -1
  57. package/dist/internal/lyrics.d.ts +6 -0
  58. package/dist/internal/lyrics.d.ts.map +1 -0
  59. package/dist/internal/lyrics.test.d.ts +2 -0
  60. package/dist/internal/lyrics.test.d.ts.map +1 -0
  61. package/dist/media/useMediaSession.d.ts +10 -0
  62. package/dist/media/useMediaSession.d.ts.map +1 -0
  63. package/dist/testing/index.cjs +91 -0
  64. package/dist/testing/index.cjs.map +1 -0
  65. package/dist/testing/index.d.ts +8 -0
  66. package/dist/testing/index.d.ts.map +1 -0
  67. package/dist/testing/index.js +11076 -0
  68. package/dist/testing/index.js.map +1 -0
  69. package/dist/types.d.ts +46 -0
  70. package/dist/types.d.ts.map +1 -1
  71. package/dist/useSeekDrag-CU-8Mi3A.cjs +2 -0
  72. package/dist/useSeekDrag-CU-8Mi3A.cjs.map +1 -0
  73. package/dist/useSeekDrag-DdlE-Qej.js +174 -0
  74. package/dist/useSeekDrag-DdlE-Qej.js.map +1 -0
  75. package/dist/waveform/index.cjs +2 -0
  76. package/dist/waveform/index.cjs.map +1 -0
  77. package/dist/waveform/index.d.ts +3 -0
  78. package/dist/waveform/index.d.ts.map +1 -0
  79. package/dist/waveform/index.js +40 -0
  80. package/dist/waveform/index.js.map +1 -0
  81. package/dist/waveform/useAudioPeaks.d.ts +7 -0
  82. package/dist/waveform/useAudioPeaks.d.ts.map +1 -0
  83. package/package.json +23 -2
package/dist/types.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { CSSProperties, ReactNode } from 'react';
2
2
  export type RepeatMode = "off" | "all" | "one";
3
+ export type PlaybackMode = "playlist" | "single";
3
4
  /** Default control strings; override via `Ginger.Provider` `locale` prop. */
4
5
  export type GingerLocaleMessages = {
5
6
  seek: string;
@@ -32,6 +33,14 @@ export type Track = {
32
33
  isrc?: string;
33
34
  trackNumber?: number;
34
35
  lyrics?: string;
36
+ lyricsTimed?: Array<{
37
+ time: number;
38
+ text: string;
39
+ }>;
40
+ chapters?: Array<{
41
+ title: string;
42
+ startSeconds: number;
43
+ }>;
35
44
  /** Hint before metadata loads; never overrides finite media duration */
36
45
  durationSeconds?: number;
37
46
  /** App-specific data for custom UI; not read by Ginger core */
@@ -64,6 +73,7 @@ export type GingerMediaSlice = {
64
73
  export type GingerPlaybackSlice = {
65
74
  tracks: Track[];
66
75
  currentIndex: number;
76
+ playbackMode: PlaybackMode;
67
77
  isPaused: boolean;
68
78
  isShuffled: boolean;
69
79
  repeatMode: RepeatMode;
@@ -81,6 +91,7 @@ export type GingerAction = {
81
91
  isPaused?: boolean;
82
92
  isShuffled?: boolean;
83
93
  repeatMode?: RepeatMode;
94
+ playbackMode?: PlaybackMode;
84
95
  volume?: number;
85
96
  muted?: boolean;
86
97
  playbackRate?: number;
@@ -91,6 +102,29 @@ export type GingerAction = {
91
102
  tracks: Track[];
92
103
  currentIndex?: number;
93
104
  };
105
+ } | {
106
+ type: "INSERT_TRACK";
107
+ payload: {
108
+ track: Track;
109
+ index?: number;
110
+ autoPlay?: boolean;
111
+ };
112
+ } | {
113
+ type: "REMOVE_TRACK";
114
+ payload: {
115
+ index: number;
116
+ };
117
+ } | {
118
+ type: "MOVE_TRACK";
119
+ payload: {
120
+ fromIndex: number;
121
+ toIndex: number;
122
+ };
123
+ } | {
124
+ type: "ADD_NEXT";
125
+ payload: {
126
+ track: Track;
127
+ };
94
128
  } | {
95
129
  type: "SET_INDEX";
96
130
  payload: {
@@ -175,6 +209,7 @@ export type GingerProviderProps = {
175
209
  /** When true, shuffle starts enabled (still applies shuffle transform on mount if tracks.length > 1) */
176
210
  initialShuffle?: boolean;
177
211
  initialRepeatMode?: RepeatMode;
212
+ initialPlaybackMode?: PlaybackMode;
178
213
  initialPaused?: boolean;
179
214
  /** 0…1, default 1 */
180
215
  initialVolume?: number;
@@ -188,6 +223,13 @@ export type GingerProviderProps = {
188
223
  initialStateKey?: string | number;
189
224
  /** Override default English strings on built-in controls */
190
225
  locale?: Partial<GingerLocaleMessages>;
226
+ mediaSession?: boolean;
227
+ beforePlay?: () => boolean | Promise<boolean>;
228
+ onPlayBlocked?: () => void;
229
+ persistence?: GingerPersistenceAdapter;
230
+ hydrateOnMount?: boolean;
231
+ resumeOnTrackChange?: boolean;
232
+ respectReducedMotion?: boolean;
191
233
  className?: string;
192
234
  style?: CSSProperties;
193
235
  onTrackChange?: (track: Track | null, index: number) => void;
@@ -196,6 +238,10 @@ export type GingerProviderProps = {
196
238
  onQueueEnd?: () => void;
197
239
  onError?: (message: string) => void;
198
240
  };
241
+ export type GingerPersistenceAdapter = {
242
+ get: (key: string) => unknown;
243
+ set: (key: string, value: unknown) => void;
244
+ };
199
245
  export type DisplayBaseProps = {
200
246
  className?: string;
201
247
  style?: CSSProperties;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";const a=require("react"),g=require("./GingerSplitContexts-Bze1Bqe2.cjs"),y=require("./ginger-C1fV612c.cjs");function P(){const t=g.useGingerPlayback(),e=g.useGingerMedia();return a.useMemo(()=>{const r=g.gingerStateFromContextValues(t,e);return{state:r,currentTrack:y.getCurrentTrack(r),playbackUi:y.derivePlaybackUiState(r),duration:y.effectiveDuration(r),remaining:y.effectiveRemaining(r),progress:y.progressFraction(r),artworkUrl:y.resolvedArtwork(r),albumLine:y.resolvedAlbumLine(r),play:t.play,pause:t.pause,togglePlayPause:t.togglePlayPause,seek:e.seek,setVolume:e.setVolume,setMuted:e.setMuted,toggleMute:e.toggleMute,setPlaybackRate:e.setPlaybackRate,next:t.next,prev:t.prev,setRepeatMode:t.setRepeatMode,cycleRepeat:t.cycleRepeat,toggleShuffle:t.toggleShuffle,setQueue:t.setQueue,insertTrackAt:t.insertTrackAt,removeTrackAt:t.removeTrackAt,moveTrack:t.moveTrack,enqueueNext:t.enqueueNext,playTrackAt:t.playTrackAt,selectTrackAt:t.selectTrackAt,setPlaylistMeta:t.setPlaylistMeta,init:t.init,audioRef:e.audioRef,dispatch:t.dispatch}},[t,e])}function M(t=!0,e={}){const{togglePlayPause:r,next:n,prev:c}=g.useGingerPlayback(),{toggleMute:u}=g.useGingerMedia();a.useEffect(()=>{if(!t||typeof window>"u")return;const s=(e.playPause??" ").toLowerCase(),i=(e.next??"ArrowRight").toLowerCase(),o=(e.previous??"ArrowLeft").toLowerCase(),l=(e.mute??"m").toLowerCase(),d=f=>{const m=f.target;if(m&&["INPUT","TEXTAREA","SELECT"].includes(m.tagName))return;const p=f.key.toLowerCase();p===s?(f.preventDefault(),r()):p===i?(f.preventDefault(),n()):p===o?(f.preventDefault(),c()):p===l&&(f.preventDefault(),u())};return window.addEventListener("keydown",d),()=>window.removeEventListener("keydown",d)},[e.mute,e.next,e.playPause,e.previous,t,n,c,u,r])}function L(){const{tracks:t,currentIndex:e}=g.useGingerPlayback(),{currentTime:r,seek:n}=g.useGingerMedia(),c=a.useMemo(()=>{var i;return[...((i=t[e])==null?void 0:i.chapters)??[]].filter(o=>o&&Number.isFinite(o.startSeconds)&&o.startSeconds>=0).sort((o,l)=>o.startSeconds-l.startSeconds)},[e,t]),u=a.useMemo(()=>{if(c.length===0)return-1;for(let s=c.length-1;s>=0;s-=1)if(r>=c[s].startSeconds)return s;return-1},[r,c]);return{list:c,activeIndex:u,active:u>=0?c[u]??null:null,seekTo:s=>{const i=c[s];i&&n(i.startSeconds)}}}const T=/\[(\d{1,2}):(\d{2})(?:\.(\d{1,3}))?\]/g;function v(t){const e=[];for(const r of t.split(/\r?\n/)){const n=[...r.matchAll(T)];if(n.length===0)continue;const c=r.replace(T,"").trim();for(const u of n){const s=Number(u[1]??0),i=Number(u[2]??0),o=Number((u[3]??"0").padEnd(3,"0")),l=s*60+i+o/1e3;Number.isFinite(l)&&l>=0&&e.push({time:l,text:c})}}return e.sort((r,n)=>r.time-n.time)}function h(){const{tracks:t,currentIndex:e}=g.useGingerPlayback(),{currentTime:r}=g.useGingerMedia(),n=t[e],c=a.useMemo(()=>n?Array.isArray(n.lyricsTimed)&&n.lyricsTimed.length>0?[...n.lyricsTimed].filter(s=>Number.isFinite(s.time)&&s.time>=0).sort((s,i)=>s.time-i.time):typeof n.lyrics=="string"?v(n.lyrics):[]:[],[n]),u=a.useMemo(()=>{for(let s=c.length-1;s>=0;s-=1)if(r>=c[s].time)return s;return-1},[r,c]);return{lines:c,activeIndex:u,activeLine:u>=0?c[u]??null:null}}function G(t){const{durationMs:e,stopAfterTracks:r,respectPause:n=!0,enabled:c=!0,onFire:u}=t,{currentIndex:s,pause:i,isPaused:o}=g.useGingerPlayback(),l=a.useRef(r??0),d=a.useRef(s);a.useEffect(()=>{l.current=r??0},[r]),a.useEffect(()=>{if(!c||!e||e<=0||n&&o)return;const f=setTimeout(()=>{i(),u==null||u()},e);return()=>clearTimeout(f)},[e,c,o,u,i,n]),a.useEffect(()=>{if(!c||!r||r<=0)return;const f=d.current;d.current=s,s!==f&&(l.current-=1,l.current<=0&&(i(),u==null||u()))},[s,c,u,i,r])}function S(t=!1){const e=g.useGingerState(),r=a.useRef(e);a.useEffect(()=>{if(!t||typeof console>"u")return;const n=r.current;n!==e&&console.debug("[ginger]",{from:{currentIndex:n.currentIndex,isPaused:n.isPaused,currentTime:n.currentTime,repeatMode:n.repeatMode},to:{currentIndex:e.currentIndex,isPaused:e.isPaused,currentTime:e.currentTime,repeatMode:e.repeatMode}}),r.current=e},[t,e])}function x(t){return Math.max(0,Math.min(1,t))}function w(t){const{seek:e}=g.useGingerMedia(),[r,n]=a.useState(0),[c,u]=a.useState(!1),s=a.useCallback(i=>{if(!(t>0))return;const o=i.currentTarget,l=o.getBoundingClientRect(),d=p=>{const k=x((p-l.left)/l.width);n(k),e(k*t)};u(!0),o.setPointerCapture(i.pointerId),d(i.clientX);const f=p=>d(p.clientX),m=p=>{d(p.clientX),u(!1),o.releasePointerCapture(i.pointerId),o.removeEventListener("pointermove",f),o.removeEventListener("pointerup",m),o.removeEventListener("pointercancel",m)};o.addEventListener("pointermove",f),o.addEventListener("pointerup",m),o.addEventListener("pointercancel",m)},[t,e]);return{fraction:r,isDragging:c,onPointerDown:s}}exports.parseLrc=v;exports.useGinger=P;exports.useGingerChapters=L;exports.useGingerDebugLog=S;exports.useGingerKeyboardShortcuts=M;exports.useGingerLyricsSync=h;exports.useGingerSleepTimer=G;exports.useSeekDrag=w;
2
+ //# sourceMappingURL=useSeekDrag-CU-8Mi3A.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSeekDrag-CU-8Mi3A.cjs","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 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 = (bindings.mute ?? \"m\").toLowerCase();\n\n const onKeyDown = (event: KeyboardEvent) => {\n const target = event.target as HTMLElement | null;\n if (target && [\"INPUT\", \"TEXTAREA\", \"SELECT\"].includes(target.tagName)) 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 (key === muteKey) {\n event.preventDefault();\n toggleMute();\n }\n };\n window.addEventListener(\"keydown\", onKeyDown);\n return () => window.removeEventListener(\"keydown\", onKeyDown);\n }, [bindings.mute, bindings.next, bindings.playPause, bindings.previous, enabled, 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 } from \"../context/GingerSplitContexts\";\n\nexport type SeekDragState = {\n fraction: 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 { seek } = useGingerMedia();\n const [fraction, setFraction] = useState(0);\n const [isDragging, setIsDragging] = useState(false);\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, 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","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","fraction","setFraction","useState","isDragging","setIsDragging","onPointerDown","useCallback","rect","update","clientX","ratio","onMove","moveEvent","onUp","upEvent"],"mappings":"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"}
@@ -0,0 +1,174 @@
1
+ import { useMemo as g, useEffect as m, useRef as v, useState as P, useCallback as x } from "react";
2
+ import { b as y, u as k, g as L, c as w } from "./GingerSplitContexts-4YZ-OJ9V.js";
3
+ import { r as M, h as A, p as S, i as R, j as E, b as I, k as b } from "./ginger-gb6OJ3eQ.js";
4
+ function U() {
5
+ const t = y(), e = k();
6
+ return g(
7
+ () => {
8
+ const r = L(t, e);
9
+ return {
10
+ state: r,
11
+ currentTrack: b(r),
12
+ playbackUi: I(r),
13
+ duration: E(r),
14
+ remaining: R(r),
15
+ progress: S(r),
16
+ artworkUrl: A(r),
17
+ albumLine: M(r),
18
+ play: t.play,
19
+ pause: t.pause,
20
+ togglePlayPause: t.togglePlayPause,
21
+ seek: e.seek,
22
+ setVolume: e.setVolume,
23
+ setMuted: e.setMuted,
24
+ toggleMute: e.toggleMute,
25
+ setPlaybackRate: e.setPlaybackRate,
26
+ next: t.next,
27
+ prev: t.prev,
28
+ setRepeatMode: t.setRepeatMode,
29
+ cycleRepeat: t.cycleRepeat,
30
+ toggleShuffle: t.toggleShuffle,
31
+ setQueue: t.setQueue,
32
+ insertTrackAt: t.insertTrackAt,
33
+ removeTrackAt: t.removeTrackAt,
34
+ moveTrack: t.moveTrack,
35
+ enqueueNext: t.enqueueNext,
36
+ playTrackAt: t.playTrackAt,
37
+ selectTrackAt: t.selectTrackAt,
38
+ setPlaylistMeta: t.setPlaylistMeta,
39
+ init: t.init,
40
+ audioRef: e.audioRef,
41
+ dispatch: t.dispatch
42
+ };
43
+ },
44
+ [t, e]
45
+ );
46
+ }
47
+ function X(t = !0, e = {}) {
48
+ const { togglePlayPause: r, next: n, prev: a } = y(), { toggleMute: o } = k();
49
+ m(() => {
50
+ if (!t || typeof window > "u") return;
51
+ const s = (e.playPause ?? " ").toLowerCase(), i = (e.next ?? "ArrowRight").toLowerCase(), c = (e.previous ?? "ArrowLeft").toLowerCase(), u = (e.mute ?? "m").toLowerCase(), p = (l) => {
52
+ const d = l.target;
53
+ if (d && ["INPUT", "TEXTAREA", "SELECT"].includes(d.tagName)) return;
54
+ const f = l.key.toLowerCase();
55
+ f === s ? (l.preventDefault(), r()) : f === i ? (l.preventDefault(), n()) : f === c ? (l.preventDefault(), a()) : f === u && (l.preventDefault(), o());
56
+ };
57
+ return window.addEventListener("keydown", p), () => window.removeEventListener("keydown", p);
58
+ }, [e.mute, e.next, e.playPause, e.previous, t, n, a, o, r]);
59
+ }
60
+ function F() {
61
+ const { tracks: t, currentIndex: e } = y(), { currentTime: r, seek: n } = k(), a = g(() => {
62
+ var i;
63
+ return [...((i = t[e]) == null ? void 0 : i.chapters) ?? []].filter((c) => c && Number.isFinite(c.startSeconds) && c.startSeconds >= 0).sort((c, u) => c.startSeconds - u.startSeconds);
64
+ }, [e, t]), o = g(() => {
65
+ if (a.length === 0) return -1;
66
+ for (let s = a.length - 1; s >= 0; s -= 1)
67
+ if (r >= a[s].startSeconds) return s;
68
+ return -1;
69
+ }, [r, a]);
70
+ return {
71
+ list: a,
72
+ activeIndex: o,
73
+ active: o >= 0 ? a[o] ?? null : null,
74
+ seekTo: (s) => {
75
+ const i = a[s];
76
+ i && n(i.startSeconds);
77
+ }
78
+ };
79
+ }
80
+ const h = /\[(\d{1,2}):(\d{2})(?:\.(\d{1,3}))?\]/g;
81
+ function C(t) {
82
+ const e = [];
83
+ for (const r of t.split(/\r?\n/)) {
84
+ const n = [...r.matchAll(h)];
85
+ if (n.length === 0) continue;
86
+ const a = r.replace(h, "").trim();
87
+ for (const o of n) {
88
+ const s = Number(o[1] ?? 0), i = Number(o[2] ?? 0), c = Number((o[3] ?? "0").padEnd(3, "0")), u = s * 60 + i + c / 1e3;
89
+ Number.isFinite(u) && u >= 0 && e.push({ time: u, text: a });
90
+ }
91
+ }
92
+ return e.sort((r, n) => r.time - n.time);
93
+ }
94
+ function V() {
95
+ const { tracks: t, currentIndex: e } = y(), { currentTime: r } = k(), n = t[e], a = g(() => n ? Array.isArray(n.lyricsTimed) && n.lyricsTimed.length > 0 ? [...n.lyricsTimed].filter((s) => Number.isFinite(s.time) && s.time >= 0).sort((s, i) => s.time - i.time) : typeof n.lyrics == "string" ? C(n.lyrics) : [] : [], [n]), o = g(() => {
96
+ for (let s = a.length - 1; s >= 0; s -= 1)
97
+ if (r >= a[s].time) return s;
98
+ return -1;
99
+ }, [r, a]);
100
+ return {
101
+ lines: a,
102
+ activeIndex: o,
103
+ activeLine: o >= 0 ? a[o] ?? null : null
104
+ };
105
+ }
106
+ function q(t) {
107
+ const { durationMs: e, stopAfterTracks: r, respectPause: n = !0, enabled: a = !0, onFire: o } = t, { currentIndex: s, pause: i, isPaused: c } = y(), u = v(r ?? 0), p = v(s);
108
+ m(() => {
109
+ u.current = r ?? 0;
110
+ }, [r]), m(() => {
111
+ if (!a || !e || e <= 0 || n && c) return;
112
+ const l = setTimeout(() => {
113
+ i(), o == null || o();
114
+ }, e);
115
+ return () => clearTimeout(l);
116
+ }, [e, a, c, o, i, n]), m(() => {
117
+ if (!a || !r || r <= 0) return;
118
+ const l = p.current;
119
+ p.current = s, s !== l && (u.current -= 1, u.current <= 0 && (i(), o == null || o()));
120
+ }, [s, a, o, i, r]);
121
+ }
122
+ function Q(t = !1) {
123
+ const e = w(), r = v(e);
124
+ m(() => {
125
+ if (!t || typeof console > "u") return;
126
+ const n = r.current;
127
+ n !== e && console.debug("[ginger]", {
128
+ from: {
129
+ currentIndex: n.currentIndex,
130
+ isPaused: n.isPaused,
131
+ currentTime: n.currentTime,
132
+ repeatMode: n.repeatMode
133
+ },
134
+ to: {
135
+ currentIndex: e.currentIndex,
136
+ isPaused: e.isPaused,
137
+ currentTime: e.currentTime,
138
+ repeatMode: e.repeatMode
139
+ }
140
+ }), r.current = e;
141
+ }, [t, e]);
142
+ }
143
+ function D(t) {
144
+ return Math.max(0, Math.min(1, t));
145
+ }
146
+ function j(t) {
147
+ const { seek: e } = k(), [r, n] = P(0), [a, o] = P(!1), s = x(
148
+ (i) => {
149
+ if (!(t > 0)) return;
150
+ const c = i.currentTarget, u = c.getBoundingClientRect(), p = (f) => {
151
+ const T = D((f - u.left) / u.width);
152
+ n(T), e(T * t);
153
+ };
154
+ o(!0), c.setPointerCapture(i.pointerId), p(i.clientX);
155
+ const l = (f) => p(f.clientX), d = (f) => {
156
+ p(f.clientX), o(!1), c.releasePointerCapture(i.pointerId), c.removeEventListener("pointermove", l), c.removeEventListener("pointerup", d), c.removeEventListener("pointercancel", d);
157
+ };
158
+ c.addEventListener("pointermove", l), c.addEventListener("pointerup", d), c.addEventListener("pointercancel", d);
159
+ },
160
+ [t, e]
161
+ );
162
+ return { fraction: r, isDragging: a, onPointerDown: s };
163
+ }
164
+ export {
165
+ F as a,
166
+ Q as b,
167
+ X as c,
168
+ V as d,
169
+ q as e,
170
+ j as f,
171
+ C as p,
172
+ U as u
173
+ };
174
+ //# sourceMappingURL=useSeekDrag-DdlE-Qej.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSeekDrag-DdlE-Qej.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 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 = (bindings.mute ?? \"m\").toLowerCase();\n\n const onKeyDown = (event: KeyboardEvent) => {\n const target = event.target as HTMLElement | null;\n if (target && [\"INPUT\", \"TEXTAREA\", \"SELECT\"].includes(target.tagName)) 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 (key === muteKey) {\n event.preventDefault();\n toggleMute();\n }\n };\n window.addEventListener(\"keydown\", onKeyDown);\n return () => window.removeEventListener(\"keydown\", onKeyDown);\n }, [bindings.mute, bindings.next, bindings.playPause, bindings.previous, enabled, 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 } from \"../context/GingerSplitContexts\";\n\nexport type SeekDragState = {\n fraction: 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 { seek } = useGingerMedia();\n const [fraction, setFraction] = useState(0);\n const [isDragging, setIsDragging] = useState(false);\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, 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","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","fraction","setFraction","useState","isDragging","setIsDragging","onPointerDown","useCallback","rect","update","clientX","ratio","onMove","moveEvent","onUp","upEvent"],"mappings":"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"}
@@ -0,0 +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;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +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"}
@@ -0,0 +1,3 @@
1
+ export { useAudioPeaks } from './useAudioPeaks';
2
+ export type { UseAudioPeaksState } from './useAudioPeaks';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/waveform/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,40 @@
1
+ import { useState as m, useEffect as x } from "react";
2
+ function M(t, o = 64) {
3
+ const [h, a] = m({
4
+ peaks: [],
5
+ isLoading: !1,
6
+ error: null
7
+ });
8
+ return x(() => {
9
+ if (!t) {
10
+ a({ peaks: [], isLoading: !1, error: null });
11
+ return;
12
+ }
13
+ let r = !1;
14
+ return a((e) => ({ ...e, isLoading: !0, error: null })), (async () => {
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 = [];
17
+ for (let n = 0; n < o; n += 1) {
18
+ let i = 0;
19
+ const d = n * u, g = Math.min(s.length, d + u);
20
+ for (let f = d; f < g; f += 1)
21
+ i = Math.max(i, Math.abs(s[f] ?? 0));
22
+ c.push(i);
23
+ }
24
+ await l.close(), r || a({ peaks: c, isLoading: !1, error: null });
25
+ } catch (e) {
26
+ r || a({
27
+ peaks: [],
28
+ isLoading: !1,
29
+ error: e instanceof Error ? e.message : "Failed to decode peaks"
30
+ });
31
+ }
32
+ })(), () => {
33
+ r = !0;
34
+ };
35
+ }, [o, t]), h;
36
+ }
37
+ export {
38
+ M as useAudioPeaks
39
+ };
40
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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;"}
@@ -0,0 +1,7 @@
1
+ export type UseAudioPeaksState = {
2
+ peaks: number[];
3
+ isLoading: boolean;
4
+ error: string | null;
5
+ };
6
+ export declare function useAudioPeaks(fileUrl: string | null | undefined, buckets?: number): UseAudioPeaksState;
7
+ //# sourceMappingURL=useAudioPeaks.d.ts.map
@@ -0,0 +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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lucaismyname/ginger",
3
- "version": "0.0.8",
4
- "description": "Headless React audio player with compound components",
3
+ "version": "0.0.9",
4
+ "description": "A headless React audio player",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "files": [
@@ -17,6 +17,26 @@
17
17
  "types": "./dist/index.d.ts",
18
18
  "import": "./dist/index.js",
19
19
  "require": "./dist/index.cjs"
20
+ },
21
+ "./client": {
22
+ "types": "./dist/client.d.ts",
23
+ "import": "./dist/client.js",
24
+ "require": "./dist/client.cjs"
25
+ },
26
+ "./testing": {
27
+ "types": "./dist/testing/index.d.ts",
28
+ "import": "./dist/testing/index.js",
29
+ "require": "./dist/testing/index.cjs"
30
+ },
31
+ "./waveform": {
32
+ "types": "./dist/waveform/index.d.ts",
33
+ "import": "./dist/waveform/index.js",
34
+ "require": "./dist/waveform/index.cjs"
35
+ },
36
+ "./experimental-gapless": {
37
+ "types": "./dist/experimental-gapless/index.d.ts",
38
+ "import": "./dist/experimental-gapless/index.js",
39
+ "require": "./dist/experimental-gapless/index.cjs"
20
40
  }
21
41
  },
22
42
  "scripts": {
@@ -34,6 +54,7 @@
34
54
  }
35
55
  },
36
56
  "devDependencies": {
57
+ "@testing-library/react": "^16.3.0",
37
58
  "@types/react": "^18.3.12",
38
59
  "@types/react-dom": "^18.3.1",
39
60
  "@vitejs/plugin-react": "^4.3.4",