@lemoncat7/dsh-theme-xiaohei 0.3.3 → 0.3.5

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/lib/client.js CHANGED
@@ -850,6 +850,7 @@ var XIAOHEI_HEIXIU_OPEN = "data:image/webp;base64,UklGRtYUAABXRUJQVlA4WAoAAAASAA
850
850
  var XIAOHEI_HEIXIU_BLINK = "data:image/webp;base64,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";
851
851
 
852
852
  // src/chrome/conversation-messages.ts
853
+ var thinkingOnlyRow = `#root [data-chat-flow-kind='assistant-step']:has(> [data-slot='conversation.chat.node'] > div:first-child > div:only-child > div:only-child > [data-variant='think']:only-child)`;
853
854
  var XIAOHEI_CONVERSATION_MESSAGES_CSS = `
854
855
  /* Keep DSH's native max width; only tune vertical reading rhythm. */
855
856
  #root [data-chat-flow] {
@@ -860,6 +861,24 @@ var XIAOHEI_CONVERSATION_MESSAGES_CSS = `
860
861
  color: var(--xiaohei-conversation-text);
861
862
  }
862
863
 
864
+ ${thinkingOnlyRow} {
865
+ padding-inline: 0 !important;
866
+ }
867
+
868
+ ${thinkingOnlyRow}::before {
869
+ content: none !important;
870
+ display: none !important;
871
+ }
872
+
873
+ ${thinkingOnlyRow} > [data-slot='conversation.chat.node'] > div:first-child {
874
+ inline-size: 100% !important;
875
+ padding: 0 !important;
876
+ border: 0 !important;
877
+ border-radius: 0 !important;
878
+ background: transparent !important;
879
+ box-shadow: none !important;
880
+ }
881
+
863
882
  /* Message portraits are deliberately static and only identify the speaker. */
864
883
  #root [data-chat-flow-kind='assistant-step'],
865
884
  #root [data-chat-flow-kind='user'] {
