makersclaw 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +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 +225 -0
- package/registry/blocks/agent-pane/agent-pane.jsx +244 -0
- package/registry/blocks/agent-pane/block.json +41 -0
- package/registry/blocks/agent-pane/composer.jsx +6 -0
- package/registry/blocks/agent-pane/demo.jsx +233 -0
- package/registry/blocks/agent-pane/dock-context.js +10 -0
- package/registry/blocks/agent-pane/index.js +4 -0
- package/registry/blocks/agent-pane/prose.jsx +84 -0
- package/registry/blocks/agent-pane/questions.jsx +134 -0
- package/registry/blocks/agent-pane/transcript.jsx +268 -0
- package/registry/blocks/agent-pane/work.jsx +368 -0
- package/registry/blocks/ai-action/README.md +94 -0
- package/registry/blocks/ai-action/ai-action.jsx +188 -0
- package/registry/blocks/ai-action/block.json +34 -0
- package/registry/blocks/ai-action/demo.jsx +93 -0
- package/registry/blocks/ai-action/index.js +3 -0
- package/registry/blocks/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 +1627 -8
- package/templates/next/files/lib/platform.js +529 -21
- package/templates/next/template.json +13 -3
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useId, useMemo, useState } from "react";
|
|
4
|
+
import { Button, Checkbox, Field, Input, Select, Tabs, TabPanel, Textarea, cx } from "../../ui";
|
|
5
|
+
|
|
6
|
+
// ColumnPicker — choose the columns an agent will fill: common ones from
|
|
7
|
+
// a list the app passes in (each with its cost per record), or the
|
|
8
|
+
// person's own question with the kind of answer. It says what they cost
|
|
9
|
+
// together before anything starts. It saves nothing: onSubmit gets the
|
|
10
|
+
// choice.
|
|
11
|
+
//
|
|
12
|
+
// recipes [{ key, name, description, credits, input?: "role" | "instructions",
|
|
13
|
+
// defaultRole?, needs?: ["Domain"], checked? }]
|
|
14
|
+
// rows how many records the columns would fill (for the total)
|
|
15
|
+
// heading the common tab's label ("Common, for companies")
|
|
16
|
+
// onSubmit([{ recipe, name, ask?, example?, answerType?, options? }])
|
|
17
|
+
// submitLabel(n) the button's words ("Add 6 columns")
|
|
18
|
+
// answerTypes [{ value, label }] for your own question
|
|
19
|
+
// ownCredits what your own question costs per record
|
|
20
|
+
|
|
21
|
+
const ANSWER_TYPES = [
|
|
22
|
+
{ value: "yes_no", label: "Yes or no" },
|
|
23
|
+
{ value: "number", label: "A number" },
|
|
24
|
+
{ value: "text", label: "A short text" },
|
|
25
|
+
{ value: "link", label: "A link" },
|
|
26
|
+
{ value: "choice", label: "One of a few choices" },
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
export function ColumnPicker({
|
|
30
|
+
recipes,
|
|
31
|
+
rows = 0,
|
|
32
|
+
heading = "Common",
|
|
33
|
+
onSubmit,
|
|
34
|
+
submitLabel = (n) => (n === 1 ? "Add 1 column" : `Add ${n} columns`),
|
|
35
|
+
answerTypes = ANSWER_TYPES,
|
|
36
|
+
ownCredits = 50,
|
|
37
|
+
busy = false,
|
|
38
|
+
unit = "credits",
|
|
39
|
+
noun = "row",
|
|
40
|
+
}) {
|
|
41
|
+
const id = useId();
|
|
42
|
+
const [tab, setTab] = useState("common");
|
|
43
|
+
const [picked, setPicked] = useState(() => Object.fromEntries(recipes.filter((r) => r.checked).map((r) => [r.key, true])));
|
|
44
|
+
const [roles, setRoles] = useState(() => Object.fromEntries(recipes.filter((r) => r.input === "role").map((r) => [r.key, r.defaultRole ?? ""])));
|
|
45
|
+
const [how, setHow] = useState(() => Object.fromEntries(recipes.filter((r) => r.input === "instructions").map((r) => [r.key, { ask: r.defaultAsk ?? "", example: r.defaultExample ?? "" }])));
|
|
46
|
+
const [own, setOwn] = useState({ question: "", name: "", answerType: "yes_no", options: "" });
|
|
47
|
+
|
|
48
|
+
const chosen = useMemo(() => {
|
|
49
|
+
const out = recipes
|
|
50
|
+
.filter((r) => picked[r.key])
|
|
51
|
+
.map((r) => ({
|
|
52
|
+
recipe: r.key,
|
|
53
|
+
name: r.input === "role" ? roles[r.key]?.trim() || r.name : r.name,
|
|
54
|
+
ask: r.input === "role" ? roles[r.key]?.trim() : r.input === "instructions" ? how[r.key]?.ask?.trim() : undefined,
|
|
55
|
+
example: r.input === "instructions" ? how[r.key]?.example?.trim() : undefined,
|
|
56
|
+
credits: r.credits,
|
|
57
|
+
}));
|
|
58
|
+
if (own.question.trim())
|
|
59
|
+
out.push({
|
|
60
|
+
recipe: "question",
|
|
61
|
+
name: own.name.trim() || own.question.trim().slice(0, 40),
|
|
62
|
+
ask: own.question.trim(),
|
|
63
|
+
answerType: own.answerType,
|
|
64
|
+
options: own.answerType === "choice" ? own.options.split(",").map((s) => s.trim()).filter(Boolean) : [],
|
|
65
|
+
credits: ownCredits,
|
|
66
|
+
});
|
|
67
|
+
return out;
|
|
68
|
+
}, [recipes, picked, roles, how, own, ownCredits]);
|
|
69
|
+
const perRow = chosen.reduce((n, c) => n + (c.credits ?? 0), 0);
|
|
70
|
+
const missingRole = recipes.some((r) => picked[r.key] && r.input === "role" && !roles[r.key]?.trim());
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<div className="grid gap-4">
|
|
74
|
+
<Tabs
|
|
75
|
+
aria-label="Which columns"
|
|
76
|
+
id={id}
|
|
77
|
+
value={tab}
|
|
78
|
+
onChange={setTab}
|
|
79
|
+
tabs={[
|
|
80
|
+
{ key: "common", label: heading },
|
|
81
|
+
{ key: "own", label: "Your own question" },
|
|
82
|
+
]}
|
|
83
|
+
/>
|
|
84
|
+
<TabPanel tab="common" value={tab} id={id} className="pt-0">
|
|
85
|
+
<ul className="grid divide-y divide-border">
|
|
86
|
+
{recipes.map((r) => {
|
|
87
|
+
const on = Boolean(picked[r.key]);
|
|
88
|
+
return (
|
|
89
|
+
<li key={r.key} className="grid gap-2 py-2">
|
|
90
|
+
<div className="flex items-start gap-3">
|
|
91
|
+
<Checkbox
|
|
92
|
+
id={`${id}-${r.key}`}
|
|
93
|
+
checked={on}
|
|
94
|
+
onChange={(e) => setPicked((p) => ({ ...p, [r.key]: e.target.checked }))}
|
|
95
|
+
aria-label={r.name}
|
|
96
|
+
className="min-h-0 pt-0.5"
|
|
97
|
+
/>
|
|
98
|
+
<label htmlFor={`${id}-${r.key}`} className="min-w-0 flex-1 cursor-pointer">
|
|
99
|
+
<span className="block text-sm font-medium">{r.name}</span>
|
|
100
|
+
{r.description ? <span className="block text-xs text-muted-foreground">{r.description}</span> : null}
|
|
101
|
+
</label>
|
|
102
|
+
<span data-nums className="shrink-0 font-mono text-xs text-muted-foreground">
|
|
103
|
+
{r.credits} / {noun}
|
|
104
|
+
</span>
|
|
105
|
+
</div>
|
|
106
|
+
{on && r.input === "role" ? (
|
|
107
|
+
<Field id={`${id}-${r.key}-role`} label="Role" className="ml-7">
|
|
108
|
+
<Input id={`${id}-${r.key}-role`} value={roles[r.key] ?? ""} onChange={(e) => setRoles((s) => ({ ...s, [r.key]: e.target.value }))} placeholder="Head of Compliance" />
|
|
109
|
+
</Field>
|
|
110
|
+
) : null}
|
|
111
|
+
{on && r.input === "instructions" ? (
|
|
112
|
+
<div className="ml-7 grid gap-3">
|
|
113
|
+
<Field id={`${id}-${r.key}-how`} label="How to write it">
|
|
114
|
+
<Textarea id={`${id}-${r.key}-how`} rows={3} value={how[r.key]?.ask ?? ""} onChange={(e) => setHow((s) => ({ ...s, [r.key]: { ...s[r.key], ask: e.target.value } }))} />
|
|
115
|
+
</Field>
|
|
116
|
+
<Field id={`${id}-${r.key}-ex`} label="A line of yours, as an example">
|
|
117
|
+
<Textarea id={`${id}-${r.key}-ex`} rows={2} value={how[r.key]?.example ?? ""} onChange={(e) => setHow((s) => ({ ...s, [r.key]: { ...s[r.key], example: e.target.value } }))} />
|
|
118
|
+
</Field>
|
|
119
|
+
</div>
|
|
120
|
+
) : null}
|
|
121
|
+
</li>
|
|
122
|
+
);
|
|
123
|
+
})}
|
|
124
|
+
</ul>
|
|
125
|
+
</TabPanel>
|
|
126
|
+
<TabPanel tab="own" value={tab} id={id} className="grid gap-3 pt-0">
|
|
127
|
+
<Field id={`${id}-q`} label="Your question" hint="The agent answers it for every row, from the public web.">
|
|
128
|
+
<Textarea id={`${id}-q`} rows={2} value={own.question} onChange={(e) => setOwn((s) => ({ ...s, question: e.target.value }))} placeholder="Do they sell to banks?" />
|
|
129
|
+
</Field>
|
|
130
|
+
<Field id={`${id}-n`} label="Column name">
|
|
131
|
+
<Input id={`${id}-n`} value={own.name} onChange={(e) => setOwn((s) => ({ ...s, name: e.target.value }))} placeholder="Sells to banks" />
|
|
132
|
+
</Field>
|
|
133
|
+
<Field id={`${id}-t`} label="The kind of answer">
|
|
134
|
+
<Select id={`${id}-t`} value={own.answerType} onChange={(e) => setOwn((s) => ({ ...s, answerType: e.target.value }))} options={answerTypes} />
|
|
135
|
+
</Field>
|
|
136
|
+
{own.answerType === "choice" ? (
|
|
137
|
+
<Field id={`${id}-o`} label="The choices" hint="Separated by commas.">
|
|
138
|
+
<Input id={`${id}-o`} value={own.options} onChange={(e) => setOwn((s) => ({ ...s, options: e.target.value }))} placeholder="Banks, Fintechs, Both, Neither" />
|
|
139
|
+
</Field>
|
|
140
|
+
) : null}
|
|
141
|
+
<p data-nums className="font-mono text-xs text-muted-foreground">
|
|
142
|
+
{ownCredits} {unit} / {noun}
|
|
143
|
+
</p>
|
|
144
|
+
</TabPanel>
|
|
145
|
+
<div className="grid gap-2 border-t border-border pt-3">
|
|
146
|
+
<p className="text-sm">
|
|
147
|
+
<span className="font-medium">
|
|
148
|
+
{chosen.length} {chosen.length === 1 ? "column" : "columns"}
|
|
149
|
+
</span>{" "}
|
|
150
|
+
· <span data-nums className="font-mono">{perRow.toLocaleString("en-US")}</span> {unit} a {noun}
|
|
151
|
+
</p>
|
|
152
|
+
{rows ? (
|
|
153
|
+
<p className="text-xs text-muted-foreground">
|
|
154
|
+
All {rows.toLocaleString("en-US")} {rows === 1 ? noun : `${noun}s`}: about <span data-nums className="font-mono">{(perRow * rows).toLocaleString("en-US")}</span>. Nothing is spent until you start a fill.
|
|
155
|
+
</p>
|
|
156
|
+
) : null}
|
|
157
|
+
<Button variant="primary" disabled={busy || chosen.length === 0 || missingRole} loading={busy} onClick={() => onSubmit?.(chosen)} className={cx("justify-self-start")}>
|
|
158
|
+
{submitLabel(chosen.length)}
|
|
159
|
+
</Button>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// AgentColumn's pieces over answers in every state (found, two answers,
|
|
4
|
+
// not found, failed, queued, looking up, not asked) — the gallery's and
|
|
5
|
+
// the smoke test's surface. Not installed into apps.
|
|
6
|
+
import { useState } from "react";
|
|
7
|
+
import { AnswerCell, AnswerDetail, ColumnHeader, ColumnPicker, Receipt, SureLegend, summarizeAnswers } from "../components/blocks/agent-column";
|
|
8
|
+
|
|
9
|
+
const ANSWERS = [
|
|
10
|
+
{ status: "found", value: "62", detail: "51–200 on LinkedIn", confidence: "likely", source: { title: "Their team page", url: "pennantpay.com/team", evidence: "62 people building payouts" } },
|
|
11
|
+
{ status: "two_answers", value: "85", other: { value: "70", source: { title: "Careers at Klarwerk", url: "klarwerk.de/careers", evidence: "We're a team of 70" } }, source: { title: "Klarwerk on LinkedIn", url: "linkedin.com/company/klarwerk", evidence: "85 members" } },
|
|
12
|
+
{ status: "not_found", note: "Searched the team page, LinkedIn and the news.", tried: [{ title: "Their team page", url: "spreeloop.io/team" }] },
|
|
13
|
+
{ status: "queued", waitsFor: ["Domain"] },
|
|
14
|
+
{ status: "working", looking: "reading gleisneun.de" },
|
|
15
|
+
{ status: "failed", detail: "the page timed out", tried: [{ title: "Their team page", url: "nordlicht.io/team" }] },
|
|
16
|
+
null,
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
export default function Demo() {
|
|
20
|
+
const [open, setOpen] = useState(0);
|
|
21
|
+
return (
|
|
22
|
+
<div className="grid gap-6 p-6">
|
|
23
|
+
<SureLegend />
|
|
24
|
+
<ColumnHeader name="Headcount" progress={summarizeAnswers(ANSWERS)} />
|
|
25
|
+
<div className="grid w-80 max-w-full gap-3">
|
|
26
|
+
{ANSWERS.map((a, i) => (
|
|
27
|
+
<AnswerCell key={i} answer={a} type="number" onOpen={() => setOpen(i)} label={`Answer ${i + 1}`} />
|
|
28
|
+
))}
|
|
29
|
+
</div>
|
|
30
|
+
<div className="w-full max-w-md">
|
|
31
|
+
<Receipt answer={ANSWERS[1]} title="Klarwerk" question="Headcount" read="read 22 Sep" onPick={() => {}} />
|
|
32
|
+
</div>
|
|
33
|
+
<div className="w-full max-w-md">
|
|
34
|
+
<AnswerDetail answer={ANSWERS[open]} title="Klarwerk" question="Headcount" askCost={40} onPick={() => {}} onType={() => {}} onAskAgain={() => {}} onAccept={() => {}} onKeepNotFound={() => {}} />
|
|
35
|
+
</div>
|
|
36
|
+
<div className="max-w-md">
|
|
37
|
+
<ColumnPicker
|
|
38
|
+
rows={22}
|
|
39
|
+
heading="Common, for companies"
|
|
40
|
+
recipes={[
|
|
41
|
+
{ key: "domain", name: "Domain", description: "Their website's address", credits: 15, checked: true },
|
|
42
|
+
{ key: "person", name: "A person in a role", description: "Their name, title and profile", credits: 80, input: "role", defaultRole: "Head of Compliance", checked: true },
|
|
43
|
+
{ key: "opener", name: "Opener", description: "An opening line, written the way you write", credits: 30, input: "instructions" },
|
|
44
|
+
]}
|
|
45
|
+
onSubmit={() => {}}
|
|
46
|
+
/>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// The AgentColumn block — `makersclaw add agent-column`. Fixed: compose it
|
|
2
|
+
// through its props (README.md beside this file).
|
|
3
|
+
export { AnswerCell, AnswerDetail, ColumnHeader, SureMark, SureLegend, Source } from "./agent-column";
|
|
4
|
+
export { AgentMark, AnswerLine, Clipping, JobHeader, Receipt, ReceiptActions, TriedList, TrustMark } from "./receipt";
|
|
5
|
+
export { ColumnPicker } from "./column-picker";
|
|
6
|
+
export { answerState, answerWords, needsCheck, summarizeAnswers, progressWords, markWords, decidingWords, plainWords, shortUrl, CONFIDENCE } from "./model";
|
|
7
|
+
export { answerColumnType } from "./spreadsheet-type";
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// agent-column — the pure half: what state an answer is in, what it says
|
|
2
|
+
// in words, whether a person should look at it, and a column's progress.
|
|
3
|
+
// Server-safe (no React), so a server surface can count the same way the
|
|
4
|
+
// screen draws.
|
|
5
|
+
//
|
|
6
|
+
// An answer is one cell an agent fills (or suggests) for one record:
|
|
7
|
+
//
|
|
8
|
+
// {
|
|
9
|
+
// id, status, // queued | working | found | not_found | two_answers | failed | stale | suggested
|
|
10
|
+
// value, detail, link, // the answer ("62"), a line under it ("51–200 on LinkedIn"), a URL
|
|
11
|
+
// confidence, // sure | likely | guess
|
|
12
|
+
// source: { url, title, evidence }, // where it came from, and the words on the page
|
|
13
|
+
// other: { value, detail, link, source: { url, title, evidence } }, // the second answer
|
|
14
|
+
// note, // how it was found, or what was tried
|
|
15
|
+
// tried: [{ title, url, evidence }], // what it read (not found)
|
|
16
|
+
// flag, // may_not_fit | left (or your own word)
|
|
17
|
+
// setBy, // agent | person — a person's value is never overwritten
|
|
18
|
+
// checked, // a person accepted it
|
|
19
|
+
// looking, // what the agent is reading now (working)
|
|
20
|
+
// waitsFor: ["Domain"], // columns it waits for (queued)
|
|
21
|
+
// blockedBy: ["Head of Compliance"], // columns it needs that weren't found (queued)
|
|
22
|
+
// waitsForRow, // it reads the whole record and goes last
|
|
23
|
+
// }
|
|
24
|
+
//
|
|
25
|
+
// A missing answer (null) is "not asked yet".
|
|
26
|
+
|
|
27
|
+
export const CONFIDENCE = {
|
|
28
|
+
sure: { label: "sure", words: "their own page says it, or two sources agree" },
|
|
29
|
+
likely: { label: "likely", words: "one good source" },
|
|
30
|
+
guess: { label: "guess", words: "worked out, not found written" },
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/** The state an answer shows, one word the screen can switch on. */
|
|
34
|
+
export function answerState(a) {
|
|
35
|
+
if (!a) return "empty";
|
|
36
|
+
if (a.setBy === "person") return "typed";
|
|
37
|
+
const s = a.status;
|
|
38
|
+
if (s === "queued") {
|
|
39
|
+
if (a.blockedBy?.length) return "blocked";
|
|
40
|
+
if (a.waitsFor?.length || a.waitsForRow) return "waiting";
|
|
41
|
+
return "queued";
|
|
42
|
+
}
|
|
43
|
+
if (s === "found" && a.checked) return "checked";
|
|
44
|
+
return s || "empty";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const list = (names) => (names.length <= 1 ? names[0] ?? "" : `${names.slice(0, -1).join(", ")} and ${names[names.length - 1]}`);
|
|
48
|
+
|
|
49
|
+
/** What the answer's state says, in plain words ("Waits for Domain"). */
|
|
50
|
+
export function answerWords(a) {
|
|
51
|
+
switch (answerState(a)) {
|
|
52
|
+
case "empty":
|
|
53
|
+
return "not asked yet";
|
|
54
|
+
case "queued":
|
|
55
|
+
return "Queued";
|
|
56
|
+
case "waiting":
|
|
57
|
+
return a.waitsFor?.length ? `Waits for ${list(a.waitsFor)}` : "Waits for the rest of the row";
|
|
58
|
+
case "blocked":
|
|
59
|
+
return "Can't fill";
|
|
60
|
+
case "working":
|
|
61
|
+
return "Looking up";
|
|
62
|
+
case "not_found":
|
|
63
|
+
return "Not found";
|
|
64
|
+
case "two_answers":
|
|
65
|
+
return "two answers";
|
|
66
|
+
case "failed":
|
|
67
|
+
return "Failed";
|
|
68
|
+
case "stale":
|
|
69
|
+
return "out of date";
|
|
70
|
+
case "suggested":
|
|
71
|
+
return "suggested";
|
|
72
|
+
case "checked":
|
|
73
|
+
return "checked by you";
|
|
74
|
+
case "typed":
|
|
75
|
+
return "typed by you";
|
|
76
|
+
default:
|
|
77
|
+
return CONFIDENCE[a.confidence]?.label ?? "found";
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** A person should look at it: nobody accepted it, and it is not found,
|
|
82
|
+
* two answers, failed, a guess, a flag, or a suggestion. */
|
|
83
|
+
export function needsCheck(a) {
|
|
84
|
+
if (!a || a.checked || a.setBy === "person") return false;
|
|
85
|
+
if (["not_found", "two_answers", "failed", "suggested"].includes(a.status)) return true;
|
|
86
|
+
return a.status === "found" && (a.confidence === "guess" || Boolean(a.flag));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** A column's progress over its answers (null ones count as not asked). */
|
|
90
|
+
export function summarizeAnswers(answers, total = answers.length) {
|
|
91
|
+
const out = { total, done: 0, toCheck: 0, waiting: 0, looking: 0, queued: 0, stale: 0, blocked: 0 };
|
|
92
|
+
for (const a of answers) {
|
|
93
|
+
const st = answerState(a);
|
|
94
|
+
if (["found", "checked", "typed", "not_found", "two_answers", "failed"].includes(st)) out.done += 1;
|
|
95
|
+
if (needsCheck(a)) out.toCheck += 1;
|
|
96
|
+
if (st === "waiting") out.waiting += 1;
|
|
97
|
+
if (st === "working") out.looking += 1;
|
|
98
|
+
if (st === "queued") out.queued += 1;
|
|
99
|
+
if (st === "stale") out.stale += 1;
|
|
100
|
+
if (st === "blocked") out.blocked += 1;
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** The progress line under a column's name: "138 of 480 · 3 to check". */
|
|
106
|
+
export function progressWords(p, { lookingWord = "looking" } = {}) {
|
|
107
|
+
const parts = [`${p.done.toLocaleString("en-US")} of ${p.total.toLocaleString("en-US")}`];
|
|
108
|
+
if (p.toCheck) parts.push(`${p.toCheck} to check`);
|
|
109
|
+
if (p.looking) parts.push(`${p.looking} ${lookingWord}`);
|
|
110
|
+
if (p.waiting) parts.push(`${p.waiting} waiting`);
|
|
111
|
+
if (p.stale) parts.push(`${p.stale} out of date`);
|
|
112
|
+
return parts.join(" · ");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Split `text` around the occurrences of `mark` (the answer inside its
|
|
116
|
+
* quote), for highlighting: [{ text, marked }]. */
|
|
117
|
+
export function markWords(text, mark) {
|
|
118
|
+
const t = String(text ?? "");
|
|
119
|
+
const m = String(mark ?? "").trim();
|
|
120
|
+
if (!t || !m || m.length > 60) return [{ text: t, marked: false }];
|
|
121
|
+
const lower = t.toLowerCase();
|
|
122
|
+
const needle = m.toLowerCase();
|
|
123
|
+
const out = [];
|
|
124
|
+
let at = 0;
|
|
125
|
+
for (;;) {
|
|
126
|
+
const i = lower.indexOf(needle, at);
|
|
127
|
+
if (i < 0) break;
|
|
128
|
+
if (i > at) out.push({ text: t.slice(at, i), marked: false });
|
|
129
|
+
out.push({ text: t.slice(i, i + m.length), marked: true });
|
|
130
|
+
at = i + m.length;
|
|
131
|
+
}
|
|
132
|
+
if (at < t.length) out.push({ text: t.slice(at), marked: false });
|
|
133
|
+
return out.length ? out : [{ text: t, marked: false }];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** A URL without its scheme and "www.", for reading ("klarwerk.de/careers"). */
|
|
137
|
+
export function shortUrl(url) {
|
|
138
|
+
return String(url ?? "")
|
|
139
|
+
.replace(/^https?:\/\//i, "")
|
|
140
|
+
.replace(/^www\./i, "")
|
|
141
|
+
.replace(/\/$/, "");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** The words on a page with the deciding ones marked: `[[…]]` in the
|
|
145
|
+
* words marks them (the agent marks what decides the answer), else the
|
|
146
|
+
* answer's value where it appears (markWords). → [{ text, marked }] */
|
|
147
|
+
export function decidingWords(text, mark) {
|
|
148
|
+
const t = String(text ?? "");
|
|
149
|
+
if (!/\[\[[^\]]+\]\]/.test(t)) return markWords(t, mark);
|
|
150
|
+
const out = [];
|
|
151
|
+
let at = 0;
|
|
152
|
+
for (const m of t.matchAll(/\[\[([^\]]+)\]\]/g)) {
|
|
153
|
+
if (m.index > at) out.push({ text: t.slice(at, m.index), marked: false });
|
|
154
|
+
out.push({ text: m[1], marked: true });
|
|
155
|
+
at = m.index + m[0].length;
|
|
156
|
+
}
|
|
157
|
+
if (at < t.length) out.push({ text: t.slice(at), marked: false });
|
|
158
|
+
return out;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** The words with any `[[…]]` marks taken out, for a line of plain text. */
|
|
162
|
+
export function plainWords(text) {
|
|
163
|
+
return String(text ?? "").replace(/\[\[([^\]]+)\]\]/g, "$1");
|
|
164
|
+
}
|