@sarmal/core 0.37.1 → 0.38.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 (52) hide show
  1. package/README.md +4 -4
  2. package/dist/auto-init.cjs +111 -14
  3. package/dist/auto-init.cjs.map +1 -1
  4. package/dist/auto-init.js +111 -14
  5. package/dist/auto-init.js.map +1 -1
  6. package/dist/cli.js.map +1 -1
  7. package/dist/curves/artemis2.d.cts +1 -1
  8. package/dist/curves/artemis2.d.ts +1 -1
  9. package/dist/curves/astroid.d.cts +1 -1
  10. package/dist/curves/astroid.d.ts +1 -1
  11. package/dist/curves/deltoid.d.cts +1 -1
  12. package/dist/curves/deltoid.d.ts +1 -1
  13. package/dist/curves/epicycloid3.d.cts +1 -1
  14. package/dist/curves/epicycloid3.d.ts +1 -1
  15. package/dist/curves/epitrochoid7.d.cts +1 -1
  16. package/dist/curves/epitrochoid7.d.ts +1 -1
  17. package/dist/curves/index.d.cts +1 -1
  18. package/dist/curves/index.d.ts +1 -1
  19. package/dist/curves/lame.d.cts +1 -1
  20. package/dist/curves/lame.d.ts +1 -1
  21. package/dist/curves/lissajous32.d.cts +1 -1
  22. package/dist/curves/lissajous32.d.ts +1 -1
  23. package/dist/curves/lissajous43.d.cts +1 -1
  24. package/dist/curves/lissajous43.d.ts +1 -1
  25. package/dist/curves/rose3.d.cts +1 -1
  26. package/dist/curves/rose3.d.ts +1 -1
  27. package/dist/curves/rose5.d.cts +1 -1
  28. package/dist/curves/rose5.d.ts +1 -1
  29. package/dist/curves/rose52.d.cts +1 -1
  30. package/dist/curves/rose52.d.ts +1 -1
  31. package/dist/curves/star.d.cts +1 -1
  32. package/dist/curves/star.d.ts +1 -1
  33. package/dist/curves/star4.d.cts +1 -1
  34. package/dist/curves/star4.d.ts +1 -1
  35. package/dist/curves/star7.d.cts +1 -1
  36. package/dist/curves/star7.d.ts +1 -1
  37. package/dist/index.cjs +111 -14
  38. package/dist/index.cjs.map +1 -1
  39. package/dist/index.d.cts +12 -53
  40. package/dist/index.d.ts +12 -53
  41. package/dist/index.js +111 -14
  42. package/dist/index.js.map +1 -1
  43. package/dist/{renderer-shared-DWPVHjKZ.d.ts → renderer-shared-2tEwOWJm.d.ts} +1 -1
  44. package/dist/{renderer-shared-Bcc_1IaT.d.cts → renderer-shared-Bh33A5Av.d.cts} +1 -1
  45. package/dist/terminal.cjs.map +1 -1
  46. package/dist/terminal.d.cts +2 -2
  47. package/dist/terminal.d.ts +2 -2
  48. package/dist/terminal.js.map +1 -1
  49. package/dist/{types-B1XeFpuq.d.cts → types-CmPFR9U3.d.cts} +126 -2
  50. package/dist/{types-B1XeFpuq.d.ts → types-CmPFR9U3.d.ts} +126 -2
  51. package/package.json +5 -5
  52. package/skills/core/SKILL.md +3 -3
package/dist/auto-init.js CHANGED
@@ -362,15 +362,42 @@ function computeBoundaries(pts, logicalWidth, logicalHeight, minPaddingPx = FIT_
362
362
  offsetY: (logicalHeight - h * scale) / 2 - minY * scale
363
363
  };
364
364
  }
