semiotic 3.5.2 → 3.5.3

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 (75) hide show
  1. package/CLAUDE.md +8 -7
  2. package/README.md +15 -15
  3. package/ai/schema.json +15 -1
  4. package/ai/system-prompt.md +1 -1
  5. package/dist/components/ThemeProvider.d.ts +2 -2
  6. package/dist/components/charts/network/ProcessSankey.d.ts +22 -0
  7. package/dist/components/charts/network/SankeyDiagram.d.ts +5 -1
  8. package/dist/components/charts/network/processSankey/algorithm.d.ts +57 -0
  9. package/dist/components/charts/network/processSankey/buildScenes.d.ts +6 -0
  10. package/dist/components/charts/network/processSankey/streamingLayout.d.ts +13 -0
  11. package/dist/components/charts/realtime/RealtimeHeatmap.d.ts +2 -0
  12. package/dist/components/charts/realtime/RealtimeHistogram.d.ts +2 -0
  13. package/dist/components/charts/realtime/RealtimeLineChart.d.ts +2 -0
  14. package/dist/components/charts/realtime/RealtimeSwarmChart.d.ts +2 -0
  15. package/dist/components/charts/realtime/RealtimeWaterfallChart.d.ts +2 -0
  16. package/dist/components/charts/shared/tooltipUtils.d.ts +11 -0
  17. package/dist/components/charts/shared/types.d.ts +6 -0
  18. package/dist/components/charts/shared/useAreaSeriesSetup.d.ts +4 -1
  19. package/dist/components/charts/shared/useChartSetup.d.ts +2 -0
  20. package/dist/components/charts/shared/useCustomChartSetup.d.ts +1 -0
  21. package/dist/components/charts/shared/useNetworkChartSetup.d.ts +2 -0
  22. package/dist/components/charts/shared/withChartWrapper.d.ts +10 -3
  23. package/dist/components/charts/xy/AreaChart.d.ts +12 -2
  24. package/dist/components/charts/xy/LineChart.d.ts +26 -2
  25. package/dist/components/charts/xy/Scatterplot.d.ts +5 -1
  26. package/dist/components/semiotic.d.ts +1 -1
  27. package/dist/components/server/renderToStaticSVG.d.ts +2 -1
  28. package/dist/components/server/serverChartConfigs.d.ts +1 -0
  29. package/dist/components/server/staticAnnotations.d.ts +1 -1
  30. package/dist/components/server/themeResolver.d.ts +7 -1
  31. package/dist/components/store/ThemeStore.d.ts +7 -1
  32. package/dist/components/stream/AccessibleDataTable.d.ts +2 -2
  33. package/dist/components/stream/PipelineStore.d.ts +20 -13
  34. package/dist/components/stream/SVGOverlay.d.ts +2 -29
  35. package/dist/components/stream/accessorUtils.d.ts +2 -1
  36. package/dist/components/stream/layouts/hierarchyLayoutPlugin.d.ts +1 -1
  37. package/dist/components/stream/networkTypes.d.ts +11 -0
  38. package/dist/components/stream/ordinalTypes.d.ts +15 -1
  39. package/dist/components/stream/renderers/wedgePathBuilder.d.ts +56 -0
  40. package/dist/components/stream/types.d.ts +108 -15
  41. package/dist/components/stream/xySceneBuilders/areaGradient.d.ts +20 -0
  42. package/dist/components/stream/xySceneBuilders/barScene.d.ts +2 -2
  43. package/dist/components/stream/xySceneBuilders/candlestickScene.d.ts +2 -2
  44. package/dist/components/stream/xySceneBuilders/heatmapScene.d.ts +2 -2
  45. package/dist/components/stream/xySceneBuilders/lineScene.d.ts +4 -3
  46. package/dist/components/stream/xySceneBuilders/pointScene.d.ts +2 -2
  47. package/dist/components/stream/xySceneBuilders/ribbonScene.d.ts +107 -0
  48. package/dist/components/stream/xySceneBuilders/swarmScene.d.ts +2 -2
  49. package/dist/components/stream/xySceneBuilders/types.d.ts +9 -12
  50. package/dist/components/stream/xySceneBuilders/waterfallScene.d.ts +2 -2
  51. package/dist/geo.min.js +1 -1
  52. package/dist/geo.module.min.js +1 -1
  53. package/dist/network.min.js +1 -1
  54. package/dist/network.module.min.js +1 -1
  55. package/dist/ordinal.min.js +1 -1
  56. package/dist/ordinal.module.min.js +1 -1
  57. package/dist/realtime.min.js +1 -1
  58. package/dist/realtime.module.min.js +1 -1
  59. package/dist/semiotic-ai.min.js +1 -1
  60. package/dist/semiotic-ai.module.min.js +1 -1
  61. package/dist/semiotic-data.min.js +1 -1
  62. package/dist/semiotic-data.module.min.js +1 -1
  63. package/dist/semiotic-themes.min.js +1 -1
  64. package/dist/semiotic-themes.module.min.js +1 -1
  65. package/dist/semiotic-utils.min.js +1 -1
  66. package/dist/semiotic-utils.module.min.js +1 -1
  67. package/dist/semiotic.d.ts +1 -1
  68. package/dist/semiotic.min.js +1 -1
  69. package/dist/semiotic.module.min.js +1 -1
  70. package/dist/server.min.js +1 -1
  71. package/dist/server.module.min.js +1 -1
  72. package/dist/xy.min.js +1 -1
  73. package/dist/xy.module.min.js +1 -1
  74. package/package.json +16 -8
  75. package/dist/components/stream/xySceneBuilders/boundsScene.d.ts +0 -9
package/CLAUDE.md CHANGED
@@ -4,7 +4,7 @@
4
4
  - Install: `npm install semiotic`
5
5
  <!-- semiotic-bundle-sizes:start -->
6
6
  <!-- Auto-generated by scripts/sync-bundle-sizes.mjs — do not edit by hand. -->
7
- - **Use sub-path imports** — `semiotic/xy` (83KB gz), `semiotic/ordinal` (67KB gz), `semiotic/network` (62KB gz), `semiotic/geo` (50KB gz), `semiotic/realtime` (88KB gz), `semiotic/server` (114KB gz), `semiotic/utils` (22KB gz), `semiotic/recipes` (5KB gz), `semiotic/themes` (4KB gz), `semiotic/data` (3KB gz), `semiotic/ai` (184KB gz). Full `semiotic` is 183KB gz.
7
+ - **Use sub-path imports** — `semiotic/xy` (85KB gz), `semiotic/ordinal` (68KB gz), `semiotic/network` (63KB gz), `semiotic/geo` (51KB gz), `semiotic/realtime` (90KB gz), `semiotic/server` (117KB gz), `semiotic/utils` (22KB gz), `semiotic/recipes` (5KB gz), `semiotic/themes` (4KB gz), `semiotic/data` (3KB gz), `semiotic/ai` (188KB gz). Full `semiotic` is 186KB gz.
8
8
  <!-- semiotic-bundle-sizes:end -->
9
9
  - CLI: `npx semiotic-ai [--schema|--compact|--examples|--doctor]`
10
10
  - MCP: `npx semiotic-mcp`
@@ -15,7 +15,7 @@
15
15
  - Every HOC accepts `frameProps` for pass-through. TypeScript `strict: true`. Every HOC has error boundary + dev-mode validation.
