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,398 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useContext, useEffect, useRef, useState } from "react";
|
|
4
|
+
import { Button, Icon, IconButton, Spinner, cx } from "../../ui";
|
|
5
|
+
import { addFiles, filesFromTransfer, readFile, useDictation } from "./chat-attach";
|
|
6
|
+
import { ChatDockContext } from "./chat-context";
|
|
7
|
+
import { ChatComposer } from "./chat-composer";
|
|
8
|
+
import { ChatsList } from "./chats-list";
|
|
9
|
+
import { startStep } from "./chat-model";
|
|
10
|
+
|
|
11
|
+
// ChatPane — the desk's chat pane (claw's web/src/os/windows/artifact/
|
|
12
|
+
// ChatPane.tsx and the composer of its EveChat), as the app's own, in the
|
|
13
|
+
// app's theme (0.8.1, X5; the founder: "add claw's ChatPane as a block in
|
|
14
|
+
// the makersclaw template"). It is the chrome around a conversation:
|
|
15
|
+
//
|
|
16
|
+
// head the title, New chat, Chats (the list drops over the chat,
|
|
17
|
+
// the desk's column mode), close
|
|
18
|
+
// empty before the first ask: one line, the past chats, the
|
|
19
|
+
// suggestions
|
|
20
|
+
// scroll the conversation, which stays at the newest words until the
|
|
21
|
+
// member scrolls up, with a jump to latest
|
|
22
|
+
// bar what waits on the member, or says the agent is working
|
|
23
|
+
// composer ONE box: the words on top; under them + (attach) at the
|
|
24
|
+
// left, the microphone and a round arrow at the right, the
|
|
25
|
+
// arrow a stop while a turn runs; a quiet hint under it
|
|
26
|
+
//
|
|
27
|
+
// It is not the conversation's lines: those are the children. agent-pane
|
|
28
|
+
// draws the agent's (the plan, the work folded, the questions, what it
|
|
29
|
+
// made); any lines will do. Nothing here asks the agent anything: `chat`
|
|
30
|
+
// is an object of the shape useAgentPane() returns (lib/hooks.js), and the
|
|
31
|
+
// pane calls its send, stop, newChat and openSession.
|
|
32
|
+
//
|
|
33
|
+
// A "have the agent do it" button reaches the pane through the dock:
|
|
34
|
+
// openAgent({ ask }) (blocks/agent-dock) hands the pane a starting line,
|
|
35
|
+
// which it sends as the member's first words in a fresh chat (or puts in
|
|
36
|
+
// the box). The agent asks only for what it is missing, in the chat.
|
|
37
|
+
//
|
|
38
|
+
// chat the object useAgentPane() returns (required)
|
|
39
|
+
// title the head's words, and the box's label (default "Ask the agent")
|
|
40
|
+
// suggestions asks that fit the moment, before the first ask
|
|
41
|
+
// empty one line before the first ask
|
|
42
|
+
// placeholder the box's words before the first ask; once there is a
|
|
43
|
+
// conversation it says "Ask a follow-up…" (D29) unless
|
|
44
|
+
// `chat.sameConversation` is false (the next message starts a
|
|
45
|
+
// new request, and the box keeps the app's own words)
|
|
46
|
+
// closedWords the box's words while `blocked` ("Answer above first")
|
|
47
|
+
// hint a quiet line under the box
|
|
48
|
+
// recent past chats listed before the first ask (default 5; 0 none)
|
|
49
|
+
// bare no head (inside a Sheet or a Card that has its own)
|
|
50
|
+
// fill take the container's height (default); false: compact,
|
|
51
|
+
// the conversation scrolls inside a capped height
|
|
52
|
+
// onClose a close in the head; inside an AgentDock it is the dock's
|
|
53
|
+
// started there is a conversation to draw (else the empty state)
|
|
54
|
+
// busy a turn runs: the arrow is a stop while the box is empty
|
|
55
|
+
// blocked something waits on the member: the whole box is closed
|
|
56
|
+
// bar a node between the conversation and the box
|
|
57
|
+
// children the conversation's lines
|
|
58
|
+
// className layout around it. Never colour.
|
|
59
|
+
export function ChatPane({
|
|
60
|
+
chat,
|
|
61
|
+
title = "Ask the agent",
|
|
62
|
+
suggestions = [],
|
|
63
|
+
empty,
|
|
64
|
+
placeholder,
|
|
65
|
+
closedWords = "Not available right now",
|
|
66
|
+
hint,
|
|
67
|
+
recent = 5,
|
|
68
|
+
bare = false,
|
|
69
|
+
fill = true,
|
|
70
|
+
onClose: onCloseProp,
|
|
71
|
+
started = false,
|
|
72
|
+
busy = false,
|
|
73
|
+
blocked = false,
|
|
74
|
+
bar,
|
|
75
|
+
className,
|
|
76
|
+
children,
|
|
77
|
+
}) {
|
|
78
|
+
const dock = useContext(ChatDockContext);
|
|
79
|
+
const onClose = onCloseProp ?? dock?.close;
|
|
80
|
+
const [text, setText] = useState("");
|
|
81
|
+
const [listOpen, setListOpen] = useState(false);
|
|
82
|
+
const [stopping, setStopping] = useState(false);
|
|
83
|
+
const [atBottom, setAtBottom] = useState(true);
|
|
84
|
+
const [files, setFiles] = useState([]);
|
|
85
|
+
const [attachError, setAttachError] = useState(null);
|
|
86
|
+
const [encoding, setEncoding] = useState(false);
|
|
87
|
+
const [dragOver, setDragOver] = useState(false);
|
|
88
|
+
const fileInput = useRef(null);
|
|
89
|
+
const textarea = useRef(null);
|
|
90
|
+
const dragDepth = useRef(0);
|
|
91
|
+
const listRef = useRef(null);
|
|
92
|
+
const rootRef = useRef(null);
|
|
93
|
+
const stick = useRef(true);
|
|
94
|
+
const chatRef = useRef(chat);
|
|
95
|
+
chatRef.current = chat;
|
|
96
|
+
|
|
97
|
+
const chats = chat?.chats && !chat.chats.unavailable ? chat.chats : null;
|
|
98
|
+
const canNew = typeof chat?.newChat === "function";
|
|
99
|
+
const canStop = busy && !blocked && Boolean(chat?.request?.id) && Boolean(chat?.stop);
|
|
100
|
+
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (!busy) setStopping(false);
|
|
103
|
+
}, [busy]);
|
|
104
|
+
|
|
105
|
+
// Dictation adds finished phrases to the box.
|
|
106
|
+
const dictation = useDictation((words) => setText((t) => (t.trim() ? `${t.trimEnd()} ${words}` : words)));
|
|
107
|
+
|
|
108
|
+
const attach = (incoming) => {
|
|
109
|
+
const r = addFiles(files, incoming);
|
|
110
|
+
setFiles(r.files);
|
|
111
|
+
setAttachError(r.error);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// The Chats list closes on a press outside it and on Escape.
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
if (!listOpen) return undefined;
|
|
117
|
+
const onDown = (e) => {
|
|
118
|
+
if (!rootRef.current?.querySelector("[data-chats]")?.contains(e.target) && !e.target.closest?.("[data-chats-toggle]")) setListOpen(false);
|
|
119
|
+
};
|
|
120
|
+
const onKey = (e) => {
|
|
121
|
+
if (e.key === "Escape") setListOpen(false);
|
|
122
|
+
};
|
|
123
|
+
document.addEventListener("mousedown", onDown);
|
|
124
|
+
document.addEventListener("keydown", onKey);
|
|
125
|
+
return () => {
|
|
126
|
+
document.removeEventListener("mousedown", onDown);
|
|
127
|
+
document.removeEventListener("keydown", onKey);
|
|
128
|
+
};
|
|
129
|
+
}, [listOpen]);
|
|
130
|
+
|
|
131
|
+
// The newest words stay in view while the agent writes — until the
|
|
132
|
+
// member scrolls up to read, which stops it following.
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
const el = listRef.current;
|
|
135
|
+
if (el && stick.current) el.scrollTop = el.scrollHeight;
|
|
136
|
+
}, [chat?.view, chat?.turns, chat?.pending, chat?.state, chat?.needs?.at, started]);
|
|
137
|
+
const onScroll = () => {
|
|
138
|
+
const el = listRef.current;
|
|
139
|
+
if (!el) return;
|
|
140
|
+
const near = el.scrollHeight - el.scrollTop - el.clientHeight < 48;
|
|
141
|
+
stick.current = near;
|
|
142
|
+
setAtBottom(near);
|
|
143
|
+
};
|
|
144
|
+
const jump = () => {
|
|
145
|
+
const el = listRef.current;
|
|
146
|
+
if (el) el.scrollTo({ top: el.scrollHeight, behavior: "smooth" });
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const submit = async (words) => {
|
|
150
|
+
const typed = String(words ?? text).trim();
|
|
151
|
+
const sending = words === undefined ? files : [];
|
|
152
|
+
// Files without words still go: the agent is told what they are.
|
|
153
|
+
const t = typed || (sending.length ? `Attached: ${sending.map((f) => f.name).join(", ")}` : "");
|
|
154
|
+
if (!t || !chat?.send || encoding) return;
|
|
155
|
+
dictation.stop();
|
|
156
|
+
stick.current = true;
|
|
157
|
+
if (sending.length === 0) {
|
|
158
|
+
setText("");
|
|
159
|
+
chat.send(t);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
setEncoding(true);
|
|
163
|
+
try {
|
|
164
|
+
const read = await Promise.all(sending.map(readFile));
|
|
165
|
+
setText("");
|
|
166
|
+
setFiles([]);
|
|
167
|
+
setAttachError(null);
|
|
168
|
+
chat.send(t, { files: read });
|
|
169
|
+
} catch {
|
|
170
|
+
setAttachError("A file could not be read. Try attaching it again.");
|
|
171
|
+
} finally {
|
|
172
|
+
setEncoding(false);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
const stop = () => {
|
|
176
|
+
if (stopping || !chat?.stop) return;
|
|
177
|
+
setStopping(true);
|
|
178
|
+
chat.stop();
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
// A starting line from the dock (openAgent). Fresh: a new conversation
|
|
182
|
+
// first, then the line sent as its first words, so the agent asks only
|
|
183
|
+
// for what it is missing. Or prefilled for the member to finish. The pane
|
|
184
|
+
// tells the dock it took it; a pane that has not mounted yet takes it when
|
|
185
|
+
// it does.
|
|
186
|
+
const start = dock?.start ?? null;
|
|
187
|
+
const takenRef = useRef(null);
|
|
188
|
+
const newedRef = useRef(null);
|
|
189
|
+
useEffect(() => {
|
|
190
|
+
if (!start) return;
|
|
191
|
+
const step = startStep({
|
|
192
|
+
start,
|
|
193
|
+
handled: takenRef.current,
|
|
194
|
+
sessionId: chat?.sessionId ?? null,
|
|
195
|
+
blocked,
|
|
196
|
+
canNew,
|
|
197
|
+
newed: newedRef.current === start.nonce,
|
|
198
|
+
});
|
|
199
|
+
if (step === "none" || step === "wait") return;
|
|
200
|
+
if (step === "new-chat") {
|
|
201
|
+
newedRef.current = start.nonce;
|
|
202
|
+
setListOpen(false);
|
|
203
|
+
chatRef.current?.newChat?.();
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
takenRef.current = start.nonce;
|
|
207
|
+
if (step === "send") {
|
|
208
|
+
stick.current = true;
|
|
209
|
+
chatRef.current?.send?.(String(start.ask).trim());
|
|
210
|
+
} else {
|
|
211
|
+
// Words already in the box are the member's: the line goes under them.
|
|
212
|
+
setText((t) => (t.trim() ? `${t.trimEnd()}\n${start.ask}` : String(start.ask)));
|
|
213
|
+
requestAnimationFrame(() => {
|
|
214
|
+
const el = textarea.current;
|
|
215
|
+
if (!el || el.disabled) return;
|
|
216
|
+
el.focus({ preventScroll: true });
|
|
217
|
+
el.setSelectionRange(el.value.length, el.value.length);
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
dock?.taken?.(start.nonce);
|
|
221
|
+
}, [start, chat?.sessionId, blocked, canNew, dock]);
|
|
222
|
+
|
|
223
|
+
const followUp = started && chat?.sameConversation !== false;
|
|
224
|
+
const words = blocked ? closedWords : followUp ? "Ask a follow-up…" : (placeholder ?? "Ask anything…");
|
|
225
|
+
|
|
226
|
+
return (
|
|
227
|
+
<div
|
|
228
|
+
ref={rootRef}
|
|
229
|
+
className={cx("@container relative flex min-w-0 flex-col bg-background text-sm text-foreground", fill && "h-full min-h-0", className)}
|
|
230
|
+
onDragEnter={(e) => {
|
|
231
|
+
if (!e.dataTransfer?.types?.includes("Files") || blocked) return;
|
|
232
|
+
e.preventDefault();
|
|
233
|
+
dragDepth.current += 1;
|
|
234
|
+
setDragOver(true);
|
|
235
|
+
}}
|
|
236
|
+
onDragLeave={(e) => {
|
|
237
|
+
if (!e.dataTransfer?.types?.includes("Files")) return;
|
|
238
|
+
dragDepth.current = Math.max(0, dragDepth.current - 1);
|
|
239
|
+
if (dragDepth.current === 0) setDragOver(false);
|
|
240
|
+
}}
|
|
241
|
+
onDragOver={(e) => {
|
|
242
|
+
if (e.dataTransfer?.types?.includes("Files")) e.preventDefault();
|
|
243
|
+
}}
|
|
244
|
+
onDrop={(e) => {
|
|
245
|
+
if (!e.dataTransfer?.types?.includes("Files")) return;
|
|
246
|
+
e.preventDefault();
|
|
247
|
+
dragDepth.current = 0;
|
|
248
|
+
setDragOver(false);
|
|
249
|
+
if (!blocked) attach(filesFromTransfer(e.dataTransfer));
|
|
250
|
+
}}
|
|
251
|
+
>
|
|
252
|
+
{dragOver ? (
|
|
253
|
+
<div className="pointer-events-none absolute inset-0 z-20 grid place-items-center rounded-[var(--radius)] border-2 border-dashed border-primary bg-background/80 text-sm font-medium">
|
|
254
|
+
Drop files to attach
|
|
255
|
+
</div>
|
|
256
|
+
) : null}
|
|
257
|
+
{!bare ? (
|
|
258
|
+
<div className="flex h-10 shrink-0 items-center gap-1 border-b border-border pl-3.5 pr-2 font-semibold">
|
|
259
|
+
<Icon name="sparkles" size={16} className="shrink-0 text-primary" />
|
|
260
|
+
<span className="min-w-0 flex-1 truncate">{title}</span>
|
|
261
|
+
{canNew ? (
|
|
262
|
+
<IconButton
|
|
263
|
+
icon="plus"
|
|
264
|
+
size="sm"
|
|
265
|
+
label="New chat"
|
|
266
|
+
onClick={() => {
|
|
267
|
+
setListOpen(false);
|
|
268
|
+
chat.newChat();
|
|
269
|
+
}}
|
|
270
|
+
/>
|
|
271
|
+
) : null}
|
|
272
|
+
{chats ? (
|
|
273
|
+
<Button size="sm" variant="quiet" data-chats-toggle="" aria-expanded={listOpen} aria-label="Chats" onClick={() => setListOpen((v) => !v)}>
|
|
274
|
+
<Icon name="history" size={14} />
|
|
275
|
+
<span className="hidden @[16rem]:inline">Chats</span>
|
|
276
|
+
{chats.items.length > 0 ? <span className="text-xs text-muted-foreground">{chats.items.length}</span> : null}
|
|
277
|
+
</Button>
|
|
278
|
+
) : null}
|
|
279
|
+
{onClose ? <IconButton icon="x" size="sm" label="Close" onClick={onClose} /> : null}
|
|
280
|
+
</div>
|
|
281
|
+
) : null}
|
|
282
|
+
|
|
283
|
+
{listOpen && chats ? (
|
|
284
|
+
<div data-chats="" className="absolute inset-x-0 z-10 max-h-72 overflow-y-auto border-b border-border bg-popover text-popover-foreground shadow-md" style={{ top: bare ? 0 : "2.5rem" }}>
|
|
285
|
+
<ChatsList
|
|
286
|
+
chats={chats}
|
|
287
|
+
currentId={chat?.sessionId}
|
|
288
|
+
onPick={(id) => {
|
|
289
|
+
setListOpen(false);
|
|
290
|
+
chat.openSession?.(id);
|
|
291
|
+
}}
|
|
292
|
+
/>
|
|
293
|
+
</div>
|
|
294
|
+
) : null}
|
|
295
|
+
|
|
296
|
+
<div className={cx("relative", fill ? "min-h-0 flex-1" : "")}>
|
|
297
|
+
<div
|
|
298
|
+
ref={listRef}
|
|
299
|
+
onScroll={onScroll}
|
|
300
|
+
role="log"
|
|
301
|
+
aria-live="polite"
|
|
302
|
+
aria-label={title}
|
|
303
|
+
className={cx("grid min-w-0 content-start gap-3 overflow-y-auto overscroll-contain p-3.5", fill ? "h-full min-h-0" : started ? "max-h-96" : "")}
|
|
304
|
+
>
|
|
305
|
+
{started ? (
|
|
306
|
+
children
|
|
307
|
+
) : (
|
|
308
|
+
<>
|
|
309
|
+
{empty ? <p className="text-muted-foreground">{empty}</p> : null}
|
|
310
|
+
{chat?.loading ? (
|
|
311
|
+
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
|
312
|
+
<Spinner size="sm" label="Loading" /> Opening the chat…
|
|
313
|
+
</div>
|
|
314
|
+
) : null}
|
|
315
|
+
{chats && recent > 0 && !chat?.loading ? (
|
|
316
|
+
<section aria-label="Recent chats" className="grid min-w-0 gap-0.5">
|
|
317
|
+
<h3 className="text-xs font-medium text-muted-foreground">Recent chats</h3>
|
|
318
|
+
<div className="-mx-3.5">
|
|
319
|
+
<ChatsList chats={{ ...chats, items: chats.items.slice(0, recent) }} currentId={chat?.sessionId} onPick={(id) => chat.openSession?.(id)} />
|
|
320
|
+
</div>
|
|
321
|
+
{chats.items.length > recent ? (
|
|
322
|
+
<Button variant="link" size="inherit" className="justify-self-start text-xs" onClick={() => setListOpen(true)}>
|
|
323
|
+
All {chats.items.length} chats
|
|
324
|
+
</Button>
|
|
325
|
+
) : null}
|
|
326
|
+
</section>
|
|
327
|
+
) : null}
|
|
328
|
+
{suggestions.length ? (
|
|
329
|
+
<div className="flex min-w-0 flex-wrap gap-1.5">
|
|
330
|
+
{suggestions.map((s) => (
|
|
331
|
+
<button
|
|
332
|
+
key={s}
|
|
333
|
+
type="button"
|
|
334
|
+
onClick={() => submit(s)}
|
|
335
|
+
className="flex max-w-full min-w-0 items-start gap-1.5 rounded-[var(--radius)] border border-border px-2.5 py-1 text-left text-xs hover:bg-muted focus-visible:outline-2 focus-visible:outline-ring"
|
|
336
|
+
>
|
|
337
|
+
<Icon name="sparkles" size={12} className="mt-0.5 shrink-0 text-muted-foreground" />
|
|
338
|
+
<span className="min-w-0 break-words">{s}</span>
|
|
339
|
+
</button>
|
|
340
|
+
))}
|
|
341
|
+
</div>
|
|
342
|
+
) : null}
|
|
343
|
+
</>
|
|
344
|
+
)}
|
|
345
|
+
</div>
|
|
346
|
+
{!atBottom && started ? (
|
|
347
|
+
<IconButton icon="arrow-down" size="sm" label="Jump to latest" onClick={jump} className="absolute bottom-2 right-3 shadow-md" />
|
|
348
|
+
) : null}
|
|
349
|
+
</div>
|
|
350
|
+
|
|
351
|
+
{bar}
|
|
352
|
+
{chat?.error ? <p className="shrink-0 px-3.5 pt-2 text-xs text-destructive">{chat.error}</p> : null}
|
|
353
|
+
{chat?.notice ? (
|
|
354
|
+
<p role="status" className="shrink-0 px-3.5 pt-2 text-xs text-muted-foreground">
|
|
355
|
+
{chat.notice}
|
|
356
|
+
</p>
|
|
357
|
+
) : null}
|
|
358
|
+
{chat?.unavailable ? (
|
|
359
|
+
<p className="shrink-0 px-3.5 pt-2 text-xs text-muted-foreground">
|
|
360
|
+
This chat needs a newer Makersclaw.{" "}
|
|
361
|
+
{chat.request ? (
|
|
362
|
+
<Button variant="link" size="inherit" onClick={() => chat.openOnDesk?.()}>
|
|
363
|
+
Open it on the desk
|
|
364
|
+
</Button>
|
|
365
|
+
) : null}
|
|
366
|
+
</p>
|
|
367
|
+
) : null}
|
|
368
|
+
{attachError || dictation.error ? (
|
|
369
|
+
<p role="alert" className="shrink-0 px-3.5 pt-2 text-xs text-destructive">
|
|
370
|
+
{attachError ?? dictation.error}
|
|
371
|
+
</p>
|
|
372
|
+
) : null}
|
|
373
|
+
|
|
374
|
+
<ChatComposer
|
|
375
|
+
className="shrink-0 px-3 pb-3 pt-2"
|
|
376
|
+
label={title}
|
|
377
|
+
value={text}
|
|
378
|
+
onChange={setText}
|
|
379
|
+
onSubmit={() => submit()}
|
|
380
|
+
onStop={stop}
|
|
381
|
+
onAttach={attach}
|
|
382
|
+
files={files}
|
|
383
|
+
onRemoveFile={(i) => setFiles((all) => all.filter((_, j) => j !== i))}
|
|
384
|
+
busy={busy}
|
|
385
|
+
canStop={canStop}
|
|
386
|
+
stopping={stopping}
|
|
387
|
+
blocked={blocked}
|
|
388
|
+
sending={Boolean(chat?.sending)}
|
|
389
|
+
encoding={encoding}
|
|
390
|
+
dictation={dictation}
|
|
391
|
+
placeholder={words}
|
|
392
|
+
hint={hint}
|
|
393
|
+
textareaRef={textarea}
|
|
394
|
+
fileInputRef={fileInput}
|
|
395
|
+
/>
|
|
396
|
+
</div>
|
|
397
|
+
);
|
|
398
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cx } from "../../ui";
|
|
4
|
+
import { ago } from "./chat-model";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The member's past chats, newest first, one line each: the title, a dot on
|
|
8
|
+
* one that needs them (warning) or is working (info), and its age. The
|
|
9
|
+
* pane's Chats overlay and its empty state both draw this.
|
|
10
|
+
*
|
|
11
|
+
* chats { items: [{ sessionId, title, mark: "needs" | "working" | null, at }],
|
|
12
|
+
* loading, error }
|
|
13
|
+
* currentId the open chat's session
|
|
14
|
+
* onPick (sessionId) => void
|
|
15
|
+
*/
|
|
16
|
+
export function ChatsList({ chats, currentId, onPick }) {
|
|
17
|
+
if (chats.items.length === 0)
|
|
18
|
+
return (
|
|
19
|
+
<p className="px-3.5 py-3 text-xs text-muted-foreground">{chats.loading ? "Loading chats…" : chats.error ? "The chats did not load." : "No chats yet. Your first message starts one."}</p>
|
|
20
|
+
);
|
|
21
|
+
return (
|
|
22
|
+
<nav aria-label="Past chats" className="grid">
|
|
23
|
+
{chats.items.map((c) => (
|
|
24
|
+
<button
|
|
25
|
+
key={c.sessionId}
|
|
26
|
+
type="button"
|
|
27
|
+
aria-current={c.sessionId === currentId ? "true" : undefined}
|
|
28
|
+
onClick={() => onPick(c.sessionId)}
|
|
29
|
+
className={cx(
|
|
30
|
+
"flex min-w-0 items-center gap-2 px-3.5 py-2 text-left text-xs hover:bg-muted focus-visible:outline-2 focus-visible:outline-ring",
|
|
31
|
+
c.sessionId === currentId && "bg-muted font-medium",
|
|
32
|
+
)}
|
|
33
|
+
>
|
|
34
|
+
<span className="min-w-0 flex-1 truncate" title={c.title}>
|
|
35
|
+
{c.title}
|
|
36
|
+
</span>
|
|
37
|
+
{c.mark ? (
|
|
38
|
+
<span
|
|
39
|
+
role="img"
|
|
40
|
+
aria-label={c.mark === "needs" ? "Needs you" : "Working"}
|
|
41
|
+
title={c.mark === "needs" ? "Needs you" : "Working"}
|
|
42
|
+
className={cx("size-2 shrink-0 rounded-full", c.mark === "needs" ? "bg-warning" : "animate-pulse bg-info")}
|
|
43
|
+
/>
|
|
44
|
+
) : null}
|
|
45
|
+
<time className="shrink-0 text-muted-foreground" dateTime={c.at ?? undefined}>
|
|
46
|
+
{ago(c.at)}
|
|
47
|
+
</time>
|
|
48
|
+
</button>
|
|
49
|
+
))}
|
|
50
|
+
</nav>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// ChatPane over a pretend chat — the gallery's and the smoke test's surface.
|
|
4
|
+
// Not installed into apps. Three frames side by side: an empty chat (past
|
|
5
|
+
// chats, suggestions, the message box and its hint), a chat that answers
|
|
6
|
+
// after a moment (ask anything: a stop takes the send arrow's place while it
|
|
7
|
+
// works), and a chat that waits on the member (the box is closed and says
|
|
8
|
+
// why). The message box is the desk's: the words on top, + at the left and
|
|
9
|
+
// the microphone and a round arrow at the right.
|
|
10
|
+
import { useEffect, useRef, useState } from "react";
|
|
11
|
+
import { ChatPane } from "../components/blocks/chat-pane";
|
|
12
|
+
|
|
13
|
+
const CHATS = {
|
|
14
|
+
items: [
|
|
15
|
+
{ sessionId: "past-1", title: "Who should we follow up with this week?", mark: "needs", at: new Date(Date.now() - 20 * 60 * 1000).toISOString() },
|
|
16
|
+
{ sessionId: "past-2", title: "Weekly pipeline read", mark: null, at: new Date(Date.now() - 3 * 3600 * 1000).toISOString() },
|
|
17
|
+
],
|
|
18
|
+
loading: false,
|
|
19
|
+
error: null,
|
|
20
|
+
unavailable: false,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function usePretendChat({ chats = CHATS, answer = true } = {}) {
|
|
24
|
+
const [messages, setMessages] = useState([]);
|
|
25
|
+
const [state, setState] = useState(null);
|
|
26
|
+
const timers = useRef([]);
|
|
27
|
+
useEffect(() => () => timers.current.forEach(clearTimeout), []);
|
|
28
|
+
return {
|
|
29
|
+
sessionId: messages.length ? "demo" : null,
|
|
30
|
+
state,
|
|
31
|
+
request: messages.length ? { id: "demo-request", title: "Ask" } : null,
|
|
32
|
+
view: { messages, thinking: state === "working", failure: null, questions: [] },
|
|
33
|
+
turns: [],
|
|
34
|
+
pending: null,
|
|
35
|
+
sending: false,
|
|
36
|
+
loading: false,
|
|
37
|
+
error: null,
|
|
38
|
+
unavailable: false,
|
|
39
|
+
chats,
|
|
40
|
+
send: (text) => {
|
|
41
|
+
const id = `m${Date.now()}`;
|
|
42
|
+
setState("working");
|
|
43
|
+
setMessages((m) => [...m, { id: `${id}u`, role: "user", text }]);
|
|
44
|
+
if (!answer) return;
|
|
45
|
+
timers.current.push(
|
|
46
|
+
setTimeout(() => {
|
|
47
|
+
setMessages((m) => [...m, { id: `${id}a`, role: "assistant", text: "Three people have gone quiet for over two weeks. Want me to draft a note to each?" }]);
|
|
48
|
+
setState("done");
|
|
49
|
+
}, 1800),
|
|
50
|
+
);
|
|
51
|
+
},
|
|
52
|
+
stop: () => setState("stopped"),
|
|
53
|
+
newChat: () => {
|
|
54
|
+
setMessages([]);
|
|
55
|
+
setState(null);
|
|
56
|
+
},
|
|
57
|
+
openSession: () => undefined,
|
|
58
|
+
openOnDesk: () => true,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function Lines({ messages }) {
|
|
63
|
+
return messages.map((m) => (
|
|
64
|
+
<p key={m.id} className={m.role === "user" ? "justify-self-end rounded-[var(--radius)] bg-muted px-3 py-2" : ""}>
|
|
65
|
+
{m.text}
|
|
66
|
+
</p>
|
|
67
|
+
));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function Frame({ title, ...options }) {
|
|
71
|
+
const chat = usePretendChat(options);
|
|
72
|
+
const messages = chat.view.messages;
|
|
73
|
+
return (
|
|
74
|
+
<div className="h-[560px] min-w-0 overflow-hidden rounded-lg border border-border">
|
|
75
|
+
<ChatPane
|
|
76
|
+
chat={chat}
|
|
77
|
+
title={title}
|
|
78
|
+
empty="The agent reads every person and deal. What it changes you can take back."
|
|
79
|
+
suggestions={["Who should we follow up with?", "What changed this week?"]}
|
|
80
|
+
hint="Enter sends · Shift+Enter is a new line"
|
|
81
|
+
started={messages.length > 0}
|
|
82
|
+
busy={chat.state === "working"}
|
|
83
|
+
>
|
|
84
|
+
<Lines messages={messages} />
|
|
85
|
+
</ChatPane>
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export default function ChatPaneDemo() {
|
|
91
|
+
const waiting = usePretendChat({ chats: null });
|
|
92
|
+
return (
|
|
93
|
+
<div className="grid gap-4 p-6 md:grid-cols-3">
|
|
94
|
+
<Frame title="Ask the agent" />
|
|
95
|
+
<Frame title="No chats yet" chats={{ items: [], loading: false, error: null, unavailable: false }} />
|
|
96
|
+
<div className="h-[560px] min-w-0 overflow-hidden rounded-lg border border-border">
|
|
97
|
+
<ChatPane chat={waiting} title="Waiting on you" started blocked closedWords="Answer above first" bar={<p className="border-t border-border px-3.5 py-2 text-xs text-muted-foreground">A question for you: which list first?</p>}>
|
|
98
|
+
<p>I can start with the warm leads or the investors.</p>
|
|
99
|
+
</ChatPane>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// The ChatPane block — `makersclaw add chat-pane`. Fixed: compose it
|
|
2
|
+
// through its props (README.md beside this file). The model's helpers are
|
|
3
|
+
// plain functions; they work on the server too.
|
|
4
|
+
export { ChatPane } from "./chat-pane";
|
|
5
|
+
export { ChatComposer } from "./chat-composer";
|
|
6
|
+
export { ChatsList } from "./chats-list";
|
|
7
|
+
export { ChatDockContext } from "./chat-context";
|
|
8
|
+
export { ago, startStep } from "./chat-model";
|
|
9
|
+
export { MAX_BYTES, MAX_FILES, addFiles, filesFromTransfer, formatBytes, readFile, useDictation } from "./chat-attach";
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# ConnectCard
|
|
2
|
+
|
|
3
|
+
The accounts the app declares, as rows the member already knows from the
|
|
4
|
+
window's Settings › Connections: what each is for, its kind (Mail,
|
|
5
|
+
Calendar), needed or optional, **live** or **not connected** (a mark and
|
|
6
|
+
a word), which accounts meet it, and **Connect** or **Change**. Part of
|
|
7
|
+
the agent kit (0.8.1): the member connects where the need is felt,
|
|
8
|
+
inside the app.
|
|
9
|
+
|
|
10
|
+
```jsx
|
|
11
|
+
import { ConnectCard, ConnectRow } from "../components/blocks/connect-card"; // a Makersclaw app
|
|
12
|
+
import { ConnectCard, ConnectRow } from "@/components/makersclaw/blocks/connect-card"; // a shadcn project
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
A client component. Its own width decides its layout: under about
|
|
16
|
+
384 px each row's state and button go under its words.
|
|
17
|
+
|
|
18
|
+
## What Connect does
|
|
19
|
+
|
|
20
|
+
The block only calls `onConnect(key)`. `useConnections().connect` then:
|
|
21
|
+
|
|
22
|
+
1. Inside the desk, asks it to open its connections panel for that
|
|
23
|
+
need (`claw:open-connections`). The panel lists every provider of the
|
|
24
|
+
need's kind — a "mail" need shows Gmail, Outlook, Zoho Mail — and the
|
|
25
|
+
member signs in there. No agent turn, no chat.
|
|
26
|
+
2. Outside the desk (the app on its own host), fetches the link (`POST
|
|
27
|
+
_claw/connect`) and opens it in a new tab.
|
|
28
|
+
3. Either way, looks every 3 s for two minutes until the account is
|
|
29
|
+
live; `connecting` is its key meanwhile, and the row says "Waiting
|
|
30
|
+
for sign-in".
|
|
31
|
+
|
|
32
|
+
## Props
|
|
33
|
+
|
|
34
|
+
### ConnectCard
|
|
35
|
+
|
|
36
|
+
- `connections` — useConnections' `connections`: `[{ key, label,
|
|
37
|
+
required, connected, accounts, category, category_label }]`.
|
|
38
|
+
- `onConnect(key)` — useConnections' `connect`. Leave it out and no
|
|
39
|
+
button is drawn (a read-only list).
|
|
40
|
+
- `connecting` — the key being connected (useConnections'
|
|
41
|
+
`connecting`).
|
|
42
|
+
- `title` — the head (default "Accounts it uses"); `null` for none.
|
|
43
|
+
`description` — one line under it: what the app does with them.
|
|
44
|
+
- `empty` — what it says with no accounts; `null` draws nothing.
|
|
45
|
+
- `className` — layout around it. Never colour.
|
|
46
|
+
|
|
47
|
+
### ConnectRow
|
|
48
|
+
|
|
49
|
+
One row, for a list of your own (an empty state, a setup step):
|
|
50
|
+
`connection`, `onConnect(key)`, `connecting` (boolean), `connectLabel`
|
|
51
|
+
("Connect"), `changeLabel` ("Change"). Put it inside a `<ul>`, in an
|
|
52
|
+
element with `@container` so its layout follows its width.
|
|
53
|
+
|
|
54
|
+
## useConnections (lib/hooks.js)
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
const { connections, connecting, connect } = useConnections();
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
It reads `GET _claw/connect` once, then only while an account is being
|
|
61
|
+
connected, and again when the page comes back. `connect(key)` resolves
|
|
62
|
+
`{ opened: "desk" | "tab" | false, connected?, error? }`. On a claw from
|
|
63
|
+
before the door it is `unavailable`: draw the card with `empty`, never
|
|
64
|
+
an error.
|
|
65
|
+
|
|
66
|
+
## Who may do what
|
|
67
|
+
|
|
68
|
+
Any member who opens the app sees the accounts and may connect one: the
|
|
69
|
+
sign-in is theirs, at the provider, as on the desk. Which accounts an
|
|
70
|
+
app may use is the app's declaration, not this block's.
|
|
71
|
+
|
|
72
|
+
## Never
|
|
73
|
+
|
|
74
|
+
- Never a sign-in form, a password or a key field: the member signs in
|
|
75
|
+
at the provider, on the desk's panel or the link's page.
|
|
76
|
+
- Never an agent turn to connect an account ("Ask the agent to connect
|
|
77
|
+
Gmail"): Connect opens the panel.
|
|
78
|
+
- Never live or not by colour alone.
|
|
79
|
+
|
|
80
|
+
## Example
|
|
81
|
+
|
|
82
|
+
```jsx
|
|
83
|
+
"use client";
|
|
84
|
+
import { useConnections } from "../lib/hooks";
|
|
85
|
+
import { ConnectCard } from "../components/blocks/connect-card";
|
|
86
|
+
|
|
87
|
+
export default function WhatItReads() {
|
|
88
|
+
const { connections, connecting, connect } = useConnections();
|
|
89
|
+
return (
|
|
90
|
+
<ConnectCard
|
|
91
|
+
title="What the CRM reads"
|
|
92
|
+
description="Your mail and calendar, to build each person's timeline."
|
|
93
|
+
connections={connections}
|
|
94
|
+
connecting={connecting}
|
|
95
|
+
onConnect={connect}
|
|
96
|
+
/>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
```
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "connect-card",
|
|
3
|
+
"title": "ConnectCard",
|
|
4
|
+
"description": "The accounts the app declares, as rows like the window's Settings › Connections: what each is for, its kind (Mail, Calendar), needed or optional, live or not connected as a mark and a word, which accounts meet it, and Connect or Change. The press goes out as onConnect(key): useConnections() (lib/hooks.js) opens the desk's connections panel for that need, listing every provider of the kind (no agent turn), or hands out the link in a new tab outside the desk, then watches until the account is live. Signing in stays the member's, at the provider.",
|
|
5
|
+
"useWhen": "An app that reads or writes through an outside account shows what it uses and lets the member connect it where the need is felt: a CRM's \"What the CRM reads\" (mailbox, calendar), an inbox app's accounts, a setup step, the empty state of a screen that needs a mailbox. For a first-run checklist use setup-checklist; for the app's own settings page use settings-list.",
|
|
6
|
+
"tags": ["connect", "connections", "accounts", "integrations", "oauth", "mailbox", "calendar", "sign in", "setup", "live", "kit"],
|
|
7
|
+
"exports": ["ConnectCard", "ConnectRow"],
|
|
8
|
+
"files": ["index.js", "connect-card.jsx"],
|
|
9
|
+
"requires": []
|
|
10
|
+
}
|