basalt-ui 1.20.0 → 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 +9 -5
- package/CHANGELOG.md +19 -0
- package/MIGRATING.md +147 -49
- package/README.md +42 -8
- package/agent/rules/basalt-app.md +41 -0
- 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 +73 -16
- package/agent/skills/basalt-app/SKILL.md +4 -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 +181 -68
- package/configs/oxlint-plugin.test.ts +215 -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 +48 -14
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +328 -66
- 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 +47 -2
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +176 -31
- 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 +24 -2
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +54 -13
- 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/index.ts +607 -128
- 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 +162 -0
- package/src/cli/waiver-audit.test.ts +131 -0
- package/src/guard/check-source.test.ts +441 -1
- package/src/guard/index.ts +430 -60
- 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 +125 -6
- package/src/vite.ts +148 -20
package/AGENTS.md
CHANGED
|
@@ -79,11 +79,15 @@ 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.
|
|
87
91
|
|
|
88
92
|
> **Framework-internal only** — `bunx basalt-ui check-coverage` is a self-consistency gate for the
|
|
89
93
|
> basalt-ui repo itself (asserts SURFACES ↔ rule files ↔ skill files ↔ package.json exports). It
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
# [1.21.0](https://github.com/jkrumm/basalt-ui/compare/v1.20.0...v1.21.0) (2026-08-22)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* delete a waiver that stopped covering anything ([3984281](https://github.com/jkrumm/basalt-ui/commit/3984281fff6313c225bdf1e185de4a427d2ef4df))
|
|
7
|
+
* emit 0.1, not 0.10, from the shadow palette itself ([48adfff](https://github.com/jkrumm/basalt-ui/commit/48adfffcc9ad7713cacc07c1fc090b219fd175fa))
|
|
8
|
+
* let the real color scheme win, and keep <meta charset> in its window ([8708c85](https://github.com/jkrumm/basalt-ui/commit/8708c85f1488b25e6a059ee879dcadf6350278a2))
|
|
9
|
+
* make an annotation start its comment, and let it scope to one node ([a4cf78c](https://github.com/jkrumm/basalt-ui/commit/a4cf78c546d291c33a27bf94d7636758072a1005))
|
|
10
|
+
* persist sidebar collapse through basalt's own state primitive ([c576040](https://github.com/jkrumm/basalt-ui/commit/c576040172dfeb8d66b52909bbcf55b7b1f91c70))
|
|
11
|
+
* resolve the paths the wiring checks only matched, and audit the waivers ([c595181](https://github.com/jkrumm/basalt-ui/commit/c5951817ed01d7729f031bba2f3da50c3bf60666))
|
|
12
|
+
* restore the palette fixture to emitter bytes ([605e240](https://github.com/jkrumm/basalt-ui/commit/605e240387a417f57ae8070f97bad79d97545d60))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* give the type ladder the two rungs consumers could only waive ([29b9ef3](https://github.com/jkrumm/basalt-ui/commit/29b9ef3767282dffb51fb52138d6ae4f1d24dd62))
|
|
18
|
+
* make the search-param store's reader reachable from the API ([6924970](https://github.com/jkrumm/basalt-ui/commit/6924970f410b82ba5cfc65fd1d5088124934fc8f))
|
|
19
|
+
|
|
1
20
|
# [1.20.0](https://github.com/jkrumm/basalt-ui/compare/v1.19.1...v1.20.0) (2026-08-22)
|
|
2
21
|
|
|
3
22
|
|
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.20.1 section was
|
|
12
|
+
written against source, not against its commit messages. Check the types, not this table, if the two
|
|
13
|
+
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
|
|
@@ -22,11 +25,86 @@ at 1.3.0, `./agent-chat` at 1.10.0.
|
|
|
22
25
|
|
|
23
26
|
---
|
|
24
27
|
|
|
28
|
+
## 1.20.1 — the `theme-allow` grammar
|
|
29
|
+
|
|
30
|
+
**No export removed or renamed.** One break, and it is in the escape hatch itself: **file scope must
|
|
31
|
+
now be spelled `theme-allow-file`.** At 1.20.0 an annotation that named a rule and gave a reason —
|
|
32
|
+
the exact shape the rule's own message asks for — was promoted to a whole-file declaration, which is
|
|
33
|
+
why per-node scoping never actually shipped. The two forms are now distinct:
|
|
34
|
+
|
|
35
|
+
```text
|
|
36
|
+
theme-allow → this node/line, EVERY rule (reports theme-allow-unscoped)
|
|
37
|
+
theme-allow <id>[, <id>…] [— <why>] → this node/line, those rules
|
|
38
|
+
theme-allow-file <id>[, <id>…] — <why> → the WHOLE FILE, those rules; a bare one waives NOTHING
|
|
39
|
+
"basalt:theme-allow[-file]": "<id>… — <why>" → the same two, for JSON / .webmanifest
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**The migration is one word per file declaration.** Measured across the consumer sweep: linewatch
|
|
43
|
+
0 → 11 findings, argo 0 → 6, rb 0 → 0 — all `warn`, so **no build changes colour**.
|
|
44
|
+
|
|
45
|
+
```diff
|
|
46
|
+
-// theme-allow hand-rolled-plot — two panes over one x scale
|
|
47
|
+
+// theme-allow-file hand-rolled-plot — two panes over one x scale
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**An annotation must now START its comment** — after `//`, `/*`, `<!--`, a block-comment gutter `*`,
|
|
51
|
+
or nothing but whitespace. Both parsers used a bare substring search, so a comment that merely
|
|
52
|
+
_mentioned_ the token parsed as the legacy blanket form and switched every rule off on the line
|
|
53
|
+
below. linewatch documented its own waivers in a docblock and thereby disarmed the file — a false
|
|
54
|
+
NEGATIVE, and the reason this ships as a break rather than a grace period. Every annotation anyone
|
|
55
|
+
actually writes still qualifies; a sentence about the escape hatch no longer waives anything.
|
|
56
|
+
|
|
57
|
+
**A comment-only annotation now reaches the first CODE line below it**, walking through the rest of
|
|
58
|
+
its own comment block. Before, a reason that wrapped onto a second line, or a docblock's `*/`,
|
|
59
|
+
absorbed the waiver and the natural shape silently waived nothing — argo hit that three times in one
|
|
60
|
+
upgrade. A blank line still ends the block.
|
|
61
|
+
|
|
62
|
+
**`.json` / `.webmanifest` finally have a waiver.** They have been scanned since 1.20.0 and cannot
|
|
63
|
+
hold a comment, so their findings were unwaivable and the printed remedy prescribed something
|
|
64
|
+
impossible; both consumers fell back to a blanket `exemptRules`. Use a member key — but for a
|
|
65
|
+
manifest, `basaltAppPlugin` is the first remedy, since a hand-copied hex drifts from the palette:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{ "basalt:theme-allow-file": "raw-hex — a PWA manifest theme_color must be a literal hex" }
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Two API-shaped fixes worth acting on:**
|
|
72
|
+
|
|
73
|
+
| Change | What to do |
|
|
74
|
+
| -------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
75
|
+
| `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 |
|
|
76
|
+
| `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.20.1 value, so the sidebar does not re-expand on upgrade |
|
|
77
|
+
|
|
78
|
+
**New, additive:**
|
|
79
|
+
|
|
80
|
+
| Surface | Note |
|
|
81
|
+
| ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
82
|
+
| `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 |
|
|
83
|
+
| `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 |
|
|
84
|
+
| `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 |
|
|
85
|
+
| `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 |
|
|
86
|
+
| `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 |
|
|
87
|
+
| `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 |
|
|
88
|
+
| `basalt/shadow-basalt-export` reads all nine barrels | the charts layer included. Still exact-name-only — a **tripwire, not coverage** |
|
|
89
|
+
|
|
90
|
+
Also: `check-theme`'s `inline-spacing` no longer reads a unitless number in a plain options bag as
|
|
91
|
+
CSS (`fitBounds({ padding: 48 })` stops reporting); the shipped `oxfmt` pre-commit job drops back to
|
|
92
|
+
`*.{ts,tsx,js,jsx,css}` and gains `--no-error-on-unmatched-pattern`; `tokens:css` emits `0.1` rather
|
|
93
|
+
than `0.10`, so a committed sheet stops failing prettier — re-run the command to pick it up.
|
|
94
|
+
|
|
95
|
+
**The lefthook preset overrides YOU, not the other way round.** An `extends` target wins on a
|
|
96
|
+
colliding key: declare `pre-commit.commands.oxfmt.run` (or `glob:`) in your own file and **yours**
|
|
97
|
+
is the one silently discarded. Only keys the preset does not define merge in. The guard job runs
|
|
98
|
+
`${BASALT_BIN:-bunx --no-install basalt-ui}`; that shell default is the sanctioned seam, set via
|
|
99
|
+
`env:`, which does merge.
|
|
100
|
+
|
|
25
101
|
## 1.20.0 — enforcement
|
|
26
102
|
|
|
27
103
|
**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
|
|
104
|
+
your build goes red on this upgrade, that is the release working. Every new **kind** and **rule**
|
|
105
|
+
lands `warn` — but two other changes in this release do fail a build: `basalt/raw-size-literal`
|
|
106
|
+
promotes to `error`, and the widened markup scan reads `index.html` / `.webmanifest`, where a raw
|
|
107
|
+
hex is an `error`-severity colour kind. Two consumers exited 1 on exactly that.
|
|
30
108
|
|
|
31
109
|
| Change | What you'll see | What to do |
|
|
32
110
|
| ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
|
|
@@ -69,9 +147,18 @@ line is usually a multi-line opening tag or a `{expr}` child), and in CSS a trai
|
|
|
69
147
|
reaching back over the declaration it terminates, which is what survives the shipped `oxfmt`
|
|
70
148
|
reflowing a long `background-color` so the hex lands above the comment.
|
|
71
149
|
|
|
72
|
-
**`basalt/hand-rolled-plot`
|
|
73
|
-
and
|
|
74
|
-
|
|
150
|
+
**`basalt/hand-rolled-plot` waivers must now be written deliberately.** Every assembly node is
|
|
151
|
+
reported individually, and a waiver is no longer picked up off whatever comment happened to sit on
|
|
152
|
+
the file's first assembly node — it needs a written declaration naming the rule and giving a reason,
|
|
153
|
+
anywhere in the file: `// theme-allow hand-rolled-plot — two panes over one x scale`.
|
|
154
|
+
|
|
155
|
+
**But the waiver is still whole-file, and per-node scoping is not expressible at 1.20.0.** Naming
|
|
156
|
+
the rule AND giving a reason is what `hasFileDeclaration` matches, at any line — so a comment
|
|
157
|
+
intended for one node silences the file. Dropping the reason keeps it node-scoped in the oxlint
|
|
158
|
+
plugin, but then `check-theme` reports `theme-allow-unscoped ("no reason")`. The two halves of the
|
|
159
|
+
contract intersect at exactly one legal shape and that shape is whole-file. Write the declaration in
|
|
160
|
+
the component's docblock, where it reads as the file-level decision it is. **Fixed in 1.20.1** —
|
|
161
|
+
see that section; the declaration moves to `theme-allow-file`.
|
|
75
162
|
|
|
76
163
|
**`doctor` will go red.** `SKIPPED` is a third outcome beside pass/warn/fail and exits non-zero on
|
|
77
164
|
its own — "All checks passed" is only printable when every check RAN. Three new hard checks:
|
|
@@ -98,18 +185,18 @@ each root's PARENT now contributes its `index.html` and `public/` tree.
|
|
|
98
185
|
|
|
99
186
|
## 1.19.0 — nav
|
|
100
187
|
|
|
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
|
|
188
|
+
| Removed / renamed | Replacement | Note |
|
|
189
|
+
| ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
190
|
+
| `NavLinkRenderer` (type) | `defineNav` + `{...useNav(NAV)}` (`basalt-ui/router-tanstack`), or `SidebarItem.Anchor` | basalt now paints every nav pixel |
|
|
191
|
+
| `BasaltShellProps.renderNavLink`, `AppSidebarProps.renderNavLink` | same | |
|
|
192
|
+
| `BreadcrumbLinkRenderer` (type) | `AppBreadcrumbs.parentAnchor` | |
|
|
193
|
+
| `BasaltShellProps.renderBreadcrumbLink` | same | |
|
|
194
|
+
| `BasaltShellProps.sidebarFooterExtra` | `mobileNav.moreExtra` | its only host was the mobile drawer; it rendered nowhere on desktop |
|
|
195
|
+
| `AppSidebarProps.footerExtra`, `AppSidebarProps.onClose` | — | the full-height mobile sidebar drawer is deleted |
|
|
196
|
+
| `MobileNavItem` (type) | `MobileNavSlot` | |
|
|
197
|
+
| `MobileNavSection` (type) | `MobileNavGroup` | |
|
|
198
|
+
| `MobileNavLinkRenderer` (type) | `MobileNavModel` | |
|
|
199
|
+
| `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
200
|
|
|
114
201
|
A consumer with no router needs no migration — `href` + `onClick` still work.
|
|
115
202
|
|
|
@@ -130,20 +217,21 @@ never `search: true`.
|
|
|
130
217
|
|
|
131
218
|
The largest delta in the 1.x line. No shims were shipped.
|
|
132
219
|
|
|
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
|
-
| `
|
|
220
|
+
| Removed / renamed | Replacement | Note |
|
|
221
|
+
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
222
|
+
| `ChartHoverSync`, `ChartHoverSyncProps` | `globalCursorStore` / `createCursorStore` / `useChartCursor` / `useCursorState` — **no provider** | `ChartCursorScope` now _isolates_ a subtree; it is the inverse of the old provider, not a rename |
|
|
223
|
+
| `HoverContext`, `HoverCtx`, `useHoverSync`, `DEFAULT_NO_OP_SET_HOVER` | `useChartCursor`, `useCursorState`, `CursorState`, `CursorStore` | context → `useSyncExternalStore` |
|
|
224
|
+
| `ResponsiveChart`, `ResponsiveChartProps` | `CartesianChart` / `CartesianChartProps` (+ `autoMargin`, `PlotContext`, `PlotRect`) | now mandatory for every single-plot cartesian chart, enforced by `basalt/hand-rolled-plot` |
|
|
225
|
+
| `ChartTooltip` (tip-based), `useChartTooltip`, `useTooltipStyles` | `ChartTooltipFloat` + the `tooltip: CartesianTooltipConfig` prop | portal / flip / clamp done once |
|
|
226
|
+
| `BarsAxisConfig` | `AxisConfig` + `resolveAxisDomain` | |
|
|
227
|
+
| `ZonedLineTooltipLabel` | `tooltip` config on the kind | |
|
|
228
|
+
| `ZonedLine`/`MultiLine`: `yDomain`, `yAutoPad`, `yAutoMaxFloor`, `yAutoMinCeil`, `numTicksY`, `formatYTick` | one `y?: AxisConfig<T>` | `y` is **optional** where `yDomain` was required |
|
|
229
|
+
| `ZonedLine`/`MultiLine`: `tooltipLabel`, `renderExtraTooltipRows` | `tooltip.label`, `tooltip.extraRows` (`CartesianTooltipConfig`) | there is no `appendRows`; the field is `extraRows` |
|
|
230
|
+
| `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 |
|
|
231
|
+
| `ZonedLine`/`MultiLine`: `numTicksX` | `xTicks` | `xZones` added alongside |
|
|
232
|
+
| `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 |
|
|
233
|
+
| `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` |
|
|
234
|
+
| `Heatmap.width` | measures itself; takes `height` / `aspectRatio` / `fill` | |
|
|
147
235
|
|
|
148
236
|
## 1.12.0 — agent-chat, behaviour only
|
|
149
237
|
|
|
@@ -176,6 +264,12 @@ Names that have no 1.x equivalent: the `--chart-blue-1..8` sequential ramp (1.x
|
|
|
176
264
|
`--vx-fill-*` only), the default font stacks as a token (they live in `styles.css`), `purple` as a
|
|
177
265
|
text color, `black`, `blue-400`/`green-400`.
|
|
178
266
|
|
|
267
|
+
**The typefaces changed at 1.0, not just their delivery.** 0.4.2 shipped Instrument Sans; 1.x ships
|
|
268
|
+
Nunito Sans (body) + Hubot Sans (condensed headings), mono unchanged at JetBrains Mono. `fonts:css`
|
|
269
|
+
emits the 1.x stacks — for a 0.4.2 migrant that is a **rebrand**, not a restoration. It also emits
|
|
270
|
+
`--basalt-font-head-stretch: 88%`, tuned for Hubot Sans specifically; pointed at another face it
|
|
271
|
+
silently condenses it.
|
|
272
|
+
|
|
179
273
|
## 1.0.1 — CLI binary renamed
|
|
180
274
|
|
|
181
275
|
`basalt` → **`basalt-ui`**. Never `bunx basalt`.
|
|
@@ -191,18 +285,18 @@ live in `configs/oxlint.json`, and since 1.20.0 their grace is tracked by `PLUGI
|
|
|
191
285
|
named export beside the plugin — a test asserts it against the shipped preset in both directions, so
|
|
192
286
|
deleting an entry forces the level flip in the same commit.
|
|
193
287
|
|
|
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.20.
|
|
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` | —
|
|
288
|
+
| Rule | Landed | Became `error` |
|
|
289
|
+
| ---------------------------------- | ---------------- | ------------------------------------------------------------------------ |
|
|
290
|
+
| `mantine-shade-index` (guard kind) | 1.7.0 as `warn` | **1.11.0** |
|
|
291
|
+
| `basalt/raw-scroll-container` | ≤1.2.0 as `off` | `warn` 1.10.0 → **`error` 1.13.0** |
|
|
292
|
+
| `basalt/ai-sdk-major` | 1.10.0 as `warn` | **1.13.0** |
|
|
293
|
+
| `basalt/agent-no-raw-usechat` | 1.10.0 as `warn` | **1.13.0** |
|
|
294
|
+
| `basalt/agent-resume-guard` | 1.10.0 as `warn` | **1.13.0** |
|
|
295
|
+
| `basalt/raw-size-literal` | 1.7.0 as `warn` | **1.20.0** |
|
|
296
|
+
| `basalt/hand-rolled-plot` | 1.15.0 as `warn` | still `warn` — widened again at 1.20.1; grace restarts with the widening |
|
|
297
|
+
| `basalt/chart-legend-literal` | 1.15.0 as `warn` | still `warn` — widened at 1.20.0 |
|
|
298
|
+
| `basalt/shadow-basalt-export` | 1.20.0 as `warn` | may stay `warn` permanently — widened at 1.20.1 |
|
|
299
|
+
| `basalt/hand-rolled-shell` | 1.20.0 as `warn` | — |
|
|
206
300
|
|
|
207
301
|
`card-with-border`, `inline-display`, `raw-html-layout`, `raw-form-control`, `raw-font-family` and
|
|
208
302
|
the other original guard kinds have been `error` since before 1.2.0 — they never had a grace minor.
|
|
@@ -214,7 +308,7 @@ in the shipped preset). A config still naming `import-boundary` after 1.1.0 disa
|
|
|
214
308
|
|
|
215
309
|
## Deprecated, not yet removed
|
|
216
310
|
|
|
217
|
-
The 32 camelCase `--vx-*` aliases deprecated in 1.5.0 are **still emitted at 1.20.
|
|
311
|
+
The 32 camelCase `--vx-*` aliases deprecated in 1.5.0 are **still emitted at 1.20.1**.
|
|
218
312
|
`buildPaletteCss({ legacyAliases: false })` / `tokens:css --no-legacy-aliases` opts out now; a later
|
|
219
313
|
minor flips the default.
|
|
220
314
|
|
|
@@ -223,6 +317,10 @@ minor flips the default.
|
|
|
223
317
|
- Props declared inline on a component (`function X({ a }: { a: string })`) rather than on an
|
|
224
318
|
exported type are outside the diff method used here. Spot checks on the shell and charts modules
|
|
225
319
|
found none, but the negative is unproven.
|
|
226
|
-
-
|
|
227
|
-
commit
|
|
228
|
-
|
|
320
|
+
- The 0.4.2 `./css` line counts in the 1.0.0 table (877 lines / 307 custom props / 19 utilities) come
|
|
321
|
+
from the removal commit, not from a re-read of the 0.4.2 tarball.
|
|
322
|
+
|
|
323
|
+
`AppBreadcrumbs.parentAnchor` was listed here through 1.20.0 and is now verified: it is declared on
|
|
324
|
+
the shipped `dist/shell/app-breadcrumbs.d.ts` (`parentAnchor?: NavAnchor`, with `parentHref` as the
|
|
325
|
+
no-router fallback). The type is inline on the component rather than an exported `AppBreadcrumbsProps`,
|
|
326
|
+
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.20.1 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,26 @@ 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.20.1 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.20.1 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 guard with that one waiver neutralized. Exits 1
|
|
264
|
+
on a dead waiver, so it is usable as a CI gate.
|
|
231
265
|
|
|
232
266
|
`doctor`'s `ai-major-parity` hard check fails a monorepo where workspace packages declare different
|
|
233
267
|
`ai` package majors — unless the split is intentional and written down. A producer pinned to an
|
|
@@ -96,6 +96,25 @@ refuses on a commented config rather than deleting the comments.
|
|
|
96
96
|
Run `basalt-ui doctor` afterwards to confirm the wiring took. A check that cannot RUN is reported as
|
|
97
97
|
`SKIPPED` and exits non-zero on its own — "All checks passed" is only printable when every check ran.
|
|
98
98
|
|
|
99
|
+
### The lefthook preset overrides YOU, not the other way round
|
|
100
|
+
|
|
101
|
+
New in 1.20.1: `doctor`'s `lefthook-preset` check hard-fails when the repo-root lefthook config
|
|
102
|
+
`extends` a path that does not resolve. That state is otherwise invisible — lefthook merges a
|
|
103
|
+
missing target into **zero** commands and exits 0, so `lefthook dump` looks clean and the repo has
|
|
104
|
+
no pre-commit gate with nothing anywhere saying so. It warns on a lefthook.yml wiring its own hooks,
|
|
105
|
+
and is n/a with no lefthook at all. `sync` reports the same seam, since it is the command an upgrade
|
|
106
|
+
actually runs and neither file is one basalt owns.
|
|
107
|
+
|
|
108
|
+
**An `extends` target WINS on a colliding key.** Declare `pre-commit.commands.oxfmt.run` (or
|
|
109
|
+
`glob:`) in your own file and the preset's value is what runs — yours is discarded silently. Only
|
|
110
|
+
keys the preset does not define merge in. So `env:`, `exclude:`, `skip:` and a differently-named
|
|
111
|
+
command of your own are yours; `run:` and `glob:` on a shipped command are not. The sanctioned seam
|
|
112
|
+
is therefore `BASALT_BIN`: the guard job runs `${BASALT_BIN:-bunx --no-install basalt-ui}`, and the
|
|
113
|
+
shell default is the whole reason the seam exists at all. `init` renders it with the resolved local
|
|
114
|
+
bin. `--no-install` is deliberate — a bare `bunx basalt-ui` in a repo where basalt is not resolvable
|
|
115
|
+
from the root silently downloads a different copy from npm and gates the commit with a version
|
|
116
|
+
nobody pinned.
|
|
117
|
+
|
|
99
118
|
## `index.html` and `public/` are scanned
|
|
100
119
|
|
|
101
120
|
`check-theme` resolves `.html` / `.webmanifest` / `.json` as markup (colour kinds only), and each
|
|
@@ -133,6 +152,28 @@ they track the palette automatically. It also injects the `apple-mobile-web-app-
|
|
|
133
152
|
`viewport-fit=cover` (skipped when the consumer's own `index.html` already declares one), and
|
|
134
153
|
site-wide OG/Twitter defaults from `options.seo`.
|
|
135
154
|
|
|
155
|
+
**Set `colorScheme` to whatever the app passes as `defaultColorScheme`** — `'dark'` (the default),
|
|
156
|
+
`'light'`, `'auto'` (declares `color-scheme: light dark` and picks the pre-boot background off
|
|
157
|
+
`prefers-color-scheme`), or `false` (paint the background, declare no `color-scheme`).
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
plugins: [react(), ...basaltAppPlugin({ name: 'Argo', colorScheme: 'auto' })]
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Before 1.20.1 the anti-FOUC rule emitted `html{color-scheme:dark}` **unlayered**, which outranks
|
|
164
|
+
Mantine's own `@layer mantine` declaration regardless of specificity — a light-scheme consumer got
|
|
165
|
+
dark scrollbars, dark `<select>` popups and dark date pickers permanently, with no opt-out. The rule
|
|
166
|
+
is now scoped to `html:not([data-mantine-color-scheme])`, so it expires the instant MantineProvider
|
|
167
|
+
resolves the real scheme. A mismatched `colorScheme` is therefore a flash of the wrong surface, not
|
|
168
|
+
a stuck one.
|
|
169
|
+
|
|
170
|
+
**The plugin hoists your `<meta charset>`.** Every injected tag defaults to `head-prepend`, which
|
|
171
|
+
landed ~20 tags ahead of the consumer's own encoding declaration — measured at byte 1653 on a
|
|
172
|
+
realistic shell, past the 1024-byte window the HTML spec gives it, with no consumer-side fix
|
|
173
|
+
available. The plugin now re-emits the declaration as its own first head-prepend tag (46 bytes after
|
|
174
|
+
the fix) and runs `order: 'pre'` so nothing else can land ahead of it. The legacy `http-equiv` form
|
|
175
|
+
is detected and left verbatim.
|
|
176
|
+
|
|
136
177
|
Icons are **bring-your-own** — basalt-ui takes no `sharp`/image-processing dependency. The plugin
|
|
137
178
|
expects these filenames under `public/` (or `icons.dir` if customized):
|
|
138
179
|
|
|
@@ -57,12 +57,14 @@ radial/matrix shape, is not optional — two `basalt` oxlint plugin rules
|
|
|
57
57
|
`AxisRightNumeric`, `AxisBottomDate`, `HoverOverlay`, `Crosshair`) in a file that does not
|
|
58
58
|
compose `CartesianChart`. **Since 1.20.0 every assembly node is reported and waived on its own**
|
|
59
59
|
— one comment no longer grants the whole file permanent immunity, which is how a 604-line chart
|
|
60
|
-
file went unpoliced.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
`DualPanel` carries the repo's only one.
|
|
64
|
-
|
|
65
|
-
|
|
60
|
+
file went unpoliced. To waive ONE node, put `theme-allow hand-rolled-plot — <why>` on it (or on a
|
|
61
|
+
comment-only line above it). A genuinely non-single-plot shape (multi-pane, radial, matrix)
|
|
62
|
+
declares the whole file with `theme-allow-file hand-rolled-plot — two panes over one x scale`,
|
|
63
|
+
anywhere in it; `DualPanel` carries the repo's only one. **`theme-allow-file` is the 1.20.1
|
|
64
|
+
spelling** — at 1.20.0 a node-scoped annotation was silently promoted to a file declaration, so
|
|
65
|
+
per-node scoping was not expressible; move the one word. The file that DEFINES `CartesianChart` is
|
|
66
|
+
exempt definitionally (detected by declaration, not by path), since a rule saying "compose X"
|
|
67
|
+
cannot fire inside X.
|
|
66
68
|
- **`basalt/chart-legend-literal`** — a hand-written array literal passed to `ChartLegend`'s
|
|
67
69
|
`items`; **since 1.20.0 also a `.map()` over a non-`series` array**, because deriving from AN
|
|
68
70
|
array is not deriving from THE series. The legend must come from the same `series` the chart
|
|
@@ -93,12 +95,13 @@ ships no map kind.
|
|
|
93
95
|
- **Want `@visx/geo` anyway?** Install it yourself — basalt doesn't pin it — and keep it under a
|
|
94
96
|
`charts/` segment like every other visx import, same as any bespoke chart.
|
|
95
97
|
- **Style map chrome with `--vx-*` tokens** (`VX.*`/`alpha()`) so it matches the rest of the app.
|
|
96
|
-
- **The guard trap:** `check-theme`'s `inline-spacing` kind is a per-line regex
|
|
97
|
-
an AST pass
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
- **The guard trap, narrowed at 1.20.1:** `check-theme`'s `inline-spacing` kind is a per-line regex
|
|
99
|
+
over raw source, not an AST pass. A **unitless** number now only reports inside a style-object
|
|
100
|
+
context (a `style=`/`sx=`/`css=` attribute, or a const whose name or type says styles), so a map's
|
|
101
|
+
pixel geometry — `fitBounds({ padding: 48 })`, marker offsets — no longer trips it: an options bag
|
|
102
|
+
is not CSS. A value carrying a unit is CSS wherever it was written and still reports. Where it
|
|
103
|
+
does fire on genuine map chrome, scope a `theme-allow inline-spacing — map geometry` comment to
|
|
104
|
+
it; that's the sanctioned answer, not a bug to file.
|
|
102
105
|
|
|
103
106
|
## Every chart has
|
|
104
107
|
|
|
@@ -208,6 +208,10 @@ axes AND together — matching the existing multi-facet `filterFn` semantics in
|
|
|
208
208
|
|
|
209
209
|
**Filter state is URL state.** Single-select axes (category) use `createSearchParamStore`;
|
|
210
210
|
multi-select axes (tags) use `createMultiSearchParamStore` — both from `basalt-ui/router-tanstack`.
|
|
211
|
+
That holds for the enum-shaped axes an article filter has. **It is not a general URL-state mandate**:
|
|
212
|
+
a route whose `validateSearch` is a real Zod object is hand-written, and `createSearchSchemaStore`
|
|
213
|
+
is planned, not shipped — see `basalt-router.md` → "validateSearch".
|
|
214
|
+
|
|
211
215
|
Two stores compose on one route by spreading both `validateSearch` results:
|
|
212
216
|
|
|
213
217
|
```ts
|