@5even7/dlc-ui 0.2.0 → 0.2.2

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
@@ -432,7 +432,7 @@ var DEFAULTS = {
432
432
  renderer: 'auto',
433
433
  respectReducedMotion: true,
434
434
  mouseColor: true,
435
- showCopy: true
435
+ textRatio: 39
436
436
  },
437
437
  progress: {
438
438
  width: 454,
@@ -441,43 +441,250 @@ var DEFAULTS = {
441
441
  max: 100,
442
442
  draggable: true,
443
443
  keyboard: true,
444
+ disabled: false,
445
+ valueSuffix: '%',
444
446
  edgeStyle: 'flow',
445
447
  quality: 'auto',
446
448
  renderer: 'auto',
447
- showCopy: true,
449
+ textRatio: 54,
450
+ showValue: true,
448
451
  respectReducedMotion: true
449
452
  }
450
453
  };
451
454
 
452
- /**
453
- * All user-facing copy lives here so wording/brand changes never require a
454
- * global search. Templates use {brand} {code} {name} placeholders.
455
+ /**
456
+ * Internal accessibility labels and small UI text. Since the copy API was
457
+ * removed (slots own the text area), these are no longer user-overridable.
458
+ * Templates use {brand} {code} {name} placeholders.
455
459
  */
456
460
  var COPY = {
457
461
  brandName: '画境观屿',
458
- dragLabel: 'DRAG',
459
462
  valueSuffix: '%',
460
463
  progressAria: '{brand} {code} 加载进度',
461
464
  capsuleAria: '打开 {name} 沉浸预览'
462
465
  };
463
466
 
464
- function hexToRgb01$1(hex) {
465
- var normalized = hex.replace('#', '');
466
- var value = Number.parseInt(normalized, 16);
467
+ /**
468
+ * Color helpers. `normalizeColor` converts any supported CSS color
469
+ * (hex / named / rgb() / rgba()) into a 6-digit hex string, so renderers can
470
+ * keep working with #rrggbb only. rgba() alpha is intentionally dropped:
471
+ * the WebGL shader has no per-color alpha channel, and the component is
472
+ * opaque by design — use component-level `opacity` for transparency.
473
+ */
474
+
475
+ var NAMED_COLORS = {
476
+ aliceblue: '#f0f8ff',
477
+ antiquewhite: '#faebd7',
478
+ aqua: '#00ffff',
479
+ aquamarine: '#7fffd4',
480
+ azure: '#f0ffff',
481
+ beige: '#f5f5dc',
482
+ bisque: '#ffe4c4',
483
+ black: '#000000',
484
+ blanchedalmond: '#ffebcd',
485
+ blue: '#0000ff',
486
+ blueviolet: '#8a2be2',
487
+ brown: '#a52a2a',
488
+ burlywood: '#deb887',
489
+ cadetblue: '#5f9ea0',
490
+ chartreuse: '#7fff00',
491
+ chocolate: '#d2691e',
492
+ coral: '#ff7f50',
493
+ cornflowerblue: '#6495ed',
494
+ cornsilk: '#fff8dc',
495
+ crimson: '#dc143c',
496
+ cyan: '#00ffff',
497
+ darkblue: '#00008b',
498
+ darkcyan: '#008b8b',
499
+ darkgoldenrod: '#b8860b',
500
+ darkgray: '#a9a9a9',
501
+ darkgreen: '#006400',
502
+ darkgrey: '#a9a9a9',
503
+ darkkhaki: '#bdb76b',
504
+ darkmagenta: '#8b008b',
505
+ darkolivegreen: '#556b2f',
506
+ darkorange: '#ff8c00',
507
+ darkorchid: '#9932cc',
508
+ darkred: '#8b0000',
509
+ darksalmon: '#e9967a',
510
+ darkseagreen: '#8fbc8f',
511
+ darkslateblue: '#483d8b',
512
+ darkslategray: '#2f4f4f',
513
+ darkslategrey: '#2f4f4f',
514
+ darkturquoise: '#00ced1',
515
+ darkviolet: '#9400d3',
516
+ deeppink: '#ff1493',
517
+ deepskyblue: '#00bfff',
518
+ dimgray: '#696969',
519
+ dimgrey: '#696969',
520
+ dodgerblue: '#1e90ff',
521
+ firebrick: '#b22222',
522
+ floralwhite: '#fffaf0',
523
+ forestgreen: '#228b22',
524
+ fuchsia: '#ff00ff',
525
+ gainsboro: '#dcdcdc',
526
+ ghostwhite: '#f8f8ff',
527
+ gold: '#ffd700',
528
+ goldenrod: '#daa520',
529
+ gray: '#808080',
530
+ green: '#008000',
531
+ greenyellow: '#adff2f',
532
+ grey: '#808080',
533
+ honeydew: '#f0fff0',
534
+ hotpink: '#ff69b4',
535
+ indianred: '#cd5c5c',
536
+ indigo: '#4b0082',
537
+ ivory: '#fffff0',
538
+ khaki: '#f0e68c',
539
+ lavender: '#e6e6fa',
540
+ lavenderblush: '#fff0f5',
541
+ lawngreen: '#7cfc00',
542
+ lemonchiffon: '#fffacd',
543
+ lightblue: '#add8e6',
544
+ lightcoral: '#f08080',
545
+ lightcyan: '#e0ffff',
546
+ lightgoldenrodyellow: '#fafad2',
547
+ lightgray: '#d3d3d3',
548
+ lightgreen: '#90ee90',
549
+ lightgrey: '#d3d3d3',
550
+ lightpink: '#ffb6c1',
551
+ lightsalmon: '#ffa07a',
552
+ lightseagreen: '#20b2aa',
553
+ lightskyblue: '#87cefa',
554
+ lightslategray: '#778899',
555
+ lightslategrey: '#778899',
556
+ lightsteelblue: '#b0c4de',
557
+ lightyellow: '#ffffe0',
558
+ lime: '#00ff00',
559
+ limegreen: '#32cd32',
560
+ linen: '#faf0e6',
561
+ magenta: '#ff00ff',
562
+ maroon: '#800000',
563
+ mediumaquamarine: '#66cdaa',
564
+ mediumblue: '#0000cd',
565
+ mediumorchid: '#ba55d3',
566
+ mediumpurple: '#9370db',
567
+ mediumseagreen: '#3cb371',
568
+ mediumslateblue: '#7b68ee',
569
+ mediumspringgreen: '#00fa9a',
570
+ mediumturquoise: '#48d1cc',
571
+ mediumvioletred: '#c71585',
572
+ midnightblue: '#191970',
573
+ mintcream: '#f5fffa',
574
+ mistyrose: '#ffe4e1',
575
+ moccasin: '#ffe4b5',
576
+ navajowhite: '#ffdead',
577
+ navy: '#000080',
578
+ oldlace: '#fdf5e6',
579
+ olive: '#808000',
580
+ olivedrab: '#6b8e23',
581
+ orange: '#ffa500',
582
+ orangered: '#ff4500',
583
+ orchid: '#da70d6',
584
+ palegoldenrod: '#eee8aa',
585
+ palegreen: '#98fb98',
586
+ paleturquoise: '#afeeee',
587
+ palevioletred: '#db7093',
588
+ papayawhip: '#ffefd5',
589
+ peachpuff: '#ffdab9',
590
+ peru: '#cd853f',
591
+ pink: '#ffc0cb',
592
+ plum: '#dda0dd',
593
+ powderblue: '#b0e0e6',
594
+ purple: '#800080',
595
+ rebeccapurple: '#663399',
596
+ red: '#ff0000',
597
+ rosybrown: '#bc8f8f',
598
+ royalblue: '#4169e1',
599
+ saddlebrown: '#8b4513',
600
+ salmon: '#fa8072',
601
+ sandybrown: '#f4a460',
602
+ seagreen: '#2e8b57',
603
+ seashell: '#fff5ee',
604
+ sienna: '#a0522d',
605
+ silver: '#c0c0c0',
606
+ skyblue: '#87ceeb',
607
+ slateblue: '#6a5acd',
608
+ slategray: '#708090',
609
+ slategrey: '#708090',
610
+ snow: '#fffafa',
611
+ springgreen: '#00ff7f',
612
+ steelblue: '#4682b4',
613
+ tan: '#d2b48c',
614
+ teal: '#008080',
615
+ thistle: '#d8bfd8',
616
+ tomato: '#ff6347',
617
+ turquoise: '#40e0d0',
618
+ violet: '#ee82ee',
619
+ wheat: '#f5deb3',
620
+ white: '#ffffff',
621
+ whitesmoke: '#f5f5f5',
622
+ yellow: '#ffff00',
623
+ yellowgreen: '#9acd32'
624
+ };
625
+ function clampChannel(value) {
626
+ return Math.min(255, Math.max(0, Math.round(value)));
627
+ }
628
+ function normalizeRgbArgs(args) {
629
+ var parts = args.split(/[,\s/]+/).filter(Boolean);
630
+ if (parts.length < 3) return null;
631
+ var to255 = function to255(value) {
632
+ if (typeof value !== 'string' || !value) return null;
633
+ if (value.endsWith('%')) return clampChannel(parseFloat(value) / 100 * 255);
634
+ var number = Number(value);
635
+ return Number.isFinite(number) ? clampChannel(number) : null;
636
+ };
637
+ var red = to255(parts[0]);
638
+ var green = to255(parts[1]);
639
+ var blue = to255(parts[2]);
640
+ if (red === null || green === null || blue === null) return null;
641
+ return "#".concat([red, green, blue].map(function (n) {
642
+ return n.toString(16).padStart(2, '0');
643
+ }).join(''));
644
+ }
645
+
646
+ /**
647
+ * Convert any supported CSS color into `#rrggbb`. Returns null when the
648
+ * value cannot be parsed. Supports: #rgb / #rrggbb (case-insensitive),
649
+ * CSS named colors, rgb() / rgba() with comma or modern space syntax,
650
+ * including percentages. Alpha in rgba() is ignored.
651
+ */
652
+ function normalizeColor(value) {
653
+ if (typeof value !== 'string') return null;
654
+ var input = value.trim();
655
+ if (!input) return null;
656
+ var lower = input.toLowerCase();
657
+ if (NAMED_COLORS[lower]) return NAMED_COLORS[lower];
658
+ if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(input)) {
659
+ var hex = input.slice(1).toLowerCase();
660
+ if (hex.length === 3) return "#".concat(hex.split('').map(function (c) {
661
+ return c + c;
662
+ }).join(''));
663
+ return "#".concat(hex);
664
+ }
665
+ var match = input.match(/^rgba?\((.*)\)$/i);
666
+ if (match) return normalizeRgbArgs(match[1]);
667
+ return null;
668
+ }
669
+ function hexToRgb01$1(color) {
670
+ var hex = normalizeColor(color);
671
+ if (!hex) return [0, 0, 0];
672
+ var value = Number.parseInt(hex.slice(1), 16);
467
673
  return [(value >> 16 & 255) / 255, (value >> 8 & 255) / 255, (value & 255) / 255];
468
674
  }