16
16
 
17
17
  ## Common Props (all HOCs)
18
- `title`, `description` (aria-label), `summary` (sr-only), `width` (600), `height` (400), `responsiveWidth`, `responsiveHeight`, `margin`, `className`, `color` (uniform fill), `stroke` (uniform stroke color — CSS var OK), `strokeWidth` (uniform stroke width in px), `opacity` (uniform 0–1 opacity), `enableHover` (true), `tooltip` (boolean | "multi" | function | config object), `showLegend`, `showGrid` (false), `frameProps`, `onObservation`, `onClick`, `chartId`, `loading` (false), `emptyContent`, `legendInteraction` ("none"|"highlight"|"isolate"), `legendPosition` ("right"|"left"|"top"|"bottom"), `emphasis` ("primary"|"secondary"), `annotations` (array), `accessibleTable` (true), `hoverHighlight` (boolean — dims non-hovered series, requires `colorBy`), `hoverRadius` (30), `animate` (boolean | { duration?, easing?, intro? } — animated intro on first render + smooth transitions on data change; intro defaults to true when animate is enabled), `axisExtent` ("nice" default | "exact" — pins first/last tick to actual data min/max with equidistant intermediates. Applies to XY x/y axes and ordinal value axis only; no-op on network/geo/hierarchy. Explicit `tickValues` still wins.)
18
+ `title`, `description` (aria-label), `summary` (sr-only), `width` (600), `height` (400), `responsiveWidth`, `responsiveHeight`, `margin`, `className`, `color` (uniform fill), `stroke` (uniform stroke color — CSS var OK), `strokeWidth` (uniform stroke width in px), `opacity` (uniform 0–1 opacity), `enableHover` (true), `tooltip` (boolean | "multi" | function | config object), `showLegend`, `showGrid` (false), `frameProps`, `onObservation`, `onClick`, `chartId`, `loading` (false), `loadingContent` (ReactNode — replaces the default skeleton when `loading` is true; pass `false` to suppress the loading UI entirely), `emptyContent`, `legendInteraction` ("none"|"highlight"|"isolate"), `legendPosition` ("right"|"left"|"top"|"bottom"), `emphasis` ("primary"|"secondary"), `annotations` (array), `accessibleTable` (true), `hoverHighlight` (boolean — dims non-hovered series, requires `colorBy`), `hoverRadius` (30), `animate` (boolean | { duration?, easing?, intro? } — animated intro on first render + smooth transitions on data change; intro defaults to true when animate is enabled), `axisExtent` ("nice" default | "exact" — pins first/last tick to actual data min/max with equidistant intermediates. Applies to XY x/y axes and ordinal value axis only; no-op on network/geo/hierarchy. Explicit `tickValues` still wins.)
19
19
 
20
20
  **Primitive styling props** (`color`, `stroke`, `strokeWidth`, `opacity`) apply to any shape the chart draws (bars, circles, lines, wedges, rects). Precedence: top-level prop > `frameProps.*Style` function return > HOC base > theme fallback. Use CSS variables (`stroke="var(--semiotic-border)"`) for theme-aware, cascade-overridable styling. For per-datum customization, keep using the function-form `frameProps.pieceStyle` / `pointStyle` / `lineStyle` etc. — the top-level prop overlays on top of whatever the function returns.
21
21
 
@@ -23,8 +23,8 @@
23
23
 
24
24
  ## XY Charts (`semiotic/xy`)
25
25
 
26
- **LineChart** — `data`, `xAccessor` ("x"), `yAccessor` ("y"), `lineBy`, `lineDataAccessor`, `colorBy`, `colorScheme`, `curve`, `lineWidth` (2), `showPoints`, `pointRadius` (3), `fillArea` (boolean|string[]), `areaOpacity` (0.3), `lineGradient`, `anomaly`, `forecast`, `directLabel`, `gapStrategy`, `xScaleType`/`yScaleType` ("linear"|"log"|"time"), `tooltip="multi"` for hover-anywhere series comparison
27
- **AreaChart** — LineChart props + `areaBy`, `y0Accessor`, `gradientFill`, `areaOpacity` (0.7), `showLine` (true), `tooltip="multi"` for hover-anywhere area comparison
26
+ **LineChart** — `data`, `xAccessor` ("x"), `yAccessor` ("y"), `lineBy`, `lineDataAccessor`, `colorBy`, `colorScheme`, `curve`, `lineWidth` (2), `showPoints`, `pointRadius` (3), `fillArea` (boolean|string[]), `areaOpacity` (0.3), `lineGradient`, `anomaly`, `forecast`, `band` (asymmetric min/max envelope: `{ y0Accessor, y1Accessor, style?, perSeries?, interactive? }` or array for fan charts; participates in yExtent; non-interactive by default; hovered datum is enriched with `band: {y0,y1}` and `bands: [...]`), `directLabel`, `gapStrategy`, `xScaleType`/`yScaleType` ("linear"|"log"|"time"), `tooltip="multi"` for hover-anywhere series comparison
27
+ **AreaChart** — LineChart props + `areaBy`, `y0Accessor`, `gradientFill`, `areaOpacity` (0.7), `showLine` (true), `band` (same shape as LineChart — decorative envelope under the area), `tooltip="multi"` for hover-anywhere area comparison
28
28
  **DifferenceChart** — Two-series A/B comparison. Fills the area between with `seriesAColor` where A > B and `seriesBColor` where B > A; crossovers interpolated. Props: `data`, `xAccessor` ("x"), `seriesAAccessor` ("a"), `seriesBAccessor` ("b"), `seriesALabel`/`seriesBLabel`, `seriesAColor` (var(--semiotic-danger))/`seriesBColor` (var(--semiotic-info)), `showLines` (true), `lineWidth` (1.5), `showPoints` (false), `pointRadius` (3), `curve` ("linear"), `areaOpacity` (0.6), `gradientFill`, `xExtent`/`yExtent`, `pointIdAccessor`, `windowSize` (max raw rows in push buffer; FIFO eviction). Push API via `ref.current.push({x, a, b})`. Accessor outputs coerce through `toNumber` so `Date` + numeric strings are accepted.
29
29
  **StackedAreaChart** — flat array + `areaBy` (required), `colorBy`, `normalize`, `baseline` (`"zero"` default | `"wiggle"` streamgraph | `"silhouette"` centered), `stackOrder` (`"key"` alpha | `"insideOut"` largest-in-middle | `"asc"`/`"desc"`). Streamgraph: `baseline="wiggle"` + `stackOrder="insideOut"`. `baseline` ⊥ `normalize`. No `lineBy`/`lineDataAccessor`. `tooltip="multi"` lists every series at the hovered x (values interpolated between samples).
30
30
  **Scatterplot** — `data`, `xAccessor`, `yAccessor`, `colorBy`, `sizeBy`, `sizeRange`, `pointRadius` (5), `pointOpacity` (0.8), `marginalGraphics`, `regression` (boolean | "linear" | "polynomial" | "loess" | RegressionConfig — sugar for a trend-annotation overlay; sits underneath user annotations)
