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.
- package/README.md +9 -3
- package/package.json +1 -1
- package/r/activity-log.json +52 -0
- package/r/agent-changes.json +51 -0
- package/r/agent-chat.json +53 -0
- package/r/agent-column.json +85 -0
- package/r/agent-dock.json +57 -0
- package/r/agent-kit.json +49 -0
- package/r/agent-marks.json +49 -0
- package/r/agent-pane.json +93 -0
- package/r/ai-action.json +56 -0
- package/r/annotate.json +3 -3
- package/r/approval-request.json +2 -2
- package/r/approval.json +54 -0
- package/r/ask-bar.json +50 -0
- package/r/audio-player.json +2 -2
- package/r/auth-form.json +2 -2
- package/r/automation-editor.json +66 -0
- package/r/automation-list.json +57 -0
- package/r/automation-model.json +118 -0
- package/r/automations-screen.json +71 -0
- package/r/board.json +2 -2
- package/r/calendar-view.json +3 -3
- package/r/cart.json +2 -2
- package/r/chart.json +2 -2
- package/r/chat-pane.json +84 -0
- package/r/chat.json +2 -2
- package/r/code-editor.json +2 -2
- package/r/comment-box.json +3 -3
- package/r/connect-card.json +51 -0
- package/r/contact-section.json +2 -2
- package/r/core.json +13 -7
- package/r/cost.json +51 -0
- package/r/csv-import.json +3 -3
- package/r/data-picker.json +60 -0
- package/r/data-table.json +2 -2
- package/r/diagram.json +2 -2
- package/r/doc-editor.json +2 -2
- package/r/doc-page.json +3 -3
- package/r/email-render.json +2 -2
- package/r/faq.json +2 -2
- package/r/file-upload.json +2 -2
- package/r/first-run.json +63 -0
- package/r/fix-note.json +57 -0
- package/r/flow-canvas.json +2 -2
- package/r/flow-editor.json +79 -0
- package/r/footer.json +2 -2
- package/r/gantt.json +3 -3
- package/r/job-panel.json +2 -2
- package/r/json-editor.json +3 -3
- package/r/library-shelf.json +56 -0
- package/r/map.json +2 -2
- package/r/margin-notes.json +67 -0
- package/r/menubar.json +2 -2
- package/r/mind-map.json +3 -3
- package/r/navigation-menu.json +2 -2
- package/r/needs-you.json +55 -0
- package/r/order-detail.json +2 -2
- package/r/org-chart.json +3 -3
- package/r/pace-bar.json +69 -0
- package/r/page-builder.json +2 -2
- package/r/payment-methods.json +2 -2
- package/r/pdf-document.json +2 -2
- package/r/pdf-viewer.json +2 -2
- package/r/product-detail.json +2 -2
- package/r/product-grid.json +3 -3
- package/r/record-form.json +2 -2
- package/r/record-grid.json +52 -0
- package/r/record-notes.json +4 -4
- package/r/registry.json +4086 -1512
- package/r/run-history.json +52 -0
- package/r/run-view.json +80 -0
- package/r/schedule-card.json +54 -0
- package/r/schedule-engine.json +79 -0
- package/r/schedule-gantt.json +83 -0
- package/r/schedule-ruler.json +64 -0
- package/r/search-dialog.json +2 -2
- package/r/settings-list.json +2 -2
- package/r/setup-checklist.json +2 -2
- package/r/share-dialog.json +2 -2
- package/r/sidebar.json +2 -2
- package/r/signature-pad.json +2 -2
- package/r/sketch-canvas.json +2 -2
- package/r/source-receipt.json +52 -0
- package/r/spreadsheet.json +3 -3
- package/r/stat-cards.json +3 -3
- package/r/step-picker.json +53 -0
- package/r/step-settings.json +72 -0
- package/r/suggestion.json +58 -0
- package/r/sum-bar.json +64 -0
- package/r/task-list.json +3 -3
- package/r/task-model.json +2 -2
- package/r/testimonials.json +2 -2
- package/r/tier-map.json +67 -0
- package/r/time-grid.json +73 -0
- package/r/time-strip.json +64 -0
- package/r/top-bar.json +66 -0
- package/r/transaction-list.json +2 -2
- package/r/trigger-picker.json +61 -0
- package/r/whiteboard.json +3 -3
- package/r/workflow-view.json +55 -0
- package/registry/blocks/activity-log/README.md +112 -0
- package/registry/blocks/activity-log/activity-log.jsx +351 -0
- package/registry/blocks/activity-log/block.json +33 -0
- package/registry/blocks/activity-log/demo.jsx +48 -0
- package/registry/blocks/activity-log/index.js +4 -0
- package/registry/blocks/agent-changes/README.md +94 -0
- package/registry/blocks/agent-changes/agent-changes.jsx +118 -0
- package/registry/blocks/agent-changes/block.json +28 -0
- package/registry/blocks/agent-changes/demo.jsx +39 -0
- package/registry/blocks/agent-changes/index.js +3 -0
- package/registry/blocks/agent-chat/README.md +145 -0
- package/registry/blocks/agent-chat/agent-chat.jsx +415 -0
- package/registry/blocks/agent-chat/block.json +30 -0
- package/registry/blocks/agent-chat/demo.jsx +165 -0
- package/registry/blocks/agent-chat/index.js +3 -0
- package/registry/blocks/agent-column/README.md +197 -0
- package/registry/blocks/agent-column/agent-column.jsx +492 -0
- package/registry/blocks/agent-column/block.json +67 -0
- package/registry/blocks/agent-column/column-picker.jsx +163 -0
- package/registry/blocks/agent-column/demo.jsx +50 -0
- package/registry/blocks/agent-column/index.js +7 -0
- package/registry/blocks/agent-column/model.js +164 -0
- package/registry/blocks/agent-column/receipt.jsx +483 -0
- package/registry/blocks/agent-column/spreadsheet-type.jsx +24 -0
- package/registry/blocks/agent-dock/README.md +210 -0
- package/registry/blocks/agent-dock/agent-dock.jsx +356 -0
- package/registry/blocks/agent-dock/block.json +47 -0
- package/registry/blocks/agent-dock/demo.jsx +153 -0
- package/registry/blocks/agent-dock/dock-model.js +118 -0
- package/registry/blocks/agent-dock/index.js +5 -0
- package/registry/blocks/agent-kit/README.md +73 -0
- package/registry/blocks/agent-kit/agent-kit.jsx +86 -0
- package/registry/blocks/agent-kit/block.json +10 -0
- package/registry/blocks/agent-kit/demo.jsx +35 -0
- package/registry/blocks/agent-kit/index.js +3 -0
- package/registry/blocks/agent-marks/README.md +80 -0
- package/registry/blocks/agent-marks/agent-marks.jsx +133 -0
- package/registry/blocks/agent-marks/block.json +27 -0
- package/registry/blocks/agent-marks/demo.jsx +49 -0
- package/registry/blocks/agent-marks/index.js +3 -0
- package/registry/blocks/agent-pane/README.md +234 -0
- package/registry/blocks/agent-pane/agent-pane.jsx +246 -0
- package/registry/blocks/agent-pane/block.json +41 -0
- package/registry/blocks/agent-pane/composer.jsx +6 -0
- package/registry/blocks/agent-pane/demo.jsx +233 -0
- package/registry/blocks/agent-pane/dock-context.js +10 -0
- package/registry/blocks/agent-pane/index.js +4 -0
- package/registry/blocks/agent-pane/prose.jsx +84 -0
- package/registry/blocks/agent-pane/questions.jsx +134 -0
- package/registry/blocks/agent-pane/transcript.jsx +268 -0
- package/registry/blocks/agent-pane/work.jsx +368 -0
- package/registry/blocks/ai-action/README.md +94 -0
- package/registry/blocks/ai-action/ai-action.jsx +188 -0
- package/registry/blocks/ai-action/block.json +34 -0
- package/registry/blocks/ai-action/demo.jsx +93 -0
- package/registry/blocks/ai-action/index.js +3 -0
- package/registry/blocks/approval/README.md +145 -0
- package/registry/blocks/approval/approval.jsx +163 -0
- package/registry/blocks/approval/block.json +31 -0
- package/registry/blocks/approval/demo.jsx +121 -0
- package/registry/blocks/approval/index.js +3 -0
- package/registry/blocks/ask-bar/README.md +105 -0
- package/registry/blocks/ask-bar/ask-bar.jsx +166 -0
- package/registry/blocks/ask-bar/block.json +26 -0
- package/registry/blocks/ask-bar/demo.jsx +78 -0
- package/registry/blocks/ask-bar/index.js +3 -0
- package/registry/blocks/automation-editor/README.md +291 -0
- package/registry/blocks/automation-editor/automation-editor.jsx +656 -0
- package/registry/blocks/automation-editor/block.json +39 -0
- package/registry/blocks/automation-editor/demo.jsx +284 -0
- package/registry/blocks/automation-editor/index.js +4 -0
- package/registry/blocks/automation-editor/run-inputs.jsx +82 -0
- package/registry/blocks/automation-list/README.md +129 -0
- package/registry/blocks/automation-list/automation-list.jsx +476 -0
- package/registry/blocks/automation-list/block.json +35 -0
- package/registry/blocks/automation-list/demo.jsx +67 -0
- package/registry/blocks/automation-list/index.js +3 -0
- package/registry/blocks/automation-model/README.md +109 -0
- package/registry/blocks/automation-model/block.json +135 -0
- package/registry/blocks/automation-model/catalog.js +256 -0
- package/registry/blocks/automation-model/check.js +161 -0
- package/registry/blocks/automation-model/demo.jsx +42 -0
- package/registry/blocks/automation-model/edit.js +262 -0
- package/registry/blocks/automation-model/fixes.js +287 -0
- package/registry/blocks/automation-model/glyph.jsx +13 -0
- package/registry/blocks/automation-model/graph.js +410 -0
- package/registry/blocks/automation-model/index.js +33 -0
- package/registry/blocks/automation-model/overlay.js +175 -0
- package/registry/blocks/automation-model/parts.jsx +94 -0
- package/registry/blocks/automation-model/schedule.js +53 -0
- package/registry/blocks/automation-model/templates.js +128 -0
- package/registry/blocks/automation-model/values.js +29 -0
- package/registry/blocks/automation-model/words.js +439 -0
- package/registry/blocks/automations-screen/README.md +207 -0
- package/registry/blocks/automations-screen/automations-screen.jsx +452 -0
- package/registry/blocks/automations-screen/block.json +47 -0
- package/registry/blocks/automations-screen/demo.jsx +364 -0
- package/registry/blocks/automations-screen/index.js +4 -0
- package/registry/blocks/automations-screen/screen-model.js +129 -0
- package/registry/blocks/chat-pane/README.md +143 -0
- package/registry/blocks/chat-pane/block.json +48 -0
- package/registry/blocks/chat-pane/chat-attach.js +166 -0
- package/registry/blocks/chat-pane/chat-composer.jsx +195 -0
- package/registry/blocks/chat-pane/chat-context.js +17 -0
- package/registry/blocks/chat-pane/chat-model.js +43 -0
- package/registry/blocks/chat-pane/chat-pane.jsx +398 -0
- package/registry/blocks/chat-pane/chats-list.jsx +52 -0
- package/registry/blocks/chat-pane/demo.jsx +103 -0
- package/registry/blocks/chat-pane/index.js +9 -0
- package/registry/blocks/connect-card/README.md +99 -0
- package/registry/blocks/connect-card/block.json +10 -0
- package/registry/blocks/connect-card/connect-card.jsx +103 -0
- package/registry/blocks/connect-card/demo.jsx +49 -0
- package/registry/blocks/connect-card/index.js +3 -0
- package/registry/blocks/cost/README.md +115 -0
- package/registry/blocks/cost/block.json +10 -0
- package/registry/blocks/cost/cost.jsx +176 -0
- package/registry/blocks/cost/demo.jsx +66 -0
- package/registry/blocks/cost/index.js +3 -0
- package/registry/blocks/data-picker/README.md +81 -0
- package/registry/blocks/data-picker/block.json +38 -0
- package/registry/blocks/data-picker/data-picker.jsx +210 -0
- package/registry/blocks/data-picker/demo.jsx +34 -0
- package/registry/blocks/data-picker/fields.js +89 -0
- package/registry/blocks/data-picker/index.js +4 -0
- package/registry/blocks/data-table/demo.jsx +1 -1
- package/registry/blocks/first-run/README.md +118 -0
- package/registry/blocks/first-run/block.json +11 -0
- package/registry/blocks/first-run/demo.jsx +88 -0
- package/registry/blocks/first-run/first-run-model.js +51 -0
- package/registry/blocks/first-run/first-run.jsx +156 -0
- package/registry/blocks/first-run/index.js +5 -0
- package/registry/blocks/first-run/probe-rules.js +26 -0
- package/registry/blocks/fix-note/README.md +79 -0
- package/registry/blocks/fix-note/block.json +35 -0
- package/registry/blocks/fix-note/demo.jsx +30 -0
- package/registry/blocks/fix-note/fix-note.jsx +235 -0
- package/registry/blocks/fix-note/index.js +3 -0
- package/registry/blocks/flow-editor/README.md +108 -0
- package/registry/blocks/flow-editor/block.json +44 -0
- package/registry/blocks/flow-editor/demo.jsx +115 -0
- package/registry/blocks/flow-editor/flow-canvas.jsx +568 -0
- package/registry/blocks/flow-editor/flow-editor.jsx +154 -0
- package/registry/blocks/flow-editor/index.js +6 -0
- package/registry/blocks/flow-editor/minimap.jsx +78 -0
- package/registry/blocks/flow-editor/outline.jsx +134 -0
- package/registry/blocks/library-shelf/README.md +117 -0
- package/registry/blocks/library-shelf/block.json +28 -0
- package/registry/blocks/library-shelf/demo.jsx +68 -0
- package/registry/blocks/library-shelf/index.js +3 -0
- package/registry/blocks/library-shelf/library-shelf.jsx +211 -0
- package/registry/blocks/library-shelf/probe-rules.js +15 -0
- package/registry/blocks/margin-notes/README.md +222 -0
- package/registry/blocks/margin-notes/block.json +10 -0
- package/registry/blocks/margin-notes/demo.jsx +144 -0
- package/registry/blocks/margin-notes/index.js +20 -0
- package/registry/blocks/margin-notes/margin-model.js +224 -0
- package/registry/blocks/margin-notes/margin-notes.jsx +465 -0
- package/registry/blocks/needs-you/README.md +177 -0
- package/registry/blocks/needs-you/block.json +34 -0
- package/registry/blocks/needs-you/demo.jsx +143 -0
- package/registry/blocks/needs-you/index.js +3 -0
- package/registry/blocks/needs-you/needs-you.jsx +441 -0
- package/registry/blocks/pace-bar/README.md +230 -0
- package/registry/blocks/pace-bar/block.json +10 -0
- package/registry/blocks/pace-bar/demo.jsx +255 -0
- package/registry/blocks/pace-bar/index.js +22 -0
- package/registry/blocks/pace-bar/pace-bar.jsx +461 -0
- package/registry/blocks/pace-bar/pace-model.js +288 -0
- package/registry/blocks/record-grid/README.md +100 -0
- package/registry/blocks/record-grid/block.json +28 -0
- package/registry/blocks/record-grid/demo.jsx +43 -0
- package/registry/blocks/record-grid/index.js +3 -0
- package/registry/blocks/record-grid/record-grid.jsx +251 -0
- package/registry/blocks/run-history/README.md +138 -0
- package/registry/blocks/run-history/block.json +10 -0
- package/registry/blocks/run-history/demo.jsx +74 -0
- package/registry/blocks/run-history/index.js +3 -0
- package/registry/blocks/run-history/run-history.jsx +218 -0
- package/registry/blocks/run-view/README.md +174 -0
- package/registry/blocks/run-view/block.json +50 -0
- package/registry/blocks/run-view/demo.jsx +286 -0
- package/registry/blocks/run-view/index.js +5 -0
- package/registry/blocks/run-view/run-controls.jsx +266 -0
- package/registry/blocks/run-view/run-view.jsx +236 -0
- package/registry/blocks/run-view/runs-list.jsx +99 -0
- package/registry/blocks/schedule-card/README.md +121 -0
- package/registry/blocks/schedule-card/block.json +10 -0
- package/registry/blocks/schedule-card/demo.jsx +82 -0
- package/registry/blocks/schedule-card/index.js +3 -0
- package/registry/blocks/schedule-card/schedule-card.jsx +136 -0
- package/registry/blocks/schedule-engine/README.md +137 -0
- package/registry/blocks/schedule-engine/block.json +10 -0
- package/registry/blocks/schedule-engine/demo.jsx +82 -0
- package/registry/blocks/schedule-engine/engine.js +769 -0
- package/registry/blocks/schedule-engine/index.js +9 -0
- package/registry/blocks/schedule-engine/ops.js +71 -0
- package/registry/blocks/schedule-engine/place.js +294 -0
- package/registry/blocks/schedule-engine/tree.js +79 -0
- package/registry/blocks/schedule-engine/waits.js +80 -0
- package/registry/blocks/schedule-gantt/README.md +159 -0
- package/registry/blocks/schedule-gantt/block.json +10 -0
- package/registry/blocks/schedule-gantt/demo.jsx +223 -0
- package/registry/blocks/schedule-gantt/gantt-model.js +219 -0
- package/registry/blocks/schedule-gantt/geometry.js +176 -0
- package/registry/blocks/schedule-gantt/index.js +26 -0
- package/registry/blocks/schedule-gantt/schedule-gantt.jsx +882 -0
- package/registry/blocks/schedule-gantt/sheet.jsx +372 -0
- package/registry/blocks/schedule-gantt/track.jsx +190 -0
- package/registry/blocks/schedule-ruler/README.md +148 -0
- package/registry/blocks/schedule-ruler/block.json +10 -0
- package/registry/blocks/schedule-ruler/demo.jsx +181 -0
- package/registry/blocks/schedule-ruler/index.js +21 -0
- package/registry/blocks/schedule-ruler/ruler-model.js +239 -0
- package/registry/blocks/schedule-ruler/schedule-ruler.jsx +548 -0
- package/registry/blocks/source-receipt/README.md +97 -0
- package/registry/blocks/source-receipt/block.json +30 -0
- package/registry/blocks/source-receipt/demo.jsx +71 -0
- package/registry/blocks/source-receipt/index.js +3 -0
- package/registry/blocks/source-receipt/source-receipt.jsx +107 -0
- package/registry/blocks/spreadsheet/spreadsheet.jsx +15 -1
- package/registry/blocks/step-picker/README.md +78 -0
- package/registry/blocks/step-picker/block.json +31 -0
- package/registry/blocks/step-picker/demo.jsx +23 -0
- package/registry/blocks/step-picker/index.js +3 -0
- package/registry/blocks/step-picker/step-picker.jsx +144 -0
- package/registry/blocks/step-settings/README.md +100 -0
- package/registry/blocks/step-settings/block.json +44 -0
- package/registry/blocks/step-settings/demo.jsx +93 -0
- package/registry/blocks/step-settings/fields.jsx +58 -0
- package/registry/blocks/step-settings/flow-settings.jsx +115 -0
- package/registry/blocks/step-settings/index.js +5 -0
- package/registry/blocks/step-settings/step-settings.jsx +290 -0
- package/registry/blocks/suggestion/README.md +171 -0
- package/registry/blocks/suggestion/block.json +40 -0
- package/registry/blocks/suggestion/demo.jsx +91 -0
- package/registry/blocks/suggestion/index.js +3 -0
- package/registry/blocks/suggestion/suggestion.jsx +436 -0
- package/registry/blocks/sum-bar/README.md +122 -0
- package/registry/blocks/sum-bar/block.json +10 -0
- package/registry/blocks/sum-bar/demo.jsx +110 -0
- package/registry/blocks/sum-bar/index.js +5 -0
- package/registry/blocks/sum-bar/sum-bar.jsx +209 -0
- package/registry/blocks/sum-bar/sum-model.js +126 -0
- package/registry/blocks/tier-map/README.md +217 -0
- package/registry/blocks/tier-map/block.json +10 -0
- package/registry/blocks/tier-map/demo.jsx +222 -0
- package/registry/blocks/tier-map/index.js +24 -0
- package/registry/blocks/tier-map/tier-map.jsx +574 -0
- package/registry/blocks/tier-map/tier-model.js +318 -0
- package/registry/blocks/time-grid/README.md +305 -0
- package/registry/blocks/time-grid/block.json +10 -0
- package/registry/blocks/time-grid/demo.jsx +279 -0
- package/registry/blocks/time-grid/grid-model.js +322 -0
- package/registry/blocks/time-grid/index.js +25 -0
- package/registry/blocks/time-grid/time-grid.jsx +1325 -0
- package/registry/blocks/time-strip/README.md +141 -0
- package/registry/blocks/time-strip/block.json +10 -0
- package/registry/blocks/time-strip/demo.jsx +129 -0
- package/registry/blocks/time-strip/index.js +19 -0
- package/registry/blocks/time-strip/strip-model.js +178 -0
- package/registry/blocks/time-strip/time-strip.jsx +260 -0
- package/registry/blocks/top-bar/README.md +149 -0
- package/registry/blocks/top-bar/block.json +35 -0
- package/registry/blocks/top-bar/demo.jsx +78 -0
- package/registry/blocks/top-bar/index.js +5 -0
- package/registry/blocks/top-bar/probe-rules.js +74 -0
- package/registry/blocks/top-bar/top-bar-model.js +152 -0
- package/registry/blocks/top-bar/top-bar.jsx +244 -0
- package/registry/blocks/trigger-picker/README.md +98 -0
- package/registry/blocks/trigger-picker/block.json +38 -0
- package/registry/blocks/trigger-picker/demo.jsx +59 -0
- package/registry/blocks/trigger-picker/index.js +3 -0
- package/registry/blocks/trigger-picker/trigger-picker.jsx +374 -0
- package/registry/blocks/workflow-view/README.md +123 -0
- package/registry/blocks/workflow-view/block.json +10 -0
- package/registry/blocks/workflow-view/demo.jsx +90 -0
- package/registry/blocks/workflow-view/index.js +3 -0
- package/registry/blocks/workflow-view/workflow-view.jsx +203 -0
- package/src/install.mjs +77 -16
- package/src/registry.mjs +3 -0
- package/templates/next/browser/lib/data.js +6 -1
- package/templates/next/files/README.md +627 -24
- package/templates/next/files/check/automation-format.ts +2103 -0
- package/templates/next/files/check/automation-package.ts +142 -0
- package/templates/next/files/check/run.mjs +968 -9
- package/templates/next/files/components/ui/field.jsx +17 -4
- package/templates/next/files/components/ui/icon.jsx +13 -0
- package/templates/next/files/components/ui/index.js +2 -1
- package/templates/next/files/components/ui/layout.jsx +77 -2
- package/templates/next/files/components/ui/notice.jsx +70 -0
- package/templates/next/files/components/ui/page.jsx +124 -0
- package/templates/next/files/components/ui/stat.jsx +22 -7
- package/templates/next/files/components/ui/toast.jsx +6 -3
- package/templates/next/files/lib/automations.js +660 -0
- package/templates/next/files/lib/claw.js +241 -5
- package/templates/next/files/lib/door.js +92 -0
- package/templates/next/files/lib/hooks.js +1677 -8
- package/templates/next/files/lib/platform.js +529 -21
- package/templates/next/template.json +13 -3
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "automation-editor",
|
|
3
|
+
"title": "AutomationEditor",
|
|
4
|
+
"description": "The whole editor for one automation: a header with its name, a status (New, Draft, Draft ahead, Live, Off, Unsaved changes), a test chip (Not tested, Tested with the steps that would run, Last test failed), the run picker, the Live switch, Test run (a dry run: it reads real data and changes nothing) with its price on it, Keep this version and Save; a strip that says what is wrong with it, in words and by step; the node canvas (flow-editor) with an undo history; and the panel for whatever is pressed (the trigger, a step, or the automation as a whole), over the canvas on a narrow width and beside it on a wide one. It holds the working copy of the definition and knows nothing of the network: Save, Keep, Live and Test are callbacks that resolve the platform's answer. Keep and Live need a passed test; when the platform refuses for want of one, the editor offers a test or, after one more question, a deliberate skip. A draft shared with an agent is never buried: the editor remembers the version its copy began on, saves against it, and when a newer version arrives while there are edits not saved it says so (Look at it, Keep mine, or save on top of it on purpose); a line under the header says when the agent drew the draft. Delete automation is in a menu at the end of the header and asks first.",
|
|
5
|
+
"useWhen": "A member opens, edits, tests and switches on an automation inside an app. Pair it with automation-list (the rows) and run-view (a run), and the useAutomations / useAutomation / useAutomationRun hooks (lib/hooks.js).",
|
|
6
|
+
"tags": [
|
|
7
|
+
"automation",
|
|
8
|
+
"workflow",
|
|
9
|
+
"builder",
|
|
10
|
+
"editor",
|
|
11
|
+
"canvas",
|
|
12
|
+
"save",
|
|
13
|
+
"test run",
|
|
14
|
+
"keep",
|
|
15
|
+
"live",
|
|
16
|
+
"draft",
|
|
17
|
+
"undo",
|
|
18
|
+
"n8n",
|
|
19
|
+
"make",
|
|
20
|
+
"zapier",
|
|
21
|
+
"page"
|
|
22
|
+
],
|
|
23
|
+
"exports": [
|
|
24
|
+
"AutomationEditor",
|
|
25
|
+
"RunInputs"
|
|
26
|
+
],
|
|
27
|
+
"files": [
|
|
28
|
+
"index.js",
|
|
29
|
+
"automation-editor.jsx",
|
|
30
|
+
"run-inputs.jsx"
|
|
31
|
+
],
|
|
32
|
+
"requires": [
|
|
33
|
+
"automation-model",
|
|
34
|
+
"fix-note",
|
|
35
|
+
"flow-editor",
|
|
36
|
+
"step-settings",
|
|
37
|
+
"trigger-picker"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// An Automations page with a pretend platform behind it — the gallery's and
|
|
4
|
+
// the smoke test's surface. Not installed into apps. The list, the builder
|
|
5
|
+
// (edit a step, add one with a +, Save, Keep, switch it Live, Test run) and
|
|
6
|
+
// a run on the same canvas, all in memory. Test run is a dry run (dashed
|
|
7
|
+
// pencil cards); Keep and Live refuse with "needs_test" until one passed, and
|
|
8
|
+
// the note offers Run a test or, after one more question, Go live without one.
|
|
9
|
+
// "The agent saves a draft" pretends the agent wrote a newer version while the
|
|
10
|
+
// editor is open: with nothing edited the canvas follows it ("Drawn by the
|
|
11
|
+
// agent", with What changed); with edits not saved, a strip offers Look at it or
|
|
12
|
+
// Keep mine, and Save, which names the version the copy began on, is refused
|
|
13
|
+
// ("changed") until the member chooses to save on top. Delete automation is in
|
|
14
|
+
// the header's menu and each row's (it asks first; the app's own shipped job
|
|
15
|
+
// says to use the app's settings instead). A width picker shows the page at
|
|
16
|
+
// 1280, 800, 368 and 320 px of the block's own width.
|
|
17
|
+
import { useMemo, useState } from "react";
|
|
18
|
+
import { Button, ButtonGroup } from "../components/ui";
|
|
19
|
+
import { AutomationEditor } from "../components/blocks/automation-editor";
|
|
20
|
+
import { AutomationList } from "../components/blocks/automation-list";
|
|
21
|
+
import { RunView, RunsList } from "../components/blocks/run-view";
|
|
22
|
+
import { STARTING_POINTS, blankAutomation, keyFromName } from "../components/blocks/automation-model";
|
|
23
|
+
|
|
24
|
+
const MORNING = {
|
|
25
|
+
format: 1,
|
|
26
|
+
key: "morning-read",
|
|
27
|
+
name: "Morning read",
|
|
28
|
+
does: "Reads your mail, saves who wrote, drafts the follow-ups that are owed.",
|
|
29
|
+
trigger: { type: "schedule", cron: "0 7 * * 1-5", tz: "team" },
|
|
30
|
+
inputs: {},
|
|
31
|
+
settings: { overlap: "skip", on_failure: "retry_then_agent", only_if_changed: false },
|
|
32
|
+
steps: [
|
|
33
|
+
{ id: "mail", kind: "tool", label: "Read the mail since the last read", needs: { category: "mail" }, tool: "list_messages", input: { since: "{{ trigger.at }}", limit: 200 } },
|
|
34
|
+
{ id: "touches", kind: "action", label: "Save who wrote", action: "record_touches", input: { touches: "{{ steps.mail.output.messages }}" } },
|
|
35
|
+
{ id: "write", kind: "llm", label: "Write the follow-up", tier: "smart", prompt: "Write a short follow-up to the last person who wrote." },
|
|
36
|
+
{ id: "tell", kind: "notify", message: "Morning read done." },
|
|
37
|
+
],
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const FRIDAY = {
|
|
41
|
+
format: 1,
|
|
42
|
+
key: "friday-review",
|
|
43
|
+
name: "Friday review",
|
|
44
|
+
trigger: { type: "schedule", cron: "0 16 * * 5", tz: "team" },
|
|
45
|
+
inputs: {},
|
|
46
|
+
settings: { overlap: "skip", on_failure: "retry_then_agent", only_if_changed: false },
|
|
47
|
+
steps: [{ id: "tell", kind: "notify", message: "Friday review is ready." }],
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const flat = (steps) => steps.flatMap((s) => [s, ...(s.then ?? []).flatMap((x) => flat([x])), ...(s.else ?? []).flatMap((x) => flat([x])), ...flat(s.steps ?? [])]);
|
|
51
|
+
const WIDTHS = [1280, 800, 368, 320];
|
|
52
|
+
|
|
53
|
+
function Page() {
|
|
54
|
+
const [store, setStore] = useState(() => ({
|
|
55
|
+
a1: {
|
|
56
|
+
id: "a1",
|
|
57
|
+
source: "package",
|
|
58
|
+
def: MORNING,
|
|
59
|
+
liveDef: MORNING,
|
|
60
|
+
live: 1,
|
|
61
|
+
draft: 1,
|
|
62
|
+
status: "on",
|
|
63
|
+
versions: [{ version: 1, kind: "shipped", created_at: "2026-10-09T09:00:00Z" }],
|
|
64
|
+
runs: [],
|
|
65
|
+
// What the agent drafted from a failed run: kept, it becomes version 2 and runs.
|
|
66
|
+
suggestions: [{ id: "s1", run_id: "r0", why: "The mail tool refused a limit of 200. Asking for 50 at a time works.", base_version: 1, stale: false, patch: { changes: [{ id: "mail", input: { limit: 50 } }] } }],
|
|
67
|
+
},
|
|
68
|
+
a2: { id: "a2", source: "member", def: FRIDAY, liveDef: FRIDAY, live: 1, draft: 1, status: "off", versions: [{ version: 1, kind: "member", created_at: "2026-10-09T09:00:00Z" }], runs: [] },
|
|
69
|
+
}));
|
|
70
|
+
const [at, setAt] = useState({ id: null, run: null, tab: "flow", start: null });
|
|
71
|
+
const go = (next) => setAt({ id: null, run: null, tab: "flow", start: null, ...next });
|
|
72
|
+
|
|
73
|
+
const rows = useMemo(
|
|
74
|
+
() =>
|
|
75
|
+
Object.values(store).map((a) => ({
|
|
76
|
+
id: a.id,
|
|
77
|
+
key: a.def.key,
|
|
78
|
+
name: a.def.name,
|
|
79
|
+
status: a.status,
|
|
80
|
+
live_version: a.live,
|
|
81
|
+
draft_version: a.draft,
|
|
82
|
+
trigger: a.def.trigger,
|
|
83
|
+
tz: "America/New_York",
|
|
84
|
+
next_run_at: a.status === "on" ? "2026-10-12T11:00:00.000Z" : null,
|
|
85
|
+
last_run: a.runs[0] ? { status: a.runs[0].status, started_at: a.runs[0].created_at, credits: a.runs[0].credits } : null,
|
|
86
|
+
owner_kind: "app",
|
|
87
|
+
app_id: "crm",
|
|
88
|
+
edited: a.versions.some((v) => v.kind === "member"),
|
|
89
|
+
source: a.source,
|
|
90
|
+
})),
|
|
91
|
+
[store],
|
|
92
|
+
);
|
|
93
|
+
const patch = (id, fn) => setStore((s) => ({ ...s, [id]: fn(s[id]) }));
|
|
94
|
+
// The platform's answer to a delete: gone (its runs stay in history), and the one that followed it named.
|
|
95
|
+
const remove = async (x) => {
|
|
96
|
+
setStore((s) => Object.fromEntries(Object.entries(s).filter(([k]) => k !== x.id)));
|
|
97
|
+
go({});
|
|
98
|
+
return { ok: true };
|
|
99
|
+
};
|
|
100
|
+
// What the platform says about tests: a shipped version needs none; any other
|
|
101
|
+
// needs a passed test of that very version.
|
|
102
|
+
const testsOf = (a) => {
|
|
103
|
+
const view = (version) => (version === null || version === undefined ? null : { version, exempt: a.versions.some((v) => v.version === version && v.kind === "shipped"), passed: a.passed === version, last: a.lastTest && a.lastTest.version === version ? a.lastTest.last : null });
|
|
104
|
+
return { live: view(a.live), draft: a.draft !== a.live ? view(a.draft) : null };
|
|
105
|
+
};
|
|
106
|
+
const needsTest = (a, version, opts) => !opts?.skipTest && !testsOf(a)[version === a.live ? "live" : "draft"]?.exempt && a.passed !== version;
|
|
107
|
+
const REFUSED = { ok: false, code: "needs_test", error: "Run a test of this version first. It reads your real data and changes nothing." };
|
|
108
|
+
|
|
109
|
+
if (at.id === "new") {
|
|
110
|
+
const taken = new Set(Object.values(store).map((a) => a.def.key));
|
|
111
|
+
const picked = STARTING_POINTS.find((s) => s.id === at.start);
|
|
112
|
+
return (
|
|
113
|
+
<AutomationEditor
|
|
114
|
+
key="new"
|
|
115
|
+
automationId={null}
|
|
116
|
+
status="new"
|
|
117
|
+
baseline={picked ? picked.build(taken) : blankAutomation("", keyFromName("New automation", taken))}
|
|
118
|
+
ownedByApp
|
|
119
|
+
onBack={() => go({})}
|
|
120
|
+
onSave={async (def) => {
|
|
121
|
+
const id = `a${Object.keys(store).length + 1}`;
|
|
122
|
+
setStore((s) => ({ ...s, [id]: { id, def: { ...def, key: keyFromName(def.name, taken) }, live: null, draft: 1, status: "draft", versions: [{ version: 1, kind: "member", created_at: new Date().toISOString() }], runs: [] } }));
|
|
123
|
+
go({ id });
|
|
124
|
+
return { ok: true };
|
|
125
|
+
}}
|
|
126
|
+
/>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const a = at.id ? store[at.id] : null;
|
|
131
|
+
if (!a) {
|
|
132
|
+
return (
|
|
133
|
+
<AutomationList
|
|
134
|
+
rows={rows}
|
|
135
|
+
grouped={false}
|
|
136
|
+
appName={() => "CRM"}
|
|
137
|
+
now={new Date("2026-10-11T12:00:00.000Z")}
|
|
138
|
+
onOpen={(id) => go({ id })}
|
|
139
|
+
onNew={(s) => go({ id: "new", start: s?.id ?? null })}
|
|
140
|
+
onToggle={async (row, on) => patch(row.id, (x) => ({ ...x, status: on ? "on" : "off" }))}
|
|
141
|
+
onDelete={remove}
|
|
142
|
+
/>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const tabs = (
|
|
147
|
+
<ButtonGroup aria-label="Inside this automation">
|
|
148
|
+
{[["flow", "Flow"], ["runs", "Runs"]].map(([id, label]) => (
|
|
149
|
+
<Button key={id} size="sm" variant={at.tab === id ? "primary" : "secondary"} onClick={() => go({ id: a.id, tab: id })}>
|
|
150
|
+
{label}
|
|
151
|
+
</Button>
|
|
152
|
+
))}
|
|
153
|
+
</ButtonGroup>
|
|
154
|
+
);
|
|
155
|
+
const run = at.run ? a.runs.find((r) => r.id === at.run) : null;
|
|
156
|
+
if (run) {
|
|
157
|
+
return (
|
|
158
|
+
<RunView
|
|
159
|
+
def={a.def}
|
|
160
|
+
detail={{ run, steps: run.steps, steps_visible: true, automation: { name: a.def.name } }}
|
|
161
|
+
runs={a.runs}
|
|
162
|
+
tz="America/New_York"
|
|
163
|
+
tabs={tabs}
|
|
164
|
+
name={a.def.name}
|
|
165
|
+
onBack={() => go({ id: a.id })}
|
|
166
|
+
onPickRun={(r) => go({ id: a.id, run: r })}
|
|
167
|
+
/>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
if (at.tab === "runs") {
|
|
171
|
+
return (
|
|
172
|
+
<div className="flex min-h-0 flex-1 flex-col">
|
|
173
|
+
<div className="flex items-center gap-2 border-b border-border px-3 py-2">
|
|
174
|
+
<Button size="sm" variant="quiet" icon="arrow-left" onClick={() => go({})}>
|
|
175
|
+
Automations
|
|
176
|
+
</Button>
|
|
177
|
+
{tabs}
|
|
178
|
+
</div>
|
|
179
|
+
<RunsList runs={a.runs} tz="America/New_York" onOpen={(r) => go({ id: a.id, run: r })} />
|
|
180
|
+
</div>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
const agentSaves = () =>
|
|
184
|
+
patch(a.id, (x) => {
|
|
185
|
+
const version = x.versions.length + 1;
|
|
186
|
+
const def = { ...x.def, steps: x.def.steps.map((st) => (st.id === "mail" ? { ...st, input: { ...st.input, limit: 100 } } : st)) };
|
|
187
|
+
return { ...x, def, draft: version, versions: [...x.versions, { version, kind: "agent", author: "agent", note: "Changed: read 100 at a time", created_at: new Date().toISOString() }] };
|
|
188
|
+
});
|
|
189
|
+
return (
|
|
190
|
+
<>
|
|
191
|
+
<div className="flex items-center gap-2 border-b border-border bg-muted px-3 py-1.5 text-xs text-muted-foreground">
|
|
192
|
+
<span>Pretend platform:</span>
|
|
193
|
+
<Button size="sm" variant="secondary" onClick={agentSaves}>
|
|
194
|
+
The agent saves a draft
|
|
195
|
+
</Button>
|
|
196
|
+
</div>
|
|
197
|
+
<AutomationEditor
|
|
198
|
+
key={a.id}
|
|
199
|
+
automationId={a.id}
|
|
200
|
+
status={a.status}
|
|
201
|
+
liveVersion={a.live}
|
|
202
|
+
draftVersion={a.draft}
|
|
203
|
+
baseline={a.def}
|
|
204
|
+
compareTo={{ version: a.live, definition: a.liveDef }}
|
|
205
|
+
versions={[...a.versions].reverse()}
|
|
206
|
+
runs={a.runs}
|
|
207
|
+
estimate={{ credits: 3, credits_max: 12, steps: [{ id: "write", credits: 3, credits_max: 12 }] }}
|
|
208
|
+
ownedByApp
|
|
209
|
+
tabs={tabs}
|
|
210
|
+
source={a.source}
|
|
211
|
+
onDelete={remove}
|
|
212
|
+
onBack={() => go({})}
|
|
213
|
+
onSave={async (def, { baseVersion } = {}) => {
|
|
214
|
+
// The platform's own rule: a save names the version it was made from; a newer one refuses it.
|
|
215
|
+
const latest = a.versions.length;
|
|
216
|
+
if (baseVersion !== latest) return { ok: false, code: "changed", latest, error: "Someone saved a newer version while you were editing. Open it again to see theirs." };
|
|
217
|
+
patch(a.id, (x) => ({ ...x, def, draft: x.versions.length + 1, versions: [...x.versions, { version: x.versions.length + 1, kind: "member", created_at: new Date().toISOString() }] }));
|
|
218
|
+
return { ok: true, version: latest + 1 };
|
|
219
|
+
}}
|
|
220
|
+
suggestions={a.suggestions ?? []}
|
|
221
|
+
onKeepSuggestion={async (id) => {
|
|
222
|
+
const s = (a.suggestions ?? []).find((x) => x.id === id);
|
|
223
|
+
if (!s) return { ok: false, error: "That suggestion is gone." };
|
|
224
|
+
patch(a.id, (x) => {
|
|
225
|
+
const version = x.versions.length + 1;
|
|
226
|
+
const def = { ...x.def, steps: x.def.steps.map((st) => { const c = s.patch.changes.find((ch) => ch.id === st.id); return c && c.input ? { ...st, input: { ...st.input, ...c.input } } : st; }) };
|
|
227
|
+
return { ...x, def, liveDef: def, live: version, draft: version, versions: [...x.versions, { version, kind: "agent", created_at: new Date().toISOString() }], suggestions: x.suggestions.filter((y) => y.id !== id) };
|
|
228
|
+
});
|
|
229
|
+
return { ok: true };
|
|
230
|
+
}}
|
|
231
|
+
onDropSuggestion={async (id) => {
|
|
232
|
+
patch(a.id, (x) => ({ ...x, suggestions: (x.suggestions ?? []).filter((y) => y.id !== id) }));
|
|
233
|
+
return { ok: true };
|
|
234
|
+
}}
|
|
235
|
+
tests={testsOf(a)}
|
|
236
|
+
onKeep={async (opts) => {
|
|
237
|
+
if (needsTest(a, a.draft, opts)) return REFUSED;
|
|
238
|
+
patch(a.id, (x) => ({ ...x, live: x.draft, liveDef: x.def }));
|
|
239
|
+
return { ok: true };
|
|
240
|
+
}}
|
|
241
|
+
onLive={async (on, opts) => {
|
|
242
|
+
if (on && needsTest(a, a.live, opts)) return REFUSED;
|
|
243
|
+
patch(a.id, (x) => ({ ...x, status: on ? "on" : "off" }));
|
|
244
|
+
return { ok: true };
|
|
245
|
+
}}
|
|
246
|
+
onTest={async () => {
|
|
247
|
+
const id = `r${a.runs.length + 1}`;
|
|
248
|
+
const t = new Date().toISOString();
|
|
249
|
+
// A dry run: reading and thinking happen, writing and telling are only drawn.
|
|
250
|
+
const standIn = new Set(["action", "notify"]);
|
|
251
|
+
const steps = flat(a.def.steps).map((s) => {
|
|
252
|
+
const wouldDo = standIn.has(s.kind);
|
|
253
|
+
return { path: s.id, step_id: s.id, kind: s.kind, attempt: 1, status: "done", started_at: t, ended_at: t, credits: s.kind === "llm" ? 3 : 0, error: null, ...(wouldDo ? { simulated: true, output: { simulated: true, would: { kind: s.kind, effect: s.kind === "notify" ? "note" : "write", words: s.kind === "notify" ? "Would leave a note." : `Would run ${s.action}.` } } } : { output: { ok: true } }) };
|
|
254
|
+
});
|
|
255
|
+
const would = steps.filter((x) => x.simulated).length;
|
|
256
|
+
const wouldList = steps.filter((x) => x.simulated).map((x) => ({ step: x.step_id, path: x.path, kind: x.kind, effect: x.output.would.effect, words: x.output.would.words }));
|
|
257
|
+
const version = a.draft;
|
|
258
|
+
patch(a.id, (x) => ({ ...x, passed: version, lastTest: { version, last: { run_id: id, status: "done", ended_at: t, credits: 3, would } }, runs: [{ id, status: "done", dry: true, version, trigger_type: "manual", is_test: true, created_at: t, started_at: t, ended_at: t, credits: 3, result: { dry: { simulated: would, would: wouldList, unchecked: [], sampled: [] } }, steps }, ...x.runs] }));
|
|
259
|
+
go({ id: a.id, run: id });
|
|
260
|
+
return { ok: true };
|
|
261
|
+
}}
|
|
262
|
+
onOpenRun={(r) => go({ id: a.id, run: r })}
|
|
263
|
+
/>
|
|
264
|
+
</>
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export default function AutomationEditorDemo() {
|
|
269
|
+
const [width, setWidth] = useState(1280);
|
|
270
|
+
return (
|
|
271
|
+
<div className="grid gap-4 p-4">
|
|
272
|
+
<ButtonGroup aria-label="The block's width">
|
|
273
|
+
{WIDTHS.map((w) => (
|
|
274
|
+
<Button key={w} size="sm" variant={w === width ? "primary" : "secondary"} onClick={() => setWidth(w)}>
|
|
275
|
+
{w} px
|
|
276
|
+
</Button>
|
|
277
|
+
))}
|
|
278
|
+
</ButtonGroup>
|
|
279
|
+
<div className="flex h-[720px] w-full flex-col overflow-hidden rounded-lg border border-border" style={{ maxWidth: width }}>
|
|
280
|
+
<Page />
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
);
|
|
284
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// The small question a manual run asks first: one field for each input the
|
|
4
|
+
// automation declares, with its default. The values are typed to the
|
|
5
|
+
// input's kind before they go (the platform checks them again).
|
|
6
|
+
|
|
7
|
+
import { useId, useState } from "react";
|
|
8
|
+
import { Button, Checkbox, Field, Input, Select, Sheet } from "../../ui";
|
|
9
|
+
|
|
10
|
+
function initial(spec) {
|
|
11
|
+
if (spec.type === "boolean") return spec.default === true;
|
|
12
|
+
return spec.default === undefined ? "" : String(spec.default);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* open, inputs { name: { type: string|number|boolean|date, label, default, required, options } }
|
|
17
|
+
* onRun(values), onCancel()
|
|
18
|
+
*/
|
|
19
|
+
export function RunInputs({ open, inputs, onRun, onCancel }) {
|
|
20
|
+
const names = Object.keys(inputs ?? {});
|
|
21
|
+
const [values, setValues] = useState(() => Object.fromEntries(names.map((n) => [n, initial(inputs[n])])));
|
|
22
|
+
const id = useId();
|
|
23
|
+
const missing = names.filter((n) => inputs[n].required && inputs[n].type !== "boolean" && String(values[n] ?? "").trim() === "");
|
|
24
|
+
|
|
25
|
+
const submit = () => {
|
|
26
|
+
const out = {};
|
|
27
|
+
for (const n of names) {
|
|
28
|
+
const spec = inputs[n];
|
|
29
|
+
const v = values[n];
|
|
30
|
+
if (spec.type === "boolean") out[n] = v === true;
|
|
31
|
+
else if (String(v ?? "").trim() === "") continue;
|
|
32
|
+
else out[n] = spec.type === "number" ? Number(v) : String(v);
|
|
33
|
+
}
|
|
34
|
+
onRun(out);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Sheet
|
|
39
|
+
open={open}
|
|
40
|
+
onClose={onCancel}
|
|
41
|
+
side="center"
|
|
42
|
+
title="Test run"
|
|
43
|
+
footer={
|
|
44
|
+
<>
|
|
45
|
+
<Button variant="quiet" onClick={onCancel}>
|
|
46
|
+
Cancel
|
|
47
|
+
</Button>
|
|
48
|
+
<Button variant="primary" disabled={missing.length > 0} onClick={submit}>
|
|
49
|
+
Run it
|
|
50
|
+
</Button>
|
|
51
|
+
</>
|
|
52
|
+
}
|
|
53
|
+
>
|
|
54
|
+
<form
|
|
55
|
+
className="grid gap-4"
|
|
56
|
+
onSubmit={(e) => {
|
|
57
|
+
e.preventDefault();
|
|
58
|
+
if (!missing.length) submit();
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
<p className="m-0 text-sm text-muted-foreground">It runs once with real data. Nothing is sent or changed by a step marked as a pencil draft.</p>
|
|
62
|
+
{names.map((n, i) => {
|
|
63
|
+
const spec = inputs[n];
|
|
64
|
+
const fid = `${id}-${i}`;
|
|
65
|
+
const label = `${spec.label || n}${spec.required ? " *" : ""}`;
|
|
66
|
+
if (spec.type === "boolean") {
|
|
67
|
+
return <Checkbox key={n} id={fid} label={label} checked={values[n] === true} onChange={(e) => setValues({ ...values, [n]: e.target.checked })} />;
|
|
68
|
+
}
|
|
69
|
+
return (
|
|
70
|
+
<Field key={n} id={fid} label={label}>
|
|
71
|
+
{spec.options?.length ? (
|
|
72
|
+
<Select id={fid} value={String(values[n] ?? "")} options={[{ value: "", label: "" }, ...spec.options.map((o) => ({ value: o, label: o }))]} onChange={(e) => setValues({ ...values, [n]: e.target.value })} />
|
|
73
|
+
) : (
|
|
74
|
+
<Input id={fid} type={spec.type === "number" ? "number" : spec.type === "date" ? "date" : "text"} value={String(values[n] ?? "")} onChange={(e) => setValues({ ...values, [n]: e.target.value })} />
|
|
75
|
+
)}
|
|
76
|
+
</Field>
|
|
77
|
+
);
|
|
78
|
+
})}
|
|
79
|
+
</form>
|
|
80
|
+
</Sheet>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# AutomationList
|
|
2
|
+
|
|
3
|
+
An app's automations as rows. **The whole row opens the automation** (a
|
|
4
|
+
stretched button under the switch), so a member is never hunting for a
|
|
5
|
+
small arrow. Each row says what it is called and when it runs (in words, in
|
|
6
|
+
the automation's own time zone), how its last run went as a dot and a word
|
|
7
|
+
(Healthy, Last run failed, Needs its owner, Waiting, Running, Off, Not live
|
|
8
|
+
yet), when the next is due, what the last one cost, and an on/off switch
|
|
9
|
+
that keeps its own press.
|
|
10
|
+
|
|
11
|
+
- A line above the rows counts what is on, what failed last time, what
|
|
12
|
+
waits for its owner, and which runs next.
|
|
13
|
+
- A search box; **New automation** (a picker of starting points once there
|
|
14
|
+
are some, straight to a blank when there are none).
|
|
15
|
+
- **First run** — with no automations, a few ready-made flows to start
|
|
16
|
+
from (a Monday reminder, check then tell me, wait then follow up, an
|
|
17
|
+
app's action every morning, ask the model for a line).
|
|
18
|
+
- **Describe it** (with `onDescribe`) — a box at the top, above the rows, on
|
|
19
|
+
the first-run screen and in the New automation sheet: say what it should
|
|
20
|
+
do in a sentence, press "Draft it" (the button carries its price), and the
|
|
21
|
+
platform draws it; it opens when ready. It says "Drawing it…" while it
|
|
22
|
+
works. A question back is listed under the box ("Ask me about: …"); any
|
|
23
|
+
other refusal shows its words. Three chips start the sentence. Without
|
|
24
|
+
`onDescribe` nothing changes.
|
|
25
|
+
- On a team page, the rows are grouped Team and From <app>; on an app's
|
|
26
|
+
own page pass `grouped={false}`.
|
|
27
|
+
|
|
28
|
+
```jsx
|
|
29
|
+
import { AutomationList } from "../components/blocks/automation-list"; // a Makersclaw app
|
|
30
|
+
import { AutomationList } from "@/components/makersclaw/blocks/automation-list"; // a shadcn project
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`makersclaw add automation-list` lays `automation-model` with it. A client
|
|
34
|
+
component. It replaces `schedule-card` for an app whose jobs run on the
|
|
35
|
+
automations engine (the card still works for an app that has not moved).
|
|
36
|
+
Its own width decides its layout: under about 576 px a row stacks its
|
|
37
|
+
facts under its name.
|
|
38
|
+
|
|
39
|
+
## Props
|
|
40
|
+
|
|
41
|
+
- `rows` — the list: `useAutomations().automations`. Each: `{ id, name,
|
|
42
|
+
key, status (on · off · draft · needs_owner), live_version, trigger,
|
|
43
|
+
next_run_at, tz, last_run: { status, started_at, credits }, active_runs,
|
|
44
|
+
edited, owner_kind, app_id }`.
|
|
45
|
+
- `onOpen(id)` — a whole row was pressed.
|
|
46
|
+
- `onNew(start?)` — New automation; `start` is a starting point (`{ id,
|
|
47
|
+
name, build(takenKeys) }`) when one was picked, else a blank is meant.
|
|
48
|
+
- `onDelete(row)` — async: **Delete automation**, in a menu on each row. It
|
|
49
|
+
asks first, in words that say what it does (`Deletes "<name>". Its runs
|
|
50
|
+
stay in history.`, and for a row that is on, that deleting also switches it
|
|
51
|
+
off). Resolves `{ ok: true }` (the screen says what followed) or `{ ok:
|
|
52
|
+
false, error }` (shown above the rows as it is). A row from an app's own job
|
|
53
|
+
or one an app shipped (`row.source` `legacy`, `package`) has the item
|
|
54
|
+
disabled, pointing at the app's settings. `canDelete` hides the menu
|
|
55
|
+
(default true). Without `onDelete`, no menu.
|
|
56
|
+
- `onToggle(row, on)` — async; the switch. A refusal throws its words
|
|
57
|
+
(shown above the rows). `canToggle` — draw the switch (default true).
|
|
58
|
+
`canCreate` — draw New automation (default true).
|
|
59
|
+
- `appName(id)`, `appFilter`, `onClearFilter`, `grouped`.
|
|
60
|
+
- `query`, `onQueryChange(text)`: the search box starts with `query` and reports each change. A screen that opens an automation and comes back keeps the words in its own place and hands them back, so the list returns as it was left.
|
|
61
|
+
- `starts` — your own starting points (default automation-model's
|
|
62
|
+
`STARTING_POINTS`). An app offers flows that name its own actions here.
|
|
63
|
+
The platform's own come through `startsFromPlatform(useAutomations().starts)`
|
|
64
|
+
(automation-model): each card then also says what it needs, what a model
|
|
65
|
+
does and what it costs ("needs payments, a mailbox · a model writes three
|
|
66
|
+
lines · about 6 cr", or "free"). The raw list is also accepted.
|
|
67
|
+
- `onDescribe(ask)` — async: `useAutomations().describe`. Resolves `{ ok,
|
|
68
|
+
id }` (the list calls `onOpen(id)`), `{ ok: true }` with no id (the
|
|
69
|
+
sentence was handed on, to an agent beside the list say: the box empties),
|
|
70
|
+
`{ ok: false, questions }` or `{ ok: false, error }`. `describePrice` — what the button says it costs (default
|
|
71
|
+
"1 call · about 1 cr"); `describeChips` — the sentences that start the
|
|
72
|
+
box (default three).
|
|
73
|
+
- `title`, `intro`, `now` (the clock, for a test), `className`.
|
|
74
|
+
|
|
75
|
+
### StartingPoints
|
|
76
|
+
|
|
77
|
+
The picker New automation opens: `open`, `onClose`, `starts`, `onPick(s)`,
|
|
78
|
+
`onBlank()`, and (for Describe it) `onDescribe`, `onOpen`, `describePrice`,
|
|
79
|
+
`describeChips`. With Describe it, the ready-made ones sit under "Start from
|
|
80
|
+
a ready one".
|
|
81
|
+
|
|
82
|
+
## Hooks (lib/hooks.js)
|
|
83
|
+
|
|
84
|
+
`useAutomations()` → `{ automations, triggers, starts, describe(ask, app?),
|
|
85
|
+
describing, busy, creating, setStatus(id, on), create(definition), loading,
|
|
86
|
+
error, unavailable, reload }`. It reads
|
|
87
|
+
`GET _claw/automations`; `setStatus` patches `status`; `create` posts a
|
|
88
|
+
definition and resolves `{ ok, data: { id } }` or `{ ok: false, error,
|
|
89
|
+
problems }`. Looks every 30 s (5 s while one runs), not at all while the
|
|
90
|
+
page is hidden. On a claw from before the engine it is `unavailable`:
|
|
91
|
+
show a quiet "not set up yet". `starts` is `[]`: the desk's starting points are not served to an app
|
|
92
|
+
(`_claw/automation-starts` does not exist), so the lib asks nothing and
|
|
93
|
+
an app brings its own; `describe(ask, app?)` posts `_claw/automation-draft`
|
|
94
|
+
and resolves `{ ok: true, id, … }`, `{ ok: false, error, code, questions? }`
|
|
95
|
+
or `{ ok: false, code: "unavailable" }`, never thrown.
|
|
96
|
+
|
|
97
|
+
## Never
|
|
98
|
+
|
|
99
|
+
- Never switch an automation on that has no version kept: the switch is
|
|
100
|
+
disabled until one is (`live_version`).
|
|
101
|
+
- Never show a time in the viewer's zone: `next_run_at` is shown in `tz`.
|
|
102
|
+
|
|
103
|
+
## Example
|
|
104
|
+
|
|
105
|
+
```jsx
|
|
106
|
+
"use client";
|
|
107
|
+
import { useRouter } from "next/navigation";
|
|
108
|
+
import { useAutomations } from "../lib/hooks";
|
|
109
|
+
import { AutomationList } from "../components/blocks/automation-list";
|
|
110
|
+
|
|
111
|
+
export default function Automations() {
|
|
112
|
+
const router = useRouter();
|
|
113
|
+
const { automations, setStatus, unavailable } = useAutomations();
|
|
114
|
+
if (unavailable) return <p className="p-4 text-sm text-muted-foreground">Automations are not set up here yet.</p>;
|
|
115
|
+
return (
|
|
116
|
+
<AutomationList
|
|
117
|
+
rows={automations}
|
|
118
|
+
grouped={false}
|
|
119
|
+
title="Automations"
|
|
120
|
+
onOpen={(id) => router.push(`?automation=${id}`)}
|
|
121
|
+
onToggle={async (row, on) => {
|
|
122
|
+
const r = await setStatus(row.id, on);
|
|
123
|
+
if (!r.ok) throw new Error(r.error);
|
|
124
|
+
}}
|
|
125
|
+
onNew={(start) => router.push(`?automation=new${start ? `&start=${start.id}` : ""}`)}
|
|
126
|
+
/>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
```
|