@lucaismyname/ginger 0.0.38 → 0.0.41

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 (36) hide show
  1. package/README.md +15 -16
  2. package/dist/client.cjs +1 -1
  3. package/dist/client.js +2 -2
  4. package/dist/context/GingerProvider.d.ts.map +1 -1
  5. package/dist/equalizer/useGingerEqualizer.test.d.ts +2 -0
  6. package/dist/equalizer/useGingerEqualizer.test.d.ts.map +1 -0
  7. package/dist/{ginger-Ca8910_n.js → ginger-CgHqHrrG.js} +309 -304
  8. package/dist/{ginger-Ca8910_n.js.map → ginger-CgHqHrrG.js.map} +1 -1
  9. package/dist/{ginger-DrD8F4HX.cjs → ginger-DFdZGaMi.cjs} +2 -2
  10. package/dist/{ginger-DrD8F4HX.cjs.map → ginger-DFdZGaMi.cjs.map} +1 -1
  11. package/dist/index.cjs +1 -1
  12. package/dist/index.js +2 -2
  13. package/dist/store.test.d.ts +2 -0
  14. package/dist/store.test.d.ts.map +1 -0
  15. package/dist/testing/index.cjs +1 -1
  16. package/dist/testing/index.js +1 -1
  17. package/dist/transcript/index.cjs +7 -7
  18. package/dist/transcript/index.cjs.map +1 -1
  19. package/dist/transcript/index.js +57 -52
  20. package/dist/transcript/index.js.map +1 -1
  21. package/dist/transcript/useGingerTranscriptSync.d.ts.map +1 -1
  22. package/dist/transcript/useGingerTranscriptSync.test.d.ts +2 -0
  23. package/dist/transcript/useGingerTranscriptSync.test.d.ts.map +1 -0
  24. package/dist/{useGingerChapterProgress-TeWWJ8Fd.cjs → useGingerChapterProgress-COLWYX2-.cjs} +2 -2
  25. package/dist/{useGingerChapterProgress-TeWWJ8Fd.cjs.map → useGingerChapterProgress-COLWYX2-.cjs.map} +1 -1
  26. package/dist/{useGingerChapterProgress-Dbwiwnko.js → useGingerChapterProgress-Jj_zfnds.js} +2 -2
  27. package/dist/{useGingerChapterProgress-Dbwiwnko.js.map → useGingerChapterProgress-Jj_zfnds.js.map} +1 -1
  28. package/dist/waveform/index.cjs +1 -1
  29. package/dist/waveform/index.cjs.map +1 -1
  30. package/dist/waveform/index.d.ts +1 -1
  31. package/dist/waveform/index.d.ts.map +1 -1
  32. package/dist/waveform/index.js +129 -129
  33. package/dist/waveform/index.js.map +1 -1
  34. package/dist/waveform/useAudioPeaks.d.ts +14 -1
  35. package/dist/waveform/useAudioPeaks.d.ts.map +1 -1
  36. package/package.json +6 -4
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./ginger-DrD8F4HX.cjs"),s=require("./useGinger-BXgia32v.cjs"),r=require("./useGingerChapterProgress-TeWWJ8Fd.cjs"),i=require("./liveAudioGraph-0cpHD_Ic.cjs"),n=require("./selectors-YXnP8Y8g.cjs"),a=require("./GingerSplitContexts-C7puo0M7.cjs");exports.Chapters=e.Chapters;exports.Ginger=e.Ginger;exports.LyricsSynced=e.LyricsSynced;exports.Pause=e.Pause;exports.Play=e.Play;exports.RepeatGlyph=e.RepeatGlyph;exports.ShuffleIcon=e.ShuffleIcon;exports.SkipBack=e.SkipBack;exports.SkipForward=e.SkipForward;exports.Volume2=e.Volume2;exports.VolumeX=e.VolumeX;exports.Wrapper=e.Wrapper;exports.clampPlaybackRate=e.clampPlaybackRate;exports.clampVolume=e.clampVolume;exports.defaultGingerLocale=e.defaultGingerLocale;exports.parseLrc=e.parseLrc;exports.useGingerChapters=e.useGingerChapters;exports.useGingerLocale=e.useGingerLocale;exports.useGingerLyricsSync=e.useGingerLyricsSync;exports.usePlayPauseBinding=e.usePlayPauseBinding;exports.useSeekBarBinding=e.useSeekBarBinding;exports.useVolumeSlider=e.useVolumeSlider;exports.useGinger=s.useGinger;exports.createGingerStore=r.createGingerStore;exports.useGingerChapterProgress=r.useGingerChapterProgress;exports.useGingerDebugLog=r.useGingerDebugLog;exports.useGingerKeyboardShortcuts=r.useGingerKeyboardShortcuts;exports.useGingerLiveAnalyzer=r.useGingerLiveAnalyzer;exports.useGingerPlaybackHistory=r.useGingerPlaybackHistory;exports.useGingerSleepTimer=r.useGingerSleepTimer;exports.useGingerVolumeFade=r.useGingerVolumeFade;exports.useNextTrackPrefetch=r.useNextTrackPrefetch;exports.useSeekDrag=r.useSeekDrag;exports.attachLiveAnalyser=i.attachLiveAnalyser;exports.detachLiveAnalyser=i.detachLiveAnalyser;exports.setProcessingChain=i.setProcessingChain;exports.derivePlaybackUiState=n.derivePlaybackUiState;exports.gingerStateFromContextValues=a.gingerStateFromContextValues;exports.gingerStateFromContexts=a.gingerStateFromContexts;exports.useGingerMedia=a.useGingerMedia;exports.useGingerPlayback=a.useGingerPlayback;exports.useGingerState=a.useGingerState;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./ginger-DFdZGaMi.cjs"),s=require("./useGinger-BXgia32v.cjs"),r=require("./useGingerChapterProgress-COLWYX2-.cjs"),i=require("./liveAudioGraph-0cpHD_Ic.cjs"),n=require("./selectors-YXnP8Y8g.cjs"),a=require("./GingerSplitContexts-C7puo0M7.cjs");exports.Chapters=e.Chapters;exports.Ginger=e.Ginger;exports.LyricsSynced=e.LyricsSynced;exports.Pause=e.Pause;exports.Play=e.Play;exports.RepeatGlyph=e.RepeatGlyph;exports.ShuffleIcon=e.ShuffleIcon;exports.SkipBack=e.SkipBack;exports.SkipForward=e.SkipForward;exports.Volume2=e.Volume2;exports.VolumeX=e.VolumeX;exports.Wrapper=e.Wrapper;exports.clampPlaybackRate=e.clampPlaybackRate;exports.clampVolume=e.clampVolume;exports.defaultGingerLocale=e.defaultGingerLocale;exports.parseLrc=e.parseLrc;exports.useGingerChapters=e.useGingerChapters;exports.useGingerLocale=e.useGingerLocale;exports.useGingerLyricsSync=e.useGingerLyricsSync;exports.usePlayPauseBinding=e.usePlayPauseBinding;exports.useSeekBarBinding=e.useSeekBarBinding;exports.useVolumeSlider=e.useVolumeSlider;exports.useGinger=s.useGinger;exports.createGingerStore=r.createGingerStore;exports.useGingerChapterProgress=r.useGingerChapterProgress;exports.useGingerDebugLog=r.useGingerDebugLog;exports.useGingerKeyboardShortcuts=r.useGingerKeyboardShortcuts;exports.useGingerLiveAnalyzer=r.useGingerLiveAnalyzer;exports.useGingerPlaybackHistory=r.useGingerPlaybackHistory;exports.useGingerSleepTimer=r.useGingerSleepTimer;exports.useGingerVolumeFade=r.useGingerVolumeFade;exports.useNextTrackPrefetch=r.useNextTrackPrefetch;exports.useSeekDrag=r.useSeekDrag;exports.attachLiveAnalyser=i.attachLiveAnalyser;exports.detachLiveAnalyser=i.detachLiveAnalyser;exports.setProcessingChain=i.setProcessingChain;exports.derivePlaybackUiState=n.derivePlaybackUiState;exports.gingerStateFromContextValues=a.gingerStateFromContextValues;exports.gingerStateFromContexts=a.gingerStateFromContexts;exports.useGingerMedia=a.useGingerMedia;exports.useGingerPlayback=a.useGingerPlayback;exports.useGingerState=a.useGingerState;
2
2
  //# sourceMappingURL=index.cjs.map
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { C as s, G as r, L as i, P as t, a as u, R as n, S as o, b as g, c, V as l, d as p, W as G, e as m, f as y, g as S, p as d, u as f, h, i as P, j as k, k as L, l as b } from "./ginger-Ca8910_n.js";
1
+ import { C as s, G as r, L as i, P as t, a as u, R as n, S as o, b as g, c, V as l, d as p, W as G, e as m, f as y, g as S, p as d, u as f, h, i as P, j as k, k as L, l as b } from "./ginger-CgHqHrrG.js";
2
2
  import { u as C } from "./useGinger-hpp2pAGY.js";
3
- import { c as v, u as B, a as F, b as A, d as R, e as D, f as T, g as W, h as j, i as w } from "./useGingerChapterProgress-Dbwiwnko.js";
3
+ import { c as v, u as B, a as F, b as A, d as R, e as D, f as T, g as W, h as j, i as w } from "./useGingerChapterProgress-Jj_zfnds.js";
4
4
  import { a as H, d as I, s as K } from "./liveAudioGraph-DvPaxBCP.js";
5
5
  import { d as N } from "./selectors-BalBCc7X.js";
