semiotic 3.2.0 → 3.2.1

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 (107) hide show
  1. package/CLAUDE.md +50 -8
  2. package/README.md +11 -11
  3. package/ai/examples.md +91 -0
  4. package/ai/schema.json +223 -9
  5. package/ai/system-prompt.md +12 -4
  6. package/dist/components/ChartContainer.d.ts +2 -0
  7. package/dist/components/DataSummaryContext.d.ts +12 -0
  8. package/dist/components/Tooltip/FlippingTooltip.d.ts +34 -0
  9. package/dist/components/charts/index.d.ts +4 -0
  10. package/dist/components/charts/ordinal/BarChart.d.ts +3 -1
  11. package/dist/components/charts/ordinal/BoxPlot.d.ts +3 -1
  12. package/dist/components/charts/ordinal/DonutChart.d.ts +0 -1
  13. package/dist/components/charts/ordinal/DotPlot.d.ts +3 -1
  14. package/dist/components/charts/ordinal/FunnelChart.d.ts +3 -1
  15. package/dist/components/charts/ordinal/GroupedBarChart.d.ts +3 -1
  16. package/dist/components/charts/ordinal/Histogram.d.ts +16 -2
  17. package/dist/components/charts/ordinal/LikertChart.d.ts +94 -0
  18. package/dist/components/charts/ordinal/PieChart.d.ts +0 -1
  19. package/dist/components/charts/ordinal/RidgelinePlot.d.ts +11 -7
  20. package/dist/components/charts/ordinal/StackedBarChart.d.ts +3 -1
  21. package/dist/components/charts/ordinal/SwarmPlot.d.ts +14 -1
  22. package/dist/components/charts/ordinal/SwimlaneChart.d.ts +65 -0
  23. package/dist/components/charts/ordinal/ViolinPlot.d.ts +16 -2
  24. package/dist/components/charts/realtime/RealtimeHistogram.d.ts +20 -0
  25. package/dist/components/charts/shared/annotationResolvers.d.ts +28 -0
  26. package/dist/components/charts/shared/hooks.d.ts +23 -2
  27. package/dist/components/charts/shared/selectionUtils.d.ts +5 -1
  28. package/dist/components/charts/shared/statsTooltip.d.ts +11 -0
  29. package/dist/components/charts/shared/types.d.ts +22 -1
  30. package/dist/components/charts/shared/useChartSetup.d.ts +12 -2
  31. package/dist/components/charts/shared/useLikertAggregation.d.ts +51 -0
  32. package/dist/components/charts/shared/useOrdinalBrush.d.ts +28 -0
  33. package/dist/components/charts/shared/useOrdinalStreaming.d.ts +54 -0
  34. package/dist/components/charts/shared/useStreamingLegend.d.ts +2 -2
  35. package/dist/components/charts/shared/validateProps.d.ts +2 -2
  36. package/dist/components/charts/shared/validationMap.d.ts +12 -0
  37. package/dist/components/charts/xy/AreaChart.d.ts +11 -0
  38. package/dist/components/charts/xy/MinimapChart.d.ts +1 -1
  39. package/dist/components/charts/xy/StackedAreaChart.d.ts +11 -0
  40. package/dist/components/realtime/types.d.ts +4 -0
  41. package/dist/components/semiotic-ai.d.ts +1 -0
  42. package/dist/components/semiotic-ordinal.d.ts +2 -0
  43. package/dist/components/semiotic-themes.d.ts +16 -0
  44. package/dist/components/semiotic-utils.d.ts +30 -0
  45. package/dist/components/semiotic.d.ts +4 -4
  46. package/dist/components/store/LinkedCrosshairStore.d.ts +11 -0
  47. package/dist/components/store/useSelection.d.ts +1 -0
  48. package/dist/components/stream/AccessibleDataTable.d.ts +28 -6
  49. package/dist/components/stream/FocusRing.d.ts +33 -0
  50. package/dist/components/stream/OrdinalBrushOverlay.d.ts +43 -0
  51. package/dist/components/stream/OrdinalPipelineStore.d.ts +16 -0
  52. package/dist/components/stream/OrdinalSVGOverlay.d.ts +1 -1
  53. package/dist/components/stream/PipelineStore.d.ts +5 -47
  54. package/dist/components/stream/SVGOverlay.d.ts +4 -0
  55. package/dist/components/stream/SceneGraph.d.ts +6 -1
  56. package/dist/components/stream/XYBrushOverlay.d.ts +47 -0
  57. package/dist/components/stream/geoTypes.d.ts +5 -1
  58. package/dist/components/stream/keyboardNav.d.ts +85 -9
  59. package/dist/components/stream/layouts/hierarchySceneBuilders.d.ts +35 -0
  60. package/dist/components/stream/layouts/hierarchyUtils.d.ts +25 -0
  61. package/dist/components/stream/networkTypes.d.ts +5 -1
  62. package/dist/components/stream/ordinalSceneBuilders/swimlaneScene.d.ts +12 -0
  63. package/dist/components/stream/ordinalTypes.d.ts +15 -3
  64. package/dist/components/stream/pipelineDecay.d.ts +20 -0
  65. package/dist/components/stream/pipelinePulse.d.ts +24 -0
  66. package/dist/components/stream/pipelineTransitions.d.ts +59 -0
  67. package/dist/components/stream/renderers/pointCanvasRenderer.d.ts +2 -1
  68. package/dist/components/stream/types.d.ts +19 -1
  69. package/dist/components/stream/useMediaPreferences.d.ts +11 -0
  70. package/dist/components/stream/xySceneBuilders/areaScene.d.ts +13 -0
  71. package/dist/components/stream/xySceneBuilders/barScene.d.ts +18 -0
  72. package/dist/components/stream/xySceneBuilders/boundsScene.d.ts +8 -0
  73. package/dist/components/stream/xySceneBuilders/candlestickScene.d.ts +10 -0
  74. package/dist/components/stream/xySceneBuilders/emitPointNodes.d.ts +13 -0
  75. package/dist/components/stream/xySceneBuilders/heatmapScene.d.ts +3 -0
  76. package/dist/components/stream/xySceneBuilders/lineScene.d.ts +12 -0
  77. package/dist/components/stream/xySceneBuilders/pointScene.d.ts +12 -0
  78. package/dist/components/stream/xySceneBuilders/swarmScene.d.ts +10 -0
  79. package/dist/components/stream/xySceneBuilders/types.d.ts +93 -0
  80. package/dist/components/stream/xySceneBuilders/waterfallScene.d.ts +12 -0
  81. package/dist/geo.min.js +1 -1
  82. package/dist/geo.module.min.js +1 -1
  83. package/dist/network.min.js +1 -1
  84. package/dist/network.module.min.js +1 -1
  85. package/dist/ordinal.min.js +1 -1
  86. package/dist/ordinal.module.min.js +1 -1
  87. package/dist/realtime.min.js +1 -1
  88. package/dist/realtime.module.min.js +1 -1
  89. package/dist/semiotic-ai.d.ts +1 -0
  90. package/dist/semiotic-ai.min.js +1 -1
  91. package/dist/semiotic-ai.module.min.js +1 -1
  92. package/dist/semiotic-ordinal.d.ts +2 -0
  93. package/dist/{semiotic-statisticalOverlays-D8LhSbQt.js → semiotic-statisticalOverlays-DGX_WWc5.js} +1 -1
  94. package/dist/semiotic-themes.d.ts +16 -0
  95. package/dist/semiotic-themes.min.js +1 -1
  96. package/dist/semiotic-themes.module.min.js +1 -1
  97. package/dist/semiotic-utils.d.ts +30 -0
  98. package/dist/semiotic-utils.min.js +1 -0
  99. package/dist/semiotic-utils.module.min.js +1 -0
  100. package/dist/semiotic.d.ts +4 -4
  101. package/dist/semiotic.min.js +1 -1
  102. package/dist/semiotic.module.min.js +1 -1
  103. package/dist/server.min.js +1 -1
  104. package/dist/server.module.min.js +1 -1
  105. package/dist/xy.min.js +1 -1
  106. package/dist/xy.module.min.js +1 -1
  107. package/package.json +15 -8
