@5even7/dlc-ui 0.2.0 → 0.2.1

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.umd.js CHANGED
@@ -152,6 +152,9 @@
152
152
  }
153
153
  return e;
154
154
  }
155
+ function _readOnlyError(r) {
156
+ throw new TypeError('"' + r + '" is read-only');
157
+ }
155
158
  function _slicedToArray(r, e) {
156
159
  return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
157
160
  }
@@ -436,7 +439,7 @@
436
439
  renderer: 'auto',
437
440
  respectReducedMotion: true,
438
441
  mouseColor: true,
439
- showCopy: true
442
+ textRatio: 39
440
443
  },
441
444
  progress: {
442
445
  width: 454,
@@ -445,43 +448,250 @@
445
448
  max: 100,
446
449
  draggable: true,
447
450
  keyboard: true,
451
+ disabled: false,
452
+ valueSuffix: '%',
448
453
  edgeStyle: 'flow',
449
454
  quality: 'auto',
450
455
  renderer: 'auto',
451
- showCopy: true,
456
+ textRatio: 54,
457
+ showValue: true,
452
458
  respectReducedMotion: true
453
459
  }
454
460
  };
455
461
 
456
- /**
457
- * All user-facing copy lives here so wording/brand changes never require a
458
- * global search. Templates use {brand} {code} {name} placeholders.
462
+ /**
463
+ * Internal accessibility labels and small UI text. Since the copy API was
464
+ * removed (slots own the text area), these are no longer user-overridable.
465
+ * Templates use {brand} {code} {name} placeholders.
459
466
  */
460
467
  var COPY = {
461
468
  brandName: '画境观屿',
462
- dragLabel: 'DRAG',
463
469
  valueSuffix: '%',
464
470
  progressAria: '{brand} {code} 加载进度',
465
471
  capsuleAria: '打开 {name} 沉浸预览'
466
472
  };
467
473
 
468
- function hexToRgb01$1(hex) {
469
- var normalized = hex.replace('#', '');
470
- var value = Number.parseInt(normalized, 16);
474
+ /**
475
+ * Color helpers. `normalizeColor` converts any supported CSS color
476
+ * (hex / named / rgb() / rgba()) into a 6-digit hex string, so renderers can
477
+ * keep working with #rrggbb only. rgba() alpha is intentionally dropped:
478
+ * the WebGL shader has no per-color alpha channel, and the component is
479
+ * opaque by design — use component-level `opacity` for transparency.
480
+ */
481
+
482
+ var NAMED_COLORS = {
483
+ aliceblue: '#f0f8ff',
484
+ antiquewhite: '#faebd7',
485
+ aqua: '#00ffff',
486
+ aquamarine: '#7fffd4',
487
+ azure: '#f0ffff',
488
+ beige: '#f5f5dc',
489
+ bisque: '#ffe4c4',
490
+ black: '#000000',
491
+ blanchedalmond: '#ffebcd',
492
+ blue: '#0000ff',
493
+ blueviolet: '#8a2be2',
494
+ brown: '#a52a2a',
495
+ burlywood: '#deb887',
496
+ cadetblue: '#5f9ea0',
497
+ chartreuse: '#7fff00',
498
+ chocolate: '#d2691e',
499
+ coral: '#ff7f50',
500
+ cornflowerblue: '#6495ed',
501
+ cornsilk: '#fff8dc',
502
+ crimson: '#dc143c',
503
+ cyan: '#00ffff',
504
+ darkblue: '#00008b',
505
+ darkcyan: '#008b8b',
506
+ darkgoldenrod: '#b8860b',
507
+ darkgray: '#a9a9a9',
508
+ darkgreen: '#006400',
509
+ darkgrey: '#a9a9a9',
510
+ darkkhaki: '#bdb76b',
511
+ darkmagenta: '#8b008b',
512
+ darkolivegreen: '#556b2f',
513
+ darkorange: '#ff8c00',
514
+ darkorchid: '#9932cc',
515
+ darkred: '#8b0000',
516
+ darksalmon: '#e9967a',
517
+ darkseagreen: '#8fbc8f',
518
+ darkslateblue: '#483d8b',
519
+ darkslategray: '#2f4f4f',
520
+ darkslategrey: '#2f4f4f',
521
+ darkturquoise: '#00ced1',
522
+ darkviolet: '#9400d3',
523
+ deeppink: '#ff1493',
524
+ deepskyblue: '#00bfff',
525
+ dimgray: '#696969',
526
+ dimgrey: '#696969',
527
+ dodgerblue: '#1e90ff',
528
+ firebrick: '#b22222',
529
+ floralwhite: '#fffaf0',
530
+ forestgreen: '#228b22',
531
+ fuchsia: '#ff00ff',
532
+ gainsboro: '#dcdcdc',
533
+ ghostwhite: '#f8f8ff',
534
+ gold: '#ffd700',
535
+ goldenrod: '#daa520',
536
+ gray: '#808080',
537
+ green: '#008000',
538
+ greenyellow: '#adff2f',
539
+ grey: '#808080',
540
+ honeydew: '#f0fff0',
541
+ hotpink: '#ff69b4',
542
+ indianred: '#cd5c5c',
543
+ indigo: '#4b0082',
544
+ ivory: '#fffff0',
545
+ khaki: '#f0e68c',
546
+ lavender: '#e6e6fa',
547
+ lavenderblush: '#fff0f5',
548
+ lawngreen: '#7cfc00',
549
+ lemonchiffon: '#fffacd',
550
+ lightblue: '#add8e6',
551
+ lightcoral: '#f08080',
552
+ lightcyan: '#e0ffff',
553
+ lightgoldenrodyellow: '#fafad2',
554
+ lightgray: '#d3d3d3',
555
+ lightgreen: '#90ee90',
556
+ lightgrey: '#d3d3d3',
557
+ lightpink: '#ffb6c1',
558
+ lightsalmon: '#ffa07a',
559
+ lightseagreen: '#20b2aa',
560
+ lightskyblue: '#87cefa',
561
+ lightslategray: '#778899',
562
+ lightslategrey: '#778899',
563
+ lightsteelblue: '#b0c4de',
564
+ lightyellow: '#ffffe0',
565
+ lime: '#00ff00',
566
+ limegreen: '#32cd32',
567
+ linen: '#faf0e6',
568
+ magenta: '#ff00ff',
569
+ maroon: '#800000',
570
+ mediumaquamarine: '#66cdaa',
571
+ mediumblue: '#0000cd',
572
+ mediumorchid: '#ba55d3',
573
+ mediumpurple: '#9370db',
574
+ mediumseagreen: '#3cb371',
575
+ mediumslateblue: '#7b68ee',
576
+ mediumspringgreen: '#00fa9a',
577
+ mediumturquoise: '#48d1cc',
578
+ mediumvioletred: '#c71585',
579
+ midnightblue: '#191970',
580
+ mintcream: '#f5fffa',
581
+ mistyrose: '#ffe4e1',
582
+ moccasin: '#ffe4b5',
583
+ navajowhite: '#ffdead',
584
+ navy: '#000080',
585
+ oldlace: '#fdf5e6',
586
+ olive: '#808000',
587
+ olivedrab: '#6b8e23',
588
+ orange: '#ffa500',
589
+ orangered: '#ff4500',
590
+ orchid: '#da70d6',
591
+ palegoldenrod: '#eee8aa',
592
+ palegreen: '#98fb98',
593
+ paleturquoise: '#afeeee',
594
+ palevioletred: '#db7093',
595
+ papayawhip: '#ffefd5',
596
+ peachpuff: '#ffdab9',
597
+ peru: '#cd853f',
598
+ pink: '#ffc0cb',
599
+ plum: '#dda0dd',
600
+ powderblue: '#b0e0e6',
601
+ purple: '#800080',
602
+ rebeccapurple: '#663399',
603
+ red: '#ff0000',
604
+ rosybrown: '#bc8f8f',
605
+ royalblue: '#4169e1',
606
+ saddlebrown: '#8b4513',
607
+ salmon: '#fa8072',
608
+ sandybrown: '#f4a460',
609
+ seagreen: '#2e8b57',
610
+ seashell: '#fff5ee',
611
+ sienna: '#a0522d',
612
+ silver: '#c0c0c0',
613
+ skyblue: '#87ceeb',
614
+ slateblue: '#6a5acd',
615
+ slategray: '#708090',
616
+ slategrey: '#708090',
617
+ snow: '#fffafa',
618
+ springgreen: '#00ff7f',
619
+ steelblue: '#4682b4',
620
+ tan: '#d2b48c',
621
+ teal: '#008080',
622
+ thistle: '#d8bfd8',
623
+ tomato: '#ff6347',
624
+ turquoise: '#40e0d0',
625
+ violet: '#ee82ee',
626
+ wheat: '#f5deb3',
627
+ white: '#ffffff',
628
+ whitesmoke: '#f5f5f5',
629
+ yellow: '#ffff00',
630
+ yellowgreen: '#9acd32'
631
+ };
632
+ function clampChannel(value) {
633
+ return Math.min(255, Math.max(0, Math.round(value)));
634
+ }
635
+ function normalizeRgbArgs(args) {
636
+ var parts = args.split(/[,\s/]+/).filter(Boolean);
637
+ if (parts.length < 3) return null;
638
+ var to255 = function to255(value) {
639
+ if (typeof value !== 'string' || !value) return null;
640
+ if (value.endsWith('%')) return clampChannel(parseFloat(value) / 100 * 255);
641
+ var number = Number(value);
642
+ return Number.isFinite(number) ? clampChannel(number) : null;
643
+ };
644
+ var red = to255(parts[0]);
645
+ var green = to255(parts[1]);
646
+ var blue = to255(parts[2]);
647
+ if (red === null || green === null || blue === null) return null;
648
+ return "#".concat([red, green, blue].map(function (n) {
649
+ return n.toString(16).padStart(2, '0');
650
+ }).join(''));
651
+ }
652
+
653
+ /**
654
+ * Convert any supported CSS color into `#rrggbb`. Returns null when the
655
+ * value cannot be parsed. Supports: #rgb / #rrggbb (case-insensitive),
656
+ * CSS named colors, rgb() / rgba() with comma or modern space syntax,
657
+ * including percentages. Alpha in rgba() is ignored.
658
+ */
659
+ function normalizeColor(value) {
660
+ if (typeof value !== 'string') return null;
661
+ var input = value.trim();
662
+ if (!input) return null;
663
+ var lower = input.toLowerCase();
664
+ if (NAMED_COLORS[lower]) return NAMED_COLORS[lower];
665
+ if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(input)) {
666
+ var hex = input.slice(1).toLowerCase();
667
+ if (hex.length === 3) return "#".concat(hex.split('').map(function (c) {
668
+ return c + c;
669
+ }).join(''));
670
+ return "#".concat(hex);
671
+ }
672
+ var match = input.match(/^rgba?\((.*)\)$/i);
673
+ if (match) return normalizeRgbArgs(match[1]);
674
+ return null;
675
+ }
676
+ function hexToRgb01$1(color) {
677
+ var hex = normalizeColor(color);
678
+ if (!hex) return [0, 0, 0];
679
+ var value = Number.parseInt(hex.slice(1), 16);
471
680
  return [(value >> 16 & 255) / 255, (value >> 8 & 255) / 255, (value & 255) / 255];
472
681
  }
