@sarmal/core 0.30.0 → 0.31.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/auto-init.js CHANGED
@@ -404,6 +404,49 @@ function parseColorToRgb(s) {
404
404
  }
405
405
  return null;
406
406
  }
407
+ var OKLCH_RE = /^oklch\(\s*([\d.]+)\s+([\d.]+)\s+([\d.]+)(?:\s*\/\s*[\d.]+)?\s*\)$/i;
408
+ function parseOklchToOklab(s) {
409
+ const m = OKLCH_RE.exec(s.trim());
410
+ if (!m) {
411
+ return null;
412
+ }
413
+ const L = parseFloat(m[1]);
414
+ const C = parseFloat(m[2]);
415
+ const H = parseFloat(m[3]);
416
+ if (Number.isNaN(L) || Number.isNaN(C) || Number.isNaN(H)) {
417
+ return null;
418
+ }
419
+ const clampedL = Math.max(0, Math.min(1, L));
420
+ const clampedC = Math.max(0, Math.min(0.4, C));
421
+ const H_rad = H * (Math.PI / 180);
422
+ return {
423
+ L: clampedL,
424
+ a: clampedC * Math.cos(H_rad),
425
+ b: clampedC * Math.sin(H_rad)
426
+ };
427
+ }
428
+ function parseColorToOklab(s) {
429
+ const oklab = parseOklchToOklab(s);
430
+ if (oklab !== null) {
431
+ return oklab;
432
+ }
433
+ const rgb = parseColorToRgb(s);
434
+ if (rgb === null) {
435
+ return null;
436
+ }
437
+ return rgbToOklab(rgb);
438
+ }
439
+ function colorToRgb(color) {
440
+ const rgb = parseColorToRgb(color);
441
+ if (rgb !== null) {
442
+ return rgb;
443
+ }
444
+ const lab = parseOklchToOklab(color);
445
+ if (lab !== null) {
446
+ return oklabToRgb(lab);
447
+ }
448
+ throw new Error(`[sarmal] unrecognized color "${color}"`);
449
+ }
407
450
  function srgbByteToLinear(c) {
408
451
  const n = c / 255;
409
452
  return n <= 0.04045 ? n / 12.92 : Math.pow((n + 0.055) / 1.055, 2.4);
@@ -441,26 +484,25 @@ var lerpOklab = (a, b, t) => {
441
484
  if (t >= 1) {
442
485
  return b;
443
486
  }
444
- const la = rgbToOklab(a), lb = rgbToOklab(b);
445
- return oklabToRgb({
446
- L: la.L + (lb.L - la.L) * t,
447
- a: la.a + (lb.a - la.a) * t,
448
- b: la.b + (lb.b - la.b) * t
449
- });
487
+ return {
488
+ L: a.L + (b.L - a.L) * t,
489
+ a: a.a + (b.a - a.a) * t,
490
+ b: a.b + (b.b - a.b) * t
491
+ };
450
492
  };
451
493
  function getPaletteColor(palette, position, timeOffset = 0) {
452
494
  if (palette.length === 0) {
453
- return { r: 255, g: 255, b: 255 };
495
+ return { L: 1, a: 0, b: 0 };
454
496
  }
455
497
  if (palette.length === 1) {
456
- return hexToRgb(palette[0]);
498
+ return palette[0];
457
499
  }
458
500
  const cyclePos = ((position + timeOffset) % 1 + 1) % 1;
459
501
  const scaled = cyclePos * palette.length;
460
502
  const idx = Math.floor(scaled);
461
503
  const t = scaled - idx;
462
- const c1 = hexToRgb(palette[idx % palette.length]);
463
- const c2 = hexToRgb(palette[(idx + 1) % palette.length]);
504
+ const c1 = palette[idx % palette.length];
505
+ const c2 = palette[(idx + 1) % palette.length];
464
506
  return lerpOklab(c1, c2, t);
465
507
  }
466
508
  var TRAIL_STYLES = ["default", "gradient-static", "gradient-animated"];
@@ -495,9 +537,9 @@ function validateRenderOptions(partial) {
495
537
  }
496
538
  function assertTrailColor(value) {
497
539
  if (typeof value === "string") {
498
- if (parseColorToRgb(value) === null) {
540
+ if (parseColorToOklab(value) === null) {
499
541
  throw new TypeError(
500
- `[sarmal] setRenderOptions: trailColor must be a valid color string (#rrggbb, #rgb, rgb(), rgba()), got "${value}"`
542
+ `[sarmal] setRenderOptions: trailColor must be a valid color string (#rrggbb, #rgb, rgb(), rgba(), oklch()), got "${value}"`
501
543
  );
502
544
  }
503
545
  return;
@@ -510,25 +552,25 @@ function assertTrailColor(value) {
510
552
  }
511
553
  for (let i = 0; i < value.length; i++) {
512
554
  const entry = value[i];
513
- if (typeof entry !== "string" || parseColorToRgb(entry) === null) {
555
+ if (typeof entry !== "string" || parseColorToOklab(entry) === null) {
514
556
  throw new TypeError(
515
- `[sarmal] setRenderOptions: trailColor[${i}] must be a valid color string (#rrggbb, #rgb, rgb(), rgba()), got ${JSON.stringify(entry)}`
557
+ `[sarmal] setRenderOptions: trailColor[${i}] must be a valid color string (#rrggbb, #rgb, rgb(), rgba(), oklch()), got ${JSON.stringify(entry)}`
516
558
  );
517
559
  }
518
560
  }
519
561
  return;
520
562
  }
521
563
  throw new TypeError(
522
- `[sarmal] setRenderOptions: trailColor must be a valid color string (#rrggbb, #rgb, rgb(), rgba()) or an array of color strings, got ${JSON.stringify(value)}`
564
+ `[sarmal] setRenderOptions: trailColor must be a valid color string (#rrggbb, #rgb, rgb(), rgba(), oklch()) or an array of color strings, got ${JSON.stringify(value)}`
523
565
  );
524
566
  }
525
567
  function assertHeadColor(value) {
526
568
  if (value === null) {
527
569
  return;
528
570
  }
529
- if (typeof value !== "string" || parseColorToRgb(value) === null) {
571
+ if (typeof value !== "string" || parseColorToOklab(value) === null) {
530
572
  throw new TypeError(
531
- `[sarmal] setRenderOptions: headColor must be a valid color string (#rrggbb, #rgb, rgb(), rgba()) or null, got ${JSON.stringify(value)}`
573
+ `[sarmal] setRenderOptions: headColor must be a valid color string (#rrggbb, #rgb, rgb(), rgba(), oklch()) or null, got ${JSON.stringify(value)}`
532
574
  );
533
575
  }
534
576
  }
@@ -536,9 +578,9 @@ function assertSkeletonColor(value) {
536
578
  if (value === "transparent") {
537
579
  return;
538
580
  }
539
- if (typeof value !== "string" || parseColorToRgb(value) === null) {
581
+ if (typeof value !== "string" || parseColorToOklab(value) === null) {
540
582
  throw new TypeError(
541
- `[sarmal] setRenderOptions: skeletonColor must be a valid color string (#rrggbb, #rgb, rgb(), rgba()) or "transparent", got ${JSON.stringify(value)}`
583
+ `[sarmal] setRenderOptions: skeletonColor must be a valid color string (#rrggbb, #rgb, rgb(), rgba(), oklch()) or "transparent", got ${JSON.stringify(value)}`
542
584
  );
543
585
  }
544
586
  }
@@ -573,7 +615,7 @@ function resolveHeadColor(trailColor, trailStyle) {
573
615
  }
574
616
  const palette = resolveTrailPalette(trailColor);
575
617
  const last = palette[palette.length - 1];
576
- const { r, g, b } = parseColorToRgb(last);
618
+ const { r, g, b } = colorToRgb(last);
577
619
  return `rgb(${r},${g},${b})`;
578
620
  }
579
621
  function warnIfTrailColorMismatch(trailColor, trailStyle) {
@@ -594,8 +636,8 @@ function warnIfTrailColorMismatch(trailColor, trailStyle) {
594
636
  var getHeadDotRadius = (w, h) => Math.max(1, 3 * Math.sqrt(Math.min(w, h) / 160));
595
637
  var WHITE_HEX = "#ffffff";
596
638
  function colorToRgbComponents(color) {
597
- const c = parseColorToRgb(color);
598
- return `${c.r},${c.g},${c.b}`;
639
+ const { r, g, b } = colorToRgb(color);
640
+ return `${r},${g},${b}`;
599
641
  }
600
642
  function applyDprSizing(target, logicalWidth, logicalHeight, dpr) {
601
643
  target.style.width = `${logicalWidth}px`;
@@ -617,6 +659,7 @@ function createRenderer(options) {
617
659
  let headColor = userHeadColor ?? resolveHeadColor(trailColor, trailStyle);
618
660
  let trailSolidRgb = colorToRgbComponents(resolveTrailMainColor(trailColor));
619
661
  let trailPalette = resolveTrailPalette(trailColor);
662
+ let trailPaletteOklab = trailPalette.map((c) => parseColorToOklab(c));
620
663
  warnIfTrailColorMismatch(trailColor, trailStyle);
621
664
  const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
622
665
  function setupCanvas() {
@@ -728,8 +771,8 @@ function createRenderer(options) {
728
771
  ctx.fillStyle = `rgba(${trailSolidRgb},${opacity})`;
729
772
  } else {
730
773
  const timeOffset = trailStyle === "gradient-animated" ? gradientAnimTime * 5e-4 : 0;
731
- const color = getPaletteColor(trailPalette, progress, timeOffset);
732
- ctx.fillStyle = `rgba(${color.r},${color.g},${color.b},${opacity})`;
774
+ const { r, g, b } = oklabToRgb(getPaletteColor(trailPaletteOklab, progress, timeOffset));
775
+ ctx.fillStyle = `rgba(${r},${g},${b},${opacity})`;
733
776
  }
734
777
  ctx.beginPath();
735
778
  ctx.moveTo(l0x, l0y);
@@ -863,6 +906,7 @@ function createRenderer(options) {
863
906
  trailColor = partial.trailColor;
864
907
  trailSolidRgb = colorToRgbComponents(resolveTrailMainColor(trailColor));
865
908
  trailPalette = resolveTrailPalette(trailColor);
909
+ trailPaletteOklab = trailPalette.map((c) => parseColorToOklab(c));
866
910
  }
867
911
  if (partial.skeletonColor !== void 0) {
868
912
  skeletonColor = partial.skeletonColor;
@@ -946,8 +990,8 @@ function el(tag) {
946
990
  return document.createElementNS("http://www.w3.org/2000/svg", tag);
947
991
  }
948
992
  function colorToRgbAttr(color) {
949
- const c = parseColorToRgb(color);
950
- return `rgb(${c.r},${c.g},${c.b})`;
993
+ const { r, g, b } = colorToRgb(color);
994
+ return `rgb(${r},${g},${b})`;
951
995
  }
952
996
  function createSVGRenderer(options) {
953
997
  const { container, engine } = options;
@@ -965,6 +1009,7 @@ function createSVGRenderer(options) {
965
1009
  let headRadius;
966
1010
  let trailSolid = colorToRgbAttr(resolveTrailMainColor(trailColor));
967
1011
  let trailPalette = resolveTrailPalette(trailColor);
1012
+ let trailPaletteOklab = trailPalette.map((c) => parseColorToOklab(c));
968
1013
  const ariaLabel = options.ariaLabel ?? "Loading";
969
1014
  warnIfTrailColorMismatch(trailColor, trailStyle);
970
1015
  const viewSize = 100;
@@ -1079,7 +1124,7 @@ function createSVGRenderer(options) {
1079
1124
  trailPaths[i].setAttribute("fill-opacity", opacity.toFixed(3));
1080
1125
  if (trailStyle !== "default") {
1081
1126
  const timeOffset = trailStyle === "gradient-animated" ? gradientAnimTime * 5e-4 : 0;
1082
- const { r, g, b } = getPaletteColor(trailPalette, progress, timeOffset);
1127
+ const { r, g, b } = oklabToRgb(getPaletteColor(trailPaletteOklab, progress, timeOffset));
1083
1128
  trailPaths[i].setAttribute("fill", `rgb(${r},${g},${b})`);
1084
1129
  }
1085
1130
  }
@@ -1231,6 +1276,7 @@ function createSVGRenderer(options) {
1231
1276
  trailColor = partial.trailColor;
1232
1277
  trailSolid = colorToRgbAttr(resolveTrailMainColor(trailColor));
1233
1278
  trailPalette = resolveTrailPalette(trailColor);
1279
+ trailPaletteOklab = trailPalette.map((c) => parseColorToOklab(c));
1234
1280
  if (trailStyle === "default") {
1235
1281
  for (const p of trailPaths) {
1236
1282
  p.setAttribute("fill", trailSolid);