makersclaw 0.2.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 (473) 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/install.mjs +18 -0
  430. package/src/shadcn.mjs +8 -0
  431. package/templates/next/files/README.md +145 -11
  432. package/templates/next/files/app/globals.css +55 -4
  433. package/templates/next/files/check/run.mjs +3 -3
  434. package/templates/next/files/components/ui/avatar.jsx +141 -0
  435. package/templates/next/files/components/ui/breadcrumb.jsx +149 -0
  436. package/templates/next/files/components/ui/button-group.jsx +70 -0
  437. package/templates/next/files/components/ui/button.jsx +132 -12
  438. package/templates/next/files/components/ui/calendar.jsx +609 -0
  439. package/templates/next/files/components/ui/carousel.jsx +281 -0
  440. package/templates/next/files/components/ui/collapsible.jsx +208 -0
  441. package/templates/next/files/components/ui/combobox.jsx +608 -0
  442. package/templates/next/files/components/ui/command.jsx +577 -0
  443. package/templates/next/files/components/ui/context-menu.jsx +191 -0
  444. package/templates/next/files/components/ui/date-picker.jsx +360 -0
  445. package/templates/next/files/components/ui/distribution.jsx +117 -0
  446. package/templates/next/files/components/ui/feedback.jsx +143 -0
  447. package/templates/next/files/components/ui/field.jsx +115 -3
  448. package/templates/next/files/components/ui/file-input.jsx +234 -0
  449. package/templates/next/files/components/ui/icon.jsx +130 -8
  450. package/templates/next/files/components/ui/image.jsx +143 -0
  451. package/templates/next/files/components/ui/index.js +47 -2
  452. package/templates/next/files/components/ui/input-group.jsx +213 -0
  453. package/templates/next/files/components/ui/input-otp.jsx +170 -0
  454. package/templates/next/files/components/ui/item.jsx +206 -0
  455. package/templates/next/files/components/ui/menu.jsx +454 -82
  456. package/templates/next/files/components/ui/notice.jsx +126 -14
  457. package/templates/next/files/components/ui/pagination.jsx +156 -0
  458. package/templates/next/files/components/ui/popover.jsx +284 -0
  459. package/templates/next/files/components/ui/radio.jsx +193 -0
  460. package/templates/next/files/components/ui/rating.jsx +131 -0
  461. package/templates/next/files/components/ui/resizable.jsx +338 -0
  462. package/templates/next/files/components/ui/scroll-area.jsx +123 -0
  463. package/templates/next/files/components/ui/separator.jsx +119 -0
  464. package/templates/next/files/components/ui/sheet.jsx +96 -11
  465. package/templates/next/files/components/ui/slider.jsx +174 -0
  466. package/templates/next/files/components/ui/stat.jsx +574 -0
  467. package/templates/next/files/components/ui/steps.jsx +338 -0
  468. package/templates/next/files/components/ui/swatch.jsx +33 -0
  469. package/templates/next/files/components/ui/table.jsx +130 -0
  470. package/templates/next/files/components/ui/toast.jsx +440 -0
  471. package/templates/next/files/components/ui/toggle.jsx +195 -0
  472. package/templates/next/files/components/ui/tooltip.jsx +270 -0
  473. package/templates/next/template.json +264 -8
