@sarmal/core 0.23.0 → 0.24.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 (75) hide show
  1. package/dist/auto-init.cjs +148 -137
  2. package/dist/auto-init.cjs.map +1 -1
  3. package/dist/auto-init.js +147 -136
  4. package/dist/auto-init.js.map +1 -1
  5. package/dist/curves/artemis2.cjs +7 -10
  6. package/dist/curves/artemis2.d.cts +1 -1
  7. package/dist/curves/artemis2.d.ts +1 -1
  8. package/dist/curves/artemis2.js +6 -9
  9. package/dist/curves/astroid.cjs +4 -4
  10. package/dist/curves/astroid.d.cts +1 -1
  11. package/dist/curves/astroid.d.ts +1 -1
  12. package/dist/curves/astroid.js +3 -3
  13. package/dist/curves/deltoid.cjs +4 -4
  14. package/dist/curves/deltoid.d.cts +1 -1
  15. package/dist/curves/deltoid.d.ts +1 -1
  16. package/dist/curves/deltoid.js +3 -3
  17. package/dist/curves/epicycloid3.cjs +4 -4
  18. package/dist/curves/epicycloid3.d.cts +1 -1
  19. package/dist/curves/epicycloid3.d.ts +1 -1
  20. package/dist/curves/epicycloid3.js +3 -3
  21. package/dist/curves/epitrochoid7.cjs +5 -5
  22. package/dist/curves/epitrochoid7.d.cts +1 -1
  23. package/dist/curves/epitrochoid7.d.ts +1 -1
  24. package/dist/curves/epitrochoid7.js +4 -4
  25. package/dist/curves/index.cjs +40 -53
  26. package/dist/curves/index.d.cts +29 -29
  27. package/dist/curves/index.d.ts +29 -29
  28. package/dist/curves/index.js +40 -69
  29. package/dist/curves/lame.cjs +5 -6
  30. package/dist/curves/lame.d.cts +1 -1
  31. package/dist/curves/lame.d.ts +1 -1
  32. package/dist/curves/lame.js +4 -5
  33. package/dist/curves/lissajous32.cjs +4 -4
  34. package/dist/curves/lissajous32.d.cts +1 -1
  35. package/dist/curves/lissajous32.d.ts +1 -1
  36. package/dist/curves/lissajous32.js +3 -3
  37. package/dist/curves/lissajous43.cjs +4 -4
  38. package/dist/curves/lissajous43.d.cts +1 -1
  39. package/dist/curves/lissajous43.d.ts +1 -1
  40. package/dist/curves/lissajous43.js +3 -3
  41. package/dist/curves/rose3.cjs +4 -4
  42. package/dist/curves/rose3.d.cts +1 -1
  43. package/dist/curves/rose3.d.ts +1 -1
  44. package/dist/curves/rose3.js +3 -3
  45. package/dist/curves/rose5.cjs +4 -4
  46. package/dist/curves/rose5.d.cts +1 -1
  47. package/dist/curves/rose5.d.ts +1 -1
  48. package/dist/curves/rose5.js +3 -3
  49. package/dist/curves/rose52.cjs +5 -5
  50. package/dist/curves/rose52.d.cts +1 -1
  51. package/dist/curves/rose52.d.ts +1 -1
  52. package/dist/curves/rose52.js +4 -4
  53. package/dist/curves/star.cjs +5 -8
  54. package/dist/curves/star.d.cts +1 -1
  55. package/dist/curves/star.d.ts +1 -1
  56. package/dist/curves/star.js +4 -7
  57. package/dist/curves/star4.cjs +5 -8
  58. package/dist/curves/star4.d.cts +1 -1
  59. package/dist/curves/star4.d.ts +1 -1
  60. package/dist/curves/star4.js +4 -7
  61. package/dist/curves/star7.cjs +5 -8
  62. package/dist/curves/star7.d.cts +1 -1
  63. package/dist/curves/star7.d.ts +1 -1
  64. package/dist/curves/star7.js +4 -7
  65. package/dist/index.cjs +140 -133
  66. package/dist/index.cjs.map +1 -1
  67. package/dist/index.d.cts +33 -74
  68. package/dist/index.d.ts +33 -74
  69. package/dist/index.js +140 -153
  70. package/dist/index.js.map +1 -1
  71. package/dist/types-CknrlCAf.d.cts +314 -0
  72. package/dist/types-CknrlCAf.d.ts +314 -0
  73. package/package.json +1 -1
  74. package/dist/types-C0b4MPtI.d.cts +0 -321
  75. package/dist/types-C0b4MPtI.d.ts +0 -321
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";
1
+ 'use strict';
2
2
 
3
3
  // src/engine.ts
4
4
  var TWO_PI = Math.PI * 2;
@@ -63,13 +63,13 @@ function resolveCurve(curveDef) {
63
63
  period,
64
64
  speed,
65
65
  skeleton: curveDef.skeleton,
66
- skeletonFn: curveDef.skeletonFn,
66
+ skeletonFn: curveDef.skeletonFn
67
67
  };
68
68
  }
