makersclaw 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (478) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +41 -1
  3. package/THIRD-PARTY-NOTICES.md +180 -0
  4. package/package.json +15 -8
  5. package/r/annotate.json +62 -0
  6. package/r/approval-request.json +52 -0
  7. package/r/audio-player.json +74 -0
  8. package/r/auth-form.json +55 -0
  9. package/r/board.json +55 -0
  10. package/r/calendar-view.json +105 -0
  11. package/r/cart.json +62 -0
  12. package/r/chart.json +83 -0
  13. package/r/chat.json +95 -0
  14. package/r/code-editor.json +94 -0
  15. package/r/comment-box.json +64 -0
  16. package/r/contact-section.json +53 -0
  17. package/r/core.json +341 -0
  18. package/r/csv-import.json +63 -0
  19. package/r/data-table.json +69 -0
  20. package/r/diagram.json +74 -0
  21. package/r/doc-editor.json +82 -0
  22. package/r/doc-page.json +61 -0
  23. package/r/email-render.json +73 -0
  24. package/r/faq.json +50 -0
  25. package/r/file-upload.json +54 -0
  26. package/r/flow-canvas.json +93 -0
  27. package/r/footer.json +52 -0
  28. package/r/gantt.json +70 -0
  29. package/r/job-panel.json +56 -0
  30. package/r/json-editor.json +66 -0
  31. package/r/map.json +89 -0
  32. package/r/menubar.json +52 -0
  33. package/r/mind-map.json +67 -0
  34. package/r/navigation-menu.json +52 -0
  35. package/r/order-detail.json +54 -0
  36. package/r/org-chart.json +66 -0
  37. package/r/page-builder.json +105 -0
  38. package/r/payment-methods.json +51 -0
  39. package/r/pdf-document.json +79 -0
  40. package/r/pdf-viewer.json +88 -0
  41. package/r/product-detail.json +76 -0
  42. package/r/product-grid.json +65 -0
  43. package/r/record-form.json +67 -0
  44. package/r/record-notes.json +55 -0
  45. package/r/registry.json +3612 -0
  46. package/r/search-dialog.json +51 -0
  47. package/r/settings-list.json +66 -0
  48. package/r/setup-checklist.json +51 -0
  49. package/r/share-dialog.json +52 -0
  50. package/r/sidebar.json +69 -0
  51. package/r/signature-pad.json +72 -0
  52. package/r/sketch-canvas.json +99 -0
  53. package/r/spreadsheet.json +90 -0
  54. package/r/stat-cards.json +66 -0
  55. package/r/task-list.json +63 -0
  56. package/r/task-model.json +98 -0
  57. package/r/testimonials.json +52 -0
  58. package/r/transaction-list.json +53 -0
  59. package/r/whiteboard.json +66 -0
  60. package/registry/blocks/annotate/README.md +179 -0
  61. package/registry/blocks/annotate/annotate-data.js +250 -0
  62. package/registry/blocks/annotate/annotate.jsx +646 -0
  63. package/registry/blocks/annotate/block.json +10 -0
  64. package/registry/blocks/annotate/demo.jsx +135 -0
  65. package/registry/blocks/annotate/index.js +15 -0
  66. package/registry/blocks/approval-request/README.md +116 -0
  67. package/registry/blocks/approval-request/approval-request.jsx +386 -0
  68. package/registry/blocks/approval-request/block.json +10 -0
  69. package/registry/blocks/approval-request/demo.jsx +127 -0
  70. package/registry/blocks/approval-request/index.js +4 -0
  71. package/registry/blocks/audio-player/README.md +167 -0
  72. package/registry/blocks/audio-player/audio-player.jsx +623 -0
  73. package/registry/blocks/audio-player/block.json +14 -0
  74. package/registry/blocks/audio-player/colors.js +109 -0
  75. package/registry/blocks/audio-player/demo.jsx +209 -0
  76. package/registry/blocks/audio-player/index.js +6 -0
  77. package/registry/blocks/audio-player/time.js +67 -0
  78. package/registry/blocks/audio-player/wavesurfer.js +9 -0
  79. package/registry/blocks/auth-form/README.md +126 -0
  80. package/registry/blocks/auth-form/auth-form.jsx +568 -0
  81. package/registry/blocks/auth-form/block.json +10 -0
  82. package/registry/blocks/auth-form/demo.jsx +131 -0
  83. package/registry/blocks/auth-form/index.js +3 -0
  84. package/registry/blocks/board/README.md +64 -13
  85. package/registry/blocks/board/block.json +2 -2
  86. package/registry/blocks/board/board.jsx +237 -94
  87. package/registry/blocks/board/demo.jsx +103 -24
  88. package/registry/blocks/calendar-view/README.md +180 -0
  89. package/registry/blocks/calendar-view/agenda.jsx +147 -0
  90. package/registry/blocks/calendar-view/block.json +76 -0
  91. package/registry/blocks/calendar-view/calendar-model.js +442 -0
  92. package/registry/blocks/calendar-view/calendar.jsx +627 -0
  93. package/registry/blocks/calendar-view/demo.jsx +195 -0
  94. package/registry/blocks/calendar-view/draft.jsx +49 -0
  95. package/registry/blocks/calendar-view/index.js +39 -0
  96. package/registry/blocks/calendar-view/month.jsx +346 -0
  97. package/registry/blocks/calendar-view/paint.js +69 -0
  98. package/registry/blocks/calendar-view/time-grid.jsx +527 -0
  99. package/registry/blocks/calendar-view/tray.jsx +87 -0
  100. package/registry/blocks/cart/README.md +146 -0
  101. package/registry/blocks/cart/block.json +10 -0
  102. package/registry/blocks/cart/cart.jsx +597 -0
  103. package/registry/blocks/cart/demo.jsx +98 -0
  104. package/registry/blocks/cart/index.js +4 -0
  105. package/registry/blocks/cart/money.js +28 -0
  106. package/registry/blocks/chart/README.md +316 -0
  107. package/registry/blocks/chart/block.json +10 -0
  108. package/registry/blocks/chart/cartesian.jsx +642 -0
  109. package/registry/blocks/chart/core.jsx +262 -0
  110. package/registry/blocks/chart/demo.jsx +357 -0
  111. package/registry/blocks/chart/heatmap.jsx +336 -0
  112. package/registry/blocks/chart/index.js +8 -0
  113. package/registry/blocks/chart/pie.jsx +245 -0
  114. package/registry/blocks/chart/sparkline.jsx +88 -0
  115. package/registry/blocks/chat/README.md +273 -0
  116. package/registry/blocks/chat/attachment.jsx +95 -0
  117. package/registry/blocks/chat/block.json +10 -0
  118. package/registry/blocks/chat/chat.jsx +132 -0
  119. package/registry/blocks/chat/composer.jsx +364 -0
  120. package/registry/blocks/chat/demo.jsx +578 -0
  121. package/registry/blocks/chat/header.jsx +34 -0
  122. package/registry/blocks/chat/index.js +9 -0
  123. package/registry/blocks/chat/message.jsx +450 -0
  124. package/registry/blocks/chat/scroller.jsx +247 -0
  125. package/registry/blocks/chat/text.jsx +104 -0
  126. package/registry/blocks/code-editor/README.md +210 -0
  127. package/registry/blocks/code-editor/block.json +22 -0
  128. package/registry/blocks/code-editor/code-editor.jsx +534 -0
  129. package/registry/blocks/code-editor/codemirror.js +38 -0
  130. package/registry/blocks/code-editor/demo.jsx +205 -0
  131. package/registry/blocks/code-editor/index.js +8 -0
  132. package/registry/blocks/code-editor/json.js +317 -0
  133. package/registry/blocks/code-editor/sql.js +136 -0
  134. package/registry/blocks/code-editor/text.js +101 -0
  135. package/registry/blocks/code-editor/theme.js +186 -0
  136. package/registry/blocks/comment-box/README.md +123 -0
  137. package/registry/blocks/comment-box/block.json +10 -0
  138. package/registry/blocks/comment-box/comment-body.jsx +217 -0
  139. package/registry/blocks/comment-box/comment-box.jsx +198 -0
  140. package/registry/blocks/comment-box/comment-time.jsx +77 -0
  141. package/registry/blocks/comment-box/demo.jsx +141 -0
  142. package/registry/blocks/comment-box/index.js +8 -0
  143. package/registry/blocks/contact-section/README.md +152 -0
  144. package/registry/blocks/contact-section/block.json +11 -0
  145. package/registry/blocks/contact-section/contact-section.jsx +509 -0
  146. package/registry/blocks/contact-section/demo.jsx +93 -0
  147. package/registry/blocks/contact-section/index.js +4 -0
  148. package/registry/blocks/csv-import/README.md +133 -0
  149. package/registry/blocks/csv-import/block.json +13 -0
  150. package/registry/blocks/csv-import/csv-import.jsx +479 -0
  151. package/registry/blocks/csv-import/demo.jsx +106 -0
  152. package/registry/blocks/csv-import/import.js +200 -0
  153. package/registry/blocks/csv-import/index.js +7 -0
  154. package/registry/blocks/data-table/README.md +386 -0
  155. package/registry/blocks/data-table/block.json +10 -0
  156. package/registry/blocks/data-table/data-table.jsx +1164 -0
  157. package/registry/blocks/data-table/demo.jsx +588 -0
  158. package/registry/blocks/data-table/index.js +5 -0
  159. package/registry/blocks/data-table/model.js +169 -0
  160. package/registry/blocks/diagram/README.md +108 -0
  161. package/registry/blocks/diagram/block.json +13 -0
  162. package/registry/blocks/diagram/demo.jsx +113 -0
  163. package/registry/blocks/diagram/diagram.jsx +481 -0
  164. package/registry/blocks/diagram/index.js +7 -0
  165. package/registry/blocks/diagram/mermaid.js +52 -0
  166. package/registry/blocks/diagram/palette.js +313 -0
  167. package/registry/blocks/diagram/source.js +98 -0
  168. package/registry/blocks/doc-editor/README.md +215 -0
  169. package/registry/blocks/doc-editor/block.json +15 -0
  170. package/registry/blocks/doc-editor/demo.jsx +225 -0
  171. package/registry/blocks/doc-editor/doc-editor.jsx +291 -0
  172. package/registry/blocks/doc-editor/doc.js +309 -0
  173. package/registry/blocks/doc-editor/index.js +7 -0
  174. package/registry/blocks/doc-editor/schema.jsx +56 -0
  175. package/registry/blocks/doc-editor/shadcn-classes.js +110 -0
  176. package/registry/blocks/doc-editor/theme.js +82 -0
  177. package/registry/blocks/doc-page/README.md +119 -0
  178. package/registry/blocks/doc-page/autosave.js +104 -0
  179. package/registry/blocks/doc-page/block.json +10 -0
  180. package/registry/blocks/doc-page/demo.jsx +137 -0
  181. package/registry/blocks/doc-page/doc-page.jsx +276 -0
  182. package/registry/blocks/doc-page/index.js +4 -0
  183. package/registry/blocks/email-render/README.md +151 -0
  184. package/registry/blocks/email-render/block.json +14 -0
  185. package/registry/blocks/email-render/demo.jsx +153 -0
  186. package/registry/blocks/email-render/doc-email.jsx +210 -0
  187. package/registry/blocks/email-render/email.jsx +221 -0
  188. package/registry/blocks/email-render/index.js +10 -0
  189. package/registry/blocks/email-render/render.js +50 -0
  190. package/registry/blocks/email-render/theme.js +215 -0
  191. package/registry/blocks/faq/README.md +101 -0
  192. package/registry/blocks/faq/block.json +11 -0
  193. package/registry/blocks/faq/demo.jsx +89 -0
  194. package/registry/blocks/faq/faq.jsx +220 -0
  195. package/registry/blocks/faq/index.js +4 -0
  196. package/registry/blocks/file-upload/README.md +138 -0
  197. package/registry/blocks/file-upload/block.json +10 -0
  198. package/registry/blocks/file-upload/demo.jsx +204 -0
  199. package/registry/blocks/file-upload/file-upload.jsx +635 -0
  200. package/registry/blocks/file-upload/index.js +4 -0
  201. package/registry/blocks/flow-canvas/README.md +420 -0
  202. package/registry/blocks/flow-canvas/block.json +13 -0
  203. package/registry/blocks/flow-canvas/board.js +819 -0
  204. package/registry/blocks/flow-canvas/chrome.jsx +649 -0
  205. package/registry/blocks/flow-canvas/context.jsx +35 -0
  206. package/registry/blocks/flow-canvas/demo.jsx +249 -0
  207. package/registry/blocks/flow-canvas/edges.jsx +107 -0
  208. package/registry/blocks/flow-canvas/flow-canvas.jsx +1329 -0
  209. package/registry/blocks/flow-canvas/index.js +41 -0
  210. package/registry/blocks/flow-canvas/nodes.jsx +392 -0
  211. package/registry/blocks/flow-canvas/theme.js +174 -0
  212. package/registry/blocks/footer/README.md +133 -0
  213. package/registry/blocks/footer/block.json +11 -0
  214. package/registry/blocks/footer/demo.jsx +119 -0
  215. package/registry/blocks/footer/footer.jsx +406 -0
  216. package/registry/blocks/footer/index.js +4 -0
  217. package/registry/blocks/gantt/README.md +220 -0
  218. package/registry/blocks/gantt/block.json +10 -0
  219. package/registry/blocks/gantt/demo.jsx +148 -0
  220. package/registry/blocks/gantt/gantt.jsx +923 -0
  221. package/registry/blocks/gantt/index.js +7 -0
  222. package/registry/blocks/gantt/rows.jsx +468 -0
  223. package/registry/blocks/gantt/schedule.js +278 -0
  224. package/registry/blocks/job-panel/README.md +86 -2
  225. package/registry/blocks/job-panel/block.json +3 -3
  226. package/registry/blocks/job-panel/demo.jsx +230 -3
  227. package/registry/blocks/job-panel/job-panel.jsx +169 -3
  228. package/registry/blocks/json-editor/README.md +133 -0
  229. package/registry/blocks/json-editor/block.json +10 -0
  230. package/registry/blocks/json-editor/demo.jsx +92 -0
  231. package/registry/blocks/json-editor/index.js +7 -0
  232. package/registry/blocks/json-editor/json-editor.jsx +305 -0
  233. package/registry/blocks/json-editor/json-tree.jsx +393 -0
  234. package/registry/blocks/json-editor/tree.js +225 -0
  235. package/registry/blocks/map/README.md +221 -0
  236. package/registry/blocks/map/block.json +14 -0
  237. package/registry/blocks/map/demo.jsx +201 -0
  238. package/registry/blocks/map/geo.js +198 -0
  239. package/registry/blocks/map/index.js +8 -0
  240. package/registry/blocks/map/list.jsx +128 -0
  241. package/registry/blocks/map/map-view.jsx +715 -0
  242. package/registry/blocks/map/maplibre.js +30 -0
  243. package/registry/blocks/map/marks.jsx +146 -0
  244. package/registry/blocks/map/theme.js +142 -0
  245. package/registry/blocks/menubar/README.md +109 -0
  246. package/registry/blocks/menubar/block.json +10 -0
  247. package/registry/blocks/menubar/demo.jsx +128 -0
  248. package/registry/blocks/menubar/index.js +4 -0
  249. package/registry/blocks/menubar/menubar.jsx +199 -0
  250. package/registry/blocks/mind-map/README.md +206 -0
  251. package/registry/blocks/mind-map/block.json +10 -0
  252. package/registry/blocks/mind-map/demo.jsx +103 -0
  253. package/registry/blocks/mind-map/index.js +31 -0
  254. package/registry/blocks/mind-map/mind-map.jsx +1125 -0
  255. package/registry/blocks/mind-map/topic.jsx +143 -0
  256. package/registry/blocks/mind-map/tree.js +645 -0
  257. package/registry/blocks/navigation-menu/README.md +124 -0
  258. package/registry/blocks/navigation-menu/block.json +10 -0
  259. package/registry/blocks/navigation-menu/demo.jsx +84 -0
  260. package/registry/blocks/navigation-menu/index.js +4 -0
  261. package/registry/blocks/navigation-menu/navigation-menu.jsx +448 -0
  262. package/registry/blocks/order-detail/README.md +109 -0
  263. package/registry/blocks/order-detail/block.json +10 -0
  264. package/registry/blocks/order-detail/demo.jsx +88 -0
  265. package/registry/blocks/order-detail/index.js +4 -0
  266. package/registry/blocks/order-detail/order-detail.jsx +354 -0
  267. package/registry/blocks/org-chart/README.md +145 -0
  268. package/registry/blocks/org-chart/block.json +10 -0
  269. package/registry/blocks/org-chart/demo.jsx +94 -0
  270. package/registry/blocks/org-chart/index.js +7 -0
  271. package/registry/blocks/org-chart/org-chart.jsx +631 -0
  272. package/registry/blocks/org-chart/org.js +245 -0
  273. package/registry/blocks/org-chart/person.jsx +96 -0
  274. package/registry/blocks/page-builder/README.md +257 -0
  275. package/registry/blocks/page-builder/block.json +14 -0
  276. package/registry/blocks/page-builder/demo.jsx +145 -0
  277. package/registry/blocks/page-builder/grapes.js +18 -0
  278. package/registry/blocks/page-builder/index.js +11 -0
  279. package/registry/blocks/page-builder/page-builder.jsx +704 -0
  280. package/registry/blocks/page-builder/page-view.jsx +33 -0
  281. package/registry/blocks/page-builder/page.js +118 -0
  282. package/registry/blocks/page-builder/panels.jsx +588 -0
  283. package/registry/blocks/page-builder/sections.js +209 -0
  284. package/registry/blocks/page-builder/theme.js +87 -0
  285. package/registry/blocks/page-builder/thumbs.jsx +189 -0
  286. package/registry/blocks/page-builder/tokens.js +329 -0
  287. package/registry/blocks/payment-methods/README.md +104 -0
  288. package/registry/blocks/payment-methods/block.json +10 -0
  289. package/registry/blocks/payment-methods/demo.jsx +71 -0
  290. package/registry/blocks/payment-methods/index.js +4 -0
  291. package/registry/blocks/payment-methods/payment-methods.jsx +302 -0
  292. package/registry/blocks/pdf-document/README.md +213 -0
  293. package/registry/blocks/pdf-document/block.json +13 -0
  294. package/registry/blocks/pdf-document/demo.jsx +136 -0
  295. package/registry/blocks/pdf-document/index.js +33 -0
  296. package/registry/blocks/pdf-document/money.js +83 -0
  297. package/registry/blocks/pdf-document/pdf-document.jsx +144 -0
  298. package/registry/blocks/pdf-document/primitives.jsx +271 -0
  299. package/registry/blocks/pdf-document/quote.jsx +179 -0
  300. package/registry/blocks/pdf-document/theme.js +193 -0
  301. package/registry/blocks/pdf-viewer/README.md +191 -0
  302. package/registry/blocks/pdf-viewer/block.json +14 -0
  303. package/registry/blocks/pdf-viewer/demo.jsx +252 -0
  304. package/registry/blocks/pdf-viewer/helpers.js +270 -0
  305. package/registry/blocks/pdf-viewer/index.js +8 -0
  306. package/registry/blocks/pdf-viewer/notes.jsx +195 -0
  307. package/registry/blocks/pdf-viewer/pdf-viewer.jsx +1368 -0
  308. package/registry/blocks/pdf-viewer/pdfjs.js +37 -0
  309. package/registry/blocks/pdf-viewer/ssr-shim-end.js +6 -0
  310. package/registry/blocks/pdf-viewer/ssr-shim.js +44 -0
  311. package/registry/blocks/product-detail/README.md +217 -0
  312. package/registry/blocks/product-detail/block.json +10 -0
  313. package/registry/blocks/product-detail/demo.jsx +233 -0
  314. package/registry/blocks/product-detail/index.js +7 -0
  315. package/registry/blocks/product-detail/price.jsx +115 -0
  316. package/registry/blocks/product-detail/product-detail.jsx +348 -0
  317. package/registry/blocks/product-detail/product-media.jsx +102 -0
  318. package/registry/blocks/product-detail/product-options.jsx +144 -0
  319. package/registry/blocks/product-grid/README.md +162 -0
  320. package/registry/blocks/product-grid/block.json +10 -0
  321. package/registry/blocks/product-grid/demo.jsx +255 -0
  322. package/registry/blocks/product-grid/index.js +5 -0
  323. package/registry/blocks/product-grid/product-card.jsx +232 -0
  324. package/registry/blocks/product-grid/product-grid.jsx +235 -0
  325. package/registry/blocks/record-form/README.md +196 -0
  326. package/registry/blocks/record-form/block.json +10 -0
  327. package/registry/blocks/record-form/demo.jsx +525 -0
  328. package/registry/blocks/record-form/index.js +4 -0
  329. package/registry/blocks/record-form/record-fields.jsx +457 -0
  330. package/registry/blocks/record-form/record-form.jsx +415 -0
  331. package/registry/blocks/record-form/rules.js +81 -0
  332. package/registry/blocks/record-notes/README.md +139 -0
  333. package/registry/blocks/record-notes/block.json +10 -0
  334. package/registry/blocks/record-notes/demo.jsx +179 -0
  335. package/registry/blocks/record-notes/index.js +4 -0
  336. package/registry/blocks/record-notes/record-notes.jsx +266 -0
  337. package/registry/blocks/search-dialog/README.md +108 -0
  338. package/registry/blocks/search-dialog/block.json +10 -0
  339. package/registry/blocks/search-dialog/demo.jsx +116 -0
  340. package/registry/blocks/search-dialog/index.js +3 -0
  341. package/registry/blocks/search-dialog/search-dialog.jsx +235 -0
  342. package/registry/blocks/settings-list/README.md +171 -0
  343. package/registry/blocks/settings-list/block.json +10 -0
  344. package/registry/blocks/settings-list/demo.jsx +333 -0
  345. package/registry/blocks/settings-list/index.js +4 -0
  346. package/registry/blocks/settings-list/row-state.jsx +93 -0
  347. package/registry/blocks/settings-list/settings-list.jsx +349 -0
  348. package/registry/blocks/settings-list/settings-matrix.jsx +118 -0
  349. package/registry/blocks/setup-checklist/README.md +142 -0
  350. package/registry/blocks/setup-checklist/block.json +10 -0
  351. package/registry/blocks/setup-checklist/demo.jsx +159 -0
  352. package/registry/blocks/setup-checklist/index.js +4 -0
  353. package/registry/blocks/setup-checklist/setup-checklist.jsx +446 -0
  354. package/registry/blocks/share-dialog/README.md +113 -0
  355. package/registry/blocks/share-dialog/block.json +10 -0
  356. package/registry/blocks/share-dialog/demo.jsx +140 -0
  357. package/registry/blocks/share-dialog/index.js +3 -0
  358. package/registry/blocks/share-dialog/share-dialog.jsx +384 -0
  359. package/registry/blocks/sidebar/README.md +190 -0
  360. package/registry/blocks/sidebar/block.json +43 -0
  361. package/registry/blocks/sidebar/demo.jsx +577 -0
  362. package/registry/blocks/sidebar/index.js +4 -0
  363. package/registry/blocks/sidebar/sidebar-panel.jsx +149 -0
  364. package/registry/blocks/sidebar/sidebar.jsx +864 -0
  365. package/registry/blocks/signature-pad/README.md +137 -0
  366. package/registry/blocks/signature-pad/block.json +13 -0
  367. package/registry/blocks/signature-pad/colors.js +115 -0
  368. package/registry/blocks/signature-pad/demo.jsx +94 -0
  369. package/registry/blocks/signature-pad/index.js +6 -0
  370. package/registry/blocks/signature-pad/pad.js +7 -0
  371. package/registry/blocks/signature-pad/signature-pad.jsx +434 -0
  372. package/registry/blocks/signature-pad/signature.js +68 -0
  373. package/registry/blocks/sketch-canvas/README.md +280 -0
  374. package/registry/blocks/sketch-canvas/block.json +13 -0
  375. package/registry/blocks/sketch-canvas/demo.jsx +159 -0
  376. package/registry/blocks/sketch-canvas/excalidraw.js +40 -0
  377. package/registry/blocks/sketch-canvas/index.js +9 -0
  378. package/registry/blocks/sketch-canvas/palette.js +226 -0
  379. package/registry/blocks/sketch-canvas/sketch-canvas.jsx +1188 -0
  380. package/registry/blocks/sketch-canvas/sketch.js +179 -0
  381. package/registry/blocks/sketch-canvas/ssr-shim-end.js +6 -0
  382. package/registry/blocks/sketch-canvas/ssr-shim.js +45 -0
  383. package/registry/blocks/sketch-canvas/stamps.js +104 -0
  384. package/registry/blocks/sketch-canvas/theme.js +131 -0
  385. package/registry/blocks/spreadsheet/README.md +307 -0
  386. package/registry/blocks/spreadsheet/block.json +13 -0
  387. package/registry/blocks/spreadsheet/cells.jsx +455 -0
  388. package/registry/blocks/spreadsheet/chrome.jsx +325 -0
  389. package/registry/blocks/spreadsheet/demo.jsx +177 -0
  390. package/registry/blocks/spreadsheet/grid.js +387 -0
  391. package/registry/blocks/spreadsheet/index.js +34 -0
  392. package/registry/blocks/spreadsheet/spreadsheet.jsx +1732 -0
  393. package/registry/blocks/spreadsheet/types.js +473 -0
  394. package/registry/blocks/stat-cards/README.md +173 -0
  395. package/registry/blocks/stat-cards/block.json +10 -0
  396. package/registry/blocks/stat-cards/demo.jsx +257 -0
  397. package/registry/blocks/stat-cards/index.js +4 -0
  398. package/registry/blocks/stat-cards/stat-cards.jsx +307 -0
  399. package/registry/blocks/stat-cards/usage.jsx +243 -0
  400. package/registry/blocks/task-list/README.md +176 -0
  401. package/registry/blocks/task-list/block.json +10 -0
  402. package/registry/blocks/task-list/demo.jsx +134 -0
  403. package/registry/blocks/task-list/index.js +5 -0
  404. package/registry/blocks/task-list/task-list.jsx +907 -0
  405. package/registry/blocks/task-model/README.md +455 -0
  406. package/registry/blocks/task-model/block.json +167 -0
  407. package/registry/blocks/task-model/changes.js +89 -0
  408. package/registry/blocks/task-model/dates.js +371 -0
  409. package/registry/blocks/task-model/demo.jsx +215 -0
  410. package/registry/blocks/task-model/dnd.jsx +240 -0
  411. package/registry/blocks/task-model/index.js +97 -0
  412. package/registry/blocks/task-model/model.js +919 -0
  413. package/registry/blocks/task-model/parts.jsx +449 -0
  414. package/registry/blocks/task-model/toolbar.jsx +271 -0
  415. package/registry/blocks/testimonials/README.md +139 -0
  416. package/registry/blocks/testimonials/block.json +11 -0
  417. package/registry/blocks/testimonials/demo.jsx +137 -0
  418. package/registry/blocks/testimonials/index.js +4 -0
  419. package/registry/blocks/testimonials/testimonials.jsx +430 -0
  420. package/registry/blocks/transaction-list/README.md +126 -0
  421. package/registry/blocks/transaction-list/block.json +10 -0
  422. package/registry/blocks/transaction-list/demo.jsx +79 -0
  423. package/registry/blocks/transaction-list/index.js +4 -0
  424. package/registry/blocks/transaction-list/transaction-list.jsx +383 -0
  425. package/registry/blocks/whiteboard/README.md +179 -0
  426. package/registry/blocks/whiteboard/block.json +10 -0
  427. package/registry/blocks/whiteboard/demo.jsx +169 -0
  428. package/registry/blocks/whiteboard/index.js +6 -0
  429. package/registry/blocks/whiteboard/templates.js +150 -0
  430. package/registry/blocks/whiteboard/whiteboard.jsx +721 -0
  431. package/src/install.mjs +18 -0
  432. package/src/shadcn.mjs +171 -0
  433. package/templates/next/files/README.md +145 -11
  434. package/templates/next/files/app/globals.css +55 -4
  435. package/templates/next/files/check/run.mjs +3 -3
  436. package/templates/next/files/components/parts/fixture.jsx +1 -1
  437. package/templates/next/files/components/parts/sample.js +15 -0
  438. package/templates/next/files/components/ui/avatar.jsx +141 -0
  439. package/templates/next/files/components/ui/breadcrumb.jsx +149 -0
  440. package/templates/next/files/components/ui/button-group.jsx +70 -0
  441. package/templates/next/files/components/ui/button.jsx +132 -12
  442. package/templates/next/files/components/ui/calendar.jsx +609 -0
  443. package/templates/next/files/components/ui/carousel.jsx +281 -0
  444. package/templates/next/files/components/ui/collapsible.jsx +208 -0
  445. package/templates/next/files/components/ui/combobox.jsx +608 -0
  446. package/templates/next/files/components/ui/command.jsx +577 -0
  447. package/templates/next/files/components/ui/context-menu.jsx +191 -0
  448. package/templates/next/files/components/ui/date-picker.jsx +360 -0
  449. package/templates/next/files/components/ui/distribution.jsx +117 -0
  450. package/templates/next/files/components/ui/feedback.jsx +143 -0
  451. package/templates/next/files/components/ui/field.jsx +115 -3
  452. package/templates/next/files/components/ui/file-input.jsx +234 -0
  453. package/templates/next/files/components/ui/icon.jsx +130 -8
  454. package/templates/next/files/components/ui/image.jsx +143 -0
  455. package/templates/next/files/components/ui/index.js +47 -2
  456. package/templates/next/files/components/ui/input-group.jsx +213 -0
  457. package/templates/next/files/components/ui/input-otp.jsx +170 -0
  458. package/templates/next/files/components/ui/item.jsx +206 -0
  459. package/templates/next/files/components/ui/menu.jsx +454 -82
  460. package/templates/next/files/components/ui/notice.jsx +126 -14
  461. package/templates/next/files/components/ui/pagination.jsx +156 -0
  462. package/templates/next/files/components/ui/popover.jsx +284 -0
  463. package/templates/next/files/components/ui/radio.jsx +193 -0
  464. package/templates/next/files/components/ui/rating.jsx +131 -0
  465. package/templates/next/files/components/ui/resizable.jsx +338 -0
  466. package/templates/next/files/components/ui/scroll-area.jsx +123 -0
  467. package/templates/next/files/components/ui/separator.jsx +119 -0
  468. package/templates/next/files/components/ui/sheet.jsx +96 -11
  469. package/templates/next/files/components/ui/slider.jsx +174 -0
  470. package/templates/next/files/components/ui/stat.jsx +574 -0
  471. package/templates/next/files/components/ui/steps.jsx +338 -0
  472. package/templates/next/files/components/ui/swatch.jsx +33 -0
  473. package/templates/next/files/components/ui/table.jsx +130 -0
  474. package/templates/next/files/components/ui/toast.jsx +440 -0
  475. package/templates/next/files/components/ui/toggle.jsx +195 -0
  476. package/templates/next/files/components/ui/tooltip.jsx +270 -0
  477. package/templates/next/files/lib/sample.js +4 -12
  478. package/templates/next/template.json +264 -8
