makersclaw 0.8.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -3
- package/package.json +1 -1
- package/r/activity-log.json +52 -0
- package/r/agent-changes.json +51 -0
- package/r/agent-chat.json +53 -0
- package/r/agent-column.json +85 -0
- package/r/agent-dock.json +57 -0
- package/r/agent-kit.json +49 -0
- package/r/agent-marks.json +49 -0
- package/r/agent-pane.json +93 -0
- package/r/ai-action.json +56 -0
- package/r/annotate.json +3 -3
- package/r/approval-request.json +2 -2
- package/r/approval.json +54 -0
- package/r/ask-bar.json +50 -0
- package/r/audio-player.json +2 -2
- package/r/auth-form.json +2 -2
- package/r/automation-editor.json +66 -0
- package/r/automation-list.json +57 -0
- package/r/automation-model.json +118 -0
- package/r/automations-screen.json +71 -0
- package/r/board.json +2 -2
- package/r/calendar-view.json +3 -3
- package/r/cart.json +2 -2
- package/r/chart.json +2 -2
- package/r/chat-pane.json +84 -0
- package/r/chat.json +2 -2
- package/r/code-editor.json +2 -2
- package/r/comment-box.json +3 -3
- package/r/connect-card.json +51 -0
- package/r/contact-section.json +2 -2
- package/r/core.json +13 -7
- package/r/cost.json +51 -0
- package/r/csv-import.json +3 -3
- package/r/data-picker.json +60 -0
- package/r/data-table.json +2 -2
- package/r/diagram.json +2 -2
- package/r/doc-editor.json +2 -2
- package/r/doc-page.json +3 -3
- package/r/email-render.json +2 -2
- package/r/faq.json +2 -2
- package/r/file-upload.json +2 -2
- package/r/first-run.json +63 -0
- package/r/fix-note.json +57 -0
- package/r/flow-canvas.json +2 -2
- package/r/flow-editor.json +79 -0
- package/r/footer.json +2 -2
- package/r/gantt.json +3 -3
- package/r/job-panel.json +2 -2
- package/r/json-editor.json +3 -3
- package/r/library-shelf.json +56 -0
- package/r/map.json +2 -2
- package/r/margin-notes.json +67 -0
- package/r/menubar.json +2 -2
- package/r/mind-map.json +3 -3
- package/r/navigation-menu.json +2 -2
- package/r/needs-you.json +55 -0
- package/r/order-detail.json +2 -2
- package/r/org-chart.json +3 -3
- package/r/pace-bar.json +69 -0
- package/r/page-builder.json +2 -2
- package/r/payment-methods.json +2 -2
- package/r/pdf-document.json +2 -2
- package/r/pdf-viewer.json +2 -2
- package/r/product-detail.json +2 -2
- package/r/product-grid.json +3 -3
- package/r/record-form.json +2 -2
- package/r/record-grid.json +52 -0
- package/r/record-notes.json +4 -4
- package/r/registry.json +4086 -1512
- package/r/run-history.json +52 -0
- package/r/run-view.json +80 -0
- package/r/schedule-card.json +54 -0
- package/r/schedule-engine.json +79 -0
- package/r/schedule-gantt.json +83 -0
- package/r/schedule-ruler.json +64 -0
- package/r/search-dialog.json +2 -2
- package/r/settings-list.json +2 -2
- package/r/setup-checklist.json +2 -2
- package/r/share-dialog.json +2 -2
- package/r/sidebar.json +2 -2
- package/r/signature-pad.json +2 -2
- package/r/sketch-canvas.json +2 -2
- package/r/source-receipt.json +52 -0
- package/r/spreadsheet.json +3 -3
- package/r/stat-cards.json +3 -3
- package/r/step-picker.json +53 -0
- package/r/step-settings.json +72 -0
- package/r/suggestion.json +58 -0
- package/r/sum-bar.json +64 -0
- package/r/task-list.json +3 -3
- package/r/task-model.json +2 -2
- package/r/testimonials.json +2 -2
- package/r/tier-map.json +67 -0
- package/r/time-grid.json +73 -0
- package/r/time-strip.json +64 -0
- package/r/top-bar.json +66 -0
- package/r/transaction-list.json +2 -2
- package/r/trigger-picker.json +61 -0
- package/r/whiteboard.json +3 -3
- package/r/workflow-view.json +55 -0
- package/registry/blocks/activity-log/README.md +112 -0
- package/registry/blocks/activity-log/activity-log.jsx +351 -0
- package/registry/blocks/activity-log/block.json +33 -0
- package/registry/blocks/activity-log/demo.jsx +48 -0
- package/registry/blocks/activity-log/index.js +4 -0
- package/registry/blocks/agent-changes/README.md +94 -0
- package/registry/blocks/agent-changes/agent-changes.jsx +118 -0
- package/registry/blocks/agent-changes/block.json +28 -0
- package/registry/blocks/agent-changes/demo.jsx +39 -0
- package/registry/blocks/agent-changes/index.js +3 -0
- package/registry/blocks/agent-chat/README.md +145 -0
- package/registry/blocks/agent-chat/agent-chat.jsx +415 -0
- package/registry/blocks/agent-chat/block.json +30 -0
- package/registry/blocks/agent-chat/demo.jsx +165 -0
- package/registry/blocks/agent-chat/index.js +3 -0
- package/registry/blocks/agent-column/README.md +197 -0
- package/registry/blocks/agent-column/agent-column.jsx +492 -0
- package/registry/blocks/agent-column/block.json +67 -0
- package/registry/blocks/agent-column/column-picker.jsx +163 -0
- package/registry/blocks/agent-column/demo.jsx +50 -0
- package/registry/blocks/agent-column/index.js +7 -0
- package/registry/blocks/agent-column/model.js +164 -0
- package/registry/blocks/agent-column/receipt.jsx +483 -0
- package/registry/blocks/agent-column/spreadsheet-type.jsx +24 -0
- package/registry/blocks/agent-dock/README.md +210 -0
- package/registry/blocks/agent-dock/agent-dock.jsx +356 -0
- package/registry/blocks/agent-dock/block.json +47 -0
- package/registry/blocks/agent-dock/demo.jsx +153 -0
- package/registry/blocks/agent-dock/dock-model.js +118 -0
- package/registry/blocks/agent-dock/index.js +5 -0
- package/registry/blocks/agent-kit/README.md +73 -0
- package/registry/blocks/agent-kit/agent-kit.jsx +86 -0
- package/registry/blocks/agent-kit/block.json +10 -0
- package/registry/blocks/agent-kit/demo.jsx +35 -0
- package/registry/blocks/agent-kit/index.js +3 -0
- package/registry/blocks/agent-marks/README.md +80 -0
- package/registry/blocks/agent-marks/agent-marks.jsx +133 -0
- package/registry/blocks/agent-marks/block.json +27 -0
- package/registry/blocks/agent-marks/demo.jsx +49 -0
- package/registry/blocks/agent-marks/index.js +3 -0
- package/registry/blocks/agent-pane/README.md +234 -0
- package/registry/blocks/agent-pane/agent-pane.jsx +246 -0
- package/registry/blocks/agent-pane/block.json +41 -0
- package/registry/blocks/agent-pane/composer.jsx +6 -0
- package/registry/blocks/agent-pane/demo.jsx +233 -0
- package/registry/blocks/agent-pane/dock-context.js +10 -0
- package/registry/blocks/agent-pane/index.js +4 -0
- package/registry/blocks/agent-pane/prose.jsx +84 -0
- package/registry/blocks/agent-pane/questions.jsx +134 -0
- package/registry/blocks/agent-pane/transcript.jsx +268 -0
- package/registry/blocks/agent-pane/work.jsx +368 -0
- package/registry/blocks/ai-action/README.md +94 -0
- package/registry/blocks/ai-action/ai-action.jsx +188 -0
- package/registry/blocks/ai-action/block.json +34 -0
- package/registry/blocks/ai-action/demo.jsx +93 -0
- package/registry/blocks/ai-action/index.js +3 -0
- package/registry/blocks/approval/README.md +145 -0
- package/registry/blocks/approval/approval.jsx +163 -0
- package/registry/blocks/approval/block.json +31 -0
- package/registry/blocks/approval/demo.jsx +121 -0
- package/registry/blocks/approval/index.js +3 -0
- package/registry/blocks/ask-bar/README.md +105 -0
- package/registry/blocks/ask-bar/ask-bar.jsx +166 -0
- package/registry/blocks/ask-bar/block.json +26 -0
- package/registry/blocks/ask-bar/demo.jsx +78 -0
- package/registry/blocks/ask-bar/index.js +3 -0
- package/registry/blocks/automation-editor/README.md +291 -0
- package/registry/blocks/automation-editor/automation-editor.jsx +656 -0
- package/registry/blocks/automation-editor/block.json +39 -0
- package/registry/blocks/automation-editor/demo.jsx +284 -0
- package/registry/blocks/automation-editor/index.js +4 -0
- package/registry/blocks/automation-editor/run-inputs.jsx +82 -0
- package/registry/blocks/automation-list/README.md +129 -0
- package/registry/blocks/automation-list/automation-list.jsx +476 -0
- package/registry/blocks/automation-list/block.json +35 -0
- package/registry/blocks/automation-list/demo.jsx +67 -0
- package/registry/blocks/automation-list/index.js +3 -0
- package/registry/blocks/automation-model/README.md +109 -0
- package/registry/blocks/automation-model/block.json +135 -0
- package/registry/blocks/automation-model/catalog.js +256 -0
- package/registry/blocks/automation-model/check.js +161 -0
- package/registry/blocks/automation-model/demo.jsx +42 -0
- package/registry/blocks/automation-model/edit.js +262 -0
- package/registry/blocks/automation-model/fixes.js +287 -0
- package/registry/blocks/automation-model/glyph.jsx +13 -0
- package/registry/blocks/automation-model/graph.js +410 -0
- package/registry/blocks/automation-model/index.js +33 -0
- package/registry/blocks/automation-model/overlay.js +175 -0
- package/registry/blocks/automation-model/parts.jsx +94 -0
- package/registry/blocks/automation-model/schedule.js +53 -0
- package/registry/blocks/automation-model/templates.js +128 -0
- package/registry/blocks/automation-model/values.js +29 -0
- package/registry/blocks/automation-model/words.js +439 -0
- package/registry/blocks/automations-screen/README.md +207 -0
- package/registry/blocks/automations-screen/automations-screen.jsx +452 -0
- package/registry/blocks/automations-screen/block.json +47 -0
- package/registry/blocks/automations-screen/demo.jsx +364 -0
- package/registry/blocks/automations-screen/index.js +4 -0
- package/registry/blocks/automations-screen/screen-model.js +129 -0
- package/registry/blocks/chat-pane/README.md +143 -0
- package/registry/blocks/chat-pane/block.json +48 -0
- package/registry/blocks/chat-pane/chat-attach.js +166 -0
- package/registry/blocks/chat-pane/chat-composer.jsx +195 -0
- package/registry/blocks/chat-pane/chat-context.js +17 -0
- package/registry/blocks/chat-pane/chat-model.js +43 -0
- package/registry/blocks/chat-pane/chat-pane.jsx +398 -0
- package/registry/blocks/chat-pane/chats-list.jsx +52 -0
- package/registry/blocks/chat-pane/demo.jsx +103 -0
- package/registry/blocks/chat-pane/index.js +9 -0
- package/registry/blocks/connect-card/README.md +99 -0
- package/registry/blocks/connect-card/block.json +10 -0
- package/registry/blocks/connect-card/connect-card.jsx +103 -0
- package/registry/blocks/connect-card/demo.jsx +49 -0
- package/registry/blocks/connect-card/index.js +3 -0
- package/registry/blocks/cost/README.md +115 -0
- package/registry/blocks/cost/block.json +10 -0
- package/registry/blocks/cost/cost.jsx +176 -0
- package/registry/blocks/cost/demo.jsx +66 -0
- package/registry/blocks/cost/index.js +3 -0
- package/registry/blocks/data-picker/README.md +81 -0
- package/registry/blocks/data-picker/block.json +38 -0
- package/registry/blocks/data-picker/data-picker.jsx +210 -0
- package/registry/blocks/data-picker/demo.jsx +34 -0
- package/registry/blocks/data-picker/fields.js +89 -0
- package/registry/blocks/data-picker/index.js +4 -0
- package/registry/blocks/data-table/demo.jsx +1 -1
- package/registry/blocks/first-run/README.md +118 -0
- package/registry/blocks/first-run/block.json +11 -0
- package/registry/blocks/first-run/demo.jsx +88 -0
- package/registry/blocks/first-run/first-run-model.js +51 -0
- package/registry/blocks/first-run/first-run.jsx +156 -0
- package/registry/blocks/first-run/index.js +5 -0
- package/registry/blocks/first-run/probe-rules.js +26 -0
- package/registry/blocks/fix-note/README.md +79 -0
- package/registry/blocks/fix-note/block.json +35 -0
- package/registry/blocks/fix-note/demo.jsx +30 -0
- package/registry/blocks/fix-note/fix-note.jsx +235 -0
- package/registry/blocks/fix-note/index.js +3 -0
- package/registry/blocks/flow-editor/README.md +108 -0
- package/registry/blocks/flow-editor/block.json +44 -0
- package/registry/blocks/flow-editor/demo.jsx +115 -0
- package/registry/blocks/flow-editor/flow-canvas.jsx +568 -0
- package/registry/blocks/flow-editor/flow-editor.jsx +154 -0
- package/registry/blocks/flow-editor/index.js +6 -0
- package/registry/blocks/flow-editor/minimap.jsx +78 -0
- package/registry/blocks/flow-editor/outline.jsx +134 -0
- package/registry/blocks/library-shelf/README.md +117 -0
- package/registry/blocks/library-shelf/block.json +28 -0
- package/registry/blocks/library-shelf/demo.jsx +68 -0
- package/registry/blocks/library-shelf/index.js +3 -0
- package/registry/blocks/library-shelf/library-shelf.jsx +211 -0
- package/registry/blocks/library-shelf/probe-rules.js +15 -0
- package/registry/blocks/margin-notes/README.md +222 -0
- package/registry/blocks/margin-notes/block.json +10 -0
- package/registry/blocks/margin-notes/demo.jsx +144 -0
- package/registry/blocks/margin-notes/index.js +20 -0
- package/registry/blocks/margin-notes/margin-model.js +224 -0
- package/registry/blocks/margin-notes/margin-notes.jsx +465 -0
- package/registry/blocks/needs-you/README.md +177 -0
- package/registry/blocks/needs-you/block.json +34 -0
- package/registry/blocks/needs-you/demo.jsx +143 -0
- package/registry/blocks/needs-you/index.js +3 -0
- package/registry/blocks/needs-you/needs-you.jsx +441 -0
- package/registry/blocks/pace-bar/README.md +230 -0
- package/registry/blocks/pace-bar/block.json +10 -0
- package/registry/blocks/pace-bar/demo.jsx +255 -0
- package/registry/blocks/pace-bar/index.js +22 -0
- package/registry/blocks/pace-bar/pace-bar.jsx +461 -0
- package/registry/blocks/pace-bar/pace-model.js +288 -0
- package/registry/blocks/record-grid/README.md +100 -0
- package/registry/blocks/record-grid/block.json +28 -0
- package/registry/blocks/record-grid/demo.jsx +43 -0
- package/registry/blocks/record-grid/index.js +3 -0
- package/registry/blocks/record-grid/record-grid.jsx +251 -0
- package/registry/blocks/run-history/README.md +138 -0
- package/registry/blocks/run-history/block.json +10 -0
- package/registry/blocks/run-history/demo.jsx +74 -0
- package/registry/blocks/run-history/index.js +3 -0
- package/registry/blocks/run-history/run-history.jsx +218 -0
- package/registry/blocks/run-view/README.md +174 -0
- package/registry/blocks/run-view/block.json +50 -0
- package/registry/blocks/run-view/demo.jsx +286 -0
- package/registry/blocks/run-view/index.js +5 -0
- package/registry/blocks/run-view/run-controls.jsx +266 -0
- package/registry/blocks/run-view/run-view.jsx +236 -0
- package/registry/blocks/run-view/runs-list.jsx +99 -0
- package/registry/blocks/schedule-card/README.md +121 -0
- package/registry/blocks/schedule-card/block.json +10 -0
- package/registry/blocks/schedule-card/demo.jsx +82 -0
- package/registry/blocks/schedule-card/index.js +3 -0
- package/registry/blocks/schedule-card/schedule-card.jsx +136 -0
- package/registry/blocks/schedule-engine/README.md +137 -0
- package/registry/blocks/schedule-engine/block.json +10 -0
- package/registry/blocks/schedule-engine/demo.jsx +82 -0
- package/registry/blocks/schedule-engine/engine.js +769 -0
- package/registry/blocks/schedule-engine/index.js +9 -0
- package/registry/blocks/schedule-engine/ops.js +71 -0
- package/registry/blocks/schedule-engine/place.js +294 -0
- package/registry/blocks/schedule-engine/tree.js +79 -0
- package/registry/blocks/schedule-engine/waits.js +80 -0
- package/registry/blocks/schedule-gantt/README.md +159 -0
- package/registry/blocks/schedule-gantt/block.json +10 -0
- package/registry/blocks/schedule-gantt/demo.jsx +223 -0
- package/registry/blocks/schedule-gantt/gantt-model.js +219 -0
- package/registry/blocks/schedule-gantt/geometry.js +176 -0
- package/registry/blocks/schedule-gantt/index.js +26 -0
- package/registry/blocks/schedule-gantt/schedule-gantt.jsx +882 -0
- package/registry/blocks/schedule-gantt/sheet.jsx +372 -0
- package/registry/blocks/schedule-gantt/track.jsx +190 -0
- package/registry/blocks/schedule-ruler/README.md +148 -0
- package/registry/blocks/schedule-ruler/block.json +10 -0
- package/registry/blocks/schedule-ruler/demo.jsx +181 -0
- package/registry/blocks/schedule-ruler/index.js +21 -0
- package/registry/blocks/schedule-ruler/ruler-model.js +239 -0
- package/registry/blocks/schedule-ruler/schedule-ruler.jsx +548 -0
- package/registry/blocks/source-receipt/README.md +97 -0
- package/registry/blocks/source-receipt/block.json +30 -0
- package/registry/blocks/source-receipt/demo.jsx +71 -0
- package/registry/blocks/source-receipt/index.js +3 -0
- package/registry/blocks/source-receipt/source-receipt.jsx +107 -0
- package/registry/blocks/spreadsheet/spreadsheet.jsx +15 -1
- package/registry/blocks/step-picker/README.md +78 -0
- package/registry/blocks/step-picker/block.json +31 -0
- package/registry/blocks/step-picker/demo.jsx +23 -0
- package/registry/blocks/step-picker/index.js +3 -0
- package/registry/blocks/step-picker/step-picker.jsx +144 -0
- package/registry/blocks/step-settings/README.md +100 -0
- package/registry/blocks/step-settings/block.json +44 -0
- package/registry/blocks/step-settings/demo.jsx +93 -0
- package/registry/blocks/step-settings/fields.jsx +58 -0
- package/registry/blocks/step-settings/flow-settings.jsx +115 -0
- package/registry/blocks/step-settings/index.js +5 -0
- package/registry/blocks/step-settings/step-settings.jsx +290 -0
- package/registry/blocks/suggestion/README.md +171 -0
- package/registry/blocks/suggestion/block.json +40 -0
- package/registry/blocks/suggestion/demo.jsx +91 -0
- package/registry/blocks/suggestion/index.js +3 -0
- package/registry/blocks/suggestion/suggestion.jsx +436 -0
- package/registry/blocks/sum-bar/README.md +122 -0
- package/registry/blocks/sum-bar/block.json +10 -0
- package/registry/blocks/sum-bar/demo.jsx +110 -0
- package/registry/blocks/sum-bar/index.js +5 -0
- package/registry/blocks/sum-bar/sum-bar.jsx +209 -0
- package/registry/blocks/sum-bar/sum-model.js +126 -0
- package/registry/blocks/tier-map/README.md +217 -0
- package/registry/blocks/tier-map/block.json +10 -0
- package/registry/blocks/tier-map/demo.jsx +222 -0
- package/registry/blocks/tier-map/index.js +24 -0
- package/registry/blocks/tier-map/tier-map.jsx +574 -0
- package/registry/blocks/tier-map/tier-model.js +318 -0
- package/registry/blocks/time-grid/README.md +305 -0
- package/registry/blocks/time-grid/block.json +10 -0
- package/registry/blocks/time-grid/demo.jsx +279 -0
- package/registry/blocks/time-grid/grid-model.js +322 -0
- package/registry/blocks/time-grid/index.js +25 -0
- package/registry/blocks/time-grid/time-grid.jsx +1325 -0
- package/registry/blocks/time-strip/README.md +141 -0
- package/registry/blocks/time-strip/block.json +10 -0
- package/registry/blocks/time-strip/demo.jsx +129 -0
- package/registry/blocks/time-strip/index.js +19 -0
- package/registry/blocks/time-strip/strip-model.js +178 -0
- package/registry/blocks/time-strip/time-strip.jsx +260 -0
- package/registry/blocks/top-bar/README.md +149 -0
- package/registry/blocks/top-bar/block.json +35 -0
- package/registry/blocks/top-bar/demo.jsx +78 -0
- package/registry/blocks/top-bar/index.js +5 -0
- package/registry/blocks/top-bar/probe-rules.js +74 -0
- package/registry/blocks/top-bar/top-bar-model.js +152 -0
- package/registry/blocks/top-bar/top-bar.jsx +244 -0
- package/registry/blocks/trigger-picker/README.md +98 -0
- package/registry/blocks/trigger-picker/block.json +38 -0
- package/registry/blocks/trigger-picker/demo.jsx +59 -0
- package/registry/blocks/trigger-picker/index.js +3 -0
- package/registry/blocks/trigger-picker/trigger-picker.jsx +374 -0
- package/registry/blocks/workflow-view/README.md +123 -0
- package/registry/blocks/workflow-view/block.json +10 -0
- package/registry/blocks/workflow-view/demo.jsx +90 -0
- package/registry/blocks/workflow-view/index.js +3 -0
- package/registry/blocks/workflow-view/workflow-view.jsx +203 -0
- package/src/install.mjs +77 -16
- package/src/registry.mjs +3 -0
- package/templates/next/browser/lib/data.js +6 -1
- package/templates/next/files/README.md +627 -24
- package/templates/next/files/check/automation-format.ts +2103 -0
- package/templates/next/files/check/automation-package.ts +142 -0
- package/templates/next/files/check/run.mjs +968 -9
- package/templates/next/files/components/ui/field.jsx +17 -4
- package/templates/next/files/components/ui/icon.jsx +13 -0
- package/templates/next/files/components/ui/index.js +2 -1
- package/templates/next/files/components/ui/layout.jsx +77 -2
- package/templates/next/files/components/ui/notice.jsx +70 -0
- package/templates/next/files/components/ui/page.jsx +124 -0
- package/templates/next/files/components/ui/stat.jsx +22 -7
- package/templates/next/files/components/ui/toast.jsx +6 -3
- package/templates/next/files/lib/automations.js +660 -0
- package/templates/next/files/lib/claw.js +241 -5
- package/templates/next/files/lib/door.js +92 -0
- package/templates/next/files/lib/hooks.js +1677 -8
- package/templates/next/files/lib/platform.js +529 -21
- package/templates/next/template.json +13 -3
|
@@ -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";
|
|
@@ -49,6 +49,7 @@ export { Steps } from "./steps";
|
|
|
49
49
|
export { Separator, Kbd, KbdGroup, AspectRatio } from "./separator";
|
|
50
50
|
export { ScrollArea } from "./scroll-area";
|
|
51
51
|
export { AppLayout, DocumentLayout } from "./layout";
|
|
52
|
+
export { PageLayout } from "./page";
|
|
52
53
|
export { Slider } from "./slider";
|
|
53
54
|
export { InputGroup, PasswordInput } from "./input-group";
|
|
54
55
|
export { Image } from "./image";
|
|
@@ -51,8 +51,22 @@ import { Sheet } from "./sheet";
|
|
|
51
51
|
// inspectorWidth in px (360; 240 to 560). Builds have needed 300 and 440
|
|
52
52
|
// inspectorAt md · lg (the default) · xl: the width from which the
|
|
53
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
|
|
54
60
|
// footer under the content, always in view (the save state,
|
|
55
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
|
|
56
70
|
// height viewport (the default): the frame is the window's
|
|
57
71
|
// height · parent: it fills a bounded parent. Nested,
|
|
58
72
|
// it is parent whatever this says
|
|
@@ -108,7 +122,60 @@ const BESIDE = {
|
|
|
108
122
|
},
|
|
109
123
|
};
|
|
110
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
|
+
|
|
111
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)]";
|
|
112
179
|
const LETS_GO = "print:block print:h-auto print:min-h-0 print:overflow-visible";
|
|
113
180
|
|
|
114
181
|
export function AppLayout({
|
|
@@ -125,14 +192,18 @@ export function AppLayout({
|
|
|
125
192
|
inspectorLabel = "Details",
|
|
126
193
|
inspectorWidth,
|
|
127
194
|
inspectorAt = "lg",
|
|
195
|
+
inspectorIn = "window",
|
|
128
196
|
footer,
|
|
197
|
+
dock,
|
|
129
198
|
height = "viewport",
|
|
130
199
|
className,
|
|
131
200
|
}) {
|
|
132
201
|
const nested = useContext(Nested);
|
|
133
202
|
const bounded = nested || height === "parent";
|
|
134
203
|
const fill = contentMode === "fill";
|
|
135
|
-
const
|
|
204
|
+
const byFrame = inspectorIn === "frame";
|
|
205
|
+
const table = byFrame ? BESIDE_FRAME : BESIDE;
|
|
206
|
+
const at = table[inspectorAt] ?? table.lg;
|
|
136
207
|
const hasInspector = inspector != null && inspector !== false;
|
|
137
208
|
const hasNavigation = navigation != null && navigation !== false;
|
|
138
209
|
const shared = hasInspector && !fill; // below `at`: content and inspector in one scroll region
|
|
@@ -197,6 +268,7 @@ export function AppLayout({
|
|
|
197
268
|
</div>
|
|
198
269
|
) : null}
|
|
199
270
|
|
|
271
|
+
<FrameBox on={byFrame && hasInspector}>
|
|
200
272
|
<div data-layout-main className={cx("flex min-h-0 min-w-0 flex-1 flex-col", hasInspector && at.main, LETS_GO)}>
|
|
201
273
|
<div
|
|
202
274
|
ref={flowRef}
|
|
@@ -238,7 +310,8 @@ export function AppLayout({
|
|
|
238
310
|
aria-label={inspectorLabel}
|
|
239
311
|
tabIndex={0}
|
|
240
312
|
className={cx(
|
|
241
|
-
"
|
|
313
|
+
"shrink-0 border-t border-border print:hidden",
|
|
314
|
+
INSPECTOR_FIT,
|
|
242
315
|
REGION,
|
|
243
316
|
fill && "max-h-[45%] overflow-y-auto overscroll-contain",
|
|
244
317
|
at.inspector,
|
|
@@ -254,6 +327,8 @@ export function AppLayout({
|
|
|
254
327
|
</div>
|
|
255
328
|
) : null}
|
|
256
329
|
</div>
|
|
330
|
+
</FrameBox>
|
|
331
|
+
{dock != null && dock !== false ? dock : null}
|
|
257
332
|
</div>
|
|
258
333
|
</div>
|
|
259
334
|
</Nested.Provider>
|
|
@@ -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
|
+
}
|
|
@@ -355,6 +355,17 @@ const METER_SIZES = { thin: { track: "h-1", mark: "h-3" }, md: { track: "h-2", m
|
|
|
355
355
|
* bar's
|
|
356
356
|
* center a ring's middle instead of the percent: a string or a node
|
|
357
357
|
* ("4.2 GB", an Icon); null leaves it empty
|
|
358
|
+
* compact the bar alone (the ring alone, for shape="ring"): no label,
|
|
359
|
+
* no numbers, no caption, no target line, so it fits where
|
|
360
|
+
* words spill: a top bar, a table cell, a card corner. The
|
|
361
|
+
* label and the numbers are not lost: a screen reader hears
|
|
362
|
+
* them as always, and the bar's tooltip (`title`) shows
|
|
363
|
+
* "label: used of limit". Give it its width in className
|
|
364
|
+
* (w-14, w-24); size thin reads best in a top bar. Without
|
|
365
|
+
* compact a Meter draws its label as visible text, which
|
|
366
|
+
* spills in a box under about 8rem (the Newsletter's "15,000
|
|
367
|
+
* cr left" ran into Find, UI review X1; check rule 20,
|
|
368
|
+
* meter, refuses it)
|
|
358
369
|
*/
|
|
359
370
|
export function Meter({
|
|
360
371
|
value,
|
|
@@ -373,6 +384,7 @@ export function Meter({
|
|
|
373
384
|
size = "md",
|
|
374
385
|
shape = "bar",
|
|
375
386
|
center,
|
|
387
|
+
compact = false,
|
|
376
388
|
className,
|
|
377
389
|
...rest
|
|
378
390
|
}) {
|
|
@@ -416,21 +428,24 @@ export function Meter({
|
|
|
416
428
|
"aria-valuetext": said,
|
|
417
429
|
};
|
|
418
430
|
|
|
431
|
+
// The tooltip of a compact meter: the name, then the numbers a reader hears.
|
|
432
|
+
const tip = compact ? (label ? `${label}: ${said}` : said) : undefined;
|
|
433
|
+
|
|
419
434
|
if (shape === "ring") {
|
|
420
435
|
return (
|
|
421
436
|
<MeterRing
|
|
422
|
-
{...{ pct, over, overBad, barTone, valueTone, valueClass, label, shown, caption, hasTarget, targetMark, targetPct, center, size, className, meterProps, rest }}
|
|
437
|
+
{...{ pct, over, overBad, barTone, valueTone, valueClass, label, shown, caption, hasTarget, targetMark, targetPct, center, size, className, meterProps, rest, compact, tip }}
|
|
423
438
|
targetText={hasTarget ? `Target ${format(target)}${u}` : null}
|
|
424
439
|
/>
|
|
425
440
|
);
|
|
426
441
|
}
|
|
427
442
|
|
|
428
443
|
const s = METER_SIZES[size] ?? METER_SIZES.md;
|
|
429
|
-
const footer = caption || (hasTarget && size === "md");
|
|
444
|
+
const footer = !compact && (caption || (hasTarget && size === "md"));
|
|
430
445
|
|
|
431
446
|
return (
|
|
432
|
-
<div className={cx("grid gap-1.5", className)}>
|
|
433
|
-
{label || shown ? (
|
|
447
|
+
<div className={cx("grid min-w-0 gap-1.5", className)} title={tip}>
|
|
448
|
+
{!compact && (label || shown) ? (
|
|
434
449
|
<div aria-hidden="true" className="flex items-baseline justify-between gap-3 text-sm">
|
|
435
450
|
<span className="min-w-0 truncate font-medium text-foreground">{label}</span>
|
|
436
451
|
{shown ? (
|
|
@@ -499,7 +514,7 @@ const RING_SIZES = {
|
|
|
499
514
|
RING_SIZES.thin = RING_SIZES.sm;
|
|
500
515
|
|
|
501
516
|
// Meter shape="ring": the same numbers, drawn as an arc from twelve o'clock.
|
|
502
|
-
function MeterRing({ pct, overBad, barTone, valueTone, valueClass, label, shown, caption, hasTarget, targetMark, targetPct, targetText, center, size, className, meterProps, rest }) {
|
|
517
|
+
function MeterRing({ pct, overBad, barTone, valueTone, valueClass, label, shown, caption, hasTarget, targetMark, targetPct, targetText, center, size, className, meterProps, rest, compact, tip }) {
|
|
503
518
|
const g = RING_SIZES[size] ?? RING_SIZES.md;
|
|
504
519
|
const c = g.px / 2;
|
|
505
520
|
const r = c - g.w / 2;
|
|
@@ -521,10 +536,10 @@ function MeterRing({ pct, overBad, barTone, valueTone, valueClass, label, shown,
|
|
|
521
536
|
const tick = (d) => [c + Math.cos(a) * d, c + Math.sin(a) * d];
|
|
522
537
|
const [x1, y1] = tick(r - g.w / 2 - 1.5);
|
|
523
538
|
const [x2, y2] = tick(r + g.w / 2 + 1.5);
|
|
524
|
-
const side = label || shown || caption || (hasTarget && size !== "sm");
|
|
539
|
+
const side = !compact && (label || shown || caption || (hasTarget && size !== "sm"));
|
|
525
540
|
|
|
526
541
|
return (
|
|
527
|
-
<div className={cx("flex min-w-0 items-center", g.gap, className)}>
|
|
542
|
+
<div className={cx("flex min-w-0 items-center", g.gap, className)} title={tip}>
|
|
528
543
|
<div {...meterProps} className="relative shrink-0" style={{ width: g.px, height: g.px }} {...rest}>
|
|
529
544
|
<svg aria-hidden="true" width={g.px} height={g.px} viewBox={`0 0 ${g.px} ${g.px}`} className="block overflow-visible">
|
|
530
545
|
{/* raw: an SVG gauge — the kit has no smaller part to draw an arc. */}
|
|
@@ -37,7 +37,8 @@ import { Spinner } from "./feedback";
|
|
|
37
37
|
//
|
|
38
38
|
// Newest in front; the rest stack behind and fan out on hover or focus,
|
|
39
39
|
// and every timer pauses while they do (and while the tab is hidden).
|
|
40
|
-
// Close with the ×, Escape, or a sideways swipe
|
|
40
|
+
// Close with the ×, Escape, or a sideways swipe (touch and pen; a mouse
|
|
41
|
+
// selects the text instead, so an error can be copied). Screen readers hear each
|
|
41
42
|
// toast once through a live region (assertive for .error). A toast is
|
|
42
43
|
// never the only record of something that matters: an error the user
|
|
43
44
|
// must act on is a Notice on the surface.
|
|
@@ -343,7 +344,8 @@ function ToastItem({ t, index, top, beyond, offset, expanded, clip, paused, onHe
|
|
|
343
344
|
}
|
|
344
345
|
|
|
345
346
|
function onPointerDown(e) {
|
|
346
|
-
|
|
347
|
+
// A mouse selects the words (to copy an error); only touch and pen swipe.
|
|
348
|
+
if (e.pointerType === "mouse" || e.button !== 0 || e.target.closest("button, a, input, textarea, select")) return;
|
|
347
349
|
drag.current = { x: e.clientX, at: e.timeStamp };
|
|
348
350
|
e.currentTarget.setPointerCapture(e.pointerId);
|
|
349
351
|
}
|
|
@@ -392,7 +394,8 @@ function ToastItem({ t, index, top, beyond, offset, expanded, clip, paused, onHe
|
|
|
392
394
|
if (e.key === "Escape") dismiss(t.id);
|
|
393
395
|
}}
|
|
394
396
|
className={cx(
|
|
395
|
-
"pointer-events-auto absolute inset-x-0 select-
|
|
397
|
+
"pointer-events-auto absolute inset-x-0 select-text",
|
|
398
|
+
dragging && "select-none",
|
|
396
399
|
"transition-[opacity,translate,transform,height] duration-300 ease-out motion-reduce:transition-none",
|
|
397
400
|
top ? "top-0 starting:-translate-y-full" : "bottom-0 starting:translate-y-full",
|
|
398
401
|
"starting:opacity-0",
|