@sarmal/core 0.15.0 → 0.16.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.
package/dist/index.cjs CHANGED
@@ -521,6 +521,7 @@ function warnIfTrailColorMismatch(trailColor, trailStyle) {
521
521
  );
522
522
  }
523
523
  }
524
+ var getHeadDotRadius = (w, h) => Math.max(1, 3 * Math.sqrt(Math.min(w, h) / 160));
524
525
 
525
526
  // src/renderer.ts
526
527
  var WHITE_HEX = "#ffffff";
@@ -551,9 +552,8 @@ function createRenderer(options) {
551
552
  warnIfTrailColorMismatch(trailColor, trailStyle);
552
553
  const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
553
554
  function setupCanvas() {
554
- const rect = canvas.getBoundingClientRect();
555
- const lw = rect.width || 200;
556
- const lh = rect.height || 200;
555
+ const lw = canvas.offsetWidth || 200;
556
+ const lh = canvas.offsetHeight || 200;
557
557
  applyDprSizing(canvas, lw, lh, dpr);
558
558
  ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
559
559
  }
@@ -571,6 +571,7 @@ function createRenderer(options) {
571
571
  let animationId = null;
572
572
  let lastTime = 0;
573
573
  let morphResolve = null;
574
+ let morphReject = null;
574
575
  let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
575
576
  let morphAlpha = 0;
576
577
  let gradientAnimTime = 0;
@@ -675,8 +676,7 @@ function createRenderer(options) {
675
676
  }
676
677
  const x = head.x * scale + offsetX;
677
678
  const y = head.y * scale + offsetY;
678
- const r =
679
- options.headRadius ?? Math.max(2, 3 * Math.sqrt(Math.min(logicalWidth, logicalHeight) / 160));
679
+ const r = options.headRadius ?? getHeadDotRadius(logicalWidth, logicalHeight);
680
680
  ctx.fillStyle = headColor;
681
681
  ctx.beginPath();
682
682
  ctx.arc(x, y, r, 0, Math.PI * 2);
@@ -700,6 +700,7 @@ function createRenderer(options) {
700
700
  engine.completeMorph();
701
701
  morphResolve?.();
702
702
  morphResolve = null;
703
+ morphReject = null;
703
704
  morphAlpha = 0;
704
705
  skeleton = engine.getSarmalSkeleton();
705
706
  if (!engine.isLiveSkeleton) {
@@ -762,6 +763,11 @@ function createRenderer(options) {
762
763
  cancelAnimationFrame(animationId);
763
764
  animationId = null;
764
765
  }
766
+ if (morphReject !== null) {
767
+ morphReject(new Error("Instance destroyed during morph"));
768
+ morphResolve = null;
769
+ morphReject = null;
770
+ }
765
771
  },
766
772
  ...enginePassthroughs(engine),
767
773
  morphTo(target, options2) {
@@ -769,13 +775,15 @@ function createRenderer(options) {
769
775
  engine.completeMorph();
770
776
  morphResolve();
771
777
  morphResolve = null;
778
+ morphReject = null;
772
779
  morphAlpha = 0;
773
780
  }
774
781
  morphDurationMs = options2?.duration ?? DEFAULT_MORPH_DURATION_MS;
775
782
  morphAlpha = 0;
776
783
  engine.startMorph(target, options2?.morphStrategy);
777
- return new Promise((resolve) => {
784
+ return new Promise((resolve, reject) => {
778
785
  morphResolve = resolve;
786
+ morphReject = reject;
779
787
  });
780
788
  },
781
789
  setRenderOptions(partial) {
@@ -850,11 +858,10 @@ function createSVGRenderer(options) {
850
858
  let trailPalette = resolveTrailPalette(trailColor);
851
859
  const ariaLabel = options.ariaLabel ?? "Loading";
852
860
  warnIfTrailColorMismatch(trailColor, trailStyle);
853
- const rect = container.getBoundingClientRect();
854
- const width = rect.width || 200;
855
- const height = rect.height || 200;
856
- const headRadius =
857
- options.headRadius ?? Math.max(2, 3 * Math.sqrt(Math.min(width, height) / 160));
861
+ const htmlContainer = container;
862
+ const width = htmlContainer.offsetWidth || 200;
863
+ const height = htmlContainer.offsetHeight || 200;
864
+ const headRadius = options.headRadius ?? getHeadDotRadius(width, height);
858
865
  const svg = el("svg");
859
866
  svg.setAttribute("width", String(width));
860
867
  svg.setAttribute("height", String(height));
@@ -934,7 +941,10 @@ function createSVGRenderer(options) {
934
941
  }
935
942
  return;
936
943
  }
937
- for (let i = 0; i < trailCount - 1; i++) {
944
+ const startIdx = Math.max(0, trailCount - 1 - MAX_TRAIL_SEGMENTS);
945
+ const drawnCount = trailCount - 1 - startIdx;
946
+ for (let i = startIdx; i < trailCount - 1; i++) {
947
+ const j = i - startIdx;
938
948
  const { l0x, l0y, r0x, r0y, l1x, l1y, r1x, r1y, opacity, progress } = computeTrailQuad(
939
949
  trail,
940
950
  i,
@@ -943,15 +953,15 @@ function createSVGRenderer(options) {
943
953
  py,
944
954
  );
945
955
  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`;
946
- trailPaths[i].setAttribute("d", d);
947
- trailPaths[i].setAttribute("fill-opacity", opacity.toFixed(3));
956
+ trailPaths[j].setAttribute("d", d);
957
+ trailPaths[j].setAttribute("fill-opacity", opacity.toFixed(3));
948
958
  if (trailStyle !== "default") {
949
959
  const timeOffset = trailStyle === "gradient-animated" ? gradientAnimTime * 5e-4 : 0;
950
960
  const { r, g, b } = getPaletteColor(trailPalette, progress, timeOffset);
951
- trailPaths[i].setAttribute("fill", `rgb(${r},${g},${b})`);
961
+ trailPaths[j].setAttribute("fill", `rgb(${r},${g},${b})`);
952
962
  }
953
963
  }
954
- for (let i = trailCount - 1; i < trailPaths.length; i++) {
964
+ for (let i = drawnCount; i < trailPaths.length; i++) {
955
965
  trailPaths[i].setAttribute("d", "");
956
966
  }
957
967
  }
@@ -970,6 +980,7 @@ function createSVGRenderer(options) {
970
980
  const prefersReducedMotion =
971
981
  typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
972
982
  let morphResolve = null;
983
+ let morphReject = null;
973
984
  let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
974
985
  let morphTarget = null;
975
986
  let morphAlpha = 0;
@@ -997,6 +1008,7 @@ function createSVGRenderer(options) {
997
1008
  engine.completeMorph();
998
1009
  morphResolve?.();
999
1010
  morphResolve = null;
1011
+ morphReject = null;
1000
1012
  morphTarget = null;
1001
1013
  morphAlpha = 0;
1002
1014
  morphPathABuilt = "";
@@ -1056,6 +1068,11 @@ function createSVGRenderer(options) {
1056
1068
  cancelAnimationFrame(animationId);
1057
1069
  animationId = null;
1058
1070
  }
1071
+ if (morphReject !== null) {
1072
+ morphReject(new Error("Instance destroyed during morph"));
1073
+ morphResolve = null;
1074
+ morphReject = null;
1075
+ }
1059
1076
  svg.remove();
1060
1077
  },
1061
1078
  ...enginePassthroughs(engine),
@@ -1064,6 +1081,7 @@ function createSVGRenderer(options) {
1064
1081
  engine.completeMorph();
1065
1082
  morphResolve();
1066
1083
  morphResolve = null;
1084
+ morphReject = null;
1067
1085
  morphAlpha = 0;
1068
1086
  skeletonPathA.setAttribute("visibility", "hidden");
1069
1087
  skeletonPathB.setAttribute("visibility", "hidden");
@@ -1078,8 +1096,9 @@ function createSVGRenderer(options) {
1078
1096
  const targetSkeleton = sampleCurveSkeleton(target);
1079
1097
  morphPathBBuilt = pointsToPathString(targetSkeleton, scale, offsetX, offsetY);
1080
1098
  }
1081
- return new Promise((resolve) => {
1099
+ return new Promise((resolve, reject) => {
1082
1100
  morphResolve = resolve;
1101
+ morphReject = reject;
1083
1102
  });
1084
1103
  },
1085
1104
  setRenderOptions(partial) {