basalt-ui 2.0.5 → 2.2.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/MIGRATING.md +37 -12
  3. package/configs/oxlint.json +1 -0
  4. package/dist/agent/error.d.ts +16 -1
  5. package/dist/agent/error.d.ts.map +1 -1
  6. package/dist/agent/index.d.ts +1 -0
  7. package/dist/agent/index.d.ts.map +1 -1
  8. package/dist/agent/index.js +2 -0
  9. package/dist/charts/Heatmap.d.ts +3 -1
  10. package/dist/charts/Heatmap.d.ts.map +1 -1
  11. package/dist/charts/Heatmap.js +8 -2
  12. package/dist/charts/Sparkline.d.ts +4 -0
  13. package/dist/charts/Sparkline.d.ts.map +1 -1
  14. package/dist/charts/Sparkline.js +18 -12
  15. package/dist/charts/compile.d.ts +6 -0
  16. package/dist/charts/compile.d.ts.map +1 -1
  17. package/dist/charts/compile.js +5 -0
  18. package/dist/charts/state.d.ts +3 -0
  19. package/dist/charts/state.d.ts.map +1 -1
  20. package/dist/charts/state.js +14 -3
  21. package/dist/charts/types.d.ts +2 -1
  22. package/dist/charts/types.d.ts.map +1 -1
  23. package/dist/cli/doctor.js +2 -2
  24. package/dist/dashboard/index.d.ts +1 -1
  25. package/dist/dashboard/index.d.ts.map +1 -1
  26. package/dist/dashboard/index.js +3 -1
  27. package/dist/dashboard/stat-card.d.ts +16 -1
  28. package/dist/dashboard/stat-card.d.ts.map +1 -1
  29. package/dist/dashboard/stat-card.js +14 -1
  30. package/dist/index.d.ts +1 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/shell/app-main.module.css +5 -3
  33. package/dist/shell/app-sidebar.d.ts.map +1 -1
  34. package/dist/shell/app-sidebar.js +1 -1
  35. package/dist/table/data-table.module.css +4 -10
  36. package/llms.txt +2 -2
  37. package/package.json +1 -1
  38. package/src/agent/error.ts +16 -1
  39. package/src/agent/index.ts +1 -0
  40. package/src/charts/Heatmap.tsx +11 -3
  41. package/src/charts/Sparkline.tsx +9 -1
  42. package/src/charts/compile.ts +10 -0
  43. package/src/charts/state.tsx +15 -3
  44. package/src/charts/types.ts +2 -1
  45. package/src/cli/doctor.ts +2 -2
  46. package/src/dashboard/index.ts +6 -1
  47. package/src/dashboard/stat-card.tsx +34 -3
  48. package/src/index.ts +1 -0
  49. package/src/shell/app-main.module.css +5 -3
  50. package/src/shell/app-sidebar.tsx +3 -1
  51. package/src/table/data-table.module.css +4 -10
