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,302 @@
1
+ "use client";
2
+
3
+ import { useEffect, useState } from "react";
4
+ import { Badge, Button, Confirm, Empty, Icon, Notice, Skeleton, Tooltip, cx, todayString } from "../../ui";
5
+
6
+ // The payment methods a customer has saved, as cards: the brand, the last
7
+ // four digits, the expiry and which one is the default. Make default and
8
+ // Remove (behind a Confirm) go out through callbacks that return { ok }.
9
+ // Adding one is the app's: `onAdd` opens the payment provider (Paddle),
10
+ // which collects the card. The block never holds a card number.
11
+
12
+ // Server and browser may differ in locale, time zone and even the day:
13
+ // the first render uses fixed ones (en-US, UTC) so it matches the server's
14
+ // HTML, then the reader's own take over.
15
+ function useHydrated() {
16
+ const [hydrated, setHydrated] = useState(false);
17
+ useEffect(() => setHydrated(true), []);
18
+ return hydrated;
19
+ }
20
+
21
+ const BRANDS = {
22
+ visa: { name: "Visa", icon: "credit-card" },
23
+ mastercard: { name: "Mastercard", icon: "credit-card" },
24
+ amex: { name: "American Express", icon: "credit-card" },
25
+ discover: { name: "Discover", icon: "credit-card" },
26
+ jcb: { name: "JCB", icon: "credit-card" },
27
+ unionpay: { name: "UnionPay", icon: "credit-card" },
28
+ maestro: { name: "Maestro", icon: "credit-card" },
29
+ card: { name: "Card", icon: "credit-card" },
30
+ paypal: { name: "PayPal", icon: "credit-card" },
31
+ apple_pay: { name: "Apple Pay", icon: "apple" },
32
+ google_pay: { name: "Google Pay", icon: "google" },
33
+ bank: { name: "Bank account", icon: "building" },
34
+ };
35
+
36
+ function brandOf(method) {
37
+ const key = String(method.brand ?? "card").toLowerCase().replace(/[\s-]+/g, "_");
38
+ const known = BRANDS[key];
39
+ return known ?? { name: method.brand || "Card", icon: "credit-card" };
40
+ }
41
+
42
+ // The name a method is called by in buttons and dialogs: "Visa ending 4242".
43
+ function nameOf(method) {
44
+ if (method.label) return method.label;
45
+ const brand = brandOf(method).name;
46
+ return method.last4 ? `${brand} ending ${method.last4}` : brand;
47
+ }
48
+
49
+ // "expired" · "soon" (this month or next) · "ok" · null (no expiry).
50
+ function expiryState(method, today) {
51
+ const m = Number(method.expMonth);
52
+ let y = Number(method.expYear);
53
+ if (!m || !y) return null;
54
+ if (y < 100) y += 2000;
55
+ const [ty, tm] = today.split("-").map(Number);
56
+ const months = (y - ty) * 12 + (m - tm);
57
+ if (months < 0) return "expired";
58
+ if (months <= 1) return "soon";
59
+ return "ok";
60
+ }
61
+
62
+ const expiryText = (method) => {
63
+ const m = String(method.expMonth).padStart(2, "0");
64
+ const y = String(method.expYear).slice(-2);
65
+ return `${m}/${y}`;
66
+ };
67
+
68
+ async function attempt(fn) {
69
+ try {
70
+ const res = await fn();
71
+ return res && res.ok === false ? res.error || "That did not save." : null;
72
+ } catch (e) {
73
+ return e?.message || "That did not save.";
74
+ }
75
+ }
76
+
77
+ function MethodCard({ method, today, busy, error, onMakeDefault, onAskRemove, removable, lockedReason }) {
78
+ const brand = brandOf(method);
79
+ const expiry = expiryState(method, today);
80
+ const name = nameOf(method);
81
+ return (
82
+ <li
83
+ className={cx(
84
+ "relative grid gap-4 rounded-lg border bg-card p-4 text-card-foreground",
85
+ method.isDefault ? "border-primary/60" : "border-border",
86
+ )}
87
+ >
88
+ <div className="flex items-start gap-3">
89
+ <span className="grid h-11 w-14 shrink-0 place-items-center rounded-md border border-border bg-muted text-foreground">
90
+ <Icon name={brand.icon} size={22} />
91
+ </span>
92
+ <div className="grid min-w-0 flex-1 gap-0.5">
93
+ <p className="truncate font-medium">
94
+ {method.last4 ? (
95
+ <>
96
+ <span aria-hidden="true" className="tracking-widest text-muted-foreground">
97
+ ••••{" "}
98
+ </span>
99
+ <span className="sr-only">{brand.name} ending </span>
100
+ <span className="tabular-nums">{method.last4}</span>
101
+ </>
102
+ ) : (
103
+ method.label || brand.name
104
+ )}
105
+ </p>
106
+ <p className="flex flex-wrap items-center gap-x-2 gap-y-1 text-sm text-muted-foreground">
107
+ <span>{method.last4 ? brand.name : method.holder || method.email || ""}</span>
108
+ {method.expMonth && method.expYear ? (
109
+ <span className={cx(expiry === "expired" && "text-destructive")}>
110
+ {expiry === "expired" ? "Expired" : "Expires"} {expiryText(method)}
111
+ </span>
112
+ ) : null}
113
+ </p>
114
+ {method.last4 && method.holder ? <p className="truncate text-xs text-muted-foreground">{method.holder}</p> : null}
115
+ </div>
116
+ <div className="flex shrink-0 flex-col items-end gap-1">
117
+ {method.isDefault ? (
118
+ <Badge tone="primary" variant="soft">
119
+ Default
120
+ </Badge>
121
+ ) : null}
122
+ {expiry === "expired" ? (
123
+ <Badge tone="danger" variant="soft">
124
+ Expired
125
+ </Badge>
126
+ ) : expiry === "soon" ? (
127
+ <Badge tone="warning" variant="soft">
128
+ Expires soon
129
+ </Badge>
130
+ ) : null}
131
+ </div>
132
+ </div>
133
+
134
+ {onMakeDefault || onAskRemove ? (
135
+ <div className="flex flex-wrap items-center gap-2 border-t border-border pt-3">
136
+ {onMakeDefault && !method.isDefault ? (
137
+ <Button
138
+ size="sm"
139
+ variant="quiet"
140
+ icon="check"
141
+ onClick={() => onMakeDefault(method)}
142
+ loading={busy === "default"}
143
+ disabled={Boolean(busy) || expiry === "expired"}
144
+ aria-label={`Make ${name} the default`}
145
+ >
146
+ Make default
147
+ </Button>
148
+ ) : null}
149
+ {onAskRemove ? (
150
+ <Tooltip label={removable ? "" : lockedReason} className="ml-auto">
151
+ <Button
152
+ size="sm"
153
+ variant="danger"
154
+ icon="trash"
155
+ onClick={() => onAskRemove(method)}
156
+ disabled={Boolean(busy) || !removable}
157
+ aria-label={`Remove ${name}`}
158
+ className="ml-auto"
159
+ >
160
+ Remove
161
+ </Button>
162
+ </Tooltip>
163
+ ) : null}
164
+ </div>
165
+ ) : null}
166
+
167
+ {error ? (
168
+ <p role="alert" className="text-xs text-destructive">
169
+ {error}
170
+ </p>
171
+ ) : null}
172
+ </li>
173
+ );
174
+ }
175
+
176
+ function Loading() {
177
+ return (
178
+ <ul aria-busy="true" aria-label="Loading payment methods" className="grid gap-3 md:grid-cols-2">
179
+ {[0, 1].map((i) => (
180
+ <li key={i} className="flex gap-3 rounded-lg border border-border p-4">
181
+ <Skeleton shape="rect" className="h-11 w-14" />
182
+ <div className="grid flex-1 content-start gap-2">
183
+ <Skeleton className="w-1/2" />
184
+ <Skeleton className="w-1/3" />
185
+ </div>
186
+ </li>
187
+ ))}
188
+ </ul>
189
+ );
190
+ }
191
+
192
+ export function PaymentMethods({
193
+ methods,
194
+ onAdd,
195
+ onSetDefault,
196
+ onRemove,
197
+ loading = false,
198
+ error,
199
+ title = "Payment methods",
200
+ description = "The cards and wallets saved to your account. The default is charged first.",
201
+ addLabel = "Add payment method",
202
+ emptyTitle = "No saved payment methods",
203
+ emptyBody = "Add one and it is ready at checkout.",
204
+ today,
205
+ className,
206
+ }) {
207
+ const [busy, setBusy] = useState({});
208
+ const [errors, setErrors] = useState({});
209
+ const [removing, setRemoving] = useState(null);
210
+ const [removeBusy, setRemoveBusy] = useState(false);
211
+ const [adding, setAdding] = useState(false);
212
+ const [addError, setAddError] = useState(null);
213
+ const hydrated = useHydrated();
214
+ const now = today ?? (hydrated ? todayString() : new Date().toISOString().slice(0, 10));
215
+ const list = (methods ?? []).filter(Boolean);
216
+ const onlyOne = list.length <= 1;
217
+
218
+ const mark = (id, state) => setBusy((b) => ({ ...b, [id]: state }));
219
+ const fail = (id, message) => setErrors((e) => ({ ...e, [id]: message }));
220
+
221
+ async function makeDefault(method) {
222
+ mark(method.id, "default");
223
+ fail(method.id, null);
224
+ fail(method.id, await attempt(() => onSetDefault(method)));
225
+ mark(method.id, null);
226
+ }
227
+
228
+ async function confirmRemove() {
229
+ const method = removing;
230
+ if (!method) return;
231
+ setRemoveBusy(true);
232
+ fail(method.id, null);
233
+ const failed = await attempt(() => onRemove(method));
234
+ setRemoveBusy(false);
235
+ setRemoving(null);
236
+ if (failed) fail(method.id, failed);
237
+ }
238
+
239
+ async function add() {
240
+ setAdding(true);
241
+ setAddError(null);
242
+ setAddError(await attempt(() => onAdd()));
243
+ setAdding(false);
244
+ }
245
+
246
+ const addButton = onAdd ? (
247
+ <Button variant="primary" icon="plus" onClick={add} loading={adding}>
248
+ {addLabel}
249
+ </Button>
250
+ ) : null;
251
+
252
+ let body;
253
+ if (loading) body = <Loading />;
254
+ else if (!list.length) body = <Empty icon="credit-card" title={emptyTitle} body={emptyBody} action={addButton} />;
255
+ else
256
+ body = (
257
+ <ul aria-label={title} className="grid gap-3 md:grid-cols-2">
258
+ {list.map((method) => (
259
+ <MethodCard
260
+ key={method.id}
261
+ method={method}
262
+ today={now}
263
+ busy={busy[method.id]}
264
+ error={errors[method.id]}
265
+ onMakeDefault={onSetDefault ? makeDefault : null}
266
+ onAskRemove={onRemove ? setRemoving : null}
267
+ removable={!method.isDefault || onlyOne}
268
+ lockedReason="Make another method the default before removing this one"
269
+ />
270
+ ))}
271
+ </ul>
272
+ );
273
+
274
+ return (
275
+ <section className={cx("grid gap-5", className)}>
276
+ <header className="flex flex-wrap items-start justify-between gap-3">
277
+ <div className="min-w-0 max-w-prose">
278
+ <h2 className="text-xl font-semibold">{title}</h2>
279
+ {description ? <p className="mt-1 text-sm text-muted-foreground">{description}</p> : null}
280
+ </div>
281
+ {!loading && list.length ? <div className="w-full sm:w-auto [&>button]:w-full">{addButton}</div> : null}
282
+ </header>
283
+ {error ? <Notice tone="danger">{error}</Notice> : null}
284
+ {addError ? (
285
+ <Notice tone="danger" onDismiss={() => setAddError(null)}>
286
+ {addError}
287
+ </Notice>
288
+ ) : null}
289
+ {body}
290
+ <Confirm
291
+ open={Boolean(removing)}
292
+ onClose={() => (removeBusy ? null : setRemoving(null))}
293
+ onConfirm={confirmRemove}
294
+ tone="danger"
295
+ busy={removeBusy}
296
+ title={removing ? `Remove ${nameOf(removing)}?` : "Remove?"}
297
+ body="It will no longer be offered at checkout. You can add it again later."
298
+ confirmLabel="Remove"
299
+ />
300
+ </section>
301
+ );
302
+ }
@@ -0,0 +1,213 @@
1
+ # PdfDocument
2
+
3
+ Make PDFs in the workspace theme, in the browser. A small set of document
4
+ parts — headings, text, label-over-value fields, tables whose header
5
+ repeats on every page, totals, callouts, badges, rows and columns — and
6
+ a ready quote / estimate / invoice layout built from them. `renderPdf`
7
+ turns a document into a `Blob` to download, preview, upload or attach;
8
+ `usePdf` keeps one fresh while a form changes (a live preview); and
9
+ `PdfDownloadButton` is the "Download PDF" beside it.
10
+
11
+ The look is a clean business document: white paper, near-black ink,
12
+ grey for secondary text, hairline rules, one accent — the theme's
13
+ primary — on a bar along the top edge and on the amount that matters.
14
+ Type is one face at a few sizes on a 4pt rhythm, sentence case, figures
15
+ aligned in their columns. It prints well in black and white.
16
+
17
+ It is [@react-pdf/renderer](https://github.com/diegomura/react-pdf)
18
+ (MIT) underneath: it lays the document out in JavaScript and writes the
19
+ PDF, no browser print dialog and no server. The blocks that print
20
+ (quote-builder, proposal-accept…) import this one, never
21
+ `@react-pdf/renderer`.
22
+
23
+ ```jsx
24
+ import { QuoteDocument, renderPdf, PdfDownloadButton } from "../components/blocks/pdf-document"; // a Makersclaw app ("./blocks/pdf-document" from components/)
25
+ import { QuoteDocument, renderPdf, PdfDownloadButton } from "@/components/makersclaw/blocks/pdf-document"; // a shadcn project
26
+ ```
27
+
28
+ Render from a `"use client"` component. `quoteTotals`, `formatMoney`,
29
+ `formatQuantity`, `formatPdfDate`, `buildPdfTheme` are plain functions
30
+ and work on the server (use the same sums on the screen and in the PDF).
31
+
32
+ ## Colours: why the theme is read at render time
33
+
34
+ A PDF cannot read CSS variables — every colour in it is a value written
35
+ into the file. So `renderPdf` reads the workspace theme from the page at
36
+ the moment it makes the PDF (a hidden probe resolves `--color-primary`
37
+ and the other tokens to plain colours) and passes them in. Change the
38
+ theme and the next PDF follows; a PDF already made keeps its colours.
39
+
40
+ Two schemes:
41
+
42
+ - `paper` (the default) — what gets printed, emailed or signed: white
43
+ paper and dark ink whatever the workspace looks like, with the theme's
44
+ primary as the accent. A dark workspace still sends a light invoice.
45
+ If the primary is too pale to read on white (a mint, a lemon), text
46
+ uses a darker shade of it (4.5:1 or more) while bars keep the colour.
47
+ - `theme` — the workspace's own background, ink and borders, for a PDF
48
+ meant to be read on screen in the brand's look.
49
+
50
+ Nothing in a document names a colour: parts take a `tone` (`default` ·
51
+ `muted` · `accent` · `success` · `warning` · `danger` · `info`).
52
+
53
+ Fonts: Helvetica, which every PDF reader has built in, so a PDF is small
54
+ and nothing is fetched. It draws Latin text; for other scripts, or
55
+ symbols like ₹, register a font that has them (`registerPdfFont`).
56
+
57
+ ## QuoteDocument
58
+
59
+ A quote, an estimate or an invoice from data. The sender and the
60
+ document's name at the top, then who it is for, the dates and the amount
61
+ due; the lines; the sums; notes, terms and how to pay.
62
+
63
+ ```jsx
64
+ <QuoteDocument
65
+ kind="invoice" // quote (default) · estimate · invoice
66
+ number="INV-2026-041"
67
+ issued="2026-09-23" // "YYYY-MM-DD" or a Date
68
+ until="2026-10-07" // valid until (quote) · due (invoice)
69
+ from={{ name: "Northwind Studio", lines: ["18 Harbour Street", "Bristol BS1 4QA"], email: "billing@northwind.studio", logo: "/logo.png" }}
70
+ to={{ name: "Priya Raman", company: "Juniper Health", lines: ["400 Market St"], email: "priya@juniper.health" }}
71
+ items={[{ description: "Design system", detail: "32 components", quantity: 1, unit: "", unitPrice: 9600, discountPercent: 0, taxable: true }]}
72
+ currency="GBP" locale="en-GB"
73
+ taxRate={20} taxLabel="VAT (20%)"
74
+ discount={{ percent: 5 }} // or { amount: 250, label: "Loyalty" }
75
+ paid={3000} // a deposit: prints Total, Paid, Balance due
76
+ status="Deposit received" statusTone="success" // an outlined badge under the number
77
+ notes="Thanks for the conversation…" // a tinted callout
78
+ terms="Payment is due within 14 days."
79
+ payment={["Northwind Studio Ltd", "Sort code 00-00-00"]} // "How to pay" (lines or a string)
80
+ />
81
+ ```
82
+
83
+ Every prop but the data you have is optional: `title` overrides the
84
+ document's name, `reference` adds a "Reference" field (a PO number),
85
+ `footer` replaces the footer line (default: sender, email, website),
86
+ `dateLocale` formats dates (default `en-GB`: 23 Sep 2026), `size` is
87
+ `"A4"` (default) or `"LETTER"`. Amounts are in the currency's main unit
88
+ (pounds, not pence); `quoteTotals` does the sums — each line rounded,
89
+ the discount before tax, tax only on `taxable` lines.
90
+
91
+ ## The parts
92
+
93
+ Compose your own document from these; they only render inside a
94
+ `PdfDocument`, passed to `renderPdf` (never into the page).
95
+
96
+ - `PdfDocument` — `title`, `author`, `subject` (the file's properties),
97
+ `size` (`"A4"` · `"LETTER"` · `"LEGAL"` · `[w, h]` in points),
98
+ `landscape`, `margin` (`compact` 36pt · `normal` 48pt · `roomy` 64pt),
99
+ `accentBar` (default `true`), `footer` (a line at the foot of every
100
+ page, with "Page 2 of 3" at the right when there is more than one;
101
+ `null` for none). Content flows onto new pages by itself.
102
+ - `PdfStack` — a column with `gap` (pt, default 8); `break` starts it on
103
+ a new page; `wrap={false}` keeps it on one page.
104
+ - `PdfRow` — a row: `gap` (default 16), `justify` (`start` · `between` ·
105
+ `end` · `center`), `align` (`start` · `center` · `end` · `baseline`).
106
+ `PdfCol` — one column of it: `flex` (share, default 1; `0` = as wide as
107
+ its content) or `width` (pt).
108
+ - `PdfHeading` — `level` 1 (24pt, the document's name) · 2 (12pt, a
109
+ section) · 3 (bold body size); `tone`, `align`.
110
+ - `PdfText` — `size` (`xs` · `sm` · `md` · `lg` · `xl` · `xxl`), `tone`,
111
+ `bold`, `align`.
112
+ - `PdfLabel` — the small muted line above a value. `PdfField` — a label
113
+ over a value (`strong` sets the value large and bold).
114
+ - `PdfLines` — lines of an address: the first bold, the rest muted
115
+ (`first={false}`, `muted={false}` to change).
116
+ - `PdfTable` — `columns` `[{ key, label, flex? | width?, align?,
117
+ render?(row, i) }]`, `rows` (a row's `detail` prints as a muted second
118
+ line under the first column), `empty`. The header repeats on each page;
119
+ a row never splits.
120
+ - `PdfTotals` — `rows` `[{ label, value, strong?, tone? }]` flush right;
121
+ the `strong` row is larger, over a rule. `width` (default 220pt).
122
+ - `PdfCallout` — a tinted box with an accent edge (`tone="neutral"` for
123
+ grey), optional `title`.
124
+ - `PdfBadge` — an outlined pill: `tone` `accent` · `success` · `warning`
125
+ · `danger` · `neutral`.
126
+ - `PdfDivider` — a hairline; `space` above and below (pt), `strong` for
127
+ an ink rule.
128
+ - `PdfImage` (`src`: a URL the browser can fetch, or a data URL),
129
+ `PdfLink` (`src`), `PdfView`, `PdfRawText` — the library's own parts,
130
+ for anything else; style them with `usePdfTheme()` values.
131
+ - `usePdfTheme()` — inside a part: `{ paper, ink, muted, rule, band,
132
+ accent, accentInk, accentSoft, onAccent, success, warning, danger,
133
+ info, font, fontBold }`. `PDF_TYPE` is the size scale in points.
134
+
135
+ ## Functions
136
+
137
+ - `renderPdf(doc, { theme?, root?, scheme? })` → `Promise<Blob>`
138
+ (`application/pdf`). `theme` from `resolvePdfTheme(root, { scheme })`
139
+ or `buildPdfTheme(slots, { scheme, font })`; by default it is read from
140
+ `<body>` (or `root`) now. Rejects when the document cannot be made (an
141
+ image that will not load, a font that will not fetch).
142
+ - `usePdf(build, deps, { delay = 400, enabled = true, ...renderOptions })`
143
+ → `{ blob, url, loading, error, refresh }`. Re-renders `delay` ms after
144
+ `deps` settle; `url` is an object URL, revoked when replaced.
145
+ - `PdfDownloadButton` — `document` (an element, or a function returning
146
+ one, called on press), `fileName`, `label` ("Download PDF"), `icon`,
147
+ `variant`, `size`, `disabled`, `options` (for renderPdf). Busy while it
148
+ renders; a failure shows a toast and the button is ready again.
149
+ - `downloadBlob(blob, fileName)` — save any Blob as a file.
150
+ - `registerPdfFont({ family, regular, bold? })` → `{ family, bold }` —
151
+ a TTF/WOFF by URL; pass the result as `font` to `buildPdfTheme`.
152
+ - `quoteTotals({ items, discount?, taxRate?, paid? })` → `{ lines,
153
+ subtotal, discount, tax, total, paid, due }`; `lineAmount(item)`;
154
+ `round2(n)`.
155
+ - `formatMoney(n, currency = "USD", locale = "en-US")` ("£1,234.50"),
156
+ `formatQuantity(q, locale)` ("1.5"), `formatPdfDate(value, locale =
157
+ "en-GB")` ("23 Sep 2026") — in characters the PDF's face can draw.
158
+
159
+ ## Example
160
+
161
+ A quote screen: the form, a live preview in the `pdf-viewer` block, and
162
+ the download.
163
+
164
+ ```jsx
165
+ "use client";
166
+ import { PdfViewer } from "../components/blocks/pdf-viewer";
167
+ import { PdfDownloadButton, QuoteDocument, usePdf } from "../components/blocks/pdf-document";
168
+
169
+ export function QuotePreview({ quote }) {
170
+ const doc = () => <QuoteDocument {...quote} />;
171
+ const { blob, error } = usePdf(doc, [quote]);
172
+ return (
173
+ <div className="grid gap-3">
174
+ <div className="flex justify-end">
175
+ <PdfDownloadButton variant="primary" document={doc} fileName={`${quote.number}.pdf`} />
176
+ </div>
177
+ <PdfViewer file={blob} loading={!blob && !error} title={`Quote ${quote.number}`} thumbnails={false} />
178
+ </div>
179
+ );
180
+ }
181
+ ```
182
+
183
+ Your own document:
184
+
185
+ ```jsx
186
+ const blob = await renderPdf(
187
+ <PdfDocument title="Statement" footer="Juniper Health · Statement for August">
188
+ <PdfHeading level={1}>Statement</PdfHeading>
189
+ <PdfText tone="muted">1–31 August 2026</PdfText>
190
+ <PdfDivider />
191
+ <PdfTable columns={[{ key: "date", label: "Date", width: 80 }, { key: "what", label: "Description", flex: 3 }, { key: "amount", label: "Amount", width: 90, align: "right" }]} rows={rows} />
192
+ <PdfTotals rows={[{ label: "Closing balance", value: formatMoney(balance), strong: true }]} />
193
+ </PdfDocument>,
194
+ );
195
+ ```
196
+
197
+ ## What it will not do
198
+
199
+ - Render the parts into the page: they are PDF parts, not HTML. Preview
200
+ a PDF with the `pdf-viewer` block.
201
+ - Edit or fill an existing PDF, or sign one (see `signature-pad`).
202
+ - Use the workspace's web font on its own: register one with
203
+ `registerPdfFont`, or the PDF uses Helvetica.
204
+ - Make PDFs on the server from this block (it is a client block that
205
+ reads the theme from the page); `quoteTotals` and the formatters do run
206
+ there.
207
+ - Write right-to-left text or hyphenate: words are never split at a line
208
+ end.
209
+
210
+ ## Credits
211
+
212
+ Built on [@react-pdf/renderer](https://github.com/diegomura/react-pdf) by
213
+ Diego Muracciole (MIT), used unmodified. See THIRD-PARTY-NOTICES.md.
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "pdf-document",
3
+ "title": "PdfDocument",
4
+ "description": "Make PDFs in the workspace theme (@react-pdf/renderer): a small set of document primitives (headings, text, label/value fields, tables with repeating headers, totals, callouts, badges, rows and columns) and a clean ready-made quote / estimate / invoice layout, rendered in the browser to a Blob with renderPdf(doc). The theme's colours are read from the page when the PDF is made (a PDF cannot read CSS variables): white paper, near-black ink and the theme's primary as the one accent. Also a live-preview hook (usePdf), a Download PDF button, and the quote sums and money formatting as plain functions.",
5
+ "useWhen": "A screen produces a document someone downloads, prints, emails or signs: a quote, an invoice, an estimate, a receipt, a statement, a certificate, a one-page report. The quote-builder and proposal blocks print through it. Not for showing an existing PDF on screen (pdf-viewer) or for a web page people read in the app (doc-page).",
6
+ "tags": ["pdf", "document", "invoice", "quote", "estimate", "receipt", "print", "download", "export", "react-pdf", "billing", "proposal", "statement"],
7
+ "exports": ["renderPdf", "usePdf", "PdfDownloadButton", "downloadBlob", "registerPdfFont", "PdfDocument", "PdfStack", "PdfRow", "PdfCol", "PdfText", "PdfHeading", "PdfLabel", "PdfField", "PdfDivider", "PdfCallout", "PdfBadge", "PdfTable", "PdfTotals", "PdfLines", "PdfView", "PdfRawText", "PdfImage", "PdfLink", "PdfThemeProvider", "usePdfTheme", "PDF_TYPE", "QuoteDocument", "quoteTotals", "lineAmount", "round2", "formatMoney", "formatQuantity", "formatPdfDate", "resolvePdfTheme", "buildPdfTheme", "DEFAULT_PDF_THEME"],
8
+ "files": ["index.js", "pdf-document.jsx", "primitives.jsx", "quote.jsx", "money.js", "theme.js"],
9
+ "requires": [],
10
+ "dependencies": {
11
+ "@react-pdf/renderer": "4.9.0"
12
+ }
13
+ }
@@ -0,0 +1,136 @@
1
+ "use client";
2
+
3
+ // PdfDocument — the gallery's and the smoke test's surface. Not installed
4
+ // into apps. A small quote editor: the fields on the left, the PDF on the
5
+ // right, made in the browser from QuoteDocument as you type (usePdf) and
6
+ // shown in the pdf-viewer block, with a Download PDF button. Switch the
7
+ // kind to Invoice and the same data prints as an invoice with a deposit.
8
+ // The colours are the workspace theme's, read when the PDF is made.
9
+ import { useState } from "react";
10
+ import { Button, Card, Field, IconButton, Input, Notice, Textarea, ToggleGroup } from "../components/ui";
11
+ import { PdfViewer } from "../components/blocks/pdf-viewer";
12
+ import { PdfDownloadButton, QuoteDocument, formatMoney, quoteTotals, usePdf } from "../components/blocks/pdf-document";
13
+
14
+ const STUDIO = {
15
+ name: "Northwind Studio",
16
+ lines: ["18 Harbour Street", "Bristol BS1 4QA, United Kingdom"],
17
+ email: "billing@northwind.studio",
18
+ website: "northwind.studio",
19
+ };
20
+
21
+ const START_ITEMS = [
22
+ { id: "l1", description: "Discovery workshop", detail: "Two half-day sessions with your team", quantity: 2, unit: "days", unitPrice: 1400 },
23
+ { id: "l2", description: "Design system", detail: "Tokens, 32 components, documentation", quantity: 1, unitPrice: 9600 },
24
+ { id: "l3", description: "Landing page build", quantity: 3, unitPrice: 1850 },
25
+ ];
26
+
27
+ let nextId = 4;
28
+
29
+ export default function PdfDocumentDemo() {
30
+ const [kind, setKind] = useState("quote");
31
+ const [client, setClient] = useState({ name: "Priya Raman", company: "Juniper Health", email: "priya@juniper.health" });
32
+ const [items, setItems] = useState(START_ITEMS);
33
+ const [taxRate, setTaxRate] = useState("20");
34
+ const [notes, setNotes] = useState("Thank you for the conversation last week. This quote covers the first phase; we will scope the build together once discovery is done.");
35
+
36
+ const invoice = kind === "invoice";
37
+ const doc = {
38
+ kind,
39
+ number: invoice ? "INV-2026-041" : "Q-2026-118",
40
+ issued: "2026-09-23",
41
+ until: invoice ? "2026-10-07" : "2026-10-23",
42
+ from: STUDIO,
43
+ to: { name: client.name, company: client.company, email: client.email },
44
+ items,
45
+ currency: "GBP",
46
+ locale: "en-GB",
47
+ taxRate: Number(taxRate) || 0,
48
+ taxLabel: `VAT (${Number(taxRate) || 0}%)`,
49
+ paid: invoice ? 3000 : 0,
50
+ status: invoice ? "Deposit received" : null,
51
+ statusTone: "success",
52
+ notes,
53
+ terms: invoice ? "Payment is due within 14 days." : "This quote is valid for 30 days. Work starts on a signed quote and a 30% deposit.",
54
+ payment: invoice ? ["Northwind Studio Ltd", "Sort code 00-00-00", "Account 00000000"] : null,
55
+ };
56
+ const pdf = usePdf(() => <QuoteDocument {...doc} />, [JSON.stringify(doc)], { delay: 500 });
57
+ const sums = quoteTotals({ items, taxRate: doc.taxRate, paid: doc.paid });
58
+
59
+ const setItem = (id, patch) => setItems((list) => list.map((it) => (it.id === id ? { ...it, ...patch } : it)));
60
+
61
+ return (
62
+ <div className="grid gap-4 lg:grid-cols-[minmax(0,400px)_minmax(0,1fr)] lg:items-start">
63
+ <Card title="Quote details" padding="md">
64
+ <div className="grid gap-4">
65
+ <ToggleGroup
66
+ aria-label="Kind of document"
67
+ className="w-full"
68
+ value={kind}
69
+ onChange={setKind}
70
+ items={[
71
+ { value: "quote", label: "Quote" },
72
+ { value: "invoice", label: "Invoice" },
73
+ ]}
74
+ />
75
+ <div className="grid gap-3 sm:grid-cols-2">
76
+ <Field id="q-name" label="Client">
77
+ <Input id="q-name" value={client.name} onChange={(e) => setClient({ ...client, name: e.target.value })} />
78
+ </Field>
79
+ <Field id="q-company" label="Company">
80
+ <Input id="q-company" value={client.company} onChange={(e) => setClient({ ...client, company: e.target.value })} />
81
+ </Field>
82
+ </div>
83
+
84
+ <div className="grid gap-2">
85
+ <p className="text-sm font-medium">Line items</p>
86
+ <ul className="grid gap-2">
87
+ {items.map((it) => (
88
+ <li key={it.id} className="grid grid-cols-[minmax(0,1fr)_4rem_6rem_auto] items-center gap-2">
89
+ <Input aria-label="Description" value={it.description} placeholder="What is it?" onChange={(e) => setItem(it.id, { description: e.target.value })} />
90
+ <Input aria-label="Quantity" inputMode="decimal" className="text-right tabular-nums" value={String(it.quantity)} onChange={(e) => setItem(it.id, { quantity: e.target.value })} />
91
+ <Input aria-label="Unit price" inputMode="decimal" className="text-right tabular-nums" value={String(it.unitPrice)} onChange={(e) => setItem(it.id, { unitPrice: e.target.value })} />
92
+ <IconButton icon="trash" label={`Remove ${it.description || "line"}`} onClick={() => setItems((list) => list.filter((x) => x.id !== it.id))} />
93
+ </li>
94
+ ))}
95
+ </ul>
96
+ <div className="flex items-center justify-between gap-2">
97
+ <Button variant="quiet" size="sm" icon="plus" onClick={() => setItems((list) => [...list, { id: `l${nextId++}`, description: "", quantity: 1, unitPrice: 0 }])}>
98
+ Add line
99
+ </Button>
100
+ <p className="text-sm text-muted-foreground tabular-nums">
101
+ Subtotal <span className="font-medium text-foreground">{formatMoney(sums.subtotal, "GBP", "en-GB")}</span>
102
+ </p>
103
+ </div>
104
+ </div>
105
+
106
+ <Field id="q-tax" label="VAT rate" hint="Percent, on every line.">
107
+ <Input id="q-tax" inputMode="decimal" className="tabular-nums" value={taxRate} onChange={(e) => setTaxRate(e.target.value)} />
108
+ </Field>
109
+ <Field id="q-notes" label="Note to the client">
110
+ <Textarea id="q-notes" autoGrow minRows={2} maxRows={6} value={notes} onChange={(e) => setNotes(e.target.value)} />
111
+ </Field>
112
+ </div>
113
+ </Card>
114
+
115
+ <div className="grid min-w-0 gap-3">
116
+ <div className="flex flex-wrap items-center justify-between gap-3">
117
+ <div className="min-w-0">
118
+ <p className="text-sm font-medium">
119
+ {invoice ? "Invoice" : "Quote"} {doc.number}
120
+ </p>
121
+ <p className="text-sm text-muted-foreground tabular-nums">
122
+ {invoice ? "Balance due" : "Total"} {formatMoney(sums.due, "GBP", "en-GB")} · {pdf.loading ? "Updating preview…" : "Preview is up to date"}
123
+ </p>
124
+ </div>
125
+ <PdfDownloadButton variant="primary" document={() => <QuoteDocument {...doc} />} fileName={`${doc.number}.pdf`} />
126
+ </div>
127
+ {pdf.error ? (
128
+ <Notice tone="danger" title="The preview couldn't be made">
129
+ {String(pdf.error.message ?? pdf.error)}
130
+ </Notice>
131
+ ) : null}
132
+ <PdfViewer file={pdf.blob} loading={!pdf.blob} title={`${invoice ? "Invoice" : "Quote"} ${doc.number}`} fileName={`${doc.number}.pdf`} thumbnails={false} height="760px" />
133
+ </div>
134
+ </div>
135
+ );
136
+ }