473
- function hexToRgba(hex) {
682
+ function hexToRgba(color) {
474
683
  var alpha = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
475
- var normalized = hex.replace('#', '');
476
- var value = Number.parseInt(normalized, 16);
684
+ var hex = normalizeColor(color);
685
+ if (!hex) return 'rgba(0, 0, 0, 0)';
686
+ var value = Number.parseInt(hex.slice(1), 16);
477
687
  var red = value >> 16 & 255;
478
688
  var green = value >> 8 & 255;
479
689
  var blue = value & 255;
480
690
  return "rgba(".concat(red, ", ").concat(green, ", ").concat(blue, ", ").concat(alpha, ")");
481
691
  }
482
692
  function validateColors(colors) {
483
- return Array.isArray(colors) && colors.length === 4 && colors.every(function (c) {
484
- return typeof c === 'string' && /^#[0-9a-f]{6}$/i.test(c);
693
+ return Array.isArray(colors) && colors.length === 4 && colors.every(function (color) {
694
+ return normalizeColor(color) !== null;
485
695
  });
486
696
  }
487
697
 
@@ -559,6 +769,26 @@
559
769
  this.options.dprCap = cap;
560
770
  this.resize();
561
771
  }
772
+ }, {
773
+ key: "setMouseColor",
774
+ value: function setMouseColor(enabled) {
775
+ this.options.mouseColor = enabled !== false;
776
+ if (this.options.mouseColor) {
777
+ if (this.onPointerMove) return this;
778
+ _assertClassBrand(_CosmicRenderer_brand, this, _bindEvents).call(this);
779
+ return this;
780
+ }
781
+ if (this.onPointerMove) {
782
+ var target = this.eventTarget;
783
+ target.removeEventListener('pointermove', this.onPointerMove);
784
+ target.removeEventListener('pointerdown', this.onPointerMove);
785
+ target.removeEventListener('pointerleave', this.onPointerLeave);
786
+ this.onPointerMove = null;
787
+ this.onPointerLeave = null;
788
+ }
789
+ this.motionTarget = 0;
790
+ return this;
791
+ }
562
792
  }, {
563
793
  key: "randomize",
564
794
  value: function randomize() {
@@ -707,6 +937,8 @@
707
937
  var _this$canvas = this.canvas,
708
938
  width = _this$canvas.width,
709
939
  height = _this$canvas.height;
940
+ var t = time * (this.preset.speed || 1);
941
+ var phase = (this.preset.seed || 0) * 0.7;
710
942
  var gradient = ctx.createLinearGradient(0, 0, width, height);
711
943
  gradient.addColorStop(0, this.preset.colors[0]);
712
944
  gradient.addColorStop(0.38, this.preset.colors[1]);
@@ -716,8 +948,8 @@
716
948
  ctx.fillRect(0, 0, width, height);
717
949
  ctx.globalCompositeOperation = 'screen';
718
950
  for (var index = 0; index < 6; index += 1) {
719
- var x = (0.5 + 0.45 * Math.sin(time * 0.32 + index * 1.7)) * width;
720
- var y = (0.5 + 0.4 * Math.cos(time * 0.25 + index)) * height;
951
+ var x = (0.5 + 0.45 * Math.sin(t * 0.32 + index * 1.7 + phase)) * width;
952
+ var y = (0.5 + 0.4 * Math.cos(t * 0.25 + index + phase)) * height;
721
953
  var radius = Math.max(width, height) * (0.08 + index % 3 * 0.04);
722
954
  var glow = ctx.createRadialGradient(x, y, 0, x, y, radius);
723
955
  glow.addColorStop(0, rgb(this.preset.colors[(index + 1) % 4], 0.24));
@@ -741,7 +973,12 @@
741
973
  }
742
974
  }, {
743
975
  key: "randomize",
744
- value: function randomize() {}
976
+ value: function randomize() {
977
+ if (this.preset) this.preset.seed = Math.random() * 100;
978
+ }
979
+ }, {
980
+ key: "setMouseColor",
981
+ value: function setMouseColor() {}
745
982
  }, {
746
983
  key: "dispose",
747
984
  value: function dispose() {}
@@ -859,16 +1096,34 @@
859
1096
  };
860
1097
  }
861
1098
 
1099
+ /**
1100
+ * Run a callback asynchronously, right after the current task and before the
1101
+ * next paint. Used for mount-time events (ready / error) so listeners that
1102
+ * are attached after create() still receive them. Falls back for legacy
1103
+ * browsers that lack queueMicrotask / Promise.
1104
+ */
1105
+ function nextTick(fn) {
1106
+ if (typeof queueMicrotask === 'function') {
1107
+ queueMicrotask(fn);
1108
+ } else if (typeof Promise !== 'undefined' && typeof Promise.resolve === 'function') {
1109
+ Promise.resolve().then(fn);
1110
+ } else {
1111
+ setTimeout(fn, 0);
1112
+ }
1113
+ }
1114
+
862
1115
  function prefersReducedMotion$2() {
863
1116
  return typeof matchMedia !== 'undefined' && matchMedia('(prefers-reduced-motion: reduce)').matches;
864
1117
  }
865
1118
 
866
1119
  /**
867
- * Mount a cosmic (nebula) capsule into `container`.
868
- *
869
- * Options: preset (id/code/name or object), width, height (number=px or
870
- * string with px/%/vw/vh/em/rem), colors, seed, speed, quality,
871
- * respectReducedMotion, copy, cssVars.
1120
+ * Mount a cosmic (nebula) capsule into `container`.
1121
+ *
1122
+ * Options: preset (literary name), width, height (number=px or string with
1123
+ * px/%/vw/vh/em/rem), colors, seed, speed, textRatio (0-100, text region
1124
+ * width in percent), text (HTML string or DOM nodes for the text slot),
1125
+ * colorContent (HTML string or DOM nodes for the color slot), quality,
1126
+ * renderer, respectReducedMotion, mouseColor, cssVars.
872
1127
  */
873
1128
  function createCapsule(container) {
874
1129
  var _options$preset;
@@ -878,51 +1133,71 @@
878
1133
  }
879
1134
  var preset = _objectSpread2({}, getPreset('capsule', (_options$preset = options.preset) !== null && _options$preset !== void 0 ? _options$preset : '初光'));
880
1135
  var merged = normalizeOptions(DEFAULTS.capsule, preset, options);
881
- var copy = _objectSpread2(_objectSpread2({}, COPY), merged.copy || {});
1136
+ var normalizedColors = merged.colors.map(normalizeColor);
1137
+ if (normalizedColors.every(Boolean)) preset.colors = normalizedColors;
1138
+ preset.seed = merged.seed;
1139
+ preset.speed = merged.speed;
1140
+ var customLabel = typeof options.label === 'string' && options.label ? options.label : null;
882
1141
  var root = document.createElement('div');
883
1142
  root.className = 'hj-capsule-root hj-capsule-cosmic';
884
1143
  root.dataset.group = preset.group;
885
1144
  root.dataset.mode = 'nebula';
886
1145
  root.dataset.theme = preset.theme || 'light';
887
- root.setAttribute('aria-label', copy.capsuleAria.replace('{name}', preset.name));
888
- var copyEnabled = copy.enabled !== false && merged.showCopy !== false;
889
- var copyText = function copyText(key, fallback) {
890
- var value = copy[key];
891
- return value === undefined ? fallback : value;
892
- };
893
- function buildCopyHtml() {
894
- var codeText = copyText('code', preset.code);
895
- var nameText = copyText('name', preset.name);
896
- var subtitleText = copyText('subtitle', preset.subtitle);
897
- var stateText = copyText('state', 'LIVE COSMIC STUDY');
898
- var html = '';
899
- if (codeText) html += "<span class=\"hj-capsule-code\">".concat(codeText, "</span>");
900
- if (nameText) html += "<span class=\"hj-capsule-name\">".concat(nameText, "</span>");
901
- if (subtitleText) html += "<span class=\"hj-capsule-brand\">".concat(subtitleText, "</span>");
902
- if (stateText) html += "<span class=\"hj-capsule-state\">".concat(stateText, "</span>");
903
- return html;
904
- }
905
- var copyLayer = document.createElement('div');
906
- copyLayer.className = 'hj-capsule-copy';
907
- var renderCopy = function renderCopy() {
908
- if (copyEnabled) copyLayer.innerHTML = buildCopyHtml();
1146
+ root.setAttribute('role', 'img');
1147
+ var updateAria = function updateAria() {
1148
+ root.setAttribute('aria-label', customLabel || COPY.capsuleAria.replace('{name}', preset.name));
909
1149
  };
910
- renderCopy();
911
- var isHexColor = function isHexColor(value) {
912
- return typeof value === 'string' && /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(value);
1150
+ updateAria();
1151
+
1152
+ // Slot containers are always present but transparent by default: without
1153
+ // content they are invisible, so "no slot" behaves like the old
1154
+ // showCopy=false. They only provide geometry — no typography, padding or
1155
+ // background is imposed on user content (see docs: 插槽 CSS 约定).
1156
+ var fillContent = function fillContent(layer, content) {
1157
+ layer.innerHTML = '';
1158
+ if (content == null) return;
1159
+ if (typeof content === 'string') {
1160
+ layer.innerHTML = content;
1161
+ return;
1162
+ }
1163
+ var nodes = Array.isArray(content) ? content : [content];
1164
+ var _iterator = _createForOfIteratorHelper(nodes),
1165
+ _step;
1166
+ try {
1167
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
1168
+ var node = _step.value;
1169
+ if (node && typeof node.nodeType === 'number') layer.appendChild(node);
1170
+ }
1171
+ } catch (err) {
1172
+ _iterator.e(err);
1173
+ } finally {
1174
+ _iterator.f();
1175
+ }
913
1176
  };
914
- if (copy.background) {
915
- 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);
916
- }
917
- if (copy.color) root.style.setProperty('--hj-copy-color', copy.color);
1177
+ var textLayer = document.createElement('div');
1178
+ textLayer.className = 'hj-capsule-text hj-capsule-copy';
1179
+ var visualLayer = document.createElement('div');
1180
+ visualLayer.className = 'hj-capsule-visual';
1181
+ fillContent(textLayer, options.text);
1182
+ fillContent(visualLayer, options.colorContent);
918
1183
  var canvas = document.createElement('canvas');
919
1184
  canvas.className = 'hj-capsule-canvas';
920
1185
  canvas.setAttribute('aria-hidden', 'true');
921
- if (copyEnabled) root.appendChild(copyLayer);
1186
+ root.appendChild(textLayer);
1187
+ root.appendChild(visualLayer);
922
1188
  root.appendChild(canvas);
923
1189
  container.appendChild(root);
924
1190
  var emitter = createEmitter();
925
1191
  var paused = merged.respectReducedMotion && prefersReducedMotion$2();
1192
+ var disposed = false;
1193
+ var dirty = {
1194
+ preset: false,
1195
+ seed: false,
1196
+ speed: false,
1197
+ colors: false,
1198
+ textRatio: false,
1199
+ cssVars: false
1200
+ };
926
1201
  var renderer;
927
1202
  var useWebgl = merged.renderer !== 'canvas2d';
928
1203
  if (useWebgl) {
@@ -932,8 +1207,11 @@
932
1207
  });
