@sarmal/core 0.22.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 +158 -139
  2. package/dist/auto-init.cjs.map +1 -1
  3. package/dist/auto-init.js +157 -138
  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 +150 -135
  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 +150 -155
  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-DVerJ9cl.d.cts +0 -321
  75. package/dist/types-DVerJ9cl.d.ts +0 -321
@@ -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,17 +369,17 @@ 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
  function hexToRgb(hex) {
393
376
  const n = parseInt(hex.slice(1), 16);
394
- return { r: n >> 16, g: (n >> 8) & 255, b: n & 255 };
377
+ return { r: n >> 16, g: n >> 8 & 255, b: n & 255 };
395
378
  }
396
379
  var lerpRgb = (a, b, t) => ({
397
380
  r: Math.round(a.r + (b.r - a.r) * t),
398
381
  g: Math.round(a.g + (b.g - a.g) * t),
399
- b: Math.round(a.b + (b.b - a.b) * t),
382
+ b: Math.round(a.b + (b.b - a.b) * t)
400
383
  });
401
384
  function getPaletteColor(palette, position, timeOffset = 0) {
402
385
  if (palette.length === 0) {
@@ -405,7 +388,7 @@ function getPaletteColor(palette, position, timeOffset = 0) {
405
388
  if (palette.length === 1) {
406
389
  return hexToRgb(palette[0]);
407
390
  }
408
- const cyclePos = (((position + timeOffset) % 1) + 1) % 1;
391
+ const cyclePos = ((position + timeOffset) % 1 + 1) % 1;
409
392
  const scaled = cyclePos * palette.length;
410
393
  const idx = Math.floor(scaled);
411
394
  const t = scaled - idx;
@@ -420,7 +403,7 @@ var RENDER_OPTION_KEYS = /* @__PURE__ */ new Set([
420
403
  "headColor",
421
404
  "skeletonColor",
422
405
  "trailStyle",
423
- "headRadius",
406
+ "headRadius"
424
407
  ]);
425
408
  function validateRenderOptions(partial) {
426
409
  for (const key of Object.keys(partial)) {
@@ -448,7 +431,7 @@ function assertTrailColor(value) {
448
431
  if (typeof value === "string") {
449
432
  if (!HEX_COLOR_RE.test(value)) {
450
433
  throw new TypeError(
451
- `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string, got "${value}"`,
434
+ `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string, got "${value}"`
452
435
  );
453
436
  }
454
437
  return;
@@ -456,21 +439,21 @@ function assertTrailColor(value) {
456
439
  if (Array.isArray(value)) {
457
440
  if (value.length < 2) {
458
441
  throw new RangeError(
459
- `[sarmal] setRenderOptions: trailColor array must have at least 2 entries, got ${value.length}`,
442
+ `[sarmal] setRenderOptions: trailColor array must have at least 2 entries, got ${value.length}`
460
443
  );
461
444
  }
462
445
  for (let i = 0; i < value.length; i++) {
463
446
  const entry = value[i];
464
447
  if (typeof entry !== "string" || !HEX_COLOR_RE.test(entry)) {
465
448
  throw new TypeError(
466
- `[sarmal] setRenderOptions: trailColor[${i}] must be a 6-digit hex string, got ${JSON.stringify(entry)}`,
449
+ `[sarmal] setRenderOptions: trailColor[${i}] must be a 6-digit hex string, got ${JSON.stringify(entry)}`
467
450
  );
468
451
  }
469
452
  }
470
453
  return;
471
454
  }
472
455
  throw new TypeError(
473
- `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string or an array of hex strings, got ${JSON.stringify(value)}`,
456
+ `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string or an array of hex strings, got ${JSON.stringify(value)}`
474
457
  );
475
458
  }
476
459
  function assertHeadColor(value) {
@@ -479,7 +462,7 @@ function assertHeadColor(value) {
479
462
  }
480
463
  if (typeof value !== "string" || !HEX_COLOR_RE.test(value)) {
481
464
  throw new TypeError(
482
- `[sarmal] setRenderOptions: headColor must be a 6-digit hex string or null, got ${JSON.stringify(value)}`,
465
+ `[sarmal] setRenderOptions: headColor must be a 6-digit hex string or null, got ${JSON.stringify(value)}`
483
466
  );
484
467
  }
485
468
  }
@@ -489,26 +472,26 @@ function assertSkeletonColor(value) {
489
472
  }
490
473
  if (typeof value !== "string" || !HEX_COLOR_RE.test(value)) {
491
474
  throw new TypeError(
492
- `[sarmal] setRenderOptions: skeletonColor must be a 6-digit hex string or "transparent", got ${JSON.stringify(value)}`,
475
+ `[sarmal] setRenderOptions: skeletonColor must be a 6-digit hex string or "transparent", got ${JSON.stringify(value)}`
493
476
  );
494
477
  }
495
478
  }
496
479
  function assertTrailStyle(value) {
497
480
  if (!TRAIL_STYLES.includes(value)) {
498
481
  throw new RangeError(
499
- `[sarmal] setRenderOptions: trailStyle must be one of "default", "gradient-static", "gradient-animated", got ${JSON.stringify(value)}`,
482
+ `[sarmal] setRenderOptions: trailStyle must be one of "default", "gradient-static", "gradient-animated", got ${JSON.stringify(value)}`
500
483
  );
501
484
  }
502
485
  }
503
486
  function assertHeadRadius(value) {
504
487
  if (typeof value !== "number") {
505
488
  throw new TypeError(
506
- `[sarmal] setRenderOptions: headRadius must be a number, got ${JSON.stringify(value)}`,
489
+ `[sarmal] setRenderOptions: headRadius must be a number, got ${JSON.stringify(value)}`
507
490
  );
508
491
  }
509
492
  if (!Number.isFinite(value) || value <= 0) {
510
493
  throw new TypeError(
511
- `[sarmal] setRenderOptions: headRadius must be a finite positive number, got ${value}`,
494
+ `[sarmal] setRenderOptions: headRadius must be a finite positive number, got ${value}`
512
495
  );
513
496
  }
514
497
  }
@@ -530,23 +513,23 @@ function resolveHeadColor(trailColor, trailStyle) {
530
513
  function warnIfTrailColorMismatch(trailColor, trailStyle) {
531
514
  if (trailStyle === "default" && Array.isArray(trailColor)) {
532
515
  console.warn(
533
- '[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.',
516
+ '[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.'
534
517
  );
535
518
  return;
536
519
  }
537
520
  if (trailStyle !== "default" && typeof trailColor === "string") {
538
521
  console.warn(
539
- `[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.`,
522
+ `[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.`
540
523
  );
541
524
  }
542
525
  }
543
- var getHeadDotRadius = (w, h) => Math.max(1, 3 * Math.sqrt(Math.min(w, h) / 160));
544
526
 
545
527
  // src/renderer.ts
528
+ var getHeadDotRadius = (w, h) => Math.max(1, 3 * Math.sqrt(Math.min(w, h) / 160));
546
529
  var WHITE_HEX = "#ffffff";
547
530
  function hexToRgbComponents(hex) {
548
531
  const n = parseInt(hex.slice(1), 16);
549
- return `${n >> 16},${(n >> 8) & 255},${n & 255}`;
532
+ return `${n >> 16},${n >> 8 & 255},${n & 255}`;
550
533
  }
551
534
  function applyDprSizing(target, logicalWidth, logicalHeight, dpr) {
552
535
  target.style.width = `${logicalWidth}px`;
@@ -590,6 +573,7 @@ function createRenderer(options) {
590
573
  let offsetY = 0;
591
574
  let animationId = null;
592
575
  let lastTime = 0;
576
+ let pausedByVisibility = false;
593
577
  let morphResolve = null;
594
578
  let morphReject = null;
595
579
  let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
@@ -672,7 +656,7 @@ function createRenderer(options) {
672
656
  i,
673
657
  trailCount,
674
658
  toX,
675
- toY,
659
+ toY
676
660
  );
677
661
  if (trailStyle === "default") {
678
662
  ctx.fillStyle = `rgba(${trailSolidRgb},${opacity})`;
@@ -783,6 +767,7 @@ function createRenderer(options) {
783
767
  cancelAnimationFrame(animationId);
784
768
  animationId = null;
785
769
  }
770
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
786
771
  if (morphReject !== null) {
787
772
  morphReject(new Error("Instance destroyed during morph"));
788
773
  morphResolve = null;
@@ -836,16 +821,38 @@ function createRenderer(options) {
836
821
  if (partial.trailColor !== void 0 || partial.trailStyle !== void 0) {
837
822
  warnIfTrailColorMismatch(trailColor, trailStyle);
838
823
  }
839
- },
824
+ }
840
825
  };
841
- if (shouldAutoStart) {
826
+ const pauseOnHidden = options.pauseOnHidden !== false;
827
+ function handleVisibilityChange() {
828
+ if (document.hidden) {
829
+ if (animationId !== null) {
830
+ instance.pause();
831
+ pausedByVisibility = true;
832
+ }
833
+ } else {
834
+ if (pausedByVisibility) {
835
+ pausedByVisibility = false;
836
+ instance.play();
837
+ }
838
+ }
839
+ }
840
+ if (pauseOnHidden) {
841
+ document.addEventListener("visibilitychange", handleVisibilityChange);
842
+ }
843
+ const actuallyAutoStart = shouldAutoStart && !(pauseOnHidden && document.hidden);
844
+ if (actuallyAutoStart) {
842
845
  instance.play();
846
+ } else if (shouldAutoStart) {
847
+ pausedByVisibility = true;
843
848
  }
844
849
  return instance;
845
850
  }
846
851
 
847
852
  // src/renderer-svg.ts
848
853
  var EMPTY_PARAMS2 = {};
854
+ var SVG_DEFAULT_HEAD_RADIUS = 0.5;
855
+ var SKELETON_STROKE_WIDTH_PX = 1.5;
849
856
  var HIGH_TRAIL_LENGTH_THRESHOLD = 5e3;
850
857
  function pointsToPathString(pts, scale, offsetX, offsetY) {
851
858
  if (pts.length < 2) {
@@ -864,7 +871,7 @@ function sampleCurveSkeleton(curveDef) {
864
871
  const samples = Math.ceil(period * 50);
865
872
  const pts = Array.from({ length: samples });
866
873
  for (let i = 0; i < samples; i++) {
867
- const t = (i / (samples - 1)) * period;
874
+ const t = i / (samples - 1) * period;
868
875
  pts[i] = curveDef.skeletonFn ? curveDef.skeletonFn(t) : curveDef.fn(t, 0, EMPTY_PARAMS2);
869
876
  }
870
877
  return pts;
@@ -877,7 +884,7 @@ function createSVGRenderer(options) {
877
884
  const poolSize = engine.trailLength;
878
885
  if (poolSize > HIGH_TRAIL_LENGTH_THRESHOLD) {
879
886
  console.warn(
880
- `[sarmal] High trailLength in SVG renderer (${poolSize}). Consider using the canvas renderer for long trails.`,
887
+ `[sarmal] High trailLength in SVG renderer (${poolSize}). Consider using the canvas renderer for long trails.`
881
888
  );
882
889
  }
883
890
  let trailStyle = options.trailStyle ?? "default";
@@ -885,15 +892,21 @@ function createSVGRenderer(options) {
885
892
  let skeletonColor = options.skeletonColor ?? "#ffffff";
886
893
  let userHeadColor = options.headColor ?? null;
887
894
  let headColor = userHeadColor ?? resolveHeadColor(trailColor, trailStyle);
888
- let headRadius = options.headRadius ?? 1.5;
895
+ let headRadius;
889
896
  let trailSolid = resolveTrailMainColor(trailColor);
890
897
  let trailPalette = resolveTrailPalette(trailColor);
891
898
  const ariaLabel = options.ariaLabel ?? "Loading";
892
899
  warnIfTrailColorMismatch(trailColor, trailStyle);
893
900
  const viewSize = 100;
894
- const svgTrailMinWidth = 0.25;
895
- const svgTrailMaxWidth = 1.25;
896
- const svgSkeletonStrokeWidth = "0.75";
901
+ function getContainerPixelSize() {
902
+ const rect = container.getBoundingClientRect();
903
+ return rect.width && rect.height ? Math.min(rect.width, rect.height) : 200;
904
+ }
905
+ const containerPx = getContainerPixelSize();
906
+ const svgTrailMinWidth = TRAIL_MIN_WIDTH * viewSize / containerPx;
907
+ const svgTrailMaxWidth = TRAIL_MAX_WIDTH * viewSize / containerPx;
908
+ const svgSkeletonStrokeWidth = String(SKELETON_STROKE_WIDTH_PX * viewSize / containerPx);
909
+ headRadius = options.headRadius ?? SVG_DEFAULT_HEAD_RADIUS;
897
910
  container.setAttribute("viewBox", `0 0 ${viewSize} ${viewSize}`);
898
911
  container.setAttribute("role", "img");
899
912
  container.setAttribute("aria-label", ariaLabel);
@@ -980,7 +993,7 @@ function createSVGRenderer(options) {
980
993
  px,
981
994
  py,
982
995
  svgTrailMinWidth,
983
- svgTrailMaxWidth,
996
+ svgTrailMaxWidth
984
997
  );
985
998
  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`;
986
999
  trailPaths[i].setAttribute("d", d);
@@ -1007,8 +1020,8 @@ function createSVGRenderer(options) {
1007
1020
  }
1008
1021
  let animationId = null;
1009
1022
  let lastTime = 0;
1010
- const prefersReducedMotion =
1011
- typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1023
+ let pausedByVisibility = false;
1024
+ const prefersReducedMotion = typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1012
1025
  let morphResolve = null;
1013
1026
  let morphReject = null;
1014
1027
  let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
@@ -1026,7 +1039,7 @@ function createSVGRenderer(options) {
1026
1039
  skeletonPathA.setAttribute("visibility", "visible");
1027
1040
  skeletonPathA.setAttribute(
1028
1041
  "stroke-opacity",
1029
- String((1 - morphAlpha) * DEFAULT_SKELETON_OPACITY),
1042
+ String((1 - morphAlpha) * DEFAULT_SKELETON_OPACITY)
1030
1043
  );
1031
1044
  }
1032
1045
  if (morphPathBBuilt) {
@@ -1098,6 +1111,7 @@ function createSVGRenderer(options) {
1098
1111
  cancelAnimationFrame(animationId);
1099
1112
  animationId = null;
1100
1113
  }
1114
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
1101
1115
  if (morphReject !== null) {
1102
1116
  morphReject(new Error("Instance destroyed during morph"));
1103
1117
  morphResolve = null;
@@ -1179,10 +1193,30 @@ function createSVGRenderer(options) {
1179
1193
  if (partial.trailColor !== void 0 || partial.trailStyle !== void 0) {
1180
1194
  warnIfTrailColorMismatch(trailColor, trailStyle);
1181
1195
  }
1182
- },
1196
+ }
1183
1197
  };
1184
- if (shouldAutoStart) {
1198
+ const pauseOnHidden = options.pauseOnHidden !== false;
1199
+ function handleVisibilityChange() {
1200
+ if (document.hidden) {
1201
+ if (animationId !== null) {
1202
+ instance.pause();
1203
+ pausedByVisibility = true;
1204
+ }
1205
+ } else {
1206
+ if (pausedByVisibility) {
1207
+ pausedByVisibility = false;
1208
+ instance.play();
1209
+ }
1210
+ }
1211
+ }
1212
+ if (pauseOnHidden) {
1213
+ document.addEventListener("visibilitychange", handleVisibilityChange);
1214
+ }
1215
+ const actuallyAutoStart = shouldAutoStart && !(pauseOnHidden && document.hidden);
1216
+ if (actuallyAutoStart) {
1185
1217
  instance.play();
1218
+ } else if (shouldAutoStart) {
1219
+ pausedByVisibility = true;
1186
1220
  }
1187
1221
  return instance;
1188
1222
  }
@@ -1195,22 +1229,19 @@ function createSarmalSVG(container, curveDef, options) {
1195
1229
  // src/curves/artemis2.ts
1196
1230
  var TWO_PI2 = Math.PI * 2;
1197
1231
  function artemis2Fn(t, _time, _params) {
1198
- const a = 0.35,
1199
- b = 0.15,
1200
- ox = 0.175;
1201
- const s = Math.sin(t),
1202
- c = Math.cos(t);
1232
+ const a = 0.35, b = 0.15, ox = 0.175;
1233
+ const s = Math.sin(t), c = Math.cos(t);
1203
1234
  const denom = 1 + s * s;
1204
1235
  return {
1205
- x: (c * (1 + a * c)) / denom - ox,
1206
- y: (s * c * (1 + b * c)) / denom,
1236
+ x: c * (1 + a * c) / denom - ox,
1237
+ y: s * c * (1 + b * c) / denom
1207
1238
  };
1208
1239
  }
1209
1240
  var artemis2 = {
1210
1241
  name: "Artemis II",
1211
1242
  fn: artemis2Fn,
1212
1243
  period: TWO_PI2,
1213
- speed: 0.7,
1244
+ speed: 0.7
1214
1245
  };
1215
1246
 
1216
1247
  // src/curves/astroid.ts
@@ -1220,14 +1251,14 @@ function astroidFn(t, _time, _params) {
1220
1251
  const s = Math.sin(t);
1221
1252
  return {
1222
1253
  x: c * c * c,
1223
- y: s * s * s,
1254
+ y: s * s * s
1224
1255
  };
1225
1256
  }
1226
1257
  var astroid = {
1227
1258
  name: "Astroid",
1228
1259
  fn: astroidFn,
1229
1260
  period: TWO_PI3,
1230
- speed: 1.1,
1261
+ speed: 1.1
1231
1262
  };
1232
1263
 
1233
1264
  // src/curves/deltoid.ts
@@ -1235,14 +1266,14 @@ var TWO_PI4 = Math.PI * 2;
1235
1266
  function deltoidFn(t, _time, _params) {
1236
1267
  return {
1237
1268
  x: 2 * Math.cos(t) + Math.cos(2 * t),
1238
- y: 2 * Math.sin(t) - Math.sin(2 * t),
1269
+ y: 2 * Math.sin(t) - Math.sin(2 * t)
1239
1270
  };
1240
1271
  }
1241
1272
  var deltoid = {
1242
1273
  name: "Deltoid",
1243
1274
  fn: deltoidFn,
1244
1275
  period: TWO_PI4,
1245
- speed: 0.9,
1276
+ speed: 0.9
1246
1277
  };
1247
1278
 
1248
1279
  // src/curves/epicycloid3.ts
@@ -1250,14 +1281,14 @@ var TWO_PI5 = Math.PI * 2;
1250
1281
  function epicycloid3Fn(t, _time, _params) {
1251
1282
  return {
1252
1283
  x: 4 * Math.cos(t) - Math.cos(4 * t),
1253
- y: 4 * Math.sin(t) - Math.sin(4 * t),
1284
+ y: 4 * Math.sin(t) - Math.sin(4 * t)
1254
1285
  };
1255
1286
  }
1256
1287
  var epicycloid3 = {
1257
1288
  name: "Epicycloid (n=3)",
1258
1289
  fn: epicycloid3Fn,
1259
1290
  period: TWO_PI5,
1260
- speed: 0.75,
1291
+ speed: 0.75
1261
1292
  };
1262
1293
 
1263
1294
  // src/curves/epitrochoid7.ts
@@ -1266,14 +1297,14 @@ function epitrochoid7Fn(t, _time, _params) {
1266
1297
  const d = 1 + 0.55 * Math.sin(t * 0.5);
1267
1298
  return {
1268
1299
  x: 7 * Math.cos(t) - d * Math.cos(7 * t),
1269
- y: 7 * Math.sin(t) - d * Math.sin(7 * t),
1300
+ y: 7 * Math.sin(t) - d * Math.sin(7 * t)
1270
1301
  };
1271
1302
  }
1272
1303
  function epitrochoid7SkeletonFn(t) {
1273
1304
  const d = 1.275;
1274
1305
  return {
1275
1306
  x: 7 * Math.cos(t) - d * Math.cos(7 * t),
1276
- y: 7 * Math.sin(t) - d * Math.sin(7 * t),
1307
+ y: 7 * Math.sin(t) - d * Math.sin(7 * t)
1277
1308
  };
1278
1309
  }
1279
1310
  var epitrochoid7 = {
@@ -1281,7 +1312,7 @@ var epitrochoid7 = {
1281
1312
  fn: epitrochoid7Fn,
1282
1313
  period: TWO_PI6,
1283
1314
  speed: 1.4,
1284
- skeletonFn: epitrochoid7SkeletonFn,
1315
+ skeletonFn: epitrochoid7SkeletonFn
1285
1316
  };
1286
1317
 
1287
1318
  // src/curves/lissajous32.ts
@@ -1290,7 +1321,7 @@ function lissajous32Fn(t, time, _params) {
1290
1321
  const phi = time * 0.45;
1291
1322
  return {
1292
1323
  x: Math.sin(3 * t + phi),
1293
- y: Math.sin(2 * t),
1324
+ y: Math.sin(2 * t)
1294
1325
  };
1295
1326
  }
1296
1327
  var lissajous32 = {
@@ -1298,7 +1329,7 @@ var lissajous32 = {
1298
1329
  fn: lissajous32Fn,
1299
1330
  period: TWO_PI7,
1300
1331
  speed: 2,
1301
- skeleton: "live",
1332
+ skeleton: "live"
1302
1333
  };
1303
1334
 
1304
1335
  // src/curves/lissajous43.ts
@@ -1307,7 +1338,7 @@ function lissajous43Fn(t, time, _params) {
1307
1338
  const phi = time * 0.38;
1308
1339
  return {
1309
1340
  x: Math.sin(4 * t + phi),
1310
- y: Math.sin(3 * t),
1341
+ y: Math.sin(3 * t)
1311
1342
  };
1312
1343
  }
1313
1344
  var lissajous43 = {
@@ -1315,18 +1346,17 @@ var lissajous43 = {
1315
1346
  fn: lissajous43Fn,
1316
1347
  period: TWO_PI8,
1317
1348
  speed: 1.8,
1318
- skeleton: "live",
1349
+ skeleton: "live"
1319
1350
  };
1320
1351
 
1321
1352
  // src/curves/lame.ts
1322
1353
  var TWO_PI9 = Math.PI * 2;
1323
1354
  function lameFn(t, time, _params) {
1324
1355
  const p = 1.75 + 1.25 * Math.sin(time * 0.48);
1325
- const c = Math.cos(t),
1326
- s = Math.sin(t);
1356
+ const c = Math.cos(t), s = Math.sin(t);
1327
1357
  return {
1328
1358
  x: Math.sign(c) * Math.pow(Math.abs(c), p),
1329
- y: Math.sign(s) * Math.pow(Math.abs(s), p),
1359
+ y: Math.sign(s) * Math.pow(Math.abs(s), p)
1330
1360
  };
1331
1361
  }
1332
1362
  var lame = {
@@ -1334,7 +1364,7 @@ var lame = {
1334
1364
  fn: lameFn,
1335
1365
  period: TWO_PI9,
1336
1366
  speed: 1,
1337
- skeleton: "live",
1367
+ skeleton: "live"
1338
1368
  };
1339
1369
 
1340
1370
  // src/curves/rose3.ts
@@ -1343,14 +1373,14 @@ function rose3Fn(t, _time, _params) {
1343
1373
  const r = Math.cos(3 * t);
1344
1374
  return {
1345
1375
  x: r * Math.cos(t),
1346
- y: r * Math.sin(t),
1376
+ y: r * Math.sin(t)
1347
1377
  };
1348
1378
  }
1349
1379
  var rose3 = {
1350
1380
  name: "Rose (n=3)",
1351
1381
  fn: rose3Fn,
1352
1382
  period: TWO_PI10,
1353
- speed: 1.15,
1383
+ speed: 1.15
1354
1384
  };
1355
1385
 
1356
1386
  // src/curves/rose5.ts
@@ -1359,87 +1389,78 @@ function rose5Fn(t, _time, _params) {
1359
1389
  const r = Math.cos(5 * t);
1360
1390
  return {
1361
1391
  x: r * Math.cos(t),
1362
- y: r * Math.sin(t),
1392
+ y: r * Math.sin(t)
1363
1393
  };
1364
1394
  }
1365
1395
  var rose5 = {
1366
1396
  name: "Rose (n=5)",
1367
1397
  fn: rose5Fn,
1368
1398
  period: TWO_PI11,
1369
- speed: 1,
1399
+ speed: 1
1370
1400
  };
1371
1401
 
1372
1402
  // src/curves/rose52.ts
1373
1403
  var FOUR_PI = Math.PI * 4;
1374
1404
  function rose52Fn(t, _time, _params) {
1375
- const r = Math.cos((5 / 2) * t);
1405
+ const r = Math.cos(5 / 2 * t);
1376
1406
  return {
1377
1407
  x: r * Math.cos(t),
1378
- y: r * Math.sin(t),
1408
+ y: r * Math.sin(t)
1379
1409
  };
1380
1410
  }
1381
1411
  var rose52 = {
1382
1412
  name: "Rose (n=5/2)",
1383
1413
  fn: rose52Fn,
1384
1414
  period: FOUR_PI,
1385
- speed: 0.8,
1415
+ speed: 0.8
1386
1416
  };
1387
1417
 
1388
1418
  // src/curves/star.ts
1389
1419
  var TWO_PI12 = Math.PI * 2;
1390
1420
  function starFn(t, _time, _params) {
1391
- const r =
1392
- Math.abs(Math.cos((5 / 2) * t)) +
1393
- 0.35 * Math.abs(Math.cos((15 / 2) * t)) +
1394
- 0.15 * Math.abs(Math.cos((25 / 2) * t));
1421
+ 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));
1395
1422
  return {
1396
1423
  x: r * Math.cos(t),
1397
- y: r * Math.sin(t),
1424
+ y: r * Math.sin(t)
1398
1425
  };
1399
1426
  }
1400
1427
  var star = {
1401
1428
  name: "Star",
1402
1429
  fn: starFn,
1403
1430
  period: TWO_PI12,
1404
- speed: 1,
1431
+ speed: 1
1405
1432
  };
1406
1433
 
1407
1434
  // src/curves/star4.ts
1408
1435
  var TWO_PI13 = Math.PI * 2;
1409
1436
  function star4Fn(t, _time, _params) {
1410
- const r =
1411
- Math.abs(Math.cos(2 * t)) +
1412
- 0.35 * Math.abs(Math.cos(6 * t)) +
1413
- 0.15 * Math.abs(Math.cos(10 * t));
1437
+ const r = Math.abs(Math.cos(2 * t)) + 0.35 * Math.abs(Math.cos(6 * t)) + 0.15 * Math.abs(Math.cos(10 * t));
1414
1438
  return {
1415
1439
  x: r * Math.cos(t),
1416
- y: r * Math.sin(t),
1440
+ y: r * Math.sin(t)
1417
1441
  };
1418
1442
  }
1419
1443
  var star4 = {
1420
1444
  name: "Star (4-arm)",
1421
1445
  fn: star4Fn,
1422
1446
  period: TWO_PI13,
1423
- speed: 1,
1447
+ speed: 1
1424
1448
  };
1425
1449
 
1426
1450
  // src/curves/star7.ts
1427
1451
  var TWO_PI14 = Math.PI * 2;
1428
1452
  function star7Fn(t, _time, _params) {
1429
- const r =
1430
- Math.abs(Math.cos((7 / 2) * t)) +
1431
- 0.35 * Math.abs(Math.cos((21 / 2) * t)) +
1432
- 0.15 * Math.abs(Math.cos((35 / 2) * t));
1453
+ 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));
1433
1454
  return {
1434
1455
  x: r * Math.cos(t),
1435
- y: r * Math.sin(t),
1456
+ y: r * Math.sin(t)
1436
1457
  };
1437
1458
  }
1438
1459
  var star7 = {
1439
1460
  name: "Star (7-arm)",
1440
1461
  fn: star7Fn,
1441
1462
  period: TWO_PI14,
1442
- speed: 1,
1463
+ speed: 1
1443
1464
  };
1444
1465
 
1445
1466
  // src/curves/index.ts
@@ -1457,7 +1478,7 @@ var curves = {
1457
1478
  lissajous32,
1458
1479
  lissajous43,
1459
1480
  epicycloid3,
1460
- lame,
1481
+ lame
1461
1482
  };
1462
1483
 
1463
1484
  // src/index.ts
@@ -1474,21 +1495,22 @@ function parseTrailColor(value) {
1474
1495
  if (Array.isArray(parsed)) {
1475
1496
  return parsed;
1476
1497
  }
1477
- } catch {}
1498
+ } catch {
1499
+ }
1478
1500
  return value;
1479
1501
  }
1480
1502
  function buildOptions(el2) {
1481
1503
  return {
1482
- ...(el2.dataset.trailColor && {
1483
- trailColor: parseTrailColor(el2.dataset.trailColor),
1484
- }),
1485
- ...(el2.dataset.skeletonColor && { skeletonColor: el2.dataset.skeletonColor }),
1486
- ...(el2.dataset.headColor && { headColor: el2.dataset.headColor }),
1487
- ...(el2.dataset.headRadius && { headRadius: parseFloat(el2.dataset.headRadius) }),
1488
- ...(el2.dataset.trailLength && { trailLength: parseInt(el2.dataset.trailLength, 10) }),
1489
- ...(el2.dataset.trailStyle && {
1490
- trailStyle: el2.dataset.trailStyle,
1491
- }),
1504
+ ...el2.dataset.trailColor && {
1505
+ trailColor: parseTrailColor(el2.dataset.trailColor)
1506
+ },
1507
+ ...el2.dataset.skeletonColor && { skeletonColor: el2.dataset.skeletonColor },
1508
+ ...el2.dataset.headColor && { headColor: el2.dataset.headColor },
1509
+ ...el2.dataset.headRadius && { headRadius: parseFloat(el2.dataset.headRadius) },
1510
+ ...el2.dataset.trailLength && { trailLength: parseInt(el2.dataset.trailLength, 10) },
1511
+ ...el2.dataset.trailStyle && {
1512
+ trailStyle: el2.dataset.trailStyle
1513
+ }
1492
1514
  };
1493
1515
  }
1494
1516
  function init() {
@@ -1503,10 +1525,7 @@ function init() {
1503
1525
  return console.error(`[sarmal] "${curveName}" is not a valid curve name`);
1504
1526
  }
1505
1527
  const options = buildOptions(el2);
1506
- const instance =
1507
- el2 instanceof HTMLCanvasElement
1508
- ? createSarmal(el2, curveDef, options)
1509
- : createSarmalSVG(el2, curveDef, options);
1528
+ const instance = el2 instanceof HTMLCanvasElement ? createSarmal(el2, curveDef, options) : createSarmalSVG(el2, curveDef, options);
1510
1529
  if (el2.dataset.speed) {
1511
1530
  instance.setSpeed(parseFloat(el2.dataset.speed));
1512
1531
  }
@@ -1522,4 +1541,4 @@ if (document.readyState === "loading") {
1522
1541
 
1523
1542
  exports.init = init;
1524
1543
  //# sourceMappingURL=auto-init.cjs.map
1525
- //# sourceMappingURL=auto-init.cjs.map
1544
+ //# sourceMappingURL=auto-init.cjs.map