basalt-ui 1.25.0 → 1.27.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 +8 -6
- package/CHANGELOG.md +26 -0
- package/MIGRATING.md +815 -23
- package/README.md +45 -32
- package/agent/rules/basalt-batteries.md +205 -0
- package/agent/rules/basalt-charts.md +125 -555
- package/agent/rules/basalt-controls.md +160 -0
- package/agent/rules/basalt-mantine.md +146 -439
- package/agent/rules/basalt-state.md +118 -96
- package/agent/rules/basalt-tokens.md +126 -288
- package/agent/skills/basalt-app/SKILL.md +70 -195
- package/agent/skills/basalt-charts/SKILL.md +54 -234
- package/agent/skills/basalt-design/SKILL.md +56 -248
- package/agent/templates/CLAUDE-block.md.tpl +31 -40
- package/agent/templates/DESIGN.md.tpl +22 -51
- package/configs/oxlint-plugin.js +1465 -36
- package/configs/oxlint-plugin.test.ts +1190 -11
- package/configs/oxlint.json +11 -3
- package/dist/charts/cursor/resolve.d.ts +18 -0
- package/dist/charts/cursor/resolve.d.ts.map +1 -1
- package/dist/charts/cursor/resolve.js +15 -6
- package/dist/charts/cursor/scope.d.ts +6 -3
- package/dist/charts/cursor/scope.d.ts.map +1 -1
- package/dist/charts/cursor/store.d.ts +5 -1
- package/dist/charts/cursor/store.d.ts.map +1 -1
- package/dist/charts/cursor/store.js +4 -4
- package/dist/charts/hooks/useChartCursor.d.ts.map +1 -1
- package/dist/charts/hooks/useChartCursor.js +9 -8
- package/dist/charts/index.d.ts +2 -2
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/charts/primitives/ChartCard.d.ts +34 -9
- package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
- package/dist/charts/primitives/ChartCard.js +29 -114
- package/dist/charts/primitives/ChartCard.test.d.ts +2 -0
- package/dist/charts/primitives/ChartCard.test.d.ts.map +1 -0
- package/dist/charts/sparklines/BarSparkline.d.ts +26 -2
- package/dist/charts/sparklines/BarSparkline.d.ts.map +1 -1
- package/dist/charts/sparklines/BarSparkline.js +18 -4
- package/dist/cli/check-coverage.test.d.ts +2 -0
- package/dist/cli/check-coverage.test.d.ts.map +1 -0
- package/dist/cli/index.d.ts +67 -4
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +225 -7
- package/dist/connectivity/connectivity-indicator.d.ts +4 -1
- package/dist/connectivity/connectivity-indicator.d.ts.map +1 -1
- package/dist/connectivity/connectivity-indicator.js +1 -1
- package/dist/content/index.d.ts +5 -3
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +4 -2
- package/dist/content/prose.module.css +7 -28
- package/dist/controls/actions.d.ts +169 -0
- package/dist/controls/actions.d.ts.map +1 -0
- package/dist/controls/actions.js +287 -0
- package/dist/controls/actions.module.css +62 -0
- package/dist/controls/actions.module.css.d.ts +13 -0
- package/dist/controls/actions.test.d.ts +2 -0
- package/dist/controls/actions.test.d.ts.map +1 -0
- package/dist/controls/actions.type-guard.test.d.ts +2 -0
- package/dist/controls/actions.type-guard.test.d.ts.map +1 -0
- package/dist/controls/compare-filter.d.ts +41 -0
- package/dist/controls/compare-filter.d.ts.map +1 -0
- package/dist/controls/compare-filter.js +23 -0
- package/dist/controls/control-group.d.ts +19 -0
- package/dist/controls/control-group.d.ts.map +1 -0
- package/dist/controls/control-group.js +20 -0
- package/dist/controls/control-group.test.d.ts +2 -0
- package/dist/controls/control-group.test.d.ts.map +1 -0
- package/dist/controls/controls.module.css +542 -0
- package/dist/controls/controls.module.css.d.ts +35 -0
- package/dist/controls/controls.router.test.d.ts +2 -0
- package/dist/controls/controls.router.test.d.ts.map +1 -0
- package/dist/controls/enum-filter.d.ts +39 -0
- package/dist/controls/enum-filter.d.ts.map +1 -0
- package/dist/controls/enum-filter.js +93 -0
- package/dist/controls/filter-context.d.ts +60 -0
- package/dist/controls/filter-context.d.ts.map +1 -0
- package/dist/controls/filter-context.js +69 -0
- package/dist/controls/filter-pill.d.ts +28 -0
- package/dist/controls/filter-pill.d.ts.map +1 -0
- package/dist/controls/filter-pill.js +70 -0
- package/dist/controls/filter-set.d.ts +13 -0
- package/dist/controls/filter-set.d.ts.map +1 -0
- package/dist/controls/filter-set.js +125 -0
- package/dist/controls/filter-set.test.d.ts +2 -0
- package/dist/controls/filter-set.test.d.ts.map +1 -0
- package/dist/controls/filter-sheet.d.ts +117 -0
- package/dist/controls/filter-sheet.d.ts.map +1 -0
- package/dist/controls/filter-sheet.js +146 -0
- package/dist/controls/glyphs.d.ts +27 -0
- package/dist/controls/glyphs.d.ts.map +1 -0
- package/dist/controls/glyphs.js +49 -0
- package/dist/controls/index.d.ts +20 -0
- package/dist/controls/index.d.ts.map +1 -0
- package/dist/controls/index.js +46 -0
- package/dist/controls/multi-select-filter.d.ts +23 -0
- package/dist/controls/multi-select-filter.d.ts.map +1 -0
- package/dist/controls/multi-select-filter.js +89 -0
- package/dist/controls/number-filter.d.ts +26 -0
- package/dist/controls/number-filter.d.ts.map +1 -0
- package/dist/controls/number-filter.js +143 -0
- package/dist/controls/peer-boundary.test.d.ts +2 -0
- package/dist/controls/peer-boundary.test.d.ts.map +1 -0
- package/dist/controls/props-contract.test.d.ts +2 -0
- package/dist/controls/props-contract.test.d.ts.map +1 -0
- package/dist/controls/range-filter.d.ts +43 -0
- package/dist/controls/range-filter.d.ts.map +1 -0
- package/dist/controls/range-filter.js +135 -0
- package/dist/controls/range-filter.type-guard.test.d.ts +2 -0
- package/dist/controls/range-filter.type-guard.test.d.ts.map +1 -0
- package/dist/controls/search-filter.d.ts +10 -0
- package/dist/controls/search-filter.d.ts.map +1 -0
- package/dist/controls/search-filter.js +81 -0
- package/dist/controls/select-filter.d.ts +74 -0
- package/dist/controls/select-filter.d.ts.map +1 -0
- package/dist/controls/select-filter.js +10 -0
- package/dist/controls/sync-button.d.ts +38 -0
- package/dist/controls/sync-button.d.ts.map +1 -0
- package/dist/controls/sync-button.js +119 -0
- package/dist/controls/sync-button.module.css +63 -0
- package/dist/controls/sync-button.module.css.d.ts +11 -0
- package/dist/controls/sync-button.test.d.ts +2 -0
- package/dist/controls/sync-button.test.d.ts.map +1 -0
- package/dist/controls/toggle-filter.d.ts +9 -0
- package/dist/controls/toggle-filter.d.ts.map +1 -0
- package/dist/controls/toggle-filter.js +42 -0
- package/dist/controls/view-tabs.d.ts +23 -0
- package/dist/controls/view-tabs.d.ts.map +1 -0
- package/dist/controls/view-tabs.js +59 -0
- package/dist/controls-dates/date-range-picker.d.ts +4 -0
- package/dist/controls-dates/date-range-picker.d.ts.map +1 -0
- package/dist/controls-dates/date-range-picker.js +18 -0
- package/dist/controls-dates/index.d.ts +9 -0
- package/dist/controls-dates/index.d.ts.map +1 -0
- package/dist/controls-dates/index.js +4 -0
- package/dist/dashboard/delta-badge.d.ts +6 -12
- package/dist/dashboard/delta-badge.d.ts.map +1 -1
- package/dist/dashboard/delta-badge.js +1 -50
- package/dist/dashboard/index.d.ts +1 -1
- package/dist/dashboard/index.d.ts.map +1 -1
- package/dist/dashboard/index.js +3 -1
- package/dist/dashboard/settings-section.d.ts +6 -4
- package/dist/dashboard/settings-section.d.ts.map +1 -1
- package/dist/dashboard/settings-section.js +23 -16
- package/dist/dashboard/settings-section.module.css +10 -0
- package/dist/dashboard/settings-section.module.css.d.ts +1 -0
- package/dist/dashboard/settings-section.test.d.ts +2 -0
- package/dist/dashboard/settings-section.test.d.ts.map +1 -0
- package/dist/dashboard/stat-card.d.ts +95 -9
- package/dist/dashboard/stat-card.d.ts.map +1 -1
- package/dist/dashboard/stat-card.js +108 -49
- package/dist/dashboard/stat-card.module.css +153 -0
- package/dist/dashboard/stat-card.module.css.d.ts +15 -0
- package/dist/data/data-table.d.ts +37 -15
- package/dist/data/data-table.d.ts.map +1 -1
- package/dist/data/data-table.js +96 -58
- package/dist/guard/grace.test.d.ts +20 -0
- package/dist/guard/grace.test.d.ts.map +1 -0
- package/dist/guard/index.d.ts +66 -3
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +78 -7
- package/dist/guard/types.d.ts +3 -3
- package/dist/guard/types.d.ts.map +1 -1
- package/dist/index.d.ts +13 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -10
- package/dist/nav/types.d.ts +78 -2
- package/dist/nav/types.d.ts.map +1 -1
- package/dist/notifications/bell.d.ts +4 -1
- package/dist/notifications/bell.d.ts.map +1 -1
- package/dist/notifications/index.d.ts +1 -1
- package/dist/router-tanstack/field.d.ts +14 -0
- package/dist/router-tanstack/field.d.ts.map +1 -0
- package/dist/router-tanstack/field.js +4 -0
- package/dist/router-tanstack/index.d.ts +6 -2
- package/dist/router-tanstack/index.d.ts.map +1 -1
- package/dist/router-tanstack/index.js +4 -0
- package/dist/router-tanstack/multi-search-param-store.d.ts +4 -52
- package/dist/router-tanstack/multi-search-param-store.d.ts.map +1 -1
- package/dist/router-tanstack/multi-search-param-store.js +19 -27
- package/dist/router-tanstack/search-param-store.d.ts +3 -87
- package/dist/router-tanstack/search-param-store.d.ts.map +1 -1
- package/dist/router-tanstack/search-param-store.js +16 -45
- package/dist/router-tanstack/search-store.d.ts +84 -0
- package/dist/router-tanstack/search-store.d.ts.map +1 -0
- package/dist/router-tanstack/search-store.js +271 -0
- package/dist/router-tanstack/search-store.router.test.d.ts +2 -0
- package/dist/router-tanstack/search-store.router.test.d.ts.map +1 -0
- package/dist/router-tanstack/search-store.test.d.ts +2 -0
- package/dist/router-tanstack/search-store.test.d.ts.map +1 -0
- package/dist/router-tanstack/use-nav.d.ts +0 -2
- package/dist/router-tanstack/use-nav.d.ts.map +1 -1
- package/dist/router-tanstack/use-nav.js +34 -12
- package/dist/router-tanstack/use-nav.test.d.ts +2 -0
- package/dist/router-tanstack/use-nav.test.d.ts.map +1 -0
- package/dist/section/index.d.ts +2 -0
- package/dist/section/index.d.ts.map +1 -0
- package/dist/section/index.js +4 -0
- package/dist/section/section.d.ts +24 -0
- package/dist/section/section.d.ts.map +1 -0
- package/dist/section/section.js +126 -0
- package/dist/section/section.module.css +104 -0
- package/dist/section/section.module.css.d.ts +11 -0
- package/dist/section/section.test.d.ts +2 -0
- package/dist/section/section.test.d.ts.map +1 -0
- package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
- package/dist/shell/app-breadcrumbs.js +18 -15
- package/dist/shell/app-header.module.css +77 -37
- package/dist/shell/app-header.module.css.d.ts +2 -1
- package/dist/shell/app-mobile-nav.d.ts +9 -2
- package/dist/shell/app-mobile-nav.d.ts.map +1 -1
- package/dist/shell/app-mobile-nav.js +98 -11
- package/dist/shell/app-mobile-nav.module.css +8 -0
- package/dist/shell/app-mobile-nav.module.css.d.ts +1 -0
- package/dist/shell/app-sidebar.d.ts +55 -28
- package/dist/shell/app-sidebar.d.ts.map +1 -1
- package/dist/shell/app-sidebar.js +116 -67
- package/dist/shell/app-sidebar.module.css +303 -20
- package/dist/shell/app-sidebar.module.css.d.ts +19 -1
- package/dist/shell/index.d.ts +55 -21
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +40 -18
- package/dist/shell/mobile-nav-model.d.ts +13 -2
- package/dist/shell/mobile-nav-model.d.ts.map +1 -1
- package/dist/shell/mobile-nav-model.js +15 -1
- package/dist/shell/nav-count-badge.d.ts +1 -1
- package/dist/shell/page-bar.d.ts +51 -0
- package/dist/shell/page-bar.d.ts.map +1 -0
- package/dist/shell/page-bar.js +154 -0
- package/dist/shell/page-bar.module.css +178 -0
- package/dist/shell/page-bar.module.css.d.ts +17 -0
- package/dist/shell/page-bar.test.d.ts +2 -0
- package/dist/shell/page-bar.test.d.ts.map +1 -0
- package/dist/shell/sidebar-block-model.d.ts +48 -0
- package/dist/shell/sidebar-block-model.d.ts.map +1 -0
- package/dist/shell/sidebar-block-model.js +39 -0
- package/dist/shell/sidebar-blocks.d.ts +32 -0
- package/dist/shell/sidebar-blocks.d.ts.map +1 -0
- package/dist/shell/sidebar-blocks.js +169 -0
- package/dist/shell/sidebar-search.d.ts +13 -1
- package/dist/shell/sidebar-search.d.ts.map +1 -1
- package/dist/shell/sidebar-search.js +34 -1
- package/dist/shell/sidebar-search.module.css +26 -0
- package/dist/shell/sidebar-search.module.css.d.ts +2 -0
- package/dist/state/fields.d.ts +565 -0
- package/dist/state/fields.d.ts.map +1 -0
- package/dist/state/fields.js +405 -0
- package/dist/state/fields.test.d.ts +2 -0
- package/dist/state/fields.test.d.ts.map +1 -0
- package/dist/state/persisted.d.ts +60 -0
- package/dist/state/persisted.d.ts.map +1 -0
- package/dist/state/persisted.js +123 -0
- package/dist/state/persisted.test.d.ts +2 -0
- package/dist/state/persisted.test.d.ts.map +1 -0
- package/dist/state.d.ts +11 -56
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +14 -120
- package/dist/styles.css +13 -0
- package/dist/surfaces.d.ts +106 -24
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +156 -19
- package/dist/theme/controls.module.css +16 -1
- package/dist/theme/ctl-theme.d.ts +32 -0
- package/dist/theme/ctl-theme.d.ts.map +1 -0
- package/dist/theme/ctl-theme.js +54 -0
- package/dist/theme/ctl-theme.test.d.ts +2 -0
- package/dist/theme/ctl-theme.test.d.ts.map +1 -0
- package/dist/theme/ctl-tier-coverage.test.d.ts +2 -0
- package/dist/theme/ctl-tier-coverage.test.d.ts.map +1 -0
- package/dist/theme/divider-law.test.d.ts +2 -0
- package/dist/theme/divider-law.test.d.ts.map +1 -0
- package/dist/theme/icon-slot.d.ts +44 -0
- package/dist/theme/icon-slot.d.ts.map +1 -0
- package/dist/theme/icon-slot.js +21 -0
- package/dist/theme/icon-slot.module.css +47 -0
- package/dist/theme/icon-slot.module.css.d.ts +8 -0
- package/dist/theme/icon-slot.test.d.ts +2 -0
- package/dist/theme/icon-slot.test.d.ts.map +1 -0
- package/dist/theme/index.d.ts +1 -0
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +82 -5
- package/dist/theme/layout-rhythm-css.test.d.ts +2 -0
- package/dist/theme/layout-rhythm-css.test.d.ts.map +1 -0
- package/dist/theme/segmented-control.module.css +20 -0
- package/dist/theme/shadow-surfaces.test.d.ts.map +1 -1
- package/dist/tokens/index.d.ts +12 -0
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +38 -8
- package/dist/tokens/palette.d.ts +97 -61
- package/dist/tokens/palette.d.ts.map +1 -1
- package/dist/tokens/palette.js +76 -38
- package/dist/tokens.css +10 -2
- package/dist/widget-header/delta-badge.d.ts +13 -0
- package/dist/widget-header/delta-badge.d.ts.map +1 -0
- package/dist/widget-header/delta-badge.js +29 -0
- package/dist/widget-header/delta-badge.test.d.ts +2 -0
- package/dist/widget-header/delta-badge.test.d.ts.map +1 -0
- package/dist/widget-header/index.d.ts +3 -0
- package/dist/widget-header/index.d.ts.map +1 -0
- package/dist/widget-header/index.js +6 -0
- package/dist/widget-header/no-mantine.test.d.ts +2 -0
- package/dist/widget-header/no-mantine.test.d.ts.map +1 -0
- package/dist/widget-header/widget-header.d.ts +67 -0
- package/dist/widget-header/widget-header.d.ts.map +1 -0
- package/dist/widget-header/widget-header.js +89 -0
- package/dist/widget-header/widget-header.module.css +350 -0
- package/dist/widget-header/widget-header.module.css.d.ts +25 -0
- package/dist/widget-header/widget-header.test.d.ts +2 -0
- package/dist/widget-header/widget-header.test.d.ts.map +1 -0
- package/llms.txt +30 -17
- package/package.json +14 -1
- package/src/charts/cursor/resolve.test.ts +29 -1
- package/src/charts/cursor/resolve.ts +38 -6
- package/src/charts/cursor/scope.tsx +6 -3
- package/src/charts/cursor/store.test.ts +19 -1
- package/src/charts/cursor/store.ts +10 -5
- package/src/charts/hooks/useChartCursor.ts +16 -8
- package/src/charts/index.ts +2 -2
- package/src/charts/primitives/CartesianChart.test.tsx +124 -0
- package/src/charts/primitives/ChartCard.test.tsx +66 -0
- package/src/charts/primitives/ChartCard.tsx +86 -146
- package/src/charts/sparklines/BarSparkline.tsx +43 -5
- package/src/cli/check-coverage.test.ts +297 -0
- package/src/cli/check-theme.test.ts +38 -14
- package/src/cli/index.ts +392 -16
- package/src/cli/init-sync.test.ts +97 -0
- package/src/connectivity/connectivity-indicator.tsx +16 -9
- package/src/content/index.ts +16 -3
- package/src/content/prose.module.css +7 -28
- package/src/content/prose.module.css.test.ts +8 -24
- package/src/controls/actions.module.css +62 -0
- package/src/controls/actions.module.css.d.ts +13 -0
- package/src/controls/actions.test.tsx +252 -0
- package/src/controls/actions.tsx +655 -0
- package/src/controls/actions.type-guard.test.ts +63 -0
- package/src/controls/compare-filter.tsx +64 -0
- package/src/controls/control-group.test.tsx +229 -0
- package/src/controls/control-group.tsx +63 -0
- package/src/controls/controls.module.css +542 -0
- package/src/controls/controls.module.css.d.ts +35 -0
- package/src/controls/controls.router.test.tsx +1053 -0
- package/src/controls/enum-filter.tsx +170 -0
- package/src/controls/filter-context.tsx +158 -0
- package/src/controls/filter-pill.tsx +120 -0
- package/src/controls/filter-set.test.tsx +472 -0
- package/src/controls/filter-set.tsx +225 -0
- package/src/controls/filter-sheet.tsx +285 -0
- package/src/controls/glyphs.tsx +81 -0
- package/src/controls/index.ts +49 -0
- package/src/controls/multi-select-filter.tsx +140 -0
- package/src/controls/number-filter.tsx +291 -0
- package/src/controls/peer-boundary.test.ts +85 -0
- package/src/controls/props-contract.test.tsx +137 -0
- package/src/controls/range-filter.tsx +237 -0
- package/src/controls/range-filter.type-guard.test.tsx +122 -0
- package/src/controls/search-filter.tsx +123 -0
- package/src/controls/select-filter.tsx +89 -0
- package/src/controls/sync-button.module.css +63 -0
- package/src/controls/sync-button.module.css.d.ts +11 -0
- package/src/controls/sync-button.test.tsx +183 -0
- package/src/controls/sync-button.tsx +197 -0
- package/src/controls/toggle-filter.tsx +60 -0
- package/src/controls/view-tabs.tsx +110 -0
- package/src/controls-dates/date-range-picker.tsx +42 -0
- package/src/controls-dates/index.ts +8 -0
- package/src/dashboard/delta-badge.tsx +4 -74
- package/src/dashboard/index.ts +6 -1
- package/src/dashboard/settings-section.module.css +10 -0
- package/src/dashboard/settings-section.module.css.d.ts +1 -0
- package/src/dashboard/settings-section.test.tsx +56 -0
- package/src/dashboard/settings-section.tsx +33 -30
- package/src/dashboard/stat-card.module.css +153 -0
- package/src/dashboard/stat-card.module.css.d.ts +15 -0
- package/src/dashboard/stat-card.test.tsx +298 -1
- package/src/dashboard/stat-card.tsx +289 -67
- package/src/data/data-table.test.tsx +96 -11
- package/src/data/data-table.tsx +228 -84
- package/src/guard/check-source.test.ts +159 -13
- package/src/guard/grace.test.ts +158 -0
- package/src/guard/index.ts +185 -29
- package/src/guard/types.ts +4 -2
- package/src/index.ts +51 -5
- package/src/nav/types.ts +84 -2
- package/src/notifications/bell.tsx +6 -3
- package/src/notifications/index.ts +1 -1
- package/src/router-tanstack/field.ts +35 -0
- package/src/router-tanstack/index.ts +28 -2
- package/src/router-tanstack/multi-search-param-store.test.ts +71 -3
- package/src/router-tanstack/multi-search-param-store.ts +46 -77
- package/src/router-tanstack/search-param-store.ts +42 -159
- package/src/router-tanstack/search-store.router.test.tsx +882 -0
- package/src/router-tanstack/search-store.test.ts +578 -0
- package/src/router-tanstack/search-store.ts +546 -0
- package/src/router-tanstack/use-nav.test.tsx +187 -0
- package/src/router-tanstack/use-nav.ts +72 -14
- package/src/section/index.ts +1 -0
- package/src/section/section.module.css +104 -0
- package/src/section/section.module.css.d.ts +11 -0
- package/src/section/section.test.tsx +204 -0
- package/src/section/section.tsx +220 -0
- package/src/shell/app-breadcrumbs.tsx +44 -31
- package/src/shell/app-header.module.css +77 -37
- package/src/shell/app-header.module.css.d.ts +2 -1
- package/src/shell/app-mobile-nav.module.css +8 -0
- package/src/shell/app-mobile-nav.module.css.d.ts +1 -0
- package/src/shell/app-mobile-nav.tsx +147 -8
- package/src/shell/app-sidebar-account.tsx +2 -2
- package/src/shell/app-sidebar.module.css +303 -20
- package/src/shell/app-sidebar.module.css.d.ts +19 -1
- package/src/shell/app-sidebar.test.tsx +456 -41
- package/src/shell/app-sidebar.tsx +276 -140
- package/src/shell/index.test.tsx +291 -5
- package/src/shell/index.tsx +137 -38
- package/src/shell/mobile-nav-model.test.ts +228 -7
- package/src/shell/mobile-nav-model.ts +53 -3
- package/src/shell/nav-count-badge.tsx +1 -1
- package/src/shell/page-bar.module.css +178 -0
- package/src/shell/page-bar.module.css.d.ts +17 -0
- package/src/shell/page-bar.test.tsx +316 -0
- package/src/shell/page-bar.tsx +315 -0
- package/src/shell/sidebar-block-model.ts +83 -0
- package/src/shell/sidebar-blocks.tsx +301 -0
- package/src/shell/sidebar-search.module.css +26 -0
- package/src/shell/sidebar-search.module.css.d.ts +2 -0
- package/src/shell/sidebar-search.tsx +71 -2
- package/src/state/fields.test.tsx +512 -0
- package/src/state/fields.ts +1150 -0
- package/src/state/persisted.test.ts +91 -0
- package/src/state/persisted.ts +261 -0
- package/src/state.ts +52 -240
- package/src/styles.css +13 -0
- package/src/surfaces.ts +204 -36
- package/src/theme/controls.module.css +16 -1
- package/src/theme/ctl-theme.test.tsx +275 -0
- package/src/theme/ctl-theme.tsx +138 -0
- package/src/theme/ctl-tier-coverage.test.ts +170 -0
- package/src/theme/density-relations.test.ts +106 -51
- package/src/theme/divider-law.test.ts +259 -0
- package/src/theme/icon-slot.module.css +47 -0
- package/src/theme/icon-slot.module.css.d.ts +8 -0
- package/src/theme/icon-slot.test.tsx +275 -0
- package/src/theme/icon-slot.tsx +59 -0
- package/src/theme/index.ts +165 -3
- package/src/theme/layout-rhythm-css.test.ts +72 -0
- package/src/theme/radius.test.ts +11 -1
- package/src/theme/segmented-control.module.css +20 -0
- package/src/theme/shadow-surfaces.test.ts +20 -7
- package/src/theme/spacing.test.ts +75 -27
- package/src/theme/use-basalt-spacing.ts +2 -2
- package/src/tokens/density.test.ts +7 -25
- package/src/tokens/index.ts +48 -8
- package/src/tokens/palette.ts +107 -73
- package/src/widget-header/delta-badge.test.tsx +60 -0
- package/src/widget-header/delta-badge.tsx +57 -0
- package/src/widget-header/index.ts +2 -0
- package/src/widget-header/no-mantine.test.ts +33 -0
- package/src/widget-header/widget-header.module.css +350 -0
- package/src/widget-header/widget-header.module.css.d.ts +25 -0
- package/src/widget-header/widget-header.test.tsx +187 -0
- package/src/widget-header/widget-header.tsx +213 -0
- package/agent/rules/basalt-agent.md +0 -452
- package/agent/rules/basalt-app.md +0 -246
- package/agent/rules/basalt-commands.md +0 -320
- package/agent/rules/basalt-content.md +0 -409
- package/agent/rules/basalt-data.md +0 -329
- package/agent/rules/basalt-forms.md +0 -186
- package/agent/rules/basalt-notifications.md +0 -264
- package/agent/rules/basalt-query.md +0 -187
- package/agent/rules/basalt-router.md +0 -336
- package/dist/content/article-filter-bar.d.ts +0 -40
- package/dist/content/article-filter-bar.d.ts.map +0 -1
- package/dist/content/article-filter-bar.js +0 -56
- package/dist/shell/page-header.d.ts +0 -13
- package/dist/shell/page-header.d.ts.map +0 -1
- package/dist/shell/page-header.js +0 -23
- package/src/content/article-filter-bar.tsx +0 -88
- package/src/shell/page-header.tsx +0 -35
package/src/guard/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ./guard — headless policy core. Mantine-free, dependency-free.
|
|
3
3
|
*
|
|
4
|
-
* GUARD_RULES: the closed registry of all
|
|
4
|
+
* GUARD_RULES: the closed registry of all 27 violation kinds.
|
|
5
5
|
* checkSource: pure (text, relPath, cfg) → Finding[]. No FS, no walk, no console.
|
|
6
6
|
*/
|
|
7
7
|
import type { Finding, GuardConfig, GuardKind, GuardSeverity } from './types'
|
|
@@ -483,6 +483,68 @@ const RAW_FORM_CONTROL = /<(?:input|select|textarea)\b/g
|
|
|
483
483
|
// for a sub-floor fontSize.
|
|
484
484
|
const RAW_FORM_CONTROL_TAG = /<(?:input|select|textarea)\b(?:=>|[^>])*?>/g
|
|
485
485
|
|
|
486
|
+
// ── Control-home patterns (docs/CONTROLS-SPEC.md §6 — the text lane of laws C1/C8) ──────────────
|
|
487
|
+
//
|
|
488
|
+
// Bounded full-text tag scans, the CARD_SURFACE_TAG / CHART_ENTRY_POINT_TAG shape: a `<Title>` and a
|
|
489
|
+
// `<Select>` are both routinely formatted across lines, so a per-line regex would see neither.
|
|
490
|
+
|
|
491
|
+
/** A `<Title …>` opening tag, however it is wrapped. `order` is judged separately, on the tag text. */
|
|
492
|
+
const PAGE_TITLE_TAG = /<Title(?![\w.])(?:=>|[^>])*?>/g
|
|
493
|
+
/** `order={1}` / `order={2}` — the two orders that name a PAGE rather than a section. */
|
|
494
|
+
const PAGE_TITLE_ORDER_PROP = /\border\s*=\s*\{\s*([12])\s*\}/
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* A raw Mantine selection control's opening tag — the text-lane twin of the oxlint plugin's
|
|
498
|
+
* RAW_FILTER_TAGS. `Chip.Group` carries a dot, so the name class allows one.
|
|
499
|
+
*/
|
|
500
|
+
const RAW_SELECTION_CONTROL_TAG =
|
|
501
|
+
/<(SegmentedControl|Select|MultiSelect|NativeSelect|DatePickerInput|DateInput|TagsInput|Chip\.Group)(?![\w])(?:=>|[^>])*?>/g
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* A tag whose presence makes an `order={1|2}` Title a DOCUMENT heading rather than a page title.
|
|
505
|
+
* File-scoped on purpose — see the scan's own comment for why the coarser test is the honest one.
|
|
506
|
+
*/
|
|
507
|
+
const PROSE_CONTEXT_TAG = /<(?:Prose|ArticleLayout|Modal|Drawer)(?![\w])/
|
|
508
|
+
|
|
509
|
+
/** An opening tag for one of the declared non-homes — a settings row, an overlay, a composer. */
|
|
510
|
+
const CONTROL_HOST_TAG =
|
|
511
|
+
/<(?:SettingsRow|Modal|Drawer|Popover\.Dropdown|Menu\.Dropdown|Composer)(?![\w])/
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* How far ABOVE a control the host-tag window reaches, in lines.
|
|
515
|
+
*
|
|
516
|
+
* The text lane has no ancestry, so "inside a SettingsRow" is approximated by "a SettingsRow opens
|
|
517
|
+
* within the last N lines". 12 covers every row shape in the five consumer repos (label +
|
|
518
|
+
* description + the `control={` line); past that the window would start swallowing the next row's
|
|
519
|
+
* control, which is the direction that makes the kind silent rather than noisy. The plugin rule
|
|
520
|
+
* (`basalt/control-outside-home`) is the one that answers this exactly — this kind exists for the
|
|
521
|
+
* PreToolUse hook lane, which sees one file's TEXT and no AST at all.
|
|
522
|
+
*/
|
|
523
|
+
const CONTROL_HOST_WINDOW_LINES = 12
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* A file whose BASENAME declares it is an overlay's or a form's own body — the cross-file half of
|
|
527
|
+
* {@link CONTROL_HOST_TAG}, and the same regex the plugin's `OVERLAY_CONVENTION_FILE` applies to
|
|
528
|
+
* `basalt/control-outside-home` (one law, two lanes, one exemption).
|
|
529
|
+
*
|
|
530
|
+
* Law C1's cross-file case is advisory by declaration (`docs/CONTROLS-SPEC.md` §6, "Honest
|
|
531
|
+
* coverage"), and this kind was paying for it: a `<Select>` inside `edit-session-modal.tsx` whose
|
|
532
|
+
* `<Modal>` is rendered by the parent route is outside BOTH the 12-line host window and any
|
|
533
|
+
* ancestry walk, because the host tag is not in the file at all. argo carried 9 of them.
|
|
534
|
+
*
|
|
535
|
+
* The trade is that a whole file goes unscanned on a naming convention — the same bargain
|
|
536
|
+
* {@link MANTINE_FORM_IMPORT} already buys, and a smaller one than promoting the kind with 9 known
|
|
537
|
+
* false positives. Basename only: a `modal/` DIRECTORY holds the page pieces around the modals too.
|
|
538
|
+
*/
|
|
539
|
+
const OVERLAY_CONVENTION_FILE = /(?:^|\/)[^/]*-(?:modal|drawer|popover|panel|form)\.[jt]sx$/
|
|
540
|
+
|
|
541
|
+
/** A name whose DECLARATION means this file DEFINES a basalt control rather than consuming one. */
|
|
542
|
+
const CONTROL_OWNER_DEF =
|
|
543
|
+
/\b(?:function|const|class)\s+(?:RangeFilter|CompareFilter|SelectFilter|MultiSelectFilter|NumberFilter|SearchFilter|ToggleFilter|ViewTabs|FilterSet|FilterPill|SyncButton|ActionGroup|OverflowMenu|CtlSlot)(?![\w])/
|
|
544
|
+
|
|
545
|
+
/** `@mantine/form` — a form is C1's third home, and its inputs are not filters. */
|
|
546
|
+
const MANTINE_FORM_IMPORT = /from\s+['"]@mantine\/form['"]/
|
|
547
|
+
|
|
486
548
|
// A Mantine `styles={{ input: {...} }}` per-part style — the `input` key specifically targets the
|
|
487
549
|
// rendered <input>/<textarea> part of TextInput/Select/Textarea/etc. Requires `input` be the FIRST
|
|
488
550
|
// key in the styles object (see the sub-16-input-font doc comment for the scoping trade-off this
|
|
@@ -541,20 +603,32 @@ export const DEFAULT_GUARD_CONFIG: GuardConfig = {
|
|
|
541
603
|
severity: {},
|
|
542
604
|
}
|
|
543
605
|
|
|
606
|
+
/**
|
|
607
|
+
* A grace-ledger entry: `since` and `promote` are plain `x.y.z` semver strings (C16,
|
|
608
|
+
* `docs/CONTROLS-SPEC.md` §1). `grace.test.ts` fails the build once `package.json`'s version
|
|
609
|
+
* reaches `promote` while the entry is still here — the version-gate that replaces the honor
|
|
610
|
+
* system a bare promotion-note string used to run on. `since` must be strictly before `promote`,
|
|
611
|
+
* also asserted there.
|
|
612
|
+
*/
|
|
613
|
+
export type GraceEntry = { since: string; promote: string; why: string }
|
|
614
|
+
|
|
544
615
|
/**
|
|
545
616
|
* Kinds still inside their grace minor — reported, not fatal, until the next minor promotes them.
|
|
546
617
|
* See {@link GuardSeverity} for the doctrine and why basalt-ui in particular needs it.
|
|
547
618
|
*
|
|
548
|
-
*
|
|
549
|
-
*
|
|
550
|
-
*
|
|
619
|
+
* Adding a kind here is part of shipping it; removing the entry IS the promotion, and belongs in
|
|
620
|
+
* its own commit so the changelog says enforcement got stricter. `grace.test.ts` is the other half
|
|
621
|
+
* of "belongs in its own commit": it fails once `package.json`'s version reaches an entry's
|
|
622
|
+
* `promote` while the entry is still here, so a kind can no longer sit at `warn` with nothing
|
|
623
|
+
* tracking it — which is exactly what happened to the five entries this ledger shipped in the
|
|
624
|
+
* round-4 guard minor (D4, `docs/CONTROLS-SPEC.md` §6): all five stayed `warn` for five minors
|
|
625
|
+
* because deleting the entry was the only enforcement, and nothing checked that anyone had.
|
|
551
626
|
*
|
|
552
627
|
* The runway is measured in consumer UPGRADES, not in version numbers: `mantine-shade-index`
|
|
553
628
|
* (the table's first tenant, promoted and gone) sat here across two minors because 1.8.0 shipped
|
|
554
629
|
* the same day as 1.7.0 and 1.9.0 carried the chart batch the same consumer was upgrading for —
|
|
555
630
|
* bundling a build-breaking promotion into the minor they take for the fixes turns a routine bump
|
|
556
|
-
* into an unplanned refactor, which is exactly what the grace minor exists to prevent.
|
|
557
|
-
* entries below all landed together in the round-4 guard minor and all promote one minor later.
|
|
631
|
+
* into an unplanned refactor, which is exactly what the grace minor exists to prevent.
|
|
558
632
|
*
|
|
559
633
|
* **A `SCANNABLE_EXT` widening cannot be expressed here, and 1.23.2's was deliberately not.**
|
|
560
634
|
* Adding `.astro`/`.jsx`/`.vue` widens the FILE SET for all 25 kinds at once — there is no kind to
|
|
@@ -572,29 +646,25 @@ export const DEFAULT_GUARD_CONFIG: GuardConfig = {
|
|
|
572
646
|
* with `theme-allow`, `basalt.severity` and `exemptRules` all still available.
|
|
573
647
|
*
|
|
574
648
|
* @example
|
|
575
|
-
* const GRACE: Partial<Record<GuardKind,
|
|
576
|
-
* 'raw-font-family': '
|
|
649
|
+
* const GRACE: Partial<Record<GuardKind, GraceEntry>> = {
|
|
650
|
+
* 'raw-font-family': { since: '1.4.0', promote: '1.5.0', why: 'introduced 1.4.0 — …' },
|
|
577
651
|
* }
|
|
578
652
|
*/
|
|
579
|
-
const GRACE_PERIOD_KINDS: Partial<Record<GuardKind,
|
|
580
|
-
'
|
|
581
|
-
'
|
|
582
|
-
'
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
'both sees nothing new; one running check-theme alone gets a minor of runway.',
|
|
595
|
-
'hidden-inline-style':
|
|
596
|
-
'introduced in the round-4 guard minor — promote by DELETING the kind and folding its two ' +
|
|
597
|
-
'scans into `raw-html-layout`, which is where they belong once the widening has had its minor.',
|
|
653
|
+
export const GRACE_PERIOD_KINDS: Partial<Record<GuardKind, GraceEntry>> = {
|
|
654
|
+
'raw-selection-control': {
|
|
655
|
+
since: '1.26.0',
|
|
656
|
+
promote: '1.28.0',
|
|
657
|
+
why:
|
|
658
|
+
'new in the wave-6 control guards (docs/CONTROLS-SPEC.md §6, law C1). The text lane cannot ' +
|
|
659
|
+
'see ancestry, so "no home" is approximated by a 12-line host-tag window — the loosest ' +
|
|
660
|
+
'reading in the guard, and the reason this one lands warn rather than error even though its ' +
|
|
661
|
+
'law is settled. It moved from 1.27.0 to 1.28.0 with its AST twin, `basalt/control-outside-' +
|
|
662
|
+
'home`, whose PLUGIN_RULE_GRACE entry carries the measurement: the wave-7 run left 9 warns ' +
|
|
663
|
+
'in argo, all of them a control in a modal/form module whose `<Modal>` is rendered by the ' +
|
|
664
|
+
'PARENT — law C1 cross-file, which neither lane can see. `OVERLAY_CONVENTION_FILE` (this ' +
|
|
665
|
+
'file, and the same regex in the plugin) exempts that declared naming convention in both ' +
|
|
666
|
+
'lanes; 1.28.0 is when the remainder is re-measured. One law, two lanes, one promotion.',
|
|
667
|
+
},
|
|
598
668
|
}
|
|
599
669
|
|
|
600
670
|
/** A kind's effective severity: consumer override first, then the grace table, then `error`. */
|
|
@@ -762,6 +832,16 @@ export const PLUGIN_RULE_IDS: ReadonlySet<string> = new Set([
|
|
|
762
832
|
'hand-rolled-shell',
|
|
763
833
|
'shadow-basalt-export',
|
|
764
834
|
'raw-scroll-container',
|
|
835
|
+
'hand-rolled-filter',
|
|
836
|
+
'control-outside-home',
|
|
837
|
+
'control-size-literal',
|
|
838
|
+
'page-bar-budget',
|
|
839
|
+
'responsive-twin',
|
|
840
|
+
'search-literal-link',
|
|
841
|
+
'use-search-from-literal',
|
|
842
|
+
// `in-body-page-title` is deliberately ABSENT: it is a plugin rule AND a guard kind under one id
|
|
843
|
+
// (one law, two lanes, one annotation), and this set is the ids OUTSIDE `checkSource`'s reach.
|
|
844
|
+
// `check-source.test.ts` asserts the two registries stay disjoint.
|
|
765
845
|
'visx-boundary',
|
|
766
846
|
'visx-tooltip',
|
|
767
847
|
'token-layer-boundary',
|
|
@@ -1388,6 +1468,11 @@ export const TOKENS_ONLY_DISABLED_KINDS: ReadonlySet<GuardKind> = new Set([
|
|
|
1388
1468
|
'chart-missing-aria-label',
|
|
1389
1469
|
'raw-form-control',
|
|
1390
1470
|
'sub-16-input-font',
|
|
1471
|
+
// Both wave-6 control kinds: `<Title>` is a Mantine component and the remedy for either one is a
|
|
1472
|
+
// Mantine-rendered home (PageBar / WidgetHeader) or a basalt control over @mantine/core. Neither
|
|
1473
|
+
// sentence is followable in a consumer that installs the token layer and nothing else.
|
|
1474
|
+
'in-body-page-title',
|
|
1475
|
+
'raw-selection-control',
|
|
1391
1476
|
])
|
|
1392
1477
|
|
|
1393
1478
|
// A `/` opens a regex literal (not division) when the previous significant token is one of these
|
|
@@ -1608,7 +1693,7 @@ type GuardRule = {
|
|
|
1608
1693
|
}
|
|
1609
1694
|
|
|
1610
1695
|
/**
|
|
1611
|
-
* The closed registry of all
|
|
1696
|
+
* The closed registry of all 27 guard kinds. The triad test asserts
|
|
1612
1697
|
* `surface.guardKinds ⊆ keyof GUARD_RULES` at runtime.
|
|
1613
1698
|
*
|
|
1614
1699
|
* raw-surface, raw-html-layout, and sub-16-input-font are handled inline in checkSource
|
|
@@ -1827,7 +1912,23 @@ export const GUARD_RULES = {
|
|
|
1827
1912
|
enabled: (cfg: GuardConfig) => cfg.rawHtmlLayout,
|
|
1828
1913
|
appliesTo: (relPath) => !isChartFile(relPath) && !relPath.endsWith('.css'),
|
|
1829
1914
|
message:
|
|
1830
|
-
'Raw HTML element with inline layout/surface styling that the line scan cannot see — the tag is formatted across lines, or the style object is hoisted to a const. Same violation as raw-html-layout: use a Mantine layout primitive (Box/Flex/Grid/Stack/Group).
|
|
1915
|
+
'Raw HTML element with inline layout/surface styling that the line scan cannot see — the tag is formatted across lines, or the style object is hoisted to a const. Same violation as raw-html-layout: use a Mantine layout primitive (Box/Flex/Grid/Stack/Group). Promoted to error at 1.26.0 (C16) with severity only — the kind stayed standalone rather than folding into raw-html-layout as originally planned; the merge is still open, tracked as a follow-up, not done here.',
|
|
1916
|
+
},
|
|
1917
|
+
'in-body-page-title': {
|
|
1918
|
+
kind: 'in-body-page-title',
|
|
1919
|
+
pattern: PAGE_TITLE_TAG, // handled inline (full-text tag-scoped scan); entry keeps the registry complete
|
|
1920
|
+
// JSX-tag-shaped (`<Title order={1}>`) — never appears in CSS text.
|
|
1921
|
+
appliesTo: (relPath) => !relPath.endsWith('.css'),
|
|
1922
|
+
message:
|
|
1923
|
+
'In-body page title — a page is named ONCE, by the breadcrumb (staticData.title) or by PageBar.title in a shell-less app, and every section/card/table title is a WidgetHeader (docs/CONTROLS-SPEC.md law C8). An <Title order={1|2}> in the body is a second name for the same page, and it drifts. Prose / ArticleLayout / an overlay and anything under a content/ path are document headings and never report. Same law the oxlint plugin enforces as basalt/in-body-page-title — one id, two lanes, so one theme-allow covers both.',
|
|
1924
|
+
},
|
|
1925
|
+
'raw-selection-control': {
|
|
1926
|
+
kind: 'raw-selection-control',
|
|
1927
|
+
pattern: RAW_SELECTION_CONTROL_TAG, // handled inline (full-text tag-scoped scan + host window)
|
|
1928
|
+
// JSX-tag-shaped (`<Select …>`) — never appears in CSS text.
|
|
1929
|
+
appliesTo: (relPath) => !relPath.endsWith('.css'),
|
|
1930
|
+
message:
|
|
1931
|
+
'Raw selection control with no home — a filter or tab belongs in a PageBar / Section / WidgetHeader slot and takes a `field` (a FieldHandle), so it owns the URL write and the localStorage mirror instead of carrying value/onChange (docs/CONTROLS-SPEC.md laws C1–C3). Use the bound control from basalt-ui/controls. A settings row, an overlay and an @mantine/form file are the declared non-homes and never report. This is the TEXT lane of basalt/control-outside-home, which answers the same question against the real AST — where the two disagree, the plugin is right.',
|
|
1831
1932
|
},
|
|
1832
1933
|
'inline-font-size': {
|
|
1833
1934
|
kind: 'inline-font-size',
|
|
@@ -2249,6 +2350,61 @@ export function checkSource(text: string, relPath: string, cfg: GuardConfig): Fi
|
|
|
2249
2350
|
}
|
|
2250
2351
|
}
|
|
2251
2352
|
|
|
2353
|
+
// in-body-page-title — full-text tag scan (the card-with-border shape), because a `<Title>` with
|
|
2354
|
+
// its props on separate lines is the formatted default. `order` is judged on the tag's own text.
|
|
2355
|
+
//
|
|
2356
|
+
// Two file-level exemptions, both deliberately coarser than the plugin's node-level ones and both
|
|
2357
|
+
// false-NEGATIVE-only: anything under a `content/` path segment, and any file that renders
|
|
2358
|
+
// `<Prose>` / `<ArticleLayout>` / an overlay anywhere in it. A text scan has no ancestry, so
|
|
2359
|
+
// "under Prose" is not answerable per node — and a document heading told to become a breadcrumb is
|
|
2360
|
+
// the wrong advice, not merely noise. `basalt/in-body-page-title` (the AST lane, same id) is the
|
|
2361
|
+
// half that scopes this exactly.
|
|
2362
|
+
if (
|
|
2363
|
+
ruleApplies('in-body-page-title', relPath) &&
|
|
2364
|
+
!relPath.split('/').includes('content') &&
|
|
2365
|
+
!PROSE_CONTEXT_TAG.test(codeText)
|
|
2366
|
+
) {
|
|
2367
|
+
for (const m of codeText.matchAll(PAGE_TITLE_TAG)) {
|
|
2368
|
+
const order = PAGE_TITLE_ORDER_PROP.exec(m[0])
|
|
2369
|
+
if (order === null) continue
|
|
2370
|
+
const startLine = codeText.slice(0, m.index ?? 0).split('\n').length
|
|
2371
|
+
const endLine = startLine + (m[0].split('\n').length - 1)
|
|
2372
|
+
if (isAllowedInRange(startLine, endLine, 'in-body-page-title')) continue
|
|
2373
|
+
findings.push({
|
|
2374
|
+
relPath,
|
|
2375
|
+
line: startLine,
|
|
2376
|
+
token: `<Title order={${order[1]}}`,
|
|
2377
|
+
kind: 'in-body-page-title',
|
|
2378
|
+
})
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
// raw-selection-control — full-text tag scan plus the host WINDOW (see CONTROL_HOST_WINDOW_LINES).
|
|
2383
|
+
// Three file-level exemptions mirror the plugin rule's, so the two lanes agree on the same file:
|
|
2384
|
+
// a file that DEFINES a basalt control cannot be told to use one, a file importing
|
|
2385
|
+
// `@mantine/form` is a form — C1's third home, whose inputs are not filters — and a file NAMED as
|
|
2386
|
+
// an overlay body carries its `<Modal>` in the parent, where no scan of this file can reach it.
|
|
2387
|
+
if (
|
|
2388
|
+
ruleApplies('raw-selection-control', relPath) &&
|
|
2389
|
+
!OVERLAY_CONVENTION_FILE.test(relPath) &&
|
|
2390
|
+
!CONTROL_OWNER_DEF.test(codeText) &&
|
|
2391
|
+
!MANTINE_FORM_IMPORT.test(codeText)
|
|
2392
|
+
) {
|
|
2393
|
+
for (const m of codeText.matchAll(RAW_SELECTION_CONTROL_TAG)) {
|
|
2394
|
+
const startLine = codeText.slice(0, m.index ?? 0).split('\n').length
|
|
2395
|
+
const windowStart = Math.max(0, startLine - 1 - CONTROL_HOST_WINDOW_LINES)
|
|
2396
|
+
if (CONTROL_HOST_TAG.test(codeLines.slice(windowStart, startLine).join('\n'))) continue
|
|
2397
|
+
const endLine = startLine + (m[0].split('\n').length - 1)
|
|
2398
|
+
if (isAllowedInRange(startLine, endLine, 'raw-selection-control')) continue
|
|
2399
|
+
findings.push({
|
|
2400
|
+
relPath,
|
|
2401
|
+
line: startLine,
|
|
2402
|
+
token: `<${m[1] ?? 'Select'}`,
|
|
2403
|
+
kind: 'raw-selection-control',
|
|
2404
|
+
})
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
|
|
2252
2408
|
// sub-16-input-font — two full-text scans (same shape as the tag-scoped scans above): (a) a raw
|
|
2253
2409
|
// form-control's own inline `style={{ fontSize: N }}`, (b) a Mantine `styles={{ input: { fontSize:
|
|
2254
2410
|
// N } }}` per-part style. Either shape is now DEAD CODE against the `!important` floor in
|
package/src/guard/types.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Guard types — Mantine-free, dependency-free (zero imports beyond TS types).
|
|
3
3
|
*
|
|
4
|
-
* GuardKind is the closed set of
|
|
4
|
+
* GuardKind is the closed set of 27 violation kinds the theme guard can emit.
|
|
5
5
|
* Finding is the structured result per violation, replacing the old `Violation` shape.
|
|
6
6
|
* GuardConfig is the per-run configuration that drives checkSource.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
/** The
|
|
9
|
+
/** The 27 theme-guard violation kinds. */
|
|
10
10
|
export type GuardKind =
|
|
11
11
|
| 'raw-hex'
|
|
12
12
|
| 'raw-color-fn'
|
|
@@ -33,6 +33,8 @@ export type GuardKind =
|
|
|
33
33
|
| 'css-raw-surface'
|
|
34
34
|
| 'inline-font-size'
|
|
35
35
|
| 'hidden-inline-style'
|
|
36
|
+
| 'in-body-page-title'
|
|
37
|
+
| 'raw-selection-control'
|
|
36
38
|
|
|
37
39
|
/**
|
|
38
40
|
* How hard a finding lands. `error` fails the build; `warn` reports and passes.
|
package/src/index.ts
CHANGED
|
@@ -14,6 +14,14 @@ export {
|
|
|
14
14
|
} from './provider'
|
|
15
15
|
export { createBasaltTheme, baseTheme, cssVariablesResolver } from './theme'
|
|
16
16
|
export type { BasaltFontsConfig, CreateBasaltThemeOptions } from './theme'
|
|
17
|
+
export { CTL_THEME, CtlSlot } from './theme'
|
|
18
|
+
export type { CtlSlotProps } from './theme'
|
|
19
|
+
/**
|
|
20
|
+
* The action vocabulary every home's `actions` slot takes (`docs/CONTROLS-SPEC.md` §2.1). The
|
|
21
|
+
* COMPONENTS that project it (`ActionGroup`, `OverflowMenu`, `SyncButton`, and the filter family)
|
|
22
|
+
* live on `basalt-ui/controls` — only the types a `PageBar`/`BasaltShell` prop mentions are here.
|
|
23
|
+
*/
|
|
24
|
+
export type { BarAction, ActionGroupProps, GlobalAction } from './controls/actions'
|
|
17
25
|
export { ThemeToggle, type ThemeToggleProps } from './theme-toggle'
|
|
18
26
|
export { MOTION_DURATION, MOTION_SPRING, MOTION_EASE_STANDARD } from './motion'
|
|
19
27
|
export {
|
|
@@ -22,6 +30,7 @@ export {
|
|
|
22
30
|
AppSidebar,
|
|
23
31
|
SidebarSearch,
|
|
24
32
|
type SidebarSearchConfig,
|
|
33
|
+
type SidebarSearchActions,
|
|
25
34
|
SidebarAccount,
|
|
26
35
|
MobileNav,
|
|
27
36
|
type MobileNavProps,
|
|
@@ -31,11 +40,16 @@ export {
|
|
|
31
40
|
MOBILE_MENU_MAX_DEFAULT,
|
|
32
41
|
MOBILE_MORE_KEY,
|
|
33
42
|
AppBreadcrumbs,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
PageActionsOutlet,
|
|
43
|
+
PageBar,
|
|
44
|
+
type PageBarProps,
|
|
37
45
|
type SidebarSection,
|
|
38
46
|
type SidebarItem,
|
|
47
|
+
type SidebarBlock,
|
|
48
|
+
type SidebarBlockItem,
|
|
49
|
+
type SidebarBlockTone,
|
|
50
|
+
type SidebarListBlock,
|
|
51
|
+
type SidebarProgressBlock,
|
|
52
|
+
type SidebarCustomBlock,
|
|
39
53
|
type NavAnchor,
|
|
40
54
|
type NavAnchorProps,
|
|
41
55
|
type NavMobilePlacement,
|
|
@@ -72,19 +86,51 @@ export {
|
|
|
72
86
|
type StandardSchemaV1,
|
|
73
87
|
} from './register'
|
|
74
88
|
|
|
75
|
-
// ── Persisted state +
|
|
89
|
+
// ── Persisted state + the headless store kernel (state.ts) ───────────────────────────────────────
|
|
90
|
+
// The `field.*` VALUE is deliberately not re-exported here — `basalt-ui/forms` already owns the
|
|
91
|
+
// name `field` on its own barrel. Import it from `basalt-ui/router-tanstack` (beside
|
|
92
|
+
// `createSearchStore`) or from `basalt-ui/state` (beside `createLocalStore`).
|
|
76
93
|
export {
|
|
77
94
|
createPersistedState,
|
|
78
95
|
type PersistedStateOptions,
|
|
79
|
-
useOnlineStatus,
|
|
80
96
|
readPersistedValue,
|
|
97
|
+
createLocalStore,
|
|
98
|
+
type LocalStore,
|
|
99
|
+
type AnyField,
|
|
100
|
+
type BooleanField,
|
|
101
|
+
type EnumField,
|
|
102
|
+
type FieldFallback,
|
|
103
|
+
type FieldHandle,
|
|
104
|
+
type FieldLane,
|
|
105
|
+
type FieldOption,
|
|
106
|
+
type FieldSetOptions,
|
|
107
|
+
type FieldValue,
|
|
108
|
+
type MultiField,
|
|
109
|
+
type NumberField,
|
|
110
|
+
type RangeField,
|
|
111
|
+
type RangeParams,
|
|
112
|
+
type RangePresets,
|
|
113
|
+
type RangeValue,
|
|
114
|
+
type RangeWindow,
|
|
115
|
+
type RangeWindows,
|
|
116
|
+
type ResolvedLane,
|
|
117
|
+
type SearchValues,
|
|
118
|
+
type StoredValues,
|
|
119
|
+
type StringField,
|
|
81
120
|
} from './state'
|
|
82
121
|
|
|
122
|
+
// ── WidgetHeader (unified section/widget/card heading primitive, docs/CONTROLS-SPEC.md §2.2) ─────
|
|
123
|
+
export { WidgetHeader, type WidgetHeaderProps } from './widget-header'
|
|
124
|
+
|
|
125
|
+
// ── Section (tier-2 heading composer over WidgetHeader, docs/CONTROLS-SPEC.md §2.2) ───────────────
|
|
126
|
+
export { Section, type SectionProps } from './section'
|
|
127
|
+
|
|
83
128
|
// ── Dashboard composites (KPI atoms + settings building blocks) ──────────────────────────────────
|
|
84
129
|
export {
|
|
85
130
|
DeltaBadge,
|
|
86
131
|
type DeltaBadgeProps,
|
|
87
132
|
StatCard,
|
|
133
|
+
type StatCardBreakdownRow,
|
|
88
134
|
type StatCardProps,
|
|
89
135
|
type StatCardTone,
|
|
90
136
|
EmptyState,
|
package/src/nav/types.ts
CHANGED
|
@@ -40,6 +40,8 @@ export type SidebarItem = {
|
|
|
40
40
|
/** Plain href — the no-router fallback, and the breadcrumb parent target. */
|
|
41
41
|
href?: string
|
|
42
42
|
active?: boolean
|
|
43
|
+
/** A parent on the path to the active row. NOT active itself — never carries `aria-current`. */
|
|
44
|
+
ancestor?: boolean
|
|
43
45
|
disabled?: boolean
|
|
44
46
|
onClick?: (e: MouseEvent) => void
|
|
45
47
|
/** Rich desktop badge (e.g. `NavCountBadge`). Desktop only. */
|
|
@@ -70,6 +72,88 @@ export type SidebarSection = {
|
|
|
70
72
|
mobile?: false | NavSectionMobile
|
|
71
73
|
}
|
|
72
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Status tone on a sidebar-block item — the same three-member vocabulary as `StatCardTone`, whose
|
|
77
|
+
* members are `VX.status` keys by construction. Declared here rather than imported from
|
|
78
|
+
* `../dashboard/stat-card`: this module is the one `./router-tanstack` reads, and even a
|
|
79
|
+
* type-only edge into the Mantine layer would make `nav/types.d.ts` unresolvable without
|
|
80
|
+
* `@mantine/core` installed.
|
|
81
|
+
*/
|
|
82
|
+
export type SidebarBlockTone = 'good' | 'warn' | 'bad'
|
|
83
|
+
|
|
84
|
+
/** One row inside a `kind: 'list'` block. Same router seam as `SidebarItem` — `Anchor`, then href. */
|
|
85
|
+
export type SidebarBlockItem = {
|
|
86
|
+
key: string
|
|
87
|
+
label: string
|
|
88
|
+
/** Trailing muted text (a timestamp, a count, an owner). */
|
|
89
|
+
meta?: string
|
|
90
|
+
icon?: ReactNode
|
|
91
|
+
/** Renders a status dot in place of a missing `icon`; reads `--vx-status-*`, never a hex. */
|
|
92
|
+
tone?: SidebarBlockTone
|
|
93
|
+
Anchor?: NavAnchor
|
|
94
|
+
href?: string
|
|
95
|
+
onClick?: () => void
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* A list of non-destination rows under a micro-label — "Awaiting action", "Recents", a project
|
|
100
|
+
* list. `count` is the header badge (and what earns a rail dot); `max` shows the first N with a
|
|
101
|
+
* "Show more" toggle past it.
|
|
102
|
+
*/
|
|
103
|
+
export type SidebarListBlock = {
|
|
104
|
+
kind: 'list'
|
|
105
|
+
key: string
|
|
106
|
+
label: string
|
|
107
|
+
icon?: ReactNode
|
|
108
|
+
count?: number
|
|
109
|
+
/** Rows shown before the "Show more" toggle. Omit to show every row. */
|
|
110
|
+
max?: number
|
|
111
|
+
items: SidebarBlockItem[]
|
|
112
|
+
/** @default 'nav' */
|
|
113
|
+
placement?: 'nav' | 'bottom'
|
|
114
|
+
collapsible?: boolean
|
|
115
|
+
/** Collapsed-rail projection. @default 'dot' when `count` is set, `'hidden'` otherwise */
|
|
116
|
+
rail?: 'dot' | 'hidden'
|
|
117
|
+
/** @default 'more' */
|
|
118
|
+
mobile?: 'more' | 'hidden'
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** A "Getting started 1 of 5" progress row. Pinned above the settings footer; never in the nav. */
|
|
122
|
+
export type SidebarProgressBlock = {
|
|
123
|
+
kind: 'progress'
|
|
124
|
+
key: string
|
|
125
|
+
label: string
|
|
126
|
+
value: number
|
|
127
|
+
total: number
|
|
128
|
+
onClick?: () => void
|
|
129
|
+
/** @default 'bottom' — the only placement a progress row has. */
|
|
130
|
+
placement?: 'bottom'
|
|
131
|
+
/** Collapsed-rail projection: a ring on the settings row. @default 'ring' */
|
|
132
|
+
rail?: 'ring' | 'hidden'
|
|
133
|
+
/** @default 'hidden' */
|
|
134
|
+
mobile?: 'more' | 'hidden'
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* DESKTOP ONLY — arbitrary consumer content (a tree, a filter panel) that no set of rows can
|
|
139
|
+
* express. Replaced `BasaltShellProps.sidebarNavExtra`; hidden on the collapsed rail and absent
|
|
140
|
+
* from mobile for the same reason that prop was.
|
|
141
|
+
*/
|
|
142
|
+
export type SidebarCustomBlock = {
|
|
143
|
+
kind: 'custom'
|
|
144
|
+
key: string
|
|
145
|
+
node: ReactNode
|
|
146
|
+
/** @default 'nav' */
|
|
147
|
+
placement?: 'nav' | 'bottom'
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* A sidebar block — DECLARED DATA, never a `ReactNode` slot (law C13, `docs/CONTROLS-SPEC.md`
|
|
152
|
+
* §2.3), which is what lets basalt own the collapsed-rail badge and the mobile More-sheet
|
|
153
|
+
* projection instead of each consumer hand-rolling both.
|
|
154
|
+
*/
|
|
155
|
+
export type SidebarBlock = SidebarListBlock | SidebarProgressBlock | SidebarCustomBlock
|
|
156
|
+
|
|
73
157
|
export type MobileNavSurface = 'link' | 'menu' | 'sheet'
|
|
74
158
|
|
|
75
159
|
/** One row inside a `menu`/`sheet` slot, grouped under its source section. */
|
|
@@ -108,8 +192,6 @@ export type MobileNavConfig = {
|
|
|
108
192
|
menuMax?: number
|
|
109
193
|
/** @default 'More' */
|
|
110
194
|
moreLabel?: string
|
|
111
|
-
/** Extra content pinned to the bottom of the More surface. */
|
|
112
|
-
moreExtra?: ReactNode
|
|
113
195
|
/** Scroll container for active-slot re-tap. @default document.scrollingElement */
|
|
114
196
|
getScrollElement?: () => HTMLElement | null
|
|
115
197
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* NotificationBell — ActionIcon + Indicator showing the unread Inbox count, opens a
|
|
3
|
-
* NotificationCenter in a Popover. Designed for the shell `globalActions` slot (
|
|
3
|
+
* NotificationCenter in a Popover. Designed for the shell `globalActions` slot (`GlobalAction[]`).
|
|
4
4
|
*
|
|
5
5
|
* The badge counts unread Inbox items (errors / warnings / actionable notifications you haven't
|
|
6
6
|
* seen) — not every toast that fired. Opening the bell does NOT mark anything read; items are
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*
|
|
12
12
|
* // In the shell's globalActions prop:
|
|
13
13
|
* <BasaltShell
|
|
14
|
-
* globalActions={<NotificationBell
|
|
14
|
+
* globalActions={[{ key: 'notifications', node: <NotificationBell />, mobile: 'bar' }]}
|
|
15
15
|
* {...rest}
|
|
16
16
|
* />
|
|
17
17
|
*/
|
|
@@ -33,7 +33,10 @@ export type NotificationBellProps = {
|
|
|
33
33
|
* Bell icon button with an unread-Inbox indicator. Opens a NotificationCenter popover.
|
|
34
34
|
*
|
|
35
35
|
* @example
|
|
36
|
-
* <BasaltShell
|
|
36
|
+
* <BasaltShell
|
|
37
|
+
* globalActions={[{ key: 'notifications', node: <NotificationBell />, mobile: 'bar' }]}
|
|
38
|
+
* {...rest}
|
|
39
|
+
* />
|
|
37
40
|
*/
|
|
38
41
|
export function NotificationBell({ label = 'Notifications' }: NotificationBellProps) {
|
|
39
42
|
const [opened, { open, toggle, close }] = useDisclosure(false)
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* notifyPromise(save(), { loading: 'Saving…', success: 'Saved', error: 'Failed' })
|
|
21
21
|
*
|
|
22
22
|
* // Shell bell (globalActions slot):
|
|
23
|
-
* <BasaltShell globalActions={<NotificationBell
|
|
23
|
+
* <BasaltShell globalActions={[{ key: 'notifications', node: <NotificationBell />, mobile: 'bar' }]} ... />
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
// ── notify core ───────────────────────────────────────────────────────────────
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `field.*` — the field vocabulary `createSearchStore` takes, re-exported at the surface a consumer
|
|
3
|
+
* imports it from (`basalt-ui/router-tanstack`, beside the store factory).
|
|
4
|
+
*
|
|
5
|
+
* The builders and their codecs LIVE in `../state` on purpose: a field is declarative data with no
|
|
6
|
+
* router in it, and `createLocalStore` — the router-free lane, published on `basalt-ui/state` —
|
|
7
|
+
* has to build the same fields without pulling in `@tanstack/react-router`. One definition, two
|
|
8
|
+
* surfaces, no second description of the same param.
|
|
9
|
+
*
|
|
10
|
+
* Full field reference: the JSDoc on `field` itself, and `docs/CONTROLS-SPEC.md` §4.
|
|
11
|
+
*/
|
|
12
|
+
export { field } from '../state'
|
|
13
|
+
export type {
|
|
14
|
+
AnyField,
|
|
15
|
+
BooleanField,
|
|
16
|
+
EnumField,
|
|
17
|
+
FieldFallback,
|
|
18
|
+
FieldHandle,
|
|
19
|
+
FieldLane,
|
|
20
|
+
FieldOption,
|
|
21
|
+
FieldSetOptions,
|
|
22
|
+
FieldValue,
|
|
23
|
+
MultiField,
|
|
24
|
+
NumberField,
|
|
25
|
+
RangeField,
|
|
26
|
+
RangeParams,
|
|
27
|
+
RangePresets,
|
|
28
|
+
RangeWindow,
|
|
29
|
+
RangeWindows,
|
|
30
|
+
RangeValue,
|
|
31
|
+
ResolvedLane,
|
|
32
|
+
SearchValues,
|
|
33
|
+
StoredValues,
|
|
34
|
+
StringField,
|
|
35
|
+
} from '../state'
|
|
@@ -3,10 +3,36 @@
|
|
|
3
3
|
* Optional peer: @tanstack/react-router.
|
|
4
4
|
* Carries the typed navigation definition (`defineNav`/`navGroup`/`navTarget`/`flattenNav`) and
|
|
5
5
|
* `useNav`, which resolves it into `BasaltShell`'s `sections` + `mobileNav` props; plus the
|
|
6
|
-
* StaticDataRouteOption augmentation (title/icon/navSection) for router-derived breadcrumbs
|
|
7
|
-
* the
|
|
6
|
+
* StaticDataRouteOption augmentation (title/icon/navSection) for router-derived breadcrumbs, plus
|
|
7
|
+
* `createSearchStore` + the `field.*` vocabulary (and the two deprecated enum-only wrappers it
|
|
8
|
+
* replaced) — no Mantine, no JSX.
|
|
8
9
|
*/
|
|
9
10
|
|
|
11
|
+
export { createSearchStore, type CreateSearchStoreOptions, type SearchStore } from './search-store'
|
|
12
|
+
export { field } from './field'
|
|
13
|
+
export type {
|
|
14
|
+
AnyField,
|
|
15
|
+
BooleanField,
|
|
16
|
+
EnumField,
|
|
17
|
+
FieldFallback,
|
|
18
|
+
FieldHandle,
|
|
19
|
+
FieldLane,
|
|
20
|
+
FieldOption,
|
|
21
|
+
FieldSetOptions,
|
|
22
|
+
FieldValue,
|
|
23
|
+
MultiField,
|
|
24
|
+
NumberField,
|
|
25
|
+
RangeField,
|
|
26
|
+
RangeParams,
|
|
27
|
+
RangePresets,
|
|
28
|
+
RangeValue,
|
|
29
|
+
RangeWindow,
|
|
30
|
+
RangeWindows,
|
|
31
|
+
ResolvedLane,
|
|
32
|
+
SearchValues,
|
|
33
|
+
StoredValues,
|
|
34
|
+
StringField,
|
|
35
|
+
} from './field'
|
|
10
36
|
export {
|
|
11
37
|
createSearchParamStore,
|
|
12
38
|
type SearchParamStore,
|