@lovo/matter 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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @lovo/matter
2
2
 
3
+ ## 0.4.1
4
+
5
+ ### Patch Changes
6
+
7
+ - b4ecdda: Reorganize engine source into kebab-case module folders under `inputs/`, `primitives/`, and `runtime/` (matching `matter-react` and `registry` layout). No public API changes.
8
+
9
+ ## 0.4.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 1c69220: Rename public API symbols to domain-accurate names.
14
+
15
+ New primary names: `FrameScheduler`, `GpuRenderer`, `GpuBackend` (`@lovo/matter`); `ShaderScene`, `ShaderSceneProps`, `ShaderContext`, `ShaderContextValue`, `useShaderContext`, `ShaderMonitor`, `ShaderMonitorProps`, `AnimatableSignal` (`@lovo/matter-react`).
16
+
17
+ Old names (`MatterScheduler`, `MatterRenderer`, `MatterBackend`, `MatterScene`, `MatterSceneProps`, `MatterContext`, `MatterContextValue`, `useMatterContext`, `MatterMonitor`, `MatterMonitorProps`, `MatterSignal`, `MatterBackend`) are deprecated with `@deprecated` JSDoc and continue to work. They will be removed no earlier than 0.5.0.
18
+
19
+ **Migration:** Replace old names with new in your imports and JSX. A one-pass find-and-replace is sufficient — no behavioral changes.
20
+
3
21
  ## 0.3.0
4
22
 
5
23
  ### Minor Changes
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Framework-agnostic engine for **Matter** — React shader components on WebGPU + Three.js TSL.
4
4
 
5
- This package contains the TSL primitives, the renderer, and the scheduler. It has no React dependency. If you're using React, install [`@lovo/matter-react`](https://www.npmjs.com/package/@lovo/matter-react) alongside this package — it adds React-friendly wrappers (a shared `<MatterScene>`, input hooks, and `@react-three/fiber` integration) on top of this engine.
5
+ This package contains the TSL primitives, the renderer, and the scheduler. It has no React dependency. If you're using React, install [`@lovo/matter-react`](https://www.npmjs.com/package/@lovo/matter-react) alongside this package — it adds React-friendly wrappers (a shared `<ShaderScene>`, input hooks, and `@react-three/fiber` integration) on top of this engine.
6
6
 
7
7
  ## Install
8
8
 
@@ -39,6 +39,10 @@ For polished drop-in components like `<LinearGradient>` and `<Aurora>`, install
39
39
 
40
40
  Full docs and live demos: <https://github.com/lovo-hq/matter>
41
41
 
42
+ ## Migration from 0.3.x
43
+
44
+ `MatterScheduler`, `MatterRenderer`, and `MatterBackend` have been renamed to `FrameScheduler`, `GpuRenderer`, and `GpuBackend`. The old names are deprecated and still work — remove them at your leisure before 0.5.0.
45
+
42
46
  ## License
43
47
 
44
48
  MIT — see [LICENSE](./LICENSE).
package/dist/index.cjs CHANGED
@@ -21,7 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  CursorInput: () => CursorInput,
24
- MatterScheduler: () => MatterScheduler,
24
+ FrameScheduler: () => FrameScheduler,
25
25
  colorRamp: () => colorRamp,
26
26
  createIntersectionWatcher: () => createIntersectionWatcher,
27
27
  createReducedMotionWatcher: () => createReducedMotionWatcher,
@@ -42,9 +42,9 @@ __export(index_exports, {
42
42
  });
43
43
  module.exports = __toCommonJS(index_exports);
44
44
 
45
- // src/runtime/createRenderer.ts
46
- var import_webgpu = require("three/webgpu");
45
+ // src/runtime/create-renderer/create-renderer.ts
47
46
  var import_three = require("three");
