basalt-ui 1.8.0 → 1.10.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 +1 -0
- package/CHANGELOG.md +25 -0
- package/README.md +64 -34
- package/agent/rules/basalt-agent.md +49 -6
- package/agent/rules/basalt-charts.md +74 -5
- package/configs/oxlint-plugin.js +279 -0
- package/configs/oxlint-plugin.test.ts +216 -1
- package/configs/oxlint.json +5 -2
- package/dist/agent/use-agent-thread-runs.d.ts.map +1 -1
- package/dist/agent/use-agent-thread-runs.js +1 -0
- package/dist/agent-chat/index.d.ts +4 -3
- package/dist/agent-chat/index.d.ts.map +1 -1
- package/dist/charts/hooks/useHoverSync.d.ts +18 -2
- package/dist/charts/hooks/useHoverSync.d.ts.map +1 -1
- package/dist/charts/hooks/useHoverSync.js +12 -7
- package/dist/charts/index.d.ts +1 -0
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/charts/index.js +6 -0
- package/dist/charts/kinds/Bars.d.ts +2 -0
- package/dist/charts/kinds/Bars.d.ts.map +1 -1
- package/dist/charts/kinds/Bars.js +3 -1
- package/dist/charts/kinds/Donut.d.ts +2 -0
- package/dist/charts/kinds/Donut.d.ts.map +1 -1
- package/dist/charts/kinds/Donut.js +8 -4
- package/dist/charts/kinds/DualPanel.d.ts +2 -0
- package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
- package/dist/charts/kinds/DualPanel.js +2 -1
- package/dist/charts/kinds/Heatmap.d.ts +8 -0
- package/dist/charts/kinds/Heatmap.d.ts.map +1 -1
- package/dist/charts/kinds/Heatmap.js +7 -3
- package/dist/charts/kinds/MultiLine.d.ts +2 -0
- package/dist/charts/kinds/MultiLine.d.ts.map +1 -1
- package/dist/charts/kinds/MultiLine.js +2 -1
- package/dist/charts/kinds/StackedArea.d.ts +2 -0
- package/dist/charts/kinds/StackedArea.d.ts.map +1 -1
- package/dist/charts/kinds/StackedArea.js +2 -1
- package/dist/charts/kinds/ZonedLine.d.ts +2 -0
- package/dist/charts/kinds/ZonedLine.d.ts.map +1 -1
- package/dist/charts/kinds/ZonedLine.js +2 -1
- package/dist/charts/primitives/Axes.d.ts +6 -1
- package/dist/charts/primitives/Axes.d.ts.map +1 -1
- package/dist/charts/primitives/Axes.js +3 -2
- package/dist/charts/primitives/ChartFrame.d.ts +8 -1
- package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
- package/dist/charts/primitives/ChartFrame.js +8 -4
- package/dist/charts/primitives/ChartPending.d.ts +34 -0
- package/dist/charts/primitives/ChartPending.d.ts.map +1 -0
- package/dist/charts/primitives/ChartPending.js +23 -0
- package/dist/charts/primitives/ChartTooltip.d.ts +5 -2
- package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
- package/dist/charts/primitives/ChartTooltip.js +6 -1
- package/dist/charts/primitives/HoverOverlay.d.ts +5 -4
- package/dist/charts/primitives/HoverOverlay.d.ts.map +1 -1
- package/dist/charts/primitives/HoverOverlay.js +4 -2
- package/dist/cli/index.d.ts +55 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +217 -5
- package/dist/dashboard/stat-card.d.ts.map +1 -1
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +13 -5
- package/dist/surfaces.d.ts +10 -0
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +21 -0
- package/llms.txt +9 -2
- package/package.json +8 -5
- package/src/agent/ai-sdk-transport.test.ts +269 -0
- package/src/agent/parts.test.ts +261 -0
- package/src/agent/thread.test.ts +147 -0
- package/src/agent/use-agent-stream.test.tsx +231 -0
- package/src/agent/use-agent-thread-runs.resume.test.tsx +261 -0
- package/src/agent/use-agent-thread-runs.test.tsx +398 -0
- package/src/agent/use-agent-thread-runs.ts +7 -1
- package/src/agent/use-agent-thread-runs.wedge.test.tsx +299 -0
- package/src/agent-chat/index.ts +4 -3
- package/src/charts/hooks/useHoverSync.ts +43 -7
- package/src/charts/index.ts +6 -0
- package/src/charts/kinds/Bars.tsx +4 -0
- package/src/charts/kinds/Donut.tsx +10 -4
- package/src/charts/kinds/DualPanel.tsx +5 -1
- package/src/charts/kinds/Heatmap.tsx +19 -2
- package/src/charts/kinds/MultiLine.tsx +4 -1
- package/src/charts/kinds/StackedArea.tsx +4 -1
- package/src/charts/kinds/ZonedLine.tsx +4 -1
- package/src/charts/primitives/Axes.tsx +7 -1
- package/src/charts/primitives/ChartFrame.test.tsx +67 -0
- package/src/charts/primitives/ChartFrame.tsx +17 -4
- package/src/charts/primitives/ChartPending.tsx +59 -0
- package/src/charts/primitives/ChartTooltip.tsx +15 -3
- package/src/charts/primitives/HoverOverlay.tsx +12 -6
- package/src/cli/index.ts +439 -5
- package/src/cli/placement-engine.test.ts +104 -0
- package/src/dashboard/stat-card.test.tsx +5 -3
- package/src/dashboard/stat-card.tsx +12 -0
- package/src/guard/check-source.test.ts +20 -0
- package/src/guard/index.ts +20 -9
- package/src/provider/index.test.tsx +6 -5
- package/src/surfaces.ts +22 -0
- package/src/theme/use-basalt-spacing.test.tsx +7 -4
- package/src/tokens/build-fonts-css.test.ts +3 -2
package/AGENTS.md
CHANGED
|
@@ -23,6 +23,7 @@ Toolchain: Bun runtime, oxlint + oxfmt, conventional commits (empty scope), `mas
|
|
|
23
23
|
| `basalt-ui/data/table` | mantine-coupled | BasaltDataTable: a sortable data table over TanStack Table, rendered with Mantine (Mantine-rendered) |
|
|
24
24
|
| `basalt-ui/data/virtual` | mantine-coupled | BasaltVirtualList: a windowed virtual list over TanStack Virtual, rendered with Mantine (Mantine-rendered) |
|
|
25
25
|
| `basalt-ui/agent` | headless | Headless streaming-chat layer: useAgentStream, aiSdkTransport (recommended default) + edenTransport, PartList, plus the multi-thread createThreadsStore + useAgentThreadRuns + outcome-resolver seam (Mantine-free) |
|
|
26
|
+
| `basalt-ui/agent-chat` | mantine-coupled | Mantine-styled thread-chat components over basalt-ui/agent: ThreadWorkspace, ThreadFeed, ThreadOutcomeCard, ThreadDetailPanel, Composer, ThreadTranscript, threadPartRenderers (Mantine-coupled). remend and motion are required, not optional — ThreadTranscript imports content/markdown.tsx (remend eagerly), and ThreadFeed/ThreadDetailPanel import motion/react eagerly, so this subpath fails to resolve without both installed even though peerDependenciesMeta marks them optional (npm has no per-subpath optionality). |
|
|
26
27
|
| `basalt-ui/content` | mantine-coupled | Prose (article/chat typography), CodeBlock (shiki, optional peer), Callout, TableOfContents, ReadingProgress, Markdown (react-markdown + remark-gfm, optional peers, streaming-aware), MermaidDiagram (beautiful-mermaid, optional peer), mdxComponents/createMdxComponents, ArticleLayout (docs-page frame), ArticleCard/ArticleGrid (overview cards), Article model (sortArticles/filterArticles/formatArticleDate), ArticleFilterBar (category/tags filter UI), toArticleActions (Spotlight projector, @mantine/spotlight type-only), GuideLink/GuideDrawer (contextual-help drawer) — the content/prose surface |
|
|
27
28
|
| `basalt-ui/state` | headless | createPersistedState (versioned localStorage) + useOnlineStatus — Mantine-free state primitives |
|
|
28
29
|
| `basalt-ui/connectivity` | mantine-coupled | ConnectivityProvider (aggregates browser online/offline, React Query onlineManager, SSE, and health-check pings into one status), useConnectivity, and ConnectivityIndicator — auto-mounted by BasaltProvider |
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
# [1.10.0](https://github.com/jkrumm/basalt-ui/compare/v1.9.0...v1.10.0) (2026-08-02)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* open ./agent-chat as its own door, and put the agent layer under test ([2005aa6](https://github.com/jkrumm/basalt-ui/commit/2005aa6db90d02e9310188873027ecf8b93027ff))
|
|
7
|
+
* prove the agent-chat subpath and the streaming wedge in the playground ([caff9c4](https://github.com/jkrumm/basalt-ui/commit/caff9c4f4a3d92259a1e8a88348474c90dc4c18a))
|
|
8
|
+
|
|
9
|
+
# [1.9.0](https://github.com/jkrumm/basalt-ui/compare/v1.8.0...v1.9.0) (2026-08-02)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* stop seeding repo-root files into a subdirectory package ([cd2fa7e](https://github.com/jkrumm/basalt-ui/commit/cd2fa7ea851eb1c73943e6bbdad5211b2fc65200))
|
|
15
|
+
* stop the layout guards firing where their remedy is banned ([6851a90](https://github.com/jkrumm/basalt-ui/commit/6851a908293cb5c46dc6e7c4c01185d3ac909928))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* capture chart hover through pointer events ([4f53f4b](https://github.com/jkrumm/basalt-ui/commit/4f53f4bd9ff2bb4ededb8db7ba9bf6049a62dc48))
|
|
21
|
+
* give charts a pending state ([c3a8d05](https://github.com/jkrumm/basalt-ui/commit/c3a8d05a4aac166d4264a8dc6b8afa2b45d1893b))
|
|
22
|
+
* let the date axis take a tick format ([5502ef5](https://github.com/jkrumm/basalt-ui/commit/5502ef5614e482a422b5f70e5701588d9612209e))
|
|
23
|
+
* portal the chart tooltip out of the svg namespace trap ([6e9c399](https://github.com/jkrumm/basalt-ui/commit/6e9c399d3d52c232765959386091d06379c35a98))
|
|
24
|
+
* resolve a sibling's hover key through a consumer seam ([c8b7c93](https://github.com/jkrumm/basalt-ui/commit/c8b7c93fb9cf0d56d940d0d466efa07a5b90508e))
|
|
25
|
+
|
|
1
26
|
# [1.8.0](https://github.com/jkrumm/basalt-ui/compare/v1.7.0...v1.8.0) (2026-08-02)
|
|
2
27
|
|
|
3
28
|
|
package/README.md
CHANGED
|
@@ -195,34 +195,55 @@ key in your `package.json`:
|
|
|
195
195
|
scanned files. Other `"basalt"` config keys (`exempt`,
|
|
196
196
|
`spacingSteps`, `forbiddenAccents`, …) are documented on the `BasaltConfig` type.
|
|
197
197
|
|
|
198
|
+
`doctor`'s `ai-major-parity` hard check fails a monorepo where workspace packages declare different
|
|
199
|
+
`ai` package majors — unless the split is intentional and written down. A producer pinned to an
|
|
200
|
+
older major with a client on a newer one, neutralized by a transform, is a locked decision, not a
|
|
201
|
+
defect:
|
|
202
|
+
|
|
203
|
+
```json
|
|
204
|
+
// package.json (repo root)
|
|
205
|
+
{
|
|
206
|
+
"basalt": {
|
|
207
|
+
"aiMajorSkewReason": "apps/api streams on ai@5, apps/dashboard parses on ai@7 — the one enum value that differs is neutralized by a producer-side TransformStream in apps/api/src/stream-transform.ts"
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
The value **is** the reason — omitting the key, or setting it to `true`/`""`, does not weaken the
|
|
213
|
+
check; `doctor` still hard-fails a detected skew exactly as if `aiMajorSkewReason` were absent. Once
|
|
214
|
+
the reason is written, `doctor` passes and echoes both the detected skew and the reason back. If the
|
|
215
|
+
majors later come back into agreement, `doctor` warns that the declaration is stale so it gets
|
|
216
|
+
deleted rather than silently surviving into the next real skew.
|
|
217
|
+
|
|
198
218
|
---
|
|
199
219
|
|
|
200
220
|
## Subpath exports
|
|
201
221
|
|
|
202
|
-
| Subpath | Mantine? | Purpose
|
|
203
|
-
| ------------------- | -------- |
|
|
204
|
-
| `.` | coupled | `BasaltProvider`, `createBasaltTheme` / `baseTheme` / `cssVariablesResolver`, `BasaltShell` + sidebar / mobile-nav / breadcrumbs / page-header, `NavCountBadge`, `SidebarAccount` + the provider-agnostic account contract (`BasaltAccountProps`/`State`/`Actions`), `ThreadWorkspace` + thread-chat components, shell types |
|
|
205
|
-
| `./charts` | **free** | visx chart primitives, sparklines, hooks, and token re-exports
|
|
206
|
-
| `./tokens` | **free** | `VX` token refs, `buildPaletteCss`, `defineSeries`, `seriesTokens`, `groupTokens`, `alpha`, `ColorPair` / `SeriesMap` types
|
|
207
|
-
| `./theme-lab` | coupled | `ThemeLabControls`, `applyOverrides`, `loadOverrides`, `COLOR_GROUPS` for live theme inspection
|
|
208
|
-
| `./vite` | — | `basaltViteConfig(opts)` — Vite preset for basalt-ui consumer apps; `basaltAppPlugin(opts)` — PWA head, manifest, and icon metadata derived from the token palette
|
|
209
|
-
| `./guard` | **free** | `checkSource`, `GUARD_RULES`, `Finding` types — the headless theme-guard core
|
|
210
|
-
| `./query` | **free** | `createBasaltQueryClient`, transport-agnostic `unwrap`, lazy `BasaltQueryDevtools`
|
|
211
|
-
| `./router-tanstack` | **free** | TanStack Router bridge: `useBasaltNav` (active route) + `useRouterBreadcrumbs`
|
|
212
|
-
| `./forms` | coupled | Mantine form adapter: `useBasaltForm`, `field`, `FormErrorSummary`, `useFormDraft` (Standard Schema)
|
|
213
|
-
| `./notifications` | coupled | Mantine notifications: `notify` helpers, typed registry, persisted history, `NotificationBell`
|
|
214
|
-
| `./commands` | coupled | Typed command bus + overlay controller, `toSpotlightActions`, `ShortcutsHelp`, `BasaltOverlays`
|
|
215
|
-
| `./data` | coupled | Convenience barrel pulling both peer groups: `BasaltDataTable`, `BasaltVirtualList` (Mantine-rendered) — prefer `./data/table` / `./data/virtual` for per-feature opt-in
|
|
216
|
-
| `./data/table` | coupled | `BasaltDataTable` — sortable data table over TanStack Table, rendered with Mantine
|
|
217
|
-
| `./data/virtual` | coupled | `BasaltVirtualList` — windowed virtual list over TanStack Virtual, rendered with Mantine
|
|
218
|
-
| `./agent` | **free** | Headless streaming layer (`useAgentStream`, `PartList`) + multi-thread `createThreadsStore` / `useAgentThreadRuns` / outcome seam
|
|
219
|
-
| `./
|
|
220
|
-
| `./
|
|
221
|
-
| `./
|
|
222
|
-
| `./
|
|
223
|
-
| `./
|
|
224
|
-
| `./
|
|
225
|
-
| `./
|
|
222
|
+
| Subpath | Mantine? | Purpose |
|
|
223
|
+
| ------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
224
|
+
| `.` | coupled | `BasaltProvider`, `createBasaltTheme` / `baseTheme` / `cssVariablesResolver`, `BasaltShell` + sidebar / mobile-nav / breadcrumbs / page-header, `NavCountBadge`, `SidebarAccount` + the provider-agnostic account contract (`BasaltAccountProps`/`State`/`Actions`), `ThreadWorkspace` + thread-chat components, shell types — the root entry still re-exports the thread-chat components; `./agent-chat` takes the same components standalone, without `BasaltProvider`, the shell, the dashboard composites, or `./connectivity` |
|
|
225
|
+
| `./charts` | **free** | visx chart primitives, sparklines, hooks, and token re-exports |
|
|
226
|
+
| `./tokens` | **free** | `VX` token refs, `buildPaletteCss`, `defineSeries`, `seriesTokens`, `groupTokens`, `alpha`, `ColorPair` / `SeriesMap` types |
|
|
227
|
+
| `./theme-lab` | coupled | `ThemeLabControls`, `applyOverrides`, `loadOverrides`, `COLOR_GROUPS` for live theme inspection |
|
|
228
|
+
| `./vite` | — | `basaltViteConfig(opts)` — Vite preset for basalt-ui consumer apps; `basaltAppPlugin(opts)` — PWA head, manifest, and icon metadata derived from the token palette |
|
|
229
|
+
| `./guard` | **free** | `checkSource`, `GUARD_RULES`, `Finding` types — the headless theme-guard core |
|
|
230
|
+
| `./query` | **free** | `createBasaltQueryClient`, transport-agnostic `unwrap`, lazy `BasaltQueryDevtools` |
|
|
231
|
+
| `./router-tanstack` | **free** | TanStack Router bridge: `useBasaltNav` (active route) + `useRouterBreadcrumbs` |
|
|
232
|
+
| `./forms` | coupled | Mantine form adapter: `useBasaltForm`, `field`, `FormErrorSummary`, `useFormDraft` (Standard Schema) |
|
|
233
|
+
| `./notifications` | coupled | Mantine notifications: `notify` helpers, typed registry, persisted history, `NotificationBell` |
|
|
234
|
+
| `./commands` | coupled | Typed command bus + overlay controller, `toSpotlightActions`, `ShortcutsHelp`, `BasaltOverlays` |
|
|
235
|
+
| `./data` | coupled | Convenience barrel pulling both peer groups: `BasaltDataTable`, `BasaltVirtualList` (Mantine-rendered) — prefer `./data/table` / `./data/virtual` for per-feature opt-in |
|
|
236
|
+
| `./data/table` | coupled | `BasaltDataTable` — sortable data table over TanStack Table, rendered with Mantine |
|
|
237
|
+
| `./data/virtual` | coupled | `BasaltVirtualList` — windowed virtual list over TanStack Virtual, rendered with Mantine |
|
|
238
|
+
| `./agent` | **free** | Headless streaming layer (`useAgentStream`, `PartList`) + multi-thread `createThreadsStore` / `useAgentThreadRuns` / outcome seam |
|
|
239
|
+
| `./agent-chat` | coupled | Mantine-styled thread-chat components over `./agent`: `ThreadWorkspace`, `ThreadFeed`, `ThreadOutcomeCard`, `ThreadDetailPanel`, `Composer`, `ThreadTranscript`, `threadPartRenderers` — **requires `remend` and `motion`**, not merely optional peers; see [Requirements](#requirements) |
|
|
240
|
+
| `./state` | **free** | `createPersistedState` (versioned localStorage) + `useOnlineStatus` — Mantine-free state primitives |
|
|
241
|
+
| `./connectivity` | coupled | `ConnectivityProvider` (aggregates browser/React-Query/SSE/health-check status), `useConnectivity`, `ConnectivityIndicator` — auto-mounted by `BasaltProvider` |
|
|
242
|
+
| `./content` | coupled | `Prose`, `CodeBlock`, `Callout`, `TableOfContents`, `ReadingProgress`, `Markdown`, `MermaidDiagram`, `ArticleLayout`, `ArticleCard` / `ArticleGrid`, `GuideLink` / `GuideDrawer`, `mdxComponents` |
|
|
243
|
+
| `./styles.css` | — | `@layer basalt` base styles, iOS input safety net, font stack |
|
|
244
|
+
| `./tokens.css` | **free** | Prebuilt `--vx-*` stylesheet — the default `buildPaletteCss()` output as a plain file, for a consumer with no bundler, React or Mantine; `basalt-ui tokens:css` re-emits it with a custom scheme selector |
|
|
245
|
+
| `./configs/*` | — | Raw toolchain presets — oxlint, oxfmt, tsconfig (base/react-app/node), lefthook |
|
|
246
|
+
| `./llms.txt` | — | Machine-readable surface map — one entry per published subpath with import specifier, description, layer, and optional peers |
|
|
226
247
|
|
|
227
248
|
Named exports only — no default exports.
|
|
228
249
|
|
|
@@ -429,7 +450,11 @@ bun add beautiful-mermaid # MermaidDiagram / ```mermaid fences
|
|
|
429
450
|
````
|
|
430
451
|
|
|
431
452
|
`remend` is the one peer here that is not lazy: `Markdown` imports it at the top of the module, so
|
|
432
|
-
`basalt-ui/content` fails to resolve without it even if you never render `Markdown` yourself.
|
|
453
|
+
`basalt-ui/content` fails to resolve without it even if you never render `Markdown` yourself. That
|
|
454
|
+
eager import also reaches past this subpath: both the root `.` entry and `./agent-chat`'s
|
|
455
|
+
`ThreadWorkspace` / `ThreadTranscript` import `Markdown` directly (not lazily) as their text
|
|
456
|
+
renderer, so `remend` is listed as **required** in the [Requirements](#requirements) table too, not
|
|
457
|
+
only here.
|
|
433
458
|
Everything else degrades gracefully when absent (markdown falls back to plain text, fences to
|
|
434
459
|
plain mono, mermaid renders nothing). `Markdown` is the package's **only** markdown
|
|
435
460
|
renderer — it backs long-form content and AI chat alike:
|
|
@@ -468,10 +493,11 @@ const { parts, send, status } = useAgentStream({ transport })
|
|
|
468
493
|
```
|
|
469
494
|
|
|
470
495
|
Many short chats — the `ThreadWorkspace` composite (a distilled-outcome feed + a detail panel).
|
|
471
|
-
The headless multi-thread layer lives in `./agent`; the Mantine chrome ships from
|
|
496
|
+
The headless multi-thread layer lives in `./agent`; the Mantine chrome ships from
|
|
497
|
+
`basalt-ui/agent-chat` (also re-exported from the root entry):
|
|
472
498
|
|
|
473
499
|
```tsx
|
|
474
|
-
import { ThreadWorkspace } from 'basalt-ui'
|
|
500
|
+
import { ThreadWorkspace } from 'basalt-ui/agent-chat'
|
|
475
501
|
import { createThreadsStore, heuristicOutcome } from 'basalt-ui/agent'
|
|
476
502
|
|
|
477
503
|
// Once at module scope:
|
|
@@ -541,7 +567,7 @@ bunx basalt-ui init # scaffold doctrine into a consumer repo
|
|
|
541
567
|
bunx basalt-ui sync # three-way diff against .basalt/manifest.json after a basalt-ui upgrade
|
|
542
568
|
bunx basalt-ui sync --check # CI drift gate — non-zero exit on any managed-file drift
|
|
543
569
|
bunx basalt-ui check-theme # palette guard — fails on colors that bypass the central --vx-* system
|
|
544
|
-
bunx basalt-ui doctor # check consumer repo basalt integration health
|
|
570
|
+
bunx basalt-ui doctor # check consumer repo basalt integration health: manifest presence, node_modules-vs-manifest version, spacing-scale drift, and a cross-package `ai` major-version parity check (HARD failure — walks every workspace manifest, exemptable via `basalt.aiMajorSkewReason` — see above) + warn on missing basaltAppPlugin icon files under public/
|
|
545
571
|
bunx basalt-ui info # human-readable surface map: subpath, layer, rule, skills, optional peers
|
|
546
572
|
bunx basalt-ui info --json # same map as stable JSON (InfoOutput shape)
|
|
547
573
|
```
|
|
@@ -580,12 +606,16 @@ Wire the drift gate to catch doctrine falling behind after a basalt-ui upgrade:
|
|
|
580
606
|
|
|
581
607
|
## Requirements
|
|
582
608
|
|
|
583
|
-
| Peer | Version
|
|
584
|
-
| ----------------------- |
|
|
585
|
-
| `react` / `react-dom` | `^19`
|
|
586
|
-
| `@mantine/core` | `^9.3`
|
|
587
|
-
| `@mantine/hooks` | `^9.3`
|
|
588
|
-
| `@tanstack/react-query` | —
|
|
609
|
+
| Peer | Version | Notes |
|
|
610
|
+
| ----------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
611
|
+
| `react` / `react-dom` | `^19` | required |
|
|
612
|
+
| `@mantine/core` | `^9.3` | required |
|
|
613
|
+
| `@mantine/hooks` | `^9.3` | required |
|
|
614
|
+
| `@tanstack/react-query` | — | required for the root `.` entry — `BasaltProvider` hard-requires it at build time; NOT required by `./agent-chat`, which doesn't touch it |
|
|
615
|
+
| `remend` | `1.3.0` | required for both the root `.` entry and `./agent-chat` — both export `ThreadWorkspace`/`ThreadTranscript` (`agent-chat/thread-message.tsx`), which statically imports `content/markdown.tsx`, which imports `remend` eagerly; see [`./content`](#content--prose--markdown) below |
|
|
616
|
+
| `motion` | `12.42.0` | required for both the root `.` entry and `./agent-chat` — both export `ThreadFeed`/`ThreadDetailPanel` (`agent-chat/thread-feed.tsx` / `thread-detail-panel.tsx`), which import `motion/react` eagerly; the root's `ThemeToggle` also uses it |
|
|
617
|
+
|
|
618
|
+
`./agent-chat`'s `optionalPeers` in `llms.txt`/`surfaces.ts` still lists `remend` and `motion` (npm has no per-subpath optionality in `peerDependenciesMeta`), but treat both as required when installing just this subpath — see the subpath table above.
|
|
589
619
|
|
|
590
620
|
Optional peer batteries and their packages are listed per battery above.
|
|
591
621
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
source: basalt-ui
|
|
3
|
-
description: Streaming-agent layer for basalt-ui apps — Eden-native transport, AgentPart exhaustive handling, StickToBottom, chat history, AND the multi-thread ThreadWorkspace (concurrent runs + a distilled-outcome feed + detail panel). Headless layer in ./agent; the Mantine chrome
|
|
3
|
+
description: Streaming-agent layer for basalt-ui apps — Eden-native transport, AgentPart exhaustive handling, StickToBottom, chat history, AND the multi-thread ThreadWorkspace (concurrent runs + a distilled-outcome feed + detail panel). Headless layer in ./agent (Mantine-free); the Mantine-coupled chrome ships from its own basalt-ui/agent-chat subpath (also re-exported from the root entry) and uses basalt-ui/content's Markdown for rendering. Covers authoring doctrine, the Eden #231 footgun, and the agent-chat lint guards (agent-resume-guard, agent-no-raw-usechat, ai-sdk-major) with their basalt-agent-allow escape token.
|
|
4
4
|
paths:
|
|
5
5
|
- 'src/**/*agent*'
|
|
6
6
|
- 'src/**/chat*'
|
|
@@ -200,7 +200,8 @@ Built on `createPersistedState` — SSR-safe, cross-tab via the `storage` event.
|
|
|
200
200
|
For the "many short chats" pattern — each prompt a short-lived thread, the feed showing only a
|
|
201
201
|
distilled outcome (title + summary), a right-hand detail panel to open and continue — basalt-ui
|
|
202
202
|
ships a full workspace. The headless multi-thread layer lives in `./agent` (Mantine-free); the
|
|
203
|
-
ready-built Mantine chrome ships from
|
|
203
|
+
ready-built Mantine chrome ships from `basalt-ui/agent-chat` (also re-exported from the root
|
|
204
|
+
entry).
|
|
204
205
|
|
|
205
206
|
### Headless (`basalt-ui/agent`)
|
|
206
207
|
|
|
@@ -231,10 +232,16 @@ Promise<AgentOutcome>` — the app supplies it. In production derive `{ title, s
|
|
|
231
232
|
finished run (e.g. a structured final step of your own model) and return it. `heuristicOutcome` is
|
|
232
233
|
a demo-only fallback that truncates the last assistant text — never the production path.
|
|
233
234
|
|
|
234
|
-
### Ready-built UI (`basalt-ui`, Mantine)
|
|
235
|
+
### Ready-built UI (`basalt-ui/agent-chat`, Mantine)
|
|
236
|
+
|
|
237
|
+
The Mantine-coupled chrome (`ThreadWorkspace` and the lower-level pieces below it) ships its own
|
|
238
|
+
subpath, `basalt-ui/agent-chat` — added in 1.10.0. The root `basalt-ui` entry still re-exports the
|
|
239
|
+
same components (nothing that imported from `basalt-ui` before 1.10.0 needs to change), but a
|
|
240
|
+
consumer who only needs the thread-chat UI can import from `./agent-chat` directly, without pulling
|
|
241
|
+
in `BasaltProvider`, `BasaltShell`, the dashboard composites, or `basalt-ui/connectivity`:
|
|
235
242
|
|
|
236
243
|
```tsx
|
|
237
|
-
import { ThreadWorkspace } from 'basalt-ui'
|
|
244
|
+
import { ThreadWorkspace } from 'basalt-ui/agent-chat'
|
|
238
245
|
;<ThreadWorkspace
|
|
239
246
|
useThreads={useThreads}
|
|
240
247
|
transport={transport}
|
|
@@ -251,8 +258,9 @@ composer) on the right, collapsing to a single pane below 768px. The lower-level
|
|
|
251
258
|
on the shared `MOTION_*` tokens and honours `useReducedMotion`.
|
|
252
259
|
|
|
253
260
|
**Boundary:** the headless layer (`createThreadsStore`, `useAgentThreadRuns`, the outcome types)
|
|
254
|
-
stays Mantine-free in `./agent`; the components are Mantine-coupled and ship from
|
|
255
|
-
Never add `@mantine/*` under `src/agent/**` — it is
|
|
261
|
+
stays Mantine-free in `./agent`; the components are Mantine-coupled and ship from `./agent-chat`
|
|
262
|
+
(also re-exported from the root entry). Never add `@mantine/*` under `src/agent/**` — it is
|
|
263
|
+
oxlint-enforced Mantine-free.
|
|
256
264
|
|
|
257
265
|
**No custom part types at the workspace level.** `ThreadWorkspace`, `ThreadDetailPanel`, and
|
|
258
266
|
`ThreadTranscript` render the framework's `AgentPart` union only — they are not generic over a
|
|
@@ -260,6 +268,41 @@ consumer-extended part type. If you need custom rendering for the existing part
|
|
|
260
268
|
the headless layer instead: compose your own transcript from `PartList` (`basalt-ui/agent`) with a
|
|
261
269
|
custom `components` renderer map, rather than passing a wider type through `ThreadWorkspace`.
|
|
262
270
|
|
|
271
|
+
## Lint guards (agent-chat) — a SEPARATE escape token from `theme-allow`
|
|
272
|
+
|
|
273
|
+
Three oxlint rules (1.10.0) protect the streaming/resume discipline above. All three share one
|
|
274
|
+
escape comment, `basalt-agent-allow` — deliberately NOT `theme-allow`. The two tokens are unrelated:
|
|
275
|
+
a `theme-allow` comment on a color/spacing exception never suppresses one of these, and a
|
|
276
|
+
`basalt-agent-allow` comment never suppresses a `theme-allow` finding. Don't reach for the color
|
|
277
|
+
exemption to quiet a streaming guard — mark the correct token, on the correct line, and only when
|
|
278
|
+
you actually own the guard you're bypassing.
|
|
279
|
+
|
|
280
|
+
- **`basalt/agent-resume-guard`** — flags an unguarded `useChat({ resume: true })` or a bare
|
|
281
|
+
`resumeStream()` call outside `useAgentThreadRuns`. `useAgentThreadRuns` owns single-consumer
|
|
282
|
+
discipline and StrictMode-safe reconnection; a raw resume call re-fires on every effect re-run
|
|
283
|
+
(vercel/ai#7891, no merged fix upstream). Mark `basalt-agent-allow` only if you own the guard
|
|
284
|
+
yourself.
|
|
285
|
+
- **`basalt/agent-no-raw-usechat`** — flags importing `useChat`/`useCompletion` directly from
|
|
286
|
+
`@ai-sdk/react` or `ai/react`. Use `useAgentStream`/`useAgentThreadRuns` over `aiSdkTransport`
|
|
287
|
+
instead — they add unmount abort, supersede guards, and single-consumer resume that the raw hook
|
|
288
|
+
doesn't. Type-only imports (`import type { useChat }`) are exempt; nothing to guard against at
|
|
289
|
+
runtime.
|
|
290
|
+
- **`basalt/ai-sdk-major`** — flags a file whose nearest `package.json` declares a different `ai`
|
|
291
|
+
major than the one basalt-ui itself declares as a peer. A producer and consumer on different `ai`
|
|
292
|
+
majors can throw `'Unknown chunk type'` at runtime — this is the exact defect one production
|
|
293
|
+
consumer hit (one app package on `ai@5`, a sibling on `ai@7`). This rule is PER-FILE — it only
|
|
294
|
+
sees the nearest `package.json` to the linted file, so a lint run scoped to one workspace package
|
|
295
|
+
can't catch a skew against a _different_ package. `bunx basalt-ui doctor`'s `ai-major-parity`
|
|
296
|
+
check is the cross-package counterpart: it walks every workspace manifest and is a HARD failure,
|
|
297
|
+
not a lint warning — run both, they check different things. Like its two siblings above, it
|
|
298
|
+
honours `basalt-agent-allow` on the flagged import (or the line above it) — mark it on an
|
|
299
|
+
intentional producer/consumer file. The repo-wide equivalent for `doctor`'s check is
|
|
300
|
+
`basalt.aiMajorSkewReason` in package.json (a mandatory written reason, not a line comment) — see
|
|
301
|
+
the README and the `BasaltConfig` JSDoc.
|
|
302
|
+
|
|
303
|
+
None of these three rules honour `theme-allow` — only `basalt-agent-allow`, on the flagged line or
|
|
304
|
+
the line above it.
|
|
305
|
+
|
|
263
306
|
## Stream resumption — the reconnect seam
|
|
264
307
|
|
|
265
308
|
basalt-ui ships the CLIENT-SIDE contract for resuming a run after a disconnect (reload, network
|
|
@@ -39,20 +39,34 @@ tree to keep Mantine-free and no bridge file to own — `BasaltProvider` already
|
|
|
39
39
|
color scheme to the `--vx-*` CSS variables charts read internally. Just compose the shipped
|
|
40
40
|
primitives/kinds and pull color from `VX.*`/`alpha()` (`basalt-ui/tokens`).
|
|
41
41
|
|
|
42
|
+
`check-theme`'s `inline-display` and `raw-html-layout` guards don't fire inside a chart file
|
|
43
|
+
either — both remedies point at a Mantine layout primitive (`Flex`/`Grid`/`Group`/`Box`), which the
|
|
44
|
+
Mantine-free boundary above already forbids there, so the finding would be unactionable. Reach for
|
|
45
|
+
`ChartCenter` (below, under "Pending state") when a chart file needs to center something.
|
|
46
|
+
|
|
42
47
|
## Every chart has
|
|
43
48
|
|
|
44
49
|
1. **ChartCard** wrapper — gives title + info-tooltip + extra slot, consistent margin.
|
|
45
50
|
2. **ChartLegend** — never hand-rolled legend markup. Supports `line | bar | split | splitLine`
|
|
46
51
|
shapes and optional highlight state.
|
|
47
52
|
3. **ChartTooltip** + `TooltipHeader` + `TooltipRow` + `TooltipBody` — never `@visx/tooltip` directly.
|
|
53
|
+
Portals to `document.body` (SSR-guarded: renders nothing when `document` is undefined), so it
|
|
54
|
+
can be authored anywhere in the tree, including inside an `<svg>`. A plain `<div>` there used to
|
|
55
|
+
mount in the SVG namespace — accepting every prop, throwing nothing, typechecking, passing lint
|
|
56
|
+
— and never paint; one consumer shipped eight authored tooltip rows no one had ever seen. The
|
|
57
|
+
portal also un-breaks `position: fixed` under a transformed ancestor.
|
|
48
58
|
4. **AxisLeftNumeric** / **AxisRightNumeric** + **AxisBottomDate** — never raw `<AxisLeft>`/`<AxisBottom>`/`<AxisRight>`
|
|
49
59
|
(they miss theme tokens + smart ticks). Enforced by `basalt-ui check-theme` (`raw-visx-axis` guard
|
|
50
60
|
fails the build on a raw axis in a `/charts/` file; escape via `theme-allow`), not just convention.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
61
|
+
`AxisBottomDate` takes an optional `tickFormat` (`TickFormatter<string>`, defaults to
|
|
62
|
+
`fmtAxisDate` — `DD.MM`) — the only supported exit for a sub-day window, where the default
|
|
63
|
+
collapses every tick to the same label and a raw `<AxisBottom>` isn't an option.
|
|
64
|
+
5. **HoverOverlay** for pointer capture (mouse + touch + pen — `touch-action: pan-y` lets vertical
|
|
65
|
+
page scroll pass through while a horizontal drag scrubs the chart), **HoverContext** for
|
|
66
|
+
cross-chart crosshair sync, **useChartTooltip** for tip state, **useHoverSync** for the shared
|
|
67
|
+
cursor. Wrap a group of date-aligned charts in **ChartHoverSync** (from `basalt-ui/charts`) to
|
|
68
|
+
activate cross-chart sync — hovering one chart casts a ghost crosshair on all siblings; without
|
|
69
|
+
it `useHoverSync` runs per-chart only and logs a dev warning:
|
|
56
70
|
|
|
57
71
|
```tsx
|
|
58
72
|
<ChartHoverSync>
|
|
@@ -61,12 +75,67 @@ primitives/kinds and pull color from `VX.*`/`alpha()` (`basalt-ui/tokens`).
|
|
|
61
75
|
</ChartHoverSync>
|
|
62
76
|
```
|
|
63
77
|
|
|
78
|
+
Sync is exact string match on the broadcast key by default, so two charts share a cursor only
|
|
79
|
+
when they emit identical key strings. A chart that folds/downsamples its domain for a narrow
|
|
80
|
+
viewport stops owning most of the keys its unfolded siblings broadcast — the shared crosshair
|
|
81
|
+
then lands on roughly one hover in three, worse than no shared cursor at all. Pass
|
|
82
|
+
`useHoverSync`'s `resolveKey` to map a foreign key onto whichever of this chart's own points
|
|
83
|
+
swallowed it; it only affects reading a SIBLING's broadcast key, never this chart's own hover:
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
useHoverSync({
|
|
87
|
+
data: foldedData,
|
|
88
|
+
chartId: 'load-mobile',
|
|
89
|
+
getKey: (d) => d.bucketKey,
|
|
90
|
+
xScale,
|
|
91
|
+
marginLeft,
|
|
92
|
+
// ~288 daily buckets folded into ~97 columns at this width — map an unfolded sibling's key
|
|
93
|
+
// onto the folded bucket that swallowed it.
|
|
94
|
+
resolveKey: (key) => pointByKey.get(foldKey(key)) ?? null,
|
|
95
|
+
})
|
|
96
|
+
```
|
|
97
|
+
|
|
64
98
|
6. **Theme-aware colors** via `VX.*` tokens + `alpha()`. **Never** a raw hex literal in a chart file.
|
|
65
99
|
**Never** `localStorage.getItem('theme')` — the scheme resolves via CSS vars (see Dark/light below).
|
|
66
100
|
|
|
67
101
|
**Exemption:** sparklines (`LineSparkline`, `BarSparkline` — tiny inline charts with no legend/tooltip)
|
|
68
102
|
don't have to compose ChartCard/ChartLegend/ChartTooltip — but still use `VX.*` tokens.
|
|
69
103
|
|
|
104
|
+
## Pending state (`isPending`)
|
|
105
|
+
|
|
106
|
+
"Nothing to draw" is three states, not two: **measured-and-empty**, **measured-and-absent** (a
|
|
107
|
+
genuine coverage gap), and **not-asked-yet** (the query hasn't resolved). The `data ?? []` idiom
|
|
108
|
+
collapses the third into the second — an in-flight query renders as a fully-hatched "not measured"
|
|
109
|
+
window, a positive claim the series was watched and carried nothing. Pass `isPending` instead of
|
|
110
|
+
faking an empty array:
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
import { Bars } from 'basalt-ui/charts'
|
|
114
|
+
;<Bars
|
|
115
|
+
data={data ?? []}
|
|
116
|
+
isPending={query.isPending}
|
|
117
|
+
chartId="load"
|
|
118
|
+
getX={(d) => d.date}
|
|
119
|
+
getY={(d) => d.load}
|
|
120
|
+
/>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
All seven kinds accept `isPending` and forward it to `ChartFrame` (`Heatmap` handles it locally —
|
|
124
|
+
it composes no `ChartFrame`, taking `width`/`height` directly rather than measuring). `ChartFrame`
|
|
125
|
+
with `isPending` renders `ChartPending` over the plot rect in place of `children`, suppresses the
|
|
126
|
+
legend entirely (a legend naming a series with nothing yet to point at is its own small lie), and
|
|
127
|
+
sets `aria-busy="true"` on the outer container.
|
|
128
|
+
|
|
129
|
+
`ChartPending` reserves the plot's exact footprint and draws **nothing** that could be mistaken for
|
|
130
|
+
a measurement — no axes, no gridlines, no hatching, no marks, no animation (the motion doctrine
|
|
131
|
+
bans idle pulsing) — just a faint, static, centered label (`ChartPendingProps.label`, default
|
|
132
|
+
`'Loading…'`).
|
|
133
|
+
|
|
134
|
+
`ChartCenter` (also exported from `./charts`) is the centering primitive `ChartPending` is built
|
|
135
|
+
on — it exists only because chart files can't import Mantine's `Center`/`Flex`. Reach for it
|
|
136
|
+
directly in a bespoke chart file the same way; it's deliberately minimal (`width`, `height`,
|
|
137
|
+
`children`, nothing else), not a general layout system.
|
|
138
|
+
|
|
70
139
|
## Kinds — the recurring shapes
|
|
71
140
|
|
|
72
141
|
basalt-ui ships these kinds (declarative props, generic over your point type via `getX`/`getY`):
|