6
6
  import { g as X, a as q, u as E, b as J, c as O } from "./GingerSplitContexts-BzBExb95.js";
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=store.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.test.d.ts","sourceRoot":"","sources":["../src/store.test.ts"],"names":[],"mappings":""}
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const Al=require("react/jsx-runtime"),Xi=require("react"),yt=require("react-dom"),Il=require("../ginger-DrD8F4HX.cjs");function Pc(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const r in e)if(r!=="default"){const n=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,n.get?n:{enumerable:!0,get:()=>e[r]})}}return t.default=e,Object.freeze(t)}const ur=Pc(Xi);function Tc(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var wi={exports:{}},ne={};/**
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const Al=require("react/jsx-runtime"),Xi=require("react"),yt=require("react-dom"),Il=require("../ginger-DFdZGaMi.cjs");function Pc(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const r in e)if(r!=="default"){const n=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,n.get?n:{enumerable:!0,get:()=>e[r]})}}return t.default=e,Object.freeze(t)}const ur=Pc(Xi);function Tc(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var wi={exports:{}},ne={};/**
2
2
  * @license React
3
3
  * react-dom-test-utils.production.min.js
4
4
  *
@@ -2,7 +2,7 @@ import { jsxs as Rc, jsx as Pc } from "react/jsx-runtime";
2
2
  import * as ur from "react";
3
3
  import Us from "react";
4
4
  import yt from "react-dom";
5
- import { G as Ml } from "../ginger-Ca8910_n.js";
5
+ import { G as Ml } from "../ginger-CgHqHrrG.js";
6
6
  function Tc(e) {
7
7
  return e && e.__esModule && Object.prototype.hasOwnProperty.call(e, "default") ? e.default : e;
8
8
  }
@@ -1,8 +1,8 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("react"),v=require("../GingerSplitContexts-C7puo0M7.cjs");function h(n){return n.replace(/<[^>]+>/g,"").trim()}function T(n){const r=n.trim().replace(",",".").split(":");if(r.length===3){const s=Number(r[0]),t=Number(r[1]),c=Number(r[2]);return[s,t,c].every(Number.isFinite)?s*3600+t*60+c:Number.NaN}if(r.length===2){const s=Number(r[0]),t=Number(r[1]);return[s,t].every(Number.isFinite)?s*60+t:Number.NaN}return Number.NaN}function S(n){const i=n.indexOf("-->");if(i<0)return null;const r=n.slice(0,i).trim(),t=n.slice(i+3).trim().match(/^(\S+)/);return t?{start:r,end:t[1]}:null}function N(n){const i=[],r=n.replace(/\r\n/g,`
2
- `).split(/\n\s*\n/);for(const s of r){const t=s.split(`
3
- `).map(a=>a.trim()).filter(a=>a.length>0);if(t.length===0)continue;let c=0;/^\d+$/.test(t[0])&&(c=1);const o=t[c];if(!o)continue;const e=S(o);if(!e)continue;const m=T(e.start),u=T(e.end);if(!Number.isFinite(m)||!Number.isFinite(u))continue;const l=t.slice(c+1),f=h(l.join(`
4
- `));f&&i.push({startTime:m,endTime:u,text:f})}return i.sort((s,t)=>s.startTime-t.startTime)}function b(n){const i=[];let r=n.replace(/\r\n/g,`
5
- `);if(r.startsWith("WEBVTT")){const t=r.search(/\n\s*\n/);r=t>=0?r.slice(t).trim():""}const s=r.split(/\n\s*\n/);for(const t of s){const o=t.split(`
6
- `).map(p=>p.trimEnd());if(o.length===0||o[0].startsWith("NOTE")||o[0].startsWith("STYLE")||o[0].startsWith("REGION"))continue;let e=0,m,u=o[e];if(u.includes("-->")||(m=o[e],e+=1,u=o[e]),!(u!=null&&u.includes("-->")))continue;const l=S(u);if(!l)continue;const f=T(l.start),a=T(l.end);if(!Number.isFinite(f)||!Number.isFinite(a))continue;const F=o.slice(e+1).filter(p=>p.trim().length>0),g=h(F.join(`
7
- `));g&&i.push({startTime:f,endTime:a,text:g,...m?{id:m}:{}})}return i.sort((t,c)=>t.startTime-c.startTime)}function y(n){return n.trimStart().startsWith("WEBVTT")?b(n):N(n)}function x(n,i){return i==="vtt"?b(n):i==="srt"?N(n):y(n)}function W(n){const{transcript:i,format:r="auto"}=n,{currentTime:s}=v.useGingerMedia(),t=d.useMemo(()=>Array.isArray(i)?[...i].filter(e=>Number.isFinite(e.startTime)&&Number.isFinite(e.endTime)&&e.startTime>=0&&e.endTime>=e.startTime).sort((e,m)=>e.startTime-m.startTime):x(i,r),[i,r]),c=d.useMemo(()=>{for(let e=t.length-1;e>=0;e-=1)if(s>=t[e].startTime)return e;return-1},[s,t]),o=d.useMemo(()=>t.filter(e=>s>=e.startTime&&s<e.endTime),[s,t]);return{cues:t,activeIndex:c,activeCue:c>=0?t[c]??null:null,activeCues:o}}exports.parseSrt=N;exports.parseTimestampToSeconds=T;exports.parseTranscriptAuto=y;exports.parseVtt=b;exports.useGingerTranscriptSync=W;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("react"),F=require("../GingerSplitContexts-C7puo0M7.cjs");function g(n){return n.replace(/<[^>]+>/g,"").trim()}function T(n){const e=n.trim().replace(",",".").split(":");if(e.length===3){const i=Number(e[0]),t=Number(e[1]),s=Number(e[2]);return[i,t,s].every(Number.isFinite)?i*3600+t*60+s:Number.NaN}if(e.length===2){const i=Number(e[0]),t=Number(e[1]);return[i,t].every(Number.isFinite)?i*60+t:Number.NaN}return Number.NaN}function S(n){const r=n.indexOf("-->");if(r<0)return null;const e=n.slice(0,r).trim(),t=n.slice(r+3).trim().match(/^(\S+)/);return t?{start:e,end:t[1]}:null}function b(n){const r=[],e=n.replace(/\r\n/g,`
2
+ `).split(/\n\s*\n/);for(const i of e){const t=i.split(`
3
+ `).map(m=>m.trim()).filter(m=>m.length>0);if(t.length===0)continue;let s=0;/^\d+$/.test(t[0])&&(s=1);const c=t[s];if(!c)continue;const o=S(c);if(!o)continue;const a=T(o.start),u=T(o.end);if(!Number.isFinite(a)||!Number.isFinite(u))continue;const l=t.slice(s+1),f=g(l.join(`
4
+ `));f&&r.push({startTime:a,endTime:u,text:f})}return r.sort((i,t)=>i.startTime-t.startTime)}function N(n){const r=[];let e=n.replace(/\r\n/g,`
5
+ `);if(e.startsWith("WEBVTT")){const t=e.search(/\n\s*\n/);e=t>=0?e.slice(t).trim():""}const i=e.split(/\n\s*\n/);for(const t of i){const c=t.split(`
6
+ `).map(p=>p.trimEnd());if(c.length===0||c[0].startsWith("NOTE")||c[0].startsWith("STYLE")||c[0].startsWith("REGION"))continue;let o=0,a,u=c[o];if(u.includes("-->")||(a=c[o],o+=1,u=c[o]),!(u!=null&&u.includes("-->")))continue;const l=S(u);if(!l)continue;const f=T(l.start),m=T(l.end);if(!Number.isFinite(f)||!Number.isFinite(m))continue;const x=c.slice(o+1).filter(p=>p.trim().length>0),h=g(x.join(`
7
+ `));h&&r.push({startTime:f,endTime:m,text:h,...a?{id:a}:{}})}return r.sort((t,s)=>t.startTime-s.startTime)}function y(n){return n.trimStart().startsWith("WEBVTT")?N(n):b(n)}function v(n,r){return r==="vtt"?N(n):r==="srt"?b(n):y(n)}function L(n,r){let e=0,i=n.length-1,t=-1;for(;e<=i;){const s=e+Math.floor((i-e)/2),c=n[s];if(!c)break;c.startTime<=r?(t=s,e=s+1):i=s-1}return t}function M(n){const{transcript:r,format:e="auto"}=n,{currentTime:i}=F.useGingerMedia(),t=d.useMemo(()=>Array.isArray(r)?[...r].filter(o=>Number.isFinite(o.startTime)&&Number.isFinite(o.endTime)&&o.startTime>=0&&o.endTime>=o.startTime).sort((o,a)=>o.startTime-a.startTime):v(r,e),[r,e]),s=d.useMemo(()=>L(t,i),[i,t]),c=d.useMemo(()=>s<0?[]:t.slice(0,s+1).filter(o=>i>=o.startTime&&i<o.endTime),[i,t,s]);return{cues:t,activeIndex:s,activeCue:s>=0?t[s]??null:null,activeCues:c}}exports.parseSrt=b;exports.parseTimestampToSeconds=T;exports.parseTranscriptAuto=y;exports.parseVtt=N;exports.useGingerTranscriptSync=M;
8
8
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../src/transcript/parseTranscript.ts","../../src/transcript/useGingerTranscriptSync.ts"],"sourcesContent":["/**\n * A single timed transcript cue (SRT / WebVTT).\n */\nexport type TranscriptCue = {\n /** Start time in seconds. */\n startTime: number;\n /** End time in seconds. */\n endTime: number;\n text: string;\n /** Present for WebVTT cues that declare an identifier line. */\n id?: string;\n};\n\nfunction stripHtmlTags(text: string): string {\n return text.replace(/<[^>]+>/g, \"\").trim();\n}\n\n/** Parse `HH:MM:SS.mmm` / `HH:MM:SS,mmm` / `MM:SS.mmm` (WebVTT) to seconds. */\nexport function parseTimestampToSeconds(raw: string): number {\n const t = raw.trim().replace(\",\", \".\");\n const segs = t.split(\":\");\n if (segs.length === 3) {\n const h = Number(segs[0]);\n const m = Number(segs[1]);\n const sec = Number(segs[2]);\n if (![h, m, sec].every(Number.isFinite)) return Number.NaN;\n return h * 3600 + m * 60 + sec;\n }\n if (segs.length === 2) {\n const m = Number(segs[0]);\n const sec = Number(segs[1]);\n if (![m, sec].every(Number.isFinite)) return Number.NaN;\n return m * 60 + sec;\n }\n return Number.NaN;\n}\n\nfunction parseTimingLine(line: string): { start: string; end: string } | null {\n const arrow = line.indexOf(\"-->\");\n if (arrow < 0) return null;\n const start = line.slice(0, arrow).trim();\n const rest = line.slice(arrow + 3).trim();\n const endMatch = rest.match(/^(\\S+)/);\n if (!endMatch) return null;\n return { start, end: endMatch[1]! };\n}\n\n/**\n * Parse SubRip (`.srt`) content into ordered cues.\n */\nexport function parseSrt(srt: string): TranscriptCue[] {\n const cues: TranscriptCue[] = [];\n const blocks = srt.replace(/\\r\\n/g, \"\\n\").split(/\\n\\s*\\n/);\n\n for (const block of blocks) {\n const lines = block\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter((l) => l.length > 0);\n if (lines.length === 0) continue;\n\n let i = 0;\n if (/^\\d+$/.test(lines[0]!)) {\n i = 1;\n }\n const timingLine = lines[i];\n if (!timingLine) continue;\n const times = parseTimingLine(timingLine);\n if (!times) continue;\n const startTime = parseTimestampToSeconds(times.start);\n const endTime = parseTimestampToSeconds(times.end);\n if (!Number.isFinite(startTime) || !Number.isFinite(endTime)) continue;\n const textLines = lines.slice(i + 1);\n const text = stripHtmlTags(textLines.join(\"\\n\"));\n if (!text) continue;\n cues.push({ startTime, endTime, text });\n }\n\n return cues.sort((a, b) => a.startTime - b.startTime);\n}\n\n/**\n * Parse WebVTT (`.vtt`) content into ordered cues. Ignores `NOTE` blocks and region/style headers.\n */\nexport function parseVtt(vtt: string): TranscriptCue[] {\n const cues: TranscriptCue[] = [];\n let body = vtt.replace(/\\r\\n/g, \"\\n\");\n if (body.startsWith(\"WEBVTT\")) {\n const firstBlank = body.search(/\\n\\s*\\n/);\n body = firstBlank >= 0 ? body.slice(firstBlank).trim() : \"\";\n }\n\n const blocks = body.split(/\\n\\s*\\n/);\n\n for (const block of blocks) {\n const rawLines = block.split(\"\\n\");\n const lines = rawLines.map((l) => l.trimEnd());\n if (lines.length === 0) continue;\n if (\n lines[0]!.startsWith(\"NOTE\") ||\n lines[0]!.startsWith(\"STYLE\") ||\n lines[0]!.startsWith(\"REGION\")\n ) {\n continue;\n }\n\n let i = 0;\n let id: string | undefined;\n let timingLine = lines[i]!;\n if (!timingLine.includes(\"-->\")) {\n id = lines[i]!;\n i += 1;\n timingLine = lines[i]!;\n }\n if (!timingLine?.includes(\"-->\")) continue;\n\n const times = parseTimingLine(timingLine);\n if (!times) continue;\n const startTime = parseTimestampToSeconds(times.start);\n const endTime = parseTimestampToSeconds(times.end);\n if (!Number.isFinite(startTime) || !Number.isFinite(endTime)) continue;\n\n const textLines = lines.slice(i + 1).filter((l) => l.trim().length > 0);\n const text = stripHtmlTags(textLines.join(\"\\n\"));\n if (!text) continue;\n cues.push({ startTime, endTime, text, ...(id ? { id } : {}) });\n }\n\n return cues.sort((a, b) => a.startTime - b.startTime);\n}\n\n/**\n * Auto-detect format: WebVTT if the string starts with `WEBVTT`, otherwise SRT.\n */\nexport function parseTranscriptAuto(input: string): TranscriptCue[] {\n const trimmed = input.trimStart();\n if (trimmed.startsWith(\"WEBVTT\")) {\n return parseVtt(input);\n }\n return parseSrt(input);\n}\n","import { useMemo } from \"react\";\nimport { useGingerMedia } from \"../context/GingerSplitContexts\";\nimport { type TranscriptCue, parseSrt, parseTranscriptAuto, parseVtt } from \"./parseTranscript\";\n\nexport type UseGingerTranscriptSyncOptions = {\n transcript: string | TranscriptCue[];\n /** Default: `\"auto\"` (WEBVTT header → VTT, else SRT). Ignored when `transcript` is a cue array. */\n format?: \"vtt\" | \"srt\" | \"auto\";\n};\n\nexport type GingerTranscriptSyncState = {\n cues: TranscriptCue[];\n /** Last cue index where `startTime <= currentTime` (same scan as lyrics sync). */\n activeIndex: number;\n activeCue: TranscriptCue | null;\n /** All cues active at `currentTime` (`startTime <= t < endTime`), including overlaps. */\n activeCues: TranscriptCue[];\n};\n\nfunction parseString(transcript: string, format: \"vtt\" | \"srt\" | \"auto\"): TranscriptCue[] {\n if (format === \"vtt\") return parseVtt(transcript);\n if (format === \"srt\") return parseSrt(transcript);\n return parseTranscriptAuto(transcript);\n}\n\n/**\n * Syncs SRT / WebVTT transcript cues to the current Ginger playback time.\n *\n * ```ts\n * import { useGingerTranscriptSync } from \"@lucaismyname/ginger/transcript\";\n * ```\n */\nexport function useGingerTranscriptSync(\n options: UseGingerTranscriptSyncOptions,\n): GingerTranscriptSyncState {\n const { transcript, format = \"auto\" } = options;\n const { currentTime } = useGingerMedia();\n\n const cues = useMemo(() => {\n if (Array.isArray(transcript)) {\n return [...transcript]\n .filter(\n (c) =>\n Number.isFinite(c.startTime) &&\n Number.isFinite(c.endTime) &&\n c.startTime >= 0 &&\n c.endTime >= c.startTime,\n )\n .sort((a, b) => a.startTime - b.startTime);\n }\n return parseString(transcript, format);\n }, [transcript, format]);\n\n const activeIndex = useMemo(() => {\n for (let i = cues.length - 1; i >= 0; i -= 1) {\n if (currentTime >= cues[i]!.startTime) return i;\n }\n return -1;\n }, [currentTime, cues]);\n\n const activeCues = useMemo(() => {\n return cues.filter((c) => currentTime >= c.startTime && currentTime < c.endTime);\n }, [currentTime, cues]);\n\n return {\n cues,\n activeIndex,\n activeCue: activeIndex >= 0 ? (cues[activeIndex] ?? null) : null,\n activeCues,\n };\n}\n"],"names":["stripHtmlTags","text","parseTimestampToSeconds","raw","segs","h","m","sec","parseTimingLine","line","arrow","start","endMatch","parseSrt","srt","cues","blocks","block","lines","l","i","timingLine","times","startTime","endTime","textLines","a","b","parseVtt","vtt","body","firstBlank","id","parseTranscriptAuto","input","parseString","transcript","format","useGingerTranscriptSync","options","currentTime","useGingerMedia","useMemo","c","activeIndex","activeCues"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../src/transcript/parseTranscript.ts","../../src/transcript/useGingerTranscriptSync.ts"],"sourcesContent":["/**\n * A single timed transcript cue (SRT / WebVTT).\n */\nexport type TranscriptCue = {\n /** Start time in seconds. */\n startTime: number;\n /** End time in seconds. */\n endTime: number;\n text: string;\n /** Present for WebVTT cues that declare an identifier line. */\n id?: string;\n};\n\nfunction stripHtmlTags(text: string): string {\n return text.replace(/<[^>]+>/g, \"\").trim();\n}\n\n/** Parse `HH:MM:SS.mmm` / `HH:MM:SS,mmm` / `MM:SS.mmm` (WebVTT) to seconds. */\nexport function parseTimestampToSeconds(raw: string): number {\n const t = raw.trim().replace(\",\", \".\");\n const segs = t.split(\":\");\n if (segs.length === 3) {\n const h = Number(segs[0]);\n const m = Number(segs[1]);\n const sec = Number(segs[2]);\n if (![h, m, sec].every(Number.isFinite)) return Number.NaN;\n return h * 3600 + m * 60 + sec;\n }\n if (segs.length === 2) {\n const m = Number(segs[0]);\n const sec = Number(segs[1]);\n if (![m, sec].every(Number.isFinite)) return Number.NaN;\n return m * 60 + sec;\n }\n return Number.NaN;\n}\n\nfunction parseTimingLine(line: string): { start: string; end: string } | null {\n const arrow = line.indexOf(\"-->\");\n if (arrow < 0) return null;\n const start = line.slice(0, arrow).trim();\n const rest = line.slice(arrow + 3).trim();\n const endMatch = rest.match(/^(\\S+)/);\n if (!endMatch) return null;\n return { start, end: endMatch[1]! };\n}\n\n/**\n * Parse SubRip (`.srt`) content into ordered cues.\n */\nexport function parseSrt(srt: string): TranscriptCue[] {\n const cues: TranscriptCue[] = [];\n const blocks = srt.replace(/\\r\\n/g, \"\\n\").split(/\\n\\s*\\n/);\n\n for (const block of blocks) {\n const lines = block\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter((l) => l.length > 0);\n if (lines.length === 0) continue;\n\n let i = 0;\n if (/^\\d+$/.test(lines[0]!)) {\n i = 1;\n }\n const timingLine = lines[i];\n if (!timingLine) continue;\n const times = parseTimingLine(timingLine);\n if (!times) continue;\n const startTime = parseTimestampToSeconds(times.start);\n const endTime = parseTimestampToSeconds(times.end);\n if (!Number.isFinite(startTime) || !Number.isFinite(endTime)) continue;\n const textLines = lines.slice(i + 1);\n const text = stripHtmlTags(textLines.join(\"\\n\"));\n if (!text) continue;\n cues.push({ startTime, endTime, text });\n }\n\n return cues.sort((a, b) => a.startTime - b.startTime);\n}\n\n/**\n * Parse WebVTT (`.vtt`) content into ordered cues. Ignores `NOTE` blocks and region/style headers.\n */\nexport function parseVtt(vtt: string): TranscriptCue[] {\n const cues: TranscriptCue[] = [];\n let body = vtt.replace(/\\r\\n/g, \"\\n\");\n if (body.startsWith(\"WEBVTT\")) {\n const firstBlank = body.search(/\\n\\s*\\n/);\n body = firstBlank >= 0 ? body.slice(firstBlank).trim() : \"\";\n }\n\n const blocks = body.split(/\\n\\s*\\n/);\n\n for (const block of blocks) {\n const rawLines = block.split(\"\\n\");\n const lines = rawLines.map((l) => l.trimEnd());\n if (lines.length === 0) continue;\n if (\n lines[0]!.startsWith(\"NOTE\") ||\n lines[0]!.startsWith(\"STYLE\") ||\n lines[0]!.startsWith(\"REGION\")\n ) {\n continue;\n }\n\n let i = 0;\n let id: string | undefined;\n let timingLine = lines[i]!;\n if (!timingLine.includes(\"-->\")) {\n id = lines[i]!;\n i += 1;\n timingLine = lines[i]!;\n }\n if (!timingLine?.includes(\"-->\")) continue;\n\n const times = parseTimingLine(timingLine);\n if (!times) continue;\n const startTime = parseTimestampToSeconds(times.start);\n const endTime = parseTimestampToSeconds(times.end);\n if (!Number.isFinite(startTime) || !Number.isFinite(endTime)) continue;\n\n const textLines = lines.slice(i + 1).filter((l) => l.trim().length > 0);\n const text = stripHtmlTags(textLines.join(\"\\n\"));\n if (!text) continue;\n cues.push({ startTime, endTime, text, ...(id ? { id } : {}) });\n }\n\n return cues.sort((a, b) => a.startTime - b.startTime);\n}\n\n/**\n * Auto-detect format: WebVTT if the string starts with `WEBVTT`, otherwise SRT.\n */\nexport function parseTranscriptAuto(input: string): TranscriptCue[] {\n const trimmed = input.trimStart();\n if (trimmed.startsWith(\"WEBVTT\")) {\n return parseVtt(input);\n }\n return parseSrt(input);\n}\n","import { useMemo } from \"react\";\nimport { useGingerMedia } from \"../context/GingerSplitContexts\";\nimport { type TranscriptCue, parseSrt, parseTranscriptAuto, parseVtt } from \"./parseTranscript\";\n\nexport type UseGingerTranscriptSyncOptions = {\n transcript: string | TranscriptCue[];\n /** Default: `\"auto\"` (WEBVTT header → VTT, else SRT). Ignored when `transcript` is a cue array. */\n format?: \"vtt\" | \"srt\" | \"auto\";\n};\n\nexport type GingerTranscriptSyncState = {\n cues: TranscriptCue[];\n /** Last cue index where `startTime <= currentTime` (same scan as lyrics sync). */\n activeIndex: number;\n activeCue: TranscriptCue | null;\n /** All cues active at `currentTime` (`startTime <= t < endTime`), including overlaps. */\n activeCues: TranscriptCue[];\n};\n\nfunction parseString(transcript: string, format: \"vtt\" | \"srt\" | \"auto\"): TranscriptCue[] {\n if (format === \"vtt\") return parseVtt(transcript);\n if (format === \"srt\") return parseSrt(transcript);\n return parseTranscriptAuto(transcript);\n}\n\nfunction findLastCueIndexAtOrBeforeTime(cues: TranscriptCue[], currentTime: number): number {\n let low = 0;\n let high = cues.length - 1;\n let best = -1;\n\n while (low <= high) {\n const mid = low + Math.floor((high - low) / 2);\n const cue = cues[mid];\n if (!cue) break;\n if (cue.startTime <= currentTime) {\n best = mid;\n low = mid + 1;\n } else {\n high = mid - 1;\n }\n }\n\n return best;\n}\n\n/**\n * Syncs SRT / WebVTT transcript cues to the current Ginger playback time.\n *\n * ```ts\n * import { useGingerTranscriptSync } from \"@lucaismyname/ginger/transcript\";\n * ```\n */\nexport function useGingerTranscriptSync(\n options: UseGingerTranscriptSyncOptions,\n): GingerTranscriptSyncState {\n const { transcript, format = \"auto\" } = options;\n const { currentTime } = useGingerMedia();\n\n const cues = useMemo(() => {\n if (Array.isArray(transcript)) {\n return [...transcript]\n .filter(\n (c) =>\n Number.isFinite(c.startTime) &&\n Number.isFinite(c.endTime) &&\n c.startTime >= 0 &&\n c.endTime >= c.startTime,\n )\n .sort((a, b) => a.startTime - b.startTime);\n }\n return parseString(transcript, format);\n }, [transcript, format]);\n\n const activeIndex = useMemo(() => {\n return findLastCueIndexAtOrBeforeTime(cues, currentTime);\n }, [currentTime, cues]);\n\n const activeCues = useMemo(() => {\n if (activeIndex < 0) return [];\n return cues\n .slice(0, activeIndex + 1)\n .filter((c) => currentTime >= c.startTime && currentTime < c.endTime);\n }, [currentTime, cues, activeIndex]);\n\n return {\n cues,\n activeIndex,\n activeCue: activeIndex >= 0 ? (cues[activeIndex] ?? null) : null,\n activeCues,\n };\n}\n"],"names":["stripHtmlTags","text","parseTimestampToSeconds","raw","segs","h","m","sec","parseTimingLine","line","arrow","start","endMatch","parseSrt","srt","cues","blocks","block","lines","l","i","timingLine","times","startTime","endTime","textLines","a","b","parseVtt","vtt","body","firstBlank","id","parseTranscriptAuto","input","parseString","transcript","format","findLastCueIndexAtOrBeforeTime","currentTime","low","high","best","mid","cue","useGingerTranscriptSync","options","useGingerMedia","useMemo","c","activeIndex","activeCues"],"mappings":"0JAaA,SAASA,EAAcC,EAAsB,CAC3C,OAAOA,EAAK,QAAQ,WAAY,EAAE,EAAE,KAAA,CACtC,CAGO,SAASC,EAAwBC,EAAqB,CAE3D,MAAMC,EADID,EAAI,KAAA,EAAO,QAAQ,IAAK,GAAG,EACtB,MAAM,GAAG,EACxB,GAAIC,EAAK,SAAW,EAAG,CACrB,MAAMC,EAAI,OAAOD,EAAK,CAAC,CAAC,EAClBE,EAAI,OAAOF,EAAK,CAAC,CAAC,EAClBG,EAAM,OAAOH,EAAK,CAAC,CAAC,EAC1B,MAAK,CAACC,EAAGC,EAAGC,CAAG,EAAE,MAAM,OAAO,QAAQ,EAC/BF,EAAI,KAAOC,EAAI,GAAKC,EADqB,OAAO,GAEzD,CACA,GAAIH,EAAK,SAAW,EAAG,CACrB,MAAME,EAAI,OAAOF,EAAK,CAAC,CAAC,EAClBG,EAAM,OAAOH,EAAK,CAAC,CAAC,EAC1B,MAAK,CAACE,EAAGC,CAAG,EAAE,MAAM,OAAO,QAAQ,EAC5BD,EAAI,GAAKC,EAD6B,OAAO,GAEtD,CACA,OAAO,OAAO,GAChB,CAEA,SAASC,EAAgBC,EAAqD,CAC5E,MAAMC,EAAQD,EAAK,QAAQ,KAAK,EAChC,GAAIC,EAAQ,EAAG,OAAO,KACtB,MAAMC,EAAQF,EAAK,MAAM,EAAGC,CAAK,EAAE,KAAA,EAE7BE,EADOH,EAAK,MAAMC,EAAQ,CAAC,EAAE,KAAA,EACb,MAAM,QAAQ,EACpC,OAAKE,EACE,CAAE,MAAAD,EAAO,IAAKC,EAAS,CAAC,CAAA,EADT,IAExB,CAKO,SAASC,EAASC,EAA8B,CACrD,MAAMC,EAAwB,CAAA,EACxBC,EAASF,EAAI,QAAQ,QAAS;AAAA,CAAI,EAAE,MAAM,SAAS,EAEzD,UAAWG,KAASD,EAAQ,CAC1B,MAAME,EAAQD,EACX,MAAM;AAAA,CAAI,EACV,IAAKE,GAAMA,EAAE,KAAA,CAAM,EACnB,OAAQA,GAAMA,EAAE,OAAS,CAAC,EAC7B,GAAID,EAAM,SAAW,EAAG,SAExB,IAAIE,EAAI,EACJ,QAAQ,KAAKF,EAAM,CAAC,CAAE,IACxBE,EAAI,GAEN,MAAMC,EAAaH,EAAME,CAAC,EAC1B,GAAI,CAACC,EAAY,SACjB,MAAMC,EAAQd,EAAgBa,CAAU,EACxC,GAAI,CAACC,EAAO,SACZ,MAAMC,EAAYrB,EAAwBoB,EAAM,KAAK,EAC/CE,EAAUtB,EAAwBoB,EAAM,GAAG,EACjD,GAAI,CAAC,OAAO,SAASC,CAAS,GAAK,CAAC,OAAO,SAASC,CAAO,EAAG,SAC9D,MAAMC,EAAYP,EAAM,MAAME,EAAI,CAAC,EAC7BnB,EAAOD,EAAcyB,EAAU,KAAK;AAAA,CAAI,CAAC,EAC1CxB,GACLc,EAAK,KAAK,CAAE,UAAAQ,EAAW,QAAAC,EAAS,KAAAvB,EAAM,CACxC,CAEA,OAAOc,EAAK,KAAK,CAACW,EAAGC,IAAMD,EAAE,UAAYC,EAAE,SAAS,CACtD,CAKO,SAASC,EAASC,EAA8B,CACrD,MAAMd,EAAwB,CAAA,EAC9B,IAAIe,EAAOD,EAAI,QAAQ,QAAS;AAAA,CAAI,EACpC,GAAIC,EAAK,WAAW,QAAQ,EAAG,CAC7B,MAAMC,EAAaD,EAAK,OAAO,SAAS,EACxCA,EAAOC,GAAc,EAAID,EAAK,MAAMC,CAAU,EAAE,OAAS,EAC3D,CAEA,MAAMf,EAASc,EAAK,MAAM,SAAS,EAEnC,UAAWb,KAASD,EAAQ,CAE1B,MAAME,EADWD,EAAM,MAAM;AAAA,CAAI,EACV,IAAKE,GAAMA,EAAE,SAAS,EAE7C,GADID,EAAM,SAAW,GAEnBA,EAAM,CAAC,EAAG,WAAW,MAAM,GAC3BA,EAAM,CAAC,EAAG,WAAW,OAAO,GAC5BA,EAAM,CAAC,EAAG,WAAW,QAAQ,EAE7B,SAGF,IAAIE,EAAI,EACJY,EACAX,EAAaH,EAAME,CAAC,EAMxB,GALKC,EAAW,SAAS,KAAK,IAC5BW,EAAKd,EAAME,CAAC,EACZA,GAAK,EACLC,EAAaH,EAAME,CAAC,GAElB,EAACC,GAAA,MAAAA,EAAY,SAAS,QAAQ,SAElC,MAAMC,EAAQd,EAAgBa,CAAU,EACxC,GAAI,CAACC,EAAO,SACZ,MAAMC,EAAYrB,EAAwBoB,EAAM,KAAK,EAC/CE,EAAUtB,EAAwBoB,EAAM,GAAG,EACjD,GAAI,CAAC,OAAO,SAASC,CAAS,GAAK,CAAC,OAAO,SAASC,CAAO,EAAG,SAE9D,MAAMC,EAAYP,EAAM,MAAME,EAAI,CAAC,EAAE,OAAQD,GAAMA,EAAE,KAAA,EAAO,OAAS,CAAC,EAChElB,EAAOD,EAAcyB,EAAU,KAAK;AAAA,CAAI,CAAC,EAC1CxB,GACLc,EAAK,KAAK,CAAE,UAAAQ,EAAW,QAAAC,EAAS,KAAAvB,EAAM,GAAI+B,EAAK,CAAE,GAAAA,GAAO,CAAA,EAAK,CAC/D,CAEA,OAAOjB,EAAK,KAAK,CAACW,EAAGC,IAAMD,EAAE,UAAYC,EAAE,SAAS,CACtD,CAKO,SAASM,EAAoBC,EAAgC,CAElE,OADgBA,EAAM,UAAA,EACV,WAAW,QAAQ,EACtBN,EAASM,CAAK,EAEhBrB,EAASqB,CAAK,CACvB,CCzHA,SAASC,EAAYC,EAAoBC,EAAiD,CACxF,OAAIA,IAAW,MAAcT,EAASQ,CAAU,EAC5CC,IAAW,MAAcxB,EAASuB,CAAU,EACzCH,EAAoBG,CAAU,CACvC,CAEA,SAASE,EAA+BvB,EAAuBwB,EAA6B,CAC1F,IAAIC,EAAM,EACNC,EAAO1B,EAAK,OAAS,EACrB2B,EAAO,GAEX,KAAOF,GAAOC,GAAM,CAClB,MAAME,EAAMH,EAAM,KAAK,OAAOC,EAAOD,GAAO,CAAC,EACvCI,EAAM7B,EAAK4B,CAAG,EACpB,GAAI,CAACC,EAAK,MACNA,EAAI,WAAaL,GACnBG,EAAOC,EACPH,EAAMG,EAAM,GAEZF,EAAOE,EAAM,CAEjB,CAEA,OAAOD,CACT,CASO,SAASG,EACdC,EAC2B,CAC3B,KAAM,CAAE,WAAAV,EAAY,OAAAC,EAAS,MAAA,EAAWS,EAClC,CAAE,YAAAP,CAAA,EAAgBQ,iBAAA,EAElBhC,EAAOiC,EAAAA,QAAQ,IACf,MAAM,QAAQZ,CAAU,EACnB,CAAC,GAAGA,CAAU,EAClB,OACEa,GACC,OAAO,SAASA,EAAE,SAAS,GAC3B,OAAO,SAASA,EAAE,OAAO,GACzBA,EAAE,WAAa,GACfA,EAAE,SAAWA,EAAE,SAAA,EAElB,KAAK,CAACvB,EAAGC,IAAMD,EAAE,UAAYC,EAAE,SAAS,EAEtCQ,EAAYC,EAAYC,CAAM,EACpC,CAACD,EAAYC,CAAM,CAAC,EAEjBa,EAAcF,EAAAA,QAAQ,IACnBV,EAA+BvB,EAAMwB,CAAW,EACtD,CAACA,EAAaxB,CAAI,CAAC,EAEhBoC,EAAaH,EAAAA,QAAQ,IACrBE,EAAc,EAAU,CAAA,EACrBnC,EACJ,MAAM,EAAGmC,EAAc,CAAC,EACxB,OAAQD,GAAMV,GAAeU,EAAE,WAAaV,EAAcU,EAAE,OAAO,EACrE,CAACV,EAAaxB,EAAMmC,CAAW,CAAC,EAEnC,MAAO,CACL,KAAAnC,EACA,YAAAmC,EACA,UAAWA,GAAe,EAAKnC,EAAKmC,CAAW,GAAK,KAAQ,KAC5D,WAAAC,CAAA,CAEJ"}
@@ -1,99 +1,104 @@
1
- import { useMemo as p } from "react";
2
- import { u as S } from "../GingerSplitContexts-BzBExb95.js";
3
- function d(n) {
1
+ import { useMemo as N } from "react";
2
+ import { u as L } from "../GingerSplitContexts-BzBExb95.js";
3
+ function p(n) {
4
4
  return n.replace(/<[^>]+>/g, "").trim();
5
5
  }
6
6
  function T(n) {
7
- const i = n.trim().replace(",", ".").split(":");
8
- if (i.length === 3) {
9
- const s = Number(i[0]), t = Number(i[1]), o = Number(i[2]);
10
- return [s, t, o].every(Number.isFinite) ? s * 3600 + t * 60 + o : Number.NaN;
7
+ const e = n.trim().replace(",", ".").split(":");
8
+ if (e.length === 3) {
9
+ const i = Number(e[0]), t = Number(e[1]), s = Number(e[2]);
10
+ return [i, t, s].every(Number.isFinite) ? i * 3600 + t * 60 + s : Number.NaN;
11
11
  }
12
- if (i.length === 2) {
13
- const s = Number(i[0]), t = Number(i[1]);
14
- return [s, t].every(Number.isFinite) ? s * 60 + t : Number.NaN;
12
+ if (e.length === 2) {
13
+ const i = Number(e[0]), t = Number(e[1]);
14
+ return [i, t].every(Number.isFinite) ? i * 60 + t : Number.NaN;
15
15
  }
16
16
  return Number.NaN;
17
17
  }
18
18
  function h(n) {
19
19
  const r = n.indexOf("-->");
20
20
  if (r < 0) return null;
21
- const i = n.slice(0, r).trim(), t = n.slice(r + 3).trim().match(/^(\S+)/);
22
- return t ? { start: i, end: t[1] } : null;
21
+ const e = n.slice(0, r).trim(), t = n.slice(r + 3).trim().match(/^(\S+)/);
22
+ return t ? { start: e, end: t[1] } : null;
23
23
  }
24
24
  function g(n) {
25
- const r = [], i = n.replace(/\r\n/g, `
25
+ const r = [], e = n.replace(/\r\n/g, `
26
26
  `).split(/\n\s*\n/);
27
- for (const s of i) {
28
- const t = s.split(`
27
+ for (const i of e) {
28
+ const t = i.split(`
29
29
  `).map((a) => a.trim()).filter((a) => a.length > 0);
30
30
  if (t.length === 0) continue;
31
- let o = 0;
32
- /^\d+$/.test(t[0]) && (o = 1);
33
- const c = t[o];
31
+ let s = 0;
32
+ /^\d+$/.test(t[0]) && (s = 1);
33
+ const c = t[s];
34
34
  if (!c) continue;
35
- const e = h(c);
36
- if (!e) continue;
37
- const m = T(e.start), u = T(e.end);
35
+ const o = h(c);
36
+ if (!o) continue;
37
+ const m = T(o.start), u = T(o.end);
38
38
  if (!Number.isFinite(m) || !Number.isFinite(u)) continue;
39
- const l = t.slice(o + 1), f = d(l.join(`
39
+ const l = t.slice(s + 1), f = p(l.join(`
40
40
  `));
41
41
  f && r.push({ startTime: m, endTime: u, text: f });
42
42
  }
43
- return r.sort((s, t) => s.startTime - t.startTime);
43
+ return r.sort((i, t) => i.startTime - t.startTime);
44
44
  }
45
- function F(n) {
45
+ function x(n) {
46
46
  const r = [];
47
- let i = n.replace(/\r\n/g, `
47
+ let e = n.replace(/\r\n/g, `
48
48
  `);
49
- if (i.startsWith("WEBVTT")) {
50
- const t = i.search(/\n\s*\n/);
51
- i = t >= 0 ? i.slice(t).trim() : "";
49
+ if (e.startsWith("WEBVTT")) {
50
+ const t = e.search(/\n\s*\n/);
51
+ e = t >= 0 ? e.slice(t).trim() : "";
52
52
  }
53
- const s = i.split(/\n\s*\n/);
54
- for (const t of s) {
53
+ const i = e.split(/\n\s*\n/);
54
+ for (const t of i) {
55
55
  const c = t.split(`
56
- `).map((N) => N.trimEnd());
56
+ `).map((d) => d.trimEnd());
57
57
  if (c.length === 0 || c[0].startsWith("NOTE") || c[0].startsWith("STYLE") || c[0].startsWith("REGION"))
58
58
  continue;
59
- let e = 0, m, u = c[e];
60
- if (u.includes("-->") || (m = c[e], e += 1, u = c[e]), !(u != null && u.includes("-->"))) continue;
59
+ let o = 0, m, u = c[o];
60
+ if (u.includes("-->") || (m = c[o], o += 1, u = c[o]), !(u != null && u.includes("-->"))) continue;
61
61
  const l = h(u);
62
62
  if (!l) continue;
63
63
  const f = T(l.start), a = T(l.end);
64
64
  if (!Number.isFinite(f) || !Number.isFinite(a)) continue;
65
- const x = c.slice(e + 1).filter((N) => N.trim().length > 0), b = d(x.join(`
65
+ const F = c.slice(o + 1).filter((d) => d.trim().length > 0), b = p(F.join(`
66
66
  `));
67
67
  b && r.push({ startTime: f, endTime: a, text: b, ...m ? { id: m } : {} });
68
68
  }
69
- return r.sort((t, o) => t.startTime - o.startTime);
69
+ return r.sort((t, s) => t.startTime - s.startTime);
70
70
  }
71
- function W(n) {
72
- return n.trimStart().startsWith("WEBVTT") ? F(n) : g(n);
71
+ function S(n) {
72
+ return n.trimStart().startsWith("WEBVTT") ? x(n) : g(n);
73
73
  }
74
- function v(n, r) {
75
- return r === "vtt" ? F(n) : r === "srt" ? g(n) : W(n);
74
+ function W(n, r) {
75
+ return r === "vtt" ? x(n) : r === "srt" ? g(n) : S(n);
76
76
  }
77
- function L(n) {
78
- const { transcript: r, format: i = "auto" } = n, { currentTime: s } = S(), t = p(() => Array.isArray(r) ? [...r].filter(
79
- (e) => Number.isFinite(e.startTime) && Number.isFinite(e.endTime) && e.startTime >= 0 && e.endTime >= e.startTime
80
- ).sort((e, m) => e.startTime - m.startTime) : v(r, i), [r, i]), o = p(() => {
81
- for (let e = t.length - 1; e >= 0; e -= 1)
82
- if (s >= t[e].startTime) return e;
83
- return -1;
84
- }, [s, t]), c = p(() => t.filter((e) => s >= e.startTime && s < e.endTime), [s, t]);
77
+ function k(n, r) {
78
+ let e = 0, i = n.length - 1, t = -1;
79
+ for (; e <= i; ) {
80
+ const s = e + Math.floor((i - e) / 2), c = n[s];
81
+ if (!c) break;
82
+ c.startTime <= r ? (t = s, e = s + 1) : i = s - 1;
83
+ }
84
+ return t;
85
+ }
86
+ function E(n) {
87
+ const { transcript: r, format: e = "auto" } = n, { currentTime: i } = L(), t = N(() => Array.isArray(r) ? [...r].filter(
88
+ (o) => Number.isFinite(o.startTime) && Number.isFinite(o.endTime) && o.startTime >= 0 && o.endTime >= o.startTime
89
+ ).sort((o, m) => o.startTime - m.startTime) : W(r, e), [r, e]), s = N(() => k(t, i), [i, t]), c = N(() => s < 0 ? [] : t.slice(0, s + 1).filter((o) => i >= o.startTime && i < o.endTime), [i, t, s]);
85
90
  return {
86
91
  cues: t,
87
- activeIndex: o,
88
- activeCue: o >= 0 ? t[o] ?? null : null,
92
+ activeIndex: s,
93
+ activeCue: s >= 0 ? t[s] ?? null : null,
89
94
  activeCues: c
90
95
  };
91
96
  }
92
97
  export {
93
98
  g as parseSrt,
94
99
  T as parseTimestampToSeconds,
95
- W as parseTranscriptAuto,
96
- F as parseVtt,
97
- L as useGingerTranscriptSync
100
+ S as parseTranscriptAuto,
101
+ x as parseVtt,
102
+ E as useGingerTranscriptSync
98
103
  };
99
104
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/transcript/parseTranscript.ts","../../src/transcript/useGingerTranscriptSync.ts"],"sourcesContent":["/**\n * A single timed transcript cue (SRT / WebVTT).\n */\nexport type TranscriptCue = {\n /** Start time in seconds. */\n startTime: number;\n /** End time in seconds. */\n endTime: number;\n text: string;\n /** Present for WebVTT cues that declare an identifier line. */\n id?: string;\n};\n\nfunction stripHtmlTags(text: string): string {\n return text.replace(/<[^>]+>/g, \"\").trim();\n}\n\n/** Parse `HH:MM:SS.mmm` / `HH:MM:SS,mmm` / `MM:SS.mmm` (WebVTT) to seconds. */\nexport function parseTimestampToSeconds(raw: string): number {\n const t = raw.trim().replace(\",\", \".\");\n const segs = t.split(\":\");\n if (segs.length === 3) {\n const h = Number(segs[0]);\n const m = Number(segs[1]);\n const sec = Number(segs[2]);\n if (![h, m, sec].every(Number.isFinite)) return Number.NaN;\n return h * 3600 + m * 60 + sec;\n }\n if (segs.length === 2) {\n const m = Number(segs[0]);\n const sec = Number(segs[1]);\n if (![m, sec].every(Number.isFinite)) return Number.NaN;\n return m * 60 + sec;\n }\n return Number.NaN;\n}\n\nfunction parseTimingLine(line: string): { start: string; end: string } | null {\n const arrow = line.indexOf(\"-->\");\n if (arrow < 0) return null;\n const start = line.slice(0, arrow).trim();\n const rest = line.slice(arrow + 3).trim();\n const endMatch = rest.match(/^(\\S+)/);\n if (!endMatch) return null;\n return { start, end: endMatch[1]! };\n}\n\n/**\n * Parse SubRip (`.srt`) content into ordered cues.\n */\nexport function parseSrt(srt: string): TranscriptCue[] {\n const cues: TranscriptCue[] = [];\n const blocks = srt.replace(/\\r\\n/g, \"\\n\").split(/\\n\\s*\\n/);\n\n for (const block of blocks) {\n const lines = block\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter((l) => l.length > 0);\n if (lines.length === 0) continue;\n\n let i = 0;\n if (/^\\d+$/.test(lines[0]!)) {\n i = 1;\n }\n const timingLine = lines[i];\n if (!timingLine) continue;\n const times = parseTimingLine(timingLine);\n if (!times) continue;\n const startTime = parseTimestampToSeconds(times.start);\n const endTime = parseTimestampToSeconds(times.end);\n if (!Number.isFinite(startTime) || !Number.isFinite(endTime)) continue;\n const textLines = lines.slice(i + 1);\n const text = stripHtmlTags(textLines.join(\"\\n\"));\n if (!text) continue;\n cues.push({ startTime, endTime, text });\n }\n\n return cues.sort((a, b) => a.startTime - b.startTime);\n}\n\n/**\n * Parse WebVTT (`.vtt`) content into ordered cues. Ignores `NOTE` blocks and region/style headers.\n */\nexport function parseVtt(vtt: string): TranscriptCue[] {\n const cues: TranscriptCue[] = [];\n let body = vtt.replace(/\\r\\n/g, \"\\n\");\n if (body.startsWith(\"WEBVTT\")) {\n const firstBlank = body.search(/\\n\\s*\\n/);\n body = firstBlank >= 0 ? body.slice(firstBlank).trim() : \"\";\n }\n\n const blocks = body.split(/\\n\\s*\\n/);\n\n for (const block of blocks) {\n const rawLines = block.split(\"\\n\");\n const lines = rawLines.map((l) => l.trimEnd());\n if (lines.length === 0) continue;\n if (\n lines[0]!.startsWith(\"NOTE\") ||\n lines[0]!.startsWith(\"STYLE\") ||\n lines[0]!.startsWith(\"REGION\")\n ) {\n continue;\n }\n\n let i = 0;\n let id: string | undefined;\n let timingLine = lines[i]!;\n if (!timingLine.includes(\"-->\")) {\n id = lines[i]!;\n i += 1;\n timingLine = lines[i]!;\n }\n if (!timingLine?.includes(\"-->\")) continue;\n\n const times = parseTimingLine(timingLine);\n if (!times) continue;\n const startTime = parseTimestampToSeconds(times.start);\n const endTime = parseTimestampToSeconds(times.end);\n if (!Number.isFinite(startTime) || !Number.isFinite(endTime)) continue;\n\n const textLines = lines.slice(i + 1).filter((l) => l.trim().length > 0);\n const text = stripHtmlTags(textLines.join(\"\\n\"));\n if (!text) continue;\n cues.push({ startTime, endTime, text, ...(id ? { id } : {}) });\n }\n\n return cues.sort((a, b) => a.startTime - b.startTime);\n}\n\n/**\n * Auto-detect format: WebVTT if the string starts with `WEBVTT`, otherwise SRT.\n */\nexport function parseTranscriptAuto(input: string): TranscriptCue[] {\n const trimmed = input.trimStart();\n if (trimmed.startsWith(\"WEBVTT\")) {\n return parseVtt(input);\n }\n return parseSrt(input);\n}\n","import { useMemo } from \"react\";\nimport { useGingerMedia } from \"../context/GingerSplitContexts\";\nimport { type TranscriptCue, parseSrt, parseTranscriptAuto, parseVtt } from \"./parseTranscript\";\n\nexport type UseGingerTranscriptSyncOptions = {\n transcript: string | TranscriptCue[];\n /** Default: `\"auto\"` (WEBVTT header → VTT, else SRT). Ignored when `transcript` is a cue array. */\n format?: \"vtt\" | \"srt\" | \"auto\";\n};\n\nexport type GingerTranscriptSyncState = {\n cues: TranscriptCue[];\n /** Last cue index where `startTime <= currentTime` (same scan as lyrics sync). */\n activeIndex: number;\n activeCue: TranscriptCue | null;\n /** All cues active at `currentTime` (`startTime <= t < endTime`), including overlaps. */\n activeCues: TranscriptCue[];\n};\n\nfunction parseString(transcript: string, format: \"vtt\" | \"srt\" | \"auto\"): TranscriptCue[] {\n if (format === \"vtt\") return parseVtt(transcript);\n if (format === \"srt\") return parseSrt(transcript);\n return parseTranscriptAuto(transcript);\n}\n\n/**\n * Syncs SRT / WebVTT transcript cues to the current Ginger playback time.\n *\n * ```ts\n * import { useGingerTranscriptSync } from \"@lucaismyname/ginger/transcript\";\n * ```\n */\nexport function useGingerTranscriptSync(\n options: UseGingerTranscriptSyncOptions,\n): GingerTranscriptSyncState {\n const { transcript, format = \"auto\" } = options;\n const { currentTime } = useGingerMedia();\n\n const cues = useMemo(() => {\n if (Array.isArray(transcript)) {\n return [...transcript]\n .filter(\n (c) =>\n Number.isFinite(c.startTime) &&\n Number.isFinite(c.endTime) &&\n c.startTime >= 0 &&\n c.endTime >= c.startTime,\n )\n .sort((a, b) => a.startTime - b.startTime);\n }\n return parseString(transcript, format);\n }, [transcript, format]);\n\n const activeIndex = useMemo(() => {\n for (let i = cues.length - 1; i >= 0; i -= 1) {\n if (currentTime >= cues[i]!.startTime) return i;\n }\n return -1;\n }, [currentTime, cues]);\n\n const activeCues = useMemo(() => {\n return cues.filter((c) => currentTime >= c.startTime && currentTime < c.endTime);\n }, [currentTime, cues]);\n\n return {\n cues,\n activeIndex,\n activeCue: activeIndex >= 0 ? (cues[activeIndex] ?? null) : null,\n activeCues,\n };\n}\n"],"names":["stripHtmlTags","text","parseTimestampToSeconds","raw","segs","h","m","sec","parseTimingLine","line","arrow","start","endMatch","parseSrt","srt","cues","blocks","block","lines","l","i","timingLine","times","startTime","endTime","textLines","a","b","parseVtt","vtt","body","firstBlank","id","parseTranscriptAuto","input","parseString","transcript","format","useGingerTranscriptSync","options","currentTime","useGingerMedia","useMemo","c","activeIndex","activeCues"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../src/transcript/parseTranscript.ts","../../src/transcript/useGingerTranscriptSync.ts"],"sourcesContent":["/**\n * A single timed transcript cue (SRT / WebVTT).\n */\nexport type TranscriptCue = {\n /** Start time in seconds. */\n startTime: number;\n /** End time in seconds. */\n endTime: number;\n text: string;\n /** Present for WebVTT cues that declare an identifier line. */\n id?: string;\n};\n\nfunction stripHtmlTags(text: string): string {\n return text.replace(/<[^>]+>/g, \"\").trim();\n}\n\n/** Parse `HH:MM:SS.mmm` / `HH:MM:SS,mmm` / `MM:SS.mmm` (WebVTT) to seconds. */\nexport function parseTimestampToSeconds(raw: string): number {\n const t = raw.trim().replace(\",\", \".\");\n const segs = t.split(\":\");\n if (segs.length === 3) {\n const h = Number(segs[0]);\n const m = Number(segs[1]);\n const sec = Number(segs[2]);\n if (![h, m, sec].every(Number.isFinite)) return Number.NaN;\n return h * 3600 + m * 60 + sec;\n }\n if (segs.length === 2) {\n const m = Number(segs[0]);\n const sec = Number(segs[1]);\n if (![m, sec].every(Number.isFinite)) return Number.NaN;\n return m * 60 + sec;\n }\n return Number.NaN;\n}\n\nfunction parseTimingLine(line: string): { start: string; end: string } | null {\n const arrow = line.indexOf(\"-->\");\n if (arrow < 0) return null;\n const start = line.slice(0, arrow).trim();\n const rest = line.slice(arrow + 3).trim();\n const endMatch = rest.match(/^(\\S+)/);\n if (!endMatch) return null;\n return { start, end: endMatch[1]! };\n}\n\n/**\n * Parse SubRip (`.srt`) content into ordered cues.\n */\nexport function parseSrt(srt: string): TranscriptCue[] {\n const cues: TranscriptCue[] = [];\n const blocks = srt.replace(/\\r\\n/g, \"\\n\").split(/\\n\\s*\\n/);\n\n for (const block of blocks) {\n const lines = block\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter((l) => l.length > 0);\n if (lines.length === 0) continue;\n\n let i = 0;\n if (/^\\d+$/.test(lines[0]!)) {\n i = 1;\n }\n const timingLine = lines[i];\n if (!timingLine) continue;\n const times = parseTimingLine(timingLine);\n if (!times) continue;\n const startTime = parseTimestampToSeconds(times.start);\n const endTime = parseTimestampToSeconds(times.end);\n if (!Number.isFinite(startTime) || !Number.isFinite(endTime)) continue;\n const textLines = lines.slice(i + 1);\n const text = stripHtmlTags(textLines.join(\"\\n\"));\n if (!text) continue;\n cues.push({ startTime, endTime, text });\n }\n\n return cues.sort((a, b) => a.startTime - b.startTime);\n}\n\n/**\n * Parse WebVTT (`.vtt`) content into ordered cues. Ignores `NOTE` blocks and region/style headers.\n */\nexport function parseVtt(vtt: string): TranscriptCue[] {\n const cues: TranscriptCue[] = [];\n let body = vtt.replace(/\\r\\n/g, \"\\n\");\n if (body.startsWith(\"WEBVTT\")) {\n const firstBlank = body.search(/\\n\\s*\\n/);\n body = firstBlank >= 0 ? body.slice(firstBlank).trim() : \"\";\n }\n\n const blocks = body.split(/\\n\\s*\\n/);\n\n for (const block of blocks) {\n const rawLines = block.split(\"\\n\");\n const lines = rawLines.map((l) => l.trimEnd());\n if (lines.length === 0) continue;\n if (\n lines[0]!.startsWith(\"NOTE\") ||\n lines[0]!.startsWith(\"STYLE\") ||\n lines[0]!.startsWith(\"REGION\")\n ) {\n continue;\n }\n\n let i = 0;\n let id: string | undefined;\n let timingLine = lines[i]!;\n if (!timingLine.includes(\"-->\")) {\n id = lines[i]!;\n i += 1;\n timingLine = lines[i]!;\n }\n if (!timingLine?.includes(\"-->\")) continue;\n\n const times = parseTimingLine(timingLine);\n if (!times) continue;\n const startTime = parseTimestampToSeconds(times.start);\n const endTime = parseTimestampToSeconds(times.end);\n if (!Number.isFinite(startTime) || !Number.isFinite(endTime)) continue;\n\n const textLines = lines.slice(i + 1).filter((l) => l.trim().length > 0);\n const text = stripHtmlTags(textLines.join(\"\\n\"));\n if (!text) continue;\n cues.push({ startTime, endTime, text, ...(id ? { id } : {}) });\n }\n\n return cues.sort((a, b) => a.startTime - b.startTime);\n}\n\n/**\n * Auto-detect format: WebVTT if the string starts with `WEBVTT`, otherwise SRT.\n */\nexport function parseTranscriptAuto(input: string): TranscriptCue[] {\n const trimmed = input.trimStart();\n if (trimmed.startsWith(\"WEBVTT\")) {\n return parseVtt(input);\n }\n return parseSrt(input);\n}\n","import { useMemo } from \"react\";\nimport { useGingerMedia } from \"../context/GingerSplitContexts\";\nimport { type TranscriptCue, parseSrt, parseTranscriptAuto, parseVtt } from \"./parseTranscript\";\n\nexport type UseGingerTranscriptSyncOptions = {\n transcript: string | TranscriptCue[];\n /** Default: `\"auto\"` (WEBVTT header → VTT, else SRT). Ignored when `transcript` is a cue array. */\n format?: \"vtt\" | \"srt\" | \"auto\";\n};\n\nexport type GingerTranscriptSyncState = {\n cues: TranscriptCue[];\n /** Last cue index where `startTime <= currentTime` (same scan as lyrics sync). */\n activeIndex: number;\n activeCue: TranscriptCue | null;\n /** All cues active at `currentTime` (`startTime <= t < endTime`), including overlaps. */\n activeCues: TranscriptCue[];\n};\n\nfunction parseString(transcript: string, format: \"vtt\" | \"srt\" | \"auto\"): TranscriptCue[] {\n if (format === \"vtt\") return parseVtt(transcript);\n if (format === \"srt\") return parseSrt(transcript);\n return parseTranscriptAuto(transcript);\n}\n\nfunction findLastCueIndexAtOrBeforeTime(cues: TranscriptCue[], currentTime: number): number {\n let low = 0;\n let high = cues.length - 1;\n let best = -1;\n\n while (low <= high) {\n const mid = low + Math.floor((high - low) / 2);\n const cue = cues[mid];\n if (!cue) break;\n if (cue.startTime <= currentTime) {\n best = mid;\n low = mid + 1;\n } else {\n high = mid - 1;\n }\n }\n\n return best;\n}\n\n/**\n * Syncs SRT / WebVTT transcript cues to the current Ginger playback time.\n *\n * ```ts\n * import { useGingerTranscriptSync } from \"@lucaismyname/ginger/transcript\";\n * ```\n */\nexport function useGingerTranscriptSync(\n options: UseGingerTranscriptSyncOptions,\n): GingerTranscriptSyncState {\n const { transcript, format = \"auto\" } = options;\n const { currentTime } = useGingerMedia();\n\n const cues = useMemo(() => {\n if (Array.isArray(transcript)) {\n return [...transcript]\n .filter(\n (c) =>\n Number.isFinite(c.startTime) &&\n Number.isFinite(c.endTime) &&\n c.startTime >= 0 &&\n c.endTime >= c.startTime,\n )\n .sort((a, b) => a.startTime - b.startTime);\n }\n return parseString(transcript, format);\n }, [transcript, format]);\n\n const activeIndex = useMemo(() => {\n return findLastCueIndexAtOrBeforeTime(cues, currentTime);\n }, [currentTime, cues]);\n\n const activeCues = useMemo(() => {\n if (activeIndex < 0) return [];\n return cues\n .slice(0, activeIndex + 1)\n .filter((c) => currentTime >= c.startTime && currentTime < c.endTime);\n }, [currentTime, cues, activeIndex]);\n\n return {\n cues,\n activeIndex,\n activeCue: activeIndex >= 0 ? (cues[activeIndex] ?? null) : null,\n activeCues,\n };\n}\n"],"names":["stripHtmlTags","text","parseTimestampToSeconds","raw","segs","h","m","sec","parseTimingLine","line","arrow","start","endMatch","parseSrt","srt","cues","blocks","block","lines","l","i","timingLine","times","startTime","endTime","textLines","a","b","parseVtt","vtt","body","firstBlank","id","parseTranscriptAuto","input","parseString","transcript","format","findLastCueIndexAtOrBeforeTime","currentTime","low","high","best","mid","cue","useGingerTranscriptSync","options","useGingerMedia","useMemo","c","activeIndex","activeCues"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useGingerTranscriptSync.d.ts","sourceRoot":"","sources":["../../src/transcript/useGingerTranscriptSync.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,aAAa,EAA2C,MAAM,mBAAmB,CAAC;AAEhG,MAAM,MAAM,8BAA8B,GAAG;IAC3C,UAAU,EAAE,MAAM,GAAG,aAAa,EAAE,CAAC;IACrC,mGAAmG;IACnG,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,aAAa,GAAG,IAAI,CAAC;IAChC,yFAAyF;IACzF,UAAU,EAAE,aAAa,EAAE,CAAC;CAC7B,CAAC;AAQF;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,8BAA8B,GACtC,yBAAyB,CAoC3B"}
1
+ {"version":3,"file":"useGingerTranscriptSync.d.ts","sourceRoot":"","sources":["../../src/transcript/useGingerTranscriptSync.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,aAAa,EAA2C,MAAM,mBAAmB,CAAC;AAEhG,MAAM,MAAM,8BAA8B,GAAG;IAC3C,UAAU,EAAE,MAAM,GAAG,aAAa,EAAE,CAAC;IACrC,mGAAmG;IACnG,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,aAAa,GAAG,IAAI,CAAC;IAChC,yFAAyF;IACzF,UAAU,EAAE,aAAa,EAAE,CAAC;CAC7B,CAAC;AA4BF;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,8BAA8B,GACtC,yBAAyB,CAoC3B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=useGingerTranscriptSync.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useGingerTranscriptSync.test.d.ts","sourceRoot":"","sources":["../../src/transcript/useGingerTranscriptSync.test.tsx"],"names":[],"mappings":""}
@@ -1,2 +1,2 @@
1
- "use strict";const t=require("react"),z=require("./useGinger-BXgia32v.cjs"),N=require("./liveAudioGraph-0cpHD_Ic.cjs"),w=require("./GingerSplitContexts-C7puo0M7.cjs"),U=require("./selectors-YXnP8Y8g.cjs"),D=require("./ginger-DrD8F4HX.cjs"),C=new Uint8Array(0),F=new Uint8Array(0);function X(s={}){const{enabled:e=!0,fftSize:r=2048,smoothingTimeConstant:n=.8,minDecibels:u=-100,maxDecibels:c=-30}=s,{audioRef:a,state:o}=z.useGinger(),d=t.useMemo(()=>({fftSize:r,smoothingTimeConstant:n,minDecibels:u,maxDecibels:c}),[r,n,u,c]),[l,m]=t.useState(0),[f,i]=t.useState(null),[h,R]=t.useState(!1),[S,p]=t.useState({frequencyBinCount:0,sampleRate:0}),g=t.useRef(C),v=t.useRef(F),E=t.useCallback(async()=>{const x=y.current;x&&x.state==="suspended"&&await x.resume()},[]),y=t.useRef(null),I=t.useRef(null);return t.useLayoutEffect(()=>{if(!e||typeof window>"u")return;let x=!1,b=null,A=null,T=0;const q=()=>{const k=y.current;k&&R(k.state==="suspended")},V=()=>{if(x)return;const k=I.current,P=g.current,M=v.current;k&&P.length>0&&M.length>0&&(k.getByteFrequencyData(P),k.getByteTimeDomainData(M),m(G=>G+1)),T=requestAnimationFrame(V)},B=()=>{const k=a.current;if(!k||x)return"no-element";try{const{id:P,context:M,analyser:G}=N.attachLiveAnalyser(k,d);b=P,A=k,y.current=M,I.current=G,R(M.state==="suspended"),i(null),M.addEventListener("statechange",q);const L=G.frequencyBinCount,H=G.fftSize;return g.current=new Uint8Array(L),v.current=new Uint8Array(H),p({frequencyBinCount:L,sampleRate:M.sampleRate}),T=requestAnimationFrame(V),"ok"}catch(P){const M=P instanceof Error?P.message:"Failed to attach live analyser";return i(M),y.current=null,I.current=null,g.current=C,v.current=F,p({frequencyBinCount:0,sampleRate:0}),"error"}},K=B();if(K!=="ok"){let k=0;const P=120;let M=0;const G=()=>{if(x)return;const L=B();L==="ok"||L==="error"||(M+=1,!(M>=P)&&(k=requestAnimationFrame(G)))};return K==="no-element"&&(k=requestAnimationFrame(G)),()=>{var L;x=!0,cancelAnimationFrame(k),cancelAnimationFrame(T),b!=null&&A&&N.detachLiveAnalyser(A,b),(L=y.current)==null||L.removeEventListener("statechange",q),y.current=null,I.current=null,g.current=C,v.current=F}}return()=>{var k;x=!0,cancelAnimationFrame(T),b!=null&&A&&N.detachLiveAnalyser(A,b),(k=y.current)==null||k.removeEventListener("statechange",q),y.current=null,I.current=null,g.current=C,v.current=F,p({frequencyBinCount:0,sampleRate:0})}},[e,a,d,o.currentIndex]),{frequencyData:g.current,timeDomainData:v.current,frequencyBinCount:S.frequencyBinCount,sampleRate:S.sampleRate,isSuspended:h,error:f,resume:E,frame:l}}function O(s=!0,e={}){const{togglePlayPause:r,next:n,prev:u}=w.useGingerPlayback(),{toggleMute:c,seek:a,currentTime:o,duration:d}=w.useGingerMedia(),{mute:l,seekForward:m,seekBackward:f}=e;t.useEffect(()=>{if(!s||typeof window>"u")return;const i=(e.playPause??" ").toLowerCase(),h=(e.next??"ArrowRight").toLowerCase(),R=(e.previous??"ArrowLeft").toLowerCase(),S=l==null?void 0:l.toLowerCase(),p=m==null?void 0:m.toLowerCase(),g=f==null?void 0:f.toLowerCase(),v=e.seekSeconds??5,E=y=>{const I=y.target;if(I&&(["INPUT","TEXTAREA","SELECT"].includes(I.tagName)||I.isContentEditable))return;const x=y.key.toLowerCase();if(x===i)y.preventDefault(),r();else if(x===h)y.preventDefault(),n();else if(x===R)y.preventDefault(),u();else if(S&&x===S)y.preventDefault(),c();else if(p&&x===p){y.preventDefault();const b=d>0?d:Number.POSITIVE_INFINITY;a(Math.min(b,o+v))}else g&&x===g&&(y.preventDefault(),a(Math.max(0,o-v)))};return window.addEventListener("keydown",E),()=>window.removeEventListener("keydown",E)},[e.next,e.playPause,e.previous,e.seekSeconds,o,d,s,l,n,u,a,f,m,c,r])}function Y(s){const{durationMs:e,stopAfterTracks:r,respectPause:n=!0,enabled:u=!0,onFire:c}=s,{currentIndex:a,pause:o,isPaused:d}=w.useGingerPlayback(),l=t.useRef(r??0),m=t.useRef(a),f=t.useRef(e??0),i=t.useRef(null);t.useEffect(()=>{l.current=r??0},[r]);const h=t.useRef(e);t.useEffect(()=>{h.current!==e&&(f.current=e??0,h.current=e)},[e]),t.useEffect(()=>{if(!u||!e||e<=0){f.current=e??0,i.current=null;return}if(n&&d){if(i.current!==null){const S=Date.now()-i.current;f.current=Math.max(0,f.current-S),i.current=null}return}i.current=Date.now();const R=setTimeout(()=>{f.current=0,i.current=null,o(),c==null||c()},f.current);return()=>{if(clearTimeout(R),i.current!==null){const S=Date.now()-i.current;f.current=Math.max(0,f.current-S),i.current=null}}},[e,u,d,c,o,n]),t.useEffect(()=>{if(!u||!r||r<=0)return;const R=m.current;m.current=a,a!==R&&(l.current-=1,l.current<=0&&(o(),c==null||c()))},[a,u,c,o,r])}function _(s=!1){const e=w.useGingerState(),r=t.useRef(e);t.useEffect(()=>{if(!s||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},[s,e])}function j(s){return Math.max(0,Math.min(1,s))}function J(s){const e=w.useGingerMedia(),r=w.useGingerPlayback(),{seek:n}=e,[u,c]=t.useState(0),[a,o]=t.useState(!1),d=U.progressFraction(w.gingerStateFromContextValues(r,e)),l=a?u:d,m=t.useCallback(f=>{if(!(s>0))return;const i=f.currentTarget,h=i.getBoundingClientRect(),R=g=>{const v=j((g-h.left)/h.width);c(v),n(v*s)};o(!0),i.setPointerCapture(f.pointerId),R(f.clientX);const S=g=>R(g.clientX),p=g=>{R(g.clientX),o(!1),i.releasePointerCapture(f.pointerId),i.removeEventListener("pointermove",S),i.removeEventListener("pointerup",p),i.removeEventListener("pointercancel",p)};i.addEventListener("pointermove",S),i.addEventListener("pointerup",p),i.addEventListener("pointercancel",p)},[s,n]);return{fraction:u,displayFraction:l,isDragging:a,onPointerDown:m}}function Q(s={}){const{enabled:e=!0,crossOrigin:r}=s,{tracks:n,currentIndex:u,repeatMode:c,playbackMode:a}=w.useGingerPlayback();t.useEffect(()=>{var m;if(!e||typeof document>"u")return;const o=U.computeNextIndex({tracks:n,currentIndex:u,repeatMode:c,playbackMode:a});if(o===u)return;const d=((m=n[o])==null?void 0:m.fileUrl)??"";if(!d)return;const l=document.createElement("audio");return l.preload="auto",r&&(l.crossOrigin=r),l.src=d,l.load(),()=>{l.removeAttribute("src"),l.load()}},[e,r,n,u,c,a])}function W(s={}){let e=D.createInitialState({tracks:s.tracks??[],currentIndex:s.currentIndex,playlistMeta:s.playlistMeta,isPaused:s.isPaused,isShuffled:s.isShuffled,repeatMode:s.repeatMode,playbackMode:s.playbackMode,volume:s.volume,muted:s.muted,playbackRate:s.playbackRate});const r=new Set,n=a=>{const o=D.gingerReducer(e,a);if(o!==e){e=o;for(const d of r)d(e)}};return{getState:()=>e,dispatch:n,subscribe:a=>(r.add(a),()=>r.delete(a)),init:a=>{n({type:"INIT",payload:a})},clampVolume:D.clampVolume,clampPlaybackRate:D.clampPlaybackRate}}function Z(s={}){const{maxLength:e=50}=s,{tracks:r,currentIndex:n}=w.useGingerPlayback(),[u,c]=t.useState([]),a=t.useRef(null),o=t.useRef(r);o.current=r,t.useEffect(()=>{const l=r[n];if(!l||a.current===n)return;a.current=n;const m={track:l,index:n,playedAt:Date.now()};c(f=>{const i=[...f,m];return i.length>e?i.slice(i.length-e):i})},[n,r,e]);const d=t.useCallback(()=>c([]),[]);return{history:u,clearHistory:d}}function $(){const{setVolume:s,volume:e}=w.useGingerMedia(),[r,n]=t.useState(!1),u=t.useRef(0),c=t.useRef(!1),a=t.useCallback(()=>{cancelAnimationFrame(u.current),c.current=!0,n(!1)},[]);return{fadeVolumeTo:t.useCallback(({targetVolume:d,durationMs:l,onComplete:m})=>{cancelAnimationFrame(u.current),c.current=!1;const f=p=>Math.min(1,Math.max(0,p)),i=f(d),h=performance.now();let R=e;n(!0);const S=p=>{if(c.current)return;const g=p-h,v=Math.min(1,g/Math.max(1,l)),E=R+(i-R)*v;s(f(E)),v<1?u.current=requestAnimationFrame(S):(n(!1),m==null||m())};u.current=requestAnimationFrame(p=>{R=e,S(p)})},[s,e]),cancelFade:a,isFading:r}}function ee(){const{tracks:s,currentIndex:e}=w.useGingerPlayback(),{currentTime:r,duration:n}=w.useGingerMedia(),u=t.useMemo(()=>{var a;return[...((a=s[e])==null?void 0:a.chapters)??[]].filter(o=>o&&Number.isFinite(o.startSeconds)&&o.startSeconds>=0).sort((o,d)=>o.startSeconds-d.startSeconds)},[s,e]);return t.useMemo(()=>{if(u.length===0)return{progress:0,elapsed:0,remaining:0};let c=-1;for(let h=u.length-1;h>=0;h--)if(r>=u[h].startSeconds){c=h;break}if(c===-1)return{progress:0,elapsed:0,remaining:0};const a=u[c],o=u[c+1],d=(o==null?void 0:o.startSeconds)??(n>0?n:r),l=Math.max(0,d-a.startSeconds),m=Math.max(0,r-a.startSeconds),f=Math.max(0,d-r);return{progress:l>0?Math.min(1,m/l):0,elapsed:m,remaining:f}},[u,r,n])}exports.createGingerStore=W;exports.useGingerChapterProgress=ee;exports.useGingerDebugLog=_;exports.useGingerKeyboardShortcuts=O;exports.useGingerLiveAnalyzer=X;exports.useGingerPlaybackHistory=Z;exports.useGingerSleepTimer=Y;exports.useGingerVolumeFade=$;exports.useNextTrackPrefetch=Q;exports.useSeekDrag=J;
2
- //# sourceMappingURL=useGingerChapterProgress-TeWWJ8Fd.cjs.map
1
+ "use strict";const t=require("react"),z=require("./useGinger-BXgia32v.cjs"),N=require("./liveAudioGraph-0cpHD_Ic.cjs"),w=require("./GingerSplitContexts-C7puo0M7.cjs"),U=require("./selectors-YXnP8Y8g.cjs"),D=require("./ginger-DFdZGaMi.cjs"),C=new Uint8Array(0),F=new Uint8Array(0);function X(s={}){const{enabled:e=!0,fftSize:r=2048,smoothingTimeConstant:n=.8,minDecibels:u=-100,maxDecibels:c=-30}=s,{audioRef:a,state:o}=z.useGinger(),d=t.useMemo(()=>({fftSize:r,smoothingTimeConstant:n,minDecibels:u,maxDecibels:c}),[r,n,u,c]),[l,m]=t.useState(0),[f,i]=t.useState(null),[h,R]=t.useState(!1),[S,p]=t.useState({frequencyBinCount:0,sampleRate:0}),g=t.useRef(C),v=t.useRef(F),E=t.useCallback(async()=>{const x=y.current;x&&x.state==="suspended"&&await x.resume()},[]),y=t.useRef(null),I=t.useRef(null);return t.useLayoutEffect(()=>{if(!e||typeof window>"u")return;let x=!1,b=null,A=null,T=0;const q=()=>{const k=y.current;k&&R(k.state==="suspended")},V=()=>{if(x)return;const k=I.current,P=g.current,M=v.current;k&&P.length>0&&M.length>0&&(k.getByteFrequencyData(P),k.getByteTimeDomainData(M),m(G=>G+1)),T=requestAnimationFrame(V)},B=()=>{const k=a.current;if(!k||x)return"no-element";try{const{id:P,context:M,analyser:G}=N.attachLiveAnalyser(k,d);b=P,A=k,y.current=M,I.current=G,R(M.state==="suspended"),i(null),M.addEventListener("statechange",q);const L=G.frequencyBinCount,H=G.fftSize;return g.current=new Uint8Array(L),v.current=new Uint8Array(H),p({frequencyBinCount:L,sampleRate:M.sampleRate}),T=requestAnimationFrame(V),"ok"}catch(P){const M=P instanceof Error?P.message:"Failed to attach live analyser";return i(M),y.current=null,I.current=null,g.current=C,v.current=F,p({frequencyBinCount:0,sampleRate:0}),"error"}},K=B();if(K!=="ok"){let k=0;const P=120;let M=0;const G=()=>{if(x)return;const L=B();L==="ok"||L==="error"||(M+=1,!(M>=P)&&(k=requestAnimationFrame(G)))};return K==="no-element"&&(k=requestAnimationFrame(G)),()=>{var L;x=!0,cancelAnimationFrame(k),cancelAnimationFrame(T),b!=null&&A&&N.detachLiveAnalyser(A,b),(L=y.current)==null||L.removeEventListener("statechange",q),y.current=null,I.current=null,g.current=C,v.current=F}}return()=>{var k;x=!0,cancelAnimationFrame(T),b!=null&&A&&N.detachLiveAnalyser(A,b),(k=y.current)==null||k.removeEventListener("statechange",q),y.current=null,I.current=null,g.current=C,v.current=F,p({frequencyBinCount:0,sampleRate:0})}},[e,a,d,o.currentIndex]),{frequencyData:g.current,timeDomainData:v.current,frequencyBinCount:S.frequencyBinCount,sampleRate:S.sampleRate,isSuspended:h,error:f,resume:E,frame:l}}function O(s=!0,e={}){const{togglePlayPause:r,next:n,prev:u}=w.useGingerPlayback(),{toggleMute:c,seek:a,currentTime:o,duration:d}=w.useGingerMedia(),{mute:l,seekForward:m,seekBackward:f}=e;t.useEffect(()=>{if(!s||typeof window>"u")return;const i=(e.playPause??" ").toLowerCase(),h=(e.next??"ArrowRight").toLowerCase(),R=(e.previous??"ArrowLeft").toLowerCase(),S=l==null?void 0:l.toLowerCase(),p=m==null?void 0:m.toLowerCase(),g=f==null?void 0:f.toLowerCase(),v=e.seekSeconds??5,E=y=>{const I=y.target;if(I&&(["INPUT","TEXTAREA","SELECT"].includes(I.tagName)||I.isContentEditable))return;const x=y.key.toLowerCase();if(x===i)y.preventDefault(),r();else if(x===h)y.preventDefault(),n();else if(x===R)y.preventDefault(),u();else if(S&&x===S)y.preventDefault(),c();else if(p&&x===p){y.preventDefault();const b=d>0?d:Number.POSITIVE_INFINITY;a(Math.min(b,o+v))}else g&&x===g&&(y.preventDefault(),a(Math.max(0,o-v)))};return window.addEventListener("keydown",E),()=>window.removeEventListener("keydown",E)},[e.next,e.playPause,e.previous,e.seekSeconds,o,d,s,l,n,u,a,f,m,c,r])}function Y(s){const{durationMs:e,stopAfterTracks:r,respectPause:n=!0,enabled:u=!0,onFire:c}=s,{currentIndex:a,pause:o,isPaused:d}=w.useGingerPlayback(),l=t.useRef(r??0),m=t.useRef(a),f=t.useRef(e??0),i=t.useRef(null);t.useEffect(()=>{l.current=r??0},[r]);const h=t.useRef(e);t.useEffect(()=>{h.current!==e&&(f.current=e??0,h.current=e)},[e]),t.useEffect(()=>{if(!u||!e||e<=0){f.current=e??0,i.current=null;return}if(n&&d){if(i.current!==null){const S=Date.now()-i.current;f.current=Math.max(0,f.current-S),i.current=null}return}i.current=Date.now();const R=setTimeout(()=>{f.current=0,i.current=null,o(),c==null||c()},f.current);return()=>{if(clearTimeout(R),i.current!==null){const S=Date.now()-i.current;f.current=Math.max(0,f.current-S),i.current=null}}},[e,u,d,c,o,n]),t.useEffect(()=>{if(!u||!r||r<=0)return;const R=m.current;m.current=a,a!==R&&(l.current-=1,l.current<=0&&(o(),c==null||c()))},[a,u,c,o,r])}function _(s=!1){const e=w.useGingerState(),r=t.useRef(e);t.useEffect(()=>{if(!s||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},[s,e])}function j(s){return Math.max(0,Math.min(1,s))}function J(s){const e=w.useGingerMedia(),r=w.useGingerPlayback(),{seek:n}=e,[u,c]=t.useState(0),[a,o]=t.useState(!1),d=U.progressFraction(w.gingerStateFromContextValues(r,e)),l=a?u:d,m=t.useCallback(f=>{if(!(s>0))return;const i=f.currentTarget,h=i.getBoundingClientRect(),R=g=>{const v=j((g-h.left)/h.width);c(v),n(v*s)};o(!0),i.setPointerCapture(f.pointerId),R(f.clientX);const S=g=>R(g.clientX),p=g=>{R(g.clientX),o(!1),i.releasePointerCapture(f.pointerId),i.removeEventListener("pointermove",S),i.removeEventListener("pointerup",p),i.removeEventListener("pointercancel",p)};i.addEventListener("pointermove",S),i.addEventListener("pointerup",p),i.addEventListener("pointercancel",p)},[s,n]);return{fraction:u,displayFraction:l,isDragging:a,onPointerDown:m}}function Q(s={}){const{enabled:e=!0,crossOrigin:r}=s,{tracks:n,currentIndex:u,repeatMode:c,playbackMode:a}=w.useGingerPlayback();t.useEffect(()=>{var m;if(!e||typeof document>"u")return;const o=U.computeNextIndex({tracks:n,currentIndex:u,repeatMode:c,playbackMode:a});if(o===u)return;const d=((m=n[o])==null?void 0:m.fileUrl)??"";if(!d)return;const l=document.createElement("audio");return l.preload="auto",r&&(l.crossOrigin=r),l.src=d,l.load(),()=>{l.removeAttribute("src"),l.load()}},[e,r,n,u,c,a])}function W(s={}){let e=D.createInitialState({tracks:s.tracks??[],currentIndex:s.currentIndex,playlistMeta:s.playlistMeta,isPaused:s.isPaused,isShuffled:s.isShuffled,repeatMode:s.repeatMode,playbackMode:s.playbackMode,volume:s.volume,muted:s.muted,playbackRate:s.playbackRate});const r=new Set,n=a=>{const o=D.gingerReducer(e,a);if(o!==e){e=o;for(const d of r)d(e)}};return{getState:()=>e,dispatch:n,subscribe:a=>(r.add(a),()=>r.delete(a)),init:a=>{n({type:"INIT",payload:a})},clampVolume:D.clampVolume,clampPlaybackRate:D.clampPlaybackRate}}function Z(s={}){const{maxLength:e=50}=s,{tracks:r,currentIndex:n}=w.useGingerPlayback(),[u,c]=t.useState([]),a=t.useRef(null),o=t.useRef(r);o.current=r,t.useEffect(()=>{const l=r[n];if(!l||a.current===n)return;a.current=n;const m={track:l,index:n,playedAt:Date.now()};c(f=>{const i=[...f,m];return i.length>e?i.slice(i.length-e):i})},[n,r,e]);const d=t.useCallback(()=>c([]),[]);return{history:u,clearHistory:d}}function $(){const{setVolume:s,volume:e}=w.useGingerMedia(),[r,n]=t.useState(!1),u=t.useRef(0),c=t.useRef(!1),a=t.useCallback(()=>{cancelAnimationFrame(u.current),c.current=!0,n(!1)},[]);return{fadeVolumeTo:t.useCallback(({targetVolume:d,durationMs:l,onComplete:m})=>{cancelAnimationFrame(u.current),c.current=!1;const f=p=>Math.min(1,Math.max(0,p)),i=f(d),h=performance.now();let R=e;n(!0);const S=p=>{if(c.current)return;const g=p-h,v=Math.min(1,g/Math.max(1,l)),E=R+(i-R)*v;s(f(E)),v<1?u.current=requestAnimationFrame(S):(n(!1),m==null||m())};u.current=requestAnimationFrame(p=>{R=e,S(p)})},[s,e]),cancelFade:a,isFading:r}}function ee(){const{tracks:s,currentIndex:e}=w.useGingerPlayback(),{currentTime:r,duration:n}=w.useGingerMedia(),u=t.useMemo(()=>{var a;return[...((a=s[e])==null?void 0:a.chapters)??[]].filter(o=>o&&Number.isFinite(o.startSeconds)&&o.startSeconds>=0).sort((o,d)=>o.startSeconds-d.startSeconds)},[s,e]);return t.useMemo(()=>{if(u.length===0)return{progress:0,elapsed:0,remaining:0};let c=-1;for(let h=u.length-1;h>=0;h--)if(r>=u[h].startSeconds){c=h;break}if(c===-1)return{progress:0,elapsed:0,remaining:0};const a=u[c],o=u[c+1],d=(o==null?void 0:o.startSeconds)??(n>0?n:r),l=Math.max(0,d-a.startSeconds),m=Math.max(0,r-a.startSeconds),f=Math.max(0,d-r);return{progress:l>0?Math.min(1,m/l):0,elapsed:m,remaining:f}},[u,r,n])}exports.createGingerStore=W;exports.useGingerChapterProgress=ee;exports.useGingerDebugLog=_;exports.useGingerKeyboardShortcuts=O;exports.useGingerLiveAnalyzer=X;exports.useGingerPlaybackHistory=Z;exports.useGingerSleepTimer=Y;exports.useGingerVolumeFade=$;exports.useNextTrackPrefetch=Q;exports.useSeekDrag=J;
2
+ //# sourceMappingURL=useGingerChapterProgress-COLWYX2-.cjs.map