package/CLAUDE.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Quick Start
4
4
  - Install: `npm install semiotic`
5
- - Import: `semiotic`, `semiotic/xy`, `semiotic/ordinal`, `semiotic/network`, `semiotic/geo`, `semiotic/realtime`, `semiotic/ai`, `semiotic/data`, `semiotic/server`, `semiotic/themes`
5
+ - Import: `semiotic`, `semiotic/xy`, `semiotic/ordinal`, `semiotic/network`, `semiotic/geo`, `semiotic/realtime`, `semiotic/ai`, `semiotic/data`, `semiotic/server`, `semiotic/themes`, `semiotic/utils`
6
6
  - CLI: `npx semiotic-ai [--schema|--compact|--examples|--doctor]`
7
7
  - MCP: `npx semiotic-mcp`
8
8
  - Every HOC has a built-in error boundary and dev-mode validation warnings
@@ -13,7 +13,9 @@
13
13
  - Every HOC accepts `frameProps` to pass through. TypeScript `strict: true`.
14
14
 
15
15
  ## Common Props (all HOCs)
16
- `title`, `width` (600), `height` (400), `responsiveWidth`, `responsiveHeight`, `margin`, `className`, `color` (uniform fill — overrides theme/colorScheme), `enableHover` (true), `tooltip` (boolean | `(datum) => ReactNode` | `{ fields?, title?, format?, style? }`), `showLegend`, `showGrid` (false), `frameProps`, `onObservation`, `chartId`, `loading` (false), `emptyContent`, `legendInteraction` ("none"|"highlight"|"isolate"), `legendPosition` ("right"|"left"|"top"|"bottom"), `emphasis` ("primary"|"secondary"), `annotations` (array)
16
+ `title`, `description` (overrides aria-label), `summary` (sr-only note), `width` (600), `height` (400), `responsiveWidth`, `responsiveHeight`, `margin`, `className`, `color` (uniform fill — overrides theme/colorScheme), `enableHover` (true), `tooltip` (boolean | `(datum) => ReactNode` | `{ fields?, title?, format?, style? }`), `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)
17
+
18
+ `onClick` receives `(datum, { x, y })` — the original datum and pixel coordinates. Works on lines, bars, areas, pie slices, nodes, and geo features.
17
19
 
18
20
  `onObservation` receives `{ type: "hover"|"hover-end"|"click"|"brush"|"selection", datum?, x?, y?, timestamp, chartType, chartId }`. The `datum` is your original data object.
19
21
 
@@ -40,11 +42,14 @@
40
42
  **ViolinPlot** — + `bins`, `curve`, `showIQR`
41
43
  **RidgelinePlot** — + `bins`, `amplitude` (1.5, unitless multiplier of lane width)
42
44
  **DotPlot** — + `sort` (true), `dotRadius`, `showGrid` default true
43
- **PieChart** — `data`, `categoryAccessor`, `valueAccessor`, `colorBy`, `startAngle`, `slicePadding`
45
+ **PieChart** — `data`, `categoryAccessor`, `valueAccessor`, `colorBy`, `startAngle`
44
46
  **DonutChart** — PieChart + `innerRadius` (60), `centerContent` (ReactNode)
45
47
  **FunnelChart** — `data`, `stepAccessor` ("step"), `valueAccessor` ("value"), `categoryAccessor` (optional), `colorBy`, `connectorOpacity` (0.3), `orientation` ("horizontal"|"vertical"). Horizontal: centered bars with trapezoid connectors. Vertical: bars with diagonal hatch for dropoff. Multi-category: `categoryAccessor="channel"` mirrors (horizontal) or groups (vertical).
48
+ **SwimlaneChart** — `data`, `categoryAccessor` ("category"), `subcategoryAccessor` (required), `valueAccessor` ("value"), `colorBy` (defaults to subcategoryAccessor), `colorScheme`, `orientation` ("horizontal"|"vertical"), `barPadding` (40). Renders categorical lanes with items stacked sequentially — unlike StackedBarChart, the same subcategory can appear multiple times in the same lane. Items stack left-to-right (horizontal) or bottom-to-top (vertical) in data order. Wraps StreamOrdinalFrame with `chartType="swimlane"`. Supports push API for streaming.
49
+
50
+ **LikertChart** — `data`, `categoryAccessor` ("question"), `valueAccessor` ("score", raw mode) or `levelAccessor`+`countAccessor` ("count", pre-aggregated mode), `levels` (required, ordered negative→positive), `orientation` ("horizontal"|"vertical"), `colorScheme`. Horizontal (default): diverging bar chart centered at 0% — negative levels extend left, positive right, neutral (odd count) split 50/50 across centerline. Vertical: stacked 100% bar chart. Supports any scale size (3-point to 7-point+). Raw mode aggregates integer scores automatically (1-based: score 1 → levels[0]). The `levels` array order defines polarity — first half negative, second half positive, center neutral if odd. Supports push API for streaming — accumulates raw data internally and re-aggregates percentages on each push.
46
51
 
47
- All ordinal HOCs support `colorBy` and `colorScheme`. `showCategoryTicks` (default true) hides per-tick labels when false — margins auto-adjust. For distribution charts with `colorBy`, set `showCategoryTicks={false}` since the legend identifies categories.
52
+ All ordinal HOCs support `colorBy` and `colorScheme`. `categoryFormat` (`(label: string, index?: number) => string`) customizes individual tick labels (truncation, formatting). `showCategoryTicks` (default true) hides per-tick labels when false — margins auto-adjust. For distribution charts with `colorBy`, set `showCategoryTicks={false}` since the legend identifies categories.
48
53
 
49
54
  ## Network Charts (`semiotic/network`)
50
55
 
@@ -89,7 +94,7 @@ Push API: `chartRef.current.push({ time, value })`
89
94
  **IMPORTANT**: All pushed data must include a time field (default: `"time"`). Set `timeAccessor` if your field differs. Without valid time field, charts render blank.
90
95
 
91
96
  **RealtimeLineChart** — `timeAccessor` ("time"), `valueAccessor` ("value"), `windowSize` (200), `windowMode`, `stroke`, `strokeWidth`
92
- **RealtimeHistogram** — `binSize` (required), `timeAccessor`, `valueAccessor`, `categoryAccessor`, `colors`
97
+ **RealtimeHistogram** — `binSize` (required), `timeAccessor`, `valueAccessor`, `categoryAccessor`, `colors`, `brush` (boolean|"x"|object, defaults to `{ dimension: "x", snap: "bin" }` when `true`), `onBrush`, `linkedBrush` (cross-chart coordination)
93
98
  **RealtimeSwarmChart** — `timeAccessor`, `valueAccessor`, `categoryAccessor`, `radius`, `opacity`
94
99
  **RealtimeWaterfallChart** — `timeAccessor`, `valueAccessor`, `positiveColor`, `negativeColor`
95
100
  **RealtimeHeatmap** — `timeAccessor`, `valueAccessor`, `heatmapXBins`, `heatmapYBins`, `aggregation`
@@ -97,6 +102,8 @@ Push API: `chartRef.current.push({ time, value })`
97
102
 
98
103
  Encoding: `decay`, `pulse`, `transition`, `staleness` — compose freely on all streaming charts.
99
104
 
105
+ All Realtime* charts accept `data` props for static mode (no push API needed). RealtimeHistogram brush supports bin-snapping (`snap: "bin"`) and streaming tracking — the brush shrinks as selected bins scroll off and auto-clears when fully evicted. Bin snapping uses actual computed bin boundaries (data-driven), not a uniform grid — works with irregular bin widths. `snapDuring: true` enables continuous snap feedback during drag (not just on release).
106
+
100
107
  ### Push API on HOC charts
101
108
  Most HOC charts support push via `forwardRef`. **Omit** `data`/`nodes`/`edges` — do NOT pass `data={[]}`.
102
109
  ```jsx
