makersclaw 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -4
- package/package.json +1 -1
- package/r/activity-log.json +52 -0
- package/r/agent-changes.json +51 -0
- package/r/agent-chat.json +53 -0
- package/r/agent-column.json +85 -0
- package/r/agent-dock.json +57 -0
- package/r/agent-kit.json +49 -0
- package/r/agent-marks.json +49 -0
- package/r/agent-pane.json +93 -0
- package/r/ai-action.json +56 -0
- package/r/annotate.json +4 -4
- package/r/approval-request.json +2 -2
- package/r/approval.json +54 -0
- package/r/ask-bar.json +50 -0
- package/r/audio-player.json +2 -2
- package/r/auth-form.json +2 -2
- package/r/automation-editor.json +66 -0
- package/r/automation-list.json +57 -0
- package/r/automation-model.json +118 -0
- package/r/automations-screen.json +71 -0
- package/r/board.json +2 -2
- package/r/calendar-view.json +3 -3
- package/r/cart.json +2 -2
- package/r/chart.json +5 -5
- package/r/chat-pane.json +84 -0
- package/r/chat.json +3 -3
- package/r/code-editor.json +2 -2
- package/r/comment-box.json +3 -3
- package/r/connect-card.json +51 -0
- package/r/contact-section.json +2 -2
- package/r/core.json +18 -6
- package/r/cost.json +51 -0
- package/r/csv-import.json +3 -3
- package/r/data-picker.json +60 -0
- package/r/data-table.json +2 -2
- package/r/diagram.json +2 -2
- package/r/doc-editor.json +2 -2
- package/r/doc-page.json +3 -3
- package/r/email-render.json +2 -2
- package/r/faq.json +2 -2
- package/r/file-upload.json +2 -2
- package/r/first-run.json +63 -0
- package/r/fix-note.json +57 -0
- package/r/flow-canvas.json +3 -3
- package/r/flow-editor.json +79 -0
- package/r/footer.json +3 -3
- package/r/gantt.json +3 -3
- package/r/job-panel.json +2 -2
- package/r/json-editor.json +3 -3
- package/r/library-shelf.json +56 -0
- package/r/map.json +2 -2
- package/r/margin-notes.json +67 -0
- package/r/menubar.json +2 -2
- package/r/mind-map.json +4 -4
- package/r/navigation-menu.json +2 -2
- package/r/needs-you.json +55 -0
- package/r/order-detail.json +2 -2
- package/r/org-chart.json +4 -4
- package/r/pace-bar.json +69 -0
- package/r/page-builder.json +3 -3
- package/r/payment-methods.json +2 -2
- package/r/pdf-document.json +2 -2
- package/r/pdf-viewer.json +3 -3
- package/r/product-detail.json +2 -2
- package/r/product-grid.json +3 -3
- package/r/record-form.json +2 -2
- package/r/record-grid.json +52 -0
- package/r/record-notes.json +4 -4
- package/r/registry.json +4089 -1510
- package/r/run-history.json +52 -0
- package/r/run-view.json +80 -0
- package/r/schedule-card.json +54 -0
- package/r/schedule-engine.json +79 -0
- package/r/schedule-gantt.json +83 -0
- package/r/schedule-ruler.json +64 -0
- package/r/search-dialog.json +2 -2
- package/r/settings-list.json +2 -2
- package/r/setup-checklist.json +2 -2
- package/r/share-dialog.json +2 -2
- package/r/sidebar.json +3 -3
- package/r/signature-pad.json +2 -2
- package/r/sketch-canvas.json +3 -3
- package/r/source-receipt.json +52 -0
- package/r/spreadsheet.json +3 -3
- package/r/stat-cards.json +3 -3
- package/r/step-picker.json +53 -0
- package/r/step-settings.json +72 -0
- package/r/suggestion.json +58 -0
- package/r/sum-bar.json +64 -0
- package/r/task-list.json +3 -3
- package/r/task-model.json +2 -2
- package/r/testimonials.json +2 -2
- package/r/tier-map.json +67 -0
- package/r/time-grid.json +73 -0
- package/r/time-strip.json +64 -0
- package/r/top-bar.json +66 -0
- package/r/transaction-list.json +2 -2
- package/r/trigger-picker.json +61 -0
- package/r/whiteboard.json +4 -4
- package/r/workflow-view.json +55 -0
- package/registry/blocks/activity-log/README.md +112 -0
- package/registry/blocks/activity-log/activity-log.jsx +351 -0
- package/registry/blocks/activity-log/block.json +33 -0
- package/registry/blocks/activity-log/demo.jsx +48 -0
- package/registry/blocks/activity-log/index.js +4 -0
- package/registry/blocks/agent-changes/README.md +94 -0
- package/registry/blocks/agent-changes/agent-changes.jsx +118 -0
- package/registry/blocks/agent-changes/block.json +28 -0
- package/registry/blocks/agent-changes/demo.jsx +39 -0
- package/registry/blocks/agent-changes/index.js +3 -0
- package/registry/blocks/agent-chat/README.md +145 -0
- package/registry/blocks/agent-chat/agent-chat.jsx +415 -0
- package/registry/blocks/agent-chat/block.json +30 -0
- package/registry/blocks/agent-chat/demo.jsx +165 -0
- package/registry/blocks/agent-chat/index.js +3 -0
- package/registry/blocks/agent-column/README.md +197 -0
- package/registry/blocks/agent-column/agent-column.jsx +492 -0
- package/registry/blocks/agent-column/block.json +67 -0
- package/registry/blocks/agent-column/column-picker.jsx +163 -0
- package/registry/blocks/agent-column/demo.jsx +50 -0
- package/registry/blocks/agent-column/index.js +7 -0
- package/registry/blocks/agent-column/model.js +164 -0
- package/registry/blocks/agent-column/receipt.jsx +483 -0
- package/registry/blocks/agent-column/spreadsheet-type.jsx +24 -0
- package/registry/blocks/agent-dock/README.md +210 -0
- package/registry/blocks/agent-dock/agent-dock.jsx +356 -0
- package/registry/blocks/agent-dock/block.json +47 -0
- package/registry/blocks/agent-dock/demo.jsx +153 -0
- package/registry/blocks/agent-dock/dock-model.js +118 -0
- package/registry/blocks/agent-dock/index.js +5 -0
- package/registry/blocks/agent-kit/README.md +73 -0
- package/registry/blocks/agent-kit/agent-kit.jsx +86 -0
- package/registry/blocks/agent-kit/block.json +10 -0
- package/registry/blocks/agent-kit/demo.jsx +35 -0
- package/registry/blocks/agent-kit/index.js +3 -0
- package/registry/blocks/agent-marks/README.md +80 -0
- package/registry/blocks/agent-marks/agent-marks.jsx +133 -0
- package/registry/blocks/agent-marks/block.json +27 -0
- package/registry/blocks/agent-marks/demo.jsx +49 -0
- package/registry/blocks/agent-marks/index.js +3 -0
- package/registry/blocks/agent-pane/README.md +225 -0
- package/registry/blocks/agent-pane/agent-pane.jsx +244 -0
- package/registry/blocks/agent-pane/block.json +41 -0
- package/registry/blocks/agent-pane/composer.jsx +6 -0
- package/registry/blocks/agent-pane/demo.jsx +233 -0
- package/registry/blocks/agent-pane/dock-context.js +10 -0
- package/registry/blocks/agent-pane/index.js +4 -0
- package/registry/blocks/agent-pane/prose.jsx +84 -0
- package/registry/blocks/agent-pane/questions.jsx +134 -0
- package/registry/blocks/agent-pane/transcript.jsx +268 -0
- package/registry/blocks/agent-pane/work.jsx +368 -0
- package/registry/blocks/ai-action/README.md +94 -0
- package/registry/blocks/ai-action/ai-action.jsx +188 -0
- package/registry/blocks/ai-action/block.json +34 -0
- package/registry/blocks/ai-action/demo.jsx +93 -0
- package/registry/blocks/ai-action/index.js +3 -0
- package/registry/blocks/annotate/README.md +14 -11
- package/registry/blocks/approval/README.md +145 -0
- package/registry/blocks/approval/approval.jsx +163 -0
- package/registry/blocks/approval/block.json +31 -0
- package/registry/blocks/approval/demo.jsx +121 -0
- package/registry/blocks/approval/index.js +3 -0
- package/registry/blocks/ask-bar/README.md +105 -0
- package/registry/blocks/ask-bar/ask-bar.jsx +166 -0
- package/registry/blocks/ask-bar/block.json +26 -0
- package/registry/blocks/ask-bar/demo.jsx +78 -0
- package/registry/blocks/ask-bar/index.js +3 -0
- package/registry/blocks/automation-editor/README.md +291 -0
- package/registry/blocks/automation-editor/automation-editor.jsx +656 -0
- package/registry/blocks/automation-editor/block.json +39 -0
- package/registry/blocks/automation-editor/demo.jsx +284 -0
- package/registry/blocks/automation-editor/index.js +4 -0
- package/registry/blocks/automation-editor/run-inputs.jsx +82 -0
- package/registry/blocks/automation-list/README.md +129 -0
- package/registry/blocks/automation-list/automation-list.jsx +476 -0
- package/registry/blocks/automation-list/block.json +35 -0
- package/registry/blocks/automation-list/demo.jsx +67 -0
- package/registry/blocks/automation-list/index.js +3 -0
- package/registry/blocks/automation-model/README.md +109 -0
- package/registry/blocks/automation-model/block.json +135 -0
- package/registry/blocks/automation-model/catalog.js +256 -0
- package/registry/blocks/automation-model/check.js +161 -0
- package/registry/blocks/automation-model/demo.jsx +42 -0
- package/registry/blocks/automation-model/edit.js +262 -0
- package/registry/blocks/automation-model/fixes.js +287 -0
- package/registry/blocks/automation-model/glyph.jsx +13 -0
- package/registry/blocks/automation-model/graph.js +410 -0
- package/registry/blocks/automation-model/index.js +33 -0
- package/registry/blocks/automation-model/overlay.js +175 -0
- package/registry/blocks/automation-model/parts.jsx +94 -0
- package/registry/blocks/automation-model/schedule.js +53 -0
- package/registry/blocks/automation-model/templates.js +128 -0
- package/registry/blocks/automation-model/values.js +29 -0
- package/registry/blocks/automation-model/words.js +439 -0
- package/registry/blocks/automations-screen/README.md +207 -0
- package/registry/blocks/automations-screen/automations-screen.jsx +452 -0
- package/registry/blocks/automations-screen/block.json +47 -0
- package/registry/blocks/automations-screen/demo.jsx +364 -0
- package/registry/blocks/automations-screen/index.js +4 -0
- package/registry/blocks/automations-screen/screen-model.js +129 -0
- package/registry/blocks/chart/cartesian.jsx +1 -1
- package/registry/blocks/chart/core.jsx +33 -23
- package/registry/blocks/chart/pie.jsx +1 -1
- package/registry/blocks/chat/README.md +4 -1
- package/registry/blocks/chat/demo.jsx +2 -2
- package/registry/blocks/chat-pane/README.md +143 -0
- package/registry/blocks/chat-pane/block.json +48 -0
- package/registry/blocks/chat-pane/chat-attach.js +166 -0
- package/registry/blocks/chat-pane/chat-composer.jsx +195 -0
- package/registry/blocks/chat-pane/chat-context.js +17 -0
- package/registry/blocks/chat-pane/chat-model.js +43 -0
- package/registry/blocks/chat-pane/chat-pane.jsx +398 -0
- package/registry/blocks/chat-pane/chats-list.jsx +52 -0
- package/registry/blocks/chat-pane/demo.jsx +103 -0
- package/registry/blocks/chat-pane/index.js +9 -0
- package/registry/blocks/connect-card/README.md +99 -0
- package/registry/blocks/connect-card/block.json +10 -0
- package/registry/blocks/connect-card/connect-card.jsx +103 -0
- package/registry/blocks/connect-card/demo.jsx +49 -0
- package/registry/blocks/connect-card/index.js +3 -0
- package/registry/blocks/cost/README.md +115 -0
- package/registry/blocks/cost/block.json +10 -0
- package/registry/blocks/cost/cost.jsx +176 -0
- package/registry/blocks/cost/demo.jsx +66 -0
- package/registry/blocks/cost/index.js +3 -0
- package/registry/blocks/data-picker/README.md +81 -0
- package/registry/blocks/data-picker/block.json +38 -0
- package/registry/blocks/data-picker/data-picker.jsx +210 -0
- package/registry/blocks/data-picker/demo.jsx +34 -0
- package/registry/blocks/data-picker/fields.js +89 -0
- package/registry/blocks/data-picker/index.js +4 -0
- package/registry/blocks/data-table/demo.jsx +1 -1
- package/registry/blocks/first-run/README.md +118 -0
- package/registry/blocks/first-run/block.json +11 -0
- package/registry/blocks/first-run/demo.jsx +88 -0
- package/registry/blocks/first-run/first-run-model.js +51 -0
- package/registry/blocks/first-run/first-run.jsx +156 -0
- package/registry/blocks/first-run/index.js +5 -0
- package/registry/blocks/first-run/probe-rules.js +26 -0
- package/registry/blocks/fix-note/README.md +79 -0
- package/registry/blocks/fix-note/block.json +35 -0
- package/registry/blocks/fix-note/demo.jsx +30 -0
- package/registry/blocks/fix-note/fix-note.jsx +235 -0
- package/registry/blocks/fix-note/index.js +3 -0
- package/registry/blocks/flow-canvas/README.md +15 -8
- package/registry/blocks/flow-editor/README.md +108 -0
- package/registry/blocks/flow-editor/block.json +44 -0
- package/registry/blocks/flow-editor/demo.jsx +115 -0
- package/registry/blocks/flow-editor/flow-canvas.jsx +568 -0
- package/registry/blocks/flow-editor/flow-editor.jsx +154 -0
- package/registry/blocks/flow-editor/index.js +6 -0
- package/registry/blocks/flow-editor/minimap.jsx +78 -0
- package/registry/blocks/flow-editor/outline.jsx +134 -0
- package/registry/blocks/footer/README.md +3 -2
- package/registry/blocks/library-shelf/README.md +117 -0
- package/registry/blocks/library-shelf/block.json +28 -0
- package/registry/blocks/library-shelf/demo.jsx +68 -0
- package/registry/blocks/library-shelf/index.js +3 -0
- package/registry/blocks/library-shelf/library-shelf.jsx +211 -0
- package/registry/blocks/library-shelf/probe-rules.js +15 -0
- package/registry/blocks/margin-notes/README.md +222 -0
- package/registry/blocks/margin-notes/block.json +10 -0
- package/registry/blocks/margin-notes/demo.jsx +144 -0
- package/registry/blocks/margin-notes/index.js +20 -0
- package/registry/blocks/margin-notes/margin-model.js +224 -0
- package/registry/blocks/margin-notes/margin-notes.jsx +465 -0
- package/registry/blocks/mind-map/README.md +4 -2
- package/registry/blocks/needs-you/README.md +177 -0
- package/registry/blocks/needs-you/block.json +34 -0
- package/registry/blocks/needs-you/demo.jsx +143 -0
- package/registry/blocks/needs-you/index.js +3 -0
- package/registry/blocks/needs-you/needs-you.jsx +441 -0
- package/registry/blocks/org-chart/README.md +4 -2
- package/registry/blocks/pace-bar/README.md +230 -0
- package/registry/blocks/pace-bar/block.json +10 -0
- package/registry/blocks/pace-bar/demo.jsx +255 -0
- package/registry/blocks/pace-bar/index.js +22 -0
- package/registry/blocks/pace-bar/pace-bar.jsx +461 -0
- package/registry/blocks/pace-bar/pace-model.js +288 -0
- package/registry/blocks/page-builder/README.md +1 -1
- package/registry/blocks/page-builder/demo.jsx +3 -3
- package/registry/blocks/pdf-viewer/README.md +4 -1
- package/registry/blocks/record-grid/README.md +100 -0
- package/registry/blocks/record-grid/block.json +28 -0
- package/registry/blocks/record-grid/demo.jsx +43 -0
- package/registry/blocks/record-grid/index.js +3 -0
- package/registry/blocks/record-grid/record-grid.jsx +251 -0
- package/registry/blocks/run-history/README.md +138 -0
- package/registry/blocks/run-history/block.json +10 -0
- package/registry/blocks/run-history/demo.jsx +74 -0
- package/registry/blocks/run-history/index.js +3 -0
- package/registry/blocks/run-history/run-history.jsx +218 -0
- package/registry/blocks/run-view/README.md +174 -0
- package/registry/blocks/run-view/block.json +50 -0
- package/registry/blocks/run-view/demo.jsx +286 -0
- package/registry/blocks/run-view/index.js +5 -0
- package/registry/blocks/run-view/run-controls.jsx +266 -0
- package/registry/blocks/run-view/run-view.jsx +236 -0
- package/registry/blocks/run-view/runs-list.jsx +99 -0
- package/registry/blocks/schedule-card/README.md +121 -0
- package/registry/blocks/schedule-card/block.json +10 -0
- package/registry/blocks/schedule-card/demo.jsx +82 -0
- package/registry/blocks/schedule-card/index.js +3 -0
- package/registry/blocks/schedule-card/schedule-card.jsx +136 -0
- package/registry/blocks/schedule-engine/README.md +137 -0
- package/registry/blocks/schedule-engine/block.json +10 -0
- package/registry/blocks/schedule-engine/demo.jsx +82 -0
- package/registry/blocks/schedule-engine/engine.js +769 -0
- package/registry/blocks/schedule-engine/index.js +9 -0
- package/registry/blocks/schedule-engine/ops.js +71 -0
- package/registry/blocks/schedule-engine/place.js +294 -0
- package/registry/blocks/schedule-engine/tree.js +79 -0
- package/registry/blocks/schedule-engine/waits.js +80 -0
- package/registry/blocks/schedule-gantt/README.md +159 -0
- package/registry/blocks/schedule-gantt/block.json +10 -0
- package/registry/blocks/schedule-gantt/demo.jsx +223 -0
- package/registry/blocks/schedule-gantt/gantt-model.js +219 -0
- package/registry/blocks/schedule-gantt/geometry.js +176 -0
- package/registry/blocks/schedule-gantt/index.js +26 -0
- package/registry/blocks/schedule-gantt/schedule-gantt.jsx +882 -0
- package/registry/blocks/schedule-gantt/sheet.jsx +372 -0
- package/registry/blocks/schedule-gantt/track.jsx +190 -0
- package/registry/blocks/schedule-ruler/README.md +148 -0
- package/registry/blocks/schedule-ruler/block.json +10 -0
- package/registry/blocks/schedule-ruler/demo.jsx +181 -0
- package/registry/blocks/schedule-ruler/index.js +21 -0
- package/registry/blocks/schedule-ruler/ruler-model.js +239 -0
- package/registry/blocks/schedule-ruler/schedule-ruler.jsx +548 -0
- package/registry/blocks/sidebar/README.md +23 -13
- package/registry/blocks/sketch-canvas/README.md +15 -9
- package/registry/blocks/source-receipt/README.md +97 -0
- package/registry/blocks/source-receipt/block.json +30 -0
- package/registry/blocks/source-receipt/demo.jsx +71 -0
- package/registry/blocks/source-receipt/index.js +3 -0
- package/registry/blocks/source-receipt/source-receipt.jsx +107 -0
- package/registry/blocks/spreadsheet/spreadsheet.jsx +15 -1
- package/registry/blocks/step-picker/README.md +78 -0
- package/registry/blocks/step-picker/block.json +31 -0
- package/registry/blocks/step-picker/demo.jsx +23 -0
- package/registry/blocks/step-picker/index.js +3 -0
- package/registry/blocks/step-picker/step-picker.jsx +144 -0
- package/registry/blocks/step-settings/README.md +100 -0
- package/registry/blocks/step-settings/block.json +44 -0
- package/registry/blocks/step-settings/demo.jsx +93 -0
- package/registry/blocks/step-settings/fields.jsx +58 -0
- package/registry/blocks/step-settings/flow-settings.jsx +115 -0
- package/registry/blocks/step-settings/index.js +5 -0
- package/registry/blocks/step-settings/step-settings.jsx +290 -0
- package/registry/blocks/suggestion/README.md +171 -0
- package/registry/blocks/suggestion/block.json +40 -0
- package/registry/blocks/suggestion/demo.jsx +91 -0
- package/registry/blocks/suggestion/index.js +3 -0
- package/registry/blocks/suggestion/suggestion.jsx +436 -0
- package/registry/blocks/sum-bar/README.md +122 -0
- package/registry/blocks/sum-bar/block.json +10 -0
- package/registry/blocks/sum-bar/demo.jsx +110 -0
- package/registry/blocks/sum-bar/index.js +5 -0
- package/registry/blocks/sum-bar/sum-bar.jsx +209 -0
- package/registry/blocks/sum-bar/sum-model.js +126 -0
- package/registry/blocks/tier-map/README.md +217 -0
- package/registry/blocks/tier-map/block.json +10 -0
- package/registry/blocks/tier-map/demo.jsx +222 -0
- package/registry/blocks/tier-map/index.js +24 -0
- package/registry/blocks/tier-map/tier-map.jsx +574 -0
- package/registry/blocks/tier-map/tier-model.js +318 -0
- package/registry/blocks/time-grid/README.md +305 -0
- package/registry/blocks/time-grid/block.json +10 -0
- package/registry/blocks/time-grid/demo.jsx +279 -0
- package/registry/blocks/time-grid/grid-model.js +322 -0
- package/registry/blocks/time-grid/index.js +25 -0
- package/registry/blocks/time-grid/time-grid.jsx +1325 -0
- package/registry/blocks/time-strip/README.md +141 -0
- package/registry/blocks/time-strip/block.json +10 -0
- package/registry/blocks/time-strip/demo.jsx +129 -0
- package/registry/blocks/time-strip/index.js +19 -0
- package/registry/blocks/time-strip/strip-model.js +178 -0
- package/registry/blocks/time-strip/time-strip.jsx +260 -0
- package/registry/blocks/top-bar/README.md +149 -0
- package/registry/blocks/top-bar/block.json +35 -0
- package/registry/blocks/top-bar/demo.jsx +78 -0
- package/registry/blocks/top-bar/index.js +5 -0
- package/registry/blocks/top-bar/probe-rules.js +74 -0
- package/registry/blocks/top-bar/top-bar-model.js +152 -0
- package/registry/blocks/top-bar/top-bar.jsx +244 -0
- package/registry/blocks/trigger-picker/README.md +98 -0
- package/registry/blocks/trigger-picker/block.json +38 -0
- package/registry/blocks/trigger-picker/demo.jsx +59 -0
- package/registry/blocks/trigger-picker/index.js +3 -0
- package/registry/blocks/trigger-picker/trigger-picker.jsx +374 -0
- package/registry/blocks/whiteboard/README.md +20 -14
- package/registry/blocks/workflow-view/README.md +123 -0
- package/registry/blocks/workflow-view/block.json +10 -0
- package/registry/blocks/workflow-view/demo.jsx +90 -0
- package/registry/blocks/workflow-view/index.js +3 -0
- package/registry/blocks/workflow-view/workflow-view.jsx +203 -0
- package/src/cli.mjs +2 -2
- package/src/install.mjs +100 -22
- package/src/registry.mjs +37 -3
- package/templates/next/browser/lib/data.js +6 -1
- package/templates/next/files/README.md +909 -26
- package/templates/next/files/app/shell.jsx +54 -37
- package/templates/next/files/check/automation-format.ts +2103 -0
- package/templates/next/files/check/automation-package.ts +142 -0
- package/templates/next/files/check/run.mjs +1043 -12
- package/templates/next/files/components/ui/field.jsx +17 -4
- package/templates/next/files/components/ui/icon.jsx +13 -0
- package/templates/next/files/components/ui/index.js +3 -1
- package/templates/next/files/components/ui/layout.jsx +382 -0
- package/templates/next/files/components/ui/notice.jsx +70 -0
- package/templates/next/files/components/ui/page.jsx +124 -0
- package/templates/next/files/components/ui/stat.jsx +22 -7
- package/templates/next/files/components/ui/toast.jsx +6 -3
- package/templates/next/files/lib/automations.js +660 -0
- package/templates/next/files/lib/claw.js +244 -5
- package/templates/next/files/lib/door.js +92 -0
- package/templates/next/files/lib/hooks.js +1694 -6
- package/templates/next/files/lib/platform.js +611 -14
- package/templates/next/template.json +22 -3
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# ChatPane
|
|
2
|
+
|
|
3
|
+
The desk's chat pane, as the app's own, in the app's theme (0.8.1). The
|
|
4
|
+
founder, after reviewing the app agent's message box next to the
|
|
5
|
+
Automations window's: "add claw's ChatPane as a block in the makersclaw
|
|
6
|
+
template". This is that port: claw's `web/src/os/windows/artifact/ChatPane`
|
|
7
|
+
and the composer of its `EveChat`, in JSX, with the kit's `Icon` and
|
|
8
|
+
`Button` and the app's tokens, talking to the agent through the app's own
|
|
9
|
+
hook (`useAgentPane`, `lib/hooks.js`) instead of `eve/client`.
|
|
10
|
+
|
|
11
|
+
It is the **chrome around a conversation**:
|
|
12
|
+
|
|
13
|
+
- **The head**: the title, **New chat**, **Chats** (the past chats drop over
|
|
14
|
+
the conversation, newest first, with a dot on one that needs you or is
|
|
15
|
+
working), and a close.
|
|
16
|
+
- **The empty state**: before the first ask, one line, the recent chats
|
|
17
|
+
("No chats yet. Your first message starts one." when there are none), and
|
|
18
|
+
the app's suggestions.
|
|
19
|
+
- **The conversation**: its lines are the children. It stays at the newest
|
|
20
|
+
words until the member scrolls up, then offers a jump to latest.
|
|
21
|
+
- **The bar** between the conversation and the box: what waits on the member,
|
|
22
|
+
or that the agent is working.
|
|
23
|
+
- **The message box**: the desk's composer. **One rounded box.** The words
|
|
24
|
+
on top, full width, growing with what is typed. Under them a row: **+**
|
|
25
|
+
(attach) at the left, and at the right the **microphone** and a **round
|
|
26
|
+
arrow** to send. **While a turn runs, a round stop takes the arrow's
|
|
27
|
+
place** (once the member types, the arrow comes back: a message sent then
|
|
28
|
+
waits its turn, as before). A quiet `hint` can sit under the box.
|
|
29
|
+
- **Files** by the + button, by paste and by a drop anywhere on the pane (up
|
|
30
|
+
to 4 files and 3 MB together); **dictation** where the browser has speech
|
|
31
|
+
recognition; the box **closed** while something waits on the member, with
|
|
32
|
+
the reason in its placeholder.
|
|
33
|
+
- **A starting line** from the dock: `openAgent({ ask })` (blocks/agent-dock).
|
|
34
|
+
The pane sends it as the member's first words in a new chat, or puts it
|
|
35
|
+
in the box. See "Have the agent do it" below.
|
|
36
|
+
|
|
37
|
+
```jsx
|
|
38
|
+
import { ChatPane } from "../components/blocks/chat-pane"; // a Makersclaw app ("./blocks/chat-pane" from components/)
|
|
39
|
+
import { ChatPane } from "@/components/makersclaw/blocks/chat-pane"; // a shadcn project
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
A client component. It needs the kit and nothing else; it is not tied to the
|
|
43
|
+
agent's lines. **For the agent's conversation use `agent-pane`**, which
|
|
44
|
+
draws the plan, the work folded into "Worked through N steps", the
|
|
45
|
+
questions, the yes and what the agent made, inside this pane. Use `ChatPane`
|
|
46
|
+
itself for lines of your own in the desk's frame.
|
|
47
|
+
|
|
48
|
+
## What was ported, and what was not
|
|
49
|
+
|
|
50
|
+
Ported: the head (New chat, Chats), the past chats list and its marks, the
|
|
51
|
+
empty state, the scroll that follows, the composer (its layout, the
|
|
52
|
+
attachments, the dictation, the send and stop), the closed box, and the
|
|
53
|
+
hand-off seam in a form the app needs (the starting line).
|
|
54
|
+
|
|
55
|
+
Not ported, because the app has no such thing: the desk's **rail** beside the
|
|
56
|
+
chat (an app's agent lives in the dock, a column; the Chats overlay is the
|
|
57
|
+
column mode's), the **binding** (headers, scope, the session pointer: the
|
|
58
|
+
app's `useAgentPane` holds those), **`openSession`/`pendingLine`** (the desk's
|
|
59
|
+
hand-off of a request into the agent's chat; the app's starting line does
|
|
60
|
+
that job), **`chatMarks` "In Makersclaw"** (an ask made in Makersclaw shows
|
|
61
|
+
as a pointer in the conversation instead) and the wallet gate (the platform
|
|
62
|
+
refuses a turn with no credits and says so).
|
|
63
|
+
|
|
64
|
+
## Props
|
|
65
|
+
|
|
66
|
+
- `chat` — what `useAgentPane()` returns. Required. The pane calls its
|
|
67
|
+
`send(text, { files })`, `stop()`, `newChat()` and `openSession(id)` and
|
|
68
|
+
reads `sessionId`, `request`, `chats`, `view`/`turns`/`pending`/`state`
|
|
69
|
+
(to follow the newest words), `sameConversation`, `sending`, `loading`,
|
|
70
|
+
`error`, `notice`, `unavailable`.
|
|
71
|
+
- `title` — the head's words, and the box's label. Default `"Ask the agent"`.
|
|
72
|
+
- `suggestions` — asks that fit the moment, as chips before the first ask.
|
|
73
|
+
- `empty` — one line before the first ask.
|
|
74
|
+
- `placeholder` — the box's words before the first ask. Once there is a
|
|
75
|
+
conversation it says "Ask a follow-up…" unless `chat.sameConversation` is
|
|
76
|
+
false.
|
|
77
|
+
- `closedWords` — the box's words while `blocked` ("Answer above first").
|
|
78
|
+
- `hint` — a quiet line under the box ("Enter sends · Shift+Enter is a new
|
|
79
|
+
line").
|
|
80
|
+
- `recent` — past chats listed before the first ask. Default `5`; `0` none.
|
|
81
|
+
- `bare` — no head, inside a `Sheet` or a `Card` that has its own.
|
|
82
|
+
- `fill` — default `true`: the pane takes its container's height and the
|
|
83
|
+
conversation scrolls inside it. `false`: compact, a capped height.
|
|
84
|
+
- `onClose` — a close in the head. Inside an `agent-dock` it is the dock's.
|
|
85
|
+
- `started` — there is a conversation to draw (else the empty state).
|
|
86
|
+
- `busy` — a turn runs: the arrow is a stop while the box is empty.
|
|
87
|
+
- `blocked` — something waits on the member: the whole box is closed.
|
|
88
|
+
- `bar` — a node between the conversation and the box.
|
|
89
|
+
- `children` — the conversation's lines.
|
|
90
|
+
- `className` — layout around it. Never colour.
|
|
91
|
+
|
|
92
|
+
`ChatComposer` is also exported, for a box of your own around the same
|
|
93
|
+
frame: `value`, `onChange`, `onSubmit`, `onStop`, `onAttach`, `files`,
|
|
94
|
+
`onRemoveFile`, `busy`, `canStop`, `stopping`, `blocked`, `sending`,
|
|
95
|
+
`encoding`, `dictation` (`useDictation()`), `placeholder`, `label`, `hint`,
|
|
96
|
+
`textareaRef`, `fileInputRef`. `ChatsList` draws the past chats.
|
|
97
|
+
|
|
98
|
+
## Have the agent do it
|
|
99
|
+
|
|
100
|
+
A button that says the agent will do the work does not open a form. It calls
|
|
101
|
+
`openAgent("Draft a project plan for me")` (blocks/agent-dock). The dock
|
|
102
|
+
opens, tells this pane the line through `ChatDockContext`, and the pane:
|
|
103
|
+
|
|
104
|
+
1. starts a new chat (if one is open; `fresh: false` carries on in it),
|
|
105
|
+
2. sends the line as the member's first words, once,
|
|
106
|
+
3. and tells the dock it took it.
|
|
107
|
+
|
|
108
|
+
With `send: false` it puts the line in the box instead, with the cursor at
|
|
109
|
+
its end (words the member already typed stay, and the line goes under them). A closed box (something waits on the member) always takes the line
|
|
110
|
+
as a prefill. The pure rule is `startStep(...)` in `chat-model.js`.
|
|
111
|
+
|
|
112
|
+
## The model (plain functions)
|
|
113
|
+
|
|
114
|
+
`ago(iso)`, `startStep({ start, handled, sessionId, blocked, canNew, newed })`,
|
|
115
|
+
and the composer's parts: `addFiles`, `filesFromTransfer`, `readFile`,
|
|
116
|
+
`formatBytes`, `MAX_FILES`, `MAX_BYTES`, `useDictation`.
|
|
117
|
+
|
|
118
|
+
## Example
|
|
119
|
+
|
|
120
|
+
```jsx
|
|
121
|
+
"use client";
|
|
122
|
+
import { useAgentPane } from "../lib/hooks";
|
|
123
|
+
import { ChatPane } from "../components/blocks/chat-pane";
|
|
124
|
+
|
|
125
|
+
export default function Notes() {
|
|
126
|
+
const chat = useAgentPane({ key: "notes" });
|
|
127
|
+
const messages = chat.view?.messages ?? [];
|
|
128
|
+
return (
|
|
129
|
+
<ChatPane
|
|
130
|
+
chat={chat}
|
|
131
|
+
title="Ask about your notes"
|
|
132
|
+
suggestions={["Summarise this week"]}
|
|
133
|
+
hint="Enter sends · Shift+Enter is a new line"
|
|
134
|
+
started={messages.length > 0}
|
|
135
|
+
busy={chat.state === "working"}
|
|
136
|
+
>
|
|
137
|
+
{messages.map((m) => (
|
|
138
|
+
<p key={m.id}>{m.text}</p>
|
|
139
|
+
))}
|
|
140
|
+
</ChatPane>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
```
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "chat-pane",
|
|
3
|
+
"title": "ChatPane",
|
|
4
|
+
"description": "The desk's chat pane as the app's own, in the app's theme: the chrome around a conversation. A head (title, New chat, Chats, close) with the past chats dropping over the chat; before the first ask one line, the recent chats and the suggestions; the conversation, kept at the newest words until the member scrolls up, with a jump to latest; a bar for what waits on the member; and the desk's composer: ONE rounded box with the words on top and under them a row, + (attach) at the left and the microphone and a round arrow to send at the right, the arrow a stop while a turn runs, a quiet hint under the box. Files by + or paste or drop, dictation, the box closed while something waits. A starting line handed down by the dock (openAgent) is sent as the member's first words in a fresh chat, or put in the box. It draws what useAgentPane() returns (lib/hooks.js) and decides nothing about the lines of the conversation: those are its children (agent-pane draws the agent's).",
|
|
5
|
+
"useWhen": "Any app chat with the agent: the pane inside an agent-dock, a compact card beside a record, a sheet. Use agent-pane when the conversation is the agent's (it puts the plan, the work, the questions and the yes inside this pane); use chat-pane itself to draw your own lines in the desk's frame, or just the composer (ChatComposer). Not for a chat between people (chat) or an agent run that is commissioned from a verb and watched (job-panel).",
|
|
6
|
+
"tags": [
|
|
7
|
+
"chat",
|
|
8
|
+
"composer",
|
|
9
|
+
"message box",
|
|
10
|
+
"ask",
|
|
11
|
+
"assistant",
|
|
12
|
+
"conversation",
|
|
13
|
+
"new chat",
|
|
14
|
+
"chats",
|
|
15
|
+
"send",
|
|
16
|
+
"stop",
|
|
17
|
+
"attach",
|
|
18
|
+
"dictate",
|
|
19
|
+
"copilot",
|
|
20
|
+
"sidebar"
|
|
21
|
+
],
|
|
22
|
+
"exports": [
|
|
23
|
+
"ChatPane",
|
|
24
|
+
"ChatComposer",
|
|
25
|
+
"ChatsList",
|
|
26
|
+
"ChatDockContext",
|
|
27
|
+
"ago",
|
|
28
|
+
"startStep",
|
|
29
|
+
"MAX_BYTES",
|
|
30
|
+
"MAX_FILES",
|
|
31
|
+
"addFiles",
|
|
32
|
+
"filesFromTransfer",
|
|
33
|
+
"formatBytes",
|
|
34
|
+
"readFile",
|
|
35
|
+
"useDictation"
|
|
36
|
+
],
|
|
37
|
+
"files": [
|
|
38
|
+
"index.js",
|
|
39
|
+
"chat-pane.jsx",
|
|
40
|
+
"chat-composer.jsx",
|
|
41
|
+
"chats-list.jsx",
|
|
42
|
+
"chat-context.js",
|
|
43
|
+
"chat-model.js",
|
|
44
|
+
"chat-attach.js"
|
|
45
|
+
],
|
|
46
|
+
"requires": [],
|
|
47
|
+
"offer": { "since": "0.8.1", "why": "the desk's chat pane and message box as the app's own, for a chat of the app's own lines (agent-pane already lays it)" }
|
|
48
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
4
|
+
|
|
5
|
+
// The composer's parts (0.8.1, D27 phase 4): files to send with the words,
|
|
6
|
+
// and dictation. Both are the browser's own — nothing here reads a door. The
|
|
7
|
+
// platform checks the files again (agent-app app-attachments.ts) and the
|
|
8
|
+
// limits below are its. They lived in agent-pane/composer.jsx until the
|
|
9
|
+
// chat-pane block (X5); that file now re-exports them from here.
|
|
10
|
+
|
|
11
|
+
/** At most this many files, and this many bytes together, per message. */
|
|
12
|
+
export const MAX_FILES = 4;
|
|
13
|
+
export const MAX_BYTES = 3 * 1024 * 1024;
|
|
14
|
+
|
|
15
|
+
export function formatBytes(bytes) {
|
|
16
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
17
|
+
if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
|
|
18
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Add `incoming` files to `current`, within the limits.
|
|
23
|
+
* → { files, error } `error` is words for the member, or null
|
|
24
|
+
*/
|
|
25
|
+
export function addFiles(current, incoming) {
|
|
26
|
+
const files = [...current];
|
|
27
|
+
let error = null;
|
|
28
|
+
let total = files.reduce((n, f) => n + f.size, 0);
|
|
29
|
+
for (const file of incoming) {
|
|
30
|
+
if (files.length >= MAX_FILES) {
|
|
31
|
+
error = `Send at most ${MAX_FILES} files at a time.`;
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
if (file.size === 0) {
|
|
35
|
+
error = `${file.name} is empty.`;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (total + file.size > MAX_BYTES) {
|
|
39
|
+
error = `Files can be up to ${formatBytes(MAX_BYTES)} together here. Attach a smaller one, or attach it in the Makersclaw window.`;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
total += file.size;
|
|
43
|
+
files.push(file);
|
|
44
|
+
}
|
|
45
|
+
return { files, error };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** A File as the ask takes it: { name, type, data } with a base64 data URL. */
|
|
49
|
+
export function readFile(file) {
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
const reader = new FileReader();
|
|
52
|
+
reader.onload = () => resolve({ name: file.name, type: file.type || "application/octet-stream", data: String(reader.result) });
|
|
53
|
+
reader.onerror = () => reject(reader.error ?? new Error("read failed"));
|
|
54
|
+
reader.readAsDataURL(file);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Files off a paste or a drop. A pasted image is named "image.png" by the
|
|
59
|
+
* browser; stamp it so several stay apart in the thread. */
|
|
60
|
+
export function filesFromTransfer(dt) {
|
|
61
|
+
if (!dt) return [];
|
|
62
|
+
const out = [];
|
|
63
|
+
for (const item of Array.from(dt.items ?? [])) {
|
|
64
|
+
if (item.kind !== "file") continue;
|
|
65
|
+
const file = item.getAsFile();
|
|
66
|
+
if (!file) continue;
|
|
67
|
+
out.push(
|
|
68
|
+
file.name === "image.png" && file.type.startsWith("image/")
|
|
69
|
+
? new File([file], `pasted-${new Date().toISOString().replace(/[:.]/g, "-")}.png`, { type: file.type })
|
|
70
|
+
: file,
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function recognitionCtor() {
|
|
77
|
+
if (typeof window === "undefined") return null;
|
|
78
|
+
return window.SpeechRecognition ?? window.webkitSpeechRecognition ?? null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Dictation: the microphone to text through the browser's speech
|
|
83
|
+
* recognition (the desk's composer does the same). `onText(words)` hears
|
|
84
|
+
* each finished phrase. `supported` is false where the browser has none,
|
|
85
|
+
* and the pane draws no microphone. `error` says when the microphone is
|
|
86
|
+
* refused (a frame the desk has not allowed it in, or the member said no).
|
|
87
|
+
*/
|
|
88
|
+
export function useDictation(onText) {
|
|
89
|
+
const [supported] = useState(() => recognitionCtor() !== null);
|
|
90
|
+
const [listening, setListening] = useState(false);
|
|
91
|
+
const [interim, setInterim] = useState("");
|
|
92
|
+
const [error, setError] = useState(null);
|
|
93
|
+
const recRef = useRef(null);
|
|
94
|
+
const wantRef = useRef(false);
|
|
95
|
+
const onTextRef = useRef(onText);
|
|
96
|
+
useLayoutEffect(() => {
|
|
97
|
+
onTextRef.current = onText;
|
|
98
|
+
}, [onText]);
|
|
99
|
+
|
|
100
|
+
const stop = useCallback(() => {
|
|
101
|
+
wantRef.current = false;
|
|
102
|
+
recRef.current?.stop();
|
|
103
|
+
recRef.current = null;
|
|
104
|
+
setListening(false);
|
|
105
|
+
setInterim("");
|
|
106
|
+
}, []);
|
|
107
|
+
|
|
108
|
+
const start = useCallback(() => {
|
|
109
|
+
const Ctor = recognitionCtor();
|
|
110
|
+
if (!Ctor || recRef.current) return;
|
|
111
|
+
setError(null);
|
|
112
|
+
const rec = new Ctor();
|
|
113
|
+
rec.continuous = true;
|
|
114
|
+
rec.interimResults = true;
|
|
115
|
+
rec.lang = (typeof navigator !== "undefined" && navigator.language) || "en-US";
|
|
116
|
+
rec.onresult = (event) => {
|
|
117
|
+
let interimText = "";
|
|
118
|
+
for (let i = event.resultIndex; i < event.results.length; i += 1) {
|
|
119
|
+
const result = event.results[i];
|
|
120
|
+
const transcript = result[0]?.transcript ?? "";
|
|
121
|
+
if (result.isFinal) {
|
|
122
|
+
if (transcript.trim()) onTextRef.current(transcript.trim());
|
|
123
|
+
} else {
|
|
124
|
+
interimText += transcript;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
setInterim(interimText);
|
|
128
|
+
};
|
|
129
|
+
rec.onend = () => {
|
|
130
|
+
recRef.current = null;
|
|
131
|
+
setInterim("");
|
|
132
|
+
if (wantRef.current) {
|
|
133
|
+
// Silence ends a recognition; while the toggle is on, listen again.
|
|
134
|
+
try {
|
|
135
|
+
rec.start();
|
|
136
|
+
recRef.current = rec;
|
|
137
|
+
return;
|
|
138
|
+
} catch {
|
|
139
|
+
wantRef.current = false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
setListening(false);
|
|
143
|
+
};
|
|
144
|
+
rec.onerror = (event) => {
|
|
145
|
+
if (event.error === "not-allowed" || event.error === "service-not-allowed") {
|
|
146
|
+
wantRef.current = false;
|
|
147
|
+
setListening(false);
|
|
148
|
+
setError("The microphone is not allowed here. Allow it for this site, or type instead.");
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
wantRef.current = true;
|
|
152
|
+
try {
|
|
153
|
+
rec.start();
|
|
154
|
+
} catch {
|
|
155
|
+
wantRef.current = false;
|
|
156
|
+
setError("The microphone could not start.");
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
recRef.current = rec;
|
|
160
|
+
setListening(true);
|
|
161
|
+
}, []);
|
|
162
|
+
|
|
163
|
+
const toggle = useCallback(() => (listening ? stop() : start()), [listening, start, stop]);
|
|
164
|
+
useEffect(() => stop, [stop]);
|
|
165
|
+
return { supported, listening, interim, error, toggle, stop };
|
|
166
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect } from "react";
|
|
4
|
+
import { Icon, cx } from "../../ui";
|
|
5
|
+
import { filesFromTransfer, formatBytes } from "./chat-attach";
|
|
6
|
+
|
|
7
|
+
// ChatComposer — the desk's message box (claw's ChatComposer, as the app
|
|
8
|
+
// draws it): ONE rounded box. The words on top, full width, growing with
|
|
9
|
+
// what is typed; under them a row with + (attach) at the left, and at the
|
|
10
|
+
// right the microphone and a round arrow to send. While a turn runs, a round
|
|
11
|
+
// stop takes the arrow's place. A quiet hint can sit under the box.
|
|
12
|
+
//
|
|
13
|
+
// It draws the box and nothing else: the words, the files and the dictation
|
|
14
|
+
// are the pane's (so a drop anywhere on the pane, a prefilled line and a
|
|
15
|
+
// sent suggestion all reach the same box).
|
|
16
|
+
//
|
|
17
|
+
// value, onChange the words (controlled)
|
|
18
|
+
// onSubmit() Enter, or the arrow
|
|
19
|
+
// onStop() the stop (shown while `busy` and `canStop`)
|
|
20
|
+
// onAttach(File[]) the + and a paste
|
|
21
|
+
// files, onRemoveFile(i) what is attached, as chips inside the box
|
|
22
|
+
// busy a turn runs: the arrow is a stop while the box is empty
|
|
23
|
+
// canStop, stopping the stop can be pressed / has been
|
|
24
|
+
// blocked something waits on the member: the whole box is closed
|
|
25
|
+
// sending, encoding the arrow waits
|
|
26
|
+
// dictation useDictation()'s object (no microphone where the
|
|
27
|
+
// browser has none)
|
|
28
|
+
// placeholder, label the box's words, and its name for a screen reader
|
|
29
|
+
// hint a quiet line under the box
|
|
30
|
+
// textareaRef the pane's handle on the box (focus, caret)
|
|
31
|
+
|
|
32
|
+
const ROUND = "inline-flex size-8 shrink-0 items-center justify-center outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-card disabled:cursor-not-allowed disabled:opacity-40";
|
|
33
|
+
|
|
34
|
+
export function ChatComposer({
|
|
35
|
+
value,
|
|
36
|
+
onChange,
|
|
37
|
+
onSubmit,
|
|
38
|
+
onStop,
|
|
39
|
+
onAttach,
|
|
40
|
+
files = [],
|
|
41
|
+
onRemoveFile,
|
|
42
|
+
busy = false,
|
|
43
|
+
canStop = false,
|
|
44
|
+
stopping = false,
|
|
45
|
+
blocked = false,
|
|
46
|
+
sending = false,
|
|
47
|
+
encoding = false,
|
|
48
|
+
dictation,
|
|
49
|
+
placeholder,
|
|
50
|
+
label = "Message",
|
|
51
|
+
hint,
|
|
52
|
+
textareaRef,
|
|
53
|
+
fileInputRef,
|
|
54
|
+
className,
|
|
55
|
+
}) {
|
|
56
|
+
const empty = !value.trim() && files.length === 0;
|
|
57
|
+
const stopShown = busy && canStop && empty;
|
|
58
|
+
|
|
59
|
+
// The box grows with its words, up to its max-h; a hidden box measures 0
|
|
60
|
+
// and is left natural (pinning 0px would stick).
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
const el = textareaRef?.current;
|
|
63
|
+
if (!el) return;
|
|
64
|
+
el.style.height = "auto";
|
|
65
|
+
if (el.scrollHeight > 0) el.style.height = `${el.scrollHeight}px`;
|
|
66
|
+
}, [value, textareaRef]);
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<div className={cx("grid min-w-0 gap-1.5", className)}>
|
|
70
|
+
<form
|
|
71
|
+
data-chat-composer=""
|
|
72
|
+
className="grid min-w-0 gap-2 rounded-xl border border-border bg-card px-3 pb-2 pt-2.5 text-card-foreground shadow-sm transition-colors focus-within:border-ring motion-reduce:transition-none"
|
|
73
|
+
onSubmit={(e) => {
|
|
74
|
+
e.preventDefault();
|
|
75
|
+
onSubmit?.();
|
|
76
|
+
}}
|
|
77
|
+
>
|
|
78
|
+
{files.length > 0 ? (
|
|
79
|
+
<ul aria-label="Files to send" className="flex min-w-0 flex-wrap gap-1.5">
|
|
80
|
+
{files.map((f, i) => (
|
|
81
|
+
<li key={`${f.name}-${i}`} className="inline-flex max-w-full min-w-0 items-center gap-1.5 rounded-md border border-border py-0.5 pl-2 pr-1 text-xs">
|
|
82
|
+
<Icon name="paperclip" size={12} className="shrink-0 text-muted-foreground" />
|
|
83
|
+
<span className="min-w-0 truncate">{f.name}</span>
|
|
84
|
+
<span className="shrink-0 text-muted-foreground">{formatBytes(f.size)}</span>
|
|
85
|
+
<button
|
|
86
|
+
type="button"
|
|
87
|
+
aria-label={`Remove ${f.name}`}
|
|
88
|
+
title={`Remove ${f.name}`}
|
|
89
|
+
onClick={() => onRemoveFile?.(i)}
|
|
90
|
+
className="inline-flex size-5 shrink-0 items-center justify-center rounded-sm text-muted-foreground outline-none hover:bg-accent hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring"
|
|
91
|
+
>
|
|
92
|
+
<Icon name="x" size={12} />
|
|
93
|
+
</button>
|
|
94
|
+
</li>
|
|
95
|
+
))}
|
|
96
|
+
</ul>
|
|
97
|
+
) : null}
|
|
98
|
+
|
|
99
|
+
<textarea
|
|
100
|
+
ref={textareaRef}
|
|
101
|
+
aria-label={label}
|
|
102
|
+
rows={1}
|
|
103
|
+
placeholder={placeholder}
|
|
104
|
+
value={value}
|
|
105
|
+
disabled={blocked}
|
|
106
|
+
onChange={(e) => onChange?.(e.target.value)}
|
|
107
|
+
onPaste={(e) => {
|
|
108
|
+
const pasted = filesFromTransfer(e.clipboardData);
|
|
109
|
+
if (pasted.length > 0) {
|
|
110
|
+
e.preventDefault();
|
|
111
|
+
onAttach?.(pasted);
|
|
112
|
+
}
|
|
113
|
+
}}
|
|
114
|
+
onKeyDown={(e) => {
|
|
115
|
+
if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) {
|
|
116
|
+
e.preventDefault();
|
|
117
|
+
onSubmit?.();
|
|
118
|
+
}
|
|
119
|
+
}}
|
|
120
|
+
className="block max-h-40 min-h-6 w-full min-w-0 resize-none overflow-y-auto bg-transparent text-sm leading-6 text-foreground outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed"
|
|
121
|
+
/>
|
|
122
|
+
|
|
123
|
+
{dictation?.listening ? (
|
|
124
|
+
<p role="status" className="flex min-w-0 items-center gap-1.5 text-xs text-muted-foreground">
|
|
125
|
+
<span aria-hidden="true" className="size-1.5 shrink-0 animate-pulse rounded-full bg-destructive motion-reduce:animate-none" />
|
|
126
|
+
Listening…
|
|
127
|
+
{dictation.interim ? <span className="min-w-0 truncate italic">{dictation.interim}</span> : null}
|
|
128
|
+
</p>
|
|
129
|
+
) : null}
|
|
130
|
+
|
|
131
|
+
<div className="flex items-center gap-1.5">
|
|
132
|
+
<input
|
|
133
|
+
ref={fileInputRef}
|
|
134
|
+
type="file"
|
|
135
|
+
multiple
|
|
136
|
+
hidden
|
|
137
|
+
aria-hidden="true"
|
|
138
|
+
tabIndex={-1}
|
|
139
|
+
onChange={(e) => {
|
|
140
|
+
onAttach?.(Array.from(e.target.files ?? []));
|
|
141
|
+
e.target.value = "";
|
|
142
|
+
}}
|
|
143
|
+
/>
|
|
144
|
+
<button
|
|
145
|
+
type="button"
|
|
146
|
+
aria-label="Attach a file"
|
|
147
|
+
title="Attach a file"
|
|
148
|
+
disabled={blocked || encoding}
|
|
149
|
+
onClick={() => fileInputRef?.current?.click()}
|
|
150
|
+
className={cx(ROUND, "rounded-lg border border-border text-muted-foreground hover:bg-accent hover:text-foreground")}
|
|
151
|
+
>
|
|
152
|
+
<Icon name="plus" size={16} />
|
|
153
|
+
</button>
|
|
154
|
+
<span className="flex-1" />
|
|
155
|
+
{dictation?.supported ? (
|
|
156
|
+
<button
|
|
157
|
+
type="button"
|
|
158
|
+
aria-label={dictation.listening ? "Stop dictating" : "Dictate"}
|
|
159
|
+
title={dictation.listening ? "Stop dictating" : "Dictate"}
|
|
160
|
+
aria-pressed={dictation.listening}
|
|
161
|
+
disabled={blocked}
|
|
162
|
+
onClick={dictation.toggle}
|
|
163
|
+
className={cx(ROUND, "rounded-lg hover:bg-accent", dictation.listening ? "text-destructive" : "text-muted-foreground hover:text-foreground")}
|
|
164
|
+
>
|
|
165
|
+
<Icon name="mic" size={16} />
|
|
166
|
+
</button>
|
|
167
|
+
) : null}
|
|
168
|
+
{stopShown ? (
|
|
169
|
+
<button
|
|
170
|
+
type="button"
|
|
171
|
+
aria-label={stopping ? "Stopping…" : "Stop"}
|
|
172
|
+
title={stopping ? "Stopping…" : "Stop"}
|
|
173
|
+
disabled={stopping}
|
|
174
|
+
onClick={onStop}
|
|
175
|
+
className={cx(ROUND, "rounded-full bg-primary text-primary-foreground hover:opacity-90")}
|
|
176
|
+
>
|
|
177
|
+
<Icon name="stop" size={12} />
|
|
178
|
+
</button>
|
|
179
|
+
) : (
|
|
180
|
+
<button
|
|
181
|
+
type="submit"
|
|
182
|
+
aria-label="Send"
|
|
183
|
+
title="Send"
|
|
184
|
+
disabled={blocked || encoding || sending || empty}
|
|
185
|
+
className={cx(ROUND, "rounded-full bg-primary text-primary-foreground hover:opacity-90")}
|
|
186
|
+
>
|
|
187
|
+
<Icon name="arrow-up" size={16} />
|
|
188
|
+
</button>
|
|
189
|
+
)}
|
|
190
|
+
</div>
|
|
191
|
+
</form>
|
|
192
|
+
{hint ? <p className="px-1 text-xs text-muted-foreground">{hint}</p> : null}
|
|
193
|
+
</div>
|
|
194
|
+
);
|
|
195
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { createContext } from "react";
|
|
4
|
+
|
|
5
|
+
// What a ChatPane placed inside an AgentDock (blocks/agent-dock) is told:
|
|
6
|
+
//
|
|
7
|
+
// close folds the dock (the head's close, when the app gave no onClose)
|
|
8
|
+
// mode "column" or "sheet"
|
|
9
|
+
// start the starting line a "have the agent do it" button asked for
|
|
10
|
+
// (openAgent({ ask }), blocks/agent-dock), or null:
|
|
11
|
+
// { ask, send, fresh, nonce }
|
|
12
|
+
// taken (nonce) => void — the pane took it; the dock drops it
|
|
13
|
+
//
|
|
14
|
+
// Null outside a dock, where nothing changes. It lives here, beside the
|
|
15
|
+
// pane, so the pane never imports the dock; agent-pane re-exports it as
|
|
16
|
+
// AgentDockContext, which is the name the dock and apps have always used.
|
|
17
|
+
export const ChatDockContext = createContext(null);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// chat-pane: the rules that need no React, as plain functions (they work on
|
|
2
|
+
// the server too, and test/chat-pane.test.mjs holds them).
|
|
3
|
+
|
|
4
|
+
/** "now", "5m", "3h", "2d", else the date: a past chat's age. */
|
|
5
|
+
export function ago(iso, now = Date.now()) {
|
|
6
|
+
const t = iso ? Date.parse(iso) : Number.NaN;
|
|
7
|
+
if (!Number.isFinite(t)) return "";
|
|
8
|
+
const s = Math.max(0, Math.round((now - t) / 1000));
|
|
9
|
+
if (s < 60) return "now";
|
|
10
|
+
if (s < 3600) return `${Math.floor(s / 60)}m`;
|
|
11
|
+
if (s < 86400) return `${Math.floor(s / 3600)}h`;
|
|
12
|
+
if (s < 7 * 86400) return `${Math.floor(s / 86400)}d`;
|
|
13
|
+
return new Date(t).toLocaleDateString(undefined, { month: "short", day: "numeric" });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* What the pane does next with a starting line the dock holds (openAgent in
|
|
18
|
+
* blocks/agent-dock). A "have the agent do it" button does not open a form:
|
|
19
|
+
* it hands the agent its first line, and the agent asks only for what it is
|
|
20
|
+
* missing, in the chat.
|
|
21
|
+
*
|
|
22
|
+
* start { ask, send, fresh, nonce } or null
|
|
23
|
+
* handled the nonce this pane already took, or null
|
|
24
|
+
* sessionId the conversation open now (null: a fresh one)
|
|
25
|
+
* blocked the box is closed (something waits on the member)
|
|
26
|
+
* canNew the chat can start a new conversation (chat.newChat)
|
|
27
|
+
* newed newChat was already called for this nonce
|
|
28
|
+
*
|
|
29
|
+
* → "none" nothing to do
|
|
30
|
+
* "new-chat" start a fresh conversation first (fresh, and one is open)
|
|
31
|
+
* "wait" the fresh conversation has not replaced the old one yet
|
|
32
|
+
* "send" send the line as the member's first words
|
|
33
|
+
* "prefill" put the line in the box for the member to finish (send is
|
|
34
|
+
* false, or the box is closed and cannot send)
|
|
35
|
+
*/
|
|
36
|
+
export function startStep({ start, handled = null, sessionId = null, blocked = false, canNew = true, newed = false } = {}) {
|
|
37
|
+
if (!start || !String(start.ask ?? "").trim()) return "none";
|
|
38
|
+
if (handled !== null && handled === start.nonce) return "none";
|
|
39
|
+
if (start.send === false || blocked) return "prefill";
|
|
40
|
+
const fresh = start.fresh !== false && canNew;
|
|
41
|
+
if (fresh && sessionId) return newed ? "wait" : "new-chat";
|
|
42
|
+
return "send";
|
|
43
|
+
}
|