@lovo/matter-react 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.
package/dist/index.d.cts CHANGED
@@ -2,47 +2,28 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, CSSProperties, DependencyList } from 'react';
3
3
  import { ShaderNodeObject } from 'three/tsl';
4
4
  import { Node, MeshBasicNodeMaterial } from 'three/webgpu';
5
- import { Vec2, CursorInputOptions, GpuRenderer, FrameScheduler } from '@lovo/matter';
5
+ import { Vector2, CursorInputOptions, GpuRenderer, FrameScheduler } from '@lovo/matter';
6
6
  import { Scene, Camera } from 'three';
7
7
 
8
8
  interface FallbackBoundaryProps {
9
- /** Rendered until WebGPU/WebGL is available on the client. */
10
9
  fallback?: ReactNode;
11
10
  children: ReactNode;
12
11
  }
13
- /**
14
- * Render `fallback` until the component mounts on the client. Gates the
15
- * children behind client-only mounting so SSR/no-WebGPU users see a
16
- * sensible static placeholder rather than a flash of nothing.
17
- */
18
12
  declare function FallbackBoundary({ fallback, children }: FallbackBoundaryProps): react_jsx_runtime.JSX.Element;
19
13
 
20
- type MonitorAnchor = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
14
+ type ShaderMonitorAnchor = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
21
15
  interface ShaderMonitorProps {
22
- anchor?: MonitorAnchor;
16
+ anchor?: ShaderMonitorAnchor;
23
17
  }
24
- /**
25
- * Dev-only overlay that displays the current scene's FPS, tick count, and
26
- * paused/idle state. Reads from the surrounding `<ShaderScene>` via context
27
- * and subscribes to its scheduler. Renders nothing useful if mounted outside
28
- * a scene.
29
- */
30
18
  declare function ShaderMonitor({ anchor }: ShaderMonitorProps): react_jsx_runtime.JSX.Element;
31
19
 
32
20
  interface ShaderSceneProps {
33
21
  children?: ReactNode;
34
- /** Rendered server-side and during WebGPU init. Default: empty. */
35
22
  fallback?: ReactNode;
36
23
  className?: string;
37
24
  style?: CSSProperties;
38
- /** Cap on devicePixelRatio. Default: 2. */
39
25
  maxDPR?: number;
40
26
  }
41
- /**
42
- * Owns a canvas, a Three.js renderer (WebGPU + WebGL2 fallback), an
43
- * orthographic camera covering the canvas, an empty Scene, and a
44
- * FrameScheduler. Children consume these via useShaderContext().
45
- */
46
27
  declare function ShaderScene(props: ShaderSceneProps): react_jsx_runtime.JSX.Element;
47
28
 
