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,93 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// AiAction over a pretend useLlmAction(): priced, running, answered,
|
|
4
|
+
// refused, shaped by a schema, and not connected yet — the gallery's and
|
|
5
|
+
// the smoke test's surface. Not installed into apps.
|
|
6
|
+
import { useEffect, useRef, useState } from "react";
|
|
7
|
+
import { Card } from "../components/ui";
|
|
8
|
+
import { AiAction } from "../components/blocks/ai-action";
|
|
9
|
+
|
|
10
|
+
/** Shaped as useLlmAction returns it; `answer` is what run() resolves. */
|
|
11
|
+
function usePretendCall(answer, { estimate = { credits: 1, credits_max: 1 }, ms = 1200 } = {}) {
|
|
12
|
+
const [state, setState] = useState({ result: null, loading: false, error: null });
|
|
13
|
+
const [estimating, setEstimating] = useState(true);
|
|
14
|
+
const timers = useRef([]);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
timers.current.push(setTimeout(() => setEstimating(false), 500));
|
|
17
|
+
return () => timers.current.forEach(clearTimeout);
|
|
18
|
+
}, []);
|
|
19
|
+
return {
|
|
20
|
+
estimate: estimating ? null : estimate,
|
|
21
|
+
estimating,
|
|
22
|
+
loading: state.loading,
|
|
23
|
+
result: state.result,
|
|
24
|
+
error: state.error,
|
|
25
|
+
unavailable: false,
|
|
26
|
+
run: () =>
|
|
27
|
+
new Promise((resolve) => {
|
|
28
|
+
setState({ result: null, loading: true, error: null });
|
|
29
|
+
timers.current.push(
|
|
30
|
+
setTimeout(() => {
|
|
31
|
+
const r = typeof answer === "function" ? answer() : answer;
|
|
32
|
+
setState(r.ok === false ? { result: null, loading: false, error: r.error } : { result: r, loading: false, error: null });
|
|
33
|
+
resolve(r);
|
|
34
|
+
}, ms),
|
|
35
|
+
);
|
|
36
|
+
}),
|
|
37
|
+
reset: () => setState({ result: null, loading: false, error: null }),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const DRAFT =
|
|
42
|
+
"Thanks so much for taking the time to meet with us last Thursday. We really enjoyed hearing about how Crumb & Co schedules its 22 shops, and we think there is a lot we could do together over the coming months.";
|
|
43
|
+
|
|
44
|
+
export default function AiActionDemo() {
|
|
45
|
+
const [draft, setDraft] = useState(DRAFT);
|
|
46
|
+
const shorter = usePretendCall({ ok: true, text: "Thanks for meeting last Thursday. We'd love to help Crumb & Co schedule all 22 shops." });
|
|
47
|
+
const why = usePretendCall(
|
|
48
|
+
{ ok: true, data: { reasons: ["Two replies in a week", "Asked for pricing", "A new head of operations"] } },
|
|
49
|
+
{ estimate: { credits: 2, credits_max: 6 } },
|
|
50
|
+
);
|
|
51
|
+
const refused = usePretendCall({ ok: false, error: "The app's weekly allowance is used up. It starts again on Monday." });
|
|
52
|
+
const running = usePretendCall({ ok: true, text: "…" }, { ms: 1e9 });
|
|
53
|
+
return (
|
|
54
|
+
<div className="grid gap-6 p-6 [grid-template-columns:repeat(auto-fit,minmax(min(100%,20rem),1fr))]">
|
|
55
|
+
<Card title="A draft">
|
|
56
|
+
<div className="grid gap-3">
|
|
57
|
+
<p className="text-sm">{draft}</p>
|
|
58
|
+
<AiAction label="Shorter" action={shorter} useLabel="Replace the draft" onUse={(r) => setDraft(r.text)} />
|
|
59
|
+
</div>
|
|
60
|
+
</Card>
|
|
61
|
+
<Card title="A score">
|
|
62
|
+
<AiAction
|
|
63
|
+
label="Why is it 82?"
|
|
64
|
+
icon="lightbulb"
|
|
65
|
+
action={why}
|
|
66
|
+
render={(r) => (
|
|
67
|
+
<ul className="list-disc pl-5">
|
|
68
|
+
{r.data.reasons.map((x) => (
|
|
69
|
+
<li key={x}>{x}</li>
|
|
70
|
+
))}
|
|
71
|
+
</ul>
|
|
72
|
+
)}
|
|
73
|
+
/>
|
|
74
|
+
</Card>
|
|
75
|
+
<Card title="Refused">
|
|
76
|
+
<AiAction label="Summarize the thread" action={refused} />
|
|
77
|
+
</Card>
|
|
78
|
+
<Card title="Running">
|
|
79
|
+
<AiAction label="Summarize" action={{ ...running, estimate: { credits: 0.4, credits_max: 0.4 }, estimating: false, loading: true, run: () => undefined }} />
|
|
80
|
+
</Card>
|
|
81
|
+
<Card title="Not connected yet">
|
|
82
|
+
<AiAction label="Shorter" action={{ unavailable: true }} />
|
|
83
|
+
</Card>
|
|
84
|
+
<div className="w-80 max-w-full">
|
|
85
|
+
<AiAction
|
|
86
|
+
label="Explain why this account's health score moved down this week"
|
|
87
|
+
action={{ estimate: { credits: 1200, credits_max: 2400 }, estimating: false, run: () => undefined, result: { text: "Two of its five shops stopped scheduling on Monday, and the owner hasn't opened the last three emails." }, reset: () => undefined }}
|
|
88
|
+
onUse={() => undefined}
|
|
89
|
+
/>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Approval
|
|
2
|
+
|
|
3
|
+
The yes, given in the app. The app draws the card in its own theme: what
|
|
4
|
+
will happen, to whom, the exact words, the cost. The press is not the
|
|
5
|
+
app's. Under the card sits a small page of Makersclaw's, in the app's
|
|
6
|
+
colours, that app code cannot script. It shows the server's own words for
|
|
7
|
+
the request and only counts a press a person made on a card they can see.
|
|
8
|
+
|
|
9
|
+
```jsx
|
|
10
|
+
import { Approval } from "../components/blocks/approval"; // a Makersclaw app ("./blocks/approval" from components/)
|
|
11
|
+
import { Approval } from "@/components/makersclaw/blocks/approval"; // a shadcn project
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
A client component. The Example is at the end.
|
|
15
|
+
|
|
16
|
+
## Why a frame
|
|
17
|
+
|
|
18
|
+
App code is written by an agent, and an in-app Approve button could be
|
|
19
|
+
pressed by that same code, or by a script that got into the page. So the
|
|
20
|
+
button that gives the yes is not in the app's page. It is a Makersclaw
|
|
21
|
+
page, embedded in the card:
|
|
22
|
+
|
|
23
|
+
- **Another origin.** The app cannot reach into it, read it or click it.
|
|
24
|
+
- **The server's words.** It shows what Makersclaw holds for the request:
|
|
25
|
+
the agent's one question, who it goes to, from which account, the first
|
|
26
|
+
message in full, and the rule "Always allow this kind" would save. Never
|
|
27
|
+
the app's text. The app's card above it shows the same thing in the
|
|
28
|
+
app's own design; the frame is the one to trust.
|
|
29
|
+
- **A press a person made, on a card they can see.** Where the browser
|
|
30
|
+
can tell (Chrome, Edge) the Approve button works only after the card
|
|
31
|
+
has been in view, uncovered and not faded, for half a second. Where it
|
|
32
|
+
cannot (Safari, Firefox) Approve is press and hold, for most of a
|
|
33
|
+
second. A click the page's script makes does nothing.
|
|
34
|
+
- **The member's own sign-in.** The press carries the member's token from
|
|
35
|
+
the desk's storage, which the app has no way to read, and the server
|
|
36
|
+
checks it is the member who asked, for this app, for the question the
|
|
37
|
+
card showed.
|
|
38
|
+
- **The same path as the desk's Approve.** The answer goes to the agent
|
|
39
|
+
as a structured answer from the member, so the audit trail, the rules
|
|
40
|
+
and the Standing yes stay one.
|
|
41
|
+
|
|
42
|
+
"Always allow this kind" is in the same card: one tick, and the server
|
|
43
|
+
writes one bounded line into the app's Standing yes (the kind: what is
|
|
44
|
+
sent, to whom, from which account), shown beside the box before it is
|
|
45
|
+
ticked. Rules, not items: the next one of that kind goes without asking.
|
|
46
|
+
Anything wider still asks.
|
|
47
|
+
|
|
48
|
+
## Which hook feeds it
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
const approval = useApproval(item.id); // lib/hooks.js; item from useNeedsYou
|
|
52
|
+
// → { approval, state, always, fallback, frame, height, loading, error, … }
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
It reads `GET _claw/requests/:id/approval` once, for the request that
|
|
56
|
+
waits on this member's yes (`item.via === "press"`), and listens to the
|
|
57
|
+
frame. It approves nothing.
|
|
58
|
+
|
|
59
|
+
## Props
|
|
60
|
+
|
|
61
|
+
- `approval` — `useApproval(...)`'s answer (required).
|
|
62
|
+
- `title` — what is about to happen, in the app's words ("Send 3
|
|
63
|
+
follow-ups to the Replies list").
|
|
64
|
+
- `kicker` — the line over it. Default "Waiting for your yes".
|
|
65
|
+
- `children` — the app's own card content: the draft, the recipients, the
|
|
66
|
+
exact words. Drawn above the press, in the app's theme.
|
|
67
|
+
- `cost` — a node for the price (the `cost` block's `CostTag`), beside the
|
|
68
|
+
title.
|
|
69
|
+
- `onOpenDesk()` — opens the desk on this ask (`needs.open(item)`): the
|
|
70
|
+
press of the fallback.
|
|
71
|
+
- `onDone({ state, always })` — once, when the member has said yes
|
|
72
|
+
(`"approved"`) or not now (`"declined"`); `always` is true when "Always
|
|
73
|
+
allow this kind" was saved. Refresh what the yes unlocks.
|
|
74
|
+
- `className` — layout around it. Never colour.
|
|
75
|
+
|
|
76
|
+
## The states
|
|
77
|
+
|
|
78
|
+
`approval.state` is one of: `loading` (a skeleton) · `ready` (the frame)
|
|
79
|
+
· `approved` ("You said yes · the agent is on it", and "Always allowed for
|
|
80
|
+
this kind" when it was) · `declined` ("Not now. Nothing went out.") ·
|
|
81
|
+
`stale` (the question moved on since the card was drawn; it looks again
|
|
82
|
+
by itself) · `gone` (nothing waits any more: answered elsewhere).
|
|
83
|
+
|
|
84
|
+
When the frame cannot do its part, `approval.fallback` is true and the
|
|
85
|
+
block draws `WaitingYes` instead: "Waiting for your yes on the desk",
|
|
86
|
+
with **Open the desk**. That is the case when the frame did not load in
|
|
87
|
+
seven seconds (`blocked`), the member must sign in again on the desk
|
|
88
|
+
(`signin`), the claw is from before this door (`unavailable`) or the read
|
|
89
|
+
failed (`error`). Nothing is lost: the yes is the same yes.
|
|
90
|
+
|
|
91
|
+
## Who may do what
|
|
92
|
+
|
|
93
|
+
- The member who asked gives the yes. Owners and admins do not give a
|
|
94
|
+
teammate's: the door refuses them (`not_your_request`).
|
|
95
|
+
- Only a yes: one question, one Approve and Not now. A tool approval, a
|
|
96
|
+
connect, a spend and several questions at once go to the desk.
|
|
97
|
+
- A question (not a yes) is answered in place, with `QuestionBox`
|
|
98
|
+
(`needs-you`), never here.
|
|
99
|
+
- Topping up credits and signing in to an account stay on the desk.
|
|
100
|
+
|
|
101
|
+
## Never
|
|
102
|
+
|
|
103
|
+
- **Never an Approve, Yes or Send button of your own that gives the
|
|
104
|
+
yes.** The app's card may have a "Review the draft" press; the
|
|
105
|
+
approval is the frame's.
|
|
106
|
+
- Never hide, cover, fade, scale or position the frame so that part of it
|
|
107
|
+
is out of view: a press then does not count, by design.
|
|
108
|
+
- Never put the frame in an element with `pointer-events: none`,
|
|
109
|
+
`opacity` below 1 or a `transform`.
|
|
110
|
+
- Never show another member's asks here.
|
|
111
|
+
|
|
112
|
+
## Example
|
|
113
|
+
|
|
114
|
+
The post that waits for its yes, inside the page that already shows it.
|
|
115
|
+
|
|
116
|
+
```jsx
|
|
117
|
+
"use client";
|
|
118
|
+
import { useNeedsYou, useApproval } from "../lib/hooks";
|
|
119
|
+
import { Approval } from "../components/blocks/approval";
|
|
120
|
+
import { CostTag } from "../components/blocks/cost";
|
|
121
|
+
|
|
122
|
+
export function SendCard({ item, draft }) {
|
|
123
|
+
const needs = useNeedsYou();
|
|
124
|
+
const approval = useApproval(item.id);
|
|
125
|
+
return (
|
|
126
|
+
<Approval
|
|
127
|
+
approval={approval}
|
|
128
|
+
title={`Send ${draft.count} follow-ups to ${draft.list}`}
|
|
129
|
+
cost={<CostTag credits={draft.credits} />}
|
|
130
|
+
onOpenDesk={() => needs.open(item)}
|
|
131
|
+
onDone={({ state }) => state === "approved" && refreshRows()}
|
|
132
|
+
>
|
|
133
|
+
<DraftList draft={draft} />
|
|
134
|
+
</Approval>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// …and in a queue of everything that waits:
|
|
139
|
+
<NeedsYou
|
|
140
|
+
items={needs.items}
|
|
141
|
+
onOpen={needs.open}
|
|
142
|
+
onAnswer={needs.answer}
|
|
143
|
+
renderYes={(item) => <SendCard item={item} draft={draftFor(item)} />}
|
|
144
|
+
/>
|
|
145
|
+
```
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
4
|
+
import { Button, Icon, Skeleton, Spinner, cx } from "../../ui";
|
|
5
|
+
import { WaitingYes } from "../needs-you";
|
|
6
|
+
|
|
7
|
+
// Approval — the yes, given in the app (the app kit, D18). The app draws
|
|
8
|
+
// the card in its own theme: what will happen, to whom, the exact words,
|
|
9
|
+
// the cost. The press is not the app's. Under the card sits a small page
|
|
10
|
+
// of Makersclaw's, in another origin, that app code cannot script: it
|
|
11
|
+
// shows the SERVER's words for the request (the agent's question, who,
|
|
12
|
+
// from which account, the first message, the rule "Always allow this
|
|
13
|
+
// kind" would save), in the app's colours, and only counts a press a
|
|
14
|
+
// person made on a card they can see (in view and uncovered, or held).
|
|
15
|
+
// The block draws that frame and what it says back; it never draws an
|
|
16
|
+
// Approve of its own.
|
|
17
|
+
//
|
|
18
|
+
// const approval = useApproval(item.id); // lib/hooks.js
|
|
19
|
+
// <Approval approval={approval} title="Send 3 follow-ups" onOpenDesk={…}>
|
|
20
|
+
// …the draft the app already shows…
|
|
21
|
+
// </Approval>
|
|
22
|
+
//
|
|
23
|
+
// If the frame cannot do its part (it did not load in time, the member
|
|
24
|
+
// must sign in again, a claw from before this door) the block falls back
|
|
25
|
+
// to WaitingYes: "Waiting for your yes on the desk", with the desk one
|
|
26
|
+
// press away. Props in, callbacks out.
|
|
27
|
+
|
|
28
|
+
/** A frame that has not said how tall it wants to be. */
|
|
29
|
+
const FRAME_HEIGHT = 240;
|
|
30
|
+
|
|
31
|
+
/** Why the fallback is showing, in a sentence. */
|
|
32
|
+
const WHY = {
|
|
33
|
+
signin: "Sign in again on Makersclaw to say yes here. The desk has it.",
|
|
34
|
+
blocked: "The press could not load here. The desk has it.",
|
|
35
|
+
unavailable: "Saying yes here needs a newer Makersclaw. The desk has it.",
|
|
36
|
+
error: "The press could not be loaded just now. The desk has it.",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/** The card's receipt, once the press has been used. */
|
|
40
|
+
function Receipt({ icon, tone, head, children }) {
|
|
41
|
+
return (
|
|
42
|
+
<div role="status" className="grid grid-cols-1 min-w-0 gap-1">
|
|
43
|
+
<p className={cx("flex min-w-0 items-center gap-2 font-medium", tone)}>
|
|
44
|
+
<Icon name={icon} size={16} className="shrink-0" />
|
|
45
|
+
<span className="min-w-0 break-words">{head}</span>
|
|
46
|
+
</p>
|
|
47
|
+
{children ? <p className="text-muted-foreground">{children}</p> : null}
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* approval what useApproval(requestId) returns (required)
|
|
54
|
+
* title what is about to happen, in the app's words ("Send 3
|
|
55
|
+
* follow-ups to the Replies list")
|
|
56
|
+
* kicker the line over it; default "Waiting for your yes"
|
|
57
|
+
* children the app's own card content: the draft, the recipients,
|
|
58
|
+
* the exact words. Drawn above the press, in the app's
|
|
59
|
+
* theme
|
|
60
|
+
* cost a node for the price ("about 700 cr": the `cost`
|
|
61
|
+
* block's CostTag); shown beside the title
|
|
62
|
+
* onOpenDesk() opens the desk on this ask: the press of the fallback
|
|
63
|
+
* (useNeedsYou's `open(item)`); without it the fallback
|
|
64
|
+
* only says where the yes is
|
|
65
|
+
* onDone({ state, always }) called once when the member has said yes
|
|
66
|
+
* or not now ("approved" or "declined"; `always` true when
|
|
67
|
+
* "Always allow this kind" was saved): refresh what the
|
|
68
|
+
* yes unlocks
|
|
69
|
+
* className layout around it. Never colour.
|
|
70
|
+
*/
|
|
71
|
+
export function Approval({ approval, title, kicker = "Waiting for your yes", children, cost, onOpenDesk, onDone, className }) {
|
|
72
|
+
const state = approval?.state ?? "idle";
|
|
73
|
+
const spoken = useRef(null);
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
if ((state === "approved" || state === "declined") && spoken.current !== state) {
|
|
76
|
+
spoken.current = state;
|
|
77
|
+
onDone?.({ state, always: Boolean(approval?.always) });
|
|
78
|
+
}
|
|
79
|
+
if (state !== "approved" && state !== "declined") spoken.current = null;
|
|
80
|
+
}, [state, approval?.always]);
|
|
81
|
+
|
|
82
|
+
if (state === "idle") return null;
|
|
83
|
+
|
|
84
|
+
if (state === "loading")
|
|
85
|
+
return (
|
|
86
|
+
<section aria-busy="true" aria-label="Waiting for your yes" className={cx("@container grid grid-cols-1 min-w-0 gap-3 rounded-lg border border-border bg-card p-3.5", className)}>
|
|
87
|
+
<Skeleton className="w-1/3" />
|
|
88
|
+
<Skeleton className="w-3/4" />
|
|
89
|
+
<Skeleton className="h-16 w-full" />
|
|
90
|
+
</section>
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
// The frame cannot be used: the desk's card, with the words the app drew.
|
|
94
|
+
if (approval?.fallback)
|
|
95
|
+
return (
|
|
96
|
+
<WaitingYes
|
|
97
|
+
title={title}
|
|
98
|
+
frozen={children}
|
|
99
|
+
kicker={kicker}
|
|
100
|
+
body={WHY[state] ?? WHY.error}
|
|
101
|
+
onOpen={onOpenDesk}
|
|
102
|
+
className={className}
|
|
103
|
+
/>
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const done = state === "approved" || state === "declined";
|
|
107
|
+
const head = (
|
|
108
|
+
<div className="grid grid-cols-1 min-w-0 gap-1">
|
|
109
|
+
<div className="flex min-w-0 items-start gap-2">
|
|
110
|
+
<div className="grid grid-cols-1 min-w-0 flex-1 gap-1">
|
|
111
|
+
{kicker ? (
|
|
112
|
+
<p className="flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wide text-muted-foreground">
|
|
113
|
+
{done ? null : <Icon name="shield-check" size={12} className="shrink-0" />}
|
|
114
|
+
{kicker}
|
|
115
|
+
</p>
|
|
116
|
+
) : null}
|
|
117
|
+
{title ? <p className="break-words text-[15px] font-semibold leading-snug">{title}</p> : null}
|
|
118
|
+
</div>
|
|
119
|
+
{cost ? <div className="shrink-0 pt-0.5 text-xs text-muted-foreground">{cost}</div> : null}
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<section
|
|
126
|
+
aria-label={typeof title === "string" ? title : "Waiting for your yes"}
|
|
127
|
+
className={cx("@container grid grid-cols-1 min-w-0 content-start gap-3 rounded-lg border border-border bg-card p-3.5 text-sm text-card-foreground", className)}
|
|
128
|
+
>
|
|
129
|
+
{head}
|
|
130
|
+
{children != null && children !== "" ? <div className={cx("min-w-0 break-words", done && "text-muted-foreground")}>{children}</div> : null}
|
|
131
|
+
|
|
132
|
+
{state === "approved" ? (
|
|
133
|
+
<Receipt icon="check" tone="text-success" head="You said yes · the agent is on it">
|
|
134
|
+
{approval?.always ? "Always allowed for this kind. It will not ask again for the same." : null}
|
|
135
|
+
</Receipt>
|
|
136
|
+
) : state === "declined" ? (
|
|
137
|
+
<Receipt icon="x" tone="text-muted-foreground" head="Not now. Nothing went out." />
|
|
138
|
+
) : state === "gone" ? (
|
|
139
|
+
<Receipt icon="check" tone="text-muted-foreground" head="This has been answered." />
|
|
140
|
+
) : state === "stale" ? (
|
|
141
|
+
<p role="status" className="flex items-center gap-2 text-muted-foreground">
|
|
142
|
+
<Spinner size="sm" label="Looking again" /> This changed since the card was drawn. Looking again…
|
|
143
|
+
</p>
|
|
144
|
+
) : approval?.frame ? (
|
|
145
|
+
<iframe
|
|
146
|
+
ref={approval.frame.ref}
|
|
147
|
+
src={approval.frame.src}
|
|
148
|
+
srcDoc={approval.frame.srcDoc}
|
|
149
|
+
title={approval.frame.title ?? "Approve"}
|
|
150
|
+
scrolling="no"
|
|
151
|
+
className="block w-full border-0 bg-transparent"
|
|
152
|
+
style={{ height: approval.height ?? FRAME_HEIGHT }}
|
|
153
|
+
/>
|
|
154
|
+
) : null}
|
|
155
|
+
{state === "ready" && approval?.error ? <p className="text-xs text-destructive">{approval.error}</p> : null}
|
|
156
|
+
{state === "gone" && onOpenDesk ? (
|
|
157
|
+
<Button size="sm" variant="quiet" className="justify-self-start" onClick={onOpenDesk}>
|
|
158
|
+
Open it on the desk
|
|
159
|
+
</Button>
|
|
160
|
+
) : null}
|
|
161
|
+
</section>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "approval",
|
|
3
|
+
"title": "Approval",
|
|
4
|
+
"description": "The yes, given in the app. The app draws the card in its own theme — what will happen, to whom, the exact words, the cost — and under it sits the press: a small Makersclaw-origin frame, in the app's colours, that app code cannot script. It shows the server's own words for the request (the agent's question, who, from which account, the first message, the rule \"Always allow this kind\" would save) and only counts a press a person made on a card they can see. Fed by useApproval(requestId) (lib/hooks.js). If the frame cannot load or the member must sign in again, the card falls back to WaitingYes: \"Waiting for your yes on the desk\".",
|
|
5
|
+
"useWhen": "Anywhere the agent is about to do something public or paid and waits for the member's yes — a send, a post, a purchase: put the app's own card (the draft, the recipients, the cost) inside it, give it useApproval(item.id) for the request that waits, and the member says yes without leaving the app. For the queue of everything that waits use needs-you; for the conversation, agent-chat.",
|
|
6
|
+
"tags": [
|
|
7
|
+
"approval",
|
|
8
|
+
"approve",
|
|
9
|
+
"yes",
|
|
10
|
+
"send",
|
|
11
|
+
"confirm",
|
|
12
|
+
"needs you",
|
|
13
|
+
"agent",
|
|
14
|
+
"trust",
|
|
15
|
+
"always allow",
|
|
16
|
+
"rule",
|
|
17
|
+
"desk",
|
|
18
|
+
"publish",
|
|
19
|
+
"spend"
|
|
20
|
+
],
|
|
21
|
+
"exports": [
|
|
22
|
+
"Approval"
|
|
23
|
+
],
|
|
24
|
+
"files": [
|
|
25
|
+
"index.js",
|
|
26
|
+
"approval.jsx"
|
|
27
|
+
],
|
|
28
|
+
"requires": [
|
|
29
|
+
"needs-you"
|
|
30
|
+
]
|
|
31
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// Approval in every state, with fake data shaped as useApproval() returns
|
|
4
|
+
// it — the gallery's and the smoke test's surface. Not installed into
|
|
5
|
+
// apps. The press frame is a page of Makersclaw's in another origin; here
|
|
6
|
+
// a stand-in of the same shape (`srcDoc`) takes its place, so the card's
|
|
7
|
+
// layout can be looked at at every width.
|
|
8
|
+
import { useEffect, useRef, useState } from "react";
|
|
9
|
+
import { Approval } from "../components/blocks/approval";
|
|
10
|
+
|
|
11
|
+
// Inline styles and the browser's own colour names only: the check refuses
|
|
12
|
+
// a hex, a colour function or a <style> tag in a surface, and this is a
|
|
13
|
+
// stand-in, not a design.
|
|
14
|
+
const BOX = "border:1px solid GrayText;border-radius:10px;padding:12px 14px;display:flex;flex-direction:column;gap:10px";
|
|
15
|
+
const BTN = "font:inherit;font-weight:600;padding:8px 14px;border-radius:8px;border:1px solid GrayText;background:transparent;color:inherit;min-height:36px";
|
|
16
|
+
const standIn = (scheme) =>
|
|
17
|
+
`<!doctype html><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="color-scheme" content="${scheme}">` +
|
|
18
|
+
`<body style="margin:0;background:transparent;font:14px/1.45 system-ui,sans-serif">` +
|
|
19
|
+
`<main style="${BOX}"><span style="font-size:11px;letter-spacing:.06em;text-transform:uppercase;opacity:.65">Waiting for your yes</span>` +
|
|
20
|
+
`<p style="margin:0;font-size:15px;font-weight:600">Send 3 follow-ups from ada@acme.com to the Replies list?</p>` +
|
|
21
|
+
`<div style="display:grid;grid-template-columns:auto 1fr;gap:2px 10px;font-size:13px"><span style="opacity:.65">To</span><span>3 investors on the Replies list</span>` +
|
|
22
|
+
`<span style="opacity:.65">From</span><span>ada@acme.com</span><span style="opacity:.65">First to</span><span>Maya Chen</span></div>` +
|
|
23
|
+
`<pre style="margin:0;padding:8px 10px;border-radius:8px;border:1px solid GrayText;font:13px/1.4 inherit;white-space:pre-wrap">Hi Maya, a short note on where we are…</pre>` +
|
|
24
|
+
`<label style="display:flex;gap:8px;font-size:13px"><input type="checkbox"><span>Always allow this kind: Send messages like “Quick question about Northwind” to the Replies list, from ada@acme.com.</span></label>` +
|
|
25
|
+
`<div style="display:flex;gap:8px;justify-content:flex-end;flex-wrap:wrap"><button style="${BTN}">Not now</button><button style="${BTN};background:Highlight;color:HighlightText;border-color:transparent">Send 3</button></div></main>`;
|
|
26
|
+
|
|
27
|
+
// The real frame wears the app's theme sheet, whose :root names the same
|
|
28
|
+
// colour scheme as the page around it, so its transparent ground stays
|
|
29
|
+
// transparent. The stand-in reads the page's scheme for the same reason.
|
|
30
|
+
function useScheme() {
|
|
31
|
+
const [scheme, setScheme] = useState("light dark");
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
const read = getComputedStyle(document.documentElement).colorScheme;
|
|
34
|
+
if (read && read !== "normal") setScheme(read);
|
|
35
|
+
}, []);
|
|
36
|
+
return scheme;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const FACTS = {
|
|
40
|
+
request_id: "r1",
|
|
41
|
+
question: "Send 3 follow-ups from ada@acme.com to the Replies list?",
|
|
42
|
+
label: "Send 3",
|
|
43
|
+
covers: null,
|
|
44
|
+
always: "Send messages like “Quick question about Northwind” to the Replies list, from ada@acme.com.",
|
|
45
|
+
cap_credits: null,
|
|
46
|
+
at: "t1",
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/** What useApproval() returns, for a state. */
|
|
50
|
+
function fake(state, scheme, over = {}) {
|
|
51
|
+
const ref = useRef(null);
|
|
52
|
+
const framed = state === "ready";
|
|
53
|
+
return {
|
|
54
|
+
approval: FACTS,
|
|
55
|
+
state,
|
|
56
|
+
always: false,
|
|
57
|
+
fallback: state === "signin" || state === "blocked" || state === "unavailable" || state === "error",
|
|
58
|
+
frame: framed ? { src: undefined, srcDoc: standIn(scheme), ref, title: "Approve" } : null,
|
|
59
|
+
height: 440,
|
|
60
|
+
loading: state === "loading",
|
|
61
|
+
error: null,
|
|
62
|
+
code: null,
|
|
63
|
+
unavailable: state === "unavailable",
|
|
64
|
+
reload: () => {},
|
|
65
|
+
...over,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const DRAFT = (
|
|
70
|
+
<div className="grid gap-2">
|
|
71
|
+
<p className="text-muted-foreground">3 follow-ups, ready to go</p>
|
|
72
|
+
<ul className="grid gap-1 pl-4 [list-style:disc]">
|
|
73
|
+
<li>Maya Chen · Northwind</li>
|
|
74
|
+
<li>Priya Nair · Harbor Seed</li>
|
|
75
|
+
<li>Tom Osei · Coastline</li>
|
|
76
|
+
</ul>
|
|
77
|
+
<p className="whitespace-pre-wrap rounded-[var(--radius)] bg-muted p-2.5">{"Hi Maya,\n\nA short note on where we are after the demo. Happy to answer anything before Friday."}</p>
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
export default function ApprovalDemo() {
|
|
82
|
+
const scheme = useScheme();
|
|
83
|
+
const ready = fake("ready", scheme);
|
|
84
|
+
const approved = fake("approved", scheme, { always: true });
|
|
85
|
+
const declined = fake("declined", scheme);
|
|
86
|
+
const stale = fake("stale", scheme);
|
|
87
|
+
const gone = fake("gone", scheme);
|
|
88
|
+
const loading = fake("loading", scheme);
|
|
89
|
+
const signin = fake("signin", scheme);
|
|
90
|
+
const blocked = fake("blocked", scheme);
|
|
91
|
+
const unavailable = fake("unavailable", scheme);
|
|
92
|
+
return (
|
|
93
|
+
<div className="grid gap-4 p-6 [grid-template-columns:repeat(auto-fit,minmax(min(100%,22rem),1fr))]">
|
|
94
|
+
<Approval approval={ready} title="Send 3 follow-ups" cost={<span>about 12 cr</span>} onOpenDesk={() => true}>
|
|
95
|
+
{DRAFT}
|
|
96
|
+
</Approval>
|
|
97
|
+
<Approval approval={approved} title="Send 3 follow-ups">
|
|
98
|
+
{DRAFT}
|
|
99
|
+
</Approval>
|
|
100
|
+
<Approval approval={declined} title="Send 3 follow-ups">
|
|
101
|
+
{DRAFT}
|
|
102
|
+
</Approval>
|
|
103
|
+
<Approval approval={stale} title="Send 3 follow-ups">
|
|
104
|
+
{DRAFT}
|
|
105
|
+
</Approval>
|
|
106
|
+
<Approval approval={gone} title="Send 3 follow-ups" onOpenDesk={() => true}>
|
|
107
|
+
{DRAFT}
|
|
108
|
+
</Approval>
|
|
109
|
+
<Approval approval={loading} title="Send 3 follow-ups" />
|
|
110
|
+
<Approval approval={signin} title="Send 3 follow-ups" onOpenDesk={() => true}>
|
|
111
|
+
{DRAFT}
|
|
112
|
+
</Approval>
|
|
113
|
+
<Approval approval={blocked} title="Send 3 follow-ups" onOpenDesk={() => true}>
|
|
114
|
+
{DRAFT}
|
|
115
|
+
</Approval>
|
|
116
|
+
<Approval approval={unavailable} title="Send 3 follow-ups">
|
|
117
|
+
{DRAFT}
|
|
118
|
+
</Approval>
|
|
119
|
+
</div>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# AskBar
|
|
2
|
+
|
|
3
|
+
One line to ask the app's agent, in the app's look. Chips under it offer
|
|
4
|
+
asks that fit the moment; a chip sends its words. Long chips wrap; they
|
|
5
|
+
never run off the screen.
|
|
6
|
+
|
|
7
|
+
After a send the bar says where the ask stands, in one line:
|
|
8
|
+
|
|
9
|
+
- "Working…" while the agent is on it;
|
|
10
|
+
- "Waiting for you" when it needs the member's yes or answer;
|
|
11
|
+
- "Answered", "Stopped", or "It didn't finish".
|
|
12
|
+
|
|
13
|
+
Beside the line, **Open** shows the conversation. Where it shows is the
|
|
14
|
+
screen's choice: a `Sheet` with the `agent-chat` block, the inspector,
|
|
15
|
+
a side column. When the agent waits on a question or a yes the app can
|
|
16
|
+
settle (`chat.needs.in_app` is `"answer"` or `"press"`), Open shows that
|
|
17
|
+
chat, where the answer is given (`agent-chat`) and the yes is pressed
|
|
18
|
+
(`approval`). A need the app cannot settle goes to the desk instead
|
|
19
|
+
(`chat.openOnDesk()`).
|
|
20
|
+
|
|
21
|
+
It is a door into the conversation, not the conversation. The thread is
|
|
22
|
+
the same one `agent-chat` and the desk's agent column draw.
|
|
23
|
+
|
|
24
|
+
```jsx
|
|
25
|
+
import { AskBar } from "../components/blocks/ask-bar"; // a Makersclaw app ("./blocks/ask-bar" from components/)
|
|
26
|
+
import { AskBar } from "@/components/makersclaw/blocks/ask-bar"; // a shadcn project
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
A client component. The Example is at the end.
|
|
30
|
+
|
|
31
|
+
## Props
|
|
32
|
+
|
|
33
|
+
- `chat` — what `useAgentThread()` returns (lib/hooks.js). A send calls
|
|
34
|
+
`chat.send(text)`; the line reads `chat.state`, the last ask from
|
|
35
|
+
`chat.turns` or `chat.pending`, and `chat.error`.
|
|
36
|
+
- `onAsk(text)` — instead of `chat`: your own sender. It may be async; an
|
|
37
|
+
answer of `{ ok: false, error }` (or a throw) shows its words under the
|
|
38
|
+
bar.
|
|
39
|
+
- `state` — with `onAsk` only: where your ask stands, in
|
|
40
|
+
`useAgentThread`'s words (`queued` · `working` · `needs_you` · `done` ·
|
|
41
|
+
`stopped` · `failed`).
|
|
42
|
+
- `suggestions` — asks that fit the moment, as chips. Say them in the
|
|
43
|
+
app's words ("Who should I call today?").
|
|
44
|
+
- `placeholder` — the box's words. Default "Ask the agent…".
|
|
45
|
+
- `label` — the box's name for a screen reader. Default "Ask the agent".
|
|
46
|
+
- `onOpen()` — shows the conversation. Without it there is no Open.
|
|
47
|
+
- `openOnSend` — `true`: a send also calls `onOpen`, so the chat opens at
|
|
48
|
+
once.
|
|
49
|
+
- `className` — layout around it. Never colour.
|
|
50
|
+
|
|
51
|
+
## Which hook feeds it
|
|
52
|
+
|
|
53
|
+
`useAgentThread({ row, how, key })` (lib/hooks.js) over
|
|
54
|
+
`POST _claw/ask` and `GET _claw/threads/<session>`. Give the same `row`
|
|
55
|
+
the chat on the same screen gets, and both draw one thread.
|
|
56
|
+
|
|
57
|
+
## Who may do what
|
|
58
|
+
|
|
59
|
+
Any member who can open the app may ask. Each member sees only their own
|
|
60
|
+
thread. Stop lives in the chat (`agent-chat`), not here.
|
|
61
|
+
|
|
62
|
+
## Never
|
|
63
|
+
|
|
64
|
+
- **Never an Approve.** The yes is the `approval` block's frame, which the
|
|
65
|
+
app's code cannot press. The bar says "Waiting for you" and Open shows
|
|
66
|
+
the chat that holds it.
|
|
67
|
+
- Never answer the agent's question here: that is the chat's
|
|
68
|
+
`QuestionBox`.
|
|
69
|
+
- Never show the agent's answer in the bar. One line of state, then Open.
|
|
70
|
+
|
|
71
|
+
## Outside Makersclaw
|
|
72
|
+
|
|
73
|
+
Any object of the same shape works: `state`, `turns` (`[{ member }]`),
|
|
74
|
+
`pending`, `error`, `unavailable`, `send(text)` and `openOnDesk()`. Or
|
|
75
|
+
use `onAsk` and `state`.
|
|
76
|
+
|
|
77
|
+
## Example
|
|
78
|
+
|
|
79
|
+
A home screen with the ask on top and the chat in a sheet.
|
|
80
|
+
|
|
81
|
+
```jsx
|
|
82
|
+
"use client";
|
|
83
|
+
import { useState } from "react";
|
|
84
|
+
import { Sheet } from "../components/ui";
|
|
85
|
+
import { useAgentThread } from "../lib/hooks";
|
|
86
|
+
import { AskBar } from "../components/blocks/ask-bar";
|
|
87
|
+
import { AgentChat } from "../components/blocks/agent-chat";
|
|
88
|
+
|
|
89
|
+
export default function Today() {
|
|
90
|
+
const [open, setOpen] = useState(false);
|
|
91
|
+
const chat = useAgentThread({ key: "today" });
|
|
92
|
+
return (
|
|
93
|
+
<>
|
|
94
|
+
<AskBar
|
|
95
|
+
chat={chat}
|
|
96
|
+
suggestions={["Who should I call today?", "What changed since Friday?"]}
|
|
97
|
+
onOpen={() => setOpen(true)}
|
|
98
|
+
/>
|
|
99
|
+
<Sheet open={open} onClose={() => setOpen(false)} title="Ask the agent">
|
|
100
|
+
<AgentChat chat={chat} bare />
|
|
101
|
+
</Sheet>
|
|
102
|
+
</>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
```
|