@lemoncat7/dsh-theme-xiaohei 0.4.6 → 0.5.0

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.
Files changed (232) hide show
  1. package/README.md +15 -9
  2. package/THIRD_PARTY_NOTICES.md +13 -1
  3. package/docs/dsh-021-compatibility.md +23 -0
  4. package/docs/releases/0.5.0.md +24 -0
  5. package/docs/rift-interaction.md +35 -0
  6. package/docs/rift-preview.md +39 -0
  7. package/lib/client.companion.js +3 -4
  8. package/lib/client.decorations.js +224 -51
  9. package/lib/client.js +532 -2405
  10. package/lib/index.d.ts +1 -2
  11. package/lib/index.js +393 -11
  12. package/lib/rift/boot.d.ts +6 -0
  13. package/package.json +29 -19
  14. package/lib/appearance.d.ts +0 -12
  15. package/lib/appearance.js +0 -42
  16. package/lib/blink.d.ts +0 -4
  17. package/lib/blink.js +0 -110
  18. package/lib/bloub-heixiu/ambient.d.ts +0 -3
  19. package/lib/bloub-heixiu/ambient.js +0 -12
  20. package/lib/bloub-heixiu/character.d.ts +0 -33
  21. package/lib/bloub-heixiu/character.js +0 -124
  22. package/lib/bloub-heixiu/effects.d.ts +0 -4
  23. package/lib/bloub-heixiu/effects.js +0 -91
  24. package/lib/bloub-heixiu/flight.d.ts +0 -15
  25. package/lib/bloub-heixiu/flight.js +0 -66
  26. package/lib/bloub-heixiu/follow.d.ts +0 -13
  27. package/lib/bloub-heixiu/follow.js +0 -23
  28. package/lib/bloub-heixiu/layout.d.ts +0 -34
  29. package/lib/bloub-heixiu/layout.js +0 -26
  30. package/lib/bloub-heixiu/renderer.d.ts +0 -16
  31. package/lib/bloub-heixiu/renderer.js +0 -94
  32. package/lib/bloub-heixiu/runtime.d.ts +0 -6
  33. package/lib/bloub-heixiu/runtime.js +0 -440
  34. package/lib/bloub-heixiu/signals.d.ts +0 -17
  35. package/lib/bloub-heixiu/signals.js +0 -55
  36. package/lib/bloub-heixiu/styles.d.ts +0 -2
  37. package/lib/bloub-heixiu/styles.js +0 -98
  38. package/lib/boot-appearance.d.ts +0 -10
  39. package/lib/boot-appearance.js +0 -47
  40. package/lib/boot-loader.d.ts +0 -5
  41. package/lib/boot-loader.js +0 -158
  42. package/lib/chrome/accessibility.d.ts +0 -3
  43. package/lib/chrome/accessibility.js +0 -69
  44. package/lib/chrome/blink.d.ts +0 -3
  45. package/lib/chrome/blink.js +0 -15
  46. package/lib/chrome/composer-avatar.d.ts +0 -10
  47. package/lib/chrome/composer-avatar.js +0 -599
  48. package/lib/chrome/composer-send-heixiu.d.ts +0 -3
  49. package/lib/chrome/composer-send-heixiu.js +0 -143
  50. package/lib/chrome/composer.d.ts +0 -3
  51. package/lib/chrome/composer.js +0 -135
  52. package/lib/chrome/controls.d.ts +0 -8
  53. package/lib/chrome/controls.js +0 -348
  54. package/lib/chrome/conversation-messages.d.ts +0 -3
  55. package/lib/chrome/conversation-messages.js +0 -329
  56. package/lib/chrome/conversation-submissions.d.ts +0 -3
  57. package/lib/chrome/conversation-submissions.js +0 -68
  58. package/lib/chrome/conversation.d.ts +0 -3
  59. package/lib/chrome/conversation.js +0 -67
  60. package/lib/chrome/frames.d.ts +0 -6
  61. package/lib/chrome/frames.js +0 -134
  62. package/lib/chrome/gaze.d.ts +0 -3
  63. package/lib/chrome/gaze.js +0 -103
  64. package/lib/chrome/identity.d.ts +0 -6
  65. package/lib/chrome/identity.js +0 -47
  66. package/lib/chrome/overlays.d.ts +0 -3
  67. package/lib/chrome/overlays.js +0 -104
  68. package/lib/chrome/portal.d.ts +0 -6
  69. package/lib/chrome/portal.js +0 -169
  70. package/lib/chrome/reactions.d.ts +0 -3
  71. package/lib/chrome/reactions.js +0 -85
  72. package/lib/chrome/sidebar.d.ts +0 -3
  73. package/lib/chrome/sidebar.js +0 -313
  74. package/lib/chrome/tokens.d.ts +0 -3
  75. package/lib/chrome/tokens.js +0 -224
  76. package/lib/chrome/workspace.d.ts +0 -3
  77. package/lib/chrome/workspace.js +0 -94
  78. package/lib/chrome.d.ts +0 -10
  79. package/lib/chrome.js +0 -46
  80. package/lib/client-entry.d.ts +0 -3
  81. package/lib/client-entry.js +0 -3
  82. package/lib/client.d.ts +0 -10
  83. package/lib/client.pose-chin-dark.js +0 -4
  84. package/lib/client.pose-chin-light.js +0 -4
  85. package/lib/client.pose-doze-dark.js +0 -4
  86. package/lib/client.pose-doze-light.js +0 -4
  87. package/lib/client.pose-halfpeek-dark.js +0 -4
  88. package/lib/client.pose-halfpeek-light.js +0 -4
  89. package/lib/client.pose-peek-dark.js +0 -4
  90. package/lib/client.pose-peek-light.js +0 -4
  91. package/lib/client.pose-seated-dark.js +0 -4
  92. package/lib/client.pose-seated-light.js +0 -4
  93. package/lib/client.rig-chin-dark.js +0 -4
  94. package/lib/client.rig-chin-light.js +0 -4
  95. package/lib/client.rig-halfpeek-dark.js +0 -4
  96. package/lib/client.rig-halfpeek-light.js +0 -4
  97. package/lib/client.rig-peek-dark.js +0 -4
  98. package/lib/client.rig-peek-light.js +0 -4
  99. package/lib/client.rig-seated-dark.js +0 -4
  100. package/lib/client.rig-seated-light.js +0 -4
  101. package/lib/companion-entry.d.ts +0 -3
  102. package/lib/companion-entry.js +0 -3
  103. package/lib/composer-avatar.d.ts +0 -18
  104. package/lib/composer-avatar.js +0 -441
  105. package/lib/composer-draft-preview.d.ts +0 -4
  106. package/lib/composer-draft-preview.js +0 -8
  107. package/lib/composer-send-heixiu.d.ts +0 -8
  108. package/lib/composer-send-heixiu.js +0 -133
  109. package/lib/current-session.d.ts +0 -6
  110. package/lib/current-session.js +0 -5
  111. package/lib/decorations-entry.d.ts +0 -3
  112. package/lib/decorations-entry.js +0 -18
  113. package/lib/deferred-companion.d.ts +0 -5
  114. package/lib/deferred-companion.js +0 -9
  115. package/lib/deferred-decorations.d.ts +0 -3
  116. package/lib/deferred-decorations.js +0 -51
  117. package/lib/gaze.d.ts +0 -4
  118. package/lib/gaze.js +0 -230
  119. package/lib/generated-backgrounds.d.ts +0 -14
  120. package/lib/generated-backgrounds.js +0 -14
  121. package/lib/generated-character-motion.d.ts +0 -4
  122. package/lib/generated-character-motion.js +0 -2
  123. package/lib/generated-identity.d.ts +0 -6
  124. package/lib/generated-identity.js +0 -6
  125. package/lib/generated-keyart.d.ts +0 -34
  126. package/lib/generated-keyart.js +0 -34
  127. package/lib/generated-loading-assets.d.ts +0 -4
  128. package/lib/generated-loading-assets.js +0 -4
  129. package/lib/heixiu-interactions.d.ts +0 -10
  130. package/lib/heixiu-interactions.js +0 -161
  131. package/lib/host-contract.d.ts +0 -9
  132. package/lib/host-contract.js +0 -13
  133. package/lib/host-dom.d.ts +0 -11
  134. package/lib/host-dom.js +0 -72
  135. package/lib/loading-heixiu.d.ts +0 -8
  136. package/lib/loading-heixiu.js +0 -287
  137. package/lib/metallic-brand-mark.d.ts +0 -10
  138. package/lib/metallic-brand-mark.js +0 -296
  139. package/lib/plugin.d.ts +0 -6
  140. package/lib/plugin.js +0 -60
  141. package/lib/portal.d.ts +0 -40
  142. package/lib/portal.js +0 -225
  143. package/lib/reactions.d.ts +0 -20
  144. package/lib/reactions.js +0 -380
  145. package/lib/scene/avatar-model.d.ts +0 -6
  146. package/lib/scene/avatar-model.js +0 -646
  147. package/lib/scene/avatar-motion.d.ts +0 -3
  148. package/lib/scene/avatar-motion.js +0 -799
  149. package/lib/scene/character-assets.d.ts +0 -13
  150. package/lib/scene/character-assets.js +0 -26
  151. package/lib/scene/character-expression.d.ts +0 -4
  152. package/lib/scene/character-expression.js +0 -15
  153. package/lib/scene/character-eye-material.d.ts +0 -13
  154. package/lib/scene/character-eye-material.js +0 -88
  155. package/lib/scene/character-eye-renderer.d.ts +0 -5
  156. package/lib/scene/character-eye-renderer.js +0 -80
  157. package/lib/scene/character-eye-rig.d.ts +0 -20
  158. package/lib/scene/character-eye-rig.js +0 -35
  159. package/lib/scene/character-gaze.d.ts +0 -16
  160. package/lib/scene/character-gaze.js +0 -36
  161. package/lib/scene/character-idle.d.ts +0 -17
  162. package/lib/scene/character-idle.js +0 -65
  163. package/lib/scene/character-layout.d.ts +0 -24
  164. package/lib/scene/character-layout.js +0 -51
  165. package/lib/scene/character-motion-clock.d.ts +0 -18
  166. package/lib/scene/character-motion-clock.js +0 -70
  167. package/lib/scene/character-motion-types.d.ts +0 -12
  168. package/lib/scene/character-motion-types.js +0 -2
  169. package/lib/scene/character-motion.d.ts +0 -6
  170. package/lib/scene/character-motion.js +0 -187
  171. package/lib/scene/character-poses.d.ts +0 -26
  172. package/lib/scene/character-poses.js +0 -11
  173. package/lib/scene/character-rig-renderer.d.ts +0 -5
  174. package/lib/scene/character-rig-renderer.js +0 -95
  175. package/lib/scene/character-rig.d.ts +0 -22
  176. package/lib/scene/character-rig.js +0 -61
  177. package/lib/scene/heixiu-feedback.d.ts +0 -3
  178. package/lib/scene/heixiu-feedback.js +0 -64
  179. package/lib/scene/heixiu-interactions.d.ts +0 -3
  180. package/lib/scene/heixiu-interactions.js +0 -92
  181. package/lib/scene/ids.d.ts +0 -5
  182. package/lib/scene/ids.js +0 -5
  183. package/lib/scene/pointer-bridge.d.ts +0 -21
  184. package/lib/scene/pointer-bridge.js +0 -155
  185. package/lib/scene/runtime.d.ts +0 -12
  186. package/lib/scene/runtime.js +0 -74
  187. package/lib/scene/state-transitions.d.ts +0 -3
  188. package/lib/scene/state-transitions.js +0 -37
  189. package/lib/scene/styles.d.ts +0 -5
  190. package/lib/scene/styles.js +0 -69
  191. package/lib/scene/sylva-performance.d.ts +0 -11
  192. package/lib/scene/sylva-performance.js +0 -58
  193. package/lib/scene/wallpaper-character-styles.d.ts +0 -3
  194. package/lib/scene/wallpaper-character-styles.js +0 -49
  195. package/lib/scene/wallpaper-character.d.ts +0 -5
  196. package/lib/scene/wallpaper-character.js +0 -222
  197. package/lib/scene.d.ts +0 -8
  198. package/lib/scene.js +0 -8
  199. package/lib/sidebar-glass.d.ts +0 -16
  200. package/lib/sidebar-glass.js +0 -168
  201. package/lib/sidebar-heixiu-escape.d.ts +0 -44
  202. package/lib/sidebar-heixiu-escape.js +0 -340
  203. package/lib/sidebar-heixiu-roaming.d.ts +0 -27
  204. package/lib/sidebar-heixiu-roaming.js +0 -310
  205. package/lib/sidebar-reveal.d.ts +0 -8
  206. package/lib/sidebar-reveal.js +0 -59
  207. package/lib/state.d.ts +0 -21
  208. package/lib/state.js +0 -123
  209. package/lib/theme.d.ts +0 -19
  210. package/lib/theme.js +0 -188
  211. package/lib/vendor/bloub/decor.d.ts +0 -114
  212. package/lib/vendor/bloub/decor.js +0 -198
  213. package/lib/vendor/bloub/engine.d.ts +0 -213
  214. package/lib/vendor/bloub/engine.js +0 -452
  215. package/lib/vendor/bloub/expressions.d.ts +0 -35
  216. package/lib/vendor/bloub/expressions.js +0 -143
  217. package/lib/vendor/bloub/eyefit.d.ts +0 -75
  218. package/lib/vendor/bloub/eyefit.js +0 -393
  219. package/lib/vendor/bloub/face.d.ts +0 -63
  220. package/lib/vendor/bloub/face.js +0 -106
  221. package/lib/vendor/bloub/math.d.ts +0 -22
  222. package/lib/vendor/bloub/math.js +0 -36
  223. package/lib/vendor/bloub/profiles.d.ts +0 -8
  224. package/lib/vendor/bloub/profiles.js +0 -20
  225. package/lib/vendor/bloub/repere.d.ts +0 -30
  226. package/lib/vendor/bloub/repere.js +0 -32
  227. package/lib/vendor/bloub/shape.d.ts +0 -80
  228. package/lib/vendor/bloub/shape.js +0 -268
  229. package/lib/vendor/bloub/skins.d.ts +0 -36
  230. package/lib/vendor/bloub/skins.js +0 -74
  231. package/lib/vendor/bloub/states.d.ts +0 -87
  232. package/lib/vendor/bloub/states.js +0 -468
