@5even7/dlc-ui 0.1.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
  }
@@ -324,7 +327,7 @@
324
327
  var CAPSULE_PRESETS = [{
325
328
  id: 'original',
326
329
  code: 'NC-01',
327
- name: 'ORIGINAL',
330
+ name: '初光',
328
331
  group: 'warm',
329
332
  seed: 1.7,
330
333
  speed: 0.5,
@@ -332,7 +335,7 @@
332
335
  }, {
333
336
  id: 'ocean',
334
337
  code: 'NC-02',
335
- name: 'OCEAN',
338
+ name: '沧溟',
336
339
  group: 'cold',
337
340
  seed: 8.2,
338
341
  speed: 0.48,
@@ -340,7 +343,7 @@
340
343
  }, {
341
344
  id: 'klein',
342
345
  code: 'NC-03',
343
- name: 'KLEIN',
346
+ name: '玄夜',
344
347
  group: 'cold',
345
348
  seed: 14.1,
346
349
  speed: 0.49,
@@ -348,7 +351,7 @@
348
351
  }, {
349
352
  id: 'ultraviolet',
350
353
  code: 'NC-04',
351
- name: 'ULTRAVIOLET',
354
+ name: '紫霄',
352
355
  group: 'cold',
353
356
  seed: 23.4,
354
357
  speed: 0.47,
@@ -356,7 +359,7 @@
356
359
  }, {
357
360
  id: 'chrome',
358
361
  code: 'NC-05',
359
- name: 'CHROME',
362
+ name: '银汉',
360
363
  group: 'cold',
361
364
  seed: 37.8,
362
365
  speed: 0.42,
@@ -364,7 +367,7 @@
364
367
  }, {
365
368
  id: 'plus',
366
369
  code: 'NC-06',
367
- name: 'PLUS',
370
+ name: '熔金',
368
371
  group: 'warm',
369
372
  seed: 51.3,
370
373
  speed: 0.5,
@@ -374,7 +377,7 @@
374
377
  var PROGRESS_PRESETS = [{
375
378
  id: 'model-training',
376
379
  code: 'NC-10',
377
- name: 'MODEL TRAINING',
380
+ name: '星火',
378
381
  subtitle: 'SHA 4.5 + 100 2026 TKN',
379
382
  group: 'progress',
380
383
  initialProgress: 43,
@@ -383,7 +386,7 @@
383
386
  }, {
384
387
  id: 'agent-migration',
385
388
  code: 'NC-11',
386
- name: 'AGENT MIGRATION',
389
+ name: '迁流',
387
390
  subtitle: 'TRANSFERRING PROTOCOL',
388
391
  group: 'progress',
389
392
  initialProgress: 33,
@@ -392,7 +395,7 @@
392
395
  }, {
393
396
  id: 'visual-training',
394
397
  code: 'NC-12',
395
- name: 'VISUAL TRAINING',
398
+ name: '幻境',
396
399
  subtitle: 'GENERATING POWER ++',
397
400
  group: 'progress',
398
401
  initialProgress: 58,
@@ -401,7 +404,7 @@
401
404
  }, {
402
405
  id: 'tide',
403
406
  code: 'NC-13',
404
- name: 'TIDE',
407
+ name: '汐潮',
405
408
  subtitle: 'MOON PULL / COAST',
406
409
  group: 'progress',
407
410
  initialProgress: 30,
@@ -411,23 +414,18 @@
411
414
 
412
415
  var PRESETS = CAPSULE_PRESETS;
413
416
  var ALL_PRESETS = [].concat(_toConsumableArray(CAPSULE_PRESETS), _toConsumableArray(PROGRESS_PRESETS));
414
- function validatePreset(preset) {
415
- return Boolean(preset && typeof preset.id === 'string' && typeof preset.code === 'string' && typeof preset.name === 'string' && Number.isFinite(preset.seed) && Number.isFinite(preset.speed) && Array.isArray(preset.colors) && preset.colors.length === 4);
416
- }
417
- function validateProgressPreset(preset) {
418
- return Boolean(preset && typeof preset.id === 'string' && typeof preset.code === 'string' && typeof preset.name === 'string' && preset.group === 'progress' && Number.isFinite(preset.initialProgress) && ['flow', 'tide'].indexOf(preset.edgeStyle || 'flow') !== -1 && Array.isArray(preset.colors) && preset.colors.length === 4);
419
- }
417
+
418
+ /**
419
+ * Resolve a preset by its literary name (e.g. '沧溟').
420
+ * Matching is case-insensitive and ignores surrounding whitespace,
421
+ * so '沧溟' and ' 沧溟 ' both work.
422
+ */
420
423
  function getPreset(kind, ref) {
421
424
  var list = kind === 'capsule' ? CAPSULE_PRESETS : kind === 'progress' ? PROGRESS_PRESETS : null;
422
425
  if (!list) throw new Error("Unknown preset kind: ".concat(kind, " (use \"capsule\" or \"progress\")"));
423
- if (ref && _typeof(ref) === 'object') {
424
- var valid = kind === 'capsule' ? validatePreset(ref) : validateProgressPreset(ref);
425
- if (!valid) throw new Error("Invalid ".concat(kind, " preset object"));
426
- return ref;
427
- }
428
426
  var key = String(ref).trim().toLowerCase();
429
427
  var found = list.find(function (preset) {
430
- return preset.id.toLowerCase() === key || preset.code.toLowerCase() === key || preset.name.toLowerCase() === key;
428
+ return preset.name.toLowerCase() === key;
431
429
  });
432
430
  if (!found) throw new Error("Unknown ".concat(kind, " preset: ").concat(ref));
433
431
  return found;
@@ -440,9 +438,8 @@
440
438
  quality: 'auto',
441
439
  renderer: 'auto',
442
440
  respectReducedMotion: true,
443
- interactive: true,
444
441
  mouseColor: true,
445
- showCopy: true
442
+ textRatio: 39
446
443
  },
447
444
  progress: {
448
445
  width: 454,
@@ -451,43 +448,250 @@
451
448
  max: 100,
452
449
  draggable: true,
453
450
  keyboard: true,
451
+ disabled: false,
452
+ valueSuffix: '%',
454
453
  edgeStyle: 'flow',
455
454
  quality: 'auto',
456
455
  renderer: 'auto',
457
- showCopy: true,
456
+ textRatio: 54,
457
+ showValue: true,
458
458
  respectReducedMotion: true
459
459
  }
460
460
  };
461
461
 
462
- /**
463
- * All user-facing copy lives here so wording/brand changes never require a
464
- * 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.
465
466
  */
466
467
  var COPY = {
467
468
  brandName: '画境观屿',
468
- dragLabel: 'DRAG',
469
469
  valueSuffix: '%',
470
470
  progressAria: '{brand} {code} 加载进度',
471
471
  capsuleAria: '打开 {name} 沉浸预览'
472
472
  };
473
473
 
474
- function hexToRgb01$1(hex) {
475
- var normalized = hex.replace('#', '');
476
- 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);
477
680
  return [(value >> 16 & 255) / 255, (value >> 8 & 255) / 255, (value & 255) / 255];
478
681
  }
479
- function hexToRgba(hex) {
682
+ function hexToRgba(color) {
480
683
  var alpha = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
481
- var normalized = hex.replace('#', '');
482
- 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);
483
687
  var red = value >> 16 & 255;
484
688
  var green = value >> 8 & 255;
485
689
  var blue = value & 255;
486
690
  return "rgba(".concat(red, ", ").concat(green, ", ").concat(blue, ", ").concat(alpha, ")");
487
691
  }
488
692
  function validateColors(colors) {
489
- return Array.isArray(colors) && colors.length === 4 && colors.every(function (c) {
490
- 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;
491
695
  });
492
696
  }
493
697
 
@@ -565,6 +769,26 @@
565
769
  this.options.dprCap = cap;
566
770
  this.resize();
567
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
+ }
568
792
  }, {
569
793
  key: "randomize",
570
794
  value: function randomize() {
@@ -713,6 +937,8 @@
713
937
  var _this$canvas = this.canvas,
714
938
  width = _this$canvas.width,
715
939
  height = _this$canvas.height;
940
+ var t = time * (this.preset.speed || 1);
941
+ var phase = (this.preset.seed || 0) * 0.7;
716
942
  var gradient = ctx.createLinearGradient(0, 0, width, height);
717
943
  gradient.addColorStop(0, this.preset.colors[0]);
718
944
  gradient.addColorStop(0.38, this.preset.colors[1]);
@@ -722,8 +948,8 @@
722
948
  ctx.fillRect(0, 0, width, height);
723
949
  ctx.globalCompositeOperation = 'screen';
724
950
  for (var index = 0; index < 6; index += 1) {
725
- var x = (0.5 + 0.45 * Math.sin(time * 0.32 + index * 1.7)) * width;
726
- 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;
727
953
  var radius = Math.max(width, height) * (0.08 + index % 3 * 0.04);
728
954
  var glow = ctx.createRadialGradient(x, y, 0, x, y, radius);
729
955
  glow.addColorStop(0, rgb(this.preset.colors[(index + 1) % 4], 0.24));
@@ -747,7 +973,12 @@
747
973
  }
748
974
  }, {
749
975
  key: "randomize",
750
- 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() {}
751
982
  }, {
752
983
  key: "dispose",
753
984
  value: function dispose() {}
@@ -865,16 +1096,34 @@
865
1096
  };
866
1097
  }
