cursedbelt 5.40.0 → 5.45.0
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/dist/react/Toast.d.ts +12 -2
- package/dist/react/Toast.d.ts.map +1 -1
- package/dist/react/Toast.js +32 -3
- package/dist/react/Toast.js.map +1 -1
- package/dist/react/analytics/MetricDrilldown.js +1 -1
- package/dist/react/charts/AreaChart.d.ts.map +1 -1
- package/dist/react/charts/AreaChart.js +5 -5
- package/dist/react/charts/AreaChart.js.map +1 -1
- package/dist/react/charts/BarChart.d.ts.map +1 -1
- package/dist/react/charts/BarChart.js +3 -3
- package/dist/react/charts/BarChart.js.map +1 -1
- package/dist/react/charts/ChartCard.d.ts +53 -0
- package/dist/react/charts/ChartCard.d.ts.map +1 -0
- package/dist/react/charts/ChartCard.js +46 -0
- package/dist/react/charts/ChartCard.js.map +1 -0
- package/dist/react/charts/ChartFrame.d.ts +12 -5
- package/dist/react/charts/ChartFrame.d.ts.map +1 -1
- package/dist/react/charts/ChartFrame.js +12 -5
- package/dist/react/charts/ChartFrame.js.map +1 -1
- package/dist/react/charts/ChartLegend.js +1 -1
- package/dist/react/charts/ChartLegend.js.map +1 -1
- package/dist/react/charts/HeatmapChart.js +3 -3
- package/dist/react/charts/HeatmapChart.js.map +1 -1
- package/dist/react/charts/IntervalTimeline.js +1 -1
- package/dist/react/charts/IntervalTimeline.js.map +1 -1
- package/dist/react/charts/LineChart.d.ts.map +1 -1
- package/dist/react/charts/LineChart.js +3 -3
- package/dist/react/charts/LineChart.js.map +1 -1
- package/dist/react/charts/TimeSeriesChart.d.ts.map +1 -1
- package/dist/react/charts/TimeSeriesChart.js +6 -6
- package/dist/react/charts/TimeSeriesChart.js.map +1 -1
- package/dist/react/charts/chartBox.d.ts +46 -0
- package/dist/react/charts/chartBox.d.ts.map +1 -0
- package/dist/react/charts/chartBox.js +45 -0
- package/dist/react/charts/chartBox.js.map +1 -0
- package/dist/react/charts/index.d.ts +2 -0
- package/dist/react/charts/index.d.ts.map +1 -1
- package/dist/react/charts/index.js +2 -0
- package/dist/react/charts/index.js.map +1 -1
- package/dist/react/charts/shared.d.ts +13 -2
- package/dist/react/charts/shared.d.ts.map +1 -1
- package/dist/react/charts/shared.js +14 -3
- package/dist/react/charts/shared.js.map +1 -1
- package/dist/react/chat/Bubble.js +4 -4
- package/dist/react/chat/Bubble.js.map +1 -1
- package/dist/react/chat/MessageScroller.js +1 -1
- package/dist/react/chat/MessageScroller.js.map +1 -1
- package/dist/react/components/ActionBar.d.ts.map +1 -1
- package/dist/react/components/ActionBar.js +63 -1
- package/dist/react/components/ActionBar.js.map +1 -1
- package/dist/react/components/AddItemsMenu.js +1 -1
- package/dist/react/components/AddItemsMenu.js.map +1 -1
- package/dist/react/components/Alert.d.ts +16 -2
- package/dist/react/components/Alert.d.ts.map +1 -1
- package/dist/react/components/Alert.js +35 -2
- package/dist/react/components/Alert.js.map +1 -1
- package/dist/react/components/AppShell.d.ts +55 -33
- package/dist/react/components/AppShell.d.ts.map +1 -1
- package/dist/react/components/AppShell.js +121 -31
- package/dist/react/components/AppShell.js.map +1 -1
- package/dist/react/components/AsyncPanel.d.ts +21 -3
- package/dist/react/components/AsyncPanel.d.ts.map +1 -1
- package/dist/react/components/AsyncPanel.js +49 -5
- package/dist/react/components/AsyncPanel.js.map +1 -1
- package/dist/react/components/BannerLane.d.ts +32 -0
- package/dist/react/components/BannerLane.d.ts.map +1 -0
- package/dist/react/components/BannerLane.js +41 -0
- package/dist/react/components/BannerLane.js.map +1 -0
- package/dist/react/components/Button.d.ts.map +1 -1
- package/dist/react/components/Button.js +2 -1
- package/dist/react/components/Button.js.map +1 -1
- package/dist/react/components/ChipFilterStrip.js +1 -1
- package/dist/react/components/ChipFilterStrip.js.map +1 -1
- package/dist/react/components/ComboboxField.d.ts.map +1 -1
- package/dist/react/components/ComboboxField.js +14 -4
- package/dist/react/components/ComboboxField.js.map +1 -1
- package/dist/react/components/CommentThread.js +2 -2
- package/dist/react/components/CommentThread.js.map +1 -1
- package/dist/react/components/ConfirmDialog.js +1 -1
- package/dist/react/components/ConfirmDialog.js.map +1 -1
- package/dist/react/components/DataTable/DataTable.d.ts +18 -2
- package/dist/react/components/DataTable/DataTable.d.ts.map +1 -1
- package/dist/react/components/DataTable/DataTable.js +51 -12
- package/dist/react/components/DataTable/DataTable.js.map +1 -1
- package/dist/react/components/DataTable/Pagination.js +1 -1
- package/dist/react/components/DataTable/Pagination.js.map +1 -1
- package/dist/react/components/DataTable/RowActions.d.ts +7 -1
- package/dist/react/components/DataTable/RowActions.d.ts.map +1 -1
- package/dist/react/components/DataTable/RowActions.js +10 -3
- package/dist/react/components/DataTable/RowActions.js.map +1 -1
- package/dist/react/components/DataTable/TableFilterBuilder.js +1 -1
- package/dist/react/components/DataTable/TableFilterBuilder.js.map +1 -1
- package/dist/react/components/DataTable/cells/CodeCell.js +1 -1
- package/dist/react/components/DataTable/cells/CodeCell.js.map +1 -1
- package/dist/react/components/DataTable/cells/LinkCell.js +1 -1
- package/dist/react/components/DataTable/cells/LinkCell.js.map +1 -1
- package/dist/react/components/DataTable/index.d.ts +1 -1
- package/dist/react/components/DataTable/index.d.ts.map +1 -1
- package/dist/react/components/DataTable/index.js.map +1 -1
- package/dist/react/components/DataTable/types.d.ts +20 -0
- package/dist/react/components/DataTable/types.d.ts.map +1 -1
- package/dist/react/components/DataTable/types.js +22 -0
- package/dist/react/components/DataTable/types.js.map +1 -1
- package/dist/react/components/DateTimeField.d.ts.map +1 -1
- package/dist/react/components/DateTimeField.js +4 -1
- package/dist/react/components/DateTimeField.js.map +1 -1
- package/dist/react/components/DeleteButton.js +2 -0
- package/dist/react/components/DeleteButton.js.map +1 -1
- package/dist/react/components/Drawer.js +3 -3
- package/dist/react/components/Drawer.js.map +1 -1
- package/dist/react/components/DropdownMenu.d.ts +16 -0
- package/dist/react/components/DropdownMenu.d.ts.map +1 -1
- package/dist/react/components/DropdownMenu.js +22 -6
- package/dist/react/components/DropdownMenu.js.map +1 -1
- package/dist/react/components/EmptyState.d.ts +10 -2
- package/dist/react/components/EmptyState.d.ts.map +1 -1
- package/dist/react/components/EmptyState.js +10 -5
- package/dist/react/components/EmptyState.js.map +1 -1
- package/dist/react/components/EventTimeline.js +1 -1
- package/dist/react/components/EventTimeline.js.map +1 -1
- package/dist/react/components/FileDropZone.js +1 -1
- package/dist/react/components/FileDropZone.js.map +1 -1
- package/dist/react/components/Grid.d.ts +9 -1
- package/dist/react/components/Grid.d.ts.map +1 -1
- package/dist/react/components/Grid.js +20 -2
- package/dist/react/components/Grid.js.map +1 -1
- package/dist/react/components/Guide.d.ts +31 -0
- package/dist/react/components/Guide.d.ts.map +1 -0
- package/dist/react/components/Guide.js +18 -0
- package/dist/react/components/Guide.js.map +1 -0
- package/dist/react/components/InfoHintImpl.d.ts +0 -13
- package/dist/react/components/InfoHintImpl.d.ts.map +1 -1
- package/dist/react/components/InfoHintImpl.js +17 -1
- package/dist/react/components/InfoHintImpl.js.map +1 -1
- package/dist/react/components/InlineEditableText.js +1 -1
- package/dist/react/components/InlineEditableText.js.map +1 -1
- package/dist/react/components/IsolatedFrame.js +1 -1
- package/dist/react/components/IsolatedFrame.js.map +1 -1
- package/dist/react/components/ListToolbar.d.ts.map +1 -1
- package/dist/react/components/ListToolbar.js +2 -2
- package/dist/react/components/ListToolbar.js.map +1 -1
- package/dist/react/components/LogViewer.d.ts +26 -2
- package/dist/react/components/LogViewer.d.ts.map +1 -1
- package/dist/react/components/LogViewer.js +41 -26
- package/dist/react/components/LogViewer.js.map +1 -1
- package/dist/react/components/Modal.d.ts +5 -1
- package/dist/react/components/Modal.d.ts.map +1 -1
- package/dist/react/components/Modal.js +18 -8
- package/dist/react/components/Modal.js.map +1 -1
- package/dist/react/components/Page.d.ts +51 -7
- package/dist/react/components/Page.d.ts.map +1 -1
- package/dist/react/components/Page.js +53 -8
- package/dist/react/components/Page.js.map +1 -1
- package/dist/react/components/PageGuideButton.d.ts +11 -0
- package/dist/react/components/PageGuideButton.d.ts.map +1 -0
- package/dist/react/components/PageGuideButton.js +13 -0
- package/dist/react/components/PageGuideButton.js.map +1 -0
- package/dist/react/components/PanelSeparator.js +1 -1
- package/dist/react/components/PanelSeparator.js.map +1 -1
- package/dist/react/components/Popover.d.ts.map +1 -1
- package/dist/react/components/Popover.js +2 -1
- package/dist/react/components/Popover.js.map +1 -1
- package/dist/react/components/PromptDialog.js +1 -1
- package/dist/react/components/PromptDialog.js.map +1 -1
- package/dist/react/components/ScrollArea.d.ts +9 -1
- package/dist/react/components/ScrollArea.d.ts.map +1 -1
- package/dist/react/components/ScrollArea.js +9 -3
- package/dist/react/components/ScrollArea.js.map +1 -1
- package/dist/react/components/Section.d.ts +5 -2
- package/dist/react/components/Section.d.ts.map +1 -1
- package/dist/react/components/Section.js +5 -2
- package/dist/react/components/Section.js.map +1 -1
- package/dist/react/components/Skeleton.d.ts +19 -1
- package/dist/react/components/Skeleton.d.ts.map +1 -1
- package/dist/react/components/Skeleton.js +24 -2
- package/dist/react/components/Skeleton.js.map +1 -1
- package/dist/react/components/StatStrip.js +1 -1
- package/dist/react/components/StatStrip.js.map +1 -1
- package/dist/react/components/StatTile.d.ts +30 -3
- package/dist/react/components/StatTile.d.ts.map +1 -1
- package/dist/react/components/StatTile.js +35 -9
- package/dist/react/components/StatTile.js.map +1 -1
- package/dist/react/components/StatTileGrid.d.ts +10 -1
- package/dist/react/components/StatTileGrid.d.ts.map +1 -1
- package/dist/react/components/StatTileGrid.js +50 -33
- package/dist/react/components/StatTileGrid.js.map +1 -1
- package/dist/react/components/StatusPill.d.ts.map +1 -1
- package/dist/react/components/StatusPill.js +26 -1
- package/dist/react/components/StatusPill.js.map +1 -1
- package/dist/react/components/Surface.d.ts +1 -1
- package/dist/react/components/Surface.d.ts.map +1 -1
- package/dist/react/components/Surface.js +20 -11
- package/dist/react/components/Surface.js.map +1 -1
- package/dist/react/components/SwatchPicker.d.ts.map +1 -1
- package/dist/react/components/SwatchPicker.js +5 -1
- package/dist/react/components/SwatchPicker.js.map +1 -1
- package/dist/react/components/Switch.js +1 -1
- package/dist/react/components/Switch.js.map +1 -1
- package/dist/react/components/WidgetHeader.js +1 -1
- package/dist/react/components/WidgetHeader.js.map +1 -1
- package/dist/react/components/calendar/CalendarLegend.js +1 -1
- package/dist/react/components/calendar/CalendarLegend.js.map +1 -1
- package/dist/react/components/calendar/DayAgenda.js +1 -1
- package/dist/react/components/calendar/DayAgenda.js.map +1 -1
- package/dist/react/components/calendar/EventChip.js +1 -1
- package/dist/react/components/calendar/EventChip.js.map +1 -1
- package/dist/react/components/calendar/MonthGrid.js +1 -1
- package/dist/react/components/calendar/MonthGrid.js.map +1 -1
- package/dist/react/components/calendar/WeekView.js +2 -2
- package/dist/react/components/calendar/WeekView.js.map +1 -1
- package/dist/react/components/cardFrame.d.ts +16 -0
- package/dist/react/components/cardFrame.d.ts.map +1 -0
- package/dist/react/components/cardFrame.js +24 -0
- package/dist/react/components/cardFrame.js.map +1 -0
- package/dist/react/components/dialog.d.ts.map +1 -1
- package/dist/react/components/dialog.js +2 -1
- package/dist/react/components/dialog.js.map +1 -1
- package/dist/react/dashboard-grid/WidgetFrame.js +2 -2
- package/dist/react/dashboard-grid/WidgetFrame.js.map +1 -1
- package/dist/react/emoji/EmojiPicker.js +1 -1
- package/dist/react/emoji/EmojiPicker.js.map +1 -1
- package/dist/react/file-tree/FileTree.d.ts +8 -1
- package/dist/react/file-tree/FileTree.d.ts.map +1 -1
- package/dist/react/file-tree/FileTree.js +29 -4
- package/dist/react/file-tree/FileTree.js.map +1 -1
- package/dist/react/filter-rail/FilterRail.js +1 -1
- package/dist/react/filter-rail/FilterRail.js.map +1 -1
- package/dist/react/icons.js +1 -1
- package/dist/react/icons.js.map +1 -1
- package/dist/react/index.d.ts +8 -5
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +5 -2
- package/dist/react/index.js.map +1 -1
- package/dist/react/lib/menuSections.d.ts +8 -0
- package/dist/react/lib/menuSections.d.ts.map +1 -0
- package/dist/react/lib/menuSections.js +37 -0
- package/dist/react/lib/menuSections.js.map +1 -0
- package/dist/react/markdown/Markdown.js +2 -2
- package/dist/react/markdown/Markdown.js.map +1 -1
- package/dist/react/media/HoverScrubVideoThumb.js +1 -1
- package/dist/react/media/HoverScrubVideoThumb.js.map +1 -1
- package/dist/react/media/MaskPainter.js +1 -1
- package/dist/react/media/MediaUploadControl.js +1 -1
- package/dist/react/media/MediaUploadControl.js.map +1 -1
- package/dist/react/media/VideoTrimmer.js +1 -1
- package/dist/react/media/VideoTrimmer.js.map +1 -1
- package/dist/react/media/denseGallery.css +5 -1
- package/dist/react/namespace/NamespaceTargetCapsule.js +1 -1
- package/dist/react/namespace/NamespaceTargetCapsule.js.map +1 -1
- package/dist/react/nav/BreadcrumbTrail.js +1 -1
- package/dist/react/nav/BreadcrumbTrail.js.map +1 -1
- package/dist/react/nav/CommandBar.d.ts +7 -1
- package/dist/react/nav/CommandBar.d.ts.map +1 -1
- package/dist/react/nav/CommandBar.js +5 -3
- package/dist/react/nav/CommandBar.js.map +1 -1
- package/dist/react/nav/PlaceBar.d.ts.map +1 -1
- package/dist/react/nav/PlaceBar.js +7 -9
- package/dist/react/nav/PlaceBar.js.map +1 -1
- package/dist/react/nav/SideNav.d.ts +12 -2
- package/dist/react/nav/SideNav.d.ts.map +1 -1
- package/dist/react/nav/SideNav.js +42 -22
- package/dist/react/nav/SideNav.js.map +1 -1
- package/dist/react/nav/TopBar.d.ts +65 -31
- package/dist/react/nav/TopBar.d.ts.map +1 -1
- package/dist/react/nav/TopBar.js +59 -24
- package/dist/react/nav/TopBar.js.map +1 -1
- package/dist/react/nav/index.d.ts +1 -1
- package/dist/react/nav/index.d.ts.map +1 -1
- package/dist/react/nav/index.js +1 -1
- package/dist/react/nav/index.js.map +1 -1
- package/dist/react/nav/navShared.d.ts +10 -1
- package/dist/react/nav/navShared.d.ts.map +1 -1
- package/dist/react/nav/navShared.js +23 -0
- package/dist/react/nav/navShared.js.map +1 -1
- package/dist/react/notifications/NotificationFeed.js +2 -2
- package/dist/react/notifications/NotificationFeed.js.map +1 -1
- package/dist/react/palette/Command.d.ts.map +1 -1
- package/dist/react/palette/Command.js +1 -1
- package/dist/react/palette/Command.js.map +1 -1
- package/dist/react/palette/CommandPalette.d.ts +13 -1
- package/dist/react/palette/CommandPalette.d.ts.map +1 -1
- package/dist/react/palette/CommandPalette.js +44 -8
- package/dist/react/palette/CommandPalette.js.map +1 -1
- package/dist/react/palette/commandSources.d.ts +57 -0
- package/dist/react/palette/commandSources.d.ts.map +1 -0
- package/dist/react/palette/commandSources.js +129 -0
- package/dist/react/palette/commandSources.js.map +1 -0
- package/dist/react/palette/index.d.ts +1 -0
- package/dist/react/palette/index.d.ts.map +1 -1
- package/dist/react/palette/index.js +1 -0
- package/dist/react/palette/index.js.map +1 -1
- package/dist/react/rich-text/RichTextEditor.js +1 -1
- package/dist/react/rich-text/RichTextEditor.js.map +1 -1
- package/dist/react/rich-text/RichTextToolbar.js +1 -1
- package/dist/react/rich-text/RichTextToolbar.js.map +1 -1
- package/dist/scripts/guardrailsEnforce.d.ts.map +1 -1
- package/dist/styles-areas/analytics.css +16 -16
- package/dist/styles-areas/auth.css +3 -3
- package/dist/styles-areas/calendar.css +6 -6
- package/dist/styles-areas/chart-table.css +1 -1
- package/dist/styles-areas/charts.css +3 -3
- package/dist/styles-areas/chat.css +7 -6
- package/dist/styles-areas/companion-link.css +2 -2
- package/dist/styles-areas/core.css +98 -89
- package/dist/styles-areas/dashboard-grid.css +4 -4
- package/dist/styles-areas/data-table.css +16 -16
- package/dist/styles-areas/disk-usage.css +5 -5
- package/dist/styles-areas/emoji.css +2 -2
- package/dist/styles-areas/fields.css +13 -14
- package/dist/styles-areas/filter-rail.css +4 -4
- package/dist/styles-areas/folder-tree.css +3 -3
- package/dist/styles-areas/markdown.css +2 -1
- package/dist/styles-areas/master-detail.css +3 -3
- package/dist/styles-areas/media-gallery.css +8 -8
- package/dist/styles-areas/media.css +12 -13
- package/dist/styles-areas/nav.css +28 -14
- package/dist/styles-areas/notifications.css +4 -4
- package/dist/styles-areas/overlays.css +4 -4
- package/dist/styles-areas/palette.css +4 -3
- package/dist/styles-areas/rich-text.css +3 -3
- package/dist/styles-areas/sharing.css +4 -3
- package/dist/styles-areas/stats.css +9 -8
- package/dist/styles-areas/test-report.css +3 -3
- package/dist/styles-areas/wizard.css +6 -5
- package/dist/styles-areas/workbench.css +10 -9
- package/dist/styles-areas/workbook-viewer.css +2 -2
- package/dist/styles-utilities.css +272 -187
- package/dist/testing/designAudit.d.ts.map +1 -1
- package/dist/testing/designAudit.js +4 -0
- package/dist/testing/designAudit.js.map +1 -1
- package/dist/testing/designBaseline.d.ts.map +1 -1
- package/dist/testing/designBaseline.js +4 -0
- package/dist/testing/designBaseline.js.map +1 -1
- package/dist/theme.css +103 -2
- package/package.json +3 -2
- package/scripts/guardrails-baseline.json +2 -1
- package/scripts/guardrailsEnforce.spec.ts +64 -0
- package/scripts/guardrailsEnforce.ts +65 -8
- package/scripts/styleAreas.ts +1 -0
- package/src/nodeTypesDeclared.spec.ts +47 -0
- package/src/react/Toast.spec.tsx +43 -0
- package/src/react/Toast.tsx +45 -2
- package/src/react/analytics/MetricDrilldown.tsx +1 -1
- package/src/react/charts/AreaChart.tsx +9 -8
- package/src/react/charts/BarChart.tsx +11 -10
- package/src/react/charts/ChartCard.tsx +146 -0
- package/src/react/charts/ChartFrame.tsx +28 -10
- package/src/react/charts/ChartLegend.tsx +1 -1
- package/src/react/charts/HeatmapChart.tsx +5 -5
- package/src/react/charts/IntervalTimeline.tsx +2 -2
- package/src/react/charts/LineChart.tsx +9 -8
- package/src/react/charts/TimeSeriesChart.tsx +11 -10
- package/src/react/charts/chartBox.spec.ts +40 -0
- package/src/react/charts/chartBox.ts +75 -0
- package/src/react/charts/index.ts +2 -0
- package/src/react/charts/shared.ts +16 -3
- package/src/react/chat/Bubble.tsx +5 -5
- package/src/react/chat/MessageScroller.tsx +1 -1
- package/src/react/components/ActionBar.spec.tsx +90 -0
- package/src/react/components/ActionBar.tsx +82 -2
- package/src/react/components/AddItemsMenu.tsx +1 -1
- package/src/react/components/Alert.spec.tsx +32 -2
- package/src/react/components/Alert.tsx +47 -3
- package/src/react/components/AppShell.spec.tsx +199 -13
- package/src/react/components/AppShell.tsx +235 -92
- package/src/react/components/AsyncPanel.spec.tsx +7 -1
- package/src/react/components/AsyncPanel.tsx +85 -5
- package/src/react/components/BannerLane.tsx +128 -0
- package/src/react/components/Button.tsx +2 -0
- package/src/react/components/ChipFilterStrip.tsx +1 -1
- package/src/react/components/ComboboxField.tsx +20 -3
- package/src/react/components/CommentThread.tsx +2 -2
- package/src/react/components/ConfirmDialog.tsx +1 -1
- package/src/react/components/DataTable/DataTable.tsx +80 -8
- package/src/react/components/DataTable/Pagination.tsx +1 -1
- package/src/react/components/DataTable/RowActions.tsx +55 -2
- package/src/react/components/DataTable/TableFilterBuilder.tsx +1 -1
- package/src/react/components/DataTable/cells/CodeCell.tsx +1 -1
- package/src/react/components/DataTable/cells/LinkCell.tsx +1 -1
- package/src/react/components/DataTable/index.ts +1 -0
- package/src/react/components/DataTable/numericColumn.spec.ts +19 -0
- package/src/react/components/DataTable/types.ts +36 -0
- package/src/react/components/DateTimeField.tsx +3 -1
- package/src/react/components/DeleteButton.tsx +3 -0
- package/src/react/components/Drawer.tsx +3 -3
- package/src/react/components/DropdownMenu.spec.tsx +55 -0
- package/src/react/components/DropdownMenu.tsx +73 -20
- package/src/react/components/EmptyState.tsx +26 -7
- package/src/react/components/EventTimeline.tsx +1 -1
- package/src/react/components/Field.width.spec.tsx +11 -0
- package/src/react/components/FileDropZone.tsx +1 -1
- package/src/react/components/Grid.spec.tsx +44 -0
- package/src/react/components/Grid.tsx +41 -11
- package/src/react/components/Guide.tsx +77 -0
- package/src/react/components/InfoHint.spec.tsx +21 -0
- package/src/react/components/InfoHintImpl.tsx +22 -1
- package/src/react/components/InlineEditableText.tsx +1 -1
- package/src/react/components/IsolatedFrame.tsx +1 -1
- package/src/react/components/ListToolbar.spec.tsx +12 -0
- package/src/react/components/ListToolbar.tsx +8 -5
- package/src/react/components/LogViewer.tsx +118 -18
- package/src/react/components/Modal.spec.tsx +17 -2
- package/src/react/components/Modal.tsx +25 -10
- package/src/react/components/MultiSelectField.spec.tsx +6 -0
- package/src/react/components/Page.spec.tsx +89 -5
- package/src/react/components/Page.tsx +100 -16
- package/src/react/components/PageGuideButton.tsx +42 -0
- package/src/react/components/PanelSeparator.tsx +1 -1
- package/src/react/components/Popover.tsx +3 -1
- package/src/react/components/PromptDialog.tsx +1 -1
- package/src/react/components/ScrollArea.spec.tsx +20 -0
- package/src/react/components/ScrollArea.tsx +19 -0
- package/src/react/components/Section.spec.tsx +82 -1
- package/src/react/components/Section.tsx +10 -4
- package/src/react/components/Skeleton.tsx +50 -2
- package/src/react/components/StatStrip.tsx +1 -1
- package/src/react/components/StatTile.tsx +84 -11
- package/src/react/components/StatTileGrid.spec.tsx +6 -1
- package/src/react/components/StatTileGrid.tsx +25 -0
- package/src/react/components/StatusPill.tsx +26 -1
- package/src/react/components/Surface.tsx +22 -14
- package/src/react/components/SwatchPicker.tsx +4 -0
- package/src/react/components/Switch.tsx +1 -1
- package/src/react/components/WidgetHeader.tsx +1 -1
- package/src/react/components/calendar/CalendarLegend.tsx +1 -1
- package/src/react/components/calendar/DayAgenda.tsx +1 -1
- package/src/react/components/calendar/EventChip.tsx +1 -1
- package/src/react/components/calendar/MonthGrid.tsx +1 -1
- package/src/react/components/calendar/WeekView.tsx +2 -2
- package/src/react/components/cardFrame.ts +28 -0
- package/src/react/components/dataDisplay.spec.tsx +176 -0
- package/src/react/components/dialog.tsx +4 -1
- package/src/react/dashboard-grid/WidgetFrame.tsx +2 -2
- package/src/react/emoji/EmojiPicker.tsx +1 -1
- package/src/react/file-tree/FileTree.spec.tsx +49 -0
- package/src/react/file-tree/FileTree.tsx +37 -4
- package/src/react/filter-rail/FilterRail.tsx +1 -1
- package/src/react/icons.tsx +1 -1
- package/src/react/index.ts +28 -3
- package/src/react/lib/menuSections.ts +33 -0
- package/src/react/markdown/Markdown.tsx +2 -2
- package/src/react/media/HoverScrubVideoThumb.tsx +2 -2
- package/src/react/media/MaskPainter.tsx +1 -1
- package/src/react/media/MediaUploadControl.tsx +1 -1
- package/src/react/media/VideoTrimmer.tsx +1 -1
- package/src/react/media/denseGallery.css +5 -1
- package/src/react/namespace/NamespaceTargetCapsule.tsx +1 -1
- package/src/react/nav/BreadcrumbTrail.tsx +2 -2
- package/src/react/nav/CommandBar.tsx +14 -3
- package/src/react/nav/PlaceBar.tsx +18 -8
- package/src/react/nav/SideNav.tsx +99 -17
- package/src/react/nav/TopBar.spec.tsx +78 -21
- package/src/react/nav/TopBar.tsx +270 -98
- package/src/react/nav/index.ts +8 -1
- package/src/react/nav/nav.spec.tsx +46 -2
- package/src/react/nav/navShared.tsx +23 -2
- package/src/react/notifications/NotificationFeed.tsx +2 -2
- package/src/react/palette/Command.tsx +1 -1
- package/src/react/palette/CommandPalette.spec.tsx +31 -0
- package/src/react/palette/CommandPalette.tsx +108 -11
- package/src/react/palette/commandSources.tsx +186 -0
- package/src/react/palette/index.ts +7 -0
- package/src/react/rich-text/RichTextEditor.tsx +1 -1
- package/src/react/rich-text/RichTextToolbar.tsx +1 -1
- package/src/styles-areas/analytics.css +16 -16
- package/src/styles-areas/auth.css +3 -3
- package/src/styles-areas/calendar.css +6 -6
- package/src/styles-areas/chart-table.css +1 -1
- package/src/styles-areas/charts.css +3 -3
- package/src/styles-areas/chat.css +7 -6
- package/src/styles-areas/companion-link.css +2 -2
- package/src/styles-areas/core.css +98 -89
- package/src/styles-areas/dashboard-grid.css +4 -4
- package/src/styles-areas/data-table.css +16 -16
- package/src/styles-areas/disk-usage.css +5 -5
- package/src/styles-areas/emoji.css +2 -2
- package/src/styles-areas/fields.css +13 -14
- package/src/styles-areas/filter-rail.css +4 -4
- package/src/styles-areas/folder-tree.css +3 -3
- package/src/styles-areas/markdown.css +2 -1
- package/src/styles-areas/master-detail.css +3 -3
- package/src/styles-areas/media-gallery.css +8 -8
- package/src/styles-areas/media.css +12 -13
- package/src/styles-areas/nav.css +28 -14
- package/src/styles-areas/notifications.css +4 -4
- package/src/styles-areas/overlays.css +4 -4
- package/src/styles-areas/palette.css +4 -3
- package/src/styles-areas/rich-text.css +3 -3
- package/src/styles-areas/sharing.css +4 -3
- package/src/styles-areas/stats.css +9 -8
- package/src/styles-areas/test-report.css +3 -3
- package/src/styles-areas/wizard.css +6 -5
- package/src/styles-areas/workbench.css +10 -9
- package/src/styles-areas/workbook-viewer.css +2 -2
- package/src/styles-utilities.css +272 -187
- package/src/testing/designAudit.ts +3 -0
- package/src/testing/designBaseline.ts +4 -0
- package/src/theme.css +103 -2
- package/src/themeDarkScopeAliases.spec.ts +65 -0
|
@@ -1,12 +1,24 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import
|
|
1
|
+
import { type ElementType, type ReactNode, type Ref, useLayoutEffect, useRef } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
allowAllGate,
|
|
4
|
+
deriveSideNavDomains,
|
|
5
|
+
type GateFn,
|
|
6
|
+
type NavigationModel,
|
|
7
|
+
type NavPreferences,
|
|
8
|
+
} from 'cursedbelt-core/navigation';
|
|
3
9
|
import { useKeyboardInset } from '../hooks/useKeyboardInset.js';
|
|
4
10
|
import { cn } from '../lib/cn.js';
|
|
11
|
+
import { devWarnOnce } from '../lib/devWarn.js';
|
|
5
12
|
import { CommandBar } from '../nav/CommandBar.js';
|
|
6
|
-
import type
|
|
13
|
+
import { type IconResolver, isDomainActive, type NavBadgeTone, renderIcon, routeClick } from '../nav/navShared.js';
|
|
7
14
|
import { PlaceBar } from '../nav/PlaceBar.js';
|
|
8
15
|
import { SideNav } from '../nav/SideNav.js';
|
|
16
|
+
import { TopBarPlaces } from '../nav/TopBar.js';
|
|
17
|
+
import { CommandPalette } from '../palette/CommandPalette.js';
|
|
18
|
+
import { CommandSourceProvider, useCommandSources } from '../palette/commandSources.js';
|
|
19
|
+
import { useCommandPalette } from '../palette/useCommandPalette.js';
|
|
9
20
|
import { AmbientBackdrop, type AmbientBackdropProps } from './AmbientBackdrop.js';
|
|
21
|
+
import { BannerLane, type ShellBanner } from './BannerLane.js';
|
|
10
22
|
import { ScrollArea, type ScrollAreaSpace } from './ScrollArea.js';
|
|
11
23
|
|
|
12
24
|
export interface AppShellProps {
|
|
@@ -14,11 +26,15 @@ export interface AppShellProps {
|
|
|
14
26
|
children: ReactNode;
|
|
15
27
|
|
|
16
28
|
/**
|
|
17
|
-
* The navigation model — the
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
29
|
+
* The navigation model — the one registry every nav surface reads. The app passes it and
|
|
30
|
+
* the SHELL picks the pattern from the number of places (NAV-3):
|
|
31
|
+
*
|
|
32
|
+
* | places | ≥1024px | 768–1023px | <768px |
|
|
33
|
+
* |---|---|---|---|
|
|
34
|
+
* | 2–5 | labeled `TopBar` places in the header's center | same | `PlaceBar`, every place |
|
|
35
|
+
* | 6+ | `SideNav` with labeled sections, 240px | the 64px icon rail | `PlaceBar`, top 4 + More |
|
|
36
|
+
*
|
|
37
|
+
* Six or more places mount the ⌘K `CommandPalette` too (NAV-9). Omit for chromeless pages.
|
|
22
38
|
*/
|
|
23
39
|
nav?: NavigationModel;
|
|
24
40
|
|
|
@@ -50,9 +66,34 @@ export interface AppShellProps {
|
|
|
50
66
|
/** Brand slot — top-left of the command spine (logo / app name). */
|
|
51
67
|
navHeader?: ReactNode;
|
|
52
68
|
|
|
53
|
-
/**
|
|
69
|
+
/**
|
|
70
|
+
* The sidebar's footer (NAV-13): the version, `BuildFreshness` and a Settings link, set in
|
|
71
|
+
* 12px muted type. Only a 6+-place shell has a sidebar; a 2–5-place app keeps these in
|
|
72
|
+
* Settings → About.
|
|
73
|
+
*/
|
|
54
74
|
navFooter?: ReactNode;
|
|
55
75
|
|
|
76
|
+
/**
|
|
77
|
+
* App-wide conditions — a sign-in broke, the Mac is asleep, the app is offline — as the ONE
|
|
78
|
+
* banner lane between the header and the page (NAV-10). One line each; two or more collapse
|
|
79
|
+
* into "N problems · View". Never a card inside the page.
|
|
80
|
+
*/
|
|
81
|
+
banners?: readonly ShellBanner[];
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The header's height (NAV-1): `dense` 48px for a tool (station, desk, binary-server),
|
|
85
|
+
* `roomy` 64px for media and consumer apps. Unset, it reads `--header-h` (64px; 48px
|
|
86
|
+
* inside `.cb-dense`).
|
|
87
|
+
*/
|
|
88
|
+
density?: 'roomy' | 'dense';
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Mount the shell's own ⌘K `CommandPalette` — every place preloaded, plus whatever pages
|
|
92
|
+
* contribute through `useCommandSource`. `'auto'` (default) mounts it for 6+ places
|
|
93
|
+
* (NAV-9). Ignored when the app renders its own through `palette`.
|
|
94
|
+
*/
|
|
95
|
+
commandPalette?: boolean | 'auto';
|
|
96
|
+
|
|
56
97
|
/**
|
|
57
98
|
* Universal capture (UX north-star §8.2) — wired to the command spine's capture
|
|
58
99
|
* hero (desktop) and the `PlaceBar`'s raised center FAB (mobile). Capture is a
|
|
@@ -94,7 +135,7 @@ export interface AppShellProps {
|
|
|
94
135
|
*/
|
|
95
136
|
palette?: ReactNode;
|
|
96
137
|
|
|
97
|
-
/** Max places in the mobile `PlaceBar` thumb zone
|
|
138
|
+
/** Max places in the mobile `PlaceBar` thumb zone; past it, `max - 1` places + More. Default 5. */
|
|
98
139
|
placeBarMax?: number;
|
|
99
140
|
|
|
100
141
|
/** PlaceBar caption policy (`'active'` | `'all'`). Pass `'all'` when place names
|
|
@@ -127,52 +168,58 @@ export interface AppShellProps {
|
|
|
127
168
|
className?: string;
|
|
128
169
|
}
|
|
129
170
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
171
|
+
/** NAV-3's line: up to this many places ride a labeled top bar; past it, a sectioned sidebar. */
|
|
172
|
+
const TOP_BAR_MAX_PLACES = 5;
|
|
173
|
+
/** Past this many places, a sidebar with no sections is a wall (NAV-3: ≤7 items a section). */
|
|
174
|
+
const UNSECTIONED_MAX_PLACES = 7;
|
|
133
175
|
|
|
134
176
|
/**
|
|
135
|
-
* THE single application shell. One per app —
|
|
136
|
-
* (`
|
|
177
|
+
* THE single application shell. One per app — the frame every page sits in
|
|
178
|
+
* (`docs/design/shell-and-navigation.md`, NAV-1…NAV-14). The app passes ONE navigation
|
|
179
|
+
* model and the shell decides how to draw it, so no app can pick the wrong pattern:
|
|
137
180
|
*
|
|
138
|
-
* **
|
|
139
|
-
*
|
|
140
|
-
* -
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* place-set in the thumb zone around a raised center capture FAB (mobile is
|
|
144
|
-
* capture-forward, not tab-forward — §7).
|
|
145
|
-
* - The buried, invisible ⌘K palette + no capture → the first-class **`CommandBar`
|
|
146
|
-
* spine**: a visible command/search field (the co-equal second way to move) + a
|
|
147
|
-
* capture hero (universal capture, §8.2).
|
|
148
|
-
* - The avatar-in-nav → **account chrome** behind the avatar in the spine (not a door).
|
|
181
|
+
* - **2–5 places** → their labels in the header's center zone (NAV-2: destinations have
|
|
182
|
+
* words from 768px up), every place in the `PlaceBar` on a phone.
|
|
183
|
+
* - **6+ places** → a 240px `SideNav` in labeled sections at ≥1024px, the 64px icon rail
|
|
184
|
+
* at 768–1023px, the `PlaceBar` with the top 4 + More on a phone, and a ⌘K
|
|
185
|
+
* `CommandPalette` preloaded with every place (NAV-9).
|
|
149
186
|
*
|
|
150
|
-
*
|
|
151
|
-
* coverage test
|
|
152
|
-
*
|
|
187
|
+
* Kept from the shell before it (owner teardowns 2026-07-15, the UX north-star): the single
|
|
188
|
+
* `navigationModel` registry and its coverage test, per-user place prefs, badges, universal
|
|
189
|
+
* capture (the spine's hero on desktop, the `PlaceBar` FAB on a phone), and the
|
|
190
|
+
* keyboard-safe single `ScrollArea`. No hamburger drawer, ever.
|
|
153
191
|
*
|
|
154
192
|
* Layout model:
|
|
155
193
|
* ```
|
|
156
194
|
* <div h-dvh flex flex-col overflow-hidden> ← dvh excludes the mobile URL bar
|
|
157
|
-
* <header
|
|
158
|
-
* <
|
|
159
|
-
*
|
|
195
|
+
* <header h-[--header-h]> ← left: brand · center: places or ⌘K field · right: account
|
|
196
|
+
* <BannerLane /> ← NAV-10, only when there is a banner
|
|
197
|
+
* <div flex min-h-0 grow> ← sidebar + content row
|
|
198
|
+
* <aside md:flex lg:hidden w-16 /> ← 6+ places: the icon rail
|
|
199
|
+
* <aside lg:flex w-60 /> ← 6+ places: the sectioned SideNav
|
|
160
200
|
* <main><ScrollArea>{children}</ScrollArea></main> ← THE ONLY page scroll container
|
|
161
201
|
* </div>
|
|
162
202
|
* <div pb-[safe-area]> ← bottomDock + mobile PlaceBar
|
|
163
|
-
* {bottomDock}
|
|
164
|
-
* <PlaceBar md:hidden /> ← capture-forward bottom bar
|
|
165
|
-
* </div>
|
|
166
203
|
* </div>
|
|
167
204
|
* ```
|
|
168
205
|
*
|
|
169
206
|
* Keyboard model: `useKeyboardInset` (installed exactly once, here) writes
|
|
170
207
|
* `--keyboard-inset` onto `<html>`; `ScrollArea` reads it via CSS `calc()`.
|
|
171
208
|
*/
|
|
172
|
-
export function AppShell({
|
|
209
|
+
export function AppShell(props: AppShellProps) {
|
|
210
|
+
// The provider sits ABOVE the shell, so a page anywhere in `children` can contribute to the
|
|
211
|
+
// palette the shell mounts, and the shell can read what they contributed.
|
|
212
|
+
return (
|
|
213
|
+
<CommandSourceProvider>
|
|
214
|
+
<Shell {...props} />
|
|
215
|
+
</CommandSourceProvider>
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function Shell({
|
|
173
220
|
children,
|
|
174
221
|
nav,
|
|
175
|
-
navGate,
|
|
222
|
+
navGate = allowAllGate,
|
|
176
223
|
activeHref,
|
|
177
224
|
onNavigate,
|
|
178
225
|
navLinkComponent,
|
|
@@ -183,6 +230,9 @@ export function AppShell({
|
|
|
183
230
|
navBadgeTone,
|
|
184
231
|
navHeader,
|
|
185
232
|
navFooter,
|
|
233
|
+
banners,
|
|
234
|
+
density,
|
|
235
|
+
commandPalette = 'auto',
|
|
186
236
|
onCapture,
|
|
187
237
|
captureLabel,
|
|
188
238
|
onOpenCommand,
|
|
@@ -201,16 +251,68 @@ export function AppShell({
|
|
|
201
251
|
}: AppShellProps) {
|
|
202
252
|
useKeyboardInset();
|
|
203
253
|
|
|
204
|
-
const
|
|
254
|
+
const places = nav ? deriveSideNavDomains(nav, navGate) : [];
|
|
255
|
+
const pattern: 'none' | 'top' | 'side' = !nav ? 'none' : places.length <= TOP_BAR_MAX_PLACES ? 'top' : 'side';
|
|
256
|
+
if (pattern === 'side' && places.length > UNSECTIONED_MAX_PLACES && !places.some((d) => d.section)) {
|
|
257
|
+
devWarnOnce(
|
|
258
|
+
'AppShell:unsectioned',
|
|
259
|
+
`cursedbelt: <AppShell> was given ${places.length} places and no sections. NAV-3 wants 2–5 labeled sections ` +
|
|
260
|
+
'of ≤7 places each — give each NavDomain a `section` ("Operate", "Insight", "Limits", "Records").',
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// NAV-9: the shell's own palette, unless the app renders one.
|
|
265
|
+
const ownPalette = !palette && Boolean(nav) && (commandPalette === 'auto' ? pattern === 'side' : commandPalette);
|
|
266
|
+
const paletteControls = useCommandPalette({ enabled: ownPalette });
|
|
267
|
+
const sources = useCommandSources();
|
|
268
|
+
const openCommand = onOpenCommand ?? (ownPalette ? paletteControls.toggle : undefined);
|
|
269
|
+
const navigate = (path: string) => {
|
|
270
|
+
if (onNavigate) onNavigate(path);
|
|
271
|
+
else window.location.assign(path);
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const showSpine = Boolean(nav || navHeader || account || openCommand || onCapture || commandBarExtra);
|
|
205
275
|
const showBottom = Boolean(bottomDock || nav);
|
|
276
|
+
const bottomChromeRef = usePublishedBottomChrome(showBottom);
|
|
206
277
|
// Chrome that lets the field through. The rails/spine keep their own directional
|
|
207
278
|
// border either way — only the FILL changes, so the layout is byte-identical and
|
|
208
279
|
// an app can flip `ambient` on and off without anything reflowing.
|
|
209
280
|
const chrome = ambient ? 'cb-glass-chrome' : 'bg-background';
|
|
210
281
|
|
|
282
|
+
const commandBar = (
|
|
283
|
+
<CommandBar
|
|
284
|
+
// The search field is capped and centered: a 2000px-wide input reads as a form, not a
|
|
285
|
+
// header (Linear and GitHub cap theirs the same way).
|
|
286
|
+
className={pattern === 'top' ? 'shrink-0' : 'min-w-0 max-w-2xl flex-1'}
|
|
287
|
+
compact={pattern === 'top'}
|
|
288
|
+
onCapture={onCapture}
|
|
289
|
+
captureLabel={captureLabel}
|
|
290
|
+
// On mobile the PlaceBar FAB owns capture — don't duplicate it in the spine.
|
|
291
|
+
captureDesktopOnly={Boolean(nav)}
|
|
292
|
+
onOpenCommand={openCommand}
|
|
293
|
+
commandPlaceholder={commandLabel}
|
|
294
|
+
commandHint={commandHint}
|
|
295
|
+
extra={commandBarExtra}
|
|
296
|
+
/>
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
const sideNavProps = {
|
|
300
|
+
model: nav as NavigationModel,
|
|
301
|
+
gate: navGate,
|
|
302
|
+
activeHref,
|
|
303
|
+
onNavigate,
|
|
304
|
+
linkComponent: navLinkComponent,
|
|
305
|
+
resolveIcon,
|
|
306
|
+
prefs: navPrefs,
|
|
307
|
+
badges: navBadges,
|
|
308
|
+
badgeTone: navBadgeTone,
|
|
309
|
+
footer: navFooter,
|
|
310
|
+
};
|
|
311
|
+
|
|
211
312
|
return (
|
|
212
313
|
<div
|
|
213
314
|
data-cb-appshell=''
|
|
315
|
+
data-cb-nav-pattern={pattern}
|
|
214
316
|
className={cn(
|
|
215
317
|
'flex h-dvh flex-col overflow-hidden bg-background text-foreground',
|
|
216
318
|
// `isolation: isolate` is load-bearing, not decoration: the blobs blend with
|
|
@@ -228,86 +330,89 @@ export function AppShell({
|
|
|
228
330
|
<AmbientBackdrop position='absolute' {...(ambient === true ? {} : ambient)} />
|
|
229
331
|
) : null}
|
|
230
332
|
|
|
231
|
-
{/* ──
|
|
333
|
+
{/* ── Header: three zones on one midline (NAV-1) ─────────────────────── */}
|
|
232
334
|
{showSpine && (
|
|
233
335
|
<header
|
|
234
336
|
data-cb-appheader=''
|
|
337
|
+
data-cb-density={density}
|
|
235
338
|
className={cn(
|
|
236
|
-
'relative z-30 flex shrink-0 items-center gap-3 border-b border-border px-3
|
|
339
|
+
'relative z-30 flex shrink-0 items-center gap-3 border-b border-border px-3 md:px-6',
|
|
340
|
+
density === 'dense'
|
|
341
|
+
? 'h-[var(--header-h-dense)]'
|
|
342
|
+
: density === 'roomy'
|
|
343
|
+
? 'h-[var(--header-h-roomy)]'
|
|
344
|
+
: 'h-[var(--header-h)]',
|
|
237
345
|
ambient && chrome,
|
|
238
346
|
)}
|
|
239
347
|
>
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
348
|
+
<div data-cb-header-zone='left' className='flex shrink-0 items-center'>
|
|
349
|
+
{navHeader}
|
|
350
|
+
</div>
|
|
351
|
+
<div data-cb-header-zone='center' className='flex h-full min-w-0 flex-1 items-center justify-center'>
|
|
352
|
+
{pattern === 'top' ? (
|
|
353
|
+
<TopBarPlaces
|
|
354
|
+
// Below 768px the PlaceBar carries every place (NAV-14), so the header doesn't.
|
|
355
|
+
phonePlaces='none'
|
|
356
|
+
linkComponent={navLinkComponent}
|
|
357
|
+
items={places.map((d) => ({
|
|
358
|
+
id: d.id,
|
|
359
|
+
label: d.label,
|
|
360
|
+
icon: renderIcon(resolveIcon, d.icon),
|
|
361
|
+
href: d.path,
|
|
362
|
+
active: isDomainActive(d, activeHref),
|
|
363
|
+
badge: navBadges?.[d.id],
|
|
364
|
+
badgeTone: navBadgeTone,
|
|
365
|
+
unavailable: d.hostLimit && d.hostLimit.navigable !== true ? d.hostLimit.reason : undefined,
|
|
366
|
+
linkProps: navLinkComponent ? undefined : { onClick: routeClick(d.path, onNavigate) },
|
|
367
|
+
}))}
|
|
368
|
+
/>
|
|
369
|
+
) : (
|
|
370
|
+
commandBar
|
|
371
|
+
)}
|
|
372
|
+
</div>
|
|
373
|
+
{/* The right zone is locked: it never shrinks (NAV-1). */}
|
|
374
|
+
{(pattern === 'top' || account) && (
|
|
375
|
+
<div data-cb-header-zone='right' className='flex shrink-0 items-center gap-2'>
|
|
376
|
+
{pattern === 'top' ? commandBar : null}
|
|
377
|
+
{account && (
|
|
378
|
+
<div data-cb-account='' className='shrink-0'>
|
|
379
|
+
{account}
|
|
380
|
+
</div>
|
|
381
|
+
)}
|
|
255
382
|
</div>
|
|
256
383
|
)}
|
|
257
384
|
</header>
|
|
258
385
|
)}
|
|
259
386
|
|
|
260
|
-
{/* ──
|
|
387
|
+
{/* ── The one banner lane (NAV-10) ──────────────────────────────────── */}
|
|
388
|
+
{banners && banners.length > 0 ? <BannerLane banners={banners} className={cn(ambient && chrome)} /> : null}
|
|
389
|
+
|
|
390
|
+
{/* ── Body row (sidebar + content) ───────────────────────────────────── */}
|
|
261
391
|
<div className='relative flex min-h-0 grow overflow-hidden'>
|
|
262
|
-
{/*
|
|
263
|
-
240px rail on an ~820px screen squeezes the workspace
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
{nav && (
|
|
392
|
+
{/* 6+ places: the labeled, sectioned sidebar at lg (240px), the icon rail at md–lg
|
|
393
|
+
(64px — a 240px rail on an ~820px screen squeezes the workspace, owner tablet
|
|
394
|
+
teardown 2026-07-15 §4). Two CSS-switched renders: SideNav's `collapsed` is a
|
|
395
|
+
prop, not a media query, and the rail is cheap to render twice. */}
|
|
396
|
+
{pattern === 'side' && (
|
|
268
397
|
<>
|
|
269
398
|
<aside
|
|
270
399
|
data-cb-nav-rail=''
|
|
400
|
+
data-cb-nav-collapsed=''
|
|
271
401
|
className={cn(
|
|
272
|
-
'hidden w-
|
|
402
|
+
'hidden w-[var(--rail-w-collapsed)] border-r border-border md:flex md:shrink-0 md:flex-col lg:hidden',
|
|
273
403
|
chrome,
|
|
274
404
|
)}
|
|
275
405
|
>
|
|
276
|
-
<SideNav
|
|
277
|
-
model={nav}
|
|
278
|
-
gate={navGate}
|
|
279
|
-
activeHref={activeHref}
|
|
280
|
-
onNavigate={onNavigate}
|
|
281
|
-
linkComponent={navLinkComponent}
|
|
282
|
-
resolveIcon={resolveIcon}
|
|
283
|
-
prefs={navPrefs}
|
|
284
|
-
collapsed
|
|
285
|
-
badges={navBadges}
|
|
286
|
-
badgeTone={navBadgeTone}
|
|
287
|
-
footer={navFooter}
|
|
288
|
-
/>
|
|
406
|
+
<SideNav {...sideNavProps} collapsed />
|
|
289
407
|
</aside>
|
|
290
408
|
<aside
|
|
291
409
|
data-cb-nav-rail=''
|
|
292
410
|
className={cn(
|
|
293
|
-
'hidden border-r border-border lg:flex lg:shrink-0 lg:flex-col',
|
|
411
|
+
'hidden w-[var(--rail-w)] border-r border-border lg:flex lg:shrink-0 lg:flex-col',
|
|
294
412
|
chrome,
|
|
295
|
-
RAIL_WIDTH,
|
|
296
413
|
)}
|
|
297
414
|
>
|
|
298
|
-
<SideNav
|
|
299
|
-
model={nav}
|
|
300
|
-
gate={navGate}
|
|
301
|
-
activeHref={activeHref}
|
|
302
|
-
onNavigate={onNavigate}
|
|
303
|
-
linkComponent={navLinkComponent}
|
|
304
|
-
resolveIcon={resolveIcon}
|
|
305
|
-
prefs={navPrefs}
|
|
306
|
-
onPrefsChange={onNavPrefsChange}
|
|
307
|
-
badges={navBadges}
|
|
308
|
-
badgeTone={navBadgeTone}
|
|
309
|
-
footer={navFooter}
|
|
310
|
-
/>
|
|
415
|
+
<SideNav {...sideNavProps} onPrefsChange={onNavPrefsChange} />
|
|
311
416
|
</aside>
|
|
312
417
|
</>
|
|
313
418
|
)}
|
|
@@ -325,7 +430,7 @@ export function AppShell({
|
|
|
325
430
|
|
|
326
431
|
{/* ── Bottom: dock + mobile PlaceBar — the ONLY safe-area owner. ──────── */}
|
|
327
432
|
{showBottom && (
|
|
328
|
-
<div className='shrink-0 pb-[env(safe-area-inset-bottom,0px)]'>
|
|
433
|
+
<div ref={bottomChromeRef} className='shrink-0 pb-[env(safe-area-inset-bottom,0px)]'>
|
|
329
434
|
{bottomDock && <div className='border-t border-border'>{bottomDock}</div>}
|
|
330
435
|
{nav && (
|
|
331
436
|
<PlaceBar
|
|
@@ -349,6 +454,44 @@ export function AppShell({
|
|
|
349
454
|
|
|
350
455
|
{/* The command palette portals its own Dialog; rendered here so one shell owns one. */}
|
|
351
456
|
{palette}
|
|
457
|
+
{ownPalette && nav ? (
|
|
458
|
+
<CommandPalette
|
|
459
|
+
model={nav}
|
|
460
|
+
gate={navGate}
|
|
461
|
+
open={paletteControls.open}
|
|
462
|
+
onOpenChange={paletteControls.setOpen}
|
|
463
|
+
onNavigate={navigate}
|
|
464
|
+
resolveIcon={resolveIcon}
|
|
465
|
+
currentPath={activeHref}
|
|
466
|
+
places
|
|
467
|
+
sources={sources}
|
|
468
|
+
/>
|
|
469
|
+
) : null}
|
|
352
470
|
</div>
|
|
353
471
|
);
|
|
354
472
|
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Publishes the bottom chrome's height (dock + `PlaceBar` + safe area) as
|
|
476
|
+
* `--cb-bottom-chrome-h` on the root, so what floats over the page — the `Toaster`'s stack
|
|
477
|
+
* (OVL-9) — rises above the navigation instead of covering it. A `PlaceBar` hidden at `md` up
|
|
478
|
+
* measures 0, so the desktop stack drops back to the corner by itself.
|
|
479
|
+
*/
|
|
480
|
+
function usePublishedBottomChrome(shown: boolean) {
|
|
481
|
+
const ref = useRef<HTMLDivElement | null>(null);
|
|
482
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: `shown` mounts or drops the element the ref points at.
|
|
483
|
+
useLayoutEffect(() => {
|
|
484
|
+
const el = ref.current;
|
|
485
|
+
const root = document.documentElement;
|
|
486
|
+
if (!el) return;
|
|
487
|
+
const publish = () => root.style.setProperty('--cb-bottom-chrome-h', `${el.offsetHeight}px`);
|
|
488
|
+
publish();
|
|
489
|
+
const observer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(publish);
|
|
490
|
+
observer?.observe(el);
|
|
491
|
+
return () => {
|
|
492
|
+
observer?.disconnect();
|
|
493
|
+
root.style.removeProperty('--cb-bottom-chrome-h');
|
|
494
|
+
};
|
|
495
|
+
}, [shown]);
|
|
496
|
+
return ref;
|
|
497
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { afterEach, describe, expect, test } from 'bun:test';
|
|
2
2
|
import { cleanup, render } from '@testing-library/react';
|
|
3
|
-
import { AsyncPanel } from './AsyncPanel.js';
|
|
3
|
+
import { AsyncPanel, ageLabel } from './AsyncPanel.js';
|
|
4
4
|
|
|
5
5
|
afterEach(cleanup);
|
|
6
6
|
|
|
@@ -65,4 +65,10 @@ describe('AsyncPanel', () => {
|
|
|
65
65
|
expect(getByText('No volumes reported')).toBeDefined();
|
|
66
66
|
expect(getByText('The collector has not run.')).toBeDefined();
|
|
67
67
|
});
|
|
68
|
+
|
|
69
|
+
test('ageLabel reads in the largest whole unit', () => {
|
|
70
|
+
expect(ageLabel(4_000)).toBe('just now');
|
|
71
|
+
expect(ageLabel(12_000)).toBe('12s ago');
|
|
72
|
+
expect(ageLabel(5 * 60_000)).toBe('5m ago');
|
|
73
|
+
});
|
|
68
74
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ReactNode, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import { Alert } from './Alert.js';
|
|
3
3
|
import { EmptyState } from './EmptyState.js';
|
|
4
|
-
import {
|
|
4
|
+
import { ContentSkeleton, type SkeletonVariant } from './Skeleton.js';
|
|
5
5
|
|
|
6
6
|
export interface AsyncPanelProps {
|
|
7
7
|
loading?: boolean;
|
|
@@ -12,8 +12,23 @@ export interface AsyncPanelProps {
|
|
|
12
12
|
/** Copy for the empty state. */
|
|
13
13
|
emptyTitle?: ReactNode;
|
|
14
14
|
emptyDescription?: ReactNode;
|
|
15
|
-
/** Rows of skeleton to show while loading. Default 3. */
|
|
15
|
+
/** Rows (or tiles) of skeleton to show while loading. Default 3. */
|
|
16
16
|
skeletonRows?: number;
|
|
17
|
+
/**
|
|
18
|
+
* The SHAPE of the first-load skeleton (STA-1): `rows` for a list or table, `tiles` for a
|
|
19
|
+
* stat strip, `chart` for a chart frame at the chart's own height. Default `rows`.
|
|
20
|
+
*/
|
|
21
|
+
skeleton?: SkeletonVariant;
|
|
22
|
+
/**
|
|
23
|
+
* A refetch is in flight while the panel already shows data (STA-2). The data stays and a
|
|
24
|
+
* quiet "Updating…" replaces the freshness stamp — the panel never blanks to a skeleton on
|
|
25
|
+
* every poll. `loading` after the first content behaves the same way on its own.
|
|
26
|
+
*/
|
|
27
|
+
refreshing?: boolean;
|
|
28
|
+
/** When the shown data was fetched — rendered as "Updated 12s ago" (STA-2, STA-8). */
|
|
29
|
+
updatedAt?: number | Date | null;
|
|
30
|
+
/** Past this age the stamp turns amber and says so ("No update for 5m", STA-8). */
|
|
31
|
+
staleAfterMs?: number;
|
|
17
32
|
/**
|
|
18
33
|
* Names the failing section in its error message ("AWS cost is unavailable").
|
|
19
34
|
* Without it the reader gets "Something went wrong" on a page of twelve
|
|
@@ -25,6 +40,54 @@ export interface AsyncPanelProps {
|
|
|
25
40
|
children: ReactNode;
|
|
26
41
|
}
|
|
27
42
|
|
|
43
|
+
/** "just now", "12s ago", "3m ago", "2h ago", "4d ago" — the freshness stamp's wording. */
|
|
44
|
+
export function ageLabel(ms: number): string {
|
|
45
|
+
if (!Number.isFinite(ms) || ms < 5_000) return 'just now';
|
|
46
|
+
const s = Math.floor(ms / 1000);
|
|
47
|
+
if (s < 60) return `${s}s ago`;
|
|
48
|
+
const m = Math.floor(s / 60);
|
|
49
|
+
if (m < 60) return `${m}m ago`;
|
|
50
|
+
const h = Math.floor(m / 60);
|
|
51
|
+
if (h < 48) return `${h}h ago`;
|
|
52
|
+
return `${Math.floor(h / 24)}d ago`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Re-render on a slow tick so an age keeps reading true without a refetch. */
|
|
56
|
+
function useNow(active: boolean, everyMs = 10_000): number {
|
|
57
|
+
const [now, setNow] = useState(() => Date.now());
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (!active) return;
|
|
60
|
+
const id = setInterval(() => setNow(Date.now()), everyMs);
|
|
61
|
+
return () => clearInterval(id);
|
|
62
|
+
}, [active, everyMs]);
|
|
63
|
+
return now;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function Freshness({
|
|
67
|
+
refreshing,
|
|
68
|
+
updatedAt,
|
|
69
|
+
staleAfterMs,
|
|
70
|
+
}: {
|
|
71
|
+
refreshing: boolean;
|
|
72
|
+
updatedAt: number | Date | null | undefined;
|
|
73
|
+
staleAfterMs: number | undefined;
|
|
74
|
+
}) {
|
|
75
|
+
const at = updatedAt == null ? null : typeof updatedAt === 'number' ? updatedAt : updatedAt.getTime();
|
|
76
|
+
const now = useNow(at != null);
|
|
77
|
+
if (!refreshing && at == null) return null;
|
|
78
|
+
const age = at == null ? 0 : Math.max(0, now - at);
|
|
79
|
+
const stale = !refreshing && staleAfterMs != null && at != null && age > staleAfterMs;
|
|
80
|
+
return (
|
|
81
|
+
<p
|
|
82
|
+
data-cb-freshness={refreshing ? 'updating' : stale ? 'stale' : 'fresh'}
|
|
83
|
+
aria-live='polite'
|
|
84
|
+
className={stale ? 'text-right text-xs tabular-nums text-warning' : 'text-right text-xs tabular-nums text-muted-foreground'}
|
|
85
|
+
>
|
|
86
|
+
{refreshing ? 'Updating…' : stale ? `No update for ${ageLabel(age).replace(' ago', '')}` : `Updated ${ageLabel(age)}`}
|
|
87
|
+
</p>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
28
91
|
const messageOf = (error: unknown): string => {
|
|
29
92
|
if (error instanceof Error) return error.message;
|
|
30
93
|
if (typeof error === 'string') return error;
|
|
@@ -54,11 +117,20 @@ export function AsyncPanel({
|
|
|
54
117
|
emptyTitle = 'Nothing to show',
|
|
55
118
|
emptyDescription,
|
|
56
119
|
skeletonRows = 3,
|
|
120
|
+
skeleton = 'rows',
|
|
121
|
+
refreshing = false,
|
|
122
|
+
updatedAt,
|
|
123
|
+
staleAfterMs,
|
|
57
124
|
label,
|
|
58
125
|
onRetryAction,
|
|
59
126
|
children,
|
|
60
127
|
}: AsyncPanelProps) {
|
|
61
|
-
|
|
128
|
+
// STA-2: once the panel has shown its content, a later `loading` is a REFRESH — the content
|
|
129
|
+
// stays on screen. Only the first load draws the skeleton.
|
|
130
|
+
const shownContent = useRef(false);
|
|
131
|
+
if (loading && !shownContent.current) {
|
|
132
|
+
return <ContentSkeleton variant={skeleton} count={skeletonRows} />;
|
|
133
|
+
}
|
|
62
134
|
if (error) {
|
|
63
135
|
return (
|
|
64
136
|
<Alert tone='warning' {...(onRetryAction ? { action: onRetryAction } : {})}>
|
|
@@ -76,5 +148,13 @@ export function AsyncPanel({
|
|
|
76
148
|
/>
|
|
77
149
|
);
|
|
78
150
|
}
|
|
79
|
-
|
|
151
|
+
shownContent.current = true;
|
|
152
|
+
const busy = refreshing || loading === true;
|
|
153
|
+
if (!busy && updatedAt == null) return <>{children}</>;
|
|
154
|
+
return (
|
|
155
|
+
<div className='flex min-w-0 flex-col gap-2' aria-busy={busy || undefined}>
|
|
156
|
+
<Freshness refreshing={busy} updatedAt={updatedAt} staleAfterMs={staleAfterMs} />
|
|
157
|
+
{children}
|
|
158
|
+
</div>
|
|
159
|
+
);
|
|
80
160
|
}
|