@editframe/react 0.37.3-beta → 0.38.0

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 (42) hide show
  1. package/dist/components/TimelineRoot.d.ts +19 -12
  2. package/dist/components/TimelineRoot.js +34 -34
  3. package/dist/components/TimelineRoot.js.map +1 -1
  4. package/dist/gui/OverlayItem.js.map +1 -1
  5. package/dist/gui/Scrubber.d.ts +2 -0
  6. package/dist/gui/Scrubber.js.map +1 -1
  7. package/dist/{elements → gui}/ThumbnailStrip.d.ts +1 -1
  8. package/dist/{elements → gui}/ThumbnailStrip.js +2 -4
  9. package/dist/gui/ThumbnailStrip.js.map +1 -0
  10. package/dist/gui/TimelineRuler.js.map +1 -1
  11. package/dist/gui/TrimHandles.d.ts +11 -0
  12. package/dist/gui/TrimHandles.js +18 -0
  13. package/dist/gui/TrimHandles.js.map +1 -0
  14. package/dist/hooks/create-element.d.ts +10 -3
  15. package/dist/hooks/create-element.js +4 -1
  16. package/dist/hooks/create-element.js.map +1 -1
  17. package/dist/hooks/useMediaInfo.d.ts +15 -0
  18. package/dist/hooks/useMediaInfo.js +59 -0
  19. package/dist/hooks/useMediaInfo.js.map +1 -0
  20. package/dist/hooks/usePanZoomTransform.js.map +1 -1
  21. package/dist/hooks/useTimingInfo.d.ts +2 -2
  22. package/dist/hooks/useTimingInfo.js +12 -11
  23. package/dist/hooks/useTimingInfo.js.map +1 -1
  24. package/dist/index.d.ts +5 -3
  25. package/dist/index.js +4 -2
  26. package/dist/r3f/CompositionCanvas.d.ts +32 -0
  27. package/dist/r3f/CompositionCanvas.js +94 -0
  28. package/dist/r3f/CompositionCanvas.js.map +1 -0
  29. package/dist/r3f/OffscreenCompositionCanvas.d.ts +28 -0
  30. package/dist/r3f/OffscreenCompositionCanvas.js +119 -0
  31. package/dist/r3f/OffscreenCompositionCanvas.js.map +1 -0
  32. package/dist/r3f/index.d.ts +5 -0
  33. package/dist/r3f/index.js +5 -0
  34. package/dist/r3f/renderOffscreen.d.ts +27 -0
  35. package/dist/r3f/renderOffscreen.js +291 -0
  36. package/dist/r3f/renderOffscreen.js.map +1 -0
  37. package/dist/r3f/worker-protocol.d.ts +39 -0
  38. package/dist/server.d.ts +11 -0
  39. package/dist/server.js +11 -0
  40. package/package.json +45 -11
  41. package/tsdown.config.ts +1 -0
  42. package/dist/elements/ThumbnailStrip.js.map +0 -1
