@bendyline/squisq-video-react 1.0.0 → 1.0.2

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 (38) hide show
  1. package/README.md +137 -0
  2. package/dist/VideoExportButton.d.ts +28 -0
  3. package/dist/VideoExportButton.d.ts.map +1 -0
  4. package/dist/VideoExportButton.js +18 -0
  5. package/dist/VideoExportButton.js.map +1 -0
  6. package/dist/VideoExportModal.d.ts +26 -0
  7. package/dist/VideoExportModal.d.ts.map +1 -0
  8. package/dist/VideoExportModal.js +164 -0
  9. package/dist/VideoExportModal.js.map +1 -0
  10. package/dist/hooks/useFrameCapture.d.ts +27 -0
  11. package/dist/hooks/useFrameCapture.d.ts.map +1 -0
  12. package/dist/hooks/useFrameCapture.js +211 -0
  13. package/dist/hooks/useFrameCapture.js.map +1 -0
  14. package/dist/hooks/useVideoExport.d.ts +75 -0
  15. package/dist/hooks/useVideoExport.d.ts.map +1 -0
  16. package/dist/hooks/useVideoExport.js +241 -0
  17. package/dist/hooks/useVideoExport.js.map +1 -0
  18. package/dist/index.d.ts +19 -171
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +21 -815
  21. package/dist/index.js.map +1 -1
  22. package/dist/mainThreadEncoder.d.ts +37 -0
  23. package/dist/mainThreadEncoder.d.ts.map +1 -0
  24. package/dist/mainThreadEncoder.js +100 -0
  25. package/dist/mainThreadEncoder.js.map +1 -0
  26. package/dist/mp4Mux.d.ts +22 -0
  27. package/dist/mp4Mux.d.ts.map +1 -0
  28. package/dist/mp4Mux.js +32 -0
  29. package/dist/mp4Mux.js.map +1 -0
  30. package/dist/workers/encode.worker.d.ts +13 -0
  31. package/dist/workers/encode.worker.d.ts.map +1 -0
  32. package/dist/workers/encode.worker.js +318 -0
  33. package/dist/workers/encode.worker.js.map +1 -0
  34. package/dist/workers/workerTypes.d.ts +63 -0
  35. package/dist/workers/workerTypes.d.ts.map +1 -0
  36. package/dist/workers/workerTypes.js +8 -0
  37. package/dist/workers/workerTypes.js.map +1 -0
  38. package/package.json +4 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFrameCapture.js","sourceRoot":"","sources":["../../src/hooks/useFrameCapture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,UAAU,EAAa,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAGrD,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAElE,OAAO,WAAW,MAAM,aAAa,CAAC;AAetC,yEAAyE;AACzE,MAAM,QAAQ,GAA2B;IACvC,GAAG,EAAE,YAAY;IACjB,IAAI,EAAE,YAAY;IAClB,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,WAAW;IAChB,IAAI,EAAE,YAAY;IAClB,GAAG,EAAE,eAAe;IACpB,GAAG,EAAE,WAAW;IAChB,IAAI,EAAE,YAAY;CACnB,CAAC;AAEF,iFAAiF;AACjF,SAAS,oBAAoB,CAAC,MAAmB,EAAE,IAAY;IAC7D,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IACrC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,KAAK,GAAG,IAAI,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,QAAQ,IAAI,WAAW,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;AACxD,CAAC;AAED;;GAEG;AACH,SAAS,oBAAoB,CAAC,MAAgC;IAC5D,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC5C,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACpC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;QACvD,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,0BAA0B,CAAC;QACzD,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,oBAAoB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;QACvD,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5B,CAAC;IAED,OAAO;QACL,KAAK,CAAC,UAAU,CAAC,YAAoB;YACnC,OAAO,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,YAAY,CAAC;QACpD,CAAC;QACD,KAAK,CAAC,SAAS;YACb,OAAO,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBACzC,IAAI;gBACJ,QAAQ,EAAE,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,0BAA0B;gBAC3D,IAAI,EAAE,CAAC;aACR,CAAC,CAAC,CAAC;QACN,CAAC;QACD,KAAK,CAAC,QAAQ;YACZ,MAAM,IAAI,KAAK,CAAC,WAAW,CAAC,CAAC;QAC/B,CAAC;QACD,KAAK,CAAC,WAAW;YACf,MAAM,IAAI,KAAK,CAAC,WAAW,CAAC,CAAC;QAC/B,CAAC;QACD,OAAO,KAAI,CAAC;KACb,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,YAAY,GAAG,MAAM,CAAwB,IAAI,CAAC,CAAC;IACzD,MAAM,OAAO,GAAG,MAAM,CAAc,IAAI,CAAC,CAAC;IAC1C,MAAM,aAAa,GAAG,MAAM,CAAoC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAE/F,MAAM,IAAI,GAAG,WAAW,CACtB,KAAK,EACH,GAAQ,EACR,aAAsD,EACtD,WAAyB,EACR,EAAE;QACnB,mCAAmC;QACnC,mEAAmE;QACnE,qEAAqE;QACrE,IAAI,OAAO,CAAC,OAAO,IAAI,YAAY,CAAC,OAAO,EAAE,CAAC;YAC5C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;YAChC,MAAM,YAAY,GAAG,YAAY,CAAC,OAAO,CAAC;YAC1C,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;YACvB,YAAY,CAAC,OAAO,GAAG,IAAI,CAAC;YAC5B,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;gBAClC,UAAU,CAAC,GAAG,EAAE;oBACd,IAAI,OAAO;wBAAE,OAAO,CAAC,OAAO,EAAE,CAAC;oBAC/B,IAAI,YAAY;wBAAE,YAAY,CAAC,MAAM,EAAE,CAAC;oBACxC,OAAO,EAAE,CAAC;gBACZ,CAAC,EAAE,CAAC,CAAC,CAAC;YACR,CAAC,CAAC,CAAC;QACL,CAAC;QAED,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,IAAI,IAAI,CAAC;QAC1C,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,IAAI,IAAI,CAAC;QAC5C,aAAa,CAAC,OAAO,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAE1C,4BAA4B;QAC5B,MAAM,SAAS,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAChD,SAAS,CAAC,KAAK,CAAC,OAAO;YACrB,qCAAqC,KAAK,aAAa,MAAM,KAAK;gBAClE,2DAA2D,CAAC;QAC9D,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QACrC,YAAY,CAAC,OAAO,GAAG,SAAS,CAAC;QAEjC,qBAAqB;QACrB,MAAM,UAAU,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QACjD,UAAU,CAAC,EAAE,GAAG,qBAAqB,CAAC;QACtC,UAAU,CAAC,KAAK,CAAC,OAAO,GAAG,SAAS,KAAK,aAAa,MAAM,KAAK,CAAC;QAClE,SAAS,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;QAElC,mCAAmC;QACnC,MAAM,aAAa,GAAG,aAAa,CAAC,MAAM;YACxC,CAAC,CAAC,oBAAoB,CAAC,aAAa,CAAC,MAAM,CAAC;YAC5C,CAAC,CAAC,IAAI,CAAC;QAET,0CAA0C;QAC1C,MAAM,IAAI,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;QACpC,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;QAEvB,wCAAwC;QACxC,MAAM,eAAe,GAAG,WAAW,KAAK,SAAS,IAAI,WAAW,KAAK,KAAK,CAAC;QAC3E,MAAM,YAAY,GAAiB,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC;QAEpF,MAAM,aAAa,GAAG,aAAa,CAAC,SAAS,EAAE;YAC7C,MAAM,EAAE,GAAG;YACX,QAAQ,EAAE,GAAG;YACb,UAAU,EAAE,IAAI;YAChB,YAAY,EAAE,KAAK;YACnB,QAAQ,EAAE,KAAK;YACf,aAAa,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE;YAChD,eAAe;YACf,YAAY;SACb,CAAC,CAAC;QAEH,wEAAwE;QACxE,yEAAyE;QACzE,qEAAqE;QACrE,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;QAE7D,IAAI,aAAa,EAAE,CAAC;YAClB,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,aAAa,CAAC,CAAC,CAAC;QAC7F,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;QAC7B,CAAC;QAED,8CAA8C;QAC9C,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC7C,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC9B,MAAM,CAAC,GAAG,MAAsB,CAAC;gBACjC,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC;gBAC/C,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,WAAW,KAAK,UAAU,CAAC;gBACnD,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,EAAE,aAAa,CAAC,sBAAsB,CAAC,CAAC;gBAC3E,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,aAAa,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;gBAChF,MAAM,CACJ,IAAI,KAAK,CACP,4CAA4C;oBAC1C,UAAU,OAAO,iBAAiB,MAAM,YAAY,SAAS,UAAU,CAAC,CAAC,MAAM,EAAE,CACpF,CACF,CAAC;YACJ,CAAC,EAAE,KAAK,CAAC,CAAC;YAEV,MAAM,QAAQ,GAAG,GAAG,EAAE;gBACpB,MAAM,CAAC,GAAG,MAAsB,CAAC;gBACjC,IAAI,OAAO,CAAC,CAAC,WAAW,KAAK,UAAU,IAAI,OAAO,CAAC,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;oBAC1E,YAAY,CAAC,OAAO,CAAC,CAAC;oBACtB,MAAM,QAAQ,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;oBACjC,OAAO,CAAC,QAAQ,CAAC,CAAC;gBACpB,CAAC;qBAAM,CAAC;oBACN,qBAAqB,CAAC,QAAQ,CAAC,CAAC;gBAClC,CAAC;YACH,CAAC,CAAC;YAEF,8CAA8C;YAC9C,UAAU,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;IACL,CAAC,EACD,EAAE,CACH,CAAC;IAEF,MAAM,YAAY,GAAG,WAAW,CAAC,KAAK,EAAE,IAAY,EAAwB,EAAE;QAC5E,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC;QACvC,MAAM,CAAC,GAAG,MAAsB,CAAC;QACjC,IAAI,CAAC,SAAS,IAAI,OAAO,CAAC,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YACjD,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACvE,CAAC;QAED,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC;QAEhD,qCAAqC;QACrC,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAErB,wCAAwC;QACxC,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAClC,qBAAqB,CAAC,GAAG,EAAE,CAAC,qBAAqB,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CACpE,CAAC;QAEF,MAAM,IAAI,GAAG,SAAS,CAAC,aAAa,CAAC,sBAAsB,CAAgB,CAAC;QAC5E,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QACpD,CAAC;QAED,8CAA8C;QAC9C,yEAAyE;QACzE,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE;YACrC,KAAK;YACL,MAAM;YACN,KAAK,EAAE,CAAC;YACR,OAAO,EAAE,IAAI;YACb,UAAU,EAAE,IAAI;YAChB,eAAe,EAAE,SAAS;YAC1B,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;QAEH,8DAA8D;QAC9D,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,MAAM,CAAC,CAAC;QAC/C,OAAO,MAAM,CAAC;IAChB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE;QAC/B,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YAC1B,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;QACzB,CAAC;QACD,IAAI,YAAY,CAAC,OAAO,EAAE,CAAC;YACzB,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YAC9B,YAAY,CAAC,OAAO,GAAG,IAAI,CAAC;QAC9B,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,4DAA4D;IAC5D,oDAAoD;IACpD,OAAO,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC;AACzF,CAAC"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * useVideoExport — Main orchestration hook for browser video export.