@@ -129,8 +136,10 @@ Fallback chain: `pointColor` → element color → `--semiotic-primary` CSS var
129
136
  **LinkedCharts** — `selections` (resolution: "union"|"intersect"|"crossfilter"), `showLegend`, `legendPosition`, `legendInteraction`, `legendSelectionName`, `legendField`
130
137
  **CategoryColorProvider** — `colors` (map) or `categories` + `colorScheme`
131
138
  Chart props: `selection`, `linkedHover`, `linkedBrush`. Hooks: `useSelection`, `useLinkedHover`, `useBrushSelection`, `useFilteredData`
139
+
140
+ **Linked crosshair** (coordinate-based hover sync): `linkedHover={{ name: "sync", mode: "x-position", xField: "time" }}` broadcasts the hovered X data value. Other charts with the same `linkedHover` name render a synced vertical crosshair at that X position. Each chart shows its own Y values independently. Use for dashboards with multiple time-series at different scales.
132
141
  **ScatterplotMatrix** — `data`, `fields`, `colorBy`, `cellSize`, `hoverMode`, `brushMode`
133
- **ChartContainer** — `title`, `subtitle`, `height` (400), `width` ("100%"), `status`, `loading`, `error`, `errorBoundary`, `actions` ({ export, fullscreen, copyConfig }), `controls`
142
+ **ChartContainer** — `title`, `subtitle`, `height` (400), `width` ("100%"), `status`, `loading`, `error`, `errorBoundary`, `actions` ({ export, fullscreen, copyConfig, dataSummary }), `controls`
134
143
  **ChartGrid** — `columns` (number|"auto"), `minCellWidth` (300), `gap` (16). `emphasis="primary"` spans two columns.
135
144
  **ContextLayout** — `context` (ReactNode), `position`, `contextSize` (250)
136
145
 