@@ -1438,16 +1457,23 @@ var XIAOHEI_SIDEBAR_CSS = `
1438
1457
  position: relative;
1439
1458
  }
1440
1459
 
1460
+ /* Target the native control by its accessible identity, not DOM order.
1461
+ * Do not resurrect controls explicitly hidden/disabled by the Host. */
1462
+ #root [data-slot='sidebar'] button:is([aria-label='Collapse sidebar'], [aria-label='\u6536\u8D77\u4FA7\u8FB9\u680F']):not([hidden]):not([disabled]):not([aria-hidden='true']) {
1463
+ display: inline-flex !important;
1464
+ visibility: visible !important;
1465
+ opacity: 1 !important;
1466
+ pointer-events: auto !important;
1467
+ }
1468
+
1441
1469
  /* Reveal wide navigation only after its column has reached the target width.
1442
1470
  * Keep the toggle itself native. Opacity is applied to content, never the shell
1443
1471
  * (which also hosts fixed-position settings dialogs). */
1444
1472
  @media (prefers-reduced-motion: no-preference) {
1445
- #root [data-slot='sidebar'] > div[data-xiaohei-sidebar-reveal]:not([class*='_collapsed']) > :not(:first-child),
1446
- #root [data-slot='sidebar'] > div[data-xiaohei-sidebar-reveal]:not([class*='_collapsed']) > div:first-child > button:first-child {
1473
+ #root [data-xiaohei-sidebar-reveal] [data-xiaohei-sidebar-brand] {
1447
1474
  transition: opacity 100ms ease-out;
1448
1475
  }
1449
- #root [data-slot='sidebar'] > div[data-xiaohei-sidebar-reveal='waiting']:not([class*='_collapsed']) > :not(:first-child),
1450
- #root [data-slot='sidebar'] > div[data-xiaohei-sidebar-reveal='waiting']:not([class*='_collapsed']) > div:first-child > button:first-child {
1476
+ #root [data-xiaohei-sidebar-reveal='waiting'] [data-xiaohei-sidebar-brand] {
1451
1477
  opacity: 0 !important;
1452
1478
  visibility: hidden;
1453
1479
  transition: none;
@@ -1596,9 +1622,9 @@ html[data-xiaohei-sidebar-resizing] #dsh-theme-xiaohei\\/sidebar-glass {
1596
1622
  }
1597
1623
 
1598
1624
  /* Keep native rail/wide geometry and one consistent control material. */
1599
- #root [data-slot='sidebar'] > div:not([class*='_collapsed']) > div:first-child button:last-child,
1600
- #root [data-slot='sidebar'] > div:not([class*='_collapsed']) > button[aria-label='\u65B0\u5EFA\u4F1A\u8BDD'],
1601
- #root [data-slot='sidebar'] > div:not([class*='_collapsed']) > button[aria-label='New session'] {
1625
+ #root [data-slot='sidebar'] button:is([aria-label='Collapse sidebar'], [aria-label='\u6536\u8D77\u4FA7\u8FB9\u680F']),
1626
+ #root [data-slot='sidebar'] > div:has(button:is([aria-label='Collapse sidebar'], [aria-label='\u6536\u8D77\u4FA7\u8FB9\u680F'])) > button[aria-label='\u65B0\u5EFA\u4F1A\u8BDD'],
1627
+ #root [data-slot='sidebar'] > div:has(button:is([aria-label='Collapse sidebar'], [aria-label='\u6536\u8D77\u4FA7\u8FB9\u680F'])) > button[aria-label='New session'] {
1602
1628
  color: var(--xiaohei-sidebar-emphasis);
1603
1629
  transition:
1604
1630
  background-color var(--xiaohei-motion-fast) ease,
@@ -1606,12 +1632,12 @@ html[data-xiaohei-sidebar-resizing] #dsh-theme-xiaohei\\/sidebar-glass {
1606
1632
  transform var(--xiaohei-motion-fast) var(--xiaohei-motion-curve);
1607
1633
  }
1608
1634
 
1609
- #root [data-slot='sidebar'] > div:not([class*='_collapsed']) > div:first-child button:last-child:hover {
1635
+ #root [data-slot='sidebar'] button:is([aria-label='Collapse sidebar'], [aria-label='\u6536\u8D77\u4FA7\u8FB9\u680F']):hover {
1610
1636
  background: var(--xiaohei-sidebar-hover);
1611
1637
  }
1612
1638
 
1613
- #root [data-slot='sidebar'] > div:not([class*='_collapsed']) > button[aria-label='\u65B0\u5EFA\u4F1A\u8BDD'],
1614
- #root [data-slot='sidebar'] > div:not([class*='_collapsed']) > button[aria-label='New session'] {
1639
+ #root [data-slot='sidebar'] > div:has(button:is([aria-label='Collapse sidebar'], [aria-label='\u6536\u8D77\u4FA7\u8FB9\u680F'])) > button[aria-label='\u65B0\u5EFA\u4F1A\u8BDD'],
1640
+ #root [data-slot='sidebar'] > div:has(button:is([aria-label='Collapse sidebar'], [aria-label='\u6536\u8D77\u4FA7\u8FB9\u680F'])) > button[aria-label='New session'] {
1615
1641
  border: 1px solid var(--xiaohei-sidebar-control-edge);
1616
1642
  border-radius: var(--xiaohei-radius-control);
1617
1643
  background: var(--xiaohei-sidebar-control);
@@ -1622,14 +1648,14 @@ html[data-xiaohei-sidebar-resizing] #dsh-theme-xiaohei\\/sidebar-glass {
1622
1648
  text-align: start;
1623
1649
  }
1624
1650
 
1625
- #root [data-slot='sidebar'] > div:not([class*='_collapsed']) > button[aria-label='\u65B0\u5EFA\u4F1A\u8BDD']:hover,
1626
- #root [data-slot='sidebar'] > div:not([class*='_collapsed']) > button[aria-label='New session']:hover {
1651
+ #root [data-slot='sidebar'] > div:has(button:is([aria-label='Collapse sidebar'], [aria-label='\u6536\u8D77\u4FA7\u8FB9\u680F'])) > button[aria-label='\u65B0\u5EFA\u4F1A\u8BDD']:hover,
1652
+ #root [data-slot='sidebar'] > div:has(button:is([aria-label='Collapse sidebar'], [aria-label='\u6536\u8D77\u4FA7\u8FB9\u680F'])) > button[aria-label='New session']:hover {
1627
1653
  border-color: var(--xiaohei-sidebar-control-edge-hover);
1628
1654
  background: var(--xiaohei-sidebar-hover);
1629
1655
  }
1630
1656
 
1631
1657
  /* The footer uses a divider instead of another framed card. */
1632
- #root [data-slot='sidebar'] > div:not([class*='_collapsed'])
1658
+ #root [data-slot='sidebar'] > div:has(button:is([aria-label='Collapse sidebar'], [aria-label='\u6536\u8D77\u4FA7\u8FB9\u680F']))
1633
1659
  > :has(> div > [data-slot='sidebar.footer.action']) {
1634
1660
  box-sizing: border-box;
1635
1661
  gap: 2px;
@@ -1702,8 +1728,8 @@ html[data-xiaohei-sidebar-resizing] #dsh-theme-xiaohei\\/sidebar-glass {
1702
1728
  backdrop-filter: none;
1703
1729
  }
1704
1730
 
1705
- #root [data-slot='sidebar'] > div:not([class*='_collapsed']) > button[aria-label='\u65B0\u5EFA\u4F1A\u8BDD'],
1706
- #root [data-slot='sidebar'] > div:not([class*='_collapsed']) > button[aria-label='New session'] {
1731
+ #root [data-slot='sidebar'] > div:has(button:is([aria-label='Collapse sidebar'], [aria-label='\u6536\u8D77\u4FA7\u8FB9\u680F'])) > button[aria-label='\u65B0\u5EFA\u4F1A\u8BDD'],
1732
+ #root [data-slot='sidebar'] > div:has(button:is([aria-label='Collapse sidebar'], [aria-label='\u6536\u8D77\u4FA7\u8FB9\u680F'])) > button[aria-label='New session'] {
1707
1733
  border-color: ButtonText;
1708
1734
  }
1709
1735
 
@@ -1727,13 +1753,13 @@ html {
1727
1753
  --xiaohei-sidebar-emphasis: #E1E6E7;
1728
1754
  --xiaohei-sidebar-icon: rgb(220 227 229 / 70%);
1729
1755
  --xiaohei-sidebar-hover: rgb(231 238 240 / 6.5%);
1730
- --xiaohei-sidebar-glass-fill: rgb(18 22 25 / 48%);
1756
+ --xiaohei-sidebar-glass-fill: rgb(18 22 25 / 25%);
1731
1757
  --xiaohei-sidebar-glass-solid: #1B2023;
1732
1758
  --xiaohei-sidebar-glass-edge: rgb(255 255 255 / 8%);
1733
1759
  --xiaohei-sidebar-glass-highlight: rgb(255 255 255 / 13%);
1734
1760
  --xiaohei-sidebar-glass-refraction: rgb(255 255 255 / 18%);
1735
1761
  --xiaohei-sidebar-glass-saturation: 102%;
1736
- --xiaohei-sidebar-glass-blur: 18px;
1762
+ --xiaohei-sidebar-glass-blur: 12px;
1737
1763
  --xiaohei-sidebar-glass-radius: 18px;
1738
1764
  --xiaohei-sidebar-control: rgb(231 238 240 / 7%);
1739
1765
  --xiaohei-sidebar-control-edge: rgb(231 238 240 / 8%);
@@ -1841,13 +1867,13 @@ html[data-xiaohei-appearance='light'] {
1841
1867
  --xiaohei-sidebar-emphasis: #3F454C;
1842
1868
  --xiaohei-sidebar-icon: rgb(48 54 61 / 68%);
1843
1869
  --xiaohei-sidebar-hover: rgb(48 54 61 / 6%);
1844
- --xiaohei-sidebar-glass-fill: rgb(247 245 242 / 58%);
1870
+ --xiaohei-sidebar-glass-fill: rgb(247 245 242 / 30%);
1845
1871
  --xiaohei-sidebar-glass-solid: #EDF1F3;
1846
1872
  --xiaohei-sidebar-glass-edge: rgb(37 45 51 / 9%);
1847
1873
  --xiaohei-sidebar-glass-highlight: rgb(255 255 255 / 64%);
1848
1874
  --xiaohei-sidebar-glass-refraction: rgb(255 255 255 / 18%);
1849
1875
  --xiaohei-sidebar-glass-saturation: 102%;
1850
- --xiaohei-sidebar-glass-blur: 18px;
1876
+ --xiaohei-sidebar-glass-blur: 12px;
1851
1877
  --xiaohei-sidebar-control: rgb(255 255 255 / 30%);
1852
1878
  --xiaohei-sidebar-control-edge: rgb(46 51 58 / 8%);
1853
1879
  --xiaohei-sidebar-control-edge-hover: rgb(46 51 58 / 13%);
@@ -3421,7 +3447,13 @@ function createSidebarReveal(doc) {
3421
3447
  let observer;
3422
3448
  let phase = "", width = 0, waiting = false;
3423
3449
  const attribute = "data-xiaohei-sidebar-reveal";
3424
- const readPhase = () => shell?.className.includes("_collapsed") ? "rail" : shell?.className.includes("_fading") ? "closing" : "wide";
3450
+ const readPhase = () => {
3451
+ const state = shell?.closest("[data-sidebar-collapsed]")?.getAttribute("data-sidebar-collapsed");
3452
+ if (state === "true") return "rail";
3453
+ if (state === "false") return "wide";
3454
+ if (shell?.querySelector("button[aria-label='Collapse sidebar'], button[aria-label='\u6536\u8D77\u4FA7\u8FB9\u680F']")) return "wide";
3455
+ return "rail";
3456
+ };
3425
3457
  const apply3 = () => {
3426
3458
  if (!shell) return;
3427
3459
  const target = Number.parseFloat(shell.style.width);
@@ -3447,7 +3479,7 @@ function createSidebarReveal(doc) {
3447
3479
  phase = readPhase();
3448
3480
  if (!shell) return;
3449
3481
  observer = new doc.defaultView.MutationObserver(changed);
3450
- observer.observe(shell, { attributes: true, attributeFilter: ["class", "style"] });
3482
+ observer.observe(column, { subtree: true, childList: true, attributes: true, attributeFilter: ["data-sidebar-collapsed", "aria-label", "style"] });
3451
3483
  apply3();
3452
3484
  },
3453
3485
  resize(nextWidth) {
@@ -3719,8 +3751,9 @@ var CHARACTER_MOTION = { "peek": { "light": { "layers": { "ear0": { "box": [17.5
3719
3751
 
3720
3752
  // src/scene/character-motion-clock.ts
3721
3753
  function createCharacterMotionClock(clock, changed, random = Math.random) {
3722
- let timer, disposed = false, turn = 0, active = "";
3754
+ let timer, disposed = false, active = "";
3723
3755
  let clips;
3756
+ let blinksSinceEar = 0, nextEar = 0;
3724
3757
  const clear = () => {
3725
3758
  if (timer !== void 0) clock.clearTimeout(timer);
3726
3759
  timer = void 0;
@@ -3745,7 +3778,7 @@ function createCharacterMotionClock(clock, changed, random = Math.random) {
3745
3778
  rest();
3746
3779
  return;
3747
3780
  }
3748
- timer = clock.setTimeout(step, 1e3 / 30);
3781
+ timer = clock.setTimeout(step, 1e3 / 60);
3749
3782
  };
3750
3783
  step();
3751
3784
  }
@@ -3753,7 +3786,11 @@ function createCharacterMotionClock(clock, changed, random = Math.random) {
3753
3786
  timer = void 0;
3754
3787
  if (!clips || disposed) return;
3755
3788
  const gestures = Object.keys(clips).filter((name) => name !== "blink" && name !== "attention");
3756
- play(turn++ % 2 === 0 && clips.blink || !gestures.length ? "blink" : gestures[Math.floor(random() * gestures.length)]);
3789
+ const ears = gestures.filter((name) => name.startsWith("ear"));
3790
+ const action = ears.length && blinksSinceEar >= 2 ? ears[nextEar++ % ears.length] : random() < 0.72 && clips.blink || !gestures.length ? "blink" : gestures[Math.floor(random() * gestures.length)];
3791
+ if (action.startsWith("ear")) blinksSinceEar = 0;
3792
+ else blinksSinceEar++;
3793
+ play(action);
3757
3794
  }
3758
3795
  return {
3759
3796
  setClips(next) {
@@ -3761,6 +3798,8 @@ function createCharacterMotionClock(clock, changed, random = Math.random) {
3761
3798
  clear();
3762
3799
  clips = next;
3763
3800
  active = "";
3801
+ blinksSinceEar = 0;
3802
+ nextEar = 0;
3764
3803
  changed(void 0, 0);
3765
3804
  if (clips) timer = clock.setTimeout(start, 1200 + random() * 800);
3766
3805
  },
@@ -3776,6 +3815,206 @@ function createCharacterMotionClock(clock, changed, random = Math.random) {
3776
3815
  };
3777
3816
  }
3778
3817
 
3818
+ // src/scene/character-expression.ts
3819
+ var smooth = (t) => {
3820
+ const x = Math.max(0, Math.min(1, t));
3821
+ return x * x * (3 - 2 * x);
3822
+ };
3823
+ function blinkClosure(progress) {
3824
+ if (progress <= 0 || progress >= 1) return 0;
3825
+ if (progress < 0.3) return smooth(progress / 0.3);
3826
+ if (progress < 0.4) return 1;
3827
+ return 1 - smooth((progress - 0.4) / 0.6);
3828
+ }
3829
+ function attentionBlink(progress) {
3830
+ return blinkClosure((progress - 0.12) / 0.16);
3831
+ }
3832
+
3833
+ // src/scene/character-eye-rig.ts
3834
+ var EYE_RIGS = {
3835
+ seated: [
3836
+ { left: [285, 223], right: [326, 224], upper: [302, 188], lower: [310, 276], iris: [309, 230, 13, 20, 0], skin: [315, 258] },
3837
+ { left: [373, 221], right: [421, 226], upper: [387, 186], lower: [393, 277], iris: [393, 228, 15, 22, 0], skin: [391, 261] }
3838
+ ],
3839
+ peek: [
3840
+ { left: [80, 214], right: [119, 221], upper: [103, 186], lower: [91, 267], iris: [102, 225, 14, 20, 0.25], skin: [110, 253] },
3841
+ { left: [171, 252], right: [210, 265], upper: [195, 221], lower: [175, 300], iris: [186, 260, 15, 20, 0.4], skin: [186, 288] }
3842
+ ],
3843
+ chin: [
3844
+ { left: [252, 252], right: [290, 247], upper: [266, 223], lower: [287, 297], iris: [278, 258, 14, 19, -0.3], skin: [291, 285] },
3845
+ { left: [339, 234], right: [387, 227], upper: [358, 196], lower: [365, 280], iris: [361, 235, 15, 21, -0.25], skin: [367, 269] }
3846
+ ],
3847
+ halfpeek: [
3848
+ { left: [89, 390], right: [159, 395], upper: [116, 337], lower: [110, 471], iris: [116, 401, 23, 30, 0.22], skin: [117, 445] }
3849
+ ]
3850
+ };
3851
+ function clampGaze(x, y) {
3852
+ if (!Number.isFinite(x) || !Number.isFinite(y)) return [0, 0];
3853
+ const length = Math.max(1, Math.hypot(x, y));
3854
+ return [x / length, y / length];
3855
+ }
3856
+ function eyelidControls(eye2, closure) {
3857
+ const t = Math.max(0, Math.min(1, closure));
3858
+ const cornerY = (eye2.left[1] + eye2.right[1]) / 2;
3859
+ const drop = (eye2.lower[1] - cornerY) * 0.24;
3860
+ const middle = [(eye2.left[0] + eye2.right[0]) / 2, cornerY + drop + (eye2.right[0] - eye2.left[0]) * 0.18];
3861
+ const lerp2 = (p) => [p[0] + (middle[0] - p[0]) * t, p[1] + (middle[1] - p[1]) * t];
3862
+ return { upper: lerp2(eye2.upper), lower: lerp2(eye2.lower), left: [eye2.left[0], eye2.left[1] + drop * t], right: [eye2.right[0], eye2.right[1] + drop * t] };
3863
+ }
3864
+
3865
+ // src/scene/character-eye-material.ts
3866
+ function lidY(eye2, control, x) {
3867
+ let low = 0, high = 1;
3868
+ for (let i = 0; i < 14; i++) {
3869
+ const t2 = (low + high) / 2, s2 = 1 - t2;
3870
+ if (s2 * s2 * eye2.left[0] + 2 * s2 * t2 * control[0] + t2 * t2 * eye2.right[0] < x) low = t2;
3871
+ else high = t2;
3872
+ }
3873
+ const t = (low + high) / 2, s = 1 - t;
3874
+ return s * s * eye2.left[1] + 2 * s * t * control[1] + t * t * eye2.right[1];
3875
+ }
3876
+ function prepareEyeMaterial(doc, source, eye2) {
3877
+ const left = Math.floor(eye2.left[0] - 9), right = Math.ceil(eye2.right[0] + 9);
3878
+ const top = Math.floor(Math.min(eye2.upper[1], eye2.left[1], eye2.right[1]) - 5);
3879
+ const bottom = Math.ceil(Math.max(eye2.lower[1], eye2.left[1], eye2.right[1]) + 5);
3880
+ const width = right - left, height = bottom - top;
3881
+ const canvas = () => {
3882
+ const c = doc.createElement("canvas");
3883
+ c.width = width;
3884
+ c.height = height;
3885
+ return c;
3886
+ };
3887
+ const ink = canvas(), skin = canvas(), mask = canvas();
3888
+ const ic = ink.getContext("2d"), sc = skin.getContext("2d"), mc = mask.getContext("2d");
3889
+ const pixels = source.getImageData(0, 0, source.canvas.width, source.canvas.height);
3890
+ const pixel = (x, y) => {
3891
+ const offset = (Math.max(0, Math.min(pixels.height - 1, Math.round(y / 2))) * pixels.width + Math.max(0, Math.min(pixels.width - 1, Math.round(x / 2)))) * 4;
3892
+ return [pixels.data[offset], pixels.data[offset + 1], pixels.data[offset + 2]];
3893
+ };
3894
+ const ref = pixel(...eye2.skin), output = ic.createImageData(width, height), plate = sc.createImageData(width, height);
3895
+ const isSkin = (c) => c.every((v, i) => Math.abs(v - ref[i]) < 25);
3896
+ const skinAt = (x, y, c) => {
3897
+ if (isSkin(c)) return c;
3898
+ const sum = [ref[0] * 0.05, ref[1] * 0.05, ref[2] * 0.05];
3899
+ let weight = 0.05;
3900
+ for (const radius of [6, 12, 20, 30]) for (let i = 0; i < 8; i++) {
3901
+ const p = pixel(x + Math.cos(i * Math.PI / 4) * radius, y + Math.sin(i * Math.PI / 4) * radius);
3902
+ if (!isSkin(p)) continue;
3903
+ const w = 1 / radius;
3904
+ weight += w;
3905
+ for (let k = 0; k < 3; k++) sum[k] += p[k] * w;
3906
+ }
3907
+ return sum.map((v) => Math.round(v / weight));
3908
+ };
3909
+ for (let x = 0; x < width; x++) {
3910
+ const ax = left + x, upper = lidY(eye2, eye2.upper, ax);
3911
+ for (let y = 0; y < height; y++) {
3912
+ const ay = top + y, c = pixel(ax, ay), index = (y * width + x) * 4;
3913
+ const darkness = 1 - (c[0] + c[1] + c[2]) / Math.max(1, ref[0] + ref[1] + ref[2]);
3914
+ const green = c[1] > c[0] * 1.15 && c[1] > c[2] * 1.12;
3915
+ const inBand = ay >= upper - 12 && ay <= upper + 3;
3916
+ const alpha = inBand && !green ? Math.max(0, Math.min(1, (darkness - 0.3) / 0.35)) : 0;
3917
+ output.data.set([...c, Math.round(alpha * 255)], index);
3918
+ plate.data.set([...skinAt(ax, ay, c), 255], index);
3919
+ }
3920
+ }
3921
+ ic.putImageData(output, 0, 0);
3922
+ sc.putImageData(plate, 0, 0);
3923
+ const inkMask = mc.createImageData(width, height);
3924
+ for (let i = 0; i < output.data.length; i += 4) inkMask.data[i + 3] = output.data[i + 3] > 4 ? 255 : 0;
3925
+ mc.putImageData(inkMask, 0, 0);
3926
+ const expanded = canvas(), ec = expanded.getContext("2d");
3927
+ for (const [dx, dy] of [[0, 0], [-1, 0], [1, 0], [0, -1], [0, 1]]) ec.drawImage(mask, dx, dy);
3928
+ mc.drawImage(expanded, 0, 0);
3929
+ mc.translate(-left, -top);
3930
+ mc.beginPath();
3931
+ mc.moveTo(eye2.left[0] - 7, eye2.left[1]);
3932
+ mc.quadraticCurveTo(eye2.upper[0], eye2.upper[1] - 4, eye2.right[0] + 7, eye2.right[1]);
3933
+ mc.quadraticCurveTo(eye2.lower[0], eye2.lower[1] + 20, eye2.left[0] - 7, eye2.left[1]);
3934
+ mc.closePath();
3935
+ mc.fill();
3936
+ sc.globalCompositeOperation = "destination-in";
3937
+ sc.filter = "blur(0.65px)";
3938
+ sc.drawImage(mask, 0, 0);
3939
+ sc.filter = "none";
3940
+ return { ink, skin, left, top, width, height };
3941
+ }
3942
+
3943
+ // src/scene/character-eye-renderer.ts
3944
+ function createEyeRenderer(ctx, body, eyes) {
3945
+ const doc = ctx.canvas.ownerDocument;
3946
+ const source = doc.createElement("canvas");
3947
+ source.width = body.width;
3948
+ source.height = body.height;
3949
+ const sample = source.getContext("2d", { willReadFrequently: true });
3950
+ sample.drawImage(body, 0, 0);
3951
+ const color = (p) => {
3952
+ const data = sample.getImageData(Math.round(p[0] / 2), Math.round(p[1] / 2), 1, 1).data;
3953
+ return [data[0], data[1], data[2]];
3954
+ };
3955
+ const rgb = (c) => `rgb(${c.map((v) => Math.round(v)).join(" ")})`;
3956
+ const prepared = eyes.map((eye2) => {
3957
+ const [cx, cy, rx, ry, angle] = eye2.iris;
3958
+ const iris = doc.createElement("canvas");
3959
+ iris.width = Math.ceil(rx * 2 + 4);
3960
+ iris.height = Math.ceil(ry * 2 + 4);
3961
+ const ic = iris.getContext("2d");
3962
+ ic.translate(iris.width / 2, iris.height / 2);
3963
+ ic.rotate(angle);
3964
+ ic.beginPath();
3965
+ ic.ellipse(0, 0, rx, ry, 0, 0, Math.PI * 2);
3966
+ ic.clip();
3967
+ ic.rotate(-angle);
3968
+ ic.drawImage(body, 0, 0, body.width, body.height, -cx, -cy, body.width * 2, body.height * 2);
3969
+ return { eye: eye2, iris, skin: color(eye2.skin), material: prepareEyeMaterial(doc, sample, eye2) };
3970
+ });
3971
+ function aperture(eye2, closure) {
3972
+ const { upper, lower, left, right } = eyelidControls(eye2, closure);
3973
+ ctx.beginPath();
3974
+ ctx.moveTo(...left);
3975
+ ctx.quadraticCurveTo(...upper, ...right);
3976
+ ctx.quadraticCurveTo(...lower, ...left);
3977
+ ctx.closePath();
3978
+ }
3979
+ return (gaze, closure) => {
3980
+ const [gx, gy] = clampGaze(...gaze);
3981
+ ctx.save();
3982
+ ctx.scale(0.5, 0.5);
3983
+ for (const { eye: eye2, iris, skin, material } of prepared) {
3984
+ const [cx, cy, rx, ry] = eye2.iris;
3985
+ ctx.save();
3986
+ ctx.drawImage(material.skin, material.left, material.top);
3987
+ ctx.save();
3988
+ aperture(eye2, 0);
3989
+ ctx.clip();
3990
+ if (closure < 0.999) {
3991
+ ctx.save();
3992
+ aperture(eye2, closure);
3993
+ ctx.clip();
3994
+ const shade = ctx.createLinearGradient(0, cy - ry, 0, cy + ry);
3995
+ const brightness = (skin[0] + skin[1] + skin[2]) / 765;
3996
+ const white = brightness > 0.65 ? [255, 253, 241] : [166, 185, 193];
3997
+ shade.addColorStop(0, rgb(white.map((c) => c * 0.83)));
3998
+ shade.addColorStop(0.5, rgb(white));
3999
+ ctx.fillStyle = shade;
4000
+ ctx.fillRect(cx - 70, cy - 70, 140, 140);
4001
+ const dx = gx * rx * 0.26, dy = gy * ry * 0.13;
4002
+ ctx.drawImage(iris, cx - iris.width / 2 + dx, cy - iris.height / 2 + dy);
4003
+ ctx.restore();
4004
+ }
4005
+ ctx.restore();
4006
+ const lids = eyelidControls(eye2, closure);
4007
+ for (let x = 0; x < material.width; x++) {
4008
+ const ax = material.left + x;
4009
+ const dy = lidY({ ...eye2, left: lids.left, right: lids.right }, lids.upper, ax) - lidY(eye2, eye2.upper, ax);
4010
+ ctx.drawImage(material.ink, x, 0, 1, material.height, ax, material.top + dy, 1, material.height);
4011
+ }
4012
+ ctx.restore();
4013
+ }
4014
+ ctx.restore();
4015
+ };
4016
+ }
4017
+
3779
4018
  // src/scene/character-rig.ts
3780
4019
  var TAIL_JOINTS = [
3781
4020
  [284, 328],
@@ -3786,6 +4025,12 @@ var TAIL_JOINTS = [
3786
4025
  [307, 448],
3787
4026
  [319, 464]
3788
4027
  ];
4028
+ function earOffset(y, top, root, angle) {
4029
+ const span = Math.max(1, root - top);
4030
+ const weight = Math.max(0, Math.min(1, (root - y) / span));
4031
+ if (weight === 0) return 0;
4032
+ return Math.sin(angle) * Math.min(20, span * 0.55) * weight * weight;
4033
+ }
3789
4034
  function solveBones(rest, angles) {
3790
4035
  return rest.reduce((out, p, i) => {
3791
4036
  const parent = out[i - 1];
@@ -3826,11 +4071,13 @@ function skinVertex(v, joints, rest = TAIL_JOINTS) {
3826
4071
  function rigAngles(progress, count, tail) {
3827
4072
  const t = Math.max(0, Math.min(1, progress));
3828
4073
  const envelope = Math.sin(Math.PI * t) ** 2;
3829
- return Array.from({ length: count }, (_, i) => envelope * (tail ? i ? 0.12 : 0 : 0.22) * Math.sin(t * Math.PI * (tail ? 3 : 4) - i * (tail ? 0.65 : 1.1)));
4074
+ return Array.from({ length: count }, (_, i) => envelope * (tail ? i ? 0.12 : 0 : 0.32) * Math.sin(t * Math.PI * (tail ? 3 : 4) - i * (tail ? 0.65 : 1.1)));
3830
4075
  }
3831
4076
 
3832
4077
  // src/scene/character-rig-renderer.ts
3833
- function createRigRenderer(ctx, rig, images) {
4078
+ function createRigRenderer(ctx, rig, images, eyes = []) {
4079
+ const body = images.get(rig.layers.body.src);
4080
+ const drawEyes = body ? createEyeRenderer(ctx, body, eyes) : void 0;
3834
4081
  const tail = rig.layers.tail;
3835
4082
  const vertices = [], triangles = [];
3836
4083
  if (tail) {
@@ -3884,24 +4131,67 @@ function createRigRenderer(ctx, rig, images) {
3884
4131
  ctx.restore();
3885
4132
  }
3886
4133
  }
3887
- return (action, progress) => {
4134
+ return (action, progress, gaze = [0, 0]) => {
3888
4135
  ctx.clearRect(0, 0, rig.size[0], rig.size[1]);
3889
- const ears = rigAngles(progress, rig.ears.length, false);
3890
- rig.ears.forEach(([x, y], i) => {
3891
- ctx.save();
3892
- ctx.translate(x, y);
3893
- ctx.rotate(action === "attention" || action === "ear" + i ? ears[i] : 0);
3894
- ctx.translate(-x, -y);
3895
- layer("ear" + i);
3896
- ctx.restore();
4136
+ const earProgress = action === "attention" ? Math.max(0, Math.min(1, (progress - 0.03) / 0.65)) : progress;
4137
+ const ears = rigAngles(earProgress, rig.ears.length, false);
4138
+ rig.ears.forEach(([, root], i) => {
4139
+ const part = rig.layers["ear" + i], image = part && images.get(part.src);
4140
+ if (!part || !image) return;
4141
+ const angle = action === "attention" || action === "ear" + i ? ears[i] : 0;
4142
+ if (Math.abs(angle) < 1e-8) {
4143
+ layer("ear" + i);
4144
+ return;
4145
+ }
4146
+ const [x, top, right, bottom] = part.box;
4147
+ for (let row = 0; row < image.height; row++) {
4148
+ const y = top + row * (bottom - top) / image.height;
4149
+ ctx.drawImage(image, 0, row, image.width, 1, x + earOffset(y, top, root, angle), y, right - x, (bottom - top) / image.height);
4150
+ }
3897
4151
  });
3898
4152
  layer("body");
3899
- drawTail(action === "tail" || action === "attention" ? progress : 0);
3900
- if (action === "blink") {
3901
- ctx.save();
3902
- ctx.globalAlpha = Math.min(1, Math.sin(Math.PI * progress) * 2);
3903
- layer("blink");
3904
- ctx.restore();
4153
+ drawTail(action === "attention" ? Math.max(0, Math.min(1, (progress - 0.22) / 0.78)) : action === "tail" ? progress : 0);
4154
+ const closure = action === "blink" ? blinkClosure(progress) : action === "attention" ? attentionBlink(progress) : 0;
4155
+ drawEyes?.(gaze, closure);
4156
+ };
4157
+ }
4158
+
4159
+ // src/scene/character-gaze.ts
4160
+ function createCharacterGaze(clock, changed) {
4161
+ let value = [0, 0], target = [0, 0], frame, last = 0, disposed = false;
4162
+ function step(now) {
4163
+ frame = void 0;
4164
+ if (disposed) return;
4165
+ const dt = Math.min(64, Math.max(0, now - last));
4166
+ last = now;
4167
+ const amount = 1 - Math.exp(-dt / 95);
4168
+ value = [value[0] + (target[0] - value[0]) * amount, value[1] + (target[1] - value[1]) * amount];
4169
+ const settled = Math.hypot(value[0] - target[0], value[1] - target[1]) < 1e-3;
4170
+ if (settled) value = target;
4171
+ changed(value);
4172
+ if (!settled) frame = clock.requestAnimationFrame(step);
4173
+ }
4174
+ function stop() {
4175
+ if (frame !== void 0) clock.cancelAnimationFrame(frame);
4176
+ frame = void 0;
4177
+ }
4178
+ return {
4179
+ target(x, y) {
4180
+ if (disposed) return;
4181
+ target = clampGaze(x, y);
4182
+ if (frame === void 0 && Math.hypot(value[0] - target[0], value[1] - target[1]) >= 1e-3) {
4183
+ last = clock.performance.now();
4184
+ frame = clock.requestAnimationFrame(step);
4185
+ }
4186
+ },
4187
+ reset() {
4188
+ stop();
4189
+ target = value = [0, 0];
4190
+ changed(value);
4191
+ },
4192
+ dispose() {
4193
+ disposed = true;
4194
+ stop();
3905
4195
  }
3906
4196
  };
3907
4197
  }
@@ -3913,6 +4203,23 @@ function createCharacterMotion(doc) {
3913
4203
  let disposed = false, part, rig;
3914
4204
  let canvas, render;
3915
4205
  let pose2 = "", appearance = "", revision = 0, nearby = false, lastPointer = 0;
4206
+ let attentionTimer, lastAttention = -Infinity;
4207
+ let gazePoint = [0, 0], activeAction, activeProgress = 0;
4208
+ let bounds;
4209
+ const paint = () => {
4210
+ if (render && part) {
4211
+ render(activeAction, activeProgress, gazePoint);
4212
+ part.dataset.motion = activeAction ?? "rig-rest";
4213
+ }
4214
+ };
4215
+ const gaze = createCharacterGaze(win, (point) => {
4216
+ gazePoint = point;
4217
+ paint();
4218
+ });
4219
+ const cancelAttention = () => {
4220
+ if (attentionTimer !== void 0) win.clearTimeout(attentionTimer);
4221
+ attentionTimer = void 0;
4222
+ };
3916
4223
  function decode(src) {
3917
4224
  if (loading.has(src)) return loading.get(src);
3918
4225
  if (images.has(src)) return Promise.resolve();
@@ -3927,8 +4234,9 @@ function createCharacterMotion(doc) {
3927
4234
  }
3928
4235
  const clock = createCharacterMotionClock(win, (action, progress) => {
3929
4236
  if (!canvas || !part || !render) return;
3930
- render(action, progress);
3931
- part.dataset.motion = action ?? "rig-rest";
4237
+ activeAction = action;
4238
+ activeProgress = progress;
4239
+ paint();
3932
4240
  });
3933
4241
  function update() {
3934
4242
  if (disposed) return;
@@ -3936,8 +4244,13 @@ function createCharacterMotion(doc) {
3936
4244
  const moving = doc.documentElement.hasAttribute("data-xiaohei-sidebar-resizing");
3937
4245
  const next = !doc.hidden && !reduced.matches && !moving && part ? CHARACTER_MOTION[pose2]?.[mode] : void 0;
3938
4246
  if (next === rig && appearance === mode) return;
3939
- clock.setClips(void 0);
4247
+ cancelAttention();
3940
4248
  render = void 0;
4249
+ clock.setClips(void 0);
4250
+ gaze.reset();
4251
+ bounds = void 0;
4252
+ activeAction = void 0;
4253
+ activeProgress = 0;
3941
4254
  const current = ++revision;
3942
4255
  canvas?.remove();
3943
4256
  canvas = void 0;
@@ -3959,9 +4272,9 @@ function createCharacterMotion(doc) {
3959
4272
  return;
3960
4273
  }
3961
4274
  currentPart.append(canvas);
3962
- render = createRigRenderer(ctx, next, images);
4275
+ render = createRigRenderer(ctx, next, images, EYE_RIGS[pose2]);
3963
4276
  clock.setClips({
3964
- blink: { duration: 330 },
4277
+ blink: { duration: 280 },
3965
4278
  ...Object.fromEntries(next.ears.map((_, i) => ["ear" + i, { duration: 1050 }])),
3966
4279
  ...next.layers.tail ? { tail: { duration: 2400 } } : {},
3967
4280
  attention: { duration: 2400 }
@@ -3971,14 +4284,48 @@ function createCharacterMotion(doc) {
3971
4284
  const pointer = (event) => {
3972
4285
  if (!part || !render || event.pointerType !== "mouse") return;
3973
4286
  const now = win.performance.now();
3974
- if (now - lastPointer < 100) return;
3975
- lastPointer = now;
3976
- const rect = part.getBoundingClientRect();
4287
+ if (!bounds || now - lastPointer >= 100) {
4288
+ bounds = part.getBoundingClientRect();
4289
+ lastPointer = now;
4290
+ }
4291
+ const rect = bounds, eyes = EYE_RIGS[pose2];
4292
+ if (rig && eyes?.length) {
4293
+ const scale = Math.min(rect.width / rig.size[0], rect.height / rig.size[1]);
4294
+ const eyeX = eyes.reduce((sum, eye2) => sum + eye2.iris[0], 0) / eyes.length / 2;
4295
+ const eyeY = eyes.reduce((sum, eye2) => sum + eye2.iris[1], 0) / eyes.length / 2;
4296
+ const cx = rect.left + eyeX * scale;
4297
+ const cy = rect.top + (rect.height - rig.size[1] * scale) / 2 + eyeY * scale;
4298
+ gaze.target((event.clientX - cx) / Math.max(220, win.innerWidth * 0.32), (event.clientY - cy) / Math.max(180, win.innerHeight * 0.32));
4299
+ }
3977
4300
  const near = event.clientX >= rect.left - 70 && event.clientX <= rect.right + 70 && event.clientY >= rect.top - 50 && event.clientY <= rect.bottom + 50;
3978
- if (near && !nearby) clock.attention();
4301
+ if (!near) cancelAttention();
4302
+ if (near && !nearby && now - lastAttention > 6e3) {
4303
+ const current = revision;
4304
+ attentionTimer = win.setTimeout(() => {
4305
+ attentionTimer = void 0;
4306
+ if (!disposed && nearby && current === revision) {
4307
+ lastAttention = win.performance.now();
4308
+ clock.attention();
4309
+ }
4310
+ }, 180);
4311
+ }
3979
4312
  nearby = near;
3980
4313
  };
3981
4314
  reduced.addEventListener("change", update);
4315
+ const neutral = () => {
4316
+ gaze.target(0, 0);
4317
+ nearby = false;
4318
+ cancelAttention();
4319
+ };
4320
+ const leave = (event) => {
4321
+ if (!event.relatedTarget) neutral();
4322
+ };
4323
+ const resize = () => {
4324
+ bounds = void 0;
4325
+ };
4326
+ doc.addEventListener("pointerout", leave);
4327
+ win.addEventListener("blur", neutral);
4328
+ win.addEventListener("resize", resize);
3982
4329
  doc.addEventListener("visibilitychange", update);
3983
4330
  doc.addEventListener("pointermove", pointer, { passive: true });
3984
4331
  const observer = new win.MutationObserver(update);
@@ -3986,9 +4333,12 @@ function createCharacterMotion(doc) {
3986
4333
  return {
3987
4334
  setPose(nextPart, nextPose) {
3988
4335
  if (nextPart !== part || pose2 !== nextPose) {
4336
+ cancelAttention();
3989
4337
  revision++;
3990
- clock.setClips(void 0);
3991
4338
  render = void 0;
4339
+ clock.setClips(void 0);
4340
+ gaze.reset();
4341
+ bounds = void 0;
3992
4342
  canvas?.remove();
3993
4343
  canvas = void 0;
3994
4344
  rig = void 0;
@@ -4000,8 +4350,10 @@ function createCharacterMotion(doc) {
4000
4350
  },
4001
4351
  dispose() {
4002
4352
  disposed = true;
4353
+ cancelAttention();
4003
4354
  revision++;
4004
4355
  clock.dispose();
4356
+ gaze.dispose();
4005
4357
  canvas?.remove();
4006
4358
  render = void 0;
4007
4359
  images.clear();
@@ -4011,6 +4363,9 @@ function createCharacterMotion(doc) {
4011
4363
  observer.disconnect();
4012
4364
  doc.removeEventListener("visibilitychange", update);
4013
4365
  doc.removeEventListener("pointermove", pointer);
4366
+ doc.removeEventListener("pointerout", leave);
4367
+ win.removeEventListener("blur", neutral);
4368
+ win.removeEventListener("resize", resize);
4014
4369
  }
4015
4370
  };
4016
4371
  }
@@ -5862,7 +6217,7 @@ var HEIXIU_FACE = {
5862
6217
  var HEIXIU_FOOT = Math.max(...HEIXIU_PROFILE.map((r, i) => r * Math.sin(i / HEIXIU_PROFILE.length * Math.PI * 2))) * 100;
5863
6218
  var HEIXIU_REST = { x: 0, y: 0, sx: 1, sy: 1, roll: 0, lid: 1, cheer: 0 };
5864
6219
  var HEIXIU_REACTIONS = ["greet", "hop", "wiggle"];
5865
- var smooth = (x) => {
6220
+ var smooth2 = (x) => {
5866
6221
  const k = Math.max(0, Math.min(1, x));
5867
6222
  return k * k * (3 - 2 * k);
5868
6223
  };
@@ -5898,7 +6253,7 @@ var HeixiuReactions = class {
5898
6253
  this.aim(0, time);
5899
6254
  }
5900
6255
  aim(direction, time) {
5901
- this.hover = this.hoverFrom + (this.hoverTo - this.hoverFrom) * smooth((time - this.hoverAt) / 0.28);
6256
+ this.hover = this.hoverFrom + (this.hoverTo - this.hoverFrom) * smooth2((time - this.hoverAt) / 0.28);
5902
6257
  this.hoverFrom = this.hover;
5903
6258
  this.hoverTo = Math.max(-1, Math.min(1, direction));
5904
6259
  this.hoverAt = time;
@@ -5954,7 +6309,7 @@ var HeixiuReactions = class {
5954
6309
  }
5955
6310
  sample(time) {
5956
6311
  const out = this.track(time);
5957
- this.hover = this.hoverFrom + (this.hoverTo - this.hoverFrom) * smooth((time - this.hoverAt) / 0.28);
6312
+ this.hover = this.hoverFrom + (this.hoverTo - this.hoverFrom) * smooth2((time - this.hoverAt) / 0.28);
5958
6313
  out.roll += this.hover * 5;
5959
6314
  out.x += this.hover * 4;
5960
6315
  return out;
@@ -7247,6 +7602,7 @@ function XiaoheiMetallicBrandMark({ size, className }) {
7247
7602
  {
7248
7603
  className: className === void 0 ? "xiaohei-brand-mark" : `${className} xiaohei-brand-mark`,
7249
7604
  "data-brand-context": context,
7605
+ "data-xiaohei-sidebar-brand": context === "sidebar" ? "" : void 0,
7250
7606
  "data-metallic-ready": ready || snapshot !== void 0 ? "true" : "false",
7251
7607
  style: geometry,
7252
7608
  "aria-hidden": "true",
@@ -7258,7 +7614,7 @@ function XiaoheiMetallicBrandMark({ size, className }) {
7258
7614
  );
7259
7615
  }
7260
7616
  function XiaoheiBrandName() {
7261
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "xiaohei-brand-name", children: "\u7F57\u5C0F\u9ED1 \xB7 \u5996\u7075\u4F1A\u9986" });
7617
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "xiaohei-brand-name", "data-xiaohei-sidebar-brand": "", children: "\u7F57\u5C0F\u9ED1 \xB7 \u5996\u7075\u4F1A\u9986" });
7262
7618
  }
7263
7619
 
7264
7620
  // src/plugin.ts