48
29
  interface AnimatableSignal<T> {
@@ -50,142 +31,46 @@ interface AnimatableSignal<T> {
50
31
  on(event: 'change', cb: (value: T) => void): () => void;
51
32
  }
52
33
  type AnimatableProp<T> = T | AnimatableSignal<T>;
53
- /**
54
- * Bind an AnimatableProp<T> to a TSL uniform. Plain values create a
55
- * static uniform that updates only when the prop changes (React render
56
- * path). Signals subscribe via .on('change') and write into the uniform
57
- * imperatively without re-rendering.
58
- *
59
- * Returns a chainable TSL node with `.value: T` exposed for callers that
60
- * need to read the current uniform value imperatively (e.g., to compute
61
- * derived JS-side math). The runtime object is a UniformNode<T>; we
62
- * present it as `ShaderNodeObject<Node> & { value: T }` because TSL's
63
- * generic invariance blocks the more precise type from flowing through
64
- * downstream consumers like OverlayTransform.
65
- */
66
34
  declare function useAnimatableUniform<T>(value: AnimatableProp<T>): ShaderNodeObject<Node> & {
67
35
  value: T;
68
36
  };
69
37
 
70
38
  interface CursorSignal {
71
- /** Current smoothed cursor position (Vec2 in 0..1 viewport space). */
72
- get(): Vec2;
73
- /** Subscribe to change events. Returns unsubscribe. */
74
- on(event: 'change', cb: (value: Vec2) => void): () => void;
39
+ get(): Vector2;
40
+ on(event: 'change', cb: (value: Vector2) => void): () => void;
75
41
  }
76
- /**
77
- * React wrapper for CursorInput. Auto-attaches to the parent <ShaderScene>'s
78
- * scheduler if available; otherwise creates a free-running rAF tick.
79
- *
80
- * Lifecycle is in a single effect so React 19 Strict Mode's intentional
81
- * mount→unmount→mount cycle creates a *fresh* CursorInput per real mount
82
- * instead of disposing a long-lived one (which would silently break the
83
- * window mousemove listener and the smoothing tick).
84
- */
85
42
  declare function useCursor(opts?: CursorInputOptions): CursorSignal;
86
43
 
87
- type OverlayTransform = (input: ShaderNodeObject<Node>) => ShaderNodeObject<Node>;
44
+ type PostProcessTransform = (input: ShaderNodeObject<Node>) => ShaderNodeObject<Node>;
88
45
  interface ShaderContextValue {
89
46
  renderer: GpuRenderer;
90
47
  scene: Scene;
91
48
  camera: Camera;
92
49
  scheduler: FrameScheduler;
93
- registerOverlay: (transform: OverlayTransform) => () => void;
50
+ registerOverlay: (transform: PostProcessTransform) => () => void;
94
51
  }
95
52
 
96
- /**
97
- * Read the shader scene context. Returns null when called outside a
98
- * <ShaderScene>; useShaderMaterial and similar hooks check this.
99
- */
100
53
  declare function useShaderContext(): ShaderContextValue | null;
101
54
 
102
- /**
103
- * Register a TSL transform as an overlay pass on the parent <ShaderScene>.
104
- *
105
- * The transform takes the "color so far" — base scene + any earlier
106
- * overlays as a TSL vec4 node — and returns a modified vec4. Registration
107
- * happens on mount; unregistration on unmount. The hook re-registers
108
- * whenever any value in `deps` changes (useEffect semantics): use this
109
- * for structural changes (e.g., a `mode: 'additive' | 'subtractive'`
110
- * toggle) that swap the transform function itself. Uniforms captured
111
- * inside the transform mutate in place, so uniform value changes do
112
- * NOT need to be in deps.
113
- *
114
- * When called outside a <ShaderScene> provider, this hook is a no-op.
115
- * Matches the existing useShaderContext convention.
116
- */
117
- declare function useOverlayPass(transform: OverlayTransform, deps: DependencyList): void;
55
+ declare function usePostProcessPass(transform: PostProcessTransform, deps: DependencyList): void;
118
56
 
119
57
  type ResizeValue = readonly [width: number, height: number, dpr: number];
120
58
  interface ResizeSignal {
121
- /** Current size in CSS pixels + devicePixelRatio. */
122
59
  get(): ResizeValue;
123
60
  on(event: 'change', cb: (value: ResizeValue) => void): () => void;
124
61
  }
125
- /**
126
- * Track the parent <ShaderScene>'s canvas size + DPR. Exposes an AnimatableSignal
127
- * that components can pass into a TSL uniform to make pixel-aware effects
128
- * (e.g., DotField's pixel-spacing math).
129
- *
130
- * Strict-Mode-safe: lifecycle is in one effect, so React 19's intentional
131
- * mount→unmount→mount cycle creates a fresh ResizeObserver per real mount
132
- * (CLAUDE.md gotcha #14).
133
- *
134
- * Falls back to the stub signal until the parent context is ready.
135
- */
136
62
  declare function useResize(): ResizeSignal;
137
63
 
138
64
  type ScrollValue = readonly [scrollY: number, progress: number];
139
65
  interface ScrollSignal {
140
- /** Current scroll Y (px) and normalized progress in [0,1]. */
141
66
  get(): ScrollValue;
142
67
  on(event: 'change', cb: (value: ScrollValue) => void): () => void;
143
68
  }
144
- /**
145
- * Track window scroll position. Exposes an AnimatableSignal of `[scrollY, progress]`
146
- * where `progress` is `scrollY / max(documentHeight - innerHeight, 1)` clamped
147
- * to [0, 1]. Listener is rAF-throttled and `passive: true` so it never blocks
148
- * scrolling.
149
- *
150
- * No v1 Tier 1 component consumes this hook; it ships so users can pass
151
- * `inputs={{ scroll: useScroll() }}` to any Matter component.
152
- *
153
- * Strict-Mode-safe: lifecycle is in one effect, so React 19's intentional
154
- * mount→unmount→mount cycle in dev creates a fresh listener pair per real
155
- * mount and tears down cleanly on each pseudo-unmount (CLAUDE.md gotcha #14).
156
- *
157
- * **Known limitation (v1):** `progress` is computed against whichever
158
- * `documentHeight` was current when the last scroll fired. If the page grows
159
- * after mount (async content, font load reflow, expanding panels) without
160
- * the user scrolling, the denominator goes stale. A future ResizeObserver/
161
- * MutationObserver pass would close the gap; deferred until a v1 component
162
- * consumes scroll input.
163
- */
164
69
  declare function useScroll(): ScrollSignal;
165
70
 
166
- /** A TSL fragment that produces a color. Accept any Node or TSL-wrapped node. */
167
71
  type ColorTSL = Node | ShaderNodeObject<Node>;
168
- /**
169
- * Bind a TSL color expression to a NodeMaterial. Returns the material;
170
- * caller is responsible for adding it to a mesh and disposing when done.
171
- *
172
- * The TSL fragment is computed once via `useMemo` and re-applied if the
173
- * factory function changes. For dynamic uniforms, mutate `.value` on the
174
- * uniform nodes — don't recreate the TSL fragment per render.
175
- */
176
72
  declare function useShaderMaterial(build: () => ColorTSL): MeshBasicNodeMaterial;
177
73
 
178
- /**
179
- * Opt a component out of the rAF loop while it has no dynamic uniforms.
180
- *
181
- * When `hint` is true, the scheduler runs one final flush tick (so any
182
- * uniform changes since the last frame are rendered) and then halts the
183
- * rAF loop until either `hint` becomes false or another component in the
184
- * same scene calls `scheduler.requestRender()`.
185
- *
186
- * Use for components whose animation is fully derived from props that don't
187
- * include `time`, e.g. `<LinearGradient speed={0}>` with no `interactive`.
188
- */
189
- declare function useStaticHint(hint: boolean): void;
74
+ declare function useStaticSceneHint(isStatic: boolean): void;
190
75
 
191
- export { type AnimatableProp, type AnimatableSignal, type CursorSignal, FallbackBoundary, type FallbackBoundaryProps, type MonitorAnchor, type OverlayTransform, type ResizeSignal, type ResizeValue, type ScrollSignal, type ScrollValue, type ShaderContextValue, ShaderMonitor, type ShaderMonitorProps, ShaderScene, type ShaderSceneProps, useAnimatableUniform, useCursor, useOverlayPass, useResize, useScroll, useShaderContext, useShaderMaterial, useStaticHint };
76
+ export { type AnimatableProp, type AnimatableSignal, type CursorSignal, FallbackBoundary, type FallbackBoundaryProps, type PostProcessTransform, type ResizeSignal, type ResizeValue, type ScrollSignal, type ScrollValue, type ShaderContextValue, ShaderMonitor, type ShaderMonitorAnchor, type ShaderMonitorProps, ShaderScene, type ShaderSceneProps, useAnimatableUniform, useCursor, usePostProcessPass, useResize, useScroll, useShaderContext, useShaderMaterial, useStaticSceneHint };
package/dist/index.d.ts CHANGED
@@ -2,47 +2,28 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, CSSProperties, DependencyList } from 'react';
3
3
  import { ShaderNodeObject } from 'three/tsl';