@@ -180,7 +189,8 @@ const svg = renderOrdinalToStaticSVG({ data, categoryAccessor: "cat", valueAcces
180
189
  All HOCs accept `annotations` (array). Coordinates use your data field names. Network/orbit use `nodeId`.
181
190
 
182
191
  **Positioning**: `widget` (React content at data coords — v3 replacement for v2 `htmlAnnotationRules`; props: `content`, `dx`, `dy`, `width`, `height`, `anchor`), `label` (callout with connector), `callout` (circle + label), `text` (plain text), `bracket`
183
- **Reference lines**: `y-threshold` (`value`, `label`, `color`), `x-threshold`, `band` (`y0`, `y1`, `label`, `fill`)
192
+ **Reference lines**: `y-threshold` (`value`, `label`, `color`, `labelPosition`: "left"|"center"|"right", `strokeDasharray`), `x-threshold` (`labelPosition`: "top"|"center"|"bottom"), `band` (`y0`, `y1`, `label`, `fill`)
193
+ **Ordinal**: `category-highlight` (`category`, `color`, `opacity`, `label`) — highlights a category column/row. Works on BarChart, StackedBarChart, etc. `y-threshold` also works on vertical ordinal charts.
184
194
  **Enclosures**: `enclose` (circle around `coordinates`), `rect-enclose`, `highlight` (`filter` fn or `field`+`value`)
185
195
  **Statistical** (XY): `trend` (`method`: linear/polynomial/loess), `envelope`, `anomaly-band`, `forecast`
186
196
  **Streaming anchors**: `"fixed"` (default), `"latest"` (tracks newest datum), `"sticky"` (freezes when evicted)
@@ -205,10 +215,24 @@ import { ThemeProvider } from "semiotic"
205
215
  <ThemeProvider theme={{ colors: { primary: "#ff6b6b", categorical: [...] } }}> {/* Custom */}
206
216
  ```
207
217
 
208
- Presets: `light`, `dark`, `high-contrast`, `pastels`, `pastels-dark`, `bi-tool`, `bi-tool-dark`, `italian`, `italian-dark`, `tufte`, `tufte-dark`, `journalist`, `journalist-dark`, `playful`, `playful-dark`.
218
+ **Color resolution priority** (when `colorBy` is set): explicit `colorScheme` prop > ThemeProvider `colors.categorical` > `"category10"` fallback. This means ThemeProvider categorical colors automatically apply to all charts — no need to pass `colorScheme` on every component.
219
+
220
+ Presets: `light`, `dark`, `high-contrast`, `pastels`, `pastels-dark`, `bi-tool`, `bi-tool-dark`, `italian`, `italian-dark`, `tufte`, `tufte-dark`, `journalist`, `journalist-dark`, `playful`, `playful-dark`, `carbon`, `carbon-dark`.
209
221
 
210
222
  Serialization (`semiotic/themes`): `themeToCSS(theme, selector)`, `themeToTokens(theme)`, `resolveThemePreset(name)`.
211
223
  Color-blind palette: `import { COLOR_BLIND_SAFE_CATEGORICAL } from "semiotic"` (8-color Wong 2011).
224
+ IBM Carbon palette: `import { CARBON_CATEGORICAL_14, CARBON_ALERT } from "semiotic"` (14-color categorical + 4 alert colors).
225
+
226
+ **`semiotic/utils`** (~137KB, ~10% of full bundle) — Lightweight entry point for utilities without any chart components:
227
+ - **Theme**: `ThemeProvider`, `useTheme`, `LIGHT_THEME`, `DARK_THEME`, `HIGH_CONTRAST_THEME`, `COLOR_BLIND_SAFE_CATEGORICAL`, `CARBON_CATEGORICAL_14`, `CARBON_ALERT`, `themeToCSS`, `themeToTokens`, `resolveThemePreset`, `THEME_PRESETS`
228
+ - **Format**: `adaptiveTimeTicks`, `smartTickFormat`
229
+ - **Color**: `darkenColor`, `lightenColor`
230
+ - **Patterns**: `createHatchPattern`
231
+ - **Validation**: `validateProps`, `diagnoseConfig`
232
+ - **Serialization**: `toConfig`, `fromConfig`, `toURL`, `fromURL`, `copyConfig`, `configToJSX`, `serializeSelections`, `deserializeSelections`, `exportChart`
233
+ - **Vega-Lite**: `fromVegaLite` — convert Vega-Lite specs to Semiotic configs
234
+ - **Data structures**: `RingBuffer`, `IncrementalExtent`
235
+ - **Tooltip**: `normalizeTooltip`
212
236
 
213
237
  Key: `ThemeProvider` sets CSS vars on a wrapper div (no React context). Canvas charts read vars via `getComputedStyle`. `exportChart` inlines computed styles.
214
238
 
@@ -236,9 +260,26 @@ Key: `ThemeProvider` sets CSS vars on a wrapper div (no React context). Canvas c
236
260
 
237
261
  `createHatchPattern({ background, stroke, lineWidth, spacing, angle })` from `semiotic` — returns `CanvasPattern | null` for use as `fill` in style functions. Used by FunnelChart vertical mode for dropoff bars.
238
262
 
263
+ ## Accessibility
264
+
265
+ Charts render with `role="group"` (outer interactive wrapper, keyboard/focus) and `role="img"` (inner canvas, read by assistive tech). SVG overlays include `<title>` and `<desc>`.
266
+
267
+ **Keyboard navigation**: Arrow keys navigate data points. In XY/ordinal charts, ArrowRight/Left moves within a series, ArrowUp/Down switches series. In network charts, arrows move to the spatially nearest node in the pressed direction; Enter cycles edge-connected neighbors. Home/End jump to first/last. PageUp/PageDown skip 10%. Escape clears focus.
268
+
269
+ **Focus ring**: Shape-adaptive dashed ring (circle for points, rect for bars, arc for wedges). Color: `--semiotic-focus` CSS var.
270
+
271
+ **Data summary**: `accessibleTable` (default true) renders a sr-only summary. Activate via keyboard focus or `actions.dataSummary` in ChartContainer. JIT-computed — no render cost until activated.
272
+
273
+ **Reduced motion**: `prefers-reduced-motion` auto-detected. Transitions skip to end state, orbit stops, pulse/decay disabled.
274
+
275
+ **High contrast**: `forced-colors` / `prefers-contrast: more` auto-detected. ThemeProvider applies `HIGH_CONTRAST_THEME` automatically.
276
+
277
+ **Hooks** (from `semiotic`): `useReducedMotion()`, `useHighContrast()` — SSR-safe, return `false` on server.
278
+
239
279
  ## Known Pitfalls
240
280
 
241
281
  - **Tooltip datum shape**: HOC tooltip functions get raw data. Frame `tooltipContent` gets wrapped data — use `d.data`.
282
+ - **Tooltip positioning**: Tooltips auto-flip when near container edges (right→left, bottom→top). Custom `tooltip` content should not add its own background — the wrapper provides `--semiotic-tooltip-bg`, `--semiotic-tooltip-text`, etc. Override wrapper styles via CSS custom properties, not inline styles.
242
283
  - **Legend positioning**: "bottom" auto-expands margin ~80px. For narrow charts (<400px), prefer "bottom" or "top".
243
284
  - **MultiAxisLineChart legend**: Always use `legendPosition="bottom"` (or `"top"`) — the right-hand axis occupies the space where a right-side legend would go.
244
285
  - **Log scale**: Clamps domain min to 1e-6 (log(0) undefined).
@@ -248,6 +289,7 @@ Key: `ThemeProvider` sets CSS vars on a wrapper div (no React context). Canvas c
248
289
  - **Push API**: Omit `data` prop entirely. `data={[]}` clears pushed data every render.
249
290
  - **frameProps style functions**: Bypass HOC color resolution — use `colorBy` prop instead. Frame style functions receive `(datum, categoryName)`, not `(datum, index)`.
250
291
  - **v2 migration**: `htmlAnnotationRules` → `widget` annotations + `svgAnnotationRules`. v2 `summaryStyle` index-based coloring → v3 category-string-based.
292
+ - **accessibleTable**: Direct prop on HOCs. Set `accessibleTable={false}` to disable the sr-only data summary.
251
293
 
252
294
  ## Performance
253
295
 
package/README.md CHANGED
@@ -34,7 +34,7 @@ generate correct code without examples.
34
34
  Semiotic ships with everything an AI coding assistant needs to generate
35
35
  correct visualizations without trial and error:
36
36
 
37
- - **`semiotic/ai`** — a single import with all 37 chart components, optimized for LLM code generation
37
+ - **`semiotic/ai`** — a single import with all 38 chart components, optimized for LLM code generation
38
38
  - **`ai/schema.json`** — machine-readable prop schemas for every component
39
39
  - **`npx semiotic-mcp`** — an MCP server for tool-based chart rendering in any MCP client
40
40
  - **`npx semiotic-ai --doctor`** — validate component + props JSON from the command line with typo suggestions and anti-pattern detection
@@ -59,8 +59,8 @@ ref-based push API. Built-in decay, pulse, and staleness encoding for
59
59
  monitoring dashboards.
60
60
 
61
61
  **Coordinated views.** `LinkedCharts` provides hover cross-highlighting,
62
- brush cross-filtering, and selection synchronization across any combination
63
- of chart types — zero wiring.
62
+ brush cross-filtering, coordinate-based linked crosshairs, and selection
63
+ synchronization across any combination of chart types — zero wiring.
64
64
 
65
65
  **Geographic visualization.** Choropleth maps, proportional symbol maps, flow
66
66
  maps with animated particles, and distance cartograms — all canvas-rendered
@@ -239,8 +239,8 @@ import { LineChart, BarChart } from "semiotic"
239
239
 
240
240
  | Category | Components |
241
241
  |---|---|
242
- | **XY** | `LineChart` `AreaChart` `StackedAreaChart` `Scatterplot` `ConnectedScatterplot` `BubbleChart` `Heatmap` `QuadrantChart` `MinimapChart` |
243
- | **Categorical** | `BarChart` `StackedBarChart` `GroupedBarChart` `SwarmPlot` `BoxPlot` `Histogram` `ViolinPlot` `RidgelinePlot` `DotPlot` `PieChart` `DonutChart` |
242
+ | **XY** | `LineChart` `AreaChart` `StackedAreaChart` `Scatterplot` `ConnectedScatterplot` `BubbleChart` `Heatmap` `QuadrantChart` `MultiAxisLineChart` `MinimapChart` |
243
+ | **Categorical** | `BarChart` `StackedBarChart` `GroupedBarChart` `LikertChart` `SwimlaneChart` `FunnelChart` `SwarmPlot` `BoxPlot` `Histogram` `ViolinPlot` `RidgelinePlot` `DotPlot` `PieChart` `DonutChart` |
244
244
  | **Network** | `ForceDirectedGraph` `ChordDiagram` `SankeyDiagram` `TreeDiagram` `Treemap` `CirclePack` `OrbitDiagram` |
245
245
  | **Geo** | `ChoroplethMap` `ProportionalSymbolMap` `FlowMap` `DistanceCartogram` |
246
246
  | **Realtime** | `RealtimeLineChart` `RealtimeHistogram` `RealtimeSwarmChart` `RealtimeWaterfallChart` `RealtimeHeatmap` |
@@ -282,11 +282,11 @@ for color, size, aggregation, and binning.
282
282
  Import only what you need:
283
283
 
284
284
  ```jsx
285
- import { LineChart } from "semiotic/xy" // ~156 KB
286
- import { BarChart } from "semiotic/ordinal" // ~124 KB
287
- import { ForceDirectedGraph } from "semiotic/network" // ~123 KB
288
- import { ChoroplethMap } from "semiotic/geo" // ~102 KB (+ d3-geo peer)
289
- import { LineChart } from "semiotic/ai" // ~397 KB (all HOCs)
285
+ import { LineChart } from "semiotic/xy" // ~123 KB gzip
286
+ import { BarChart } from "semiotic/ordinal" // ~88 KB gzip
287
+ import { ForceDirectedGraph } from "semiotic/network" // ~89 KB gzip
288
+ import { ChoroplethMap } from "semiotic/geo" // ~82 KB gzip
289
+ import { LineChart } from "semiotic/ai" // ~236 KB gzip (all HOCs)
290
290
  ```
291
291
 
292
292
  Granular entry points export only v3 Stream Frames and HOC charts — no legacy
@@ -443,7 +443,7 @@ npx semiotic-ai --compact # compact schema (fewer tokens)
443
443
  [Interactive docs and examples](https://semiotic.nteract.io)
444
444
 
445
445
  - [Getting Started](https://semiotic.nteract.io/getting-started)
446
- - [Charts](https://semiotic.nteract.io/charts) — all 37 chart types with live examples
446
+ - [Charts](https://semiotic.nteract.io/charts) — all 38 chart types with live examples
447
447
  - [Frames](https://semiotic.nteract.io/frames) — full Frame API reference
448
448
  - [Features](https://semiotic.nteract.io/features) — axes, annotations, tooltips, styling, Vega-Lite translator
449
449
  - [Cookbook](https://semiotic.nteract.io/cookbook) — advanced patterns and recipes
package/ai/examples.md CHANGED
@@ -995,3 +995,94 @@ import { ThemeProvider, DARK_THEME, COLOR_BLIND_SAFE_CATEGORICAL } from "semioti
995
995
  showGrid
996
996
  />
997
997
  ```
998
+
999
+ ---
1000
+
1001
+ ## Click Handlers
1002
+
1003
+ ### onClick on BarChart
1004
+
1005
+ ```jsx
1006
+ import { BarChart } from "semiotic/ai"
1007
+
1008
+ <BarChart
1009
+ data={salesData}
1010
+ categoryAccessor="region"
1011
+ valueAccessor="revenue"
1012
+ onClick={(datum, { x, y }) => {
1013
+ console.log(`Clicked ${datum.region}: $${datum.revenue}`)
1014
+ setSelectedRegion(datum.region)
1015
+ }}
1016
+ />
1017
+ ```
1018
+
1019
+ Key props: `onClick` receives the original datum and pixel coordinates. Works on all chart types.
1020
+
1021
+ ---
1022
+
1023
+ ## Linked Crosshair (Multi-chart Hover Sync)
1024
+
1025
+ ### Synced crosshair across time-series charts
1026
+
1027
+ ```jsx
1028
+ import { LineChart, LinkedCharts } from "semiotic/ai"
1029
+
1030
+ <LinkedCharts>
1031
+ <LineChart
1032
+ data={cpuData}
1033
+ xAccessor="time" yAccessor="cpu"
1034
+ linkedHover={{ name: "metrics", mode: "x-position", xField: "time" }}
1035
+ selection={{ name: "metrics" }}
1036
+ />
1037
+ <LineChart
1038
+ data={memoryData}
1039
+ xAccessor="time" yAccessor="memory"
1040
+ linkedHover={{ name: "metrics", mode: "x-position", xField: "time" }}
1041
+ selection={{ name: "metrics" }}
1042
+ />
1043
+ </LinkedCharts>
1044
+ ```
1045
+
1046
+ Key props: `linkedHover` with `mode: "x-position"` broadcasts the hovered X value. Each chart shows its own tooltip with its own Y values. Use for multi-metric dashboards.
1047
+
1048
+ ---
1049
+
1050
+ ## Category Format (Custom Tick Labels)
1051
+
1052
+ ### Truncated category labels
1053
+
1054
+ ```jsx
1055
+ import { BarChart } from "semiotic/ai"
1056
+
1057
+ <BarChart
1058
+ data={data}
1059
+ categoryAccessor="department"
1060
+ valueAccessor="headcount"
1061
+ orientation="horizontal"
1062
+ categoryFormat={(label) => label.length > 12 ? label.slice(0, 12) + "…" : label}
1063
+ />
1064
+ ```
1065
+
1066
+ Key props: `categoryFormat` receives each tick label and returns a formatted string. Available on all ordinal HOCs except Pie/Donut.
1067
+
1068
+ ---
1069
+
1070
+ ## Ordinal Annotations
1071
+
1072
+ ### Threshold + category highlight on BarChart
1073
+
1074
+ ```jsx
1075
+ import { BarChart } from "semiotic/ai"
1076
+
1077
+ <BarChart
1078
+ data={quarterlyData}
1079
+ categoryAccessor="quarter"
1080
+ valueAccessor="revenue"
1081
+ annotations={[
1082
+ { type: "y-threshold", value: 50000, label: "Target", color: "#e45050", labelPosition: "left" },
1083
+ { type: "category-highlight", category: "Q3 2024", color: "#4589ff", opacity: 0.15, label: "Current" },
1084
+ ]}
1085
+ />
1086
+ ```
1087
+
1088
+ Key props: `y-threshold` works on vertical ordinal charts. `category-highlight` highlights a category column. `labelPosition` controls label placement.
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.2.0",
4
+ "version": "3.2.1",
5
5
  "description": "React data visualization library for charts, networks, and beyond",
6
6
  "tools": [
7
7
  {
@@ -1578,6 +1578,100 @@
1578
1578
  }
1579
1579
  }
1580
1580
  },
1581
+ {
1582
+ "type": "function",
1583
+ "function": {
1584
+ "name": "LikertChart",
1585
+ "description": "Visualize Likert scale survey responses. Horizontal (default): diverging bar chart centered at 0% — negative levels extend left, positive right, neutral (if odd count) split 50/50 across centerline. Vertical: stacked 100% bar chart. Supports raw integer scores (1-based, aggregated automatically) or pre-aggregated (question, level, count) data. The levels array defines polarity: first half = negative, second half = positive, center = neutral (if odd). Works with any scale size (3-point to 7-point+). Supports push API for streaming — accumulates raw data and re-aggregates on each push.",
1586
+ "parameters": {
1587
+ "type": "object",
1588
+ "properties": {
1589
+ "data": {
1590
+ "type": "array",
1591
+ "items": {
1592
+ "type": "object"
1593
+ },
1594
+ "description": "Array of raw response or pre-aggregated data objects"
1595
+ },
1596
+ "levels": {
1597
+ "type": "array",
1598
+ "items": {
1599
+ "type": "string"
1600
+ },
1601
+ "description": "Ordered response labels, most negative to most positive (required). Odd count = center is neutral."
1602
+ },
1603
+ "categoryAccessor": {
1604
+ "type": "string",
1605
+ "description": "Question/item field (ordinal axis)",
1606
+ "default": "question"
1607
+ },
1608
+ "valueAccessor": {
1609
+ "type": "string",
1610
+ "description": "Integer score field for raw response mode (1-based: score 1 → levels[0])",
1611
+ "default": "score"
1612
+ },
1613
+ "levelAccessor": {
1614
+ "type": "string",
1615
+ "description": "Level name field for pre-aggregated mode. Each value must match an entry in levels."
1616
+ },
1617
+ "countAccessor": {
1618
+ "type": "string",
1619
+ "description": "Count/frequency field for pre-aggregated mode",
1620
+ "default": "count"
1621
+ },
1622
+ "orientation": {
1623
+ "type": "string",
1624
+ "enum": [
1625
+ "horizontal",
1626
+ "vertical"
1627
+ ],
1628
+ "default": "horizontal"
1629
+ },
1630
+ "colorScheme": {
1631
+ "type": "array",
1632
+ "items": {
1633
+ "type": "string"
1634
+ },
1635
+ "description": "One color per level. Use a diverging palette (red → gray → blue). Defaults to Carbon-inspired diverging."
1636
+ },
1637
+ "barPadding": {
1638
+ "type": "number",
1639
+ "default": 20
1640
+ },
1641
+ "title": {
1642
+ "type": "string"
1643
+ },
1644
+ "width": {
1645
+ "type": "number",
1646
+ "default": 600
1647
+ },
1648
+ "height": {
1649
+ "type": "number",
1650
+ "default": 400
1651
+ },
1652
+ "enableHover": {
1653
+ "type": "boolean",
1654
+ "default": true
1655
+ },
1656
+ "showLegend": {
1657
+ "type": "boolean",
1658
+ "default": true
1659
+ },
1660
+ "showGrid": {
1661
+ "type": "boolean",
1662
+ "default": false
1663
+ },
1664
+ "tooltip": {
1665
+ "type": "boolean",
1666
+ "default": true
1667
+ }
1668
+ },
1669
+ "required": [
1670
+ "levels"
1671
+ ]
1672
+ }
1673
+ }
1674
+ },
1581
1675
  {
1582
1676
  "type": "function",
1583
1677
  "function": {
@@ -2508,10 +2602,6 @@
2508
2602
  "description": "Starting angle in radians",
2509
2603
  "default": 0
2510
2604
  },
2511
- "slicePadding": {
2512
- "type": "number",
2513
- "default": 2
2514
- },
2515
2605
  "title": {
2516
2606
  "type": "string"
2517
2607
  },
@@ -2623,10 +2713,6 @@
2623
2713
  "type": "number",
2624
2714
  "default": 0
2625
2715
  },
2626
- "slicePadding": {
2627
- "type": "number",
2628
- "default": 2
2629
- },
2630
2716
  "title": {
2631
2717
  "type": "string"
2632
2718
  },
@@ -4504,6 +4590,134 @@
4504
4590
  ]
