makersclaw 0.8.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -3
- package/package.json +1 -1
- package/r/activity-log.json +52 -0
- package/r/agent-changes.json +51 -0
- package/r/agent-chat.json +53 -0
- package/r/agent-column.json +85 -0
- package/r/agent-dock.json +57 -0
- package/r/agent-kit.json +49 -0
- package/r/agent-marks.json +49 -0
- package/r/agent-pane.json +93 -0
- package/r/ai-action.json +56 -0
- package/r/annotate.json +3 -3
- package/r/approval-request.json +2 -2
- package/r/approval.json +54 -0
- package/r/ask-bar.json +50 -0
- package/r/audio-player.json +2 -2
- package/r/auth-form.json +2 -2
- package/r/automation-editor.json +66 -0
- package/r/automation-list.json +57 -0
- package/r/automation-model.json +118 -0
- package/r/automations-screen.json +71 -0
- package/r/board.json +2 -2
- package/r/calendar-view.json +3 -3
- package/r/cart.json +2 -2
- package/r/chart.json +2 -2
- package/r/chat-pane.json +84 -0
- package/r/chat.json +2 -2
- package/r/code-editor.json +2 -2
- package/r/comment-box.json +3 -3
- package/r/connect-card.json +51 -0
- package/r/contact-section.json +2 -2
- package/r/core.json +13 -7
- package/r/cost.json +51 -0
- package/r/csv-import.json +3 -3
- package/r/data-picker.json +60 -0
- package/r/data-table.json +2 -2
- package/r/diagram.json +2 -2
- package/r/doc-editor.json +2 -2
- package/r/doc-page.json +3 -3
- package/r/email-render.json +2 -2
- package/r/faq.json +2 -2
- package/r/file-upload.json +2 -2
- package/r/first-run.json +63 -0
- package/r/fix-note.json +57 -0
- package/r/flow-canvas.json +2 -2
- package/r/flow-editor.json +79 -0
- package/r/footer.json +2 -2
- package/r/gantt.json +3 -3
- package/r/job-panel.json +2 -2
- package/r/json-editor.json +3 -3
- package/r/library-shelf.json +56 -0
- package/r/map.json +2 -2
- package/r/margin-notes.json +67 -0
- package/r/menubar.json +2 -2
- package/r/mind-map.json +3 -3
- package/r/navigation-menu.json +2 -2
- package/r/needs-you.json +55 -0
- package/r/order-detail.json +2 -2
- package/r/org-chart.json +3 -3
- package/r/pace-bar.json +69 -0
- package/r/page-builder.json +2 -2
- package/r/payment-methods.json +2 -2
- package/r/pdf-document.json +2 -2
- package/r/pdf-viewer.json +2 -2
- package/r/product-detail.json +2 -2
- package/r/product-grid.json +3 -3
- package/r/record-form.json +2 -2
- package/r/record-grid.json +52 -0
- package/r/record-notes.json +4 -4
- package/r/registry.json +4086 -1512
- package/r/run-history.json +52 -0
- package/r/run-view.json +80 -0
- package/r/schedule-card.json +54 -0
- package/r/schedule-engine.json +79 -0
- package/r/schedule-gantt.json +83 -0
- package/r/schedule-ruler.json +64 -0
- package/r/search-dialog.json +2 -2
- package/r/settings-list.json +2 -2
- package/r/setup-checklist.json +2 -2
- package/r/share-dialog.json +2 -2
- package/r/sidebar.json +2 -2
- package/r/signature-pad.json +2 -2
- package/r/sketch-canvas.json +2 -2
- package/r/source-receipt.json +52 -0
- package/r/spreadsheet.json +3 -3
- package/r/stat-cards.json +3 -3
- package/r/step-picker.json +53 -0
- package/r/step-settings.json +72 -0
- package/r/suggestion.json +58 -0
- package/r/sum-bar.json +64 -0
- package/r/task-list.json +3 -3
- package/r/task-model.json +2 -2
- package/r/testimonials.json +2 -2
- package/r/tier-map.json +67 -0
- package/r/time-grid.json +73 -0
- package/r/time-strip.json +64 -0
- package/r/top-bar.json +66 -0
- package/r/transaction-list.json +2 -2
- package/r/trigger-picker.json +61 -0
- package/r/whiteboard.json +3 -3
- package/r/workflow-view.json +55 -0
- package/registry/blocks/activity-log/README.md +112 -0
- package/registry/blocks/activity-log/activity-log.jsx +351 -0
- package/registry/blocks/activity-log/block.json +33 -0
- package/registry/blocks/activity-log/demo.jsx +48 -0
- package/registry/blocks/activity-log/index.js +4 -0
- package/registry/blocks/agent-changes/README.md +94 -0
- package/registry/blocks/agent-changes/agent-changes.jsx +118 -0
- package/registry/blocks/agent-changes/block.json +28 -0
- package/registry/blocks/agent-changes/demo.jsx +39 -0
- package/registry/blocks/agent-changes/index.js +3 -0
- package/registry/blocks/agent-chat/README.md +145 -0
- package/registry/blocks/agent-chat/agent-chat.jsx +415 -0
- package/registry/blocks/agent-chat/block.json +30 -0
- package/registry/blocks/agent-chat/demo.jsx +165 -0
- package/registry/blocks/agent-chat/index.js +3 -0
- package/registry/blocks/agent-column/README.md +197 -0
- package/registry/blocks/agent-column/agent-column.jsx +492 -0
- package/registry/blocks/agent-column/block.json +67 -0
- package/registry/blocks/agent-column/column-picker.jsx +163 -0
- package/registry/blocks/agent-column/demo.jsx +50 -0
- package/registry/blocks/agent-column/index.js +7 -0
- package/registry/blocks/agent-column/model.js +164 -0
- package/registry/blocks/agent-column/receipt.jsx +483 -0
- package/registry/blocks/agent-column/spreadsheet-type.jsx +24 -0
- package/registry/blocks/agent-dock/README.md +210 -0
- package/registry/blocks/agent-dock/agent-dock.jsx +356 -0
- package/registry/blocks/agent-dock/block.json +47 -0
- package/registry/blocks/agent-dock/demo.jsx +153 -0
- package/registry/blocks/agent-dock/dock-model.js +118 -0
- package/registry/blocks/agent-dock/index.js +5 -0
- package/registry/blocks/agent-kit/README.md +73 -0
- package/registry/blocks/agent-kit/agent-kit.jsx +86 -0
- package/registry/blocks/agent-kit/block.json +10 -0
- package/registry/blocks/agent-kit/demo.jsx +35 -0
- package/registry/blocks/agent-kit/index.js +3 -0
- package/registry/blocks/agent-marks/README.md +80 -0
- package/registry/blocks/agent-marks/agent-marks.jsx +133 -0
- package/registry/blocks/agent-marks/block.json +27 -0
- package/registry/blocks/agent-marks/demo.jsx +49 -0
- package/registry/blocks/agent-marks/index.js +3 -0
- package/registry/blocks/agent-pane/README.md +234 -0
- package/registry/blocks/agent-pane/agent-pane.jsx +246 -0
- package/registry/blocks/agent-pane/block.json +41 -0
- package/registry/blocks/agent-pane/composer.jsx +6 -0
- package/registry/blocks/agent-pane/demo.jsx +233 -0
- package/registry/blocks/agent-pane/dock-context.js +10 -0
- package/registry/blocks/agent-pane/index.js +4 -0
- package/registry/blocks/agent-pane/prose.jsx +84 -0
- package/registry/blocks/agent-pane/questions.jsx +134 -0
- package/registry/blocks/agent-pane/transcript.jsx +268 -0
- package/registry/blocks/agent-pane/work.jsx +368 -0
- package/registry/blocks/ai-action/README.md +94 -0
- package/registry/blocks/ai-action/ai-action.jsx +188 -0
- package/registry/blocks/ai-action/block.json +34 -0
- package/registry/blocks/ai-action/demo.jsx +93 -0
- package/registry/blocks/ai-action/index.js +3 -0
- package/registry/blocks/approval/README.md +145 -0
- package/registry/blocks/approval/approval.jsx +163 -0
- package/registry/blocks/approval/block.json +31 -0
- package/registry/blocks/approval/demo.jsx +121 -0
- package/registry/blocks/approval/index.js +3 -0
- package/registry/blocks/ask-bar/README.md +105 -0
- package/registry/blocks/ask-bar/ask-bar.jsx +166 -0
- package/registry/blocks/ask-bar/block.json +26 -0
- package/registry/blocks/ask-bar/demo.jsx +78 -0
- package/registry/blocks/ask-bar/index.js +3 -0
- package/registry/blocks/automation-editor/README.md +291 -0
- package/registry/blocks/automation-editor/automation-editor.jsx +656 -0
- package/registry/blocks/automation-editor/block.json +39 -0
- package/registry/blocks/automation-editor/demo.jsx +284 -0
- package/registry/blocks/automation-editor/index.js +4 -0
- package/registry/blocks/automation-editor/run-inputs.jsx +82 -0
- package/registry/blocks/automation-list/README.md +129 -0
- package/registry/blocks/automation-list/automation-list.jsx +476 -0
- package/registry/blocks/automation-list/block.json +35 -0
- package/registry/blocks/automation-list/demo.jsx +67 -0
- package/registry/blocks/automation-list/index.js +3 -0
- package/registry/blocks/automation-model/README.md +109 -0
- package/registry/blocks/automation-model/block.json +135 -0
- package/registry/blocks/automation-model/catalog.js +256 -0
- package/registry/blocks/automation-model/check.js +161 -0
- package/registry/blocks/automation-model/demo.jsx +42 -0
- package/registry/blocks/automation-model/edit.js +262 -0
- package/registry/blocks/automation-model/fixes.js +287 -0
- package/registry/blocks/automation-model/glyph.jsx +13 -0
- package/registry/blocks/automation-model/graph.js +410 -0
- package/registry/blocks/automation-model/index.js +33 -0
- package/registry/blocks/automation-model/overlay.js +175 -0
- package/registry/blocks/automation-model/parts.jsx +94 -0
- package/registry/blocks/automation-model/schedule.js +53 -0
- package/registry/blocks/automation-model/templates.js +128 -0
- package/registry/blocks/automation-model/values.js +29 -0
- package/registry/blocks/automation-model/words.js +439 -0
- package/registry/blocks/automations-screen/README.md +207 -0
- package/registry/blocks/automations-screen/automations-screen.jsx +452 -0
- package/registry/blocks/automations-screen/block.json +47 -0
- package/registry/blocks/automations-screen/demo.jsx +364 -0
- package/registry/blocks/automations-screen/index.js +4 -0
- package/registry/blocks/automations-screen/screen-model.js +129 -0
- package/registry/blocks/chat-pane/README.md +143 -0
- package/registry/blocks/chat-pane/block.json +48 -0
- package/registry/blocks/chat-pane/chat-attach.js +166 -0
- package/registry/blocks/chat-pane/chat-composer.jsx +195 -0
- package/registry/blocks/chat-pane/chat-context.js +17 -0
- package/registry/blocks/chat-pane/chat-model.js +43 -0
- package/registry/blocks/chat-pane/chat-pane.jsx +398 -0
- package/registry/blocks/chat-pane/chats-list.jsx +52 -0
- package/registry/blocks/chat-pane/demo.jsx +103 -0
- package/registry/blocks/chat-pane/index.js +9 -0
- package/registry/blocks/connect-card/README.md +99 -0
- package/registry/blocks/connect-card/block.json +10 -0
- package/registry/blocks/connect-card/connect-card.jsx +103 -0
- package/registry/blocks/connect-card/demo.jsx +49 -0
- package/registry/blocks/connect-card/index.js +3 -0
- package/registry/blocks/cost/README.md +115 -0
- package/registry/blocks/cost/block.json +10 -0
- package/registry/blocks/cost/cost.jsx +176 -0
- package/registry/blocks/cost/demo.jsx +66 -0
- package/registry/blocks/cost/index.js +3 -0
- package/registry/blocks/data-picker/README.md +81 -0
- package/registry/blocks/data-picker/block.json +38 -0
- package/registry/blocks/data-picker/data-picker.jsx +210 -0
- package/registry/blocks/data-picker/demo.jsx +34 -0
- package/registry/blocks/data-picker/fields.js +89 -0
- package/registry/blocks/data-picker/index.js +4 -0
- package/registry/blocks/data-table/demo.jsx +1 -1
- package/registry/blocks/first-run/README.md +118 -0
- package/registry/blocks/first-run/block.json +11 -0
- package/registry/blocks/first-run/demo.jsx +88 -0
- package/registry/blocks/first-run/first-run-model.js +51 -0
- package/registry/blocks/first-run/first-run.jsx +156 -0
- package/registry/blocks/first-run/index.js +5 -0
- package/registry/blocks/first-run/probe-rules.js +26 -0
- package/registry/blocks/fix-note/README.md +79 -0
- package/registry/blocks/fix-note/block.json +35 -0
- package/registry/blocks/fix-note/demo.jsx +30 -0
- package/registry/blocks/fix-note/fix-note.jsx +235 -0
- package/registry/blocks/fix-note/index.js +3 -0
- package/registry/blocks/flow-editor/README.md +108 -0
- package/registry/blocks/flow-editor/block.json +44 -0
- package/registry/blocks/flow-editor/demo.jsx +115 -0
- package/registry/blocks/flow-editor/flow-canvas.jsx +568 -0
- package/registry/blocks/flow-editor/flow-editor.jsx +154 -0
- package/registry/blocks/flow-editor/index.js +6 -0
- package/registry/blocks/flow-editor/minimap.jsx +78 -0
- package/registry/blocks/flow-editor/outline.jsx +134 -0
- package/registry/blocks/library-shelf/README.md +117 -0
- package/registry/blocks/library-shelf/block.json +28 -0
- package/registry/blocks/library-shelf/demo.jsx +68 -0
- package/registry/blocks/library-shelf/index.js +3 -0
- package/registry/blocks/library-shelf/library-shelf.jsx +211 -0
- package/registry/blocks/library-shelf/probe-rules.js +15 -0
- package/registry/blocks/margin-notes/README.md +222 -0
- package/registry/blocks/margin-notes/block.json +10 -0
- package/registry/blocks/margin-notes/demo.jsx +144 -0
- package/registry/blocks/margin-notes/index.js +20 -0
- package/registry/blocks/margin-notes/margin-model.js +224 -0
- package/registry/blocks/margin-notes/margin-notes.jsx +465 -0
- package/registry/blocks/needs-you/README.md +177 -0
- package/registry/blocks/needs-you/block.json +34 -0
- package/registry/blocks/needs-you/demo.jsx +143 -0
- package/registry/blocks/needs-you/index.js +3 -0
- package/registry/blocks/needs-you/needs-you.jsx +441 -0
- package/registry/blocks/pace-bar/README.md +230 -0
- package/registry/blocks/pace-bar/block.json +10 -0
- package/registry/blocks/pace-bar/demo.jsx +255 -0
- package/registry/blocks/pace-bar/index.js +22 -0
- package/registry/blocks/pace-bar/pace-bar.jsx +461 -0
- package/registry/blocks/pace-bar/pace-model.js +288 -0
- package/registry/blocks/record-grid/README.md +100 -0
- package/registry/blocks/record-grid/block.json +28 -0
- package/registry/blocks/record-grid/demo.jsx +43 -0
- package/registry/blocks/record-grid/index.js +3 -0
- package/registry/blocks/record-grid/record-grid.jsx +251 -0
- package/registry/blocks/run-history/README.md +138 -0
- package/registry/blocks/run-history/block.json +10 -0
- package/registry/blocks/run-history/demo.jsx +74 -0
- package/registry/blocks/run-history/index.js +3 -0
- package/registry/blocks/run-history/run-history.jsx +218 -0
- package/registry/blocks/run-view/README.md +174 -0
- package/registry/blocks/run-view/block.json +50 -0
- package/registry/blocks/run-view/demo.jsx +286 -0
- package/registry/blocks/run-view/index.js +5 -0
- package/registry/blocks/run-view/run-controls.jsx +266 -0
- package/registry/blocks/run-view/run-view.jsx +236 -0
- package/registry/blocks/run-view/runs-list.jsx +99 -0
- package/registry/blocks/schedule-card/README.md +121 -0
- package/registry/blocks/schedule-card/block.json +10 -0
- package/registry/blocks/schedule-card/demo.jsx +82 -0
- package/registry/blocks/schedule-card/index.js +3 -0
- package/registry/blocks/schedule-card/schedule-card.jsx +136 -0
- package/registry/blocks/schedule-engine/README.md +137 -0
- package/registry/blocks/schedule-engine/block.json +10 -0
- package/registry/blocks/schedule-engine/demo.jsx +82 -0
- package/registry/blocks/schedule-engine/engine.js +769 -0
- package/registry/blocks/schedule-engine/index.js +9 -0
- package/registry/blocks/schedule-engine/ops.js +71 -0
- package/registry/blocks/schedule-engine/place.js +294 -0
- package/registry/blocks/schedule-engine/tree.js +79 -0
- package/registry/blocks/schedule-engine/waits.js +80 -0
- package/registry/blocks/schedule-gantt/README.md +159 -0
- package/registry/blocks/schedule-gantt/block.json +10 -0
- package/registry/blocks/schedule-gantt/demo.jsx +223 -0
- package/registry/blocks/schedule-gantt/gantt-model.js +219 -0
- package/registry/blocks/schedule-gantt/geometry.js +176 -0
- package/registry/blocks/schedule-gantt/index.js +26 -0
- package/registry/blocks/schedule-gantt/schedule-gantt.jsx +882 -0
- package/registry/blocks/schedule-gantt/sheet.jsx +372 -0
- package/registry/blocks/schedule-gantt/track.jsx +190 -0
- package/registry/blocks/schedule-ruler/README.md +148 -0
- package/registry/blocks/schedule-ruler/block.json +10 -0
- package/registry/blocks/schedule-ruler/demo.jsx +181 -0
- package/registry/blocks/schedule-ruler/index.js +21 -0
- package/registry/blocks/schedule-ruler/ruler-model.js +239 -0
- package/registry/blocks/schedule-ruler/schedule-ruler.jsx +548 -0
- package/registry/blocks/source-receipt/README.md +97 -0
- package/registry/blocks/source-receipt/block.json +30 -0
- package/registry/blocks/source-receipt/demo.jsx +71 -0
- package/registry/blocks/source-receipt/index.js +3 -0
- package/registry/blocks/source-receipt/source-receipt.jsx +107 -0
- package/registry/blocks/spreadsheet/spreadsheet.jsx +15 -1
- package/registry/blocks/step-picker/README.md +78 -0
- package/registry/blocks/step-picker/block.json +31 -0
- package/registry/blocks/step-picker/demo.jsx +23 -0
- package/registry/blocks/step-picker/index.js +3 -0
- package/registry/blocks/step-picker/step-picker.jsx +144 -0
- package/registry/blocks/step-settings/README.md +100 -0
- package/registry/blocks/step-settings/block.json +44 -0
- package/registry/blocks/step-settings/demo.jsx +93 -0
- package/registry/blocks/step-settings/fields.jsx +58 -0
- package/registry/blocks/step-settings/flow-settings.jsx +115 -0
- package/registry/blocks/step-settings/index.js +5 -0
- package/registry/blocks/step-settings/step-settings.jsx +290 -0
- package/registry/blocks/suggestion/README.md +171 -0
- package/registry/blocks/suggestion/block.json +40 -0
- package/registry/blocks/suggestion/demo.jsx +91 -0
- package/registry/blocks/suggestion/index.js +3 -0
- package/registry/blocks/suggestion/suggestion.jsx +436 -0
- package/registry/blocks/sum-bar/README.md +122 -0
- package/registry/blocks/sum-bar/block.json +10 -0
- package/registry/blocks/sum-bar/demo.jsx +110 -0
- package/registry/blocks/sum-bar/index.js +5 -0
- package/registry/blocks/sum-bar/sum-bar.jsx +209 -0
- package/registry/blocks/sum-bar/sum-model.js +126 -0
- package/registry/blocks/tier-map/README.md +217 -0
- package/registry/blocks/tier-map/block.json +10 -0
- package/registry/blocks/tier-map/demo.jsx +222 -0
- package/registry/blocks/tier-map/index.js +24 -0
- package/registry/blocks/tier-map/tier-map.jsx +574 -0
- package/registry/blocks/tier-map/tier-model.js +318 -0
- package/registry/blocks/time-grid/README.md +305 -0
- package/registry/blocks/time-grid/block.json +10 -0
- package/registry/blocks/time-grid/demo.jsx +279 -0
- package/registry/blocks/time-grid/grid-model.js +322 -0
- package/registry/blocks/time-grid/index.js +25 -0
- package/registry/blocks/time-grid/time-grid.jsx +1325 -0
- package/registry/blocks/time-strip/README.md +141 -0
- package/registry/blocks/time-strip/block.json +10 -0
- package/registry/blocks/time-strip/demo.jsx +129 -0
- package/registry/blocks/time-strip/index.js +19 -0
- package/registry/blocks/time-strip/strip-model.js +178 -0
- package/registry/blocks/time-strip/time-strip.jsx +260 -0
- package/registry/blocks/top-bar/README.md +149 -0
- package/registry/blocks/top-bar/block.json +35 -0
- package/registry/blocks/top-bar/demo.jsx +78 -0
- package/registry/blocks/top-bar/index.js +5 -0
- package/registry/blocks/top-bar/probe-rules.js +74 -0
- package/registry/blocks/top-bar/top-bar-model.js +152 -0
- package/registry/blocks/top-bar/top-bar.jsx +244 -0
- package/registry/blocks/trigger-picker/README.md +98 -0
- package/registry/blocks/trigger-picker/block.json +38 -0
- package/registry/blocks/trigger-picker/demo.jsx +59 -0
- package/registry/blocks/trigger-picker/index.js +3 -0
- package/registry/blocks/trigger-picker/trigger-picker.jsx +374 -0
- package/registry/blocks/workflow-view/README.md +123 -0
- package/registry/blocks/workflow-view/block.json +10 -0
- package/registry/blocks/workflow-view/demo.jsx +90 -0
- package/registry/blocks/workflow-view/index.js +3 -0
- package/registry/blocks/workflow-view/workflow-view.jsx +203 -0
- package/src/install.mjs +77 -16
- package/src/registry.mjs +3 -0
- package/templates/next/browser/lib/data.js +6 -1
- package/templates/next/files/README.md +627 -24
- package/templates/next/files/check/automation-format.ts +2103 -0
- package/templates/next/files/check/automation-package.ts +142 -0
- package/templates/next/files/check/run.mjs +968 -9
- package/templates/next/files/components/ui/field.jsx +17 -4
- package/templates/next/files/components/ui/icon.jsx +13 -0
- package/templates/next/files/components/ui/index.js +2 -1
- package/templates/next/files/components/ui/layout.jsx +77 -2
- package/templates/next/files/components/ui/notice.jsx +70 -0
- package/templates/next/files/components/ui/page.jsx +124 -0
- package/templates/next/files/components/ui/stat.jsx +22 -7
- package/templates/next/files/components/ui/toast.jsx +6 -3
- package/templates/next/files/lib/automations.js +660 -0
- package/templates/next/files/lib/claw.js +241 -5
- package/templates/next/files/lib/door.js +92 -0
- package/templates/next/files/lib/hooks.js +1677 -8
- package/templates/next/files/lib/platform.js +529 -21
- package/templates/next/template.json +13 -3
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# AgentDock
|
|
2
|
+
|
|
3
|
+
Where an app's agent lives (0.8.1): a column on the right, beside the work,
|
|
4
|
+
like the desk's own agent sidebar.
|
|
5
|
+
|
|
6
|
+
The founder, testing an app: "why are we still using this weak ai sidebar
|
|
7
|
+
component? I thought we had created the original agent sidebar as component
|
|
8
|
+
in the template". We had: `agent-pane` is the desk's sidebar. What was
|
|
9
|
+
missing was its host. Every app invented one: a modal `Sheet` that dimmed
|
|
10
|
+
the work, an inspector tab, a card. So no two apps felt alike, and none felt
|
|
11
|
+
like the sidebar. This is the host, once.
|
|
12
|
+
|
|
13
|
+
- A **column beside the content**, under the top bar and right of the rail.
|
|
14
|
+
The work reflows beside it. Nothing is dimmed.
|
|
15
|
+
- **Resizable.** A quarter of the body until dragged, never under 280 px,
|
|
16
|
+
never over half. The divider is a separator a keyboard can move: arrows
|
|
17
|
+
nudge it, Home goes back to a quarter, End to the widest.
|
|
18
|
+
- **Remembered.** The open state and the width are kept in this browser, per
|
|
19
|
+
app (`storageKey`), in `localStorage` with a `try/catch`; with no storage
|
|
20
|
+
it still works.
|
|
21
|
+
- **A sheet where there is no room.** Where the body is under `columnAt`
|
|
22
|
+
(900 px; a measure of the frame's own width, never the window) the dock is
|
|
23
|
+
a sheet laid over the app from the right. Escape folds it. The chat is not
|
|
24
|
+
remounted on the way: a turn in flight survives a resize.
|
|
25
|
+
- **The pane keeps its head.** The `agent-pane` inside draws title, New chat,
|
|
26
|
+
Chats and the close; its empty state lists the past chats (or "No chats
|
|
27
|
+
yet"), then the app's suggestions, then the box: the desk's message box
|
|
28
|
+
(the `chat-pane` block's), with the words on top and + to attach and a
|
|
29
|
+
round send under them. The dock only tells the pane how to close, and
|
|
30
|
+
hands it a starting line when a "have the agent do it" button asked for one.
|
|
31
|
+
- **Stays mounted once opened**, so a draft and a running turn survive
|
|
32
|
+
folding the column.
|
|
33
|
+
|
|
34
|
+
```jsx
|
|
35
|
+
import { AgentDock, useAgentDock } from "../components/blocks/agent-dock"; // a Makersclaw app ("./blocks/agent-dock" from components/)
|
|
36
|
+
import { AgentDock, useAgentDock } from "@/components/makersclaw/blocks/agent-dock"; // a shadcn project
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
A client component. It needs the kit's `AppLayout` with its `dock` slot
|
|
40
|
+
(0.8.1), or any flex row it is the last child of.
|
|
41
|
+
|
|
42
|
+
## Adopting it in a shell (a few lines)
|
|
43
|
+
|
|
44
|
+
The shell is the app's one `AppLayout`. Give it the dock, and make every Ask
|
|
45
|
+
button open it: the top bar's own Ask does that when the shell wraps the
|
|
46
|
+
layout in `AskProvider` (blocks/top-bar).
|
|
47
|
+
|
|
48
|
+
```jsx
|
|
49
|
+
"use client";
|
|
50
|
+
import { AppLayout } from "../components/ui";
|
|
51
|
+
import { AgentDock, useAgentDock } from "../components/blocks/agent-dock";
|
|
52
|
+
import { AskProvider } from "../components/blocks/top-bar";
|
|
53
|
+
import { AskPanel } from "../components/ask-panel"; // the app's own pane over AgentPane
|
|
54
|
+
|
|
55
|
+
export default function Shell({ children, budget }) {
|
|
56
|
+
const dock = useAgentDock({ storageKey: "crm" }); // { open, setOpen, toggle }
|
|
57
|
+
return (
|
|
58
|
+
<AskProvider dock={dock} allowance={{ used: budget.used, cap: budget.cap }} findEvent="crm:find">
|
|
59
|
+
<AppLayout
|
|
60
|
+
navigation={<Rail />}
|
|
61
|
+
dock={
|
|
62
|
+
<AgentDock dock={dock} label="Ask the agent">
|
|
63
|
+
<AskPanel /> {/* useAgentPane({ row }) inside, AgentPane (not bare) */}
|
|
64
|
+
</AgentDock>
|
|
65
|
+
}
|
|
66
|
+
>
|
|
67
|
+
{children} {/* each screen draws its own <TopBar crumb=… action=…/> */}
|
|
68
|
+
</AppLayout>
|
|
69
|
+
</AskProvider>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Delete the shell's old `<Sheet>` for the agent, and drop `bare` from the pane
|
|
75
|
+
(the dock's pane has its own head).
|
|
76
|
+
|
|
77
|
+
**One dock per app, in the shell.** A record's own pane (a meeting, a
|
|
78
|
+
person) is this same dock with a chat scoped to the open record: make the
|
|
79
|
+
`AskPanel` read the record the page has open and pass it as
|
|
80
|
+
`useAgentPane({ row })`. The thread follows the record; the column stays
|
|
81
|
+
where it is. Do not add a second column.
|
|
82
|
+
|
|
83
|
+
Also set `"agent_pane": true` in the package's blueprint (see `agent-pane`)
|
|
84
|
+
so the desk draws no second sidebar for the window.
|
|
85
|
+
|
|
86
|
+
## Just open it
|
|
87
|
+
|
|
88
|
+
**To open the dock and send nothing, call `openAgent()` with no line.** One
|
|
89
|
+
way, from anywhere on the page:
|
|
90
|
+
|
|
91
|
+
```jsx
|
|
92
|
+
import { openAgent } from "../components/blocks/agent-dock";
|
|
93
|
+
|
|
94
|
+
<Button variant="link" onClick={() => openAgent()}>Talk to the agent</Button>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
- `openAgent()` and `openAgent({ ask: "" })` open the dock (column or sheet),
|
|
98
|
+
put the cursor in the box, send nothing and start no chat. It returns
|
|
99
|
+
`true` when a dock took it.
|
|
100
|
+
- It only ever **opens**. The top bar's Ask **toggles** (it also closes);
|
|
101
|
+
`useAsk()` is for reading whether the agent is open (`open`) and for that
|
|
102
|
+
toggle, not for opening it from a screen.
|
|
103
|
+
- From the shell, where `useAgentDock()` is held, `dock.setOpen(true)` is the
|
|
104
|
+
same thing, and `dock.openAgent()` too. A screen far from the shell uses the
|
|
105
|
+
exported `openAgent()`: do not write your own event, ref or context for it.
|
|
106
|
+
- With a line, it is the next section: the dock opens and the pane sends it.
|
|
107
|
+
|
|
108
|
+
## Have the agent do it
|
|
109
|
+
|
|
110
|
+
**Every button that says the agent will do the work calls `openAgent`.** It
|
|
111
|
+
never opens a form, a route or a sheet first. The founder, on a project
|
|
112
|
+
planner whose "Have the agent draft it" first asked for a name, a code and
|
|
113
|
+
two dates: "the agent should be able to create these".
|
|
114
|
+
|
|
115
|
+
```jsx
|
|
116
|
+
import { openAgent } from "../components/blocks/agent-dock";
|
|
117
|
+
|
|
118
|
+
<Button variant="primary" icon="sparkles" onClick={() => openAgent("Draft a project plan for me")}>
|
|
119
|
+
Have the agent draft it
|
|
120
|
+
</Button>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`openAgent("…")` opens the dock and hands the pane its first line. The pane
|
|
124
|
+
starts a new chat and sends the line as the member's first words; the agent
|
|
125
|
+
asks only for what it is missing (the name, the target date), in the chat
|
|
126
|
+
as a question the member answers there; then it creates the record through
|
|
127
|
+
the app's own actions. A form stays only for "type it yourself".
|
|
128
|
+
|
|
129
|
+
- `openAgent("Draft a project plan for me")`, or an object:
|
|
130
|
+
- `ask`: the starting line. Say what the member wants done, in their words.
|
|
131
|
+
Left out or empty, the dock only opens ("Just open it", above).
|
|
132
|
+
- `send`: `true` (the default) sends it. `false` puts it in the box for the
|
|
133
|
+
member to finish: `openAgent({ ask: "Draft a project plan for ", send: false })`.
|
|
134
|
+
- `fresh`: `true` (the default) is a new chat, so it is its own ask and not a
|
|
135
|
+
follow-up to the last one. `false` carries on in the chat that is open.
|
|
136
|
+
- `storageKey`: only that app's dock (default: any dock on the page, and an
|
|
137
|
+
app has one).
|
|
138
|
+
- It returns `true` when a dock took it and `false` when the page has none.
|
|
139
|
+
Do not use the `false` to open a form: an app that has an agent has the dock
|
|
140
|
+
in its shell (AppLayout's `dock` slot). `npm run check` (rule `agentfirst`)
|
|
141
|
+
refuses a button whose words say the agent will do the work ("Have the
|
|
142
|
+
agent draft it", "Let the agent plan it") when it submits a form, follows a
|
|
143
|
+
link or only changes a step; it must call `openAgent` or ask the agent
|
|
144
|
+
(`ask`, `chat.send`). A choice labelled that way inside a form fails too.
|
|
145
|
+
|
|
146
|
+
From the shell, where `useAgentDock()` is held, `dock.openAgent(ask)` does the
|
|
147
|
+
same without the page event. A screen far from the shell uses the exported
|
|
148
|
+
`openAgent`; a line the member is shown first (a chip, an ask bar) uses
|
|
149
|
+
`chat.send` as before.
|
|
150
|
+
|
|
151
|
+
The agent's side: its brief names the app's create actions in
|
|
152
|
+
`references/actions.md`, and the draft action takes its inputs as optional
|
|
153
|
+
(`name`, `start`, `target`), so a line like "Draft a project plan for me"
|
|
154
|
+
reaches it with nothing and it asks. Write the line so it carries what the
|
|
155
|
+
screen already knows ("Draft a project plan for the Pinewood launch").
|
|
156
|
+
|
|
157
|
+
## Props
|
|
158
|
+
|
|
159
|
+
- `dock`: what `useAgentDock()` returns (`open`, `setOpen`, `storageKey`,
|
|
160
|
+
`asked`, `start`, `startTaken`). Or pass the parts:
|
|
161
|
+
- `open`, `onOpenChange`: the dock is yours to open and close.
|
|
162
|
+
- `storageKey`: where the width is kept (default the dock's, else `"app"`).
|
|
163
|
+
- `label`: the column's name for a screen reader. Default `"Agent"`.
|
|
164
|
+
- `columnAt`: body width in px from which it is a column. Default `900`.
|
|
165
|
+
- `children`: the pane. Your own component over `AgentPane` (the app's yes
|
|
166
|
+
card, its titles). Without children the dock draws an `AgentPane` from:
|
|
167
|
+
- `chat`, `title`, `suggestions`, `empty`, `placeholder`, `yes`, `mark`,
|
|
168
|
+
`onOpen`, `recent`: the `AgentPane`'s own props.
|
|
169
|
+
- `className`: the column's layout. Never colour.
|
|
170
|
+
|
|
171
|
+
### useAgentDock({ storageKey, defaultOpen, listen })
|
|
172
|
+
|
|
173
|
+
Returns `{ open, setOpen(bool | fn), toggle(), openAgent(ask), storageKey,
|
|
174
|
+
asked, start, startTaken }`. `listen` (default true) is whether it hears
|
|
175
|
+
`openAgent()` calls from anywhere on the page; the shell's one hook is the
|
|
176
|
+
one that does. The
|
|
177
|
+
choice is read from storage after the first paint (so the server's and the
|
|
178
|
+
browser's first render agree) and written when the member changes it.
|
|
179
|
+
`defaultOpen` (false) holds until they choose. `asked` counts the member's
|
|
180
|
+
opens: when it rises the dock puts the cursor in the box. A restored open
|
|
181
|
+
dock never steals focus. Folding the dock gives focus back to what had it
|
|
182
|
+
when it opened (the Ask button), unless the member has gone elsewhere.
|
|
183
|
+
|
|
184
|
+
### The model (plain functions)
|
|
185
|
+
|
|
186
|
+
`MIN_W`, `COLUMN_AT`, `maxWidth(body)`, `clampWidth(w, body)`,
|
|
187
|
+
`storedWidth(raw)`, `columnWidth(width, body)`, `hasRoom(body, columnAt)`,
|
|
188
|
+
`dragged(startWidth, startX, x, body)`, `nudged(key, now, body)`,
|
|
189
|
+
`dockKeys(storageKey)`, `openRequest(input)`, `OPEN_EVENT`.
|
|
190
|
+
|
|
191
|
+
## Example
|
|
192
|
+
|
|
193
|
+
```jsx
|
|
194
|
+
// Without an app-owned pane: the dock draws the AgentPane itself.
|
|
195
|
+
const dock = useAgentDock({ storageKey: "notes" });
|
|
196
|
+
const chat = useAgentPane();
|
|
197
|
+
<AppLayout
|
|
198
|
+
dock={<AgentDock dock={dock} chat={chat} title="Ask about your notes" suggestions={["Summarise this week"]} />}
|
|
199
|
+
>
|
|
200
|
+
…
|
|
201
|
+
</AppLayout>;
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## States drawn in the demo
|
|
205
|
+
|
|
206
|
+
Closed (nothing drawn but the app); open as a column at 1100 px with past
|
|
207
|
+
chats, and with "No chats yet"; dragged and nudged; a sheet at 700 px with
|
|
208
|
+
Escape; a chat answering; "Have the agent draft it" on the page, which opens
|
|
209
|
+
the dock with its line already sent in a new chat (and "Start a line" beside
|
|
210
|
+
it, which puts the line in the box instead).
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { cx } from "../../ui";
|
|
5
|
+
import { AgentDockContext, AgentPane } from "../agent-pane";
|
|
6
|
+
import { COLUMN_AT, MIN_W, OPEN_EVENT, carriesLine, columnWidth, dockKeys, dragged, hasRoom, nudged, openRequest, storedWidth } from "./dock-model";
|
|
7
|
+
|
|
8
|
+
// AgentDock — where the app's agent lives: a column on the right, beside the
|
|
9
|
+
// work, like the desk's own agent sidebar (2026-10-12). The founder: "why are
|
|
10
|
+
// we still using this weak ai sidebar component?" The pane was the desk's
|
|
11
|
+
// sidebar, but every app invented its own host for it (a modal sheet that
|
|
12
|
+
// dimmed the work, an inspector tab), so none felt like the sidebar. This is
|
|
13
|
+
// the host, once.
|
|
14
|
+
//
|
|
15
|
+
// const dock = useAgentDock({ storageKey: "crm" }); // dock.open, dock.toggle
|
|
16
|
+
// <AppLayout dock={<AgentDock dock={dock}><AskPanel /></AgentDock>} …>…</AppLayout>
|
|
17
|
+
// <Button onClick={dock.toggle}>Ask</Button>
|
|
18
|
+
//
|
|
19
|
+
// It draws the head-less shell around the pane: the column (a quarter of the
|
|
20
|
+
// body until dragged, never under MIN_W, never over half), the divider that
|
|
21
|
+
// is dragged or nudged with the arrow keys (width kept in this browser, per
|
|
22
|
+
// app), the work reflowing beside it, and a sheet laid over the app where the
|
|
23
|
+
// body has no room for both. The pane inside draws its own head (title, New
|
|
24
|
+
// chat, Chats, close); the dock only tells it how to close, through
|
|
25
|
+
// AgentDockContext. The chat stays mounted once opened, so a turn in flight
|
|
26
|
+
// and a draft survive folding the column.
|
|
27
|
+
//
|
|
28
|
+
// A button that says the agent will do the work calls openAgent("Draft a
|
|
29
|
+
// project plan for me") (X9): the dock opens and the pane sends that line in
|
|
30
|
+
// a new chat, and the agent asks only for what it is missing. Never a form
|
|
31
|
+
// first.
|
|
32
|
+
|
|
33
|
+
const readKey = (key) => {
|
|
34
|
+
try {
|
|
35
|
+
return localStorage.getItem(key);
|
|
36
|
+
} catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const writeKey = (key, value) => {
|
|
41
|
+
try {
|
|
42
|
+
if (value === null) localStorage.removeItem(key);
|
|
43
|
+
else localStorage.setItem(key, value);
|
|
44
|
+
} catch {
|
|
45
|
+
/* private mode, blocked storage */
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Whether the dock is open, remembered in this browser per app.
|
|
51
|
+
*
|
|
52
|
+
* storageKey the app (a short word: "crm"); the open state and the
|
|
53
|
+
* width are kept under it
|
|
54
|
+
* defaultOpen until the member has chosen (default false)
|
|
55
|
+
* listen hear openAgent() calls from anywhere on the page (default
|
|
56
|
+
* true; the one dock in the shell is the one that listens)
|
|
57
|
+
*
|
|
58
|
+
* Returns { open, setOpen(bool), toggle(), openAgent(ask), storageKey,
|
|
59
|
+
* asked, start, startTaken }. `asked` counts the member's opens: the dock
|
|
60
|
+
* puts the cursor in the box when it rises, never on a page load that
|
|
61
|
+
* restores an open dock. `start` is the starting line an openAgent call
|
|
62
|
+
* left for the pane, until the pane takes it (`startTaken`).
|
|
63
|
+
*/
|
|
64
|
+
export function useAgentDock({ storageKey = "app", defaultOpen = false, listen = true } = {}) {
|
|
65
|
+
const [open, setOpenState] = useState(defaultOpen);
|
|
66
|
+
const [asked, setAsked] = useState(0);
|
|
67
|
+
const [start, setStart] = useState(null);
|
|
68
|
+
const now = useRef(open);
|
|
69
|
+
now.current = open;
|
|
70
|
+
// The stored choice is read after the first paint, so the server's and the
|
|
71
|
+
// browser's first render agree.
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
const v = readKey(dockKeys(storageKey).open);
|
|
74
|
+
if (v === "1") setOpenState(true);
|
|
75
|
+
else if (v === "0") setOpenState(false);
|
|
76
|
+
}, [storageKey]);
|
|
77
|
+
const setOpen = useCallback(
|
|
78
|
+
(next) => {
|
|
79
|
+
const v = Boolean(typeof next === "function" ? next(now.current) : next);
|
|
80
|
+
now.current = v;
|
|
81
|
+
setOpenState(v);
|
|
82
|
+
if (v) setAsked((n) => n + 1);
|
|
83
|
+
writeKey(dockKeys(storageKey).open, v ? "1" : "0");
|
|
84
|
+
},
|
|
85
|
+
[storageKey],
|
|
86
|
+
);
|
|
87
|
+
const toggle = useCallback(() => setOpen(!now.current), [setOpen]);
|
|
88
|
+
// "Have the agent do it": open, and leave the pane its first line.
|
|
89
|
+
const openWith = useCallback(
|
|
90
|
+
(request) => {
|
|
91
|
+
setOpen(true);
|
|
92
|
+
if (carriesLine(request)) setStart(request);
|
|
93
|
+
},
|
|
94
|
+
[setOpen],
|
|
95
|
+
);
|
|
96
|
+
const openHere = useCallback((input) => openWith(openRequest(input)), [openWith]);
|
|
97
|
+
const startTaken = useCallback((nonce) => setStart((s) => (s && s.nonce === nonce ? null : s)), []);
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
if (!listen || typeof window === "undefined") return undefined;
|
|
100
|
+
const on = (e) => {
|
|
101
|
+
const d = e.detail;
|
|
102
|
+
if (!d || (d.storageKey && d.storageKey !== storageKey)) return;
|
|
103
|
+
d.handled = true;
|
|
104
|
+
openWith(d);
|
|
105
|
+
};
|
|
106
|
+
window.addEventListener(OPEN_EVENT, on);
|
|
107
|
+
return () => window.removeEventListener(OPEN_EVENT, on);
|
|
108
|
+
}, [listen, storageKey, openWith]);
|
|
109
|
+
return { open, setOpen, toggle, openAgent: openHere, storageKey, asked, start, startTaken };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* "Have the agent do it": open the agent with its first line already in
|
|
114
|
+
* hand. Call it from anywhere on the page (a button on a screen, a card's
|
|
115
|
+
* action); the app's one dock hears it.
|
|
116
|
+
*
|
|
117
|
+
* openAgent("Draft a project plan for me")
|
|
118
|
+
* openAgent({ ask: "Draft a project plan for …", send: false })
|
|
119
|
+
*
|
|
120
|
+
* The line is sent as the member's first words in a new chat, and the agent
|
|
121
|
+
* asks for only what it is missing, in the chat; it then does the work with
|
|
122
|
+
* the app's own actions. `send: false` puts the line in the box instead, to
|
|
123
|
+
* finish. `fresh: false` carries on in the open chat. `storageKey` aims at
|
|
124
|
+
* one app's dock. → true when a dock took it; false when the page has none
|
|
125
|
+
* (the caller's own fallback, e.g. a form, may run then).
|
|
126
|
+
*
|
|
127
|
+
* With no line (`openAgent()`, or `{ ask: "" }`) it only opens the dock: it
|
|
128
|
+
* sends nothing and starts no chat. That is the one way to open the agent
|
|
129
|
+
* from a screen far from the shell (a "Talk to the agent" link, an empty
|
|
130
|
+
* state). Ask in the top bar toggles; this only ever opens.
|
|
131
|
+
*/
|
|
132
|
+
export function openAgent(input) {
|
|
133
|
+
if (typeof window === "undefined") return false;
|
|
134
|
+
const detail = { ...openRequest(input), handled: false };
|
|
135
|
+
window.dispatchEvent(new CustomEvent(OPEN_EVENT, { detail }));
|
|
136
|
+
return detail.handled;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* dock what useAgentDock() returns: the open state, its setter and
|
|
141
|
+
* the storage key. Or pass the parts:
|
|
142
|
+
* open, onOpenChange the dock is yours to open and close (controlled)
|
|
143
|
+
* storageKey where the width is kept (default "app", or dock's)
|
|
144
|
+
* label the column's name for a screen reader (default "Agent")
|
|
145
|
+
* columnAt the body width in px from which it is a column beside the
|
|
146
|
+
* work; under it, a sheet over the app (default 900)
|
|
147
|
+
* children the pane: your own component over AgentPane (the app's
|
|
148
|
+
* yes card, its title). Without children the dock draws an
|
|
149
|
+
* AgentPane from chat and the props below
|
|
150
|
+
* chat, title, suggestions, empty, placeholder, yes, mark, onOpen, recent
|
|
151
|
+
* the AgentPane's own (see agent-pane)
|
|
152
|
+
* className the column's own layout. Never colour.
|
|
153
|
+
*/
|
|
154
|
+
export function AgentDock({
|
|
155
|
+
dock,
|
|
156
|
+
open: openProp,
|
|
157
|
+
onOpenChange,
|
|
158
|
+
storageKey: keyProp,
|
|
159
|
+
label = "Agent",
|
|
160
|
+
columnAt = COLUMN_AT,
|
|
161
|
+
children,
|
|
162
|
+
chat,
|
|
163
|
+
title,
|
|
164
|
+
suggestions,
|
|
165
|
+
empty,
|
|
166
|
+
placeholder,
|
|
167
|
+
yes,
|
|
168
|
+
mark,
|
|
169
|
+
onOpen,
|
|
170
|
+
recent,
|
|
171
|
+
className,
|
|
172
|
+
}) {
|
|
173
|
+
const storageKey = keyProp ?? dock?.storageKey ?? "app";
|
|
174
|
+
// Without a dock from the shell the column keeps its own state (and hears
|
|
175
|
+
// openAgent); with one, the shell's hook is the one that listens.
|
|
176
|
+
const own = useAgentDock({ storageKey, listen: !dock });
|
|
177
|
+
const open = Boolean(openProp ?? dock?.open ?? own.open);
|
|
178
|
+
const setOpen = onOpenChange ?? dock?.setOpen ?? own.setOpen;
|
|
179
|
+
const asked = (dock ?? own).asked;
|
|
180
|
+
const start = (dock ?? own).start ?? null;
|
|
181
|
+
const startTaken = (dock ?? own).startTaken;
|
|
182
|
+
// A controlled dock (open + onOpenChange, no dock) is told when openAgent
|
|
183
|
+
// asks it to open.
|
|
184
|
+
const ownAsked = useRef(own.asked);
|
|
185
|
+
useEffect(() => {
|
|
186
|
+
if (ownAsked.current === own.asked) return;
|
|
187
|
+
ownAsked.current = own.asked;
|
|
188
|
+
if (!dock && openProp !== undefined) onOpenChange?.(true);
|
|
189
|
+
}, [own.asked, dock, openProp, onOpenChange]);
|
|
190
|
+
|
|
191
|
+
const asideRef = useRef(null);
|
|
192
|
+
const bodyRef = useRef(null);
|
|
193
|
+
const [body, setBody] = useState(0);
|
|
194
|
+
const [width, setWidth] = useState(null);
|
|
195
|
+
const widthRef = useRef(null);
|
|
196
|
+
const [mounted, setMounted] = useState(open);
|
|
197
|
+
const column = hasRoom(body, columnAt);
|
|
198
|
+
|
|
199
|
+
useEffect(() => {
|
|
200
|
+
if (open) setMounted(true);
|
|
201
|
+
}, [open]);
|
|
202
|
+
|
|
203
|
+
// The width this browser kept for this app.
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
const w = storedWidth(readKey(dockKeys(storageKey).width));
|
|
206
|
+
widthRef.current = w;
|
|
207
|
+
setWidth(w);
|
|
208
|
+
}, [storageKey]);
|
|
209
|
+
const keepWidth = useCallback(
|
|
210
|
+
(w) => {
|
|
211
|
+
widthRef.current = w;
|
|
212
|
+
setWidth(w);
|
|
213
|
+
writeKey(dockKeys(storageKey).width, w === null ? null : String(w));
|
|
214
|
+
},
|
|
215
|
+
[storageKey],
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
// The body it sits beside (the frame's row), measured, never the window.
|
|
219
|
+
useEffect(() => {
|
|
220
|
+
const el = asideRef.current?.parentElement;
|
|
221
|
+
if (!el) return undefined;
|
|
222
|
+
bodyRef.current = el;
|
|
223
|
+
const measure = () => setBody(el.clientWidth);
|
|
224
|
+
measure();
|
|
225
|
+
if (typeof ResizeObserver === "undefined") return undefined;
|
|
226
|
+
const ro = new ResizeObserver(measure);
|
|
227
|
+
ro.observe(el);
|
|
228
|
+
return () => ro.disconnect();
|
|
229
|
+
}, []);
|
|
230
|
+
|
|
231
|
+
// The member opened it: the cursor goes to the box. The pane mounts on the
|
|
232
|
+
// same render that opens the column, so the box may not exist for a frame or
|
|
233
|
+
// two: try on each frame until it does (and give up after a second). What
|
|
234
|
+
// had focus (the Ask button) is remembered, and folding the dock gives focus
|
|
235
|
+
// back to it.
|
|
236
|
+
const firstAsk = useRef(asked);
|
|
237
|
+
const opener = useRef(null);
|
|
238
|
+
useEffect(() => {
|
|
239
|
+
if (asked === firstAsk.current || !open) return undefined;
|
|
240
|
+
firstAsk.current = asked;
|
|
241
|
+
const here = document.activeElement;
|
|
242
|
+
if (here && here !== document.body && !asideRef.current?.contains(here)) opener.current = here;
|
|
243
|
+
let id = 0;
|
|
244
|
+
let tries = 0;
|
|
245
|
+
const attempt = () => {
|
|
246
|
+
const box = asideRef.current?.querySelector("textarea:not([disabled])");
|
|
247
|
+
if (box) {
|
|
248
|
+
box.focus({ preventScroll: true });
|
|
249
|
+
if (document.activeElement === box) return;
|
|
250
|
+
}
|
|
251
|
+
if (++tries < 60) id = requestAnimationFrame(attempt);
|
|
252
|
+
};
|
|
253
|
+
id = requestAnimationFrame(attempt);
|
|
254
|
+
return () => cancelAnimationFrame(id);
|
|
255
|
+
}, [asked, open]);
|
|
256
|
+
const wasOpen = useRef(open);
|
|
257
|
+
useEffect(() => {
|
|
258
|
+
const was = wasOpen.current;
|
|
259
|
+
wasOpen.current = open;
|
|
260
|
+
if (!was || open) return;
|
|
261
|
+
const back = opener.current;
|
|
262
|
+
opener.current = null;
|
|
263
|
+
const active = document.activeElement;
|
|
264
|
+
// Only when focus was inside the dock (or lost): never steal it from
|
|
265
|
+
// somewhere the member has since gone.
|
|
266
|
+
if (back && back.isConnected && (!active || active === document.body || asideRef.current?.contains(active))) back.focus({ preventScroll: true });
|
|
267
|
+
}, [open]);
|
|
268
|
+
|
|
269
|
+
// A sheet lays over the app, so Escape folds it.
|
|
270
|
+
useEffect(() => {
|
|
271
|
+
if (!open || column) return undefined;
|
|
272
|
+
const onKey = (e) => {
|
|
273
|
+
if (e.key === "Escape" && !e.defaultPrevented) setOpen(false);
|
|
274
|
+
};
|
|
275
|
+
document.addEventListener("keydown", onKey);
|
|
276
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
277
|
+
}, [open, column, setOpen]);
|
|
278
|
+
|
|
279
|
+
const bodyWidth = () => bodyRef.current?.clientWidth ?? body;
|
|
280
|
+
const onPointerDown = (e) => {
|
|
281
|
+
if (e.button !== 0) return;
|
|
282
|
+
e.preventDefault();
|
|
283
|
+
const b = bodyWidth();
|
|
284
|
+
const startWidth = asideRef.current?.offsetWidth ?? columnWidth(widthRef.current, b);
|
|
285
|
+
const startX = e.clientX;
|
|
286
|
+
try {
|
|
287
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
288
|
+
} catch {
|
|
289
|
+
/* a pointer the runtime does not know */
|
|
290
|
+
}
|
|
291
|
+
const onMove = (ev) => {
|
|
292
|
+
const w = dragged(startWidth, startX, ev.clientX, b);
|
|
293
|
+
widthRef.current = w;
|
|
294
|
+
setWidth(w);
|
|
295
|
+
};
|
|
296
|
+
const onUp = () => {
|
|
297
|
+
window.removeEventListener("pointermove", onMove);
|
|
298
|
+
window.removeEventListener("pointerup", onUp);
|
|
299
|
+
window.removeEventListener("pointercancel", onUp);
|
|
300
|
+
keepWidth(widthRef.current);
|
|
301
|
+
};
|
|
302
|
+
window.addEventListener("pointermove", onMove);
|
|
303
|
+
window.addEventListener("pointerup", onUp);
|
|
304
|
+
window.addEventListener("pointercancel", onUp);
|
|
305
|
+
};
|
|
306
|
+
const onKeyDown = (e) => {
|
|
307
|
+
const b = bodyWidth();
|
|
308
|
+
const next = nudged(e.key, asideRef.current?.offsetWidth ?? columnWidth(widthRef.current, b), b);
|
|
309
|
+
if (next === undefined) return;
|
|
310
|
+
e.preventDefault();
|
|
311
|
+
keepWidth(next);
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
const ctx = useMemo(() => ({ close: () => setOpen(false), mode: column ? "column" : "sheet", start, taken: startTaken }), [setOpen, column, start, startTaken]);
|
|
315
|
+
const pane =
|
|
316
|
+
children ??
|
|
317
|
+
(chat ? (
|
|
318
|
+
<AgentPane chat={chat} title={title} suggestions={suggestions} empty={empty} placeholder={placeholder} yes={yes} mark={mark} onOpen={onOpen} recent={recent} />
|
|
319
|
+
) : null);
|
|
320
|
+
|
|
321
|
+
return (
|
|
322
|
+
<AgentDockContext.Provider value={ctx}>
|
|
323
|
+
{open && column ? (
|
|
324
|
+
<div
|
|
325
|
+
role="separator"
|
|
326
|
+
aria-orientation="vertical"
|
|
327
|
+
aria-label="Resize the agent column"
|
|
328
|
+
aria-valuemin={MIN_W}
|
|
329
|
+
aria-valuenow={width ?? undefined}
|
|
330
|
+
title="Drag to resize · ← → to nudge · Home for a quarter"
|
|
331
|
+
tabIndex={0}
|
|
332
|
+
onPointerDown={onPointerDown}
|
|
333
|
+
onKeyDown={onKeyDown}
|
|
334
|
+
className="group relative z-10 -mx-[3px] w-1.5 shrink-0 cursor-col-resize touch-none outline-none print:hidden"
|
|
335
|
+
>
|
|
336
|
+
<span aria-hidden="true" className="absolute inset-y-0 left-[2px] right-[2px] rounded-sm transition-colors group-hover:bg-border group-focus-visible:bg-ring group-active:bg-ring motion-reduce:transition-none" />
|
|
337
|
+
</div>
|
|
338
|
+
) : null}
|
|
339
|
+
<aside
|
|
340
|
+
ref={asideRef}
|
|
341
|
+
aria-label={label}
|
|
342
|
+
data-agent-dock={column ? "column" : "sheet"}
|
|
343
|
+
data-open={open ? "" : undefined}
|
|
344
|
+
style={column ? { width: "clamp(280px, var(--agent-dock-w, 25%), 50%)", "--agent-dock-w": width !== null ? `${width}px` : undefined } : undefined}
|
|
345
|
+
className={cx(
|
|
346
|
+
open ? "flex" : "hidden",
|
|
347
|
+
"min-h-0 min-w-0 flex-col border-l border-border bg-background text-foreground print:hidden",
|
|
348
|
+
column ? "shrink-0" : "fixed inset-y-0 right-0 z-40 w-[min(100%,26rem)] shadow-xl",
|
|
349
|
+
className,
|
|
350
|
+
)}
|
|
351
|
+
>
|
|
352
|
+
<div className="flex min-h-0 flex-1 flex-col">{mounted ? pane : null}</div>
|
|
353
|
+
</aside>
|
|
354
|
+
</AgentDockContext.Provider>
|
|
355
|
+
);
|
|
356
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agent-dock",
|
|
3
|
+
"title": "AgentDock",
|
|
4
|
+
"description": "Where an app's agent lives, as the desk's agent sidebar does: a column on the right beside the work, under the top bar, with the content reflowing beside it. A quarter of the body until the member drags the divider (never under 280 px, never over half), the width and the open state remembered in this browser per app; the divider is a separator a keyboard can move (arrows nudge, Home goes back to a quarter, End to the widest). Where the body is under 900 px (a container measure, not the window) the dock becomes a sheet laid over the app from the right, folded by Escape, and the chat does not remount on the way. It is the host only: the pane inside draws its own head (title, New chat, Chats, close) and its empty state (past chats listed, suggestions, the box); the dock tells the pane how to close. The pane stays mounted once opened, so a turn in flight and a draft survive folding. It sits in the `dock` slot of the kit's AppLayout. useAgentDock() holds the open state for the app's Ask button, remembered per app, and puts the cursor in the box when the member opens it. openAgent({ ask }) is how every \"have the agent do it\" button works (X9): it opens the dock with the agent's first line already sent in a new chat (or put in the box), so the agent asks only for what it is missing, in the chat, and no form comes first.",
|
|
5
|
+
"useWhen": "Any app that has an agent the member asks from the shell: the top bar's Ask, a chip that sends a question, a record whose Ask follows the open record. One dock per app, in the shell; the record's own pane is the same dock with a chat scoped to that record (useAgentPane({ row })), never a second column. Replaces a modal Sheet or an inspector tab as the agent's place. Not for work the member commissions from a verb and watches land (job-panel), and not for a conversation that fills a whole screen (agent-pane in a page of its own).",
|
|
6
|
+
"tags": [
|
|
7
|
+
"dock",
|
|
8
|
+
"sidebar",
|
|
9
|
+
"column",
|
|
10
|
+
"ask",
|
|
11
|
+
"chat",
|
|
12
|
+
"copilot",
|
|
13
|
+
"resize",
|
|
14
|
+
"panel",
|
|
15
|
+
"sheet",
|
|
16
|
+
"shell"
|
|
17
|
+
],
|
|
18
|
+
"exports": [
|
|
19
|
+
"AgentDock",
|
|
20
|
+
"useAgentDock",
|
|
21
|
+
"openAgent",
|
|
22
|
+
"COLUMN_AT",
|
|
23
|
+
"DEFAULT_SHARE",
|
|
24
|
+
"MIN_W",
|
|
25
|
+
"OPEN_EVENT",
|
|
26
|
+
"STEP",
|
|
27
|
+
"carriesLine",
|
|
28
|
+
"clampWidth",
|
|
29
|
+
"columnWidth",
|
|
30
|
+
"dockKeys",
|
|
31
|
+
"dragged",
|
|
32
|
+
"hasRoom",
|
|
33
|
+
"maxWidth",
|
|
34
|
+
"nudged",
|
|
35
|
+
"openRequest",
|
|
36
|
+
"storedWidth"
|
|
37
|
+
],
|
|
38
|
+
"files": [
|
|
39
|
+
"index.js",
|
|
40
|
+
"agent-dock.jsx",
|
|
41
|
+
"dock-model.js"
|
|
42
|
+
],
|
|
43
|
+
"requires": [
|
|
44
|
+
"agent-pane"
|
|
45
|
+
],
|
|
46
|
+
"offer": { "since": "0.8.1", "why": "where the app's agent lives, a column beside the work, with openAgent() for every have-the-agent-do-it button" }
|
|
47
|
+
}
|