basalt-ui 1.32.1 → 1.34.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 +1 -1
- package/CHANGELOG.md +65 -0
- package/MIGRATING.md +218 -25
- package/README.md +3 -0
- package/agent/rules/basalt-state.md +2 -2
- package/agent/rules/basalt-tokens.md +1 -1
- package/agent/skills/basalt-app/SKILL.md +5 -3
- package/configs/oxlint-basalt.json +33 -0
- package/configs/oxlint-plugin.js +89 -67
- package/configs/oxlint-plugin.test.ts +224 -19
- package/configs/oxlint.json +2 -30
- package/dist/agent/coalesce.d.ts.map +1 -1
- package/dist/agent/coalesce.js +1 -2
- package/dist/agent/merge.d.ts.map +1 -1
- package/dist/agent/merge.js +1 -2
- package/dist/agent-chat/relative-time.d.ts +9 -0
- package/dist/agent-chat/relative-time.d.ts.map +1 -1
- package/dist/agent-chat/relative-time.js +1 -2
- package/dist/agent-chat/thread-message.d.ts.map +1 -1
- package/dist/agent-chat/thread-message.js +1 -2
- package/dist/charts/kinds/BandStrip.d.ts.map +1 -1
- package/dist/charts/kinds/BandStrip.js +3 -3
- package/dist/charts/kinds/Bars.d.ts.map +1 -1
- package/dist/charts/kinds/Bars.js +1 -2
- package/dist/charts/kinds/StackedArea.d.ts.map +1 -1
- package/dist/charts/kinds/StackedArea.js +1 -2
- package/dist/charts/primitives/legend-disclosure.d.ts.map +1 -1
- package/dist/cli/audit-allows.d.ts +1 -1
- package/dist/cli/audit-allows.d.ts.map +1 -1
- package/dist/cli/audit-allows.js +4 -4
- package/dist/cli/check-theme.d.ts.map +1 -1
- package/dist/cli/check-theme.js +38 -38
- package/dist/cli/config.d.ts +197 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +20 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +4 -6
- package/dist/cli/guard-hook.d.ts.map +1 -1
- package/dist/cli/guard-hook.js +7 -41
- package/dist/cli/index.d.ts +31 -257
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +64 -37
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +7 -16
- 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.map +1 -1
- package/dist/commands/define-overlays.js +1 -2
- package/dist/common/index.d.ts +0 -1
- package/dist/common/index.d.ts.map +1 -1
- package/dist/common/index.js +0 -2
- package/dist/common/validate.d.ts +2 -2
- package/dist/common/validate.d.ts.map +1 -1
- package/dist/common/validate.js +2 -3
- package/dist/common/vx-families.d.ts +7 -0
- package/dist/common/vx-families.d.ts.map +1 -0
- package/dist/common/vx-families.js +32 -0
- package/dist/common/vx-family.d.ts +11 -0
- package/dist/common/vx-family.d.ts.map +1 -0
- package/dist/common/vx-family.js +12 -0
- package/dist/content/article-card.d.ts.map +1 -1
- package/dist/content/article-card.js +4 -1
- package/dist/controls/enum-filter.d.ts.map +1 -1
- package/dist/controls/enum-filter.js +3 -13
- package/dist/controls/filter-pill.d.ts +8 -0
- package/dist/controls/filter-pill.d.ts.map +1 -1
- package/dist/controls/filter-pill.js +15 -0
- package/dist/controls/multi-select-filter.d.ts.map +1 -1
- package/dist/controls/multi-select-filter.js +3 -13
- package/dist/controls/number-filter.d.ts +3 -0
- package/dist/controls/number-filter.d.ts.map +1 -1
- package/dist/controls/number-filter.js +12 -4
- package/dist/controls/range-filter.d.ts.map +1 -1
- package/dist/controls/range-filter.js +1 -2
- package/dist/data/data-table.d.ts.map +1 -1
- package/dist/data/data-table.js +2 -3
- package/dist/guard/index.d.ts +17 -3
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +62 -23
- package/dist/guard/types.d.ts +3 -3
- package/dist/guard/types.d.ts.map +1 -1
- package/dist/guard/vx-names.d.ts +3 -0
- package/dist/guard/vx-names.d.ts.map +1 -0
- package/dist/guard/vx-names.js +257 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -2
- package/dist/notifications/mount-guard.d.ts.map +1 -1
- package/dist/notifications/mount-guard.js +1 -2
- package/dist/provider/index.d.ts.map +1 -1
- package/dist/provider/index.js +2 -3
- package/dist/provider/lab-theme.d.ts +5 -4
- package/dist/provider/lab-theme.d.ts.map +1 -1
- package/dist/provider/lab-theme.js +1 -3
- package/dist/router-tanstack/search-store.d.ts +18 -0
- package/dist/router-tanstack/search-store.d.ts.map +1 -1
- package/dist/router-tanstack/search-store.js +33 -1
- package/dist/shell/app-mobile-nav.d.ts.map +1 -1
- package/dist/shell/app-mobile-nav.js +2 -1
- package/dist/shell/app-mobile-nav.module.css +12 -0
- package/dist/shell/app-sidebar.d.ts.map +1 -1
- package/dist/shell/app-sidebar.js +32 -36
- package/dist/shell/app-sidebar.module.css +6 -0
- package/dist/shell/index.d.ts +0 -1
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +0 -2
- package/dist/shell/mobile-nav-model.d.ts.map +1 -1
- package/dist/shell/mobile-nav-model.js +3 -4
- package/dist/shell/nav-glyph.d.ts +14 -0
- package/dist/shell/nav-glyph.d.ts.map +1 -0
- package/dist/shell/nav-glyph.js +21 -0
- package/dist/shell/nav-types.d.ts +1 -0
- package/dist/shell/nav-types.d.ts.map +1 -1
- package/dist/shell/page-bar.d.ts.map +1 -1
- package/dist/shell/page-bar.js +1 -2
- package/dist/shell/sidebar-search.d.ts.map +1 -1
- package/dist/shell/sidebar-search.js +1 -3
- package/dist/surfaces.d.ts +3 -3
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +4 -2
- package/dist/tokens/index.d.ts +1 -1
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +24 -2
- package/dist/tokens/size-classes.d.ts +25 -0
- package/dist/tokens/size-classes.d.ts.map +1 -1
- package/dist/tokens/size-classes.js +14 -0
- package/llms.txt +4 -4
- package/package.json +1 -1
- package/src/agent/coalesce.ts +1 -2
- package/src/agent/merge.ts +1 -2
- package/src/agent-chat/relative-time.ts +1 -2
- package/src/agent-chat/thread-message.tsx +2 -3
- package/src/charts/kinds/BandStrip.tsx +7 -3
- package/src/charts/kinds/Bars.tsx +1 -2
- package/src/charts/kinds/StackedArea.tsx +4 -2
- package/src/charts/primitives/legend-disclosure.tsx +1 -0
- package/src/cli/audit-allows.ts +10 -9
- package/src/cli/check-theme.ts +61 -39
- package/src/cli/config.ts +209 -0
- package/src/cli/doctor.ts +5 -7
- package/src/cli/guard-hook.ts +9 -42
- package/src/cli/index.ts +101 -294
- package/src/cli/sync.ts +7 -16
- package/src/commands/define-commands.ts +2 -2
- package/src/commands/define-overlays.ts +1 -2
- package/src/common/index.ts +0 -1
- package/src/common/validate.ts +4 -5
- package/src/common/vx-families.ts +36 -0
- package/src/common/vx-family.ts +22 -0
- package/src/content/article-card.tsx +1 -0
- package/src/controls/enum-filter.tsx +3 -13
- package/src/controls/filter-pill.tsx +20 -0
- package/src/controls/multi-select-filter.tsx +3 -13
- package/src/controls/number-filter.tsx +24 -3
- package/src/controls/range-filter.tsx +1 -2
- package/src/data/data-table.tsx +2 -3
- package/src/guard/index.ts +134 -47
- package/src/guard/types.ts +3 -2
- package/src/guard/vx-names.ts +257 -0
- package/src/index.ts +0 -2
- package/src/notifications/mount-guard.ts +1 -2
- package/src/provider/index.tsx +2 -3
- package/src/provider/lab-theme.ts +28 -21
- package/src/router-tanstack/search-store.ts +73 -1
- package/src/shell/app-mobile-nav.module.css +12 -0
- package/src/shell/app-mobile-nav.tsx +8 -1
- package/src/shell/app-sidebar.module.css +6 -0
- package/src/shell/app-sidebar.tsx +52 -50
- package/src/shell/index.tsx +0 -1
- package/src/shell/mobile-nav-model.ts +3 -5
- package/src/shell/nav-glyph.tsx +38 -0
- package/src/shell/nav-types.ts +1 -0
- package/src/shell/page-bar.tsx +1 -4
- package/src/shell/sidebar-search.tsx +1 -5
- package/src/surfaces.ts +4 -2
- package/src/tokens/index.ts +34 -2
- package/src/tokens/size-classes.ts +27 -0
- package/dist/common/is-dev.d.ts +0 -3
- package/dist/common/is-dev.d.ts.map +0 -1
- package/dist/common/is-dev.js +0 -6
- package/dist/shell/use-breakpoint.d.ts +0 -10
- package/dist/shell/use-breakpoint.d.ts.map +0 -1
- package/dist/shell/use-breakpoint.js +0 -11
- package/src/common/is-dev.ts +0 -4
- package/src/shell/use-breakpoint.ts +0 -37
package/src/guard/types.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Guard types — Mantine-free, dependency-free (zero imports beyond TS types).
|
|
3
3
|
*
|
|
4
|
-
* GuardKind is the closed set of
|
|
4
|
+
* GuardKind is the closed set of 28 violation kinds the theme guard can emit — `unframed-chart`
|
|
5
5
|
* retired at the C2 consolidation wave: its only case (a hand-authored `<ChartLegend items={[...]}>`
|
|
6
6
|
* array literal) is fully subsumed by the oxlint plugin's `basalt/chart-legend-literal`, which also
|
|
7
7
|
* catches the `.map()`-over-a-non-series form the regex never could. `'unframed-chart'` stays a
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* GuardConfig is the per-run configuration that drives checkSource.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
/** The
|
|
14
|
+
/** The 28 theme-guard violation kinds. */
|
|
15
15
|
export type GuardKind =
|
|
16
16
|
| 'raw-hex'
|
|
17
17
|
| 'raw-color-fn'
|
|
@@ -40,6 +40,7 @@ export type GuardKind =
|
|
|
40
40
|
| 'in-body-page-title'
|
|
41
41
|
| 'raw-selection-control'
|
|
42
42
|
| 'raw-media-query'
|
|
43
|
+
| 'unknown-vx-token'
|
|
43
44
|
|
|
44
45
|
/**
|
|
45
46
|
* How hard a finding lands. `error` fails the build; `warn` reports and passes.
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
// GENERATED by scripts/gen-vx-names.ts — do not edit. `vx-names.test.ts` fails on drift.
|
|
2
|
+
|
|
3
|
+
/** Every `--vx-*` name basalt emits: `dist/tokens.css` plus its component-scoped names. */
|
|
4
|
+
export const BASALT_VX_NAMES: readonly string[] = [
|
|
5
|
+
'--vx-accent',
|
|
6
|
+
'--vx-accent-fill',
|
|
7
|
+
'--vx-accent-fill-hover',
|
|
8
|
+
'--vx-accent-hover',
|
|
9
|
+
'--vx-accentFill',
|
|
10
|
+
'--vx-accentFillHover',
|
|
11
|
+
'--vx-accentHover',
|
|
12
|
+
'--vx-area-bottom',
|
|
13
|
+
'--vx-area-top',
|
|
14
|
+
'--vx-axis',
|
|
15
|
+
'--vx-axis-stroke',
|
|
16
|
+
'--vx-axisStroke',
|
|
17
|
+
'--vx-bad',
|
|
18
|
+
'--vx-bad-ref',
|
|
19
|
+
'--vx-bad-solid',
|
|
20
|
+
'--vx-badRef',
|
|
21
|
+
'--vx-badSolid',
|
|
22
|
+
'--vx-crosshair',
|
|
23
|
+
'--vx-divider',
|
|
24
|
+
'--vx-dot-stroke',
|
|
25
|
+
'--vx-dotStroke',
|
|
26
|
+
'--vx-faint',
|
|
27
|
+
'--vx-fill-cyan',
|
|
28
|
+
'--vx-fill-grape',
|
|
29
|
+
'--vx-fill-gray',
|
|
30
|
+
'--vx-fill-green',
|
|
31
|
+
'--vx-fill-hover-cyan',
|
|
32
|
+
'--vx-fill-hover-grape',
|
|
33
|
+
'--vx-fill-hover-gray',
|
|
34
|
+
'--vx-fill-hover-green',
|
|
35
|
+
'--vx-fill-hover-indigo',
|
|
36
|
+
'--vx-fill-hover-lime',
|
|
37
|
+
'--vx-fill-hover-orange',
|
|
38
|
+
'--vx-fill-hover-pink',
|
|
39
|
+
'--vx-fill-hover-red',
|
|
40
|
+
'--vx-fill-hover-teal',
|
|
41
|
+
'--vx-fill-hover-violet',
|
|
42
|
+
'--vx-fill-hover-yellow',
|
|
43
|
+
'--vx-fill-indigo',
|
|
44
|
+
'--vx-fill-lime',
|
|
45
|
+
'--vx-fill-orange',
|
|
46
|
+
'--vx-fill-pink',
|
|
47
|
+
'--vx-fill-red',
|
|
48
|
+
'--vx-fill-teal',
|
|
49
|
+
'--vx-fill-violet',
|
|
50
|
+
'--vx-fill-yellow',
|
|
51
|
+
'--vx-fillHover-cyan',
|
|
52
|
+
'--vx-fillHover-grape',
|
|
53
|
+
'--vx-fillHover-gray',
|
|
54
|
+
'--vx-fillHover-green',
|
|
55
|
+
'--vx-fillHover-indigo',
|
|
56
|
+
'--vx-fillHover-lime',
|
|
57
|
+
'--vx-fillHover-orange',
|
|
58
|
+
'--vx-fillHover-pink',
|
|
59
|
+
'--vx-fillHover-red',
|
|
60
|
+
'--vx-fillHover-teal',
|
|
61
|
+
'--vx-fillHover-violet',
|
|
62
|
+
'--vx-fillHover-yellow',
|
|
63
|
+
'--vx-good',
|
|
64
|
+
'--vx-good-ref',
|
|
65
|
+
'--vx-good-soft',
|
|
66
|
+
'--vx-good-solid',
|
|
67
|
+
'--vx-goodRef',
|
|
68
|
+
'--vx-goodSoft',
|
|
69
|
+
'--vx-goodSolid',
|
|
70
|
+
'--vx-grid',
|
|
71
|
+
'--vx-hit',
|
|
72
|
+
'--vx-hit-gap',
|
|
73
|
+
'--vx-ink',
|
|
74
|
+
'--vx-ink2',
|
|
75
|
+
'--vx-keyboard-inset',
|
|
76
|
+
'--vx-legend-text',
|
|
77
|
+
'--vx-legendText',
|
|
78
|
+
'--vx-line',
|
|
79
|
+
'--vx-line2',
|
|
80
|
+
'--vx-muted',
|
|
81
|
+
'--vx-neutral',
|
|
82
|
+
'--vx-on-accent',
|
|
83
|
+
'--vx-onAccent',
|
|
84
|
+
'--vx-prose-measure',
|
|
85
|
+
'--vx-radius-card',
|
|
86
|
+
'--vx-radius-ctrl',
|
|
87
|
+
'--vx-radius-fine',
|
|
88
|
+
'--vx-radius-floating',
|
|
89
|
+
'--vx-radius-pill',
|
|
90
|
+
'--vx-radius-tight',
|
|
91
|
+
'--vx-shadow-card',
|
|
92
|
+
'--vx-shadow-ctrl',
|
|
93
|
+
'--vx-shadow-overlay',
|
|
94
|
+
'--vx-shadow-raised',
|
|
95
|
+
'--vx-space-agent-code-inset',
|
|
96
|
+
'--vx-space-agent-error-inset-x',
|
|
97
|
+
'--vx-space-agent-error-inset-y',
|
|
98
|
+
'--vx-space-agent-message-inset-x',
|
|
99
|
+
'--vx-space-agent-message-inset-y',
|
|
100
|
+
'--vx-space-agent-part-gap-top',
|
|
101
|
+
'--vx-space-agent-rail-inset-x',
|
|
102
|
+
'--vx-space-agent-scroll-button-gap-top',
|
|
103
|
+
'--vx-space-agent-transcript-inset',
|
|
104
|
+
'--vx-space-app-shell-header-lead-min',
|
|
105
|
+
'--vx-space-app-shell-inset',
|
|
106
|
+
'--vx-space-app-shell-inset-mobile',
|
|
107
|
+
'--vx-space-article-card-meta-gap-top',
|
|
108
|
+
'--vx-space-article-card-tag-inset-x',
|
|
109
|
+
'--vx-space-article-card-tag-inset-y',
|
|
110
|
+
'--vx-space-article-card-tags-gap',
|
|
111
|
+
'--vx-space-article-column-gap',
|
|
112
|
+
'--vx-space-article-footer-gap',
|
|
113
|
+
'--vx-space-article-footer-margin-top',
|
|
114
|
+
'--vx-space-article-footer-padding-top',
|
|
115
|
+
'--vx-space-article-header-gap',
|
|
116
|
+
'--vx-space-article-header-margin-bottom',
|
|
117
|
+
'--vx-space-article-header-padding-bottom',
|
|
118
|
+
'--vx-space-article-meta-row-gap',
|
|
119
|
+
'--vx-space-article-nav-target-gap',
|
|
120
|
+
'--vx-space-article-toc-rail-width',
|
|
121
|
+
'--vx-space-badge-inset-x',
|
|
122
|
+
'--vx-space-badge-inset-y',
|
|
123
|
+
'--vx-space-callout-inset-x',
|
|
124
|
+
'--vx-space-callout-inset-y',
|
|
125
|
+
'--vx-space-callout-title-row-gap',
|
|
126
|
+
'--vx-space-card-inset-x',
|
|
127
|
+
'--vx-space-card-inset-y',
|
|
128
|
+
'--vx-space-code-block-body-inset-y',
|
|
129
|
+
'--vx-space-code-block-content-inset-x',
|
|
130
|
+
'--vx-space-code-block-floating-copy-offset',
|
|
131
|
+
'--vx-space-code-block-header-gap',
|
|
132
|
+
'--vx-space-code-block-header-inset-right',
|
|
133
|
+
'--vx-space-code-block-header-inset-y',
|
|
134
|
+
'--vx-space-code-block-header-right-gap',
|
|
135
|
+
'--vx-space-control-gap',
|
|
136
|
+
'--vx-space-control-height',
|
|
137
|
+
'--vx-space-control-height-ctl',
|
|
138
|
+
'--vx-space-control-height-tag',
|
|
139
|
+
'--vx-space-control-height-widget',
|
|
140
|
+
'--vx-space-guide-body-gap-bottom',
|
|
141
|
+
'--vx-space-guide-footer-gap-top',
|
|
142
|
+
'--vx-space-guide-footer-inset-top',
|
|
143
|
+
'--vx-space-icon-size',
|
|
144
|
+
'--vx-space-input-height',
|
|
145
|
+
'--vx-space-mermaid-container-inset',
|
|
146
|
+
'--vx-space-mobile-nav-icon-size',
|
|
147
|
+
'--vx-space-mobile-nav-row-height',
|
|
148
|
+
'--vx-space-mobile-nav-tab-gap',
|
|
149
|
+
'--vx-space-mobile-nav-tab-inset-x',
|
|
150
|
+
'--vx-space-mobile-nav-tab-inset-y',
|
|
151
|
+
'--vx-space-nav-icon-gap',
|
|
152
|
+
'--vx-space-page-bar-row-height',
|
|
153
|
+
'--vx-space-prose-article-block-gap',
|
|
154
|
+
'--vx-space-prose-article-h1-gap-bottom',
|
|
155
|
+
'--vx-space-prose-article-h1-gap-top',
|
|
156
|
+
'--vx-space-prose-article-h2-rule-gap',
|
|
157
|
+
'--vx-space-prose-article-heading-gap-bottom',
|
|
158
|
+
'--vx-space-prose-article-heading-gap-top',
|
|
159
|
+
'--vx-space-prose-article-list-gap-bottom',
|
|
160
|
+
'--vx-space-prose-article-list-gap-top',
|
|
161
|
+
'--vx-space-prose-article-list-indent',
|
|
162
|
+
'--vx-space-prose-article-paragraph-gap',
|
|
163
|
+
'--vx-space-prose-chat-heading-gap-bottom',
|
|
164
|
+
'--vx-space-prose-chat-heading-gap-top',
|
|
165
|
+
'--vx-space-prose-chat-list-gap-bottom',
|
|
166
|
+
'--vx-space-prose-chat-list-indent',
|
|
167
|
+
'--vx-space-prose-chat-list-item-gap',
|
|
168
|
+
'--vx-space-prose-chat-nested-list-gap',
|
|
169
|
+
'--vx-space-prose-code-block-inset-x',
|
|
170
|
+
'--vx-space-prose-code-block-inset-y',
|
|
171
|
+
'--vx-space-prose-heading-anchor-gap',
|
|
172
|
+
'--vx-space-prose-inline-code-inset-x',
|
|
173
|
+
'--vx-space-prose-inline-code-inset-y',
|
|
174
|
+
'--vx-space-prose-quote-indent',
|
|
175
|
+
'--vx-space-prose-quote-inset-y',
|
|
176
|
+
'--vx-space-prose-table-cell-inset-x',
|
|
177
|
+
'--vx-space-prose-table-cell-inset-y',
|
|
178
|
+
'--vx-space-row-inset-x',
|
|
179
|
+
'--vx-space-row-inset-y',
|
|
180
|
+
'--vx-space-row-line-height',
|
|
181
|
+
'--vx-space-section-gap',
|
|
182
|
+
'--vx-space-section-header-height',
|
|
183
|
+
'--vx-space-settings-row-gap',
|
|
184
|
+
'--vx-space-settings-row-inset-y',
|
|
185
|
+
'--vx-space-sheet-row-height',
|
|
186
|
+
'--vx-space-sidebar-account-inset-bottom',
|
|
187
|
+
'--vx-space-sidebar-account-inset-top',
|
|
188
|
+
'--vx-space-sidebar-account-inset-x',
|
|
189
|
+
'--vx-space-sidebar-avatar-size',
|
|
190
|
+
'--vx-space-sidebar-block-row-height',
|
|
191
|
+
'--vx-space-sidebar-brand-inset-x',
|
|
192
|
+
'--vx-space-sidebar-child-list-gap-bottom',
|
|
193
|
+
'--vx-space-sidebar-child-list-gap-top',
|
|
194
|
+
'--vx-space-sidebar-child-list-indent',
|
|
195
|
+
'--vx-space-sidebar-child-row-indent',
|
|
196
|
+
'--vx-space-sidebar-child-row-inset-y',
|
|
197
|
+
'--vx-space-sidebar-region-gap',
|
|
198
|
+
'--vx-space-sidebar-search-gap',
|
|
199
|
+
'--vx-space-sidebar-search-trigger-height',
|
|
200
|
+
'--vx-space-sidebar-section-gap',
|
|
201
|
+
'--vx-space-sidebar-section-label-gap',
|
|
202
|
+
'--vx-space-stack-lg',
|
|
203
|
+
'--vx-space-stack-md',
|
|
204
|
+
'--vx-space-stack-sm',
|
|
205
|
+
'--vx-space-stack-xl',
|
|
206
|
+
'--vx-space-stack-xs',
|
|
207
|
+
'--vx-space-stat-card-gap',
|
|
208
|
+
'--vx-space-sticky-header-clearance',
|
|
209
|
+
'--vx-space-toc-link-indent',
|
|
210
|
+
'--vx-space-toc-link-inset-y',
|
|
211
|
+
'--vx-space-toc-root-gap',
|
|
212
|
+
'--vx-space-toc-sub-indent',
|
|
213
|
+
'--vx-space-virtual-row-inset-x',
|
|
214
|
+
'--vx-space-virtual-row-inset-y',
|
|
215
|
+
'--vx-space-widget-header-height',
|
|
216
|
+
'--vx-status-bad',
|
|
217
|
+
'--vx-status-excellent',
|
|
218
|
+
'--vx-status-good',
|
|
219
|
+
'--vx-status-neutral',
|
|
220
|
+
'--vx-status-warn',
|
|
221
|
+
'--vx-surface-bg',
|
|
222
|
+
'--vx-surface-border',
|
|
223
|
+
'--vx-surface-elevated',
|
|
224
|
+
'--vx-surface-field',
|
|
225
|
+
'--vx-surface-hairline',
|
|
226
|
+
'--vx-surface-overlay',
|
|
227
|
+
'--vx-surface-panel',
|
|
228
|
+
'--vx-surface-panel-hover',
|
|
229
|
+
'--vx-surface-panelHover',
|
|
230
|
+
'--vx-surface-subtle',
|
|
231
|
+
'--vx-text-display',
|
|
232
|
+
'--vx-text-h1',
|
|
233
|
+
'--vx-text-h2',
|
|
234
|
+
'--vx-text-kpi',
|
|
235
|
+
'--vx-text-lg',
|
|
236
|
+
'--vx-text-md',
|
|
237
|
+
'--vx-text-micro',
|
|
238
|
+
'--vx-text-nano',
|
|
239
|
+
'--vx-text-sm',
|
|
240
|
+
'--vx-text-xl',
|
|
241
|
+
'--vx-text-xs',
|
|
242
|
+
'--vx-tooltip-bg',
|
|
243
|
+
'--vx-tooltip-border',
|
|
244
|
+
'--vx-tooltip-muted',
|
|
245
|
+
'--vx-tooltip-shadow',
|
|
246
|
+
'--vx-tooltip-text',
|
|
247
|
+
'--vx-tooltipBg',
|
|
248
|
+
'--vx-tooltipBorder',
|
|
249
|
+
'--vx-tooltipMuted',
|
|
250
|
+
'--vx-tooltipShadow',
|
|
251
|
+
'--vx-tooltipText',
|
|
252
|
+
'--vx-warn',
|
|
253
|
+
'--vx-warn-ref',
|
|
254
|
+
'--vx-warn-solid',
|
|
255
|
+
'--vx-warnRef',
|
|
256
|
+
'--vx-warnSolid',
|
|
257
|
+
]
|
package/src/index.ts
CHANGED
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
* as `provider/lab-theme.ts`.
|
|
13
13
|
*/
|
|
14
14
|
import { useEffect } from 'react'
|
|
15
|
-
import { isDev } from '../common/is-dev'
|
|
16
15
|
|
|
17
16
|
let mountedNotificationsCount = 0
|
|
18
17
|
|
|
@@ -25,7 +24,7 @@ let mountedNotificationsCount = 0
|
|
|
25
24
|
export function useNotificationsMountGuard(): void {
|
|
26
25
|
useEffect(() => {
|
|
27
26
|
mountedNotificationsCount++
|
|
28
|
-
if (mountedNotificationsCount > 1 &&
|
|
27
|
+
if (mountedNotificationsCount > 1 && process.env.NODE_ENV !== 'production') {
|
|
29
28
|
console.warn(
|
|
30
29
|
'[basalt] more than one Notifications overlay is mounted at once — mount exactly ONE ' +
|
|
31
30
|
"<BasaltOverlays notifications /> (they all render Mantine's <Notifications />, so " +
|
package/src/provider/index.tsx
CHANGED
|
@@ -34,7 +34,6 @@ import { buildDensityCss, buildFontsCss, buildPaletteCss, buildRadiusCss } from
|
|
|
34
34
|
import type { BuildPaletteOpts } from '../tokens'
|
|
35
35
|
import { isDefaultDeriveConfig } from '../tokens/derive'
|
|
36
36
|
import { buildPaletteData } from '../tokens/palette'
|
|
37
|
-
import { isDev } from '../common/is-dev'
|
|
38
37
|
import { useMediaQuery } from '../common/use-media-query'
|
|
39
38
|
import { registerColorSchemeSetter } from '../commands/shell-bridge'
|
|
40
39
|
import { useIsomorphicLayoutEffect } from '../common/isomorphic-layout-effect'
|
|
@@ -125,7 +124,7 @@ export type BasaltProviderProps = {
|
|
|
125
124
|
// ── Default error handler ─────────────────────────────────────────────────────────────────────────
|
|
126
125
|
|
|
127
126
|
function defaultOnError(error: unknown, ctx: BasaltErrorContext): void {
|
|
128
|
-
if (
|
|
127
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
129
128
|
// eslint-disable-next-line no-console -- intentional dev-only diagnostic when no onError is supplied
|
|
130
129
|
console.error('[BasaltProvider] unhandled error', ctx.kind, error)
|
|
131
130
|
}
|
|
@@ -382,7 +381,7 @@ const providerMounts = createMountCounter()
|
|
|
382
381
|
|
|
383
382
|
function useDuplicateProviderGuard(): void {
|
|
384
383
|
useEffect(() => {
|
|
385
|
-
if (providerMounts.mount() > 1 &&
|
|
384
|
+
if (providerMounts.mount() > 1 && process.env.NODE_ENV !== 'production') {
|
|
386
385
|
console.warn(
|
|
387
386
|
'[basalt] BasaltProvider: more than one instance is mounted at once — nested/duplicate ' +
|
|
388
387
|
'BasaltProviders double-mount MantineProvider, the palette <style> and ' +
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
* The delta also carries `other.basaltDerive`/`basaltRadius`/`basaltDensity`, so `BasaltBridge`'s
|
|
34
34
|
* existing injection emits the matching `--vx-*` CSS off the running theme — one path, both halves.
|
|
35
35
|
*
|
|
36
|
-
* DEV-tool path only, twice over: it is compiled out of a production build entirely (see
|
|
37
|
-
*
|
|
38
|
-
* never mounts the panel, since the persisted key is written by nothing else —
|
|
36
|
+
* DEV-tool path only, twice over: it is compiled out of a production build entirely (see the
|
|
37
|
+
* DEV-build gate on `useLabTheme`), and even in a dev build, with the "Apply" switch off — or in
|
|
38
|
+
* any app that never mounts the panel, since the persisted key is written by nothing else —
|
|
39
39
|
* {@link applyLabOverride} returns the consumer theme verbatim.
|
|
40
40
|
*/
|
|
41
41
|
import { mergeThemeOverrides } from '@mantine/core'
|
|
@@ -44,7 +44,6 @@ import { useMemo } from 'react'
|
|
|
44
44
|
import { useDeriveControlsState, toDeriveOverride } from '../theme-lab/derive-state'
|
|
45
45
|
import type { DeriveOverride } from '../theme-lab/derive-state'
|
|
46
46
|
import { baseTheme, createBasaltTheme } from '../theme'
|
|
47
|
-
import { isDev } from '../common/is-dev'
|
|
48
47
|
|
|
49
48
|
type Plain = Record<string, unknown>
|
|
50
49
|
|
|
@@ -124,20 +123,27 @@ export function applyLabOverride(
|
|
|
124
123
|
return mergeThemeOverrides(theme, themeOverrideDelta(baseTheme, labTheme))
|
|
125
124
|
}
|
|
126
125
|
|
|
127
|
-
|
|
128
|
-
* DEV-build gate. `BasaltProvider` is the mandatory `.` entry, so an
|
|
129
|
-
* the lab store would make EVERY production app pay a localStorage
|
|
130
|
-
* render and keep a `window` 'storage' listener registered for
|
|
131
|
-
* question ("is the dev lab active?") that is always "no" in an
|
|
132
|
-
*
|
|
133
|
-
*
|
|
126
|
+
/*
|
|
127
|
+
* DEV-build gate (on `useLabTheme` below). `BasaltProvider` is the mandatory `.` entry, so an
|
|
128
|
+
* unconditional subscription to the lab store would make EVERY production app pay a localStorage
|
|
129
|
+
* read + schema validation on first render and keep a `window` 'storage' listener registered for
|
|
130
|
+
* its whole lifetime — to answer a question ("is the dev lab active?") that is always "no" in an
|
|
131
|
+
* app that never mounts `<DeriveControls>`.
|
|
132
|
+
*
|
|
133
|
+
* Resolved ONCE at module scope, so a bundler can drop `useLabThemeDev` (and with it the
|
|
134
|
+
* `theme-lab/derive-state` + `state/persisted` imports) from a production build — but only
|
|
135
|
+
* because the gate is a literal, dotted `process.env.NODE_ENV` read inside the ternary itself.
|
|
136
|
+
* Measured (minified prod builds, dev branch behind an import): the literal read folds in
|
|
137
|
+
* Vite/Rolldown, esbuild, Bun and webpack + terser alike, in both the dotted and the bracketed
|
|
138
|
+
* form; dotted is kept as the form every `define` implementation matches. A cross-module
|
|
139
|
+
* `isDev()` call and a module-level `const` both LEAK the dev branch in esbuild and Bun (and
|
|
140
|
+
* `isDev()` in Rolldown too), which is how 1.32.1 shipped the whole lab store in every production
|
|
141
|
+
* app's first-paint chunk. `check-budgets.ts`'s provider-only budget pins it.
|
|
134
142
|
*
|
|
135
|
-
* The cost: the lab does nothing in a PRODUCTION BUILD of a playground/dev app. It is a dev tool
|
|
136
|
-
* through a dev server (`bun run dev`), so that is the right side of the trade — but if you
|
|
137
|
-
* preview the playground via `vite build && vite preview`, the sliders will be inert there.
|
|
143
|
+
* The cost: the lab does nothing in a PRODUCTION BUILD of a playground/dev app. It is a dev tool
|
|
144
|
+
* run through a dev server (`bun run dev`), so that is the right side of the trade — but if you
|
|
145
|
+
* ever preview the playground via `vite build && vite preview`, the sliders will be inert there.
|
|
138
146
|
*/
|
|
139
|
-
const LAB_ENABLED = isDev()
|
|
140
|
-
|
|
141
147
|
/**
|
|
142
148
|
* Dev build: subscribed to the lab store.
|
|
143
149
|
*
|
|
@@ -159,9 +165,10 @@ function useLabThemeProd(overrides: MantineThemeOverride | undefined): MantineTh
|
|
|
159
165
|
}
|
|
160
166
|
|
|
161
167
|
/**
|
|
162
|
-
* `BasaltProvider`'s theme resolution. Picked at module scope, not per render — both
|
|
163
|
-
* call their hooks unconditionally at their own top level, and which one is bound
|
|
164
|
-
* the app's lifetime, so the hook order is as stable as a plain function's (a
|
|
165
|
-
* inline in one function body would be a conditional hook call and a
|
|
168
|
+
* `BasaltProvider`'s theme resolution. Picked at module scope, not per render — both
|
|
169
|
+
* implementations call their hooks unconditionally at their own top level, and which one is bound
|
|
170
|
+
* never changes for the app's lifetime, so the hook order is as stable as a plain function's (a
|
|
171
|
+
* `dev ? use…()` inline in one function body would be a conditional hook call and a
|
|
172
|
+
* `react/rules-of-hooks` error).
|
|
166
173
|
*/
|
|
167
|
-
export const useLabTheme =
|
|
174
|
+
export const useLabTheme = process.env.NODE_ENV !== 'production' ? useLabThemeDev : useLabThemeProd
|
|
@@ -43,6 +43,24 @@ export type CreateSearchStoreOptions<S extends Record<string, AnyField>> = {
|
|
|
43
43
|
fields: S
|
|
44
44
|
/** Envelope version — bump when the field set changes to discard stale localStorage. */
|
|
45
45
|
version?: number
|
|
46
|
+
/**
|
|
47
|
+
* Search params this store does NOT own that a URL-lane write drops, in the SAME navigate —
|
|
48
|
+
* `['page']` on a paginated list, so narrowing the set on page 5 lands on page 1 and Back restores
|
|
49
|
+
* both together. Dropped, not written: the route's own `validateSearch` resolves the default
|
|
50
|
+
* (`page` → 1), so the store never has to know it. Applies to a URL-lane field's setter and
|
|
51
|
+
* `clear()` when the value moves, and to `useReset()` when any field moves; an explicit `patch`
|
|
52
|
+
* naming the same key wins.
|
|
53
|
+
*
|
|
54
|
+
* **URL-lane writes only.** A `url: false` field (mirror-only or memory) makes no navigate, so it
|
|
55
|
+
* has none to ride — and a `useReset()` on a store with no URL-lane field keeps the page too.
|
|
56
|
+
*
|
|
57
|
+
* Store-wide by design: every filter of a list narrows the same list. A param the store owns is
|
|
58
|
+
* refused at definition — reset a field with `useReset()` or its own `clear()`.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* createSearchStore({ key: 'shares', fields: { q: field.string({}), sort: … }, resets: ['page'] })
|
|
62
|
+
*/
|
|
63
|
+
resets?: readonly string[]
|
|
46
64
|
}
|
|
47
65
|
|
|
48
66
|
export type SearchStore<S extends Record<string, AnyField>> = {
|
|
@@ -205,6 +223,28 @@ function assertNoLazyUrlFallback(key: string, fields: Record<string, AnyField>):
|
|
|
205
223
|
}
|
|
206
224
|
}
|
|
207
225
|
|
|
226
|
+
/**
|
|
227
|
+
* `resets` exists for keys the store does NOT own; one it owns would be dropped from the URL while
|
|
228
|
+
* the mirror kept its value — the same URL/mirror split `patch` refuses. Static, so thrown at
|
|
229
|
+
* definition.
|
|
230
|
+
*/
|
|
231
|
+
function assertResetsNotOwned(input: {
|
|
232
|
+
key: string
|
|
233
|
+
resets: readonly string[]
|
|
234
|
+
paramOwners: ReadonlyMap<string, string>
|
|
235
|
+
}): void {
|
|
236
|
+
const { key, resets, paramOwners } = input
|
|
237
|
+
for (const param of resets) {
|
|
238
|
+
const owner = paramOwners.get(param)
|
|
239
|
+
if (owner === undefined) continue
|
|
240
|
+
throw new Error(
|
|
241
|
+
`basalt-ui: createSearchStore('${key}'): \`resets\` names \`${param}\`, which this store's ` +
|
|
242
|
+
`field '${owner}' owns. \`resets\` is for sibling params the store does not own (a ` +
|
|
243
|
+
'`page`); a field of the store is reset through `useReset()` or its own `clear()`.',
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
208
248
|
/** The memory-only lane: neither the URL nor the mirror, so the value lives in the store's own
|
|
209
249
|
* session-scoped external store (`StoreCore.memoryUse`) — the lane `createLocalStore` also uses. */
|
|
210
250
|
function isMemoryLane(entry: StoreEntry): boolean {
|
|
@@ -240,6 +280,25 @@ export function buildSearchStore<const S extends Record<string, AnyField>>(
|
|
|
240
280
|
for (const entry of core.urlEntries) {
|
|
241
281
|
for (const param of entry.codec.params) paramOwners.set(param, entry.name)
|
|
242
282
|
}
|
|
283
|
+
const resets = o.resets ?? []
|
|
284
|
+
assertResetsNotOwned({ key: o.key, resets, paramOwners })
|
|
285
|
+
/** Spread right after `prev` in every store navigate, so a `patch` or the field itself wins. */
|
|
286
|
+
const resetPatch: Record<string, undefined> = Object.fromEntries(
|
|
287
|
+
resets.map((param) => [param, undefined]),
|
|
288
|
+
)
|
|
289
|
+
/**
|
|
290
|
+
* `resetPatch` when `next` moves the field off what the URL holds NOW — read from the navigate's
|
|
291
|
+
* own `prev`, never the render-captured value, which a second `set()` in the same tick would
|
|
292
|
+
* misjudge against a value the first one already replaced.
|
|
293
|
+
*/
|
|
294
|
+
const resetsFor = (
|
|
295
|
+
entry: StoreEntry,
|
|
296
|
+
prev: Record<string, unknown>,
|
|
297
|
+
next: unknown,
|
|
298
|
+
): Record<string, undefined> | undefined => {
|
|
299
|
+
const current = core.resolve(entry, prev, core.readRecord())
|
|
300
|
+
return entry.codec.equals(next, current) ? undefined : resetPatch
|
|
301
|
+
}
|
|
243
302
|
const persistedNames = new Set(
|
|
244
303
|
core.entries.filter((entry) => entry.codec.lane.persist).map((entry) => entry.name),
|
|
245
304
|
)
|
|
@@ -326,8 +385,10 @@ export function buildSearchStore<const S extends Record<string, AnyField>>(
|
|
|
326
385
|
if (!validated) return
|
|
327
386
|
navigate({
|
|
328
387
|
to: '.',
|
|
388
|
+
// Gated like the setter: clearing a field already at its fallback keeps the page.
|
|
329
389
|
search: (prev: Record<string, unknown>) => ({
|
|
330
390
|
...prev,
|
|
391
|
+
...resetsFor(entry, prev, entry.codec.fallback),
|
|
331
392
|
...entry.codec.toSearch(entry.codec.fallback),
|
|
332
393
|
}),
|
|
333
394
|
replace: true,
|
|
@@ -356,8 +417,11 @@ export function buildSearchStore<const S extends Record<string, AnyField>>(
|
|
|
356
417
|
// `patch` FIRST, the field's own params last: a patch is for keys the store does not
|
|
357
418
|
// own (clearing a sibling `detailDate` with `undefined`), so it must never be able to
|
|
358
419
|
// overwrite the value this very call is setting.
|
|
420
|
+
// `resets` only when the value moves: a no-op write (a debounced box settling back on
|
|
421
|
+
// what it held) must not throw the reader off the page they are on.
|
|
359
422
|
search: (prev: Record<string, unknown>) => ({
|
|
360
423
|
...prev,
|
|
424
|
+
...resetsFor(entry, prev, next),
|
|
361
425
|
...opts?.patch,
|
|
362
426
|
...entry.codec.toSearch(next),
|
|
363
427
|
}),
|
|
@@ -464,7 +528,15 @@ export function buildSearchStore<const S extends Record<string, AnyField>>(
|
|
|
464
528
|
}
|
|
465
529
|
navigate({
|
|
466
530
|
to: '.',
|
|
467
|
-
|
|
531
|
+
// `resets` only when some field actually moves — the same gate as the setter and
|
|
532
|
+
// `clear()`. A `Reset all` with everything already at its fallback must keep the page:
|
|
533
|
+
// this navigate REPLACES, so a page dropped here could not be got back with Back.
|
|
534
|
+
search: (prev: Record<string, unknown>) => {
|
|
535
|
+
const moved = validatedEntries.some(
|
|
536
|
+
(entry) => resetsFor(entry, prev, entry.codec.fallback) !== undefined,
|
|
537
|
+
)
|
|
538
|
+
return { ...prev, ...(moved ? resetPatch : undefined), ...patch }
|
|
539
|
+
},
|
|
468
540
|
replace: true,
|
|
469
541
|
// Same reason as a single field's write above — a `Reset all` pressed from the mobile
|
|
470
542
|
// sheet must not also scroll the page it was pressed on.
|
|
@@ -69,6 +69,18 @@
|
|
|
69
69
|
height: var(--vx-space-mobile-nav-icon-size);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
/* The first-letter fallback for an icon-less slot (`nav-glyph.tsx`) sits in the same icon-size box
|
|
73
|
+
* an svg gets, so the pill's height does not depend on the glyph's line box. */
|
|
74
|
+
.tabIcon > [data-nav-glyph] {
|
|
75
|
+
display: inline-flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
justify-content: center;
|
|
78
|
+
width: var(--vx-space-mobile-nav-icon-size);
|
|
79
|
+
height: var(--vx-space-mobile-nav-icon-size);
|
|
80
|
+
line-height: 1;
|
|
81
|
+
font-weight: 600;
|
|
82
|
+
}
|
|
83
|
+
|
|
72
84
|
.tab:active .tabIcon {
|
|
73
85
|
transform: scale(0.9);
|
|
74
86
|
}
|
|
@@ -39,6 +39,7 @@ import type {
|
|
|
39
39
|
import type { BasaltAccountProps } from './account-types'
|
|
40
40
|
import type { SettingsMenuItem } from './index'
|
|
41
41
|
import { NavCountBadge } from './nav-count-badge'
|
|
42
|
+
import { hasIcon, initialGlyph } from './nav-glyph'
|
|
42
43
|
import { SidebarBlockToneDot } from './sidebar-blocks'
|
|
43
44
|
import { sidebarBlockMobile } from './sidebar-block-model'
|
|
44
45
|
import { useBasaltSpacing } from '../theme'
|
|
@@ -456,7 +457,13 @@ export function MobileNav({
|
|
|
456
457
|
const tabInner = (slot: MobileNavSlot) => (
|
|
457
458
|
<>
|
|
458
459
|
<span className={classes.tabIcon}>
|
|
459
|
-
{slot.icon
|
|
460
|
+
{hasIcon(slot.icon) ? (
|
|
461
|
+
slot.icon
|
|
462
|
+
) : slot.kind !== 'link' && slot.isMore ? (
|
|
463
|
+
<IconMore />
|
|
464
|
+
) : (
|
|
465
|
+
initialGlyph(slot.short)
|
|
466
|
+
)}
|
|
460
467
|
{hasCount(slot) ? <span className={classes.tabDot} aria-hidden /> : null}
|
|
461
468
|
</span>
|
|
462
469
|
<Text className={classes.label}>{slot.short}</Text>
|
|
@@ -47,6 +47,12 @@
|
|
|
47
47
|
margin-inline-end: var(--vx-space-nav-icon-gap);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/* The first-letter fallback for an icon-less row (`nav-glyph.tsx`) — a glyph, not a text run. */
|
|
51
|
+
.link :global(.mantine-NavLink-section[data-position='left']) > [data-nav-glyph] {
|
|
52
|
+
line-height: 1;
|
|
53
|
+
font-weight: 600;
|
|
54
|
+
}
|
|
55
|
+
|
|
50
56
|
/* Inactive rows: muted text + faint icon. Active rows keep the theme's ink text (via `--nl-color`)
|
|
51
57
|
* untouched — this selector only ever matches when `data-active` is absent. `aria-current` is no
|
|
52
58
|
* longer part of the exclusion: `use-nav.ts` is the sole authority on active, stamping exactly one
|