4505
4591
  }
4506
4592
  }
4593
+ },
4594
+ {
4595
+ "type": "function",
4596
+ "function": {
4597
+ "name": "SwimlaneChart",
4598
+ "description": "Categorical lanes with sequentially stacked items colored by subcategory. Unlike StackedBarChart, the same subcategory can appear multiple times in the same lane — items stack left-to-right (horizontal) or bottom-to-top (vertical) in data order. Supports brush for value-axis selection and push API for streaming.",
4599
+ "parameters": {
4600
+ "type": "object",
4601
+ "properties": {
4602
+ "data": {
4603
+ "type": "array",
4604
+ "items": {
4605
+ "type": "object"
4606
+ },
4607
+ "description": "Array of data objects. Omit for push API mode."
4608
+ },
4609
+ "categoryAccessor": {
4610
+ "type": "string",
4611
+ "description": "Key for lane categories (swim lanes)",
4612
+ "default": "category"
4613
+ },
4614
+ "subcategoryAccessor": {
4615
+ "type": "string",
4616
+ "description": "Key for item subcategory (color grouping within lanes). Required. Duplicate subcategories in the same lane stack sequentially."
4617
+ },
4618
+ "valueAccessor": {
4619
+ "type": "string",
4620
+ "description": "Key for item size/duration along the value axis",
4621
+ "default": "value"
4622
+ },
4623
+ "colorBy": {
4624
+ "type": "string",
4625
+ "description": "Key to determine color encoding. Defaults to subcategoryAccessor."
4626
+ },
4627
+ "colorScheme": {
4628
+ "type": [
4629
+ "string",
4630
+ "array"
4631
+ ],
4632
+ "description": "Named d3 color scheme or array of color strings"
4633
+ },
4634
+ "orientation": {
4635
+ "type": "string",
4636
+ "enum": ["horizontal", "vertical"],
4637
+ "description": "Horizontal renders lanes as rows; vertical as columns.",
4638
+ "default": "horizontal"
4639
+ },
4640
+ "barPadding": {
4641
+ "type": "number",
4642
+ "description": "Padding between lanes in pixels",
4643
+ "default": 40
4644
+ },
4645
+ "brush": {
4646
+ "type": "boolean",
4647
+ "description": "Enable value-axis brush selection"
4648
+ },
4649
+ "onBrush": {
4650
+ "type": "function",
4651
+ "description": "Callback with { r: [min, max] } or null when brush clears"
4652
+ },
4653
+ "linkedBrush": {
4654
+ "type": [
4655
+ "string",
4656
+ "object"
4657
+ ],
4658
+ "description": "LinkedCharts brush integration name"
4659
+ },
4660
+ "showCategoryTicks": {
4661
+ "type": "boolean",
4662
+ "description": "Show lane labels on the category axis"
4663
+ },
4664
+ "annotations": {
4665
+ "type": "array",
4666
+ "description": "Annotation objects to render on the chart.",
4667
+ "items": {
4668
+ "type": "object"
4669
+ }
4670
+ },
4671
+ "title": {
4672
+ "type": "string"
4673
+ },
4674
+ "width": {
4675
+ "type": "number",
4676
+ "default": 600
4677
+ },
4678
+ "height": {
4679
+ "type": "number",
4680
+ "default": 400
4681
+ },
4682
+ "margin": {
4683
+ "type": "object",
4684
+ "description": "Chart margins: { top, right, bottom, left }"
4685
+ },
4686
+ "responsiveWidth": {
4687
+ "type": "boolean",
4688
+ "description": "Auto-resize to container width"
4689
+ },
4690
+ "tooltip": {
4691
+ "type": [
4692
+ "function",
4693
+ "object"
4694
+ ],
4695
+ "description": "Tooltip content function or config."
4696
+ },
4697
+ "showLegend": {
4698
+ "type": "boolean",
4699
+ "description": "Show legend (auto-enabled with colorBy)"
4700
+ },
4701
+ "legendPosition": {
4702
+ "type": "string",
4703
+ "enum": [
4704
+ "right",
4705
+ "left",
4706
+ "top",
4707
+ "bottom"
4708
+ ],
4709
+ "default": "right"
4710
+ },
4711
+ "enableHover": {
4712
+ "type": "boolean",
4713
+ "default": true
4714
+ }
4715
+ },
4716
+ "required": [
4717
+ "subcategoryAccessor"
4718
+ ]
4719
+ }
4720
+ }
4507
4721
  }
