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,89 @@
1
+ "use client";
2
+
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
+ import { isSample } from "../../parts";
5
+ import { toast } from "../../ui";
6
+ import { applyChange, changeWrites, changedIds, invertChange, normalizeTasks } from "./model";
7
+
8
+ // useTaskChanges — how every view reports a change, and shows it at once.
9
+ //
10
+ // const { tasks, commit, error, clearError } = useTaskChanges(props.tasks, props.onChange);
11
+ // commit({ type: "update", id, patch: { status: "done" } }, { label: "“Write brief”" });
12
+ //
13
+ // `commit` applies the change on screen, calls the page's
14
+ // `onChange(change)` (async, answering { ok } or { ok: false, error }; a
15
+ // throw counts as failed), and on a failure takes the change back and
16
+ // sets `error` — the view shows it in a Notice. The change stays on
17
+ // screen until the page's `tasks` carry it (the page re-read, or set its
18
+ // own state), so nothing flickers back between the save and the re-read.
19
+ //
20
+ // `undo: "Task deleted"` also shows a toast with an Undo button, which
21
+ // commits the inverse (the deleted tasks come back with their ids).
22
+ //
23
+ // Example rows (`useFixture`'s, `_sample: true`) change on screen and are
24
+ // never sent: they are not rows.
25
+
26
+ let seq = 0;
27
+
28
+ export function useTaskChanges(tasks, onChange) {
29
+ const base = useMemo(() => normalizeTasks(tasks), [tasks]);
30
+ const baseRef = useRef(base);
31
+ baseRef.current = base;
32
+ const [pending, setPending] = useState([]);
33
+ const [error, setError] = useState(null);
34
+
35
+ // A confirmed change has done its job once the page's tasks move on.
36
+ useEffect(() => {
37
+ setPending((list) => {
38
+ const next = list.filter((e) => !(e.confirmed && e.base !== base));
39
+ return next.length === list.length ? list : next;
40
+ });
41
+ }, [base]);
42
+
43
+ const shown = useMemo(() => pending.reduce((acc, e) => applyChange(acc, e.change), base), [base, pending]);
44
+ const latest = useRef(shown);
45
+ latest.current = shown;
46
+
47
+ const commit = useCallback(
48
+ async (change, { label, undo } = {}) => {
49
+ // A null change is a drop the model refused (movePatch → null).
50
+ if (!change) return { ok: false, error: "it cannot go there" };
51
+ const before = latest.current;
52
+ const key = ++seq;
53
+ const entry = { key, change, base: baseRef.current, confirmed: false };
54
+ setError(null);
55
+ setPending((list) => [...list, entry]);
56
+ const ids = new Set(changedIds(change));
57
+ const touched = before.filter((t) => ids.has(String(t.id)));
58
+ const creates = changeWrites(change).some((w) => w.op === "insert");
59
+ const onlySamples = !creates && touched.length > 0 && touched.every(isSample);
60
+ let result;
61
+ if (onlySamples || typeof onChange !== "function") result = { ok: true, sample: true };
62
+ else {
63
+ try {
64
+ result = (await onChange(change)) ?? { ok: true };
65
+ } catch (e) {
66
+ result = { ok: false, error: e?.message ?? String(e) };
67
+ }
68
+ }
69
+ if (result.ok === false) {
70
+ setPending((list) => list.filter((e) => e.key !== key));
71
+ setError(`${label ?? "The change"} was not saved: ${result.error ?? "the write failed"}. It is back as it was.`);
72
+ return result;
73
+ }
74
+ setPending((list) =>
75
+ list.map((e) => (e.key === key ? { ...e, confirmed: true } : e)).filter((e) => !(e.confirmed && e.base !== baseRef.current)),
76
+ );
77
+ if (undo) {
78
+ const inverse = invertChange(before, change);
79
+ toast(undo, {
80
+ action: { label: "Undo", onClick: () => commit(inverse, { label: "Undo" }) },
81
+ });
82
+ }
83
+ return result;
84
+ },
85
+ [onChange],
86
+ );
87
+
88
+ return { tasks: shown, commit, error, clearError: () => setError(null), saving: pending.some((e) => !e.confirmed) };
89
+ }
@@ -0,0 +1,371 @@
1
+ // Date math for work items — pure, no React, no Date objects in or out.
2
+ //
3
+ // A task's `start` and `due` are wall-calendar strings: a day
4
+ // "2026-09-23", or a day and a time "2026-09-23T14:30" (no zone: a
5
+ // meeting at 14:30 is at 14:30 wherever the founder is). Strings compare
6
+ // in order, survive JSON and a `date` column unchanged, and mean the same
7
+ // day everywhere. Arithmetic runs on day numbers (days since 1970-01-01,
8
+ // counted on UTC midnights), where no daylight-saving shift can add or
9
+ // lose a day. Calendar, gantt, workload and roadmap all read these.
10
+
11
+ const DAY_RE = /^(\d{4})-(\d{2})-(\d{2})/;
12
+ const TIME_RE = /T(\d{2}):(\d{2})/;
13
+ const MS = 86400000;
14
+ const pad = (n, w = 2) => String(n).padStart(w, "0");
15
+
16
+ /** A real "YYYY-MM-DD" (a time after it is allowed). */
17
+ export function isDay(value) {
18
+ return dayNumber(value) !== null;
19
+ }
20
+
21
+ /** Days since 1970-01-01 for a day string (or datetime string, or Date);
22
+ * null when it is not a real day. */
23
+ export function dayNumber(value) {
24
+ if (value instanceof Date) {
25
+ if (Number.isNaN(value.getTime())) return null;
26
+ return Math.floor(Date.UTC(value.getFullYear(), value.getMonth(), value.getDate()) / MS);
27
+ }
28
+ const m = typeof value === "string" ? DAY_RE.exec(value) : null;
29
+ if (!m) return null;
30
+ const t = Date.UTC(+m[1], +m[2] - 1, +m[3]);
31
+ const d = new Date(t);
32
+ if (d.getUTCMonth() !== +m[2] - 1 || d.getUTCDate() !== +m[3]) return null;
33
+ return Math.round(t / MS);
34
+ }
35
+
36
+ /** The day string for a day number. */
37
+ export function fromDayNumber(n) {
38
+ const d = new Date(n * MS);
39
+ return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())}`;
40
+ }
41
+
42
+ /** "2026-09-23T14:30" → "2026-09-23"; a Date → its local day; else null. */
43
+ export function dayOf(value) {
44
+ const n = dayNumber(value);
45
+ return n === null ? null : fromDayNumber(n);
46
+ }
47
+
48
+ /** Minutes after midnight for a datetime string ("…T14:30" → 870); null
49
+ * for a plain day (an all-day item). */
50
+ export function minutesOf(value) {
51
+ const m = typeof value === "string" ? TIME_RE.exec(value) : null;
52
+ return m ? +m[1] * 60 + +m[2] : null;
53
+ }
54
+
55
+ /** A day and minutes after midnight → "YYYY-MM-DDTHH:mm"; null minutes →
56
+ * the plain day. */
57
+ export function withTime(day, minutes) {
58
+ const d = dayOf(day);
59
+ if (!d) return null;
60
+ if (minutes == null) return d;
61
+ const m = Math.max(0, Math.min(24 * 60 - 1, Math.round(minutes)));
62
+ return `${d}T${pad(Math.floor(m / 60))}:${pad(m % 60)}`;
63
+ }
64
+
65
+ /** Today in the browser's zone, or `now`'s. Pass a fixed `today` string to
66
+ * the views where a screen must not change from day to day (a demo). */
67
+ export function todayDay(now = new Date()) {
68
+ return dayOf(now);
69
+ }
70
+
71
+ export function addDays(day, n) {
72
+ const d = dayNumber(day);
73
+ return d === null ? null : fromDayNumber(d + n);
74
+ }
75
+
76
+ /** b − a in days ("2026-09-01", "2026-09-03" → 2). */
77
+ export function diffDays(a, b) {
78
+ const x = dayNumber(a);
79
+ const y = dayNumber(b);
80
+ return x === null || y === null ? null : y - x;
81
+ }
82
+
83
+ /** 0 = Sunday … 6 = Saturday. */
84
+ export function weekdayOf(day) {
85
+ const n = dayNumber(day);
86
+ return n === null ? null : (n + 4) % 7; // 1970-01-01 was a Thursday
87
+ }
88
+
89
+ export function startOfWeek(day, weekStartsOn = 1) {
90
+ const w = weekdayOf(day);
91
+ return w === null ? null : addDays(day, -((w - weekStartsOn + 7) % 7));
92
+ }
93
+
94
+ export function endOfWeek(day, weekStartsOn = 1) {
95
+ const s = startOfWeek(day, weekStartsOn);
96
+ return s && addDays(s, 6);
97
+ }
98
+
99
+ /** The seven days of `day`'s week. */
100
+ export function weekDays(day, weekStartsOn = 1) {
101
+ const s = startOfWeek(day, weekStartsOn);
102
+ return s ? Array.from({ length: 7 }, (_, i) => addDays(s, i)) : [];
103
+ }
104
+
105
+ export function startOfMonth(day) {
106
+ const d = dayOf(day);
107
+ return d ? `${d.slice(0, 8)}01` : null;
108
+ }
109
+
110
+ export function endOfMonth(day) {
111
+ const s = startOfMonth(day);
112
+ return s ? addDays(addMonths(s, 1), -1) : null;
113
+ }
114
+
115
+ /** The same day n months on, clamped to the month's end (Jan 31 + 1 → Feb 28). */
116
+ export function addMonths(day, n) {
117
+ const d = dayOf(day);
118
+ if (!d) return null;
119
+ const y = +d.slice(0, 4);
120
+ const m = +d.slice(5, 7) - 1 + n;
121
+ const ty = y + Math.floor(m / 12);
122
+ const tm = ((m % 12) + 12) % 12;
123
+ const last = new Date(Date.UTC(ty, tm + 1, 0)).getUTCDate();
124
+ return `${ty}-${pad(tm + 1)}-${pad(Math.min(+d.slice(8, 10), last))}`;
125
+ }
126
+
127
+ /** Every day from a to b, both included. */
128
+ export function daysBetween(a, b) {
129
+ const x = dayNumber(a);
130
+ const y = dayNumber(b);
131
+ if (x === null || y === null || y < x) return [];
132
+ return Array.from({ length: y - x + 1 }, (_, i) => fromDayNumber(x + i));
133
+ }
134
+
135
+ /** A month as weeks of seven days, for a month grid: the weeks that hold
136
+ * any of its days (4–6), or always six with `fixedWeeks` (a grid that
137
+ * never changes height). Days outside the month are included; compare
138
+ * `day.slice(0, 7)` with the month to mute them. */
139
+ export function monthGrid(monthOrDay, { weekStartsOn = 1, fixedWeeks = false } = {}) {
140
+ const first = startOfMonth(/^\d{4}-\d{2}$/.test(monthOrDay ?? "") ? `${monthOrDay}-01` : monthOrDay);
141
+ if (!first) return [];
142
+ const last = endOfMonth(first);
143
+ let cursor = startOfWeek(first, weekStartsOn);
144
+ const weeks = [];
145
+ while (weeks.length < 6 && (fixedWeeks || cursor <= last)) {
146
+ weeks.push(Array.from({ length: 7 }, (_, i) => addDays(cursor, i)));
147
+ cursor = addDays(cursor, 7);
148
+ }
149
+ return weeks;
150
+ }
151
+
152
+ /** Weekend days as weekday numbers; default Saturday and Sunday. */
153
+ export const WEEKEND = [0, 6];
154
+
155
+ export function isWeekend(day, weekend = WEEKEND) {
156
+ return weekend.includes(weekdayOf(day));
157
+ }
158
+
159
+ /** Not a weekend and not in `holidays` (a list of day strings). */
160
+ export function isWorkingDay(day, { weekend = WEEKEND, holidays } = {}) {
161
+ const d = dayOf(day);
162
+ if (!d || isWeekend(d, weekend)) return false;
163
+ return !(holidays && (holidays instanceof Set ? holidays.has(d) : holidays.includes(d)));
164
+ }
165
+
166
+ /** Working days from a to b, both included (0 when b is before a). */
167
+ export function workingDaysBetween(a, b, options) {
168
+ return daysBetween(a, b).filter((d) => isWorkingDay(d, options)).length;
169
+ }
170
+
171
+ /** n working days after `day` (n may be negative; 0 → `day` itself, or
172
+ * the next working day when `day` is not one). */
173
+ export function addWorkingDays(day, n, options) {
174
+ let d = dayOf(day);
175
+ if (!d) return null;
176
+ const step = n < 0 ? -1 : 1;
177
+ while (!isWorkingDay(d, options)) d = addDays(d, step);
178
+ let left = Math.abs(n);
179
+ while (left > 0) {
180
+ d = addDays(d, step);
181
+ if (isWorkingDay(d, options)) left--;
182
+ }
183
+ return d;
184
+ }
185
+
186
+ /** A task's days on a calendar: { start, end } (day strings, start ≤
187
+ * end). One date → a one-day span; neither → null (unscheduled). */
188
+ export function spanOf(task) {
189
+ const s = dayOf(task?.start);
190
+ const e = dayOf(task?.due);
191
+ if (!s && !e) return null;
192
+ const a = s ?? e;
193
+ const b = e ?? s;
194
+ return a <= b ? { start: a, end: b } : { start: b, end: a };
195
+ }
196
+
197
+ /** Whether two spans ({ start, end }, inclusive) share a day. */
198
+ export function spansOverlap(a, b) {
199
+ return Boolean(a && b && a.start <= b.end && b.start <= a.end);
200
+ }
201
+
202
+ /** The tasks whose span touches [from, to] (inclusive). */
203
+ export function tasksInRange(tasks, from, to) {
204
+ const range = { start: dayOf(from), end: dayOf(to) };
205
+ return (tasks ?? []).filter((t) => spansOverlap(spanOf(t), range));
206
+ }
207
+
208
+ /** The tasks on one day. */
209
+ export function tasksOnDay(tasks, day) {
210
+ return tasksInRange(tasks, day, day);
211
+ }
212
+
213
+ /** Moves a task's dates by n days, keeping its length and its times:
214
+ * the patch a drag on a calendar or a gantt saves. */
215
+ export function shiftDates(task, n) {
216
+ const patch = {};
217
+ for (const key of ["start", "due"]) {
218
+ const v = task?.[key];
219
+ if (!isDay(v)) continue;
220
+ patch[key] = withTime(addDays(v, n), minutesOf(v));
221
+ }
222
+ return patch;
223
+ }
224
+
225
+ /** The patch that gives a task the span [start, end] (a resize, a drop on
226
+ * a range), keeping any times it had. A one-day task with only `due`
227
+ * stays that way when start === end. */
228
+ export function spanPatch(task, start, end) {
229
+ if (start === end && !isDay(task?.start)) return { due: withTime(end, minutesOf(task?.due)) };
230
+ return { start: withTime(start, minutesOf(task?.start)), due: withTime(end, minutesOf(task?.due)) };
231
+ }
232
+
233
+ /** Where a due date sits from `today`: "overdue" · "today" · "tomorrow" ·
234
+ * "this-week" · "next-week" · "later" · "none". A done task is never
235
+ * overdue: `done` turns "overdue" into "past". */
236
+ export function dueBucket(due, today, { done = false, weekStartsOn = 1 } = {}) {
237
+ const d = dayOf(due);
238
+ if (!d) return "none";
239
+ const n = diffDays(today, d);
240
+ if (n < 0) return done ? "past" : "overdue";
241
+ if (n === 0) return "today";
242
+ if (n === 1) return "tomorrow";
243
+ const weekEnd = endOfWeek(today, weekStartsOn);
244
+ if (d <= weekEnd) return "this-week";
245
+ if (d <= addDays(weekEnd, 7)) return "next-week";
246
+ return "later";
247
+ }
248
+
249
+ /** The due buckets in order, with their words: the groups of "group by
250
+ * due date" and the choices of a due filter. */
251
+ export const DUE_BUCKETS = [
252
+ { id: "overdue", label: "Overdue", tone: "danger" },
253
+ { id: "today", label: "Today", tone: "warning" },
254
+ { id: "tomorrow", label: "Tomorrow" },
255
+ { id: "this-week", label: "This week" },
256
+ { id: "next-week", label: "Next week" },
257
+ { id: "later", label: "Later" },
258
+ { id: "past", label: "Earlier" },
259
+ { id: "none", label: "No date" },
260
+ ];
261
+
262
+ /** The day a task dropped into a due bucket gets (null for "none"; the
263
+ * task's own date for a bucket that has no single day). */
264
+ export function dayForBucket(bucket, today, { weekStartsOn = 1 } = {}) {
265
+ switch (bucket) {
266
+ case "today":
267
+ return today;
268
+ case "tomorrow":
269
+ return addDays(today, 1);
270
+ case "this-week": {
271
+ const end = endOfWeek(today, weekStartsOn);
272
+ return diffDays(today, end) >= 2 ? addDays(today, 2) : end;
273
+ }
274
+ case "next-week":
275
+ return addDays(endOfWeek(today, weekStartsOn), 1);
276
+ case "later":
277
+ return addDays(endOfWeek(today, weekStartsOn), 8);
278
+ case "none":
279
+ return null;
280
+ default:
281
+ return undefined; // overdue / past: not a place to drop
282
+ }
283
+ }
284
+
285
+ function fmt(day, options, locale) {
286
+ const n = dayNumber(day);
287
+ return n === null ? "" : new Intl.DateTimeFormat(locale, { ...options, timeZone: "UTC" }).format(new Date(n * MS));
288
+ }
289
+
290
+ /** A day the way a person says it, from `today`: "Today", "Tomorrow",
291
+ * "Yesterday", a weekday within the week either side ("Fri"), else
292
+ * "Sep 30" (with the year when it is not this year's). */
293
+ export function formatDay(day, today, { locale, weekday = "short" } = {}) {
294
+ const d = dayOf(day);
295
+ if (!d) return "";
296
+ const n = today ? diffDays(today, d) : null;
297
+ if (n === 0) return "Today";
298
+ if (n === 1) return "Tomorrow";
299
+ if (n === -1) return "Yesterday";
300
+ if (n !== null && n > 1 && n < 7) return fmt(d, { weekday }, locale);
301
+ const sameYear = today && d.slice(0, 4) === dayOf(today).slice(0, 4);
302
+ return fmt(d, sameYear ? { month: "short", day: "numeric" } : { month: "short", day: "numeric", year: "numeric" }, locale);
303
+ }
304
+
305
+ /** A time the locale's way ("2:30 PM", "14:30"); "" for a plain day. */
306
+ export function formatTime(value, { locale } = {}) {
307
+ const m = minutesOf(value);
308
+ if (m === null) return "";
309
+ return new Intl.DateTimeFormat(locale, { hour: "numeric", minute: "2-digit", timeZone: "UTC" }).format(new Date(m * 60000));
310
+ }
311
+
312
+ /** A day in any Intl shape: formatDate("2026-09-23", { weekday: "long" }). */
313
+ export function formatDate(day, options = { dateStyle: "medium" }, { locale } = {}) {
314
+ return fmt(day, options, locale);
315
+ }
316
+
317
+ /** The time scale of a timeline: columns of `unit` (day · week · month ·
318
+ * quarter) covering [from, to], each { start, end, label, key }. A gantt
319
+ * draws its header from it, a workload its columns. */
320
+ export function timeScale(from, to, unit = "day", { weekStartsOn = 1, locale } = {}) {
321
+ const a = dayOf(from);
322
+ const b = dayOf(to);
323
+ if (!a || !b || b < a) return [];
324
+ const out = [];
325
+ let cursor =
326
+ unit === "week" ? startOfWeek(a, weekStartsOn) : unit === "month" ? startOfMonth(a) : unit === "quarter" ? quarterStart(a) : a;
327
+ while (cursor <= b && out.length < 2000) {
328
+ let end;
329
+ let label;
330
+ if (unit === "week") {
331
+ end = addDays(cursor, 6);
332
+ label = fmt(cursor, { month: "short", day: "numeric" }, locale);
333
+ } else if (unit === "month") {
334
+ end = endOfMonth(cursor);
335
+ label = fmt(cursor, { month: "short", year: cursor.slice(0, 4) === a.slice(0, 4) ? undefined : "numeric" }, locale);
336
+ } else if (unit === "quarter") {
337
+ end = addDays(addMonths(cursor, 3), -1);
338
+ label = `Q${Math.floor((+cursor.slice(5, 7) - 1) / 3) + 1} ${cursor.slice(0, 4)}`;
339
+ } else {
340
+ end = cursor;
341
+ label = fmt(cursor, { day: "numeric" }, locale);
342
+ }
343
+ out.push({ key: cursor, start: cursor, end, label });
344
+ cursor = addDays(end, 1);
345
+ }
346
+ return out;
347
+ }
348
+
349
+ function quarterStart(day) {
350
+ const m = +day.slice(5, 7);
351
+ return `${day.slice(0, 4)}-${pad(Math.floor((m - 1) / 3) * 3 + 1)}-01`;
352
+ }
353
+
354
+ /** The first and last day any task touches, padded by `pad` days either
355
+ * side — a timeline's default window. `fallback` (a day) when no task has
356
+ * a date. */
357
+ export function rangeOfTasks(tasks, { pad: padDays = 0, fallback } = {}) {
358
+ let lo = null;
359
+ let hi = null;
360
+ for (const t of tasks ?? []) {
361
+ const s = spanOf(t);
362
+ if (!s) continue;
363
+ if (lo === null || s.start < lo) lo = s.start;
364
+ if (hi === null || s.end > hi) hi = s.end;
365
+ }
366
+ if (lo === null) {
367
+ const f = dayOf(fallback) ?? todayDay();
368
+ return { start: addDays(f, -padDays), end: addDays(f, padDays) };
369
+ }
370
+ return { start: addDays(lo, -padDays), end: addDays(hi, padDays) };
371
+ }
@@ -0,0 +1,215 @@
1
+ "use client";
2
+
3
+ // task-model in the gallery — the gallery's and the smoke test's surface.
4
+ // Not installed into apps. One dataset, one ViewToolbar, three
5
+ // surfaces over it, the way work-views will switch between them:
6
+ // Board the Board block driven by toBoard() (drag a card: the
7
+ // status, assignee or due date is saved through one commit)
8
+ // Table the DataTable block driven by toTableRows(), taskColumns()
9
+ // and taskFacets() — the status, people, due and priority
10
+ // cells are pickers
11
+ // Parts the shared pieces, and the drag primitives on a small
12
+ // sortable list
13
+ import { useMemo, useState } from "react";
14
+ import { Card, Notice, Switch, TabPanel, Tabs } from "../components/ui";
15
+ import { Board } from "../components/blocks/board";
16
+ import { DataTable } from "../components/blocks/data-table";
17
+ import {
18
+ AssigneeStack,
19
+ CompleteToggle,
20
+ DragHandle,
21
+ DueLabel,
22
+ LIFTED,
23
+ PriorityFlag,
24
+ SortableGroup,
25
+ StatusPill,
26
+ SubtaskCount,
27
+ TaskChip,
28
+ TaskDragProvider,
29
+ ViewToolbar,
30
+ applyChange,
31
+ applyView,
32
+ arrayMove,
33
+ createTaskContext,
34
+ isDone,
35
+ placeChange,
36
+ sortTasks,
37
+ subtaskCounts,
38
+ taskColumns,
39
+ taskFacets,
40
+ toBoard,
41
+ toTableRows,
42
+ toggleDoneChange,
43
+ useSortableTask,
44
+ useTaskChanges,
45
+ workload,
46
+ } from "../components/blocks/task-model";
47
+
48
+ const TODAY = "2026-09-23";
49
+
50
+ const STATUSES = [
51
+ { id: "todo", label: "To do", tone: "neutral" },
52
+ { id: "doing", label: "In progress", tone: "info" },
53
+ { id: "review", label: "In review", tone: "warning" },
54
+ { id: "done", label: "Done", tone: "success", done: true },
55
+ ];
56
+ const PEOPLE = [
57
+ { id: "maya", name: "Maya Chen", capacity: 6 },
58
+ { id: "leo", name: "Leo Park" },
59
+ { id: "priya", name: "Priya Nair" },
60
+ ];
61
+ const PROJECTS = [
62
+ { id: "site", name: "Website relaunch", hue: 1 },
63
+ { id: "q4", name: "Q4 campaign", hue: 3 },
64
+ ];
65
+ const START = [
66
+ { id: "a1", title: "Finalise pricing page copy", status: "doing", assignees: ["maya"], start: "2026-09-22", due: "2026-09-24", priority: "high", project: "site", estimate: 6, order: 1024 },
67
+ { id: "a2", title: "Pick the hero illustration", status: "todo", assignees: ["priya", "leo"], due: "2026-09-25", priority: "medium", project: "site", estimate: 4, order: 2048 },
68
+ { id: "a3", title: "Shortlist three styles", status: "done", parentId: "a2", assignees: ["priya"], order: 1024 },
69
+ { id: "a4", title: "Book the product photo shoot", status: "todo", assignees: ["leo"], due: "2026-09-21", priority: "urgent", project: "q4", estimate: 1, order: 3072 },
70
+ { id: "a5", title: "Write the launch email", status: "review", assignees: ["maya"], due: "2026-09-29T10:00", priority: "high", project: "q4", estimate: 2, dependsOn: ["a1"], order: 1024 },
71
+ { id: "a6", title: "Draft the Northwind customer story", status: "todo", assignees: [], due: "2026-10-06", project: "q4", order: 4096 },
72
+ { id: "a7", title: "Choose the analytics events", status: "done", assignees: ["leo"], due: "2026-09-18", project: "site", order: 2048 },
73
+ ];
74
+
75
+ export default function TaskModelDemo() {
76
+ const [tasks, setTasks] = useState(START);
77
+ const [fail, setFail] = useState(false);
78
+ const [tab, setTab] = useState("board");
79
+ const [view, setView] = useState({ groupBy: "status", search: "", filters: {}, sort: [], fields: ["assignees", "due", "priority"] });
80
+ const ctx = useMemo(() => createTaskContext({ statuses: STATUSES, people: PEOPLE, projects: PROJECTS, today: TODAY }), []);
81
+
82
+ // The page's one save: in an app, a loop over changeWrites(change)
83
+ // into insertRow / updateRow / deleteRow.
84
+ const onChange = async (change) => {
85
+ await new Promise((r) => setTimeout(r, 300));
86
+ if (fail) return { ok: false, error: "the server did not answer" };
87
+ setTasks((all) => applyChange(all, change));
88
+ return { ok: true };
89
+ };
90
+ const { tasks: live, commit, error, clearError } = useTaskChanges(tasks, onChange);
91
+ const shown = applyView(live, view, ctx);
92
+ const by = view.groupBy === "none" ? "status" : view.groupBy;
93
+ const board = toBoard(shown.tasks, { groupBy: by, ctx });
94
+ const counts = subtaskCounts(live, ctx);
95
+
96
+ return (
97
+ <div className="grid gap-4">
98
+ <ViewToolbar view={view} onViewChange={setView} ctx={ctx} tasks={live} count={shown.tasks.length} controls={["search", "filter", "group", "sort"]} extra={<Switch id="tm-fail" label="Make saves fail" checked={fail} onChange={(e) => setFail(e.target.checked)} />} />
99
+ {error ? (
100
+ <Notice tone="danger" onDismiss={clearError}>
101
+ {error}
102
+ </Notice>
103
+ ) : null}
104
+ <Tabs
105
+ aria-label="Views over one dataset"
106
+ value={tab}
107
+ onChange={setTab}
108
+ tabs={[
109
+ { key: "board", label: "Board" },
110
+ { key: "table", label: "Table" },
111
+ { key: "parts", label: "Parts" },
112
+ ]}
113
+ />
114
+ <TabPanel tab="board" value={tab}>
115
+ <Board
116
+ label={`Tasks by ${by}`}
117
+ rows={board.rows}
118
+ groupBy={board.groupBy}
119
+ columns={board.columns}
120
+ otherLabel={board.otherLabel}
121
+ onMove={(row, to, from) => commit(board.moveChange(row, to, from), { label: `“${row.title}”` })}
122
+ titleOf={(t) => t.title}
123
+ renderDetails={(t) => (
124
+ <div className="flex items-center justify-between gap-2">
125
+ <span className="flex items-center gap-2">
126
+ <DueLabel value={t.due} done={isDone(t, ctx)} ctx={ctx} />
127
+ <PriorityFlag value={t.priority} />
128
+ <SubtaskCount {...(counts.get(t.id) ?? {})} />
129
+ </span>
130
+ <AssigneeStack ids={t.assignees} ctx={ctx} />
131
+ </div>
132
+ )}
133
+ />
134
+ </TabPanel>
135
+ <TabPanel tab="table" value={tab}>
136
+ <DataTable
137
+ label="Tasks"
138
+ noun="task"
139
+ rows={toTableRows(shown.tasks, ctx)}
140
+ columns={taskColumns(ctx, { keys: ["title", "status", "assignees", "due", "priority", "project", "estimate"], onChange: (c) => commit(c) })}
141
+ facets={taskFacets(ctx, { keys: ["project"] })}
142
+ searchable={false}
143
+ mobile="cards"
144
+ />
145
+ </TabPanel>
146
+ <TabPanel tab="parts" value={tab}>
147
+ <Parts tasks={shown.tasks} all={live} ctx={ctx} commit={commit} />
148
+ </TabPanel>
149
+ </div>
150
+ );
151
+ }
152
+
153
+ function Parts({ tasks, all, ctx, commit }) {
154
+ const roots = sortTasks(tasks.filter((t) => !t.parentId), [], ctx);
155
+ const load = workload(all, ctx, { from: "2026-09-21", to: "2026-09-27", unit: "week" });
156
+ return (
157
+ <div className="grid gap-4 lg:grid-cols-2">
158
+ <Card title="Drag to reorder" description="useSortableTask + DragHandle; the drop saves a placeChange.">
159
+ <TaskDragProvider
160
+ onDragEnd={({ active, over }) => {
161
+ if (!over || active.id === over.id) return;
162
+ const ids = roots.map((t) => t.id);
163
+ const next = arrayMove(ids, ids.indexOf(active.id), ids.indexOf(over.id));
164
+ const task = active.data.task;
165
+ commit(placeChange(roots.filter((t) => t.id !== task.id), task, next.indexOf(task.id)), { label: `“${task.title}”` });
166
+ }}
167
+ overlay={(a) => <div className={LIFTED}><TaskChip task={a.data.task} ctx={ctx} /></div>}
168
+ >
169
+ <SortableGroup ids={roots.map((t) => t.id)}>
170
+ <ul className="grid gap-1">
171
+ {roots.map((t) => (
172
+ <SortableItem key={t.id} task={t} ctx={ctx} commit={commit} />
173
+ ))}
174
+ </ul>
175
+ </SortableGroup>
176
+ </TaskDragProvider>
177
+ </Card>
178
+ <Card title="Load this week" description="workload(tasks, ctx, { unit: 'week' }) — hours against capacity.">
179
+ <ul className="grid gap-3">
180
+ {load.rows.map((r) => {
181
+ const b = r.buckets[0];
182
+ const pct = b.capacity ? Math.min(100, (b.hours / b.capacity) * 100) : 0;
183
+ return (
184
+ <li key={r.person.id} className="grid gap-1">
185
+ <div className="flex items-center justify-between text-sm">
186
+ <span>{r.person.name}</span>
187
+ <span data-nums className={b.over ? "text-destructive" : "text-muted-foreground"}>
188
+ {Math.round(b.hours * 10) / 10}h of {b.capacity}h
189
+ </span>
190
+ </div>
191
+ <span className="block h-1.5 overflow-hidden rounded-full bg-muted">
192
+ <span className={b.over ? "block h-full bg-destructive" : "block h-full bg-primary"} style={{ width: `${pct}%` }} />
193
+ </span>
194
+ </li>
195
+ );
196
+ })}
197
+ </ul>
198
+ </Card>
199
+ </div>
200
+ );
201
+ }
202
+
203
+ function SortableItem({ task, ctx, commit }) {
204
+ const s = useSortableTask(task.id, { task });
205
+ const done = isDone(task, ctx);
206
+ return (
207
+ <li ref={s.setNodeRef} style={s.style} className={s.isDragging ? "group/row flex items-center gap-1 opacity-40" : "group/row flex items-center gap-1"}>
208
+ <DragHandle handle={s.handle} handleRef={s.setHandleRef} label={task.title} />
209
+ <CompleteToggle done={done} onToggle={() => commit(toggleDoneChange(task, ctx))} label={task.title} size="sm" />
210
+ <span className={done ? "min-w-0 flex-1 truncate text-sm text-muted-foreground line-through" : "min-w-0 flex-1 truncate text-sm"}>{task.title}</span>
211
+ <StatusPill status={task.status} ctx={ctx} onChange={(status) => commit({ type: "update", id: task.id, patch: { status } })} />
212
+ <AssigneeStack ids={task.assignees} ctx={ctx} onChange={(assignees) => commit({ type: "update", id: task.id, patch: { assignees } })} />
213
+ </li>
214
+ );
215
+ }