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