basalt-ui 1.28.0 → 1.29.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/AGENTS.md +11 -18
- package/CHANGELOG.md +13 -0
- package/MIGRATING.md +151 -7
- package/README.md +23 -66
- package/agent/rules/basalt-batteries.md +59 -182
- package/agent/rules/basalt-charts.md +56 -75
- package/agent/rules/basalt-controls.md +38 -69
- package/agent/rules/basalt-mantine.md +58 -103
- package/agent/rules/basalt-state.md +42 -60
- package/agent/rules/basalt-tokens.md +46 -67
- package/agent/skills/basalt-app/SKILL.md +2 -0
- package/agent/templates/CLAUDE-block.md.tpl +14 -14
- package/configs/oxlint-plugin.js +70 -167
- package/configs/oxlint-plugin.test.ts +20 -186
- package/configs/oxlint.json +0 -1
- package/dist/agent/ai-sdk-transport.d.ts.map +1 -1
- package/dist/agent/ai-sdk-transport.js +7 -4
- package/dist/agent/coalesce.js +1 -1
- package/dist/agent/merge.js +1 -1
- package/dist/agent/transport.d.ts +13 -2
- package/dist/agent/transport.d.ts.map +1 -1
- package/dist/agent/use-agent-thread-runs.d.ts +12 -1
- package/dist/agent/use-agent-thread-runs.d.ts.map +1 -1
- package/dist/agent/use-agent-thread-runs.js +20 -6
- package/dist/agent-chat/index.d.ts +1 -1
- package/dist/agent-chat/index.d.ts.map +1 -1
- package/dist/agent-chat/relative-time.js +1 -1
- package/dist/agent-chat/thread-detail-panel.js +1 -1
- package/dist/agent-chat/thread-feed-row.d.ts +32 -7
- package/dist/agent-chat/thread-feed-row.d.ts.map +1 -1
- package/dist/agent-chat/thread-feed-row.js +121 -86
- package/dist/agent-chat/thread-feed.d.ts +11 -1
- package/dist/agent-chat/thread-feed.d.ts.map +1 -1
- package/dist/agent-chat/thread-feed.js +10 -6
- package/dist/agent-chat/thread-message.js +1 -1
- package/dist/agent-chat/virtualize.d.ts +39 -0
- package/dist/agent-chat/virtualize.d.ts.map +1 -1
- package/dist/agent-chat/virtualize.js +15 -0
- package/dist/charts/index.d.ts +3 -3
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/charts/index.js +8 -4
- package/dist/charts/kinds/BandStrip.js +1 -1
- package/dist/charts/kinds/Bars.d.ts +1 -3
- package/dist/charts/kinds/Bars.d.ts.map +1 -1
- package/dist/charts/kinds/Bars.js +1 -1
- package/dist/charts/kinds/DualPanel.d.ts +3 -1
- package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
- package/dist/charts/kinds/DualPanel.js +1 -1
- package/dist/charts/kinds/StackedArea.js +1 -1
- package/dist/charts/kinds/ZonedLine.d.ts +1 -3
- package/dist/charts/kinds/ZonedLine.d.ts.map +1 -1
- package/dist/charts/primitives/Axes.d.ts +21 -2
- package/dist/charts/primitives/Axes.d.ts.map +1 -1
- package/dist/charts/primitives/Axes.js +34 -2
- package/dist/charts/primitives/ChartCard.d.ts +20 -4
- package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
- package/dist/charts/primitives/ChartCard.js +62 -33
- package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
- package/dist/charts/primitives/ChartFrame.js +8 -1
- package/dist/charts/primitives/ChartPending.d.ts +12 -6
- package/dist/charts/primitives/ChartPending.d.ts.map +1 -1
- package/dist/charts/primitives/ChartPending.js +1 -1
- package/dist/charts/utils/format.d.ts +5 -92
- package/dist/charts/utils/format.d.ts.map +1 -1
- package/dist/charts/utils/format.js +10 -80
- package/dist/cli/audit-allows.d.ts +24 -0
- package/dist/cli/audit-allows.d.ts.map +1 -0
- package/dist/cli/audit-allows.js +240 -0
- package/dist/cli/check-theme.d.ts +10 -0
- package/dist/cli/check-theme.d.ts.map +1 -0
- package/dist/cli/check-theme.js +139 -0
- package/dist/cli/doctor.d.ts +47 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +197 -0
- package/dist/cli/guard-hook.d.ts +9 -0
- package/dist/cli/guard-hook.d.ts.map +1 -0
- package/dist/cli/guard-hook.js +93 -0
- package/dist/cli/help.d.ts +9 -0
- package/dist/cli/help.d.ts.map +1 -0
- package/dist/cli/help.js +53 -0
- package/dist/cli/index.d.ts +360 -270
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +85 -1901
- package/dist/cli/init.d.ts +4 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +165 -0
- package/dist/cli/sync.d.ts +43 -0
- package/dist/cli/sync.d.ts.map +1 -0
- package/dist/cli/sync.js +206 -0
- package/dist/cli/tokens-css.d.ts +51 -0
- package/dist/cli/tokens-css.d.ts.map +1 -0
- package/dist/cli/tokens-css.js +165 -0
- package/dist/commands/define-commands.js +1 -1
- package/dist/commands/define-overlays.js +1 -1
- package/dist/commands/index.d.ts +2 -1
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +6 -0
- package/dist/commands/overlays-mount.d.ts +7 -9
- package/dist/commands/overlays-mount.d.ts.map +1 -1
- package/dist/commands/shell-bridge.d.ts +57 -0
- package/dist/commands/shell-bridge.d.ts.map +1 -0
- package/dist/commands/shell-bridge.js +32 -0
- package/dist/common/errors.d.ts +35 -0
- package/dist/common/errors.d.ts.map +1 -1
- package/dist/common/errors.js +29 -1
- package/dist/common/index.d.ts +2 -0
- package/dist/common/index.d.ts.map +1 -1
- package/dist/common/index.js +6 -0
- package/dist/common/is-dev.d.ts.map +1 -0
- package/dist/{motion/index.d.ts → common/motion.d.ts} +1 -1
- package/dist/common/motion.d.ts.map +1 -0
- package/dist/common/props.d.ts +1 -1
- package/dist/common/props.d.ts.map +1 -1
- package/dist/common/validate.js +1 -1
- package/dist/content/markdown.d.ts +3 -4
- package/dist/content/markdown.d.ts.map +1 -1
- package/dist/content/markdown.js +1 -4
- package/dist/controls/actions.d.ts +1 -1
- package/dist/controls/actions.d.ts.map +1 -1
- package/dist/controls/panel-row.js +1 -1
- package/dist/controls/range-filter.js +1 -1
- package/dist/dashboard/delta-badge.d.ts +18 -10
- package/dist/dashboard/delta-badge.d.ts.map +1 -1
- package/dist/dashboard/delta-badge.js +33 -1
- package/dist/dashboard/empty-state.d.ts +1 -4
- package/dist/dashboard/empty-state.d.ts.map +1 -1
- package/dist/dashboard/empty-state.js +1 -2
- package/dist/dashboard/index.d.ts +4 -2
- package/dist/dashboard/index.d.ts.map +1 -1
- package/dist/dashboard/index.js +7 -1
- package/dist/dashboard/query-state.d.ts +4 -13
- package/dist/dashboard/query-state.d.ts.map +1 -1
- package/dist/dashboard/query-state.js +4 -7
- package/dist/{section → dashboard}/section.d.ts +2 -2
- package/dist/{section → dashboard}/section.d.ts.map +1 -1
- package/dist/{section → dashboard}/section.js +2 -2
- package/dist/dashboard/settings-section.d.ts +1 -1
- package/dist/dashboard/settings-section.d.ts.map +1 -1
- package/dist/dashboard/settings-section.js +1 -1
- package/dist/dashboard/stat-card.d.ts +1 -1
- package/dist/dashboard/stat-card.d.ts.map +1 -1
- package/dist/dashboard/stat-card.js +1 -1
- package/dist/dashboard/widget-header.d.ts.map +1 -0
- package/dist/data/data-table.js +2 -2
- package/dist/data/table.d.ts +1 -1
- package/dist/data/table.d.ts.map +1 -1
- package/dist/data/table.js +2 -16
- package/dist/data/virtual.d.ts +3 -3
- package/dist/dev-dock.d.ts +28 -0
- package/dist/dev-dock.d.ts.map +1 -0
- package/dist/dev-dock.js +85 -0
- package/dist/format/date.d.ts +62 -0
- package/dist/format/date.d.ts.map +1 -0
- package/dist/format/date.js +106 -0
- package/dist/format/index.d.ts +39 -0
- package/dist/format/index.d.ts.map +1 -0
- package/dist/format/index.js +54 -0
- package/dist/format/measure.d.ts +15 -0
- package/dist/format/measure.d.ts.map +1 -0
- package/dist/format/measure.js +16 -0
- package/dist/format/number.d.ts +69 -0
- package/dist/format/number.d.ts.map +1 -0
- package/dist/format/number.js +62 -0
- package/dist/format/shared.d.ts +27 -0
- package/dist/format/shared.d.ts.map +1 -0
- package/dist/format/shared.js +29 -0
- package/dist/forms/field.d.ts +4 -20
- package/dist/forms/field.d.ts.map +1 -1
- package/dist/forms/field.js +0 -4
- package/dist/forms/form-layout.d.ts +1 -1
- package/dist/forms/form-layout.d.ts.map +1 -1
- package/dist/forms/form-layout.js +1 -1
- package/dist/forms/index.d.ts +1 -1
- package/dist/forms/index.d.ts.map +1 -1
- package/dist/forms/index.js +1 -2
- package/dist/forms/use-form-submit.js +1 -1
- package/dist/guard/index.d.ts +2 -9
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +16 -26
- package/dist/guard/types.d.ts +8 -10
- package/dist/guard/types.d.ts.map +1 -1
- package/dist/index.d.ts +16 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -29
- package/dist/notifications/index.d.ts +5 -5
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +0 -2
- package/dist/notifications/mount-guard.d.ts.map +1 -1
- package/dist/notifications/mount-guard.js +2 -2
- package/dist/provider/connectivity-indicator.d.ts.map +1 -0
- package/dist/{connectivity → provider}/connectivity-provider.d.ts +1 -1
- package/dist/provider/connectivity-provider.d.ts.map +1 -0
- package/dist/{connectivity/types.d.ts → provider/connectivity-types.d.ts} +1 -1
- package/dist/provider/connectivity-types.d.ts.map +1 -0
- package/dist/provider/error-fallback.module.css +21 -0
- package/dist/provider/error-fallback.module.css.d.ts +9 -0
- package/dist/provider/index.d.ts +20 -36
- package/dist/provider/index.d.ts.map +1 -1
- package/dist/provider/index.js +42 -32
- package/dist/provider/lab-theme.js +1 -1
- package/dist/provider/use-connectivity.d.ts +3 -0
- package/dist/provider/use-connectivity.d.ts.map +1 -0
- package/dist/query-client.d.ts +63 -0
- package/dist/query-client.d.ts.map +1 -0
- package/dist/query-client.js +33 -0
- package/dist/{query/devtools.d.ts → query-devtools.d.ts} +2 -2
- package/dist/query-devtools.d.ts.map +1 -0
- package/dist/register.d.ts +17 -3
- package/dist/register.d.ts.map +1 -1
- package/dist/router-tanstack/index.d.ts +4 -7
- package/dist/router-tanstack/index.d.ts.map +1 -1
- package/dist/router-tanstack/index.js +1 -9
- package/dist/router-tanstack/nav.d.ts +1 -1
- package/dist/router-tanstack/nav.d.ts.map +1 -1
- package/dist/router-tanstack/search-store.d.ts +3 -6
- package/dist/router-tanstack/search-store.d.ts.map +1 -1
- package/dist/router-tanstack/use-nav.d.ts +1 -1
- package/dist/router-tanstack/use-nav.d.ts.map +1 -1
- package/dist/shell/app-breadcrumbs.d.ts +1 -1
- package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
- package/dist/shell/app-main.module.css +11 -0
- package/dist/shell/app-mobile-nav.d.ts +1 -1
- package/dist/shell/app-mobile-nav.d.ts.map +1 -1
- package/dist/shell/app-mobile-nav.js +24 -2
- package/dist/shell/app-sidebar.d.ts +1 -1
- package/dist/shell/app-sidebar.d.ts.map +1 -1
- package/dist/shell/app-sidebar.js +31 -2
- package/dist/shell/app-sidebar.module.css +10 -0
- package/dist/shell/index.d.ts +10 -4
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +13 -17
- package/dist/shell/mobile-nav-model.d.ts +1 -1
- package/dist/shell/mobile-nav-model.d.ts.map +1 -1
- package/dist/{nav/types.d.ts → shell/nav-types.d.ts} +1 -1
- package/dist/shell/nav-types.d.ts.map +1 -0
- package/dist/shell/page-aside.d.ts.map +1 -1
- package/dist/shell/page-aside.js +2 -29
- package/dist/shell/page-title.d.ts +14 -0
- package/dist/shell/page-title.d.ts.map +1 -0
- package/dist/shell/page-title.js +25 -0
- package/dist/shell/page-title.module.css +33 -0
- package/dist/shell/page-title.module.css.d.ts +11 -0
- package/dist/shell/sidebar-block-model.d.ts +1 -1
- package/dist/shell/sidebar-block-model.d.ts.map +1 -1
- package/dist/shell/sidebar-blocks.d.ts +1 -1
- package/dist/shell/sidebar-blocks.d.ts.map +1 -1
- package/dist/shell/use-breakpoint.d.ts +11 -0
- package/dist/shell/use-breakpoint.d.ts.map +1 -0
- package/dist/shell/use-breakpoint.js +33 -0
- package/dist/state/fields.d.ts +3 -19
- package/dist/state/fields.d.ts.map +1 -1
- package/dist/state/fields.js +6 -16
- package/dist/styles.css +36 -0
- package/dist/surfaces.d.ts +18 -41
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +46 -59
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/{theme-toggle/index.d.ts → theme/theme-toggle.d.ts} +1 -1
- package/dist/theme/theme-toggle.d.ts.map +1 -0
- package/dist/{theme-toggle/index.js → theme/theme-toggle.js} +1 -1
- package/dist/tokens/index.d.ts +1 -0
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +10 -0
- package/dist/tokens/palette.d.ts +12 -0
- package/dist/tokens/palette.d.ts.map +1 -1
- package/dist/tokens/palette.js +13 -1
- package/dist/tokens.css +1 -0
- package/llms.txt +9 -24
- package/package.json +12 -16
- package/src/agent/ai-sdk-transport.ts +11 -4
- package/src/agent/coalesce.ts +1 -1
- package/src/agent/merge.ts +1 -1
- package/src/agent/transport.ts +15 -2
- package/src/agent/use-agent-thread-runs.ts +65 -15
- package/src/agent-chat/index.ts +6 -4
- package/src/agent-chat/relative-time.ts +1 -1
- package/src/agent-chat/thread-detail-panel.tsx +1 -1
- package/src/agent-chat/thread-feed-row.tsx +105 -30
- package/src/agent-chat/thread-feed.tsx +51 -25
- package/src/agent-chat/thread-message.tsx +1 -1
- package/src/agent-chat/virtualize.ts +48 -0
- package/src/charts/index.ts +7 -10
- package/src/charts/kinds/BandStrip.tsx +1 -1
- package/src/charts/kinds/Bars.tsx +2 -5
- package/src/charts/kinds/DualPanel.tsx +4 -3
- package/src/charts/kinds/StackedArea.tsx +1 -1
- package/src/charts/kinds/ZonedLine.tsx +1 -4
- package/src/charts/primitives/Axes.tsx +49 -0
- package/src/charts/primitives/ChartCard.tsx +49 -6
- package/src/charts/primitives/ChartFrame.tsx +5 -1
- package/src/charts/primitives/ChartPending.tsx +14 -8
- package/src/charts/utils/format.ts +17 -186
- package/src/cli/audit-allows.ts +371 -0
- package/src/cli/check-theme.ts +197 -0
- package/src/cli/doctor.ts +331 -0
- package/src/cli/guard-hook.ts +115 -0
- package/src/cli/help.ts +101 -0
- package/src/cli/index.ts +269 -3439
- package/src/cli/init.ts +247 -0
- package/src/cli/sync.ts +324 -0
- package/src/cli/tokens-css.ts +252 -0
- package/src/commands/define-commands.ts +1 -1
- package/src/commands/define-overlays.ts +1 -1
- package/src/commands/index.ts +10 -1
- package/src/commands/overlays-mount.tsx +8 -10
- package/src/commands/shell-bridge.ts +86 -0
- package/src/common/errors.ts +70 -0
- package/src/common/index.ts +2 -0
- package/src/common/props.ts +1 -1
- package/src/common/validate.ts +1 -1
- package/src/content/markdown.tsx +4 -8
- package/src/controls/actions.tsx +1 -1
- package/src/controls/panel-row.tsx +1 -1
- package/src/controls/range-filter.tsx +1 -1
- package/src/dashboard/delta-badge.tsx +74 -7
- package/src/dashboard/empty-state.tsx +2 -6
- package/src/dashboard/index.ts +11 -2
- package/src/dashboard/query-state.tsx +5 -18
- package/src/{section → dashboard}/section.tsx +4 -4
- package/src/dashboard/settings-section.tsx +2 -2
- package/src/dashboard/stat-card.tsx +2 -2
- package/src/data/data-table.tsx +3 -3
- package/src/data/table.ts +6 -25
- package/src/data/virtual-list.tsx +1 -1
- package/src/data/virtual.ts +3 -3
- package/src/dev-dock.tsx +159 -0
- package/src/format/date.ts +184 -0
- package/src/format/index.ts +69 -0
- package/src/format/measure.ts +31 -0
- package/src/format/number.ts +129 -0
- package/src/format/shared.ts +63 -0
- package/src/forms/field.ts +4 -24
- package/src/forms/form-layout.tsx +2 -2
- package/src/forms/index.ts +1 -2
- package/src/forms/use-form-submit.ts +1 -1
- package/src/guard/index.ts +30 -56
- package/src/guard/types.ts +7 -10
- package/src/index.ts +38 -29
- package/src/notifications/index.ts +5 -7
- package/src/notifications/mount-guard.ts +7 -9
- package/src/{connectivity → provider}/connectivity-indicator.tsx +1 -1
- package/src/{connectivity → provider}/connectivity-provider.tsx +1 -1
- package/src/provider/error-fallback.module.css +21 -0
- package/src/provider/error-fallback.module.css.d.ts +9 -0
- package/src/provider/index.tsx +71 -71
- package/src/provider/lab-theme.ts +1 -1
- package/src/{connectivity → provider}/use-connectivity.ts +3 -3
- package/src/query-client.ts +100 -0
- package/src/{query/devtools.tsx → query-devtools.tsx} +5 -4
- package/src/register.ts +20 -4
- package/src/router-tanstack/index.ts +4 -15
- package/src/router-tanstack/nav.ts +1 -1
- package/src/router-tanstack/search-store.ts +3 -4
- package/src/router-tanstack/use-nav.ts +1 -1
- package/src/shell/app-breadcrumbs.tsx +1 -1
- package/src/shell/app-main.module.css +11 -0
- package/src/shell/app-mobile-nav.tsx +28 -1
- package/src/shell/app-sidebar.module.css +10 -0
- package/src/shell/app-sidebar.tsx +31 -2
- package/src/shell/index.tsx +23 -28
- package/src/shell/mobile-nav-model.ts +1 -1
- package/src/shell/page-aside.tsx +2 -52
- package/src/shell/page-title.module.css +33 -0
- package/src/shell/page-title.module.css.d.ts +11 -0
- package/src/shell/page-title.tsx +57 -0
- package/src/shell/sidebar-block-model.ts +1 -1
- package/src/shell/sidebar-blocks.tsx +1 -1
- package/src/shell/use-breakpoint.ts +73 -0
- package/src/state/fields.ts +7 -43
- package/src/styles.css +36 -0
- package/src/surfaces.ts +45 -60
- package/src/theme/index.ts +7 -0
- package/src/{theme-toggle/index.tsx → theme/theme-toggle.tsx} +1 -1
- package/src/tokens/index.ts +10 -0
- package/src/tokens/palette.ts +12 -0
- package/dist/connectivity/connectivity-indicator.d.ts.map +0 -1
- package/dist/connectivity/connectivity-provider.d.ts.map +0 -1
- package/dist/connectivity/index.d.ts +0 -14
- package/dist/connectivity/index.d.ts.map +0 -1
- package/dist/connectivity/index.js +0 -8
- package/dist/connectivity/types.d.ts.map +0 -1
- package/dist/connectivity/use-connectivity.d.ts +0 -3
- package/dist/connectivity/use-connectivity.d.ts.map +0 -1
- package/dist/data/index.d.ts +0 -38
- package/dist/data/index.d.ts.map +0 -1
- package/dist/data/index.js +0 -24
- package/dist/motion/index.d.ts.map +0 -1
- package/dist/nav/types.d.ts.map +0 -1
- package/dist/notifications/overlay.d.ts +0 -20
- package/dist/notifications/overlay.d.ts.map +0 -1
- package/dist/notifications/overlay.js +0 -14
- package/dist/query/devtools.d.ts.map +0 -1
- package/dist/query/error-message.d.ts +0 -41
- package/dist/query/error-message.d.ts.map +0 -1
- package/dist/query/error-message.js +0 -30
- package/dist/query/index.d.ts +0 -47
- package/dist/query/index.d.ts.map +0 -1
- package/dist/query/index.js +0 -56
- package/dist/router-tanstack/field.d.ts +0 -14
- package/dist/router-tanstack/field.d.ts.map +0 -1
- package/dist/router-tanstack/field.js +0 -4
- package/dist/router-tanstack/multi-search-param-store.d.ts +0 -43
- package/dist/router-tanstack/multi-search-param-store.d.ts.map +0 -1
- package/dist/router-tanstack/multi-search-param-store.js +0 -30
- package/dist/router-tanstack/search-param-store.d.ts +0 -42
- package/dist/router-tanstack/search-param-store.d.ts.map +0 -1
- package/dist/router-tanstack/search-param-store.js +0 -25
- package/dist/section/index.d.ts +0 -2
- package/dist/section/index.d.ts.map +0 -1
- package/dist/section/index.js +0 -4
- package/dist/theme-toggle/index.d.ts.map +0 -1
- package/dist/utils/is-dev.d.ts.map +0 -1
- package/dist/widget-header/delta-badge.d.ts +0 -19
- package/dist/widget-header/delta-badge.d.ts.map +0 -1
- package/dist/widget-header/delta-badge.js +0 -36
- package/dist/widget-header/index.d.ts +0 -3
- package/dist/widget-header/index.d.ts.map +0 -1
- package/dist/widget-header/index.js +0 -8
- package/dist/widget-header/widget-header.d.ts.map +0 -1
- package/src/connectivity/index.ts +0 -19
- package/src/data/index.ts +0 -69
- package/src/notifications/overlay.tsx +0 -66
- package/src/query/error-message.ts +0 -70
- package/src/query/index.ts +0 -97
- package/src/router-tanstack/field.ts +0 -36
- package/src/router-tanstack/multi-search-param-store.ts +0 -106
- package/src/router-tanstack/search-param-store.ts +0 -100
- package/src/section/index.ts +0 -1
- package/src/widget-header/delta-badge.tsx +0 -77
- package/src/widget-header/index.ts +0 -10
- /package/dist/{utils → common}/is-dev.d.ts +0 -0
- /package/dist/{utils → common}/is-dev.js +0 -0
- /package/dist/{motion/index.js → common/motion.js} +0 -0
- /package/dist/{section → dashboard}/section.module.css +0 -0
- /package/dist/{section → dashboard}/section.module.css.d.ts +0 -0
- /package/dist/{widget-header → dashboard}/widget-header.d.ts +0 -0
- /package/dist/{widget-header → dashboard}/widget-header.js +0 -0
- /package/dist/{widget-header → dashboard}/widget-header.module.css +0 -0
- /package/dist/{widget-header → dashboard}/widget-header.module.css.d.ts +0 -0
- /package/dist/{connectivity → provider}/connectivity-indicator.d.ts +0 -0
- /package/dist/{connectivity → provider}/connectivity-indicator.js +0 -0
- /package/dist/{connectivity → provider}/connectivity-provider.js +0 -0
- /package/dist/{connectivity/types.js → provider/connectivity-types.js} +0 -0
- /package/dist/{connectivity → provider}/use-connectivity.js +0 -0
- /package/dist/{query/devtools.js → query-devtools.js} +0 -0
- /package/dist/{nav/types.js → shell/nav-types.js} +0 -0
- /package/src/{utils → common}/is-dev.ts +0 -0
- /package/src/{motion/index.ts → common/motion.ts} +0 -0
- /package/src/{section → dashboard}/section.module.css +0 -0
- /package/src/{section → dashboard}/section.module.css.d.ts +0 -0
- /package/src/{widget-header → dashboard}/widget-header.module.css +0 -0
- /package/src/{widget-header → dashboard}/widget-header.module.css.d.ts +0 -0
- /package/src/{widget-header → dashboard}/widget-header.tsx +0 -0
- /package/src/{connectivity/types.ts → provider/connectivity-types.ts} +0 -0
- /package/src/{nav/types.ts → shell/nav-types.ts} +0 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `tokens:css` / `fonts:css` — split out of `src/cli/index.ts` (C2) so the dispatcher file holds
|
|
3
|
+
* routing only. Imports its shared plumbing (`readFrameworkVersion`, `packageRoot`, `readSource`,
|
|
4
|
+
* `readIfExists`) back from `./index`, this package's shared CLI helpers.
|
|
5
|
+
*/
|
|
6
|
+
import { mkdirSync, writeFileSync } from 'node:fs'
|
|
7
|
+
import { dirname, isAbsolute, relative, resolve } from 'node:path'
|
|
8
|
+
|
|
9
|
+
import { GENERATED_HEADER_LINE } from '../guard'
|
|
10
|
+
import { buildPaletteCss } from '../tokens'
|
|
11
|
+
import { packageRoot, readFrameworkVersion, readIfExists, readSource } from './index'
|
|
12
|
+
|
|
13
|
+
/** Read the value that follows a `--flag` in an argv slice; `undefined` when absent or terminal. */
|
|
14
|
+
function flagValue(flags: string[], name: string): string | undefined {
|
|
15
|
+
const i = flags.indexOf(name)
|
|
16
|
+
if (i === -1) return undefined
|
|
17
|
+
const value = flags[i + 1]
|
|
18
|
+
return value === undefined || value.startsWith('--') ? undefined : value
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The generated-file header: the canonical marker line (imported from the guard, which owns it),
|
|
23
|
+
* then the version + invocation that produced the file.
|
|
24
|
+
*
|
|
25
|
+
* Both lines are load-bearing, not decoration — `checkSource` only exempts a stylesheet whose line
|
|
26
|
+
* 1 is the marker verbatim AND whose line 2 parses as this provenance line. Reword either half and
|
|
27
|
+
* basalt starts reporting its own output again (116 findings in rollhook's token sheet).
|
|
28
|
+
*/
|
|
29
|
+
function generatedHeader(version: string, command: string, flags: string[]): string {
|
|
30
|
+
const shown = flags.filter((f) => f !== '--check')
|
|
31
|
+
const invocation = shown.length === 0 ? command : `${command} ${shown.join(' ')}`
|
|
32
|
+
return `${GENERATED_HEADER_LINE}\n/* basalt-ui ${version} — \`basalt-ui ${invocation}\` */\n`
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Space- and number-normalize the argument list of a legacy color function so the emitted file
|
|
37
|
+
* survives a normal repo's formatter.
|
|
38
|
+
*
|
|
39
|
+
* This is FORMATTING ONLY and deliberately the CLI's single deviation from "print exactly what
|
|
40
|
+
* `buildPaletteCss` returned": `rgba(255,255,255,0.6)` and `rgba(255, 255, 255, 0.6)` are the same
|
|
41
|
+
* colour, the shipped palette emits the first form on the dark side and the second on the light
|
|
42
|
+
* side, and a consumer committing the output ate a lint-ignore entry for the difference. The token
|
|
43
|
+
* VALUES are untouched — nothing here can change what basalt's tokens are.
|
|
44
|
+
*
|
|
45
|
+
* Trailing zeros go the same way and for the same reason: prettier rewrites `0.10` → `0.1`, so the
|
|
46
|
+
* two `rgba(28, 25, 23, 0.10)` alphas in the light shadow tokens were the whole reason the one
|
|
47
|
+
* framework-free consumer still could not lint its committed sheet — `--fix` and `tokens:css
|
|
48
|
+
* --check` disagreed forever, and the lint-ignore entry survived two releases. Only a bare decimal
|
|
49
|
+
* literal is touched (never a `%`, a `var()` or a unit), and `0.10`/`0.1` are the same number.
|
|
50
|
+
*
|
|
51
|
+
* Exported for tests, so the expectation can be computed rather than restated.
|
|
52
|
+
*/
|
|
53
|
+
export function normalizeColorFunctions(css: string): string {
|
|
54
|
+
return css.replace(/\b(rgba?|hsla?)\(([^()]*)\)/g, (_match, fn: string, args: string) => {
|
|
55
|
+
const parts = args.split(',').map((part) => normalizeCssNumber(part.trim()))
|
|
56
|
+
return `${fn}(${parts.join(', ')})`
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** `0.10` → `0.1`, `1.0` → `1`; anything that is not a bare decimal literal is returned verbatim. */
|
|
61
|
+
function normalizeCssNumber(arg: string): string {
|
|
62
|
+
if (!/^\d+\.\d+$/.test(arg)) return arg
|
|
63
|
+
return arg.replace(/\.?0+$/, '')
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** A scheme-class emission uses this sentinel attribute, then rewrites it to a class selector. */
|
|
67
|
+
const SCHEME_CLASS_SENTINEL = 'data-basalt-scheme-class'
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Write (or drift-check) a generated stylesheet. `--check` makes no writes and exits 1 when the
|
|
71
|
+
* file on disk differs from what would be emitted — the CI gate for a committed `tokens:css`
|
|
72
|
+
* artifact, mirroring `sync --check`. Without `--out` the content goes to stdout unchanged.
|
|
73
|
+
*/
|
|
74
|
+
function emitGeneratedCss(content: string, flags: string[], cwd: string, command: string): number {
|
|
75
|
+
const out = flagValue(flags, '--out')
|
|
76
|
+
const check = flags.includes('--check')
|
|
77
|
+
if (out === undefined) {
|
|
78
|
+
if (check) {
|
|
79
|
+
console.error(
|
|
80
|
+
`${command}: --check needs --out <path> — there is nothing to compare stdout to.`,
|
|
81
|
+
)
|
|
82
|
+
return 1
|
|
83
|
+
}
|
|
84
|
+
process.stdout.write(content)
|
|
85
|
+
return 0
|
|
86
|
+
}
|
|
87
|
+
const target = isAbsolute(out) ? out : resolve(cwd, out)
|
|
88
|
+
const shown = relative(cwd, target) || target
|
|
89
|
+
if (check) {
|
|
90
|
+
const onDisk = readIfExists(target)
|
|
91
|
+
if (onDisk === null) {
|
|
92
|
+
console.error(
|
|
93
|
+
`✖ ${command} --check: ${shown} does not exist — run without --check to write it.`,
|
|
94
|
+
)
|
|
95
|
+
return 1
|
|
96
|
+
}
|
|
97
|
+
if (onDisk === content) {
|
|
98
|
+
console.log(`✓ ${command} --check: ${shown} is up to date.`)
|
|
99
|
+
return 0
|
|
100
|
+
}
|
|
101
|
+
const diskLines = onDisk.split('\n')
|
|
102
|
+
const wantLines = content.split('\n')
|
|
103
|
+
console.error(
|
|
104
|
+
`✖ ${command} --check: ${shown} differs from what \`basalt-ui ${command}\` emits today ` +
|
|
105
|
+
`(on disk ${diskLines.length} lines, emitted ${wantLines.length}) — re-run the same ` +
|
|
106
|
+
'command without --check and commit the result. A basalt-ui release moves the ' +
|
|
107
|
+
'`@generated` header line, so this is byte-exact — expect a no-op commit on every upgrade ' +
|
|
108
|
+
'that ships a committed generated sheet.',
|
|
109
|
+
)
|
|
110
|
+
return 1
|
|
111
|
+
}
|
|
112
|
+
mkdirSync(dirname(target), { recursive: true })
|
|
113
|
+
writeFileSync(target, content)
|
|
114
|
+
console.log(`wrote ${content.split('\n').length} lines → ${shown}`)
|
|
115
|
+
return 0
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* tokens:css — emit the `--vx-*` stylesheet, optionally retargeted.
|
|
120
|
+
*
|
|
121
|
+
* The escape hatch from installing anything: a static site runs this once (`bunx basalt-ui
|
|
122
|
+
* tokens:css --selector-attribute data-theme --out src/tokens.css`) and consumes basalt's token
|
|
123
|
+
* system as plain CSS, with no package in its dependency tree at all. `basalt-ui/tokens.css` is the
|
|
124
|
+
* same artifact for a consumer that does install; this command exists for the ones that shouldn't
|
|
125
|
+
* have to just to change a selector.
|
|
126
|
+
*
|
|
127
|
+
* It parses flags and calls `buildPaletteCss` for the token VALUES — the CLI and the API must not
|
|
128
|
+
* be able to disagree about what basalt's tokens are. What it adds is strictly file framing for an
|
|
129
|
+
* artifact a consumer COMMITS: the `@generated` header (the guard's skip marker), a trailing
|
|
130
|
+
* newline, and `rgba()` argument spacing. All three were reported by the one framework-free
|
|
131
|
+
* consumer as the reasons the output could not simply be committed.
|
|
132
|
+
*/
|
|
133
|
+
export function tokensCss(flags: string[], cwd: string = process.cwd()): number {
|
|
134
|
+
const attribute = flagValue(flags, '--selector-attribute')
|
|
135
|
+
const schemeClass = flagValue(flags, '--selector-class')
|
|
136
|
+
const lightClass = flagValue(flags, '--light-class')
|
|
137
|
+
const darkValue = flagValue(flags, '--dark-value')
|
|
138
|
+
const lightValue = flagValue(flags, '--light-value')
|
|
139
|
+
const defaultScheme = flagValue(flags, '--default-scheme')
|
|
140
|
+
|
|
141
|
+
const only = flagValue(flags, '--only')
|
|
142
|
+
|
|
143
|
+
if (defaultScheme !== undefined && !['dark', 'light', 'none'].includes(defaultScheme)) {
|
|
144
|
+
console.error(
|
|
145
|
+
`tokens:css: --default-scheme must be dark, light or none (got '${defaultScheme}')`,
|
|
146
|
+
)
|
|
147
|
+
return 1
|
|
148
|
+
}
|
|
149
|
+
if (only !== undefined && !['core', 'all'].includes(only)) {
|
|
150
|
+
console.error(`tokens:css: --only must be core or all (got '${only}')`)
|
|
151
|
+
return 1
|
|
152
|
+
}
|
|
153
|
+
if (schemeClass !== undefined && attribute !== undefined) {
|
|
154
|
+
console.error(
|
|
155
|
+
'tokens:css: --selector-class and --selector-attribute are alternatives — pass one.',
|
|
156
|
+
)
|
|
157
|
+
return 1
|
|
158
|
+
}
|
|
159
|
+
if (schemeClass !== undefined && /[^\w-]/.test(schemeClass)) {
|
|
160
|
+
console.error(
|
|
161
|
+
`tokens:css: --selector-class must be a plain CSS class name (got '${schemeClass}')`,
|
|
162
|
+
)
|
|
163
|
+
return 1
|
|
164
|
+
}
|
|
165
|
+
if (lightClass !== undefined && /[^\w-]/.test(lightClass)) {
|
|
166
|
+
console.error(`tokens:css: --light-class must be a plain CSS class name (got '${lightClass}')`)
|
|
167
|
+
return 1
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// A class selector is Tailwind's universal dark convention (`<html class="dark">`) and was
|
|
171
|
+
// reachable before only by parking dark on the bare `:root` via --default-scheme. `buildPaletteCss`
|
|
172
|
+
// emits attribute selectors, so the class form is produced by emitting against a sentinel
|
|
173
|
+
// attribute this CLI chose itself and rewriting exactly those selectors — a rewrite of strings
|
|
174
|
+
// the command generated deterministically, never of a token value.
|
|
175
|
+
const resolvedDarkValue = schemeClass !== undefined ? schemeClass : darkValue
|
|
176
|
+
const resolvedLightValue = schemeClass !== undefined ? (lightClass ?? 'light') : lightValue
|
|
177
|
+
const resolvedAttribute = schemeClass !== undefined ? SCHEME_CLASS_SENTINEL : attribute
|
|
178
|
+
|
|
179
|
+
const scheme = {
|
|
180
|
+
...(resolvedAttribute === undefined ? {} : { attribute: resolvedAttribute }),
|
|
181
|
+
...(resolvedDarkValue === undefined ? {} : { darkValue: resolvedDarkValue }),
|
|
182
|
+
...(resolvedLightValue === undefined ? {} : { lightValue: resolvedLightValue }),
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
let css = buildPaletteCss({
|
|
186
|
+
...(Object.keys(scheme).length === 0 ? {} : { scheme }),
|
|
187
|
+
...(defaultScheme === undefined
|
|
188
|
+
? {}
|
|
189
|
+
: { defaultScheme: defaultScheme as 'dark' | 'light' | 'none' }),
|
|
190
|
+
...(flags.includes('--media-fallback') ? { mediaFallback: true } : {}),
|
|
191
|
+
...(only === undefined ? {} : { only: only as 'core' | 'all' }),
|
|
192
|
+
// The tokens-only consumer is exactly the one who writes these names by hand, so they are also
|
|
193
|
+
// the one who wants the deprecated camelCase aliases gone — and this command is their only
|
|
194
|
+
// entry point. Without the flag the CLI and the API could disagree about what basalt's tokens
|
|
195
|
+
// are, which is the one thing this command is documented never to allow.
|
|
196
|
+
...(flags.includes('--no-legacy-aliases') ? { legacyAliases: false } : {}),
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
if (schemeClass !== undefined) {
|
|
200
|
+
css = css.replace(
|
|
201
|
+
new RegExp(`\\[${SCHEME_CLASS_SENTINEL}='([\\w-]+)'\\]`, 'g'),
|
|
202
|
+
(_match, value: string) => `.${value}`,
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const version = readFrameworkVersion(packageRoot())
|
|
207
|
+
const content = `${generatedHeader(version, 'tokens:css', flags)}${normalizeColorFunctions(css)}\n`
|
|
208
|
+
return emitGeneratedCss(content, flags, cwd, 'tokens:css')
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** Every `--basalt-font-*` declaration in the shipped stylesheet, in source order. */
|
|
212
|
+
function readShippedFontDecls(pkgRoot: string): { name: string; value: string }[] {
|
|
213
|
+
const css = readSource(pkgRoot, 'dist/styles.css') ?? readSource(pkgRoot, 'src/styles.css')
|
|
214
|
+
if (css === null) return []
|
|
215
|
+
const decls: { name: string; value: string }[] = []
|
|
216
|
+
for (const match of css.matchAll(/(--basalt-font[\w-]*)\s*:\s*([^;]+);/g)) {
|
|
217
|
+
decls.push({
|
|
218
|
+
name: match[1] as string,
|
|
219
|
+
value: (match[2] as string).replace(/\s+/g, ' ').trim(),
|
|
220
|
+
})
|
|
221
|
+
}
|
|
222
|
+
return decls
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* fonts:css — emit the shipped `--basalt-font-*` stacks as plain CSS.
|
|
227
|
+
*
|
|
228
|
+
* The typeface half of the framework-free route, which had no supported path at all: `tokens.css`
|
|
229
|
+
* emits no font vars, `styles.css` is the one place the defaults live and a framework-free consumer
|
|
230
|
+
* is told not to import it, and `buildFontsCss` returns `''` unless the caller already knows the
|
|
231
|
+
* stacks. The one non-Mantine consumer lost its font identity entirely and hardcoded the stacks —
|
|
232
|
+
* which the theme guard then flagged as `raw-font-family`.
|
|
233
|
+
*
|
|
234
|
+
* The declarations are READ from the shipped stylesheet rather than restated here, so this command
|
|
235
|
+
* and `basalt-ui/styles.css` can never name different typefaces. Note the fonts themselves are
|
|
236
|
+
* `@fontsource-variable/*` packages: without them the stacks fall through to their own system
|
|
237
|
+
* fallbacks, which is the intended framework-free behaviour.
|
|
238
|
+
*/
|
|
239
|
+
export function fontsCss(flags: string[], cwd: string = process.cwd()): number {
|
|
240
|
+
const pkgRoot = packageRoot()
|
|
241
|
+
const decls = readShippedFontDecls(pkgRoot)
|
|
242
|
+
if (decls.length === 0) {
|
|
243
|
+
console.error(
|
|
244
|
+
'fonts:css: could not read the shipped font stacks from styles.css — this is a packaging ' +
|
|
245
|
+
'bug in basalt-ui, not a config error.',
|
|
246
|
+
)
|
|
247
|
+
return 1
|
|
248
|
+
}
|
|
249
|
+
const body = `:root {\n${decls.map((d) => ` ${d.name}: ${d.value};`).join('\n')}\n}\n`
|
|
250
|
+
const header = generatedHeader(readFrameworkVersion(pkgRoot), 'fonts:css', flags)
|
|
251
|
+
return emitGeneratedCss(`${header}${body}`, flags, cwd, 'fonts:css')
|
|
252
|
+
}
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
*/
|
|
25
25
|
import type { ReactNode } from 'react'
|
|
26
26
|
import type { Slot } from '../register'
|
|
27
|
-
import { isDev } from '../
|
|
27
|
+
import { isDev } from '../common/is-dev'
|
|
28
28
|
|
|
29
29
|
// ── Context ───────────────────────────────────────────────────────────────────
|
|
30
30
|
|
|
@@ -29,7 +29,7 @@ import type { ReactNode } from 'react'
|
|
|
29
29
|
import { useEffect } from 'react'
|
|
30
30
|
import { missingLayer } from '../common/errors'
|
|
31
31
|
import type { Slot } from '../register'
|
|
32
|
-
import { isDev } from '../
|
|
32
|
+
import { isDev } from '../common/is-dev'
|
|
33
33
|
|
|
34
34
|
// ── Lazy @mantine/modals resolution ───────────────────────────────────────────
|
|
35
35
|
// @mantine/modals is an OPTIONAL peer — never imported at module evaluation time, so importing
|
package/src/commands/index.ts
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* interface BasaltRegister { commands: typeof COMMANDS }
|
|
20
20
|
* }
|
|
21
21
|
*
|
|
22
|
-
* // main.tsx — mount composable overlays
|
|
22
|
+
* // main.tsx — mount composable overlays:
|
|
23
23
|
* import { BasaltOverlays } from 'basalt-ui/commands'
|
|
24
24
|
* import '@mantine/spotlight/styles.layer.css'
|
|
25
25
|
* <BasaltProvider><BasaltOverlays><App /></BasaltOverlays></BasaltProvider>
|
|
@@ -79,6 +79,15 @@ export {
|
|
|
79
79
|
type HotkeyBinding,
|
|
80
80
|
} from './projectors'
|
|
81
81
|
|
|
82
|
+
// ── Imperative shell handles (setColorScheme/toggleSidebar) ──────────────────
|
|
83
|
+
export {
|
|
84
|
+
setColorScheme,
|
|
85
|
+
toggleSidebar,
|
|
86
|
+
type ColorScheme,
|
|
87
|
+
type ColorSchemeSetter,
|
|
88
|
+
type SidebarToggle,
|
|
89
|
+
} from './shell-bridge'
|
|
90
|
+
|
|
82
91
|
// ── Shortcut format utils ─────────────────────────────────────────────────────
|
|
83
92
|
export { parseShortcut, detectMac, formatShortcutDisplay } from './shortcut-format'
|
|
84
93
|
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* overlays-mount — composable overlay mount for basalt-ui apps.
|
|
3
3
|
*
|
|
4
4
|
* `BasaltOverlays` bundles ModalsProvider, Spotlight, and Notifications into a single mount point.
|
|
5
|
-
* Put it inside BasaltProvider, before the router. It
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Put it inside BasaltProvider, before the router. It is the ONLY notifications mount — the
|
|
6
|
+
* standalone `basalt-ui/notifications` overlay it superseded is gone (C1 consolidation); mounting
|
|
7
|
+
* a second, hand-rolled `<Notifications />` alongside `<BasaltOverlays notifications />` still
|
|
8
|
+
* double-mounts the overlay and is still wrong for the same reason.
|
|
9
9
|
*
|
|
10
10
|
* Every layer — ModalsProvider included — mounts as a SIBLING of `children`, never as a wrapper:
|
|
11
11
|
* see the comment in the component body (R1). `@mantine/modals`' imperative API (`modals.open*`,
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
* code. The re-exported `openSpotlight` / `closeSpotlight` helpers delegate to this store.
|
|
34
34
|
*
|
|
35
35
|
* @example
|
|
36
|
-
* // main.tsx —
|
|
37
|
-
* //
|
|
36
|
+
* // main.tsx — mount <BasaltOverlays>. Use the layered bundle — the unlayered one outranks
|
|
37
|
+
* // basalt's `@layer basalt` styles regardless of specificity:
|
|
38
38
|
* import { BasaltOverlays } from 'basalt-ui/commands'
|
|
39
39
|
* import '@mantine/spotlight/styles.layer.css'
|
|
40
40
|
*
|
|
@@ -244,7 +244,7 @@ Object.assign(LazyNotifications, { displayName: 'LazyNotifications' })
|
|
|
244
244
|
// ── NotificationsLayer (runs the shared duplicate-mount guard, F15) ───────────
|
|
245
245
|
|
|
246
246
|
/** Wraps `LazyNotifications` so `useNotificationsMountGuard` only fires while this layer is
|
|
247
|
-
* actually enabled —
|
|
247
|
+
* actually enabled — the shared counter guards against any second `<Notifications />` mount. */
|
|
248
248
|
function NotificationsLayer({
|
|
249
249
|
notificationsProps,
|
|
250
250
|
}: {
|
|
@@ -278,9 +278,7 @@ function HotkeysMount() {
|
|
|
278
278
|
* `children`. All three layers are enabled by default; pass `false` to disable. A disabled layer's
|
|
279
279
|
* optional peer is never imported.
|
|
280
280
|
*
|
|
281
|
-
* Mount exactly ONE BasaltOverlays per app
|
|
282
|
-
* `<BasaltNotifications />` from `basalt-ui/notifications` — that would double-mount
|
|
283
|
-
* `<Notifications />`.
|
|
281
|
+
* Mount exactly ONE BasaltOverlays per app — a second instance double-mounts `<Notifications />`.
|
|
284
282
|
*
|
|
285
283
|
* @example
|
|
286
284
|
* <BasaltProvider>
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `setColorScheme`/`toggleSidebar` — imperative shell handles, wired automatically.
|
|
3
|
+
*
|
|
4
|
+
* Command bodies run OUTSIDE the React tree (a Spotlight action, a hotkey), so a `run: () =>
|
|
5
|
+
* ...` has no hook access to `useMantineColorScheme()` or `BasaltShell`'s collapse state. Every
|
|
6
|
+
* consumer of `defineCommands` therefore hand-rolled the SAME "register a live setter ref, read
|
|
7
|
+
* it later" bridge per action — argo's own `lib/color-scheme-bridge.ts` / `lib/sidebar-bridge.ts`
|
|
8
|
+
* are the seed for this file, right down to the reason each is its OWN leaf module: `commands.tsx`
|
|
9
|
+
* imports the router, which imports the generated route tree, which imports every route file
|
|
10
|
+
* including the one that owns the shell — so a command file importing anything back out of the
|
|
11
|
+
* shell closes a real circular module dependency (surfaces as TS's "implicitly has type any…
|
|
12
|
+
* referenced in its own initializer"). A leaf with ZERO local imports is what both sides can
|
|
13
|
+
* depend on without re-closing that cycle (`register.ts`'s `Slot` doc has the type-level half of
|
|
14
|
+
* the same story for `commands`↔`overlays`).
|
|
15
|
+
*
|
|
16
|
+
* basalt registers BOTH setters itself — `BasaltProvider` calls {@link registerColorSchemeSetter}
|
|
17
|
+
* from inside `MantineProvider` (`provider/index.tsx`'s `BasaltBridge`), `BasaltShell` calls
|
|
18
|
+
* {@link registerSidebarToggle} (`shell/index.tsx`'s `ShellFrame`) — so a consumer's own
|
|
19
|
+
* `commands.tsx` needs no `__root.tsx` wiring at all, unlike argo's hand-rolled version: mount
|
|
20
|
+
* `BasaltProvider`/`BasaltShell` once, and `setColorScheme`/`toggleSidebar` just work.
|
|
21
|
+
*
|
|
22
|
+
* Last-registered wins, same "last call wins" rule `defineCommands`/`defineOverlays`'s own runtime
|
|
23
|
+
* stash documents — two overlapping mounts (a route swap) are normal, not a race to guard against.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* // commands.ts
|
|
27
|
+
* import { defineCommands, setColorScheme, toggleSidebar } from 'basalt-ui/commands'
|
|
28
|
+
* export const COMMANDS = defineCommands({
|
|
29
|
+
* 'theme:dark': { label: 'Dark theme', run: () => setColorScheme('dark') },
|
|
30
|
+
* 'ui:toggle-sidebar': { label: 'Toggle sidebar', shortcut: 'Mod+B', run: () => toggleSidebar() },
|
|
31
|
+
* })
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
export type ColorScheme = 'light' | 'dark' | 'auto'
|
|
35
|
+
export type ColorSchemeSetter = (scheme: ColorScheme) => void
|
|
36
|
+
export type SidebarToggle = () => void
|
|
37
|
+
|
|
38
|
+
let colorSchemeSetterRef: ColorSchemeSetter | null = null
|
|
39
|
+
let sidebarToggleRef: SidebarToggle | null = null
|
|
40
|
+
|
|
41
|
+
/** @internal called by `BasaltProvider`. Not part of the public API surface. */
|
|
42
|
+
export function registerColorSchemeSetter(setter: ColorSchemeSetter | null): void {
|
|
43
|
+
colorSchemeSetterRef = setter
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** @internal called by `BasaltShell`. Not part of the public API surface. */
|
|
47
|
+
export function registerSidebarToggle(toggle: SidebarToggle | null): void {
|
|
48
|
+
sidebarToggleRef = toggle
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Set the color scheme from outside the React tree. A no-op (with a dev-only console warning)
|
|
53
|
+
* when no `BasaltProvider` is mounted yet — the same "not wired" shape `runCommand`/
|
|
54
|
+
* `overlays.open` already use for an unregistered id.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* setColorScheme('dark')
|
|
58
|
+
*/
|
|
59
|
+
export function setColorScheme(scheme: ColorScheme): void {
|
|
60
|
+
if (colorSchemeSetterRef === null) {
|
|
61
|
+
if (process.env['NODE_ENV'] !== 'production') {
|
|
62
|
+
// eslint-disable-next-line no-console -- dev-only diagnostic, mirrors runCommand's own
|
|
63
|
+
console.warn('[basalt] setColorScheme: no BasaltProvider is mounted yet')
|
|
64
|
+
}
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
colorSchemeSetterRef(scheme)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Toggle `BasaltShell`'s desktop sidebar collapse from outside the React tree. A no-op (with a
|
|
72
|
+
* dev-only console warning) when no `BasaltShell` is mounted yet.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* toggleSidebar()
|
|
76
|
+
*/
|
|
77
|
+
export function toggleSidebar(): void {
|
|
78
|
+
if (sidebarToggleRef === null) {
|
|
79
|
+
if (process.env['NODE_ENV'] !== 'production') {
|
|
80
|
+
// eslint-disable-next-line no-console -- dev-only diagnostic, mirrors runCommand's own
|
|
81
|
+
console.warn('[basalt] toggleSidebar: no BasaltShell is mounted yet')
|
|
82
|
+
}
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
sidebarToggleRef()
|
|
86
|
+
}
|
package/src/common/errors.ts
CHANGED
|
@@ -71,3 +71,73 @@ export function duplicateMount(component: string): string {
|
|
|
71
71
|
'more than one instance is mounted at once — mount exactly one, at the app root.',
|
|
72
72
|
)
|
|
73
73
|
}
|
|
74
|
+
|
|
75
|
+
// ── Transport-agnostic error decoding ────────────────────────────────────────────────────────────
|
|
76
|
+
//
|
|
77
|
+
// Deliberately in this module, with NO `@tanstack/react-query` import — `dashboard/query-state.tsx`
|
|
78
|
+
// renders server messages and must not drag the query peer into the root barrel's graph to do it.
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The HTTP status behind a thrown envelope (Eden Treaty's `{ status, value }`, a `Response`-shaped
|
|
82
|
+
* throw), when there is one. Use it to BRANCH — 404 → "not found" copy — rather than to build a
|
|
83
|
+
* message; `toErrorMessage` already folds the status into its text when the body says nothing.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* if (errorStatus(err) === 404) return <EmptyState title="Gone" />
|
|
87
|
+
*/
|
|
88
|
+
export function errorStatus(err: unknown): number | undefined {
|
|
89
|
+
if (err === null || typeof err !== 'object') return undefined
|
|
90
|
+
const status = (err as { status?: unknown }).status
|
|
91
|
+
return typeof status === 'number' ? status : undefined
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Strings that technically decoded but tell a reader nothing. `JSON.stringify` produces most. */
|
|
95
|
+
const UNUSABLE_MESSAGES = new Set(['', '{}', '[]', 'null', 'undefined', '[object Object]'])
|
|
96
|
+
|
|
97
|
+
function decodeError(err: unknown): string {
|
|
98
|
+
if (err instanceof Error) return err.message
|
|
99
|
+
if (typeof err === 'string') return err
|
|
100
|
+
if (typeof err === 'object' && err !== null) {
|
|
101
|
+
const obj = err as Record<string, unknown>
|
|
102
|
+
if (typeof obj['message'] === 'string') return obj['message']
|
|
103
|
+
// Eden-style { value: ... } or { status, value } envelopes — unwrap one level.
|
|
104
|
+
if ('value' in obj) return decodeError(obj['value'])
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
return JSON.stringify(err) ?? ''
|
|
108
|
+
} catch {
|
|
109
|
+
return String(err)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Extract a human-readable message from an unknown thrown value (e.g. the raw Eden envelope
|
|
115
|
+
* thrown by `unwrap`). Removes the 8-line three-branch boilerplate consumers duplicate.
|
|
116
|
+
*
|
|
117
|
+
* Resolution order:
|
|
118
|
+
* 1. `Error` instance → err.message
|
|
119
|
+
* 2. `{ message: string }` → message (Eden / Elysia error shape)
|
|
120
|
+
* 3. `{ value: ... }` → recursively (Eden nested value envelope)
|
|
121
|
+
* 4. plain string → itself
|
|
122
|
+
* 5. anything else → `JSON.stringify`
|
|
123
|
+
*
|
|
124
|
+
* **If that yields nothing a human can read** (`''`, `'{}'`, `'[]'`, `'null'`, `'undefined'`,
|
|
125
|
+
* `'[object Object]'` — the shapes `JSON.stringify` produces for an opaque envelope) the `fallback`
|
|
126
|
+
* is returned instead, with `(HTTP <status>)` appended when the envelope carries one. Without that
|
|
127
|
+
* guard the caller renders a literal `{}` at the user, or — for `toErrorMessage(undefined)` —
|
|
128
|
+
* the actual value `undefined`, since `JSON.stringify(undefined)` is not a string at all.
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* try {
|
|
132
|
+
* await unwrap(api.resource.post({ body }))
|
|
133
|
+
* } catch (e) {
|
|
134
|
+
* toast(toErrorMessage(e, 'Could not create share')) // → "slug already in use"
|
|
135
|
+
* // → "Could not create share (HTTP 502)"
|
|
136
|
+
* }
|
|
137
|
+
*/
|
|
138
|
+
export function toErrorMessage(err: unknown, fallback = 'The request failed.'): string {
|
|
139
|
+
const decoded = decodeError(err).trim()
|
|
140
|
+
if (!UNUSABLE_MESSAGES.has(decoded)) return decoded
|
|
141
|
+
const status = errorStatus(err)
|
|
142
|
+
return status === undefined ? fallback : `${fallback} (HTTP ${status})`
|
|
143
|
+
}
|
package/src/common/index.ts
CHANGED
|
@@ -19,3 +19,5 @@ export {
|
|
|
19
19
|
} from './errors'
|
|
20
20
|
export { useValidateProps, assertRequiredProps, resetValidatedProps } from './validate'
|
|
21
21
|
export { scrollParentOf, SCROLLPORT_ATTRIBUTE } from './scroll-parent'
|
|
22
|
+
export { isDev } from './is-dev'
|
|
23
|
+
export { MOTION_DURATION, MOTION_EASE_STANDARD, MOTION_SPRING } from './motion'
|
package/src/common/props.ts
CHANGED
|
@@ -60,7 +60,7 @@ export type ToneWithNeutral = Tone | 'neutral'
|
|
|
60
60
|
* twice — `WidgetHeaderTier` stays the name on the public barrel; `Tier` is what `common`-aware
|
|
61
61
|
* code spells. Type-only, so this file emits no runtime edge into the Mantine-coupled half.
|
|
62
62
|
*/
|
|
63
|
-
export type { WidgetHeaderTier as Tier } from '../
|
|
63
|
+
export type { WidgetHeaderTier as Tier } from '../dashboard/widget-header'
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
66
|
* The class joiner. Replaces the `[a, b].filter(Boolean).join(' ')` spelled out in seven files
|
package/src/common/validate.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* mean warns once in dev and renders on (`useValidateProps`).
|
|
8
8
|
*/
|
|
9
9
|
import { useEffect } from 'react'
|
|
10
|
-
import { isDev } from '
|
|
10
|
+
import { isDev } from './is-dev'
|
|
11
11
|
import { requiredProp } from './errors'
|
|
12
12
|
|
|
13
13
|
/** A check returns the message(s) it has, or nothing. `null`/`undefined` entries are ignored. */
|
package/src/content/markdown.tsx
CHANGED
|
@@ -101,10 +101,9 @@ export type MarkdownProps = {
|
|
|
101
101
|
* default to same-origin `['/']`; `'trusted'` (your own authored content) keeps
|
|
102
102
|
* `['https://', '/']`. An explicit `allowedImagePrefixes` always wins over both.
|
|
103
103
|
*
|
|
104
|
-
* Default: `'
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
* must pin `contentTrust="untrusted"` explicitly rather than lean on `streaming`.
|
|
104
|
+
* Default: `'trusted'`. Settledness has nothing to do with trust: a FINISHED agent message is
|
|
105
|
+
* still model-generated, so any surface rendering agent text must pin `contentTrust="untrusted"`
|
|
106
|
+
* explicitly rather than lean on `streaming`.
|
|
108
107
|
*/
|
|
109
108
|
readonly contentTrust?: MarkdownContentTrust
|
|
110
109
|
/**
|
|
@@ -618,10 +617,7 @@ export function Markdown({
|
|
|
618
617
|
density = 'article',
|
|
619
618
|
measure,
|
|
620
619
|
streaming = false,
|
|
621
|
-
|
|
622
|
-
// read straight off `streaming`, so dropping it would silently widen every existing streaming
|
|
623
|
-
// call site). It is a floor, never the policy — see the prop's JSDoc.
|
|
624
|
-
contentTrust = streaming ? 'untrusted' : 'trusted',
|
|
620
|
+
contentTrust = 'trusted',
|
|
625
621
|
components,
|
|
626
622
|
fenceRenderers,
|
|
627
623
|
sanitizeSchema,
|
package/src/controls/actions.tsx
CHANGED
|
@@ -16,7 +16,7 @@ import { ActionIcon, Button, Group, Menu } from '@mantine/core'
|
|
|
16
16
|
import { createContext, isValidElement, useContext, useEffect } from 'react'
|
|
17
17
|
import type { ReactNode } from 'react'
|
|
18
18
|
import type { BasaltProps } from '../common/props'
|
|
19
|
-
import type { NavAnchor } from '../nav
|
|
19
|
+
import type { NavAnchor } from '../shell/nav-types'
|
|
20
20
|
import type { AnyNavLink } from '../router-tanstack/nav'
|
|
21
21
|
import { IconSlot } from '../theme/icon-slot'
|
|
22
22
|
import { ControlGroup } from './control-group'
|
|
@@ -28,7 +28,7 @@ import type { ReactNode, RefObject } from 'react'
|
|
|
28
28
|
import { cx } from '../common/props'
|
|
29
29
|
import type { BasaltProps } from '../common/props'
|
|
30
30
|
import { CtlSlot } from '../theme'
|
|
31
|
-
import { InfoGlyph } from '../
|
|
31
|
+
import { InfoGlyph } from '../dashboard/widget-header'
|
|
32
32
|
import classes from './panel-row.module.css'
|
|
33
33
|
|
|
34
34
|
/**
|
|
@@ -32,7 +32,7 @@ import { useEffect, useRef, useState } from 'react'
|
|
|
32
32
|
import type { ComponentType, ReactNode } from 'react'
|
|
33
33
|
import type { BasaltProps } from '../common/props'
|
|
34
34
|
import { assertRequiredProps } from '../common/validate'
|
|
35
|
-
import { isDev } from '../
|
|
35
|
+
import { isDev } from '../common/is-dev'
|
|
36
36
|
import type { FieldHandle, RangeField, RangeValue } from '../state'
|
|
37
37
|
import classes from './controls.module.css'
|
|
38
38
|
import { useFilterRegistration, useFilterSurface } from './filter-context'
|