867
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
+
868
1115
  function prefersReducedMotion$2() {
869
1116
  return typeof matchMedia !== 'undefined' && matchMedia('(prefers-reduced-motion: reduce)').matches;
870
1117
  }
871
1118
 
872
1119
  /**
873
- * Mount a cosmic (nebula) capsule into `container`.
874
- *
875
- * Options: preset (id/code/name or object), width, height (number=px or
876
- * string with px/%/vw/vh/em/rem), colors, seed, speed, quality, interactive,
877
- * 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.
878
1127
  */
879
1128
  function createCapsule(container) {
880
1129
  var _options$preset;
@@ -882,53 +1131,73 @@
882
1131
  if (!container || typeof container.appendChild !== 'function') {
883
1132
  throw new Error('createCapsule: container element is required');
884
1133
  }
885
- var preset = _objectSpread2({}, getPreset('capsule', (_options$preset = options.preset) !== null && _options$preset !== void 0 ? _options$preset : 'NC-01'));
1134
+ var preset = _objectSpread2({}, getPreset('capsule', (_options$preset = options.preset) !== null && _options$preset !== void 0 ? _options$preset : '初光'));
886
1135
  var merged = normalizeOptions(DEFAULTS.capsule, preset, options);
887
- 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;
888
1141
  var root = document.createElement('div');
889
1142
  root.className = 'hj-capsule-root hj-capsule-cosmic';
890
1143
  root.dataset.group = preset.group;
891
1144
  root.dataset.mode = 'nebula';
892
1145
  root.dataset.theme = preset.theme || 'light';
893
- root.setAttribute('aria-label', copy.capsuleAria.replace('{name}', preset.name));
894
- var copyEnabled = copy.enabled !== false && merged.showCopy !== false;
895
- var copyText = function copyText(key, fallback) {
896
- var value = copy[key];
897
- return value === undefined ? fallback : value;
898
- };
899
- function buildCopyHtml() {
900
- var codeText = copyText('code', preset.code);
901
- var nameText = copyText('name', preset.name);
902
- var subtitleText = copyText('subtitle', preset.subtitle);
903
- var stateText = copyText('state', 'LIVE COSMIC STUDY');
904
- var html = '';
905
- if (codeText) html += "<span class=\"hj-capsule-code\">".concat(codeText, "</span>");
906
- if (nameText) html += "<span class=\"hj-capsule-name\">".concat(nameText, "</span>");
907
- if (subtitleText) html += "<span class=\"hj-capsule-brand\">".concat(subtitleText, "</span>");
908
- if (stateText) html += "<span class=\"hj-capsule-state\">".concat(stateText, "</span>");
909
- return html;
910
- }
911
- var copyLayer = document.createElement('div');
912
- copyLayer.className = 'hj-capsule-copy';
913
- var renderCopy = function renderCopy() {
914
- 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));
915
1149
  };