933
1208
  } catch (error) {
934
1209
  renderer = new FallbackRenderer(canvas, merged);
935
- emitter.emit('error', {
936
- message: String(error && error.message ? error.message : error)
1210
+ nextTick(function () {
1211
+ if (disposed) return;
1212
+ emitter.emit('error', {
1213
+ message: String(error && error.message ? error.message : error)
1214
+ });
937
1215
  });
938
1216
  }
939
1217
  } else {
@@ -947,6 +1225,9 @@
947
1225
  var name = _Object$keys[_i];
948
1226
  root.style.setProperty(name, vars[name]);
949
1227
  }
1228
+ if (merged.textRatio !== undefined) {
1229
+ root.style.setProperty('--hj-text-width', "".concat(merged.textRatio, "%"));
1230
+ }
950
1231
  renderer.resize();
951
1232
  };
952
1233
  applySize();
@@ -996,13 +1277,16 @@
996
1277
  }, function () {
997
1278
  return paused;
998
1279
  });
999
- emitter.emit('ready', {
1000
- preset: _objectSpread2({}, preset)
1280
+ nextTick(function () {
1281
+ if (disposed) return;
1282
+ emitter.emit('ready', {
1283
+ preset: _objectSpread2({}, preset)
1284
+ });
1001
1285
  });
1002
- var syncCopy = function syncCopy() {
1286
+ var syncTheme = function syncTheme() {
1003
1287
  root.dataset.mode = 'nebula';
1004
1288
  root.dataset.theme = preset.theme || 'light';
1005
- renderCopy();
1289
+ updateAria();
1006
1290
  };
1007
1291
  return {
1008
1292
  element: root,
@@ -1012,9 +1296,12 @@
1012
1296
  off: emitter.off,
1013
1297
  setPreset: function setPreset(ref) {
1014
1298
  var next = getPreset('capsule', ref);
1299
+ dirty.preset = true;
1015
1300
  Object.assign(preset, next);
1016
- renderer.setPreset(next);
1017
- syncCopy();
1301
+ var nextColors = preset.colors.map(normalizeColor);
1302
+ if (nextColors.every(Boolean)) preset.colors = nextColors;
1303
+ renderer.setPreset(_objectSpread2({}, preset));
1304
+ syncTheme();
1018
1305
  renderer.resize();
1019
1306
  emitter.emit('presetchange', {
1020
1307
  preset: _objectSpread2({}, next)
@@ -1022,16 +1309,21 @@
1022
1309
  return this;
1023
1310
  },
1024
1311
  setColors: function setColors(colors) {
1025
- if (!Array.isArray(colors) || colors.length !== 4) return this;
1026
- preset.colors = colors;
1312
+ var next = Array.isArray(colors) ? colors.map(normalizeColor) : [];
1313
+ if (next.length !== 4 || next.some(function (color) {
1314
+ return !color;
1315
+ })) return this;
1316
+ dirty.colors = true;
1317
+ preset.colors = next;
1027
1318
  renderer.setPreset(_objectSpread2(_objectSpread2({}, preset), {}, {
1028
- colors: colors
1319
+ colors: next
1029
1320
  }));
1030
1321
  return this;
1031
1322
  },
1032
1323
  setSeed: function setSeed(seed) {
1033
1324
  var value = Number(seed);
1034
1325
  if (!Number.isFinite(value)) return this;
1326
+ dirty.seed = true;
1035
1327
  preset.seed = value;
1036
1328
  renderer.setPreset(_objectSpread2({}, preset));
1037
1329
  return this;
@@ -1039,10 +1331,42 @@
1039
1331
  setSpeed: function setSpeed(speed) {
1040
1332
  var value = Number(speed);
1041
1333
  if (!Number.isFinite(value)) return this;
1334
+ dirty.speed = true;
1042
1335
  preset.speed = value;
1043
1336
  renderer.setPreset(_objectSpread2({}, preset));
1044
1337
  return this;
1045
1338
  },
1339
+ setText: function setText(content) {
1340
+ fillContent(textLayer, content);
1341
+ return this;
1342
+ },
1343
+ setColorContent: function setColorContent(content) {
1344
+ fillContent(visualLayer, content);
1345
+ return this;
1346
+ },
1347
+ setTextRatio: function setTextRatio(ratio) {
1348
+ var value = Number(ratio);
1349
+ if (!Number.isFinite(value) || value < 0 || value > 100) return this;
1350
+ dirty.textRatio = true;
1351
+ merged.textRatio = value;
1352
+ root.style.setProperty('--hj-text-width', "".concat(value, "%"));
1353
+ return this;
1354
+ },
1355
+ setCssVars: function setCssVars(vars) {
1356
+ if (!vars || _typeof(vars) !== 'object') return this;
1357
+ dirty.cssVars = true;
1358
+ merged.cssVars = _objectSpread2(_objectSpread2({}, merged.cssVars || {}), vars);
1359
+ for (var _i2 = 0, _Object$keys2 = Object.keys(vars); _i2 < _Object$keys2.length; _i2++) {
1360
+ var name = _Object$keys2[_i2];
1361
+ root.style.setProperty(name, vars[name]);
1362
+ }
1363
+ return this;
1364
+ },
1365
+ setLabel: function setLabel(label) {
1366
+ customLabel = typeof label === 'string' && label ? label : null;
1367
+ updateAria();
1368
+ return this;
1369
+ },
1046
1370
  setSize: function setSize(width, height) {
1047
1371
  if (width !== undefined) merged.width = width;
1048
1372
  if (height !== undefined) merged.height = height;
@@ -1050,7 +1374,7 @@
1050
1374
  return this;
1051
1375
  },
1052
1376
  randomize: function randomize() {
1053
- renderer.randomize();
1377
+ this.setSeed(Math.random() * 100);
1054
1378
  return this;
1055
1379
  },
1056
1380
  pause: function pause() {
@@ -1067,12 +1391,16 @@
1067
1391
  return this;
1068
1392
  },
1069
1393
  dispose: function dispose() {
1394
+ disposed = true;
1070
1395
  unsubscribe();
1071
1396
  visibility.dispose();
1072
1397
  if (resizeObserver) resizeObserver.disconnect();else window.removeEventListener('resize', renderer.resize);
1073
1398
  renderer.dispose();
1074
1399
  root.remove();
1075
- }
1400
+ },
1401
+ textRatio: merged.textRatio,
1402
+ cssVars: merged.cssVars,
1403
+ dirty: dirty
1076
1404
  };
1077
1405
  }
1078
1406
 
@@ -1576,7 +1904,8 @@
1576
1904
  };
1577
1905
  var ProgressCapsuleController = /*#__PURE__*/function () {
1578
1906
  function ProgressCapsuleController(_ref) {
1579
- var _options$min,
1907
+ var _options$valueSuffix,
1908
+ _options$min,
1580
1909
  _options$max,
1581
1910
  _options$value,
1582
1911
  _this = this;
@@ -1586,7 +1915,8 @@
1586
1915
  preset = _ref.preset,
1587
1916
  emitter = _ref.emitter,
1588
1917
  options = _ref.options,
1589
- copy = _ref.copy;
1918
+ copy = _ref.copy,
1919
+ dirty = _ref.dirty;
1590
1920
  _classCallCheck(this, ProgressCapsuleController);
1591
1921
  this.root = root;
1592
1922
  this.canvas = canvas;
@@ -1595,6 +1925,8 @@
1595
1925
  this.emitter = emitter;
1596
1926
  this.options = options;
1597
1927
  this.copy = copy;
1928
+ this.dirty = dirty;
1929
+ this.valueSuffix = (_options$valueSuffix = options.valueSuffix) !== null && _options$valueSuffix !== void 0 ? _options$valueSuffix : copy.valueSuffix;
1598
1930
  this.profile = preset.edgeStyle === 'tide' ? FLOW_PROFILES.tide : FLOW_PROFILES[preset.id] || FLOW_PROFILES['visual-training'];
1599
1931
  this.min = (_options$min = options.min) !== null && _options$min !== void 0 ? _options$min : 0;
1600
1932
  this.max = (_options$max = options.max) !== null && _options$max !== void 0 ? _options$max : 100;
@@ -1634,12 +1966,24 @@
1634
1966
  var rounded = Math.round(this.value);
1635
1967
  this.root.style.setProperty('--progress', this.value.toFixed(2));
1636
1968
  this.root.setAttribute('aria-valuenow', String(rounded));
1637
- this.root.setAttribute('aria-valuetext', "".concat(rounded).concat(this.copy.valueSuffix));
1638
- this.valueElement.textContent = "".concat(rounded).concat(this.copy.valueSuffix);
1639
- if (!this.suppressEvents) this.emitter.emit('change', {
1640
- value: this.value,
1641
- source: source
1642
- });
1969
+ this.root.setAttribute('aria-valuetext', "".concat(rounded).concat(this.valueSuffix));
1970
+ if (this.valueElement) this.valueElement.textContent = "".concat(rounded).concat(this.valueSuffix);
1971
+ if (!this.suppressEvents) {
1972
+ if (this.dirty) this.dirty.value = true;
1973
+ this.emitter.emit('change', {
1974
+ value: this.value,
1975
+ source: source
1976
+ });
1977
+ }
1978
+ }
1979
+ }, {
1980
+ key: "setValueSuffix",
1981
+ value: function setValueSuffix(suffix) {
1982
+ this.valueSuffix = String(suffix == null ? '' : suffix);
1983
+ var rounded = Math.round(this.value);
1984
+ if (this.valueElement) this.valueElement.textContent = "".concat(rounded).concat(this.valueSuffix);
1985
+ this.root.setAttribute('aria-valuetext', "".concat(rounded).concat(this.valueSuffix));
1986
+ return this;
1643
1987
  }
1644
1988
  }, {
1645
1989
  key: "resizeCanvas",
@@ -1814,7 +2158,8 @@
1814
2158
  var width = this.width;
1815
2159
  var height = this.height;
1816
2160
  if (!ctx || width <= 0 || height <= 0) return;
1817
- var shoreline = width * (this.value / 100);
2161
+ var range = Math.max(this.max - this.min, 1);
2162
+ var shoreline = width * Math.min(Math.max((this.value - this.min) / range, 0), 1);
1818
2163
  var time = this.flowTime;
1819
2164
  var _this$preset$colors = _slicedToArray(this.preset.colors, 4),
1820
2165
  dark = _this$preset$colors[0],
@@ -1929,7 +2274,7 @@
1929
2274
  value: function updateFromPointer(event) {
1930
2275
  var bounds = this.root.getBoundingClientRect();
1931
2276
  var ratio = bounds.width > 0 ? (event.clientX - bounds.left) / bounds.width : 0;
1932
- this.setProgress(ratio * 100, 'drag');
2277
+ this.setProgress(this.min + ratio * (this.max - this.min), 'drag');
1933
2278
  }
1934
2279
  }, {
1935
2280
  key: "beginDrag",
@@ -1937,6 +2282,8 @@
1937
2282
  if (event.button !== undefined && event.button !== 0) return;
1938
2283
  event.preventDefault();
1939
2284
  this.dragging = true;
2285
+ this.pendingPointer = null;
2286
+ this._dragRaf = 0;
1940
2287
  this.root.classList.add('is-dragging');
1941
2288
  try {
1942
2289
  var _this$root$setPointer, _this$root;
@@ -1950,15 +2297,31 @@
1950
2297
  }, {
1951
2298
  key: "moveDrag",
1952
2299
  value: function moveDrag(event) {
2300
+ var _this2 = this;
1953
2301
  if (!this.dragging) return;
1954
2302
  event.preventDefault();
1955
- this.updateFromPointer(event);
2303
+ // rAF 合并:一帧最多一次 setProgress/change,视觉仍每帧更新。
2304
+ this.pendingPointer = event;
2305
+ if (this._dragRaf) return;
2306
+ this._dragRaf = requestAnimationFrame(function () {
2307
+ _this2._dragRaf = 0;
2308
+ var pending = _this2.pendingPointer;
2309
+ _this2.pendingPointer = null;
2310
+ if (pending) _this2.updateFromPointer(pending);
2311
+ });
1956
2312
  }
1957
2313
  }, {
1958
2314
  key: "endDrag",
1959
2315
  value: function endDrag(event) {
1960
2316
  if (!this.dragging) return;
1961
2317
  this.dragging = false;
2318
+ if (this._dragRaf) {
2319
+ cancelAnimationFrame(this._dragRaf);
2320
+ this._dragRaf = 0;
2321
+ }
2322
+ var pending = this.pendingPointer;
2323
+ this.pendingPointer = null;
2324
+ if (pending) this.updateFromPointer(pending);
1962
2325
  this.root.classList.remove('is-dragging');
1963
2326
  try {
1964
2327
  var _this$root$hasPointer, _this$root2;
@@ -1973,19 +2336,19 @@
1973
2336
  }, {
1974
2337
  key: "bindEvents",
1975
2338
  value: function bindEvents() {
1976
- var _this2 = this;
2339
+ var _this3 = this;
1977
2340
  if (this.options.draggable !== false) {
1978
2341
  this.handlers.pointerdown = function (event) {
1979
- return _this2.beginDrag(event);
2342
+ return _this3.beginDrag(event);
1980
2343
  };
1981
2344
  this.handlers.pointermove = function (event) {
1982
- return _this2.moveDrag(event);
2345
+ return _this3.moveDrag(event);
1983
2346
  };
1984
2347
  this.handlers.pointerup = function (event) {
1985
- return _this2.endDrag(event);
2348
+ return _this3.endDrag(event);
1986
2349
  };
1987
2350
  this.handlers.pointercancel = function (event) {
1988
- return _this2.endDrag(event);
2351
+ return _this3.endDrag(event);
1989
2352
  };
1990
2353
  this.root.addEventListener('pointerdown', this.handlers.pointerdown);
1991
2354
  this.root.addEventListener('pointermove', this.handlers.pointermove);
@@ -1994,23 +2357,24 @@
1994
2357
  }
1995
2358
  if (this.options.keyboard !== false) {
1996
2359
  this.handlers.keydown = function (event) {
2360
+ var step = (_this3.max - _this3.min) * 0.02;
1997
2361
  var actions = {
1998
- ArrowLeft: -2,
1999
- ArrowDown: -2,
2000
- ArrowRight: 2,
2001
- ArrowUp: 2,
2002
- PageDown: -10,
2003
- PageUp: 10
2362
+ ArrowLeft: -step,
2363
+ ArrowDown: -step,
2364
+ ArrowRight: step,
2365
+ ArrowUp: step,
2366
+ PageDown: -step * 5,
2367
+ PageUp: step * 5
2004
2368
  };
2005
2369
  if (event.key === 'Home') {
2006
2370
  event.preventDefault();
2007
- _this2.setProgress(_this2.min, 'keyboard');
2371
+ _this3.setProgress(_this3.min, 'keyboard');
2008
2372
  } else if (event.key === 'End') {
2009
2373
  event.preventDefault();
2010
- _this2.setProgress(_this2.max, 'keyboard');
2374
+ _this3.setProgress(_this3.max, 'keyboard');
2011
2375
  } else if (actions[event.key]) {
2012
2376
  event.preventDefault();
2013
- _this2.setProgress(_this2.value + actions[event.key], 'keyboard');
2377
+ _this3.setProgress(_this3.value + actions[event.key], 'keyboard');
2014
2378
  } else {
2015
2379
  return;
2016
2380
  }
@@ -2047,6 +2411,7 @@
2047
2411
  }, {
2048
2412
  key: "dispose",
2049
2413
  value: function dispose() {
2414
+ if (this._dragRaf) cancelAnimationFrame(this._dragRaf);
2050
2415
  if (this.resizeObserver) this.resizeObserver.disconnect();else window.removeEventListener('resize', this.resizeCanvas);
2051
2416
  for (var _i = 0, _Object$keys = Object.keys(this.handlers); _i < _Object$keys.length; _i++) {
2052
2417
  var name = _Object$keys[_i];
@@ -2059,11 +2424,14 @@
2059
2424
  }();
2060
2425
 
2061
2426
  /**
2062
- * Mount a fluid progress capsule into `container`.
2063
- *
2064
- * Options: preset (id/code/name or object), width, height, value, min, max,
2065
- * draggable, keyboard, colors, edgeStyle, quality,
2066
- * respectReducedMotion, copy, cssVars.
2427
+ * Mount a fluid progress capsule into `container`.
2428
+ *
2429
+ * Options: preset (id/code/name or object), width, height, value, min, max,
2430
+ * draggable, keyboard, colors, edgeStyle, textRatio (0-100, text region
2431
+ * width in percent), text (HTML string or DOM nodes for the text slot),
2432
+ * colorContent (HTML string or DOM nodes for the color slot),
2433
+ * showValue (show/hide the right-side percentage), quality,
2434
+ * respectReducedMotion, cssVars.
2067
2435
  */
2068
2436
  function createProgressCapsule(container) {
2069
2437
  var _options$preset, _merged$min, _merged$max;
@@ -2073,62 +2441,91 @@
2073
2441
  }
2074
2442
  var preset = _objectSpread2({}, getPreset('progress', (_options$preset = options.preset) !== null && _options$preset !== void 0 ? _options$preset : '星火'));
2075
2443
  var merged = normalizeOptions(DEFAULTS.progress, preset, options);
2076
- var copy = _objectSpread2(_objectSpread2({}, COPY), merged.copy || {});
2444
+ var normalizedColors = merged.colors.map(normalizeColor);
2445
+ if (normalizedColors.every(Boolean)) preset.colors = normalizedColors;
2446
+ preset.edgeStyle = merged.edgeStyle;
2447
+ var copy = COPY;
2448
+ var customLabel = typeof options.label === 'string' && options.label ? options.label : null;
2449
+ // Vue 的裸布尔属性(<ProgressCapsule disabled />)会传成空字符串,
2450
+ // 这里统一按 true 处理。
2451
+ var disabled = merged.disabled === true || merged.disabled === '' || merged.disabled === 'true';
2452
+ var effectiveDraggable = disabled ? false : merged.draggable !== false;
2453
+ var effectiveKeyboard = disabled ? false : merged.keyboard !== false;
2454
+ var dirty = {
2455
+ preset: false,
2456
+ colors: false,
2457
+ textRatio: false,
2458
+ cssVars: false,
2459
+ edgeStyle: false,
2460
+ value: false
2461
+ };
2077
2462
  var root = document.createElement('div');
2078
2463
  root.className = 'hj-capsule-root hj-progress-root';
2079
2464
  root.setAttribute('role', 'slider');
2080
2465
  root.setAttribute('tabindex', '0');
2081
- root.setAttribute('data-draggable', String(merged.draggable !== false));
2466
+ root.setAttribute('data-draggable', String(effectiveDraggable));
2467
+ if (disabled) {
2468
+ root.setAttribute('aria-disabled', 'true');
2469
+ root.classList.add('is-disabled');
2470
+ }
2082
2471
  root.setAttribute('aria-valuemin', String((_merged$min = merged.min) !== null && _merged$min !== void 0 ? _merged$min : 0));
2083
2472
  root.setAttribute('aria-valuemax', String((_merged$max = merged.max) !== null && _merged$max !== void 0 ? _merged$max : 100));
2084
2473
  root.setAttribute('aria-valuenow', String(preset.initialProgress));
2085
- root.setAttribute('aria-label', interpolate(copy.progressAria, {
2086
- brand: copy.brandName,
2087
- code: preset.code,
2088
- name: preset.name
2089
- }));
2090
- if (merged.keyboard === false) root.setAttribute('tabindex', '-1');
2474
+ var updateAria = function updateAria() {
2475
+ root.setAttribute('aria-label', customLabel || interpolate(copy.progressAria, {
2476
+ brand: copy.brandName,
2477
+ code: preset.code,
2478
+ name: preset.name
2479
+ }));
2480
+ };
2481
+ updateAria();
2482
+ if (!effectiveKeyboard) root.setAttribute('tabindex', '-1');
2091
2483
  var canvas = document.createElement('canvas');
2092
2484
  canvas.className = 'hj-progress-canvas';
2093
2485
  canvas.setAttribute('aria-hidden', 'true');
2094
- var copyEnabled = copy.enabled !== false && merged.showCopy !== false;
2095
- var copyText = function copyText(key, fallback) {
2096
- var value = copy[key];
2097
- return value === undefined ? fallback : value;
2098
- };
2099
- function buildCopyHtml() {
2100
- var html = '';
2101
- var brandText = copy.brandName || '';
2102
- var subtitleText = copyText('subtitle', preset.subtitle);
2103
- if (brandText) html += "<span class=\"hj-progress-name\">".concat(brandText, "</span>");
2104
- if (subtitleText) html += "<span class=\"hj-progress-subtitle\">".concat(subtitleText, "</span>");
2105
- return html;
2106
- }
2107
- var copyLayer = document.createElement('div');
2108
- copyLayer.className = 'hj-progress-copy';
2109
- var renderCopy = function renderCopy() {
2110
- if (copyEnabled) copyLayer.innerHTML = buildCopyHtml();
2486
+
2487
+ // Slot containers are always present but transparent by default; they only
2488
+ // provide geometry, never typography/padding/background (docs: 插槽 CSS 约定).
2489
+ var fillContent = function fillContent(layer, content) {
2490
+ layer.innerHTML = '';
2491
+ if (content == null) return;
2492
+ if (typeof content === 'string') {
2493
+ layer.innerHTML = content;
2494
+ return;
2495
+ }
2496
+ var nodes = Array.isArray(content) ? content : [content];
2497
+ var _iterator2 = _createForOfIteratorHelper(nodes),
2498
+ _step2;
2499
+ try {
2500
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
2501
+ var node = _step2.value;
2502
+ if (node && typeof node.nodeType === 'number') layer.appendChild(node);
2503
+ }
2504
+ } catch (err) {
2505
+ _iterator2.e(err);
2506
+ } finally {
2507
+ _iterator2.f();
2508
+ }
2111
2509
  };
2112
- renderCopy();
2113
- if (copy.background) {
2114
- root.style.setProperty('--hj-progress-copy-bg', copy.background);
2115
- root.style.setProperty('--hj-progress-copy-pad', '6px 12px');
2510
+ var textLayer = document.createElement('div');
2511
+ textLayer.className = 'hj-progress-text hj-progress-copy';
2512
+ var visualLayer = document.createElement('div');
2513
+ visualLayer.className = 'hj-progress-visual';
2514
+ fillContent(textLayer, options.text);
2515
+ fillContent(visualLayer, options.colorContent);
2516
+ var valueElement = merged.showValue === false ? null : document.createElement('div');
2517
+ if (valueElement) {
2518
+ valueElement.className = 'hj-progress-value';
2519
+ valueElement.setAttribute('aria-hidden', 'true');
2116
2520
  }
2117
- if (copy.color) root.style.setProperty('--hj-progress-copy-color', copy.color);
2118
- var valueElement = document.createElement('div');
2119
- valueElement.className = 'hj-progress-value';
2120
- valueElement.setAttribute('aria-hidden', 'true');
2121
- var dragLabel = document.createElement('span');
2122
- dragLabel.className = 'hj-progress-drag-label';
2123
- dragLabel.setAttribute('aria-hidden', 'true');
2124
- dragLabel.textContent = copy.dragLabel;
2125
2521
  root.appendChild(canvas);
2126
- if (copyEnabled) root.appendChild(copyLayer);
2127
- root.appendChild(valueElement);
2128
- root.appendChild(dragLabel);
2522
+ root.appendChild(textLayer);
2523
+ root.appendChild(visualLayer);
2524
+ if (valueElement) root.appendChild(valueElement);
2129
2525
  container.appendChild(root);
2130
2526
  var emitter = createEmitter();
2131
2527
  var paused = merged.respectReducedMotion && prefersReducedMotion$1();
2528
+ var disposed = false;
2132
2529
  var applySize = function applySize() {
2133
2530
  root.style.width = parseSize(merged.width);
2134
2531
  root.style.height = parseSize(merged.height);
@@ -2137,6 +2534,9 @@
2137
2534
  var name = _Object$keys2[_i2];
2138
2535
  root.style.setProperty(name, vars[name]);
2139
2536
  }
2537
+ if (merged.textRatio !== undefined) {
2538
+ root.style.setProperty('--hj-text-width', "".concat(merged.textRatio, "%"));
2539
+ }
2140
2540
  };
2141
2541
  applySize();
2142
2542
  var controller = new ProgressCapsuleController({
@@ -2145,8 +2545,12 @@
2145
2545
  valueElement: valueElement,
2146
2546
  preset: preset,
2147
2547
  emitter: emitter,
2148
- options: merged,
2149
- copy: copy
2548
+ options: _objectSpread2(_objectSpread2({}, merged), {}, {
2549
+ draggable: effectiveDraggable,
2550
+ keyboard: effectiveKeyboard
2551
+ }),
2552
+ copy: copy,
2553
+ dirty: dirty
2150
2554
  });
2151
2555
  var overlay = null;
2152
2556
  if (merged.renderer !== 'canvas2d') {
@@ -2159,7 +2563,14 @@
2159
2563
  }
2160
2564
  });
2161
2565
  }
2162
- if (overlay) controller.webglActive = true;
2566
+ if (overlay) controller.webglActive = true;else if (merged.renderer !== 'canvas2d') {
2567
+ nextTick(function () {
2568
+ if (disposed) return;
2569
+ emitter.emit('error', {
2570
+ message: 'WebGL2 unavailable, using Canvas2D fallback'
2571
+ });
2572
+ });
2573
+ }
2163
2574
  var visibility = createVisibilityGuard(root);
2164
2575
  var flowTime = 0;
2165
2576
  var unsubscribe = subscribeScheduler(function (delta, now) {
@@ -2172,16 +2583,14 @@
2172
2583
  }, function () {
2173
2584
  return paused;
2174
2585
  });
2175
- emitter.emit('ready', {
2176
- preset: _objectSpread2({}, preset)
2586
+ nextTick(function () {
2587
+ if (disposed) return;
2588
+ emitter.emit('ready', {
2589
+ preset: _objectSpread2({}, preset)
2590
+ });
2177
2591
  });
2178
2592
  var syncDom = function syncDom() {
2179
- root.setAttribute('aria-label', interpolate(copy.progressAria, {
2180
- brand: copy.brandName,
2181
- code: preset.code,
2182
- name: preset.name
2183
- }));
2184
- renderCopy();
2593
+ updateAria();
2185
2594
  };
2186
2595
  return {
2187
2596
  element: root,
@@ -2203,8 +2612,11 @@
2203
2612
  },
2204
2613
  setPreset: function setPreset(ref) {
2205
2614
  var next = getPreset('progress', ref);
2615
+ dirty.preset = true;
2206
2616
  Object.assign(preset, next);
2207
- controller.preset = next;
2617
+ var nextColors = preset.colors.map(normalizeColor);
2618
+ if (nextColors.every(Boolean)) preset.colors = nextColors;
2619
+ controller.preset = preset;
2208
2620
  controller.profile = next.edgeStyle === 'tide' ? FLOW_PROFILES.tide : FLOW_PROFILES[next.id] || FLOW_PROFILES['visual-training'];
2209
2621
  controller.flowTime = stringSeed(next.id) * 31;
2210
2622
  controller.seed = stringSeed("".concat(next.id, "-reference")) * Math.PI * 2;
@@ -2214,7 +2626,7 @@
2214
2626
  overlay = attachProgressFlowOverlay({
2215
2627
  root: root,
2216
2628
  canvas: canvas,
2217
- preset: next,
2629
+ preset: preset,
2218
2630
  getProgress: function getProgress() {
2219
2631
  return controller.value;
2220
2632
  }
@@ -2223,13 +2635,14 @@
2223
2635
  controller.webglActive = Boolean(overlay);
2224
2636
  controller.resizeCanvas();
2225
2637
  emitter.emit('presetchange', {
2226
- preset: _objectSpread2({}, next)
2638
+ preset: _objectSpread2({}, preset)
2227
2639
  });
2228
2640
  return this;
2229
2641
  },
2230
2642
  setEdgeStyle: function setEdgeStyle(edgeStyle) {
2231
2643
  var value = String(edgeStyle || '').toLowerCase();
2232
2644
  if (value !== 'flow' && value !== 'tide') return this;
2645
+ dirty.edgeStyle = true;
2233
2646
  preset.edgeStyle = value;
2234
2647
  controller.profile = value === 'tide' ? FLOW_PROFILES.tide : FLOW_PROFILES[preset.id] || FLOW_PROFILES['visual-training'];
2235
2648
  if (overlay) {
@@ -2247,10 +2660,49 @@
2247
2660
  controller.resizeCanvas();
2248
2661
  return this;
2249
2662
  },
2663
+ setText: function setText(content) {
2664
+ fillContent(textLayer, content);
2665
+ return this;
2666
+ },
2667
+ setColorContent: function setColorContent(content) {
2668
+ fillContent(visualLayer, content);
2669
+ return this;
2670
+ },
2671
+ setTextRatio: function setTextRatio(ratio) {
2672
+ var value = Number(ratio);
2673
+ if (!Number.isFinite(value) || value < 0 || value > 100) return this;
2674
+ dirty.textRatio = true;
2675
+ merged.textRatio = value;
2676
+ root.style.setProperty('--hj-text-width', "".concat(value, "%"));
2677
+ return this;
2678
+ },
2679
+ setCssVars: function setCssVars(vars) {
2680
+ if (!vars || _typeof(vars) !== 'object') return this;
2681
+ dirty.cssVars = true;
2682
+ merged.cssVars = _objectSpread2(_objectSpread2({}, merged.cssVars || {}), vars);
2683
+ for (var _i3 = 0, _Object$keys3 = Object.keys(vars); _i3 < _Object$keys3.length; _i3++) {
2684
+ var name = _Object$keys3[_i3];
2685
+ root.style.setProperty(name, vars[name]);
2686
+ }
2687
+ return this;
2688
+ },
2689
+ setLabel: function setLabel(label) {
2690
+ _readOnlyError("customLabel");
2691
+ updateAria();
2692
+ return this;
2693
+ },
2694
+ setValueSuffix: function setValueSuffix(suffix) {
2695
+ controller.setValueSuffix(suffix);
2696
+ return this;
2697
+ },
2250
2698
  setColors: function setColors(colors) {
2251
- if (!Array.isArray(colors) || colors.length !== 4) return this;
2252
- controller.setColors(colors);
2253
- if (overlay) overlay.setColors(colors);
2699
+ var next = Array.isArray(colors) ? colors.map(normalizeColor) : [];
2700
+ if (next.length !== 4 || next.some(function (color) {
2701
+ return !color;
2702
+ })) return this;
2703
+ dirty.colors = true;
2704
+ controller.setColors(next);
2705
+ if (overlay) overlay.setColors(next);
2254
2706
  controller.resizeCanvas();
2255
2707
  return this;
2256
2708
  },
@@ -2280,12 +2732,16 @@
2280
2732
  return this;
2281
2733
  },
2282
2734
  dispose: function dispose() {
2735
+ disposed = true;
2283
2736
  unsubscribe();
2284
2737
  visibility.dispose();
2285
2738
  if (overlay) overlay.dispose();
2286
2739
  controller.dispose();
2287
2740
  root.remove();
2288
- }
2741
+ },
2742
+ textRatio: merged.textRatio,
2743
+ cssVars: merged.cssVars,
2744
+ dirty: dirty
2289
2745
  };
2290
2746
  }
2291
2747
 
@@ -2321,6 +2777,10 @@
2321
2777
  opacity: 1,
2322
2778
  fallbackColor: true
2323
2779
  }, preset, options);
2780
+ var normalizedColors = merged.colors.map(normalizeColor);
2781
+ if (normalizedColors.every(Boolean)) preset.colors = normalizedColors;
2782
+ preset.seed = merged.seed;
2783
+ preset.speed = merged.speed;
2324
2784
  var computed = getComputedStyle(host);
2325
2785
  if (computed.position === 'static' || computed.position === '') {
2326
2786
  host.style.position = 'relative';
@@ -2341,6 +2801,16 @@
2341
2801
  host.appendChild(layer);
2342
2802
  var emitter = createEmitter();
2343
2803
  var paused = merged.respectReducedMotion && prefersReducedMotion();
2804
+ var disposed = false;
2805
+ var dirty = {
2806
+ preset: false,
2807
+ seed: false,
2808
+ speed: false,
2809
+ colors: false,
2810
+ opacity: false,
2811
+ fallbackColor: false,
2812
+ mouseColor: false
2813
+ };
2344
2814
  var renderer;
2345
2815
  var useWebgl = merged.renderer !== 'canvas2d';
2346
2816
  if (useWebgl) {
@@ -2352,8 +2822,11 @@
2352
2822
  });
2353
2823
  } catch (error) {
2354
2824
  renderer = new FallbackRenderer(canvas, merged);
2355
- emitter.emit('error', {
2356
- message: String(error && error.message ? error.message : error)
2825
+ nextTick(function () {
2826
+ if (disposed) return;
2827
+ emitter.emit('error', {
2828
+ message: String(error && error.message ? error.message : error)
2829
+ });
2357
2830
  });
2358
2831
  }
2359
2832
  } else {
@@ -2371,8 +2844,11 @@
2371
2844
  }, function () {
2372
2845
  return paused;
2373
2846
  });
2374
- emitter.emit('ready', {
2375
- preset: _objectSpread2({}, preset)
2847
+ nextTick(function () {
2848
+ if (disposed) return;
2849
+ emitter.emit('ready', {
2850
+ preset: _objectSpread2({}, preset)
2851
+ });
2376
2852
  });
2377
2853
  return {
2378
2854
  element: host,
@@ -2383,24 +2859,59 @@
2383
2859
  off: emitter.off,
2384
2860
  setPreset: function setPreset(ref) {
2385
2861
  var next = getPreset('capsule', ref);
2862
+ dirty.preset = true;
2386
2863
  Object.assign(preset, next);
2387
- renderer.setPreset(next);
2864
+ var nextColors = preset.colors.map(normalizeColor);
2865
+ if (nextColors.every(Boolean)) preset.colors = nextColors;
2866
+ renderer.setPreset(_objectSpread2({}, preset));
2388
2867
  renderer.resize();
2389
2868
  syncLayerVars();
2390
2869
  emitter.emit('presetchange', {
2391
- preset: _objectSpread2({}, next)
2870
+ preset: _objectSpread2({}, preset)
2392
2871
  });
2393
2872
  return this;
2394
2873
  },
2395
2874
  setColors: function setColors(colors) {
2396
- if (!Array.isArray(colors) || colors.length !== 4) return this;
2397
- preset.colors = colors;
2875
+ var next = Array.isArray(colors) ? colors.map(normalizeColor) : [];
2876
+ if (next.length !== 4 || next.some(function (color) {
2877
+ return !color;
2878
+ })) return this;
2879
+ dirty.colors = true;
2880
+ preset.colors = next;
2398
2881
  renderer.setPreset(_objectSpread2(_objectSpread2({}, preset), {}, {
2399
- colors: colors
2882
+ colors: next
2400
2883
  }));
2401
2884
  syncLayerVars();
2402
2885
  return this;
2403
2886
  },
2887
+ setSeed: function setSeed(seed) {
2888
+ var value = Number(seed);
2889
+ if (!Number.isFinite(value)) return this;
2890
+ dirty.seed = true;
2891
+ preset.seed = value;
2892
+ renderer.setPreset(_objectSpread2({}, preset));
2893
+ return this;
2894
+ },
2895
+ setSpeed: function setSpeed(speed) {
2896
+ var value = Number(speed);
2897
+ if (!Number.isFinite(value)) return this;
2898
+ dirty.speed = true;
2899
+ preset.speed = value;
2900
+ renderer.setPreset(_objectSpread2({}, preset));
2901
+ return this;
2902
+ },
2903
+ setFallbackColor: function setFallbackColor(value) {
2904
+ dirty.fallbackColor = true;
2905
+ merged.fallbackColor = value;
2906
+ syncLayerVars();
2907
+ return this;
2908
+ },
2909
+ setMouseColor: function setMouseColor(value) {
2910
+ dirty.mouseColor = true;
2911
+ merged.mouseColor = value !== false;
2912
+ if (typeof renderer.setMouseColor === 'function') renderer.setMouseColor(merged.mouseColor);
2913
+ return this;
2914
+ },
2404
2915
  setQuality: function setQuality(quality) {
2405
2916
  merged.quality = quality;
2406
2917
  if (typeof renderer.setDprCap === 'function') renderer.setDprCap(dprCapFor(quality));
@@ -2409,12 +2920,13 @@
2409
2920
  setOpacity: function setOpacity(value) {
2410
2921
  var opacity = Number(value);
2411
2922
  if (!Number.isFinite(opacity)) return this;
2923
+ dirty.opacity = true;
2412
2924
  merged.opacity = Math.min(1, Math.max(0, opacity));
2413
2925
  layer.style.setProperty('--dlc-color-opacity', String(merged.opacity));
2414
2926
  return this;
2415
2927
  },
2416
2928
  randomize: function randomize() {
2417
- renderer.randomize();
2929
+ this.setSeed(Math.random() * 100);
2418
2930
  return this;
2419
2931
  },
2420
2932
  pause: function pause() {
@@ -2426,12 +2938,17 @@
2426
2938
  return this;
2427
2939
  },
2428
2940
  dispose: function dispose() {
2941
+ disposed = true;
2429
2942
  unsubscribe();
2430
2943
  visibility.dispose();
2431
2944
  if (resizeObserver) resizeObserver.disconnect();else window.removeEventListener('resize', renderer.resize);
2432
2945
  renderer.dispose();
2433
2946
  layer.remove();
2434
- }
2947
+ },
2948
+ opacity: merged.opacity,
2949
+ fallbackColor: merged.fallbackColor,
2950
+ mouseColor: merged.mouseColor,
2951
+ dirty: dirty
2435
2952
  };
2436
2953
  }
2437
2954
 
@@ -2452,6 +2969,7 @@
2452
2969
  exports.getPreset = getPreset;
2453
2970
  exports.hexToRgb01 = hexToRgb01$1;
2454
2971
  exports.hexToRgba = hexToRgba;
2972
+ exports.normalizeColor = normalizeColor;
2455
2973
  exports.parseSize = parseSize;
2456
2974
  exports.pauseAll = pauseAll;
2457
2975
  exports.resumeAll = resumeAll;