@motion-core/motion-gpu 0.3.0 → 0.4.1

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 +72 -1
  2. package/dist/core/material-preprocess.d.ts +5 -5
  3. package/dist/core/material-preprocess.js +1 -4
  4. package/dist/core/material.d.ts +32 -23
  5. package/dist/core/material.js +14 -7
  6. package/dist/core/render-targets.d.ts +1 -0
  7. package/dist/core/runtime-loop.d.ts +1 -0
  8. package/dist/core/textures.d.ts +1 -0
  9. package/dist/core/types.d.ts +21 -10
  10. package/dist/passes/CopyPass.d.ts +1 -0
  11. package/dist/passes/FullscreenPass.d.ts +1 -0
  12. package/dist/react/FragCanvas.d.ts +27 -0
  13. package/dist/react/FragCanvas.js +218 -0
  14. package/dist/react/MotionGPUErrorOverlay.d.ts +6 -0
  15. package/dist/react/MotionGPUErrorOverlay.js +305 -0
  16. package/dist/react/Portal.d.ts +6 -0
  17. package/dist/react/Portal.js +24 -0
  18. package/dist/react/advanced.d.ts +11 -0
  19. package/dist/react/advanced.js +6 -0
  20. package/dist/react/frame-context.d.ts +14 -0
  21. package/dist/react/frame-context.js +98 -0
  22. package/dist/react/index.d.ts +15 -0
  23. package/dist/react/index.js +9 -0
  24. package/dist/react/motiongpu-context.d.ts +73 -0
  25. package/dist/react/motiongpu-context.js +18 -0
  26. package/dist/react/use-motiongpu-user-context.d.ts +49 -0
  27. package/dist/react/use-motiongpu-user-context.js +94 -0
  28. package/dist/react/use-texture.d.ts +40 -0
  29. package/dist/react/use-texture.js +162 -0
  30. package/dist/svelte/FragCanvas.svelte +8 -22
  31. package/dist/svelte/FragCanvas.svelte.d.ts +1 -0
  32. package/dist/svelte/MotionGPUErrorOverlay.svelte +17 -20
  33. package/dist/svelte/Portal.svelte +6 -21
  34. package/dist/svelte/use-motiongpu-user-context.d.ts +9 -1
  35. package/dist/svelte/use-motiongpu-user-context.js +4 -1
  36. package/dist/svelte/use-texture.d.ts +6 -2
  37. package/dist/svelte/use-texture.js +6 -2
  38. package/package.json +32 -4
