makersclaw 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +225 -0
- package/registry/blocks/agent-pane/agent-pane.jsx +244 -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 +1627 -8
- package/templates/next/files/lib/platform.js +529 -21
- package/templates/next/template.json +13 -3
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// AutomationModel's gallery surface: every kind of step with its face,
|
|
4
|
+
// its group and whether it costs, and the words the blocks say. Not
|
|
5
|
+
// installed into apps.
|
|
6
|
+
import { Glyph, KINDS, clockWords, creditsWords, healthOf, paletteGroups, triggerWords } from "../components/blocks/automation-model";
|
|
7
|
+
|
|
8
|
+
const NOW = new Date("2026-10-11T12:00:00.000Z");
|
|
9
|
+
|
|
10
|
+
export default function AutomationModelDemo() {
|
|
11
|
+
return (
|
|
12
|
+
<div className="grid gap-6 p-4">
|
|
13
|
+
{paletteGroups().map((g) => (
|
|
14
|
+
<section key={g.group} className="grid gap-2">
|
|
15
|
+
<h3 className="m-0 text-sm font-semibold">{g.label}</h3>
|
|
16
|
+
<ul className="m-0 grid list-none gap-2 p-0 sm:grid-cols-2">
|
|
17
|
+
{g.kinds.map((k) => (
|
|
18
|
+
<li key={k.kind} className="flex items-start gap-3 rounded-lg border border-border bg-card p-3 text-card-foreground">
|
|
19
|
+
<span className="inline-flex size-9 shrink-0 items-center justify-center rounded-lg border border-border">
|
|
20
|
+
<Glyph name={KINDS[k.kind].glyph} size={18} />
|
|
21
|
+
</span>
|
|
22
|
+
<span className="grid gap-0.5">
|
|
23
|
+
<b className="text-sm">{k.name}</b>
|
|
24
|
+
<span className="text-xs text-muted-foreground">{k.blurb}</span>
|
|
25
|
+
<span className="text-xs text-muted-foreground">{k.paid ? "Costs credits" : "Free"}</span>
|
|
26
|
+
</span>
|
|
27
|
+
</li>
|
|
28
|
+
))}
|
|
29
|
+
</ul>
|
|
30
|
+
</section>
|
|
31
|
+
))}
|
|
32
|
+
<section className="grid gap-1 text-sm">
|
|
33
|
+
<h3 className="m-0 text-sm font-semibold">The words</h3>
|
|
34
|
+
<p className="m-0">{triggerWords({ type: "schedule", cron: "0 7 * * 1-5", tz: "team" })}</p>
|
|
35
|
+
<p className="m-0">{triggerWords({ type: "schedule", cron: "0 13,21 * * 1-5", tz: "Europe/Paris" })}</p>
|
|
36
|
+
<p className="m-0">Next: {clockWords("2026-10-12T11:00:00.000Z", "America/New_York", NOW)}</p>
|
|
37
|
+
<p className="m-0">Cost: {creditsWords(3.24)}; free: {creditsWords(0)}</p>
|
|
38
|
+
<p className="m-0">Health: {healthOf({ status: "on", live_version: 1, last_run: { status: "failed" } }).text}</p>
|
|
39
|
+
</section>
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
// Edits to a definition, as pure functions: each takes the definition and
|
|
2
|
+
// returns a new one, never touching the old (so undo is a stack of them).
|
|
3
|
+
//
|
|
4
|
+
// The canvas speaks in slots (graph.js: a list and a place in it) and in
|
|
5
|
+
// step ids; the format speaks in `then`, `else` and `steps` lists. This
|
|
6
|
+
// file is the only place the two meet.
|
|
7
|
+
//
|
|
8
|
+
// Step ids are the merge key between a shipped version and a member's
|
|
9
|
+
// edit, so an edit never renames one.
|
|
10
|
+
|
|
11
|
+
import { newStep } from "./catalog";
|
|
12
|
+
|
|
13
|
+
const clone = (v) => JSON.parse(JSON.stringify(v));
|
|
14
|
+
|
|
15
|
+
/** Every step id in the definition, nested ones included. */
|
|
16
|
+
export function allStepIds(steps) {
|
|
17
|
+
const ids = new Set();
|
|
18
|
+
const walk = (list) => {
|
|
19
|
+
for (const s of list) {
|
|
20
|
+
ids.add(s.id);
|
|
21
|
+
if (s.kind === "condition") {
|
|
22
|
+
walk(s.then ?? []);
|
|
23
|
+
walk(s.else ?? []);
|
|
24
|
+
} else if (s.kind === "loop") walk(s.steps ?? []);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
walk(steps ?? []);
|
|
28
|
+
return ids;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** A step by id, wherever it sits. */
|
|
32
|
+
export function findStep(steps, id) {
|
|
33
|
+
for (const s of steps ?? []) {
|
|
34
|
+
if (s.id === id) return s;
|
|
35
|
+
if (s.kind === "condition") {
|
|
36
|
+
const hit = findStep(s.then ?? [], id) ?? findStep(s.else ?? [], id);
|
|
37
|
+
if (hit) return hit;
|
|
38
|
+
} else if (s.kind === "loop") {
|
|
39
|
+
const hit = findStep(s.steps ?? [], id);
|
|
40
|
+
if (hit) return hit;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** The list a ref names, in a (mutable, cloned) definition. A condition's
|
|
47
|
+
* empty branch is made when it is first needed. */
|
|
48
|
+
function listOf(steps, ref) {
|
|
49
|
+
if (ref.at === "root") return steps;
|
|
50
|
+
const owner = findStep(steps, ref.step);
|
|
51
|
+
if (!owner) return null;
|
|
52
|
+
if (ref.at === "loop") return owner.kind === "loop" ? (owner.steps ??= []) : null;
|
|
53
|
+
if (owner.kind !== "condition") return null;
|
|
54
|
+
if (ref.at === "then") return (owner.then ??= []);
|
|
55
|
+
return (owner.else ??= []);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Put a step into the definition at a slot. */
|
|
59
|
+
export function insertStep(def, slot, step) {
|
|
60
|
+
const next = clone(def);
|
|
61
|
+
const list = listOf(next.steps, slot.list);
|
|
62
|
+
if (!list) return def;
|
|
63
|
+
list.splice(Math.max(0, Math.min(slot.index, list.length)), 0, step);
|
|
64
|
+
return next;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** A new step of `kind` at a slot, with an id nobody uses. */
|
|
68
|
+
export function addStep(def, slot, kind) {
|
|
69
|
+
const step = newStep(kind, allStepIds(def.steps));
|
|
70
|
+
return { def: insertStep(def, slot, step), id: step.id };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Where a step sits: its list and its index. */
|
|
74
|
+
export function locate(steps, id, ref = { at: "root" }) {
|
|
75
|
+
for (let i = 0; i < (steps ?? []).length; i += 1) {
|
|
76
|
+
const s = steps[i];
|
|
77
|
+
if (s.id === id) return { list: ref, index: i };
|
|
78
|
+
if (s.kind === "condition") {
|
|
79
|
+
const hit = locate(s.then ?? [], id, { at: "then", step: s.id }) ?? locate(s.else ?? [], id, { at: "else", step: s.id });
|
|
80
|
+
if (hit) return hit;
|
|
81
|
+
} else if (s.kind === "loop") {
|
|
82
|
+
const hit = locate(s.steps ?? [], id, { at: "loop", step: s.id });
|
|
83
|
+
if (hit) return hit;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** The slot right after a step: where "add a step here" puts one. */
|
|
90
|
+
export function slotAfter(def, id) {
|
|
91
|
+
const at = locate(def.steps, id);
|
|
92
|
+
return at ? { list: at.list, index: at.index + 1 } : null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** The end of the main path: where a palette press adds a step when none
|
|
96
|
+
* is selected. */
|
|
97
|
+
export function slotAtEnd(def) {
|
|
98
|
+
return { list: { at: "root" }, index: def.steps.length };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** How many steps a step holds inside it (a condition's branches, a loop). */
|
|
102
|
+
export function innerCount(step) {
|
|
103
|
+
if (step.kind === "condition") return (step.then?.length ?? 0) + (step.else?.length ?? 0);
|
|
104
|
+
if (step.kind === "loop") return step.steps?.length ?? 0;
|
|
105
|
+
return 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Take a step out, with everything inside it. */
|
|
109
|
+
export function removeStep(def, id) {
|
|
110
|
+
const next = clone(def);
|
|
111
|
+
const cut = (list) => {
|
|
112
|
+
const i = list.findIndex((s) => s.id === id);
|
|
113
|
+
if (i >= 0) {
|
|
114
|
+
list.splice(i, 1);
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
for (const s of list) {
|
|
118
|
+
if (s.kind === "condition" && (cut(s.then ?? []) || cut(s.else ?? []))) return true;
|
|
119
|
+
if (s.kind === "loop" && cut(s.steps ?? [])) return true;
|
|
120
|
+
}
|
|
121
|
+
return false;
|
|
122
|
+
};
|
|
123
|
+
return cut(next.steps) ? next : def;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Change a step's own fields. Its id and kind never change. */
|
|
127
|
+
export function updateStep(def, id, patch) {
|
|
128
|
+
const next = clone(def);
|
|
129
|
+
const step = findStep(next.steps, id);
|
|
130
|
+
if (!step) return def;
|
|
131
|
+
for (const [k, v] of Object.entries(patch)) {
|
|
132
|
+
if (k === "id" || k === "kind") continue;
|
|
133
|
+
if (v === undefined) delete step[k];
|
|
134
|
+
else step[k] = v;
|
|
135
|
+
}
|
|
136
|
+
return next;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Move a step one place earlier or later within its own list. */
|
|
140
|
+
export function nudgeStep(def, id, by) {
|
|
141
|
+
const at = locate(def.steps, id);
|
|
142
|
+
if (!at) return def;
|
|
143
|
+
const next = clone(def);
|
|
144
|
+
const list = listOf(next.steps, at.list);
|
|
145
|
+
if (!list) return def;
|
|
146
|
+
const to = at.index + by;
|
|
147
|
+
if (to < 0 || to >= list.length) return def;
|
|
148
|
+
const [moved] = list.splice(at.index, 1);
|
|
149
|
+
list.splice(to, 0, moved);
|
|
150
|
+
return next;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** A copy of a step (and what is inside it) right after it, with fresh
|
|
154
|
+
* ids. → { def, id } or null. */
|
|
155
|
+
export function duplicateStep(def, id) {
|
|
156
|
+
const at = locate(def.steps, id);
|
|
157
|
+
const step = findStep(def.steps, id);
|
|
158
|
+
if (!at || !step) return null;
|
|
159
|
+
const taken = allStepIds(def.steps);
|
|
160
|
+
const copy = clone(step);
|
|
161
|
+
const rename = (s) => {
|
|
162
|
+
const base = s.id.replace(/_\d+$/, "");
|
|
163
|
+
let n = 2;
|
|
164
|
+
let fresh = `${base}_${n}`;
|
|
165
|
+
while (taken.has(fresh)) fresh = `${base}_${(n += 1)}`;
|
|
166
|
+
taken.add(fresh);
|
|
167
|
+
s.id = fresh;
|
|
168
|
+
if (s.kind === "condition") {
|
|
169
|
+
(s.then ?? []).forEach(rename);
|
|
170
|
+
(s.else ?? []).forEach(rename);
|
|
171
|
+
} else if (s.kind === "loop") (s.steps ?? []).forEach(rename);
|
|
172
|
+
};
|
|
173
|
+
rename(copy);
|
|
174
|
+
return { def: insertStep(def, { list: at.list, index: at.index + 1 }, copy), id: copy.id };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** What a step may read: the steps that run before it (and, in a loop, the
|
|
178
|
+
* item). For the data picker. → { steps: [{ id, kind, label }], inLoop } */
|
|
179
|
+
export function availableBefore(def, id, titleOf) {
|
|
180
|
+
const out = [];
|
|
181
|
+
let inLoop = false;
|
|
182
|
+
const entry = (s) => ({ id: s.id, kind: s.kind, label: titleOf(s) });
|
|
183
|
+
const walk = (list, inside) => {
|
|
184
|
+
for (const s of list) {
|
|
185
|
+
if (s.id === id) {
|
|
186
|
+
inLoop = inside;
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
out.push(entry(s));
|
|
190
|
+
// A step inside a branch or a loop sees what ran before it there, and
|
|
191
|
+
// the step that holds it; never the sibling branch it did not take.
|
|
192
|
+
if (s.kind === "condition") {
|
|
193
|
+
const then = s.then ?? [];
|
|
194
|
+
const other = s.else ?? [];
|
|
195
|
+
if (findStep(then, id)) return walk(then, inside);
|
|
196
|
+
if (findStep(other, id)) return walk(other, inside);
|
|
197
|
+
} else if (s.kind === "loop" && findStep(s.steps ?? [], id)) return walk(s.steps ?? [], true);
|
|
198
|
+
}
|
|
199
|
+
return false;
|
|
200
|
+
};
|
|
201
|
+
walk(def.steps ?? [], false);
|
|
202
|
+
return { steps: out, inLoop };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** A key from a name: `Morning read` becomes `morning-read`. */
|
|
206
|
+
export function keyFromName(name, taken = new Set()) {
|
|
207
|
+
let base = String(name ?? "")
|
|
208
|
+
.toLowerCase()
|
|
209
|
+
.normalize("NFKD")
|
|
210
|
+
.replace(/[̀-ͯ]/g, "")
|
|
211
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
212
|
+
.replace(/^-+|-+$/g, "")
|
|
213
|
+
.slice(0, 52);
|
|
214
|
+
if (!/^[a-z]/.test(base)) base = `a-${base}`.replace(/-+$/, "");
|
|
215
|
+
if (base === "a" || base === "") base = "automation";
|
|
216
|
+
if (!taken.has(base)) return base;
|
|
217
|
+
for (let n = 2; n < 1000; n += 1) if (!taken.has(`${base}-${n}`)) return `${base}-${n}`;
|
|
218
|
+
return `${base}-${taken.size + 1}`;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** An empty automation: a trigger and nothing else. */
|
|
222
|
+
export function blankAutomation(name, key, trigger = { type: "manual" }) {
|
|
223
|
+
return {
|
|
224
|
+
format: 1,
|
|
225
|
+
key,
|
|
226
|
+
name,
|
|
227
|
+
trigger,
|
|
228
|
+
inputs: {},
|
|
229
|
+
settings: { overlap: "skip", on_failure: "retry_then_agent", only_if_changed: false },
|
|
230
|
+
steps: [],
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** The step a problem's path points into, for the red mark on its card:
|
|
235
|
+
* `steps[2].then[0].input.since` becomes the step at that place. A path
|
|
236
|
+
* that is not inside a step (`trigger.cron`, `settings`) answers null. */
|
|
237
|
+
export function stepIdForPath(def, path) {
|
|
238
|
+
const segs = [...String(path ?? "").matchAll(/(steps|then|else)\[(\d+)\]/g)].map((m) => ({ key: m[1], idx: Number(m[2]) }));
|
|
239
|
+
if (segs.length === 0 || segs[0].key !== "steps") return null;
|
|
240
|
+
let step = def.steps?.[segs[0].idx];
|
|
241
|
+
for (const seg of segs.slice(1)) {
|
|
242
|
+
if (!step) break;
|
|
243
|
+
let list;
|
|
244
|
+
if (step.kind === "condition" && (seg.key === "then" || seg.key === "else")) list = step[seg.key] ?? [];
|
|
245
|
+
else if (step.kind === "loop" && seg.key === "steps") list = step.steps;
|
|
246
|
+
const next = list?.[seg.idx];
|
|
247
|
+
if (!next) break;
|
|
248
|
+
step = next;
|
|
249
|
+
}
|
|
250
|
+
return step?.id ?? null;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** A definition as text with its keys in order, so two that read the same
|
|
254
|
+
* are equal whatever order they came in. */
|
|
255
|
+
export function canonicalJson(value) {
|
|
256
|
+
const sort = (v) => {
|
|
257
|
+
if (Array.isArray(v)) return v.map(sort);
|
|
258
|
+
if (v && typeof v === "object") return Object.fromEntries(Object.keys(v).sort().map((k) => [k, sort(v[k])]));
|
|
259
|
+
return v;
|
|
260
|
+
};
|
|
261
|
+
return JSON.stringify(sort(value));
|
|
262
|
+
}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
// The agent's fixes and a person's run controls, in words and in rules.
|
|
2
|
+
//
|
|
3
|
+
// Pure; no React. Three questions the screens ask:
|
|
4
|
+
//
|
|
5
|
+
// draftOfStep(fixes, stepId) is there a change for next time, drafted
|
|
6
|
+
// for this step, and has a person decided?
|
|
7
|
+
// changeLines(patch, def) what a drafted change would touch, by step
|
|
8
|
+
// controlsOf(run, rows, opts) which controls a run offers right now
|
|
9
|
+
// retryFromOf(run, rows, def, stepId)
|
|
10
|
+
// whether "Retry from this step" shows for a
|
|
11
|
+
// step, where it starts, and whether it asks
|
|
12
|
+
//
|
|
13
|
+
// A run's `fixes` are the fixer's attempts (GET automation-runs/<id>):
|
|
14
|
+
// [{ id, step_path, step_id, attempt, status, credits, drafts: [{ id, why,
|
|
15
|
+
// status: proposed | kept | dropped, base_version, new_version }] }].
|
|
16
|
+
// A drafted change is `{ changes: [{ id: <step id>, input?, values?, retry?,
|
|
17
|
+
// prompt?, system?, query? }] }`.
|
|
18
|
+
//
|
|
19
|
+
// And the versions of one automation, for a screen that shares the draft with
|
|
20
|
+
// the agent: which version is the newest, whether the draft is the agent's and
|
|
21
|
+
// what it said it did, what changed between two definitions, and the words of
|
|
22
|
+
// "a newer version was saved while you were editing".
|
|
23
|
+
|
|
24
|
+
import { findStep } from "./edit";
|
|
25
|
+
import { stepTitle } from "./catalog";
|
|
26
|
+
import { latestRows } from "./overlay";
|
|
27
|
+
import { ACTIVE, triggerWords } from "./words";
|
|
28
|
+
|
|
29
|
+
/** The two words a card wears for a step that was fixed or changed:
|
|
30
|
+
* "The agent fixed this" or, for a person's own edit, "You changed its data". */
|
|
31
|
+
export function fixTag(fix) {
|
|
32
|
+
if (!fix) return "";
|
|
33
|
+
if (fix.by === "member") return fix.kind === "skip" ? "You skipped it" : "You changed its data";
|
|
34
|
+
return "The agent fixed this";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** The newest draft the fixer made for a step id (any pass of it), or null. */
|
|
38
|
+
export function draftOfStep(fixes, stepId) {
|
|
39
|
+
let found = null;
|
|
40
|
+
for (const f of fixes ?? []) {
|
|
41
|
+
if (f.step_id !== stepId) continue;
|
|
42
|
+
for (const d of f.drafts ?? []) found = d;
|
|
43
|
+
}
|
|
44
|
+
return found;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const PARTS = { retry: "how it tries again", prompt: "the words it asks with", system: "its instructions", query: "what it searches for" };
|
|
48
|
+
|
|
49
|
+
/** What a drafted change touches: [{ stepId, title, what }], one line per
|
|
50
|
+
* step. A value is named by its key, never shown. */
|
|
51
|
+
export function changeLines(patch, def) {
|
|
52
|
+
const out = [];
|
|
53
|
+
for (const change of Array.isArray(patch?.changes) ? patch.changes : []) {
|
|
54
|
+
if (!change || typeof change.id !== "string") continue;
|
|
55
|
+
const step = def ? findStep(def.steps, change.id) : null;
|
|
56
|
+
const parts = [];
|
|
57
|
+
for (const field of ["input", "values"]) {
|
|
58
|
+
const v = change[field];
|
|
59
|
+
if (v && typeof v === "object") parts.push(...Object.keys(v).map((k) => `“${k}”`));
|
|
60
|
+
}
|
|
61
|
+
for (const [key, words] of Object.entries(PARTS)) if (change[key] !== undefined) parts.push(words);
|
|
62
|
+
out.push({ stepId: change.id, title: step ? stepTitle(step) : change.id, what: parts.length ? parts.join(", ") : "its settings" });
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** The step a parked run is stuck at: the last that failed or whose send is
|
|
68
|
+
* not known to have gone. */
|
|
69
|
+
function blockedRow(rows) {
|
|
70
|
+
let found = null;
|
|
71
|
+
for (const r of latestRows(rows)) if (r.status === "failed" || r.status === "unknown") found = r;
|
|
72
|
+
return found;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** The ways a run can be limited by money: only the run's own limit can be
|
|
76
|
+
* raised from here; the wallet, the owner's cap and the app's weekly
|
|
77
|
+
* allowance are not this screen's to lift. */
|
|
78
|
+
const RAISABLE = "run_cap";
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Which controls a run offers right now.
|
|
82
|
+
*
|
|
83
|
+
* run { status, paused?, cap_credits?, error? }
|
|
84
|
+
* rows the run's step rows
|
|
85
|
+
* opts { stepsVisible }: the member may read a step's data (so may edit it)
|
|
86
|
+
*
|
|
87
|
+
* → { state: ended | active | paused | parked, pause, resume, stop, retry,
|
|
88
|
+
* skip, edit, raise, blocked: { path, stepId, unknownSend } | null,
|
|
89
|
+
* raiseDefault }
|
|
90
|
+
*/
|
|
91
|
+
export function controlsOf(run, rows, { stepsVisible = false } = {}) {
|
|
92
|
+
const off = { state: "ended", pause: false, resume: false, stop: false, retry: false, skip: false, edit: false, raise: false, blocked: null, raiseDefault: 0 };
|
|
93
|
+
if (!run || !ACTIVE.has(run.status)) return off;
|
|
94
|
+
const paused = Boolean(run.paused);
|
|
95
|
+
const parked = run.status === "needs_you";
|
|
96
|
+
const row = parked ? blockedRow(rows) : null;
|
|
97
|
+
const blocked = row ? { path: row.path, stepId: row.step_id, unknownSend: row.status === "unknown" } : null;
|
|
98
|
+
const cap = Number(run.cap_credits) || 0;
|
|
99
|
+
return {
|
|
100
|
+
state: paused ? "paused" : parked ? "parked" : "active",
|
|
101
|
+
pause: !paused && !parked,
|
|
102
|
+
resume: paused || parked,
|
|
103
|
+
stop: true,
|
|
104
|
+
retry: Boolean(blocked),
|
|
105
|
+
skip: Boolean(blocked),
|
|
106
|
+
edit: Boolean(blocked) && stepsVisible && !blocked.unknownSend,
|
|
107
|
+
raise: parked && run.error?.code === RAISABLE,
|
|
108
|
+
blocked,
|
|
109
|
+
raiseDefault: cap > 0 ? Math.max(10, Math.ceil(cap / 2)) : 100,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const OVER = new Set(["done", "failed", "stopped"]);
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* "Retry from this step": start a run over at a step, running it and every
|
|
117
|
+
* step after it again. It is offered for a step that ran, in a run that is
|
|
118
|
+
* over (done, failed, stopped) or parked on its owner; never in one that is
|
|
119
|
+
* going or paused, and never in a run the app's own job runner started.
|
|
120
|
+
*
|
|
121
|
+
* run { status, trigger_key? }
|
|
122
|
+
* rows the run's step rows
|
|
123
|
+
* def the definition the run used
|
|
124
|
+
* stepId the step the member is looking at
|
|
125
|
+
*
|
|
126
|
+
* → { show, path, title, inLoop, loopTitle, sent }
|
|
127
|
+
* path the step to start from, at the TOP level of the flow: a step
|
|
128
|
+
* inside a loop or a branch starts over from the loop or branch
|
|
129
|
+
* that holds it (the platform takes the top-level step's id)
|
|
130
|
+
* inLoop the step sits inside another step, so `path` is that step
|
|
131
|
+
* loopTitle that holding step's name, for the words
|
|
132
|
+
* sent a step from there on is known to have sent something, so ask
|
|
133
|
+
* first; the platform asks too (`needs_confirm_resend`) when this
|
|
134
|
+
* cannot tell
|
|
135
|
+
*/
|
|
136
|
+
export function retryFromOf(run, rows, def, stepId) {
|
|
137
|
+
const off = { show: false, path: null, title: null, inLoop: false, loopTitle: null, sent: false };
|
|
138
|
+
if (!run || !def || !Array.isArray(def.steps) || !stepId) return off;
|
|
139
|
+
if (!OVER.has(run.status) && run.status !== "needs_you") return off;
|
|
140
|
+
if (run.paused || (typeof run.trigger_key === "string" && run.trigger_key.startsWith("brief:"))) return off;
|
|
141
|
+
const at = def.steps.findIndex((top) => top.id === stepId || findStep([top], stepId));
|
|
142
|
+
if (at < 0) return off;
|
|
143
|
+
const top = def.steps[at];
|
|
144
|
+
const latest = latestRows(rows);
|
|
145
|
+
// Only a step that ran can be started over from, and the platform starts at
|
|
146
|
+
// the top-level step's own place.
|
|
147
|
+
if (!latest.some((r) => r.step_id === stepId) || !latest.some((r) => r.path === top.id)) return off;
|
|
148
|
+
const index = new Map();
|
|
149
|
+
def.steps.forEach((s, i) => {
|
|
150
|
+
index.set(s.id, i);
|
|
151
|
+
for (const inner of nestedIds(s)) index.set(inner, i);
|
|
152
|
+
});
|
|
153
|
+
const sent = latest.some((r) => (index.get(r.step_id) ?? -1) >= at && (r.status === "done" || r.status === "unknown") && (r.status === "unknown" || r.effect === "send" || r.fix?.effect === "send"));
|
|
154
|
+
const inLoop = top.id !== stepId;
|
|
155
|
+
return { show: true, path: top.id, title: stepTitle(findStep([top], stepId)), inLoop, loopTitle: inLoop ? stepTitle(top) : null, sent };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function nestedIds(step) {
|
|
159
|
+
const out = [];
|
|
160
|
+
for (const list of [step.steps, step.then, step.else]) {
|
|
161
|
+
for (const s of Array.isArray(list) ? list : []) out.push(s.id, ...nestedIds(s));
|
|
162
|
+
}
|
|
163
|
+
return out;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** The words of "Retry from this step". */
|
|
167
|
+
export const RETRY_FROM = {
|
|
168
|
+
button: "Retry from this step",
|
|
169
|
+
what: "Runs this step and every step after it again. The steps before it keep what they made.",
|
|
170
|
+
inLoop: (title) => `This step is inside “${title}”, so it starts over from there.`,
|
|
171
|
+
ask: "It already sent something. Run it again from here?",
|
|
172
|
+
yes: "Run it again",
|
|
173
|
+
no: "Cancel",
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/** A control's answer in words: what it did, or why it could not. */
|
|
177
|
+
export const CONTROL_DONE = {
|
|
178
|
+
pause: "Paused. Nothing new starts until you resume it.",
|
|
179
|
+
resume: "Resumed.",
|
|
180
|
+
stop: "Stopped.",
|
|
181
|
+
retry: "Trying that step again.",
|
|
182
|
+
skip: "Skipped that step.",
|
|
183
|
+
edit: "Trying that step again with your data.",
|
|
184
|
+
retry_from: "Starting over from that step.",
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
/** The platform's refusal codes, for when it sends no words of its own. */
|
|
188
|
+
export const CONTROL_REFUSED = {
|
|
189
|
+
not_found: "That run is gone.",
|
|
190
|
+
not_active: "This run has already ended.",
|
|
191
|
+
not_blocked: "Nothing is stuck right now.",
|
|
192
|
+
running: "A step is running. Try again in a moment.",
|
|
193
|
+
needs_confirm: "Say whether it went first.",
|
|
194
|
+
unknown_step: "That step is not part of this run.",
|
|
195
|
+
output_needed: "A later step uses what this one makes, so it cannot be skipped.",
|
|
196
|
+
bad_request: "That did not work. Check what you typed.",
|
|
197
|
+
bad_step: "That step has not run in this run, so there is nothing to start over from.",
|
|
198
|
+
busy: "This automation is already running. Let that one finish, or stop it, then start over here.",
|
|
199
|
+
not_controllable: "This run was started by the app's own job runner, so it cannot be changed here.",
|
|
200
|
+
needs_confirm_resend: "A step from here on already sent something. Confirm that is right, or start over from a later step.",
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
/* ---- the versions, and the agent's draft ------------------------------------ */
|
|
204
|
+
|
|
205
|
+
/** The newest version number: the highest in `versions`, else the draft's,
|
|
206
|
+
* else the live one's. Null when there is none (an automation not saved). */
|
|
207
|
+
export function latestVersionOf({ versions, draftVersion, liveVersion } = {}) {
|
|
208
|
+
let top = null;
|
|
209
|
+
for (const v of Array.isArray(versions) ? versions : []) {
|
|
210
|
+
const n = Number(v?.version);
|
|
211
|
+
if (Number.isInteger(n) && (top === null || n > top)) top = n;
|
|
212
|
+
}
|
|
213
|
+
if (top !== null) return top;
|
|
214
|
+
for (const n of [draftVersion, liveVersion]) if (Number.isInteger(n) && (top === null || n > top)) top = n;
|
|
215
|
+
return top;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Whether a version row was written by the agent. */
|
|
219
|
+
const byAgent = (v) => Boolean(v) && (v.kind === "agent" || v.author === "agent");
|
|
220
|
+
|
|
221
|
+
/** The note on an agent's version, said as a person would: "also copy Owen".
|
|
222
|
+
* The agent files it as "Changed: also copy Owen"; the prefix goes. */
|
|
223
|
+
function noteWords(note) {
|
|
224
|
+
const text = typeof note === "string" ? note.trim().replace(/^changed:\s*/i, "").replace(/^changed by the agent\.?$/i, "").trim() : "";
|
|
225
|
+
return text.length > 160 ? `${text.slice(0, 159).trimEnd()}…` : text;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* The draft, when the agent wrote it and it is not live yet: `{ version,
|
|
230
|
+
* note }` (the note may be ""), else null. The editor draws "Drawn by the
|
|
231
|
+
* agent · from: <note>" under its header while this is not null.
|
|
232
|
+
*/
|
|
233
|
+
export function agentDraftOf({ versions, draftVersion, liveVersion } = {}) {
|
|
234
|
+
if (!Number.isInteger(draftVersion) || draftVersion === liveVersion) return null;
|
|
235
|
+
const row = (Array.isArray(versions) ? versions : []).find((v) => v?.version === draftVersion);
|
|
236
|
+
return byAgent(row) ? { version: draftVersion, note: noteWords(row.note) } : null;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* The words over "a newer version arrived while you have changes".
|
|
241
|
+
* version the newer version's number
|
|
242
|
+
* versions the version rows, to see who wrote it
|
|
243
|
+
*/
|
|
244
|
+
export function newerVersionWords(version, versions) {
|
|
245
|
+
const row = (Array.isArray(versions) ? versions : []).find((v) => v?.version === version);
|
|
246
|
+
return byAgent(row)
|
|
247
|
+
? `The agent drew v${version} from your last save. You have changes that are not saved.`
|
|
248
|
+
: `Version ${version} was saved after you opened this. You have changes that are not saved.`;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** Every step in a list with its nested ones, by id. */
|
|
252
|
+
function indexSteps(steps, into = new Map()) {
|
|
253
|
+
for (const s of Array.isArray(steps) ? steps : []) {
|
|
254
|
+
into.set(s.id, s);
|
|
255
|
+
indexSteps(s.then, into);
|
|
256
|
+
indexSteps(s.else, into);
|
|
257
|
+
indexSteps(s.steps, into);
|
|
258
|
+
}
|
|
259
|
+
return into;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** A step without the steps inside it: a container's own change is its own fields. */
|
|
263
|
+
const bare = (s) => (s.kind === "condition" ? { ...s, then: undefined, else: undefined } : s.kind === "loop" ? { ...s, steps: undefined } : s);
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* What changed between two definitions, one plain line each, at most 20:
|
|
267
|
+
* how it starts, its name, a step added, removed or changed (by its title),
|
|
268
|
+
* its settings. Pure; a value is never shown, only what it touched. The
|
|
269
|
+
* platform's agent writes the same lines for itself (agent-app
|
|
270
|
+
* automation-verbs `changeLines`); this is the screen's copy of it.
|
|
271
|
+
*/
|
|
272
|
+
export function changeLinesBetween(before, after) {
|
|
273
|
+
if (!before || !after) return [];
|
|
274
|
+
const a = indexSteps(before.steps);
|
|
275
|
+
const b = indexSteps(after.steps);
|
|
276
|
+
const out = [];
|
|
277
|
+
if (JSON.stringify(before.trigger ?? null) !== JSON.stringify(after.trigger ?? null)) out.push(`Starts: ${triggerWords(before.trigger)} → ${triggerWords(after.trigger)}`);
|
|
278
|
+
if ((before.name ?? "") !== (after.name ?? "")) out.push(`Renamed to “${after.name ?? ""}”`);
|
|
279
|
+
for (const [id, s] of b) if (!a.has(id)) out.push(`Added “${stepTitle(s)}”`);
|
|
280
|
+
for (const [id, s] of a) if (!b.has(id)) out.push(`Removed “${stepTitle(s)}”`);
|
|
281
|
+
for (const [id, s] of b) {
|
|
282
|
+
const was = a.get(id);
|
|
283
|
+
if (was && JSON.stringify(bare(was)) !== JSON.stringify(bare(s))) out.push(`Changed “${stepTitle(s)}”`);
|
|
284
|
+
}
|
|
285
|
+
if (JSON.stringify(before.settings ?? null) !== JSON.stringify(after.settings ?? null)) out.push("Changed its settings");
|
|
286
|
+
return out.slice(0, 20);
|
|
287
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Icon } from "../../ui";
|
|
4
|
+
import { GLYPH_ICON } from "./catalog";
|
|
5
|
+
|
|
6
|
+
// The face of a step kind, drawn with the kit's own icons. One table
|
|
7
|
+
// (catalog.js GLYPH_ICON): the canvas, the picker, the outline and the
|
|
8
|
+
// settings panel all ask it, so a kind wears one face everywhere.
|
|
9
|
+
|
|
10
|
+
/** name: a glyph from the catalog ("action", "condition", "trigger-schedule" …). */
|
|
11
|
+
export function Glyph({ name, size = 22, className }) {
|
|
12
|
+
return <Icon name={GLYPH_ICON[name] ?? "zap"} size={size} className={className} />;
|
|
13
|
+
}
|