makersclaw 0.2.0 → 0.3.1

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 (475) hide show
  1. package/THIRD-PARTY-NOTICES.md +180 -0
  2. package/package.json +3 -2
  3. package/r/annotate.json +62 -0
  4. package/r/approval-request.json +52 -0
  5. package/r/audio-player.json +74 -0
  6. package/r/auth-form.json +55 -0
  7. package/r/board.json +10 -6
  8. package/r/calendar-view.json +105 -0
  9. package/r/cart.json +62 -0
  10. package/r/chart.json +83 -0
  11. package/r/chat.json +95 -0
  12. package/r/code-editor.json +94 -0
  13. package/r/comment-box.json +64 -0
  14. package/r/contact-section.json +53 -0
  15. package/r/core.json +205 -8
  16. package/r/csv-import.json +63 -0
  17. package/r/data-table.json +69 -0
  18. package/r/diagram.json +74 -0
  19. package/r/doc-editor.json +82 -0
  20. package/r/doc-page.json +61 -0
  21. package/r/email-render.json +73 -0
  22. package/r/faq.json +50 -0
  23. package/r/file-upload.json +54 -0
  24. package/r/flow-canvas.json +93 -0
  25. package/r/footer.json +52 -0
  26. package/r/gantt.json +70 -0
  27. package/r/job-panel.json +12 -7
  28. package/r/json-editor.json +66 -0
  29. package/r/map.json +89 -0
  30. package/r/menubar.json +52 -0
  31. package/r/mind-map.json +67 -0
  32. package/r/navigation-menu.json +52 -0
  33. package/r/order-detail.json +54 -0
  34. package/r/org-chart.json +66 -0
  35. package/r/page-builder.json +105 -0
  36. package/r/payment-methods.json +51 -0
  37. package/r/pdf-document.json +79 -0
  38. package/r/pdf-viewer.json +88 -0
  39. package/r/product-detail.json +76 -0
  40. package/r/product-grid.json +65 -0
  41. package/r/record-form.json +67 -0
  42. package/r/record-notes.json +55 -0
  43. package/r/registry.json +3411 -29
  44. package/r/search-dialog.json +51 -0
  45. package/r/settings-list.json +66 -0
  46. package/r/setup-checklist.json +51 -0
  47. package/r/share-dialog.json +52 -0
  48. package/r/sidebar.json +69 -0
  49. package/r/signature-pad.json +72 -0
  50. package/r/sketch-canvas.json +99 -0
  51. package/r/spreadsheet.json +90 -0
  52. package/r/stat-cards.json +66 -0
  53. package/r/task-list.json +63 -0
  54. package/r/task-model.json +98 -0
  55. package/r/testimonials.json +52 -0
  56. package/r/transaction-list.json +53 -0
  57. package/r/whiteboard.json +66 -0
  58. package/registry/blocks/annotate/README.md +179 -0
  59. package/registry/blocks/annotate/annotate-data.js +250 -0
  60. package/registry/blocks/annotate/annotate.jsx +646 -0
  61. package/registry/blocks/annotate/block.json +10 -0
  62. package/registry/blocks/annotate/demo.jsx +135 -0
  63. package/registry/blocks/annotate/index.js +15 -0
  64. package/registry/blocks/approval-request/README.md +116 -0
  65. package/registry/blocks/approval-request/approval-request.jsx +386 -0
  66. package/registry/blocks/approval-request/block.json +10 -0
  67. package/registry/blocks/approval-request/demo.jsx +127 -0
  68. package/registry/blocks/approval-request/index.js +4 -0
  69. package/registry/blocks/audio-player/README.md +167 -0
  70. package/registry/blocks/audio-player/audio-player.jsx +623 -0
  71. package/registry/blocks/audio-player/block.json +14 -0
  72. package/registry/blocks/audio-player/colors.js +109 -0
  73. package/registry/blocks/audio-player/demo.jsx +209 -0
  74. package/registry/blocks/audio-player/index.js +6 -0
  75. package/registry/blocks/audio-player/time.js +67 -0
  76. package/registry/blocks/audio-player/wavesurfer.js +9 -0
  77. package/registry/blocks/auth-form/README.md +126 -0
  78. package/registry/blocks/auth-form/auth-form.jsx +568 -0
  79. package/registry/blocks/auth-form/block.json +10 -0
  80. package/registry/blocks/auth-form/demo.jsx +131 -0
  81. package/registry/blocks/auth-form/index.js +3 -0
  82. package/registry/blocks/board/README.md +55 -5
  83. package/registry/blocks/board/block.json +2 -2
  84. package/registry/blocks/board/board.jsx +230 -86
  85. package/registry/blocks/board/demo.jsx +103 -24
  86. package/registry/blocks/calendar-view/README.md +180 -0
  87. package/registry/blocks/calendar-view/agenda.jsx +147 -0
  88. package/registry/blocks/calendar-view/block.json +76 -0
  89. package/registry/blocks/calendar-view/calendar-model.js +442 -0
  90. package/registry/blocks/calendar-view/calendar.jsx +627 -0
  91. package/registry/blocks/calendar-view/demo.jsx +195 -0
  92. package/registry/blocks/calendar-view/draft.jsx +49 -0
  93. package/registry/blocks/calendar-view/index.js +39 -0
  94. package/registry/blocks/calendar-view/month.jsx +346 -0
  95. package/registry/blocks/calendar-view/paint.js +69 -0
  96. package/registry/blocks/calendar-view/time-grid.jsx +527 -0
  97. package/registry/blocks/calendar-view/tray.jsx +87 -0
  98. package/registry/blocks/cart/README.md +146 -0
  99. package/registry/blocks/cart/block.json +10 -0
  100. package/registry/blocks/cart/cart.jsx +597 -0
  101. package/registry/blocks/cart/demo.jsx +98 -0
  102. package/registry/blocks/cart/index.js +4 -0
  103. package/registry/blocks/cart/money.js +28 -0
  104. package/registry/blocks/chart/README.md +316 -0
  105. package/registry/blocks/chart/block.json +10 -0
  106. package/registry/blocks/chart/cartesian.jsx +642 -0
  107. package/registry/blocks/chart/core.jsx +262 -0
  108. package/registry/blocks/chart/demo.jsx +357 -0
  109. package/registry/blocks/chart/heatmap.jsx +336 -0
  110. package/registry/blocks/chart/index.js +8 -0
  111. package/registry/blocks/chart/pie.jsx +245 -0
  112. package/registry/blocks/chart/sparkline.jsx +88 -0
  113. package/registry/blocks/chat/README.md +273 -0
  114. package/registry/blocks/chat/attachment.jsx +95 -0
  115. package/registry/blocks/chat/block.json +10 -0
  116. package/registry/blocks/chat/chat.jsx +132 -0
  117. package/registry/blocks/chat/composer.jsx +364 -0
  118. package/registry/blocks/chat/demo.jsx +578 -0
  119. package/registry/blocks/chat/header.jsx +34 -0
  120. package/registry/blocks/chat/index.js +9 -0
  121. package/registry/blocks/chat/message.jsx +450 -0
  122. package/registry/blocks/chat/scroller.jsx +247 -0
  123. package/registry/blocks/chat/text.jsx +104 -0
  124. package/registry/blocks/code-editor/README.md +210 -0
  125. package/registry/blocks/code-editor/block.json +22 -0
  126. package/registry/blocks/code-editor/code-editor.jsx +534 -0
  127. package/registry/blocks/code-editor/codemirror.js +38 -0
  128. package/registry/blocks/code-editor/demo.jsx +205 -0
  129. package/registry/blocks/code-editor/index.js +8 -0
  130. package/registry/blocks/code-editor/json.js +317 -0
  131. package/registry/blocks/code-editor/sql.js +136 -0
  132. package/registry/blocks/code-editor/text.js +101 -0
  133. package/registry/blocks/code-editor/theme.js +186 -0
  134. package/registry/blocks/comment-box/README.md +123 -0
  135. package/registry/blocks/comment-box/block.json +10 -0
  136. package/registry/blocks/comment-box/comment-body.jsx +217 -0
  137. package/registry/blocks/comment-box/comment-box.jsx +198 -0
  138. package/registry/blocks/comment-box/comment-time.jsx +77 -0
  139. package/registry/blocks/comment-box/demo.jsx +141 -0
  140. package/registry/blocks/comment-box/index.js +8 -0
  141. package/registry/blocks/contact-section/README.md +152 -0
  142. package/registry/blocks/contact-section/block.json +11 -0
  143. package/registry/blocks/contact-section/contact-section.jsx +509 -0
  144. package/registry/blocks/contact-section/demo.jsx +93 -0
  145. package/registry/blocks/contact-section/index.js +4 -0
  146. package/registry/blocks/csv-import/README.md +133 -0
  147. package/registry/blocks/csv-import/block.json +13 -0
  148. package/registry/blocks/csv-import/csv-import.jsx +479 -0
  149. package/registry/blocks/csv-import/demo.jsx +106 -0
  150. package/registry/blocks/csv-import/import.js +200 -0
  151. package/registry/blocks/csv-import/index.js +7 -0
  152. package/registry/blocks/data-table/README.md +386 -0
  153. package/registry/blocks/data-table/block.json +10 -0
  154. package/registry/blocks/data-table/data-table.jsx +1164 -0
  155. package/registry/blocks/data-table/demo.jsx +588 -0
  156. package/registry/blocks/data-table/index.js +5 -0
  157. package/registry/blocks/data-table/model.js +169 -0
  158. package/registry/blocks/diagram/README.md +108 -0
  159. package/registry/blocks/diagram/block.json +13 -0
  160. package/registry/blocks/diagram/demo.jsx +113 -0
  161. package/registry/blocks/diagram/diagram.jsx +481 -0
  162. package/registry/blocks/diagram/index.js +7 -0
  163. package/registry/blocks/diagram/mermaid.js +52 -0
  164. package/registry/blocks/diagram/palette.js +313 -0
  165. package/registry/blocks/diagram/source.js +98 -0
  166. package/registry/blocks/doc-editor/README.md +215 -0
  167. package/registry/blocks/doc-editor/block.json +15 -0
  168. package/registry/blocks/doc-editor/demo.jsx +225 -0
  169. package/registry/blocks/doc-editor/doc-editor.jsx +291 -0
  170. package/registry/blocks/doc-editor/doc.js +309 -0
  171. package/registry/blocks/doc-editor/index.js +7 -0
  172. package/registry/blocks/doc-editor/schema.jsx +56 -0
  173. package/registry/blocks/doc-editor/shadcn-classes.js +110 -0
  174. package/registry/blocks/doc-editor/theme.js +82 -0
  175. package/registry/blocks/doc-page/README.md +119 -0
  176. package/registry/blocks/doc-page/autosave.js +104 -0
  177. package/registry/blocks/doc-page/block.json +10 -0
  178. package/registry/blocks/doc-page/demo.jsx +137 -0
  179. package/registry/blocks/doc-page/doc-page.jsx +276 -0
  180. package/registry/blocks/doc-page/index.js +4 -0
  181. package/registry/blocks/email-render/README.md +151 -0
  182. package/registry/blocks/email-render/block.json +14 -0
  183. package/registry/blocks/email-render/demo.jsx +153 -0
  184. package/registry/blocks/email-render/doc-email.jsx +210 -0
  185. package/registry/blocks/email-render/email.jsx +221 -0
  186. package/registry/blocks/email-render/index.js +10 -0
  187. package/registry/blocks/email-render/render.js +50 -0
  188. package/registry/blocks/email-render/theme.js +215 -0
  189. package/registry/blocks/faq/README.md +101 -0
  190. package/registry/blocks/faq/block.json +11 -0
  191. package/registry/blocks/faq/demo.jsx +89 -0
  192. package/registry/blocks/faq/faq.jsx +220 -0
  193. package/registry/blocks/faq/index.js +4 -0
  194. package/registry/blocks/file-upload/README.md +138 -0
  195. package/registry/blocks/file-upload/block.json +10 -0
  196. package/registry/blocks/file-upload/demo.jsx +204 -0
  197. package/registry/blocks/file-upload/file-upload.jsx +635 -0
  198. package/registry/blocks/file-upload/index.js +4 -0
  199. package/registry/blocks/flow-canvas/README.md +420 -0
  200. package/registry/blocks/flow-canvas/block.json +13 -0
  201. package/registry/blocks/flow-canvas/board.js +819 -0
  202. package/registry/blocks/flow-canvas/chrome.jsx +649 -0
  203. package/registry/blocks/flow-canvas/context.jsx +35 -0
  204. package/registry/blocks/flow-canvas/demo.jsx +249 -0
  205. package/registry/blocks/flow-canvas/edges.jsx +107 -0
  206. package/registry/blocks/flow-canvas/flow-canvas.jsx +1329 -0
  207. package/registry/blocks/flow-canvas/index.js +41 -0
  208. package/registry/blocks/flow-canvas/nodes.jsx +392 -0
  209. package/registry/blocks/flow-canvas/theme.js +174 -0
  210. package/registry/blocks/footer/README.md +133 -0
  211. package/registry/blocks/footer/block.json +11 -0
  212. package/registry/blocks/footer/demo.jsx +119 -0
  213. package/registry/blocks/footer/footer.jsx +406 -0
  214. package/registry/blocks/footer/index.js +4 -0
  215. package/registry/blocks/gantt/README.md +220 -0
  216. package/registry/blocks/gantt/block.json +10 -0
  217. package/registry/blocks/gantt/demo.jsx +148 -0
  218. package/registry/blocks/gantt/gantt.jsx +923 -0
  219. package/registry/blocks/gantt/index.js +7 -0
  220. package/registry/blocks/gantt/rows.jsx +468 -0
  221. package/registry/blocks/gantt/schedule.js +278 -0
  222. package/registry/blocks/job-panel/README.md +80 -0
  223. package/registry/blocks/job-panel/block.json +3 -3
  224. package/registry/blocks/job-panel/demo.jsx +230 -3
  225. package/registry/blocks/job-panel/job-panel.jsx +169 -3
  226. package/registry/blocks/json-editor/README.md +133 -0
  227. package/registry/blocks/json-editor/block.json +10 -0
  228. package/registry/blocks/json-editor/demo.jsx +92 -0
  229. package/registry/blocks/json-editor/index.js +7 -0
  230. package/registry/blocks/json-editor/json-editor.jsx +305 -0
  231. package/registry/blocks/json-editor/json-tree.jsx +393 -0
  232. package/registry/blocks/json-editor/tree.js +225 -0
  233. package/registry/blocks/map/README.md +221 -0
  234. package/registry/blocks/map/block.json +14 -0
  235. package/registry/blocks/map/demo.jsx +201 -0
  236. package/registry/blocks/map/geo.js +198 -0
  237. package/registry/blocks/map/index.js +8 -0
  238. package/registry/blocks/map/list.jsx +128 -0
  239. package/registry/blocks/map/map-view.jsx +715 -0
  240. package/registry/blocks/map/maplibre.js +30 -0
  241. package/registry/blocks/map/marks.jsx +146 -0
  242. package/registry/blocks/map/theme.js +142 -0
  243. package/registry/blocks/menubar/README.md +109 -0
  244. package/registry/blocks/menubar/block.json +10 -0
  245. package/registry/blocks/menubar/demo.jsx +128 -0
  246. package/registry/blocks/menubar/index.js +4 -0
  247. package/registry/blocks/menubar/menubar.jsx +199 -0
  248. package/registry/blocks/mind-map/README.md +206 -0
  249. package/registry/blocks/mind-map/block.json +10 -0
  250. package/registry/blocks/mind-map/demo.jsx +103 -0
  251. package/registry/blocks/mind-map/index.js +31 -0
  252. package/registry/blocks/mind-map/mind-map.jsx +1125 -0
  253. package/registry/blocks/mind-map/topic.jsx +143 -0
  254. package/registry/blocks/mind-map/tree.js +645 -0
  255. package/registry/blocks/navigation-menu/README.md +124 -0
  256. package/registry/blocks/navigation-menu/block.json +10 -0
  257. package/registry/blocks/navigation-menu/demo.jsx +84 -0
  258. package/registry/blocks/navigation-menu/index.js +4 -0
  259. package/registry/blocks/navigation-menu/navigation-menu.jsx +448 -0
  260. package/registry/blocks/order-detail/README.md +109 -0
  261. package/registry/blocks/order-detail/block.json +10 -0
  262. package/registry/blocks/order-detail/demo.jsx +88 -0
  263. package/registry/blocks/order-detail/index.js +4 -0
  264. package/registry/blocks/order-detail/order-detail.jsx +354 -0
  265. package/registry/blocks/org-chart/README.md +145 -0
  266. package/registry/blocks/org-chart/block.json +10 -0
  267. package/registry/blocks/org-chart/demo.jsx +94 -0
  268. package/registry/blocks/org-chart/index.js +7 -0
  269. package/registry/blocks/org-chart/org-chart.jsx +631 -0
  270. package/registry/blocks/org-chart/org.js +245 -0
  271. package/registry/blocks/org-chart/person.jsx +96 -0
  272. package/registry/blocks/page-builder/README.md +257 -0
  273. package/registry/blocks/page-builder/block.json +14 -0
  274. package/registry/blocks/page-builder/demo.jsx +145 -0
  275. package/registry/blocks/page-builder/grapes.js +18 -0
  276. package/registry/blocks/page-builder/index.js +11 -0
  277. package/registry/blocks/page-builder/page-builder.jsx +704 -0
  278. package/registry/blocks/page-builder/page-view.jsx +33 -0
  279. package/registry/blocks/page-builder/page.js +118 -0
  280. package/registry/blocks/page-builder/panels.jsx +588 -0
  281. package/registry/blocks/page-builder/sections.js +209 -0
  282. package/registry/blocks/page-builder/theme.js +87 -0
  283. package/registry/blocks/page-builder/thumbs.jsx +189 -0
  284. package/registry/blocks/page-builder/tokens.js +329 -0
  285. package/registry/blocks/payment-methods/README.md +104 -0
  286. package/registry/blocks/payment-methods/block.json +10 -0
  287. package/registry/blocks/payment-methods/demo.jsx +71 -0
  288. package/registry/blocks/payment-methods/index.js +4 -0
  289. package/registry/blocks/payment-methods/payment-methods.jsx +302 -0
  290. package/registry/blocks/pdf-document/README.md +213 -0
  291. package/registry/blocks/pdf-document/block.json +13 -0
  292. package/registry/blocks/pdf-document/demo.jsx +136 -0
  293. package/registry/blocks/pdf-document/index.js +33 -0
  294. package/registry/blocks/pdf-document/money.js +83 -0
  295. package/registry/blocks/pdf-document/pdf-document.jsx +144 -0
  296. package/registry/blocks/pdf-document/primitives.jsx +271 -0
  297. package/registry/blocks/pdf-document/quote.jsx +179 -0
  298. package/registry/blocks/pdf-document/theme.js +193 -0
  299. package/registry/blocks/pdf-viewer/README.md +191 -0
  300. package/registry/blocks/pdf-viewer/block.json +14 -0
  301. package/registry/blocks/pdf-viewer/demo.jsx +252 -0
  302. package/registry/blocks/pdf-viewer/helpers.js +270 -0
  303. package/registry/blocks/pdf-viewer/index.js +8 -0
  304. package/registry/blocks/pdf-viewer/notes.jsx +195 -0
  305. package/registry/blocks/pdf-viewer/pdf-viewer.jsx +1368 -0
  306. package/registry/blocks/pdf-viewer/pdfjs.js +37 -0
  307. package/registry/blocks/pdf-viewer/ssr-shim-end.js +6 -0
  308. package/registry/blocks/pdf-viewer/ssr-shim.js +44 -0
  309. package/registry/blocks/product-detail/README.md +217 -0
  310. package/registry/blocks/product-detail/block.json +10 -0
  311. package/registry/blocks/product-detail/demo.jsx +233 -0
  312. package/registry/blocks/product-detail/index.js +7 -0
  313. package/registry/blocks/product-detail/price.jsx +115 -0
  314. package/registry/blocks/product-detail/product-detail.jsx +348 -0
  315. package/registry/blocks/product-detail/product-media.jsx +102 -0
  316. package/registry/blocks/product-detail/product-options.jsx +144 -0
  317. package/registry/blocks/product-grid/README.md +162 -0
  318. package/registry/blocks/product-grid/block.json +10 -0
  319. package/registry/blocks/product-grid/demo.jsx +255 -0
  320. package/registry/blocks/product-grid/index.js +5 -0
  321. package/registry/blocks/product-grid/product-card.jsx +232 -0
  322. package/registry/blocks/product-grid/product-grid.jsx +235 -0
  323. package/registry/blocks/record-form/README.md +196 -0
  324. package/registry/blocks/record-form/block.json +10 -0
  325. package/registry/blocks/record-form/demo.jsx +525 -0
  326. package/registry/blocks/record-form/index.js +4 -0
  327. package/registry/blocks/record-form/record-fields.jsx +457 -0
  328. package/registry/blocks/record-form/record-form.jsx +415 -0
  329. package/registry/blocks/record-form/rules.js +81 -0
  330. package/registry/blocks/record-notes/README.md +139 -0
  331. package/registry/blocks/record-notes/block.json +10 -0
  332. package/registry/blocks/record-notes/demo.jsx +179 -0
  333. package/registry/blocks/record-notes/index.js +4 -0
  334. package/registry/blocks/record-notes/record-notes.jsx +266 -0
  335. package/registry/blocks/search-dialog/README.md +108 -0
  336. package/registry/blocks/search-dialog/block.json +10 -0
  337. package/registry/blocks/search-dialog/demo.jsx +116 -0
  338. package/registry/blocks/search-dialog/index.js +3 -0
  339. package/registry/blocks/search-dialog/search-dialog.jsx +235 -0
  340. package/registry/blocks/settings-list/README.md +171 -0
  341. package/registry/blocks/settings-list/block.json +10 -0
  342. package/registry/blocks/settings-list/demo.jsx +333 -0
  343. package/registry/blocks/settings-list/index.js +4 -0
  344. package/registry/blocks/settings-list/row-state.jsx +93 -0
  345. package/registry/blocks/settings-list/settings-list.jsx +349 -0
  346. package/registry/blocks/settings-list/settings-matrix.jsx +118 -0
  347. package/registry/blocks/setup-checklist/README.md +142 -0
  348. package/registry/blocks/setup-checklist/block.json +10 -0
  349. package/registry/blocks/setup-checklist/demo.jsx +159 -0
  350. package/registry/blocks/setup-checklist/index.js +4 -0
  351. package/registry/blocks/setup-checklist/setup-checklist.jsx +446 -0
  352. package/registry/blocks/share-dialog/README.md +113 -0
  353. package/registry/blocks/share-dialog/block.json +10 -0
  354. package/registry/blocks/share-dialog/demo.jsx +140 -0
  355. package/registry/blocks/share-dialog/index.js +3 -0
  356. package/registry/blocks/share-dialog/share-dialog.jsx +384 -0
  357. package/registry/blocks/sidebar/README.md +190 -0
  358. package/registry/blocks/sidebar/block.json +43 -0
  359. package/registry/blocks/sidebar/demo.jsx +577 -0
  360. package/registry/blocks/sidebar/index.js +4 -0
  361. package/registry/blocks/sidebar/sidebar-panel.jsx +149 -0
  362. package/registry/blocks/sidebar/sidebar.jsx +864 -0
  363. package/registry/blocks/signature-pad/README.md +137 -0
  364. package/registry/blocks/signature-pad/block.json +13 -0
  365. package/registry/blocks/signature-pad/colors.js +115 -0
  366. package/registry/blocks/signature-pad/demo.jsx +94 -0
  367. package/registry/blocks/signature-pad/index.js +6 -0
  368. package/registry/blocks/signature-pad/pad.js +7 -0
  369. package/registry/blocks/signature-pad/signature-pad.jsx +434 -0
  370. package/registry/blocks/signature-pad/signature.js +68 -0
  371. package/registry/blocks/sketch-canvas/README.md +280 -0
  372. package/registry/blocks/sketch-canvas/block.json +13 -0
  373. package/registry/blocks/sketch-canvas/demo.jsx +159 -0
  374. package/registry/blocks/sketch-canvas/excalidraw.js +40 -0
  375. package/registry/blocks/sketch-canvas/index.js +9 -0
  376. package/registry/blocks/sketch-canvas/palette.js +226 -0
  377. package/registry/blocks/sketch-canvas/sketch-canvas.jsx +1188 -0
  378. package/registry/blocks/sketch-canvas/sketch.js +179 -0
  379. package/registry/blocks/sketch-canvas/ssr-shim-end.js +6 -0
  380. package/registry/blocks/sketch-canvas/ssr-shim.js +45 -0
  381. package/registry/blocks/sketch-canvas/stamps.js +104 -0
  382. package/registry/blocks/sketch-canvas/theme.js +131 -0
  383. package/registry/blocks/spreadsheet/README.md +307 -0
  384. package/registry/blocks/spreadsheet/block.json +13 -0
  385. package/registry/blocks/spreadsheet/cells.jsx +455 -0
  386. package/registry/blocks/spreadsheet/chrome.jsx +325 -0
  387. package/registry/blocks/spreadsheet/demo.jsx +177 -0
  388. package/registry/blocks/spreadsheet/grid.js +387 -0
  389. package/registry/blocks/spreadsheet/index.js +34 -0
  390. package/registry/blocks/spreadsheet/spreadsheet.jsx +1732 -0
  391. package/registry/blocks/spreadsheet/types.js +473 -0
  392. package/registry/blocks/stat-cards/README.md +173 -0
  393. package/registry/blocks/stat-cards/block.json +10 -0
  394. package/registry/blocks/stat-cards/demo.jsx +257 -0
  395. package/registry/blocks/stat-cards/index.js +4 -0
  396. package/registry/blocks/stat-cards/stat-cards.jsx +307 -0
  397. package/registry/blocks/stat-cards/usage.jsx +243 -0
  398. package/registry/blocks/task-list/README.md +176 -0
  399. package/registry/blocks/task-list/block.json +10 -0
  400. package/registry/blocks/task-list/demo.jsx +134 -0
  401. package/registry/blocks/task-list/index.js +5 -0
  402. package/registry/blocks/task-list/task-list.jsx +907 -0
  403. package/registry/blocks/task-model/README.md +455 -0
  404. package/registry/blocks/task-model/block.json +167 -0
  405. package/registry/blocks/task-model/changes.js +89 -0
  406. package/registry/blocks/task-model/dates.js +371 -0
  407. package/registry/blocks/task-model/demo.jsx +215 -0
  408. package/registry/blocks/task-model/dnd.jsx +240 -0
  409. package/registry/blocks/task-model/index.js +97 -0
  410. package/registry/blocks/task-model/model.js +919 -0
  411. package/registry/blocks/task-model/parts.jsx +449 -0
  412. package/registry/blocks/task-model/toolbar.jsx +271 -0
  413. package/registry/blocks/testimonials/README.md +139 -0
  414. package/registry/blocks/testimonials/block.json +11 -0
  415. package/registry/blocks/testimonials/demo.jsx +137 -0
  416. package/registry/blocks/testimonials/index.js +4 -0
  417. package/registry/blocks/testimonials/testimonials.jsx +430 -0
  418. package/registry/blocks/transaction-list/README.md +126 -0
  419. package/registry/blocks/transaction-list/block.json +10 -0
  420. package/registry/blocks/transaction-list/demo.jsx +79 -0
  421. package/registry/blocks/transaction-list/index.js +4 -0
  422. package/registry/blocks/transaction-list/transaction-list.jsx +383 -0
  423. package/registry/blocks/whiteboard/README.md +179 -0
  424. package/registry/blocks/whiteboard/block.json +10 -0
  425. package/registry/blocks/whiteboard/demo.jsx +169 -0
  426. package/registry/blocks/whiteboard/index.js +6 -0
  427. package/registry/blocks/whiteboard/templates.js +150 -0
  428. package/registry/blocks/whiteboard/whiteboard.jsx +721 -0
  429. package/src/cli.mjs +19 -6
  430. package/src/install.mjs +18 -0
  431. package/src/registry.mjs +38 -4
  432. package/src/shadcn.mjs +14 -1
  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/ui/avatar.jsx +141 -0
  437. package/templates/next/files/components/ui/breadcrumb.jsx +149 -0
  438. package/templates/next/files/components/ui/button-group.jsx +70 -0
  439. package/templates/next/files/components/ui/button.jsx +132 -12
  440. package/templates/next/files/components/ui/calendar.jsx +609 -0
  441. package/templates/next/files/components/ui/carousel.jsx +281 -0
  442. package/templates/next/files/components/ui/collapsible.jsx +208 -0
  443. package/templates/next/files/components/ui/combobox.jsx +608 -0
  444. package/templates/next/files/components/ui/command.jsx +577 -0
  445. package/templates/next/files/components/ui/context-menu.jsx +191 -0
  446. package/templates/next/files/components/ui/date-picker.jsx +360 -0
  447. package/templates/next/files/components/ui/distribution.jsx +117 -0
  448. package/templates/next/files/components/ui/feedback.jsx +143 -0
  449. package/templates/next/files/components/ui/field.jsx +115 -3
  450. package/templates/next/files/components/ui/file-input.jsx +234 -0
  451. package/templates/next/files/components/ui/icon.jsx +130 -8
  452. package/templates/next/files/components/ui/image.jsx +143 -0
  453. package/templates/next/files/components/ui/index.js +47 -2
  454. package/templates/next/files/components/ui/input-group.jsx +213 -0
  455. package/templates/next/files/components/ui/input-otp.jsx +170 -0
  456. package/templates/next/files/components/ui/item.jsx +206 -0
  457. package/templates/next/files/components/ui/menu.jsx +454 -82
  458. package/templates/next/files/components/ui/notice.jsx +126 -14
  459. package/templates/next/files/components/ui/pagination.jsx +156 -0
  460. package/templates/next/files/components/ui/popover.jsx +284 -0
  461. package/templates/next/files/components/ui/radio.jsx +193 -0
  462. package/templates/next/files/components/ui/rating.jsx +131 -0
  463. package/templates/next/files/components/ui/resizable.jsx +338 -0
  464. package/templates/next/files/components/ui/scroll-area.jsx +123 -0
  465. package/templates/next/files/components/ui/separator.jsx +119 -0
  466. package/templates/next/files/components/ui/sheet.jsx +96 -11
  467. package/templates/next/files/components/ui/slider.jsx +174 -0
  468. package/templates/next/files/components/ui/stat.jsx +574 -0
  469. package/templates/next/files/components/ui/steps.jsx +338 -0
  470. package/templates/next/files/components/ui/swatch.jsx +33 -0
  471. package/templates/next/files/components/ui/table.jsx +130 -0
  472. package/templates/next/files/components/ui/toast.jsx +440 -0
  473. package/templates/next/files/components/ui/toggle.jsx +195 -0
  474. package/templates/next/files/components/ui/tooltip.jsx +270 -0
  475. package/templates/next/template.json +264 -8