@@ -0,0 +1,218 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { createCurrentWritable as currentWritable } from '../core/current-value.js';
3
+ import { toMotionGPUErrorReport } from '../core/error-report.js';
4
+ import { createFrameRegistry } from '../core/frame-registry.js';
5
+ import { createMotionGPURuntimeLoop } from '../core/runtime-loop.js';
6
+ import { useEffect, useRef, useState } from 'react';
7
+ import { FrameRegistryReactContext } from './frame-context.js';
8
+ import { MotionGPUErrorOverlay } from './MotionGPUErrorOverlay.js';
9
+ import { MotionGPUReactContext } from './motiongpu-context.js';
10
+ function getInitialDpr() {
11
+ if (typeof window === 'undefined') {
12
+ return 1;
13
+ }
14
+ return window.devicePixelRatio ?? 1;
15
+ }
16
+ function createRuntimeState(initialDpr) {
17
+ const registry = createFrameRegistry({ maxDelta: 0.1 });
18
+ const canvasRef = { current: undefined };
19
+ const requestFrameSignalRef = { current: null };
20
+ const requestFrame = () => {
21
+ requestFrameSignalRef.current?.();
22
+ };
23
+ const invalidateFrame = () => {
24
+ registry.invalidate();
25
+ requestFrame();
26
+ };
27
+ const advanceFrame = () => {
28
+ registry.advance();
29
+ requestFrame();
30
+ };
31
+ const size = currentWritable({ width: 0, height: 0 });
32
+ const dprState = currentWritable(initialDpr, requestFrame);
33
+ const maxDeltaState = currentWritable(0.1, (value) => {
34
+ registry.setMaxDelta(value);
35
+ requestFrame();
36
+ });
37
+ const renderModeState = currentWritable('always', (value) => {
38
+ registry.setRenderMode(value);
39
+ requestFrame();
40
+ });
41
+ const autoRenderState = currentWritable(true, (value) => {
42
+ registry.setAutoRender(value);
43
+ requestFrame();
44
+ });
45
+ const userState = currentWritable({});
46
+ const context = {
47
+ get canvas() {
48
+ return canvasRef.current;
49
+ },
50
+ size,
51
+ dpr: dprState,
52
+ maxDelta: maxDeltaState,
53
+ renderMode: renderModeState,
54
+ autoRender: autoRenderState,
55
+ user: userState,
56
+ invalidate: invalidateFrame,
57
+ advance: advanceFrame,
58
+ scheduler: {
59
+ createStage: registry.createStage,
60
+ getStage: registry.getStage,
61
+ setDiagnosticsEnabled: registry.setDiagnosticsEnabled,
62
+ getDiagnosticsEnabled: registry.getDiagnosticsEnabled,
63
+ getLastRunTimings: registry.getLastRunTimings,
64
+ getSchedule: registry.getSchedule,
65
+ setProfilingEnabled: registry.setProfilingEnabled,
66
+ setProfilingWindow: registry.setProfilingWindow,
67
+ resetProfiling: registry.resetProfiling,
68
+ getProfilingEnabled: registry.getProfilingEnabled,
69
+ getProfilingWindow: registry.getProfilingWindow,
70
+ getProfilingSnapshot: registry.getProfilingSnapshot
71
+ }
72
+ };
73
+ return {
74
+ registry,
75
+ context,
76
+ canvasRef,
77
+ size,
78
+ dprState,
79
+ maxDeltaState,
80
+ renderModeState,
81
+ autoRenderState,
82
+ requestFrameSignalRef,
83
+ requestFrame,
84
+ invalidateFrame,
85
+ advanceFrame
86
+ };
87
+ }
88
+ function getNormalizedErrorHistoryLimit(value) {
89
+ if (!Number.isFinite(value) || value <= 0) {
90
+ return 0;
91
+ }
92
+ return Math.floor(value);
93
+ }
94
+ export function FragCanvas({ material, renderTargets = {}, passes = [], clearColor = [0, 0, 0, 1], outputColorSpace = 'srgb', renderMode = 'always', autoRender = true, maxDelta = 0.1, adapterOptions = undefined, deviceDescriptor = undefined, dpr = getInitialDpr(), showErrorOverlay = true, errorRenderer, onError = undefined, errorHistoryLimit = 0, onErrorHistory = undefined, className = '', style, children }) {
95
+ const runtimeRef = useRef(null);
96
+ if (!runtimeRef.current) {
97
+ runtimeRef.current = createRuntimeState(getInitialDpr());
98
+ }
99
+ const runtime = runtimeRef.current;
100
+ const runtimePropsRef = useRef({
101
+ material,
102
+ renderTargets,
103
+ passes,
104
+ clearColor,
105
+ outputColorSpace,
106
+ adapterOptions,
107
+ deviceDescriptor,
108
+ onError,
109
+ errorHistoryLimit,
110
+ onErrorHistory
111
+ });
112
+ runtimePropsRef.current = {
113
+ material,
114
+ renderTargets,
115
+ passes,
116
+ clearColor,
117
+ outputColorSpace,
118
+ adapterOptions,
119
+ deviceDescriptor,
120
+ onError,
121
+ errorHistoryLimit,
122
+ onErrorHistory
123
+ };
124
+ const [errorReport, setErrorReport] = useState(null);
125
+ const [errorHistory, setErrorHistory] = useState([]);
126
+ useEffect(() => {
127
+ runtime.renderModeState.set(renderMode);
128
+ }, [renderMode, runtime]);
129
+ useEffect(() => {
130
+ runtime.autoRenderState.set(autoRender);
131
+ }, [autoRender, runtime]);
132
+ useEffect(() => {
133
+ runtime.maxDeltaState.set(maxDelta);
134
+ }, [maxDelta, runtime]);
135
+ useEffect(() => {
136
+ runtime.dprState.set(dpr);
137
+ }, [dpr, runtime]);
138
+ useEffect(() => {
139
+ const limit = getNormalizedErrorHistoryLimit(errorHistoryLimit);
140
+ if (limit <= 0) {
141
+ if (errorHistory.length === 0) {
142
+ return;
143
+ }
144
+ setErrorHistory([]);
145
+ onErrorHistory?.([]);
146
+ return;
147
+ }
148
+ if (errorHistory.length <= limit) {
149
+ return;
150
+ }
151
+ const trimmed = errorHistory.slice(errorHistory.length - limit);
152
+ setErrorHistory(trimmed);
153
+ onErrorHistory?.(trimmed);
154
+ }, [errorHistory, errorHistoryLimit, onErrorHistory]);
155
+ useEffect(() => {
156
+ const canvas = runtime.canvasRef.current;
157
+ if (!canvas) {
158
+ const report = toMotionGPUErrorReport(new Error('Canvas element is not available'), 'initialization');
159
+ setErrorReport(report);
160
+ const historyLimit = getNormalizedErrorHistoryLimit(runtimePropsRef.current.errorHistoryLimit);
161
+ if (historyLimit > 0) {
162
+ const nextHistory = [report].slice(-historyLimit);
163
+ setErrorHistory(nextHistory);
164
+ runtimePropsRef.current.onErrorHistory?.(nextHistory);
165
+ }
166
+ runtimePropsRef.current.onError?.(report);
167
+ return () => {
168
+ runtime.registry.clear();
169
+ };
170
+ }
171
+ const runtimeLoop = createMotionGPURuntimeLoop({
172
+ canvas,
173
+ registry: runtime.registry,
174
+ size: runtime.size,
175
+ dpr: runtime.dprState,
176
+ maxDelta: runtime.maxDeltaState,
177
+ getMaterial: () => runtimePropsRef.current.material,
178
+ getRenderTargets: () => runtimePropsRef.current.renderTargets,
179
+ getPasses: () => runtimePropsRef.current.passes,
180
+ getClearColor: () => runtimePropsRef.current.clearColor,
181
+ getOutputColorSpace: () => runtimePropsRef.current.outputColorSpace,
182
+ getAdapterOptions: () => runtimePropsRef.current.adapterOptions,
183
+ getDeviceDescriptor: () => runtimePropsRef.current.deviceDescriptor,
184
+ getOnError: () => runtimePropsRef.current.onError,
185
+ getErrorHistoryLimit: () => runtimePropsRef.current.errorHistoryLimit,
186
+ getOnErrorHistory: () => runtimePropsRef.current.onErrorHistory,
187
+ reportErrorHistory: (history) => {
188
+ setErrorHistory(history);
189
+ },
190
+ reportError: (report) => {
191
+ setErrorReport(report);
192
+ }
193
+ });
194
+ runtime.requestFrameSignalRef.current = runtimeLoop.requestFrame;
195
+ return () => {
196
+ runtime.requestFrameSignalRef.current = null;
197
+ runtimeLoop.destroy();
198
+ };
199
+ }, [runtime]);
200
+ const canvasStyle = {
201
+ position: 'absolute',
202
+ inset: 0,
203
+ display: 'block',
204
+ width: '100%',
205
+ height: '100%',
206
+ ...style
207
+ };
208
+ return (_jsx(FrameRegistryReactContext.Provider, { value: runtime.registry, children: _jsx(MotionGPUReactContext.Provider, { value: runtime.context, children: _jsxs("div", { className: "motiongpu-canvas-wrap", style: {
209
+ position: 'relative',
210
+ width: '100%',
211
+ height: '100%',
212
+ minWidth: 0,
213
+ minHeight: 0,
214
+ overflow: 'hidden'
215
+ }, children: [_jsx("canvas", { className: className, style: canvasStyle, ref: (node) => {
216
+ runtime.canvasRef.current = node ?? undefined;
217
+ } }), showErrorOverlay && errorReport ? (errorRenderer ? (errorRenderer(errorReport)) : (_jsx(MotionGPUErrorOverlay, { report: errorReport }))) : null, children] }) }) }));
218
+ }
@@ -0,0 +1,6 @@
1
+ import type { MotionGPUErrorReport } from '../core/error-report.js';
2
+ interface MotionGPUErrorOverlayProps {
3
+ report: MotionGPUErrorReport;
4
+ }
5
+ export declare function MotionGPUErrorOverlay({ report }: MotionGPUErrorOverlayProps): import("react/jsx-runtime").JSX.Element;
6
+ export {};
@@ -0,0 +1,305 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Portal } from './Portal.js';
3
+ const MOTIONGPU_ERROR_OVERLAY_STYLES = `
4
+ .motiongpu-error-overlay {
5
+ --motiongpu-base-hue: var(--base-hue, 265);
6
+ --motiongpu-color-background: oklch(0.2178 0.0056 var(--motiongpu-base-hue));
7
+ --motiongpu-color-background-muted: oklch(0.261 0.007 var(--motiongpu-base-hue));
8
+ --motiongpu-color-foreground: oklch(1 0 0);
9
+ --motiongpu-color-foreground-muted: oklch(0.6699 0.0081 var(--motiongpu-base-hue));
10
+ --motiongpu-color-card: var(--motiongpu-color-background);
11
+ --motiongpu-color-accent: oklch(0.6996 0.181959 44.4414);
12
+ --motiongpu-color-accent-secondary: oklch(0.5096 0.131959 44.4414);
13
+ --motiongpu-color-border: oklch(0.928 0.013 var(--motiongpu-base-hue) / 0.05);
14
+ --motiongpu-color-white-fixed: oklch(1 0 0);
15
+ --motiongpu-shadow-card: var(
16
+ --shadow-2xl,
17
+ 0px 1px 1px -0.5px rgba(0, 0, 0, 0.06),
18
+ 0px 3px 3px -1.5px rgba(0, 0, 0, 0.06),
19
+ 0px 6px 6px -3px rgba(0, 0, 0, 0.06),
20
+ 0px 12px 12px -6px rgba(0, 0, 0, 0.06),
21
+ 0px 24px 24px -12px rgba(0, 0, 0, 0.05),
22
+ 0px 48px 48px -24px rgba(0, 0, 0, 0.06)
23
+ );
24
+ --motiongpu-radius-md: var(--radius-md, 0.5rem);
25
+ --motiongpu-radius-lg: var(--radius-lg, 0.75rem);
26
+ --motiongpu-radius-xl: var(--radius-xl, 1rem);
27
+ --motiongpu-font-sans: var(
28
+ --font-sans,
29
+ 'Inter',
30
+ 'Segoe UI',
31
+ 'Helvetica Neue',
32
+ Arial,
33
+ sans-serif
34
+ );
35
+ --motiongpu-font-mono: var(--font-mono, 'SFMono-Regular', 'Menlo', 'Consolas', monospace);
36
+ position: fixed;
37
+ inset: 0;
38
+ display: grid;
39
+ place-items: center;
40
+ padding: clamp(0.75rem, 1.4vw, 1.5rem);
41
+ background: rgba(0, 0, 0, 0.8);
42
+ backdrop-filter: blur(10px);
43
+ z-index: 2147483647;
44
+ font-family: var(--motiongpu-font-sans);
45
+ color-scheme: dark;
46
+ }
47
+
48
+ .motiongpu-error-dialog {
49
+ width: min(52rem, calc(100vw - 1.5rem));
50
+ max-height: min(84vh, 44rem);
51
+ overflow: auto;
52
+ margin: 0;
53
+ padding: 1.1rem;
54
+ border: 1px solid var(--motiongpu-color-border);
55
+ border-radius: var(--motiongpu-radius-xl);
56
+ max-width: calc(100vw - 1.5rem);
57
+ box-sizing: border-box;
58
+ font-size: 0.875rem;
59
+ font-weight: 400;
60
+ line-height: 1.45;
61
+ background: var(--motiongpu-color-card);
62
+ color: var(--motiongpu-color-foreground);
63
+ box-shadow: var(--motiongpu-shadow-card);
64
+ }
65
+
66
+ .motiongpu-error-header {
67
+ display: grid;
68
+ gap: 0.55rem;
69
+ padding-bottom: 0.9rem;
70
+ border-bottom: 1px solid var(--motiongpu-color-border);
71
+ }
72
+
73
+ .motiongpu-error-badge-wrap {
74
+ display: inline-flex;
75
+ align-items: center;
76
+ gap: 0.4rem;
77
+ width: fit-content;
78
+ padding: 0.18rem;
79
+ border-radius: 999px;
80
+ border: 1px solid var(--motiongpu-color-border);
81
+ background: var(--motiongpu-color-background-muted);
82
+ }
83
+
84
+ .motiongpu-error-phase {
85
+ display: inline-flex;
86
+ align-items: center;
87
+ margin: 0;
88
+ padding: 0.22rem 0.56rem;
89
+ border-radius: 999px;
90
+ font-size: 0.66rem;
91
+ letter-spacing: 0.08em;
92
+ line-height: 1;
93
+ font-weight: 500;
94
+ text-transform: uppercase;
95
+ color: var(--motiongpu-color-white-fixed);
96
+ background: linear-gradient(
97
+ 180deg,
98
+ var(--motiongpu-color-accent) 0%,
99
+ var(--motiongpu-color-accent-secondary) 100%
100
+ );
101
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.24);
102
+ }
103
+
104
+ .motiongpu-error-title {
105
+ margin: 0;
106
+ font-size: clamp(1.02rem, 1vw + 0.72rem, 1.32rem);
107
+ font-weight: 500;
108
+ line-height: 1.18;
109
+ letter-spacing: -0.02em;
110
+ text-wrap: balance;
111
+ color: var(--motiongpu-color-foreground);
112
+ }
113
+
114
+ .motiongpu-error-body {
115
+ display: grid;
116
+ gap: 0.62rem;
117
+ margin-top: 0.92rem;
118
+ }
119
+
120
+ .motiongpu-error-message {
121
+ margin: 0;
122
+ padding: 0.72rem 0.78rem;
123
+ border: 1px solid color-mix(in oklch, var(--motiongpu-color-accent) 28%, transparent);
124
+ border-radius: var(--motiongpu-radius-md);
125
+ background: color-mix(in oklch, var(--motiongpu-color-accent) 10%, transparent);
126
+ font-size: 0.82rem;
127
+ line-height: 1.4;
128
+ font-weight: 400;
129
+ color: var(--motiongpu-color-foreground);
130
+ }
131
+
132
+ .motiongpu-error-hint {
133
+ margin: 0;
134
+ font-size: 0.82rem;
135
+ line-height: 1.45;
136
+ font-weight: 400;
137
+ color: var(--motiongpu-color-foreground-muted);
138
+ }
139
+
140
+ .motiongpu-error-sections {
141
+ display: grid;
142
+ gap: 0.62rem;
143
+ margin-top: 0.95rem;
144
+ }
145
+
146
+ .motiongpu-error-source {
147
+ display: grid;
148
+ gap: 0.48rem;
149
+ margin-top: 0.96rem;
150
+ }
151
+
152
+ .motiongpu-error-source-title {
153
+ margin: 0;
154
+ font-size: 0.8rem;
155
+ font-weight: 500;
156
+ line-height: 1.3;
157
+ letter-spacing: 0.045em;
158
+ text-transform: uppercase;
159
+ color: var(--motiongpu-color-foreground);
160
+ }
161
+
162
+ .motiongpu-error-source-frame {
163
+ border: 1px solid var(--motiongpu-color-border);
164
+ border-radius: var(--motiongpu-radius-lg);
165
+ overflow: hidden;
166
+ background: var(--motiongpu-color-background-muted);
167
+ }
168
+
169
+ .motiongpu-error-source-tabs {
170
+ display: flex;
171
+ align-items: stretch;
172
+ border-bottom: 1px solid var(--motiongpu-color-border);
173
+ background: var(--motiongpu-color-background);
174
+ }
175
+
176
+ .motiongpu-error-source-tab {
177
+ display: inline-flex;
178
+ align-items: center;
179
+ padding: 0.5rem 0.68rem;
180
+ font-size: 0.76rem;
181
+ font-weight: 400;
182
+ line-height: 1.2;
183
+ color: var(--motiongpu-color-foreground-muted);
184
+ border-right: 1px solid var(--motiongpu-color-border);
185
+ }
186
+
187
+ .motiongpu-error-source-tab-active {
188
+ color: var(--motiongpu-color-foreground);
189
+ background: var(--motiongpu-color-background-muted);
190
+ }
191
+
192
+ .motiongpu-error-source-tab-spacer {
193
+ flex: 1 1 auto;
194
+ }
195
+
196
+ .motiongpu-error-source-snippet {
197
+ display: grid;
198
+ background: var(--motiongpu-color-background-muted);
199
+ }
200
+
201
+ .motiongpu-error-source-row {
202
+ display: grid;
203
+ grid-template-columns: 2rem minmax(0, 1fr);
204
+ align-items: start;
205
+ gap: 0.42rem;
206
+ padding: 0.2rem 0.68rem;
207
+ }
208
+
209
+ .motiongpu-error-source-row-active {
210
+ background: color-mix(in oklch, var(--motiongpu-color-accent) 10%, transparent);
211
+ }
212
+
213
+ .motiongpu-error-source-line {
214
+ font-family: var(--motiongpu-font-mono);
215
+ font-size: 0.77rem;
216
+ font-weight: 400;
217
+ line-height: 1.3;
218
+ font-variant-numeric: tabular-nums;
219
+ font-feature-settings: 'tnum' 1;
220
+ border-right: 1px solid var(--motiongpu-color-border);
221
+ color: var(--motiongpu-color-foreground-muted);
222
+ text-align: left;
223
+ }
224
+
225
+ .motiongpu-error-source-code {
226
+ font-family: var(--motiongpu-font-mono);
227
+ font-size: 0.77rem;
228
+ font-weight: 400;
229
+ line-height: 1.3;
230
+ color: var(--motiongpu-color-foreground);
231
+ white-space: pre-wrap;
232
+ word-break: break-word;
233
+ }
234
+
235
+ .motiongpu-error-details {
236
+ border: 1px solid var(--motiongpu-color-border);
237
+ border-radius: var(--motiongpu-radius-lg);
238
+ overflow: hidden;
239
+ background: var(--motiongpu-color-background);
240
+ }
241
+
242
+ .motiongpu-error-details summary {
243
+ cursor: pointer;
244
+ padding: 0.56rem 0.68rem;
245
+ font-size: 0.7rem;
246
+ letter-spacing: 0.07em;
247
+ line-height: 1.2;
248
+ font-weight: 500;
249
+ text-transform: uppercase;
250
+ color: var(--motiongpu-color-foreground);
251
+ }
252
+
253
+ .motiongpu-error-details[open] summary {
254
+ border-bottom: 1px solid var(--motiongpu-color-border);
255
+ }
256
+
257
+ .motiongpu-error-details pre {
258
+ margin: 0;
259
+ padding: 0.62rem 0.68rem;
260
+ white-space: pre-wrap;
261
+ word-break: break-word;
262
+ overflow: auto;
263
+ background: var(--motiongpu-color-background-muted);
264
+ font-size: 0.74rem;
265
+ line-height: 1.4;
266
+ font-weight: 400;
267
+ color: var(--motiongpu-color-foreground);
268
+ font-family: var(--motiongpu-font-mono);
269
+ }
270
+
271
+ @media (max-width: 42rem) {
272
+ .motiongpu-error-overlay {
273
+ padding: 0.62rem;
274
+ }
275
+
276
+ .motiongpu-error-dialog {
277
+ padding: 0.85rem;
278
+ }
279
+
280
+ .motiongpu-error-title {
281
+ font-size: 1.02rem;
282
+ }
283
+ }
284
+
285
+ @media (prefers-reduced-motion: reduce) {
286
+ .motiongpu-error-overlay {
287
+ backdrop-filter: none;
288
+ }
289
+ }
290
+ `;
291
+ function normalizeErrorText(value) {
292
+ return value
293
+ .trim()
294
+ .replace(/[.:!]+$/g, '')
295
+ .toLowerCase();
296
+ }
297
+ function shouldShowErrorMessage(value) {
298
+ return normalizeErrorText(value.message) !== normalizeErrorText(value.title);
299
+ }
300
+ export function MotionGPUErrorOverlay({ report }) {
301
+ const detailsSummary = report.source ? 'Additional diagnostics' : 'Technical details';
302
+ return (_jsxs(Portal, { children: [_jsx("style", { children: MOTIONGPU_ERROR_OVERLAY_STYLES }), _jsx("div", { className: "motiongpu-error-overlay", role: "presentation", children: _jsxs("section", { className: "motiongpu-error-dialog", role: "alertdialog", "aria-live": "assertive", "aria-modal": "true", "data-testid": "motiongpu-error", children: [_jsxs("header", { className: "motiongpu-error-header", children: [_jsx("div", { className: "motiongpu-error-badge-wrap", children: _jsx("p", { className: "motiongpu-error-phase", children: report.phase }) }), _jsx("h2", { className: "motiongpu-error-title", children: report.title })] }), _jsxs("div", { className: "motiongpu-error-body", children: [shouldShowErrorMessage(report) ? (_jsx("p", { className: "motiongpu-error-message", children: report.message })) : null, _jsx("p", { className: "motiongpu-error-hint", children: report.hint })] }), report.source ? (_jsxs("section", { className: "motiongpu-error-source", "aria-label": "Source", children: [_jsx("h3", { className: "motiongpu-error-source-title", children: "Source" }), _jsxs("div", { className: "motiongpu-error-source-frame", role: "presentation", children: [_jsxs("div", { className: "motiongpu-error-source-tabs", role: "tablist", "aria-label": "Source files", children: [_jsxs("span", { className: "motiongpu-error-source-tab motiongpu-error-source-tab-active", role: "tab", "aria-selected": "true", children: [report.source.location, report.source.column ? `, col ${report.source.column}` : ''] }), _jsx("span", { className: "motiongpu-error-source-tab-spacer", "aria-hidden": "true" })] }), _jsx("div", { className: "motiongpu-error-source-snippet", children: report.source.snippet.map((snippetLine) => (_jsxs("div", { className: snippetLine.highlight
303
+ ? 'motiongpu-error-source-row motiongpu-error-source-row-active'
304
+ : 'motiongpu-error-source-row', children: [_jsx("span", { className: "motiongpu-error-source-line", children: snippetLine.number }), _jsx("span", { className: "motiongpu-error-source-code", children: snippetLine.code || ' ' })] }, `snippet-${snippetLine.number}`))) })] })] })) : null, _jsxs("div", { className: "motiongpu-error-sections", children: [report.details.length > 0 ? (_jsxs("details", { className: "motiongpu-error-details", open: true, children: [_jsx("summary", { children: detailsSummary }), _jsx("pre", { children: report.details.join('\n') })] })) : null, report.stack.length > 0 ? (_jsxs("details", { className: "motiongpu-error-details", children: [_jsx("summary", { children: "Stack trace" }), _jsx("pre", { children: report.stack.join('\n') })] })) : null] })] }) })] }));
305
+ }
@@ -0,0 +1,6 @@
1
+ import { type ReactNode } from 'react';
2
+ export interface PortalProps {
3
+ target?: string | HTMLElement | null;
4
+ children?: ReactNode;
5
+ }
6
+ export declare function Portal({ target, children }: PortalProps): import("react").ReactPortal | null;
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { createPortal } from 'react-dom';
4
+ function resolveTargetElement(input) {
5
+ if (typeof document === 'undefined') {
6
+ throw new Error('Portal target resolution requires a browser environment');
7
+ }
8
+ return typeof input === 'string'
9
+ ? (document.querySelector(input) ?? document.body)
10
+ : (input ?? document.body);
11
+ }
12
+ export function Portal({ target = 'body', children }) {
13
+ const [targetElement, setTargetElement] = useState(null);
14
+ useEffect(() => {
15
+ if (typeof document === 'undefined') {
16
+ return;
17
+ }
18
+ setTargetElement(resolveTargetElement(target));
19
+ }, [target]);
20
+ if (!targetElement) {
21
+ return null;
22
+ }
23
+ return createPortal(_jsx("div", { children: children ?? null }), targetElement);
24
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * React adapter advanced entrypoint for MotionGPU.
3
+ */
4
+ export * from './index.js';
5
+ export { applySchedulerPreset, captureSchedulerDebugSnapshot } from '../core/scheduler-helpers.js';
6
+ export { setMotionGPUUserContext, useMotionGPUUserContext, useSetMotionGPUUserContext } from './use-motiongpu-user-context.js';
7
+ export type { ApplySchedulerPresetOptions, SchedulerDebugSnapshot, SchedulerPreset, SchedulerPresetConfig } from '../core/scheduler-helpers.js';
8
+ export type { MotionGPUUserContext, MotionGPUUserNamespace } from './motiongpu-context.js';
9
+ export type { FrameProfilingSnapshot, FrameKey, FrameTaskInvalidation, FrameTaskInvalidationToken, FrameRunTimings, FrameScheduleSnapshot, FrameStage, FrameStageCallback, FrameTimingStats, FrameTask } from '../core/frame-registry.js';
10
+ export type { SetMotionGPUUserContextOptions } from './use-motiongpu-user-context.js';
11
+ export type { RenderPassContext, RenderTarget, UniformLayout, UniformLayoutEntry } from '../core/types.js';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * React adapter advanced entrypoint for MotionGPU.
3
+ */
4
+ export * from './index.js';
5
+ export { applySchedulerPreset, captureSchedulerDebugSnapshot } from '../core/scheduler-helpers.js';
6
+ export { setMotionGPUUserContext, useMotionGPUUserContext, useSetMotionGPUUserContext } from './use-motiongpu-user-context.js';
@@ -0,0 +1,14 @@
1
+ import type { FrameCallback, FrameKey, FrameProfilingSnapshot, FrameRegistry, FrameRunTimings, FrameScheduleSnapshot, FrameStage, FrameStageCallback, FrameTask, FrameTaskInvalidation, FrameTaskInvalidationToken, UseFrameOptions, UseFrameResult } from '../core/frame-registry.js';
2
+ /**
3
+ * React context container for the active frame registry.
4
+ */
5
+ export declare const FrameRegistryReactContext: import("react").Context<FrameRegistry | null>;
6
+ export type { FrameCallback, FrameKey, FrameProfilingSnapshot, FrameRegistry, FrameRunTimings, FrameScheduleSnapshot, FrameStage, FrameStageCallback, FrameTask, FrameTaskInvalidation, FrameTaskInvalidationToken, UseFrameOptions, UseFrameResult };
7
+ /**
8
+ * Registers a frame callback using an auto-generated task key.
9
+ */
10
+ export declare function useFrame(callback: FrameCallback, options?: UseFrameOptions): UseFrameResult;
11
+ /**
12
+ * Registers a frame callback with an explicit task key.
13
+ */
14
+ export declare function useFrame(key: FrameKey, callback: FrameCallback, options?: UseFrameOptions): UseFrameResult;