@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
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,23 +521,23 @@ 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
  }
551
- var getHeadDotRadius = (w, h) => Math.max(1, 3 * Math.sqrt(Math.min(w, h) / 160));
552
534
 
553
535
  // src/renderer.ts
536
+ 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,16 +829,38 @@ 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
  }
854
859
 
855
860
  // src/renderer-svg.ts
856
861
  var EMPTY_PARAMS2 = {};
862
+ var SVG_DEFAULT_HEAD_RADIUS = 0.5;
863
+ var SKELETON_STROKE_WIDTH_PX = 1.5;
857
864
  var HIGH_TRAIL_LENGTH_THRESHOLD = 5e3;
858
865
  function pointsToPathString(pts, scale, offsetX, offsetY) {
859
866
  if (pts.length < 2) {
@@ -872,7 +879,7 @@ function sampleCurveSkeleton(curveDef) {
872
879
  const samples = Math.ceil(period * 50);
873
880
  const pts = Array.from({ length: samples });
874
881
  for (let i = 0; i < samples; i++) {
875
- const t = (i / (samples - 1)) * period;
882
+ const t = i / (samples - 1) * period;
876
883
  pts[i] = curveDef.skeletonFn ? curveDef.skeletonFn(t) : curveDef.fn(t, 0, EMPTY_PARAMS2);
877
884
  }
878
885
  return pts;
@@ -885,7 +892,7 @@ function createSVGRenderer(options) {
885
892
  const poolSize = engine.trailLength;
886
893
  if (poolSize > HIGH_TRAIL_LENGTH_THRESHOLD) {
887
894
  console.warn(
888
- `[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.`
889
896
  );
890
897
  }
891
898
  let trailStyle = options.trailStyle ?? "default";
@@ -893,15 +900,21 @@ function createSVGRenderer(options) {
893
900
  let skeletonColor = options.skeletonColor ?? "#ffffff";
894
901
  let userHeadColor = options.headColor ?? null;
895
902
  let headColor = userHeadColor ?? resolveHeadColor(trailColor, trailStyle);
896
- let headRadius = options.headRadius ?? 1.5;
903
+ let headRadius;
897
904
  let trailSolid = resolveTrailMainColor(trailColor);
898
905
  let trailPalette = resolveTrailPalette(trailColor);
899
906
  const ariaLabel = options.ariaLabel ?? "Loading";
900
907
  warnIfTrailColorMismatch(trailColor, trailStyle);
901
908
  const viewSize = 100;
902
- const svgTrailMinWidth = 0.25;
903
- const svgTrailMaxWidth = 1.25;
904
- const svgSkeletonStrokeWidth = "0.75";
909
+ function getContainerPixelSize() {
910
+ const rect = container.getBoundingClientRect();
911
+ return rect.width && rect.height ? Math.min(rect.width, rect.height) : 200;
912
+ }
913
+ const containerPx = getContainerPixelSize();
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);
917
+ headRadius = options.headRadius ?? SVG_DEFAULT_HEAD_RADIUS;
905
918
  container.setAttribute("viewBox", `0 0 ${viewSize} ${viewSize}`);
906
919
  container.setAttribute("role", "img");
907
920
  container.setAttribute("aria-label", ariaLabel);
@@ -988,7 +1001,7 @@ function createSVGRenderer(options) {
988
1001
  px,
989
1002
  py,
990
1003
  svgTrailMinWidth,
991
- svgTrailMaxWidth,
1004
+ svgTrailMaxWidth
992
1005
  );
993
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`;
994
1007
  trailPaths[i].setAttribute("d", d);
@@ -1015,8 +1028,8 @@ function createSVGRenderer(options) {
1015
1028
  }
1016
1029
  let animationId = null;
1017
1030
  let lastTime = 0;
1018
- const prefersReducedMotion =
1019
- 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;
1020
1033
  let morphResolve = null;
1021
1034
  let morphReject = null;
1022
1035
  let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
@@ -1034,7 +1047,7 @@ function createSVGRenderer(options) {
1034
1047
  skeletonPathA.setAttribute("visibility", "visible");
1035
1048
  skeletonPathA.setAttribute(
1036
1049
  "stroke-opacity",
1037
- String((1 - morphAlpha) * DEFAULT_SKELETON_OPACITY),
1050
+ String((1 - morphAlpha) * DEFAULT_SKELETON_OPACITY)
1038
1051
  );
1039
1052
  }
1040
1053
  if (morphPathBBuilt) {
@@ -1106,6 +1119,7 @@ function createSVGRenderer(options) {
1106
1119
  cancelAnimationFrame(animationId);
1107
1120
  animationId = null;
1108
1121
  }
1122
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
1109
1123
  if (morphReject !== null) {
1110
1124
  morphReject(new Error("Instance destroyed during morph"));
1111
1125
  morphResolve = null;
@@ -1187,10 +1201,30 @@ function createSVGRenderer(options) {
1187
1201
  if (partial.trailColor !== void 0 || partial.trailStyle !== void 0) {
1188
1202
  warnIfTrailColorMismatch(trailColor, trailStyle);
1189
1203
  }
1190
- },
1204
+ }
1191
1205
  };
1192
- 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) {
1193
1225
  instance.play();
1226
+ } else if (shouldAutoStart) {
1227
+ pausedByVisibility = true;
1194
1228
  }
1195
1229
  return instance;
1196
1230
  }
@@ -1203,22 +1237,19 @@ function createSarmalSVG(container, curveDef, options) {
1203
1237
  // src/curves/artemis2.ts
1204
1238
  var TWO_PI2 = Math.PI * 2;
1205
1239
  function artemis2Fn(t, _time, _params) {
1206
- const a = 0.35,
1207
- b = 0.15,
1208
- ox = 0.175;
1209
- const s = Math.sin(t),
1210
- 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);
1211
1242
  const denom = 1 + s * s;
1212
1243
  return {
1213
- x: (c * (1 + a * c)) / denom - ox,
1214
- y: (s * c * (1 + b * c)) / denom,
1244
+ x: c * (1 + a * c) / denom - ox,
1245
+ y: s * c * (1 + b * c) / denom
1215
1246
  };
1216
1247
  }
1217
1248
  var artemis2 = {
1218
1249
  name: "Artemis II",
1219
1250
  fn: artemis2Fn,
1220
1251
  period: TWO_PI2,
1221
- speed: 0.7,
1252
+ speed: 0.7
1222
1253
  };
1223
1254
 
1224
1255
  // src/curves/astroid.ts
@@ -1228,14 +1259,14 @@ function astroidFn(t, _time, _params) {
1228
1259
  const s = Math.sin(t);
1229
1260
  return {
1230
1261
  x: c * c * c,
1231
- y: s * s * s,
1262
+ y: s * s * s
1232
1263
  };
1233
1264
  }
1234
1265
  var astroid = {
1235
1266
  name: "Astroid",
1236
1267
  fn: astroidFn,
1237
1268
  period: TWO_PI3,
1238
- speed: 1.1,
1269
+ speed: 1.1
1239
1270
  };
1240
1271
 
1241
1272
  // src/curves/deltoid.ts
@@ -1243,14 +1274,14 @@ var TWO_PI4 = Math.PI * 2;
1243
1274
  function deltoidFn(t, _time, _params) {
1244
1275
  return {
1245
1276
  x: 2 * Math.cos(t) + Math.cos(2 * t),
1246
- y: 2 * Math.sin(t) - Math.sin(2 * t),
1277
+ y: 2 * Math.sin(t) - Math.sin(2 * t)
1247
1278
  };
1248
1279
  }
1249
1280
  var deltoid = {
1250
1281
  name: "Deltoid",
1251
1282
  fn: deltoidFn,
1252
1283
  period: TWO_PI4,
1253
- speed: 0.9,
1284
+ speed: 0.9
1254
1285
  };
1255
1286
 
1256
1287
  // src/curves/epicycloid3.ts
@@ -1258,14 +1289,14 @@ var TWO_PI5 = Math.PI * 2;
1258
1289
  function epicycloid3Fn(t, _time, _params) {
1259
1290
  return {
1260
1291
  x: 4 * Math.cos(t) - Math.cos(4 * t),
1261
- y: 4 * Math.sin(t) - Math.sin(4 * t),
1292
+ y: 4 * Math.sin(t) - Math.sin(4 * t)
1262
1293
  };
1263
1294
  }
1264
1295
  var epicycloid3 = {
1265
1296
  name: "Epicycloid (n=3)",
1266
1297
  fn: epicycloid3Fn,
1267
1298
  period: TWO_PI5,
1268
- speed: 0.75,
1299
+ speed: 0.75
1269
1300
  };
1270
1301
 
1271
1302
  // src/curves/epitrochoid7.ts
@@ -1274,14 +1305,14 @@ function epitrochoid7Fn(t, _time, _params) {
1274
1305
  const d = 1 + 0.55 * Math.sin(t * 0.5);
1275
1306
  return {
1276
1307
  x: 7 * Math.cos(t) - d * Math.cos(7 * t),
1277
- y: 7 * Math.sin(t) - d * Math.sin(7 * t),
1308
+ y: 7 * Math.sin(t) - d * Math.sin(7 * t)
1278
1309
  };
1279
1310
  }
1280
1311
  function epitrochoid7SkeletonFn(t) {
1281
1312
  const d = 1.275;
1282
1313
  return {
1283
1314
  x: 7 * Math.cos(t) - d * Math.cos(7 * t),
1284
- y: 7 * Math.sin(t) - d * Math.sin(7 * t),
1315
+ y: 7 * Math.sin(t) - d * Math.sin(7 * t)
1285
1316
  };
1286
1317
  }
1287
1318
  var epitrochoid7 = {
@@ -1289,7 +1320,7 @@ var epitrochoid7 = {
1289
1320
  fn: epitrochoid7Fn,
1290
1321
  period: TWO_PI6,
1291
1322
  speed: 1.4,
1292
- skeletonFn: epitrochoid7SkeletonFn,
1323
+ skeletonFn: epitrochoid7SkeletonFn
1293
1324
  };
1294
1325
 
1295
1326
  // src/curves/lissajous32.ts
@@ -1298,7 +1329,7 @@ function lissajous32Fn(t, time, _params) {
1298
1329
  const phi = time * 0.45;
1299
1330
  return {
1300
1331
  x: Math.sin(3 * t + phi),
1301
- y: Math.sin(2 * t),
1332
+ y: Math.sin(2 * t)
1302
1333
  };
1303
1334
  }
1304
1335
  var lissajous32 = {
@@ -1306,7 +1337,7 @@ var lissajous32 = {
1306
1337
  fn: lissajous32Fn,
1307
1338
  period: TWO_PI7,
1308
1339
  speed: 2,
1309
- skeleton: "live",
1340
+ skeleton: "live"
1310
1341
  };
1311
1342
 
1312
1343
  // src/curves/lissajous43.ts
@@ -1315,7 +1346,7 @@ function lissajous43Fn(t, time, _params) {
1315
1346
  const phi = time * 0.38;
1316
1347
  return {
1317
1348
  x: Math.sin(4 * t + phi),
1318
- y: Math.sin(3 * t),
1349
+ y: Math.sin(3 * t)
1319
1350
  };
1320
1351
  }
1321
1352
  var lissajous43 = {
@@ -1323,18 +1354,17 @@ var lissajous43 = {
1323
1354
  fn: lissajous43Fn,
1324
1355
  period: TWO_PI8,
1325
1356
  speed: 1.8,
1326
- skeleton: "live",
1357
+ skeleton: "live"
1327
1358
  };
1328
1359
 
1329
1360
  // src/curves/lame.ts
1330
1361
  var TWO_PI9 = Math.PI * 2;
1331
1362
  function lameFn(t, time, _params) {
1332
1363
  const p = 1.75 + 1.25 * Math.sin(time * 0.48);
1333
- const c = Math.cos(t),
1334
- s = Math.sin(t);
1364
+ const c = Math.cos(t), s = Math.sin(t);
1335
1365
  return {
1336
1366
  x: Math.sign(c) * Math.pow(Math.abs(c), p),
1337
- y: Math.sign(s) * Math.pow(Math.abs(s), p),
1367
+ y: Math.sign(s) * Math.pow(Math.abs(s), p)
1338
1368
  };
1339
1369
  }
1340
1370
  var lame = {
@@ -1342,7 +1372,7 @@ var lame = {
1342
1372
  fn: lameFn,
1343
1373
  period: TWO_PI9,
1344
1374
  speed: 1,
1345
- skeleton: "live",
1375
+ skeleton: "live"
1346
1376
  };
1347
1377
 
1348
1378
  // src/curves/rose3.ts
@@ -1351,14 +1381,14 @@ function rose3Fn(t, _time, _params) {
1351
1381
  const r = Math.cos(3 * t);
1352
1382
  return {
1353
1383
  x: r * Math.cos(t),
1354
- y: r * Math.sin(t),
1384
+ y: r * Math.sin(t)
1355
1385
  };
1356
1386
  }
1357
1387
  var rose3 = {
1358
1388
  name: "Rose (n=3)",
1359
1389
  fn: rose3Fn,
1360
1390
  period: TWO_PI10,
1361
- speed: 1.15,
1391
+ speed: 1.15
1362
1392
  };
1363
1393
 
1364
1394
  // src/curves/rose5.ts
@@ -1367,87 +1397,78 @@ function rose5Fn(t, _time, _params) {
1367
1397
  const r = Math.cos(5 * t);
1368
1398
  return {
1369
1399
  x: r * Math.cos(t),
1370
- y: r * Math.sin(t),
1400
+ y: r * Math.sin(t)
1371
1401
  };
1372
1402
  }
1373
1403
  var rose5 = {
1374
1404
  name: "Rose (n=5)",
1375
1405
  fn: rose5Fn,
1376
1406
  period: TWO_PI11,
1377
- speed: 1,
1407
+ speed: 1
1378
1408
  };
1379
1409
 
1380
1410
  // src/curves/rose52.ts
1381
1411
  var FOUR_PI = Math.PI * 4;
1382
1412
  function rose52Fn(t, _time, _params) {
1383
- const r = Math.cos((5 / 2) * t);
1413
+ const r = Math.cos(5 / 2 * t);
1384
1414
  return {
1385
1415
  x: r * Math.cos(t),
1386
- y: r * Math.sin(t),
1416
+ y: r * Math.sin(t)
1387
1417
  };
1388
1418
  }
1389
1419
  var rose52 = {
1390
1420
  name: "Rose (n=5/2)",
1391
1421
  fn: rose52Fn,
1392
1422
  period: FOUR_PI,
1393
- speed: 0.8,
1423
+ speed: 0.8
1394
1424
  };
1395
1425
 
1396
1426
  // src/curves/star.ts
1397
1427
  var TWO_PI12 = Math.PI * 2;
1398
1428
  function starFn(t, _time, _params) {
1399
- const r =
1400
- Math.abs(Math.cos((5 / 2) * t)) +
1401
- 0.35 * Math.abs(Math.cos((15 / 2) * t)) +
1402
- 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));
1403
1430
  return {
1404
1431
  x: r * Math.cos(t),
1405
- y: r * Math.sin(t),
1432
+ y: r * Math.sin(t)
1406
1433
  };
1407
1434
  }
1408
1435
  var star = {
1409
1436
  name: "Star",
1410
1437
  fn: starFn,
1411
1438
  period: TWO_PI12,
1412
- speed: 1,
1439
+ speed: 1
1413
1440
  };
1414
1441
 
1415
1442
  // src/curves/star4.ts
1416
1443
  var TWO_PI13 = Math.PI * 2;
1417
1444
  function star4Fn(t, _time, _params) {
1418
- const r =
1419
- Math.abs(Math.cos(2 * t)) +
1420
- 0.35 * Math.abs(Math.cos(6 * t)) +
1421
- 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));
1422
1446
  return {
1423
1447
  x: r * Math.cos(t),
1424
- y: r * Math.sin(t),
1448
+ y: r * Math.sin(t)
1425
1449
  };
1426
1450
  }
1427
1451
  var star4 = {
1428
1452
  name: "Star (4-arm)",
1429
1453
  fn: star4Fn,
1430
1454
  period: TWO_PI13,
1431
- speed: 1,
1455
+ speed: 1
1432
1456
  };
1433
1457
 
1434
1458
  // src/curves/star7.ts
1435
1459
  var TWO_PI14 = Math.PI * 2;
1436
1460
  function star7Fn(t, _time, _params) {
1437
- const r =
1438
- Math.abs(Math.cos((7 / 2) * t)) +
1439
- 0.35 * Math.abs(Math.cos((21 / 2) * t)) +
1440
- 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));
1441
1462
  return {
1442
1463
  x: r * Math.cos(t),
1443
- y: r * Math.sin(t),
1464
+ y: r * Math.sin(t)
1444
1465
  };
1445
1466
  }
1446
1467
  var star7 = {
1447
1468
  name: "Star (7-arm)",
1448
1469
  fn: star7Fn,
1449
1470
  period: TWO_PI14,
1450
- speed: 1,
1471
+ speed: 1
1451
1472
  };
1452
1473
 
1453
1474
  // src/curves/index.ts
@@ -1465,7 +1486,7 @@ var curves = {
1465
1486
  lissajous32,
1466
1487
  lissajous43,
1467
1488
  epicycloid3,
1468
- lame,
1489
+ lame
1469
1490
  };
1470
1491
 
1471
1492
  // src/catmull-rom.ts
@@ -1473,13 +1494,7 @@ var PERIOD = 2 * Math.PI;
1473
1494
  function catmullRom1D(p0, p1, p2, p3, u) {
1474
1495
  const u2 = u * u;
1475
1496
  const u3 = u2 * u;
1476
- return (
1477
- 0.5 *
1478
- (2 * p1 +
1479
- (-p0 + p2) * u +
1480
- (2 * p0 - 5 * p1 + 4 * p2 - p3) * u2 +
1481
- (-p0 + 3 * p1 - 3 * p2 + p3) * u3)
1482
- );
1497
+ return 0.5 * (2 * p1 + (-p0 + p2) * u + (2 * p0 - 5 * p1 + 4 * p2 - p3) * u2 + (-p0 + 3 * p1 - 3 * p2 + p3) * u3);
1483
1498
  }
1484
1499
  function evaluateCatmullRom(points, t) {
1485
1500
  const N = points.length;
@@ -1489,7 +1504,7 @@ function evaluateCatmullRom(points, t) {
1489
1504
  if (N === 1) {
1490
1505
  return { x: points[0][0], y: points[0][1] };
1491
1506
  }
1492
- t = ((t % PERIOD) + PERIOD) % PERIOD;
1507
+ t = (t % PERIOD + PERIOD) % PERIOD;
1493
1508
  const segmentSize = PERIOD / N;
1494
1509
  let i = Math.floor(t / segmentSize);
1495
1510
  if (i >= N) {
@@ -1503,7 +1518,7 @@ function evaluateCatmullRom(points, t) {
1503
1518
  const p3 = points[(i + 2) % N];
1504
1519
  return {
1505
1520
  x: catmullRom1D(p0[0], p1[0], p2[0], p3[0], u),
1506
- y: catmullRom1D(p0[1], p1[1], p2[1], p3[1], u),
1521
+ y: catmullRom1D(p0[1], p1[1], p2[1], p3[1], u)
1507
1522
  };
1508
1523
  }
1509
1524
  function drawCurve(points) {
@@ -1513,7 +1528,7 @@ function drawCurve(points) {
1513
1528
  return {
1514
1529
  name: "custom",
1515
1530
  fn: (t) => evaluateCatmullRom(points, t),
1516
- period: PERIOD,
1531
+ period: PERIOD
1517
1532
  };
1518
1533
  }
1519
1534
 
@@ -1544,4 +1559,4 @@ exports.palettes = palettes;
1544
1559
  exports.rose3 = rose3;
1545
1560
  exports.rose5 = rose5;
1546
1561
  //# sourceMappingURL=index.cjs.map
1547
- //# sourceMappingURL=index.cjs.map
1562
+ //# sourceMappingURL=index.cjs.map