469
- function hexToRgba(hex) {
675
+ function hexToRgba(color) {
470
676
  var alpha = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
471
- var normalized = hex.replace('#', '');
472
- var value = Number.parseInt(normalized, 16);
677
+ var hex = normalizeColor(color);
678
+ if (!hex) return 'rgba(0, 0, 0, 0)';
679
+ var value = Number.parseInt(hex.slice(1), 16);
473
680
  var red = value >> 16 & 255;
474
681
  var green = value >> 8 & 255;
475
682
  var blue = value & 255;
476
683
  return "rgba(".concat(red, ", ").concat(green, ", ").concat(blue, ", ").concat(alpha, ")");
477
684
  }
478
685
  function validateColors(colors) {
479
- return Array.isArray(colors) && colors.length === 4 && colors.every(function (c) {
480
- return typeof c === 'string' && /^#[0-9a-f]{6}$/i.test(c);
686
+ return Array.isArray(colors) && colors.length === 4 && colors.every(function (color) {
687
+ return normalizeColor(color) !== null;
481
688
  });
482
689
  }
483
690
 
@@ -555,6 +762,26 @@ var CosmicRenderer = /*#__PURE__*/function () {
555
762
  this.options.dprCap = cap;
556
763
  this.resize();
557
764
  }
765
+ }, {
766
+ key: "setMouseColor",
767
+ value: function setMouseColor(enabled) {
768
+ this.options.mouseColor = enabled !== false;
769
+ if (this.options.mouseColor) {
770
+ if (this.onPointerMove) return this;
771
+ _assertClassBrand(_CosmicRenderer_brand, this, _bindEvents).call(this);
772
+ return this;
773
+ }
774
+ if (this.onPointerMove) {
775
+ var target = this.eventTarget;
776
+ target.removeEventListener('pointermove', this.onPointerMove);
777
+ target.removeEventListener('pointerdown', this.onPointerMove);
778
+ target.removeEventListener('pointerleave', this.onPointerLeave);
779
+ this.onPointerMove = null;
780
+ this.onPointerLeave = null;
781
+ }
782
+ this.motionTarget = 0;
783
+ return this;
784
+ }
558
785
  }, {
559
786
  key: "randomize",
560
787
  value: function randomize() {
@@ -703,6 +930,8 @@ var FallbackRenderer = /*#__PURE__*/function () {
703
930
  var _this$canvas = this.canvas,
704
931
  width = _this$canvas.width,
705
932
  height = _this$canvas.height;
933
+ var t = time * (this.preset.speed || 1);
934
+ var phase = (this.preset.seed || 0) * 0.7;
706
935
  var gradient = ctx.createLinearGradient(0, 0, width, height);
707
936
  gradient.addColorStop(0, this.preset.colors[0]);
708
937
  gradient.addColorStop(0.38, this.preset.colors[1]);
@@ -712,8 +941,8 @@ var FallbackRenderer = /*#__PURE__*/function () {
712
941
  ctx.fillRect(0, 0, width, height);
713
942
  ctx.globalCompositeOperation = 'screen';
714
943
  for (var index = 0; index < 6; index += 1) {
715
- var x = (0.5 + 0.45 * Math.sin(time * 0.32 + index * 1.7)) * width;
716
- var y = (0.5 + 0.4 * Math.cos(time * 0.25 + index)) * height;
944
+ var x = (0.5 + 0.45 * Math.sin(t * 0.32 + index * 1.7 + phase)) * width;
945
+ var y = (0.5 + 0.4 * Math.cos(t * 0.25 + index + phase)) * height;
717
946
  var radius = Math.max(width, height) * (0.08 + index % 3 * 0.04);
718
947
  var glow = ctx.createRadialGradient(x, y, 0, x, y, radius);
719
948
  glow.addColorStop(0, rgb(this.preset.colors[(index + 1) % 4], 0.24));
@@ -737,7 +966,12 @@ var FallbackRenderer = /*#__PURE__*/function () {
737
966
  }
738
967
  }, {
739
968
  key: "randomize",
740
- value: function randomize() {}
969
+ value: function randomize() {
970
+ if (this.preset) this.preset.seed = Math.random() * 100;
971
+ }
972
+ }, {
973
+ key: "setMouseColor",
974
+ value: function setMouseColor() {}
741
975
  }, {
742
976
  key: "dispose",
743
977
  value: function dispose() {}
@@ -855,16 +1089,34 @@ function createVisibilityGuard(element) {
855
1089
  };
856
1090
  }
857
1091
 
1092
+ /**
1093
+ * Run a callback asynchronously, right after the current task and before the
1094
+ * next paint. Used for mount-time events (ready / error) so listeners that
1095
+ * are attached after create() still receive them. Falls back for legacy
1096
+ * browsers that lack queueMicrotask / Promise.
1097
+ */
1098
+ function nextTick(fn) {
1099
+ if (typeof queueMicrotask === 'function') {
1100
+ queueMicrotask(fn);
1101
+ } else if (typeof Promise !== 'undefined' && typeof Promise.resolve === 'function') {
1102
+ Promise.resolve().then(fn);
1103
+ } else {
1104
+ setTimeout(fn, 0);
1105
+ }
1106
+ }
1107
+
858
1108
  function prefersReducedMotion$2() {
859
1109
  return typeof matchMedia !== 'undefined' && matchMedia('(prefers-reduced-motion: reduce)').matches;
860
1110
  }
861
1111
 
862
1112
  /**
863
- * Mount a cosmic (nebula) capsule into `container`.
864
- *
865
- * Options: preset (id/code/name or object), width, height (number=px or
866
- * string with px/%/vw/vh/em/rem), colors, seed, speed, quality,
867
- * respectReducedMotion, copy, cssVars.
1113
+ * Mount a cosmic (nebula) capsule into `container`.
1114
+ *
1115
+ * Options: preset (literary name), width, height (number=px or string with
1116
+ * px/%/vw/vh/em/rem), colors, seed, speed, textRatio (0-100, text region
1117
+ * width in percent), text (HTML string or DOM nodes for the text slot),
1118
+ * colorContent (HTML string or DOM nodes for the color slot), quality,
1119
+ * renderer, respectReducedMotion, mouseColor, cssVars.
868
1120
  */
869
1121
  function createCapsule(container) {
870
1122
  var _options$preset;
@@ -874,51 +1126,71 @@ function createCapsule(container) {
874
1126
  }
875
1127
  var preset = _objectSpread2({}, getPreset('capsule', (_options$preset = options.preset) !== null && _options$preset !== void 0 ? _options$preset : '初光'));
876
1128
  var merged = normalizeOptions(DEFAULTS.capsule, preset, options);
877
- var copy = _objectSpread2(_objectSpread2({}, COPY), merged.copy || {});
1129
+ var normalizedColors = merged.colors.map(normalizeColor);
1130
+ if (normalizedColors.every(Boolean)) preset.colors = normalizedColors;
1131
+ preset.seed = merged.seed;
1132
+ preset.speed = merged.speed;
1133
+ var customLabel = typeof options.label === 'string' && options.label ? options.label : null;
878
1134
  var root = document.createElement('div');
879
1135
  root.className = 'hj-capsule-root hj-capsule-cosmic';
880
1136
  root.dataset.group = preset.group;
881
1137
  root.dataset.mode = 'nebula';
882
1138
  root.dataset.theme = preset.theme || 'light';
883
- root.setAttribute('aria-label', copy.capsuleAria.replace('{name}', preset.name));
884
- var copyEnabled = copy.enabled !== false && merged.showCopy !== false;
885
- var copyText = function copyText(key, fallback) {
886
- var value = copy[key];
887
- return value === undefined ? fallback : value;
1139
+ root.setAttribute('role', 'img');
1140
+ var updateAria = function updateAria() {
1141
+ root.setAttribute('aria-label', customLabel || COPY.capsuleAria.replace('{name}', preset.name));
888
1142
  };
889
- function buildCopyHtml() {
890
- var codeText = copyText('code', preset.code);
891
- var nameText = copyText('name', preset.name);
892
- var subtitleText = copyText('subtitle', preset.subtitle);
893
- var stateText = copyText('state', 'LIVE COSMIC STUDY');
894
- var html = '';
895
- if (codeText) html += "<span class=\"hj-capsule-code\">".concat(codeText, "</span>");
896
- if (nameText) html += "<span class=\"hj-capsule-name\">".concat(nameText, "</span>");
897
- if (subtitleText) html += "<span class=\"hj-capsule-brand\">".concat(subtitleText, "</span>");
898
- if (stateText) html += "<span class=\"hj-capsule-state\">".concat(stateText, "</span>");
899
- return html;
900
- }
901
- var copyLayer = document.createElement('div');
902
- copyLayer.className = 'hj-capsule-copy';
903
- var renderCopy = function renderCopy() {
904
- if (copyEnabled) copyLayer.innerHTML = buildCopyHtml();
905
- };
906
- renderCopy();
907
- var isHexColor = function isHexColor(value) {
908
- return typeof value === 'string' && /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(value);
1143
+ updateAria();
1144
+
1145
+ // Slot containers are always present but transparent by default: without
1146
+ // content they are invisible, so "no slot" behaves like the old
1147
+ // showCopy=false. They only provide geometry — no typography, padding or
1148
+ // background is imposed on user content (see docs: 插槽 CSS 约定).
1149
+ var fillContent = function fillContent(layer, content) {
1150
+ layer.innerHTML = '';
1151
+ if (content == null) return;
1152
+ if (typeof content === 'string') {
1153
+ layer.innerHTML = content;
1154
+ return;
1155
+ }
1156
+ var nodes = Array.isArray(content) ? content : [content];
1157
+ var _iterator = _createForOfIteratorHelper(nodes),
1158
+ _step;
1159
+ try {
1160
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
1161
+ var node = _step.value;
1162
+ if (node && typeof node.nodeType === 'number') layer.appendChild(node);
1163
+ }
1164
+ } catch (err) {
1165
+ _iterator.e(err);
1166
+ } finally {
1167
+ _iterator.f();
1168
+ }
909
1169
  };
910
- if (copy.background) {
911
- root.style.setProperty('--hj-copy-bg', isHexColor(copy.background) ? "linear-gradient(90deg, ".concat(copy.background, " 0%, ").concat(copy.background, " 74%, ").concat(hexToRgba(copy.background, 0.97), " 84%, ").concat(hexToRgba(copy.background, 0), " 100%)") : copy.background);
912
- }
913
- if (copy.color) root.style.setProperty('--hj-copy-color', copy.color);
1170
+ var textLayer = document.createElement('div');
1171
+ textLayer.className = 'hj-capsule-text hj-capsule-copy';
1172
+ var visualLayer = document.createElement('div');
1173
+ visualLayer.className = 'hj-capsule-visual';
1174
+ fillContent(textLayer, options.text);
1175
+ fillContent(visualLayer, options.colorContent);
914
1176
  var canvas = document.createElement('canvas');
915
1177
  canvas.className = 'hj-capsule-canvas';
916
1178
  canvas.setAttribute('aria-hidden', 'true');
917
- if (copyEnabled) root.appendChild(copyLayer);
1179
+ root.appendChild(textLayer);
1180
+ root.appendChild(visualLayer);
918
1181
  root.appendChild(canvas);
919
1182
  container.appendChild(root);
920
1183
  var emitter = createEmitter();
921
1184
  var paused = merged.respectReducedMotion && prefersReducedMotion$2();
1185
+ var disposed = false;
1186
+ var dirty = {
1187
+ preset: false,
1188
+ seed: false,
1189
+ speed: false,
1190
+ colors: false,
1191
+ textRatio: false,
1192
+ cssVars: false
1193
+ };
922
1194
  var renderer;
923
1195
  var useWebgl = merged.renderer !== 'canvas2d';
924
1196
  if (useWebgl) {
@@ -928,8 +1200,11 @@ function createCapsule(container) {
928
1200
  });
929
1201
  } catch (error) {
930
1202
  renderer = new FallbackRenderer(canvas, merged);
931
- emitter.emit('error', {
932
- message: String(error && error.message ? error.message : error)
1203
+ nextTick(function () {
1204
+ if (disposed) return;
1205
+ emitter.emit('error', {
1206
+ message: String(error && error.message ? error.message : error)
1207
+ });
933
1208
  });
934
1209
  }
935
1210
  } else {
@@ -943,6 +1218,9 @@ function createCapsule(container) {
943
1218
  var name = _Object$keys[_i];
944
1219
  root.style.setProperty(name, vars[name]);
945
1220
  }
1221
+ if (merged.textRatio !== undefined) {
1222
+ root.style.setProperty('--hj-text-width', "".concat(merged.textRatio, "%"));
1223
+ }
946
1224
  renderer.resize();
947
1225
  };
948
1226
  applySize();
@@ -992,13 +1270,16 @@ function createCapsule(container) {
992
1270
  }, function () {
993
1271
  return paused;
994
1272
  });
995
- emitter.emit('ready', {
996
- preset: _objectSpread2({}, preset)
1273
+ nextTick(function () {
1274
+ if (disposed) return;
1275
+ emitter.emit('ready', {
1276
+ preset: _objectSpread2({}, preset)
1277
+ });
997
1278
  });
998
- var syncCopy = function syncCopy() {
1279
+ var syncTheme = function syncTheme() {
999
1280
  root.dataset.mode = 'nebula';
1000
1281
  root.dataset.theme = preset.theme || 'light';
1001
- renderCopy();
1282
+ updateAria();
1002
1283
  };
1003
1284
  return {
1004
1285
  element: root,
@@ -1008,9 +1289,12 @@ function createCapsule(container) {
1008
1289
  off: emitter.off,
1009
1290
  setPreset: function setPreset(ref) {
1010
1291
  var next = getPreset('capsule', ref);
1292
+ dirty.preset = true;
1011
1293
  Object.assign(preset, next);
1012
- renderer.setPreset(next);
1013
- syncCopy();
1294
+ var nextColors = preset.colors.map(normalizeColor);
1295
+ if (nextColors.every(Boolean)) preset.colors = nextColors;
1296
+ renderer.setPreset(_objectSpread2({}, preset));
1297
+ syncTheme();
1014
1298
  renderer.resize();
1015
1299
  emitter.emit('presetchange', {
1016
1300
  preset: _objectSpread2({}, next)
@@ -1018,16 +1302,21 @@ function createCapsule(container) {
1018
1302
  return this;
1019
1303
  },
1020
1304
  setColors: function setColors(colors) {
1021
- if (!Array.isArray(colors) || colors.length !== 4) return this;
1022
- preset.colors = colors;
1305
+ var next = Array.isArray(colors) ? colors.map(normalizeColor) : [];
1306
+ if (next.length !== 4 || next.some(function (color) {
1307
+ return !color;
1308
+ })) return this;
1309
+ dirty.colors = true;
1310
+ preset.colors = next;
1023
1311
  renderer.setPreset(_objectSpread2(_objectSpread2({}, preset), {}, {
1024
- colors: colors
1312
+ colors: next
1025
1313
  }));
1026
1314
  return this;
1027
1315
  },
1028
1316
  setSeed: function setSeed(seed) {
1029
1317
  var value = Number(seed);
1030
1318
  if (!Number.isFinite(value)) return this;
1319
+ dirty.seed = true;
1031
1320
  preset.seed = value;
1032
1321
  renderer.setPreset(_objectSpread2({}, preset));
1033
1322
  return this;
@@ -1035,10 +1324,42 @@ function createCapsule(container) {
1035
1324
  setSpeed: function setSpeed(speed) {
1036
1325
  var value = Number(speed);
1037
1326
  if (!Number.isFinite(value)) return this;
1327
+ dirty.speed = true;
1038
1328
  preset.speed = value;
1039
1329
  renderer.setPreset(_objectSpread2({}, preset));
1040
1330
  return this;
1041
1331
  },
1332
+ setText: function setText(content) {
1333
+ fillContent(textLayer, content);
1334
+ return this;
1335
+ },
1336
+ setColorContent: function setColorContent(content) {
1337
+ fillContent(visualLayer, content);
1338
+ return this;
1339
+ },
1340
+ setTextRatio: function setTextRatio(ratio) {
1341
+ var value = Number(ratio);
1342
+ if (!Number.isFinite(value) || value < 0 || value > 100) return this;
1343
+ dirty.textRatio = true;
1344
+ merged.textRatio = value;
1345
+ root.style.setProperty('--hj-text-width', "".concat(value, "%"));
1346
+ return this;
1347
+ },
1348
+ setCssVars: function setCssVars(vars) {
1349
+ if (!vars || _typeof(vars) !== 'object') return this;
1350
+ dirty.cssVars = true;
1351
+ merged.cssVars = _objectSpread2(_objectSpread2({}, merged.cssVars || {}), vars);
1352
+ for (var _i2 = 0, _Object$keys2 = Object.keys(vars); _i2 < _Object$keys2.length; _i2++) {
1353
+ var name = _Object$keys2[_i2];
1354
+ root.style.setProperty(name, vars[name]);
1355
+ }
1356
+ return this;
1357
+ },
1358
+ setLabel: function setLabel(label) {
1359
+ customLabel = typeof label === 'string' && label ? label : null;
1360
+ updateAria();
1361
+ return this;
1362
+ },
1042
1363
  setSize: function setSize(width, height) {
1043
1364
  if (width !== undefined) merged.width = width;
1044
1365
  if (height !== undefined) merged.height = height;
@@ -1046,7 +1367,7 @@ function createCapsule(container) {
1046
1367
  return this;
1047
1368
  },
1048
1369
  randomize: function randomize() {
1049
- renderer.randomize();
1370
+ this.setSeed(Math.random() * 100);
1050
1371
  return this;
1051
1372
  },
1052
1373
  pause: function pause() {
@@ -1063,12 +1384,16 @@ function createCapsule(container) {
1063
1384
  return this;
1064
1385
  },
1065
1386
  dispose: function dispose() {
1387
+ disposed = true;
1066
1388
  unsubscribe();
1067
1389
  visibility.dispose();
1068
1390
  if (resizeObserver) resizeObserver.disconnect();else window.removeEventListener('resize', renderer.resize);
1069
1391
  renderer.dispose();
1070
1392
  root.remove();
1071
- }
1393
+ },
1394
+ textRatio: merged.textRatio,
1395
+ cssVars: merged.cssVars,
1396
+ dirty: dirty
1072
1397
  };
1073
1398
  }
1074
1399
 
@@ -1572,7 +1897,8 @@ var FLOW_PROFILES = {
1572
1897
  };
1573
1898
  var ProgressCapsuleController = /*#__PURE__*/function () {
1574
1899
  function ProgressCapsuleController(_ref) {
1575
- var _options$min,
1900
+ var _options$valueSuffix,
1901
+ _options$min,
1576
1902
  _options$max,
1577
1903
  _options$value,
1578
1904
  _this = this;
@@ -1582,7 +1908,8 @@ var ProgressCapsuleController = /*#__PURE__*/function () {
1582
1908
  preset = _ref.preset,
1583
1909
  emitter = _ref.emitter,
1584
1910
  options = _ref.options,
1585
- copy = _ref.copy;
1911
+ copy = _ref.copy,
1912
+ dirty = _ref.dirty;
1586
1913
  _classCallCheck(this, ProgressCapsuleController);
1587
1914
  this.root = root;
1588
1915
  this.canvas = canvas;
@@ -1591,6 +1918,8 @@ var ProgressCapsuleController = /*#__PURE__*/function () {
1591
1918
  this.emitter = emitter;
1592
1919
  this.options = options;
1593
1920
  this.copy = copy;
1921
+ this.dirty = dirty;
1922
+ this.valueSuffix = (_options$valueSuffix = options.valueSuffix) !== null && _options$valueSuffix !== void 0 ? _options$valueSuffix : copy.valueSuffix;
1594
1923
  this.profile = preset.edgeStyle === 'tide' ? FLOW_PROFILES.tide : FLOW_PROFILES[preset.id] || FLOW_PROFILES['visual-training'];
1595
1924
  this.min = (_options$min = options.min) !== null && _options$min !== void 0 ? _options$min : 0;
1596
1925
  this.max = (_options$max = options.max) !== null && _options$max !== void 0 ? _options$max : 100;
@@ -1630,12 +1959,24 @@ var ProgressCapsuleController = /*#__PURE__*/function () {
1630
1959
  var rounded = Math.round(this.value);
1631
1960
  this.root.style.setProperty('--progress', this.value.toFixed(2));
1632
1961
  this.root.setAttribute('aria-valuenow', String(rounded));
1633
- this.root.setAttribute('aria-valuetext', "".concat(rounded).concat(this.copy.valueSuffix));
1634
- this.valueElement.textContent = "".concat(rounded).concat(this.copy.valueSuffix);
1635
- if (!this.suppressEvents) this.emitter.emit('change', {
1636
- value: this.value,
1637
- source: source
1638
- });
1962
+ this.root.setAttribute('aria-valuetext', "".concat(rounded).concat(this.valueSuffix));
1963
+ if (this.valueElement) this.valueElement.textContent = "".concat(rounded).concat(this.valueSuffix);
1964
+ if (!this.suppressEvents) {
1965
+ if (this.dirty) this.dirty.value = true;
1966
+ this.emitter.emit('change', {
1967
+ value: this.value,
1968
+ source: source
1969
+ });
1970
+ }
1971
+ }
1972
+ }, {
1973
+ key: "setValueSuffix",
1974
+ value: function setValueSuffix(suffix) {
1975
+ this.valueSuffix = String(suffix == null ? '' : suffix);
1976
+ var rounded = Math.round(this.value);
1977
+ if (this.valueElement) this.valueElement.textContent = "".concat(rounded).concat(this.valueSuffix);
1978
+ this.root.setAttribute('aria-valuetext', "".concat(rounded).concat(this.valueSuffix));
1979
+ return this;
1639
1980
  }
1640
1981
  }, {
1641
1982
  key: "resizeCanvas",
@@ -1810,7 +2151,8 @@ var ProgressCapsuleController = /*#__PURE__*/function () {
1810
2151
  var width = this.width;
1811
2152
  var height = this.height;
1812
2153
  if (!ctx || width <= 0 || height <= 0) return;
1813
- var shoreline = width * (this.value / 100);
2154
+ var range = Math.max(this.max - this.min, 1);
2155
+ var shoreline = width * Math.min(Math.max((this.value - this.min) / range, 0), 1);
1814
2156
  var time = this.flowTime;
1815
2157
  var _this$preset$colors = _slicedToArray(this.preset.colors, 4),
1816
2158
  dark = _this$preset$colors[0],
@@ -1925,7 +2267,7 @@ var ProgressCapsuleController = /*#__PURE__*/function () {
1925
2267
  value: function updateFromPointer(event) {
1926
2268
  var bounds = this.root.getBoundingClientRect();
1927
2269
  var ratio = bounds.width > 0 ? (event.clientX - bounds.left) / bounds.width : 0;
1928
- this.setProgress(ratio * 100, 'drag');
2270
+ this.setProgress(this.min + ratio * (this.max - this.min), 'drag');
1929
2271
  }
1930
2272
  }, {
1931
2273
  key: "beginDrag",
@@ -1933,6 +2275,8 @@ var ProgressCapsuleController = /*#__PURE__*/function () {
1933
2275
  if (event.button !== undefined && event.button !== 0) return;
1934
2276
  event.preventDefault();
1935
2277
  this.dragging = true;
2278
+ this.pendingPointer = null;
2279
+ this._dragRaf = 0;
1936
2280
  this.root.classList.add('is-dragging');
1937
2281
  try {
1938
2282
  var _this$root$setPointer, _this$root;
@@ -1946,15 +2290,31 @@ var ProgressCapsuleController = /*#__PURE__*/function () {
1946
2290
  }, {
1947
2291
  key: "moveDrag",
1948
2292
  value: function moveDrag(event) {
2293
+ var _this2 = this;
1949
2294
  if (!this.dragging) return;
1950
2295
  event.preventDefault();
1951
- this.updateFromPointer(event);
2296
+ // rAF 合并:一帧最多一次 setProgress/change,视觉仍每帧更新。
2297
+ this.pendingPointer = event;
2298
+ if (this._dragRaf) return;
2299
+ this._dragRaf = requestAnimationFrame(function () {
2300
+ _this2._dragRaf = 0;
2301
+ var pending = _this2.pendingPointer;
2302
+ _this2.pendingPointer = null;
2303
+ if (pending) _this2.updateFromPointer(pending);
2304
+ });
1952
2305
  }
1953
2306
  }, {
1954
2307
  key: "endDrag",
1955
2308
  value: function endDrag(event) {
1956
2309
  if (!this.dragging) return;
1957
2310
  this.dragging = false;
2311
+ if (this._dragRaf) {
2312
+ cancelAnimationFrame(this._dragRaf);
2313
+ this._dragRaf = 0;
2314
+ }
2315
+ var pending = this.pendingPointer;
2316
+ this.pendingPointer = null;
2317
+ if (pending) this.updateFromPointer(pending);
1958
2318
  this.root.classList.remove('is-dragging');
1959
2319
  try {
1960
2320
  var _this$root$hasPointer, _this$root2;
@@ -1969,19 +2329,19 @@ var ProgressCapsuleController = /*#__PURE__*/function () {
1969
2329
  }, {
1970
2330
  key: "bindEvents",
1971
2331
  value: function bindEvents() {
1972
- var _this2 = this;
2332
+ var _this3 = this;
1973
2333
  if (this.options.draggable !== false) {
1974
2334
  this.handlers.pointerdown = function (event) {
1975
- return _this2.beginDrag(event);
2335
+ return _this3.beginDrag(event);
1976
2336
  };
1977
2337
  this.handlers.pointermove = function (event) {
1978
- return _this2.moveDrag(event);
2338
+ return _this3.moveDrag(event);
1979
2339
  };
1980
2340
  this.handlers.pointerup = function (event) {
1981
- return _this2.endDrag(event);
2341
+ return _this3.endDrag(event);
1982
2342
  };
1983
2343
  this.handlers.pointercancel = function (event) {
1984
- return _this2.endDrag(event);
2344
+ return _this3.endDrag(event);
1985
2345
  };
1986
2346
  this.root.addEventListener('pointerdown', this.handlers.pointerdown);
1987
2347
  this.root.addEventListener('pointermove', this.handlers.pointermove);
@@ -1990,23 +2350,24 @@ var ProgressCapsuleController = /*#__PURE__*/function () {
1990
2350
  }
1991
2351
  if (this.options.keyboard !== false) {
1992
2352
  this.handlers.keydown = function (event) {
2353
+ var step = (_this3.max - _this3.min) * 0.02;
1993
2354
  var actions = {
1994
- ArrowLeft: -2,
1995
- ArrowDown: -2,
1996
- ArrowRight: 2,
1997
- ArrowUp: 2,
1998
- PageDown: -10,
1999
- PageUp: 10
2355
+ ArrowLeft: -step,
2356
+ ArrowDown: -step,
2357
+ ArrowRight: step,
2358
+ ArrowUp: step,
2359
+ PageDown: -step * 5,
2360
+ PageUp: step * 5
2000
2361
  };
2001
2362
  if (event.key === 'Home') {
2002
2363
  event.preventDefault();
2003
- _this2.setProgress(_this2.min, 'keyboard');
2364
+ _this3.setProgress(_this3.min, 'keyboard');
2004
2365
  } else if (event.key === 'End') {
2005
2366
  event.preventDefault();
2006
- _this2.setProgress(_this2.max, 'keyboard');
2367
+ _this3.setProgress(_this3.max, 'keyboard');
2007
2368
  } else if (actions[event.key]) {
2008
2369
  event.preventDefault();
2009
- _this2.setProgress(_this2.value + actions[event.key], 'keyboard');
2370
+ _this3.setProgress(_this3.value + actions[event.key], 'keyboard');
2010
2371
  } else {
2011
2372
  return;
2012
2373
  }
@@ -2043,6 +2404,7 @@ var ProgressCapsuleController = /*#__PURE__*/function () {
2043
2404
  }, {
2044
2405
  key: "dispose",
2045
2406
  value: function dispose() {
2407
+ if (this._dragRaf) cancelAnimationFrame(this._dragRaf);
2046
2408
  if (this.resizeObserver) this.resizeObserver.disconnect();else window.removeEventListener('resize', this.resizeCanvas);
2047
2409
  for (var _i = 0, _Object$keys = Object.keys(this.handlers); _i < _Object$keys.length; _i++) {
2048
2410
  var name = _Object$keys[_i];
@@ -2055,11 +2417,14 @@ var ProgressCapsuleController = /*#__PURE__*/function () {
2055
2417
  }();
2056
2418
 
2057
2419
  /**
2058
- * Mount a fluid progress capsule into `container`.
2059
- *
2060
- * Options: preset (id/code/name or object), width, height, value, min, max,
2061
- * draggable, keyboard, colors, edgeStyle, quality,
2062
- * respectReducedMotion, copy, cssVars.
2420
+ * Mount a fluid progress capsule into `container`.
2421
+ *
2422
+ * Options: preset (id/code/name or object), width, height, value, min, max,
2423
+ * draggable, keyboard, colors, edgeStyle, textRatio (0-100, text region
2424
+ * width in percent), text (HTML string or DOM nodes for the text slot),
2425
+ * colorContent (HTML string or DOM nodes for the color slot),
2426
+ * showValue (show/hide the right-side percentage), quality,
2427
+ * respectReducedMotion, cssVars.
2063
2428
  */
2064
2429
  function createProgressCapsule(container) {
2065
2430
  var _options$preset, _merged$min, _merged$max;
@@ -2069,62 +2434,91 @@ function createProgressCapsule(container) {
2069
2434
  }
2070
2435
  var preset = _objectSpread2({}, getPreset('progress', (_options$preset = options.preset) !== null && _options$preset !== void 0 ? _options$preset : '星火'));
2071
2436
  var merged = normalizeOptions(DEFAULTS.progress, preset, options);
2072
- var copy = _objectSpread2(_objectSpread2({}, COPY), merged.copy || {});
2437
+ var normalizedColors = merged.colors.map(normalizeColor);
2438
+ if (normalizedColors.every(Boolean)) preset.colors = normalizedColors;
2439
+ preset.edgeStyle = merged.edgeStyle;
2440
+ var copy = COPY;
2441
+ var customLabel = typeof options.label === 'string' && options.label ? options.label : null;
2442
+ // Vue 的裸布尔属性(<ProgressCapsule disabled />)会传成空字符串,
2443
+ // 这里统一按 true 处理。
2444
+ var disabled = merged.disabled === true || merged.disabled === '' || merged.disabled === 'true';
2445
+ var effectiveDraggable = disabled ? false : merged.draggable !== false;
2446
+ var effectiveKeyboard = disabled ? false : merged.keyboard !== false;
2447
+ var dirty = {
2448
+ preset: false,
2449
+ colors: false,
2450
+ textRatio: false,
2451
+ cssVars: false,
2452
+ edgeStyle: false,
2453
+ value: false
2454
+ };
2073
2455
  var root = document.createElement('div');
2074
2456
  root.className = 'hj-capsule-root hj-progress-root';
2075
2457
  root.setAttribute('role', 'slider');
2076
2458
  root.setAttribute('tabindex', '0');
2077
- root.setAttribute('data-draggable', String(merged.draggable !== false));
2459
+ root.setAttribute('data-draggable', String(effectiveDraggable));
2460
+ if (disabled) {
2461
+ root.setAttribute('aria-disabled', 'true');
2462
+ root.classList.add('is-disabled');
2463
+ }
2078
2464
  root.setAttribute('aria-valuemin', String((_merged$min = merged.min) !== null && _merged$min !== void 0 ? _merged$min : 0));
2079
2465
  root.setAttribute('aria-valuemax', String((_merged$max = merged.max) !== null && _merged$max !== void 0 ? _merged$max : 100));
2080
2466
  root.setAttribute('aria-valuenow', String(preset.initialProgress));
2081
- root.setAttribute('aria-label', interpolate(copy.progressAria, {
2082
- brand: copy.brandName,
2083
- code: preset.code,
2084
- name: preset.name
2085
- }));
2086
- if (merged.keyboard === false) root.setAttribute('tabindex', '-1');
2467
+ var updateAria = function updateAria() {
2468
+ root.setAttribute('aria-label', customLabel || interpolate(copy.progressAria, {
2469
+ brand: copy.brandName,
2470
+ code: preset.code,
2471
+ name: preset.name
2472
+ }));
2473
+ };
2474
+ updateAria();
2475
+ if (!effectiveKeyboard) root.setAttribute('tabindex', '-1');
2087
2476
  var canvas = document.createElement('canvas');
2088
2477
  canvas.className = 'hj-progress-canvas';
2089
2478
  canvas.setAttribute('aria-hidden', 'true');
2090
- var copyEnabled = copy.enabled !== false && merged.showCopy !== false;
2091
- var copyText = function copyText(key, fallback) {
2092
- var value = copy[key];
2093
- return value === undefined ? fallback : value;
2094
- };
2095
- function buildCopyHtml() {
2096
- var html = '';
2097
- var brandText = copy.brandName || '';
2098
- var subtitleText = copyText('subtitle', preset.subtitle);
2099
- if (brandText) html += "<span class=\"hj-progress-name\">".concat(brandText, "</span>");
2100
- if (subtitleText) html += "<span class=\"hj-progress-subtitle\">".concat(subtitleText, "</span>");
2101
- return html;
2102
- }
2103
- var copyLayer = document.createElement('div');
2104
- copyLayer.className = 'hj-progress-copy';
2105
- var renderCopy = function renderCopy() {
2106
- if (copyEnabled) copyLayer.innerHTML = buildCopyHtml();
2479
+
2480
+ // Slot containers are always present but transparent by default; they only
2481
+ // provide geometry, never typography/padding/background (docs: 插槽 CSS 约定).
2482
+ var fillContent = function fillContent(layer, content) {
2483
+ layer.innerHTML = '';
2484
+ if (content == null) return;
2485
+ if (typeof content === 'string') {
2486
+ layer.innerHTML = content;
2487
+ return;
2488
+ }
2489
+ var nodes = Array.isArray(content) ? content : [content];
2490
+ var _iterator2 = _createForOfIteratorHelper(nodes),
2491
+ _step2;
2492
+ try {
2493
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
2494
+ var node = _step2.value;
2495
+ if (node && typeof node.nodeType === 'number') layer.appendChild(node);
2496
+ }
2497
+ } catch (err) {
2498
+ _iterator2.e(err);
2499
+ } finally {
2500
+ _iterator2.f();
2501
+ }
2107
2502
  };
2108
- renderCopy();
2109
- if (copy.background) {
2110
- root.style.setProperty('--hj-progress-copy-bg', copy.background);
2111
- root.style.setProperty('--hj-progress-copy-pad', '6px 12px');
2503
+ var textLayer = document.createElement('div');
2504
+ textLayer.className = 'hj-progress-text hj-progress-copy';
2505
+ var visualLayer = document.createElement('div');
2506
+ visualLayer.className = 'hj-progress-visual';
2507
+ fillContent(textLayer, options.text);
2508
+ fillContent(visualLayer, options.colorContent);
2509
+ var valueElement = merged.showValue === false ? null : document.createElement('div');
2510
+ if (valueElement) {
2511
+ valueElement.className = 'hj-progress-value';
2512
+ valueElement.setAttribute('aria-hidden', 'true');
2112
2513
  }
2113
- if (copy.color) root.style.setProperty('--hj-progress-copy-color', copy.color);
2114
- var valueElement = document.createElement('div');
2115
- valueElement.className = 'hj-progress-value';
2116
- valueElement.setAttribute('aria-hidden', 'true');
2117
- var dragLabel = document.createElement('span');
2118
- dragLabel.className = 'hj-progress-drag-label';
2119
- dragLabel.setAttribute('aria-hidden', 'true');
2120
- dragLabel.textContent = copy.dragLabel;
2121
2514
  root.appendChild(canvas);
2122
- if (copyEnabled) root.appendChild(copyLayer);
2123
- root.appendChild(valueElement);
2124
- root.appendChild(dragLabel);
2515
+ root.appendChild(textLayer);
2516
+ root.appendChild(visualLayer);
2517
+ if (valueElement) root.appendChild(valueElement);
2125
2518
  container.appendChild(root);
2126
2519
  var emitter = createEmitter();
2127
2520
  var paused = merged.respectReducedMotion && prefersReducedMotion$1();
2521
+ var disposed = false;
2128
2522
  var applySize = function applySize() {
2129
2523
  root.style.width = parseSize(merged.width);
2130
2524
  root.style.height = parseSize(merged.height);
@@ -2133,6 +2527,9 @@ function createProgressCapsule(container) {
2133
2527
  var name = _Object$keys2[_i2];
2134
2528
  root.style.setProperty(name, vars[name]);
2135
2529
  }
2530
+ if (merged.textRatio !== undefined) {
2531
+ root.style.setProperty('--hj-text-width', "".concat(merged.textRatio, "%"));
2532
+ }
2136
2533
  };
2137
2534
  applySize();
2138
2535
  var controller = new ProgressCapsuleController({
@@ -2141,8 +2538,12 @@ function createProgressCapsule(container) {
2141
2538
  valueElement: valueElement,
2142
2539
  preset: preset,
2143
2540
  emitter: emitter,
2144
- options: merged,
2145
- copy: copy
2541
+ options: _objectSpread2(_objectSpread2({}, merged), {}, {
2542
+ draggable: effectiveDraggable,
2543
+ keyboard: effectiveKeyboard
2544
+ }),
2545
+ copy: copy,
2546
+ dirty: dirty
2146
2547
  });
2147
2548
  var overlay = null;
2148
2549
  if (merged.renderer !== 'canvas2d') {
@@ -2155,7 +2556,14 @@ function createProgressCapsule(container) {
2155
2556
  }
2156
2557
  });
2157
2558
  }
2158
- if (overlay) controller.webglActive = true;
2559
+ if (overlay) controller.webglActive = true;else if (merged.renderer !== 'canvas2d') {
2560
+ nextTick(function () {
2561
+ if (disposed) return;
2562
+ emitter.emit('error', {
2563
+ message: 'WebGL2 unavailable, using Canvas2D fallback'
2564
+ });
2565
+ });
2566
+ }
2159
2567
  var visibility = createVisibilityGuard(root);
2160
2568
  var flowTime = 0;
2161
2569
  var unsubscribe = subscribeScheduler(function (delta, now) {
@@ -2168,16 +2576,14 @@ function createProgressCapsule(container) {
2168
2576
  }, function () {
2169
2577
  return paused;
2170
2578
  });
2171
- emitter.emit('ready', {
2172
- preset: _objectSpread2({}, preset)
2579
+ nextTick(function () {
2580
+ if (disposed) return;
2581
+ emitter.emit('ready', {
2582
+ preset: _objectSpread2({}, preset)
2583
+ });
2173
2584
  });
2174
2585
  var syncDom = function syncDom() {
2175
- root.setAttribute('aria-label', interpolate(copy.progressAria, {
2176
- brand: copy.brandName,
2177
- code: preset.code,
2178
- name: preset.name
2179
- }));
2180
- renderCopy();
2586
+ updateAria();
2181
2587
  };
2182
2588
  return {
2183
2589
  element: root,
@@ -2199,8 +2605,11 @@ function createProgressCapsule(container) {
2199
2605
  },
2200
2606
  setPreset: function setPreset(ref) {
2201
2607
  var next = getPreset('progress', ref);
2608
+ dirty.preset = true;
2202
2609
  Object.assign(preset, next);
2203
- controller.preset = next;
2610
+ var nextColors = preset.colors.map(normalizeColor);
2611
+ if (nextColors.every(Boolean)) preset.colors = nextColors;
2612
+ controller.preset = preset;
2204
2613
  controller.profile = next.edgeStyle === 'tide' ? FLOW_PROFILES.tide : FLOW_PROFILES[next.id] || FLOW_PROFILES['visual-training'];
2205
2614
  controller.flowTime = stringSeed(next.id) * 31;
2206
2615
  controller.seed = stringSeed("".concat(next.id, "-reference")) * Math.PI * 2;
@@ -2210,7 +2619,7 @@ function createProgressCapsule(container) {
2210
2619
  overlay = attachProgressFlowOverlay({
2211
2620
  root: root,
2212
2621
  canvas: canvas,
2213
- preset: next,
2622
+ preset: preset,
2214
2623
  getProgress: function getProgress() {
2215
2624
  return controller.value;
2216
2625
  }
@@ -2219,13 +2628,14 @@ function createProgressCapsule(container) {
2219
2628
  controller.webglActive = Boolean(overlay);
2220
2629
  controller.resizeCanvas();
2221
2630
  emitter.emit('presetchange', {
2222
- preset: _objectSpread2({}, next)
2631
+ preset: _objectSpread2({}, preset)
2223
2632
  });
2224
2633
  return this;
2225
2634
  },
2226
2635
  setEdgeStyle: function setEdgeStyle(edgeStyle) {
2227
2636
  var value = String(edgeStyle || '').toLowerCase();
2228
2637
  if (value !== 'flow' && value !== 'tide') return this;
2638
+ dirty.edgeStyle = true;
2229
2639
  preset.edgeStyle = value;
2230
2640
  controller.profile = value === 'tide' ? FLOW_PROFILES.tide : FLOW_PROFILES[preset.id] || FLOW_PROFILES['visual-training'];
2231
2641
  if (overlay) {
@@ -2243,10 +2653,49 @@ function createProgressCapsule(container) {
2243
2653
  controller.resizeCanvas();
2244
2654
  return this;
2245
2655
  },
2656
+ setText: function setText(content) {
2657
+ fillContent(textLayer, content);
2658
+ return this;
2659
+ },
2660
+ setColorContent: function setColorContent(content) {
2661
+ fillContent(visualLayer, content);
2662
+ return this;
2663
+ },
2664
+ setTextRatio: function setTextRatio(ratio) {
2665
+ var value = Number(ratio);
2666
+ if (!Number.isFinite(value) || value < 0 || value > 100) return this;
2667
+ dirty.textRatio = true;
2668
+ merged.textRatio = value;
2669
+ root.style.setProperty('--hj-text-width', "".concat(value, "%"));
2670
+ return this;
2671
+ },
2672
+ setCssVars: function setCssVars(vars) {
2673
+ if (!vars || _typeof(vars) !== 'object') return this;
2674
+ dirty.cssVars = true;
2675
+ merged.cssVars = _objectSpread2(_objectSpread2({}, merged.cssVars || {}), vars);
2676
+ for (var _i3 = 0, _Object$keys3 = Object.keys(vars); _i3 < _Object$keys3.length; _i3++) {
2677
+ var name = _Object$keys3[_i3];
2678
+ root.style.setProperty(name, vars[name]);
2679
+ }
2680
+ return this;
2681
+ },
2682
+ setLabel: function setLabel(label) {
2683
+ customLabel = typeof label === 'string' && label ? label : null;
2684
+ updateAria();
2685
+ return this;
2686
+ },
2687
+ setValueSuffix: function setValueSuffix(suffix) {
2688
+ controller.setValueSuffix(suffix);
2689
+ return this;
2690
+ },
2246
2691
  setColors: function setColors(colors) {
2247
- if (!Array.isArray(colors) || colors.length !== 4) return this;
2248
- controller.setColors(colors);
2249
- if (overlay) overlay.setColors(colors);
2692
+ var next = Array.isArray(colors) ? colors.map(normalizeColor) : [];
2693
+ if (next.length !== 4 || next.some(function (color) {
2694
+ return !color;
2695
+ })) return this;
2696
+ dirty.colors = true;
2697
+ controller.setColors(next);
2698
+ if (overlay) overlay.setColors(next);
2250
2699
  controller.resizeCanvas();
2251
2700
  return this;
2252
2701
  },
@@ -2276,12 +2725,16 @@ function createProgressCapsule(container) {
2276
2725
  return this;
2277
2726
  },
2278
2727
  dispose: function dispose() {
2728
+ disposed = true;
2279
2729
  unsubscribe();
2280
2730
  visibility.dispose();
2281
2731
  if (overlay) overlay.dispose();
2282
2732
  controller.dispose();
2283
2733
  root.remove();
2284
- }
2734
+ },
2735
+ textRatio: merged.textRatio,
2736
+ cssVars: merged.cssVars,
2737
+ dirty: dirty
2285
2738
  };
2286
2739
  }
2287
2740
 
@@ -2317,6 +2770,10 @@ function createColorBackground(host) {
2317
2770
  opacity: 1,
2318
2771
  fallbackColor: true
2319
2772
  }, preset, options);
2773
+ var normalizedColors = merged.colors.map(normalizeColor);
2774
+ if (normalizedColors.every(Boolean)) preset.colors = normalizedColors;
2775
+ preset.seed = merged.seed;
2776
+ preset.speed = merged.speed;
2320
2777
  var computed = getComputedStyle(host);
2321
2778
  if (computed.position === 'static' || computed.position === '') {
2322
2779
  host.style.position = 'relative';
@@ -2337,6 +2794,16 @@ function createColorBackground(host) {
2337
2794
  host.appendChild(layer);
2338
2795
  var emitter = createEmitter();
2339
2796
  var paused = merged.respectReducedMotion && prefersReducedMotion();
2797
+ var disposed = false;
2798
+ var dirty = {
2799
+ preset: false,
2800
+ seed: false,
2801
+ speed: false,
2802
+ colors: false,
2803
+ opacity: false,
2804
+ fallbackColor: false,
2805
+ mouseColor: false
2806
+ };
2340
2807
  var renderer;
2341
2808
  var useWebgl = merged.renderer !== 'canvas2d';
2342
2809
  if (useWebgl) {
@@ -2348,8 +2815,11 @@ function createColorBackground(host) {
2348
2815
  });
2349
2816
  } catch (error) {
2350
2817
  renderer = new FallbackRenderer(canvas, merged);
2351
- emitter.emit('error', {
2352
- message: String(error && error.message ? error.message : error)
2818
+ nextTick(function () {
2819
+ if (disposed) return;
2820
+ emitter.emit('error', {
2821
+ message: String(error && error.message ? error.message : error)
2822
+ });
2353
2823
  });
2354
2824
  }
2355
2825
  } else {
@@ -2367,8 +2837,11 @@ function createColorBackground(host) {
2367
2837
  }, function () {
2368
2838
  return paused;
2369
2839
  });
2370
- emitter.emit('ready', {
2371
- preset: _objectSpread2({}, preset)
2840
+ nextTick(function () {
2841
+ if (disposed) return;
2842
+ emitter.emit('ready', {
2843
+ preset: _objectSpread2({}, preset)
2844
+ });
2372
2845
  });
2373
2846
  return {
2374
2847
  element: host,
@@ -2379,24 +2852,59 @@ function createColorBackground(host) {
2379
2852
  off: emitter.off,
2380
2853
  setPreset: function setPreset(ref) {
2381
2854
  var next = getPreset('capsule', ref);
2855
+ dirty.preset = true;
2382
2856
  Object.assign(preset, next);
2383
- renderer.setPreset(next);
2857
+ var nextColors = preset.colors.map(normalizeColor);
2858
+ if (nextColors.every(Boolean)) preset.colors = nextColors;
2859
+ renderer.setPreset(_objectSpread2({}, preset));
2384
2860
  renderer.resize();
2385
2861
  syncLayerVars();
2386
2862
  emitter.emit('presetchange', {
2387
- preset: _objectSpread2({}, next)
2863
+ preset: _objectSpread2({}, preset)
2388
2864
  });
2389
2865
  return this;
2390
2866
  },
2391
2867
  setColors: function setColors(colors) {
2392
- if (!Array.isArray(colors) || colors.length !== 4) return this;
2393
- preset.colors = colors;
2868
+ var next = Array.isArray(colors) ? colors.map(normalizeColor) : [];
2869
+ if (next.length !== 4 || next.some(function (color) {
2870
+ return !color;
2871
+ })) return this;
2872
+ dirty.colors = true;
2873
+ preset.colors = next;
2394
2874
  renderer.setPreset(_objectSpread2(_objectSpread2({}, preset), {}, {
2395
- colors: colors
2875
+ colors: next
2396
2876
  }));
2397
2877
  syncLayerVars();
2398
2878
  return this;
2399
2879
  },
2880
+ setSeed: function setSeed(seed) {
2881
+ var value = Number(seed);
2882
+ if (!Number.isFinite(value)) return this;
2883
+ dirty.seed = true;
2884
+ preset.seed = value;
2885
+ renderer.setPreset(_objectSpread2({}, preset));
2886
+ return this;
2887
+ },
2888
+ setSpeed: function setSpeed(speed) {
2889
+ var value = Number(speed);
2890
+ if (!Number.isFinite(value)) return this;
2891
+ dirty.speed = true;
2892
+ preset.speed = value;
2893
+ renderer.setPreset(_objectSpread2({}, preset));
2894
+ return this;
2895
+ },
2896
+ setFallbackColor: function setFallbackColor(value) {
2897
+ dirty.fallbackColor = true;
2898
+ merged.fallbackColor = value;
2899
+ syncLayerVars();
2900
+ return this;
2901
+ },
2902
+ setMouseColor: function setMouseColor(value) {
2903
+ dirty.mouseColor = true;
2904
+ merged.mouseColor = value !== false;
2905
+ if (typeof renderer.setMouseColor === 'function') renderer.setMouseColor(merged.mouseColor);
2906
+ return this;
2907
+ },
2400
2908
  setQuality: function setQuality(quality) {
2401
2909
  merged.quality = quality;
2402
2910
  if (typeof renderer.setDprCap === 'function') renderer.setDprCap(dprCapFor(quality));
@@ -2405,12 +2913,13 @@ function createColorBackground(host) {
2405
2913
  setOpacity: function setOpacity(value) {
2406
2914
  var opacity = Number(value);
2407
2915
  if (!Number.isFinite(opacity)) return this;
2916
+ dirty.opacity = true;
2408
2917
  merged.opacity = Math.min(1, Math.max(0, opacity));
2409
2918
  layer.style.setProperty('--dlc-color-opacity', String(merged.opacity));
2410
2919
  return this;
2411
2920
  },
2412
2921
  randomize: function randomize() {
2413
- renderer.randomize();
2922
+ this.setSeed(Math.random() * 100);
2414
2923
  return this;
2415
2924
  },
2416
2925
  pause: function pause() {
@@ -2422,12 +2931,17 @@ function createColorBackground(host) {
2422
2931
  return this;
2423
2932
  },
2424
2933
  dispose: function dispose() {
2934
+ disposed = true;
2425
2935
  unsubscribe();
2426
2936
  visibility.dispose();
2427
2937
  if (resizeObserver) resizeObserver.disconnect();else window.removeEventListener('resize', renderer.resize);
2428
2938
  renderer.dispose();
2429
2939
  layer.remove();
2430
- }
2940
+ },
2941
+ opacity: merged.opacity,
2942
+ fallbackColor: merged.fallbackColor,
2943
+ mouseColor: merged.mouseColor,
2944
+ dirty: dirty
2431
2945
  };
2432
2946
  }
2433
2947
 
@@ -2448,6 +2962,7 @@ exports.dprCapFor = dprCapFor;
2448
2962
  exports.getPreset = getPreset;
2449
2963
  exports.hexToRgb01 = hexToRgb01$1;
2450
2964
  exports.hexToRgba = hexToRgba;
2965
+ exports.normalizeColor = normalizeColor;
2451
2966
  exports.parseSize = parseSize;
2452
2967
  exports.pauseAll = pauseAll;
2453
2968
  exports.resumeAll = resumeAll;