69
69
  function createEngine(curveDef, trailLength = 120) {
70
70
  if (!Number.isFinite(trailLength) || trailLength <= 0) {
71
71
  throw new RangeError(
72
- `[sarmal] trailLength must be a positive finite number, got ${trailLength}`,
72
+ `[sarmal] trailLength must be a positive finite number, got ${trailLength}`
73
73
  );
74
74
  }
75
75
  let curve = resolveCurve(curveDef);
@@ -110,7 +110,7 @@ function createEngine(curveDef, trailLength = 120) {
110
110
  actualTime += deltaTime;
111
111
  if (morphCurveB !== null && _morphAlpha !== null) {
112
112
  const a = curve.fn(t, actualTime, EMPTY_PARAMS);
113
- const tB = _morphStrategy === "normalized" ? (t / curve.period) * morphCurveB.period : t;
113
+ const tB = _morphStrategy === "normalized" ? t / curve.period * morphCurveB.period : t;
114
114
  const b = morphCurveB.fn(tB, actualTime, EMPTY_PARAMS);
115
115
  trail.push(a.x + (b.x - a.x) * _morphAlpha, a.y + (b.y - a.y) * _morphAlpha);
116
116
  } else {
@@ -137,14 +137,14 @@ function createEngine(curveDef, trailLength = 120) {
137
137
  trail.clear();
138
138
  },
139
139
  jump(newT, { clearTrail = false } = {}) {
140
- t = ((newT % curve.period) + curve.period) % curve.period;
140
+ t = (newT % curve.period + curve.period) % curve.period;
141
141
  if (clearTrail) {
142
142
  trail.clear();
143
143
  }
144
144
  },
145
145
  seek(targetT, { wrap = false, step = curve.period / trailLength } = {}) {
146
146
  const advance = curve.speed * step;
147
- const target = ((targetT % curve.period) + curve.period) % curve.period;
147
+ const target = (targetT % curve.period + curve.period) % curve.period;
148
148
  const targetTime = target / curve.speed;
149
149
  t = target;
150
150
  actualTime = targetTime;
@@ -153,7 +153,7 @@ function createEngine(curveDef, trailLength = 120) {
153
153
  const count = wrap ? trailLength : Math.min(trailLength, pointsFromStart);
154
154
  for (let i = count - 1; i >= 0; i--) {
155
155
  const sampleT = target - i * advance;
156
- const wrappedT = ((sampleT % curve.period) + curve.period) % curve.period;
156
+ const wrappedT = (sampleT % curve.period + curve.period) % curve.period;
157
157
  const time = targetTime - i * step;
158
158
  const point = curve.fn(wrappedT, time, EMPTY_PARAMS);
159
159
  trail.push(point.x, point.y);
@@ -170,16 +170,13 @@ function createEngine(curveDef, trailLength = 120) {
170
170
  ...frozenB,
171
171
  fn: (sampleT, time, params) => {
172
172
  const a = frozenA.fn(sampleT, time, params);
173
- const tB =
174
- frozenStrategy === "normalized"
175
- ? (sampleT / frozenA.period) * frozenB.period
176
- : sampleT;
173
+ const tB = frozenStrategy === "normalized" ? sampleT / frozenA.period * frozenB.period : sampleT;
177
174
  const b = frozenB.fn(tB, time, params);
178
175
  return {
179
176
  x: a.x + (b.x - a.x) * frozenAlpha,
180
- y: a.y + (b.y - a.y) * frozenAlpha,
177
+ y: a.y + (b.y - a.y) * frozenAlpha
181
178
  };
182
- },
179
+ }
183
180
  };
184
181
  }
185
182
  _morphStrategy = strategy;
@@ -192,7 +189,7 @@ function createEngine(curveDef, trailLength = 120) {
192
189
  completeMorph() {
193
190
  if (morphCurveB !== null) {
194
191
  if (_morphStrategy === "normalized" && curve.period !== morphCurveB.period) {
195
- t = (t / curve.period) * morphCurveB.period;
192
+ t = t / curve.period * morphCurveB.period;
196
193
  }
197
194
  curve = morphCurveB;
198
195
  }
@@ -204,22 +201,19 @@ function createEngine(curveDef, trailLength = 120) {
204
201
  const points = new Array(steps);
205
202
  if (morphCurveB !== null && _morphAlpha !== null) {
206
203
  for (let i = 0; i < steps; i++) {
207
- const sampleT = (i / (steps - 1)) * curve.period;
204
+ const sampleT = i / (steps - 1) * curve.period;
208
205
  const a = sampleSkeleton(curve, sampleT);
209
- const tB =
210
- _morphStrategy === "normalized"
211
- ? (sampleT / curve.period) * morphCurveB.period
212
- : sampleT;
206
+ const tB = _morphStrategy === "normalized" ? sampleT / curve.period * morphCurveB.period : sampleT;
213
207
  const b = sampleSkeleton(morphCurveB, tB);
214
208
  points[i] = {
215
209
  x: a.x + (b.x - a.x) * _morphAlpha,
216
- y: a.y + (b.y - a.y) * _morphAlpha,
210
+ y: a.y + (b.y - a.y) * _morphAlpha
217
211
  };
218
212
  }
219
213
  return points;
220
214
  }
221
215
  for (let i = 0; i < steps; i++) {
222
- const sampleT = (i / (steps - 1)) * curve.period;
216
+ const sampleT = i / (steps - 1) * curve.period;
223
217
  points[i] = sampleSkeleton(curve, sampleT);
224
218
  }
225
219
  return points;
@@ -261,7 +255,7 @@ function createEngine(curveDef, trailLength = 120) {
261
255
  _speedTransition.reject(new Error("Speed transition cancelled"));
262
256
  _speedTransition = null;
263
257
  }
264
- },
258
+ }
265
259
  };
266
260
  }
267
261
 
@@ -300,15 +294,7 @@ function computeNormal(trail, i) {
300
294
  const tangent = computeTangent(trail, i);
301
295
  return { x: -tangent.y, y: tangent.x };
302
296
  }
303
- function computeTrailQuad(
304
- trail,
305
- i,
306
- trailCount,
307
- toX,
308
- toY,
309
- minWidth = TRAIL_MIN_WIDTH,
310
- maxWidth = TRAIL_MAX_WIDTH,
311
- ) {
297
+ function computeTrailQuad(trail, i, trailCount, toX, toY, minWidth = TRAIL_MIN_WIDTH, maxWidth = TRAIL_MAX_WIDTH) {
312
298
  const progress = i / (trailCount - 1);
313
299
  const nextProgress = (i + 1) / (trailCount - 1);
314
300
  const opacity = Math.pow(progress, TRAIL_FADE_CURVE) * TRAIL_MAX_OPACITY;
@@ -332,16 +318,13 @@ function computeTrailQuad(
332
318
  r1x: nx - n1.x * w1,
333
319
  r1y: ny - n1.y * w1,
334
320
  opacity,
335
- progress,
321
+ progress
336
322
  };
337
323
  }
338
324
  function computeBoundaries(pts, logicalWidth, logicalHeight) {
339
325
  if (pts.length === 0) return null;
340
326
  const first = pts[0];
341
- let minX = first.x,
342
- maxX = first.x,
343
- minY = first.y,
344
- maxY = first.y;
327
+ let minX = first.x, maxX = first.x, minY = first.y, maxY = first.y;
345
328
  for (const p of pts) {
346
329
  if (p.x < minX) {
347
330
  minX = p.x;
@@ -360,7 +343,7 @@ function computeBoundaries(pts, logicalWidth, logicalHeight) {
360
343
  const h = maxY - minY;
361
344
  if (w === 0 && h === 0) {
362
345
  throw new Error(
363
- "[sarmal] Degenerate curve: all skeleton points are identical. Check that your curve fn returns distinct points for different values of t.",
346
+ "[sarmal] Degenerate curve: all skeleton points are identical. Check that your curve fn returns distinct points for different values of t."
364
347
  );
365
348
  }
366
349
  const scaleXProportional = logicalWidth / (w * (1 + FIT_PADDING * 2));
@@ -371,12 +354,12 @@ function computeBoundaries(pts, logicalWidth, logicalHeight) {
371
354
  scaleXProportional,
372
355
  scaleYProportional,
373
356
  scaleXMinPadding,
374
- scaleYMinPadding,
357
+ scaleYMinPadding
375
358
  );
376
359
  return {
377
360
  scale,
378
361
  offsetX: (logicalWidth - w * scale) / 2 - minX * scale,
379
- offsetY: (logicalHeight - h * scale) / 2 - minY * scale,
362
+ offsetY: (logicalHeight - h * scale) / 2 - minY * scale
380
363
  };
381
364
  }
382
365
  function enginePassthroughs(engine) {
@@ -386,7 +369,7 @@ function enginePassthroughs(engine) {
386
369
  setSpeed: engine.setSpeed,
387
370
  getSpeed: engine.getSpeed,
388
371
  resetSpeed: engine.resetSpeed,
389
- setSpeedOver: engine.setSpeedOver,
372
+ setSpeedOver: engine.setSpeedOver
390
373
  };
391
374
  }
392
375
  var palettes = {
@@ -395,16 +378,16 @@ var palettes = {
395
378
  ocean: ["#1e3a8a", "#06b6d4", "#22d3ee", "#e0f2fe"],
396
379
  ice: ["#1e3a8a", "#67e8f9"],
397
380
  fire: ["#7f1d1d", "#fbbf24"],
398
- forest: ["#14532d", "#86efac"],
381
+ forest: ["#14532d", "#86efac"]
399
382
  };
400
383
  function hexToRgb(hex) {
401
384
  const n = parseInt(hex.slice(1), 16);
402
- return { r: n >> 16, g: (n >> 8) & 255, b: n & 255 };
385
+ return { r: n >> 16, g: n >> 8 & 255, b: n & 255 };
403
386
  }
404
387
  var lerpRgb = (a, b, t) => ({
405
388
  r: Math.round(a.r + (b.r - a.r) * t),
406
389
  g: Math.round(a.g + (b.g - a.g) * t),
407
- b: Math.round(a.b + (b.b - a.b) * t),
390
+ b: Math.round(a.b + (b.b - a.b) * t)
408
391
  });
409
392
  function getPaletteColor(palette, position, timeOffset = 0) {
410
393
  if (palette.length === 0) {
@@ -413,7 +396,7 @@ function getPaletteColor(palette, position, timeOffset = 0) {
413
396
  if (palette.length === 1) {
414
397
  return hexToRgb(palette[0]);
415
398
  }
416
- const cyclePos = (((position + timeOffset) % 1) + 1) % 1;
399
+ const cyclePos = ((position + timeOffset) % 1 + 1) % 1;
417
400
  const scaled = cyclePos * palette.length;
418
401
  const idx = Math.floor(scaled);
419
402
  const t = scaled - idx;
@@ -428,7 +411,7 @@ var RENDER_OPTION_KEYS = /* @__PURE__ */ new Set([
428
411
  "headColor",
429
412
  "skeletonColor",
430
413
  "trailStyle",
431
- "headRadius",
414
+ "headRadius"
432
415
  ]);
433
416
  function validateRenderOptions(partial) {
434
417
  for (const key of Object.keys(partial)) {
@@ -456,7 +439,7 @@ function assertTrailColor(value) {
456
439
  if (typeof value === "string") {
457
440
  if (!HEX_COLOR_RE.test(value)) {
458
441
  throw new TypeError(
459
- `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string, got "${value}"`,
442
+ `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string, got "${value}"`
460
443
  );
461
444
  }
462
445
  return;
@@ -464,21 +447,21 @@ function assertTrailColor(value) {
464
447
  if (Array.isArray(value)) {
465
448
  if (value.length < 2) {
466
449
  throw new RangeError(
467
- `[sarmal] setRenderOptions: trailColor array must have at least 2 entries, got ${value.length}`,
450
+ `[sarmal] setRenderOptions: trailColor array must have at least 2 entries, got ${value.length}`
468
451
  );
469
452
  }
470
453
  for (let i = 0; i < value.length; i++) {
471
454
  const entry = value[i];
472
455
  if (typeof entry !== "string" || !HEX_COLOR_RE.test(entry)) {
473
456
  throw new TypeError(
474
- `[sarmal] setRenderOptions: trailColor[${i}] must be a 6-digit hex string, got ${JSON.stringify(entry)}`,
457
+ `[sarmal] setRenderOptions: trailColor[${i}] must be a 6-digit hex string, got ${JSON.stringify(entry)}`
475
458
  );
476
459
  }
477
460
  }
478
461
  return;
479
462
  }
480
463
  throw new TypeError(
481
- `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string or an array of hex strings, got ${JSON.stringify(value)}`,
464
+ `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string or an array of hex strings, got ${JSON.stringify(value)}`
482
465
  );
483
466
  }
484
467
  function assertHeadColor(value) {
@@ -487,7 +470,7 @@ function assertHeadColor(value) {
487
470
  }
488
471
  if (typeof value !== "string" || !HEX_COLOR_RE.test(value)) {
489
472
  throw new TypeError(
490
- `[sarmal] setRenderOptions: headColor must be a 6-digit hex string or null, got ${JSON.stringify(value)}`,
473
+ `[sarmal] setRenderOptions: headColor must be a 6-digit hex string or null, got ${JSON.stringify(value)}`
491
474
  );
492
475
  }
493
476
  }
@@ -497,26 +480,26 @@ function assertSkeletonColor(value) {
497
480
  }
498
481
  if (typeof value !== "string" || !HEX_COLOR_RE.test(value)) {
499
482
  throw new TypeError(
500
- `[sarmal] setRenderOptions: skeletonColor must be a 6-digit hex string or "transparent", got ${JSON.stringify(value)}`,
483
+ `[sarmal] setRenderOptions: skeletonColor must be a 6-digit hex string or "transparent", got ${JSON.stringify(value)}`
501
484
  );
502
485
  }
503
486
  }
504
487
  function assertTrailStyle(value) {
505
488
  if (!TRAIL_STYLES.includes(value)) {
506
489
  throw new RangeError(
507
- `[sarmal] setRenderOptions: trailStyle must be one of "default", "gradient-static", "gradient-animated", got ${JSON.stringify(value)}`,
490
+ `[sarmal] setRenderOptions: trailStyle must be one of "default", "gradient-static", "gradient-animated", got ${JSON.stringify(value)}`
508
491
  );
509
492
  }
510
493
  }
511
494
  function assertHeadRadius(value) {
512
495
  if (typeof value !== "number") {
513
496
  throw new TypeError(
514
- `[sarmal] setRenderOptions: headRadius must be a number, got ${JSON.stringify(value)}`,
497
+ `[sarmal] setRenderOptions: headRadius must be a number, got ${JSON.stringify(value)}`
515
498
  );
516
499
  }
517
500
  if (!Number.isFinite(value) || value <= 0) {
518
501
  throw new TypeError(
519
- `[sarmal] setRenderOptions: headRadius must be a finite positive number, got ${value}`,
502
+ `[sarmal] setRenderOptions: headRadius must be a finite positive number, got ${value}`
520
503
  );
521
504
  }
522
505
  }
@@ -538,13 +521,13 @@ function resolveHeadColor(trailColor, trailStyle) {
538
521
  function warnIfTrailColorMismatch(trailColor, trailStyle) {
539
522
  if (trailStyle === "default" && Array.isArray(trailColor)) {
540
523
  console.warn(
541
- '[sarmal] trailColor is an array but trailStyle is "default"; only the first color will be used. Pass a gradient trailStyle to use the whole palette.',
524
+ '[sarmal] trailColor is an array but trailStyle is "default"; only the first color will be used. Pass a gradient trailStyle to use the whole palette.'
542
525
  );
543
526
  return;
544
527
  }
545
528
  if (trailStyle !== "default" && typeof trailColor === "string") {
546
529
  console.warn(
547
- `[sarmal] trailColor is a single color but trailStyle is "${trailStyle}"; the trail will render as a solid color. Pass an array of hex colors to use a real gradient.`,
530
+ `[sarmal] trailColor is a single color but trailStyle is "${trailStyle}"; the trail will render as a solid color. Pass an array of hex colors to use a real gradient.`
548
531
  );
549
532
  }
550
533
  }
@@ -554,7 +537,7 @@ var getHeadDotRadius = (w, h) => Math.max(1, 3 * Math.sqrt(Math.min(w, h) / 160)
554
537
  var WHITE_HEX = "#ffffff";
555
538
  function hexToRgbComponents(hex) {
556
539
  const n = parseInt(hex.slice(1), 16);
557
- return `${n >> 16},${(n >> 8) & 255},${n & 255}`;
540
+ return `${n >> 16},${n >> 8 & 255},${n & 255}`;
558
541
  }
559
542
  function applyDprSizing(target, logicalWidth, logicalHeight, dpr) {
560
543
  target.style.width = `${logicalWidth}px`;
@@ -598,6 +581,7 @@ function createRenderer(options) {
598
581
  let offsetY = 0;
599
582
  let animationId = null;
600
583
  let lastTime = 0;
584
+ let pausedByVisibility = false;
601
585
  let morphResolve = null;
602
586
  let morphReject = null;
603
587
  let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
@@ -680,7 +664,7 @@ function createRenderer(options) {
680
664
  i,
681
665
  trailCount,
682
666
  toX,
683
- toY,
667
+ toY
684
668
  );
685
669
  if (trailStyle === "default") {
686
670
  ctx.fillStyle = `rgba(${trailSolidRgb},${opacity})`;
@@ -791,6 +775,7 @@ function createRenderer(options) {
791
775
  cancelAnimationFrame(animationId);
792
776
  animationId = null;
793
777
  }
778
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
794
779
  if (morphReject !== null) {
795
780
  morphReject(new Error("Instance destroyed during morph"));
796
781
  morphResolve = null;
@@ -844,10 +829,30 @@ function createRenderer(options) {
844
829
  if (partial.trailColor !== void 0 || partial.trailStyle !== void 0) {
845
830
  warnIfTrailColorMismatch(trailColor, trailStyle);
846
831
  }
847
- },
832
+ }
848
833
  };
849
- if (shouldAutoStart) {
834
+ const pauseOnHidden = options.pauseOnHidden !== false;
835
+ function handleVisibilityChange() {
836
+ if (document.hidden) {
837
+ if (animationId !== null) {
838
+ instance.pause();
839
+ pausedByVisibility = true;
840
+ }
841
+ } else {
842
+ if (pausedByVisibility) {
843
+ pausedByVisibility = false;
844
+ instance.play();
845
+ }
846
+ }
847
+ }
848
+ if (pauseOnHidden) {
849
+ document.addEventListener("visibilitychange", handleVisibilityChange);
850
+ }
851
+ const actuallyAutoStart = shouldAutoStart && !(pauseOnHidden && document.hidden);
852
+ if (actuallyAutoStart) {
850
853
  instance.play();
854
+ } else if (shouldAutoStart) {
855
+ pausedByVisibility = true;
851
856
  }
852
857
  return instance;
853
858
  }
@@ -874,7 +879,7 @@ function sampleCurveSkeleton(curveDef) {
874
879
  const samples = Math.ceil(period * 50);
875
880
  const pts = Array.from({ length: samples });
876
881
  for (let i = 0; i < samples; i++) {
877
- const t = (i / (samples - 1)) * period;
882
+ const t = i / (samples - 1) * period;
878
883
  pts[i] = curveDef.skeletonFn ? curveDef.skeletonFn(t) : curveDef.fn(t, 0, EMPTY_PARAMS2);
879
884
  }
880
885
  return pts;
@@ -887,7 +892,7 @@ function createSVGRenderer(options) {
887
892
  const poolSize = engine.trailLength;
888
893
  if (poolSize > HIGH_TRAIL_LENGTH_THRESHOLD) {
889
894
  console.warn(
890
- `[sarmal] High trailLength in SVG renderer (${poolSize}). Consider using the canvas renderer for long trails.`,
895
+ `[sarmal] High trailLength in SVG renderer (${poolSize}). Consider using the canvas renderer for long trails.`
891
896
  );
892
897
  }
893
898
  let trailStyle = options.trailStyle ?? "default";
@@ -906,9 +911,9 @@ function createSVGRenderer(options) {
906
911
  return rect.width && rect.height ? Math.min(rect.width, rect.height) : 200;
907
912
  }
908
913
  const containerPx = getContainerPixelSize();
909
- const svgTrailMinWidth = (TRAIL_MIN_WIDTH * viewSize) / containerPx;
910
- const svgTrailMaxWidth = (TRAIL_MAX_WIDTH * viewSize) / containerPx;
911
- const svgSkeletonStrokeWidth = String((SKELETON_STROKE_WIDTH_PX * viewSize) / containerPx);
914
+ const svgTrailMinWidth = TRAIL_MIN_WIDTH * viewSize / containerPx;
915
+ const svgTrailMaxWidth = TRAIL_MAX_WIDTH * viewSize / containerPx;
916
+ const svgSkeletonStrokeWidth = String(SKELETON_STROKE_WIDTH_PX * viewSize / containerPx);
912
917
  headRadius = options.headRadius ?? SVG_DEFAULT_HEAD_RADIUS;
913
918
  container.setAttribute("viewBox", `0 0 ${viewSize} ${viewSize}`);
914
919
  container.setAttribute("role", "img");
@@ -996,7 +1001,7 @@ function createSVGRenderer(options) {
996
1001
  px,
997
1002
  py,
998
1003
  svgTrailMinWidth,
999
- svgTrailMaxWidth,
1004
+ svgTrailMaxWidth
1000
1005
  );
1001
1006
  const d = `M${l0x.toFixed(2)} ${l0y.toFixed(2)} L${l1x.toFixed(2)} ${l1y.toFixed(2)} L${r1x.toFixed(2)} ${r1y.toFixed(2)} L${r0x.toFixed(2)} ${r0y.toFixed(2)} Z`;
1002
1007
  trailPaths[i].setAttribute("d", d);
@@ -1023,8 +1028,8 @@ function createSVGRenderer(options) {
1023
1028
  }
1024
1029
  let animationId = null;
1025
1030
  let lastTime = 0;
1026
- const prefersReducedMotion =
1027
- typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1031
+ let pausedByVisibility = false;
1032
+ const prefersReducedMotion = typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1028
1033
  let morphResolve = null;
1029
1034
  let morphReject = null;
1030
1035
  let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
@@ -1042,7 +1047,7 @@ function createSVGRenderer(options) {
1042
1047
  skeletonPathA.setAttribute("visibility", "visible");
1043
1048
  skeletonPathA.setAttribute(
1044
1049
  "stroke-opacity",
1045
- String((1 - morphAlpha) * DEFAULT_SKELETON_OPACITY),
1050
+ String((1 - morphAlpha) * DEFAULT_SKELETON_OPACITY)
1046
1051
  );
1047
1052
  }
1048
1053
  if (morphPathBBuilt) {
@@ -1114,6 +1119,7 @@ function createSVGRenderer(options) {
1114
1119
  cancelAnimationFrame(animationId);
1115
1120
  animationId = null;
1116
1121
  }
1122
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
1117
1123
  if (morphReject !== null) {
1118
1124
  morphReject(new Error("Instance destroyed during morph"));
1119
1125
  morphResolve = null;
@@ -1195,10 +1201,30 @@ function createSVGRenderer(options) {
1195
1201
  if (partial.trailColor !== void 0 || partial.trailStyle !== void 0) {
1196
1202
  warnIfTrailColorMismatch(trailColor, trailStyle);
1197
1203
  }
1198
- },
1204
+ }
1199
1205
  };
1200
- if (shouldAutoStart) {
1206
+ const pauseOnHidden = options.pauseOnHidden !== false;
1207
+ function handleVisibilityChange() {
1208
+ if (document.hidden) {
1209
+ if (animationId !== null) {
1210
+ instance.pause();
1211
+ pausedByVisibility = true;
1212
+ }
1213
+ } else {
1214
+ if (pausedByVisibility) {
1215
+ pausedByVisibility = false;
1216
+ instance.play();
1217
+ }
1218
+ }
1219
+ }
1220
+ if (pauseOnHidden) {
1221
+ document.addEventListener("visibilitychange", handleVisibilityChange);
1222
+ }
1223
+ const actuallyAutoStart = shouldAutoStart && !(pauseOnHidden && document.hidden);
1224
+ if (actuallyAutoStart) {
1201
1225
  instance.play();
1226
+ } else if (shouldAutoStart) {
1227
+ pausedByVisibility = true;
1202
1228
  }
1203
1229
  return instance;
1204
1230
  }
@@ -1211,22 +1237,19 @@ function createSarmalSVG(container, curveDef, options) {
1211
1237
  // src/curves/artemis2.ts
1212
1238
  var TWO_PI2 = Math.PI * 2;
1213
1239
  function artemis2Fn(t, _time, _params) {
1214
- const a = 0.35,
1215
- b = 0.15,
1216
- ox = 0.175;
1217
- const s = Math.sin(t),
1218
- c = Math.cos(t);
1240
+ const a = 0.35, b = 0.15, ox = 0.175;
1241
+ const s = Math.sin(t), c = Math.cos(t);
1219
1242
  const denom = 1 + s * s;
1220
1243
  return {
1221
- x: (c * (1 + a * c)) / denom - ox,
1222
- y: (s * c * (1 + b * c)) / denom,
1244
+ x: c * (1 + a * c) / denom - ox,
1245
+ y: s * c * (1 + b * c) / denom
1223
1246
  };
1224
1247
  }
1225
1248
  var artemis2 = {
1226
1249
  name: "Artemis II",
1227
1250
  fn: artemis2Fn,
1228
1251
  period: TWO_PI2,
1229
- speed: 0.7,
1252
+ speed: 0.7
1230
1253
  };
1231
1254
 
1232
1255
  // src/curves/astroid.ts
@@ -1236,14 +1259,14 @@ function astroidFn(t, _time, _params) {
1236
1259
  const s = Math.sin(t);
1237
1260
  return {
1238
1261
  x: c * c * c,
1239
- y: s * s * s,
1262
+ y: s * s * s
1240
1263
  };
1241
1264
  }
1242
1265
  var astroid = {
1243
1266
  name: "Astroid",
1244
1267
  fn: astroidFn,
1245
1268
  period: TWO_PI3,
1246
- speed: 1.1,
1269
+ speed: 1.1
1247
1270
  };
1248
1271
 
1249
1272
  // src/curves/deltoid.ts
@@ -1251,14 +1274,14 @@ var TWO_PI4 = Math.PI * 2;
1251
1274
  function deltoidFn(t, _time, _params) {
1252
1275
  return {
1253
1276
  x: 2 * Math.cos(t) + Math.cos(2 * t),
1254
- y: 2 * Math.sin(t) - Math.sin(2 * t),
1277
+ y: 2 * Math.sin(t) - Math.sin(2 * t)
1255
1278
  };
1256
1279
  }
1257
1280
  var deltoid = {
1258
1281
  name: "Deltoid",
1259
1282
  fn: deltoidFn,
1260
1283
  period: TWO_PI4,
1261
- speed: 0.9,
1284
+ speed: 0.9
1262
1285
  };
1263
1286
 
1264
1287
  // src/curves/epicycloid3.ts
@@ -1266,14 +1289,14 @@ var TWO_PI5 = Math.PI * 2;
1266
1289
  function epicycloid3Fn(t, _time, _params) {
1267
1290
  return {
1268
1291
  x: 4 * Math.cos(t) - Math.cos(4 * t),
1269
- y: 4 * Math.sin(t) - Math.sin(4 * t),
1292
+ y: 4 * Math.sin(t) - Math.sin(4 * t)
1270
1293
  };
1271
1294
  }
1272
1295
  var epicycloid3 = {
1273
1296
  name: "Epicycloid (n=3)",
1274
1297
  fn: epicycloid3Fn,
1275
1298
  period: TWO_PI5,
1276
- speed: 0.75,
1299
+ speed: 0.75
1277
1300
  };
1278
1301
 
1279
1302
  // src/curves/epitrochoid7.ts
@@ -1282,14 +1305,14 @@ function epitrochoid7Fn(t, _time, _params) {
1282
1305
  const d = 1 + 0.55 * Math.sin(t * 0.5);
1283
1306
  return {
1284
1307
  x: 7 * Math.cos(t) - d * Math.cos(7 * t),
1285
- y: 7 * Math.sin(t) - d * Math.sin(7 * t),
1308
+ y: 7 * Math.sin(t) - d * Math.sin(7 * t)
1286
1309
  };
1287
1310
  }
1288
1311
  function epitrochoid7SkeletonFn(t) {
1289
1312
  const d = 1.275;
1290
1313
  return {
1291
1314
  x: 7 * Math.cos(t) - d * Math.cos(7 * t),
1292
- y: 7 * Math.sin(t) - d * Math.sin(7 * t),
1315
+ y: 7 * Math.sin(t) - d * Math.sin(7 * t)
1293
1316
  };
1294
1317
  }
1295
1318
  var epitrochoid7 = {
@@ -1297,7 +1320,7 @@ var epitrochoid7 = {
1297
1320
  fn: epitrochoid7Fn,
1298
1321
  period: TWO_PI6,
1299
1322
  speed: 1.4,
1300
- skeletonFn: epitrochoid7SkeletonFn,
1323
+ skeletonFn: epitrochoid7SkeletonFn
1301
1324
  };
1302
1325
 
1303
1326
  // src/curves/lissajous32.ts
@@ -1306,7 +1329,7 @@ function lissajous32Fn(t, time, _params) {
1306
1329
  const phi = time * 0.45;
1307
1330
  return {
1308
1331
  x: Math.sin(3 * t + phi),
1309
- y: Math.sin(2 * t),
1332
+ y: Math.sin(2 * t)
1310
1333
  };
1311
1334
  }
1312
1335
  var lissajous32 = {
@@ -1314,7 +1337,7 @@ var lissajous32 = {
1314
1337
  fn: lissajous32Fn,
1315
1338
  period: TWO_PI7,
1316
1339
  speed: 2,
1317
- skeleton: "live",
1340
+ skeleton: "live"
1318
1341
  };
1319
1342
 
1320
1343
  // src/curves/lissajous43.ts
@@ -1323,7 +1346,7 @@ function lissajous43Fn(t, time, _params) {
1323
1346
  const phi = time * 0.38;
1324
1347
  return {
1325
1348
  x: Math.sin(4 * t + phi),
1326
- y: Math.sin(3 * t),
1349
+ y: Math.sin(3 * t)
1327
1350
  };
1328
1351
  }
1329
1352
  var lissajous43 = {
@@ -1331,18 +1354,17 @@ var lissajous43 = {
1331
1354
  fn: lissajous43Fn,
1332
1355
  period: TWO_PI8,
1333
1356
  speed: 1.8,
1334
- skeleton: "live",
1357
+ skeleton: "live"
1335
1358
  };
1336
1359
 
1337
1360
  // src/curves/lame.ts
1338
1361
  var TWO_PI9 = Math.PI * 2;
1339
1362
  function lameFn(t, time, _params) {
1340
1363
  const p = 1.75 + 1.25 * Math.sin(time * 0.48);
1341
- const c = Math.cos(t),
1342
- s = Math.sin(t);
1364
+ const c = Math.cos(t), s = Math.sin(t);
1343
1365
  return {
1344
1366
  x: Math.sign(c) * Math.pow(Math.abs(c), p),
1345
- y: Math.sign(s) * Math.pow(Math.abs(s), p),
1367
+ y: Math.sign(s) * Math.pow(Math.abs(s), p)
1346
1368
  };
1347
1369
  }
1348
1370
  var lame = {
@@ -1350,7 +1372,7 @@ var lame = {
1350
1372
  fn: lameFn,
1351
1373
  period: TWO_PI9,
1352
1374
  speed: 1,
1353
- skeleton: "live",
1375
+ skeleton: "live"
1354
1376
  };
1355
1377
 
1356
1378
  // src/curves/rose3.ts
@@ -1359,14 +1381,14 @@ function rose3Fn(t, _time, _params) {
1359
1381
  const r = Math.cos(3 * t);
1360
1382
  return {
1361
1383
  x: r * Math.cos(t),
1362
- y: r * Math.sin(t),
1384
+ y: r * Math.sin(t)
1363
1385
  };
1364
1386
  }
1365
1387
  var rose3 = {
1366
1388
  name: "Rose (n=3)",
1367
1389
  fn: rose3Fn,
1368
1390
  period: TWO_PI10,
1369
- speed: 1.15,
1391
+ speed: 1.15
1370
1392
  };
1371
1393
 
1372
1394
  // src/curves/rose5.ts
@@ -1375,87 +1397,78 @@ function rose5Fn(t, _time, _params) {
1375
1397
  const r = Math.cos(5 * t);
1376
1398
  return {
1377
1399
  x: r * Math.cos(t),
1378
- y: r * Math.sin(t),
1400
+ y: r * Math.sin(t)
1379
1401
  };
1380
1402
  }
1381
1403
  var rose5 = {
1382
1404
  name: "Rose (n=5)",
1383
1405
  fn: rose5Fn,
1384
1406
  period: TWO_PI11,
1385
- speed: 1,
1407
+ speed: 1
1386
1408
  };
1387
1409
 
1388
1410
  // src/curves/rose52.ts
1389
1411
  var FOUR_PI = Math.PI * 4;
1390
1412
  function rose52Fn(t, _time, _params) {
1391
- const r = Math.cos((5 / 2) * t);
1413
+ const r = Math.cos(5 / 2 * t);
1392
1414
  return {
1393
1415
  x: r * Math.cos(t),
1394
- y: r * Math.sin(t),
1416
+ y: r * Math.sin(t)
1395
1417
  };
1396
1418
  }
1397
1419
  var rose52 = {
1398
1420
  name: "Rose (n=5/2)",
1399
1421
  fn: rose52Fn,
1400
1422
  period: FOUR_PI,
1401
- speed: 0.8,
1423
+ speed: 0.8
1402
1424
  };
1403
1425
 
1404
1426
  // src/curves/star.ts
1405
1427
  var TWO_PI12 = Math.PI * 2;
1406
1428
  function starFn(t, _time, _params) {
1407
- const r =
1408
- Math.abs(Math.cos((5 / 2) * t)) +
1409
- 0.35 * Math.abs(Math.cos((15 / 2) * t)) +
1410
- 0.15 * Math.abs(Math.cos((25 / 2) * t));
1429
+ const r = Math.abs(Math.cos(5 / 2 * t)) + 0.35 * Math.abs(Math.cos(15 / 2 * t)) + 0.15 * Math.abs(Math.cos(25 / 2 * t));
1411
1430
  return {
1412
1431
  x: r * Math.cos(t),
1413
- y: r * Math.sin(t),
1432
+ y: r * Math.sin(t)
1414
1433
  };
1415
1434
  }
1416
1435
  var star = {
1417
1436
  name: "Star",
1418
1437
  fn: starFn,
1419
1438
  period: TWO_PI12,
1420
- speed: 1,
1439
+ speed: 1
1421
1440
  };
1422
1441
 
1423
1442
  // src/curves/star4.ts
1424
1443
  var TWO_PI13 = Math.PI * 2;
1425
1444
  function star4Fn(t, _time, _params) {
1426
- const r =
1427
- Math.abs(Math.cos(2 * t)) +
1428
- 0.35 * Math.abs(Math.cos(6 * t)) +
1429
- 0.15 * Math.abs(Math.cos(10 * t));
1445
+ const r = Math.abs(Math.cos(2 * t)) + 0.35 * Math.abs(Math.cos(6 * t)) + 0.15 * Math.abs(Math.cos(10 * t));
1430
1446
  return {
1431
1447
  x: r * Math.cos(t),
1432
- y: r * Math.sin(t),
1448
+ y: r * Math.sin(t)
1433
1449
  };
1434
1450
  }
1435
1451
  var star4 = {
1436
1452
  name: "Star (4-arm)",
1437
1453
  fn: star4Fn,
1438
1454
  period: TWO_PI13,
1439
- speed: 1,
1455
+ speed: 1
1440
1456
  };
1441
1457
 
1442
1458
  // src/curves/star7.ts
1443
1459
  var TWO_PI14 = Math.PI * 2;
1444
1460
  function star7Fn(t, _time, _params) {
1445
- const r =
1446
- Math.abs(Math.cos((7 / 2) * t)) +
1447
- 0.35 * Math.abs(Math.cos((21 / 2) * t)) +
1448
- 0.15 * Math.abs(Math.cos((35 / 2) * t));
1461
+ const r = Math.abs(Math.cos(7 / 2 * t)) + 0.35 * Math.abs(Math.cos(21 / 2 * t)) + 0.15 * Math.abs(Math.cos(35 / 2 * t));
1449
1462
  return {
1450
1463
  x: r * Math.cos(t),
1451
- y: r * Math.sin(t),
1464
+ y: r * Math.sin(t)
1452
1465
  };
1453
1466
  }
1454
1467
  var star7 = {
1455
1468
  name: "Star (7-arm)",
1456
1469
  fn: star7Fn,
1457
1470
  period: TWO_PI14,
1458
- speed: 1,
1471
+ speed: 1
1459
1472
  };
1460
1473
 
1461
1474
  // src/curves/index.ts
@@ -1473,7 +1486,7 @@ var curves = {
1473
1486
  lissajous32,
1474
1487
  lissajous43,
1475
1488
  epicycloid3,
1476
- lame,
1489
+ lame
1477
1490
  };
1478
1491
 
1479
1492
  // src/catmull-rom.ts
@@ -1481,13 +1494,7 @@ var PERIOD = 2 * Math.PI;
1481
1494
  function catmullRom1D(p0, p1, p2, p3, u) {
1482
1495
  const u2 = u * u;
1483
1496
  const u3 = u2 * u;
1484
- return (
1485
- 0.5 *
1486
- (2 * p1 +
1487
- (-p0 + p2) * u +
1488
- (2 * p0 - 5 * p1 + 4 * p2 - p3) * u2 +
1489
- (-p0 + 3 * p1 - 3 * p2 + p3) * u3)
1490
- );
1497
+ return 0.5 * (2 * p1 + (-p0 + p2) * u + (2 * p0 - 5 * p1 + 4 * p2 - p3) * u2 + (-p0 + 3 * p1 - 3 * p2 + p3) * u3);
1491
1498
  }
1492
1499
  function evaluateCatmullRom(points, t) {
1493
1500
  const N = points.length;
@@ -1497,7 +1504,7 @@ function evaluateCatmullRom(points, t) {
1497
1504
  if (N === 1) {
1498
1505
  return { x: points[0][0], y: points[0][1] };
1499
1506
  }
1500
- t = ((t % PERIOD) + PERIOD) % PERIOD;
1507
+ t = (t % PERIOD + PERIOD) % PERIOD;
1501
1508
  const segmentSize = PERIOD / N;
1502
1509
  let i = Math.floor(t / segmentSize);
1503
1510
  if (i >= N) {
@@ -1511,7 +1518,7 @@ function evaluateCatmullRom(points, t) {
1511
1518
  const p3 = points[(i + 2) % N];
1512
1519
  return {
1513
1520
  x: catmullRom1D(p0[0], p1[0], p2[0], p3[0], u),
1514
- y: catmullRom1D(p0[1], p1[1], p2[1], p3[1], u),
1521
+ y: catmullRom1D(p0[1], p1[1], p2[1], p3[1], u)
1515
1522
  };
1516
1523
  }
1517
1524
  function drawCurve(points) {
@@ -1521,7 +1528,7 @@ function drawCurve(points) {
1521
1528
  return {
1522
1529
  name: "custom",
1523
1530
  fn: (t) => evaluateCatmullRom(points, t),
1524
- period: PERIOD,
1531
+ period: PERIOD
1525
1532
  };
1526
1533
  }
1527
1534
 
@@ -1552,4 +1559,4 @@ exports.palettes = palettes;
1552
1559
  exports.rose3 = rose3;
1553
1560
  exports.rose5 = rose5;
1554
1561
  //# sourceMappingURL=index.cjs.map
1555
- //# sourceMappingURL=index.cjs.map
1562
+ //# sourceMappingURL=index.cjs.map