365
- function enginePassthroughs(engine) {
365
+ var DESTROYED_ERROR = "[sarmal] Instance has been destroyed and cannot be used again. Call pause() instead of destroy() for temporary suspension.";
366
+ function enginePassthroughs(engine, isDestroyed) {
367
+ function guard() {
368
+ if (isDestroyed()) {
369
+ throw new Error(DESTROYED_ERROR);
370
+ }
371
+ }
366
372
  return {
367
- jump: engine.jump,
368
- seek: engine.seek,
369
- setSpeed: engine.setSpeed,
370
- getSpeed: engine.getSpeed,
371
- resetSpeed: engine.resetSpeed,
372
- setSpeedOver: engine.setSpeedOver,
373
- getSarmalSkeleton: engine.getSarmalSkeleton
373
+ jump(phase, options) {
374
+ guard();
375
+ engine.jump(phase, options);
376
+ },
377
+ seek(phase, options) {
378
+ guard();
379
+ engine.seek(phase, options);
380
+ },
381
+ setSpeed(speed) {
382
+ guard();
383
+ engine.setSpeed(speed);
384
+ },
385
+ getSpeed() {
386
+ guard();
387
+ return engine.getSpeed();
388
+ },
389
+ resetSpeed() {
390
+ guard();
391
+ engine.resetSpeed();
392
+ },
393
+ setSpeedOver(speed, duration) {
394
+ guard();
395
+ return engine.setSpeedOver(speed, duration);
396
+ },
397
+ getSarmalSkeleton() {
398
+ guard();
399
+ return engine.getSarmalSkeleton();
400
+ }
374
401
  };
375
402
  }
376
403
  function hexToRgb(hex) {
@@ -729,6 +756,7 @@ function createRenderer(options) {
729
756
  let animationId = null;
730
757
  let lastTime = 0;
731
758
  let pausedByVisibility = false;
759
+ let destroyed = false;
732
760
  let morphResolve = null;
733
761
  let morphReject = null;
734
762
  let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
@@ -900,6 +928,9 @@ function createRenderer(options) {
900
928
  const shouldAutoStart = options.autoStart !== false;
901
929
  const instance = {
902
930
  play() {
931
+ if (destroyed) {
932
+ throw new Error(DESTROYED_ERROR);
933
+ }
903
934
  if (animationId !== null) {
904
935
  return;
905
936
  }
@@ -907,6 +938,9 @@ function createRenderer(options) {
907
938
  loop();
908
939
  },
909
940
  pause() {
941
+ if (destroyed) {
942
+ throw new Error(DESTROYED_ERROR);
943
+ }
910
944
  if (animationId === null) {
911
945
  return;
912
946
  }
@@ -915,24 +949,36 @@ function createRenderer(options) {
915
949
  engine.cancelSpeedTransition();
916
950
  },
917
951
  reset() {
952
+ if (destroyed) {
953
+ throw new Error(DESTROYED_ERROR);
954
+ }
918
955
  engine.reset();
919
956
  trail = [];
920
957
  head = null;
921
958
  },
922
959
  destroy() {
960
+ if (destroyed) {
961
+ return;
962
+ }
963
+ destroyed = true;
923
964
  if (animationId !== null) {
924
965
  cancelAnimationFrame(animationId);
925
966
  animationId = null;
926
967
  }
927
968
  document.removeEventListener("visibilitychange", handleVisibilityChange);
969
+ engine.cancelSpeedTransition();
928
970
  if (morphReject !== null) {
929
- morphReject(new Error("Instance destroyed during morph"));
971
+ morphReject(new Error("[sarmal] Instance destroyed during morph"));
930
972
  morphResolve = null;
931
973
  morphReject = null;
932
974
  }
975
+ ctx.clearRect(0, 0, logicalWidth, logicalHeight);
933
976
  },
934
- ...enginePassthroughs(engine),
977
+ ...enginePassthroughs(engine, () => destroyed),
935
978
  morphTo(target, options2) {
979
+ if (destroyed) {
980
+ throw new Error(DESTROYED_ERROR);
981
+ }
936
982
  if (morphResolve !== null) {
937
983
  engine.completeMorph();
938
984
  morphResolve();
@@ -949,6 +995,9 @@ function createRenderer(options) {
949
995
  });
950
996
  },
951
997
  setRenderOptions(partial) {
998
+ if (destroyed) {
999
+ throw new Error(DESTROYED_ERROR);
1000
+ }
952
1001
  validateRenderOptions(partial);
953
1002
  if (partial.trailColor !== void 0) {
954
1003
  trailColor = partial.trailColor;
@@ -1203,6 +1252,7 @@ function createSVGRenderer(options) {
1203
1252
  let animationId = null;
1204
1253
  let lastTime = 0;
1205
1254
  let pausedByVisibility = false;
1255
+ let destroyed = false;
1206
1256
  const prefersReducedMotion = typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1207
1257
  let morphResolve = null;
1208
1258
  let morphReject = null;
@@ -1271,6 +1321,9 @@ function createSVGRenderer(options) {
1271
1321
  const shouldAutoStart = options.autoStart !== false;
1272
1322
  const instance = {
1273
1323
  play() {
1324
+ if (destroyed) {
1325
+ throw new Error(DESTROYED_ERROR);
1326
+ }
1274
1327
  if (animationId !== null) {
1275
1328
  return;
1276
1329
  }
@@ -1278,6 +1331,9 @@ function createSVGRenderer(options) {
1278
1331
  loop();
1279
1332
  },
1280
1333
  pause() {
1334
+ if (destroyed) {
1335
+ throw new Error(DESTROYED_ERROR);
1336
+ }
1281
1337
  if (animationId === null) {
1282
1338
  return;
1283
1339
  }
@@ -1286,23 +1342,34 @@ function createSVGRenderer(options) {
1286
1342
  engine.cancelSpeedTransition();
1287
1343
  },
1288
1344
  reset() {
1345
+ if (destroyed) {
1346
+ throw new Error(DESTROYED_ERROR);
1347
+ }
1289
1348
  engine.reset();
1290
1349
  },
1291
1350
  destroy() {
1351
+ if (destroyed) {
1352
+ return;
1353
+ }
1354
+ destroyed = true;
1292
1355
  if (animationId !== null) {
1293
1356
  cancelAnimationFrame(animationId);
1294
1357
  animationId = null;
1295
1358
  }
1296
1359
  document.removeEventListener("visibilitychange", handleVisibilityChange);
1360
+ engine.cancelSpeedTransition();
1297
1361
  if (morphReject !== null) {
1298
- morphReject(new Error("Instance destroyed during morph"));
1362
+ morphReject(new Error("[sarmal] Instance destroyed during morph"));
1299
1363
  morphResolve = null;
1300
1364
  morphReject = null;
1301
1365
  }
1302
1366
  group.remove();
1303
1367
  },
1304
- ...enginePassthroughs(engine),
1368
+ ...enginePassthroughs(engine, () => destroyed),
1305
1369
  morphTo(target, options2) {
1370
+ if (destroyed) {
1371
+ throw new Error(DESTROYED_ERROR);
1372
+ }
1306
1373
  if (morphResolve !== null) {
1307
1374
  engine.completeMorph();
1308
1375
  morphResolve();
@@ -1328,6 +1395,9 @@ function createSVGRenderer(options) {
1328
1395
  });
1329
1396
  },
1330
1397
  setRenderOptions(partial) {
1398
+ if (destroyed) {
1399
+ throw new Error(DESTROYED_ERROR);
1400
+ }
1331
1401
  validateRenderOptions(partial);
1332
1402
  const prevTrailStyle = trailStyle;
1333
1403
  if (partial.trailColor !== void 0) {
@@ -1457,6 +1527,7 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1457
1527
  let animationId = null;
1458
1528
  let lastTime = 0;
1459
1529
  let pausedByVisibility = false;
1530
+ let destroyed = false;
1460
1531
  let morphResolve = null;
1461
1532
  let morphReject = null;
1462
1533
  let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
@@ -1710,6 +1781,9 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1710
1781
  const instance = {
1711
1782
  /** Starts the animation loop. Does nothing if already running. */
1712
1783
  play() {
1784
+ if (destroyed) {
1785
+ throw new Error(DESTROYED_ERROR);
1786
+ }
1713
1787
  if (animationId !== null) {
1714
1788
  return;
1715
1789
  }
@@ -1718,6 +1792,9 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1718
1792
  },
1719
1793
  /** Pauses the animation loop. Preserves current trail state. */
1720
1794
  pause() {
1795
+ if (destroyed) {
1796
+ throw new Error(DESTROYED_ERROR);
1797
+ }
1721
1798
  if (animationId === null) {
1722
1799
  return;
1723
1800
  }
@@ -1727,29 +1804,46 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1727
1804
  },
1728
1805
  /** Resets the animation to the start of the curve and clears the grid. */
1729
1806
  reset() {
1807
+ if (destroyed) {
1808
+ throw new Error(DESTROYED_ERROR);
1809
+ }
1730
1810
  engine.reset();
1731
1811
  grid.fill(0);
1732
1812
  },
1733
- /** Stops the animation and removes all event listeners. */
1813
+ /**
1814
+ * Permanently stops the animation and clears the visual output.
1815
+ * Calling any method on a destroyed instance throws an error.
1816
+ * `destroy()` is idempotent — calling it multiple times is safe.
1817
+ */
1734
1818
  destroy() {
1819
+ if (destroyed) {
1820
+ return;
1821
+ }
1822
+ destroyed = true;
1735
1823
  if (animationId !== null) {
1736
1824
  cancelAnimationFrame(animationId);
1737
1825
  animationId = null;
1738
1826
  }
1739
1827
  document.removeEventListener("visibilitychange", handleVisibilityChange);
1828
+ engine.cancelSpeedTransition();
1740
1829
  if (morphReject !== null) {
1741
1830
  morphReject(new Error("[sarmal] Instance destroyed during morph"));
1742
1831
  morphResolve = null;
1743
1832
  morphReject = null;
1744
1833
  }
1834
+ grid.fill(0);
1835
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
1745
1836
  },
1746
- ...enginePassthroughs(engine),
1837
+ ...enginePassthroughs(engine, () => destroyed),
1747
1838
  /**
1748
1839
  * Smoothly transitions from the current curve to `target`.
1749
1840
  * If a morph is already in progress, it is snapped to completion before the new one starts.
1750
1841
  * @returns A Promise that resolves when the transition finishes.
1751
1842
  */
1752
1843
  morphTo(target, opts) {
1844
+ if (destroyed) {
1845
+ throw new Error(DESTROYED_ERROR);
1846
+ }
1753
1847
  if (morphResolve !== null) {
1754
1848
  completeMorphNow();
1755
1849
  }
@@ -1769,6 +1863,9 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1769
1863
  * ! Validation fails the entire call if any field is invalid, leaving options unchanged.
1770
1864
  */
1771
1865
  setRenderOptions(partial) {
1866
+ if (destroyed) {
1867
+ throw new Error(DESTROYED_ERROR);
1868
+ }
1772
1869
  validateBaseRenderOptions(partial);
1773
1870
  let needsRebuildBg = false;
1774
1871
  if (partial.trailColor !== void 0) {