@@ -0,0 +1,923 @@
1
+ "use client";
2
+
3
+ import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
4
+ import { Button, Empty, Icon, Input, Notice, Skeleton, Toggle, ToggleGroup, cx } from "../../ui";
5
+ import {
6
+ DEFAULT_VIEW,
7
+ NONE,
8
+ TaskDragProvider,
9
+ ViewToolbar,
10
+ addDays,
11
+ applyView,
12
+ createTaskContext,
13
+ criticalPath,
14
+ dateConflicts,
15
+ deleteChange,
16
+ dependencyEdges,
17
+ descendantIds,
18
+ diffDays,
19
+ draftTask,
20
+ flattenTree,
21
+ formatDate,
22
+ isDone,
23
+ movePatch,
24
+ progressOf,
25
+ spanOf,
26
+ spanPatch,
27
+ toggleDoneChange,
28
+ useTaskChanges,
29
+ wouldCycle,
30
+ } from "../task-model";
31
+ import { ADD_ROW, Bar, GroupRow, ListRow, QuickAdd, ROW, TINTS, TimelineRow, Unscheduled, spanWords } from "./rows";
32
+ import {
33
+ ZOOMS,
34
+ afterBlockersPatch,
35
+ arrowPath,
36
+ ganttRange,
37
+ headerTiers,
38
+ isMilestone,
39
+ lengthOf,
40
+ planMove,
41
+ summarySpan,
42
+ weekendBands,
43
+ xOf,
44
+ zoomOf,
45
+ } from "./schedule";
46
+
47
+ // Gantt — tasks on a timeline, like Linear's timeline or TeamGantt: the
48
+ // task list on the left (groups that fold, subtasks under their parent),
49
+ // the bars on the right under a two-tier date header. Drag a bar to move
50
+ // it, an edge to change its start or end, the dot past its end onto
51
+ // another bar to make that one wait on it; every task that waits on a
52
+ // moved one slides along while you drag, and the finish date at the top
53
+ // says what the move cost. Milestones are diamonds, parents are summary
54
+ // brackets, dependencies are arrows (red when the dates disagree), and the
55
+ // critical path lights up on demand. Zoom day · week · month · quarter;
56
+ // a today line; the toolbar's search, filters, group and sort.
57
+ //
58
+ // Everything saves through one async `onChange(change)` answering { ok }
59
+ // (task-model's change shapes); the timeline shows each change at once
60
+ // and takes it back, with a notice, when the save fails.
61
+ //
62
+ // README.md beside this file lists every prop.
63
+
64
+ const HEADER = 52;
65
+ const ZOOM_ITEMS = Object.values(ZOOMS).map((z) => ({ value: z.key, label: z.label }));
66
+ const GROUP_FIELDS = ["project", "status", "assignees", "priority"];
67
+
68
+ function useControlled(value, fallback, onChange) {
69
+ const [own, setOwn] = useState(fallback);
70
+ const controlled = value !== undefined;
71
+ const set = useCallback(
72
+ (next) => {
73
+ if (!controlled) setOwn(next);
74
+ onChange?.(next);
75
+ },
76
+ [controlled, onChange],
77
+ );
78
+ return [controlled ? value : own, set];
79
+ }
80
+
81
+ export function Gantt({
82
+ tasks,
83
+ statuses,
84
+ people,
85
+ projects,
86
+ fields,
87
+ today,
88
+ weekStartsOn,
89
+ weekend,
90
+ holidays,
91
+ onChange,
92
+ onOpen,
93
+ view: viewProp,
94
+ defaultView,
95
+ onViewChange,
96
+ zoom: zoomProp,
97
+ defaultZoom = "week",
98
+ onZoomChange,
99
+ colorBy = "project",
100
+ pushDependents = true,
101
+ defaultCritical = false,
102
+ newTaskLength = 3,
103
+ toolbar = true,
104
+ toolbarExtra,
105
+ addLabel = "Add task",
106
+ starters,
107
+ emptyTitle = "Nothing on the timeline yet",
108
+ emptyBody = "Add the first task and give it dates, then drag it into place. Link tasks to see what waits on what.",
109
+ height = 560,
110
+ loading = false,
111
+ error: readError,
112
+ label = "Timeline",
113
+ locale,
114
+ className,
115
+ }) {
116
+ const uid = useId();
117
+ const ctx = useMemo(
118
+ () => createTaskContext({ statuses, people, projects, fields, today, weekStartsOn, weekend, holidays }),
119
+ [statuses, people, projects, fields, today, weekStartsOn, weekend, holidays],
120
+ );
121
+ const editable = typeof onChange === "function";
122
+ const { tasks: all, commit, error, clearError } = useTaskChanges(tasks, onChange);
123
+ const [viewRaw, setViewRaw] = useControlled(viewProp, { ...DEFAULT_VIEW, groupBy: "project", sort: [{ key: "start", dir: "asc" }], ...defaultView }, onViewChange);
124
+ const view = useMemo(() => ({ ...DEFAULT_VIEW, ...viewRaw }), [viewRaw]);
125
+ const [zoom, setZoomRaw] = useControlled(zoomProp, ZOOMS[defaultZoom] ? defaultZoom : "week", onZoomChange);
126
+ const z = zoomOf(zoom);
127
+ const dw = z.dayWidth;
128
+
129
+ const [critical, setCritical] = useState(defaultCritical);
130
+ const [listOpen, setListOpen] = useState(null); // null: open from 640px, closed on a phone
131
+ const [narrow, setNarrow] = useState(false);
132
+ const [folded, setFolded] = useState(() => new Set());
133
+ const [collapsed, setCollapsed] = useState(() => new Set());
134
+ const [hot, setHot] = useState(null);
135
+ const [adding, setAdding] = useState(null);
136
+ const [drag, setDrag] = useState(null); // { id, kind, days, keys? }
137
+ const [linking, setLinking] = useState(false);
138
+ const [notice, setNotice] = useState(null);
139
+ const [announce, setAnnounce] = useState("");
140
+ const scrollRef = useRef(null);
141
+ const listRef = useRef(null);
142
+ const dragEndedAt = useRef(0);
143
+ const nudgeTimer = useRef(null);
144
+ const dragRef = useRef(null);
145
+ dragRef.current = drag;
146
+
147
+ useEffect(() => {
148
+ const mq = window.matchMedia("(max-width: 639px)");
149
+ const on = () => setNarrow(mq.matches);
150
+ on();
151
+ mq.addEventListener("change", on);
152
+ return () => mq.removeEventListener("change", on);
153
+ }, []);
154
+ const showList = listOpen ?? !narrow;
155
+
156
+ const say = (text) => setAnnounce(text);
157
+ const name = (t) => `“${t?.title || "Untitled"}”`;
158
+ const byIdAll = useMemo(() => new Map(all.map((t) => [t.id, t])), [all]);
159
+
160
+ // ---- what a drag shows before it saves -------------------------------
161
+ const plan = useMemo(() => (drag ? planMove(all, drag.id, drag.kind, drag.days, { push: pushDependents }) : []), [all, drag, pushDependents]);
162
+ const display = useMemo(() => {
163
+ if (!plan.length) return all;
164
+ const patches = new Map(plan.map((p) => [p.id, p.patch]));
165
+ return all.map((t) => (patches.has(t.id) ? { ...t, ...patches.get(t.id) } : t));
166
+ }, [all, plan]);
167
+ const byId = useMemo(() => new Map(display.map((t) => [t.id, t])), [display]);
168
+ const moved = useMemo(() => new Set(plan.map((p) => p.id)), [plan]);
169
+
170
+ // ---- the view ----------------------------------------------------------
171
+ const shown = useMemo(() => applyView(all, view, ctx), [all, view, ctx]);
172
+ const by = view.groupBy && view.groupBy !== "none" ? view.groupBy : null;
173
+ const range = useMemo(() => ganttRange(all, zoom, ctx.today, { weekStartsOn: ctx.weekStartsOn }), [all, zoom, ctx]);
174
+ const from = range.from;
175
+ const width = range.days * dw;
176
+ const tiers = useMemo(() => (from ? headerTiers(from, range.to, zoom, { today: ctx.today, weekStartsOn: ctx.weekStartsOn, locale }) : { top: [], bottom: [] }), [from, range.to, zoom, ctx, locale]);
177
+ const bands = useMemo(() => (from ? weekendBands(from, range.to, zoom, ctx.weekend) : []), [from, range.to, zoom, ctx]);
178
+
179
+ // Rows in reading order, each with its height and top: the list pane and
180
+ // the timeline draw the same list, so they always line up.
181
+ const rows = useMemo(() => {
182
+ const out = [];
183
+ let y = 0;
184
+ const push = (r, h) => {
185
+ out.push({ ...r, y, height: h });
186
+ y += h;
187
+ };
188
+ const single = !by;
189
+ for (const g of shown.groups) {
190
+ const shut = folded.has(g.key);
191
+ if (!single) push({ kind: "group", key: `g:${g.key}`, group: g }, ROW);
192
+ if (!shut || single) {
193
+ for (const r of flattenTree(g.tasks, { collapsed })) {
194
+ const task = byId.get(r.task.id) ?? r.task;
195
+ const milestone = isMilestone(task);
196
+ const span = r.hasChildren ? summarySpan(task, display) : spanOf(task);
197
+ push(
198
+ {
199
+ kind: "task",
200
+ key: `${g.key}::${task.id}`,
201
+ group: g.key,
202
+ task,
203
+ depth: r.depth,
204
+ hasChildren: r.hasChildren,
205
+ collapsed: r.collapsed,
206
+ milestone,
207
+ span,
208
+ done: isDone(task, ctx),
209
+ progress: progressOf(task, display, ctx),
210
+ },
211
+ ROW,
212
+ );
213
+ }
214
+ if (editable) push({ kind: "add", key: `add:${g.key}`, group: g }, ADD_ROW);
215
+ }
216
+ }
217
+ return { list: out, height: y };
218
+ }, [shown.groups, by, folded, collapsed, byId, display, ctx, editable]);
219
+
220
+ // Where each task's bar is (its first row, when it shows twice).
221
+ const boxes = useMemo(() => {
222
+ const m = new Map();
223
+ if (!from) return m;
224
+ for (const r of rows.list) {
225
+ if (r.kind !== "task" || !r.span || m.has(r.task.id)) continue;
226
+ const cy = r.y + ROW / 2;
227
+ if (r.milestone && !r.hasChildren) {
228
+ const cx = xOf(r.span.end, from, dw) + dw / 2;
229
+ m.set(r.task.id, { x0: cx - 8, x1: cx + 8, y: cy });
230
+ } else {
231
+ const x0 = xOf(r.span.start, from, dw);
232
+ m.set(r.task.id, { x0, x1: x0 + Math.max(lengthOf(r.span) * dw, 6), y: cy });
233
+ }
234
+ }
235
+ return m;
236
+ }, [rows, from, dw]);
237
+
238
+ const path = useMemo(() => (critical ? criticalPath(display) : new Set()), [critical, display]);
239
+ const conflicts = useMemo(() => new Set(dateConflicts(display).map((e) => `${e.from}>${e.to}`)), [display]);
240
+ const conflicted = useMemo(() => {
241
+ const m = new Map();
242
+ for (const k of conflicts) {
243
+ const [a, b] = k.split(">");
244
+ m.set(b, [...(m.get(b) ?? []), a]);
245
+ }
246
+ return m;
247
+ }, [conflicts]);
248
+ const edges = useMemo(() => dependencyEdges(display).filter((e) => boxes.has(e.from) && boxes.has(e.to)), [display, boxes]);
249
+
250
+ // The finish: the last day any shown task ends, and how a drag moves it.
251
+ const finish = useMemo(() => {
252
+ let base = null;
253
+ let now = null;
254
+ for (const t of shown.tasks) {
255
+ const a = spanOf(t);
256
+ const b = spanOf(byId.get(t.id));
257
+ if (a && (!base || a.end > base)) base = a.end;
258
+ if (b && (!now || b.end > now)) now = b.end;
259
+ }
260
+ return { day: now, delta: base && now ? diffDays(base, now) : 0 };
261
+ }, [shown.tasks, byId]);
262
+ const pathDays = useMemo(() => {
263
+ let lo = null;
264
+ let hi = null;
265
+ for (const id of path) {
266
+ const s = spanOf(byId.get(id));
267
+ if (!s) continue;
268
+ if (!lo || s.start < lo) lo = s.start;
269
+ if (!hi || s.end > hi) hi = s.end;
270
+ }
271
+ return lo ? diffDays(lo, hi) + 1 : 0;
272
+ }, [path, byId]);
273
+
274
+ // ---- scrolling ---------------------------------------------------------
275
+ const listWidth = () => (showList ? listRef.current?.offsetWidth ?? 0 : 0);
276
+ const scrollToDay = useCallback(
277
+ (day, { smooth = false, at = 1 / 3 } = {}) => {
278
+ const el = scrollRef.current;
279
+ if (!el || !from || !day) return;
280
+ const lw = showList ? listRef.current?.offsetWidth ?? 0 : 0;
281
+ const left = xOf(day, from, dw) - (el.clientWidth - lw) * at;
282
+ const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
283
+ el.scrollTo({ left: Math.max(0, left), behavior: smooth && !reduced ? "smooth" : "auto" });
284
+ },
285
+ [from, dw, showList],
286
+ );
287
+ // Open on today, once there is a timeline.
288
+ const opened = useRef(false);
289
+ useEffect(() => {
290
+ if (opened.current || !from) return;
291
+ opened.current = true;
292
+ scrollToDay(ctx.today);
293
+ }, [from, ctx.today, scrollToDay]);
294
+ // A new zoom keeps the day in the middle where it was.
295
+ const anchor = useRef(null);
296
+ function setZoom(next) {
297
+ const el = scrollRef.current;
298
+ if (el && from) {
299
+ const lw = listWidth();
300
+ const mid = el.scrollLeft + (el.clientWidth - lw) / 2;
301
+ anchor.current = addDays(from, Math.round(mid / dw));
302
+ }
303
+ setZoomRaw(next);
304
+ }
305
+ useEffect(() => {
306
+ if (!anchor.current) return;
307
+ scrollToDay(anchor.current, { at: 1 / 2 });
308
+ anchor.current = null;
309
+ }, [zoom, scrollToDay]);
310
+
311
+ // ---- changes -----------------------------------------------------------
312
+ const commitPlan = useCallback(
313
+ (id, kind, days) => {
314
+ const steps = planMove(all, id, kind, days, { push: pushDependents });
315
+ if (!steps.length) return;
316
+ const task = byIdAll.get(id);
317
+ const family = new Set([id, ...descendantIds(all, id)]);
318
+ const pushed = steps.filter((s) => !family.has(s.id)).length;
319
+ const change = steps.length === 1 ? { type: "update", id: steps[0].id, patch: steps[0].patch } : { type: "batch", changes: steps.map((s) => ({ type: "update", id: s.id, patch: s.patch })) };
320
+ const after = spanOf({ ...task, ...steps.find((s) => s.id === id)?.patch }) ?? summarySpan({ ...task }, all);
321
+ commit(change, {
322
+ label: name(task),
323
+ undo: pushed ? `Moved ${name(task)} and ${pushed} task${pushed === 1 ? "" : "s"} waiting on it` : undefined,
324
+ }).then((r) => {
325
+ if (r?.ok !== false) say(`${name(task)} is now ${spanWords(after, locale)}${pushed ? `; ${pushed} waiting task${pushed === 1 ? "" : "s"} moved too` : ""}.`);
326
+ });
327
+ },
328
+ [all, byIdAll, commit, pushDependents, locale],
329
+ );
330
+
331
+ const onPreview = useCallback((id, kind, days) => {
332
+ setDrag((d) => (d && d.id === id && d.kind === kind && d.days === days ? d : { id, kind, days }));
333
+ }, []);
334
+
335
+ // Keyboard: each arrow adds a day to a pending move; it saves when the
336
+ // keys rest (or on Enter), Escape drops it.
337
+ const onNudge = useCallback(
338
+ (id, kind, step, action) => {
339
+ const d = dragRef.current;
340
+ clearTimeout(nudgeTimer.current);
341
+ const flush = (x) => {
342
+ setDrag(null);
343
+ if (x?.days) commitPlan(x.id, x.kind, x.days);
344
+ };
345
+ if (action === "flush") return flush(d);
346
+ if (action === "cancel") {
347
+ setDrag(null);
348
+ say("Move cancelled.");
349
+ return;
350
+ }
351
+ let next;
352
+ if (d && d.keys && d.id === id && d.kind === kind) next = { ...d, days: d.days + step };
353
+ else {
354
+ if (d?.keys) flush(d);
355
+ next = { id, kind, days: step, keys: true };
356
+ }
357
+ setDrag(next);
358
+ nudgeTimer.current = setTimeout(() => flush(dragRef.current), 800);
359
+ },
360
+ [commitPlan],
361
+ );
362
+ useEffect(() => () => clearTimeout(nudgeTimer.current), []);
363
+
364
+ function onDragStart({ active }) {
365
+ if (active?.data?.kind === "link") setLinking(true);
366
+ }
367
+
368
+ function onDragEnd({ active, over, delta }) {
369
+ dragEndedAt.current = Date.now();
370
+ setLinking(false);
371
+ const a = active?.data ?? {};
372
+ if (a.kind === "link") {
373
+ setDrag(null);
374
+ const to = over?.data?.task;
375
+ if (to && to.id !== a.task.id) link(a.task.id, to.id);
376
+ return;
377
+ }
378
+ const days = Math.round((delta?.x ?? 0) / dw);
379
+ setDrag(null);
380
+ if (a.task && days) commitPlan(a.task.id, a.kind, days);
381
+ }
382
+
383
+ function link(fromId, toId) {
384
+ const target = byIdAll.get(toId);
385
+ const source = byIdAll.get(fromId);
386
+ if (!target || !source) return;
387
+ if ((target.dependsOn ?? []).includes(fromId)) return;
388
+ if (wouldCycle(all, fromId, toId)) {
389
+ setNotice(`${name(source)} already waits on ${name(target)}, so ${name(target)} cannot wait on it.`);
390
+ return;
391
+ }
392
+ setNotice(null);
393
+ commit({ type: "update", id: toId, patch: { dependsOn: [...(target.dependsOn ?? []), fromId] } }, { label: name(target) }).then((r) => {
394
+ if (r?.ok !== false) say(`${name(target)} now waits on ${name(source)}.`);
395
+ });
396
+ }
397
+
398
+ function unlink(fromId, toId) {
399
+ const target = byIdAll.get(toId);
400
+ if (!target) return;
401
+ commit(
402
+ { type: "update", id: toId, patch: { dependsOn: (target.dependsOn ?? []).filter((d) => d !== fromId) } },
403
+ { label: name(target), undo: `${name(target)} no longer waits on ${name(byIdAll.get(fromId))}` },
404
+ );
405
+ }
406
+
407
+ function groupPatch(key) {
408
+ if (!by) return {};
409
+ return movePatch({ assignees: [], tags: [] }, by, NONE, key, ctx);
410
+ }
411
+
412
+ async function create(title, groupKey) {
413
+ const patch = groupPatch(groupKey);
414
+ if (patch === null) return { ok: false };
415
+ const start = ctx.today;
416
+ const task = draftTask(title, ctx, { ...patch, start, due: addDays(start, Math.max(1, newTaskLength) - 1) });
417
+ const r = await commit({ type: "create", task }, { label: name(task) });
418
+ if (r?.ok !== false) say(`Added ${name(task)}, starting today.`);
419
+ return r;
420
+ }
421
+
422
+ function schedule(task, dayIndex) {
423
+ const start = addDays(from, dayIndex);
424
+ const end = addDays(start, Math.max(1, isMilestone(task) ? 1 : newTaskLength) - 1);
425
+ commit({ type: "update", id: task.id, patch: spanPatch(task, start, end) }, { label: name(task) }).then((r) => {
426
+ if (r?.ok !== false) say(`${name(task)} is scheduled for ${spanWords({ start, end }, locale)}.`);
427
+ });
428
+ }
429
+
430
+ function remove(task) {
431
+ commit(deleteChange(all, [task.id]), { label: name(task), undo: `Deleted ${name(task)}` });
432
+ }
433
+
434
+ async function startFrom(s) {
435
+ let cursor = ctx.today;
436
+ let prev = null;
437
+ const made = [];
438
+ for (const item of s.tasks ?? []) {
439
+ const len = Math.max(1, item.milestone ? 1 : item.days ?? newTaskLength);
440
+ const start = item.parallel && prev ? prev.start : cursor;
441
+ const end = addDays(start, len - 1);
442
+ const t = draftTask(item.title, ctx, {
443
+ start: item.milestone ? undefined : start,
444
+ due: end,
445
+ ...(item.milestone ? { milestone: true } : {}),
446
+ dependsOn: prev && !item.parallel ? [prev.id] : [],
447
+ });
448
+ made.push(t);
449
+ if (!item.parallel) cursor = addDays(end, 1);
450
+ prev = { id: t.id, start };
451
+ }
452
+ await commit({ type: "create", tasks: made }, { label: s.label });
453
+ }
454
+
455
+ function clickGuard() {
456
+ return Date.now() - dragEndedAt.current < 250;
457
+ }
458
+
459
+ function menuFor(row) {
460
+ const t = row.task;
461
+ const items = [];
462
+ if (onOpen) items.push({ label: "Open", icon: "external", onSelect: () => onOpen(t) });
463
+ if (!editable) return items;
464
+ if (!row.span) items.push({ label: "Schedule from today", icon: "calendar", onSelect: () => schedule(t, diffDays(from, ctx.today)) });
465
+ const fix = conflicted.has(t.id) ? afterBlockersPatch(t, all) : null;
466
+ if (fix) items.push({ label: "Start after what it waits on", icon: "arrow-right", onSelect: () => commit({ type: "update", id: t.id, patch: fix }, { label: name(t) }) });
467
+ // "Waits on": what it waits on now, then the tasks nearest its start
468
+ // (a dozen; the dot on a bar links anything).
469
+ const mine = new Set(t.dependsOn ?? []);
470
+ const at = row.span?.start ?? ctx.today;
471
+ const near = (o) => Math.abs(diffDays(at, spanOf(o)?.start ?? at) ?? 0);
472
+ const others = shown.tasks
473
+ .filter((o) => o.id !== t.id && o.id !== t.parentId)
474
+ .sort((a, b) => (mine.has(b.id) ? 1 : 0) - (mine.has(a.id) ? 1 : 0) || near(a) - near(b))
475
+ .slice(0, Math.max(12, mine.size));
476
+ if (others.length) {
477
+ items.push({
478
+ label: "Waits on",
479
+ icon: "link",
480
+ items: others.map((o) => {
481
+ const on = (t.dependsOn ?? []).includes(o.id);
482
+ return {
483
+ type: "checkbox",
484
+ label: o.title || "Untitled",
485
+ checked: on,
486
+ disabled: !on && wouldCycle(all, o.id, t.id),
487
+ onCheckedChange: (next) => (next ? link(o.id, t.id) : unlink(o.id, t.id)),
488
+ };
489
+ }),
490
+ });
491
+ }
492
+ items.push({ label: row.done ? "Mark not done" : "Mark done", icon: "check", onSelect: () => commit(toggleDoneChange(t, ctx), { label: name(t) }) });
493
+ items.push({ type: "separator" });
494
+ items.push({ label: "Delete", icon: "trash", tone: "danger", onSelect: () => remove(t) });
495
+ return items;
496
+ }
497
+
498
+ function tintOf(task) {
499
+ if (colorBy === "status") {
500
+ const s = ctx.statusById.get(String(task.status));
501
+ return TINTS[s?.hue] ?? TINTS[s?.tone] ?? TINTS.neutral;
502
+ }
503
+ if (colorBy === "project") return TINTS[ctx.projectById.get(String(task.project))?.hue] ?? TINTS.primary;
504
+ return TINTS.primary;
505
+ }
506
+
507
+ const clearFilters = () => setViewRaw({ ...view, search: "", filters: {}, hideDone: false });
508
+ const firstGroup = shown.groups[0]?.key ?? "all";
509
+
510
+ // ---- render ------------------------------------------------------------
511
+ const addButton = editable ? (
512
+ <Button
513
+ variant="primary"
514
+ size="sm"
515
+ icon="plus"
516
+ onClick={() => {
517
+ setListOpen(true);
518
+ setFolded((s) => (s.has(firstGroup) ? new Set([...s].filter((k) => k !== firstGroup)) : s));
519
+ setAdding(firstGroup);
520
+ }}
521
+ >
522
+ <span className="sr-only sm:not-sr-only">{addLabel}</span>
523
+ </Button>
524
+ ) : null;
525
+
526
+ const bar = (
527
+ <div className="flex flex-wrap items-center gap-2 border-b border-border px-2 py-2">
528
+ <Toggle
529
+ size="sm"
530
+ variant="quiet"
531
+ icon="panel-left"
532
+ label={showList ? "Hide the task list" : "Show the task list"}
533
+ pressed={showList}
534
+ onPressedChange={(on) => setListOpen(on)}
535
+ />
536
+ <Button size="sm" variant="quiet" onClick={() => scrollToDay(ctx.today, { smooth: true })}>
537
+ Today
538
+ </Button>
539
+ <ToggleGroup aria-label="Zoom" size="sm" items={ZOOM_ITEMS} value={zoom} onChange={setZoom} className="order-last w-full sm:order-none sm:w-auto" />
540
+ <div className="ms-auto flex min-w-0 items-center gap-2">
541
+ {finish.day ? (
542
+ <p className="hidden items-center gap-1.5 text-sm text-muted-foreground md:flex">
543
+ Finishes <span className="font-medium text-foreground">{formatDate(finish.day, { weekday: "short", month: "short", day: "numeric" }, { locale })}</span>
544
+ {drag && finish.delta ? (
545
+ <span data-nums className={cx("rounded-full px-1.5 text-xs font-medium tabular-nums", finish.delta > 0 ? "bg-warning/15 text-warning" : "bg-success/15 text-success")}>
546
+ {finish.delta > 0 ? "+" : "−"}
547
+ {Math.abs(finish.delta)} {Math.abs(finish.delta) === 1 ? "day" : "days"}
548
+ </span>
549
+ ) : null}
550
+ </p>
551
+ ) : null}
552
+ <Toggle size="sm" variant="quiet" icon="zap" pressed={critical} onPressedChange={setCritical}>
553
+ <span className="sr-only sm:not-sr-only">Critical path</span>
554
+ </Toggle>
555
+ </div>
556
+ </div>
557
+ );
558
+
559
+ let content;
560
+ if (loading) {
561
+ content = (
562
+ <div aria-busy="true" className="grid gap-3 p-4" style={{ height: Math.min(height, 320) }}>
563
+ {[64, 48, 72, 40, 56].map((w, i) => (
564
+ <div key={i} className="flex items-center gap-4">
565
+ <Skeleton className="w-28 shrink-0 sm:w-56" />
566
+ <div className="min-w-0 flex-1" style={{ paddingInlineStart: `${i * 6}%`, paddingInlineEnd: `${100 - w - i * 6}%` }}>
567
+ <Skeleton shape="rect" className="h-6 w-full" />
568
+ </div>
569
+ </div>
570
+ ))}
571
+ <span className="sr-only">Loading the timeline</span>
572
+ </div>
573
+ );
574
+ } else if (!all.length) {
575
+ content = (
576
+ <div className="p-4">
577
+ <Empty
578
+ icon="calendar"
579
+ title={emptyTitle}
580
+ body={emptyBody}
581
+ action={editable ? <FirstTask onCreate={(title) => create(title, firstGroup)} starters={starters} onStarter={startFrom} addLabel={addLabel} uid={uid} /> : null}
582
+ />
583
+ </div>
584
+ );
585
+ } else if (!shown.tasks.length) {
586
+ content = (
587
+ <div className="p-4">
588
+ <Empty
589
+ icon="filter"
590
+ title="No tasks match"
591
+ body="Nothing on this timeline fits the search and filters."
592
+ action={
593
+ <Button size="sm" variant="secondary" onClick={clearFilters}>
594
+ Clear filters
595
+ </Button>
596
+ }
597
+ />
598
+ </div>
599
+ );
600
+ } else {
601
+ const tx = from ? xOf(ctx.today, from, dw) : null;
602
+ const todayIn = tx !== null && tx >= 0 && tx < width;
603
+ content = (
604
+ <TaskDragProvider collision="pointer" onDragStart={onDragStart} onDragEnd={onDragEnd} onDragCancel={() => (setDrag(null), setLinking(false))} disabled={!editable}>
605
+ <div
606
+ ref={scrollRef}
607
+ className={cx("relative overflow-auto overscroll-x-contain", showList ? "[--gantt-list:11rem] sm:[--gantt-list:18rem]" : "[--gantt-list:0px]")}
608
+ style={{ maxHeight: `min(${height}px, 75vh)` }}
609
+ onPointerLeave={() => setHot(null)}
610
+ >
611
+ <div className="w-max min-w-full">
612
+ {/* The header: the list's heading, then months over days (or
613
+ years over months). Sticky to the top. */}
614
+ <div className="sticky top-0 z-40 flex border-b border-border bg-card" style={{ height: HEADER }}>
615
+ {showList ? (
616
+ <div className="sticky left-0 z-10 flex w-44 shrink-0 items-end border-e border-border bg-card px-3 pb-1.5 sm:w-72">
617
+ <span className="text-xs font-medium text-muted-foreground">
618
+ Task{" "}
619
+ <span data-nums className="tabular-nums">
620
+ · {shown.tasks.length}
621
+ </span>
622
+ </span>
623
+ </div>
624
+ ) : null}
625
+ <div className="relative shrink-0" style={{ width }} aria-hidden="true">
626
+ {tiers.top.map((c) => (
627
+ <div key={c.key} className="absolute top-0 flex h-6 items-center border-s border-border px-2 first:border-s-0" style={{ left: c.x, width: c.w }}>
628
+ <span className="sticky left-[calc(var(--gantt-list)+8px)] truncate text-xs font-medium text-foreground">
629
+ {c.label}
630
+ </span>
631
+ </div>
632
+ ))}
633
+ {tiers.bottom.map((c) => (
634
+ <div
635
+ key={c.key}
636
+ className={cx("absolute bottom-0 flex h-7 items-center justify-center border-s border-border/60 first:border-s-0", c.weekend ? "text-muted-foreground/60" : "text-muted-foreground")}
637
+ style={{ left: c.x, width: c.w }}
638
+ >
639
+ {c.today && z.unit === "day" ? (
640
+ <span data-nums className="rounded-full bg-primary px-1.5 text-2xs font-semibold tabular-nums text-primary-foreground">
641
+ {c.label}
642
+ </span>
643
+ ) : c.w >= 22 ? (
644
+ <span data-nums className={cx("truncate text-2xs tabular-nums", c.today && "font-semibold text-foreground")}>
645
+ {c.label}
646
+ </span>
647
+ ) : null}
648
+ </div>
649
+ ))}
650
+ {todayIn && z.unit !== "day" ? <span className="absolute bottom-0 size-1.5 -translate-x-1/2 translate-y-1/2 rounded-full bg-primary" style={{ left: tx + dw / 2 }} /> : null}
651
+ </div>
652
+ </div>
653
+
654
+ <div className="relative flex">
655
+ {/* The list pane: sticky to the left while the timeline scrolls. */}
656
+ {showList ? (
657
+ <div ref={listRef} className="sticky left-0 z-30 w-44 shrink-0 border-e border-border bg-card sm:w-72">
658
+ {rows.list.map((r) =>
659
+ r.kind === "group" ? (
660
+ <GroupRow
661
+ key={r.key}
662
+ group={r.group}
663
+ by={by}
664
+ ctx={ctx}
665
+ count={r.group.tasks.length}
666
+ folded={folded.has(r.group.key)}
667
+ hot={hot === r.key}
668
+ onHot={() => setHot(r.key)}
669
+ onFold={() =>
670
+ setFolded((s) => {
671
+ const n = new Set(s);
672
+ if (n.has(r.group.key)) n.delete(r.group.key);
673
+ else n.add(r.group.key);
674
+ return n;
675
+ })
676
+ }
677
+ />
678
+ ) : r.kind === "task" ? (
679
+ <ListRow
680
+ key={r.key}
681
+ row={r}
682
+ ctx={ctx}
683
+ hot={hot === r.key}
684
+ onHot={() => setHot(r.key)}
685
+ onOpen={onOpen}
686
+ menu={menuFor(r)}
687
+ conflict={conflicted.has(r.task.id)}
688
+ blockers={(conflicted.get(r.task.id) ?? []).map((id) => name(byIdAll.get(id))).join(" and ")}
689
+ onFold={() =>
690
+ setCollapsed((s) => {
691
+ const n = new Set(s);
692
+ if (n.has(r.task.id)) n.delete(r.task.id);
693
+ else n.add(r.task.id);
694
+ return n;
695
+ })
696
+ }
697
+ />
698
+ ) : (
699
+ <QuickAdd
700
+ key={r.key}
701
+ uid={uid}
702
+ group={r.group}
703
+ addLabel={addLabel}
704
+ open={adding === r.group.key}
705
+ onOpen={() => setAdding(r.group.key)}
706
+ onClose={() => setAdding((a) => (a === r.group.key ? null : a))}
707
+ onCreate={create}
708
+ />
709
+ ),
710
+ )}
711
+ </div>
712
+ ) : null}
713
+
714
+ {/* The timeline. */}
715
+ <div className="relative shrink-0" style={{ width, height: rows.height }}>
716
+ <div aria-hidden="true" className="pointer-events-none absolute inset-0">
717
+ {bands.map((b) => (
718
+ <span key={b.start} className="absolute inset-y-0 bg-muted/50" style={{ left: b.x, width: b.w }} />
719
+ ))}
720
+ {tiers.bottom.slice(1).map((c) => (
721
+ <span key={c.key} className="absolute inset-y-0 w-px bg-border/50" style={{ left: c.x }} />
722
+ ))}
723
+ {rows.list
724
+ .filter((r) => r.kind === "group")
725
+ .map((r) => (
726
+ <span key={r.key} className={cx("absolute inset-x-0 bg-muted/40", r.y > 0 && "border-t border-border")} style={{ top: r.y, height: ROW }} />
727
+ ))}
728
+ </div>
729
+
730
+ {rows.list.map((r) => {
731
+ if (r.kind === "add") return <div key={r.key} style={{ height: ADD_ROW }} />;
732
+ if (r.kind === "group") {
733
+ const s = groupSpan(r.group.tasks, byId);
734
+ return (
735
+ <div key={r.key} className={cx("relative", hot === r.key && "bg-accent/40")} style={{ height: ROW }} onPointerEnter={() => setHot(r.key)}>
736
+ {s ? (
737
+ <span
738
+ aria-hidden="true"
739
+ className="absolute top-1/2 h-1.5 -translate-y-1/2 rounded-full bg-muted-foreground/25"
740
+ style={{ left: xOf(s.start, from, dw), width: Math.max(lengthOf(s) * dw, 6) }}
741
+ />
742
+ ) : null}
743
+ </div>
744
+ );
745
+ }
746
+ const box = boxes.get(r.task.id);
747
+ const first = box && Math.abs(box.y - (r.y + ROW / 2)) < 1;
748
+ return (
749
+ <TimelineRow key={r.key} row={r} hot={hot === r.key} onHot={() => setHot(r.key)} linking={linking && editable}>
750
+ {(over) =>
751
+ r.span ? (
752
+ <Bar
753
+ row={r}
754
+ x={r.milestone && !r.hasChildren ? xOf(r.span.end, from, dw) : xOf(r.span.start, from, dw)}
755
+ w={r.milestone && !r.hasChildren ? dw : Math.max(lengthOf(r.span) * dw, 6)}
756
+ dayWidth={dw}
757
+ tint={tintOf(r.task)}
758
+ shape={r.hasChildren ? "summary" : r.milestone ? "milestone" : "bar"}
759
+ editable={editable && first !== false}
760
+ onOpen={onOpen}
761
+ onPreview={onPreview}
762
+ onNudge={onNudge}
763
+ clickGuard={clickGuard}
764
+ dim={critical && path.size > 0 && !path.has(r.task.id)}
765
+ critical={critical && path.has(r.task.id)}
766
+ over={over}
767
+ moving={Boolean(drag && moved.has(r.task.id))}
768
+ hintId={`${uid}-hint`}
769
+ locale={locale}
770
+ />
771
+ ) : editable ? (
772
+ <Unscheduled row={r} dayWidth={dw} length={r.milestone ? 1 : newTaskLength} onSchedule={schedule} />
773
+ ) : null
774
+ }
775
+ </TimelineRow>
776
+ );
777
+ })}
778
+
779
+ {/* raw: an SVG drawing — the dependency arrows over the
780
+ bars; no kit part draws them. */}
781
+ <svg aria-hidden="true" className="pointer-events-none absolute inset-0 z-[5] overflow-visible" width={width} height={rows.height}>
782
+ {edges.map((e) => {
783
+ const a = boxes.get(e.from);
784
+ const b = boxes.get(e.to);
785
+ const { d, head } = arrowPath(a, b, { rowHeight: ROW });
786
+ const bad = conflicts.has(`${e.from}>${e.to}`);
787
+ const hotPath = critical && path.has(e.from) && path.has(e.to);
788
+ const faded = critical && path.size > 0 && !hotPath;
789
+ const stroke = bad ? "stroke-destructive" : hotPath ? "stroke-primary" : "stroke-muted-foreground/70";
790
+ const fill = bad ? "fill-destructive" : hotPath ? "fill-primary" : "fill-muted-foreground/70";
791
+ return (
792
+ <g key={`${e.from}>${e.to}`} className={cx(faded && "opacity-30", "motion-safe:transition-opacity motion-safe:duration-150")}>
793
+ <path d={d} fill="none" className={stroke} strokeWidth={hotPath ? 2 : 1.25} strokeDasharray={bad ? "4 3" : undefined} strokeLinecap="round" />
794
+ <path d={head} className={fill} />
795
+ </g>
796
+ );
797
+ })}
798
+ </svg>
799
+
800
+ {todayIn ? (
801
+ <span aria-hidden="true" className="pointer-events-none absolute inset-y-0 z-[4] w-px bg-primary/70" style={{ left: tx + dw / 2 }} />
802
+ ) : null}
803
+ </div>
804
+ </div>
805
+ </div>
806
+ </div>
807
+ </TaskDragProvider>
808
+ );
809
+ }
810
+
811
+ return (
812
+ <section aria-label={label} className={cx("grid min-w-0 gap-3", className)}>
813
+ {toolbar && all.length ? (
814
+ <ViewToolbar
815
+ view={view}
816
+ onViewChange={setViewRaw}
817
+ ctx={ctx}
818
+ tasks={all}
819
+ controls={["search", "filter", "group", "sort"]}
820
+ groupFields={GROUP_FIELDS}
821
+ count={shown.tasks.length}
822
+ extra={
823
+ toolbarExtra || addButton ? (
824
+ <>
825
+ {toolbarExtra}
826
+ {addButton}
827
+ </>
828
+ ) : null
829
+ }
830
+ />
831
+ ) : null}
832
+ {readError ? <Notice tone="danger">{readError}</Notice> : null}
833
+ {error ? (
834
+ <Notice tone="danger" onDismiss={clearError}>
835
+ {error}
836
+ </Notice>
837
+ ) : null}
838
+ {notice ? (
839
+ <Notice tone="warning" onDismiss={() => setNotice(null)}>
840
+ {notice}
841
+ </Notice>
842
+ ) : null}
843
+ <div className="min-w-0 overflow-hidden rounded-lg border border-border bg-card text-card-foreground">
844
+ {all.length && !loading ? bar : null}
845
+ {content}
846
+ {critical && all.length && !loading ? (
847
+ <p className="border-t border-border px-3 py-2 text-xs text-muted-foreground" aria-live="polite">
848
+ {path.size > 1 ? (
849
+ <>
850
+ <Icon name="zap" size={12} className="me-1 inline-block align-[-2px] text-primary" />
851
+ The critical path: {path.size} linked tasks, {pathDays} days end to end. Any day they slip, the finish slips.
852
+ </>
853
+ ) : (
854
+ "No critical path yet: link tasks (drag the dot past a bar's end onto another bar) to see the chain that sets the finish."
855
+ )}
856
+ </p>
857
+ ) : null}
858
+ </div>
859
+ <p id={`${uid}-hint`} className="sr-only">
860
+ Left and right arrows move it a day; with Shift they change its end, with Alt its start. Enter saves at once or opens it; Escape cancels a move.
861
+ </p>
862
+ <p className="sr-only" role="status" aria-live="polite">
863
+ {announce}
864
+ </p>
865
+ </section>
866
+ );
867
+ }
868
+
869
+ function groupSpan(tasks, byId) {
870
+ let s = null;
871
+ for (const t of tasks) {
872
+ const x = spanOf(byId.get(t.id) ?? t);
873
+ if (!x) continue;
874
+ s = s ? { start: x.start < s.start ? x.start : s.start, end: x.end > s.end ? x.end : s.end } : x;
875
+ }
876
+ return s;
877
+ }
878
+
879
+ // The empty state's action: name the first task (it starts today), or
880
+ // start from a template.
881
+ function FirstTask({ onCreate, starters, onStarter, addLabel, uid }) {
882
+ const [draft, setDraft] = useState("");
883
+ const [busy, setBusy] = useState(false);
884
+ async function submit() {
885
+ const title = draft.trim();
886
+ if (!title || busy) return;
887
+ setBusy(true);
888
+ await onCreate(title);
889
+ setBusy(false);
890
+ setDraft("");
891
+ }
892
+ return (
893
+ <div className="grid w-full max-w-sm justify-items-center gap-3">
894
+ <form
895
+ className="flex w-full items-center gap-2"
896
+ onSubmit={(e) => {
897
+ e.preventDefault();
898
+ submit();
899
+ }}
900
+ >
901
+ <label htmlFor={`${uid}-first`} className="sr-only">
902
+ The first task
903
+ </label>
904
+ <Input id={`${uid}-first`} value={draft} placeholder="What needs doing first?" onChange={(e) => setDraft(e.target.value)} />
905
+ <Button type="submit" variant="primary" icon="plus" loading={busy} disabled={!draft.trim()}>
906
+ {addLabel}
907
+ </Button>
908
+ </form>
909
+ {starters?.length ? (
910
+ <div className="grid justify-items-center gap-1.5">
911
+ <span className="text-xs text-muted-foreground">Or start from a plan</span>
912
+ <div className="flex flex-wrap justify-center gap-2">
913
+ {starters.map((s) => (
914
+ <Button key={s.label} size="sm" variant="secondary" icon={s.icon ?? "sparkles"} onClick={() => onStarter(s)}>
915
+ {s.label}
916
+ </Button>
917
+ ))}
918
+ </div>
919
+ </div>
920
+ ) : null}
921
+ </div>
922
+ );
923
+ }