makersclaw 0.8.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -3
- package/package.json +1 -1
- package/r/activity-log.json +52 -0
- package/r/agent-changes.json +51 -0
- package/r/agent-chat.json +53 -0
- package/r/agent-column.json +85 -0
- package/r/agent-dock.json +57 -0
- package/r/agent-kit.json +49 -0
- package/r/agent-marks.json +49 -0
- package/r/agent-pane.json +93 -0
- package/r/ai-action.json +56 -0
- package/r/annotate.json +3 -3
- package/r/approval-request.json +2 -2
- package/r/approval.json +54 -0
- package/r/ask-bar.json +50 -0
- package/r/audio-player.json +2 -2
- package/r/auth-form.json +2 -2
- package/r/automation-editor.json +66 -0
- package/r/automation-list.json +57 -0
- package/r/automation-model.json +118 -0
- package/r/automations-screen.json +71 -0
- package/r/board.json +2 -2
- package/r/calendar-view.json +3 -3
- package/r/cart.json +2 -2
- package/r/chart.json +2 -2
- package/r/chat-pane.json +84 -0
- package/r/chat.json +2 -2
- package/r/code-editor.json +2 -2
- package/r/comment-box.json +3 -3
- package/r/connect-card.json +51 -0
- package/r/contact-section.json +2 -2
- package/r/core.json +13 -7
- package/r/cost.json +51 -0
- package/r/csv-import.json +3 -3
- package/r/data-picker.json +60 -0
- package/r/data-table.json +2 -2
- package/r/diagram.json +2 -2
- package/r/doc-editor.json +2 -2
- package/r/doc-page.json +3 -3
- package/r/email-render.json +2 -2
- package/r/faq.json +2 -2
- package/r/file-upload.json +2 -2
- package/r/first-run.json +63 -0
- package/r/fix-note.json +57 -0
- package/r/flow-canvas.json +2 -2
- package/r/flow-editor.json +79 -0
- package/r/footer.json +2 -2
- package/r/gantt.json +3 -3
- package/r/job-panel.json +2 -2
- package/r/json-editor.json +3 -3
- package/r/library-shelf.json +56 -0
- package/r/map.json +2 -2
- package/r/margin-notes.json +67 -0
- package/r/menubar.json +2 -2
- package/r/mind-map.json +3 -3
- package/r/navigation-menu.json +2 -2
- package/r/needs-you.json +55 -0
- package/r/order-detail.json +2 -2
- package/r/org-chart.json +3 -3
- package/r/pace-bar.json +69 -0
- package/r/page-builder.json +2 -2
- package/r/payment-methods.json +2 -2
- package/r/pdf-document.json +2 -2
- package/r/pdf-viewer.json +2 -2
- package/r/product-detail.json +2 -2
- package/r/product-grid.json +3 -3
- package/r/record-form.json +2 -2
- package/r/record-grid.json +52 -0
- package/r/record-notes.json +4 -4
- package/r/registry.json +4086 -1512
- package/r/run-history.json +52 -0
- package/r/run-view.json +80 -0
- package/r/schedule-card.json +54 -0
- package/r/schedule-engine.json +79 -0
- package/r/schedule-gantt.json +83 -0
- package/r/schedule-ruler.json +64 -0
- package/r/search-dialog.json +2 -2
- package/r/settings-list.json +2 -2
- package/r/setup-checklist.json +2 -2
- package/r/share-dialog.json +2 -2
- package/r/sidebar.json +2 -2
- package/r/signature-pad.json +2 -2
- package/r/sketch-canvas.json +2 -2
- package/r/source-receipt.json +52 -0
- package/r/spreadsheet.json +3 -3
- package/r/stat-cards.json +3 -3
- package/r/step-picker.json +53 -0
- package/r/step-settings.json +72 -0
- package/r/suggestion.json +58 -0
- package/r/sum-bar.json +64 -0
- package/r/task-list.json +3 -3
- package/r/task-model.json +2 -2
- package/r/testimonials.json +2 -2
- package/r/tier-map.json +67 -0
- package/r/time-grid.json +73 -0
- package/r/time-strip.json +64 -0
- package/r/top-bar.json +66 -0
- package/r/transaction-list.json +2 -2
- package/r/trigger-picker.json +61 -0
- package/r/whiteboard.json +3 -3
- package/r/workflow-view.json +55 -0
- package/registry/blocks/activity-log/README.md +112 -0
- package/registry/blocks/activity-log/activity-log.jsx +351 -0
- package/registry/blocks/activity-log/block.json +33 -0
- package/registry/blocks/activity-log/demo.jsx +48 -0
- package/registry/blocks/activity-log/index.js +4 -0
- package/registry/blocks/agent-changes/README.md +94 -0
- package/registry/blocks/agent-changes/agent-changes.jsx +118 -0
- package/registry/blocks/agent-changes/block.json +28 -0
- package/registry/blocks/agent-changes/demo.jsx +39 -0
- package/registry/blocks/agent-changes/index.js +3 -0
- package/registry/blocks/agent-chat/README.md +145 -0
- package/registry/blocks/agent-chat/agent-chat.jsx +415 -0
- package/registry/blocks/agent-chat/block.json +30 -0
- package/registry/blocks/agent-chat/demo.jsx +165 -0
- package/registry/blocks/agent-chat/index.js +3 -0
- package/registry/blocks/agent-column/README.md +197 -0
- package/registry/blocks/agent-column/agent-column.jsx +492 -0
- package/registry/blocks/agent-column/block.json +67 -0
- package/registry/blocks/agent-column/column-picker.jsx +163 -0
- package/registry/blocks/agent-column/demo.jsx +50 -0
- package/registry/blocks/agent-column/index.js +7 -0
- package/registry/blocks/agent-column/model.js +164 -0
- package/registry/blocks/agent-column/receipt.jsx +483 -0
- package/registry/blocks/agent-column/spreadsheet-type.jsx +24 -0
- package/registry/blocks/agent-dock/README.md +210 -0
- package/registry/blocks/agent-dock/agent-dock.jsx +356 -0
- package/registry/blocks/agent-dock/block.json +47 -0
- package/registry/blocks/agent-dock/demo.jsx +153 -0
- package/registry/blocks/agent-dock/dock-model.js +118 -0
- package/registry/blocks/agent-dock/index.js +5 -0
- package/registry/blocks/agent-kit/README.md +73 -0
- package/registry/blocks/agent-kit/agent-kit.jsx +86 -0
- package/registry/blocks/agent-kit/block.json +10 -0
- package/registry/blocks/agent-kit/demo.jsx +35 -0
- package/registry/blocks/agent-kit/index.js +3 -0
- package/registry/blocks/agent-marks/README.md +80 -0
- package/registry/blocks/agent-marks/agent-marks.jsx +133 -0
- package/registry/blocks/agent-marks/block.json +27 -0
- package/registry/blocks/agent-marks/demo.jsx +49 -0
- package/registry/blocks/agent-marks/index.js +3 -0
- package/registry/blocks/agent-pane/README.md +234 -0
- package/registry/blocks/agent-pane/agent-pane.jsx +246 -0
- package/registry/blocks/agent-pane/block.json +41 -0
- package/registry/blocks/agent-pane/composer.jsx +6 -0
- package/registry/blocks/agent-pane/demo.jsx +233 -0
- package/registry/blocks/agent-pane/dock-context.js +10 -0
- package/registry/blocks/agent-pane/index.js +4 -0
- package/registry/blocks/agent-pane/prose.jsx +84 -0
- package/registry/blocks/agent-pane/questions.jsx +134 -0
- package/registry/blocks/agent-pane/transcript.jsx +268 -0
- package/registry/blocks/agent-pane/work.jsx +368 -0
- package/registry/blocks/ai-action/README.md +94 -0
- package/registry/blocks/ai-action/ai-action.jsx +188 -0
- package/registry/blocks/ai-action/block.json +34 -0
- package/registry/blocks/ai-action/demo.jsx +93 -0
- package/registry/blocks/ai-action/index.js +3 -0
- package/registry/blocks/approval/README.md +145 -0
- package/registry/blocks/approval/approval.jsx +163 -0
- package/registry/blocks/approval/block.json +31 -0
- package/registry/blocks/approval/demo.jsx +121 -0
- package/registry/blocks/approval/index.js +3 -0
- package/registry/blocks/ask-bar/README.md +105 -0
- package/registry/blocks/ask-bar/ask-bar.jsx +166 -0
- package/registry/blocks/ask-bar/block.json +26 -0
- package/registry/blocks/ask-bar/demo.jsx +78 -0
- package/registry/blocks/ask-bar/index.js +3 -0
- package/registry/blocks/automation-editor/README.md +291 -0
- package/registry/blocks/automation-editor/automation-editor.jsx +656 -0
- package/registry/blocks/automation-editor/block.json +39 -0
- package/registry/blocks/automation-editor/demo.jsx +284 -0
- package/registry/blocks/automation-editor/index.js +4 -0
- package/registry/blocks/automation-editor/run-inputs.jsx +82 -0
- package/registry/blocks/automation-list/README.md +129 -0
- package/registry/blocks/automation-list/automation-list.jsx +476 -0
- package/registry/blocks/automation-list/block.json +35 -0
- package/registry/blocks/automation-list/demo.jsx +67 -0
- package/registry/blocks/automation-list/index.js +3 -0
- package/registry/blocks/automation-model/README.md +109 -0
- package/registry/blocks/automation-model/block.json +135 -0
- package/registry/blocks/automation-model/catalog.js +256 -0
- package/registry/blocks/automation-model/check.js +161 -0
- package/registry/blocks/automation-model/demo.jsx +42 -0
- package/registry/blocks/automation-model/edit.js +262 -0
- package/registry/blocks/automation-model/fixes.js +287 -0
- package/registry/blocks/automation-model/glyph.jsx +13 -0
- package/registry/blocks/automation-model/graph.js +410 -0
- package/registry/blocks/automation-model/index.js +33 -0
- package/registry/blocks/automation-model/overlay.js +175 -0
- package/registry/blocks/automation-model/parts.jsx +94 -0
- package/registry/blocks/automation-model/schedule.js +53 -0
- package/registry/blocks/automation-model/templates.js +128 -0
- package/registry/blocks/automation-model/values.js +29 -0
- package/registry/blocks/automation-model/words.js +439 -0
- package/registry/blocks/automations-screen/README.md +207 -0
- package/registry/blocks/automations-screen/automations-screen.jsx +452 -0
- package/registry/blocks/automations-screen/block.json +47 -0
- package/registry/blocks/automations-screen/demo.jsx +364 -0
- package/registry/blocks/automations-screen/index.js +4 -0
- package/registry/blocks/automations-screen/screen-model.js +129 -0
- package/registry/blocks/chat-pane/README.md +143 -0
- package/registry/blocks/chat-pane/block.json +48 -0
- package/registry/blocks/chat-pane/chat-attach.js +166 -0
- package/registry/blocks/chat-pane/chat-composer.jsx +195 -0
- package/registry/blocks/chat-pane/chat-context.js +17 -0
- package/registry/blocks/chat-pane/chat-model.js +43 -0
- package/registry/blocks/chat-pane/chat-pane.jsx +398 -0
- package/registry/blocks/chat-pane/chats-list.jsx +52 -0
- package/registry/blocks/chat-pane/demo.jsx +103 -0
- package/registry/blocks/chat-pane/index.js +9 -0
- package/registry/blocks/connect-card/README.md +99 -0
- package/registry/blocks/connect-card/block.json +10 -0
- package/registry/blocks/connect-card/connect-card.jsx +103 -0
- package/registry/blocks/connect-card/demo.jsx +49 -0
- package/registry/blocks/connect-card/index.js +3 -0
- package/registry/blocks/cost/README.md +115 -0
- package/registry/blocks/cost/block.json +10 -0
- package/registry/blocks/cost/cost.jsx +176 -0
- package/registry/blocks/cost/demo.jsx +66 -0
- package/registry/blocks/cost/index.js +3 -0
- package/registry/blocks/data-picker/README.md +81 -0
- package/registry/blocks/data-picker/block.json +38 -0
- package/registry/blocks/data-picker/data-picker.jsx +210 -0
- package/registry/blocks/data-picker/demo.jsx +34 -0
- package/registry/blocks/data-picker/fields.js +89 -0
- package/registry/blocks/data-picker/index.js +4 -0
- package/registry/blocks/data-table/demo.jsx +1 -1
- package/registry/blocks/first-run/README.md +118 -0
- package/registry/blocks/first-run/block.json +11 -0
- package/registry/blocks/first-run/demo.jsx +88 -0
- package/registry/blocks/first-run/first-run-model.js +51 -0
- package/registry/blocks/first-run/first-run.jsx +156 -0
- package/registry/blocks/first-run/index.js +5 -0
- package/registry/blocks/first-run/probe-rules.js +26 -0
- package/registry/blocks/fix-note/README.md +79 -0
- package/registry/blocks/fix-note/block.json +35 -0
- package/registry/blocks/fix-note/demo.jsx +30 -0
- package/registry/blocks/fix-note/fix-note.jsx +235 -0
- package/registry/blocks/fix-note/index.js +3 -0
- package/registry/blocks/flow-editor/README.md +108 -0
- package/registry/blocks/flow-editor/block.json +44 -0
- package/registry/blocks/flow-editor/demo.jsx +115 -0
- package/registry/blocks/flow-editor/flow-canvas.jsx +568 -0
- package/registry/blocks/flow-editor/flow-editor.jsx +154 -0
- package/registry/blocks/flow-editor/index.js +6 -0
- package/registry/blocks/flow-editor/minimap.jsx +78 -0
- package/registry/blocks/flow-editor/outline.jsx +134 -0
- package/registry/blocks/library-shelf/README.md +117 -0
- package/registry/blocks/library-shelf/block.json +28 -0
- package/registry/blocks/library-shelf/demo.jsx +68 -0
- package/registry/blocks/library-shelf/index.js +3 -0
- package/registry/blocks/library-shelf/library-shelf.jsx +211 -0
- package/registry/blocks/library-shelf/probe-rules.js +15 -0
- package/registry/blocks/margin-notes/README.md +222 -0
- package/registry/blocks/margin-notes/block.json +10 -0
- package/registry/blocks/margin-notes/demo.jsx +144 -0
- package/registry/blocks/margin-notes/index.js +20 -0
- package/registry/blocks/margin-notes/margin-model.js +224 -0
- package/registry/blocks/margin-notes/margin-notes.jsx +465 -0
- package/registry/blocks/needs-you/README.md +177 -0
- package/registry/blocks/needs-you/block.json +34 -0
- package/registry/blocks/needs-you/demo.jsx +143 -0
- package/registry/blocks/needs-you/index.js +3 -0
- package/registry/blocks/needs-you/needs-you.jsx +441 -0
- package/registry/blocks/pace-bar/README.md +230 -0
- package/registry/blocks/pace-bar/block.json +10 -0
- package/registry/blocks/pace-bar/demo.jsx +255 -0
- package/registry/blocks/pace-bar/index.js +22 -0
- package/registry/blocks/pace-bar/pace-bar.jsx +461 -0
- package/registry/blocks/pace-bar/pace-model.js +288 -0
- package/registry/blocks/record-grid/README.md +100 -0
- package/registry/blocks/record-grid/block.json +28 -0
- package/registry/blocks/record-grid/demo.jsx +43 -0
- package/registry/blocks/record-grid/index.js +3 -0
- package/registry/blocks/record-grid/record-grid.jsx +251 -0
- package/registry/blocks/run-history/README.md +138 -0
- package/registry/blocks/run-history/block.json +10 -0
- package/registry/blocks/run-history/demo.jsx +74 -0
- package/registry/blocks/run-history/index.js +3 -0
- package/registry/blocks/run-history/run-history.jsx +218 -0
- package/registry/blocks/run-view/README.md +174 -0
- package/registry/blocks/run-view/block.json +50 -0
- package/registry/blocks/run-view/demo.jsx +286 -0
- package/registry/blocks/run-view/index.js +5 -0
- package/registry/blocks/run-view/run-controls.jsx +266 -0
- package/registry/blocks/run-view/run-view.jsx +236 -0
- package/registry/blocks/run-view/runs-list.jsx +99 -0
- package/registry/blocks/schedule-card/README.md +121 -0
- package/registry/blocks/schedule-card/block.json +10 -0
- package/registry/blocks/schedule-card/demo.jsx +82 -0
- package/registry/blocks/schedule-card/index.js +3 -0
- package/registry/blocks/schedule-card/schedule-card.jsx +136 -0
- package/registry/blocks/schedule-engine/README.md +137 -0
- package/registry/blocks/schedule-engine/block.json +10 -0
- package/registry/blocks/schedule-engine/demo.jsx +82 -0
- package/registry/blocks/schedule-engine/engine.js +769 -0
- package/registry/blocks/schedule-engine/index.js +9 -0
- package/registry/blocks/schedule-engine/ops.js +71 -0
- package/registry/blocks/schedule-engine/place.js +294 -0
- package/registry/blocks/schedule-engine/tree.js +79 -0
- package/registry/blocks/schedule-engine/waits.js +80 -0
- package/registry/blocks/schedule-gantt/README.md +159 -0
- package/registry/blocks/schedule-gantt/block.json +10 -0
- package/registry/blocks/schedule-gantt/demo.jsx +223 -0
- package/registry/blocks/schedule-gantt/gantt-model.js +219 -0
- package/registry/blocks/schedule-gantt/geometry.js +176 -0
- package/registry/blocks/schedule-gantt/index.js +26 -0
- package/registry/blocks/schedule-gantt/schedule-gantt.jsx +882 -0
- package/registry/blocks/schedule-gantt/sheet.jsx +372 -0
- package/registry/blocks/schedule-gantt/track.jsx +190 -0
- package/registry/blocks/schedule-ruler/README.md +148 -0
- package/registry/blocks/schedule-ruler/block.json +10 -0
- package/registry/blocks/schedule-ruler/demo.jsx +181 -0
- package/registry/blocks/schedule-ruler/index.js +21 -0
- package/registry/blocks/schedule-ruler/ruler-model.js +239 -0
- package/registry/blocks/schedule-ruler/schedule-ruler.jsx +548 -0
- package/registry/blocks/source-receipt/README.md +97 -0
- package/registry/blocks/source-receipt/block.json +30 -0
- package/registry/blocks/source-receipt/demo.jsx +71 -0
- package/registry/blocks/source-receipt/index.js +3 -0
- package/registry/blocks/source-receipt/source-receipt.jsx +107 -0
- package/registry/blocks/spreadsheet/spreadsheet.jsx +15 -1
- package/registry/blocks/step-picker/README.md +78 -0
- package/registry/blocks/step-picker/block.json +31 -0
- package/registry/blocks/step-picker/demo.jsx +23 -0
- package/registry/blocks/step-picker/index.js +3 -0
- package/registry/blocks/step-picker/step-picker.jsx +144 -0
- package/registry/blocks/step-settings/README.md +100 -0
- package/registry/blocks/step-settings/block.json +44 -0
- package/registry/blocks/step-settings/demo.jsx +93 -0
- package/registry/blocks/step-settings/fields.jsx +58 -0
- package/registry/blocks/step-settings/flow-settings.jsx +115 -0
- package/registry/blocks/step-settings/index.js +5 -0
- package/registry/blocks/step-settings/step-settings.jsx +290 -0
- package/registry/blocks/suggestion/README.md +171 -0
- package/registry/blocks/suggestion/block.json +40 -0
- package/registry/blocks/suggestion/demo.jsx +91 -0
- package/registry/blocks/suggestion/index.js +3 -0
- package/registry/blocks/suggestion/suggestion.jsx +436 -0
- package/registry/blocks/sum-bar/README.md +122 -0
- package/registry/blocks/sum-bar/block.json +10 -0
- package/registry/blocks/sum-bar/demo.jsx +110 -0
- package/registry/blocks/sum-bar/index.js +5 -0
- package/registry/blocks/sum-bar/sum-bar.jsx +209 -0
- package/registry/blocks/sum-bar/sum-model.js +126 -0
- package/registry/blocks/tier-map/README.md +217 -0
- package/registry/blocks/tier-map/block.json +10 -0
- package/registry/blocks/tier-map/demo.jsx +222 -0
- package/registry/blocks/tier-map/index.js +24 -0
- package/registry/blocks/tier-map/tier-map.jsx +574 -0
- package/registry/blocks/tier-map/tier-model.js +318 -0
- package/registry/blocks/time-grid/README.md +305 -0
- package/registry/blocks/time-grid/block.json +10 -0
- package/registry/blocks/time-grid/demo.jsx +279 -0
- package/registry/blocks/time-grid/grid-model.js +322 -0
- package/registry/blocks/time-grid/index.js +25 -0
- package/registry/blocks/time-grid/time-grid.jsx +1325 -0
- package/registry/blocks/time-strip/README.md +141 -0
- package/registry/blocks/time-strip/block.json +10 -0
- package/registry/blocks/time-strip/demo.jsx +129 -0
- package/registry/blocks/time-strip/index.js +19 -0
- package/registry/blocks/time-strip/strip-model.js +178 -0
- package/registry/blocks/time-strip/time-strip.jsx +260 -0
- package/registry/blocks/top-bar/README.md +149 -0
- package/registry/blocks/top-bar/block.json +35 -0
- package/registry/blocks/top-bar/demo.jsx +78 -0
- package/registry/blocks/top-bar/index.js +5 -0
- package/registry/blocks/top-bar/probe-rules.js +74 -0
- package/registry/blocks/top-bar/top-bar-model.js +152 -0
- package/registry/blocks/top-bar/top-bar.jsx +244 -0
- package/registry/blocks/trigger-picker/README.md +98 -0
- package/registry/blocks/trigger-picker/block.json +38 -0
- package/registry/blocks/trigger-picker/demo.jsx +59 -0
- package/registry/blocks/trigger-picker/index.js +3 -0
- package/registry/blocks/trigger-picker/trigger-picker.jsx +374 -0
- package/registry/blocks/workflow-view/README.md +123 -0
- package/registry/blocks/workflow-view/block.json +10 -0
- package/registry/blocks/workflow-view/demo.jsx +90 -0
- package/registry/blocks/workflow-view/index.js +3 -0
- package/registry/blocks/workflow-view/workflow-view.jsx +203 -0
- package/src/install.mjs +77 -16
- package/src/registry.mjs +3 -0
- package/templates/next/browser/lib/data.js +6 -1
- package/templates/next/files/README.md +627 -24
- package/templates/next/files/check/automation-format.ts +2103 -0
- package/templates/next/files/check/automation-package.ts +142 -0
- package/templates/next/files/check/run.mjs +968 -9
- package/templates/next/files/components/ui/field.jsx +17 -4
- package/templates/next/files/components/ui/icon.jsx +13 -0
- package/templates/next/files/components/ui/index.js +2 -1
- package/templates/next/files/components/ui/layout.jsx +77 -2
- package/templates/next/files/components/ui/notice.jsx +70 -0
- package/templates/next/files/components/ui/page.jsx +124 -0
- package/templates/next/files/components/ui/stat.jsx +22 -7
- package/templates/next/files/components/ui/toast.jsx +6 -3
- package/templates/next/files/lib/automations.js +660 -0
- package/templates/next/files/lib/claw.js +241 -5
- package/templates/next/files/lib/door.js +92 -0
- package/templates/next/files/lib/hooks.js +1677 -8
- package/templates/next/files/lib/platform.js +529 -21
- package/templates/next/template.json +13 -3
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// The editor around the canvas: lays the definition out, folds a for-each
|
|
4
|
+
// on request, and offers the canvas and the outline as two views of one
|
|
5
|
+
// flow. It holds nothing of the definition itself (the screen around it
|
|
6
|
+
// does, as a stack of pure edits from automation-model); it only draws
|
|
7
|
+
// what it is given and says what a person pressed.
|
|
8
|
+
//
|
|
9
|
+
// Editing: `editable` with onInsert / onAddAtSelection. A run:
|
|
10
|
+
// `overlay` (automation-model's overlayOf) and nothing editable.
|
|
11
|
+
|
|
12
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
13
|
+
import { Icon, cx } from "../../ui";
|
|
14
|
+
import { layoutAutomation, triggerWords as wordsOf } from "../automation-model";
|
|
15
|
+
import { FlowCanvas } from "./flow-canvas";
|
|
16
|
+
import { StepOutline } from "./outline";
|
|
17
|
+
|
|
18
|
+
/** Under this width the editor opens on the outline. */
|
|
19
|
+
const NARROW = 480;
|
|
20
|
+
|
|
21
|
+
const BAR_BUTTON = "inline-flex size-7 items-center justify-center rounded-md text-muted-foreground hover:bg-accent disabled:opacity-35 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";
|
|
22
|
+
|
|
23
|
+
/** Canvas | Outline, as the head of either view. */
|
|
24
|
+
function ViewToggle({ view, onView }) {
|
|
25
|
+
const one = (name, icon, label) => (
|
|
26
|
+
<button
|
|
27
|
+
type="button"
|
|
28
|
+
aria-pressed={view === name}
|
|
29
|
+
onClick={() => onView(name)}
|
|
30
|
+
className={cx("inline-flex h-7 items-center gap-1.5 rounded-md px-2 text-[13px] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", view === name ? "bg-accent font-medium text-accent-foreground" : "text-muted-foreground hover:bg-accent")}
|
|
31
|
+
>
|
|
32
|
+
<Icon name={icon} size={14} /> {label}
|
|
33
|
+
</button>
|
|
34
|
+
);
|
|
35
|
+
return (
|
|
36
|
+
<div role="group" aria-label="View" className="flex items-center gap-px rounded-lg border border-border bg-card p-0.5">
|
|
37
|
+
{one("canvas", "network", "Canvas")}
|
|
38
|
+
{one("outline", "list", "Outline")}
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* def the definition shown: { trigger, steps, … }
|
|
45
|
+
* selectedId, onSelect(id|null)
|
|
46
|
+
* editable the + buttons and the palette
|
|
47
|
+
* onInsert(slot, kind), onAddAtSelection(kind)
|
|
48
|
+
* overlay a run's overlayOf(), or null
|
|
49
|
+
* flagged a Set of step ids with something to fix
|
|
50
|
+
* prices a Map of step id to { credits, max }
|
|
51
|
+
* fitKey, revealId see FlowCanvas
|
|
52
|
+
* runnable a Set of kinds the platform runs
|
|
53
|
+
* undo { canUndo, canRedo, onUndo, onRedo } or null: the history buttons
|
|
54
|
+
* onOpenFlow the gear: open the automation's own settings (draws it when given)
|
|
55
|
+
*/
|
|
56
|
+
export function FlowEditor({ def, selectedId = null, onSelect, editable = false, onInsert, onAddAtSelection, overlay = null, flagged, prices, fitKey, revealId, runnable, undo = null, onOpenFlow, className }) {
|
|
57
|
+
const rootRef = useRef(null);
|
|
58
|
+
const [narrow, setNarrow] = useState(false);
|
|
59
|
+
const [chosen, setChosen] = useState(null);
|
|
60
|
+
// The canvas is a poor fit for a thumb: under about 480 px of its own
|
|
61
|
+
// width it opens on the outline, until a person asks for the canvas.
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
const el = rootRef.current;
|
|
64
|
+
if (!el) return undefined;
|
|
65
|
+
const measure = () => setNarrow(el.clientWidth > 0 && el.clientWidth < NARROW);
|
|
66
|
+
measure();
|
|
67
|
+
if (typeof ResizeObserver === "undefined") return undefined;
|
|
68
|
+
// Measure in the callback, act in the next frame (check rule 17).
|
|
69
|
+
let raf = 0;
|
|
70
|
+
const ro = new ResizeObserver(() => {
|
|
71
|
+
cancelAnimationFrame(raf);
|
|
72
|
+
raf = requestAnimationFrame(measure);
|
|
73
|
+
});
|
|
74
|
+
ro.observe(el);
|
|
75
|
+
return () => {
|
|
76
|
+
cancelAnimationFrame(raf);
|
|
77
|
+
ro.disconnect();
|
|
78
|
+
};
|
|
79
|
+
}, []);
|
|
80
|
+
const view = chosen ?? (narrow ? "outline" : "canvas");
|
|
81
|
+
const setView = setChosen;
|
|
82
|
+
const [collapsed, setCollapsed] = useState(() => new Set());
|
|
83
|
+
const words = wordsOf(def.trigger);
|
|
84
|
+
const layout = useMemo(() => layoutAutomation(def, { triggerWords: words, collapsed }), [def, words, collapsed]);
|
|
85
|
+
const steps = layout.nodes.filter((n) => n.type === "step" || n.type === "card").length;
|
|
86
|
+
|
|
87
|
+
const toggleCollapse = (id) =>
|
|
88
|
+
setCollapsed((c) => {
|
|
89
|
+
const next = new Set(c);
|
|
90
|
+
if (next.has(id)) next.delete(id);
|
|
91
|
+
else next.add(id);
|
|
92
|
+
return next;
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const history = undo ? (
|
|
96
|
+
<div role="group" aria-label="History" className="flex items-center gap-px rounded-lg border border-border bg-card p-0.5">
|
|
97
|
+
<button type="button" aria-label="Undo" disabled={!undo.canUndo} onClick={undo.onUndo} className={BAR_BUTTON}>
|
|
98
|
+
<Icon name="undo" size={15} />
|
|
99
|
+
</button>
|
|
100
|
+
<button type="button" aria-label="Redo" disabled={!undo.canRedo} onClick={undo.onRedo} className={BAR_BUTTON}>
|
|
101
|
+
<Icon name="redo" size={15} />
|
|
102
|
+
</button>
|
|
103
|
+
</div>
|
|
104
|
+
) : null;
|
|
105
|
+
|
|
106
|
+
const gear = onOpenFlow ? (
|
|
107
|
+
<button type="button" aria-label="Settings of this automation" title="Settings of this automation" onClick={onOpenFlow} className={cx(BAR_BUTTON, "size-9 rounded-lg border border-border bg-card")}>
|
|
108
|
+
<Icon name="settings" size={15} />
|
|
109
|
+
</button>
|
|
110
|
+
) : null;
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<div ref={rootRef} className={cx("flex min-h-0 min-w-0 flex-1 flex-col", className)}>
|
|
114
|
+
{view === "outline" ? (
|
|
115
|
+
<>
|
|
116
|
+
<div className="flex flex-wrap items-center gap-2 border-b border-border px-3 py-2">
|
|
117
|
+
{history}
|
|
118
|
+
<ViewToggle view={view} onView={setView} />
|
|
119
|
+
{gear}
|
|
120
|
+
</div>
|
|
121
|
+
<div data-scroll className="min-h-0 flex-1 overflow-y-auto">
|
|
122
|
+
<StepOutline def={def} triggerWords={words} selectedId={selectedId} onSelect={onSelect} overlay={overlay} editable={editable} onInsert={onInsert} flagged={flagged} runnable={runnable} />
|
|
123
|
+
</div>
|
|
124
|
+
</>
|
|
125
|
+
) : (
|
|
126
|
+
<FlowCanvas
|
|
127
|
+
layout={layout}
|
|
128
|
+
selectedId={selectedId}
|
|
129
|
+
onSelect={onSelect}
|
|
130
|
+
editable={editable}
|
|
131
|
+
onInsert={onInsert}
|
|
132
|
+
onAddAtSelection={onAddAtSelection}
|
|
133
|
+
onToggleCollapse={toggleCollapse}
|
|
134
|
+
overlay={overlay}
|
|
135
|
+
flagged={flagged}
|
|
136
|
+
prices={prices}
|
|
137
|
+
fitKey={fitKey}
|
|
138
|
+
revealId={revealId}
|
|
139
|
+
runnable={runnable}
|
|
140
|
+
info={
|
|
141
|
+
<>
|
|
142
|
+
{history}
|
|
143
|
+
<div className="rounded-lg border border-border bg-card px-2.5 py-1 text-[13px] text-muted-foreground">
|
|
144
|
+
<b className="font-semibold text-foreground">{steps}</b> {steps === 1 ? "step" : "steps"}
|
|
145
|
+
</div>
|
|
146
|
+
<ViewToggle view={view} onView={setView} />
|
|
147
|
+
{gear}
|
|
148
|
+
</>
|
|
149
|
+
}
|
|
150
|
+
/>
|
|
151
|
+
)}
|
|
152
|
+
</div>
|
|
153
|
+
);
|
|
154
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// The FlowEditor block — `makersclaw add flow-editor`. Fixed: compose it
|
|
2
|
+
// through its props (README.md beside this file).
|
|
3
|
+
export { FlowCanvas } from "./flow-canvas";
|
|
4
|
+
export { FlowEditor } from "./flow-editor";
|
|
5
|
+
export { Minimap } from "./minimap";
|
|
6
|
+
export { StepOutline } from "./outline";
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// The map in the corner: the whole flow small, the part you are looking at
|
|
4
|
+
// outlined. Press or drag on it to go there. In a run it wears the run's
|
|
5
|
+
// colours, so a long flow shows where it went wrong at a glance.
|
|
6
|
+
|
|
7
|
+
import { useRef } from "react";
|
|
8
|
+
import { cx } from "../../ui";
|
|
9
|
+
|
|
10
|
+
export const MAP_W = 176;
|
|
11
|
+
export const MAP_H = 104;
|
|
12
|
+
|
|
13
|
+
const DOT = {
|
|
14
|
+
idle: "bg-muted-foreground/60",
|
|
15
|
+
done: "bg-success",
|
|
16
|
+
would: "bg-primary/60",
|
|
17
|
+
running: "bg-info",
|
|
18
|
+
failed: "bg-destructive",
|
|
19
|
+
fixed: "bg-primary",
|
|
20
|
+
waiting: "bg-warning",
|
|
21
|
+
skipped: "bg-muted-foreground/30",
|
|
22
|
+
unknown: "bg-muted-foreground/60",
|
|
23
|
+
notreached: "bg-muted-foreground/25",
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* layout automation-model's layoutAutomation()
|
|
28
|
+
* view { x, y, k }: where the canvas is
|
|
29
|
+
* size { w, h }: the canvas's size
|
|
30
|
+
* overlay a run's overlayOf(), or null
|
|
31
|
+
* onCenter(worldX, worldY)
|
|
32
|
+
*/
|
|
33
|
+
export function Minimap({ layout, view, size, overlay, onCenter }) {
|
|
34
|
+
const ref = useRef(null);
|
|
35
|
+
const b = layout.bounds;
|
|
36
|
+
if (b.w <= 0 || b.h <= 0) return null;
|
|
37
|
+
const s = Math.min((MAP_W - 16) / b.w, (MAP_H - 24) / b.h);
|
|
38
|
+
const ox = (MAP_W - b.w * s) / 2 - b.x * s;
|
|
39
|
+
const oy = (MAP_H - b.h * s) / 2 + 6 - b.y * s;
|
|
40
|
+
const rect = (x, y, w, h) => ({ left: x * s + ox, top: y * s + oy, width: Math.max(2, w * s), height: Math.max(2, h * s) });
|
|
41
|
+
const vp = rect(-view.x / view.k, -view.y / view.k, size.w / view.k, size.h / view.k);
|
|
42
|
+
|
|
43
|
+
const at = (e) => {
|
|
44
|
+
const r = ref.current?.getBoundingClientRect();
|
|
45
|
+
if (r) onCenter((e.clientX - r.left - ox) / s, (e.clientY - r.top - oy) / s);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<div
|
|
50
|
+
ref={ref}
|
|
51
|
+
data-nopan
|
|
52
|
+
role="img"
|
|
53
|
+
aria-label="Map of the whole flow"
|
|
54
|
+
style={{ width: MAP_W, height: MAP_H }}
|
|
55
|
+
className="absolute bottom-3 right-3 z-[6] cursor-pointer overflow-hidden rounded-lg border border-border bg-card shadow-sm"
|
|
56
|
+
onPointerDown={(e) => {
|
|
57
|
+
e.stopPropagation();
|
|
58
|
+
e.currentTarget.setPointerCapture?.(e.pointerId);
|
|
59
|
+
at(e);
|
|
60
|
+
}}
|
|
61
|
+
onPointerMove={(e) => {
|
|
62
|
+
if (e.buttons) at(e);
|
|
63
|
+
}}
|
|
64
|
+
>
|
|
65
|
+
<span className="absolute left-2 top-1 text-2xs font-medium text-muted-foreground">Map</span>
|
|
66
|
+
{layout.groups.map((g) => (
|
|
67
|
+
<span key={g.id} style={rect(g.x, g.y, g.w, g.h)} className="absolute rounded-sm border border-dashed border-muted-foreground/40" />
|
|
68
|
+
))}
|
|
69
|
+
{layout.nodes
|
|
70
|
+
.filter((n) => n.type === "step" || n.type === "trigger" || n.type === "card")
|
|
71
|
+
.map((n) => {
|
|
72
|
+
const state = overlay ? (overlay.nodes.get(n.id) ?? "notreached") : "idle";
|
|
73
|
+
return <span key={n.id} style={rect(n.x, n.y, n.w, n.h)} className={cx("absolute rounded-[2px]", DOT[state] ?? DOT.idle)} />;
|
|
74
|
+
})}
|
|
75
|
+
<span style={vp} className="absolute rounded-sm border-2 border-foreground/70 bg-foreground/5" />
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// The same flow as an outline: the trigger, then every step in order, a
|
|
4
|
+
// condition's branches and a loop's steps nested under it. It is the
|
|
5
|
+
// phone's second view (the canvas is a poor fit for a thumb), and a way in
|
|
6
|
+
// for anyone who works by keyboard or with a screen reader: every step is
|
|
7
|
+
// a button, and every place a step can go has an "Add a step" button.
|
|
8
|
+
|
|
9
|
+
import { useState } from "react";
|
|
10
|
+
import { Icon, cx } from "../../ui";
|
|
11
|
+
import { Glyph, KINDS, fixTag, stepSub, stepTitle, triggerGlyph, triggerTitle } from "../automation-model";
|
|
12
|
+
import { StepPicker } from "../step-picker";
|
|
13
|
+
|
|
14
|
+
const STATE_ICON = { done: "check", would: "pencil", running: "loader", failed: "x", fixed: "wand", waiting: "clock", skipped: "minus", unknown: "circle-help" };
|
|
15
|
+
const STATE_TONE = { done: "bg-success", would: "bg-muted-foreground", running: "bg-info", failed: "bg-destructive", fixed: "bg-primary", waiting: "bg-warning", skipped: "bg-muted-foreground", unknown: "bg-muted-foreground" };
|
|
16
|
+
const FACE = { plain: "text-muted-foreground", model: "text-primary", agent: "text-primary bg-primary/15 border-primary" };
|
|
17
|
+
|
|
18
|
+
const slotKey = (s) => `${s.list.at}:${"step" in s.list ? s.list.step : ""}:${s.index}`;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* def the definition: { trigger, steps }
|
|
22
|
+
* triggerWords the trigger in words
|
|
23
|
+
* selectedId a step id, "trigger", or null
|
|
24
|
+
* onSelect(id)
|
|
25
|
+
* overlay a run's overlayOf(), or null
|
|
26
|
+
* editable, onInsert(slot, kind) the "Add a step" buttons
|
|
27
|
+
* flagged a Set of step ids with something to fix
|
|
28
|
+
* runnable a Set of kinds the platform runs
|
|
29
|
+
*/
|
|
30
|
+
export function StepOutline({ def, triggerWords, selectedId = null, onSelect, overlay = null, editable = false, onInsert, flagged, runnable }) {
|
|
31
|
+
const [open, setOpen] = useState(null);
|
|
32
|
+
|
|
33
|
+
const adder = (slot, label = "Add a step") => {
|
|
34
|
+
if (!editable || !onInsert) return null;
|
|
35
|
+
const key = slotKey(slot);
|
|
36
|
+
return open === key ? (
|
|
37
|
+
<StepPicker
|
|
38
|
+
inline
|
|
39
|
+
runnable={runnable}
|
|
40
|
+
onClose={() => setOpen(null)}
|
|
41
|
+
onPick={(kind) => {
|
|
42
|
+
setOpen(null);
|
|
43
|
+
onInsert(slot, kind);
|
|
44
|
+
}}
|
|
45
|
+
/>
|
|
46
|
+
) : (
|
|
47
|
+
<button type="button" onClick={() => setOpen(key)} className="inline-flex min-h-9 items-center gap-1.5 rounded-md border border-dashed border-border px-2.5 text-[13px] text-muted-foreground hover:border-solid hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring">
|
|
48
|
+
<span aria-hidden="true">+</span> {label}
|
|
49
|
+
</button>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const row = (step, state) => {
|
|
54
|
+
const info = KINDS[step.kind] ?? KINDS.action;
|
|
55
|
+
const sum = overlay?.summary.get(step.id);
|
|
56
|
+
const sel = selectedId === step.id;
|
|
57
|
+
return (
|
|
58
|
+
<button
|
|
59
|
+
type="button"
|
|
60
|
+
aria-pressed={sel}
|
|
61
|
+
onClick={() => onSelect?.(step.id)}
|
|
62
|
+
className={cx(
|
|
63
|
+
"flex w-full min-w-0 items-center gap-2.5 rounded-lg border bg-card px-2.5 py-2 text-left text-card-foreground hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
64
|
+
sel ? "border-foreground" : flagged?.has(step.id) || state === "failed" ? "border-destructive" : state === "would" ? "border-primary/70" : "border-border",
|
|
65
|
+
state === "notreached" && "border-dashed opacity-60",
|
|
66
|
+
state === "would" && "border-dashed",
|
|
67
|
+
)}
|
|
68
|
+
>
|
|
69
|
+
<span className={cx("inline-flex size-9 shrink-0 items-center justify-center rounded-lg border border-border", FACE[info.tone] ?? FACE.plain)}>
|
|
70
|
+
<Glyph name={info.glyph} size={18} />
|
|
71
|
+
</span>
|
|
72
|
+
<span className="grid min-w-0 flex-1 gap-0.5">
|
|
73
|
+
<b className="break-words text-[13px] font-semibold">{stepTitle(step)}</b>
|
|
74
|
+
<span className="break-words text-xs text-muted-foreground">{stepSub(step)}</span>
|
|
75
|
+
{sum?.fix ? <span className="text-xs text-primary">{fixTag(sum.fix)}</span> : null}
|
|
76
|
+
{sum && state === "failed" && sum.words ? <span className="text-xs text-destructive">{sum.words}</span> : null}
|
|
77
|
+
{sum?.unchecked ? <span className="text-xs text-muted-foreground">Not checked</span> : sum && state === "would" && sum.would ? <span className="text-xs text-primary">{sum.would}</span> : null}
|
|
78
|
+
</span>
|
|
79
|
+
{state && STATE_ICON[state] ? (
|
|
80
|
+
<span className={cx("inline-flex size-5 shrink-0 items-center justify-center rounded-full text-background", STATE_TONE[state])}>
|
|
81
|
+
<Icon name={STATE_ICON[state]} size={13} className={state === "running" ? "animate-spin motion-reduce:animate-none" : undefined} />
|
|
82
|
+
</span>
|
|
83
|
+
) : null}
|
|
84
|
+
</button>
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const list = (steps, ref) => (
|
|
89
|
+
<ol className="m-0 grid list-none gap-1.5 p-0">
|
|
90
|
+
{steps.map((step, i) => {
|
|
91
|
+
const state = overlay ? (overlay.nodes.get(step.id) ?? "notreached") : null;
|
|
92
|
+
return (
|
|
93
|
+
<li key={step.id} className="grid gap-1.5">
|
|
94
|
+
{adder({ list: ref, index: i })}
|
|
95
|
+
{row(step, state)}
|
|
96
|
+
{step.kind === "condition" ? (
|
|
97
|
+
<div className="grid gap-2 border-l-2 border-border pl-3">
|
|
98
|
+
<div className="grid gap-1.5">
|
|
99
|
+
<h5 className="m-0 text-xs font-semibold text-muted-foreground">Yes</h5>
|
|
100
|
+
{list(step.then ?? [], { at: "then", step: step.id })}
|
|
101
|
+
</div>
|
|
102
|
+
<div className="grid gap-1.5">
|
|
103
|
+
<h5 className="m-0 text-xs font-semibold text-muted-foreground">No</h5>
|
|
104
|
+
{list(step.else ?? [], { at: "else", step: step.id })}
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
) : null}
|
|
108
|
+
{step.kind === "loop" ? <div className="grid gap-1.5 border-l-2 border-dashed border-border pl-3">{list(step.steps ?? [], { at: "loop", step: step.id })}</div> : null}
|
|
109
|
+
</li>
|
|
110
|
+
);
|
|
111
|
+
})}
|
|
112
|
+
<li className="grid gap-1.5">{adder({ list: ref, index: steps.length }, steps.length === 0 ? "Add the first step" : "Add a step")}</li>
|
|
113
|
+
</ol>
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
const triggerType = def.trigger?.type;
|
|
117
|
+
return (
|
|
118
|
+
<div className="grid min-w-0 content-start gap-3 p-3">
|
|
119
|
+
<div className={cx("flex min-w-0 items-center gap-2.5 rounded-lg border bg-card px-2.5 py-2 text-card-foreground", selectedId === "trigger" ? "border-foreground" : "border-border")}>
|
|
120
|
+
<span className="inline-flex size-9 shrink-0 items-center justify-center rounded-lg border border-border text-muted-foreground">
|
|
121
|
+
<Glyph name={triggerGlyph(triggerType)} size={18} />
|
|
122
|
+
</span>
|
|
123
|
+
<span className="grid min-w-0 flex-1 gap-0.5">
|
|
124
|
+
<b className="text-[13px] font-semibold">{triggerTitle(triggerType)}</b>
|
|
125
|
+
<span className="text-xs text-muted-foreground">{triggerWords}</span>
|
|
126
|
+
</span>
|
|
127
|
+
<button type="button" aria-pressed={selectedId === "trigger"} onClick={() => onSelect?.("trigger")} className="rounded-md px-2 py-1 text-[13px] text-muted-foreground underline hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring">
|
|
128
|
+
Edit
|
|
129
|
+
</button>
|
|
130
|
+
</div>
|
|
131
|
+
{list(def.steps ?? [], { at: "root" })}
|
|
132
|
+
</div>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# LibraryShelf
|
|
2
|
+
|
|
3
|
+
"Start from a template": a shelf of starting points from the Library, in
|
|
4
|
+
a few named columns. Each item is its mark or cover, its name, one muted
|
|
5
|
+
fact ("6 stages", "PDF") and a **Use** at its end.
|
|
6
|
+
|
|
7
|
+
Use is your async callback. While it runs the button spins; then the
|
|
8
|
+
item says **Used**; a failure shows its reason under the item. A heading
|
|
9
|
+
names the shelf and where its items come from.
|
|
10
|
+
|
|
11
|
+
Grown in the CRM (0.7.0) as "Start from a template · CRM starting points,
|
|
12
|
+
from the Library". Every app ships its first Library collection; this is
|
|
13
|
+
where it shows.
|
|
14
|
+
|
|
15
|
+
```jsx
|
|
16
|
+
import { LibraryShelf } from "../components/blocks/library-shelf"; // a Makersclaw app ("./blocks/library-shelf" from components/)
|
|
17
|
+
import { LibraryShelf } from "@/components/makersclaw/blocks/library-shelf"; // a shadcn project
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
A client component. The Example is at the end.
|
|
21
|
+
|
|
22
|
+
## Which hook feeds it
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
const library = useLibraryItems("crm-starting-points"); // lib/hooks.js
|
|
26
|
+
// → { items, total, previewOnly, hasMore, loadMore, loading, loadingMore, error, unavailable, … }
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`useLibraryItems(id)` reads `GET _claw/library/collections/<id>/items`:
|
|
30
|
+
a files collection's items, `{ id, title, description, tags, format,
|
|
31
|
+
preview_url, locked, … }`, a page at a time. Give the whole answer to
|
|
32
|
+
`library`. On a Free plan a paid collection is a preview: the shelf says
|
|
33
|
+
how many more come with a paid plan, and a `locked` item has no Use.
|
|
34
|
+
|
|
35
|
+
## Props
|
|
36
|
+
|
|
37
|
+
Two ways in: `library` with `onUse(item)`, or `sections` you shaped.
|
|
38
|
+
|
|
39
|
+
### From the Library
|
|
40
|
+
|
|
41
|
+
- `library` — what `useLibraryItems()` returns. The shelf draws one
|
|
42
|
+
column of its items, each with its cover (`preview_url`) or its
|
|
43
|
+
format's glyph, and the format as its fact.
|
|
44
|
+
- `onUse(item)` — async, answers `{ ok: true }` or `{ ok: false, error }`
|
|
45
|
+
(a throw counts as failed). What Use does with a Library item: ask the
|
|
46
|
+
agent to copy it in, open it as a draft. The Library door never writes;
|
|
47
|
+
a copy into the workspace is the agent's.
|
|
48
|
+
- `sectionTitle` — the column's title. Default none.
|
|
49
|
+
- `useLabel` — default "Use". `once` — `false`: an item can be used again.
|
|
50
|
+
- `empty` — the line when the collection has nothing. Default "Nothing on
|
|
51
|
+
this shelf yet."
|
|
52
|
+
|
|
53
|
+
Under the items: "Show more" when `hasMore`, "N more come with a paid
|
|
54
|
+
plan" on a preview, the error's words, or "The Library is not connected
|
|
55
|
+
yet." Skeleton lines while the first page is out.
|
|
56
|
+
|
|
57
|
+
### Sections you shaped
|
|
58
|
+
|
|
59
|
+
- `sections` — `[{ key, title, items }]`, side by side from 48rem of the
|
|
60
|
+
shelf's own width, stacked below. An item:
|
|
61
|
+
- `id`, `label` — the name.
|
|
62
|
+
- `meta` — one short muted fact after it ("6 stages"). It truncates, and
|
|
63
|
+
the label too: no row is wider than its box. Never a sentence.
|
|
64
|
+
- `description` — a sentence under the label, muted, wrapping onto as
|
|
65
|
+
many lines as it needs ("Turn a developer's line into a customer's").
|
|
66
|
+
- `mark` — a node before it (a stage's dot, an icon).
|
|
67
|
+
- `onUse()` — async `{ ok }`. Without it the item has no Use.
|
|
68
|
+
- `useLabel` — default "Use". `used` — already used. `usedLabel` —
|
|
69
|
+
default "Used". `once: false` — usable again.
|
|
70
|
+
- `locked` — a lock instead of Use.
|
|
71
|
+
|
|
72
|
+
`libraryShelfItems(items, { onUse, useLabel, once })` turns Library items
|
|
73
|
+
into shelf items, for a shelf of several collections.
|
|
74
|
+
|
|
75
|
+
### Both
|
|
76
|
+
|
|
77
|
+
- `title` — default "Start from a template".
|
|
78
|
+
- `source` — where the items come from ("CRM starting points, from the
|
|
79
|
+
Library"), muted after the title.
|
|
80
|
+
- `icon` — a kit `Icon` name before the title. Default `"layers"`.
|
|
81
|
+
- `className` — layout around it. Never colour.
|
|
82
|
+
|
|
83
|
+
## Who may do what
|
|
84
|
+
|
|
85
|
+
Any member who can open the app reads the shelf. A paid collection on a
|
|
86
|
+
Free plan shows its preview; the Upgrade is on the desk's Library window,
|
|
87
|
+
never in the app.
|
|
88
|
+
|
|
89
|
+
## Never
|
|
90
|
+
|
|
91
|
+
- Never read the Library inside the block: the items come in as props.
|
|
92
|
+
- Never keep a `file_url`: Use asks for the item when it is pressed.
|
|
93
|
+
- Never offer an Upgrade or a price here: money is the desk's.
|
|
94
|
+
|
|
95
|
+
## Example
|
|
96
|
+
|
|
97
|
+
The CRM's starting points on an empty board, straight from its
|
|
98
|
+
collection.
|
|
99
|
+
|
|
100
|
+
```jsx
|
|
101
|
+
"use client";
|
|
102
|
+
import { ask } from "../lib/platform";
|
|
103
|
+
import { useLibraryItems } from "../lib/hooks";
|
|
104
|
+
import { LibraryShelf } from "../components/blocks/library-shelf";
|
|
105
|
+
|
|
106
|
+
export default function StartFrom() {
|
|
107
|
+
const library = useLibraryItems("crm-starting-points", { limit: 12 });
|
|
108
|
+
return (
|
|
109
|
+
<LibraryShelf
|
|
110
|
+
source="CRM starting points, from the Library"
|
|
111
|
+
library={library}
|
|
112
|
+
sectionTitle="Sales processes"
|
|
113
|
+
onUse={(item) => ask({ text: `Set up the sales process "${item.title}" from the Library (item ${item.id}).` })}
|
|
114
|
+
/>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "library-shelf",
|
|
3
|
+
"title": "LibraryShelf",
|
|
4
|
+
"description": "A shelf of starting points from the Library, in a few named columns (sales processes, follow-up emails, saved searches): each item its mark or cover, its name, one muted fact (\"6 stages\", \"PDF\") and a Use at its end. Use goes out through an async callback that answers { ok }; the item says it is being used, then Used, and the reason in place when it fails. It takes sections you shaped, or what useLibraryItems() returns (lib/hooks.js) with onUse(item): the collection's items with their covers, Show more, \"N more come with a paid plan\" on a preview, locked items without a Use, and a quiet \"not connected yet\".",
|
|
5
|
+
"useWhen": "An empty or new screen that offers the app's own starting points from the Library (\"Start from a template\"): a CRM's starting processes and follow-up emails, a planner's templates, a deck app's starter decks, a growth app's examples. Not for a gallery of large previews (a card grid over useLibraryItems), a picker inside a form (Combobox) or the records themselves.",
|
|
6
|
+
"tags": [
|
|
7
|
+
"library",
|
|
8
|
+
"templates",
|
|
9
|
+
"starting points",
|
|
10
|
+
"shelf",
|
|
11
|
+
"empty state",
|
|
12
|
+
"use",
|
|
13
|
+
"collection",
|
|
14
|
+
"onboarding",
|
|
15
|
+
"examples",
|
|
16
|
+
"start from"
|
|
17
|
+
],
|
|
18
|
+
"exports": [
|
|
19
|
+
"LibraryShelf",
|
|
20
|
+
"libraryShelfItems"
|
|
21
|
+
],
|
|
22
|
+
"files": [
|
|
23
|
+
"index.js",
|
|
24
|
+
"library-shelf.jsx",
|
|
25
|
+
"probe-rules.js"
|
|
26
|
+
],
|
|
27
|
+
"requires": []
|
|
28
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// LibraryShelf with sections, and straight from fake useLibraryItems()
|
|
4
|
+
// answers: a preview with more to load, loading, empty, refused and not
|
|
5
|
+
// connected yet — the gallery's and the smoke test's surface. Not
|
|
6
|
+
// installed into apps.
|
|
7
|
+
import { useState } from "react";
|
|
8
|
+
import { Icon } from "../components/ui";
|
|
9
|
+
import { LibraryShelf } from "../components/blocks/library-shelf";
|
|
10
|
+
|
|
11
|
+
const wait = (ms, r) => new Promise((res) => setTimeout(() => res(r), ms));
|
|
12
|
+
const DOTS = { "chart-1": "bg-chart-1", "chart-4": "bg-chart-4" };
|
|
13
|
+
const dot = (tone) => <span aria-hidden="true" className={`size-[7px] shrink-0 rounded-full ${DOTS[tone]}`} />;
|
|
14
|
+
|
|
15
|
+
const ITEMS = [
|
|
16
|
+
{ id: "i1", title: "Seed deck, 12 slides", format: "slides", preview_url: null, locked: false },
|
|
17
|
+
{ id: "i2", title: "Investor update, monthly", format: "document", locked: false },
|
|
18
|
+
{ id: "i3", title: "A landing page for a waitlist with a very long name that has to truncate", format: "html", locked: false },
|
|
19
|
+
{ id: "i4", title: "Pricing page teardown", format: "pdf", locked: true },
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const base = { total: null, previewOnly: false, hasMore: false, loading: false, loadingMore: false, error: null, unavailable: false, loadMore: () => undefined };
|
|
23
|
+
|
|
24
|
+
export default function LibraryShelfDemo() {
|
|
25
|
+
const [items, setItems] = useState(ITEMS.slice(0, 3));
|
|
26
|
+
const [loadingMore, setLoadingMore] = useState(false);
|
|
27
|
+
const library = {
|
|
28
|
+
...base,
|
|
29
|
+
items,
|
|
30
|
+
total: 9,
|
|
31
|
+
previewOnly: true,
|
|
32
|
+
hasMore: items.length < 4,
|
|
33
|
+
loadingMore,
|
|
34
|
+
loadMore: () => {
|
|
35
|
+
setLoadingMore(true);
|
|
36
|
+
setTimeout(() => {
|
|
37
|
+
setItems(ITEMS);
|
|
38
|
+
setLoadingMore(false);
|
|
39
|
+
}, 700);
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
return (
|
|
43
|
+
<div className="grid gap-8 p-6">
|
|
44
|
+
<LibraryShelf
|
|
45
|
+
source="CRM starting points, from the Library"
|
|
46
|
+
sections={[
|
|
47
|
+
{
|
|
48
|
+
key: "p",
|
|
49
|
+
title: "Sales processes",
|
|
50
|
+
items: [
|
|
51
|
+
{ id: "a", label: "Selling yearly plans", meta: "6 stages", mark: dot("chart-1"), onUse: () => wait(600) },
|
|
52
|
+
{ id: "b", label: "A funding round", meta: "7 stages", description: "From the first intro to the wire: investors, diligence and the close, one stage each.", mark: dot("chart-4"), onUse: () => wait(400, { ok: false, error: "There is already a process called A funding round." }) },
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
{ key: "e", title: "Follow-up emails", items: [{ id: "c", label: "After a demo", mark: <Icon name="mail" size={14} className="text-muted-foreground" />, onUse: () => wait(300), once: false, useLabel: "Look" }] },
|
|
56
|
+
{ key: "s", title: "Saved searches", items: [{ id: "d", label: "Waiting on you", mark: <Icon name="filter" size={14} className="text-muted-foreground" />, used: true }] },
|
|
57
|
+
]}
|
|
58
|
+
/>
|
|
59
|
+
<LibraryShelf title="Start from a deck" source="Pitch decks, from the Library" icon="file" sectionTitle="Decks and updates" library={library} onUse={() => wait(600, { ok: true })} />
|
|
60
|
+
<div className="grid gap-6 [grid-template-columns:repeat(auto-fit,minmax(min(100%,18rem),1fr))]">
|
|
61
|
+
<LibraryShelf title="Loading" library={{ ...base, items: [], loading: true }} onUse={() => undefined} />
|
|
62
|
+
<LibraryShelf title="Empty" library={{ ...base, items: [] }} onUse={() => undefined} />
|
|
63
|
+
<LibraryShelf title="Refused" library={{ ...base, items: [], error: "This collection is not on your workspace's shelf." }} onUse={() => undefined} />
|
|
64
|
+
<LibraryShelf title="Not connected yet" library={{ ...base, items: [], unavailable: true }} onUse={() => undefined} />
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|