@motion-core/motion-gpu 0.4.1 → 0.5.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 (228) hide show
  1. package/README.md +99 -0
  2. package/dist/advanced.d.ts +1 -0
  3. package/dist/advanced.d.ts.map +1 -0
  4. package/dist/advanced.js +14 -6
  5. package/dist/core/advanced.d.ts +1 -0
  6. package/dist/core/advanced.d.ts.map +1 -0
  7. package/dist/core/advanced.js +14 -5
  8. package/dist/core/compute-shader.d.ts +87 -0
  9. package/dist/core/compute-shader.d.ts.map +1 -0
  10. package/dist/core/compute-shader.js +205 -0
  11. package/dist/core/compute-shader.js.map +1 -0
  12. package/dist/core/current-value.d.ts +1 -0
  13. package/dist/core/current-value.d.ts.map +1 -0
  14. package/dist/core/current-value.js +35 -34
  15. package/dist/core/current-value.js.map +1 -0
  16. package/dist/core/error-diagnostics.d.ts +1 -0
  17. package/dist/core/error-diagnostics.d.ts.map +1 -0
  18. package/dist/core/error-diagnostics.js +70 -137
  19. package/dist/core/error-diagnostics.js.map +1 -0
  20. package/dist/core/error-report.d.ts +2 -1
  21. package/dist/core/error-report.d.ts.map +1 -0
  22. package/dist/core/error-report.js +247 -233
  23. package/dist/core/error-report.js.map +1 -0
  24. package/dist/core/frame-registry.d.ts +1 -0
  25. package/dist/core/frame-registry.d.ts.map +1 -0
  26. package/dist/core/frame-registry.js +546 -662
  27. package/dist/core/frame-registry.js.map +1 -0
  28. package/dist/core/index.d.ts +6 -2
  29. package/dist/core/index.d.ts.map +1 -0
  30. package/dist/core/index.js +13 -12
  31. package/dist/core/material-preprocess.d.ts +1 -0
  32. package/dist/core/material-preprocess.d.ts.map +1 -0
  33. package/dist/core/material-preprocess.js +131 -152
  34. package/dist/core/material-preprocess.js.map +1 -0
  35. package/dist/core/material.d.ts +23 -6
  36. package/dist/core/material.d.ts.map +1 -0
  37. package/dist/core/material.js +290 -317
  38. package/dist/core/material.js.map +1 -0
  39. package/dist/core/recompile-policy.d.ts +1 -0
  40. package/dist/core/recompile-policy.d.ts.map +1 -0
  41. package/dist/core/recompile-policy.js +18 -13
  42. package/dist/core/recompile-policy.js.map +1 -0
  43. package/dist/core/render-graph.d.ts +8 -3
  44. package/dist/core/render-graph.d.ts.map +1 -0
  45. package/dist/core/render-graph.js +77 -68
  46. package/dist/core/render-graph.js.map +1 -0
  47. package/dist/core/render-targets.d.ts +1 -0
  48. package/dist/core/render-targets.d.ts.map +1 -0
  49. package/dist/core/render-targets.js +52 -53
  50. package/dist/core/render-targets.js.map +1 -0
  51. package/dist/core/renderer.d.ts +1 -0
  52. package/dist/core/renderer.d.ts.map +1 -0
  53. package/dist/core/renderer.js +1337 -1081
  54. package/dist/core/renderer.js.map +1 -0
  55. package/dist/core/runtime-loop.d.ts +3 -2
  56. package/dist/core/runtime-loop.d.ts.map +1 -0
  57. package/dist/core/runtime-loop.js +353 -362
  58. package/dist/core/runtime-loop.js.map +1 -0
  59. package/dist/core/scheduler-helpers.d.ts +1 -0
  60. package/dist/core/scheduler-helpers.d.ts.map +1 -0
  61. package/dist/core/scheduler-helpers.js +52 -51
  62. package/dist/core/scheduler-helpers.js.map +1 -0
  63. package/dist/core/shader.d.ts +10 -1
  64. package/dist/core/shader.d.ts.map +1 -0
  65. package/dist/core/shader.js +109 -115
  66. package/dist/core/shader.js.map +1 -0
  67. package/dist/core/storage-buffers.d.ts +37 -0
  68. package/dist/core/storage-buffers.d.ts.map +1 -0
  69. package/dist/core/storage-buffers.js +95 -0
  70. package/dist/core/storage-buffers.js.map +1 -0
  71. package/dist/core/texture-loader.d.ts +1 -0
  72. package/dist/core/texture-loader.d.ts.map +1 -0
  73. package/dist/core/texture-loader.js +209 -273
  74. package/dist/core/texture-loader.js.map +1 -0
  75. package/dist/core/textures.d.ts +13 -0
  76. package/dist/core/textures.d.ts.map +1 -0
  77. package/dist/core/textures.js +111 -116
  78. package/dist/core/textures.js.map +1 -0
  79. package/dist/core/types.d.ts +147 -4
  80. package/dist/core/types.d.ts.map +1 -0
  81. package/dist/core/types.js +0 -4
  82. package/dist/core/uniforms.d.ts +1 -0
  83. package/dist/core/uniforms.d.ts.map +1 -0
  84. package/dist/core/uniforms.js +170 -191
  85. package/dist/core/uniforms.js.map +1 -0
  86. package/dist/index.d.ts +1 -0
  87. package/dist/index.d.ts.map +1 -0
  88. package/dist/index.js +13 -6
  89. package/dist/passes/BlitPass.d.ts +1 -0
  90. package/dist/passes/BlitPass.d.ts.map +1 -0
  91. package/dist/passes/BlitPass.js +23 -18
  92. package/dist/passes/BlitPass.js.map +1 -0
  93. package/dist/passes/ComputePass.d.ts +83 -0
  94. package/dist/passes/ComputePass.d.ts.map +1 -0
  95. package/dist/passes/ComputePass.js +92 -0
  96. package/dist/passes/ComputePass.js.map +1 -0
  97. package/dist/passes/CopyPass.d.ts +1 -0
  98. package/dist/passes/CopyPass.d.ts.map +1 -0
  99. package/dist/passes/CopyPass.js +58 -52
  100. package/dist/passes/CopyPass.js.map +1 -0
  101. package/dist/passes/FullscreenPass.d.ts +1 -0
  102. package/dist/passes/FullscreenPass.d.ts.map +1 -0
  103. package/dist/passes/FullscreenPass.js +127 -130
  104. package/dist/passes/FullscreenPass.js.map +1 -0
  105. package/dist/passes/PingPongComputePass.d.ts +104 -0
  106. package/dist/passes/PingPongComputePass.d.ts.map +1 -0
  107. package/dist/passes/PingPongComputePass.js +132 -0
  108. package/dist/passes/PingPongComputePass.js.map +1 -0
  109. package/dist/passes/ShaderPass.d.ts +1 -0
  110. package/dist/passes/ShaderPass.d.ts.map +1 -0
  111. package/dist/passes/ShaderPass.js +41 -37
  112. package/dist/passes/ShaderPass.js.map +1 -0
  113. package/dist/passes/index.d.ts +3 -0
  114. package/dist/passes/index.d.ts.map +1 -0
  115. package/dist/passes/index.js +6 -3
  116. package/dist/react/FragCanvas.d.ts +3 -2
  117. package/dist/react/FragCanvas.d.ts.map +1 -0
  118. package/dist/react/FragCanvas.js +234 -211
  119. package/dist/react/FragCanvas.js.map +1 -0
  120. package/dist/react/MotionGPUErrorOverlay.d.ts +1 -0
  121. package/dist/react/MotionGPUErrorOverlay.d.ts.map +1 -0
  122. package/dist/react/MotionGPUErrorOverlay.js +200 -14
  123. package/dist/react/MotionGPUErrorOverlay.js.map +1 -0
  124. package/dist/react/Portal.d.ts +1 -0
  125. package/dist/react/Portal.d.ts.map +1 -0
  126. package/dist/react/Portal.js +18 -21
  127. package/dist/react/Portal.js.map +1 -0
  128. package/dist/react/advanced.d.ts +1 -0
  129. package/dist/react/advanced.d.ts.map +1 -0
  130. package/dist/react/advanced.js +14 -6
  131. package/dist/react/frame-context.d.ts +1 -0
  132. package/dist/react/frame-context.d.ts.map +1 -0
  133. package/dist/react/frame-context.js +88 -94
  134. package/dist/react/frame-context.js.map +1 -0
  135. package/dist/react/index.d.ts +6 -2
  136. package/dist/react/index.d.ts.map +1 -0
  137. package/dist/react/index.js +12 -9
  138. package/dist/react/motiongpu-context.d.ts +1 -0
  139. package/dist/react/motiongpu-context.d.ts.map +1 -0
  140. package/dist/react/motiongpu-context.js +18 -15
  141. package/dist/react/motiongpu-context.js.map +1 -0
  142. package/dist/react/use-motiongpu-user-context.d.ts +1 -0
  143. package/dist/react/use-motiongpu-user-context.d.ts.map +1 -0
  144. package/dist/react/use-motiongpu-user-context.js +83 -82
  145. package/dist/react/use-motiongpu-user-context.js.map +1 -0
  146. package/dist/react/use-texture.d.ts +1 -0
  147. package/dist/react/use-texture.d.ts.map +1 -0
  148. package/dist/react/use-texture.js +132 -152
  149. package/dist/react/use-texture.js.map +1 -0
  150. package/dist/svelte/FragCanvas.svelte +2 -2
  151. package/dist/svelte/FragCanvas.svelte.d.ts +3 -2
  152. package/dist/svelte/FragCanvas.svelte.d.ts.map +1 -0
  153. package/dist/svelte/MotionGPUErrorOverlay.svelte +137 -7
  154. package/dist/svelte/MotionGPUErrorOverlay.svelte.d.ts +1 -0
  155. package/dist/svelte/MotionGPUErrorOverlay.svelte.d.ts.map +1 -0
  156. package/dist/svelte/Portal.svelte.d.ts +1 -0
  157. package/dist/svelte/Portal.svelte.d.ts.map +1 -0
  158. package/dist/svelte/advanced.d.ts +1 -0
  159. package/dist/svelte/advanced.d.ts.map +1 -0
  160. package/dist/svelte/advanced.js +13 -6
  161. package/dist/svelte/frame-context.d.ts +1 -0
  162. package/dist/svelte/frame-context.d.ts.map +1 -0
  163. package/dist/svelte/frame-context.js +27 -27
  164. package/dist/svelte/frame-context.js.map +1 -0
  165. package/dist/svelte/index.d.ts +6 -2
  166. package/dist/svelte/index.d.ts.map +1 -0
  167. package/dist/svelte/index.js +12 -9
  168. package/dist/svelte/motiongpu-context.d.ts +1 -0
  169. package/dist/svelte/motiongpu-context.d.ts.map +1 -0
  170. package/dist/svelte/motiongpu-context.js +24 -21
  171. package/dist/svelte/motiongpu-context.js.map +1 -0
  172. package/dist/svelte/use-motiongpu-user-context.d.ts +1 -0
  173. package/dist/svelte/use-motiongpu-user-context.d.ts.map +1 -0
  174. package/dist/svelte/use-motiongpu-user-context.js +69 -70
  175. package/dist/svelte/use-motiongpu-user-context.js.map +1 -0
  176. package/dist/svelte/use-texture.d.ts +1 -0
  177. package/dist/svelte/use-texture.d.ts.map +1 -0
  178. package/dist/svelte/use-texture.js +125 -147
  179. package/dist/svelte/use-texture.js.map +1 -0
  180. package/package.json +12 -7
  181. package/src/lib/advanced.ts +6 -0
  182. package/src/lib/core/advanced.ts +12 -0
  183. package/src/lib/core/compute-shader.ts +326 -0
  184. package/src/lib/core/current-value.ts +64 -0
  185. package/src/lib/core/error-diagnostics.ts +236 -0
  186. package/src/lib/core/error-report.ts +535 -0
  187. package/src/lib/core/frame-registry.ts +1190 -0
  188. package/src/lib/core/index.ts +94 -0
  189. package/src/lib/core/material-preprocess.ts +295 -0
  190. package/src/lib/core/material.ts +748 -0
  191. package/src/lib/core/recompile-policy.ts +31 -0
  192. package/src/lib/core/render-graph.ts +173 -0
  193. package/src/lib/core/render-targets.ts +107 -0
  194. package/src/lib/core/renderer.ts +2161 -0
  195. package/src/lib/core/runtime-loop.ts +537 -0
  196. package/src/lib/core/scheduler-helpers.ts +136 -0
  197. package/src/lib/core/shader.ts +301 -0
  198. package/src/lib/core/storage-buffers.ts +142 -0
  199. package/src/lib/core/texture-loader.ts +482 -0
  200. package/src/lib/core/textures.ts +257 -0
  201. package/src/lib/core/types.ts +743 -0
  202. package/src/lib/core/uniforms.ts +282 -0
  203. package/src/lib/index.ts +6 -0
  204. package/src/lib/passes/BlitPass.ts +54 -0
  205. package/src/lib/passes/ComputePass.ts +136 -0
  206. package/src/lib/passes/CopyPass.ts +80 -0
  207. package/src/lib/passes/FullscreenPass.ts +173 -0
  208. package/src/lib/passes/PingPongComputePass.ts +180 -0
  209. package/src/lib/passes/ShaderPass.ts +89 -0
  210. package/src/lib/passes/index.ts +9 -0
  211. package/src/lib/react/FragCanvas.tsx +345 -0
  212. package/src/lib/react/MotionGPUErrorOverlay.tsx +524 -0
  213. package/src/lib/react/Portal.tsx +34 -0
  214. package/src/lib/react/advanced.ts +36 -0
  215. package/src/lib/react/frame-context.ts +169 -0
  216. package/src/lib/react/index.ts +68 -0
  217. package/src/lib/react/motiongpu-context.ts +88 -0
  218. package/src/lib/react/use-motiongpu-user-context.ts +186 -0
  219. package/src/lib/react/use-texture.ts +233 -0
  220. package/src/lib/svelte/FragCanvas.svelte +249 -0
  221. package/src/lib/svelte/MotionGPUErrorOverlay.svelte +512 -0
  222. package/src/lib/svelte/Portal.svelte +31 -0
  223. package/src/lib/svelte/advanced.ts +32 -0
  224. package/src/lib/svelte/frame-context.ts +87 -0
  225. package/src/lib/svelte/index.ts +68 -0
  226. package/src/lib/svelte/motiongpu-context.ts +97 -0
  227. package/src/lib/svelte/use-motiongpu-user-context.ts +145 -0
  228. package/src/lib/svelte/use-texture.ts +232 -0
