@fungi.computer/caps 0.0.0-stage → 0.0.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 (387) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1125 -2
  3. package/dist/blocks/app-bar.d.ts +43 -0
  4. package/dist/blocks/app-bar.js +244 -0
  5. package/dist/blocks/app-status-bar.d.ts +93 -0
  6. package/dist/blocks/app-status-bar.js +42 -0
  7. package/dist/blocks/async-state.d.ts +21 -0
  8. package/dist/blocks/async-state.js +8 -0
  9. package/dist/blocks/card-grid.d.ts +11 -0
  10. package/dist/blocks/card-grid.js +21 -0
  11. package/dist/blocks/command-menu.d.ts +26 -0
  12. package/dist/blocks/command-menu.js +70 -0
  13. package/dist/blocks/composer.d.ts +23 -0
  14. package/dist/blocks/composer.js +13 -0
  15. package/dist/blocks/empty-state.d.ts +59 -0
  16. package/dist/blocks/empty-state.js +35 -0
  17. package/dist/blocks/faq-chat.d.ts +62 -0
  18. package/dist/blocks/faq-chat.js +77 -0
  19. package/dist/blocks/game-share-card.d.ts +17 -0
  20. package/dist/blocks/game-share-card.js +9 -0
  21. package/dist/blocks/launcher-grid.d.ts +50 -0
  22. package/dist/blocks/launcher-grid.js +11 -0
  23. package/dist/blocks/listing-detail.d.ts +23 -0
  24. package/dist/blocks/listing-detail.js +12 -0
  25. package/dist/blocks/loading-state.d.ts +5 -0
  26. package/dist/blocks/loading-state.js +8 -0
  27. package/dist/blocks/mosaic.d.ts +29 -0
  28. package/dist/blocks/mosaic.js +19 -0
  29. package/dist/blocks/permission-grid.d.ts +45 -0
  30. package/dist/blocks/permission-grid.js +14 -0
  31. package/dist/blocks/pricing-card.d.ts +53 -0
  32. package/dist/blocks/pricing-card.js +12 -0
  33. package/dist/blocks/release-tag.d.ts +20 -0
  34. package/dist/blocks/release-tag.js +10 -0
  35. package/dist/blocks/resource-select.d.ts +22 -0
  36. package/dist/blocks/resource-select.js +16 -0
  37. package/dist/blocks/result-state.d.ts +15 -0
  38. package/dist/blocks/result-state.js +8 -0
  39. package/dist/blocks/section-card.d.ts +31 -0
  40. package/dist/blocks/section-card.js +7 -0
  41. package/dist/blocks/section-heading.d.ts +13 -0
  42. package/dist/blocks/section-heading.js +11 -0
  43. package/dist/blocks/spec-sheet.d.ts +125 -0
  44. package/dist/blocks/spec-sheet.js +47 -0
  45. package/dist/blocks/status-tray.d.ts +83 -0
  46. package/dist/blocks/status-tray.js +79 -0
  47. package/dist/blocks/upload-queue.d.ts +48 -0
  48. package/dist/blocks/upload-queue.js +24 -0
  49. package/dist/components/action-row.d.ts +16 -0
  50. package/dist/components/action-row.js +11 -0
  51. package/dist/components/activity-line.d.ts +33 -0
  52. package/dist/components/activity-line.js +69 -0
  53. package/dist/components/alert.d.ts +17 -0
  54. package/dist/components/alert.js +17 -0
  55. package/dist/components/badge.d.ts +19 -0
  56. package/dist/components/badge.js +24 -0
  57. package/dist/components/button.d.ts +55 -0
  58. package/dist/components/button.js +230 -0
  59. package/dist/components/card.d.ts +61 -0
  60. package/dist/components/card.js +48 -0
  61. package/dist/components/chat-bubble.d.ts +34 -0
  62. package/dist/components/chat-bubble.js +10 -0
  63. package/dist/components/checkbox-bead.d.ts +6 -0
  64. package/dist/components/checkbox-bead.js +67 -0
  65. package/dist/components/checkbox.d.ts +21 -0
  66. package/dist/components/checkbox.js +46 -0
  67. package/dist/components/code-input.d.ts +35 -0
  68. package/dist/components/code-input.js +126 -0
  69. package/dist/components/combobox.d.ts +86 -0
  70. package/dist/components/combobox.js +218 -0
  71. package/dist/components/confirm-action.d.ts +23 -0
  72. package/dist/components/confirm-action.js +64 -0
  73. package/dist/components/copy-field.d.ts +53 -0
  74. package/dist/components/copy-field.js +49 -0
  75. package/dist/components/count-badge.d.ts +15 -0
  76. package/dist/components/count-badge.js +9 -0
  77. package/dist/components/cover-pattern.d.ts +19 -0
  78. package/dist/components/cover-pattern.js +74 -0
  79. package/dist/components/data-list.d.ts +25 -0
  80. package/dist/components/data-list.js +16 -0
  81. package/dist/components/dialog.d.ts +39 -0
  82. package/dist/components/dialog.js +73 -0
  83. package/dist/components/diff-view.d.ts +14 -0
  84. package/dist/components/diff-view.js +13 -0
  85. package/dist/components/disclosure.d.ts +29 -0
  86. package/dist/components/disclosure.js +19 -0
  87. package/dist/components/dropdown-menu.d.ts +49 -0
  88. package/dist/components/dropdown-menu.js +98 -0
  89. package/dist/components/field-guide.d.ts +41 -0
  90. package/dist/components/field-guide.js +18 -0
  91. package/dist/components/field.d.ts +164 -0
  92. package/dist/components/field.js +228 -0
  93. package/dist/components/game-feedback.d.ts +84 -0
  94. package/dist/components/game-feedback.js +48 -0
  95. package/dist/components/game-hud.d.ts +126 -0
  96. package/dist/components/game-hud.js +136 -0
  97. package/dist/components/generation-frame.d.ts +36 -0
  98. package/dist/components/generation-frame.js +97 -0
  99. package/dist/components/gooey.d.ts +32 -0
  100. package/dist/components/gooey.js +25 -0
  101. package/dist/components/hold-button.d.ts +26 -0
  102. package/dist/components/hold-button.js +115 -0
  103. package/dist/components/icon-button.d.ts +24 -0
  104. package/dist/components/icon-button.js +18 -0
  105. package/dist/components/icon-checkbox.d.ts +20 -0
  106. package/dist/components/icon-checkbox.js +9 -0
  107. package/dist/components/icon-choice-mark.d.ts +6 -0
  108. package/dist/components/icon-choice-mark.js +5 -0
  109. package/dist/components/icon-lamp.d.ts +42 -0
  110. package/dist/components/icon-lamp.js +15 -0
  111. package/dist/components/icon-radio.d.ts +16 -0
  112. package/dist/components/icon-radio.js +9 -0
  113. package/dist/components/inline-code.d.ts +3 -0
  114. package/dist/components/inline-code.js +7 -0
  115. package/dist/components/input-group.d.ts +9 -0
  116. package/dist/components/input-group.js +15 -0
  117. package/dist/components/input.d.ts +15 -0
  118. package/dist/components/input.js +15 -0
  119. package/dist/components/item-grid.d.ts +110 -0
  120. package/dist/components/item-grid.js +136 -0
  121. package/dist/components/keycap.d.ts +80 -0
  122. package/dist/components/keycap.js +18 -0
  123. package/dist/components/label.d.ts +13 -0
  124. package/dist/components/label.js +7 -0
  125. package/dist/components/listbox.d.ts +50 -0
  126. package/dist/components/listbox.js +134 -0
  127. package/dist/components/loader-blobs.d.ts +5 -0
  128. package/dist/components/loader-blobs.js +128 -0
  129. package/dist/components/loader.d.ts +21 -0
  130. package/dist/components/loader.js +25 -0
  131. package/dist/components/markdown.d.ts +23 -0
  132. package/dist/components/markdown.js +70 -0
  133. package/dist/components/minimap-labels.d.ts +39 -0
  134. package/dist/components/minimap-labels.js +113 -0
  135. package/dist/components/monogram.d.ts +32 -0
  136. package/dist/components/monogram.js +24 -0
  137. package/dist/components/motion-visibility.d.ts +2 -0
  138. package/dist/components/motion-visibility.js +25 -0
  139. package/dist/components/navigation-rail.d.ts +11 -0
  140. package/dist/components/navigation-rail.js +55 -0
  141. package/dist/components/overlay-root.d.ts +16 -0
  142. package/dist/components/overlay-root.js +24 -0
  143. package/dist/components/pixel-number.d.ts +50 -0
  144. package/dist/components/pixel-number.js +113 -0
  145. package/dist/components/popover.d.ts +31 -0
  146. package/dist/components/popover.js +24 -0
  147. package/dist/components/presence.d.ts +59 -0
  148. package/dist/components/presence.js +232 -0
  149. package/dist/components/price-tag.d.ts +11 -0
  150. package/dist/components/price-tag.js +6 -0
  151. package/dist/components/progress.d.ts +25 -0
  152. package/dist/components/progress.js +24 -0
  153. package/dist/components/prose.d.ts +14 -0
  154. package/dist/components/prose.js +10 -0
  155. package/dist/components/quota-meter.d.ts +24 -0
  156. package/dist/components/quota-meter.js +11 -0
  157. package/dist/components/radio-bead.d.ts +4 -0
  158. package/dist/components/radio-bead.js +53 -0
  159. package/dist/components/radio-group.d.ts +54 -0
  160. package/dist/components/radio-group.js +64 -0
  161. package/dist/components/resize-handle.d.ts +23 -0
  162. package/dist/components/resize-handle.js +31 -0
  163. package/dist/components/rough-drawing.d.ts +42 -0
  164. package/dist/components/rough-drawing.js +231 -0
  165. package/dist/components/rough.d.ts +68 -0
  166. package/dist/components/rough.js +33 -0
  167. package/dist/components/scroll-area.d.ts +12 -0
  168. package/dist/components/scroll-area.js +96 -0
  169. package/dist/components/scroll-motion.d.ts +12 -0
  170. package/dist/components/scroll-motion.js +31 -0
  171. package/dist/components/scroll-to-bottom-blob-state.d.ts +40 -0
  172. package/dist/components/scroll-to-bottom-blob-state.js +109 -0
  173. package/dist/components/scroll-to-bottom-blob.d.ts +16 -0
  174. package/dist/components/scroll-to-bottom-blob.js +174 -0
  175. package/dist/components/select.d.ts +9 -0
  176. package/dist/components/select.js +12 -0
  177. package/dist/components/selectable.d.ts +13 -0
  178. package/dist/components/selectable.js +45 -0
  179. package/dist/components/send-action.d.ts +33 -0
  180. package/dist/components/send-action.js +96 -0
  181. package/dist/components/separator.d.ts +11 -0
  182. package/dist/components/separator.js +7 -0
  183. package/dist/components/skeleton.d.ts +5 -0
  184. package/dist/components/skeleton.js +7 -0
  185. package/dist/components/slider.d.ts +18 -0
  186. package/dist/components/slider.js +31 -0
  187. package/dist/components/stat.d.ts +17 -0
  188. package/dist/components/stat.js +7 -0
  189. package/dist/components/status-bead.d.ts +9 -0
  190. package/dist/components/status-bead.js +107 -0
  191. package/dist/components/status-mark.d.ts +12 -0
  192. package/dist/components/status-mark.js +177 -0
  193. package/dist/components/status.d.ts +38 -0
  194. package/dist/components/status.js +28 -0
  195. package/dist/components/store-card.d.ts +21 -0
  196. package/dist/components/store-card.js +10 -0
  197. package/dist/components/surface.d.ts +89 -0
  198. package/dist/components/surface.js +31 -0
  199. package/dist/components/switch-bead.d.ts +4 -0
  200. package/dist/components/switch-bead.js +62 -0
  201. package/dist/components/switch.d.ts +11 -0
  202. package/dist/components/switch.js +36 -0
  203. package/dist/components/tabs.d.ts +55 -0
  204. package/dist/components/tabs.js +81 -0
  205. package/dist/components/technonomicon.d.ts +192 -0
  206. package/dist/components/technonomicon.js +121 -0
  207. package/dist/components/textarea.d.ts +9 -0
  208. package/dist/components/textarea.js +11 -0
  209. package/dist/components/toaster.d.ts +4 -0
  210. package/dist/components/toaster.js +13 -0
  211. package/dist/components/tool-call-chip.d.ts +22 -0
  212. package/dist/components/tool-call-chip.js +100 -0
  213. package/dist/components/tooltip.d.ts +41 -0
  214. package/dist/components/tooltip.js +169 -0
  215. package/dist/components/trading-card.d.ts +60 -0
  216. package/dist/components/trading-card.js +13 -0
  217. package/dist/components/undo-action.d.ts +52 -0
  218. package/dist/components/undo-action.js +100 -0
  219. package/dist/components/undo-toast.d.ts +21 -0
  220. package/dist/components/undo-toast.js +58 -0
  221. package/dist/components/visually-hidden.d.ts +9 -0
  222. package/dist/components/visually-hidden.js +11 -0
  223. package/dist/components/window.d.ts +65 -0
  224. package/dist/components/window.js +31 -0
  225. package/dist/components/wizard.d.ts +17 -0
  226. package/dist/components/wizard.js +19 -0
  227. package/dist/enhance/cigarette-card.d.ts +8 -0
  228. package/dist/enhance/cigarette-card.js +33 -0
  229. package/dist/enhance/sketch.d.ts +16 -0
  230. package/dist/enhance/sketch.js +130 -0
  231. package/dist/form-error.d.ts +7 -0
  232. package/dist/form-error.js +14 -0
  233. package/dist/form-fields.d.ts +15 -0
  234. package/dist/form-fields.js +250 -0
  235. package/dist/form-schema.d.ts +166 -0
  236. package/dist/form-schema.js +398 -0
  237. package/dist/form.d.ts +30 -0
  238. package/dist/form.js +20 -0
  239. package/dist/lib/cn.d.ts +8 -0
  240. package/dist/lib/cn.js +12 -0
  241. package/dist/lib/format-money.d.ts +2 -0
  242. package/dist/lib/format-money.js +22 -0
  243. package/dist/lib/glide.d.ts +12 -0
  244. package/dist/lib/glide.js +143 -0
  245. package/dist/lib/ime.d.ts +3 -0
  246. package/dist/lib/ime.js +8 -0
  247. package/dist/lib/motion-spring.d.ts +27 -0
  248. package/dist/lib/motion-spring.js +36 -0
  249. package/dist/lib/motion.d.ts +51 -0
  250. package/dist/lib/motion.js +161 -0
  251. package/dist/lib/reorder-motion.d.ts +13 -0
  252. package/dist/lib/reorder-motion.js +201 -0
  253. package/dist/lib/seed-hash.d.ts +6 -0
  254. package/dist/lib/seed-hash.js +13 -0
  255. package/dist/lib/tanstack-highlight.d.ts +4 -0
  256. package/dist/lib/tanstack-highlight.js +41 -0
  257. package/dist/schema-form.d.ts +120 -0
  258. package/dist/schema-form.js +129 -0
  259. package/dist/styles.css +18814 -0
  260. package/docs/api/README.md +134 -0
  261. package/docs/api/blocks/app-bar.md +227 -0
  262. package/docs/api/blocks/app-status-bar.md +257 -0
  263. package/docs/api/blocks/async-state.md +53 -0
  264. package/docs/api/blocks/card-grid.md +31 -0
  265. package/docs/api/blocks/command-menu.md +89 -0
  266. package/docs/api/blocks/composer.md +40 -0
  267. package/docs/api/blocks/empty-state.md +97 -0
  268. package/docs/api/blocks/faq-chat.md +147 -0
  269. package/docs/api/blocks/game-share-card.md +40 -0
  270. package/docs/api/blocks/launcher-grid.md +86 -0
  271. package/docs/api/blocks/listing-detail.md +51 -0
  272. package/docs/api/blocks/loading-state.md +21 -0
  273. package/docs/api/blocks/mosaic.md +94 -0
  274. package/docs/api/blocks/permission-grid.md +79 -0
  275. package/docs/api/blocks/pricing-card.md +90 -0
  276. package/docs/api/blocks/release-tag.md +62 -0
  277. package/docs/api/blocks/resource-select.md +126 -0
  278. package/docs/api/blocks/result-state.md +42 -0
  279. package/docs/api/blocks/section-card.md +42 -0
  280. package/docs/api/blocks/section-heading.md +61 -0
  281. package/docs/api/blocks/spec-sheet.md +400 -0
  282. package/docs/api/blocks/status-tray.md +141 -0
  283. package/docs/api/blocks/upload-queue.md +110 -0
  284. package/docs/api/components/action-row.md +51 -0
  285. package/docs/api/components/activity-line.md +103 -0
  286. package/docs/api/components/alert.md +60 -0
  287. package/docs/api/components/badge.md +77 -0
  288. package/docs/api/components/button.md +94 -0
  289. package/docs/api/components/card.md +165 -0
  290. package/docs/api/components/chat-bubble.md +66 -0
  291. package/docs/api/components/checkbox-bead.md +20 -0
  292. package/docs/api/components/checkbox.md +42 -0
  293. package/docs/api/components/code-input.md +61 -0
  294. package/docs/api/components/combobox.md +303 -0
  295. package/docs/api/components/confirm-action.md +58 -0
  296. package/docs/api/components/copy-field.md +106 -0
  297. package/docs/api/components/count-badge.md +39 -0
  298. package/docs/api/components/cover-pattern.md +43 -0
  299. package/docs/api/components/data-list.md +80 -0
  300. package/docs/api/components/dialog.md +235 -0
  301. package/docs/api/components/diff-view.md +59 -0
  302. package/docs/api/components/disclosure.md +71 -0
  303. package/docs/api/components/dropdown-menu.md +269 -0
  304. package/docs/api/components/field-guide.md +174 -0
  305. package/docs/api/components/field.md +252 -0
  306. package/docs/api/components/game-feedback.md +157 -0
  307. package/docs/api/components/game-hud.md +359 -0
  308. package/docs/api/components/generation-frame.md +59 -0
  309. package/docs/api/components/hold-button.md +44 -0
  310. package/docs/api/components/icon-button.md +56 -0
  311. package/docs/api/components/icon-checkbox.md +42 -0
  312. package/docs/api/components/icon-choice-mark.md +31 -0
  313. package/docs/api/components/icon-lamp.md +92 -0
  314. package/docs/api/components/icon-radio.md +42 -0
  315. package/docs/api/components/inline-code.md +21 -0
  316. package/docs/api/components/input-group.md +53 -0
  317. package/docs/api/components/input.md +54 -0
  318. package/docs/api/components/item-grid.md +262 -0
  319. package/docs/api/components/keycap.md +102 -0
  320. package/docs/api/components/label.md +40 -0
  321. package/docs/api/components/listbox.md +131 -0
  322. package/docs/api/components/loader-blobs.md +9 -0
  323. package/docs/api/components/loader.md +51 -0
  324. package/docs/api/components/markdown.md +91 -0
  325. package/docs/api/components/minimap-labels.md +84 -0
  326. package/docs/api/components/monogram.md +96 -0
  327. package/docs/api/components/motion-visibility.md +9 -0
  328. package/docs/api/components/navigation-rail.md +37 -0
  329. package/docs/api/components/overlay-root.md +92 -0
  330. package/docs/api/components/pixel-number.md +120 -0
  331. package/docs/api/components/popover.md +80 -0
  332. package/docs/api/components/presence.md +106 -0
  333. package/docs/api/components/price-tag.md +48 -0
  334. package/docs/api/components/progress.md +90 -0
  335. package/docs/api/components/prose.md +48 -0
  336. package/docs/api/components/quota-meter.md +47 -0
  337. package/docs/api/components/radio-bead.md +9 -0
  338. package/docs/api/components/radio-group.md +126 -0
  339. package/docs/api/components/resize-handle.md +46 -0
  340. package/docs/api/components/rough-drawing.md +122 -0
  341. package/docs/api/components/rough.md +66 -0
  342. package/docs/api/components/scroll-area.md +22 -0
  343. package/docs/api/components/scroll-motion.md +9 -0
  344. package/docs/api/components/scroll-to-bottom-blob-state.md +162 -0
  345. package/docs/api/components/scroll-to-bottom-blob.md +73 -0
  346. package/docs/api/components/select.md +39 -0
  347. package/docs/api/components/send-action.md +58 -0
  348. package/docs/api/components/separator.md +38 -0
  349. package/docs/api/components/skeleton.md +31 -0
  350. package/docs/api/components/slider.md +70 -0
  351. package/docs/api/components/stat.md +42 -0
  352. package/docs/api/components/status-bead.md +19 -0
  353. package/docs/api/components/status-mark.md +19 -0
  354. package/docs/api/components/status.md +105 -0
  355. package/docs/api/components/store-card.md +53 -0
  356. package/docs/api/components/surface.md +161 -0
  357. package/docs/api/components/switch-bead.md +9 -0
  358. package/docs/api/components/switch.md +40 -0
  359. package/docs/api/components/tabs.md +138 -0
  360. package/docs/api/components/technonomicon.md +773 -0
  361. package/docs/api/components/textarea.md +40 -0
  362. package/docs/api/components/toaster.md +29 -0
  363. package/docs/api/components/tool-call-chip.md +68 -0
  364. package/docs/api/components/tooltip.md +113 -0
  365. package/docs/api/components/trading-card.md +95 -0
  366. package/docs/api/components/undo-action.md +117 -0
  367. package/docs/api/components/undo-toast.md +49 -0
  368. package/docs/api/components/visually-hidden.md +37 -0
  369. package/docs/api/components/window.md +291 -0
  370. package/docs/api/components/wizard.md +65 -0
  371. package/docs/api/enhance/cigarette-card.md +52 -0
  372. package/docs/api/enhance/sketch.md +42 -0
  373. package/docs/api/form.md +1623 -0
  374. package/docs/api/lib/cn.md +46 -0
  375. package/docs/api/lib/format-money.md +30 -0
  376. package/docs/api/lib/glide.md +36 -0
  377. package/docs/api/lib/ime.md +29 -0
  378. package/docs/api/lib/motion-spring.md +91 -0
  379. package/docs/api/lib/motion.md +212 -0
  380. package/docs/api/lib/reorder-motion.md +46 -0
  381. package/docs/api/lib/seed-hash.md +31 -0
  382. package/docs/api/lib/tanstack-highlight.md +40 -0
  383. package/package.json +597 -3
  384. package/src/app-status-bar.css +158 -0
  385. package/src/fonts.css +1 -0
  386. package/src/status-tray.css +282 -0
  387. package/src/styles.css +10177 -0
