basalt-ui 1.27.0 → 1.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +28 -35
- package/CHANGELOG.md +72 -0
- package/MIGRATING.md +759 -22
- package/README.md +56 -69
- package/agent/rules/basalt-batteries.md +60 -168
- package/agent/rules/basalt-charts.md +56 -75
- package/agent/rules/basalt-controls.md +81 -87
- package/agent/rules/basalt-mantine.md +64 -98
- package/agent/rules/basalt-state.md +42 -60
- package/agent/rules/basalt-tokens.md +46 -67
- package/agent/skills/basalt-app/SKILL.md +2 -0
- package/agent/templates/CLAUDE-block.md.tpl +14 -14
- package/configs/oxlint-plugin.js +964 -33
- package/configs/oxlint-plugin.test.ts +902 -2
- package/configs/oxlint.json +6 -0
- package/dist/agent/ai-sdk-transport.d.ts.map +1 -1
- package/dist/agent/ai-sdk-transport.js +7 -4
- package/dist/agent/coalesce.d.ts.map +1 -1
- package/dist/agent/coalesce.js +2 -1
- package/dist/agent/merge.d.ts.map +1 -1
- package/dist/agent/merge.js +2 -1
- package/dist/agent/part-list.d.ts.map +1 -1
- package/dist/agent/part-list.js +2 -0
- package/dist/agent/stick-to-bottom.d.ts +3 -6
- package/dist/agent/stick-to-bottom.d.ts.map +1 -1
- package/dist/agent/stick-to-bottom.js +6 -3
- package/dist/agent/transport.d.ts +13 -2
- package/dist/agent/transport.d.ts.map +1 -1
- package/dist/agent/use-agent-thread-runs.d.ts +12 -1
- package/dist/agent/use-agent-thread-runs.d.ts.map +1 -1
- package/dist/agent/use-agent-thread-runs.js +20 -6
- package/dist/agent-chat/composer.d.ts +6 -2
- package/dist/agent-chat/composer.d.ts.map +1 -1
- package/dist/agent-chat/composer.js +49 -33
- package/dist/agent-chat/index.d.ts +1 -1
- package/dist/agent-chat/index.d.ts.map +1 -1
- package/dist/agent-chat/relative-time.d.ts +0 -9
- package/dist/agent-chat/relative-time.d.ts.map +1 -1
- package/dist/agent-chat/relative-time.js +2 -1
- package/dist/agent-chat/thread-detail-panel.d.ts +3 -2
- package/dist/agent-chat/thread-detail-panel.d.ts.map +1 -1
- package/dist/agent-chat/thread-detail-panel.js +9 -6
- package/dist/agent-chat/thread-feed-row.d.ts +32 -6
- package/dist/agent-chat/thread-feed-row.d.ts.map +1 -1
- package/dist/agent-chat/thread-feed-row.js +61 -26
- package/dist/agent-chat/thread-feed.d.ts +13 -2
- package/dist/agent-chat/thread-feed.d.ts.map +1 -1
- package/dist/agent-chat/thread-feed.js +16 -7
- package/dist/agent-chat/thread-message.d.ts +4 -3
- package/dist/agent-chat/thread-message.d.ts.map +1 -1
- package/dist/agent-chat/thread-message.js +70 -31
- package/dist/agent-chat/thread-outcome-card.d.ts +3 -2
- package/dist/agent-chat/thread-outcome-card.d.ts.map +1 -1
- package/dist/agent-chat/thread-outcome-card.js +9 -2
- package/dist/agent-chat/thread-workspace.d.ts +7 -2
- package/dist/agent-chat/thread-workspace.d.ts.map +1 -1
- package/dist/agent-chat/thread-workspace.js +62 -29
- package/dist/agent-chat/tool-chip.d.ts +3 -2
- package/dist/agent-chat/tool-chip.d.ts.map +1 -1
- package/dist/agent-chat/tool-chip.js +7 -2
- package/dist/agent-chat/virtualize.d.ts +39 -0
- package/dist/agent-chat/virtualize.d.ts.map +1 -1
- package/dist/agent-chat/virtualize.js +15 -0
- package/dist/charts/hooks/useBandPlot.d.ts.map +1 -1
- package/dist/charts/hooks/useBandPlot.js +15 -8
- package/dist/charts/index.d.ts +11 -10
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/charts/index.js +53 -9
- package/dist/charts/kinds/BandStrip.d.ts +10 -7
- package/dist/charts/kinds/BandStrip.d.ts.map +1 -1
- package/dist/charts/kinds/BandStrip.js +12 -7
- package/dist/charts/kinds/Bars.d.ts +13 -9
- package/dist/charts/kinds/Bars.d.ts.map +1 -1
- package/dist/charts/kinds/Bars.js +37 -11
- package/dist/charts/kinds/Donut.d.ts +6 -5
- package/dist/charts/kinds/Donut.d.ts.map +1 -1
- package/dist/charts/kinds/Donut.js +19 -2
- package/dist/charts/kinds/DualPanel.d.ts +9 -6
- package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
- package/dist/charts/kinds/DualPanel.js +37 -9
- package/dist/charts/kinds/Heatmap.d.ts +10 -6
- package/dist/charts/kinds/Heatmap.d.ts.map +1 -1
- package/dist/charts/kinds/Heatmap.js +58 -31
- package/dist/charts/kinds/MirroredBars.d.ts +6 -5
- package/dist/charts/kinds/MirroredBars.d.ts.map +1 -1
- package/dist/charts/kinds/MirroredBars.js +9 -2
- package/dist/charts/kinds/MultiLine.d.ts +11 -6
- package/dist/charts/kinds/MultiLine.d.ts.map +1 -1
- package/dist/charts/kinds/MultiLine.js +24 -15
- package/dist/charts/kinds/StackedArea.d.ts +18 -8
- package/dist/charts/kinds/StackedArea.d.ts.map +1 -1
- package/dist/charts/kinds/StackedArea.js +29 -4
- package/dist/charts/kinds/ZonedLine.d.ts +13 -11
- package/dist/charts/kinds/ZonedLine.d.ts.map +1 -1
- package/dist/charts/kinds/ZonedLine.js +33 -23
- package/dist/charts/layout/auto-margin.d.ts +65 -8
- package/dist/charts/layout/auto-margin.d.ts.map +1 -1
- package/dist/charts/layout/auto-margin.js +45 -10
- package/dist/charts/layout/log-ticks.d.ts +27 -0
- package/dist/charts/layout/log-ticks.d.ts.map +1 -0
- package/dist/charts/layout/log-ticks.js +42 -0
- package/dist/charts/primitives/Axes.d.ts +32 -4
- package/dist/charts/primitives/Axes.d.ts.map +1 -1
- package/dist/charts/primitives/Axes.js +49 -7
- package/dist/charts/primitives/CartesianChart.d.ts +45 -7
- package/dist/charts/primitives/CartesianChart.d.ts.map +1 -1
- package/dist/charts/primitives/CartesianChart.js +86 -28
- package/dist/charts/primitives/ChartCard.d.ts +45 -4
- package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
- package/dist/charts/primitives/ChartCard.js +70 -21
- package/dist/charts/primitives/ChartFrame.d.ts +30 -4
- package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
- package/dist/charts/primitives/ChartFrame.js +52 -15
- package/dist/charts/primitives/ChartLegend.d.ts +13 -2
- package/dist/charts/primitives/ChartLegend.d.ts.map +1 -1
- package/dist/charts/primitives/ChartLegend.js +33 -13
- package/dist/charts/primitives/ChartPending.d.ts +85 -6
- package/dist/charts/primitives/ChartPending.d.ts.map +1 -1
- package/dist/charts/primitives/ChartPending.js +100 -6
- package/dist/charts/primitives/ChartTooltip.d.ts +7 -0
- package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
- package/dist/charts/primitives/ChartTooltip.js +18 -5
- package/dist/charts/primitives/Crosshair.d.ts +3 -0
- package/dist/charts/primitives/Crosshair.d.ts.map +1 -1
- package/dist/charts/primitives/Crosshair.js +3 -1
- package/dist/charts/primitives/chart-frame-layout.d.ts +95 -0
- package/dist/charts/primitives/chart-frame-layout.d.ts.map +1 -0
- package/dist/charts/primitives/chart-frame-layout.js +89 -0
- package/dist/charts/primitives/chart-tier.d.ts +16 -0
- package/dist/charts/primitives/chart-tier.d.ts.map +1 -0
- package/dist/charts/primitives/chart-tier.js +20 -0
- package/dist/charts/series.d.ts +55 -1
- package/dist/charts/series.d.ts.map +1 -1
- package/dist/charts/series.js +27 -1
- package/dist/charts/sparklines/BarSparkline.d.ts +3 -3
- package/dist/charts/sparklines/BarSparkline.d.ts.map +1 -1
- package/dist/charts/sparklines/BarSparkline.js +9 -3
- package/dist/charts/sparklines/LineSparkline.d.ts +3 -3
- package/dist/charts/sparklines/LineSparkline.d.ts.map +1 -1
- package/dist/charts/sparklines/LineSparkline.js +15 -3
- package/dist/charts/sparklines/index.d.ts +2 -2
- package/dist/charts/sparklines/index.d.ts.map +1 -1
- package/dist/charts/utils/format.d.ts +7 -4
- package/dist/charts/utils/format.d.ts.map +1 -1
- package/dist/charts/utils/format.js +17 -24
- package/dist/charts/utils/ticks.d.ts +81 -2
- package/dist/charts/utils/ticks.d.ts.map +1 -1
- package/dist/charts/utils/ticks.js +54 -4
- package/dist/cli/audit-allows.d.ts +24 -0
- package/dist/cli/audit-allows.d.ts.map +1 -0
- package/dist/cli/audit-allows.js +240 -0
- package/dist/cli/check-theme.d.ts +10 -0
- package/dist/cli/check-theme.d.ts.map +1 -0
- package/dist/cli/check-theme.js +139 -0
- package/dist/cli/doctor.d.ts +47 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +197 -0
- package/dist/cli/guard-hook.d.ts +9 -0
- package/dist/cli/guard-hook.d.ts.map +1 -0
- package/dist/cli/guard-hook.js +93 -0
- package/dist/cli/help.d.ts +9 -0
- package/dist/cli/help.d.ts.map +1 -0
- package/dist/cli/help.js +53 -0
- package/dist/cli/index.d.ts +360 -270
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +85 -1901
- package/dist/cli/init.d.ts +4 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +165 -0
- package/dist/cli/sync.d.ts +43 -0
- package/dist/cli/sync.d.ts.map +1 -0
- package/dist/cli/sync.js +206 -0
- package/dist/cli/tokens-css.d.ts +51 -0
- package/dist/cli/tokens-css.d.ts.map +1 -0
- package/dist/cli/tokens-css.js +165 -0
- package/dist/commands/ShortcutsHelp.d.ts +3 -2
- package/dist/commands/ShortcutsHelp.d.ts.map +1 -1
- package/dist/commands/ShortcutsHelp.js +8 -2
- package/dist/commands/define-commands.d.ts.map +1 -1
- package/dist/commands/define-commands.js +2 -2
- package/dist/commands/define-overlays.d.ts +102 -0
- package/dist/commands/define-overlays.d.ts.map +1 -1
- package/dist/commands/define-overlays.js +102 -2
- package/dist/commands/index.d.ts +12 -2
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +9 -1
- package/dist/commands/overlays-mount.d.ts +16 -11
- package/dist/commands/overlays-mount.d.ts.map +1 -1
- package/dist/commands/overlays-mount.js +23 -14
- package/dist/commands/shell-bridge.d.ts +57 -0
- package/dist/commands/shell-bridge.d.ts.map +1 -0
- package/dist/commands/shell-bridge.js +32 -0
- package/dist/common/errors.d.ts +69 -0
- package/dist/common/errors.d.ts.map +1 -0
- package/dist/common/errors.js +64 -0
- package/dist/common/index.d.ts +17 -0
- package/dist/common/index.d.ts.map +1 -0
- package/dist/common/index.js +35 -0
- package/dist/common/is-dev.d.ts +3 -0
- package/dist/common/is-dev.d.ts.map +1 -0
- package/dist/common/is-dev.js +6 -0
- package/dist/{motion/index.d.ts → common/motion.d.ts} +1 -1
- package/dist/common/motion.d.ts.map +1 -0
- package/dist/common/props.d.ts +63 -0
- package/dist/common/props.d.ts.map +1 -0
- package/dist/common/props.js +6 -0
- package/dist/common/query-state-like.d.ts +43 -0
- package/dist/common/query-state-like.d.ts.map +1 -0
- package/dist/common/query-state-like.js +20 -0
- package/dist/common/refs.d.ts +29 -0
- package/dist/common/refs.d.ts.map +1 -0
- package/dist/common/refs.js +29 -0
- package/dist/common/scroll-parent.d.ts +23 -0
- package/dist/common/scroll-parent.d.ts.map +1 -0
- package/dist/common/scroll-parent.js +21 -0
- package/dist/common/validate.d.ts +38 -0
- package/dist/common/validate.d.ts.map +1 -0
- package/dist/common/validate.js +36 -0
- package/dist/content/article-layout.module.css +11 -4
- package/dist/content/callout.d.ts +8 -4
- package/dist/content/callout.d.ts.map +1 -1
- package/dist/content/callout.js +15 -8
- package/dist/content/guide.d.ts +5 -4
- package/dist/content/guide.d.ts.map +1 -1
- package/dist/content/guide.js +13 -4
- package/dist/content/heading-anchor.d.ts +24 -2
- package/dist/content/heading-anchor.d.ts.map +1 -1
- package/dist/content/heading-anchor.js +23 -9
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/markdown.d.ts +3 -4
- package/dist/content/markdown.d.ts.map +1 -1
- package/dist/content/markdown.js +3 -4
- package/dist/content/prose.module.css +9 -7
- package/dist/content/reading-progress.d.ts +10 -4
- package/dist/content/reading-progress.d.ts.map +1 -1
- package/dist/content/reading-progress.js +26 -10
- package/dist/content/slug.d.ts +4 -0
- package/dist/content/slug.d.ts.map +1 -1
- package/dist/content/toc.d.ts +11 -0
- package/dist/content/toc.d.ts.map +1 -1
- package/dist/content/toc.js +23 -10
- package/dist/controls/actions.d.ts +50 -7
- package/dist/controls/actions.d.ts.map +1 -1
- package/dist/controls/actions.js +39 -8
- package/dist/controls/compare-filter.d.ts +3 -2
- package/dist/controls/compare-filter.d.ts.map +1 -1
- package/dist/controls/compare-filter.js +17 -2
- package/dist/controls/control-group.d.ts +3 -3
- package/dist/controls/control-group.d.ts.map +1 -1
- package/dist/controls/control-group.js +9 -2
- package/dist/controls/controls.module.css +169 -186
- package/dist/controls/controls.module.css.d.ts +9 -11
- package/dist/controls/enum-filter.d.ts +3 -2
- package/dist/controls/enum-filter.d.ts.map +1 -1
- package/dist/controls/enum-filter.js +28 -13
- package/dist/controls/filter-context.d.ts +8 -4
- package/dist/controls/filter-context.d.ts.map +1 -1
- package/dist/controls/filter-pill.d.ts +3 -3
- package/dist/controls/filter-pill.d.ts.map +1 -1
- package/dist/controls/filter-pill.js +4 -1
- package/dist/controls/filter-set.d.ts +3 -2
- package/dist/controls/filter-set.d.ts.map +1 -1
- package/dist/controls/filter-set.js +66 -41
- package/dist/controls/filter-sheet.d.ts +20 -80
- package/dist/controls/filter-sheet.d.ts.map +1 -1
- package/dist/controls/filter-sheet.js +9 -95
- package/dist/controls/index.d.ts +2 -0
- package/dist/controls/index.d.ts.map +1 -1
- package/dist/controls/index.js +4 -0
- package/dist/controls/multi-select-filter.d.ts +18 -2
- package/dist/controls/multi-select-filter.d.ts.map +1 -1
- package/dist/controls/multi-select-filter.js +122 -20
- package/dist/controls/number-filter.d.ts +7 -5
- package/dist/controls/number-filter.d.ts.map +1 -1
- package/dist/controls/number-filter.js +40 -11
- package/dist/controls/panel-row.d.ts +132 -0
- package/dist/controls/panel-row.d.ts.map +1 -0
- package/dist/controls/panel-row.js +173 -0
- package/dist/controls/panel-row.module.css +86 -0
- package/dist/controls/panel-row.module.css.d.ts +13 -0
- package/dist/controls/range-filter.d.ts +2 -1
- package/dist/controls/range-filter.d.ts.map +1 -1
- package/dist/controls/range-filter.js +57 -35
- package/dist/controls/search-filter.d.ts +3 -2
- package/dist/controls/search-filter.d.ts.map +1 -1
- package/dist/controls/search-filter.js +25 -10
- package/dist/controls/select-filter.d.ts +2 -1
- package/dist/controls/select-filter.d.ts.map +1 -1
- package/dist/controls/select-filter.js +4 -0
- package/dist/controls/slider-control.d.ts +36 -0
- package/dist/controls/slider-control.d.ts.map +1 -0
- package/dist/controls/slider-control.js +64 -0
- package/dist/controls/sync-button.d.ts +3 -2
- package/dist/controls/sync-button.d.ts.map +1 -1
- package/dist/controls/sync-button.js +16 -3
- package/dist/controls/toggle-filter.d.ts +3 -2
- package/dist/controls/toggle-filter.d.ts.map +1 -1
- package/dist/controls/toggle-filter.js +28 -11
- package/dist/controls/view-tabs.d.ts +3 -2
- package/dist/controls/view-tabs.d.ts.map +1 -1
- package/dist/controls/view-tabs.js +43 -11
- package/dist/controls-dates/date-range-picker.d.ts +1 -1
- package/dist/controls-dates/date-range-picker.d.ts.map +1 -1
- package/dist/controls-dates/date-range-picker.js +6 -1
- package/dist/dashboard/delta-badge.d.ts +18 -6
- package/dist/dashboard/delta-badge.d.ts.map +1 -1
- package/dist/dashboard/delta-badge.js +33 -1
- package/dist/dashboard/empty-state.d.ts +8 -4
- package/dist/dashboard/empty-state.d.ts.map +1 -1
- package/dist/dashboard/empty-state.js +36 -6
- package/dist/dashboard/index.d.ts +9 -5
- package/dist/dashboard/index.d.ts.map +1 -1
- package/dist/dashboard/index.js +13 -1
- package/dist/dashboard/query-state.d.ts +39 -27
- package/dist/dashboard/query-state.d.ts.map +1 -1
- package/dist/dashboard/query-state.js +109 -58
- package/dist/dashboard/section.d.ts +58 -0
- package/dist/dashboard/section.d.ts.map +1 -0
- package/dist/dashboard/section.js +118 -0
- package/dist/{section → dashboard}/section.module.css +11 -0
- package/dist/dashboard/settings-section.d.ts +19 -9
- package/dist/dashboard/settings-section.d.ts.map +1 -1
- package/dist/dashboard/settings-section.js +75 -26
- package/dist/dashboard/stat-card.d.ts +44 -38
- package/dist/dashboard/stat-card.d.ts.map +1 -1
- package/dist/dashboard/stat-card.js +56 -43
- package/dist/dashboard/stat-card.module.css +46 -31
- package/dist/dashboard/stat-card.module.css.d.ts +1 -0
- package/dist/dashboard/stat-group.d.ts +41 -0
- package/dist/dashboard/stat-group.d.ts.map +1 -0
- package/dist/dashboard/stat-group.js +30 -0
- package/dist/dashboard/stat-group.module.css +66 -0
- package/dist/dashboard/stat-group.module.css.d.ts +8 -0
- package/dist/dashboard/widget-grid.d.ts +27 -0
- package/dist/dashboard/widget-grid.d.ts.map +1 -0
- package/dist/dashboard/widget-grid.js +41 -0
- package/dist/dashboard/widget-grid.module.css +53 -0
- package/dist/dashboard/widget-grid.module.css.d.ts +9 -0
- package/dist/dashboard/widget-header.d.ts +132 -0
- package/dist/dashboard/widget-header.d.ts.map +1 -0
- package/dist/{widget-header → dashboard}/widget-header.js +42 -27
- package/dist/{widget-header → dashboard}/widget-header.module.css +72 -7
- package/dist/data/data-table.d.ts +151 -12
- package/dist/data/data-table.d.ts.map +1 -1
- package/dist/data/data-table.js +385 -139
- package/dist/data/data-table.module.css +64 -0
- package/dist/data/data-table.module.css.d.ts +11 -0
- package/dist/data/query-branch.d.ts +28 -0
- package/dist/data/query-branch.d.ts.map +1 -0
- package/dist/data/query-branch.js +10 -0
- package/dist/data/table.d.ts +2 -2
- package/dist/data/table.d.ts.map +1 -1
- package/dist/data/table.js +2 -16
- package/dist/data/virtual-list.d.ts +65 -6
- package/dist/data/virtual-list.d.ts.map +1 -1
- package/dist/data/virtual-list.js +89 -38
- package/dist/data/virtual.d.ts +4 -4
- package/dist/data/virtual.d.ts.map +1 -1
- package/dist/dev-dock.d.ts +28 -0
- package/dist/dev-dock.d.ts.map +1 -0
- package/dist/dev-dock.js +85 -0
- package/dist/format/date.d.ts +62 -0
- package/dist/format/date.d.ts.map +1 -0
- package/dist/format/date.js +106 -0
- package/dist/format/index.d.ts +39 -0
- package/dist/format/index.d.ts.map +1 -0
- package/dist/format/index.js +54 -0
- package/dist/format/measure.d.ts +15 -0
- package/dist/format/measure.d.ts.map +1 -0
- package/dist/format/measure.js +16 -0
- package/dist/format/number.d.ts +69 -0
- package/dist/format/number.d.ts.map +1 -0
- package/dist/format/number.js +62 -0
- package/dist/format/shared.d.ts +27 -0
- package/dist/format/shared.d.ts.map +1 -0
- package/dist/format/shared.js +29 -0
- package/dist/forms/FormErrorSummary.d.ts +4 -3
- package/dist/forms/FormErrorSummary.d.ts.map +1 -1
- package/dist/forms/FormErrorSummary.js +6 -4
- package/dist/forms/create-form.d.ts +2 -2
- package/dist/forms/field.d.ts +33 -9
- package/dist/forms/field.d.ts.map +1 -1
- package/dist/forms/field.js +7 -6
- package/dist/forms/form-layout.d.ts +142 -0
- package/dist/forms/form-layout.d.ts.map +1 -0
- package/dist/forms/form-layout.js +110 -0
- package/dist/forms/form-layout.module.css +145 -0
- package/dist/forms/form-layout.module.css.d.ts +20 -0
- package/dist/forms/form-state.d.ts +42 -0
- package/dist/forms/form-state.d.ts.map +1 -0
- package/dist/forms/form-state.js +26 -0
- package/dist/forms/index.d.ts +22 -6
- package/dist/forms/index.d.ts.map +1 -1
- package/dist/forms/index.js +19 -4
- package/dist/forms/use-field-array.d.ts +60 -0
- package/dist/forms/use-field-array.d.ts.map +1 -0
- package/dist/forms/use-field-array.js +20 -0
- package/dist/forms/use-form-draft.d.ts +25 -10
- package/dist/forms/use-form-draft.d.ts.map +1 -1
- package/dist/forms/use-form-draft.js +55 -18
- package/dist/forms/use-form-submit.d.ts +88 -0
- package/dist/forms/use-form-submit.d.ts.map +1 -0
- package/dist/forms/use-form-submit.js +63 -0
- package/dist/guard/index.d.ts +2 -9
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +38 -28
- package/dist/guard/types.d.ts +8 -10
- package/dist/guard/types.d.ts.map +1 -1
- package/dist/index.d.ts +21 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -6
- package/dist/notifications/bell.d.ts +3 -2
- package/dist/notifications/bell.d.ts.map +1 -1
- package/dist/notifications/bell.js +73 -56
- package/dist/notifications/center.d.ts +15 -4
- package/dist/notifications/center.d.ts.map +1 -1
- package/dist/notifications/center.js +5 -2
- package/dist/notifications/define-notifications.d.ts +56 -14
- package/dist/notifications/define-notifications.d.ts.map +1 -1
- package/dist/notifications/define-notifications.js +2 -1
- package/dist/notifications/index.d.ts +11 -7
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +5 -3
- package/dist/notifications/mount-guard.d.ts +8 -0
- package/dist/notifications/mount-guard.d.ts.map +1 -0
- package/dist/notifications/mount-guard.js +19 -0
- package/dist/notifications/notify.d.ts +58 -1
- package/dist/notifications/notify.d.ts.map +1 -1
- package/dist/notifications/notify.js +46 -2
- package/dist/notifications/store.d.ts +7 -0
- package/dist/notifications/store.d.ts.map +1 -1
- package/dist/provider/connectivity-indicator.d.ts +13 -0
- package/dist/provider/connectivity-indicator.d.ts.map +1 -0
- package/dist/{connectivity → provider}/connectivity-indicator.js +3 -1
- package/dist/{connectivity → provider}/connectivity-provider.d.ts +1 -1
- package/dist/provider/connectivity-provider.d.ts.map +1 -0
- package/dist/{connectivity → provider}/connectivity-provider.js +4 -1
- package/dist/{connectivity/types.d.ts → provider/connectivity-types.d.ts} +3 -3
- package/dist/provider/connectivity-types.d.ts.map +1 -0
- package/dist/provider/error-fallback.module.css +21 -0
- package/dist/provider/error-fallback.module.css.d.ts +9 -0
- package/dist/provider/index.d.ts +29 -13
- package/dist/provider/index.d.ts.map +1 -1
- package/dist/provider/index.js +82 -34
- package/dist/provider/lab-theme.d.ts.map +1 -1
- package/dist/provider/lab-theme.js +2 -1
- package/dist/provider/use-connectivity.d.ts +3 -0
- package/dist/provider/use-connectivity.d.ts.map +1 -0
- package/dist/query-client.d.ts +63 -0
- package/dist/query-client.d.ts.map +1 -0
- package/dist/query-client.js +33 -0
- package/dist/{query/devtools.d.ts → query-devtools.d.ts} +2 -2
- package/dist/query-devtools.d.ts.map +1 -0
- package/dist/{query/devtools.js → query-devtools.js} +1 -0
- package/dist/register.d.ts +17 -3
- package/dist/register.d.ts.map +1 -1
- package/dist/router-tanstack/index.d.ts +4 -7
- package/dist/router-tanstack/index.d.ts.map +1 -1
- package/dist/router-tanstack/index.js +1 -9
- package/dist/router-tanstack/nav.d.ts +1 -1
- package/dist/router-tanstack/nav.d.ts.map +1 -1
- package/dist/router-tanstack/search-store.d.ts +3 -6
- package/dist/router-tanstack/search-store.d.ts.map +1 -1
- package/dist/router-tanstack/search-store.js +1 -1
- package/dist/router-tanstack/use-nav.d.ts +1 -1
- package/dist/router-tanstack/use-nav.d.ts.map +1 -1
- package/dist/router-tanstack/use-nav.js +1 -1
- package/dist/shell/account-types.d.ts +5 -2
- package/dist/shell/account-types.d.ts.map +1 -1
- package/dist/shell/app-brand.d.ts +13 -0
- package/dist/shell/app-brand.d.ts.map +1 -0
- package/dist/shell/app-brand.js +94 -0
- package/dist/shell/app-brand.module.css +109 -0
- package/dist/shell/app-brand.module.css.d.ts +14 -0
- package/dist/shell/app-breadcrumbs.d.ts +5 -3
- package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
- package/dist/shell/app-breadcrumbs.js +33 -22
- package/dist/shell/app-header.module.css +10 -5
- package/dist/shell/app-main.module.css +118 -0
- package/dist/shell/app-main.module.css.d.ts +11 -0
- package/dist/shell/app-mobile-nav.d.ts +4 -3
- package/dist/shell/app-mobile-nav.d.ts.map +1 -1
- package/dist/shell/app-mobile-nav.js +53 -17
- package/dist/shell/app-mobile-nav.module.css +48 -27
- package/dist/shell/app-mobile-nav.module.css.d.ts +0 -1
- package/dist/shell/app-sidebar-account.d.ts +2 -1
- package/dist/shell/app-sidebar-account.d.ts.map +1 -1
- package/dist/shell/app-sidebar-account.js +57 -22
- package/dist/shell/app-sidebar.d.ts +10 -4
- package/dist/shell/app-sidebar.d.ts.map +1 -1
- package/dist/shell/app-sidebar.js +78 -103
- package/dist/shell/app-sidebar.module.css +63 -96
- package/dist/shell/app-sidebar.module.css.d.ts +0 -5
- package/dist/shell/index.d.ts +24 -10
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +60 -29
- package/dist/shell/isomorphic-layout-effect.d.ts +14 -0
- package/dist/shell/isomorphic-layout-effect.d.ts.map +1 -0
- package/dist/shell/isomorphic-layout-effect.js +5 -0
- package/dist/shell/mobile-nav-model.d.ts +1 -1
- package/dist/shell/mobile-nav-model.d.ts.map +1 -1
- package/dist/shell/mobile-nav-model.js +27 -6
- package/dist/shell/nav-count-badge.d.ts +5 -3
- package/dist/shell/nav-count-badge.d.ts.map +1 -1
- package/dist/shell/nav-count-badge.js +3 -1
- package/dist/{nav/types.d.ts → shell/nav-types.d.ts} +16 -7
- package/dist/shell/nav-types.d.ts.map +1 -0
- package/dist/shell/page-aside.d.ts +43 -0
- package/dist/shell/page-aside.d.ts.map +1 -0
- package/dist/shell/page-aside.js +175 -0
- package/dist/shell/page-aside.module.css +174 -0
- package/dist/shell/page-aside.module.css.d.ts +14 -0
- package/dist/shell/page-bar.d.ts +55 -15
- package/dist/shell/page-bar.d.ts.map +1 -1
- package/dist/shell/page-bar.js +130 -41
- package/dist/shell/page-bar.module.css +107 -13
- package/dist/shell/page-bar.module.css.d.ts +4 -1
- package/dist/shell/page-title.d.ts +14 -0
- package/dist/shell/page-title.d.ts.map +1 -0
- package/dist/shell/page-title.js +25 -0
- package/dist/shell/page-title.module.css +33 -0
- package/dist/shell/page-title.module.css.d.ts +11 -0
- package/dist/shell/sidebar-block-model.d.ts +1 -1
- package/dist/shell/sidebar-block-model.d.ts.map +1 -1
- package/dist/shell/sidebar-blocks.d.ts +1 -1
- package/dist/shell/sidebar-blocks.d.ts.map +1 -1
- package/dist/shell/sidebar-search.d.ts +4 -2
- package/dist/shell/sidebar-search.d.ts.map +1 -1
- package/dist/shell/sidebar-search.js +9 -4
- package/dist/shell/sidebar-search.module.css +1 -1
- package/dist/shell/use-breakpoint.d.ts +11 -0
- package/dist/shell/use-breakpoint.d.ts.map +1 -0
- package/dist/shell/use-breakpoint.js +33 -0
- package/dist/state/fields.d.ts +26 -22
- package/dist/state/fields.d.ts.map +1 -1
- package/dist/state/fields.js +19 -18
- package/dist/state/persisted-or-local.d.ts +9 -0
- package/dist/state/persisted-or-local.d.ts.map +1 -0
- package/dist/state/persisted-or-local.js +24 -0
- package/dist/state.d.ts +1 -1
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +2 -6
- package/dist/styles.css +36 -0
- package/dist/surfaces.d.ts +25 -47
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +72 -55
- package/dist/theme/ctl-theme.d.ts.map +1 -1
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +102 -17
- package/dist/theme/nav-link.module.css +37 -26
- package/dist/theme/segmented-control.module.css +22 -0
- package/dist/theme/segmented-control.module.css.d.ts +1 -0
- package/dist/theme/theme-toggle.d.ts +9 -0
- package/dist/theme/theme-toggle.d.ts.map +1 -0
- package/dist/{theme-toggle/index.js → theme/theme-toggle.js} +9 -3
- package/dist/theme-lab/boot.d.ts +66 -0
- package/dist/theme-lab/boot.d.ts.map +1 -0
- package/dist/theme-lab/boot.js +82 -0
- package/dist/theme-lab/index.d.ts +4 -43
- package/dist/theme-lab/index.d.ts.map +1 -1
- package/dist/theme-lab/index.js +9 -69
- package/dist/tokens/index.d.ts +43 -0
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +45 -6
- package/dist/tokens/palette.d.ts +57 -25
- package/dist/tokens/palette.d.ts.map +1 -1
- package/dist/tokens/palette.js +44 -15
- package/dist/tokens.css +5 -5
- package/llms.txt +11 -26
- package/package.json +54 -34
- package/src/agent/ai-sdk-transport.ts +11 -4
- package/src/agent/coalesce.ts +2 -1
- package/src/agent/merge.ts +2 -1
- package/src/agent/part-list.tsx +2 -0
- package/src/agent/stick-to-bottom.tsx +9 -11
- package/src/agent/transport.ts +15 -2
- package/src/agent/use-agent-thread-runs.ts +65 -15
- package/src/agent-chat/composer.tsx +124 -103
- package/src/agent-chat/index.ts +6 -4
- package/src/agent-chat/relative-time.ts +2 -1
- package/src/agent-chat/thread-detail-panel.tsx +10 -6
- package/src/agent-chat/thread-feed-row.tsx +117 -34
- package/src/agent-chat/thread-feed.tsx +59 -27
- package/src/agent-chat/thread-message.tsx +48 -13
- package/src/agent-chat/thread-outcome-card.tsx +9 -1
- package/src/agent-chat/thread-workspace.tsx +72 -33
- package/src/agent-chat/tool-chip.tsx +8 -2
- package/src/agent-chat/virtualize.ts +48 -0
- package/src/charts/hooks/useBandPlot.ts +26 -8
- package/src/charts/index.ts +46 -14
- package/src/charts/kinds/BandStrip.tsx +36 -14
- package/src/charts/kinds/Bars.tsx +68 -20
- package/src/charts/kinds/Donut.tsx +30 -3
- package/src/charts/kinds/DualPanel.tsx +60 -13
- package/src/charts/kinds/Heatmap.tsx +90 -33
- package/src/charts/kinds/MirroredBars.tsx +21 -3
- package/src/charts/kinds/MultiLine.tsx +61 -18
- package/src/charts/kinds/StackedArea.tsx +77 -6
- package/src/charts/kinds/ZonedLine.tsx +72 -35
- package/src/charts/layout/auto-margin.ts +134 -21
- package/src/charts/layout/log-ticks.ts +90 -0
- package/src/charts/primitives/Axes.tsx +82 -5
- package/src/charts/primitives/CartesianChart.tsx +194 -41
- package/src/charts/primitives/ChartCard.tsx +111 -27
- package/src/charts/primitives/ChartFrame.tsx +111 -26
- package/src/charts/primitives/ChartLegend.tsx +43 -13
- package/src/charts/primitives/ChartPending.tsx +194 -10
- package/src/charts/primitives/ChartTooltip.tsx +37 -5
- package/src/charts/primitives/Crosshair.tsx +6 -3
- package/src/charts/primitives/chart-frame-layout.ts +212 -0
- package/src/charts/primitives/chart-tier.tsx +37 -0
- package/src/charts/series.ts +77 -1
- package/src/charts/sparklines/BarSparkline.tsx +11 -3
- package/src/charts/sparklines/LineSparkline.tsx +18 -4
- package/src/charts/sparklines/index.ts +2 -2
- package/src/charts/utils/format.ts +19 -27
- package/src/charts/utils/ticks.ts +149 -4
- package/src/cli/audit-allows.ts +371 -0
- package/src/cli/check-theme.ts +197 -0
- package/src/cli/doctor.ts +331 -0
- package/src/cli/guard-hook.ts +115 -0
- package/src/cli/help.ts +101 -0
- package/src/cli/index.ts +269 -3434
- package/src/cli/init.ts +247 -0
- package/src/cli/sync.ts +324 -0
- package/src/cli/tokens-css.ts +252 -0
- package/src/commands/ShortcutsHelp.tsx +10 -3
- package/src/commands/define-commands.ts +2 -2
- package/src/commands/define-overlays.ts +216 -1
- package/src/commands/index.ts +22 -1
- package/src/commands/overlays-mount.tsx +63 -31
- package/src/commands/shell-bridge.ts +86 -0
- package/src/common/errors.ts +143 -0
- package/src/common/index.ts +23 -0
- package/src/common/is-dev.ts +4 -0
- package/src/common/props.ts +71 -0
- package/src/common/query-state-like.ts +64 -0
- package/src/common/refs.ts +58 -0
- package/src/common/scroll-parent.ts +42 -0
- package/src/common/validate.ts +94 -0
- package/src/content/article-layout.module.css +11 -4
- package/src/content/callout.tsx +13 -7
- package/src/content/guide.tsx +14 -4
- package/src/content/heading-anchor.tsx +14 -3
- package/src/content/index.ts +2 -0
- package/src/content/markdown.tsx +6 -8
- package/src/content/prose.module.css +9 -7
- package/src/content/reading-progress.tsx +40 -15
- package/src/content/slug.ts +4 -0
- package/src/content/toc.tsx +47 -10
- package/src/controls/actions.tsx +90 -10
- package/src/controls/compare-filter.tsx +20 -3
- package/src/controls/control-group.tsx +11 -4
- package/src/controls/controls.module.css +169 -186
- package/src/controls/controls.module.css.d.ts +9 -11
- package/src/controls/enum-filter.tsx +32 -16
- package/src/controls/filter-context.tsx +14 -6
- package/src/controls/filter-pill.tsx +6 -3
- package/src/controls/filter-set.tsx +32 -13
- package/src/controls/filter-sheet.tsx +44 -194
- package/src/controls/index.ts +7 -0
- package/src/controls/multi-select-filter.tsx +177 -27
- package/src/controls/number-filter.tsx +61 -23
- package/src/controls/panel-row.module.css +86 -0
- package/src/controls/panel-row.module.css.d.ts +13 -0
- package/src/controls/panel-row.tsx +392 -0
- package/src/controls/range-filter.tsx +76 -39
- package/src/controls/search-filter.tsx +30 -12
- package/src/controls/select-filter.tsx +11 -1
- package/src/controls/slider-control.tsx +159 -0
- package/src/controls/sync-button.tsx +13 -3
- package/src/controls/toggle-filter.tsx +36 -16
- package/src/controls/view-tabs.tsx +63 -11
- package/src/controls-dates/date-range-picker.tsx +8 -1
- package/src/dashboard/delta-badge.tsx +75 -4
- package/src/dashboard/empty-state.tsx +67 -24
- package/src/dashboard/index.ts +24 -3
- package/src/dashboard/query-state.tsx +142 -85
- package/src/{section → dashboard}/section.module.css +11 -0
- package/src/dashboard/section.tsx +287 -0
- package/src/dashboard/settings-section.tsx +88 -32
- package/src/dashboard/stat-card.module.css +46 -31
- package/src/dashboard/stat-card.module.css.d.ts +1 -0
- package/src/dashboard/stat-card.tsx +186 -128
- package/src/dashboard/stat-group.module.css +66 -0
- package/src/dashboard/stat-group.module.css.d.ts +8 -0
- package/src/dashboard/stat-group.tsx +74 -0
- package/src/dashboard/widget-grid.module.css +53 -0
- package/src/dashboard/widget-grid.module.css.d.ts +9 -0
- package/src/dashboard/widget-grid.tsx +127 -0
- package/src/{widget-header → dashboard}/widget-header.module.css +72 -7
- package/src/dashboard/widget-header.tsx +292 -0
- package/src/data/data-table.module.css +64 -0
- package/src/data/data-table.module.css.d.ts +11 -0
- package/src/data/data-table.tsx +921 -348
- package/src/data/query-branch.ts +53 -0
- package/src/data/table.ts +9 -23
- package/src/data/virtual-list.tsx +190 -56
- package/src/data/virtual.ts +8 -4
- package/src/dev-dock.tsx +159 -0
- package/src/format/date.ts +184 -0
- package/src/format/index.ts +69 -0
- package/src/format/measure.ts +31 -0
- package/src/format/number.ts +129 -0
- package/src/format/shared.ts +63 -0
- package/src/forms/FormErrorSummary.tsx +13 -6
- package/src/forms/create-form.ts +2 -2
- package/src/forms/field.ts +42 -13
- package/src/forms/form-layout.module.css +145 -0
- package/src/forms/form-layout.module.css.d.ts +20 -0
- package/src/forms/form-layout.tsx +305 -0
- package/src/forms/form-state.tsx +94 -0
- package/src/forms/index.ts +37 -7
- package/src/forms/use-field-array.ts +94 -0
- package/src/forms/use-form-draft.ts +145 -53
- package/src/forms/use-form-submit.ts +199 -0
- package/src/guard/index.ts +99 -70
- package/src/guard/types.ts +7 -10
- package/src/index.ts +92 -19
- package/src/notifications/bell.tsx +18 -4
- package/src/notifications/center.tsx +18 -5
- package/src/notifications/define-notifications.ts +64 -18
- package/src/notifications/index.ts +17 -8
- package/src/notifications/mount-guard.ts +39 -0
- package/src/notifications/notify.tsx +122 -3
- package/src/notifications/store.ts +7 -0
- package/src/{connectivity → provider}/connectivity-indicator.tsx +7 -2
- package/src/{connectivity → provider}/connectivity-provider.tsx +5 -2
- package/src/{connectivity/types.ts → provider/connectivity-types.ts} +5 -2
- package/src/provider/error-fallback.module.css +21 -0
- package/src/provider/error-fallback.module.css.d.ts +9 -0
- package/src/provider/index.tsx +116 -28
- package/src/provider/lab-theme.ts +2 -1
- package/src/{connectivity → provider}/use-connectivity.ts +4 -3
- package/src/query-client.ts +100 -0
- package/src/{query/devtools.tsx → query-devtools.tsx} +6 -4
- package/src/register.ts +20 -4
- package/src/router-tanstack/index.ts +4 -15
- package/src/router-tanstack/nav.ts +1 -1
- package/src/router-tanstack/search-store.ts +6 -14
- package/src/router-tanstack/use-nav.ts +19 -14
- package/src/shell/account-types.ts +5 -2
- package/src/shell/app-brand.module.css +109 -0
- package/src/shell/app-brand.module.css.d.ts +14 -0
- package/src/shell/app-brand.tsx +140 -0
- package/src/shell/app-breadcrumbs.tsx +20 -10
- package/src/shell/app-header.module.css +10 -5
- package/src/shell/app-main.module.css +118 -0
- package/src/shell/app-main.module.css.d.ts +11 -0
- package/src/shell/app-mobile-nav.module.css +48 -27
- package/src/shell/app-mobile-nav.module.css.d.ts +0 -1
- package/src/shell/app-mobile-nav.tsx +71 -12
- package/src/shell/app-sidebar-account.tsx +29 -6
- package/src/shell/app-sidebar.module.css +63 -96
- package/src/shell/app-sidebar.module.css.d.ts +0 -5
- package/src/shell/app-sidebar.tsx +61 -105
- package/src/shell/index.tsx +179 -93
- package/src/shell/isomorphic-layout-effect.ts +14 -0
- package/src/shell/mobile-nav-model.ts +67 -11
- package/src/shell/nav-count-badge.tsx +6 -1
- package/src/{nav/types.ts → shell/nav-types.ts} +15 -6
- package/src/shell/page-aside.module.css +174 -0
- package/src/shell/page-aside.module.css.d.ts +14 -0
- package/src/shell/page-aside.tsx +327 -0
- package/src/shell/page-bar.module.css +107 -13
- package/src/shell/page-bar.module.css.d.ts +4 -1
- package/src/shell/page-bar.tsx +294 -94
- package/src/shell/page-title.module.css +33 -0
- package/src/shell/page-title.module.css.d.ts +11 -0
- package/src/shell/page-title.tsx +57 -0
- package/src/shell/sidebar-block-model.ts +1 -1
- package/src/shell/sidebar-blocks.tsx +1 -1
- package/src/shell/sidebar-search.module.css +1 -1
- package/src/shell/sidebar-search.tsx +13 -4
- package/src/shell/use-breakpoint.ts +73 -0
- package/src/state/fields.ts +50 -53
- package/src/state/persisted-or-local.ts +50 -0
- package/src/state.ts +0 -9
- package/src/styles.css +36 -0
- package/src/surfaces.ts +72 -57
- package/src/theme/ctl-theme.tsx +10 -0
- package/src/theme/index.ts +136 -16
- package/src/theme/nav-link.module.css +37 -26
- package/src/theme/segmented-control.module.css +22 -0
- package/src/theme/segmented-control.module.css.d.ts +1 -0
- package/src/{theme-toggle/index.tsx → theme/theme-toggle.tsx} +11 -4
- package/src/theme-lab/boot.ts +140 -0
- package/src/theme-lab/index.tsx +21 -112
- package/src/tokens/index.ts +60 -6
- package/src/tokens/palette.ts +70 -30
- package/dist/agent/adapter.test.d.ts +0 -2
- package/dist/agent/adapter.test.d.ts.map +0 -1
- package/dist/agent/ai-sdk-transport.test.d.ts +0 -2
- package/dist/agent/ai-sdk-transport.test.d.ts.map +0 -1
- package/dist/agent/coalesce.test.d.ts +0 -2
- package/dist/agent/coalesce.test.d.ts.map +0 -1
- package/dist/agent/foreign.test.d.ts +0 -2
- package/dist/agent/foreign.test.d.ts.map +0 -1
- package/dist/agent/id.test.d.ts +0 -2
- package/dist/agent/id.test.d.ts.map +0 -1
- package/dist/agent/merge.test.d.ts +0 -2
- package/dist/agent/merge.test.d.ts.map +0 -1
- package/dist/agent/part-list.test.d.ts +0 -2
- package/dist/agent/part-list.test.d.ts.map +0 -1
- package/dist/agent/parts.test.d.ts +0 -2
- package/dist/agent/parts.test.d.ts.map +0 -1
- package/dist/agent/thread.test.d.ts +0 -2
- package/dist/agent/thread.test.d.ts.map +0 -1
- package/dist/agent/transport.test.d.ts +0 -2
- package/dist/agent/transport.test.d.ts.map +0 -1
- package/dist/agent/use-agent-stream.test.d.ts +0 -2
- package/dist/agent/use-agent-stream.test.d.ts.map +0 -1
- package/dist/agent/use-agent-thread-runs.foreign.test.d.ts +0 -2
- package/dist/agent/use-agent-thread-runs.foreign.test.d.ts.map +0 -1
- package/dist/agent/use-agent-thread-runs.resume.test.d.ts +0 -2
- package/dist/agent/use-agent-thread-runs.resume.test.d.ts.map +0 -1
- package/dist/agent/use-agent-thread-runs.test.d.ts +0 -2
- package/dist/agent/use-agent-thread-runs.test.d.ts.map +0 -1
- package/dist/agent/use-agent-thread-runs.wedge.test.d.ts +0 -2
- package/dist/agent/use-agent-thread-runs.wedge.test.d.ts.map +0 -1
- package/dist/agent-chat/composer.test.d.ts +0 -2
- package/dist/agent-chat/composer.test.d.ts.map +0 -1
- package/dist/agent-chat/relative-time.test.d.ts +0 -2
- package/dist/agent-chat/relative-time.test.d.ts.map +0 -1
- package/dist/agent-chat/thread-feed-row.test.d.ts +0 -2
- package/dist/agent-chat/thread-feed-row.test.d.ts.map +0 -1
- package/dist/agent-chat/thread-feed.test.d.ts +0 -2
- package/dist/agent-chat/thread-feed.test.d.ts.map +0 -1
- package/dist/agent-chat/thread-message.test.d.ts +0 -2
- package/dist/agent-chat/thread-message.test.d.ts.map +0 -1
- package/dist/agent-chat/thread-workspace.test.d.ts +0 -2
- package/dist/agent-chat/thread-workspace.test.d.ts.map +0 -1
- package/dist/agent-chat/tool-chip.test.d.ts +0 -2
- package/dist/agent-chat/tool-chip.test.d.ts.map +0 -1
- package/dist/agent-chat/virtualize.type-guard.test.d.ts +0 -2
- package/dist/agent-chat/virtualize.type-guard.test.d.ts.map +0 -1
- package/dist/charts/cursor/resolve.test.d.ts +0 -2
- package/dist/charts/cursor/resolve.test.d.ts.map +0 -1
- package/dist/charts/cursor/store.test.d.ts +0 -2
- package/dist/charts/cursor/store.test.d.ts.map +0 -1
- package/dist/charts/kinds/BandStrip.test.d.ts +0 -2
- package/dist/charts/kinds/BandStrip.test.d.ts.map +0 -1
- package/dist/charts/kinds/Bars.test.d.ts +0 -2
- package/dist/charts/kinds/Bars.test.d.ts.map +0 -1
- package/dist/charts/kinds/DualPanel.test.d.ts +0 -2
- package/dist/charts/kinds/DualPanel.test.d.ts.map +0 -1
- package/dist/charts/kinds/MirroredBars.test.d.ts +0 -2
- package/dist/charts/kinds/MirroredBars.test.d.ts.map +0 -1
- package/dist/charts/kinds/MultiLine.test.d.ts +0 -2
- package/dist/charts/kinds/MultiLine.test.d.ts.map +0 -1
- package/dist/charts/kinds/StackedArea.test.d.ts +0 -2
- package/dist/charts/kinds/StackedArea.test.d.ts.map +0 -1
- package/dist/charts/kinds/ZonedLine.test.d.ts +0 -2
- package/dist/charts/kinds/ZonedLine.test.d.ts.map +0 -1
- package/dist/charts/layout/auto-margin.test.d.ts +0 -2
- package/dist/charts/layout/auto-margin.test.d.ts.map +0 -1
- package/dist/charts/primitives/CartesianChart.test.d.ts +0 -2
- package/dist/charts/primitives/CartesianChart.test.d.ts.map +0 -1
- package/dist/charts/primitives/ChartCard.test.d.ts +0 -2
- package/dist/charts/primitives/ChartCard.test.d.ts.map +0 -1
- package/dist/charts/primitives/ChartFrame.test.d.ts +0 -2
- package/dist/charts/primitives/ChartFrame.test.d.ts.map +0 -1
- package/dist/charts/primitives/ChartLegend.test.d.ts +0 -2
- package/dist/charts/primitives/ChartLegend.test.d.ts.map +0 -1
- package/dist/charts/primitives/ChartTooltip.test.d.ts +0 -2
- package/dist/charts/primitives/ChartTooltip.test.d.ts.map +0 -1
- package/dist/charts/primitives/XZoneRects.test.d.ts +0 -2
- package/dist/charts/primitives/XZoneRects.test.d.ts.map +0 -1
- package/dist/charts/series.test.d.ts +0 -2
- package/dist/charts/series.test.d.ts.map +0 -1
- package/dist/charts/utils/domain.test.d.ts +0 -2
- package/dist/charts/utils/domain.test.d.ts.map +0 -1
- package/dist/charts/utils/measure-text.test.d.ts +0 -2
- package/dist/charts/utils/measure-text.test.d.ts.map +0 -1
- package/dist/cli/check-coverage.test.d.ts +0 -2
- package/dist/cli/check-coverage.test.d.ts.map +0 -1
- package/dist/cli/check-theme.test.d.ts +0 -2
- package/dist/cli/check-theme.test.d.ts.map +0 -1
- package/dist/cli/help.test.d.ts +0 -2
- package/dist/cli/help.test.d.ts.map +0 -1
- package/dist/cli/init-sync.test.d.ts +0 -2
- package/dist/cli/init-sync.test.d.ts.map +0 -1
- package/dist/cli/placement-engine.test.d.ts +0 -2
- package/dist/cli/placement-engine.test.d.ts.map +0 -1
- package/dist/cli/shipped-versions.test.d.ts +0 -2
- package/dist/cli/shipped-versions.test.d.ts.map +0 -1
- package/dist/cli/tarball-parity.test.d.ts +0 -2
- package/dist/cli/tarball-parity.test.d.ts.map +0 -1
- package/dist/cli/template-format.test.d.ts +0 -2
- package/dist/cli/template-format.test.d.ts.map +0 -1
- package/dist/cli/template-ledger.test.d.ts +0 -2
- package/dist/cli/template-ledger.test.d.ts.map +0 -1
- package/dist/cli/tokens-css.test.d.ts +0 -2
- package/dist/cli/tokens-css.test.d.ts.map +0 -1
- package/dist/cli/toolchain-wiring.test.d.ts +0 -2
- package/dist/cli/toolchain-wiring.test.d.ts.map +0 -1
- package/dist/cli/waiver-audit.test.d.ts +0 -2
- package/dist/cli/waiver-audit.test.d.ts.map +0 -1
- package/dist/connectivity/connectivity-indicator.d.ts +0 -11
- package/dist/connectivity/connectivity-indicator.d.ts.map +0 -1
- package/dist/connectivity/connectivity-provider.d.ts.map +0 -1
- package/dist/connectivity/index.d.ts +0 -13
- package/dist/connectivity/index.d.ts.map +0 -1
- package/dist/connectivity/index.js +0 -8
- package/dist/connectivity/types.d.ts.map +0 -1
- package/dist/connectivity/use-connectivity.d.ts +0 -3
- package/dist/connectivity/use-connectivity.d.ts.map +0 -1
- package/dist/content/article-actions.test.d.ts +0 -2
- package/dist/content/article-actions.test.d.ts.map +0 -1
- package/dist/content/article-model.test.d.ts +0 -2
- package/dist/content/article-model.test.d.ts.map +0 -1
- package/dist/content/block-split.test.d.ts +0 -2
- package/dist/content/block-split.test.d.ts.map +0 -1
- package/dist/content/heading-components.test.d.ts +0 -2
- package/dist/content/heading-components.test.d.ts.map +0 -1
- package/dist/content/markdown.test.d.ts +0 -2
- package/dist/content/markdown.test.d.ts.map +0 -1
- package/dist/content/prose.module.css.test.d.ts +0 -2
- package/dist/content/prose.module.css.test.d.ts.map +0 -1
- package/dist/content/sanitize.test.d.ts +0 -2
- package/dist/content/sanitize.test.d.ts.map +0 -1
- package/dist/content/slug.test.d.ts +0 -2
- package/dist/content/slug.test.d.ts.map +0 -1
- package/dist/controls/actions.test.d.ts +0 -2
- package/dist/controls/actions.test.d.ts.map +0 -1
- package/dist/controls/actions.type-guard.test.d.ts +0 -2
- package/dist/controls/actions.type-guard.test.d.ts.map +0 -1
- package/dist/controls/control-group.test.d.ts +0 -2
- package/dist/controls/control-group.test.d.ts.map +0 -1
- package/dist/controls/controls.router.test.d.ts +0 -2
- package/dist/controls/controls.router.test.d.ts.map +0 -1
- package/dist/controls/filter-set.test.d.ts +0 -2
- package/dist/controls/filter-set.test.d.ts.map +0 -1
- package/dist/controls/peer-boundary.test.d.ts +0 -2
- package/dist/controls/peer-boundary.test.d.ts.map +0 -1
- package/dist/controls/props-contract.test.d.ts +0 -2
- package/dist/controls/props-contract.test.d.ts.map +0 -1
- package/dist/controls/range-filter.type-guard.test.d.ts +0 -2
- package/dist/controls/range-filter.type-guard.test.d.ts.map +0 -1
- package/dist/controls/sync-button.test.d.ts +0 -2
- package/dist/controls/sync-button.test.d.ts.map +0 -1
- package/dist/dashboard/query-state.test.d.ts +0 -2
- package/dist/dashboard/query-state.test.d.ts.map +0 -1
- package/dist/dashboard/settings-section.test.d.ts +0 -2
- package/dist/dashboard/settings-section.test.d.ts.map +0 -1
- package/dist/dashboard/stat-card.test.d.ts +0 -2
- package/dist/dashboard/stat-card.test.d.ts.map +0 -1
- package/dist/data/data-table.test.d.ts +0 -2
- package/dist/data/data-table.test.d.ts.map +0 -1
- package/dist/data/index.d.ts +0 -38
- package/dist/data/index.d.ts.map +0 -1
- package/dist/data/index.js +0 -2
- package/dist/guard/check-source.test.d.ts +0 -2
- package/dist/guard/check-source.test.d.ts.map +0 -1
- package/dist/guard/grace.test.d.ts +0 -20
- package/dist/guard/grace.test.d.ts.map +0 -1
- package/dist/guard/guard-hook.test.d.ts +0 -2
- package/dist/guard/guard-hook.test.d.ts.map +0 -1
- package/dist/motion/index.d.ts.map +0 -1
- package/dist/nav/types.d.ts.map +0 -1
- package/dist/notifications/overlay.d.ts +0 -20
- package/dist/notifications/overlay.d.ts.map +0 -1
- package/dist/notifications/overlay.js +0 -12
- package/dist/provider/index.test.d.ts +0 -2
- package/dist/provider/index.test.d.ts.map +0 -1
- package/dist/provider/lab-theme.test.d.ts +0 -2
- package/dist/provider/lab-theme.test.d.ts.map +0 -1
- package/dist/query/devtools.d.ts.map +0 -1
- package/dist/query/error-message.d.ts +0 -41
- package/dist/query/error-message.d.ts.map +0 -1
- package/dist/query/error-message.js +0 -30
- package/dist/query/error-message.test.d.ts +0 -2
- package/dist/query/error-message.test.d.ts.map +0 -1
- package/dist/query/index.d.ts +0 -47
- package/dist/query/index.d.ts.map +0 -1
- package/dist/query/index.js +0 -56
- package/dist/router-tanstack/field.d.ts +0 -14
- package/dist/router-tanstack/field.d.ts.map +0 -1
- package/dist/router-tanstack/field.js +0 -4
- package/dist/router-tanstack/multi-search-param-store.d.ts +0 -43
- package/dist/router-tanstack/multi-search-param-store.d.ts.map +0 -1
- package/dist/router-tanstack/multi-search-param-store.js +0 -30
- package/dist/router-tanstack/multi-search-param-store.test.d.ts +0 -2
- package/dist/router-tanstack/multi-search-param-store.test.d.ts.map +0 -1
- package/dist/router-tanstack/nav.test.d.ts +0 -2
- package/dist/router-tanstack/nav.test.d.ts.map +0 -1
- package/dist/router-tanstack/search-param-store.d.ts +0 -42
- package/dist/router-tanstack/search-param-store.d.ts.map +0 -1
- package/dist/router-tanstack/search-param-store.js +0 -25
- package/dist/router-tanstack/search-param-store.test.d.ts +0 -2
- package/dist/router-tanstack/search-param-store.test.d.ts.map +0 -1
- package/dist/router-tanstack/search-store.router.test.d.ts +0 -2
- package/dist/router-tanstack/search-store.router.test.d.ts.map +0 -1
- package/dist/router-tanstack/search-store.test.d.ts +0 -2
- package/dist/router-tanstack/search-store.test.d.ts.map +0 -1
- package/dist/router-tanstack/use-nav.test.d.ts +0 -2
- package/dist/router-tanstack/use-nav.test.d.ts.map +0 -1
- package/dist/section/index.d.ts +0 -2
- package/dist/section/index.d.ts.map +0 -1
- package/dist/section/index.js +0 -4
- package/dist/section/section.d.ts +0 -24
- package/dist/section/section.d.ts.map +0 -1
- package/dist/section/section.js +0 -126
- package/dist/section/section.test.d.ts +0 -2
- package/dist/section/section.test.d.ts.map +0 -1
- package/dist/shell/app-mobile-nav.test.d.ts +0 -2
- package/dist/shell/app-mobile-nav.test.d.ts.map +0 -1
- package/dist/shell/app-sidebar.test.d.ts +0 -2
- package/dist/shell/app-sidebar.test.d.ts.map +0 -1
- package/dist/shell/index.test.d.ts +0 -2
- package/dist/shell/index.test.d.ts.map +0 -1
- package/dist/shell/mobile-nav-model.test.d.ts +0 -2
- package/dist/shell/mobile-nav-model.test.d.ts.map +0 -1
- package/dist/shell/page-bar.test.d.ts +0 -2
- package/dist/shell/page-bar.test.d.ts.map +0 -1
- package/dist/state/fields.test.d.ts +0 -2
- package/dist/state/fields.test.d.ts.map +0 -1
- package/dist/state/persisted.test.d.ts +0 -2
- package/dist/state/persisted.test.d.ts.map +0 -1
- package/dist/styles.floor.test.d.ts +0 -2
- package/dist/styles.floor.test.d.ts.map +0 -1
- package/dist/theme/border-coverage.test.d.ts +0 -18
- package/dist/theme/border-coverage.test.d.ts.map +0 -1
- package/dist/theme/contrast.test.d.ts +0 -2
- package/dist/theme/contrast.test.d.ts.map +0 -1
- package/dist/theme/ctl-theme.test.d.ts +0 -2
- package/dist/theme/ctl-theme.test.d.ts.map +0 -1
- package/dist/theme/ctl-tier-coverage.test.d.ts +0 -2
- package/dist/theme/ctl-tier-coverage.test.d.ts.map +0 -1
- package/dist/theme/density-option.test.d.ts +0 -2
- package/dist/theme/density-option.test.d.ts.map +0 -1
- package/dist/theme/density-relations.test.d.ts +0 -2
- package/dist/theme/density-relations.test.d.ts.map +0 -1
- package/dist/theme/derive-theme.test.d.ts +0 -2
- package/dist/theme/derive-theme.test.d.ts.map +0 -1
- package/dist/theme/divider-law.test.d.ts +0 -2
- package/dist/theme/divider-law.test.d.ts.map +0 -1
- package/dist/theme/fonts-option.test.d.ts +0 -2
- package/dist/theme/fonts-option.test.d.ts.map +0 -1
- package/dist/theme/icon-slot.test.d.ts +0 -2
- package/dist/theme/icon-slot.test.d.ts.map +0 -1
- package/dist/theme/layout-rhythm-css.test.d.ts +0 -2
- package/dist/theme/layout-rhythm-css.test.d.ts.map +0 -1
- package/dist/theme/radius-option.test.d.ts +0 -2
- package/dist/theme/radius-option.test.d.ts.map +0 -1
- package/dist/theme/radius.test.d.ts +0 -2
- package/dist/theme/radius.test.d.ts.map +0 -1
- package/dist/theme/shadow-surfaces.test.d.ts +0 -26
- package/dist/theme/shadow-surfaces.test.d.ts.map +0 -1
- package/dist/theme/spacing.test.d.ts +0 -2
- package/dist/theme/spacing.test.d.ts.map +0 -1
- package/dist/theme/use-basalt-spacing.test.d.ts +0 -2
- package/dist/theme/use-basalt-spacing.test.d.ts.map +0 -1
- package/dist/theme-lab/derive-controls.test.d.ts +0 -2
- package/dist/theme-lab/derive-controls.test.d.ts.map +0 -1
- package/dist/theme-lab/derive-state.test.d.ts +0 -2
- package/dist/theme-lab/derive-state.test.d.ts.map +0 -1
- package/dist/theme-lab/theme-lab.test.d.ts +0 -2
- package/dist/theme-lab/theme-lab.test.d.ts.map +0 -1
- package/dist/theme-toggle/index.d.ts +0 -8
- package/dist/theme-toggle/index.d.ts.map +0 -1
- package/dist/tokens/build-fonts-css.test.d.ts +0 -2
- package/dist/tokens/build-fonts-css.test.d.ts.map +0 -1
- package/dist/tokens/build-radius-css.test.d.ts +0 -2
- package/dist/tokens/build-radius-css.test.d.ts.map +0 -1
- package/dist/tokens/chart-margin.test.d.ts +0 -2
- package/dist/tokens/chart-margin.test.d.ts.map +0 -1
- package/dist/tokens/density.test.d.ts +0 -2
- package/dist/tokens/density.test.d.ts.map +0 -1
- package/dist/tokens/derive-radius.test.d.ts +0 -2
- package/dist/tokens/derive-radius.test.d.ts.map +0 -1
- package/dist/tokens/derive.test.d.ts +0 -2
- package/dist/tokens/derive.test.d.ts.map +0 -1
- package/dist/tokens/palette.test.d.ts +0 -2
- package/dist/tokens/palette.test.d.ts.map +0 -1
- package/dist/tokens/text-scale.test.d.ts +0 -2
- package/dist/tokens/text-scale.test.d.ts.map +0 -1
- package/dist/vite-color-mix.test.d.ts +0 -2
- package/dist/vite-color-mix.test.d.ts.map +0 -1
- package/dist/vite.test.d.ts +0 -2
- package/dist/vite.test.d.ts.map +0 -1
- package/dist/widget-header/delta-badge.d.ts +0 -13
- package/dist/widget-header/delta-badge.d.ts.map +0 -1
- package/dist/widget-header/delta-badge.js +0 -29
- package/dist/widget-header/delta-badge.test.d.ts +0 -2
- package/dist/widget-header/delta-badge.test.d.ts.map +0 -1
- package/dist/widget-header/index.d.ts +0 -3
- package/dist/widget-header/index.d.ts.map +0 -1
- package/dist/widget-header/index.js +0 -6
- package/dist/widget-header/no-mantine.test.d.ts +0 -2
- package/dist/widget-header/no-mantine.test.d.ts.map +0 -1
- package/dist/widget-header/widget-header.d.ts +0 -67
- package/dist/widget-header/widget-header.d.ts.map +0 -1
- package/dist/widget-header/widget-header.test.d.ts +0 -2
- package/dist/widget-header/widget-header.test.d.ts.map +0 -1
- package/src/agent/adapter.test.ts +0 -1640
- package/src/agent/ai-sdk-transport.test.ts +0 -658
- package/src/agent/coalesce.test.ts +0 -387
- package/src/agent/foreign.test.ts +0 -51
- package/src/agent/id.test.ts +0 -169
- package/src/agent/merge.test.ts +0 -206
- package/src/agent/part-list.test.tsx +0 -112
- package/src/agent/parts.test.ts +0 -720
- package/src/agent/thread.test.ts +0 -220
- package/src/agent/transport.test.ts +0 -70
- package/src/agent/use-agent-stream.test.tsx +0 -287
- package/src/agent/use-agent-thread-runs.foreign.test.tsx +0 -138
- package/src/agent/use-agent-thread-runs.resume.test.tsx +0 -294
- package/src/agent/use-agent-thread-runs.test.tsx +0 -1120
- package/src/agent/use-agent-thread-runs.wedge.test.tsx +0 -387
- package/src/agent-chat/composer.test.tsx +0 -924
- package/src/agent-chat/relative-time.test.ts +0 -95
- package/src/agent-chat/thread-feed-row.test.tsx +0 -510
- package/src/agent-chat/thread-feed.test.tsx +0 -241
- package/src/agent-chat/thread-message.test.tsx +0 -1285
- package/src/agent-chat/thread-workspace.test.tsx +0 -169
- package/src/agent-chat/tool-chip.test.tsx +0 -139
- package/src/agent-chat/virtualize.type-guard.test.ts +0 -49
- package/src/charts/cursor/resolve.test.ts +0 -151
- package/src/charts/cursor/store.test.ts +0 -64
- package/src/charts/kinds/BandStrip.test.tsx +0 -282
- package/src/charts/kinds/Bars.test.tsx +0 -183
- package/src/charts/kinds/DualPanel.test.tsx +0 -527
- package/src/charts/kinds/MirroredBars.test.tsx +0 -232
- package/src/charts/kinds/MultiLine.test.tsx +0 -218
- package/src/charts/kinds/StackedArea.test.tsx +0 -114
- package/src/charts/kinds/ZonedLine.test.tsx +0 -147
- package/src/charts/layout/auto-margin.test.ts +0 -42
- package/src/charts/primitives/CartesianChart.test.tsx +0 -647
- package/src/charts/primitives/ChartCard.test.tsx +0 -66
- package/src/charts/primitives/ChartFrame.test.tsx +0 -142
- package/src/charts/primitives/ChartLegend.test.tsx +0 -78
- package/src/charts/primitives/ChartTooltip.test.tsx +0 -44
- package/src/charts/primitives/XZoneRects.test.tsx +0 -123
- package/src/charts/series.test.ts +0 -112
- package/src/charts/utils/domain.test.ts +0 -54
- package/src/charts/utils/measure-text.test.ts +0 -36
- package/src/cli/check-coverage.test.ts +0 -297
- package/src/cli/check-theme.test.ts +0 -837
- package/src/cli/help.test.ts +0 -170
- package/src/cli/init-sync.test.ts +0 -343
- package/src/cli/placement-engine.test.ts +0 -298
- package/src/cli/shipped-versions.test.ts +0 -83
- package/src/cli/tarball-parity.test.ts +0 -44
- package/src/cli/template-format.test.ts +0 -64
- package/src/cli/template-ledger.test.ts +0 -219
- package/src/cli/tokens-css.test.ts +0 -249
- package/src/cli/toolchain-wiring.test.ts +0 -1358
- package/src/cli/waiver-audit.test.ts +0 -215
- package/src/connectivity/index.ts +0 -18
- package/src/content/article-actions.test.ts +0 -78
- package/src/content/article-model.test.ts +0 -119
- package/src/content/block-split.test.ts +0 -75
- package/src/content/heading-components.test.tsx +0 -65
- package/src/content/markdown.test.tsx +0 -603
- package/src/content/prose.module.css.test.ts +0 -39
- package/src/content/sanitize.test.ts +0 -659
- package/src/content/slug.test.ts +0 -62
- package/src/controls/actions.test.tsx +0 -252
- package/src/controls/actions.type-guard.test.ts +0 -63
- package/src/controls/control-group.test.tsx +0 -229
- package/src/controls/controls.router.test.tsx +0 -1053
- package/src/controls/filter-set.test.tsx +0 -472
- package/src/controls/peer-boundary.test.ts +0 -85
- package/src/controls/props-contract.test.tsx +0 -137
- package/src/controls/range-filter.type-guard.test.tsx +0 -122
- package/src/controls/sync-button.test.tsx +0 -183
- package/src/dashboard/query-state.test.tsx +0 -152
- package/src/dashboard/settings-section.test.tsx +0 -56
- package/src/dashboard/stat-card.test.tsx +0 -385
- package/src/data/data-table.test.tsx +0 -434
- package/src/data/index.ts +0 -41
- package/src/guard/check-source.test.ts +0 -3388
- package/src/guard/grace.test.ts +0 -158
- package/src/guard/guard-hook.test.ts +0 -203
- package/src/notifications/overlay.tsx +0 -64
- package/src/provider/index.test.tsx +0 -101
- package/src/provider/lab-theme.test.ts +0 -147
- package/src/query/error-message.test.ts +0 -67
- package/src/query/error-message.ts +0 -70
- package/src/query/index.ts +0 -97
- package/src/router-tanstack/field.ts +0 -35
- package/src/router-tanstack/multi-search-param-store.test.ts +0 -124
- package/src/router-tanstack/multi-search-param-store.ts +0 -106
- package/src/router-tanstack/nav.test.ts +0 -111
- package/src/router-tanstack/search-param-store.test.ts +0 -135
- package/src/router-tanstack/search-param-store.ts +0 -100
- package/src/router-tanstack/search-store.router.test.tsx +0 -882
- package/src/router-tanstack/search-store.test.ts +0 -578
- package/src/router-tanstack/use-nav.test.tsx +0 -187
- package/src/section/index.ts +0 -1
- package/src/section/section.test.tsx +0 -204
- package/src/section/section.tsx +0 -220
- package/src/shell/app-mobile-nav.test.tsx +0 -403
- package/src/shell/app-sidebar.test.tsx +0 -509
- package/src/shell/index.test.tsx +0 -428
- package/src/shell/mobile-nav-model.test.ts +0 -517
- package/src/shell/page-bar.test.tsx +0 -316
- package/src/state/fields.test.tsx +0 -512
- package/src/state/persisted.test.ts +0 -91
- package/src/styles.floor.test.ts +0 -36
- package/src/theme/border-coverage.test.ts +0 -272
- package/src/theme/contrast.test.ts +0 -354
- package/src/theme/ctl-theme.test.tsx +0 -275
- package/src/theme/ctl-tier-coverage.test.ts +0 -170
- package/src/theme/density-option.test.ts +0 -96
- package/src/theme/density-relations.test.ts +0 -506
- package/src/theme/derive-theme.test.ts +0 -101
- package/src/theme/divider-law.test.ts +0 -259
- package/src/theme/fonts-option.test.ts +0 -75
- package/src/theme/icon-slot.test.tsx +0 -275
- package/src/theme/layout-rhythm-css.test.ts +0 -72
- package/src/theme/radius-option.test.ts +0 -86
- package/src/theme/radius.test.ts +0 -142
- package/src/theme/shadow-surfaces.test.ts +0 -488
- package/src/theme/spacing.test.ts +0 -495
- package/src/theme/use-basalt-spacing.test.tsx +0 -55
- package/src/theme-lab/derive-controls.test.ts +0 -94
- package/src/theme-lab/derive-state.test.ts +0 -71
- package/src/theme-lab/theme-lab.test.ts +0 -46
- package/src/tokens/build-fonts-css.test.ts +0 -52
- package/src/tokens/build-radius-css.test.ts +0 -25
- package/src/tokens/chart-margin.test.ts +0 -39
- package/src/tokens/density.test.ts +0 -259
- package/src/tokens/derive-radius.test.ts +0 -62
- package/src/tokens/derive.test.ts +0 -30
- package/src/tokens/palette.test.ts +0 -114
- package/src/tokens/text-scale.test.ts +0 -51
- package/src/vite-color-mix.test.ts +0 -46
- package/src/vite.test.ts +0 -423
- package/src/widget-header/delta-badge.test.tsx +0 -60
- package/src/widget-header/delta-badge.tsx +0 -57
- package/src/widget-header/index.ts +0 -2
- package/src/widget-header/no-mantine.test.ts +0 -33
- package/src/widget-header/widget-header.test.tsx +0 -187
- package/src/widget-header/widget-header.tsx +0 -213
- /package/dist/{motion/index.js → common/motion.js} +0 -0
- /package/dist/{section → dashboard}/section.module.css.d.ts +0 -0
- /package/dist/{widget-header → dashboard}/widget-header.module.css.d.ts +0 -0
- /package/dist/{connectivity/types.js → provider/connectivity-types.js} +0 -0
- /package/dist/{connectivity → provider}/use-connectivity.js +0 -0
- /package/dist/{nav/types.js → shell/nav-types.js} +0 -0
- /package/src/{motion/index.ts → common/motion.ts} +0 -0
- /package/src/{section → dashboard}/section.module.css.d.ts +0 -0
- /package/src/{widget-header → dashboard}/widget-header.module.css.d.ts +0 -0
package/dist/cli/index.js
CHANGED
|
@@ -12,27 +12,15 @@ import {
|
|
|
12
12
|
} from "node:fs";
|
|
13
13
|
import { dirname, isAbsolute, relative, resolve } from "node:path";
|
|
14
14
|
import { fileURLToPath } from "node:url";
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
TOKENS_ONLY_DISABLED_KINDS,
|
|
25
|
-
unmatchedExemptPatterns
|
|
26
|
-
} from "../guard/index.js";
|
|
27
|
-
import { evaluateGuardHook } from "../guard/guard-hook.js";
|
|
28
|
-
import {
|
|
29
|
-
PLUGIN_RULE_ID_LIST,
|
|
30
|
-
RULE_NAMES,
|
|
31
|
-
SKILL_NAMES,
|
|
32
|
-
SURFACES,
|
|
33
|
-
TOKEN_LAYER_BOUNDARY_SURFACES
|
|
34
|
-
} from "../surfaces.js";
|
|
35
|
-
import { buildPaletteCss, deriveSpacing } from "../tokens/index.js";
|
|
15
|
+
import { DEFAULT_GUARD_CONFIG, guardWaiverHint } from "../guard/index.js";
|
|
16
|
+
import { RULE_NAMES, SKILL_NAMES } from "../surfaces.js";
|
|
17
|
+
import { checkTheme } from "./check-theme.js";
|
|
18
|
+
import { doctor } from "./doctor.js";
|
|
19
|
+
import { guardHook } from "./guard-hook.js";
|
|
20
|
+
import { COMMAND_OPTIONS, USAGE, unknownFlag } from "./help.js";
|
|
21
|
+
import { init } from "./init.js";
|
|
22
|
+
import { sync } from "./sync.js";
|
|
23
|
+
import { fontsCss, normalizeColorFunctions, tokensCss } from "./tokens-css.js";
|
|
36
24
|
function exemptRulePaths(entry) {
|
|
37
25
|
if (entry === void 0) return [];
|
|
38
26
|
return Array.isArray(entry) ? entry : entry.paths;
|
|
@@ -157,54 +145,13 @@ function shippedAssetPath(install, fromDir, asset) {
|
|
|
157
145
|
if (install.dir === null) return `./node_modules/basalt-ui/${asset}`;
|
|
158
146
|
return `${relativePosix(fromDir, install.dir)}/${asset}`;
|
|
159
147
|
}
|
|
160
|
-
function hasBasaltProject(dir) {
|
|
161
|
-
if (existsSync(resolve(dir, MANIFEST_PATH))) return true;
|
|
162
|
-
const raw = readIfExists(resolve(dir, "package.json"));
|
|
163
|
-
if (raw === null) return false;
|
|
164
|
-
try {
|
|
165
|
-
return JSON.parse(raw).basalt !== void 0;
|
|
166
|
-
} catch {
|
|
167
|
-
return false;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
function descendantProjects(cwd, depth = 2) {
|
|
171
|
-
if (depth === 0) return [];
|
|
172
|
-
const out = [];
|
|
173
|
-
for (const name of subdirNames(cwd, { skipDotDirs: true })) {
|
|
174
|
-
const dir = resolve(cwd, name);
|
|
175
|
-
if (hasBasaltProject(dir)) out.push(dir);
|
|
176
|
-
else out.push(...descendantProjects(dir, depth - 1));
|
|
177
|
-
}
|
|
178
|
-
return out;
|
|
179
|
-
}
|
|
180
|
-
function ascendantProject(cwd) {
|
|
181
|
-
const repoRoot = findRepoRoot(cwd);
|
|
182
|
-
if (repoRoot === cwd) return null;
|
|
183
|
-
let current = dirname(cwd);
|
|
184
|
-
for (; ; ) {
|
|
185
|
-
if (hasBasaltProject(current)) return current;
|
|
186
|
-
if (current === repoRoot) return null;
|
|
187
|
-
const parent = dirname(current);
|
|
188
|
-
if (parent === current) return null;
|
|
189
|
-
current = parent;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
148
|
function resolveProjectDir(cwd) {
|
|
193
149
|
const override = process.env["BASALT_CWD"];
|
|
194
|
-
if (override
|
|
195
|
-
|
|
196
|
-
return { dir, relocatedFrom: dir === cwd ? null : cwd, ambiguous: null };
|
|
150
|
+
if (override === void 0 || override.length === 0) {
|
|
151
|
+
return { dir: cwd, relocatedFrom: null };
|
|
197
152
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const candidates = declared.length > 0 ? declared : descendantProjects(cwd);
|
|
201
|
-
if (candidates.length === 1) {
|
|
202
|
-
return { dir: candidates[0], relocatedFrom: cwd, ambiguous: null };
|
|
203
|
-
}
|
|
204
|
-
if (candidates.length > 1) return { dir: cwd, relocatedFrom: null, ambiguous: candidates };
|
|
205
|
-
const above = ascendantProject(cwd);
|
|
206
|
-
if (above !== null) return { dir: above, relocatedFrom: cwd, ambiguous: null };
|
|
207
|
-
return { dir: cwd, relocatedFrom: null, ambiguous: null };
|
|
153
|
+
const dir = isAbsolute(override) ? override : resolve(cwd, override);
|
|
154
|
+
return { dir, relocatedFrom: dir === cwd ? null : cwd };
|
|
208
155
|
}
|
|
209
156
|
function declaredProfile(cfg, flags) {
|
|
210
157
|
if (flags.includes("--tokens-only")) return "tokens-only";
|
|
@@ -214,18 +161,6 @@ function declaredProfile(cfg, flags) {
|
|
|
214
161
|
function conflictingProfileFlags(flags) {
|
|
215
162
|
return flags.includes("--tokens-only") && flags.includes("--framework");
|
|
216
163
|
}
|
|
217
|
-
function workspaceHasMantine(cwd) {
|
|
218
|
-
if (hasDependency(cwd, "@mantine/core")) return true;
|
|
219
|
-
return collectWorkspacePackages(cwd).packages.some(
|
|
220
|
-
(pkg) => hasDependency(pkg.dir, "@mantine/core")
|
|
221
|
-
);
|
|
222
|
-
}
|
|
223
|
-
function inferredProfile(cwd, cfg, flags) {
|
|
224
|
-
if (flags.includes("--tokens-only") || flags.includes("--framework") || cfg.profile !== void 0) {
|
|
225
|
-
return declaredProfile(cfg, flags);
|
|
226
|
-
}
|
|
227
|
-
return !existsSync(resolve(cwd, MANIFEST_PATH)) && !workspaceHasMantine(cwd) ? "tokens-only" : "framework";
|
|
228
|
-
}
|
|
229
164
|
function scannableFiles(cwd, cfg) {
|
|
230
165
|
const exempt = new Set(cfg.exempt ?? defaultExempt(cfg));
|
|
231
166
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -261,354 +196,6 @@ function waiverHintFor(relPath, profile) {
|
|
|
261
196
|
}
|
|
262
197
|
return `${guardWaiverHint(PLAIN_JSON_HINT_PATH)} (basaltAppPlugin would emit the file instead, but it needs basalt's Vite/React layer, which a tokens-only consumer has opted out of.)`;
|
|
263
198
|
}
|
|
264
|
-
function checkTheme(invocationCwd = process.cwd(), flags = []) {
|
|
265
|
-
if (conflictingProfileFlags(flags)) {
|
|
266
|
-
console.error(
|
|
267
|
-
"basalt-ui check-theme: --tokens-only and --framework are alternatives \u2014 pass one."
|
|
268
|
-
);
|
|
269
|
-
return 1;
|
|
270
|
-
}
|
|
271
|
-
const project = resolveProjectDir(invocationCwd);
|
|
272
|
-
if (project.ambiguous !== null) {
|
|
273
|
-
console.error(
|
|
274
|
-
`\u2716 basalt-ui check-theme: no basalt config at ${invocationCwd}, and ${project.ambiguous.length} packages below it carry one (${project.ambiguous.map((d) => relativePosix(invocationCwd, d)).join(", ")}) \u2014 run it from one of them, or set BASALT_CWD to pick.`
|
|
275
|
-
);
|
|
276
|
-
return 1;
|
|
277
|
-
}
|
|
278
|
-
const cwd = project.dir;
|
|
279
|
-
if (project.relocatedFrom !== null) {
|
|
280
|
-
console.log(
|
|
281
|
-
`basalt-ui check-theme: no basalt config at ${project.relocatedFrom} \u2014 running in ${relativePosix(project.relocatedFrom, cwd)}, where it lives.`
|
|
282
|
-
);
|
|
283
|
-
}
|
|
284
|
-
const cfg = readBasaltConfig(cwd);
|
|
285
|
-
const roots = resolveRoots(cfg);
|
|
286
|
-
const profile = declaredProfile(cfg, flags);
|
|
287
|
-
if (profile === "tokens-only") {
|
|
288
|
-
console.log(
|
|
289
|
-
`basalt-ui check-theme: tokens-only profile \u2014 ${TOKENS_ONLY_DISABLED_KINDS.size} Mantine-coupled kinds are off; the color and typography kinds still apply. Pass --framework to force the full set.`
|
|
290
|
-
);
|
|
291
|
-
}
|
|
292
|
-
const guardCfg = {
|
|
293
|
-
spacingSteps: cfg.spacingSteps ?? DEFAULT_GUARD_CONFIG.spacingSteps,
|
|
294
|
-
rawRadius: cfg.rawRadius ?? DEFAULT_GUARD_CONFIG.rawRadius,
|
|
295
|
-
forbiddenAccents: cfg.forbiddenAccents ?? DEFAULT_GUARD_CONFIG.forbiddenAccents,
|
|
296
|
-
mantineShadeIndex: cfg.mantineShadeIndex ?? DEFAULT_GUARD_CONFIG.mantineShadeIndex,
|
|
297
|
-
rawSurface: cfg.rawSurface ?? DEFAULT_GUARD_CONFIG.rawSurface,
|
|
298
|
-
cardWithBorder: cfg.cardWithBorder ?? DEFAULT_GUARD_CONFIG.cardWithBorder,
|
|
299
|
-
offSystemSurfaceVar: cfg.offSystemSurfaceVar ?? DEFAULT_GUARD_CONFIG.offSystemSurfaceVar,
|
|
300
|
-
rawHtmlLayout: cfg.rawHtmlLayout ?? DEFAULT_GUARD_CONFIG.rawHtmlLayout,
|
|
301
|
-
inlineSpacing: cfg.inlineSpacing ?? DEFAULT_GUARD_CONFIG.inlineSpacing,
|
|
302
|
-
inlineDisplay: cfg.inlineDisplay ?? DEFAULT_GUARD_CONFIG.inlineDisplay,
|
|
303
|
-
rawVisxAxis: cfg.rawVisxAxis ?? DEFAULT_GUARD_CONFIG.rawVisxAxis,
|
|
304
|
-
rawMotionValue: cfg.rawMotionValue ?? DEFAULT_GUARD_CONFIG.rawMotionValue,
|
|
305
|
-
unframedChart: cfg.unframedChart ?? DEFAULT_GUARD_CONFIG.unframedChart,
|
|
306
|
-
chartMissingAriaLabel: cfg.chartMissingAriaLabel ?? DEFAULT_GUARD_CONFIG.chartMissingAriaLabel,
|
|
307
|
-
rawFormControl: cfg.rawFormControl ?? DEFAULT_GUARD_CONFIG.rawFormControl,
|
|
308
|
-
sub16InputFont: cfg.sub16InputFont ?? DEFAULT_GUARD_CONFIG.sub16InputFont,
|
|
309
|
-
allowComment: "theme-allow",
|
|
310
|
-
exemptRules: resolveExemptRules(cfg),
|
|
311
|
-
severity: cfg.severity ?? DEFAULT_GUARD_CONFIG.severity,
|
|
312
|
-
...profile === "tokens-only" ? { profile: "tokens-only" } : {}
|
|
313
|
-
};
|
|
314
|
-
const findings = [];
|
|
315
|
-
const scanned = scannableFiles(cwd, cfg);
|
|
316
|
-
for (const rel of scanned) {
|
|
317
|
-
findings.push(...checkSource(readFileSync(resolve(cwd, rel), "utf8"), rel, guardCfg));
|
|
318
|
-
}
|
|
319
|
-
if (scanned.length === 0) {
|
|
320
|
-
if (cfg.roots === void 0) {
|
|
321
|
-
console.error(
|
|
322
|
-
`\u2716 basalt-ui check-theme: 0 files scanned \u2014 no "basalt.roots" configured in package.json, and the built-in default roots (${DEFAULT_ROOTS.join(", ")}) matched zero files. Set "basalt": { "roots": [...] } in package.json to point at your source directories.`
|
|
323
|
-
);
|
|
324
|
-
} else {
|
|
325
|
-
console.error(
|
|
326
|
-
`\u2716 basalt-ui check-theme: 0 files scanned \u2014 the configured "basalt.roots" (${roots.join(", ")}) matched zero files. Check the paths in "basalt.roots" in package.json.`
|
|
327
|
-
);
|
|
328
|
-
}
|
|
329
|
-
return 1;
|
|
330
|
-
}
|
|
331
|
-
if (flags.includes("--audit-allows")) return auditAllows(cwd, cfg, guardCfg, scanned);
|
|
332
|
-
for (const unmatched of unmatchedExemptPatterns(guardCfg, scanned)) {
|
|
333
|
-
console.error(
|
|
334
|
-
`\u26A0 basalt-ui check-theme: "basalt.exemptRules" entry ${unmatched.kind}: "${unmatched.pattern}" ` + (unmatched.reason === "unknown-kind" ? "names no guard kind \u2014 it exempts nothing at all (a typo, or a kind that was renamed)." : "matched none of the scanned files \u2014 it suppresses nothing, and reads as coverage in a config review while enforcing as much as an empty object.")
|
|
335
|
-
);
|
|
336
|
-
}
|
|
337
|
-
if (findings.length === 0) {
|
|
338
|
-
console.log("\u2713 Theme guard: no off-palette colors.");
|
|
339
|
-
return 0;
|
|
340
|
-
}
|
|
341
|
-
const errors = findings.filter((f) => f.severity === "error");
|
|
342
|
-
const warnings = findings.filter((f) => f.severity === "warn");
|
|
343
|
-
const report = (group, heading) => {
|
|
344
|
-
const byFile = /* @__PURE__ */ new Map();
|
|
345
|
-
for (const f of group) {
|
|
346
|
-
const list = byFile.get(f.relPath) ?? [];
|
|
347
|
-
list.push(f);
|
|
348
|
-
byFile.set(f.relPath, list);
|
|
349
|
-
}
|
|
350
|
-
console.error(heading);
|
|
351
|
-
for (const [file, vs] of [...byFile].toSorted()) {
|
|
352
|
-
console.error(file);
|
|
353
|
-
for (const v of vs.toSorted((a, b) => a.line - b.line)) {
|
|
354
|
-
console.error(` ${String(v.line).padStart(4)} ${v.kind.padEnd(22)} ${v.text}`);
|
|
355
|
-
}
|
|
356
|
-
console.error("");
|
|
357
|
-
}
|
|
358
|
-
};
|
|
359
|
-
if (warnings.length > 0) {
|
|
360
|
-
report(
|
|
361
|
-
warnings,
|
|
362
|
-
// Deliberately neutral about WHY a kind is a warning. Two origins reach this block — a kind
|
|
363
|
-
// in its grace minor, and a consumer's own `basalt.severity` override — and the finding
|
|
364
|
-
// doesn't carry which. Promising promotion next minor would be wrong for the second.
|
|
365
|
-
`\u26A0 Theme guard: ${warnings.length} warning(s) \u2014 reported, not fatal. A kind warns while it is in its grace minor, or because \`basalt.severity\` turned it down; fix on your own schedule.
|
|
366
|
-
`
|
|
367
|
-
);
|
|
368
|
-
}
|
|
369
|
-
if (errors.length > 0) {
|
|
370
|
-
report(errors, `\u2716 Theme guard: ${errors.length} off-palette / off-identity violation(s)
|
|
371
|
-
`);
|
|
372
|
-
}
|
|
373
|
-
const presentKinds = [...new Set(findings.map((f) => f.kind))].toSorted();
|
|
374
|
-
const waiverHints = [
|
|
375
|
-
...new Set(findings.map((f) => waiverHintFor(f.relPath, profile)))
|
|
376
|
-
].toSorted();
|
|
377
|
-
console.error(["Fix:", ...presentKinds.map(guardKindRemedy), ...waiverHints].join(" "));
|
|
378
|
-
if (findings.some((f) => SERIES_MODULE_HINT_RE.test(f.relPath))) {
|
|
379
|
-
console.error(
|
|
380
|
-
'Hint: if a flagged file IS your palette/series source, exempt it via "basalt": { "exempt": ["<path>"] } in package.json.'
|
|
381
|
-
);
|
|
382
|
-
}
|
|
383
|
-
return errors.length > 0 ? 1 : 0;
|
|
384
|
-
}
|
|
385
|
-
function findingKey(f) {
|
|
386
|
-
return `${f.relPath}:${f.line}:${f.kind}`;
|
|
387
|
-
}
|
|
388
|
-
function resolveToolBin(dir, name) {
|
|
389
|
-
let current = dir;
|
|
390
|
-
for (; ; ) {
|
|
391
|
-
const candidate = resolve(current, "node_modules/.bin", name);
|
|
392
|
-
if (existsSync(candidate)) return candidate;
|
|
393
|
-
const parent = resolve(current, "..");
|
|
394
|
-
if (parent === current) break;
|
|
395
|
-
current = parent;
|
|
396
|
-
}
|
|
397
|
-
const probe = spawnSync(name, ["--version"], { stdio: "ignore", timeout: 1e4 });
|
|
398
|
-
return probe.error === void 0 && probe.status === 0 ? name : null;
|
|
399
|
-
}
|
|
400
|
-
function diagnosticCounts(diagnostics, filename) {
|
|
401
|
-
const counts = /* @__PURE__ */ new Map();
|
|
402
|
-
for (const d of diagnostics) {
|
|
403
|
-
if (d.filename !== filename) continue;
|
|
404
|
-
const code = typeof d.code === "string" ? d.code : "";
|
|
405
|
-
const rule = /^basalt\((.+)\)$/.exec(code)?.[1];
|
|
406
|
-
if (rule === void 0) continue;
|
|
407
|
-
const line = d.labels?.[0]?.span?.line;
|
|
408
|
-
const key = `${typeof line === "number" ? line : 0}:${rule}`;
|
|
409
|
-
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
410
|
-
}
|
|
411
|
-
return counts;
|
|
412
|
-
}
|
|
413
|
-
function probePluginRules(cwd, guardCfg, probes, sources) {
|
|
414
|
-
if (probes.length === 0) return /* @__PURE__ */ new Map();
|
|
415
|
-
const bin = resolveToolBin(cwd, "oxlint");
|
|
416
|
-
if (bin === null) return null;
|
|
417
|
-
const written = [];
|
|
418
|
-
const probePaths = /* @__PURE__ */ new Map();
|
|
419
|
-
try {
|
|
420
|
-
for (const probe of probes) {
|
|
421
|
-
const probed = neutralizeAllowAnnotation(sources.get(probe.rel) ?? "", probe.line, guardCfg);
|
|
422
|
-
const dot = probe.rel.lastIndexOf(".");
|
|
423
|
-
const rel = dot === -1 ? `${probe.rel}.basalt-audit-${probe.slot}` : `${probe.rel.slice(0, dot)}.basalt-audit-${probe.slot}${probe.rel.slice(dot)}`;
|
|
424
|
-
const abs = resolve(cwd, rel);
|
|
425
|
-
if (existsSync(abs)) continue;
|
|
426
|
-
writeFileSync(abs, probed, "utf8");
|
|
427
|
-
written.push(abs);
|
|
428
|
-
probePaths.set(probe.slot, rel);
|
|
429
|
-
}
|
|
430
|
-
if (probePaths.size === 0) return null;
|
|
431
|
-
const targets = [...new Set(probes.map((p) => p.rel)), ...probePaths.values()];
|
|
432
|
-
const run2 = spawnSync(bin, ["--format=json", "--no-error-on-unmatched-pattern", ...targets], {
|
|
433
|
-
cwd,
|
|
434
|
-
encoding: "utf8",
|
|
435
|
-
timeout: 12e4,
|
|
436
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
437
|
-
});
|
|
438
|
-
if (run2.error !== void 0 || typeof run2.stdout !== "string") return null;
|
|
439
|
-
let diagnostics;
|
|
440
|
-
try {
|
|
441
|
-
diagnostics = JSON.parse(run2.stdout).diagnostics ?? [];
|
|
442
|
-
} catch {
|
|
443
|
-
return null;
|
|
444
|
-
}
|
|
445
|
-
const verdicts = /* @__PURE__ */ new Map();
|
|
446
|
-
for (const probe of probes) {
|
|
447
|
-
const probeRel = probePaths.get(probe.slot);
|
|
448
|
-
if (probeRel === void 0) continue;
|
|
449
|
-
const before = diagnosticCounts(diagnostics, probe.rel);
|
|
450
|
-
const after = diagnosticCounts(diagnostics, probeRel);
|
|
451
|
-
const revealed = [];
|
|
452
|
-
for (const [key, count] of after) {
|
|
453
|
-
if (count <= (before.get(key) ?? 0)) continue;
|
|
454
|
-
const [line = "", rule = ""] = key.split(":");
|
|
455
|
-
revealed.push(`${rule}@${line}`);
|
|
456
|
-
}
|
|
457
|
-
verdicts.set(probe.slot, revealed.toSorted());
|
|
458
|
-
}
|
|
459
|
-
return verdicts;
|
|
460
|
-
} finally {
|
|
461
|
-
for (const abs of written) {
|
|
462
|
-
try {
|
|
463
|
-
unlinkSync(abs);
|
|
464
|
-
} catch {
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
function auditAllows(cwd, cfg, guardCfg, scanned) {
|
|
470
|
-
const token = guardCfg.allowComment;
|
|
471
|
-
const lines = [`
|
|
472
|
-
basalt-ui check-theme --audit-allows \u2014 ${cwd}
|
|
473
|
-
`];
|
|
474
|
-
let dead = 0;
|
|
475
|
-
let live = 0;
|
|
476
|
-
let outOfReach = 0;
|
|
477
|
-
let unaccountable = 0;
|
|
478
|
-
const waiverLines = [];
|
|
479
|
-
const pluginProbes = [];
|
|
480
|
-
const sources = /* @__PURE__ */ new Map();
|
|
481
|
-
for (const rel of scanned) sources.set(rel, readFileSync(resolve(cwd, rel), "utf8"));
|
|
482
|
-
for (const rel of scanned) {
|
|
483
|
-
const text = sources.get(rel) ?? "";
|
|
484
|
-
if (!text.includes(token)) continue;
|
|
485
|
-
const baseline = checkSource(text, rel, guardCfg);
|
|
486
|
-
const baselineKeys = new Set(
|
|
487
|
-
baseline.filter((f) => f.kind !== "theme-allow-unscoped").map(findingKey)
|
|
488
|
-
);
|
|
489
|
-
const unscopedAt = new Map(
|
|
490
|
-
baseline.filter((f) => f.kind === "theme-allow-unscoped").map((f) => [f.line, f.token])
|
|
491
|
-
);
|
|
492
|
-
for (const site of findAllowAnnotations(text, rel, guardCfg)) {
|
|
493
|
-
const revealed = checkSource(
|
|
494
|
-
neutralizeAllowAnnotation(text, site.line, guardCfg),
|
|
495
|
-
rel,
|
|
496
|
-
guardCfg
|
|
497
|
-
).filter((f) => f.kind !== "theme-allow-unscoped" && !baselineKeys.has(findingKey(f))).map((f) => `${f.kind}@${f.line}`);
|
|
498
|
-
const note = unscopedAt.get(site.line);
|
|
499
|
-
if (note !== void 0) unaccountable++;
|
|
500
|
-
const label = ` ${rel}:${site.line}`.padEnd(52);
|
|
501
|
-
const suffix = note === void 0 ? "" : ` [${note}]`;
|
|
502
|
-
if (revealed.length > 0) {
|
|
503
|
-
live++;
|
|
504
|
-
waiverLines.push(`${label} suppresses ${[...new Set(revealed)].join(", ")}${suffix}`);
|
|
505
|
-
continue;
|
|
506
|
-
}
|
|
507
|
-
if (site.pluginRules.length > 0 && site.guardKinds.length === 0) {
|
|
508
|
-
pluginProbes.push({
|
|
509
|
-
slot: waiverLines.length,
|
|
510
|
-
rel,
|
|
511
|
-
line: site.line,
|
|
512
|
-
ids: site.pluginRules,
|
|
513
|
-
site: label,
|
|
514
|
-
suffix
|
|
515
|
-
});
|
|
516
|
-
waiverLines.push("");
|
|
517
|
-
continue;
|
|
518
|
-
}
|
|
519
|
-
if (site.guardKinds.length === 0 && site.unknownRules.length > 0) {
|
|
520
|
-
outOfReach++;
|
|
521
|
-
waiverLines.push(
|
|
522
|
-
`${label} scoped to ${site.unknownRules.join(", ")} \u2014 names no guard kind and no plugin rule, so this audit cannot judge it (most likely a typo, which theme-allow-unscoped reports)${suffix}`
|
|
523
|
-
);
|
|
524
|
-
continue;
|
|
525
|
-
}
|
|
526
|
-
dead++;
|
|
527
|
-
waiverLines.push(`${label} SUPPRESSES NOTHING \u2014 dead, delete it${suffix}`);
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
const pluginVerdicts = probePluginRules(cwd, guardCfg, pluginProbes, sources);
|
|
531
|
-
for (const probe of pluginProbes) {
|
|
532
|
-
const revealed = pluginVerdicts?.get(probe.slot);
|
|
533
|
-
if (revealed === void 0) {
|
|
534
|
-
outOfReach++;
|
|
535
|
-
waiverLines[probe.slot] = `${probe.site} scoped to ${probe.ids.join(", ")} \u2014 an oxlint plugin rule, and oxlint could not be run here, so this audit cannot judge it${probe.suffix}`;
|
|
536
|
-
continue;
|
|
537
|
-
}
|
|
538
|
-
if (revealed.length > 0) {
|
|
539
|
-
live++;
|
|
540
|
-
waiverLines[probe.slot] = `${probe.site} suppresses ${revealed.join(", ")} (oxlint)${probe.suffix}`;
|
|
541
|
-
continue;
|
|
542
|
-
}
|
|
543
|
-
dead++;
|
|
544
|
-
waiverLines[probe.slot] = `${probe.site} SUPPRESSES NOTHING \u2014 dead, delete it (proved by re-running oxlint over the file with this annotation neutralized)${probe.suffix}`;
|
|
545
|
-
}
|
|
546
|
-
lines.push(`${token} annotations (${waiverLines.length}):`);
|
|
547
|
-
lines.push(...waiverLines.length === 0 ? [" (none)"] : waiverLines);
|
|
548
|
-
const exemptLines = [];
|
|
549
|
-
let exemptCount = 0;
|
|
550
|
-
const declared = Object.entries(cfg.exemptRules ?? {});
|
|
551
|
-
if (declared.length > 0) {
|
|
552
|
-
const baselineKeys = /* @__PURE__ */ new Set();
|
|
553
|
-
for (const rel of scanned) {
|
|
554
|
-
for (const f of checkSource(sources.get(rel) ?? "", rel, guardCfg))
|
|
555
|
-
baselineKeys.add(findingKey(f));
|
|
556
|
-
}
|
|
557
|
-
for (const [kind, entry] of declared) {
|
|
558
|
-
const reason = exemptRuleReason(entry);
|
|
559
|
-
for (const pattern of exemptRulePaths(entry)) {
|
|
560
|
-
exemptCount++;
|
|
561
|
-
const probeCfg = {
|
|
562
|
-
...guardCfg,
|
|
563
|
-
exemptRules: {
|
|
564
|
-
...guardCfg.exemptRules,
|
|
565
|
-
[kind]: exemptRulePaths(entry).filter((p) => p !== pattern)
|
|
566
|
-
}
|
|
567
|
-
};
|
|
568
|
-
const revealed = /* @__PURE__ */ new Set();
|
|
569
|
-
for (const rel of scanned) {
|
|
570
|
-
for (const f of checkSource(sources.get(rel) ?? "", rel, probeCfg)) {
|
|
571
|
-
if (!baselineKeys.has(findingKey(f))) revealed.add(f.relPath);
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
const site = ` ${kind}: "${pattern}"`.padEnd(52);
|
|
575
|
-
const why = reason === null ? " [no reason recorded]" : ` \u2014 ${reason}`;
|
|
576
|
-
if (revealed.size === 0) {
|
|
577
|
-
dead++;
|
|
578
|
-
const unmatched = unmatchedExemptPatterns(guardCfg, scanned).find(
|
|
579
|
-
(u) => u.kind === kind && u.pattern === pattern
|
|
580
|
-
);
|
|
581
|
-
exemptLines.push(
|
|
582
|
-
`${site} SUPPRESSES NOTHING \u2014 ` + (unmatched === void 0 ? "the files it matches have no such finding; delete it" : unmatched.reason === "unknown-kind" ? "it names no guard kind (a typo, or a renamed kind)" : "it matches no scanned file at all") + why
|
|
583
|
-
);
|
|
584
|
-
} else {
|
|
585
|
-
live++;
|
|
586
|
-
exemptLines.push(
|
|
587
|
-
`${site} suppresses findings in ${[...revealed].toSorted().join(", ")}${why}`
|
|
588
|
-
);
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
lines.push(`
|
|
594
|
-
basalt.exemptRules entries (${exemptCount}):`);
|
|
595
|
-
lines.push(...exemptLines.length === 0 ? [" (none)"] : exemptLines);
|
|
596
|
-
lines.push(
|
|
597
|
-
`
|
|
598
|
-
${live} live, ${dead} dead, ${outOfReach} unjudgeable, ${unaccountable} unaccountable (reported as theme-allow-unscoped by a normal run).`
|
|
599
|
-
);
|
|
600
|
-
const includes = cfg.include ?? [];
|
|
601
|
-
lines.push(
|
|
602
|
-
`Scope: the ${scanned.length} file(s) check-theme scans \u2014 everything under basalt.roots (${resolveRoots(cfg).join(", ")}), plus each root's sibling index.html and public/ tree, ` + (includes.length === 0 ? "plus anything named in basalt.include (none). " : `plus basalt.include (${includes.join(", ")}). `) + "A waiver in a file none of those reach is not audited \u2014 widen roots, or name it in basalt.include, or accept that nothing polices that file."
|
|
603
|
-
);
|
|
604
|
-
if (dead > 0) {
|
|
605
|
-
lines.push(
|
|
606
|
-
"A dead waiver is not harmless: it is an exception nobody re-checked, and it will silently cover the next real finding on that line."
|
|
607
|
-
);
|
|
608
|
-
}
|
|
609
|
-
console.log(lines.join("\n"));
|
|
610
|
-
return dead > 0 ? 1 : 0;
|
|
611
|
-
}
|
|
612
199
|
const BLOCK_BEGIN_PREFIX = "<!-- basalt:begin";
|
|
613
200
|
const BLOCK_END = "<!-- basalt:end -->";
|
|
614
201
|
const MANIFEST_PATH = ".basalt/manifest.json";
|
|
@@ -887,16 +474,6 @@ function readManifest(cwd) {
|
|
|
887
474
|
return { version: 1, files: {} };
|
|
888
475
|
}
|
|
889
476
|
}
|
|
890
|
-
const LEGACY_OXFMT_DEST = "oxfmt.json";
|
|
891
|
-
function migrateLegacyOxfmt(cwd, pkgRoot, manifest) {
|
|
892
|
-
if (!(LEGACY_OXFMT_DEST in manifest.files)) return;
|
|
893
|
-
delete manifest.files[LEGACY_OXFMT_DEST];
|
|
894
|
-
const legacyAbs = resolve(cwd, LEGACY_OXFMT_DEST);
|
|
895
|
-
const onDisk = readIfExists(legacyAbs);
|
|
896
|
-
if (onDisk === null) return;
|
|
897
|
-
const shipped = readSource(pkgRoot, "configs/oxfmt.json");
|
|
898
|
-
if (shipped !== null && onDisk === shipped) unlinkSync(legacyAbs);
|
|
899
|
-
}
|
|
900
477
|
const RETIREABLE_DEST_PATTERNS = [
|
|
901
478
|
/^\.claude\/rules\/basalt-[a-z0-9-]+\.md$/,
|
|
902
479
|
/^\.claude\/skills\/basalt-[a-z0-9-]+\/SKILL\.md$/
|
|
@@ -946,16 +523,6 @@ function removeEmptyParentDir(fileAbs) {
|
|
|
946
523
|
} catch {
|
|
947
524
|
}
|
|
948
525
|
}
|
|
949
|
-
const DESIGN_VERSION_LINE = /^(> Managed by basalt-ui) \(\d+\.\d+\.\d+\)\./m;
|
|
950
|
-
function migrateDesignVersionLine(cwd) {
|
|
951
|
-
const dest = resolve(cwd, "DESIGN.md");
|
|
952
|
-
const onDisk = readIfExists(dest);
|
|
953
|
-
if (onDisk === null || !DESIGN_VERSION_LINE.test(onDisk)) return;
|
|
954
|
-
writeFileEnsuringDir(
|
|
955
|
-
dest,
|
|
956
|
-
onDisk.replace(DESIGN_VERSION_LINE, "$1 \u2014 run `basalt-ui doctor` for the version.")
|
|
957
|
-
);
|
|
958
|
-
}
|
|
959
526
|
function writeFileEnsuringDir(abs, content) {
|
|
960
527
|
mkdirSync(dirname(abs), { recursive: true });
|
|
961
528
|
writeFileSync(abs, content);
|
|
@@ -1097,36 +664,25 @@ function rootsNotices(state) {
|
|
|
1097
664
|
function stripJsonc(text) {
|
|
1098
665
|
let out = "";
|
|
1099
666
|
let inString = false;
|
|
1100
|
-
let
|
|
1101
|
-
let i = 0;
|
|
1102
|
-
while (i < text.length) {
|
|
667
|
+
for (let i = 0; i < text.length; i++) {
|
|
1103
668
|
const ch = text[i];
|
|
1104
669
|
if (inString) {
|
|
1105
670
|
out += ch;
|
|
1106
|
-
if (
|
|
1107
|
-
else if (ch === "\\") escaped = true;
|
|
671
|
+
if (ch === "\\") out += text[++i] ?? "";
|
|
1108
672
|
else if (ch === '"') inString = false;
|
|
1109
|
-
i += 1;
|
|
1110
673
|
continue;
|
|
1111
674
|
}
|
|
1112
|
-
if (ch === '"')
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
i += 1;
|
|
675
|
+
if (ch === '"') inString = true;
|
|
676
|
+
else if (ch === "/" && text[i + 1] === "/") {
|
|
677
|
+
while (i < text.length && text[i] !== "\n") i++;
|
|
1116
678
|
continue;
|
|
1117
|
-
}
|
|
1118
|
-
if (ch === "/" && text[i + 1] === "/") {
|
|
1119
|
-
while (i < text.length && text[i] !== "\n") i += 1;
|
|
1120
|
-
continue;
|
|
1121
|
-
}
|
|
1122
|
-
if (ch === "/" && text[i + 1] === "*") {
|
|
1123
|
-
i += 2;
|
|
1124
|
-
while (i < text.length && !(text[i] === "*" && text[i + 1] === "/")) i += 1;
|
|
679
|
+
} else if (ch === "/" && text[i + 1] === "*") {
|
|
1125
680
|
i += 2;
|
|
681
|
+
while (i < text.length && !(text[i] === "*" && text[i + 1] === "/")) i++;
|
|
682
|
+
i++;
|
|
1126
683
|
continue;
|
|
1127
684
|
}
|
|
1128
685
|
out += ch;
|
|
1129
|
-
i += 1;
|
|
1130
686
|
}
|
|
1131
687
|
return out.replace(/,(\s*[}\]])/g, "$1");
|
|
1132
688
|
}
|
|
@@ -1148,68 +704,39 @@ const LEFTHOOK_CONFIG_NAMES = ["lefthook.yml", "lefthook.yaml", ".lefthook.yml",
|
|
|
1148
704
|
function findLefthookConfig(dir) {
|
|
1149
705
|
return LEFTHOOK_CONFIG_NAMES.find((name) => existsSync(resolve(dir, name))) ?? null;
|
|
1150
706
|
}
|
|
1151
|
-
function
|
|
1152
|
-
return raw.trim().replace(/^['"]|['"]$/g, "");
|
|
1153
|
-
}
|
|
1154
|
-
function yamlExtendsEntries(text) {
|
|
1155
|
-
const lines = text.split("\n");
|
|
707
|
+
function lefthookDumpRunLines(dump) {
|
|
1156
708
|
const out = [];
|
|
1157
|
-
for (const
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
for (const next of lines.slice(index + 1)) {
|
|
1165
|
-
if (/^\s*(#.*)?$/.test(next ?? "")) continue;
|
|
1166
|
-
const item = /^\s*-\s*(.+?)\s*$/.exec(next ?? "");
|
|
1167
|
-
if (item === null) break;
|
|
1168
|
-
out.push(yamlUnquote(item[1] ?? ""));
|
|
709
|
+
for (const hook of Object.values(dump)) {
|
|
710
|
+
if (typeof hook !== "object" || hook === null || !("commands" in hook)) continue;
|
|
711
|
+
const commands = hook.commands;
|
|
712
|
+
if (typeof commands !== "object" || commands === null) continue;
|
|
713
|
+
for (const command of Object.values(commands)) {
|
|
714
|
+
const run2 = command?.run;
|
|
715
|
+
if (typeof run2 === "string") out.push(run2);
|
|
1169
716
|
}
|
|
1170
717
|
}
|
|
1171
718
|
return out;
|
|
1172
719
|
}
|
|
1173
|
-
function
|
|
720
|
+
function inspectLefthookGate(repoRoot) {
|
|
1174
721
|
const file = findLefthookConfig(repoRoot);
|
|
1175
722
|
if (file === null) return { kind: "no-file" };
|
|
1176
|
-
const raw = readIfExists(resolve(repoRoot, file));
|
|
1177
|
-
if (raw === null) return { kind: "no-file" };
|
|
1178
|
-
const entry = yamlExtendsEntries(raw).find((e) => e.endsWith("basalt-ui/configs/lefthook.yml"));
|
|
1179
|
-
if (entry === void 0) return { kind: "unwired", file };
|
|
1180
|
-
return existsSync(resolve(repoRoot, entry)) ? { kind: "ok", file, entry } : { kind: "broken", file, entry };
|
|
1181
|
-
}
|
|
1182
|
-
const LEFTHOOK_GUARD_RUN = /^\s*run:\s*.*\bcheck-theme\b/m;
|
|
1183
|
-
function lefthookDump(repoRoot) {
|
|
1184
723
|
const localBin = resolve(repoRoot, "node_modules/.bin/lefthook");
|
|
1185
724
|
const bin = existsSync(localBin) ? localBin : "lefthook";
|
|
725
|
+
let dump = null;
|
|
1186
726
|
try {
|
|
1187
|
-
const result = spawnSync(bin, ["dump"], {
|
|
727
|
+
const result = spawnSync(bin, ["dump", "--format", "json"], {
|
|
1188
728
|
cwd: repoRoot,
|
|
1189
729
|
encoding: "utf8",
|
|
1190
730
|
timeout: 1e4,
|
|
1191
731
|
stdio: ["ignore", "pipe", "ignore"]
|
|
1192
732
|
});
|
|
1193
|
-
|
|
1194
|
-
return result.stdout;
|
|
733
|
+
dump = result.error === void 0 && result.status === 0 ? JSON.parse(result.stdout) : null;
|
|
1195
734
|
} catch {
|
|
1196
|
-
|
|
735
|
+
dump = null;
|
|
1197
736
|
}
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
if (seam.kind === "no-file") return { kind: "no-file" };
|
|
1202
|
-
if (seam.kind === "ok") return { kind: "extends-ok", file: seam.file, entry: seam.entry };
|
|
1203
|
-
if (seam.kind === "broken") {
|
|
1204
|
-
return { kind: "extends-broken", file: seam.file, entry: seam.entry };
|
|
1205
|
-
}
|
|
1206
|
-
const dump = lefthookDump(repoRoot);
|
|
1207
|
-
if (dump !== null) {
|
|
1208
|
-
return LEFTHOOK_GUARD_RUN.test(dump) ? { kind: "wired", file: seam.file, proof: "dump" } : { kind: "absent", file: seam.file };
|
|
1209
|
-
}
|
|
1210
|
-
const raw = readIfExists(resolve(repoRoot, seam.file)) ?? "";
|
|
1211
|
-
if (LEFTHOOK_GUARD_RUN.test(raw)) return { kind: "wired", file: seam.file, proof: "config" };
|
|
1212
|
-
return { kind: "advisory", file: seam.file };
|
|
737
|
+
if (dump === null) return { kind: "unreadable", file };
|
|
738
|
+
const wired = lefthookDumpRunLines(dump).some((run2) => /\bcheck-theme\b/.test(run2));
|
|
739
|
+
return wired ? { kind: "wired", file } : { kind: "absent", file };
|
|
1213
740
|
}
|
|
1214
741
|
function mergeOxlintExtends(cwd, presetPath) {
|
|
1215
742
|
const abs = resolve(cwd, ".oxlintrc.json");
|
|
@@ -1228,571 +755,6 @@ function mergeOxlintExtends(cwd, presetPath) {
|
|
|
1228
755
|
writeFileSync(abs, JSON.stringify(merged, null, indent) + (raw.endsWith("\n") ? "\n" : ""));
|
|
1229
756
|
return "added";
|
|
1230
757
|
}
|
|
1231
|
-
const KEPT_FILE_COST = {
|
|
1232
|
-
".oxlintrc.json": "the shipped oxlint preset (the basalt/* design rules AND the jsx-a11y / import / promise / unicorn plugins) is NOT active \u2014 nothing else turns it on, and every gate stays green without it. Re-run with `--merge-lint` to splice the extends in, or add it by hand.",
|
|
1233
|
-
"lefthook.yml": "the pre-commit oxlint / oxfmt / check-theme jobs are not wired \u2014 add `extends: [<preset>]` to your own file.",
|
|
1234
|
-
".github/workflows/check.yml": "CI does not run check-theme or the sync drift gate \u2014 copy those steps into your own workflow.",
|
|
1235
|
-
".oxfmtrc.json": "your existing formatter config stands; basalt formats nothing on its own.",
|
|
1236
|
-
"DESIGN.md": "the design brief Claude reads is yours, not the seeded one."
|
|
1237
|
-
};
|
|
1238
|
-
function init(cwd = process.cwd(), scaffoldFlags = {}) {
|
|
1239
|
-
const install = findBasaltInstall(cwd);
|
|
1240
|
-
if (install.dir === null) {
|
|
1241
|
-
console.error(
|
|
1242
|
-
`\u26A0 basalt-ui init: basalt-ui does not resolve from ${cwd} or any directory above it \u2014 the seeded \`extends\` paths and CI steps are being written against the repo root by assumption. Install basalt-ui here (or at the repo root) and re-run \`basalt-ui init\` so they resolve.`
|
|
1243
|
-
);
|
|
1244
|
-
}
|
|
1245
|
-
const rootsState = reconcileRoots(cwd, { write: true });
|
|
1246
|
-
const ctx = renderContext(cwd);
|
|
1247
|
-
const lintScript = `oxlint . && ${ctx.vars.BASALT_BIN} check-theme`;
|
|
1248
|
-
const seededLintScript = patchPackageJson(cwd, (pkg) => {
|
|
1249
|
-
const scripts = pkg["scripts"] ?? {};
|
|
1250
|
-
if (scripts["lint:basalt"] !== void 0) return false;
|
|
1251
|
-
pkg["scripts"] = { ...scripts, "lint:basalt": lintScript };
|
|
1252
|
-
return true;
|
|
1253
|
-
});
|
|
1254
|
-
const manifest = readManifest(cwd);
|
|
1255
|
-
migrateLegacyOxfmt(cwd, ctx.pkgRoot, manifest);
|
|
1256
|
-
const peers = resolvePeerFlags(cwd, scaffoldFlags);
|
|
1257
|
-
const placement = resolvePlacement(cwd);
|
|
1258
|
-
const files = managedFiles(peers, placement);
|
|
1259
|
-
const writtenFiles = [];
|
|
1260
|
-
const keptFiles = [];
|
|
1261
|
-
const missingSources = [];
|
|
1262
|
-
for (const file of files) {
|
|
1263
|
-
const state = unitState(file, cwd, ctx);
|
|
1264
|
-
if (state.desired === null) {
|
|
1265
|
-
missingSources.push(file.source);
|
|
1266
|
-
continue;
|
|
1267
|
-
}
|
|
1268
|
-
const destExists = existsSync(resolve(cwd, file.dest));
|
|
1269
|
-
if (!file.markers && destExists) {
|
|
1270
|
-
manifest.files[file.dest] = sha256(normalizeForLedger(state.desired));
|
|
1271
|
-
keptFiles.push(file.dest);
|
|
1272
|
-
continue;
|
|
1273
|
-
}
|
|
1274
|
-
const hash = writeUnit(file, cwd, state.desired);
|
|
1275
|
-
manifest.files[file.dest] = hash;
|
|
1276
|
-
writtenFiles.push(file.dest);
|
|
1277
|
-
}
|
|
1278
|
-
const pruned = pruneRetiredManagedFiles(cwd, manifest, files);
|
|
1279
|
-
const mergeLint = scaffoldFlags.mergeLint === true ? mergeOxlintExtends(cwd, ctx.vars.OXLINT_PRESET_PATH) : null;
|
|
1280
|
-
manifest.basaltVersion = ctx.vars.BASALT_VERSION;
|
|
1281
|
-
manifest.spacingScale = { ...deriveSpacing(0).scale };
|
|
1282
|
-
writeFileEnsuringDir(resolve(cwd, MANIFEST_PATH), `${JSON.stringify(manifest, null, 2)}
|
|
1283
|
-
`);
|
|
1284
|
-
console.log(
|
|
1285
|
-
`basalt-ui init: ${writtenFiles.length} written, ${keptFiles.length} kept, manifest at ${MANIFEST_PATH}`
|
|
1286
|
-
);
|
|
1287
|
-
reportPrune(pruned);
|
|
1288
|
-
if (missingSources.length > 0) {
|
|
1289
|
-
console.log(
|
|
1290
|
-
`basalt-ui init: ${missingSources.length} shipped asset(s) not present, skipped: ${missingSources.join(", ")}`
|
|
1291
|
-
);
|
|
1292
|
-
}
|
|
1293
|
-
if (keptFiles.length > 0) {
|
|
1294
|
-
console.log(`
|
|
1295
|
-
Kept (already present \u2014 basalt did not touch them):`);
|
|
1296
|
-
for (const dest of keptFiles) {
|
|
1297
|
-
const cost = KEPT_FILE_COST[dest];
|
|
1298
|
-
console.log(cost === void 0 ? ` \xB7 ${dest}` : ` \xB7 ${dest} \u2014 ${cost}`);
|
|
1299
|
-
}
|
|
1300
|
-
}
|
|
1301
|
-
if (mergeLint !== null) {
|
|
1302
|
-
const mergeMessage = {
|
|
1303
|
-
added: `spliced "${ctx.vars.OXLINT_PRESET_PATH}" into the existing .oxlintrc.json extends`,
|
|
1304
|
-
already: ".oxlintrc.json already extends the shipped preset \u2014 nothing to do",
|
|
1305
|
-
absent: "no existing .oxlintrc.json to merge into (the seeded one already extends the preset)",
|
|
1306
|
-
unreadable: ".oxlintrc.json is not valid JSON \u2014 merge the extends entry by hand",
|
|
1307
|
-
"has-comments": `.oxlintrc.json carries comments; rewriting it would delete them (usually the reason each rule is off). Add "extends": ["${ctx.vars.OXLINT_PRESET_PATH}"] as the first entry by hand.`
|
|
1308
|
-
};
|
|
1309
|
-
console.log(`basalt-ui init: --merge-lint \u2014 ${mergeMessage[mergeLint]}`);
|
|
1310
|
-
}
|
|
1311
|
-
const rootsMessages = rootsNotices(rootsState);
|
|
1312
|
-
for (const line of rootsMessages.log) console.log(`basalt-ui init: ${line}`);
|
|
1313
|
-
for (const line of rootsMessages.error) console.error(`\u26A0 basalt-ui init: ${line}`);
|
|
1314
|
-
if (seededLintScript) {
|
|
1315
|
-
console.log(`basalt-ui init: added the "lint:basalt" script (${lintScript}) to package.json.`);
|
|
1316
|
-
}
|
|
1317
|
-
if (!placement.isPackageRepoRoot) {
|
|
1318
|
-
console.log(
|
|
1319
|
-
`basalt-ui init: skipped lefthook.yml (this package is not the repo root \u2014 repo root detected at ${placement.repoRoot}) \u2014 lefthook only reads config at the repo root; extend ${ctx.vars.LEFTHOOK_PRESET_PATH} from your root lefthook.yml instead, and give its check-theme command \`env: { BASALT_CWD: ${relativePosix(placement.repoRoot, cwd)} }\` so the guard runs where your basalt config actually lives.`
|
|
1320
|
-
);
|
|
1321
|
-
console.log(
|
|
1322
|
-
`basalt-ui init: skipped .github/workflows/check.yml (this package is not the repo root \u2014 repo root detected at ${placement.repoRoot}) \u2014 GitHub Actions only reads .github/ at the repo root; copy the steps from ${shippedAssetPath(install, placement.repoRoot, "configs/check.yml")} into your root CI workflow instead.`
|
|
1323
|
-
);
|
|
1324
|
-
}
|
|
1325
|
-
if (placement.relocatedQueryClient !== null) {
|
|
1326
|
-
console.log(
|
|
1327
|
-
`basalt-ui init: skipped src/query-client.ts (found an existing query client at ${relative(cwd, placement.relocatedQueryClient)}) \u2014 import it from there instead of re-seeding at the original path.`
|
|
1328
|
-
);
|
|
1329
|
-
}
|
|
1330
|
-
if (!peers.hasQuery) {
|
|
1331
|
-
console.log(
|
|
1332
|
-
"basalt-ui init: skipped src/query-client.ts (no @tanstack/react-query dependency detected) \u2014 install it, or re-run with --with-query, to scaffold it."
|
|
1333
|
-
);
|
|
1334
|
-
}
|
|
1335
|
-
if (!peers.hasRouter || !peers.hasQuery) {
|
|
1336
|
-
console.log(
|
|
1337
|
-
"basalt-ui init: skipped src/routes/__root.tsx (needs both @tanstack/react-router and @tanstack/react-query) \u2014 install both, or re-run with --with-router --with-query, to scaffold it."
|
|
1338
|
-
);
|
|
1339
|
-
}
|
|
1340
|
-
console.log(
|
|
1341
|
-
`
|
|
1342
|
-
Theme guard hook: add to .claude/settings.json \u2192 hooks.PreToolUse to catch violations before they land:
|
|
1343
|
-
"hooks": {
|
|
1344
|
-
"PreToolUse": [
|
|
1345
|
-
{
|
|
1346
|
-
"matcher": "Write|Edit|MultiEdit",
|
|
1347
|
-
"hooks": [{ "type": "command", "command": "${ctx.vars.BASALT_BIN} guard-hook" }]
|
|
1348
|
-
}
|
|
1349
|
-
]
|
|
1350
|
-
}`
|
|
1351
|
-
);
|
|
1352
|
-
const presetPlugins = readOxlintPresetPlugins(ctx.pkgRoot);
|
|
1353
|
-
if (presetPlugins.length > 0) {
|
|
1354
|
-
console.log(
|
|
1355
|
-
`
|
|
1356
|
-
Lint debt: adopting the shipped oxlint preset on an EXISTING app is not a no-op \u2014 it turns on ${presetPlugins.length} plugins (${presetPlugins.join(", ")}) plus the basalt/* design rules, on code never linted against them. Run \`oxlint .\` now and triage the count before your next commit; turn a rule off in your own .oxlintrc.json with a written reason rather than blanket-disabling a plugin.`
|
|
1357
|
-
);
|
|
1358
|
-
}
|
|
1359
|
-
console.log(
|
|
1360
|
-
"Activate the hooks: `lefthook install` \u2014 the seeded lefthook.yml is inert until the git hooks are written, and looks configured either way."
|
|
1361
|
-
);
|
|
1362
|
-
console.log(
|
|
1363
|
-
`
|
|
1364
|
-
First run: run \`${ctx.vars.BASALT_BIN} check-theme\` next (or \`bun run lint:basalt\`), then tune the per-rule \`basalt.*\` config keys in package.json for anything that fires \u2014 do not mass-\`theme-allow\` findings. Then \`basalt-ui doctor\` to confirm the wiring actually took.`
|
|
1365
|
-
);
|
|
1366
|
-
return 0;
|
|
1367
|
-
}
|
|
1368
|
-
function readOxlintPresetPlugins(pkgRoot) {
|
|
1369
|
-
const raw = readSource(pkgRoot, "configs/oxlint.json");
|
|
1370
|
-
if (raw === null) return [];
|
|
1371
|
-
try {
|
|
1372
|
-
const plugins = JSON.parse(raw).plugins;
|
|
1373
|
-
return isStringArray(plugins) ? plugins : [];
|
|
1374
|
-
} catch {
|
|
1375
|
-
return [];
|
|
1376
|
-
}
|
|
1377
|
-
}
|
|
1378
|
-
function parentInstallAdvice(cwd, parent, verb) {
|
|
1379
|
-
return `This repo's install is at ${relativePosix(cwd, parent)} (${parent}) \u2014 run ${verb} there, or set BASALT_CWD to it.`;
|
|
1380
|
-
}
|
|
1381
|
-
function findManifestAbove(dir) {
|
|
1382
|
-
let current = resolve(dir, "..");
|
|
1383
|
-
for (; ; ) {
|
|
1384
|
-
if (existsSync(resolve(current, MANIFEST_PATH))) return current;
|
|
1385
|
-
const parent = resolve(current, "..");
|
|
1386
|
-
if (parent === current) return null;
|
|
1387
|
-
current = parent;
|
|
1388
|
-
}
|
|
1389
|
-
}
|
|
1390
|
-
function sync(opts = {}, invocationCwd = process.cwd()) {
|
|
1391
|
-
const syncFlags = opts.flags ?? [];
|
|
1392
|
-
if (conflictingProfileFlags(syncFlags)) {
|
|
1393
|
-
console.error("basalt-ui sync: --tokens-only and --framework are alternatives \u2014 pass one.");
|
|
1394
|
-
return 1;
|
|
1395
|
-
}
|
|
1396
|
-
const project = resolveProjectDir(invocationCwd);
|
|
1397
|
-
if (project.ambiguous !== null) {
|
|
1398
|
-
console.error(
|
|
1399
|
-
`\u2716 basalt-ui sync: no basalt config at ${invocationCwd}, and ${project.ambiguous.length} packages below it carry one (${project.ambiguous.map((d) => relativePosix(invocationCwd, d)).join(", ")}) \u2014 run it from one of them, or set BASALT_CWD to pick.`
|
|
1400
|
-
);
|
|
1401
|
-
return 1;
|
|
1402
|
-
}
|
|
1403
|
-
const cwd = project.dir;
|
|
1404
|
-
if (project.relocatedFrom !== null) {
|
|
1405
|
-
console.log(
|
|
1406
|
-
`basalt-ui sync: no basalt config at ${project.relocatedFrom} \u2014 running in ${relativePosix(project.relocatedFrom, cwd)}, where it lives.`
|
|
1407
|
-
);
|
|
1408
|
-
}
|
|
1409
|
-
if (declaredProfile(readBasaltConfig(cwd), syncFlags) === "tokens-only") {
|
|
1410
|
-
console.log(
|
|
1411
|
-
`basalt-ui sync: ${MANIFEST_PATH}: n/a \u2014 a tokens-only consumer has no scaffold to reconcile. The token layer is refreshed by re-running \`basalt-ui tokens:css\`, and \`tokens:css --check\` is its drift gate. Pass --framework to force the full profile.`
|
|
1412
|
-
);
|
|
1413
|
-
return 0;
|
|
1414
|
-
}
|
|
1415
|
-
if (!existsSync(resolve(cwd, MANIFEST_PATH))) {
|
|
1416
|
-
const elsewhere = findManifestAbove(cwd);
|
|
1417
|
-
const wouldWrite = managedFiles(resolvePeerFlags(cwd, {}), resolvePlacement(cwd)).length;
|
|
1418
|
-
console.error(
|
|
1419
|
-
`\u2716 basalt-ui sync: no ${MANIFEST_PATH} at ${cwd} \u2014 refusing to scaffold. \`sync\` refreshes an EXISTING basalt install; creating one is \`basalt-ui init\`'s decision, and continuing here would write ${wouldWrite} managed file(s) into this directory.` + (elsewhere === null ? " No manifest exists at any ancestor either \u2014 run `basalt-ui init` here if this package really is meant to be a consumer." : ` ${parentInstallAdvice(cwd, elsewhere, "sync")}`)
|
|
1420
|
-
);
|
|
1421
|
-
return 1;
|
|
1422
|
-
}
|
|
1423
|
-
const rootsState = reconcileRoots(cwd, { write: opts.check !== true });
|
|
1424
|
-
const ctx = renderContext(cwd);
|
|
1425
|
-
const manifest = readManifest(cwd);
|
|
1426
|
-
if (!opts.check) {
|
|
1427
|
-
migrateLegacyOxfmt(cwd, ctx.pkgRoot, manifest);
|
|
1428
|
-
migrateDesignVersionLine(cwd);
|
|
1429
|
-
}
|
|
1430
|
-
const peers = resolvePeerFlags(cwd, {});
|
|
1431
|
-
const placement = resolvePlacement(cwd);
|
|
1432
|
-
const files = managedFiles(peers, placement);
|
|
1433
|
-
let updated = 0;
|
|
1434
|
-
let created = 0;
|
|
1435
|
-
let recreated = 0;
|
|
1436
|
-
let skippedDrift = 0;
|
|
1437
|
-
let staleForCheck = 0;
|
|
1438
|
-
const driftLines = [];
|
|
1439
|
-
const missingSources = [];
|
|
1440
|
-
for (const file of files) {
|
|
1441
|
-
const state = unitState(file, cwd, ctx);
|
|
1442
|
-
if (state.desired === null) {
|
|
1443
|
-
missingSources.push(file.source);
|
|
1444
|
-
continue;
|
|
1445
|
-
}
|
|
1446
|
-
const tracked = manifest.files[file.dest] !== void 0;
|
|
1447
|
-
if (file.mode === "seed") {
|
|
1448
|
-
if (state.current === null && !opts.check) {
|
|
1449
|
-
manifest.files[file.dest] = writeUnit(file, cwd, state.desired);
|
|
1450
|
-
if (tracked) recreated++;
|
|
1451
|
-
else created++;
|
|
1452
|
-
}
|
|
1453
|
-
continue;
|
|
1454
|
-
}
|
|
1455
|
-
const kind = classify(state, manifest.files[file.dest]);
|
|
1456
|
-
if (kind === "current") {
|
|
1457
|
-
manifest.files[file.dest] = sha256(normalizeForLedger(state.desired));
|
|
1458
|
-
continue;
|
|
1459
|
-
}
|
|
1460
|
-
if (kind === "drifted" && !opts.force) {
|
|
1461
|
-
driftLines.push(diffSummary(file, state));
|
|
1462
|
-
staleForCheck++;
|
|
1463
|
-
skippedDrift++;
|
|
1464
|
-
continue;
|
|
1465
|
-
}
|
|
1466
|
-
staleForCheck++;
|
|
1467
|
-
if (opts.check) continue;
|
|
1468
|
-
manifest.files[file.dest] = writeUnit(file, cwd, state.desired);
|
|
1469
|
-
if (kind !== "missing") updated++;
|
|
1470
|
-
else if (tracked) recreated++;
|
|
1471
|
-
else created++;
|
|
1472
|
-
}
|
|
1473
|
-
const pruned = pruneRetiredManagedFiles(cwd, manifest, files, {
|
|
1474
|
-
dryRun: opts.check === true,
|
|
1475
|
-
force: opts.force === true
|
|
1476
|
-
});
|
|
1477
|
-
staleForCheck += pruned.removed.length + pruned.drifted.length;
|
|
1478
|
-
if (!placement.isPackageRepoRoot) {
|
|
1479
|
-
console.log(
|
|
1480
|
-
`basalt-ui sync: skipped lefthook.yml (this package is not the repo root \u2014 repo root detected at ${placement.repoRoot}) \u2014 lefthook only reads config at the repo root; extend node_modules/basalt-ui/configs/lefthook.yml from your root lefthook.yml instead.`
|
|
1481
|
-
);
|
|
1482
|
-
console.log(
|
|
1483
|
-
`basalt-ui sync: skipped .github/workflows/check.yml (this package is not the repo root \u2014 repo root detected at ${placement.repoRoot}) \u2014 GitHub Actions only reads .github/ at the repo root; extend node_modules/basalt-ui/configs/check.yml from your root CI workflow instead.`
|
|
1484
|
-
);
|
|
1485
|
-
}
|
|
1486
|
-
if (placement.relocatedQueryClient !== null) {
|
|
1487
|
-
console.log(
|
|
1488
|
-
`basalt-ui sync: skipped src/query-client.ts (found an existing query client at ${relative(cwd, placement.relocatedQueryClient)}) \u2014 import it from there instead of re-seeding at the original path.`
|
|
1489
|
-
);
|
|
1490
|
-
}
|
|
1491
|
-
const rootsMessages = rootsNotices(rootsState);
|
|
1492
|
-
for (const line of rootsMessages.log) console.log(`basalt-ui sync: ${line}`);
|
|
1493
|
-
for (const line of rootsMessages.error) console.error(`\u26A0 basalt-ui sync: ${line}`);
|
|
1494
|
-
const install = findBasaltInstall(cwd);
|
|
1495
|
-
const lefthookSeam = inspectLefthookSeam(placement.repoRoot);
|
|
1496
|
-
if (lefthookSeam.kind === "broken") {
|
|
1497
|
-
console.error(
|
|
1498
|
-
`\u26A0 basalt-ui sync: ${lefthookSeam.file} extends "${lefthookSeam.entry}", which does not exist \u2014 lefthook merges a missing extends target into ZERO commands and still exits 0, so there is no pre-commit gate. Repoint it at "${shippedAssetPath(install, placement.repoRoot, "configs/lefthook.yml")}".`
|
|
1499
|
-
);
|
|
1500
|
-
}
|
|
1501
|
-
const oxlintrcRaw = readIfExists(resolve(cwd, ".oxlintrc.json"));
|
|
1502
|
-
const oxlintEntry = oxlintrcRaw === null ? null : basaltPresetEntry(parseJsonc(oxlintrcRaw)?.["extends"]);
|
|
1503
|
-
if (oxlintEntry !== null && !existsSync(resolve(cwd, oxlintEntry))) {
|
|
1504
|
-
console.error(
|
|
1505
|
-
`\u26A0 basalt-ui sync: .oxlintrc.json extends "${oxlintEntry}", which does not exist \u2014 oxlint refuses to start on a missing extends target (\`NotFound\`). Repoint it at "${shippedAssetPath(install, cwd, "configs/oxlint.json")}".`
|
|
1506
|
-
);
|
|
1507
|
-
}
|
|
1508
|
-
if (opts.check) {
|
|
1509
|
-
if (driftLines.length > 0) {
|
|
1510
|
-
console.error("basalt-ui sync --check: locally-drifted managed files:");
|
|
1511
|
-
for (const l of driftLines) console.error(l);
|
|
1512
|
-
}
|
|
1513
|
-
const retired = [...pruned.removed, ...pruned.drifted];
|
|
1514
|
-
if (retired.length > 0) {
|
|
1515
|
-
console.error(
|
|
1516
|
-
`basalt-ui sync --check: ${retired.length} retired rule/skill file(s) still present: ${retired.join(", ")} \u2014 run \`basalt-ui sync\`.`
|
|
1517
|
-
);
|
|
1518
|
-
}
|
|
1519
|
-
if (staleForCheck > 0) {
|
|
1520
|
-
console.error(`basalt-ui sync --check: ${staleForCheck} managed file(s) out of date.`);
|
|
1521
|
-
return 1;
|
|
1522
|
-
}
|
|
1523
|
-
console.log("\u2713 basalt-ui sync --check: all managed files current.");
|
|
1524
|
-
return 0;
|
|
1525
|
-
}
|
|
1526
|
-
manifest.basaltVersion = ctx.vars.BASALT_VERSION;
|
|
1527
|
-
manifest.spacingScale = { ...deriveSpacing(0).scale };
|
|
1528
|
-
writeFileEnsuringDir(resolve(cwd, MANIFEST_PATH), `${JSON.stringify(manifest, null, 2)}
|
|
1529
|
-
`);
|
|
1530
|
-
console.log(
|
|
1531
|
-
`basalt-ui sync: ${updated} updated, ${created} created, ${recreated} recreated, ${skippedDrift} skipped (drift), ${pruned.removed.length} retired.`
|
|
1532
|
-
);
|
|
1533
|
-
reportPrune(pruned);
|
|
1534
|
-
if (driftLines.length > 0) {
|
|
1535
|
-
console.log("Locally-edited files were skipped (run with --force to overwrite):");
|
|
1536
|
-
for (const l of driftLines) console.log(l);
|
|
1537
|
-
}
|
|
1538
|
-
if (missingSources.length > 0) {
|
|
1539
|
-
console.log(
|
|
1540
|
-
`basalt-ui sync: ${missingSources.length} shipped asset(s) not present, skipped: ${missingSources.join(", ")}`
|
|
1541
|
-
);
|
|
1542
|
-
}
|
|
1543
|
-
return 0;
|
|
1544
|
-
}
|
|
1545
|
-
const COVERAGE_BLOCK_OPEN = "<!-- basalt:coverage -->";
|
|
1546
|
-
const COVERAGE_BLOCK_CLOSE = "<!-- /basalt:coverage -->";
|
|
1547
|
-
const COVERAGE_BLOCK_RE = /<!-- basalt:coverage -->[\s\S]*?<!-- \/basalt:coverage -->/;
|
|
1548
|
-
function coverageFor(rule) {
|
|
1549
|
-
const specs = Object.values(SURFACES).filter(
|
|
1550
|
-
(s) => s.kind === "doctrine" && s.rule === rule
|
|
1551
|
-
);
|
|
1552
|
-
const unique = (values) => [...new Set(values)].toSorted();
|
|
1553
|
-
return {
|
|
1554
|
-
guardKinds: unique(specs.flatMap((s) => [...s.guardKinds])),
|
|
1555
|
-
pluginRules: unique(specs.flatMap((s) => [...s.pluginRules])),
|
|
1556
|
-
// NOT sorted, and not deduped across surfaces by content: an advisory law is a sentence, and
|
|
1557
|
-
// its order is the order the spec argues it in.
|
|
1558
|
-
advisoryLaws: specs.flatMap((s) => [...s.advisoryLaws ?? []])
|
|
1559
|
-
};
|
|
1560
|
-
}
|
|
1561
|
-
function coverageBlock(rule) {
|
|
1562
|
-
const { guardKinds, pluginRules, advisoryLaws } = coverageFor(rule);
|
|
1563
|
-
const backedBy = [
|
|
1564
|
-
guardKinds.length > 0 ? `guard kinds \u2014 ${guardKinds.join(", ")}` : "guard kinds \u2014 none",
|
|
1565
|
-
pluginRules.length > 0 ? `oxlint rules \u2014 ${pluginRules.map((id) => `basalt/${id}`).join(", ")}` : "oxlint rules \u2014 none"
|
|
1566
|
-
].join(" \xB7 ");
|
|
1567
|
-
const lines = [
|
|
1568
|
-
COVERAGE_BLOCK_OPEN,
|
|
1569
|
-
"<!-- GENERATED from src/surfaces.ts \u2014 `basalt-ui check-coverage --write`. Do not hand-edit. -->",
|
|
1570
|
-
`<!-- backed by: ${backedBy} -->`
|
|
1571
|
-
];
|
|
1572
|
-
if (advisoryLaws.length === 0) lines.push("<!-- not guarded: \u2014 -->");
|
|
1573
|
-
else for (const law of advisoryLaws) lines.push(`<!-- not guarded: ${law} -->`);
|
|
1574
|
-
lines.push(COVERAGE_BLOCK_CLOSE);
|
|
1575
|
-
return lines.join("\n");
|
|
1576
|
-
}
|
|
1577
|
-
function readCoverageBlock(text) {
|
|
1578
|
-
return COVERAGE_BLOCK_RE.exec(text)?.[0] ?? null;
|
|
1579
|
-
}
|
|
1580
|
-
function applyCoverageBlock(text, block) {
|
|
1581
|
-
if (COVERAGE_BLOCK_RE.test(text)) return text.replace(COVERAGE_BLOCK_RE, block);
|
|
1582
|
-
const frontmatter = /^---\n[\s\S]*?\n---\n/.exec(text);
|
|
1583
|
-
if (frontmatter === null) return `${block}
|
|
1584
|
-
|
|
1585
|
-
${text}`;
|
|
1586
|
-
const end = frontmatter[0].length;
|
|
1587
|
-
return `${text.slice(0, end)}
|
|
1588
|
-
${block}
|
|
1589
|
-
${text.slice(end)}`;
|
|
1590
|
-
}
|
|
1591
|
-
function reconcileCoverageBlocks(pkgRoot, mode) {
|
|
1592
|
-
const failures = [];
|
|
1593
|
-
const notes = [];
|
|
1594
|
-
for (const rule of RULE_NAMES) {
|
|
1595
|
-
const rel = `agent/rules/basalt-${rule}.md`;
|
|
1596
|
-
const path = resolve(pkgRoot, rel);
|
|
1597
|
-
if (!existsSync(path)) continue;
|
|
1598
|
-
const text = readFileSync(path, "utf8");
|
|
1599
|
-
const current = readCoverageBlock(text);
|
|
1600
|
-
const expected = coverageBlock(rule);
|
|
1601
|
-
if (mode === "check") {
|
|
1602
|
-
if (current === null) notes.push(`${rel}: no <!-- basalt:coverage --> block yet`);
|
|
1603
|
-
else if (current !== expected) {
|
|
1604
|
-
failures.push(
|
|
1605
|
-
`${rel}: coverage block is out of sync with SURFACES (check-coverage --write)`
|
|
1606
|
-
);
|
|
1607
|
-
}
|
|
1608
|
-
continue;
|
|
1609
|
-
}
|
|
1610
|
-
if (current === expected) {
|
|
1611
|
-
notes.push(`${rel}: unchanged`);
|
|
1612
|
-
continue;
|
|
1613
|
-
}
|
|
1614
|
-
writeFileSync(path, applyCoverageBlock(text, expected), "utf8");
|
|
1615
|
-
notes.push(`${rel}: ${current === null ? "block inserted" : "block updated"}`);
|
|
1616
|
-
}
|
|
1617
|
-
return { failures, notes };
|
|
1618
|
-
}
|
|
1619
|
-
const AGENT_LINE_BUDGETS = {
|
|
1620
|
-
"agent/rules/basalt-tokens.md": 160,
|
|
1621
|
-
"agent/rules/basalt-mantine.md": 180,
|
|
1622
|
-
"agent/rules/basalt-charts.md": 140,
|
|
1623
|
-
"agent/rules/basalt-state.md": 160,
|
|
1624
|
-
"agent/rules/basalt-controls.md": 160,
|
|
1625
|
-
"agent/rules/basalt-batteries.md": 220,
|
|
1626
|
-
"agent/skills/basalt-app/SKILL.md": 100,
|
|
1627
|
-
"agent/skills/basalt-design/SKILL.md": 100,
|
|
1628
|
-
"agent/skills/basalt-charts/SKILL.md": 100,
|
|
1629
|
-
"agent/templates/CLAUDE-block.md.tpl": 40,
|
|
1630
|
-
"agent/templates/DESIGN.md.tpl": 45
|
|
1631
|
-
};
|
|
1632
|
-
const AGENT_RULE_TOTAL_BUDGET = 1050;
|
|
1633
|
-
function checkCoverage(flags = []) {
|
|
1634
|
-
const pkgRoot = packageRoot();
|
|
1635
|
-
const failures = [];
|
|
1636
|
-
const notes = [];
|
|
1637
|
-
const allSpecs = Object.entries(SURFACES);
|
|
1638
|
-
const doctrineSpecs = Object.values(SURFACES).filter(
|
|
1639
|
-
(s) => s.kind === "doctrine"
|
|
1640
|
-
);
|
|
1641
|
-
const validGuardKinds = new Set(Object.keys(GUARD_RULES));
|
|
1642
|
-
for (const [key, spec] of allSpecs) {
|
|
1643
|
-
if (spec.kind !== "doctrine") continue;
|
|
1644
|
-
for (const kind of spec.guardKinds) {
|
|
1645
|
-
if (!validGuardKinds.has(kind)) {
|
|
1646
|
-
failures.push(
|
|
1647
|
-
`SURFACES['${key}'].guardKinds includes '${kind}' which is not in GUARD_RULES`
|
|
1648
|
-
);
|
|
1649
|
-
}
|
|
1650
|
-
}
|
|
1651
|
-
}
|
|
1652
|
-
for (const rule of RULE_NAMES) {
|
|
1653
|
-
const rulePath = resolve(pkgRoot, `agent/rules/basalt-${rule}.md`);
|
|
1654
|
-
if (!existsSync(rulePath)) {
|
|
1655
|
-
failures.push(
|
|
1656
|
-
`Missing rule file: agent/rules/basalt-${rule}.md (derived from SURFACES doctrine rules)`
|
|
1657
|
-
);
|
|
1658
|
-
}
|
|
1659
|
-
}
|
|
1660
|
-
for (const skill of SKILL_NAMES) {
|
|
1661
|
-
const skillPath = resolve(pkgRoot, `agent/skills/${skill}/SKILL.md`);
|
|
1662
|
-
if (!existsSync(skillPath)) {
|
|
1663
|
-
failures.push(
|
|
1664
|
-
`Missing skill file: agent/skills/${skill}/SKILL.md (derived from SURFACES doctrine skills)`
|
|
1665
|
-
);
|
|
1666
|
-
}
|
|
1667
|
-
}
|
|
1668
|
-
let pkgExports = /* @__PURE__ */ new Set();
|
|
1669
|
-
try {
|
|
1670
|
-
const consumerPkgPath = resolve(pkgRoot, "package.json");
|
|
1671
|
-
const consumerPkg = JSON.parse(readFileSync(consumerPkgPath, "utf8"));
|
|
1672
|
-
pkgExports = new Set(Object.keys(consumerPkg.exports ?? {}));
|
|
1673
|
-
} catch {
|
|
1674
|
-
failures.push(`Cannot read package.json at ${pkgRoot}`);
|
|
1675
|
-
}
|
|
1676
|
-
for (const key of Object.keys(SURFACES)) {
|
|
1677
|
-
if (key.startsWith("#") || key === ".") continue;
|
|
1678
|
-
if (!pkgExports.has(key)) {
|
|
1679
|
-
failures.push(
|
|
1680
|
-
`SURFACES key '${key}' is a JS subpath but has no entry in package.json exports`
|
|
1681
|
-
);
|
|
1682
|
-
}
|
|
1683
|
-
}
|
|
1684
|
-
for (const exportKey of pkgExports) {
|
|
1685
|
-
if (exportKey === "." || exportKey === "./styles.css" || exportKey.startsWith("./configs/"))
|
|
1686
|
-
continue;
|
|
1687
|
-
if (!Object.hasOwn(SURFACES, exportKey)) {
|
|
1688
|
-
failures.push(`package.json exports key '${exportKey}' has no matching SURFACES entry`);
|
|
1689
|
-
}
|
|
1690
|
-
}
|
|
1691
|
-
for (const [key, spec] of allSpecs) {
|
|
1692
|
-
if (spec.forbiddenImports.length === 0) continue;
|
|
1693
|
-
if (!("globs" in spec) || spec.globs === void 0) {
|
|
1694
|
-
failures.push(
|
|
1695
|
-
`SURFACES['${key}'] has non-empty forbiddenImports but no globs field (required for oxlint emission)`
|
|
1696
|
-
);
|
|
1697
|
-
}
|
|
1698
|
-
}
|
|
1699
|
-
const REQUIRED_MANTINE_BANS = ["@mantine/core", "@mantine/hooks", "@mantine/*"];
|
|
1700
|
-
for (const [key, spec] of allSpecs) {
|
|
1701
|
-
if (spec.layer !== "headless") continue;
|
|
1702
|
-
if (TOKEN_LAYER_BOUNDARY_SURFACES.has(key)) continue;
|
|
1703
|
-
for (const required of REQUIRED_MANTINE_BANS) {
|
|
1704
|
-
const hasBan = spec.forbiddenImports.some((fi) => fi.spec === required);
|
|
1705
|
-
if (!hasBan) {
|
|
1706
|
-
failures.push(
|
|
1707
|
-
`SURFACES['${key}'] is headless but missing Mantine ban for '${required}' in forbiddenImports`
|
|
1708
|
-
);
|
|
1709
|
-
}
|
|
1710
|
-
}
|
|
1711
|
-
}
|
|
1712
|
-
let peerDependencies = {};
|
|
1713
|
-
let peerDependenciesMeta = {};
|
|
1714
|
-
try {
|
|
1715
|
-
const frameworkPkg = JSON.parse(readFileSync(resolve(pkgRoot, "package.json"), "utf8"));
|
|
1716
|
-
peerDependencies = frameworkPkg.peerDependencies ?? {};
|
|
1717
|
-
peerDependenciesMeta = frameworkPkg.peerDependenciesMeta ?? {};
|
|
1718
|
-
} catch {
|
|
1719
|
-
failures.push(`Cannot read package.json peerDependencies at ${pkgRoot}`);
|
|
1720
|
-
}
|
|
1721
|
-
for (const spec of doctrineSpecs) {
|
|
1722
|
-
for (const peer of spec.optionalPeers ?? []) {
|
|
1723
|
-
if (!(peer in peerDependencies)) {
|
|
1724
|
-
failures.push(
|
|
1725
|
-
`SURFACES doctrine optionalPeer '${peer}' is not listed in package.json peerDependencies`
|
|
1726
|
-
);
|
|
1727
|
-
}
|
|
1728
|
-
if (peerDependenciesMeta[peer]?.optional !== true) {
|
|
1729
|
-
failures.push(
|
|
1730
|
-
`SURFACES doctrine optionalPeer '${peer}' is not marked optional in package.json peerDependenciesMeta`
|
|
1731
|
-
);
|
|
1732
|
-
}
|
|
1733
|
-
}
|
|
1734
|
-
}
|
|
1735
|
-
const surfacesPerPluginRule = /* @__PURE__ */ new Map();
|
|
1736
|
-
for (const [key, spec] of allSpecs) {
|
|
1737
|
-
if (spec.kind !== "doctrine") continue;
|
|
1738
|
-
for (const id of spec.pluginRules) {
|
|
1739
|
-
surfacesPerPluginRule.set(id, [...surfacesPerPluginRule.get(id) ?? [], key]);
|
|
1740
|
-
}
|
|
1741
|
-
}
|
|
1742
|
-
for (const id of PLUGIN_RULE_ID_LIST) {
|
|
1743
|
-
const owners = surfacesPerPluginRule.get(id) ?? [];
|
|
1744
|
-
if (owners.length === 1) continue;
|
|
1745
|
-
failures.push(
|
|
1746
|
-
owners.length === 0 ? `Plugin rule 'basalt/${id}' maps to no surface \u2014 add it to one surface's pluginRules` : `Plugin rule 'basalt/${id}' maps to ${owners.length} surfaces (${owners.join(", ")}) \u2014 exactly one`
|
|
1747
|
-
);
|
|
1748
|
-
}
|
|
1749
|
-
for (const [rel, budget] of Object.entries(AGENT_LINE_BUDGETS)) {
|
|
1750
|
-
const abs = resolve(pkgRoot, rel);
|
|
1751
|
-
if (!existsSync(abs)) continue;
|
|
1752
|
-
const lines = readFileSync(abs, "utf8").split("\n").length - 1;
|
|
1753
|
-
if (lines > budget) failures.push(`${rel}: ${lines} lines exceeds its ${budget}-line budget`);
|
|
1754
|
-
}
|
|
1755
|
-
const ruleTotal = RULE_NAMES.reduce((sum, rule) => {
|
|
1756
|
-
const abs = resolve(pkgRoot, `agent/rules/basalt-${rule}.md`);
|
|
1757
|
-
return existsSync(abs) ? sum + readFileSync(abs, "utf8").split("\n").length - 1 : sum;
|
|
1758
|
-
}, 0);
|
|
1759
|
-
if (ruleTotal > AGENT_RULE_TOTAL_BUDGET) {
|
|
1760
|
-
failures.push(
|
|
1761
|
-
`agent/rules/*.md: ${ruleTotal} lines total exceeds the ${AGENT_RULE_TOTAL_BUDGET}-line budget`
|
|
1762
|
-
);
|
|
1763
|
-
}
|
|
1764
|
-
const surfacedGuardKinds = /* @__PURE__ */ new Set();
|
|
1765
|
-
for (const spec of doctrineSpecs) {
|
|
1766
|
-
for (const kind of spec.guardKinds) surfacedGuardKinds.add(kind);
|
|
1767
|
-
}
|
|
1768
|
-
for (const kind of Object.keys(GUARD_RULES)) {
|
|
1769
|
-
if (surfacedGuardKinds.has(kind)) continue;
|
|
1770
|
-
failures.push(
|
|
1771
|
-
`Guard kind '${kind}' maps to no surface \u2014 add it to one surface's guardKinds so the generated coverage headers name it`
|
|
1772
|
-
);
|
|
1773
|
-
}
|
|
1774
|
-
const write = flags.includes("--write");
|
|
1775
|
-
const check = flags.includes("--check");
|
|
1776
|
-
if (write && check) {
|
|
1777
|
-
failures.push("--write and --check are alternatives; pass one");
|
|
1778
|
-
} else if (write || check) {
|
|
1779
|
-
const result = reconcileCoverageBlocks(pkgRoot, write ? "write" : "check");
|
|
1780
|
-
failures.push(...result.failures);
|
|
1781
|
-
notes.push(...result.notes);
|
|
1782
|
-
}
|
|
1783
|
-
for (const note of notes) {
|
|
1784
|
-
console.log(` ${note}`);
|
|
1785
|
-
}
|
|
1786
|
-
if (failures.length === 0) {
|
|
1787
|
-
console.log("\u2713 check-coverage: all 11 assertions pass.");
|
|
1788
|
-
return 0;
|
|
1789
|
-
}
|
|
1790
|
-
console.error(`\u2716 check-coverage: ${failures.length} failure(s)`);
|
|
1791
|
-
for (const f of failures) {
|
|
1792
|
-
console.error(` ${f}`);
|
|
1793
|
-
}
|
|
1794
|
-
return 1;
|
|
1795
|
-
}
|
|
1796
758
|
function subdirNames(base, options = {}) {
|
|
1797
759
|
let entries;
|
|
1798
760
|
try {
|
|
@@ -1865,817 +827,6 @@ function collectWorkspacePackages(cwd) {
|
|
|
1865
827
|
function isStringArray(value) {
|
|
1866
828
|
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
1867
829
|
}
|
|
1868
|
-
function majorOf(range) {
|
|
1869
|
-
if (typeof range !== "string") return null;
|
|
1870
|
-
const match = range.match(/\d+/);
|
|
1871
|
-
return match === null ? null : Number(match[0]);
|
|
1872
|
-
}
|
|
1873
|
-
function aiMajorAt(dir) {
|
|
1874
|
-
const raw = readIfExists(resolve(dir, "package.json"));
|
|
1875
|
-
if (raw === null) return null;
|
|
1876
|
-
try {
|
|
1877
|
-
const pkg = JSON.parse(raw);
|
|
1878
|
-
return majorOf(
|
|
1879
|
-
pkg.dependencies?.["ai"] ?? pkg.devDependencies?.["ai"] ?? pkg.peerDependencies?.["ai"]
|
|
1880
|
-
);
|
|
1881
|
-
} catch {
|
|
1882
|
-
return null;
|
|
1883
|
-
}
|
|
1884
|
-
}
|
|
1885
|
-
function resolveAiMajorSkewReason(cfg) {
|
|
1886
|
-
const raw = cfg.aiMajorSkewReason;
|
|
1887
|
-
if (raw === void 0) return { reason: null, presentButInvalid: false };
|
|
1888
|
-
if (typeof raw === "string" && raw.trim().length > 0) {
|
|
1889
|
-
return { reason: raw, presentButInvalid: false };
|
|
1890
|
-
}
|
|
1891
|
-
return { reason: null, presentButInvalid: true };
|
|
1892
|
-
}
|
|
1893
|
-
function doctor(invocationCwd = process.cwd(), flags = []) {
|
|
1894
|
-
if (conflictingProfileFlags(flags)) {
|
|
1895
|
-
console.error("basalt-ui doctor: --tokens-only and --framework are alternatives \u2014 pass one.");
|
|
1896
|
-
return 1;
|
|
1897
|
-
}
|
|
1898
|
-
const project = resolveProjectDir(invocationCwd);
|
|
1899
|
-
if (project.ambiguous !== null) {
|
|
1900
|
-
console.error(
|
|
1901
|
-
`
|
|
1902
|
-
basalt-ui doctor \u2014 ${invocationCwd}
|
|
1903
|
-
|
|
1904
|
-
\u2716 no basalt config at ${invocationCwd}, and ${project.ambiguous.length} packages below it carry one (${project.ambiguous.map((d) => relativePosix(invocationCwd, d)).join(", ")}) \u2014 run doctor from one of them, or set BASALT_CWD to pick.
|
|
1905
|
-
|
|
1906
|
-
1 hard failure(s), 0 warning(s).
|
|
1907
|
-
`
|
|
1908
|
-
);
|
|
1909
|
-
return 1;
|
|
1910
|
-
}
|
|
1911
|
-
const cwd = project.dir;
|
|
1912
|
-
const cfg = readBasaltConfig(cwd);
|
|
1913
|
-
const result = { hardFailures: 0, warnings: 0, skipped: 0 };
|
|
1914
|
-
const lines = [`
|
|
1915
|
-
basalt-ui doctor \u2014 ${cwd}
|
|
1916
|
-
`];
|
|
1917
|
-
function pass(msg) {
|
|
1918
|
-
lines.push(` \u2713 ${msg}`);
|
|
1919
|
-
}
|
|
1920
|
-
function warn(msg) {
|
|
1921
|
-
lines.push(` \u26A0 ${msg}`);
|
|
1922
|
-
result.warnings++;
|
|
1923
|
-
}
|
|
1924
|
-
function skip(msg) {
|
|
1925
|
-
lines.push(` \u2298 SKIPPED \u2014 ${msg}`);
|
|
1926
|
-
result.skipped++;
|
|
1927
|
-
}
|
|
1928
|
-
function fail(msg) {
|
|
1929
|
-
lines.push(` \u2716 ${msg}`);
|
|
1930
|
-
result.hardFailures++;
|
|
1931
|
-
}
|
|
1932
|
-
if (project.relocatedFrom !== null) {
|
|
1933
|
-
lines.push(
|
|
1934
|
-
` \u2192 no basalt config at ${project.relocatedFrom} \u2014 reporting on ${relativePosix(project.relocatedFrom, cwd)}, where it lives.
|
|
1935
|
-
`
|
|
1936
|
-
);
|
|
1937
|
-
}
|
|
1938
|
-
const manifestAbs = resolve(cwd, MANIFEST_PATH);
|
|
1939
|
-
const manifestExists = existsSync(manifestAbs);
|
|
1940
|
-
const parentInstall = manifestExists ? null : findManifestAbove(cwd);
|
|
1941
|
-
const profile = inferredProfile(cwd, cfg, flags);
|
|
1942
|
-
if (profile === "tokens-only") {
|
|
1943
|
-
lines.push(
|
|
1944
|
-
" profile: tokens-only \u2014 checking the token layer only. The scaffold checks below are not\n applicable; `basalt-ui init` is NOT the fix here, it places a Mantine doctrine you have no\n use for. Pass --framework to force the full profile.\n"
|
|
1945
|
-
);
|
|
1946
|
-
if (cfg.profile === void 0) {
|
|
1947
|
-
lines.push(
|
|
1948
|
-
' \u2192 inferred (no .basalt/manifest.json and no @mantine/core in this workspace). Write it\n down as "basalt": { "profile": "tokens-only" } in package.json \u2014 check-theme does not\n infer it, so today it still reports the Mantine-only kinds against this repo.\n'
|
|
1949
|
-
);
|
|
1950
|
-
}
|
|
1951
|
-
}
|
|
1952
|
-
if (profile === "tokens-only") {
|
|
1953
|
-
pass(`${MANIFEST_PATH}: n/a \u2014 a tokens-only consumer has no scaffold to reconcile`);
|
|
1954
|
-
} else if (manifestExists) {
|
|
1955
|
-
pass(`${MANIFEST_PATH} exists`);
|
|
1956
|
-
} else if (parentInstall !== null) {
|
|
1957
|
-
fail(
|
|
1958
|
-
`${MANIFEST_PATH} missing here, but this is not an unscaffolded consumer. ${parentInstallAdvice(cwd, parentInstall, "doctor")} \`basalt-ui init\` is NOT the fix: it would scaffold a SECOND consumer beside the real one.`
|
|
1959
|
-
);
|
|
1960
|
-
} else {
|
|
1961
|
-
fail(`${MANIFEST_PATH} missing \u2014 run \`basalt-ui init\` to scaffold the consumer repo`);
|
|
1962
|
-
}
|
|
1963
|
-
const install = findBasaltInstall(cwd);
|
|
1964
|
-
const installedVersion = install.version;
|
|
1965
|
-
if (install.dir === null) {
|
|
1966
|
-
fail(
|
|
1967
|
-
`basalt-ui does not resolve from ${cwd}, any directory above it, or any workspace package \u2014 so every version check below is unrunnable, \`extends: [<preset>]\` cannot resolve, and \`bunx basalt-ui\` will silently fetch a DIFFERENT copy from npm instead of your pinned one. Add basalt-ui as a dependency here (or at the repo root) and re-run.`
|
|
1968
|
-
);
|
|
1969
|
-
} else if (install.how === "cwd") {
|
|
1970
|
-
pass(
|
|
1971
|
-
`basalt-ui resolves at ./node_modules/basalt-ui (${installedVersion ?? "unknown version"})`
|
|
1972
|
-
);
|
|
1973
|
-
} else {
|
|
1974
|
-
pass(
|
|
1975
|
-
`basalt-ui resolves at ${relativePosix(cwd, install.dir)} (${installedVersion ?? "unknown version"}, found via the ${install.how}) \u2014 seeded \`extends\` paths and CI steps must use that path, not ./node_modules/basalt-ui.`
|
|
1976
|
-
);
|
|
1977
|
-
}
|
|
1978
|
-
if (profile === "framework" && manifestExists && installedVersion === null) {
|
|
1979
|
-
skip(
|
|
1980
|
-
"installed-vs-manifest version \u2014 basalt-ui did not resolve, so there is nothing to compare"
|
|
1981
|
-
);
|
|
1982
|
-
}
|
|
1983
|
-
if (manifestExists && installedVersion !== null) {
|
|
1984
|
-
const manifestVersion = readManifest(cwd).basaltVersion;
|
|
1985
|
-
if (manifestVersion === void 0) {
|
|
1986
|
-
warn(
|
|
1987
|
-
`${MANIFEST_PATH} records no basaltVersion (written by an older basalt-ui) \u2014 run \`basalt-ui sync\` to refresh the doctrine and stamp the manifest.`
|
|
1988
|
-
);
|
|
1989
|
-
} else if (manifestVersion !== installedVersion) {
|
|
1990
|
-
warn(
|
|
1991
|
-
`installed basalt-ui (${installedVersion}) differs from the version that last synced (${manifestVersion}) \u2014 the placed doctrine is stale; run \`basalt-ui sync\`.`
|
|
1992
|
-
);
|
|
1993
|
-
} else {
|
|
1994
|
-
pass(`installed basalt-ui (${installedVersion}) matches the manifest's basaltVersion`);
|
|
1995
|
-
}
|
|
1996
|
-
}
|
|
1997
|
-
const cliVersion = readFrameworkVersion(packageRoot());
|
|
1998
|
-
const cliIsTheInstall = installedVersion === null || installedVersion === cliVersion;
|
|
1999
|
-
if (profile === "tokens-only") {
|
|
2000
|
-
pass("spacing scale: n/a \u2014 a tokens-only consumer renders no basalt Mantine theme");
|
|
2001
|
-
} else if (!manifestExists) {
|
|
2002
|
-
skip(
|
|
2003
|
-
`spacing scale \u2014 no ${MANIFEST_PATH} records the scale this app last synced with, so there is nothing to compare against`
|
|
2004
|
-
);
|
|
2005
|
-
} else if (!cliIsTheInstall) {
|
|
2006
|
-
pass(
|
|
2007
|
-
`spacing scale not compared \u2014 this CLI (${cliVersion}) is not the installed basalt-ui (${installedVersion}), so its scale is not the one the app renders with`
|
|
2008
|
-
);
|
|
2009
|
-
} else {
|
|
2010
|
-
const recorded = readManifest(cwd).spacingScale;
|
|
2011
|
-
const current = deriveSpacing(0).scale;
|
|
2012
|
-
if (recorded === void 0) {
|
|
2013
|
-
pass("spacing scale not yet recorded \u2014 `basalt-ui sync` will stamp it");
|
|
2014
|
-
} else {
|
|
2015
|
-
const steps = [.../* @__PURE__ */ new Set([...Object.keys(current), ...Object.keys(recorded)])].toSorted();
|
|
2016
|
-
const moved = steps.filter((step) => recorded[step] !== current[step]).map((step) => `${step} ${recorded[step] ?? "(none)"}\u2192${current[step] ?? "(removed)"}`);
|
|
2017
|
-
if (moved.length > 0) {
|
|
2018
|
-
warn(
|
|
2019
|
-
`the spacing scale moved since the last sync (${moved.join(", ")}) \u2014 every surface in an app calling createBasaltTheme() bare shifts with it. Review the app visually, then run \`basalt-ui sync\` to record the new scale.`
|
|
2020
|
-
);
|
|
2021
|
-
} else {
|
|
2022
|
-
pass("spacing scale matches the last sync");
|
|
2023
|
-
}
|
|
2024
|
-
}
|
|
2025
|
-
}
|
|
2026
|
-
if (installedVersion === null) {
|
|
2027
|
-
skip("CLI-vs-installed version \u2014 basalt-ui did not resolve, so there is no installed version");
|
|
2028
|
-
} else if (installedVersion !== cliVersion) {
|
|
2029
|
-
warn(
|
|
2030
|
-
`running CLI version (${cliVersion}) differs from the installed basalt-ui version at ${install.dir === null ? "node_modules" : relativePosix(cwd, install.dir)} (${installedVersion}) \u2014 likely a stale \`bunx basalt-ui\` fetch from npm; run the local bin (\`node_modules/.bin/basalt-ui\`, or a package.json script) so the pinned copy is the one that runs.`
|
|
2031
|
-
);
|
|
2032
|
-
} else {
|
|
2033
|
-
pass(`CLI version (${cliVersion}) matches the installed basalt-ui`);
|
|
2034
|
-
}
|
|
2035
|
-
if (profile === "tokens-only") {
|
|
2036
|
-
pass("guard-scan: n/a \u2014 the theme guard is a Mantine-app check");
|
|
2037
|
-
} else {
|
|
2038
|
-
const scanned = scannableFiles(cwd, cfg);
|
|
2039
|
-
if (scanned.length === 0) {
|
|
2040
|
-
fail(
|
|
2041
|
-
`guard-scan: check-theme would scan 0 files \u2014 the roots (${resolveRoots(cfg).join(", ")}) match nothing under ${cwd}, so the palette guard is a no-op that still exits green in every gate that does not run it. Set "basalt": { "roots": [...] } in package.json.`
|
|
2042
|
-
);
|
|
2043
|
-
} else {
|
|
2044
|
-
pass(
|
|
2045
|
-
`guard-scan: check-theme covers ${scanned.length} file(s) under ${resolveRoots(cfg).join(", ")}`
|
|
2046
|
-
);
|
|
2047
|
-
}
|
|
2048
|
-
}
|
|
2049
|
-
if (profile === "tokens-only") {
|
|
2050
|
-
pass("oxlint-preset: n/a \u2014 the shipped preset is a Mantine/React preset");
|
|
2051
|
-
} else {
|
|
2052
|
-
const oxlintrcRaw = readIfExists(resolve(cwd, ".oxlintrc.json"));
|
|
2053
|
-
if (oxlintrcRaw === null) {
|
|
2054
|
-
fail(
|
|
2055
|
-
".oxlintrc.json missing \u2014 the shipped oxlint preset (the basalt/* design rules) is not active. " + (parentInstall === null ? "Run `basalt-ui init` to seed it." : `${parentInstallAdvice(cwd, parentInstall, "doctor")} The preset is seeded once, at the install \u2014 not per package.`)
|
|
2056
|
-
);
|
|
2057
|
-
} else {
|
|
2058
|
-
const parsed = parseJsonc(oxlintrcRaw);
|
|
2059
|
-
if (parsed === null) {
|
|
2060
|
-
skip(
|
|
2061
|
-
".oxlintrc.json does not parse as JSON/JSONC \u2014 cannot tell whether it extends the preset"
|
|
2062
|
-
);
|
|
2063
|
-
} else {
|
|
2064
|
-
const entry = basaltPresetEntry(parsed["extends"]);
|
|
2065
|
-
const correct = shippedAssetPath(install, cwd, "configs/oxlint.json");
|
|
2066
|
-
if (entry === null) {
|
|
2067
|
-
fail(
|
|
2068
|
-
`.oxlintrc.json does NOT extend the shipped preset \u2014 every basalt/* design rule is off and oxlint reports green without them. Add "extends": ["${correct}"], or re-run \`basalt-ui init --merge-lint\` to splice it in.`
|
|
2069
|
-
);
|
|
2070
|
-
} else if (!existsSync(resolve(cwd, entry)) && entry === correct && install.dir !== null) {
|
|
2071
|
-
fail(
|
|
2072
|
-
`.oxlintrc.json extends "${entry}", which does not exist (${resolve(cwd, entry)}) \u2014 basalt-ui resolves there but ships no \`configs/oxlint.json\` at that path, so oxlint refuses to start (\`NotFound\`) and nothing is linted. Reinstall basalt-ui.`
|
|
2073
|
-
);
|
|
2074
|
-
} else if (!existsSync(resolve(cwd, entry))) {
|
|
2075
|
-
fail(
|
|
2076
|
-
`.oxlintrc.json extends "${entry}", which does not exist (${resolve(cwd, entry)}) \u2014 oxlint refuses to start on a missing extends target (\`NotFound\`), so nothing is linted at all. Repoint it at "${correct}", where basalt-ui actually resolves.`
|
|
2077
|
-
);
|
|
2078
|
-
} else {
|
|
2079
|
-
pass(`.oxlintrc.json extends the shipped basalt-ui oxlint preset (${entry})`);
|
|
2080
|
-
}
|
|
2081
|
-
}
|
|
2082
|
-
}
|
|
2083
|
-
}
|
|
2084
|
-
const repoRoot = findRepoRoot(cwd);
|
|
2085
|
-
const lefthookGate = profile === "tokens-only" ? { kind: "n/a" } : inspectLefthookGate(repoRoot);
|
|
2086
|
-
const lefthookCorrect = shippedAssetPath(install, repoRoot, "configs/lefthook.yml");
|
|
2087
|
-
if (lefthookGate.kind === "n/a") {
|
|
2088
|
-
pass("lefthook-preset: n/a \u2014 a tokens-only consumer wires its own hooks");
|
|
2089
|
-
} else if (lefthookGate.kind === "no-file") {
|
|
2090
|
-
pass(`lefthook-preset: n/a \u2014 no lefthook config at ${repoRoot}`);
|
|
2091
|
-
} else if (lefthookGate.kind === "extends-ok") {
|
|
2092
|
-
pass(`${lefthookGate.file} extends the shipped lefthook preset (${lefthookGate.entry})`);
|
|
2093
|
-
} else if (lefthookGate.kind === "wired") {
|
|
2094
|
-
pass(
|
|
2095
|
-
`lefthook-preset: ${lefthookGate.file} does not extend the shipped preset, but a pre-commit command runs check-theme directly \u2014 the gate exists (read from ` + (lefthookGate.proof === "dump" ? "`lefthook dump`, so extends/include/root: are all resolved)." : "the config text; `lefthook dump` was not runnable here, so an `include:` or a remote config was not consulted).")
|
|
2096
|
-
);
|
|
2097
|
-
} else if (lefthookGate.kind === "extends-broken") {
|
|
2098
|
-
fail(
|
|
2099
|
-
`${lefthookGate.file} extends "${lefthookGate.entry}", which does not exist (${resolve(repoRoot, lefthookGate.entry)}) \u2014 lefthook merges a missing extends target into ZERO commands and still exits 0, so this repo has no pre-commit gate and \`lefthook dump\` looks clean. Repoint it at "${lefthookCorrect}".`
|
|
2100
|
-
);
|
|
2101
|
-
} else if (lefthookGate.kind === "absent") {
|
|
2102
|
-
warn(
|
|
2103
|
-
`${lefthookGate.file}: \`lefthook dump\` resolves the merged config and NO pre-commit command runs check-theme \u2014 the theme guard is not gating commits. Either add "extends: [${lefthookCorrect}]", or add your own command running \`${basaltBinCommand(install, cwd)} check-theme\` (with \`root:\` if it must run in a subdirectory).`
|
|
2104
|
-
);
|
|
2105
|
-
} else {
|
|
2106
|
-
warn(
|
|
2107
|
-
`${lefthookGate.file} names no basalt gate and \`lefthook dump\` could not be run here, so this check cannot tell a missing gate from one wired through an \`include:\` or a remote config it never read \u2014 treat it as advisory. If the guard really is unwired, add "extends: [${lefthookCorrect}]" or your own command running \`${basaltBinCommand(install, cwd)} check-theme\`.`
|
|
2108
|
-
);
|
|
2109
|
-
}
|
|
2110
|
-
if (profile === "framework") {
|
|
2111
|
-
const appDir = findAppPluginDir(cwd, cfg);
|
|
2112
|
-
const iconsIn = appDir ?? cwd;
|
|
2113
|
-
const icons = readAppIconsOption(iconsIn);
|
|
2114
|
-
const here = appDir === null || appDir === cwd ? "" : `${relativePosix(cwd, appDir)}/`;
|
|
2115
|
-
const publicDir = resolve(iconsIn, "public");
|
|
2116
|
-
if (appDir === null && !existsSync(publicDir)) {
|
|
2117
|
-
pass("app icons: no `basaltAppPlugin(` under this project and no public/ \u2014 nothing to check");
|
|
2118
|
-
} else if (icons.kind === "none") {
|
|
2119
|
-
pass("basaltAppPlugin is configured with no icons \u2014 nothing to check");
|
|
2120
|
-
} else if (!existsSync(publicDir)) {
|
|
2121
|
-
skip(
|
|
2122
|
-
`app icons \u2014 ${here === "" ? "this package" : here.replace(/\/$/, "")} configures basaltAppPlugin, but there is no \`public/\` beside its vite config, so the files it references cannot be located. Use Vite's public-dir convention, or set \`icons: false\` if this app ships none.`
|
|
2123
|
-
);
|
|
2124
|
-
} else if (icons.kind === "explicit") {
|
|
2125
|
-
const missing = icons.sources.filter(
|
|
2126
|
-
(src) => !existsSync(resolve(publicDir, src.replace(/^\//, "")))
|
|
2127
|
-
);
|
|
2128
|
-
if (missing.length > 0) {
|
|
2129
|
-
warn(
|
|
2130
|
-
`basaltAppPlugin declares icon(s) that are not in ${here}public/: ${missing.join(", ")} \u2014 the manifest and the head links will point at a 404. Place them, or drop them from the \`icons\` array.`
|
|
2131
|
-
);
|
|
2132
|
-
} else {
|
|
2133
|
-
pass(
|
|
2134
|
-
`${here}public/ has all ${icons.sources.length} icon file(s) basaltAppPlugin's \`icons\` option names`
|
|
2135
|
-
);
|
|
2136
|
-
}
|
|
2137
|
-
} else {
|
|
2138
|
-
const dir = icons.kind === "default" ? icons.dir : null;
|
|
2139
|
-
const iconRoot = dir === null ? publicDir : resolve(publicDir, dir.replace(/^\//, ""));
|
|
2140
|
-
const shown = dir === null ? `${here}public/` : `${here}public/${dir.replace(/^\//, "")}`;
|
|
2141
|
-
const missingIcons = DEFAULT_APP_ICON_FILES.filter((f) => !existsSync(resolve(iconRoot, f)));
|
|
2142
|
-
if (missingIcons.length > 0) {
|
|
2143
|
-
warn(
|
|
2144
|
-
`${shown} is missing basaltAppPlugin icon file(s): ${missingIcons.join(", ")} \u2014 generate them (e.g. via realfavicongenerator.net) and place them at the ${shown} root, or name the icons this app really has via the plugin's \`icons\` array.`
|
|
2145
|
-
);
|
|
2146
|
-
} else {
|
|
2147
|
-
pass(`${shown} has all of basaltAppPlugin's default icon files`);
|
|
2148
|
-
}
|
|
2149
|
-
}
|
|
2150
|
-
}
|
|
2151
|
-
const workspaceWalk = collectWorkspacePackages(cwd);
|
|
2152
|
-
const aiMajors = workspaceWalk.packages.map((pkg) => ({ name: pkg.name, major: aiMajorAt(pkg.dir) })).filter((entry) => entry.major !== null);
|
|
2153
|
-
const { reason: aiMajorSkewReason, presentButInvalid: aiMajorSkewReasonInvalid } = resolveAiMajorSkewReason(cfg);
|
|
2154
|
-
if (aiMajors.length > 0) {
|
|
2155
|
-
const distinctMajors = new Set(aiMajors.map((entry) => entry.major));
|
|
2156
|
-
if (distinctMajors.size > 1) {
|
|
2157
|
-
const summary = aiMajors.map((entry) => `${entry.name}@ai${entry.major}`).join(", ");
|
|
2158
|
-
if (aiMajorSkewReason !== null) {
|
|
2159
|
-
pass(
|
|
2160
|
-
`ai package major version mismatch across workspace packages: ${summary} \u2014 exempted via basalt.aiMajorSkewReason: "${aiMajorSkewReason}"`
|
|
2161
|
-
);
|
|
2162
|
-
} else if (aiMajorSkewReasonInvalid) {
|
|
2163
|
-
fail(
|
|
2164
|
-
`ai package major version mismatch across workspace packages: ${summary} \u2014 basalt.aiMajorSkewReason is present but is not a non-empty string (a bare \`true\` is not accepted); the exemption must carry a written reason, e.g. \`aiMajorSkewReason: "apps/api on ai@5, apps/dashboard on ai@7, neutralized by a producer-side TransformStream"\`.`
|
|
2165
|
-
);
|
|
2166
|
-
} else {
|
|
2167
|
-
fail(`ai package major version mismatch across workspace packages: ${summary}`);
|
|
2168
|
-
}
|
|
2169
|
-
} else {
|
|
2170
|
-
const [major] = distinctMajors;
|
|
2171
|
-
if (aiMajorSkewReason !== null) {
|
|
2172
|
-
warn(
|
|
2173
|
-
`basalt.aiMajorSkewReason ("${aiMajorSkewReason}") is declared but the ai package major already matches across all ${aiMajors.length} workspace package(s) declaring it (ai@${major}) \u2014 the exemption is no longer needed and can be deleted.`
|
|
2174
|
-
);
|
|
2175
|
-
} else {
|
|
2176
|
-
pass(
|
|
2177
|
-
`ai package major matches across ${aiMajors.length} workspace package(s) declaring it (ai@${major})`
|
|
2178
|
-
);
|
|
2179
|
-
}
|
|
2180
|
-
}
|
|
2181
|
-
} else if (workspaceWalk.hasWorkspacesField) {
|
|
2182
|
-
pass("ai-major-parity: no workspace package declares the ai dependency \u2014 nothing to compare");
|
|
2183
|
-
}
|
|
2184
|
-
lines.push("");
|
|
2185
|
-
if (result.hardFailures === 0 && result.warnings === 0 && result.skipped === 0) {
|
|
2186
|
-
lines.push("All checks passed.");
|
|
2187
|
-
} else {
|
|
2188
|
-
const parts = [];
|
|
2189
|
-
if (result.hardFailures > 0) parts.push(`${result.hardFailures} hard failure(s)`);
|
|
2190
|
-
if (result.skipped > 0) parts.push(`${result.skipped} check(s) SKIPPED (could not run)`);
|
|
2191
|
-
parts.push(`${result.warnings} warning(s)`);
|
|
2192
|
-
lines.push(`${parts.join(", ")}.`);
|
|
2193
|
-
}
|
|
2194
|
-
lines.push("");
|
|
2195
|
-
if (result.hardFailures > 0 || result.skipped > 0) {
|
|
2196
|
-
console.error(lines.join("\n"));
|
|
2197
|
-
return 1;
|
|
2198
|
-
}
|
|
2199
|
-
console.log(lines.join("\n"));
|
|
2200
|
-
return 0;
|
|
2201
|
-
}
|
|
2202
|
-
function info(flags) {
|
|
2203
|
-
const pkgRoot = packageRoot();
|
|
2204
|
-
let pkgName = "basalt-ui";
|
|
2205
|
-
let pkgVersion = "0.0.0";
|
|
2206
|
-
let peerDeps = {};
|
|
2207
|
-
let peerMeta = {};
|
|
2208
|
-
let pkgExports = {};
|
|
2209
|
-
try {
|
|
2210
|
-
const pkg = JSON.parse(readFileSync(resolve(pkgRoot, "package.json"), "utf8"));
|
|
2211
|
-
pkgName = pkg.name ?? pkgName;
|
|
2212
|
-
pkgVersion = pkg.version ?? pkgVersion;
|
|
2213
|
-
peerDeps = pkg.peerDependencies ?? {};
|
|
2214
|
-
peerMeta = pkg.peerDependenciesMeta ?? {};
|
|
2215
|
-
pkgExports = pkg.exports ?? {};
|
|
2216
|
-
} catch {
|
|
2217
|
-
}
|
|
2218
|
-
const optionalPeerSet = new Set(
|
|
2219
|
-
Object.entries(peerMeta).filter(([, m]) => m.optional === true).map(([name]) => name)
|
|
2220
|
-
);
|
|
2221
|
-
const publishedExportKeys = new Set(Object.keys(pkgExports));
|
|
2222
|
-
const subpaths = [];
|
|
2223
|
-
for (const [key, spec] of Object.entries(SURFACES)) {
|
|
2224
|
-
if (key.startsWith("#")) continue;
|
|
2225
|
-
if (!publishedExportKeys.has(key)) continue;
|
|
2226
|
-
const docSpec = spec.kind === "doctrine" ? spec : null;
|
|
2227
|
-
const specPeers = docSpec !== null && "optionalPeers" in docSpec && Array.isArray(docSpec.optionalPeers) ? docSpec.optionalPeers : [];
|
|
2228
|
-
const optionalPeers = specPeers.filter((p) => optionalPeerSet.has(p) && p in peerDeps).map((p) => `${p}@${peerDeps[p]}`);
|
|
2229
|
-
subpaths.push({
|
|
2230
|
-
path: `basalt-ui${key === "." ? "" : key.slice(1)}`,
|
|
2231
|
-
description: spec.description ?? `basalt-ui${key === "." ? "" : key} subpath`,
|
|
2232
|
-
layer: spec.layer,
|
|
2233
|
-
rule: docSpec?.rule ?? null,
|
|
2234
|
-
skills: docSpec?.skill ?? [],
|
|
2235
|
-
optionalPeers
|
|
2236
|
-
});
|
|
2237
|
-
}
|
|
2238
|
-
const output = { name: pkgName, version: pkgVersion, subpaths };
|
|
2239
|
-
if (flags.includes("--json")) {
|
|
2240
|
-
console.log(JSON.stringify(output, null, 2));
|
|
2241
|
-
return 0;
|
|
2242
|
-
}
|
|
2243
|
-
console.log(`
|
|
2244
|
-
basalt-ui v${pkgVersion} \u2014 published surface
|
|
2245
|
-
`);
|
|
2246
|
-
const COL = { path: 32, layer: 18, rule: 18, skills: 36 };
|
|
2247
|
-
const header = [
|
|
2248
|
-
"SUBPATH".padEnd(COL.path),
|
|
2249
|
-
"LAYER".padEnd(COL.layer),
|
|
2250
|
-
"RULE".padEnd(COL.rule),
|
|
2251
|
-
"SKILLS".padEnd(COL.skills),
|
|
2252
|
-
"OPTIONAL PEERS"
|
|
2253
|
-
].join(" ");
|
|
2254
|
-
console.log(header);
|
|
2255
|
-
console.log("-".repeat(header.length));
|
|
2256
|
-
for (const row of output.subpaths) {
|
|
2257
|
-
const line = [
|
|
2258
|
-
row.path.padEnd(COL.path),
|
|
2259
|
-
row.layer.padEnd(COL.layer),
|
|
2260
|
-
(row.rule ?? "\u2014").padEnd(COL.rule),
|
|
2261
|
-
(row.skills.join(", ") || "\u2014").padEnd(COL.skills),
|
|
2262
|
-
row.optionalPeers.join(", ") || "\u2014"
|
|
2263
|
-
].join(" ");
|
|
2264
|
-
console.log(line);
|
|
2265
|
-
}
|
|
2266
|
-
console.log("");
|
|
2267
|
-
return 0;
|
|
2268
|
-
}
|
|
2269
|
-
const DEFAULT_APP_ICON_FILES = [
|
|
2270
|
-
"favicon.ico",
|
|
2271
|
-
"favicon.svg",
|
|
2272
|
-
"favicon-96x96.png",
|
|
2273
|
-
"apple-touch-icon.png",
|
|
2274
|
-
"web-app-manifest-192x192.png",
|
|
2275
|
-
"web-app-manifest-512x512.png"
|
|
2276
|
-
];
|
|
2277
|
-
const VITE_CONFIG_FILES = [
|
|
2278
|
-
"vite.config.ts",
|
|
2279
|
-
"vite.config.mts",
|
|
2280
|
-
"vite.config.cts",
|
|
2281
|
-
"vite.config.js",
|
|
2282
|
-
"vite.config.mjs",
|
|
2283
|
-
"vite.config.cjs"
|
|
2284
|
-
];
|
|
2285
|
-
function findAppPluginDir(cwd, cfg) {
|
|
2286
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2287
|
-
const candidates = [];
|
|
2288
|
-
const add = (dir) => {
|
|
2289
|
-
if (seen.has(dir)) return;
|
|
2290
|
-
seen.add(dir);
|
|
2291
|
-
candidates.push(dir);
|
|
2292
|
-
};
|
|
2293
|
-
add(cwd);
|
|
2294
|
-
for (const root of resolveRoots(cfg)) {
|
|
2295
|
-
let current = resolve(cwd, root);
|
|
2296
|
-
while (current !== cwd && !relative(cwd, current).startsWith("..")) {
|
|
2297
|
-
add(current);
|
|
2298
|
-
const parent = dirname(current);
|
|
2299
|
-
if (parent === current) break;
|
|
2300
|
-
current = parent;
|
|
2301
|
-
}
|
|
2302
|
-
}
|
|
2303
|
-
for (const dir of candidates) {
|
|
2304
|
-
const configPath = VITE_CONFIG_FILES.map((f) => resolve(dir, f)).find((f) => existsSync(f));
|
|
2305
|
-
if (configPath === void 0) continue;
|
|
2306
|
-
if (readIfExists(configPath)?.includes("basaltAppPlugin(") === true) return dir;
|
|
2307
|
-
}
|
|
2308
|
-
return null;
|
|
2309
|
-
}
|
|
2310
|
-
function balancedSpan(text, open) {
|
|
2311
|
-
const pairs = { "(": ")", "{": "}", "[": "]" };
|
|
2312
|
-
const closer = pairs[text[open]];
|
|
2313
|
-
if (closer === void 0) return null;
|
|
2314
|
-
let depth = 0;
|
|
2315
|
-
for (let i = open; i < text.length; i++) {
|
|
2316
|
-
const ch = text[i];
|
|
2317
|
-
if (ch === text[open]) depth++;
|
|
2318
|
-
else if (ch === closer) {
|
|
2319
|
-
depth--;
|
|
2320
|
-
if (depth === 0) return text.slice(open, i + 1);
|
|
2321
|
-
}
|
|
2322
|
-
}
|
|
2323
|
-
return null;
|
|
2324
|
-
}
|
|
2325
|
-
function readAppIconsOption(cwd) {
|
|
2326
|
-
const configPath = VITE_CONFIG_FILES.map((f) => resolve(cwd, f)).find((f) => existsSync(f));
|
|
2327
|
-
if (configPath === void 0) return { kind: "unknown" };
|
|
2328
|
-
const text = readIfExists(configPath);
|
|
2329
|
-
if (text === null) return { kind: "unknown" };
|
|
2330
|
-
const call = text.indexOf("basaltAppPlugin(");
|
|
2331
|
-
if (call === -1) return { kind: "unknown" };
|
|
2332
|
-
const args = balancedSpan(text, call + "basaltAppPlugin".length);
|
|
2333
|
-
if (args === null) return { kind: "unknown" };
|
|
2334
|
-
const key = args.search(/\bicons\s*:/);
|
|
2335
|
-
if (key === -1) return { kind: "default", dir: null };
|
|
2336
|
-
const valueStart = args.indexOf(":", key) + 1;
|
|
2337
|
-
const rest = args.slice(valueStart).trimStart();
|
|
2338
|
-
if (rest.startsWith("false")) return { kind: "none" };
|
|
2339
|
-
const offset = args.length - rest.length;
|
|
2340
|
-
if (rest.startsWith("[")) {
|
|
2341
|
-
const list = balancedSpan(args, offset);
|
|
2342
|
-
if (list === null) return { kind: "unknown" };
|
|
2343
|
-
const sources = [...list.matchAll(/\bsrc\s*:\s*['"`]([^'"`]+)['"`]/g)].map(
|
|
2344
|
-
(m) => m[1]
|
|
2345
|
-
);
|
|
2346
|
-
return sources.length === 0 ? { kind: "none" } : { kind: "explicit", sources };
|
|
2347
|
-
}
|
|
2348
|
-
if (rest.startsWith("{")) {
|
|
2349
|
-
const obj = balancedSpan(args, offset);
|
|
2350
|
-
if (obj === null) return { kind: "unknown" };
|
|
2351
|
-
const dir = /\bdir\s*:\s*['"`]([^'"`]+)['"`]/.exec(obj);
|
|
2352
|
-
return { kind: "default", dir: dir === null ? null : dir[1] };
|
|
2353
|
-
}
|
|
2354
|
-
return { kind: "unknown" };
|
|
2355
|
-
}
|
|
2356
|
-
function flagValue(flags, name) {
|
|
2357
|
-
const i = flags.indexOf(name);
|
|
2358
|
-
if (i === -1) return void 0;
|
|
2359
|
-
const value = flags[i + 1];
|
|
2360
|
-
return value === void 0 || value.startsWith("--") ? void 0 : value;
|
|
2361
|
-
}
|
|
2362
|
-
function generatedHeader(version, command, flags) {
|
|
2363
|
-
const shown = flags.filter((f) => f !== "--check");
|
|
2364
|
-
const invocation = shown.length === 0 ? command : `${command} ${shown.join(" ")}`;
|
|
2365
|
-
return `${GENERATED_HEADER_LINE}
|
|
2366
|
-
/* basalt-ui ${version} \u2014 \`basalt-ui ${invocation}\` */
|
|
2367
|
-
`;
|
|
2368
|
-
}
|
|
2369
|
-
const PROVENANCE_VERSION = /^(\/\* basalt-ui )(\d+\.\d+\.\d+\S*)( — `basalt-ui (?:tokens:css|fonts:css)[^`]*` \*\/)$/;
|
|
2370
|
-
function provenanceVersion(css) {
|
|
2371
|
-
const match = PROVENANCE_VERSION.exec(css.split("\n")[1] ?? "");
|
|
2372
|
-
return match === null ? null : match[2];
|
|
2373
|
-
}
|
|
2374
|
-
function withoutProvenanceVersion(css) {
|
|
2375
|
-
const lines = css.split("\n");
|
|
2376
|
-
const line = lines[1];
|
|
2377
|
-
if (line === void 0) return css;
|
|
2378
|
-
const match = PROVENANCE_VERSION.exec(line);
|
|
2379
|
-
if (match === null) return css;
|
|
2380
|
-
lines[1] = `${match[1]}<version>${match[3]}`;
|
|
2381
|
-
return lines.join("\n");
|
|
2382
|
-
}
|
|
2383
|
-
function versionTriple(version) {
|
|
2384
|
-
const match = /^(\d+)\.(\d+)\.(\d+)/.exec(version);
|
|
2385
|
-
return match === null ? null : [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
2386
|
-
}
|
|
2387
|
-
function describeVersionSkew(onDisk, current) {
|
|
2388
|
-
const a = versionTriple(onDisk);
|
|
2389
|
-
const b = versionTriple(current);
|
|
2390
|
-
if (a === null || b === null) return "a different";
|
|
2391
|
-
for (let i = 0; i < 3; i++) {
|
|
2392
|
-
const left = a[i];
|
|
2393
|
-
const right = b[i];
|
|
2394
|
-
if (left < right) return "an older";
|
|
2395
|
-
if (left > right) return "a newer";
|
|
2396
|
-
}
|
|
2397
|
-
return "a different";
|
|
2398
|
-
}
|
|
2399
|
-
function normalizeColorFunctions(css) {
|
|
2400
|
-
return css.replace(/\b(rgba?|hsla?)\(([^()]*)\)/g, (_match, fn, args) => {
|
|
2401
|
-
const parts = args.split(",").map((part) => normalizeCssNumber(part.trim()));
|
|
2402
|
-
return `${fn}(${parts.join(", ")})`;
|
|
2403
|
-
});
|
|
2404
|
-
}
|
|
2405
|
-
function normalizeCssNumber(arg) {
|
|
2406
|
-
if (!/^\d+\.\d+$/.test(arg)) return arg;
|
|
2407
|
-
return arg.replace(/\.?0+$/, "");
|
|
2408
|
-
}
|
|
2409
|
-
const SCHEME_CLASS_SENTINEL = "data-basalt-scheme-class";
|
|
2410
|
-
function emitGeneratedCss(content, flags, cwd, command) {
|
|
2411
|
-
const out = flagValue(flags, "--out");
|
|
2412
|
-
const check = flags.includes("--check");
|
|
2413
|
-
if (out === void 0) {
|
|
2414
|
-
if (check) {
|
|
2415
|
-
console.error(
|
|
2416
|
-
`${command}: --check needs --out <path> \u2014 there is nothing to compare stdout to.`
|
|
2417
|
-
);
|
|
2418
|
-
return 1;
|
|
2419
|
-
}
|
|
2420
|
-
process.stdout.write(content);
|
|
2421
|
-
return 0;
|
|
2422
|
-
}
|
|
2423
|
-
const target = isAbsolute(out) ? out : resolve(cwd, out);
|
|
2424
|
-
const shown = relative(cwd, target) || target;
|
|
2425
|
-
if (check) {
|
|
2426
|
-
const onDisk = readIfExists(target);
|
|
2427
|
-
if (onDisk === null) {
|
|
2428
|
-
console.error(
|
|
2429
|
-
`\u2716 ${command} --check: ${shown} does not exist \u2014 run without --check to write it.`
|
|
2430
|
-
);
|
|
2431
|
-
return 1;
|
|
2432
|
-
}
|
|
2433
|
-
if (withoutProvenanceVersion(onDisk) === withoutProvenanceVersion(content)) {
|
|
2434
|
-
const diskVersion = provenanceVersion(onDisk);
|
|
2435
|
-
const currentVersion = provenanceVersion(content);
|
|
2436
|
-
const skew = diskVersion !== null && currentVersion !== null && diskVersion !== currentVersion ? ` (Its provenance line names basalt-ui ${diskVersion}, ${describeVersionSkew(diskVersion, currentVersion)} release than the ${currentVersion} that just ran \u2014 only that version token is excluded from the comparison. Re-run without --check whenever you want it refreshed.)` : "";
|
|
2437
|
-
console.log(`\u2713 ${command} --check: ${shown} is up to date.${skew}`);
|
|
2438
|
-
return 0;
|
|
2439
|
-
}
|
|
2440
|
-
const diskLines = onDisk.split("\n");
|
|
2441
|
-
const wantLines = content.split("\n");
|
|
2442
|
-
const provenanceOnly = diskLines.length === wantLines.length && diskLines.every((line, i) => i === 1 || line === wantLines[i]) && diskLines[1] !== wantLines[1];
|
|
2443
|
-
if (provenanceOnly) {
|
|
2444
|
-
console.error(
|
|
2445
|
-
`\u2716 ${command} --check: ${shown}'s \`@generated\` provenance line does not match the command that produced this content \u2014 the token values agree, line 2 does not.
|
|
2446
|
-
on disk: ${diskLines[1]}
|
|
2447
|
-
emitted: ${wantLines[1]}
|
|
2448
|
-
Line 1 tells the reader to regenerate with line 2, so line 2 is gated: run the emitted invocation without --check and commit the result, or change the flags this check runs with to the ones the file was really built from. Only the version token is exempt.`
|
|
2449
|
-
);
|
|
2450
|
-
return 1;
|
|
2451
|
-
}
|
|
2452
|
-
console.error(
|
|
2453
|
-
`\u2716 ${command} --check: ${shown} differs from what \`basalt-ui ${command}\` emits today (on disk ${diskLines.length} lines, emitted ${wantLines.length}) \u2014 re-run the same command without --check and commit the result. Only the \`@generated\` provenance line's VERSION token is excluded from this comparison, so a release alone never trips it \u2014 but its flags do.`
|
|
2454
|
-
);
|
|
2455
|
-
return 1;
|
|
2456
|
-
}
|
|
2457
|
-
mkdirSync(dirname(target), { recursive: true });
|
|
2458
|
-
writeFileSync(target, content);
|
|
2459
|
-
console.log(`wrote ${content.split("\n").length} lines \u2192 ${shown}`);
|
|
2460
|
-
return 0;
|
|
2461
|
-
}
|
|
2462
|
-
function tokensCss(flags, cwd = process.cwd()) {
|
|
2463
|
-
const attribute = flagValue(flags, "--selector-attribute");
|
|
2464
|
-
const schemeClass = flagValue(flags, "--selector-class");
|
|
2465
|
-
const lightClass = flagValue(flags, "--light-class");
|
|
2466
|
-
const darkValue = flagValue(flags, "--dark-value");
|
|
2467
|
-
const lightValue = flagValue(flags, "--light-value");
|
|
2468
|
-
const defaultScheme = flagValue(flags, "--default-scheme");
|
|
2469
|
-
const only = flagValue(flags, "--only");
|
|
2470
|
-
if (defaultScheme !== void 0 && !["dark", "light", "none"].includes(defaultScheme)) {
|
|
2471
|
-
console.error(
|
|
2472
|
-
`tokens:css: --default-scheme must be dark, light or none (got '${defaultScheme}')`
|
|
2473
|
-
);
|
|
2474
|
-
return 1;
|
|
2475
|
-
}
|
|
2476
|
-
if (only !== void 0 && !["core", "all"].includes(only)) {
|
|
2477
|
-
console.error(`tokens:css: --only must be core or all (got '${only}')`);
|
|
2478
|
-
return 1;
|
|
2479
|
-
}
|
|
2480
|
-
if (schemeClass !== void 0 && attribute !== void 0) {
|
|
2481
|
-
console.error(
|
|
2482
|
-
"tokens:css: --selector-class and --selector-attribute are alternatives \u2014 pass one."
|
|
2483
|
-
);
|
|
2484
|
-
return 1;
|
|
2485
|
-
}
|
|
2486
|
-
if (schemeClass !== void 0 && /[^\w-]/.test(schemeClass)) {
|
|
2487
|
-
console.error(
|
|
2488
|
-
`tokens:css: --selector-class must be a plain CSS class name (got '${schemeClass}')`
|
|
2489
|
-
);
|
|
2490
|
-
return 1;
|
|
2491
|
-
}
|
|
2492
|
-
if (lightClass !== void 0 && /[^\w-]/.test(lightClass)) {
|
|
2493
|
-
console.error(`tokens:css: --light-class must be a plain CSS class name (got '${lightClass}')`);
|
|
2494
|
-
return 1;
|
|
2495
|
-
}
|
|
2496
|
-
const resolvedDarkValue = schemeClass !== void 0 ? schemeClass : darkValue;
|
|
2497
|
-
const resolvedLightValue = schemeClass !== void 0 ? lightClass ?? "light" : lightValue;
|
|
2498
|
-
const resolvedAttribute = schemeClass !== void 0 ? SCHEME_CLASS_SENTINEL : attribute;
|
|
2499
|
-
const scheme = {
|
|
2500
|
-
...resolvedAttribute === void 0 ? {} : { attribute: resolvedAttribute },
|
|
2501
|
-
...resolvedDarkValue === void 0 ? {} : { darkValue: resolvedDarkValue },
|
|
2502
|
-
...resolvedLightValue === void 0 ? {} : { lightValue: resolvedLightValue }
|
|
2503
|
-
};
|
|
2504
|
-
let css = buildPaletteCss({
|
|
2505
|
-
...Object.keys(scheme).length === 0 ? {} : { scheme },
|
|
2506
|
-
...defaultScheme === void 0 ? {} : { defaultScheme },
|
|
2507
|
-
...flags.includes("--media-fallback") ? { mediaFallback: true } : {},
|
|
2508
|
-
...only === void 0 ? {} : { only },
|
|
2509
|
-
// The tokens-only consumer is exactly the one who writes these names by hand, so they are also
|
|
2510
|
-
// the one who wants the deprecated camelCase aliases gone — and this command is their only
|
|
2511
|
-
// entry point. Without the flag the CLI and the API could disagree about what basalt's tokens
|
|
2512
|
-
// are, which is the one thing this command is documented never to allow.
|
|
2513
|
-
...flags.includes("--no-legacy-aliases") ? { legacyAliases: false } : {}
|
|
2514
|
-
});
|
|
2515
|
-
if (schemeClass !== void 0) {
|
|
2516
|
-
css = css.replace(
|
|
2517
|
-
new RegExp(`\\[${SCHEME_CLASS_SENTINEL}='([\\w-]+)'\\]`, "g"),
|
|
2518
|
-
(_match, value) => `.${value}`
|
|
2519
|
-
);
|
|
2520
|
-
}
|
|
2521
|
-
const version = readFrameworkVersion(packageRoot());
|
|
2522
|
-
const content = `${generatedHeader(version, "tokens:css", flags)}${normalizeColorFunctions(css)}
|
|
2523
|
-
`;
|
|
2524
|
-
return emitGeneratedCss(content, flags, cwd, "tokens:css");
|
|
2525
|
-
}
|
|
2526
|
-
function readShippedFontDecls(pkgRoot) {
|
|
2527
|
-
const css = readSource(pkgRoot, "dist/styles.css") ?? readSource(pkgRoot, "src/styles.css");
|
|
2528
|
-
if (css === null) return [];
|
|
2529
|
-
const decls = [];
|
|
2530
|
-
for (const match of css.matchAll(/(--basalt-font[\w-]*)\s*:\s*([^;]+);/g)) {
|
|
2531
|
-
decls.push({
|
|
2532
|
-
name: match[1],
|
|
2533
|
-
value: match[2].replace(/\s+/g, " ").trim()
|
|
2534
|
-
});
|
|
2535
|
-
}
|
|
2536
|
-
return decls;
|
|
2537
|
-
}
|
|
2538
|
-
function fontsCss(flags, cwd = process.cwd()) {
|
|
2539
|
-
const pkgRoot = packageRoot();
|
|
2540
|
-
const decls = readShippedFontDecls(pkgRoot);
|
|
2541
|
-
if (decls.length === 0) {
|
|
2542
|
-
console.error(
|
|
2543
|
-
"fonts:css: could not read the shipped font stacks from styles.css \u2014 this is a packaging bug in basalt-ui, not a config error."
|
|
2544
|
-
);
|
|
2545
|
-
return 1;
|
|
2546
|
-
}
|
|
2547
|
-
const body = `:root {
|
|
2548
|
-
${decls.map((d) => ` ${d.name}: ${d.value};`).join("\n")}
|
|
2549
|
-
}
|
|
2550
|
-
`;
|
|
2551
|
-
const header = generatedHeader(readFrameworkVersion(pkgRoot), "fonts:css", flags);
|
|
2552
|
-
return emitGeneratedCss(`${header}${body}`, flags, cwd, "fonts:css");
|
|
2553
|
-
}
|
|
2554
|
-
async function guardHook(cwd = process.cwd()) {
|
|
2555
|
-
let raw;
|
|
2556
|
-
try {
|
|
2557
|
-
if (typeof globalThis["Bun"] !== "undefined") {
|
|
2558
|
-
raw = await globalThis["Bun"].stdin.text();
|
|
2559
|
-
} else {
|
|
2560
|
-
const chunks = [];
|
|
2561
|
-
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
2562
|
-
raw = Buffer.concat(chunks).toString("utf8");
|
|
2563
|
-
}
|
|
2564
|
-
} catch {
|
|
2565
|
-
process.stdout.write(
|
|
2566
|
-
'{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"}}\n'
|
|
2567
|
-
);
|
|
2568
|
-
return 0;
|
|
2569
|
-
}
|
|
2570
|
-
let payload;
|
|
2571
|
-
try {
|
|
2572
|
-
payload = JSON.parse(raw);
|
|
2573
|
-
} catch {
|
|
2574
|
-
process.stdout.write(
|
|
2575
|
-
'{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"}}\n'
|
|
2576
|
-
);
|
|
2577
|
-
return 0;
|
|
2578
|
-
}
|
|
2579
|
-
const cfg = readBasaltConfig(cwd);
|
|
2580
|
-
const guardCfg = {
|
|
2581
|
-
spacingSteps: cfg.spacingSteps ?? DEFAULT_GUARD_CONFIG.spacingSteps,
|
|
2582
|
-
rawRadius: cfg.rawRadius ?? DEFAULT_GUARD_CONFIG.rawRadius,
|
|
2583
|
-
forbiddenAccents: cfg.forbiddenAccents ?? DEFAULT_GUARD_CONFIG.forbiddenAccents,
|
|
2584
|
-
mantineShadeIndex: cfg.mantineShadeIndex ?? DEFAULT_GUARD_CONFIG.mantineShadeIndex,
|
|
2585
|
-
rawSurface: cfg.rawSurface ?? DEFAULT_GUARD_CONFIG.rawSurface,
|
|
2586
|
-
cardWithBorder: cfg.cardWithBorder ?? DEFAULT_GUARD_CONFIG.cardWithBorder,
|
|
2587
|
-
offSystemSurfaceVar: cfg.offSystemSurfaceVar ?? DEFAULT_GUARD_CONFIG.offSystemSurfaceVar,
|
|
2588
|
-
rawHtmlLayout: cfg.rawHtmlLayout ?? DEFAULT_GUARD_CONFIG.rawHtmlLayout,
|
|
2589
|
-
inlineSpacing: cfg.inlineSpacing ?? DEFAULT_GUARD_CONFIG.inlineSpacing,
|
|
2590
|
-
inlineDisplay: cfg.inlineDisplay ?? DEFAULT_GUARD_CONFIG.inlineDisplay,
|
|
2591
|
-
rawVisxAxis: cfg.rawVisxAxis ?? DEFAULT_GUARD_CONFIG.rawVisxAxis,
|
|
2592
|
-
rawMotionValue: cfg.rawMotionValue ?? DEFAULT_GUARD_CONFIG.rawMotionValue,
|
|
2593
|
-
unframedChart: cfg.unframedChart ?? DEFAULT_GUARD_CONFIG.unframedChart,
|
|
2594
|
-
chartMissingAriaLabel: cfg.chartMissingAriaLabel ?? DEFAULT_GUARD_CONFIG.chartMissingAriaLabel,
|
|
2595
|
-
rawFormControl: cfg.rawFormControl ?? DEFAULT_GUARD_CONFIG.rawFormControl,
|
|
2596
|
-
sub16InputFont: cfg.sub16InputFont ?? DEFAULT_GUARD_CONFIG.sub16InputFont,
|
|
2597
|
-
allowComment: "theme-allow",
|
|
2598
|
-
exemptRules: resolveExemptRules(cfg),
|
|
2599
|
-
severity: cfg.severity ?? DEFAULT_GUARD_CONFIG.severity,
|
|
2600
|
-
// Same detection check-theme uses: the hook must never block an edit over advice the app
|
|
2601
|
-
// cannot take ("use @mantine/core's Select") in a repo with no Mantine.
|
|
2602
|
-
...declaredProfile(cfg, []) === "tokens-only" ? { profile: "tokens-only" } : {}
|
|
2603
|
-
};
|
|
2604
|
-
const roots = cfg.roots ?? DEFAULT_ROOTS;
|
|
2605
|
-
const exempt = new Set(cfg.exempt ?? defaultExempt(cfg));
|
|
2606
|
-
const isInScope = (filePath) => {
|
|
2607
|
-
const abs = isAbsolute(filePath) ? filePath : resolve(cwd, filePath);
|
|
2608
|
-
const rel = relative(cwd, abs).replace(/\\/g, "/");
|
|
2609
|
-
if (rel === "" || rel.startsWith("..")) return false;
|
|
2610
|
-
if (SKIP.test(rel) || exempt.has(rel)) return false;
|
|
2611
|
-
return roots.some((root) => {
|
|
2612
|
-
const r = root.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
2613
|
-
return rel === r || rel.startsWith(`${r}/`);
|
|
2614
|
-
});
|
|
2615
|
-
};
|
|
2616
|
-
const result = evaluateGuardHook(payload, guardCfg, { isInScope });
|
|
2617
|
-
if (result.permissionDecision === "deny" && result.reason !== void 0) {
|
|
2618
|
-
process.stdout.write(
|
|
2619
|
-
JSON.stringify({
|
|
2620
|
-
hookSpecificOutput: {
|
|
2621
|
-
hookEventName: "PreToolUse",
|
|
2622
|
-
permissionDecision: "deny",
|
|
2623
|
-
permissionDecisionReason: result.reason
|
|
2624
|
-
}
|
|
2625
|
-
}) + "\n"
|
|
2626
|
-
);
|
|
2627
|
-
} else {
|
|
2628
|
-
process.stdout.write(
|
|
2629
|
-
'{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"}}\n'
|
|
2630
|
-
);
|
|
2631
|
-
}
|
|
2632
|
-
return 0;
|
|
2633
|
-
}
|
|
2634
|
-
const COMMAND_FLAGS = {
|
|
2635
|
-
init: ["--with-router", "--with-query", "--merge-lint"],
|
|
2636
|
-
sync: ["--force", "--check", "--tokens-only", "--framework"],
|
|
2637
|
-
"check-theme": ["--audit-allows", "--tokens-only", "--framework"],
|
|
2638
|
-
"check-coverage": ["--write", "--check"],
|
|
2639
|
-
info: ["--json"],
|
|
2640
|
-
doctor: ["--tokens-only", "--framework"],
|
|
2641
|
-
"guard-hook": [],
|
|
2642
|
-
"tokens:css": [
|
|
2643
|
-
"--out",
|
|
2644
|
-
"--check",
|
|
2645
|
-
"--selector-attribute",
|
|
2646
|
-
"--selector-class",
|
|
2647
|
-
"--light-class",
|
|
2648
|
-
"--dark-value",
|
|
2649
|
-
"--light-value",
|
|
2650
|
-
"--default-scheme",
|
|
2651
|
-
"--media-fallback",
|
|
2652
|
-
"--only",
|
|
2653
|
-
"--no-legacy-aliases"
|
|
2654
|
-
],
|
|
2655
|
-
"fonts:css": ["--out", "--check"]
|
|
2656
|
-
};
|
|
2657
|
-
const VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
2658
|
-
"--out",
|
|
2659
|
-
"--selector-attribute",
|
|
2660
|
-
"--selector-class",
|
|
2661
|
-
"--light-class",
|
|
2662
|
-
"--dark-value",
|
|
2663
|
-
"--light-value",
|
|
2664
|
-
"--default-scheme",
|
|
2665
|
-
"--only"
|
|
2666
|
-
]);
|
|
2667
|
-
function unknownFlag(cmd, flags) {
|
|
2668
|
-
const allowed = COMMAND_FLAGS[cmd];
|
|
2669
|
-
if (allowed === void 0) return null;
|
|
2670
|
-
for (let i = 0; i < flags.length; i++) {
|
|
2671
|
-
const flag = flags[i];
|
|
2672
|
-
if (!flag.startsWith("-")) continue;
|
|
2673
|
-
if (!allowed.includes(flag)) return flag;
|
|
2674
|
-
if (VALUE_FLAGS.has(flag)) i++;
|
|
2675
|
-
}
|
|
2676
|
-
return null;
|
|
2677
|
-
}
|
|
2678
|
-
const USAGE = "Usage: basalt-ui <--version | init [--with-router] [--with-query] [--merge-lint] |\n sync [--force] [--check] [--tokens-only|--framework] |\n check-theme [--audit-allows] | check-coverage [--write|--check] |\n info [--json] |\n doctor [--tokens-only|--framework] | guard-hook | tokens:css | fonts:css | help>\n\ncheck-theme [--tokens-only|--framework] [--audit-allows]\n --audit-allows reports instead of scanning: every `theme-allow` annotation and every\n `basalt.exemptRules` entry, with what each one still suppresses \u2014 proved by re-running the\n guard with that one waiver neutralized, not by reading its text. Exits 1 on a waiver that\n suppresses nothing (dead), which is what makes it usable as a CI gate.\n\ntokens:css [--out <path>] [--check] [--selector-attribute <attr> | --selector-class <class>]\n [--light-class <class>] [--dark-value <v>] [--light-value <v>]\n [--default-scheme <dark|light|none>] [--media-fallback] [--only <core|all>]\n [--no-legacy-aliases]\n Emit the --vx-* stylesheet (stdout unless --out). Defaults reproduce basalt-ui/tokens.css.\n --selector-class emits `:root.dark` instead of an attribute selector (the Tailwind convention).\n --check writes nothing and exits 1 when --out differs from what would be emitted (a CI gate).\n --no-legacy-aliases drops the deprecated camelCase spellings (--vx-accentFill and friends),\n which are emitted by default as aliases of the canonical kebab-case names.\n\nfonts:css [--out <path>] [--check]\n Emit the shipped --basalt-font-* stacks as plain CSS \u2014 the typeface half of the token layer,\n otherwise reachable only by importing styles.css.\n\ndoctor [--tokens-only|--framework]\n Auto-detects a tokens-only consumer (no manifest + no @mantine/core) and checks only what\n applies. A check that cannot RUN is reported as SKIPPED and exits non-zero.\n\nsync [--force] [--check] [--tokens-only|--framework]\n Refreshes an EXISTING install; it never creates one (that is init). A tokens-only consumer\n has no scaffold to reconcile, so sync reports n/a and exits 0 there \u2014 as doctor does.\n\ncheck-theme / doctor / sync all resolve their project the same way: they honour BASALT_CWD, and\nrelocate to the single package below the cwd carrying a basalt config when invoked from a repo\nroot that has none \u2014 several carrying one is ambiguous and exits 1 in ALL THREE.\n--tokens-only and --framework are alternatives; passing both is an error.\n\nEvery subcommand accepts --help / -h to print this message and exit without running.\n--version / -v prints the resolved package version and exits 0 \u2014 the one command that proves\nWHICH basalt-ui ran, which a `bunx` cache can otherwise make a lie. An unknown command or an\nunrecognized flag exits 1 and names what it did not understand; neither is ever ignored.";
|
|
2679
830
|
function run(argv, cwd = process.cwd()) {
|
|
2680
831
|
const [cmd, ...flags] = argv;
|
|
2681
832
|
if (cmd === "help" || cmd === "--help" || cmd === "-h" || flags.includes("--help") || flags.includes("-h")) {
|
|
@@ -2689,8 +840,9 @@ function run(argv, cwd = process.cwd()) {
|
|
|
2689
840
|
if (cmd !== void 0) {
|
|
2690
841
|
const bad = unknownFlag(cmd, flags);
|
|
2691
842
|
if (bad !== null) {
|
|
843
|
+
const accepted = Object.keys(COMMAND_OPTIONS[cmd] ?? {}).map((k) => `--${k}`);
|
|
2692
844
|
console.error(
|
|
2693
|
-
`basalt-ui ${cmd}: unrecognized flag '${bad}'. Accepted: ${
|
|
845
|
+
`basalt-ui ${cmd}: unrecognized flag '${bad}'. Accepted: ${accepted.join(" ") || "(none)"} --help
|
|
2694
846
|
`
|
|
2695
847
|
);
|
|
2696
848
|
return 1;
|
|
@@ -2710,10 +862,6 @@ function run(argv, cwd = process.cwd()) {
|
|
|
2710
862
|
);
|
|
2711
863
|
case "check-theme":
|
|
2712
864
|
return checkTheme(cwd, flags);
|
|
2713
|
-
case "check-coverage":
|
|
2714
|
-
return checkCoverage(flags);
|
|
2715
|
-
case "info":
|
|
2716
|
-
return info(flags);
|
|
2717
865
|
case "doctor":
|
|
2718
866
|
return doctor(cwd, flags);
|
|
2719
867
|
case "guard-hook":
|
|
@@ -2732,28 +880,64 @@ function run(argv, cwd = process.cwd()) {
|
|
|
2732
880
|
}
|
|
2733
881
|
}
|
|
2734
882
|
export {
|
|
2735
|
-
|
|
2736
|
-
|
|
883
|
+
DEFAULT_ROOT,
|
|
884
|
+
DEFAULT_ROOTS,
|
|
2737
885
|
MANIFEST_PATH,
|
|
886
|
+
ROOT_PLACEMENT,
|
|
2738
887
|
RULE_NAMES,
|
|
888
|
+
SERIES_MODULE_HINT_RE,
|
|
2739
889
|
SKILL_NAMES,
|
|
2740
|
-
|
|
2741
|
-
|
|
890
|
+
SKIP,
|
|
891
|
+
basaltBinCommand,
|
|
892
|
+
basaltPresetEntry,
|
|
2742
893
|
checkTheme,
|
|
2743
|
-
|
|
2744
|
-
|
|
894
|
+
classify,
|
|
895
|
+
conflictingProfileFlags,
|
|
896
|
+
declaredProfile,
|
|
897
|
+
defaultExempt,
|
|
898
|
+
diffSummary,
|
|
2745
899
|
doctor,
|
|
900
|
+
exemptRulePaths,
|
|
901
|
+
exemptRuleReason,
|
|
902
|
+
findBasaltInstall,
|
|
903
|
+
findRepoRoot,
|
|
2746
904
|
fontsCss,
|
|
2747
905
|
guardHook,
|
|
2748
|
-
info,
|
|
2749
906
|
init,
|
|
907
|
+
inspectLefthookGate,
|
|
908
|
+
isStringArray,
|
|
2750
909
|
managedFiles,
|
|
910
|
+
mergeOxlintExtends,
|
|
2751
911
|
normalizeColorFunctions,
|
|
2752
912
|
normalizeForLedger,
|
|
2753
|
-
|
|
2754
|
-
|
|
913
|
+
packageRoot,
|
|
914
|
+
parseJsonc,
|
|
915
|
+
patchPackageJson,
|
|
916
|
+
pruneRetiredManagedFiles,
|
|
917
|
+
readBasaltConfig,
|
|
918
|
+
readFrameworkVersion,
|
|
919
|
+
readIfExists,
|
|
920
|
+
readManifest,
|
|
921
|
+
readSource,
|
|
922
|
+
reconcileRoots,
|
|
923
|
+
relativePosix,
|
|
924
|
+
renderContext,
|
|
925
|
+
reportPrune,
|
|
926
|
+
resolveExemptRules,
|
|
927
|
+
resolvePeerFlags,
|
|
928
|
+
resolvePlacement,
|
|
929
|
+
resolveProjectDir,
|
|
930
|
+
resolveRoots,
|
|
2755
931
|
retiredManagedDests,
|
|
932
|
+
rootsNotices,
|
|
2756
933
|
run,
|
|
934
|
+
scannableFiles,
|
|
935
|
+
sha256,
|
|
936
|
+
shippedAssetPath,
|
|
2757
937
|
sync,
|
|
2758
|
-
tokensCss
|
|
938
|
+
tokensCss,
|
|
939
|
+
unitState,
|
|
940
|
+
waiverHintFor,
|
|
941
|
+
writeFileEnsuringDir,
|
|
942
|
+
writeUnit
|
|
2759
943
|
};
|