@lemoncat7/dsh-theme-xiaohei 0.4.6 → 0.5.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/README.md +15 -9
- package/THIRD_PARTY_NOTICES.md +13 -1
- package/docs/dsh-021-compatibility.md +23 -0
- package/docs/releases/0.5.0.md +24 -0
- package/docs/rift-interaction.md +35 -0
- package/docs/rift-preview.md +39 -0
- package/lib/client.companion.js +3 -4
- package/lib/client.decorations.js +224 -51
- package/lib/client.js +534 -2405
- package/lib/index.d.ts +1 -2
- package/lib/index.js +395 -11
- package/lib/rift/boot.d.ts +6 -0
- package/package.json +29 -19
- package/lib/appearance.d.ts +0 -12
- package/lib/appearance.js +0 -42
- package/lib/blink.d.ts +0 -4
- package/lib/blink.js +0 -110
- package/lib/bloub-heixiu/ambient.d.ts +0 -3
- package/lib/bloub-heixiu/ambient.js +0 -12
- package/lib/bloub-heixiu/character.d.ts +0 -33
- package/lib/bloub-heixiu/character.js +0 -124
- package/lib/bloub-heixiu/effects.d.ts +0 -4
- package/lib/bloub-heixiu/effects.js +0 -91
- package/lib/bloub-heixiu/flight.d.ts +0 -15
- package/lib/bloub-heixiu/flight.js +0 -66
- package/lib/bloub-heixiu/follow.d.ts +0 -13
- package/lib/bloub-heixiu/follow.js +0 -23
- package/lib/bloub-heixiu/layout.d.ts +0 -34
- package/lib/bloub-heixiu/layout.js +0 -26
- package/lib/bloub-heixiu/renderer.d.ts +0 -16
- package/lib/bloub-heixiu/renderer.js +0 -94
- package/lib/bloub-heixiu/runtime.d.ts +0 -6
- package/lib/bloub-heixiu/runtime.js +0 -440
- package/lib/bloub-heixiu/signals.d.ts +0 -17
- package/lib/bloub-heixiu/signals.js +0 -55
- package/lib/bloub-heixiu/styles.d.ts +0 -2
- package/lib/bloub-heixiu/styles.js +0 -98
- package/lib/boot-appearance.d.ts +0 -10
- package/lib/boot-appearance.js +0 -47
- package/lib/boot-loader.d.ts +0 -5
- package/lib/boot-loader.js +0 -158
- package/lib/chrome/accessibility.d.ts +0 -3
- package/lib/chrome/accessibility.js +0 -69
- package/lib/chrome/blink.d.ts +0 -3
- package/lib/chrome/blink.js +0 -15
- package/lib/chrome/composer-avatar.d.ts +0 -10
- package/lib/chrome/composer-avatar.js +0 -599
- package/lib/chrome/composer-send-heixiu.d.ts +0 -3
- package/lib/chrome/composer-send-heixiu.js +0 -143
- package/lib/chrome/composer.d.ts +0 -3
- package/lib/chrome/composer.js +0 -135
- package/lib/chrome/controls.d.ts +0 -8
- package/lib/chrome/controls.js +0 -348
- package/lib/chrome/conversation-messages.d.ts +0 -3
- package/lib/chrome/conversation-messages.js +0 -329
- package/lib/chrome/conversation-submissions.d.ts +0 -3
- package/lib/chrome/conversation-submissions.js +0 -68
- package/lib/chrome/conversation.d.ts +0 -3
- package/lib/chrome/conversation.js +0 -67
- package/lib/chrome/frames.d.ts +0 -6
- package/lib/chrome/frames.js +0 -134
- package/lib/chrome/gaze.d.ts +0 -3
- package/lib/chrome/gaze.js +0 -103
- package/lib/chrome/identity.d.ts +0 -6
- package/lib/chrome/identity.js +0 -47
- package/lib/chrome/overlays.d.ts +0 -3
- package/lib/chrome/overlays.js +0 -104
- package/lib/chrome/portal.d.ts +0 -6
- package/lib/chrome/portal.js +0 -169
- package/lib/chrome/reactions.d.ts +0 -3
- package/lib/chrome/reactions.js +0 -85
- package/lib/chrome/sidebar.d.ts +0 -3
- package/lib/chrome/sidebar.js +0 -313
- package/lib/chrome/tokens.d.ts +0 -3
- package/lib/chrome/tokens.js +0 -224
- package/lib/chrome/workspace.d.ts +0 -3
- package/lib/chrome/workspace.js +0 -94
- package/lib/chrome.d.ts +0 -10
- package/lib/chrome.js +0 -46
- package/lib/client-entry.d.ts +0 -3
- package/lib/client-entry.js +0 -3
- package/lib/client.d.ts +0 -10
- package/lib/client.pose-chin-dark.js +0 -4
- package/lib/client.pose-chin-light.js +0 -4
- package/lib/client.pose-doze-dark.js +0 -4
- package/lib/client.pose-doze-light.js +0 -4
- package/lib/client.pose-halfpeek-dark.js +0 -4
- package/lib/client.pose-halfpeek-light.js +0 -4
- package/lib/client.pose-peek-dark.js +0 -4
- package/lib/client.pose-peek-light.js +0 -4
- package/lib/client.pose-seated-dark.js +0 -4
- package/lib/client.pose-seated-light.js +0 -4
- package/lib/client.rig-chin-dark.js +0 -4
- package/lib/client.rig-chin-light.js +0 -4
- package/lib/client.rig-halfpeek-dark.js +0 -4
- package/lib/client.rig-halfpeek-light.js +0 -4
- package/lib/client.rig-peek-dark.js +0 -4
- package/lib/client.rig-peek-light.js +0 -4
- package/lib/client.rig-seated-dark.js +0 -4
- package/lib/client.rig-seated-light.js +0 -4
- package/lib/companion-entry.d.ts +0 -3
- package/lib/companion-entry.js +0 -3
- package/lib/composer-avatar.d.ts +0 -18
- package/lib/composer-avatar.js +0 -441
- package/lib/composer-draft-preview.d.ts +0 -4
- package/lib/composer-draft-preview.js +0 -8
- package/lib/composer-send-heixiu.d.ts +0 -8
- package/lib/composer-send-heixiu.js +0 -133
- package/lib/current-session.d.ts +0 -6
- package/lib/current-session.js +0 -5
- package/lib/decorations-entry.d.ts +0 -3
- package/lib/decorations-entry.js +0 -18
- package/lib/deferred-companion.d.ts +0 -5
- package/lib/deferred-companion.js +0 -9
- package/lib/deferred-decorations.d.ts +0 -3
- package/lib/deferred-decorations.js +0 -51
- package/lib/gaze.d.ts +0 -4
- package/lib/gaze.js +0 -230
- package/lib/generated-backgrounds.d.ts +0 -14
- package/lib/generated-backgrounds.js +0 -14
- package/lib/generated-character-motion.d.ts +0 -4
- package/lib/generated-character-motion.js +0 -2
- package/lib/generated-identity.d.ts +0 -6
- package/lib/generated-identity.js +0 -6
- package/lib/generated-keyart.d.ts +0 -34
- package/lib/generated-keyart.js +0 -34
- package/lib/generated-loading-assets.d.ts +0 -4
- package/lib/generated-loading-assets.js +0 -4
- package/lib/heixiu-interactions.d.ts +0 -10
- package/lib/heixiu-interactions.js +0 -161
- package/lib/host-contract.d.ts +0 -9
- package/lib/host-contract.js +0 -13
- package/lib/host-dom.d.ts +0 -11
- package/lib/host-dom.js +0 -72
- package/lib/loading-heixiu.d.ts +0 -8
- package/lib/loading-heixiu.js +0 -287
- package/lib/metallic-brand-mark.d.ts +0 -10
- package/lib/metallic-brand-mark.js +0 -296
- package/lib/plugin.d.ts +0 -6
- package/lib/plugin.js +0 -60
- package/lib/portal.d.ts +0 -40
- package/lib/portal.js +0 -225
- package/lib/reactions.d.ts +0 -20
- package/lib/reactions.js +0 -380
- package/lib/scene/avatar-model.d.ts +0 -6
- package/lib/scene/avatar-model.js +0 -646
- package/lib/scene/avatar-motion.d.ts +0 -3
- package/lib/scene/avatar-motion.js +0 -799
- package/lib/scene/character-assets.d.ts +0 -13
- package/lib/scene/character-assets.js +0 -26
- package/lib/scene/character-expression.d.ts +0 -4
- package/lib/scene/character-expression.js +0 -15
- package/lib/scene/character-eye-material.d.ts +0 -13
- package/lib/scene/character-eye-material.js +0 -88
- package/lib/scene/character-eye-renderer.d.ts +0 -5
- package/lib/scene/character-eye-renderer.js +0 -80
- package/lib/scene/character-eye-rig.d.ts +0 -20
- package/lib/scene/character-eye-rig.js +0 -35
- package/lib/scene/character-gaze.d.ts +0 -16
- package/lib/scene/character-gaze.js +0 -36
- package/lib/scene/character-idle.d.ts +0 -17
- package/lib/scene/character-idle.js +0 -65
- package/lib/scene/character-layout.d.ts +0 -24
- package/lib/scene/character-layout.js +0 -51
- package/lib/scene/character-motion-clock.d.ts +0 -18
- package/lib/scene/character-motion-clock.js +0 -70
- package/lib/scene/character-motion-types.d.ts +0 -12
- package/lib/scene/character-motion-types.js +0 -2
- package/lib/scene/character-motion.d.ts +0 -6
- package/lib/scene/character-motion.js +0 -187
- package/lib/scene/character-poses.d.ts +0 -26
- package/lib/scene/character-poses.js +0 -11
- package/lib/scene/character-rig-renderer.d.ts +0 -5
- package/lib/scene/character-rig-renderer.js +0 -95
- package/lib/scene/character-rig.d.ts +0 -22
- package/lib/scene/character-rig.js +0 -61
- package/lib/scene/heixiu-feedback.d.ts +0 -3
- package/lib/scene/heixiu-feedback.js +0 -64
- package/lib/scene/heixiu-interactions.d.ts +0 -3
- package/lib/scene/heixiu-interactions.js +0 -92
- package/lib/scene/ids.d.ts +0 -5
- package/lib/scene/ids.js +0 -5
- package/lib/scene/pointer-bridge.d.ts +0 -21
- package/lib/scene/pointer-bridge.js +0 -155
- package/lib/scene/runtime.d.ts +0 -12
- package/lib/scene/runtime.js +0 -74
- package/lib/scene/state-transitions.d.ts +0 -3
- package/lib/scene/state-transitions.js +0 -37
- package/lib/scene/styles.d.ts +0 -5
- package/lib/scene/styles.js +0 -69
- package/lib/scene/sylva-performance.d.ts +0 -11
- package/lib/scene/sylva-performance.js +0 -58
- package/lib/scene/wallpaper-character-styles.d.ts +0 -3
- package/lib/scene/wallpaper-character-styles.js +0 -49
- package/lib/scene/wallpaper-character.d.ts +0 -5
- package/lib/scene/wallpaper-character.js +0 -222
- package/lib/scene.d.ts +0 -8
- package/lib/scene.js +0 -8
- package/lib/sidebar-glass.d.ts +0 -16
- package/lib/sidebar-glass.js +0 -168
- package/lib/sidebar-heixiu-escape.d.ts +0 -44
- package/lib/sidebar-heixiu-escape.js +0 -340
- package/lib/sidebar-heixiu-roaming.d.ts +0 -27
- package/lib/sidebar-heixiu-roaming.js +0 -310
- package/lib/sidebar-reveal.d.ts +0 -8
- package/lib/sidebar-reveal.js +0 -59
- package/lib/state.d.ts +0 -21
- package/lib/state.js +0 -123
- package/lib/theme.d.ts +0 -19
- package/lib/theme.js +0 -188
- package/lib/vendor/bloub/decor.d.ts +0 -114
- package/lib/vendor/bloub/decor.js +0 -198
- package/lib/vendor/bloub/engine.d.ts +0 -213
- package/lib/vendor/bloub/engine.js +0 -452
- package/lib/vendor/bloub/expressions.d.ts +0 -35
- package/lib/vendor/bloub/expressions.js +0 -143
- package/lib/vendor/bloub/eyefit.d.ts +0 -75
- package/lib/vendor/bloub/eyefit.js +0 -393
- package/lib/vendor/bloub/face.d.ts +0 -63
- package/lib/vendor/bloub/face.js +0 -106
- package/lib/vendor/bloub/math.d.ts +0 -22
- package/lib/vendor/bloub/math.js +0 -36
- package/lib/vendor/bloub/profiles.d.ts +0 -8
- package/lib/vendor/bloub/profiles.js +0 -20
- package/lib/vendor/bloub/repere.d.ts +0 -30
- package/lib/vendor/bloub/repere.js +0 -32
- package/lib/vendor/bloub/shape.d.ts +0 -80
- package/lib/vendor/bloub/shape.js +0 -268
- package/lib/vendor/bloub/skins.d.ts +0 -36
- package/lib/vendor/bloub/skins.js +0 -74
- package/lib/vendor/bloub/states.d.ts +0 -87
- package/lib/vendor/bloub/states.js +0 -468
package/lib/reactions.js
DELETED
|
@@ -1,380 +0,0 @@
|
|
|
1
|
-
import { XIAOHEI_IDLE_EAR_LEFT, XIAOHEI_IDLE_EAR_RIGHT, XIAOHEI_IDLE_TAIL, } from './generated-keyart.js';
|
|
2
|
-
import { XIAOHEI_REACTION_CSS } from './chrome/reactions.js';
|
|
3
|
-
import { subscribeXiaoheiHostDom } from './host-dom.js';
|
|
4
|
-
import { XIAOHEI_HEIXIU_GREETING_EVENT, } from './heixiu-interactions.js';
|
|
5
|
-
import { XIAOHEI_PORTAL_PROXIMITY_EVENT, } from './portal.js';
|
|
6
|
-
export const XIAOHEI_REACTION_STYLE_ID = 'dsh-theme-xiaohei/reaction-style';
|
|
7
|
-
const EAR_DELAY_MS = 190;
|
|
8
|
-
const HEIXIU_EAR_DELAY_MS = 180;
|
|
9
|
-
const HEIXIU_INTERACTION_DURATION_MS = 800;
|
|
10
|
-
const EAR_DURATION_MS = 560;
|
|
11
|
-
const TAIL_SLOW_DURATION_MS = 1280;
|
|
12
|
-
const TAIL_COMPLETE_DURATION_MS = 700;
|
|
13
|
-
const IDLE_TAIL_MIN_DELAY_MS = 12_000;
|
|
14
|
-
const IDLE_TAIL_DELAY_RANGE_MS = 12_000;
|
|
15
|
-
const LEFT_EAR_CENTER_X = 84 / 256;
|
|
16
|
-
const LEFT_EAR_CENTER_Y = 62 / 256;
|
|
17
|
-
const RIGHT_EAR_CENTER_X = 166 / 256;
|
|
18
|
-
const RIGHT_EAR_CENTER_Y = 82 / 256;
|
|
19
|
-
const EAR_POINTER_ENTER_RADIUS_X = 34 / 256;
|
|
20
|
-
const EAR_POINTER_ENTER_RADIUS_Y = 42 / 256;
|
|
21
|
-
const EAR_POINTER_EXIT_RADIUS_X = 40 / 256;
|
|
22
|
-
const EAR_POINTER_EXIT_RADIUS_Y = 48 / 256;
|
|
23
|
-
/** Resolve a pointer only inside the two local, non-overlapping ear regions. */
|
|
24
|
-
export function resolveXiaoheiPointerEar(bounds, target, activeEar) {
|
|
25
|
-
if (bounds.width <= 0 || bounds.height <= 0)
|
|
26
|
-
return undefined;
|
|
27
|
-
const normalizedDistance = (ear, radiusX, radiusY) => {
|
|
28
|
-
const centerX = ear === 'ear-left' ? LEFT_EAR_CENTER_X : RIGHT_EAR_CENTER_X;
|
|
29
|
-
const centerY = ear === 'ear-left' ? LEFT_EAR_CENTER_Y : RIGHT_EAR_CENTER_Y;
|
|
30
|
-
const dx = (target.x - (bounds.left + bounds.width * centerX)) / (bounds.width * radiusX);
|
|
31
|
-
const dy = (target.y - (bounds.top + bounds.height * centerY)) / (bounds.height * radiusY);
|
|
32
|
-
return Math.hypot(dx, dy);
|
|
33
|
-
};
|
|
34
|
-
const entering = ['ear-left', 'ear-right']
|
|
35
|
-
.map((ear) => ({
|
|
36
|
-
ear,
|
|
37
|
-
distance: normalizedDistance(ear, EAR_POINTER_ENTER_RADIUS_X, EAR_POINTER_ENTER_RADIUS_Y),
|
|
38
|
-
}))
|
|
39
|
-
.filter(({ distance }) => distance <= 1)
|
|
40
|
-
.sort((left, right) => left.distance - right.distance)[0];
|
|
41
|
-
if (entering !== undefined)
|
|
42
|
-
return entering.ear;
|
|
43
|
-
if (activeEar !== undefined
|
|
44
|
-
&& normalizedDistance(activeEar, EAR_POINTER_EXIT_RADIUS_X, EAR_POINTER_EXIT_RADIUS_Y) <= 1) {
|
|
45
|
-
return activeEar;
|
|
46
|
-
}
|
|
47
|
-
return undefined;
|
|
48
|
-
}
|
|
49
|
-
/** Resolve the next sparse tail visit; exported to keep the timing contract testable. */
|
|
50
|
-
export function resolveXiaoheiIdleTailDelay(randomValue = Math.random()) {
|
|
51
|
-
const normalized = Math.min(1, Math.max(0, randomValue));
|
|
52
|
-
return IDLE_TAIL_MIN_DELAY_MS + Math.round(normalized * IDLE_TAIL_DELAY_RANGE_MS);
|
|
53
|
-
}
|
|
54
|
-
/** Install proximity ear motion and sparse idle tails without a JavaScript frame loop. */
|
|
55
|
-
export function installXiaoheiIdleReactions(doc = typeof document === 'undefined' ? undefined : document) {
|
|
56
|
-
if (doc?.body === undefined || doc.defaultView === null)
|
|
57
|
-
return () => { };
|
|
58
|
-
const win = doc.defaultView;
|
|
59
|
-
doc.getElementById(XIAOHEI_REACTION_STYLE_ID)?.remove();
|
|
60
|
-
const style = doc.createElement('style');
|
|
61
|
-
style.id = XIAOHEI_REACTION_STYLE_ID;
|
|
62
|
-
style.textContent = XIAOHEI_REACTION_CSS;
|
|
63
|
-
doc.head.append(style);
|
|
64
|
-
const reducedMotion = win.matchMedia('(prefers-reduced-motion: reduce)');
|
|
65
|
-
const coarsePointer = win.matchMedia('(hover: none) and (pointer: coarse)');
|
|
66
|
-
const preloads = [XIAOHEI_IDLE_EAR_LEFT, XIAOHEI_IDLE_EAR_RIGHT, XIAOHEI_IDLE_TAIL]
|
|
67
|
-
.map((source) => {
|
|
68
|
-
const image = new win.Image();
|
|
69
|
-
image.decoding = 'async';
|
|
70
|
-
image.src = source;
|
|
71
|
-
return image;
|
|
72
|
-
});
|
|
73
|
-
let disposed = false;
|
|
74
|
-
let mascot;
|
|
75
|
-
let reactionLayer;
|
|
76
|
-
let pointer;
|
|
77
|
-
let pointerEar;
|
|
78
|
-
let previousState = currentState(doc);
|
|
79
|
-
let reactionTimer = 0;
|
|
80
|
-
let pendingEarTimer = 0;
|
|
81
|
-
let heixiuEarTimer = 0;
|
|
82
|
-
let heixiuInteractionTimer = 0;
|
|
83
|
-
let idleTailTimer = 0;
|
|
84
|
-
const behaviorDisabled = () => reducedMotion.matches || coarsePointer.matches;
|
|
85
|
-
const isIdle = () => currentState(doc) === 'idle' && doc.visibilityState !== 'hidden';
|
|
86
|
-
const clearTimer = (timer) => {
|
|
87
|
-
if (timer !== 0)
|
|
88
|
-
win.clearTimeout(timer);
|
|
89
|
-
};
|
|
90
|
-
const clearPendingEar = () => {
|
|
91
|
-
clearTimer(pendingEarTimer);
|
|
92
|
-
pendingEarTimer = 0;
|
|
93
|
-
};
|
|
94
|
-
const clearHeixiuEar = () => {
|
|
95
|
-
clearTimer(heixiuEarTimer);
|
|
96
|
-
heixiuEarTimer = 0;
|
|
97
|
-
};
|
|
98
|
-
const clearHeixiuInteraction = () => {
|
|
99
|
-
clearTimer(heixiuInteractionTimer);
|
|
100
|
-
heixiuInteractionTimer = 0;
|
|
101
|
-
mascot?.removeAttribute('data-xiaohei-heixiu-interaction');
|
|
102
|
-
};
|
|
103
|
-
const clearReaction = () => {
|
|
104
|
-
clearTimer(reactionTimer);
|
|
105
|
-
reactionTimer = 0;
|
|
106
|
-
mascot?.removeAttribute('data-xiaohei-reaction');
|
|
107
|
-
mascot?.removeAttribute('data-xiaohei-ear-loop');
|
|
108
|
-
reactionLayer?.removeAttribute('data-reaction');
|
|
109
|
-
};
|
|
110
|
-
const sourceFor = (reaction) => {
|
|
111
|
-
if (reaction === 'ear-left')
|
|
112
|
-
return XIAOHEI_IDLE_EAR_LEFT;
|
|
113
|
-
if (reaction === 'ear-right')
|
|
114
|
-
return XIAOHEI_IDLE_EAR_RIGHT;
|
|
115
|
-
return XIAOHEI_IDLE_TAIL;
|
|
116
|
-
};
|
|
117
|
-
const play = (reaction, durationMs) => {
|
|
118
|
-
if (disposed || behaviorDisabled() || !isIdle() || mascot === undefined || reactionLayer === undefined) {
|
|
119
|
-
return false;
|
|
120
|
-
}
|
|
121
|
-
if (mascot.hasAttribute('data-xiaohei-reaction'))
|
|
122
|
-
return false;
|
|
123
|
-
reactionLayer.src = sourceFor(reaction);
|
|
124
|
-
reactionLayer.dataset.reaction = reaction;
|
|
125
|
-
mascot.dataset.xiaoheiReaction = reaction;
|
|
126
|
-
reactionTimer = win.setTimeout(() => {
|
|
127
|
-
reactionTimer = 0;
|
|
128
|
-
mascot?.removeAttribute('data-xiaohei-reaction');
|
|
129
|
-
reactionLayer?.removeAttribute('data-reaction');
|
|
130
|
-
}, durationMs + 40);
|
|
131
|
-
return true;
|
|
132
|
-
};
|
|
133
|
-
const playEar = (ear) => {
|
|
134
|
-
play(ear, EAR_DURATION_MS);
|
|
135
|
-
};
|
|
136
|
-
const setEarFrame = (ear) => {
|
|
137
|
-
if (mascot === undefined || reactionLayer === undefined)
|
|
138
|
-
return;
|
|
139
|
-
reactionLayer.src = sourceFor(ear);
|
|
140
|
-
reactionLayer.dataset.reaction = ear;
|
|
141
|
-
mascot.dataset.xiaoheiReaction = ear;
|
|
142
|
-
};
|
|
143
|
-
const stopPointerEarLoop = () => {
|
|
144
|
-
clearPendingEar();
|
|
145
|
-
if (mascot?.dataset.xiaoheiEarLoop === 'true')
|
|
146
|
-
clearReaction();
|
|
147
|
-
};
|
|
148
|
-
const attach = () => {
|
|
149
|
-
const nextMascot = doc.querySelector('.xiaohei-scene__mascot') ?? undefined;
|
|
150
|
-
if (nextMascot === mascot && reactionLayer?.isConnected === true)
|
|
151
|
-
return;
|
|
152
|
-
clearReaction();
|
|
153
|
-
reactionLayer?.remove();
|
|
154
|
-
reactionLayer = undefined;
|
|
155
|
-
mascot = nextMascot;
|
|
156
|
-
const viewport = nextMascot?.querySelector('.xiaohei-scene__mascot-idle-viewport');
|
|
157
|
-
if (viewport === null || viewport === undefined)
|
|
158
|
-
return;
|
|
159
|
-
const layer = doc.createElement('img');
|
|
160
|
-
layer.className = 'xiaohei-scene__idle-reaction';
|
|
161
|
-
layer.alt = '';
|
|
162
|
-
layer.decoding = 'async';
|
|
163
|
-
layer.fetchPriority = 'low';
|
|
164
|
-
viewport.append(layer);
|
|
165
|
-
reactionLayer = layer;
|
|
166
|
-
};
|
|
167
|
-
const earToward = (target) => {
|
|
168
|
-
if (mascot === undefined)
|
|
169
|
-
return undefined;
|
|
170
|
-
const rect = mascot.getBoundingClientRect();
|
|
171
|
-
if (rect.width <= 0 || rect.height <= 0)
|
|
172
|
-
return undefined;
|
|
173
|
-
const leftEar = {
|
|
174
|
-
x: rect.left + rect.width * LEFT_EAR_CENTER_X,
|
|
175
|
-
y: rect.top + rect.height * LEFT_EAR_CENTER_Y,
|
|
176
|
-
};
|
|
177
|
-
const rightEar = {
|
|
178
|
-
x: rect.left + rect.width * RIGHT_EAR_CENTER_X,
|
|
179
|
-
y: rect.top + rect.height * RIGHT_EAR_CENTER_Y,
|
|
180
|
-
};
|
|
181
|
-
const leftDistance = Math.hypot(target.x - leftEar.x, target.y - leftEar.y);
|
|
182
|
-
const rightDistance = Math.hypot(target.x - rightEar.x, target.y - rightEar.y);
|
|
183
|
-
return leftDistance <= rightDistance ? 'ear-left' : 'ear-right';
|
|
184
|
-
};
|
|
185
|
-
const startPointerEarLoop = () => {
|
|
186
|
-
if (pointerEar === undefined || behaviorDisabled() || !isIdle())
|
|
187
|
-
return;
|
|
188
|
-
clearHeixiuEar();
|
|
189
|
-
clearHeixiuInteraction();
|
|
190
|
-
clearReaction();
|
|
191
|
-
setEarFrame(pointerEar);
|
|
192
|
-
if (mascot !== undefined)
|
|
193
|
-
mascot.dataset.xiaoheiEarLoop = 'true';
|
|
194
|
-
};
|
|
195
|
-
const schedulePointerEarLoop = () => {
|
|
196
|
-
if (pendingEarTimer !== 0 || mascot?.dataset.xiaoheiEarLoop === 'true')
|
|
197
|
-
return;
|
|
198
|
-
if (pointerEar === undefined || behaviorDisabled() || !isIdle())
|
|
199
|
-
return;
|
|
200
|
-
pendingEarTimer = win.setTimeout(() => {
|
|
201
|
-
pendingEarTimer = 0;
|
|
202
|
-
startPointerEarLoop();
|
|
203
|
-
}, EAR_DELAY_MS);
|
|
204
|
-
};
|
|
205
|
-
const refreshPointerEarDirection = () => {
|
|
206
|
-
if (mascot?.dataset.xiaoheiEarLoop !== 'true' || pointerEar === undefined)
|
|
207
|
-
return;
|
|
208
|
-
if (mascot.dataset.xiaoheiReaction === pointerEar)
|
|
209
|
-
return;
|
|
210
|
-
setEarFrame(pointerEar);
|
|
211
|
-
};
|
|
212
|
-
const syncPointerEarLoop = () => {
|
|
213
|
-
const rect = mascot?.getBoundingClientRect();
|
|
214
|
-
pointerEar = pointer !== undefined && rect !== undefined
|
|
215
|
-
? resolveXiaoheiPointerEar(rect, pointer, pointerEar)
|
|
216
|
-
: undefined;
|
|
217
|
-
if (pointerEar !== undefined) {
|
|
218
|
-
schedulePointerEarLoop();
|
|
219
|
-
refreshPointerEarDirection();
|
|
220
|
-
}
|
|
221
|
-
else {
|
|
222
|
-
stopPointerEarLoop();
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
const scheduleIdleTail = () => {
|
|
226
|
-
clearTimer(idleTailTimer);
|
|
227
|
-
idleTailTimer = 0;
|
|
228
|
-
if (disposed || behaviorDisabled() || !isIdle())
|
|
229
|
-
return;
|
|
230
|
-
const delay = resolveXiaoheiIdleTailDelay();
|
|
231
|
-
idleTailTimer = win.setTimeout(() => {
|
|
232
|
-
idleTailTimer = 0;
|
|
233
|
-
play('tail-slow', TAIL_SLOW_DURATION_MS);
|
|
234
|
-
scheduleIdleTail();
|
|
235
|
-
}, delay);
|
|
236
|
-
};
|
|
237
|
-
const onPointerMove = (event) => {
|
|
238
|
-
if (event.pointerType === 'touch') {
|
|
239
|
-
pointer = undefined;
|
|
240
|
-
pointerEar = undefined;
|
|
241
|
-
stopPointerEarLoop();
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
pointer = { x: event.clientX, y: event.clientY };
|
|
245
|
-
syncPointerEarLoop();
|
|
246
|
-
};
|
|
247
|
-
const clearPointer = () => {
|
|
248
|
-
pointer = undefined;
|
|
249
|
-
pointerEar = undefined;
|
|
250
|
-
stopPointerEarLoop();
|
|
251
|
-
};
|
|
252
|
-
const onPortalProximity = (event) => {
|
|
253
|
-
const detail = event.detail;
|
|
254
|
-
const active = Boolean(detail?.active);
|
|
255
|
-
if (!active) {
|
|
256
|
-
clearHeixiuEar();
|
|
257
|
-
clearHeixiuInteraction();
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
if (pointerEar !== undefined || !isIdle() || detail.target === undefined || mascot === undefined)
|
|
261
|
-
return;
|
|
262
|
-
startHeixiuInteraction(detail.target, false);
|
|
263
|
-
};
|
|
264
|
-
const startHeixiuInteraction = (target, supersedePointer) => {
|
|
265
|
-
if (!isIdle() || behaviorDisabled() || mascot === undefined)
|
|
266
|
-
return;
|
|
267
|
-
if (pointerEar !== undefined && !supersedePointer)
|
|
268
|
-
return;
|
|
269
|
-
if (supersedePointer)
|
|
270
|
-
stopPointerEarLoop();
|
|
271
|
-
clearHeixiuInteraction();
|
|
272
|
-
mascot.dataset.xiaoheiHeixiuInteraction = 'true';
|
|
273
|
-
heixiuInteractionTimer = win.setTimeout(() => {
|
|
274
|
-
heixiuInteractionTimer = 0;
|
|
275
|
-
mascot?.removeAttribute('data-xiaohei-heixiu-interaction');
|
|
276
|
-
syncPointerEarLoop();
|
|
277
|
-
}, HEIXIU_INTERACTION_DURATION_MS);
|
|
278
|
-
clearHeixiuEar();
|
|
279
|
-
heixiuEarTimer = win.setTimeout(() => {
|
|
280
|
-
heixiuEarTimer = 0;
|
|
281
|
-
const ear = earToward(target);
|
|
282
|
-
if (ear !== undefined)
|
|
283
|
-
playEar(ear);
|
|
284
|
-
}, HEIXIU_EAR_DELAY_MS);
|
|
285
|
-
};
|
|
286
|
-
const onHeixiuGreeting = (event) => {
|
|
287
|
-
const detail = event.detail;
|
|
288
|
-
if (detail?.target === undefined)
|
|
289
|
-
return;
|
|
290
|
-
startHeixiuInteraction(detail.target, true);
|
|
291
|
-
};
|
|
292
|
-
const onStateChange = () => {
|
|
293
|
-
const nextState = currentState(doc);
|
|
294
|
-
if (nextState === previousState)
|
|
295
|
-
return;
|
|
296
|
-
const cameFromComplete = previousState === 'complete' && nextState === 'idle';
|
|
297
|
-
previousState = nextState;
|
|
298
|
-
clearPendingEar();
|
|
299
|
-
clearHeixiuEar();
|
|
300
|
-
clearHeixiuInteraction();
|
|
301
|
-
clearTimer(idleTailTimer);
|
|
302
|
-
idleTailTimer = 0;
|
|
303
|
-
if (nextState !== 'idle') {
|
|
304
|
-
clearReaction();
|
|
305
|
-
pointerEar = undefined;
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
if (cameFromComplete)
|
|
309
|
-
play('tail-complete', TAIL_COMPLETE_DURATION_MS);
|
|
310
|
-
scheduleIdleTail();
|
|
311
|
-
syncPointerEarLoop();
|
|
312
|
-
};
|
|
313
|
-
const onVisibilityChange = () => {
|
|
314
|
-
clearPendingEar();
|
|
315
|
-
clearHeixiuEar();
|
|
316
|
-
clearHeixiuInteraction();
|
|
317
|
-
clearTimer(idleTailTimer);
|
|
318
|
-
idleTailTimer = 0;
|
|
319
|
-
if (doc.visibilityState === 'hidden') {
|
|
320
|
-
pointerEar = undefined;
|
|
321
|
-
clearReaction();
|
|
322
|
-
}
|
|
323
|
-
else {
|
|
324
|
-
scheduleIdleTail();
|
|
325
|
-
syncPointerEarLoop();
|
|
326
|
-
}
|
|
327
|
-
};
|
|
328
|
-
const onPreferenceChange = () => {
|
|
329
|
-
clearPendingEar();
|
|
330
|
-
clearHeixiuEar();
|
|
331
|
-
clearHeixiuInteraction();
|
|
332
|
-
clearReaction();
|
|
333
|
-
scheduleIdleTail();
|
|
334
|
-
syncPointerEarLoop();
|
|
335
|
-
};
|
|
336
|
-
const unsubscribeHostDom = subscribeXiaoheiHostDom(doc, attach);
|
|
337
|
-
const stateObserver = new win.MutationObserver(onStateChange);
|
|
338
|
-
stateObserver.observe(doc.documentElement, {
|
|
339
|
-
attributes: true,
|
|
340
|
-
attributeFilter: ['data-xiaohei-state'],
|
|
341
|
-
});
|
|
342
|
-
doc.addEventListener('pointermove', onPointerMove, { passive: true });
|
|
343
|
-
doc.addEventListener('pointerleave', clearPointer);
|
|
344
|
-
doc.addEventListener('visibilitychange', onVisibilityChange);
|
|
345
|
-
doc.addEventListener(XIAOHEI_PORTAL_PROXIMITY_EVENT, onPortalProximity);
|
|
346
|
-
doc.addEventListener(XIAOHEI_HEIXIU_GREETING_EVENT, onHeixiuGreeting);
|
|
347
|
-
win.addEventListener('blur', clearPointer);
|
|
348
|
-
reducedMotion.addEventListener('change', onPreferenceChange);
|
|
349
|
-
coarsePointer.addEventListener('change', onPreferenceChange);
|
|
350
|
-
attach();
|
|
351
|
-
scheduleIdleTail();
|
|
352
|
-
return () => {
|
|
353
|
-
disposed = true;
|
|
354
|
-
clearPendingEar();
|
|
355
|
-
clearHeixiuEar();
|
|
356
|
-
clearHeixiuInteraction();
|
|
357
|
-
clearTimer(idleTailTimer);
|
|
358
|
-
clearReaction();
|
|
359
|
-
unsubscribeHostDom();
|
|
360
|
-
stateObserver.disconnect();
|
|
361
|
-
doc.removeEventListener('pointermove', onPointerMove);
|
|
362
|
-
doc.removeEventListener('pointerleave', clearPointer);
|
|
363
|
-
doc.removeEventListener('visibilitychange', onVisibilityChange);
|
|
364
|
-
doc.removeEventListener(XIAOHEI_PORTAL_PROXIMITY_EVENT, onPortalProximity);
|
|
365
|
-
doc.removeEventListener(XIAOHEI_HEIXIU_GREETING_EVENT, onHeixiuGreeting);
|
|
366
|
-
win.removeEventListener('blur', clearPointer);
|
|
367
|
-
reducedMotion.removeEventListener('change', onPreferenceChange);
|
|
368
|
-
coarsePointer.removeEventListener('change', onPreferenceChange);
|
|
369
|
-
reactionLayer?.remove();
|
|
370
|
-
mascot?.removeAttribute('data-xiaohei-reaction');
|
|
371
|
-
mascot?.removeAttribute('data-xiaohei-ear-loop');
|
|
372
|
-
mascot?.removeAttribute('data-xiaohei-heixiu-interaction');
|
|
373
|
-
preloads.length = 0;
|
|
374
|
-
style.remove();
|
|
375
|
-
};
|
|
376
|
-
}
|
|
377
|
-
function currentState(doc) {
|
|
378
|
-
return doc.documentElement.getAttribute('data-xiaohei-state') ?? 'idle';
|
|
379
|
-
}
|
|
380
|
-
//# sourceMappingURL=reactions.js.map
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export declare const XIAOHEI_SYLVA_AVATAR_MARKER = "data-xiaohei-avatar-model";
|
|
2
|
-
/** Inject the renderer-sharing 2.5D avatar without editing registered ThreeUI files. */
|
|
3
|
-
export declare function injectXiaoheiSylvaAvatarModel(source: string): string;
|
|
4
|
-
/** Prepare the detached iframe so the avatar exists on its first WebGL boot. */
|
|
5
|
-
export declare function prepareXiaoheiSylvaAvatarFrame(frame: HTMLIFrameElement): boolean;
|
|
6
|
-
//# sourceMappingURL=avatar-model.d.ts.map
|