@lemoncat7/dsh-theme-xiaohei 0.3.0-alpha.23 → 0.3.2

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 (222) hide show
  1. package/README.md +39 -17
  2. package/THIRD_PARTY_NOTICES.md +41 -0
  3. package/lib/appearance.js +26 -10
  4. package/lib/blink.js +3 -3
  5. package/lib/bloub-heixiu/ambient.d.ts +3 -0
  6. package/lib/bloub-heixiu/ambient.js +12 -0
  7. package/lib/bloub-heixiu/character.d.ts +33 -0
  8. package/lib/bloub-heixiu/character.js +124 -0
  9. package/lib/bloub-heixiu/effects.d.ts +4 -0
  10. package/lib/bloub-heixiu/effects.js +91 -0
  11. package/lib/bloub-heixiu/flight.d.ts +15 -0
  12. package/lib/bloub-heixiu/flight.js +66 -0
  13. package/lib/bloub-heixiu/follow.d.ts +13 -0
  14. package/lib/bloub-heixiu/follow.js +23 -0
  15. package/lib/bloub-heixiu/layout.d.ts +18 -0
  16. package/lib/bloub-heixiu/layout.js +12 -0
  17. package/lib/bloub-heixiu/renderer.d.ts +16 -0
  18. package/lib/bloub-heixiu/renderer.js +94 -0
  19. package/lib/bloub-heixiu/runtime.d.ts +6 -0
  20. package/lib/bloub-heixiu/runtime.js +395 -0
  21. package/lib/bloub-heixiu/signals.d.ts +17 -0
  22. package/lib/bloub-heixiu/signals.js +53 -0
  23. package/lib/bloub-heixiu/styles.d.ts +2 -0
  24. package/lib/bloub-heixiu/styles.js +98 -0
  25. package/lib/chrome/accessibility.d.ts +1 -1
  26. package/lib/chrome/accessibility.js +5 -13
  27. package/lib/chrome/composer-avatar.d.ts +10 -0
  28. package/lib/chrome/composer-avatar.js +599 -0
  29. package/lib/chrome/composer-send-heixiu.d.ts +3 -0
  30. package/lib/chrome/composer-send-heixiu.js +143 -0
  31. package/lib/chrome/composer.d.ts +2 -2
  32. package/lib/chrome/composer.js +84 -95
  33. package/lib/chrome/controls.d.ts +8 -0
  34. package/lib/chrome/controls.js +348 -0
  35. package/lib/chrome/conversation-messages.d.ts +1 -1
  36. package/lib/chrome/conversation-messages.js +207 -9
  37. package/lib/chrome/conversation.d.ts +1 -1
  38. package/lib/chrome/conversation.js +6 -0
  39. package/lib/chrome/identity.js +4 -18
  40. package/lib/chrome/portal.d.ts +1 -1
  41. package/lib/chrome/portal.js +1 -1
  42. package/lib/chrome/sidebar.d.ts +1 -1
  43. package/lib/chrome/sidebar.js +134 -153
  44. package/lib/chrome/tokens.d.ts +1 -1
  45. package/lib/chrome/tokens.js +121 -68
  46. package/lib/chrome/workspace.d.ts +2 -2
  47. package/lib/chrome/workspace.js +35 -254
  48. package/lib/chrome.js +4 -0
  49. package/lib/client.d.ts +5 -12
  50. package/lib/client.js +6262 -4455
  51. package/lib/composer-avatar.d.ts +18 -0
  52. package/lib/composer-avatar.js +441 -0
  53. package/lib/composer-send-heixiu.d.ts +8 -0
  54. package/lib/composer-send-heixiu.js +133 -0
  55. package/lib/gaze.js +3 -3
  56. package/lib/generated-backgrounds.d.ts +14 -0
  57. package/lib/generated-backgrounds.js +14 -0
  58. package/lib/generated-character-motion.d.ts +4 -0
  59. package/lib/generated-character-motion.js +2 -0
  60. package/lib/generated-identity.d.ts +1 -0
  61. package/lib/generated-identity.js +1 -0
  62. package/lib/generated-keyart.d.ts +18 -3
  63. package/lib/generated-keyart.js +18 -3
  64. package/lib/host-contract.d.ts +9 -0
  65. package/lib/host-contract.js +13 -0
  66. package/lib/host-dom.d.ts +11 -0
  67. package/lib/host-dom.js +60 -0
  68. package/lib/loading-heixiu.js +3 -3
  69. package/lib/metallic-brand-mark.d.ts +10 -0
  70. package/lib/metallic-brand-mark.js +323 -0
  71. package/lib/plugin.d.ts +1 -1
  72. package/lib/plugin.js +31 -16
  73. package/lib/portal.js +11 -10
  74. package/lib/reactions.js +3 -3
  75. package/lib/scene/avatar-model.d.ts +6 -0
  76. package/lib/scene/avatar-model.js +646 -0
  77. package/lib/scene/avatar-motion.d.ts +3 -0
  78. package/lib/scene/avatar-motion.js +799 -0
  79. package/lib/scene/character-idle.d.ts +17 -0
  80. package/lib/scene/character-idle.js +65 -0
  81. package/lib/scene/character-layout.d.ts +24 -0
  82. package/lib/scene/character-layout.js +51 -0
  83. package/lib/scene/character-motion-clock.d.ts +18 -0
  84. package/lib/scene/character-motion-clock.js +57 -0
  85. package/lib/scene/character-motion-types.d.ts +12 -0
  86. package/lib/scene/character-motion-types.js +2 -0
  87. package/lib/scene/character-motion.d.ts +6 -0
  88. package/lib/scene/character-motion.js +126 -0
  89. package/lib/scene/character-poses.d.ts +26 -0
  90. package/lib/scene/character-poses.js +11 -0
  91. package/lib/scene/character-rig-renderer.d.ts +4 -0
  92. package/lib/scene/character-rig-renderer.js +85 -0
  93. package/lib/scene/character-rig.d.ts +20 -0
  94. package/lib/scene/character-rig.js +53 -0
  95. package/lib/scene/heixiu-feedback.d.ts +1 -1
  96. package/lib/scene/heixiu-feedback.js +0 -17
  97. package/lib/scene/pointer-bridge.d.ts +21 -0
  98. package/lib/scene/pointer-bridge.js +155 -0
  99. package/lib/scene/runtime.d.ts +12 -0
  100. package/lib/scene/runtime.js +87 -0
  101. package/lib/scene/styles.d.ts +7 -0
  102. package/lib/scene/styles.js +75 -0
  103. package/lib/scene/sylva-performance.d.ts +11 -0
  104. package/lib/scene/sylva-performance.js +58 -0
  105. package/lib/scene/wallpaper-character-styles.d.ts +3 -0
  106. package/lib/scene/wallpaper-character-styles.js +53 -0
  107. package/lib/scene/wallpaper-character.d.ts +4 -0
  108. package/lib/scene/wallpaper-character.js +213 -0
  109. package/lib/scene.d.ts +7 -13
  110. package/lib/scene.js +7 -1023
  111. package/lib/sidebar-glass.js +99 -36
  112. package/lib/sidebar-heixiu-roaming.js +5 -5
  113. package/lib/sidebar-reveal.d.ts +8 -0
  114. package/lib/sidebar-reveal.js +51 -0
  115. package/lib/state.d.ts +14 -2
  116. package/lib/state.js +12 -4
  117. package/lib/theme.js +2 -2
  118. package/lib/vendor/bloub/decor.d.ts +114 -0
  119. package/lib/vendor/bloub/decor.js +198 -0
  120. package/lib/vendor/bloub/engine.d.ts +213 -0
  121. package/lib/vendor/bloub/engine.js +452 -0
  122. package/lib/vendor/bloub/expressions.d.ts +35 -0
  123. package/lib/vendor/bloub/expressions.js +143 -0
  124. package/lib/vendor/bloub/eyefit.d.ts +75 -0
  125. package/lib/vendor/bloub/eyefit.js +393 -0
  126. package/lib/vendor/bloub/face.d.ts +63 -0
  127. package/lib/vendor/bloub/face.js +106 -0
  128. package/lib/vendor/bloub/math.d.ts +22 -0
  129. package/lib/vendor/bloub/math.js +36 -0
  130. package/lib/vendor/bloub/profiles.d.ts +8 -0
  131. package/lib/vendor/bloub/profiles.js +20 -0
  132. package/lib/vendor/bloub/repere.d.ts +30 -0
  133. package/lib/vendor/bloub/repere.js +32 -0
  134. package/lib/vendor/bloub/shape.d.ts +80 -0
  135. package/lib/vendor/bloub/shape.js +268 -0
  136. package/lib/vendor/bloub/skins.d.ts +36 -0
  137. package/lib/vendor/bloub/skins.js +74 -0
  138. package/lib/vendor/bloub/states.d.ts +87 -0
  139. package/lib/vendor/bloub/states.js +468 -0
  140. package/package.json +23 -6
  141. package/lib/appearance.d.ts.map +0 -1
  142. package/lib/appearance.js.map +0 -1
  143. package/lib/blink.d.ts.map +0 -1
  144. package/lib/blink.js.map +0 -1
  145. package/lib/boot-appearance.d.ts.map +0 -1
  146. package/lib/boot-appearance.js.map +0 -1
  147. package/lib/boot-loader.d.ts.map +0 -1
  148. package/lib/boot-loader.js.map +0 -1
  149. package/lib/chrome/accessibility.d.ts.map +0 -1
  150. package/lib/chrome/accessibility.js.map +0 -1
  151. package/lib/chrome/blink.d.ts.map +0 -1
  152. package/lib/chrome/blink.js.map +0 -1
  153. package/lib/chrome/composer.d.ts.map +0 -1
  154. package/lib/chrome/composer.js.map +0 -1
  155. package/lib/chrome/conversation-messages.d.ts.map +0 -1
  156. package/lib/chrome/conversation-messages.js.map +0 -1
  157. package/lib/chrome/conversation.d.ts.map +0 -1
  158. package/lib/chrome/conversation.js.map +0 -1
  159. package/lib/chrome/frames.d.ts.map +0 -1
  160. package/lib/chrome/frames.js.map +0 -1
  161. package/lib/chrome/gaze.d.ts.map +0 -1
  162. package/lib/chrome/gaze.js.map +0 -1
  163. package/lib/chrome/identity.d.ts.map +0 -1
  164. package/lib/chrome/identity.js.map +0 -1
  165. package/lib/chrome/overlays.d.ts.map +0 -1
  166. package/lib/chrome/overlays.js.map +0 -1
  167. package/lib/chrome/portal.d.ts.map +0 -1
  168. package/lib/chrome/portal.js.map +0 -1
  169. package/lib/chrome/reactions.d.ts.map +0 -1
  170. package/lib/chrome/reactions.js.map +0 -1
  171. package/lib/chrome/sidebar.d.ts.map +0 -1
  172. package/lib/chrome/sidebar.js.map +0 -1
  173. package/lib/chrome/tokens.d.ts.map +0 -1
  174. package/lib/chrome/tokens.js.map +0 -1
  175. package/lib/chrome/workspace.d.ts.map +0 -1
  176. package/lib/chrome/workspace.js.map +0 -1
  177. package/lib/chrome.d.ts.map +0 -1
  178. package/lib/chrome.js.map +0 -1
  179. package/lib/client.d.ts.map +0 -1
  180. package/lib/client.js.map +0 -7
  181. package/lib/gaze.d.ts.map +0 -1
  182. package/lib/gaze.js.map +0 -1
  183. package/lib/generated-identity.d.ts.map +0 -1
  184. package/lib/generated-identity.js.map +0 -1
  185. package/lib/generated-keyart.d.ts.map +0 -1
  186. package/lib/generated-keyart.js.map +0 -1
  187. package/lib/generated-loading-assets.d.ts.map +0 -1
  188. package/lib/generated-loading-assets.js.map +0 -1
  189. package/lib/generated-sidebar-assets.d.ts +0 -4
  190. package/lib/generated-sidebar-assets.d.ts.map +0 -1
  191. package/lib/generated-sidebar-assets.js +0 -4
  192. package/lib/generated-sidebar-assets.js.map +0 -1
  193. package/lib/heixiu-interactions.d.ts.map +0 -1
  194. package/lib/heixiu-interactions.js.map +0 -1
  195. package/lib/index.d.ts.map +0 -1
  196. package/lib/index.js.map +0 -1
  197. package/lib/loading-heixiu.d.ts.map +0 -1
  198. package/lib/loading-heixiu.js.map +0 -1
  199. package/lib/plugin.d.ts.map +0 -1
  200. package/lib/plugin.js.map +0 -1
  201. package/lib/portal.d.ts.map +0 -1
  202. package/lib/portal.js.map +0 -1
  203. package/lib/reactions.d.ts.map +0 -1
  204. package/lib/reactions.js.map +0 -1
  205. package/lib/scene/heixiu-feedback.d.ts.map +0 -1
  206. package/lib/scene/heixiu-feedback.js.map +0 -1
  207. package/lib/scene/heixiu-interactions.d.ts.map +0 -1
  208. package/lib/scene/heixiu-interactions.js.map +0 -1
  209. package/lib/scene/state-transitions.d.ts.map +0 -1
  210. package/lib/scene/state-transitions.js.map +0 -1
  211. package/lib/scene.d.ts.map +0 -1
  212. package/lib/scene.js.map +0 -1
  213. package/lib/sidebar-glass.d.ts.map +0 -1
  214. package/lib/sidebar-glass.js.map +0 -1
  215. package/lib/sidebar-heixiu-escape.d.ts.map +0 -1
  216. package/lib/sidebar-heixiu-escape.js.map +0 -1
  217. package/lib/sidebar-heixiu-roaming.d.ts.map +0 -1
  218. package/lib/sidebar-heixiu-roaming.js.map +0 -1
  219. package/lib/state.d.ts.map +0 -1
  220. package/lib/state.js.map +0 -1
  221. package/lib/theme.d.ts.map +0 -1
  222. package/lib/theme.js.map +0 -1
