makersclaw 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -4
- package/package.json +1 -1
- package/r/activity-log.json +52 -0
- package/r/agent-changes.json +51 -0
- package/r/agent-chat.json +53 -0
- package/r/agent-column.json +85 -0
- package/r/agent-dock.json +57 -0
- package/r/agent-kit.json +49 -0
- package/r/agent-marks.json +49 -0
- package/r/agent-pane.json +93 -0
- package/r/ai-action.json +56 -0
- package/r/annotate.json +4 -4
- package/r/approval-request.json +2 -2
- package/r/approval.json +54 -0
- package/r/ask-bar.json +50 -0
- package/r/audio-player.json +2 -2
- package/r/auth-form.json +2 -2
- package/r/automation-editor.json +66 -0
- package/r/automation-list.json +57 -0
- package/r/automation-model.json +118 -0
- package/r/automations-screen.json +71 -0
- package/r/board.json +2 -2
- package/r/calendar-view.json +3 -3
- package/r/cart.json +2 -2
- package/r/chart.json +5 -5
- package/r/chat-pane.json +84 -0
- package/r/chat.json +3 -3
- package/r/code-editor.json +2 -2
- package/r/comment-box.json +3 -3
- package/r/connect-card.json +51 -0
- package/r/contact-section.json +2 -2
- package/r/core.json +18 -6
- package/r/cost.json +51 -0
- package/r/csv-import.json +3 -3
- package/r/data-picker.json +60 -0
- package/r/data-table.json +2 -2
- package/r/diagram.json +2 -2
- package/r/doc-editor.json +2 -2
- package/r/doc-page.json +3 -3
- package/r/email-render.json +2 -2
- package/r/faq.json +2 -2
- package/r/file-upload.json +2 -2
- package/r/first-run.json +63 -0
- package/r/fix-note.json +57 -0
- package/r/flow-canvas.json +3 -3
- package/r/flow-editor.json +79 -0
- package/r/footer.json +3 -3
- package/r/gantt.json +3 -3
- package/r/job-panel.json +2 -2
- package/r/json-editor.json +3 -3
- package/r/library-shelf.json +56 -0
- package/r/map.json +2 -2
- package/r/margin-notes.json +67 -0
- package/r/menubar.json +2 -2
- package/r/mind-map.json +4 -4
- package/r/navigation-menu.json +2 -2
- package/r/needs-you.json +55 -0
- package/r/order-detail.json +2 -2
- package/r/org-chart.json +4 -4
- package/r/pace-bar.json +69 -0
- package/r/page-builder.json +3 -3
- package/r/payment-methods.json +2 -2
- package/r/pdf-document.json +2 -2
- package/r/pdf-viewer.json +3 -3
- package/r/product-detail.json +2 -2
- package/r/product-grid.json +3 -3
- package/r/record-form.json +2 -2
- package/r/record-grid.json +52 -0
- package/r/record-notes.json +4 -4
- package/r/registry.json +4089 -1510
- package/r/run-history.json +52 -0
- package/r/run-view.json +80 -0
- package/r/schedule-card.json +54 -0
- package/r/schedule-engine.json +79 -0
- package/r/schedule-gantt.json +83 -0
- package/r/schedule-ruler.json +64 -0
- package/r/search-dialog.json +2 -2
- package/r/settings-list.json +2 -2
- package/r/setup-checklist.json +2 -2
- package/r/share-dialog.json +2 -2
- package/r/sidebar.json +3 -3
- package/r/signature-pad.json +2 -2
- package/r/sketch-canvas.json +3 -3
- package/r/source-receipt.json +52 -0
- package/r/spreadsheet.json +3 -3
- package/r/stat-cards.json +3 -3
- package/r/step-picker.json +53 -0
- package/r/step-settings.json +72 -0
- package/r/suggestion.json +58 -0
- package/r/sum-bar.json +64 -0
- package/r/task-list.json +3 -3
- package/r/task-model.json +2 -2
- package/r/testimonials.json +2 -2
- package/r/tier-map.json +67 -0
- package/r/time-grid.json +73 -0
- package/r/time-strip.json +64 -0
- package/r/top-bar.json +66 -0
- package/r/transaction-list.json +2 -2
- package/r/trigger-picker.json +61 -0
- package/r/whiteboard.json +4 -4
- package/r/workflow-view.json +55 -0
- package/registry/blocks/activity-log/README.md +112 -0
- package/registry/blocks/activity-log/activity-log.jsx +351 -0
- package/registry/blocks/activity-log/block.json +33 -0
- package/registry/blocks/activity-log/demo.jsx +48 -0
- package/registry/blocks/activity-log/index.js +4 -0
- package/registry/blocks/agent-changes/README.md +94 -0
- package/registry/blocks/agent-changes/agent-changes.jsx +118 -0
- package/registry/blocks/agent-changes/block.json +28 -0
- package/registry/blocks/agent-changes/demo.jsx +39 -0
- package/registry/blocks/agent-changes/index.js +3 -0
- package/registry/blocks/agent-chat/README.md +145 -0
- package/registry/blocks/agent-chat/agent-chat.jsx +415 -0
- package/registry/blocks/agent-chat/block.json +30 -0
- package/registry/blocks/agent-chat/demo.jsx +165 -0
- package/registry/blocks/agent-chat/index.js +3 -0
- package/registry/blocks/agent-column/README.md +197 -0
- package/registry/blocks/agent-column/agent-column.jsx +492 -0
- package/registry/blocks/agent-column/block.json +67 -0
- package/registry/blocks/agent-column/column-picker.jsx +163 -0
- package/registry/blocks/agent-column/demo.jsx +50 -0
- package/registry/blocks/agent-column/index.js +7 -0
- package/registry/blocks/agent-column/model.js +164 -0
- package/registry/blocks/agent-column/receipt.jsx +483 -0
- package/registry/blocks/agent-column/spreadsheet-type.jsx +24 -0
- package/registry/blocks/agent-dock/README.md +210 -0
- package/registry/blocks/agent-dock/agent-dock.jsx +356 -0
- package/registry/blocks/agent-dock/block.json +47 -0
- package/registry/blocks/agent-dock/demo.jsx +153 -0
- package/registry/blocks/agent-dock/dock-model.js +118 -0
- package/registry/blocks/agent-dock/index.js +5 -0
- package/registry/blocks/agent-kit/README.md +73 -0
- package/registry/blocks/agent-kit/agent-kit.jsx +86 -0
- package/registry/blocks/agent-kit/block.json +10 -0
- package/registry/blocks/agent-kit/demo.jsx +35 -0
- package/registry/blocks/agent-kit/index.js +3 -0
- package/registry/blocks/agent-marks/README.md +80 -0
- package/registry/blocks/agent-marks/agent-marks.jsx +133 -0
- package/registry/blocks/agent-marks/block.json +27 -0
- package/registry/blocks/agent-marks/demo.jsx +49 -0
- package/registry/blocks/agent-marks/index.js +3 -0
- package/registry/blocks/agent-pane/README.md +225 -0
- package/registry/blocks/agent-pane/agent-pane.jsx +244 -0
- package/registry/blocks/agent-pane/block.json +41 -0
- package/registry/blocks/agent-pane/composer.jsx +6 -0
- package/registry/blocks/agent-pane/demo.jsx +233 -0
- package/registry/blocks/agent-pane/dock-context.js +10 -0
- package/registry/blocks/agent-pane/index.js +4 -0
- package/registry/blocks/agent-pane/prose.jsx +84 -0
- package/registry/blocks/agent-pane/questions.jsx +134 -0
- package/registry/blocks/agent-pane/transcript.jsx +268 -0
- package/registry/blocks/agent-pane/work.jsx +368 -0
- package/registry/blocks/ai-action/README.md +94 -0
- package/registry/blocks/ai-action/ai-action.jsx +188 -0
- package/registry/blocks/ai-action/block.json +34 -0
- package/registry/blocks/ai-action/demo.jsx +93 -0
- package/registry/blocks/ai-action/index.js +3 -0
- package/registry/blocks/annotate/README.md +14 -11
- package/registry/blocks/approval/README.md +145 -0
- package/registry/blocks/approval/approval.jsx +163 -0
- package/registry/blocks/approval/block.json +31 -0
- package/registry/blocks/approval/demo.jsx +121 -0
- package/registry/blocks/approval/index.js +3 -0
- package/registry/blocks/ask-bar/README.md +105 -0
- package/registry/blocks/ask-bar/ask-bar.jsx +166 -0
- package/registry/blocks/ask-bar/block.json +26 -0
- package/registry/blocks/ask-bar/demo.jsx +78 -0
- package/registry/blocks/ask-bar/index.js +3 -0
- package/registry/blocks/automation-editor/README.md +291 -0
- package/registry/blocks/automation-editor/automation-editor.jsx +656 -0
- package/registry/blocks/automation-editor/block.json +39 -0
- package/registry/blocks/automation-editor/demo.jsx +284 -0
- package/registry/blocks/automation-editor/index.js +4 -0
- package/registry/blocks/automation-editor/run-inputs.jsx +82 -0
- package/registry/blocks/automation-list/README.md +129 -0
- package/registry/blocks/automation-list/automation-list.jsx +476 -0
- package/registry/blocks/automation-list/block.json +35 -0
- package/registry/blocks/automation-list/demo.jsx +67 -0
- package/registry/blocks/automation-list/index.js +3 -0
- package/registry/blocks/automation-model/README.md +109 -0
- package/registry/blocks/automation-model/block.json +135 -0
- package/registry/blocks/automation-model/catalog.js +256 -0
- package/registry/blocks/automation-model/check.js +161 -0
- package/registry/blocks/automation-model/demo.jsx +42 -0
- package/registry/blocks/automation-model/edit.js +262 -0
- package/registry/blocks/automation-model/fixes.js +287 -0
- package/registry/blocks/automation-model/glyph.jsx +13 -0
- package/registry/blocks/automation-model/graph.js +410 -0
- package/registry/blocks/automation-model/index.js +33 -0
- package/registry/blocks/automation-model/overlay.js +175 -0
- package/registry/blocks/automation-model/parts.jsx +94 -0
- package/registry/blocks/automation-model/schedule.js +53 -0
- package/registry/blocks/automation-model/templates.js +128 -0
- package/registry/blocks/automation-model/values.js +29 -0
- package/registry/blocks/automation-model/words.js +439 -0
- package/registry/blocks/automations-screen/README.md +207 -0
- package/registry/blocks/automations-screen/automations-screen.jsx +452 -0
- package/registry/blocks/automations-screen/block.json +47 -0
- package/registry/blocks/automations-screen/demo.jsx +364 -0
- package/registry/blocks/automations-screen/index.js +4 -0
- package/registry/blocks/automations-screen/screen-model.js +129 -0
- package/registry/blocks/chart/cartesian.jsx +1 -1
- package/registry/blocks/chart/core.jsx +33 -23
- package/registry/blocks/chart/pie.jsx +1 -1
- package/registry/blocks/chat/README.md +4 -1
- package/registry/blocks/chat/demo.jsx +2 -2
- package/registry/blocks/chat-pane/README.md +143 -0
- package/registry/blocks/chat-pane/block.json +48 -0
- package/registry/blocks/chat-pane/chat-attach.js +166 -0
- package/registry/blocks/chat-pane/chat-composer.jsx +195 -0
- package/registry/blocks/chat-pane/chat-context.js +17 -0
- package/registry/blocks/chat-pane/chat-model.js +43 -0
- package/registry/blocks/chat-pane/chat-pane.jsx +398 -0
- package/registry/blocks/chat-pane/chats-list.jsx +52 -0
- package/registry/blocks/chat-pane/demo.jsx +103 -0
- package/registry/blocks/chat-pane/index.js +9 -0
- package/registry/blocks/connect-card/README.md +99 -0
- package/registry/blocks/connect-card/block.json +10 -0
- package/registry/blocks/connect-card/connect-card.jsx +103 -0
- package/registry/blocks/connect-card/demo.jsx +49 -0
- package/registry/blocks/connect-card/index.js +3 -0
- package/registry/blocks/cost/README.md +115 -0
- package/registry/blocks/cost/block.json +10 -0
- package/registry/blocks/cost/cost.jsx +176 -0
- package/registry/blocks/cost/demo.jsx +66 -0
- package/registry/blocks/cost/index.js +3 -0
- package/registry/blocks/data-picker/README.md +81 -0
- package/registry/blocks/data-picker/block.json +38 -0
- package/registry/blocks/data-picker/data-picker.jsx +210 -0
- package/registry/blocks/data-picker/demo.jsx +34 -0
- package/registry/blocks/data-picker/fields.js +89 -0
- package/registry/blocks/data-picker/index.js +4 -0
- package/registry/blocks/data-table/demo.jsx +1 -1
- package/registry/blocks/first-run/README.md +118 -0
- package/registry/blocks/first-run/block.json +11 -0
- package/registry/blocks/first-run/demo.jsx +88 -0
- package/registry/blocks/first-run/first-run-model.js +51 -0
- package/registry/blocks/first-run/first-run.jsx +156 -0
- package/registry/blocks/first-run/index.js +5 -0
- package/registry/blocks/first-run/probe-rules.js +26 -0
- package/registry/blocks/fix-note/README.md +79 -0
- package/registry/blocks/fix-note/block.json +35 -0
- package/registry/blocks/fix-note/demo.jsx +30 -0
- package/registry/blocks/fix-note/fix-note.jsx +235 -0
- package/registry/blocks/fix-note/index.js +3 -0
- package/registry/blocks/flow-canvas/README.md +15 -8
- package/registry/blocks/flow-editor/README.md +108 -0
- package/registry/blocks/flow-editor/block.json +44 -0
- package/registry/blocks/flow-editor/demo.jsx +115 -0
- package/registry/blocks/flow-editor/flow-canvas.jsx +568 -0
- package/registry/blocks/flow-editor/flow-editor.jsx +154 -0
- package/registry/blocks/flow-editor/index.js +6 -0
- package/registry/blocks/flow-editor/minimap.jsx +78 -0
- package/registry/blocks/flow-editor/outline.jsx +134 -0
- package/registry/blocks/footer/README.md +3 -2
- package/registry/blocks/library-shelf/README.md +117 -0
- package/registry/blocks/library-shelf/block.json +28 -0
- package/registry/blocks/library-shelf/demo.jsx +68 -0
- package/registry/blocks/library-shelf/index.js +3 -0
- package/registry/blocks/library-shelf/library-shelf.jsx +211 -0
- package/registry/blocks/library-shelf/probe-rules.js +15 -0
- package/registry/blocks/margin-notes/README.md +222 -0
- package/registry/blocks/margin-notes/block.json +10 -0
- package/registry/blocks/margin-notes/demo.jsx +144 -0
- package/registry/blocks/margin-notes/index.js +20 -0
- package/registry/blocks/margin-notes/margin-model.js +224 -0
- package/registry/blocks/margin-notes/margin-notes.jsx +465 -0
- package/registry/blocks/mind-map/README.md +4 -2
- package/registry/blocks/needs-you/README.md +177 -0
- package/registry/blocks/needs-you/block.json +34 -0
- package/registry/blocks/needs-you/demo.jsx +143 -0
- package/registry/blocks/needs-you/index.js +3 -0
- package/registry/blocks/needs-you/needs-you.jsx +441 -0
- package/registry/blocks/org-chart/README.md +4 -2
- package/registry/blocks/pace-bar/README.md +230 -0
- package/registry/blocks/pace-bar/block.json +10 -0
- package/registry/blocks/pace-bar/demo.jsx +255 -0
- package/registry/blocks/pace-bar/index.js +22 -0
- package/registry/blocks/pace-bar/pace-bar.jsx +461 -0
- package/registry/blocks/pace-bar/pace-model.js +288 -0
- package/registry/blocks/page-builder/README.md +1 -1
- package/registry/blocks/page-builder/demo.jsx +3 -3
- package/registry/blocks/pdf-viewer/README.md +4 -1
- package/registry/blocks/record-grid/README.md +100 -0
- package/registry/blocks/record-grid/block.json +28 -0
- package/registry/blocks/record-grid/demo.jsx +43 -0
- package/registry/blocks/record-grid/index.js +3 -0
- package/registry/blocks/record-grid/record-grid.jsx +251 -0
- package/registry/blocks/run-history/README.md +138 -0
- package/registry/blocks/run-history/block.json +10 -0
- package/registry/blocks/run-history/demo.jsx +74 -0
- package/registry/blocks/run-history/index.js +3 -0
- package/registry/blocks/run-history/run-history.jsx +218 -0
- package/registry/blocks/run-view/README.md +174 -0
- package/registry/blocks/run-view/block.json +50 -0
- package/registry/blocks/run-view/demo.jsx +286 -0
- package/registry/blocks/run-view/index.js +5 -0
- package/registry/blocks/run-view/run-controls.jsx +266 -0
- package/registry/blocks/run-view/run-view.jsx +236 -0
- package/registry/blocks/run-view/runs-list.jsx +99 -0
- package/registry/blocks/schedule-card/README.md +121 -0
- package/registry/blocks/schedule-card/block.json +10 -0
- package/registry/blocks/schedule-card/demo.jsx +82 -0
- package/registry/blocks/schedule-card/index.js +3 -0
- package/registry/blocks/schedule-card/schedule-card.jsx +136 -0
- package/registry/blocks/schedule-engine/README.md +137 -0
- package/registry/blocks/schedule-engine/block.json +10 -0
- package/registry/blocks/schedule-engine/demo.jsx +82 -0
- package/registry/blocks/schedule-engine/engine.js +769 -0
- package/registry/blocks/schedule-engine/index.js +9 -0
- package/registry/blocks/schedule-engine/ops.js +71 -0
- package/registry/blocks/schedule-engine/place.js +294 -0
- package/registry/blocks/schedule-engine/tree.js +79 -0
- package/registry/blocks/schedule-engine/waits.js +80 -0
- package/registry/blocks/schedule-gantt/README.md +159 -0
- package/registry/blocks/schedule-gantt/block.json +10 -0
- package/registry/blocks/schedule-gantt/demo.jsx +223 -0
- package/registry/blocks/schedule-gantt/gantt-model.js +219 -0
- package/registry/blocks/schedule-gantt/geometry.js +176 -0
- package/registry/blocks/schedule-gantt/index.js +26 -0
- package/registry/blocks/schedule-gantt/schedule-gantt.jsx +882 -0
- package/registry/blocks/schedule-gantt/sheet.jsx +372 -0
- package/registry/blocks/schedule-gantt/track.jsx +190 -0
- package/registry/blocks/schedule-ruler/README.md +148 -0
- package/registry/blocks/schedule-ruler/block.json +10 -0
- package/registry/blocks/schedule-ruler/demo.jsx +181 -0
- package/registry/blocks/schedule-ruler/index.js +21 -0
- package/registry/blocks/schedule-ruler/ruler-model.js +239 -0
- package/registry/blocks/schedule-ruler/schedule-ruler.jsx +548 -0
- package/registry/blocks/sidebar/README.md +23 -13
- package/registry/blocks/sketch-canvas/README.md +15 -9
- package/registry/blocks/source-receipt/README.md +97 -0
- package/registry/blocks/source-receipt/block.json +30 -0
- package/registry/blocks/source-receipt/demo.jsx +71 -0
- package/registry/blocks/source-receipt/index.js +3 -0
- package/registry/blocks/source-receipt/source-receipt.jsx +107 -0
- package/registry/blocks/spreadsheet/spreadsheet.jsx +15 -1
- package/registry/blocks/step-picker/README.md +78 -0
- package/registry/blocks/step-picker/block.json +31 -0
- package/registry/blocks/step-picker/demo.jsx +23 -0
- package/registry/blocks/step-picker/index.js +3 -0
- package/registry/blocks/step-picker/step-picker.jsx +144 -0
- package/registry/blocks/step-settings/README.md +100 -0
- package/registry/blocks/step-settings/block.json +44 -0
- package/registry/blocks/step-settings/demo.jsx +93 -0
- package/registry/blocks/step-settings/fields.jsx +58 -0
- package/registry/blocks/step-settings/flow-settings.jsx +115 -0
- package/registry/blocks/step-settings/index.js +5 -0
- package/registry/blocks/step-settings/step-settings.jsx +290 -0
- package/registry/blocks/suggestion/README.md +171 -0
- package/registry/blocks/suggestion/block.json +40 -0
- package/registry/blocks/suggestion/demo.jsx +91 -0
- package/registry/blocks/suggestion/index.js +3 -0
- package/registry/blocks/suggestion/suggestion.jsx +436 -0
- package/registry/blocks/sum-bar/README.md +122 -0
- package/registry/blocks/sum-bar/block.json +10 -0
- package/registry/blocks/sum-bar/demo.jsx +110 -0
- package/registry/blocks/sum-bar/index.js +5 -0
- package/registry/blocks/sum-bar/sum-bar.jsx +209 -0
- package/registry/blocks/sum-bar/sum-model.js +126 -0
- package/registry/blocks/tier-map/README.md +217 -0
- package/registry/blocks/tier-map/block.json +10 -0
- package/registry/blocks/tier-map/demo.jsx +222 -0
- package/registry/blocks/tier-map/index.js +24 -0
- package/registry/blocks/tier-map/tier-map.jsx +574 -0
- package/registry/blocks/tier-map/tier-model.js +318 -0
- package/registry/blocks/time-grid/README.md +305 -0
- package/registry/blocks/time-grid/block.json +10 -0
- package/registry/blocks/time-grid/demo.jsx +279 -0
- package/registry/blocks/time-grid/grid-model.js +322 -0
- package/registry/blocks/time-grid/index.js +25 -0
- package/registry/blocks/time-grid/time-grid.jsx +1325 -0
- package/registry/blocks/time-strip/README.md +141 -0
- package/registry/blocks/time-strip/block.json +10 -0
- package/registry/blocks/time-strip/demo.jsx +129 -0
- package/registry/blocks/time-strip/index.js +19 -0
- package/registry/blocks/time-strip/strip-model.js +178 -0
- package/registry/blocks/time-strip/time-strip.jsx +260 -0
- package/registry/blocks/top-bar/README.md +149 -0
- package/registry/blocks/top-bar/block.json +35 -0
- package/registry/blocks/top-bar/demo.jsx +78 -0
- package/registry/blocks/top-bar/index.js +5 -0
- package/registry/blocks/top-bar/probe-rules.js +74 -0
- package/registry/blocks/top-bar/top-bar-model.js +152 -0
- package/registry/blocks/top-bar/top-bar.jsx +244 -0
- package/registry/blocks/trigger-picker/README.md +98 -0
- package/registry/blocks/trigger-picker/block.json +38 -0
- package/registry/blocks/trigger-picker/demo.jsx +59 -0
- package/registry/blocks/trigger-picker/index.js +3 -0
- package/registry/blocks/trigger-picker/trigger-picker.jsx +374 -0
- package/registry/blocks/whiteboard/README.md +20 -14
- package/registry/blocks/workflow-view/README.md +123 -0
- package/registry/blocks/workflow-view/block.json +10 -0
- package/registry/blocks/workflow-view/demo.jsx +90 -0
- package/registry/blocks/workflow-view/index.js +3 -0
- package/registry/blocks/workflow-view/workflow-view.jsx +203 -0
- package/src/cli.mjs +2 -2
- package/src/install.mjs +100 -22
- package/src/registry.mjs +37 -3
- package/templates/next/browser/lib/data.js +6 -1
- package/templates/next/files/README.md +909 -26
- package/templates/next/files/app/shell.jsx +54 -37
- package/templates/next/files/check/automation-format.ts +2103 -0
- package/templates/next/files/check/automation-package.ts +142 -0
- package/templates/next/files/check/run.mjs +1043 -12
- package/templates/next/files/components/ui/field.jsx +17 -4
- package/templates/next/files/components/ui/icon.jsx +13 -0
- package/templates/next/files/components/ui/index.js +3 -1
- package/templates/next/files/components/ui/layout.jsx +382 -0
- package/templates/next/files/components/ui/notice.jsx +70 -0
- package/templates/next/files/components/ui/page.jsx +124 -0
- package/templates/next/files/components/ui/stat.jsx +22 -7
- package/templates/next/files/components/ui/toast.jsx +6 -3
- package/templates/next/files/lib/automations.js +660 -0
- package/templates/next/files/lib/claw.js +244 -5
- package/templates/next/files/lib/door.js +92 -0
- package/templates/next/files/lib/hooks.js +1694 -6
- package/templates/next/files/lib/platform.js +611 -14
- package/templates/next/template.json +22 -3
|
@@ -23,10 +23,14 @@ const CONTROL = `w-full ${BOX}`;
|
|
|
23
23
|
const ownWidth = (className) => /(?:^|\s)!?(?:w|size)-/.test(className ?? "");
|
|
24
24
|
|
|
25
25
|
/** Label, control, hint and error in one stack. Pass `id`; the label
|
|
26
|
-
* points at it. `error` (a string) marks the control invalid.
|
|
26
|
+
* points at it. `error` (a string) marks the control invalid.
|
|
27
|
+
* Two Fields side by side in a grid row are stretched to the taller one;
|
|
28
|
+
* `content-start` keeps the shorter one's label and control at the top
|
|
29
|
+
* where they were (without it the grid spread them apart, and Start
|
|
30
|
+
* date's box sat lower than Target date's when only Target had a hint). */
|
|
27
31
|
export function Field({ id, label, hint, error, required, className, children }) {
|
|
28
32
|
return (
|
|
29
|
-
<div className={cx("grid gap-1.5", className)}>
|
|
33
|
+
<div className={cx("grid content-start gap-1.5", className)}>
|
|
30
34
|
{label ? (
|
|
31
35
|
<label htmlFor={id} className="text-sm font-medium text-foreground">
|
|
32
36
|
{label}
|
|
@@ -177,15 +181,24 @@ export function Textarea({ className, invalid, rows = 4, autoGrow = false, minRo
|
|
|
177
181
|
* `options` is empty it renders `empty` (a short muted line, or nothing
|
|
178
182
|
* when `empty` is null) rather than a disabled dropdown. For "pick a
|
|
179
183
|
* saved record or make a new one" use RecordSwitcher (components/parts).
|
|
184
|
+
* A width in `className` (w-40, w-fit, w-[12rem], size-10) sets the width of
|
|
185
|
+
* the whole control, as it does on an Input (0.8.1).
|
|
180
186
|
*/
|
|
181
187
|
export function Select({ options, empty = "Nothing to choose yet.", className, invalid, children, ...rest }) {
|
|
182
188
|
if (options && options.length === 0 && !children) {
|
|
183
189
|
return empty ? <p className="text-sm text-muted-foreground">{empty}</p> : null;
|
|
184
190
|
}
|
|
191
|
+
// As Input: a width in className (w-40, w-auto, w-[12rem], size-10) is
|
|
192
|
+
// honoured. The chevron sits on the wrapper, so the width goes to the
|
|
193
|
+
// wrapper and the select fills it; the rest of className stays on the
|
|
194
|
+
// select. Without one it is as wide as its column, as before.
|
|
195
|
+
const tokens = String(className ?? "").split(/\s+/).filter(Boolean);
|
|
196
|
+
const own = tokens.filter((t) => /^!?(?:w|size)-/.test(t));
|
|
197
|
+
const others = tokens.filter((t) => !own.includes(t));
|
|
185
198
|
return (
|
|
186
|
-
<span className="relative block">
|
|
199
|
+
<span className={cx("relative block", own.join(" "))}>
|
|
187
200
|
<select
|
|
188
|
-
className={cx(CONTROL, "appearance-none pr-9",
|
|
201
|
+
className={cx(CONTROL, "appearance-none pr-9", others.join(" "))}
|
|
189
202
|
aria-invalid={invalid ? true : undefined}
|
|
190
203
|
{...rest}
|
|
191
204
|
>
|
|
@@ -186,6 +186,19 @@ const PATHS = {
|
|
|
186
186
|
"calendar-check": "M3 5h18v16H3zM16 3v4M8 3v4M3 10h18M9 15.5l2 2 4-4",
|
|
187
187
|
"trash-2": "M3 6h18M8 6V4h8v2m-9 0 1 14h8l1-14M10 11v6M14 11v6",
|
|
188
188
|
network: "M9 2h6v6H9zM2 16h6v6H2zM16 16h6v6h-6zM12 8v4M5 16v-4h14v4",
|
|
189
|
+
// Automations (flow-editor and the blocks around it): one glyph per kind
|
|
190
|
+
// of step, drawn to the same 24 box. calendar-clock is a schedule's
|
|
191
|
+
// trigger, split a condition's fork, braces a set of values, timer a
|
|
192
|
+
// wait, plug a connected tool, bot the agent, workflow another
|
|
193
|
+
// automation, book-open a read of the company brain.
|
|
194
|
+
"calendar-clock": "M12 21H5V5h14v7M3 10h18M8 3v4M16 3v4M17.5 14a4 4 0 1 0 .01 0M17.5 16v2l1.2.8",
|
|
195
|
+
split: "M3 12h5c3 0 3-6 6-6h6M8 12c3 0 3 6 6 6h6M17 3l3 3-3 3M17 15l3 3-3 3",
|
|
196
|
+
braces: "M8 3H7a2 2 0 0 0-2 2v4a2 2 0 0 1-2 2 2 2 0 0 1 2 2v4a2 2 0 0 0 2 2h1M16 3h1a2 2 0 0 1 2 2v4a2 2 0 0 0 2 2 2 2 0 0 0-2 2v4a2 2 0 0 1-2 2h-1",
|
|
197
|
+
timer: "M10 2h4M12 8v5l3 2M12 6a8 8 0 1 0 0 16 8 8 0 0 0 0-16Z",
|
|
198
|
+
plug: "M9 2v5M15 2v5M6 7h12v4a6 6 0 0 1-12 0V7ZM12 17v5",
|
|
199
|
+
bot: "M12 3v3M5 8h14v11H5ZM9 13h.01M15 13h.01M2 13v3M22 13v3M9 17h6",
|
|
200
|
+
workflow: "M3 4h7v7H3zM14 13h7v7h-7zM10 7.5h2.5a2 2 0 0 1 2 2V13",
|
|
201
|
+
"book-open": "M12 6c-2-2-5-3-9-3v15c4 0 7 1 9 3 2-2 5-3 9-3V3c-4 0-7 1-9 3ZM12 6v15",
|
|
189
202
|
};
|
|
190
203
|
|
|
191
204
|
const BRANDS = {
|
|
@@ -5,7 +5,7 @@ export { cx } from "./cx";
|
|
|
5
5
|
export { Icon, ICON_NAMES } from "./icon";
|
|
6
6
|
export { Button, IconButton } from "./button";
|
|
7
7
|
export { Field, Input, TimeInput, Textarea, Select, Switch, Checkbox } from "./field";
|
|
8
|
-
export { Card, Notice, Badge, Kicker, Empty } from "./notice";
|
|
8
|
+
export { Card, ActionCard, Notice, Badge, Kicker, Empty } from "./notice";
|
|
9
9
|
export { Sheet, Confirm } from "./sheet";
|
|
10
10
|
export { Tabs, TabPanel } from "./tabs";
|
|
11
11
|
export { Menu, MenuPanel } from "./menu";
|
|
@@ -48,6 +48,8 @@ export { Stat, StatGroup, Delta, StatusDot, Legend, Meter } from "./stat";
|
|
|
48
48
|
export { Steps } from "./steps";
|
|
49
49
|
export { Separator, Kbd, KbdGroup, AspectRatio } from "./separator";
|
|
50
50
|
export { ScrollArea } from "./scroll-area";
|
|
51
|
+
export { AppLayout, DocumentLayout } from "./layout";
|
|
52
|
+
export { PageLayout } from "./page";
|
|
51
53
|
export { Slider } from "./slider";
|
|
52
54
|
export { InputGroup, PasswordInput } from "./input-group";
|
|
53
55
|
export { Image } from "./image";
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { createContext, useContext, useEffect, useRef, useState } from "react";
|
|
4
|
+
import { cx } from "./cx";
|
|
5
|
+
import { Button, IconButton } from "./button";
|
|
6
|
+
import { Sheet } from "./sheet";
|
|
7
|
+
|
|
8
|
+
// Layouts — who owns the window's height, and who scrolls.
|
|
9
|
+
//
|
|
10
|
+
// Every design-first build of 2026-10-07 shipped a shell of `min-h-dvh`
|
|
11
|
+
// that grew with its content: the page itself scrolled, no pane scrolled
|
|
12
|
+
// on its own, and a footer, an inspector or a button below a long
|
|
13
|
+
// article sat past the end of the iframe, where nothing could reach it.
|
|
14
|
+
// So the kit owns the window's height, once, and the app's code never
|
|
15
|
+
// sizes anything by the window (`npm run check` refuses min-h-dvh,
|
|
16
|
+
// h-screen, 100vh, h-[80vh]… outside the kit, rule 12).
|
|
17
|
+
//
|
|
18
|
+
// AppLayout — the app frame. Exactly one owns the viewport: the
|
|
19
|
+
// outermost. A layout inside another fills the slot it is in instead,
|
|
20
|
+
// whatever `height` says, so there is never a second viewport owner. The
|
|
21
|
+
// shell (app/shell.jsx) is the outermost one in every app the template
|
|
22
|
+
// lays; a surface that needs an inspector or a fill of its own returns
|
|
23
|
+
// an AppLayout as its outermost element, and it fills the shell's
|
|
24
|
+
// content edge to edge (the content's padding drops for it).
|
|
25
|
+
//
|
|
26
|
+
// header the top bar, always in view (title, the surface's
|
|
27
|
+
// commands). The bar draws its own bottom border
|
|
28
|
+
// navigation the surfaces, a page tree: a rail beside the content
|
|
29
|
+
// from md (768px); below md a Sheet from the leading
|
|
30
|
+
// edge, opened by the menu button in the top bar. A
|
|
31
|
+
// press on a link or a button inside closes it
|
|
32
|
+
// navigationLabel the Sheet's title and the menu button's words
|
|
33
|
+
// ("Navigation")
|
|
34
|
+
// navigationWidth the rail, in px (176; 160 to 320)
|
|
35
|
+
// children the content. It scrolls inside its own region
|
|
36
|
+
// contentMode scroll (the default): the content region scrolls ·
|
|
37
|
+
// fill: the region is a bounded flex column and the
|
|
38
|
+
// child owns its own scrolling (a canvas, a grid, a
|
|
39
|
+
// spreadsheet, an editor that scrolls itself)
|
|
40
|
+
// contentLabel the content region's name for a screen reader ("Content")
|
|
41
|
+
// contentClassName spacing and token backgrounds for the content
|
|
42
|
+
// region (p-4 sm:p-6, bg-card). Never a height or an
|
|
43
|
+
// overflow: the layout owns those
|
|
44
|
+
// scrollKey when it changes the content scrolls back to its top
|
|
45
|
+
// and the navigation Sheet closes: pass the route
|
|
46
|
+
// (usePathname()), since the frame stays mounted while
|
|
47
|
+
// the surfaces change inside it
|
|
48
|
+
// inspector the details, review or feedback panel beside the
|
|
49
|
+
// content, scrolling on its own
|
|
50
|
+
// inspectorLabel the inspector region's name ("Details")
|
|
51
|
+
// inspectorWidth in px (360; 240 to 560). Builds have needed 300 and 440
|
|
52
|
+
// inspectorAt md · lg (the default) · xl: the width from which the
|
|
53
|
+
// inspector sits beside the content
|
|
54
|
+
// inspectorIn window (the default): inspectorAt is the window's
|
|
55
|
+
// width (768 · 1024 · 1280 px) · frame: it is the
|
|
56
|
+
// width of the body between the rail and the dock
|
|
57
|
+
// (42 · 48 · 64 rem), so an agent dock that takes a
|
|
58
|
+
// quarter of the window makes the inspector go under
|
|
59
|
+
// the work when there is no room, not squeeze it
|
|
60
|
+
// footer under the content, always in view (the save state,
|
|
61
|
+
// the composer, the commands for what is above)
|
|
62
|
+
// dock a column beside the whole body (rail, content and
|
|
63
|
+
// inspector), under the header, that the app's own
|
|
64
|
+
// block draws: the agent's dock (blocks/agent-dock).
|
|
65
|
+
// It sizes itself, remembers its width and, where the
|
|
66
|
+
// body is too narrow, lays itself over the app as a
|
|
67
|
+
// sheet; the layout only gives it a place in the row
|
|
68
|
+
// and takes no part in its width. Nothing when it is
|
|
69
|
+
// not given
|
|
70
|
+
// height viewport (the default): the frame is the window's
|
|
71
|
+
// height · parent: it fills a bounded parent. Nested,
|
|
72
|
+
// it is parent whatever this says
|
|
73
|
+
// className the frame's own tokens; never a height or an overflow
|
|
74
|
+
//
|
|
75
|
+
// Below `inspectorAt`, where there is no room for two columns:
|
|
76
|
+
// - contentMode scroll: the content and the inspector share ONE scroll
|
|
77
|
+
// region, the inspector after the content. Everything is reachable
|
|
78
|
+
// by scrolling, and nothing is hidden behind a button.
|
|
79
|
+
// - contentMode fill: the content keeps the room and the inspector is
|
|
80
|
+
// a bounded pane under it (at most 45% of the height), scrolling on
|
|
81
|
+
// its own. A fill child has no flow to stack into, and a pane with
|
|
82
|
+
// no bound under it is cut off (workspace-layout's fault, 2026-10-07).
|
|
83
|
+
// The footer stays under both, in view.
|
|
84
|
+
//
|
|
85
|
+
// In print the frame lets go: no bound, no overflow, no navigation, no
|
|
86
|
+
// header, inspector or footer; the content flows onto pages. A page meant
|
|
87
|
+
// for print or export is a DocumentLayout inside the content.
|
|
88
|
+
|
|
89
|
+
const Nested = createContext(false);
|
|
90
|
+
|
|
91
|
+
const px = (value, fallback, min, max) => {
|
|
92
|
+
const n = Number(value);
|
|
93
|
+
return `${Number.isFinite(n) && n > 0 ? Math.min(max, Math.max(min, Math.round(n))) : fallback}px`;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// Tailwind reads class names whole from the source, so every breakpoint
|
|
97
|
+
// is written out.
|
|
98
|
+
const BESIDE = {
|
|
99
|
+
md: {
|
|
100
|
+
main: "md:grid md:grid-cols-[minmax(0,1fr)_var(--app-inspector)] md:grid-rows-[minmax(0,1fr)_auto]",
|
|
101
|
+
flow: "md:contents",
|
|
102
|
+
content: "md:col-start-1 md:row-start-1",
|
|
103
|
+
scrolls: "md:min-h-0 md:overflow-y-auto md:overscroll-contain",
|
|
104
|
+
inspector: "md:col-start-2 md:row-span-2 md:row-start-1 md:max-h-none md:min-h-0 md:overflow-y-auto md:border-l md:border-t-0",
|
|
105
|
+
footer: "md:col-start-1 md:row-start-2",
|
|
106
|
+
},
|
|
107
|
+
lg: {
|
|
108
|
+
main: "lg:grid lg:grid-cols-[minmax(0,1fr)_var(--app-inspector)] lg:grid-rows-[minmax(0,1fr)_auto]",
|
|
109
|
+
flow: "lg:contents",
|
|
110
|
+
content: "lg:col-start-1 lg:row-start-1",
|
|
111
|
+
scrolls: "lg:min-h-0 lg:overflow-y-auto lg:overscroll-contain",
|
|
112
|
+
inspector: "lg:col-start-2 lg:row-span-2 lg:row-start-1 lg:max-h-none lg:min-h-0 lg:overflow-y-auto lg:border-l lg:border-t-0",
|
|
113
|
+
footer: "lg:col-start-1 lg:row-start-2",
|
|
114
|
+
},
|
|
115
|
+
xl: {
|
|
116
|
+
main: "xl:grid xl:grid-cols-[minmax(0,1fr)_var(--app-inspector)] xl:grid-rows-[minmax(0,1fr)_auto]",
|
|
117
|
+
flow: "xl:contents",
|
|
118
|
+
content: "xl:col-start-1 xl:row-start-1",
|
|
119
|
+
scrolls: "xl:min-h-0 xl:overflow-y-auto xl:overscroll-contain",
|
|
120
|
+
inspector: "xl:col-start-2 xl:row-span-2 xl:row-start-1 xl:max-h-none xl:min-h-0 xl:overflow-y-auto xl:border-l xl:border-t-0",
|
|
121
|
+
footer: "xl:col-start-1 xl:row-start-2",
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// The same classes by the width of the body's own column (a container
|
|
126
|
+
// query on the frame between the rail and the dock), for an app whose dock
|
|
127
|
+
// takes a quarter of the window: md is a body 42rem wide, lg 48rem, xl 64rem.
|
|
128
|
+
const BESIDE_FRAME = {
|
|
129
|
+
md: {
|
|
130
|
+
main: "@2xl:grid @2xl:grid-cols-[minmax(0,1fr)_var(--app-inspector)] @2xl:grid-rows-[minmax(0,1fr)_auto]",
|
|
131
|
+
flow: "@2xl:contents",
|
|
132
|
+
content: "@2xl:col-start-1 @2xl:row-start-1",
|
|
133
|
+
scrolls: "@2xl:min-h-0 @2xl:overflow-y-auto @2xl:overscroll-contain",
|
|
134
|
+
inspector: "@2xl:col-start-2 @2xl:row-span-2 @2xl:row-start-1 @2xl:max-h-none @2xl:min-h-0 @2xl:overflow-y-auto @2xl:border-l @2xl:border-t-0",
|
|
135
|
+
footer: "@2xl:col-start-1 @2xl:row-start-2",
|
|
136
|
+
},
|
|
137
|
+
lg: {
|
|
138
|
+
main: "@3xl:grid @3xl:grid-cols-[minmax(0,1fr)_var(--app-inspector)] @3xl:grid-rows-[minmax(0,1fr)_auto]",
|
|
139
|
+
flow: "@3xl:contents",
|
|
140
|
+
content: "@3xl:col-start-1 @3xl:row-start-1",
|
|
141
|
+
scrolls: "@3xl:min-h-0 @3xl:overflow-y-auto @3xl:overscroll-contain",
|
|
142
|
+
inspector: "@3xl:col-start-2 @3xl:row-span-2 @3xl:row-start-1 @3xl:max-h-none @3xl:min-h-0 @3xl:overflow-y-auto @3xl:border-l @3xl:border-t-0",
|
|
143
|
+
footer: "@3xl:col-start-1 @3xl:row-start-2",
|
|
144
|
+
},
|
|
145
|
+
xl: {
|
|
146
|
+
main: "@5xl:grid @5xl:grid-cols-[minmax(0,1fr)_var(--app-inspector)] @5xl:grid-rows-[minmax(0,1fr)_auto]",
|
|
147
|
+
flow: "@5xl:contents",
|
|
148
|
+
content: "@5xl:col-start-1 @5xl:row-start-1",
|
|
149
|
+
scrolls: "@5xl:min-h-0 @5xl:overflow-y-auto @5xl:overscroll-contain",
|
|
150
|
+
inspector: "@5xl:col-start-2 @5xl:row-span-2 @5xl:row-start-1 @5xl:max-h-none @5xl:min-h-0 @5xl:overflow-y-auto @5xl:border-l @5xl:border-t-0",
|
|
151
|
+
footer: "@5xl:col-start-1 @5xl:row-start-2",
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// inspectorIn="frame": the body's column is the container the BESIDE_FRAME
|
|
156
|
+
// classes answer to. Off, it is nothing: the children, in the same place.
|
|
157
|
+
function FrameBox({ on, children }) {
|
|
158
|
+
if (!on) return children;
|
|
159
|
+
return (
|
|
160
|
+
<div data-layout-frame className={cx("@container flex min-h-0 min-w-0 flex-1 flex-col", LETS_GO)}>
|
|
161
|
+
{children}
|
|
162
|
+
</div>
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const REGION = "outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring";
|
|
167
|
+
|
|
168
|
+
// The inspector holds its width and its contents fit it (UI review X4: a
|
|
169
|
+
// shelf's row ran out of the panel and its title was cut at the edge). A
|
|
170
|
+
// grid with no grid-cols-* has one `auto` column, and `auto` grows to its
|
|
171
|
+
// widest child's minimum width, past the panel; the usual inspector is
|
|
172
|
+
// exactly `<div className="grid content-start gap-3 p-4">`. So the panel's
|
|
173
|
+
// own direct children may not grow it (min-w-0), a direct child that is a
|
|
174
|
+
// grid with no columns of its own gets the one column that can shrink
|
|
175
|
+
// (minmax(0,1fr)), and nothing paints past the panel's edge (overflow-x
|
|
176
|
+
// hidden: a wide child wraps or truncates, it does not sideways-scroll).
|
|
177
|
+
const INSPECTOR_FIT =
|
|
178
|
+
"min-w-0 max-w-full overflow-x-hidden [&>*]:min-w-0 [&>.grid:not([class*=grid-cols])]:grid-cols-[minmax(0,1fr)]";
|
|
179
|
+
const LETS_GO = "print:block print:h-auto print:min-h-0 print:overflow-visible";
|
|
180
|
+
|
|
181
|
+
export function AppLayout({
|
|
182
|
+
header,
|
|
183
|
+
navigation,
|
|
184
|
+
navigationLabel = "Navigation",
|
|
185
|
+
navigationWidth,
|
|
186
|
+
children,
|
|
187
|
+
contentMode = "scroll",
|
|
188
|
+
contentLabel = "Content",
|
|
189
|
+
contentClassName,
|
|
190
|
+
scrollKey,
|
|
191
|
+
inspector,
|
|
192
|
+
inspectorLabel = "Details",
|
|
193
|
+
inspectorWidth,
|
|
194
|
+
inspectorAt = "lg",
|
|
195
|
+
inspectorIn = "window",
|
|
196
|
+
footer,
|
|
197
|
+
dock,
|
|
198
|
+
height = "viewport",
|
|
199
|
+
className,
|
|
200
|
+
}) {
|
|
201
|
+
const nested = useContext(Nested);
|
|
202
|
+
const bounded = nested || height === "parent";
|
|
203
|
+
const fill = contentMode === "fill";
|
|
204
|
+
const byFrame = inspectorIn === "frame";
|
|
205
|
+
const table = byFrame ? BESIDE_FRAME : BESIDE;
|
|
206
|
+
const at = table[inspectorAt] ?? table.lg;
|
|
207
|
+
const hasInspector = inspector != null && inspector !== false;
|
|
208
|
+
const hasNavigation = navigation != null && navigation !== false;
|
|
209
|
+
const shared = hasInspector && !fill; // below `at`: content and inspector in one scroll region
|
|
210
|
+
const [open, setOpen] = useState(false);
|
|
211
|
+
const flowRef = useRef(null);
|
|
212
|
+
const contentRef = useRef(null);
|
|
213
|
+
|
|
214
|
+
useEffect(() => {
|
|
215
|
+
if (flowRef.current) flowRef.current.scrollTop = 0;
|
|
216
|
+
if (contentRef.current) contentRef.current.scrollTop = 0;
|
|
217
|
+
setOpen(false);
|
|
218
|
+
}, [scrollKey]);
|
|
219
|
+
|
|
220
|
+
return (
|
|
221
|
+
<Nested.Provider value={true}>
|
|
222
|
+
<div
|
|
223
|
+
data-app-layout={bounded ? "parent" : "viewport"}
|
|
224
|
+
style={{ "--app-nav": px(navigationWidth, 176, 160, 320), "--app-inspector": px(inspectorWidth, 360, 240, 560) }}
|
|
225
|
+
className={cx(
|
|
226
|
+
"flex min-w-0 flex-col overflow-hidden bg-background text-foreground",
|
|
227
|
+
bounded ? "h-full min-h-0 flex-1" : "h-dvh",
|
|
228
|
+
LETS_GO,
|
|
229
|
+
className,
|
|
230
|
+
)}
|
|
231
|
+
>
|
|
232
|
+
{header != null && header !== false ? (
|
|
233
|
+
<div data-layout-header className="flex shrink-0 items-center gap-1 border-b border-border print:hidden">
|
|
234
|
+
{hasNavigation ? (
|
|
235
|
+
<IconButton icon="menu" label={`Open ${navigationLabel.toLowerCase()}`} onClick={() => setOpen(true)} className="ml-1 md:hidden" />
|
|
236
|
+
) : null}
|
|
237
|
+
<div className="min-w-0 flex-1">{header}</div>
|
|
238
|
+
</div>
|
|
239
|
+
) : hasNavigation ? (
|
|
240
|
+
<div data-layout-header className="flex shrink-0 items-center border-b border-border px-1 py-1 md:hidden print:hidden">
|
|
241
|
+
<Button variant="quiet" size="sm" icon="menu" onClick={() => setOpen(true)}>
|
|
242
|
+
{navigationLabel}
|
|
243
|
+
</Button>
|
|
244
|
+
</div>
|
|
245
|
+
) : null}
|
|
246
|
+
|
|
247
|
+
{hasNavigation ? (
|
|
248
|
+
<Sheet open={open} onClose={() => setOpen(false)} title={navigationLabel} side="start">
|
|
249
|
+
{open ? (
|
|
250
|
+
<div
|
|
251
|
+
onClick={(e) => {
|
|
252
|
+
if (e.target instanceof Element && e.target.closest("a,button")) setOpen(false);
|
|
253
|
+
}}
|
|
254
|
+
>
|
|
255
|
+
{navigation}
|
|
256
|
+
</div>
|
|
257
|
+
) : null}
|
|
258
|
+
</Sheet>
|
|
259
|
+
) : null}
|
|
260
|
+
|
|
261
|
+
<div data-layout-body className={cx("flex min-h-0 min-w-0 flex-1", LETS_GO)}>
|
|
262
|
+
{hasNavigation ? (
|
|
263
|
+
<div
|
|
264
|
+
data-layout-navigation
|
|
265
|
+
className="hidden shrink-0 flex-col overflow-y-auto overscroll-contain border-r border-border md:flex md:w-(--app-nav) print:hidden"
|
|
266
|
+
>
|
|
267
|
+
{navigation}
|
|
268
|
+
</div>
|
|
269
|
+
) : null}
|
|
270
|
+
|
|
271
|
+
<FrameBox on={byFrame && hasInspector}>
|
|
272
|
+
<div data-layout-main className={cx("flex min-h-0 min-w-0 flex-1 flex-col", hasInspector && at.main, LETS_GO)}>
|
|
273
|
+
<div
|
|
274
|
+
ref={flowRef}
|
|
275
|
+
data-layout-flow
|
|
276
|
+
className={cx(
|
|
277
|
+
"flex min-h-0 min-w-0 flex-1 flex-col",
|
|
278
|
+
shared && "overflow-y-auto overscroll-contain",
|
|
279
|
+
hasInspector && at.flow,
|
|
280
|
+
LETS_GO,
|
|
281
|
+
)}
|
|
282
|
+
>
|
|
283
|
+
<div
|
|
284
|
+
ref={contentRef}
|
|
285
|
+
data-layout-content
|
|
286
|
+
role="region"
|
|
287
|
+
aria-label={contentLabel}
|
|
288
|
+
tabIndex={0}
|
|
289
|
+
className={cx(
|
|
290
|
+
"flex min-w-0 flex-col",
|
|
291
|
+
REGION,
|
|
292
|
+
fill
|
|
293
|
+
? "min-h-0 flex-1 overflow-hidden"
|
|
294
|
+
: shared
|
|
295
|
+
? cx("shrink-0", at.scrolls)
|
|
296
|
+
: "min-h-0 flex-1 overflow-y-auto overscroll-contain",
|
|
297
|
+
hasInspector && at.content,
|
|
298
|
+
LETS_GO,
|
|
299
|
+
contentClassName,
|
|
300
|
+
// A surface that is a frame of its own runs to the edges.
|
|
301
|
+
"has-[>[data-app-layout]]:p-0",
|
|
302
|
+
)}
|
|
303
|
+
>
|
|
304
|
+
{children}
|
|
305
|
+
</div>
|
|
306
|
+
{hasInspector ? (
|
|
307
|
+
<div
|
|
308
|
+
data-layout-inspector
|
|
309
|
+
role="region"
|
|
310
|
+
aria-label={inspectorLabel}
|
|
311
|
+
tabIndex={0}
|
|
312
|
+
className={cx(
|
|
313
|
+
"shrink-0 border-t border-border print:hidden",
|
|
314
|
+
INSPECTOR_FIT,
|
|
315
|
+
REGION,
|
|
316
|
+
fill && "max-h-[45%] overflow-y-auto overscroll-contain",
|
|
317
|
+
at.inspector,
|
|
318
|
+
)}
|
|
319
|
+
>
|
|
320
|
+
{inspector}
|
|
321
|
+
</div>
|
|
322
|
+
) : null}
|
|
323
|
+
</div>
|
|
324
|
+
{footer != null && footer !== false ? (
|
|
325
|
+
<div data-layout-footer className={cx("shrink-0 border-t border-border print:hidden", hasInspector && at.footer)}>
|
|
326
|
+
{footer}
|
|
327
|
+
</div>
|
|
328
|
+
) : null}
|
|
329
|
+
</div>
|
|
330
|
+
</FrameBox>
|
|
331
|
+
{dock != null && dock !== false ? dock : null}
|
|
332
|
+
</div>
|
|
333
|
+
</div>
|
|
334
|
+
</Nested.Provider>
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// DocumentLayout — a page that flows: a report, an invoice, an article
|
|
339
|
+
// meant for print or export. It owns no height and scrolls nothing; it
|
|
340
|
+
// sits in the AppLayout's content (contentMode scroll), which scrolls it,
|
|
341
|
+
// and in print the frame lets go and it flows onto pages.
|
|
342
|
+
//
|
|
343
|
+
// children the document
|
|
344
|
+
// toolbar a bar held at the top while the document scrolls under it
|
|
345
|
+
// (Print, Export, the zoom); never printed
|
|
346
|
+
// width prose (the default: a measure for reading) · page (A4,
|
|
347
|
+
// 210mm) · letter (8.5in) · full. In print it is the paper's
|
|
348
|
+
// paper true: drawn as a sheet on the desk on screen (bg-card, a
|
|
349
|
+
// hairline, padding); plain in print
|
|
350
|
+
// align start (the default) · center
|
|
351
|
+
// label the document's name for a screen reader ("Document")
|
|
352
|
+
// className the document's own spacing and tokens; never a height
|
|
353
|
+
const WIDTHS = {
|
|
354
|
+
prose: "max-w-prose",
|
|
355
|
+
page: "max-w-[210mm]",
|
|
356
|
+
letter: "max-w-[8.5in]",
|
|
357
|
+
full: "max-w-none",
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
export function DocumentLayout({ children, toolbar, width = "prose", paper = false, align = "start", label = "Document", className }) {
|
|
361
|
+
return (
|
|
362
|
+
<div data-document-layout className="flex min-w-0 flex-col">
|
|
363
|
+
{toolbar != null && toolbar !== false ? (
|
|
364
|
+
<div data-document-toolbar className="sticky top-0 z-10 border-b border-border bg-background print:hidden">
|
|
365
|
+
{toolbar}
|
|
366
|
+
</div>
|
|
367
|
+
) : null}
|
|
368
|
+
<article
|
|
369
|
+
aria-label={label}
|
|
370
|
+
className={cx(
|
|
371
|
+
"w-full min-w-0 print:max-w-none",
|
|
372
|
+
WIDTHS[width] ?? WIDTHS.prose,
|
|
373
|
+
align === "center" && "mx-auto",
|
|
374
|
+
paper && "my-6 rounded-lg border border-border bg-card p-8 text-card-foreground shadow-sm print:my-0 print:rounded-none print:border-0 print:p-0 print:shadow-none",
|
|
375
|
+
className,
|
|
376
|
+
)}
|
|
377
|
+
>
|
|
378
|
+
{children}
|
|
379
|
+
</article>
|
|
380
|
+
</div>
|
|
381
|
+
);
|
|
382
|
+
}
|
|
@@ -53,6 +53,76 @@ export function Card({
|
|
|
53
53
|
);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
const STATUS_ICON = {
|
|
57
|
+
success: { name: "circle-check", box: "text-success" },
|
|
58
|
+
warning: { name: "warning", box: "text-warning" },
|
|
59
|
+
danger: { name: "warning", box: "text-destructive" },
|
|
60
|
+
neutral: { name: "info", box: "text-muted-foreground" },
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/** A card that is one step the member takes: a title (with an icon), what
|
|
64
|
+
* it does, optionally the state it is in, and ONE action under it.
|
|
65
|
+
* Stacked, always: the status line above a full-width button, so neither
|
|
66
|
+
* squeezes the other and the button's label has the room it needs.
|
|
67
|
+
*
|
|
68
|
+
* Cards in a row are the same height, and their buttons line up (UI
|
|
69
|
+
* review X6). A card stretches to the height of its row, and the status
|
|
70
|
+
* and the action are pinned to the card's bottom: title, words, children,
|
|
71
|
+
* then whatever room is left, then the status, then the button. So in a
|
|
72
|
+
* row of four the buttons sit on one line, however long each card's words
|
|
73
|
+
* are, and a status line in only some of them sits just above its button.
|
|
74
|
+
* It was `self-start` (each card only as tall as its own words) from 0.8.1's
|
|
75
|
+
* first cut: that meant ragged heights and buttons at different heights.
|
|
76
|
+
* The big empty block it was meant to stop came from a squeezed row (check
|
|
77
|
+
* rule 16, squeeze), not from stretching, and it cannot come back here:
|
|
78
|
+
* the spare room is a gap above the footer, never a blank box below it.
|
|
79
|
+
* icon a kit icon name, before the title
|
|
80
|
+
* title the step ("Connect your mailbox")
|
|
81
|
+
* description what it does, in a sentence or two (muted)
|
|
82
|
+
* children anything between the description and the state (a price
|
|
83
|
+
* line, a form, a list of rows)
|
|
84
|
+
* status { tone: success · warning · danger · neutral, children } the
|
|
85
|
+
* state it is in, with its mark, above the action. Say
|
|
86
|
+
* "Connected" only when the thing exists (an account, a file).
|
|
87
|
+
* action the one button (or a link), laid full width below
|
|
88
|
+
* fit true: only as tall as its own words (self-start), for ONE
|
|
89
|
+
* card on its own beside something unrelated. Never for
|
|
90
|
+
* cards in a row: check rule 19 (stretch) refuses a row of
|
|
91
|
+
* ActionCards that sets self-start, or items-start on the row
|
|
92
|
+
* For a card that holds a table or a list, use Card (padding none). For
|
|
93
|
+
* a screen's boxes in general: Card, ActionCard, Notice, never a hand-drawn
|
|
94
|
+
* bordered div. */
|
|
95
|
+
export function ActionCard({ icon, title, description, status, action, className, children, fit = false, as: Tag = "section" }) {
|
|
96
|
+
const mark = status ? (STATUS_ICON[status.tone] ?? STATUS_ICON.neutral) : null;
|
|
97
|
+
return (
|
|
98
|
+
<Tag
|
|
99
|
+
className={cx(
|
|
100
|
+
"flex min-w-0 flex-col gap-2.5 rounded-lg border border-border bg-card p-4 text-sm text-card-foreground",
|
|
101
|
+
fit && "self-start",
|
|
102
|
+
className,
|
|
103
|
+
)}
|
|
104
|
+
>
|
|
105
|
+
<h2 className="flex min-w-0 items-center gap-2 font-semibold leading-tight">
|
|
106
|
+
{icon ? <Icon name={icon} size={16} className="shrink-0 text-muted-foreground" /> : null}
|
|
107
|
+
<span className="min-w-0">{title}</span>
|
|
108
|
+
</h2>
|
|
109
|
+
{description ? <p className="text-muted-foreground">{description}</p> : null}
|
|
110
|
+
{children}
|
|
111
|
+
{status || action ? (
|
|
112
|
+
<div className="mt-auto grid gap-2.5">
|
|
113
|
+
{status ? (
|
|
114
|
+
<div className="flex items-start gap-2 border-t border-border pt-2.5 text-[13px]">
|
|
115
|
+
<Icon name={mark.name} size={14} className={cx("mt-0.5 shrink-0", mark.box)} />
|
|
116
|
+
<div className="min-w-0">{status.children}</div>
|
|
117
|
+
</div>
|
|
118
|
+
) : null}
|
|
119
|
+
{action ? <div className="grid">{action}</div> : null}
|
|
120
|
+
</div>
|
|
121
|
+
) : null}
|
|
122
|
+
</Tag>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
56
126
|
/** One line the user should read, with an optional action. `tone`
|
|
57
127
|
* neutral · info · success · warning · danger. Colour is the signal, so
|
|
58
128
|
* status tones are for status only — never decoration. */
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cx } from "./cx";
|
|
4
|
+
|
|
5
|
+
// PageLayout — a screen's main area with an optional side column, taking the
|
|
6
|
+
// full height of the content it sits in. The MAIN is the column that scrolls.
|
|
7
|
+
// The SIDE is the fixed one: it holds its place, top to bottom, while the
|
|
8
|
+
// main scrolls, and it is short.
|
|
9
|
+
//
|
|
10
|
+
// Why (UI review X12, 2026-10-12): Project Roadmap's Today ended at 680px in
|
|
11
|
+
// a 1240px window. The screen drew its own two-column grid inside the
|
|
12
|
+
// scrolling content, so it was only as tall as its words: the side column's
|
|
13
|
+
// divider stopped halfway down and the bottom half of the window was empty.
|
|
14
|
+
// Every screen with a side drew that grid by hand. This is the one.
|
|
15
|
+
// `npm run check` (rule 23, page) refuses a hand-drawn main + side with a
|
|
16
|
+
// ruled <aside>.
|
|
17
|
+
//
|
|
18
|
+
// What goes in the side, and what never does (UI review X13, 2026-10-12:
|
|
19
|
+
// Newsletter Engine's first run was a short intro on the left and a long
|
|
20
|
+
// "Name it" form in the right column. "this can only work if one column is
|
|
21
|
+
// fixed, usually the 3rd column, and the middle one is scrolling"): the side
|
|
22
|
+
// is a summary, the next step, a small panel, a few facts, a short list. It
|
|
23
|
+
// never needs a scroll of its own. A form, a paste box, a long list, a
|
|
24
|
+
// shelf of examples belong in the MAIN, where the page scrolls: a step that
|
|
25
|
+
// opens reveals its own card there. The side's own scroll is a last resort
|
|
26
|
+
// for a window shorter than the side (the bar is thin to say so), never how
|
|
27
|
+
// a long thing is meant to be read. `npm run check` (rule 27, sidelong)
|
|
28
|
+
// refuses a side column that holds a form or a long list.
|
|
29
|
+
//
|
|
30
|
+
// It lives INSIDE an AppLayout (the shell or the screen's own): the layout
|
|
31
|
+
// owns the window's height, this fills the content region it is given. Use
|
|
32
|
+
// the AppLayout's `contentMode="fill"` and give its content no padding
|
|
33
|
+
// (omit `contentClassName`): the columns pad themselves, so the divider can
|
|
34
|
+
// run to the edges. In contentMode scroll it fills the same.
|
|
35
|
+
//
|
|
36
|
+
// children the main area
|
|
37
|
+
// side the side column, the FIXED one: short things (a summary, the
|
|
38
|
+
// next step, a record's facts, a small panel). It stays put
|
|
39
|
+
// while the main scrolls; never a long form or a long list.
|
|
40
|
+
// Without it the page is one scrolling column
|
|
41
|
+
// sideWidth px (304; 220 to 560)
|
|
42
|
+
// sideAt md · lg (the default) · xl: the width of THIS page from
|
|
43
|
+
// which the side sits beside the main (42 · 48 · 64 rem). It
|
|
44
|
+
// is the page's own width, not the window's: an open agent
|
|
45
|
+
// dock takes a quarter of the window, and the page must not
|
|
46
|
+
// be squeezed by it. Narrower, the side goes under the main in
|
|
47
|
+
// one scroll, nothing hidden behind a button
|
|
48
|
+
// mainLabel the main area's name for a screen reader ("Content")
|
|
49
|
+
// sideLabel the side's name ("Details")
|
|
50
|
+
// padded default true: p-4 sm:p-6 on the main, p-4 on the side. false
|
|
51
|
+
// for a page that draws its own edges
|
|
52
|
+
// fill true: the main is a flex column, so a child with `flex-1`
|
|
53
|
+
// (a list, a table) grows into the height instead of stopping
|
|
54
|
+
// where its rows end. With the side beside it, or alone; with
|
|
55
|
+
// the side under it (narrow) the page is one scroll and there
|
|
56
|
+
// is no height to grow into
|
|
57
|
+
// className, mainClassName, sideClassName spacing and token backgrounds
|
|
58
|
+
// (bg-muted). Never a height or an overflow: the layout owns those
|
|
59
|
+
const px = (value, fallback, min, max) => {
|
|
60
|
+
const n = Number(value);
|
|
61
|
+
return `${Number.isFinite(n) && n > 0 ? Math.min(max, Math.max(min, Math.round(n))) : fallback}px`;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Tailwind reads class names whole from the source, so every size is written
|
|
65
|
+
// out. md is a page 42rem wide, lg 48rem, xl 64rem (the AppLayout's
|
|
66
|
+
// inspectorIn="frame" sizes).
|
|
67
|
+
const BESIDE = {
|
|
68
|
+
md: {
|
|
69
|
+
grid: "@2xl:grid-cols-[minmax(0,1fr)_var(--page-side)] @2xl:grid-rows-[minmax(0,1fr)] @2xl:overflow-hidden",
|
|
70
|
+
column: "@2xl:min-h-0 @2xl:overflow-y-auto @2xl:overscroll-contain",
|
|
71
|
+
side: "@2xl:border-l @2xl:border-t-0 @2xl:[scrollbar-width:thin]",
|
|
72
|
+
},
|
|
73
|
+
lg: {
|
|
74
|
+
grid: "@3xl:grid-cols-[minmax(0,1fr)_var(--page-side)] @3xl:grid-rows-[minmax(0,1fr)] @3xl:overflow-hidden",
|
|
75
|
+
column: "@3xl:min-h-0 @3xl:overflow-y-auto @3xl:overscroll-contain",
|
|
76
|
+
side: "@3xl:border-l @3xl:border-t-0 @3xl:[scrollbar-width:thin]",
|
|
77
|
+
},
|
|
78
|
+
xl: {
|
|
79
|
+
grid: "@5xl:grid-cols-[minmax(0,1fr)_var(--page-side)] @5xl:grid-rows-[minmax(0,1fr)] @5xl:overflow-hidden",
|
|
80
|
+
column: "@5xl:min-h-0 @5xl:overflow-y-auto @5xl:overscroll-contain",
|
|
81
|
+
side: "@5xl:border-l @5xl:border-t-0 @5xl:[scrollbar-width:thin]",
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const REGION = "outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring";
|
|
86
|
+
const LETS_GO = "print:block print:h-auto print:min-h-0 print:overflow-visible";
|
|
87
|
+
|
|
88
|
+
export function PageLayout({ children, side, sideWidth, sideAt = "lg", mainLabel = "Content", sideLabel = "Details", padded = true, fill = false, className, mainClassName, sideClassName }) {
|
|
89
|
+
const has = side != null && side !== false;
|
|
90
|
+
const at = BESIDE[sideAt] ?? BESIDE.lg;
|
|
91
|
+
return (
|
|
92
|
+
<div data-page-layout className={cx("@container flex min-h-0 min-w-0 flex-1 flex-col", LETS_GO, className)}>
|
|
93
|
+
<div
|
|
94
|
+
data-page-scroll
|
|
95
|
+
style={{ "--page-side": px(sideWidth, 304, 220, 560) }}
|
|
96
|
+
className={cx(
|
|
97
|
+
"grid min-h-0 min-w-0 flex-1 grid-cols-[minmax(0,1fr)] content-start",
|
|
98
|
+
has ? cx("overflow-y-auto overscroll-contain", at.grid) : "grid-rows-[minmax(0,1fr)] overflow-hidden",
|
|
99
|
+
LETS_GO,
|
|
100
|
+
)}
|
|
101
|
+
>
|
|
102
|
+
<div
|
|
103
|
+
data-page-main
|
|
104
|
+
role="region"
|
|
105
|
+
aria-label={mainLabel}
|
|
106
|
+
tabIndex={0}
|
|
107
|
+
className={cx("min-w-0", REGION, has ? at.column : "min-h-0 overflow-y-auto overscroll-contain", fill && "flex flex-col", padded && "p-4 sm:p-6", LETS_GO, mainClassName)}
|
|
108
|
+
>
|
|
109
|
+
{children}
|
|
110
|
+
</div>
|
|
111
|
+
{has ? (
|
|
112
|
+
<aside
|
|
113
|
+
data-page-side
|
|
114
|
+
aria-label={sideLabel}
|
|
115
|
+
tabIndex={0}
|
|
116
|
+
className={cx("min-w-0 border-t border-border print:hidden", REGION, at.column, at.side, padded && "p-4", sideClassName)}
|
|
117
|
+
>
|
|
118
|
+
{side}
|
|
119
|
+
</aside>
|
|
120
|
+
) : null}
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
}
|