@motion-core/motion-gpu 0.2.0 → 0.4.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 (52) hide show
  1. package/README.md +78 -1
  2. package/dist/core/current-value.js +3 -0
  3. package/dist/core/error-diagnostics.d.ts +14 -0
  4. package/dist/core/error-diagnostics.js +41 -1
  5. package/dist/core/error-report.d.ts +37 -0
  6. package/dist/core/error-report.js +60 -1
  7. package/dist/core/index.d.ts +1 -1
  8. package/dist/core/material-preprocess.d.ts +5 -5
  9. package/dist/core/material-preprocess.js +1 -4
  10. package/dist/core/material.d.ts +32 -23
  11. package/dist/core/material.js +14 -7
  12. package/dist/core/renderer.d.ts +10 -0
  13. package/dist/core/renderer.js +66 -4
  14. package/dist/core/runtime-loop.d.ts +3 -0
  15. package/dist/core/runtime-loop.js +72 -1
  16. package/dist/core/types.d.ts +24 -10
  17. package/dist/passes/BlitPass.d.ts +6 -27
  18. package/dist/passes/BlitPass.js +10 -121
  19. package/dist/passes/FullscreenPass.d.ts +37 -0
  20. package/dist/passes/FullscreenPass.js +131 -0
  21. package/dist/passes/ShaderPass.d.ts +6 -26
  22. package/dist/passes/ShaderPass.js +10 -121
  23. package/dist/react/FragCanvas.d.ts +26 -0
  24. package/dist/react/FragCanvas.js +218 -0
  25. package/dist/react/FragCanvas.tsx +345 -0
  26. package/dist/react/MotionGPUErrorOverlay.d.ts +6 -0
  27. package/dist/react/MotionGPUErrorOverlay.js +52 -0
  28. package/dist/react/MotionGPUErrorOverlay.tsx +129 -0
  29. package/dist/react/Portal.d.ts +6 -0
  30. package/dist/react/Portal.js +24 -0
  31. package/dist/react/Portal.tsx +34 -0
  32. package/dist/react/advanced.d.ts +11 -0
  33. package/dist/react/advanced.js +6 -0
  34. package/dist/react/frame-context.d.ts +14 -0
  35. package/dist/react/frame-context.js +98 -0
  36. package/dist/react/index.d.ts +15 -0
  37. package/dist/react/index.js +9 -0
  38. package/dist/react/motiongpu-context.d.ts +73 -0
  39. package/dist/react/motiongpu-context.js +18 -0
  40. package/dist/react/use-motiongpu-user-context.d.ts +49 -0
  41. package/dist/react/use-motiongpu-user-context.js +94 -0
  42. package/dist/react/use-texture.d.ts +40 -0
  43. package/dist/react/use-texture.js +162 -0
  44. package/dist/svelte/FragCanvas.svelte +45 -16
  45. package/dist/svelte/FragCanvas.svelte.d.ts +2 -0
  46. package/dist/svelte/MotionGPUErrorOverlay.svelte +10 -19
  47. package/dist/svelte/Portal.svelte +6 -21
  48. package/dist/svelte/use-motiongpu-user-context.d.ts +9 -1
  49. package/dist/svelte/use-motiongpu-user-context.js +4 -1
  50. package/dist/svelte/use-texture.d.ts +11 -2
  51. package/dist/svelte/use-texture.js +13 -3
  52. package/package.json +28 -3
@@ -53,9 +53,8 @@
53
53
  class="motiongpu-error-source-tab motiongpu-error-source-tab-active"
54
54
  role="tab"
55
55
  aria-selected="true"