3
+ *
4
+ * Coordinates frame capture (hidden iframe + html2canvas) with
5
+ * main-thread WebCodecs encoding via mp4-muxer. Manages the full
6
+ * lifecycle: prepare → capture + encode → download.
7
+ *
8
+ * Encoding runs on the main thread because frame capture via html2canvas
9
+ * (~100-200ms per frame) is the bottleneck, not encoding (~1ms per frame
10
+ * with hardware-accelerated WebCodecs). Worker offloading would add
11
+ * complexity with minimal benefit.
12
+ *
13
+ * Usage:
14
+ * const { state, progress, phase, startExport, cancel, downloadUrl } = useVideoExport();
15
+ * <button onClick={() => startExport(doc, options)}>Export</button>
16
+ */
17
+ import type { Doc } from '@bendyline/squisq/schemas';
18
+ import type { MediaProvider } from '@bendyline/squisq/schemas';
19
+ import type { VideoQuality, VideoOrientation } from '@bendyline/squisq-video';
20
+ import type { CaptionMode } from '@bendyline/squisq-react';
21
+ export type VideoExportState = 'idle' | 'preparing' | 'capturing' | 'encoding' | 'complete' | 'error';
22
+ export interface VideoExportConfig {
23
+ /** Encoding quality preset (default: 'normal') */
24
+ quality?: VideoQuality;
25
+ /** Frames per second (default: 30) */
26
+ fps?: number;
27
+ /** Viewport orientation (default: 'landscape') */
28
+ orientation?: VideoOrientation;
29
+ /**
30
+ * Map of relative image paths to binary data.
31
+ * Used to embed images into the render HTML.
32
+ */
33
+ images?: Map<string, ArrayBuffer>;
34
+ /**
35
+ * Map of audio segment names to binary data.
36
+ * Used to embed audio into the render HTML.
37
+ */
38
+ audio?: Map<string, ArrayBuffer>;
39
+ /** MediaProvider to resolve media URLs (alternative to passing images directly) */
40
+ mediaProvider?: MediaProvider;
41
+ /** Caption mode for the exported video (default: 'off') */
42
+ captionMode?: CaptionMode;
43
+ /** Player IIFE bundle (unused in browser export, kept for CLI/Playwright path) */
44
+ playerScript?: string;
45
+ }
46
+ export interface VideoExportResult {
47
+ /** Current export state */
48
+ state: VideoExportState;
49
+ /** 0–100 progress percentage */
50
+ progress: number;
51
+ /** Human-readable description of the current phase */
52
+ phase: string;
53
+ /** Video duration detected from the doc (seconds) */
54
+ duration: number;
55
+ /** Encoder backend ('webcodecs' when active, null when idle) */
56
+ backend: 'webcodecs' | null;
57
+ /** Blob download URL (populated when state === 'complete') */
58
+ downloadUrl: string | null;
59
+ /** File size in bytes (populated when state === 'complete') */
60
+ fileSize: number;
61
+ /** Error message (populated when state === 'error') */
62
+ error: string | null;
63
+ /** Seconds elapsed since export started */
64
+ elapsed: number;
65
+ /** Estimated seconds remaining (0 when idle or complete) */
66
+ estimatedRemaining: number;
67
+ /** Start a new export */
68
+ startExport: (doc: Doc, config: VideoExportConfig) => Promise<void>;
69
+ /** Cancel an in-progress export */
70
+ cancel: () => void;
71
+ /** Reset state back to idle (e.g., after complete or error) */
72
+ reset: () => void;
73
+ }
74
+ export declare function useVideoExport(): VideoExportResult;
75
+ //# sourceMappingURL=useVideoExport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useVideoExport.d.ts","sourceRoot":"","sources":["../../src/hooks/useVideoExport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,2BAA2B,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAM3D,MAAM,MAAM,gBAAgB,GACxB,MAAM,GACN,WAAW,GACX,WAAW,GACX,UAAU,GACV,UAAU,GACV,OAAO,CAAC;AAEZ,MAAM,WAAW,iBAAiB;IAChC,kDAAkD;IAClD,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,sCAAsC;IACtC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B;;;OAGG;IACH,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAClC;;;OAGG;IACH,KAAK,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACjC,mFAAmF;IACnF,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,2DAA2D;IAC3D,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,kFAAkF;IAClF,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,2BAA2B;IAC3B,KAAK,EAAE,gBAAgB,CAAC;IACxB,gCAAgC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5B,8DAA8D;IAC9D,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,+DAA+D;IAC/D,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,kBAAkB,EAAE,MAAM,CAAC;IAC3B,yBAAyB;IACzB,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,mCAAmC;IACnC,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,+DAA+D;IAC/D,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAID,wBAAgB,cAAc,IAAI,iBAAiB,CA0PlD"}
@@ -0,0 +1,241 @@
1
+ /**
2
+ * useVideoExport — Main orchestration hook for browser video export.
3
+ *
4
+ * Coordinates frame capture (hidden iframe + html2canvas) with
5
+ * main-thread WebCodecs encoding via mp4-muxer. Manages the full
6
+ * lifecycle: prepare → capture + encode → download.
7
+ *
8
+ * Encoding runs on the main thread because frame capture via html2canvas
9
+ * (~100-200ms per frame) is the bottleneck, not encoding (~1ms per frame
10
+ * with hardware-accelerated WebCodecs). Worker offloading would add
11
+ * complexity with minimal benefit.
12
+ *
13
+ * Usage:
14
+ * const { state, progress, phase, startExport, cancel, downloadUrl } = useVideoExport();
15
+ * <button onClick={() => startExport(doc, options)}>Export</button>
16
+ */
17
+ import { useState, useRef, useCallback, useEffect } from 'react';
18
+ import { resolveDimensions } from '@bendyline/squisq-video';
19
+ import { createEncoder, supportsWebCodecs } from '../mainThreadEncoder.js';
20
+ import { useFrameCapture } from './useFrameCapture.js';
21
+ // ── Hook ───────────────────────────────────────────────────────────
22
+ export function useVideoExport() {
23
+ const [state, setState] = useState('idle');
24
+ const [progress, setProgress] = useState(0);
25
+ const [phase, setPhase] = useState('');
26
+ const [duration, setDuration] = useState(0);
27
+ const [backend, setBackend] = useState(null);
28
+ const [downloadUrl, setDownloadUrl] = useState(null);
29
+ const [fileSize, setFileSize] = useState(0);
30
+ const [error, setError] = useState(null);
31
+ const [elapsed, setElapsed] = useState(0);
32
+ const [estimatedRemaining, setEstimatedRemaining] = useState(0);
33
+ const encoderRef = useRef(null);
34
+ const cancelledRef = useRef(false);
35
+ const downloadUrlRef = useRef(null);
36
+ const startTimeRef = useRef(0);
37
+ const elapsedTimerRef = useRef(null);
38
+ const frameCapture = useFrameCapture();
39
+ // Clean up on unmount
40
+ useEffect(() => {
41
+ return () => {
42
+ if (elapsedTimerRef.current)
43
+ clearInterval(elapsedTimerRef.current);
44
+ if (downloadUrlRef.current) {
45
+ URL.revokeObjectURL(downloadUrlRef.current);
46
+ }
47
+ if (encoderRef.current) {
48
+ encoderRef.current.close();
49
+ }
50
+ frameCapture.destroy();
51
+ };
52
+ }, [frameCapture]);
53
+ const reset = useCallback(() => {
54
+ if (downloadUrlRef.current) {
55
+ URL.revokeObjectURL(downloadUrlRef.current);
56
+ downloadUrlRef.current = null;
57
+ }
58
+ if (encoderRef.current) {
59
+ encoderRef.current.close();
60
+ encoderRef.current = null;
61
+ }
62
+ frameCapture.destroy();
63
+ setState('idle');
64
+ setProgress(0);
65
+ setPhase('');
66
+ setDuration(0);
67
+ setBackend(null);
68
+ setDownloadUrl(null);
69
+ setFileSize(0);
70
+ setError(null);
71
+ setElapsed(0);
72
+ setEstimatedRemaining(0);
73
+ if (elapsedTimerRef.current)
74
+ clearInterval(elapsedTimerRef.current);
75
+ cancelledRef.current = false;
76
+ }, [frameCapture]);
77
+ const cancel = useCallback(() => {
78
+ cancelledRef.current = true;
79
+ if (elapsedTimerRef.current)
80
+ clearInterval(elapsedTimerRef.current);
81
+ if (encoderRef.current) {
82
+ encoderRef.current.close();
83
+ encoderRef.current = null;
84
+ }
85
+ frameCapture.destroy();
86
+ setState('idle');
87
+ setProgress(0);
88
+ setPhase('Cancelled');
89
+ }, [frameCapture]);
90
+ const startExport = useCallback(async (doc, config) => {
91
+ // Clear previous state
92
+ cancelledRef.current = false;
93
+ if (downloadUrlRef.current) {
94
+ URL.revokeObjectURL(downloadUrlRef.current);
95
+ downloadUrlRef.current = null;
96
+ }
97
+ setDownloadUrl(null);
98
+ setFileSize(0);
99
+ setError(null);
100
+ const quality = config.quality ?? 'normal';
101
+ const fps = config.fps ?? 30;
102
+ const orientation = config.orientation ?? 'landscape';
103
+ const { width, height } = resolveDimensions({ orientation });
104
+ try {
105
+ // ── Check browser support ─────────────────────────────────
106
+ if (!supportsWebCodecs()) {
107
+ throw new Error('WebCodecs is not available in this browser. ' +
108
+ 'Video export requires Chrome 94+, Edge 94+, or another Chromium-based browser.');
109
+ }
110
+ // ── Step 1: Prepare ───────────────────────────────────────
111
+ setState('preparing');
112
+ setPhase('Loading document…');
113
+ setProgress(0);
114
+ setElapsed(0);
115
+ setEstimatedRemaining(0);
116
+ // Start elapsed timer
117
+ startTimeRef.current = performance.now();
118
+ if (elapsedTimerRef.current)
119
+ clearInterval(elapsedTimerRef.current);
120
+ elapsedTimerRef.current = setInterval(() => {
121
+ setElapsed(Math.floor((performance.now() - startTimeRef.current) / 1000));
122
+ }, 1000);
123
+ // Collect images from MediaProvider if provided and images not passed directly
124
+ let images = config.images;
125
+ if (!images && config.mediaProvider) {
126
+ images = new Map();
127
+ const entries = await config.mediaProvider.listMedia();
128
+ for (const entry of entries) {
129
+ const url = await config.mediaProvider.resolveUrl(entry.name);
130
+ const res = await fetch(url);
131
+ if (res.ok) {
132
+ const data = await res.arrayBuffer();
133
+ images.set(entry.name, data);
134
+ }
135
+ }
136
+ }
137
+ const docDuration = await frameCapture.init(doc, { images, audio: config.audio, width, height }, config.captionMode);
138
+ if (cancelledRef.current)
139
+ return;
140
+ setDuration(docDuration);
141
+ if (docDuration <= 0) {
142
+ throw new Error('Document has zero duration — nothing to export');
143
+ }
144
+ // ── Step 2: Create encoder ────────────────────────────────
145
+ setPhase('Starting encoder…');
146
+ setProgress(5);
147
+ const encoder = createEncoder({ width, height, fps, quality });
148
+ encoderRef.current = encoder;
149
+ setBackend('webcodecs');
150
+ if (cancelledRef.current)
151
+ return;
152
+ // ── Step 3: Capture frames and encode ─────────────────────
153
+ setState('capturing');
154
+ const totalFrames = Math.ceil(docDuration * fps);
155
+ const captureStartTime = performance.now();
156
+ // Throttle UI updates to every ~10 frames to avoid excessive re-renders.
157
+ // Each setState between awaits triggers a separate render cycle.
158
+ const UI_UPDATE_INTERVAL = 10;
159
+ for (let i = 0; i < totalFrames; i++) {
160
+ if (cancelledRef.current)
161
+ return;
162
+ const time = i / fps;
163
+ // Update UI periodically (not every frame)
164
+ if (i % UI_UPDATE_INTERVAL === 0 || i === totalFrames - 1) {
165
+ const captureProgress = Math.round((i / totalFrames) * 90);
166
+ setProgress(5 + captureProgress);
167
+ setPhase(`Capturing frame ${i + 1}/${totalFrames} (${time.toFixed(1)}s)`);
168
+ if (i > 0) {
169
+ const elapsedCapture = (performance.now() - captureStartTime) / 1000;
170
+ const avgPerFrame = elapsedCapture / i;
171
+ const remaining = Math.round(avgPerFrame * (totalFrames - i));
172
+ setEstimatedRemaining(remaining);
173
+ }
174
+ }
175
+ const bitmap = await frameCapture.captureFrame(time);
176
+ if (cancelledRef.current) {
177
+ bitmap.close();
178
+ return;
179
+ }
180
+ // Encode immediately — WebCodecs is fast and async internally
181
+ encoder.encodeFrame(bitmap, i);
182
+ }
183
+ if (cancelledRef.current)
184
+ return;
185
+ // ── Step 4: Finalize MP4 ──────────────────────────────────
186
+ setState('encoding');
187
+ setPhase('Finalizing video…');
188
+ setProgress(95);
189
+ const mp4Buffer = await encoder.finalize();
190
+ encoderRef.current = null;
191
+ if (cancelledRef.current)
192
+ return;
193
+ // ── Step 5: Create download URL ───────────────────────────
194
+ const blob = new Blob([mp4Buffer], { type: 'video/mp4' });
195
+ const url = URL.createObjectURL(blob);
196
+ downloadUrlRef.current = url;
197
+ setDownloadUrl(url);
198
+ setFileSize(mp4Buffer.byteLength);
199
+ setState('complete');
200
+ setProgress(100);
201
+ setPhase('Export complete');
202
+ setEstimatedRemaining(0);
203
+ if (elapsedTimerRef.current)
204
+ clearInterval(elapsedTimerRef.current);
205
+ // Clean up
206
+ frameCapture.destroy();
207
+ }
208
+ catch (err) {
209
+ if (elapsedTimerRef.current)
210
+ clearInterval(elapsedTimerRef.current);
211
+ if (cancelledRef.current)
212
+ return;
213
+ const message = err instanceof Error ? err.message : String(err);
214
+ setState('error');
215
+ setError(message);
216
+ setPhase('Export failed');
217
+ // Clean up on error
218
+ if (encoderRef.current) {
219
+ encoderRef.current.close();
220
+ encoderRef.current = null;
221
+ }
222
+ frameCapture.destroy();
223
+ }
224
+ }, [frameCapture]);
225
+ return {
226
+ state,
227
+ progress,
228
+ phase,
229
+ duration,
230
+ backend,
231
+ downloadUrl,
232
+ fileSize,
233
+ error,
234
+ elapsed,
235
+ estimatedRemaining,
236
+ startExport,
237
+ cancel,
238
+ reset,
239
+ };
240
+ }
241
+ //# sourceMappingURL=useVideoExport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useVideoExport.js","sourceRoot":"","sources":["../../src/hooks/useVideoExport.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,176 +1,24 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { Doc, MediaProvider } from '@bendyline/squisq/schemas';
3
- import { VideoQuality, VideoOrientation, RenderHtmlOptions } from '@bendyline/squisq-video';
4
- import { CaptionMode } from '@bendyline/squisq-react';
5
-
6
- interface VideoExportModalProps {
7
- /** The document to export */
8
- doc: Doc;
9
- /** Player IIFE bundle source */
10
- playerScript: string;
11
- /** Optional media provider for resolving images/audio */
12
- mediaProvider?: MediaProvider;
13
- /** Pre-collected images map (alternative to mediaProvider) */
14
- images?: Map<string, ArrayBuffer>;
15
- /** Pre-collected audio map */
16
- audio?: Map<string, ArrayBuffer>;
17
- /** Called when the modal should close */
18
- onClose: () => void;
19
- }
20
- declare function VideoExportModal({ doc, playerScript, mediaProvider, images, audio, onClose, }: VideoExportModalProps): react_jsx_runtime.JSX.Element;
21
-
22
- interface VideoExportButtonProps {
23
- /** The document to export */
24
- doc: Doc;
25
- /** Player IIFE bundle source */
26
- playerScript: string;
27
- /** Optional media provider for resolving images/audio */
28
- mediaProvider?: MediaProvider;
29
- /** Pre-collected images map */
30
- images?: Map<string, ArrayBuffer>;
31
- /** Pre-collected audio map */
32
- audio?: Map<string, ArrayBuffer>;
33
- /** Button label (default: "Export Video") */
34
- label?: string;
35
- /** Additional inline styles for the button */
36
- style?: React.CSSProperties;
37
- /** Whether the button is disabled */
38
- disabled?: boolean;
39
- }
40
- declare function VideoExportButton({ doc, playerScript, mediaProvider, images, audio, label, style, disabled, }: VideoExportButtonProps): react_jsx_runtime.JSX.Element;
41
-
42
1
  /**
43
- * useVideoExportMain orchestration hook for browser video export.
2
+ * @bendyline/squisq-video-reactBrowser Video Export for Squisq Documents
44
3
  *
45
- * Coordinates frame capture (hidden iframe + html2canvas) with
46
- * main-thread WebCodecs encoding via mp4-muxer. Manages the full
47
- * lifecycle: prepare → capture + encode → download.
4
+ * Provides React components and hooks for exporting Squisq documents
5
+ * to MP4 video directly in the browser.
48
6
  *
49
- * Encoding runs on the main thread because frame capture via html2canvas
50
- * (~100-200ms per frame) is the bottleneck, not encoding (~1ms per frame
51
- * with hardware-accelerated WebCodecs). Worker offloading would add
52
- * complexity with minimal benefit.
7
+ * - VideoExportModal: Full modal UI for configure export download
8
+ * - VideoExportButton: Drop-in button that opens the modal
9
+ * - useVideoExport: Low-level hook for custom UIs
10
+ * - useFrameCapture: Frame capture via hidden iframe + html2canvas
53
11
  *
54
- * Usage:
55
- * const { state, progress, phase, startExport, cancel, downloadUrl } = useVideoExport();
56
- * <button onClick={() => startExport(doc, options)}>Export</button>
12
+ * Encoding: WebCodecs (H.264 via hardware-accelerated VideoEncoder, Chrome 94+)
57
13
  */
