@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.
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 +534 -2405
  10. package/lib/index.d.ts +1 -2
  11. package/lib/index.js +395 -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,13 +0,0 @@
1
- import type { CharacterRig } from './character-motion-types.js';
2
- export declare const CHARACTER_POSE_NAMES: readonly ["seated", "peek", "chin", "doze", "halfpeek"];
3
- export type CharacterPose = typeof CHARACTER_POSE_NAMES[number];
4
- export type CharacterAppearance = 'light' | 'dark';
5
- export declare function createCharacterAssetLoader(importChunk: (path: string) => Promise<unknown>): {
6
- pose(pose: CharacterPose, appearance: CharacterAppearance): Promise<string>;
7
- rig(pose: string, appearance: CharacterAppearance): Promise<CharacterRig | undefined>;
8
- };
9
- export declare const characterAssets: {
10
- pose(pose: CharacterPose, appearance: CharacterAppearance): Promise<string>;
11
- rig(pose: string, appearance: CharacterAppearance): Promise<CharacterRig | undefined>;
12
- };
13
- //# sourceMappingURL=character-assets.d.ts.map
@@ -1,26 +0,0 @@
1
- export const CHARACTER_POSE_NAMES = ['seated', 'peek', 'chin', 'doze', 'halfpeek'];
2
- export function createCharacterAssetLoader(importChunk) {
3
- const cache = new Map();
4
- function load(path) {
5
- let promise = cache.get(path);
6
- if (!promise) {
7
- promise = Promise.resolve().then(() => importChunk(path)).catch(error => { cache.delete(path); throw error; });
8
- cache.set(path, promise);
9
- }
10
- return promise;
11
- }
12
- return {
13
- async pose(pose, appearance) {
14
- const module = await load(`./client.pose-${pose}-${appearance}.js`);
15
- return module.image;
16
- },
17
- async rig(pose, appearance) {
18
- if (pose === 'doze' || !CHARACTER_POSE_NAMES.includes(pose))
19
- return undefined;
20
- const module = await load(`./client.rig-${pose}-${appearance}.js`);
21
- return module.rig;
22
- },
23
- };
24
- }
25
- export const characterAssets = createCharacterAssetLoader(path => require.async(path));
26
- //# sourceMappingURL=character-assets.js.map
@@ -1,4 +0,0 @@
1
- /** Fast closure, short hold, relaxed opening; zero velocity at each join. */
2
- export declare function blinkClosure(progress: number): number;
3
- export declare function attentionBlink(progress: number): number;
4
- //# sourceMappingURL=character-expression.d.ts.map
@@ -1,15 +0,0 @@
1
- const smooth = (t) => { const x = Math.max(0, Math.min(1, t)); return x * x * (3 - 2 * x); };
2
- /** Fast closure, short hold, relaxed opening; zero velocity at each join. */
3
- export function blinkClosure(progress) {
4
- if (progress <= 0 || progress >= 1)
5
- return 0;
6
- if (progress < .3)
7
- return smooth(progress / .3);
8
- if (progress < .4)
9
- return 1;
10
- return 1 - smooth((progress - .4) / .6);
11
- }
12
- export function attentionBlink(progress) {
13
- return blinkClosure((progress - .12) / .16);
14
- }
15
- //# sourceMappingURL=character-expression.js.map
@@ -1,13 +0,0 @@
1
- import type { EyeRig } from './character-eye-rig.js';
2
- /** Quadratic lid ordinate at a horizontal art-space coordinate. */
3
- export declare function lidY(eye: EyeRig, control: readonly [number, number], x: number): number;
4
- /** Cache original ink and a locally textured skin plate, never read pixels per frame. */
5
- export declare function prepareEyeMaterial(doc: Document, source: CanvasRenderingContext2D, eye: EyeRig): {
6
- ink: HTMLCanvasElement;
7
- skin: HTMLCanvasElement;
8
- left: number;
9
- top: number;
10
- width: number;
11
- height: number;
12
- };
13
- //# sourceMappingURL=character-eye-material.d.ts.map
@@ -1,88 +0,0 @@
1
- /** Quadratic lid ordinate at a horizontal art-space coordinate. */
2
- export function lidY(eye, control, x) {
3
- let low = 0, high = 1;
4
- for (let i = 0; i < 14; i++) {
5
- const t = (low + high) / 2, s = 1 - t;
6
- if (s * s * eye.left[0] + 2 * s * t * control[0] + t * t * eye.right[0] < x)
7
- low = t;
8
- else
9
- high = t;
10
- }
11
- const t = (low + high) / 2, s = 1 - t;
12
- return s * s * eye.left[1] + 2 * s * t * control[1] + t * t * eye.right[1];
13
- }
14
- /** Cache original ink and a locally textured skin plate, never read pixels per frame. */
15
- export function prepareEyeMaterial(doc, source, eye) {
16
- const left = Math.floor(eye.left[0] - 9), right = Math.ceil(eye.right[0] + 9);
17
- const top = Math.floor(Math.min(eye.upper[1], eye.left[1], eye.right[1]) - 5);
18
- const bottom = Math.ceil(Math.max(eye.lower[1], eye.left[1], eye.right[1]) + 5);
19
- const width = right - left, height = bottom - top;
20
- const canvas = () => { const c = doc.createElement('canvas'); c.width = width; c.height = height; return c; };
21
- const ink = canvas(), skin = canvas(), mask = canvas();
22
- const ic = ink.getContext('2d'), sc = skin.getContext('2d'), mc = mask.getContext('2d');
23
- const pixels = source.getImageData(0, 0, source.canvas.width, source.canvas.height);
24
- const pixel = (x, y) => {
25
- 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;
26
- return [pixels.data[offset], pixels.data[offset + 1], pixels.data[offset + 2]];
27
- };
28
- const ref = pixel(...eye.skin), output = ic.createImageData(width, height), plate = sc.createImageData(width, height);
29
- const isSkin = (c) => c.every((v, i) => Math.abs(v - ref[i]) < 25);
30
- const skinAt = (x, y, c) => {
31
- if (isSkin(c))
32
- return c;
33
- const sum = [ref[0] * .05, ref[1] * .05, ref[2] * .05];
34
- let weight = .05;
35
- // Inpaint from nearby matching face pixels, preserving local lighting.
36
- // Source hair, green iris and dark ink cannot bleed into the skin plate.
37
- for (const radius of [6, 12, 20, 30])
38
- for (let i = 0; i < 8; i++) {
39
- const p = pixel(x + Math.cos(i * Math.PI / 4) * radius, y + Math.sin(i * Math.PI / 4) * radius);
40
- if (!isSkin(p))
41
- continue;
42
- const w = 1 / radius;
43
- weight += w;
44
- for (let k = 0; k < 3; k++)
45
- sum[k] += p[k] * w;
46
- }
47
- return sum.map(v => Math.round(v / weight));
48
- };
49
- for (let x = 0; x < width; x++) {
50
- const ax = left + x, upper = lidY(eye, eye.upper, ax);
51
- // Restrict extraction to the upper lash: exclude eyebrow, pupil and hair.
52
- for (let y = 0; y < height; y++) {
53
- const ay = top + y, c = pixel(ax, ay), index = (y * width + x) * 4;
54
- const darkness = 1 - (c[0] + c[1] + c[2]) / Math.max(1, ref[0] + ref[1] + ref[2]);
55
- const green = c[1] > c[0] * 1.15 && c[1] > c[2] * 1.12;
56
- const inBand = ay >= upper - 12 && ay <= upper + 3;
57
- const alpha = inBand && !green ? Math.max(0, Math.min(1, (darkness - .3) / .35)) : 0;
58
- output.data.set([...c, Math.round(alpha * 255)], index);
59
- plate.data.set([...skinAt(ax, ay, c), 255], index);
60
- }
61
- }
62
- ic.putImageData(output, 0, 0);
63
- sc.putImageData(plate, 0, 0);
64
- // Opaque coverage of extracted ink, including its antialiased fringe. Using
65
- // the ink's original alpha here leaves a second, stationary lash behind.
66
- const inkMask = mc.createImageData(width, height);
67
- for (let i = 0; i < output.data.length; i += 4)
68
- inkMask.data[i + 3] = output.data[i + 3] > 4 ? 255 : 0;
69
- mc.putImageData(inkMask, 0, 0);
70
- const expanded = canvas(), ec = expanded.getContext('2d');
71
- for (const [dx, dy] of [[0, 0], [-1, 0], [1, 0], [0, -1], [0, 1]])
72
- ec.drawImage(mask, dx, dy);
73
- mc.drawImage(expanded, 0, 0);
74
- mc.translate(-left, -top);
75
- mc.beginPath();
76
- mc.moveTo(eye.left[0] - 7, eye.left[1]);
77
- mc.quadraticCurveTo(eye.upper[0], eye.upper[1] - 4, eye.right[0] + 7, eye.right[1]);
78
- mc.quadraticCurveTo(eye.lower[0], eye.lower[1] + 20, eye.left[0] - 7, eye.left[1]);
79
- mc.closePath();
80
- mc.fill();
81
- // Feather only a subpixel edge, not the eye or the whole character.
82
- sc.globalCompositeOperation = 'destination-in';
83
- sc.filter = 'blur(0.65px)';
84
- sc.drawImage(mask, 0, 0);
85
- sc.filter = 'none';
86
- return { ink, skin, left, top, width, height };
87
- }
88
- //# sourceMappingURL=character-eye-material.js.map
@@ -1,5 +0,0 @@
1
- import { type EyeRig } from './character-eye-rig.js';
2
- import type { Point } from './character-motion-types.js';
3
- /** Prepare tiny iris surfaces once. Never shift a rectangular face/eye patch. */
4
- export declare function createEyeRenderer(ctx: CanvasRenderingContext2D, body: HTMLImageElement, eyes: readonly EyeRig[]): (gaze: Point, closure: number) => void;
5
- //# sourceMappingURL=character-eye-renderer.d.ts.map
@@ -1,80 +0,0 @@
1
- import { clampGaze, eyelidControls } from './character-eye-rig.js';
2
- import { lidY, prepareEyeMaterial } from './character-eye-material.js';
3
- /** Prepare tiny iris surfaces once. Never shift a rectangular face/eye patch. */
4
- export function createEyeRenderer(ctx, body, eyes) {
5
- const doc = ctx.canvas.ownerDocument;
6
- const source = doc.createElement('canvas');
7
- source.width = body.width;
8
- source.height = body.height;
9
- const sample = source.getContext('2d', { willReadFrequently: true });
10
- sample.drawImage(body, 0, 0);
11
- const color = (p) => {
12
- const data = sample.getImageData(Math.round(p[0] / 2), Math.round(p[1] / 2), 1, 1).data;
13
- return [data[0], data[1], data[2]];
14
- };
15
- const rgb = (c) => `rgb(${c.map(v => Math.round(v)).join(' ')})`;
16
- const prepared = eyes.map(eye => {
17
- const [cx, cy, rx, ry, angle] = eye.iris;
18
- // Full-size local surface preserves source texels when moved fractionally.
19
- const iris = doc.createElement('canvas');
20
- iris.width = Math.ceil(rx * 2 + 4);
21
- iris.height = Math.ceil(ry * 2 + 4);
22
- const ic = iris.getContext('2d');
23
- ic.translate(iris.width / 2, iris.height / 2);
24
- ic.rotate(angle);
25
- ic.beginPath();
26
- ic.ellipse(0, 0, rx, ry, 0, 0, Math.PI * 2);
27
- ic.clip();
28
- ic.rotate(-angle);
29
- ic.drawImage(body, 0, 0, body.width, body.height, -cx, -cy, body.width * 2, body.height * 2);
30
- return { eye, iris, skin: color(eye.skin), material: prepareEyeMaterial(doc, sample, eye) };
31
- });
32
- function aperture(eye, closure) {
33
- const { upper, lower, left, right } = eyelidControls(eye, closure);
34
- ctx.beginPath();
35
- ctx.moveTo(...left);
36
- ctx.quadraticCurveTo(...upper, ...right);
37
- ctx.quadraticCurveTo(...lower, ...left);
38
- ctx.closePath();
39
- }
40
- return (gaze, closure) => {
41
- const [gx, gy] = clampGaze(...gaze);
42
- ctx.save();
43
- ctx.scale(.5, .5);
44
- for (const { eye, iris, skin, material } of prepared) {
45
- const [cx, cy, rx, ry] = eye.iris;
46
- ctx.save();
47
- ctx.drawImage(material.skin, material.left, material.top);
48
- ctx.save();
49
- aperture(eye, 0);
50
- ctx.clip();
51
- if (closure < .999) {
52
- ctx.save();
53
- aperture(eye, closure);
54
- ctx.clip();
55
- const shade = ctx.createLinearGradient(0, cy - ry, 0, cy + ry);
56
- const brightness = (skin[0] + skin[1] + skin[2]) / 765;
57
- const white = brightness > .65 ? [255, 253, 241] : [166, 185, 193];
58
- shade.addColorStop(0, rgb(white.map(c => c * .83)));
59
- shade.addColorStop(.5, rgb(white));
60
- ctx.fillStyle = shade;
61
- ctx.fillRect(cx - 70, cy - 70, 140, 140);
62
- const dx = gx * rx * .26, dy = gy * ry * .13;
63
- ctx.drawImage(iris, cx - iris.width / 2 + dx, cy - iris.height / 2 + dy);
64
- ctx.restore();
65
- }
66
- ctx.restore();
67
- const lids = eyelidControls(eye, closure);
68
- // Move narrow strips of the original brushwork, preserving lash thickness.
69
- // Neither a replacement stroke nor an opacity swap is used for blinking.
70
- for (let x = 0; x < material.width; x++) {
71
- const ax = material.left + x;
72
- const dy = lidY({ ...eye, left: lids.left, right: lids.right }, lids.upper, ax) - lidY(eye, eye.upper, ax);
73
- ctx.drawImage(material.ink, x, 0, 1, material.height, ax, material.top + dy, 1, material.height);
74
- }
75
- ctx.restore();
76
- }
77
- ctx.restore();
78
- };
79
- }
80
- //# sourceMappingURL=character-eye-renderer.js.map
@@ -1,20 +0,0 @@
1
- import type { Point } from './character-motion-types.js';
2
- /** Landmarks in the approved full-resolution artwork, not screen coordinates. */
3
- export interface EyeRig {
4
- left: Point;
5
- right: Point;
6
- upper: Point;
7
- lower: Point;
8
- iris: readonly [number, number, number, number, number];
9
- skin: Point;
10
- }
11
- export declare const EYE_RIGS: Readonly<Record<string, readonly EyeRig[]>>;
12
- export declare function clampGaze(x: number, y: number): Point;
13
- /** Both lids meet on the same gently curved lash line, never a rectangular wipe. */
14
- export declare function eyelidControls(eye: EyeRig, closure: number): {
15
- upper: Point;
16
- lower: Point;
17
- left: Point;
18
- right: Point;
19
- };
20
- //# sourceMappingURL=character-eye-rig.d.ts.map
@@ -1,35 +0,0 @@
1
- export const EYE_RIGS = {
2
- seated: [
3
- { left: [285, 223], right: [326, 224], upper: [302, 188], lower: [310, 276], iris: [309, 230, 13, 20, 0], skin: [315, 258] },
4
- { left: [373, 221], right: [421, 226], upper: [387, 186], lower: [393, 277], iris: [393, 228, 15, 22, 0], skin: [391, 261] },
5
- ],
6
- peek: [
7
- { left: [80, 214], right: [119, 221], upper: [103, 186], lower: [91, 267], iris: [102, 225, 14, 20, .25], skin: [110, 253] },
8
- { left: [171, 252], right: [210, 265], upper: [195, 221], lower: [175, 300], iris: [186, 260, 15, 20, .4], skin: [186, 288] },
9
- ],
10
- chin: [
11
- { left: [252, 252], right: [290, 247], upper: [266, 223], lower: [287, 297], iris: [278, 258, 14, 19, -.3], skin: [291, 285] },
12
- { left: [339, 234], right: [387, 227], upper: [358, 196], lower: [365, 280], iris: [361, 235, 15, 21, -.25], skin: [367, 269] },
13
- ],
14
- halfpeek: [
15
- { left: [89, 390], right: [159, 395], upper: [116, 337], lower: [110, 471], iris: [116, 401, 23, 30, .22], skin: [117, 445] },
16
- ],
17
- };
18
- export function clampGaze(x, y) {
19
- if (!Number.isFinite(x) || !Number.isFinite(y))
20
- return [0, 0];
21
- const length = Math.max(1, Math.hypot(x, y));
22
- return [x / length, y / length];
23
- }
24
- /** Both lids meet on the same gently curved lash line, never a rectangular wipe. */
25
- export function eyelidControls(eye, closure) {
26
- const t = Math.max(0, Math.min(1, closure));
27
- // Upper lid does most of the travel. Closing at the eye's centre looked like
28
- // a squint; the resting lash sits near the lower rim instead.
29
- const cornerY = (eye.left[1] + eye.right[1]) / 2;
30
- const drop = (eye.lower[1] - cornerY) * .24;
31
- const middle = [(eye.left[0] + eye.right[0]) / 2, cornerY + drop + (eye.right[0] - eye.left[0]) * .18];
32
- const lerp = (p) => [p[0] + (middle[0] - p[0]) * t, p[1] + (middle[1] - p[1]) * t];
33
- return { upper: lerp(eye.upper), lower: lerp(eye.lower), left: [eye.left[0], eye.left[1] + drop * t], right: [eye.right[0], eye.right[1] + drop * t] };
34
- }
35
- //# sourceMappingURL=character-eye-rig.js.map
@@ -1,16 +0,0 @@
1
- import type { Point } from './character-motion-types.js';
2
- interface GazeClock {
3
- requestAnimationFrame(callback: (time: number) => void): number;
4
- cancelAnimationFrame(handle: number): void;
5
- performance: {
6
- now(): number;
7
- };
8
- }
9
- /** Event-driven damping. At rest there are no queued frames or polling. */
10
- export declare function createCharacterGaze(clock: GazeClock, changed: (point: Point) => void): {
11
- target(x: number, y: number): void;
12
- reset(): void;
13
- dispose(): void;
14
- };
15
- export {};
16
- //# sourceMappingURL=character-gaze.d.ts.map
@@ -1,36 +0,0 @@
1
- import { clampGaze } from './character-eye-rig.js';
2
- /** Event-driven damping. At rest there are no queued frames or polling. */
3
- export function createCharacterGaze(clock, changed) {
4
- let value = [0, 0], target = [0, 0], frame, last = 0, disposed = false;
5
- function step(now) {
6
- frame = undefined;
7
- if (disposed)
8
- return;
9
- const dt = Math.min(64, Math.max(0, now - last));
10
- last = now;
11
- const amount = 1 - Math.exp(-dt / 95);
12
- value = [value[0] + (target[0] - value[0]) * amount, value[1] + (target[1] - value[1]) * amount];
13
- const settled = Math.hypot(value[0] - target[0], value[1] - target[1]) < .001;
14
- if (settled)
15
- value = target;
16
- changed(value);
17
- if (!settled)
18
- frame = clock.requestAnimationFrame(step);
19
- }
20
- function stop() { if (frame !== undefined)
21
- clock.cancelAnimationFrame(frame); frame = undefined; }
22
- return {
23
- target(x, y) {
24
- if (disposed)
25
- return;
26
- target = clampGaze(x, y);
27
- if (frame === undefined && Math.hypot(value[0] - target[0], value[1] - target[1]) >= .001) {
28
- last = clock.performance.now();
29
- frame = clock.requestAnimationFrame(step);
30
- }
31
- },
32
- reset() { stop(); target = value = [0, 0]; changed(value); },
33
- dispose() { disposed = true; stop(); },
34
- };
35
- }
36
- //# sourceMappingURL=character-gaze.js.map
@@ -1,17 +0,0 @@
1
- export type CharacterIdlePose = 'seated' | 'chin' | 'doze';
2
- export declare const CHARACTER_CHIN_DELAY = 30000;
3
- export declare const CHARACTER_DOZE_DELAY = 180000;
4
- interface IdleClock {
5
- now(): number;
6
- setTimeout(callback: () => void, delay: number): number;
7
- clearTimeout(handle: number): void;
8
- }
9
- /** One pending deadline, no polling, no model/session state or persisted user data. */
10
- export declare function createCharacterIdleController(clock: IdleClock, changed: (pose: CharacterIdlePose) => void): {
11
- readonly pose: CharacterIdlePose;
12
- activity(): void;
13
- setPaused(value: boolean): void;
14
- dispose(): void;
15
- };
16
- export {};
17
- //# sourceMappingURL=character-idle.d.ts.map
@@ -1,65 +0,0 @@
1
- export const CHARACTER_CHIN_DELAY = 30_000;
2
- export const CHARACTER_DOZE_DELAY = 180_000;
3
- /** One pending deadline, no polling, no model/session state or persisted user data. */
4
- export function createCharacterIdleController(clock, changed) {
5
- let pose = 'seated';
6
- let lastActivity = clock.now();
7
- let timer;
8
- let paused = true;
9
- let disposed = false;
10
- const clear = () => {
11
- if (timer !== undefined)
12
- clock.clearTimeout(timer);
13
- timer = undefined;
14
- };
15
- const update = (next) => {
16
- if (next !== pose) {
17
- pose = next;
18
- changed(next);
19
- }
20
- };
21
- const schedule = () => {
22
- clear();
23
- if (paused || disposed || pose === 'doze')
24
- return;
25
- const deadline = pose === 'seated' ? CHARACTER_CHIN_DELAY : CHARACTER_DOZE_DELAY;
26
- timer = clock.setTimeout(tick, Math.max(1, lastActivity + deadline - clock.now()));
27
- };
28
- function tick() {
29
- timer = undefined;
30
- if (paused || disposed)
31
- return;
32
- const elapsed = clock.now() - lastActivity;
33
- update(elapsed >= CHARACTER_DOZE_DELAY ? 'doze' : elapsed >= CHARACTER_CHIN_DELAY ? 'chin' : 'seated');
34
- schedule();
35
- }
36
- return {
37
- get pose() { return pose; },
38
- activity() {
39
- if (paused || disposed)
40
- return;
41
- const now = clock.now();
42
- // Pointer events can fire at 120 Hz. Update a timestamp at most once per
43
- // second; never reset a timeout on each pointer movement or keystroke.
44
- if (pose === 'seated' && now - lastActivity < 1000)
45
- return;
46
- lastActivity = now;
47
- if (pose !== 'seated') {
48
- update('seated');
49
- schedule();
50
- }
51
- },
52
- setPaused(value) {
53
- if (disposed || value === paused)
54
- return;
55
- paused = value;
56
- clear();
57
- lastActivity = clock.now();
58
- update('seated');
59
- if (!paused)
60
- schedule();
61
- },
62
- dispose() { disposed = true; clear(); },
63
- };
64
- }
65
- //# sourceMappingURL=character-idle.js.map
@@ -1,24 +0,0 @@
1
- /** Pure placement policy: decoration may only occupy space outside message bounds. */
2
- export interface CharacterRect {
3
- left: number;
4
- top: number;
5
- right: number;
6
- bottom: number;
7
- }
8
- export interface CharacterLayoutInput {
9
- sidebar: CharacterRect;
10
- scroll: CharacterRect;
11
- flow: CharacterRect;
12
- viewportWidth: number;
13
- viewportHeight: number;
14
- controls: CharacterRect[];
15
- }
16
- export interface CharacterPlacement {
17
- pose: 'seated' | 'peek' | 'halfpeek' | 'hidden';
18
- left: number;
19
- top: number;
20
- width: number;
21
- height: number;
22
- }
23
- export declare function resolveCharacterPlacement(input: CharacterLayoutInput): CharacterPlacement;
24
- //# sourceMappingURL=character-layout.d.ts.map
@@ -1,51 +0,0 @@
1
- const hidden = { pose: 'hidden', left: 0, top: 0, width: 0, height: 0 };
2
- const SEATED_RATIO = 0.71;
3
- const PEEK_RATIO = 0.63;
4
- const HALF_PEEK_RATIO = 0.44;
5
- export function resolveCharacterPlacement(input) {
6
- const { sidebar, scroll, flow, viewportWidth, viewportHeight, controls } = input;
7
- // Wallpaper belongs to the viewport, not the welcome/composer dock. Reserve
8
- // the same top/bottom chrome space before and after the first message.
9
- const bottom = Math.min(sidebar.bottom, viewportHeight) - 148;
10
- const top = Math.max(sidebar.top + 24, 96);
11
- if (bottom - top < 130 || sidebar.right > flow.left)
12
- return hidden;
13
- const rightStart = flow.right + 56;
14
- const seatedWidth = Math.min(250, scroll.right - rightStart - 20, (bottom - top) * SEATED_RATIO, 340 * SEATED_RATIO);
15
- if (seatedWidth >= 190) {
16
- const height = seatedWidth / SEATED_RATIO;
17
- return { pose: 'seated', left: scroll.right - seatedWidth - 16,
18
- top: top + (bottom - top - height) * 0.6, width: seatedWidth, height };
19
- }
20
- const avatarInset = viewportWidth > 760 ? 52 : 0;
21
- const freeGap = flow.left - avatarInset - sidebar.right;
22
- if (freeGap >= 66) {
23
- const width = Math.min(104, freeGap - 12, (bottom - top) * PEEK_RATIO);
24
- const height = width / PEEK_RATIO;
25
- return { pose: 'peek', left: sidebar.right - 6,
26
- top: top + Math.min(160, (bottom - top - height) * 0.5), width, height };
27
- }
28
- // A collapsed rail has a large blank region between navigation and Settings.
29
- // Use that region only after excluding every real interactive control.
30
- if (sidebar.right - sidebar.left > 100)
31
- return hidden;
32
- const left = sidebar.left + 7;
33
- const width = Math.min(60, flow.left - avatarInset - left - 10);
34
- if (width < 40)
35
- return hidden;
36
- const height = width / HALF_PEEK_RATIO;
37
- const blocked = controls.filter(r => r.right > left && r.left < left + width)
38
- .map(r => ({ start: Math.max(top, r.top - 12), end: Math.min(bottom, r.bottom + 12) }))
39
- .filter(r => r.end > r.start).sort((a, b) => a.start - b.start);
40
- let cursor = top;
41
- let best = { start: top, end: top };
42
- for (const block of [...blocked, { start: bottom, end: bottom }]) {
43
- if (block.start - cursor > best.end - best.start)
44
- best = { start: cursor, end: block.start };
45
- cursor = Math.max(cursor, block.end);
46
- }
47
- if (best.end - best.start < height + 12)
48
- return hidden;
49
- return { pose: 'halfpeek', left, top: best.start + (best.end - best.start - height) * 0.45, width, height };
50
- }
51
- //# sourceMappingURL=character-layout.js.map
@@ -1,18 +0,0 @@
1
- interface Clock {
2
- setTimeout(callback: () => void, delay: number): number;
3
- clearTimeout(handle: number): void;
4
- performance: {
5
- now(): number;
6
- };
7
- }
8
- export interface CharacterMotionClip {
9
- duration: number;
10
- }
11
- /** One deadline; 60 fps only during a short gesture, no idle frame loop. */
12
- export declare function createCharacterMotionClock(clock: Clock, changed: (action: string | undefined, progress: number) => void, random?: () => number): {
13
- setClips(next: Readonly<Record<string, CharacterMotionClip>> | undefined): void;
14
- attention(): void;
15
- dispose(): void;
16
- };
17
- export {};
18
- //# sourceMappingURL=character-motion-clock.d.ts.map
@@ -1,70 +0,0 @@
1
- /** One deadline; 60 fps only during a short gesture, no idle frame loop. */
2
- export function createCharacterMotionClock(clock, changed, random = Math.random) {
3
- let timer, disposed = false, active = '';
4
- let clips;
5
- let blinksSinceEar = 0, nextEar = 0;
6
- const clear = () => { if (timer !== undefined)
7
- clock.clearTimeout(timer); timer = undefined; };
8
- function rest() {
9
- active = '';
10
- changed(undefined, 0);
11
- if (clips && !disposed)
12
- timer = clock.setTimeout(start, 2800 + random() * 2800);
13
- }
14
- function play(action) {
15
- const clip = clips?.[action];
16
- if (!clip || disposed)
17
- return;
18
- clear();
19
- active = action;
20
- const started = clock.performance.now();
21
- const step = () => {
22
- timer = undefined;
23
- if (!clips || disposed)
24
- return;
25
- const t = Math.min(1, (clock.performance.now() - started) / clip.duration);
26
- changed(active, t);
27
- if (t >= 1) {
28
- rest();
29
- return;
30
- }
31
- timer = clock.setTimeout(step, 1000 / 60);
32
- };
33
- step();
34
- }
35
- function start() {
36
- timer = undefined;
37
- if (!clips || disposed)
38
- return;
39
- const gestures = Object.keys(clips).filter(name => name !== 'blink' && name !== 'attention');
40
- const ears = gestures.filter(name => name.startsWith('ear'));
41
- // Random selection alone can starve ear motion indefinitely. Keep a quiet,
42
- // bounded cadence, alternating ears instead of twitching both in lockstep.
43
- const action = ears.length && blinksSinceEar >= 2 ? ears[nextEar++ % ears.length]
44
- : (random() < .72 && clips.blink) || !gestures.length ? 'blink'
45
- : gestures[Math.floor(random() * gestures.length)];
46
- if (action.startsWith('ear'))
47
- blinksSinceEar = 0;
48
- else
49
- blinksSinceEar++;
50
- play(action);
51
- }
52
- return {
53
- setClips(next) {
54
- if (disposed || next === clips)
55
- return;
56
- clear();
57
- clips = next;
58
- active = '';
59
- blinksSinceEar = 0;
60
- nextEar = 0;
61
- changed(undefined, 0);
62
- if (clips)
63
- timer = clock.setTimeout(start, 1200 + random() * 800);
64
- },
65
- attention() { if (!active)
66
- play('attention'); },
67
- dispose() { disposed = true; clear(); clips = undefined; changed(undefined, 0); },
68
- };
69
- }
70
- //# sourceMappingURL=character-motion-clock.js.map
@@ -1,12 +0,0 @@
1
- export type Point = readonly [number, number];
2
- export interface CharacterLayer {
3
- readonly src: string;
4
- readonly box: readonly [number, number, number, number];
5
- }
6
- export interface CharacterRig {
7
- readonly layers: Readonly<Record<string, CharacterLayer>>;
8
- readonly ears: readonly Point[];
9
- readonly size: Point;
10
- }
11
- export type CharacterMotionCatalog = Readonly<Record<string, Readonly<Record<'light' | 'dark', CharacterRig>>>>;
12
- //# sourceMappingURL=character-motion-types.d.ts.map
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=character-motion-types.js.map
@@ -1,6 +0,0 @@
1
- /** Owns loading and lifecycle only. Rig math and painting stay independent. */
2
- export declare function createCharacterMotion(doc: Document): {
3
- setPose(nextPart: HTMLElement | undefined, nextPose: string): void;
4
- dispose(): void;
5
- };
6
- //# sourceMappingURL=character-motion.d.ts.map