@yewdiel21/motion-kit 0.0.0-stage → 0.9.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 (391) hide show
  1. package/NOTICE +38 -0
  2. package/README.md +80 -2
  3. package/bin/motion-kit.mjs +242 -0
  4. package/css.d.ts +2 -0
  5. package/dist/auto/index.d.ts +63 -0
  6. package/dist/auto/index.js +207 -0
  7. package/dist/auto/index.js.map +1 -0
  8. package/dist/chunk-2BQGFOA7.js +177 -0
  9. package/dist/chunk-2BQGFOA7.js.map +1 -0
  10. package/dist/chunk-34XYOXHG.js +2374 -0
  11. package/dist/chunk-34XYOXHG.js.map +1 -0
  12. package/dist/chunk-6OJAP6ZL.js +832 -0
  13. package/dist/chunk-6OJAP6ZL.js.map +1 -0
  14. package/dist/chunk-ABNRDDO7.js +28 -0
  15. package/dist/chunk-ABNRDDO7.js.map +1 -0
  16. package/dist/chunk-FS2HJVVY.js +3320 -0
  17. package/dist/chunk-FS2HJVVY.js.map +1 -0
  18. package/dist/chunk-J27KH2A2.js +157 -0
  19. package/dist/chunk-J27KH2A2.js.map +1 -0
  20. package/dist/chunk-KLY5BRRI.js +18 -0
  21. package/dist/chunk-KLY5BRRI.js.map +1 -0
  22. package/dist/chunk-Q4LVFNLN.js +1617 -0
  23. package/dist/chunk-Q4LVFNLN.js.map +1 -0
  24. package/dist/chunk-RFDIZYFX.js +304 -0
  25. package/dist/chunk-RFDIZYFX.js.map +1 -0
  26. package/dist/chunk-TGV27ZWM.js +17 -0
  27. package/dist/chunk-TGV27ZWM.js.map +1 -0
  28. package/dist/confetti-BSX1CLLV.d.ts +41 -0
  29. package/dist/css/components/chat-background.css +87 -0
  30. package/dist/css/components/chat.css +237 -0
  31. package/dist/css/components/cursor.css +178 -0
  32. package/dist/css/components/dialog.css +259 -0
  33. package/dist/css/components/island.css +301 -0
  34. package/dist/css/components/menu.css +105 -0
  35. package/dist/css/components/nav.css +1054 -0
  36. package/dist/css/components/steps.css +50 -0
  37. package/dist/css/components/theme-picker.css +115 -0
  38. package/dist/css/components/toast.css +115 -0
  39. package/dist/css/controls.css +950 -0
  40. package/dist/css/effects.css +587 -0
  41. package/dist/css/fonts.css +3 -0
  42. package/dist/css/themes/black-green.css +126 -0
  43. package/dist/css/themes/black.css +126 -0
  44. package/dist/css/themes/blue.css +126 -0
  45. package/dist/css/themes/brown.css +126 -0
  46. package/dist/css/themes/cold-blue.css +126 -0
  47. package/dist/css/themes/funny.css +126 -0
  48. package/dist/css/themes/gray.css +126 -0
  49. package/dist/css/themes/green.css +126 -0
  50. package/dist/css/themes/modern-1.css +126 -0
  51. package/dist/css/themes/neogreen.css +126 -0
  52. package/dist/css/themes/new.css +126 -0
  53. package/dist/css/themes/new2.css +126 -0
  54. package/dist/css/themes/oled.css +126 -0
  55. package/dist/css/themes/orange.css +126 -0
  56. package/dist/css/themes/pastel-pink.css +126 -0
  57. package/dist/css/themes/pink.css +126 -0
  58. package/dist/css/themes/purple.css +126 -0
  59. package/dist/css/themes/red.css +126 -0
  60. package/dist/css/themes/super-blue.css +126 -0
  61. package/dist/css/themes.css +2431 -0
  62. package/dist/css/tokens.css +452 -0
  63. package/dist/elements/index.d.ts +183 -0
  64. package/dist/elements/index.js +472 -0
  65. package/dist/elements/index.js.map +1 -0
  66. package/dist/engine-CUr9QijM.d.ts +46 -0
  67. package/dist/gl/index.d.ts +61 -0
  68. package/dist/gl/index.js +18 -0
  69. package/dist/gl/index.js.map +1 -0
  70. package/dist/index.css +5296 -0
  71. package/dist/index.d.ts +440 -0
  72. package/dist/index.js +390 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/react/index.d.ts +476 -0
  75. package/dist/react/index.js +969 -0
  76. package/dist/react/index.js.map +1 -0
  77. package/dist/reduced-BM4Ls0g5.d.ts +566 -0
  78. package/dist/reveal-WFUWDrei.d.ts +416 -0
  79. package/dist/store-Bqy_obnD.d.ts +195 -0
  80. package/dist/theme/generate.d.ts +18 -0
  81. package/dist/theme/generate.js +64 -0
  82. package/dist/theme/generate.js.map +1 -0
  83. package/dist/theme/index.d.ts +17 -0
  84. package/dist/theme/index.js +32 -0
  85. package/dist/theme/index.js.map +1 -0
  86. package/dist/theme-BFMoCzi6.d.ts +158 -0
  87. package/dist/types-BkS_raL8.d.ts +7 -0
  88. package/package.json +114 -3
  89. package/registry.json +469 -0
  90. package/skills/motion-kit/SKILL.md +189 -0
  91. package/skills/motion-kit/reference/components.md +86 -0
  92. package/skills/motion-kit/reference/design.md +92 -0
  93. package/skills/motion-kit/reference/recipes.md +172 -0
  94. package/src/auto/auto-init.ts +151 -0
  95. package/src/auto/index.ts +3 -0
  96. package/src/auto/parse-options.ts +68 -0
  97. package/src/components/chat/README.md +90 -0
  98. package/src/components/chat/chat.css +236 -0
  99. package/src/components/chat/element.ts +98 -0
  100. package/src/components/chat/engine.ts +295 -0
  101. package/src/components/chat/react.tsx +196 -0
  102. package/src/components/chat-background/README.md +50 -0
  103. package/src/components/chat-background/audio.ts +56 -0
  104. package/src/components/chat-background/chat-background.css +86 -0
  105. package/src/components/chat-background/element.ts +31 -0
  106. package/src/components/chat-background/engine.ts +108 -0
  107. package/src/components/chat-background/react.tsx +36 -0
  108. package/src/components/cursor/README.md +53 -0
  109. package/src/components/cursor/cursor.css +177 -0
  110. package/src/components/cursor/element.ts +23 -0
  111. package/src/components/cursor/engine.ts +254 -0
  112. package/src/components/cursor/react.tsx +26 -0
  113. package/src/components/dialog/README.md +106 -0
  114. package/src/components/dialog/dialog.css +258 -0
  115. package/src/components/dialog/element.ts +118 -0
  116. package/src/components/dialog/engine.ts +432 -0
  117. package/src/components/dialog/react.tsx +200 -0
  118. package/src/components/icon.tsx +10 -0
  119. package/src/components/icons.ts +24 -0
  120. package/src/components/island/README.md +91 -0
  121. package/src/components/island/element.ts +45 -0
  122. package/src/components/island/engine.ts +200 -0
  123. package/src/components/island/island.css +300 -0
  124. package/src/components/island/react.tsx +115 -0
  125. package/src/components/menu/README.md +67 -0
  126. package/src/components/menu/element.ts +55 -0
  127. package/src/components/menu/engine.ts +142 -0
  128. package/src/components/menu/menu.css +104 -0
  129. package/src/components/menu/react.tsx +82 -0
  130. package/src/components/nav/README.md +85 -0
  131. package/src/components/nav/element.ts +39 -0
  132. package/src/components/nav/engine.ts +164 -0
  133. package/src/components/nav/nav.css +1053 -0
  134. package/src/components/nav/react.tsx +90 -0
  135. package/src/components/steps/README.md +70 -0
  136. package/src/components/steps/element.ts +43 -0
  137. package/src/components/steps/engine.ts +185 -0
  138. package/src/components/steps/react.tsx +91 -0
  139. package/src/components/steps/steps.css +49 -0
  140. package/src/components/theme-picker/README.md +57 -0
  141. package/src/components/theme-picker/element.ts +20 -0
  142. package/src/components/theme-picker/engine.ts +91 -0
  143. package/src/components/theme-picker/react.tsx +31 -0
  144. package/src/components/theme-picker/theme-picker.css +114 -0
  145. package/src/components/toast/README.md +60 -0
  146. package/src/components/toast/element.ts +25 -0
  147. package/src/components/toast/engine.ts +483 -0
  148. package/src/components/toast/react.tsx +22 -0
  149. package/src/components/toast/store.ts +122 -0
  150. package/src/components/toast/toast.css +114 -0
  151. package/src/core/channels.ts +165 -0
  152. package/src/core/curves.ts +35 -0
  153. package/src/core/frame.ts +12 -0
  154. package/src/core/gsap.ts +65 -0
  155. package/src/core/loop.ts +44 -0
  156. package/src/core/math.ts +9 -0
  157. package/src/core/physics-spring.ts +59 -0
  158. package/src/core/pointer.ts +63 -0
  159. package/src/core/reduced.ts +21 -0
  160. package/src/core/scope.ts +70 -0
  161. package/src/core/scroll-track.ts +106 -0
  162. package/src/core/split.ts +68 -0
  163. package/src/core/spring.ts +22 -0
  164. package/src/core/styles.ts +31 -0
  165. package/src/core/timing.ts +204 -0
  166. package/src/core/types.ts +20 -0
  167. package/src/core/visible.ts +19 -0
  168. package/src/css/base.css +142 -0
  169. package/src/css/controls/README.md +122 -0
  170. package/src/css/controls/burger.css +67 -0
  171. package/src/css/controls/button.css +226 -0
  172. package/src/css/controls/checkbox.css +53 -0
  173. package/src/css/controls/chip.css +85 -0
  174. package/src/css/controls/field.css +124 -0
  175. package/src/css/controls/glass.css +220 -0
  176. package/src/css/controls/icon.css +26 -0
  177. package/src/css/controls/segmented.css +60 -0
  178. package/src/css/controls/switch.css +72 -0
  179. package/src/css/default-theme.css +123 -0
  180. package/src/css/effects/README.md +134 -0
  181. package/src/css/effects/anim.css +224 -0
  182. package/src/css/effects/fade.css +15 -0
  183. package/src/css/effects/float.css +64 -0
  184. package/src/css/effects/frost.css +62 -0
  185. package/src/css/effects/grain.css +58 -0
  186. package/src/css/effects/loading.css +60 -0
  187. package/src/css/effects/success.css +22 -0
  188. package/src/css/effects/text.css +67 -0
  189. package/src/css/fonts.css +3 -0
  190. package/src/css/states.css +51 -0
  191. package/src/css/themes/black-green.css +126 -0
  192. package/src/css/themes/black.css +126 -0
  193. package/src/css/themes/blue.css +126 -0
  194. package/src/css/themes/brown.css +126 -0
  195. package/src/css/themes/cold-blue.css +126 -0
  196. package/src/css/themes/funny.css +126 -0
  197. package/src/css/themes/gray.css +126 -0
  198. package/src/css/themes/green.css +126 -0
  199. package/src/css/themes/modern-1.css +126 -0
  200. package/src/css/themes/neogreen.css +126 -0
  201. package/src/css/themes/new.css +126 -0
  202. package/src/css/themes/new2.css +126 -0
  203. package/src/css/themes/oled.css +126 -0
  204. package/src/css/themes/orange.css +126 -0
  205. package/src/css/themes/pastel-pink.css +126 -0
  206. package/src/css/themes/pink.css +126 -0
  207. package/src/css/themes/purple.css +126 -0
  208. package/src/css/themes/red.css +126 -0
  209. package/src/css/themes/super-blue.css +126 -0
  210. package/src/css/tokens.css +182 -0
  211. package/src/elements/base.ts +76 -0
  212. package/src/elements/index.ts +48 -0
  213. package/src/gl/index.ts +15 -0
  214. package/src/gl/liquid-blend/README.md +23 -0
  215. package/src/gl/liquid-blend/create-liquid-blend.ts +7 -0
  216. package/src/gl/liquid-blend/shader.ts +9 -0
  217. package/src/gl/matcap/README.md +23 -0
  218. package/src/gl/matcap/create-matcap.ts +7 -0
  219. package/src/gl/matcap/shader.ts +16 -0
  220. package/src/gl/particle-hero/README.md +41 -0
  221. package/src/gl/particle-hero/create-particle-hero.ts +544 -0
  222. package/src/gl/particle-hero/particle-hero.css +19 -0
  223. package/src/gl/particle-hero/shaders.ts +84 -0
  224. package/src/gl/shader-blur/README.md +23 -0
  225. package/src/gl/shader-blur/create-shader-blur.ts +7 -0
  226. package/src/gl/shader-blur/shader.ts +11 -0
  227. package/src/gl/shader-grain/README.md +23 -0
  228. package/src/gl/shader-grain/create-shader-grain.ts +7 -0
  229. package/src/gl/shader-grain/shader.ts +6 -0
  230. package/src/gl/shared/create-shader-effect.ts +186 -0
  231. package/src/gl/shared/shader.css +3 -0
  232. package/src/gl/shared/shaders.ts +30 -0
  233. package/src/index.ts +177 -0
  234. package/src/motion/anchor.ts +45 -0
  235. package/src/motion/chat.ts +97 -0
  236. package/src/motion/confetti.ts +130 -0
  237. package/src/motion/count.ts +95 -0
  238. package/src/motion/details.ts +70 -0
  239. package/src/motion/drag-dismiss.ts +231 -0
  240. package/src/motion/effects.ts +81 -0
  241. package/src/motion/entrances.ts +27 -0
  242. package/src/motion/floating.ts +389 -0
  243. package/src/motion/flow.ts +115 -0
  244. package/src/motion/indicator.ts +44 -0
  245. package/src/motion/layout.ts +39 -0
  246. package/src/motion/liquid.ts +227 -0
  247. package/src/motion/morph.ts +540 -0
  248. package/src/motion/reveal.ts +112 -0
  249. package/src/motion/sheet.ts +65 -0
  250. package/src/motion/surface.ts +108 -0
  251. package/src/react/animated-number.tsx +30 -0
  252. package/src/react/flip-group.tsx +44 -0
  253. package/src/react/frost.tsx +33 -0
  254. package/src/react/index.ts +80 -0
  255. package/src/react/slot-text.tsx +44 -0
  256. package/src/react/theme-provider.tsx +42 -0
  257. package/src/react/use-card-dock.ts +9 -0
  258. package/src/react/use-cascade.ts +9 -0
  259. package/src/react/use-contrast.ts +9 -0
  260. package/src/react/use-counter.ts +8 -0
  261. package/src/react/use-curved-carousel.ts +10 -0
  262. package/src/react/use-depth-grid.ts +9 -0
  263. package/src/react/use-details.ts +7 -0
  264. package/src/react/use-dock-pill.ts +10 -0
  265. package/src/react/use-drag-ruler.ts +10 -0
  266. package/src/react/use-fill-text.ts +9 -0
  267. package/src/react/use-fit-text.ts +8 -0
  268. package/src/react/use-flow.ts +8 -0
  269. package/src/react/use-frame-zoom.ts +9 -0
  270. package/src/react/use-infinite-reel.ts +8 -0
  271. package/src/react/use-letters.ts +9 -0
  272. package/src/react/use-liquid-blend.ts +8 -0
  273. package/src/react/use-marquee.ts +9 -0
  274. package/src/react/use-mask-text.ts +9 -0
  275. package/src/react/use-matcap.ts +8 -0
  276. package/src/react/use-parallax.ts +9 -0
  277. package/src/react/use-particle-hero.ts +9 -0
  278. package/src/react/use-pile.ts +8 -0
  279. package/src/react/use-pop.ts +8 -0
  280. package/src/react/use-radial-menu.ts +10 -0
  281. package/src/react/use-recipe.ts +38 -0
  282. package/src/react/use-reflow.ts +9 -0
  283. package/src/react/use-repel-bento.ts +8 -0
  284. package/src/react/use-reveal.ts +8 -0
  285. package/src/react/use-rotator.ts +10 -0
  286. package/src/react/use-shader-blur.ts +8 -0
  287. package/src/react/use-shader-grain.ts +8 -0
  288. package/src/react/use-slot-text.ts +17 -0
  289. package/src/react/use-snap-strip.ts +9 -0
  290. package/src/react/use-sortable.ts +14 -0
  291. package/src/react/use-spring-cards.ts +8 -0
  292. package/src/react/use-stretch-footer.ts +20 -0
  293. package/src/react/use-tabs.ts +10 -0
  294. package/src/react/use-text-roll.ts +9 -0
  295. package/src/react/use-ticket-menu.ts +15 -0
  296. package/src/react/use-words.ts +8 -0
  297. package/src/recipes/card-dock/README.md +36 -0
  298. package/src/recipes/card-dock/card-dock.css +178 -0
  299. package/src/recipes/card-dock/create-card-dock.ts +97 -0
  300. package/src/recipes/cascade/README.md +33 -0
  301. package/src/recipes/cascade/create-cascade.ts +111 -0
  302. package/src/recipes/contrast/README.md +29 -0
  303. package/src/recipes/contrast/create-contrast.ts +68 -0
  304. package/src/recipes/counter/README.md +42 -0
  305. package/src/recipes/counter/create-counter.ts +84 -0
  306. package/src/recipes/curved-carousel/README.md +50 -0
  307. package/src/recipes/curved-carousel/carousel-math.ts +20 -0
  308. package/src/recipes/curved-carousel/create-curved-carousel.ts +257 -0
  309. package/src/recipes/curved-carousel/curved-carousel.css +50 -0
  310. package/src/recipes/depth-grid/README.md +30 -0
  311. package/src/recipes/depth-grid/create-depth-grid.ts +120 -0
  312. package/src/recipes/details/README.md +31 -0
  313. package/src/recipes/details/create-details.ts +27 -0
  314. package/src/recipes/dock-pill/README.md +23 -0
  315. package/src/recipes/dock-pill/create-dock-pill.ts +68 -0
  316. package/src/recipes/dock-pill/dock-pill.css +5 -0
  317. package/src/recipes/drag-ruler/README.md +21 -0
  318. package/src/recipes/drag-ruler/create-drag-ruler.ts +95 -0
  319. package/src/recipes/drag-ruler/drag-ruler.css +16 -0
  320. package/src/recipes/fill-text/README.md +27 -0
  321. package/src/recipes/fill-text/create-fill-text.ts +93 -0
  322. package/src/recipes/fit-text/README.md +37 -0
  323. package/src/recipes/fit-text/create-fit-text.ts +55 -0
  324. package/src/recipes/fit-text/fit-text.css +6 -0
  325. package/src/recipes/flow/README.md +46 -0
  326. package/src/recipes/flow/create-flow.ts +55 -0
  327. package/src/recipes/frame-zoom/README.md +29 -0
  328. package/src/recipes/frame-zoom/create-frame-zoom.ts +91 -0
  329. package/src/recipes/infinite-reel/README.md +23 -0
  330. package/src/recipes/infinite-reel/create-infinite-reel.ts +140 -0
  331. package/src/recipes/infinite-reel/infinite-reel.css +8 -0
  332. package/src/recipes/letters/README.md +30 -0
  333. package/src/recipes/letters/create-letters.ts +113 -0
  334. package/src/recipes/marquee/README.md +32 -0
  335. package/src/recipes/marquee/create-marquee.ts +74 -0
  336. package/src/recipes/marquee/marquee.css +41 -0
  337. package/src/recipes/mask-text/README.md +39 -0
  338. package/src/recipes/mask-text/create-mask-text.ts +134 -0
  339. package/src/recipes/parallax/README.md +31 -0
  340. package/src/recipes/parallax/create-parallax.ts +74 -0
  341. package/src/recipes/pile/README.md +48 -0
  342. package/src/recipes/pile/create-pile.ts +50 -0
  343. package/src/recipes/pile/pile-math.ts +27 -0
  344. package/src/recipes/pile/pile.css +63 -0
  345. package/src/recipes/pill/README.md +30 -0
  346. package/src/recipes/pill/pill.css +48 -0
  347. package/src/recipes/pop/README.md +36 -0
  348. package/src/recipes/pop/create-pop.ts +52 -0
  349. package/src/recipes/radial-menu/README.md +44 -0
  350. package/src/recipes/radial-menu/create-radial-menu.ts +96 -0
  351. package/src/recipes/radial-menu/radial-menu.css +45 -0
  352. package/src/recipes/reflow/README.md +47 -0
  353. package/src/recipes/reflow/create-reflow.ts +49 -0
  354. package/src/recipes/repel-bento/README.md +21 -0
  355. package/src/recipes/repel-bento/create-repel-bento.ts +65 -0
  356. package/src/recipes/repel-bento/repel-bento.css +3 -0
  357. package/src/recipes/reveal/README.md +36 -0
  358. package/src/recipes/reveal/create-reveal.ts +48 -0
  359. package/src/recipes/rotator/README.md +21 -0
  360. package/src/recipes/rotator/create-rotator.ts +66 -0
  361. package/src/recipes/rotator/rotator.css +3 -0
  362. package/src/recipes/slot-text/README.md +44 -0
  363. package/src/recipes/slot-text/create-slot-text.ts +244 -0
  364. package/src/recipes/slot-text/slot-text.css +42 -0
  365. package/src/recipes/snap-strip/README.md +31 -0
  366. package/src/recipes/snap-strip/create-snap-strip.ts +133 -0
  367. package/src/recipes/snap-strip/snap-strip.css +23 -0
  368. package/src/recipes/sortable/README.md +88 -0
  369. package/src/recipes/sortable/create-sortable.ts +863 -0
  370. package/src/recipes/sortable/sortable.css +40 -0
  371. package/src/recipes/spring-cards/README.md +48 -0
  372. package/src/recipes/spring-cards/create-spring-cards.ts +140 -0
  373. package/src/recipes/spring-cards/spring-cards.css +23 -0
  374. package/src/recipes/stretch-footer/README.md +44 -0
  375. package/src/recipes/stretch-footer/create-stretch-footer.ts +69 -0
  376. package/src/recipes/stretch-footer/stretch-footer.css +28 -0
  377. package/src/recipes/tabs/README.md +39 -0
  378. package/src/recipes/tabs/create-tabs.ts +91 -0
  379. package/src/recipes/tabs/tabs.css +19 -0
  380. package/src/recipes/text-roll/README.md +35 -0
  381. package/src/recipes/text-roll/create-text-roll.ts +137 -0
  382. package/src/recipes/text-roll/text-roll.css +21 -0
  383. package/src/recipes/ticket-menu/README.md +24 -0
  384. package/src/recipes/ticket-menu/create-ticket-menu.ts +6 -0
  385. package/src/recipes/words/README.md +43 -0
  386. package/src/recipes/words/create-words.ts +114 -0
  387. package/src/theme/css-format.ts +32 -0
  388. package/src/theme/generate.ts +72 -0
  389. package/src/theme/index.ts +15 -0
  390. package/src/theme/presets.ts +79 -0
  391. package/src/theme/theme.ts +119 -0
