basalt-ui 1.19.1 → 1.21.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 +19 -1
- package/CHANGELOG.md +39 -0
- package/MIGRATING.md +326 -0
- package/README.md +87 -9
- package/agent/rules/basalt-app.md +84 -0
- package/agent/rules/basalt-charts.md +32 -17
- package/agent/rules/basalt-content.md +4 -0
- package/agent/rules/basalt-mantine.md +25 -10
- package/agent/rules/basalt-router.md +79 -15
- package/agent/rules/basalt-state.md +21 -3
- package/agent/rules/basalt-tokens.md +132 -6
- package/agent/skills/basalt-app/SKILL.md +45 -6
- package/agent/skills/basalt-charts/SKILL.md +5 -3
- package/agent/templates/CLAUDE-block.md.tpl +11 -5
- package/configs/check.yml +11 -4
- package/configs/lefthook.yml +59 -7
- package/configs/oxlint-plugin.js +633 -30
- package/configs/oxlint-plugin.test.ts +594 -15
- package/configs/oxlint.json +10 -1
- package/dist/agent/stick-to-bottom.js +1 -1
- package/dist/agent-chat/thread-detail-panel.js +1 -1
- package/dist/agent-chat/thread-message.js +2 -2
- package/dist/agent-chat/tool-chip.js +1 -1
- package/dist/charts/kinds/Bars.d.ts +1 -1
- package/dist/charts/kinds/Bars.d.ts.map +1 -1
- package/dist/charts/kinds/Donut.d.ts +1 -1
- package/dist/charts/kinds/Donut.d.ts.map +1 -1
- package/dist/charts/kinds/Donut.js +1 -1
- package/dist/charts/kinds/DualPanel.d.ts +1 -1
- package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
- package/dist/charts/kinds/Heatmap.d.ts +1 -1
- package/dist/charts/kinds/Heatmap.d.ts.map +1 -1
- package/dist/charts/kinds/MultiLine.d.ts +1 -1
- package/dist/charts/kinds/MultiLine.d.ts.map +1 -1
- package/dist/charts/kinds/StackedArea.d.ts +1 -1
- package/dist/charts/kinds/StackedArea.d.ts.map +1 -1
- package/dist/charts/kinds/ZonedLine.d.ts +1 -1
- package/dist/charts/kinds/ZonedLine.d.ts.map +1 -1
- package/dist/charts/primitives/ChartLegend.js +2 -2
- package/dist/charts/primitives/ChartTooltip.js +1 -1
- package/dist/cli/index.d.ts +146 -20
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +901 -101
- package/dist/cli/toolchain-wiring.test.d.ts +2 -0
- package/dist/cli/toolchain-wiring.test.d.ts.map +1 -0
- package/dist/cli/waiver-audit.test.d.ts +2 -0
- package/dist/cli/waiver-audit.test.d.ts.map +1 -0
- package/dist/dashboard/delta-badge.js +2 -2
- package/dist/dashboard/stat-card.js +1 -1
- package/dist/data/virtual-list.js +5 -5
- package/dist/guard/index.d.ts +101 -2
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +474 -62
- package/dist/guard/types.d.ts +25 -5
- package/dist/guard/types.d.ts.map +1 -1
- package/dist/register.d.ts +25 -0
- package/dist/register.d.ts.map +1 -1
- package/dist/router-tanstack/multi-search-param-store.d.ts +13 -0
- package/dist/router-tanstack/multi-search-param-store.d.ts.map +1 -1
- package/dist/router-tanstack/multi-search-param-store.js +4 -1
- package/dist/router-tanstack/search-param-store.d.ts +29 -5
- package/dist/router-tanstack/search-param-store.d.ts.map +1 -1
- package/dist/router-tanstack/search-param-store.js +31 -3
- package/dist/router-tanstack/search-param-store.test.d.ts +2 -0
- package/dist/router-tanstack/search-param-store.test.d.ts.map +1 -0
- package/dist/shell/app-mobile-nav.module.css +11 -3
- package/dist/shell/index.d.ts +12 -5
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +24 -6
- package/dist/surfaces.d.ts +1 -1
- package/dist/surfaces.js +1 -1
- package/dist/tokens/index.d.ts +5 -3
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +7 -1
- package/dist/tokens/palette.d.ts +35 -27
- package/dist/tokens/palette.d.ts.map +1 -1
- package/dist/tokens/palette.js +24 -18
- package/dist/tokens/text-scale.test.d.ts +2 -0
- package/dist/tokens/text-scale.test.d.ts.map +1 -0
- package/dist/tokens.css +21 -17
- package/dist/vite-color-mix.js +2 -2
- package/dist/vite.d.ts +24 -2
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +54 -13
- package/llms.txt +3 -3
- package/package.json +4 -3
- package/src/agent/stick-to-bottom.tsx +1 -1
- package/src/agent-chat/thread-detail-panel.tsx +1 -1
- package/src/agent-chat/thread-message.tsx +3 -3
- package/src/agent-chat/tool-chip.tsx +1 -1
- package/src/charts/kinds/Bars.tsx +1 -1
- package/src/charts/kinds/Donut.tsx +3 -2
- package/src/charts/kinds/DualPanel.tsx +2 -2
- package/src/charts/kinds/Heatmap.tsx +1 -1
- package/src/charts/kinds/MultiLine.tsx +1 -1
- package/src/charts/kinds/StackedArea.tsx +1 -1
- package/src/charts/kinds/ZonedLine.tsx +1 -1
- package/src/charts/primitives/ChartLegend.tsx +2 -2
- package/src/charts/primitives/ChartTooltip.tsx +1 -1
- package/src/cli/index.ts +1614 -164
- package/src/cli/init-sync.test.ts +21 -3
- package/src/cli/template-ledger.test.ts +1 -1
- package/src/cli/tokens-css.test.ts +27 -14
- package/src/cli/toolchain-wiring.test.ts +856 -0
- package/src/cli/waiver-audit.test.ts +131 -0
- package/src/dashboard/delta-badge.tsx +3 -3
- package/src/dashboard/stat-card.tsx +1 -1
- package/src/data/virtual-list.tsx +8 -8
- package/src/guard/check-source.test.ts +1104 -12
- package/src/guard/index.ts +1106 -103
- package/src/guard/types.ts +29 -4
- package/src/register.ts +32 -0
- package/src/router-tanstack/multi-search-param-store.ts +16 -1
- package/src/router-tanstack/search-param-store.test.ts +135 -0
- package/src/router-tanstack/search-param-store.ts +90 -8
- package/src/shell/app-mobile-nav.module.css +11 -3
- package/src/shell/app-mobile-nav.tsx +1 -1
- package/src/shell/index.test.tsx +52 -0
- package/src/shell/index.tsx +64 -13
- package/src/surfaces.ts +1 -1
- package/src/theme/density-relations.test.ts +8 -5
- package/src/theme/spacing.test.ts +2 -0
- package/src/theme-toggle/index.tsx +2 -2
- package/src/tokens/index.ts +28 -3
- package/src/tokens/palette.ts +24 -18
- package/src/tokens/text-scale.test.ts +51 -0
- package/src/vite-color-mix.ts +2 -2
- package/src/vite.test.ts +125 -6
- package/src/vite.ts +148 -20
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `check-theme --audit-allows` — the accountability half of the escape hatch.
|
|
3
|
+
*
|
|
4
|
+
* 1.20.0 shipped `theme-allow-unscoped`, which reports a waiver that names no rule. It cannot see
|
|
5
|
+
* the other failure: a perfectly well-written waiver whose finding no longer exists. Two consumers
|
|
6
|
+
* found five of those by hand — editing the token out and re-running the guard — and that manual
|
|
7
|
+
* method is what this command automates, which is why every assertion below is about a VERDICT
|
|
8
|
+
* derived from re-running the scan, never from parsing the annotation's text.
|
|
9
|
+
*
|
|
10
|
+
* Run: bun test packages/basalt-ui/src/cli/waiver-audit.test.ts
|
|
11
|
+
*/
|
|
12
|
+
import { afterEach, beforeEach, describe, expect, it } from 'bun:test'
|
|
13
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
|
14
|
+
import { tmpdir } from 'node:os'
|
|
15
|
+
import { join, resolve } from 'node:path'
|
|
16
|
+
|
|
17
|
+
import { checkTheme } from './index.ts'
|
|
18
|
+
|
|
19
|
+
let dir: string
|
|
20
|
+
|
|
21
|
+
function write(relPath: string, content: string): void {
|
|
22
|
+
const abs = join(dir, relPath)
|
|
23
|
+
mkdirSync(resolve(abs, '..'), { recursive: true })
|
|
24
|
+
writeFileSync(abs, content, 'utf8')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function audit(): { code: number; log: string } {
|
|
28
|
+
const originalLog = console.log
|
|
29
|
+
const originalError = console.error
|
|
30
|
+
let log = ''
|
|
31
|
+
const sink = (...args: unknown[]) => {
|
|
32
|
+
log += `${args.join(' ')}\n`
|
|
33
|
+
}
|
|
34
|
+
console.log = sink
|
|
35
|
+
console.error = sink
|
|
36
|
+
try {
|
|
37
|
+
return { code: checkTheme(dir, ['--audit-allows']), log }
|
|
38
|
+
} finally {
|
|
39
|
+
console.log = originalLog
|
|
40
|
+
console.error = originalError
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** A repo whose only source file is the one under test. */
|
|
45
|
+
function fixture(source: string, basalt: Record<string, unknown> = {}): void {
|
|
46
|
+
write('package.json', JSON.stringify({ name: 'fixture', basalt: { roots: ['src'], ...basalt } }))
|
|
47
|
+
write('src/app.tsx', source)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
beforeEach(() => {
|
|
51
|
+
dir = mkdtempSync(resolve(tmpdir(), 'basalt-audit-'))
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
afterEach(() => {
|
|
55
|
+
rmSync(dir, { recursive: true, force: true })
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
describe('check-theme --audit-allows — theme-allow annotations', () => {
|
|
59
|
+
it('names what a live waiver still suppresses, and exits 0', () => {
|
|
60
|
+
fixture("export const c = '#ff0000' // theme-allow raw-hex — a deliberate brand literal\n")
|
|
61
|
+
const { code, log } = audit()
|
|
62
|
+
expect(log).toContain('suppresses raw-hex@1')
|
|
63
|
+
expect(code).toBe(0)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('calls a waiver over a line with no finding DEAD, and exits 1', () => {
|
|
67
|
+
fixture('export const c = 1 // theme-allow raw-hex — nothing here fires any more\n')
|
|
68
|
+
const { code, log } = audit()
|
|
69
|
+
expect(log).toContain('SUPPRESSES NOTHING')
|
|
70
|
+
expect(code).toBe(1)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('does not report prose that merely mentions the token', () => {
|
|
74
|
+
// The guard stopped treating a mid-comment mention as an annotation for exactly this reason;
|
|
75
|
+
// an audit that listed them as dead waivers would send someone editing documentation.
|
|
76
|
+
fixture('// Waive it with a `theme-allow raw-hex — why` comment.\nexport const c = 1\n')
|
|
77
|
+
const { code, log } = audit()
|
|
78
|
+
expect(log).toContain('annotations (0)')
|
|
79
|
+
expect(code).toBe(0)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('refuses to judge a waiver scoped to an oxlint plugin rule instead of calling it dead', () => {
|
|
83
|
+
// `hand-rolled-plot` lives in the plugin, so `checkSource` can never see what it suppresses.
|
|
84
|
+
// Reporting "delete it" here would be the command telling someone to remove a live waiver.
|
|
85
|
+
fixture(
|
|
86
|
+
'// theme-allow hand-rolled-plot — DualPanel is not a single cartesian plot\nexport const c = 1\n',
|
|
87
|
+
)
|
|
88
|
+
const { code, log } = audit()
|
|
89
|
+
expect(log).toContain('not a check-theme kind')
|
|
90
|
+
expect(log).not.toContain('SUPPRESSES NOTHING')
|
|
91
|
+
expect(code).toBe(0)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('marks an unaccountable waiver while still reporting what it covers', () => {
|
|
95
|
+
fixture("export const c = '#ff0000' // theme-allow\n")
|
|
96
|
+
const { log } = audit()
|
|
97
|
+
expect(log).toContain('suppresses raw-hex@1')
|
|
98
|
+
expect(log).toContain('no rule id')
|
|
99
|
+
expect(log).toContain('1 unaccountable')
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
describe('check-theme --audit-allows — basalt.exemptRules', () => {
|
|
104
|
+
it('prints the reason from the object form, and says so when there is none', () => {
|
|
105
|
+
fixture("export const c = '#ff0000'\n", {
|
|
106
|
+
exemptRules: { 'raw-hex': { paths: ['src'], reason: 'a vendor asset basalt does not own' } },
|
|
107
|
+
})
|
|
108
|
+
const { log } = audit()
|
|
109
|
+
expect(log).toContain('a vendor asset basalt does not own')
|
|
110
|
+
expect(log).toContain('suppresses findings in src/app.tsx')
|
|
111
|
+
|
|
112
|
+
fixture("export const c = '#ff0000'\n", { exemptRules: { 'raw-hex': ['src'] } })
|
|
113
|
+
expect(audit().log).toContain('[no reason recorded]')
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('the object form really does exempt — the reason is accountability, not a second config', () => {
|
|
117
|
+
fixture("export const c = '#ff0000'\n", {
|
|
118
|
+
exemptRules: { 'raw-hex': { paths: ['src'], reason: 'a vendor asset basalt does not own' } },
|
|
119
|
+
})
|
|
120
|
+
expect(checkTheme(dir, [])).toBe(0)
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it('reports a pattern that matches no scanned file as dead, and exits 1', () => {
|
|
124
|
+
// rollhook wrote a real, correct relative path, it matched nothing, and nothing said so — the
|
|
125
|
+
// finding kept failing the build with the config sitting right there looking like the answer.
|
|
126
|
+
fixture("export const c = '#ff0000'\n", { exemptRules: { 'raw-hex': ['does-not-exist'] } })
|
|
127
|
+
const { code, log } = audit()
|
|
128
|
+
expect(log).toContain('matches no scanned file')
|
|
129
|
+
expect(code).toBe(1)
|
|
130
|
+
})
|
|
131
|
+
})
|
|
@@ -49,9 +49,9 @@ export function DeltaBadge({
|
|
|
49
49
|
height: 'auto',
|
|
50
50
|
},
|
|
51
51
|
label: {
|
|
52
|
-
display: 'inline-flex', // theme-allow
|
|
52
|
+
display: 'inline-flex', // theme-allow inline-display — a Badge `styles.label` part, not a layout node Flex could replace
|
|
53
53
|
alignItems: 'center',
|
|
54
|
-
gap: 3, // theme-allow
|
|
54
|
+
gap: 3, // theme-allow inline-spacing — sub-scale glyph-to-label gap, below the smallest scale stop
|
|
55
55
|
fontFamily: 'var(--basalt-font-mono)',
|
|
56
56
|
fontSize: VX.text.xs,
|
|
57
57
|
fontWeight: 600,
|
|
@@ -66,7 +66,7 @@ export function DeltaBadge({
|
|
|
66
66
|
{format(value)}
|
|
67
67
|
{period && (
|
|
68
68
|
<span
|
|
69
|
-
style={{ marginLeft: 4, fontWeight: 500, opacity: 0.6 }} // theme-allow
|
|
69
|
+
style={{ marginLeft: 4, fontWeight: 500, opacity: 0.6 }} // theme-allow inline-spacing — sub-scale optical period offset, below the smallest scale stop
|
|
70
70
|
>
|
|
71
71
|
{period}
|
|
72
72
|
</span>
|
|
@@ -110,7 +110,7 @@ export function StatCard({
|
|
|
110
110
|
// explicit for intent.
|
|
111
111
|
padding: 'var(--mantine-spacing-xs) var(--mantine-spacing-sm)',
|
|
112
112
|
minHeight: 118,
|
|
113
|
-
display: 'flex', // theme-allow
|
|
113
|
+
display: 'flex', // theme-allow inline-display — Card root as a flex column; Mantine Card takes no layout props
|
|
114
114
|
flexDirection: 'column',
|
|
115
115
|
gap: 'var(--vx-space-stat-card-gap)',
|
|
116
116
|
overflow: 'hidden',
|
|
@@ -126,10 +126,10 @@ export function BasaltVirtualList<T>({
|
|
|
126
126
|
|
|
127
127
|
if (isLoading) {
|
|
128
128
|
return (
|
|
129
|
-
// theme-allow — matches the virtualizer's own scroll box below, so the skeleton doesn't reflow.
|
|
129
|
+
// theme-allow raw-scroll-container — matches the virtualizer's own scroll box below, so the skeleton doesn't reflow.
|
|
130
130
|
<Box style={{ height, overflow: 'auto' }}>
|
|
131
131
|
{Array.from({ length: skeletonRows }, (_, i) => (
|
|
132
|
-
<
|
|
132
|
+
<Box
|
|
133
133
|
key={`skeleton-${i}`}
|
|
134
134
|
style={{
|
|
135
135
|
height: estimateSize,
|
|
@@ -144,7 +144,7 @@ export function BasaltVirtualList<T>({
|
|
|
144
144
|
height={`calc(${estimateSize}px - 2 * var(--vx-space-virtual-row-inset-y))`}
|
|
145
145
|
radius="sm"
|
|
146
146
|
/>
|
|
147
|
-
</
|
|
147
|
+
</Box>
|
|
148
148
|
))}
|
|
149
149
|
</Box>
|
|
150
150
|
)
|
|
@@ -155,12 +155,12 @@ export function BasaltVirtualList<T>({
|
|
|
155
155
|
ref={parentRef}
|
|
156
156
|
style={{
|
|
157
157
|
height,
|
|
158
|
-
// theme-allow — TanStack Virtual measures this element as the scroll container.
|
|
158
|
+
// theme-allow raw-scroll-container — TanStack Virtual measures this element as the scroll container.
|
|
159
159
|
overflow: 'auto',
|
|
160
160
|
}}
|
|
161
161
|
>
|
|
162
162
|
{/* Inner sizer — defines total scroll height for the virtualizer */}
|
|
163
|
-
<
|
|
163
|
+
<Box
|
|
164
164
|
style={{
|
|
165
165
|
height: rowVirtualizer.getTotalSize(),
|
|
166
166
|
position: 'relative',
|
|
@@ -170,7 +170,7 @@ export function BasaltVirtualList<T>({
|
|
|
170
170
|
const item = items[virtualItem.index]
|
|
171
171
|
if (item === undefined) return null
|
|
172
172
|
return (
|
|
173
|
-
<
|
|
173
|
+
<Box
|
|
174
174
|
key={virtualItem.key}
|
|
175
175
|
style={{
|
|
176
176
|
position: 'absolute',
|
|
@@ -182,10 +182,10 @@ export function BasaltVirtualList<T>({
|
|
|
182
182
|
}}
|
|
183
183
|
>
|
|
184
184
|
{renderItem(item, virtualItem.index)}
|
|
185
|
-
</
|
|
185
|
+
</Box>
|
|
186
186
|
)
|
|
187
187
|
})}
|
|
188
|
-
</
|
|
188
|
+
</Box>
|
|
189
189
|
</Box>
|
|
190
190
|
)
|
|
191
191
|
}
|