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
package/r/menubar.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "Menubar",
|
|
6
6
|
"description": "A desktop app's menu bar — File, Edit, View, Help — each a menu of actions with shortcuts, checkboxes, radio groups and submenus; click to open, hover to switch, arrow keys along the bar; one menu button on a phone.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.1/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [],
|
|
11
11
|
"files": [
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"docs": "Installed to components/makersclaw/blocks/menubar. Import: import { Menubar } from \"@/components/makersclaw/blocks/menubar\". Props and an example: components/makersclaw/blocks/menubar/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
46
46
|
"meta": {
|
|
47
47
|
"makersclaw": {
|
|
48
|
-
"version": "0.8.
|
|
48
|
+
"version": "0.8.1",
|
|
49
49
|
"useWhen": "A document-style tool with many commands grouped by kind: an editor, a canvas, a spreadsheet, an admin console that mimics a desktop app. Not for a site's navigation (a navigation menu) or one record's actions (a menu on the row)."
|
|
50
50
|
}
|
|
51
51
|
}
|
package/r/mind-map.json
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
"title": "MindMap",
|
|
6
6
|
"description": "A mind map you can build without the mouse, on flow-canvas: a centre topic with main branches balanced on both sides, each branch a tidy auto-laid-out tree in its own chart hue. Tab adds a subtopic, Enter the next topic, typing renames, arrows walk the map, ⌘↑/⌘↓ fold a branch (a folded one shows how many it hides), ⌥↑/⌥↓ reorder, Shift+Tab moves up a level; drag a topic onto another to move its branch. Undo/redo, a colour per branch, an Outline view, copy a branch as an outline and paste an outline as topics, starter templates. The map is plain JSON (a tree of topics); saves are async callbacks answering { ok }.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
9
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.1/r/core.json",
|
|
9
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.1/r/flow-canvas.json"
|
|
10
10
|
],
|
|
11
11
|
"dependencies": [],
|
|
12
12
|
"files": [
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"docs": "Installed to components/makersclaw/blocks/mind-map. Import: import { MindMap, BRANCH_COLORS, normalizeMap, createTopic, findTopic, updateTopic, insertTopic, removeTopic, moveTopic, layoutMindMap, mindMapToBoard, mindMapToText, mindMapToMarkdown, outlineToMindMap, outlineToTopics, mindMapTemplates } from \"@/components/makersclaw/blocks/mind-map\". Props and an example: components/makersclaw/blocks/mind-map/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
61
61
|
"meta": {
|
|
62
62
|
"makersclaw": {
|
|
63
|
-
"version": "0.8.
|
|
63
|
+
"version": "0.8.1",
|
|
64
64
|
"useWhen": "A screen for thinking in a tree: brainstorming around one idea, planning a project, breaking a goal down, notes from a meeting, a topic outline an agent drafted. Not for free-form boards with stickies and arrows (flow-canvas, whiteboard), a people hierarchy (org-chart), or a process with steps (process-map)."
|
|
65
65
|
}
|
|
66
66
|
}
|
package/r/navigation-menu.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "Navigation Menu",
|
|
6
6
|
"description": "A site's top nav: plain links, and triggers that open a mega-menu panel of links (icon, title, one-line description) with an optional featured card; hover with intent, click, arrow keys and Escape; a menu button and a sheet of collapsible groups on a phone.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.1/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [],
|
|
11
11
|
"files": [
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"docs": "Installed to components/makersclaw/blocks/navigation-menu. Import: import { NavigationMenu } from \"@/components/makersclaw/blocks/navigation-menu\". Props and an example: components/makersclaw/blocks/navigation-menu/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
46
46
|
"meta": {
|
|
47
47
|
"makersclaw": {
|
|
48
|
-
"version": "0.8.
|
|
48
|
+
"version": "0.8.1",
|
|
49
49
|
"useWhen": "The header of a public-facing site or a product with many destinations: a marketing site, a docs hub, a customer portal. Not for an app's own sidebar or a row of tabs that switch views on one page."
|
|
50
50
|
}
|
|
51
51
|
}
|
package/r/needs-you.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "needs-you",
|
|
4
|
+
"type": "registry:block",
|
|
5
|
+
"title": "NeedsYou",
|
|
6
|
+
"description": "What waits on the member, in the app: NeedsYou is the queue of the agent's questions and yeses — a question is answered in place (QuestionBox: its options, and the member's own words where it welcomes them), a yes is the `approval` block's press frame (renderYes), and the rest open the desk (\"Waiting for your yes on the desk\" when the press frame cannot load, \"Connect on the desk\"); WaitingYes is the frozen-text card for that fallback — what goes out, frozen, then \"Waiting for your yes on the desk\", Open the desk and Take the ask back; AgentWorking is the strip while the agent works, with what it is on, Stop, and \"Stop keeps everything written so far\". Fed by useNeedsYou() (lib/hooks.js). Never an Approve in the app's own code: the yes is pressed in the Makersclaw-held frame; account sign-ins and money stay on the desk.",
|
|
7
|
+
"registryDependencies": [
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.1/r/core.json"
|
|
9
|
+
],
|
|
10
|
+
"dependencies": [],
|
|
11
|
+
"files": [
|
|
12
|
+
{
|
|
13
|
+
"path": "registry/blocks/needs-you/index.js",
|
|
14
|
+
"type": "registry:component",
|
|
15
|
+
"target": "@components/makersclaw/blocks/needs-you/index.js",
|
|
16
|
+
"content": "// The NeedsYou block — `makersclaw add needs-you`. Fixed: compose it\n// through its props (README.md beside this file).\nexport { NeedsYou, QuestionBox, WaitingYes, AgentWorking } from \"./needs-you\";\n"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"path": "registry/blocks/needs-you/needs-you.jsx",
|
|
20
|
+
"type": "registry:component",
|
|
21
|
+
"target": "@components/makersclaw/blocks/needs-you/needs-you.jsx",
|
|
22
|
+
"content": "\"use client\";\n\nimport { useState } from \"react\";\nimport { Button, Icon, Input, Skeleton, Spinner, cx } from \"../../ui\";\n\n// What waits on the member, in the app (the app kit, D15). Four pieces:\n//\n// NeedsYou the queue: the agent's questions and its yeses. A\n// question is answered in place (QuestionBox, 0.8.1, D19);\n// a yes is the `approval` block's press frame, given to\n// `renderYes` (D18); everything else opens the desk.\n// QuestionBox the agent's question answered where it was asked: its\n// options as one press each, and the member's own words\n// where the question welcomes them.\n// WaitingYes the frozen-text card: what goes out, frozen, then\n// \"Waiting for your yes on the desk\", Open the desk, and\n// Take the ask back (Blog Post Studio's design).\n// AgentWorking the strip while the agent works: what it is on, Stop,\n// and \"Stop keeps everything written so far\".\n//\n// Fed by useNeedsYou() (lib/hooks.js). An answer is words, so the app\n// takes it (D19). The YES is not: these blocks never draw an Approve — the\n// press is the Makersclaw-held frame of the `approval` block, which app\n// code cannot press (D18). The account sign-in and money stay on the\n// desk. WaitingYes is the card for when the press frame cannot load: it\n// says the yes waits on the desk and opens it. Props in, callbacks out.\n\nconst ISO = /^\\d{4}-\\d{2}-\\d{2}T/;\n\n/** \"11:20\" today, \"12 Sep\" another day; words as given. */\nfunction when(at) {\n if (at == null || at === \"\") return null;\n const d = at instanceof Date ? at : typeof at === \"string\" && ISO.test(at) ? new Date(at) : null;\n if (!d) return { words: String(at) };\n if (Number.isNaN(d.getTime())) return null;\n const today = d.toDateString() === new Date().toDateString();\n return {\n words: today ? d.toLocaleTimeString(\"en-GB\", { hour: \"2-digit\", minute: \"2-digit\" }) : d.toLocaleDateString(\"en-GB\", { day: \"numeric\", month: \"short\" }),\n iso: d.toISOString(),\n };\n}\n\n/** Each kind of need: its word, its icon, what its one press says. */\nconst KINDS = {\n question: { word: \"A question\", icon: \"circle-help\", head: \"Waiting for your answer on the desk\", press: \"Answer on the desk\" },\n yes: { word: \"Your yes\", icon: \"shield-check\", head: \"Waiting for your yes on the desk\", press: \"Open the desk\" },\n approval: { word: \"Your yes\", icon: \"shield-check\", head: \"Waiting for your yes on the desk\", press: \"Open the desk\" },\n spend: { word: \"Spending\", icon: \"credit-card\", head: \"Waiting for your yes to spend, on the desk\", press: \"Open the desk\" },\n limit: { word: \"Credits\", icon: \"credit-card\", head: \"Paused for credits — see the desk\", press: \"Open the desk\" },\n connect: { word: \"An account\", icon: \"link\", head: \"Waiting for you to connect an account on the desk\", press: \"Connect on the desk\" },\n};\nconst OTHER = { word: \"Needs you\", icon: \"bell\", head: \"Needs you on the desk\", press: \"Open the desk\" };\nconst kindOf = (k) => KINDS[k] ?? OTHER;\n\n/**\n * The agent's question, answered where it was asked (D19). Its options are\n * one press each (a lone question with options answers on the spot, as the\n * desk's form does), and where the question welcomes words there is a box\n * for them.\n *\n * options [{ id, label }] the question offers (none: words only)\n * free false when the question takes one of its options only\n * onAnswer({ optionId } | { text }) async → { ok } | { ok: false, error }\n * placeholder, sendLabel the box's words\n *\n * Fed by useNeedsYou().answer(item, …) or useAgentThread().answer(…). The\n * door checks it all again (the member who asked, one of the options, the\n * words not empty): a refusal is shown as it is.\n */\nexport function QuestionBox({ options = [], free = true, onAnswer, placeholder, sendLabel = \"Send answer\", className }) {\n const list = Array.isArray(options) ? options.filter((o) => o && o.id) : [];\n const [text, setText] = useState(\"\");\n const [busy, setBusy] = useState(null); // an option id, or \"text\"\n const [error, setError] = useState(null);\n const [sent, setSent] = useState(false);\n if (!onAnswer) return null;\n\n async function give(payload, which) {\n if (busy || sent) return;\n setBusy(which);\n setError(null);\n let r;\n try {\n r = await onAnswer(payload);\n } catch (e) {\n r = { ok: false, error: e?.message ?? String(e) };\n }\n setBusy(null);\n if (r && r.ok === false) setError(r.error || \"That did not go through. Try again.\");\n else setSent(true);\n }\n const words = text.trim();\n const hasBox = free !== false || list.length === 0;\n\n if (sent)\n return (\n <p role=\"status\" className={cx(\"flex items-center gap-2 text-sm text-success\", className)}>\n <Icon name=\"check\" size={14} className=\"shrink-0\" /> Sent. The agent is on it.\n </p>\n );\n return (\n <div className={cx(\"grid grid-cols-1 min-w-0 gap-2\", className)}>\n {list.length ? (\n <div className=\"flex min-w-0 flex-wrap gap-1.5\">\n {list.map((o) => (\n <Button key={o.id} size=\"sm\" variant=\"secondary\" loading={busy === o.id} disabled={Boolean(busy) && busy !== o.id} onClick={() => give({ optionId: o.id }, o.id)}>\n {o.label}\n </Button>\n ))}\n </div>\n ) : null}\n {hasBox ? (\n <form\n className=\"flex min-w-0 items-center gap-2\"\n onSubmit={(e) => {\n e.preventDefault();\n if (words) give({ text: words }, \"text\");\n }}\n >\n <Input\n aria-label={placeholder ?? (list.length ? \"Or answer in your own words\" : \"Your answer\")}\n placeholder={placeholder ?? (list.length ? \"Or answer in your own words…\" : \"Your answer…\")}\n value={text}\n disabled={Boolean(busy)}\n onChange={(e) => setText(e.target.value)}\n className=\"min-w-0 flex-1\"\n />\n <Button size=\"sm\" variant=\"primary\" type=\"submit\" loading={busy === \"text\"} disabled={!words || Boolean(busy)}>\n {sendLabel}\n </Button>\n </form>\n ) : null}\n {error ? (\n <p role=\"alert\" className=\"text-xs text-destructive\">\n {error}\n </p>\n ) : null}\n </div>\n );\n}\n\n/** One thing that waits. */\nfunction Need({ item, onOpen, onAnswer, renderYes }) {\n const k = kindOf(item.kind);\n const answerHere = item.via === \"answer\" && Boolean(onAnswer);\n const pressHere = item.via === \"press\" && typeof renderYes === \"function\";\n const t = when(item.at);\n const [opening, setOpening] = useState(false);\n async function open() {\n if (!onOpen || opening) return;\n setOpening(true);\n try {\n await onOpen(item);\n } finally {\n setOpening(false);\n }\n }\n return (\n <li\n className={cx(\n \"grid grid-cols-1 min-w-0 gap-2 border-b border-border px-3.5 py-3 last:border-b-0\",\n !answerHere && !pressHere && \"@lg:grid-cols-[minmax(0,1fr)_auto] @lg:items-start\",\n )}\n >\n <div className=\"flex min-w-0 items-start gap-2.5\">\n <Icon name={k.icon} size={16} className=\"mt-0.5 shrink-0 text-warning\" />\n <div className=\"grid grid-cols-1 min-w-0 gap-0.5\">\n <p className=\"flex min-w-0 flex-wrap items-baseline gap-x-2 text-xs text-muted-foreground\">\n <span className=\"font-medium text-foreground\">{k.word}</span>\n {item.title ? <span className=\"min-w-0 break-words\">{item.title}</span> : null}\n {t ? <time dateTime={t.iso}>{t.words}</time> : null}\n </p>\n <p className=\"font-medium\">{answerHere ? \"A question for you\" : pressHere ? \"Waiting for your yes\" : k.head}</p>\n {item.text ? <p className={cx(\"break-words text-muted-foreground\", !answerHere && !pressHere && \"line-clamp-3\")}>{item.text}</p> : null}\n </div>\n </div>\n {answerHere ? (\n <QuestionBox\n options={item.options ?? []}\n free={item.free}\n onAnswer={(payload) => onAnswer(item, payload)}\n className=\"pl-[26px]\"\n />\n ) : pressHere ? (\n <div className=\"min-w-0 pl-[26px]\">{renderYes(item)}</div>\n ) : onOpen ? (\n <Button size=\"sm\" variant=\"secondary\" loading={opening} onClick={open} className=\"justify-self-start @lg:justify-self-end\">\n {k.press} <Icon name=\"external\" size={12} />\n </Button>\n ) : null}\n </li>\n );\n}\n\n/**\n * items what useNeedsYou() returns as `items`:\n * [{ id, request, kind, text, title, at, via, options,\n * free, needsAt }]; kind is question · yes · spend ·\n * connect (another word reads \"Needs you\"); `via` says how\n * the app can settle it (\"answer\", \"press\" or null: the desk)\n * onOpen(item) opens it on the desk (useNeedsYou's `open`); the press\n * of everything the app cannot settle itself\n * onAnswer(item, { optionId | text }) answers a question in place\n * (useNeedsYou's `answer`, D19): an item with via \"answer\"\n * shows its QuestionBox; without it, it opens the desk\n * renderYes(item) draws the yes of an item with via \"press\" (D18):\n * `<YesCard item={item} />`, a component of yours that\n * calls useApproval(item.id) and returns the `approval`\n * block; without it the item opens the desk\n * empty the line when nothing waits (default \"Nothing waits on\n * you here.\"); `false` draws nothing at all then\n * title the head (default \"Needs you\"); `null` for none\n * loading skeleton rows before the first read\n * unavailable a quiet \"not connected yet\" instead of the list\n */\nexport function NeedsYou({ items = [], onOpen, onAnswer, renderYes, empty = \"Nothing waits on you here.\", title = \"Needs you\", loading = false, unavailable = false, className }) {\n const list = (items ?? []).filter(Boolean);\n if (!list.length && empty === false && !loading) return null;\n return (\n <section aria-label={typeof title === \"string\" ? title : \"Needs you\"} className={cx(\"@container min-w-0 overflow-hidden rounded-lg border border-border bg-card text-sm text-card-foreground\", className)}>\n {title ? (\n <div className=\"flex min-w-0 items-center gap-2 border-b border-border px-3.5 py-2.5 font-semibold\">\n <span className=\"min-w-0 flex-1 truncate\">{title}</span>\n {list.length ? <span className=\"shrink-0 rounded-full bg-warning/15 px-2 text-xs font-medium text-warning\">{list.length}</span> : null}\n </div>\n ) : null}\n {unavailable ? (\n <p className=\"px-3.5 py-3 text-muted-foreground\">What waits on you is not connected yet.</p>\n ) : loading && !list.length ? (\n <div aria-busy=\"true\" aria-label=\"Loading\" className=\"grid grid-cols-1 gap-3 px-3.5 py-3\">\n <Skeleton className=\"w-3/4\" />\n <Skeleton className=\"w-1/2\" />\n </div>\n ) : !list.length ? (\n <p className=\"flex items-center gap-2 px-3.5 py-3 text-muted-foreground\">\n <Icon name=\"check\" size={14} className=\"shrink-0 text-success\" />\n {empty}\n </p>\n ) : (\n <ul className=\"m-0 grid grid-cols-1 list-none p-0\">\n {list.map((item, i) => (\n <Need key={item.id ?? i} item={item} onOpen={onOpen} onAnswer={onAnswer} renderYes={renderYes} />\n ))}\n </ul>\n )}\n </section>\n );\n}\n\n/** What WaitingYes says in each state. */\nconst YES = {\n ready: null,\n waiting: { icon: \"clock\", head: \"Waiting for your yes on the desk\", tone: \"text-primary\" },\n approved: { icon: \"check\", head: \"You said yes · the agent is on it\", tone: \"text-success\" },\n declined: { icon: \"x\", head: \"You said no on the desk. Nothing went out.\", tone: \"text-muted-foreground\" },\n taken: { icon: \"undo\", head: \"Taken back. Nothing went out.\", tone: \"text-muted-foreground\" },\n failed: { icon: \"warning\", head: \"It didn't go through\", tone: \"text-destructive\" },\n};\n\n/**\n * The frozen-text card: what goes out, frozen while the ask waits, and\n * where it stands. Never an Approve: the yes is the `approval` block's\n * press frame (D18), and this card is what shows when that frame cannot\n * load — \"Waiting for your yes on the desk\", with the desk one press away.\n *\n * title what it is (\"How to cover a sudden sick day\")\n * frozen what goes out, as it will go (text or a node)\n * kicker the line over it (default \"Ready to send\")\n * state ready · waiting (default) · approved · declined ·\n * taken · failed\n * body one sentence under the state, in the app's words\n * (default: \"The ask is on your desk. Say yes there, and\n * the agent does it.\")\n * error the words for `failed`\n * onOpen() opens the desk on this ask (useNeedsYou's `open`)\n * onTakeBack() async → { ok } | { ok: false, error }: stops the ask\n * while the agent has not acted (useNeedsYou's `takeBack`);\n * without it there is no Take back\n * takeBackLabel default \"Take the ask back\"\n */\nexport function WaitingYes({\n title,\n frozen,\n kicker = \"Ready to send\",\n state = \"waiting\",\n body = \"The ask is on your desk. Say yes there, and the agent does it.\",\n error,\n onOpen,\n onTakeBack,\n takeBackLabel = \"Take the ask back\",\n className,\n}) {\n const [taking, setTaking] = useState(false);\n const [takeError, setTakeError] = useState(null);\n const [taken, setTaken] = useState(false);\n const now = taken ? \"taken\" : state;\n const s = YES[now] ?? YES.waiting;\n\n async function takeBack() {\n if (!onTakeBack || taking) return;\n setTaking(true);\n setTakeError(null);\n let r;\n try {\n r = await onTakeBack();\n } catch (e) {\n r = { ok: false, error: e?.message ?? String(e) };\n }\n setTaking(false);\n if (r && r.ok === false) setTakeError(r.error || \"Couldn't take it back. Open the desk to stop it there.\");\n else setTaken(true);\n }\n\n return (\n <section\n aria-label={typeof title === \"string\" ? title : \"What goes out\"}\n 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)}\n >\n <div className=\"grid grid-cols-1 min-w-0 gap-1\">\n {kicker ? (\n <p className=\"flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wide text-muted-foreground\">\n {now !== \"ready\" && now !== \"taken\" ? <Icon name=\"lock\" size={12} label=\"Frozen while it waits\" /> : null}\n {kicker}\n </p>\n ) : null}\n {title ? <p className=\"break-words text-[15px] font-semibold leading-snug\">{title}</p> : null}\n </div>\n {frozen != null && frozen !== \"\" ? (\n <div\n className={cx(\n \"min-w-0 whitespace-pre-wrap break-words rounded-[var(--radius)] border border-border px-3 py-2.5\",\n now === \"ready\" || now === \"taken\" ? \"bg-background\" : \"bg-muted text-muted-foreground\",\n )}\n >\n {frozen}\n </div>\n ) : null}\n\n {s ? (\n <div role=\"status\" className=\"grid grid-cols-1 min-w-0 gap-2 border-t border-border pt-3\">\n <p className={cx(\"flex min-w-0 items-center gap-2 font-medium\", now === \"waiting\" ? null : s.tone)}>\n <Icon name={s.icon} size={16} className={cx(\"shrink-0\", s.tone)} />\n <span className=\"min-w-0 break-words\">{s.head}</span>\n </p>\n {now === \"waiting\" && body ? <p className=\"text-muted-foreground\">{body}</p> : null}\n {now === \"failed\" && error ? <p className=\"text-destructive\">{error}</p> : null}\n {now === \"waiting\" ? (\n <div className=\"flex min-w-0 flex-wrap items-center gap-2\">\n {onOpen ? (\n <Button size=\"sm\" variant=\"primary\" onClick={onOpen}>\n Open the desk <Icon name=\"external\" size={12} />\n </Button>\n ) : null}\n {onTakeBack ? (\n <>\n <Button size=\"sm\" variant=\"quiet\" loading={taking} onClick={takeBack}>\n {takeBackLabel}\n </Button>\n <span className=\"text-xs text-muted-foreground\">while the agent hasn't acted</span>\n </>\n ) : null}\n </div>\n ) : now === \"failed\" && onOpen ? (\n <Button size=\"sm\" variant=\"secondary\" className=\"justify-self-start\" onClick={onOpen}>\n Open the desk <Icon name=\"external\" size={12} />\n </Button>\n ) : null}\n {takeError ? (\n <p role=\"alert\" className=\"text-xs text-destructive\">\n {takeError}\n </p>\n ) : null}\n </div>\n ) : null}\n </section>\n );\n}\n\n/**\n * The strip while the agent works on an ask: what it is on now, Stop,\n * and what Stop keeps.\n *\n * words what it is doing (\"Writing the draft\"); default \"Working on it\"\n * steps [{ label, status: running · done · failed }]: the running\n * step's label shows, with the count\n * onStop() stops it (useNeedsYou's `stop`, useAgentThread's `stop`);\n * may be async → { ok } | { ok: false, error }; without it\n * there is no Stop (the member may not stop it)\n * stopping true while a stop you track is out\n * stopHint default \"Stop keeps everything written so far\"; null for none\n */\nexport function AgentWorking({ words = \"Working on it\", steps, onStop, stopping = false, stopHint = \"Stop keeps everything written so far\", className }) {\n const list = Array.isArray(steps) ? steps : [];\n const running = list.filter((s) => s?.status === \"running\").at(-1);\n const done = list.filter((s) => s?.status === \"done\").length;\n // useNeedsYou's stop(request) answers { ok } | { ok: false, error }: the\n // strip spins while it is out and says why when it fails.\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState(null);\n async function stop() {\n if (!onStop || busy || stopping) return;\n setBusy(true);\n setError(null);\n let r;\n try {\n r = await onStop();\n } catch (e) {\n r = { ok: false, error: e?.message ?? String(e) };\n }\n setBusy(false);\n if (r && r.ok === false) setError(r.error || \"Couldn't stop it. Open the desk to stop it there.\");\n }\n return (\n <div role=\"status\" className={cx(\"@container flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1.5 rounded-lg border border-border bg-card px-3.5 py-2.5 text-sm text-card-foreground\", className)}>\n <Spinner size=\"sm\" label=\"Working\" className=\"shrink-0 text-primary\" />\n <div className=\"grid grid-cols-1 min-w-0 flex-1 basis-48 gap-0.5\">\n <span className=\"min-w-0 break-words font-medium\">{words}</span>\n {running || list.length ? (\n <span className=\"min-w-0 truncate text-xs text-muted-foreground\">\n {running ? running.label : null}\n {running && list.length > 1 ? \" · \" : null}\n {list.length > 1 ? `${done} of ${list.length} steps done` : null}\n </span>\n ) : null}\n </div>\n {onStop ? (\n <span className=\"flex min-w-0 flex-wrap items-center gap-x-2 gap-y-0.5\">\n {stopHint ? <span className=\"text-xs text-muted-foreground\">{stopHint}</span> : null}\n <Button size=\"sm\" variant=\"secondary\" loading={stopping || busy} onClick={stop}>\n <Icon name=\"stop\" size={12} /> Stop\n </Button>\n </span>\n ) : null}\n {error ? (\n <p role=\"alert\" className=\"basis-full text-xs text-destructive\">\n {error}\n </p>\n ) : null}\n </div>\n );\n}\n"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"path": "registry/blocks/needs-you/README.md",
|
|
26
|
+
"type": "registry:file",
|
|
27
|
+
"target": "@components/makersclaw/blocks/needs-you/README.md",
|
|
28
|
+
"content": "# NeedsYou\n\nWhat waits on the member, drawn inside the app. Four pieces:\n\n- **NeedsYou** — the queue. The agent's questions and its yeses on this\n app. A question is **answered in place** (`QuestionBox`); a yes is the\n `approval` block's press frame (`renderYes`); anything else the app\n cannot settle (connecting an account, spending money, several questions\n at once) opens the desk with one press.\n- **QuestionBox** — the agent's question, answered where it was asked: its\n options, one press each, and a box for the member's own words where the\n question welcomes them.\n- **WaitingYes** — the frozen-text card. What goes out, frozen while the\n ask waits, then \"Waiting for your yes on the desk\", **Open the desk**\n and **Take the ask back**. It is also what the `approval` block shows\n when its press frame cannot load.\n- **AgentWorking** — the strip while the agent works: what it is on now,\n **Stop**, and \"Stop keeps everything written so far\".\n\nAn answer is words, so the app takes it. The yes is not: the app's own\ncode never draws an Approve. The press is the Makersclaw-held frame of the\n`approval` block, which app code cannot script. Connecting an account and\nmoney stay on the desk.\n\n```jsx\nimport { NeedsYou, QuestionBox, WaitingYes, AgentWorking } from \"../components/blocks/needs-you\"; // a Makersclaw app (\"./blocks/needs-you\" from components/)\nimport { NeedsYou, QuestionBox, WaitingYes, AgentWorking } from \"@/components/makersclaw/blocks/needs-you\"; // a shadcn project\n```\n\nClient components. The Example is at the end.\n\n## Which hook feeds it\n\n```js\nconst needs = useNeedsYou(); // lib/hooks.js\n// → { items, working, open(item), answer(item, …), takeBack(item), stop(request), loading, error, unavailable, reload }\n```\n\nIt reads `GET _claw/requests`: only the member's own asks (`mine`).\n`items` are the asks that wait on them; `working` are their asks that\nrun now. Each item says how the app can settle it: `via` is `\"answer\"` (a\nquestion: `answer(item, { optionId | text })`, which is\n`POST _claw/requests/:id/answer`), `\"press\"` (the one ask's yes: the\n`approval` block, `useApproval(item.id)`) or `null` (the desk: `open`).\n`takeBack` and `stop` are `POST _claw/requests/:id/stop`.\n\n## Props\n\n### NeedsYou\n\n- `items` — `needs.items`: `[{ id, request, kind, text, title, at, via,\n options, free, needsAt }]`. `kind` is `question`, `yes` or `spend`\n (\"Waiting for your yes on the desk · Open the desk\" when the app cannot\n take it), `connect` (\"Connect on the desk\"); any other word reads \"Needs\n you on the desk\". `text` is the agent's words, `title` the ask's, `at`\n when (words, a `Date` or ISO).\n- `onOpen(item)` — `needs.open`. The press of everything the app cannot\n settle itself. Without it those rows have no press.\n- `onAnswer(item, { optionId | text })` — `needs.answer`. An item with\n `via: \"answer\"` then shows its `QuestionBox`; without it that item opens\n the desk, as before.\n- `renderYes(item)` — draws the yes of an item with `via: \"press\"`: a\n component of yours that calls `useApproval(item.id)` and returns the\n `approval` block. Without it that item opens the desk.\n- `empty` — the line when nothing waits. Default \"Nothing waits on you\n here.\" `false` draws nothing at all when the list is empty.\n- `title` — the head. Default \"Needs you\"; `null` for none.\n- `loading` — `needs.loading`: skeleton lines before the first read.\n- `unavailable` — `needs.unavailable`: a quiet \"not connected yet\".\n- `className` — layout around it. Never colour.\n\n### QuestionBox\n\n- `options` — `[{ id, label }]` the question offers (none: words only).\n A lone press answers on the spot, as the desk's form does.\n- `free` — `false` when the question takes one of its options only\n (default: words are welcome).\n- `onAnswer({ optionId } | { text })` — `needs.answer(item, …)` or\n `chat.answer(…)`; async, answers `{ ok }` or `{ ok: false, error }`. A\n refusal is shown as the door words it.\n- `placeholder`, `sendLabel`, `className`.\n\n### WaitingYes\n\n- `title` — what it is (\"How to cover a sudden sick day\").\n- `frozen` — what goes out, exactly as it will go (text or a node). It is\n drawn frozen (muted, with a lock) while the ask waits.\n- `kicker` — the line over it. Default \"Ready to send\".\n- `state` — `ready` (nothing asked yet: the text only, no state line) ·\n `waiting` (default) · `approved` (\"You said yes · the agent is on it\")\n · `declined` · `taken` · `failed` (with `error`).\n- `body` — one sentence under \"Waiting for your yes on the desk\", in the\n app's words: say what the agent does after the yes.\n- `onOpen()` — opens the desk on this ask (`() => needs.open(item)`).\n- `onTakeBack()` — async, answers `{ ok: true }` or `{ ok: false, error\n }` (`() => needs.takeBack(item)`). It stops the ask while the agent\n has not acted; the card then says \"Taken back. Nothing went out.\"\n Without it there is no Take back.\n- `takeBackLabel` — default \"Take the ask back\".\n\n### AgentWorking\n\n- `words` — what the agent is doing, in the app's words (\"Writing the\n draft\"). Default \"Working on it\".\n- `steps` — `[{ label, status }]` (`running` · `done` · `failed`): the\n running step shows, with \"2 of 6 steps done\".\n- `onStop()` — `() => needs.stop(request)` or `chat.stop`. It may be\n async and answer `{ ok }` or `{ ok: false, error }`: the button spins\n while it is out and a failure says why. Without it there is no Stop.\n- `stopping` — true while a stop you track yourself is out.\n- `stopHint` — default \"Stop keeps everything written so far\"; `null`\n for none.\n\n## Who may do what\n\n- Every member sees only their own asks: the door lists `mine`.\n- Stop and Take back: the asker, an owner, an admin, or the app's owner\n (`mayStop` on the request). Give `onStop`/`onTakeBack` only when the\n request says the member may; the door refuses anyone else anyway.\n- Only the member who asked answers a question or gives a yes; the door\n refuses everyone else (owners and admins too), and a question answered\n after it moved on (`needsAt`) is refused.\n- The yes: the `approval` block's press frame, never these blocks.\n- An account sign-in and money: the desk, always.\n\n## Never\n\n- **Never an Approve, a Yes, or a Send that gives the yes.** The yes is\n the `approval` block's frame; this block's only press toward it is Open\n the desk, when the frame cannot load.\n- Never answer the one ask (a yes) through `QuestionBox`: the door refuses\n it (`needs_a_press`).\n- Never let the frozen text be edited while the ask waits. To change it,\n take the ask back first.\n- Never show another member's asks here.\n\n## Example\n\nA post that waits for its yes, the strip while the agent works, and the\nqueue on the home screen.\n\n```jsx\n\"use client\";\nimport { useNeedsYou } from \"../lib/hooks\";\nimport { NeedsYou, WaitingYes, AgentWorking } from \"../components/blocks/needs-you\";\n\nexport function PostStatus({ post }) {\n const needs = useNeedsYou();\n const item = needs.items.find((i) => i.request?.id === post.request_id);\n const running = needs.working.find((r) => r.id === post.request_id);\n if (running) return <AgentWorking words=\"Putting the post on Webflow\" onStop={() => needs.stop(running)} />;\n return (\n <WaitingYes\n title={post.title}\n frozen={post.body}\n state={item ? \"waiting\" : \"ready\"}\n body=\"Say yes there, and the agent makes this one post live on Webflow, then adds its link here.\"\n onOpen={item ? () => needs.open(item) : undefined}\n onTakeBack={item ? () => needs.takeBack(item) : undefined}\n />\n );\n}\n\nexport function Waiting() {\n const needs = useNeedsYou();\n return (\n <NeedsYou\n items={needs.items}\n onOpen={needs.open}\n onAnswer={needs.answer}\n renderYes={(item) => <SendCard item={item} />} // an Approval card, see the approval block\n loading={needs.loading}\n unavailable={needs.unavailable}\n />\n );\n}\n```\n"
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"categories": [
|
|
32
|
+
"needs you",
|
|
33
|
+
"approval",
|
|
34
|
+
"yes",
|
|
35
|
+
"waiting",
|
|
36
|
+
"inbox",
|
|
37
|
+
"queue",
|
|
38
|
+
"agent",
|
|
39
|
+
"stop",
|
|
40
|
+
"working",
|
|
41
|
+
"take back",
|
|
42
|
+
"send",
|
|
43
|
+
"desk",
|
|
44
|
+
"question",
|
|
45
|
+
"answer",
|
|
46
|
+
"press"
|
|
47
|
+
],
|
|
48
|
+
"docs": "Installed to components/makersclaw/blocks/needs-you. Import: import { NeedsYou, QuestionBox, WaitingYes, AgentWorking } from \"@/components/makersclaw/blocks/needs-you\". Props and an example: components/makersclaw/blocks/needs-you/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
49
|
+
"meta": {
|
|
50
|
+
"makersclaw": {
|
|
51
|
+
"version": "0.8.1",
|
|
52
|
+
"useWhen": "A home screen or a record that should say what the agent waits on and let the member settle it there (answer a question, give a yes before sending or spending), a screen where something is about to go out (a post, an email, a batch) and waits for the member's yes, or any place an ask is running and the member should be able to stop it. For the yes card itself use approval; for the conversation use agent-chat; for one job's progress use job-panel."
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
package/r/order-detail.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "OrderDetail",
|
|
6
6
|
"description": "One placed order on a page: its number, status and date, the items, the delivery address and contact, a timeline from placed to delivered (Steps), and the totals, with Track order and Invoice actions. Loading, error and not-found states built in.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.1/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [],
|
|
11
11
|
"files": [
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"docs": "Installed to components/makersclaw/blocks/order-detail. Import: import { OrderDetail } from \"@/components/makersclaw/blocks/order-detail\". Props and an example: components/makersclaw/blocks/order-detail/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
48
48
|
"meta": {
|
|
49
49
|
"makersclaw": {
|
|
50
|
-
"version": "0.8.
|
|
50
|
+
"version": "0.8.1",
|
|
51
51
|
"useWhen": "A buyer or an operator opens one order: \"where is my order\", an order confirmation page, a support view of a purchase. Not for a list of orders (data-table) or for a basket before paying (cart)."
|
|
52
52
|
}
|
|
53
53
|
}
|
package/r/org-chart.json
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
"title": "OrgChart",
|
|
6
6
|
"description": "An org chart on flow-canvas: people laid out top down from a flat list (each names a manager), managers centred over their teams, a team with no managers in it as a compact column, every line drawn for you. Teams fold past the first levels behind a chip that says how many are hidden; search finds a person, unfolds the way to them, centres them and lights their chain of managers; a card opens a details sheet (reports to, direct reports, team size). Colour strips and a legend by department. Optional: drag a card onto another to change someone's manager (async { ok }, optimistic, with Undo). Keyboard throughout.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
9
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.1/r/core.json",
|
|
9
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.1/r/flow-canvas.json"
|
|
10
10
|
],
|
|
11
11
|
"dependencies": [],
|
|
12
12
|
"files": [
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"docs": "Installed to components/makersclaw/blocks/org-chart. Import: import { OrgChart, buildOrg, layoutOrg, chainOf, orgToText, groupHues } from \"@/components/makersclaw/blocks/org-chart\". Props and an example: components/makersclaw/blocks/org-chart/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
60
60
|
"meta": {
|
|
61
61
|
"makersclaw": {
|
|
62
|
-
"version": "0.8.
|
|
62
|
+
"version": "0.8.1",
|
|
63
63
|
"useWhen": "A screen that shows who reports to whom: a team or company directory's chart, an account's buying committee as a hierarchy, a reporting line to check before a reorg. Not for a free-form map of people and notes (account-map, flow-canvas), a plain list of people (data-table), or a tree of ideas (mind-map)."
|
|
64
64
|
}
|
|
65
65
|
}
|
package/r/pace-bar.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "pace-bar",
|
|
4
|
+
"type": "registry:block",
|
|
5
|
+
"title": "PaceBar",
|
|
6
|
+
"description": "Progress against a clock. PaceBar is one bar filled to its value with a hard upright tick where it should be by now (the share of a period that has gone), and the stretch between them hatched, so behind is a shape you see; a spend (hours burned against time gone) hatches the other way. PaceList puts many bars on ONE axis: the period's month names and a tick each Monday above, one today line running unbroken through the whole list and its group headings, each row's percent, who last moved it (a person's dot, the agent's mark, or none) and the gap said in weeks (\"4 wk behind\", \"On pace\", \"Not judged yet\", \"Done\"); rows sit in optional groups and a row can be pressed. PaceRuler is the axis alone, with a flag for today (\"Today · 92% gone\"). WeekStrip is the period as its weeks in one row: weeks gone filled, this one marked, a mark on each week a check-in was made (person, agent, none), a dashed ring on a past week with none. The sums are exported as plain functions (paceOf, paceWords, paceSentence: \"57% done, 88% of the time gone\"). Positions are percentages, so it draws the same on the server; narrow, a list row is its name above its bar. Loading, failed and empty states are drawn.",
|
|
7
|
+
"registryDependencies": [
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.1/r/core.json",
|
|
9
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.1/r/agent-marks.json"
|
|
10
|
+
],
|
|
11
|
+
"dependencies": [],
|
|
12
|
+
"files": [
|
|
13
|
+
{
|
|
14
|
+
"path": "registry/blocks/pace-bar/index.js",
|
|
15
|
+
"type": "registry:component",
|
|
16
|
+
"target": "@components/makersclaw/blocks/pace-bar/index.js",
|
|
17
|
+
"content": "// The PaceBar block — `makersclaw add pace-bar`. Fixed: compose it through\n// its props (README.md beside this file). The model's helpers are plain\n// functions; they work on the server too.\nexport { PaceBar, PaceList, PaceRuler, WeekStrip, PaceMark } from \"./pace-bar\";\nexport {\n WORDS,\n atDay,\n axisOf,\n dayName,\n dayNumber,\n groupRows,\n isoOf,\n marksOnWeeks,\n paceOf,\n paceSentence,\n paceWords,\n percent,\n periodOf,\n shortDay,\n toneOf,\n weeksOf,\n} from \"./pace-model\";\n"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"path": "registry/blocks/pace-bar/pace-bar.jsx",
|
|
21
|
+
"type": "registry:component",
|
|
22
|
+
"target": "@components/makersclaw/blocks/pace-bar/pace-bar.jsx",
|
|
23
|
+
"content": "\"use client\";\n\nimport { Button, Empty, Notice, Skeleton, cx } from \"../../ui\";\nimport { AgentMark } from \"../agent-marks\";\nimport { axisOf, dayName, groupRows, marksOnWeeks, paceOf, paceSentence, paceWords, percent, share, shortDay, toneOf, weeksOf, WORDS } from \"./pace-model\";\n\n// Progress against a clock. Four pieces that share one idea, \"how far\n// along, and how far along should it be by now\":\n//\n// PaceBar one bar: filled to `value`, a hard upright tick at the pace,\n// and the stretch between them hatched, so \"behind\" is a shape\n// you see and not a sum you do.\n// PaceList many bars on ONE axis: the period's months and Mondays above,\n// one today line running through the whole list, and each\n// row's gap said in weeks (\"4 wk behind\"). Rows can sit in\n// groups.\n// PaceRuler the axis alone (month names, a tick each Monday, a flag for\n// today), for a screen that lays out its own rows.\n// WeekStrip the period as its weeks in one row: the weeks gone filled,\n// this one marked, a mark on the week each check-in was made,\n// by a person, by the agent, or by no one.\n//\n// Props in, one callback out (`onOpen`); the block imports only React, the\n// kit and the agent's mark. README.md beside this file lists every prop.\n// Server-safe: nothing here measures the page, positions are percentages.\n\n// ---- looks (literal class names, so Tailwind sees every one)\n\nconst FILL = {\n ink: \"bg-foreground\",\n quiet: \"bg-muted-foreground\",\n info: \"bg-info\",\n success: \"bg-success\",\n warning: \"bg-warning\",\n danger: \"bg-destructive\",\n};\n// The hatch is drawn in currentColor, so a text-* token paints it.\nconst HATCH_TEXT = {\n ink: \"text-foreground\",\n quiet: \"text-muted-foreground\",\n info: \"text-info\",\n success: \"text-success\",\n warning: \"text-warning\",\n danger: \"text-destructive\",\n};\nconst GAP_TEXT = { ...HATCH_TEXT, ink: \"text-muted-foreground\" };\nconst HATCH = { backgroundImage: \"repeating-linear-gradient(135deg, currentColor 0 2px, transparent 2px 5px)\" };\n\nconst SIZE = {\n lg: { track: \"h-3.5\", tick: \"-inset-y-2\" },\n md: { track: \"h-2.5\", tick: \"-inset-y-1\" },\n sm: { track: \"h-1.5\", tick: \"-inset-y-1\" },\n};\n\nconst pct = (n) => `${Math.round(n * 1000) / 1000}%`;\nconst asTone = (tone, p, cut) => (tone === \"auto\" || tone == null ? toneOf(p, cut) : FILL[tone] ? tone : \"ink\");\n\n/** What the bar is made of, without a meter's role: the fill, the hatched gap, and the tick when asked. */\nfunction Bars({ p, tone, size, hatch, tick, animate, delay }) {\n const s = SIZE[size] ?? SIZE.md;\n const fill = (p.value ?? 0) * 100;\n const mark = p.pace == null ? null : p.pace * 100;\n const spend = p.kind === \"spend\";\n let from = 0;\n let width = 0;\n if (hatch && mark != null) {\n if (!spend && mark > fill) [from, width] = [fill, mark - fill];\n if (spend && fill > mark) [from, width] = [mark, fill - mark];\n }\n const grow = animate ? \"transition-[width] duration-700 ease-out starting:w-0 motion-reduce:transition-none\" : \"\";\n const wait = animate && delay ? { transitionDelay: `${Math.min(540, Math.max(0, Number(delay) || 0))}ms` } : undefined;\n return (\n <>\n <span className={cx(\"relative block overflow-hidden rounded-[1px] bg-muted\", s.track)}>\n {width ? (\n <span\n aria-hidden=\"true\"\n className={cx(\"absolute inset-y-0 left-(--from) w-(--width) opacity-60\", HATCH_TEXT[tone])}\n style={{ ...HATCH, \"--from\": pct(from), \"--width\": pct(width) }}\n />\n ) : null}\n <span aria-hidden=\"true\" className={cx(\"absolute inset-y-0 left-0 w-(--fill) rounded-[1px]\", FILL[tone], grow)} style={{ \"--fill\": pct(fill), ...wait }} />\n </span>\n {tick && mark != null ? (\n <span aria-hidden=\"true\" className={cx(\"absolute left-(--mark) w-0.5 -translate-x-1/2 bg-foreground ring-1 ring-background\", s.tick)} style={{ \"--mark\": pct(mark) }} />\n ) : null}\n </>\n );\n}\n\n/**\n * One bar against a pace. Give `pace` (0..1) yourself, or give the period\n * (`start`, `end`, `today`) and the share of the time gone is the pace.\n *\n * Props: see README.md. In short: `value`, `pace`, `start`, `end`, `today`,\n * `kind` (progress | spend), `tone` (ink | quiet | info | success | warning\n * | danger | auto), `size` (lg | md | sm), `hatch`, `animate`, `delay`,\n * `words`, `label`, `className`.\n */\nexport function PaceBar({ value, pace, start, end, today, kind = \"progress\", tone = \"ink\", size = \"md\", hatch = true, animate = false, delay = 0, words, tolerance, graceDays, graceFraction, label = \"Progress\", className }) {\n const p = paceOf({ value, pace, start, end, today, kind, tolerance, graceDays, graceFraction });\n const t = asTone(tone, p);\n const valuetext = paceSentence(p, words) || (p.valuePct == null ? \"not known\" : `${p.valuePct}%`);\n return (\n <span\n role=\"meter\"\n aria-label={label}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-valuenow={p.valuePct ?? 0}\n aria-valuetext={`${valuetext}${p.state === \"worse\" || p.state === \"better\" || p.state === \"on\" ? `. ${paceWords(p, words)}` : \"\"}`}\n className={cx(\"relative block min-w-0\", className)}\n >\n <Bars p={p} tone={t} size={size} hatch={hatch} tick animate={animate} delay={delay} />\n </span>\n );\n}\n\n// ---- marks\n\nconst MARK_WORDS = { person: \"A person\", agent: \"The agent\" };\n\n/**\n * Who made a check-in: a filled dot for a person, the platform's mark for\n * the agent, nothing for no one. With `empty` a mark made by no one is a\n * hollow ring (the place a mark would be); with `missed` it is a dashed\n * ring (a week that should have one and has none). `label` is what a\n * screen reader hears; leave it out when words beside the mark already say it.\n */\nexport function PaceMark({ by, label, empty = false, missed = false, size = \"sm\", className }) {\n const aria = label ? { role: \"img\", \"aria-label\": label, title: label } : { \"aria-hidden\": \"true\" };\n const dot = size === \"sm\" ? \"size-2\" : \"size-2.5\";\n if (by === \"agent\") return <AgentMark size={size === \"sm\" ? \"xs\" : size} label={label} className={className} />;\n if (by === \"person\") return <span {...aria} className={cx(\"inline-block shrink-0 rounded-full bg-foreground\", dot, className)} />;\n if (missed) return <span {...aria} className={cx(\"inline-block shrink-0 rounded-full border border-dashed border-muted-foreground\", dot, className)} />;\n if (empty) return <span {...aria} className={cx(\"inline-block shrink-0 rounded-full border border-muted-foreground\", dot, className)} />;\n return null;\n}\n\n// ---- the ruler\n\n/** The flag's sides: a flag near the right edge hangs left of the line, near the left edge right of it. */\nconst flagSide = (at) => (at > 0.78 ? \"-translate-x-full\" : at < 0.22 ? \"\" : \"-translate-x-1/2\");\n\n/**\n * The axis of a period: month names, a tick each week start, the today\n * line and its flag. Place it in the column your rows' bars share.\n * Props: `start`, `end`, `today`, `weekStartsOn`, `words`, `showToday`,\n * `label`, `className`.\n */\nexport function PaceRuler({ start, end, today, weekStartsOn = 0, words, showToday = true, label = \"Period\", className }) {\n const w = { ...WORDS, ...words };\n const axis = axisOf({ start, end, today, weekStartsOn });\n if (!axis) return null;\n const { period } = axis;\n const at = showToday ? axis.todayAt : null;\n const flag =\n at == null\n ? period.where === \"before\"\n ? w.before\n : null\n : period.where === \"after\"\n ? w.after\n : `${w.today} · ${w.gone(percent(period.share))}`;\n const summary = `${label}: ${shortDay(period.s)} to ${shortDay(period.e)}${period.t != null ? `. ${dayName(period.t)}${period.where === \"in\" ? `, ${percent(period.share)}% of the time gone, ${period.left} ${period.left === 1 ? \"day\" : \"days\"} left` : period.where === \"after\" ? \", ended\" : \", not started\"}` : \"\"}`;\n return (\n <span role=\"img\" aria-label={summary} className={cx(\"relative block h-11 min-w-0\", className)}>\n {flag ? (\n <span\n aria-hidden=\"true\"\n className={cx(\n \"absolute top-0 z-10 whitespace-nowrap rounded-sm px-1.5 text-xs font-semibold leading-4\",\n at == null ? \"left-0 bg-muted text-muted-foreground\" : cx(\"bg-foreground text-background\", flagSide(at)),\n )}\n style={at == null ? undefined : { left: pct(at * 100) }}\n >\n {flag}\n </span>\n ) : null}\n <span aria-hidden=\"true\" className=\"absolute inset-x-0 bottom-0 h-px bg-border\" />\n {axis.weeks.map((d) => (\n <span key={`w${d.day}`} aria-hidden=\"true\" className=\"absolute bottom-0 h-1.5 w-px bg-border\" style={{ left: pct(d.at * 100) }} />\n ))}\n {axis.months.map((m) => (\n <span key={`m${m.day}`} aria-hidden=\"true\">\n <span className=\"absolute bottom-0 h-2.5 w-px bg-foreground\" style={{ left: pct(m.at * 100) }} />\n <span className=\"absolute bottom-3 whitespace-nowrap pl-1 text-xs font-semibold text-foreground\" style={{ left: pct(m.at * 100) }}>\n {m.label}\n </span>\n </span>\n ))}\n {at != null ? <span aria-hidden=\"true\" className=\"absolute inset-y-0 w-0.5 -translate-x-1/2 bg-foreground\" style={{ left: pct(at * 100) }} /> : null}\n </span>\n );\n}\n\n// ---- the list\n\n// Rows are one grid, the same columns in every row, so the bars of every\n// row start and end at the same x: that is the shared axis. Narrow, a row is\n// its name on one line and the bar and number under it.\nconst ROW = \"grid grid-cols-[minmax(0,1fr)_4rem] items-center gap-x-3 gap-y-1 px-3 py-2 @2xl:grid-cols-(--cols)\";\n// The name takes the whole narrow row; wide it is one column, or two (name and\n// meta) in a heading, where nothing sits in the meta column.\nconst NAME = \"col-span-2 @2xl:col-span-1\";\nconst NAME_WIDE = \"col-span-2\";\nconst HEAD = \"text-xs font-semibold uppercase tracking-wide text-muted-foreground\";\n// The today line reaches into the row's padding and its border, so the lines\n// of the rows below one another read as one.\nconst LINE = \"pointer-events-none absolute -top-2 -bottom-[calc(0.5rem+1px)] w-0.5 -translate-x-1/2 bg-foreground\";\n\nfunction Track({ children, at, className }) {\n return (\n <span className={cx(\"relative min-h-5 self-stretch\", className)}>\n {children}\n {at != null ? <span aria-hidden=\"true\" className={LINE} style={{ left: pct(at * 100) }} /> : null}\n </span>\n );\n}\n\n/**\n * Many bars on one axis. `rows` are `{ id, label, sub, value, pace, kind,\n * tone, by, byLabel, meta, note, group }`; `groups` are `{ id, label, sub,\n * note, empty }`.\n *\n * Props: see README.md. In short: `rows`, `groups`, `start`, `end`,\n * `today`, `pace`, `kind`, `tolerance`, `graceDays`, `graceFraction`, `unit`, `words`,\n * `heads`, `ruler`, `metaWidth`, `trackWidth`, `gapWidth`, `size`, `selected`,\n * `onOpen`, `loading`, `error`, `onRetry`, `empty`, `emptyAction`, `label`,\n * `className`.\n */\nexport function PaceList({\n rows,\n groups,\n start,\n end,\n today,\n pace,\n kind = \"progress\",\n tolerance,\n graceDays,\n graceFraction,\n unit,\n words: wordsIn,\n heads,\n ruler = true,\n weekStartsOn = 0,\n warnAt,\n dangerAt,\n metaWidth,\n trackWidth = \"24rem\",\n gapWidth = \"9rem\",\n size = \"sm\",\n selected,\n onOpen,\n loading = false,\n error,\n onRetry,\n empty,\n emptyAction,\n label = \"Progress against the clock\",\n className,\n}) {\n const words = { ...WORDS, ...wordsIn };\n if (loading) {\n return (\n <div aria-busy=\"true\" aria-label={`${label}, loading`} className={cx(\"@container grid min-w-0 gap-3 px-3 py-2\", className)}>\n <Skeleton shape=\"rect\" className=\"h-8 w-full\" />\n {[0, 1, 2, 3].map((i) => (\n <Skeleton key={i} shape=\"rect\" className=\"h-5 w-full\" />\n ))}\n </div>\n );\n }\n if (error) {\n return (\n <div className={cx(\"@container min-w-0\", className)}>\n <Notice tone=\"danger\" title={`${label} did not load`} action={onRetry ? <Button size=\"sm\" onClick={onRetry}>Try again</Button> : null}>\n {typeof error === \"string\" ? error : \"Something went wrong reading the rows.\"}\n </Notice>\n </div>\n );\n }\n\n const axis = axisOf({ start, end, today, weekStartsOn });\n const sharedPace = pace === undefined ? undefined : share(pace);\n // The line: the end of today on the period, or the pace given.\n const lineAt = axis ? axis.todayAt : sharedPace ?? null;\n const list = rows ?? [];\n const cols = [\"minmax(0,1fr)\", metaWidth, `minmax(10rem,${trackWidth})`, \"4rem\", gapWidth].filter(Boolean).join(\" \");\n const style = { \"--cols\": cols };\n // In a heading the name also takes the meta column's place.\n const headName = cx(\"min-w-0\", metaWidth ? NAME_WIDE : NAME);\n\n if (!list.length && !(groups ?? []).length) {\n return (\n <div className={cx(\"@container min-w-0\", className)}>\n <Empty icon=\"clock\" title={empty ?? \"Nothing to measure yet\"} action={emptyAction} />\n </div>\n );\n }\n\n const read = new Map(\n list.map((r) => {\n const p = paceOf({\n value: r.value,\n pace: r.pace === undefined ? sharedPace : r.pace,\n start,\n end,\n today,\n kind: r.kind ?? kind,\n tolerance,\n graceDays,\n graceFraction,\n unit: r.unit ?? unit,\n });\n return [r, { p, tone: asTone(r.tone, p, { warnAt, dangerAt }) }];\n }),\n );\n const grouped = groupRows(list, groups);\n\n const head = heads ?? {};\n const showRuler = ruler && axis;\n const summary = `${label}: ${list.length} ${list.length === 1 ? \"row\" : \"rows\"}${axis?.period.t != null && axis.period.where === \"in\" ? `, ${percent(axis.period.share)}% of the time gone` : \"\"}`;\n\n return (\n <div role=\"group\" aria-label={summary} className={cx(\"@container min-w-0\", className)}>\n {showRuler || head.label || head.pct || head.gap ? (\n <div className={cx(ROW, \"border-b border-border bg-background\")} style={style}>\n <span className={cx(HEAD, \"self-end\", headName)}>{head.label}</span>\n <span className=\"relative col-span-2 min-w-0 self-stretch @2xl:col-span-1\">\n {showRuler ? <PaceRuler start={start} end={end} today={today} weekStartsOn={weekStartsOn} words={words} label={label} /> : null}\n {showRuler && lineAt != null ? <span aria-hidden=\"true\" className=\"absolute top-full h-[calc(0.5rem+1px)] w-0.5 -translate-x-1/2 bg-foreground\" style={{ left: pct(lineAt * 100) }} /> : null}\n </span>\n <span className={cx(HEAD, \"hidden self-end text-right @2xl:block\")}>{head.pct}</span>\n <span className={cx(HEAD, \"hidden self-end @2xl:block\")}>{head.gap}</span>\n </div>\n ) : null}\n {grouped.map(({ group, rows: inGroup }, gi) => (\n <div key={group?.id ?? `rest-${gi}`} role=\"group\" aria-label={group?.label ?? undefined}>\n {group ? (\n <div className={cx(ROW, \"border-b border-border bg-background\")} style={style}>\n <div className={cx(\"flex flex-wrap items-baseline gap-x-3 gap-y-0.5\", headName)}>\n <h3 className=\"text-sm font-semibold\">{group.label}</h3>\n {group.sub ? <span className=\"text-xs text-muted-foreground\">{group.sub}</span> : null}\n {!inGroup.length && group.empty ? <span className=\"text-xs text-muted-foreground\">{group.empty}</span> : null}\n {group.note ? <span className=\"text-xs text-muted-foreground\">{group.note}</span> : null}\n </div>\n <Track at={lineAt} className=\"hidden @2xl:block\" />\n <span className=\"hidden @2xl:block\" />\n <span className=\"hidden @2xl:block\" />\n </div>\n ) : null}\n <ul className=\"m-0 grid list-none p-0\">\n {inGroup.map((r, i) => {\n const { p, tone } = read.get(r);\n const said = r.note ?? paceWords(p, words);\n const Tag = onOpen ? \"button\" : \"div\";\n const on = selected != null && selected === r.id;\n // A row with a pace of its own gets its own tick; one on the shared clock leaves it to the line.\n const own = p.pace != null && lineAt != null && Math.abs(p.pace - lineAt) > 0.004;\n return (\n <li key={r.id ?? i} className=\"min-w-0\">\n <Tag\n {...(onOpen ? { type: \"button\", onClick: () => onOpen(r), \"aria-current\": on ? \"true\" : undefined } : {})}\n className={cx(\n ROW,\n \"w-full border-b border-border text-left outline-none\",\n onOpen && \"cursor-pointer hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset\",\n on && \"bg-accent\",\n )}\n style={style}\n >\n <span className={cx(\"min-w-0\", NAME)}>\n <span className=\"block break-words text-sm font-medium leading-snug\">{r.label}</span>\n {r.sub ? <span className=\"block break-words text-xs text-muted-foreground\">{r.sub}</span> : null}\n </span>\n {metaWidth ? <span className=\"col-span-2 min-w-0 @2xl:col-span-1\">{r.meta}</span> : null}\n <Track at={lineAt} className=\"col-start-1 @2xl:col-start-auto\">\n <span className=\"absolute inset-x-0 top-1/2 block -translate-y-1/2\">\n <span className=\"relative block\">\n <Bars p={p} tone={tone} size={size} hatch tick={own} />\n </span>\n </span>\n </Track>\n <span className=\"col-start-2 flex items-center justify-end gap-1.5 text-sm font-semibold tabular-nums @2xl:col-start-auto\">\n <PaceMark by={r.by} label={r.byLabel ?? (r.by ? MARK_WORDS[r.by] : undefined)} />\n <span>{p.valuePct == null ? \"–\" : `${p.valuePct}%`}</span>\n </span>\n <span className={cx(\"col-span-2 min-w-0 break-words text-xs @2xl:col-span-1\", p.state === \"worse\" && tone !== \"ink\" ? cx(\"font-medium\", GAP_TEXT[tone]) : \"text-muted-foreground\")}>{said}</span>\n </Tag>\n </li>\n );\n })}\n </ul>\n </div>\n ))}\n </div>\n );\n}\n\n// ---- the week strip\n\nconst WEEK_WORD = \"Week\";\n\n/**\n * The period as its weeks in one row. A week gone is filled, the current\n * one is marked, weeks to come are empty. A mark sits on the week its date\n * falls in: a filled dot for a person, the agent's mark, or (with `missed`)\n * a dashed ring on a past week with no mark at all.\n *\n * Props: `start`, `end`, `today`, `marks` [{ id, date, by, label }],\n * `weekStartsOn`, `missed`, `onOpen(week)`, `words`, `label`, `className`.\n */\nexport function WeekStrip({ start, end, today, marks, weekStartsOn = 0, missed = false, onOpen, words, label = \"Weeks\", className }) {\n const w = { week: WEEK_WORD, ...words };\n const base = weeksOf({ start, end, today, weekStartsOn });\n if (!base.length) return null;\n const { weeks, skipped } = marksOnWeeks(base, marks);\n const current = weeks.find((x) => x.current);\n const total = weeks.reduce((n, x) => n + x.marks.length, 0);\n const summary = `${label}: ${weeks.length} ${weeks.length === 1 ? \"week\" : \"weeks\"}${current ? `, ${w.week.toLowerCase()} ${current.n} now` : \"\"}, ${total} ${total === 1 ? \"mark\" : \"marks\"}${skipped ? `, ${skipped} without a date left off` : \"\"}`;\n return (\n <div className={cx(\"@container min-w-0\", className)}>\n <div role=\"region\" aria-label={`${label}, scrolls sideways`} tabIndex={0} className=\"overflow-x-auto rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-ring\">\n <ol aria-label={summary} className=\"m-0 grid list-none gap-0.5 p-0\" style={{ gridTemplateColumns: `repeat(${weeks.length}, minmax(1.5rem, 1fr))` }}>\n {weeks.map((x) => {\n const Tag = onOpen ? \"button\" : \"div\";\n const name = `${w.week} ${x.n}, ${shortDay(x.from)} to ${shortDay(x.to)}${x.current ? \", now\" : \"\"}${x.marks.length ? `, ${x.marks.map((m) => m.label ?? MARK_WORDS[m.by] ?? \"a mark\").join(\", \")}` : \"\"}`;\n return (\n <li key={x.n} className=\"min-w-0\">\n <Tag\n {...(onOpen ? { type: \"button\", onClick: () => onOpen(x), \"aria-label\": name } : { role: \"img\", \"aria-label\": name })}\n {...(x.current ? { \"aria-current\": \"date\" } : {})}\n className={cx(\"grid w-full min-w-0 justify-items-center gap-1 rounded-sm px-0.5 py-1 outline-none\", onOpen && \"cursor-pointer hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring\")}\n >\n <span aria-hidden=\"true\" className={cx(\"text-xs tabular-nums\", x.current ? \"font-semibold text-foreground\" : \"text-muted-foreground\")}>\n {x.n}\n </span>\n <span\n aria-hidden=\"true\"\n className={cx(\"block w-full rounded-[1px]\", x.state === \"past\" ? \"h-1.5 bg-muted-foreground\" : x.current ? \"h-2.5 bg-foreground\" : \"h-1.5 bg-muted\")}\n />\n <span aria-hidden=\"true\" className=\"flex min-h-3 flex-wrap items-center justify-center gap-0.5\">\n {x.marks.slice(0, 3).map((m) => (\n <PaceMark key={m.id} by={m.by} empty />\n ))}\n {x.marks.length > 3 ? <span className=\"text-xs text-muted-foreground\">+{x.marks.length - 3}</span> : null}\n {missed && x.state === \"past\" && !x.marks.length ? <PaceMark missed /> : null}\n </span>\n </Tag>\n </li>\n );\n })}\n </ol>\n </div>\n </div>\n );\n}\n\nexport default PaceBar;\n"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"path": "registry/blocks/pace-bar/pace-model.js",
|
|
27
|
+
"type": "registry:component",
|
|
28
|
+
"target": "@components/makersclaw/blocks/pace-bar/pace-model.js",
|
|
29
|
+
"content": "// The pace bar's arithmetic: a period on a clock, how much of it is gone,\n// how a piece of work stands against that, the gap said in weeks (or days,\n// or points), the words for it, the axis (month names, a tick each Monday,\n// a today line) and the weeks of a strip. Pure: no React, no DOM, no clock\n// of its own (`today` always comes in). The components (pace-bar.jsx) draw\n// what this returns; test/pace-bar.test.mjs pins it.\n//\n// A \"day\" is a whole number: days since 1970-01-01 in UTC, so a date string\n// means the same day on the server and in the browser. A period includes\n// both its first and its last day, and TODAY COUNTS AS GONE: on the 23rd of\n// a quarter that began on the 1st of July and ends on the 30th of\n// September, 85 of its 92 days are gone, and 92% of the time is gone.\n\nexport const DAY_MS = 86400000;\nexport const MONTHS = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"];\nexport const WEEKDAYS = [\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\"];\n\n/** 'YYYY-MM-DD' (or an ISO time) to a day number; null when it is not a date. */\nexport function dayNumber(iso) {\n if (typeof iso === \"number\" && Number.isFinite(iso)) return Math.round(iso);\n const m = /^(\\d{4})-(\\d{2})-(\\d{2})/.exec(String(iso ?? \"\"));\n if (!m) return null;\n const t = Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3]));\n // 2026-02-31 is not a day: Date rolls it over, and the roll-over shows.\n if (new Date(t).getUTCMonth() !== Number(m[2]) - 1) return null;\n return Math.round(t / DAY_MS);\n}\n\n/** A day number back to 'YYYY-MM-DD'. */\nexport function isoOf(day) {\n const d = new Date(day * DAY_MS);\n return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, \"0\")}-${String(d.getUTCDate()).padStart(2, \"0\")}`;\n}\n\n/** 0 is Monday, 6 is Sunday. */\nexport const weekdayOf = (day) => (new Date(day * DAY_MS).getUTCDay() + 6) % 7;\nexport const dateOfMonth = (day) => new Date(day * DAY_MS).getUTCDate();\nexport const monthOf = (day) => new Date(day * DAY_MS).getUTCMonth();\n\n/** \"23 Sep\" */\nexport const shortDay = (day) => `${dateOfMonth(day)} ${MONTHS[monthOf(day)]}`;\n/** \"Wed 23 Sep\" */\nexport const dayName = (day) => `${WEEKDAYS[weekdayOf(day)]} ${shortDay(day)}`;\n\n/** A number as a 0..1 share, clamped; null when it is not a number. */\nexport function share(x) {\n if (x == null || x === \"\") return null;\n const v = Number(x);\n return Number.isFinite(v) ? Math.max(0, Math.min(1, v)) : null;\n}\n\n/** A share as a whole percent; null stays null. */\nexport const percent = (x) => (x == null ? null : Math.round(x * 100));\n\n/**\n * The period and where today stands in it.\n * total days in it, both ends counted\n * gone days gone, today counted (0 before it starts, total after it ends)\n * share gone / total, 0..1\n * left days after today (0 on the last day)\n * where \"before\" | \"in\" | \"after\"\n * Null when either end is missing or the end is before the start. With no\n * `today`, `where` is \"in\" with nothing gone and `share` is null: the\n * period is known but the clock is not.\n */\nexport function periodOf({ start, end, today } = {}) {\n const s = dayNumber(start);\n const e = dayNumber(end);\n if (s == null || e == null || e < s) return null;\n const t = dayNumber(today);\n const total = e - s + 1;\n if (t == null) return { s, e, t: null, total, gone: null, share: null, left: null, where: \"in\" };\n if (t < s) return { s, e, t, total, gone: 0, share: 0, left: total, where: \"before\" };\n if (t > e) return { s, e, t, total, gone: total, share: 1, left: 0, where: \"after\" };\n const gone = t - s + 1;\n return { s, e, t, total, gone, share: gone / total, left: e - t, where: \"in\" };\n}\n\n/**\n * How a piece of work stands against the clock.\n *\n * value how far along: 0..1 (a spend: how much is used). Null: not known.\n * pace where it should be by now, 0..1. Left out, it is worked out\n * from `start`, `end` and `today` (the share of the time gone).\n * `null` on purpose: pace is not judged.\n * kind \"progress\" (default: short of the pace is the gap) or\n * \"spend\" (over the pace is the gap: hours burned against\n * time gone)\n * tolerance whole points of the period inside which it is \"on pace\" (5)\n * graceDays days from the start in which it is not judged yet (0)\n * graceFraction the same as a share of the period, 0..1 (0.2: the first\n * fifth; 19 days of a 92-day quarter). The longer of the two\n * counts. Without a period it does nothing.\n * unit \"week\" · \"day\" · \"point\". Default \"week\" when the period is\n * known and the kind is progress; otherwise \"point\".\n *\n * Returns\n * state \"done\" · \"unjudged\" · \"better\" · \"on\" · \"worse\"\n * better/worse mean ahead/behind for progress, under/over for a spend\n * value, pace 0..1 (pace null when not judged)\n * valuePct, pacePct whole percents\n * gapPts whole points between them, always >= 0\n * amount the gap in `unit`s (whole, at least 1 when it is not \"on\"),\n * null when on pace or not judged\n * unit\n * period periodOf(), or null\n */\nexport function paceOf({ value, pace, start, end, today, kind = \"progress\", tolerance = 5, graceDays = 0, graceFraction = 0, unit } = {}) {\n const period = periodOf({ start, end, today });\n const v = share(value);\n // pace: undefined → from the clock; null → none.\n const p = pace === undefined ? (period && period.where !== \"before\" ? period.share : null) : share(pace);\n const spend = kind === \"spend\";\n const u = unit ?? (period && !spend ? \"week\" : \"point\");\n const valuePct = percent(v);\n const pacePct = percent(p);\n const base = { kind, value: v, pace: p, valuePct, pacePct, gapPts: 0, amount: null, unit: u, period };\n if (!spend && v != null && v >= 1) return { ...base, state: \"done\" };\n const fraction = Number.isFinite(graceFraction) && graceFraction > 0 ? Math.min(1, graceFraction) : 0;\n const graceUntil = period ? Math.max(Number(graceDays) || 0, Math.ceil(fraction * period.total)) : 0;\n const grace = period && period.gone != null && period.where === \"in\" && period.gone <= graceUntil;\n if (v == null || p == null || grace) return { ...base, state: \"unjudged\" };\n const diff = spend ? valuePct - pacePct : pacePct - valuePct; // positive: worse\n const gapPts = Math.abs(diff);\n if (gapPts <= tolerance) return { ...base, state: \"on\", gapPts };\n let amount = gapPts;\n if (u === \"day\" && period) amount = Math.max(1, Math.round((gapPts / 100) * period.total));\n else if (u === \"week\" && period) amount = Math.max(1, Math.round(((gapPts / 100) * period.total) / 7));\n return { ...base, state: diff > 0 ? \"worse\" : \"better\", gapPts, amount, unit: period || u === \"point\" ? u : \"point\" };\n}\n\nexport const WORDS = {\n on: \"On pace\",\n unjudged: \"Not judged yet\",\n done: \"Done\",\n today: \"Today\",\n before: \"Not started\",\n after: \"Ended\",\n units: { week: \"wk\", day: \"d\", point: \"pts\" },\n behind: (n, unit) => `${n} ${unit} behind`,\n ahead: (n, unit) => `${n} ${unit} ahead`,\n over: (n, unit) => `${n} ${unit} over`,\n under: (n, unit) => `${n} ${unit} under`,\n sentence: ({ done, gone }) => `${done}% done, ${gone}% of the time gone`,\n spendSentence: ({ done, gone }) => `${done}% used, ${gone}% of the time gone`,\n gone: (n) => `${n}% gone`,\n};\n\nconst merge = (words) => ({ ...WORDS, ...words, units: { ...WORDS.units, ...(words?.units ?? {}) } });\n\n/** The gap in words: \"4 wk behind\", \"On pace\", \"2 wk ahead\", \"Not judged yet\", \"Done\". */\nexport function paceWords(p, words) {\n if (!p) return \"\";\n const w = merge(words);\n if (p.state === \"done\") return w.done;\n if (p.state === \"unjudged\") return w.unjudged;\n if (p.state === \"on\") return w.on;\n const unit = w.units[p.unit] ?? p.unit;\n const spend = p.kind === \"spend\";\n const say = p.state === \"worse\" ? (spend ? w.over : w.behind) : spend ? w.under : w.ahead;\n return say(p.amount, unit);\n}\n\n/** The sum in words: \"57% done, 88% of the time gone\". Empty when either number is missing. */\nexport function paceSentence(p, words) {\n if (!p || p.valuePct == null) return \"\";\n const w = merge(words);\n const gone = p.period?.share != null ? percent(p.period.share) : p.pacePct;\n if (gone == null) return `${p.valuePct}%`;\n return (p.kind === \"spend\" ? w.spendSentence : w.sentence)({ done: p.valuePct, gone });\n}\n\n/**\n * A tone for a state, for a screen that has no status of its own to say:\n * on pace or better is \"ink\", worse by `warnAt` points is \"warning\", by\n * `dangerAt` points \"danger\"; not judged yet is \"quiet\"; done is \"success\".\n */\nexport function toneOf(p, { warnAt = 10, dangerAt = 25 } = {}) {\n if (!p) return \"ink\";\n if (p.state === \"done\") return \"success\";\n if (p.state === \"unjudged\") return \"quiet\";\n if (p.state !== \"worse\") return \"ink\";\n if (p.gapPts >= dangerAt) return \"danger\";\n if (p.gapPts >= warnAt) return \"warning\";\n return \"ink\";\n}\n\n/** Where a day's left edge falls on the period, 0..1 (can be outside it). */\nexport const atDay = (period, day) => (day - period.s) / period.total;\n\n/**\n * The axis of a period: the share each Monday's left edge falls at (a tick\n * each, while the period is short enough to read them), each month's 1st\n * with its name (the period's first day names its month unless the next\n * month starts right after it), and today.\n *\n * weeks [{ n, day, at }] n counts the weeks from 1; `at` is 0..1\n * months [{ day, label, at }]\n * todayAt the share the today line stands at (the end of today), or null\n * where \"before\" | \"in\" | \"after\" (as periodOf)\n */\nexport function axisOf({ start, end, today, weekStartsOn = 0 } = {}) {\n const period = periodOf({ start, end, today });\n if (!period) return null;\n const { s, e, total } = period;\n const weeks = [];\n if (total <= 200) {\n let n = 0;\n for (let d = s; d <= e; d += 1) {\n if ((weekdayOf(d) - weekStartsOn + 7) % 7 === 0) {\n n += 1;\n weeks.push({ n, day: d, at: atDay(period, d) });\n }\n }\n }\n const months = [];\n for (let d = s; d <= e; d += 1) if (dateOfMonth(d) === 1) months.push({ day: d, label: MONTHS[monthOf(d)], at: atDay(period, d) });\n const startsOnFirst = dateOfMonth(s) === 1;\n const crowded = !startsOnFirst && months.length > 0 && months[0].at < 0.09;\n if (!startsOnFirst && !crowded) months.unshift({ day: s, label: MONTHS[monthOf(s)], at: 0 });\n else if (!months.length) months.push({ day: s, label: MONTHS[monthOf(s)], at: 0 });\n const todayAt = period.where === \"in\" && period.share != null ? period.share : period.where === \"after\" ? 1 : null;\n return { period, weeks, months, todayAt, where: period.where };\n}\n\n/**\n * The weeks of a strip: one for each week start (Monday by default) in the\n * period. The days before the first week start belong to week 1, the last\n * week runs to the period's end. Each week says whether it is gone, current\n * or still to come.\n *\n * [{ n, from, to, current, state }] state: \"past\" · \"current\" · \"future\"\n */\nexport function weeksOf({ start, end, today, weekStartsOn = 0 } = {}) {\n const period = periodOf({ start, end, today });\n if (!period) return [];\n const { s, e } = period;\n const starts = [];\n for (let d = s; d <= e; d += 1) if ((weekdayOf(d) - weekStartsOn + 7) % 7 === 0) starts.push(d);\n if (!starts.length) starts.push(s);\n else starts[0] = s;\n return starts.map((from, i) => {\n const to = i + 1 < starts.length ? starts[i + 1] - 1 : e;\n const t = period.t;\n const state = t == null ? \"future\" : t > to ? \"past\" : t >= from ? \"current\" : \"future\";\n return { n: i + 1, from, to, state, current: state === \"current\" };\n });\n}\n\n/**\n * Marks on the weeks: each mark `{ id, date, by, label }` lands on the week\n * its date falls in (a date before the first week goes to the first, after\n * the last to the last; no date is left off, and counted in `skipped`).\n * `by` is \"person\", \"agent\" or nothing.\n * Returns { weeks: [{ ...week, marks: [...] }], skipped }.\n */\nexport function marksOnWeeks(weeks, marks) {\n const out = weeks.map((w) => ({ ...w, marks: [] }));\n let skipped = 0;\n for (const [i, m] of (marks ?? []).entries()) {\n const d = dayNumber(m?.date);\n if (d == null || !out.length) {\n skipped += 1;\n continue;\n }\n const at = d < out[0].from ? 0 : out.findLastIndex((w) => w.from <= d);\n out[Math.max(0, at)].marks.push({ ...m, id: m.id ?? `mark-${i}`, day: d, by: m.by === \"person\" || m.by === \"agent\" ? m.by : null });\n }\n for (const w of out) w.marks.sort((a, b) => a.day - b.day);\n return { weeks: out, skipped };\n}\n\n/**\n * Rows in groups. `groups` is `[{ id, ... }]` in the order to draw; a row\n * names its group with `group`. A row whose group is not listed (or has\n * none) goes in a trailing group with no heading when any group exists.\n * With no `groups` there is one headless group of everything.\n * Returns [{ group | null, rows }].\n */\nexport function groupRows(rows, groups) {\n const list = rows ?? [];\n if (!groups?.length) return [{ group: null, rows: list }];\n const known = new Set(groups.map((g) => g.id));\n const out = groups.map((g) => ({ group: g, rows: list.filter((r) => r.group === g.id) }));\n const rest = list.filter((r) => !known.has(r.group));\n if (rest.length) out.push({ group: null, rows: rest });\n return out;\n}\n"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"path": "registry/blocks/pace-bar/README.md",
|
|
33
|
+
"type": "registry:file",
|
|
34
|
+
"target": "@components/makersclaw/blocks/pace-bar/README.md",
|
|
35
|
+
"content": "# PaceBar\n\nProgress against a clock: how far along, and how far along it should be by now.\n\n\"58%\" alone says little. 58% with the mark at 91% says *behind*. This block\ndraws that, four ways:\n\n- **PaceBar**: one bar, filled to its value, with a hard upright tick where it\n should be by now. The stretch between the fill and the tick is hatched, so\n \"behind\" is a shape you see, not a sum you do.\n- **PaceList**: many bars on **one axis**. The period's month names and a tick\n each Monday run above. **One today line runs unbroken through the whole\n list**, group headings included. Each row says its percent, who last moved it\n and the gap in weeks (\"4 wk behind\").\n- **PaceRuler**: that axis alone, with a flag for today (\"Today · 92% gone\").\n- **WeekStrip**: the period as its weeks in one row, with a mark on each week a\n check-in was made.\n\nIt draws a result the app has already worked out. It collects no check-ins, it\nchanges no number. For a chain of dated phases with gates use `schedule-ruler`;\nfor a plain meter with no clock use the kit's `Progress`.\n\nBorn in two store apps that each drew it by hand: a goals tracker's today line\ndown a whole quarter, and a project monitor's \"57% done, 88% of the time gone\".\n\n```jsx\nimport { PaceBar, PaceList, PaceRuler, WeekStrip, PaceMark, paceOf, paceWords, paceSentence } from \"../components/blocks/pace-bar\"; // a Makersclaw app (\"./blocks/pace-bar\" from components/)\n// shadcn project: import { PaceList } from \"@/components/makersclaw/blocks/pace-bar\"\n```\n\nA client component. It needs the `agent-marks` block (the agent's mark) and no\nnpm package. Dates are `'YYYY-MM-DD'` strings and are days, never times, so the\nserver and the browser draw the same picture. Positions are percentages;\nnothing is measured.\n\n## The clock\n\nGive a **period** (`start`, `end`, both days included) and **today**. The\npace is the share of the period that has gone, **today counted**: on Wed 23 Sep\na quarter that began 1 Jul and ends 30 Sep has 85 of its 92 days gone, so the\npace is 92%.\n\nOr skip the period and give `pace` yourself (0 to 1): a bar needs no dates.\n\nThe block never reads the clock. `today` always comes in as a prop, so a\nserver render and a browser render agree.\n\n## Props: PaceBar\n\n- `value`: how far along, 0 to 1 (clamped). `null`: not known.\n- `pace`: where it should be by now, 0 to 1. Left out, it is worked out from\n `start`, `end` and `today`. `null` on purpose: no tick, pace not judged.\n- `start`, `end`, `today`: the period.\n- `kind`: `\"progress\"` (default; short of the pace is the gap) or `\"spend\"`\n (over the pace is the gap: hours burned against time gone, a budget used).\n- `tone`: `ink` (default), `quiet`, `info`, `success`, `warning`, `danger`,\n or `auto` (ink while on pace or ahead, warning at 10 points behind, danger at\n 25; success when done; quiet when not judged yet). A tone is a status: leave\n it `ink` when nothing needs attention.\n- `size`: `lg` (14 px), `md` (10 px, default), `sm` (6 px).\n- `hatch`: draw the gap between the fill and the tick (default true).\n- `animate`: fill from zero on first paint (CSS only), off under reduced\n motion. `delay`: milliseconds before it starts, to stagger a list (capped\n at 540).\n- `tolerance`, `graceDays`, `graceFraction`: see \"How it judges\" below.\n- `words`: the app's vocabulary (below).\n- `label`: what a screen reader calls it. It reads the sum as words\n (`role=\"meter\"`): \"62% done, 92% of the time gone. 4 wk behind\".\n- `className`: layout (width, margin). Never colour.\n\n## Props: PaceList\n\n- `rows`: `[{ id, label, sub, value, pace, kind, tone, by, byLabel, meta, note, group, unit }]`\n - `label`, `sub`: the name and the small line under it.\n - `value`: 0 to 1. `pace`: only when this row has a clock of its own (it\n gets its own tick); otherwise the shared line is its pace.\n - `tone`: as above; default `auto`.\n - `by`: who last moved it: `\"person\"` (a filled dot), `\"agent\"` (the agent's\n mark) or nothing. `byLabel`: what a screen reader hears (default \"A\n person\", \"The agent\").\n - `meta`: anything to put in the meta column (an owner's avatar, a count).\n Needs `metaWidth`.\n - `note`: replaces the gap words (\"1 wk behind · Likely\").\n - `group`: the `id` of a group.\n- `groups`: `[{ id, label, sub, note, empty }]` in the order to draw. A heading\n row carries the today line too. `empty` is the line shown when the group has\n no rows. A row whose `group` is not listed goes last, with no heading.\n- `start`, `end`, `today`: the period. They draw the ruler and the line.\n- `pace`: with no period, the shared line's place (0 to 1).\n- `kind`, `tolerance`, `graceDays`, `graceFraction`, `unit`, `warnAt`, `dangerAt`: how rows are\n judged (below). A row's own `kind` or `unit` wins.\n- `heads`: `{ label, meta, pct, gap }`: the column heads, shown from 42rem of\n the container's width.\n- `ruler`: draw the axis above the rows (default true).\n- `weekStartsOn`: weekday the week begins on, 0 = Monday (default).\n- `metaWidth`: a CSS length (\"8rem\") adds a meta column. Without it there is none.\n- `trackWidth` (default `\"24rem\"`, the most the bar takes), `gapWidth` (`\"9rem\"`).\n- `size`: bar thickness (default `sm`).\n- `selected`: the `id` of the row to mark as current.\n- `onOpen(row)`: makes each row a button. Leave it out for a read-only list.\n- `loading`, `error`, `onRetry`, `empty`, `emptyAction`: the other states: a\n skeleton; a danger notice with \"Try again\"; and, with no rows, an empty panel\n titled `empty`.\n- `label`, `className`.\n\nEvery row is one grid with the same columns, so the bars of all rows start and\nend at the same x: that is the shared axis. Under 42rem of its own width a row is\nits name on one line and its bar and percent under it.\n\n## Props: PaceRuler\n\n`start`, `end`, `today`, `weekStartsOn`, `words`, `showToday` (default true),\n`label`, `className`. It is 44px tall and as wide as its parent: put it in the\ncolumn your own rows share, and draw the line down from it with your rows'\ntoday position (`axisOf(...).todayAt`).\n\n## Props: WeekStrip\n\n- `start`, `end`, `today`, `weekStartsOn`.\n- `marks`: `[{ id, date, by, label }]`. A mark lands on the week its date falls\n in (before the first week: the first; after the last: the last). `by`:\n `\"person\"`, `\"agent\"`, or nothing (a hollow ring). More than three marks in a\n week show \"+n\".\n- `missed`: put a dashed ring on a past week with no mark at all.\n- `onOpen(week)`: makes each week a button (`{ n, from, to, state, marks }`).\n- `words`: `{ week: \"Week\" }`. `label`, `className`.\n\nThe strip keeps each week at least 1.5rem wide and scrolls sideways when the\nperiod has too many weeks for the container (focusable, so the keyboard scrolls\nit).\n\n## PaceMark\n\n`<PaceMark by=\"person\" | \"agent\" label empty missed size />`. Nothing for no\none; `empty` draws a hollow ring (a mark made by no one), `missed` a dashed one\n(a mark that should be there).\n\n## How it judges\n\n`paceOf({ value, pace, start, end, today, kind, tolerance, graceDays, graceFraction, unit })`\n(also exported, pure) returns the state:\n\n- `done`: progress at 100% or more.\n- `unjudged`: no value, no pace, the period has not started, or it is inside\n `graceDays` of the start (default 0; a quarterly tracker passes 14), or\n inside `graceFraction` of the period from its start (0..1; `0.2` is the\n first fifth, 19 days of a 92-day quarter). The longer of the two counts.\n- `on`: the gap is within `tolerance` whole points of the period (default 5).\n- `worse` / `better`: behind or ahead (progress); over or under (spend).\n\nThe gap is said in whole weeks, **at least 1**, from the period's length:\n62% against a pace of 92% in a 92-day quarter is `30` points, 27.6 days, \"4 wk\nbehind\". `unit` is `\"week\"` (default when there is a period), `\"day\"` or\n`\"point\"` (no period: \"30 pts behind\").\n\n## Words\n\nEvery string is the app's to change. `words` merges over these:\n\n```\non: \"On pace\" unjudged: \"Not judged yet\" done: \"Done\"\ntoday: \"Today\" before: \"Not started\" after: \"Ended\"\nunits: { week: \"wk\", day: \"d\", point: \"pts\" }\nbehind(n, unit) \"4 wk behind\" ahead(n, unit) \"2 wk ahead\"\nover(n, unit) \"34 pts over\" under(n, unit) \"17 pts under\"\nsentence({ done, gone }) \"57% done, 88% of the time gone\"\nspendSentence({ done, gone }) \"91% used, 57% of the time gone\"\ngone(n) \"92% gone\"\n```\n\nAlso exported, pure and without React, for a screen that wants the same numbers\nin a sentence: `paceOf`, `paceWords(state, words)`, `paceSentence(state,\nwords)`, `toneOf(state, { warnAt, dangerAt })`, `periodOf`, `axisOf`,\n`weeksOf`, `marksOnWeeks`, `groupRows`, `dayNumber`, `isoOf`, `dayName`,\n`shortDay`, `percent`, `WORDS`.\n\n## Example\n\n```jsx\n<PaceList\n label=\"Q3 goals\"\n start=\"2026-07-01\"\n end=\"2026-09-30\"\n today=\"2026-09-23\"\n graceDays={14}\n heads={{ label: \"Goal\", pct: \"Done\", gap: \"Against today\" }}\n metaWidth=\"7rem\"\n groups={[{ id: \"sales\", label: \"Sales\", sub: \"2 goals\" }, { id: \"eng\", label: \"Engineering\", sub: \"1 goal\" }]}\n rows={[\n { id: \"a\", group: \"sales\", label: \"Close the enterprise gap\", value: 0.62, by: \"person\", meta: owner(\"Priya\") },\n { id: \"b\", group: \"sales\", label: \"Turn demos into a motion\", value: 0.87 },\n { id: \"c\", group: \"eng\", label: \"Keep the rate API fast\", value: 0.9, by: \"agent\" },\n ]}\n onOpen={(row) => router.push(`?goal=${row.id}`)}\n/>\n\n<WeekStrip start=\"2026-07-01\" end=\"2026-09-30\" today=\"2026-09-23\" missed\n marks={[{ id: \"m1\", date: \"2026-09-14\", by: \"person\" }, { id: \"m2\", date: \"2026-09-21\", by: \"agent\" }]} />\n\n// \"57% done, 88% of the time gone\"\n<p>{paceSentence(paceOf({ value: 0.57, pace: 0.88 }))}</p>\n\n// A budget: 91% of the hours used with 57% of the time gone\n<PaceBar kind=\"spend\" value={0.91} pace={0.57} tone=\"danger\" label=\"Hours\" />\n```\n\n## States drawn\n\n- **Empty**: a panel with what to do. With groups and no rows, each heading says\n its `empty` line.\n- **One row**, **many rows**, **grouped**.\n- **Very long names**: the name wraps (rows grow); the bar's column is unchanged.\n- **Narrow** (a 320px container): a row is its name above its bar and percent;\n the ruler is under the column heads.\n- **Wide**: the bar column keeps its `trackWidth`; the name takes the rest.\n- **On pace, behind, ahead, done, not judged yet, before the period, after it.**\n- **Spend**: over the pace hatches from the tick to the fill.\n- **Loading**, **failed** (with Try again), **read-only** (no `onOpen`).\n- Reduced motion: nothing moves unless `animate` is set, and then not under it.\n\n## Notes for the app that places it\n\n- It is a picture. Put the numbers in words beside it (the list does: the\n percent and the gap words are in every row).\n- Colour is a signal. The fill is ink unless a row is behind (warning, danger)\n or done (success). Do not tint rows for any other reason.\n- For a goals list, give each group a `note` such as \"2 need attention\" and\n compute it from `paceOf(...).state` yourself; the block does not know your\n words for \"needs attention\".\n- A row with `by` shows who last moved it; keep the dates in your detail view.\n"
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
"categories": [
|
|
39
|
+
"pace",
|
|
40
|
+
"progress",
|
|
41
|
+
"bar",
|
|
42
|
+
"target",
|
|
43
|
+
"behind",
|
|
44
|
+
"ahead",
|
|
45
|
+
"on track",
|
|
46
|
+
"today line",
|
|
47
|
+
"quarter",
|
|
48
|
+
"okr",
|
|
49
|
+
"goals",
|
|
50
|
+
"key results",
|
|
51
|
+
"burn",
|
|
52
|
+
"budget",
|
|
53
|
+
"spend",
|
|
54
|
+
"sprint",
|
|
55
|
+
"weeks behind",
|
|
56
|
+
"check-in",
|
|
57
|
+
"cadence",
|
|
58
|
+
"deadline",
|
|
59
|
+
"ruler",
|
|
60
|
+
"agent"
|
|
61
|
+
],
|
|
62
|
+
"docs": "Installed to components/makersclaw/blocks/pace-bar. Import: import { PaceBar, PaceList, PaceRuler, WeekStrip, PaceMark, WORDS, atDay, axisOf, dayName, dayNumber, groupRows, isoOf, marksOnWeeks, paceOf, paceSentence, paceWords, percent, periodOf, shortDay, toneOf, weeksOf } from \"@/components/makersclaw/blocks/pace-bar\". Props and an example: components/makersclaw/blocks/pace-bar/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
63
|
+
"meta": {
|
|
64
|
+
"makersclaw": {
|
|
65
|
+
"version": "0.8.1",
|
|
66
|
+
"useWhen": "Any screen where progress belongs to a period and the question is 'are we on pace': a quarter's goals and their key results, a project's work done against its days gone, hours burned against hours sold, a sprint, a savings or budget target, a course or a reading plan, a campaign's delivery against its flight dates, a weekly check-in cadence. Draws a result the app has already worked out; it does not collect check-ins. Not for a chain of dated phases with gates (schedule-ruler), editable bars with dependencies (gantt), a plain meter with no clock (Progress in the kit), or a time series (chart)."
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
package/r/page-builder.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "PageBuilder",
|
|
6
6
|
"description": "A visual page editor (GrapesJS core) in the workspace theme: our own clean panels instead of GrapesJS's chrome — an Add panel of section and basic blocks (click or drag), a Layers tree, a Design panel limited to theme-token choices (colours, type, spacing, width, corners, borders), a device toggle (desktop, tablet, phone), undo/redo, preview, and a friendly empty state with starter templates. Pages style with CSS variables, so a saved page repaints with the theme. Saves out as { html, css, project }; PageView shows a saved page in an app.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.1/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [
|
|
11
11
|
"grapesjs@0.22.16",
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
"docs": "Installed to components/makersclaw/blocks/page-builder. Import: import { PageBuilder, usePageBuilder, readPage, DEVICES, PageView, standaloneDocument, BlockThumb, PageThumb, THUMB_NAMES, BASE_CSS, SECTION_BLOCKS, BASIC_BLOCKS, STARTER_TEMPLATES, STYLE_GROUPS, COLOR_TOKENS, groupsFor, currentOption, patchFor, tokenCss, fontFaceCss, fontLinks, PAGE_BASE_CSS, emptyPage, isPageEmpty, cleanHtml, cleanCss, normalizeBlock, templatePage, pageDocument, projectSections } from \"@/components/makersclaw/blocks/page-builder\". Props and an example: components/makersclaw/blocks/page-builder/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
99
99
|
"meta": {
|
|
100
100
|
"makersclaw": {
|
|
101
|
-
"version": "0.8.
|
|
101
|
+
"version": "0.8.1",
|
|
102
102
|
"useWhen": "A screen where someone builds or edits a web page visually: a landing page, a waitlist or launch page, a simple site section, an announcement page. landing-builder is built on it (section templates, publishing). Not for rich text in a document (doc-editor), an email body (newsletter-builder), a form of fields (record-form) or a freeform drawing (sketch-canvas)."
|
|
103
103
|
}
|
|
104
104
|
}
|
package/r/payment-methods.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "PaymentMethods",
|
|
6
6
|
"description": "A customer's SAVED payment methods as cards: brand, last four digits, expiry (expired and expiring soon flagged) and a Default badge; Make default, Remove behind a Confirm, and Add, which hands off to the payment provider (Paddle). Never a card-number field. Empty, loading and error states built in.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.1/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [],
|
|
11
11
|
"files": [
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"docs": "Installed to components/makersclaw/blocks/payment-methods. Import: import { PaymentMethods } from \"@/components/makersclaw/blocks/payment-methods\". Props and an example: components/makersclaw/blocks/payment-methods/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
45
45
|
"meta": {
|
|
46
46
|
"makersclaw": {
|
|
47
|
-
"version": "0.8.
|
|
47
|
+
"version": "0.8.1",
|
|
48
48
|
"useWhen": "A billing or account page where a customer manages the cards and wallets on file. Not for taking a payment (cart's checkout step hands off to Paddle) or for a list of charges (transaction-list)."
|
|
49
49
|
}
|
|
50
50
|
}
|
package/r/pdf-document.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "PdfDocument",
|
|
6
6
|
"description": "Make PDFs in the workspace theme (@react-pdf/renderer): a small set of document primitives (headings, text, label/value fields, tables with repeating headers, totals, callouts, badges, rows and columns) and a clean ready-made quote / estimate / invoice layout, rendered in the browser to a Blob with renderPdf(doc). The theme's colours are read from the page when the PDF is made (a PDF cannot read CSS variables): white paper, near-black ink and the theme's primary as the one accent. Also a live-preview hook (usePdf), a Download PDF button, and the quote sums and money formatting as plain functions.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.1/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [
|
|
11
11
|
"@react-pdf/renderer@4.9.0"
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"docs": "Installed to components/makersclaw/blocks/pdf-document. Import: import { renderPdf, usePdf, PdfDownloadButton, downloadBlob, registerPdfFont, PdfDocument, PdfStack, PdfRow, PdfCol, PdfText, PdfHeading, PdfLabel, PdfField, PdfDivider, PdfCallout, PdfBadge, PdfTable, PdfTotals, PdfLines, PdfView, PdfRawText, PdfImage, PdfLink, PdfThemeProvider, usePdfTheme, PDF_TYPE, QuoteDocument, quoteTotals, lineAmount, round2, formatMoney, formatQuantity, formatPdfDate, resolvePdfTheme, buildPdfTheme, DEFAULT_PDF_THEME } from \"@/components/makersclaw/blocks/pdf-document\". Props and an example: components/makersclaw/blocks/pdf-document/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
73
73
|
"meta": {
|
|
74
74
|
"makersclaw": {
|
|
75
|
-
"version": "0.8.
|
|
75
|
+
"version": "0.8.1",
|
|
76
76
|
"useWhen": "A screen produces a document someone downloads, prints, emails or signs: a quote, an invoice, an estimate, a receipt, a statement, a certificate, a one-page report. The quote-builder and proposal blocks print through it. Not for showing an existing PDF on screen (pdf-viewer) or for a web page people read in the app (doc-page)."
|
|
77
77
|
}
|
|
78
78
|
}
|
package/r/pdf-viewer.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "PdfViewer",
|
|
6
6
|
"description": "A PDF on screen, in the workspace theme (react-pdf + pdf.js): pages in one smooth scrolling column (only the pages near the window are drawn, so long files stay fast), a thumbnail rail, a page box, zoom by steps, fit width, fit page and Ctrl/⌘ + wheel or pinch, find in the text with every match marked, and highlights with notes: select text, choose Highlight or Add note, and it saves through onHighlight; notes are edited in a side list, colours are theme tokens, delete has Undo. Opens a URL, a Blob or bytes; password-protected files ask for the password; a narrow viewer turns its rails into sheets. The pdf.js worker ships with the app (no public/ file, no CDN).",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.1/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [
|
|
11
11
|
"react-pdf@11.0.0",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"docs": "Installed to components/makersclaw/blocks/pdf-viewer. Import: import { PdfViewer, usePdfViewer, HIGHLIGHT_TONES, findMatches, toPageRects, mergeRects, sortHighlights, highlightAt, fitScale, ZOOM_STEPS } from \"@/components/makersclaw/blocks/pdf-viewer\". Props and an example: components/makersclaw/blocks/pdf-viewer/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
82
82
|
"meta": {
|
|
83
83
|
"makersclaw": {
|
|
84
|
-
"version": "0.8.
|
|
84
|
+
"version": "0.8.1",
|
|
85
85
|
"useWhen": "A screen shows an existing PDF: a contract or proposal to review, a signed agreement, an uploaded invoice or statement, a report, a generated quote before it is sent; or people mark up a PDF and leave notes on passages. Pair it with pdf-document to preview a PDF you make. Not for making PDFs (pdf-document), for rich text people edit (doc-editor), or for marking up a screenshot (annotate)."
|
|
86
86
|
}
|
|
87
87
|
}
|
package/r/product-detail.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "Product detail",
|
|
6
6
|
"description": "One product on its own page: pictures (thumbnails, a side rail, a carousel, a tall stack, or one scene with the buy box floating over it), brand, name, price with the old price struck through, star rating and review count, options (sizes as a segmented row, colours as swatches, long lists as a select, plans as cards with their own price), a quantity control, add to cart through an async callback that may fail, buy now, save to favourites, perks, and details in an accordion or tabs. Loading, error, not-found and sold-out states.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.1/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [],
|
|
11
11
|
"files": [
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"docs": "Installed to components/makersclaw/blocks/product-detail. Import: import { ProductDetail, ProductMedia, OptionPicker, QuantityControl, Price, formatPrice, cartLine } from \"@/components/makersclaw/blocks/product-detail\". Props and an example: components/makersclaw/blocks/product-detail/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
70
70
|
"meta": {
|
|
71
71
|
"makersclaw": {
|
|
72
|
-
"version": "0.8.
|
|
72
|
+
"version": "0.8.1",
|
|
73
73
|
"useWhen": "Selling one thing a customer looks at before buying: a product page, a template or digital download with plans, a merch item, a booking add-on. Not for many products at once (product-grid), the cart itself (cart), or a plan comparison table."
|
|
74
74
|
}
|
|
75
75
|
}
|
package/r/product-grid.json
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
"title": "Product grid",
|
|
6
6
|
"description": "Products as cards in a grid: picture, badge, favourite heart, brand, name, star rating, price with the old one struck through, and a quick add to cart through an async callback that may fail (\"Choose options\" when the product has sizes or colours). Facet filters as selects that also match a product's option values, a sort, a live count, a lead tile that takes a double cell (a picture or a muted looping video), and a promo card for collection tiles. Loading, error, empty and no-matches states; two columns on a phone.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
9
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.1/r/core.json",
|
|
9
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.1/r/product-detail.json"
|
|
10
10
|
],
|
|
11
11
|
"dependencies": [],
|
|
12
12
|
"files": [
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"docs": "Installed to components/makersclaw/blocks/product-grid. Import: import { ProductGrid, ProductCard, PromoTile, productMatches } from \"@/components/makersclaw/blocks/product-grid\". Props and an example: components/makersclaw/blocks/product-grid/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
59
59
|
"meta": {
|
|
60
60
|
"makersclaw": {
|
|
61
|
-
"version": "0.8.
|
|
61
|
+
"version": "0.8.1",
|
|
62
62
|
"useWhen": "A shop's catalogue, a collection page, related products under a product page, a row of promotions ('New', 'Sale'). Not for records to scan and act on in columns (data-table), one product's page (product-detail), or the cart (cart)."
|
|
63
63
|
}
|
|
64
64
|
}
|
package/r/record-form.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"title": "RecordForm",
|
|
6
6
|
"description": "Edit or create one record from a list of fields given as data (type, label, required, options, help, validation), grouped into titled sections — text, email, number, password, select, combobox, tags, radio, choice cards with prices, checkboxes, switch, date, time, rating, a photo and a row of images. It checks as you type, saves through an async onSave that answers { ok } or per-field errors shown under each field, and sits in a card, on a page (sections split title-left) or in a dialog.",
|
|
7
7
|
"registryDependencies": [
|
|
8
|
-
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/makersclaw@0.8.1/r/core.json"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": [],
|
|
11
11
|
"files": [
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"docs": "Installed to components/makersclaw/blocks/record-form. Import: import { RecordForm } from \"@/components/makersclaw/blocks/record-form\". Props and an example: components/makersclaw/blocks/record-form/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
|
|
61
61
|
"meta": {
|
|
62
62
|
"makersclaw": {
|
|
63
|
-
"version": "0.8.
|
|
63
|
+
"version": "0.8.1",
|
|
64
64
|
"useWhen": "A screen edits one record's fields: a profile, a product, a team member, account details, a sign-up or application form, a settings page of fields with one Save. Not for a list of records (data-table), for toggles that each save on their own (settings-list), or for sign-in (auth-form)."
|
|
65
65
|
}
|
|
66
66
|
}
|