@sarmal/core 0.30.0 → 0.33.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 (53) hide show
  1. package/dist/auto-init.cjs +115 -31
  2. package/dist/auto-init.cjs.map +1 -1
  3. package/dist/auto-init.js +115 -31
  4. package/dist/auto-init.js.map +1 -1
  5. package/dist/cli.js +8 -7
  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 +122 -34
  38. package/dist/index.cjs.map +1 -1
  39. package/dist/index.d.cts +3 -3
  40. package/dist/index.d.ts +3 -3
  41. package/dist/index.js +122 -34
  42. package/dist/index.js.map +1 -1
  43. package/dist/{renderer-shared-OR--cv-t.d.ts → renderer-shared-Bdca4O4G.d.ts} +8 -4
  44. package/dist/{renderer-shared-jqw_Q1WO.d.cts → renderer-shared-Ke9BeK1P.d.cts} +8 -4
  45. package/dist/terminal.cjs +8 -7
  46. package/dist/terminal.cjs.map +1 -1
  47. package/dist/terminal.d.cts +2 -2
  48. package/dist/terminal.d.ts +2 -2
  49. package/dist/terminal.js +8 -7
  50. package/dist/terminal.js.map +1 -1
  51. package/dist/{types-zbxUgcmZ.d.cts → types-BBuUk6nn.d.cts} +9 -0
  52. package/dist/{types-zbxUgcmZ.d.ts → types-BBuUk6nn.d.ts} +9 -0
  53. package/package.json +1 -1
@@ -406,6 +406,49 @@ function parseColorToRgb(s) {
406
406
  }
407
407
  return null;
408
408
  }