@@ -0,0 +1,169 @@
1
+ import { createContext, useContext, useEffect, useRef } from 'react';
2
+ import { createCurrentWritable } from '../core/current-value.js';
3
+ import { useMotionGPU } from './motiongpu-context.js';
4
+ import type {
5
+ FrameCallback,
6
+ FrameKey,
7
+ FrameProfilingSnapshot,
8
+ FrameRegistry,
9
+ FrameRunTimings,
10
+ FrameScheduleSnapshot,
11
+ FrameStage,
12
+ FrameStageCallback,
13
+ FrameTask,
14
+ FrameTaskInvalidation,
15
+ FrameTaskInvalidationToken,
16
+ UseFrameOptions,
17
+ UseFrameResult
18
+ } from '../core/frame-registry.js';
19
+
20
+ /**
21
+ * Placeholder stage used before a frame task registration becomes available.
22
+ */
23
+ const PENDING_STAGE_KEY = Symbol('motiongpu-react-pending-stage');
24
+
25
+ /**
26
+ * React context container for the active frame registry.
27
+ */
28
+ export const FrameRegistryReactContext = createContext<FrameRegistry | null>(null);
29
+
30
+ export type {
31
+ FrameCallback,
32
+ FrameKey,
33
+ FrameProfilingSnapshot,
34
+ FrameRegistry,
35
+ FrameRunTimings,
36
+ FrameScheduleSnapshot,
37
+ FrameStage,
38
+ FrameStageCallback,
39
+ FrameTask,
40
+ FrameTaskInvalidation,
41
+ FrameTaskInvalidationToken,
42
+ UseFrameOptions,
43
+ UseFrameResult
44
+ };
45
+
46
+ /**
47
+ * Registers a frame callback using an auto-generated task key.
48
+ */
49
+ export function useFrame(callback: FrameCallback, options?: UseFrameOptions): UseFrameResult;
50
+
51
+ /**
52
+ * Registers a frame callback with an explicit task key.
53
+ */
54
+ export function useFrame(
55
+ key: FrameKey,
56
+ callback: FrameCallback,
57
+ options?: UseFrameOptions
58
+ ): UseFrameResult;
59
+
60
+ /**
61
+ * Registers a callback in the active frame registry and auto-unsubscribes on unmount.
62
+ *
63
+ * @param keyOrCallback - Task key or callback for auto-key registration.
64
+ * @param callbackOrOptions - Callback (keyed overload) or options (auto-key overload).
65
+ * @param maybeOptions - Optional registration options for keyed overload.
66
+ * Registration key/options are frozen on first render; subsequent renders do not re-register.
67
+ * @returns Registration control API with task, start/stop controls and started state.
68
+ * @throws {Error} When called outside `<FragCanvas>`.
69
+ * @throws {Error} When callback is missing in keyed overload.
70
+ */
71
+ export function useFrame(
72
+ keyOrCallback: FrameKey | FrameCallback,
73
+ callbackOrOptions?: FrameCallback | UseFrameOptions,
74
+ maybeOptions?: UseFrameOptions
75
+ ): UseFrameResult {
76
+ const registry = useContext(FrameRegistryReactContext);
77
+ if (!registry) {
78
+ throw new Error('useFrame must be used inside <FragCanvas>');
79
+ }
80
+ const motiongpu = useMotionGPU();
81
+
82
+ const resolved =
83
+ typeof keyOrCallback === 'function'
84
+ ? {
85
+ key: undefined,
86
+ callback: keyOrCallback,
87
+ options: callbackOrOptions as UseFrameOptions | undefined
88
+ }
89
+ : {
90
+ key: keyOrCallback,
91
+ callback: callbackOrOptions as FrameCallback,
92
+ options: maybeOptions
93
+ };
94
+ if (typeof resolved.callback !== 'function') {
95
+ throw new Error('useFrame requires a callback');
96
+ }
97
+
98
+ const callbackRef = useRef(resolved.callback);
99
+ callbackRef.current = resolved.callback;
100
+ const registrationConfigRef = useRef<{
101
+ key: FrameKey | undefined;
102
+ options: UseFrameOptions | undefined;
103
+ } | null>(null);
104
+ if (!registrationConfigRef.current) {
105
+ registrationConfigRef.current = {
106
+ key: resolved.key,
107
+ options: resolved.options
108
+ };
109
+ }
110
+ const registrationConfig = registrationConfigRef.current;
111
+
112
+ const registrationRef = useRef<{
113
+ task: FrameTask;
114
+ start: () => void;
115
+ stop: () => void;
116
+ started: UseFrameResult['started'];
117
+ unsubscribe: () => void;
118
+ } | null>(null);
119
+ const taskRef = useRef<FrameTask>({
120
+ key:
121
+ registrationConfig.key !== undefined
122
+ ? registrationConfig.key
123
+ : Symbol('motiongpu-react-pending-task-key'),
124
+ stage: PENDING_STAGE_KEY
125
+ });
126
+ const startedStoreRef = useRef(createCurrentWritable(false));
127
+ const startedStore = startedStoreRef.current;
128
+
129
+ useEffect(() => {
130
+ const wrappedCallback: FrameCallback = (state) => {
131
+ callbackRef.current(state);
132
+ };
133
+ const registration =
134
+ registrationConfig.key === undefined
135
+ ? registry.register(wrappedCallback, registrationConfig.options)
136
+ : registry.register(registrationConfig.key, wrappedCallback, registrationConfig.options);
137
+ registrationRef.current = registration;
138
+ taskRef.current = registration.task;
139
+ const unsubscribeStarted = registration.started.subscribe((value) => {
140
+ startedStore.set(value);
141
+ });
142
+
143
+ return () => {
144
+ unsubscribeStarted();
145
+ registration.unsubscribe();
146
+ if (registrationRef.current === registration) {
147
+ registrationRef.current = null;
148
+ }
149
+ startedStore.set(false);
150
+ };
151
+ }, [registrationConfig, registry, startedStore]);
152
+
153
+ useEffect(() => {
154
+ motiongpu.invalidate();
155
+ }, [motiongpu, resolved.callback]);
156
+
157
+ return {
158
+ get task() {
159
+ return taskRef.current;
160
+ },
161
+ start: () => {
162
+ registrationRef.current?.start();
163
+ },
164
+ stop: () => {
165
+ registrationRef.current?.stop();
166
+ },
167
+ started: startedStore
168
+ };
169
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * React adapter entrypoint for MotionGPU.
3
+ */
4
+ export { FragCanvas } from './FragCanvas.js';
5
+ export { defineMaterial } from '../core/material.js';
6
+ export {
7
+ BlitPass,
8
+ CopyPass,
9
+ ShaderPass,
10
+ ComputePass,
11
+ PingPongComputePass
12
+ } from '../passes/index.js';
13
+ export { useMotionGPU } from './motiongpu-context.js';
14
+ export { useFrame } from './frame-context.js';
15
+ export { useTexture } from './use-texture.js';
16
+ export type {
17
+ FrameInvalidationToken,
18
+ FrameState,
19
+ OutputColorSpace,
20
+ AnyPass,
21
+ ComputePassLike,
22
+ RenderPass,
23
+ RenderPassContext,
24
+ RenderPassFlags,
25
+ RenderPassInputSlot,
26
+ RenderPassOutputSlot,
27
+ RenderMode,
28
+ RenderTarget,
29
+ RenderTargetDefinition,
30
+ RenderTargetDefinitionMap,
31
+ TextureData,
32
+ TextureDefinition,
33
+ TextureDefinitionMap,
34
+ TextureUpdateMode,
35
+ TextureMap,
36
+ TextureSource,
37
+ TextureValue,
38
+ TypedUniform,
39
+ UniformMat4Value,
40
+ UniformMap,
41
+ UniformType,
42
+ UniformValue
43
+ } from '../core/types.js';
44
+ export type {
45
+ LoadedTexture,
46
+ TextureDecodeOptions,
47
+ TextureLoadOptions
48
+ } from '../core/texture-loader.js';
49
+ export type {
50
+ FragMaterial,
51
+ FragMaterialInput,
52
+ MaterialIncludes,
53
+ MaterialDefineValue,
54
+ MaterialDefines,
55
+ TypedMaterialDefineValue
56
+ } from '../core/material.js';
57
+ export type { MotionGPUContext } from './motiongpu-context.js';
58
+ export type { UseFrameOptions, UseFrameResult } from './frame-context.js';
59
+ export type { TextureUrlInput, UseTextureResult } from './use-texture.js';
60
+ export type {
61
+ StorageBufferAccess,
62
+ StorageBufferDefinition,
63
+ StorageBufferDefinitionMap,
64
+ StorageBufferType,
65
+ ComputePassContext
66
+ } from '../core/types.js';
67
+ export type { ComputePassOptions, ComputeDispatchContext } from '../passes/ComputePass.js';
68
+ export type { PingPongComputePassOptions } from '../passes/PingPongComputePass.js';
@@ -0,0 +1,88 @@
1
+ import { createContext, useContext } from 'react';
2
+ import type { RenderMode } from '../core/types.js';
3
+ import type { CurrentReadable, CurrentWritable } from '../core/current-value.js';
4
+ import type {
5
+ FrameProfilingSnapshot,
6
+ FrameRunTimings,
7
+ FrameScheduleSnapshot
8
+ } from '../core/frame-registry.js';
9
+ import type { MotionGPUScheduler as CoreMotionGPUScheduler } from '../core/scheduler-helpers.js';
10
+
11
+ /**
12
+ * React context payload exposed by `<FragCanvas>`.
13
+ */
14
+ export interface MotionGPUContext {
15
+ /**
16
+ * Underlying canvas element used by the renderer.
17
+ */
18
+ canvas: HTMLCanvasElement | undefined;
19
+ /**
20
+ * Reactive canvas pixel size.
21
+ */
22
+ size: CurrentReadable<{ width: number; height: number }>;
23
+ /**
24
+ * Device pixel ratio multiplier.
25
+ */
26
+ dpr: CurrentWritable<number>;
27
+ /**
28
+ * Max frame delta clamp passed to scheduled callbacks.
29
+ */
30
+ maxDelta: CurrentWritable<number>;
31
+ /**
32
+ * Scheduler render mode (`always`, `on-demand`, `manual`).
33
+ */
34
+ renderMode: CurrentWritable<RenderMode>;
35
+ /**
36
+ * Global toggle for automatic rendering.
37
+ */
38
+ autoRender: CurrentWritable<boolean>;
39
+ /**
40
+ * Namespaced user context store shared within the canvas subtree.
41
+ */
42
+ user: MotionGPUUserContext;
43
+ /**
44
+ * Marks current frame as invalidated.
45
+ */
46
+ invalidate: () => void;
47
+ /**
48
+ * Requests one manual frame advance.
49
+ */
50
+ advance: () => void;
51
+ /**
52
+ * Public scheduler API.
53
+ */
54
+ scheduler: MotionGPUScheduler;
55
+ }
56
+
57
+ export type MotionGPUScheduler = CoreMotionGPUScheduler;
58
+ export type { FrameProfilingSnapshot, FrameRunTimings, FrameScheduleSnapshot };
59
+
60
+ /**
61
+ * Namespace identifier for user-owned context entries.
62
+ */
63
+ export type MotionGPUUserNamespace = string | symbol;
64
+
65
+ /**
66
+ * Shared user context store exposed by `FragCanvas`.
67
+ */
68
+ export type MotionGPUUserContext = CurrentWritable<Record<MotionGPUUserNamespace, unknown>>;
69
+
70
+ /**
71
+ * Internal React context container.
72
+ */
73
+ export const MotionGPUReactContext = createContext<MotionGPUContext | null>(null);
74
+
75
+ /**
76
+ * Returns active MotionGPU runtime context.
77
+ *
78
+ * @returns Active context.
79
+ * @throws {Error} When called outside `<FragCanvas>`.
80
+ */
81
+ export function useMotionGPU(): MotionGPUContext {
82
+ const context = useContext(MotionGPUReactContext);
83
+ if (!context) {
84
+ throw new Error('useMotionGPU must be used inside <FragCanvas>');
85
+ }
86
+
87
+ return context;
88
+ }
@@ -0,0 +1,186 @@
1
+ import { useCallback, useMemo } from 'react';
2
+ import type { CurrentReadable } from '../core/current-value.js';
3
+ import {
4
+ useMotionGPU,
5
+ type MotionGPUUserContext,
6
+ type MotionGPUUserNamespace
7
+ } from './motiongpu-context.js';
8
+
9
+ /**
10
+ * Internal shape of the user context store.
11
+ */
12
+ type UserContextStore = Record<MotionGPUUserNamespace, unknown>;
13
+
14
+ /**
15
+ * Object-like context payload used by merge semantics.
16
+ */
17
+ type UserContextEntry = Record<string, unknown>;
18
+
19
+ /**
20
+ * Controls how a namespaced user context value behaves when already present.
21
+ */
22
+ export interface SetMotionGPUUserContextOptions {
23
+ /**
24
+ * Conflict strategy when namespace already exists:
25
+ * - `skip`: keep current value
26
+ * - `replace`: replace current value
27
+ * - `merge`: shallow merge object values, fallback to replace otherwise
28
+ *
29
+ * @default 'skip'
30
+ */
31
+ existing?: 'merge' | 'replace' | 'skip';
32
+ /**
33
+ * How function inputs should be interpreted:
34
+ * - `factory`: call function and store its return value
35
+ * - `value`: store function itself
36
+ *
37
+ * @default 'factory'
38
+ */
39
+ functionValue?: 'factory' | 'value';
40
+ }
41
+
42
+ /**
43
+ * Checks whether a value is a non-array object suitable for shallow merge.
44
+ */
45
+ function isObjectEntry(value: unknown): value is UserContextEntry {
46
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
47
+ }
48
+
49
+ /**
50
+ * Sets a namespaced user context value in the provided user store.
51
+ *
52
+ * Returns the effective value stored under the namespace.
53
+ */
54
+ function setMotionGPUUserContextInStore<UCT = unknown>(
55
+ userStore: MotionGPUUserContext,
56
+ namespace: MotionGPUUserNamespace,
57
+ value: UCT | (() => UCT),
58
+ options?: SetMotionGPUUserContextOptions
59
+ ): UCT | undefined {
60
+ const mode = options?.existing ?? 'skip';
61
+ const functionValueMode = options?.functionValue ?? 'factory';
62
+ let resolvedValue: UCT | undefined;
63
+
64
+ userStore.update((context) => {
65
+ const hasExisting = namespace in context;
66
+ if (hasExisting && mode === 'skip') {
67
+ resolvedValue = context[namespace] as UCT | undefined;
68
+ return context;
69
+ }
70
+
71
+ const nextValue =
72
+ typeof value === 'function' && functionValueMode === 'factory'
73
+ ? (value as () => UCT)()
74
+ : (value as UCT);
75
+ if (hasExisting && mode === 'merge') {
76
+ const currentValue = context[namespace];
77
+ if (isObjectEntry(currentValue) && isObjectEntry(nextValue)) {
78
+ resolvedValue = {
79
+ ...currentValue,
80
+ ...nextValue
81
+ } as UCT;
82
+ return {
83
+ ...context,
84
+ [namespace]: resolvedValue
85
+ };
86
+ }
87
+ }
88
+
89
+ resolvedValue = nextValue;
90
+ return {
91
+ ...context,
92
+ [namespace]: nextValue
93
+ };
94
+ });
95
+
96
+ return resolvedValue;
97
+ }
98
+
99
+ /**
100
+ * Returns a read-only view of the entire motiongpu user context store.
101
+ */
102
+ export function useMotionGPUUserContext<
103
+ UC extends UserContextStore = UserContextStore
104
+ >(): CurrentReadable<UC>;
105
+
106
+ /**
107
+ * Reads a namespaced user context value as a reactive readable store.
108
+ */
109
+ export function useMotionGPUUserContext<
110
+ UC extends UserContextStore = UserContextStore,
111
+ K extends keyof UC & MotionGPUUserNamespace = keyof UC & MotionGPUUserNamespace
112
+ >(namespace: K): CurrentReadable<UC[K] | undefined>;
113
+
114
+ /**
115
+ * Read-only user context hook:
116
+ * - no args: returns full user context store
117
+ * - namespace: returns namespaced store view
118
+ *
119
+ * @param namespace - Optional namespace key.
120
+ */
121
+ export function useMotionGPUUserContext<
122
+ UC extends UserContextStore = UserContextStore,
123
+ K extends keyof UC & MotionGPUUserNamespace = keyof UC & MotionGPUUserNamespace
124
+ >(namespace?: K): CurrentReadable<UC> | CurrentReadable<UC[K] | undefined> {
125
+ const userStore = useMotionGPU().user;
126
+ const allStore = useMemo<CurrentReadable<UC>>(
127
+ () => ({
128
+ get current() {
129
+ return userStore.current as UC;
130
+ },
131
+ subscribe(run) {
132
+ return userStore.subscribe((context) => run(context as UC));
133
+ }
134
+ }),
135
+ [userStore]
136
+ );
137
+ const scopedStore = useMemo<CurrentReadable<UC[K] | undefined>>(
138
+ () => ({
139
+ get current() {
140
+ return userStore.current[namespace as MotionGPUUserNamespace] as UC[K] | undefined;
141
+ },
142
+ subscribe(run) {
143
+ return userStore.subscribe((context) =>
144
+ run(context[namespace as MotionGPUUserNamespace] as UC[K] | undefined)
145
+ );
146
+ }
147
+ }),
148
+ [namespace, userStore]
149
+ );
150
+
151
+ if (namespace === undefined) {
152
+ return allStore;
153
+ }
154
+
155
+ return scopedStore;
156
+ }
157
+
158
+ /**
159
+ * Returns a stable setter bound to the active MotionGPU user context store.
160
+ *
161
+ * @returns Setter function that preserves namespace write semantics.
162
+ */
163
+ export function useSetMotionGPUUserContext() {
164
+ const userStore = useMotionGPU().user;
165
+ return useCallback(
166
+ <UCT = unknown>(
167
+ namespace: MotionGPUUserNamespace,
168
+ value: UCT | (() => UCT),
169
+ options?: SetMotionGPUUserContextOptions
170
+ ): UCT | undefined => setMotionGPUUserContextInStore(userStore, namespace, value, options),
171
+ [userStore]
172
+ );
173
+ }
174
+
175
+ /**
176
+ * Sets a namespaced user context value with explicit write semantics.
177
+ *
178
+ * Returns the effective value stored under the namespace.
179
+ */
180
+ export function setMotionGPUUserContext<UCT = unknown>(
181
+ namespace: MotionGPUUserNamespace,
182
+ value: UCT | (() => UCT),
183
+ options?: SetMotionGPUUserContextOptions
184
+ ): UCT | undefined {
185
+ return setMotionGPUUserContextInStore(useMotionGPU().user, namespace, value, options);
186
+ }