@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,231 @@
1
+ /**
2
+ * Hand-drawn Rough.js chrome for any element marked `data-caps-rough`: boxes,
3
+ * underlines, circles, highlights, speech bubbles and leader lines drawn in the
4
+ * theme's ink or primary. The {@link Rough} component writes that markup; pages
5
+ * without React call {@link drawRough} once. Until Rough.js has drawn an
6
+ * element (or without JavaScript), plain CSS in `styles.css` stands in for it.
7
+ *
8
+ * Each element keeps its seed, so a redraw after a resize bends the same way.
9
+ */
10
+ const MARKS = [
11
+ "box",
12
+ "underline",
13
+ "circle",
14
+ "highlight",
15
+ "bubble",
16
+ "line",
17
+ ];
18
+ const SVG_NS = "http://www.w3.org/2000/svg";
19
+ /** How far a mark may spill past its element, in CSS pixels. */
20
+ const BLEED = 12;
21
+ function finite(value, fallback) {
22
+ const number = Number(value);
23
+ return value !== undefined && Number.isFinite(number) ? number : fallback;
24
+ }
25
+ /** Read `0,100 40,20 100,0` as percent points, dropping malformed pairs. */
26
+ export function parseRoughPoints(value) {
27
+ const points = [];
28
+ for (const pair of (value ?? "").trim().split(/\s+/)) {
29
+ const [x, y] = pair.split(",").map(Number);
30
+ if (x === undefined || y === undefined)
31
+ continue;
32
+ if (Number.isFinite(x) && Number.isFinite(y))
33
+ points.push([x, y]);
34
+ }
35
+ return points;
36
+ }
37
+ /** Parse an element's `data-caps-rough` and `data-rough-*` attributes. */
38
+ export function readRoughSpec(element) {
39
+ const data = element.dataset;
40
+ const mark = MARKS.find((name) => name === data.capsRough);
41
+ if (!mark)
42
+ return null;
43
+ const fill = data.roughFill === "solid" || data.roughFill === "hachure"
44
+ ? data.roughFill
45
+ : "none";
46
+ const tail = data.roughTail === "right" || data.roughTail === "none"
47
+ ? data.roughTail
48
+ : "left";
49
+ return {
50
+ mark,
51
+ seed: Math.max(1, Math.round(finite(data.roughSeed, 7))),
52
+ weight: finite(data.roughWeight, mark === "highlight" ? 1 : 2.2),
53
+ roughness: finite(data.roughRoughness, 1.4),
54
+ fill,
55
+ tail,
56
+ points: parseRoughPoints(data.roughPoints),
57
+ };
58
+ }
59
+ /** The drawables for one mark in a `width` × `height` box offset by BLEED. */
60
+ function sketch(generator, spec, width, height) {
61
+ const x = BLEED;
62
+ const y = BLEED;
63
+ const options = {
64
+ seed: spec.seed,
65
+ roughness: spec.roughness,
66
+ bowing: 1.2,
67
+ stroke: "currentColor",
68
+ strokeWidth: spec.weight,
69
+ };
70
+ if (spec.fill !== "none") {
71
+ options.fill = "currentColor";
72
+ options.fillStyle = spec.fill;
73
+ options.fillWeight = spec.weight * 0.6;
74
+ options.hachureGap = spec.weight * 3;
75
+ }
76
+ switch (spec.mark) {
77
+ case "box":
78
+ return [generator.rectangle(x, y, width, height, options)];
79
+ case "underline": {
80
+ const base = y + height + 2;
81
+ return [
82
+ generator.curve([
83
+ [x - 2, base],
84
+ [x + width * 0.5, base + 3],
85
+ [x + width + 2, base - 1],
86
+ ], options),
87
+ ];
88
+ }
89
+ case "circle":
90
+ return [
91
+ generator.ellipse(x + width / 2, y + height / 2, width + BLEED * 1.4, height + BLEED * 1.2, { ...options, disableMultiStroke: false }),
92
+ ];
93
+ case "highlight":
94
+ return [
95
+ generator.rectangle(x - 3, y + height * 0.45, width + 6, height * 0.5, {
96
+ ...options,
97
+ stroke: "none",
98
+ fill: "currentColor",
99
+ fillStyle: spec.fill === "hachure" ? "hachure" : "solid",
100
+ }),
101
+ ];
102
+ case "bubble": {
103
+ const tailX = spec.tail === "right" ? x + width * 0.78 : x + width * 0.22;
104
+ const flip = spec.tail === "right" ? 1 : -1;
105
+ const tail = spec.tail === "none"
106
+ ? []
107
+ : [
108
+ generator.linearPath([
109
+ [tailX - 10 * flip, y + height - 4],
110
+ [tailX + 18 * flip, y + height + BLEED - 1],
111
+ [tailX + 6 * flip, y + height - 6],
112
+ ], options),
113
+ ];
114
+ return [
115
+ generator.ellipse(x + width / 2, y + height / 2, width, height, options),
116
+ ...tail,
117
+ ];
118
+ }
119
+ case "line": {
120
+ const points = spec.points.map(([px, py]) => [
121
+ x + (px / 100) * width,
122
+ y + (py / 100) * height,
123
+ ]);
124
+ if (points.length < 2)
125
+ return [];
126
+ return [
127
+ points.length === 2
128
+ ? generator.linearPath(points, options)
129
+ : generator.curve(points, options),
130
+ ];
131
+ }
132
+ }
133
+ }
134
+ /** Draw (or redraw) one element's mark into its own overlay SVG. */
135
+ function paint(generator, element, spec) {
136
+ const width = element.offsetWidth;
137
+ const height = element.offsetHeight;
138
+ if (width < 1 || height < 1)
139
+ return;
140
+ let svg = element.querySelector(":scope > .caps-rough-svg");
141
+ if (!svg) {
142
+ svg = element.ownerDocument.createElementNS(SVG_NS, "svg");
143
+ svg.classList.add("caps-rough-svg");
144
+ svg.setAttribute("aria-hidden", "true");
145
+ svg.setAttribute("focusable", "false");
146
+ element.append(svg);
147
+ }
148
+ svg.setAttribute("width", String(width + BLEED * 2));
149
+ svg.setAttribute("height", String(height + BLEED * 2));
150
+ svg.setAttribute("viewBox", `0 0 ${String(width + BLEED * 2)} ${String(height + BLEED * 2)}`);
151
+ const paths = [];
152
+ for (const drawable of sketch(generator, spec, width, height)) {
153
+ for (const info of generator.toPaths(drawable)) {
154
+ const path = element.ownerDocument.createElementNS(SVG_NS, "path");
155
+ path.setAttribute("d", info.d);
156
+ path.setAttribute("stroke", info.stroke);
157
+ path.setAttribute("stroke-width", String(info.strokeWidth));
158
+ path.setAttribute("fill", info.fill ?? "none");
159
+ path.setAttribute("stroke-linecap", "round");
160
+ path.setAttribute("pathLength", "1");
161
+ paths.push(path);
162
+ }
163
+ }
164
+ svg.replaceChildren(...paths);
165
+ const delay = Number(element.dataset.roughDelay);
166
+ if (element.dataset.roughDelay && Number.isFinite(delay))
167
+ element.style.setProperty("--caps-rough-delay", `${String(delay)}s`);
168
+ element.dataset.roughDrawn = "";
169
+ }
170
+ let generatorPromise;
171
+ function loadGenerator() {
172
+ generatorPromise ??= import("roughjs/bin/generator.js").then(({ RoughGenerator }) => new RoughGenerator());
173
+ return generatorPromise;
174
+ }
175
+ /**
176
+ * Draw every `[data-caps-rough]` element inside `root` (and `root` itself),
177
+ * loading Rough.js on first use, and redraw each one when its size changes.
178
+ * Returns a function that stops watching sizes. If Rough.js cannot load, the
179
+ * CSS fallback stays.
180
+ */
181
+ export function drawRough(root = document) {
182
+ const elements = Array.from(root.querySelectorAll("[data-caps-rough]"));
183
+ if (root instanceof HTMLElement && root.dataset.capsRough !== undefined)
184
+ elements.unshift(root);
185
+ const marks = elements.flatMap((element) => {
186
+ const spec = readRoughSpec(element);
187
+ return spec ? [{ element, spec }] : [];
188
+ });
189
+ if (marks.length === 0)
190
+ return () => undefined;
191
+ let stopped = false;
192
+ let observer;
193
+ void loadGenerator()
194
+ .then((generator) => {
195
+ if (stopped)
196
+ return;
197
+ const sizes = new Map();
198
+ const redraw = (element, spec) => {
199
+ const size = `${String(element.offsetWidth)}x${String(element.offsetHeight)}`;
200
+ if (sizes.get(element) === size)
201
+ return;
202
+ sizes.set(element, size);
203
+ paint(generator, element, spec);
204
+ };
205
+ for (const { element, spec } of marks)
206
+ redraw(element, spec);
207
+ const browser = globalThis;
208
+ if (!("ResizeObserver" in globalThis) || !browser.ResizeObserver)
209
+ return;
210
+ const specs = new Map(marks.map(({ element, spec }) => [element, spec]));
211
+ observer = new browser.ResizeObserver((entries) => {
212
+ for (const entry of entries) {
213
+ const element = entry.target;
214
+ if (!(element instanceof HTMLElement))
215
+ continue;
216
+ const spec = specs.get(element);
217
+ if (spec)
218
+ redraw(element, spec);
219
+ }
220
+ });
221
+ for (const { element } of marks)
222
+ observer.observe(element);
223
+ })
224
+ .catch((cause) => {
225
+ console.error("Caps rough drawing failed; CSS fallback remains.", cause);
226
+ });
227
+ return () => {
228
+ stopped = true;
229
+ observer?.disconnect();
230
+ };
231
+ }
@@ -0,0 +1,68 @@
1
+ import { type HTMLAttributes } from "react";
2
+ import { type RoughMark, type RoughTone } from "./rough-drawing.js";
3
+ export type { RoughMark, RoughTone } from "./rough-drawing.js";
4
+ /** Props accepted by Rough. */
5
+ export type RoughProps = {
6
+ /** The mark: a box, underline, circle, highlight, speech bubble or line. */
7
+ mark: RoughMark;
8
+ /** Theme ink (the default) or the primary accent. */
9
+ tone?: RoughTone;
10
+ /** Fixes the wobble, so the mark looks the same on every render. */
11
+ seed?: number;
12
+ /** Stroke width in CSS pixels. */
13
+ weight?: number;
14
+ /** 0 is a ruler, 3 is a scribble. */
15
+ roughness?: number;
16
+ /** Fill inside a box, circle or bubble. */
17
+ fill?: "none" | "solid" | "hachure";
18
+ /** For `bubble`: the lower corner its tail leaves from. */
19
+ tail?: "left" | "right" | "none";
20
+ /** For `line`: percent points in the box, e.g. `0,100 100,0`. */
21
+ points?: string;
22
+ /**
23
+ * Draw the mark in once, stroke by stroke, after `delay`. Skipped for
24
+ * reduced motion.
25
+ */
26
+ animate?: boolean;
27
+ /** Seconds to wait before an `animate` mark draws in (default 0.2). */
28
+ delay?: number;
29
+ /** Render as a block (`div`) instead of inline (`span`). */
30
+ block?: boolean;
31
+ } & HTMLAttributes<HTMLElement>;
32
+ /**
33
+ * A hand-drawn Rough.js mark (box, underline, circle, highlight, speech bubble
34
+ * or leader line) around its children, in theme ink or primary. Rough.js loads
35
+ * lazily after hydration; until then, and without JavaScript, a plain CSS
36
+ * border, underline or tint stands in. The mark is `aria-hidden` and never
37
+ * takes pointer events, so the children keep their focus rings and clicks.
38
+ *
39
+ * Static pages can write the same attributes (see {@link drawRough}) instead
40
+ * of hydrating this component.
41
+ */
42
+ export declare const Rough: import("react").ForwardRefExoticComponent<{
43
+ /** The mark: a box, underline, circle, highlight, speech bubble or line. */
44
+ mark: RoughMark;
45
+ /** Theme ink (the default) or the primary accent. */
46
+ tone?: RoughTone;
47
+ /** Fixes the wobble, so the mark looks the same on every render. */
48
+ seed?: number;
49
+ /** Stroke width in CSS pixels. */
50
+ weight?: number;
51
+ /** 0 is a ruler, 3 is a scribble. */
52
+ roughness?: number;
53
+ /** Fill inside a box, circle or bubble. */
54
+ fill?: "none" | "solid" | "hachure";
55
+ /** For `bubble`: the lower corner its tail leaves from. */
56
+ tail?: "left" | "right" | "none";
57
+ /** For `line`: percent points in the box, e.g. `0,100 100,0`. */
58
+ points?: string;
59
+ /**
60
+ * Draw the mark in once, stroke by stroke, after `delay`. Skipped for
61
+ * reduced motion.
62
+ */
63
+ animate?: boolean;
64
+ /** Seconds to wait before an `animate` mark draws in (default 0.2). */
65
+ delay?: number;
66
+ /** Render as a block (`div`) instead of inline (`span`). */
67
+ block?: boolean;
68
+ } & HTMLAttributes<HTMLElement> & import("react").RefAttributes<HTMLElement>>;
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef, useEffect, useImperativeHandle, useRef, } from "react";
3
+ import { cn } from "../lib/cn.js";
4
+ import { drawRough } from "./rough-drawing.js";
5
+ /**
6
+ * A hand-drawn Rough.js mark (box, underline, circle, highlight, speech bubble
7
+ * or leader line) around its children, in theme ink or primary. Rough.js loads
8
+ * lazily after hydration; until then, and without JavaScript, a plain CSS
9
+ * border, underline or tint stands in. The mark is `aria-hidden` and never
10
+ * takes pointer events, so the children keep their focus rings and clicks.
11
+ *
12
+ * Static pages can write the same attributes (see {@link drawRough}) instead
13
+ * of hydrating this component.
14
+ */
15
+ export const Rough = forwardRef(({ mark, tone = "ink", seed = 7, weight, roughness, fill, tail, points, animate = false, delay, block = false, className, children, ...props }, forwardedRef) => {
16
+ const ref = useRef(null);
17
+ useImperativeHandle(forwardedRef, () => {
18
+ if (!ref.current)
19
+ throw new Error("Rough is not mounted");
20
+ return ref.current;
21
+ }, []);
22
+ useEffect(() => {
23
+ const element = ref.current;
24
+ if (!element)
25
+ return;
26
+ return drawRough(element);
27
+ }, [mark, seed, weight, roughness, fill, tail, points]);
28
+ const Element = block ? "div" : "span";
29
+ return (_jsx(Element, { ...props, ref: (element) => {
30
+ ref.current = element;
31
+ }, className: cn("caps-rough", `caps-rough-${mark}`, tone === "primary" && "caps-rough-primary", className), "data-caps-rough": mark, "data-rough-seed": seed, "data-rough-weight": weight, "data-rough-roughness": roughness, "data-rough-fill": fill, "data-rough-tail": tail, "data-rough-points": points, "data-rough-animate": animate ? "" : undefined, "data-rough-delay": delay, children: children }));
32
+ });
33
+ Rough.displayName = "Rough";
@@ -0,0 +1,12 @@
1
+ import { type HTMLAttributes } from "react";
2
+ import { type SurfaceProps } from "./surface.js";
3
+ /** Native scrolling with a plastic thumb responding to changes in scroll speed.
4
+ * The ref and native props address the viewport; className sizes the frame.
5
+ */
6
+ export declare const ScrollArea: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & {
7
+ surface?: boolean;
8
+ surfaceSize?: SurfaceProps["size"];
9
+ surfaceDepth?: SurfaceProps["depth"];
10
+ /** Use the shared theme-aware display material on the fixed viewport. */
11
+ screen?: boolean;
12
+ } & import("react").RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,96 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ScrollArea as BaseScrollArea } from "@base-ui/react/scroll-area";
3
+ import { animate } from "animejs";
4
+ import { forwardRef, useEffect, useRef } from "react";
5
+ import { cn } from "../lib/cn.js";
6
+ import { readMotion, useReducedMotion } from "../lib/motion.js";
7
+ import { themedEase, themedSpring } from "../lib/motion-spring.js";
8
+ import { overscrollImpulse, scrollImpulse } from "./scroll-motion.js";
9
+ import { Screen, Surface } from "./surface.js";
10
+ /** Native scrolling with a plastic thumb responding to changes in scroll speed.
11
+ * The ref and native props address the viewport; className sizes the frame.
12
+ */
13
+ export const ScrollArea = forwardRef(function ScrollArea({ children, className, surface = false, surfaceSize, surfaceDepth, screen = false, ...props }, ref) {
14
+ const root = useRef(null);
15
+ const thumbSurface = useRef(null);
16
+ const reduced = useReducedMotion();
17
+ useEffect(() => {
18
+ const frame = root.current;
19
+ const viewport = frame?.querySelector(".caps-scroll-viewport");
20
+ if (!viewport || !frame)
21
+ return;
22
+ let motion = null;
23
+ let idle;
24
+ let position = viewport.scrollTop;
25
+ let time = performance.now();
26
+ let velocity = 0;
27
+ const deform = (impulse) => {
28
+ const thumb = thumbSurface.current;
29
+ if (!impulse || reduced || !thumb)
30
+ return;
31
+ motion?.cancel();
32
+ thumb.style.transformOrigin = impulse.origin;
33
+ const feel = readMotion(thumb);
34
+ const kick = themedEase(feel, { ease: "outQuad", duration: 45 });
35
+ const settle = themedSpring(feel, { bounce: 0.18, duration: 180 });
36
+ motion = animate(thumb, {
37
+ scaleY: [
38
+ { to: impulse.scaleY, ...kick },
39
+ { to: 1, ...settle },
40
+ ],
41
+ scaleX: [
42
+ { to: impulse.scaleX, ...kick },
43
+ { to: 1, ...settle },
44
+ ],
45
+ });
46
+ };
47
+ const react = (nextVelocity, elapsed) => {
48
+ const impulse = scrollImpulse(velocity, nextVelocity, elapsed);
49
+ velocity = nextVelocity;
50
+ deform(impulse);
51
+ };
52
+ const onWheel = (event) => {
53
+ if (event.ctrlKey || Math.abs(event.deltaX) > Math.abs(event.deltaY))
54
+ return;
55
+ const unit = event.deltaMode === 1
56
+ ? 16
57
+ : event.deltaMode === 2
58
+ ? viewport.clientHeight
59
+ : 1;
60
+ const impulse = overscrollImpulse(viewport.scrollTop, viewport.scrollHeight - viewport.clientHeight, event.deltaY * unit);
61
+ if (!impulse)
62
+ return;
63
+ clearTimeout(idle);
64
+ velocity = 0;
65
+ deform(impulse);
66
+ };
67
+ const onScroll = () => {
68
+ const now = performance.now();
69
+ const gap = now - time;
70
+ const elapsed = gap > 100 ? 16 : Math.max(16, gap);
71
+ const delta = viewport.scrollTop - position;
72
+ if (gap > 100)
73
+ velocity = 0;
74
+ react(delta / elapsed, elapsed);
75
+ position = viewport.scrollTop;
76
+ time = now;
77
+ clearTimeout(idle);
78
+ idle = setTimeout(() => {
79
+ react(0, 32);
80
+ }, 64);
81
+ };
82
+ viewport.addEventListener("scroll", onScroll, { passive: true });
83
+ frame.addEventListener("wheel", onWheel, { passive: true });
84
+ return () => {
85
+ clearTimeout(idle);
86
+ motion?.cancel();
87
+ // A preference change re-runs this effect: never strand a squash.
88
+ if (thumbSurface.current)
89
+ thumbSurface.current.style.transform = "none";
90
+ viewport.removeEventListener("scroll", onScroll);
91
+ frame.removeEventListener("wheel", onWheel);
92
+ };
93
+ }, [screen, surface, reduced]);
94
+ const viewport = (_jsx(BaseScrollArea.Viewport, { render: screen ? _jsx(Screen, {}) : _jsx("div", {}), ...props, ref: ref, className: "caps-scroll-viewport", children: _jsx(BaseScrollArea.Content, { style: { minWidth: 0 }, children: children }) }));
95
+ return (_jsxs(BaseScrollArea.Root, { render: surface ? (_jsx(Surface, { size: surfaceSize ?? "md", depth: surfaceDepth ?? "shallow" })) : (_jsx("div", {})), ref: root, className: cn("caps-scroll-area", className), children: [screen ? (_jsx("div", { className: "caps-display-gasket caps-scroll-display", children: viewport })) : (viewport), _jsx(Surface, { size: "sm", depth: "deep", placement: "embedded", className: "caps-scroll-track-surface", children: _jsx(BaseScrollArea.Scrollbar, { className: "caps-scroll-track", children: _jsx(BaseScrollArea.Thumb, { className: "caps-scroll-thumb", children: _jsx("span", { className: "caps-scroll-thumb-surface", ref: thumbSurface }) }) }) })] }));
96
+ });
@@ -0,0 +1,12 @@
1
+ /** @internal Velocities are signed pixels/ms. Only meaningful speed changes deform the thumb. */
2
+ export declare function scrollImpulse(previousVelocity: number, velocity: number, elapsed: number): {
3
+ scaleY: number;
4
+ scaleX: number;
5
+ origin: string;
6
+ } | null;
7
+ /** @internal Wheel/trackpad pressure at an edge compresses toward that edge. */
8
+ export declare function overscrollImpulse(position: number, maximum: number, delta: number): {
9
+ scaleY: number;
10
+ scaleX: number;
11
+ origin: string;
12
+ } | null;
@@ -0,0 +1,31 @@
1
+ /** @internal Velocities are signed pixels/ms. Only meaningful speed changes deform the thumb. */
2
+ export function scrollImpulse(previousVelocity, velocity, elapsed) {
3
+ const acceleration = (Math.abs(velocity) - Math.abs(previousVelocity)) / Math.max(16, elapsed);
4
+ if (Math.abs(acceleration) < 0.035)
5
+ return null;
6
+ const amount = Math.min(0.04, Math.abs(acceleration) * 0.3);
7
+ const stretching = acceleration > 0;
8
+ const direction = Math.sign(velocity || previousVelocity);
9
+ return {
10
+ scaleY: 1 + (stretching ? amount : -amount),
11
+ scaleX: 1 + (stretching ? -amount / 2 : amount / 2),
12
+ origin: (stretching ? direction > 0 : direction < 0)
13
+ ? "center top"
14
+ : "center bottom",
15
+ };
16
+ }
17
+ /** @internal Wheel/trackpad pressure at an edge compresses toward that edge. */
18
+ export function overscrollImpulse(position, maximum, delta) {
19
+ if (maximum <= 0 || Math.abs(delta) < 4)
20
+ return null;
21
+ const top = position <= 0 && delta < 0;
22
+ const bottom = position >= maximum - 1 && delta > 0;
23
+ if (!top && !bottom)
24
+ return null;
25
+ const amount = Math.min(0.06, (Math.abs(delta) / 120) * 0.06);
26
+ return {
27
+ scaleY: 1 - amount,
28
+ scaleX: 1 + amount / 2,
29
+ origin: top ? "center top" : "center bottom",
30
+ };
31
+ }
@@ -0,0 +1,40 @@
1
+ import type { Motion } from "../lib/motion.js";
2
+ /** Scroll layout used to measure the distance from the latest content. */
3
+ export type ScrollDirection = "forward" | "reverse";
4
+ /** The native geometry needed by the drop, in CSS pixels. */
5
+ export type ScrollGeometry = Readonly<{
6
+ clientHeight: number;
7
+ scrollHeight: number;
8
+ scrollTop: number;
9
+ }>;
10
+ /** Distance from the visual end; column-reverse ends at scrollTop zero. */
11
+ export declare function distanceToBottom(geometry: ScrollGeometry, direction: ScrollDirection): number;
12
+ /** Theme-resolved physics and accessibility settings. */
13
+ export type DropTokens = Pick<Motion, "springDuration" | "bounce" | "stretch" | "fast" | "reduced">;
14
+ /** Continuous drop state; no DOM or timers are needed to advance it. */
15
+ export type DropState = Readonly<{
16
+ phase: "hidden" | "welling" | "resting" | "tugged" | "merging";
17
+ shown: boolean;
18
+ jumped: boolean;
19
+ velocity: number;
20
+ y: number;
21
+ vy: number;
22
+ volume: number;
23
+ vv: number;
24
+ opacity: number;
25
+ unread: number;
26
+ bead: number;
27
+ }>;
28
+ /** One frame's scroll displacement, end distance, unread count and jump intent. */
29
+ export type DropInput = Readonly<{
30
+ distance: number;
31
+ delta: number;
32
+ unread: number;
33
+ jump?: boolean;
34
+ }>;
35
+ /** A drop starts hidden until its scroller is more than 48px from the end. */
36
+ export declare function createDropState(): DropState;
37
+ /** Subtle unread volume, capped at fifteen percent. */
38
+ export declare function unreadVolume(unread: number): number;
39
+ /** Advance with bounded substeps so long browser frames cannot destabilize the springs. */
40
+ export declare function stepDrop(previous: DropState, input: DropInput, elapsed: number, tokens: DropTokens): DropState;
@@ -0,0 +1,109 @@
1
+ /** Distance from the visual end; column-reverse ends at scrollTop zero. */
2
+ export function distanceToBottom(geometry, direction) {
3
+ return Math.max(0, direction === "reverse"
4
+ ? -geometry.scrollTop
5
+ : geometry.scrollHeight - geometry.clientHeight - geometry.scrollTop);
6
+ }
7
+ /** A drop starts hidden until its scroller is more than 48px from the end. */
8
+ export function createDropState() {
9
+ return {
10
+ phase: "hidden",
11
+ shown: false,
12
+ jumped: false,
13
+ velocity: 0,
14
+ y: 0,
15
+ vy: 0,
16
+ volume: 0,
17
+ vv: 0,
18
+ opacity: 0,
19
+ unread: 0,
20
+ bead: 0,
21
+ };
22
+ }
23
+ /** Subtle unread volume, capped at fifteen percent. */
24
+ export function unreadVolume(unread) {
25
+ return 1 + Math.min(0.15, 0.06 * Math.log2(1 + Math.max(0, unread)));
26
+ }
27
+ function spring(x, v, target, dt, omega, damping) {
28
+ const nextV = v + (-omega * omega * (x - target) - 2 * damping * omega * v) * dt;
29
+ return [x + nextV * dt, nextV];
30
+ }
31
+ /** Advance with bounded substeps so long browser frames cannot destabilize the springs. */
32
+ export function stepDrop(previous, input, elapsed, tokens) {
33
+ const state = { ...previous };
34
+ const ms = Math.max(1, Math.min(64, elapsed));
35
+ const merging = input.jump === true || previous.jumped;
36
+ state.jumped = merging;
37
+ if (merging) {
38
+ state.phase = "merging";
39
+ state.shown = false;
40
+ state.unread = 0;
41
+ state.velocity = 0;
42
+ }
43
+ else {
44
+ if (input.distance > 48)
45
+ state.shown = true;
46
+ else if (input.distance < 8)
47
+ state.shown = false;
48
+ state.unread = state.shown ? Math.max(0, input.unread) : 0;
49
+ if (state.unread > previous.unread && state.shown && !tokens.reduced)
50
+ state.bead = 1;
51
+ state.velocity +=
52
+ (input.delta / ms - state.velocity) * (1 - Math.exp(-ms / 50));
53
+ }
54
+ if (!state.shown && previous.phase !== "hidden")
55
+ state.phase = "merging";
56
+ if (state.shown && !previous.shown)
57
+ state.phase = "welling";
58
+ const target = state.shown ? unreadVolume(state.unread) : 0;
59
+ if (previous.phase === "hidden" && state.shown) {
60
+ state.phase = "welling";
61
+ state.y = tokens.reduced ? 0 : 5;
62
+ }
63
+ if (tokens.reduced) {
64
+ state.y = 0;
65
+ state.vy = 0;
66
+ state.velocity = 0;
67
+ state.vv = 0;
68
+ state.bead = 0;
69
+ state.volume = unreadVolume(state.unread);
70
+ const fade = ms / Math.max(1, tokens.fast);
71
+ state.opacity = state.shown
72
+ ? Math.min(1, state.opacity + fade)
73
+ : Math.max(0, state.opacity - fade);
74
+ }
75
+ else {
76
+ const omega = (2 * Math.PI) / Math.max(0.08, tokens.springDuration / 1000);
77
+ const damping = Math.max(0.08, 1 - tokens.bounce);
78
+ const pull = Math.max(-9, Math.min(9, -state.velocity * 7));
79
+ const yTarget = merging ? 12 : state.shown ? pull : 4;
80
+ const steps = Math.ceil(ms / 8);
81
+ for (let n = 0; n < steps; n++) {
82
+ [state.y, state.vy] = spring(state.y, state.vy, yTarget, ms / steps / 1000, omega, damping);
83
+ [state.volume, state.vv] = spring(state.volume, state.vv, target, ms / steps / 1000, merging ? omega / 1.7 : omega, merging ? 1 : damping);
84
+ }
85
+ state.opacity = Math.min(1, Math.max(0, state.volume * 1.4));
86
+ state.bead = Math.max(0, state.bead - ms / 580);
87
+ }
88
+ if (!state.shown &&
89
+ (tokens.reduced
90
+ ? state.opacity === 0
91
+ : state.volume < 0.002 && Math.abs(state.vv) < 0.02))
92
+ return createDropState();
93
+ if (state.shown &&
94
+ Math.abs(state.volume - target) < 0.001 &&
95
+ Math.abs(state.vv) < 0.01 &&
96
+ Math.abs(state.y) < 0.02 &&
97
+ Math.abs(state.vy) < 0.2 &&
98
+ state.bead === 0) {
99
+ state.phase = "resting";
100
+ state.volume = target;
101
+ state.vv = 0;
102
+ state.y = 0;
103
+ state.vy = 0;
104
+ state.velocity = 0;
105
+ }
106
+ else if (state.shown && state.volume > 0.95)
107
+ state.phase = "tugged";
108
+ return state;
109
+ }
@@ -0,0 +1,16 @@
1
+ import { type RefObject } from "react";
2
+ import { type ScrollDirection } from "./scroll-to-bottom-blob-state.js";
3
+ /** Props accepted by the persistent jump-to-bottom water drop. */
4
+ export type ScrollToBottomBlobProps = {
5
+ /** Forward is ordinary DOM order; reverse supports column-reverse scrollers. */
6
+ direction?: ScrollDirection;
7
+ scrollerRef: RefObject<HTMLDivElement | null>;
8
+ /** Ask the host to scroll to its latest content. */
9
+ onJump: () => void;
10
+ /** Appended messages since the reader left the end. */
11
+ unread?: number;
12
+ /** Focus destination after keyboard activation; defaults to the scroller. */
13
+ focusAfterJump?: RefObject<HTMLElement | null>;
14
+ };
15
+ /** A continuously tugged water drop, with its own visibility and focus lifecycle. */
16
+ export declare const ScrollToBottomBlob: import("react").NamedExoticComponent<ScrollToBottomBlobProps>;