basalt-ui 1.25.0 → 1.26.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 +18 -0
- package/MIGRATING.md +546 -20
- 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 +155 -0
- package/agent/rules/basalt-mantine.md +146 -439
- package/agent/rules/basalt-state.md +113 -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 +1275 -31
- package/configs/oxlint-plugin.test.ts +947 -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 +30 -0
- package/dist/controls/enum-filter.d.ts.map +1 -0
- package/dist/controls/enum-filter.js +91 -0
- package/dist/controls/filter-context.d.ts +55 -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 +19 -0
- package/dist/controls/index.d.ts.map +1 -0
- package/dist/controls/index.js +44 -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/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 +29 -0
- package/dist/controls/range-filter.d.ts.map +1 -0
- package/dist/controls/range-filter.js +120 -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/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 +44 -9
- package/dist/dashboard/stat-card.d.ts.map +1 -1
- package/dist/dashboard/stat-card.js +80 -48
- package/dist/dashboard/stat-card.module.css +82 -0
- package/dist/dashboard/stat-card.module.css.d.ts +11 -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 +88 -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 +82 -7
- package/dist/guard/types.d.ts +3 -3
- package/dist/guard/types.d.ts.map +1 -1
- package/dist/index.d.ts +12 -2
- 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 +81 -0
- package/dist/router-tanstack/search-store.d.ts.map +1 -0
- package/dist/router-tanstack/search-store.js +187 -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 +392 -0
- package/dist/state/fields.d.ts.map +1 -0
- package/dist/state/fields.js +369 -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 +48 -0
- package/dist/state/persisted.d.ts.map +1 -0
- package/dist/state/persisted.js +105 -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 +155 -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 +37 -0
- package/dist/widget-header/widget-header.d.ts.map +1 -0
- package/dist/widget-header/widget-header.js +77 -0
- package/dist/widget-header/widget-header.module.css +335 -0
- package/dist/widget-header/widget-header.module.css.d.ts +24 -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 +637 -0
- package/src/controls/enum-filter.tsx +159 -0
- package/src/controls/filter-context.tsx +153 -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 +48 -0
- package/src/controls/multi-select-filter.tsx +140 -0
- package/src/controls/peer-boundary.test.ts +85 -0
- package/src/controls/props-contract.test.tsx +106 -0
- package/src/controls/range-filter.tsx +184 -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/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 +82 -0
- package/src/dashboard/stat-card.module.css.d.ts +11 -0
- package/src/dashboard/stat-card.test.tsx +67 -1
- package/src/dashboard/stat-card.tsx +209 -68
- package/src/data/data-table.test.tsx +96 -11
- package/src/data/data-table.tsx +220 -84
- package/src/guard/check-source.test.ts +126 -13
- package/src/guard/grace.test.ts +149 -0
- package/src/guard/index.ts +172 -29
- package/src/guard/types.ts +4 -2
- package/src/index.ts +46 -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 +31 -0
- package/src/router-tanstack/index.ts +24 -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 +661 -0
- package/src/router-tanstack/search-store.test.ts +418 -0
- package/src/router-tanstack/search-store.ts +380 -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 +258 -0
- package/src/state/fields.ts +892 -0
- package/src/state/persisted.ts +211 -0
- package/src/state.ts +46 -240
- package/src/styles.css +13 -0
- package/src/surfaces.ts +203 -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 +335 -0
- package/src/widget-header/widget-header.module.css.d.ts +24 -0
- package/src/widget-header/widget-header.test.tsx +187 -0
- package/src/widget-header/widget-header.tsx +168 -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/README.md
CHANGED
|
@@ -154,9 +154,9 @@ palette. Skip both — `BasaltOverlays` and `paletteGroups` — if you don't use
|
|
|
154
154
|
> need it — there is no hydration to mismatch. See `docs/MANTINE-THEMING.md`.
|
|
155
155
|
|
|
156
156
|
```tsx
|
|
157
|
-
// Shell — sidebar / mobile
|
|
158
|
-
import { BasaltShell, NavCountBadge } from 'basalt-ui'
|
|
159
|
-
import type { SidebarSection } from 'basalt-ui'
|
|
157
|
+
// Shell — sidebar / mobile nav / breadcrumbs / PageBar; router-agnostic
|
|
158
|
+
import { BasaltShell, NavCountBadge, PageBar } from 'basalt-ui'
|
|
159
|
+
import type { SidebarBlock, SidebarSection } from 'basalt-ui'
|
|
160
160
|
```
|
|
161
161
|
|
|
162
162
|
```ts
|
|
@@ -215,7 +215,7 @@ scanned files, and `doctor`'s `guard-scan` check agrees with it. Each root's **p
|
|
|
215
215
|
contributes its `index.html` and its `public/` tree — the Vite layout `basaltViteConfig` assumes,
|
|
216
216
|
and where a raw `theme-color` or webmanifest `background_color` actually lives. `.json` is never
|
|
217
217
|
blanket-scanned; `include` names one explicitly and is the only route to it. `profile:
|
|
218
|
-
"tokens-only"` turns off the
|
|
218
|
+
"tokens-only"` turns off the 19 kinds whose remedy is a Mantine component or prop, and must be
|
|
219
219
|
declared (or `--tokens-only`) — it is deliberately never inferred from a missing `@mantine/core`,
|
|
220
220
|
which would silence half the guard on any repo keeping Mantine in a different workspace package.
|
|
221
221
|
Other keys (`exempt`, `severity`, `spacingSteps`, `forbiddenAccents`, …) are documented on the
|
|
@@ -292,31 +292,31 @@ deleted rather than silently surviving into the next real skew.
|
|
|
292
292
|
|
|
293
293
|
## Subpath exports
|
|
294
294
|
|
|
295
|
-
| Subpath | Mantine? | Purpose
|
|
296
|
-
| ------------------- | -------- |
|
|
297
|
-
| `.` | coupled | `BasaltProvider`, `createBasaltTheme` / `baseTheme` / `cssVariablesResolver`, `BasaltShell` + sidebar / mobile
|
|
298
|
-
| `./charts` | **free** | `CartesianChart` + visx primitives, kinds, sparklines, hooks, token re-exports
|
|
299
|
-
| `./tokens` | **free** | `VX` token refs, `buildPaletteCss`, `defineSeries`, `seriesTokens`, `groupTokens`, `alpha`, `ColorPair` / `SeriesMap` types
|
|
300
|
-
| `./theme-lab` | coupled | `ThemeLabControls`, `applyOverrides`, `loadOverrides`, `COLOR_GROUPS` for live theme inspection
|
|
301
|
-
| `./vite` | — | `basaltViteConfig(opts)` — Vite preset for basalt-ui consumer apps; `basaltAppPlugin(opts)` — PWA head, manifest, and icon metadata derived from the token palette
|
|
302
|
-
| `./guard` | **free** | `checkSource`, `GUARD_RULES`, `Finding` types — the headless theme-guard core; plus the annotation reader `--audit-allows` is built on: `findAllowAnnotations`, `neutralizeAllowAnnotation`, `NEUTRALIZED_ALLOW_TOKEN`, `PLUGIN_RULE_IDS`, `AllowAnnotationSite`
|
|
303
|
-
| `./query` | **free** | `createBasaltQueryClient`, transport-agnostic `unwrap`, lazy `BasaltQueryDevtools`, `toErrorMessage` / `errorStatus` — the only route to those two; the root barrel does not re-export them
|
|
304
|
-
| `./router-tanstack` | **free** | TanStack Router bridge: `useBasaltNav` (active route) + `
|
|
305
|
-
| `./forms` | coupled | Mantine form adapter: `useBasaltForm`, `field`, `FormErrorSummary`, `useFormDraft` (Standard Schema)
|
|
306
|
-
| `./notifications` | coupled | Mantine notifications: `notify` helpers, typed registry, persisted history, `NotificationBell`
|
|
307
|
-
| `./commands` | coupled | Typed command bus + overlay controller, `toSpotlightActions`, `ShortcutsHelp`, `BasaltOverlays`
|
|
308
|
-
| `./data` | coupled | Convenience barrel pulling both peer groups: `BasaltDataTable`, `BasaltVirtualList` (Mantine-rendered) — prefer `./data/table` / `./data/virtual` for per-feature opt-in
|
|
309
|
-
| `./data/table` | coupled | `BasaltDataTable` — sortable data table over TanStack Table, rendered with Mantine
|
|
310
|
-
| `./data/virtual` | coupled | `BasaltVirtualList` — windowed virtual list over TanStack Virtual, rendered with Mantine
|
|
311
|
-
| `./agent` | **free** | Headless streaming layer (`useAgentStream`, `PartList`) + multi-thread `createThreadsStore` / `useAgentThreadRuns` / outcome seam
|
|
312
|
-
| `./agent-chat` | coupled | Mantine-styled thread-chat components over `./agent`: `ThreadWorkspace`, `ThreadFeed` (`variant`/`renderRow`), `ThreadFeedRow` (inline-expanding Slack row, lazily mounted + kept mounted), `ThreadOutcomeCard`, `ThreadDetailPanel`, `Composer`, `ThreadTranscript` (`groupConsecutive`/`affordances`/`virtualize` — windowed transcripts open at the newest message unless `initialScroll: 'start'`; `virtualize` is enabled by the optional `@tanstack/react-virtual` peer, absent it degrades to an unwindowed, height-bound pane), `threadPartRenderers` — **requires `motion`**, not merely an optional peer; see [Requirements](#requirements)
|
|
313
|
-
| `./state` | **free** |
|
|
314
|
-
| `./connectivity` | coupled | `ConnectivityProvider` (aggregates browser/React-Query/SSE/health-check status), `useConnectivity`, `ConnectivityIndicator` — auto-mounted by `BasaltProvider`
|
|
315
|
-
| `./content` | coupled | `Prose`, `CodeBlock`, `Callout`, `TableOfContents`, `ReadingProgress`, `Markdown`, `MermaidDiagram`, `ArticleLayout`, `ArticleCard` / `ArticleGrid`, `GuideLink` / `GuideDrawer`, `mdxComponents`
|
|
316
|
-
| `./styles.css` | — | `@layer basalt` base styles, iOS input safety net, font stack
|
|
317
|
-
| `./tokens.css` | **free** | Prebuilt `--vx-*` stylesheet — the default `buildPaletteCss()` output as a plain file, for a consumer with a bundler but no React or Mantine. **With no bundler, use the `basalt-ui tokens:css` CLI instead** and commit the emitted file: importing this subpath needs bare-specifier resolution. See [`docs/FRAMEWORK-FREE.md`](https://github.com/jkrumm/basalt-ui/blob/master/docs/FRAMEWORK-FREE.md)
|
|
318
|
-
| `./configs/*` | — | Raw toolchain presets — oxlint, oxfmt, tsconfig (base/react-app/node), lefthook
|
|
319
|
-
| `./llms.txt` | — | Machine-readable surface map — one entry per published subpath with import specifier, description, layer, and optional peers
|
|
295
|
+
| Subpath | Mantine? | Purpose |
|
|
296
|
+
| ------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
297
|
+
| `.` | coupled | `BasaltProvider`, `createBasaltTheme` / `baseTheme` / `cssVariablesResolver`, `BasaltShell` + sidebar / mobile nav / breadcrumbs / `PageBar`, `Section` / `WidgetHeader`, `NavCountBadge`, `SidebarAccount` + the provider-agnostic account contract (`BasaltAccountProps`/`State`/`Actions`), `ThreadWorkspace` + thread-chat components, dashboard composites (`DeltaBadge`, `StatCard`, `EmptyState`, `SettingsSection`/`SettingsRow`/`DangerZone`) and the query-branch trio `QueryState` / `LoadingState` / `ErrorState`, shell types — the root entry still re-exports the thread-chat components; `./agent-chat` takes the same components standalone, without `BasaltProvider`, the shell, the dashboard composites, or `./connectivity` |
|
|
298
|
+
| `./charts` | **free** | `CartesianChart` + visx primitives, kinds, sparklines, hooks, token re-exports |
|
|
299
|
+
| `./tokens` | **free** | `VX` token refs, `buildPaletteCss`, `defineSeries`, `seriesTokens`, `groupTokens`, `alpha`, `ColorPair` / `SeriesMap` types |
|
|
300
|
+
| `./theme-lab` | coupled | `ThemeLabControls`, `applyOverrides`, `loadOverrides`, `COLOR_GROUPS` for live theme inspection |
|
|
301
|
+
| `./vite` | — | `basaltViteConfig(opts)` — Vite preset for basalt-ui consumer apps; `basaltAppPlugin(opts)` — PWA head, manifest, and icon metadata derived from the token palette |
|
|
302
|
+
| `./guard` | **free** | `checkSource`, `GUARD_RULES`, `Finding` types — the headless theme-guard core; plus the annotation reader `--audit-allows` is built on: `findAllowAnnotations`, `neutralizeAllowAnnotation`, `NEUTRALIZED_ALLOW_TOKEN`, `PLUGIN_RULE_IDS`, `AllowAnnotationSite` |
|
|
303
|
+
| `./query` | **free** | `createBasaltQueryClient`, transport-agnostic `unwrap`, lazy `BasaltQueryDevtools`, `toErrorMessage` / `errorStatus` — the only route to those two; the root barrel does not re-export them |
|
|
304
|
+
| `./router-tanstack` | **free** | TanStack Router bridge: `defineNav`/`navGroup`/`navTarget`/`flattenNav` + `useNav` (→ `{ sections, mobileNav }`), `useBasaltNav` (active route), `useRouterBreadcrumbs`, and `createSearchStore` + the `field.*` vocabulary |
|
|
305
|
+
| `./forms` | coupled | Mantine form adapter: `useBasaltForm`, `field`, `FormErrorSummary`, `useFormDraft` (Standard Schema) |
|
|
306
|
+
| `./notifications` | coupled | Mantine notifications: `notify` helpers, typed registry, persisted history, `NotificationBell` |
|
|
307
|
+
| `./commands` | coupled | Typed command bus + overlay controller, `toSpotlightActions`, `ShortcutsHelp`, `BasaltOverlays` |
|
|
308
|
+
| `./data` | coupled | Convenience barrel pulling both peer groups: `BasaltDataTable`, `BasaltVirtualList` (Mantine-rendered) — prefer `./data/table` / `./data/virtual` for per-feature opt-in |
|
|
309
|
+
| `./data/table` | coupled | `BasaltDataTable` — sortable data table over TanStack Table, rendered with Mantine |
|
|
310
|
+
| `./data/virtual` | coupled | `BasaltVirtualList` — windowed virtual list over TanStack Virtual, rendered with Mantine |
|
|
311
|
+
| `./agent` | **free** | Headless streaming layer (`useAgentStream`, `PartList`) + multi-thread `createThreadsStore` / `useAgentThreadRuns` / outcome seam |
|
|
312
|
+
| `./agent-chat` | coupled | Mantine-styled thread-chat components over `./agent`: `ThreadWorkspace`, `ThreadFeed` (`variant`/`renderRow`), `ThreadFeedRow` (inline-expanding Slack row, lazily mounted + kept mounted), `ThreadOutcomeCard`, `ThreadDetailPanel`, `Composer`, `ThreadTranscript` (`groupConsecutive`/`affordances`/`virtualize` — windowed transcripts open at the newest message unless `initialScroll: 'start'`; `virtualize` is enabled by the optional `@tanstack/react-virtual` peer, absent it degrades to an unwindowed, height-bound pane), `threadPartRenderers` — **requires `motion`**, not merely an optional peer; see [Requirements](#requirements) |
|
|
313
|
+
| `./state` | **free** | createPersistedState (versioned localStorage) + the store field vocabulary (field.enum/multi/range/number/boolean/string, FieldHandle, lanes) + createLocalStore, the router-free store — Mantine-free state primitives |
|
|
314
|
+
| `./connectivity` | coupled | `ConnectivityProvider` (aggregates browser/React-Query/SSE/health-check status), `useConnectivity`, `ConnectivityIndicator` — auto-mounted by `BasaltProvider` |
|
|
315
|
+
| `./content` | coupled | `Prose`, `CodeBlock`, `Callout`, `TableOfContents`, `ReadingProgress`, `Markdown`, `MermaidDiagram`, `ArticleLayout`, `ArticleCard` / `ArticleGrid`, `GuideLink` / `GuideDrawer`, `mdxComponents` |
|
|
316
|
+
| `./styles.css` | — | `@layer basalt` base styles, iOS input safety net, font stack |
|
|
317
|
+
| `./tokens.css` | **free** | Prebuilt `--vx-*` stylesheet — the default `buildPaletteCss()` output as a plain file, for a consumer with a bundler but no React or Mantine. **With no bundler, use the `basalt-ui tokens:css` CLI instead** and commit the emitted file: importing this subpath needs bare-specifier resolution. See [`docs/FRAMEWORK-FREE.md`](https://github.com/jkrumm/basalt-ui/blob/master/docs/FRAMEWORK-FREE.md) |
|
|
318
|
+
| `./configs/*` | — | Raw toolchain presets — oxlint, oxfmt, tsconfig (base/react-app/node), lefthook |
|
|
319
|
+
| `./llms.txt` | — | Machine-readable surface map — one entry per published subpath with import specifier, description, layer, and optional peers |
|
|
320
320
|
|
|
321
321
|
Named exports only — no default exports.
|
|
322
322
|
|
|
@@ -542,12 +542,25 @@ bun add @tanstack/react-router
|
|
|
542
542
|
```
|
|
543
543
|
|
|
544
544
|
```tsx
|
|
545
|
-
import {
|
|
545
|
+
import { createSearchStore, defineNav, field, useNav } from 'basalt-ui/router-tanstack'
|
|
546
546
|
|
|
547
|
-
const
|
|
548
|
-
|
|
547
|
+
export const NAV = defineNav({
|
|
548
|
+
groups: [
|
|
549
|
+
/* navGroup(...) */
|
|
550
|
+
],
|
|
551
|
+
})
|
|
552
|
+
export const analytics = createSearchStore({
|
|
553
|
+
key: 'analytics',
|
|
554
|
+
fields: { range: field.range({ presets: ['7d', '30d'], fallback: '30d' }) },
|
|
555
|
+
})
|
|
556
|
+
|
|
557
|
+
const nav = useNav(NAV) // spread onto <BasaltShell {...nav} />
|
|
558
|
+
// route: validateSearch: analytics.validateSearch — nav link: search: analytics.linkSearch
|
|
549
559
|
```
|
|
550
560
|
|
|
561
|
+
`useBasaltNav` (active route) and `useRouterBreadcrumbs` ship here too. A control binds to
|
|
562
|
+
`analytics.field.range`, never to `value`/`onChange` — see `basalt-ui/controls`.
|
|
563
|
+
|
|
551
564
|
### `./forms` — Mantine form adapter
|
|
552
565
|
|
|
553
566
|
```bash
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
---
|
|
2
|
+
source: basalt-ui
|
|
3
|
+
description: The adapter batteries — query, forms, notifications, commands, data, content, agent — plus app bootstrapping (vite preset, CSS layer order, init/sync). One doctrine each: use the shipped seam, don't re-roll it.
|
|
4
|
+
paths:
|
|
5
|
+
- 'src/**'
|
|
6
|
+
- 'apps/**/src/**'
|
|
7
|
+
- '**/vite.config.ts'
|
|
8
|
+
- '**/index.html'
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<!-- basalt:coverage -->
|
|
12
|
+
<!-- GENERATED from src/surfaces.ts — `basalt-ui check-coverage --write`. Do not hand-edit. -->
|
|
13
|
+
<!-- backed by: guard kinds — none · oxlint rules — basalt/agent-no-raw-usechat, basalt/agent-resume-guard, basalt/ai-sdk-major -->
|
|
14
|
+
<!-- not guarded: — -->
|
|
15
|
+
<!-- /basalt:coverage -->
|
|
16
|
+
|
|
17
|
+
# Basalt Batteries — the adapter surfaces
|
|
18
|
+
|
|
19
|
+
Each battery is a subpath with **one** doctrine: the shipped seam is the seam, and re-rolling it is
|
|
20
|
+
the failure this rule exists to prevent. Every peer named below is OPTIONAL — install it before
|
|
21
|
+
importing the battery. The API is in the shipped types, the JSDoc and `llms.txt` at the install
|
|
22
|
+
directory; what follows is only what a reader gets wrong without being told.
|
|
23
|
+
|
|
24
|
+
## `./query` — TanStack Query
|
|
25
|
+
|
|
26
|
+
`@tanstack/react-query` is a REQUIRED peer (`BasaltProvider` needs it at build time). Import the
|
|
27
|
+
hooks from `basalt-ui/query`, never dual-import from `@tanstack/react-query`.
|
|
28
|
+
|
|
29
|
+
- **One factory per resource**, keyed `[resource, action, ...params]`, built with `queryOptions`;
|
|
30
|
+
components import the factory, never a raw string key.
|
|
31
|
+
- **`queryFn` wraps the call in `unwrap()`** — it throws on the error branch AND on a `null` data
|
|
32
|
+
with no error (a 204, a silent transport failure). Both are caller bugs, so both must surface.
|
|
33
|
+
- **Render a query with `QueryState`, don't write the switch.** Rendering only the empty branch is
|
|
34
|
+
the bug it retires: a consumer's library showed `No images` on a 500 because nobody wrote the other
|
|
35
|
+
three branches. Precedence is error-without-data → empty → loading → children, plus a cached-data
|
|
36
|
+
banner when a refresh fails over existing data. `LoadingState`/`ErrorState` ship beside it for a
|
|
37
|
+
page that must place the branches in different DOM positions.
|
|
38
|
+
- **`QueryState` takes `useQuery`, not `useSuspenseQuery`** — a suspense read never hands you an
|
|
39
|
+
undefined `data` or an error branch, so there is nothing to decide. Suspense + an error boundary
|
|
40
|
+
and `QueryState` are the two ways to render a query; pick ONE per read. Inside a
|
|
41
|
+
loader-prefetched route, `useSuspenseQuery` + `ensureQueryData` is the pair.
|
|
42
|
+
- **`query` is a structural subset checked at runtime** — a composed or hand-rolled result passes
|
|
43
|
+
with no cast, and a missing `isError` throws naming the field rather than rendering a false claim.
|
|
44
|
+
- Decode an unknown error with `toErrorMessage(err, fallback?)` / `errorStatus(err)`; invalidate by
|
|
45
|
+
resource PREFIX after a mutation; `BasaltQueryDevtools` is lazy and production-excluded, so import
|
|
46
|
+
it unconditionally and never the raw devtools package.
|
|
47
|
+
|
|
48
|
+
## Eden Treaty — three silent `any`s (stated once)
|
|
49
|
+
|
|
50
|
+
Stated here because they reach both `./query`'s `unwrap` and `./agent`'s transport. Three
|
|
51
|
+
patterns silently degrade Treaty inference to `any`, with no TypeScript error at the point of
|
|
52
|
+
breakage: a route that is not method-chained back onto the app instance; client and server packages
|
|
53
|
+
whose tsconfig path aliases do not resolve identically; and a `t.Object`/`t.Union` **response schema
|
|
54
|
+
on a streaming route** (Eden #231, still open). For a stream: no response schema, an explicit
|
|
55
|
+
`: AsyncGenerator<AgentPart>` return annotation, and validation at yield time via `parseAgentPart`.
|
|
56
|
+
Check these three first whenever Eden types regress.
|
|
57
|
+
|
|
58
|
+
## `./forms` — `@mantine/form`
|
|
59
|
+
|
|
60
|
+
- **`useBasaltForm` is the entry**: `mode: 'uncontrolled'` by default (no per-keystroke re-render)
|
|
61
|
+
and `schemaResolver(schema, { sync: true })` when a `schema` is passed. Type a schema param as
|
|
62
|
+
`StandardSchemaV1` from `basalt-ui`, never `ZodSchema` — Valibot, Zod 4, ArkType and Effect all
|
|
63
|
+
satisfy it structurally.
|
|
64
|
+
- **`validate` is deliberately omitted** from its options — a cross-field or async rule that the
|
|
65
|
+
schema cannot express drops to a raw `useForm`.
|
|
66
|
+
- **`field(form, path)` is the one spread** every uncontrolled Mantine field needs (it bundles
|
|
67
|
+
`getInputProps` + `key`); writing the two by hand is how one gets forgotten.
|
|
68
|
+
- **`FormErrorSummary` goes at the TOP of the form** so assistive tech lands on it after a failed
|
|
69
|
+
submit. It renders `null` on a clean form, so include it unconditionally.
|
|
70
|
+
- **`useFormDraft` persists through `createPersistedState`** — wire `saveDraft` into
|
|
71
|
+
`onValuesChange` for autosave, call `clearDraft()` in the success path, keep `key` stable across
|
|
72
|
+
renders (changing it recreates the store and loses the draft) and bump `version` on a shape change.
|
|
73
|
+
- Errors are inline field errors; a toast is for submit-level failure only.
|
|
74
|
+
|
|
75
|
+
## `./notifications` — `@mantine/notifications`
|
|
76
|
+
|
|
77
|
+
- **`notify({ intent, message })` is the surface**, plus the four `notifySuccess/Error/Warning/Info`
|
|
78
|
+
wrappers and `notifyPromise` for an async operation. Intent — not a color — is what maps to the
|
|
79
|
+
Mantine color, the correct aria `role` (polite vs assertive) and the per-intent `autoClose`, so
|
|
80
|
+
**never pass a raw color instead of an intent**.
|
|
81
|
+
- **`defineNotifications` + `emit` is the typed registry**: augment `BasaltRegister.notifications`
|
|
82
|
+
once and unknown kinds become tsc errors. `defineNotification` only TYPES a single spec — only
|
|
83
|
+
`defineNotifications(map)` registers, and `emit` before that registration is a runtime no-op.
|
|
84
|
+
- **A spec carries no `title`/`icon`** by design; those stay per-call so a varying title is not
|
|
85
|
+
forced into the registry.
|
|
86
|
+
- **Colors beyond the four intents live OUTSIDE the registry** — call Mantine's raw
|
|
87
|
+
`notifications.show({ color })` directly, and accept the stated cost: a raw toast is invisible to
|
|
88
|
+
the persisted history, the bell and the center, because only `notify`/`notifyPromise` record.
|
|
89
|
+
- History is a module-level persisted ring buffer; `useNotificationHistory` reads it, and
|
|
90
|
+
`NotificationBell` (built for the shell's `globalActions`) opens a `NotificationCenter`, which also
|
|
91
|
+
mounts standalone in any Drawer or panel.
|
|
92
|
+
- The overlay MOUNT is basalt-mantine.md's — one place, one statement.
|
|
93
|
+
|
|
94
|
+
## `./commands` — the command bus
|
|
95
|
+
|
|
96
|
+
- **`defineCommands` + `runCommand`, `defineOverlays` + `overlays.open`** — augment
|
|
97
|
+
`BasaltRegister` once per registry and unknown ids are tsc errors. The augmentation is type-only
|
|
98
|
+
and erased, so both factories stash their map at module scope: call the factory once, at app boot,
|
|
99
|
+
before anything runs a command.
|
|
100
|
+
- **A TS circular-inference footgun**: `overlays.open(...)` inside a `run` in the same module that
|
|
101
|
+
augments `BasaltRegister.commands` can make the map infer as `any`. Annotate that `run`'s return
|
|
102
|
+
type explicitly, or split overlay-opening commands into their own file.
|
|
103
|
+
- **Projections, not parallel lists**: `toSpotlightActions()` (respecting each command's `when()`),
|
|
104
|
+
`toShortcutList()` / `ShortcutsHelp` for display, `toHotkeyBindings()` / `useCommandHotkeys()` for
|
|
105
|
+
the live keys. Never hand-author a second list of the same commands.
|
|
106
|
+
- Reach for `openSpotlight`/`closeSpotlight` from `basalt-ui/commands`, not Mantine's global
|
|
107
|
+
`spotlight` — `BasaltOverlays` mounts against basalt's own store.
|
|
108
|
+
- **Imperative overlay vs route**: `overlays.open` for an ephemeral confirm or quick edit; a route
|
|
109
|
+
for anything that must be shareable, refreshable or back-button-addressable.
|
|
110
|
+
|
|
111
|
+
## `./data` — TanStack Table + Virtual
|
|
112
|
+
|
|
113
|
+
- **Read the props before hand-rolling anything**: title/icon/subtitle + count, an `actions`
|
|
114
|
+
toolbar slot, `facets`, global search, client- or server-side pagination, column pinning, sticky
|
|
115
|
+
header, `maxHeight`/`minWidth`, per-column `meta.align` and `meta.numeral` are all there.
|
|
116
|
+
`meta.align` is a `ColumnMeta` augmentation, so a typo is a tsc error and a bad VALUE throws naming
|
|
117
|
+
the column — declare alignment once instead of on both the `th` and the `td`.
|
|
118
|
+
- **Adopt it for ownership, not for a line count.** One consumer's port ran 29–38 lines LONGER:
|
|
119
|
+
accessor blocks cost more than bespoke `<Table.Td>` rows. What it buys is the `type="native"`
|
|
120
|
+
scroll footgun, alignment stated once, and sorting/filtering/pagination no longer being yours.
|
|
121
|
+
A table with no sorting, no pagination, no filter and no alignment to declare is a layout grid —
|
|
122
|
+
adopting the component there buys nothing.
|
|
123
|
+
- **`manualPagination` imposes a contract on every other prop**: `data` becomes one server page
|
|
124
|
+
while the bar still claims `of N`, so sorting (`manualSorting` or `enableSorting={false}`),
|
|
125
|
+
filtering (`manualFiltering`) and the count (`rowCount`) must each be resolved explicitly.
|
|
126
|
+
Unresolved, it throws in dev naming every breach and degrades to the honest table in production.
|
|
127
|
+
Read the throw; don't work around it.
|
|
128
|
+
- **The escape hatch is the same subpath**: `useReactTable`/`flexRender`/the row models are
|
|
129
|
+
re-exported, so a bespoke table needs no direct `@tanstack/react-table` import — and it scrolls
|
|
130
|
+
through the identical `Table.ScrollContainer type="native"` node.
|
|
131
|
+
- **`BasaltVirtualList` above a few hundred rows**; below that a plain `Stack`/`Table` is cheaper.
|
|
132
|
+
It sets `useFlushSync: false` for React 19 itself — do not override it.
|
|
133
|
+
|
|
134
|
+
## `./content` — prose, markdown, MDX
|
|
135
|
+
|
|
136
|
+
- **Three entries, one typography layer**: `Prose` (JSX children), `Markdown` (a string — CMS,
|
|
137
|
+
files, streamed model output), `mdxComponents` (an MDX pipeline's component map). `Markdown` wraps
|
|
138
|
+
its own `Prose`; MDX output does not, so wrap it or use `ArticleLayout`.
|
|
139
|
+
- **`streaming` is a RENDERING mode, not a trust claim.** `contentTrust` is the independent security
|
|
140
|
+
input and the sole input to the image-origin allowlist: **any surface rendering agent or model
|
|
141
|
+
output must pin `contentTrust="untrusted"`** — an auto-fetched image is the classic
|
|
142
|
+
prompt-injection exfiltration channel. Link/script hardening is always on, streaming or not, and
|
|
143
|
+
`sanitizeSchema` only ADDS to the default schema.
|
|
144
|
+
- **Every optional peer degrades, except one.** No `shiki` (or an unknown language) renders a plain
|
|
145
|
+
mono block; no `beautiful-mermaid` leaves the fence as code; `remend`'s streaming repair and the
|
|
146
|
+
markdown renderer resolve lazily. Six mermaid diagram types are covered — anything else is a
|
|
147
|
+
consumer escape hatch outside `Markdown`.
|
|
148
|
+
- **`ArticleLayout` is the docs frame** (meta header, sticky scroll-spy TOC, prev/next); every
|
|
149
|
+
linking content component takes `renderLink` as the router bridge and defaults to a plain anchor.
|
|
150
|
+
- **The Article model is data + pure operators**, generic over the taxonomy — the app declares which
|
|
151
|
+
categories and tags exist (`satisfies readonly Article<Cat, Tag>[]`, deliberately no factory).
|
|
152
|
+
Filter state is store fields, and the filter UI is `FilterSet`/`ViewTabs`/`MultiSelectFilter`,
|
|
153
|
+
re-exported here so a content-only consumer has one import path (basalt-controls.md).
|
|
154
|
+
- Build-time content tooling (`content-collections`) is a RECIPE, not a dependency; its vite plugin
|
|
155
|
+
must run FIRST because other transforms resolve its virtual module.
|
|
156
|
+
|
|
157
|
+
## `./agent` + `./agent-chat` — streaming chat
|
|
158
|
+
|
|
159
|
+
- **`./agent` is headless and Mantine-free; the chrome is `./agent-chat`.** `./agent` ships no
|
|
160
|
+
markdown renderer at all (`agent → content` is lint-blocked), so `PartList` takes a
|
|
161
|
+
consumer-supplied `components.text`; basalt's own is `./content`'s `Markdown`.
|
|
162
|
+
- **Every switch over `AgentPart` ends with `default: return assertNever(part)`** — that is what
|
|
163
|
+
makes a new variant a compile error instead of a silently dropped part.
|
|
164
|
+
- **`aiSdkTransport` is the recommended default** (optional peer `ai`, resolved lazily on first
|
|
165
|
+
stream); `edenTransport` is the zero-extra-dependency alternative. Neither is deprecated. The
|
|
166
|
+
transport is an injected seam, so a mock is just an object with a `stream` generator.
|
|
167
|
+
- **`useAgentStream` runs ONE turn; `useAgentThreadRuns` runs N keyed by thread id.** Reach for the
|
|
168
|
+
latter for the many-short-chats shape, with `createThreadsStore` as its persisted registry.
|
|
169
|
+
- **basalt ships the outcome-resolver SEAM, never an LLM call.** `heuristicOutcome` is a demo
|
|
170
|
+
fallback; production derives `{ title, summary, status }` from the finished run. The outcome shape
|
|
171
|
+
is deliberately DIFFERENT from the transcript so raw prompts and thinking cannot leak into a feed.
|
|
172
|
+
- **Stream resumption is a client contract, not a backend**: a `StartPart` carries the token, the
|
|
173
|
+
transport implements `resume`, and a mount-time reconnect is attempted before an orphaned thread
|
|
174
|
+
falls back to interrupted. A transport that does neither behaves exactly as before.
|
|
175
|
+
- **Three lint guards protect this discipline** — an unguarded `resume`, a raw `useChat`, and an `ai`
|
|
176
|
+
major mismatch — and they honour `basalt-agent-allow`, never `theme-allow`. `doctor`'s
|
|
177
|
+
`ai-major-parity` is the cross-package counterpart, because a lint run only ever sees one
|
|
178
|
+
package's manifest.
|
|
179
|
+
|
|
180
|
+
## App bootstrapping — `./vite`, CSS order, `init`
|
|
181
|
+
|
|
182
|
+
- **`basaltViteConfig` is config-only and always will be; `basaltAppPlugin` is the plugin half** —
|
|
183
|
+
spread it into your own `plugins` array. Keeping plugins out of the preset is why adopting it never
|
|
184
|
+
injects one you did not ask for. Position is free (`vite-plugin-pwa` self-orders by `enforce`); the
|
|
185
|
+
one real caveat is a plugin emitting files from its own `closeBundle` after the PWA plugin's.
|
|
186
|
+
- **CSS layer order is load-bearing**: every `@mantine/*/styles.layer.css` first, then
|
|
187
|
+
`basalt-ui/styles.css`. An unlayered Mantine import outranks `@layer basalt` regardless of
|
|
188
|
+
specificity.
|
|
189
|
+
- **Head, manifest and theme colors come from the token palette** — never hand-compute a
|
|
190
|
+
`theme-color` hex or an anti-FOUC background, and set `colorScheme` to whatever the app passes as
|
|
191
|
+
`defaultColorScheme` or you ship a flash of the wrong surface. Icons are bring-your-own: either
|
|
192
|
+
the six default filenames under `public/`, or an `icons` array naming what you actually have (an
|
|
193
|
+
entry reaches the head only when it names a `rel`; every entry becomes a manifest icon). The
|
|
194
|
+
service worker is opt-in and degrades to a warning without its peer.
|
|
195
|
+
- **`init` on an existing app is a lint-debt event, not a no-op** — the shipped preset turns on
|
|
196
|
+
whole oxlint plugins the repo was never linted against, so previously-clean code lands with real
|
|
197
|
+
findings. Triage them; do not blanket-disable a plugin. It also patches `basalt.roots` (everything
|
|
198
|
+
derives from it) and a `lint:basalt` script, and it KEEPS an existing `.oxlintrc.json`, which
|
|
199
|
+
leaves the whole basalt lint half off until you extend the preset.
|
|
200
|
+
- **The lefthook preset overrides YOU on a colliding key** — a `run:`/`glob:` under one of its
|
|
201
|
+
command names is discarded silently, while `env:`, `exclude:`, `skip:` and your own command names
|
|
202
|
+
merge; `BASALT_BIN` is the sanctioned seam. A broken `extends` target is a hard fail: lefthook
|
|
203
|
+
merges a missing target into ZERO commands and still exits 0.
|
|
204
|
+
- **Then run `doctor` and read it to the last line.** `SKIPPED` is a third outcome that exits
|
|
205
|
+
non-zero on its own — a check that could not run is not a check that passed.
|