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,473 @@
1
+ // The column-type registry: what each kind of column holds, how it reads
2
+ // and writes text, sorts and filters. Plain functions — no React, so they
3
+ // run on the server, in a test, in csv-import. How a type is DRAWN and
4
+ // EDITED lives in cells.jsx, keyed by `editor` below.
5
+ //
6
+ // A type is:
7
+ // label what a person calls it ("Currency")
8
+ // icon a kit glyph for the header
9
+ // align start · end (numbers line up on the right)
10
+ // width the default column width, px
11
+ // editor how it is edited in a cell: text · number · date · select ·
12
+ // person · toggle (flips in place) · rating · url · none
13
+ // empty(v) true when the cell holds nothing
14
+ // parse(text, col, ctx) → { value } | { error } — typed or pasted text in.
15
+ // A select may add `create: [{ value, label }]` (options that
16
+ // do not exist yet)
17
+ // toText(v, col, ctx) the text a copy puts on the clipboard and an
18
+ // edit starts from ("1250.5", "2026-09-23", "Won, Lost")
19
+ // format(v, col, ctx) the text a cell shows ("$1,250.50", "Sep 23, 2026")
20
+ // compare(a, b, col, ctx) the sort order; empties always sort last
21
+ // filters the conditions the Filter panel offers: [{ id, label, arg,
22
+ // test(v, arg, col, ctx) }] — `arg` names the value control:
23
+ // text · number · date · option · options · person · none
24
+ //
25
+ // `ctx` is { people, locale, currency }: what a type reads beyond its own
26
+ // column (the person list, the reader's locale).
27
+
28
+ const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" });
29
+
30
+ const isBlank = (v) => v == null || v === "" || (Array.isArray(v) && v.length === 0);
31
+ const str = (v) => (v == null ? "" : String(v));
32
+ const lc = (v) => str(v).toLowerCase();
33
+
34
+ // ── numbers ─────────────────────────────────────────────────────────────
35
+
36
+ /** "$1,250.50", "(300)", "1 250,5", "-12" → a number, or null for blank,
37
+ * or NaN when it is not one. */
38
+ export function parseNumberText(text) {
39
+ let s = str(text).trim();
40
+ if (!s) return null;
41
+ let neg = false;
42
+ if (/^\(.*\)$/.test(s)) {
43
+ neg = true;
44
+ s = s.slice(1, -1);
45
+ }
46
+ s = s.replace(/[\s ]/g, "").replace(/^[^\d.,+-]+|[^\d.,]+$/g, "");
47
+ if (s.startsWith("-")) {
48
+ neg = !neg;
49
+ s = s.slice(1);
50
+ } else if (s.startsWith("+")) s = s.slice(1);
51
+ // A comma is a decimal point only when it is the one separator and two
52
+ // or fewer digits follow it ("12,5"); else it groups thousands.
53
+ const commas = (s.match(/,/g) || []).length;
54
+ const dots = (s.match(/\./g) || []).length;
55
+ if (commas === 1 && dots === 0 && /,\d{1,2}$/.test(s)) s = s.replace(",", ".");
56
+ else if (dots > 1 && commas <= 1) s = s.replace(/\./g, "").replace(",", ".");
57
+ else s = s.replace(/,/g, "");
58
+ if (!/^\d*\.?\d+$|^\d+\.$/.test(s)) return NaN;
59
+ const n = Number(s);
60
+ return neg ? -n : n;
61
+ }
62
+
63
+ function numberFormat(col, ctx, currency) {
64
+ const digits = col?.precision ?? (currency ? 2 : undefined);
65
+ const opts = currency
66
+ ? { style: "currency", currency: col?.currency || ctx?.currency || "USD", minimumFractionDigits: digits, maximumFractionDigits: digits }
67
+ : { maximumFractionDigits: digits ?? 2, minimumFractionDigits: col?.precision ?? 0 };
68
+ return cachedFormat("n", ctx?.locale, opts, () => new Intl.NumberFormat(ctx?.locale, opts), () => new Intl.NumberFormat(undefined, { maximumFractionDigits: 2 }));
69
+ }
70
+
71
+ // Building an Intl formatter costs far more than using one, and a sort or
72
+ // a search formats every cell: one per locale and options, kept.
73
+ const FORMATS = new Map();
74
+ function cachedFormat(kind, locale, opts, make, fallback) {
75
+ const key = `${kind}|${locale ?? ""}|${JSON.stringify(opts)}`;
76
+ let f = FORMATS.get(key);
77
+ if (!f) {
78
+ try {
79
+ f = make();
80
+ } catch {
81
+ f = fallback();
82
+ }
83
+ FORMATS.set(key, f);
84
+ }
85
+ return f;
86
+ }
87
+
88
+ const numberCompare = (a, b) => a - b;
89
+
90
+ const NUMBER_FILTERS = [
91
+ { id: "eq", label: "=", arg: "number", test: (v, a) => v === a },
92
+ { id: "ne", label: "≠", arg: "number", test: (v, a) => v !== a },
93
+ { id: "gt", label: ">", arg: "number", test: (v, a) => v > a },
94
+ { id: "lt", label: "<", arg: "number", test: (v, a) => v < a },
95
+ { id: "gte", label: "≥", arg: "number", test: (v, a) => v >= a },
96
+ { id: "lte", label: "≤", arg: "number", test: (v, a) => v <= a },
97
+ ];
98
+
99
+ // ── dates ───────────────────────────────────────────────────────────────
100
+
101
+ const MONTHS = ["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"];
102
+ const pad = (n) => String(n).padStart(2, "0");
103
+
104
+ function validDay(y, m, d) {
105
+ if (!(y >= 1000 && y <= 9999 && m >= 1 && m <= 12 && d >= 1)) return null;
106
+ const last = new Date(Date.UTC(y, m, 0)).getUTCDate();
107
+ return d <= last ? `${y}-${pad(m)}-${pad(d)}` : null;
108
+ }
109
+
110
+ /** Text → "YYYY-MM-DD", null for blank, or undefined when it is no day.
111
+ * ISO first, then 9/23/2026 (or 23/9/2026 with order "dmy", or when the
112
+ * first number cannot be a month), then "Sep 23, 2026" / "23 Sep 2026". */
113
+ export function parseDateText(text, order = "mdy") {
114
+ const s = str(text).trim();
115
+ if (!s) return null;
116
+ let m = /^(\d{4})-(\d{1,2})-(\d{1,2})(?:[T\s].*)?$/.exec(s);
117
+ if (m) return validDay(+m[1], +m[2], +m[3]) ?? undefined;
118
+ m = /^(\d{1,2})[/.-](\d{1,2})[/.-](\d{2}|\d{4})$/.exec(s);
119
+ if (m) {
120
+ let [a, b] = [+m[1], +m[2]];
121
+ const y = m[3].length === 2 ? 2000 + +m[3] : +m[3];
122
+ if (order === "dmy" || a > 12) [a, b] = [b, a];
123
+ return validDay(y, a, b) ?? undefined;
124
+ }
125
+ m = /^([a-z]{3,})\.?\s+(\d{1,2})(?:st|nd|rd|th)?,?\s+(\d{4})$/i.exec(s);
126
+ if (m) {
127
+ const mo = MONTHS.indexOf(m[1].slice(0, 3).toLowerCase());
128
+ return mo < 0 ? undefined : validDay(+m[3], mo + 1, +m[2]) ?? undefined;
129
+ }
130
+ m = /^(\d{1,2})(?:st|nd|rd|th)?\s+([a-z]{3,})\.?,?\s+(\d{4})$/i.exec(s);
131
+ if (m) {
132
+ const mo = MONTHS.indexOf(m[2].slice(0, 3).toLowerCase());
133
+ return mo < 0 ? undefined : validDay(+m[3], mo + 1, +m[1]) ?? undefined;
134
+ }
135
+ return undefined;
136
+ }
137
+
138
+ function formatDay(v, ctx) {
139
+ if (!v) return "";
140
+ const [y, m, d] = String(v).split("-").map(Number);
141
+ if (!y || !m || !d) return str(v);
142
+ const opts = { month: "short", day: "numeric", year: "numeric", timeZone: "UTC" };
143
+ const fmt = cachedFormat("d", ctx?.locale, opts, () => new Intl.DateTimeFormat(ctx?.locale, opts), () => new Intl.DateTimeFormat(undefined, opts));
144
+ return fmt.format(new Date(Date.UTC(y, m - 1, d)));
145
+ }
146
+
147
+ // ── options (select, multi-select) ──────────────────────────────────────
148
+
149
+ const optionsOf = (col) => col?.options ?? [];
150
+ export const optionOf = (col, value) => optionsOf(col).find((o) => o.value === value);
151
+ const labelOf = (col, value) => optionOf(col, value)?.label ?? str(value);
152
+
153
+ /** A new option's value from its label: "Closed won" → "closed-won". */
154
+ export function optionValue(label, taken = []) {
155
+ const base = lc(label).trim().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "option";
156
+ let v = base;
157
+ for (let i = 2; taken.includes(v); i++) v = `${base}-${i}`;
158
+ return v;
159
+ }
160
+
161
+ // Hues 1–5 cycle through new options, so a created one is never grey.
162
+ function nextHue(col, extra = 0) {
163
+ return ((optionsOf(col).length + extra) % 5) + 1;
164
+ }
165
+
166
+ function matchOption(col, text) {
167
+ const t = lc(text).trim();
168
+ return optionsOf(col).find((o) => lc(o.label) === t || lc(o.value) === t);
169
+ }
170
+
171
+ function parseOptions(text, col, multiple) {
172
+ const s = str(text).trim();
173
+ if (!s) return { value: multiple ? [] : null };
174
+ const parts = multiple ? s.split(/\s*[,;\n]\s*/).filter(Boolean) : [s];
175
+ const value = [];
176
+ const create = [];
177
+ for (const p of parts) {
178
+ const hit = matchOption(col, p) ?? create.find((c) => lc(c.label) === lc(p));
179
+ if (hit) {
180
+ if (!value.includes(hit.value)) value.push(hit.value);
181
+ continue;
182
+ }
183
+ if (col?.allowNew === false) return { error: `“${p}” is not one of the options` };
184
+ const o = { value: optionValue(p, [...optionsOf(col).map((x) => x.value), ...create.map((x) => x.value)]), label: p, hue: nextHue(col, create.length) };
185
+ create.push(o);
186
+ value.push(o.value);
187
+ }
188
+ return create.length ? { value: multiple ? value : value[0], create } : { value: multiple ? value : value[0] };
189
+ }
190
+
191
+ const optionRank = (col, v) => {
192
+ const i = optionsOf(col).findIndex((o) => o.value === v);
193
+ return i < 0 ? Infinity : i;
194
+ };
195
+
196
+ // ── people ──────────────────────────────────────────────────────────────
197
+
198
+ const personOf = (ctx, id) => (ctx?.people ?? []).find((p) => p.id === id);
199
+ const personName = (ctx, id) => personOf(ctx, id)?.name ?? str(id);
200
+ const asList = (v) => (Array.isArray(v) ? v : isBlank(v) ? [] : [v]);
201
+
202
+ function parsePeople(text, col, ctx) {
203
+ const s = str(text).trim();
204
+ if (!s) return { value: col?.multiple ? [] : null };
205
+ const parts = col?.multiple ? s.split(/\s*[,;\n]\s*/).filter(Boolean) : [s];
206
+ const ids = [];
207
+ for (const p of parts) {
208
+ const t = lc(p);
209
+ const hit = (ctx?.people ?? []).find((x) => lc(x.name) === t || lc(x.email) === t || lc(x.id) === t || lc(x.name).split(" ")[0] === t);
210
+ if (!hit) return { error: `No one called “${p}”` };
211
+ if (!ids.includes(hit.id)) ids.push(hit.id);
212
+ }
213
+ return { value: col?.multiple ? ids : ids[0] };
214
+ }
215
+
216
+ // ── the registry ────────────────────────────────────────────────────────
217
+
218
+ const TEXT_FILTERS = [
219
+ { id: "contains", label: "contains", arg: "text", test: (v, a) => lc(v).includes(lc(a)) },
220
+ { id: "not-contains", label: "does not contain", arg: "text", test: (v, a) => !lc(v).includes(lc(a)) },
221
+ { id: "is", label: "is", arg: "text", test: (v, a) => lc(v).trim() === lc(a).trim() },
222
+ ];
223
+
224
+ export const COLUMN_TYPES = {
225
+ text: {
226
+ label: "Text",
227
+ icon: "type",
228
+ align: "start",
229
+ width: 220,
230
+ editor: "text",
231
+ parse: (t) => ({ value: str(t) }),
232
+ toText: (v) => str(v),
233
+ format: (v) => str(v),
234
+ compare: (a, b) => collator.compare(str(a), str(b)),
235
+ filters: TEXT_FILTERS,
236
+ },
237
+ number: {
238
+ label: "Number",
239
+ icon: "hash",
240
+ align: "end",
241
+ width: 120,
242
+ editor: "number",
243
+ parse: (t) => {
244
+ const n = parseNumberText(t);
245
+ return Number.isNaN(n) ? { error: "That is not a number" } : { value: n };
246
+ },
247
+ toText: (v) => (typeof v === "number" ? String(v) : str(v)),
248
+ format: (v, col, ctx) => (typeof v === "number" ? numberFormat(col, ctx, false).format(v) : str(v)),
249
+ compare: numberCompare,
250
+ filters: NUMBER_FILTERS,
251
+ },
252
+ currency: {
253
+ label: "Currency",
254
+ icon: "dollar",
255
+ align: "end",
256
+ width: 130,
257
+ editor: "number",
258
+ parse: (t) => {
259
+ const n = parseNumberText(t);
260
+ return Number.isNaN(n) ? { error: "That is not an amount" } : { value: n };
261
+ },
262
+ toText: (v) => (typeof v === "number" ? String(v) : str(v)),
263
+ format: (v, col, ctx) => (typeof v === "number" ? numberFormat(col, ctx, true).format(v) : str(v)),
264
+ compare: numberCompare,
265
+ filters: NUMBER_FILTERS,
266
+ },
267
+ date: {
268
+ label: "Date",
269
+ icon: "calendar",
270
+ align: "start",
271
+ width: 140,
272
+ editor: "date",
273
+ parse: (t, col) => {
274
+ const d = parseDateText(t, col?.dateOrder);
275
+ return d === undefined ? { error: "That is not a date — try 2026-09-23" } : { value: d };
276
+ },
277
+ toText: (v) => str(v),
278
+ format: (v, col, ctx) => formatDay(v, ctx),
279
+ compare: (a, b) => (a < b ? -1 : a > b ? 1 : 0),
280
+ filters: [
281
+ { id: "is", label: "is", arg: "date", test: (v, a) => v === a },
282
+ { id: "before", label: "is before", arg: "date", test: (v, a) => v < a },
283
+ { id: "after", label: "is after", arg: "date", test: (v, a) => v > a },
284
+ ],
285
+ },
286
+ select: {
287
+ label: "Single select",
288
+ icon: "chevron-down",
289
+ align: "start",
290
+ width: 150,
291
+ editor: "select",
292
+ parse: (t, col) => parseOptions(t, col, false),
293
+ toText: (v, col) => (isBlank(v) ? "" : labelOf(col, v)),
294
+ format: (v, col) => (isBlank(v) ? "" : labelOf(col, v)),
295
+ compare: (a, b, col) => optionRank(col, a) - optionRank(col, b) || collator.compare(str(a), str(b)),
296
+ filters: [
297
+ { id: "any", label: "is any of", arg: "options", test: (v, a) => asList(a).includes(v) },
298
+ { id: "none", label: "is none of", arg: "options", test: (v, a) => !asList(a).includes(v) },
299
+ ],
300
+ },
301
+ "multi-select": {
302
+ label: "Multiple select",
303
+ icon: "tag",
304
+ align: "start",
305
+ width: 200,
306
+ editor: "select",
307
+ multiple: true,
308
+ parse: (t, col) => parseOptions(t, col, true),
309
+ toText: (v, col) => asList(v).map((x) => labelOf(col, x)).join(", "),
310
+ format: (v, col) => asList(v).map((x) => labelOf(col, x)).join(", "),
311
+ compare: (a, b, col) => optionRank(col, asList(a)[0]) - optionRank(col, asList(b)[0]) || asList(a).length - asList(b).length,
312
+ filters: [
313
+ { id: "any", label: "has any of", arg: "options", test: (v, a) => asList(v).some((x) => asList(a).includes(x)) },
314
+ { id: "all", label: "has all of", arg: "options", test: (v, a) => asList(a).every((x) => asList(v).includes(x)) },
315
+ { id: "none", label: "has none of", arg: "options", test: (v, a) => !asList(v).some((x) => asList(a).includes(x)) },
316
+ ],
317
+ },
318
+ person: {
319
+ label: "Person",
320
+ icon: "user",
321
+ align: "start",
322
+ width: 170,
323
+ editor: "person",
324
+ parse: parsePeople,
325
+ toText: (v, col, ctx) => asList(v).map((id) => personName(ctx, id)).join(", "),
326
+ format: (v, col, ctx) => asList(v).map((id) => personName(ctx, id)).join(", "),
327
+ compare: (a, b, col, ctx) => collator.compare(personName(ctx, asList(a)[0]), personName(ctx, asList(b)[0])),
328
+ filters: [
329
+ { id: "any", label: "is any of", arg: "person", test: (v, a) => asList(v).some((x) => asList(a).includes(x)) },
330
+ { id: "none", label: "is none of", arg: "person", test: (v, a) => !asList(v).some((x) => asList(a).includes(x)) },
331
+ ],
332
+ },
333
+ checkbox: {
334
+ label: "Checkbox",
335
+ icon: "square-check",
336
+ align: "center",
337
+ width: 96,
338
+ editor: "toggle",
339
+ // A checkbox is never "empty": unchecked is a value.
340
+ empty: () => false,
341
+ parse: (t) => {
342
+ const s = lc(t).trim();
343
+ if (["true", "yes", "y", "1", "x", "✓", "✔", "checked", "on", "done"].includes(s)) return { value: true };
344
+ if (["false", "no", "n", "0", "", "unchecked", "off"].includes(s)) return { value: false };
345
+ return { error: "Use yes or no" };
346
+ },
347
+ toText: (v) => (v ? "true" : "false"),
348
+ format: (v) => (v ? "Yes" : "No"),
349
+ compare: (a, b) => Number(Boolean(b)) - Number(Boolean(a)),
350
+ filters: [
351
+ { id: "checked", label: "is checked", arg: "none", test: (v) => Boolean(v) },
352
+ { id: "unchecked", label: "is not checked", arg: "none", test: (v) => !v },
353
+ ],
354
+ },
355
+ url: {
356
+ label: "Link",
357
+ icon: "link",
358
+ align: "start",
359
+ width: 200,
360
+ editor: "url",
361
+ parse: (t) => {
362
+ const s = str(t).trim();
363
+ if (!s) return { value: "" };
364
+ if (/\s/.test(s) || !/[.:]/.test(s)) return { error: "That does not look like a link" };
365
+ return { value: s };
366
+ },
367
+ toText: (v) => str(v),
368
+ format: (v) => str(v).replace(/^https?:\/\/(www\.)?/, "").replace(/\/$/, ""),
369
+ compare: (a, b) => collator.compare(str(a), str(b)),
370
+ filters: TEXT_FILTERS,
371
+ },
372
+ rating: {
373
+ label: "Rating",
374
+ icon: "star",
375
+ align: "start",
376
+ width: 130,
377
+ editor: "rating",
378
+ empty: (v) => !v,
379
+ parse: (t, col) => {
380
+ const s = str(t).trim();
381
+ if (!s) return { value: 0 };
382
+ const stars = (s.match(/[★⭐*]/g) || []).length;
383
+ const n = stars && stars === s.replace(/\s/g, "").length ? stars : Number(s);
384
+ const max = col?.max ?? 5;
385
+ if (!Number.isInteger(n) || n < 0 || n > max) return { error: `Use a whole number from 0 to ${max}` };
386
+ return { value: n };
387
+ },
388
+ toText: (v) => (v ? String(v) : ""),
389
+ format: (v, col) => (v ? `${v} of ${col?.max ?? 5}` : ""),
390
+ compare: (a, b) => (a || 0) - (b || 0),
391
+ filters: [
392
+ { id: "gte", label: "is at least", arg: "number", test: (v, a) => (v || 0) >= a },
393
+ { id: "lte", label: "is at most", arg: "number", test: (v, a) => (v || 0) <= a },
394
+ { id: "eq", label: "is", arg: "number", test: (v, a) => (v || 0) === a },
395
+ ],
396
+ },
397
+ };
398
+
399
+ // Every type also filters on emptiness.
400
+ const EMPTY_FILTERS = [
401
+ { id: "empty", label: "is empty", arg: "none", test: (v, a, col, ctx, type) => type.empty(v) },
402
+ { id: "not-empty", label: "is not empty", arg: "none", test: (v, a, col, ctx, type) => !type.empty(v) },
403
+ ];
404
+
405
+ /** The type a column has: `custom` (the Spreadsheet's `types` prop)
406
+ * first, then the built-ins; an unknown name reads as text. Missing
407
+ * functions fall back to text's, so a custom type may be small. */
408
+ export function typeOf(col, custom) {
409
+ const name = col?.type ?? "text";
410
+ const key = custom ?? NONE;
411
+ let byName = CACHE.get(key);
412
+ if (!byName) CACHE.set(key, (byName = new Map()));
413
+ let t = byName.get(name);
414
+ if (t) return t;
415
+ const own = custom?.[name] ? { ...(COLUMN_TYPES[name] ?? {}), ...custom[name] } : COLUMN_TYPES[name] ?? COLUMN_TYPES.text;
416
+ t = { ...COLUMN_TYPES.text, empty: isBlank, ...own, name };
417
+ t.allFilters = [...(own.filters ?? COLUMN_TYPES.text.filters), ...(name === "checkbox" ? [] : EMPTY_FILTERS)];
418
+ byName.set(name, t);
419
+ return t;
420
+ }
421
+
422
+ // One resolved type per (custom registry, name): typeOf runs per cell.
423
+ const NONE = {};
424
+ const CACHE = new WeakMap();
425
+
426
+ export { isBlank };
427
+
428
+ /** Parse `text` for `col`; a blank cell is always fine (an empty value). */
429
+ export function parseCell(text, col, ctx, custom) {
430
+ const t = typeOf(col, custom);
431
+ try {
432
+ return t.parse(text, col, ctx);
433
+ } catch {
434
+ return { error: "That value does not fit here" };
435
+ }
436
+ }
437
+
438
+ /** A value moved to a column of another type: through its text. What
439
+ * does not parse becomes empty. */
440
+ export function convertValue(value, from, to, ctx, custom) {
441
+ const text = typeOf(from, custom).toText(value, from, ctx);
442
+ const r = parseCell(text, to, ctx, custom);
443
+ return "error" in r ? typeOf(to, custom).parse("", to, ctx).value ?? null : r.value;
444
+ }
445
+
446
+ /** Guess a column type from sample text values (csv-import uses it):
447
+ * checkbox, currency, number, date, url, select (few distinct values),
448
+ * multi-select (comma lists of few distinct parts), else text. Most
449
+ * values (four in five) must fit, so a few bad cells in an import are
450
+ * flagged to fix instead of turning the whole column into text. */
451
+ export function guessType(values) {
452
+ const vals = values.map((v) => str(v).trim()).filter(Boolean);
453
+ if (vals.length === 0) return "text";
454
+ const need = Math.max(1, Math.ceil(vals.length * 0.8));
455
+ const most = (fn) => vals.filter(fn).length >= need;
456
+ const isNum = (v) => {
457
+ const n = parseNumberText(v);
458
+ return n !== null && !Number.isNaN(n);
459
+ };
460
+ if (vals.every((v) => /^(true|false|yes|no|y|n|x|✓)$/i.test(v))) return "checkbox";
461
+ if (most(isNum)) return vals.some((v) => /[$€£¥₹]|^[A-Z]{3}\s?[\d.,]/.test(v)) ? "currency" : "number";
462
+ if (most((v) => /\d/.test(v) && parseDateText(v) !== undefined)) return "date";
463
+ if (most((v) => /^(https?:\/\/|www\.)\S+$/i.test(v))) return "url";
464
+ const distinct = new Set(vals.map((v) => v.toLowerCase()));
465
+ if (vals.some((v) => v.includes(",")) && vals.every((v) => v.length <= 80)) {
466
+ const all = vals.flatMap((v) => v.split(/\s*,\s*/)).map((p) => p.toLowerCase());
467
+ const parts = new Set(all);
468
+ // Parts that repeat across cells ("SaaS, B2B" … "B2B"): tags, not prose.
469
+ if (parts.size <= 12 && parts.size < all.length) return "multi-select";
470
+ }
471
+ if (vals.length >= 4 && distinct.size <= Math.min(12, Math.ceil(vals.length / 2)) && vals.every((v) => v.length <= 40)) return "select";
472
+ return "text";
473
+ }
@@ -0,0 +1,173 @@
1
+ # Stat cards
2
+
3
+ The few numbers at the top of a dashboard, each with its name and what
4
+ changed. Four shapes over one list of items: a bordered `strip`; `cards`,
5
+ one per number, with a change in the corner or after the value, a "from"
6
+ value, a status dot and a goal meter, a sparkline, a "View more" footer;
7
+ `usage`, one tile per amount against its limit, as a bar or a ring, a
8
+ split by kind with what is free, a warning line near the cap, an action
9
+ along the foot and a limit the founder can change; and a plain `list` of
10
+ values with their change. Loading, empty and error states are built in.
11
+
12
+ ```jsx
13
+ import { StatCards } from "../components/blocks/stat-cards"; // a Makersclaw app ("./blocks/stat-cards" from components/)
14
+ import { StatCards } from "@/components/makersclaw/blocks/stat-cards"; // a shadcn project
15
+ ```
16
+
17
+ It is a client component: render it from a `"use client"` component, and
18
+ load the numbers in the server surface (README §4). A sparkline is drawn
19
+ by the free `chart` block, so `makersclaw add stat-cards` lays that one
20
+ too.
21
+
22
+ ## The data
23
+
24
+ One item per number, already formatted where it is shown as text:
25
+
26
+ ```js
27
+ {
28
+ id: "mrr", // key; default the label
29
+ label: "Monthly recurring revenue",
30
+ value: "$48.6K", // a string you formatted, or a number (gets thousands separators)
31
+ unit: "ms", // after the value, smaller and muted
32
+ sublabel: "ORBT", // after the label, muted: a ticker, a region code
33
+ delta: 5.2, // a percent; or Delta's props: { value, suffix, display, label, invert }
34
+ invert: true, // lower is better (churn, cost): down turns green
35
+ previous: "$46.2K", // shown after the value as "from $46.2K"
36
+ caption: "Paid plans only", // a muted line under the value
37
+ status: "warning", // a dot and a word at the top right: success · warning · danger · info,
38
+ // or { tone, label: "Needs attention" }
39
+ goal: { value: 3, max: 5, label: "Goals achieved" }, // a thin meter in the status tone
40
+ trend: [12, 14, 13, 17], // a sparkline under the value (numbers, oldest first)
41
+ trendLabel: "MRR, last 16 weeks", // what a screen reader hears for it
42
+ trendTone: "chart-2", // default: green when the change is good, red when bad
43
+ href: "/revenue", // a "View more →" footer link; or onSelect(item) for a button
44
+ linkLabel: "View region",
45
+ }
46
+ ```
47
+
48
+ In `usage`, an item is an amount against a limit:
49
+
50
+ ```js
51
+ {
52
+ id: "egress",
53
+ label: "Egress",
54
+ used: 188, // a number, in the limit's unit
55
+ limit: 200, // the cap; null means none ("/ Unlimited"); left out, no limit is shown
56
+ unit: "GB",
57
+ value: "188 GB", // how `used` reads; default the number and the unit
58
+ limitText: "$150 budget", // how the limit reads; default the number and the unit
59
+ suffix: "this month", // after the value when there is no limit
60
+ format: (n) => `$${n}`, // every number shown, default thousands separators
61
+ parts: [{ label: "Writes", value: 7410000 }, { label: "Reads", value: 2410000 }],
62
+ // a split bar with a legend in place of the meter; with a
63
+ // limit, the rest shows as "Free" (restLabel)
64
+ note: "Past 200 GB is billed per GB.", // a line under the bar: warning near the cap
65
+ noteTone: "muted", // override: muted · success · warning · danger · info
66
+ delta: 9.5, // a badge beside the label
67
+ thresholds: [{ at: 75, tone: "warning" }], // default: warning at warnAt
68
+ action: { label: "Upgrade", icon: "zap", onSelect: (item) => … }, // a button along the foot
69
+ editable: false, // no "Change limit" button on this tile
70
+ limitLabel: "Change budget", limitPrefix: "$", // this tile's limit button and field
71
+ meter: "ring", // this tile's shape, over the block's `meter`
72
+ }
73
+ ```
74
+
75
+ Over the limit the bar stays full and turns red; a reader hears "over by".
76
+
77
+ ## Props
78
+
79
+ - `items` — the numbers, as above. Required.
80
+ - `variant` — `"strip"` (default) · `"cards"` · `"usage"` · `"list"`.
81
+ - `columns` — how many across from `lg` up, 1 to 4; default the number of
82
+ items, at most 4. Cards and usage tiles are one column on a phone and
83
+ two from `sm`; a strip is two on a phone.
84
+ - `title` · `description` · `actions` — a header over the numbers
85
+ ("Plan usage", "You are on the Team plan. Compare plans", a button).
86
+ - `label` — the group's name for a screen reader when there is no title.
87
+
88
+ Stats (`strip`, `cards`, `list`)
89
+
90
+ - `deltaPlacement` — `"inline"` (after the value) or `"corner"` (at the
91
+ label's right). Default corner in a strip, inline elsewhere. A tile with
92
+ a `status` keeps its change inline.
93
+ - `deltaVariant` — `"text"` or `"badge"` (a tinted pill). Default badge
94
+ in a list, text elsewhere.
95
+ - `size` — the value's size: `"sm"` · `"md"` (default in a strip) · `"lg"`
96
+ (default in cards).
97
+ - `linkLabel` — the footer link's words, default `"View more"`.
98
+
99
+ Usage
100
+
101
+ - `meter` — `"bar"` (default) or `"ring"`, for every tile without its own
102
+ `meter`. A tile with `parts` is always a split bar.
103
+ - `format(n)` — every number shown, unless a tile has its own.
104
+ - `warnAt` — the percent of the limit where the bar and the note turn
105
+ warning, default 90.
106
+ - `onLimitChange(item, limit)` — adds a "Change limit" button to each tile
107
+ (not those with `editable: false`). It opens a dialog with the limit in
108
+ a number field; Save calls this with the new number. Return (or resolve
109
+ to) `{ ok: false, error }`, or throw, and the dialog stays open and says
110
+ why; anything else closes it. The block does not change the number on
111
+ screen: pass the new `limit` back in `items`.
112
+ - `limitLabel` — the button's and dialog's words, default `"Change limit"`.
113
+ - `limitHint` — the dialog's description, default the current limit and
114
+ what is used.
115
+
116
+ States
117
+
118
+ - `loading` — skeleton tiles in the shape of the variant; `loadingCount`
119
+ of them (default 4).
120
+ - `error` — a danger notice above the numbers (they still show, if any);
121
+ `onRetry` adds a Retry button to it.
122
+ - `emptyTitle` · `emptyBody` · `emptyAction` — when `items` is empty.
123
+ - `className` — layout around it (width, margin). Never colour.
124
+
125
+ ## Example
126
+
127
+ ```jsx
128
+ // components/Overview.jsx — client: the numbers the server read
129
+ "use client";
130
+ import { StatCards } from "./blocks/stat-cards";
131
+ import { updateRow } from "../lib/actions";
132
+
133
+ export default function Overview({ kpis, usage }) {
134
+ return (
135
+ <div className="grid gap-6">
136
+ <StatCards
137
+ items={[
138
+ { id: "mrr", label: "MRR", value: kpis.mrr, delta: kpis.mrrChange, trend: kpis.mrrWeeks, href: "/revenue" },
139
+ { id: "churn", label: "Logo churn", value: kpis.churn, delta: kpis.churnChange, invert: true },
140
+ { id: "trials", label: "Active trials", value: kpis.trials, previous: kpis.trialsLastWeek, delta: kpis.trialsChange },
141
+ ]}
142
+ variant="cards"
143
+ />
144
+ <StatCards
145
+ variant="usage"
146
+ title="Plan usage"
147
+ items={usage.map((u) => ({ id: u.id, label: u.name, used: u.used, limit: u.limit, unit: u.unit }))}
148
+ onLimitChange={(item, limit) => updateRow({ source: "public.quotas", id: item.id, patch: { limit } })}
149
+ limitLabel="Change budget"
150
+ />
151
+ </div>
152
+ );
153
+ }
154
+ ```
155
+
156
+ ## What it will not do
157
+
158
+ - Fetch or compute: numbers come in formatted, changes already worked out.
159
+ - Draw a chart worth reading on its own: that is the `chart` block.
160
+ - Score one subject against thresholds with an overall status, or list
161
+ many meters under a shared summary: those are other blocks.
162
+ - Colour a number for decoration: tones are for status (a change's
163
+ direction, a tile near its cap, a status dot).
164
+
165
+ A tile it cannot be is your own component in `components/`, built from
166
+ the kit's `Stat`, `Meter` and `Distribution` — not an edit to this block.
167
+ Its files are fixed: `npm run check` refuses a change, and `makersclaw add
168
+ stat-cards` restores them.
169
+
170
+ ## Credits
171
+
172
+ Ported from blocks.so's stats (https://github.com/ephraimduncan/blocks,
173
+ MIT): a rewrite on the Makersclaw kit, not their code.
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "stat-cards",
3
+ "title": "Stat cards",
4
+ "description": "A row of key numbers, each with its name and its change: one bordered strip, or a card each with a change in the corner or after the value, a \"from\" value, a status dot and a goal meter, a sparkline, a \"View more\" footer; or a usage tile per amount against its limit (a bar or a ring, a split by kind with what is free, a warning line near the cap, an action and a limit the founder can change); or a plain list of values with their change. Loading, empty and error states.",
5
+ "useWhen": "The few numbers at the top of a dashboard or a report: revenue, users, conversion, spend against budget, seats and storage against a plan. Not for a trend worth reading on its own (a chart), many rows of records (a table), or one subject scored against thresholds (a scorecard).",
6
+ "tags": ["stats", "kpi", "metrics", "numbers", "dashboard", "overview", "delta", "change", "trend", "sparkline", "usage", "limit", "quota", "plan", "budget", "goal", "status", "ring", "meter"],
7
+ "exports": ["StatCards"],
8
+ "files": ["index.js", "stat-cards.jsx", "usage.jsx"],
9
+ "requires": ["chart"]
10
+ }