makersclaw 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -4
- 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 +4 -4
- 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 +5 -5
- package/r/chat-pane.json +84 -0
- package/r/chat.json +3 -3
- 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 +18 -6
- 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 +3 -3
- package/r/flow-editor.json +79 -0
- package/r/footer.json +3 -3
- 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 +4 -4
- 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 +4 -4
- package/r/pace-bar.json +69 -0
- package/r/page-builder.json +3 -3
- package/r/payment-methods.json +2 -2
- package/r/pdf-document.json +2 -2
- package/r/pdf-viewer.json +3 -3
- 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 +4089 -1510
- 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 +3 -3
- package/r/signature-pad.json +2 -2
- package/r/sketch-canvas.json +3 -3
- 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 +4 -4
- 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/annotate/README.md +14 -11
- 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/chart/cartesian.jsx +1 -1
- package/registry/blocks/chart/core.jsx +33 -23
- package/registry/blocks/chart/pie.jsx +1 -1
- package/registry/blocks/chat/README.md +4 -1
- package/registry/blocks/chat/demo.jsx +2 -2
- 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-canvas/README.md +15 -8
- 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/footer/README.md +3 -2
- 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/mind-map/README.md +4 -2
- 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/org-chart/README.md +4 -2
- 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/page-builder/README.md +1 -1
- package/registry/blocks/page-builder/demo.jsx +3 -3
- package/registry/blocks/pdf-viewer/README.md +4 -1
- 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/sidebar/README.md +23 -13
- package/registry/blocks/sketch-canvas/README.md +15 -9
- 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/whiteboard/README.md +20 -14
- 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/cli.mjs +2 -2
- package/src/install.mjs +100 -22
- package/src/registry.mjs +37 -3
- package/templates/next/browser/lib/data.js +6 -1
- package/templates/next/files/README.md +909 -26
- package/templates/next/files/app/shell.jsx +54 -37
- 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 +1043 -12
- 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 +3 -1
- package/templates/next/files/components/ui/layout.jsx +382 -0
- 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 +244 -5
- package/templates/next/files/lib/door.js +92 -0
- package/templates/next/files/lib/hooks.js +1694 -6
- package/templates/next/files/lib/platform.js +611 -14
- package/templates/next/template.json +22 -3
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// AutomationsScreen over a pretend platform — the gallery's and the smoke
|
|
4
|
+
// test's surface. Not installed into apps. The platform's hooks are written
|
|
5
|
+
// here, in memory, and handed to the block as `hooks`, exactly as an app hands
|
|
6
|
+
// its lib/hooks. A scenario picker draws each state: many automations, one,
|
|
7
|
+
// none (the first-run screen), still loading, the list failing to load, an
|
|
8
|
+
// older platform without the engine, an app whose job is still its old
|
|
9
|
+
// schedule (read-only), and an automation that will not open. A width picker
|
|
10
|
+
// shows the page at 1280, 800, 368 and 320 px of the block's own width.
|
|
11
|
+
//
|
|
12
|
+
// Try: open a row (Back returns to the list with its search kept), Test run
|
|
13
|
+
// then Back from the run, Runs then a run then Back (it returns to Runs),
|
|
14
|
+
// the app's own tab on Morning read, Delete a row (a toast with Undo),
|
|
15
|
+
// New automation from a starting point, the switch on a row. The line under
|
|
16
|
+
// the pickers is the page address the place would travel in.
|
|
17
|
+
import { useMemo, useState, useSyncExternalStore } from "react";
|
|
18
|
+
import { AppLayout, Button, ButtonGroup, Notice } from "../components/ui";
|
|
19
|
+
import { AutomationsScreen, paramsOfPlace, placeFromParams } from "../components/blocks/automations-screen";
|
|
20
|
+
|
|
21
|
+
const MORNING = {
|
|
22
|
+
format: 1,
|
|
23
|
+
key: "morning-read",
|
|
24
|
+
name: "Morning read",
|
|
25
|
+
does: "Reads your mail, saves who wrote, drafts the follow-ups that are owed.",
|
|
26
|
+
trigger: { type: "schedule", cron: "0 7 * * 1-5", tz: "team" },
|
|
27
|
+
inputs: {},
|
|
28
|
+
settings: { overlap: "skip", on_failure: "retry_then_agent", only_if_changed: false },
|
|
29
|
+
steps: [
|
|
30
|
+
{ id: "mail", kind: "tool", label: "Read the mail since the last read", needs: { category: "mail" }, tool: "list_messages", input: { since: "{{ trigger.at }}", limit: 200 } },
|
|
31
|
+
{ id: "touches", kind: "action", label: "Save who wrote", action: "record_touches", input: { touches: "{{ steps.mail.output.messages }}" } },
|
|
32
|
+
{ id: "write", kind: "llm", label: "Write the follow-up", tier: "smart", prompt: "Write a short follow-up to the last person who wrote." },
|
|
33
|
+
{ id: "tell", kind: "notify", message: "Morning read done." },
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
const simple = (key, name, trigger, message = "Done.") => ({ format: 1, key, name, trigger, inputs: {}, settings: { overlap: "skip", on_failure: "retry_then_agent", only_if_changed: false }, steps: [{ id: "tell", kind: "notify", message }] });
|
|
37
|
+
const FRIDAY = simple("friday-review", "Friday review", { type: "schedule", cron: "0 16 * * 5", tz: "team" }, "Friday review is ready.");
|
|
38
|
+
const LONG = simple("long", "Every Monday morning, collect every deal that has gone quiet for more than three weeks, draft a short note to each owner and leave all of it for the team lead to read before the stand-up", { type: "schedule", cron: "0 9 * * 1", tz: "team" });
|
|
39
|
+
|
|
40
|
+
const ACTIONS = [
|
|
41
|
+
{ name: "record_touches", description: "Save who wrote to whom" },
|
|
42
|
+
{ name: "suggest", description: "Leave a suggestion on Today" },
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
const T0 = "2026-10-09T11:00:00.000Z";
|
|
46
|
+
const auto = (id, def, over = {}) => ({ id, def, liveDef: def, status: "off", live: 1, draft: 1, source: "member", versions: [{ version: 1, kind: "member", created_at: T0 }], runs: [], suggestions: [], passed: null, ...over });
|
|
47
|
+
const SCENARIOS = {
|
|
48
|
+
many: () => ({
|
|
49
|
+
a1: auto("a1", MORNING, { status: "on", source: "package", versions: [{ version: 1, kind: "shipped", created_at: T0 }], runs: ["r1"], passed: 1 }),
|
|
50
|
+
a2: auto("a2", FRIDAY, { status: "on", runs: ["r2"], passed: 1 }),
|
|
51
|
+
a3: auto("a3", simple("weekly-signups", "Weekly signups", { type: "schedule", cron: "0 9 * * 1", tz: "team" })),
|
|
52
|
+
a4: auto("a4", simple("a-draft", "A draft I started", { type: "manual" }), { status: "draft", live: null }),
|
|
53
|
+
a5: auto("a5", LONG, { status: "on", passed: 1 }),
|
|
54
|
+
}),
|
|
55
|
+
one: () => ({ a1: auto("a1", MORNING, { status: "on", source: "package", versions: [{ version: 1, kind: "shipped", created_at: T0 }], runs: ["r1"], passed: 1 }) }),
|
|
56
|
+
none: () => ({}),
|
|
57
|
+
loading: () => ({}),
|
|
58
|
+
failed: () => ({}),
|
|
59
|
+
unavailable: () => ({}),
|
|
60
|
+
readonly: () => ({ a1: auto("a1", simple("morning", "Morning read", { type: "schedule", cron: "0 7 * * 1-5", tz: "team" }), { status: "on", source: "legacy" }), a2: auto("a2", simple("weekly-review", "Weekly look back", { type: "schedule", cron: "0 16 * * 5", tz: "team" }), { source: "legacy" }) }),
|
|
61
|
+
unopenable: () => ({ a1: auto("a1", MORNING, { status: "on" }), a2: auto("a2", FRIDAY) }),
|
|
62
|
+
};
|
|
63
|
+
const SCENARIO_LABELS = { many: "Many", one: "One", none: "None", loading: "Loading", failed: "Failed to load", unavailable: "No engine", readonly: "Read-only", unopenable: "Will not open" };
|
|
64
|
+
|
|
65
|
+
const row = (step_id, over = {}) => ({ path: step_id, step_id, kind: "notify", attempt: 1, status: "done", started_at: T0, ended_at: T0, credits: 0, error: null, ...over });
|
|
66
|
+
const PAST = {
|
|
67
|
+
r1: { id: "r1", status: "done", version: 1, trigger_type: "schedule", is_test: false, created_at: T0, started_at: T0, ended_at: "2026-10-09T11:01:00.000Z", credits: 4.2, cap_credits: 900, result: null, error: null, steps: [row("mail", { kind: "tool" }), row("touches", { kind: "action" }), row("write", { kind: "llm", credits: 4.2 }), row("tell")] },
|
|
68
|
+
r2: { id: "r2", status: "failed", version: 1, trigger_type: "schedule", is_test: false, created_at: T0, started_at: T0, ended_at: T0, credits: 0.8, result: null, error: { words: "The note could not be left." }, steps: [row("tell", { status: "failed", error: { code: "x", words: "The note could not be left." } })] },
|
|
69
|
+
};
|
|
70
|
+
const flat = (steps) => steps.flatMap((s) => [s, ...(s.then ?? []).flatMap((x) => flat([x])), ...(s.else ?? []).flatMap((x) => flat([x])), ...flat(s.steps ?? [])]);
|
|
71
|
+
|
|
72
|
+
/** A platform in memory: the three hooks and two doors the block asks for. */
|
|
73
|
+
function createPlatform(scenario) {
|
|
74
|
+
const state = { autos: SCENARIOS[scenario](), runs: { ...(scenario === "many" || scenario === "one" ? PAST : {}) }, deleted: {}, n: 0, tick: 0 };
|
|
75
|
+
const subs = new Set();
|
|
76
|
+
const bump = () => {
|
|
77
|
+
state.tick += 1;
|
|
78
|
+
subs.forEach((f) => f());
|
|
79
|
+
};
|
|
80
|
+
const useTick = () => useSyncExternalStore((f) => (subs.add(f), () => subs.delete(f)), () => state.tick, () => state.tick);
|
|
81
|
+
const rowOf = (a) => {
|
|
82
|
+
const last = a.runs.map((id) => state.runs[id]).filter(Boolean)[0];
|
|
83
|
+
return {
|
|
84
|
+
id: a.id,
|
|
85
|
+
key: a.def.key,
|
|
86
|
+
name: a.def.name,
|
|
87
|
+
status: a.status,
|
|
88
|
+
live_version: a.live,
|
|
89
|
+
draft_version: a.draft,
|
|
90
|
+
trigger: a.def.trigger,
|
|
91
|
+
tz: "America/New_York",
|
|
92
|
+
next_run_at: a.status === "on" && a.def.trigger.type === "schedule" ? "2026-10-12T11:00:00.000Z" : null,
|
|
93
|
+
last_run: last ? { status: last.status, started_at: last.created_at, credits: last.credits } : null,
|
|
94
|
+
active_runs: 0,
|
|
95
|
+
owner_kind: "app",
|
|
96
|
+
app_id: "crm",
|
|
97
|
+
edited: a.versions.some((v) => v.kind === "member"),
|
|
98
|
+
source: a.source,
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
const testView = (a, version) => (version === null || version === undefined ? null : { version, exempt: a.versions.some((v) => v.version === version && v.kind === "shipped"), passed: a.passed === version, last: null });
|
|
102
|
+
const needsTest = (a, version, opts) => !opts?.skipTest && !testView(a, version)?.exempt && a.passed !== version;
|
|
103
|
+
const REFUSED = { ok: false, status: 409, code: "needs_test", error: "Run a test of this version first. It reads your real data and changes nothing." };
|
|
104
|
+
const wait = (ms = 250) => new Promise((r) => setTimeout(r, ms));
|
|
105
|
+
|
|
106
|
+
const useAutomations = () => {
|
|
107
|
+
useTick();
|
|
108
|
+
if (scenario === "unavailable") return { automations: [], triggers: null, starts: [], describe: async () => ({ ok: false, code: "unavailable" }), loading: false, error: null, unavailable: true, reload() {}, setStatus: async () => ({ ok: false }), create: async () => ({ ok: false }) };
|
|
109
|
+
const loading = scenario === "loading";
|
|
110
|
+
const error = scenario === "failed" ? "not reachable" : null;
|
|
111
|
+
return {
|
|
112
|
+
automations: loading || error ? [] : Object.values(state.autos).map(rowOf),
|
|
113
|
+
triggers: null,
|
|
114
|
+
starts: [],
|
|
115
|
+
describe: async (ask) => {
|
|
116
|
+
await wait(600);
|
|
117
|
+
return ask.length < 24 ? { ok: false, code: "needs_answer", error: "I need a little more.", questions: ["Who should it tell?", "How often should it run?"] } : { ok: true, id: "a1" };
|
|
118
|
+
},
|
|
119
|
+
describing: false,
|
|
120
|
+
loading,
|
|
121
|
+
error,
|
|
122
|
+
code: undefined,
|
|
123
|
+
unavailable: false,
|
|
124
|
+
reload: bump,
|
|
125
|
+
setStatus: async (id, on) => {
|
|
126
|
+
await wait(150);
|
|
127
|
+
state.autos[id] = { ...state.autos[id], status: on ? "on" : "off" };
|
|
128
|
+
bump();
|
|
129
|
+
return { ok: true, status: 200, data: {} };
|
|
130
|
+
},
|
|
131
|
+
create: async (def) => {
|
|
132
|
+
const id = `n${++state.n}`;
|
|
133
|
+
state.autos[id] = auto(id, def, { status: "draft", live: null });
|
|
134
|
+
bump();
|
|
135
|
+
return { ok: true, status: 200, data: { id } };
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const useAutomation = (id) => {
|
|
141
|
+
useTick();
|
|
142
|
+
const a = state.autos[id];
|
|
143
|
+
if (scenario === "loading") return { automation: null, definition: null, versions: [], runs: [], suggestions: [], tests: null, hook: null, loading: true, error: null, unavailable: false, busy: null, reload: bump };
|
|
144
|
+
const dead = scenario === "unopenable" || !a;
|
|
145
|
+
if (dead) return { automation: null, definition: null, versions: [], runs: [], suggestions: [], tests: null, hook: null, loading: false, error: "That did not open.", code: a ? undefined : "not_found", unavailable: false, busy: null, reload: bump };
|
|
146
|
+
const set = (patch) => {
|
|
147
|
+
state.autos[id] = { ...state.autos[id], ...patch };
|
|
148
|
+
bump();
|
|
149
|
+
};
|
|
150
|
+
return {
|
|
151
|
+
automation: { ...rowOf(a), tz: "America/New_York" },
|
|
152
|
+
live: a.live ? { version: a.live, definition: a.liveDef } : null,
|
|
153
|
+
draft: a.draft !== a.live ? { version: a.draft, definition: a.def } : null,
|
|
154
|
+
definition: a.def,
|
|
155
|
+
versions: [...a.versions].reverse(),
|
|
156
|
+
estimate: { credits: 3, credits_max: 12, steps: [] },
|
|
157
|
+
tests: { live: testView(a, a.live), draft: a.draft !== a.live ? testView(a, a.draft) : null },
|
|
158
|
+
runs: a.runs.map((r) => state.runs[r]).filter(Boolean),
|
|
159
|
+
suggestions: a.suggestions,
|
|
160
|
+
keepSuggestion: async () => ({ ok: true }),
|
|
161
|
+
dropSuggestion: async () => ({ ok: true }),
|
|
162
|
+
hook: null,
|
|
163
|
+
rotateHook: async () => ({ ok: true, hook: null }),
|
|
164
|
+
busy: null,
|
|
165
|
+
save: async (def, how) => {
|
|
166
|
+
await wait(150);
|
|
167
|
+
const latest = a.versions.length;
|
|
168
|
+
const base = how && typeof how === "object" ? how.baseVersion : latest;
|
|
169
|
+
if (base !== latest) return { ok: false, status: 409, code: "changed", latest, error: "Someone saved a newer version while you were editing." };
|
|
170
|
+
set({ def, draft: latest + 1, versions: [...a.versions, { version: latest + 1, kind: "member", created_at: new Date().toISOString() }] });
|
|
171
|
+
return { ok: true, status: 200, data: { version: latest + 1 } };
|
|
172
|
+
},
|
|
173
|
+
keep: async (o) => {
|
|
174
|
+
if (needsTest(a, a.draft, o)) return REFUSED;
|
|
175
|
+
set({ live: a.draft, liveDef: a.def });
|
|
176
|
+
return { ok: true, status: 200, data: {} };
|
|
177
|
+
},
|
|
178
|
+
setLive: async (on, o) => {
|
|
179
|
+
if (on && needsTest(a, a.live ?? a.draft, o)) return REFUSED;
|
|
180
|
+
set({ status: on ? "on" : "off" });
|
|
181
|
+
return { ok: true, status: 200, data: {} };
|
|
182
|
+
},
|
|
183
|
+
test: async (given = {}) => {
|
|
184
|
+
await wait(300);
|
|
185
|
+
const dry = !(given && given.dry === false);
|
|
186
|
+
const id2 = `t${++state.n}`;
|
|
187
|
+
const t = new Date().toISOString();
|
|
188
|
+
const standIn = new Set(["action", "notify"]);
|
|
189
|
+
const steps = flat(a.def.steps).map((s) => {
|
|
190
|
+
const would = dry && standIn.has(s.kind);
|
|
191
|
+
return row(s.id, { kind: s.kind, started_at: t, ended_at: t, credits: s.kind === "llm" ? 3 : 0, ...(would ? { 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 } }) });
|
|
192
|
+
});
|
|
193
|
+
const woulds = steps.filter((x) => x.simulated);
|
|
194
|
+
state.runs[id2] = { id: id2, status: "done", dry, version: a.draft, trigger_type: "manual", is_test: dry, created_at: t, started_at: t, ended_at: t, credits: 3, result: dry ? { dry: { simulated: woulds.length, would: woulds.map((x) => ({ step: x.step_id, path: x.path, kind: x.kind, effect: x.output.would.effect, words: x.output.would.words })), unchecked: [], sampled: [] } } : null, error: null, steps, automation_id: id };
|
|
195
|
+
set({ runs: [id2, ...a.runs], passed: a.draft });
|
|
196
|
+
return { ok: true, status: 200, data: { run_id: id2, test: dry, dry, version: a.draft } };
|
|
197
|
+
},
|
|
198
|
+
loading: false,
|
|
199
|
+
error: null,
|
|
200
|
+
code: undefined,
|
|
201
|
+
unavailable: false,
|
|
202
|
+
reload: bump,
|
|
203
|
+
};
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const useAutomationRun = (runId) => {
|
|
207
|
+
useTick();
|
|
208
|
+
const r = runId ? state.runs[runId] : null;
|
|
209
|
+
const none = { run: null, steps: [], stepsVisible: false, automation: null, definition: null, fixes: [], detail: null, control: async () => ({ ok: true }), keepFix: async () => ({ ok: true }), dropFix: async () => ({ ok: true }), busy: null, loading: false, error: null, unavailable: false, reload: bump };
|
|
210
|
+
if (!r) return runId ? { ...none, error: "gone" } : none;
|
|
211
|
+
return { ...none, run: r, steps: r.steps, stepsVisible: true, detail: { run: r, automation: { name: "" }, steps: r.steps, steps_visible: true, fixes: [] } };
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const deleteAutomation = async (id) => {
|
|
215
|
+
await wait(200);
|
|
216
|
+
const a = state.autos[id];
|
|
217
|
+
if (!a) return { ok: false, status: 404, code: "not_found", error: "That automation is not there any more." };
|
|
218
|
+
if (a.source === "package" || a.source === "legacy") return { ok: false, status: 409, code: a.source, error: "This one came with the app. Switch it off in the app's settings instead." };
|
|
219
|
+
state.deleted[id] = a;
|
|
220
|
+
delete state.autos[id];
|
|
221
|
+
bump();
|
|
222
|
+
return { ok: true, status: 200, data: { ok: true, id, name: a.def.name, was_status: a.status, followers: id === "a2" ? [{ id: "a3", key: "weekly-signups", name: "Weekly signups" }] : [], undo: true } };
|
|
223
|
+
};
|
|
224
|
+
const restoreAutomation = async (id) => {
|
|
225
|
+
await wait(150);
|
|
226
|
+
if (!state.deleted[id]) return { ok: false, status: 404, code: "not_found", error: "It could not be brought back." };
|
|
227
|
+
state.autos[id] = state.deleted[id];
|
|
228
|
+
delete state.deleted[id];
|
|
229
|
+
bump();
|
|
230
|
+
return { ok: true, status: 200, data: { ok: true, id } };
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
return { useAutomations, useAutomation, useAutomationRun, deleteAutomation, restoreAutomation };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const WIDTHS = [1280, 800, 368, 320];
|
|
237
|
+
const CHARGE = { many: { used: 4180, of: 10000 }, one: { used: 0, of: 10000 } };
|
|
238
|
+
|
|
239
|
+
function Page({ scenario }) {
|
|
240
|
+
const hooks = useMemo(() => createPlatform(scenario), [scenario]);
|
|
241
|
+
const [place, setPlace] = useState(() => placeFromParams(null));
|
|
242
|
+
const [asked, setAsked] = useState(null);
|
|
243
|
+
return (
|
|
244
|
+
<div className="flex h-full min-h-0 flex-col">
|
|
245
|
+
<p className="m-0 shrink-0 border-b border-border bg-muted px-3 py-1.5 text-xs text-muted-foreground" role="status">
|
|
246
|
+
Page address: <b className="font-semibold text-foreground">{`?${new URLSearchParams({ view: "automations", ...paramsOfPlace(place) })}`}</b>
|
|
247
|
+
{asked ? ` · Describe it handed on: “${asked}”` : ""}
|
|
248
|
+
</p>
|
|
249
|
+
<div className="flex min-h-0 flex-1 flex-col">
|
|
250
|
+
<AutomationsScreen
|
|
251
|
+
key={scenario}
|
|
252
|
+
hooks={hooks}
|
|
253
|
+
place={place}
|
|
254
|
+
onPlace={setPlace}
|
|
255
|
+
actions={ACTIONS}
|
|
256
|
+
title="Automations"
|
|
257
|
+
intro="What the CRM does on its own. Open one to see its steps, change them, test it and switch it on."
|
|
258
|
+
allowance={CHARGE[scenario] ? { ...CHARGE[scenario], words: CHARGE[scenario].used > 0 ? "4,180 of 10,000 credits this week" : "10,000 credits a week" } : null}
|
|
259
|
+
starts={[
|
|
260
|
+
{
|
|
261
|
+
id: "quiet-monday",
|
|
262
|
+
name: "Look at who has gone quiet every Monday",
|
|
263
|
+
blurb: "The agent drafts a short follow-up for the people on open deals who have gone quiet. Nothing is sent.",
|
|
264
|
+
glyph: "agent",
|
|
265
|
+
needs: null,
|
|
266
|
+
build: (taken) => ({ ...simple(taken.has("quiet-monday") ? "quiet-monday-2" : "quiet-monday", "Who has gone quiet", { type: "schedule", cron: "30 8 * * 1", tz: "team" }, "Drafted the follow-ups."), does: "Drafts follow-ups for people who have gone quiet." }),
|
|
267
|
+
},
|
|
268
|
+
]}
|
|
269
|
+
onDescribe={scenario === "one" ? (ask) => (setAsked(ask), Promise.resolve({ ok: true })) : "platform"}
|
|
270
|
+
describePrice=""
|
|
271
|
+
onRequest={
|
|
272
|
+
<section aria-labelledby="demo-on-request" className="grid gap-2">
|
|
273
|
+
<h2 id="demo-on-request" className="m-0 text-[13px] font-semibold">
|
|
274
|
+
On request
|
|
275
|
+
</h2>
|
|
276
|
+
<ul className="m-0 grid list-none rounded-md border border-border p-0">
|
|
277
|
+
{[
|
|
278
|
+
["The first read", "Once, when a mailbox connects: the last 30 days of mail and meetings."],
|
|
279
|
+
["Fill in", "When someone presses Fill in on a person, a company or a list's column."],
|
|
280
|
+
].map(([name, when]) => (
|
|
281
|
+
<li key={name} className="grid gap-0.5 border-b border-border px-3.5 py-2.5 text-[13px] last:border-b-0">
|
|
282
|
+
<b className="font-semibold">{name}</b>
|
|
283
|
+
<span className="text-muted-foreground">{when}</span>
|
|
284
|
+
</li>
|
|
285
|
+
))}
|
|
286
|
+
</ul>
|
|
287
|
+
</section>
|
|
288
|
+
}
|
|
289
|
+
extras={({ automations }) => <p className="m-0 text-xs text-muted-foreground">{automations.length} of the app's own automations are drawn above.</p>}
|
|
290
|
+
extraTabs={[
|
|
291
|
+
{
|
|
292
|
+
key: "reads",
|
|
293
|
+
label: "What it reads",
|
|
294
|
+
when: (a) => a.key === "morning-read",
|
|
295
|
+
render: () => (
|
|
296
|
+
<div className="grid max-w-3xl gap-1.5 p-5 text-[13px]">
|
|
297
|
+
<b className="font-semibold">Mail</b>
|
|
298
|
+
<p className="m-0 text-muted-foreground">For each email: who wrote to whom, when, the subject and one line. Never the full text.</p>
|
|
299
|
+
</div>
|
|
300
|
+
),
|
|
301
|
+
},
|
|
302
|
+
]}
|
|
303
|
+
aboveOne={({ automation, runId }) => (automation.key === "morning-read" && !runId ? <Notice tone="info">It needs a mailbox. Connect one on the desk.</Notice> : null)}
|
|
304
|
+
afterRun={() => <p className="m-0 shrink-0 border-t border-border px-4 py-2 text-xs text-muted-foreground">The app's own account of this run sits here.</p>}
|
|
305
|
+
frame={({ crumb, label, fill, children }) => (
|
|
306
|
+
<AppLayout
|
|
307
|
+
height="parent"
|
|
308
|
+
contentLabel={label}
|
|
309
|
+
contentMode={fill ? "fill" : "scroll"}
|
|
310
|
+
header={
|
|
311
|
+
<nav aria-label="Where you are" className="flex min-h-11 min-w-0 items-center gap-1 px-3 text-sm">
|
|
312
|
+
{crumb.map((c, i) =>
|
|
313
|
+
c.onPress ? (
|
|
314
|
+
<span key={c.label} className="flex items-center gap-1">
|
|
315
|
+
<Button size="sm" variant="quiet" onClick={c.onPress}>
|
|
316
|
+
{c.label}
|
|
317
|
+
</Button>
|
|
318
|
+
<span aria-hidden="true" className="text-muted-foreground">
|
|
319
|
+
›
|
|
320
|
+
</span>
|
|
321
|
+
</span>
|
|
322
|
+
) : (
|
|
323
|
+
<b key={`${c.label}-${i}`} className="min-w-0 truncate font-semibold">
|
|
324
|
+
{c.label}
|
|
325
|
+
</b>
|
|
326
|
+
),
|
|
327
|
+
)}
|
|
328
|
+
</nav>
|
|
329
|
+
}
|
|
330
|
+
>
|
|
331
|
+
{children}
|
|
332
|
+
</AppLayout>
|
|
333
|
+
)}
|
|
334
|
+
/>
|
|
335
|
+
</div>
|
|
336
|
+
</div>
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export default function AutomationsScreenDemo() {
|
|
341
|
+
const [width, setWidth] = useState(1280);
|
|
342
|
+
const [scenario, setScenario] = useState("many");
|
|
343
|
+
return (
|
|
344
|
+
<div className="grid gap-4 p-4">
|
|
345
|
+
<ButtonGroup aria-label="The state">
|
|
346
|
+
{Object.entries(SCENARIO_LABELS).map(([key, label]) => (
|
|
347
|
+
<Button key={key} size="sm" variant={key === scenario ? "primary" : "secondary"} onClick={() => setScenario(key)}>
|
|
348
|
+
{label}
|
|
349
|
+
</Button>
|
|
350
|
+
))}
|
|
351
|
+
</ButtonGroup>
|
|
352
|
+
<ButtonGroup aria-label="The block's width">
|
|
353
|
+
{WIDTHS.map((w) => (
|
|
354
|
+
<Button key={w} size="sm" variant={w === width ? "primary" : "secondary"} onClick={() => setWidth(w)}>
|
|
355
|
+
{w} px
|
|
356
|
+
</Button>
|
|
357
|
+
))}
|
|
358
|
+
</ButtonGroup>
|
|
359
|
+
<div className="flex h-[720px] w-full flex-col overflow-hidden rounded-lg border border-border" style={{ maxWidth: width }}>
|
|
360
|
+
<Page key={scenario} scenario={scenario} />
|
|
361
|
+
</div>
|
|
362
|
+
</div>
|
|
363
|
+
);
|
|
364
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// The AutomationsScreen block — `makersclaw add automations-screen`. Fixed:
|
|
2
|
+
// compose it through its props (README.md beside this file).
|
|
3
|
+
export { AutomationsScreen } from "./automations-screen";
|
|
4
|
+
export { deletedWords, normalizePlace, paramsOfPlace, placeFromParams } from "./screen-model";
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// The pure part of AutomationsScreen: where the member is (the place), how
|
|
2
|
+
// the place travels in a URL, where Back goes, what a delete says, which
|
|
3
|
+
// starting points are offered, which tabs an automation has. No React, no
|
|
4
|
+
// network: a test runs every function here.
|
|
5
|
+
|
|
6
|
+
/** The `automation` value of a screen that makes a new one. */
|
|
7
|
+
export const NEW = "new";
|
|
8
|
+
/** The two places inside one automation that every automation has. */
|
|
9
|
+
export const FLOW = "flow";
|
|
10
|
+
export const RUNS = "runs";
|
|
11
|
+
|
|
12
|
+
const text = (v) => (typeof v === "string" && v.trim() ? v.trim() : null);
|
|
13
|
+
const list = (v) => (Array.isArray(v) ? v : []);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A place, made well-formed. The list is `{ automation: null }`. One
|
|
17
|
+
* automation is `{ automation: <id>, tab, run }`: `tab` is `flow` (default),
|
|
18
|
+
* `runs`, or the key of an extra tab; `run` is a run's id. A new one is
|
|
19
|
+
* `{ automation: "new", start }`, `start` being the starting point's id.
|
|
20
|
+
*/
|
|
21
|
+
export function normalizePlace(p) {
|
|
22
|
+
const o = p && typeof p === "object" ? p : {};
|
|
23
|
+
const automation = text(o.automation);
|
|
24
|
+
if (!automation) return { automation: null, tab: FLOW, run: null, start: null };
|
|
25
|
+
if (automation === NEW) return { automation: NEW, tab: FLOW, run: null, start: text(o.start) };
|
|
26
|
+
return { automation, tab: text(o.tab) ?? FLOW, run: text(o.run), start: null };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Which screen a place is: list · new · one · run. */
|
|
30
|
+
export function whereOf(place) {
|
|
31
|
+
const p = normalizePlace(place);
|
|
32
|
+
if (!p.automation) return "list";
|
|
33
|
+
if (p.automation === NEW) return "new";
|
|
34
|
+
return p.run ? "run" : "one";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const samePlace = (a, b) => JSON.stringify(normalizePlace(a)) === JSON.stringify(normalizePlace(b));
|
|
38
|
+
|
|
39
|
+
/** A place from the page's query string: `placeFromParams(useSearchParams())`,
|
|
40
|
+
* a URLSearchParams, or a function name → value. The names are `automation`,
|
|
41
|
+
* `tab`, `run`, `start`. */
|
|
42
|
+
export function placeFromParams(params) {
|
|
43
|
+
const get = typeof params === "function" ? params : (name) => (params && typeof params.get === "function" ? params.get(name) : null);
|
|
44
|
+
return normalizePlace({ automation: get("automation"), tab: get("tab"), run: get("run"), start: get("start") });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** The query parameters of a place, only what is set: the list is `{}`. Spread
|
|
48
|
+
* them into your own (`{ view: "automations", ...paramsOfPlace(place) }`). */
|
|
49
|
+
export function paramsOfPlace(place) {
|
|
50
|
+
const p = normalizePlace(place);
|
|
51
|
+
if (!p.automation) return {};
|
|
52
|
+
return {
|
|
53
|
+
automation: p.automation,
|
|
54
|
+
...(p.tab !== FLOW && !p.run ? { tab: p.tab } : {}),
|
|
55
|
+
...(p.run ? { run: p.run } : {}),
|
|
56
|
+
...(p.start ? { start: p.start } : {}),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Where the member came from when a run opened: remembered so that Back from
|
|
62
|
+
* a run returns to the tab it was opened from (the Runs list), not always to
|
|
63
|
+
* the flow. `from` and `to` are the places before and after a move; `origin`
|
|
64
|
+
* is what was remembered. Choosing another run from inside a run keeps it.
|
|
65
|
+
*/
|
|
66
|
+
export function originAfter(from, to, origin = null) {
|
|
67
|
+
const a = normalizePlace(from);
|
|
68
|
+
const b = normalizePlace(to);
|
|
69
|
+
if (!b.run) return null;
|
|
70
|
+
if (a.run) return origin;
|
|
71
|
+
return a.automation === b.automation ? a : null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** The place Back from a run goes to: the tab the run was opened from, else the flow. */
|
|
75
|
+
export function backFrom(place, origin = null) {
|
|
76
|
+
const p = normalizePlace(place);
|
|
77
|
+
const o = origin ? normalizePlace(origin) : null;
|
|
78
|
+
if (o && o.automation === p.automation && !o.run && o.tab !== FLOW) return { automation: p.automation, tab: o.tab, run: null, start: null };
|
|
79
|
+
return { automation: p.automation, tab: FLOW, run: null, start: null };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The words after a delete: a title, what followed it (the automations that
|
|
84
|
+
* started when it finished and now never will), and whether Undo is offered.
|
|
85
|
+
* `done` is the platform's answer `{ id, name, followers, undo }`.
|
|
86
|
+
*/
|
|
87
|
+
export function deletedWords(done, fallbackName = "") {
|
|
88
|
+
const name = text(done?.name) ?? text(fallbackName);
|
|
89
|
+
const followers = list(done?.followers).filter((f) => f && text(f.name));
|
|
90
|
+
return {
|
|
91
|
+
title: name ? `Deleted “${name}”.` : "Deleted.",
|
|
92
|
+
detail: followers.length
|
|
93
|
+
? `${followers.length} ${followers.length === 1 ? "automation" : "automations"} followed it and will no longer start: ${followers.map((f) => f.name).join(", ")}.`
|
|
94
|
+
: "",
|
|
95
|
+
undo: Boolean(done?.undo) && Boolean(text(done?.id)),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** The starting points offered under New: the app's own first, then the platform's
|
|
100
|
+
* (already turned into the list's shape), else the built-in ones. One of each id. */
|
|
101
|
+
export function combineStarts(app, platform, builtIn) {
|
|
102
|
+
const seen = new Set();
|
|
103
|
+
const out = [];
|
|
104
|
+
for (const s of [...list(app), ...(list(platform).length ? list(platform) : list(builtIn))]) {
|
|
105
|
+
if (!s || typeof s.id !== "string" || seen.has(s.id)) continue;
|
|
106
|
+
seen.add(s.id);
|
|
107
|
+
out.push(s);
|
|
108
|
+
}
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** The tabs of one automation: Flow, Runs, then the app's own that apply to it. */
|
|
113
|
+
export function tabsOf(extras, automation) {
|
|
114
|
+
const more = [];
|
|
115
|
+
for (const t of list(extras)) {
|
|
116
|
+
if (!t || typeof t.key !== "string" || t.key === FLOW || t.key === RUNS || typeof t.label !== "string") continue;
|
|
117
|
+
if (more.some((x) => x.key === t.key)) continue;
|
|
118
|
+
if (typeof t.when === "function" && !t.when(automation)) continue;
|
|
119
|
+
more.push(t);
|
|
120
|
+
}
|
|
121
|
+
return [{ key: FLOW, label: "Flow" }, { key: RUNS, label: "Runs" }, ...more.map((t) => ({ key: t.key, label: t.label }))];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** What `hooks` must hold. A string naming what is missing, else null. */
|
|
125
|
+
export function hooksProblem(hooks) {
|
|
126
|
+
if (!hooks || typeof hooks !== "object") return "AutomationsScreen needs hooks={{ useAutomations, useAutomation, useAutomationRun }} (from lib/hooks).";
|
|
127
|
+
const missing = ["useAutomations", "useAutomation", "useAutomationRun"].filter((k) => typeof hooks[k] !== "function");
|
|
128
|
+
return missing.length ? `AutomationsScreen: hooks is missing ${missing.join(", ")}.` : null;
|
|
129
|
+
}
|
|
@@ -244,32 +244,42 @@ export function TooltipBox({ at, title, rows, footer }) {
|
|
|
244
244
|
}
|
|
245
245
|
|
|
246
246
|
/** The numbers behind the picture, for a screen reader: a real table,
|
|
247
|
-
* visually hidden. `head` the column names, `body` rows of strings.
|
|
247
|
+
* visually hidden. `head` the column names, `body` rows of strings.
|
|
248
|
+
*
|
|
249
|
+
* The hiding is a div's, never the table's: a table takes `width: 1px`
|
|
250
|
+
* as a least width and grows to its rows, and a browser need not clip a
|
|
251
|
+
* table's overflow, so `sr-only` on the table itself left a box as wide
|
|
252
|
+
* as its longest row, which pushed a phone's page past the window (the
|
|
253
|
+
* store's probe at 375px, 2026-10-08). The div is 1px, clips what it
|
|
254
|
+
* holds and is absolute inside the chart's own box (each chart's root
|
|
255
|
+
* is `relative`); the table in it is a plain table, read as one. */
|
|
248
256
|
export function DataTable({ caption, head, body }) {
|
|
249
257
|
return (
|
|
250
|
-
<
|
|
251
|
-
<
|
|
252
|
-
|
|
253
|
-
<
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
</thead>
|
|
259
|
-
<tbody>
|
|
260
|
-
{body.map((cells, r) => (
|
|
261
|
-
<tr key={r}>
|
|
262
|
-
{cells.map((c, i) =>
|
|
263
|
-
i === 0 ? (
|
|
264
|
-
<th key={i} scope="row">{c}</th>
|
|
265
|
-
) : (
|
|
266
|
-
<td key={i}>{c}</td>
|
|
267
|
-
),
|
|
268
|
-
)}
|
|
258
|
+
<div className="sr-only">
|
|
259
|
+
<table>
|
|
260
|
+
<caption>{caption}</caption>
|
|
261
|
+
<thead>
|
|
262
|
+
<tr>
|
|
263
|
+
{head.map((h, i) => (
|
|
264
|
+
<th key={i} scope="col">{h}</th>
|
|
265
|
+
))}
|
|
269
266
|
</tr>
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
267
|
+
</thead>
|
|
268
|
+
<tbody>
|
|
269
|
+
{body.map((cells, r) => (
|
|
270
|
+
<tr key={r}>
|
|
271
|
+
{cells.map((c, i) =>
|
|
272
|
+
i === 0 ? (
|
|
273
|
+
<th key={i} scope="row">{c}</th>
|
|
274
|
+
) : (
|
|
275
|
+
<td key={i}>{c}</td>
|
|
276
|
+
),
|
|
277
|
+
)}
|
|
278
|
+
</tr>
|
|
279
|
+
))}
|
|
280
|
+
</tbody>
|
|
281
|
+
</table>
|
|
282
|
+
</div>
|
|
273
283
|
);
|
|
274
284
|
}
|
|
275
285
|
|
|
@@ -168,7 +168,7 @@ export function PieChart({
|
|
|
168
168
|
: { value: format(total), label: totalLabel };
|
|
169
169
|
|
|
170
170
|
return (
|
|
171
|
-
<div className={cx("grid min-w-0 gap-3", className)}>
|
|
171
|
+
<div className={cx("relative grid min-w-0 gap-3", className)}>
|
|
172
172
|
<div
|
|
173
173
|
ref={boxRef}
|
|
174
174
|
tabIndex={0}
|
|
@@ -127,7 +127,10 @@ not run.
|
|
|
127
127
|
has no `author`.
|
|
128
128
|
- `emptyTitle` · `emptyBody` · `emptyState` — what an empty thread says;
|
|
129
129
|
`emptyState` replaces the whole thing.
|
|
130
|
-
- `height` — default `"min(40rem, 80dvh)"
|
|
130
|
+
- `height` — default `"min(40rem, 80dvh)"` (the block's own); the list
|
|
131
|
+
scrolls inside it. Yours is a length (`"40rem"`) or `"100%"` in a sized
|
|
132
|
+
box, such as the kit's `AppLayout contentMode="fill"`: a `vh` length in
|
|
133
|
+
your code is refused by `npm run check`.
|
|
131
134
|
- `label` — what the thread is called to a screen reader.
|
|
132
135
|
- `className` — layout around it (width, margin). Never colour.
|
|
133
136
|
|
|
@@ -233,7 +233,7 @@ function SupportDemo() {
|
|
|
233
233
|
emptyTitle="No messages yet"
|
|
234
234
|
emptyBody="Ask about billing, your workspace or an invoice — Ada answers in a few seconds."
|
|
235
235
|
label="Support chat"
|
|
236
|
-
height="
|
|
236
|
+
height="44rem"
|
|
237
237
|
/>
|
|
238
238
|
</div>
|
|
239
239
|
);
|
|
@@ -412,7 +412,7 @@ function AssistantDemo() {
|
|
|
412
412
|
emptyTitle="What should we look at?"
|
|
413
413
|
emptyBody="Pip reads your orders, stock and returns. Pick a question or ask your own."
|
|
414
414
|
label="Planning assistant"
|
|
415
|
-
height="
|
|
415
|
+
height="40rem"
|
|
416
416
|
/>
|
|
417
417
|
</div>
|
|
418
418
|
);
|