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