56
- >{report.source.component} (fragment line {report.source
57
- .line}{#if report.source.column}, col
58
- {report.source.column}{/if})</span
56
+ >{report.source.location}{#if report.source.column}, col {report.source
57
+ .column}{/if}</span
59
58
  >
60
59
  <span class="motiongpu-error-source-tab-spacer" aria-hidden="true"></span>
61
60
  </div>
@@ -117,21 +116,13 @@
117
116
  --motiongpu-radius-xl: var(--radius-xl, 1rem);
118
117
  --motiongpu-font-sans: var(
119
118
  --font-sans,
120
- 'Aeonik Pro',
121
119
  'Inter',
122
120
  'Segoe UI',
123
121
  'Helvetica Neue',
124
122
  Arial,
125
123
  sans-serif
126
124
  );
127
- --motiongpu-font-mono: var(
128
- --font-mono,
129
- 'Aeonik font-mono',
130
- 'SFMono-Regular',
131
- 'Menlo',
132
- 'Consolas',
133
- monospace
134
- );
125
+ --motiongpu-font-mono: var(--font-mono, 'SFMono-Regular', 'Menlo', 'Consolas', monospace);
135
126
  position: fixed;
136
127
  inset: 0;
137
128
  display: grid;
@@ -156,7 +147,7 @@
156
147
  max-width: calc(100vw - 1.5rem);
157
148
  box-sizing: border-box;
158
149
  font-size: 0.875rem;
159
- font-weight: 300;
150
+ font-weight: 400;
160
151
  line-height: 1.45;
161
152
  background: linear-gradient(
162
153
  180deg,
@@ -229,7 +220,7 @@
229
220
  background: color-mix(in srgb, var(--motiongpu-color-accent) 9%, var(--motiongpu-color-card));
230
221
  font-size: 0.82rem;
231
222
  line-height: 1.4;
232
- font-weight: 300;
223
+ font-weight: 400;
233
224
  color: var(--motiongpu-color-foreground);
234
225
  }
235
226
 
@@ -237,7 +228,7 @@
237
228
  margin: 0;
238
229
  font-size: 0.82rem;
239
230
  line-height: 1.45;
240
- font-weight: 300;
231
+ font-weight: 400;
241
232
  color: var(--motiongpu-color-foreground-muted);
242
233
  }
243
234
 
@@ -282,7 +273,7 @@
282
273
  align-items: center;
283
274
  padding: 0.5rem 0.68rem;
284
275
  font-size: 0.76rem;
285
- font-weight: 300;
276
+ font-weight: 400;
286
277
  line-height: 1.2;
287
278
  color: var(--motiongpu-color-foreground-muted);
288
279
  border-right: 1px solid var(--motiongpu-color-border);
@@ -317,7 +308,7 @@
317
308
  .motiongpu-error-source-line {
318
309
  font-family: var(--motiongpu-font-mono);
319
310
  font-size: 0.77rem;
320
- font-weight: 300;
311
+ font-weight: 400;
321
312
  line-height: 1.3;
322
313
  font-variant-numeric: tabular-nums;
323
314
  font-feature-settings: 'tnum' 1;
@@ -329,7 +320,7 @@
329
320
  .motiongpu-error-source-code {
330
321
  font-family: var(--motiongpu-font-mono);
331
322
  font-size: 0.77rem;
332
- font-weight: 350;
323
+ font-weight: 400;
333
324
  line-height: 1.3;
334
325
  color: var(--motiongpu-color-foreground);
335
326
  white-space: pre-wrap;
@@ -367,7 +358,7 @@
367
358
  background: var(--motiongpu-color-background-muted);
368
359
  font-size: 0.74rem;
369
360
  line-height: 1.4;
370
- font-weight: 300;
361
+ font-weight: 400;
371
362
  color: var(--motiongpu-color-foreground);
372
363
  font-family: var(--motiongpu-font-mono);
373
364
  }
@@ -14,33 +14,18 @@
14
14
  : (input ?? document.body);
15
15
  }
16
16
 
