makersclaw 0.8.0 → 0.8.2

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 (401) hide show
  1. package/README.md +9 -3
  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 +3 -3
  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 +2 -2
  26. package/r/chat-pane.json +84 -0
  27. package/r/chat.json +2 -2
  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 +13 -7
  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 +2 -2
  46. package/r/flow-editor.json +79 -0
  47. package/r/footer.json +2 -2
  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 +3 -3
  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 +3 -3
  60. package/r/pace-bar.json +69 -0
  61. package/r/page-builder.json +2 -2
  62. package/r/payment-methods.json +2 -2
  63. package/r/pdf-document.json +2 -2
  64. package/r/pdf-viewer.json +2 -2
  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 +4086 -1512
  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 +2 -2
  82. package/r/signature-pad.json +2 -2
  83. package/r/sketch-canvas.json +2 -2
  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 +3 -3
  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 +234 -0
  143. package/registry/blocks/agent-pane/agent-pane.jsx +246 -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/approval/README.md +145 -0
  159. package/registry/blocks/approval/approval.jsx +163 -0
  160. package/registry/blocks/approval/block.json +31 -0
  161. package/registry/blocks/approval/demo.jsx +121 -0
  162. package/registry/blocks/approval/index.js +3 -0
  163. package/registry/blocks/ask-bar/README.md +105 -0
  164. package/registry/blocks/ask-bar/ask-bar.jsx +166 -0
  165. package/registry/blocks/ask-bar/block.json +26 -0
  166. package/registry/blocks/ask-bar/demo.jsx +78 -0
  167. package/registry/blocks/ask-bar/index.js +3 -0
  168. package/registry/blocks/automation-editor/README.md +291 -0
  169. package/registry/blocks/automation-editor/automation-editor.jsx +656 -0
  170. package/registry/blocks/automation-editor/block.json +39 -0
  171. package/registry/blocks/automation-editor/demo.jsx +284 -0
  172. package/registry/blocks/automation-editor/index.js +4 -0
  173. package/registry/blocks/automation-editor/run-inputs.jsx +82 -0
  174. package/registry/blocks/automation-list/README.md +129 -0
  175. package/registry/blocks/automation-list/automation-list.jsx +476 -0
  176. package/registry/blocks/automation-list/block.json +35 -0
  177. package/registry/blocks/automation-list/demo.jsx +67 -0
  178. package/registry/blocks/automation-list/index.js +3 -0
  179. package/registry/blocks/automation-model/README.md +109 -0
  180. package/registry/blocks/automation-model/block.json +135 -0
  181. package/registry/blocks/automation-model/catalog.js +256 -0
  182. package/registry/blocks/automation-model/check.js +161 -0
  183. package/registry/blocks/automation-model/demo.jsx +42 -0
  184. package/registry/blocks/automation-model/edit.js +262 -0
  185. package/registry/blocks/automation-model/fixes.js +287 -0
  186. package/registry/blocks/automation-model/glyph.jsx +13 -0
  187. package/registry/blocks/automation-model/graph.js +410 -0
  188. package/registry/blocks/automation-model/index.js +33 -0
  189. package/registry/blocks/automation-model/overlay.js +175 -0
  190. package/registry/blocks/automation-model/parts.jsx +94 -0
  191. package/registry/blocks/automation-model/schedule.js +53 -0
  192. package/registry/blocks/automation-model/templates.js +128 -0
  193. package/registry/blocks/automation-model/values.js +29 -0
  194. package/registry/blocks/automation-model/words.js +439 -0
  195. package/registry/blocks/automations-screen/README.md +207 -0
  196. package/registry/blocks/automations-screen/automations-screen.jsx +452 -0
  197. package/registry/blocks/automations-screen/block.json +47 -0
  198. package/registry/blocks/automations-screen/demo.jsx +364 -0
  199. package/registry/blocks/automations-screen/index.js +4 -0
  200. package/registry/blocks/automations-screen/screen-model.js +129 -0
  201. package/registry/blocks/chat-pane/README.md +143 -0
  202. package/registry/blocks/chat-pane/block.json +48 -0
  203. package/registry/blocks/chat-pane/chat-attach.js +166 -0
  204. package/registry/blocks/chat-pane/chat-composer.jsx +195 -0
  205. package/registry/blocks/chat-pane/chat-context.js +17 -0
  206. package/registry/blocks/chat-pane/chat-model.js +43 -0
  207. package/registry/blocks/chat-pane/chat-pane.jsx +398 -0
  208. package/registry/blocks/chat-pane/chats-list.jsx +52 -0
  209. package/registry/blocks/chat-pane/demo.jsx +103 -0
  210. package/registry/blocks/chat-pane/index.js +9 -0
  211. package/registry/blocks/connect-card/README.md +99 -0
  212. package/registry/blocks/connect-card/block.json +10 -0
  213. package/registry/blocks/connect-card/connect-card.jsx +103 -0
  214. package/registry/blocks/connect-card/demo.jsx +49 -0
  215. package/registry/blocks/connect-card/index.js +3 -0
  216. package/registry/blocks/cost/README.md +115 -0
  217. package/registry/blocks/cost/block.json +10 -0
  218. package/registry/blocks/cost/cost.jsx +176 -0
  219. package/registry/blocks/cost/demo.jsx +66 -0
  220. package/registry/blocks/cost/index.js +3 -0
  221. package/registry/blocks/data-picker/README.md +81 -0
  222. package/registry/blocks/data-picker/block.json +38 -0
  223. package/registry/blocks/data-picker/data-picker.jsx +210 -0
  224. package/registry/blocks/data-picker/demo.jsx +34 -0
  225. package/registry/blocks/data-picker/fields.js +89 -0
  226. package/registry/blocks/data-picker/index.js +4 -0
  227. package/registry/blocks/data-table/demo.jsx +1 -1
  228. package/registry/blocks/first-run/README.md +118 -0
  229. package/registry/blocks/first-run/block.json +11 -0
  230. package/registry/blocks/first-run/demo.jsx +88 -0
  231. package/registry/blocks/first-run/first-run-model.js +51 -0
  232. package/registry/blocks/first-run/first-run.jsx +156 -0
  233. package/registry/blocks/first-run/index.js +5 -0
  234. package/registry/blocks/first-run/probe-rules.js +26 -0
  235. package/registry/blocks/fix-note/README.md +79 -0
  236. package/registry/blocks/fix-note/block.json +35 -0
  237. package/registry/blocks/fix-note/demo.jsx +30 -0
  238. package/registry/blocks/fix-note/fix-note.jsx +235 -0
  239. package/registry/blocks/fix-note/index.js +3 -0
  240. package/registry/blocks/flow-editor/README.md +108 -0
  241. package/registry/blocks/flow-editor/block.json +44 -0
  242. package/registry/blocks/flow-editor/demo.jsx +115 -0
  243. package/registry/blocks/flow-editor/flow-canvas.jsx +568 -0
  244. package/registry/blocks/flow-editor/flow-editor.jsx +154 -0
  245. package/registry/blocks/flow-editor/index.js +6 -0
  246. package/registry/blocks/flow-editor/minimap.jsx +78 -0
  247. package/registry/blocks/flow-editor/outline.jsx +134 -0
  248. package/registry/blocks/library-shelf/README.md +117 -0
  249. package/registry/blocks/library-shelf/block.json +28 -0
  250. package/registry/blocks/library-shelf/demo.jsx +68 -0
  251. package/registry/blocks/library-shelf/index.js +3 -0
  252. package/registry/blocks/library-shelf/library-shelf.jsx +211 -0
  253. package/registry/blocks/library-shelf/probe-rules.js +15 -0
  254. package/registry/blocks/margin-notes/README.md +222 -0
  255. package/registry/blocks/margin-notes/block.json +10 -0
  256. package/registry/blocks/margin-notes/demo.jsx +144 -0
  257. package/registry/blocks/margin-notes/index.js +20 -0
  258. package/registry/blocks/margin-notes/margin-model.js +224 -0
  259. package/registry/blocks/margin-notes/margin-notes.jsx +465 -0
  260. package/registry/blocks/needs-you/README.md +177 -0
  261. package/registry/blocks/needs-you/block.json +34 -0
  262. package/registry/blocks/needs-you/demo.jsx +143 -0
  263. package/registry/blocks/needs-you/index.js +3 -0
  264. package/registry/blocks/needs-you/needs-you.jsx +441 -0
  265. package/registry/blocks/pace-bar/README.md +230 -0
  266. package/registry/blocks/pace-bar/block.json +10 -0
  267. package/registry/blocks/pace-bar/demo.jsx +255 -0
  268. package/registry/blocks/pace-bar/index.js +22 -0
  269. package/registry/blocks/pace-bar/pace-bar.jsx +461 -0
  270. package/registry/blocks/pace-bar/pace-model.js +288 -0
  271. package/registry/blocks/record-grid/README.md +100 -0
  272. package/registry/blocks/record-grid/block.json +28 -0
  273. package/registry/blocks/record-grid/demo.jsx +43 -0
  274. package/registry/blocks/record-grid/index.js +3 -0
  275. package/registry/blocks/record-grid/record-grid.jsx +251 -0
  276. package/registry/blocks/run-history/README.md +138 -0
  277. package/registry/blocks/run-history/block.json +10 -0
  278. package/registry/blocks/run-history/demo.jsx +74 -0
  279. package/registry/blocks/run-history/index.js +3 -0
  280. package/registry/blocks/run-history/run-history.jsx +218 -0
  281. package/registry/blocks/run-view/README.md +174 -0
  282. package/registry/blocks/run-view/block.json +50 -0
  283. package/registry/blocks/run-view/demo.jsx +286 -0
  284. package/registry/blocks/run-view/index.js +5 -0
  285. package/registry/blocks/run-view/run-controls.jsx +266 -0
  286. package/registry/blocks/run-view/run-view.jsx +236 -0
  287. package/registry/blocks/run-view/runs-list.jsx +99 -0
  288. package/registry/blocks/schedule-card/README.md +121 -0
  289. package/registry/blocks/schedule-card/block.json +10 -0
  290. package/registry/blocks/schedule-card/demo.jsx +82 -0
  291. package/registry/blocks/schedule-card/index.js +3 -0
  292. package/registry/blocks/schedule-card/schedule-card.jsx +136 -0
  293. package/registry/blocks/schedule-engine/README.md +137 -0
  294. package/registry/blocks/schedule-engine/block.json +10 -0
  295. package/registry/blocks/schedule-engine/demo.jsx +82 -0
  296. package/registry/blocks/schedule-engine/engine.js +769 -0
  297. package/registry/blocks/schedule-engine/index.js +9 -0
  298. package/registry/blocks/schedule-engine/ops.js +71 -0
  299. package/registry/blocks/schedule-engine/place.js +294 -0
  300. package/registry/blocks/schedule-engine/tree.js +79 -0
  301. package/registry/blocks/schedule-engine/waits.js +80 -0
  302. package/registry/blocks/schedule-gantt/README.md +159 -0
  303. package/registry/blocks/schedule-gantt/block.json +10 -0
  304. package/registry/blocks/schedule-gantt/demo.jsx +223 -0
  305. package/registry/blocks/schedule-gantt/gantt-model.js +219 -0
  306. package/registry/blocks/schedule-gantt/geometry.js +176 -0
  307. package/registry/blocks/schedule-gantt/index.js +26 -0
  308. package/registry/blocks/schedule-gantt/schedule-gantt.jsx +882 -0
  309. package/registry/blocks/schedule-gantt/sheet.jsx +372 -0
  310. package/registry/blocks/schedule-gantt/track.jsx +190 -0
  311. package/registry/blocks/schedule-ruler/README.md +148 -0
  312. package/registry/blocks/schedule-ruler/block.json +10 -0
  313. package/registry/blocks/schedule-ruler/demo.jsx +181 -0
  314. package/registry/blocks/schedule-ruler/index.js +21 -0
  315. package/registry/blocks/schedule-ruler/ruler-model.js +239 -0
  316. package/registry/blocks/schedule-ruler/schedule-ruler.jsx +548 -0
  317. package/registry/blocks/source-receipt/README.md +97 -0
  318. package/registry/blocks/source-receipt/block.json +30 -0
  319. package/registry/blocks/source-receipt/demo.jsx +71 -0
  320. package/registry/blocks/source-receipt/index.js +3 -0
  321. package/registry/blocks/source-receipt/source-receipt.jsx +107 -0
  322. package/registry/blocks/spreadsheet/spreadsheet.jsx +15 -1
  323. package/registry/blocks/step-picker/README.md +78 -0
  324. package/registry/blocks/step-picker/block.json +31 -0
  325. package/registry/blocks/step-picker/demo.jsx +23 -0
  326. package/registry/blocks/step-picker/index.js +3 -0
  327. package/registry/blocks/step-picker/step-picker.jsx +144 -0
  328. package/registry/blocks/step-settings/README.md +100 -0
  329. package/registry/blocks/step-settings/block.json +44 -0
  330. package/registry/blocks/step-settings/demo.jsx +93 -0
  331. package/registry/blocks/step-settings/fields.jsx +58 -0
  332. package/registry/blocks/step-settings/flow-settings.jsx +115 -0
  333. package/registry/blocks/step-settings/index.js +5 -0
  334. package/registry/blocks/step-settings/step-settings.jsx +290 -0
  335. package/registry/blocks/suggestion/README.md +171 -0
  336. package/registry/blocks/suggestion/block.json +40 -0
  337. package/registry/blocks/suggestion/demo.jsx +91 -0
  338. package/registry/blocks/suggestion/index.js +3 -0
  339. package/registry/blocks/suggestion/suggestion.jsx +436 -0
  340. package/registry/blocks/sum-bar/README.md +122 -0
  341. package/registry/blocks/sum-bar/block.json +10 -0
  342. package/registry/blocks/sum-bar/demo.jsx +110 -0
  343. package/registry/blocks/sum-bar/index.js +5 -0
  344. package/registry/blocks/sum-bar/sum-bar.jsx +209 -0
  345. package/registry/blocks/sum-bar/sum-model.js +126 -0
  346. package/registry/blocks/tier-map/README.md +217 -0
  347. package/registry/blocks/tier-map/block.json +10 -0
  348. package/registry/blocks/tier-map/demo.jsx +222 -0
  349. package/registry/blocks/tier-map/index.js +24 -0
  350. package/registry/blocks/tier-map/tier-map.jsx +574 -0
  351. package/registry/blocks/tier-map/tier-model.js +318 -0
  352. package/registry/blocks/time-grid/README.md +305 -0
  353. package/registry/blocks/time-grid/block.json +10 -0
  354. package/registry/blocks/time-grid/demo.jsx +279 -0
  355. package/registry/blocks/time-grid/grid-model.js +322 -0
  356. package/registry/blocks/time-grid/index.js +25 -0
  357. package/registry/blocks/time-grid/time-grid.jsx +1325 -0
  358. package/registry/blocks/time-strip/README.md +141 -0
  359. package/registry/blocks/time-strip/block.json +10 -0
  360. package/registry/blocks/time-strip/demo.jsx +129 -0
  361. package/registry/blocks/time-strip/index.js +19 -0
  362. package/registry/blocks/time-strip/strip-model.js +178 -0
  363. package/registry/blocks/time-strip/time-strip.jsx +260 -0
  364. package/registry/blocks/top-bar/README.md +149 -0
  365. package/registry/blocks/top-bar/block.json +35 -0
  366. package/registry/blocks/top-bar/demo.jsx +78 -0
  367. package/registry/blocks/top-bar/index.js +5 -0
  368. package/registry/blocks/top-bar/probe-rules.js +74 -0
  369. package/registry/blocks/top-bar/top-bar-model.js +152 -0
  370. package/registry/blocks/top-bar/top-bar.jsx +244 -0
  371. package/registry/blocks/trigger-picker/README.md +98 -0
  372. package/registry/blocks/trigger-picker/block.json +38 -0
  373. package/registry/blocks/trigger-picker/demo.jsx +59 -0
  374. package/registry/blocks/trigger-picker/index.js +3 -0
  375. package/registry/blocks/trigger-picker/trigger-picker.jsx +374 -0
  376. package/registry/blocks/workflow-view/README.md +123 -0
  377. package/registry/blocks/workflow-view/block.json +10 -0
  378. package/registry/blocks/workflow-view/demo.jsx +90 -0
  379. package/registry/blocks/workflow-view/index.js +3 -0
  380. package/registry/blocks/workflow-view/workflow-view.jsx +203 -0
  381. package/src/install.mjs +77 -16
  382. package/src/registry.mjs +3 -0
  383. package/templates/next/browser/lib/data.js +6 -1
  384. package/templates/next/files/README.md +627 -24
  385. package/templates/next/files/check/automation-format.ts +2103 -0
  386. package/templates/next/files/check/automation-package.ts +142 -0
  387. package/templates/next/files/check/run.mjs +968 -9
  388. package/templates/next/files/components/ui/field.jsx +17 -4
  389. package/templates/next/files/components/ui/icon.jsx +13 -0
  390. package/templates/next/files/components/ui/index.js +2 -1
  391. package/templates/next/files/components/ui/layout.jsx +77 -2
  392. package/templates/next/files/components/ui/notice.jsx +70 -0
  393. package/templates/next/files/components/ui/page.jsx +124 -0
  394. package/templates/next/files/components/ui/stat.jsx +22 -7
  395. package/templates/next/files/components/ui/toast.jsx +6 -3
  396. package/templates/next/files/lib/automations.js +660 -0
  397. package/templates/next/files/lib/claw.js +241 -5
  398. package/templates/next/files/lib/door.js +92 -0
  399. package/templates/next/files/lib/hooks.js +1677 -8
  400. package/templates/next/files/lib/platform.js +529 -21
  401. package/templates/next/template.json +13 -3