4
4
  import { Node, MeshBasicNodeMaterial } from 'three/webgpu';
5
- import { Vec2, CursorInputOptions, GpuRenderer, FrameScheduler } from '@lovo/matter';
5
+ import { Vector2, CursorInputOptions, GpuRenderer, FrameScheduler } from '@lovo/matter';
6
6
  import { Scene, Camera } from 'three';
7
7
 
8
8
  interface FallbackBoundaryProps {
9
- /** Rendered until WebGPU/WebGL is available on the client. */
10
9
  fallback?: ReactNode;
11
10
  children: ReactNode;
12
11
  }
13
- /**
14
- * Render `fallback` until the component mounts on the client. Gates the
15
- * children behind client-only mounting so SSR/no-WebGPU users see a
16
- * sensible static placeholder rather than a flash of nothing.
17
- */
18
12
  declare function FallbackBoundary({ fallback, children }: FallbackBoundaryProps): react_jsx_runtime.JSX.Element;
19
13
 
20
- type MonitorAnchor = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
14
+ type ShaderMonitorAnchor = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
21
15
  interface ShaderMonitorProps {
22
- anchor?: MonitorAnchor;
16
+ anchor?: ShaderMonitorAnchor;
23
17
  }
24
- /**
25
- * Dev-only overlay that displays the current scene's FPS, tick count, and
26
- * paused/idle state. Reads from the surrounding `<ShaderScene>` via context
27
- * and subscribes to its scheduler. Renders nothing useful if mounted outside
28
- * a scene.
29
- */
30
18
  declare function ShaderMonitor({ anchor }: ShaderMonitorProps): react_jsx_runtime.JSX.Element;