@@ -0,0 +1,907 @@
1
+ "use client";
2
+
3
+ import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
4
+ import { Button, Checkbox, Empty, Icon, IconButton, Input, Menu, Notice, Skeleton, cx, toast } from "../../ui";
5
+ import {
6
+ AssigneeStack,
7
+ CompleteToggle,
8
+ DEFAULT_VIEW,
9
+ DragHandle,
10
+ DueLabel,
11
+ GroupLabel,
12
+ LIFTED,
13
+ NONE,
14
+ PriorityFlag,
15
+ ProgressLine,
16
+ SortableGroup,
17
+ StatusPill,
18
+ SubtaskCount,
19
+ TagList,
20
+ TaskDragProvider,
21
+ ViewToolbar,
22
+ applyView,
23
+ createTaskContext,
24
+ deleteChange,
25
+ draftTask,
26
+ flattenTree,
27
+ formatDay,
28
+ isDone,
29
+ movePatch,
30
+ placeChange,
31
+ subtaskCounts,
32
+ sumOf,
33
+ toggleDoneChange,
34
+ useDropTarget,
35
+ useSortableTask,
36
+ useTaskChanges,
37
+ } from "../task-model";
38
+
39
+ // TaskList — the List view of work: tasks under headings that fold (by
40
+ // status, assignee, priority, project, due date… — any field), each row
41
+ // a round check to complete it, its title edited in place, its subtasks
42
+ // indented under it, the fields the view shows (people, due date,
43
+ // priority…) as pickers, and a menu. A quick-add row ends every group;
44
+ // rows drag to reorder and to another group (which saves the group's
45
+ // field: a new status, a new assignee, a new due date). Tick several and
46
+ // a bar acts on them together; a delete can be undone from its toast.
47
+ //
48
+ // Everything saves through one async `onChange(change)` answering { ok }
49
+ // (the change shapes are task-model's); the list shows each change at
50
+ // once and takes it back, with a notice, when the save fails.
51
+ //
52
+ // README.md beside this file lists every prop.
53
+
54
+ function useViewState(value, defaultValue, onChange) {
55
+ const [own, setOwn] = useState(() => ({ ...DEFAULT_VIEW, ...defaultValue }));
56
+ const controlled = value !== undefined;
57
+ const view = controlled ? { ...DEFAULT_VIEW, ...value } : own;
58
+ const set = useCallback(
59
+ (next) => {
60
+ if (!controlled) setOwn(next);
61
+ onChange?.(next);
62
+ },
63
+ [controlled, onChange],
64
+ );
65
+ return [view, set];
66
+ }
67
+
68
+ const sid = (group, id) => `${group}::${id}`;
69
+
70
+ export function TaskList({
71
+ tasks,
72
+ statuses,
73
+ people,
74
+ projects,
75
+ fields,
76
+ today,
77
+ weekStartsOn,
78
+ onChange,
79
+ onOpen,
80
+ view: viewProp,
81
+ defaultView,
82
+ onViewChange,
83
+ toolbar = true,
84
+ toolbarExtra,
85
+ addLabel = "Add task",
86
+ starters,
87
+ emptyTitle = "No tasks yet",
88
+ emptyBody = "Add the first thing that needs doing. Press Enter to add the next one.",
89
+ loading = false,
90
+ error: readError,
91
+ label = "Tasks",
92
+ className,
93
+ }) {
94
+ const uid = useId();
95
+ const regionRef = useRef(null);
96
+ const ctx = useMemo(
97
+ () => createTaskContext({ statuses, people, projects, fields, today, weekStartsOn }),
98
+ [statuses, people, projects, fields, today, weekStartsOn],
99
+ );
100
+ const editable = typeof onChange === "function";
101
+ const { tasks: all, commit, error, clearError } = useTaskChanges(tasks, onChange);
102
+ const [view, setView] = useViewState(viewProp, defaultView, onViewChange);
103
+ const shown = useMemo(() => applyView(all, view, ctx), [all, view, ctx]);
104
+ const counts = useMemo(() => subtaskCounts(all, ctx), [all, ctx]);
105
+ const titleOf = useMemo(() => new Map(all.map((t) => [t.id, t.title])), [all]);
106
+ const manual = !view.sort?.length;
107
+ const by = view.groupBy && view.groupBy !== "none" ? view.groupBy : null;
108
+ const show = new Set(view.fields ?? []);
109
+
110
+ const [folded, setFolded] = useState(() => new Set());
111
+ const [collapsed, setCollapsed] = useState(() => new Set());
112
+ const [selected, setSelected] = useState(() => new Set());
113
+ const lastPicked = useRef(null);
114
+ const [editing, setEditing] = useState(null);
115
+ const [adding, setAdding] = useState(null); // { group, parentId? }
116
+ const [overGroup, setOverGroup] = useState(null);
117
+ const [announce, setAnnounce] = useState("");
118
+ const [started, setStarted] = useState(false);
119
+
120
+ // Drop selections of tasks that are gone.
121
+ useEffect(() => {
122
+ setSelected((s) => {
123
+ if (!s.size) return s;
124
+ const ids = new Set(all.map((t) => t.id));
125
+ const next = new Set([...s].filter((id) => ids.has(id)));
126
+ return next.size === s.size ? s : next;
127
+ });
128
+ }, [all]);
129
+
130
+ // Rows per group, nested, in reading order: the model for the screen,
131
+ // for shift-click ranges and for the arrow keys.
132
+ const groups = useMemo(
133
+ () =>
134
+ shown.groups.map((g) => {
135
+ const rows = flattenTree(g.tasks, { collapsed });
136
+ // Each top-level row with the rows under it: dragged as one.
137
+ const blocks = [];
138
+ for (const r of rows) {
139
+ if (r.depth === 0) blocks.push({ root: r, rest: [] });
140
+ else blocks[blocks.length - 1]?.rest.push(r);
141
+ }
142
+ return { ...g, rows, blocks, roots: blocks.map((b) => b.root.task) };
143
+ }),
144
+ [shown.groups, collapsed],
145
+ );
146
+ const order = useMemo(() => groups.flatMap((g) => (folded.has(g.key) ? [] : g.rows.map((r) => r.task.id))), [groups, folded]);
147
+ const selectMode = selected.size > 0;
148
+
149
+ const say = (text) => setAnnounce(text);
150
+ const name = (t) => `“${t.title || "Untitled"}”`;
151
+
152
+ function groupPatch(key) {
153
+ if (!by) return {};
154
+ return movePatch({ assignees: [], tags: [] }, by, NONE, key, ctx);
155
+ }
156
+
157
+ function update(task, patch, words) {
158
+ return commit({ type: "update", id: task.id, patch }, { label: name(task) }).then((r) => {
159
+ if (r.ok !== false && words) say(words);
160
+ return r;
161
+ });
162
+ }
163
+
164
+ async function create(title, group, parentId) {
165
+ const patch = groupPatch(group);
166
+ if (patch === null) return { ok: false };
167
+ const g = groups.find((x) => x.key === group);
168
+ const siblings = parentId ? all.filter((t) => t.parentId === parentId) : g?.roots ?? [];
169
+ const task = draftTask(title, ctx, { ...patch, ...(parentId ? { parentId } : {}) }, { after: siblings[siblings.length - 1] });
170
+ if (parentId) setCollapsed((s) => (s.has(parentId) ? new Set([...s].filter((x) => x !== parentId)) : s));
171
+ const r = await commit({ type: "create", task }, { label: name(task) });
172
+ if (r.ok !== false) say(`Added ${name(task)}${g && by ? ` to ${g.label}` : ""}`);
173
+ return r;
174
+ }
175
+
176
+ function remove(ids) {
177
+ const change = deleteChange(all, ids);
178
+ const n = ids.length;
179
+ const one = n === 1 ? all.find((t) => t.id === ids[0]) : null;
180
+ setSelected(new Set());
181
+ return commit(change, { label: one ? name(one) : `${n} tasks`, undo: one ? `Deleted ${name(one)}` : `Deleted ${n} tasks` });
182
+ }
183
+
184
+ function toggleSelect(id, { range = false } = {}) {
185
+ setSelected((s) => {
186
+ const next = new Set(s);
187
+ if (range && lastPicked.current && order.includes(lastPicked.current)) {
188
+ const a = order.indexOf(lastPicked.current);
189
+ const b = order.indexOf(id);
190
+ const [lo, hi] = a < b ? [a, b] : [b, a];
191
+ for (const x of order.slice(lo, hi + 1)) next.add(x);
192
+ } else if (next.has(id)) next.delete(id);
193
+ else next.add(id);
194
+ return next;
195
+ });
196
+ lastPicked.current = id;
197
+ }
198
+
199
+ function onDragEnd({ active, over }) {
200
+ setOverGroup(null);
201
+ if (!active || !over || !editable) return;
202
+ const task = active.data.task;
203
+ const from = active.data.group;
204
+ const to = over.data.group;
205
+ if (!task || to == null) return;
206
+ const g = groups.find((x) => x.key === to);
207
+ if (!g) return;
208
+ const patch = from === to ? {} : movePatch(task, by, from, to, ctx);
209
+ if (patch === null) {
210
+ toast.info(`${name(task)} stays where it was`, { description: `Tasks cannot be moved into ${g.label}. Give it a due date instead.` });
211
+ say(`${name(task)} cannot go to ${g.label}`);
212
+ return;
213
+ }
214
+ let change;
215
+ if (manual) {
216
+ const ids = g.roots.map((t) => t.id);
217
+ const list = g.roots.filter((t) => t.id !== task.id);
218
+ let index = list.length;
219
+ if (over.data.task) {
220
+ const overIndex = ids.indexOf(over.data.task.id);
221
+ const activeIndex = ids.indexOf(task.id);
222
+ index = from === to && activeIndex >= 0 && activeIndex < overIndex ? overIndex : list.findIndex((t) => t.id === over.data.task.id);
223
+ if (index < 0) index = list.length;
224
+ }
225
+ if (from === to && ids.indexOf(task.id) === index) return;
226
+ change = placeChange(list, task, index, patch);
227
+ } else {
228
+ if (from === to) return;
229
+ change = { type: "update", id: task.id, patch };
230
+ }
231
+ commit(change, { label: name(task) }).then((r) => {
232
+ if (r.ok !== false) say(from === to ? `${name(task)} moved` : `${name(task)} moved to ${g.label}`);
233
+ });
234
+ }
235
+
236
+ const startAdding = (group, parentId) => {
237
+ setStarted(true);
238
+ if (group && folded.has(group)) setFolded((s) => new Set([...s].filter((x) => x !== group)));
239
+ setAdding({ group: group ?? groups.find((g) => groupPatch(g.key) !== null)?.key ?? "all", parentId });
240
+ };
241
+
242
+ const empty = !loading && all.length === 0 && !started;
243
+ const noMatches = !loading && all.length > 0 && shown.tasks.length === 0;
244
+ // Only "hide done" is on and it hid everything: that is good news.
245
+ const allDone = noMatches && view.hideDone && !view.search && !Object.values(view.filters ?? {}).some((v) => v?.length);
246
+ const clearView = () => setView({ ...view, search: "", filters: {}, hideDone: false });
247
+
248
+ return (
249
+ <div className={cx("grid min-w-0 grid-cols-1 gap-3", className)}>
250
+ {toolbar && !empty ? (
251
+ <ViewToolbar
252
+ view={view}
253
+ onViewChange={setView}
254
+ ctx={ctx}
255
+ tasks={all}
256
+ count={shown.tasks.length}
257
+ extra={
258
+ <>
259
+ {toolbarExtra}
260
+ {editable ? (
261
+ <Button variant="primary" size="sm" icon="plus" onClick={() => startAdding(adding?.group)}>
262
+ <span className="sr-only sm:not-sr-only">{addLabel}</span>
263
+ </Button>
264
+ ) : null}
265
+ </>
266
+ }
267
+ />
268
+ ) : null}
269
+ {readError ? <Notice tone="danger">{readError}</Notice> : null}
270
+ {error ? (
271
+ <Notice tone="danger" onDismiss={clearError}>
272
+ {error}
273
+ </Notice>
274
+ ) : null}
275
+
276
+ {loading ? (
277
+ <div className="grid gap-2 rounded-lg border border-border bg-card p-4" aria-busy="true" aria-label="Loading tasks">
278
+ <Skeleton className="w-32" />
279
+ {[0, 1, 2, 3, 4].map((i) => (
280
+ <div key={i} className="flex items-center gap-3 py-1.5">
281
+ <Skeleton shape="circle" className="size-4" />
282
+ <Skeleton className={["w-2/3", "w-1/2", "w-3/5", "w-2/5", "w-1/2"][i]} />
283
+ </div>
284
+ ))}
285
+ </div>
286
+ ) : empty ? (
287
+ <div className="rounded-lg bg-card">
288
+ <Empty
289
+ icon="circle-check"
290
+ title={emptyTitle}
291
+ body={emptyBody}
292
+ action={
293
+ editable ? (
294
+ <div className="flex flex-wrap items-center justify-center gap-2">
295
+ <Button variant="primary" icon="plus" onClick={() => startAdding()}>
296
+ {addLabel}
297
+ </Button>
298
+ {(starters ?? []).map((s) => (
299
+ <Button
300
+ key={s.label}
301
+ variant="secondary"
302
+ icon={s.icon ?? "sparkles"}
303
+ onClick={() => {
304
+ setStarted(true);
305
+ let prev = null;
306
+ const made = s.tasks.map((t) => (prev = draftTask(t.title, ctx, t, { after: prev })));
307
+ commit({ type: "create", tasks: made }, { label: `“${s.label}”` });
308
+ say(`Added ${made.length} tasks from ${s.label}`);
309
+ }}
310
+ >
311
+ {s.label}
312
+ </Button>
313
+ ))}
314
+ </div>
315
+ ) : null
316
+ }
317
+ />
318
+ </div>
319
+ ) : noMatches ? (
320
+ <div className="rounded-lg bg-card">
321
+ {allDone ? (
322
+ <Empty
323
+ icon="circle-check"
324
+ title="All done"
325
+ body="Every task here is finished. Nice work."
326
+ action={
327
+ <Button variant="secondary" onClick={clearView}>
328
+ Show done tasks
329
+ </Button>
330
+ }
331
+ />
332
+ ) : (
333
+ <Empty
334
+ icon="search"
335
+ title="No tasks match"
336
+ body={view.search ? `Nothing has “${view.search}” with these filters.` : "Nothing fits these filters."}
337
+ action={
338
+ <Button variant="secondary" onClick={clearView}>
339
+ Clear search and filters
340
+ </Button>
341
+ }
342
+ />
343
+ )}
344
+ </div>
345
+ ) : (
346
+ <TaskDragProvider
347
+ disabled={!editable}
348
+ onDragOver={({ over }) => setOverGroup(over?.data?.group ?? null)}
349
+ onDragEnd={onDragEnd}
350
+ onDragCancel={() => setOverGroup(null)}
351
+ place={(o) => (o.data.task ? `${name(o.data.task)} in ${groups.find((g) => g.key === o.data.group)?.label ?? "the list"}` : o.data.label)}
352
+ overlay={(a) => (
353
+ <div className={cx(LIFTED, "flex w-[min(28rem,85vw)] items-center gap-2 px-3 py-2 text-sm")}>
354
+ <StatusPill status={a.data.task?.status} ctx={ctx} hideLabel />
355
+ <span className="min-w-0 flex-1 truncate font-medium">{a.data.task?.title}</span>
356
+ <AssigneeStack ids={a.data.task?.assignees} ctx={ctx} max={2} />
357
+ </div>
358
+ )}
359
+ >
360
+ <div ref={regionRef} role="region" aria-label={label} className="rounded-lg border border-border bg-card text-card-foreground">
361
+ {groups.map((g, gi) => (
362
+ <Group
363
+ key={g.key}
364
+ group={g}
365
+ first={gi === 0}
366
+ single={!by}
367
+ by={by}
368
+ ctx={ctx}
369
+ uid={uid}
370
+ folded={folded.has(g.key)}
371
+ onFold={() => setFolded((s) => (s.has(g.key) ? new Set([...s].filter((x) => x !== g.key)) : new Set([...s, g.key])))}
372
+ highlight={overGroup === g.key}
373
+ canAdd={editable && groupPatch(g.key) !== null}
374
+ addLabel={addLabel}
375
+ adding={adding}
376
+ setAdding={setAdding}
377
+ onCreate={create}
378
+ startAdding={startAdding}
379
+ >
380
+ <SortableGroup ids={g.roots.map((t) => sid(g.key, t.id))}>
381
+ {g.blocks.map(({ root, rest }) => (
382
+ <SortableRow
383
+ key={root.task.id}
384
+ row={root}
385
+ group={g.key}
386
+ draggable={editable}
387
+ rowProps={rowProps(root)}
388
+ handlers={rowHandlers(root)}
389
+ regionRef={regionRef}
390
+ >
391
+ {rest.length || adding?.parentId === root.task.id ? (
392
+ <ul>
393
+ {rest.map((r) => (
394
+ <Row key={r.task.id} row={r} {...rowProps(r)} handlers={rowHandlers(r)} regionRef={regionRef}>
395
+ {adding?.parentId === r.task.id ? <SubtaskAdd parent={r} onCreate={create} onClose={() => setAdding(null)} group={g.key} /> : null}
396
+ </Row>
397
+ ))}
398
+ {adding?.parentId === root.task.id ? <SubtaskAdd parent={root} onCreate={create} onClose={() => setAdding(null)} group={g.key} /> : null}
399
+ </ul>
400
+ ) : null}
401
+ </SortableRow>
402
+ ))}
403
+ </SortableGroup>
404
+ </Group>
405
+ ))}
406
+ </div>
407
+ </TaskDragProvider>
408
+ )}
409
+
410
+ {selected.size > 0 ? (
411
+ <BulkBar
412
+ count={selected.size}
413
+ ctx={ctx}
414
+ editable={editable}
415
+ onClear={() => setSelected(new Set())}
416
+ onSelectAll={() => setSelected(new Set(order))}
417
+ allSelected={selected.size > 0 && selected.size === order.length}
418
+ apply={(patch, words) => {
419
+ const ids = [...selected];
420
+ commit({ type: "update", ids, patch }, { label: `${ids.length} tasks` }).then((r) => {
421
+ if (r.ok !== false) say(words);
422
+ });
423
+ }}
424
+ onDelete={() => remove([...selected])}
425
+ />
426
+ ) : null}
427
+
428
+ <p aria-live="polite" className="sr-only">
429
+ {announce}
430
+ </p>
431
+ </div>
432
+ );
433
+
434
+ function rowProps(r) {
435
+ return {
436
+ ctx,
437
+ show,
438
+ editable,
439
+ counts,
440
+ parentTitle: r.depth === 0 && r.task.parentId ? titleOf.get(r.task.parentId) : null,
441
+ selectMode,
442
+ selected: selected.has(r.task.id),
443
+ editing: editing === r.task.id,
444
+ };
445
+ }
446
+
447
+ function rowHandlers(r) {
448
+ const t = r.task;
449
+ return {
450
+ onToggleDone: () => {
451
+ const change = toggleDoneChange(t, ctx);
452
+ commit(change, { label: name(t) }).then((res) => {
453
+ if (res.ok !== false) say(isDone(t, ctx) ? `${name(t)} reopened` : `${name(t)} done`);
454
+ });
455
+ },
456
+ onRename: (title) => {
457
+ setEditing(null);
458
+ const next = title.trim();
459
+ if (next && next !== t.title) update(t, { title: next }, `Renamed to “${next}”`);
460
+ },
461
+ onEdit: () => setEditing(t.id),
462
+ onCancelEdit: () => setEditing(null),
463
+ onPatch: (patch) => update(t, patch),
464
+ onOpen: onOpen ? () => onOpen(t) : null,
465
+ onSelect: (range) => toggleSelect(t.id, { range }),
466
+ onFold: r.hasChildren
467
+ ? () => setCollapsed((s) => (s.has(t.id) ? new Set([...s].filter((x) => x !== t.id)) : new Set([...s, t.id])))
468
+ : null,
469
+ menu: [
470
+ onOpen && { label: "Open", icon: "maximize", onSelect: () => onOpen(t) },
471
+ editable && { label: "Rename", icon: "pencil", onSelect: () => setEditing(t.id) },
472
+ editable && r.depth < 2 && { label: "Add subtask", icon: "plus", onSelect: () => startAdding(groupOfRow(t.id), t.id) },
473
+ { label: selected.has(t.id) ? "Deselect" : "Select", icon: "check", onSelect: () => toggleSelect(t.id) },
474
+ editable && {
475
+ label: "Status",
476
+ icon: "circle-dashed",
477
+ items: ctx.statuses.filter((s) => s.id !== t.status).map((s) => ({ label: s.label, onSelect: () => update(t, { status: s.id }, `${name(t)} is ${s.label}`) })),
478
+ },
479
+ editable && t.parentId && { label: "Make it a task", icon: "arrow-left", onSelect: () => update(t, { parentId: null }, `${name(t)} is its own task`) },
480
+ editable && {
481
+ label: "Duplicate",
482
+ icon: "copy",
483
+ onSelect: () => {
484
+ const { id, ...rest } = t;
485
+ commit({ type: "create", task: draftTask(`${t.title} (copy)`, ctx, { ...rest, title: `${t.title} (copy)` }, { after: t }) }, { label: name(t) });
486
+ },
487
+ },
488
+ editable && { type: "separator" },
489
+ editable && { label: "Delete", icon: "trash", tone: "danger", onSelect: () => remove([t.id]) },
490
+ ],
491
+ };
492
+ }
493
+
494
+ function groupOfRow(id) {
495
+ return groups.find((g) => g.rows.some((r) => r.task.id === id))?.key;
496
+ }
497
+ }
498
+
499
+ // ---- a group ------------------------------------------------------------
500
+
501
+ function Group({ group, first, single, by, ctx, uid, folded, onFold, highlight, canAdd, addLabel, adding, setAdding, onCreate, startAdding, children }) {
502
+ const drop = useDropTarget(`group::${group.key}`, { group: group.key, label: group.label });
503
+ const headingId = `${uid}-g-${group.key}`;
504
+ const est = sumOf(group.tasks);
505
+ const addingHere = adding && adding.group === group.key;
506
+ return (
507
+ <section
508
+ ref={drop.setNodeRef}
509
+ aria-labelledby={single ? undefined : headingId}
510
+ aria-label={single ? "All tasks" : undefined}
511
+ className={cx("transition-colors duration-150", !first && "border-t border-border", (highlight || drop.isOver) && "bg-accent/40")}
512
+ >
513
+ {!single ? (
514
+ <header className={cx("group/head sticky top-0 z-10 flex min-h-11 items-center gap-1 bg-card pe-2 ps-1", first && "rounded-t-lg")}>
515
+ {/* raw: the whole heading folds the group; a kit Button would
516
+ centre it and weigh it as an action. */}
517
+ <button
518
+ type="button"
519
+ aria-expanded={!folded}
520
+ onClick={onFold}
521
+ className="flex min-h-11 min-w-0 flex-1 items-center gap-2 rounded-md px-2 text-left outline-none hover:bg-accent/60 focus-visible:ring-2 focus-visible:ring-ring"
522
+ >
523
+ <Icon name="chevron-right" size={14} className={cx("shrink-0 text-muted-foreground transition-transform duration-150 motion-reduce:transition-none", !folded && "rotate-90")} />
524
+ <span id={headingId} className="min-w-0">
525
+ <GroupLabel group={group} by={by} ctx={ctx} />
526
+ </span>
527
+ <span data-nums className="text-xs text-muted-foreground">
528
+ {group.tasks.length}
529
+ </span>
530
+ </button>
531
+ {est > 0 ? (
532
+ <span data-nums className="hidden text-xs text-muted-foreground sm:inline" title="Estimated hours">
533
+ {Math.round(est * 10) / 10}h
534
+ </span>
535
+ ) : null}
536
+ {canAdd ? (
537
+ <IconButton
538
+ icon="plus"
539
+ size="sm"
540
+ label={`${addLabel} to ${group.label}`}
541
+ className="opacity-100 sm:opacity-0 sm:transition-opacity sm:duration-150 sm:group-hover/head:opacity-100 sm:focus-visible:opacity-100"
542
+ onClick={() => startAdding(group.key)}
543
+ />
544
+ ) : null}
545
+ </header>
546
+ ) : null}
547
+ {!folded ? (
548
+ <>
549
+ <ul className="grid grid-cols-1">
550
+ {children}
551
+ {group.tasks.length === 0 && !addingHere ? (
552
+ <li className="px-4 py-2 text-xs text-muted-foreground sm:ps-12">{canAdd ? "Nothing here — drop a task or add one." : "Nothing here."}</li>
553
+ ) : null}
554
+ </ul>
555
+ {canAdd ? <QuickAdd group={group} adding={adding} setAdding={setAdding} onCreate={onCreate} addLabel={addLabel} uid={uid} /> : null}
556
+ </>
557
+ ) : null}
558
+ </section>
559
+ );
560
+ }
561
+
562
+ function QuickAdd({ group, adding, setAdding, onCreate, addLabel, uid }) {
563
+ const open = adding && adding.group === group.key && !adding.parentId;
564
+ const [draft, setDraft] = useState("");
565
+ const [busy, setBusy] = useState(false);
566
+ const inputId = `${uid}-add-${group.key}`;
567
+ if (!open) {
568
+ return (
569
+ <div className="px-2 pb-1.5 sm:ps-9">
570
+ <Button variant="quiet" size="sm" icon="plus" align="start" onClick={() => setAdding({ group: group.key })}>
571
+ {addLabel}
572
+ </Button>
573
+ </div>
574
+ );
575
+ }
576
+ async function submit() {
577
+ const title = draft.trim();
578
+ if (!title || busy) return;
579
+ setBusy(true);
580
+ const r = await onCreate(title, group.key);
581
+ setBusy(false);
582
+ if (r?.ok !== false) setDraft("");
583
+ }
584
+ return (
585
+ <div className="flex items-center gap-2 px-3 pb-2 sm:ps-12">
586
+ <label htmlFor={inputId} className="sr-only">
587
+ New task in {group.label}
588
+ </label>
589
+ <Input
590
+ id={inputId}
591
+ autoFocus
592
+ value={draft}
593
+ disabled={busy}
594
+ placeholder="Task name, then Enter"
595
+ onChange={(e) => setDraft(e.target.value)}
596
+ onKeyDown={(e) => {
597
+ if (e.key === "Enter" && !e.nativeEvent.isComposing) {
598
+ e.preventDefault();
599
+ submit();
600
+ } else if (e.key === "Escape") {
601
+ setDraft("");
602
+ setAdding(null);
603
+ }
604
+ }}
605
+ onBlur={() => {
606
+ if (!draft.trim()) setAdding(null);
607
+ }}
608
+ />
609
+ <Button size="sm" variant="secondary" disabled={!draft.trim() || busy} loading={busy} onMouseDown={(e) => e.preventDefault()} onClick={submit}>
610
+ Add
611
+ </Button>
612
+ </div>
613
+ );
614
+ }
615
+
616
+ // ---- a row --------------------------------------------------------------
617
+
618
+ function SortableRow({ row, group, draggable, rowProps, handlers, regionRef, children }) {
619
+ const s = useSortableTask(sid(group, row.task.id), { task: row.task, group, label: row.task.title }, { disabled: !draggable || rowProps.editing });
620
+ return (
621
+ <Row
622
+ row={row}
623
+ {...rowProps}
624
+ handlers={handlers}
625
+ regionRef={regionRef}
626
+ nodeRef={s.setNodeRef}
627
+ style={s.style}
628
+ dragging={s.isDragging}
629
+ grip={draggable ? <DragHandle handle={s.handle} handleRef={s.setHandleRef} label={row.task.title || "task"} /> : null}
630
+ >
631
+ {children}
632
+ </Row>
633
+ );
634
+ }
635
+
636
+ function Row({ row, ctx, show, editable, counts, parentTitle, selectMode, selected, editing, handlers, regionRef, nodeRef, style, dragging, grip, children }) {
637
+ const t = row.task;
638
+ const done = isDone(t, ctx);
639
+ const sub = counts.get(t.id);
640
+ const titleRef = useRef(null);
641
+ const selId = useId();
642
+ const [draft, setDraft] = useState(t.title);
643
+ // Enter, Escape and blur each end an edit; only the first one counts.
644
+ const settled = useRef(false);
645
+ useEffect(() => {
646
+ if (editing) {
647
+ setDraft(t.title);
648
+ settled.current = false;
649
+ }
650
+ }, [editing, t.title]);
651
+ const finish = (save) => {
652
+ if (settled.current) return;
653
+ settled.current = true;
654
+ if (save) handlers.onRename(draft);
655
+ else handlers.onCancelEdit();
656
+ requestAnimationFrame(() => titleRef.current?.focus());
657
+ };
658
+
659
+ // ↑ ↓ walk the titles; x selects; Enter (a click) renames.
660
+ function onTitleKey(e) {
661
+ if (e.key === "ArrowDown" || e.key === "ArrowUp") {
662
+ const all = [...(regionRef.current?.querySelectorAll("[data-task-title]") ?? [])];
663
+ const i = all.indexOf(e.currentTarget);
664
+ const next = all[i + (e.key === "ArrowDown" ? 1 : -1)];
665
+ if (next) {
666
+ e.preventDefault();
667
+ next.focus();
668
+ }
669
+ } else if (e.key === "x" && !e.metaKey && !e.ctrlKey) {
670
+ e.preventDefault();
671
+ handlers.onSelect(e.shiftKey);
672
+ }
673
+ }
674
+
675
+ const indent = row.depth * 24;
676
+ return (
677
+ <li
678
+ ref={nodeRef}
679
+ style={style}
680
+ className={cx(
681
+ "group/row relative border-t border-border/60 bg-card [ul.grid>&:first-child]:border-t-0",
682
+ dragging && "z-10 opacity-40",
683
+ selected && "bg-primary/5",
684
+ )}
685
+ >
686
+ <div className="flex min-h-11 items-center gap-0.5 pe-1 ps-1 sm:pe-2">
687
+ <span className="flex w-6 shrink-0 justify-center">{grip}</span>
688
+ {selectMode ? (
689
+ <Checkbox
690
+ id={selId}
691
+ aria-label={`Select ${t.title || "task"}`}
692
+ checked={selected}
693
+ onChange={(e) => handlers.onSelect(e.nativeEvent?.shiftKey)}
694
+ className="w-6 shrink-0 justify-center"
695
+ />
696
+ ) : null}
697
+ <span style={{ width: indent }} className="shrink-0" aria-hidden="true" />
698
+ {row.hasChildren ? (
699
+ <IconButton
700
+ icon="chevron-right"
701
+ size="sm"
702
+ label={row.collapsed ? `Show ${row.childCount} subtasks` : `Hide subtasks`}
703
+ aria-expanded={!row.collapsed}
704
+ onClick={handlers.onFold}
705
+ className={cx("-mx-1.5 [&>svg]:transition-transform [&>svg]:duration-150 motion-reduce:[&>svg]:transition-none", !row.collapsed && "[&>svg]:rotate-90")}
706
+ />
707
+ ) : row.depth > 0 ? (
708
+ <span className="w-3 shrink-0" aria-hidden="true" />
709
+ ) : null}
710
+ <CompleteToggle done={done} onToggle={editable ? handlers.onToggleDone : undefined} label={t.title || "task"} size={row.depth > 0 ? "sm" : "md"} />
711
+ <div className="flex min-w-0 flex-1 flex-wrap items-center gap-x-3 gap-y-0.5 py-1 sm:flex-nowrap sm:py-0">
712
+ <div className="flex min-w-0 basis-full items-center gap-2 sm:flex-1 sm:basis-auto">
713
+ {editing ? (
714
+ <Input
715
+ aria-label="Task name"
716
+ autoFocus
717
+ value={draft}
718
+ onChange={(e) => setDraft(e.target.value)}
719
+ onFocus={(e) => e.target.select()}
720
+ onKeyDown={(e) => {
721
+ if (e.key === "Enter" && !e.nativeEvent.isComposing) {
722
+ e.preventDefault();
723
+ finish(true);
724
+ } else if (e.key === "Escape") {
725
+ e.preventDefault();
726
+ finish(false);
727
+ }
728
+ }}
729
+ onBlur={() => {
730
+ if (settled.current) return;
731
+ settled.current = true;
732
+ handlers.onRename(draft);
733
+ }}
734
+ className="flex-1"
735
+ />
736
+ ) : (
737
+ // raw: the title is text that becomes a field on click — a
738
+ // kit Button would centre and weigh it as an action.
739
+ <button
740
+ ref={titleRef}
741
+ type="button"
742
+ data-task-title
743
+ onClick={(e) => {
744
+ if (e.shiftKey || e.metaKey || e.ctrlKey) handlers.onSelect(e.shiftKey);
745
+ else if (editable) handlers.onEdit();
746
+ else handlers.onOpen?.();
747
+ }}
748
+ onKeyDown={onTitleKey}
749
+ title={editable ? "Click to rename" : undefined}
750
+ className={cx(
751
+ "min-w-0 truncate rounded-sm text-left text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring",
752
+ editable && "cursor-text",
753
+ done ? "text-muted-foreground line-through decoration-muted-foreground/60" : "text-foreground",
754
+ row.depth === 0 && "font-medium",
755
+ )}
756
+ >
757
+ {t.title || <span className="text-muted-foreground">Untitled</span>}
758
+ </button>
759
+ )}
760
+ {!editing && sub ? <SubtaskCount done={sub.done} total={sub.total} className="shrink-0" /> : null}
761
+ {!editing && parentTitle && row.depth === 0 ? (
762
+ <span className="hidden min-w-0 shrink truncate text-xs text-muted-foreground sm:inline" title={`Subtask of ${parentTitle}`}>
763
+ in {parentTitle}
764
+ </span>
765
+ ) : null}
766
+ {!editing && handlers.onOpen ? (
767
+ <IconButton
768
+ icon="maximize"
769
+ size="sm"
770
+ label={`Open ${t.title || "task"}`}
771
+ onClick={handlers.onOpen}
772
+ className="-my-1 ms-auto opacity-0 transition-opacity duration-150 group-hover/row:opacity-100 focus-visible:opacity-100 [@media(hover:none)]:hidden"
773
+ />
774
+ ) : null}
775
+ </div>
776
+ {show.has("tags") ? <TagList tags={t.tags} className="shrink-0" /> : null}
777
+ {show.has("project") && t.project ? (
778
+ <span className="max-w-32 shrink-0 truncate text-xs text-muted-foreground">{ctx.projectById.get(String(t.project))?.name ?? t.project}</span>
779
+ ) : null}
780
+ {show.has("estimate") && t.estimate ? (
781
+ <span data-nums className="shrink-0 text-xs text-muted-foreground">
782
+ {t.estimate}h
783
+ </span>
784
+ ) : null}
785
+ {show.has("start") && t.start ? (
786
+ <span data-nums className="shrink-0 text-xs text-muted-foreground">
787
+ from {formatDay(t.start, ctx.today)}
788
+ </span>
789
+ ) : null}
790
+ {show.has("progress") && typeof t.progress === "number" ? (
791
+ <span className="w-16 shrink-0">
792
+ <ProgressLine value={t.progress} />
793
+ </span>
794
+ ) : null}
795
+ {show.has("assignees") ? (
796
+ <AssigneeStack ids={t.assignees} ctx={ctx} onChange={editable ? (ids) => handlers.onPatch({ assignees: ids }) : undefined} className="shrink-0" />
797
+ ) : null}
798
+ {show.has("due") ? (
799
+ <DueLabel value={t.due} done={done} ctx={ctx} onChange={editable ? (due) => handlers.onPatch({ due }) : undefined} className="shrink-0 sm:w-24 sm:justify-start" />
800
+ ) : null}
801
+ {show.has("priority") ? (
802
+ <PriorityFlag value={t.priority} onChange={editable ? (priority) => handlers.onPatch({ priority }) : undefined} className="shrink-0" />
803
+ ) : null}
804
+ {show.has("status") ? <StatusPill status={t.status} ctx={ctx} onChange={editable ? (status) => handlers.onPatch({ status }) : undefined} className="shrink-0" /> : null}
805
+ </div>
806
+ <Menu items={handlers.menu} label={`Actions for ${t.title || "task"}`} className="shrink-0 sm:opacity-0 sm:transition-opacity sm:duration-150 sm:group-hover/row:opacity-100 sm:focus-visible:opacity-100 sm:aria-expanded:opacity-100" />
807
+ </div>
808
+ {children}
809
+ </li>
810
+ );
811
+ }
812
+
813
+ // ---- the bulk bar -------------------------------------------------------
814
+
815
+ function BulkBar({ count, ctx, editable, onClear, onSelectAll, allSelected, apply, onDelete }) {
816
+ return (
817
+ <div role="toolbar" aria-label={`${count} selected`} className="sticky bottom-3 z-30 mx-auto flex w-full max-w-2xl flex-wrap items-center gap-1 rounded-lg border border-border bg-popover p-1.5 text-popover-foreground shadow-lg">
818
+ <span data-nums className="px-2 text-sm font-medium">
819
+ {count} selected
820
+ </span>
821
+ {!allSelected ? (
822
+ <Button variant="link" size="sm" tone="muted" onClick={onSelectAll}>
823
+ Select all
824
+ </Button>
825
+ ) : null}
826
+ <span className="flex-1" />
827
+ {editable && count > 0 ? (
828
+ <>
829
+ <Menu
830
+ label="Set status"
831
+ trigger={
832
+ <span className="inline-flex items-center gap-1.5 text-sm">
833
+ <Icon name="circle-dashed" size={16} />
834
+ <span className="hidden sm:inline">Status</span>
835
+ </span>
836
+ }
837
+ items={ctx.statuses.map((s) => ({ label: s.label, onSelect: () => apply({ status: s.id }, `${count} tasks set to ${s.label}`) }))}
838
+ />
839
+ <Menu
840
+ label="Assign"
841
+ trigger={
842
+ <span className="inline-flex items-center gap-1.5 text-sm">
843
+ <Icon name="user" size={16} />
844
+ <span className="hidden sm:inline">Assign</span>
845
+ </span>
846
+ }
847
+ items={[
848
+ ...ctx.people.map((p) => ({ label: p.name, onSelect: () => apply({ assignees: [p.id] }, `${count} tasks assigned to ${p.name}`) })),
849
+ { type: "separator" },
850
+ { label: "Unassign", onSelect: () => apply({ assignees: [] }, `${count} tasks unassigned`) },
851
+ ]}
852
+ />
853
+ <DueLabel value={null} ctx={ctx} quiet={false} placeholder="Due" onChange={(due) => apply({ due }, due ? `${count} tasks due ${formatDay(due, ctx.today)}` : `${count} tasks have no due date`)} className="px-2 text-sm" />
854
+ <Menu
855
+ label="Set priority"
856
+ trigger={
857
+ <span className="inline-flex items-center gap-1.5 text-sm">
858
+ <Icon name="flag" size={16} />
859
+ <span className="hidden sm:inline">Priority</span>
860
+ </span>
861
+ }
862
+ items={[
863
+ ...["urgent", "high", "medium", "low"].map((p) => ({ label: p[0].toUpperCase() + p.slice(1), onSelect: () => apply({ priority: p }, `${count} tasks set to ${p}`) })),
864
+ { label: "No priority", onSelect: () => apply({ priority: null }, `${count} tasks have no priority`) },
865
+ ]}
866
+ />
867
+ <IconButton icon="trash" size="sm" label={`Delete ${count} tasks`} onClick={onDelete} className="text-destructive" />
868
+ </>
869
+ ) : null}
870
+ <IconButton icon="x" size="sm" label="Clear selection" onClick={onClear} />
871
+ </div>
872
+ );
873
+ }
874
+
875
+ function SubtaskAdd({ parent, group, onCreate, onClose }) {
876
+ const [draft, setDraft] = useState("");
877
+ const [busy, setBusy] = useState(false);
878
+ async function submit() {
879
+ const title = draft.trim();
880
+ if (!title || busy) return;
881
+ setBusy(true);
882
+ const r = await onCreate(title, group, parent.task.id);
883
+ setBusy(false);
884
+ if (r?.ok !== false) setDraft("");
885
+ }
886
+ return (
887
+ <li className="flex items-center gap-2 border-t border-border/60 py-1.5 pe-3" style={{ paddingInlineStart: 64 + (parent.depth + 1) * 24 }}>
888
+ <Input
889
+ aria-label={`New subtask of ${parent.task.title || "task"}`}
890
+ autoFocus
891
+ value={draft}
892
+ disabled={busy}
893
+ placeholder="Subtask name, then Enter"
894
+ onChange={(e) => setDraft(e.target.value)}
895
+ onKeyDown={(e) => {
896
+ if (e.key === "Enter" && !e.nativeEvent.isComposing) {
897
+ e.preventDefault();
898
+ submit();
899
+ } else if (e.key === "Escape") onClose();
900
+ }}
901
+ onBlur={() => {
902
+ if (!draft.trim()) onClose();
903
+ }}
904
+ />
905
+ </li>
906
+ );
907
+ }