package/dist/index.js CHANGED
@@ -4,7 +4,6 @@ import { Captions, CaptionsActiveWord, CaptionsAfterActiveWord, CaptionsBeforeAc
4
4
  import { Text, TextSegment } from "./elements/Text.js";
5
5
  import { Image } from "./elements/Image.js";
6
6
  import { Surface } from "./elements/Surface.js";
7
- import { ThumbnailStrip } from "./elements/ThumbnailStrip.js";
8
7
  import { Timegroup } from "./elements/Timegroup.js";
9
8
  import { TimelineRoot } from "./components/TimelineRoot.js";
10
9
  import { Video } from "./elements/Video.js";
@@ -24,12 +23,15 @@ import { Pause } from "./gui/Pause.js";
24
23
  import { Play } from "./gui/Play.js";
25
24
  import { Preview } from "./gui/Preview.js";
26
25
  import { Scrubber } from "./gui/Scrubber.js";
26
+ import { ThumbnailStrip } from "./gui/ThumbnailStrip.js";
27
+ import { TrimHandles } from "./gui/TrimHandles.js";
27
28
  import { TimelineRuler } from "./gui/TimelineRuler.js";
28
29
  import { ToggleLoop } from "./gui/ToggleLoop.js";
29
30
  import { TogglePlay } from "./gui/TogglePlay.js";
30
31
  import { Workbench } from "./gui/Workbench.js";
31
32
  import { useTimingInfo } from "./hooks/useTimingInfo.js";
33
+ import { useMediaInfo } from "./hooks/useMediaInfo.js";
32
34
  import { usePanZoomTransform } from "./hooks/usePanZoomTransform.js";
33
35
  import { elementNeedsFitScale, needsFitScale } from "@editframe/elements";
34
36
 
35
- export { Audio, Captions, CaptionsActiveWord, CaptionsAfterActiveWord, CaptionsBeforeActiveWord, CaptionsSegment, Configuration, Controls, Dial, Filmstrip, FitScale, FocusOverlay, Image, OverlayItem, OverlayLayer, PanZoom, Pause, Play, Preview, ResizableBox, Scrubber, Surface, Text, TextSegment, ThumbnailStrip, TimeDisplay, Timegroup, TimelineRoot, TimelineRuler, ToggleLoop, TogglePlay, TransformHandles, Video, Waveform, Workbench, elementNeedsFitScale, needsFitScale, usePanZoomTransform, useTimingInfo };
37
+ export { Audio, Captions, CaptionsActiveWord, CaptionsAfterActiveWord, CaptionsBeforeActiveWord, CaptionsSegment, Configuration, Controls, Dial, Filmstrip, FitScale, FocusOverlay, Image, OverlayItem, OverlayLayer, PanZoom, Pause, Play, Preview, ResizableBox, Scrubber, Surface, Text, TextSegment, ThumbnailStrip, TimeDisplay, Timegroup, TimelineRoot, TimelineRuler, ToggleLoop, TogglePlay, TransformHandles, TrimHandles, Video, Waveform, Workbench, elementNeedsFitScale, needsFitScale, useMediaInfo, usePanZoomTransform, useTimingInfo };
@@ -0,0 +1,32 @@
1
+ import * as React$1 from "react";
2
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
3
+ import { CanvasProps } from "@react-three/fiber";
4
+
5
+ //#region src/r3f/CompositionCanvas.d.ts
6
+
7
+ /**
8
+ * Hook to read the current composition time inside an R3F scene.
9
+ * Must be used within a `<CompositionCanvas>`.
10
+ *
11
+ * @returns { timeMs, durationMs } — current time and total duration in ms
12
+ */
13
+ declare function useCompositionTime(): {
14
+ timeMs: number;
15
+ durationMs: number;
16
+ };
17
+ interface CompositionCanvasProps extends Omit<CanvasProps, "frameloop"> {
18
+ /** Extra styles for the container div */
19
+ containerStyle?: React$1.CSSProperties;
20
+ /** Extra className for the container div */
21
+ containerClassName?: string;
22
+ }
23
+ declare function CompositionCanvas({
24
+ children,
25
+ containerStyle,
26
+ containerClassName,
27
+ gl: glProp,
28
+ ...canvasProps
29
+ }: CompositionCanvasProps): react_jsx_runtime0.JSX.Element;
30
+ //#endregion
31
+ export { CompositionCanvas, CompositionCanvasProps, useCompositionTime };
32
+ //# sourceMappingURL=CompositionCanvas.d.ts.map
@@ -0,0 +1,94 @@
1
+ import { createContext, useContext, useEffect, useLayoutEffect, useRef, useState } from "react";
2
+ import { flushSync } from "react-dom";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { Canvas, useFrame, useThree } from "@react-three/fiber";
5
+
6
+ //#region src/r3f/CompositionCanvas.tsx
7
+ const CompositionTimeContext = createContext({
8
+ timeMs: 0,
9
+ durationMs: 0
10
+ });
11
+ /**
12
+ * Hook to read the current composition time inside an R3F scene.
13
+ * Must be used within a `<CompositionCanvas>`.
14
+ *
15
+ * @returns { timeMs, durationMs } — current time and total duration in ms
16
+ */
17
+ function useCompositionTime() {
18
+ return useContext(CompositionTimeContext);
19
+ }
20
+ function GLSync() {
21
+ const { gl } = useThree();
22
+ useFrame(() => {
23
+ gl.getContext().finish();
24
+ });
25
+ return null;
26
+ }
27
+ function InvalidateOnTimeChange({ timeMs }) {
28
+ const { invalidate } = useThree();
29
+ useLayoutEffect(() => {
30
+ invalidate();
31
+ }, [timeMs, invalidate]);
32
+ return null;
33
+ }
34
+ function CompositionCanvas({ children, containerStyle, containerClassName, gl: glProp,...canvasProps }) {
35
+ const [timeMs, setTimeMs] = useState(0);
36
+ const [durationMs, setDurationMs] = useState(0);
37
+ const containerRef = useRef(null);
38
+ useEffect(() => {
39
+ const el = containerRef.current;
40
+ if (!el) return;
41
+ const tg = el.closest("ef-timegroup");
42
+ if (!tg) {
43
+ console.warn("[CompositionCanvas] No ef-timegroup ancestor found. Wrap CompositionCanvas inside a <Timegroup>.");
44
+ return;
45
+ }
46
+ if (tg.durationMs) setDurationMs(tg.durationMs);
47
+ return tg.addFrameTask?.(({ ownCurrentTimeMs, durationMs: dur }) => {
48
+ flushSync(() => {
49
+ setTimeMs(ownCurrentTimeMs);
50
+ setDurationMs(dur);
51
+ });
52
+ });
53
+ }, []);
54
+ const mergedGl = typeof glProp === "object" ? {
55
+ preserveDrawingBuffer: true,
56
+ ...glProp
57
+ } : glProp ?? { preserveDrawingBuffer: true };
58
+ return /* @__PURE__ */ jsx("div", {
59
+ ref: containerRef,
60
+ className: containerClassName,
61
+ style: {
62
+ position: "absolute",
63
+ inset: 0,
64
+ width: "100%",
65
+ height: "100%",
66
+ ...containerStyle
67
+ },
68
+ children: /* @__PURE__ */ jsx(Canvas, {
69
+ frameloop: "demand",
70
+ gl: mergedGl,
71
+ ...canvasProps,
72
+ style: {
73
+ width: "100%",
74
+ height: "100%",
75
+ ...canvasProps.style
76
+ },
77
+ children: /* @__PURE__ */ jsxs(CompositionTimeContext.Provider, {
78
+ value: {
79
+ timeMs,
80
+ durationMs
81
+ },
82
+ children: [
83
+ /* @__PURE__ */ jsx(GLSync, {}),
84
+ /* @__PURE__ */ jsx(InvalidateOnTimeChange, { timeMs }),
85
+ children
86
+ ]
87
+ })
88
+ })
89
+ });
90
+ }
91
+
92
+ //#endregion
93
+ export { CompositionCanvas, useCompositionTime };
94
+ //# sourceMappingURL=CompositionCanvas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CompositionCanvas.js","names":[],"sources":["../../src/r3f/CompositionCanvas.tsx"],"sourcesContent":["/**\n * CompositionCanvas — R3F Canvas that automatically bridges\n * Editframe composition time into the 3D scene.\n *\n * Handles: addFrameTask → React state, preserveDrawingBuffer,\n * gl.finish(), frameloop=\"demand\", and invalidation.\n *\n * Usage:\n * ```tsx\n * <Timegroup mode=\"fixed\" duration=\"14s\">\n * <CompositionCanvas shadows>\n * <MyScene />\n * </CompositionCanvas>\n * </Timegroup>\n * ```\n *\n * Inside scene components, use `useCompositionTime()` to read the\n * current composition time in milliseconds.\n */\n\nimport * as React from \"react\";\nimport {\n createContext,\n useContext,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from \"react\";\nimport { Canvas, useThree, useFrame } from \"@react-three/fiber\";\nimport { flushSync } from \"react-dom\";\nimport type { CanvasProps } from \"@react-three/fiber\";\n\n/* ━━ Context for composition time ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */\n\nconst CompositionTimeContext = createContext<{\n timeMs: number;\n durationMs: number;\n}>({ timeMs: 0, durationMs: 0 });\n\n/**\n * Hook to read the current composition time inside an R3F scene.\n * Must be used within a `<CompositionCanvas>`.\n *\n * @returns { timeMs, durationMs } — current time and total duration in ms\n */\nexport function useCompositionTime() {\n return useContext(CompositionTimeContext);\n}\n\n/* ━━ Internal: GL sync for renderToVideo ━━━━━━━━━━━━━━━━━━━━━━━━━━ */\n\nfunction GLSync() {\n const { gl } = useThree();\n useFrame(() => {\n gl.getContext().finish();\n });\n return null;\n}\n\n/* ━━ Internal: invalidate on time change ━━━━━━━━━━━━━━━━━━━━━━━━━━ */\n\nfunction InvalidateOnTimeChange({ timeMs }: { timeMs: number }) {\n const { invalidate } = useThree();\n // useLayoutEffect fires synchronously during flushSync, ensuring\n // invalidate() runs before the addFrameTask callback returns.\n useLayoutEffect(() => {\n invalidate();\n }, [timeMs, invalidate]);\n return null;\n}\n\n/* ━━ CompositionCanvas ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */\n\nexport interface CompositionCanvasProps extends Omit<CanvasProps, \"frameloop\"> {\n /** Extra styles for the container div */\n containerStyle?: React.CSSProperties;\n /** Extra className for the container div */\n containerClassName?: string;\n}\n\nexport function CompositionCanvas({\n children,\n containerStyle,\n containerClassName,\n gl: glProp,\n ...canvasProps\n}: CompositionCanvasProps) {\n const [timeMs, setTimeMs] = useState(0);\n const [durationMs, setDurationMs] = useState(0);\n const containerRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n\n // Walk up to find the ef-timegroup ancestor\n const tg = el.closest(\"ef-timegroup\") as\n | (HTMLElement & {\n addFrameTask?: (\n cb: (info: {\n ownCurrentTimeMs: number;\n durationMs: number;\n }) => void,\n ) => () => void;\n durationMs?: number;\n })\n | null;\n\n if (!tg) {\n console.warn(\n \"[CompositionCanvas] No ef-timegroup ancestor found. \" +\n \"Wrap CompositionCanvas inside a <Timegroup>.\",\n );\n return;\n }\n\n if (tg.durationMs) setDurationMs(tg.durationMs);\n\n const cleanup = tg.addFrameTask?.(\n ({ ownCurrentTimeMs, durationMs: dur }) => {\n // flushSync commits the state update synchronously so the\n // useLayoutEffect → invalidate() fires before we return.\n // R3F's demand render then runs useFrame subscribers (which\n // update instancedMesh matrices, cameras, etc.) and gl.render\n // in a single pass — no duplicate GPU work.\n flushSync(() => {\n setTimeMs(ownCurrentTimeMs);\n setDurationMs(dur);\n });\n },\n );\n\n return cleanup;\n }, []);\n\n // Merge user gl options with required defaults\n const mergedGl =\n typeof glProp === \"object\"\n ? { preserveDrawingBuffer: true, ...glProp }\n : (glProp ?? { preserveDrawingBuffer: true });\n\n return (\n <div\n ref={containerRef}\n className={containerClassName}\n style={{\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n ...containerStyle,\n }}\n >\n <Canvas\n frameloop=\"demand\"\n gl={mergedGl}\n {...canvasProps}\n style={{ width: \"100%\", height: \"100%\", ...canvasProps.style }}\n >\n <CompositionTimeContext.Provider value={{ timeMs, durationMs }}>\n <GLSync />\n <InvalidateOnTimeChange timeMs={timeMs} />\n {children}\n </CompositionTimeContext.Provider>\n </Canvas>\n </div>\n );\n}\n"],"mappings":";;;;;;AAmCA,MAAM,yBAAyB,cAG5B;CAAE,QAAQ;CAAG,YAAY;CAAG,CAAC;;;;;;;AAQhC,SAAgB,qBAAqB;AACnC,QAAO,WAAW,uBAAuB;;AAK3C,SAAS,SAAS;CAChB,MAAM,EAAE,OAAO,UAAU;AACzB,gBAAe;AACb,KAAG,YAAY,CAAC,QAAQ;GACxB;AACF,QAAO;;AAKT,SAAS,uBAAuB,EAAE,UAA8B;CAC9D,MAAM,EAAE,eAAe,UAAU;AAGjC,uBAAsB;AACpB,cAAY;IACX,CAAC,QAAQ,WAAW,CAAC;AACxB,QAAO;;AAYT,SAAgB,kBAAkB,EAChC,UACA,gBACA,oBACA,IAAI,OACJ,GAAG,eACsB;CACzB,MAAM,CAAC,QAAQ,aAAa,SAAS,EAAE;CACvC,MAAM,CAAC,YAAY,iBAAiB,SAAS,EAAE;CAC/C,MAAM,eAAe,OAAuB,KAAK;AAEjD,iBAAgB;EACd,MAAM,KAAK,aAAa;AACxB,MAAI,CAAC,GAAI;EAGT,MAAM,KAAK,GAAG,QAAQ,eAAe;AAYrC,MAAI,CAAC,IAAI;AACP,WAAQ,KACN,mGAED;AACD;;AAGF,MAAI,GAAG,WAAY,eAAc,GAAG,WAAW;AAgB/C,SAdgB,GAAG,gBAChB,EAAE,kBAAkB,YAAY,UAAU;AAMzC,mBAAgB;AACd,cAAU,iBAAiB;AAC3B,kBAAc,IAAI;KAClB;IAEL;IAGA,EAAE,CAAC;CAGN,MAAM,WACJ,OAAO,WAAW,WACd;EAAE,uBAAuB;EAAM,GAAG;EAAQ,GACzC,UAAU,EAAE,uBAAuB,MAAM;AAEhD,QACE,oBAAC;EACC,KAAK;EACL,WAAW;EACX,OAAO;GACL,UAAU;GACV,OAAO;GACP,OAAO;GACP,QAAQ;GACR,GAAG;GACJ;YAED,oBAAC;GACC,WAAU;GACV,IAAI;GACJ,GAAI;GACJ,OAAO;IAAE,OAAO;IAAQ,QAAQ;IAAQ,GAAG,YAAY;IAAO;aAE9D,qBAAC,uBAAuB;IAAS,OAAO;KAAE;KAAQ;KAAY;;KAC5D,oBAAC,WAAS;KACV,oBAAC,0BAA+B,SAAU;KACzC;;KAC+B;IAC3B;GACL"}
@@ -0,0 +1,28 @@
1
+ import * as React$1 from "react";
2
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
3
+ import { CanvasProps } from "@react-three/fiber";
4
+
5
+ //#region src/r3f/OffscreenCompositionCanvas.d.ts
6
+
7
+ interface OffscreenCompositionCanvasProps {
8
+ /** Web worker that will handle R3F rendering */
9
+ worker: Worker;
10
+ /** Fallback content for browsers without OffscreenCanvas support (Safari) */
11
+ fallback?: React$1.ReactNode;
12
+ /** Extra styles for the container div */
13
+ containerStyle?: React$1.CSSProperties;
14
+ /** Extra className for the container div */
15
+ containerClassName?: string;
16
+ /** Canvas props to forward to @react-three/offscreen Canvas (shadows, dpr, gl, camera, scene, etc.) */
17
+ canvasProps?: Omit<CanvasProps, "frameloop">;
18
+ }
19
+ declare function OffscreenCompositionCanvas({
20
+ worker,
21
+ fallback,
22
+ containerStyle,
23
+ containerClassName,
24
+ canvasProps
25
+ }: OffscreenCompositionCanvasProps): react_jsx_runtime0.JSX.Element;
26
+ //#endregion
27
+ export { OffscreenCompositionCanvas, OffscreenCompositionCanvasProps };
28
+ //# sourceMappingURL=OffscreenCompositionCanvas.d.ts.map
@@ -0,0 +1,119 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { Canvas } from "@react-three/offscreen";
4
+
5
+ //#region src/r3f/OffscreenCompositionCanvas.tsx
6
+ function waitForBitmap(worker, requestId) {
7
+ return new Promise((resolve, reject) => {
8
+ const timeout = setTimeout(() => {
9
+ worker.removeEventListener("message", handler);
10
+ reject(/* @__PURE__ */ new Error(`[OffscreenCompositionCanvas] Timeout waiting for frame ${requestId}`));
11
+ }, 5e3);
12
+ const handler = (e) => {
13
+ if (e.data.type === "frameRendered" && e.data.requestId === requestId) {
14
+ clearTimeout(timeout);
15
+ worker.removeEventListener("message", handler);
16
+ resolve(e.data.bitmap);
17
+ } else if (e.data.type === "error") {
18
+ clearTimeout(timeout);
19
+ worker.removeEventListener("message", handler);
20
+ reject(/* @__PURE__ */ new Error(`[OffscreenCompositionCanvas] Worker error: ${e.data.message}`));
21
+ }
22
+ };
23
+ worker.addEventListener("message", handler);
24
+ });
25
+ }
26
+ function OffscreenCompositionCanvas({ worker, fallback, containerStyle, containerClassName, canvasProps }) {
27
+ const containerRef = useRef(null);
28
+ const captureCanvasRef = useRef(null);
29
+ const [dimensions, setDimensions] = useState({
30
+ width: 0,
31
+ height: 0
32
+ });
33
+ useEffect(() => {
34
+ const container = containerRef.current;
35
+ if (!container) return;
36
+ const observer = new ResizeObserver((entries) => {
37
+ for (const entry of entries) {
38
+ const { width, height } = entry.contentRect;
39
+ setDimensions({
40
+ width,
41
+ height
42
+ });
43
+ }
44
+ });
45
+ observer.observe(container);
46
+ return () => observer.disconnect();
47
+ }, []);
48
+ useEffect(() => {
49
+ const container = containerRef.current;
50
+ if (!container) return;
51
+ const tg = container.closest("ef-timegroup");
52
+ if (!tg) {
53
+ console.warn("[OffscreenCompositionCanvas] No ef-timegroup ancestor found. Wrap OffscreenCompositionCanvas inside a <Timegroup>.");
54
+ return;
55
+ }
56
+ if (!tg.addFrameTask) {
57
+ console.warn("[OffscreenCompositionCanvas] ef-timegroup does not have addFrameTask method");
58
+ return;
59
+ }
60
+ let nextRequestId = 0;
61
+ return tg.addFrameTask(async ({ ownCurrentTimeMs, durationMs }) => {
62
+ const requestId = nextRequestId++;
63
+ worker.postMessage({
64
+ type: "renderFrame",
65
+ timeMs: ownCurrentTimeMs,
66
+ durationMs,
67
+ requestId
68
+ });
69
+ try {
70
+ const bitmap = await waitForBitmap(worker, requestId);
71
+ const captureCanvas = captureCanvasRef.current;
72
+ if (captureCanvas) {
73
+ const ctx = captureCanvas.getContext("2d");
74
+ if (ctx) {
75
+ if (captureCanvas.width !== bitmap.width || captureCanvas.height !== bitmap.height) {
76
+ captureCanvas.width = bitmap.width;
77
+ captureCanvas.height = bitmap.height;
78
+ }
79
+ ctx.drawImage(bitmap, 0, 0);
80
+ }
81
+ bitmap.close();
82
+ }
83
+ } catch (error) {
84
+ console.error("[OffscreenCompositionCanvas] Frame render error:", error);
85
+ }
86
+ });
87
+ }, [worker]);
88
+ return /* @__PURE__ */ jsxs("div", {
89
+ ref: containerRef,
90
+ className: containerClassName,
91
+ style: {
92
+ position: "absolute",
93
+ inset: 0,
94
+ width: "100%",
95
+ height: "100%",
96
+ ...containerStyle
97
+ },
98
+ children: [/* @__PURE__ */ jsx(Canvas, {
99
+ worker,
100
+ fallback,
101
+ ...canvasProps,
102
+ style: {
103
+ width: "100%",
104
+ height: "100%",
105
+ ...canvasProps?.style
106
+ }
107
+ }), /* @__PURE__ */ jsx("canvas", {
108
+ ref: captureCanvasRef,
109
+ "data-offscreen-capture": "true",
110
+ style: { display: "none" },
111
+ width: dimensions.width,
112
+ height: dimensions.height
113
+ })]
114
+ });
115
+ }
116
+
117
+ //#endregion
118
+ export { OffscreenCompositionCanvas };
119
+ //# sourceMappingURL=OffscreenCompositionCanvas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OffscreenCompositionCanvas.js","names":["OffscreenCanvas"],"sources":["../../src/r3f/OffscreenCompositionCanvas.tsx"],"sourcesContent":["/**\n * OffscreenCompositionCanvas — R3F Canvas that renders in a web worker via OffscreenCanvas.\n *\n * This component integrates with Editframe's timeline system by:\n * - Registering an addFrameTask that sends time updates to the worker\n * - Receiving rendered frames (ImageBitmap) from the worker\n * - Drawing frames onto a hidden capture canvas for video export\n *\n * The worker handles all R3F rendering, keeping the main thread free and enabling\n * rendering to continue even when the browser tab is hidden.\n *\n * Usage:\n * ```tsx\n * const worker = new Worker(new URL('./scene-worker.ts', import.meta.url), { type: 'module' });\n *\n * <Timegroup mode=\"fixed\" duration=\"14s\">\n * <OffscreenCompositionCanvas\n * worker={worker}\n * fallback={<MainThreadFallback />}\n * canvasProps={{ shadows: true, dpr: [1, 2] }}\n * />\n * </Timegroup>\n * ```\n */\n\nimport * as React from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { Canvas as OffscreenCanvas } from \"@react-three/offscreen\";\nimport type { CanvasProps } from \"@react-three/fiber\";\nimport type { EFTimegroup } from \"@editframe/elements\";\n\n/* ━━ Types ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */\n\nexport interface OffscreenCompositionCanvasProps {\n /** Web worker that will handle R3F rendering */\n worker: Worker;\n /** Fallback content for browsers without OffscreenCanvas support (Safari) */\n fallback?: React.ReactNode;\n /** Extra styles for the container div */\n containerStyle?: React.CSSProperties;\n /** Extra className for the container div */\n containerClassName?: string;\n /** Canvas props to forward to @react-three/offscreen Canvas (shadows, dpr, gl, camera, scene, etc.) */\n canvasProps?: Omit<CanvasProps, \"frameloop\">;\n}\n\n/* ━━ Helper: Wait for bitmap from worker ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */\n\nfunction waitForBitmap(\n worker: Worker,\n requestId: number,\n): Promise<ImageBitmap> {\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n worker.removeEventListener(\"message\", handler);\n reject(\n new Error(\n `[OffscreenCompositionCanvas] Timeout waiting for frame ${requestId}`,\n ),\n );\n }, 5000); // 5 second timeout\n\n const handler = (e: MessageEvent) => {\n if (e.data.type === \"frameRendered\" && e.data.requestId === requestId) {\n clearTimeout(timeout);\n worker.removeEventListener(\"message\", handler);\n resolve(e.data.bitmap);\n } else if (e.data.type === \"error\") {\n clearTimeout(timeout);\n worker.removeEventListener(\"message\", handler);\n reject(\n new Error(\n `[OffscreenCompositionCanvas] Worker error: ${e.data.message}`,\n ),\n );\n }\n };\n\n worker.addEventListener(\"message\", handler);\n });\n}\n\n/* ━━ Component ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */\n\nexport function OffscreenCompositionCanvas({\n worker,\n fallback,\n containerStyle,\n containerClassName,\n canvasProps,\n}: OffscreenCompositionCanvasProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const captureCanvasRef = useRef<HTMLCanvasElement>(null);\n const [dimensions, setDimensions] = useState({ width: 0, height: 0 });\n\n /* ━━ Resize observer to keep capture canvas in sync ━━━━━━━━━━━━━━━━━ */\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const { width, height } = entry.contentRect;\n setDimensions({ width, height });\n }\n });\n\n observer.observe(container);\n return () => observer.disconnect();\n }, []);\n\n /* ━━ addFrameTask integration ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n // Walk up to find the parent ef-timegroup\n const tg = container.closest(\"ef-timegroup\") as\n | (HTMLElement & EFTimegroup)\n | null;\n\n if (!tg) {\n console.warn(\n \"[OffscreenCompositionCanvas] No ef-timegroup ancestor found. \" +\n \"Wrap OffscreenCompositionCanvas inside a <Timegroup>.\",\n );\n return;\n }\n\n if (!tg.addFrameTask) {\n console.warn(\n \"[OffscreenCompositionCanvas] ef-timegroup does not have addFrameTask method\",\n );\n return;\n }\n\n let nextRequestId = 0;\n\n const cleanup = tg.addFrameTask(\n async ({ ownCurrentTimeMs, durationMs }) => {\n const requestId = nextRequestId++;\n\n // Send render request to worker\n worker.postMessage({\n type: \"renderFrame\",\n timeMs: ownCurrentTimeMs,\n durationMs,\n requestId,\n });\n\n try {\n // Wait for worker to finish rendering and return pixels\n const bitmap = await waitForBitmap(worker, requestId);\n\n // Draw onto capture canvas so serialization pipeline can read pixels\n const captureCanvas = captureCanvasRef.current;\n if (captureCanvas) {\n const ctx = captureCanvas.getContext(\"2d\");\n if (ctx) {\n // Resize capture canvas to match bitmap\n if (\n captureCanvas.width !== bitmap.width ||\n captureCanvas.height !== bitmap.height\n ) {\n captureCanvas.width = bitmap.width;\n captureCanvas.height = bitmap.height;\n }\n\n // Draw the bitmap\n ctx.drawImage(bitmap, 0, 0);\n }\n\n // Close bitmap to free memory\n bitmap.close();\n }\n } catch (error) {\n console.error(\n \"[OffscreenCompositionCanvas] Frame render error:\",\n error,\n );\n }\n },\n );\n\n return cleanup;\n }, [worker]);\n\n /* ━━ Render ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */\n\n return (\n <div\n ref={containerRef}\n className={containerClassName}\n style={{\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n ...containerStyle,\n }}\n >\n {/* Display canvas - handled by @react-three/offscreen */}\n <OffscreenCanvas\n worker={worker}\n fallback={fallback}\n {...canvasProps}\n style={{ width: \"100%\", height: \"100%\", ...canvasProps?.style }}\n />\n\n {/* Hidden capture canvas for video export */}\n <canvas\n ref={captureCanvasRef}\n data-offscreen-capture=\"true\"\n style={{ display: \"none\" }}\n width={dimensions.width}\n height={dimensions.height}\n />\n </div>\n );\n}\n"],"mappings":";;;;;AAgDA,SAAS,cACP,QACA,WACsB;AACtB,QAAO,IAAI,SAAS,SAAS,WAAW;EACtC,MAAM,UAAU,iBAAiB;AAC/B,UAAO,oBAAoB,WAAW,QAAQ;AAC9C,0BACE,IAAI,MACF,0DAA0D,YAC3D,CACF;KACA,IAAK;EAER,MAAM,WAAW,MAAoB;AACnC,OAAI,EAAE,KAAK,SAAS,mBAAmB,EAAE,KAAK,cAAc,WAAW;AACrE,iBAAa,QAAQ;AACrB,WAAO,oBAAoB,WAAW,QAAQ;AAC9C,YAAQ,EAAE,KAAK,OAAO;cACb,EAAE,KAAK,SAAS,SAAS;AAClC,iBAAa,QAAQ;AACrB,WAAO,oBAAoB,WAAW,QAAQ;AAC9C,2BACE,IAAI,MACF,8CAA8C,EAAE,KAAK,UACtD,CACF;;;AAIL,SAAO,iBAAiB,WAAW,QAAQ;GAC3C;;AAKJ,SAAgB,2BAA2B,EACzC,QACA,UACA,gBACA,oBACA,eACkC;CAClC,MAAM,eAAe,OAAuB,KAAK;CACjD,MAAM,mBAAmB,OAA0B,KAAK;CACxD,MAAM,CAAC,YAAY,iBAAiB,SAAS;EAAE,OAAO;EAAG,QAAQ;EAAG,CAAC;AAIrE,iBAAgB;EACd,MAAM,YAAY,aAAa;AAC/B,MAAI,CAAC,UAAW;EAEhB,MAAM,WAAW,IAAI,gBAAgB,YAAY;AAC/C,QAAK,MAAM,SAAS,SAAS;IAC3B,MAAM,EAAE,OAAO,WAAW,MAAM;AAChC,kBAAc;KAAE;KAAO;KAAQ,CAAC;;IAElC;AAEF,WAAS,QAAQ,UAAU;AAC3B,eAAa,SAAS,YAAY;IACjC,EAAE,CAAC;AAIN,iBAAgB;EACd,MAAM,YAAY,aAAa;AAC/B,MAAI,CAAC,UAAW;EAGhB,MAAM,KAAK,UAAU,QAAQ,eAAe;AAI5C,MAAI,CAAC,IAAI;AACP,WAAQ,KACN,qHAED;AACD;;AAGF,MAAI,CAAC,GAAG,cAAc;AACpB,WAAQ,KACN,8EACD;AACD;;EAGF,IAAI,gBAAgB;AAgDpB,SA9CgB,GAAG,aACjB,OAAO,EAAE,kBAAkB,iBAAiB;GAC1C,MAAM,YAAY;AAGlB,UAAO,YAAY;IACjB,MAAM;IACN,QAAQ;IACR;IACA;IACD,CAAC;AAEF,OAAI;IAEF,MAAM,SAAS,MAAM,cAAc,QAAQ,UAAU;IAGrD,MAAM,gBAAgB,iBAAiB;AACvC,QAAI,eAAe;KACjB,MAAM,MAAM,cAAc,WAAW,KAAK;AAC1C,SAAI,KAAK;AAEP,UACE,cAAc,UAAU,OAAO,SAC/B,cAAc,WAAW,OAAO,QAChC;AACA,qBAAc,QAAQ,OAAO;AAC7B,qBAAc,SAAS,OAAO;;AAIhC,UAAI,UAAU,QAAQ,GAAG,EAAE;;AAI7B,YAAO,OAAO;;YAET,OAAO;AACd,YAAQ,MACN,oDACA,MACD;;IAGN;IAGA,CAAC,OAAO,CAAC;AAIZ,QACE,qBAAC;EACC,KAAK;EACL,WAAW;EACX,OAAO;GACL,UAAU;GACV,OAAO;GACP,OAAO;GACP,QAAQ;GACR,GAAG;GACJ;aAGD,oBAACA;GACS;GACE;GACV,GAAI;GACJ,OAAO;IAAE,OAAO;IAAQ,QAAQ;IAAQ,GAAG,aAAa;IAAO;IAC/D,EAGF,oBAAC;GACC,KAAK;GACL,0BAAuB;GACvB,OAAO,EAAE,SAAS,QAAQ;GAC1B,OAAO,WAAW;GAClB,QAAQ,WAAW;IACnB;GACE"}
@@ -0,0 +1,5 @@
1
+ import { OffscreenCompositionCanvas, OffscreenCompositionCanvasProps } from "./OffscreenCompositionCanvas.js";
2
+ import { CompositionCanvas, CompositionCanvasProps, useCompositionTime } from "./CompositionCanvas.js";
3
+ import { renderOffscreen } from "./renderOffscreen.js";
4
+ import { MainToWorkerMessage, RenderFramePayload, WorkerToMainMessage } from "./worker-protocol.js";
5
+ export { CompositionCanvas, type CompositionCanvasProps, type MainToWorkerMessage, OffscreenCompositionCanvas, type OffscreenCompositionCanvasProps, type RenderFramePayload, type WorkerToMainMessage, renderOffscreen, useCompositionTime };
@@ -0,0 +1,5 @@
1
+ import { OffscreenCompositionCanvas } from "./OffscreenCompositionCanvas.js";
2
+ import { CompositionCanvas, useCompositionTime } from "./CompositionCanvas.js";
3
+ import { renderOffscreen } from "./renderOffscreen.js";
4
+
5
+ export { CompositionCanvas, OffscreenCompositionCanvas, renderOffscreen, useCompositionTime };
@@ -0,0 +1,27 @@
1
+ import * as React$1 from "react";
2
+
3
+ //#region src/r3f/renderOffscreen.d.ts
4
+
5
+ /**
6
+ * Render a React Three Fiber scene in a web worker with offscreen canvas.
7
+ *
8
+ * This extends @react-three/offscreen's render() with Editframe-specific features:
9
+ * - Time synchronization via timeStore and useCompositionTime hook
10
+ * - Frame-by-frame rendering on demand (renderFrame message)
11
+ * - Pixel capture and transfer back to main thread via ImageBitmap
12
+ *
13
+ * @param children - React node containing the R3F scene
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * // worker.ts
18
+ * import { renderOffscreen } from '@editframe/react/r3f';
19
+ * import { MyScene } from './MyScene';
20
+ *
21
+ * renderOffscreen(<MyScene />);
22
+ * ```
23
+ */
24
+ declare function renderOffscreen(children: React$1.ReactNode): void;
25
+ //#endregion
26
+ export { renderOffscreen };
27
+ //# sourceMappingURL=renderOffscreen.d.ts.map
@@ -0,0 +1,291 @@
1
+ import "react";
2
+ import { createEvents, createRoot } from "@react-three/fiber";
3
+ import * as THREE from "three";
4
+
5
+ //#region src/r3f/renderOffscreen.ts
6
+ const EVENTS = {
7
+ onClick: ["click", false],
8
+ onContextMenu: ["contextmenu", false],
9
+ onDoubleClick: ["dblclick", false],
10
+ onWheel: ["wheel", true],
11
+ onPointerDown: ["pointerdown", true],
12
+ onPointerUp: ["pointerup", true],
13
+ onPointerLeave: ["pointerleave", true],
14
+ onPointerMove: ["pointermove", true],
15
+ onPointerCancel: ["pointercancel", true],
16
+ onLostPointerCapture: ["lostpointercapture", true]
17
+ };
18
+ function createPointerEvents(emitter) {
19
+ return (store) => {
20
+ const { handlePointer } = createEvents(store);
21
+ return {
22
+ priority: 1,
23
+ enabled: true,
24
+ compute(event, state) {
25
+ state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
26
+ state.raycaster.setFromCamera(state.pointer, state.camera);
27
+ },
28
+ connected: void 0,
29
+ handlers: Object.keys(EVENTS).reduce((acc, key) => ({
30
+ ...acc,
31
+ [key]: handlePointer(key)
32
+ }), {}),
33
+ connect: (target) => {
34
+ const { set, events } = store.getState();
35
+ events.disconnect?.();
36
+ set((state) => ({ events: {
37
+ ...state.events,
38
+ connected: target
39
+ } }));
40
+ Object.entries(events?.handlers ?? []).forEach(([name, event]) => {
41
+ const [eventName] = EVENTS[name];
42
+ emitter.on(eventName, event);
43
+ });
44
+ },
45
+ disconnect: () => {
46
+ const { set, events } = store.getState();
47
+ if (events.connected) {
48
+ Object.entries(events.handlers ?? []).forEach(([name, event]) => {
49
+ const [eventName] = EVENTS[name];
50
+ emitter.off(eventName, event);
51
+ });
52
+ set((state) => ({ events: {
53
+ ...state.events,
54
+ connected: void 0
55
+ } }));
56
+ }
57
+ }
58
+ };
59
+ };
60
+ }
61
+ const timeStore = {
62
+ timeMs: 0,
63
+ durationMs: 0,
64
+ listeners: /* @__PURE__ */ new Set(),
65
+ update(timeMs, durationMs) {
66
+ this.timeMs = timeMs;
67
+ this.durationMs = durationMs;
68
+ this.listeners.forEach((l) => l());
69
+ }
70
+ };
71
+ /**
72
+ * Render a React Three Fiber scene in a web worker with offscreen canvas.
73
+ *
74
+ * This extends @react-three/offscreen's render() with Editframe-specific features:
75
+ * - Time synchronization via timeStore and useCompositionTime hook
76
+ * - Frame-by-frame rendering on demand (renderFrame message)
77
+ * - Pixel capture and transfer back to main thread via ImageBitmap
78
+ *
79
+ * @param children - React node containing the R3F scene
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * // worker.ts
84
+ * import { renderOffscreen } from '@editframe/react/r3f';
85
+ * import { MyScene } from './MyScene';
86
+ *
87
+ * renderOffscreen(<MyScene />);
88
+ * ```
89
+ */
90
+ function renderOffscreen(children) {
91
+ let root = null;
92
+ let offscreenCanvas = null;
93
+ let size = {
94
+ width: 0,
95
+ height: 0,
96
+ top: 0,
97
+ left: 0
98
+ };
99
+ let dpr = 1;
100
+ const handlers = /* @__PURE__ */ new Map();
101
+ const emitter = {
102
+ all: /* @__PURE__ */ new Map(),
103
+ on(type, handler) {
104
+ const s = handlers.get(type) ?? /* @__PURE__ */ new Set();
105
+ s.add(handler);
106
+ handlers.set(type, s);
107
+ },
108
+ off(type, handler) {
109
+ handlers.get(type)?.delete(handler);
110
+ },
111
+ emit(type, event) {
112
+ handlers.get(type)?.forEach((h) => h(event));
113
+ handlers.get("*")?.forEach((h) => h(type, event));
114
+ }
115
+ };
116
+ const handleInit = (payload) => {
117
+ const { props, drawingSurface: canvas, width, top, left, height, pixelRatio } = payload;
118
+ console.log("[renderOffscreen] Init received", {
119
+ width,
120
+ height,
121
+ pixelRatio
122
+ });
123
+ try {
124
+ if (root) root.unmount();
125
+ offscreenCanvas = canvas;
126
+ Object.assign(canvas, {
127
+ pageXOffset: left,
128
+ pageYOffset: top,
129
+ clientLeft: left,
130
+ clientTop: top,
131
+ clientWidth: width,
132
+ clientHeight: height,
133
+ style: { touchAction: "none" },
134
+ ownerDocument: canvas,
135
+ documentElement: canvas,
136
+ getBoundingClientRect() {
137
+ return size;
138
+ },
139
+ setAttribute() {},
140
+ setPointerCapture() {},
141
+ releasePointerCapture() {},
142
+ addEventListener(event, callback) {
143
+ emitter.on(event, callback);
144
+ },
145
+ removeEventListener(event, callback) {
146
+ emitter.off(event, callback);
147
+ }
148
+ });
149
+ root = createRoot(canvas);
150
+ root.configure({
151
+ events: createPointerEvents(emitter),
152
+ size: size = {
153
+ width,
154
+ height,
155
+ top,
156
+ left
157
+ },
158
+ dpr: dpr = Math.min(Math.max(1, pixelRatio), 2),
159
+ frameloop: "demand",
160
+ ...props,
161
+ onCreated: (state) => {
162
+ if (props.eventPrefix) state.setEvents({ compute: (event, state$1) => {
163
+ const x = event[props.eventPrefix + "X"];
164
+ const y = event[props.eventPrefix + "Y"];
165
+ state$1.pointer.set(x / state$1.size.width * 2 - 1, -(y / state$1.size.height) * 2 + 1);
166
+ state$1.raycaster.setFromCamera(state$1.pointer, state$1.camera);
167
+ } });
168
+ }
169
+ });
170
+ console.log("[renderOffscreen] Rendering children");
171
+ root.render(children);
172
+ console.log("[renderOffscreen] Init complete");
173
+ } catch (e) {
174
+ console.error("[renderOffscreen] Init error:", e);
175
+ postMessage({
176
+ type: "error",
177
+ payload: e?.message
178
+ });
179
+ }
180
+ self.window = canvas;
181
+ };
182
+ const handleResize = ({ width, height, top, left }) => {
183
+ if (!root) return;
184
+ root.configure({
185
+ size: size = {
186
+ width,
187
+ height,
188
+ top,
189
+ left
190
+ },
191
+ dpr
192
+ });
193
+ };
194
+ const handleEvents = (payload) => {
195
+ emitter.emit(payload.eventName, {
196
+ ...payload,
197
+ preventDefault() {},
198
+ stopPropagation() {}
199
+ });
200
+ };
201
+ const handleProps = (payload) => {
202
+ if (!root) return;
203
+ if (payload.dpr) dpr = payload.dpr;
204
+ root.configure({
205
+ size,
206
+ dpr,
207
+ ...payload
208
+ });
209
+ };
210
+ const handleRenderFrame = async ({ timeMs, durationMs, requestId }) => {
211
+ console.log("[renderOffscreen] Render frame", {
212
+ timeMs,
213
+ requestId
214
+ });
215
+ try {
216
+ timeStore.update(timeMs, durationMs);
217
+ const state = root?.store?.getState?.();
218
+ if (!state) throw new Error("[renderOffscreen] No R3F root state available");
219
+ if (state?.gl && state?.scene && state?.camera) {
220
+ state.invalidate();
221
+ state.gl.render(state.scene, state.camera);
222
+ state.gl.getContext().finish();
223
+ } else throw new Error("[renderOffscreen] Missing gl/scene/camera in state");
224
+ if (!offscreenCanvas) throw new Error("[renderOffscreen] No offscreen canvas available");
225
+ const bitmap = await createImageBitmap(offscreenCanvas);
226
+ console.log("[renderOffscreen] Bitmap created", {
227
+ width: bitmap.width,
228
+ height: bitmap.height
229
+ });
230
+ self.postMessage({
231
+ type: "frameRendered",
232
+ requestId,
233
+ bitmap
234
+ }, [bitmap]);
235
+ } catch (e) {
236
+ console.error("[renderOffscreen] Frame render error:", e);
237
+ postMessage({
238
+ type: "error",
239
+ message: e?.message || "Unknown error in handleRenderFrame"
240
+ });
241
+ }
242
+ };
243
+ const handlerMap = {
244
+ resize: handleResize,
245
+ init: handleInit,
246
+ dom_events: handleEvents,
247
+ props: handleProps,
248
+ renderFrame: handleRenderFrame
249
+ };
250
+ self.onmessage = (event) => {
251
+ const { type, payload } = event.data;
252
+ const handler = handlerMap[type];
253
+ if (handler) handler(payload);
254
+ };
255
+ THREE.ImageLoader.prototype.load = function(url, onLoad, _onProgress, onError) {
256
+ if (this.path !== void 0) url = this.path + url;
257
+ url = this.manager.resolveURL(url);
258
+ const scope = this;
259
+ const cached = THREE.Cache.get(url);
260
+ if (cached !== void 0) {
261
+ scope.manager.itemStart(url);
262
+ if (onLoad) onLoad(cached);
263
+ scope.manager.itemEnd(url);
264
+ return cached;
265
+ }
266
+ fetch(url).then((res) => res.blob()).then((res) => createImageBitmap(res, {
267
+ premultiplyAlpha: "none",
268
+ colorSpaceConversion: "none"
269
+ })).then((bitmap) => {
270
+ THREE.Cache.add(url, bitmap);
271
+ if (onLoad) onLoad(bitmap);
272
+ scope.manager.itemEnd(url);
273
+ }).catch(onError);
274
+ return {};
275
+ };
276
+ self.window = {};
277
+ self.document = {};
278
+ self.Image = class {
279
+ constructor() {
280
+ this.height = 1;
281
+ this.width = 1;
282
+ }
283
+ set onload(callback) {
284
+ callback(true);
285
+ }
286
+ };
287
+ }
288
+
289
+ //#endregion
290
+ export { renderOffscreen };
291
+ //# sourceMappingURL=renderOffscreen.js.map