@@ -61,7 +61,7 @@ All ordinal: `colorBy`, `colorScheme`, `categoryFormat` (string|ReactNode), `sho
61
61
 
62
62
  **ForceDirectedGraph** — `nodes`, `edges`, `nodeIDAccessor`, `sourceAccessor`, `targetAccessor`, `colorBy`, `nodeSize`, `nodeSizeRange`, `edgeWidth`, `iterations` (300), `forceStrength` (0.1), `showLabels`, `nodeLabel`
63
63
  **SankeyDiagram** — `edges`, `nodes`, `valueAccessor`, `nodeIdAccessor`, `colorBy`, `edgeColorBy`, `orientation`, `nodeAlign`, `nodeWidth`, `nodePaddingRatio`, `showLabels`
64
- **ProcessSankey** — temporal sankey with a real time x-axis. `nodes`, `edges` (each with `startTime`/`endTime`), `domain` (required `[t0, t1]`), `axisTicks?`, `xExtentAccessor` (optional `[start, end]` lifetime per node — lane spans `min(xExtent[0], earliestEdge)` to `max(xExtent[1], latestEdge)`), `colorBy`/`colorScheme`/`showLegend`/`legendPosition`, `pairing` ("value"|"temporal"), `packing` ("off"|"reuse"), `laneOrder` ("crossing-min"|"inside-out"|"crossing-min+inside-out"|"insertion"), `lifetimeMode` ("full"|"half"), `ribbonLane` ("source"|"target"|"both"), `showLaneRails`, `showQualityReadout`, `showParticles` + `particleStyle` (same shape as SankeyDiagram, canvas + ParticlePool), `timeFormat`/`valueFormat`, push API via ref. Static-graph cycles are valid as long as edges move forward in time. Use for time-stamped flow events; use `SankeyDiagram` for static total-flow snapshots.
64
+ **ProcessSankey** — temporal sankey with a real time x-axis. `nodes`, `edges` (each with `startTime`/`endTime`), `domain` (required `[t0, t1]`), `axisTicks?`, `xExtentAccessor` (optional `[start, end]` lifetime per node — lane spans `min(xExtent[0], earliestEdge)` to `max(xExtent[1], latestEdge)`), `colorBy`/`colorScheme`/`showLegend`/`legendPosition`, `pairing` ("value"|"temporal"), `packing` ("off"|"reuse"), `laneOrder` ("crossing-min"|"inside-out"|"crossing-min+inside-out"|"insertion"), `lifetimeMode` ("full"|"half"), `ribbonLane` ("source"|"target"|"both"), `showLaneRails`, `showLabels` (default true), `showQualityReadout`, `showParticles` + `particleStyle` (same shape as SankeyDiagram, canvas + ParticlePool), `timeFormat`/`valueFormat`, push API via ref. Static-graph cycles are valid as long as edges move forward in time. Use for time-stamped flow events; use `SankeyDiagram` for static total-flow snapshots.
65
65
  **ChordDiagram** — `edges`, `nodes`, `valueAccessor`, `edgeColorBy`, `padAngle`, `showLabels`
66
66
  **TreeDiagram** — `data` (root), `layout`, `orientation`, `childrenAccessor`, `colorBy`, `colorByDepth`
67
67
  **Treemap** — `data` (root), `childrenAccessor`, `valueAccessor`, `colorBy`, `colorByDepth`, `showLabels`
@@ -243,7 +243,7 @@ All HOCs accept `annotations` (array). Coordinates use data field names.
243
243
 
244
244
  ## Theming
245
245
 
246
- CSS custom properties: `--semiotic-bg`, `--semiotic-text`, `--semiotic-text-secondary`, `--semiotic-border`, `--semiotic-grid`, `--semiotic-primary`, `--semiotic-secondary`, `--semiotic-surface`, `--semiotic-success`, `--semiotic-danger`, `--semiotic-warning`, `--semiotic-error`, `--semiotic-info`, `--semiotic-focus`, `--semiotic-font-family`, `--semiotic-annotation-color`, `--semiotic-legend-font-size`, `--semiotic-title-font-size`, `--semiotic-tick-font-family`, `--semiotic-tooltip-bg`/`text`/`radius`/`font-size`/`shadow`.
246
+ CSS custom properties: `--semiotic-bg`, `--semiotic-text`, `--semiotic-text-secondary`, `--semiotic-border`, `--semiotic-grid`, `--semiotic-primary`, `--semiotic-secondary`, `--semiotic-surface`, `--semiotic-success`, `--semiotic-danger`, `--semiotic-warning`, `--semiotic-error`, `--semiotic-info`, `--semiotic-focus`, `--semiotic-font-family`, `--semiotic-annotation-color`, `--semiotic-legend-font-size`, `--semiotic-title-font-size`, `--semiotic-tick-font-family`, `--semiotic-tick-font-size` (10px default — drives axis tick text), `--semiotic-axis-label-font-size` (12px default — drives axis-label text + foreignObject ticks), `--semiotic-tooltip-bg`/`text`/`radius`/`font-size`/`shadow`.
247
247
 
248
248
  ```jsx
249
249
  <ThemeProvider theme="tufte"> {/* Named preset */}