@@ -0,0 +1,577 @@
1
+ "use client";
2
+
3
+ import { isValidElement, useCallback, useEffect, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
4
+ import { cx } from "./cx";
5
+ import { Icon } from "./icon";
6
+ import { KbdGroup } from "./separator";
7
+ import { Spinner } from "./feedback";
8
+ import { Sheet } from "./sheet";
9
+
10
+ // Command — a searchable list of actions: type to narrow, arrows to move,
11
+ // Enter to run. The inline list of a "jump to…" panel, and (CommandDialog)
12
+ // the ⌘K palette. No command package: the list is plain data, the filter
13
+ // is commandScore below, and the ARIA is the combobox + listbox pattern —
14
+ // focus stays in the input, aria-activedescendant names the active option,
15
+ // so a screen reader follows the highlight while the caret stays put.
16
+ //
17
+ // items [{ value?, label, description?, icon?, shortcut?, keywords?,
18
+ // group?, disabled?, onSelect? }] — `value` defaults to the
19
+ // label and must be unique; `icon` a glyph name from ./icon or
20
+ // a node (an Avatar); `shortcut` "⌘N" or ["⌘", "N"] (a hint
21
+ // only, shown from md up: bind the key yourself); `keywords`
22
+ // are matched but never shown; `group` is the heading items
23
+ // sit under, in the order groups first appear
24
+ // onSelect (item) — after the item's own onSelect
25
+ // label what a screen reader calls the input and the list
26
+ // placeholder the input's; empty — shown when nothing matches (a string
27
+ // or a node)
28
+ // query, defaultQuery, onQueryChange(query) — the typed text,
29
+ // controlled or not
30
+ // onSearch async (query, { signal }) → items: results come from you
31
+ // (a server search), debounced; the spinner shows while it
32
+ // runs, a stale answer is dropped. `items` show until the
33
+ // first answer
34
+ // loading true while you fetch `items` yourself
35
+ // filter false when `items` arrive already filtered
36
+ // autoFocus focus the input on mount
37
+ //
38
+ // Keys: ↑ ↓ move (disabled items are skipped), Home · End jump to the
39
+ // first · last, PageUp · PageDown by eight, Enter runs the active item. The
40
+ // first match is always active, so typing then Enter runs the best hit.
41
+ //
42
+ // CommandDialog — the same list in the kit's Sheet (center; a bottom sheet
43
+ // on a phone). `open` + `onOpenChange` or uncontrolled; `hotkey="k"` binds
44
+ // ⌘K / Ctrl+K to open and close it. Running an item closes it.
45
+ //
46
+ // commandScore(text, query) → 0…1 (0 = no match) and commandFilter(items,
47
+ // query) → the matching items, best first within their group, are exported
48
+ // for the parts built on this (Combobox) and for a list of your own.
49
+ //
50
+ // The one rule: the list is data. Never map items into your own markup —
51
+ // the ids, roles and keys here are what make it work for everyone.
52
+
53
+ // ─── matching ──────────────────────────────────────────────────────────
54
+
55
+ const fold = (s) =>
56
+ String(s ?? "")
57
+ .normalize("NFD")
58
+ .replace(/[\u0300-\u036f]/g, "")
59
+ .toLowerCase();
60
+
61
+ const BOUNDARY = /[\s\-_/.,:;()@&+]/;
62
+
63
+ /**
64
+ * How well `text` matches `query`, 0 (not at all) to 1 (exactly). Accents
65
+ * and case are ignored. Best to worst: equal, a prefix, a substring at a
66
+ * word start, a substring inside a word, then the query's letters in order
67
+ * ("nyc" in "New York City"), scored by how many land on word starts.
68
+ */
69
+ export function commandScore(text, query) {
70
+ const t = fold(text);
71
+ const q = fold(query).trim();
72
+ if (!q) return 1;
73
+ if (!t) return 0;
74
+ if (t === q) return 1;
75
+ if (t.startsWith(q)) return 0.9;
76
+ const at = t.indexOf(q);
77
+ if (at > 0) return (BOUNDARY.test(t[at - 1]) ? 0.8 : 0.6) - Math.min(at, 40) * 0.002;
78
+ // The letters in order.
79
+ let from = 0;
80
+ let prev = -2;
81
+ let points = 0;
82
+ let count = 0;
83
+ for (const ch of q) {
84
+ if (ch === " ") continue;
85
+ const found = t.indexOf(ch, from);
86
+ if (found < 0) return 0;
87
+ points += found === 0 || BOUNDARY.test(t[found - 1]) ? 1 : found === prev + 1 ? 0.6 : 0.2;
88
+ count++;
89
+ prev = found;
90
+ from = found + 1;
91
+ }
92
+ return 0.1 + 0.3 * (points / count);
93
+ }
94
+
95
+ // An item's best field: the label counts most, keywords nearly as much,
96
+ // the description least.
97
+ function scoreItem(item, q) {
98
+ const best = (query) =>
99
+ Math.max(
100
+ commandScore(item.label, query),
101
+ ...(item.keywords ?? []).map((k) => commandScore(k, query) * 0.95),
102
+ item.description && typeof item.description === "string" ? commandScore(item.description, query) * 0.7 : 0,
103
+ );
104
+ const whole = best(q);
105
+ if (whole > 0) return whole;
106
+ // Words in any order ("lovelace ada"): every word must match somewhere.
107
+ const words = fold(q).split(/\s+/).filter(Boolean);
108
+ if (words.length < 2) return 0;
109
+ let sum = 0;
110
+ for (const w of words) {
111
+ const s = best(w);
112
+ if (!s) return 0;
113
+ sum += s;
114
+ }
115
+ return (sum / words.length) * 0.9;
116
+ }
117
+
118
+ /** Every item with a `value` (the label when it has none). */
119
+ export function withValues(items) {
120
+ return (items ?? []).filter(Boolean).map((it) => (it.value === undefined ? { ...it, value: String(it.label) } : { ...it, value: String(it.value) }));
121
+ }
122
+
123
+ /**
124
+ * The items that match `query`, best first within their group; the groups
125
+ * keep the order they first appear in. An empty query returns them all,
126
+ * in order.
127
+ */
128
+ export function commandFilter(items, query) {
129
+ const all = withValues(items);
130
+ if (!fold(query).trim()) return all;
131
+ const order = [];
132
+ const byGroup = new Map();
133
+ all.forEach((item, i) => {
134
+ const score = scoreItem(item, query);
135
+ if (!score) return;
136
+ const g = item.group ?? "";
137
+ if (!byGroup.has(g)) {
138
+ byGroup.set(g, []);
139
+ order.push(g);
140
+ }
141
+ byGroup.get(g).push({ item, score, i });
142
+ });
143
+ return order.flatMap((g) => byGroup.get(g).sort((a, b) => b.score - a.score || a.i - b.i).map((x) => x.item));
144
+ }
145
+
146
+ // ─── pieces shared with Combobox ───────────────────────────────────────
147
+
148
+ /** The option's DOM id: stable for a list id and an index. */
149
+ export const optionId = (listId, i) => `${listId}-o${i}`;
150
+
151
+ /** The active value as the list shows it: the asked one while it is
152
+ * visible and enabled, else the first enabled item. */
153
+ export function resolveActive(items, active) {
154
+ if (active != null && items.some((it) => it.value === active && !it.disabled)) return active;
155
+ return items.find((it) => !it.disabled)?.value ?? null;
156
+ }
157
+
158
+ /**
159
+ * The list keys. Returns true (and prevents the default) when it handled
160
+ * the key: ↑ ↓ Home End PageUp PageDown move, Enter picks.
161
+ */
162
+ export function listKeyDown(e, items, active, setActive, onPick) {
163
+ const live = items.filter((it) => !it.disabled);
164
+ if (!live.length) return false;
165
+ const i = live.findIndex((it) => it.value === active);
166
+ const last = live.length - 1;
167
+ let next;
168
+ switch (e.key) {
169
+ case "ArrowDown":
170
+ next = i < 0 ? 0 : Math.min(i + 1, last);
171
+ break;
172
+ case "ArrowUp":
173
+ next = i < 0 ? last : Math.max(i - 1, 0);
174
+ break;
175
+ case "Home":
176
+ next = 0;
177
+ break;
178
+ case "End":
179
+ next = last;
180
+ break;
181
+ case "PageDown":
182
+ next = Math.min((i < 0 ? 0 : i) + 8, last);
183
+ break;
184
+ case "PageUp":
185
+ next = Math.max((i < 0 ? 0 : i) - 8, 0);
186
+ break;
187
+ case "Enter":
188
+ // An IME composing a word owns Enter.
189
+ if (i < 0 || e.nativeEvent?.isComposing) return false;
190
+ e.preventDefault();
191
+ onPick(live[i]);
192
+ return true;
193
+ default:
194
+ return false;
195
+ }
196
+ e.preventDefault();
197
+ setActive(live[next].value);
198
+ return true;
199
+ }
200
+
201
+ /**
202
+ * Results from an async `onSearch(query, { signal })`, debounced by
203
+ * `delay` ms (none for the empty query). A newer query aborts the older
204
+ * one; a failed search keeps the last results and sets `error`.
205
+ */
206
+ export function useAsyncSearch(onSearch, query, { enabled = true, delay = 200 } = {}) {
207
+ const fn = useRef(onSearch);
208
+ fn.current = onSearch;
209
+ const has = Boolean(onSearch);
210
+ const [state, setState] = useState({ items: null, loading: false, error: null });
211
+ useEffect(() => {
212
+ if (!has || !enabled) return;
213
+ const ctl = new AbortController();
214
+ setState((s) => (s.loading ? s : { ...s, loading: true }));
215
+ const timer = setTimeout(async () => {
216
+ try {
217
+ const items = await fn.current(query, { signal: ctl.signal });
218
+ if (!ctl.signal.aborted) setState({ items: items ?? [], loading: false, error: null });
219
+ } catch (error) {
220
+ if (!ctl.signal.aborted) setState((s) => ({ items: s.items ?? [], loading: false, error }));
221
+ }
222
+ }, query.trim() ? delay : 0);
223
+ return () => {
224
+ clearTimeout(timer);
225
+ ctl.abort();
226
+ };
227
+ }, [has, enabled, query, delay]);
228
+ return state;
229
+ }
230
+
231
+ const PHONE = "(max-width: 639.98px)";
232
+ const subscribePhone = (cb) => {
233
+ const m = window.matchMedia(PHONE);
234
+ m.addEventListener("change", cb);
235
+ return () => m.removeEventListener("change", cb);
236
+ };
237
+ /** True under the kit's `sm` breakpoint (640px), where Sheet turns every
238
+ * overlay into a bottom sheet. False on the server. */
239
+ export function useIsPhone() {
240
+ return useSyncExternalStore(
241
+ subscribePhone,
242
+ () => window.matchMedia(PHONE).matches,
243
+ () => false,
244
+ );
245
+ }
246
+
247
+ /** An item's icon: a glyph name, or a node (an Avatar) as it is. */
248
+ export function ItemIcon({ icon, className }) {
249
+ if (!icon) return null;
250
+ if (typeof icon === "string") return <Icon name={icon} className={cx("shrink-0 text-muted-foreground", className)} />;
251
+ return isValidElement(icon) ? <span className={cx("flex shrink-0 items-center", className)}>{icon}</span> : null;
252
+ }
253
+ /**
254
+ * The search bar at the top of a Command or a Combobox panel: a glyph, the
255
+ * input (role=combobox, pointing at `listId`), a spinner while `busy`.
256
+ * Every other prop goes to the <input>.
257
+ */
258
+ export function CommandInput({ listId, activeId, busy, inputRef, className, ...rest }) {
259
+ return (
260
+ <div className={cx("flex items-center gap-2 border-b border-border px-3", className)}>
261
+ <Icon name="search" className="shrink-0 text-muted-foreground" />
262
+ <input
263
+ ref={inputRef}
264
+ type="text"
265
+ role="combobox"
266
+ aria-expanded="true"
267
+ aria-controls={listId}
268
+ aria-activedescendant={activeId ?? undefined}
269
+ aria-autocomplete="list"
270
+ autoComplete="off"
271
+ autoCorrect="off"
272
+ spellCheck={false}
273
+ // 16px on a phone, or iOS zooms the page when it takes focus.
274
+ className="min-h-11 min-w-0 flex-1 bg-transparent py-2 text-base text-foreground outline-none placeholder:text-muted-foreground sm:text-sm"
275
+ {...rest}
276
+ />
277
+ {busy ? <Spinner size="sm" label="Searching" className="text-muted-foreground" /> : null}
278
+ </div>
279
+ );
280
+ }
281
+
282
+ /**
283
+ * The listbox: groups under their headings, options with icon, label,
284
+ * description and shortcut; the empty, loading and error lines around it.
285
+ *
286
+ * id, label the listbox's id and name
287
+ * items already filtered, in the order shown
288
+ * active the active value; onActive(value) on hover
289
+ * onPick(item) click, or Enter through listKeyDown
290
+ * selected a Set of chosen values (a Combobox): each option shows a
291
+ * check (`multiple`: a box at the start) and aria-selected
292
+ * says chosen; without it aria-selected marks the active
293
+ * className the scroll box's; its max height (default max-h-72)
294
+ * focusable the listbox takes focus itself (aria-activedescendant on
295
+ * it, `onKeyDown` for the keys) — when there is no input
296
+ */
297
+ export function CommandList({
298
+ id,
299
+ label,
300
+ items,
301
+ active,
302
+ onActive,
303
+ onPick,
304
+ selected,
305
+ multiple = false,
306
+ empty = "No results.",
307
+ loading = false,
308
+ error,
309
+ focusable = false,
310
+ onKeyDown,
311
+ listRef,
312
+ className,
313
+ }) {
314
+ const box = useRef(null);
315
+ const activeIndex = items.findIndex((it) => it.value === active);
316
+
317
+ // Keep the active option in view as the keys move it.
318
+ useEffect(() => {
319
+ if (activeIndex < 0) return;
320
+ document.getElementById(optionId(id, activeIndex))?.scrollIntoView({ block: "nearest" });
321
+ }, [id, activeIndex]);
322
+
323
+ // Consecutive items with one group share a heading.
324
+ const sections = [];
325
+ items.forEach((item, i) => {
326
+ const g = item.group ?? "";
327
+ const tail = sections[sections.length - 1];
328
+ if (tail && tail.group === g) tail.rows.push([item, i]);
329
+ else sections.push({ group: g, rows: [[item, i]] });
330
+ });
331
+
332
+ function option([item, i]) {
333
+ const isActive = item.value === active;
334
+ const chosen = selected ? selected.has(item.value) : false;
335
+ const keys = item.shortcut ? (Array.isArray(item.shortcut) ? item.shortcut : [item.shortcut]) : null;
336
+ return (
337
+ <div
338
+ key={item.value}
339
+ id={optionId(id, i)}
340
+ role="option"
341
+ aria-selected={selected ? chosen : isActive}
342
+ aria-disabled={item.disabled || undefined}
343
+ data-active={isActive || undefined}
344
+ // The input keeps focus; a press on an option must not take it.
345
+ onMouseDown={(e) => e.preventDefault()}
346
+ onMouseMove={() => {
347
+ if (!item.disabled && !isActive) onActive?.(item.value);
348
+ }}
349
+ onClick={() => {
350
+ if (!item.disabled) onPick?.(item);
351
+ }}
352
+ className={cx(
353
+ "flex min-h-11 cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm text-popover-foreground md:min-h-9",
354
+ "data-[active]:bg-accent data-[active]:text-accent-foreground",
355
+ "aria-disabled:cursor-not-allowed aria-disabled:opacity-50",
356
+ )}
357
+ >
358
+ {selected && multiple ? (
359
+ <span
360
+ aria-hidden="true"
361
+ className={cx(
362
+ "grid size-4 shrink-0 place-items-center rounded-sm border",
363
+ chosen ? "border-primary bg-primary text-primary-foreground" : "border-input bg-background",
364
+ )}
365
+ >
366
+ {chosen ? <Icon name="check" size={12} /> : null}
367
+ </span>
368
+ ) : null}
369
+ <ItemIcon icon={item.icon} />
370
+ <span className="min-w-0 flex-1">
371
+ <span className="block truncate">{item.label}</span>
372
+ {item.description ? <span className="block truncate text-xs text-muted-foreground">{item.description}</span> : null}
373
+ </span>
374
+ {keys ? (
375
+ <span className="hidden shrink-0 md:inline-flex" aria-hidden="true">
376
+ <KbdGroup keys={keys} />
377
+ </span>
378
+ ) : null}
379
+ {selected && !multiple ? (
380
+ <Icon name="check" className={cx("shrink-0 text-foreground", !chosen && "invisible")} />
381
+ ) : null}
382
+ </div>
383
+ );
384
+ }
385
+
386
+ const status = error ? "Search failed. Try again." : loading ? null : items.length === 0 ? empty : null;
387
+
388
+ return (
389
+ <div className={cx("overflow-y-auto overscroll-contain", className ?? "max-h-72")}>
390
+ {loading && items.length === 0 ? (
391
+ <div role="status" className="flex items-center justify-center gap-2 px-3 py-6 text-sm text-muted-foreground">
392
+ <Spinner size="sm" label={null} />
393
+ Searching…
394
+ </div>
395
+ ) : null}
396
+ {status && !(loading && items.length === 0) ? (
397
+ <div role="status" className="px-3 py-6 text-center text-sm text-muted-foreground">
398
+ {status}
399
+ </div>
400
+ ) : null}
401
+ <div
402
+ ref={(el) => {
403
+ box.current = el;
404
+ if (listRef) listRef.current = el;
405
+ }}
406
+ id={id}
407
+ role="listbox"
408
+ aria-label={label}
409
+ aria-multiselectable={multiple || undefined}
410
+ tabIndex={focusable ? 0 : undefined}
411
+ aria-activedescendant={focusable && activeIndex >= 0 ? optionId(id, activeIndex) : undefined}
412
+ onKeyDown={onKeyDown}
413
+ className={cx("scroll-py-1 outline-none", items.length ? "p-1" : "hidden")}
414
+ >
415
+ {sections.map((s, n) =>
416
+ s.group ? (
417
+ <div
418
+ key={`${s.group}-${n}`}
419
+ role="group"
420
+ aria-labelledby={`${id}-g${n}`}
421
+ className={cx(n > 0 && "mt-1 border-t border-border pt-1")}
422
+ >
423
+ <div id={`${id}-g${n}`} className="px-2 pb-1 pt-1.5 text-xs font-medium text-muted-foreground">
424
+ {s.group}
425
+ </div>
426
+ {s.rows.map(option)}
427
+ </div>
428
+ ) : (
429
+ <div key={`-${n}`} role="presentation" className={cx(n > 0 && "mt-1 border-t border-border pt-1")}>
430
+ {s.rows.map(option)}
431
+ </div>
432
+ ),
433
+ )}
434
+ </div>
435
+ </div>
436
+ );
437
+ }
438
+
439
+ // ─── Command ───────────────────────────────────────────────────────────
440
+
441
+ export function Command({
442
+ items,
443
+ onSelect,
444
+ label = "Commands",
445
+ placeholder = "Type a command or search…",
446
+ empty = "No results.",
447
+ query,
448
+ defaultQuery = "",
449
+ onQueryChange,
450
+ onSearch,
451
+ loading = false,
452
+ filter = true,
453
+ autoFocus = false,
454
+ className,
455
+ listClassName,
456
+ bare = false,
457
+ }) {
458
+ const listId = `${useId().replace(/[^\w-]/g, "")}-list`;
459
+ const input = useRef(null);
460
+ const [own, setOwn] = useState(defaultQuery);
461
+ const text = query ?? own;
462
+ const [active, setActive] = useState(null);
463
+ const search = useAsyncSearch(onSearch, text);
464
+
465
+ const source = onSearch ? search.items ?? items : items;
466
+ const visible = useMemo(
467
+ () => (filter && !onSearch ? commandFilter(source, text) : withValues(source)),
468
+ [source, text, filter, onSearch],
469
+ );
470
+ const current = resolveActive(visible, active);
471
+ const activeIndex = visible.findIndex((it) => it.value === current);
472
+
473
+ useEffect(() => {
474
+ if (autoFocus) input.current?.focus({ preventScroll: true });
475
+ }, [autoFocus]);
476
+
477
+ function setText(v) {
478
+ if (query === undefined) setOwn(v);
479
+ onQueryChange?.(v);
480
+ // A new query starts from its best match.
481
+ setActive(null);
482
+ }
483
+
484
+ function pick(item) {
485
+ item.onSelect?.(item);
486
+ onSelect?.(item);
487
+ }
488
+
489
+ const busy = loading || search.loading;
490
+
491
+ return (
492
+ <div
493
+ className={cx(
494
+ "flex min-w-0 flex-col overflow-hidden bg-popover text-popover-foreground",
495
+ !bare && "rounded-md border border-border has-[input:focus-visible]:ring-2 has-[input:focus-visible]:ring-ring",
496
+ className,
497
+ )}
498
+ >
499
+ <CommandInput
500
+ inputRef={input}
501
+ listId={listId}
502
+ activeId={activeIndex >= 0 ? optionId(listId, activeIndex) : null}
503
+ busy={busy && visible.length > 0}
504
+ aria-label={label}
505
+ placeholder={placeholder}
506
+ value={text}
507
+ onChange={(e) => setText(e.target.value)}
508
+ onKeyDown={(e) => listKeyDown(e, visible, current, setActive, pick)}
509
+ />
510
+ <CommandList
511
+ id={listId}
512
+ label={label}
513
+ items={visible}
514
+ active={current}
515
+ onActive={setActive}
516
+ onPick={pick}
517
+ empty={empty}
518
+ loading={busy}
519
+ error={search.error}
520
+ className={listClassName ?? "max-h-80"}
521
+ />
522
+ </div>
523
+ );
524
+ }
525
+
526
+ export function CommandDialog({ open, defaultOpen = false, onOpenChange, hotkey, label = "Command menu", onSelect, className, ...command }) {
527
+ const controlled = open !== undefined;
528
+ const [own, setOwn] = useState(defaultOpen);
529
+ const isOpen = controlled ? open : own;
530
+ const openRef = useRef(isOpen);
531
+ openRef.current = isOpen;
532
+
533
+ const change = useCallback(
534
+ (next) => {
535
+ if (next === openRef.current) return;
536
+ openRef.current = next;
537
+ if (!controlled) setOwn(next);
538
+ onOpenChange?.(next);
539
+ },
540
+ [controlled, onOpenChange],
541
+ );
542
+ const changeRef = useRef(change);
543
+ changeRef.current = change;
544
+
545
+ // ⌘K / Ctrl+K opens it, and closes it again.
546
+ useEffect(() => {
547
+ if (!hotkey) return;
548
+ function onKey(e) {
549
+ if (e.key?.toLowerCase() !== String(hotkey).toLowerCase() || !(e.metaKey || e.ctrlKey) || e.altKey || e.shiftKey) return;
550
+ e.preventDefault();
551
+ changeRef.current(!openRef.current);
552
+ }
553
+ window.addEventListener("keydown", onKey);
554
+ return () => window.removeEventListener("keydown", onKey);
555
+ }, [hotkey]);
556
+
557
+ // A bare Sheet: no header (Esc and a press outside close it), a flush
558
+ // body so the input sits at the top edge, the dialog named by `label`.
559
+ return (
560
+ <Sheet bare label={label} open={isOpen} onClose={() => change(false)} side="center" className={cx("sm:mt-[12dvh]", className)}>
561
+ {/* Mounted only while open, so each opening starts empty. */}
562
+ {isOpen ? (
563
+ <Command
564
+ bare
565
+ label={label}
566
+ autoFocus={isOpen}
567
+ listClassName="max-h-[min(24rem,60dvh)]"
568
+ onSelect={(item) => {
569
+ change(false);
570
+ onSelect?.(item);
571
+ }}
572
+ {...command}
573
+ />
574
+ ) : null}
575
+ </Sheet>
576
+ );
577
+ }