17
- const portal = (node: HTMLDivElement, initialTarget: string | HTMLElement | null) => {
18
- let targetElement = resolveTargetElement(initialTarget);
17
+ const portal = (node: HTMLDivElement) => {
18
+ const targetElement = resolveTargetElement(target);
19
19
  targetElement.appendChild(node);
20
20
 
21
- return {
22
- update(nextTarget: string | HTMLElement | null) {
23
- const nextTargetElement = resolveTargetElement(nextTarget);
24
- if (nextTargetElement === targetElement) {
25
- return;
26
- }
27
-
28
- if (node.parentNode === targetElement) {
29
- targetElement.removeChild(node);
30
- }
31
-
32
- nextTargetElement.appendChild(node);
33
- targetElement = nextTargetElement;
34
- },
35
- destroy() {
36
- if (node.parentNode === targetElement) {
37
- targetElement.removeChild(node);
38
- }
21
+ return () => {
22
+ if (node.parentNode === targetElement) {
23
+ targetElement.removeChild(node);
39
24
  }
40
25
  };
41
26
  };
42
27
  </script>
43
28
 
44
- <div use:portal={target}>
29
+ <div {@attach portal}>
45
30
  {@render children?.()}
46
31
  </div>
@@ -17,6 +17,14 @@ export interface SetMotionGPUUserContextOptions {
17
17
  * @default 'skip'
18
18
  */
19
19
  existing?: 'merge' | 'replace' | 'skip';
20
+ /**
21
+ * How function inputs should be interpreted:
22
+ * - `factory`: call function and store its return value
23
+ * - `value`: store function itself
24
+ *
25
+ * @default 'factory'
26
+ */
27
+ functionValue?: 'factory' | 'value';
20
28
  }
21
29
  /**
22
30
  * Returns a read-only view of the entire motiongpu user context store.
@@ -25,7 +33,7 @@ export declare function useMotionGPUUserContext<UC extends UserContextStore = Us
25
33
  /**
26
34
  * Reads a namespaced user context value as a reactive readable store.
27
35
  */
28
- export declare function useMotionGPUUserContext<UCT = unknown>(namespace: MotionGPUUserNamespace): CurrentReadable<UCT | undefined>;
36
+ export declare function useMotionGPUUserContext<UC extends UserContextStore = UserContextStore, K extends keyof UC & MotionGPUUserNamespace = keyof UC & MotionGPUUserNamespace>(namespace: K): CurrentReadable<UC[K] | undefined>;
29
37
  /**
30
38
  * Sets a namespaced user context value with explicit write semantics.
31
39
  *
@@ -43,6 +43,7 @@ export function useMotionGPUUserContext(namespace) {
43
43
  export function setMotionGPUUserContext(namespace, value, options) {
44
44
  const userStore = useMotionGPU().user;
45
45
  const mode = options?.existing ?? 'skip';
46
+ const functionValueMode = options?.functionValue ?? 'factory';
46
47
  let resolvedValue;
47
48
  userStore.update((context) => {
48
49
  const hasExisting = namespace in context;
@@ -50,7 +51,9 @@ export function setMotionGPUUserContext(namespace, value, options) {
50
51
  resolvedValue = context[namespace];
51
52
  return context;
52
53
  }
53
- const nextValue = typeof value === 'function' ? value() : value;
54
+ const nextValue = typeof value === 'function' && functionValueMode === 'factory'
55
+ ? value()
56
+ : value;
54
57
  if (hasExisting && mode === 'merge') {
55
58
  const currentValue = context[namespace];
56
59
  if (isObjectEntry(currentValue) && isObjectEntry(nextValue)) {
@@ -1,5 +1,6 @@
1
1
  import { type CurrentReadable } from '../core/current-value.js';
2
2
  import { type LoadedTexture, type TextureLoadOptions } from '../core/texture-loader.js';
3
+ import { type MotionGPUErrorReport } from '../core/error-report.js';
3
4
  /**
4
5
  * Reactive state returned by {@link useTexture}.
5
6
  */
@@ -16,6 +17,10 @@ export interface UseTextureResult {
16
17
  * Last loading error.
17
18
  */
18
19
  error: CurrentReadable<Error | null>;
20
+ /**
21
+ * Last loading error normalized to MotionGPU diagnostics report shape.
22
+ */
23
+ errorReport: CurrentReadable<MotionGPUErrorReport | null>;
19
24
  /**
20
25
  * Reloads all textures using current URL input.
21
26
  */
@@ -25,11 +30,15 @@ export interface UseTextureResult {
25
30
  * Supported URL input variants for `useTexture`.
26
31
  */
27
32
  export type TextureUrlInput = string[] | (() => string[]);
33
+ /**
34
+ * Supported options input variants for `useTexture`.
35
+ */
36
+ export type TextureOptionsInput = TextureLoadOptions | (() => TextureLoadOptions);
28
37
  /**
29
38
  * Loads textures from URLs and exposes reactive loading/error state.
30
39
  *
31
40
  * @param urlInput - URLs array or lazy URL provider.
32
- * @param options - Loader options passed to URL fetch/decode pipeline.
41
+ * @param optionsInput - Loader options object or lazy options provider.
33
42
  * @returns Reactive texture loading state with reload support.
34
43
  */
35
- export declare function useTexture(urlInput: TextureUrlInput, options?: TextureLoadOptions): UseTextureResult;
44
+ export declare function useTexture(urlInput: TextureUrlInput, optionsInput?: TextureOptionsInput): UseTextureResult;
@@ -1,6 +1,7 @@
1
1
  import { onDestroy } from 'svelte';
2
2
  import { createCurrentWritable as currentWritable } from '../core/current-value.js';
3
3
  import { isAbortError, loadTexturesFromUrls } from '../core/texture-loader.js';
4
+ import { toMotionGPUErrorReport } from '../core/error-report.js';
4
5
  /**
5
6
  * Normalizes unknown thrown values to an `Error` instance.
6
7
  */
@@ -53,19 +54,23 @@ function mergeAbortSignals(primary, secondary) {
53
54
  * Loads textures from URLs and exposes reactive loading/error state.
54
55
  *
55
56
  * @param urlInput - URLs array or lazy URL provider.
56
- * @param options - Loader options passed to URL fetch/decode pipeline.
57
+ * @param optionsInput - Loader options object or lazy options provider.
57
58
  * @returns Reactive texture loading state with reload support.
58
59
  */
59
- export function useTexture(urlInput, options = {}) {
60
+ export function useTexture(urlInput, optionsInput = {}) {
60
61
  const textures = currentWritable(null);
61
62
  const loading = currentWritable(true);
62
63
  const error = currentWritable(null);
64
+ const errorReport = currentWritable(null);
63
65
  let disposed = false;
64
66
  let requestVersion = 0;
65
67
  let activeController = null;
66
68
  let runningLoad = null;
67
69
  let reloadQueued = false;
68
70
  const getUrls = typeof urlInput === 'function' ? urlInput : () => urlInput;
71
+ const getOptions = typeof optionsInput === 'function'
72
+ ? optionsInput
73
+ : () => optionsInput;
69
74
  const executeLoad = async () => {
70
75
  if (disposed) {
71
76
  return;
@@ -75,7 +80,9 @@ export function useTexture(urlInput, options = {}) {
75
80
  activeController = controller;
76
81
  loading.set(true);
77
82
  error.set(null);
83
+ errorReport.set(null);
78
84
  const previous = textures.current;
85
+ const options = getOptions() ?? {};
79
86
  const mergedSignal = mergeAbortSignals(controller.signal, options.signal);
80
87
  try {
81
88
  const loaded = await loadTexturesFromUrls(getUrls(), {
@@ -98,7 +105,9 @@ export function useTexture(urlInput, options = {}) {
98
105
  }
99
106
  disposeTextures(previous);
100
107
  textures.set(null);
101
- error.set(toError(nextError));
108
+ const normalizedError = toError(nextError);
109
+ error.set(normalizedError);
110
+ errorReport.set(toMotionGPUErrorReport(normalizedError, 'initialization'));
102
111
  }
103
112
  finally {
104
113
  if (!disposed && version === requestVersion) {
@@ -142,6 +151,7 @@ export function useTexture(urlInput, options = {}) {
142
151
  textures,
143
152
  loading,
144
153
  error,
154
+ errorReport,
145
155
  reload: load
146
156
  };
147
157
  }
package/package.json CHANGED
@@ -1,10 +1,12 @@
1
1
  {
2
2
  "name": "@motion-core/motion-gpu",
3
- "version": "0.2.0",
4
- "description": "Framework-agnostic WebGPU runtime for fullscreen WGSL shaders with explicit Svelte adapter entrypoints.",
3
+ "version": "0.4.0",
4
+ "description": "Framework-agnostic WebGPU runtime for fullscreen WGSL shaders with explicit Svelte and React adapter entrypoints.",
5
5
  "keywords": [
6
6
  "svelte",
7
7
  "svelte5",
8
+ "react",
9
+ "react18",
8
10
  "webgpu",
9
11
  "wgsl",
10
12
  "shader",
@@ -46,6 +48,14 @@
46
48
  "svelte": "./dist/svelte/advanced.js",
47
49
  "default": "./dist/svelte/advanced.js"
48
50
  },
51
+ "./react": {
52
+ "types": "./dist/react/index.d.ts",
53
+ "default": "./dist/react/index.js"
54
+ },
55
+ "./react/advanced": {
56
+ "types": "./dist/react/advanced.d.ts",
57
+ "default": "./dist/react/advanced.js"
58
+ },
49
59
  "./core": {
50
60
  "types": "./dist/core/index.d.ts",
51
61
  "default": "./dist/core/index.js"
@@ -59,7 +69,7 @@
59
69
  "dist"
60
70
  ],
61
71
  "scripts": {
62
- "build": "svelte-package -i src/lib -o dist",
72
+ "build": "svelte-package -i src/lib -o dist && node ./scripts/build/transpile-react-tsx.mjs",
63
73
  "prepack": "bun run build",
64
74
  "check": "svelte-check --tsconfig ./tsconfig.json && publint",
65
75
  "test": "vitest run",
@@ -68,6 +78,8 @@
68
78
  "test:e2e": "playwright test",
69
79
  "test:e2e:headed": "playwright test --headed",
70
80
  "e2e:serve": "vite --config e2e/vite.config.ts",
81
+ "e2e:serve:svelte": "MOTION_GPU_E2E_FRAMEWORK=svelte vite --config e2e/vite.config.ts",
82
+ "e2e:serve:react": "MOTION_GPU_E2E_FRAMEWORK=react vite --config e2e/vite.config.ts",
71
83
  "perf:core": "bun run ./scripts/perf/core-benchmark.ts",
72
84
  "perf:core:check": "bun run ./scripts/perf/core-benchmark.ts --strict",
73
85
  "perf:core:baseline": "bun run ./scripts/perf/core-benchmark.ts --update-baseline",
@@ -78,9 +90,17 @@
78
90
  "format": "prettier --write ."
79
91
  },
80
92
  "peerDependencies": {
93
+ "react": "^18.0.0 || ^19.0.0",
94
+ "react-dom": "^18.0.0 || ^19.0.0",
81
95
  "svelte": "^5.0.0"
82
96
  },
83
97
  "peerDependenciesMeta": {
98
+ "react": {
99
+ "optional": true
100
+ },
101
+ "react-dom": {
102
+ "optional": true
103
+ },
84
104
  "svelte": {
85
105
  "optional": true
86
106
  }
@@ -91,7 +111,10 @@
91
111
  "@playwright/test": "^1.58.2",
92
112
  "@sveltejs/package": "^2.5.0",
93
113
  "@sveltejs/vite-plugin-svelte": "^6.2.4",
114
+ "@testing-library/react": "^16.3.0",
94
115
  "@testing-library/svelte": "^5.2.8",
116
+ "@types/react": "^19.2.2",
117
+ "@types/react-dom": "^19.2.2",
95
118
  "@vitest/coverage-v8": "^4.0.18",
96
119
  "@types/node": "^25.3.3",
97
120
  "@webgpu/types": "^0.1.66",
@@ -104,6 +127,8 @@
104
127
  "prettier-plugin-svelte": "^3.4.1",
105
128
  "prettier-plugin-tailwindcss": "^0.7.2",
106
129
  "publint": "^0.3.15",
130
+ "react": "^19.2.0",
131
+ "react-dom": "^19.2.0",
107
132
  "svelte": "^5.51.0",
108
133
  "svelte-check": "^4.3.6",
109
134
  "typescript": "^5.9.3",