31
19
 
32
20
  interface ShaderSceneProps {
33
21
  children?: ReactNode;
34
- /** Rendered server-side and during WebGPU init. Default: empty. */
35
22
  fallback?: ReactNode;
36
23
  className?: string;
37
24
  style?: CSSProperties;
38
- /** Cap on devicePixelRatio. Default: 2. */
39
25
  maxDPR?: number;
40
26
  }
41
- /**
42
- * Owns a canvas, a Three.js renderer (WebGPU + WebGL2 fallback), an
43
- * orthographic camera covering the canvas, an empty Scene, and a
44
- * FrameScheduler. Children consume these via useShaderContext().
45
- */
46
27
  declare function ShaderScene(props: ShaderSceneProps): react_jsx_runtime.JSX.Element;
47
28
 
48
29
  interface AnimatableSignal<T> {
@@ -50,142 +31,46 @@ interface AnimatableSignal<T> {
50
31
  on(event: 'change', cb: (value: T) => void): () => void;
51
32
  }
52
33
  type AnimatableProp<T> = T | AnimatableSignal<T>;
53
- /**
54
- * Bind an AnimatableProp<T> to a TSL uniform. Plain values create a
55
- * static uniform that updates only when the prop changes (React render
56
- * path). Signals subscribe via .on('change') and write into the uniform
57
- * imperatively without re-rendering.
58
- *
59
- * Returns a chainable TSL node with `.value: T` exposed for callers that
60
- * need to read the current uniform value imperatively (e.g., to compute
61
- * derived JS-side math). The runtime object is a UniformNode<T>; we
62
- * present it as `ShaderNodeObject<Node> & { value: T }` because TSL's
63
- * generic invariance blocks the more precise type from flowing through
64
- * downstream consumers like OverlayTransform.
65
- */
66
34
  declare function useAnimatableUniform<T>(value: AnimatableProp<T>): ShaderNodeObject<Node> & {
67
35
  value: T;
68
36
  };
69
37
 
70
38
  interface CursorSignal {
71
- /** Current smoothed cursor position (Vec2 in 0..1 viewport space). */
72
- get(): Vec2;
73
- /** Subscribe to change events. Returns unsubscribe. */
74
- on(event: 'change', cb: (value: Vec2) => void): () => void;
39
+ get(): Vector2;
40
+ on(event: 'change', cb: (value: Vector2) => void): () => void;
75
41
  }
76
- /**
77
- * React wrapper for CursorInput. Auto-attaches to the parent <ShaderScene>'s
78
- * scheduler if available; otherwise creates a free-running rAF tick.
79
- *
80
- * Lifecycle is in a single effect so React 19 Strict Mode's intentional
81
- * mount→unmount→mount cycle creates a *fresh* CursorInput per real mount
82
- * instead of disposing a long-lived one (which would silently break the
83
- * window mousemove listener and the smoothing tick).
84
- */
85
42
  declare function useCursor(opts?: CursorInputOptions): CursorSignal;
86
43
 
87
- type OverlayTransform = (input: ShaderNodeObject<Node>) => ShaderNodeObject<Node>;
44
+ type PostProcessTransform = (input: ShaderNodeObject<Node>) => ShaderNodeObject<Node>;
88
45
  interface ShaderContextValue {
89
46
  renderer: GpuRenderer;
90
47
  scene: Scene;
91
48
  camera: Camera;
92
49
  scheduler: FrameScheduler;
93
- registerOverlay: (transform: OverlayTransform) => () => void;
50
+ registerOverlay: (transform: PostProcessTransform) => () => void;
94
51
  }
95
52
 
96
- /**
97
- * Read the shader scene context. Returns null when called outside a
98
- * <ShaderScene>; useShaderMaterial and similar hooks check this.
99
- */
100
53
  declare function useShaderContext(): ShaderContextValue | null;
101
54
 
102
- /**
103
- * Register a TSL transform as an overlay pass on the parent <ShaderScene>.
104
- *
105
- * The transform takes the "color so far" — base scene + any earlier
106
- * overlays as a TSL vec4 node — and returns a modified vec4. Registration
107
- * happens on mount; unregistration on unmount. The hook re-registers
108
- * whenever any value in `deps` changes (useEffect semantics): use this
109
- * for structural changes (e.g., a `mode: 'additive' | 'subtractive'`
110
- * toggle) that swap the transform function itself. Uniforms captured
111
- * inside the transform mutate in place, so uniform value changes do
112
- * NOT need to be in deps.
113
- *
114
- * When called outside a <ShaderScene> provider, this hook is a no-op.
115
- * Matches the existing useShaderContext convention.
116
- */
117
- declare function useOverlayPass(transform: OverlayTransform, deps: DependencyList): void;
55
+ declare function usePostProcessPass(transform: PostProcessTransform, deps: DependencyList): void;
118
56
 
119
57
  type ResizeValue = readonly [width: number, height: number, dpr: number];
120
58
  interface ResizeSignal {
121
- /** Current size in CSS pixels + devicePixelRatio. */
122
59
  get(): ResizeValue;
123
60
  on(event: 'change', cb: (value: ResizeValue) => void): () => void;
124
61
  }
125
- /**
126
- * Track the parent <ShaderScene>'s canvas size + DPR. Exposes an AnimatableSignal
127
- * that components can pass into a TSL uniform to make pixel-aware effects
128
- * (e.g., DotField's pixel-spacing math).
129
- *
130
- * Strict-Mode-safe: lifecycle is in one effect, so React 19's intentional
131
- * mount→unmount→mount cycle creates a fresh ResizeObserver per real mount
132
- * (CLAUDE.md gotcha #14).
133
- *
134
- * Falls back to the stub signal until the parent context is ready.
135
- */
136
62
  declare function useResize(): ResizeSignal;
137
63
 
138
64
  type ScrollValue = readonly [scrollY: number, progress: number];
139
65
  interface ScrollSignal {
140
- /** Current scroll Y (px) and normalized progress in [0,1]. */
141
66
  get(): ScrollValue;
142
67
  on(event: 'change', cb: (value: ScrollValue) => void): () => void;
143
68
  }
144
- /**
145
- * Track window scroll position. Exposes an AnimatableSignal of `[scrollY, progress]`
146
- * where `progress` is `scrollY / max(documentHeight - innerHeight, 1)` clamped
147
- * to [0, 1]. Listener is rAF-throttled and `passive: true` so it never blocks
148
- * scrolling.
149
- *
150
- * No v1 Tier 1 component consumes this hook; it ships so users can pass
151
- * `inputs={{ scroll: useScroll() }}` to any Matter component.
152
- *
153
- * Strict-Mode-safe: lifecycle is in one effect, so React 19's intentional
154
- * mount→unmount→mount cycle in dev creates a fresh listener pair per real
155
- * mount and tears down cleanly on each pseudo-unmount (CLAUDE.md gotcha #14).
156
- *
157
- * **Known limitation (v1):** `progress` is computed against whichever
158
- * `documentHeight` was current when the last scroll fired. If the page grows
159
- * after mount (async content, font load reflow, expanding panels) without
160
- * the user scrolling, the denominator goes stale. A future ResizeObserver/
161
- * MutationObserver pass would close the gap; deferred until a v1 component
162
- * consumes scroll input.
163
- */
164
69
  declare function useScroll(): ScrollSignal;
165
70
 
166
- /** A TSL fragment that produces a color. Accept any Node or TSL-wrapped node. */
167
71
  type ColorTSL = Node | ShaderNodeObject<Node>;
168
- /**
169
- * Bind a TSL color expression to a NodeMaterial. Returns the material;
170
- * caller is responsible for adding it to a mesh and disposing when done.
171
- *
172
- * The TSL fragment is computed once via `useMemo` and re-applied if the
173
- * factory function changes. For dynamic uniforms, mutate `.value` on the
174
- * uniform nodes — don't recreate the TSL fragment per render.
175
- */
176
72
  declare function useShaderMaterial(build: () => ColorTSL): MeshBasicNodeMaterial;
177
73
 
178
- /**
179
- * Opt a component out of the rAF loop while it has no dynamic uniforms.
180
- *
181
- * When `hint` is true, the scheduler runs one final flush tick (so any
182
- * uniform changes since the last frame are rendered) and then halts the
183
- * rAF loop until either `hint` becomes false or another component in the
184
- * same scene calls `scheduler.requestRender()`.
185
- *
186
- * Use for components whose animation is fully derived from props that don't
187
- * include `time`, e.g. `<LinearGradient speed={0}>` with no `interactive`.
188
- */
189
- declare function useStaticHint(hint: boolean): void;
74
+ declare function useStaticSceneHint(isStatic: boolean): void;
190
75
 
191
- export { type AnimatableProp, type AnimatableSignal, type CursorSignal, FallbackBoundary, type FallbackBoundaryProps, type MonitorAnchor, type OverlayTransform, type ResizeSignal, type ResizeValue, type ScrollSignal, type ScrollValue, type ShaderContextValue, ShaderMonitor, type ShaderMonitorProps, ShaderScene, type ShaderSceneProps, useAnimatableUniform, useCursor, useOverlayPass, useResize, useScroll, useShaderContext, useShaderMaterial, useStaticHint };
76
+ export { type AnimatableProp, type AnimatableSignal, type CursorSignal, FallbackBoundary, type FallbackBoundaryProps, type PostProcessTransform, type ResizeSignal, type ResizeValue, type ScrollSignal, type ScrollValue, type ShaderContextValue, ShaderMonitor, type ShaderMonitorAnchor, type ShaderMonitorProps, ShaderScene, type ShaderSceneProps, useAnimatableUniform, useCursor, usePostProcessPass, useResize, useScroll, useShaderContext, useShaderMaterial, useStaticSceneHint };