makersclaw 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -3
- package/package.json +1 -1
- package/r/activity-log.json +52 -0
- package/r/agent-changes.json +51 -0
- package/r/agent-chat.json +53 -0
- package/r/agent-column.json +85 -0
- package/r/agent-dock.json +57 -0
- package/r/agent-kit.json +49 -0
- package/r/agent-marks.json +49 -0
- package/r/agent-pane.json +93 -0
- package/r/ai-action.json +56 -0
- package/r/annotate.json +3 -3
- package/r/approval-request.json +2 -2
- package/r/approval.json +54 -0
- package/r/ask-bar.json +50 -0
- package/r/audio-player.json +2 -2
- package/r/auth-form.json +2 -2
- package/r/automation-editor.json +66 -0
- package/r/automation-list.json +57 -0
- package/r/automation-model.json +118 -0
- package/r/automations-screen.json +71 -0
- package/r/board.json +2 -2
- package/r/calendar-view.json +3 -3
- package/r/cart.json +2 -2
- package/r/chart.json +2 -2
- package/r/chat-pane.json +84 -0
- package/r/chat.json +2 -2
- package/r/code-editor.json +2 -2
- package/r/comment-box.json +3 -3
- package/r/connect-card.json +51 -0
- package/r/contact-section.json +2 -2
- package/r/core.json +13 -7
- package/r/cost.json +51 -0
- package/r/csv-import.json +3 -3
- package/r/data-picker.json +60 -0
- package/r/data-table.json +2 -2
- package/r/diagram.json +2 -2
- package/r/doc-editor.json +2 -2
- package/r/doc-page.json +3 -3
- package/r/email-render.json +2 -2
- package/r/faq.json +2 -2
- package/r/file-upload.json +2 -2
- package/r/first-run.json +63 -0
- package/r/fix-note.json +57 -0
- package/r/flow-canvas.json +2 -2
- package/r/flow-editor.json +79 -0
- package/r/footer.json +2 -2
- package/r/gantt.json +3 -3
- package/r/job-panel.json +2 -2
- package/r/json-editor.json +3 -3
- package/r/library-shelf.json +56 -0
- package/r/map.json +2 -2
- package/r/margin-notes.json +67 -0
- package/r/menubar.json +2 -2
- package/r/mind-map.json +3 -3
- package/r/navigation-menu.json +2 -2
- package/r/needs-you.json +55 -0
- package/r/order-detail.json +2 -2
- package/r/org-chart.json +3 -3
- package/r/pace-bar.json +69 -0
- package/r/page-builder.json +2 -2
- package/r/payment-methods.json +2 -2
- package/r/pdf-document.json +2 -2
- package/r/pdf-viewer.json +2 -2
- package/r/product-detail.json +2 -2
- package/r/product-grid.json +3 -3
- package/r/record-form.json +2 -2
- package/r/record-grid.json +52 -0
- package/r/record-notes.json +4 -4
- package/r/registry.json +4086 -1512
- package/r/run-history.json +52 -0
- package/r/run-view.json +80 -0
- package/r/schedule-card.json +54 -0
- package/r/schedule-engine.json +79 -0
- package/r/schedule-gantt.json +83 -0
- package/r/schedule-ruler.json +64 -0
- package/r/search-dialog.json +2 -2
- package/r/settings-list.json +2 -2
- package/r/setup-checklist.json +2 -2
- package/r/share-dialog.json +2 -2
- package/r/sidebar.json +2 -2
- package/r/signature-pad.json +2 -2
- package/r/sketch-canvas.json +2 -2
- package/r/source-receipt.json +52 -0
- package/r/spreadsheet.json +3 -3
- package/r/stat-cards.json +3 -3
- package/r/step-picker.json +53 -0
- package/r/step-settings.json +72 -0
- package/r/suggestion.json +58 -0
- package/r/sum-bar.json +64 -0
- package/r/task-list.json +3 -3
- package/r/task-model.json +2 -2
- package/r/testimonials.json +2 -2
- package/r/tier-map.json +67 -0
- package/r/time-grid.json +73 -0
- package/r/time-strip.json +64 -0
- package/r/top-bar.json +66 -0
- package/r/transaction-list.json +2 -2
- package/r/trigger-picker.json +61 -0
- package/r/whiteboard.json +3 -3
- package/r/workflow-view.json +55 -0
- package/registry/blocks/activity-log/README.md +112 -0
- package/registry/blocks/activity-log/activity-log.jsx +351 -0
- package/registry/blocks/activity-log/block.json +33 -0
- package/registry/blocks/activity-log/demo.jsx +48 -0
- package/registry/blocks/activity-log/index.js +4 -0
- package/registry/blocks/agent-changes/README.md +94 -0
- package/registry/blocks/agent-changes/agent-changes.jsx +118 -0
- package/registry/blocks/agent-changes/block.json +28 -0
- package/registry/blocks/agent-changes/demo.jsx +39 -0
- package/registry/blocks/agent-changes/index.js +3 -0
- package/registry/blocks/agent-chat/README.md +145 -0
- package/registry/blocks/agent-chat/agent-chat.jsx +415 -0
- package/registry/blocks/agent-chat/block.json +30 -0
- package/registry/blocks/agent-chat/demo.jsx +165 -0
- package/registry/blocks/agent-chat/index.js +3 -0
- package/registry/blocks/agent-column/README.md +197 -0
- package/registry/blocks/agent-column/agent-column.jsx +492 -0
- package/registry/blocks/agent-column/block.json +67 -0
- package/registry/blocks/agent-column/column-picker.jsx +163 -0
- package/registry/blocks/agent-column/demo.jsx +50 -0
- package/registry/blocks/agent-column/index.js +7 -0
- package/registry/blocks/agent-column/model.js +164 -0
- package/registry/blocks/agent-column/receipt.jsx +483 -0
- package/registry/blocks/agent-column/spreadsheet-type.jsx +24 -0
- package/registry/blocks/agent-dock/README.md +210 -0
- package/registry/blocks/agent-dock/agent-dock.jsx +356 -0
- package/registry/blocks/agent-dock/block.json +47 -0
- package/registry/blocks/agent-dock/demo.jsx +153 -0
- package/registry/blocks/agent-dock/dock-model.js +118 -0
- package/registry/blocks/agent-dock/index.js +5 -0
- package/registry/blocks/agent-kit/README.md +73 -0
- package/registry/blocks/agent-kit/agent-kit.jsx +86 -0
- package/registry/blocks/agent-kit/block.json +10 -0
- package/registry/blocks/agent-kit/demo.jsx +35 -0
- package/registry/blocks/agent-kit/index.js +3 -0
- package/registry/blocks/agent-marks/README.md +80 -0
- package/registry/blocks/agent-marks/agent-marks.jsx +133 -0
- package/registry/blocks/agent-marks/block.json +27 -0
- package/registry/blocks/agent-marks/demo.jsx +49 -0
- package/registry/blocks/agent-marks/index.js +3 -0
- package/registry/blocks/agent-pane/README.md +225 -0
- package/registry/blocks/agent-pane/agent-pane.jsx +244 -0
- package/registry/blocks/agent-pane/block.json +41 -0
- package/registry/blocks/agent-pane/composer.jsx +6 -0
- package/registry/blocks/agent-pane/demo.jsx +233 -0
- package/registry/blocks/agent-pane/dock-context.js +10 -0
- package/registry/blocks/agent-pane/index.js +4 -0
- package/registry/blocks/agent-pane/prose.jsx +84 -0
- package/registry/blocks/agent-pane/questions.jsx +134 -0
- package/registry/blocks/agent-pane/transcript.jsx +268 -0
- package/registry/blocks/agent-pane/work.jsx +368 -0
- package/registry/blocks/ai-action/README.md +94 -0
- package/registry/blocks/ai-action/ai-action.jsx +188 -0
- package/registry/blocks/ai-action/block.json +34 -0
- package/registry/blocks/ai-action/demo.jsx +93 -0
- package/registry/blocks/ai-action/index.js +3 -0
- package/registry/blocks/approval/README.md +145 -0
- package/registry/blocks/approval/approval.jsx +163 -0
- package/registry/blocks/approval/block.json +31 -0
- package/registry/blocks/approval/demo.jsx +121 -0
- package/registry/blocks/approval/index.js +3 -0
- package/registry/blocks/ask-bar/README.md +105 -0
- package/registry/blocks/ask-bar/ask-bar.jsx +166 -0
- package/registry/blocks/ask-bar/block.json +26 -0
- package/registry/blocks/ask-bar/demo.jsx +78 -0
- package/registry/blocks/ask-bar/index.js +3 -0
- package/registry/blocks/automation-editor/README.md +291 -0
- package/registry/blocks/automation-editor/automation-editor.jsx +656 -0
- package/registry/blocks/automation-editor/block.json +39 -0
- package/registry/blocks/automation-editor/demo.jsx +284 -0
- package/registry/blocks/automation-editor/index.js +4 -0
- package/registry/blocks/automation-editor/run-inputs.jsx +82 -0
- package/registry/blocks/automation-list/README.md +129 -0
- package/registry/blocks/automation-list/automation-list.jsx +476 -0
- package/registry/blocks/automation-list/block.json +35 -0
- package/registry/blocks/automation-list/demo.jsx +67 -0
- package/registry/blocks/automation-list/index.js +3 -0
- package/registry/blocks/automation-model/README.md +109 -0
- package/registry/blocks/automation-model/block.json +135 -0
- package/registry/blocks/automation-model/catalog.js +256 -0
- package/registry/blocks/automation-model/check.js +161 -0
- package/registry/blocks/automation-model/demo.jsx +42 -0
- package/registry/blocks/automation-model/edit.js +262 -0
- package/registry/blocks/automation-model/fixes.js +287 -0
- package/registry/blocks/automation-model/glyph.jsx +13 -0
- package/registry/blocks/automation-model/graph.js +410 -0
- package/registry/blocks/automation-model/index.js +33 -0
- package/registry/blocks/automation-model/overlay.js +175 -0
- package/registry/blocks/automation-model/parts.jsx +94 -0
- package/registry/blocks/automation-model/schedule.js +53 -0
- package/registry/blocks/automation-model/templates.js +128 -0
- package/registry/blocks/automation-model/values.js +29 -0
- package/registry/blocks/automation-model/words.js +439 -0
- package/registry/blocks/automations-screen/README.md +207 -0
- package/registry/blocks/automations-screen/automations-screen.jsx +452 -0
- package/registry/blocks/automations-screen/block.json +47 -0
- package/registry/blocks/automations-screen/demo.jsx +364 -0
- package/registry/blocks/automations-screen/index.js +4 -0
- package/registry/blocks/automations-screen/screen-model.js +129 -0
- package/registry/blocks/chat-pane/README.md +143 -0
- package/registry/blocks/chat-pane/block.json +48 -0
- package/registry/blocks/chat-pane/chat-attach.js +166 -0
- package/registry/blocks/chat-pane/chat-composer.jsx +195 -0
- package/registry/blocks/chat-pane/chat-context.js +17 -0
- package/registry/blocks/chat-pane/chat-model.js +43 -0
- package/registry/blocks/chat-pane/chat-pane.jsx +398 -0
- package/registry/blocks/chat-pane/chats-list.jsx +52 -0
- package/registry/blocks/chat-pane/demo.jsx +103 -0
- package/registry/blocks/chat-pane/index.js +9 -0
- package/registry/blocks/connect-card/README.md +99 -0
- package/registry/blocks/connect-card/block.json +10 -0
- package/registry/blocks/connect-card/connect-card.jsx +103 -0
- package/registry/blocks/connect-card/demo.jsx +49 -0
- package/registry/blocks/connect-card/index.js +3 -0
- package/registry/blocks/cost/README.md +115 -0
- package/registry/blocks/cost/block.json +10 -0
- package/registry/blocks/cost/cost.jsx +176 -0
- package/registry/blocks/cost/demo.jsx +66 -0
- package/registry/blocks/cost/index.js +3 -0
- package/registry/blocks/data-picker/README.md +81 -0
- package/registry/blocks/data-picker/block.json +38 -0
- package/registry/blocks/data-picker/data-picker.jsx +210 -0
- package/registry/blocks/data-picker/demo.jsx +34 -0
- package/registry/blocks/data-picker/fields.js +89 -0
- package/registry/blocks/data-picker/index.js +4 -0
- package/registry/blocks/data-table/demo.jsx +1 -1
- package/registry/blocks/first-run/README.md +118 -0
- package/registry/blocks/first-run/block.json +11 -0
- package/registry/blocks/first-run/demo.jsx +88 -0
- package/registry/blocks/first-run/first-run-model.js +51 -0
- package/registry/blocks/first-run/first-run.jsx +156 -0
- package/registry/blocks/first-run/index.js +5 -0
- package/registry/blocks/first-run/probe-rules.js +26 -0
- package/registry/blocks/fix-note/README.md +79 -0
- package/registry/blocks/fix-note/block.json +35 -0
- package/registry/blocks/fix-note/demo.jsx +30 -0
- package/registry/blocks/fix-note/fix-note.jsx +235 -0
- package/registry/blocks/fix-note/index.js +3 -0
- package/registry/blocks/flow-editor/README.md +108 -0
- package/registry/blocks/flow-editor/block.json +44 -0
- package/registry/blocks/flow-editor/demo.jsx +115 -0
- package/registry/blocks/flow-editor/flow-canvas.jsx +568 -0
- package/registry/blocks/flow-editor/flow-editor.jsx +154 -0
- package/registry/blocks/flow-editor/index.js +6 -0
- package/registry/blocks/flow-editor/minimap.jsx +78 -0
- package/registry/blocks/flow-editor/outline.jsx +134 -0
- package/registry/blocks/library-shelf/README.md +117 -0
- package/registry/blocks/library-shelf/block.json +28 -0
- package/registry/blocks/library-shelf/demo.jsx +68 -0
- package/registry/blocks/library-shelf/index.js +3 -0
- package/registry/blocks/library-shelf/library-shelf.jsx +211 -0
- package/registry/blocks/library-shelf/probe-rules.js +15 -0
- package/registry/blocks/margin-notes/README.md +222 -0
- package/registry/blocks/margin-notes/block.json +10 -0
- package/registry/blocks/margin-notes/demo.jsx +144 -0
- package/registry/blocks/margin-notes/index.js +20 -0
- package/registry/blocks/margin-notes/margin-model.js +224 -0
- package/registry/blocks/margin-notes/margin-notes.jsx +465 -0
- package/registry/blocks/needs-you/README.md +177 -0
- package/registry/blocks/needs-you/block.json +34 -0
- package/registry/blocks/needs-you/demo.jsx +143 -0
- package/registry/blocks/needs-you/index.js +3 -0
- package/registry/blocks/needs-you/needs-you.jsx +441 -0
- package/registry/blocks/pace-bar/README.md +230 -0
- package/registry/blocks/pace-bar/block.json +10 -0
- package/registry/blocks/pace-bar/demo.jsx +255 -0
- package/registry/blocks/pace-bar/index.js +22 -0
- package/registry/blocks/pace-bar/pace-bar.jsx +461 -0
- package/registry/blocks/pace-bar/pace-model.js +288 -0
- package/registry/blocks/record-grid/README.md +100 -0
- package/registry/blocks/record-grid/block.json +28 -0
- package/registry/blocks/record-grid/demo.jsx +43 -0
- package/registry/blocks/record-grid/index.js +3 -0
- package/registry/blocks/record-grid/record-grid.jsx +251 -0
- package/registry/blocks/run-history/README.md +138 -0
- package/registry/blocks/run-history/block.json +10 -0
- package/registry/blocks/run-history/demo.jsx +74 -0
- package/registry/blocks/run-history/index.js +3 -0
- package/registry/blocks/run-history/run-history.jsx +218 -0
- package/registry/blocks/run-view/README.md +174 -0
- package/registry/blocks/run-view/block.json +50 -0
- package/registry/blocks/run-view/demo.jsx +286 -0
- package/registry/blocks/run-view/index.js +5 -0
- package/registry/blocks/run-view/run-controls.jsx +266 -0
- package/registry/blocks/run-view/run-view.jsx +236 -0
- package/registry/blocks/run-view/runs-list.jsx +99 -0
- package/registry/blocks/schedule-card/README.md +121 -0
- package/registry/blocks/schedule-card/block.json +10 -0
- package/registry/blocks/schedule-card/demo.jsx +82 -0
- package/registry/blocks/schedule-card/index.js +3 -0
- package/registry/blocks/schedule-card/schedule-card.jsx +136 -0
- package/registry/blocks/schedule-engine/README.md +137 -0
- package/registry/blocks/schedule-engine/block.json +10 -0
- package/registry/blocks/schedule-engine/demo.jsx +82 -0
- package/registry/blocks/schedule-engine/engine.js +769 -0
- package/registry/blocks/schedule-engine/index.js +9 -0
- package/registry/blocks/schedule-engine/ops.js +71 -0
- package/registry/blocks/schedule-engine/place.js +294 -0
- package/registry/blocks/schedule-engine/tree.js +79 -0
- package/registry/blocks/schedule-engine/waits.js +80 -0
- package/registry/blocks/schedule-gantt/README.md +159 -0
- package/registry/blocks/schedule-gantt/block.json +10 -0
- package/registry/blocks/schedule-gantt/demo.jsx +223 -0
- package/registry/blocks/schedule-gantt/gantt-model.js +219 -0
- package/registry/blocks/schedule-gantt/geometry.js +176 -0
- package/registry/blocks/schedule-gantt/index.js +26 -0
- package/registry/blocks/schedule-gantt/schedule-gantt.jsx +882 -0
- package/registry/blocks/schedule-gantt/sheet.jsx +372 -0
- package/registry/blocks/schedule-gantt/track.jsx +190 -0
- package/registry/blocks/schedule-ruler/README.md +148 -0
- package/registry/blocks/schedule-ruler/block.json +10 -0
- package/registry/blocks/schedule-ruler/demo.jsx +181 -0
- package/registry/blocks/schedule-ruler/index.js +21 -0
- package/registry/blocks/schedule-ruler/ruler-model.js +239 -0
- package/registry/blocks/schedule-ruler/schedule-ruler.jsx +548 -0
- package/registry/blocks/source-receipt/README.md +97 -0
- package/registry/blocks/source-receipt/block.json +30 -0
- package/registry/blocks/source-receipt/demo.jsx +71 -0
- package/registry/blocks/source-receipt/index.js +3 -0
- package/registry/blocks/source-receipt/source-receipt.jsx +107 -0
- package/registry/blocks/spreadsheet/spreadsheet.jsx +15 -1
- package/registry/blocks/step-picker/README.md +78 -0
- package/registry/blocks/step-picker/block.json +31 -0
- package/registry/blocks/step-picker/demo.jsx +23 -0
- package/registry/blocks/step-picker/index.js +3 -0
- package/registry/blocks/step-picker/step-picker.jsx +144 -0
- package/registry/blocks/step-settings/README.md +100 -0
- package/registry/blocks/step-settings/block.json +44 -0
- package/registry/blocks/step-settings/demo.jsx +93 -0
- package/registry/blocks/step-settings/fields.jsx +58 -0
- package/registry/blocks/step-settings/flow-settings.jsx +115 -0
- package/registry/blocks/step-settings/index.js +5 -0
- package/registry/blocks/step-settings/step-settings.jsx +290 -0
- package/registry/blocks/suggestion/README.md +171 -0
- package/registry/blocks/suggestion/block.json +40 -0
- package/registry/blocks/suggestion/demo.jsx +91 -0
- package/registry/blocks/suggestion/index.js +3 -0
- package/registry/blocks/suggestion/suggestion.jsx +436 -0
- package/registry/blocks/sum-bar/README.md +122 -0
- package/registry/blocks/sum-bar/block.json +10 -0
- package/registry/blocks/sum-bar/demo.jsx +110 -0
- package/registry/blocks/sum-bar/index.js +5 -0
- package/registry/blocks/sum-bar/sum-bar.jsx +209 -0
- package/registry/blocks/sum-bar/sum-model.js +126 -0
- package/registry/blocks/tier-map/README.md +217 -0
- package/registry/blocks/tier-map/block.json +10 -0
- package/registry/blocks/tier-map/demo.jsx +222 -0
- package/registry/blocks/tier-map/index.js +24 -0
- package/registry/blocks/tier-map/tier-map.jsx +574 -0
- package/registry/blocks/tier-map/tier-model.js +318 -0
- package/registry/blocks/time-grid/README.md +305 -0
- package/registry/blocks/time-grid/block.json +10 -0
- package/registry/blocks/time-grid/demo.jsx +279 -0
- package/registry/blocks/time-grid/grid-model.js +322 -0
- package/registry/blocks/time-grid/index.js +25 -0
- package/registry/blocks/time-grid/time-grid.jsx +1325 -0
- package/registry/blocks/time-strip/README.md +141 -0
- package/registry/blocks/time-strip/block.json +10 -0
- package/registry/blocks/time-strip/demo.jsx +129 -0
- package/registry/blocks/time-strip/index.js +19 -0
- package/registry/blocks/time-strip/strip-model.js +178 -0
- package/registry/blocks/time-strip/time-strip.jsx +260 -0
- package/registry/blocks/top-bar/README.md +149 -0
- package/registry/blocks/top-bar/block.json +35 -0
- package/registry/blocks/top-bar/demo.jsx +78 -0
- package/registry/blocks/top-bar/index.js +5 -0
- package/registry/blocks/top-bar/probe-rules.js +74 -0
- package/registry/blocks/top-bar/top-bar-model.js +152 -0
- package/registry/blocks/top-bar/top-bar.jsx +244 -0
- package/registry/blocks/trigger-picker/README.md +98 -0
- package/registry/blocks/trigger-picker/block.json +38 -0
- package/registry/blocks/trigger-picker/demo.jsx +59 -0
- package/registry/blocks/trigger-picker/index.js +3 -0
- package/registry/blocks/trigger-picker/trigger-picker.jsx +374 -0
- package/registry/blocks/workflow-view/README.md +123 -0
- package/registry/blocks/workflow-view/block.json +10 -0
- package/registry/blocks/workflow-view/demo.jsx +90 -0
- package/registry/blocks/workflow-view/index.js +3 -0
- package/registry/blocks/workflow-view/workflow-view.jsx +203 -0
- package/src/install.mjs +77 -16
- package/src/registry.mjs +3 -0
- package/templates/next/browser/lib/data.js +6 -1
- package/templates/next/files/README.md +627 -24
- package/templates/next/files/check/automation-format.ts +2103 -0
- package/templates/next/files/check/automation-package.ts +142 -0
- package/templates/next/files/check/run.mjs +968 -9
- package/templates/next/files/components/ui/field.jsx +17 -4
- package/templates/next/files/components/ui/icon.jsx +13 -0
- package/templates/next/files/components/ui/index.js +2 -1
- package/templates/next/files/components/ui/layout.jsx +77 -2
- package/templates/next/files/components/ui/notice.jsx +70 -0
- package/templates/next/files/components/ui/page.jsx +124 -0
- package/templates/next/files/components/ui/stat.jsx +22 -7
- package/templates/next/files/components/ui/toast.jsx +6 -3
- package/templates/next/files/lib/automations.js +660 -0
- package/templates/next/files/lib/claw.js +241 -5
- package/templates/next/files/lib/door.js +92 -0
- package/templates/next/files/lib/hooks.js +1627 -8
- package/templates/next/files/lib/platform.js +529 -21
- package/templates/next/template.json +13 -3
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// Mapping data from earlier steps into a field. The menu lists what a step
|
|
4
|
+
// may read: this run (when it started, the clock), this item (inside a
|
|
5
|
+
// for-each) and every earlier step's output, and where a real run left a
|
|
6
|
+
// sample, the fields inside it with a taste of their values. A press
|
|
7
|
+
// inserts `{{ steps.mail.output.messages }}` where the cursor was. The
|
|
8
|
+
// platform checks every reference when the automation is saved.
|
|
9
|
+
//
|
|
10
|
+
// DataInput is a labelled text field (one line or several) with the menu
|
|
11
|
+
// beside it; DataPairs is a list of name and value rows (a step's input,
|
|
12
|
+
// a Set's values), each value with its own menu. Props in, callbacks out;
|
|
13
|
+
// tokens only.
|
|
14
|
+
|
|
15
|
+
import { useId, useRef, useState } from "react";
|
|
16
|
+
import { Field, Input, Popover, Textarea, cx } from "../../ui";
|
|
17
|
+
import { textToValue, valueToText } from "../automation-model";
|
|
18
|
+
import { menuGroups } from "./fields";
|
|
19
|
+
|
|
20
|
+
const ROW = "flex w-full min-w-0 items-baseline justify-between gap-3 rounded-md px-2 py-1.5 text-left text-[13px] hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The menu itself, for a place of your own.
|
|
24
|
+
* available automation-model's availableBefore()
|
|
25
|
+
* samples { [stepId]: a real output } or null
|
|
26
|
+
* onPick(text)
|
|
27
|
+
*/
|
|
28
|
+
export function DataPicker({ available, samples = null, onPick }) {
|
|
29
|
+
const groups = menuGroups(available, samples);
|
|
30
|
+
return (
|
|
31
|
+
<div role="menu" aria-label="Insert data" className="grid gap-1">
|
|
32
|
+
{groups.map((g) => (
|
|
33
|
+
<section key={g.title} className="grid gap-px">
|
|
34
|
+
<h5 className="m-0 px-2 pt-1.5 text-2xs font-semibold uppercase tracking-wide text-muted-foreground">{g.title}</h5>
|
|
35
|
+
{g.items.map((it) =>
|
|
36
|
+
it.fields?.length ? (
|
|
37
|
+
<details key={it.text} className="group">
|
|
38
|
+
<summary className={cx(ROW, "cursor-pointer list-none")}>
|
|
39
|
+
<span className="min-w-0 truncate">{it.label}</span>
|
|
40
|
+
<span className="shrink-0 text-xs text-muted-foreground">{it.preview}</span>
|
|
41
|
+
</summary>
|
|
42
|
+
<div className="grid gap-px border-l border-border pl-2 ml-3">
|
|
43
|
+
<button type="button" role="menuitem" onClick={() => onPick(it.text)} className={ROW}>
|
|
44
|
+
<span className="min-w-0 truncate">Everything it made</span>
|
|
45
|
+
<span className="shrink-0 font-mono text-2xs text-muted-foreground">output</span>
|
|
46
|
+
</button>
|
|
47
|
+
{it.fields.map((f) => (
|
|
48
|
+
<button key={f.path} type="button" role="menuitem" onClick={() => onPick(f.text)} className={ROW}>
|
|
49
|
+
<span className="min-w-0 truncate font-mono text-xs">{f.label}</span>
|
|
50
|
+
<span className="shrink-0 text-xs text-muted-foreground">{f.preview}</span>
|
|
51
|
+
</button>
|
|
52
|
+
))}
|
|
53
|
+
</div>
|
|
54
|
+
</details>
|
|
55
|
+
) : (
|
|
56
|
+
<button key={it.text} type="button" role="menuitem" onClick={() => onPick(it.text)} className={ROW}>
|
|
57
|
+
<span className="min-w-0 truncate">{it.label}</span>
|
|
58
|
+
{it.preview ? <span className="shrink-0 text-xs text-muted-foreground">{it.preview}</span> : it.hint ? <span className="shrink-0 font-mono text-2xs text-muted-foreground">{it.hint}</span> : null}
|
|
59
|
+
</button>
|
|
60
|
+
),
|
|
61
|
+
)}
|
|
62
|
+
</section>
|
|
63
|
+
))}
|
|
64
|
+
<p className="m-0 px-2 pb-1 pt-0.5 text-xs text-muted-foreground">It goes in where the cursor is, and the step it comes from runs first.</p>
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** The { } button and its menu, for a field of your own. */
|
|
70
|
+
export function DataButton({ available, samples, onPick, disabled }) {
|
|
71
|
+
const [open, setOpen] = useState(false);
|
|
72
|
+
return (
|
|
73
|
+
<Popover
|
|
74
|
+
open={open}
|
|
75
|
+
onOpenChange={setOpen}
|
|
76
|
+
side="bottom"
|
|
77
|
+
align="end"
|
|
78
|
+
width="md"
|
|
79
|
+
label="Insert data"
|
|
80
|
+
className="max-h-80 overflow-y-auto p-1"
|
|
81
|
+
trigger={(props) => (
|
|
82
|
+
<button
|
|
83
|
+
{...props}
|
|
84
|
+
type="button"
|
|
85
|
+
disabled={disabled}
|
|
86
|
+
title="Insert data from an earlier step"
|
|
87
|
+
className="inline-flex size-9 shrink-0 items-center justify-center rounded-md border border-border bg-secondary font-mono text-xs text-secondary-foreground hover:bg-accent disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
88
|
+
>
|
|
89
|
+
{"{ }"}
|
|
90
|
+
</button>
|
|
91
|
+
)}
|
|
92
|
+
>
|
|
93
|
+
<DataPicker
|
|
94
|
+
available={available}
|
|
95
|
+
samples={samples}
|
|
96
|
+
onPick={(text) => {
|
|
97
|
+
onPick(text);
|
|
98
|
+
setOpen(false);
|
|
99
|
+
}}
|
|
100
|
+
/>
|
|
101
|
+
</Popover>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Put `text` into `value` where the caret was, and say where the caret goes. */
|
|
106
|
+
function splice(value, caret, text) {
|
|
107
|
+
const at = Math.min(caret?.start ?? value.length, value.length);
|
|
108
|
+
const end = Math.min(caret?.end ?? at, value.length);
|
|
109
|
+
return { next: `${value.slice(0, at)}${text}${value.slice(end)}`, caret: { start: at + text.length, end: at + text.length } };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* A labelled text field. With `available`, a { } button beside it.
|
|
114
|
+
* label, value, onChange(text), help, placeholder, mono, multiline, rows, disabled
|
|
115
|
+
* available automation-model's availableBefore(); leave out for plain text
|
|
116
|
+
* samples { [stepId]: a real output } or null
|
|
117
|
+
*/
|
|
118
|
+
export function DataInput({ label, value, onChange, help, placeholder, mono = false, multiline = false, rows = 4, available, samples, disabled = false }) {
|
|
119
|
+
const id = useId();
|
|
120
|
+
const caret = useRef(null);
|
|
121
|
+
const remember = (el) => {
|
|
122
|
+
caret.current = { start: el.selectionStart ?? el.value.length, end: el.selectionEnd ?? el.value.length };
|
|
123
|
+
};
|
|
124
|
+
const insert = (text) => {
|
|
125
|
+
const r = splice(value, caret.current, text);
|
|
126
|
+
caret.current = r.caret;
|
|
127
|
+
onChange(r.next);
|
|
128
|
+
};
|
|
129
|
+
const common = { id, value, disabled, placeholder, className: mono ? "font-mono text-[13px]" : undefined, onChange: (e) => onChange(e.target.value), onBlur: (e) => remember(e.currentTarget) };
|
|
130
|
+
return (
|
|
131
|
+
<Field id={id} label={label} hint={help}>
|
|
132
|
+
<div className="flex min-w-0 items-start gap-1.5">
|
|
133
|
+
<div className="min-w-0 flex-1">{multiline ? <Textarea {...common} rows={rows} /> : <Input {...common} type="text" />}</div>
|
|
134
|
+
{available && !disabled ? <DataButton available={available} samples={samples} onPick={insert} /> : null}
|
|
135
|
+
</div>
|
|
136
|
+
</Field>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* A list of name and value rows (a step's `input`, a Set's `values`).
|
|
142
|
+
* value { name: value }
|
|
143
|
+
* onChange(next)
|
|
144
|
+
* keyLabel, addLabel, help, available, samples, disabled
|
|
145
|
+
*/
|
|
146
|
+
export function DataPairs({ label, value, onChange, help, available, samples, disabled = false, keyLabel = "Name", addLabel = "Add a field" }) {
|
|
147
|
+
const entries = Object.entries(value ?? {});
|
|
148
|
+
const setAt = (i, key, text) => {
|
|
149
|
+
const next = entries.map(([k, v]) => [k, v]);
|
|
150
|
+
// A name another row already has would swallow it: keep what was typed apart.
|
|
151
|
+
if (text !== null && entries.some(([other], j) => j !== i && other === key)) return;
|
|
152
|
+
if (text === null) next.splice(i, 1);
|
|
153
|
+
else next[i] = [key, textToValue(text)];
|
|
154
|
+
onChange(Object.fromEntries(next));
|
|
155
|
+
};
|
|
156
|
+
return (
|
|
157
|
+
<fieldset className="m-0 grid min-w-0 gap-2 border-0 p-0">
|
|
158
|
+
<legend className="mb-1.5 p-0 text-sm font-medium text-foreground">{label}</legend>
|
|
159
|
+
{entries.length === 0 ? <p className="m-0 text-xs text-muted-foreground">Nothing yet.</p> : null}
|
|
160
|
+
{entries.map(([k, v], i) => (
|
|
161
|
+
<PairRow key={i} index={i} k={k} text={valueToText(v)} keyLabel={keyLabel} available={available} samples={samples} disabled={disabled} onChange={(key, text) => setAt(i, key, text)} onRemove={() => setAt(i, k, null)} />
|
|
162
|
+
))}
|
|
163
|
+
{!disabled ? (
|
|
164
|
+
<button
|
|
165
|
+
type="button"
|
|
166
|
+
onClick={() => {
|
|
167
|
+
let n = entries.length + 1;
|
|
168
|
+
while (value && `field_${n}` in value) n += 1;
|
|
169
|
+
onChange({ ...(value ?? {}), [`field_${n}`]: "" });
|
|
170
|
+
}}
|
|
171
|
+
className="inline-flex min-h-9 items-center justify-self-start rounded-md border border-border bg-secondary px-3 text-sm text-secondary-foreground hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
172
|
+
>
|
|
173
|
+
+ {addLabel}
|
|
174
|
+
</button>
|
|
175
|
+
) : null}
|
|
176
|
+
{help ? <p className="m-0 text-xs text-muted-foreground">{help}</p> : null}
|
|
177
|
+
</fieldset>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function PairRow({ index, k, text, keyLabel, available, samples, disabled, onChange, onRemove }) {
|
|
182
|
+
const caret = useRef(null);
|
|
183
|
+
const insert = (t) => {
|
|
184
|
+
const r = splice(text, caret.current, t);
|
|
185
|
+
caret.current = r.caret;
|
|
186
|
+
onChange(k, r.next);
|
|
187
|
+
};
|
|
188
|
+
return (
|
|
189
|
+
<div className="flex min-w-0 items-center gap-1.5">
|
|
190
|
+
<Input className="w-[34%] min-w-0 font-mono text-[13px]" aria-label={`${keyLabel} ${index + 1}`} value={k} disabled={disabled} onChange={(e) => onChange(e.target.value, text)} />
|
|
191
|
+
<Input
|
|
192
|
+
className="min-w-0 flex-1 font-mono text-[13px]"
|
|
193
|
+
aria-label={`Value ${index + 1}`}
|
|
194
|
+
value={text}
|
|
195
|
+
disabled={disabled}
|
|
196
|
+
placeholder="a value, or {{ data }}"
|
|
197
|
+
onChange={(e) => onChange(k, e.target.value)}
|
|
198
|
+
onBlur={(e) => {
|
|
199
|
+
caret.current = { start: e.currentTarget.selectionStart ?? text.length, end: e.currentTarget.selectionEnd ?? text.length };
|
|
200
|
+
}}
|
|
201
|
+
/>
|
|
202
|
+
{available && !disabled ? <DataButton available={available} samples={samples} onPick={insert} /> : null}
|
|
203
|
+
{!disabled ? (
|
|
204
|
+
<button type="button" aria-label={`Remove ${k || "field"}`} onClick={onRemove} className="inline-flex size-9 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring">
|
|
205
|
+
×
|
|
206
|
+
</button>
|
|
207
|
+
) : null}
|
|
208
|
+
</div>
|
|
209
|
+
);
|
|
210
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// DataInput and DataPairs over a pretend run — the gallery's and the smoke
|
|
4
|
+
// test's surface. Not installed into apps. Press { } beside a field: the
|
|
5
|
+
// menu lists this run, the item, the questions and the earlier steps, and
|
|
6
|
+
// opens a step to its fields with a taste of their values.
|
|
7
|
+
import { useState } from "react";
|
|
8
|
+
import { DataInput, DataPairs } from "../components/blocks/data-picker";
|
|
9
|
+
|
|
10
|
+
const AVAILABLE = {
|
|
11
|
+
steps: [
|
|
12
|
+
{ id: "mail", kind: "tool", label: "Read the mail since the last read" },
|
|
13
|
+
{ id: "touches", kind: "action", label: "Save who wrote" },
|
|
14
|
+
],
|
|
15
|
+
inLoop: true,
|
|
16
|
+
inputs: [{ name: "since", label: "Since when" }],
|
|
17
|
+
};
|
|
18
|
+
const OUTPUTS = {
|
|
19
|
+
mail: { count: 12, messages: [{ from: "ana@x.com", subject: "Quote for Q4", unread: true }] },
|
|
20
|
+
touches: { saved: 4, owed: [{ id: "p1", name: "Ana" }] },
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default function DataPickerDemo() {
|
|
24
|
+
const [message, setMessage] = useState("Read {{ steps.mail.output.count }} emails since {{ inputs.since }}.");
|
|
25
|
+
const [input, setInput] = useState({ person: "{{ item.id }}", limit: 20, note: "" });
|
|
26
|
+
return (
|
|
27
|
+
<div className="grid max-w-md gap-6 p-4">
|
|
28
|
+
<DataInput label="Message" value={message} onChange={setMessage} multiline rows={3} available={AVAILABLE} samples={OUTPUTS} help="Press { } to insert data." />
|
|
29
|
+
<DataInput label="Condition" value="{{ steps.touches.output.saved > 0 }}" onChange={() => {}} mono available={AVAILABLE} samples={OUTPUTS} />
|
|
30
|
+
<DataPairs label="What to give it" value={input} onChange={setInput} available={AVAILABLE} samples={OUTPUTS} />
|
|
31
|
+
<pre className="m-0 overflow-auto rounded-md bg-muted p-2 font-mono text-xs">{JSON.stringify({ message, input }, null, 2)}</pre>
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// The pure part of the data picker: what can be pointed at, as text.
|
|
2
|
+
//
|
|
3
|
+
// A step reads an earlier step's output with `{{ steps.read_mail.output.messages }}`.
|
|
4
|
+
// The picker offers the trigger, the clock, the loop's item, and every
|
|
5
|
+
// earlier step's output, and, when a real run left a sample of an output,
|
|
6
|
+
// the fields inside it with a taste of their values, so a person picks
|
|
7
|
+
// what they can see.
|
|
8
|
+
|
|
9
|
+
const MAX_FIELDS = 24;
|
|
10
|
+
const MAX_DEPTH = 3;
|
|
11
|
+
|
|
12
|
+
/** A value as a short taste: "ana@x.com", "12 items", "yes". */
|
|
13
|
+
export function preview(value) {
|
|
14
|
+
if (value === null || value === undefined) return "empty";
|
|
15
|
+
if (typeof value === "string") return value.length > 40 ? `${value.slice(0, 39)}…` : value || "empty text";
|
|
16
|
+
if (typeof value === "number") return String(value);
|
|
17
|
+
if (typeof value === "boolean") return value ? "yes" : "no";
|
|
18
|
+
if (Array.isArray(value)) return value.length === 1 ? "1 item" : `${value.length} items`;
|
|
19
|
+
return "a record";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The fields inside a sample, as [{ path, text, label, preview }]:
|
|
24
|
+
* `path` is what goes after `steps.<id>.output` (".messages[0].from"),
|
|
25
|
+
* `text` the whole reference. Objects go down a few levels; a list shows
|
|
26
|
+
* its first item's fields under `[0]`. A key that is not a plain name
|
|
27
|
+
* (spaces, dashes) is left out: the expression language cannot read it.
|
|
28
|
+
*/
|
|
29
|
+
export function fieldsOf(sample, base, depth = 0, out = [], path = "") {
|
|
30
|
+
if (depth >= MAX_DEPTH || out.length >= MAX_FIELDS || sample === null || typeof sample !== "object") return out;
|
|
31
|
+
const list = Array.isArray(sample);
|
|
32
|
+
const entries = list ? (sample.length ? [[0, sample[0]]] : []) : Object.entries(sample).filter(([key]) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(key));
|
|
33
|
+
for (const [key, value] of entries) {
|
|
34
|
+
if (out.length >= MAX_FIELDS) break;
|
|
35
|
+
const next = list ? `${path}[${key}]` : `${path}.${key}`;
|
|
36
|
+
out.push({ path: next, text: `{{ ${base}${next} }}`, label: next.replace(/^\./, ""), preview: preview(value) });
|
|
37
|
+
if (value && typeof value === "object") fieldsOf(value, base, depth + 1, out, next);
|
|
38
|
+
}
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The groups the menu shows.
|
|
44
|
+
* available automation-model's availableBefore(): { steps: [{ id, kind, label }], inLoop },
|
|
45
|
+
* and optionally `inputs: [{ name, label }]`, what a manual run asks first
|
|
46
|
+
* samples { [stepId]: a real output } or null
|
|
47
|
+
* → [{ title, items: [{ text, label, hint?, preview?, fields? }] }]
|
|
48
|
+
*/
|
|
49
|
+
export function menuGroups(available, samples = null) {
|
|
50
|
+
const groups = [
|
|
51
|
+
{
|
|
52
|
+
title: "This run",
|
|
53
|
+
items: [
|
|
54
|
+
{ text: "{{ trigger.at }}", label: "When it started", hint: "trigger.at" },
|
|
55
|
+
{ text: "{{ now }}", label: "Right now", hint: "now" },
|
|
56
|
+
{ text: "{{ run.id }}", label: "This run's id", hint: "run.id" },
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
if (available?.inputs?.length) {
|
|
61
|
+
groups.push({ title: "Your questions", items: available.inputs.map((q) => ({ text: `{{ inputs.${q.name} }}`, label: q.label || q.name, hint: `inputs.${q.name}` })) });
|
|
62
|
+
}
|
|
63
|
+
if (available?.inLoop) {
|
|
64
|
+
groups.push({
|
|
65
|
+
title: "This item",
|
|
66
|
+
items: [
|
|
67
|
+
{ text: "{{ item }}", label: "The item", hint: "item" },
|
|
68
|
+
{ text: "{{ index }}", label: "Its place in the list", hint: "index" },
|
|
69
|
+
],
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
if (available?.steps?.length) {
|
|
73
|
+
groups.push({
|
|
74
|
+
title: "Earlier steps",
|
|
75
|
+
items: [...available.steps].reverse().map((s) => {
|
|
76
|
+
const base = `steps.${s.id}.output`;
|
|
77
|
+
const sample = samples ? samples[s.id] : undefined;
|
|
78
|
+
return {
|
|
79
|
+
text: `{{ ${base} }}`,
|
|
80
|
+
label: s.label,
|
|
81
|
+
hint: base,
|
|
82
|
+
preview: sample === undefined ? undefined : preview(sample),
|
|
83
|
+
fields: sample && typeof sample === "object" ? fieldsOf(sample, base) : undefined,
|
|
84
|
+
};
|
|
85
|
+
}),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return groups;
|
|
89
|
+
}
|
|
@@ -282,7 +282,7 @@ function Orders() {
|
|
|
282
282
|
<p className="mt-1 text-sm text-muted-foreground">Your recent orders, and the invoice for each.</p>
|
|
283
283
|
</div>
|
|
284
284
|
<Card padding="none" className="flex flex-wrap items-center justify-between gap-4 p-4">
|
|
285
|
-
<StatGroup variant="divided" columns={3} className="min-w-0 flex-1">
|
|
285
|
+
<StatGroup variant="divided" columns={3} className="min-w-0 flex-1 basis-64">
|
|
286
286
|
<Stat label="Order" value={ORDER.number} size="sm" />
|
|
287
287
|
<Stat label="Placed" value={date(ORDER.placed)} size="sm" />
|
|
288
288
|
<Stat label="Total" value={money(ORDER.total)} size="sm" />
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# FirstRun
|
|
2
|
+
|
|
3
|
+
An app's first page, before anything is made (0.8.1). Working-tool style, not
|
|
4
|
+
a landing page.
|
|
5
|
+
|
|
6
|
+
The founder's review of Gantt Planner, 2026-10-12: its home was a 52px hero
|
|
7
|
+
("Plan a project that lands on its date."), four stacked cards, a big example
|
|
8
|
+
chart and a second shelf, and it "reads like a website, not an app's home".
|
|
9
|
+
Roadmap's first run and Newsletter's differed again. Each app had invented
|
|
10
|
+
its own. This is the one.
|
|
11
|
+
|
|
12
|
+
In order, at the kit's normal sizes:
|
|
13
|
+
|
|
14
|
+
1. **One plain heading line**, and one line of what the app does.
|
|
15
|
+
2. **The first steps in a row** (action cards), offered in a fixed order
|
|
16
|
+
whatever order you list them in: the agent's way in, then making the first
|
|
17
|
+
thing yourself, then starting from the Library, then bringing it in from
|
|
18
|
+
another tool. The first is the page's one main button.
|
|
19
|
+
3. **The starting points shelf**: your `LibraryShelf`, passed as a node.
|
|
20
|
+
4. **What the agent will do**, and **the accounts it can use**, side by side.
|
|
21
|
+
5. **A worked example, behind "See an example".** It never fills the page.
|
|
22
|
+
|
|
23
|
+
A step with nothing to press is dropped: a card with no action is a lie.
|
|
24
|
+
|
|
25
|
+
**An agent step never opens a form first.** Its `onClick` opens the agent's
|
|
26
|
+
dock with a starting line ("Draft a project plan for me") and the agent asks
|
|
27
|
+
for what it is missing, in chat, then makes the thing through the app's own
|
|
28
|
+
actions. A form belongs to "type it yourself". (UI review X9: the founder,
|
|
29
|
+
"the agent should be able to create these".)
|
|
30
|
+
|
|
31
|
+
**One column; a form is revealed in the main flow.** The block has no side
|
|
32
|
+
column. A "Name it" or "Paste a list" step, when pressed, reveals its own
|
|
33
|
+
card under the steps (`children`), in the page's own scroll, and the shelf
|
|
34
|
+
and the rest move down. Never put that form in a side column beside a short
|
|
35
|
+
intro: the side is the fixed, short one and the main is the one that scrolls
|
|
36
|
+
(UI review X13: the founder, looking at exactly that, "one small column and
|
|
37
|
+
then all the card long scrolling in other column"). `npm run check` (rule
|
|
38
|
+
`sidelong`) refuses an app that does.
|
|
39
|
+
|
|
40
|
+
`npm run check` (rule `hero`) refuses an `<h1>` at 36px or more, so the page
|
|
41
|
+
cannot go back to a hero.
|
|
42
|
+
|
|
43
|
+
```jsx
|
|
44
|
+
import { FirstRun } from "../components/blocks/first-run"; // a Makersclaw app ("./blocks/first-run" from components/)
|
|
45
|
+
import { FirstRun } from "@/components/makersclaw/blocks/first-run"; // a shadcn project
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
A client component. It needs the kit and nothing else; the shelf, the
|
|
49
|
+
accounts and the example come in as nodes.
|
|
50
|
+
|
|
51
|
+
## Adopting it (an app that drew its own)
|
|
52
|
+
|
|
53
|
+
1. Delete the app's own first-run page's layout: the hero, the card grid,
|
|
54
|
+
the headings of "what the agent will do" and "accounts". Keep what is the
|
|
55
|
+
app's: its copy, its starting points (`LibraryShelf`), its connect rows.
|
|
56
|
+
2. Return `<FirstRun title lede choices shelf agentDoes accounts example />`
|
|
57
|
+
from the same place, inside the screen's `AppLayout` (the page pads
|
|
58
|
+
itself).
|
|
59
|
+
3. Put the worked example behind `example`: `onOpen` if the app has an
|
|
60
|
+
example mode (Roadmap's `lookAround`), or `children` to reveal in place.
|
|
61
|
+
Do not draw it on the page.
|
|
62
|
+
4. Make the agent step an opener of the dock, not a link to a form.
|
|
63
|
+
|
|
64
|
+
## Props
|
|
65
|
+
|
|
66
|
+
- `title`: the heading, one plain line.
|
|
67
|
+
- `lede`: one line of what the app does and what happens next.
|
|
68
|
+
- `notice`: a node above the steps (a `Notice`).
|
|
69
|
+
- `choices`: `[{ key, kind, icon, title, description, action, status, note }]`.
|
|
70
|
+
`kind`: `agent` · `hand` · `library` · `import` · `other`. `action`:
|
|
71
|
+
`{ label, href | onClick, icon, cost, variant, disabled, loading }`; `cost`
|
|
72
|
+
is a node after the label (a `CostTag`). `status`: an ActionCard's
|
|
73
|
+
`{ tone, children }` ("Connected" only when an account is listed). `note`:
|
|
74
|
+
a node between the description and the status.
|
|
75
|
+
- `children`: a node after the steps, in the main flow (a card that reveals
|
|
76
|
+
the form to name the first thing when its step is pressed).
|
|
77
|
+
- `shelf`: your starting points, as a node.
|
|
78
|
+
- `agentDoes`: `[{ name, when, what, cost }]`; `agentTitle`.
|
|
79
|
+
- `accounts`: a node; `accountsTitle` (default "Accounts, all optional").
|
|
80
|
+
- `example`: `{ label, title, description, onOpen, children }`.
|
|
81
|
+
- `className`: spacing around the page.
|
|
82
|
+
|
|
83
|
+
## The model (plain functions)
|
|
84
|
+
|
|
85
|
+
`orderChoices(choices)`, `stepsOf(choices)` (ordered, each with its `variant`),
|
|
86
|
+
`variantFor(choice, index)`, `columnsFor(count)` (the container classes that
|
|
87
|
+
set how many sit in a row), `stepsWords(count)`, `KINDS`.
|
|
88
|
+
|
|
89
|
+
## Example
|
|
90
|
+
|
|
91
|
+
```jsx
|
|
92
|
+
// components/first-run-page.jsx
|
|
93
|
+
"use client";
|
|
94
|
+
import { FirstRun } from "./blocks/first-run";
|
|
95
|
+
import { openAgent } from "./blocks/agent-dock";
|
|
96
|
+
|
|
97
|
+
export function Nothing({ onType, onLibrary }) {
|
|
98
|
+
return (
|
|
99
|
+
<FirstRun
|
|
100
|
+
title="Nothing is planned here yet."
|
|
101
|
+
lede="Add one project. From the next weekday at 9:00 the agent checks the plan and tells you which dates will slip."
|
|
102
|
+
choices={[
|
|
103
|
+
{ kind: "hand", icon: "plus", title: "Add the first project", description: "A name, a start and a target.", action: { label: "New project", onClick: onType } },
|
|
104
|
+
{ kind: "agent", icon: "sparkles", title: "Have the agent draft it", description: "Say what you are building; it asks for what it is missing.", action: { label: "Draft with the agent", onClick: () => openAgent("Draft a project plan for me") } },
|
|
105
|
+
{ kind: "library", icon: "layers", title: "Start from a starting point", description: "Phases and tasks already set.", action: { label: "Browse", onClick: onLibrary } },
|
|
106
|
+
]}
|
|
107
|
+
example={{ title: "See it working first", description: "A made-up team of seven, read only.", onOpen: () => {} }}
|
|
108
|
+
/>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## States drawn in the demo
|
|
114
|
+
|
|
115
|
+
A plan's first run (four steps, listed out of order, a shelf, what the agent
|
|
116
|
+
will do, accounts, an example mode); a newsletter's (three steps, a Notice
|
|
117
|
+
above, an example revealed in place); the smallest (one heading, one line, one
|
|
118
|
+
step).
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "first-run",
|
|
3
|
+
"title": "FirstRun",
|
|
4
|
+
"description": "An app's first page, before anything is made, in working-tool style and not a landing page. In order, at the kit's normal sizes: one plain heading line and one line of what the app does; the first steps in a row as action cards, offered in a fixed order whatever order the app lists them in (the agent's way in first, then making the first thing yourself, then starting from the Library, then bringing it in from another tool; the first is the page's one main button, and a step with nothing to press is dropped); the starting points shelf (the app's LibraryShelf, passed as a node); what the agent will do by itself and the accounts it can use, side by side; and a worked example behind a \"See an example\" button, either revealed in place or handed to the app's own example mode, never filling the page. A node above the steps takes a Notice, a node after them a form. An agent step opens the agent's dock with a starting line and lets the agent ask for what it is missing; it never opens a form first. `npm run check` refuses an <h1> at 36px or more. The step order, the row width and the variants are exported as plain functions.",
|
|
5
|
+
"useWhen": "Every app's first run: the page an app opens on while it has nothing yet, replacing each app's own components/<app>/first-run.jsx or home hero (Project Roadmap's four start cards, Newsletter Engine's start page, Gantt Planner's hero). Not for a screen that shows data (that is the app's own home), not an empty list inside a screen (the kit's Empty), and not a setup checklist that stays on the page after the first run (setup-checklist).",
|
|
6
|
+
"tags": ["first run", "home", "welcome", "start", "onboarding", "empty", "getting started", "starting points", "agent", "example", "steps"],
|
|
7
|
+
"exports": ["FirstRun", "KINDS", "columnsFor", "orderChoices", "stepsOf", "stepsWords", "variantFor"],
|
|
8
|
+
"files": ["index.js", "first-run.jsx", "first-run-model.js", "probe-rules.js"],
|
|
9
|
+
"requires": [],
|
|
10
|
+
"offer": { "since": "0.8.1", "why": "the shared first page before anything is made (the agent's way in first, the starting points), in place of the app's own hero and card grid" }
|
|
11
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// FirstRun in the three shapes the apps had — the gallery's and the smoke
|
|
4
|
+
// test's surface. Not installed into apps: `makersclaw add first-run` lays
|
|
5
|
+
// index.js, first-run.jsx and first-run-model.js only.
|
|
6
|
+
// A plan four steps, the agent's first (given last: the page
|
|
7
|
+
// reorders), a starting points shelf, what the agent will
|
|
8
|
+
// do, accounts, and a worked example behind a button
|
|
9
|
+
// (Project Roadmap's and Gantt Planner's first run).
|
|
10
|
+
// A newsletter three steps, a form between the steps and the shelf, a
|
|
11
|
+
// Notice above (Newsletter Engine's first run).
|
|
12
|
+
// The smallest one heading, one line, one step.
|
|
13
|
+
import { useState } from "react";
|
|
14
|
+
import { Notice } from "../components/ui";
|
|
15
|
+
import { FirstRun } from "../components/blocks/first-run";
|
|
16
|
+
import { openAgent } from "../components/blocks/agent-dock";
|
|
17
|
+
|
|
18
|
+
const COST = <span className="font-normal text-muted-foreground">about 150 to 300 cr</span>;
|
|
19
|
+
|
|
20
|
+
function Shelf({ title }) {
|
|
21
|
+
return (
|
|
22
|
+
<section aria-label={title} className="grid gap-2">
|
|
23
|
+
<h2 className="text-xs font-semibold uppercase tracking-[0.06em] text-muted-foreground">{title}</h2>
|
|
24
|
+
<ul className="m-0 grid list-none gap-2 p-0 @xl:grid-cols-3">
|
|
25
|
+
{["Website launch", "Product release", "Event"].map((name) => (
|
|
26
|
+
<li key={name} className="rounded-lg border border-border bg-card p-3 text-sm">
|
|
27
|
+
<p className="font-semibold">{name}</p>
|
|
28
|
+
<p className="text-xs text-muted-foreground">5 phases · 28 tasks</p>
|
|
29
|
+
</li>
|
|
30
|
+
))}
|
|
31
|
+
</ul>
|
|
32
|
+
</section>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default function FirstRunDemo() {
|
|
37
|
+
const [log, setLog] = useState("Nothing pressed yet.");
|
|
38
|
+
const say = (what) => () => setLog(what);
|
|
39
|
+
return (
|
|
40
|
+
<div className="grid gap-8">
|
|
41
|
+
<p role="status" className="text-xs text-muted-foreground">
|
|
42
|
+
{log}
|
|
43
|
+
</p>
|
|
44
|
+
|
|
45
|
+
<section className="rounded-lg border border-border">
|
|
46
|
+
<FirstRun
|
|
47
|
+
title="Nothing is planned here yet."
|
|
48
|
+
lede="Add your team and one project. From the next weekday at 9:00 the agent checks the plan and tells you which dates will slip, by how much, and the fix."
|
|
49
|
+
choices={[
|
|
50
|
+
{ key: "import", kind: "import", icon: "download", title: "Import open issues", description: "Bring GitHub or Linear issues in as tasks. A preview comes first; you pick.", status: { tone: "neutral", children: "Not connected" }, action: { label: "Choose a source", onClick: say("Import") } },
|
|
51
|
+
{ key: "library", kind: "library", icon: "layers", title: "Start from a starting point", description: "Eight project shapes with phases, tasks and waits already set. Free.", action: { label: "Browse starting points", onClick: say("Library") } },
|
|
52
|
+
{ key: "hand", kind: "hand", icon: "plus", title: "Add the first project yourself", description: "A name, a start and a target; then type the tasks.", action: { label: "New project", onClick: say("By hand") } },
|
|
53
|
+
{ key: "agent", kind: "agent", icon: "sparkles", title: "Have the agent draft it", description: "Say what you are building. The agent asks for what it is missing and drafts the phases and tasks for you to review.", action: { label: "Draft with the agent", cost: COST, onClick: () => { openAgent("Draft a project plan for me"); setLog("Agent: opens the dock with a starting line"); } } },
|
|
54
|
+
]}
|
|
55
|
+
shelf={<Shelf title="Start from a template" />}
|
|
56
|
+
agentDoes={[
|
|
57
|
+
{ name: "Daily check", when: "Weekdays 9:00", what: "Counts what is late, blocked or unowned and proposes up to five fixes. You apply or dismiss each one.", cost: "up to 40 cr a run" },
|
|
58
|
+
{ name: "Monday update", when: "Mondays 9:30", what: "Writes what shipped, what is on, what is at risk. A post waits for your yes.", cost: "up to 60 cr a run" },
|
|
59
|
+
]}
|
|
60
|
+
accounts={<p className="text-sm text-muted-foreground">GitHub and Linear would import issues. A chat account would carry the Monday update, after your yes. The app is complete without them.</p>}
|
|
61
|
+
example={{ title: "See it working first: Larkline, a team of seven", description: "Example data, marked as an example everywhere, and read only.", onOpen: say("Example opened") }}
|
|
62
|
+
/>
|
|
63
|
+
</section>
|
|
64
|
+
|
|
65
|
+
<section className="rounded-lg border border-border">
|
|
66
|
+
<FirstRun
|
|
67
|
+
title="Start your newsletter."
|
|
68
|
+
lede="Every Monday the agent reads what you shipped and drafts the issue in your voice. Nothing reaches a reader until you say yes."
|
|
69
|
+
notice={
|
|
70
|
+
<Notice tone="warning" title="Before issue 1 can be made ready">
|
|
71
|
+
Add the postal address for the footer and connect an email platform.
|
|
72
|
+
</Notice>
|
|
73
|
+
}
|
|
74
|
+
choices={[
|
|
75
|
+
{ key: "hand", kind: "hand", icon: "pencil", title: "Start issue 1 by hand", description: "Write it here.", action: { label: "Start issue 1", onClick: say("Hand") } },
|
|
76
|
+
{ key: "agent", kind: "agent", icon: "sparkles", title: "Draft issue 1", description: "The agent reads what shipped and drafts it.", action: { label: "Draft with the agent", onClick: () => { openAgent("Draft issue 1 for me"); setLog("Agent: opens the dock with a starting line"); } } },
|
|
77
|
+
{ key: "lib", kind: "library", icon: "layers", title: "Begin from a shape", description: "Four issue shapes.", action: { label: "Pick a shape", onClick: say("Shape") } },
|
|
78
|
+
]}
|
|
79
|
+
example={{ label: "See an example", title: "What it will do", description: "A worked example, invented.", children: <p className="rounded-lg border border-border bg-muted p-4 text-sm">The engineer's line on the left, your reader's paragraph on the right.</p> }}
|
|
80
|
+
/>
|
|
81
|
+
</section>
|
|
82
|
+
|
|
83
|
+
<section className="rounded-lg border border-border">
|
|
84
|
+
<FirstRun title="No clients yet." lede="Add a client and the agent keeps their minutes." choices={[{ key: "hand", kind: "hand", icon: "plus", title: "Add a client", action: { label: "Add a client", onClick: say("Client") } }]} />
|
|
85
|
+
</section>
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// The first run's rules as plain functions: the order the first steps are
|
|
2
|
+
// offered in, how many sit in a row at which width, and which step is the
|
|
3
|
+
// page's one main button. No React here.
|
|
4
|
+
//
|
|
5
|
+
// The order is the founder's (UI review X9, 2026-10-12): "the agent should be
|
|
6
|
+
// able to create these". The agent's way in comes first, then making the
|
|
7
|
+
// first thing yourself, then starting from the Library, then bringing it in
|
|
8
|
+
// from another tool. An app lists its steps in any order; the page offers
|
|
9
|
+
// them in this one.
|
|
10
|
+
|
|
11
|
+
export const KINDS = ["agent", "hand", "library", "import", "other"];
|
|
12
|
+
|
|
13
|
+
/** The steps in the order they are offered; steps of one kind keep the order
|
|
14
|
+
* given. A step with no kind goes last. A step without a way to take it
|
|
15
|
+
* (no `action`) is dropped: a card with nothing to press is a lie. */
|
|
16
|
+
export function orderChoices(choices) {
|
|
17
|
+
const rank = (c) => {
|
|
18
|
+
const i = KINDS.indexOf(c?.kind);
|
|
19
|
+
return i < 0 ? KINDS.length - 1 : i;
|
|
20
|
+
};
|
|
21
|
+
return (choices ?? [])
|
|
22
|
+
.map((c, at) => ({ c, at }))
|
|
23
|
+
.filter(({ c }) => c && c.title && c.action && (c.action.href || typeof c.action.onClick === "function") && c.action.label)
|
|
24
|
+
.sort((a, b) => rank(a.c) - rank(b.c) || a.at - b.at)
|
|
25
|
+
.map(({ c }) => c);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** The container classes that set how many steps sit in a row: the page's
|
|
29
|
+
* own width, whole class names for Tailwind. */
|
|
30
|
+
export function columnsFor(count) {
|
|
31
|
+
if (count <= 1) return "";
|
|
32
|
+
if (count === 2) return "@xl:grid-cols-2";
|
|
33
|
+
if (count === 3) return "@xl:grid-cols-2 @3xl:grid-cols-3";
|
|
34
|
+
return "@xl:grid-cols-2 @5xl:grid-cols-4";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** The step's button variant: the first step is the page's one main button
|
|
38
|
+
* (primary); the rest are secondary, unless a step names its own. */
|
|
39
|
+
export function variantFor(choice, index) {
|
|
40
|
+
return choice?.action?.variant ?? (index === 0 ? "primary" : "secondary");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** The steps as the page will draw them: ordered, each with its variant. */
|
|
44
|
+
export function stepsOf(choices) {
|
|
45
|
+
return orderChoices(choices).map((c, i) => ({ ...c, variant: variantFor(c, i) }));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Words for a screen reader about how many ways in there are. */
|
|
49
|
+
export function stepsWords(count) {
|
|
50
|
+
return count === 1 ? "1 way to begin" : `${count} ways to begin`;
|
|
51
|
+
}
|