4508
4722
  ]
4509
4723
  }
@@ -21,6 +21,8 @@ Use `import { ComponentName } from "semiotic/ai"` for all components below.
21
21
  - **PieChart** — `categoryAccessor`, `valueAccessor`
22
22
  - **DonutChart** — `categoryAccessor`, `valueAccessor`, `innerRadius`, `centerContent` (ReactNode, e.g. `<div>50%</div>`)
23
23
 
24
+ All ordinal HOCs (except Pie/Donut) support `categoryFormat` — a function `(label, index?) => string` for custom tick label formatting (truncation, abbreviation).
25
+
24
26
  ## Hierarchical Data (`data: { children: [...] }`)
25
27
  - **TreeDiagram** — `childrenAccessor`, `nodeIdAccessor`, `layout` ("tree"|"cluster"|"partition"), `orientation`
26
28
  - **Treemap** — `childrenAccessor`, `valueAccessor`, `nodeIdAccessor`, `colorByDepth`
@@ -65,20 +67,24 @@ For advanced streaming control, use Stream Frames (`StreamXYFrame`, `StreamOrdin
65
67
  - When using with `size={[w, h]}`, set `height={h}` on the container or you'll get extra whitespace.
66
68
 
67
69
  ## Common Props (all components)
68
- `width`, `height`, `margin`, `title`, `colorBy`, `colorScheme`, `enableHover`, `tooltip`, `showLegend`, `className`, `frameProps`, `onObservation`, `emphasis` ("primary"|"secondary")
70
+ `width`, `height`, `margin`, `title`, `colorBy`, `colorScheme`, `enableHover`, `tooltip`, `showLegend`, `className`, `frameProps`, `onObservation`, `onClick`, `emphasis` ("primary"|"secondary")
69
71
 
70
72
  ### tooltip
71
73
  `true` (default) | `false` | `(datum) => ReactNode` (function receives your raw data) | config `{ fields?, title?, format?, style? }`
72
74
 
75
+ ### onClick
76
+ `onClick={(datum, { x, y }) => ...}` — called when a data element is clicked. Receives the original datum and pixel coordinates. Works on lines, bars, areas, pie slices, nodes, and geo features.
77
+
73
78
  ### onObservation
74
79
  Callback receiving `ChartObservation`: `{ type: "hover"|"click"|"brush"|"selection", datum: <your data>, x, y, timestamp, chartType, chartId }`. The `datum` field is your original data object. Hover-end/click-end events omit `datum`.
75
80
 
76
81
  ### emphasis
77
82
  `emphasis="primary"` makes a chart span two columns inside a `ChartGrid`.
78
83
 
79
- ## Annotations (XY charts)
80
- - `annotations={[{ type: "y-threshold", value: 200, label: "SLA limit", color: "#e45050" }]}` — horizontal reference line
81
- - `annotations={[{ type: "x-threshold", value: 50, label: "Cutoff" }]}` — vertical reference line
84
+ ## Annotations (XY and ordinal charts)
85
+ - `annotations={[{ type: "y-threshold", value: 200, label: "SLA limit", color: "#e45050", labelPosition: "right" }]}` — horizontal reference line (works on XY and vertical ordinal charts). `labelPosition`: "left"|"center"|"right"
86
+ - `annotations={[{ type: "x-threshold", value: 50, label: "Cutoff", labelPosition: "top" }]}` — vertical reference line. `labelPosition`: "top"|"center"|"bottom"
87
+ - `annotations={[{ type: "category-highlight", category: "Q3 2024", color: "#4589ff", opacity: 0.15 }]}` — highlight a category column/row in ordinal charts
82
88
  - `annotations={[{ type: "widget", time: 42, latency: 850, dy: -10, content: <span>Alert</span> }]}` — place React element at data coordinates
83
89
  - `annotations={[{ type: "enclose", coordinates: [datum1, datum2], label: "Cluster" }]}` — circle enclosing data points
84
90
 
@@ -99,6 +105,8 @@ All charts respond to CSS custom properties on any ancestor:
99
105
  ```
100
106
  Or use ThemeProvider with 15 named presets: `<ThemeProvider theme="tufte">`, `"tufte-dark"`, `"pastels"`, `"bi-tool"`, `"italian"`, `"journalist"`, `"playful"` (each with `-dark` variant), `"dark"`, `"high-contrast"`.
101
107
 
108
+ **Color resolution**: When `colorBy` is set, charts use: explicit `colorScheme` prop > ThemeProvider `colors.categorical` > `"category10"`. No need to pass `colorScheme` on every chart when using ThemeProvider.
109
+
102
110
  `semiotic/themes` entry point: `themeToCSS(theme, selector)` generates CSS string, `themeToTokens(theme)` generates DTCG design tokens, `resolveThemePreset("tufte")` returns theme object by name. Theme objects: `TUFTE_LIGHT`, `TUFTE_DARK`, `PASTELS_LIGHT`, `BI_TOOL_LIGHT`, `ITALIAN_LIGHT`, `JOURNALIST_LIGHT`, `PLAYFUL_LIGHT`, etc.
103
111
 
104
112
  `COLOR_BLIND_SAFE_CATEGORICAL` — 8-color accessible palette (Wong 2011). Import from `semiotic`.
@@ -23,6 +23,8 @@ export interface ChartContainerProps {
23
23
  copyConfig?: boolean | {
24
24
  format?: CopyFormat;
25
25
  };
26
+ /** Enable "Data Summary" action button — shows statistical summary + sample rows */
27
+ dataSummary?: boolean;
26
28
  };
27
29
  /** Chart configuration for serialization. Enables the "Copy Config" toolbar action. */
28
30
  chartConfig?: ChartConfig;
@@ -0,0 +1,12 @@
1
+ import * as React from "react";
2
+ interface DataSummaryState {
3
+ /** When true, AccessibleDataTable renders visibly instead of sr-only */
4
+ visible: boolean;
5
+ setVisible: React.Dispatch<React.SetStateAction<boolean>>;
6
+ }
7
+ export declare function DataSummaryProvider({ children }: {
8
+ children: React.ReactNode;
9
+ }): React.JSX.Element;
10
+ export declare function useDataSummary(): DataSummaryState | null;
11
+ export declare function useDataSummaryToggle(): (() => void) | null;
12
+ export {};