@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/README.md +5 -5
- package/docs/releases/0.3.5.md +7 -0
- package/lib/chrome/conversation-messages.js +22 -0
- package/lib/chrome/sidebar.d.ts +1 -1
- package/lib/chrome/sidebar.js +22 -15
- package/lib/chrome/tokens.d.ts +1 -1
- package/lib/chrome/tokens.js +4 -4
- package/lib/client.js +411 -55
- package/lib/metallic-brand-mark.js +2 -2
- package/lib/scene/character-expression.d.ts +4 -0
- package/lib/scene/character-expression.js +15 -0
- package/lib/scene/character-eye-material.d.ts +13 -0
- package/lib/scene/character-eye-material.js +88 -0
- package/lib/scene/character-eye-renderer.d.ts +5 -0
- package/lib/scene/character-eye-renderer.js +80 -0
- package/lib/scene/character-eye-rig.d.ts +20 -0
- package/lib/scene/character-eye-rig.js +35 -0
- package/lib/scene/character-gaze.d.ts +16 -0
- package/lib/scene/character-gaze.js +36 -0
- package/lib/scene/character-motion-clock.d.ts +1 -1
- package/lib/scene/character-motion-clock.js +17 -4
- package/lib/scene/character-motion.js +66 -12
- package/lib/scene/character-rig-renderer.d.ts +3 -2
- package/lib/scene/character-rig-renderer.js +28 -18
- package/lib/scene/character-rig.d.ts +2 -0
- package/lib/scene/character-rig.js +9 -1
- package/lib/sidebar-reveal.js +11 -3
- package/package.json +2 -1
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-
|
|
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-
|
|
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']
|
|
1600
|
-
#root [data-slot='sidebar'] > div:
|
|
1601
|
-
#root [data-slot='sidebar'] > div:
|
|
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']
|
|
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:
|
|
1614
|
-
#root [data-slot='sidebar'] > div:
|
|
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:
|
|
1626
|
-
#root [data-slot='sidebar'] > div:
|
|
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:
|
|
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:
|
|
1706
|
-
#root [data-slot='sidebar'] > div:
|
|
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 /
|
|
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:
|
|
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 /
|
|
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:
|
|
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 = () =>
|
|
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(
|
|
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,
|
|
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 /
|
|
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
|
-
|
|
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.
|
|
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
|
|
3890
|
-
rig.ears.
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
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 === "
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
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
|
-
|
|
3931
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
3975
|
-
|
|
3976
|
-
|
|
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
|
|
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
|
|
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) *
|
|
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) *
|
|
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
|