47
+ var import_webgpu = require("three/webgpu");
48
48
  async function createRenderer(canvas, opts = {}) {
49
49
  const {
50
50
  antialias = true,
@@ -70,7 +70,8 @@ async function createRenderer(canvas, opts = {}) {
70
70
  }
71
71
  };
72
72
  resize();
73
- const backend = forceWebGL || three.backend?.isWebGLBackend ? "webgl2" : "webgpu";
73
+ const isWebGL = "isWebGLBackend" in three.backend && three.backend.isWebGLBackend === true;
74
+ const backend = forceWebGL || isWebGL ? "webgl2" : "webgpu";
74
75
  return {
75
76
  three,
76
77
  backend,
@@ -79,105 +80,7 @@ async function createRenderer(canvas, opts = {}) {
79
80
  };
80
81
  }
81
82
 
82
- // src/runtime/MatterScheduler.ts
83
- var MatterScheduler = class {
84
- clients = /* @__PURE__ */ new Set();
85
- rafId = null;
86
- running = false;
87
- paused = false;
88
- idle = false;
89
- flushPending = false;
90
- startedAt = 0;
91
- lastTickAt = 0;
92
- /** Activate the scheduler. The rAF loop starts on the first client added. */
93
- start() {
94
- this.running = true;
95
- this.paused = false;
96
- this.maybeQueue();
97
- }
98
- /** Halt the rAF loop entirely. Use dispose() for permanent teardown. */
99
- stop() {
100
- this.running = false;
101
- this.cancel();
102
- }
103
- /** Temporarily skip ticks without losing client registrations. */
104
- pause() {
105
- this.paused = true;
106
- }
107
- /** Resume after pause(). */
108
- resume() {
109
- this.paused = false;
110
- if (this.running) this.maybeQueue();
111
- }
112
- /** Register a client to be called every frame. */
113
- add(client) {
114
- this.clients.add(client);
115
- if (this.running) this.maybeQueue();
116
- }
117
- /** Unregister a client. */
118
- remove(client) {
119
- this.clients.delete(client);
120
- }
121
- /** Permanent teardown: stop the loop and drop all clients. */
122
- dispose() {
123
- this.stop();
124
- this.clients.clear();
125
- }
126
- /**
127
- * Mark the scheduler idle. The next tick still fires (a final flush so
128
- * uniform changes that triggered the idle state are rendered), then the
129
- * rAF loop halts. Use `requestRender()` or `setIdle(false)` to wake.
130
- */
131
- setIdle(idle) {
132
- if (this.idle === idle) return;
133
- this.idle = idle;
134
- if (idle) {
135
- this.flushPending = true;
136
- this.maybeQueue();
137
- } else {
138
- this.flushPending = false;
139
- this.maybeQueue();
140
- }
141
- }
142
- /** Force a single tick while idle. Useful for prop-change invalidation. */
143
- requestRender() {
144
- if (!this.idle) return;
145
- this.flushPending = true;
146
- this.maybeQueue();
147
- }
148
- maybeQueue() {
149
- if (this.rafId !== null) return;
150
- if (!this.running) return;
151
- if (this.clients.size === 0) return;
152
- if (this.idle && !this.flushPending) return;
153
- this.rafId = requestAnimationFrame(this.frame);
154
- }
155
- cancel() {
156
- if (this.rafId !== null) {
157
- cancelAnimationFrame(this.rafId);
158
- this.rafId = null;
159
- }
160
- }
161
- frame = (now) => {
162
- this.rafId = null;
163
- if (!this.running || this.paused) return;
164
- if (this.startedAt === 0) {
165
- this.startedAt = now;
166
- this.lastTickAt = now;
167
- }
168
- const delta = (now - this.lastTickAt) / 1e3;
169
- const elapsed = (now - this.startedAt) / 1e3;
170
- this.lastTickAt = now;
171
- const tick = { delta, elapsed, now };
172
- for (const client of this.clients) {
173
- client(tick);
174
- }
175
- this.flushPending = false;
176
- this.maybeQueue();
177
- };
178
- };
179
-
180
- // src/inputs/CursorInput.ts
83
+ // src/inputs/cursor-input/cursor-input.ts
181
84
  var CursorInput = class {
182
85
  value;
183
86
  target;
@@ -196,6 +99,7 @@ var CursorInput = class {
196
99
  this.eventTarget = target ?? (typeof window !== "undefined" ? window : new EventTarget());
197
100
  this.element = element;
198
101
  this.handleMouseMove = (e) => {
102
+ if (!(e instanceof MouseEvent)) return;
199
103
  const me = e;
200
104
  if (this.element) {
201
105
  const r = this.element.getBoundingClientRect();
@@ -211,7 +115,7 @@ var CursorInput = class {
211
115
  };
212
116
  this.eventTarget.addEventListener("mousemove", this.handleMouseMove);
213
117
  }
214
- /** Current smoothed position. Implements MatterSignal protocol. */
118
+ /** Current smoothed position. Implements AnimatableSignal protocol. */
215
119
  get() {
216
120
  return this.value;
217
121
  }
@@ -250,16 +154,18 @@ var CursorInput = class {
250
154
  var clamp01 = (n) => Math.max(0, Math.min(1, n));
251
155
  var lerp = (a, b, t) => a + (b - a) * t;
252
156
 
253
- // src/primitives/colorRamp.ts
157
+ // src/primitives/color-ramp/color-ramp.ts
254
158
  var import_tsl = require("three/tsl");
255
159
  var import_tsl2 = require("three/tsl");
256
160
  function colorRamp(t, stops) {
257
- if (stops.length === 0) return (0, import_tsl.vec3)(0, 0, 0);
258
- if (stops.length === 1) return stops[0].color;
259
- let result = stops[0].color;
260
- for (let i = 1; i < stops.length; i++) {
161
+ const first = stops[0];
162
+ if (first === void 0) return (0, import_tsl.vec3)(0, 0, 0);
163
+ if (stops.length === 1) return (0, import_tsl.mix)(first.color, first.color, 0);
164
+ let result = (0, import_tsl.mix)(first.color, first.color, 0);
165
+ for (let i = 1; i < stops.length; i += 1) {
261
166
  const prev = stops[i - 1];
262
167
  const next = stops[i];
168
+ if (prev === void 0 || next === void 0) continue;
263
169
  const span = next.position - prev.position;
264
170
  if (span <= 0) continue;
265
171
  const localT = (0, import_tsl2.clamp)((0, import_tsl2.div)((0, import_tsl2.sub)(t, prev.position), span), 0, 1);
@@ -268,13 +174,13 @@ function colorRamp(t, stops) {
268
174
  return result;
269
175
  }
270
176
 
271
- // src/primitives/noise.ts
177
+ // src/primitives/noise/noise.ts
272
178
  var import_tsl3 = require("three/tsl");
273
179
  function noise(p) {
274
180
  return (0, import_tsl3.mx_noise_float)(p);
275
181
  }
276
182
 
277
- // src/primitives/fbm.ts
183
+ // src/primitives/fbm/fbm.ts
278
184
  var import_tsl4 = require("three/tsl");
279
185
  function fbm(p, opts = {}) {
280
186
  const octaves = opts.octaves ?? 4;
@@ -284,7 +190,7 @@ function fbm(p, opts = {}) {
284
190
  let amp = 1;
285
191
  let freq = 1;
286
192
  let total = amp;
287
- for (let i = 1; i < octaves; i++) {
193
+ for (let i = 1; i < octaves; i += 1) {
288
194
  freq *= lacunarity;
289
195
  amp *= gain;
290
196
  total += amp;
@@ -295,13 +201,13 @@ function fbm(p, opts = {}) {
295
201
  return sum.div(total);
296
202
  }
297
203
 
298
- // src/primitives/voronoi.ts
204
+ // src/primitives/voronoi/voronoi.ts
299
205
  var import_tsl5 = require("three/tsl");
300
206
  function voronoi(p) {
301
207
  return (0, import_tsl5.mx_worley_noise_float)(p);
302
208
  }
303
209
 
304
- // src/primitives/quantize.ts
210
+ // src/primitives/quantize/quantize.ts
305
211
  function quantize(t, steps) {
306
212
  if (steps <= 1) {
307
213
  return t.mul(0);
@@ -310,25 +216,25 @@ function quantize(t, steps) {
310
216
  return t.mul(denom).add(0.5).floor().div(denom);
311
217
  }
312
218
 
313
- // src/primitives/sdfCircle.ts
219
+ // src/primitives/sdf-circle/sdf-circle.ts
314
220
  var import_tsl6 = require("three/tsl");
315
221
  function sdfCircle(p, radius) {
316
222
  return (0, import_tsl6.length)(p).sub(radius);
317
223
  }
318
224
 
319
- // src/primitives/displace.ts
225
+ // src/primitives/displace/displace.ts
320
226
  var import_tsl7 = require("three/tsl");
321
227
  function displace(p, by) {
322
228
  return (0, import_tsl7.add)(p, by);
323
229
  }
324
230
 
325
- // src/primitives/cursorRipple.ts
231
+ // src/primitives/cursor-ripple/cursor-ripple.ts
326
232
  var import_tsl10 = require("three/tsl");
327
233
 
328
- // src/primitives/time.ts
234
+ // src/primitives/time/time.ts
329
235
  var import_tsl9 = require("three/tsl");
330
236
 
331
- // src/runtime/reducedMotion.ts
237
+ // src/runtime/reduced-motion/reduced-motion.ts
332
238
  var import_tsl8 = require("three/tsl");
333
239
  var state = {
334
240
  policy: "auto",
@@ -415,10 +321,10 @@ function getReducedMotionTimeScale() {
415
321
  return globalScaleUniform;
416
322
  }
417
323
 
418
- // src/primitives/time.ts
324
+ // src/primitives/time/time.ts
419
325
  var time = import_tsl9.time.mul(getReducedMotionTimeScale());
420
326
 
421
- // src/primitives/cursorRipple.ts
327
+ // src/primitives/cursor-ripple/cursor-ripple.ts
422
328
  function cursorRipple(p, center, opts = {}) {
423
329
  const reach = opts.reach ?? 0.4;
424
330
  const frequency = opts.frequency ?? 30;
@@ -430,7 +336,7 @@ function cursorRipple(p, center, opts = {}) {
430
336
  return wave.mul(amplitude).mul(decay);
431
337
  }
432
338
 
433
- // src/primitives/filmGrain.ts
339
+ // src/primitives/film-grain/film-grain.ts
434
340
  var import_tsl11 = require("three/tsl");
435
341
  function filmGrain(uvNode, intensity, timeOffset = 0) {
436
342
  const HASH_C1 = (0, import_tsl11.vec2)(2127.1, 81.17);
@@ -440,7 +346,7 @@ function filmGrain(uvNode, intensity, timeOffset = 0) {
440
346
  return (0, import_tsl11.length)(hash).sub(0.765).mul(intensity);
441
347
  }
442
348
 
443
- // src/runtime/visibility.ts
349
+ // src/runtime/visibility/visibility.ts
444
350
  function createVisibilityWatcher() {
445
351
  if (typeof document === "undefined") {
446
352
  return {
@@ -470,7 +376,7 @@ function createVisibilityWatcher() {
470
376
  };
471
377
  }
472
378
 
473
- // src/runtime/intersection.ts
379
+ // src/runtime/intersection/intersection.ts
474
380
  function createIntersectionWatcher(canvas) {
475
381
  if (typeof IntersectionObserver === "undefined") {
476
382
  return {
@@ -505,10 +411,108 @@ function createIntersectionWatcher(canvas) {
505
411
  }
506
412
  };
507
413
  }
414
+
415
+ // src/runtime/frame-scheduler/frame-scheduler.ts
416
+ var FrameScheduler = class {
417
+ clients = /* @__PURE__ */ new Set();
418
+ rafId = null;
419
+ running = false;
420
+ paused = false;
421
+ idle = false;
422
+ flushPending = false;
423
+ startedAt = 0;
424
+ lastTickAt = 0;
425
+ /** Activate the scheduler. The rAF loop starts on the first client added. */
426
+ start() {
427
+ this.running = true;
428
+ this.paused = false;
429
+ this.maybeQueue();
430
+ }
431
+ /** Halt the rAF loop entirely. Use dispose() for permanent teardown. */
432
+ stop() {
433
+ this.running = false;
434
+ this.cancel();
435
+ }
436
+ /** Temporarily skip ticks without losing client registrations. */
437
+ pause() {
438
+ this.paused = true;
439
+ }
440
+ /** Resume after pause(). */
441
+ resume() {
442
+ this.paused = false;
443
+ if (this.running) this.maybeQueue();
444
+ }
445
+ /** Register a client to be called every frame. */
446
+ add(client) {
447
+ this.clients.add(client);
448
+ if (this.running) this.maybeQueue();
449
+ }
450
+ /** Unregister a client. */
451
+ remove(client) {
452
+ this.clients.delete(client);
453
+ }
454
+ /** Permanent teardown: stop the loop and drop all clients. */
455
+ dispose() {
456
+ this.stop();
457
+ this.clients.clear();
458
+ }
459
+ /**
460
+ * Mark the scheduler idle. The next tick still fires (a final flush so
461
+ * uniform changes that triggered the idle state are rendered), then the
462
+ * rAF loop halts. Use `requestRender()` or `setIdle(false)` to wake.
463
+ */
464
+ setIdle(idle) {
465
+ if (this.idle === idle) return;
466
+ this.idle = idle;
467
+ if (idle) {
468
+ this.flushPending = true;
469
+ this.maybeQueue();
470
+ } else {
471
+ this.flushPending = false;
472
+ this.maybeQueue();
473
+ }
474
+ }
475
+ /** Force a single tick while idle. Useful for prop-change invalidation. */
476
+ requestRender() {
477
+ if (!this.idle) return;
478
+ this.flushPending = true;
479
+ this.maybeQueue();
480
+ }
481
+ maybeQueue() {
482
+ if (this.rafId !== null) return;
483
+ if (!this.running) return;
484
+ if (this.clients.size === 0) return;
485
+ if (this.idle && !this.flushPending) return;
486
+ this.rafId = requestAnimationFrame(this.frame);
487
+ }
488
+ cancel() {
489
+ if (this.rafId !== null) {
490
+ cancelAnimationFrame(this.rafId);
491
+ this.rafId = null;
492
+ }
493
+ }
494
+ frame = (now) => {
495
+ this.rafId = null;
496
+ if (!this.running || this.paused) return;
497
+ if (this.startedAt === 0) {
498
+ this.startedAt = now;
499
+ this.lastTickAt = now;
500
+ }
501
+ const delta = (now - this.lastTickAt) / 1e3;
502
+ const elapsed = (now - this.startedAt) / 1e3;
503
+ this.lastTickAt = now;
504
+ const tick = { delta, elapsed, now };
505
+ for (const client of this.clients) {
506
+ client(tick);
507
+ }
508
+ this.flushPending = false;
509
+ this.maybeQueue();
510
+ };
511
+ };
508
512
  // Annotate the CommonJS export names for ESM import in node:
509
513
  0 && (module.exports = {
510
514
  CursorInput,
511
- MatterScheduler,
515
+ FrameScheduler,
512
516
  colorRamp,
513
517
  createIntersectionWatcher,
514
518
  createReducedMotionWatcher,