@@ -309,7 +309,8 @@ These rules are generated from `ai/behaviorContracts.cjs` and are consumed by `s
309
309
  - **fillArea**: `fillArea={["seriesA"]}` fills named series only. Names must match `lineBy`/`colorBy` keys.
310
310
  - **hoverHighlight**: Requires `colorBy` as a string field.
311
311
  - **tooltip="multi"**: Shows all series at hovered X for LineChart, AreaChart, and StackedAreaChart. Custom fn receives `datum.allSeries`.
312
- - **Axis config**: `frameProps.axes: [{ orient, includeMax, autoRotate, gridStyle, landmarkTicks }]`
312
+ - **Axis config**: `frameProps.axes: [{ orient, includeMax, autoRotate, gridStyle, landmarkTicks, tickAnchor }]`. `tickAnchor: "edges"` flips the first tick's `text-anchor` to `start` and the last to `end` on horizontal axes (and `dominant-baseline` to `hanging`/`auto` on vertical axes) so edge labels don't overflow the plot. Pairs naturally with `axisExtent: "exact"`.
313
+ - **Targeting individual axes from CSS**: every axis renders as its own `<g class="semiotic-axis semiotic-axis-{bottom|left|right|top}" data-orient="…">`. Style with `[data-orient="left"] text { font-size: 14px }` or via the class names — no `!important` needed because the CSS-var defaults are set inline via `var(--semiotic-tick-font-size, …)`, so cascade overrides win cleanly. Tick text carries `class="semiotic-axis-tick"`; labels carry `class="semiotic-axis-label"`; titles carry `class="semiotic-chart-title"`.
313
314
  - **xScaleType: "time"**: Creates `scaleTime`. Required for landmark ticks with timestamps.
314
315
  - **scalePadding**: Pixel inset on scale ranges. Pass via `frameProps={{ scalePadding: 12 }}`.
315
316
  - **categoryFormat/xFormat/yFormat**: Can return ReactNode (renders in `<foreignObject>`).
package/README.md CHANGED
@@ -35,7 +35,7 @@ generate correct code without examples.
35
35
  Semiotic ships with everything an AI coding assistant needs to generate
36
36
  correct visualizations without trial and error:
37
37
 
38
- - **`semiotic/ai`** — a single import with 40 HOC charts (XY, ordinal, network, realtime), optimized for LLM code generation. Geo charts are in `semiotic/geo` to keep d3-geo out of non-geo bundles.
38
+ - **`semiotic/ai`** — a single import with 41 HOC charts (XY, ordinal, network, realtime), optimized for LLM code generation. Geo charts are in `semiotic/geo` to keep d3-geo out of non-geo bundles.
39
39
  - **`ai/schema.json`** — machine-readable prop schemas for every component
40
40
  - **`npx semiotic-mcp`** — an MCP server for tool-based chart rendering in any MCP client
41
41
  - **`npx semiotic-ai --doctor`** — validate component + props JSON from the command line with typo suggestions and anti-pattern detection
@@ -240,12 +240,12 @@ import { LineChart, BarChart } from "semiotic"
240
240
 
241
241
  | Category | Components |
242
242
  |---|---|
243
- | **XY** | `LineChart` `AreaChart` `StackedAreaChart` `Scatterplot` `ConnectedScatterplot` `BubbleChart` `Heatmap` `QuadrantChart` `MultiAxisLineChart` `MinimapChart` |
244
- | **Categorical** | `BarChart` `StackedBarChart` `GroupedBarChart` `LikertChart` `SwimlaneChart` `FunnelChart` `SwarmPlot` `BoxPlot` `Histogram` `ViolinPlot` `RidgelinePlot` `DotPlot` `PieChart` `DonutChart` |
245
- | **Network** | `ForceDirectedGraph` `ChordDiagram` `SankeyDiagram` `TreeDiagram` `Treemap` `CirclePack` `OrbitDiagram` |
243
+ | **XY** | `LineChart` `AreaChart` `DifferenceChart` `StackedAreaChart` `Scatterplot` `ConnectedScatterplot` `BubbleChart` `Heatmap` `QuadrantChart` `MultiAxisLineChart` `MinimapChart` `CandlestickChart` `ScatterplotMatrix` |
244
+ | **Categorical** | `BarChart` `StackedBarChart` `GroupedBarChart` `LikertChart` `SwimlaneChart` `FunnelChart` `SwarmPlot` `BoxPlot` `Histogram` `ViolinPlot` `RidgelinePlot` `DotPlot` `PieChart` `DonutChart` `GaugeChart` |
245
+ | **Network** | `ForceDirectedGraph` `ChordDiagram` `SankeyDiagram` `ProcessSankey` `TreeDiagram` `Treemap` `CirclePack` `OrbitDiagram` |
246
246
  | **Geo** | `ChoroplethMap` `ProportionalSymbolMap` `FlowMap` `DistanceCartogram` |
247
247
  | **Realtime** | `RealtimeLineChart` `RealtimeHistogram` `RealtimeSwarmChart` `RealtimeWaterfallChart` `RealtimeHeatmap` |
248
- | **Coordination** | `LinkedCharts` `ScatterplotMatrix` |
248
+ | **Coordination** | `LinkedCharts` |
249
249
  | **Layout** | `ChartGrid` `ContextLayout` `CategoryColorProvider` |
250
250
  | **Frames** | `StreamXYFrame` `StreamOrdinalFrame` `StreamNetworkFrame` `StreamGeoFrame` |
251
251
 
@@ -287,18 +287,18 @@ Semiotic ships 12 entry points. **Don't import from `"semiotic"` unless you need
287
287
 
288
288
  | Entry Point | gzip | What's inside |
289
289
  |---|---|---|
290
- | `semiotic/xy` | **83 KB** | LineChart, AreaChart, Scatterplot, Heatmap, + 8 more XY charts |
291
- | `semiotic/ordinal` | **67 KB** | BarChart, PieChart, BoxPlot, Histogram, + 11 more categorical charts |
292
- | `semiotic/network` | **62 KB** | ForceDirectedGraph, SankeyDiagram, ProcessSankey, Treemap, + 4 more |
293
- | `semiotic/geo` | **50 KB** | ChoroplethMap, FlowMap, DistanceCartogram, ProportionalSymbolMap |
294
- | `semiotic/realtime` | **88 KB** | RealtimeLineChart, RealtimeHistogram, + 3 streaming charts |
295
- | `semiotic/server` | **114 KB** | renderChart, renderDashboard, renderToImage, renderToAnimatedGif |
290
+ | `semiotic/xy` | **85 KB** | LineChart, AreaChart, Scatterplot, Heatmap, + 8 more XY charts |
291
+ | `semiotic/ordinal` | **68 KB** | BarChart, PieChart, BoxPlot, Histogram, + 11 more categorical charts |
292
+ | `semiotic/network` | **63 KB** | ForceDirectedGraph, SankeyDiagram, ProcessSankey, Treemap, + 4 more |
293
+ | `semiotic/geo` | **51 KB** | ChoroplethMap, FlowMap, DistanceCartogram, ProportionalSymbolMap |
294
+ | `semiotic/realtime` | **90 KB** | RealtimeLineChart, RealtimeHistogram, + 3 streaming charts |
295
+ | `semiotic/server` | **117 KB** | renderChart, renderDashboard, renderToImage, renderToAnimatedGif |
296
296
  | `semiotic/utils` | **22 KB** | ThemeProvider, validators, serialization — no chart components |
297
297
  | `semiotic/recipes` | **5 KB** | Pure layout functions (waffle, marimekko, flextree, dagre, …) |
298
298
  | `semiotic/themes` | **4 KB** | Theme presets only (tufte, carbon, etc.) |
299
299
  | `semiotic/data` | **3 KB** | bin, rollup, groupBy, pivot, fromVegaLite |
300
- | `semiotic/ai` | **184 KB** | All 40 HOCs + validation — optimized for LLM code generation |
301
- | `semiotic` | **183 KB** | Everything below (full bundle) |
300
+ | `semiotic/ai` | **188 KB** | All 41 HOCs + validation — optimized for LLM code generation |
301
+ | `semiotic` | **186 KB** | Everything below (full bundle) |
302
302
 
303
303
  <!-- semiotic-bundle-sizes:end -->
304
304
 
@@ -389,7 +389,7 @@ No API keys or authentication required. The server runs locally via stdio. HTTP
389
389
  | Tool | Description |
390
390
  |------|-------------|
391
391
  | **`renderChart`** | Render a Semiotic chart to static SVG. Supports the components returned by `getSchema` that are marked `[renderable]`. Pass `{ component: "LineChart", props: { data: [...], xAccessor: "x", yAccessor: "y" } }`. Returns SVG string or validation errors with fix suggestions. |
392
- | **`getSchema`** | Return the prop schema for a specific component. Pass `{ component: "LineChart" }` to get its props, or omit `component` to list all 43 chart schemas. Components marked `[renderable]` are available through `renderChart`; realtime charts require a browser/live environment. |
392
+ | **`getSchema`** | Return the prop schema for a specific component. Pass `{ component: "LineChart" }` to get its props, or omit `component` to list all 45 chart schemas. Components marked `[renderable]` are available through `renderChart`; realtime charts require a browser/live environment. |
393
393
  | **`suggestChart`** | Recommend chart types for a data sample. Pass `{ data: [{...}, ...] }` with 1–5 sample objects. Optionally include `intent` (`"comparison"`, `"trend"`, `"distribution"`, `"relationship"`, `"composition"`, `"geographic"`, `"network"`, `"hierarchy"`). Returns ranked suggestions with example props. |
394
394
  | **`diagnoseConfig`** | Check a chart configuration for common problems — empty data, bad dimensions, missing accessors, wrong data shape, and more. Returns a human-readable diagnostic report with actionable fixes. |
395
395
  | **`reportIssue`** | Generate a pre-filled GitHub issue URL for bug reports or feature requests. Pass `{ title: "...", body: "...", labels: ["bug"] }`. Returns a URL the user can open to submit. |
@@ -502,7 +502,7 @@ Semiotic is indexed by AI-coding-agent documentation tools so your assistant (Cl
502
502
 
503
503
  Agent-facing API surface:
504
504
 
505
- - **`CLAUDE.md`**, **`ai/schema.json`**, **`ai/behaviorContracts.cjs`** — bundled in the npm tarball (see `package.json#files`); agents that install Semiotic locally read these directly. `CLAUDE.md` is the quick-start cheat sheet (HOC props, push API, theming, usage notes); `ai/schema.json` is the JSON Schema for every chart's prop surface (44 charts); `ai/behaviorContracts.cjs` carries the agent-visible semantic rules (color precedence, push-mode requirements, ID-accessor contracts).
505
+ - **`CLAUDE.md`**, **`ai/schema.json`**, **`ai/behaviorContracts.cjs`** — bundled in the npm tarball (see `package.json#files`); agents that install Semiotic locally read these directly. `CLAUDE.md` is the quick-start cheat sheet (HOC props, push API, theming, usage notes); `ai/schema.json` is the JSON Schema for every chart's prop surface (45 charts); `ai/behaviorContracts.cjs` carries the agent-visible semantic rules (color precedence, push-mode requirements, ID-accessor contracts).
506
506
  - [**`semiotic.nteract.io/llms.txt`**](https://semiotic.nteract.io/llms.txt) + [**`/llms-full.txt`**](https://semiotic.nteract.io/llms-full.txt) — deployed at the docs site per the [llms.txt standard](https://llmstxt.org). Agents fetch the navigation map (`llms.txt`) or the full inlined docs (`llms-full.txt`) over HTTP; they're not part of the npm package itself.
507
507
 
508
508
  ## Documentation
package/ai/schema.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "name": "semiotic",
4
- "version": "3.5.2",
4
+ "version": "3.5.3",
5
5
  "description": "React data visualization library for charts, networks, and beyond",
6
6
  "tools": [
7
7
  {
@@ -188,6 +188,13 @@
188
188
  "anomaly": {
189
189
  "type": "object",
190
190
  "description": "Anomaly overlay config — ±σ band + anomaly dot annotations. See AnomalyConfig."
191
+ },
192
+ "band": {
193
+ "type": [
194
+ "object",
195
+ "array"
196
+ ],
197
+ "description": "Asymmetric min/max envelope drawn under the line. `{ y0Accessor, y1Accessor, style?, perSeries?, interactive? }` or an array of those for percentile fans. Distinct from `forecast`/`anomaly` (computed) — band is pure data passthrough. Hovered datum is enriched with `band: { y0, y1 }` and `bands: [...]`."
191
198
  }
192
199
  },
193
200
  "required": [
@@ -368,6 +375,13 @@
368
375
  "anomaly": {
369
376
  "type": "object",
370
377
  "description": "Anomaly overlay config — ±σ band + anomaly dot annotations. See AnomalyConfig."
378
+ },
379
+ "band": {
380
+ "type": [
381
+ "object",
382
+ "array"
383
+ ],
384
+ "description": "Asymmetric min/max envelope drawn under the area. See LineChart.band — same shape, same enrichment."
371
385
  }
372
386
  },
373
387
  "required": [
@@ -2,7 +2,7 @@
2
2
 
3
3
  <!-- semiotic-bundle-sizes:start -->
4
4
  <!-- Auto-generated by scripts/sync-bundle-sizes.mjs — do not edit by hand. -->
5
- **Use sub-path imports** — `semiotic/xy` (83KB gz), `semiotic/ordinal` (67KB gz), `semiotic/network` (62KB gz), `semiotic/geo` (50KB gz), `semiotic/realtime` (88KB gz), `semiotic/server` (114KB gz), `semiotic/utils` (22KB gz), `semiotic/recipes` (5KB gz), `semiotic/themes` (4KB gz), `semiotic/data` (3KB gz), `semiotic/ai` (184KB gz). Full `semiotic` is 183KB gz.
5
+ **Use sub-path imports** — `semiotic/xy` (85KB gz), `semiotic/ordinal` (68KB gz), `semiotic/network` (63KB gz), `semiotic/geo` (51KB gz), `semiotic/realtime` (90KB gz), `semiotic/server` (117KB gz), `semiotic/utils` (22KB gz), `semiotic/recipes` (5KB gz), `semiotic/themes` (4KB gz), `semiotic/data` (3KB gz), `semiotic/ai` (188KB gz). Full `semiotic` is 186KB gz.
6
6
  <!-- semiotic-bundle-sizes:end -->
7
7
 
8
8
  ## Flat Array Data (`data: object[]`)
@@ -1,10 +1,10 @@
1
1
  import * as React from "react";
2
2
  import { LIGHT_THEME, DARK_THEME, HIGH_CONTRAST_THEME } from "./store/ThemeStore";
3
- import type { SemioticTheme } from "./store/ThemeStore";
3
+ import type { SemioticTheme, SemioticThemeUpdate } from "./store/ThemeStore";
4
4
  import type { ThemePresetName } from "./semiotic-themes";
5
5
  interface ThemeProviderProps {
6
6
  /** Theme preset name (e.g. "tufte", "pastels-dark", "bi-tool") or a partial SemioticTheme object. */
7
- theme?: ThemePresetName | Partial<SemioticTheme>;
7
+ theme?: ThemePresetName | SemioticThemeUpdate;
8
8
  children: React.ReactNode;
9
9
  }
10
10
  declare function ThemeProviderWrapper({ theme, children }: ThemeProviderProps): import("react/jsx-runtime").JSX.Element;
@@ -22,6 +22,24 @@ export interface ProcessSankeyProps<TNode extends Datum = Datum, TEdge extends D
22
22
  valueAccessor?: ChartAccessor<TEdge, number>;
23
23
  startTimeAccessor?: ChartAccessor<TEdge, TimeLike>;
24
24
  endTimeAccessor?: ChartAccessor<TEdge, TimeLike>;
25
+ /**
26
+ * Optional accessor for the per-edge "system in" time — when
27
+ * `value` is added to the SOURCE node's mass. Use when the
28
+ * source node has an intake event distinct from when this edge
29
+ * departs (e.g. a patient is admitted to the ER at 7pm and
30
+ * transferred out at 9pm — the ER node carries the patient's
31
+ * weight between 7pm and 9pm). Without this, the source node's
32
+ * mass is synthesized as a flat baseline and the band reads as
33
+ * "always there." Set it and the node band climbs / falls as
34
+ * units arrive and depart — a true staircase profile.
35
+ */
36
+ systemInTimeAccessor?: ChartAccessor<TEdge, TimeLike>;
37
+ /**
38
+ * Optional accessor for the per-edge "system out" time — when
39
+ * `value` is removed from the TARGET node's mass. Mirror of
40
+ * `systemInTimeAccessor` for the inbound side.
41
+ */
42
+ systemOutTimeAccessor?: ChartAccessor<TEdge, TimeLike>;
25
43
  /**
26
44
  * Accessor for a node's explicit lifetime extent — a `[start, end]`
27
45
  * tuple of time-likes. Lane spans
@@ -50,6 +68,10 @@ export interface ProcessSankeyProps<TNode extends Datum = Datum, TEdge extends D
50
68
  lifetimeMode?: "full" | "half";
51
69
  showLaneRails?: boolean;
52
70
  showQualityReadout?: boolean;
71
+ /** Render the per-band node id label at the band's left edge.
72
+ * Default `true`. Set `false` for dense layouts where labels
73
+ * would overlap, or when the legend already names every band. */
74
+ showLabels?: boolean;
53
75
  edgeOpacity?: number;
54
76
  /** Tooltip content. `false` disables, `true` uses the default,
55
77
  * or pass a `Tooltip(...)` / custom function for full control. */
@@ -4,7 +4,7 @@ import type { StreamNetworkFrameProps } from "../../stream/networkTypes";
4
4
  import type { RealtimeFrameHandle } from "../../realtime/types";
5
5
  import type { BaseChartProps, ChartAccessor } from "../shared/types";
6
6
  import { type TooltipProp } from "../../Tooltip/Tooltip";
7
- import type { LegendInteractionMode } from "../shared/hooks";
7
+ import type { LegendInteractionMode, LegendPosition } from "../shared/hooks";
8
8
  /**
9
9
  * SankeyDiagram component props
10
10
  */
@@ -25,6 +25,10 @@ export interface SankeyDiagramProps<TNode extends Datum = Datum, TEdge extends D
25
25
  nodeLabel?: ChartAccessor<TNode, string>;
26
26
  showLabels?: boolean;
27
27
  enableHover?: boolean;
28
+ /** Show a swatch + label legend. Defaults to `true` when `colorBy` is set. */
29
+ showLegend?: boolean;
30
+ /** Legend position. Default `"right"`. */
31
+ legendPosition?: LegendPosition;
28
32
  legendInteraction?: LegendInteractionMode;
29
33
  edgeOpacity?: number;
30
34
  edgeSort?: (a: any, b: any) => number;
@@ -11,6 +11,25 @@ export interface ProcessSankeyEdge {
11
11
  value: number;
12
12
  startTime: number;
13
13
  endTime: number;
14
+ /** Optional: time at which this unit of mass actually "arrived" at
15
+ * the SOURCE node (e.g., the hospital admit time for an ER patient
16
+ * whose transfer to ICU happens later at `startTime`). Purely a
17
+ * rendering hint — the layout/mass profile is unchanged. The
18
+ * renderer cuts a rectangular slot out of the source node's band
19
+ * from the node's left edge up to the scaled `systemInTime`, with
20
+ * height equal to this edge's ribbon thickness. Edges without
21
+ * `systemInTime` are drawn as-is. Result: a staircase profile on
22
+ * the source side as units enter the system one by one.
23
+ * Default: undefined. */
24
+ systemInTime?: number;
25
+ /** Optional: time at which this unit of mass leaves the TARGET node
26
+ * (e.g., the discharge time for a patient who arrived at the ward
27
+ * at `endTime`). Symmetric to `systemInTime`: the renderer cuts a
28
+ * rectangular slot out of the target node's band from the scaled
29
+ * `systemOutTime` to the node's right edge, with height equal to
30
+ * this edge's ribbon thickness. Layout/mass profile unchanged.
31
+ * Default: undefined. */
32
+ systemOutTime?: number;
14
33
  }
15
34
  export interface ProcessSankeyIssue {
16
35
  kind: string;
@@ -98,6 +117,44 @@ export declare function clampTime(t: number, domain: Domain): number;
98
117
  export declare function clampSamples(samples: ProcessSankeySample[], domain: Domain): ProcessSankeySample[];
99
118
  export declare function attachmentYRange(att: ProcessSankeyAttachment, cl: number, S: number): [number, number];
100
119
  export declare function buildBandPath(samples: ProcessSankeySample[], cl: number, S: number, xScale: (t: number) => number, domain: Domain): string | null;
120
+ /**
121
+ * One 20-px gradient stub at an attachment with `systemInTime` /
122
+ * `systemOutTime`. Rendered as its own bezier scene-edge with a
123
+ * horizontal gradient, painted underneath the band. The band
124
+ * paints with `fill: none` whenever any stubs are present, so the
125
+ * stub gradients are the only colored regions inside the band's
126
+ * outline.
127
+ */
128
+ export interface BandGradientStub {
129
+ /** Rect path (M-L-L-L-Z). */
130
+ pathD: string;
131
+ /** Gradient extent in screen pixels. */
132
+ x0: number;
133
+ x1: number;
134
+ /** Color stops — 0 = transparent end, 1 = band-color end. */
135
+ from: 0 | 1;
136
+ to: 0 | 1;
137
+ }
138
+ /**
139
+ * Build the per-edge 20-px gradient stubs that visualize
140
+ * `systemInTime` / `systemOutTime` on a node band. Each stub is
141
+ * a rect on the edge's attachment slot, painted with a horizontal
142
+ * gradient that fades the band color in (or out) over 20 screen
143
+ * pixels and saturates through the rest of the rect.
144
+ *
145
+ * The rect is clipped to the band's outline bounds (so cutouts don't
146
+ * spill outside the node shape), but the gradient extent stays at
147
+ * its natural `[xSysIn - FADE_PX, xSysIn]` / `[xSysOut, xSysOut + FADE_PX]`
148
+ * range. The canvas renderer uses pad-mode clamping for color stops
149
+ * outside the rect, so a stub whose fade region falls past the band's
150
+ * edge still paints solid band-color where it's visible — instead of
151
+ * collapsing to a degenerate gradient that the renderer would clamp
152
+ * to transparent.
153
+ *
154
+ * Pure rendering hint — layout/mass-profile unchanged. Returns an
155
+ * empty array when the node has no qualifying edges.
156
+ */
157
+ export declare function buildBandCutoutsForNode(nodeId: string, edges: ProcessSankeyEdge[], layout: ProcessSankeyLayout, xScale: (t: number) => number, domain: Domain): BandGradientStub[];
101
158
  type SlotByNode = Record<string, number>;
102
159
  export declare function countCrossings(slotByNode: SlotByNode, edges: ProcessSankeyEdge[]): number;
103
160
  export declare function totalEdgeLength(slotByNode: SlotByNode, edges: ProcessSankeyEdge[]): number;
@@ -14,6 +14,12 @@ export interface ProcessSankeyNormalizedEdge {
14
14
  value: number;
15
15
  startTime: number;
16
16
  endTime: number;
17
+ /** Optional render-only hint: when this unit of mass actually
18
+ * entered the source node. Triggers a cutout in the source band. */
19
+ systemInTime?: number;
20
+ /** Optional render-only hint: when this unit of mass left the
21
+ * target node. Triggers a cutout in the target band. */
22
+ systemOutTime?: number;
17
23
  __raw?: Datum;
18
24
  }
19
25
  export interface BuildScenesInput {
@@ -3,10 +3,16 @@ import type { BezierCache } from "../../../stream/networkTypes";
3
3
  import type { Datum } from "../../shared/datumTypes";
4
4
  export interface ProcessSankeyBandSpec {
5
5
  id: string;
6
+ /** Outer band perimeter — same path used for fill and stroke. */
6
7
  pathD: string;
7
8
  fill: string;
8
9
  stroke?: string;
9
10
  strokeWidth?: number;
11
+ /** Per-edge 20-px gradient stubs (band-color fade-in for
12
+ * systemInTime, fade-out for systemOutTime). When at least one
13
+ * stub is present, the band paints outline-only and the stubs
14
+ * are the only colored regions inside the perimeter. */
15
+ gradientStubs?: BandGradientStub[];
10
16
  /** The user's raw node datum, surfaced as `data` in HoverData. */
11
17
  rawDatum: Datum;
12
18
  /** Pre-computed label x/y for the node band. */
@@ -14,6 +20,13 @@ export interface ProcessSankeyBandSpec {
14
20
  labelY: number;
15
21
  labelText: string;
16
22
  }
23
+ export interface BandGradientStub {
24
+ pathD: string;
25
+ x0: number;
26
+ x1: number;
27
+ from: 0 | 1;
28
+ to: 0 | 1;
29
+ }
17
30
  export interface ProcessSankeyRibbonSpec {
18
31
  id: string;
19
32
  pathD: string;
@@ -87,6 +87,8 @@ export interface RealtimeHeatmapProps<TDatum extends Datum = Datum> {
87
87
  selection?: SelectionConfig;
88
88
  /** Show a loading skeleton placeholder */
89
89
  loading?: boolean;
90
+ /** Custom content rendered in place of the default skeleton while `loading` is true. */
91
+ loadingContent?: React.ReactNode | false;
90
92
  /** Custom content to render when data is empty. Set to `false` to disable empty state. */
91
93
  emptyContent?: ReactNode | false;
92
94
  /** Visual emphasis level for dashboard hierarchy. "primary" spans two columns in ChartGrid. */
@@ -103,6 +103,8 @@ export interface RealtimeHistogramProps<TDatum extends Datum = Datum> {
103
103
  transition?: TransitionConfig;
104
104
  /** Show a loading skeleton placeholder */
105
105
  loading?: boolean;
106
+ /** Custom content rendered in place of the default skeleton while `loading` is true. */
107
+ loadingContent?: React.ReactNode | false;
106
108
  /** Custom content to render when data is empty. Set to `false` to disable empty state. */
107
109
  emptyContent?: ReactNode | false;
108
110
  /** Brush configuration. `true` defaults to `{ dimension: "x", snap: "bin" }`. */
@@ -89,6 +89,8 @@ export interface RealtimeLineChartProps<TDatum extends Datum = Datum> {
89
89
  selection?: SelectionConfig;
90
90
  /** Show a loading skeleton placeholder */
91
91
  loading?: boolean;
92
+ /** Custom content rendered in place of the default skeleton while `loading` is true. */
93
+ loadingContent?: React.ReactNode | false;
92
94
  /** Custom content to render when data is empty. Set to `false` to disable empty state. */
93
95
  emptyContent?: ReactNode | false;
94
96
  /** Visual emphasis level for dashboard hierarchy. "primary" spans two columns in ChartGrid. */
@@ -87,6 +87,8 @@ export interface RealtimeSwarmChartProps<TDatum extends Datum = Datum> {
87
87
  selection?: SelectionConfig;
88
88
  /** Show a loading skeleton placeholder */
89
89
  loading?: boolean;
90
+ /** Custom content rendered in place of the default skeleton while `loading` is true. */
91
+ loadingContent?: React.ReactNode | false;
90
92
  /** Custom content to render when data is empty. Set to `false` to disable empty state. */
91
93
  emptyContent?: ReactNode | false;
92
94
  /** Visual emphasis level for dashboard hierarchy. "primary" spans two columns in ChartGrid. */
@@ -89,6 +89,8 @@ export interface RealtimeWaterfallChartProps<TDatum extends Datum = Datum> {
89
89
  selection?: SelectionConfig;
90
90
  /** Show a loading skeleton placeholder */
91
91
  loading?: boolean;
92
+ /** Custom content rendered in place of the default skeleton while `loading` is true. */
93
+ loadingContent?: React.ReactNode | false;
92
94
  /** Custom content to render when data is empty. Set to `false` to disable empty state. */
93
95
  emptyContent?: ReactNode | false;
94
96
  /** Visual emphasis level for dashboard hierarchy. "primary" spans two columns in ChartGrid. */
@@ -21,6 +21,17 @@ export interface TooltipFieldConfig {
21
21
  export declare function accessorName(acc: string | ((...args: any[]) => any)): string;
22
22
  export declare function formatVal(v: unknown): string;
23
23
  export declare function resolveValue(d: Datum, acc: string | ((d: Datum) => any)): unknown;
24
+ /**
25
+ * Build TooltipFieldConfig rows for a `band` prop config so the default
26
+ * tooltip surfaces envelope values automatically. Reads from the
27
+ * `bands` array the hover handler synthesizes onto the datum — one
28
+ * row pair per configured band (low + high). String accessors are
29
+ * used verbatim as labels; function accessors fall back to "low"/"high".
30
+ *
31
+ * Returns an empty array when band is not configured, so the spread at
32
+ * the call site is a no-op for the common case.
33
+ */
34
+ export declare function bandTooltipFields(band: unknown, valueFormat?: (v: any, ...rest: any[]) => React.ReactNode): TooltipFieldConfig[];
24
35
  /**
25
36
  * Build a default tooltipContent function for StreamXYFrame HOCs.
26
37
  * Receives HoverData ({ data, time, value, x, y }) and renders
@@ -78,6 +78,12 @@ export interface BaseChartProps {
78
78
  chartId?: string;
79
79
  /** Show a loading skeleton placeholder */
80
80
  loading?: boolean;
81
+ /** Custom content to render in place of the default skeleton when `loading` is `true`.
82
+ * Sibling to `emptyContent` — use for branded loading states or progress UI.
83
+ * When omitted, the built-in shimmer-bar skeleton renders.
84
+ * Pass `false` to suppress the loading UI entirely (an outer wrapper's
85
+ * loading state takes over). */
86
+ loadingContent?: React.ReactNode | false;
81
87
  /** Custom content to render when data is empty. Set to `false` to disable empty state. */
82
88
  emptyContent?: React.ReactNode | false;
83
89
  /** Uniform fill color for all data marks. Overrides colorScheme and theme categorical.
@@ -41,7 +41,7 @@ export interface AreaSeriesSetupOptions<TDatum extends Datum = Datum> {
41
41
  showPoints: boolean;
42
42
  /** Point radius when `showPoints`. @default 3 */
43
43
  pointRadius: number;
44
- xAccessor: ChartAccessor<TDatum, number>;
44
+ xAccessor: ChartAccessor<TDatum, number | Date | string>;
45
45
  yAccessor: ChartAccessor<TDatum, number>;
46
46
  xLabel?: string;
47
47
  yLabel?: string;
@@ -52,6 +52,9 @@ export interface AreaSeriesSetupOptions<TDatum extends Datum = Datum> {
52
52
  /** Field used to label the series row in the default tooltip.
53
53
  * Typically `areaBy ?? colorBy`. */
54
54
  groupField?: Accessor<string> | ChartAccessor<TDatum, string>;
55
+ /** Optional band prop — when set, the default tooltip surfaces a
56
+ * pair of rows per band (low + high). Threaded through verbatim. */
57
+ band?: unknown;
55
58
  }
56
59
  export interface AreaSeriesSetupResult<TDatum extends Datum = Datum> {
57
60
  /** Flat data array ready for `<StreamXYFrame data={…} />`. Empty
@@ -60,6 +60,8 @@ export interface ChartSetupInput {
60
60
  hoverHighlight?: boolean;
61
61
  /** Loading state */
62
62
  loading: boolean | undefined;
63
+ /** Custom content rendered in place of the default skeleton while `loading` is true. */
64
+ loadingContent?: ReactNode | false;
63
65
  /** Empty content override */
64
66
  emptyContent?: ReactNode;
65
67
  /** Resolved width from useChartMode */
@@ -64,6 +64,7 @@ interface DataSetupOptions extends ScaffoldOptions {
64
64
  }) => void;
65
65
  chartId?: string;
66
66
  loading?: boolean;
67
+ loadingContent?: ReactNode | false;
67
68
  emptyContent?: ReactNode;
68
69
  }
69
70
  interface DataSetupResult<TFrameHandle> extends ScaffoldResult<TFrameHandle> {
@@ -45,6 +45,8 @@ export interface NetworkChartSetupInput<TNode extends Datum = Datum, TEdge exten
45
45
  width: number;
46
46
  height: number;
47
47
  loading?: boolean;
48
+ /** Custom content rendered in place of the default skeleton while `loading` is true. */
49
+ loadingContent?: ReactNode | false;
48
50
  emptyContent?: ReactNode | false;
49
51
  /**
50
52
  * Which array drives the empty-state check. `"edges"` (default)
@@ -27,10 +27,17 @@ export declare function SafeRender({ componentName, width, height, children }: S
27
27
  */
28
28
  export declare function renderEmptyState(data: any[] | undefined | null, width: number, height: number, emptyContent?: React.ReactNode | false): React.ReactElement | null;
29
29
  /**
30
- * Renders a shimmer/skeleton loading placeholder.
31
- * Returns null when loading is false.
30
+ * Renders a loading placeholder while `loading` is true.
31
+ *
32
+ * When `loadingContent` is provided, it replaces the default shimmer
33
+ * skeleton — wrapped in the same sized container so the chart still
34
+ * occupies the slot it would when rendered. Pass `false` to suppress
35
+ * the skeleton entirely (the early-return becomes null and the chart's
36
+ * own loading UI takes over).
37
+ *
38
+ * Returns null when `loading` is falsy.
32
39
  */
33
- export declare function renderLoadingState(loading: boolean | undefined, width: number, height: number): React.ReactElement | null;
40
+ export declare function renderLoadingState(loading: boolean | undefined, width: number, height: number, loadingContent?: React.ReactNode | false): React.ReactElement | null;
34
41
  /** Warn if a string accessor isn't found in the first data element */
35
42
  export declare function warnMissingField(componentName: string, data: any[] | undefined, accessorName: string, accessorValue: any): void;
36
43
  /** Warn if data looks like the wrong shape for this chart type */
@@ -1,6 +1,6 @@
1
1
  import type { Datum } from "../shared/datumTypes";
2
2
  import * as React from "react";
3
- import type { StreamXYFrameProps } from "../../stream/types";
3
+ import type { StreamXYFrameProps, BandConfig } from "../../stream/types";
4
4
  import type { RealtimeFrameHandle } from "../../realtime/types";
5
5
  import type { LegendInteractionMode, LegendPosition } from "../shared/hooks";
6
6
  import type { BaseChartProps, AxisConfig, ChartAccessor } from "../shared/types";
@@ -23,7 +23,7 @@ export interface AreaChartProps<TDatum extends Datum = Datum> extends BaseChartP
23
23
  * Field name or function to access x values
24
24
  * @default "x"
25
25
  */
26
- xAccessor?: ChartAccessor<TDatum, number>;
26
+ xAccessor?: ChartAccessor<TDatum, number | Date | string>;
27
27
  /**
28
28
  * Field name or function to access y values
29
29
  * @default "y"
@@ -183,6 +183,16 @@ export interface AreaChartProps<TDatum extends Datum = Datum> extends BaseChartP
183
183
  * ```
184
184
  */
185
185
  anomaly?: AnomalyConfig;
186
+ /**
187
+ * Asymmetric min/max band(s) drawn under the area fill. Distinct from
188
+ * `y0Accessor` (which replaces the area's baseline) — `band` is a
189
+ * decorative envelope painted beneath the area, driven by independent
190
+ * `y0Accessor` / `y1Accessor` per band. Pass an array for fan charts.
191
+ *
192
+ * The hovered datum gets `band: { y0, y1 }` and `bands: [...]` for
193
+ * tooltip access. See the LineChart docs for full ergonomics.
194
+ */
195
+ band?: BandConfig<TDatum> | Array<BandConfig<TDatum>>;
186
196
  /**
187
197
  * Fixed x domain `[min, max]`. Either bound may be `undefined` to leave
188
198
  * that side data-derived. Useful for pinning a time axis to a known
@@ -1,6 +1,6 @@
1
1
  import type { Datum } from "../shared/datumTypes";
2
2
  import * as React from "react";
3
- import type { StreamXYFrameProps } from "../../stream/types";
3
+ import type { StreamXYFrameProps, BandConfig } from "../../stream/types";
4
4
  import type { RealtimeFrameHandle } from "../../realtime/types";
5
5
  import type { LegendInteractionMode } from "../shared/hooks";
6
6
  import type { BaseChartProps, AxisConfig, ChartAccessor } from "../shared/types";
@@ -29,7 +29,7 @@ export interface LineChartProps<TDatum extends Datum = Datum> extends BaseChartP
29
29
  * Field name or function to access x values
30
30
  * @default "x"
31
31
  */
32
- xAccessor?: ChartAccessor<TDatum, number>;
32
+ xAccessor?: ChartAccessor<TDatum, number | Date | string>;
33
33
  /**
34
34
  * Field name or function to access y values
35
35
  * @default "y"
@@ -181,6 +181,30 @@ export interface LineChartProps<TDatum extends Datum = Datum> extends BaseChartP
181
181
  * envelope around the extrapolated forecast region.
182
182
  */
183
183
  forecast?: ForecastConfig;
184
+ /**
185
+ * Asymmetric min/max band(s) drawn under the line. Differs from
186
+ * `forecast`/`anomaly` (computed envelopes around a series) by being
187
+ * pure data passthrough — provide `y0Accessor` / `y1Accessor` and the
188
+ * band is drawn between them at each x.
189
+ *
190
+ * Pass an array for percentile fans (e.g. p25/p75 on top of p10/p90).
191
+ * Each band participates in y-extent auto-derivation, so a tall band
192
+ * can never get clipped. The hovered datum is enriched with
193
+ * `band: { y0, y1 }` (first band) and `bands: [...]` (all bands) so
194
+ * tooltip functions can read the envelope without re-running the
195
+ * accessors.
196
+ *
197
+ * @example
198
+ * ```tsx
199
+ * <LineChart
200
+ * data={[{ time, average, min, max }, ...]}
201
+ * xAccessor="time"
202
+ * yAccessor="average"
203
+ * band={{ y0Accessor: "min", y1Accessor: "max" }}
204
+ * />
205
+ * ```
206
+ */
207
+ band?: BandConfig<TDatum> | Array<BandConfig<TDatum>>;
184
208
  /**
185
209
  * Fixed x domain `[min, max]`. Either bound may be `undefined` to leave
186
210
  * that side data-derived.