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
|
@@ -8,7 +8,7 @@ paths:
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
<!-- basalt:coverage -->
|
|
11
|
-
<!-- GENERATED from src/surfaces.ts — `
|
|
11
|
+
<!-- GENERATED from src/surfaces.ts — `bun scripts/check-coverage.ts --write`. Do not hand-edit. -->
|
|
12
12
|
<!-- backed by: guard kinds — css-raw-surface, inline-font-size, off-identity-accent, off-system-surface-var, raw-color-fn, raw-font-family, raw-hex, raw-radius, raw-surface, surface-shadow-override, theme-allow-unscoped · oxlint rules — basalt/no-raw-font-size, basalt/raw-size-literal, basalt/token-layer-boundary -->
|
|
13
13
|
<!-- not guarded: — -->
|
|
14
14
|
<!-- /basalt:coverage -->
|
|
@@ -32,10 +32,9 @@ The accent is **split by ROLE**, and this is the one thing an author gets wrong:
|
|
|
32
32
|
control always reads white on either page. **Never fill with `VX.accent`.**
|
|
33
33
|
|
|
34
34
|
**The palette is DERIVED, not authored** (`tokens/derive.ts` from one seed + five bounded knobs), and
|
|
35
|
-
a consumer retunes it through `createBasaltTheme(overrides?, { derive, fonts, radius, density })
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
theme; read the numbers, when you genuinely need them, off `tokens/palette.ts`.
|
|
35
|
+
a consumer retunes it through `createBasaltTheme(overrides?, { derive, fonts, radius, density })` —
|
|
36
|
+
no hex, shade index or pixel constant belongs in a rule, doc or component. Read values off `VX.*`
|
|
37
|
+
and the theme; read raw numbers, if you need them, off `tokens/palette.ts`.
|
|
39
38
|
|
|
40
39
|
## Color — never a raw literal
|
|
41
40
|
|
|
@@ -44,28 +43,26 @@ theme; read the numbers, when you genuinely need them, off `tokens/palette.ts`.
|
|
|
44
43
|
in non-component files) or through the Mantine theme (`color` / `c` / `bg`, `theme.colors`).
|
|
45
44
|
- **Opacity is `alpha(token, a)`** (`color-mix`), never `rgba()` — so the hue keeps resolving per
|
|
46
45
|
scheme.
|
|
47
|
-
- **No off-identity Mantine accents
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
one fixed swatch that is identical in both schemes, so the shade that is legible on dark is the one
|
|
53
|
-
that fails on light. Use `VX.status.*` for a verdict, or the bare hue name and let the theme pick.
|
|
46
|
+
- **No off-identity Mantine accents** — allowed: **`blue`** (the earned hue), **`gray`**, and the
|
|
47
|
+
status names `red`/`green`/`orange`/`yellow`. A positive delta is `color="green"`, never `teal`.
|
|
48
|
+
- **Never pin a shade index** (`c="yellow.7"`, `var(--mantine-color-red-6)`) — one fixed swatch is
|
|
49
|
+
identical in both schemes, so what's legible on dark fails on light. Use `VX.status.*` or the
|
|
50
|
+
bare hue name.
|
|
54
51
|
- **"Ink earns its color."** Default to neutral. A hue is justified by trend, signal/status, or
|
|
55
52
|
genuine multi-series separation. The accent lands on the single primary CTA per view, focus rings,
|
|
56
53
|
links and small status pops — never on borders, large fills, routine icons or secondary buttons.
|
|
57
54
|
- **Categorical/series color is consumer data**, declared once through `defineSeries` →
|
|
58
|
-
`seriesTokens`/`groupTokens` and wired into `buildPaletteCss`, never a Mantine accent prop
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
a mismatch emits refs to variables the stylesheet never declares, with no tsc error and no
|
|
63
|
-
runtime throw — just unstyled charts.
|
|
55
|
+
`seriesTokens`/`groupTokens` and wired into `buildPaletteCss`, never a Mantine accent prop.
|
|
56
|
+
`groupTokens(GROUP, MAP)` assumes the map reaches `BasaltProvider`'s `paletteOptions.groups`
|
|
57
|
+
under the key `` `${GROUP}-` `` — **with the trailing dash**. Derive both from one `GROUP`
|
|
58
|
+
constant; a mismatch emits refs to variables the stylesheet never declares, silently.
|
|
64
59
|
|
|
65
60
|
## Surfaces — one collapsed token set
|
|
66
61
|
|
|
67
62
|
`VX.surface` is exactly: `bg` · `panel` · `panelHover` · `elevated` · `subtle` · `overlay` ·
|
|
68
|
-
`field` · `border` · `hairline`.
|
|
63
|
+
`field` · `border` · `hairline`. `VX.divider` is NOT a surface — the one seam/line token (every
|
|
64
|
+
`AppShell` region edge + between-rows rule); `hairline` is the card ring, `border` the
|
|
65
|
+
control/overlay line — neither is ever a seam.
|
|
69
66
|
|
|
70
67
|
The theme **collapses Mantine's raw ramp steps onto those tokens** (`cssVariablesResolver` + theme
|
|
71
68
|
`styles`), so every Mantine component — AppShell, Table, Input, Divider, Tabs, Popover, Accordion,
|
|
@@ -79,13 +76,12 @@ and never reach for a raw ramp-step var (`var(--mantine-color-gray-N)`).
|
|
|
79
76
|
the integer that happens to equal `md` today; `radius="sm"`, never a numeric radius.
|
|
80
77
|
- **Card radius has one source**, `VX.radiusCard` / `--vx-radius-card`: the Mantine chrome and the
|
|
81
78
|
Mantine-free `ChartCard` resolve to the same token. Cards must never diverge.
|
|
82
|
-
- **Sub-scale micro-spacing is legitimate and allowed raw** —
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
- **Icons size through the icon's own `size` prop**, which is not spacing.
|
|
79
|
+
- **Sub-scale micro-spacing is legitimate and allowed raw** — below the scale's first step, use the
|
|
80
|
+
raw number rather than inventing a micro-token. A CSS-module declaration whose every literal is
|
|
81
|
+
micro does not fire; one non-micro value makes it a finding, and an inline style OBJECT in TSX
|
|
82
|
+
still fires (there the Mantine prop existed).
|
|
83
|
+
- **Dense by default** — reach for the tighter step. Icons size through their own `size` prop,
|
|
84
|
+
which is not spacing.
|
|
89
85
|
|
|
90
86
|
## Type
|
|
91
87
|
|
|
@@ -113,48 +109,31 @@ An id is a guard kind (`raw-surface`, `inline-spacing`, …) or an oxlint plugin
|
|
|
113
109
|
<Widget style={{ borderRadius: 3 }} />
|
|
114
110
|
```
|
|
115
111
|
|
|
116
|
-
- **An annotation must START its comment**
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
they exist to prevent. The agent-chat guards honour `basalt-agent-allow`, a separate token that
|
|
133
|
-
never waives a `theme-allow` finding, or the reverse.
|
|
134
|
-
|
|
135
|
-
Unsupported shapes are asserted as unsupported in both engines' test grids (`src/guard/
|
|
136
|
-
check-source.test.ts`, `configs/oxlint-plugin.test.ts`), so "unsupported" and "silently broken"
|
|
137
|
-
cannot read the same. **Audit them**: `basalt-ui check-theme --audit-allows` re-runs the scan with each waiver
|
|
138
|
-
neutralized, one at a time, and **exits 1 on a waiver that suppresses nothing** — a dead waiver
|
|
139
|
-
silently covers the next real finding on that line. It judges plugin-rule waivers too, by re-running
|
|
140
|
-
oxlint; where oxlint is unreachable the verdict is "cannot judge", never "dead". Its scope line is
|
|
141
|
-
part of the report: `0 dead` is not `0 dead anywhere`.
|
|
112
|
+
- **An annotation must START its comment** (`//`, `/*`, `<!--`, a `*` gutter, or bare whitespace) —
|
|
113
|
+
a mention mid-prose waives nothing.
|
|
114
|
+
- **File scope is spelled, never inferred**: `theme-allow-file <id>… — <why>`, anywhere in the
|
|
115
|
+
file; a bare one waives nothing.
|
|
116
|
+
- **A typo waives nothing** — an unrecognized id is recorded unknown, the rest of the annotation
|
|
117
|
+
still covers the ids it got right.
|
|
118
|
+
- **Three placements agree in both engines**: the reported line, a comment-only line directly
|
|
119
|
+
above it, and (CSS) a trailing annotation reaching back over the declaration it terminates. A
|
|
120
|
+
blank line ends the reach.
|
|
121
|
+
- **JSON/`.webmanifest` take a member key** — first remedy is `basaltAppPlugin`, not a waiver.
|
|
122
|
+
- **The three import-boundary rules honour no escape at all**; the agent-chat guards honour
|
|
123
|
+
`basalt-agent-allow`, a separate token that never waives a `theme-allow` finding, or the reverse.
|
|
124
|
+
|
|
125
|
+
**Audit them**: `basalt-ui check-theme --audit-allows` re-runs the scan with each waiver
|
|
126
|
+
neutralized and **exits 1 on a waiver that suppresses nothing**; it judges plugin-rule waivers too
|
|
127
|
+
by re-running oxlint. Scope is part of the report — `0 dead` is not `0 dead anywhere`.
|
|
142
128
|
|
|
143
129
|
## Wiring the guard
|
|
144
130
|
|
|
145
131
|
`check-theme` reads `package.json`'s `"basalt"` key (`roots`, `exempt`, `include`, `profile`,
|
|
146
|
-
`severity`, `exemptRules
|
|
147
|
-
`src` relative to cwd
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
exception; a palette-definition file belongs in `exempt`, not behind a comment.
|
|
155
|
-
|
|
156
|
-
**The guard sees palette, not vocabulary.** A fork written by a token-fluent author uses exactly the
|
|
157
|
-
right tokens, so no palette guard can see it — `basalt/shadow-basalt-export` catches an exact name
|
|
158
|
-
collision and nothing else, and renaming the fork is what a fork's author does anyway. It is a
|
|
159
|
-
tripwire, not coverage. The habit is the control: **check whether basalt already ships the thing
|
|
160
|
-
before building it.**
|
|
132
|
+
`severity`, `exemptRules`). **`roots` is not optional in practice** — omitted, it falls back to
|
|
133
|
+
`src` relative to cwd (zero files under `apps/*/src`); `init` writes a real one. Wire it as
|
|
134
|
+
`oxlint . && basalt-ui check-theme` — what `init` seeds as `lint:basalt`.
|
|
135
|
+
|
|
136
|
+
**When it fires, fix the source** — the right token first; a palette-definition file belongs in
|
|
137
|
+
`exempt`, not behind a comment. **The guard sees palette, not vocabulary** — a token-fluent fork
|
|
138
|
+
uses exactly the right tokens, so `basalt/shadow-basalt-export` (an exact name collision) is a
|
|
139
|
+
tripwire, not coverage. Check whether basalt already ships the thing before building it.
|
|
@@ -36,6 +36,8 @@ own `.oxlintrc.json` costs.
|
|
|
36
36
|
```tsx
|
|
37
37
|
// main.tsx — CSS order is load-bearing: every @mantine/*/styles.layer.css, THEN basalt's
|
|
38
38
|
import '@mantine/core/styles.layer.css'
|
|
39
|
+
import '@mantine/notifications/styles.layer.css' // one per MOUNTED battery: notifications, spotlight…
|
|
40
|
+
import '@mantine/spotlight/styles.layer.css'
|
|
39
41
|
import 'basalt-ui/styles.css'
|
|
40
42
|
import { BasaltProvider, createBasaltTheme } from 'basalt-ui'
|
|
41
43
|
import { BasaltOverlays } from 'basalt-ui/commands'
|
|
@@ -2,32 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
## basalt-ui (managed — do not hand-edit)
|
|
4
4
|
|
|
5
|
-
Placed by `bunx basalt-ui init
|
|
6
|
-
|
|
7
|
-
changes here are overwritten on the next sync.
|
|
5
|
+
Placed by `bunx basalt-ui init`, refreshed by `basalt-ui sync` (`sync --check` gates drift in CI).
|
|
6
|
+
Framework-owned: edit `DESIGN.md` or your own files — this block is overwritten on the next sync.
|
|
8
7
|
|
|
9
|
-
**Stack:** React 19 + Mantine v9
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
**Stack:** React 19 + Mantine v9 themed by `basalt-ui` (`BasaltProvider` + `createBasaltTheme`), Bun,
|
|
9
|
+
oxlint + oxfmt (no ESLint/Biome/Prettier), no Tailwind. Color is the three-tier `--vx-*` token system —
|
|
10
|
+
`VX.*` or a series token, never a raw hex. Charts are visx via `basalt-ui/charts`; `check-theme` + the
|
|
11
|
+
`basalt/*` oxlint rules are the teeth.
|
|
13
12
|
|
|
14
13
|
**Precedence — the only statement of it.** Highest wins; a lower layer fills gaps and never
|
|
15
14
|
overrides a higher one:
|
|
16
15
|
|
|
17
16
|
> consumer `DESIGN.md` (app deltas) > the six shipped `basalt-*` rules > the `basalt-*` skills
|
|
18
17
|
|
|
19
|
-
**All six rules are law** — `basalt-tokens
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
(`/basalt-app`, `/basalt-design`, `/basalt-charts`) are the METHOD that obeys them, never law.
|
|
18
|
+
**All six rules are law** — `basalt-{tokens,mantine,charts,state,controls,batteries}` in
|
|
19
|
+
`.claude/rules/`, each with a generated `<!-- basalt:coverage -->` header naming what enforces it and
|
|
20
|
+
what is only advisory. The skills (`/basalt-app`, `/basalt-design`, `/basalt-charts`) are METHOD, never law.
|
|
23
21
|
|
|
24
22
|
**Run the LOCAL bin, not `bunx`** — `./node_modules/.bin/basalt-ui`, or a `package.json` script.
|
|
25
23
|
`bunx` does not re-resolve a cached package, so it can answer for a version you upgraded away from;
|
|
26
24
|
`init` is the one legitimate `bunx` invocation, because nothing is installed yet.
|
|
27
25
|
|
|
28
|
-
**Before guessing an import, read the installed package's
|
|
29
|
-
|
|
30
|
-
depends on basalt, not the repo root; `basalt-ui doctor`'s `basalt-resolves` line prints where.
|
|
26
|
+
**Before guessing an import, read the installed package's `llms.txt` and `AGENTS.md`** — at the
|
|
27
|
+
install directory (in a workspace, under the depending package; `basalt-ui doctor` prints where).
|
|
31
28
|
|
|
32
29
|
@./DESIGN.md
|
|
33
30
|
|
|
@@ -37,4 +34,7 @@ noise/grain and dramatic motion. Here: the shipped three-font system, depth from
|
|
|
37
34
|
with a 1px ring, neutral-by-default with the single accent spent only when earned — trend, signal,
|
|
38
35
|
or genuine categorical separation. Restraint **is** the identity.
|
|
39
36
|
|
|
37
|
+
**Chart-doctrine override.** A global `visx-charts` rule auto-loading on `**/charts/**` is generic
|
|
38
|
+
non-basalt discipline; here `basalt-charts` supersedes it — compose `CartesianChart`.
|
|
39
|
+
|
|
40
40
|
<!-- basalt:end -->
|
package/configs/oxlint-plugin.js
CHANGED
|
@@ -80,16 +80,30 @@ import { fileURLToPath } from 'node:url'
|
|
|
80
80
|
|
|
81
81
|
// ── Shared helpers ──────────────────────────────────────────────────────────────────────────────
|
|
82
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Guard kinds and plugin rules RETIRED — kept recognized by `KNOWN_RULE_IDS` (below) and the
|
|
85
|
+
* guard's own `PLUGIN_RULE_IDS` copy so an existing `theme-allow <id>` reads as a dead waiver via
|
|
86
|
+
* `check-theme --audit-allows`, never as a typo.
|
|
87
|
+
*
|
|
88
|
+
* - `unframed-chart` (regex: a `<ChartLegend items={[...]}>` array literal) →
|
|
89
|
+
* `basalt/chart-legend-literal`, which also catches the `.map()`-over-a-non-series form the
|
|
90
|
+
* regex never could reach.
|
|
91
|
+
* - `duplicate-notifications-mount` — its only case, `<BasaltNotifications />` mounted beside
|
|
92
|
+
* `<BasaltOverlays notifications>`, is unreachable now that `BasaltNotifications` no longer
|
|
93
|
+
* exists as a standalone export; the rule had nothing left to fire on.
|
|
94
|
+
*/
|
|
95
|
+
export const RETIRED_RULE_IDS = new Set(['unframed-chart', 'duplicate-notifications-mount'])
|
|
96
|
+
|
|
83
97
|
/**
|
|
84
98
|
* Rule ids a `theme-allow` may name — this plugin's own rules plus `src/guard`'s kinds. Duplicated
|
|
85
99
|
* by hand for the same reason `majorOf` is: this file loads standalone out of a consumer's
|
|
86
100
|
* node_modules and must not import from the package.
|
|
87
101
|
*
|
|
88
102
|
* Exported, and `oxlint-plugin.test.ts` asserts it is EXACTLY the plugin's own rule ids plus
|
|
89
|
-
* `GUARD_RULES`'s keys
|
|
90
|
-
* not know is treated as a typo, so the annotation naming it waives
|
|
91
|
-
* {@link parseThemeAllow}); before the fail-closed change it was worse still, silently
|
|
92
|
-
* a blanket waiver.
|
|
103
|
+
* `GUARD_RULES`'s keys plus `RETIRED_RULE_IDS`. Forgetting an entry here is not a cosmetic
|
|
104
|
+
* omission — an id this set does not know is treated as a typo, so the annotation naming it waives
|
|
105
|
+
* nothing (see {@link parseThemeAllow}); before the fail-closed change it was worse still, silently
|
|
106
|
+
* widening to a blanket waiver.
|
|
93
107
|
*/
|
|
94
108
|
export const KNOWN_RULE_IDS = new Set([
|
|
95
109
|
// this plugin
|
|
@@ -111,7 +125,6 @@ export const KNOWN_RULE_IDS = new Set([
|
|
|
111
125
|
'search-literal-link',
|
|
112
126
|
'use-search-from-literal',
|
|
113
127
|
'provider-above-router',
|
|
114
|
-
'duplicate-notifications-mount',
|
|
115
128
|
'query-dual-import',
|
|
116
129
|
'query-fn-unwrap',
|
|
117
130
|
'deprecated-export',
|
|
@@ -121,6 +134,7 @@ export const KNOWN_RULE_IDS = new Set([
|
|
|
121
134
|
'visx-boundary',
|
|
122
135
|
'visx-tooltip',
|
|
123
136
|
'token-layer-boundary',
|
|
137
|
+
'no-import-meta-env',
|
|
124
138
|
// These three honour `basalt-agent-allow`, never `theme-allow` — but they are real ids, so an
|
|
125
139
|
// annotation naming one must parse as a (useless) scoped annotation rather than as prose.
|
|
126
140
|
'agent-resume-guard',
|
|
@@ -142,7 +156,6 @@ export const KNOWN_RULE_IDS = new Set([
|
|
|
142
156
|
'inline-display',
|
|
143
157
|
'raw-visx-axis',
|
|
144
158
|
'raw-motion-value',
|
|
145
|
-
'unframed-chart',
|
|
146
159
|
'chart-missing-aria-label',
|
|
147
160
|
'raw-form-control',
|
|
148
161
|
'sub-16-input-font',
|
|
@@ -154,6 +167,9 @@ export const KNOWN_RULE_IDS = new Set([
|
|
|
154
167
|
'hidden-inline-style',
|
|
155
168
|
'in-body-page-title',
|
|
156
169
|
'raw-selection-control',
|
|
170
|
+
// Retired guard kinds — no longer in GUARD_RULES, but a `theme-allow` naming one must still
|
|
171
|
+
// parse as a real (dead) waiver rather than an unknown-id typo. See RETIRED_RULE_IDS above.
|
|
172
|
+
...RETIRED_RULE_IDS,
|
|
157
173
|
])
|
|
158
174
|
|
|
159
175
|
const ALLOW_RULE_TOKEN = /^(?:basalt\/)?([a-z][a-z0-9-]*)(?=$|[\s,:—–])/
|
|
@@ -3338,125 +3354,6 @@ const providerAboveRouter = {
|
|
|
3338
3354
|
},
|
|
3339
3355
|
}
|
|
3340
3356
|
|
|
3341
|
-
// ── Rule 26 — duplicate-notifications-mount ─────────────────────────────────────────────────────
|
|
3342
|
-
|
|
3343
|
-
const DUPLICATE_NOTIFICATIONS_MOUNT_MESSAGE =
|
|
3344
|
-
'<BasaltNotifications /> mounted alongside <BasaltOverlays notifications> — the two double-mount ' +
|
|
3345
|
-
"Mantine's <Notifications>, so a single notify() renders twice. BasaltOverlays already composes " +
|
|
3346
|
-
'the notifications layer; drop the standalone mount, or pass `notifications={false}` to ' +
|
|
3347
|
-
'BasaltOverlays if the standalone one is the mount you want. ' +
|
|
3348
|
-
'(basalt/duplicate-notifications-mount)'
|
|
3349
|
-
|
|
3350
|
-
/**
|
|
3351
|
-
* The ancestor chain from `node` up to its `Program`, OUTERMOST first — captured during the visit,
|
|
3352
|
-
* because `node.parent` is only walkable then, and compared at `Program:exit`. Unlike
|
|
3353
|
-
* {@link ancestorTagBindings} it keeps the NODES, which is what makes a nearest-common-ancestor
|
|
3354
|
-
* question answerable at all.
|
|
3355
|
-
*/
|
|
3356
|
-
function ancestorChain(node) {
|
|
3357
|
-
const chain = []
|
|
3358
|
-
let current = node
|
|
3359
|
-
for (let depth = 0; current !== null && current !== undefined; depth++) {
|
|
3360
|
-
if (depth > ANCESTRY_MAX_DEPTH) break
|
|
3361
|
-
chain.push(current)
|
|
3362
|
-
if (current.type === 'Program') break
|
|
3363
|
-
current = current.parent
|
|
3364
|
-
}
|
|
3365
|
-
return chain.toReversed()
|
|
3366
|
-
}
|
|
3367
|
-
|
|
3368
|
-
/**
|
|
3369
|
-
* True when two nodes sit in DIFFERENT branches of ONE conditional — `a ? <X/> : <Y/>`, or the two
|
|
3370
|
-
* operands of one `&&`/`||`. Exactly one of the pair ever renders, so a double-mount rule firing on
|
|
3371
|
-
* them would be reporting a mount that cannot happen.
|
|
3372
|
-
*
|
|
3373
|
-
* Deliberately the NEAREST common ancestor only, and deliberately expression-level: two separate
|
|
3374
|
-
* `cond && <X/>` expressions under one parent are NOT exclusive (both conditions can hold), and an
|
|
3375
|
-
* `if`/`return` split across statements is a control-flow question this does not pretend to answer.
|
|
3376
|
-
* The runtime guard in `src/commands/overlays-mount.tsx` is the lane that counts real mounts.
|
|
3377
|
-
*/
|
|
3378
|
-
function mutuallyExclusive(chainA, chainB) {
|
|
3379
|
-
let i = 0
|
|
3380
|
-
while (i < chainA.length && i < chainB.length && chainA[i] === chainB[i]) i++
|
|
3381
|
-
const ancestor = chainA[i - 1]
|
|
3382
|
-
const branchA = chainA[i]
|
|
3383
|
-
const branchB = chainB[i]
|
|
3384
|
-
if (ancestor === undefined || branchA === undefined || branchB === undefined) return false
|
|
3385
|
-
const [first, second] =
|
|
3386
|
-
ancestor.type === 'ConditionalExpression'
|
|
3387
|
-
? [ancestor.consequent, ancestor.alternate]
|
|
3388
|
-
: ancestor.type === 'LogicalExpression'
|
|
3389
|
-
? [ancestor.left, ancestor.right]
|
|
3390
|
-
: [undefined, undefined]
|
|
3391
|
-
if (first === undefined || second === undefined) return false
|
|
3392
|
-
return (branchA === first && branchB === second) || (branchA === second && branchB === first)
|
|
3393
|
-
}
|
|
3394
|
-
|
|
3395
|
-
/**
|
|
3396
|
-
* `<BasaltOverlays notifications>` and `<BasaltNotifications />` in ONE file — F5, the second
|
|
3397
|
-
* `basalt-mantine.md` law with no guard, and the static half of a warning that already exists at
|
|
3398
|
-
* runtime (`src/commands/overlays-mount.tsx` warns in dev when both mount).
|
|
3399
|
-
*
|
|
3400
|
-
* A pair split across the two branches of one conditional is EXEMPT: `a ? <BasaltNotifications/> :
|
|
3401
|
-
* <BasaltOverlays/>` is a file with two mounts written and one mount rendered, and reporting it
|
|
3402
|
-
* taught a consumer to reach for a waiver over code that was already right.
|
|
3403
|
-
*
|
|
3404
|
-
* `notifications` DEFAULTS to true on `BasaltOverlays`, so the absence of the attribute counts as
|
|
3405
|
-
* enabled and only an explicit `notifications={false}` clears it — the same polarity the component
|
|
3406
|
-
* itself reads. Both tags are provenance-gated through {@link collectBasaltImportMap}.
|
|
3407
|
-
*
|
|
3408
|
-
* Same-FILE only, and deliberately so: the runtime warning is the lane that sees a cross-file
|
|
3409
|
-
* double mount, because it counts real mounts rather than JSX. This is the cheap half that catches
|
|
3410
|
-
* it before the app runs, which is the whole reason a static twin is worth having.
|
|
3411
|
-
*/
|
|
3412
|
-
// Ships: warn (grace → 1.30.0)
|
|
3413
|
-
const duplicateNotificationsMount = {
|
|
3414
|
-
meta: {
|
|
3415
|
-
type: 'suggestion',
|
|
3416
|
-
docs: {
|
|
3417
|
-
description: 'Warn when BasaltOverlays and a standalone BasaltNotifications both mount.',
|
|
3418
|
-
},
|
|
3419
|
-
schema: [],
|
|
3420
|
-
},
|
|
3421
|
-
create(context) {
|
|
3422
|
-
if (isTestFile(context)) return {}
|
|
3423
|
-
const ownTree = isBasaltOwnSource(getFilename(context))
|
|
3424
|
-
const basalt = new Map()
|
|
3425
|
-
const mounts = []
|
|
3426
|
-
|
|
3427
|
-
return {
|
|
3428
|
-
ImportDeclaration(node) {
|
|
3429
|
-
collectBasaltImportMap(node, basalt, ownTree)
|
|
3430
|
-
},
|
|
3431
|
-
JSXOpeningElement(node) {
|
|
3432
|
-
const root = jsxRootName(node.name)
|
|
3433
|
-
if (root === undefined || !basalt.has(root)) return
|
|
3434
|
-
// `notifications` DEFAULTS to true, so only an explicit `={false}` clears the layer.
|
|
3435
|
-
const disabled = (node.attributes ?? []).some(
|
|
3436
|
-
(attr) =>
|
|
3437
|
-
attr.type === 'JSXAttribute' &&
|
|
3438
|
-
attr.name?.name === 'notifications' &&
|
|
3439
|
-
unwrapExpressionContainer(attr.value)?.value === false,
|
|
3440
|
-
)
|
|
3441
|
-
mounts.push({ node, root, disabled, chain: ancestorChain(node) })
|
|
3442
|
-
},
|
|
3443
|
-
'Program:exit'() {
|
|
3444
|
-
const overlays = mounts.filter(
|
|
3445
|
-
({ root, disabled }) => basalt.get(root) === 'BasaltOverlays' && !disabled,
|
|
3446
|
-
)
|
|
3447
|
-
if (overlays.length === 0) return
|
|
3448
|
-
for (const { node, root, chain } of mounts) {
|
|
3449
|
-
if (basalt.get(root) !== 'BasaltNotifications') continue
|
|
3450
|
-
// Every enabled overlays mount on the other branch of one conditional → nothing to report.
|
|
3451
|
-
if (overlays.every((overlay) => mutuallyExclusive(overlay.chain, chain))) continue
|
|
3452
|
-
if (hasThemeAllow(context, node, 'duplicate-notifications-mount')) continue
|
|
3453
|
-
context.report({ node, message: DUPLICATE_NOTIFICATIONS_MOUNT_MESSAGE })
|
|
3454
|
-
}
|
|
3455
|
-
},
|
|
3456
|
-
}
|
|
3457
|
-
},
|
|
3458
|
-
}
|
|
3459
|
-
|
|
3460
3357
|
// ── Rule 27 — query-dual-import ─────────────────────────────────────────────────────────────────
|
|
3461
3358
|
|
|
3462
3359
|
/** `@tanstack/react-query` and its subpaths — never `@tanstack/react-query-devtools`. */
|
|
@@ -3643,36 +3540,8 @@ const queryFnUnwrap = {
|
|
|
3643
3540
|
* @type {readonly DeprecatedExport[]}
|
|
3644
3541
|
*/
|
|
3645
3542
|
export const DEPRECATED_EXPORTS = [
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
name: 'field',
|
|
3649
|
-
replacement: 'inputProps',
|
|
3650
|
-
removeIn: '1.29.0',
|
|
3651
|
-
// Not a drop-in: `inputProps` no longer returns `key`, so a rename alone would silently drop
|
|
3652
|
-
// the reconciler key. The nudge stays; the rewrite is `basalt/forms-field-key`'s job.
|
|
3653
|
-
fix: false,
|
|
3654
|
-
},
|
|
3655
|
-
{
|
|
3656
|
-
subpath: 'basalt-ui',
|
|
3657
|
-
name: 'BasaltProvider',
|
|
3658
|
-
prop: 'sseUrl',
|
|
3659
|
-
replacement: 'connectivity={{ sseUrl }}',
|
|
3660
|
-
removeIn: '1.29.0',
|
|
3661
|
-
},
|
|
3662
|
-
{
|
|
3663
|
-
subpath: 'basalt-ui',
|
|
3664
|
-
name: 'BasaltProvider',
|
|
3665
|
-
prop: 'healthUrl',
|
|
3666
|
-
replacement: 'connectivity={{ healthUrl }}',
|
|
3667
|
-
removeIn: '1.29.0',
|
|
3668
|
-
},
|
|
3669
|
-
{
|
|
3670
|
-
subpath: 'basalt-ui',
|
|
3671
|
-
name: 'BasaltProvider',
|
|
3672
|
-
prop: 'healthIntervalMs',
|
|
3673
|
-
replacement: 'connectivity={{ healthIntervalMs }}',
|
|
3674
|
-
removeIn: '1.29.0',
|
|
3675
|
-
},
|
|
3543
|
+
// Empty since 1.29.0: every 1.28.x deprecation was removed on schedule. The next
|
|
3544
|
+
// deprecation adds a row here in the same commit as its @deprecated JSDoc and MIGRATING entry.
|
|
3676
3545
|
]
|
|
3677
3546
|
|
|
3678
3547
|
function deprecatedExportMessage(row) {
|
|
@@ -3914,6 +3783,51 @@ const formsFieldKey = {
|
|
|
3914
3783
|
},
|
|
3915
3784
|
}
|
|
3916
3785
|
|
|
3786
|
+
// ── Rule 31 — no-import-meta-env ────────────────────────────────────────────────────────────────
|
|
3787
|
+
|
|
3788
|
+
const NO_IMPORT_META_ENV_MESSAGE =
|
|
3789
|
+
'import.meta.env is banned in shipped basalt-ui source — this package is consumed both as a ' +
|
|
3790
|
+
'Vite-bundled dep AND resolved directly by a consumer whose own bundler may define no ' +
|
|
3791
|
+
'import.meta.env at all (basaltViteConfig defines process.env.NODE_ENV precisely so basalt ' +
|
|
3792
|
+
'code never needs it). Use process.env.NODE_ENV instead. (basalt/no-import-meta-env)'
|
|
3793
|
+
|
|
3794
|
+
/**
|
|
3795
|
+
* `import.meta.env` — a MemberExpression whose object is the `import.meta` MetaProperty and whose
|
|
3796
|
+
* property is `env`. Repo-local only: a CONSUMER's own app code legitimately uses
|
|
3797
|
+
* `import.meta.env.VITE_*` for its own config, so this must never reach the shipped preset — only
|
|
3798
|
+
* `/.oxlintrc.json` wires it, scoped to `packages/basalt-ui/src/**` the same way the Mantine-free
|
|
3799
|
+
* boundaries above are scoped, rather than through path logic in the rule itself (unlike
|
|
3800
|
+
* `token-layer-boundary`, which every package resolves the plugin from and so has to self-scope).
|
|
3801
|
+
*/
|
|
3802
|
+
// Ships: repo-local only
|
|
3803
|
+
const noImportMetaEnv = {
|
|
3804
|
+
meta: {
|
|
3805
|
+
type: 'suggestion',
|
|
3806
|
+
docs: {
|
|
3807
|
+
description: 'Disallow import.meta.env — basalt-ui source must use process.env.NODE_ENV.',
|
|
3808
|
+
},
|
|
3809
|
+
schema: [],
|
|
3810
|
+
},
|
|
3811
|
+
create(context) {
|
|
3812
|
+
if (isTestFile(context)) return {}
|
|
3813
|
+
|
|
3814
|
+
return {
|
|
3815
|
+
MemberExpression(node) {
|
|
3816
|
+
const object = node.object
|
|
3817
|
+
if (
|
|
3818
|
+
object?.type !== 'MetaProperty' ||
|
|
3819
|
+
object.meta?.name !== 'import' ||
|
|
3820
|
+
object.property?.name !== 'meta'
|
|
3821
|
+
)
|
|
3822
|
+
return
|
|
3823
|
+
if (node.computed || node.property?.name !== 'env') return
|
|
3824
|
+
if (hasThemeAllow(context, node, 'no-import-meta-env')) return
|
|
3825
|
+
context.report({ node, message: NO_IMPORT_META_ENV_MESSAGE })
|
|
3826
|
+
},
|
|
3827
|
+
}
|
|
3828
|
+
},
|
|
3829
|
+
}
|
|
3830
|
+
|
|
3917
3831
|
// ── Grace ledger ────────────────────────────────────────────────────────────────────────────────
|
|
3918
3832
|
|
|
3919
3833
|
/**
|
|
@@ -3990,17 +3904,6 @@ export const PLUGIN_RULE_GRACE = {
|
|
|
3990
3904
|
'image-gen and the playground is measured — every one of them mounts a router, so the ' +
|
|
3991
3905
|
'false-positive question has a real sample.',
|
|
3992
3906
|
},
|
|
3993
|
-
'duplicate-notifications-mount': {
|
|
3994
|
-
since: '1.28.0',
|
|
3995
|
-
promote: '1.30.0',
|
|
3996
|
-
why:
|
|
3997
|
-
'F5 — the static half of a double-mount the runtime already warns about in dev ' +
|
|
3998
|
-
'(`src/commands/overlays-mount.tsx`). Same-file only, so like its sibling it guards a ' +
|
|
3999
|
-
'fraction of the law and cannot claim `error`. It also reads a prop DEFAULT (`notifications` ' +
|
|
4000
|
-
'is true when unwritten), which is the kind of polarity a consumer disagrees with exactly ' +
|
|
4001
|
-
'once before it is worth knowing about — a minor at `warn` is how that disagreement arrives ' +
|
|
4002
|
-
'as feedback rather than as a broken build.',
|
|
4003
|
-
},
|
|
4004
3907
|
'query-dual-import': {
|
|
4005
3908
|
since: '1.28.0',
|
|
4006
3909
|
promote: '1.30.0',
|
|
@@ -4097,7 +4000,6 @@ export default {
|
|
|
4097
4000
|
'search-literal-link': searchLiteralLink,
|
|
4098
4001
|
'use-search-from-literal': useSearchFromLiteral,
|
|
4099
4002
|
'provider-above-router': providerAboveRouter,
|
|
4100
|
-
'duplicate-notifications-mount': duplicateNotificationsMount,
|
|
4101
4003
|
'query-dual-import': queryDualImport,
|
|
4102
4004
|
'query-fn-unwrap': queryFnUnwrap,
|
|
4103
4005
|
'deprecated-export': deprecatedExport,
|
|
@@ -4108,5 +4010,6 @@ export default {
|
|
|
4108
4010
|
'agent-resume-guard': agentResumeGuard,
|
|
4109
4011
|
'agent-no-raw-usechat': agentNoRawUseChat,
|
|
4110
4012
|
'ai-sdk-major': aiSdkMajor,
|
|
4013
|
+
'no-import-meta-env': noImportMetaEnv,
|
|
4111
4014
|
},
|
|
4112
4015
|
}
|