@@ -0,0 +1,166 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { Button, Icon, IconButton, Input, Spinner, cx } from "../../ui";
5
+
6
+ // AskBar — one line to ask the app's agent, with chips that fit the
7
+ // moment (the app kit, D15). It is the door into the conversation, not
8
+ // the conversation: after a send it says where the ask stands in one
9
+ // line ("Working…", "Waiting for you", "Answered") with an
10
+ // Open that shows the chat wherever the screen keeps it (a sheet, the
11
+ // inspector, the agent-chat block). Fed by the object useAgentThread()
12
+ // returns (lib/hooks.js), or by your own `onAsk(text)`.
13
+ //
14
+ // const chat = useAgentThread({ row: { kind: "deal", id, name } });
15
+ // <AskBar chat={chat} suggestions={["Who should I call today?"]} onOpen={() => setOpen(true)} />
16
+ //
17
+ // Never an Approve: an ask that needs the member's yes or answer says so
18
+ // here ("Waiting for you") and Open shows it where the screen keeps its
19
+ // chat, where the question is answered and the yes is given (the
20
+ // `agent-chat` and `approval` blocks, D18/D19). A need the app cannot
21
+ // settle (connecting an account, spending money) opens the desk.
22
+
23
+ /** The thread's state as one line. */
24
+ const STATE = {
25
+ queued: { words: "Working…", busy: true },
26
+ working: { words: "Working…", busy: true },
27
+ needs_you: { words: "Waiting for you", icon: "warning", tone: "text-warning" },
28
+ done: { words: "Answered", icon: "check", tone: "text-success" },
29
+ stopped: { words: "Stopped", icon: "stop", tone: "text-muted-foreground" },
30
+ failed: { words: "It didn't finish", icon: "warning", tone: "text-destructive" },
31
+ };
32
+
33
+ function lastAsk(chat) {
34
+ if (chat?.pending) return chat.pending;
35
+ const turns = chat?.turns ?? [];
36
+ for (let i = turns.length - 1; i >= 0; i -= 1) if (turns[i]?.member) return turns[i].member;
37
+ return null;
38
+ }
39
+
40
+ /**
41
+ * chat what useAgentThread() returns; asks go to chat.send(text)
42
+ * onAsk(text) instead of `chat`: your own sender (it may be async)
43
+ * state with onAsk: the ask's state as useAgentThread names it
44
+ * (queued · working · needs_you · done · stopped · failed)
45
+ * suggestions asks that fit the moment, as chips; a chip sends its words
46
+ * placeholder the box's words (default "Ask the agent…")
47
+ * label the box's name for a reader (default "Ask the agent")
48
+ * onOpen() shows the conversation; without it there is no Open
49
+ * openOnSend true: a send also calls onOpen
50
+ * className layout around it. Never colour.
51
+ */
52
+ export function AskBar({
53
+ chat,
54
+ onAsk,
55
+ state: stateProp,
56
+ suggestions = [],
57
+ placeholder = "Ask the agent…",
58
+ label = "Ask the agent",
59
+ onOpen,
60
+ openOnSend = false,
61
+ className,
62
+ }) {
63
+ const [text, setText] = useState("");
64
+ const [asked, setAsked] = useState(null);
65
+ const [sending, setSending] = useState(false);
66
+ const [failed, setFailed] = useState(null);
67
+
68
+ const state = chat ? (chat.pending && !chat.state ? "queued" : chat.state) : stateProp;
69
+ const line = state ? STATE[state] : null;
70
+ const ask = chat ? lastAsk(chat) : asked;
71
+ const needsYou = state === "needs_you";
72
+ const unavailable = Boolean(chat?.unavailable);
73
+ const busy = sending || Boolean(chat?.sending);
74
+ const error = chat?.error || failed;
75
+ // Waiting on the member: Open shows the chat when the app settles it
76
+ // there (a question to answer, a yes to press); else it goes to the desk.
77
+ const inApp = chat?.needs?.in_app === "answer" || chat?.needs?.in_app === "press";
78
+ const toDesk = needsYou && !(inApp && onOpen) && Boolean(chat?.openOnDesk);
79
+ const openNow = toDesk ? () => chat.openOnDesk() : onOpen;
80
+
81
+ async function send(words) {
82
+ const t = String(words ?? text).trim();
83
+ if (!t || busy || needsYou || unavailable) return;
84
+ setFailed(null);
85
+ setText("");
86
+ setAsked(t);
87
+ if (chat?.send) chat.send(t);
88
+ else if (onAsk) {
89
+ setSending(true);
90
+ try {
91
+ const r = await onAsk(t);
92
+ if (r && r.ok === false) setFailed(r.error || "That ask didn't go through. Try again.");
93
+ } catch (e) {
94
+ setFailed(e?.message || "That ask didn't go through. Try again.");
95
+ }
96
+ setSending(false);
97
+ }
98
+ if (openOnSend && onOpen) onOpen();
99
+ }
100
+
101
+ return (
102
+ <div className={cx("@container grid grid-cols-1 min-w-0 gap-2 text-sm", className)}>
103
+ <form
104
+ className="flex min-w-0 items-center gap-2"
105
+ onSubmit={(e) => {
106
+ e.preventDefault();
107
+ send();
108
+ }}
109
+ >
110
+ <span className="relative min-w-0 flex-1">
111
+ <Icon name="sparkles" size={16} className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" />
112
+ <Input
113
+ aria-label={label}
114
+ value={text}
115
+ disabled={needsYou || unavailable}
116
+ placeholder={needsYou ? (inApp ? "Answer in the chat first" : "Answer on the desk first") : placeholder}
117
+ onChange={(e) => setText(e.target.value)}
118
+ className="pl-9"
119
+ />
120
+ </span>
121
+ <IconButton icon="arrow-up" label="Send" variant="primary" type="submit" loading={busy} disabled={!text.trim() || busy || needsYou || unavailable} />
122
+ </form>
123
+
124
+ {suggestions.length && !needsYou && !unavailable ? (
125
+ <div className="flex min-w-0 flex-wrap gap-1.5" role="group" aria-label="Suggested asks">
126
+ {suggestions.map((s) => (
127
+ <button
128
+ key={s}
129
+ type="button"
130
+ disabled={busy}
131
+ onClick={() => send(s)}
132
+ className="flex max-w-full min-w-0 items-start gap-1.5 rounded-[var(--radius)] border border-border px-2.5 py-1 text-left text-xs hover:bg-muted focus-visible:outline-2 focus-visible:outline-ring disabled:opacity-50"
133
+ >
134
+ <Icon name="sparkles" size={12} className="mt-0.5 shrink-0 text-muted-foreground" />
135
+ <span className="min-w-0 break-words">{s}</span>
136
+ </button>
137
+ ))}
138
+ </div>
139
+ ) : null}
140
+
141
+ {line ? (
142
+ // The state and Open on one line, the ask it is about under it,
143
+ // cut to one line: at 320px nothing wraps into a third.
144
+ <div role="status" className="grid grid-cols-1 min-w-0 gap-0.5 text-xs text-muted-foreground">
145
+ <span className="flex min-w-0 flex-wrap items-center gap-x-2 gap-y-0.5">
146
+ {line.busy ? <Spinner size="sm" label="Working" /> : <Icon name={line.icon} size={14} className={cx("shrink-0", line.tone)} />}
147
+ <span className={cx("font-medium", needsYou ? "text-foreground" : null)}>{line.words}</span>
148
+ {openNow ? (
149
+ <Button variant="link" size="inherit" onClick={openNow} className="shrink-0">
150
+ {toDesk ? "Open the desk" : "Open"}
151
+ </Button>
152
+ ) : null}
153
+ </span>
154
+ {ask ? <span className="min-w-0 truncate pl-[22px]">“{ask}”</span> : null}
155
+ </div>
156
+ ) : null}
157
+
158
+ {error ? (
159
+ <p role="alert" className="text-xs text-destructive">
160
+ {error}
161
+ </p>
162
+ ) : null}
163
+ {unavailable ? <p className="text-xs text-muted-foreground">Asking from here is not connected yet.</p> : null}
164
+ </div>
165
+ );
166
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "ask-bar",
3
+ "title": "AskBar",
4
+ "description": "One line to ask the app's agent, with chips that fit the moment (they wrap, never run off). After a send it says where the ask stands in one line — \"Working…\", \"Waiting for you on the desk\", \"Answered\" — with an Open that shows the conversation wherever the screen keeps it. It draws what useAgentThread() returns (lib/hooks.js) and sends through it, or calls your own onAsk(text). Never an Approve: a yes is given on the desk.",
5
+ "useWhen": "A home screen, a list's head or a record's timeline should take an ask in one line and keep the page in view: CRM's Today, the Ask box under a record. Pair it with agent-chat (in a Sheet or the inspector) for the conversation it opens. For the full chat in place, use agent-chat alone.",
6
+ "tags": [
7
+ "ask",
8
+ "agent",
9
+ "ai",
10
+ "prompt",
11
+ "command",
12
+ "chips",
13
+ "suggestions",
14
+ "assistant",
15
+ "copilot",
16
+ "search bar"
17
+ ],
18
+ "exports": [
19
+ "AskBar"
20
+ ],
21
+ "files": [
22
+ "index.js",
23
+ "ask-bar.jsx"
24
+ ],
25
+ "requires": []
26
+ }
@@ -0,0 +1,78 @@
1
+ "use client";
2
+
3
+ // AskBar driven by a pretend thread, and frozen in each state — the
4
+ // gallery's and the smoke test's surface. Not installed into apps. A send
5
+ // works for a moment, then answers; an ask with "send" in it stops for a
6
+ // yes on the desk; one with "fail" does not finish.
7
+ import { useEffect, useRef, useState } from "react";
8
+ import { Card } from "../components/ui";
9
+ import { AskBar } from "../components/blocks/ask-bar";
10
+
11
+ function usePretendThread() {
12
+ const [turns, setTurns] = useState([]);
13
+ const [state, setState] = useState(null);
14
+ const timers = useRef([]);
15
+ useEffect(() => () => timers.current.forEach(clearTimeout), []);
16
+ const send = (text) => {
17
+ setTurns((ts) => [...ts, { id: `t${ts.length}`, member: text, parts: [], status: "working" }]);
18
+ setState("working");
19
+ timers.current.push(
20
+ setTimeout(() => setState(/send/i.test(text) ? "needs_you" : /fail/i.test(text) ? "failed" : "done"), 1600),
21
+ );
22
+ };
23
+ return { state, turns, pending: null, sending: false, error: null, unavailable: false, send, openOnDesk: () => true };
24
+ }
25
+
26
+ const frozen = (state, member) => ({ state, turns: [{ id: "t0", member }], send: () => undefined, openOnDesk: () => true });
27
+
28
+ export default function AskBarDemo() {
29
+ const chat = usePretendThread();
30
+ const [opened, setOpened] = useState(0);
31
+ return (
32
+ <div className="grid gap-6 p-6">
33
+ <Card title="Ask" description={opened ? `Open pressed ${opened} time${opened === 1 ? "" : "s"}` : "Try a chip, or type “send them a note”."}>
34
+ <AskBar
35
+ chat={chat}
36
+ suggestions={[
37
+ "Who should I call today?",
38
+ "Find restaurant groups in New England with ten or more sites and a new head of operations",
39
+ "Send them a note",
40
+ ]}
41
+ onOpen={() => setOpened((n) => n + 1)}
42
+ />
43
+ </Card>
44
+
45
+ <div className="grid gap-4 [grid-template-columns:repeat(auto-fit,minmax(min(100%,20rem),1fr))]">
46
+ <Card title="Empty">
47
+ <AskBar onAsk={() => ({ ok: true })} />
48
+ </Card>
49
+ <Card title="Working">
50
+ <AskBar chat={frozen("working", "Who is waiting on me?")} onOpen={() => undefined} />
51
+ </Card>
52
+ <Card title="Waiting for you">
53
+ <AskBar chat={frozen("needs_you", "Send the follow-ups to the twelve investors")} onOpen={() => undefined} />
54
+ </Card>
55
+ <Card title="Answered">
56
+ <AskBar chat={frozen("done", "What closes this month?")} onOpen={() => undefined} suggestions={["And next month?"]} />
57
+ </Card>
58
+ <Card title="Failed">
59
+ <AskBar
60
+ chat={{ ...frozen("failed", "Read every deal note since March"), error: "The agent could not start: credits ran out." }}
61
+ onOpen={() => undefined}
62
+ />
63
+ </Card>
64
+ <Card title="Not connected yet">
65
+ <AskBar chat={{ state: null, turns: [], unavailable: true, send: () => undefined }} />
66
+ </Card>
67
+ </div>
68
+
69
+ <div className="w-80 max-w-full rounded-lg border border-border p-3">
70
+ <AskBar
71
+ chat={frozen("working", "A very long ask that has to truncate in a narrow column without pushing the Open off the edge")}
72
+ suggestions={["A chip with a long ask that has to wrap inside 320 pixels of width"]}
73
+ onOpen={() => undefined}
74
+ />
75
+ </div>
76
+ </div>
77
+ );
78
+ }
@@ -0,0 +1,3 @@
1
+ // The AskBar block — `makersclaw add ask-bar`. Fixed: compose it through
2
+ // its props (README.md beside this file).
3
+ export { AskBar } from "./ask-bar";
@@ -0,0 +1,291 @@
1
+ # AutomationEditor
2
+
3
+ The whole editor for one automation, for an app's Automations page. It
4
+ answers "how do I open, edit, test and switch on my app's automations?"
5
+ inside the app, in the app's theme.
6
+
7
+ - **The header** — Back, the name (editable), a status (New · Draft · Draft
8
+ ahead · Live · Off · Unsaved changes), the Flow / Runs tabs you hand it,
9
+ a **test chip** (see below), the run picker, the **Live** switch, **Test
10
+ run** with its price on it ("about 3 cr"), **Keep this version** and
11
+ **Save**.
12
+ - **A strip** that says what is wrong with it, in words and by step, and
13
+ what the platform refused (with its lines).
14
+ - **The canvas** (`flow-editor`) with undo and redo (Ctrl or ⌘ + Z), Delete
15
+ on the selected step, and a **list view** of the same flow.
16
+ - **The panel** for whatever is pressed: the trigger (`trigger-picker`), a
17
+ step (`step-settings`) or the automation as a whole (`FlowSettings`).
18
+ Beside the canvas on a wide width; over it, from the bottom, on a narrow
19
+ one.
20
+ - **Test run** asks the questions a manual run needs first (`RunInputs`).
21
+
22
+ How saving works (nothing here is hidden from a member):
23
+
24
+ 1. **Save** makes a draft version; the live one keeps running as it was.
25
+ 2. **Test run** is a dry run of the draft (or the live one): it reads your
26
+ real data and changes nothing (nothing is sent, nothing is written). Its
27
+ run opens as a test, with what each step would have done.
28
+ 3. **Keep this version** makes the draft the one that runs.
29
+ 4. **Live** is the switch: it fires on its trigger, or it does not.
30
+
31
+ Keep and Live need a passed test of that version. Without one the platform
32
+ refuses (`needs_test`) and the editor shows its words with two buttons:
33
+ **Run a test** (the same as Test run) and **Go live without a test** (**Keep
34
+ without a test** for Keep). The second asks once more, inline: "Skip the
35
+ test? It will run for real the first time it starts." with **Skip the test**
36
+ and **Cancel**; only then does it call `onKeep({ skipTest: true })` or
37
+ `onLive(on, { skipTest: true })` again. The skip is the member's own
38
+ deliberate second press (some automations cannot be tested meaningfully);
39
+ an agent never has it, and it is never silent.
40
+
41
+ The **test chip** sits beside the status for the version being looked at
42
+ (the draft if there is one, else the live one): "Not tested" (amber),
43
+ "Tested · 3 steps would run" (green), "Last test failed" (red). Nothing
44
+ without `tests`, or for the app's own shipped version (`exempt`).
45
+
46
+ A draft never goes live by itself. An automation shipped with the app is
47
+ edited the same way: a member's edit is a new version, the shipped one
48
+ stays on file, and **Reset to the app's version** is one press (`onReset`).
49
+
50
+ ```jsx
51
+ import { AutomationEditor } from "../components/blocks/automation-editor"; // a Makersclaw app
52
+ import { AutomationEditor } from "@/components/makersclaw/blocks/automation-editor"; // a shadcn project
53
+ ```
54
+
55
+ `makersclaw add automation-editor` lays `flow-editor`, `step-settings`,
56
+ `trigger-picker`, `fix-note` and `automation-model` with it. A client component. It
57
+ fills its parent's height (give it a parent with one) and its own width
58
+ decides its layout: the panel sits beside the canvas from about 768 px.
59
+
60
+ ## Props
61
+
62
+ - `automationId` — the saved id, or `null` for one not saved yet (the
63
+ button says Create).
64
+ - `status` — `draft` · `on` · `off` · `needs_owner` · `"new"`.
65
+ `liveVersion`, `draftVersion` — the numbers, or null.
66
+ - `baseline` — the definition as last saved (or the starting point). Key the
67
+ builder by id, never by version: when the saved version moves (a kept
68
+ suggestion, an agent's draft, another tab) an editor with nothing edited
69
+ opens the new one by itself, and one with edits keeps them and says so
70
+ (the strip below). Keying by version would throw the edits away.
71
+ - `versions`, `runs` (for the picker), `apps`, `actions` (the app's declared
72
+ actions, for the action step), `ownedByApp`.
73
+ - `estimate` — what a run is expected to cost (`useAutomation().estimate`):
74
+ paid steps wear a price, Test run says what it will spend.
75
+ - `samples` — `{ [stepId]: a real output }` for the data picker.
76
+ - `runnable` — a `Set` of the kinds the platform runs.
77
+ - `backLabel`, `onBack`, `tabs`, `canEdit` (false: read-only).
78
+ - `onSave(def, { baseVersion })`, `onKeep(opts?)`, `onLive(on, opts?)`, `onTest(inputs)` — each
79
+ resolves the platform's answer (`{ ok: true }` or `{ ok: false, error,
80
+ code?, problems }`, which is what `useAutomation`'s `save`, `keep`,
81
+ `setLive` and `test` resolve) or throws an Error. The builder shows the
82
+ words and the lines, and keeps the `code`. `opts` is `{ skipTest: true }`
83
+ only after the member's confirmed skip (above); otherwise they are called
84
+ as before, with none. Pass `keep` and `setLive` straight through. `onSave`'s
85
+ second argument is `{ baseVersion }`: the version the working copy began
86
+ on. **Send it to the platform as `expect_version`, never the newest
87
+ version you can see** (`useAutomation().save(def, { baseVersion })` does).
88
+ A refusal with the code `changed` (and `latest`, when it says) draws the
89
+ conflict strip (next section). A save that works may resolve `{ ok: true,
90
+ version }` (or the hook's `{ ok: true, data: { version } }`): the editor
91
+ then begins from that version.
92
+ - `onFocus({ selected, unsaved, baseVersion })` — called whenever any of the
93
+ three changes: the pressed step (a step id, `"trigger"`, `"flow"` or
94
+ `null`), whether the working copy has edits that are not saved, and the
95
+ version it began on. For a pane beside the editor (the agent's) that
96
+ must know what the member is looking at. It never re-renders the editor:
97
+ keep the last value in a ref.
98
+ - `compareTo` — `{ version, definition }`: what "What changed" compares an
99
+ agent's draft with (the version the member kept, usually the live one).
100
+ Without it the agent's line has no What changed.
101
+ - `tests` — `{ live, draft }`, each a `TestView` `{ version, passed,
102
+ exempt, last: { run_id, status, ended_at, credits, would } | null }`
103
+ (`useAutomation().tests`): draws the test chip. Optional.
104
+ - `suggestions` — the changes the agent drafted from failed runs, waiting
105
+ for a person: `[{ id, why, stale, patch }]` (`useAutomation().suggestions`).
106
+ `onKeepSuggestion(id)` keeps one (a new version that runs; refused with
107
+ `stale` when the automation moved on) and `onDropSuggestion(id)` forgets
108
+ it. They sit in a strip above the canvas; Keep waits while the working
109
+ copy is unsaved. Without the callbacks, or when `canEdit` is false, the
110
+ strip is not drawn. When the saved version moves under an editor with no
111
+ edits of its own, it opens the new one.
112
+ - `triggers` — what can start an automation here (`useAutomations().triggers`):
113
+ the trigger panel offers events, an app's actions, records, a web address
114
+ and another automation, as the platform runs them. Without it: by hand and
115
+ a schedule. `selfKey` is this automation's key (it cannot follow itself,
116
+ defaults to the definition's), `hook` its webhook address once it is saved
117
+ (`useAutomation().hook`) and `onRotateHook` makes or replaces the webhook's
118
+ secret (`useAutomation().rotateHook`; the secret is shown once).
119
+ - `onDelete(automation)` — async: **Delete automation**, in a menu at the end
120
+ of the header (never a button beside Save), for a saved automation the
121
+ member may edit. It asks first, in words that say what it does: `Deletes
122
+ "<name>". Its runs stay in history.` (and, for one that is on, that
123
+ deleting also switches it off). `automation` is `{ id, name, status }`.
124
+ Resolves `{ ok: true }` (the screen leaves the editor and says what
125
+ followed: an Undo, the automations that followed this one) or `{ ok:
126
+ false, error, code? }`, whose words are shown as they are (it is running,
127
+ only the owner or an admin may delete it…). `canDelete` hides the item
128
+ (default true). `source` is the automation's source: an app's own job
129
+ (`legacy`) and what an app shipped (`package`) get the item disabled, with
130
+ a line pointing at the app's settings.
131
+ - `onOpenRun(runId)`, `onReset()`.
132
+
133
+ ## Shared with the agent
134
+
135
+ The draft is shared: an agent (or another tab) may save a newer version while
136
+ a member has edits that are not saved. The editor never lets a save bury it.
137
+
138
+ - **The base.** The editor remembers the version its working copy began on
139
+ and sends that with every save. Nothing edited: it follows the saved
140
+ version, so a draft the agent saves simply appears on the canvas.
141
+ - **Edits not saved, a newer version arrives:** a strip says "The agent drew
142
+ v5 from your last save. You have changes that are not saved." (or "Version 5
143
+ was saved after you opened this…" when a person saved it). **Look at v5**
144
+ replaces the working copy with it as one undoable step (Undo brings the
145
+ edits and the version they were made on back). **Keep mine** dismisses the
146
+ strip.
147
+ - **A save the platform refuses as `changed`** (after Keep mine, or from
148
+ another tab) draws the same strip with **Save on top of v5**, the member's
149
+ deliberate press that names v5 as the base, and Look at v5.
150
+ - **While the draft is the agent's** (its version is `kind: "agent"` and not
151
+ yet kept), a line under the header says "Drawn by the agent · from: <its
152
+ note> · What changed". What changed lists, in plain words, the steps added,
153
+ removed and changed against `compareTo` (`changeLinesBetween` in
154
+ automation-model).
155
+
156
+ ## Hooks (lib/hooks.js)
157
+
158
+ `useAutomation(id)` → `{ automation, live, draft, definition, versions,
159
+ estimate, tests, runs, suggestions, keepSuggestion(id), dropSuggestion(id),
160
+ busy, save(def), keep({ skipTest }?), setLive(on, { skipTest }?),
161
+ test({ inputs, sample, dry }?), loading, error, code, unavailable, reload }`.
162
+ `test` is a dry run unless you pass `dry: false`. `definition` is the draft if
163
+ there is one, else the live one: what the builder opens. A save names the
164
+ version it was made against, so two people editing at once get "changed"
165
+ rather than overwriting each other.
166
+
167
+ ## Who may do what
168
+
169
+ Any member who opens the app edits and runs its automations (roles come
170
+ later, decided in one place on the platform). A step's input and output are
171
+ shown only to the automation's owner, the admins and whoever started the
172
+ run; the others see status and the report. The platform refuses what a
173
+ member may not do; a hidden button is never the guard.
174
+
175
+ ## Never
176
+
177
+ - Never a draft that goes live by itself, or a switch that is on without a
178
+ version kept.
179
+ - Never an approval step: an automation has none. When a yes is truly
180
+ needed it goes to the automation's owner, on the desk.
181
+ - Never a step the platform cannot run without saying so: it says "soon"
182
+ and the save waits.
183
+
184
+ ## Example
185
+
186
+ The whole Automations page of an app: the list, an automation, a run.
187
+
188
+ ```jsx
189
+ "use client";
190
+ import { useRouter, useSearchParams } from "next/navigation";
191
+ import { useAutomation, useAutomationRun, useAutomations } from "../lib/hooks";
192
+ import { AutomationList } from "../components/blocks/automation-list";
193
+ import { AutomationEditor } from "../components/blocks/automation-editor";
194
+ import { RunView, RunsList } from "../components/blocks/run-view";
195
+ import { STARTING_POINTS, blankAutomation, keyFromName } from "../components/blocks/automation-model";
196
+
197
+ function Tabs({ tab, onTab }) {
198
+ return (
199
+ <div role="tablist" className="flex gap-1">
200
+ {[["flow", "Flow"], ["runs", "Runs"]].map(([id, label]) => (
201
+ <button key={id} type="button" role="tab" aria-selected={tab === id} onClick={() => onTab(id)} className={tab === id ? "rounded-md bg-accent px-3 py-1 text-sm font-medium" : "rounded-md px-3 py-1 text-sm text-muted-foreground"}>
202
+ {label}
203
+ </button>
204
+ ))}
205
+ </div>
206
+ );
207
+ }
208
+
209
+ function One({ id, runId, tab, go, triggers }) {
210
+ const a = useAutomation(id);
211
+ const run = useAutomationRun(runId);
212
+ if (a.unavailable) return <p className="p-4 text-sm">Automations are not set up here yet.</p>;
213
+ if (!a.automation || !a.definition) return <p className="p-4 text-sm text-muted-foreground">Opening…</p>;
214
+ const tabs = <Tabs tab={tab} onTab={(t) => go({ automation: id, tab: t })} />;
215
+ if (runId && run.detail) {
216
+ return <RunView def={run.definition ?? a.definition} detail={run.detail} runs={a.runs} tz={a.automation.tz} tabs={tabs} name={a.automation.name} onBack={() => go({ automation: id })} onPickRun={(r) => go({ automation: id, run: r })} onRunAgain={async () => { const r = await a.test({ dry: run.run?.dry === true }); if (r.ok) go({ automation: id, run: r.data.run_id }); }} onControl={run.control} controlBusy={run.busy} onKeepFix={run.keepFix} onDropFix={run.dropFix} />;
217
+ }
218
+ if (tab === "runs") return <RunsList runs={a.runs} tz={a.automation.tz} onOpen={(r) => go({ automation: id, run: r })} />;
219
+ return (
220
+ <AutomationEditor
221
+ key={id}
222
+ automationId={id}
223
+ status={a.automation.status}
224
+ liveVersion={a.automation.live_version}
225
+ draftVersion={a.automation.draft_version}
226
+ baseline={a.definition}
227
+ versions={a.versions}
228
+ runs={a.runs}
229
+ estimate={a.estimate}
230
+ ownedByApp
231
+ tabs={tabs}
232
+ onBack={() => go({})}
233
+ onSave={a.save}
234
+ onKeep={a.keep}
235
+ onLive={a.setLive}
236
+ tests={a.tests}
237
+ onTest={async (inputs) => { const r = await a.test({ inputs }); if (r.ok) go({ automation: id, run: r.data.run_id }); return r; }}
238
+ onOpenRun={(r) => go({ automation: id, run: r })}
239
+ suggestions={a.suggestions}
240
+ onKeepSuggestion={a.keepSuggestion}
241
+ onDropSuggestion={a.dropSuggestion}
242
+ triggers={triggers}
243
+ selfKey={a.automation.key}
244
+ hook={a.hook}
245
+ onRotateHook={a.rotateHook}
246
+ />
247
+ );
248
+ }
249
+
250
+ // A new one is not saved until Create: the platform wants a first step.
251
+ function NewOne({ start, automations, create, go, triggers }) {
252
+ const taken = new Set(automations.map((a) => a.key));
253
+ const picked = STARTING_POINTS.find((s) => s.id === start);
254
+ return (
255
+ <AutomationEditor
256
+ key="new"
257
+ automationId={null}
258
+ status="new"
259
+ baseline={picked ? picked.build(taken) : blankAutomation("", keyFromName("New automation", taken))}
260
+ ownedByApp
261
+ triggers={triggers}
262
+ onBack={() => go({})}
263
+ onSave={async (def) => {
264
+ const r = await create({ ...def, key: keyFromName(def.name, taken) });
265
+ if (r.ok) go({ automation: r.data.id });
266
+ return r;
267
+ }}
268
+ />
269
+ );
270
+ }
271
+
272
+ export default function Automations() {
273
+ const router = useRouter();
274
+ const q = useSearchParams();
275
+ const go = (p) => router.push(`?${new URLSearchParams(p)}`);
276
+ const { automations, triggers, setStatus, create, unavailable } = useAutomations();
277
+ const id = q.get("automation");
278
+ if (id === "new") return <div className="flex h-[640px] flex-col"><NewOne start={q.get("start")} automations={automations} create={create} go={go} triggers={triggers} /></div>;
279
+ if (id) return <div className="flex h-[640px] flex-col"><One id={id} runId={q.get("run")} tab={q.get("tab") ?? "flow"} go={go} triggers={triggers} /></div>;
280
+ if (unavailable) return <p className="p-4 text-sm">Automations are not set up here yet.</p>;
281
+ return (
282
+ <AutomationList
283
+ rows={automations}
284
+ grouped={false}
285
+ onOpen={(i) => go({ automation: i })}
286
+ onToggle={async (row, on) => { const r = await setStatus(row.id, on); if (!r.ok) throw new Error(r.error); }}
287
+ onNew={(start) => go({ automation: "new", ...(start ? { start: start.id } : {}) })}
288
+ />
289
+ );
290
+ }
291
+ ```