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,434 @@
1
+ "use client";
2
+
3
+ // SignaturePad — sign by drawing or by typing your name, in the workspace
4
+ // theme (signature_pad underneath for the drawing). A full-name field, a
5
+ // paper box to draw on (undo the last stroke, clear with Undo), or the
6
+ // name set in the theme's display face; then "Sign". What comes out is a
7
+ // PNG (transparent, dark ink, cropped to the signature) and the typed
8
+ // name, with when and how it was made.
9
+ //
10
+ // The pad is paper: whichever of the theme's background and foreground is
11
+ // lighter, inked with the other (colors.js). On a dark theme that is a
12
+ // light sheet — the way a signature looks on a page — and the PNG is what
13
+ // you see.
14
+ //
15
+ // Client only in effect, not in import: the pad is created in an effect.
16
+ // The server (and the first paint) shows the same frame, faded in once
17
+ // the ink is known.
18
+ import { useCallback, useEffect, useId, useImperativeHandle, useRef, useState } from "react";
19
+ import { Button, Field, Icon, IconButton, Input, Notice, ToggleGroup, Tooltip, cx, toast } from "../../ui";
20
+ import { SignaturePadEngine } from "./pad";
21
+ import { paperAndInk, readTokens, watchTheme } from "./colors";
22
+ import { formatSignedAt, missing, pointCount, signatureRecord, strokeBounds, withInk, withoutLast } from "./signature";
23
+
24
+ /** A ref for the pad's handle: `const pad = useSignaturePad()`, then
25
+ * `<SignaturePad padRef={pad} />` and `await pad.current?.toPNG()`. */
26
+ export function useSignaturePad() {
27
+ return useRef(null);
28
+ }
29
+
30
+ const MODES = [
31
+ { value: "draw", label: "Draw", icon: "pencil" },
32
+ { value: "type", label: "Type", icon: "type" },
33
+ ];
34
+
35
+ // Class names written out whole (Tailwind reads them literally), per paper.
36
+ const PAPER = {
37
+ background: { box: "bg-background", hint: "text-muted-foreground", line: "bg-border", ink: "text-foreground", mark: "text-muted-foreground" },
38
+ foreground: { box: "bg-foreground", hint: "text-background/60", line: "bg-background/25", ink: "text-background", mark: "text-background/50" },
39
+ };
40
+
41
+ export function SignaturePad({
42
+ signer = "",
43
+ label = "Signature",
44
+ nameLabel = "Full name",
45
+ modes = ["draw", "type"],
46
+ defaultMode,
47
+ requireName = true,
48
+ value,
49
+ onChange,
50
+ onSubmit,
51
+ submitLabel = "Sign",
52
+ note,
53
+ disabled = false,
54
+ height = 176,
55
+ bare = false,
56
+ padRef,
57
+ className,
58
+ }) {
59
+ const id = useId();
60
+ const rootRef = useRef(null);
61
+ const boxRef = useRef(null);
62
+ const canvasRef = useRef(null);
63
+ const typedRef = useRef(null);
64
+ const engine = useRef(null);
65
+ const strokes = useRef([]); // survives the canvas going away (signed view, mode switch)
66
+
67
+ const allowed = MODES.filter((m) => modes.includes(m.value));
68
+ const [mode, setMode] = useState(defaultMode && modes.includes(defaultMode) ? defaultMode : allowed[0]?.value ?? "draw");
69
+ const [name, setName] = useState(signer);
70
+ const nameRef = useRef(name);
71
+ nameRef.current = name;
72
+ const [drawn, setDrawn] = useState(0);
73
+ const [look, setLook] = useState({ paper: "background", ink: "foreground", inkColour: null, known: false });
74
+ const [errors, setErrors] = useState(null);
75
+ const [signed, setSigned] = useState(value?.png ? value : null);
76
+ const [saving, setSaving] = useState(false);
77
+ const [failed, setFailed] = useState(null);
78
+ const [mounted, setMounted] = useState(false);
79
+ useEffect(() => setMounted(true), []);
80
+
81
+ // A new `value` from the parent (a saved signature) shows as signed.
82
+ useEffect(() => {
83
+ if (value?.png) setSigned(value);
84
+ }, [value]);
85
+
86
+ const cbs = useRef({});
87
+ cbs.current = { onChange };
88
+ const tell = useCallback((next) => cbs.current.onChange?.(next), []);
89
+
90
+ // ---- the ink, from the theme ----------------------------------------------
91
+
92
+ useEffect(() => {
93
+ return watchTheme(rootRef.current, () => {
94
+ const next = paperAndInk(readTokens(rootRef.current, ["background", "foreground"]));
95
+ setLook((prev) => (prev.known && prev.inkColour === next.inkColour && prev.paper === next.paper ? prev : { ...next, known: true }));
96
+ });
97
+ }, []);
98
+
99
+ // ---- the drawing engine ---------------------------------------------------
100
+
101
+ const drawing = mode === "draw" && !signed;
102
+
103
+ useEffect(() => {
104
+ const canvas = canvasRef.current;
105
+ if (!drawing || !canvas || !look.inkColour) return;
106
+ const pad = new SignaturePadEngine(canvas, { penColor: look.inkColour, minWidth: 0.8, maxWidth: 2.6, velocityFilterWeight: 0.6 });
107
+ engine.current = pad;
108
+
109
+ // The canvas holds device pixels; strokes are kept in CSS pixels and
110
+ // redrawn at every size change (a phone rotating, a panel resizing).
111
+ const fit = () => {
112
+ const box = boxRef.current?.getBoundingClientRect();
113
+ if (!box || !box.width) return;
114
+ const ratio = Math.max(window.devicePixelRatio || 1, 1);
115
+ canvas.width = Math.round(box.width * ratio);
116
+ canvas.height = Math.round(box.height * ratio);
117
+ canvas.getContext("2d")?.scale(ratio, ratio);
118
+ pad.fromData(withInk(strokes.current, look.inkColour));
119
+ };
120
+ fit();
121
+ const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(fit) : null;
122
+ ro?.observe(boxRef.current);
123
+
124
+ const end = () => {
125
+ strokes.current = pad.toData();
126
+ const n = pointCount(strokes.current);
127
+ setDrawn(n);
128
+ setErrors((e) => (e?.pad ? { ...e, pad: undefined } : e));
129
+ tell({ mode: "draw", name: nameRef.current, empty: n === 0 });
130
+ };
131
+ pad.addEventListener("endStroke", end);
132
+ if (disabled) pad.off();
133
+ return () => {
134
+ pad.removeEventListener("endStroke", end);
135
+ pad.off();
136
+ ro?.disconnect();
137
+ engine.current = null;
138
+ };
139
+ }, [drawing, look.inkColour, disabled, tell]);
140
+
141
+ const undo = useCallback(() => {
142
+ strokes.current = withoutLast(strokes.current);
143
+ engine.current?.fromData(withInk(strokes.current, look.inkColour));
144
+ const n = pointCount(strokes.current);
145
+ setDrawn(n);
146
+ tell({ mode: "draw", name, empty: n === 0 });
147
+ }, [look.inkColour, name, tell]);
148
+
149
+ const clear = useCallback(
150
+ (quiet) => {
151
+ const before = strokes.current;
152
+ if (!before.length) return;
153
+ strokes.current = [];
154
+ engine.current?.clear();
155
+ setDrawn(0);
156
+ tell({ mode: "draw", name, empty: true });
157
+ if (quiet === true) return;
158
+ toast("Signature cleared", {
159
+ action: {
160
+ label: "Undo",
161
+ onClick: () => {
162
+ strokes.current = before;
163
+ engine.current?.fromData(withInk(before, look.inkColour));
164
+ setDrawn(pointCount(before));
165
+ },
166
+ },
167
+ });
168
+ },
169
+ [look.inkColour, name, tell],
170
+ );
171
+
172
+ // ---- the picture ----------------------------------------------------------
173
+
174
+ /** The signature as a PNG data URL (transparent, inked, cropped), or null. */
175
+ const toPNG = useCallback(async () => {
176
+ if (mode === "type") {
177
+ const text = name.trim();
178
+ const el = typedRef.current;
179
+ if (!text || typeof document === "undefined") return null;
180
+ try {
181
+ await document.fonts?.ready;
182
+ } catch {}
183
+ const family = el ? getComputedStyle(el).fontFamily : "serif";
184
+ const size = 64;
185
+ const scale = 2;
186
+ const c = document.createElement("canvas");
187
+ const ctx = c.getContext("2d");
188
+ const font = `italic 400 ${size}px ${family}`;
189
+ ctx.font = font;
190
+ const m = ctx.measureText(text);
191
+ const w = Math.ceil(m.width + size * 0.6);
192
+ const h = Math.ceil(size * 1.5);
193
+ c.width = w * scale;
194
+ c.height = h * scale;
195
+ ctx.scale(scale, scale);
196
+ ctx.font = font;
197
+ ctx.fillStyle = look.inkColour ?? getComputedStyle(el ?? document.body).color;
198
+ ctx.textBaseline = "alphabetic";
199
+ ctx.fillText(text, size * 0.3, size * 1.05);
200
+ return c.toDataURL("image/png");
201
+ }
202
+ const canvas = canvasRef.current;
203
+ const box = boxRef.current?.getBoundingClientRect();
204
+ const b = strokeBounds(strokes.current, 6, box ? { width: box.width, height: box.height } : undefined);
205
+ if (!canvas || !b) return null;
206
+ const ratio = canvas.width / (box?.width || canvas.width);
207
+ const out = document.createElement("canvas");
208
+ out.width = Math.round(b.width * ratio);
209
+ out.height = Math.round(b.height * ratio);
210
+ out.getContext("2d").drawImage(canvas, b.x * ratio, b.y * ratio, b.width * ratio, b.height * ratio, 0, 0, out.width, out.height);
211
+ return out.toDataURL("image/png");
212
+ }, [mode, name, look.inkColour]);
213
+
214
+ // ---- sign -----------------------------------------------------------------
215
+
216
+ const submit = useCallback(async () => {
217
+ if (!onSubmit || saving || disabled) return;
218
+ const gaps = missing({ mode, drawn, name, requireName });
219
+ setErrors(gaps);
220
+ if (gaps) {
221
+ if (gaps.name) document.getElementById(`${id}-name`)?.focus();
222
+ return;
223
+ }
224
+ const png = await toPNG();
225
+ if (!png) return setErrors({ pad: "Draw your signature in the box." });
226
+ const record = signatureRecord({ png, name, mode });
227
+ // Optimistic: it shows as signed at once; a failed save puts the pad
228
+ // back as it was, with the reason.
229
+ setFailed(null);
230
+ setSigned(record);
231
+ setSaving(true);
232
+ let res;
233
+ try {
234
+ res = await onSubmit(record);
235
+ } catch (e) {
236
+ res = { ok: false, error: e?.message };
237
+ }
238
+ setSaving(false);
239
+ if (!res?.ok) {
240
+ setSigned(null);
241
+ setFailed(res?.error || "The signature wasn't saved. Try again.");
242
+ }
243
+ }, [onSubmit, saving, disabled, mode, drawn, name, requireName, toPNG, id]);
244
+
245
+ const signAgain = useCallback(() => {
246
+ setSigned(null);
247
+ setFailed(null);
248
+ strokes.current = [];
249
+ setDrawn(0);
250
+ }, []);
251
+
252
+ useImperativeHandle(
253
+ padRef,
254
+ () => ({
255
+ toPNG,
256
+ clear: () => clear(true),
257
+ undo,
258
+ isEmpty: () => (mode === "type" ? !name.trim() : pointCount(strokes.current) === 0),
259
+ getName: () => name.trim(),
260
+ getMode: () => mode,
261
+ /** { png, name, mode, signedAt } now, or null when nothing is there. */
262
+ getSignature: async () => {
263
+ const png = await toPNG();
264
+ return png ? signatureRecord({ png, name, mode }) : null;
265
+ },
266
+ submit,
267
+ }),
268
+ [toPNG, clear, undo, mode, name, submit],
269
+ );
270
+
271
+ // ---- render ---------------------------------------------------------------
272
+
273
+ const paper = PAPER[look.paper] ?? PAPER.background;
274
+ const frame = bare ? "" : "rounded-lg border border-border bg-card p-4 sm:p-5";
275
+ const nameId = `${id}-name`;
276
+ const hintId = `${id}-hint`;
277
+ const typed = name.trim();
278
+
279
+ if (signed) {
280
+ return (
281
+ <div ref={rootRef} className={cx(frame, "text-card-foreground", className)}>
282
+ <div className="flex items-center justify-between gap-3">
283
+ <p className="text-sm font-medium">{label}</p>
284
+ {saving ? (
285
+ <span className="text-xs text-muted-foreground">Saving…</span>
286
+ ) : (
287
+ <span className="inline-flex items-center gap-1.5 text-xs font-medium text-success">
288
+ <Icon name="circle-check" size={14} /> Signed
289
+ </span>
290
+ )}
291
+ </div>
292
+ <div className={cx("mt-3 grid place-items-center rounded-md border border-border px-4 py-5 transition-opacity duration-150 motion-reduce:transition-none", paper.box, look.known ? "opacity-100" : "opacity-0")} style={{ minHeight: Math.round(height * 0.7) }}>
293
+ {/* raw: the signature on its paper — a transparent PNG over the paper token; the kit Image paints a muted fill and a skeleton under it */}
294
+ <img src={signed.png} alt={`Signature of ${signed.name || "the signer"}`} className="max-h-28 max-w-full object-contain" />
295
+ </div>
296
+ <div className="mt-3 flex flex-wrap items-center justify-between gap-2">
297
+ <p className="min-w-0 text-sm text-muted-foreground">
298
+ <span className="font-medium text-foreground">{signed.name || "Signed"}</span>
299
+ {mounted && signed.signedAt ? <span> · {formatSignedAt(signed.signedAt)}</span> : null}
300
+ {signed.mode === "type" ? <span> · typed</span> : null}
301
+ </p>
302
+ {!disabled && !saving ? (
303
+ <Button variant="quiet" size="sm" icon="refresh" onClick={signAgain}>
304
+ Sign again
305
+ </Button>
306
+ ) : null}
307
+ </div>
308
+ </div>
309
+ );
310
+ }
311
+
312
+ return (
313
+ <div ref={rootRef} className={cx(frame, "text-card-foreground", className)}>
314
+ <div className="flex items-center justify-between gap-3">
315
+ <p className="text-sm font-medium" id={`${id}-label`}>
316
+ {label}
317
+ </p>
318
+ {allowed.length > 1 ? (
319
+ <ToggleGroup
320
+ size="sm"
321
+ aria-label="How to sign"
322
+ value={mode}
323
+ onChange={(m) => {
324
+ setMode(m);
325
+ setErrors(null);
326
+ tell({ mode: m, name, empty: m === "type" ? !typed : pointCount(strokes.current) === 0 });
327
+ }}
328
+ disabled={disabled}
329
+ items={allowed}
330
+ />
331
+ ) : null}
332
+ </div>
333
+
334
+ <Field id={nameId} label={nameLabel} error={errors?.name} className="mt-3">
335
+ <Input
336
+ id={nameId}
337
+ value={name}
338
+ disabled={disabled}
339
+ autoComplete="name"
340
+ placeholder="Your full name"
341
+ invalid={Boolean(errors?.name)}
342
+ aria-describedby={errors?.name ? `${nameId}-error` : undefined}
343
+ onChange={(e) => {
344
+ const v = e.target.value;
345
+ setName(v);
346
+ if (errors?.name && v.trim()) setErrors((x) => ({ ...x, name: undefined }));
347
+ tell({ mode, name: v, empty: mode === "type" ? !v.trim() : pointCount(strokes.current) === 0 });
348
+ }}
349
+ onKeyDown={(e) => {
350
+ if (e.key === "Enter" && mode === "type" && onSubmit) {
351
+ e.preventDefault();
352
+ submit();
353
+ }
354
+ }}
355
+ />
356
+ </Field>
357
+
358
+ {/* The paper. */}
359
+ <div
360
+ ref={boxRef}
361
+ className={cx(
362
+ "relative mt-3 overflow-hidden rounded-md border transition-[opacity,border-color] duration-150 motion-reduce:transition-none",
363
+ paper.box,
364
+ errors?.pad ? "border-destructive" : "border-border",
365
+ look.known ? "opacity-100" : "opacity-0",
366
+ disabled && "opacity-60",
367
+ )}
368
+ style={{ height }}
369
+ >
370
+ {/* The line to sign on, and its cross. */}
371
+ <span aria-hidden="true" className={cx("pointer-events-none absolute right-6 left-6 h-px", paper.line)} style={{ bottom: Math.round(height * 0.26) }} />
372
+ <span aria-hidden="true" className={cx("pointer-events-none absolute left-6 text-sm select-none", paper.mark)} style={{ bottom: Math.round(height * 0.26) + 4 }}>
373
+ ×
374
+ </span>
375
+
376
+ {mode === "draw" ? (
377
+ <>
378
+ {!drawn ? (
379
+ <p aria-hidden="true" className={cx("pointer-events-none absolute inset-x-0 top-1/2 -translate-y-1/2 px-6 text-center text-sm select-none", paper.hint)}>
380
+ Sign here with your mouse, pen or finger
381
+ </p>
382
+ ) : null}
383
+ {/* raw: the drawing surface — signature_pad draws on a canvas; no kit part is one */}
384
+ <canvas
385
+ ref={canvasRef}
386
+ role="img"
387
+ aria-labelledby={`${id}-label`}
388
+ aria-describedby={hintId}
389
+ className={cx("absolute inset-0 block size-full touch-none", disabled ? "cursor-not-allowed" : "cursor-crosshair")}
390
+ />
391
+ {drawn && !disabled ? (
392
+ <div className="absolute top-2 right-2 flex gap-1">
393
+ <Tooltip label="Undo last stroke">
394
+ <IconButton icon="undo" label="Undo last stroke" size="sm" variant="secondary" onClick={undo} />
395
+ </Tooltip>
396
+ <Tooltip label="Clear">
397
+ <IconButton icon="eraser" label="Clear the signature" size="sm" variant="secondary" onClick={() => clear()} />
398
+ </Tooltip>
399
+ </div>
400
+ ) : null}
401
+ </>
402
+ ) : (
403
+ <div className="absolute inset-x-6 flex items-end justify-center" style={{ bottom: Math.round(height * 0.26) + 6, top: 12 }}>
404
+ <p ref={typedRef} className={cx("truncate pb-1 font-display text-4xl leading-tight italic sm:text-5xl", typed ? paper.ink : paper.hint)} aria-live="polite">
405
+ {typed || <span className="text-base not-italic">Type your name above</span>}
406
+ </p>
407
+ </div>
408
+ )}
409
+ </div>
410
+
411
+ <p id={hintId} className={cx("mt-1.5 text-xs", errors?.pad ? "text-destructive" : "text-muted-foreground")} role={errors?.pad ? "alert" : undefined}>
412
+ {errors?.pad ?? (mode === "draw" ? (allowed.some((m) => m.value === "type") ? "Prefer the keyboard? Switch to Type." : "Draw inside the box.") : "Your name, set as a signature.")}
413
+ </p>
414
+
415
+ {failed ? (
416
+ <Notice tone="danger" title="Not signed yet" className="mt-3" onDismiss={() => setFailed(null)}>
417
+ {failed}
418
+ </Notice>
419
+ ) : null}
420
+
421
+ {note || onSubmit ? (
422
+ <div className="mt-4 flex flex-col-reverse gap-3 sm:flex-row sm:items-center sm:justify-between">
423
+ {note ? <p className="text-xs text-muted-foreground sm:max-w-md">{note}</p> : <span />}
424
+ {onSubmit ? (
425
+ <Button variant="primary" icon="pencil" onClick={submit} disabled={disabled} className="w-full sm:w-auto">
426
+ {submitLabel}
427
+ </Button>
428
+ ) : null}
429
+ </div>
430
+ ) : null}
431
+ </div>
432
+ );
433
+ }
434
+
@@ -0,0 +1,68 @@
1
+ // Plain helpers for a signature: its strokes, what is missing before it
2
+ // can be signed, the record it becomes. No DOM, so they run anywhere.
3
+ //
4
+ // Strokes are signature_pad's point groups, in CSS pixels of the pad:
5
+ // [{ points: [{ x, y, pressure, time }], penColor, minWidth, maxWidth, … }]
6
+
7
+ /** The box around every point, grown by `pad` px on each side (a stroke
8
+ * has width); clipped to 0,0 and, when given, to the pad's size. Null
9
+ * when there is nothing drawn. */
10
+ export function strokeBounds(groups, pad = 6, size) {
11
+ let x0 = Infinity;
12
+ let y0 = Infinity;
13
+ let x1 = -Infinity;
14
+ let y1 = -Infinity;
15
+ for (const g of groups ?? []) {
16
+ for (const p of g?.points ?? []) {
17
+ if (p.x < x0) x0 = p.x;
18
+ if (p.y < y0) y0 = p.y;
19
+ if (p.x > x1) x1 = p.x;
20
+ if (p.y > y1) y1 = p.y;
21
+ }
22
+ }
23
+ if (!Number.isFinite(x0)) return null;
24
+ const maxX = size?.width ?? Infinity;
25
+ const maxY = size?.height ?? Infinity;
26
+ const x = Math.max(0, Math.floor(x0 - pad));
27
+ const y = Math.max(0, Math.floor(y0 - pad));
28
+ const right = Math.min(maxX, Math.ceil(x1 + pad));
29
+ const bottom = Math.min(maxY, Math.ceil(y1 + pad));
30
+ return { x, y, width: Math.max(1, right - x), height: Math.max(1, bottom - y) };
31
+ }
32
+
33
+ /** Every stroke inked in `colour` (a theme change repaints the pad). */
34
+ export const withInk = (groups, colour) => (colour ? (groups ?? []).map((g) => ({ ...g, penColor: colour })) : groups ?? []);
35
+
36
+ /** The strokes without the last one. */
37
+ export const withoutLast = (groups) => (groups ?? []).slice(0, -1);
38
+
39
+ /** How many points were drawn: a tap is 1, a real signature dozens. */
40
+ export const pointCount = (groups) => (groups ?? []).reduce((n, g) => n + (g?.points?.length ?? 0), 0);
41
+
42
+ /** What stops this from being signed, as the words to show, or null.
43
+ * mode "draw" | "type"
44
+ * drawn how many points are on the pad
45
+ * name the typed full name
46
+ * requireName a name is needed in draw mode too */
47
+ export function missing({ mode, drawn, name, requireName = true }) {
48
+ const hasName = Boolean(String(name ?? "").trim());
49
+ if (mode === "type") return hasName ? null : { name: "Type your full name to sign." };
50
+ const out = {};
51
+ if (requireName && !hasName) out.name = "Add your full name.";
52
+ if (!drawn) out.pad = "Draw your signature in the box.";
53
+ else if (drawn < 4) out.pad = "That looks like a dot — draw your signature in the box.";
54
+ return Object.keys(out).length ? out : null;
55
+ }
56
+
57
+ /** The record a signature becomes (what onSubmit receives, what `value`
58
+ * shows): the picture, the name, how it was made, when. */
59
+ export function signatureRecord({ png, name, mode, signedAt }) {
60
+ return { png, name: String(name ?? "").trim(), mode, signedAt: signedAt ?? new Date().toISOString() };
61
+ }
62
+
63
+ /** "Sep 23, 2026, 2:04 PM" in the reader's own locale. */
64
+ export function formatSignedAt(iso, locale) {
65
+ const d = new Date(iso);
66
+ if (Number.isNaN(d.getTime())) return "";
67
+ return d.toLocaleString(locale, { month: "short", day: "numeric", year: "numeric", hour: "numeric", minute: "2-digit" });
68
+ }