@@ -1,23 +0,0 @@
1
- /** Attraction measured from the stationary hit area, not the moving artwork. */
2
- export function resolveHeixiuGaze(dx, dy) {
3
- if (!Number.isFinite(dx) || !Number.isFinite(dy))
4
- return { yaw: 0, pitch: -7, mix: 1, spin: 0, wander: .35 };
5
- return { yaw: Math.max(-14, Math.min(14, dx / 16)),
6
- // The old upper limit was -7 + 9 = 2 degrees: barely above straight ahead.
7
- pitch: -7 - Math.max(-31, Math.min(9, dy / 16)), mix: 1, spin: 0, wander: 0 };
8
- }
9
- export function resolveHeixiuFollow(dx, dy, compact) {
10
- if (!Number.isFinite(dx) || !Number.isFinite(dy))
11
- return { x: 0, y: 0 };
12
- const distance = Math.hypot(dx, dy), radius = compact ? 90 : 140;
13
- if (distance >= radius)
14
- return { x: 0, y: 0 };
15
- const k = Math.max(0, Math.min(1, (distance - radius * .5) / (radius * .5)));
16
- const fade = 1 - k * k * (3 - 2 * k);
17
- const divisor = Math.max(compact ? 32 : 50, distance);
18
- return {
19
- x: dx / divisor * (compact ? 2 : 5) * fade,
20
- y: dy / divisor * (compact ? 1.5 : 3.5) * fade,
21
- };
22
- }
23
- //# sourceMappingURL=follow.js.map
@@ -1,34 +0,0 @@
1
- export declare function resolveHeixiuDock(composerLeft: number, sidebarRight: number, viewportWidth: number): {
2
- readonly mode: "outside" | "compact";
3
- readonly size: 96 | 52;
4
- };
5
- export declare function resolveHeixiuWelcome(hero: {
6
- left: number;
7
- top: number;
8
- width: number;
9
- height: number;
10
- }, composer: {
11
- left: number;
12
- top: number;
13
- }, sidebarRight: number, viewportWidth: number): {
14
- size: number;
15
- left: number;
16
- top: number;
17
- } | undefined;
18
- /** Mirror the owned absolute-position CSS without measuring after style writes.
19
- * The containing block is the composer's padding box (inside its border).
20
- */
21
- export declare function resolveHeixiuBox(composer: {
22
- left: number;
23
- top: number;
24
- height: number;
25
- }, border: {
26
- left: number;
27
- top: number;
28
- }, dock: ReturnType<typeof resolveHeixiuDock>, welcome: ReturnType<typeof resolveHeixiuWelcome>): {
29
- left: number;
30
- top: number;
31
- width: number;
32
- height: number;
33
- };
34
- //# sourceMappingURL=layout.d.ts.map
@@ -1,26 +0,0 @@
1
- export function resolveHeixiuDock(composerLeft, sidebarRight, viewportWidth) {
2
- const outside = viewportWidth > 760 && composerLeft - sidebarRight >= 114;
3
- return { mode: outside ? 'outside' : 'compact', size: outside ? 96 : 52 };
4
- }
5
- export function resolveHeixiuWelcome(hero, composer, sidebarRight, viewportWidth) {
6
- if (hero.width <= 0 || hero.height <= 0 || hero.top + hero.height > composer.top)
7
- return undefined;
8
- const size = viewportWidth <= 760 ? 52 : 96;
9
- const left = Math.max(sidebarRight + 6, hero.left - size - (size === 52 ? 10 : 24));
10
- return { size, left: left - composer.left, top: hero.top + hero.height / 2 - size / 2 - composer.top };
11
- }
12
- /** Mirror the owned absolute-position CSS without measuring after style writes.
13
- * The containing block is the composer's padding box (inside its border).
14
- */
15
- export function resolveHeixiuBox(composer, border, dock, welcome) {
16
- const size = welcome?.size ?? dock.size;
17
- if (welcome)
18
- return { left: composer.left + border.left + welcome.left,
19
- top: composer.top + border.top + welcome.top, width: size, height: size };
20
- if (dock.mode === 'compact')
21
- return { left: composer.left + border.left + 4,
22
- top: composer.top + border.top + 6, width: size, height: size };
23
- return { left: composer.left + border.left - 18 - size,
24
- top: composer.top + composer.height / 2 - size / 2, width: size, height: size };
25
- }
26
- //# sourceMappingURL=layout.js.map
@@ -1,16 +0,0 @@
1
- import type { BotFrame } from '../vendor/bloub/engine.js';
2
- import { type HeixiuReaction } from './character.js';
3
- /** A hollow eye, not a hole in the body: wallpaper never bleeds through it. */
4
- export declare function heixiuEyeRadii(width: number, height: number): {
5
- rx: number;
6
- ry: number;
7
- };
8
- /** Filled upstream eyes may touch; hollow rings need a small readable gap. */
9
- export declare function heixiuEyeSeparation(eyes: BotFrame['eyes']): number;
10
- /** Persistent SVG nodes: frame updates never replace HTML or retrigger host observers. */
11
- export declare function createHeixiuRenderer(doc: Document): {
12
- svg: SVGSVGElement;
13
- render: (frame: BotFrame, reaction?: HeixiuReaction) => void;
14
- prepareEffects: () => void;
15
- };
16
- //# sourceMappingURL=renderer.d.ts.map
@@ -1,94 +0,0 @@
1
- import { DEMI_VIEWBOX } from '../vendor/bloub/repere.js';
2
- import { HEIXIU_FOOT, HEIXIU_REST } from './character.js';
3
- import { createHeixiuEffects } from './effects.js';
4
- const NS = 'http://www.w3.org/2000/svg';
5
- let serial = 0;
6
- /** A hollow eye, not a hole in the body: wallpaper never bleeds through it. */
7
- export function heixiuEyeRadii(width, height) {
8
- // Upstream special poses bring their eyes close together. Keep the large
9
- // resting rings, but taper their width in small poses to avoid fused eyes.
10
- const roomy = Math.max(0, Math.min(1, (width - 20) / 12));
11
- return { rx: Math.max(0.01, Math.min(width, 38) * (1 + .15 * roomy)), ry: Math.max(0.01, Math.min(height, 62) * 0.96) };
12
- }
13
- /** Filled upstream eyes may touch; hollow rings need a small readable gap. */
14
- export function heixiuEyeSeparation(eyes) {
15
- if (eyes.length !== 2)
16
- return 1;
17
- const matrices = eyes.map(eye => eye.openMatrix.slice(7, -1).split(/[ ,]+/).map(Number));
18
- const [a, b] = matrices;
19
- if (!a || !b || a.length !== 6 || b.length !== 6 || [...a, ...b].some(n => !Number.isFinite(n)))
20
- return 1;
21
- const dx = b[4] - a[4], dy = b[5] - a[5], distance = Math.hypot(dx, dy);
22
- if (distance < 1)
23
- return .1;
24
- const nx = dx / distance, ny = dy / distance;
25
- const support = eyes.reduce((sum, eye, i) => {
26
- const m = matrices[i], { rx, ry } = heixiuEyeRadii(eye.width, eye.height);
27
- return sum + Math.hypot(rx * (m[0] * nx + m[1] * ny), ry * (m[2] * nx + m[3] * ny));
28
- }, 0);
29
- return Math.min(1, Math.max(.1, (distance - 10) / Math.max(1, support)));
30
- }
31
- /** Persistent SVG nodes: frame updates never replace HTML or retrigger host observers. */
32
- export function createHeixiuRenderer(doc) {
33
- const el = (name) => doc.createElementNS(NS, name);
34
- const attrs = (node, values) => {
35
- for (const [key, value] of Object.entries(values)) {
36
- const text = String(value);
37
- if (node.getAttribute(key) !== text)
38
- node.setAttribute(key, text);
39
- }
40
- };
41
- const svg = el('svg'), defs = el('defs'), clip = el('clipPath'), clipBody = el('path');
42
- const id = `xiaohei-bloub-${++serial}`;
43
- attrs(svg, { viewBox: `${-DEMI_VIEWBOX} ${-DEMI_VIEWBOX} ${DEMI_VIEWBOX * 2} ${DEMI_VIEWBOX * 2}`, 'aria-hidden': 'true', focusable: 'false' });
44
- attrs(clip, { id: `${id}-body`, clipPathUnits: 'userSpaceOnUse' });
45
- clip.append(clipBody);
46
- defs.append(clip);
47
- const bodyGroup = el('g'), body = el('path'), eyesGroup = el('g');
48
- attrs(body, { fill: 'var(--heixiu-ink)', stroke: 'var(--heixiu-outline)', 'stroke-width': 1.2, 'data-heixiu-body': '' });
49
- attrs(eyesGroup, { 'clip-path': `url(#${id}-body)`, fill: 'none', stroke: '#f5f3db', 'stroke-width': 10.5 });
50
- const eyes = [el('path'), el('path')];
51
- for (const eye of eyes)
52
- attrs(eye, { 'data-heixiu-eye': '', 'stroke-linecap': 'round', 'stroke-linejoin': 'round' });
53
- eyesGroup.append(...eyes);
54
- bodyGroup.append(body, eyesGroup);
55
- const character = el('g');
56
- attrs(character, { 'data-heixiu-character': '' });
57
- character.append(bodyGroup);
58
- svg.append(defs, character);
59
- let effects;
60
- function prepareEffects() {
61
- if (effects)
62
- return;
63
- const behind = el('g'), front = el('g');
64
- character.insertBefore(behind, bodyGroup);
65
- character.append(front);
66
- effects = createHeixiuEffects(doc, id, defs, behind, front, bodyGroup);
67
- }
68
- function render(frame, reaction = HEIXIU_REST) {
69
- attrs(character, { transform: `translate(${reaction.x} ${reaction.y + HEIXIU_FOOT}) rotate(${reaction.roll}) scale(${reaction.sx} ${reaction.sy}) translate(0 ${-HEIXIU_FOOT})` });
70
- attrs(body, { d: frame.bodyPath });
71
- attrs(clipBody, { d: frame.bodyPath });
72
- attrs(bodyGroup, { opacity: frame.bodyAlpha });
73
- const eyeScale = heixiuEyeSeparation(frame.eyes);
74
- for (const [i, node] of eyes.entries()) {
75
- const eye = frame.eyes[i];
76
- if (!eye) {
77
- attrs(node, { opacity: 0 });
78
- continue;
79
- }
80
- const radii = heixiuEyeRadii(eye.width, eye.height);
81
- const rx = radii.rx * eyeScale, ry = radii.ry * eyeScale;
82
- const lid = Math.max(0, Math.min(eye.lid, reaction.lid));
83
- // Both halves meet in a curved, constant-weight lid. No flattened matrix,
84
- // double-outline flash, vanished stroke, or full-body opacity swapping.
85
- const curve = -rx * .26 * reaction.cheer * (1 - lid);
86
- const top = curve - ry * lid * 1.3333, bottom = curve + ry * lid * 1.3333;
87
- const d = `M${-rx} 0C${-rx} ${top} ${rx} ${top} ${rx} 0C${rx} ${bottom} ${-rx} ${bottom} ${-rx} 0Z`;
88
- attrs(node, { d, transform: eye.openMatrix, opacity: eye.alpha, 'stroke-width': Math.min(10.5, rx * .28) });
89
- }
90
- effects?.(frame);
91
- }
92
- return { svg, render, prepareEffects };
93
- }
94
- //# sourceMappingURL=renderer.js.map
@@ -1,6 +0,0 @@
1
- import type { HeixiuSignals } from './signals.js';
2
- export declare const HEIXIU_BLOUB_ACTIONS: readonly ["burst", "egg", "orbit", "hexagon", "wink", "wide", "exclaim", "alert", "sleep", "thinking", "notify"];
3
- export declare const HEIXIU_AMBIENT_ACTIONS: readonly ["burst", "egg", "orbit", "hexagon", "wink", "wide", "sleep"];
4
- /** One native-composer-owned button; optional read-only state, never send handlers. */
5
- export declare function installComposerBloub(doc?: Document | undefined, signals?: HeixiuSignals): () => void;
6
- //# sourceMappingURL=runtime.d.ts.map
@@ -1,440 +0,0 @@
1
- import { BotEngine } from '../vendor/bloub/engine.js';
2
- import { liveliness } from '../vendor/bloub/face.js';
3
- import { STATE_BY_ID } from '../vendor/bloub/states.js';
4
- import { subscribeXiaoheiHostDom } from '../host-dom.js';
5
- import { XIAOHEI_HOST_SELECTORS } from '../host-contract.js';
6
- import { createHeixiuRenderer } from './renderer.js';
7
- import { resolveHeixiuDock, resolveHeixiuWelcome, resolveHeixiuBox } from './layout.js';
8
- import { resolveHeixiuFollow, resolveHeixiuGaze } from './follow.js';
9
- import { createHeixiuFlight } from './flight.js';
10
- import { heixiuAmbientDelay, heixiuAmbientIndex } from './ambient.js';
11
- import { HEIXIU_BLOUB_CSS } from './styles.js';
12
- import { HEIXIU_FACE, HEIXIU_PROFILE, HEIXIU_REACTIONS, HeixiuReactions } from './character.js';
13
- export const HEIXIU_BLOUB_ACTIONS = ['burst', 'egg', 'orbit', 'hexagon', 'wink', 'wide', 'exclaim', 'alert', 'sleep', 'thinking', 'notify'];
14
- export const HEIXIU_AMBIENT_ACTIONS = ['burst', 'egg', 'orbit', 'hexagon', 'wink', 'wide', 'sleep'];
15
- /** One native-composer-owned button; optional read-only state, never send handlers. */
16
- export function installComposerBloub(doc = typeof document === 'undefined' ? undefined : document, signals) {
17
- const win = doc?.defaultView;
18
- if (!doc || !win)
19
- return () => { };
20
- const style = doc.createElement('style');
21
- style.textContent = HEIXIU_BLOUB_CSS;
22
- doc.head.append(style);
23
- const button = doc.createElement('button');
24
- button.className = 'xiaohei-bloub';
25
- button.type = 'button';
26
- button.setAttribute('aria-label', '逗逗嘿咻');
27
- button.title = '单击逗逗嘿咻 · 双击轮换变形(键盘 Shift+Enter)';
28
- const renderer = createHeixiuRenderer(doc);
29
- const drift = doc.createElement('span');
30
- drift.className = 'xiaohei-bloub-drift';
31
- drift.append(renderer.svg);
32
- button.append(drift);
33
- const flight = createHeixiuFlight(button);
34
- const engine = new BotEngine(100, 'idle', HEIXIU_PROFILE, HEIXIU_FACE);
35
- const reactions = new HeixiuReactions();
36
- const neutral = { yaw: 0, pitch: -7, mix: 1, spin: 0, wander: .35 };
37
- let gaze = neutral;
38
- engine.setLook(neutral, 0);
39
- renderer.render(engine.sample(0));
40
- let disposed = false, visible = false, dirty = true;
41
- let composer = null, sidebar = null;
42
- let hero = null, heroRow = null;
43
- let bounds;
44
- let layoutFrame, animationFrame, wake;
45
- let frameTimer;
46
- let lastPointerStamp = -Infinity;
47
- let time = 0, lastStamp, activeUntil = 2.5, actionUntil = 0, reactionIndex = 0;
48
- let pointerSide = 1;
49
- let special = false, specialIndex = 0;
50
- let followingX = '', followingY = '';
51
- let ambientTimer, previousSpecial = -1, lastInput = -Infinity;
52
- let signal = signals?.getSnapshot();
53
- let dockMode, viewportWidth = win.innerWidth, viewportHeight = win.innerHeight;
54
- const motion = win.matchMedia('(prefers-reduced-motion: reduce)');
55
- const ro = typeof win.ResizeObserver === 'function' ? new win.ResizeObserver(() => { dirty = true; scheduleLayout(); }) : undefined;
56
- const io = typeof win.IntersectionObserver === 'function' ? new win.IntersectionObserver(entries => {
57
- const entry = entries.find(e => e.target === button);
58
- if (entry) {
59
- visible = entry.isIntersecting;
60
- syncPlayback();
61
- }
62
- }) : undefined;
63
- function stop() {
64
- button.dataset.floating = 'false';
65
- follow(0, 0);
66
- if (ambientTimer !== undefined)
67
- win.clearTimeout(ambientTimer);
68
- ambientTimer = undefined;
69
- flight.cancel();
70
- if (animationFrame !== undefined)
71
- win.cancelAnimationFrame(animationFrame);
72
- if (frameTimer !== undefined)
73
- win.clearTimeout(frameTimer);
74
- if (wake !== undefined)
75
- win.clearTimeout(wake);
76
- animationFrame = undefined;
77
- frameTimer = undefined;
78
- wake = undefined;
79
- lastStamp = undefined;
80
- }
81
- function follow(dx, dy) {
82
- const offset = resolveHeixiuFollow(dx, dy, win.innerWidth <= 760 || composer?.dataset.heixiuDock === 'compact');
83
- const x = `${offset.x.toFixed(3)}px`, y = `${offset.y.toFixed(3)}px`;
84
- if (x !== followingX) {
85
- button.style.setProperty('--heixiu-follow-x', x);
86
- followingX = x;
87
- }
88
- if (y !== followingY) {
89
- button.style.setProperty('--heixiu-follow-y', y);
90
- followingY = y;
91
- }
92
- }
93
- function allowed() { return !disposed && visible && !!composer?.isConnected && !doc.hidden && !motion.matches; }
94
- function scheduleAmbient(delay = heixiuAmbientDelay()) {
95
- if (ambientTimer !== undefined)
96
- win.clearTimeout(ambientTimer);
97
- ambientTimer = undefined;
98
- if (!allowed())
99
- return;
100
- ambientTimer = win.setTimeout(() => {
101
- ambientTimer = undefined;
102
- if (!allowed())
103
- return;
104
- if (actionUntil || signal?.running || flight.active || Date.now() - lastInput < 5000) {
105
- scheduleAmbient(5000);
106
- return;
107
- }
108
- playState(HEIXIU_AMBIENT_ACTIONS[heixiuAmbientIndex(HEIXIU_AMBIENT_ACTIONS.length, previousSpecial)], 'ambient');
109
- }, delay);
110
- }
111
- function animateFor(seconds) {
112
- activeUntil = Math.max(activeUntil, time + seconds);
113
- if (!allowed() || animationFrame !== undefined || frameTimer !== undefined)
114
- return;
115
- if (wake !== undefined)
116
- win.clearTimeout(wake);
117
- wake = undefined;
118
- lastStamp = undefined;
119
- animationFrame = win.requestAnimationFrame(tick);
120
- }
121
- function tick(stamp) {
122
- animationFrame = undefined;
123
- if (!allowed()) {
124
- stop();
125
- return;
126
- }
127
- if (lastStamp !== undefined)
128
- time += Math.min(.05, Math.max(0, (stamp - lastStamp) / 1000));
129
- lastStamp = stamp;
130
- if (actionUntil && time >= actionUntil) {
131
- special = false;
132
- engine.setLook(gaze, time);
133
- actionUntil = 0;
134
- engine.setState('idle', time);
135
- button.dataset.action = 'idle';
136
- activeUntil = Math.max(activeUntil, time + .65);
137
- if (signal?.running)
138
- playState('thinking', 'session', true);
139
- }
140
- renderer.render(engine.sample(time), reactions.sample(time));
141
- // Stop only with open eyes. The next short life window resumes the same
142
- // clock, so neither gaze nor a partly completed blink can jump on waking.
143
- if (time < activeUntil || actionUntil || liveliness(time).lid < .99) {
144
- // SVG path deformation needs painting. Cap it instead of following a
145
- // 120/144 Hz desktop display; preserve elapsed time and action continuity.
146
- frameTimer = win.setTimeout(() => {
147
- frameTimer = undefined;
148
- if (allowed())
149
- animationFrame = win.requestAnimationFrame(tick);
150
- }, 1000 / 30);
151
- }
152
- else {
153
- lastStamp = undefined;
154
- wake = win.setTimeout(() => { wake = undefined; animateFor(1); }, 8000);
155
- }
156
- }
157
- function syncPlayback() {
158
- if (!allowed()) {
159
- stop();
160
- if (motion.matches) {
161
- engine.reset('idle', time);
162
- engine.setLook(neutral, time, .001);
163
- actionUntil = 0;
164
- special = false;
165
- reactions.reset();
166
- time += 1;
167
- renderer.render(engine.sample(time));
168
- button.dataset.action = 'idle';
169
- }
170
- }
171
- else {
172
- button.dataset.floating = 'true';
173
- animateFor(1);
174
- if (signal?.running && !actionUntil)
175
- playState('thinking', 'session', true);
176
- if (ambientTimer === undefined)
177
- scheduleAmbient();
178
- }
179
- }
180
- function reconcile() {
181
- layoutFrame = undefined;
182
- if (disposed)
183
- return;
184
- const next = doc.querySelector(XIAOHEI_HOST_SELECTORS.composerCard);
185
- const nextSidebar = doc.querySelector(XIAOHEI_HOST_SELECTORS.sidebarShell);
186
- const nextHero = doc.querySelector(XIAOHEI_HOST_SELECTORS.composerHeroMark);
187
- let mounted = false;
188
- if (composer !== next || sidebar !== nextSidebar || hero !== nextHero) {
189
- composer?.removeAttribute('data-heixiu-dock');
190
- ro?.disconnect();
191
- heroRow?.removeAttribute('data-heixiu-hero-row');
192
- composer = next;
193
- sidebar = nextSidebar;
194
- hero = nextHero;
195
- dirty = true;
196
- heroRow = hero?.closest(XIAOHEI_HOST_SELECTORS.composerHeroRow) ?? null;
197
- heroRow?.setAttribute('data-heixiu-hero-row', '');
198
- if (composer)
199
- ro?.observe(composer);
200
- if (sidebar)
201
- ro?.observe(sidebar);
202
- if (hero)
203
- ro?.observe(hero);
204
- if (heroRow)
205
- ro?.observe(heroRow);
206
- mounted = true;
207
- }
208
- if (!composer) {
209
- button.remove();
210
- visible = false;
211
- stop();
212
- return;
213
- }
214
- if (button.parentElement !== composer || composer.lastElementChild === button) {
215
- // The native trailing toolbar must stay last: DSH and the existing send
216
- // skin use its structural position. The overlay must not replace it.
217
- const toolbar = [...composer.children].reverse().find(child => child !== button);
218
- button.hidden = true;
219
- composer.insertBefore(button, toolbar ?? null);
220
- io?.observe(button);
221
- dirty = true;
222
- mounted = true;
223
- }
224
- // Let the host lay out the new dock before reading geometry. Do not force
225
- // a synchronous layout of the entire newly mounted conversation here.
226
- if (mounted) {
227
- scheduleLayout();
228
- return;
229
- }
230
- if (!dirty)
231
- return;
232
- dirty = false;
233
- const wasFlying = flight.active;
234
- const viewportChanged = viewportWidth !== win.innerWidth || viewportHeight !== win.innerHeight;
235
- const origin = flight.capture(bounds);
236
- const rect = composer.getBoundingClientRect();
237
- const border = { left: composer.clientLeft || 0, top: composer.clientTop || 0 };
238
- const sidebarRight = sidebar?.getBoundingClientRect().right ?? 0;
239
- const dock = resolveHeixiuDock(rect.left, sidebarRight, win.innerWidth);
240
- const heroRect = hero?.getBoundingClientRect();
241
- const welcome = heroRect && resolveHeixiuWelcome(heroRect, rect, sidebarRight, win.innerWidth);
242
- const target = resolveHeixiuBox(rect, border, dock, welcome || undefined);
243
- // All geometry reads are complete. Everything below only writes.
244
- follow(0, 0);
245
- if (welcome) {
246
- for (const [key, value] of Object.entries(welcome)) {
247
- const property = `--heixiu-welcome-${key}`, css = `${value}px`;
248
- if (button.style.getPropertyValue(property) !== css)
249
- button.style.setProperty(property, css);
250
- }
251
- }
252
- const mode = welcome ? 'welcome' : dock.mode;
253
- const fly = !viewportChanged && (wasFlying || !!dockMode && dockMode !== mode && (dockMode === 'welcome' || mode === 'welcome'));
254
- dockMode = mode;
255
- viewportWidth = win.innerWidth;
256
- viewportHeight = win.innerHeight;
257
- if (composer.dataset.heixiuDock !== mode)
258
- composer.dataset.heixiuDock = mode;
259
- bounds = target;
260
- button.hidden = false;
261
- visible = rect.width > 0 && rect.height > 0 && bounds.top + bounds.height > 0 && bounds.top < win.innerHeight;
262
- syncPlayback();
263
- if (allowed() && fly)
264
- flight.move(origin, bounds, motion.matches);
265
- else
266
- flight.cancel();
267
- }
268
- function scheduleLayout() {
269
- if (disposed || layoutFrame !== undefined)
270
- return;
271
- layoutFrame = win.requestAnimationFrame(reconcile);
272
- }
273
- const move = (event) => {
274
- if (!allowed() || flight.active || !bounds?.width)
275
- return;
276
- if (event.pointerType === 'touch') {
277
- leave();
278
- return;
279
- }
280
- if (event.timeStamp - lastPointerStamp < 1000 / 30)
281
- return;
282
- lastPointerStamp = event.timeStamp;
283
- const dx = event.clientX - (bounds.left + bounds.width / 2);
284
- const dy = event.clientY - (bounds.top + bounds.height / 2);
285
- follow(dx, dy);
286
- gaze = resolveHeixiuGaze(dx, dy);
287
- if (special)
288
- return;
289
- pointerSide = dx < 0 ? -1 : 1;
290
- reactions.aim(Math.abs(dx) < 90 && Math.abs(dy) < 90 ? dx / 60 : 0, time);
291
- engine.setLook(gaze, time);
292
- animateFor(.8);
293
- };
294
- const leave = () => {
295
- follow(0, 0);
296
- gaze = neutral;
297
- if (allowed() && !special) {
298
- reactions.aim(0, time);
299
- engine.setLook(neutral, time);
300
- animateFor(.8);
301
- }
302
- };
303
- const click = (event) => {
304
- event.stopPropagation();
305
- if (!allowed() || event.detail > 1)
306
- return;
307
- // A follow-up double click must not flash idle between special actions.
308
- // A single click during one lets it finish; the next double click can
309
- // interrupt it directly, without a delayed single-click timer.
310
- if (special && event.detail > 0)
311
- return;
312
- const index = reactionIndex++ % (HEIXIU_REACTIONS.length + 1);
313
- if (index === HEIXIU_REACTIONS.length) {
314
- playState('exclaim', 'manual');
315
- return;
316
- }
317
- const kind = HEIXIU_REACTIONS[index];
318
- special = false;
319
- engine.setState('idle', time);
320
- engine.setLook(gaze, time);
321
- reactions.play(kind, time, pointerSide);
322
- button.dataset.action = kind;
323
- button.dataset.actionSource = 'manual';
324
- scheduleAmbient();
325
- actionUntil = reactions.end;
326
- animateFor(actionUntil - time + .65);
327
- };
328
- function playState(state, source, held = false) {
329
- const def = STATE_BY_ID.get(state);
330
- renderer.prepareEffects();
331
- reactions.settle(time);
332
- special = true;
333
- engine.setLook(null, time);
334
- engine.setState(state, time);
335
- button.dataset.action = state;
336
- previousSpecial = HEIXIU_AMBIENT_ACTIONS.findIndex(action => action === state);
337
- button.dataset.actionSource = source;
338
- actionUntil = held ? Infinity : time + Math.max(def.duration, def.minDuration ?? 0);
339
- // Do not poison activeUntil with Infinity: normal idle must sleep again.
340
- animateFor(Math.max(def.duration, def.minDuration ?? 0) + .65);
341
- scheduleAmbient();
342
- }
343
- const doubleClick = (event) => {
344
- event.stopPropagation();
345
- if (!allowed())
346
- return;
347
- playState(HEIXIU_BLOUB_ACTIONS[specialIndex++ % HEIXIU_BLOUB_ACTIONS.length], 'manual');
348
- };
349
- const keydown = (event) => {
350
- if (event.key === 'Enter' && event.shiftKey) {
351
- event.preventDefault();
352
- if (!event.repeat)
353
- doubleClick(event);
354
- }
355
- };
356
- const resized = () => { dirty = true; scheduleLayout(); };
357
- const input = (event) => { if (composer?.contains(event.target))
358
- lastInput = Date.now(); };
359
- const unsubscribeSignals = signals?.subscribe(() => {
360
- const next = signals.getSnapshot(), changedSession = next.sessionId !== signal?.sessionId;
361
- signal = next;
362
- button.dataset.sessionState = next.running ? 'running' : 'idle';
363
- // Never replay missed notifications on visibility/reduced-motion changes.
364
- if (!allowed()) {
365
- if (actionUntil === Infinity || button.dataset.actionSource === 'session' || changedSession) {
366
- actionUntil = 0;
367
- special = false;
368
- engine.setState('idle', time);
369
- engine.setLook(gaze, time);
370
- button.dataset.action = 'idle';
371
- }
372
- return;
373
- }
374
- if (changedSession || actionUntil === Infinity && !next.running) {
375
- actionUntil = 0;
376
- special = false;
377
- engine.setState('idle', time);
378
- engine.setLook(gaze, time);
379
- button.dataset.action = 'idle';
380
- animateFor(.65);
381
- }
382
- if (!changedSession && next.notice)
383
- playState(next.notice, 'session');
384
- else if (next.running && (!actionUntil || button.dataset.actionSource !== 'session'))
385
- playState('thinking', 'session', true);
386
- }) ?? (() => { });
387
- const unsubscribe = subscribeXiaoheiHostDom(doc, records => {
388
- // Streamed paragraphs and our own SVG effects do not replace host slots.
389
- // Parent removals still reach us, so session/composer replacement is safe.
390
- if (records.length && records.every(record => {
391
- const target = record.target.nodeType === 1 ? record.target : record.target.parentElement;
392
- return target?.closest('[data-chat-flow], .xiaohei-bloub');
393
- }))
394
- return;
395
- scheduleLayout();
396
- });
397
- const scrolled = (event) => {
398
- const target = event.target;
399
- // Captured scrolls from history/code blocks cannot move a fixed composer.
400
- if (target === doc || target === win || target instanceof win.Element &&
401
- (target.contains(composer) || target.contains(sidebar)))
402
- resized();
403
- };
404
- doc.addEventListener('pointermove', move, { passive: true });
405
- doc.addEventListener('pointerleave', leave);
406
- doc.addEventListener('visibilitychange', syncPlayback);
407
- doc.addEventListener('input', input, { passive: true });
408
- button.addEventListener('click', click);
409
- button.addEventListener('dblclick', doubleClick);
410
- button.addEventListener('keydown', keydown);
411
- win.addEventListener('resize', resized, { passive: true });
412
- win.addEventListener('scroll', scrolled, { passive: true, capture: true });
413
- motion.addEventListener('change', syncPlayback);
414
- scheduleLayout();
415
- return () => {
416
- disposed = true;
417
- stop();
418
- unsubscribe();
419
- unsubscribeSignals();
420
- ro?.disconnect();
421
- io?.disconnect();
422
- if (layoutFrame !== undefined)
423
- win.cancelAnimationFrame(layoutFrame);
424
- doc.removeEventListener('pointermove', move);
425
- doc.removeEventListener('pointerleave', leave);
426
- doc.removeEventListener('visibilitychange', syncPlayback);
427
- button.removeEventListener('click', click);
428
- doc.removeEventListener('input', input);
429
- button.removeEventListener('dblclick', doubleClick);
430
- button.removeEventListener('keydown', keydown);
431
- win.removeEventListener('resize', resized);
432
- win.removeEventListener('scroll', scrolled, true);
433
- motion.removeEventListener('change', syncPlayback);
434
- heroRow?.removeAttribute('data-heixiu-hero-row');
435
- composer?.removeAttribute('data-heixiu-dock');
436
- button.remove();
437
- style.remove();
438
- };
439
- }
440
- //# sourceMappingURL=runtime.js.map
@@ -1,17 +0,0 @@
1
- import type { ISessions } from '@deepseek-ai/dsh-api-session-controller/client';
2
- export interface HeixiuSignal {
3
- sessionId?: string;
4
- running: boolean;
5
- notice: 'notify' | 'alert' | null;
6
- revision: number;
7
- }
8
- /** Small local read model. No messages, RPCs, history scans or persisted state. */
9
- export declare function createHeixiuSignals(): {
10
- getSnapshot: () => HeixiuSignal;
11
- subscribe(fn: () => void): () => void;
12
- publish(next: Omit<HeixiuSignal, "revision">): void;
13
- };
14
- export type HeixiuSignals = ReturnType<typeof createHeixiuSignals>;
15
- /** Optional official service adapter, subscribed only to the selected session. */
16
- export declare function bindHeixiuSessions(sessions: ISessions, signals: HeixiuSignals): () => void;
17
- //# sourceMappingURL=signals.d.ts.map