makersclaw 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -4
- package/package.json +1 -1
- package/r/activity-log.json +52 -0
- package/r/agent-changes.json +51 -0
- package/r/agent-chat.json +53 -0
- package/r/agent-column.json +85 -0
- package/r/agent-dock.json +57 -0
- package/r/agent-kit.json +49 -0
- package/r/agent-marks.json +49 -0
- package/r/agent-pane.json +93 -0
- package/r/ai-action.json +56 -0
- package/r/annotate.json +4 -4
- package/r/approval-request.json +2 -2
- package/r/approval.json +54 -0
- package/r/ask-bar.json +50 -0
- package/r/audio-player.json +2 -2
- package/r/auth-form.json +2 -2
- package/r/automation-editor.json +66 -0
- package/r/automation-list.json +57 -0
- package/r/automation-model.json +118 -0
- package/r/automations-screen.json +71 -0
- package/r/board.json +2 -2
- package/r/calendar-view.json +3 -3
- package/r/cart.json +2 -2
- package/r/chart.json +5 -5
- package/r/chat-pane.json +84 -0
- package/r/chat.json +3 -3
- package/r/code-editor.json +2 -2
- package/r/comment-box.json +3 -3
- package/r/connect-card.json +51 -0
- package/r/contact-section.json +2 -2
- package/r/core.json +18 -6
- package/r/cost.json +51 -0
- package/r/csv-import.json +3 -3
- package/r/data-picker.json +60 -0
- package/r/data-table.json +2 -2
- package/r/diagram.json +2 -2
- package/r/doc-editor.json +2 -2
- package/r/doc-page.json +3 -3
- package/r/email-render.json +2 -2
- package/r/faq.json +2 -2
- package/r/file-upload.json +2 -2
- package/r/first-run.json +63 -0
- package/r/fix-note.json +57 -0
- package/r/flow-canvas.json +3 -3
- package/r/flow-editor.json +79 -0
- package/r/footer.json +3 -3
- package/r/gantt.json +3 -3
- package/r/job-panel.json +2 -2
- package/r/json-editor.json +3 -3
- package/r/library-shelf.json +56 -0
- package/r/map.json +2 -2
- package/r/margin-notes.json +67 -0
- package/r/menubar.json +2 -2
- package/r/mind-map.json +4 -4
- package/r/navigation-menu.json +2 -2
- package/r/needs-you.json +55 -0
- package/r/order-detail.json +2 -2
- package/r/org-chart.json +4 -4
- package/r/pace-bar.json +69 -0
- package/r/page-builder.json +3 -3
- package/r/payment-methods.json +2 -2
- package/r/pdf-document.json +2 -2
- package/r/pdf-viewer.json +3 -3
- package/r/product-detail.json +2 -2
- package/r/product-grid.json +3 -3
- package/r/record-form.json +2 -2
- package/r/record-grid.json +52 -0
- package/r/record-notes.json +4 -4
- package/r/registry.json +4089 -1510
- package/r/run-history.json +52 -0
- package/r/run-view.json +80 -0
- package/r/schedule-card.json +54 -0
- package/r/schedule-engine.json +79 -0
- package/r/schedule-gantt.json +83 -0
- package/r/schedule-ruler.json +64 -0
- package/r/search-dialog.json +2 -2
- package/r/settings-list.json +2 -2
- package/r/setup-checklist.json +2 -2
- package/r/share-dialog.json +2 -2
- package/r/sidebar.json +3 -3
- package/r/signature-pad.json +2 -2
- package/r/sketch-canvas.json +3 -3
- package/r/source-receipt.json +52 -0
- package/r/spreadsheet.json +3 -3
- package/r/stat-cards.json +3 -3
- package/r/step-picker.json +53 -0
- package/r/step-settings.json +72 -0
- package/r/suggestion.json +58 -0
- package/r/sum-bar.json +64 -0
- package/r/task-list.json +3 -3
- package/r/task-model.json +2 -2
- package/r/testimonials.json +2 -2
- package/r/tier-map.json +67 -0
- package/r/time-grid.json +73 -0
- package/r/time-strip.json +64 -0
- package/r/top-bar.json +66 -0
- package/r/transaction-list.json +2 -2
- package/r/trigger-picker.json +61 -0
- package/r/whiteboard.json +4 -4
- package/r/workflow-view.json +55 -0
- package/registry/blocks/activity-log/README.md +112 -0
- package/registry/blocks/activity-log/activity-log.jsx +351 -0
- package/registry/blocks/activity-log/block.json +33 -0
- package/registry/blocks/activity-log/demo.jsx +48 -0
- package/registry/blocks/activity-log/index.js +4 -0
- package/registry/blocks/agent-changes/README.md +94 -0
- package/registry/blocks/agent-changes/agent-changes.jsx +118 -0
- package/registry/blocks/agent-changes/block.json +28 -0
- package/registry/blocks/agent-changes/demo.jsx +39 -0
- package/registry/blocks/agent-changes/index.js +3 -0
- package/registry/blocks/agent-chat/README.md +145 -0
- package/registry/blocks/agent-chat/agent-chat.jsx +415 -0
- package/registry/blocks/agent-chat/block.json +30 -0
- package/registry/blocks/agent-chat/demo.jsx +165 -0
- package/registry/blocks/agent-chat/index.js +3 -0
- package/registry/blocks/agent-column/README.md +197 -0
- package/registry/blocks/agent-column/agent-column.jsx +492 -0
- package/registry/blocks/agent-column/block.json +67 -0
- package/registry/blocks/agent-column/column-picker.jsx +163 -0
- package/registry/blocks/agent-column/demo.jsx +50 -0
- package/registry/blocks/agent-column/index.js +7 -0
- package/registry/blocks/agent-column/model.js +164 -0
- package/registry/blocks/agent-column/receipt.jsx +483 -0
- package/registry/blocks/agent-column/spreadsheet-type.jsx +24 -0
- package/registry/blocks/agent-dock/README.md +210 -0
- package/registry/blocks/agent-dock/agent-dock.jsx +356 -0
- package/registry/blocks/agent-dock/block.json +47 -0
- package/registry/blocks/agent-dock/demo.jsx +153 -0
- package/registry/blocks/agent-dock/dock-model.js +118 -0
- package/registry/blocks/agent-dock/index.js +5 -0
- package/registry/blocks/agent-kit/README.md +73 -0
- package/registry/blocks/agent-kit/agent-kit.jsx +86 -0
- package/registry/blocks/agent-kit/block.json +10 -0
- package/registry/blocks/agent-kit/demo.jsx +35 -0
- package/registry/blocks/agent-kit/index.js +3 -0
- package/registry/blocks/agent-marks/README.md +80 -0
- package/registry/blocks/agent-marks/agent-marks.jsx +133 -0
- package/registry/blocks/agent-marks/block.json +27 -0
- package/registry/blocks/agent-marks/demo.jsx +49 -0
- package/registry/blocks/agent-marks/index.js +3 -0
- package/registry/blocks/agent-pane/README.md +225 -0
- package/registry/blocks/agent-pane/agent-pane.jsx +244 -0
- package/registry/blocks/agent-pane/block.json +41 -0
- package/registry/blocks/agent-pane/composer.jsx +6 -0
- package/registry/blocks/agent-pane/demo.jsx +233 -0
- package/registry/blocks/agent-pane/dock-context.js +10 -0
- package/registry/blocks/agent-pane/index.js +4 -0
- package/registry/blocks/agent-pane/prose.jsx +84 -0
- package/registry/blocks/agent-pane/questions.jsx +134 -0
- package/registry/blocks/agent-pane/transcript.jsx +268 -0
- package/registry/blocks/agent-pane/work.jsx +368 -0
- package/registry/blocks/ai-action/README.md +94 -0
- package/registry/blocks/ai-action/ai-action.jsx +188 -0
- package/registry/blocks/ai-action/block.json +34 -0
- package/registry/blocks/ai-action/demo.jsx +93 -0
- package/registry/blocks/ai-action/index.js +3 -0
- package/registry/blocks/annotate/README.md +14 -11
- package/registry/blocks/approval/README.md +145 -0
- package/registry/blocks/approval/approval.jsx +163 -0
- package/registry/blocks/approval/block.json +31 -0
- package/registry/blocks/approval/demo.jsx +121 -0
- package/registry/blocks/approval/index.js +3 -0
- package/registry/blocks/ask-bar/README.md +105 -0
- package/registry/blocks/ask-bar/ask-bar.jsx +166 -0
- package/registry/blocks/ask-bar/block.json +26 -0
- package/registry/blocks/ask-bar/demo.jsx +78 -0
- package/registry/blocks/ask-bar/index.js +3 -0
- package/registry/blocks/automation-editor/README.md +291 -0
- package/registry/blocks/automation-editor/automation-editor.jsx +656 -0
- package/registry/blocks/automation-editor/block.json +39 -0
- package/registry/blocks/automation-editor/demo.jsx +284 -0
- package/registry/blocks/automation-editor/index.js +4 -0
- package/registry/blocks/automation-editor/run-inputs.jsx +82 -0
- package/registry/blocks/automation-list/README.md +129 -0
- package/registry/blocks/automation-list/automation-list.jsx +476 -0
- package/registry/blocks/automation-list/block.json +35 -0
- package/registry/blocks/automation-list/demo.jsx +67 -0
- package/registry/blocks/automation-list/index.js +3 -0
- package/registry/blocks/automation-model/README.md +109 -0
- package/registry/blocks/automation-model/block.json +135 -0
- package/registry/blocks/automation-model/catalog.js +256 -0
- package/registry/blocks/automation-model/check.js +161 -0
- package/registry/blocks/automation-model/demo.jsx +42 -0
- package/registry/blocks/automation-model/edit.js +262 -0
- package/registry/blocks/automation-model/fixes.js +287 -0
- package/registry/blocks/automation-model/glyph.jsx +13 -0
- package/registry/blocks/automation-model/graph.js +410 -0
- package/registry/blocks/automation-model/index.js +33 -0
- package/registry/blocks/automation-model/overlay.js +175 -0
- package/registry/blocks/automation-model/parts.jsx +94 -0
- package/registry/blocks/automation-model/schedule.js +53 -0
- package/registry/blocks/automation-model/templates.js +128 -0
- package/registry/blocks/automation-model/values.js +29 -0
- package/registry/blocks/automation-model/words.js +439 -0
- package/registry/blocks/automations-screen/README.md +207 -0
- package/registry/blocks/automations-screen/automations-screen.jsx +452 -0
- package/registry/blocks/automations-screen/block.json +47 -0
- package/registry/blocks/automations-screen/demo.jsx +364 -0
- package/registry/blocks/automations-screen/index.js +4 -0
- package/registry/blocks/automations-screen/screen-model.js +129 -0
- package/registry/blocks/chart/cartesian.jsx +1 -1
- package/registry/blocks/chart/core.jsx +33 -23
- package/registry/blocks/chart/pie.jsx +1 -1
- package/registry/blocks/chat/README.md +4 -1
- package/registry/blocks/chat/demo.jsx +2 -2
- package/registry/blocks/chat-pane/README.md +143 -0
- package/registry/blocks/chat-pane/block.json +48 -0
- package/registry/blocks/chat-pane/chat-attach.js +166 -0
- package/registry/blocks/chat-pane/chat-composer.jsx +195 -0
- package/registry/blocks/chat-pane/chat-context.js +17 -0
- package/registry/blocks/chat-pane/chat-model.js +43 -0
- package/registry/blocks/chat-pane/chat-pane.jsx +398 -0
- package/registry/blocks/chat-pane/chats-list.jsx +52 -0
- package/registry/blocks/chat-pane/demo.jsx +103 -0
- package/registry/blocks/chat-pane/index.js +9 -0
- package/registry/blocks/connect-card/README.md +99 -0
- package/registry/blocks/connect-card/block.json +10 -0
- package/registry/blocks/connect-card/connect-card.jsx +103 -0
- package/registry/blocks/connect-card/demo.jsx +49 -0
- package/registry/blocks/connect-card/index.js +3 -0
- package/registry/blocks/cost/README.md +115 -0
- package/registry/blocks/cost/block.json +10 -0
- package/registry/blocks/cost/cost.jsx +176 -0
- package/registry/blocks/cost/demo.jsx +66 -0
- package/registry/blocks/cost/index.js +3 -0
- package/registry/blocks/data-picker/README.md +81 -0
- package/registry/blocks/data-picker/block.json +38 -0
- package/registry/blocks/data-picker/data-picker.jsx +210 -0
- package/registry/blocks/data-picker/demo.jsx +34 -0
- package/registry/blocks/data-picker/fields.js +89 -0
- package/registry/blocks/data-picker/index.js +4 -0
- package/registry/blocks/data-table/demo.jsx +1 -1
- package/registry/blocks/first-run/README.md +118 -0
- package/registry/blocks/first-run/block.json +11 -0
- package/registry/blocks/first-run/demo.jsx +88 -0
- package/registry/blocks/first-run/first-run-model.js +51 -0
- package/registry/blocks/first-run/first-run.jsx +156 -0
- package/registry/blocks/first-run/index.js +5 -0
- package/registry/blocks/first-run/probe-rules.js +26 -0
- package/registry/blocks/fix-note/README.md +79 -0
- package/registry/blocks/fix-note/block.json +35 -0
- package/registry/blocks/fix-note/demo.jsx +30 -0
- package/registry/blocks/fix-note/fix-note.jsx +235 -0
- package/registry/blocks/fix-note/index.js +3 -0
- package/registry/blocks/flow-canvas/README.md +15 -8
- package/registry/blocks/flow-editor/README.md +108 -0
- package/registry/blocks/flow-editor/block.json +44 -0
- package/registry/blocks/flow-editor/demo.jsx +115 -0
- package/registry/blocks/flow-editor/flow-canvas.jsx +568 -0
- package/registry/blocks/flow-editor/flow-editor.jsx +154 -0
- package/registry/blocks/flow-editor/index.js +6 -0
- package/registry/blocks/flow-editor/minimap.jsx +78 -0
- package/registry/blocks/flow-editor/outline.jsx +134 -0
- package/registry/blocks/footer/README.md +3 -2
- package/registry/blocks/library-shelf/README.md +117 -0
- package/registry/blocks/library-shelf/block.json +28 -0
- package/registry/blocks/library-shelf/demo.jsx +68 -0
- package/registry/blocks/library-shelf/index.js +3 -0
- package/registry/blocks/library-shelf/library-shelf.jsx +211 -0
- package/registry/blocks/library-shelf/probe-rules.js +15 -0
- package/registry/blocks/margin-notes/README.md +222 -0
- package/registry/blocks/margin-notes/block.json +10 -0
- package/registry/blocks/margin-notes/demo.jsx +144 -0
- package/registry/blocks/margin-notes/index.js +20 -0
- package/registry/blocks/margin-notes/margin-model.js +224 -0
- package/registry/blocks/margin-notes/margin-notes.jsx +465 -0
- package/registry/blocks/mind-map/README.md +4 -2
- package/registry/blocks/needs-you/README.md +177 -0
- package/registry/blocks/needs-you/block.json +34 -0
- package/registry/blocks/needs-you/demo.jsx +143 -0
- package/registry/blocks/needs-you/index.js +3 -0
- package/registry/blocks/needs-you/needs-you.jsx +441 -0
- package/registry/blocks/org-chart/README.md +4 -2
- package/registry/blocks/pace-bar/README.md +230 -0
- package/registry/blocks/pace-bar/block.json +10 -0
- package/registry/blocks/pace-bar/demo.jsx +255 -0
- package/registry/blocks/pace-bar/index.js +22 -0
- package/registry/blocks/pace-bar/pace-bar.jsx +461 -0
- package/registry/blocks/pace-bar/pace-model.js +288 -0
- package/registry/blocks/page-builder/README.md +1 -1
- package/registry/blocks/page-builder/demo.jsx +3 -3
- package/registry/blocks/pdf-viewer/README.md +4 -1
- package/registry/blocks/record-grid/README.md +100 -0
- package/registry/blocks/record-grid/block.json +28 -0
- package/registry/blocks/record-grid/demo.jsx +43 -0
- package/registry/blocks/record-grid/index.js +3 -0
- package/registry/blocks/record-grid/record-grid.jsx +251 -0
- package/registry/blocks/run-history/README.md +138 -0
- package/registry/blocks/run-history/block.json +10 -0
- package/registry/blocks/run-history/demo.jsx +74 -0
- package/registry/blocks/run-history/index.js +3 -0
- package/registry/blocks/run-history/run-history.jsx +218 -0
- package/registry/blocks/run-view/README.md +174 -0
- package/registry/blocks/run-view/block.json +50 -0
- package/registry/blocks/run-view/demo.jsx +286 -0
- package/registry/blocks/run-view/index.js +5 -0
- package/registry/blocks/run-view/run-controls.jsx +266 -0
- package/registry/blocks/run-view/run-view.jsx +236 -0
- package/registry/blocks/run-view/runs-list.jsx +99 -0
- package/registry/blocks/schedule-card/README.md +121 -0
- package/registry/blocks/schedule-card/block.json +10 -0
- package/registry/blocks/schedule-card/demo.jsx +82 -0
- package/registry/blocks/schedule-card/index.js +3 -0
- package/registry/blocks/schedule-card/schedule-card.jsx +136 -0
- package/registry/blocks/schedule-engine/README.md +137 -0
- package/registry/blocks/schedule-engine/block.json +10 -0
- package/registry/blocks/schedule-engine/demo.jsx +82 -0
- package/registry/blocks/schedule-engine/engine.js +769 -0
- package/registry/blocks/schedule-engine/index.js +9 -0
- package/registry/blocks/schedule-engine/ops.js +71 -0
- package/registry/blocks/schedule-engine/place.js +294 -0
- package/registry/blocks/schedule-engine/tree.js +79 -0
- package/registry/blocks/schedule-engine/waits.js +80 -0
- package/registry/blocks/schedule-gantt/README.md +159 -0
- package/registry/blocks/schedule-gantt/block.json +10 -0
- package/registry/blocks/schedule-gantt/demo.jsx +223 -0
- package/registry/blocks/schedule-gantt/gantt-model.js +219 -0
- package/registry/blocks/schedule-gantt/geometry.js +176 -0
- package/registry/blocks/schedule-gantt/index.js +26 -0
- package/registry/blocks/schedule-gantt/schedule-gantt.jsx +882 -0
- package/registry/blocks/schedule-gantt/sheet.jsx +372 -0
- package/registry/blocks/schedule-gantt/track.jsx +190 -0
- package/registry/blocks/schedule-ruler/README.md +148 -0
- package/registry/blocks/schedule-ruler/block.json +10 -0
- package/registry/blocks/schedule-ruler/demo.jsx +181 -0
- package/registry/blocks/schedule-ruler/index.js +21 -0
- package/registry/blocks/schedule-ruler/ruler-model.js +239 -0
- package/registry/blocks/schedule-ruler/schedule-ruler.jsx +548 -0
- package/registry/blocks/sidebar/README.md +23 -13
- package/registry/blocks/sketch-canvas/README.md +15 -9
- package/registry/blocks/source-receipt/README.md +97 -0
- package/registry/blocks/source-receipt/block.json +30 -0
- package/registry/blocks/source-receipt/demo.jsx +71 -0
- package/registry/blocks/source-receipt/index.js +3 -0
- package/registry/blocks/source-receipt/source-receipt.jsx +107 -0
- package/registry/blocks/spreadsheet/spreadsheet.jsx +15 -1
- package/registry/blocks/step-picker/README.md +78 -0
- package/registry/blocks/step-picker/block.json +31 -0
- package/registry/blocks/step-picker/demo.jsx +23 -0
- package/registry/blocks/step-picker/index.js +3 -0
- package/registry/blocks/step-picker/step-picker.jsx +144 -0
- package/registry/blocks/step-settings/README.md +100 -0
- package/registry/blocks/step-settings/block.json +44 -0
- package/registry/blocks/step-settings/demo.jsx +93 -0
- package/registry/blocks/step-settings/fields.jsx +58 -0
- package/registry/blocks/step-settings/flow-settings.jsx +115 -0
- package/registry/blocks/step-settings/index.js +5 -0
- package/registry/blocks/step-settings/step-settings.jsx +290 -0
- package/registry/blocks/suggestion/README.md +171 -0
- package/registry/blocks/suggestion/block.json +40 -0
- package/registry/blocks/suggestion/demo.jsx +91 -0
- package/registry/blocks/suggestion/index.js +3 -0
- package/registry/blocks/suggestion/suggestion.jsx +436 -0
- package/registry/blocks/sum-bar/README.md +122 -0
- package/registry/blocks/sum-bar/block.json +10 -0
- package/registry/blocks/sum-bar/demo.jsx +110 -0
- package/registry/blocks/sum-bar/index.js +5 -0
- package/registry/blocks/sum-bar/sum-bar.jsx +209 -0
- package/registry/blocks/sum-bar/sum-model.js +126 -0
- package/registry/blocks/tier-map/README.md +217 -0
- package/registry/blocks/tier-map/block.json +10 -0
- package/registry/blocks/tier-map/demo.jsx +222 -0
- package/registry/blocks/tier-map/index.js +24 -0
- package/registry/blocks/tier-map/tier-map.jsx +574 -0
- package/registry/blocks/tier-map/tier-model.js +318 -0
- package/registry/blocks/time-grid/README.md +305 -0
- package/registry/blocks/time-grid/block.json +10 -0
- package/registry/blocks/time-grid/demo.jsx +279 -0
- package/registry/blocks/time-grid/grid-model.js +322 -0
- package/registry/blocks/time-grid/index.js +25 -0
- package/registry/blocks/time-grid/time-grid.jsx +1325 -0
- package/registry/blocks/time-strip/README.md +141 -0
- package/registry/blocks/time-strip/block.json +10 -0
- package/registry/blocks/time-strip/demo.jsx +129 -0
- package/registry/blocks/time-strip/index.js +19 -0
- package/registry/blocks/time-strip/strip-model.js +178 -0
- package/registry/blocks/time-strip/time-strip.jsx +260 -0
- package/registry/blocks/top-bar/README.md +149 -0
- package/registry/blocks/top-bar/block.json +35 -0
- package/registry/blocks/top-bar/demo.jsx +78 -0
- package/registry/blocks/top-bar/index.js +5 -0
- package/registry/blocks/top-bar/probe-rules.js +74 -0
- package/registry/blocks/top-bar/top-bar-model.js +152 -0
- package/registry/blocks/top-bar/top-bar.jsx +244 -0
- package/registry/blocks/trigger-picker/README.md +98 -0
- package/registry/blocks/trigger-picker/block.json +38 -0
- package/registry/blocks/trigger-picker/demo.jsx +59 -0
- package/registry/blocks/trigger-picker/index.js +3 -0
- package/registry/blocks/trigger-picker/trigger-picker.jsx +374 -0
- package/registry/blocks/whiteboard/README.md +20 -14
- package/registry/blocks/workflow-view/README.md +123 -0
- package/registry/blocks/workflow-view/block.json +10 -0
- package/registry/blocks/workflow-view/demo.jsx +90 -0
- package/registry/blocks/workflow-view/index.js +3 -0
- package/registry/blocks/workflow-view/workflow-view.jsx +203 -0
- package/src/cli.mjs +2 -2
- package/src/install.mjs +100 -22
- package/src/registry.mjs +37 -3
- package/templates/next/browser/lib/data.js +6 -1
- package/templates/next/files/README.md +909 -26
- package/templates/next/files/app/shell.jsx +54 -37
- package/templates/next/files/check/automation-format.ts +2103 -0
- package/templates/next/files/check/automation-package.ts +142 -0
- package/templates/next/files/check/run.mjs +1043 -12
- package/templates/next/files/components/ui/field.jsx +17 -4
- package/templates/next/files/components/ui/icon.jsx +13 -0
- package/templates/next/files/components/ui/index.js +3 -1
- package/templates/next/files/components/ui/layout.jsx +382 -0
- package/templates/next/files/components/ui/notice.jsx +70 -0
- package/templates/next/files/components/ui/page.jsx +124 -0
- package/templates/next/files/components/ui/stat.jsx +22 -7
- package/templates/next/files/components/ui/toast.jsx +6 -3
- package/templates/next/files/lib/automations.js +660 -0
- package/templates/next/files/lib/claw.js +244 -5
- package/templates/next/files/lib/door.js +92 -0
- package/templates/next/files/lib/hooks.js +1694 -6
- package/templates/next/files/lib/platform.js +611 -14
- package/templates/next/template.json +22 -3
|
@@ -60,7 +60,7 @@ leaves out four (Per mode, below). Which to build: §4.
|
|
|
60
60
|
Yours — write and rewrite freely:
|
|
61
61
|
|
|
62
62
|
```
|
|
63
|
-
app/shell.jsx the inside of the window: nav, layout, type, density
|
|
63
|
+
app/shell.jsx the inside of the window: nav, layout, type, density — an AppLayout (§3b, Layouts)
|
|
64
64
|
app/DESIGN.md the design, written before the code (§3)
|
|
65
65
|
surfaces/<key>.jsx one file per domain surface
|
|
66
66
|
surfaces.js the manifest entries (import + component when built)
|
|
@@ -78,7 +78,7 @@ app/page.jsx / → the home surface (or "No surfaces yet" while the ma
|
|
|
78
78
|
app/s/[key]/page.jsx renders the surface with that key; planned ones say so
|
|
79
79
|
app/error.jsx, app/global-error.jsx, app/not-found.jsx, app/error-relay.jsx, app/auto-refresh.jsx,
|
|
80
80
|
app/escape-relay.jsx, app/frame.js, app/claw-alive/route.js the app inside its window: refresh while shown (a surface opts out, §7), Escape from the desk
|
|
81
|
-
components/ui/ the kit: Button, Field, Input, Select, Sheet, Tabs, Menu, Notice, Icon, Depiction… (§3b, §3c)
|
|
81
|
+
components/ui/ the kit: AppLayout, DocumentLayout, Button, Field, Input, Select, Sheet, Tabs, Menu, Notice, Icon, Depiction… (§3b, §3c)
|
|
82
82
|
components/parts/ the parts: RecordSwitcher, LiveEmpty + LiveWork, useFixture + FixtureNotice (§3a, §3b)
|
|
83
83
|
check/pins.json the fixed files by hash; `npm run check` refuses one that differs
|
|
84
84
|
check/ `npm run check` (§6)
|
|
@@ -99,7 +99,7 @@ lib/brain.js the company brain from the browser, in both modes; its ho
|
|
|
99
99
|
lib/brain-server.js the brain's reads for a server component — a server app's
|
|
100
100
|
lib/claw.js the _claw API the gateway answers, from the browser
|
|
101
101
|
lib/platform.js typed wrappers over lib/claw.js (Platform)
|
|
102
|
-
lib/hooks.js the React hooks over them: useJob, useArtifact… (Hooks)
|
|
102
|
+
lib/hooks.js the React hooks over them: useJob, useLlm, useLibraryItems, useArtifact… (Hooks)
|
|
103
103
|
lib/sample.js markSample, for sample rows (§3a)
|
|
104
104
|
```
|
|
105
105
|
|
|
@@ -147,7 +147,12 @@ Before any code, write `app/DESIGN.md` — one screen of text:
|
|
|
147
147
|
- Type: scale and weight within the workspace's faces (`font-sans`,
|
|
148
148
|
`font-mono`, `font-display`). Numbers tabular (`data-nums` on the element).
|
|
149
149
|
- Layout and nav: where the surfaces live, what the home surface shows in
|
|
150
|
-
the first second.
|
|
150
|
+
the first second. Who scrolls: the shell is an `AppLayout` (§3b,
|
|
151
|
+
Layouts), the one owner of the window's height; say for each surface
|
|
152
|
+
whether its content scrolls (a list, an article) or fills (a canvas, a
|
|
153
|
+
grid that scrolls itself), whether it has an inspector and how wide,
|
|
154
|
+
and what sits in a footer that must stay in view. Decide it before
|
|
155
|
+
the shell, not after a long page has pushed a button out of reach.
|
|
151
156
|
- Density, empty states (§3a: what each surface says while its table is
|
|
152
157
|
empty), error states.
|
|
153
158
|
- What makes this app look like ITS domain and not another.
|
|
@@ -159,7 +164,8 @@ to design: Agent, Runs and Activity are tabs the OS draws on the window,
|
|
|
159
164
|
and the app's settings — model, prompt, skills, connections, schedule —
|
|
160
165
|
are the window's Settings tab, the same for every artifact. The keys
|
|
161
166
|
`agent`, `runs` and `activity` are reserved; a surface may not take one.
|
|
162
|
-
The starter `app/shell.jsx` is a placeholder; replace it first
|
|
167
|
+
The starter `app/shell.jsx` is a placeholder; replace it first, and
|
|
168
|
+
keep it an `AppLayout`.
|
|
163
169
|
|
|
164
170
|
What the OS does **not** own is asking the agent for something. Those
|
|
165
171
|
tabs are where the founder goes to look back; the app is where they set
|
|
@@ -249,19 +255,125 @@ import { Button, IconButton, ButtonGroup, ButtonGroupSeparator, Toggle, ToggleGr
|
|
|
249
255
|
InputGroup, PasswordInput, InputOTP, Slider, Image, FileInput, FileDrop, fileMatches, Combobox, Command, CommandDialog, Calendar, DatePicker, DateInput, DATE_PRESETS,
|
|
250
256
|
parseDate, isDateString, toDateString, todayString, addDays, addMonths,
|
|
251
257
|
startOfMonth, endOfMonth, dayOfWeek, formatDate, toLocalDate, firstDayOfWeek,
|
|
252
|
-
Card, Notice, Badge, Kicker, Empty, Item, ItemGroup, Avatar, AvatarGroup,
|
|
258
|
+
Card, ActionCard, Notice, Badge, Kicker, Empty, Item, ItemGroup, Avatar, AvatarGroup,
|
|
253
259
|
Sheet, Confirm, Tabs, TabPanel, Menu, ContextMenu, Popover, Tooltip, HoverCard,
|
|
254
260
|
Breadcrumb, Pagination, Collapsible, Accordion, Carousel, ScrollArea,
|
|
255
261
|
ResizablePanelGroup, ResizablePanel, ResizableHandle,
|
|
256
262
|
Table, TableHeader, TableBody, TableFooter, TableRow, TableHead, TableCell, TableCaption,
|
|
257
263
|
Spinner, Skeleton, Progress, Separator, Kbd, KbdGroup, AspectRatio,
|
|
258
264
|
Stat, StatGroup, Delta, StatusDot, Legend, Meter, Distribution, Swatch, Steps,
|
|
259
|
-
toast, Toaster, Depiction, Icon, ICON_NAMES, usePopoverPosition, panelClass, cx
|
|
265
|
+
toast, Toaster, Depiction, Icon, ICON_NAMES, usePopoverPosition, panelClass, cx,
|
|
266
|
+
AppLayout, DocumentLayout, PageLayout }
|
|
260
267
|
from "../components/ui"; // "./ui" from a component
|
|
261
268
|
import { RecordSwitcher, LiveEmpty, LiveWork, liveWords, useFixture, FixtureNotice, isSample,
|
|
262
269
|
Examples, useExamples } from "../components/parts";
|
|
263
270
|
```
|
|
264
271
|
|
|
272
|
+
- **Layouts** — who owns the window's height, and who scrolls. Every
|
|
273
|
+
design-first build of 2026-10-07 shipped a shell of `min-h-dvh` that
|
|
274
|
+
grew with its content: no pane scrolled on its own, and a footer or a
|
|
275
|
+
button after a long article sat past the end of the window, where
|
|
276
|
+
nothing could reach it. So the kit owns the window, once, and `npm
|
|
277
|
+
run check` refuses anything else that sizes by it (`viewport`, §6).
|
|
278
|
+
|
|
279
|
+
`AppLayout` is the app frame and the one viewport owner: the
|
|
280
|
+
outermost one is exactly the window's height; one inside another
|
|
281
|
+
fills the slot it is in instead, so there is never a second owner.
|
|
282
|
+
The shell is one. Slots and props:
|
|
283
|
+
`header` (the top bar, in view; it draws its own bottom border) ·
|
|
284
|
+
`navigation` (a rail from 768px, `navigationWidth` px, 176 by default;
|
|
285
|
+
below 768px a Sheet behind the menu button, titled `navigationLabel`;
|
|
286
|
+
a press on a link closes it) · `children`, the content, with
|
|
287
|
+
`contentMode` `scroll` (the default: its region scrolls) or `fill` (a
|
|
288
|
+
bounded column whose child scrolls itself: a canvas, a grid, a
|
|
289
|
+
spreadsheet), `contentLabel`, `contentClassName` (spacing and token
|
|
290
|
+
backgrounds only) and `scrollKey` (pass the route: when it changes the
|
|
291
|
+
content scrolls back to its top and the drawer closes) · `inspector`
|
|
292
|
+
(beside the content from `inspectorAt` md · lg · xl, lg by default, of
|
|
293
|
+
the window's width; `inspectorIn="frame"` counts the body between the
|
|
294
|
+
rail and the dock instead, 42 · 48 · 64 rem, so an agent dock that
|
|
295
|
+
takes a quarter of the window sends the inspector under the work
|
|
296
|
+
rather than squeezing it; `inspectorWidth` px, 360 by default, 240 to
|
|
297
|
+
560; `inspectorLabel`) ·
|
|
298
|
+
`footer` (under the content, in view) · `height` viewport · parent.
|
|
299
|
+
Below `inspectorAt` there is no room for two columns: in `scroll` mode
|
|
300
|
+
the content and the inspector share one scroll, the inspector after
|
|
301
|
+
the content, so everything is reachable and nothing hides behind a
|
|
302
|
+
button; in `fill` mode the inspector is a bounded pane under the
|
|
303
|
+
content (at most 45% of the height) that scrolls on its own. A
|
|
304
|
+
surface that needs an inspector or a fill of its own returns an
|
|
305
|
+
`AppLayout` as its outermost element; inside the shell it fills the
|
|
306
|
+
content edge to edge (the content's padding drops for it):
|
|
307
|
+
```jsx
|
|
308
|
+
<AppLayout
|
|
309
|
+
header={<EditorBar draft={draft} />}
|
|
310
|
+
inspector={<ReviewPanel notes={notes} />}
|
|
311
|
+
inspectorWidth={440}
|
|
312
|
+
footer={<SaveState saved={saved} />}
|
|
313
|
+
contentClassName="p-6"
|
|
314
|
+
contentLabel="Draft"
|
|
315
|
+
>
|
|
316
|
+
<DraftText draft={draft} />
|
|
317
|
+
</AppLayout>
|
|
318
|
+
```
|
|
319
|
+
`DocumentLayout` is a page that flows, for print and export: no
|
|
320
|
+
height of its own, it sits in the content and the content scrolls it.
|
|
321
|
+
`toolbar` (held at the top, never printed), `width` prose · page (A4)
|
|
322
|
+
· letter · full, `paper` (a sheet on screen, plain in print), `align`
|
|
323
|
+
start · center, `label`. In print the frame lets go (no bound, no
|
|
324
|
+
navigation, header, inspector or footer) and the document flows onto
|
|
325
|
+
pages. Inside either, bound a box by its parent (`h-full`, `flex-1
|
|
326
|
+
min-h-0`) or by a size (`max-h-96`, `ScrollArea`), never by the window.
|
|
327
|
+
|
|
328
|
+
`PageLayout` is a screen's main area with an optional side column,
|
|
329
|
+
full height. The main is the column that scrolls; the side is the fixed
|
|
330
|
+
one. Roadmap's Today ended at
|
|
331
|
+
680px in a 1240px window because it drew its own two-column grid
|
|
332
|
+
inside the scrolling content: only as tall as its words, the side's
|
|
333
|
+
divider stopped halfway and the bottom of the window was empty. Put it
|
|
334
|
+
inside an `AppLayout` with `contentMode="fill"` and no padding on the
|
|
335
|
+
content; the columns pad themselves, so the divider runs to the edges.
|
|
336
|
+
`side` (the side column) · `sideWidth` px (304; 220 to 560) · `sideAt`
|
|
337
|
+
md · lg · xl (the page's own width from which the side sits beside the
|
|
338
|
+
main, 42 · 48 · 64 rem: a measure of the page, so an open agent dock
|
|
339
|
+
does not squeeze it; narrower, the side goes under the main in one
|
|
340
|
+
scroll) · `mainLabel`, `sideLabel` · `padded` · `fill` (the main is a
|
|
341
|
+
flex column, so a child with `flex-1` grows into the height) ·
|
|
342
|
+
`className`, `mainClassName`, `sideClassName` (spacing, never a height).
|
|
343
|
+
`npm run check` refuses a screen that draws its own main + side
|
|
344
|
+
(`grid-cols-[minmax(0,1fr)_19rem]` and a ruled `<aside>`, rule `page`).
|
|
345
|
+
|
|
346
|
+
**What belongs in the side, and what never does** (the founder, looking
|
|
347
|
+
at a first run that was a short intro on the left and a long "Name it"
|
|
348
|
+
form in the right column: "this can only work if one column is fixed,
|
|
349
|
+
usually the 3rd column, and the middle one is scrolling"). The side is
|
|
350
|
+
the fixed column: it stays put while the main scrolls, so it is short
|
|
351
|
+
and never needs a scroll of its own.
|
|
352
|
+
- In the side: a summary, the next step, a few facts about the record,
|
|
353
|
+
a small panel (an allowance, "needs you" with a few lines), a short
|
|
354
|
+
list that says its bound (`items.slice(0, 5)` and "and 3 more").
|
|
355
|
+
- Never in the side: a form (`<form`, several `Field` or `Input`, a
|
|
356
|
+
`Textarea`, a paste box), a list that grows with the data, a shelf of
|
|
357
|
+
examples. They go in the main, where the page scrolls. A "Name it" or
|
|
358
|
+
"Paste a list" step that opens reveals its own card in the main flow;
|
|
359
|
+
it does not open a form in the side.
|
|
360
|
+
- The side's own scroll (thin) is a last resort for a window shorter
|
|
361
|
+
than the side. It is not how anything is meant to be read.
|
|
362
|
+
- Narrower than `sideAt`, the side goes under the main in one scroll.
|
|
363
|
+
`npm run check` rule `sidelong` refuses a `side` or an `<aside>` that
|
|
364
|
+
holds a form, a `Textarea`, three or more fields, or a `.map(` of data
|
|
365
|
+
that is not bounded by a `.slice(`. It reads the side's own markup and
|
|
366
|
+
the components defined in the app that the side draws (three levels
|
|
367
|
+
deep). It cannot see a block or kit component's insides, content that
|
|
368
|
+
arrives from props or context, or how long a list really is at run time;
|
|
369
|
+
the docs and review carry those. A screen:
|
|
370
|
+
```jsx
|
|
371
|
+
<AppLayout header={<TopBar crumb={crumb} />} contentMode="fill">
|
|
372
|
+
<PageLayout side={<Projects />} fill>
|
|
373
|
+
<NeedsALook />
|
|
374
|
+
</PageLayout>
|
|
375
|
+
</AppLayout>
|
|
376
|
+
```
|
|
265
377
|
- `Button` — `variant` primary (one per surface, in the app's accent —
|
|
266
378
|
say it: a Button with no `variant` is secondary) · secondary · quiet ·
|
|
267
379
|
danger · link (inline text: no padding or height, wraps and truncates
|
|
@@ -369,6 +481,14 @@ import { RecordSwitcher, LiveEmpty, LiveWork, liveWords, useFixture, FixtureNoti
|
|
|
369
481
|
— a table that scrolls inside itself on a phone.
|
|
370
482
|
- `Card` (`padding` md · sm · none — none for a table or rows that run
|
|
371
483
|
to the border, never `p-0!`; `titleSize` md · lg for a 20px title),
|
|
484
|
+
`ActionCard` (one step the member takes: `icon`, `title`,
|
|
485
|
+
`description`, `children`, `status` = `{ tone, children }` above the
|
|
486
|
+
one full-width `action`; in a row it stretches to the row's height and
|
|
487
|
+
pins its status and action to the bottom, so the buttons line up (`fit`
|
|
488
|
+
is the old only-as-tall-as-its-words, for one lone card; never
|
|
489
|
+
`self-start` on a card in a row) — a connect-and-first-read card, "Add one person", "Bring in a
|
|
490
|
+
file". A screen's boxes are Card, ActionCard or Notice, never a
|
|
491
|
+
hand-drawn bordered div),
|
|
372
492
|
`Notice` (tone neutral · info · success · warning · danger),
|
|
373
493
|
`Badge` (tone neutral · primary · success · warning · danger · info;
|
|
374
494
|
`variant` label — small caps · soft — the case as written, every tone
|
|
@@ -395,7 +515,9 @@ import { RecordSwitcher, LiveEmpty, LiveWork, liveWords, useFixture, FixtureNoti
|
|
|
395
515
|
`Meter value max unit target thresholds` (a role=meter; over the limit
|
|
396
516
|
turns danger; `direction` higher · lower picks the tone against the
|
|
397
517
|
target; `valueTone`; `targetMark={false}`; tone `muted` for a plain
|
|
398
|
-
grey share
|
|
518
|
+
grey share; `compact` the bar (or ring) alone, its label and numbers
|
|
519
|
+
kept for screen readers and as the tooltip — a labelled Meter in a box of
|
|
520
|
+
8rem or less must be `compact`) — numbers and states, never a hand-drawn tile or bar.
|
|
399
521
|
`Delta hideValue` is the arrow alone; `StatusDot` and Legend items take
|
|
400
522
|
`hue` (1..5) for a category. `Distribution segments={[{ label, value,
|
|
401
523
|
tone, hue }]} legend label size` (thin · md) — parts of one whole as
|
|
@@ -990,6 +1112,130 @@ the check refuses a raw `INSERT INTO` anywhere outside `lib/`, and, in
|
|
|
990
1112
|
an app on its own tables, any SQL that writes in the app's code (§6,
|
|
991
1113
|
`agent`).
|
|
992
1114
|
|
|
1115
|
+
### Declared actions — `makersclaw.json` `actions` (0.8.1)
|
|
1116
|
+
|
|
1117
|
+
Every write a screen makes is a declared action: the screens call it by
|
|
1118
|
+
name with `runAction({ name, args })`, and the app's agent calls the same
|
|
1119
|
+
name as one of its tools, run by the platform's one runner. Once an app
|
|
1120
|
+
declares any, its agent can write no other way, so `npm run check`
|
|
1121
|
+
(§6, `actions`) fails a screen write that is not one: an undeclared
|
|
1122
|
+
name, a one-row write (`insertRow`, `updateRow`, `moveRow`, `deleteRow`,
|
|
1123
|
+
`saveSetting`: make it a function) and a company brain write from
|
|
1124
|
+
`lib/brain.js` (erase, confirm and merge stay a person's press).
|
|
1125
|
+
|
|
1126
|
+
```json
|
|
1127
|
+
"actions": [
|
|
1128
|
+
{
|
|
1129
|
+
"name": "add_people",
|
|
1130
|
+
"description": "Add up to 100 people, each linked to their company.",
|
|
1131
|
+
"input": {
|
|
1132
|
+
"type": "object",
|
|
1133
|
+
"properties": {
|
|
1134
|
+
"people": {
|
|
1135
|
+
"type": "array", "maxItems": 100,
|
|
1136
|
+
"items": { "type": "object", "properties": {
|
|
1137
|
+
"name": { "type": "string" }, "email": { "type": "string" },
|
|
1138
|
+
"company": { "type": "string" }, "company_domain": { "type": "string" } },
|
|
1139
|
+
"required": ["name"] }
|
|
1140
|
+
},
|
|
1141
|
+
"list_id": { "type": "string" }
|
|
1142
|
+
},
|
|
1143
|
+
"required": ["people"]
|
|
1144
|
+
},
|
|
1145
|
+
"brain": {
|
|
1146
|
+
"each": "people",
|
|
1147
|
+
"writes": [
|
|
1148
|
+
{ "as": "company", "kind": "company", "when": "company", "fields": { "name": "company", "domain": "company_domain" } },
|
|
1149
|
+
{ "as": "person", "kind": "contact", "fields": { "name": "name", "email": "email", "company_name": "company" } }
|
|
1150
|
+
],
|
|
1151
|
+
"links": [{ "from": "person", "to": "company", "type": "works_at" }]
|
|
1152
|
+
},
|
|
1153
|
+
"sql": "crm_people_added"
|
|
1154
|
+
},
|
|
1155
|
+
{ "name": "make_list", "description": "Make a list of people or companies.",
|
|
1156
|
+
"input": { "type": "object", "properties": { "name": { "type": "string" } }, "required": ["name"] },
|
|
1157
|
+
"sql": "crm_make_list" }
|
|
1158
|
+
]
|
|
1159
|
+
```
|
|
1160
|
+
|
|
1161
|
+
- `input` is a JSON Schema object (type, properties, required, items,
|
|
1162
|
+
enum, min/max, descriptions); the agent reads it whole, so keep it
|
|
1163
|
+
under 12,000 characters.
|
|
1164
|
+
- `sql` is a function `<name>(args jsonb) returns jsonb` in the app's
|
|
1165
|
+
schema, run as the app, with the member set, after the `brain` steps.
|
|
1166
|
+
- `brain` writes company records as the member and the app: each write
|
|
1167
|
+
is `{ as, kind, fields }`, each field read from an input key (`"name"`:
|
|
1168
|
+
the item's, with `each`; `"$.list_id"`: the input's top level;
|
|
1169
|
+
`{ "value": "lead" }`: a constant). An empty value is left out, never
|
|
1170
|
+
cleared. `when` writes it only when that key has a value; `each` runs
|
|
1171
|
+
the writes once per item of a list of at most 100; `links` join two
|
|
1172
|
+
writes (`from`/`to` an `as`, or `{ "id": <key> }`, a record id the
|
|
1173
|
+
input holds) by a link type the kind declares. Every kind is one the
|
|
1174
|
+
app writes (`brain.writes`), every field one of its fields. The `sql`
|
|
1175
|
+
function gets the input plus `records`: for each `as`, `{ id, created
|
|
1176
|
+
}` (a list in item order with `each`; null where `when` skipped it).
|
|
1177
|
+
If the function refuses, the records the call made are taken back.
|
|
1178
|
+
- A write with `"id"` changes the record that input key names (an edit,
|
|
1179
|
+
never a create), as the member and the app may: one they cannot both
|
|
1180
|
+
read, or of another kind, is not found and nothing is written.
|
|
1181
|
+
`"if_version"` names a key holding the version the screen read
|
|
1182
|
+
(refused `changed` when it moved on); `{ "value": null }` empties a
|
|
1183
|
+
field, and `"clear"` names a key listing fields to empty (an array of
|
|
1184
|
+
strings whose `items.enum` are among the write's fields); `{ "now":
|
|
1185
|
+
true }` is the time of the call. If a later step fails, the fields it
|
|
1186
|
+
changed get their old values back.
|
|
1187
|
+
- A link with `"op": "remove"` takes one off (removals run first, so one
|
|
1188
|
+
call moves a link), and an action may be `links` only, with no
|
|
1189
|
+
`writes`. Archive is an edit of the kind's own field (`archived_at`:
|
|
1190
|
+
`{ "now": true }`, back with `{ "value": null }`); erase and merge stay
|
|
1191
|
+
a person's press, from `lib/brain.js`.
|
|
1192
|
+
|
|
1193
|
+
```json
|
|
1194
|
+
{ "name": "move_deal", "description": "Move a deal to a stage and another company.",
|
|
1195
|
+
"input": { "type": "object", "properties": {
|
|
1196
|
+
"deal_id": { "type": "string" }, "version": { "type": "integer" }, "stage": { "type": "string" },
|
|
1197
|
+
"from_company": { "type": "string" }, "to_company": { "type": "string" } },
|
|
1198
|
+
"required": ["deal_id", "stage"] },
|
|
1199
|
+
"brain": {
|
|
1200
|
+
"writes": [{ "as": "deal", "kind": "deal", "id": "$.deal_id", "if_version": "$.version",
|
|
1201
|
+
"fields": { "stage": "$.stage", "stage_changed_at": { "now": true }, "closed_at": { "value": null } } }],
|
|
1202
|
+
"links": [
|
|
1203
|
+
{ "from": "deal", "to": { "id": "$.from_company" }, "type": "for_company", "op": "remove" },
|
|
1204
|
+
{ "from": "deal", "to": { "id": "$.to_company" }, "type": "for_company" }] },
|
|
1205
|
+
"sql": "crm_deal_moved" }
|
|
1206
|
+
```
|
|
1207
|
+
|
|
1208
|
+
- `agent: false` only for a write the agent must never make.
|
|
1209
|
+
- `references/actions.md` says, per action, when and why: never a recipe.
|
|
1210
|
+
|
|
1211
|
+
### Shipped automations — `makersclaw.json` `automations` (0.8.1)
|
|
1212
|
+
|
|
1213
|
+
An app may ship automations: flows the platform runs for it on a schedule
|
|
1214
|
+
or by hand, made of its own declared actions. List them in
|
|
1215
|
+
`makersclaw.json`, under `automations`, next to `actions`. The store ships
|
|
1216
|
+
the same list as `seeds.automations` in `store.json`.
|
|
1217
|
+
|
|
1218
|
+
- Each entry is a definition in the platform's automation format (format
|
|
1219
|
+
1: `key`, `name`, `trigger`, `inputs`, `settings`, `steps`), exactly as
|
|
1220
|
+
the automations blocks draw and save one.
|
|
1221
|
+
- An `action` step names one of the app's declared `actions` (its `app`
|
|
1222
|
+
stays out: an app's automation runs its own app's actions).
|
|
1223
|
+
- `replaces_wake` is the one field that is not part of the definition. It
|
|
1224
|
+
is the name of a legacy wake (an older scheduled job of the app, in its
|
|
1225
|
+
`store.json` `seeds.wakes`) that this automation takes over: when the app
|
|
1226
|
+
is installed or updated the wake is switched off, so the job never fires
|
|
1227
|
+
twice. Leave it out for a new automation. Name a scheduled wake (advance
|
|
1228
|
+
or review), never an event wake, and one automation per wake. The
|
|
1229
|
+
app's `store.json` lists the same name.
|
|
1230
|
+
- At most 20 automations, keys unique.
|
|
1231
|
+
|
|
1232
|
+
`npm run check` runs the list through the platform's own validator (rule
|
|
1233
|
+
`automations`), so a flow the platform would refuse at install is refused
|
|
1234
|
+
at build. It checks the shape, the kinds of step, the expressions and that
|
|
1235
|
+
every `action` step names a declared action. Which wakes exist and which
|
|
1236
|
+
kinds of account a `tool` step may ask for are checked where the whole
|
|
1237
|
+
package is known (the store's publish).
|
|
1238
|
+
|
|
993
1239
|
### Door — `lib/door.js` (browser, both modes)
|
|
994
1240
|
|
|
995
1241
|
The app's own tables, read and written from the browser through the
|
|
@@ -1049,7 +1295,10 @@ whenLanded() resolves once those reads have landed
|
|
|
1049
1295
|
Actions did. A slow save never lands after the newer one.
|
|
1050
1296
|
- A write that answers ok calls `dataChanged()`: every hook on the page
|
|
1051
1297
|
reads again, the brain's too. When the next step needs the new rows
|
|
1052
|
-
(open the row just made), `await whenLanded()` first.
|
|
1298
|
+
(open the row just made), `await whenLanded()` first. Use
|
|
1299
|
+
`afterWrite(write)`: it answers with the write's answer after the hooks
|
|
1300
|
+
hold the new rows; a record page asks `useNotFound(found, { loading })`
|
|
1301
|
+
before it says "not found".
|
|
1053
1302
|
- `loading` is true until a hook's first answer, and again when its
|
|
1054
1303
|
options change. A re-read keeps the last rows, and `loading` false,
|
|
1055
1304
|
until the new ones land; each answer is a new array. A failed re-read
|
|
@@ -1274,6 +1523,14 @@ import { platform } from "../lib/platform"; // or the named functions
|
|
|
1274
1523
|
platform.me() GET _claw/me → { artifact: { id, name, vehicle, status, home }, user: { id, email }, blueprint, desk_origin }
|
|
1275
1524
|
platform.ask({ text, row, how, job, list, surface, sessionId, open })
|
|
1276
1525
|
POST _claw/ask → { ok, request, desk_origin }, and the column opens on it
|
|
1526
|
+
platform.llm(input, { system, schema, maxTokens, tier, task, signal })
|
|
1527
|
+
POST _claw/llm → { ok: true, text } | { ok: true, data } | { ok: false, error, code } (Calling a model)
|
|
1528
|
+
platform.library.collections({ q, shelf }) GET _claw/library/collections → { collections } (Showing the Library)
|
|
1529
|
+
platform.library.collection(id) GET _claw/library/collections/<id> → { collection }
|
|
1530
|
+
platform.library.items(id, { q, tag, cursor, limit }) GET _claw/library/collections/<id>/items → { items, next_cursor, total, preview_only }
|
|
1531
|
+
platform.library.rows(id, { q, filters, cursor, limit })
|
|
1532
|
+
GET _claw/library/collections/<id>/rows → { rows, next_cursor, total, preview_only }
|
|
1533
|
+
platform.library.item(id) GET _claw/library/items/<id> → { item, file_url?, text_excerpt? }
|
|
1277
1534
|
platform.requests() GET _claw/requests → { requests, run, desk_origin }
|
|
1278
1535
|
platform.requests({ id }) GET _claw/requests?id=<id> → { request }
|
|
1279
1536
|
platform.stopRequest(id) POST _claw/requests/<id>/stop → { ok, request }
|
|
@@ -1282,6 +1539,24 @@ platform.surfaceOfPath(pathname) the <key> of /s/<key> in
|
|
|
1282
1539
|
platform.reportError({ message, stack, url, source }) POST _claw/events
|
|
1283
1540
|
platform.startJob(message, { sessionId, onEvent }) POST _claw/chat → streams session · delta · tool · done · error (legacy)
|
|
1284
1541
|
platform.cancelJob(sessionId) POST _claw/cancel (legacy)
|
|
1542
|
+
|
|
1543
|
+
// The kit (0.8.1) — what the kit's blocks draw; lib/hooks.js follows each one
|
|
1544
|
+
platform.jobs() GET _claw/jobs → { app: { status, live }, jobs: [{ id, name, kind, topic, does, cron, tz, words,
|
|
1545
|
+
enabled, next_run_at, last_run_at, last_run, plan }], budget, credits, may: { run, switch, pause } }
|
|
1546
|
+
platform.setJob(id, { enabled }) PATCH _claw/jobs/<id> → { ok, job } (not_allowed 403 · unknown_job 404)
|
|
1547
|
+
platform.runNow(wakeId) POST _claw/run → { ok, run_id, wake } (spends: its price on the button)
|
|
1548
|
+
platform.pauseApp() · platform.resumeApp() POST _claw/pause · _claw/resume → { status } (the Live switch)
|
|
1549
|
+
platform.runs({ job, limit }) GET _claw/runs → { runs: [{ id, title, state, wake_id, job, trigger, by, number,
|
|
1550
|
+
started_at, ended_at, duration_ms, credits, report, status }], overview }
|
|
1551
|
+
platform.run(id) GET _claw/runs/<id> → { run, steps: [{ label, status }], text, needs }
|
|
1552
|
+
platform.activity({ row, surface, since, limit }) GET _claw/data/activity → { rows: [{ id, at, action, surface, row_ref, detail, data, by }] }
|
|
1553
|
+
platform.connections() GET _claw/connect → { connections: [{ key, label, required, connected, accounts, category, category_label }] }
|
|
1554
|
+
platform.connectLink(key) POST _claw/connect → { ok, key, label, connected, url } (outside the desk)
|
|
1555
|
+
platform.openConnections(key) ask the desk to open its connections panel for that need → true when the desk was asked
|
|
1556
|
+
platform.agentKit() GET _claw/agent → { own_skills: [{ name, title, when }], skills: [{ name, installed }],
|
|
1557
|
+
actions: [{ name, description, agent }], … }
|
|
1558
|
+
platform.llmEstimate(input, { system, schema, maxTokens, tier, task })
|
|
1559
|
+
POST _claw/llm, estimate: true → { ok: true, estimate: { credits, credits_max } } (never sent to a model, never charged)
|
|
1285
1560
|
```
|
|
1286
1561
|
|
|
1287
1562
|
A request, as `ask` and `requests` hand it back: `{ id, title, words,
|
|
@@ -1297,13 +1572,32 @@ didnt_finish. `line` is the one line every screen shows for it
|
|
|
1297
1572
|
show it as it is, never a status word of your own. `app_session_id` is
|
|
1298
1573
|
set only on your own requests.
|
|
1299
1574
|
|
|
1300
|
-
Named exports: `me, ask,
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1575
|
+
Named exports: `me, ask, llm, llmEstimate, library, requests,
|
|
1576
|
+
stopRequest, openRequest, threads, thread, surfaceOfPath, startJob,
|
|
1577
|
+
cancelJob, reportError, jobs, setJob, runNow, pauseApp, resumeApp, runs,
|
|
1578
|
+
run, activity, connections, connectLink, openConnections, agentKit,
|
|
1579
|
+
platform`. That is the whole list.
|
|
1580
|
+
|
|
1581
|
+
Up to 0.8.1 the runs, Run now, Pause / Resume and the agent's settings
|
|
1582
|
+
were not here: they were MONITORING, and the OS drew them as the window's
|
|
1583
|
+
tabs so that every app showed its agent's work in one look. The kit
|
|
1584
|
+
(0.8.1) moved that one look into the template's blocks (The agent and AI
|
|
1585
|
+
kit, below), so the app now shows its agent's work itself, beside the
|
|
1586
|
+
rows the work wrote, and calls `_claw/jobs`, `_claw/runs`, `_claw/run`,
|
|
1587
|
+
`_claw/pause`, `_claw/resume`, `_claw/agent` and `_claw/connect` through
|
|
1588
|
+
these wrappers. The window's tabs stay, as the full history. Two things
|
|
1589
|
+
never move into the app: **the account sign-in and money** stay on the
|
|
1590
|
+
desk. The agent's questions are answered in the app (`answer`), and the
|
|
1591
|
+
yes is given in the app too, but never by the app: it is a press in a
|
|
1592
|
+
Makersclaw-held frame (`approval`, the `approval` block). The door decides
|
|
1593
|
+
who may press what (`may` on `jobs`): hide what the member may not press,
|
|
1594
|
+
and the door refuses it anyway.
|
|
1595
|
+
Asking is its own thing, and it belongs here too (`ask()` and `useJob`,
|
|
1596
|
+
below). `llm()` is neither: one call to a model, answered in the page
|
|
1597
|
+
(Calling a model, below). It is the one wrapper that resolves the platform's answer
|
|
1598
|
+
as it is, `{ ok: true, text }` and not `{ ok: true, status, data }`.
|
|
1599
|
+
`library` is neither asking nor monitoring: it reads what Makersclaw
|
|
1600
|
+
keeps, read only (Showing the Library, below).
|
|
1307
1601
|
`requests` is not monitoring either: it is this app's asks in flight, so
|
|
1308
1602
|
an empty surface can show the work coming (§3a). `startJob` and
|
|
1309
1603
|
`cancelJob` are the 0.3.x door to the same work, kept so `useJob` can
|
|
@@ -1316,10 +1610,13 @@ that exact origin — never `"*"` — and only while the app is framed.
|
|
|
1316
1610
|
|
|
1317
1611
|
### Hooks — `lib/hooks.js` ("use client")
|
|
1318
1612
|
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1613
|
+
A generic fetch over one `_claw` route, the app's identity, a job, the
|
|
1614
|
+
app's requests, a model call, five that read the Library, the app's own
|
|
1615
|
+
chat, and the kit's eight (0.8.1). Each owns
|
|
1616
|
+
fetching, polling (while the tab is visible; once a minute while
|
|
1617
|
+
`unavailable`; the Library's read once and have nothing to poll),
|
|
1618
|
+
streaming and cleanup on unmount. A surface renders what comes back and
|
|
1619
|
+
owns every pixel.
|
|
1323
1620
|
|
|
1324
1621
|
```js
|
|
1325
1622
|
useClaw(path, { every, enabled = true }) → { data, error, loading, unavailable, reload }
|
|
@@ -1328,9 +1625,50 @@ useJob() → { running, status, queued, que
|
|
|
1328
1625
|
steps, text, progress, error, sessionId, requestId, request, finishedAt,
|
|
1329
1626
|
start, cancel, reset, open }
|
|
1330
1627
|
useRequests({ surface, job, every = 5000 }) → { requests, live, run, loading, error, unavailable, reload, open, stop }
|
|
1628
|
+
useLlm() → { run, loading, result, error, unavailable } (Calling a model)
|
|
1629
|
+
useLibraryCollections({ q, shelf }) → { collections, loading, error, code, unavailable, reload } (Showing the Library)
|
|
1630
|
+
useLibraryCollection(id) → { collection, loading, error, code, unavailable, reload }
|
|
1631
|
+
useLibraryItems(id, { q, tag, limit }) → { items, total, previewOnly, hasMore, loading, loadingMore, error, code, unavailable, loadMore, reload }
|
|
1632
|
+
useLibraryRows(id, { q, filters, limit }) → { rows, total, previewOnly, hasMore, loading, loadingMore, error, code, unavailable, loadMore, reload }
|
|
1633
|
+
useLibraryItem(id) → { item, fileUrl, textExcerpt, loading, error, code, unavailable, reload }
|
|
1634
|
+
useAgentThread({ row, how, job, key, view }) → { sessionId, state, needs, turns, view, pending, place, request, sending, loading, error,
|
|
1635
|
+
code, unavailable, send, stop, answer({ optionId, text } | { answers }), openOnDesk, newChat,
|
|
1636
|
+
openSession(sessionId), reload } (the agent-chat block)
|
|
1637
|
+
useAgentPane({ row, how, job, key }) → useAgentThread with the desk sidebar's view, plus { chats: { items, loading, error, unavailable },
|
|
1638
|
+
callDetail(callId), openRequest(id) } (the agent-pane block, D27; GET _claw/threads/<session>?view=1)
|
|
1639
|
+
useThreadList({ every = 30000 }) → { items: [{ sessionId, requestId, title, mark, at }], loading, error, unavailable, reload } (the pane's Chats list)
|
|
1331
1640
|
jobFromRequest(request) → what useJob shows for one request (pure; for tests)
|
|
1641
|
+
|
|
1642
|
+
// The kit (0.8.1): each also returns { loading, error, code, unavailable, reload }
|
|
1643
|
+
useJobs({ every = 30000 }) → { jobs, app, budget, credits, may, busy, appBusy, runNow(id), setEnabled(id, on), pause(), resume() }
|
|
1644
|
+
useRuns({ job, limit = 20, every = 30000 }) → { runs, overview } every 5 s while a run is going
|
|
1645
|
+
useRun(id) → { run, steps, text, needs } every 2.5 s while it runs; nothing without an id
|
|
1646
|
+
useNeedsYou({ every = 8000 }) → { items: [{ id, request, kind, text, title, at, via, options, free, needsAt }], working, open(item), answer(item, { optionId, text }), takeBack(item), stop(request) }
|
|
1647
|
+
useApproval(requestId) → { approval, state, always, fallback, frame: { src, ref, title }, height } (the approval block; GET _claw/requests/<id>/approval)
|
|
1648
|
+
useActivity({ row, surface, since, limit = 50, agent = false, every = 20000 })
|
|
1649
|
+
→ { rows } agent: true keeps the agent's lines
|
|
1650
|
+
useConnections({ every }) → { connections, connecting, connect(key) → { opened: "desk" | "tab" | false, error? } }
|
|
1651
|
+
useAgentKit() → { skills: [{ name, title, when, from: "app" | "added", installed }], tools: [{ name, description }] }
|
|
1652
|
+
useLlmAction({ input, system, schema, tier, task, maxTokens, estimate = true })
|
|
1653
|
+
→ { estimate: { credits, credits_max } | null, estimating, run(), result, loading, error, unavailable, reset() }
|
|
1654
|
+
needsYouItem(request) · agentKitOf(data) · connectAccount(key) the kit hooks' pure parts (for tests)
|
|
1655
|
+
|
|
1656
|
+
// The automations engine (lib/automations.js, re-exported here): an app's own automations, from its own page
|
|
1657
|
+
useAutomations({ every = 30000 }) → { automations, busy, creating, setStatus(id, on), create(definition, { note, goLive }) } every 5 s while one runs
|
|
1658
|
+
useAutomation(id) → { automation, live, draft, definition, versions, estimate, estimateDraft, runs, busy: "save" | "keep" | "live" | "test" | null,
|
|
1659
|
+
save(definition), keep(), setLive(on), test(inputs) } resolve the platform's answer; a refusal carries `problems`
|
|
1660
|
+
useAutomationRun(runId) → { run, steps, stepsVisible, automation, definition, detail } every 2.5 s while it runs
|
|
1332
1661
|
```
|
|
1333
1662
|
|
|
1663
|
+
The kit's hooks poll only while the page is shown (not behind the
|
|
1664
|
+
window's other tabs, not in a hidden browser tab; coming back looks at
|
|
1665
|
+
once), look once a minute while their door is missing, abort on unmount
|
|
1666
|
+
and never throw. A missing door is `unavailable` with `error` null: a
|
|
1667
|
+
quiet "not connected yet". `useJobs`' Run now makes every `useRuns`,
|
|
1668
|
+
`useRequests` and `useNeedsYou` on the page look at once. `useActivity`
|
|
1669
|
+
here is the kit's: polled, with `row` and `agent`; `lib/door.js`'s
|
|
1670
|
+
`useActivity` (§5 Door) reads the same lines once.
|
|
1671
|
+
|
|
1334
1672
|
`useRequests` is the work asked of this app's agent — from a screen or
|
|
1335
1673
|
from the desk, by anyone in the workspace. `requests` is every one the
|
|
1336
1674
|
gateway returned (the live ones and those that ended in the last day);
|
|
@@ -1344,10 +1682,11 @@ while the App tab is on screen (behind the window's other tabs it asks
|
|
|
1344
1682
|
nothing; coming back looks at once); a start through `useJob` on the
|
|
1345
1683
|
same page makes it look at once. Hand it to `LiveEmpty` (§3a).
|
|
1346
1684
|
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1685
|
+
Up to 0.8.1 this said there was no chat or runs hook, because the OS
|
|
1686
|
+
drew those tabs. Since the kit there are both, and the blocks that draw
|
|
1687
|
+
them: a record's history beside the record is the `activity-log` block
|
|
1688
|
+
over `useActivity({ row })`; on the server in a server app it is
|
|
1689
|
+
`loadActivity`.
|
|
1351
1690
|
|
|
1352
1691
|
### Commissioning work — `useJob()`
|
|
1353
1692
|
|
|
@@ -1504,6 +1843,506 @@ if (!r.ok) setError(r.error); // r.code: needs_answer, queue_fu
|
|
|
1504
1843
|
It resolves once the platform took the ask (`{ ok: true, data: {
|
|
1505
1844
|
request, desk_origin } }`) or refused it; it never waits for the work.
|
|
1506
1845
|
|
|
1846
|
+
### The agent and AI kit — blocks (0.8.1)
|
|
1847
|
+
|
|
1848
|
+
The app shows its agent's work and its AI features itself, in its own
|
|
1849
|
+
look: a chat beside the record, the schedule and its runs, what waits on
|
|
1850
|
+
the member, the price before a press. Up to 0.8.1 the OS drew most of
|
|
1851
|
+
this as the window's tabs, so every app looked the same; now the same
|
|
1852
|
+
look lives in these blocks, and the tabs stay as the full history. Each
|
|
1853
|
+
block is `makersclaw add <name>`, reads nothing itself, and takes the
|
|
1854
|
+
hook's answer as props (a block never imports `lib/`):
|
|
1855
|
+
|
|
1856
|
+
- `agent-chat` (`AgentChat`) — the app's own chat with its agent, about
|
|
1857
|
+
the app or one record. `useAgentThread`.
|
|
1858
|
+
- `ask-bar` (`AskBar`) — one line to ask, with chips; it starts the
|
|
1859
|
+
thread and opens the chat. `useAgentThread`.
|
|
1860
|
+
- `needs-you` (`NeedsYou`, `QuestionBox`, `WaitingYes`, `AgentWorking`) —
|
|
1861
|
+
what waits on this member (a question answered in place, a yes through
|
|
1862
|
+
the `approval` block), the frozen "Ready to send" with Take back, the
|
|
1863
|
+
working strip with Stop. `useNeedsYou`.
|
|
1864
|
+
- `approval` (`Approval`) — the yes, given in the app: the app's own card
|
|
1865
|
+
(what goes out, to whom, the cost) over a Makersclaw-held press frame in
|
|
1866
|
+
the app's theme that app code cannot script, with "Always allow this
|
|
1867
|
+
kind". `useApproval`.
|
|
1868
|
+
- `activity-log` (`ActivityLog`) — who did what, newest first, the
|
|
1869
|
+
agent as an author; with `row`, one record's timeline. `useActivity`.
|
|
1870
|
+
- `agent-marks` (`AgentMark`, `CallMark`, `Byline`) — the agent's mark,
|
|
1871
|
+
a model call's mark, "By the agent, on Priya's ask". No hook.
|
|
1872
|
+
- `schedule-card` (`ScheduleCard`) — one job: its schedule in words, its
|
|
1873
|
+
last run, next run, Run now with its price, the on/off switch.
|
|
1874
|
+
`useJobs`.
|
|
1875
|
+
- `run-history` (`RunHistory`, `RunAccount`, `RunOverview`) — the runs,
|
|
1876
|
+
one run's account, the overview against the week's cap. `useRuns`,
|
|
1877
|
+
`useRun`.
|
|
1878
|
+
- `workflow-view` (`WorkflowView`) — the job as a graph: Trigger → steps
|
|
1879
|
+
→ result, the run picker, Live, Trigger manually, its runs beside.
|
|
1880
|
+
`useJobs`, `useRuns`, `useRun`.
|
|
1881
|
+
- `suggestion` (`FieldSuggestion`, `InlineSuggestion`, `TextSuggestion`,
|
|
1882
|
+
`Pencil`, `SuggestionGroup`, `Looking`) — a value the agent proposes,
|
|
1883
|
+
in pencil until kept. No hook: Keep and Drop call the app's declared
|
|
1884
|
+
actions (`runAction`).
|
|
1885
|
+
- `source-receipt` (`SourceReceipt`) — where a value came from, as a
|
|
1886
|
+
clipping. No hook: the source is app data.
|
|
1887
|
+
- `agent-column` — a column the agent fills, each answer with its state,
|
|
1888
|
+
how sure it is, and its receipt. The app's rows (`lib/data.js`).
|
|
1889
|
+
- `record-grid` — a virtualised grid of many records. The app's rows
|
|
1890
|
+
(`lib/data.js`).
|
|
1891
|
+
- `ai-action` (`AiAction`) — one model call on what the screen holds,
|
|
1892
|
+
its price on the button, the answer in place with Use. `useLlmAction`.
|
|
1893
|
+
- `cost` (`CostTag`, `CostConfirm`, `Allowance`) — the price on a
|
|
1894
|
+
control, the confirm before a big spend, the week's allowance.
|
|
1895
|
+
`useLlmAction`'s estimate, `useJobs`' budget.
|
|
1896
|
+
- `agent-changes` (`AgentChanges`) — the receipt of one ask, with Undo
|
|
1897
|
+
this ask. `useActivity({ agent: true, since })`.
|
|
1898
|
+
- `connect-card` (`ConnectCard`, `ConnectRow`) — the app's accounts,
|
|
1899
|
+
live or not, Connect. `useConnections`.
|
|
1900
|
+
- `agent-kit` (`AgentKit`) — what the agent knows and what it can do.
|
|
1901
|
+
`useAgentKit`.
|
|
1902
|
+
- `library-shelf` (`LibraryShelf`) — "Start from a template": a Library
|
|
1903
|
+
collection's items. `useLibraryItems`.
|
|
1904
|
+
|
|
1905
|
+
The rules:
|
|
1906
|
+
|
|
1907
|
+
1. **Use the kit.** Never redraw a chat, a schedule, a run list, a
|
|
1908
|
+
needs-you queue, a suggestion or a price tag by hand: the block is
|
|
1909
|
+
the one look every app shares, and it grows in the template.
|
|
1910
|
+
2. **The sign-in and money stay on the desk; the yes is the press
|
|
1911
|
+
frame's.** The agent's question is answered in the app (`QuestionBox`,
|
|
1912
|
+
`answer`). The agent's yes is the `approval` block: you draw what goes
|
|
1913
|
+
out, the frame holds the press, and when it cannot load the block falls
|
|
1914
|
+
back to "Waiting for your yes on the desk" and opens it
|
|
1915
|
+
(`openRequest`). Connect opens the desk's connections panel; topping up
|
|
1916
|
+
opens the desk. Never an Approve, Yes or Send of your own that gives
|
|
1917
|
+
the yes, a password field or a payment form in the app.
|
|
1918
|
+
3. **Cost on every control that spends.** Run now, Read now, Enrich,
|
|
1919
|
+
Summarize: the price is on the button (`CostTag`, `useLlmAction`'s
|
|
1920
|
+
estimate) before the press, and a big spend asks first
|
|
1921
|
+
(`CostConfirm`).
|
|
1922
|
+
4. **The door decides who may press.** `useJobs().may` says who may run,
|
|
1923
|
+
switch and pause; hide what the member may not press. The door
|
|
1924
|
+
refuses it anyway.
|
|
1925
|
+
5. **A missing door is quiet.** On a claw from before a door the hook is
|
|
1926
|
+
`unavailable`: the block shows its empty state ("No runs yet", "Not
|
|
1927
|
+
set up"), never an error.
|
|
1928
|
+
6. **Its own width decides its layout.** Every kit block lays itself out
|
|
1929
|
+
by `@container`, at 320, 368, 800 and 1280 px of its own width; give
|
|
1930
|
+
it the width, never a viewport breakpoint of yours.
|
|
1931
|
+
|
|
1932
|
+
### Calling a model — `llm()`
|
|
1933
|
+
|
|
1934
|
+
`llm()` is for one-shot text work on what is on screen: sort these
|
|
1935
|
+
mails, draft this reply, translate this note, summarise this thread,
|
|
1936
|
+
pull the date out of this line. The answer comes back in the page, and
|
|
1937
|
+
the screen does what it likes with it.
|
|
1938
|
+
|
|
1939
|
+
**`llm()` for one-shot text work on what the screen holds; `ask()` for
|
|
1940
|
+
work that needs tools, the workspace, or more than one step.** Finding
|
|
1941
|
+
leads, reading the Wiki, writing rows for forty threads, anything that
|
|
1942
|
+
should show in the agent column and the Requests list: that is `ask()`.
|
|
1943
|
+
The app's agent does it, with its tools. `llm()` has none: it sees the
|
|
1944
|
+
words you hand it and nothing else.
|
|
1945
|
+
|
|
1946
|
+
```js
|
|
1947
|
+
"use client";
|
|
1948
|
+
import { updateRow } from "../lib/actions";
|
|
1949
|
+
import { llm } from "../lib/platform";
|
|
1950
|
+
|
|
1951
|
+
// Text.
|
|
1952
|
+
const r = await llm(thread.body, {
|
|
1953
|
+
system: "Draft a short, warm reply from the founder. Plain text, no greeting.",
|
|
1954
|
+
task: "draft-reply",
|
|
1955
|
+
});
|
|
1956
|
+
if (r.ok) setDraft(r.text); // r.error says why not
|
|
1957
|
+
|
|
1958
|
+
// Data: a schema in, an answer checked against it out.
|
|
1959
|
+
const SORT = {
|
|
1960
|
+
type: "object",
|
|
1961
|
+
required: ["label", "urgent"],
|
|
1962
|
+
properties: { label: { enum: ["sales", "support", "billing", "other"] }, urgent: { type: "boolean" } },
|
|
1963
|
+
};
|
|
1964
|
+
const s = await llm(`From: ${mail.from}\nSubject: ${mail.subject}\n\n${mail.body}`, { system: "Sort this mail.", schema: SORT, task: "sort-mail" });
|
|
1965
|
+
if (s.ok) await updateRow({ source: "mail", id: mail.id, patch: { label: s.data.label, urgent: s.data.urgent } });
|
|
1966
|
+
```
|
|
1967
|
+
|
|
1968
|
+
`data` is the model's JSON as it came: **a key your schema does not name
|
|
1969
|
+
is kept**. Read the keys you asked for, as above, and do not spread
|
|
1970
|
+
`data` into a row.
|
|
1971
|
+
|
|
1972
|
+
`llm(input, opts)` takes:
|
|
1973
|
+
|
|
1974
|
+
- `input` — what to work on: a string, or a conversation, `[{ role:
|
|
1975
|
+
"user" | "assistant", content }]`: at most 100 messages, none empty,
|
|
1976
|
+
the last one the user's (the one to answer). Required. An empty one
|
|
1977
|
+
asks nothing of the platform and resolves `bad_input`.
|
|
1978
|
+
- `system` — how to go about it, at most 8,000 characters.
|
|
1979
|
+
- `schema` — a JSON Schema for the answer, an object, at most 16 KB
|
|
1980
|
+
once written out. The answer then comes back as `data`, checked
|
|
1981
|
+
against it, not as `text`. If the model's reply misses, the platform
|
|
1982
|
+
sends it back once with what was wrong; a second miss is
|
|
1983
|
+
`schema_mismatch`. The platform runs your schema on the model's words,
|
|
1984
|
+
so what it may hold is bounded (below).
|
|
1985
|
+
- `maxTokens` — the answer's ceiling, a whole number, 1 or more. 1,024
|
|
1986
|
+
unless you say; over 4,096 is cut to 4,096. (It travels as
|
|
1987
|
+
`max_tokens`.)
|
|
1988
|
+
- `tier` — `"fast"` (the default) or `"smart"` for the harder job. An
|
|
1989
|
+
app never names a model. `smart` costs more and is slower: a long
|
|
1990
|
+
answer from it can run into the 60 second limit, so keep `maxTokens`
|
|
1991
|
+
modest there.
|
|
1992
|
+
- `task` — a short label for the usage line, `a-z`, `0-9` and `-`, at
|
|
1993
|
+
most 40 characters (`"sort-mail"`).
|
|
1994
|
+
- `signal` — an `AbortSignal`. An abort resolves `{ ok: false, aborted:
|
|
1995
|
+
true }`.
|
|
1996
|
+
|
|
1997
|
+
**What a schema may hold.** Write it out as a tree, small. The call
|
|
1998
|
+
refuses a schema with:
|
|
1999
|
+
|
|
2000
|
+
- `pattern` or `patternProperties` (`bad_input`): a regular expression
|
|
2001
|
+
run on a model's words can hang the platform. Use `enum`, `format` or
|
|
2002
|
+
length bounds.
|
|
2003
|
+
- `$ref`, `$defs` or `definitions` (`bad_input`; `$dynamicRef` and
|
|
2004
|
+
`$recursiveRef` too): each reference is checked in full where it is
|
|
2005
|
+
used, so a few can mean a tree of any size. Write the shape out where
|
|
2006
|
+
it is used.
|
|
2007
|
+
- nesting deeper than 20 levels, a list inside a list counted
|
|
2008
|
+
(`bad_input`).
|
|
2009
|
+
- more than 1,000 parts (`too_large`): every object in it, every `enum`
|
|
2010
|
+
value and every type listed in a `type` array. A real answer's schema
|
|
2011
|
+
is a few dozen.
|
|
2012
|
+
- a schema it cannot read, such as a `type` it does not know
|
|
2013
|
+
(`bad_input`).
|
|
2014
|
+
|
|
2015
|
+
A reply with so many values that checking it against the schema would
|
|
2016
|
+
cost too much is a miss like any other (`schema_mismatch`, and the
|
|
2017
|
+
model is told why): ask for fewer values, or a smaller schema. Make the
|
|
2018
|
+
root an object (`type: "object"`): providers' structured answers mostly
|
|
2019
|
+
want one, and when one refuses the format the platform asks again in
|
|
2020
|
+
words alone, which is less sure. Everything else in JSON Schema is let
|
|
2021
|
+
through.
|
|
2022
|
+
|
|
2023
|
+
It resolves, and never throws:
|
|
2024
|
+
|
|
2025
|
+
- `{ ok: true, text }` — no schema.
|
|
2026
|
+
- `{ ok: true, data }` — with a schema.
|
|
2027
|
+
- `{ ok: false, error, code, status }` — a refusal, as the platform
|
|
2028
|
+
sent it. Branch on `code`. **Show `error` as it is for the codes that
|
|
2029
|
+
speak to the member, and a plain line of your own for the rest.** The
|
|
2030
|
+
rest speak to you, the developer ("schema uses "pattern""), and a
|
|
2031
|
+
member should not read them.
|
|
2032
|
+
|
|
2033
|
+
Words for the member, shown as they are:
|
|
2034
|
+
|
|
2035
|
+
- `no_credits` (402): the workspace's wallet is empty, the workspace
|
|
2036
|
+
is suspended, or the Free plan's daily limit is used up.
|
|
2037
|
+
`member_cap` (402): the member's monthly spend cap is used up.
|
|
2038
|
+
`app_cap` (402): this app's weekly cap is used up. Say so and stop;
|
|
2039
|
+
do not retry.
|
|
2040
|
+
- `rate_limited` (429): 30 calls a minute per member. Wait a moment,
|
|
2041
|
+
then again.
|
|
2042
|
+
- `model_failed` (502), `try_again` (503) and `failed` (500): the
|
|
2043
|
+
model, the membership check or the platform could not answer just
|
|
2044
|
+
now. Try again.
|
|
2045
|
+
- `timeout` (504): a call is held to 60 seconds, a schema retry
|
|
2046
|
+
included, and one the clock cuts is still charged. Ask for less (a
|
|
2047
|
+
lower `maxTokens`, a shorter input) rather than the same again.
|
|
2048
|
+
- `no_member_identity` (401) and `not_a_member` (403): the page is not
|
|
2049
|
+
running in a member's own session (`no_member_identity` says to open
|
|
2050
|
+
the app from the desk again), or the member was removed from the
|
|
2051
|
+
workspace.
|
|
2052
|
+
|
|
2053
|
+
Words for you, not shown:
|
|
2054
|
+
|
|
2055
|
+
- `bad_input` (400): the call itself is wrong; fix the code.
|
|
2056
|
+
- `too_large` (413): the body is over 128 KB, `system` over 8,000
|
|
2057
|
+
characters, `schema` over 16 KB or 1,000 parts, or `input` over 100
|
|
2058
|
+
messages. Send less.
|
|
2059
|
+
- `schema_mismatch` (422): the model could not fit the schema, twice;
|
|
2060
|
+
`error` names where it missed. Loosen the schema or ask for less.
|
|
2061
|
+
- `cross_origin` (403): not the app's own page (a typed URL, a form
|
|
2062
|
+
post).
|
|
2063
|
+
- `not_configured` (503): the platform has no model to give.
|
|
2064
|
+
- `{ ok: false, unavailable: true }` — the platform has no such door (a
|
|
2065
|
+
claw from before it) or there is no network. Render the quiet "not
|
|
2066
|
+
connected yet" of every `_claw` door, or leave the button out.
|
|
2067
|
+
|
|
2068
|
+
`useLlm()` is the same call from a component: `{ run, loading, result,
|
|
2069
|
+
error, unavailable }`. `run(input, opts)` takes the same arguments and
|
|
2070
|
+
resolves the same answer; `result` is that answer when it was ok, `error`
|
|
2071
|
+
the refusal's words, and `unavailable` is true on a claw with no door (or
|
|
2072
|
+
no network), when `error` stays null: that is the quiet "not connected
|
|
2073
|
+
yet", not an alert, as for `useClaw`. A new run aborts the one before it,
|
|
2074
|
+
so a slow answer never lands over a newer one, and leaving the page
|
|
2075
|
+
aborts the call in flight. `error` is the sentence whatever the code, so
|
|
2076
|
+
a screen that must tell the money (`no_credits`, `member_cap`, `app_cap`)
|
|
2077
|
+
from the rest reads `code` from what `run()` resolves.
|
|
2078
|
+
|
|
2079
|
+
```jsx
|
|
2080
|
+
"use client";
|
|
2081
|
+
import { Button, Notice } from "../components/ui";
|
|
2082
|
+
import { useLlm } from "../lib/hooks";
|
|
2083
|
+
|
|
2084
|
+
export default function Translate({ note }) {
|
|
2085
|
+
const { run, loading, result, error, unavailable } = useLlm();
|
|
2086
|
+
if (unavailable) return <p className="text-sm text-muted-foreground">Translation is not connected yet.</p>;
|
|
2087
|
+
return (
|
|
2088
|
+
<div className="grid gap-2">
|
|
2089
|
+
<Button loading={loading} onClick={() => run(`Translate to French:\n${note.text}`, { task: "translate" })}>
|
|
2090
|
+
Translate
|
|
2091
|
+
</Button>
|
|
2092
|
+
{error ? <Notice tone="danger">{error}</Notice> : result ? <p className="text-sm">{result.text}</p> : null}
|
|
2093
|
+
</div>
|
|
2094
|
+
);
|
|
2095
|
+
}
|
|
2096
|
+
```
|
|
2097
|
+
|
|
2098
|
+
What to keep in mind:
|
|
2099
|
+
|
|
2100
|
+
- **Browser only, in both modes.** The call rides the member's session:
|
|
2101
|
+
`llm()` and `useLlm()` run in a `"use client"` file. A server
|
|
2102
|
+
component or a server action has no member to charge and cannot call
|
|
2103
|
+
it.
|
|
2104
|
+
- **It spends the workspace's credits**, like any inference, and counts
|
|
2105
|
+
against the member's monthly cap and the app's weekly cap. A schema
|
|
2106
|
+
call the model misses is asked again once, and both are charged; so is
|
|
2107
|
+
a call the 60 second clock cuts. Call it on a press, never on every
|
|
2108
|
+
render or every keystroke, and never once per row of a long list: that
|
|
2109
|
+
is a batch, and a batch is `ask()`'s.
|
|
2110
|
+
- **The answer is words, not markup.** Render `text` as text. Never put a
|
|
2111
|
+
model's answer in `dangerouslySetInnerHTML`.
|
|
2112
|
+
- **The answer is not saved.** What matters goes into a row
|
|
2113
|
+
(`updateRow`, `insertRow`) before the page closes.
|
|
2114
|
+
- **No streaming.** The answer arrives whole.
|
|
2115
|
+
- **Nothing appears on the desk.** No request, no thread, no run: the
|
|
2116
|
+
agent column and the Requests list are `ask()`'s. The call is metered
|
|
2117
|
+
to the member who made it; the usage line names the app, the tier and
|
|
2118
|
+
the `task` label. Claw does not log the request or the reply.
|
|
2119
|
+
- **Never by its path.** A string naming `_claw/llm` fails
|
|
2120
|
+
`npm run check` (§6, `llm`): the two functions are the contract, and
|
|
2121
|
+
they handle what a bare `fetch` would not (a claw without the door, a
|
|
2122
|
+
dropped network, an abort).
|
|
2123
|
+
|
|
2124
|
+
### Showing the Library — `library`
|
|
2125
|
+
|
|
2126
|
+
The Library is what Makersclaw keeps for every workspace: **collections of
|
|
2127
|
+
files** (pitch decks, PDFs, HTML website templates, graphics) and
|
|
2128
|
+
**collections of rows** (a dataset: a list of investors, growth
|
|
2129
|
+
examples). Members browse it in the Library window on the desk. An app
|
|
2130
|
+
can show the same content inside a screen, read live from the platform.
|
|
2131
|
+
|
|
2132
|
+
Use it when the app **shows examples, templates or a dataset that
|
|
2133
|
+
Makersclaw keeps**: a gallery of pitch decks, a picker of website
|
|
2134
|
+
templates to start from, a table of investors to choose from. The screen
|
|
2135
|
+
asks the Library when it opens, so what it shows is what the Library
|
|
2136
|
+
holds today.
|
|
2137
|
+
|
|
2138
|
+
Not for:
|
|
2139
|
+
|
|
2140
|
+
- **The app's own data.** That is your tables (§5, Your tables) and the
|
|
2141
|
+
company brain. The Library is Makersclaw's, the same for every
|
|
2142
|
+
workspace; nothing a member does in the app is written to it.
|
|
2143
|
+
- **Sample rows for an empty screen.** Examples of your own app's data
|
|
2144
|
+
are `useFixture` (§3a), never the Library.
|
|
2145
|
+
- **Copying into the workspace.** The Library is read only: the door has
|
|
2146
|
+
no write. A member who wants a collection's rows in their company
|
|
2147
|
+
data, or a file in their Files, asks the agent, which has the Library's
|
|
2148
|
+
tools and records the copy:
|
|
2149
|
+
|
|
2150
|
+
```js
|
|
2151
|
+
import { ask } from "../lib/platform";
|
|
2152
|
+
|
|
2153
|
+
ask({ text: `Copy the "${collection.title}" list into our company data`, how: `The Library collection is "${collection.slug}".` });
|
|
2154
|
+
```
|
|
2155
|
+
|
|
2156
|
+
#### Reading it
|
|
2157
|
+
|
|
2158
|
+
```js
|
|
2159
|
+
import { library } from "../lib/platform";
|
|
2160
|
+
|
|
2161
|
+
library.collections({ q, shelf }) → data { collections: [card] }
|
|
2162
|
+
library.collection(id) → data { collection }
|
|
2163
|
+
library.items(id, { q, tag, cursor, limit }) → data { items, next_cursor, total, preview_only }
|
|
2164
|
+
library.rows(id, { q, filters, cursor, limit }) → data { rows, next_cursor, total, preview_only }
|
|
2165
|
+
library.item(id) → data { item, file_url?, text_excerpt? }
|
|
2166
|
+
```
|
|
2167
|
+
|
|
2168
|
+
Each resolves `{ ok, status, data }` or `{ ok: false, status, error,
|
|
2169
|
+
unavailable, code }` like `me`, and never throws. A collection's `id` is
|
|
2170
|
+
its slug (`"pitch-decks"`) or its uuid; an item's is its uuid. An id that
|
|
2171
|
+
names nothing asks nothing of the platform. Every call takes `signal`
|
|
2172
|
+
among its options.
|
|
2173
|
+
|
|
2174
|
+
- `shelf` is one of `decks`, `websites`, `designs`, `documents`,
|
|
2175
|
+
`datasets`, `examples`. `q` is a search. A card is `{ id, slug, title,
|
|
2176
|
+
blurb, kind: "files" | "rows", shelf, tags, access: "free" | "paid",
|
|
2177
|
+
status, public_page, cover_url, item_count, row_count, usable,
|
|
2178
|
+
preview_only }`; `collection` adds `body`, `columns`, `source`,
|
|
2179
|
+
`licence`, `preview_count`, `personal_data`, `published_at`.
|
|
2180
|
+
- **Items** (a files collection): `{ id, title, description, tags,
|
|
2181
|
+
format, mime, bytes, preview_url, source_url, licence, position,
|
|
2182
|
+
locked }`. `format` is `pdf`, `image`, `html`, `slides`, `document`,
|
|
2183
|
+
`sheet`, `archive` or `other`. `tag` narrows by a tag. `limit` is a
|
|
2184
|
+
whole number: 30 items a page unless you say, and more than 60 is cut
|
|
2185
|
+
to 60.
|
|
2186
|
+
- **Rows** (a rows collection): `{ id, data, source_url }`. `data` is
|
|
2187
|
+
keyed by the collection's `columns` (`{ key, label, type, filter, … }`;
|
|
2188
|
+
`type` is `text`, `long_text`, `number`, `url`, `email`, `image`,
|
|
2189
|
+
`tags` or `date`). `filters` is `{ <column key>: value }` for the
|
|
2190
|
+
columns with `filter: true`, an exact match (for a `tags` column, rows
|
|
2191
|
+
that have the tag); any other column is refused in words. `limit`: 50
|
|
2192
|
+
rows a page unless you say, and more than 100 is cut to 100.
|
|
2193
|
+
`source_url` is where the row
|
|
2194
|
+
was found: show it beside a person, an investor or a figure.
|
|
2195
|
+
- **Paging.** A page with more answers `next_cursor`: pass it back as
|
|
2196
|
+
`cursor`. Take it as given; do not read or build one.
|
|
2197
|
+
- `item(id)` is the item whole: its file's signed address as `file_url`
|
|
2198
|
+
and the first 4,000 characters of its text as `text_excerpt` (the
|
|
2199
|
+
start of a PDF's words, an HTML page's source).
|
|
2200
|
+
|
|
2201
|
+
The hooks read once, when the screen opens or an argument changes, and
|
|
2202
|
+
page for you:
|
|
2203
|
+
|
|
2204
|
+
```js
|
|
2205
|
+
useLibraryCollections({ q, shelf }) → { collections, loading, error, code, unavailable, reload }
|
|
2206
|
+
useLibraryCollection(id) → { collection, loading, error, code, unavailable, reload }
|
|
2207
|
+
useLibraryItems(id, { q, tag, limit }) → { items, total, previewOnly, hasMore, loading, loadingMore, error, code, unavailable, loadMore, reload }
|
|
2208
|
+
useLibraryRows(id, { q, filters, limit }) → { rows, total, previewOnly, hasMore, loading, loadingMore, error, code, unavailable, loadMore, reload }
|
|
2209
|
+
useLibraryItem(id) → { item, fileUrl, textExcerpt, loading, error, code, unavailable, reload }
|
|
2210
|
+
```
|
|
2211
|
+
|
|
2212
|
+
`items` and `rows` hold the pages read so far; `loadMore()` reads the
|
|
2213
|
+
next and adds it (`hasMore` says there is one; it does nothing while a
|
|
2214
|
+
page is out). A new `id`, `q`, `tag`, `filters` or `limit` starts the
|
|
2215
|
+
list over, showing `loading` and no rows, never the last list: type-ahead
|
|
2216
|
+
should wait for a pause before it changes `q`. Nothing is read until
|
|
2217
|
+
there is an `id` (and `enabled: false` reads nothing). `unavailable` and
|
|
2218
|
+
`error` are never both set: `unavailable` is a claw from before the door
|
|
2219
|
+
or no network (show the quiet "not connected yet"), `error` is the
|
|
2220
|
+
platform's words and `code` its code when it named one.
|
|
2221
|
+
|
|
2222
|
+
```jsx
|
|
2223
|
+
"use client";
|
|
2224
|
+
import { Badge, Button, Card, Empty, Image, Notice } from "../components/ui";
|
|
2225
|
+
import { useLibraryItems } from "../lib/hooks";
|
|
2226
|
+
|
|
2227
|
+
export default function Decks() {
|
|
2228
|
+
const { items, total, previewOnly, hasMore, loadMore, loading, loadingMore, error, unavailable } =
|
|
2229
|
+
useLibraryItems("pitch-decks", { limit: 24 });
|
|
2230
|
+
if (unavailable) return <p className="text-sm text-muted-foreground">The Library is not connected yet.</p>;
|
|
2231
|
+
if (error) return <Notice tone="danger">{error}</Notice>;
|
|
2232
|
+
if (!loading && !items.length) return <Empty title="No decks yet" body="Nothing in this collection matches." />;
|
|
2233
|
+
return (
|
|
2234
|
+
<div className="grid gap-4">
|
|
2235
|
+
<div className="grid grid-cols-2 gap-4 lg:grid-cols-4">
|
|
2236
|
+
{items.map((item) => (
|
|
2237
|
+
<Card key={item.id} padding="sm" className="grid gap-2">
|
|
2238
|
+
<Image src={item.preview_url} alt="" ratio="photo" />
|
|
2239
|
+
<p className="text-sm font-medium">{item.title}</p>
|
|
2240
|
+
<Badge>{item.format}</Badge>
|
|
2241
|
+
</Card>
|
|
2242
|
+
))}
|
|
2243
|
+
</div>
|
|
2244
|
+
{previewOnly && total > items.length ? <Notice>{total - items.length} more come with a paid plan.</Notice> : null}
|
|
2245
|
+
{hasMore ? <Button loading={loadingMore} onClick={loadMore}>Show more</Button> : null}
|
|
2246
|
+
</div>
|
|
2247
|
+
);
|
|
2248
|
+
}
|
|
2249
|
+
```
|
|
2250
|
+
|
|
2251
|
+
#### A paid collection on a Free plan
|
|
2252
|
+
|
|
2253
|
+
A collection with `access: "paid"` is for paid workspaces. On a Free plan
|
|
2254
|
+
it is a **preview**, not a refusal: the card says `usable: false` and
|
|
2255
|
+
`preview_only: true`, and `items` or `rows` answer only the first
|
|
2256
|
+
`preview_count` of them (a search or a filter runs over those), with
|
|
2257
|
+
`preview_only: true` and the **true** `total`. `total` minus what you
|
|
2258
|
+
hold is what "comes with a paid plan". The items past the preview are
|
|
2259
|
+
not in the list (the item's `locked` flag is for such an item, and a
|
|
2260
|
+
list of a preview carries only the preview's, so it reads `false`
|
|
2261
|
+
there); asking for one by its id, `library.item(id)`, is a 403 with
|
|
2262
|
+
`code: "needs_plan"` and the platform's words ("This collection comes
|
|
2263
|
+
with a paid plan."): show them, and stop. The Library window on the desk
|
|
2264
|
+
has the Upgrade button; an app has none to offer. On a paid plan the
|
|
2265
|
+
same calls answer the whole collection.
|
|
2266
|
+
|
|
2267
|
+
#### Files
|
|
2268
|
+
|
|
2269
|
+
- **`file_url` is a signed address, good for ten minutes.** Show or
|
|
2270
|
+
download from it when the member asks, and never keep it: not in a row,
|
|
2271
|
+
not in `localStorage`, not in a link that outlives the screen. A new
|
|
2272
|
+
`library.item(id)` (or `reload()`) signs another. Row cells of type
|
|
2273
|
+
`image` are signed the same way, so a table of rows with pictures that
|
|
2274
|
+
stays open should read its page again now and then.
|
|
2275
|
+
- **`preview_url` and `cover_url` are plain pictures** that stay: use
|
|
2276
|
+
them in `Image` for a card or a thumbnail. A row's image is not one;
|
|
2277
|
+
it is a signed address.
|
|
2278
|
+
- A PDF opens in a new tab (`<Button href={fileUrl} target="_blank"
|
|
2279
|
+
rel="noreferrer">`); an image is an `Image` of `fileUrl`; the other
|
|
2280
|
+
formats are a download link.
|
|
2281
|
+
- **An `html` item (a website template) is shown only in a sandboxed
|
|
2282
|
+
frame, never in the app's own page.** Read its file as text and give
|
|
2283
|
+
it to an `iframe` as `srcDoc` with `sandbox="allow-scripts"`, so its
|
|
2284
|
+
own scripts run. **Never add `allow-same-origin`**: with it the
|
|
2285
|
+
template's scripts run as your app, with the member's session and
|
|
2286
|
+
every `_claw` door. Never `dangerouslySetInnerHTML`, never
|
|
2287
|
+
`iframe src={fileUrl}`, and never your own origin: Makersclaw does not
|
|
2288
|
+
serve these files from it either.
|
|
2289
|
+
|
|
2290
|
+
```jsx
|
|
2291
|
+
"use client";
|
|
2292
|
+
import { useEffect, useState } from "react";
|
|
2293
|
+
import { Notice, Spinner } from "../components/ui";
|
|
2294
|
+
import { useLibraryItem } from "../lib/hooks";
|
|
2295
|
+
|
|
2296
|
+
export default function TemplatePreview({ id }) {
|
|
2297
|
+
const { item, fileUrl, code, error, unavailable } = useLibraryItem(id);
|
|
2298
|
+
const [html, setHtml] = useState(null);
|
|
2299
|
+
const [failed, setFailed] = useState(false);
|
|
2300
|
+
useEffect(() => {
|
|
2301
|
+
if (!fileUrl) return undefined;
|
|
2302
|
+
const controller = new AbortController();
|
|
2303
|
+
fetch(fileUrl, { signal: controller.signal })
|
|
2304
|
+
.then((res) => (res.ok ? res.text() : Promise.reject(new Error("no file"))))
|
|
2305
|
+
.then(setHtml)
|
|
2306
|
+
.catch(() => {
|
|
2307
|
+
if (!controller.signal.aborted) setFailed(true);
|
|
2308
|
+
});
|
|
2309
|
+
return () => controller.abort();
|
|
2310
|
+
}, [fileUrl]);
|
|
2311
|
+
if (unavailable) return <p className="text-sm text-muted-foreground">The Library is not connected yet.</p>;
|
|
2312
|
+
if (error) return <Notice tone={code === "needs_plan" ? "info" : "danger"}>{error}</Notice>;
|
|
2313
|
+
if (failed) return <Notice tone="danger">This template did not open. Try again.</Notice>;
|
|
2314
|
+
if (html === null) return <Spinner />;
|
|
2315
|
+
return <iframe title={item.title} srcDoc={html} sandbox="allow-scripts" className="h-96 w-full rounded-md border border-border" />;
|
|
2316
|
+
}
|
|
2317
|
+
```
|
|
2318
|
+
|
|
2319
|
+
#### What to keep in mind
|
|
2320
|
+
|
|
2321
|
+
- **Browser only, in both modes.** The read rides the member's session:
|
|
2322
|
+
`library` and the hooks run in a `"use client"` file. A server
|
|
2323
|
+
component or a server action has no member and cannot call it.
|
|
2324
|
+
- **Never by its path.** A string naming `_claw/library` fails `npm run
|
|
2325
|
+
check` (§6, `library`): the functions handle a claw without the door, a
|
|
2326
|
+
dropped network and an abort, which a bare `fetch` does not.
|
|
2327
|
+
- **A claw from before the door** answers a 404 that names no refusal:
|
|
2328
|
+
`unavailable: true`, from every call and every hook. A collection or
|
|
2329
|
+
item that is not there (or is a draft the member cannot see) is a 404
|
|
2330
|
+
that names its code, so `unavailable` stays false and `error` says so.
|
|
2331
|
+
Show a missing door as "not connected yet", and a missing collection as
|
|
2332
|
+
"not found".
|
|
2333
|
+
- **Show where it came from.** An item's `licence` and `source_url`, a
|
|
2334
|
+
row's `source_url` and a collection's `source` are in the answer for a
|
|
2335
|
+
reason: print them where the content is shown.
|
|
2336
|
+
- **A rows collection can list people** (`personal_data: true`). It is
|
|
2337
|
+
shown to the member who has the app open. Do not copy it anywhere
|
|
2338
|
+
yourself: no `insertRow` of its rows, no export, no sending it to your
|
|
2339
|
+
own server. Using it in the company's data is a copy through the
|
|
2340
|
+
agent.
|
|
2341
|
+
- **The platform notes that the app showed a collection**, once a day
|
|
2342
|
+
per app and collection.
|
|
2343
|
+
- **Read it when the screen needs it.** Not on every keystroke, and not
|
|
2344
|
+
in a loop over every collection: a shelf is one `collections` call.
|
|
2345
|
+
|
|
1507
2346
|
### Activity — the contract
|
|
1508
2347
|
|
|
1509
2348
|
`claw_app.activity` is how the founder sees the work. It is the
|
|
@@ -1577,11 +2416,15 @@ ok controls every control is a kit part
|
|
|
1577
2416
|
ok examples examples show only outside a workspace (useFixture); a workspace app opens empty
|
|
1578
2417
|
ok manifest 2 surfaces: drafts, sent
|
|
1579
2418
|
ok icons every icon name exists
|
|
1580
|
-
ok kit
|
|
2419
|
+
ok kit 74 fixed files match the template
|
|
1581
2420
|
ok depictions 1 picture of elsewhere, framed on 1 surface: x-post
|
|
1582
2421
|
ok agent no SQL write in app code; the agent's brief names every action with its call (1), every table written (2) and every server action that writes (0)
|
|
1583
2422
|
ok tables its own schema (makersclaw.json): every table named bare, and each bare name one of the 2 its schema makes (../install/schema.sql); claw. and claw_app. are the platform's
|
|
1584
2423
|
ok brain no brain block in makersclaw.json; no lib/brain.js call names a kind
|
|
2424
|
+
ok viewport nothing in the app sizes itself by the window
|
|
2425
|
+
ok llm no screen names _claw/llm: a model is called through llm() or useLlm() (README §5)
|
|
2426
|
+
ok library no screen names _claw/library: the Library is read through library() or its hooks (README §5)
|
|
2427
|
+
ok actions no actions declared in makersclaw.json: the app's agent writes as references/actions.md says
|
|
1585
2428
|
ok smoke built in 41s · / → 307 · /s/drafts 200 · /s/sent 200 (0.6s)
|
|
1586
2429
|
all clear
|
|
1587
2430
|
```
|
|
@@ -1593,9 +2436,25 @@ ok browser files and a door: 2 client surfaces; no server code, route, pro
|
|
|
1593
2436
|
ok smoke built in 38s (copy at ~/.claw-check/app) · export 2.1 MB in 64 files · / → 200 to /s/drafts · /s/drafts 200 · /s/sent 200 (0.1s)
|
|
1594
2437
|
```
|
|
1595
2438
|
|
|
2439
|
+
`actions` holds `makersclaw.json` `actions` to the platform's rules
|
|
2440
|
+
(§5, Declared actions) and, once any is declared, refuses every write a
|
|
2441
|
+
screen makes that is not one.
|
|
2442
|
+
|
|
2443
|
+
`automations` runs `makersclaw.json` `automations` (§5, Shipped
|
|
2444
|
+
automations) through the platform's own validator, one line per problem
|
|
2445
|
+
(its place, then what is wrong; at most 8, then "and N more"). It reads
|
|
2446
|
+
two copies of claw's format kept in `check/` and is skipped, not failed,
|
|
2447
|
+
on a node older than 22.18 (it cannot read them).
|
|
2448
|
+
|
|
1596
2449
|
`kit` refuses a fixed file that differs from the template (they are
|
|
1597
2450
|
hashed in `check/pins.json`) and any file of yours under `components/ui`
|
|
1598
|
-
or `components/parts`. `
|
|
2451
|
+
or `components/parts`. `viewport` refuses anything in your code that
|
|
2452
|
+
sizes itself by the window: `min-h-dvh`, `min-h-screen`, `h-screen`,
|
|
2453
|
+
`h-dvh` (and their `svh`, `lvh` and `max-h-` kin), and any `vh` unit —
|
|
2454
|
+
`h-[80vh]`, `max-h-[60vh]`, `h-[calc(100vh-4rem)]`, `style={{ height:
|
|
2455
|
+
"100vh" }}`. The window has one owner, the kit's `AppLayout` (§3b,
|
|
2456
|
+
Layouts); a box inside it is bounded by its parent (`h-full`, `flex-1
|
|
2457
|
+
min-h-0`) or by a size (`max-h-96`). `depictions` reads both directions: nothing of
|
|
1599
2458
|
the workspace inside a depiction (a token, `font-sans`, `dark:`, a rem
|
|
1600
2459
|
utility), nothing of the depiction outside its frame (a named import, a
|
|
1601
2460
|
picture in the shell or a `Card`, a dead file nobody renders).
|
|
@@ -1692,6 +2551,24 @@ kind has `kind`, `label`, `plural` and `fields`. A read's kind
|
|
|
1692
2551
|
hands the app only what `reads` and `writes` name. A kind held in a
|
|
1693
2552
|
variable is not read here; the door refuses it on the member's screen.
|
|
1694
2553
|
|
|
2554
|
+
`llm` reads every app, in both modes: no string in the code names
|
|
2555
|
+
`_claw/llm`, and none handed to `claw()`, `clawUrl()` or `clawStream()`
|
|
2556
|
+
starts `llm`. A model is called with `llm()` or `useLlm()` (§5, Calling a
|
|
2557
|
+
model), which handle a claw without the door, a dropped network and an
|
|
2558
|
+
abort; a `fetch` of the path handles none of them. The path is read as
|
|
2559
|
+
a browser reads it (`claw("//llm")`, `claw("./llm")` and `"/_claw/./llm"`
|
|
2560
|
+
are the door too). A path built from pieces or held in a variable is not
|
|
2561
|
+
read here, as for rule 8. A longer name (`_claw/llm-notes`) is another
|
|
2562
|
+
route, and a comment or text in JSX is not a string of code.
|
|
2563
|
+
|
|
2564
|
+
`library` is the same rule for the Library's door: no string in the code
|
|
2565
|
+
names `_claw/library`, and none handed to `claw()`, `clawUrl()` or
|
|
2566
|
+
`clawStream()` starts `library`. The Library is read with `library` from
|
|
2567
|
+
`lib/platform.js` or the `useLibrary…` hooks (§5, Showing the Library),
|
|
2568
|
+
which handle a claw without the door, a dropped network, an id that
|
|
2569
|
+
names nothing and an abort. It reads every app, in both modes, and the
|
|
2570
|
+
path as a browser reads it (`claw("./library/items")` is the door too).
|
|
2571
|
+
|
|
1695
2572
|
`browser` reads a browser app only (`"mode": "browser"`): its pages are
|
|
1696
2573
|
files built once, and everything they read or write goes through the
|
|
1697
2574
|
platform's data door. So it refuses, file and line, a `"use server"`
|
|
@@ -1713,6 +2590,12 @@ page. That proves every page prerenders, not that a surface renders
|
|
|
1713
2590
|
with data: the rows come in the browser. Then, by eye:
|
|
1714
2591
|
|
|
1715
2592
|
- Renders at 360px, 768px and 1280px without horizontal scroll.
|
|
2593
|
+
- The page itself never scrolls: the shell is the window's height
|
|
2594
|
+
(`AppLayout`), and each pane scrolls inside it. With the longest
|
|
2595
|
+
content a surface can hold (a 30-page article, a 40-message thread,
|
|
2596
|
+
a tall inspector), the last line and every button after it are
|
|
2597
|
+
reachable, at 375px and at 1280px; the header and the footer stay in
|
|
2598
|
+
view.
|
|
1716
2599
|
- In a workspace, an empty table shows `LiveEmpty`: the work in flight,
|
|
1717
2600
|
else what would be here and the manual way to add it — never an
|
|
1718
2601
|
example (§3a). A saved row replaces it.
|