916
- renderCopy();
917
- var isHexColor = function isHexColor(value) {
918
- 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
+ }
919
1176
  };
920
- if (copy.background) {
921
- 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);
922
- }
923
- 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);
924
1183
  var canvas = document.createElement('canvas');
925
1184
  canvas.className = 'hj-capsule-canvas';
926
1185
  canvas.setAttribute('aria-hidden', 'true');
927
- if (copyEnabled) root.appendChild(copyLayer);
1186
+ root.appendChild(textLayer);
1187
+ root.appendChild(visualLayer);
928
1188
  root.appendChild(canvas);
929
1189
  container.appendChild(root);
930
1190
  var emitter = createEmitter();
931
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
+ };
932
1201
  var renderer;
933
1202
  var useWebgl = merged.renderer !== 'canvas2d';
934
1203
  if (useWebgl) {
@@ -938,8 +1207,11 @@
938
1207
  });
939
1208
  } catch (error) {
940
1209
  renderer = new FallbackRenderer(canvas, merged);
941
- emitter.emit('error', {
942
- 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
+ });
943
1215
  });
944
1216
  }
945
1217
  } else {
@@ -953,6 +1225,9 @@
953
1225
  var name = _Object$keys[_i];
954
1226
  root.style.setProperty(name, vars[name]);
955
1227
  }
1228
+ if (merged.textRatio !== undefined) {
1229
+ root.style.setProperty('--hj-text-width', "".concat(merged.textRatio, "%"));
1230
+ }
956
1231
  renderer.resize();
957
1232
  };
958
1233
  applySize();
@@ -961,18 +1236,16 @@
961
1236
  }) : null;
962
1237
  if (resizeObserver) resizeObserver.observe(root);else window.addEventListener('resize', renderer.resize);
963
1238
  var visibility = createVisibilityGuard(root);