@@ -0,0 +1,36 @@
1
+ // Adapted from bloub, Copyright (c) 2026 Jérémy Perret (MIT).
2
+ // See THIRD_PARTY_NOTICES.md for source revision and local changes.
3
+ export const TAU = Math.PI * 2;
4
+ export const clamp = (v, lo = 0, hi = 1) => (v < lo ? lo : v > hi ? hi : v);
5
+ export const lerp = (a, b, t) => a + (b - a) * t;
6
+ /**
7
+ * Mesure sur la video : les transitions sont des ease-out exponentiels, sans
8
+ * depassement du corps. Les seuls effets de ressort sont locaux (le pop de la
9
+ * pastille de notification, l'ouverture des yeux) et sont ecrits directement
10
+ * dans l'etat concerne.
11
+ */
12
+ export const easings = {
13
+ easeOutCubic: (t) => 1 - (1 - t) ** 3,
14
+ easeInOutCubic: (t) => (t < 0.5 ? 4 * t ** 3 : 1 - (-2 * t + 2) ** 3 / 2),
15
+ easeOutQuint: (t) => 1 - (1 - t) ** 5
16
+ };
17
+ /** Bruit 1D periodique : boucle sans couture sur `period`, utile pour la derive du regard. */
18
+ export function loopNoise(t, period, seed = 0) {
19
+ const p = (t / period) * TAU;
20
+ return (0.55 * Math.sin(p + seed) +
21
+ 0.3 * Math.sin(2 * p + seed * 1.7 + 1.1) +
22
+ 0.15 * Math.sin(3 * p + seed * 2.3 + 2.4));
23
+ }
24
+ /** PRNG deterministe (mulberry32) : meme sequence a chaque lecture. */
25
+ export function createRng(seed) {
26
+ let a = seed >>> 0;
27
+ return () => {
28
+ a = (a + 0x6d2b79f5) >>> 0;
29
+ let t = Math.imul(a ^ (a >>> 15), 1 | a);
30
+ t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
31
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
32
+ };
33
+ }
34
+ /** Arrondi court : divise par ~2 le poids des chaines de path generees a 60 fps. */
35
+ export const r2 = (v) => Math.round(v * 100) / 100;
36
+ //# sourceMappingURL=math.js.map
@@ -0,0 +1,8 @@
1
+ export declare const PROFILE_SAMPLES = 64;
2
+ export declare const PROFILES: {
3
+ readonly egg: readonly [0.8369, 0.8424, 0.8497, 0.8585, 0.8674, 0.8775, 0.8878, 0.8983, 0.9089, 0.9185, 0.9288, 0.9374, 0.9445, 0.9504, 0.9543, 0.9559, 0.9555, 0.9519, 0.9466, 0.9389, 0.9302, 0.9193, 0.9085, 0.8969, 0.8852, 0.8734, 0.8625, 0.8513, 0.8411, 0.8325, 0.8243, 0.8179, 0.8137, 0.8112, 0.8102, 0.8128, 0.8178, 0.8262, 0.8374, 0.8518, 0.8702, 0.8922, 0.9169, 0.9446, 0.9741, 1.0023, 1.0267, 1.0433, 1.0481, 1.0393, 1.0216, 0.997, 0.9697, 0.9418, 0.9169, 0.8949, 0.876, 0.8604, 0.849, 0.8394, 0.8337, 0.8314, 0.8305, 0.8326];
4
+ readonly hexagon: readonly [0.921, 0.9282, 0.9441, 0.9706, 0.9984, 1.0059, 0.9896, 0.9562, 0.929, 0.9124, 0.9047, 0.9058, 0.9157, 0.9349, 0.9642, 0.9873, 0.9882, 0.9665, 0.9336, 0.9105, 0.8968, 0.8918, 0.8955, 0.908, 0.9293, 0.9611, 0.982, 0.9812, 0.959, 0.9282, 0.9089, 0.8978, 0.8964, 0.9026, 0.9189, 0.9439, 0.9778, 0.999, 0.9964, 0.9713, 0.9439, 0.9274, 0.9196, 0.9206, 0.9308, 0.9502, 0.9799, 1.0121, 1.0226, 1.0071, 0.9752, 0.951, 0.9366, 0.9316, 0.9351, 0.9485, 0.9711, 1.0026, 1.0213, 1.0155, 0.9863, 0.9547, 0.9347, 0.9232];
5
+ readonly triangle: readonly [0.7819, 0.8211, 0.8747, 0.944, 1.0223, 1.096, 1.1401, 1.134, 1.0808, 1.0047, 0.9265, 0.8603, 0.8104, 0.773, 0.745, 0.7273, 0.7151, 0.7118, 0.7148, 0.7245, 0.7427, 0.768, 0.8037, 0.8518, 0.9148, 0.9876, 1.0583, 1.1073, 1.1109, 1.0667, 0.994, 0.9164, 0.8482, 0.7948, 0.7555, 0.7261, 0.7056, 0.6925, 0.6859, 0.6869, 0.6938, 0.7084, 0.7305, 0.7615, 0.804, 0.8595, 0.9311, 1.0092, 1.0791, 1.1171, 1.1054, 1.0501, 0.9779, 0.905, 0.845, 0.799, 0.7656, 0.7413, 0.7258, 0.716, 0.7146, 0.7204, 0.733, 0.7528];
6
+ };
7
+ export type ProfileName = keyof typeof PROFILES;
8
+ //# sourceMappingURL=profiles.d.ts.map
@@ -0,0 +1,20 @@
1
+ // Adapted from bloub, Copyright (c) 2026 Jérémy Perret (MIT).
2
+ // See THIRD_PARTY_NOTICES.md for source revision and local changes.
3
+ // Profils radiaux r(theta) releves au pixel sur la video de reference.
4
+ // theta = 0 pointe vers la droite et croit dans le sens horaire (y vers le bas).
5
+ // Unite : rayon de la boule au repos = 1.
6
+ //
7
+ // Genere par tools/extract-profiles.py — ne pas editer a la main.
8
+ export const PROFILE_SAMPLES = 64;
9
+ export const PROFILES = {
10
+ // oeuf : meme hauteur que la boule, retreci en largeur
11
+ // image 164, empreinte mesuree 1.647 x 2.000
12
+ egg: [0.8369, 0.8424, 0.8497, 0.8585, 0.8674, 0.8775, 0.8878, 0.8983, 0.9089, 0.9185, 0.9288, 0.9374, 0.9445, 0.9504, 0.9543, 0.9559, 0.9555, 0.9519, 0.9466, 0.9389, 0.9302, 0.9193, 0.9085, 0.8969, 0.8852, 0.8734, 0.8625, 0.8513, 0.8411, 0.8325, 0.8243, 0.8179, 0.8137, 0.8112, 0.8102, 0.8128, 0.8178, 0.8262, 0.8374, 0.8518, 0.8702, 0.8922, 0.9169, 0.9446, 0.9741, 1.0023, 1.0267, 1.0433, 1.0481, 1.0393, 1.0216, 0.9970, 0.9697, 0.9418, 0.9169, 0.8949, 0.8760, 0.8604, 0.8490, 0.8394, 0.8337, 0.8314, 0.8305, 0.8326],
13
+ // hexagone pointe en haut, coins tres arrondis
14
+ // image 174, empreinte mesuree 1.826 x 2.011
15
+ hexagon: [0.9210, 0.9282, 0.9441, 0.9706, 0.9984, 1.0059, 0.9896, 0.9562, 0.9290, 0.9124, 0.9047, 0.9058, 0.9157, 0.9349, 0.9642, 0.9873, 0.9882, 0.9665, 0.9336, 0.9105, 0.8968, 0.8918, 0.8955, 0.9080, 0.9293, 0.9611, 0.9820, 0.9812, 0.9590, 0.9282, 0.9089, 0.8978, 0.8964, 0.9026, 0.9189, 0.9439, 0.9778, 0.9990, 0.9964, 0.9713, 0.9439, 0.9274, 0.9196, 0.9206, 0.9308, 0.9502, 0.9799, 1.0121, 1.0226, 1.0071, 0.9752, 0.9510, 0.9366, 0.9316, 0.9351, 0.9485, 0.9711, 1.0026, 1.0213, 1.0155, 0.9863, 0.9547, 0.9347, 0.9232],
16
+ // triangle pointe en haut, coins tres arrondis
17
+ // image 190, empreinte mesuree 1.995 x 1.884
18
+ triangle: [0.7819, 0.8211, 0.8747, 0.9440, 1.0223, 1.0960, 1.1401, 1.1340, 1.0808, 1.0047, 0.9265, 0.8603, 0.8104, 0.7730, 0.7450, 0.7273, 0.7151, 0.7118, 0.7148, 0.7245, 0.7427, 0.7680, 0.8037, 0.8518, 0.9148, 0.9876, 1.0583, 1.1073, 1.1109, 1.0667, 0.9940, 0.9164, 0.8482, 0.7948, 0.7555, 0.7261, 0.7056, 0.6925, 0.6859, 0.6869, 0.6938, 0.7084, 0.7305, 0.7615, 0.8040, 0.8595, 0.9311, 1.0092, 1.0791, 1.1171, 1.1054, 1.0501, 0.9779, 0.9050, 0.8450, 0.7990, 0.7656, 0.7413, 0.7258, 0.7160, 0.7146, 0.7204, 0.7330, 0.7528],
19
+ };
20
+ //# sourceMappingURL=profiles.js.map
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Le repere de tout ce que le moteur rend.
3
+ *
4
+ * `engine.sample()` sort des coordonnees en unites de viewBox, et ces deux nombres en sont
5
+ * la definition : sans eux, une sortie du moteur ne veut rien dire. Ils vivaient dans
6
+ * `BloubBot.vue`, donc hors d'atteinte — un `<script setup>` n'exporte rien — et
7
+ * `export.ts` en redisait un a la main avec le commentaire qui nommait le probleme.
8
+ *
9
+ * Ils sont ici parce que `src/bot/` est ce qui se lit et se consomme du dehors : le
10
+ * composant Vue est UN client du moteur, pas sa definition.
11
+ */
12
+ /**
13
+ * Rayon de la boule au repos, en unites de viewBox. C'est le `scale` que le composant
14
+ * passe a `BotEngine`.
15
+ *
16
+ * Choisi et non mesure : c'est l'unite de travail. Tout le reste du dossier s'exprime en
17
+ * fractions de ce rayon, ce qui rend les mesures relevees sur la video independantes de la
18
+ * taille d'affichage.
19
+ */
20
+ export declare const RAYON = 100;
21
+ /**
22
+ * Demi-cote du viewBox affiche. La marge au-dela du rayon loge les anneaux.
23
+ *
24
+ * Ce n'est pas une valeur libre : les anneaux de l'orbite et le swoosh de la comete montent
25
+ * a 1,4 fois le rayon, soit 140. Rien ne les borne au runtime — c'est le reglage a la main
26
+ * des tableaux `RINGS` et `SWOOSH` (`decor.ts`) qui les tient sous 158, et un test le
27
+ * verrouille.
28
+ */
29
+ export declare const DEMI_VIEWBOX = 158;
30
+ //# sourceMappingURL=repere.d.ts.map
@@ -0,0 +1,32 @@
1
+ // Adapted from bloub, Copyright (c) 2026 Jérémy Perret (MIT).
2
+ // See THIRD_PARTY_NOTICES.md for source revision and local changes.
3
+ /**
4
+ * Le repere de tout ce que le moteur rend.
5
+ *
6
+ * `engine.sample()` sort des coordonnees en unites de viewBox, et ces deux nombres en sont
7
+ * la definition : sans eux, une sortie du moteur ne veut rien dire. Ils vivaient dans
8
+ * `BloubBot.vue`, donc hors d'atteinte — un `<script setup>` n'exporte rien — et
9
+ * `export.ts` en redisait un a la main avec le commentaire qui nommait le probleme.
10
+ *
11
+ * Ils sont ici parce que `src/bot/` est ce qui se lit et se consomme du dehors : le
12
+ * composant Vue est UN client du moteur, pas sa definition.
13
+ */
14
+ /**
15
+ * Rayon de la boule au repos, en unites de viewBox. C'est le `scale` que le composant
16
+ * passe a `BotEngine`.
17
+ *
18
+ * Choisi et non mesure : c'est l'unite de travail. Tout le reste du dossier s'exprime en
19
+ * fractions de ce rayon, ce qui rend les mesures relevees sur la video independantes de la
20
+ * taille d'affichage.
21
+ */
22
+ export const RAYON = 100;
23
+ /**
24
+ * Demi-cote du viewBox affiche. La marge au-dela du rayon loge les anneaux.
25
+ *
26
+ * Ce n'est pas une valeur libre : les anneaux de l'orbite et le swoosh de la comete montent
27
+ * a 1,4 fois le rayon, soit 140. Rien ne les borne au runtime — c'est le reglage a la main
28
+ * des tableaux `RINGS` et `SWOOSH` (`decor.ts`) qui les tient sous 158, et un test le
29
+ * verrouille.
30
+ */
31
+ export const DEMI_VIEWBOX = 158;
32
+ //# sourceMappingURL=repere.js.map
@@ -0,0 +1,80 @@
1
+ import { type ProfileName } from './profiles.js';
2
+ export interface Point {
3
+ x: number;
4
+ y: number;
5
+ }
6
+ /**
7
+ * Une silhouette = un profil radial r(theta) plus une pose.
8
+ *
9
+ * Tout passe par des profils echantillonnes au MEME nombre d'angles : deux
10
+ * formes quelconques ont donc des points qui se correspondent un a un, et le
11
+ * morphing se reduit a une interpolation lineaire des rayons. C'est ce qui
12
+ * rend les transitions propres sans librairie de morphing de path.
13
+ */
14
+ export interface Silhouette {
15
+ radii: number[];
16
+ /** rotation du profil, en radians */
17
+ rot: number;
18
+ /** decalage du centre, en unites de rayon de boule */
19
+ cx: number;
20
+ cy: number;
21
+ /** squash & stretch, applique en repere ecran (apres rotation) */
22
+ sx: number;
23
+ sy: number;
24
+ }
25
+ export declare function silhouette(name: ProfileName, pose?: Partial<Silhouette>): Silhouette;
26
+ /** Cercle parfait : sert de base neutre (point, bulle, cible de fondu). */
27
+ export declare function circle(radius: number, pose?: Partial<Silhouette>): Silhouette;
28
+ /** Interpolation de deux silhouettes. `out` est reutilise pour eviter d'allouer a 60 fps. */
29
+ export declare function blend(a: Silhouette, b: Silhouette, t: number, out?: Silhouette): Silhouette;
30
+ /** Projette la silhouette en points ecran. `scale` = rayon de la boule en unites de viewBox. */
31
+ export declare function toPoints(s: Silhouette, scale: number, out?: Point[]): Point[];
32
+ /**
33
+ * Polyligne fermee -> cubiques Catmull-Rom.
34
+ *
35
+ * Avec 64 points les tangentes centrees suffisent largement : le contour est
36
+ * lisse au pixel pres meme affiche en 600 px, et la chaine reste courte.
37
+ */
38
+ export declare function closedPath(pts: Point[], tension?: number): string;
39
+ /**
40
+ * Polygone quelconque -> profil radial, par lancer de rayon depuis `center`.
41
+ *
42
+ * Sert a fabriquer les formes qui ne s'expriment pas naturellement en r(theta)
43
+ * (la barre tronconique du "!"). Calcule une seule fois au chargement, jamais
44
+ * dans la boucle de rendu.
45
+ */
46
+ export declare function profileFromPolygon(poly: Point[], cx: number, cy: number): number[];
47
+ /** Enveloppe convexe de deux cercles : la barre tronconique du "!" vertical. */
48
+ export declare function hullOfCircles(x1: number, y1: number, r1: number, x2: number, y2: number, r2v: number, steps?: number): Point[];
49
+ /**
50
+ * Rayon du profil dans une direction quelconque, par interpolation entre les
51
+ * deux echantillons voisins.
52
+ *
53
+ * Sert a recaler ce qui est pose "sur" le corps (les yeux, la pastille de
54
+ * notification) quand la silhouette n'est plus un cercle : sans ca, un oeil
55
+ * place a 0.62 rayon sort d'une forme dont le bord est a 0.55 dans cette
56
+ * direction, et le masque le rogne.
57
+ */
58
+ export declare function radiusAtAngle(radii: number[], angle: number): number;
59
+ /**
60
+ * Superellipse : |x/sx|^n + |y/sy|^n = 1.
61
+ * n = 2 donne une ellipse, n ~ 4 le squircle du personnalisateur.
62
+ */
63
+ export declare function superellipseProfile(n: number, sx?: number, sy?: number): number[];
64
+ /**
65
+ * Profil radial de l'UNION de disques : r(theta) = la plus lointaine des
66
+ * intersections rayon/cercle. Exact tant que l'origine est dans l'union — c'est
67
+ * ce qui donne les bosses du nuage sans booleen de path.
68
+ */
69
+ export declare function unionOfCirclesProfile(circles: Array<{
70
+ x: number;
71
+ y: number;
72
+ r: number;
73
+ }>): number[];
74
+ /** Polygone regulier a coins arrondis, inscrit dans `radius`. */
75
+ export declare function regularPolygonProfile(sides: number, radius: number, rc: number, rotationDeg?: number): number[];
76
+ /** Polyligne fermee exacte : garde les segments droits (contrairement a closedPath). */
77
+ export declare function polyPath(pts: Point[], scale?: number): string;
78
+ /** Capsule (stade) centree sur l'origine : la forme exacte des yeux du bot. */
79
+ export declare function capsulePath(w: number, h: number): string;
80
+ //# sourceMappingURL=shape.d.ts.map
@@ -0,0 +1,268 @@
1
+ // Adapted from bloub, Copyright (c) 2026 Jérémy Perret (MIT).
2
+ // See THIRD_PARTY_NOTICES.md for source revision and local changes.
3
+ import { TAU, lerp, r2 } from './math.js';
4
+ import { PROFILES, PROFILE_SAMPLES } from './profiles.js';
5
+ const ANGLES = Array.from({ length: PROFILE_SAMPLES }, (_, i) => (i / PROFILE_SAMPLES) * TAU);
6
+ const COS = ANGLES.map(Math.cos);
7
+ const SIN = ANGLES.map(Math.sin);
8
+ export function silhouette(name, pose = {}) {
9
+ return {
10
+ radii: [...PROFILES[name]],
11
+ rot: 0,
12
+ cx: 0,
13
+ cy: 0,
14
+ sx: 1,
15
+ sy: 1,
16
+ ...pose
17
+ };
18
+ }
19
+ /** Cercle parfait : sert de base neutre (point, bulle, cible de fondu). */
20
+ export function circle(radius, pose = {}) {
21
+ return {
22
+ radii: new Array(PROFILE_SAMPLES).fill(radius),
23
+ rot: 0,
24
+ cx: 0,
25
+ cy: 0,
26
+ sx: 1,
27
+ sy: 1,
28
+ ...pose
29
+ };
30
+ }
31
+ /** Interpolation de deux silhouettes. `out` est reutilise pour eviter d'allouer a 60 fps. */
32
+ export function blend(a, b, t, out) {
33
+ const dst = out ?? { radii: new Array(PROFILE_SAMPLES), rot: 0, cx: 0, cy: 0, sx: 1, sy: 1 };
34
+ for (let i = 0; i < PROFILE_SAMPLES; i++) {
35
+ dst.radii[i] = lerp(a.radii[i] ?? 1, b.radii[i] ?? 1, t);
36
+ }
37
+ // Rotation par le chemin le plus court : evite de faire un tour complet
38
+ // quand on passe par exemple de +170deg a -170deg.
39
+ let dRot = b.rot - a.rot;
40
+ while (dRot > Math.PI)
41
+ dRot -= TAU;
42
+ while (dRot < -Math.PI)
43
+ dRot += TAU;
44
+ dst.rot = a.rot + dRot * t;
45
+ dst.cx = lerp(a.cx, b.cx, t);
46
+ dst.cy = lerp(a.cy, b.cy, t);
47
+ dst.sx = lerp(a.sx, b.sx, t);
48
+ dst.sy = lerp(a.sy, b.sy, t);
49
+ return dst;
50
+ }
51
+ /** Projette la silhouette en points ecran. `scale` = rayon de la boule en unites de viewBox. */
52
+ export function toPoints(s, scale, out = []) {
53
+ const cr = Math.cos(s.rot);
54
+ const sr = Math.sin(s.rot);
55
+ for (let i = 0; i < PROFILE_SAMPLES; i++) {
56
+ const r = s.radii[i] ?? 1;
57
+ const x = r * (COS[i] ?? 0);
58
+ const y = r * (SIN[i] ?? 0);
59
+ // rotation puis squash en repere ecran, puis translation
60
+ const rx = x * cr - y * sr;
61
+ const ry = x * sr + y * cr;
62
+ const p = out[i] ?? { x: 0, y: 0 };
63
+ p.x = (rx * s.sx + s.cx) * scale;
64
+ p.y = (ry * s.sy + s.cy) * scale;
65
+ out[i] = p;
66
+ }
67
+ out.length = PROFILE_SAMPLES;
68
+ return out;
69
+ }
70
+ /**
71
+ * Polyligne fermee -> cubiques Catmull-Rom.
72
+ *
73
+ * Avec 64 points les tangentes centrees suffisent largement : le contour est
74
+ * lisse au pixel pres meme affiche en 600 px, et la chaine reste courte.
75
+ */
76
+ export function closedPath(pts, tension = 1 / 6) {
77
+ const n = pts.length;
78
+ if (n < 3)
79
+ return '';
80
+ const first = pts[0];
81
+ let d = `M${r2(first.x)} ${r2(first.y)}`;
82
+ for (let i = 0; i < n; i++) {
83
+ const p0 = pts[(i - 1 + n) % n];
84
+ const p1 = pts[i];
85
+ const p2 = pts[(i + 1) % n];
86
+ const p3 = pts[(i + 2) % n];
87
+ const c1x = p1.x + (p2.x - p0.x) * tension;
88
+ const c1y = p1.y + (p2.y - p0.y) * tension;
89
+ const c2x = p2.x - (p3.x - p1.x) * tension;
90
+ const c2y = p2.y - (p3.y - p1.y) * tension;
91
+ d += `C${r2(c1x)} ${r2(c1y)} ${r2(c2x)} ${r2(c2y)} ${r2(p2.x)} ${r2(p2.y)}`;
92
+ }
93
+ return `${d}Z`;
94
+ }
95
+ /**
96
+ * Polygone quelconque -> profil radial, par lancer de rayon depuis `center`.
97
+ *
98
+ * Sert a fabriquer les formes qui ne s'expriment pas naturellement en r(theta)
99
+ * (la barre tronconique du "!"). Calcule une seule fois au chargement, jamais
100
+ * dans la boucle de rendu.
101
+ */
102
+ export function profileFromPolygon(poly, cx, cy) {
103
+ const radii = new Array(PROFILE_SAMPLES).fill(0);
104
+ const n = poly.length;
105
+ for (let k = 0; k < PROFILE_SAMPLES; k++) {
106
+ const dx = COS[k] ?? 0;
107
+ const dy = SIN[k] ?? 0;
108
+ let best = 0;
109
+ for (let i = 0; i < n; i++) {
110
+ const a = poly[i];
111
+ const b = poly[(i + 1) % n];
112
+ const ex = b.x - a.x;
113
+ const ey = b.y - a.y;
114
+ const den = dx * ey - dy * ex;
115
+ if (Math.abs(den) < 1e-9)
116
+ continue;
117
+ const px = a.x - cx;
118
+ const py = a.y - cy;
119
+ const t = (px * ey - py * ex) / den; // distance le long du rayon
120
+ const u = (px * dy - py * dx) / den; // position sur le segment
121
+ if (t > best && u >= 0 && u <= 1)
122
+ best = t;
123
+ }
124
+ radii[k] = best;
125
+ }
126
+ return radii;
127
+ }
128
+ /** Enveloppe convexe de deux cercles : la barre tronconique du "!" vertical. */
129
+ export function hullOfCircles(x1, y1, r1, x2, y2, r2v, steps = 96) {
130
+ const dx = x2 - x1;
131
+ const dy = y2 - y1;
132
+ const dist = Math.hypot(dx, dy) || 1e-6;
133
+ // angle des tangentes externes communes
134
+ const base = Math.atan2(dy, dx);
135
+ const spread = Math.acos(Math.max(-1, Math.min(1, (r1 - r2v) / dist)));
136
+ const pts = [];
137
+ // arc du grand cercle
138
+ for (let i = 0; i <= steps / 2; i++) {
139
+ const a = base + spread + ((TAU - 2 * spread) * i) / (steps / 2);
140
+ pts.push({ x: x1 + Math.cos(a) * r1, y: y1 + Math.sin(a) * r1 });
141
+ }
142
+ // arc du petit cercle
143
+ for (let i = 0; i <= steps / 2; i++) {
144
+ const a = base - spread + ((2 * spread) * i) / (steps / 2);
145
+ pts.push({ x: x2 + Math.cos(a) * r2v, y: y2 + Math.sin(a) * r2v });
146
+ }
147
+ return pts;
148
+ }
149
+ /**
150
+ * Rayon du profil dans une direction quelconque, par interpolation entre les
151
+ * deux echantillons voisins.
152
+ *
153
+ * Sert a recaler ce qui est pose "sur" le corps (les yeux, la pastille de
154
+ * notification) quand la silhouette n'est plus un cercle : sans ca, un oeil
155
+ * place a 0.62 rayon sort d'une forme dont le bord est a 0.55 dans cette
156
+ * direction, et le masque le rogne.
157
+ */
158
+ export function radiusAtAngle(radii, angle) {
159
+ const n = radii.length;
160
+ const t = ((((angle / TAU) % 1) + 1) % 1) * n;
161
+ const i = Math.floor(t);
162
+ return lerp(radii[i % n] ?? 1, radii[(i + 1) % n] ?? 1, t - i);
163
+ }
164
+ /**
165
+ * Superellipse : |x/sx|^n + |y/sy|^n = 1.
166
+ * n = 2 donne une ellipse, n ~ 4 le squircle du personnalisateur.
167
+ */
168
+ export function superellipseProfile(n, sx = 1, sy = 1) {
169
+ return ANGLES.map((_, i) => {
170
+ const c = Math.abs((COS[i] ?? 0) / sx) ** n;
171
+ const s = Math.abs((SIN[i] ?? 0) / sy) ** n;
172
+ return (c + s) ** (-1 / n);
173
+ });
174
+ }
175
+ /**
176
+ * Profil radial de l'UNION de disques : r(theta) = la plus lointaine des
177
+ * intersections rayon/cercle. Exact tant que l'origine est dans l'union — c'est
178
+ * ce qui donne les bosses du nuage sans booleen de path.
179
+ */
180
+ export function unionOfCirclesProfile(circles) {
181
+ const out = new Array(PROFILE_SAMPLES).fill(0);
182
+ for (let i = 0; i < PROFILE_SAMPLES; i++) {
183
+ const dx = COS[i] ?? 0;
184
+ const dy = SIN[i] ?? 0;
185
+ let best = 0;
186
+ for (const c of circles) {
187
+ const b = dx * c.x + dy * c.y;
188
+ const disc = b * b - (c.x * c.x + c.y * c.y - c.r * c.r);
189
+ if (disc < 0)
190
+ continue;
191
+ const t = b + Math.sqrt(disc);
192
+ if (t > best)
193
+ best = t;
194
+ }
195
+ out[i] = best;
196
+ }
197
+ return out;
198
+ }
199
+ /**
200
+ * Polygone a coins arrondis, par somme de Minkowski avec un disque : chaque
201
+ * arete est poussee de `rc` vers l'exterieur, chaque sommet devient un arc de
202
+ * rayon `rc`. Les sommets sont donc a poser au rayon voulu MOINS rc.
203
+ * Attend un polygone en sens horaire (repere ecran, y vers le bas).
204
+ */
205
+ function roundedPolygon(verts, rc, arcSteps = 10) {
206
+ const n = verts.length;
207
+ const out = [];
208
+ const normal = (a, b) => {
209
+ const dx = b.x - a.x;
210
+ const dy = b.y - a.y;
211
+ const len = Math.hypot(dx, dy) || 1;
212
+ // sens horaire + y vers le bas : la normale sortante est (dy, -dx)
213
+ return Math.atan2(-dx / len, dy / len);
214
+ };
215
+ for (let i = 0; i < n; i++) {
216
+ const prev = verts[(i - 1 + n) % n];
217
+ const cur = verts[i];
218
+ const next = verts[(i + 1) % n];
219
+ const a0 = normal(prev, cur);
220
+ const a1 = normal(cur, next);
221
+ let d = a1 - a0;
222
+ while (d > Math.PI)
223
+ d -= TAU;
224
+ while (d < -Math.PI)
225
+ d += TAU;
226
+ for (let k = 0; k <= arcSteps; k++) {
227
+ const a = a0 + (d * k) / arcSteps;
228
+ out.push({ x: cur.x + Math.cos(a) * rc, y: cur.y + Math.sin(a) * rc });
229
+ }
230
+ }
231
+ return out;
232
+ }
233
+ /** Polygone regulier a coins arrondis, inscrit dans `radius`. */
234
+ export function regularPolygonProfile(sides, radius, rc, rotationDeg = 0) {
235
+ const rot = (rotationDeg * Math.PI) / 180;
236
+ const verts = Array.from({ length: sides }, (_, i) => {
237
+ // sens horaire a l'ecran : theta croissant avec y vers le bas
238
+ const a = rot + (i / sides) * TAU;
239
+ return { x: Math.cos(a) * (radius - rc), y: Math.sin(a) * (radius - rc) };
240
+ });
241
+ return profileFromPolygon(roundedPolygon(verts, rc), 0, 0);
242
+ }
243
+ /** Polyligne fermee exacte : garde les segments droits (contrairement a closedPath). */
244
+ export function polyPath(pts, scale = 1) {
245
+ if (pts.length < 3)
246
+ return '';
247
+ let d = '';
248
+ for (let i = 0; i < pts.length; i++) {
249
+ const p = pts[i];
250
+ d += `${i === 0 ? 'M' : 'L'}${r2(p.x * scale)} ${r2(p.y * scale)}`;
251
+ }
252
+ return `${d}Z`;
253
+ }
254
+ /** Capsule (stade) centree sur l'origine : la forme exacte des yeux du bot. */
255
+ export function capsulePath(w, h) {
256
+ const hw = Math.max(w, 0.01) / 2;
257
+ const hh = Math.max(h, 0.01) / 2;
258
+ const r = Math.min(hw, hh);
259
+ return (`M${r2(-hw)} ${r2(-hh + r)}` +
260
+ `A${r2(r)} ${r2(r)} 0 0 1 ${r2(-hw + r)} ${r2(-hh)}` +
261
+ `L${r2(hw - r)} ${r2(-hh)}` +
262
+ `A${r2(r)} ${r2(r)} 0 0 1 ${r2(hw)} ${r2(-hh + r)}` +
263
+ `L${r2(hw)} ${r2(hh - r)}` +
264
+ `A${r2(r)} ${r2(r)} 0 0 1 ${r2(hw - r)} ${r2(hh)}` +
265
+ `L${r2(-hw + r)} ${r2(hh)}` +
266
+ `A${r2(r)} ${r2(r)} 0 0 1 ${r2(-hw)} ${r2(hh - r)}Z`);
267
+ }
268
+ //# sourceMappingURL=shape.js.map
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Formes et couleurs proposees par le personnalisateur du bot.
3
+ *
4
+ * A la difference des silhouettes d'animation (`profiles.ts`), celles-ci ne sont
5
+ * PAS relevees sur la video : elles sont construites analytiquement d'apres la
6
+ * grille du personnalisateur d'origine. Deux sources distinctes, donc, et c'est
7
+ * volontaire — les etats animes doivent rester fideles a la video, les formes de
8
+ * base sont un choix d'utilisateur.
9
+ */
10
+ /**
11
+ * Les identifiants sont enumeres plutot que deduits du tableau : c'est ce qui
12
+ * permet a la couche i18n de verifier A LA COMPILATION que chaque forme a bien
13
+ * sa traduction dans les trois langues (`t(\`shapes.${id}\`)` ne compile que si
14
+ * la cle existe). Un `as const` sur le tableau aurait le meme effet mais
15
+ * rendrait `radii` en lecture seule, alors que le moteur le passe tel quel.
16
+ */
17
+ export type ShapeId = 'cercle' | 'galet' | 'squircle' | 'capsule' | 'triangle' | 'hexagone' | 'nuage' | 'goutte';
18
+ export interface BotShape {
19
+ id: ShapeId;
20
+ radii: number[];
21
+ }
22
+ export declare const SHAPES: BotShape[];
23
+ export declare const SHAPE_BY_ID: Map<string, BotShape>;
24
+ export declare const DEFAULT_SHAPE = "cercle";
25
+ export type ColorId = 'encre' | 'creme' | 'brun' | 'rouge' | 'orange' | 'ambre' | 'vert' | 'turquoise' | 'bleu' | 'violet' | 'rose' | 'gris';
26
+ export interface BotColor {
27
+ id: ColorId;
28
+ hex: string;
29
+ }
30
+ /** Palette du personnalisateur d'origine. */
31
+ export declare const COLORS: BotColor[];
32
+ export declare const COLOR_BY_ID: Map<string, BotColor>;
33
+ export declare const DEFAULT_COLOR = "encre";
34
+ /** Melange deux couleurs hex. Sert a la brume de profondeur des particules. */
35
+ export declare function mixHex(from: string, to: string, t: number): string;
36
+ //# sourceMappingURL=skins.d.ts.map
@@ -0,0 +1,74 @@
1
+ // Adapted from bloub, Copyright (c) 2026 Jérémy Perret (MIT).
2
+ // See THIRD_PARTY_NOTICES.md for source revision and local changes.
3
+ import { PROFILE_SAMPLES } from './profiles.js';
4
+ import { hullOfCircles, profileFromPolygon, regularPolygonProfile, superellipseProfile, unionOfCirclesProfile } from './shape.js';
5
+ /** Ramene le rayon maximal a `max` pour que toutes les formes pesent pareil a l'oeil. */
6
+ function normalize(radii, max = 1) {
7
+ const peak = Math.max(...radii);
8
+ if (peak <= 0)
9
+ return radii;
10
+ const k = max / peak;
11
+ return radii.map((r) => r * k);
12
+ }
13
+ const ANGLES = Array.from({ length: PROFILE_SAMPLES }, (_, i) => (i / PROFILE_SAMPLES) * Math.PI * 2);
14
+ /** Galet : cercle deforme par deux harmoniques basses, donc irregulier mais lisse. */
15
+ const pebble = normalize(ANGLES.map((a) => 1 + 0.075 * Math.cos(2 * a + 0.5) + 0.035 * Math.cos(3 * a + 2.1)), 1.02);
16
+ /** Nuage : union de bosses, large en bas, deux lobes en haut. */
17
+ const cloud = normalize(unionOfCirclesProfile([
18
+ { x: -0.44, y: 0.2, r: 0.54 },
19
+ { x: 0.46, y: 0.2, r: 0.5 },
20
+ { x: 0.02, y: 0.3, r: 0.6 },
21
+ { x: -0.24, y: -0.3, r: 0.48 },
22
+ { x: 0.3, y: -0.24, r: 0.44 }
23
+ ]), 1.02);
24
+ /** Goutte : gros disque en bas, pointe effilee en haut. */
25
+ const droplet = normalize(profileFromPolygon(hullOfCircles(0, 0.28, 0.66, 0, -0.96, 0.05), 0, 0), 1.04);
26
+ /** Capsule couchee : enveloppe de deux disques cote a cote. */
27
+ const capsule = profileFromPolygon(hullOfCircles(-0.42, 0, 0.62, 0.42, 0, 0.62), 0, 0);
28
+ export const SHAPES = [
29
+ { id: 'cercle', radii: new Array(PROFILE_SAMPLES).fill(1) },
30
+ { id: 'galet', radii: pebble },
31
+ // 1.15 et pas 1.02 : sur une superellipse le rayon maximal est la diagonale,
32
+ // donc normaliser dessus donne une forme qui parait plus petite que le cercle.
33
+ { id: 'squircle', radii: normalize(superellipseProfile(4.2), 1.15) },
34
+ { id: 'capsule', radii: capsule },
35
+ // -90deg : un sommet vers le haut de l'ecran (y est oriente vers le bas)
36
+ { id: 'triangle', radii: regularPolygonProfile(3, 1.12, 0.34, -90) },
37
+ // 0deg : sommets a gauche et a droite, donc aretes du haut et du bas plates
38
+ { id: 'hexagone', radii: regularPolygonProfile(6, 1.04, 0.26, 0) },
39
+ { id: 'nuage', radii: cloud },
40
+ { id: 'goutte', radii: droplet }
41
+ ];
42
+ // Map indexee par `string` et non par `ShapeId` : les appelants interrogent avec
43
+ // une valeur relue du localStorage ou d'une prop, donc non validee.
44
+ export const SHAPE_BY_ID = new Map(SHAPES.map((s) => [s.id, s]));
45
+ export const DEFAULT_SHAPE = 'cercle';
46
+ /** Palette du personnalisateur d'origine. */
47
+ export const COLORS = [
48
+ { id: 'encre', hex: '#0a0a0c' },
49
+ { id: 'brun', hex: '#8b5e3c' },
50
+ { id: 'rouge', hex: '#e8483f' },
51
+ { id: 'orange', hex: '#f08a24' },
52
+ { id: 'ambre', hex: '#f0b429' },
53
+ { id: 'vert', hex: '#3ecf8e' },
54
+ { id: 'turquoise', hex: '#2fbfa0' },
55
+ { id: 'bleu', hex: '#3b93f0' },
56
+ { id: 'violet', hex: '#8b5cf6' },
57
+ { id: 'rose', hex: '#e152b0' },
58
+ { id: 'gris', hex: '#a3a3a3' },
59
+ { id: 'creme', hex: '#f1efe9' }
60
+ ];
61
+ export const COLOR_BY_ID = new Map(COLORS.map((c) => [c.id, c]));
62
+ export const DEFAULT_COLOR = 'encre';
63
+ /** Melange deux couleurs hex. Sert a la brume de profondeur des particules. */
64
+ export function mixHex(from, to, t) {
65
+ const parse = (h) => {
66
+ const v = parseInt(h.slice(1), 16);
67
+ return [(v >> 16) & 255, (v >> 8) & 255, v & 255];
68
+ };
69
+ const a = parse(from);
70
+ const b = parse(to);
71
+ const c = a.map((x, i) => Math.round(x + (b[i] - x) * t));
72
+ return `#${c.map((x) => x.toString(16).padStart(2, '0')).join('')}`;
73
+ }
74
+ //# sourceMappingURL=skins.js.map