409
+ var OKLCH_RE = /^oklch\(\s*([\d.]+)\s+([\d.]+)\s+([\d.]+)(?:\s*\/\s*[\d.]+)?\s*\)$/i;
410
+ function parseOklchToOklab(s) {
411
+ const m = OKLCH_RE.exec(s.trim());
412
+ if (!m) {
413
+ return null;
414
+ }
415
+ const L = parseFloat(m[1]);
416
+ const C = parseFloat(m[2]);
417
+ const H = parseFloat(m[3]);
418
+ if (Number.isNaN(L) || Number.isNaN(C) || Number.isNaN(H)) {
419
+ return null;
420
+ }
421
+ const clampedL = Math.max(0, Math.min(1, L));
422
+ const clampedC = Math.max(0, Math.min(0.4, C));
423
+ const H_rad = H * (Math.PI / 180);
424
+ return {
425
+ L: clampedL,
426
+ a: clampedC * Math.cos(H_rad),
427
+ b: clampedC * Math.sin(H_rad)
428
+ };
429
+ }
430
+ function parseColorToOklab(s) {
431
+ const oklab = parseOklchToOklab(s);
432
+ if (oklab !== null) {
433
+ return oklab;
434
+ }
435
+ const rgb = parseColorToRgb(s);
436
+ if (rgb === null) {
437
+ return null;
438
+ }
439
+ return rgbToOklab(rgb);
440
+ }
441
+ function colorToRgb(color) {
442
+ const rgb = parseColorToRgb(color);
443
+ if (rgb !== null) {
444
+ return rgb;
445
+ }
446
+ const lab = parseOklchToOklab(color);
447
+ if (lab !== null) {
448
+ return oklabToRgb(lab);
449
+ }
450
+ throw new Error(`[sarmal] unrecognized color "${color}"`);
451
+ }
409
452
  function srgbByteToLinear(c) {
410
453
  const n = c / 255;
411
454
  return n <= 0.04045 ? n / 12.92 : Math.pow((n + 0.055) / 1.055, 2.4);
@@ -443,26 +486,25 @@ var lerpOklab = (a, b, t) => {
443
486
  if (t >= 1) {
444
487
  return b;
445
488
  }
446
- const la = rgbToOklab(a), lb = rgbToOklab(b);
447
- return oklabToRgb({
448
- L: la.L + (lb.L - la.L) * t,
449
- a: la.a + (lb.a - la.a) * t,
450
- b: la.b + (lb.b - la.b) * t
451
- });
489
+ return {
490
+ L: a.L + (b.L - a.L) * t,
491
+ a: a.a + (b.a - a.a) * t,
492
+ b: a.b + (b.b - a.b) * t
493
+ };
452
494
  };
453
495
  function getPaletteColor(palette, position, timeOffset = 0) {
454
496
  if (palette.length === 0) {
455
- return { r: 255, g: 255, b: 255 };
497
+ return { L: 1, a: 0, b: 0 };
456
498
  }
457
499
  if (palette.length === 1) {
458
- return hexToRgb(palette[0]);
500
+ return palette[0];
459
501
  }
460
502
  const cyclePos = ((position + timeOffset) % 1 + 1) % 1;
461
503
  const scaled = cyclePos * palette.length;
462
504
  const idx = Math.floor(scaled);
463
505
  const t = scaled - idx;
464
- const c1 = hexToRgb(palette[idx % palette.length]);
465
- const c2 = hexToRgb(palette[(idx + 1) % palette.length]);
506
+ const c1 = palette[idx % palette.length];
507
+ const c2 = palette[(idx + 1) % palette.length];
466
508
  return lerpOklab(c1, c2, t);
467
509
  }
468
510
  var TRAIL_STYLES = ["default", "gradient-static", "gradient-animated"];
@@ -471,7 +513,8 @@ var RENDER_OPTION_KEYS = /* @__PURE__ */ new Set([
471
513
  "headColor",
472
514
  "skeletonColor",
473
515
  "trailStyle",
474
- "headRadius"
516
+ "headRadius",
517
+ "trailWidth"
475
518
  ]);
476
519
  function validateRenderOptions(partial) {
477
520
  for (const key of Object.keys(partial)) {
@@ -494,12 +537,15 @@ function validateRenderOptions(partial) {
494
537
  if (partial.headRadius !== void 0) {
495
538
  assertHeadRadius(partial.headRadius);
496
539
  }
540
+ if (partial.trailWidth !== void 0) {
541
+ assertTrailWidth(partial.trailWidth);
542
+ }
497
543
  }
498
544
  function assertTrailColor(value) {
499
545
  if (typeof value === "string") {
500
- if (parseColorToRgb(value) === null) {
546
+ if (parseColorToOklab(value) === null) {
501
547
  throw new TypeError(
502
- `[sarmal] setRenderOptions: trailColor must be a valid color string (#rrggbb, #rgb, rgb(), rgba()), got "${value}"`
548
+ `[sarmal] setRenderOptions: trailColor must be a valid color string (#rrggbb, #rgb, rgb(), rgba(), oklch()), got "${value}"`
503
549
  );
504
550
  }
505
551
  return;
@@ -512,25 +558,25 @@ function assertTrailColor(value) {
512
558
  }
513
559
  for (let i = 0; i < value.length; i++) {
514
560
  const entry = value[i];
515
- if (typeof entry !== "string" || parseColorToRgb(entry) === null) {
561
+ if (typeof entry !== "string" || parseColorToOklab(entry) === null) {
516
562
  throw new TypeError(
517
- `[sarmal] setRenderOptions: trailColor[${i}] must be a valid color string (#rrggbb, #rgb, rgb(), rgba()), got ${JSON.stringify(entry)}`
563
+ `[sarmal] setRenderOptions: trailColor[${i}] must be a valid color string (#rrggbb, #rgb, rgb(), rgba(), oklch()), got ${JSON.stringify(entry)}`
518
564
  );
519
565
  }
520
566
  }
521
567
  return;
522
568
  }
523
569
  throw new TypeError(
524
- `[sarmal] setRenderOptions: trailColor must be a valid color string (#rrggbb, #rgb, rgb(), rgba()) or an array of color strings, got ${JSON.stringify(value)}`
570
+ `[sarmal] setRenderOptions: trailColor must be a valid color string (#rrggbb, #rgb, rgb(), rgba(), oklch()) or an array of color strings, got ${JSON.stringify(value)}`
525
571
  );
526
572
  }
527
573
  function assertHeadColor(value) {
528
574
  if (value === null) {
529
575
  return;
530
576
  }
531
- if (typeof value !== "string" || parseColorToRgb(value) === null) {
577
+ if (typeof value !== "string" || parseColorToOklab(value) === null) {
532
578
  throw new TypeError(
533
- `[sarmal] setRenderOptions: headColor must be a valid color string (#rrggbb, #rgb, rgb(), rgba()) or null, got ${JSON.stringify(value)}`
579
+ `[sarmal] setRenderOptions: headColor must be a valid color string (#rrggbb, #rgb, rgb(), rgba(), oklch()) or null, got ${JSON.stringify(value)}`
534
580
  );
535
581
  }
536
582
  }
@@ -538,9 +584,9 @@ function assertSkeletonColor(value) {
538
584
  if (value === "transparent") {
539
585
  return;
540
586
  }
541
- if (typeof value !== "string" || parseColorToRgb(value) === null) {
587
+ if (typeof value !== "string" || parseColorToOklab(value) === null) {
542
588
  throw new TypeError(
543
- `[sarmal] setRenderOptions: skeletonColor must be a valid color string (#rrggbb, #rgb, rgb(), rgba()) or "transparent", got ${JSON.stringify(value)}`
589
+ `[sarmal] setRenderOptions: skeletonColor must be a valid color string (#rrggbb, #rgb, rgb(), rgba(), oklch()) or "transparent", got ${JSON.stringify(value)}`
544
590
  );
545
591
  }
546
592
  }
@@ -563,6 +609,18 @@ function assertHeadRadius(value) {
563
609
  );
564
610
  }
565
611
  }
612
+ function assertTrailWidth(value) {
613
+ if (typeof value !== "number") {
614
+ throw new TypeError(
615
+ `[sarmal] setRenderOptions: trailWidth must be a number, got ${JSON.stringify(value)}`
616
+ );
617
+ }
618
+ if (!Number.isFinite(value) || value <= 0) {
619
+ throw new TypeError(
620
+ `[sarmal] setRenderOptions: trailWidth must be a finite positive number, got ${value}`
621
+ );
622
+ }
623
+ }
566
624
  function resolveTrailMainColor(trailColor) {
567
625
  return typeof trailColor === "string" ? trailColor : trailColor[0];
568
626
  }
@@ -575,7 +633,7 @@ function resolveHeadColor(trailColor, trailStyle) {
575
633
  }
576
634
  const palette = resolveTrailPalette(trailColor);
577
635
  const last = palette[palette.length - 1];
578
- const { r, g, b } = parseColorToRgb(last);
636
+ const { r, g, b } = colorToRgb(last);
579
637
  return `rgb(${r},${g},${b})`;
580
638
  }
581
639
  function warnIfTrailColorMismatch(trailColor, trailStyle) {
@@ -596,8 +654,8 @@ function warnIfTrailColorMismatch(trailColor, trailStyle) {
596
654
  var getHeadDotRadius = (w, h) => Math.max(1, 3 * Math.sqrt(Math.min(w, h) / 160));
597
655
  var WHITE_HEX = "#ffffff";
598
656
  function colorToRgbComponents(color) {
599
- const c = parseColorToRgb(color);
600
- return `${c.r},${c.g},${c.b}`;
657
+ const { r, g, b } = colorToRgb(color);
658
+ return `${r},${g},${b}`;
601
659
  }
602
660
  function applyDprSizing(target, logicalWidth, logicalHeight, dpr) {
603
661
  target.style.width = `${logicalWidth}px`;
@@ -619,6 +677,7 @@ function createRenderer(options) {
619
677
  let headColor = userHeadColor ?? resolveHeadColor(trailColor, trailStyle);
620
678
  let trailSolidRgb = colorToRgbComponents(resolveTrailMainColor(trailColor));
621
679
  let trailPalette = resolveTrailPalette(trailColor);
680
+ let trailPaletteOklab = trailPalette.map((c) => parseColorToOklab(c));
622
681
  warnIfTrailColorMismatch(trailColor, trailStyle);
623
682
  const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
624
683
  function setupCanvas() {
@@ -630,7 +689,14 @@ function createRenderer(options) {
630
689
  setupCanvas();
631
690
  let logicalWidth = canvas.width / dpr;
632
691
  let logicalHeight = canvas.height / dpr;
692
+ if (options.headRadius !== void 0) {
693
+ validateRenderOptions({ headRadius: options.headRadius });
694
+ }
695
+ if (options.trailWidth !== void 0) {
696
+ validateRenderOptions({ trailWidth: options.trailWidth });
697
+ }
633
698
  let headRadius = options.headRadius ?? getHeadDotRadius(logicalWidth, logicalHeight);
699
+ let trailWidth = options.trailWidth ?? 1;
634
700
  let skeleton = [];
635
701
  let skeletonCanvas = null;
636
702
  let trail = [];
@@ -724,14 +790,16 @@ function createRenderer(options) {
724
790
  i,
725
791
  trailCount,
726
792
  toX,
727
- toY
793
+ toY,
794
+ TRAIL_MIN_WIDTH * trailWidth,
795
+ TRAIL_MAX_WIDTH * trailWidth
728
796
  );
729
797
  if (trailStyle === "default") {
730
798
  ctx.fillStyle = `rgba(${trailSolidRgb},${opacity})`;
731
799
  } else {
732
800
  const timeOffset = trailStyle === "gradient-animated" ? gradientAnimTime * 5e-4 : 0;
733
- const color = getPaletteColor(trailPalette, progress, timeOffset);
734
- ctx.fillStyle = `rgba(${color.r},${color.g},${color.b},${opacity})`;
801
+ const { r, g, b } = oklabToRgb(getPaletteColor(trailPaletteOklab, progress, timeOffset));
802
+ ctx.fillStyle = `rgba(${r},${g},${b},${opacity})`;
735
803
  }
736
804
  ctx.beginPath();
737
805
  ctx.moveTo(l0x, l0y);
@@ -865,6 +933,7 @@ function createRenderer(options) {
865
933
  trailColor = partial.trailColor;
866
934
  trailSolidRgb = colorToRgbComponents(resolveTrailMainColor(trailColor));
867
935
  trailPalette = resolveTrailPalette(trailColor);
936
+ trailPaletteOklab = trailPalette.map((c) => parseColorToOklab(c));
868
937
  }
869
938
  if (partial.skeletonColor !== void 0) {
870
939
  skeletonColor = partial.skeletonColor;
@@ -881,6 +950,9 @@ function createRenderer(options) {
881
950
  if (partial.headRadius !== void 0) {
882
951
  headRadius = partial.headRadius;
883
952
  }
953
+ if (partial.trailWidth !== void 0) {
954
+ trailWidth = partial.trailWidth;
955
+ }
884
956
  if (userHeadColor === null) {
885
957
  headColor = resolveHeadColor(trailColor, trailStyle);
886
958
  } else {
@@ -948,8 +1020,8 @@ function el(tag) {
948
1020
  return document.createElementNS("http://www.w3.org/2000/svg", tag);
949
1021
  }
950
1022
  function colorToRgbAttr(color) {
951
- const c = parseColorToRgb(color);
952
- return `rgb(${c.r},${c.g},${c.b})`;
1023
+ const { r, g, b } = colorToRgb(color);
1024
+ return `rgb(${r},${g},${b})`;
953
1025
  }
954
1026
  function createSVGRenderer(options) {
955
1027
  const { container, engine } = options;
@@ -967,6 +1039,7 @@ function createSVGRenderer(options) {
967
1039
  let headRadius;
968
1040
  let trailSolid = colorToRgbAttr(resolveTrailMainColor(trailColor));
969
1041
  let trailPalette = resolveTrailPalette(trailColor);
1042
+ let trailPaletteOklab = trailPalette.map((c) => parseColorToOklab(c));
970
1043
  const ariaLabel = options.ariaLabel ?? "Loading";
971
1044
  warnIfTrailColorMismatch(trailColor, trailStyle);
972
1045
  const viewSize = 100;
@@ -978,7 +1051,14 @@ function createSVGRenderer(options) {
978
1051
  const svgTrailMinWidth = TRAIL_MIN_WIDTH * viewSize / containerPx;
979
1052
  const svgTrailMaxWidth = TRAIL_MAX_WIDTH * viewSize / containerPx;
980
1053
  const svgSkeletonStrokeWidth = String(SKELETON_STROKE_WIDTH_PX * viewSize / containerPx);
1054
+ if (options.headRadius !== void 0) {
1055
+ validateRenderOptions({ headRadius: options.headRadius });
1056
+ }
1057
+ if (options.trailWidth !== void 0) {
1058
+ validateRenderOptions({ trailWidth: options.trailWidth });
1059
+ }
981
1060
  headRadius = options.headRadius ?? SVG_DEFAULT_HEAD_RADIUS;
1061
+ let trailWidth = options.trailWidth ?? 1;
982
1062
  container.setAttribute("viewBox", `0 0 ${viewSize} ${viewSize}`);
983
1063
  container.setAttribute("role", "img");
984
1064
  container.setAttribute("aria-label", ariaLabel);
@@ -1073,15 +1153,15 @@ function createSVGRenderer(options) {
1073
1153
  trailCount,
1074
1154
  px,
1075
1155
  py,
1076
- svgTrailMinWidth,
1077
- svgTrailMaxWidth
1156
+ svgTrailMinWidth * trailWidth,
1157
+ svgTrailMaxWidth * trailWidth
1078
1158
  );
1079
1159
  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`;
1080
1160
  trailPaths[i].setAttribute("d", d);
1081
1161
  trailPaths[i].setAttribute("fill-opacity", opacity.toFixed(3));
1082
1162
  if (trailStyle !== "default") {
1083
1163
  const timeOffset = trailStyle === "gradient-animated" ? gradientAnimTime * 5e-4 : 0;
1084
- const { r, g, b } = getPaletteColor(trailPalette, progress, timeOffset);
1164
+ const { r, g, b } = oklabToRgb(getPaletteColor(trailPaletteOklab, progress, timeOffset));
1085
1165
  trailPaths[i].setAttribute("fill", `rgb(${r},${g},${b})`);
1086
1166
  }
1087
1167
  }
@@ -1233,6 +1313,7 @@ function createSVGRenderer(options) {
1233
1313
  trailColor = partial.trailColor;
1234
1314
  trailSolid = colorToRgbAttr(resolveTrailMainColor(trailColor));
1235
1315
  trailPalette = resolveTrailPalette(trailColor);
1316
+ trailPaletteOklab = trailPalette.map((c) => parseColorToOklab(c));
1236
1317
  if (trailStyle === "default") {
1237
1318
  for (const p of trailPaths) {
1238
1319
  p.setAttribute("fill", trailSolid);
@@ -1265,6 +1346,9 @@ function createSVGRenderer(options) {
1265
1346
  headRadius = partial.headRadius;
1266
1347
  headCircle.setAttribute("r", String(headRadius));
1267
1348
  }
1349
+ if (partial.trailWidth !== void 0) {
1350
+ trailWidth = partial.trailWidth;
1351
+ }
1268
1352
  if (userHeadColor === null) {
1269
1353
  headColor = resolveHeadColor(trailColor, trailStyle);
1270
1354
  } else {