basalt-ui 1.20.0 → 1.22.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 +11 -5
- package/CHANGELOG.md +33 -0
- package/MIGRATING.md +231 -50
- package/README.md +57 -10
- package/agent/rules/basalt-app.md +49 -2
- package/agent/rules/basalt-charts.md +15 -12
- package/agent/rules/basalt-content.md +4 -0
- package/agent/rules/basalt-router.md +57 -5
- package/agent/rules/basalt-state.md +21 -3
- package/agent/rules/basalt-tokens.md +78 -16
- package/agent/skills/basalt-app/SKILL.md +5 -1
- package/agent/skills/basalt-charts/SKILL.md +5 -3
- package/agent/templates/CLAUDE-block.md.tpl +11 -5
- package/configs/lefthook.yml +48 -15
- package/configs/oxlint-plugin.js +218 -69
- package/configs/oxlint-plugin.test.ts +331 -9
- 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/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/cli/index.d.ts +55 -15
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +521 -71
- package/dist/cli/waiver-audit.test.d.ts +2 -0
- package/dist/cli/waiver-audit.test.d.ts.map +1 -0
- package/dist/guard/index.d.ts +114 -2
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +216 -33
- 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/index.d.ts +12 -5
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +24 -6
- package/dist/tokens/index.d.ts +3 -1
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +5 -1
- package/dist/tokens/palette.d.ts +2 -2
- package/dist/tokens/palette.js +2 -2
- 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 +4 -2
- package/dist/vite.d.ts +28 -3
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +75 -26
- package/llms.txt +2 -2
- package/package.json +1 -1
- package/src/charts/kinds/Bars.tsx +1 -1
- package/src/charts/kinds/Donut.tsx +1 -1
- 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/cli/check-theme.test.ts +35 -0
- package/src/cli/help.test.ts +5 -1
- package/src/cli/index.ts +978 -133
- package/src/cli/init-sync.test.ts +2 -0
- package/src/cli/template-ledger.test.ts +1 -1
- package/src/cli/toolchain-wiring.test.ts +352 -1
- package/src/cli/waiver-audit.test.ts +211 -0
- package/src/guard/check-source.test.ts +638 -1
- package/src/guard/index.ts +556 -63
- 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.tsx +1 -1
- package/src/shell/index.test.tsx +52 -0
- package/src/shell/index.tsx +64 -13
- package/src/tokens/index.ts +24 -1
- package/src/tokens/palette.ts +2 -2
- package/src/tokens/text-scale.test.ts +51 -0
- package/src/vite.test.ts +158 -6
- package/src/vite.ts +176 -34
package/AGENTS.md
CHANGED
|
@@ -79,11 +79,17 @@ Register `guard-hook` in `.claude/settings.json` under `hooks.PreToolUse` with m
|
|
|
79
79
|
`Write|Edit|MultiEdit` and command `bunx basalt-ui guard-hook` so the agent can't write off-palette
|
|
80
80
|
colors without a `theme-allow` comment.
|
|
81
81
|
|
|
82
|
-
**The escape hatch is `theme-allow <rule-id> — <reason>`**, scoped to that one kind
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
**The escape hatch is `theme-allow <rule-id> — <reason>`**, scoped to that one kind and to that one
|
|
83
|
+
node/line. It must START its comment (after `//`, `/*`, `<!--`, a block gutter `*`, or whitespace),
|
|
84
|
+
and is honoured on the reported line, on a comment-only line directly above it (reaching the first
|
|
85
|
+
CODE line below, through the rest of its comment block), and — in CSS — from a trailing comment back
|
|
86
|
+
over the declaration it terminates. Whole-file scope is `theme-allow-file <id>… — <why>`; a bare
|
|
87
|
+
`theme-allow-file` waives nothing. A bare `theme-allow` still waives everything on its placement but
|
|
88
|
+
reports `theme-allow-unscoped`. A word in the id slot that names no rule waives nothing.
|
|
89
|
+
`.json`/`.webmanifest` use a `"basalt:theme-allow[-file]"` member. Audit them all with
|
|
90
|
+
`basalt-ui check-theme --audit-allows`, which exits 1 on a waiver that suppresses nothing. It judges
|
|
91
|
+
plugin-rule waivers too (since 1.21.1), by re-running oxlint — so it needs oxlint reachable;
|
|
92
|
+
otherwise the verdict is "cannot judge", never "dead".
|
|
87
93
|
|
|
88
94
|
> **Framework-internal only** — `bunx basalt-ui check-coverage` is a self-consistency gate for the
|
|
89
95
|
> basalt-ui repo itself (asserts SURFACES ↔ rule files ↔ skill files ↔ package.json exports). It
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,36 @@
|
|
|
1
|
+
# [1.22.0](https://github.com/jkrumm/basalt-ui/compare/v1.21.0...v1.22.0) (2026-08-22)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* let `icons: false` reach the manifest it was only skipping in the head ([58abe27](https://github.com/jkrumm/basalt-ui/commit/58abe27be10e5baf7c4659aa79904a7646ff092d))
|
|
7
|
+
* scope the shadow-export tripwire to basalt consumers, and to components ([60716d4](https://github.com/jkrumm/basalt-ui/commit/60716d4ba00908c1a157f5568e192c6c50e438ec))
|
|
8
|
+
* stop sync scaffolding a second consumer, and give the audit the oxlint half ([45cf3d3](https://github.com/jkrumm/basalt-ui/commit/45cf3d340f081e61f3f10f852d12f79928993d2a))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* give the waiver audit an annotation reader, and fix two shapes ([0dde850](https://github.com/jkrumm/basalt-ui/commit/0dde85052269780a61ead0688005db16e24f009a))
|
|
14
|
+
|
|
15
|
+
# [1.21.0](https://github.com/jkrumm/basalt-ui/compare/v1.20.0...v1.21.0) (2026-08-22)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* delete a waiver that stopped covering anything ([3984281](https://github.com/jkrumm/basalt-ui/commit/3984281fff6313c225bdf1e185de4a427d2ef4df))
|
|
21
|
+
* emit 0.1, not 0.10, from the shadow palette itself ([48adfff](https://github.com/jkrumm/basalt-ui/commit/48adfffcc9ad7713cacc07c1fc090b219fd175fa))
|
|
22
|
+
* let the real color scheme win, and keep <meta charset> in its window ([8708c85](https://github.com/jkrumm/basalt-ui/commit/8708c85f1488b25e6a059ee879dcadf6350278a2))
|
|
23
|
+
* make an annotation start its comment, and let it scope to one node ([a4cf78c](https://github.com/jkrumm/basalt-ui/commit/a4cf78c546d291c33a27bf94d7636758072a1005))
|
|
24
|
+
* persist sidebar collapse through basalt's own state primitive ([c576040](https://github.com/jkrumm/basalt-ui/commit/c576040172dfeb8d66b52909bbcf55b7b1f91c70))
|
|
25
|
+
* resolve the paths the wiring checks only matched, and audit the waivers ([c595181](https://github.com/jkrumm/basalt-ui/commit/c5951817ed01d7729f031bba2f3da50c3bf60666))
|
|
26
|
+
* restore the palette fixture to emitter bytes ([605e240](https://github.com/jkrumm/basalt-ui/commit/605e240387a417f57ae8070f97bad79d97545d60))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### Features
|
|
30
|
+
|
|
31
|
+
* give the type ladder the two rungs consumers could only waive ([29b9ef3](https://github.com/jkrumm/basalt-ui/commit/29b9ef3767282dffb51fb52138d6ae4f1d24dd62))
|
|
32
|
+
* make the search-param store's reader reachable from the API ([6924970](https://github.com/jkrumm/basalt-ui/commit/6924970f410b82ba5cfc65fd1d5088124934fc8f))
|
|
33
|
+
|
|
1
34
|
# [1.20.0](https://github.com/jkrumm/basalt-ui/compare/v1.19.1...v1.20.0) (2026-08-22)
|
|
2
35
|
|
|
3
36
|
|
package/MIGRATING.md
CHANGED
|
@@ -6,8 +6,11 @@ nav definition" does not say `ChartHoverSync` was deleted. This file is that hal
|
|
|
6
6
|
renamed exports per minor, with the replacement.
|
|
7
7
|
|
|
8
8
|
Reconstructed from `git diff` over the published export surface across `v1.0.0..v1.19.1`, then
|
|
9
|
-
cross-checked against the repo's `scripts/export-surface.json` snapshot.
|
|
10
|
-
|
|
9
|
+
cross-checked against the repo's `scripts/export-surface.json` snapshot. **Every replacement below
|
|
10
|
+
was re-audited against the built declaration files at 1.20.0 (2026-08-22)** after round 5 caught one
|
|
11
|
+
row that was wrong. That pass corrected 4 table rows and 3 prose claims. The 1.21.0 and 1.21.1
|
|
12
|
+
sections were written against source, not against their commit messages. Check the types, not this
|
|
13
|
+
table, if the two disagree.
|
|
11
14
|
|
|
12
15
|
**No majors, by policy.** A rename or a removal ships as a plain `feat:` on the 1.x line, so a minor
|
|
13
16
|
bump can require code changes. Skipping several at once is the expensive case — read every section
|
|
@@ -17,16 +20,174 @@ between your version and the target.
|
|
|
17
20
|
> `## [1.` finds only the patches.
|
|
18
21
|
|
|
19
22
|
**Minors with no public API delta:** 1.1.0, 1.3.0, 1.4.0, 1.5.0, 1.6.0, 1.7.0, 1.8.0, 1.9.0,
|
|
20
|
-
1.10.0, 1.13.0, 1.14.0, 1.16.0, 1.18.0 — and every patch. Additive-only subpaths: `./tokens.css`
|
|
23
|
+
1.10.0, 1.13.0, 1.14.0, 1.16.0, 1.18.0 — and every patch except 1.21.1. Additive-only subpaths: `./tokens.css`
|
|
21
24
|
at 1.3.0, `./agent-chat` at 1.10.0.
|
|
22
25
|
|
|
23
26
|
---
|
|
24
27
|
|
|
28
|
+
## 1.21.1 — the toolchain stops overclaiming
|
|
29
|
+
|
|
30
|
+
**No export removed or renamed; four added.** Nothing you wrote changed. Every entry below is a
|
|
31
|
+
check that reported an answer it had not earned, so a green CI on 1.21.0 was, for several of them,
|
|
32
|
+
green over an empty set.
|
|
33
|
+
|
|
34
|
+
**`sync` refuses where it used to scaffold.** Run from a sub-package that depends on basalt-ui but
|
|
35
|
+
holds no `.basalt/manifest.json` — `apps/dashboard`, `apps/web` — it printed `0 updated, 20
|
|
36
|
+
recreated`, wrote a second `basalt` key, and stood up a complete competing install beside the real
|
|
37
|
+
one at the repo root. argo and rb both reverted it by hand. It now resolves its project exactly as
|
|
38
|
+
`check-theme` and `doctor` do, and **exits 1** when the resolved project has no manifest, naming the
|
|
39
|
+
install it found above instead. The refusal runs before the `basalt.roots` backfill, which was half
|
|
40
|
+
the damage. Two ways to unblock it:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
cd <the package holding .basalt/manifest.json> && bunx basalt-ui sync
|
|
44
|
+
BASALT_CWD=<that package> bunx basalt-ui sync # or from anywhere
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The summary line gained a `created` counter. **`recreated` now means what it always claimed** — the
|
|
48
|
+
ledger placed that file once and it went missing. Twenty first-time writes were never that.
|
|
49
|
+
|
|
50
|
+
**`--audit-allows` judges plugin-rule annotations.** They used to print "not a check-theme kind" and
|
|
51
|
+
drop out of the exit-1 gate, so the gate covered an empty set wherever the waivers were plugin
|
|
52
|
+
rules: argo's whole tally was `0 live, 0 dead, 8 outside reach`, and 11 of linewatch's 14 went the
|
|
53
|
+
same way. Each is now probed by re-running oxlint over that one file with the annotation
|
|
54
|
+
neutralized. Judged now: argo 8 of 8, linewatch 14 of 14, basalt's own tree 23 of 23.
|
|
55
|
+
|
|
56
|
+
**It requires oxlint to be reachable.** The probe writes one neutralized sibling file and re-runs
|
|
57
|
+
oxlint over it (oxlint has no stdin mode), removing it in a `finally`. Where oxlint cannot run, the
|
|
58
|
+
annotation is reported as **"cannot judge"**, never as dead. The report now also prints the scope it
|
|
59
|
+
audited — `0 dead` over `basalt.roots` was reading as `0 dead anywhere`.
|
|
60
|
+
|
|
61
|
+
**`doctor`'s `lefthook-preset` check asks a different question.** It tested whether the config text
|
|
62
|
+
contained the `extends` string. It now asks whether the gate EXISTS, via `lefthook dump`, which
|
|
63
|
+
resolves `extends`, `include` and per-command `root:`. linewatch wires all three jobs with
|
|
64
|
+
`root: 'web/'` precisely because `extends` merges commands _without_ their working directory — it
|
|
65
|
+
was correctly configured, got warned, and the old advice would have broken it. Three outcomes: a
|
|
66
|
+
broken `extends` target is still a hard fail, a provably absent gate is a warn, and where
|
|
67
|
+
`lefthook dump` could not run you get an advisory warning naming what it could not see.
|
|
68
|
+
|
|
69
|
+
**`basalt/shadow-basalt-export` narrowed twice.** It now gates on `isBasaltScopedFile` like every
|
|
70
|
+
other rule in the file, and needs a component-SHAPED declaration — a function, an arrow, a
|
|
71
|
+
`memo`/`forwardRef` wrapper, or a class extending one. It had been firing on a `SlugTracker` class
|
|
72
|
+
in a React-free package carrying no basalt-ui dependency, telling it to import from basalt-ui. The
|
|
73
|
+
stated limit is unchanged: exact-name-only, a tripwire, not coverage.
|
|
74
|
+
|
|
75
|
+
**`basaltAppPlugin({ icons: false })` now omits the manifest's `icons` member too.** It skipped the
|
|
76
|
+
head `<link>` icons and emitted the two PNG manifest entries anyway, so `{ manifest: true, icons:
|
|
77
|
+
false }` shipped an installable app pointing at two 404s. **If you went hybrid over this** — the
|
|
78
|
+
plugin for the head, a hand-written manifest for the rest — **you can drop the hand-written half.**
|
|
79
|
+
The option's JSDoc said "skips the head `<link>` icons"; it now says what the option does.
|
|
80
|
+
|
|
81
|
+
**Two `theme-allow` comment shapes were silently broken and now work.** Plainly: this is the fourth
|
|
82
|
+
hole found in this one contract in three rounds. The full thirteen-shape matrix is now pinned in
|
|
83
|
+
both halves — `src/guard/check-source.test.ts` and `configs/oxlint-plugin.test.ts` — so the two
|
|
84
|
+
parsers can no longer disagree about what an annotation IS.
|
|
85
|
+
|
|
86
|
+
| Shape | What it did | Now |
|
|
87
|
+
| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
|
|
88
|
+
| `/** theme-allow <id> — <why> */` on one line | waived under oxlint, reported under `check-theme` | both halves honour it |
|
|
89
|
+
| `{/*` + token on the next line + `*/}` | comment-stripping left a bare `}`, so the line read as CODE and the annotation was classified trailing — scoped to a brace | scoped to the node below it, as written |
|
|
90
|
+
|
|
91
|
+
**linewatch writes that wrapped shape for every hand-composed chart axis**, so any _guard_ kind
|
|
92
|
+
annotated that way was silently unwaivable. It only appeared to work because the rules it names
|
|
93
|
+
(`hand-rolled-plot`) live in the oxlint plugin, whose placement test is comment-node-based and never
|
|
94
|
+
saw the brace.
|
|
95
|
+
|
|
96
|
+
**New, additive — `./guard` gains four runtime exports**, the reader half of the audit:
|
|
97
|
+
|
|
98
|
+
| Export | Note |
|
|
99
|
+
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
100
|
+
| `findAllowAnnotations(text, relPath, cfg)` | every annotation as written, ids split into `guardKinds` / `pluginRules` / `unknownRules`. Shares `collectAllowAnnotations` with `checkSource`, so it cannot list a line the scan does not honour |
|
|
101
|
+
| `neutralizeAllowAnnotation(text, line, cfg)` | one annotation rewritten to the token below, every other left intact — the probe half of an audit |
|
|
102
|
+
| `NEUTRALIZED_ALLOW_TOKEN` | so a guard probe and an oxlint probe neutralize identically |
|
|
103
|
+
| `PLUGIN_RULE_IDS` | the ids only oxlint can judge |
|
|
104
|
+
|
|
105
|
+
The `AllowAnnotationSite` type ships alongside them.
|
|
106
|
+
|
|
107
|
+
**Known gaps, reported and not fixed:** `--audit-allows` says nothing about `basalt.exempt` — a
|
|
108
|
+
whole file removed from the scan, the broadest exception the config surface has — and its
|
|
109
|
+
`scoped to …` line does not distinguish `theme-allow` from `theme-allow-file`.
|
|
110
|
+
|
|
111
|
+
## 1.21.0 — the `theme-allow` grammar
|
|
112
|
+
|
|
113
|
+
**No export removed or renamed.** One break, and it is in the escape hatch itself: **file scope must
|
|
114
|
+
now be spelled `theme-allow-file`.** At 1.20.0 an annotation that named a rule and gave a reason —
|
|
115
|
+
the exact shape the rule's own message asks for — was promoted to a whole-file declaration, which is
|
|
116
|
+
why per-node scoping never actually shipped. The two forms are now distinct:
|
|
117
|
+
|
|
118
|
+
```text
|
|
119
|
+
theme-allow → this node/line, EVERY rule (reports theme-allow-unscoped)
|
|
120
|
+
theme-allow <id>[, <id>…] [— <why>] → this node/line, those rules
|
|
121
|
+
theme-allow-file <id>[, <id>…] — <why> → the WHOLE FILE, those rules; a bare one waives NOTHING
|
|
122
|
+
"basalt:theme-allow[-file]": "<id>… — <why>" → the same two, for JSON / .webmanifest
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**The migration is one word per file declaration.** Measured across the consumer sweep: linewatch
|
|
126
|
+
0 → 11 findings, argo 0 → 6, rb 0 → 0 — all `warn`, so **no build changes colour**.
|
|
127
|
+
|
|
128
|
+
```diff
|
|
129
|
+
-// theme-allow hand-rolled-plot — two panes over one x scale
|
|
130
|
+
+// theme-allow-file hand-rolled-plot — two panes over one x scale
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**An annotation must now START its comment** — after `//`, `/*`, `<!--`, a block-comment gutter `*`,
|
|
134
|
+
or nothing but whitespace. Both parsers used a bare substring search, so a comment that merely
|
|
135
|
+
_mentioned_ the token parsed as the legacy blanket form and switched every rule off on the line
|
|
136
|
+
below. linewatch documented its own waivers in a docblock and thereby disarmed the file — a false
|
|
137
|
+
NEGATIVE, and the reason this ships as a break rather than a grace period. Every annotation anyone
|
|
138
|
+
actually writes still qualifies; a sentence about the escape hatch no longer waives anything.
|
|
139
|
+
|
|
140
|
+
**A comment-only annotation now reaches the first CODE line below it**, walking through the rest of
|
|
141
|
+
its own comment block. Before, a reason that wrapped onto a second line, or a docblock's `*/`,
|
|
142
|
+
absorbed the waiver and the natural shape silently waived nothing — argo hit that three times in one
|
|
143
|
+
upgrade. A blank line still ends the block.
|
|
144
|
+
|
|
145
|
+
**`.json` / `.webmanifest` finally have a waiver.** They have been scanned since 1.20.0 and cannot
|
|
146
|
+
hold a comment, so their findings were unwaivable and the printed remedy prescribed something
|
|
147
|
+
impossible; both consumers fell back to a blanket `exemptRules`. Use a member key — but for a
|
|
148
|
+
manifest, `basaltAppPlugin` is the first remedy, since a hand-copied hex drifts from the palette:
|
|
149
|
+
|
|
150
|
+
```json
|
|
151
|
+
{ "basalt:theme-allow-file": "raw-hex — a PWA manifest theme_color must be a literal hex" }
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Two API-shaped fixes worth acting on:**
|
|
155
|
+
|
|
156
|
+
| Change | What to do |
|
|
157
|
+
| -------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
158
|
+
| `createSearchParamStore` / `createMultiSearchParamStore` gain `linkSearch` | replace every nav link's `search: { <param>: '<literal>' }` with `search: <store>.linkSearch`, passed BY REFERENCE. A module-scope literal pins the fallback on every click — argo's reader had ZERO call sites, so "remember my window" had never worked |
|
|
159
|
+
| `BasaltShell` collapse moves to `createPersistedState` | the key is now `basalt:<storageKey>` holding `{ v, value }`; read it with `readPersistedValue(storageKey, 1)`. A one-time migration adopts the raw pre-1.21.0 value, so the sidebar does not re-expand on upgrade |
|
|
160
|
+
|
|
161
|
+
**New, additive:**
|
|
162
|
+
|
|
163
|
+
| Surface | Note |
|
|
164
|
+
| ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
165
|
+
| `check-theme --audit-allows` | every waiver and every `exemptRules` entry with what it still suppresses, proved by re-running the guard with that one occurrence neutralized. Exits 1 on a dead waiver — wire it into CI |
|
|
166
|
+
| `basalt.exemptRules` takes paths, globs and a reason | relative paths and directory prefixes (`public/site.webmanifest`, `src/agent`), globs (`*` stops at `/`, `**` does not, a slash-free glob also matches the basename), and `{ paths, reason }`. Entries that suppress nothing are now reported |
|
|
167
|
+
| `doctor` → `lefthook-preset` | hard-fails a broken `extends` target. lefthook merges a missing target into ZERO commands and exits 0, so a stale path leaves a repo with no pre-commit gate and a clean `lefthook dump`. `sync` reports the same seam |
|
|
168
|
+
| `basaltAppPlugin({ colorScheme })` | `'dark'` (default) / `'light'` / `'auto'` / `false`. Set it to whatever the app passes as `defaultColorScheme` — before this, a light-scheme consumer got dark native controls permanently |
|
|
169
|
+
| `VX.text.nano` (10px) + `VX.text.display` (30px) | the two rungs `inline-font-size` could previously only be waived for. A 20px rung was rejected: 21/20 = 1.05 is below the ladder's 1.06×–1.17× band, so `h2` is the remedy |
|
|
170
|
+
| `sync` backfills `basalt.roots` | only `init` wrote it, so every existing consumer sat on the undeclared `src` default while `guard-scan` passed. A declared value is never overwritten |
|
|
171
|
+
| `basalt/shadow-basalt-export` reads all nine barrels | the charts layer included. Still exact-name-only — a **tripwire, not coverage** |
|
|
172
|
+
|
|
173
|
+
Also: `check-theme`'s `inline-spacing` no longer reads a unitless number in a plain options bag as
|
|
174
|
+
CSS (`fitBounds({ padding: 48 })` stops reporting); the shipped `oxfmt` pre-commit job drops back to
|
|
175
|
+
`*.{ts,tsx,js,jsx,css}` and gains `--no-error-on-unmatched-pattern`; `tokens:css` emits `0.1` rather
|
|
176
|
+
than `0.10`, so a committed sheet stops failing prettier — re-run the command to pick it up.
|
|
177
|
+
|
|
178
|
+
**The lefthook preset overrides YOU, not the other way round.** An `extends` target wins on a
|
|
179
|
+
colliding key: declare `pre-commit.commands.oxfmt.run` (or `glob:`) in your own file and **yours**
|
|
180
|
+
is the one silently discarded. Only keys the preset does not define merge in. The guard job runs
|
|
181
|
+
`${BASALT_BIN:-bunx --no-install basalt-ui}`; that shell default is the sanctioned seam, set via
|
|
182
|
+
`env:`, which does merge.
|
|
183
|
+
|
|
25
184
|
## 1.20.0 — enforcement
|
|
26
185
|
|
|
27
186
|
**No export removed or renamed.** The whole delta is that things which used to pass now report. If
|
|
28
|
-
your build goes red on this upgrade, that is the release working
|
|
29
|
-
`warn
|
|
187
|
+
your build goes red on this upgrade, that is the release working. Every new **kind** and **rule**
|
|
188
|
+
lands `warn` — but two other changes in this release do fail a build: `basalt/raw-size-literal`
|
|
189
|
+
promotes to `error`, and the widened markup scan reads `index.html` / `.webmanifest`, where a raw
|
|
190
|
+
hex is an `error`-severity colour kind. Two consumers exited 1 on exactly that.
|
|
30
191
|
|
|
31
192
|
| Change | What you'll see | What to do |
|
|
32
193
|
| ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
|
|
@@ -69,9 +230,18 @@ line is usually a multi-line opening tag or a `{expr}` child), and in CSS a trai
|
|
|
69
230
|
reaching back over the declaration it terminates, which is what survives the shipped `oxfmt`
|
|
70
231
|
reflowing a long `background-color` so the hex lands above the comment.
|
|
71
232
|
|
|
72
|
-
**`basalt/hand-rolled-plot`
|
|
73
|
-
and
|
|
74
|
-
|
|
233
|
+
**`basalt/hand-rolled-plot` waivers must now be written deliberately.** Every assembly node is
|
|
234
|
+
reported individually, and a waiver is no longer picked up off whatever comment happened to sit on
|
|
235
|
+
the file's first assembly node — it needs a written declaration naming the rule and giving a reason,
|
|
236
|
+
anywhere in the file: `// theme-allow hand-rolled-plot — two panes over one x scale`.
|
|
237
|
+
|
|
238
|
+
**But the waiver is still whole-file, and per-node scoping is not expressible at 1.20.0.** Naming
|
|
239
|
+
the rule AND giving a reason is what `hasFileDeclaration` matches, at any line — so a comment
|
|
240
|
+
intended for one node silences the file. Dropping the reason keeps it node-scoped in the oxlint
|
|
241
|
+
plugin, but then `check-theme` reports `theme-allow-unscoped ("no reason")`. The two halves of the
|
|
242
|
+
contract intersect at exactly one legal shape and that shape is whole-file. Write the declaration in
|
|
243
|
+
the component's docblock, where it reads as the file-level decision it is. **Fixed in 1.21.0** —
|
|
244
|
+
see that section; the declaration moves to `theme-allow-file`.
|
|
75
245
|
|
|
76
246
|
**`doctor` will go red.** `SKIPPED` is a third outcome beside pass/warn/fail and exits non-zero on
|
|
77
247
|
its own — "All checks passed" is only printable when every check RAN. Three new hard checks:
|
|
@@ -98,18 +268,18 @@ each root's PARENT now contributes its `index.html` and `public/` tree.
|
|
|
98
268
|
|
|
99
269
|
## 1.19.0 — nav
|
|
100
270
|
|
|
101
|
-
| Removed / renamed | Replacement
|
|
102
|
-
| ----------------------------------------------------------------- |
|
|
103
|
-
| `NavLinkRenderer` (type) | `defineNav` + `{...useNav(NAV)}` (`basalt-ui/router-tanstack`), or `SidebarItem.Anchor`
|
|
104
|
-
| `BasaltShellProps.renderNavLink`, `AppSidebarProps.renderNavLink` | same
|
|
105
|
-
| `BreadcrumbLinkRenderer` (type) | `AppBreadcrumbs.parentAnchor`
|
|
106
|
-
| `BasaltShellProps.renderBreadcrumbLink` | same
|
|
107
|
-
| `BasaltShellProps.sidebarFooterExtra` | `mobileNav.moreExtra`
|
|
108
|
-
| `AppSidebarProps.footerExtra`, `AppSidebarProps.onClose` | —
|
|
109
|
-
| `MobileNavItem` (type) | `MobileNavSlot`
|
|
110
|
-
| `MobileNavSection` (type) | `MobileNavGroup`
|
|
111
|
-
| `MobileNavLinkRenderer` (type) | `MobileNavModel`
|
|
112
|
-
| `SidebarSection.mobileTab` | `SidebarSection.mobile
|
|
271
|
+
| Removed / renamed | Replacement | Note |
|
|
272
|
+
| ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
273
|
+
| `NavLinkRenderer` (type) | `defineNav` + `{...useNav(NAV)}` (`basalt-ui/router-tanstack`), or `SidebarItem.Anchor` | basalt now paints every nav pixel |
|
|
274
|
+
| `BasaltShellProps.renderNavLink`, `AppSidebarProps.renderNavLink` | same | |
|
|
275
|
+
| `BreadcrumbLinkRenderer` (type) | `AppBreadcrumbs.parentAnchor` | |
|
|
276
|
+
| `BasaltShellProps.renderBreadcrumbLink` | same | |
|
|
277
|
+
| `BasaltShellProps.sidebarFooterExtra` | `mobileNav.moreExtra` | its only host was the mobile drawer; it rendered nowhere on desktop |
|
|
278
|
+
| `AppSidebarProps.footerExtra`, `AppSidebarProps.onClose` | — | the full-height mobile sidebar drawer is deleted |
|
|
279
|
+
| `MobileNavItem` (type) | `MobileNavSlot` | |
|
|
280
|
+
| `MobileNavSection` (type) | `MobileNavGroup` | |
|
|
281
|
+
| `MobileNavLinkRenderer` (type) | `MobileNavModel` | |
|
|
282
|
+
| `SidebarSection.mobileTab` | `SidebarSection.mobile?: false \| NavSectionMobile` — an OBJECT (`{ tab: true, label?, icon? }`), or `false` to hide the section | `'tab' \| 'more' \| 'hidden'` is `SidebarItem.mobile` (`NavMobilePlacement`, `true` ≡ `'tab'`, `false` ≡ `'hidden'`) — a different prop on a different type |
|
|
113
283
|
|
|
114
284
|
A consumer with no router needs no migration — `href` + `onClick` still work.
|
|
115
285
|
|
|
@@ -130,20 +300,21 @@ never `search: true`.
|
|
|
130
300
|
|
|
131
301
|
The largest delta in the 1.x line. No shims were shipped.
|
|
132
302
|
|
|
133
|
-
| Removed / renamed | Replacement
|
|
134
|
-
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
135
|
-
| `ChartHoverSync`, `ChartHoverSyncProps` | `globalCursorStore` / `createCursorStore` / `useChartCursor` / `useCursorState` — **no provider**
|
|
136
|
-
| `HoverContext`, `HoverCtx`, `useHoverSync`, `DEFAULT_NO_OP_SET_HOVER` | `useChartCursor`, `useCursorState`, `CursorState`, `CursorStore`
|
|
137
|
-
| `ResponsiveChart`, `ResponsiveChartProps` | `CartesianChart` / `CartesianChartProps` (+ `autoMargin`, `PlotContext`, `PlotRect`)
|
|
138
|
-
| `ChartTooltip` (tip-based), `useChartTooltip`, `useTooltipStyles` | `ChartTooltipFloat` + the `tooltip: CartesianTooltipConfig` prop
|
|
139
|
-
| `BarsAxisConfig` | `AxisConfig` + `resolveAxisDomain`
|
|
140
|
-
| `ZonedLineTooltipLabel` | `tooltip` config on the kind
|
|
141
|
-
| `ZonedLine`/`MultiLine`: `yDomain`, `yAutoPad`, `yAutoMaxFloor`, `yAutoMinCeil`, `numTicksY`, `formatYTick` | one `y?: AxisConfig<T>`
|
|
142
|
-
| `ZonedLine`/`MultiLine`: `
|
|
143
|
-
| `ZonedLine`/`MultiLine`: `
|
|
144
|
-
| `
|
|
145
|
-
| `
|
|
146
|
-
| `
|
|
303
|
+
| Removed / renamed | Replacement | Note |
|
|
304
|
+
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
305
|
+
| `ChartHoverSync`, `ChartHoverSyncProps` | `globalCursorStore` / `createCursorStore` / `useChartCursor` / `useCursorState` — **no provider** | `ChartCursorScope` now _isolates_ a subtree; it is the inverse of the old provider, not a rename |
|
|
306
|
+
| `HoverContext`, `HoverCtx`, `useHoverSync`, `DEFAULT_NO_OP_SET_HOVER` | `useChartCursor`, `useCursorState`, `CursorState`, `CursorStore` | context → `useSyncExternalStore` |
|
|
307
|
+
| `ResponsiveChart`, `ResponsiveChartProps` | `CartesianChart` / `CartesianChartProps` (+ `autoMargin`, `PlotContext`, `PlotRect`) | now mandatory for every single-plot cartesian chart, enforced by `basalt/hand-rolled-plot` |
|
|
308
|
+
| `ChartTooltip` (tip-based), `useChartTooltip`, `useTooltipStyles` | `ChartTooltipFloat` + the `tooltip: CartesianTooltipConfig` prop | portal / flip / clamp done once |
|
|
309
|
+
| `BarsAxisConfig` | `AxisConfig` + `resolveAxisDomain` | |
|
|
310
|
+
| `ZonedLineTooltipLabel` | `tooltip` config on the kind | |
|
|
311
|
+
| `ZonedLine`/`MultiLine`: `yDomain`, `yAutoPad`, `yAutoMaxFloor`, `yAutoMinCeil`, `numTicksY`, `formatYTick` | one `y?: AxisConfig<T>` | `y` is **optional** where `yDomain` was required |
|
|
312
|
+
| `ZonedLine`/`MultiLine`: `tooltipLabel`, `renderExtraTooltipRows` | `tooltip.label`, `tooltip.extraRows` (`CartesianTooltipConfig`) | there is no `appendRows`; the field is `extraRows` |
|
|
313
|
+
| `ZonedLine`/`MultiLine`: `formatValue` | **`y.format`** — or per-series `ChartSeries.formatValue` for one row | `CartesianTooltipConfig` has no value formatter: the axis format IS the tooltip value format |
|
|
314
|
+
| `ZonedLine`/`MultiLine`: `numTicksX` | `xTicks` | `xZones` added alongside |
|
|
315
|
+
| `Bars`: `formatValue`, `hideBarTooltipRows`, `leftAxis`, `rightAxis`, `marginLeft`, `numTicksX`, `tooltipLabel`, `renderExtraTooltipRows`, `renderPrefixTooltipRows` | `y` / `y2` (`AxisConfig`), `xTicks`, `tooltip.{label,extraRows,prependRows}`, per-bar `BarsBar.formatValue` / `.tooltip`, measured `autoMargin` + `margin` | passing `y2` is what makes a chart dual-axis. `chartMargin({ rightAxis })` is **not** removed — it is still exported from `basalt-ui` and `basalt-ui/charts`; it is simply no longer needed, since the right gutter follows from measurement |
|
|
316
|
+
| `StackedArea`: `formatValue`, `numTicksX`, `numTicksY`, `yAutoMaxFloor`, `yLabel` | `y` (incl. `y.format`), `xTicks`; per-series `ChartSeries.formatValue` | `StackedAreaProps` has **no** `tooltip` prop — its tooltip is entirely derived from `series` |
|
|
317
|
+
| `Heatmap.width` | measures itself; takes `height` / `aspectRatio` / `fill` | |
|
|
147
318
|
|
|
148
319
|
## 1.12.0 — agent-chat, behaviour only
|
|
149
320
|
|
|
@@ -176,6 +347,12 @@ Names that have no 1.x equivalent: the `--chart-blue-1..8` sequential ramp (1.x
|
|
|
176
347
|
`--vx-fill-*` only), the default font stacks as a token (they live in `styles.css`), `purple` as a
|
|
177
348
|
text color, `black`, `blue-400`/`green-400`.
|
|
178
349
|
|
|
350
|
+
**The typefaces changed at 1.0, not just their delivery.** 0.4.2 shipped Instrument Sans; 1.x ships
|
|
351
|
+
Nunito Sans (body) + Hubot Sans (condensed headings), mono unchanged at JetBrains Mono. `fonts:css`
|
|
352
|
+
emits the 1.x stacks — for a 0.4.2 migrant that is a **rebrand**, not a restoration. It also emits
|
|
353
|
+
`--basalt-font-head-stretch: 88%`, tuned for Hubot Sans specifically; pointed at another face it
|
|
354
|
+
silently condenses it.
|
|
355
|
+
|
|
179
356
|
## 1.0.1 — CLI binary renamed
|
|
180
357
|
|
|
181
358
|
`basalt` → **`basalt-ui`**. Never `bunx basalt`.
|
|
@@ -191,18 +368,18 @@ live in `configs/oxlint.json`, and since 1.20.0 their grace is tracked by `PLUGI
|
|
|
191
368
|
named export beside the plugin — a test asserts it against the shipped preset in both directions, so
|
|
192
369
|
deleting an entry forces the level flip in the same commit.
|
|
193
370
|
|
|
194
|
-
| Rule | Landed | Became `error`
|
|
195
|
-
| ---------------------------------- | ---------------- |
|
|
196
|
-
| `mantine-shade-index` (guard kind) | 1.7.0 as `warn` | **1.11.0**
|
|
197
|
-
| `basalt/raw-scroll-container` | ≤1.2.0 as `off` | `warn` 1.10.0 → **`error` 1.13.0**
|
|
198
|
-
| `basalt/ai-sdk-major` | 1.10.0 as `warn` | **1.13.0**
|
|
199
|
-
| `basalt/agent-no-raw-usechat` | 1.10.0 as `warn` | **1.13.0**
|
|
200
|
-
| `basalt/agent-resume-guard` | 1.10.0 as `warn` | **1.13.0**
|
|
201
|
-
| `basalt/raw-size-literal` | 1.7.0 as `warn` | **1.20.0**
|
|
202
|
-
| `basalt/hand-rolled-plot` | 1.15.0 as `warn` | still `warn` — widened at 1.
|
|
203
|
-
| `basalt/chart-legend-literal` | 1.15.0 as `warn` | still `warn` — widened at 1.20.0
|
|
204
|
-
| `basalt/shadow-basalt-export` | 1.20.0 as `warn` | may stay `warn` permanently
|
|
205
|
-
| `basalt/hand-rolled-shell` | 1.20.0 as `warn` | —
|
|
371
|
+
| Rule | Landed | Became `error` |
|
|
372
|
+
| ---------------------------------- | ---------------- | ------------------------------------------------------------------------ |
|
|
373
|
+
| `mantine-shade-index` (guard kind) | 1.7.0 as `warn` | **1.11.0** |
|
|
374
|
+
| `basalt/raw-scroll-container` | ≤1.2.0 as `off` | `warn` 1.10.0 → **`error` 1.13.0** |
|
|
375
|
+
| `basalt/ai-sdk-major` | 1.10.0 as `warn` | **1.13.0** |
|
|
376
|
+
| `basalt/agent-no-raw-usechat` | 1.10.0 as `warn` | **1.13.0** |
|
|
377
|
+
| `basalt/agent-resume-guard` | 1.10.0 as `warn` | **1.13.0** |
|
|
378
|
+
| `basalt/raw-size-literal` | 1.7.0 as `warn` | **1.20.0** |
|
|
379
|
+
| `basalt/hand-rolled-plot` | 1.15.0 as `warn` | still `warn` — widened again at 1.21.0; grace restarts with the widening |
|
|
380
|
+
| `basalt/chart-legend-literal` | 1.15.0 as `warn` | still `warn` — widened at 1.20.0 |
|
|
381
|
+
| `basalt/shadow-basalt-export` | 1.20.0 as `warn` | may stay `warn` permanently — widened at 1.21.0, narrowed at 1.21.1 |
|
|
382
|
+
| `basalt/hand-rolled-shell` | 1.20.0 as `warn` | — |
|
|
206
383
|
|
|
207
384
|
`card-with-border`, `inline-display`, `raw-html-layout`, `raw-form-control`, `raw-font-family` and
|
|
208
385
|
the other original guard kinds have been `error` since before 1.2.0 — they never had a grace minor.
|
|
@@ -214,7 +391,7 @@ in the shipped preset). A config still naming `import-boundary` after 1.1.0 disa
|
|
|
214
391
|
|
|
215
392
|
## Deprecated, not yet removed
|
|
216
393
|
|
|
217
|
-
The 32 camelCase `--vx-*` aliases deprecated in 1.5.0 are **still emitted at 1.
|
|
394
|
+
The 32 camelCase `--vx-*` aliases deprecated in 1.5.0 are **still emitted at 1.21.0**.
|
|
218
395
|
`buildPaletteCss({ legacyAliases: false })` / `tokens:css --no-legacy-aliases` opts out now; a later
|
|
219
396
|
minor flips the default.
|
|
220
397
|
|
|
@@ -223,6 +400,10 @@ minor flips the default.
|
|
|
223
400
|
- Props declared inline on a component (`function X({ a }: { a: string })`) rather than on an
|
|
224
401
|
exported type are outside the diff method used here. Spot checks on the shell and charts modules
|
|
225
402
|
found none, but the negative is unproven.
|
|
226
|
-
-
|
|
227
|
-
commit
|
|
228
|
-
|
|
403
|
+
- The 0.4.2 `./css` line counts in the 1.0.0 table (877 lines / 307 custom props / 19 utilities) come
|
|
404
|
+
from the removal commit, not from a re-read of the 0.4.2 tarball.
|
|
405
|
+
|
|
406
|
+
`AppBreadcrumbs.parentAnchor` was listed here through 1.20.0 and is now verified: it is declared on
|
|
407
|
+
the shipped `dist/shell/app-breadcrumbs.d.ts` (`parentAnchor?: NavAnchor`, with `parentHref` as the
|
|
408
|
+
no-router fallback). The type is inline on the component rather than an exported `AppBreadcrumbsProps`,
|
|
409
|
+
which is why the export-surface diff never saw it.
|
package/README.md
CHANGED
|
@@ -193,12 +193,19 @@ and everything derives from it — the guard's scan, the seeded CI `oxfmt` globs
|
|
|
193
193
|
exemption:
|
|
194
194
|
|
|
195
195
|
```json
|
|
196
|
-
// package.json — `roots` is written by init; the rest are yours to add
|
|
196
|
+
// package.json — `roots` is written by init and backfilled by sync; the rest are yours to add
|
|
197
197
|
{
|
|
198
198
|
"basalt": {
|
|
199
199
|
"roots": ["apps/web/src"],
|
|
200
200
|
"include": ["app/manifest.json"],
|
|
201
|
-
"profile": "tokens-only"
|
|
201
|
+
"profile": "tokens-only",
|
|
202
|
+
"exemptRules": {
|
|
203
|
+
"inline-display": ["agent"],
|
|
204
|
+
"raw-hex": {
|
|
205
|
+
"paths": ["public/site.webmanifest"],
|
|
206
|
+
"reason": "a PWA manifest theme_color MUST be a literal hex — JSON cannot reference a CSS var"
|
|
207
|
+
}
|
|
208
|
+
}
|
|
202
209
|
}
|
|
203
210
|
}
|
|
204
211
|
```
|
|
@@ -214,6 +221,19 @@ which would silence half the guard on any repo keeping Mantine in a different wo
|
|
|
214
221
|
Other keys (`exempt`, `severity`, `spacingSteps`, `forbiddenAccents`, …) are documented on the
|
|
215
222
|
`BasaltConfig` type.
|
|
216
223
|
|
|
224
|
+
**`exemptRules`** turns one kind off along a path. A pattern is a relative path
|
|
225
|
+
(`public/site.webmanifest`, or `src/agent` for everything under it), a glob (`*` stops at `/`, `**`
|
|
226
|
+
does not, and a slash-free glob is also tried against the basename, so `*.module.css` works), or a
|
|
227
|
+
bare path segment — the legacy shape, and the one nobody guesses, since before 1.21.0 it was the
|
|
228
|
+
ONLY shape and a real relative path matched nothing and said nothing. The object form records the
|
|
229
|
+
reason, which is what a `theme-allow` always carried and this key could not. A pattern that
|
|
230
|
+
suppresses nothing is now reported by a normal run and fails `--audit-allows`.
|
|
231
|
+
|
|
232
|
+
`sync` **backfills `roots`** when the key is absent — only `init` used to write it, so every
|
|
233
|
+
existing consumer stayed on the undeclared `src` default while `guard-scan` passed anyway. A
|
|
234
|
+
declared value is never overwritten; a layout it does not cover is reported as one line, because
|
|
235
|
+
`roots` being a subset is the normal case in a monorepo, not drift.
|
|
236
|
+
|
|
217
237
|
Basalt-emitted LINES are skipped, not whole files. The file has to earn it first — a `.css` path,
|
|
218
238
|
the `@generated basalt-ui` header verbatim on line 1, the version + invocation line on line 2 — and
|
|
219
239
|
then each line does too: at brace depth 0 a selector or a self-closing comment, inside a block only
|
|
@@ -222,12 +242,31 @@ stylesheet `tokens:css` just wrote is not reported back at you, pasting the mark
|
|
|
222
242
|
suppresses nothing, and an ordinary declaration smuggled into a sheet wearing the header is
|
|
223
243
|
reported on its own line.
|
|
224
244
|
|
|
225
|
-
**The escape hatch is scoped
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
245
|
+
**The escape hatch is scoped, and since 1.21.0 the two scopes are spelled apart:**
|
|
246
|
+
|
|
247
|
+
```text
|
|
248
|
+
theme-allow → this node/line, EVERY rule (reports theme-allow-unscoped)
|
|
249
|
+
theme-allow <id>[, <id>…] [— <why>] → this node/line, those rules
|
|
250
|
+
theme-allow-file <id>[, <id>…] — <why> → the WHOLE FILE, those rules; a bare one waives NOTHING
|
|
251
|
+
"basalt:theme-allow[-file]": "<id>… — <why>" → the same two, for JSON / .webmanifest
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
An annotation must **start** its comment (after `//`, `/*`, `<!--`, a block gutter `*`, or
|
|
255
|
+
whitespace) — before 1.21.0 any comment merely mentioning the token parsed as the bare blanket form,
|
|
256
|
+
so a file documenting its own waivers disarmed itself. Placements: the reported line, a comment-only
|
|
257
|
+
line directly above it (the only form JSX can express, and it reaches the first CODE line below,
|
|
258
|
+
walking through the rest of its comment block), or — in CSS — a trailing comment reaching back over
|
|
259
|
+
the declaration it terminates. The id slot is read strictly: a word there that names no rule waives
|
|
260
|
+
nothing, so a typo is never the blanket form.
|
|
261
|
+
|
|
262
|
+
`basalt-ui check-theme --audit-allows` reports every annotation and every `exemptRules` entry with
|
|
263
|
+
what it still suppresses, proved by re-running the scan with that one waiver neutralized. Exits 1 on
|
|
264
|
+
a dead waiver, so it is usable as a CI gate. Since 1.21.1 it runs **both** halves — `checkSource` for
|
|
265
|
+
a guard kind, oxlint over one neutralized sibling file for a plugin rule — so a waiver naming
|
|
266
|
+
`hand-rolled-plot` is judged rather than skipped. **It needs oxlint reachable**; where it is not, the
|
|
267
|
+
verdict is "cannot judge", never "dead". The report prints the scope it audited, because `0 dead`
|
|
268
|
+
over `basalt.roots` is not `0 dead anywhere`. Two known gaps: `basalt.exempt` is not audited at all,
|
|
269
|
+
and the `scoped to …` line does not distinguish `theme-allow` from `theme-allow-file`.
|
|
231
270
|
|
|
232
271
|
`doctor`'s `ai-major-parity` hard check fails a monorepo where workspace packages declare different
|
|
233
272
|
`ai` package majors — unless the split is intentional and written down. A producer pinned to an
|
|
@@ -260,7 +299,7 @@ deleted rather than silently surviving into the next real skew.
|
|
|
260
299
|
| `./tokens` | **free** | `VX` token refs, `buildPaletteCss`, `defineSeries`, `seriesTokens`, `groupTokens`, `alpha`, `ColorPair` / `SeriesMap` types |
|
|
261
300
|
| `./theme-lab` | coupled | `ThemeLabControls`, `applyOverrides`, `loadOverrides`, `COLOR_GROUPS` for live theme inspection |
|
|
262
301
|
| `./vite` | — | `basaltViteConfig(opts)` — Vite preset for basalt-ui consumer apps; `basaltAppPlugin(opts)` — PWA head, manifest, and icon metadata derived from the token palette |
|
|
263
|
-
| `./guard` | **free** | `checkSource`, `GUARD_RULES`, `Finding` types — the headless theme-guard core
|
|
302
|
+
| `./guard` | **free** | `checkSource`, `GUARD_RULES`, `Finding` types — the headless theme-guard core; plus the annotation reader `--audit-allows` is built on: `findAllowAnnotations`, `neutralizeAllowAnnotation`, `NEUTRALIZED_ALLOW_TOKEN`, `PLUGIN_RULE_IDS`, `AllowAnnotationSite` |
|
|
264
303
|
| `./query` | **free** | `createBasaltQueryClient`, transport-agnostic `unwrap`, lazy `BasaltQueryDevtools` |
|
|
265
304
|
| `./router-tanstack` | **free** | TanStack Router bridge: `useBasaltNav` (active route) + `useRouterBreadcrumbs` |
|
|
266
305
|
| `./forms` | coupled | Mantine form adapter: `useBasaltForm`, `field`, `FormErrorSummary`, `useFormDraft` (Standard Schema) |
|
|
@@ -655,7 +694,15 @@ honour `BASALT_CWD`, and relocate to the single workspace package carrying a bas
|
|
|
655
694
|
invoked from a repo root that has none (two candidates is reported as ambiguous, never guessed).
|
|
656
695
|
`tokens:css` and `fonts:css` both take `--check` as a CI drift gate.
|
|
657
696
|
|
|
658
|
-
`sync` strategy per file: unchanged since last write → overwrite; locally edited → skip (show diff);
|
|
697
|
+
`sync` strategy per file: unchanged since last write → overwrite; locally edited → skip (show diff);
|
|
698
|
+
missing → recreate. `--force` overwrites local edits. The summary counts `created` and `recreated`
|
|
699
|
+
apart: `recreated` means the ledger placed that file once and it went missing.
|
|
700
|
+
|
|
701
|
+
**`sync` refreshes an existing install; it does not create one.** It resolves its project exactly as
|
|
702
|
+
`check-theme` and `doctor` do, and since 1.21.1 **exits 1** when the resolved project has no
|
|
703
|
+
`.basalt/manifest.json`, naming the install it found above instead of scaffolding a second one
|
|
704
|
+
beside it. Run it at the package that holds the manifest, or point `BASALT_CWD` at that package.
|
|
705
|
+
Creating an install is `basalt-ui init`'s decision.
|
|
659
706
|
|
|
660
707
|
---
|
|
661
708
|
|