@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,80 @@
1
+ import { type ButtonHTMLAttributes, type HTMLAttributes, type ReactNode } from "react";
2
+ /**
3
+ * Molded key colors. `inverse` is the contrasting key; `destructive` stays neutral until
4
+ * hovered or focused; the rest follow the theme.
5
+ */
6
+ export type KeycapTone = "neutral" | "primary" | "accent" | "inverse" | "destructive";
7
+ /** Keycap sizes: a legend chip, a dock key and a big face key. */
8
+ export type KeycapSize = "sm" | "md" | "lg";
9
+ /** Props accepted by Keycap. */
10
+ export type KeycapProps = HTMLAttributes<HTMLElement> & {
11
+ tone?: KeycapTone;
12
+ size?: KeycapSize;
13
+ /** Show the key held down. Presentation only; the host owns input. */
14
+ pressed?: boolean;
15
+ };
16
+ /**
17
+ * One molded key legend, rendered as native `<kbd>`: the key a player presses.
18
+ * The key sits on its own travel so pressed and resting keys take the same room.
19
+ */
20
+ export declare const Keycap: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLElement> & {
21
+ tone?: KeycapTone;
22
+ size?: KeycapSize;
23
+ /** Show the key held down. Presentation only; the host owns input. */
24
+ pressed?: boolean;
25
+ } & import("react").RefAttributes<HTMLElement>>;
26
+ /** Props accepted by KeycapButton. */
27
+ export type KeycapButtonProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> & {
28
+ /**
29
+ * Visible name of the action, e.g. "Build". `null` makes an icon-only key,
30
+ * which then needs an `aria-label`.
31
+ */
32
+ label: ReactNode;
33
+ /** The keyboard key that runs the same action, shown as a small keycap. */
34
+ shortcut?: string;
35
+ /** Decorative icon before the label. */
36
+ icon?: ReactNode;
37
+ /** Extra readout after the label, such as a PixelNumber count. */
38
+ readout?: ReactNode;
39
+ tone?: KeycapTone;
40
+ /**
41
+ * Toggle state for mode keys. `undefined` is a momentary key; a boolean adds
42
+ * `aria-pressed` and lights the key's indicator.
43
+ */
44
+ pressed?: boolean;
45
+ /**
46
+ * Disclosure state for keys that open a palette or window. A boolean adds
47
+ * `aria-expanded` and lights the lamp; use it instead of `pressed` for
48
+ * menus.
49
+ */
50
+ expanded?: boolean;
51
+ };
52
+ /**
53
+ * A chunky action key for game docks and toy panels. The host owns the action
54
+ * and the keyboard binding; `shortcut` only labels it.
55
+ */
56
+ export declare const KeycapButton: import("react").ForwardRefExoticComponent<Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> & {
57
+ /**
58
+ * Visible name of the action, e.g. "Build". `null` makes an icon-only key,
59
+ * which then needs an `aria-label`.
60
+ */
61
+ label: ReactNode;
62
+ /** The keyboard key that runs the same action, shown as a small keycap. */
63
+ shortcut?: string;
64
+ /** Decorative icon before the label. */
65
+ icon?: ReactNode;
66
+ /** Extra readout after the label, such as a PixelNumber count. */
67
+ readout?: ReactNode;
68
+ tone?: KeycapTone;
69
+ /**
70
+ * Toggle state for mode keys. `undefined` is a momentary key; a boolean adds
71
+ * `aria-pressed` and lights the key's indicator.
72
+ */
73
+ pressed?: boolean;
74
+ /**
75
+ * Disclosure state for keys that open a palette or window. A boolean adds
76
+ * `aria-expanded` and lights the lamp; use it instead of `pressed` for
77
+ * menus.
78
+ */
79
+ expanded?: boolean;
80
+ } & import("react").RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,18 @@
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
+ /**
5
+ * One molded key legend, rendered as native `<kbd>`: the key a player presses.
6
+ * The key sits on its own travel so pressed and resting keys take the same room.
7
+ */
8
+ export const Keycap = forwardRef(function Keycap({ tone = "neutral", size = "md", pressed = false, className, ...props }, ref) {
9
+ return (_jsx("kbd", { ...props, ref: ref, className: cn("caps-keycap", className), "data-tone": tone, "data-size": size, "data-pressed": pressed }));
10
+ });
11
+ /**
12
+ * A chunky action key for game docks and toy panels. The host owns the action
13
+ * and the keyboard binding; `shortcut` only labels it.
14
+ */
15
+ export const KeycapButton = forwardRef(function KeycapButton({ label, shortcut, icon, readout, tone = "neutral", pressed, expanded, className, type = "button", ...props }, ref) {
16
+ const lamp = pressed ?? expanded;
17
+ return (_jsxs("button", { ...props, ref: ref, type: type, className: cn("caps-keycap-button", className), "data-tone": tone, "aria-pressed": pressed, "aria-expanded": expanded, "aria-keyshortcuts": props["aria-keyshortcuts"] ?? shortcut, children: [lamp === undefined ? null : (_jsx("span", { className: "caps-keycap-led", "data-lit": lamp, "aria-hidden": "true" })), icon ? (_jsx("span", { className: "caps-keycap-button-icon", "aria-hidden": "true", children: icon })) : null, label === null ? null : (_jsx("span", { className: "caps-keycap-button-label", children: label })), readout, shortcut ? (_jsx(Keycap, { size: "sm", tone: "neutral", "aria-hidden": "true", children: shortcut })) : null] }));
18
+ });
@@ -0,0 +1,13 @@
1
+ import { type LabelHTMLAttributes } from "react";
2
+ /** Props accepted by Label. */
3
+ export type LabelProps = {
4
+ /** Adds a quiet "Optional" readout after the text. */
5
+ optional?: boolean;
6
+ required?: boolean;
7
+ } & LabelHTMLAttributes<HTMLLabelElement>;
8
+ /** A label primitive that associates caller-provided text with a form control. */
9
+ export declare const Label: import("react").ForwardRefExoticComponent<{
10
+ /** Adds a quiet "Optional" readout after the text. */
11
+ optional?: boolean;
12
+ required?: boolean;
13
+ } & LabelHTMLAttributes<HTMLLabelElement> & import("react").RefAttributes<HTMLLabelElement>>;
@@ -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 label primitive that associates caller-provided text with a form control. */
5
+ export const Label = forwardRef(function Label({ children, className, optional, required, ...props }, ref) {
6
+ return (_jsxs("label", { ...props, className: cn("label caps-label cursor-pointer justify-start gap-sm text-sm font-semibold text-base-content", className), ref: ref, children: [children, required ? (_jsx("span", { "aria-hidden": "true", className: "text-error", children: "*" })) : null, optional && !required ? (_jsx("span", { className: "caps-label-optional", children: "Optional" })) : null] }));
7
+ });
@@ -0,0 +1,50 @@
1
+ import { type HTMLAttributes, type ReactNode } from "react";
2
+ /** Intrinsic focus and selection state supplied when rendering a Listbox item. */
3
+ export type ListboxItemState = {
4
+ active: boolean;
5
+ disabled: boolean;
6
+ selected: boolean;
7
+ };
8
+ /** Props accepted by Listbox. */
9
+ export type ListboxProps<T, Multiple extends boolean = false> = {
10
+ emptyMessage?: ReactNode;
11
+ /** Returns the stable key used for focus and selection. */
12
+ getKey: (item: T) => string;
13
+ /** Returns the accessible label used for an item. */
14
+ getLabel: (item: T) => string;
15
+ /** Determines whether an item can receive focus or selection. */
16
+ isDisabled?: (item: T) => boolean;
17
+ items: readonly T[];
18
+ /** Receives the selection control's root div. */
19
+ rootRef?: React.Ref<HTMLDivElement>;
20
+ /** Renders an item with its intrinsic focus and selection state. */
21
+ renderItem?: (item: T, active: boolean, state: ListboxItemState) => ReactNode;
22
+ } & Omit<HTMLAttributes<HTMLDivElement>, "defaultValue" | "onChange" | "onSelect" | "value"> & (Multiple extends true ? {
23
+ defaultValue?: ListboxValue<true>;
24
+ multiple: true;
25
+ /** Receives the next value and selected item after local interaction. */
26
+ onValueChange?: (value: ListboxValue<true>, item: T[]) => void;
27
+ value?: ListboxValue<true>;
28
+ } : Multiple extends false ? {
29
+ defaultValue?: ListboxValue;
30
+ multiple?: false;
31
+ /** Receives the next value and selected item after local interaction. */
32
+ onValueChange?: (value: ListboxValue, item: null | T) => void;
33
+ value?: ListboxValue;
34
+ } : {
35
+ defaultValue?: ListboxValue;
36
+ multiple?: false;
37
+ /** Receives the next value and selected item after local interaction. */
38
+ onValueChange?: (value: ListboxValue, item: null | T) => void;
39
+ value?: ListboxValue;
40
+ } | {
41
+ defaultValue?: ListboxValue<true>;
42
+ multiple: true;
43
+ /** Receives the next value and selected item after local interaction. */
44
+ onValueChange?: (value: ListboxValue<true>, item: T[]) => void;
45
+ value?: ListboxValue<true>;
46
+ });
47
+ /** Supported ListboxValue values. */
48
+ export type ListboxValue<Multiple extends boolean = false> = Multiple extends true ? string[] : null | string;
49
+ /** A selection primitive with caller-owned values and intrinsic keyboard navigation. */
50
+ export declare function Listbox<T>(input: ListboxProps<T, boolean>): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,134 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useId, useCallback, useImperativeHandle, useRef, useState, } from "react";
3
+ import { cn } from "../lib/cn.js";
4
+ import { useGlide } from "../lib/glide.js";
5
+ import { useSelectableCollection } from "./selectable.js";
6
+ /** A selection primitive with caller-owned values and intrinsic keyboard navigation. */
7
+ export function Listbox(input) {
8
+ const { className, defaultValue, emptyMessage = "No options", getKey, getLabel, id: providedId, isDisabled = () => false, items, multiple, onClick, onKeyDown, renderItem, rootRef, value: controlledValue, ...props } = input;
9
+ const generatedId = useId();
10
+ const id = providedId ?? generatedId;
11
+ const root = useRef(null);
12
+ useImperativeHandle(rootRef, () => root.current);
13
+ const glide = useGlide('[role="option"][data-active]');
14
+ const attach = useCallback((node) => {
15
+ root.current = node;
16
+ glide(node);
17
+ }, [glide]);
18
+ const isMultiple = multiple === true;
19
+ const initialValue = isMultiple ? [] : null;
20
+ const [uncontrolledValue, setUncontrolledValue] = useState(() => defaultValue ?? initialValue);
21
+ const selectedValue = optionalValueOr(controlledValue, uncontrolledValue);
22
+ const selectedKeys = selectedKeysFor(selectedValue, isMultiple);
23
+ const collection = useSelectableCollection({ getKey, isDisabled, items });
24
+ const { enabledItems } = collection;
25
+ const firstSelected = items.find((item) => selectedKeys.includes(getKey(item)));
26
+ const { activeKey, activeItem, setActiveKey } = collection;
27
+ const activeEnabled = activeItem;
28
+ const controlledSelection = controlledValue === undefined
29
+ ? undefined
30
+ : JSON.stringify(isMultiple ? [...new Set(selectedKeys)].sort() : selectedKeys);
31
+ const previousControlledSelection = useRef(controlledSelection);
32
+ const initialized = useRef(false);
33
+ useEffect(() => {
34
+ if (initialized.current || activeKey !== null)
35
+ return;
36
+ const initial = firstSelected ?? enabledItems[0];
37
+ if (initial) {
38
+ initialized.current = true;
39
+ setActiveKey(getKey(initial));
40
+ }
41
+ }, [activeKey, enabledItems, firstSelected, getKey, setActiveKey]);
42
+ useEffect(() => {
43
+ if (controlledSelection !== undefined &&
44
+ controlledSelection !== previousControlledSelection.current) {
45
+ const selectedItem = items.find((item) => selectedKeys.includes(getKey(item)) && !isDisabled(item));
46
+ setActiveKey(selectedItem ? getKey(selectedItem) : null);
47
+ }
48
+ previousControlledSelection.current = controlledSelection;
49
+ }, [controlledSelection, getKey, isDisabled, items, selectedKeys]);
50
+ const commit = (item) => {
51
+ if (!item || isDisabled(item))
52
+ return;
53
+ const key = getKey(item);
54
+ let nextKeys;
55
+ if (isMultiple) {
56
+ nextKeys = selectedKeys.includes(key)
57
+ ? selectedKeys.filter((selectedKey) => selectedKey !== key)
58
+ : [...selectedKeys, key];
59
+ }
60
+ else {
61
+ nextKeys = [key];
62
+ }
63
+ if (input.multiple) {
64
+ if (controlledValue === undefined)
65
+ setUncontrolledValue(nextKeys);
66
+ input.onValueChange?.(nextKeys, items.filter((candidate) => nextKeys.includes(getKey(candidate))));
67
+ }
68
+ else {
69
+ const nextValue = nextKeys[0] ?? null;
70
+ if (controlledValue === undefined)
71
+ setUncontrolledValue(nextValue);
72
+ input.onValueChange?.(nextValue, item);
73
+ }
74
+ };
75
+ const handleKeyDown = (event) => {
76
+ onKeyDown?.(event);
77
+ if (event.defaultPrevented)
78
+ return;
79
+ if (event.key === "Escape") {
80
+ return;
81
+ }
82
+ if (event.key === "Enter" || event.key === " ") {
83
+ event.preventDefault();
84
+ commit(activeEnabled);
85
+ return;
86
+ }
87
+ if (enabledItems.length === 0)
88
+ return;
89
+ if (event.key === "ArrowDown")
90
+ collection.move(1);
91
+ else if (event.key === "ArrowUp")
92
+ collection.move(-1);
93
+ else if (event.key === "Home")
94
+ collection.move(-1, "start");
95
+ else if (event.key === "End")
96
+ collection.move(1, "end");
97
+ else
98
+ return;
99
+ event.preventDefault();
100
+ };
101
+ return (_jsx("div", { ...props, "aria-activedescendant": activeEnabled
102
+ ? `${id}-option-${encodeURIComponent(getKey(activeEnabled))}`
103
+ : undefined, "aria-multiselectable": isMultiple ? true : undefined, className: cn("max-h-72 w-full overflow-auto rounded-box border border-base-300 bg-base-100 p-xs shadow-lg focus-visible:outline-none focus-visible:ring-2 caps-focus-color", className), id: id, onClick: onClick, onKeyDown: handleKeyDown, ref: attach, role: "listbox", tabIndex: props.tabIndex ?? 0, children: items.length === 0 ? (_jsx("div", { className: "px-md py-sm text-sm text-base-content", children: emptyMessage })) : (items.map((item) => {
104
+ const key = getKey(item);
105
+ const disabled = isDisabled(item);
106
+ const selected = selectedKeys.includes(key);
107
+ const active = activeEnabled ? getKey(activeEnabled) === key : false;
108
+ const state = { active, disabled, selected };
109
+ return (_jsx("div", { "aria-disabled": disabled || undefined, "aria-selected": selected, "data-active": (active && !disabled) || undefined, className: cn("cursor-pointer rounded-field px-md py-sm text-sm outline-none", active && "bg-base-200", selected && "font-medium", disabled &&
110
+ "cursor-not-allowed bg-base-200 text-base-content outline outline-1 outline-dashed outline-base-300"), id: `${id}-option-${encodeURIComponent(key)}`, onClick: (event) => {
111
+ if (disabled) {
112
+ event.preventDefault();
113
+ return;
114
+ }
115
+ setActiveKey(key);
116
+ commit(item);
117
+ }, role: "option", children: renderItem?.(item, active, state) ?? getLabel(item) }, key));
118
+ })) }));
119
+ }
120
+ function selectedKeysFor(value, multiple) {
121
+ if (multiple)
122
+ return Array.isArray(value) ? value.slice() : [];
123
+ if (value === null || value === undefined || Array.isArray(value))
124
+ return [];
125
+ return [value];
126
+ }
127
+ function optionalValueOr(value, fallback) {
128
+ switch (value) {
129
+ case undefined:
130
+ return fallback;
131
+ default:
132
+ return value;
133
+ }
134
+ }
@@ -0,0 +1,5 @@
1
+ import type { LoaderVariant } from "./loader.js";
2
+ /** @internal Animate only the silhouette; lighting never overlaps at joins. */
3
+ export declare function LoaderBlobs({ variant }: {
4
+ variant: LoaderVariant;
5
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,128 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMotion, motionTime } from "../lib/motion.js";
3
+ import { animate } from "animejs";
4
+ import { useId, useLayoutEffect, useRef } from "react";
5
+ import { GooeyFilterDefinition, GooeyShadowDefinition, GooeySurfaceDefinition, STATUS_GOOEY_FILTER, } from "./gooey.js";
6
+ import { observeMotionVisibility } from "./motion-visibility.js";
7
+ function smoothPhase(value) {
8
+ const bounded = Math.max(0, Math.min(1, value));
9
+ return bounded * bounded * (3 - 2 * bounded);
10
+ }
11
+ function excursion(blob, progress) {
12
+ const phase = Math.max(0, Math.min(1, (progress - (blob - 1) * 0.085) / 0.68));
13
+ return Math.sin(phase * Math.PI) ** 2;
14
+ }
15
+ // Closed harmonic paths feel irregular without picking new random targets on
16
+ // render. Opposing radii give the blobs a breathing, roughly constant area.
17
+ function pose(variant, blob, progress) {
18
+ const turn = progress * Math.PI * 2;
19
+ const phase = blob * 2.1;
20
+ const breath = Math.sin(turn * 2 + phase) * 0.15;
21
+ const radii = { rx: 4 * (1 + breath), ry: 4 / (1 + breath) };
22
+ if (variant === "line") {
23
+ const distance = 7.5 + 4 * Math.cos(turn + (blob === 2 ? -0.45 : 0));
24
+ return {
25
+ cx: (blob - 1) * distance,
26
+ cy: Math.sin(turn + phase) * 0.7,
27
+ ...radii,
28
+ };
29
+ }
30
+ if (variant === "square") {
31
+ const distance = 4.7 + 2.7 * Math.cos(turn);
32
+ return {
33
+ cx: (blob % 2 ? 1 : -1) * distance,
34
+ cy: (blob < 2 ? -1 : 1) * distance,
35
+ ...radii,
36
+ };
37
+ }
38
+ if (variant === "donut") {
39
+ // Trace clockwise from one origin. Unreleased blobs travel with the head;
40
+ // after the full ring closes, everything returns to that same origin.
41
+ const sweep = smoothPhase(progress / 0.66) * Math.PI * 2;
42
+ const angle = Math.min(sweep, (blob * Math.PI) / 8) - Math.PI / 2;
43
+ const collapse = smoothPhase((progress - 0.79) / 0.21);
44
+ const distance = 8 + Math.sin(turn * 2 + phase) * 0.35 * Math.sin(progress * Math.PI);
45
+ return {
46
+ cx: Math.cos(angle) * distance * (1 - collapse),
47
+ cy: Math.sin(angle) * distance * (1 - collapse) - 8 * collapse,
48
+ rx: 3.6 * (1 + breath * Math.sin(progress * Math.PI)),
49
+ ry: 3.6 / (1 + breath * Math.sin(progress * Math.PI)),
50
+ };
51
+ }
52
+ if (variant === "bloom") {
53
+ if (blob === 0) {
54
+ const released = [1, 2, 3, 4].reduce((sum, index) => sum + excursion(index, progress), 0);
55
+ const radius = Math.sqrt(49 - released * 4);
56
+ return {
57
+ cx: Math.sin(turn) * 0.4,
58
+ cy: Math.sin(turn * 2) * 0.4,
59
+ rx: radius * (1 + breath),
60
+ ry: radius / (1 + breath),
61
+ };
62
+ }
63
+ const reach = excursion(blob, progress);
64
+ const angle = ((blob - 1) * Math.PI) / 2 - Math.PI / 4 + Math.sin(turn) * 0.22;
65
+ return {
66
+ cx: Math.cos(angle) * 14 * reach,
67
+ cy: Math.sin(angle) * 14 * reach,
68
+ rx: 3.2 * (1 + breath),
69
+ ry: 3.2 / (1 + breath),
70
+ };
71
+ }
72
+ return {
73
+ cx: Math.sin(turn + phase) * 6 + Math.sin(turn * 3 + phase) * 2.5,
74
+ cy: Math.cos(turn * 2 + phase) * 6,
75
+ ...radii,
76
+ };
77
+ }
78
+ // Relative lengths preserve each choreography; the theme owns its tempo.
79
+ const durations = {
80
+ line: 1700,
81
+ square: 1900,
82
+ donut: 3000,
83
+ wander: 4600,
84
+ bloom: 2800,
85
+ };
86
+ /** @internal Animate only the silhouette; lighting never overlaps at joins. */
87
+ export function LoaderBlobs({ variant }) {
88
+ const id = `caps-loader-${useId().replaceAll(":", "")}`;
89
+ const geometry = useRef(null);
90
+ const visual = useRef(null);
91
+ const timing = useMotion(visual);
92
+ const { reduced } = timing;
93
+ const count = variant === "donut"
94
+ ? 16
95
+ : variant === "bloom"
96
+ ? 5
97
+ : variant === "square"
98
+ ? 4
99
+ : 3;
100
+ useLayoutEffect(() => {
101
+ if (reduced || !geometry.current || !visual.current)
102
+ return;
103
+ const loops = [...geometry.current.querySelectorAll("ellipse")].map((blob, index) => animate(blob, {
104
+ keyframes: Array.from({ length: 48 }, (_, step) => ({
105
+ ...pose(variant, index, (step + 1) / 48),
106
+ duration: motionTime(timing, durations[variant]) / 48,
107
+ })),
108
+ ease: "linear",
109
+ loop: true,
110
+ }));
111
+ const stopObserving = observeMotionVisibility(visual.current, (visible) => {
112
+ for (const loop of loops) {
113
+ if (visible)
114
+ loop.resume();
115
+ else
116
+ loop.pause();
117
+ }
118
+ });
119
+ return () => {
120
+ stopObserving();
121
+ for (const loop of loops)
122
+ loop.revert();
123
+ };
124
+ }, [variant, reduced, timing]);
125
+ return (_jsxs("svg", { ref: visual, "aria-hidden": "true", focusable: "false", viewBox: variant === "line" ? "-20 -10 40 20" : "-20 -20 40 40", children: [_jsxs("defs", { children: [_jsx(GooeyFilterDefinition, { id: id, values: STATUS_GOOEY_FILTER }), _jsx(GooeySurfaceDefinition, { id: `${id}-surface`, transform: variant === "line"
126
+ ? "matrix(3.4 0 0 1.4 0 0)"
127
+ : "matrix(3 0 0 3 0 0)" }), _jsx(GooeyShadowDefinition, { id: `${id}-shadow` }), _jsx("mask", { id: `${id}-mask`, maskUnits: "userSpaceOnUse", x: "-20", y: "-20", width: "40", height: "40", style: { maskType: "alpha" }, children: _jsx("g", { ref: geometry, fill: "currentColor", filter: `url(#${id})`, children: Array.from({ length: count }, (_, index) => (_jsx("ellipse", { ...pose(variant, index, 0) }, index))) }) })] }), _jsx("g", { filter: `url(#${id}-shadow)`, children: _jsx("rect", { x: "-20", y: "-20", width: "40", height: "40", fill: `url(#${id}-surface)`, mask: `url(#${id}-mask)` }) })] }));
128
+ }
@@ -0,0 +1,21 @@
1
+ import { type HTMLAttributes, type ReactNode } from "react";
2
+ /** Fluid motion formations, sharing one material. */
3
+ export type LoaderVariant = "line" | "wander" | "square" | "donut" | "bloom";
4
+ /** Props accepted by Loader. */
5
+ export type LoaderProps = {
6
+ decorative?: boolean;
7
+ label?: ReactNode;
8
+ /** `fill` grows the formation to its container, as a full-tile placeholder. */
9
+ size?: "sm" | "md" | "lg" | "fill";
10
+ variant?: LoaderVariant;
11
+ tone?: "neutral" | "accent" | "primary" | "success";
12
+ } & HTMLAttributes<HTMLSpanElement>;
13
+ /** Connecting gooey beads, with a static silhouette for reduced motion. */
14
+ export declare const Loader: import("react").ForwardRefExoticComponent<{
15
+ decorative?: boolean;
16
+ label?: ReactNode;
17
+ /** `fill` grows the formation to its container, as a full-tile placeholder. */
18
+ size?: "sm" | "md" | "lg" | "fill";
19
+ variant?: LoaderVariant;
20
+ tone?: "neutral" | "accent" | "primary" | "success";
21
+ } & HTMLAttributes<HTMLSpanElement> & import("react").RefAttributes<HTMLSpanElement>>;
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, useId } from "react";
3
+ import { cn } from "../lib/cn.js";
4
+ import { VisuallyHidden } from "./visually-hidden.js";
5
+ import { LoaderBlobs } from "./loader-blobs.js";
6
+ const sizeClasses = {
7
+ sm: "caps-loader-sm",
8
+ md: "caps-loader-md",
9
+ lg: "caps-loader-lg",
10
+ fill: "caps-loader-fill",
11
+ };
12
+ const toneClasses = {
13
+ neutral: "text-base-content",
14
+ accent: "text-accent",
15
+ primary: "text-primary",
16
+ success: "text-success",
17
+ };
18
+ /** Connecting gooey beads, with a static silhouette for reduced motion. */
19
+ export const Loader = forwardRef(function Loader({ "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, className, decorative = false, label = "Loading", size = "md", variant = "line", tone = "neutral", ...props }, ref) {
20
+ const labelId = `${useId()}-label`;
21
+ const ownsLabel = !decorative && ariaLabel === undefined && ariaLabelledby === undefined;
22
+ return (_jsxs("span", { ...props, "aria-label": decorative ? undefined : ariaLabel, "aria-hidden": decorative || undefined, "aria-labelledby": decorative
23
+ ? undefined
24
+ : (ariaLabelledby ?? (ariaLabel === undefined ? labelId : undefined)), className: cn("caps-loader", sizeClasses[size], toneClasses[tone], className), "data-variant": variant, role: decorative ? undefined : "status", ref: ref, children: [_jsx(LoaderBlobs, { variant: variant }), ownsLabel ? _jsx(VisuallyHidden, { id: labelId, children: label }) : null] }));
25
+ });
@@ -0,0 +1,23 @@
1
+ import type { MarkdownComponents } from "@tanstack/markdown/react";
2
+ import { type ReactNode } from "react";
3
+ /** Props accepted by Markdown. */
4
+ export type MarkdownProps = {
5
+ /** The Markdown source, including an unfinished streaming response. */
6
+ children: string;
7
+ /** Extra classes merged onto the rendered container. */
8
+ className?: string;
9
+ /** Element treatments; links use the shared policy, images are filtered first. */
10
+ components?: MarkdownComponents;
11
+ /** Strip a leading YAML frontmatter block before rendering. Defaults to true. */
12
+ frontmatter?: boolean;
13
+ /** Visitor content allows only absolute HTTP(S) links and never loads images. */
14
+ policy?: "default" | "visitor";
15
+ /** Use Caps Prose, or inherit typography from the containing surface. */
16
+ typography?: "prose" | "inherit";
17
+ };
18
+ /**
19
+ * Shared Markdown renderer with safe links, images and highlighted code.
20
+ * Documents use Caps Prose; containing surfaces can supply their own typography
21
+ * and element treatments. Replacing the source renders streaming partial Markdown.
22
+ */
23
+ export declare function Markdown({ children, className, components, frontmatter, policy, typography, }: MarkdownProps): ReactNode;
@@ -0,0 +1,70 @@
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Markdown as TanStackMarkdown } from "@tanstack/markdown/react";
3
+ import { parseMarkdown } from "@tanstack/markdown/parser";
4
+ import { createElement, useMemo } from "react";
5
+ import { cn } from "../lib/cn.js";
6
+ import { highlightMarkdownCode } from "../lib/tanstack-highlight.js";
7
+ import { Prose } from "./prose.js";
8
+ /** The protocol of an absolute URL, or `null` when the value is relative. */
9
+ function absoluteProtocol(value) {
10
+ return URL.parse(value)?.protocol ?? null;
11
+ }
12
+ /** Whether an href is safe to render live: http(s), mailto, or relative/hash. */
13
+ function isSafeHref(href) {
14
+ if (href === "" || href.startsWith("//"))
15
+ return false;
16
+ const protocol = absoluteProtocol(href);
17
+ return (protocol === null ||
18
+ protocol === "http:" ||
19
+ protocol === "https:" ||
20
+ protocol === "mailto:");
21
+ }
22
+ /** Whether an href leaves the current document and should open in a new tab. */
23
+ function isExternalHref(href) {
24
+ const protocol = absoluteProtocol(href);
25
+ return protocol === "http:" || protocol === "https:";
26
+ }
27
+ /** Whether an image src is safe to render: http(s) or relative. */
28
+ function isSafeImageSrc(src) {
29
+ if (src === "" || src.startsWith("//"))
30
+ return false;
31
+ const protocol = absoluteProtocol(src);
32
+ return protocol === null || protocol === "http:" || protocol === "https:";
33
+ }
34
+ /**
35
+ * Shared Markdown renderer with safe links, images and highlighted code.
36
+ * Documents use Caps Prose; containing surfaces can supply their own typography
37
+ * and element treatments. Replacing the source renders streaming partial Markdown.
38
+ */
39
+ export function Markdown({ children, className, components, frontmatter = true, policy = "default", typography = "prose", }) {
40
+ const document = useMemo(() => parseMarkdown(children, { allowHtml: false, frontmatter }), [children, frontmatter]);
41
+ const safeComponents = useMemo(() => ({
42
+ ...components,
43
+ a(props) {
44
+ const href = props.href ?? "";
45
+ if (policy === "visitor") {
46
+ const destination = URL.parse(href);
47
+ if (destination === null || !isExternalHref(destination.href))
48
+ return _jsx("span", { children: props.children });
49
+ return (_jsx("a", { href: destination.href, target: "_blank", rel: "noopener noreferrer", children: props.children }));
50
+ }
51
+ if (!isSafeHref(href))
52
+ return _jsx(_Fragment, { children: props.children });
53
+ const external = isExternalHref(href);
54
+ return (_jsx("a", { ...props, href: href, rel: external ? "noopener noreferrer" : props.rel, target: external ? "_blank" : props.target }));
55
+ },
56
+ img(props) {
57
+ if (policy === "visitor")
58
+ return _jsx("span", { className: "text-dim", children: "Image removed" });
59
+ const source = props.src ?? "";
60
+ if (!isSafeImageSrc(source))
61
+ return _jsx(_Fragment, { children: props.alt ?? "" });
62
+ if (components?.img)
63
+ return createElement(components.img, props);
64
+ return (_jsx("img", { alt: props.alt ?? "", className: "max-w-full", src: source }));
65
+ },
66
+ }), [components, policy]);
67
+ const content = (_jsx(TanStackMarkdown, { allowHtml: false, components: safeComponents, highlighter: highlightMarkdownCode, children: document }));
68
+ const containerClass = cn("min-w-0 break-words", className);
69
+ return typography === "prose" ? (_jsx(Prose, { className: containerClass, children: content })) : (_jsx("div", { className: containerClass, children: content }));
70
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Minimap label placement: every named mark tries its label below, above,
3
+ * right and left of its dot, then at its four corners, and keeps the first spot that stays inside
4
+ * the chart and clear of every fixed dot and every label already placed. Goals
5
+ * are placed first, then places in the order the game listed them, so a
6
+ * game lists its landmarks first. A label with no clear spot is hidden; its
7
+ * full name still shows on hover and focus, and reads to assistive tech.
8
+ * Pure and deterministic: the same marks and size give the same layout.
9
+ */
10
+ /** Where a mark's name sits, or `hidden`. */
11
+ export type MinimapLabelSide = "below" | "above" | "right" | "left" | "below-right" | "below-left" | "above-right" | "above-left" | "hidden";
12
+ /** A caller-provided minimap label and its anchor. */
13
+ export type MinimapLabelInput = Readonly<{
14
+ /** Mark centre, as a fraction of the chart's width and height. */
15
+ x: number;
16
+ y: number;
17
+ /** Named marks are laid out; unnamed ones (the viewer, foes) are dots. */
18
+ named: boolean;
19
+ /** A dot labels must keep clear of. Moving marks (foes) are left out, so a
20
+ * passing crowd never hides a landmark. */
21
+ solid: boolean;
22
+ goal: boolean;
23
+ label: string;
24
+ }>;
25
+ /** Measured label dimensions used to place minimap labels. */
26
+ export type MinimapLabelMetrics = Readonly<{
27
+ /** The chart's size in CSS pixels. */
28
+ width: number;
29
+ height: number;
30
+ /** An estimate of one character's advance, and a label's line height. */
31
+ charWidth: number;
32
+ lineHeight: number;
33
+ /** Padding either side of a label, the dot's size, the gap to the dot. */
34
+ padding: number;
35
+ dot: number;
36
+ gap: number;
37
+ }>;
38
+ /** The side each mark's label takes, index for index with `marks`. */
39
+ export declare function layoutMinimapLabels(marks: readonly MinimapLabelInput[], metrics: MinimapLabelMetrics): MinimapLabelSide[];