package/CHANGELOG.md CHANGED
@@ -1,3 +1,32 @@
1
+ # [2.2.0](https://github.com/jkrumm/basalt-ui/compare/v2.1.0...v2.2.0) (2026-10-14)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * read subpath peers from the app package in a monorepo ([ca1263d](https://github.com/jkrumm/basalt-ui/commit/ca1263d04ceb41e966c0e93ef3243db4f1359bd1))
7
+ * scan the app package for dropped subpaths in a monorepo ([a4a076c](https://github.com/jkrumm/basalt-ui/commit/a4a076ceee33d70a4180c1f45eb1cbd8a43087aa))
8
+ * stride Heatmap x labels to the container class tick count ([00c93b2](https://github.com/jkrumm/basalt-ui/commit/00c93b2aa34d99e1afd67f979e1e39a9f79f3630))
9
+ * stub getBoundingClientRect on Element.prototype in the sparkline state test ([3ceeb9f](https://github.com/jkrumm/basalt-ui/commit/3ceeb9f5041978d85fbb1e696bc142bef8872402))
10
+
11
+
12
+ ### Features
13
+
14
+ * export StatCardSparklineSize and allow render props in the preset ([29fcc01](https://github.com/jkrumm/basalt-ui/commit/29fcc01b6a9565aad34001a7ac2e281022cb8b1f))
15
+ * give Sparkline a state and derive it from StatCard query ([4fb9b39](https://github.com/jkrumm/basalt-ui/commit/4fb9b394105f28cec4ca7753d755cb99b6154514))
16
+ * re-export StatCardSparklineSize from the root ([2c9629d](https://github.com/jkrumm/basalt-ui/commit/2c9629d2cfe042b2ba18947f8ffdf56915ddedc8))
17
+
18
+ # [2.1.0](https://github.com/jkrumm/basalt-ui/compare/v2.0.5...v2.1.0) (2026-10-14)
19
+
20
+
21
+ ### Bug Fixes
22
+
23
+ * reach folded nav groups from the collapsed rail ([9dc75b7](https://github.com/jkrumm/basalt-ui/commit/9dc75b70350aab4c58ebb2264710d8b71a03e707))
24
+
25
+
26
+ ### Features
27
+
28
+ * export toAgentError from basalt-ui/agent ([8f23111](https://github.com/jkrumm/basalt-ui/commit/8f23111b7f4f9eb13c44df135138ff879da89f93))
29
+
1
30
  ## [2.0.5](https://github.com/jkrumm/basalt-ui/compare/v2.0.4...v2.0.5) (2026-10-14)
2
31
 
3
32
 
package/MIGRATING.md CHANGED
@@ -66,18 +66,18 @@ There is one row per SURFACE, not per export, and a surface earns a row once any
66
66
  shipped lefthook preset both changed in 1.30.0 with no row that owned them — which is how a
67
67
  consumer read this index, found nothing, and diffed a KPI row that had silently gone 4-up → 2-up.
68
68
 
69
- | Surface | 1.27.0 | 1.28.0 | 1.29.0 | 1.30.0 | 1.30.1 | 1.30.2 | 1.31.0 | 1.32.0 | 1.32.1 | 1.33.0 | 1.34.0 | 1.34.1 | 1.35.0 | 1.36.0 | 1.36.1 | 1.37.0 | 1.37.1 | 1.38.0 | 1.38.1 | 1.39.0 | 1.40.0 | 1.40.1 | 1.40.2 | 1.40.3 | 1.41.0 | 2.0.0 | 2.0.1 | 2.0.2 | 2.0.3 | 2.0.4 | 2.0.5 | Unreleased |
70
- | ----------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------- | ------ | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ---------------------------------- | ------ | ------ | -------------------------------------------------------------------------------------------- | ------ | ------ | ------ | ------ | ------ | ----------------------------------------------- | ------ | ------ | ------ | ------ | ------------------------------------------------------------ | ---------- | --- | --- | --- | --- | --- | --- | --- |
71
- | **tokens** (`./tokens`) | — | `--vx-divider` → `rgba()` on both schemes (§ Shell) | `--vx-space-touch-target` added (§ Additions) | eight spacing numbers (§ Chrome) | — | — | `--vx-hit` replaces the touch vars; `SIZE_CLASSES` (§ 1.31.0) | — | `SIZE_CLASSES`/`CONTAINER_CLASSES` actually exported (§ Fixes) | `CONTAINER_GRID_BREAKPOINTS`; `raw-breakpoint` trusts it (§ 1.33.0) | `CONTAINER_KEYS` (§ 1.34.0) | — | — | — | — | — | — | — | | `appShellInset` 20 → 16 (§ 1.38.1) | — | — | — | — | — | — | — | — | — | — | — | — | — |
72
- | **charts** (`./charts`) | — | phone tier, `curve`, formatters, `state`, log axis (§ preamble) | `ChartState.empty: string`, `AxisBottomNumeric` (§ Additions) | responsive `height`, `legend.maxRows` (§ Charts) | `height` reaches the kinds; `maxRows` beats the `fill` fit (§ Charts) | — | container-class tier + `height` keys (§ 1.31.0) | — | `ResponsiveChartHeight` `sm` → key (§ Container recipes) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
73
- | **shell** (`.`) | — | scrollport, region seams, **the brand left the sidebar** (§§ scrollport, brand, Shell) | `PageTitle`, `BasaltDevDock`, `useBreakpoint` (§ Additions) | More popover, aside pill, the responsive gutter, `useBasaltSpacing` (§ Chrome) | — | — | `theme.breakpoints` derived, `useSizeClass`, `data-basalt-host`, `useBreakpoint` on notice (§ 1.31.0) | `useBreakpoint` removal → 1.33.0; `useSizeClass` is shell-only (§ 1.32.0) | shell-less `shell/` gutter, `AppShell` breakpoints, `useSizeClass` for overlays (§§ Shell-less, `useSizeClass`) | — | `useBreakpoint` removed (§ 1.34.0) | — | — | `useCoarsePointer()` (§ `useCoarsePointer()`) | — | — | — | — | | content gutter 16, block 8 from `sm` (§ 1.38.1) | — | — | — | — | — | — | — | — | — | — | — | — | — |
74
- | **controls** (`./controls`) | `NumberFilter`, `field.number` (§ Controls) | sheet renders panel rows; `PanelRow`, `SliderControl` (§ `basalt-ui/controls`) | — | — | — | — | — | — | — | `NumberFilter clearable`; `createSearchStore` `resets` (§ State and controls) | — | — | — | `FormSection`/`FormRow`/`FormGroup`/`FormActions` move here (§ The form layout moves) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
75
- | **forms** (`./forms`) | — | `field` → `inputProps`, **`inputProps` drops `key`**, the layout tier (§ `inputProps`) | `field` DELETED — and the `--fix` recipe no longer works (§ Consolidation) | — | — | — | — | — | `FormGroup` row no longer stretches (§ Fixes) | — | — | — | — | the layout + `useForm`/`schemaResolver` deprecated, removed 1.37.0 (§ The form layout moves) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
76
- | **data** (`./data/table`) | — | root is a `<div>`, row selection, `getItemKey` required (§ `basalt-ui/data`) | `./data` + `./query` + `./connectivity` dropped (§ Consolidation) | — | — | — | — | — | `renderCard` documented (§ `renderCard`) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
77
- | **dashboard** (`.`) | `StatCard`: `unit`, `breakdown`, delta format (§ `StatCard`) | `StatGroup`, `WidgetGrid`, query-aware `StatCard` — additive | — | **the column law keys on the CONTAINER; `cols={4}` is 2-up at `sm`** (§ Dashboard) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
78
- | **toolchain** (`./configs/*`) | — | — | the shipped lefthook preset's `check-theme` BROKE — the CLI resolver stopped ascending (§ CLI) | the preset's default bin, and the `root:` recipe (§ Toolchain) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
79
- | **CLI** (`basalt-ui`) | — | — | **resolver stops relocating; `doctor` loses 6 of 10 checks** (§ CLI) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | the managed block moves `CLAUDE.md` → `AGENTS.md` (§ 1.41.0) | — | — | — | — | — | — | — |
80
- | **guards** | five rules → `error` (§ Guards) | six new ids at `warn` (§ preamble) | `query-dual-import` retired in 1.29.1 (§ Guards) | four rules → `error` (§ Guards) | — | — | — | `raw-breakpoint`: the container-grid answer, `error` at 1.33.0 (§ 1.32.0) | `FormRow`/`FormGroup` homes in both lanes; `basalt-main` dropped (§ Fixes) | C1 + breakpoint pairs → `error`; `unknown-vx-token` `warn` (§ Guards) | — | — | — | `unknown-vx-token` → `error`; the token factories throw (§ Guards) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
69
+ | Surface | 1.27.0 | 1.28.0 | 1.29.0 | 1.30.0 | 1.30.1 | 1.30.2 | 1.31.0 | 1.32.0 | 1.32.1 | 1.33.0 | 1.34.0 | 1.34.1 | 1.35.0 | 1.36.0 | 1.36.1 | 1.37.0 | 1.37.1 | 1.38.0 | 1.38.1 | 1.39.0 | 1.40.0 | 1.40.1 | 1.40.2 | 1.40.3 | 1.41.0 | 2.0.0 | 2.0.1 | 2.0.2 | 2.0.3 | 2.0.4 | 2.0.5 | 2.1.0 | 2.2.0 | Unreleased |
70
+ | ----------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------- | ------ | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ---------------------------------- | ------ | ------ | -------------------------------------------------------------------------------------------- | ------ | ------ | ------ | ------ | ------ | ----------------------------------------------- | ------ | ------ | ------ | ------ | ------------------------------------------------------------ | ---------- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
71
+ | **tokens** (`./tokens`) | — | `--vx-divider` → `rgba()` on both schemes (§ Shell) | `--vx-space-touch-target` added (§ Additions) | eight spacing numbers (§ Chrome) | — | — | `--vx-hit` replaces the touch vars; `SIZE_CLASSES` (§ 1.31.0) | — | `SIZE_CLASSES`/`CONTAINER_CLASSES` actually exported (§ Fixes) | `CONTAINER_GRID_BREAKPOINTS`; `raw-breakpoint` trusts it (§ 1.33.0) | `CONTAINER_KEYS` (§ 1.34.0) | — | — | — | — | — | — | — | | `appShellInset` 20 → 16 (§ 1.38.1) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
72
+ | **charts** (`./charts`) | — | phone tier, `curve`, formatters, `state`, log axis (§ preamble) | `ChartState.empty: string`, `AxisBottomNumeric` (§ Additions) | responsive `height`, `legend.maxRows` (§ Charts) | `height` reaches the kinds; `maxRows` beats the `fill` fit (§ Charts) | — | container-class tier + `height` keys (§ 1.31.0) | — | `ResponsiveChartHeight` `sm` → key (§ Container recipes) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
73
+ | **shell** (`.`) | — | scrollport, region seams, **the brand left the sidebar** (§§ scrollport, brand, Shell) | `PageTitle`, `BasaltDevDock`, `useBreakpoint` (§ Additions) | More popover, aside pill, the responsive gutter, `useBasaltSpacing` (§ Chrome) | — | — | `theme.breakpoints` derived, `useSizeClass`, `data-basalt-host`, `useBreakpoint` on notice (§ 1.31.0) | `useBreakpoint` removal → 1.33.0; `useSizeClass` is shell-only (§ 1.32.0) | shell-less `shell/` gutter, `AppShell` breakpoints, `useSizeClass` for overlays (§§ Shell-less, `useSizeClass`) | — | `useBreakpoint` removed (§ 1.34.0) | — | — | `useCoarsePointer()` (§ `useCoarsePointer()`) | — | — | — | — | | content gutter 16, block 8 from `sm` (§ 1.38.1) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
74
+ | **controls** (`./controls`) | `NumberFilter`, `field.number` (§ Controls) | sheet renders panel rows; `PanelRow`, `SliderControl` (§ `basalt-ui/controls`) | — | — | — | — | — | — | — | `NumberFilter clearable`; `createSearchStore` `resets` (§ State and controls) | — | — | — | `FormSection`/`FormRow`/`FormGroup`/`FormActions` move here (§ The form layout moves) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
75
+ | **forms** (`./forms`) | — | `field` → `inputProps`, **`inputProps` drops `key`**, the layout tier (§ `inputProps`) | `field` DELETED — and the `--fix` recipe no longer works (§ Consolidation) | — | — | — | — | — | `FormGroup` row no longer stretches (§ Fixes) | — | — | — | — | the layout + `useForm`/`schemaResolver` deprecated, removed 1.37.0 (§ The form layout moves) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
76
+ | **data** (`./data/table`) | — | root is a `<div>`, row selection, `getItemKey` required (§ `basalt-ui/data`) | `./data` + `./query` + `./connectivity` dropped (§ Consolidation) | — | — | — | — | — | `renderCard` documented (§ `renderCard`) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
77
+ | **dashboard** (`.`) | `StatCard`: `unit`, `breakdown`, delta format (§ `StatCard`) | `StatGroup`, `WidgetGrid`, query-aware `StatCard` — additive | — | **the column law keys on the CONTAINER; `cols={4}` is 2-up at `sm`** (§ Dashboard) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
78
+ | **toolchain** (`./configs/*`) | — | — | the shipped lefthook preset's `check-theme` BROKE — the CLI resolver stopped ascending (§ CLI) | the preset's default bin, and the `root:` recipe (§ Toolchain) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
79
+ | **CLI** (`basalt-ui`) | — | — | **resolver stops relocating; `doctor` loses 6 of 10 checks** (§ CLI) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | the managed block moves `CLAUDE.md` → `AGENTS.md` (§ 1.41.0) | — | — | — | — | — | — | — | — | — |
80
+ | **guards** | five rules → `error` (§ Guards) | six new ids at `warn` (§ preamble) | `query-dual-import` retired in 1.29.1 (§ Guards) | four rules → `error` (§ Guards) | — | — | — | `raw-breakpoint`: the container-grid answer, `error` at 1.33.0 (§ 1.32.0) | `FormRow`/`FormGroup` homes in both lanes; `basalt-main` dropped (§ Fixes) | C1 + breakpoint pairs → `error`; `unknown-vx-token` `warn` (§ Guards) | — | — | — | `unknown-vx-token` → `error`; the token factories throw (§ Guards) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
81
81
 
82
82
  ---
83
83
 
@@ -992,6 +992,31 @@ optional, subpath-required peers.
992
992
  _Nothing yet. Author the next section under this heading as `## Unreleased — <title>`; the
993
993
  release renames it and opens a new one here._
994
994
 
995
+ ## 2.2.0 — `doctor` in a monorepo, sparkline state, heatmap ticks
996
+
997
+ - `Sparkline` takes the same `state` as the other chart kinds (pending fills its box, error and
998
+ empty show a dash with the label as its name). `StatCard` derives it from `query` and passes it to
999
+ the `sparkline` render prop as `size.state`, so `<Sparkline {...size} />` is the whole call site.
1000
+ - `StatCardSparklineSize` is exported from `basalt-ui`. The shipped oxlint preset sets
1001
+ `react/no-unstable-nested-components` `allowAsProps: true`, so the documented `sparkline` render
1002
+ prop no longer warns.
1003
+ - `Heatmap` strides its x labels to the container class's tick count (6 at `wide`), where it drew
1004
+ every column.
1005
+ - `doctor`'s `subpath-peers` check scans the app package's source and resolves each peer from the app directory (`basalt.app`, or the single workspace package that depends on basalt-ui), so a monorepo with the peers declared in `apps/<app>` no longer reports every peer missing. No action needed.
1006
+ - `doctor`'s `dropped-subpaths` check likewise scans the app package's source rather than the install root, so a monorepo app's dropped `basalt-ui/<subpath>` imports are found. No action needed.
1007
+
1008
+ ## 2.1.0 — the rail reaches folded groups, `toAgentError`
1009
+
1010
+ - `basalt-ui/agent` exports `toAgentError(error: unknown): AgentError`, the classifier
1011
+ `useAgentThreadRuns` uses for `ThreadRunState.error`. A custom transport calls it instead of a
1012
+ hand-rolled mapping; for a `Response`, pass `{ statusCode: response.status }`.
1013
+ - The collapsed rail now shows every row of a `collapsible` section, including one folded by
1014
+ `defaultCollapsed` or by the user. Its header is hidden in the rail, so a folded group (argo's
1015
+ "Other") used to strand its destinations and their count dots. Expanding the sidebar restores the
1016
+ stored fold. No API change.
1017
+ - `AppShell.Main`'s block padding is published as `--basalt-main-pad-block`; the data table's
1018
+ page-sticky offset reads it, replacing a duplicated `sm` media query. No consumer action.
1019
+
995
1020
  ## 2.0.5 — subpath peers
996
1021
 
997
1022
  - `doctor` gains `subpath-peers`: when your source imports a basalt-ui subpath whose required peers
@@ -28,6 +28,7 @@
28
28
  "react/incompatible-library": "error",
29
29
  "react/preserve-manual-memoization": "error",
30
30
  "react/refs": "error",
31
+ "react/no-unstable-nested-components": ["warn", { "allowAsProps": true }],
31
32
  "react/only-export-components": ["warn", { "allowExportNames": ["Route"] }],
32
33
  "react/set-state-in-effect": "error",
33
34
  "no-constant-binary-expression": "error"
@@ -26,6 +26,21 @@ export type AgentError = {
26
26
  } | {
27
27
  readonly kind: 'aborted';
28
28
  };
29
- /** Maps whatever a transport threw onto the closed `AgentError` union. */
29
+ /**
30
+ * Classifies whatever a chat transport threw or received into the closed `AgentError` union — the
31
+ * same mapping `useAgentThreadRuns` applies to `ThreadRunState.error`, so a custom transport can
32
+ * report failures identically.
33
+ *
34
+ * Input is any thrown value, matched by shape (never `instanceof`, so the optional `ai` peer is not
35
+ * loaded). First match wins:
36
+ * - `name === 'AbortError'` (an `Error` or a `DOMException`) → `{ kind: 'aborted' }`
37
+ * - numeric `statusCode` (AI SDK `APICallError`) → `{ kind: 'http', status }`
38
+ * - string `chunkType` and `chunkId` (AI SDK `UIMessageStreamError`) → `{ kind: 'stream', ... }`
39
+ * - anything else, including an `error` chunk's text, a plain object or a primitive →
40
+ * `{ kind: 'server', message }` with the value's `message` string, else `String(value)`.
41
+ *
42
+ * A `Response` is not classified: read its `status` and pass `{ statusCode: response.status }`.
43
+ * Never throws.
44
+ */
30
45
  export declare function toAgentError(error: unknown): AgentError;
31
46
  //# sourceMappingURL=error.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/agent/error.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACjF;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAE,CAAA;AAQhC,0EAA0E;AAC1E,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,CAavD"}
1
+ {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/agent/error.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACjF;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAE,CAAA;AAQhC;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,CAavD"}
@@ -14,6 +14,7 @@ export { aiSdkTransport } from './ai-sdk-transport.js';
14
14
  export { useAgentThreadRuns } from './use-agent-thread-runs.js';
15
15
  export type { ThreadRunState } from './use-agent-thread-runs.js';
16
16
  export type { AgentError } from './error.js';
17
+ export { toAgentError } from './error.js';
17
18
  export type { ChatMessage } from './history.js';
18
19
  export type { AgentThread, ThreadStatus, ThreadsStore } from './thread.js';
19
20
  export type { AgentOutcome, OutcomeResolver } from './outcome.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AACxC,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAGzF,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAC5D,YAAY,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAC7D,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAGzC,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAC5C,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvE,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAC9D,YAAY,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAG1E,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AACtD,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC1C,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACrE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AACxC,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAGzF,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAC5D,YAAY,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAC7D,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAGtC,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAC5C,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvE,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAC9D,YAAY,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAG1E,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AACtD,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC1C,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACrE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA"}
@@ -1,6 +1,7 @@
1
1
  import { parseAgentPart } from "./parts.js";
2
2
  import { aiSdkTransport } from "./ai-sdk-transport.js";
3
3
  import { useAgentThreadRuns } from "./use-agent-thread-runs.js";
4
+ import { toAgentError } from "./error.js";
4
5
  import { ThreadFeed } from "./chat/thread-feed.js";
5
6
  import { ThreadFeedRow } from "./chat/thread-feed-row.js";
6
7
  import { Composer } from "./chat/composer.js";
@@ -12,5 +13,6 @@ export {
12
13
  ToolChip,
13
14
  aiSdkTransport,
14
15
  parseAgentPart,
16
+ toAgentError,
15
17
  useAgentThreadRuns
16
18
  };
@@ -33,7 +33,9 @@ export type HeatmapProps<T> = BasaltProps & {
33
33
  };
34
34
  /** A fixed height, or one per container class. Default: by row count (160–420px). */
35
35
  height?: number | ResponsiveChartHeight;
36
- /** Fill the parent's height instead (the parent needs a definite height). Overrides `height`. */
36
+ /** Fill the parent's height instead. Overrides `height`. CSS-owned like every other kind's box
37
+ * (`ChartBox`, no measuring, SSR-stable), so the parent needs a definite CSS height — a grid or
38
+ * flex stretch, `h-full`, an explicit height — which no kind can read off an auto-height parent. */
37
39
  matchHeight?: boolean;
38
40
  state?: ChartState;
39
41
  };
@@ -1 +1 @@
1
- {"version":3,"file":"Heatmap.d.ts","sourceRoot":"","sources":["../../src/charts/Heatmap.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAiB,YAAY,EAAE,MAAM,OAAO,CAAA;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AASlD,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAA;AAEjF,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,WAAW,GAAG;IAC1C,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,SAAS,CAAC,EAAE,CAAA;IAClB,qCAAqC;IACrC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACxB,wCAAwC;IACxC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACxB,4EAA4E;IAC5E,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,IAAI,CAAA;IACjC,0DAA0D;IAC1D,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB,oFAAoF;IACpF,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,6EAA6E;IAC7E,MAAM,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,8EAA8E;IAC9E,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IACnC,kEAAkE;IAClE,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IACnC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IACnC,4DAA4D;IAC5D,MAAM,CAAC,EAAE,KAAK,GAAG;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACnC,oGAAoG;IACpG,OAAO,CAAC,EAAE,KAAK,GAAG;QAAE,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,SAAS,eAAe,EAAE,CAAA;KAAE,CAAA;IACjE,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,GAAG,qBAAqB,CAAA;IACvC,iGAAiG;IACjG,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,KAAK,CAAC,EAAE,UAAU,CAAA;CACnB,CAAA;AAuBD,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,YAAY,CAuM/D"}
1
+ {"version":3,"file":"Heatmap.d.ts","sourceRoot":"","sources":["../../src/charts/Heatmap.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAiB,YAAY,EAAE,MAAM,OAAO,CAAA;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAUlD,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAA;AAEjF,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,WAAW,GAAG;IAC1C,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,SAAS,CAAC,EAAE,CAAA;IAClB,qCAAqC;IACrC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACxB,wCAAwC;IACxC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACxB,4EAA4E;IAC5E,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,IAAI,CAAA;IACjC,0DAA0D;IAC1D,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB,oFAAoF;IACpF,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,6EAA6E;IAC7E,MAAM,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,8EAA8E;IAC9E,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IACnC,kEAAkE;IAClE,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IACnC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IACnC,4DAA4D;IAC5D,MAAM,CAAC,EAAE,KAAK,GAAG;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACnC,oGAAoG;IACpG,OAAO,CAAC,EAAE,KAAK,GAAG;QAAE,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,SAAS,eAAe,EAAE,CAAA;KAAE,CAAA;IACjE,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,GAAG,qBAAqB,CAAA;IACvC;;wGAEoG;IACpG,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,KAAK,CAAC,EAAE,UAAU,CAAA;CACnB,CAAA;AAuBD,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,YAAY,CA4M/D"}
@@ -9,6 +9,7 @@ import { scaleQuantize } from "d3-scale";
9
9
  import { useId, useMemo } from "react";
10
10
  import { alpha, VX } from "../tokens/index.js";
11
11
  import { resolveContainerClass } from "../tokens/size-classes.js";
12
+ import { strideTicks } from "./compile.js";
12
13
  import { ChartEngine, idPrefixOf } from "./host.js";
13
14
  import { useCoarsePointer } from "../common/use-media-query.js";
14
15
  import { HEIGHT_MAX, HEIGHT_MIN } from "./box.js";
@@ -87,8 +88,9 @@ function Heatmap(props) {
87
88
  return {
88
89
  chart: ({ width }) => {
89
90
  const ticks = { line: false, size: 0 };
91
+ const cls = resolveContainerClass(width);
90
92
  const tickLabels = {
91
- fontSize: resolveContainerClass(width) === "wide" ? VX.text.micro : VX.text.nano,
93
+ fontSize: cls === "wide" ? VX.text.micro : VX.text.nano,
92
94
  opacity: 1,
93
95
  thin: true
94
96
  };
@@ -128,7 +130,11 @@ function Heatmap(props) {
128
130
  scale: scaleBand().domain(cols).paddingInner(0).paddingOuter(0),
129
131
  axis: {
130
132
  line: AXIS_LINE,
131
- ticks: { values: cols, format: (v) => formatCol(String(v)), ...ticks },
133
+ ticks: {
134
+ values: strideTicks(cols, cls),
135
+ format: (v) => formatCol(String(v)),
136
+ ...ticks
137
+ },
132
138
  tickLabels
133
139
  }
134
140
  },
@@ -1,5 +1,6 @@
1
1
  import type { ReactElement } from 'react';
2
2
  import type { BasaltProps } from '../common/props.js';
3
+ import type { ChartState } from './types.js';
3
4
  export type SparklineProps = BasaltProps & {
4
5
  /** The accessible name. Required: a chart without one is an unlabeled image. */
5
6
  ariaLabel: string;
@@ -16,6 +17,9 @@ export type SparklineProps = BasaltProps & {
16
17
  emphasizeLast?: boolean;
17
18
  /** Default `VX.accent`. */
18
19
  emphasisColor?: string;
20
+ /** Pending / error / empty, the same contract as every other kind. The placeholder fills the
21
+ * sparkline's own `width` × `height`, so a loading tile does not shift when the trend lands. */
22
+ state?: ChartState;
19
23
  };
20
24
  /** `useId` output (`«r1»`, `:r1:`) is not a safe SVG id fragment. */
21
25
  export declare function Sparkline(props: SparklineProps): ReactElement;
@@ -1 +1 @@
1
- {"version":3,"file":"Sparkline.d.ts","sourceRoot":"","sources":["../../src/charts/Sparkline.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAA;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAQlD,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG;IACzC,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,oBAAoB;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,wBAAwB;IACxB,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAAA;IACrB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,8FAA8F;IAC9F,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,2BAA2B;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAiGD,qEAAqE;AAErE,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,YAAY,CAsC7D"}
1
+ {"version":3,"file":"Sparkline.d.ts","sourceRoot":"","sources":["../../src/charts/Sparkline.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAA;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAQlD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEzC,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG;IACzC,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,oBAAoB;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,wBAAwB;IACxB,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAAA;IACrB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,8FAA8F;IAC9F,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,2BAA2B;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;oGACgG;IAChG,KAAK,CAAC,EAAE,UAAU,CAAA;CACnB,CAAA;AAiGD,qEAAqE;AAErE,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,YAAY,CAyC7D"}
@@ -1,4 +1,4 @@
1
- import { jsx } from "react/jsx-runtime";
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { dot } from "@tanstack/charts/dot";
3
3
  import { decorative } from "@tanstack/charts/mark/decorative";
4
4
  import { lineY } from "@tanstack/charts/line";
@@ -9,6 +9,7 @@ import { VX } from "../tokens/index.js";
9
9
  import { chartCurve } from "./curves.js";
10
10
  import { barMark } from "./marks.js";
11
11
  import { ChartEngine, idPrefixOf } from "./host.js";
12
+ import { ChartPlaceholder, resolveChartState } from "./state.js";
12
13
  import { CHART_THEME } from "./theme.js";
13
14
  const LINE_WIDTH = 1.6;
14
15
  const DOT_R = 2.5;
@@ -95,6 +96,7 @@ function Sparkline(props) {
95
96
  color = VX.faint,
96
97
  emphasizeLast = false,
97
98
  emphasisColor = VX.accent,
99
+ state,
98
100
  className,
99
101
  style
100
102
  } = props;
@@ -105,22 +107,26 @@ function Sparkline(props) {
105
107
  if (range === null) return null;
106
108
  return compileSparkline(rows, range, mark, color, emphasizeLast ? emphasisColor : null);
107
109
  }, [data, mark, color, emphasizeLast, emphasisColor]);
108
- return /* @__PURE__ */ jsx(
110
+ const resolved = resolveChartState(state);
111
+ return /* @__PURE__ */ jsxs(
109
112
  "div",
110
113
  {
111
114
  "data-basalt-sparkline": "",
112
115
  ...className !== void 0 && { className },
113
116
  style: { width: width ?? "100%", height, minWidth: 0, ...style },
114
- children: definition !== null && /* @__PURE__ */ jsx(
115
- ChartEngine,
116
- {
117
- definition,
118
- ariaLabel,
119
- height,
120
- idPrefix: idPrefixOf(id, "spark"),
121
- coarse: false
122
- }
123
- )
117
+ children: [
118
+ resolved !== null && /* @__PURE__ */ jsx(ChartPlaceholder, { state, height, compact: true }),
119
+ resolved === null && definition !== null && /* @__PURE__ */ jsx(
120
+ ChartEngine,
121
+ {
122
+ definition,
123
+ ariaLabel,
124
+ height,
125
+ idPrefix: idPrefixOf(id, "spark"),
126
+ coarse: false
127
+ }
128
+ )
129
+ ]
124
130
  }
125
131
  );
126
132
  }
@@ -48,6 +48,12 @@ export declare function yGuide<T>(opts: {
48
48
  side: 'left' | 'right';
49
49
  grid: boolean;
50
50
  }): Omit<ChartPositionScaleOptions<number>, 'scale'>;
51
+ /**
52
+ * Every n-th value, n the smallest stride that leaves at most the class's x tick count (the same
53
+ * ladder a continuous x axis counts to). A band axis labels every value it is given and only drops
54
+ * the ones that collide, so a short-label grid (18 hour columns) would otherwise draw all of them.
55
+ */
56
+ export declare function strideTicks<V>(values: readonly V[], cls: ContainerClass): V[];
51
57
  export declare function xGuide<X extends ChartX>(opts: {
52
58
  model: XScaleModel<X>;
53
59
  xTicks: number | readonly X[] | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/charts/compile.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/charts/compile.ts"],"names":[],"mappings":"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"}
@@ -86,6 +86,10 @@ function xTickPolicy(model, xTicks) {
86
86
  };
87
87
  return { count: xTicks };
88
88
  }
89
+ function strideTicks(values, cls) {
90
+ const stride = Math.max(1, Math.ceil(values.length / LADDER[cls].xTicks));
91
+ return values.filter((_, i) => i % stride === 0);
92
+ }
89
93
  function xGuide(opts) {
90
94
  const { model, xTicks, format, cls } = opts;
91
95
  const ladder = LADDER[cls];
@@ -378,6 +382,7 @@ export {
378
382
  dateTicks,
379
383
  defaultFormatX,
380
384
  defaultNumberFormat,
385
+ strideTicks,
381
386
  utcAxisDate,
382
387
  utcHeaderDate,
383
388
  visibleSeries,
@@ -13,5 +13,8 @@ export declare function ChartPlaceholder(props: {
13
13
  state: ChartState | undefined;
14
14
  height?: ChartBoxHeight;
15
15
  action?: ReactNode;
16
+ /** For a box too small to hold a sentence (a sparkline): error and empty show a `—` and carry the
17
+ * label as the accessible name and the hover title. */
18
+ compact?: boolean;
16
19
  }): ReactElement | null;
17
20
  //# sourceMappingURL=state.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/charts/state.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAiB,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAInE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEzC,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,IAAI,CAAA;AAErE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,GAAG,kBAAkB,CAMnF;AAsBD;8EAC8E;AAC9E,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,KAAK,EAAE,UAAU,GAAG,SAAS,CAAA;IAC7B,MAAM,CAAC,EAAE,cAAc,CAAA;IACvB,MAAM,CAAC,EAAE,SAAS,CAAA;CACnB,GAAG,YAAY,GAAG,IAAI,CAgCtB"}
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/charts/state.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAiB,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAInE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEzC,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,IAAI,CAAA;AAErE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,GAAG,kBAAkB,CAMnF;AAsBD;8EAC8E;AAC9E,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,KAAK,EAAE,UAAU,GAAG,SAAS,CAAA;IAC7B,MAAM,CAAC,EAAE,cAAc,CAAA;IACvB,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB;2DACuD;IACvD,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,GAAG,YAAY,GAAG,IAAI,CAyCtB"}
@@ -35,16 +35,27 @@ function ChartPlaceholder(props) {
35
35
  /* @__PURE__ */ jsx("output", { "aria-label": "Loading chart", "aria-busy": "true", className: stateClass, style })
36
36
  );
37
37
  const label = kind === "error" ? toErrorMessage(props.state?.error, "The chart failed to load.") : emptyLabel(props.state);
38
- const body = /* @__PURE__ */ jsxs(Fragment, { children: [
38
+ const body = props.compact ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u2014" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
39
39
  /* @__PURE__ */ jsx("span", { children: label }),
40
40
  props.action
41
41
  ] });
42
+ const name = props.compact ? { "aria-label": label, title: label } : {};
42
43
  if (kind === "error")
43
44
  return fit(
44
- /* @__PURE__ */ jsx("div", { role: "alert", "data-basalt-chart-state": kind, className: stateClass, style, children: body })
45
+ /* @__PURE__ */ jsx(
46
+ "div",
47
+ {
48
+ role: "alert",
49
+ "data-basalt-chart-state": kind,
50
+ className: stateClass,
51
+ style,
52
+ ...name,
53
+ children: body
54
+ }
55
+ )
45
56
  );
46
57
  return fit(
47
- /* @__PURE__ */ jsx("output", { "data-basalt-chart-state": kind, className: stateClass, style, children: body })
58
+ /* @__PURE__ */ jsx("output", { "data-basalt-chart-state": kind, className: stateClass, style, ...name, children: body })
48
59
  );
49
60
  }
50
61
  export {
@@ -158,7 +158,8 @@ export type ChartState = {
158
158
  pending?: boolean;
159
159
  /** Truthy = the query failed; an `Error`'s message is the label. */
160
160
  error?: unknown;
161
- /** `true`, or the empty label itself. */
161
+ /** `true`, or the empty label itself. The label is the one way to say WHY (`'No runs match the
162
+ * filter'` vs `'No runs yet'`): § 9 fixes the state shape, so there is no reason field. */
162
163
  empty?: boolean | string;
163
164
  };
164
165
  /** A row of `plot.rows(key)`: one per datum, `y: null` kept as a gap. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/charts/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/charts/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAEzC;8CAC8C;AAC9C,MAAM,MAAM,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAA;AAE3C;;;GAGG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAA;IACb;oGACgG;IAChG,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,IAAI,CAAA;IACjC,kGAAkG;IAClG,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAAA;IAC9B,+FAA+F;IAC/F,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAA;IACzB;4FACwF;IACxF,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,sEAAsE;IACtE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,sBAAsB;IACtB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,iGAAiG;IACjG,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IACvB,qFAAqF;IACrF,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IACvB,yEAAyE;IACzE,IAAI,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAA;IACzC,mGAAmG;IACnG,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,wFAAwF;IACxF,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,6FAA6F;IAC7F,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0FAA0F;IAC1F,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,iGAAiG;IACjG,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACzC,sEAAsE;IACtE,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAA;CAClG,CAAA;AAED,kBAAkB;AAClB,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI;IAC1B;;;;OAIG;IACH,MAAM,CAAC,EACH,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,GACzB,MAAM,GACN,CAAC,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,OAAO,EAAE,SAAS,WAAW,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAC3F,kFAAkF;IAClF,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;oEACgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,0FAA0F;IAC1F,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAA;IAClC,sEAAsE;IACtE,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IAC9B,gEAAgE;IAChE,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,4FAA4F;IAC5F,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,oFAAoF;IACpF,KAAK,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAA;CACzB,CAAA;AAED,0EAA0E;AAC1E,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAC5B,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI;IACjD,IAAI,CAAC,EAAE,CAAC,CAAA;IACR,EAAE,CAAC,EAAE,CAAC,CAAA;IACN,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAA;CAC1B,CAAA;AAED,oGAAoG;AACpG,MAAM,MAAM,OAAO,GAAG;IACpB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CACxB,CAAA;AAED,iFAAiF;AACjF,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,OAAO,GAAG,OAAO,CAAA;IACvB,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,uGAAuG;AACvG,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAA;AAE9D,gCAAgC;AAChC,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,kBAAkB,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI;IAC7D;4BACwB;IACxB,YAAY,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACrC;qFACiF;IACjF,IAAI,CAAC,EAAE,CACL,CAAC,EAAE,CAAC,EACJ,GAAG,EAAE;QAAE,OAAO,EAAE,SAAS,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;QAAC,MAAM,EAAE,SAAS,WAAW,CAAC,CAAC,CAAC,EAAE,CAAA;KAAE,KAC3E,SAAS,eAAe,EAAE,CAAA;IAC/B;gFAC4E;IAC5E,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;yEAEqE;IACrE,SAAS,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAA;IACvC,6DAA6D;IAC7D,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,+FAA+F;IAC/F,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED,qFAAqF;AACrF,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED,mEAAmE;AACnE,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,oEAAoE;IACpE,KAAK,CAAC,EAAE,OAAO,CAAA;IACf;+FAC2F;IAC3F,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;CACzB,CAAA;AAED,yEAAyE;AACzE,MAAM,MAAM,OAAO,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI;IAClD,CAAC,EAAE,CAAC,CAAA;IACJ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,CAAC,CAAA;CACT,CAAA;AAED,gDAAgD;AAChD,MAAM,MAAM,WAAW,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI;IACtD;8EAC0E;IAC1E,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAA;IACtC,kDAAkD;IAClD,OAAO,EAAE,SAAS,WAAW,CAAC,CAAC,CAAC,EAAE,CAAA;IAClC,0EAA0E;IAC1E,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,8DAA8D;IAC9D,cAAc,EAAE,cAAc,CAAA;IAC9B,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,8FAA8F;IAC9F,EAAE,EAAE,IAAI,GAAG,SAAS,CAAA;IACpB,4FAA4F;IAC5F,OAAO,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,QAAQ,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAA;CAChD,CAAA;AAED;uDACuD;AACvD,MAAM,MAAM,UAAU,GAAG,eAAe,CAAC,OAAO,CAAC,CAAA;AAEjD,MAAM,MAAM,mBAAmB,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,WAAW,GAAG;IAC5E,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,SAAS,CAAC,EAAE,CAAA;IAClB,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;IACd,MAAM,EAAE,SAAS,WAAW,CAAC,CAAC,CAAC,EAAE,CAAA;IACjC,yFAAyF;IACzF,KAAK,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,UAAU,CAAA;IAC9C,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAA;IACjB,sFAAsF;IACtF,EAAE,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAA;IAClB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC,EAAE,CAAA;IAC9B,gGAAgG;IAChG,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IAC1B,KAAK,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAA;IAC3B,MAAM,CAAC,EAAE,SAAS,SAAS,CAAC,CAAC,CAAC,EAAE,CAAA;IAChC,QAAQ,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;IAC7B,gCAAgC;IAChC,MAAM,CAAC,EAAE,iBAAiB,GAAG,KAAK,CAAA;IAClC,0FAA0F;IAC1F,MAAM,CAAC,EAAE,MAAM,GAAG,qBAAqB,CAAA;IACvC,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,qEAAqE;IACrE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;qCACiC;IACjC,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IACnC,OAAO,CAAC,EAAE,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAA;CAC3C,CAAA;AAED;mBACmB;AACnB,eAAO,MAAM,IAAI,GAAI,CAAC,EAAE,CAAC,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,KAAG,CAAU,CAAA"}
@@ -266,7 +266,7 @@ basalt-ui doctor \u2014 ${cwd}
266
266
  ],
267
267
  [
268
268
  "dropped-subpaths: no import of a basalt-ui subpath the install lacks",
269
- droppedSubpathsCheck(cwd, cfg, install)
269
+ droppedSubpathsCheck(appDir, cfg, install)
270
270
  ],
271
271
  [
272
272
  "app-wiring: the entry mounts BasaltProvider and the router tree is generated",
@@ -275,7 +275,7 @@ basalt-ui doctor \u2014 ${cwd}
275
275
  ["exact-peers: installed exact-pinned peers match basalt-ui", exactPeersCheck(cwd, install)],
276
276
  [
277
277
  "subpath-peers: every imported basalt-ui subpath has its required peers installed",
278
- subpathPeersCheck(cwd, cfg, install)
278
+ subpathPeersCheck(appDir, cfg, install)
279
279
  ],
280
280
  [
281
281
  "config-keys: the basalt config block sets only keys basalt-ui reads",
@@ -1,7 +1,7 @@
1
1
  export { WidgetHeader } from './widget-header.js';
2
2
  export { DeltaBadge } from './delta-badge.js';
3
3
  export { Section } from './section.js';
4
- export { StatCard, type StatCardBreakdownRow, type StatCardTone } from './stat-card.js';
4
+ export { StatCard, type StatCardBreakdownRow, type StatCardSparklineSize, type StatCardTone, } from './stat-card.js';
5
5
  export { StatGroup } from './stat-group.js';
6
6
  export { EmptyState } from './empty-state.js';
7
7
  export { SettingsSection, SettingsRow, DangerZone } from './settings-section.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dashboard/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,QAAQ,EAAE,KAAK,oBAAoB,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA;AACpF,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dashboard/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EACL,QAAQ,EACR,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,YAAY,GAClB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA"}
@@ -1,7 +1,9 @@
1
1
  import { WidgetHeader } from "./widget-header.js";
2
2
  import { DeltaBadge } from "./delta-badge.js";
3
3
  import { Section } from "./section.js";
4
- import { StatCard } from "./stat-card.js";
4
+ import {
5
+ StatCard
6
+ } from "./stat-card.js";
5
7
  import { StatGroup } from "./stat-group.js";
6
8
  import { EmptyState } from "./empty-state.js";
7
9
  import { SettingsSection, SettingsRow, DangerZone } from "./settings-section.js";
@@ -39,6 +39,17 @@ export type StatCardBreakdownRow = {
39
39
  export type StatCardSparklineSize = {
40
40
  width: number;
41
41
  height: number;
42
+ /**
43
+ * The card's `query` as a chart state — present only while the sparkline has nothing to draw
44
+ * (pending, or failed with no data), so `<Sparkline {...size} />` forwards it and the tile shows a
45
+ * sized placeholder instead of a hand-rolled `'—'`. Structurally a `ChartState`: this module
46
+ * stays chart-free. Absent without a `query`, and once data has arrived.
47
+ */
48
+ state?: {
49
+ pending?: boolean;
50
+ error?: unknown;
51
+ empty?: boolean;
52
+ };
42
53
  };
43
54
  /**
44
55
  * `StatCard` re-publishes three NAMED `WidgetHeader` slices (`WidgetHeaderTitleProps` /
@@ -97,8 +108,12 @@ export type StatCardProps = BasaltProps & SlotStylesProps<StatCardSlot> & Widget
97
108
  * measured width is the card's own inner width including the bled inset; with `'right'` it is the
98
109
  * fixed 72×26 slot (see {@link SPARKLINE_RIGHT_WIDTH} for why that one is not measured).
99
110
  *
111
+ * A render prop is a sanctioned pattern here, so `react/no-unstable-nested-components` is
112
+ * configured with `allowAsProps` in the shipped preset. Spread the size (`{...size}`) to forward
113
+ * `state` along with the box.
114
+ *
100
115
  * @example
101
- * sparkline={({ width, height }) => <MySparkline data={history} width={width} height={height} />}
116
+ * sparkline={(size) => <Sparkline ariaLabel="Active users" data={history} {...size} />}
102
117
  */
103
118
  sparkline?: ReactNode | ((size: StatCardSparklineSize) => ReactNode);
104
119
  /** Where `sparkline` sits. `'bleed'` (default) is today's full-width row bled to the card edges;
@@ -1 +1 @@
1
- {"version":3,"file":"stat-card.d.ts","sourceRoot":"","sources":["../../src/dashboard/stat-card.tsx"],"names":[],"mappings":"AA+FA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAA;AAErD,OAAO,KAAK,EACV,sBAAsB,EACtB,uBAAuB,EACvB,sBAAsB,EACvB,MAAM,iBAAiB,CAAA;AAIxB,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAEzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAGnD;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,IAAI,CAAA;AAE/B;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAA;AAEnE;;;;;;;GAOG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,YAAY,CAAA;CAC7B,CAAA;AAcD;;;;;;;;GAQG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAiDD;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,aAAa,GAAG,WAAW,GACrC,eAAe,CAAC,YAAY,CAAC,GAC7B,sBAAsB,GACtB,IAAI,CAAC,uBAAuB,EAAE,OAAO,CAAC,GACtC,sBAAsB,GAAG;IACvB;iEAC6D;IAC7D,KAAK,EAAE,MAAM,CAAA;IACb;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAA;IAC3C;;;;;;;;;;;;;OAaG;IACH,KAAK,CAAC,EAAE,cAAc,CAAC,OAAO,CAAC,CAAA;IAC/B;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,IAAI,EAAE,qBAAqB,KAAK,SAAS,CAAC,CAAA;IACpE;4GACwG;IACxG,kBAAkB,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;IACtC;mGAC+F;IAC/F,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB;;;+EAG2E;IAC3E,IAAI,CAAC,EAAE,YAAY,CAAA;CACpB,CAAA;AAEH,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,IAAI,EACJ,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,IAAI,EACJ,SAAS,EACT,KAAK,EACL,KAAK,EACL,WAAW,EACX,aAAa,EACb,WAAW,EACX,UAAU,EACV,SAAS,EACT,kBAA4B,EAC5B,OAAO,EACP,IAAI,EACJ,SAAS,EACT,KAAK,EACL,UAAU,GACX,EAAE,aAAa,+BAqKf"}
1
+ {"version":3,"file":"stat-card.d.ts","sourceRoot":"","sources":["../../src/dashboard/stat-card.tsx"],"names":[],"mappings":"AAiGA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAA;AAErD,OAAO,KAAK,EACV,sBAAsB,EACtB,uBAAuB,EACvB,sBAAsB,EACvB,MAAM,iBAAiB,CAAA;AAIxB,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAEzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAInD;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,IAAI,CAAA;AAE/B;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAA;AAEnE;;;;;;;GAOG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,YAAY,CAAA;CAC7B,CAAA;AAcD;;;;;;;;GAQG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd;;;;;OAKG;IACH,KAAK,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAChE,CAAA;AA6DD;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,aAAa,GAAG,WAAW,GACrC,eAAe,CAAC,YAAY,CAAC,GAC7B,sBAAsB,GACtB,IAAI,CAAC,uBAAuB,EAAE,OAAO,CAAC,GACtC,sBAAsB,GAAG;IACvB;iEAC6D;IAC7D,KAAK,EAAE,MAAM,CAAA;IACb;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAA;IAC3C;;;;;;;;;;;;;OAaG;IACH,KAAK,CAAC,EAAE,cAAc,CAAC,OAAO,CAAC,CAAA;IAC/B;;;;;;;;;;;;;;;OAeG;IACH,SAAS,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,IAAI,EAAE,qBAAqB,KAAK,SAAS,CAAC,CAAA;IACpE;4GACwG;IACxG,kBAAkB,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;IACtC;mGAC+F;IAC/F,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB;;;+EAG2E;IAC3E,IAAI,CAAC,EAAE,YAAY,CAAA;CACpB,CAAA;AAEH,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,IAAI,EACJ,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,IAAI,EACJ,SAAS,EACT,KAAK,EACL,KAAK,EACL,WAAW,EACX,aAAa,EACb,WAAW,EACX,UAAU,EACV,SAAS,EACT,kBAA4B,EAC5B,OAAO,EACP,IAAI,EACJ,SAAS,EACT,KAAK,EACL,UAAU,GACX,EAAE,aAAa,+BA0Kf"}
@@ -6,6 +6,7 @@ import { CtlSlot } from "../theme/index.js";
6
6
  import { VX } from "../tokens/index.js";
7
7
  import { cx } from "../common/props.js";
8
8
  import { QueryState } from "./query-state.js";
9
+ import { assertQueryStateLike } from "../common/query-state-like.js";
9
10
  import classes from "./stat-card.module.css";
10
11
  const SPARKLINE_RIGHT_WIDTH = 72;
11
12
  const SPARKLINE_RIGHT_HEIGHT = 26;
@@ -33,6 +34,13 @@ function useSlotWidth(active) {
33
34
  width
34
35
  };
35
36
  }
37
+ function sparklineState(query) {
38
+ if (query === void 0) return void 0;
39
+ assertQueryStateLike("StatCard", query);
40
+ if (query.data !== void 0) return void 0;
41
+ if (query.isError) return { error: query.error ?? true };
42
+ return query.fetchStatus === "idle" ? void 0 : { pending: true };
43
+ }
36
44
  const TONE_RAIL_WIDTH = 3;
37
45
  const TONE_LABEL = {
38
46
  good: "Within the good threshold",
@@ -64,7 +72,12 @@ function StatCard({
64
72
  const isRender = typeof sparkline === "function";
65
73
  const bleeds = sparklinePlacement === "bleed";
66
74
  const { ref: slotRef, width: slotWidth } = useSlotWidth(isRender);
67
- const measured = { width: slotWidth, height: SPARKLINE_RIGHT_HEIGHT };
75
+ const state = sparklineState(query);
76
+ const measured = {
77
+ width: slotWidth,
78
+ height: SPARKLINE_RIGHT_HEIGHT,
79
+ ...state !== void 0 && { state }
80
+ };
68
81
  const breakdownRows = breakdown === void 0 || breakdown.length === 0 ? null : /* @__PURE__ */ jsx("dl", { className: classes.breakdown, children: breakdown.map((row) => /* @__PURE__ */ jsxs(
69
82
  "div",
70
83
  {
package/dist/index.d.ts CHANGED
@@ -13,7 +13,7 @@ export { scrollParentOf, useCoarsePointer } from './common/index.js';
13
13
  export { createBasaltTheme } from './theme/index.js';
14
14
  export { ThemeToggle } from './theme/theme-toggle.js';
15
15
  export { BasaltShell, PageBar, PageAside, useSizeClass, PageTitle, type ShellBaseProps, type SidebarBlock, type SettingsMenuItem, } from './shell/index.js';
16
- export { WidgetHeader, DeltaBadge, Section, StatCard, type StatCardBreakdownRow, type StatCardTone, StatGroup, EmptyState, SettingsSection, SettingsRow, DangerZone, } from './dashboard/index.js';
16
+ export { WidgetHeader, DeltaBadge, Section, StatCard, type StatCardBreakdownRow, type StatCardSparklineSize, type StatCardTone, StatGroup, EmptyState, SettingsSection, SettingsRow, DangerZone, } from './dashboard/index.js';
17
17
  export { unwrap } from './query-client.js';
18
18
  export type { BasaltRegister, SeriesKey } from './register.js';
19
19
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,EACL,kBAAkB,EAClB,KAAK,YAAY,EACjB,KAAK,mBAAmB,GACzB,MAAM,0BAA0B,CAAA;AACjC,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EACL,SAAS,EACT,QAAQ,EACR,SAAS,EACT,UAAU,EACV,YAAY,EACZ,KAAK,SAAS,GACf,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAClD,OAAO,EACL,WAAW,EACX,OAAO,EACP,SAAS,EACT,YAAY,EACZ,SAAS,EACT,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,gBAAgB,GACtB,MAAM,SAAS,CAAA;AAChB,OAAO,EACL,YAAY,EACZ,UAAU,EACV,OAAO,EACP,QAAQ,EACR,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,SAAS,EACT,UAAU,EACV,eAAe,EACf,WAAW,EACX,UAAU,GACX,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAMvC,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,EACL,kBAAkB,EAClB,KAAK,YAAY,EACjB,KAAK,mBAAmB,GACzB,MAAM,0BAA0B,CAAA;AACjC,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EACL,SAAS,EACT,QAAQ,EACR,SAAS,EACT,UAAU,EACV,YAAY,EACZ,KAAK,SAAS,GACf,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAClD,OAAO,EACL,WAAW,EACX,OAAO,EACP,SAAS,EACT,YAAY,EACZ,SAAS,EACT,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,gBAAgB,GACtB,MAAM,SAAS,CAAA;AAChB,OAAO,EACL,YAAY,EACZ,UAAU,EACV,OAAO,EACP,QAAQ,EACR,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,YAAY,EACjB,SAAS,EACT,UAAU,EACV,eAAe,EACf,WAAW,EACX,UAAU,GACX,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAMvC,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA"}
@@ -103,9 +103,11 @@
103
103
  * Block is breathing room under the band's single `--vx-divider` line — nothing aligns to it.
104
104
  * `stack-md` (12) on a phone, one rung down (`stack-sm`, 8) from `sm` up where the inline gutter
105
105
  * is 16: the page then sits 16 from the seam and 8 under the header, rather than the 20/12 it
106
- * used to.
106
+ * used to. The value rides `--basalt-main-pad-block`, which descendants inherit: the data
107
+ * table's page-sticky offset reads it instead of repeating this module's `sm` switch.
107
108
  */
108
- padding: var(--vx-space-stack-md) var(--app-shell-padding);
109
+ --basalt-main-pad-block: var(--vx-space-stack-md);
110
+ padding: var(--basalt-main-pad-block) var(--app-shell-padding);
109
111
  /* Without this a wide flex/grid child (a bare table, a nowrap control row) sizes Main to ITSELF. */
110
112
  min-width: 0;
111
113
  overflow-y: auto;
@@ -124,7 +126,7 @@
124
126
 
125
127
  @media (min-width: 52.5em) {
126
128
  .main {
127
- padding-block: var(--vx-space-stack-sm);
129
+ --basalt-main-pad-block: var(--vx-space-stack-sm);
128
130
  }
129
131
  }
130
132
 
@@ -1 +1 @@
1
- {"version":3,"file":"app-sidebar.d.ts","sourceRoot":"","sources":["../../src/shell/app-sidebar.tsx"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAElD,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAe,cAAc,EAAE,MAAM,aAAa,CAAA;AAI5E,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAE1E,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAgBjF,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG;IAC1C;;;;OAIG;IACH,KAAK,EAAE,WAAW,GAAG;QAAE,IAAI,CAAC,EAAE,eAAe,EAAE,CAAA;KAAE,CAAA;IACjD,QAAQ,EAAE,cAAc,EAAE,CAAA;IAC1B,SAAS,EAAE,OAAO,CAAA;IAClB;;;;OAIG;IACH,gBAAgB,EAAE,MAAM,IAAI,CAAA;IAC5B;;;;;;;;;;;OAWG;IACH,iBAAiB,CAAC,EAAE,gBAAgB,EAAE,CAAA;IACtC;;;;;;;;;;;;;;;;OAgBG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;IACvC;;;;OAIG;IACH,OAAO,CAAC,EAAE,kBAAkB,CAAA;IAC5B;;;;OAIG;IACH,MAAM,CAAC,EAAE,mBAAmB,GAAG;QAAE,OAAO,CAAC,EAAE,oBAAoB,CAAA;KAAE,CAAA;IACjE;;;;;;;;;;OAUG;IACH,MAAM,CAAC,EAAE,YAAY,EAAE,CAAA;CACxB,CAAA;AAiUD,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,+BA2KhD"}
1
+ {"version":3,"file":"app-sidebar.d.ts","sourceRoot":"","sources":["../../src/shell/app-sidebar.tsx"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAElD,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAe,cAAc,EAAE,MAAM,aAAa,CAAA;AAI5E,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAE1E,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAgBjF,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG;IAC1C;;;;OAIG;IACH,KAAK,EAAE,WAAW,GAAG;QAAE,IAAI,CAAC,EAAE,eAAe,EAAE,CAAA;KAAE,CAAA;IACjD,QAAQ,EAAE,cAAc,EAAE,CAAA;IAC1B,SAAS,EAAE,OAAO,CAAA;IAClB;;;;OAIG;IACH,gBAAgB,EAAE,MAAM,IAAI,CAAA;IAC5B;;;;;;;;;;;OAWG;IACH,iBAAiB,CAAC,EAAE,gBAAgB,EAAE,CAAA;IACtC;;;;;;;;;;;;;;;;OAgBG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;IACvC;;;;OAIG;IACH,OAAO,CAAC,EAAE,kBAAkB,CAAA;IAC5B;;;;OAIG;IACH,MAAM,CAAC,EAAE,mBAAmB,GAAG;QAAE,OAAO,CAAC,EAAE,oBAAoB,CAAA;KAAE,CAAA;IACjE;;;;;;;;;;OAUG;IACH,MAAM,CAAC,EAAE,YAAY,EAAE,CAAA;CACxB,CAAA;AAmUD,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,+BA2KhD"}
@@ -163,7 +163,7 @@ function NavSection({ section, collapsed }) {
163
163
  ]
164
164
  }
165
165
  ),
166
- /* @__PURE__ */ jsx(Collapse, { expanded: !folded, children: /* @__PURE__ */ jsx(Stack, { gap: 1, children: items }) })
166
+ /* @__PURE__ */ jsx(Collapse, { expanded: !folded || collapsed, children: /* @__PURE__ */ jsx(Stack, { gap: 1, children: items }) })
167
167
  ] });
168
168
  }
169
169
  function NavItemRow({ item, collapsed }) {
@@ -70,20 +70,14 @@
70
70
  * one rung down to `stack-sm` from `sm` up). A sticky `<thead>` resolves against the scrollport's
71
71
  * CONTENT edge, so at `top: 0` it parked that padding below the band and every body row scrolled
72
72
  * through the gap. A negative offset equal to the padding moves the stick line out to the band's
73
- * edge, flush. The two media/token pairs below mirror `.main` there — the layout suite
74
- * (`data-table.layout.test.ts`, invariant 8) fails on drift. A consumer `stickyHeaderOffset` is an
73
+ * edge, flush. The padding is read from `--basalt-main-pad-block`, which `.main` sets and owns
74
+ * (including its `sm` switch), so the two cannot drift; `data-table.layout.test.ts` invariant 8
75
+ * pins both steps. A consumer `stickyHeaderOffset` is an
75
76
  * inline `--table-sticky-header-offset` on the table and beats this class, so it still wins.
76
77
  * Outside a shell no `.mantine-AppShell-main` ancestor matches and the offset stays 0.
77
78
  */
78
79
  :global(.mantine-AppShell-main) .pageSticky {
79
- --table-sticky-header-offset: calc(-1 * var(--vx-space-stack-md));
80
- }
81
-
82
- /* theme-allow raw-media-query — mirrors app-main.module.css's own `sm` switch for Main's block padding; invariant 8 in data-table.layout.test.ts fails on drift */
83
- @media (min-width: 52.5em) {
84
- :global(.mantine-AppShell-main) .pageSticky {
85
- --table-sticky-header-offset: calc(-1 * var(--vx-space-stack-sm));
86
- }
80
+ --table-sticky-header-offset: calc(-1 * var(--basalt-main-pad-block, 0px));
87
81
  }
88
82
 
89
83
  /*
package/llms.txt CHANGED
@@ -1,4 +1,4 @@
1
- # basalt-ui v2.0.5
1
+ # basalt-ui v2.2.0
2
2
  # Opinionated framework for Mantine-based React apps — theme, app shell, TanStack Charts, toolchain presets
3
3
  #
4
4
  # Entry points: use the exact import specifier below; do not guess.
@@ -13,7 +13,7 @@
13
13
  # A subpath's `Install:` line is its required peers; `OptionalPeers:` are import()-only, install on use.
14
14
 
15
15
  Package: basalt-ui
16
- Version: 2.0.5
16
+ Version: 2.2.0
17
17
 
18
18
  ## basalt-ui
19
19
  Import: import { ... } from 'basalt-ui'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "basalt-ui",
3
- "version": "2.0.5",
3
+ "version": "2.2.0",
4
4
  "description": "Opinionated framework for Mantine-based React apps — theme, app shell, TanStack Charts, toolchain presets",
5
5
  "keywords": [
6
6
  "charts",
@@ -26,7 +26,22 @@ function stringField(value: unknown, key: string): string | undefined {
26
26
  return typeof field === 'string' ? field : undefined
27
27
  }
28
28
 
29
- /** Maps whatever a transport threw onto the closed `AgentError` union. */
29
+ /**
30
+ * Classifies whatever a chat transport threw or received into the closed `AgentError` union — the
31
+ * same mapping `useAgentThreadRuns` applies to `ThreadRunState.error`, so a custom transport can
32
+ * report failures identically.
33
+ *
34
+ * Input is any thrown value, matched by shape (never `instanceof`, so the optional `ai` peer is not
35
+ * loaded). First match wins:
36
+ * - `name === 'AbortError'` (an `Error` or a `DOMException`) → `{ kind: 'aborted' }`
37
+ * - numeric `statusCode` (AI SDK `APICallError`) → `{ kind: 'http', status }`
38
+ * - string `chunkType` and `chunkId` (AI SDK `UIMessageStreamError`) → `{ kind: 'stream', ... }`
39
+ * - anything else, including an `error` chunk's text, a plain object or a primitive →
40
+ * `{ kind: 'server', message }` with the value's `message` string, else `String(value)`.
41
+ *
42
+ * A `Response` is not classified: read its `status` and pass `{ statusCode: response.status }`.
43
+ * Never throws.
44
+ */
30
45
  export function toAgentError(error: unknown): AgentError {
31
46
  // By name, not `instanceof Error`: a `fetch` abort is a DOMException, which is not an Error.
32
47
  if (stringField(error, 'name') === 'AbortError') return { kind: 'aborted' }
@@ -18,6 +18,7 @@ export { aiSdkTransport } from './ai-sdk-transport'
18
18
  export { useAgentThreadRuns } from './use-agent-thread-runs'
19
19
  export type { ThreadRunState } from './use-agent-thread-runs'
20
20
  export type { AgentError } from './error'
21
+ export { toAgentError } from './error'
21
22
 
22
23
  // ── Thread contracts (the store is consumer-owned) ────────────────────────────
23
24
  export type { ChatMessage } from './history'
@@ -18,6 +18,7 @@ import type { CSSProperties, ReactElement } from 'react'
18
18
  import type { BasaltProps } from '../common/props'
19
19
  import { alpha, VX } from '../tokens'
20
20
  import { resolveContainerClass } from '../tokens/size-classes'
21
+ import { strideTicks } from './compile'
21
22
  import type { CartesianDefinition } from './compile'
22
23
  import { ChartEngine, idPrefixOf } from './host'
23
24
  import { useCoarsePointer } from '../common/use-media-query'
@@ -54,7 +55,9 @@ export type HeatmapProps<T> = BasaltProps & {
54
55
  tooltip?: false | { rows?: (d: T) => readonly ChartTooltipRow[] }
55
56
  /** A fixed height, or one per container class. Default: by row count (160–420px). */
56
57
  height?: number | ResponsiveChartHeight
57
- /** Fill the parent's height instead (the parent needs a definite height). Overrides `height`. */
58
+ /** Fill the parent's height instead. Overrides `height`. CSS-owned like every other kind's box
59
+ * (`ChartBox`, no measuring, SSR-stable), so the parent needs a definite CSS height — a grid or
60
+ * flex stretch, `h-full`, an explicit height — which no kind can read off an auto-height parent. */
58
61
  matchHeight?: boolean
59
62
  state?: ChartState
60
63
  }
@@ -149,8 +152,9 @@ export function Heatmap<T>(props: HeatmapProps<T>): ReactElement {
149
152
  return {
150
153
  chart: ({ width }: ChartBuildContext): StoredChartSpec => {
151
154
  const ticks = { line: false as const, size: 0 }
155
+ const cls = resolveContainerClass(width)
152
156
  const tickLabels = {
153
- fontSize: resolveContainerClass(width) === 'wide' ? VX.text.micro : VX.text.nano,
157
+ fontSize: cls === 'wide' ? VX.text.micro : VX.text.nano,
154
158
  opacity: 1,
155
159
  thin: true,
156
160
  }
@@ -190,7 +194,11 @@ export function Heatmap<T>(props: HeatmapProps<T>): ReactElement {
190
194
  scale: scaleBand<string>().domain(cols).paddingInner(0).paddingOuter(0),
191
195
  axis: {
192
196
  line: AXIS_LINE,
193
- ticks: { values: cols, format: (v) => formatCol(String(v)), ...ticks },
197
+ ticks: {
198
+ values: strideTicks(cols, cls),
199
+ format: (v) => formatCol(String(v)),
200
+ ...ticks,
201
+ },
194
202
  tickLabels,
195
203
  },
196
204
  },
@@ -18,7 +18,9 @@ import type { CartesianDefinition } from './compile'
18
18
  import { chartCurve } from './curves'
19
19
  import { barMark } from './marks'
20
20
  import { ChartEngine, idPrefixOf } from './host'
21
+ import { ChartPlaceholder, resolveChartState } from './state'
21
22
  import { CHART_THEME } from './theme'
23
+ import type { ChartState } from './types'
22
24
 
23
25
  export type SparklineProps = BasaltProps & {
24
26
  /** The accessible name. Required: a chart without one is an unlabeled image. */
@@ -36,6 +38,9 @@ export type SparklineProps = BasaltProps & {
36
38
  emphasizeLast?: boolean
37
39
  /** Default `VX.accent`. */
38
40
  emphasisColor?: string
41
+ /** Pending / error / empty, the same contract as every other kind. The placeholder fills the
42
+ * sparkline's own `width` × `height`, so a loading tile does not shift when the trend lands. */
43
+ state?: ChartState
39
44
  }
40
45
 
41
46
  type Point = { i: number; v: number | null }
@@ -145,6 +150,7 @@ export function Sparkline(props: SparklineProps): ReactElement {
145
150
  color = VX.faint,
146
151
  emphasizeLast = false,
147
152
  emphasisColor = VX.accent,
153
+ state,
148
154
  className,
149
155
  style,
150
156
  } = props
@@ -156,13 +162,15 @@ export function Sparkline(props: SparklineProps): ReactElement {
156
162
  if (range === null) return null
157
163
  return compileSparkline(rows, range, mark, color, emphasizeLast ? emphasisColor : null)
158
164
  }, [data, mark, color, emphasizeLast, emphasisColor])
165
+ const resolved = resolveChartState(state)
159
166
  return (
160
167
  <div
161
168
  data-basalt-sparkline=""
162
169
  {...(className !== undefined && { className })}
163
170
  style={{ width: width ?? '100%', height, minWidth: 0, ...style }}
164
171
  >
165
- {definition !== null && (
172
+ {resolved !== null && <ChartPlaceholder state={state} height={height} compact />}
173
+ {resolved === null && definition !== null && (
166
174
  <ChartEngine
167
175
  definition={definition}
168
176
  ariaLabel={ariaLabel}
@@ -196,6 +196,16 @@ function xTickPolicy<X extends ChartX>(
196
196
  return { count: xTicks }
197
197
  }
198
198
 
199
+ /**
200
+ * Every n-th value, n the smallest stride that leaves at most the class's x tick count (the same
201
+ * ladder a continuous x axis counts to). A band axis labels every value it is given and only drops
202
+ * the ones that collide, so a short-label grid (18 hour columns) would otherwise draw all of them.
203
+ */
204
+ export function strideTicks<V>(values: readonly V[], cls: ContainerClass): V[] {
205
+ const stride = Math.max(1, Math.ceil(values.length / LADDER[cls].xTicks))
206
+ return values.filter((_, i) => i % stride === 0)
207
+ }
208
+
199
209
  export function xGuide<X extends ChartX>(opts: {
200
210
  model: XScaleModel<X>
201
211
  xTicks: number | readonly X[] | undefined
@@ -45,6 +45,9 @@ export function ChartPlaceholder(props: {
45
45
  state: ChartState | undefined
46
46
  height?: ChartBoxHeight
47
47
  action?: ReactNode
48
+ /** For a box too small to hold a sentence (a sparkline): error and empty show a `—` and carry the
49
+ * label as the accessible name and the hover title. */
50
+ compact?: boolean
48
51
  }): ReactElement | null {
49
52
  const kind = resolveChartState(props.state)
50
53
  if (kind === null) return null
@@ -60,20 +63,29 @@ export function ChartPlaceholder(props: {
60
63
  kind === 'error'
61
64
  ? toErrorMessage(props.state?.error, 'The chart failed to load.')
62
65
  : emptyLabel(props.state)
63
- const body = (
66
+ const body = props.compact ? (
67
+ <span aria-hidden="true">—</span>
68
+ ) : (
64
69
  <>
65
70
  <span>{label}</span>
66
71
  {props.action}
67
72
  </>
68
73
  )
74
+ const name = props.compact ? { 'aria-label': label, title: label } : {}
69
75
  if (kind === 'error')
70
76
  return fit(
71
- <div role="alert" data-basalt-chart-state={kind} className={stateClass} style={style}>
77
+ <div
78
+ role="alert"
79
+ data-basalt-chart-state={kind}
80
+ className={stateClass}
81
+ style={style}
82
+ {...name}
83
+ >
72
84
  {body}
73
85
  </div>,
74
86
  )
75
87
  return fit(
76
- <output data-basalt-chart-state={kind} className={stateClass} style={style}>
88
+ <output data-basalt-chart-state={kind} className={stateClass} style={style} {...name}>
77
89
  {body}
78
90
  </output>,
79
91
  )
@@ -169,7 +169,8 @@ export type ChartState = {
169
169
  pending?: boolean
170
170
  /** Truthy = the query failed; an `Error`'s message is the label. */
171
171
  error?: unknown
172
- /** `true`, or the empty label itself. */
172
+ /** `true`, or the empty label itself. The label is the one way to say WHY (`'No runs match the
173
+ * filter'` vs `'No runs yet'`): § 9 fixes the state shape, so there is no reason field. */
173
174
  empty?: boolean | string
174
175
  }
175
176
 
package/src/cli/doctor.ts CHANGED
@@ -395,7 +395,7 @@ export function doctor(invocationCwd: string = process.cwd(), flags: string[] =
395
395
  ],
396
396
  [
397
397
  'dropped-subpaths: no import of a basalt-ui subpath the install lacks',
398
- droppedSubpathsCheck(cwd, cfg, install),
398
+ droppedSubpathsCheck(appDir, cfg, install),
399
399
  ],
400
400
  [
401
401
  'app-wiring: the entry mounts BasaltProvider and the router tree is generated',
@@ -404,7 +404,7 @@ export function doctor(invocationCwd: string = process.cwd(), flags: string[] =
404
404
  ['exact-peers: installed exact-pinned peers match basalt-ui', exactPeersCheck(cwd, install)],
405
405
  [
406
406
  'subpath-peers: every imported basalt-ui subpath has its required peers installed',
407
- subpathPeersCheck(cwd, cfg, install),
407
+ subpathPeersCheck(appDir, cfg, install),
408
408
  ],
409
409
  [
410
410
  'config-keys: the basalt config block sets only keys basalt-ui reads',
@@ -1,7 +1,12 @@
1
1
  export { WidgetHeader } from './widget-header'
2
2
  export { DeltaBadge } from './delta-badge'
3
3
  export { Section } from './section'
4
- export { StatCard, type StatCardBreakdownRow, type StatCardTone } from './stat-card'
4
+ export {
5
+ StatCard,
6
+ type StatCardBreakdownRow,
7
+ type StatCardSparklineSize,
8
+ type StatCardTone,
9
+ } from './stat-card'
5
10
  export { StatGroup } from './stat-group'
6
11
  export { EmptyState } from './empty-state'
7
12
  export { SettingsSection, SettingsRow, DangerZone } from './settings-section'
@@ -23,7 +23,9 @@
23
23
  * here. Pass your own sparkline at the call site (basalt ships no `Sparkline` kind in 2.0.0).
24
24
  *
25
25
  * `query` hands the card's pending/error/empty branch to `QueryState` at the `'section'` tier,
26
- * rendered under the header rather than over it — the title row is chrome and must not flicker.
26
+ * rendered under the header rather than over it — the title row is chrome and must not flicker. The
27
+ * same query also reaches a render-prop `sparkline` as `size.state`, so `<Sparkline {...size} />`
28
+ * shows its sized pending / error placeholder with no hand-rolled `'—'`.
27
29
  *
28
30
  * `tone` marks the card as past a threshold with an accent rail down its leading edge. It exists
29
31
  * because `value` is typed `string`: without it, a card whose number has crossed a threshold reads
@@ -106,6 +108,7 @@ import { cx } from '../common/props'
106
108
  import type { BasaltProps, SlotStylesProps, Tone } from '../common/props'
107
109
  import { QueryState } from './query-state'
108
110
  import type { QueryStateLike } from './query-state'
111
+ import { assertQueryStateLike } from '../common/query-state-like'
109
112
  import classes from './stat-card.module.css'
110
113
 
111
114
  /**
@@ -160,6 +163,13 @@ const SPARKLINE_RIGHT_HEIGHT = 26
160
163
  export type StatCardSparklineSize = {
161
164
  width: number
162
165
  height: number
166
+ /**
167
+ * The card's `query` as a chart state — present only while the sparkline has nothing to draw
168
+ * (pending, or failed with no data), so `<Sparkline {...size} />` forwards it and the tile shows a
169
+ * sized placeholder instead of a hand-rolled `'—'`. Structurally a `ChartState`: this module
170
+ * stays chart-free. Absent without a `query`, and once data has arrived.
171
+ */
172
+ state?: { pending?: boolean; error?: unknown; empty?: boolean }
163
173
  }
164
174
 
165
175
  /**
@@ -199,6 +209,18 @@ function useSlotWidth(active: boolean): {
199
209
  }
200
210
  }
201
211
 
212
+ /** The `query` as a sparkline state, by the same precedence as {@link QueryState}: an error with no
213
+ * data is an error, no data and not idle is pending, anything else draws. */
214
+ function sparklineState(
215
+ query: QueryStateLike<unknown> | undefined,
216
+ ): StatCardSparklineSize['state'] {
217
+ if (query === undefined) return undefined
218
+ assertQueryStateLike('StatCard', query)
219
+ if (query.data !== undefined) return undefined
220
+ if (query.isError) return { error: query.error ?? true }
221
+ return query.fetchStatus === 'idle' ? undefined : { pending: true }
222
+ }
223
+
202
224
  /** The rail's width, in px. Wide enough to read as a deliberate mark at a glance, narrow enough
203
225
  * that it cannot be mistaken for the card's own edge. */
204
226
  const TONE_RAIL_WIDTH = 3
@@ -270,8 +292,12 @@ export type StatCardProps = BasaltProps &
270
292
  * measured width is the card's own inner width including the bled inset; with `'right'` it is the
271
293
  * fixed 72×26 slot (see {@link SPARKLINE_RIGHT_WIDTH} for why that one is not measured).
272
294
  *
295
+ * A render prop is a sanctioned pattern here, so `react/no-unstable-nested-components` is
296
+ * configured with `allowAsProps` in the shipped preset. Spread the size (`{...size}`) to forward
297
+ * `state` along with the box.
298
+ *
273
299
  * @example
274
- * sparkline={({ width, height }) => <MySparkline data={history} width={width} height={height} />}
300
+ * sparkline={(size) => <Sparkline ariaLabel="Active users" data={history} {...size} />}
275
301
  */
276
302
  sparkline?: ReactNode | ((size: StatCardSparklineSize) => ReactNode)
277
303
  /** Where `sparkline` sits. `'bleed'` (default) is today's full-width row bled to the card edges;
@@ -313,7 +339,12 @@ export function StatCard({
313
339
  const bleeds = sparklinePlacement === 'bleed'
314
340
  // Only a render prop needs a measurement; a plain node sizes itself.
315
341
  const { ref: slotRef, width: slotWidth } = useSlotWidth(isRender)
316
- const measured: StatCardSparklineSize = { width: slotWidth, height: SPARKLINE_RIGHT_HEIGHT }
342
+ const state = sparklineState(query)
343
+ const measured: StatCardSparklineSize = {
344
+ width: slotWidth,
345
+ height: SPARKLINE_RIGHT_HEIGHT,
346
+ ...(state !== undefined && { state }),
347
+ }
317
348
 
318
349
  // A `<dl>`, not a table and not a stack of divs: each row IS a term and its value, which is the
319
350
  // one semantic that survives a reader meeting the card out of context.
package/src/index.ts CHANGED
@@ -40,6 +40,7 @@ export {
40
40
  Section,
41
41
  StatCard,
42
42
  type StatCardBreakdownRow,
43
+ type StatCardSparklineSize,
43
44
  type StatCardTone,
44
45
  StatGroup,
45
46
  EmptyState,
@@ -103,9 +103,11 @@
103
103
  * Block is breathing room under the band's single `--vx-divider` line — nothing aligns to it.
104
104
  * `stack-md` (12) on a phone, one rung down (`stack-sm`, 8) from `sm` up where the inline gutter
105
105
  * is 16: the page then sits 16 from the seam and 8 under the header, rather than the 20/12 it
106
- * used to.
106
+ * used to. The value rides `--basalt-main-pad-block`, which descendants inherit: the data
107
+ * table's page-sticky offset reads it instead of repeating this module's `sm` switch.
107
108
  */
108
- padding: var(--vx-space-stack-md) var(--app-shell-padding);
109
+ --basalt-main-pad-block: var(--vx-space-stack-md);
110
+ padding: var(--basalt-main-pad-block) var(--app-shell-padding);
109
111
  /* Without this a wide flex/grid child (a bare table, a nowrap control row) sizes Main to ITSELF. */
110
112
  min-width: 0;
111
113
  overflow-y: auto;
@@ -124,7 +126,7 @@
124
126
 
125
127
  @media (min-width: 52.5em) {
126
128
  .main {
127
- padding-block: var(--vx-space-stack-sm);
129
+ --basalt-main-pad-block: var(--vx-space-stack-sm);
128
130
  }
129
131
  }
130
132
 
@@ -327,7 +327,9 @@ function NavSection({ section, collapsed }: { section: SidebarSection; collapsed
327
327
  <SectionLabel>{section.label}</SectionLabel>
328
328
  <IconChevron open={!folded} />
329
329
  </UnstyledButton>
330
- <Collapse expanded={!folded}>
330
+ {/* The rail hides the header (the only way to unfold), so a folded section would strand its
331
+ destinations and their badges: the rail ignores the fold; expanding restores it. */}
332
+ <Collapse expanded={!folded || collapsed}>
331
333
  <Stack gap={1}>{items}</Stack>
332
334
  </Collapse>
333
335
  </div>
@@ -70,20 +70,14 @@
70
70
  * one rung down to `stack-sm` from `sm` up). A sticky `<thead>` resolves against the scrollport's
71
71
  * CONTENT edge, so at `top: 0` it parked that padding below the band and every body row scrolled
72
72
  * through the gap. A negative offset equal to the padding moves the stick line out to the band's
73
- * edge, flush. The two media/token pairs below mirror `.main` there — the layout suite
74
- * (`data-table.layout.test.ts`, invariant 8) fails on drift. A consumer `stickyHeaderOffset` is an
73
+ * edge, flush. The padding is read from `--basalt-main-pad-block`, which `.main` sets and owns
74
+ * (including its `sm` switch), so the two cannot drift; `data-table.layout.test.ts` invariant 8
75
+ * pins both steps. A consumer `stickyHeaderOffset` is an
75
76
  * inline `--table-sticky-header-offset` on the table and beats this class, so it still wins.
76
77
  * Outside a shell no `.mantine-AppShell-main` ancestor matches and the offset stays 0.
77
78
  */
78
79
  :global(.mantine-AppShell-main) .pageSticky {
79
- --table-sticky-header-offset: calc(-1 * var(--vx-space-stack-md));
80
- }
81
-
82
- /* theme-allow raw-media-query — mirrors app-main.module.css's own `sm` switch for Main's block padding; invariant 8 in data-table.layout.test.ts fails on drift */
83
- @media (min-width: 52.5em) {
84
- :global(.mantine-AppShell-main) .pageSticky {
85
- --table-sticky-header-offset: calc(-1 * var(--vx-space-stack-sm));
86
- }
80
+ --table-sticky-header-offset: calc(-1 * var(--basalt-main-pad-block, 0px));
87
81
  }
88
82
 
89
83
  /*