964
- if (merged.interactive !== false) {
965
- root.addEventListener('pointerenter', function () {
966
- return emitter.emit('pointerenter', {
967
- preset: _objectSpread2({}, preset)
968
- });
1239
+ root.addEventListener('pointerenter', function () {
1240
+ return emitter.emit('pointerenter', {
1241
+ preset: _objectSpread2({}, preset)
969
1242
  });
970
- root.addEventListener('pointerleave', function () {
971
- return emitter.emit('pointerleave', {
972
- preset: _objectSpread2({}, preset)
973
- });
1243
+ });
1244
+ root.addEventListener('pointerleave', function () {
1245
+ return emitter.emit('pointerleave', {
1246
+ preset: _objectSpread2({}, preset)
974
1247
  });
975
- }
1248
+ });
976
1249
  root.addEventListener('click', function (event) {
977
1250
  emitter.emit('click', {
978
1251
  event: event,
@@ -1004,13 +1277,16 @@
1004
1277
  }, function () {
1005
1278
  return paused;
1006
1279
  });
1007
- emitter.emit('ready', {
1008
- preset: _objectSpread2({}, preset)
1280
+ nextTick(function () {
1281
+ if (disposed) return;
1282
+ emitter.emit('ready', {
1283
+ preset: _objectSpread2({}, preset)
1284
+ });
1009
1285
  });
1010
- var syncCopy = function syncCopy() {
1286
+ var syncTheme = function syncTheme() {
1011
1287
  root.dataset.mode = 'nebula';
1012
1288
  root.dataset.theme = preset.theme || 'light';
1013
- renderCopy();
1289
+ updateAria();
1014
1290
  };
1015
1291
  return {
1016
1292
  element: root,
@@ -1020,9 +1296,12 @@
1020
1296
  off: emitter.off,
1021
1297
  setPreset: function setPreset(ref) {
1022
1298
  var next = getPreset('capsule', ref);
1299
+ dirty.preset = true;
1023
1300
  Object.assign(preset, next);
1024
- renderer.setPreset(next);
1025
- syncCopy();
1301
+ var nextColors = preset.colors.map(normalizeColor);
1302
+ if (nextColors.every(Boolean)) preset.colors = nextColors;
1303
+ renderer.setPreset(_objectSpread2({}, preset));
1304
+ syncTheme();
1026
1305
  renderer.resize();
1027
1306
  emitter.emit('presetchange', {
1028
1307
  preset: _objectSpread2({}, next)
@@ -1030,13 +1309,64 @@
1030
1309
  return this;
1031
1310
  },
1032
1311
  setColors: function setColors(colors) {
1033
- if (!Array.isArray(colors) || colors.length !== 4) return this;
1034
- 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;
1035
1318
  renderer.setPreset(_objectSpread2(_objectSpread2({}, preset), {}, {
1036
- colors: colors
1319
+ colors: next
1037
1320
  }));
1038
1321
  return this;
1039
1322
  },
1323
+ setSeed: function setSeed(seed) {
1324
+ var value = Number(seed);
1325
+ if (!Number.isFinite(value)) return this;
1326
+ dirty.seed = true;
1327
+ preset.seed = value;
1328
+ renderer.setPreset(_objectSpread2({}, preset));
1329
+ return this;
1330
+ },
1331
+ setSpeed: function setSpeed(speed) {
1332
+ var value = Number(speed);
1333
+ if (!Number.isFinite(value)) return this;
1334
+ dirty.speed = true;
1335
+ preset.speed = value;
1336
+ renderer.setPreset(_objectSpread2({}, preset));
1337
+ return this;
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
+ },
1040
1370
  setSize: function setSize(width, height) {
1041
1371
  if (width !== undefined) merged.width = width;
1042
1372
  if (height !== undefined) merged.height = height;
@@ -1044,7 +1374,7 @@
1044
1374
  return this;
1045
1375
  },
1046
1376
  randomize: function randomize() {
1047
- renderer.randomize();
1377
+ this.setSeed(Math.random() * 100);
1048
1378
  return this;
1049
1379
  },
1050
1380
  pause: function pause() {
@@ -1061,12 +1391,16 @@
1061
1391
  return this;
1062
1392
  },
1063
1393
  dispose: function dispose() {
1394
+ disposed = true;
1064
1395
  unsubscribe();
1065
1396
  visibility.dispose();
1066
1397
  if (resizeObserver) resizeObserver.disconnect();else window.removeEventListener('resize', renderer.resize);
1067
1398
  renderer.dispose();
1068
1399
  root.remove();
1069
- }
1400
+ },
1401
+ textRatio: merged.textRatio,
1402
+ cssVars: merged.cssVars,
1403
+ dirty: dirty
1070
1404
  };
1071
1405
  }
1072
1406
 
@@ -1570,7 +1904,8 @@
1570
1904
  };
1571
1905
  var ProgressCapsuleController = /*#__PURE__*/function () {
1572
1906
  function ProgressCapsuleController(_ref) {
1573
- var _options$min,
1907
+ var _options$valueSuffix,
1908
+ _options$min,
1574
1909
  _options$max,
1575
1910
  _options$value,
1576
1911
  _this = this;
@@ -1580,7 +1915,8 @@
1580
1915
  preset = _ref.preset,
1581
1916
  emitter = _ref.emitter,
1582
1917
  options = _ref.options,
1583
- copy = _ref.copy;
1918
+ copy = _ref.copy,
1919
+ dirty = _ref.dirty;
1584
1920
  _classCallCheck(this, ProgressCapsuleController);
1585
1921
  this.root = root;
1586
1922
  this.canvas = canvas;
@@ -1589,6 +1925,8 @@
1589
1925
  this.emitter = emitter;
1590
1926
  this.options = options;
1591
1927
  this.copy = copy;
1928
+ this.dirty = dirty;
1929
+ this.valueSuffix = (_options$valueSuffix = options.valueSuffix) !== null && _options$valueSuffix !== void 0 ? _options$valueSuffix : copy.valueSuffix;
1592
1930
  this.profile = preset.edgeStyle === 'tide' ? FLOW_PROFILES.tide : FLOW_PROFILES[preset.id] || FLOW_PROFILES['visual-training'];
1593
1931
  this.min = (_options$min = options.min) !== null && _options$min !== void 0 ? _options$min : 0;
1594
1932
  this.max = (_options$max = options.max) !== null && _options$max !== void 0 ? _options$max : 100;
@@ -1628,12 +1966,24 @@
1628
1966
  var rounded = Math.round(this.value);
1629
1967
  this.root.style.setProperty('--progress', this.value.toFixed(2));
1630
1968
  this.root.setAttribute('aria-valuenow', String(rounded));
1631
- this.root.setAttribute('aria-valuetext', "".concat(rounded).concat(this.copy.valueSuffix));
1632
- this.valueElement.textContent = "".concat(rounded).concat(this.copy.valueSuffix);
1633
- if (!this.suppressEvents) this.emitter.emit('change', {
1634
- value: this.value,
1635
- source: source
1636
- });
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;
1637
1987
  }
1638
1988
  }, {
1639
1989
  key: "resizeCanvas",
@@ -1808,7 +2158,8 @@
1808
2158
  var width = this.width;
1809
2159
  var height = this.height;
1810
2160
  if (!ctx || width <= 0 || height <= 0) return;
1811
- 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);
1812
2163
  var time = this.flowTime;
1813
2164
  var _this$preset$colors = _slicedToArray(this.preset.colors, 4),
1814
2165
  dark = _this$preset$colors[0],
@@ -1923,7 +2274,7 @@
1923
2274
  value: function updateFromPointer(event) {
1924
2275
  var bounds = this.root.getBoundingClientRect();
1925
2276
  var ratio = bounds.width > 0 ? (event.clientX - bounds.left) / bounds.width : 0;
1926
- this.setProgress(ratio * 100, 'drag');
2277
+ this.setProgress(this.min + ratio * (this.max - this.min), 'drag');
1927
2278
  }
1928
2279
  }, {
1929
2280
  key: "beginDrag",
@@ -1931,6 +2282,8 @@
1931
2282
  if (event.button !== undefined && event.button !== 0) return;
1932
2283
  event.preventDefault();
1933
2284
  this.dragging = true;
2285
+ this.pendingPointer = null;
2286
+ this._dragRaf = 0;
1934
2287
  this.root.classList.add('is-dragging');
1935
2288
  try {
1936
2289
  var _this$root$setPointer, _this$root;
@@ -1944,15 +2297,31 @@
1944
2297
  }, {
1945
2298
  key: "moveDrag",
1946
2299
  value: function moveDrag(event) {
2300
+ var _this2 = this;
1947
2301
  if (!this.dragging) return;
1948
2302
  event.preventDefault();
1949
- 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
+ });
1950
2312
  }
1951
2313
  }, {
1952
2314
  key: "endDrag",
1953
2315
  value: function endDrag(event) {
1954
2316
  if (!this.dragging) return;
1955
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);
1956
2325
  this.root.classList.remove('is-dragging');
1957
2326
  try {
1958
2327
  var _this$root$hasPointer, _this$root2;
@@ -1967,19 +2336,19 @@
1967
2336
  }, {
1968
2337
  key: "bindEvents",
1969
2338
  value: function bindEvents() {
1970
- var _this2 = this;
2339
+ var _this3 = this;
1971
2340
  if (this.options.draggable !== false) {
1972
2341
  this.handlers.pointerdown = function (event) {
1973
- return _this2.beginDrag(event);
2342
+ return _this3.beginDrag(event);
1974
2343
  };
1975
2344
  this.handlers.pointermove = function (event) {
1976
- return _this2.moveDrag(event);
2345
+ return _this3.moveDrag(event);
1977
2346
  };
1978
2347
  this.handlers.pointerup = function (event) {
1979
- return _this2.endDrag(event);
2348
+ return _this3.endDrag(event);
1980
2349
  };
1981
2350
  this.handlers.pointercancel = function (event) {
1982
- return _this2.endDrag(event);
2351
+ return _this3.endDrag(event);
1983
2352
  };
1984
2353
  this.root.addEventListener('pointerdown', this.handlers.pointerdown);
1985
2354
  this.root.addEventListener('pointermove', this.handlers.pointermove);
@@ -1988,23 +2357,24 @@
1988
2357
  }
1989
2358
  if (this.options.keyboard !== false) {
1990
2359
  this.handlers.keydown = function (event) {
2360
+ var step = (_this3.max - _this3.min) * 0.02;
1991
2361
  var actions = {
1992
- ArrowLeft: -2,
1993
- ArrowDown: -2,
1994
- ArrowRight: 2,
1995
- ArrowUp: 2,
1996
- PageDown: -10,
1997
- PageUp: 10
2362
+ ArrowLeft: -step,
2363
+ ArrowDown: -step,
2364
+ ArrowRight: step,
2365
+ ArrowUp: step,
2366
+ PageDown: -step * 5,
2367
+ PageUp: step * 5
1998
2368
  };
1999
2369
  if (event.key === 'Home') {
2000
2370
  event.preventDefault();
2001
- _this2.setProgress(_this2.min, 'keyboard');
2371
+ _this3.setProgress(_this3.min, 'keyboard');
2002
2372
  } else if (event.key === 'End') {
2003
2373
  event.preventDefault();
2004
- _this2.setProgress(_this2.max, 'keyboard');
2374
+ _this3.setProgress(_this3.max, 'keyboard');
2005
2375
  } else if (actions[event.key]) {
2006
2376
  event.preventDefault();
2007
- _this2.setProgress(_this2.value + actions[event.key], 'keyboard');
2377
+ _this3.setProgress(_this3.value + actions[event.key], 'keyboard');
2008
2378
  } else {
2009
2379
  return;
2010
2380
  }
@@ -2041,6 +2411,7 @@
2041
2411
  }, {
2042
2412
  key: "dispose",
2043
2413
  value: function dispose() {
2414
+ if (this._dragRaf) cancelAnimationFrame(this._dragRaf);
2044
2415
  if (this.resizeObserver) this.resizeObserver.disconnect();else window.removeEventListener('resize', this.resizeCanvas);
2045
2416
  for (var _i = 0, _Object$keys = Object.keys(this.handlers); _i < _Object$keys.length; _i++) {
2046
2417
  var name = _Object$keys[_i];
@@ -2053,11 +2424,14 @@
2053
2424
  }();
2054
2425
 
2055
2426
  /**
2056
- * Mount a fluid progress capsule into `container`.
2057
- *
2058
- * Options: preset (id/code/name or object), width, height, value, min, max,
2059
- * draggable, keyboard, colors, edgeStyle, quality,
2060
- * 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.
2061
2435
  */
2062
2436
  function createProgressCapsule(container) {
2063
2437
  var _options$preset, _merged$min, _merged$max;
@@ -2065,64 +2439,93 @@
2065
2439
  if (!container || typeof container.appendChild !== 'function') {
2066
2440
  throw new Error('createProgressCapsule: container element is required');
2067
2441
  }
2068
- var preset = _objectSpread2({}, getPreset('progress', (_options$preset = options.preset) !== null && _options$preset !== void 0 ? _options$preset : 'NC-10'));
2442
+ var preset = _objectSpread2({}, getPreset('progress', (_options$preset = options.preset) !== null && _options$preset !== void 0 ? _options$preset : '星火'));
2069
2443
  var merged = normalizeOptions(DEFAULTS.progress, preset, options);
2070
- 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
+ };
2071
2462
  var root = document.createElement('div');
2072
2463
  root.className = 'hj-capsule-root hj-progress-root';
2073
2464
  root.setAttribute('role', 'slider');
2074
2465
  root.setAttribute('tabindex', '0');
2075
- 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
+ }
2076
2471
  root.setAttribute('aria-valuemin', String((_merged$min = merged.min) !== null && _merged$min !== void 0 ? _merged$min : 0));
2077
2472
  root.setAttribute('aria-valuemax', String((_merged$max = merged.max) !== null && _merged$max !== void 0 ? _merged$max : 100));
2078
2473
  root.setAttribute('aria-valuenow', String(preset.initialProgress));
2079
- root.setAttribute('aria-label', interpolate(copy.progressAria, {
2080
- brand: copy.brandName,
2081
- code: preset.code,
2082
- name: preset.name
2083
- }));
2084
- 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');
2085
2483
  var canvas = document.createElement('canvas');
2086
2484
  canvas.className = 'hj-progress-canvas';
2087
2485
  canvas.setAttribute('aria-hidden', 'true');
2088
- var copyEnabled = copy.enabled !== false && merged.showCopy !== false;
2089
- var copyText = function copyText(key, fallback) {
2090
- var value = copy[key];
2091
- return value === undefined ? fallback : value;
2092
- };
2093
- function buildCopyHtml() {
2094
- var html = '';
2095
- var brandText = copy.brandName || '';
2096
- var subtitleText = copyText('subtitle', preset.subtitle);
2097
- if (brandText) html += "<span class=\"hj-progress-name\">".concat(brandText, "</span>");
2098
- if (subtitleText) html += "<span class=\"hj-progress-subtitle\">".concat(subtitleText, "</span>");
2099
- return html;
2100
- }
2101
- var copyLayer = document.createElement('div');
2102
- copyLayer.className = 'hj-progress-copy';
2103
- var renderCopy = function renderCopy() {
2104
- 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
+ }
2105
2509
  };
2106
- renderCopy();
2107
- if (copy.background) {
2108
- root.style.setProperty('--hj-progress-copy-bg', copy.background);
2109
- 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');
2110
2520
  }
2111
- if (copy.color) root.style.setProperty('--hj-progress-copy-color', copy.color);
2112
- var valueElement = document.createElement('div');
2113
- valueElement.className = 'hj-progress-value';
2114
- valueElement.setAttribute('aria-hidden', 'true');
2115
- var dragLabel = document.createElement('span');
2116
- dragLabel.className = 'hj-progress-drag-label';
2117
- dragLabel.setAttribute('aria-hidden', 'true');
2118
- dragLabel.textContent = copy.dragLabel;
2119
2521
  root.appendChild(canvas);
2120
- if (copyEnabled) root.appendChild(copyLayer);
2121
- root.appendChild(valueElement);
2122
- root.appendChild(dragLabel);
2522
+ root.appendChild(textLayer);
2523
+ root.appendChild(visualLayer);
2524
+ if (valueElement) root.appendChild(valueElement);
2123
2525
  container.appendChild(root);
2124
2526
  var emitter = createEmitter();
2125
2527
  var paused = merged.respectReducedMotion && prefersReducedMotion$1();
2528
+ var disposed = false;
2126
2529
  var applySize = function applySize() {
2127
2530
  root.style.width = parseSize(merged.width);
2128
2531
  root.style.height = parseSize(merged.height);
@@ -2131,6 +2534,9 @@
2131
2534
  var name = _Object$keys2[_i2];
2132
2535
  root.style.setProperty(name, vars[name]);
2133
2536
  }
2537
+ if (merged.textRatio !== undefined) {
2538
+ root.style.setProperty('--hj-text-width', "".concat(merged.textRatio, "%"));
2539
+ }
2134
2540
  };
2135
2541
  applySize();
2136
2542
  var controller = new ProgressCapsuleController({
@@ -2139,8 +2545,12 @@
2139
2545
  valueElement: valueElement,
2140
2546
  preset: preset,
2141
2547
  emitter: emitter,
2142
- options: merged,
2143
- copy: copy
2548
+ options: _objectSpread2(_objectSpread2({}, merged), {}, {
2549
+ draggable: effectiveDraggable,
2550
+ keyboard: effectiveKeyboard
2551
+ }),
2552
+ copy: copy,
2553
+ dirty: dirty
2144
2554
  });
2145
2555
  var overlay = null;
2146
2556
  if (merged.renderer !== 'canvas2d') {
@@ -2153,7 +2563,14 @@
2153
2563
  }
2154
2564
  });
2155
2565
  }
2156
- 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
+ }
2157
2574
  var visibility = createVisibilityGuard(root);
2158
2575
  var flowTime = 0;
2159
2576
  var unsubscribe = subscribeScheduler(function (delta, now) {
@@ -2166,16 +2583,14 @@
2166
2583
  }, function () {
2167
2584
  return paused;
2168
2585
  });
2169
- emitter.emit('ready', {
2170
- preset: _objectSpread2({}, preset)
2586
+ nextTick(function () {
2587
+ if (disposed) return;
2588
+ emitter.emit('ready', {
2589
+ preset: _objectSpread2({}, preset)
2590
+ });
2171
2591
  });
2172
2592
  var syncDom = function syncDom() {
2173
- root.setAttribute('aria-label', interpolate(copy.progressAria, {
2174
- brand: copy.brandName,
2175
- code: preset.code,
2176
- name: preset.name
2177
- }));
2178
- renderCopy();
2593
+ updateAria();
2179
2594
  };
2180
2595
  return {
2181
2596
  element: root,
@@ -2197,8 +2612,11 @@
2197
2612
  },
2198
2613
  setPreset: function setPreset(ref) {
2199
2614
  var next = getPreset('progress', ref);
2615
+ dirty.preset = true;
2200
2616
  Object.assign(preset, next);
2201
- controller.preset = next;
2617
+ var nextColors = preset.colors.map(normalizeColor);
2618
+ if (nextColors.every(Boolean)) preset.colors = nextColors;
2619
+ controller.preset = preset;
2202
2620
  controller.profile = next.edgeStyle === 'tide' ? FLOW_PROFILES.tide : FLOW_PROFILES[next.id] || FLOW_PROFILES['visual-training'];
2203
2621
  controller.flowTime = stringSeed(next.id) * 31;
2204
2622
  controller.seed = stringSeed("".concat(next.id, "-reference")) * Math.PI * 2;
@@ -2208,7 +2626,7 @@
2208
2626
  overlay = attachProgressFlowOverlay({
2209
2627
  root: root,
2210
2628
  canvas: canvas,
2211
- preset: next,
2629
+ preset: preset,
2212
2630
  getProgress: function getProgress() {
2213
2631
  return controller.value;
2214
2632
  }
@@ -2217,14 +2635,74 @@
2217
2635
  controller.webglActive = Boolean(overlay);
2218
2636
  controller.resizeCanvas();
2219
2637
  emitter.emit('presetchange', {
2220
- preset: _objectSpread2({}, next)
2638
+ preset: _objectSpread2({}, preset)
2221
2639
  });
2222
2640
  return this;
2223
2641
  },
2642
+ setEdgeStyle: function setEdgeStyle(edgeStyle) {
2643
+ var value = String(edgeStyle || '').toLowerCase();
2644
+ if (value !== 'flow' && value !== 'tide') return this;
2645
+ dirty.edgeStyle = true;
2646
+ preset.edgeStyle = value;
2647
+ controller.profile = value === 'tide' ? FLOW_PROFILES.tide : FLOW_PROFILES[preset.id] || FLOW_PROFILES['visual-training'];
2648
+ if (overlay) {
2649
+ overlay.dispose();
2650
+ overlay = attachProgressFlowOverlay({
2651
+ root: root,
2652
+ canvas: canvas,
2653
+ preset: preset,
2654
+ getProgress: function getProgress() {
2655
+ return controller.value;
2656
+ }
2657
+ });
2658
+ }
2659
+ controller.webglActive = Boolean(overlay);
2660
+ controller.resizeCanvas();
2661
+ return this;
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
+ },
2224
2698
  setColors: function setColors(colors) {
2225
- if (!Array.isArray(colors) || colors.length !== 4) return this;
2226
- controller.setColors(colors);
2227
- 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);
2228
2706
  controller.resizeCanvas();
2229
2707
  return this;
2230
2708
  },
@@ -2254,12 +2732,16 @@
2254
2732
  return this;
2255
2733
  },
2256
2734
  dispose: function dispose() {
2735
+ disposed = true;
2257
2736
  unsubscribe();
2258
2737
  visibility.dispose();
2259
2738
  if (overlay) overlay.dispose();
2260
2739
  controller.dispose();
2261
2740
  root.remove();
2262
- }
2741
+ },
2742
+ textRatio: merged.textRatio,
2743
+ cssVars: merged.cssVars,
2744
+ dirty: dirty
2263
2745
  };
2264
2746
  }
2265
2747
 
@@ -2286,7 +2768,7 @@
2286
2768
  if (!host || typeof host.appendChild !== 'function') {
2287
2769
  throw new Error('createColorBackground: host element is required');
2288
2770
  }
2289
- var preset = _objectSpread2({}, getPreset('capsule', (_options$preset = options.preset) !== null && _options$preset !== void 0 ? _options$preset : 'NC-01'));
2771
+ var preset = _objectSpread2({}, getPreset('capsule', (_options$preset = options.preset) !== null && _options$preset !== void 0 ? _options$preset : '初光'));
2290
2772
  var merged = normalizeOptions({
2291
2773
  quality: DEFAULTS.capsule.quality,
2292
2774
  renderer: DEFAULTS.capsule.renderer,
@@ -2295,6 +2777,10 @@
2295
2777
  opacity: 1,
2296
2778
  fallbackColor: true
2297
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;
2298
2784
  var computed = getComputedStyle(host);
2299
2785
  if (computed.position === 'static' || computed.position === '') {
2300
2786
  host.style.position = 'relative';
@@ -2315,6 +2801,16 @@
2315
2801
  host.appendChild(layer);
2316
2802
  var emitter = createEmitter();
2317
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
+ };
2318
2814
  var renderer;
2319
2815
  var useWebgl = merged.renderer !== 'canvas2d';
2320
2816
  if (useWebgl) {
@@ -2326,8 +2822,11 @@
2326
2822
  });
2327
2823
  } catch (error) {
2328
2824
  renderer = new FallbackRenderer(canvas, merged);
2329
- emitter.emit('error', {
2330
- 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
+ });
2331
2830
  });
2332
2831
  }
2333
2832
  } else {
@@ -2345,8 +2844,11 @@
2345
2844
  }, function () {
2346
2845
  return paused;
2347
2846
  });
