makersclaw 0.7.0 → 0.8.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 (419) hide show
  1. package/README.md +17 -4
  2. package/package.json +1 -1
  3. package/r/activity-log.json +52 -0
  4. package/r/agent-changes.json +51 -0
  5. package/r/agent-chat.json +53 -0
  6. package/r/agent-column.json +85 -0
  7. package/r/agent-dock.json +57 -0
  8. package/r/agent-kit.json +49 -0
  9. package/r/agent-marks.json +49 -0
  10. package/r/agent-pane.json +93 -0
  11. package/r/ai-action.json +56 -0
  12. package/r/annotate.json +4 -4
  13. package/r/approval-request.json +2 -2
  14. package/r/approval.json +54 -0
  15. package/r/ask-bar.json +50 -0
  16. package/r/audio-player.json +2 -2
  17. package/r/auth-form.json +2 -2
  18. package/r/automation-editor.json +66 -0
  19. package/r/automation-list.json +57 -0
  20. package/r/automation-model.json +118 -0
  21. package/r/automations-screen.json +71 -0
  22. package/r/board.json +2 -2
  23. package/r/calendar-view.json +3 -3
  24. package/r/cart.json +2 -2
  25. package/r/chart.json +5 -5
  26. package/r/chat-pane.json +84 -0
  27. package/r/chat.json +3 -3
  28. package/r/code-editor.json +2 -2
  29. package/r/comment-box.json +3 -3
  30. package/r/connect-card.json +51 -0
  31. package/r/contact-section.json +2 -2
  32. package/r/core.json +18 -6
  33. package/r/cost.json +51 -0
  34. package/r/csv-import.json +3 -3
  35. package/r/data-picker.json +60 -0
  36. package/r/data-table.json +2 -2
  37. package/r/diagram.json +2 -2
  38. package/r/doc-editor.json +2 -2
  39. package/r/doc-page.json +3 -3
  40. package/r/email-render.json +2 -2
  41. package/r/faq.json +2 -2
  42. package/r/file-upload.json +2 -2
  43. package/r/first-run.json +63 -0
  44. package/r/fix-note.json +57 -0
  45. package/r/flow-canvas.json +3 -3
  46. package/r/flow-editor.json +79 -0
  47. package/r/footer.json +3 -3
  48. package/r/gantt.json +3 -3
  49. package/r/job-panel.json +2 -2
  50. package/r/json-editor.json +3 -3
  51. package/r/library-shelf.json +56 -0
  52. package/r/map.json +2 -2
  53. package/r/margin-notes.json +67 -0
  54. package/r/menubar.json +2 -2
  55. package/r/mind-map.json +4 -4
  56. package/r/navigation-menu.json +2 -2
  57. package/r/needs-you.json +55 -0
  58. package/r/order-detail.json +2 -2
  59. package/r/org-chart.json +4 -4
  60. package/r/pace-bar.json +69 -0
  61. package/r/page-builder.json +3 -3
  62. package/r/payment-methods.json +2 -2
  63. package/r/pdf-document.json +2 -2
  64. package/r/pdf-viewer.json +3 -3
  65. package/r/product-detail.json +2 -2
  66. package/r/product-grid.json +3 -3
  67. package/r/record-form.json +2 -2
  68. package/r/record-grid.json +52 -0
  69. package/r/record-notes.json +4 -4
  70. package/r/registry.json +4089 -1510
  71. package/r/run-history.json +52 -0
  72. package/r/run-view.json +80 -0
  73. package/r/schedule-card.json +54 -0
  74. package/r/schedule-engine.json +79 -0
  75. package/r/schedule-gantt.json +83 -0
  76. package/r/schedule-ruler.json +64 -0
  77. package/r/search-dialog.json +2 -2
  78. package/r/settings-list.json +2 -2
  79. package/r/setup-checklist.json +2 -2
  80. package/r/share-dialog.json +2 -2
  81. package/r/sidebar.json +3 -3
  82. package/r/signature-pad.json +2 -2
  83. package/r/sketch-canvas.json +3 -3
  84. package/r/source-receipt.json +52 -0
  85. package/r/spreadsheet.json +3 -3
  86. package/r/stat-cards.json +3 -3
  87. package/r/step-picker.json +53 -0
  88. package/r/step-settings.json +72 -0
  89. package/r/suggestion.json +58 -0
  90. package/r/sum-bar.json +64 -0
  91. package/r/task-list.json +3 -3
  92. package/r/task-model.json +2 -2
  93. package/r/testimonials.json +2 -2
  94. package/r/tier-map.json +67 -0
  95. package/r/time-grid.json +73 -0
  96. package/r/time-strip.json +64 -0
  97. package/r/top-bar.json +66 -0
  98. package/r/transaction-list.json +2 -2
  99. package/r/trigger-picker.json +61 -0
  100. package/r/whiteboard.json +4 -4
  101. package/r/workflow-view.json +55 -0
  102. package/registry/blocks/activity-log/README.md +112 -0
  103. package/registry/blocks/activity-log/activity-log.jsx +351 -0
  104. package/registry/blocks/activity-log/block.json +33 -0
  105. package/registry/blocks/activity-log/demo.jsx +48 -0
  106. package/registry/blocks/activity-log/index.js +4 -0
  107. package/registry/blocks/agent-changes/README.md +94 -0
  108. package/registry/blocks/agent-changes/agent-changes.jsx +118 -0
  109. package/registry/blocks/agent-changes/block.json +28 -0
  110. package/registry/blocks/agent-changes/demo.jsx +39 -0
  111. package/registry/blocks/agent-changes/index.js +3 -0
  112. package/registry/blocks/agent-chat/README.md +145 -0
  113. package/registry/blocks/agent-chat/agent-chat.jsx +415 -0
  114. package/registry/blocks/agent-chat/block.json +30 -0
  115. package/registry/blocks/agent-chat/demo.jsx +165 -0
  116. package/registry/blocks/agent-chat/index.js +3 -0
  117. package/registry/blocks/agent-column/README.md +197 -0
  118. package/registry/blocks/agent-column/agent-column.jsx +492 -0
  119. package/registry/blocks/agent-column/block.json +67 -0
  120. package/registry/blocks/agent-column/column-picker.jsx +163 -0
  121. package/registry/blocks/agent-column/demo.jsx +50 -0
  122. package/registry/blocks/agent-column/index.js +7 -0
  123. package/registry/blocks/agent-column/model.js +164 -0
  124. package/registry/blocks/agent-column/receipt.jsx +483 -0
  125. package/registry/blocks/agent-column/spreadsheet-type.jsx +24 -0
  126. package/registry/blocks/agent-dock/README.md +210 -0
  127. package/registry/blocks/agent-dock/agent-dock.jsx +356 -0
  128. package/registry/blocks/agent-dock/block.json +47 -0
  129. package/registry/blocks/agent-dock/demo.jsx +153 -0
  130. package/registry/blocks/agent-dock/dock-model.js +118 -0
  131. package/registry/blocks/agent-dock/index.js +5 -0
  132. package/registry/blocks/agent-kit/README.md +73 -0
  133. package/registry/blocks/agent-kit/agent-kit.jsx +86 -0
  134. package/registry/blocks/agent-kit/block.json +10 -0
  135. package/registry/blocks/agent-kit/demo.jsx +35 -0
  136. package/registry/blocks/agent-kit/index.js +3 -0
  137. package/registry/blocks/agent-marks/README.md +80 -0
  138. package/registry/blocks/agent-marks/agent-marks.jsx +133 -0
  139. package/registry/blocks/agent-marks/block.json +27 -0
  140. package/registry/blocks/agent-marks/demo.jsx +49 -0
  141. package/registry/blocks/agent-marks/index.js +3 -0
  142. package/registry/blocks/agent-pane/README.md +225 -0
  143. package/registry/blocks/agent-pane/agent-pane.jsx +244 -0
  144. package/registry/blocks/agent-pane/block.json +41 -0
  145. package/registry/blocks/agent-pane/composer.jsx +6 -0
  146. package/registry/blocks/agent-pane/demo.jsx +233 -0
  147. package/registry/blocks/agent-pane/dock-context.js +10 -0
  148. package/registry/blocks/agent-pane/index.js +4 -0
  149. package/registry/blocks/agent-pane/prose.jsx +84 -0
  150. package/registry/blocks/agent-pane/questions.jsx +134 -0
  151. package/registry/blocks/agent-pane/transcript.jsx +268 -0
  152. package/registry/blocks/agent-pane/work.jsx +368 -0
  153. package/registry/blocks/ai-action/README.md +94 -0
  154. package/registry/blocks/ai-action/ai-action.jsx +188 -0
  155. package/registry/blocks/ai-action/block.json +34 -0
  156. package/registry/blocks/ai-action/demo.jsx +93 -0
  157. package/registry/blocks/ai-action/index.js +3 -0
  158. package/registry/blocks/annotate/README.md +14 -11
  159. package/registry/blocks/approval/README.md +145 -0
  160. package/registry/blocks/approval/approval.jsx +163 -0
  161. package/registry/blocks/approval/block.json +31 -0
  162. package/registry/blocks/approval/demo.jsx +121 -0
  163. package/registry/blocks/approval/index.js +3 -0
  164. package/registry/blocks/ask-bar/README.md +105 -0
  165. package/registry/blocks/ask-bar/ask-bar.jsx +166 -0
  166. package/registry/blocks/ask-bar/block.json +26 -0
  167. package/registry/blocks/ask-bar/demo.jsx +78 -0
  168. package/registry/blocks/ask-bar/index.js +3 -0
  169. package/registry/blocks/automation-editor/README.md +291 -0
  170. package/registry/blocks/automation-editor/automation-editor.jsx +656 -0
  171. package/registry/blocks/automation-editor/block.json +39 -0
  172. package/registry/blocks/automation-editor/demo.jsx +284 -0
  173. package/registry/blocks/automation-editor/index.js +4 -0
  174. package/registry/blocks/automation-editor/run-inputs.jsx +82 -0
  175. package/registry/blocks/automation-list/README.md +129 -0
  176. package/registry/blocks/automation-list/automation-list.jsx +476 -0
  177. package/registry/blocks/automation-list/block.json +35 -0
  178. package/registry/blocks/automation-list/demo.jsx +67 -0
  179. package/registry/blocks/automation-list/index.js +3 -0
  180. package/registry/blocks/automation-model/README.md +109 -0
  181. package/registry/blocks/automation-model/block.json +135 -0
  182. package/registry/blocks/automation-model/catalog.js +256 -0
  183. package/registry/blocks/automation-model/check.js +161 -0
  184. package/registry/blocks/automation-model/demo.jsx +42 -0
  185. package/registry/blocks/automation-model/edit.js +262 -0
  186. package/registry/blocks/automation-model/fixes.js +287 -0
  187. package/registry/blocks/automation-model/glyph.jsx +13 -0
  188. package/registry/blocks/automation-model/graph.js +410 -0
  189. package/registry/blocks/automation-model/index.js +33 -0
  190. package/registry/blocks/automation-model/overlay.js +175 -0
  191. package/registry/blocks/automation-model/parts.jsx +94 -0
  192. package/registry/blocks/automation-model/schedule.js +53 -0
  193. package/registry/blocks/automation-model/templates.js +128 -0
  194. package/registry/blocks/automation-model/values.js +29 -0
  195. package/registry/blocks/automation-model/words.js +439 -0
  196. package/registry/blocks/automations-screen/README.md +207 -0
  197. package/registry/blocks/automations-screen/automations-screen.jsx +452 -0
  198. package/registry/blocks/automations-screen/block.json +47 -0
  199. package/registry/blocks/automations-screen/demo.jsx +364 -0
  200. package/registry/blocks/automations-screen/index.js +4 -0
  201. package/registry/blocks/automations-screen/screen-model.js +129 -0
  202. package/registry/blocks/chart/cartesian.jsx +1 -1
  203. package/registry/blocks/chart/core.jsx +33 -23
  204. package/registry/blocks/chart/pie.jsx +1 -1
  205. package/registry/blocks/chat/README.md +4 -1
  206. package/registry/blocks/chat/demo.jsx +2 -2
  207. package/registry/blocks/chat-pane/README.md +143 -0
  208. package/registry/blocks/chat-pane/block.json +48 -0
  209. package/registry/blocks/chat-pane/chat-attach.js +166 -0
  210. package/registry/blocks/chat-pane/chat-composer.jsx +195 -0
  211. package/registry/blocks/chat-pane/chat-context.js +17 -0
  212. package/registry/blocks/chat-pane/chat-model.js +43 -0
  213. package/registry/blocks/chat-pane/chat-pane.jsx +398 -0
  214. package/registry/blocks/chat-pane/chats-list.jsx +52 -0
  215. package/registry/blocks/chat-pane/demo.jsx +103 -0
  216. package/registry/blocks/chat-pane/index.js +9 -0
  217. package/registry/blocks/connect-card/README.md +99 -0
  218. package/registry/blocks/connect-card/block.json +10 -0
  219. package/registry/blocks/connect-card/connect-card.jsx +103 -0
  220. package/registry/blocks/connect-card/demo.jsx +49 -0
  221. package/registry/blocks/connect-card/index.js +3 -0
  222. package/registry/blocks/cost/README.md +115 -0
  223. package/registry/blocks/cost/block.json +10 -0
  224. package/registry/blocks/cost/cost.jsx +176 -0
  225. package/registry/blocks/cost/demo.jsx +66 -0
  226. package/registry/blocks/cost/index.js +3 -0
  227. package/registry/blocks/data-picker/README.md +81 -0
  228. package/registry/blocks/data-picker/block.json +38 -0
  229. package/registry/blocks/data-picker/data-picker.jsx +210 -0
  230. package/registry/blocks/data-picker/demo.jsx +34 -0
  231. package/registry/blocks/data-picker/fields.js +89 -0
  232. package/registry/blocks/data-picker/index.js +4 -0
  233. package/registry/blocks/data-table/demo.jsx +1 -1
  234. package/registry/blocks/first-run/README.md +118 -0
  235. package/registry/blocks/first-run/block.json +11 -0
  236. package/registry/blocks/first-run/demo.jsx +88 -0
  237. package/registry/blocks/first-run/first-run-model.js +51 -0
  238. package/registry/blocks/first-run/first-run.jsx +156 -0
  239. package/registry/blocks/first-run/index.js +5 -0
  240. package/registry/blocks/first-run/probe-rules.js +26 -0
  241. package/registry/blocks/fix-note/README.md +79 -0
  242. package/registry/blocks/fix-note/block.json +35 -0
  243. package/registry/blocks/fix-note/demo.jsx +30 -0
  244. package/registry/blocks/fix-note/fix-note.jsx +235 -0
  245. package/registry/blocks/fix-note/index.js +3 -0
  246. package/registry/blocks/flow-canvas/README.md +15 -8
  247. package/registry/blocks/flow-editor/README.md +108 -0
  248. package/registry/blocks/flow-editor/block.json +44 -0
  249. package/registry/blocks/flow-editor/demo.jsx +115 -0
  250. package/registry/blocks/flow-editor/flow-canvas.jsx +568 -0
  251. package/registry/blocks/flow-editor/flow-editor.jsx +154 -0
  252. package/registry/blocks/flow-editor/index.js +6 -0
  253. package/registry/blocks/flow-editor/minimap.jsx +78 -0
  254. package/registry/blocks/flow-editor/outline.jsx +134 -0
  255. package/registry/blocks/footer/README.md +3 -2
  256. package/registry/blocks/library-shelf/README.md +117 -0
  257. package/registry/blocks/library-shelf/block.json +28 -0
  258. package/registry/blocks/library-shelf/demo.jsx +68 -0
  259. package/registry/blocks/library-shelf/index.js +3 -0
  260. package/registry/blocks/library-shelf/library-shelf.jsx +211 -0
  261. package/registry/blocks/library-shelf/probe-rules.js +15 -0
  262. package/registry/blocks/margin-notes/README.md +222 -0
  263. package/registry/blocks/margin-notes/block.json +10 -0
  264. package/registry/blocks/margin-notes/demo.jsx +144 -0
  265. package/registry/blocks/margin-notes/index.js +20 -0
  266. package/registry/blocks/margin-notes/margin-model.js +224 -0
  267. package/registry/blocks/margin-notes/margin-notes.jsx +465 -0
  268. package/registry/blocks/mind-map/README.md +4 -2
  269. package/registry/blocks/needs-you/README.md +177 -0
  270. package/registry/blocks/needs-you/block.json +34 -0
  271. package/registry/blocks/needs-you/demo.jsx +143 -0
  272. package/registry/blocks/needs-you/index.js +3 -0
  273. package/registry/blocks/needs-you/needs-you.jsx +441 -0
  274. package/registry/blocks/org-chart/README.md +4 -2
  275. package/registry/blocks/pace-bar/README.md +230 -0
  276. package/registry/blocks/pace-bar/block.json +10 -0
  277. package/registry/blocks/pace-bar/demo.jsx +255 -0
  278. package/registry/blocks/pace-bar/index.js +22 -0
  279. package/registry/blocks/pace-bar/pace-bar.jsx +461 -0
  280. package/registry/blocks/pace-bar/pace-model.js +288 -0
  281. package/registry/blocks/page-builder/README.md +1 -1
  282. package/registry/blocks/page-builder/demo.jsx +3 -3
  283. package/registry/blocks/pdf-viewer/README.md +4 -1
  284. package/registry/blocks/record-grid/README.md +100 -0
  285. package/registry/blocks/record-grid/block.json +28 -0
  286. package/registry/blocks/record-grid/demo.jsx +43 -0
  287. package/registry/blocks/record-grid/index.js +3 -0
  288. package/registry/blocks/record-grid/record-grid.jsx +251 -0
  289. package/registry/blocks/run-history/README.md +138 -0
  290. package/registry/blocks/run-history/block.json +10 -0
  291. package/registry/blocks/run-history/demo.jsx +74 -0
  292. package/registry/blocks/run-history/index.js +3 -0
  293. package/registry/blocks/run-history/run-history.jsx +218 -0
  294. package/registry/blocks/run-view/README.md +174 -0
  295. package/registry/blocks/run-view/block.json +50 -0
  296. package/registry/blocks/run-view/demo.jsx +286 -0
  297. package/registry/blocks/run-view/index.js +5 -0
  298. package/registry/blocks/run-view/run-controls.jsx +266 -0
  299. package/registry/blocks/run-view/run-view.jsx +236 -0
  300. package/registry/blocks/run-view/runs-list.jsx +99 -0
  301. package/registry/blocks/schedule-card/README.md +121 -0
  302. package/registry/blocks/schedule-card/block.json +10 -0
  303. package/registry/blocks/schedule-card/demo.jsx +82 -0
  304. package/registry/blocks/schedule-card/index.js +3 -0
  305. package/registry/blocks/schedule-card/schedule-card.jsx +136 -0
  306. package/registry/blocks/schedule-engine/README.md +137 -0
  307. package/registry/blocks/schedule-engine/block.json +10 -0
  308. package/registry/blocks/schedule-engine/demo.jsx +82 -0
  309. package/registry/blocks/schedule-engine/engine.js +769 -0
  310. package/registry/blocks/schedule-engine/index.js +9 -0
  311. package/registry/blocks/schedule-engine/ops.js +71 -0
  312. package/registry/blocks/schedule-engine/place.js +294 -0
  313. package/registry/blocks/schedule-engine/tree.js +79 -0
  314. package/registry/blocks/schedule-engine/waits.js +80 -0
  315. package/registry/blocks/schedule-gantt/README.md +159 -0
  316. package/registry/blocks/schedule-gantt/block.json +10 -0
  317. package/registry/blocks/schedule-gantt/demo.jsx +223 -0
  318. package/registry/blocks/schedule-gantt/gantt-model.js +219 -0
  319. package/registry/blocks/schedule-gantt/geometry.js +176 -0
  320. package/registry/blocks/schedule-gantt/index.js +26 -0
  321. package/registry/blocks/schedule-gantt/schedule-gantt.jsx +882 -0
  322. package/registry/blocks/schedule-gantt/sheet.jsx +372 -0
  323. package/registry/blocks/schedule-gantt/track.jsx +190 -0
  324. package/registry/blocks/schedule-ruler/README.md +148 -0
  325. package/registry/blocks/schedule-ruler/block.json +10 -0
  326. package/registry/blocks/schedule-ruler/demo.jsx +181 -0
  327. package/registry/blocks/schedule-ruler/index.js +21 -0
  328. package/registry/blocks/schedule-ruler/ruler-model.js +239 -0
  329. package/registry/blocks/schedule-ruler/schedule-ruler.jsx +548 -0
  330. package/registry/blocks/sidebar/README.md +23 -13
  331. package/registry/blocks/sketch-canvas/README.md +15 -9
  332. package/registry/blocks/source-receipt/README.md +97 -0
  333. package/registry/blocks/source-receipt/block.json +30 -0
  334. package/registry/blocks/source-receipt/demo.jsx +71 -0
  335. package/registry/blocks/source-receipt/index.js +3 -0
  336. package/registry/blocks/source-receipt/source-receipt.jsx +107 -0
  337. package/registry/blocks/spreadsheet/spreadsheet.jsx +15 -1
  338. package/registry/blocks/step-picker/README.md +78 -0
  339. package/registry/blocks/step-picker/block.json +31 -0
  340. package/registry/blocks/step-picker/demo.jsx +23 -0
  341. package/registry/blocks/step-picker/index.js +3 -0
  342. package/registry/blocks/step-picker/step-picker.jsx +144 -0
  343. package/registry/blocks/step-settings/README.md +100 -0
  344. package/registry/blocks/step-settings/block.json +44 -0
  345. package/registry/blocks/step-settings/demo.jsx +93 -0
  346. package/registry/blocks/step-settings/fields.jsx +58 -0
  347. package/registry/blocks/step-settings/flow-settings.jsx +115 -0
  348. package/registry/blocks/step-settings/index.js +5 -0
  349. package/registry/blocks/step-settings/step-settings.jsx +290 -0
  350. package/registry/blocks/suggestion/README.md +171 -0
  351. package/registry/blocks/suggestion/block.json +40 -0
  352. package/registry/blocks/suggestion/demo.jsx +91 -0
  353. package/registry/blocks/suggestion/index.js +3 -0
  354. package/registry/blocks/suggestion/suggestion.jsx +436 -0
  355. package/registry/blocks/sum-bar/README.md +122 -0
  356. package/registry/blocks/sum-bar/block.json +10 -0
  357. package/registry/blocks/sum-bar/demo.jsx +110 -0
  358. package/registry/blocks/sum-bar/index.js +5 -0
  359. package/registry/blocks/sum-bar/sum-bar.jsx +209 -0
  360. package/registry/blocks/sum-bar/sum-model.js +126 -0
  361. package/registry/blocks/tier-map/README.md +217 -0
  362. package/registry/blocks/tier-map/block.json +10 -0
  363. package/registry/blocks/tier-map/demo.jsx +222 -0
  364. package/registry/blocks/tier-map/index.js +24 -0
  365. package/registry/blocks/tier-map/tier-map.jsx +574 -0
  366. package/registry/blocks/tier-map/tier-model.js +318 -0
  367. package/registry/blocks/time-grid/README.md +305 -0
  368. package/registry/blocks/time-grid/block.json +10 -0
  369. package/registry/blocks/time-grid/demo.jsx +279 -0
  370. package/registry/blocks/time-grid/grid-model.js +322 -0
  371. package/registry/blocks/time-grid/index.js +25 -0
  372. package/registry/blocks/time-grid/time-grid.jsx +1325 -0
  373. package/registry/blocks/time-strip/README.md +141 -0
  374. package/registry/blocks/time-strip/block.json +10 -0
  375. package/registry/blocks/time-strip/demo.jsx +129 -0
  376. package/registry/blocks/time-strip/index.js +19 -0
  377. package/registry/blocks/time-strip/strip-model.js +178 -0
  378. package/registry/blocks/time-strip/time-strip.jsx +260 -0
  379. package/registry/blocks/top-bar/README.md +149 -0
  380. package/registry/blocks/top-bar/block.json +35 -0
  381. package/registry/blocks/top-bar/demo.jsx +78 -0
  382. package/registry/blocks/top-bar/index.js +5 -0
  383. package/registry/blocks/top-bar/probe-rules.js +74 -0
  384. package/registry/blocks/top-bar/top-bar-model.js +152 -0
  385. package/registry/blocks/top-bar/top-bar.jsx +244 -0
  386. package/registry/blocks/trigger-picker/README.md +98 -0
  387. package/registry/blocks/trigger-picker/block.json +38 -0
  388. package/registry/blocks/trigger-picker/demo.jsx +59 -0
  389. package/registry/blocks/trigger-picker/index.js +3 -0
  390. package/registry/blocks/trigger-picker/trigger-picker.jsx +374 -0
  391. package/registry/blocks/whiteboard/README.md +20 -14
  392. package/registry/blocks/workflow-view/README.md +123 -0
  393. package/registry/blocks/workflow-view/block.json +10 -0
  394. package/registry/blocks/workflow-view/demo.jsx +90 -0
  395. package/registry/blocks/workflow-view/index.js +3 -0
  396. package/registry/blocks/workflow-view/workflow-view.jsx +203 -0
  397. package/src/cli.mjs +2 -2
  398. package/src/install.mjs +100 -22
  399. package/src/registry.mjs +37 -3
  400. package/templates/next/browser/lib/data.js +6 -1
  401. package/templates/next/files/README.md +909 -26
  402. package/templates/next/files/app/shell.jsx +54 -37
  403. package/templates/next/files/check/automation-format.ts +2103 -0
  404. package/templates/next/files/check/automation-package.ts +142 -0
  405. package/templates/next/files/check/run.mjs +1043 -12
  406. package/templates/next/files/components/ui/field.jsx +17 -4
  407. package/templates/next/files/components/ui/icon.jsx +13 -0
  408. package/templates/next/files/components/ui/index.js +3 -1
  409. package/templates/next/files/components/ui/layout.jsx +382 -0
  410. package/templates/next/files/components/ui/notice.jsx +70 -0
  411. package/templates/next/files/components/ui/page.jsx +124 -0
  412. package/templates/next/files/components/ui/stat.jsx +22 -7
  413. package/templates/next/files/components/ui/toast.jsx +6 -3
  414. package/templates/next/files/lib/automations.js +660 -0
  415. package/templates/next/files/lib/claw.js +244 -5
  416. package/templates/next/files/lib/door.js +92 -0
  417. package/templates/next/files/lib/hooks.js +1694 -6
  418. package/templates/next/files/lib/platform.js +611 -14
  419. package/templates/next/template.json +22 -3