58
-
59
- type VideoExportState = 'idle' | 'preparing' | 'capturing' | 'encoding' | 'complete' | 'error';
60
- interface VideoExportConfig {
61
- /** Encoding quality preset (default: 'normal') */
62
- quality?: VideoQuality;
63
- /** Frames per second (default: 30) */
64
- fps?: number;
65
- /** Viewport orientation (default: 'landscape') */
66
- orientation?: VideoOrientation;
67
- /**
68
- * Map of relative image paths to binary data.
69
- * Used to embed images into the render HTML.
70
- */
71
- images?: Map<string, ArrayBuffer>;
72
- /**
73
- * Map of audio segment names to binary data.
74
- * Used to embed audio into the render HTML.
75
- */
76
- audio?: Map<string, ArrayBuffer>;
77
- /** MediaProvider to resolve media URLs (alternative to passing images directly) */
78
- mediaProvider?: MediaProvider;
79
- /** Caption mode for the exported video (default: 'off') */
80
- captionMode?: CaptionMode;
81
- /** Player IIFE bundle (unused in browser export, kept for CLI/Playwright path) */
82
- playerScript?: string;
83
- }
84
- interface VideoExportResult {
85
- /** Current export state */
86
- state: VideoExportState;
87
- /** 0–100 progress percentage */
88
- progress: number;
89
- /** Human-readable description of the current phase */
90
- phase: string;
91
- /** Video duration detected from the doc (seconds) */
92
- duration: number;
93
- /** Encoder backend ('webcodecs' when active, null when idle) */
94
- backend: 'webcodecs' | null;
95
- /** Blob download URL (populated when state === 'complete') */
96
- downloadUrl: string | null;
97
- /** File size in bytes (populated when state === 'complete') */
98
- fileSize: number;
99
- /** Error message (populated when state === 'error') */
100
- error: string | null;
101
- /** Seconds elapsed since export started */
102
- elapsed: number;
103
- /** Estimated seconds remaining (0 when idle or complete) */
104
- estimatedRemaining: number;
105
- /** Start a new export */
106
- startExport: (doc: Doc, config: VideoExportConfig) => Promise<void>;
107
- /** Cancel an in-progress export */
108
- cancel: () => void;
109
- /** Reset state back to idle (e.g., after complete or error) */
110
- reset: () => void;
111
- }
112
- declare function useVideoExport(): VideoExportResult;
113
-
114
- /**
115
- * useFrameCapture — Hidden div + html2canvas frame capture.
116
- *
117
- * Mounts a DocPlayer in renderMode inside a hidden div (same document),
118
- * then captures individual frames by seeking the player and rendering
119
- * the DOM to a canvas via html2canvas.
120
- *
121
- * Uses React directly — no script injection, no iframes, no eval.
122
- *
123
- * Returns an ImageBitmap for each frame (transferable to a Worker).
124
- */
125
-
126
- interface FrameCaptureHandle {
127
- /** Initialize the hidden player. Returns the video duration in seconds. */
128
- init: (doc: Doc, renderOptions: Omit<RenderHtmlOptions, 'playerScript'>, captionMode?: CaptionMode) => Promise<number>;
129
- /** Capture a single frame at the given time (seconds). Returns an ImageBitmap. */
130
- captureFrame: (time: number) => Promise<ImageBitmap>;
131
- /** Clean up resources. */
132
- destroy: () => void;
133
- }
134
- /**
135
- * Hook that manages a hidden div for frame capture.
136
- */
137
- declare function useFrameCapture(): FrameCaptureHandle;
138
-
139
- /**
140
- * Main-thread WebCodecs encoder.
141
- *
142
- * Encodes video frames to MP4 using the WebCodecs API and mp4-muxer,
143
- * running directly on the main thread. This is simpler and avoids
144
- * worker module-resolution issues with bundlers. Since frame capture
145
- * via html2canvas (~100-200ms per frame) is the bottleneck — not
146
- * encoding (~1ms per frame with hardware-accelerated WebCodecs) —
147
- * worker offloading provides minimal benefit.
148
- *
149
- * Requirements: Chrome 94+ / Edge 94+ (WebCodecs support).
150
- */
151
- interface EncoderConfig {
152
- width: number;
153
- height: number;
154
- fps: number;
155
- quality: 'draft' | 'normal' | 'high';
156
- }
157
- interface MainThreadEncoder {
158
- /** Encode a single frame. The bitmap is closed after encoding. */
159
- encodeFrame(bitmap: ImageBitmap, frameIndex: number): void;
160
- /** Flush pending frames and finalize the MP4. Returns the MP4 ArrayBuffer. */
161
- finalize(): Promise<ArrayBuffer>;
162
- /** Close the encoder without producing output (e.g., on cancel). */
163
- close(): void;
164
- }
165
- /**
166
- * Check whether the browser supports WebCodecs video encoding.
167
- */
168
- declare function supportsWebCodecs(): boolean;
169
- /**
170
- * Create a main-thread WebCodecs encoder.
171
- *
172
- * Throws if WebCodecs is not available.
173
- */
174
- declare function createEncoder(config: EncoderConfig): MainThreadEncoder;
175
-
176
- export { type FrameCaptureHandle, type MainThreadEncoder, VideoExportButton, type VideoExportButtonProps, type VideoExportConfig, VideoExportModal, type VideoExportModalProps, type VideoExportResult, type VideoExportState, createEncoder, supportsWebCodecs, useFrameCapture, useVideoExport };
14
+ export { VideoExportModal } from './VideoExportModal.js';
15
+ export type { VideoExportModalProps } from './VideoExportModal.js';
16
+ export { VideoExportButton } from './VideoExportButton.js';
17
+ export type { VideoExportButtonProps } from './VideoExportButton.js';
18
+ export { useVideoExport } from './hooks/useVideoExport.js';
19
+ export type { VideoExportState, VideoExportConfig, VideoExportResult, } from './hooks/useVideoExport.js';
20
+ export { useFrameCapture } from './hooks/useFrameCapture.js';
21
+ export type { FrameCaptureHandle } from './hooks/useFrameCapture.js';
22
+ export { supportsWebCodecs, createEncoder } from './mainThreadEncoder.js';
23
+ export type { MainThreadEncoder } from './mainThreadEncoder.js';
24
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,YAAY,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAEnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAGrE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAGrE,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC1E,YAAY,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC"}