@@ -0,0 +1,151 @@
1
+ import { ScrollTrigger } from '../core/gsap';
2
+ import { warnOnce, type Instance } from '../core/types';
3
+ import { createSpringCards } from '../recipes/spring-cards/create-spring-cards';
4
+ import { createCurvedCarousel } from '../recipes/curved-carousel/create-curved-carousel';
5
+ import { createPile } from '../recipes/pile/create-pile';
6
+ import { createWords } from '../recipes/words/create-words';
7
+ import { createFlow } from '../recipes/flow/create-flow';
8
+ import { createPop } from '../recipes/pop/create-pop';
9
+ import { createFitText } from '../recipes/fit-text/create-fit-text';
10
+ import { createStretchFooter } from '../recipes/stretch-footer/create-stretch-footer';
11
+ import { createRadialMenu } from '../recipes/radial-menu/create-radial-menu';
12
+ import { createTabs } from '../recipes/tabs/create-tabs';
13
+ import { createCounter } from '../recipes/counter/create-counter';
14
+ import { createDetails } from '../recipes/details/create-details';
15
+ import { createReveal } from '../recipes/reveal/create-reveal';
16
+ import { createReflow } from '../recipes/reflow/create-reflow';
17
+ import { createSlotText } from '../recipes/slot-text/create-slot-text';
18
+ import { createSortable } from '../recipes/sortable/create-sortable';
19
+ import { createMaskText } from '../recipes/mask-text/create-mask-text';
20
+ import { createCascade } from '../recipes/cascade/create-cascade';
21
+ import { createTextRoll } from '../recipes/text-roll/create-text-roll';
22
+ import { createLetters } from '../recipes/letters/create-letters';
23
+ import { createParallax } from '../recipes/parallax/create-parallax';
24
+ import { createDepthGrid } from '../recipes/depth-grid/create-depth-grid';
25
+ import { createFrameZoom } from '../recipes/frame-zoom/create-frame-zoom';
26
+ import { createContrast } from '../recipes/contrast/create-contrast';
27
+ import { createMarquee } from '../recipes/marquee/create-marquee';
28
+ import { createCardDock } from '../recipes/card-dock/create-card-dock';
29
+ import { createSnapStrip } from '../recipes/snap-strip/create-snap-strip';
30
+ import { createFillText } from '../recipes/fill-text/create-fill-text';
31
+ import { parseOptions } from './parse-options';
32
+ import { createTicketMenu } from '../recipes/ticket-menu/create-ticket-menu';
33
+ import { createInfiniteReel } from '../recipes/infinite-reel/create-infinite-reel';
34
+ import { createRepelBento } from '../recipes/repel-bento/create-repel-bento';
35
+ import { createRotator } from '../recipes/rotator/create-rotator';
36
+ import { createDragRuler } from '../recipes/drag-ruler/create-drag-ruler';
37
+ import { createDockPill } from '../recipes/dock-pill/create-dock-pill';
38
+
39
+ type Factory = (el: HTMLElement, options: Record<string, unknown>) => Instance;
40
+
41
+ const factories = {
42
+ 'spring-cards': createSpringCards,
43
+ 'curved-carousel': (el, options) =>
44
+ createCurvedCarousel(el, {
45
+ ...options,
46
+ onSelect: (index, slide) => el.dispatchEvent(new CustomEvent('mk:select', { detail: { index, slide }, bubbles: true })),
47
+ }),
48
+ pile: createPile,
49
+ words: createWords,
50
+ flow: createFlow,
51
+ pop: createPop,
52
+ 'fit-text': createFitText,
53
+ 'stretch-footer': createStretchFooter,
54
+ 'radial-menu': createRadialMenu,
55
+ tabs: createTabs,
56
+ counter: createCounter,
57
+ details: (el) => createDetails(el),
58
+ reveal: createReveal,
59
+ reflow: createReflow,
60
+ 'slot-text': createSlotText,
61
+ sortable: createSortable,
62
+ 'mask-text': createMaskText,
63
+ cascade: createCascade,
64
+ 'text-roll': createTextRoll,
65
+ letters: createLetters,
66
+ parallax: createParallax,
67
+ 'depth-grid': createDepthGrid,
68
+ 'frame-zoom': createFrameZoom,
69
+ contrast: createContrast,
70
+ marquee: createMarquee,
71
+ 'card-dock': createCardDock,
72
+ 'snap-strip': createSnapStrip,
73
+ 'fill-text': createFillText,
74
+ 'ticket-menu': createTicketMenu,
75
+ 'infinite-reel': createInfiniteReel,
76
+ 'repel-bento': createRepelBento,
77
+ 'rotator': createRotator,
78
+ 'drag-ruler': createDragRuler,
79
+ 'dock-pill': createDockPill,
80
+ } satisfies Record<string, Factory>;
81
+
82
+ export type RecipeName = keyof typeof factories;
83
+ export const recipeNames = Object.keys(factories) as RecipeName[];
84
+
85
+ /** Measured text: wait for web fonts, fit before splitting into words. */
86
+ const AFTER_FONTS: RecipeName[] = ['fit-text', 'words', 'slot-text'];
87
+
88
+ const instances = new WeakMap<HTMLElement, Map<RecipeName, Instance>>();
89
+
90
+ export interface AutoInitOptions {
91
+ /** Only start these recipes. */
92
+ only?: RecipeName[];
93
+ }
94
+
95
+ /**
96
+ * Start every `[data-mk]` recipe inside `root` (an element may list several:
97
+ * `data-mk="fit-text words"`). Options come from `data-mk-*` attributes.
98
+ * Returns a function that destroys what this call created. Safe to call again:
99
+ * elements that already run a recipe are skipped.
100
+ */
101
+ export function autoInit(root: ParentNode = document, { only }: AutoInitOptions = {}): () => void {
102
+ if (typeof window === 'undefined') return () => {};
103
+ const created: Array<[HTMLElement, RecipeName, Instance]> = [];
104
+ let cancelled = false;
105
+
106
+ const elements = [...root.querySelectorAll<HTMLElement>('[data-mk]')];
107
+ if (root instanceof HTMLElement && root.matches('[data-mk]')) elements.unshift(root);
108
+
109
+ const start = (el: HTMLElement, name: RecipeName) => {
110
+ if (cancelled) return;
111
+ const running = instances.get(el) ?? new Map<RecipeName, Instance>();
112
+ if (running.has(name)) return;
113
+ const instance = (factories[name] as Factory)(el, parseOptions(el));
114
+ running.set(name, instance);
115
+ instances.set(el, running);
116
+ created.push([el, name, instance]);
117
+ };
118
+
119
+ const now: Array<[HTMLElement, RecipeName]> = [];
120
+ const later: Array<[HTMLElement, RecipeName]> = [];
121
+ for (const el of elements) {
122
+ for (const raw of (el.dataset.mk ?? '').split(/\s+/).filter(Boolean)) {
123
+ if (!(raw in factories)) {
124
+ warnOnce(`autoInit: unknown recipe "${raw}" (known: ${recipeNames.join(', ')})`);
125
+ continue;
126
+ }
127
+ const name = raw as RecipeName;
128
+ if (only && !only.includes(name)) continue;
129
+ (AFTER_FONTS.includes(name) ? later : now).push([el, name]);
130
+ }
131
+ }
132
+
133
+ now.forEach(([el, name]) => start(el, name));
134
+ if (later.length) {
135
+ later.sort((a, b) => AFTER_FONTS.indexOf(a[1]) - AFTER_FONTS.indexOf(b[1]));
136
+ const fonts = document.fonts?.ready ?? Promise.resolve();
137
+ void Promise.race([fonts, new Promise((resolve) => setTimeout(resolve, 1500))]).then(() => {
138
+ later.forEach(([el, name]) => start(el, name));
139
+ if (!cancelled) ScrollTrigger.refresh();
140
+ });
141
+ }
142
+
143
+ return () => {
144
+ cancelled = true;
145
+ for (const [el, name, instance] of created.reverse()) {
146
+ instance.destroy();
147
+ instances.get(el)?.delete(name);
148
+ }
149
+ created.length = 0;
150
+ };
151
+ }
@@ -0,0 +1,3 @@
1
+ export { autoInit, recipeNames, type AutoInitOptions, type RecipeName } from './auto-init';
2
+ export { parseOptions } from './parse-options';
3
+ export { MK_HEAD_SCRIPT } from '../core/reduced';
@@ -0,0 +1,68 @@
1
+ /** Attributes the recipes write themselves or use for children; never options. */
2
+ const RESERVED = new Set([
3
+ '',
4
+ 'ready',
5
+ 'inview',
6
+ 'carousel',
7
+ 'open',
8
+ 'card',
9
+ 'slide',
10
+ 'toggle',
11
+ 'item',
12
+ 'phantom',
13
+ 'footerTitle',
14
+ 'tab',
15
+ 'indicator',
16
+ 'chevron',
17
+ 'text',
18
+ 'role',
19
+ 'motion',
20
+ 'veil',
21
+ 'swatch',
22
+ 'part',
23
+ 'sorting',
24
+ 'sortableGroup',
25
+ 'sortableLocked',
26
+ 'cascade',
27
+ 'on',
28
+ 'tone',
29
+ 'cursorZone',
30
+ 'hidden',
31
+ 'pressed',
32
+ 'parallaxTarget',
33
+ 'spread',
34
+ 'expanded',
35
+ 'settled',
36
+ 'mode',
37
+ 'active',
38
+ 'islandToggle',
39
+ 'islandPanel',
40
+ 'islandItem',
41
+ 'islandClose',
42
+ 'reelTrack',
43
+ 'reelCopy',
44
+ 'dragging',
45
+ 'dockItem',
46
+ ]);
47
+
48
+ const coerce = (value: string): unknown => {
49
+ if (value === '' || value === 'true') return true;
50
+ if (value === 'false') return false;
51
+ if (/^-?\d*\.?\d+$/.test(value)) return Number(value);
52
+ return value;
53
+ };
54
+
55
+ /**
56
+ * `data-mk-<option>` attributes → options object.
57
+ * `data-mk-throw-boost="2.5"` → `{ throwBoost: 2.5 }`, `data-mk-trigger="load"` → `{ trigger: 'load' }`.
58
+ */
59
+ export function parseOptions(el: HTMLElement): Record<string, unknown> {
60
+ const options: Record<string, unknown> = {};
61
+ for (const [key, raw] of Object.entries(el.dataset)) {
62
+ if (!key.startsWith('mk') || key.length === 2) continue;
63
+ const name = key.charAt(2).toLowerCase() + key.slice(3);
64
+ if (RESERVED.has(name)) continue;
65
+ options[name] = coerce(raw ?? '');
66
+ }
67
+ return options;
68
+ }
@@ -0,0 +1,90 @@
1
+ # chat
2
+
3
+ El chat de Stepbro (PrettyChat): burbujas de cristal sobre el fondo vivo, desvanecido de 64 px abajo, composer de 64 px que crece con el texto.
4
+
5
+ - **Mensaje nuevo:** la burbuja crece desde el lado de quien habla (`scale .5 → 1`, blur 24 px, 0.7 s curva firma, 60 ms entre burbujas) y sus palabras suben (16 px, blur 6, 0.25 s, 50 ms, `back.out`). La conversación baja al último (0.35 s).
6
+ - **Pensando:** `pending()` muestra "Pensando…" solo si la respuesta tarda más de 500 ms (con brillo que barre en 1.1 s). La respuesta se transforma desde ese hueco (Flip 0.7 s, sin escalar el texto) mientras sus palabras suben.
7
+ - **Composer:** crece/encoge con el texto (0.18 s, `power2.out`, desde 64 px). Enter envía, Shift+Enter salta de línea.
8
+ - **Confirmación:** una barra entra con flow y sus palabras suben (100 ms entre palabras).
9
+ - **Grabando:** el botón de micrófono pulsa (1.5 s).
10
+
11
+ Origen: Stepbro `pretty-chat`, `chat-message-enter`, `chat-pending-to-reply`, `chat-composer-resize`, `chat-confirm` (kit Money).
12
+
13
+ ## HTML / Astro
14
+
15
+ ```html
16
+ <div style="height: 520px">
17
+ <mk-chat background mic placeholder="Escribe un mensaje...">
18
+ <div data-mk-message data-mk-role="assistant"><span data-mk-text>Hola, ¿qué hacemos hoy?</span></div>
19
+ </mk-chat>
20
+ </div>
21
+
22
+ <script>
23
+ import { defineElements } from '@yewdiel21/motion-kit/elements';
24
+ defineElements(['mk-chat']);
25
+ const chat = document.querySelector('mk-chat');
26
+ chat.addEventListener('mk:send', async (event) => {
27
+ chat.add('user', event.detail.text);
28
+ chat.pending();
29
+ const answer = await miApi(event.detail.text);
30
+ chat.reply(answer);
31
+ });
32
+ </script>
33
+ ```
34
+
35
+ Vacío, `<mk-chat>` construye la lista y el composer; los `[data-mk-message]` hijos pasan a la lista como historial (quietos, salvo `animate-existing`). Con tu propio marcado: `[data-mk-part="messages"]`, `[data-mk-part="composer"]` (un `<form>`), `[data-mk-part="input"]`, `[data-mk-part="send"]`, `[data-mk-part="mic"]`; y `createChat(root, opciones)`.
36
+
37
+ ## React
38
+
39
+ ```tsx
40
+ const [messages, setMessages] = useState<{ id: string; role: 'user' | 'assistant'; text: string }[]>([]);
41
+ const [pending, setPending] = useState(false);
42
+
43
+ <div style={{ height: 520 }}>
44
+ <Chat.Root
45
+ onSend={async (text) => {
46
+ setMessages((m) => [...m, { id: crypto.randomUUID(), role: 'user', text }]);
47
+ setPending(true);
48
+ const answer = await miApi(text);
49
+ setMessages((m) => [...m, { id: crypto.randomUUID(), role: 'assistant', text: answer }]);
50
+ setPending(false); // en el mismo render que el mensaje: así se transforma desde "Pensando…"
51
+ }}
52
+ >
53
+ <ChatBackground />
54
+ <Chat.Messages pending={pending}>
55
+ {messages.map((m) => (
56
+ <Chat.Message key={m.id} role={m.role}>{m.text}</Chat.Message>
57
+ ))}
58
+ </Chat.Messages>
59
+ <Chat.Composer />
60
+ </Chat.Root>
61
+ </div>
62
+ ```
63
+
64
+ `useChat()` (dentro de `Chat.Root`) da el motor: `setRecording(on)`, `scrollToLatest()`. `<Chat.Confirm>` anima al montarse.
65
+
66
+ ## API
67
+
68
+ `createChat(root, { animateExisting, clearOnSend, labels: { thinking }, onSend })` →
69
+
70
+ | Método | Qué hace |
71
+ |---|---|
72
+ | `add(role, texto)` | Añade un mensaje de texto (entra solo) |
73
+ | `pending()` | "Pensando…" a los 500 ms |
74
+ | `reply(el \| texto)` | La respuesta: se transforma desde "Pensando…" o entra normal |
75
+ | `confirm(barra)` | Barra de confirmación con flow + palabras |
76
+ | `setRecording(on)` | Pulso en `[data-mk-part="mic"]` |
77
+ | `scrollToLatest()` | Baja al último mensaje |
78
+ | `adopt(el)` | Marca un mensaje como ya animado (para frameworks) |
79
+
80
+ Cualquier `[data-mk-message]` que entre en la lista (tuyo, de un framework o de `add`) se anima solo. Evento `mk:send` (`detail.text`, cancelable) antes de `onSend`.
81
+
82
+ Para otros frameworks: `capturePending(placeholder)` antes de cambiar el DOM y `morphPendingToReply(estado, respuesta, lista)` después.
83
+
84
+ ## Variables CSS
85
+
86
+ `--mk-chat-radius` (40px), `--mk-chat-bubble-radius` (32px), `--mk-chat-user-bg`, `--mk-chat-user-fg`, `--mk-chat-assistant-bg`, `--mk-chat-assistant-fg`.
87
+
88
+ ## Accesibilidad
89
+
90
+ La lista es `role="log"` con `aria-live="polite"`. El textarea y los botones tienen nombre (placeholder, "Enviar", "Micrófono"). Con movimiento reducido los mensajes aparecen sin animar y el brillo y el pulso se paran.
@@ -0,0 +1,236 @@
1
+ /* chat — Stepbro's PrettyChat: glass bubbles over the live background, a soft fade at the bottom, a 64 px composer. */
2
+ .mk-chat,
3
+ mk-chat {
4
+ --mk-chat-radius: var(--mk-radius-2xl);
5
+ --mk-chat-bubble-radius: var(--mk-radius-bubble);
6
+ --mk-chat-user-bg: color-mix(in srgb, var(--md-sys-color-on-background) 62%, transparent);
7
+ --mk-chat-user-fg: var(--md-sys-color-background);
8
+ --mk-chat-assistant-bg: color-mix(in srgb, var(--md-sys-color-background) 58%, transparent);
9
+ --mk-chat-assistant-fg: var(--md-sys-color-on-surface);
10
+ position: relative;
11
+ display: flex;
12
+ flex-direction: column;
13
+ height: 100%;
14
+ min-height: 0;
15
+ overflow: hidden;
16
+ border-radius: var(--mk-chat-radius);
17
+ color: var(--mk-text);
18
+ font-family: var(--mk-font);
19
+ }
20
+
21
+ .mk-chat > .mk-chat-background,
22
+ mk-chat > .mk-chat-background,
23
+ mk-chat > mk-chat-background {
24
+ z-index: 0;
25
+ }
26
+
27
+ :is(.mk-chat, mk-chat) [data-mk-part='messages'] {
28
+ --mk-chat-fade: 64px;
29
+ position: relative;
30
+ z-index: 1;
31
+ display: flex;
32
+ flex: 1 1 0;
33
+ flex-direction: column;
34
+ gap: 12px;
35
+ min-height: 0;
36
+ padding: 16px 16px 80px;
37
+ overflow-y: auto;
38
+ -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - var(--mk-chat-fade)), transparent 100%);
39
+ mask-image: linear-gradient(to bottom, #000 calc(100% - var(--mk-chat-fade)), transparent 100%);
40
+ overscroll-behavior: contain;
41
+ }
42
+
43
+ :is(.mk-chat, mk-chat) [data-mk-message] {
44
+ position: relative;
45
+ z-index: 10;
46
+ display: block;
47
+ flex: 0 0 auto;
48
+ width: fit-content;
49
+ max-width: 80%;
50
+ padding: 16px 24px;
51
+ overflow: hidden;
52
+ border-radius: var(--mk-chat-bubble-radius);
53
+ font-size: 16px;
54
+ font-weight: 500;
55
+ line-height: 1.3;
56
+ white-space: pre-wrap;
57
+ overflow-wrap: anywhere;
58
+ }
59
+
60
+ :is(.mk-chat, mk-chat) [data-mk-message][data-mk-role='user'] {
61
+ align-self: flex-end;
62
+ background: var(--mk-chat-user-bg);
63
+ color: var(--mk-chat-user-fg);
64
+ -webkit-backdrop-filter: blur(16px);
65
+ backdrop-filter: blur(16px);
66
+ }
67
+
68
+ :is(.mk-chat, mk-chat) [data-mk-message][data-mk-role='assistant'] {
69
+ align-self: flex-start;
70
+ background: var(--mk-chat-assistant-bg);
71
+ color: var(--mk-chat-assistant-fg);
72
+ -webkit-backdrop-filter: blur(16px);
73
+ backdrop-filter: blur(16px);
74
+ }
75
+
76
+ :is(.mk-chat, mk-chat) [data-mk-message][data-mk-role='system'] {
77
+ align-self: center;
78
+ max-width: 90%;
79
+ padding: 8px 16px;
80
+ background: none;
81
+ color: color-mix(in srgb, var(--md-sys-color-on-background) 62%, transparent);
82
+ font-size: 14px;
83
+ text-align: center;
84
+ }
85
+
86
+ @media (max-width: 679px) {
87
+ :is(.mk-chat, mk-chat) [data-mk-message][data-mk-role='assistant'] {
88
+ max-width: 100%;
89
+ }
90
+ }
91
+
92
+ :is(.mk-chat, mk-chat) [data-mk-text] {
93
+ display: block;
94
+ white-space: pre-wrap;
95
+ }
96
+
97
+ :is(.mk-chat, mk-chat) .mk-split-word {
98
+ white-space: pre;
99
+ }
100
+
101
+ /* The thinking placeholder and its glow sweep (1.1 s). */
102
+ :is(.mk-chat, mk-chat) [data-mk-pending] {
103
+ position: relative;
104
+ }
105
+
106
+ :is(.mk-chat, mk-chat) [data-mk-pending]::after {
107
+ position: absolute;
108
+ inset: 0 auto 0 0;
109
+ z-index: 2;
110
+ width: 50%;
111
+ background: radial-gradient(ellipse at center, var(--md-sys-color-background), transparent 70%);
112
+ content: '';
113
+ animation: mk-chat-glow 1.1s ease-in-out infinite;
114
+ }
115
+
116
+ .mk-chat__thinking {
117
+ display: inline-flex;
118
+ align-items: center;
119
+ gap: 8px;
120
+ }
121
+
122
+ @keyframes mk-chat-glow {
123
+ from {
124
+ transform: translate(-100%);
125
+ }
126
+
127
+ to {
128
+ transform: translate(200%);
129
+ }
130
+ }
131
+
132
+ /* Composer: the textarea grows from 64 px; round 64 px buttons. */
133
+ :is(.mk-chat, mk-chat) [data-mk-part='composer'] {
134
+ position: relative;
135
+ z-index: 2;
136
+ display: flex;
137
+ align-items: flex-end;
138
+ gap: 8px;
139
+ margin: 0;
140
+ padding: 8px;
141
+ }
142
+
143
+ :is(.mk-chat, mk-chat) [data-mk-part='input'],
144
+ :is(.mk-chat, mk-chat) [data-mk-part='composer'] textarea {
145
+ flex: 1;
146
+ min-width: 0;
147
+ min-height: 64px;
148
+ max-height: 40vh;
149
+ margin: 0;
150
+ padding: 18px 16px;
151
+ overflow: hidden;
152
+ box-sizing: border-box;
153
+ border: 0;
154
+ background: transparent;
155
+ color: inherit;
156
+ font: inherit;
157
+ font-size: 24px;
158
+ line-height: 1.3;
159
+ resize: none;
160
+ outline: none;
161
+ }
162
+
163
+ :is(.mk-chat, mk-chat) [data-mk-part='input']::placeholder {
164
+ color: var(--md-sys-color-on-background);
165
+ opacity: 0.5;
166
+ }
167
+
168
+ :is(.mk-chat, mk-chat) [data-mk-part='buttons'] {
169
+ display: flex;
170
+ gap: 8px;
171
+ }
172
+
173
+ :is(.mk-chat, mk-chat) :is([data-mk-part='mic'], [data-mk-part='send']) {
174
+ display: grid;
175
+ flex: none;
176
+ place-items: center;
177
+ width: 64px;
178
+ height: 64px;
179
+ padding: 0;
180
+ border: 0;
181
+ border-radius: 32px;
182
+ background: color-mix(in srgb, var(--md-sys-color-background) 40%, transparent);
183
+ color: inherit;
184
+ cursor: pointer;
185
+ -webkit-backdrop-filter: blur(16px);
186
+ backdrop-filter: blur(16px);
187
+ }
188
+
189
+ :is(.mk-chat, mk-chat) :is([data-mk-part='mic'], [data-mk-part='send']):disabled {
190
+ opacity: 0.6;
191
+ cursor: not-allowed;
192
+ }
193
+
194
+ :is(.mk-chat, mk-chat) :is([data-mk-part='mic'], [data-mk-part='send']):focus-visible {
195
+ outline: 2px solid var(--mk-accent);
196
+ outline-offset: 2px;
197
+ }
198
+
199
+ :is(.mk-chat, mk-chat) [data-mk-recording] {
200
+ background: color-mix(in srgb, var(--md-sys-color-error) 50%, transparent);
201
+ animation: mk-chat-recording 1.5s ease-in-out infinite;
202
+ }
203
+
204
+ @keyframes mk-chat-recording {
205
+ 0%,
206
+ 100% {
207
+ opacity: 1;
208
+ }
209
+
210
+ 50% {
211
+ opacity: 0.6;
212
+ }
213
+ }
214
+
215
+ /* A confirmation bar above the composer. */
216
+ :is(.mk-chat, mk-chat) [data-mk-part='confirm'] {
217
+ position: relative;
218
+ z-index: 2;
219
+ display: flex;
220
+ flex-wrap: wrap;
221
+ align-items: center;
222
+ gap: 8px;
223
+ margin: 0 16px 8px;
224
+ padding: 12px 16px;
225
+ border-radius: var(--mk-radius-lg);
226
+ background: var(--mk-chat-assistant-bg);
227
+ -webkit-backdrop-filter: blur(16px);
228
+ backdrop-filter: blur(16px);
229
+ }
230
+
231
+ @media (prefers-reduced-motion: reduce) {
232
+ :is(.mk-chat, mk-chat) [data-mk-pending]::after,
233
+ :is(.mk-chat, mk-chat) [data-mk-recording] {
234
+ animation: none;
235
+ }
236
+ }
@@ -0,0 +1,98 @@
1
+ import { MkElement, defineElement, type OptionType } from '../../elements/base';
2
+ import type { Instance } from '../../core/types';
3
+ import { iconSvg } from '../icons';
4
+ import { defineChatBackground } from '../chat-background/element';
5
+ import { createChat, type ChatInstance, type ChatRole } from './engine';
6
+
7
+ /**
8
+ * <mk-chat placeholder="Escribe un mensaje..." [background] [mic] [animate-existing]>
9
+ * (optional) your own [data-mk-part="messages"] / composer markup
10
+ * </mk-chat>
11
+ * Empty, it builds the messages list and the composer. Listen to `mk:send`
12
+ * (detail.text) and answer with `element.reply(text)` after `element.pending()`.
13
+ */
14
+ export class MkChatElement extends MkElement<Instance> {
15
+ static options: Record<string, OptionType> = {
16
+ placeholder: 'string',
17
+ background: 'boolean',
18
+ mic: 'boolean',
19
+ animateExisting: 'boolean',
20
+ thinking: 'string',
21
+ };
22
+
23
+ chat: ChatInstance | null = null;
24
+
25
+ protected create(options: Record<string, unknown>): Instance {
26
+ const made: Element[] = [];
27
+ if (!this.querySelector('[data-mk-part="messages"]')) {
28
+ if (options.background && !this.querySelector('.mk-chat-background, mk-chat-background')) {
29
+ defineChatBackground();
30
+ const background = document.createElement('mk-chat-background');
31
+ this.prepend(background);
32
+ made.push(background);
33
+ }
34
+ const messages = document.createElement('div');
35
+ messages.dataset.mkPart = 'messages';
36
+ // Messages written inside the element (history) move into the list.
37
+ messages.append(...[...this.children].filter((child) => child.matches('[data-mk-message]')));
38
+ const composer = document.createElement('form');
39
+ composer.dataset.mkPart = 'composer';
40
+ const input = document.createElement('textarea');
41
+ input.dataset.mkPart = 'input';
42
+ input.rows = 1;
43
+ input.placeholder = (options.placeholder as string | undefined) ?? 'Escribe un mensaje...';
44
+ input.setAttribute('aria-label', input.placeholder);
45
+ const buttons = document.createElement('div');
46
+ buttons.dataset.mkPart = 'buttons';
47
+ if (options.mic) {
48
+ const mic = document.createElement('button');
49
+ mic.type = 'button';
50
+ mic.dataset.mkPart = 'mic';
51
+ mic.setAttribute('aria-label', 'Micrófono');
52
+ mic.innerHTML = iconSvg('mic', 22);
53
+ buttons.append(mic);
54
+ }
55
+ const send = document.createElement('button');
56
+ send.type = 'submit';
57
+ send.dataset.mkPart = 'send';
58
+ send.setAttribute('aria-label', 'Enviar');
59
+ send.innerHTML = iconSvg('send', 22);
60
+ buttons.append(send);
61
+ composer.append(input, buttons);
62
+ this.append(messages, composer);
63
+ made.push(messages, composer);
64
+ }
65
+ const chat = createChat(this, {
66
+ animateExisting: Boolean(options.animateExisting),
67
+ labels: { thinking: options.thinking as string | undefined },
68
+ });
69
+ this.chat = chat;
70
+ return {
71
+ destroy: () => {
72
+ chat.destroy();
73
+ const messages = made.find((node) => node instanceof HTMLElement && node.dataset.mkPart === 'messages');
74
+ if (messages) this.append(...[...messages.children]);
75
+ for (const node of made) node.remove();
76
+ this.chat = null;
77
+ },
78
+ };
79
+ }
80
+
81
+ add(role: ChatRole, text: string) {
82
+ return this.chat?.add(role, text);
83
+ }
84
+
85
+ pending() {
86
+ this.chat?.pending();
87
+ }
88
+
89
+ reply(message: HTMLElement | string) {
90
+ return this.chat?.reply(message);
91
+ }
92
+
93
+ setRecording(on: boolean) {
94
+ this.chat?.setRecording(on);
95
+ }
96
+ }
97
+
98
+ export const defineChat = () => defineElement('mk-chat', MkChatElement);