@@ -0,0 +1,769 @@
1
+ // Schedule engine: the rules every date in a plan follows.
2
+ //
3
+ // Pure functions over plain rows: no React, no lib/, no I/O. The browser
4
+ // runs them on every pointer move of a drag (the live preview), the server
5
+ // runs them again on fresh rows before it writes, and the block's tests
6
+ // (test/schedule-engine.test.mjs) run them under node. A database function
7
+ // and an agent's reference can state the same rules. README.md beside this
8
+ // file lists them.
9
+ //
10
+ // Rows in:
11
+ // task { id, parent_id, start_date, due_date, is_milestone, progress, status }
12
+ // link { id, task_id, depends_on_id, kind, lag_days } (task_id waits on depends_on_id)
13
+ // Dates are 'YYYY-MM-DD' text: calendar days, never instants.
14
+ //
15
+ // The model, in working days (Monday to Friday by default):
16
+ // * A task occupies [s, s + d): it starts at the beginning of working day s
17
+ // and runs d working days (d >= 1). Its due date is working day s + d - 1.
18
+ // * A milestone takes no time. It sits at the end of its day, so s is the
19
+ // index after that day and d = 0. A finish-to-start successor of a task
20
+ // that ends on Friday starts Monday; a milestone after it shows Friday.
21
+ // * A task's own planned start is a floor ("not before"): the forward pass
22
+ // never moves a task earlier than its planned start, only later.
23
+ // * Finished work (status done, or 100%) is pinned where it happened.
24
+ // * A task with sub-tasks is a summary: its dates are its leaves' span. A
25
+ // link to a summary binds every leaf under it; a link from a summary
26
+ // reads the summary's span.
27
+ // * Cancelled tasks are off the plan; so are tasks with no dates.
28
+
29
+ const DAY_MS = 86400000;
30
+
31
+ const pad = (n) => String(n).padStart(2, "0");
32
+
33
+ /** 'YYYY-MM-DD' (or a longer ISO string) → whole UTC day number, or null. */
34
+ export function dayOf(iso) {
35
+ const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(String(iso ?? ""));
36
+ return m ? Math.round(Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3])) / DAY_MS) : null;
37
+ }
38
+
39
+ /** Whole UTC day number → 'YYYY-MM-DD'. */
40
+ export function isoOf(day) {
41
+ const d = new Date(day * DAY_MS);
42
+ return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())}`;
43
+ }
44
+
45
+ /** Monday 0 … Sunday 6 (1970-01-01 was a Thursday). */
46
+ export const weekdayOf = (day) => (((day + 3) % 7) + 7) % 7;
47
+
48
+ /**
49
+ * A working calendar over day numbers.
50
+ * startIndex(day) the working day on or after `day`, as an index
51
+ * lastIndex(day) the working day on or before `day`, as an index
52
+ * dayAt(index) back to a day number
53
+ * "weekdays" (the default) works Monday to Friday; "everyday" works all
54
+ * seven days.
55
+ */
56
+ export function makeCalendar(kind = "weekdays") {
57
+ if (kind && typeof kind === "object") return kind;
58
+ if (kind === "everyday") {
59
+ return { kind: "everyday", isWorkday: () => true, startIndex: (d) => d, lastIndex: (d) => d, dayAt: (i) => i };
60
+ }
61
+ const index = (day) => Math.floor((day + 3) / 7) * 5 + weekdayOf(day);
62
+ return {
63
+ kind: "weekdays",
64
+ isWorkday: (day) => weekdayOf(day) < 5,
65
+ startIndex: (day) => {
66
+ const w = weekdayOf(day);
67
+ return index(w >= 5 ? day + 7 - w : day);
68
+ },
69
+ lastIndex: (day) => {
70
+ const w = weekdayOf(day);
71
+ return index(w >= 5 ? day - (w - 4) : day);
72
+ },
73
+ dayAt: (i) => {
74
+ const weeks = Math.floor(i / 5);
75
+ return weeks * 7 + (i - weeks * 5) - 3;
76
+ },
77
+ };
78
+ }
79
+
80
+ const KINDS = {
81
+ finish_to_start: "FS",
82
+ start_to_start: "SS",
83
+ finish_to_finish: "FF",
84
+ start_to_finish: "SF",
85
+ fs: "FS",
86
+ ss: "SS",
87
+ ff: "FF",
88
+ sf: "SF",
89
+ };
90
+ /** The link kinds as the database writes them. */
91
+ export const LINK_KINDS = { FS: "finish_to_start", SS: "start_to_start", FF: "finish_to_finish", SF: "start_to_finish" };
92
+
93
+ /** finish_to_start | start_to_start | … (or FS/SS/FF/SF) → "FS" | "SS" | "FF" | "SF". */
94
+ export function linkKind(kind) {
95
+ return KINDS[String(kind ?? "").toLowerCase()] ?? "FS";
96
+ }
97
+
98
+ /** A task's stored dates as a span in working days: { dated, s, d, milestone }. */
99
+ export function spanOf(task, calendar = "weekdays") {
100
+ const cal = makeCalendar(calendar);
101
+ const milestone = Boolean(task?.is_milestone);
102
+ const a = dayOf(task?.start_date);
103
+ const b = dayOf(task?.due_date);
104
+ if (a == null && b == null) return { dated: false, s: 0, d: 0, milestone };
105
+ if (milestone) return { dated: true, s: cal.lastIndex(b ?? a) + 1, d: 0, milestone };
106
+ const start = a ?? b;
107
+ const due = Math.max(start, b ?? a);
108
+ const s = cal.startIndex(start);
109
+ return { dated: true, s, d: Math.max(1, cal.lastIndex(due) + 1 - s), milestone };
110
+ }
111
+
112
+ /** A span back to dates: { start, due } ('YYYY-MM-DD'). A milestone shows its day. */
113
+ export function datesOf(s, d, milestone, calendar = "weekdays") {
114
+ const cal = makeCalendar(calendar);
115
+ if (milestone) {
116
+ const day = isoOf(cal.dayAt(s - 1));
117
+ return { start: day, due: day };
118
+ }
119
+ return { start: isoOf(cal.dayAt(s)), due: isoOf(cal.dayAt(s + Math.max(1, d) - 1)) };
120
+ }
121
+
122
+ /** Working days from `from` to `to`: 0 when the same working day, negative when `to` is earlier. */
123
+ export function workdaysBetween(from, to, calendar = "weekdays") {
124
+ const cal = makeCalendar(calendar);
125
+ const a = dayOf(from);
126
+ const b = dayOf(to);
127
+ if (a == null || b == null) return null;
128
+ return cal.lastIndex(b) - cal.lastIndex(a);
129
+ }
130
+
131
+ /** The date `n` working days after `iso` (n may be negative). A weekend `iso` counts from the Friday before. */
132
+ export function addWorkdays(iso, n, calendar = "weekdays") {
133
+ const cal = makeCalendar(calendar);
134
+ const day = dayOf(iso);
135
+ if (day == null) return null;
136
+ return isoOf(cal.dayAt(cal.lastIndex(day) + n));
137
+ }
138
+
139
+ /** Working days a task runs, from its dates (milestones 0). */
140
+ export function durationOf(start, due, milestone = false, calendar = "weekdays") {
141
+ const span = spanOf({ start_date: start, due_date: due, is_milestone: milestone }, calendar);
142
+ return span.dated ? span.d : null;
143
+ }
144
+
145
+ /* ------------------------------------------------------------------ */
146
+ /* The network */
147
+ /* ------------------------------------------------------------------ */
148
+
149
+ /**
150
+ * Build the scheduling network: nodes for tasks (leaf, summary or undated),
151
+ * dependency edges expanded to the successor's leaves, and a topological
152
+ * order over both the edges and the summary roll-ups. Tasks stuck in (or
153
+ * behind) a loop are left out of the order and listed in `stuck`.
154
+ */
155
+ export function buildNetwork(tasks, links, { calendar = "weekdays" } = {}) {
156
+ const cal = makeCalendar(calendar);
157
+ const nodes = new Map();
158
+ const list = [];
159
+ for (const t of tasks ?? []) {
160
+ if (!t || t.id == null) continue;
161
+ const status = String(t.status ?? "");
162
+ if (status === "cancelled") continue;
163
+ const id = String(t.id);
164
+ if (nodes.has(id)) continue;
165
+ const span = spanOf(t, cal);
166
+ const node = {
167
+ id,
168
+ task: t,
169
+ parentId: t.parent_id == null ? null : String(t.parent_id),
170
+ children: [],
171
+ milestone: span.milestone,
172
+ pinned: status === "done" || Number(t.progress ?? 0) >= 100,
173
+ datedSelf: span.dated,
174
+ s: span.s,
175
+ d: span.d,
176
+ kind: null,
177
+ es: null,
178
+ ef: null,
179
+ ls: null,
180
+ lf: null,
181
+ slack: null,
182
+ stuck: false,
183
+ in: [],
184
+ out: [],
185
+ lfLimit: Infinity,
186
+ lsLimit: Infinity,
187
+ leaves: null,
188
+ };
189
+ nodes.set(id, node);
190
+ list.push(node);
191
+ }
192
+
193
+ // Parents: one outside the set (another project, cancelled) makes a
194
+ // root, and a parent chain that loops is cut where it closes.
195
+ for (const n of list) if (n.parentId && (!nodes.has(n.parentId) || n.parentId === n.id)) n.parentId = null;
196
+ for (const n of list) {
197
+ const seen = new Set([n.id]);
198
+ let cur = n;
199
+ while (cur.parentId) {
200
+ if (seen.has(cur.parentId)) {
201
+ cur.parentId = null;
202
+ break;
203
+ }
204
+ seen.add(cur.parentId);
205
+ cur = nodes.get(cur.parentId);
206
+ }
207
+ }
208
+ for (const n of list) if (n.parentId) nodes.get(n.parentId).children.push(n);
209
+
210
+ // Kinds, bottom up: a task with a dated leaf below it is a summary.
211
+ const kindOf = (n) => {
212
+ if (n.kind) return n.kind;
213
+ let dated = false;
214
+ for (const c of n.children) {
215
+ const k = kindOf(c);
216
+ if (k === "leaf" || k === "summary") dated = true;
217
+ }
218
+ n.kind = dated ? "summary" : n.datedSelf ? "leaf" : "undated";
219
+ return n.kind;
220
+ };
221
+ for (const n of list) kindOf(n);
222
+ const leavesOf = (n) => {
223
+ if (n.leaves) return n.leaves;
224
+ n.leaves = n.kind === "leaf" ? [n] : n.kind === "summary" ? n.children.flatMap(leavesOf) : [];
225
+ return n.leaves;
226
+ };
227
+ for (const n of list) leavesOf(n);
228
+ const isAncestor = (a, b) => {
229
+ for (let p = b.parentId ? nodes.get(b.parentId) : null; p; p = p.parentId ? nodes.get(p.parentId) : null) if (p === a) return true;
230
+ return false;
231
+ };
232
+
233
+ // Stored spans of summaries (for the conflict check on stored dates).
234
+ for (const n of list) {
235
+ if (n.kind !== "summary") continue;
236
+ let s = Infinity;
237
+ let f = -Infinity;
238
+ for (const l of n.leaves) {
239
+ s = Math.min(s, l.s);
240
+ f = Math.max(f, l.s + l.d);
241
+ }
242
+ n.s = s;
243
+ n.d = f - s;
244
+ }
245
+
246
+ // Dependency edges.
247
+ const linkInfo = new Map();
248
+ const edges = [];
249
+ for (const l of links ?? []) {
250
+ if (!l) continue;
251
+ const id = String(l.id ?? `${l.task_id}>${l.depends_on_id}`);
252
+ const kind = linkKind(l.kind);
253
+ const lag = Math.trunc(Number(l.lag_days ?? 0)) || 0;
254
+ const info = { id, taskId: String(l.task_id), dependsOnId: String(l.depends_on_id), kind, lag, valid: false, reason: null, edges: [] };
255
+ linkInfo.set(id, info);
256
+ const succ = nodes.get(info.taskId);
257
+ const pred = nodes.get(info.dependsOnId);
258
+ if (!succ || !pred) info.reason = "missing";
259
+ else if (succ === pred) info.reason = "self";
260
+ else if (succ.kind === "undated" || pred.kind === "undated") info.reason = "undated";
261
+ else if (isAncestor(succ, pred) || isAncestor(pred, succ)) info.reason = "family";
262
+ else {
263
+ info.valid = true;
264
+ for (const leaf of succ.leaves) {
265
+ const e = { link: info, from: pred, to: leaf, kind, lag };
266
+ edges.push(e);
267
+ info.edges.push(e);
268
+ pred.out.push(e);
269
+ leaf.in.push(e);
270
+ }
271
+ }
272
+ }
273
+
274
+ // Topological order over dependency edges and roll-ups (child → summary).
275
+ const active = list.filter((n) => n.kind === "leaf" || n.kind === "summary");
276
+ const indeg = new Map(active.map((n) => [n, 0]));
277
+ const next = new Map(active.map((n) => [n, []]));
278
+ const edge = (a, b) => {
279
+ next.get(a).push(b);
280
+ indeg.set(b, indeg.get(b) + 1);
281
+ };
282
+ for (const e of edges) edge(e.from, e.to);
283
+ for (const n of active) if (n.parentId) edge(n, nodes.get(n.parentId));
284
+ const order = active.filter((n) => indeg.get(n) === 0);
285
+ for (let i = 0; i < order.length; i += 1) {
286
+ for (const m of next.get(order[i])) {
287
+ indeg.set(m, indeg.get(m) - 1);
288
+ if (indeg.get(m) === 0) order.push(m);
289
+ }
290
+ }
291
+ const stuck = active.filter((n) => indeg.get(n) > 0);
292
+ for (const n of stuck) n.stuck = true;
293
+
294
+ return { calendar: cal, nodes, list, order, stuck, edges, links: linkInfo, isAncestor, finish: null, start: null };
295
+ }
296
+
297
+ /** The earliest start a dependency edge allows its successor leaf. */
298
+ function required(e, pred = e.from) {
299
+ const d = e.to.d;
300
+ switch (e.kind) {
301
+ case "SS":
302
+ return pred.es + e.lag;
303
+ case "FF":
304
+ return pred.ef + e.lag - d;
305
+ case "SF":
306
+ return pred.es + e.lag - d;
307
+ default:
308
+ return pred.ef + e.lag;
309
+ }
310
+ }
311
+
312
+ /** The same, on stored dates (the conflict check). */
313
+ function requiredStored(e) {
314
+ return required(e, { es: e.from.s, ef: e.from.s + e.from.d });
315
+ }
316
+
317
+ /**
318
+ * Forward pass: each leaf starts at the later of its own planned start and
319
+ * what every link into it allows (pinned work stays put); each summary
320
+ * spans its children. Sets es/ef (working-day indexes) on every node, and
321
+ * net.start / net.finish.
322
+ */
323
+ export function forwardPass(net) {
324
+ const rollup = (n) => {
325
+ let es = Infinity;
326
+ let ef = -Infinity;
327
+ for (const c of n.children) {
328
+ if (c.es == null) continue;
329
+ es = Math.min(es, c.es);
330
+ ef = Math.max(ef, c.ef);
331
+ }
332
+ n.es = es;
333
+ n.ef = ef;
334
+ };
335
+ for (const n of net.order) {
336
+ if (n.kind === "summary") {
337
+ rollup(n);
338
+ continue;
339
+ }
340
+ let es = n.s;
341
+ if (!n.pinned) for (const e of n.in) es = Math.max(es, required(e));
342
+ n.es = es;
343
+ n.ef = es + n.d;
344
+ }
345
+ // Stuck in a loop: keep the stored dates; summaries roll up deepest first.
346
+ const depth = (n) => {
347
+ let k = 0;
348
+ for (let p = n.parentId; p; p = net.nodes.get(p).parentId) k += 1;
349
+ return k;
350
+ };
351
+ for (const n of net.stuck) {
352
+ if (n.kind === "leaf") {
353
+ n.es = n.s;
354
+ n.ef = n.s + n.d;
355
+ }
356
+ }
357
+ for (const n of [...net.stuck].filter((k) => k.kind === "summary").sort((a, b) => depth(b) - depth(a))) rollup(n);
358
+
359
+ let start = Infinity;
360
+ let finish = -Infinity;
361
+ for (const n of net.list) {
362
+ if (n.kind !== "leaf") continue;
363
+ start = Math.min(start, n.es);
364
+ finish = Math.max(finish, n.ef);
365
+ }
366
+ net.start = Number.isFinite(start) ? start : null;
367
+ net.finish = Number.isFinite(finish) ? finish : null;
368
+ return net;
369
+ }
370
+
371
+ /**
372
+ * Backward pass: the latest each leaf may finish without moving the
373
+ * project's finish (or breaking a link to work that is itself as late as
374
+ * it may be). Total slack = latest finish − earliest finish, in working
375
+ * days. A link to a summary binds its leaves; a link from a summary binds
376
+ * every leaf for a finish (FS, FF) and the leaves that set its start for a
377
+ * start (SS, SF).
378
+ */
379
+ export function backwardPass(net) {
380
+ if (net.finish == null) return net;
381
+ for (const n of net.list) {
382
+ n.lfLimit = Infinity;
383
+ n.lsLimit = Infinity;
384
+ n.ls = null;
385
+ n.lf = null;
386
+ n.slack = null;
387
+ }
388
+ for (let i = net.order.length - 1; i >= 0; i -= 1) {
389
+ const n = net.order[i];
390
+ if (n.kind !== "leaf") continue;
391
+ let lf = Math.min(net.finish, n.lfLimit, n.lsLimit + n.d);
392
+ for (let a = n.parentId ? net.nodes.get(n.parentId) : null; a; a = a.parentId ? net.nodes.get(a.parentId) : null) {
393
+ lf = Math.min(lf, a.lfLimit);
394
+ if (n.es === a.es) lf = Math.min(lf, a.lsLimit + n.d);
395
+ }
396
+ n.lf = lf;
397
+ n.ls = lf - n.d;
398
+ n.slack = n.lf - n.ef;
399
+ for (const e of n.in) {
400
+ const p = e.from;
401
+ if (e.kind === "SS") p.lsLimit = Math.min(p.lsLimit, n.ls - e.lag);
402
+ else if (e.kind === "FF") p.lfLimit = Math.min(p.lfLimit, n.lf - e.lag);
403
+ else if (e.kind === "SF") p.lsLimit = Math.min(p.lsLimit, n.lf - e.lag);
404
+ else p.lfLimit = Math.min(p.lfLimit, n.ls - e.lag);
405
+ }
406
+ }
407
+ // Summaries: the least slack of their leaves.
408
+ for (const n of net.list) {
409
+ if (n.kind !== "summary") continue;
410
+ let slack = null;
411
+ for (const l of n.leaves) if (l.slack != null) slack = slack == null ? l.slack : Math.min(slack, l.slack);
412
+ n.slack = slack;
413
+ }
414
+ return net;
415
+ }
416
+
417
+ /** The critical path: leaves with no slack, in plan order (start, then finish). */
418
+ export function criticalPath(net) {
419
+ return net.list
420
+ .filter((n) => n.kind === "leaf" && n.slack != null && n.slack <= 0)
421
+ .sort((a, b) => a.es - b.es || a.ef - b.ef || net.list.indexOf(a) - net.list.indexOf(b))
422
+ .map((n) => n.id);
423
+ }
424
+
425
+ /* ------------------------------------------------------------------ */
426
+ /* The plan: everything the screen draws */
427
+ /* ------------------------------------------------------------------ */
428
+
429
+ const iso10 = (v) => (v == null ? null : String(v).slice(0, 10));
430
+
431
+ /**
432
+ * Forward pass, backward pass and critical path over one project's tasks
433
+ * and links, described in dates.
434
+ *
435
+ * → {
436
+ * rows: Map(id → { id, parentId, summary, milestone, pinned, dated, stuck,
437
+ * start, due stored dates (a summary: its leaves' span)
438
+ * planStart, planDue where the forward pass puts it
439
+ * lateStart, lateDue the latest it may run (leaves)
440
+ * duration working days
441
+ * slack, critical, progress, shifted, drivers, leafIds }),
442
+ * links: Map(id → { id, taskId, dependsOnId, kind, lag, valid, reason,
443
+ * slack, critical, violated, violation }),
444
+ * start, finish, the plan's first and last working day
445
+ * critical, critical leaf ids in plan order
446
+ * stuck, violations, link ids whose stored dates break them
447
+ * }
448
+ */
449
+ export function schedule(tasks, links, opts = {}) {
450
+ const net = buildNetwork(tasks, links, opts);
451
+ forwardPass(net);
452
+ backwardPass(net);
453
+ return describe(net);
454
+ }
455
+
456
+ function describe(net) {
457
+ const cal = net.calendar;
458
+ const rows = new Map();
459
+ const criticalIds = criticalPath(net);
460
+ const critical = new Set(criticalIds);
461
+
462
+ for (const n of net.list) {
463
+ const t = n.task;
464
+ const base = {
465
+ id: n.id,
466
+ parentId: n.parentId,
467
+ summary: n.kind === "summary",
468
+ milestone: n.milestone,
469
+ pinned: n.pinned,
470
+ dated: n.kind !== "undated",
471
+ stuck: n.stuck,
472
+ progress: Math.max(0, Math.min(100, Number(t.progress ?? 0) || 0)),
473
+ start: null,
474
+ due: null,
475
+ planStart: null,
476
+ planDue: null,
477
+ lateStart: null,
478
+ lateDue: null,
479
+ duration: null,
480
+ slack: n.slack,
481
+ critical: false,
482
+ shifted: false,
483
+ drivers: [],
484
+ leafIds: n.leaves.map((l) => l.id),
485
+ };
486
+ if (n.kind === "leaf") {
487
+ const a = iso10(t.start_date);
488
+ const b = iso10(t.due_date);
489
+ const stored = datesOf(n.s, n.d, n.milestone, cal);
490
+ const plan = datesOf(n.es, n.d, n.milestone, cal);
491
+ base.start = n.milestone ? b ?? a : a ?? b;
492
+ base.due = n.milestone ? b ?? a : b ?? a;
493
+ base.planStart = plan.start;
494
+ base.planDue = plan.due;
495
+ if (n.ls != null) {
496
+ const late = datesOf(n.ls, n.d, n.milestone, cal);
497
+ base.lateStart = late.start;
498
+ base.lateDue = late.due;
499
+ }
500
+ base.duration = n.d;
501
+ base.critical = critical.has(n.id);
502
+ base.shifted = plan.start !== stored.start || plan.due !== stored.due;
503
+ const drivers = new Set();
504
+ if (!n.pinned) for (const e of n.in) if (required(e) === n.es && n.es > n.s) drivers.add(e.link.id);
505
+ base.drivers = [...drivers];
506
+ }
507
+ rows.set(n.id, base);
508
+ }
509
+
510
+ // Summaries: the span of their leaves, stored and planned; progress
511
+ // weighted by working days.
512
+ for (const n of net.list) {
513
+ if (n.kind !== "summary") continue;
514
+ const row = rows.get(n.id);
515
+ let work = 0;
516
+ let done = 0;
517
+ for (const l of n.leaves) {
518
+ const r = rows.get(l.id);
519
+ if (!row.start || r.start < row.start) row.start = r.start;
520
+ if (!row.due || r.due > row.due) row.due = r.due;
521
+ if (!row.planStart || r.planStart < row.planStart) row.planStart = r.planStart;
522
+ if (!row.planDue || r.planDue > row.planDue) row.planDue = r.planDue;
523
+ if (r.critical) row.critical = true;
524
+ if (r.shifted) row.shifted = true;
525
+ work += l.d;
526
+ done += (l.d * r.progress) / 100;
527
+ }
528
+ row.duration = Math.max(0, cal.lastIndex(dayOf(row.due)) + 1 - cal.startIndex(dayOf(row.start)));
529
+ row.progress = work > 0 ? Math.round((done / work) * 100) : n.leaves.every((l) => l.pinned) ? 100 : 0;
530
+ }
531
+
532
+ // Links: tightness on the plan, and conflicts on the stored dates.
533
+ const outLinks = new Map();
534
+ const violations = [];
535
+ for (const info of net.links.values()) {
536
+ const out = { id: info.id, taskId: info.taskId, dependsOnId: info.dependsOnId, kind: info.kind, lag: info.lag, valid: info.valid, reason: info.reason, slack: null, critical: false, violated: false, violation: 0 };
537
+ if (info.valid && !info.edges.some((e) => e.to.stuck || e.from.stuck)) {
538
+ let slack = Infinity;
539
+ let tightCritical = false;
540
+ let worst = 0;
541
+ for (const e of info.edges) {
542
+ const gap = e.to.es - required(e);
543
+ slack = Math.min(slack, gap);
544
+ if (gap <= 0 && critical.has(e.to.id)) tightCritical = true;
545
+ if (!e.to.pinned) worst = Math.max(worst, requiredStored(e) - e.to.s);
546
+ }
547
+ out.slack = slack;
548
+ const predCritical = info.edges[0].from.kind === "summary" ? info.edges[0].from.leaves.some((l) => critical.has(l.id)) : critical.has(info.dependsOnId);
549
+ out.critical = tightCritical && predCritical;
550
+ if (worst > 0) {
551
+ out.violated = true;
552
+ out.violation = worst;
553
+ violations.push(info.id);
554
+ }
555
+ }
556
+ outLinks.set(info.id, out);
557
+ }
558
+
559
+ let start = null;
560
+ let finish = null;
561
+ for (const r of rows.values()) {
562
+ if (!r.dated || r.summary) continue;
563
+ if (!start || r.planStart < start) start = r.planStart;
564
+ if (!finish || r.planDue > finish) finish = r.planDue;
565
+ }
566
+
567
+ return {
568
+ calendar: cal.kind,
569
+ rows,
570
+ links: outLinks,
571
+ start,
572
+ finish,
573
+ critical: criticalIds,
574
+ stuck: net.stuck.filter((n) => n.kind === "leaf").map((n) => n.id),
575
+ violations,
576
+ };
577
+ }
578
+
579
+ /* ------------------------------------------------------------------ */
580
+ /* Moving work */
581
+ /* ------------------------------------------------------------------ */
582
+
583
+ const sameDates = (task, start, due) => iso10(task?.start_date) === start && iso10(task?.due_date) === due;
584
+
585
+ /**
586
+ * Move tasks and shift everything that depends on them.
587
+ *
588
+ * `moves` is [{ id, start, due }]: a task's new dates (a summary's `start`
589
+ * moves its whole block of unfinished leaves by the same working days).
590
+ * Each moved task keeps its new start as its floor; the forward pass then
591
+ * pushes every task a link binds, as far as it must and no further.
592
+ *
593
+ * → { changes: [{ id, start_date, due_date, from: { start_date, due_date }, summary? }],
594
+ * clamped: [{ id, asked, got, drivers }] a move the links pushed later
595
+ * before, after (plans), finishBefore, finishAfter }
596
+ *
597
+ * Only tasks the move affects are in `changes`: a task whose plan did not
598
+ * change keeps its stored dates, even if they were already off.
599
+ *
600
+ * `opts.nextLinks`: the links after the change, when the change is a link
601
+ * (a new link pushes its successor the same way a move does).
602
+ * `opts.nextTasks`: the tasks after a change of outline (a task put under
603
+ * another: the new parent's links now bind it). Their dates are the same.
604
+ */
605
+ export function shiftDependents(tasks, links, moves, opts = {}) {
606
+ const cal = makeCalendar(opts.calendar ?? "weekdays");
607
+ const before = schedule(tasks, links, { ...opts, calendar: cal });
608
+ const byId = new Map((tasks ?? []).filter(Boolean).map((t) => [String(t.id), t]));
609
+ const patched = new Map();
610
+ const asked = new Map();
611
+
612
+ const put = (id, start, due) => {
613
+ const t = patched.get(id) ?? byId.get(id);
614
+ const milestone = Boolean(t.is_milestone);
615
+ patched.set(id, { ...t, start_date: milestone ? due ?? start : start, due_date: milestone ? due ?? start : due });
616
+ asked.set(id, milestone ? due ?? start : start);
617
+ };
618
+
619
+ for (const m of moves ?? []) {
620
+ const id = String(m?.id ?? "");
621
+ const row = before.rows.get(id);
622
+ if (!row || !byId.has(id)) continue;
623
+ if (row.summary) {
624
+ const target = dayOf(m.start);
625
+ if (target == null || !row.start) continue;
626
+ const delta = cal.startIndex(target) - cal.startIndex(dayOf(row.start));
627
+ if (!delta) continue;
628
+ for (const leafId of row.leafIds) {
629
+ const leaf = before.rows.get(leafId);
630
+ if (!leaf || leaf.pinned) continue;
631
+ const span = spanOf(patched.get(leafId) ?? byId.get(leafId), cal);
632
+ const moved = datesOf(span.s + delta, span.d, span.milestone, cal);
633
+ put(leafId, moved.start, moved.due);
634
+ }
635
+ } else {
636
+ const t = byId.get(id);
637
+ const start = iso10(m.start) ?? iso10(t.start_date) ?? iso10(m.due);
638
+ const due = iso10(m.due) ?? iso10(t.due_date) ?? start;
639
+ put(id, start, due);
640
+ }
641
+ }
642
+
643
+ const base = opts.nextTasks ?? tasks ?? [];
644
+ const movedTasks = base.map((t) => (t && patched.has(String(t.id)) ? { ...t, ...patched.get(String(t.id)), parent_id: t.parent_id } : t));
645
+ const after = schedule(movedTasks, opts.nextLinks ?? links, { ...opts, calendar: cal });
646
+
647
+ const changes = [];
648
+ const changedLeaves = new Set();
649
+ for (const [id, a] of after.rows) {
650
+ if (!a.dated || a.summary) continue;
651
+ const b = before.rows.get(id);
652
+ const touched = patched.has(id) || !b || a.planStart !== b.planStart || a.planDue !== b.planDue;
653
+ if (!touched) continue;
654
+ const t = byId.get(id);
655
+ if (sameDates(t, a.planStart, a.planDue)) continue;
656
+ changes.push({ id, start_date: a.planStart, due_date: a.planDue, from: { start_date: iso10(t.start_date), due_date: iso10(t.due_date) } });
657
+ changedLeaves.add(id);
658
+ }
659
+ for (const [id, a] of after.rows) {
660
+ if (!a.summary || !a.leafIds.some((l) => changedLeaves.has(l))) continue;
661
+ const t = byId.get(id);
662
+ if (sameDates(t, a.planStart, a.planDue)) continue;
663
+ changes.push({ id, start_date: a.planStart, due_date: a.planDue, from: { start_date: iso10(t.start_date), due_date: iso10(t.due_date) }, summary: true });
664
+ }
665
+
666
+ const clamped = [];
667
+ for (const [id, wanted] of asked) {
668
+ const a = after.rows.get(id);
669
+ if (!a) continue;
670
+ const want = datesOf(spanOf(patched.get(id), cal).s, 1, a.milestone, cal).start;
671
+ if (a.planStart > want) clamped.push({ id, asked: wanted, got: a.planStart, drivers: a.drivers });
672
+ }
673
+
674
+ return { changes, clamped, before, after, finishBefore: before.finish, finishAfter: after.finish };
675
+ }
676
+
677
+ /**
678
+ * Re-plan unfinished work off the past, the way the morning check does:
679
+ * * not started (0%) and its start has passed → it starts today
680
+ * * started, and its due date has passed → it keeps its start and needs
681
+ * its remaining share of the work from today: ceil(duration × (100 −
682
+ * progress) / 100) working days, at least 1
683
+ * * a milestone whose day has passed, not done → today
684
+ * Then every dependent shifts. `today` is 'YYYY-MM-DD'; on a weekend
685
+ * "today" is the next Monday. Dates compared are the plan's.
686
+ *
687
+ * → shiftDependents' result, plus `moves` [{ id, start, due, reason }]
688
+ * (reason: not_started | overdue | milestone).
689
+ */
690
+ export function replanFromToday(tasks, links, today, opts = {}) {
691
+ const cal = makeCalendar(opts.calendar ?? "weekdays");
692
+ const t0 = dayOf(today);
693
+ const plan = schedule(tasks, links, { ...opts, calendar: cal });
694
+ const moves = [];
695
+ if (t0 != null) {
696
+ const ti = cal.startIndex(t0);
697
+ const todayIso = isoOf(cal.dayAt(ti));
698
+ for (const r of plan.rows.values()) {
699
+ if (!r.dated || r.summary || r.pinned || r.stuck) continue;
700
+ if (r.milestone) {
701
+ if (dayOf(r.planDue) < t0) moves.push({ id: r.id, start: todayIso, due: todayIso, reason: "milestone" });
702
+ continue;
703
+ }
704
+ if (r.progress === 0 && dayOf(r.planStart) < t0) {
705
+ moves.push({ id: r.id, start: todayIso, due: isoOf(cal.dayAt(ti + r.duration - 1)), reason: "not_started" });
706
+ } else if (r.progress > 0 && dayOf(r.planDue) < t0) {
707
+ const remaining = Math.max(1, Math.ceil((r.duration * (100 - r.progress)) / 100));
708
+ moves.push({ id: r.id, start: r.planStart, due: isoOf(cal.dayAt(ti + remaining - 1)), reason: "overdue" });
709
+ }
710
+ }
711
+ }
712
+ return { ...shiftDependents(tasks, links, moves, { ...opts, calendar: cal }), moves };
713
+ }
714
+
715
+ /**
716
+ * Mend broken links: every unfinished task that starts earlier than its
717
+ * links allow moves to its earliest date, and whatever waits on it follows.
718
+ * → shiftDependents' result, plus `moves`.
719
+ */
720
+ export function alignConflicts(tasks, links, opts = {}) {
721
+ const plan = schedule(tasks, links, opts);
722
+ const moves = [];
723
+ for (const r of plan.rows.values()) if (r.dated && !r.summary && !r.pinned && !r.stuck && r.shifted) moves.push({ id: r.id, start: r.planStart, due: r.planDue });
724
+ return { ...shiftDependents(tasks, links, moves, opts), moves };
725
+ }
726
+
727
+ /**
728
+ * Why `taskId` can't wait on `dependsOnId`, in words, or null when it can.
729
+ * Refuses: itself, its own summary or sub-task, a pair already linked
730
+ * (either way round), and any link that would close a loop.
731
+ */
732
+ export function linkProblem(tasks, links, taskId, dependsOnId, opts = {}) {
733
+ const a = String(taskId ?? "");
734
+ const b = String(dependsOnId ?? "");
735
+ if (!a || !b) return "Pick two tasks.";
736
+ if (a === b) return "A task can't wait on itself.";
737
+ const net = buildNetwork(tasks, links, opts);
738
+ const succ = net.nodes.get(a);
739
+ const pred = net.nodes.get(b);
740
+ if (!succ || !pred) return "Both tasks must be in this project.";
741
+ if (net.isAncestor(succ, pred) || net.isAncestor(pred, succ)) return "A task can't wait on its own summary or sub-task.";
742
+ if ((links ?? []).some((l) => l && ((String(l.task_id) === a && String(l.depends_on_id) === b) || (String(l.task_id) === b && String(l.depends_on_id) === a))))
743
+ return "These two are already linked.";
744
+ if (succ.kind === "undated" || pred.kind === "undated") return "Give both tasks dates first.";
745
+ const trial = buildNetwork(tasks, [...(links ?? []), { id: "__trial", task_id: a, depends_on_id: b }], opts);
746
+ if (trial.stuck.length > net.stuck.length) return "That would make a loop: the other task already waits on this one.";
747
+ return null;
748
+ }
749
+
750
+ /**
751
+ * The long pole: the unfinished critical task with the most work left
752
+ * (working days × share not done); the later wins a tie (it sits nearer
753
+ * the finish, so the finish waits on it most directly). Null when
754
+ * nothing critical is left.
755
+ */
756
+ export function longPole(plan) {
757
+ let best = null;
758
+ let bestLeft = -1;
759
+ for (const id of plan?.critical ?? []) {
760
+ const r = plan.rows.get(id);
761
+ if (!r || r.pinned || r.milestone) continue;
762
+ const left = (r.duration * (100 - r.progress)) / 100;
763
+ if (left >= bestLeft) {
764
+ best = r;
765
+ bestLeft = left;
766
+ }
767
+ }
768
+ return best ? best.id : null;
769
+ }