2348
- emitter.emit('ready', {
2349
- preset: _objectSpread2({}, preset)
2847
+ nextTick(function () {
2848
+ if (disposed) return;
2849
+ emitter.emit('ready', {
2850
+ preset: _objectSpread2({}, preset)
2851
+ });
2350
2852
  });
2351
2853
  return {
2352
2854
  element: host,
@@ -2357,24 +2859,59 @@
2357
2859
  off: emitter.off,
2358
2860
  setPreset: function setPreset(ref) {
2359
2861
  var next = getPreset('capsule', ref);
2862
+ dirty.preset = true;
2360
2863
  Object.assign(preset, next);
2361
- renderer.setPreset(next);
2864
+ var nextColors = preset.colors.map(normalizeColor);
2865
+ if (nextColors.every(Boolean)) preset.colors = nextColors;
2866
+ renderer.setPreset(_objectSpread2({}, preset));
2362
2867
  renderer.resize();
2363
2868
  syncLayerVars();
2364
2869
  emitter.emit('presetchange', {
2365
- preset: _objectSpread2({}, next)
2870
+ preset: _objectSpread2({}, preset)
2366
2871
  });
2367
2872
  return this;
2368
2873
  },
2369
2874
  setColors: function setColors(colors) {
2370
- if (!Array.isArray(colors) || colors.length !== 4) return this;
2371
- 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;
2372
2881
  renderer.setPreset(_objectSpread2(_objectSpread2({}, preset), {}, {
2373
- colors: colors
2882
+ colors: next
2374
2883
  }));
2375
2884
  syncLayerVars();
2376
2885
  return this;
2377
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
+ },
2378
2915
  setQuality: function setQuality(quality) {
2379
2916
  merged.quality = quality;
2380
2917
  if (typeof renderer.setDprCap === 'function') renderer.setDprCap(dprCapFor(quality));
@@ -2383,12 +2920,13 @@
2383
2920
  setOpacity: function setOpacity(value) {
2384
2921
  var opacity = Number(value);
2385
2922
  if (!Number.isFinite(opacity)) return this;
2923
+ dirty.opacity = true;
2386
2924
  merged.opacity = Math.min(1, Math.max(0, opacity));
2387
2925
  layer.style.setProperty('--dlc-color-opacity', String(merged.opacity));
2388
2926
  return this;
2389
2927
  },
2390
2928
  randomize: function randomize() {
2391
- renderer.randomize();
2929
+ this.setSeed(Math.random() * 100);
2392
2930
  return this;
2393
2931
  },
2394
2932
  pause: function pause() {
@@ -2400,12 +2938,17 @@
2400
2938
  return this;
2401
2939
  },
2402
2940
  dispose: function dispose() {
2941
+ disposed = true;
2403
2942
  unsubscribe();
2404
2943
  visibility.dispose();
2405
2944
  if (resizeObserver) resizeObserver.disconnect();else window.removeEventListener('resize', renderer.resize);
2406
2945
  renderer.dispose();
2407
2946
  layer.remove();
2408
- }
2947
+ },
2948
+ opacity: merged.opacity,
2949
+ fallbackColor: merged.fallbackColor,
2950
+ mouseColor: merged.mouseColor,
2951
+ dirty: dirty
2409
2952
  };
2410
2953
  }
2411
2954
 
@@ -2426,11 +2969,10 @@
2426
2969
  exports.getPreset = getPreset;
2427
2970
  exports.hexToRgb01 = hexToRgb01$1;
2428
2971
  exports.hexToRgba = hexToRgba;
2972
+ exports.normalizeColor = normalizeColor;
2429
2973
  exports.parseSize = parseSize;
2430
2974
  exports.pauseAll = pauseAll;
2431
2975
  exports.resumeAll = resumeAll;
2432
2976
  exports.validateColors = validateColors;
2433
- exports.validatePreset = validatePreset;
2434
- exports.validateProgressPreset = validateProgressPreset;
2435
2977
 
2436
2978
  }));