basalt-ui 1.23.0 → 1.24.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 +31 -17
- package/CHANGELOG.md +23 -0
- package/MIGRATING.md +222 -6
- package/README.md +159 -49
- package/agent/rules/basalt-app.md +8 -2
- package/agent/rules/basalt-charts.md +38 -2
- package/agent/rules/basalt-content.md +3 -3
- package/agent/rules/basalt-data.md +45 -13
- package/agent/rules/basalt-mantine.md +4 -4
- package/agent/rules/basalt-query.md +57 -0
- package/agent/rules/basalt-tokens.md +18 -5
- package/agent/skills/basalt-app/SKILL.md +32 -12
- package/agent/skills/basalt-charts/SKILL.md +4 -2
- package/agent/templates/CLAUDE-block.md.tpl +3 -2
- package/dist/charts/kinds/BandStrip.d.ts +20 -6
- package/dist/charts/kinds/BandStrip.d.ts.map +1 -1
- package/dist/charts/kinds/BandStrip.js +59 -6
- package/dist/charts/kinds/MirroredBars.d.ts +4 -2
- package/dist/charts/kinds/MirroredBars.d.ts.map +1 -1
- package/dist/charts/kinds/MirroredBars.js +13 -0
- package/dist/cli/index.d.ts +4 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +269 -27
- package/dist/dashboard/empty-state.d.ts +6 -2
- package/dist/dashboard/empty-state.d.ts.map +1 -1
- package/dist/dashboard/empty-state.js +1 -1
- package/dist/dashboard/index.d.ts +1 -0
- package/dist/dashboard/index.d.ts.map +1 -1
- package/dist/dashboard/index.js +8 -0
- package/dist/dashboard/query-state.d.ts +96 -0
- package/dist/dashboard/query-state.d.ts.map +1 -0
- package/dist/dashboard/query-state.js +123 -0
- package/dist/dashboard/query-state.test.d.ts +2 -0
- package/dist/dashboard/query-state.test.d.ts.map +1 -0
- package/dist/data/data-table.d.ts +61 -4
- package/dist/data/data-table.d.ts.map +1 -1
- package/dist/data/data-table.js +41 -4
- package/dist/data/data-table.test.d.ts +2 -0
- package/dist/data/data-table.test.d.ts.map +1 -0
- package/dist/data/table.d.ts +1 -1
- package/dist/data/table.d.ts.map +1 -1
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +50 -9
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/query/error-message.d.ts +41 -0
- package/dist/query/error-message.d.ts.map +1 -0
- package/dist/query/error-message.js +30 -0
- package/dist/query/error-message.test.d.ts +2 -0
- package/dist/query/error-message.test.d.ts.map +1 -0
- package/dist/query/index.d.ts +1 -19
- package/dist/query/index.d.ts.map +1 -1
- package/dist/query/index.js +2 -14
- package/dist/surfaces.d.ts +1 -1
- package/dist/surfaces.js +1 -1
- package/llms.txt +3 -3
- package/package.json +1 -1
- package/src/charts/kinds/BandStrip.test.tsx +87 -5
- package/src/charts/kinds/BandStrip.tsx +105 -12
- package/src/charts/kinds/MirroredBars.test.tsx +39 -0
- package/src/charts/kinds/MirroredBars.tsx +38 -2
- package/src/cli/check-theme.test.ts +34 -0
- package/src/cli/help.test.ts +74 -1
- package/src/cli/index.ts +496 -41
- package/src/cli/tokens-css.test.ts +129 -0
- package/src/cli/toolchain-wiring.test.ts +146 -0
- package/src/dashboard/empty-state.tsx +9 -3
- package/src/dashboard/index.ts +11 -0
- package/src/dashboard/query-state.test.tsx +152 -0
- package/src/dashboard/query-state.tsx +272 -0
- package/src/data/data-table.test.tsx +158 -0
- package/src/data/data-table.tsx +123 -5
- package/src/data/table.ts +6 -1
- package/src/guard/check-source.test.ts +214 -0
- package/src/guard/index.ts +169 -15
- package/src/index.ts +9 -0
- package/src/query/error-message.test.ts +67 -0
- package/src/query/error-message.ts +70 -0
- package/src/query/index.ts +2 -34
- package/src/surfaces.ts +1 -1
package/AGENTS.md
CHANGED
|
@@ -8,7 +8,7 @@ Toolchain: Bun runtime, oxlint + oxfmt, conventional commits (empty scope), `mas
|
|
|
8
8
|
|
|
9
9
|
| Subpath | Layer | Purpose |
|
|
10
10
|
| --------------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
11
|
-
| `basalt-ui` | mantine-coupled | BasaltProvider, createBasaltTheme, BasaltShell + sidebar/mobile-nav/breadcrumbs, NavCountBadge, ThemeToggle, ThreadWorkspace + thread-chat components, dashboard composites (DeltaBadge, StatCard with threshold tone, EmptyState, SettingsSection/SettingsRow/DangerZone)
|
|
11
|
+
| `basalt-ui` | mantine-coupled | BasaltProvider, createBasaltTheme, BasaltShell + sidebar/mobile-nav/breadcrumbs, NavCountBadge, ThemeToggle, ThreadWorkspace + thread-chat components, dashboard composites (DeltaBadge, StatCard with threshold tone, EmptyState, QueryState/LoadingState/ErrorState, SettingsSection/SettingsRow/DangerZone) |
|
|
12
12
|
| `basalt-ui/charts` | headless | CartesianChart + visx chart primitives, kinds, sparklines, hooks, and token re-exports (Mantine-free) |
|
|
13
13
|
| `basalt-ui/tokens` | headless | VX token refs, buildPaletteCss, defineSeries, seriesTokens, groupTokens, alpha (Mantine-free) |
|
|
14
14
|
| `basalt-ui/theme-lab` | mantine-coupled | ThemeLabControls, applyOverrides, COLOR_GROUPS — a low-level inspector for the non-derived structural tokens; identity/color tuning lives in DeriveControls |
|
|
@@ -62,29 +62,43 @@ duration/spring/ease literal in a `transition={{...}}` prop (enforced by `basalt
|
|
|
62
62
|
|
|
63
63
|
## Key commands
|
|
64
64
|
|
|
65
|
+
`init` and `tokens:css`/`fonts:css` are the only commands with no install to resolve, so `bunx` is
|
|
66
|
+
correct for them **and only while basalt is not installed here** — once it is, they run through the
|
|
67
|
+
local bin like everything else. See the note below.
|
|
68
|
+
|
|
65
69
|
```bash
|
|
66
|
-
bunx basalt-ui init
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
bunx basalt-ui
|
|
73
|
-
bunx basalt-ui
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
bunx basalt-ui init # FIRST STEP: scaffold .claude/rules/ + .claude/skills/, managed CLAUDE.md block, DESIGN.md seed, toolchain seeds
|
|
71
|
+
basalt-ui --version # one bare line, exit 0 — which CLI is actually running (also -v / version)
|
|
72
|
+
basalt-ui check-theme # fail on off-palette colors in consumer source
|
|
73
|
+
basalt-ui sync # reconcile managed files (.claude/rules/, .claude/skills/, CLAUDE.md block)
|
|
74
|
+
basalt-ui sync --check # CI freshness gate — exits non-zero if any managed file drifted
|
|
75
|
+
basalt-ui doctor # integration health — SKIPPED is a third outcome and exits non-zero
|
|
76
|
+
bunx basalt-ui tokens:css # emit the --vx-* stylesheet (no React, no Mantine, no bundler) — bunx ONLY if basalt is not installed
|
|
77
|
+
bunx basalt-ui fonts:css # emit the shipped --basalt-font-* stacks — same exception
|
|
78
|
+
basalt-ui info --json # stable JSON surface map
|
|
79
|
+
basalt-ui guard-hook # PreToolUse theme-guard adapter: reads a Write/Edit payload on stdin, denies off-palette writes
|
|
76
80
|
```
|
|
77
81
|
|
|
82
|
+
Every subcommand rejects a flag it does not accept, exiting 1 and naming it; an unknown command says
|
|
83
|
+
so above the usage block. Neither is ever ignored — `doctor --json` used to run doctor and exit 0.
|
|
84
|
+
|
|
78
85
|
`check-theme`, `doctor` and `sync` resolve their project identically: `BASALT_CWD` wins, then the
|
|
79
86
|
cwd, then the workspace packages the root declares, then a two-level layout scan (which is what a
|
|
80
|
-
repo with no `workspaces` field and the install one level down needs)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
87
|
+
repo with no `workspaces` field and the install one level down needs), then an **ascend** to the
|
|
88
|
+
nearest ancestor carrying a basalt project, bounded by the repo root. Both directions announce the
|
|
89
|
+
relocation in one sentence — `check-theme` used to fabricate `roots: ["src"]` on the ascend path and
|
|
90
|
+
pass silently. Two candidates is ambiguous and reported, never guessed. `sync` on a `tokens-only`
|
|
91
|
+
consumer prints `n/a` and exits 0; from a sub-package it refreshes the parent install rather than
|
|
92
|
+
refusing, and still will not scaffold a second consumer.
|
|
93
|
+
|
|
94
|
+
**Run the local bin, not `bunx`.** `bunx` does not re-resolve a cached package, so it can execute
|
|
95
|
+
the version you just upgraded away from — one consumer filed a P0 against a 1.20.0 cache while
|
|
96
|
+
believing it was on 1.22.0. `init` and `sync` render every seeded script, CI step and hook with the
|
|
97
|
+
resolved local bin for this reason (`BASALT_BIN` overrides it). `basalt-ui --version` is the
|
|
98
|
+
one-second check.
|
|
85
99
|
|
|
86
100
|
Register `guard-hook` in `.claude/settings.json` under `hooks.PreToolUse` with matcher
|
|
87
|
-
`Write|Edit|MultiEdit` and command
|
|
101
|
+
`Write|Edit|MultiEdit` and command `./node_modules/.bin/basalt-ui guard-hook` (what `init` seeds) so the agent can't write off-palette
|
|
88
102
|
colors without a `theme-allow` comment.
|
|
89
103
|
|
|
90
104
|
**The escape hatch is `theme-allow <rule-id> — <reason>`**, scoped to that one kind and to that one
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
# [1.24.0](https://github.com/jkrumm/basalt-ui/compare/v1.23.1...v1.24.0) (2026-08-22)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* stop check-theme fabricating a config, and re-gate the provenance line ([99a45c0](https://github.com/jkrumm/basalt-ui/commit/99a45c0dc284850b4140a6ff7719dbade4795f09))
|
|
7
|
+
* stop raw-hex reading HTML entities, and teach the guard the SFC dialect ([f1b0562](https://github.com/jkrumm/basalt-ui/commit/f1b0562efeb718921509253f9ee215bf461c3c01)), closes [#123](https://github.com/jkrumm/basalt-ui/issues/123) [#125](https://github.com/jkrumm/basalt-ui/issues/125)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* playground routes for QueryState and the table body chrome ([e150a00](https://github.com/jkrumm/basalt-ui/commit/e150a0051bf743137751a3d5c17f202c0984aaa7))
|
|
13
|
+
* ship the query loading/error branch and the table body chrome ([963bf04](https://github.com/jkrumm/basalt-ui/commit/963bf047cf9ac4fac3cecceecbd0c378ee37f79d))
|
|
14
|
+
|
|
15
|
+
## [1.23.1](https://github.com/jkrumm/basalt-ui/compare/v1.23.0...v1.23.1) (2026-08-22)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* gate the two chart tag rules on where the tag came from ([0075224](https://github.com/jkrumm/basalt-ui/commit/007522416bf9a3269e5f45b7ae7f45203434bae7))
|
|
21
|
+
* make the CLI answer which version ran, and stop it failing open ([0edf50e](https://github.com/jkrumm/basalt-ui/commit/0edf50e1d6f61b349f989fd10fe384d4a91a9af1))
|
|
22
|
+
* stop a typo'd band state from asserting absence ([6c1515c](https://github.com/jkrumm/basalt-ui/commit/6c1515c3ecbf019d9971e5b247e81e5cce3ae316))
|
|
23
|
+
|
|
1
24
|
# [1.23.0](https://github.com/jkrumm/basalt-ui/compare/v1.22.0...v1.23.0) (2026-08-22)
|
|
2
25
|
|
|
3
26
|
|
package/MIGRATING.md
CHANGED
|
@@ -8,9 +8,10 @@ renamed exports per minor, with the replacement.
|
|
|
8
8
|
Reconstructed from `git diff` over the published export surface across `v1.0.0..v1.19.1`, then
|
|
9
9
|
cross-checked against the repo's `scripts/export-surface.json` snapshot. **Every replacement below
|
|
10
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.
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
row that was wrong. That pass corrected 4 table rows and 3 prose claims. Every section from 1.21.0
|
|
12
|
+
on was written against source, not against its commit messages — which is not a formality: one
|
|
13
|
+
1.23.0 commit message describes a change it did not make (see `1.23.1` § Corrections). Check
|
|
14
|
+
the types, not this table, if the two disagree.
|
|
14
15
|
|
|
15
16
|
**The newest section is headed `## Unreleased`, and stays that way until npm serves it.** This file
|
|
16
17
|
is written before `semantic-release` picks the number, so a number written here is a guess — and it
|
|
@@ -39,6 +40,219 @@ version `CHANGELOG.md` does not record. This file is not in that set; the same d
|
|
|
39
40
|
by convention. Rename the section at release, or leave it — a reader can resolve `Unreleased`
|
|
40
41
|
against `CHANGELOG.md`, and could never resolve `1.21.1`.
|
|
41
42
|
|
|
43
|
+
**Nothing removed or renamed. Twelve new runtime exports on `.`/`./query`, plus table props.** Every
|
|
44
|
+
API entry is additive; the behaviour changes are in the guard and the CLI, listed after them.
|
|
45
|
+
|
|
46
|
+
### `QueryState` — the branch precedence, shipped as a component
|
|
47
|
+
|
|
48
|
+
`QueryState`, `LoadingState`, `ErrorState` (+ `QueryStateProps`, `QueryStateLike`,
|
|
49
|
+
`QueryStateVariant`, `QueryEmptyCopy`, `LoadingStateProps`, `ErrorStateProps`) on the root barrel,
|
|
50
|
+
beside `EmptyState`. `toErrorMessage` and `errorStatus` on `basalt-ui/query`.
|
|
51
|
+
|
|
52
|
+
basalt owned both ends of this file — `EmptyState` and `toErrorMessage` — and nothing in between, so
|
|
53
|
+
a consumer wrote the four-way switch and got it wrong in the direction the shape suggested:
|
|
54
|
+
image-share's library rendered `No images` on a **500** and a share detail rendered
|
|
55
|
+
`Share not found` on a dropped connection, until 204 hand-rolled lines stopped it. Open since round
|
|
56
|
+
4, re-reported in round 6.
|
|
57
|
+
|
|
58
|
+
- **A component, not a hook** — the product IS the precedence, and a hook returns the same four-way
|
|
59
|
+
switch to every call site.
|
|
60
|
+
- **It lives under `src/dashboard/`, not `src/query/`**: `check-dist-layering.mjs` asserts
|
|
61
|
+
`dist/query/index.js` reaches no `@mantine/*`, and this renders Mantine. `query` is typed as a
|
|
62
|
+
five-field structural subset (`QueryStateLike`), so basalt couples to no query-library version and
|
|
63
|
+
a hand-composed object is legal. The subset gives up the compiler, so the shape is asserted at
|
|
64
|
+
**runtime**: a missing `isError` throws naming the field, because a missing `isError` is precisely
|
|
65
|
+
the "500 renders _No images_" bug.
|
|
66
|
+
- `errorTitle` / `errorFallback` / `errorAction` apply to the no-cached-data error branch only; the
|
|
67
|
+
error-with-cached-data branch renders a fixed section banner above `children`.
|
|
68
|
+
- **`EmptyState.description` is now optional.** Five argo features wrapped the component solely
|
|
69
|
+
because a compact panel had to invent a second sentence. Existing calls are unaffected.
|
|
70
|
+
- `toErrorMessage(err, fallback?)` and `errorStatus(err)` had two live bugs the port found: an
|
|
71
|
+
opaque envelope rendered the literal `"{}"`, and `toErrorMessage(undefined)` returned the
|
|
72
|
+
`undefined` VALUE despite a `string` return type. Both fixed; a status is folded into the fallback
|
|
73
|
+
when the body decodes to nothing readable. Split into `src/query/error-message.ts` so the
|
|
74
|
+
dashboard decodes without importing the peer.
|
|
75
|
+
|
|
76
|
+
**Port result:** image-share, all 10 call sites plus a standalone `ErrorState`, **by changing one
|
|
77
|
+
import line each** — no renames, no prop changes, no casts. Total 2467 → 2221; code-only 2056 →
|
|
78
|
+
1882, **−174**; `query-state.tsx` 204 → 0.
|
|
79
|
+
|
|
80
|
+
### `BasaltDataTable` body chrome — and the honest number
|
|
81
|
+
|
|
82
|
+
New: `maxHeight`, `minWidth`, `stickyHeader`, `stickyHeaderOffset`, `verticalSpacing`,
|
|
83
|
+
`horizontalSpacing`, `withRowBorders`, `withTableBorder`, per-column `meta.align` and
|
|
84
|
+
`meta.numeral`; `striped` widens from `boolean` to `boolean | 'odd' | 'even'`. `withTableBorder`
|
|
85
|
+
defaults to `true` in basalt, overriding Mantine's `false`; every other one is a conditional
|
|
86
|
+
pass-through, so omitting it keeps Mantine's default.
|
|
87
|
+
|
|
88
|
+
**Porting argo's three tables onto them made them 341 → 370 lines. 29 LONGER.** argo named these
|
|
89
|
+
props as the reason the tables stayed hand-rolled, and adding them shortened nothing: column defs
|
|
90
|
+
cost more than JSX rows when every cell is bespoke — eight accessor blocks at 4–6 lines each against
|
|
91
|
+
an eight-`<Table.Td>` row at ~3. **The ask was mis-specified.** Adopt them for what they buy —
|
|
92
|
+
the `type="native"` footgun, alignment stated once instead of on both `th` and `td` six times in one
|
|
93
|
+
file, and sorting/filtering/pagination no longer consumer-owned — not for a line count. This is the
|
|
94
|
+
counterexample to the band kinds, and the reason the port-before-shipping rule earns its keep.
|
|
95
|
+
|
|
96
|
+
- `maxHeight` (or `minWidth`) renders **`Table.ScrollContainer type="native"`**, and
|
|
97
|
+
`agent/rules/basalt-data.md` now prescribes that same node for a bespoke table — so the blessed
|
|
98
|
+
lane and the escape cannot contradict, and `type="scrollarea"`, which breaks a sticky `thead`, is
|
|
99
|
+
unreachable through the props. (The prop's JSDoc claimed the docs already sanctioned it; they had
|
|
100
|
+
never named `Table.ScrollContainer` at all.)
|
|
101
|
+
- `meta.align` is a `ColumnMeta` module augmentation: a typo'd key is a tsc error, a wrong value
|
|
102
|
+
throws naming the column. `meta.numeral` is read only as `!== false` — an opt-OUT of the
|
|
103
|
+
mono-numeral cell style, never an opt-in.
|
|
104
|
+
- **Not shipped, known:** `emptyState` renders inside a `<td colSpan>` so the header row survives an
|
|
105
|
+
empty table — there is no `emptyState="replace"` mode. No per-column `enableSorting` of basalt's
|
|
106
|
+
own; TanStack's `ColumnDef.enableSorting` still reaches `getCanSort()` and works.
|
|
107
|
+
|
|
108
|
+
### Four false greens
|
|
109
|
+
|
|
110
|
+
1. **`check-theme` fabricated a config on the ascend path and passed silently.** From a package with
|
|
111
|
+
no `basalt` key it invented `roots: ["src"]` and reported the invention back under the name
|
|
112
|
+
`basalt.roots`. In `basalt-ui-obsidian`, run from `apps/demo`, that scanned **22 of 44** guarded
|
|
113
|
+
files and made `--audit-allows` report **0 live waivers in a repo carrying 1** — exit 0, no note.
|
|
114
|
+
The audit exists so `0 dead` cannot read as `0 dead anywhere`, and it could be made to say zero
|
|
115
|
+
by standing in the wrong directory. `resolveProjectDir` now ascends to the nearest ancestor
|
|
116
|
+
carrying a basalt project, bounded by the repo root, and announces it in the sentence descend
|
|
117
|
+
already used. **No `.git` above cwd means no ascend**, so a standalone consumer keeps the
|
|
118
|
+
built-in defaults exactly as before. After: 44 files, the real 1.
|
|
119
|
+
2. **`tokens:css --check` stopped verifying the regeneration command.** 1.23.1 blanked all of line 2
|
|
120
|
+
so a version bump would stop forcing a no-op commit — but line 2 also carries the exact
|
|
121
|
+
invocation line 1 tells the reader to regenerate with. Rewriting `--only core` to `--only all` in
|
|
122
|
+
that line **passed clean**. Only the version token is neutralized now; a line that does not parse
|
|
123
|
+
as a provenance line is compared verbatim, so a deleted or reworded header fails. The success
|
|
124
|
+
message also **parses** the versions instead of asserting them — it used to claim the file "still
|
|
125
|
+
names an older basalt-ui" without reading it, so `0.0.1-nonsense` earned the same sentence.
|
|
126
|
+
3. **`doctor`'s icons check was unreachable from the only directory where `doctor` exits 0.** On a
|
|
127
|
+
monorepo the root run omitted it with no `⊘ SKIPPED` line — the exact failure mode `SKIPPED` was
|
|
128
|
+
introduced to eliminate — while the app-package run failed on artefacts of standing in a
|
|
129
|
+
non-install package. It resolves the app package off `basalt.roots` now. No `basaltAppPlugin(`
|
|
130
|
+
anywhere and no `public/` is a pass that says so; a plugin call with no `public/` beside it is a
|
|
131
|
+
`⊘ SKIPPED`, which exits non-zero on its own.
|
|
132
|
+
4. **`SCANNABLE_EXT` gained `.astro`, `.jsx` and `.vue`.** rollhook's marketing site is Astro and its
|
|
133
|
+
two `.astro` templates are its entire markup layer — unguarded, while `check-theme` reported a
|
|
134
|
+
clean 4-file scan. It scans 6 now.
|
|
135
|
+
|
|
136
|
+
**Behaviour change to name: `sync` ascends too.** It shares the resolver, so from a sub-package it
|
|
137
|
+
relocates to the parent install and refreshes it — announced — rather than refusing. It still cannot
|
|
138
|
+
scaffold a second consumer: the refusal is keyed on the RESOLVED project and runs before the
|
|
139
|
+
`basalt.roots` backfill.
|
|
140
|
+
|
|
141
|
+
### Guard changes the widening exposed
|
|
142
|
+
|
|
143
|
+
- **`raw-hex` no longer matches inside an HTML numeric character reference.** `{` — the escaped
|
|
144
|
+
brace a template writes to show a literal `${…}` in prose — read as the hex colour `#123`. The
|
|
145
|
+
hole was in the KIND, not the extension: the same string produced the same findings in `.html`,
|
|
146
|
+
`.tsx`, `.css` and `.vue`, so `.astro` only walked into it first. The fix is precise, not blanket:
|
|
147
|
+
`HEX` rejects a full reference (`&#`, digits, `;`), so **`color: red&#fff` still flags** and
|
|
148
|
+
nothing is exempted by file type. Every neighbouring raw-text kind was checked and structurally
|
|
149
|
+
cannot share the blind spot — a character reference contains no `(`, and the rest anchor on a
|
|
150
|
+
property name, `var(`, or a JSX `=`.
|
|
151
|
+
- **A fourth guard syntax, `sfc`, for `.astro`/`.vue`.** They used to fall through to the `ts`
|
|
152
|
+
dialect, so `<!-- … -->` was never stripped: a `theme-allow` written in an HTML comment waived
|
|
153
|
+
nothing and a colour inside a commented-out block still reported. `sfc` strips both regions —
|
|
154
|
+
markup first, so an HTML comment holding an unterminated `/*` cannot open one that runs to EOF —
|
|
155
|
+
and keeps the **full 25-kind set**. A `markup` classification would have dropped 22 of them: an
|
|
156
|
+
`.astro` template is JSX-shaped and a `.vue` `<script setup>` is real TS. `.jsx` needs no branch.
|
|
157
|
+
Both the scan and `--audit-allows` now share one `stripGuardComments`, so they cannot disagree
|
|
158
|
+
about what a comment is.
|
|
159
|
+
- **Two limits, asserted rather than left ambiguous, both false-negative-only:** `css-raw-surface`
|
|
160
|
+
does not fire inside a `<style>` fence, and stripping is region-blind, so a `<!--` inside a script
|
|
161
|
+
string over-strips.
|
|
162
|
+
- **A known non-fix, deliberate:** an all-hex URL fragment or SVG reference (`href="#cafe"`,
|
|
163
|
+
`fill="url(#abcdef)"`) still reports. It is text-indistinguishable from a colour, so a fix would
|
|
164
|
+
cost real findings; `theme-allow` is the escape.
|
|
165
|
+
- **The widening ships at `error` with no `GRACE_PERIOD_KINDS` entry, and that table cannot express
|
|
166
|
+
it.** The table is keyed per KIND; widening `SCANNABLE_EXT` widens the file set for all 25 at
|
|
167
|
+
once. An entry for `raw-hex` — the kind that actually fired — would demote basalt's most
|
|
168
|
+
load-bearing kind to `warn` across every `.tsx` and `.css` in all seven consumers to buy runway on
|
|
169
|
+
a file type one consumer has. Measured: rollhook's marketing site scans 6 files with 0 findings,
|
|
170
|
+
and no other consumer holds a single `.astro`, `.vue` or `.jsx` file, so grace would have covered
|
|
171
|
+
zero incumbent violations.
|
|
172
|
+
|
|
173
|
+
## 1.23.1 — the band-state throw, the tag gate, a CLI that answers
|
|
174
|
+
|
|
175
|
+
**One type widened, nothing removed or renamed.** `BandStripSeries.formatValue` is now
|
|
176
|
+
`(d: T) => string | null`. `null` renders an em dash — an absent READING — which `''` never could:
|
|
177
|
+
`''` is a state whose label is the whole row. Every existing `(d) => string` still typechecks.
|
|
178
|
+
|
|
179
|
+
**A typo'd `BandSpan.state` no longer renders as absence.** A state naming no `series` entry used to
|
|
180
|
+
be skipped, so the band was simply not drawn — and on a measured/not-measured strip a missing band
|
|
181
|
+
is a claim about the data, not an "unknown". A misspelling asserted a reading nobody took, in a mark
|
|
182
|
+
indistinguishable from a real one.
|
|
183
|
+
|
|
184
|
+
| Key comes off | Dev | Production |
|
|
185
|
+
| ------------------------------------------------------------- | ---------------------------------------- | ------------------------------------------------------------------------ |
|
|
186
|
+
| the DATUM — `BandSpan.state`, `marker.state` | throws, naming the key and the valid set | a dashed neutral outline band + an `Unknown state` / `<key>` tooltip row |
|
|
187
|
+
| a PROP — `absentState`, `MirroredBars`' `up.key` / `down.key` | throws | throws |
|
|
188
|
+
|
|
189
|
+
The split is the whole design. `state` comes off data, so a feed that grows a state basalt has never
|
|
190
|
+
seen must degrade rather than take a dashboard down — while a typo, which is the same input, still
|
|
191
|
+
fails loudly wherever it is being written. The dashed neutral outline belongs to no legend entry and
|
|
192
|
+
no state fill, so it cannot read as data. Pane keys are props, never data-driven, so `assertPaneKey`
|
|
193
|
+
throws in every environment: an unresolvable `up.key` used to hide the pane AND its axis, which
|
|
194
|
+
reads as "that half measured zero" — the one thing `MirroredBars` exists to keep apart from absence.
|
|
195
|
+
|
|
196
|
+
**`chart-missing-aria-label` and `unframed-chart` are gated on where the tag came from.** Both key
|
|
197
|
+
on a JSX tag NAME, so a consumer's own 235-line `MirroredBars` — sharing nothing with the shipped
|
|
198
|
+
kind but the name — was told to pass an `ariaLabel` prop it does not accept, by a rule that presents
|
|
199
|
+
as a correctness finding. A tag is now skipped only when the file DEFINES a component of that name
|
|
200
|
+
and does not also import it from `basalt-ui`. Deliberately one-directional: a tag imported from
|
|
201
|
+
basalt-ui, one imported from a consumer barrel that re-exports it, and one the scan cannot attribute
|
|
202
|
+
at all still all fire. Verified old-vs-new over **945 source files across six repos: 0 findings
|
|
203
|
+
lost, 0 gained.**
|
|
204
|
+
|
|
205
|
+
**CLI: `--version` exists, and no subcommand fails open on a flag.** `--version` / `-v` / `version`
|
|
206
|
+
print one bare greppable line and exit 0, resolved BEFORE dispatch so it can never run a command to
|
|
207
|
+
answer "which basalt-ui is this". Six consumer reports reached for it in one round; all six fell
|
|
208
|
+
back to `info --json`.
|
|
209
|
+
|
|
210
|
+
The larger fix sits underneath it. **Every subcommand validates its flags** and exits 1 naming the
|
|
211
|
+
one it does not accept — `doctor --json` used to run doctor and exit **0**, and `check-theme
|
|
212
|
+
--audit-allow` scanned and reported success. An unknown COMMAND now says so above the usage block
|
|
213
|
+
instead of dumping help and letting the dump read like a choice.
|
|
214
|
+
|
|
215
|
+
- **`doctor` reads `basaltAppPlugin({ icons })`** out of the consumer's vite config instead of
|
|
216
|
+
hardcoding six filenames, so adopting 1.23.0's icons array stops producing a warning to generate
|
|
217
|
+
five files you deliberately lack. A named array is checked against itself (an icon missing from
|
|
218
|
+
`public/` still warns); an unparseable or absent config falls back to the six-filename check. It
|
|
219
|
+
can only narrow, never blind.
|
|
220
|
+
- **`doctor` and `sync` share one sentence about a parent install** (`parentInstallAdvice`). Run
|
|
221
|
+
from a package whose install is above it, `doctor` now names the parent and says `basalt-ui init`
|
|
222
|
+
is NOT the fix. Following its old advice literally scaffolded the second consumer that 1.22.0
|
|
223
|
+
exists to prevent.
|
|
224
|
+
- **Every seeded invocation resolves the local bin** (`basaltBinCommand`, overridable via
|
|
225
|
+
`BASALT_BIN`): the `lint` script, the CI steps, the `.claude` PreToolUse hook and doctor's own
|
|
226
|
+
advice all render `./node_modules/.bin/basalt-ui`. `bunx` does not re-resolve a package it has
|
|
227
|
+
cached — that is what made a round-7 report file a P0 against a 1.20.0 cache while believing it
|
|
228
|
+
was on 1.22.0, and the seed was shipping `bunx` into consumer CI in ten places.
|
|
229
|
+
`configs/lefthook.yml`'s `${BASALT_BIN:-bunx --no-install basalt-ui}` default is unchanged and
|
|
230
|
+
deliberate: `--no-install` fails loudly instead of downloading a stranger.
|
|
231
|
+
- **`tokens:css --check` blanks the provenance line (line 2) before comparing**, so a version bump
|
|
232
|
+
alone no longer forces a no-op commit in a tokens-only consumer, where the gate is byte-equality.
|
|
233
|
+
A stale provenance line is now a note on an otherwise-passing check. The `@generated` header is
|
|
234
|
+
still emitted byte-identical — the line stays, it just stops gating. **Superseded in `Unreleased`:
|
|
235
|
+
blanking the WHOLE line also stopped gating the regeneration command it carries. Only the version
|
|
236
|
+
token is neutralized now.**
|
|
237
|
+
|
|
238
|
+
### Corrections to the record
|
|
239
|
+
|
|
240
|
+
- **All six round-8 reports said `basalt-ui --version` "exits 0 printing usage". It exited 1, to
|
|
241
|
+
stderr** — the dispatcher's `default:` branch has always been `console.error(USAGE); return 1`.
|
|
242
|
+
The real fail-open was an unknown FLAG, which no report tested, and the misdiagnosis was relayed
|
|
243
|
+
verbatim into the fix brief. Report the symptom you measured, not the one you inferred from it.
|
|
244
|
+
- **`cb4e5b7`'s message is wrong** (`b9b99a6` on `origin/feat/round-7-band-kinds` is the same commit
|
|
245
|
+
pre-merge), and it is on `master` where it cannot be rewritten. It claims it taught
|
|
246
|
+
`unframed-chart` the two new kinds; it widened `CHART_ENTRY_POINT_TAG`, which only
|
|
247
|
+
`chart-missing-aria-label` reads. `unframed-chart` keys on `<ChartLegend items={[` and carries no
|
|
248
|
+
kind list at all — there was never an asymmetric pair to fix.
|
|
249
|
+
- **Open question, not a plan.** The import gate does not make `CHART_ENTRY_POINT_TAG` or the oxlint
|
|
250
|
+
plugin's `CHART_TAGS` redundant: that list still answers _which_ tags owe an `ariaLabel`. The gate
|
|
251
|
+
only converts a kind missing from the list from a false positive into an under-report. Collapsing
|
|
252
|
+
the two lists is a separate, larger change and has not been made.
|
|
253
|
+
|
|
254
|
+
## 1.23.0 — two band kinds, an x-tick seam, CLI resolution
|
|
255
|
+
|
|
42
256
|
**No export removed or renamed; six new runtime exports and nine new types.** Nothing you wrote
|
|
43
257
|
changed. Every entry is additive, and the batch runs the other way from the last three: those made
|
|
44
258
|
the guard stricter, this one makes the framework more expressive.
|
|
@@ -72,7 +286,9 @@ unchanged and still works; omit both and ticks are chosen to fit (`smartTicks`),
|
|
|
72
286
|
Reach for it on a dense time axis: the tick choosers append the final key unconditionally, so a
|
|
73
287
|
COUNT that does not land on the last index paints two labels on top of each other at the right edge
|
|
74
288
|
— at every count, not at an unlucky one. On the consumer that reported it, the local tick helper
|
|
75
|
-
went 200 →
|
|
289
|
+
went 200 → 170 lines; it shrinks, it does not disappear. (The promise was 160. The 10-line miss is
|
|
290
|
+
docblock — the port's own JSDoc grew, because the helper stopped being a fallback and became the one
|
|
291
|
+
seam every chart on the page passes through.)
|
|
76
292
|
|
|
77
293
|
**`basaltAppPlugin`'s `icons` takes an array now.**
|
|
78
294
|
|
|
@@ -147,8 +363,8 @@ install it found above instead. The refusal runs before the `basalt.roots` backf
|
|
|
147
363
|
the damage. Two ways to unblock it:
|
|
148
364
|
|
|
149
365
|
```bash
|
|
150
|
-
cd <the package holding .basalt/manifest.json> &&
|
|
151
|
-
BASALT_CWD=<that package>
|
|
366
|
+
cd <the package holding .basalt/manifest.json> && ./node_modules/.bin/basalt-ui sync
|
|
367
|
+
BASALT_CWD=<that package> ./node_modules/.bin/basalt-ui sync # or from anywhere
|
|
152
368
|
```
|
|
153
369
|
|
|
154
370
|
The summary line gained a `created` counter. **`recreated` now means what it always claimed** — the
|