@@ -0,0 +1,174 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ArrowDownToLine } from "lucide-react";
3
+ import { memo, useEffect, useId, useRef } from "react";
4
+ import { useMotion } from "../lib/motion.js";
5
+ import { observeMotionVisibility } from "./motion-visibility.js";
6
+ import { createDropState, distanceToBottom, stepDrop, } from "./scroll-to-bottom-blob-state.js";
7
+ /** A continuously tugged water drop, with its own visibility and focus lifecycle. */
8
+ export const ScrollToBottomBlob = memo(function ScrollToBottomBlob({ direction = "forward", scrollerRef, onJump, unread = 0, focusAfterJump, }) {
9
+ const frame = useRef(null);
10
+ const visual = useRef(null);
11
+ const ball = useRef(null);
12
+ const anchor = useRef(null);
13
+ const bead = useRef(null);
14
+ const button = useRef(null);
15
+ const icon = useRef(null);
16
+ const state = useRef(createDropState());
17
+ const wake = useRef(() => undefined);
18
+ const jump = useRef(false);
19
+ const count = useRef(unread);
20
+ const motion = useMotion(frame);
21
+ const id = `caps-drop-${useId().replaceAll(":", "")}`;
22
+ useEffect(() => {
23
+ count.current = unread;
24
+ wake.current();
25
+ }, [unread]);
26
+ useEffect(() => {
27
+ const scroller = scrollerRef.current;
28
+ const root = visual.current;
29
+ const body = ball.current;
30
+ const fixed = anchor.current;
31
+ const incoming = bead.current;
32
+ const control = button.current;
33
+ const arrow = icon.current;
34
+ const container = frame.current;
35
+ if (!scroller ||
36
+ !root ||
37
+ !body ||
38
+ !fixed ||
39
+ !incoming ||
40
+ !control ||
41
+ !arrow ||
42
+ !container)
43
+ return;
44
+ let raf = 0;
45
+ let previousTime = 0;
46
+ let lastTop = scroller.scrollTop;
47
+ let visible = true;
48
+ const draw = () => {
49
+ const s = state.current;
50
+ root.dataset.motionState = s.phase;
51
+ root.style.display = s.phase === "hidden" ? "none" : "";
52
+ control.tabIndex = s.phase === "hidden" || s.phase === "merging" ? -1 : 0;
53
+ control.disabled = s.phase === "merging";
54
+ control.setAttribute("aria-label", `Jump to bottom${s.unread ? `, ${String(s.unread)} new message${s.unread === 1 ? "" : "s"}` : ""}`);
55
+ root.style.opacity = motion.reduced ? String(s.opacity) : "";
56
+ fixed.style.display =
57
+ motion.reduced || s.phase === "resting" || s.phase === "hidden"
58
+ ? "none"
59
+ : "";
60
+ incoming.style.display = motion.reduced || s.bead === 0 ? "none" : "";
61
+ if (motion.reduced) {
62
+ body.style.transform = "";
63
+ body.style.transformOrigin = "";
64
+ body.style.width = s.unread
65
+ ? `${String(control.clientWidth * s.volume)}px`
66
+ : "";
67
+ body.style.height = s.unread
68
+ ? `${String(control.clientWidth * s.volume)}px`
69
+ : "";
70
+ arrow.style.transform = "";
71
+ arrow.style.opacity = "";
72
+ fixed.style.transform = "";
73
+ incoming.style.transform = "";
74
+ return;
75
+ }
76
+ body.style.width = "";
77
+ body.style.height = "";
78
+ if (s.phase === "resting" && s.unread === 0) {
79
+ body.style.transform = "";
80
+ body.style.transformOrigin = "";
81
+ arrow.style.transform = "";
82
+ arrow.style.opacity = "";
83
+ fixed.style.transform = "";
84
+ incoming.style.transform = "";
85
+ return;
86
+ }
87
+ const tug = Math.min(1, (Math.abs(s.y) / 9) * 0.7 + (Math.abs(s.vy) / 120) * 0.3);
88
+ const stretch = 1 + motion.stretch * tug;
89
+ const volume = Math.max(0.001, s.volume);
90
+ const well = Math.max(0, 1 - volume);
91
+ body.style.transformOrigin = s.y >= 0 ? "50% 0%" : "50% 100%";
92
+ body.style.transform = `translateY(${String(s.y)}px) scale(${String((volume / Math.sqrt(stretch)) * (1 - 0.12 * well))}, ${String(volume * stretch * (1 + 0.1 * well))})`;
93
+ fixed.style.transform = `scale(${String(Math.min(1, Math.abs(s.y) / 3) * volume * 0.55)})`;
94
+ incoming.style.transform = `translateY(${String(-34 * s.bead * s.bead)}px) scale(${String(0.62 * Math.min(1, s.bead * 3))})`;
95
+ arrow.style.transform = `translateY(${String(s.y * 0.8)}px)`;
96
+ arrow.style.opacity = String(s.opacity);
97
+ };
98
+ const tick = (now) => {
99
+ raf = 0;
100
+ const elapsed = previousTime ? now - previousTime : 16;
101
+ previousTime = now;
102
+ const top = scroller.scrollTop;
103
+ state.current = stepDrop(state.current, {
104
+ distance: distanceToBottom(scroller, direction),
105
+ delta: top - lastTop,
106
+ unread: count.current,
107
+ jump: jump.current,
108
+ }, elapsed, motion);
109
+ lastTop = top;
110
+ jump.current = false;
111
+ draw();
112
+ const s = state.current;
113
+ if (s.phase !== "hidden" &&
114
+ (!s.shown || s.phase !== "resting" || s.opacity < 1))
115
+ schedule();
116
+ };
117
+ function schedule() {
118
+ if (!visible || raf)
119
+ return;
120
+ raf = requestAnimationFrame(tick);
121
+ }
122
+ const onScroll = () => {
123
+ if (!raf)
124
+ previousTime = 0;
125
+ schedule();
126
+ };
127
+ wake.current = onScroll;
128
+ const stopObserving = observeMotionVisibility(container, (next) => {
129
+ visible = next;
130
+ if (!next) {
131
+ cancelAnimationFrame(raf);
132
+ raf = 0;
133
+ }
134
+ else {
135
+ previousTime = 0;
136
+ lastTop = scroller.scrollTop;
137
+ schedule();
138
+ }
139
+ });
140
+ const browser = globalThis;
141
+ const resize = browser.ResizeObserver
142
+ ? new browser.ResizeObserver(onScroll)
143
+ : undefined;
144
+ resize?.observe(scroller);
145
+ if (scroller.firstElementChild)
146
+ resize?.observe(scroller.firstElementChild);
147
+ scroller.addEventListener("scroll", onScroll, { passive: true });
148
+ schedule();
149
+ return () => {
150
+ wake.current = () => undefined;
151
+ cancelAnimationFrame(raf);
152
+ scroller.removeEventListener("scroll", onScroll);
153
+ resize?.disconnect();
154
+ stopObserving();
155
+ };
156
+ }, [direction, scrollerRef, motion]);
157
+ const activate = () => {
158
+ if (state.current.phase === "hidden" || state.current.phase === "merging")
159
+ return;
160
+ jump.current = true;
161
+ count.current = 0;
162
+ wake.current();
163
+ if (document.activeElement === button.current) {
164
+ const target = focusAfterJump?.current ?? scrollerRef.current;
165
+ if (target) {
166
+ if (target === scrollerRef.current && !target.hasAttribute("tabindex"))
167
+ target.tabIndex = -1;
168
+ target.focus({ preventScroll: true });
169
+ }
170
+ }
171
+ onJump();
172
+ };
173
+ return (_jsx("div", { ref: frame, className: "absolute bottom-4 right-4 z-1 h-8 w-8 md:h-6 md:w-6", children: _jsxs("div", { ref: visual, className: "relative h-full w-full", "data-motion-state": "hidden", style: { display: "none" }, children: [_jsxs("div", { className: "absolute -inset-1.5", style: { filter: `url(#${id})`, willChange: "filter" }, children: [_jsx("svg", { "aria-hidden": "true", className: "absolute h-0 w-0", children: _jsx("defs", { children: _jsxs("filter", { id: id, x: "-60%", y: "-60%", width: "220%", height: "220%", children: [_jsx("feGaussianBlur", { in: "SourceGraphic", result: "blur", stdDeviation: "4" }), _jsx("feColorMatrix", { in: "blur", type: "matrix", values: "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 20 -9" })] }) }) }), _jsx("div", { className: "absolute inset-0 m-auto h-8 w-8 md:h-6 md:w-6", ref: ball, "data-drop-body": "", children: _jsx("div", { className: "absolute inset-0 rounded-full border border-base-300 bg-base-200 shadow-sm" }) }), _jsx("div", { ref: anchor, className: "absolute inset-0 m-auto h-8 w-8 rounded-full bg-base-200 md:h-6 md:w-6", style: { display: "none" } }), _jsx("div", { ref: bead, className: "absolute inset-0 m-auto h-8 w-8 rounded-full bg-base-200 md:h-6 md:w-6", style: { display: "none" }, "data-drop-incoming": "" })] }), _jsx("button", { ref: button, "aria-label": "Jump to bottom", className: "absolute inset-0 z-1 grid place-items-center rounded-full caps-focus-color", "data-testid": "jump-to-bottom", onClick: activate, title: "Jump to bottom", type: "button", tabIndex: -1, children: _jsx(ArrowDownToLine, { ref: icon, className: "text-base-content", size: 13 }) })] }) }));
174
+ });
@@ -0,0 +1,9 @@
1
+ import { type SelectHTMLAttributes } from "react";
2
+ /** Props accepted by Select. */
3
+ export type SelectProps = {
4
+ invalid?: boolean;
5
+ } & SelectHTMLAttributes<HTMLSelectElement>;
6
+ /** A styled native select that preserves caller-owned value and event behavior. */
7
+ export declare const Select: import("react").ForwardRefExoticComponent<{
8
+ invalid?: boolean;
9
+ } & SelectHTMLAttributes<HTMLSelectElement> & import("react").RefAttributes<HTMLSelectElement>>;
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef } from "react";
3
+ import { ChevronDown } from "lucide-react";
4
+ import { cn } from "../lib/cn.js";
5
+ /** A styled native select that preserves caller-owned value and event behavior. */
6
+ export const Select = forwardRef(function Select({ className, invalid, ...props }, ref) {
7
+ return (_jsxs("span", { className: "caps-select-shell", children: [_jsx("select", { ...props, "aria-invalid": invalid
8
+ ? true
9
+ : props["aria-invalid"] === false
10
+ ? undefined
11
+ : props["aria-invalid"], className: cn("select select-bordered caps-select w-full", invalid && "select-error", className), ref: ref }), !props.multiple && (!props.size || props.size <= 1) ? (_jsx(ChevronDown, { className: "caps-select-chevron", "aria-hidden": "true" })) : null] }));
12
+ });
@@ -0,0 +1,13 @@
1
+ export type SelectableCollectionOptions<T> = {
2
+ getKey: (item: T) => string;
3
+ isDisabled?: (item: T) => boolean;
4
+ items: readonly T[];
5
+ };
6
+ export declare function useSelectableCollection<T>({ getKey, isDisabled, items, }: SelectableCollectionOptions<T>): {
7
+ activeIndex: number;
8
+ activeItem: T | undefined;
9
+ activeKey: string | null;
10
+ enabledItems: T[];
11
+ move: (direction: 1 | -1, edge?: "start" | "end") => void;
12
+ setActiveKey: import("react").Dispatch<import("react").SetStateAction<string | null>>;
13
+ };
@@ -0,0 +1,45 @@
1
+ import { useEffect, useMemo, useState } from "react";
2
+ export function useSelectableCollection({ getKey, isDisabled = () => false, items, }) {
3
+ const enabledItems = useMemo(() => {
4
+ const keys = items.map(getKey);
5
+ if (keys.some((key) => !key))
6
+ throw new Error("Selectable items require non-empty keys");
7
+ if (new Set(keys).size !== keys.length)
8
+ throw new Error("Selectable items require unique keys");
9
+ return items.filter((item) => !isDisabled(item));
10
+ }, [getKey, isDisabled, items]);
11
+ const [activeKey, setActiveKey] = useState(null);
12
+ useEffect(() => {
13
+ if (activeKey !== null &&
14
+ !enabledItems.some((item) => getKey(item) === activeKey))
15
+ setActiveKey(null);
16
+ }, [activeKey, enabledItems, getKey]);
17
+ const activeItem = enabledItems.find((item) => getKey(item) === activeKey);
18
+ const activeIndex = activeItem
19
+ ? enabledItems.findIndex((item) => getKey(item) === activeKey)
20
+ : -1;
21
+ const move = (direction, edge) => {
22
+ if (enabledItems.length === 0)
23
+ return;
24
+ const nextIndex = edge
25
+ ? edge === "start"
26
+ ? 0
27
+ : enabledItems.length - 1
28
+ : activeIndex < 0
29
+ ? direction > 0
30
+ ? 0
31
+ : enabledItems.length - 1
32
+ : (activeIndex + direction + enabledItems.length) % enabledItems.length;
33
+ const next = enabledItems[nextIndex];
34
+ if (next)
35
+ setActiveKey(getKey(next));
36
+ };
37
+ return {
38
+ activeIndex,
39
+ activeItem,
40
+ activeKey,
41
+ enabledItems,
42
+ move,
43
+ setActiveKey,
44
+ };
45
+ }
@@ -0,0 +1,33 @@
1
+ import { type ButtonProps } from "./button.js";
2
+ /** Where a composer's send action is: nothing to send, ready, or working. */
3
+ export type SendActionState = "empty" | "ready" | "busy";
4
+ /** Props accepted by SendAction. */
5
+ export type SendActionProps = {
6
+ state: SendActionState;
7
+ /** The accessible name while it sends, such as "Generate image". */
8
+ "aria-label": string;
9
+ /** Offered while busy: the arrow becomes a stop square that calls this. */
10
+ onStop?: (() => void) | undefined;
11
+ /** The accessible name of the stop action. */
12
+ stopLabel?: string;
13
+ /** Glyph size in pixels. */
14
+ iconSize?: number;
15
+ } & Omit<ButtonProps, "children" | "size" | "loading" | "aria-label">;
16
+ /**
17
+ * A composer's send key. Empty, it rests disabled; given something to send it
18
+ * wakes with a small pop; sending, its arrow launches away and a working
19
+ * indicator (or, with `onStop`, a stop square) takes its place; when the work
20
+ * ends the arrow drops back in. All timing comes from the theme's motion
21
+ * tokens, and reduced motion keeps short fades only.
22
+ */
23
+ export declare const SendAction: import("react").ForwardRefExoticComponent<{
24
+ state: SendActionState;
25
+ /** The accessible name while it sends, such as "Generate image". */
26
+ "aria-label": string;
27
+ /** Offered while busy: the arrow becomes a stop square that calls this. */
28
+ onStop?: (() => void) | undefined;
29
+ /** The accessible name of the stop action. */
30
+ stopLabel?: string;
31
+ /** Glyph size in pixels. */
32
+ iconSize?: number;
33
+ } & Omit<ButtonProps, "aria-label" | "children" | "size" | "loading"> & import("react").RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,96 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ArrowUp, Square } from "lucide-react";
3
+ import { forwardRef, useImperativeHandle, useLayoutEffect, useRef, } from "react";
4
+ import { canAnimate, motionEasing, readMotion, } from "../lib/motion.js";
5
+ import { Button } from "./button.js";
6
+ import { Loader } from "./loader.js";
7
+ function lift(face, motion) {
8
+ // The arrow launches upward and away: the prompt has gone.
9
+ return face.animate(motion.reduced
10
+ ? [{ opacity: 1 }, { opacity: 0 }]
11
+ : [
12
+ { opacity: 1, transform: "none" },
13
+ { opacity: 0, transform: "translateY(-120%)" },
14
+ ], {
15
+ duration: motion.reduced ? motion.fast : motion.normal,
16
+ easing: motionEasing(motion, motion.easeExit),
17
+ fill: "backwards",
18
+ });
19
+ }
20
+ function land(face, motion) {
21
+ // The arrow drops back in from below and settles, by the theme's bounce.
22
+ const overshoot = -12 * Math.min(1.5, motion.bounce / 0.3);
23
+ return face.animate(motion.reduced
24
+ ? [{ opacity: 0 }, { opacity: 1 }]
25
+ : [
26
+ { opacity: 0, transform: "translateY(70%)" },
27
+ {
28
+ opacity: 1,
29
+ transform: `translateY(${String(overshoot)}%)`,
30
+ offset: 0.6,
31
+ },
32
+ { opacity: 1, transform: "none" },
33
+ ], {
34
+ duration: motion.reduced ? motion.fast : motion.springDuration,
35
+ easing: motionEasing(motion, motion.ease),
36
+ fill: "backwards",
37
+ });
38
+ }
39
+ function fade(face, motion, to) {
40
+ return face.animate([{ opacity: 1 - to }, { opacity: to }], {
41
+ duration: motion.reduced ? motion.fast : motion.normal,
42
+ delay: to === 1 && !motion.reduced ? motion.fast / 2 : 0,
43
+ easing: motionEasing(motion, motion.ease),
44
+ fill: "backwards",
45
+ });
46
+ }
47
+ function charge(button, motion) {
48
+ // Once there is something to send, the key wakes with a small pop.
49
+ const pop = 1 + 0.05 * Math.min(1.5, 0.4 + motion.bounce / 0.3);
50
+ return button.animate([
51
+ { transform: "scale(1)" },
52
+ { transform: `scale(${String(pop)})`, offset: 0.4 },
53
+ { transform: "scale(1)" },
54
+ ], {
55
+ duration: motion.springDuration,
56
+ easing: motionEasing(motion, "ease-out"),
57
+ });
58
+ }
59
+ /**
60
+ * A composer's send key. Empty, it rests disabled; given something to send it
61
+ * wakes with a small pop; sending, its arrow launches away and a working
62
+ * indicator (or, with `onStop`, a stop square) takes its place; when the work
63
+ * ends the arrow drops back in. All timing comes from the theme's motion
64
+ * tokens, and reduced motion keeps short fades only.
65
+ */
66
+ export const SendAction = forwardRef(function SendAction({ state, onStop, stopLabel = "Stop", iconSize = 20, onClick, disabled, "aria-label": label, ...props }, ref) {
67
+ const node = useRef(null);
68
+ useImperativeHandle(ref, () => node.current);
69
+ const previous = useRef(state);
70
+ useLayoutEffect(() => {
71
+ const button = node.current;
72
+ const before = previous.current;
73
+ previous.current = state;
74
+ if (!button || before === state || !canAnimate(button))
75
+ return;
76
+ const arrow = button.querySelector('[data-glyph="arrow"]');
77
+ const work = button.querySelector('[data-glyph="busy"]');
78
+ if (!arrow || !work)
79
+ return;
80
+ const motion = readMotion(button);
81
+ const animations = [];
82
+ if (state === "busy")
83
+ animations.push(lift(arrow, motion), fade(work, motion, 1));
84
+ else if (before === "busy")
85
+ animations.push(fade(work, motion, 0), land(arrow, motion));
86
+ if (before === "empty" && state === "ready" && !motion.reduced)
87
+ animations.push(charge(button, motion));
88
+ return () => {
89
+ for (const animation of animations)
90
+ animation.cancel();
91
+ };
92
+ }, [state]);
93
+ const busy = state === "busy";
94
+ const stoppable = busy && onStop !== undefined;
95
+ return (_jsx(Button, { ...props, ref: node, "aria-busy": busy ? true : undefined, "aria-label": stoppable ? stopLabel : label, "data-send-state": state, disabled: disabled === true || state === "empty" || (busy && !stoppable), onClick: stoppable ? onStop : onClick, size: "icon", children: _jsxs("span", { className: "caps-send-glyph", "data-state": state, children: [_jsx("span", { "data-glyph": "arrow", children: _jsx(ArrowUp, { "aria-hidden": "true", size: iconSize }) }), _jsx("span", { "data-glyph": "busy", children: onStop === undefined ? (_jsx(Loader, { decorative: true, size: "sm" })) : (_jsx(Square, { "aria-hidden": "true", fill: "currentColor", size: Math.round(iconSize * 0.7) })) })] }) }));
96
+ });
@@ -0,0 +1,11 @@
1
+ import { type HTMLAttributes } from "react";
2
+ /** Props accepted by Separator. */
3
+ export type SeparatorProps = {
4
+ decorative?: boolean;
5
+ orientation?: "horizontal" | "vertical";
6
+ } & HTMLAttributes<HTMLDivElement>;
7
+ /** A visual separator between regions. */
8
+ export declare const Separator: import("react").ForwardRefExoticComponent<{
9
+ decorative?: boolean;
10
+ orientation?: "horizontal" | "vertical";
11
+ } & HTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef } from "react";
3
+ import { cn } from "../lib/cn.js";
4
+ /** A visual separator between regions. */
5
+ export const Separator = forwardRef(function Separator({ className, decorative = true, orientation = "horizontal", ...props }, ref) {
6
+ return (_jsx("div", { ...props, "aria-orientation": decorative ? undefined : orientation, className: cn("shrink-0 bg-base-300", orientation === "horizontal" ? "h-px w-full" : "h-full w-px", className), role: decorative ? "none" : "separator", ref: ref }));
7
+ });
@@ -0,0 +1,5 @@
1
+ import { type HTMLAttributes } from "react";
2
+ /** Props accepted by Skeleton. */
3
+ export type SkeletonProps = HTMLAttributes<HTMLSpanElement>;
4
+ /** A loading placeholder that preserves caller-provided native span props. */
5
+ export declare const Skeleton: import("react").ForwardRefExoticComponent<SkeletonProps & import("react").RefAttributes<HTMLSpanElement>>;
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef } from "react";
3
+ import { cn } from "../lib/cn.js";
4
+ /** A loading placeholder that preserves caller-provided native span props. */
5
+ export const Skeleton = forwardRef(function Skeleton({ className, ...props }, ref) {
6
+ return (_jsx("span", { ...props, "aria-hidden": props["aria-hidden"] ?? true, className: cn("skeleton", className), ref: ref }));
7
+ });
@@ -0,0 +1,18 @@
1
+ import { type InputHTMLAttributes } from "react";
2
+ /** Supported SliderTone values. */
3
+ export type SliderTone = "neutral" | "primary" | "secondary" | "accent" | "success" | "info" | "warning" | "error";
4
+ /** Supported SliderSize values. */
5
+ export type SliderSize = "xs" | "sm" | "default" | "lg";
6
+ /** Props accepted by Slider. */
7
+ export type SliderProps = {
8
+ size?: SliderSize;
9
+ tone?: SliderTone;
10
+ } & Omit<InputHTMLAttributes<HTMLInputElement>, "size" | "type">;
11
+ /**
12
+ * A native range input with product-neutral tone and size variants. On a
13
+ * Screen it reads the screen context and draws as a segmented level.
14
+ */
15
+ export declare const Slider: import("react").ForwardRefExoticComponent<{
16
+ size?: SliderSize;
17
+ tone?: SliderTone;
18
+ } & Omit<InputHTMLAttributes<HTMLInputElement>, "size" | "type"> & import("react").RefAttributes<HTMLInputElement>>;
@@ -0,0 +1,31 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef } from "react";
3
+ import { cn, cva } from "../lib/cn.js";
4
+ const sliderVariants = cva("range caps-slider", {
5
+ variants: {
6
+ tone: {
7
+ neutral: "range-neutral",
8
+ primary: "range-primary",
9
+ secondary: "range-secondary",
10
+ accent: "range-accent",
11
+ success: "range-success",
12
+ info: "range-info",
13
+ warning: "range-warning",
14
+ error: "range-error",
15
+ },
16
+ size: {
17
+ xs: "range-xs",
18
+ sm: "range-sm",
19
+ default: "range-md",
20
+ lg: "range-lg",
21
+ },
22
+ },
23
+ defaultVariants: { tone: "neutral", size: "default" },
24
+ });
25
+ /**
26
+ * A native range input with product-neutral tone and size variants. On a
27
+ * Screen it reads the screen context and draws as a segmented level.
28
+ */
29
+ export const Slider = forwardRef(function Slider({ className, size, tone, ...props }, ref) {
30
+ return (_jsx("input", { ...props, className: cn(sliderVariants({ size, tone }), className), ref: ref, type: "range" }));
31
+ });
@@ -0,0 +1,17 @@
1
+ import { type HTMLAttributes, type ReactNode } from "react";
2
+ /** Props accepted by Stat. */
3
+ export type StatProps = {
4
+ label: ReactNode;
5
+ value: ReactNode;
6
+ description?: ReactNode;
7
+ /** Reduce internal padding when a statistic sits inside a compact card. */
8
+ density?: "regular" | "compact";
9
+ } & Omit<HTMLAttributes<HTMLDListElement>, "children">;
10
+ /** A compact statistic presentation for caller-provided values. */
11
+ export declare const Stat: import("react").ForwardRefExoticComponent<{
12
+ label: ReactNode;
13
+ value: ReactNode;
14
+ description?: ReactNode;
15
+ /** Reduce internal padding when a statistic sits inside a compact card. */
16
+ density?: "regular" | "compact";
17
+ } & Omit<HTMLAttributes<HTMLDListElement>, "children"> & import("react").RefAttributes<HTMLDListElement>>;
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef } from "react";
3
+ import { cn } from "../lib/cn.js";
4
+ /** A compact statistic presentation for caller-provided values. */
5
+ export const Stat = forwardRef(function Stat({ className, density = "regular", label, value, description, ...props }, ref) {
6
+ return (_jsx("dl", { ...props, className: cn("stats", density === "compact" && "caps-stat-compact", className), ref: ref, children: _jsxs("div", { className: "stat", children: [_jsx("dt", { className: "stat-title text-base-content", children: label }), _jsx("dd", { className: "stat-value caps-numeric", children: value }), description !== undefined ? (_jsx("dd", { className: "stat-desc text-base-content", children: description })) : null] }) }));
7
+ });
@@ -0,0 +1,9 @@
1
+ import type { StatusMotion } from "./status.js";
2
+ /** The bead's own silhouettes. */
3
+ export type BeadGlyph = "dot" | "pill" | "bang";
4
+ /** @internal Persistent SVG geometry; Anime owns the moving attributes. */
5
+ export declare function StatusBead({ className, glyph, motion, }: {
6
+ className?: string;
7
+ glyph: BeadGlyph;
8
+ motion: StatusMotion;
9
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,107 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { animate } from "animejs";
3
+ import { motionTime, readMotion, useMotion } from "../lib/motion.js";
4
+ import { immediateTiming, themedEase, themedSpring, } from "../lib/motion-spring.js";
5
+ import { useId, useLayoutEffect, useRef } from "react";
6
+ import { GooeyFilterDefinition, STATUS_GOOEY_FILTER, GooeySurfaceDefinition, GooeyShadowDefinition, } from "./gooey.js";
7
+ import { observeMotionVisibility } from "./motion-visibility.js";
8
+ const silhouettes = {
9
+ dot: { y: -4, height: 8, cy: 0 },
10
+ pill: { y: -16, height: 32, cy: 0 },
11
+ bang: { y: -16, height: 20, cy: 12 },
12
+ };
13
+ /** @internal Persistent SVG geometry; Anime owns the moving attributes. */
14
+ export function StatusBead({ className, glyph, motion, }) {
15
+ const id = `caps-status-${useId().replaceAll(":", "")}`;
16
+ const body = useRef(null);
17
+ const drop = useRef(null);
18
+ const activity = useRef(null);
19
+ const visual = useRef(null);
20
+ const surface = useRef(null);
21
+ const dropLight = useRef(null);
22
+ const initial = useRef(silhouettes[glyph]).current;
23
+ const initialSurface = useRef(glyph === "dot" ? 1 : 4).current;
24
+ const initialDropLight = useRef(glyph === "bang" ? 1 : 0).current;
25
+ const previous = useRef(glyph);
26
+ const feel = useMotion(visual);
27
+ const { reduced } = feel;
28
+ useLayoutEffect(() => {
29
+ if (!body.current ||
30
+ !drop.current ||
31
+ !surface.current ||
32
+ !dropLight.current)
33
+ return;
34
+ const target = silhouettes[glyph];
35
+ const immediate = reduced || previous.current === glyph;
36
+ previous.current = glyph;
37
+ const motion = readMotion(body.current);
38
+ const timing = immediate
39
+ ? { duration: 0 }
40
+ : themedSpring(motion, { bounce: 0.3, duration: 360 });
41
+ const morph = animate(body.current, {
42
+ y: target.y,
43
+ height: target.height,
44
+ ...timing,
45
+ });
46
+ const separation = animate(drop.current, {
47
+ cy: target.cy,
48
+ ...(immediate
49
+ ? { duration: 0 }
50
+ : {
51
+ delay: motionTime(motion, 35),
52
+ ...themedSpring(motion, { bounce: 0.3, duration: 420 }),
53
+ }),
54
+ });
55
+ const material = animate(surface.current, {
56
+ gradientTransform: `matrix(1 0 0 ${glyph === "dot" ? "1" : "4"} 0 0)`,
57
+ ...timing,
58
+ });
59
+ const highlight = animate(dropLight.current, {
60
+ opacity: glyph === "bang" ? 1 : 0,
61
+ ...(immediate
62
+ ? immediateTiming
63
+ : themedEase(motion, { ease: "inOutSine", duration: 220 })),
64
+ });
65
+ // Cancellation preserves the current geometry for the next target.
66
+ return () => {
67
+ morph.cancel();
68
+ separation.cancel();
69
+ material.cancel();
70
+ highlight.cancel();
71
+ };
72
+ }, [glyph, reduced]);
73
+ useLayoutEffect(() => {
74
+ if (!activity.current || !visual.current || reduced || motion === "none")
75
+ return;
76
+ const loop = motion === "pulse"
77
+ ? animate(activity.current, {
78
+ opacity: [1, 0.45],
79
+ ...themedEase(feel, { ease: "inOutSine", duration: 750 }),
80
+ alternate: true,
81
+ loop: true,
82
+ })
83
+ : animate(activity.current, {
84
+ keyframes: {
85
+ "0%": { translateY: 0, scaleX: 1, scaleY: 1 },
86
+ "18%": { translateY: 0.25, scaleX: 1.1, scaleY: 0.91 },
87
+ "42%": { translateY: -1.3, scaleX: 0.94, scaleY: 1.06 },
88
+ "62%": { translateY: -1.65, scaleX: 1, scaleY: 1 },
89
+ "82%": { translateY: 0.15, scaleX: 1.06, scaleY: 0.95 },
90
+ "100%": { translateY: 0, scaleX: 1, scaleY: 1 },
91
+ },
92
+ ...themedEase(feel, { ease: "inOutSine", duration: 1000 }),
93
+ loop: true,
94
+ });
95
+ const stopObserving = observeMotionVisibility(visual.current, (visible) => {
96
+ if (visible)
97
+ loop.resume();
98
+ else
99
+ loop.pause();
100
+ });
101
+ return () => {
102
+ stopObserving();
103
+ loop.revert();
104
+ };
105
+ }, [motion, reduced, feel]);
106
+ return (_jsxs("svg", { ref: visual, "aria-hidden": "true", className: `caps-status-bead ${className ?? ""}`, width: "8", height: "8", viewBox: "-4 -4 8 8", focusable: "false", children: [_jsxs("defs", { children: [_jsx(GooeyFilterDefinition, { id: id, values: STATUS_GOOEY_FILTER }), _jsx(GooeySurfaceDefinition, { ref: surface, id: `${id}-surface`, transform: `matrix(1 0 0 ${String(initialSurface)} 0 0)` }), _jsxs("radialGradient", { id: `${id}-drop-light`, gradientUnits: "userSpaceOnUse", cx: "-1.2", cy: "10.4", r: "5.6", children: [_jsx("stop", { offset: "0", stopColor: "var(--color-white)", style: { stopOpacity: "calc(var(--depth) * 0.5)" } }), _jsx("stop", { offset: "1", stopColor: "var(--color-white)", stopOpacity: "0" })] }), _jsx(GooeyShadowDefinition, { id: `${id}-shadow` }), _jsx("mask", { id: `${id}-silhouette`, maskUnits: "userSpaceOnUse", x: "-12", y: "-24", width: "24", height: "48", style: { maskType: "alpha" }, children: _jsxs("g", { fill: "var(--color-white)", filter: `url(#${id})`, children: [_jsx("rect", { ref: body, x: "-4", y: initial.y, width: "8", height: initial.height, rx: "4" }), _jsx("circle", { ref: drop, cx: "0", cy: initial.cy, r: "4" })] }) })] }), _jsx("g", { ref: activity, filter: `url(#${id}-shadow)`, children: _jsxs("g", { mask: `url(#${id}-silhouette)`, children: [_jsx("rect", { x: "-12", y: "-24", width: "24", height: "48", fill: `url(#${id}-surface)` }), _jsx("rect", { ref: dropLight, x: "-12", y: "-24", width: "24", height: "48", fill: `url(#${id}-drop-light)`, opacity: initialDropLight })] }) })] }));
107
+ }
@@ -0,0 +1,12 @@
1
+ /** The task glyphs: waiting, working, finished, refused. */
2
+ export type StatusMarkGlyph = "ring" | "arc" | "check" | "cross";
3
+ /**
4
+ * @internal A 16-unit task mark. A dashed ring waits; an arc works (spinning,
5
+ * or filling to `progress`); when done the arc closes, the bead's material
6
+ * springs out to a disc and a check (or cross) is drawn out of it.
7
+ */
8
+ export declare function StatusMark({ className, glyph, progress, }: {
9
+ className?: string;
10
+ glyph: StatusMarkGlyph;
11
+ progress?: number | undefined;
12
+ }): import("react/jsx-runtime").JSX.Element;