semiotic 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +242 -29
- package/README.md +101 -66
- package/ai/cli.js +34 -21
- package/ai/dist/componentRegistry.js +2 -0
- package/ai/dist/mcp-server.js +54 -0
- package/ai/examples.md +433 -18
- package/ai/schema.json +134 -1
- package/ai/system-prompt.md +51 -10
- package/dist/{ChartGrid.d.ts → components/ChartGrid.d.ts} +9 -7
- package/dist/components/Legend.d.ts +9 -0
- package/dist/{LinkedCharts.d.ts → components/LinkedCharts.d.ts} +34 -1
- package/dist/{Tooltip → components/Tooltip}/Tooltip.d.ts +9 -2
- package/dist/components/charts/geo/ChoroplethMap.d.ts +53 -0
- package/dist/components/charts/geo/DistanceCartogram.d.ts +90 -0
- package/dist/components/charts/geo/FlowMap.d.ts +83 -0
- package/dist/components/charts/geo/ProportionalSymbolMap.d.ts +67 -0
- package/dist/components/charts/geo/index.d.ts +8 -0
- package/dist/{charts → components/charts}/index.d.ts +4 -0
- package/dist/{charts → components/charts}/network/ChordDiagram.d.ts +8 -5
- package/dist/{charts → components/charts}/network/CirclePack.d.ts +4 -2
- package/dist/{charts → components/charts}/network/ForceDirectedGraph.d.ts +10 -6
- package/dist/components/charts/network/OrbitDiagram.d.ts +79 -0
- package/dist/{charts → components/charts}/network/SankeyDiagram.d.ts +8 -5
- package/dist/{charts → components/charts}/network/TreeDiagram.d.ts +4 -2
- package/dist/{charts → components/charts}/network/Treemap.d.ts +4 -2
- package/dist/{charts → components/charts}/ordinal/BarChart.d.ts +9 -5
- package/dist/{charts → components/charts}/ordinal/BoxPlot.d.ts +9 -5
- package/dist/{charts → components/charts}/ordinal/DonutChart.d.ts +9 -5
- package/dist/{charts → components/charts}/ordinal/DotPlot.d.ts +9 -5
- package/dist/{charts → components/charts}/ordinal/GroupedBarChart.d.ts +9 -5
- package/dist/{charts → components/charts}/ordinal/Histogram.d.ts +8 -5
- package/dist/{charts → components/charts}/ordinal/PieChart.d.ts +9 -5
- package/dist/{charts → components/charts}/ordinal/RidgelinePlot.d.ts +2 -0
- package/dist/{charts → components/charts}/ordinal/StackedBarChart.d.ts +9 -5
- package/dist/{charts → components/charts}/ordinal/SwarmPlot.d.ts +9 -5
- package/dist/{charts → components/charts}/ordinal/ViolinPlot.d.ts +8 -5
- package/dist/{charts → components/charts}/realtime/RealtimeHeatmap.d.ts +24 -6
- package/dist/{charts → components/charts}/realtime/RealtimeHistogram.d.ts +28 -7
- package/dist/{charts → components/charts}/realtime/RealtimeLineChart.d.ts +23 -5
- package/dist/{charts → components/charts}/realtime/RealtimeSwarmChart.d.ts +24 -6
- package/dist/{charts → components/charts}/realtime/RealtimeWaterfallChart.d.ts +23 -5
- package/dist/{charts → components/charts}/shared/colorUtils.d.ts +5 -0
- package/dist/components/charts/shared/diagnoseConfig.d.ts +23 -0
- package/dist/{charts → components/charts}/shared/hooks.d.ts +36 -2
- package/dist/{charts → components/charts}/shared/legendUtils.d.ts +2 -3
- package/dist/{charts → components/charts}/shared/statisticalOverlays.d.ts +1 -2
- package/dist/components/charts/shared/statisticalOverlaysLazy.d.ts +10 -0
- package/dist/components/charts/shared/stringDistance.d.ts +11 -0
- package/dist/{charts → components/charts}/shared/tooltipUtils.d.ts +2 -2
- package/dist/{charts → components/charts}/shared/types.d.ts +16 -4
- package/dist/components/charts/shared/useChartSetup.d.ts +112 -0
- package/dist/components/charts/shared/useStreamingLegend.d.ts +65 -0
- package/dist/{charts → components/charts}/shared/withChartWrapper.d.ts +10 -0
- package/dist/{charts → components/charts}/xy/AreaChart.d.ts +18 -5
- package/dist/{charts → components/charts}/xy/BubbleChart.d.ts +18 -5
- package/dist/{charts → components/charts}/xy/ConnectedScatterplot.d.ts +10 -6
- package/dist/{charts → components/charts}/xy/Heatmap.d.ts +24 -5
- package/dist/{charts → components/charts}/xy/LineChart.d.ts +47 -5
- package/dist/{charts → components/charts}/xy/MinimapChart.d.ts +3 -0
- package/dist/components/charts/xy/QuadrantChart.d.ts +120 -0
- package/dist/{charts → components/charts}/xy/Scatterplot.d.ts +11 -5
- package/dist/{charts → components/charts}/xy/StackedAreaChart.d.ts +18 -5
- package/dist/{export → components/export}/exportChart.d.ts +6 -1
- package/dist/components/geo/mergeData.d.ts +18 -0
- package/dist/components/geo/referenceGeography.d.ts +10 -0
- package/dist/components/geo/useReferenceAreas.d.ts +13 -0
- package/dist/{realtime → components/realtime}/RingBuffer.d.ts +1 -0
- package/dist/{realtime → components/realtime}/types.d.ts +17 -0
- package/dist/components/semiotic-ai.d.ts +61 -0
- package/dist/components/semiotic-data.d.ts +8 -0
- package/dist/components/semiotic-geo.d.ts +16 -0
- package/dist/components/semiotic-network.d.ts +14 -0
- package/dist/components/semiotic-ordinal.d.ts +18 -0
- package/dist/components/semiotic-realtime.d.ts +22 -0
- package/dist/components/semiotic-server.d.ts +1 -0
- package/dist/components/semiotic-xy.d.ts +17 -0
- package/dist/components/semiotic.d.ts +57 -0
- package/dist/{server → components/server}/renderToStaticSVG.d.ts +11 -2
- package/dist/components/stream/AccessibleDataTable.d.ts +50 -0
- package/dist/{stream → components/stream}/CanvasHitTester.d.ts +8 -2
- package/dist/components/stream/DataSourceAdapter.d.ts +64 -0
- package/dist/components/stream/GeoCanvasHitTester.d.ts +19 -0
- package/dist/components/stream/GeoParticlePool.d.ts +46 -0
- package/dist/components/stream/GeoPipelineStore.d.ts +81 -0
- package/dist/components/stream/GeoTileRenderer.d.ts +31 -0
- package/dist/{stream → components/stream}/NetworkPipelineStore.d.ts +16 -4
- package/dist/{stream → components/stream}/NetworkSVGOverlay.d.ts +24 -1
- package/dist/{stream → components/stream}/OrdinalPipelineStore.d.ts +8 -4
- package/dist/{stream → components/stream}/OrdinalSVGOverlay.d.ts +31 -1
- package/dist/{stream → components/stream}/PipelineStore.d.ts +64 -5
- package/dist/components/stream/SVGOverlay.d.ts +98 -0
- package/dist/{stream → components/stream}/SceneGraph.d.ts +7 -3
- package/dist/components/stream/SceneToSVG.d.ts +22 -0
- package/dist/components/stream/StreamGeoFrame.d.ts +4 -0
- package/dist/{stream → components/stream}/accessorUtils.d.ts +1 -0
- package/dist/components/stream/canvasSetup.d.ts +26 -0
- package/dist/components/stream/geoTypes.d.ts +186 -0
- package/dist/components/stream/hitTestUtils.d.ts +23 -0
- package/dist/components/stream/layouts/forceLayoutPlugin.d.ts +2 -0
- package/dist/{stream → components/stream}/layouts/index.d.ts +2 -1
- package/dist/components/stream/layouts/orbitLayoutPlugin.d.ts +2 -0
- package/dist/components/stream/legendRenderer.d.ts +33 -0
- package/dist/{stream → components/stream}/networkTypes.d.ts +59 -3
- package/dist/{stream → components/stream}/ordinalTypes.d.ts +26 -10
- package/dist/components/stream/pipelineTransitionUtils.d.ts +42 -0
- package/dist/components/stream/renderers/areaCanvasRenderer.d.ts +2 -0
- package/dist/components/stream/renderers/geoCanvasRenderer.d.ts +9 -0
- package/dist/{stream → components/stream}/renderers/heatmapCanvasRenderer.d.ts +2 -1
- package/dist/{stream → components/stream}/renderers/lineCanvasRenderer.d.ts +1 -0
- package/dist/components/stream/renderers/renderPulse.d.ts +50 -0
- package/dist/{stream → components/stream}/types.d.ts +89 -3
- package/dist/components/stream/useStalenessCheck.d.ts +16 -0
- package/dist/components/types/legendTypes.d.ts +49 -0
- package/dist/geo.min.js +1 -0
- package/dist/geo.module.min.js +1 -0
- package/dist/network.min.js +1 -1
- package/dist/network.module.min.js +1 -1
- package/dist/ordinal.min.js +1 -1
- package/dist/ordinal.module.min.js +1 -1
- package/dist/realtime.min.js +1 -1
- package/dist/realtime.module.min.js +1 -1
- package/dist/semiotic-ai.d.ts +3 -0
- package/dist/semiotic-ai.min.js +1 -1
- package/dist/semiotic-ai.module.min.js +1 -1
- package/dist/semiotic-data.d.ts +1 -0
- package/dist/semiotic-data.min.js +1 -1
- package/dist/semiotic-data.module.min.js +1 -1
- package/dist/semiotic-geo.d.ts +16 -0
- package/dist/semiotic-network.d.ts +1 -0
- package/dist/semiotic-ordinal.d.ts +1 -0
- package/dist/semiotic-server.d.ts +1 -1
- package/dist/semiotic-xy.d.ts +1 -0
- package/dist/semiotic.d.ts +4 -4
- package/dist/semiotic.min.js +1 -1
- package/dist/semiotic.module.min.js +1 -1
- package/dist/server.min.js +1 -1
- package/dist/server.module.min.js +1 -1
- package/dist/test-utils/canvasMock.d.ts +23 -0
- package/dist/test-utils/frameMock.d.ts +78 -0
- package/dist/xy.min.js +1 -1
- package/dist/xy.module.min.js +1 -1
- package/package.json +34 -20
- package/dist/Legend.d.ts +0 -3
- package/dist/stream/DataSourceAdapter.d.ts +0 -35
- package/dist/stream/SVGOverlay.d.ts +0 -56
- package/dist/stream/layouts/forceLayoutPlugin.d.ts +0 -9
- package/dist/stream/renderers/areaCanvasRenderer.d.ts +0 -7
- package/dist/types/legendTypes.d.ts +0 -20
- /package/dist/{Annotation.d.ts → components/Annotation.d.ts} +0 -0
- /package/dist/{CategoryColors.d.ts → components/CategoryColors.d.ts} +0 -0
- /package/dist/{ChartContainer.d.ts → components/ChartContainer.d.ts} +0 -0
- /package/dist/{ChartErrorBoundary.d.ts → components/ChartErrorBoundary.d.ts} +0 -0
- /package/dist/{ContextLayout.d.ts → components/ContextLayout.d.ts} +0 -0
- /package/dist/{DetailsPanel.d.ts → components/DetailsPanel.d.ts} +0 -0
- /package/dist/{ThemeProvider.d.ts → components/ThemeProvider.d.ts} +0 -0
- /package/dist/{charts → components/charts}/shared/ChartError.d.ts +0 -0
- /package/dist/{charts → components/charts}/shared/annotationRules.d.ts +0 -0
- /package/dist/{charts → components/charts}/shared/formatUtils.d.ts +0 -0
- /package/dist/{charts → components/charts}/shared/loess.d.ts +0 -0
- /package/dist/{charts → components/charts}/shared/networkUtils.d.ts +0 -0
- /package/dist/{charts → components/charts}/shared/selectionUtils.d.ts +0 -0
- /package/dist/{charts → components/charts}/shared/validateChartData.d.ts +0 -0
- /package/dist/{charts → components/charts}/shared/validateProps.d.ts +0 -0
- /package/dist/{charts → components/charts}/xy/ScatterplotMatrix.d.ts +0 -0
- /package/dist/{data → components/data}/fromVegaLite.d.ts +0 -0
- /package/dist/{data → components/data}/transforms.d.ts +0 -0
- /package/dist/{export → components/export}/chartConfig.d.ts +0 -0
- /package/dist/{export → components/export}/selectionSerializer.d.ts +0 -0
- /package/dist/{geometry → components/geometry}/sankeyLinks.d.ts +0 -0
- /package/dist/{realtime → components/realtime}/BinAccumulator.d.ts +0 -0
- /package/dist/{realtime → components/realtime}/IncrementalExtent.d.ts +0 -0
- /package/dist/{realtime → components/realtime}/renderers/types.d.ts +0 -0
- /package/dist/{realtime → components/realtime}/renderers/waterfallRenderer.d.ts +0 -0
- /package/dist/{store → components/store}/ObservationStore.d.ts +0 -0
- /package/dist/{store → components/store}/SelectionStore.d.ts +0 -0
- /package/dist/{store → components/store}/ThemeStore.d.ts +0 -0
- /package/dist/{store → components/store}/TooltipStore.d.ts +0 -0
- /package/dist/{store → components/store}/createStore.d.ts +0 -0
- /package/dist/{store → components/store}/useObservation.d.ts +0 -0
- /package/dist/{store → components/store}/useSelection.d.ts +0 -0
- /package/dist/{stream → components/stream}/MarginalGraphics.d.ts +0 -0
- /package/dist/{stream → components/stream}/NetworkCanvasHitTester.d.ts +0 -0
- /package/dist/{stream → components/stream}/OrdinalCanvasHitTester.d.ts +0 -0
- /package/dist/{stream → components/stream}/ParticlePool.d.ts +0 -0
- /package/dist/{stream → components/stream}/StreamNetworkFrame.d.ts +0 -0
- /package/dist/{stream → components/stream}/StreamOrdinalFrame.d.ts +0 -0
- /package/dist/{stream → components/stream}/StreamXYFrame.d.ts +0 -0
- /package/dist/{stream → components/stream}/keyboardNav.d.ts +0 -0
- /package/dist/{stream → components/stream}/layouts/chordLayoutPlugin.d.ts +0 -0
- /package/dist/{stream → components/stream}/layouts/hierarchyLayoutPlugin.d.ts +0 -0
- /package/dist/{stream → components/stream}/layouts/sankeyLayoutPlugin.d.ts +0 -0
- /package/dist/{stream → components/stream}/ordinalSceneBuilders/barScene.d.ts +0 -0
- /package/dist/{stream → components/stream}/ordinalSceneBuilders/connectorScene.d.ts +0 -0
- /package/dist/{stream → components/stream}/ordinalSceneBuilders/pieScene.d.ts +0 -0
- /package/dist/{stream → components/stream}/ordinalSceneBuilders/pointScene.d.ts +0 -0
- /package/dist/{stream → components/stream}/ordinalSceneBuilders/statisticalScene.d.ts +0 -0
- /package/dist/{stream → components/stream}/ordinalSceneBuilders/timelineScene.d.ts +0 -0
- /package/dist/{stream → components/stream}/ordinalSceneBuilders/types.d.ts +0 -0
- /package/dist/{stream → components/stream}/renderers/barCanvasRenderer.d.ts +0 -0
- /package/dist/{stream → components/stream}/renderers/boxplotCanvasRenderer.d.ts +0 -0
- /package/dist/{stream → components/stream}/renderers/candlestickCanvasRenderer.d.ts +0 -0
- /package/dist/{stream → components/stream}/renderers/connectorCanvasRenderer.d.ts +0 -0
- /package/dist/{stream → components/stream}/renderers/networkArcRenderer.d.ts +0 -0
- /package/dist/{stream → components/stream}/renderers/networkCircleRenderer.d.ts +0 -0
- /package/dist/{stream → components/stream}/renderers/networkEdgeRenderer.d.ts +0 -0
- /package/dist/{stream → components/stream}/renderers/networkParticleRenderer.d.ts +0 -0
- /package/dist/{stream → components/stream}/renderers/networkRectRenderer.d.ts +0 -0
- /package/dist/{stream → components/stream}/renderers/pointCanvasRenderer.d.ts +0 -0
- /package/dist/{stream → components/stream}/renderers/swarmCanvasRenderer.d.ts +0 -0
- /package/dist/{stream → components/stream}/renderers/types.d.ts +0 -0
- /package/dist/{stream → components/stream}/renderers/violinCanvasRenderer.d.ts +0 -0
- /package/dist/{stream → components/stream}/renderers/waterfallCanvasRenderer.d.ts +0 -0
- /package/dist/{stream → components/stream}/renderers/wedgeCanvasRenderer.d.ts +0 -0
- /package/dist/{stream → components/stream}/useResponsiveSize.d.ts +0 -0
- /package/dist/{types → components/types}/annotationTypes.d.ts +0 -0
- /package/dist/{types → components/types}/generalTypes.d.ts +0 -0
- /package/dist/{types → components/types}/interactionTypes.d.ts +0 -0
- /package/dist/{types → components/types}/networkTypes.d.ts +0 -0
- /package/dist/{types → components/types}/ordinalTypes.d.ts +0 -0
package/CLAUDE.md
CHANGED
|
@@ -2,25 +2,39 @@
|
|
|
2
2
|
|
|
3
3
|
## Quick Start
|
|
4
4
|
- Install: `npm install semiotic`
|
|
5
|
-
- Import: `semiotic`, `semiotic/xy`, `semiotic/ordinal`, `semiotic/network`, `semiotic/realtime`, `semiotic/ai`, `semiotic/data`
|
|
5
|
+
- Import: `semiotic`, `semiotic/xy`, `semiotic/ordinal`, `semiotic/network`, `semiotic/geo`, `semiotic/realtime`, `semiotic/ai`, `semiotic/data`, `semiotic/server`
|
|
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 (never blanks the page) and dev-mode validation warnings
|
|
9
9
|
|
|
10
10
|
## Architecture
|
|
11
11
|
- **HOC Charts**: Simple props, sensible defaults. **Stream Frames**: Full control.
|
|
12
|
+
- **Always use HOC charts** (`ForceDirectedGraph`, `SankeyDiagram`, `LineChart`, `RealtimeLineChart`, `ChoroplethMap`, etc.) unless you need sophisticated control they don't expose. Stream Frames (`StreamNetworkFrame`, `StreamXYFrame`, `StreamOrdinalFrame`, `StreamGeoFrame`) are low-level escape hatches — they accept raw `RealtimeNode`/`RealtimeEdge` wrappers in callbacks, not your data objects directly.
|
|
12
13
|
- Every HOC accepts `frameProps` to pass through. TypeScript `strict: true`.
|
|
13
14
|
|
|
14
15
|
## Common Props (all HOCs)
|
|
15
|
-
`title`, `width` (600), `height` (400), `responsiveWidth`, `responsiveHeight`, `margin`, `className`, `enableHover` (true), `tooltip
|
|
16
|
+
`title`, `width` (600), `height` (400), `responsiveWidth`, `responsiveHeight`, `margin`, `className`, `enableHover` (true), `tooltip` (boolean | `(datum) => ReactNode` | config object), `showLegend`, `showGrid` (false), `frameProps`, `onObservation` (callback, see below), `chartId`, `loading` (false), `emptyContent`, `legendInteraction` ("none"|"highlight"|"isolate"), `legendPosition` ("right"|"left"|"top"|"bottom", default "right"), `emphasis` ("primary"|"secondary")
|
|
17
|
+
|
|
18
|
+
### tooltip
|
|
19
|
+
`tooltip` accepts: `true` (default tooltip), `false` (disabled), a **function** `(datum: Record<string, any>) => ReactNode`, or a config `{ fields?: string[], title?: accessor, format?: fn, style?: CSSProperties }`. The function form receives your raw data object directly.
|
|
20
|
+
|
|
21
|
+
### onObservation
|
|
22
|
+
`onObservation` receives a `ChartObservation` with `type` and event-specific fields:
|
|
23
|
+
- **hover**: `{ type: "hover", datum: <your data>, x, y, timestamp, chartType, chartId }`
|
|
24
|
+
- **hover-end**: `{ type: "hover-end", timestamp, chartType, chartId }`
|
|
25
|
+
- **click**: `{ type: "click", datum: <your data>, x, y, timestamp, chartType, chartId }`
|
|
26
|
+
- **brush**: `{ type: "brush", extent: { x: [min, max], y: [min, max] }, timestamp, chartType }`
|
|
27
|
+
- **selection**: `{ type: "selection", selection: { name, fields }, timestamp, chartType }`
|
|
28
|
+
|
|
29
|
+
The `datum` field contains your original data object (not a wrapper).
|
|
16
30
|
|
|
17
31
|
## XY Charts (`semiotic/xy`)
|
|
18
32
|
|
|
19
|
-
**LineChart** — `data`, `xAccessor` ("x"), `yAccessor` ("y"), `lineBy`, `lineDataAccessor` ("coordinates"), `colorBy`, `colorScheme`, `curve`, `lineWidth` (2), `showPoints`, `pointRadius` (3), `fillArea`, `areaOpacity` (0.3), `anomaly` (AnomalyConfig), `forecast` (ForecastConfig)
|
|
33
|
+
**LineChart** — `data`, `xAccessor` ("x"), `yAccessor` ("y"), `lineBy`, `lineDataAccessor` ("coordinates"), `colorBy`, `colorScheme`, `curve`, `lineWidth` (2), `showPoints`, `pointRadius` (3), `fillArea`, `areaOpacity` (0.3), `anomaly` (AnomalyConfig), `forecast` (ForecastConfig), `directLabel` (boolean|{position,fontSize}), `gapStrategy` ("break"|"interpolate"|"zero"), `xScaleType` ("linear"|"log"), `yScaleType` ("linear"|"log")
|
|
20
34
|
|
|
21
35
|
**AreaChart** — LineChart props + `areaBy`, `y0Accessor` (band/ribbon), `gradientFill` (boolean|{topOpacity,bottomOpacity}), `areaOpacity` (0.7), `showLine` (true)
|
|
22
36
|
|
|
23
|
-
**StackedAreaChart** —
|
|
37
|
+
**StackedAreaChart** — flat array data + `areaBy` (required, groups into stacked areas), `colorBy`, `normalize` (false). Do NOT use `lineBy` or `lineDataAccessor` — those are LineChart props.
|
|
24
38
|
|
|
25
39
|
**Scatterplot** — `data`, `xAccessor`, `yAccessor`, `colorBy`, `sizeBy`, `sizeRange`, `pointRadius` (5), `pointOpacity` (0.8), `marginalGraphics`
|
|
26
40
|
|
|
@@ -28,64 +42,188 @@
|
|
|
28
42
|
|
|
29
43
|
**ConnectedScatterplot** — `data`, `xAccessor`, `yAccessor`, `orderAccessor` (number|Date field for sequencing), `pointRadius` (4). Viridis colored start→end, line width = point radius, white halo under lines when <100 points.
|
|
30
44
|
|
|
31
|
-
**
|
|
45
|
+
**QuadrantChart** — Scatterplot divided into four labeled, colored quadrants. `data`, `xAccessor`, `yAccessor`, `quadrants` (required: `{ topRight, topLeft, bottomRight, bottomLeft }` each with `label`, `color`, optional `opacity`), `xCenter` (vertical center line in data units), `yCenter` (horizontal center line), `centerlineStyle` (`{ stroke, strokeWidth, strokeDasharray }`), `showQuadrantLabels` (true), `quadrantLabelSize` (12), `colorBy`, `sizeBy`, `sizeRange`, `pointRadius` (5), `pointOpacity` (0.8). Supports push API. Quadrant fills and labels drawn via `canvasPreRenderers`.
|
|
46
|
+
|
|
47
|
+
**Heatmap** — `data`, `xAccessor`, `yAccessor`, `valueAccessor`, `colorScheme` ("blues"|"reds"|"greens"|"viridis" or custom), `showValues`, `cellBorderColor`. Accessors can be string field names (including string/categorical fields) or functions.
|
|
32
48
|
|
|
33
49
|
## Ordinal Charts (`semiotic/ordinal`)
|
|
34
50
|
|
|
35
|
-
**BarChart** — `data`, `categoryAccessor`, `valueAccessor`, `orientation`, `colorBy`, `sort`, `barPadding`
|
|
36
|
-
**StackedBarChart** — + `stackBy` (required), `normalize`
|
|
37
|
-
**GroupedBarChart** — + `groupBy` (required)
|
|
51
|
+
**BarChart** — `data`, `categoryAccessor`, `valueAccessor`, `orientation`, `colorBy`, `sort`, `barPadding` (40)
|
|
52
|
+
**StackedBarChart** — + `stackBy` (required), `normalize`, `barPadding` (40)
|
|
53
|
+
**GroupedBarChart** — + `groupBy` (required), `barPadding` (60)
|
|
38
54
|
**SwarmPlot** — `data`, `categoryAccessor`, `valueAccessor`, `colorBy`, `sizeBy`, `pointRadius`, `pointOpacity`
|
|
39
55
|
**BoxPlot** — + `showOutliers`, `outlierRadius`
|
|
40
|
-
**Histogram** — + `bins` (25), `relative`. Always horizontal.
|
|
56
|
+
**Histogram** — + `bins` (25), `relative`. Always horizontal. `categoryAccessor` is optional (defaults to `"category"`) — for a single-group histogram, either omit it or ensure your data has a `category` field with a single value.
|
|
41
57
|
**ViolinPlot** — + `bins`, `curve`, `showIQR`
|
|
42
58
|
**DotPlot** — + `sort` (true), `dotRadius`, `showGrid` default true
|
|
43
59
|
**PieChart** — `data`, `categoryAccessor`, `valueAccessor`, `colorBy`, `startAngle`, `slicePadding`
|
|
44
|
-
**DonutChart** — PieChart + `innerRadius` (60), `centerContent`
|
|
60
|
+
**DonutChart** — PieChart + `innerRadius` (60), `centerContent` (ReactNode — any React element, e.g. `<div>50%</div>`)
|
|
45
61
|
|
|
46
62
|
## Network Charts (`semiotic/network`)
|
|
47
63
|
|
|
48
|
-
**ForceDirectedGraph** — `nodes`, `edges`, `nodeIDAccessor`, `sourceAccessor`, `targetAccessor`, `colorBy`, `nodeSize`, `edgeWidth`, `
|
|
64
|
+
**ForceDirectedGraph** — `nodes`, `edges`, `nodeIDAccessor`, `sourceAccessor`, `targetAccessor`, `colorBy`, `colorScheme`, `nodeSize` (number|string|fn), `nodeSizeRange`, `edgeWidth`, `edgeColor`, `edgeOpacity`, `iterations` (300), `forceStrength` (0.1), `showLabels`, `nodeLabel`, `tooltip`, `showLegend`, `legendInteraction`
|
|
49
65
|
**SankeyDiagram** — `edges`, `nodes`, `valueAccessor`, `edgeColorBy`, `orientation`, `nodeAlign`, `nodeWidth`, `showLabels`, `edgeOpacity`
|
|
50
66
|
**ChordDiagram** — `edges`, `nodes`, `valueAccessor`, `edgeColorBy`, `padAngle`, `groupWidth`, `showLabels`
|
|
51
67
|
**TreeDiagram** — `data` (root), `layout`, `orientation`, `childrenAccessor`, `colorBy`, `colorByDepth`, `edgeStyle`
|
|
52
68
|
**Treemap** — `data` (root), `childrenAccessor`, `valueAccessor`, `colorBy`, `colorByDepth`, `showLabels`, `labelMode`
|
|
53
69
|
**CirclePack** — `data` (root), `childrenAccessor`, `valueAccessor`, `colorBy`, `colorByDepth`, `circleOpacity`
|
|
70
|
+
**OrbitDiagram** — animated radial/orbital hierarchy. Use this (not TreeDiagram) when you want animated orbiting nodes. `data` (root), `childrenAccessor`, `nodeIdAccessor`, `orbitMode` ("flat"|"solar"|"atomic"|number[]), `speed` (0.25), `revolution`, `eccentricity`, `orbitSize`, `nodeRadius`, `showRings`, `showLabels`, `animated` (true), `colorBy`, `colorByDepth`, `annotations` (widget annotations anchor by nodeId). For static radial trees, use `TreeDiagram layout="radial"` instead.
|
|
71
|
+
|
|
72
|
+
## Geo Charts (`semiotic/geo`)
|
|
73
|
+
|
|
74
|
+
Geographic visualization with d3-geo projections. Canvas-rendered via `StreamGeoFrame`. Import from `semiotic/geo` to avoid adding d3-geo to non-geo bundles.
|
|
75
|
+
|
|
76
|
+
**ChoroplethMap** — `areas` (GeoJSON Feature[] or reference string like "world-110m"), `valueAccessor`, `colorScheme` ("blues"|"reds"|"greens"|"viridis"), `areaOpacity` (1), `projection` ("equalEarth"), `graticule`, `tooltip`, `showLegend`
|
|
77
|
+
**ProportionalSymbolMap** — `points`, `xAccessor` ("lon"), `yAccessor` ("lat"), `sizeBy`, `sizeRange` ([3,30]), `colorBy`, `areas` (optional background), `projection`
|
|
78
|
+
**FlowMap** — `flows` ({source, target, value}), `nodes`, `xAccessor`, `yAccessor`, `nodeIdAccessor` ("id"), `valueAccessor` ("value"), `edgeColorBy`, `edgeOpacity` (0.6), `edgeWidthRange` ([1,8]), `edgeLinecap` ("round"), `lineType` ("geo"|"line"), `areas` (optional background), `showParticles`, `particleStyle` ({ radius, color, opacity, speedMultiplier, maxPerLine, spawnRate }). Particle `color` accepts a string, `"source"` (inherit line stroke), or `(datum) => string`.
|
|
79
|
+
**DistanceCartogram** — `points`, `center` (id of center node), `costAccessor`, `strength` (0-1), `lineMode` ("straight"|"fractional"), `nodeIdAccessor` ("id"), `lines`, `projection`, `showRings` (true|false|number[]), `ringStyle` ({ stroke, strokeWidth, ... }), `showNorth` (true), `costLabel` (string for ring labels), `transition` (ms for smooth animation), `pointRadius`
|
|
80
|
+
|
|
81
|
+
All geo HOCs support: `selection`, `linkedHover`, `onObservation`, `showLegend`, `legendInteraction`, `tooltip`, `loading`, `emptyContent`, `frameProps`, `fitPadding` (0–1 fraction, insets auto-fit projection from edges), `zoomable` (defaults true with tileURL, false otherwise), `zoomExtent`, `onZoom`, `dragRotate`, `graticule`, `tileURL`, `tileAttribution`, `tileCacheSize`
|
|
82
|
+
|
|
83
|
+
**Zoom/Pan**: All geo charts accept `zoomable` (boolean), `zoomExtent` ([minZoom, maxZoom], default [1, 8]), and `onZoom` (callback with `{ projection, zoom }`). Re-renders projection directly on every zoom tick (no CSS transform). Imperative API: `ref.current.getZoom()`, `ref.current.resetZoom()`.
|
|
84
|
+
|
|
85
|
+
**Geo Particles**: `FlowMap` and `StreamGeoFrame` support `showParticles` (boolean) and `particleStyle` to animate dots flowing along line paths. Uses `GeoParticlePool` — an object-pool polyline particle system. Particle `color` accepts: `"source"` (inherit line stroke), a CSS string, or `(datum) => string` for per-line color.
|
|
86
|
+
|
|
87
|
+
**Drag Rotate (Globe Spinning)**: `dragRotate` (boolean) — when true, drag gestures rotate the projection (globe spinning) instead of panning. **Defaults to true for orthographic projection.** Scroll-wheel zoom still works normally. Explicitly set `dragRotate={false}` on orthographic to get standard pan behavior, or `dragRotate={true}` on other projections to enable rotation. Latitude rotation is clamped to [-90, 90] to prevent flipping.
|
|
88
|
+
|
|
89
|
+
**Tile Maps**: All geo charts accept `tileURL` (string template like `"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"` or `(z, x, y, dpr) => string`), `tileAttribution` (e.g., `"© OpenStreetMap contributors"`), `tileCacheSize` (default 256). Tiles render on a background canvas behind data layers. **Mercator projection only** — a dev warning is emitted for non-Mercator projections. Tiles update on zoom/pan. Retina support via `{r}` placeholder or DPR parameter. **Production**: OpenStreetMap tiles are for development/demo only. For production, use a commercial tile provider (Mapbox, MapTiler, Stadia Maps) with your own API key passed via environment variable (never hard-code keys in client code). Example: `tileURL={\`https://api.mapbox.com/styles/v1/mapbox/light-v11/tiles/{z}/{x}/{y}?access_token=\${process.env.MAPBOX_TOKEN}\`}`.
|
|
90
|
+
|
|
91
|
+
**StreamGeoFrame** — low-level frame with full control. Props: `projection`, `areas`, `points`, `lines`, `xAccessor`, `yAccessor`, `areaStyle`, `pointStyle`, `lineStyle`, `graticule`, `projectionTransform` (distance cartogram config), `projectionExtent`, `enableHover`, `tooltipContent`, `zoomable`, `zoomExtent`, `onZoom`, `tileURL`, `tileAttribution`, `tileCacheSize`, `decay`, `pulse`, `transition`. Push API: `ref.current.push(datum)`, `ref.current.pushMany(data)`, `ref.current.clear()`.
|
|
92
|
+
|
|
93
|
+
**Reference geography**: `resolveReferenceGeography("world-110m")` returns GeoJSON features from Natural Earth data (world-atlas). Supported: `"world-110m"`, `"world-50m"`, `"land-110m"`, `"land-50m"`. All geo HOCs accept `areas` as `GeoJSON.Feature[]` or a reference string.
|
|
94
|
+
|
|
95
|
+
**mergeData(features, data, { featureKey, dataKey })** — join external data into GeoJSON features by key field. Supports nested paths (e.g., `"properties.iso_a3"`). World-atlas uses ISO 3166-1 numeric codes as the `id` field. Also available from `semiotic/data` as a general join-by-key utility.
|
|
96
|
+
|
|
97
|
+
```jsx
|
|
98
|
+
// World choropleth with reference geography + data joining
|
|
99
|
+
import { ChoroplethMap, resolveReferenceGeography, mergeData } from "semiotic/geo"
|
|
100
|
+
const world = await resolveReferenceGeography("world-110m")
|
|
101
|
+
const areas = mergeData(world, gdpData, { featureKey: "id", dataKey: "id" })
|
|
102
|
+
<ChoroplethMap areas={areas} valueAccessor="gdpPerCapita" colorScheme="viridis"
|
|
103
|
+
projection="equalEarth" zoomable tooltip />
|
|
104
|
+
|
|
105
|
+
// Distance cartogram (ORBIS-style) with concentric rings overlay
|
|
106
|
+
import { DistanceCartogram } from "semiotic/geo"
|
|
107
|
+
<DistanceCartogram
|
|
108
|
+
points={cities} center="rome" costAccessor="travelDays"
|
|
109
|
+
strength={0.8} lines={routes} showLegend zoomable
|
|
110
|
+
showRings costLabel="days" showNorth
|
|
111
|
+
ringStyle={{ stroke: "#999", strokeWidth: 0.5 }}
|
|
112
|
+
/>
|
|
113
|
+
|
|
114
|
+
// Tile map basemap with proportional symbols
|
|
115
|
+
<ProportionalSymbolMap
|
|
116
|
+
points={earthquakes} xAccessor="lon" yAccessor="lat"
|
|
117
|
+
sizeBy="magnitude" sizeRange={[2, 20]}
|
|
118
|
+
projection="mercator" zoomable
|
|
119
|
+
tileURL="https://tile.openstreetmap.org/{z}/{x}/{y}.png"
|
|
120
|
+
tileAttribution="© OpenStreetMap contributors"
|
|
121
|
+
/>
|
|
122
|
+
|
|
123
|
+
// Streaming geo points with zoom
|
|
124
|
+
const geoRef = useRef()
|
|
125
|
+
geoRef.current.push({ lon: -122.4, lat: 37.8, value: 42 })
|
|
126
|
+
<StreamGeoFrame ref={geoRef} projection="mercator" xAccessor="lon" yAccessor="lat"
|
|
127
|
+
runtimeMode="streaming" decay={{ type: "linear", minOpacity: 0.1 }}
|
|
128
|
+
zoomable zoomExtent={[1, 12]} onZoom={({ zoom }) => console.log(zoom)} />
|
|
129
|
+
```
|
|
54
130
|
|
|
55
131
|
## Realtime Charts (`semiotic/realtime`)
|
|
56
132
|
|
|
57
133
|
Push API: `chartRef.current.push({ time, value })`
|
|
58
134
|
|
|
59
|
-
**
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
**
|
|
64
|
-
**
|
|
135
|
+
**IMPORTANT**: All pushed data must include a time field (default: `"time"`). If your data uses a different field name, set `timeAccessor` explicitly. Without a valid time field, charts render blank with no error.
|
|
136
|
+
|
|
137
|
+
Sizing: all Realtime HOCs accept both `size={[600, 400]}` (tuple) and `width={600} height={400}`. Either works.
|
|
138
|
+
|
|
139
|
+
**RealtimeLineChart** — `size`|`width`+`height`, **`timeAccessor`** ("time"), **`valueAccessor`** ("value"), `windowSize` (200), `windowMode`, `stroke`, `strokeWidth`
|
|
140
|
+
**RealtimeHistogram** — **`binSize`** (required), **`timeAccessor`** ("time"), **`valueAccessor`** ("value"), `categoryAccessor`, `colors`. Time field is required even though this shows a distribution — it's used for windowing.
|
|
141
|
+
**RealtimeSwarmChart** — **`timeAccessor`** ("time"), **`valueAccessor`** ("value"), `categoryAccessor`, `radius`, `opacity`
|
|
142
|
+
**RealtimeWaterfallChart** — **`timeAccessor`** ("time"), **`valueAccessor`** ("value"), `positiveColor`, `negativeColor`
|
|
143
|
+
**RealtimeHeatmap** — **`timeAccessor`** ("time"), **`valueAccessor`** ("value"), `heatmapXBins`, `heatmapYBins`, `aggregation`. Both accessors must match your data fields or the chart renders blank.
|
|
144
|
+
**Streaming Sankey** — `StreamNetworkFrame` with `chartType="sankey"`, `showParticles` (boolean), `particleStyle` (`{ radius, opacity, speedMultiplier, maxPerEdge, colorBy }`), `tensionConfig`, `thresholds`. Push **individual edges**: `ref.current.push({ source: "A", target: "B", value: 42 })`. Use `ref.current.pushMany([...edges])` for batches.
|
|
65
145
|
|
|
66
146
|
Realtime encoding: `decay`, `pulse`, `transition`, `staleness` — compose freely on all streaming charts.
|
|
67
147
|
|
|
148
|
+
### Realtime data shape
|
|
149
|
+
```jsx
|
|
150
|
+
// Every pushed datum should have a time field
|
|
151
|
+
ref.current.push({ time: Date.now(), value: 42 }) // line, waterfall
|
|
152
|
+
ref.current.push({ time: Date.now(), value: 42, category: "A" }) // histogram, swarm
|
|
153
|
+
ref.current.push({ time: Date.now(), value: 42 }) // heatmap (time=x, value=y)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Push API on HOC charts
|
|
157
|
+
Many HOC charts support the push API via `forwardRef`. Omit the `data` prop and push data imperatively:
|
|
158
|
+
```jsx
|
|
159
|
+
const chartRef = useRef()
|
|
160
|
+
chartRef.current.push({ x: 1, y: 2 }) // single point
|
|
161
|
+
chartRef.current.pushMany([...points]) // batch
|
|
162
|
+
chartRef.current.clear() // reset
|
|
163
|
+
chartRef.current.getData() // read current data
|
|
164
|
+
<Scatterplot ref={chartRef} xAccessor="x" yAccessor="y" />
|
|
165
|
+
```
|
|
166
|
+
**IMPORTANT**: When using the push API, **omit** the `data`/`nodes`/`edges` prop entirely — do NOT pass `data={[]}`, which clears pushed data on every render. Streaming-specific props (`windowSize`, `decay`, `pulse`) go in `frameProps`.
|
|
167
|
+
|
|
168
|
+
Supported: all XY charts (LineChart, AreaChart, Scatterplot, etc.), all ordinal charts (BarChart, Histogram, etc.), network charts (ForceDirectedGraph, SankeyDiagram, ChordDiagram), and geo point charts (ProportionalSymbolMap, DistanceCartogram). **Not supported**: hierarchy charts (TreeDiagram, Treemap, CirclePack, OrbitDiagram) — their root-object data shape is incompatible with flat push. ChoroplethMap (area-based, not point-based), FlowMap (line-based), and ScatterplotMatrix also do not support push.
|
|
169
|
+
|
|
170
|
+
## Stream Frame Callbacks (advanced — prefer HOCs)
|
|
171
|
+
Stream Frame callbacks (`nodeStyle`, `edgeStyle`, `nodeSize` as function, `colorBy` as function, `nodeLabel` as function) receive **`RealtimeNode`/`RealtimeEdge`** wrappers, NOT your raw data. Access your original data via `.data`:
|
|
172
|
+
```jsx
|
|
173
|
+
// WRONG: nodeSize={(d) => d.weight} — d is RealtimeNode, d.weight is undefined
|
|
174
|
+
// RIGHT: nodeSize={(d) => d.data?.weight} — d.data is your original node object
|
|
175
|
+
// RIGHT: nodeSize="weight" — string accessor handles this automatically
|
|
176
|
+
// WRONG: nodeStyle={(d) => ({ fill: d.datum.color })} — .datum does not exist
|
|
177
|
+
// RIGHT: nodeStyle={(d) => ({ fill: d.data?.color })} — use .data
|
|
178
|
+
```
|
|
179
|
+
`customHoverBehavior` and `customClickBehavior` receive `{ type: "node"|"edge", data: <your raw object>, x, y } | null`.
|
|
180
|
+
`tooltipContent` receives `{ type: "node"|"edge", data: <your raw object> }`.
|
|
181
|
+
|
|
68
182
|
## Coordinated Views
|
|
69
183
|
|
|
70
|
-
**LinkedCharts** — wraps charts. Props: `selections` (resolution: "union"|"intersect"|"crossfilter")
|
|
184
|
+
**LinkedCharts** — wraps charts. Props: `selections` (resolution: "union"|"intersect"|"crossfilter"), `showLegend` (auto when CategoryColorProvider present), `legendPosition` ("top"|"bottom"), `legendInteraction` ("highlight"|"isolate"|"none"), `legendSelectionName` (selection name for legend-driven cross-highlighting), `legendField` (data field for legend selections)
|
|
71
185
|
**CategoryColorProvider** — stable category→color mapping. Props: `colors` (map) or `categories` + `colorScheme`
|
|
72
186
|
Chart props: `selection`, `linkedHover`, `linkedBrush`. Hooks: `useSelection`, `useLinkedHover`, `useBrushSelection`, `useFilteredData`
|
|
73
187
|
**ScatterplotMatrix** — `data`, `fields`, `colorBy`, `cellSize`, `hoverMode`, `brushMode`
|
|
74
188
|
|
|
189
|
+
## ChartContainer
|
|
190
|
+
|
|
191
|
+
**ChartContainer** — wrapper with title, subtitle, status indicator, toolbar actions. Props: `title`, `subtitle`, `height` (default **400** — set this to match your chart's height or you'll get extra whitespace), `width` (default "100%"), `status` ("live"|"stale"|"error"), `loading`, `error`, `errorBoundary`, `actions` (`{ export, fullscreen, copyConfig }`), `controls`, `style`, `className`
|
|
192
|
+
|
|
193
|
+
When using `ChartContainer` with a chart that has `size={[w, h]}`, always set `height={h}` on the container to avoid a mismatch.
|
|
194
|
+
|
|
75
195
|
## Layout & Composition
|
|
76
196
|
|
|
77
|
-
**ChartGrid** — CSS Grid layout. `columns` (number|"auto"), `minCellWidth` (300), `gap` (16)
|
|
197
|
+
**ChartGrid** — CSS Grid layout. `columns` (number|"auto"), `minCellWidth` (300), `gap` (16). Children with `emphasis="primary"` span two columns.
|
|
78
198
|
**ContextLayout** — primary + context panel. `context` (ReactNode), `position`, `contextSize` (250)
|
|
79
199
|
|
|
80
200
|
## Key Patterns
|
|
81
201
|
|
|
82
202
|
```jsx
|
|
83
|
-
//
|
|
203
|
+
// Force-directed graph with custom sizing and hover
|
|
204
|
+
<ForceDirectedGraph
|
|
205
|
+
nodes={[{ id: "A", group: "eng", weight: 10 }, { id: "B", group: "design", weight: 5 }]}
|
|
206
|
+
edges={[{ source: "A", target: "B" }]}
|
|
207
|
+
colorBy="group"
|
|
208
|
+
nodeSize="weight" // string accessor → reads node.weight, scales to nodeSizeRange
|
|
209
|
+
nodeSizeRange={[5, 25]}
|
|
210
|
+
showLabels
|
|
211
|
+
showLegend
|
|
212
|
+
tooltip={(d) => <div>{d.data.id}: {d.data.weight}</div>}
|
|
213
|
+
frameProps={{
|
|
214
|
+
customClickBehavior: (d) => { if (d?.type === "node") console.log(d.data) },
|
|
215
|
+
background: "#f5f5f5",
|
|
216
|
+
}}
|
|
217
|
+
/>
|
|
218
|
+
|
|
219
|
+
// Cross-highlighting dashboard with column spanning
|
|
220
|
+
// emphasis="primary" makes a chart span 2 columns in ChartGrid
|
|
84
221
|
<CategoryColorProvider categories={["North", "South", "East"]}>
|
|
85
222
|
<LinkedCharts>
|
|
86
223
|
<ChartGrid columns={2}>
|
|
87
|
-
<LineChart data={d} colorBy="region" linkedHover={{ name: "hl", fields: ["region"] }} selection={{ name: "hl" }} responsiveWidth />
|
|
224
|
+
<LineChart data={d} colorBy="region" linkedHover={{ name: "hl", fields: ["region"] }} selection={{ name: "hl" }} emphasis="primary" responsiveWidth />
|
|
88
225
|
<BarChart data={d} colorBy="region" linkedHover={{ name: "hl", fields: ["region"] }} selection={{ name: "hl" }} responsiveWidth />
|
|
226
|
+
<Scatterplot data={d} colorBy="region" linkedHover={{ name: "hl", fields: ["region"] }} selection={{ name: "hl" }} responsiveWidth />
|
|
89
227
|
</ChartGrid>
|
|
90
228
|
</LinkedCharts>
|
|
91
229
|
</CategoryColorProvider>
|
|
@@ -99,25 +237,100 @@ Chart props: `selection`, `linkedHover`, `linkedBrush`. Hooks: `useSelection`, `
|
|
|
99
237
|
<LineChart data={ml} xAccessor="time" yAccessor="value"
|
|
100
238
|
forecast={{ isTraining: "isTraining", isForecast: "isForecast", isAnomaly: "isAnomaly", upperBounds: "upper", lowerBounds: "lower" }} />
|
|
101
239
|
|
|
102
|
-
//
|
|
103
|
-
<
|
|
240
|
+
// Stacked area (flat array + areaBy, NOT lineBy)
|
|
241
|
+
<StackedAreaChart data={flatData} xAccessor="month" yAccessor="value"
|
|
242
|
+
areaBy="category" colorBy="category" />
|
|
104
243
|
|
|
105
|
-
//
|
|
244
|
+
// Percentile band (p5–p95) with main line (p50) — MUST layer two charts
|
|
245
|
+
// AreaChart with y0Accessor renders the band; showLine only draws the TOP edge (p95), not p50
|
|
246
|
+
// To show a separate main line, add a LineChart on top:
|
|
247
|
+
<>
|
|
248
|
+
<AreaChart data={d} xAccessor="x" yAccessor="p95" y0Accessor="p5"
|
|
249
|
+
showLine={false} areaOpacity={0.3} gradientFill />
|
|
250
|
+
<LineChart data={d} xAccessor="x" yAccessor="p50" lineWidth={2} />
|
|
251
|
+
</>
|
|
252
|
+
// Simple gradient area (no band):
|
|
253
|
+
<AreaChart data={d} xAccessor="x" yAccessor="y" gradientFill />
|
|
254
|
+
|
|
255
|
+
// Realtime — always include time field in pushed data
|
|
106
256
|
const ref = useRef()
|
|
107
257
|
ref.current.push({ time: Date.now(), value: 42 })
|
|
108
258
|
<RealtimeLineChart ref={ref} timeAccessor="time" valueAccessor="value" />
|
|
259
|
+
|
|
260
|
+
// Realtime histogram — time field required even for distribution charts
|
|
261
|
+
const histRef = useRef()
|
|
262
|
+
histRef.current.push({ time: Date.now(), value: Math.abs(delta) })
|
|
263
|
+
<RealtimeHistogram ref={histRef} timeAccessor="time" valueAccessor="value" binSize={100} />
|
|
264
|
+
|
|
265
|
+
// Streaming sankey with particles — push individual edges, NOT full snapshots
|
|
266
|
+
const sankeyRef = useRef()
|
|
267
|
+
sankeyRef.current.push({ source: "Web", target: "API", value: 1 }) // one edge at a time
|
|
268
|
+
sankeyRef.current.pushMany([ // or batch
|
|
269
|
+
{ source: "Web", target: "API", value: 3 },
|
|
270
|
+
{ source: "API", target: "DB", value: 2 },
|
|
271
|
+
])
|
|
272
|
+
<StreamNetworkFrame
|
|
273
|
+
ref={sankeyRef}
|
|
274
|
+
chartType="sankey"
|
|
275
|
+
showParticles={true}
|
|
276
|
+
particleStyle={{ radius: 2, colorBy: "source", speedMultiplier: 1.5 }}
|
|
277
|
+
width={600} height={400}
|
|
278
|
+
/>
|
|
279
|
+
|
|
280
|
+
// SSR — renderToStaticSVG takes frame type string, not component name
|
|
281
|
+
import { renderOrdinalToStaticSVG } from "semiotic/server"
|
|
282
|
+
const svg = renderOrdinalToStaticSVG({
|
|
283
|
+
data, categoryAccessor: "category", valueAccessor: "value", width: 600, height: 400
|
|
284
|
+
})
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Annotations
|
|
288
|
+
- `type: "widget"` — place any React element at data coordinates. Works on all frame types. XY/ordinal use data coordinates (`x`/`y` or field names). Network/orbit use `nodeId`. Default: info emoji. Renders as HTML overlay (not SVG) so popups/threads overflow freely.
|
|
289
|
+
```jsx
|
|
290
|
+
annotations={[{ type: "widget", month: 4, revenue: 32, dy: -4, content: <MyAlertButton /> }]}
|
|
291
|
+
// OrbitDiagram: annotations={[{ type: "widget", nodeId: "Pipeline", content: <Alert /> }]}
|
|
109
292
|
```
|
|
110
293
|
|
|
294
|
+
## Server-Side Rendering
|
|
295
|
+
- All HOC charts and Stream Frames render SVG automatically in server environments (no window/document)
|
|
296
|
+
- `renderToStaticSVG(frameType, props)` — standalone SVG string from `semiotic/server`. `frameType` is `"xy"` | `"ordinal"` | `"network"` | `"geo"` (NOT a component name like "BarChart")
|
|
297
|
+
- Type-specific shortcuts: `renderXYToStaticSVG(props)`, `renderOrdinalToStaticSVG(props)`, `renderNetworkToStaticSVG(props)`, `renderGeoToStaticSVG(props)`
|
|
298
|
+
- For a bar chart: `renderOrdinalToStaticSVG({ data, categoryAccessor: "cat", valueAccessor: "val", width: 600, height: 400 })`
|
|
299
|
+
- Works with Next.js App Router, Remix, Astro — same component on server and client
|
|
300
|
+
- **Geo SSR requires pre-resolved features**: `renderGeoToStaticSVG` is synchronous — pass GeoJSON features directly, not reference strings like `"world-110m"`. Call `await resolveReferenceGeography("world-110m")` first and pass the result as `areas`.
|
|
301
|
+
|
|
111
302
|
## AI Features
|
|
112
303
|
- `onObservation` — structured events (hover, click, brush, selection) on all HOCs
|
|
113
304
|
- `useChartObserver` — aggregates observations across LinkedCharts
|
|
114
305
|
- `toConfig`/`fromConfig`/`toURL`/`fromURL`/`copyConfig`/`configToJSX` — chart state serialization
|
|
115
306
|
- `DetailsPanel` — click-driven detail panel inside `ChartContainer`
|
|
116
|
-
- `validateProps(componentName, props)` — prop validation
|
|
307
|
+
- `validateProps(componentName, props)` — prop validation with Levenshtein typo suggestions
|
|
308
|
+
- `diagnoseConfig(componentName, props)` — anti-pattern detector (12 checks: empty data, bad dimensions, missing accessors, margin overflow, etc.)
|
|
117
309
|
- `ChartErrorBoundary` — error boundary
|
|
118
|
-
- `exportChart(
|
|
119
|
-
- `
|
|
120
|
-
|
|
310
|
+
- `exportChart(containerDiv, { format: "png"|"svg" })` — pass the **wrapper div** (not the SVG element); it finds canvas + SVG internally. Default: PNG, composites canvas + SVG layers
|
|
311
|
+
- `npx semiotic-ai --doctor` — validate component + props JSON from CLI (uses both validateProps and diagnoseConfig)
|
|
312
|
+
|
|
313
|
+
## Known Pitfalls
|
|
314
|
+
|
|
315
|
+
**Tooltip datum shape**: HOC tooltip functions receive your raw data object. When using `frameProps.tooltipContent` on Stream Frames, the datum may be wrapped — access your data via `d.data`. HOC `tooltip` functions don't need this.
|
|
316
|
+
|
|
317
|
+
**Legend positioning**: `legendPosition` controls where the legend renders. When set to "bottom", the chart automatically expands the bottom margin to ~80px to clear axis labels. For "top", margin expands to ~50px. If you need more space, override `margin` explicitly. For charts narrower than ~400px, prefer `legendPosition="bottom"` or `"top"` (bottom is more common) to avoid squeezing the chart area. Similarly, for short charts (~250px or less), a side legend may compress the chart too much — use top or bottom instead.
|
|
318
|
+
|
|
319
|
+
**Log scale and zero**: `xScaleType="log"` / `yScaleType="log"` clamp domain minimums to 1e-6 because log(0) is undefined. Data with zero or negative values will be clamped.
|
|
320
|
+
|
|
321
|
+
**Heatmap with string axes**: Heatmap supports string/categorical x and y values (e.g., weekday names, hour labels). The `colorScheme` prop accepts d3-scale-chromatic names: "blues", "reds", "greens", "viridis".
|
|
322
|
+
|
|
323
|
+
**barPadding is in pixels**: `barPadding` on ordinal charts is an absolute pixel value divided by the chart width to compute a band scale padding ratio. The defaults (40 for bar/stacked, 60 for grouped) work well at 600px width. For very small charts, you may need to reduce it.
|
|
324
|
+
|
|
325
|
+
**Horizontal bar charts need wider left margins**: When using `orientation="horizontal"` with long category labels, increase the left margin manually: `margin={{ left: 120 }}`. There is no auto-measurement of label width.
|
|
326
|
+
|
|
327
|
+
**LinkedCharts suppresses child legends**: When a `CategoryColorProvider` wraps `LinkedCharts`, individual chart legends are suppressed in favor of a unified legend. To force a child chart to show its own legend, set `showLegend={true}` explicitly.
|
|
328
|
+
|
|
329
|
+
**Geo bundle isolation**: `semiotic/geo` is a separate entry point. Do NOT import geo components from `semiotic` — use `import { ChoroplethMap } from "semiotic/geo"` to avoid pulling d3-geo (~30KB) into non-geo bundles.
|
|
330
|
+
|
|
331
|
+
**Push API: omit data, don't pass empty array**: When using `ref.current.push()` on HOCs, **omit** the `data`/`nodes`/`edges` prop entirely. Passing `data={[]}` clears pushed data on every render because the HOC forwards it to the Stream Frame's `setBoundedData([])`. Similarly, `data={undefined}` is fine (prop not present), but `data={null}` is treated the same as omitted.
|
|
332
|
+
|
|
333
|
+
**`diagnoseConfig` catches common mistakes**: Run `diagnoseConfig("BarChart", props)` to check for empty data, bad dimensions, missing accessors, margin overflow, invisible bar padding, and more. Use `npx semiotic-ai --doctor` from CLI.
|
|
121
334
|
|
|
122
335
|
## Differentiators
|
|
123
|
-
Network viz, streaming canvas, realtime encoding, coordinated views, statistical summaries, AI hooks, chart serialization, global theming, keyboard navigation
|
|
336
|
+
Network viz, geographic viz (choropleth, flow maps, distance cartograms), streaming canvas, realtime encoding, coordinated views, statistical summaries, AI hooks, chart serialization, global theming, keyboard navigation, interactive legends (highlight/isolate), direct labeling, gap handling, empty/loading states, landmark tick labels, LinkedCharts unified legend
|
package/README.md
CHANGED
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/semiotic)
|
|
5
5
|
[](https://www.typescriptlang.org/)
|
|
6
6
|
|
|
7
|
-
A React data visualization library for
|
|
7
|
+
A React data visualization library designed for AI-assisted development.
|
|
8
8
|
|
|
9
|
-
Simple charts in 5 lines.
|
|
10
|
-
|
|
9
|
+
Simple charts in 5 lines. Network graphs, streaming data, and coordinated
|
|
10
|
+
dashboards when you need them. Structured schemas and an MCP server so
|
|
11
|
+
AI coding assistants generate correct chart code on the first try.
|
|
11
12
|
|
|
12
13
|
```jsx
|
|
13
14
|
import { LineChart } from "semiotic"
|
|
@@ -21,31 +22,52 @@ import { LineChart } from "semiotic"
|
|
|
21
22
|
|
|
22
23
|
## Why Semiotic
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
Semiotic is a data visualization library for React that combines broad chart
|
|
26
|
+
coverage with first-class AI tooling. It handles the chart types that most
|
|
27
|
+
libraries skip — network graphs, streaming data, statistical distributions,
|
|
28
|
+
coordinated views — and ships with machine-readable schemas so LLMs can
|
|
29
|
+
generate correct code without examples.
|
|
27
30
|
|
|
28
|
-
###
|
|
31
|
+
### Built for AI-assisted development
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
graphs, Sankey diagrams, chord diagrams, tree layouts, treemaps, and circle
|
|
33
|
-
packing as React components with the same prop API as LineChart.
|
|
33
|
+
Semiotic ships with everything an AI coding assistant needs to generate
|
|
34
|
+
correct visualizations without trial and error:
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
- **`semiotic/ai`** — a single import with all 37 chart components, optimized for LLM code generation
|
|
37
|
+
- **`ai/schema.json`** — machine-readable prop schemas for every component
|
|
38
|
+
- **`npx semiotic-mcp`** — an MCP server for tool-based chart rendering in any MCP client
|
|
39
|
+
- **`npx semiotic-ai --doctor`** — validate component + props JSON from the command line with typo suggestions and anti-pattern detection
|
|
40
|
+
- **`diagnoseConfig(component, props)`** — programmatic anti-pattern detector with 12 checks and actionable fixes
|
|
41
|
+
- **`CLAUDE.md`** — instruction files auto-synced for Claude, Cursor, Copilot, Windsurf, and Cline
|
|
42
|
+
- **`llms.txt`** — machine-readable documentation following the emerging standard
|
|
39
43
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
Every chart includes a built-in error boundary, dev-mode validation
|
|
45
|
+
warnings with typo suggestions, and accessibility features (canvas
|
|
46
|
+
`aria-label`, keyboard-navigable legends, `aria-live` tooltips, SVG
|
|
47
|
+
`<title>`/`<desc>`) so AI-generated code fails gracefully with
|
|
48
|
+
actionable diagnostics instead of a blank screen.
|
|
44
49
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
50
|
+
### Beyond standard charts
|
|
51
|
+
|
|
52
|
+
**Network visualization.** Force-directed graphs, Sankey diagrams, chord
|
|
53
|
+
diagrams, tree layouts, treemaps, circle packing, and orbit diagrams — all
|
|
54
|
+
as React components with the same prop API as LineChart.
|
|
55
|
+
|
|
56
|
+
**Streaming data.** Realtime charts render on canvas at 60fps with a
|
|
57
|
+
ref-based push API. Built-in decay, pulse, and staleness encoding for
|
|
58
|
+
monitoring dashboards.
|
|
59
|
+
|
|
60
|
+
**Coordinated views.** `LinkedCharts` provides hover cross-highlighting,
|
|
61
|
+
brush cross-filtering, and selection synchronization across any combination
|
|
62
|
+
of chart types — zero wiring.
|
|
63
|
+
|
|
64
|
+
**Geographic visualization.** Choropleth maps, proportional symbol maps, flow
|
|
65
|
+
maps with animated particles, and distance cartograms — all canvas-rendered
|
|
66
|
+
with d3-geo projections, zoom/pan, tile basemaps, and drag-rotate globe spinning.
|
|
67
|
+
|
|
68
|
+
**Statistical summaries.** Box plots, violin plots, swarm plots, histograms,
|
|
69
|
+
LOESS smoothing, forecast with confidence envelopes, and anomaly detection.
|
|
70
|
+
Marginal distribution graphics on scatterplot axes with a single prop.
|
|
49
71
|
|
|
50
72
|
### Start simple, go deep
|
|
51
73
|
|
|
@@ -57,6 +79,13 @@ violin, ridgeline, boxplot) to scatterplot margins with a single prop.
|
|
|
57
79
|
Every Chart component accepts a `frameProps` prop to access the underlying
|
|
58
80
|
Frame API without leaving the simpler interface.
|
|
59
81
|
|
|
82
|
+
### Serialization and interop
|
|
83
|
+
|
|
84
|
+
Charts serialize to JSON and back: `toConfig`, `fromConfig`, `toURL`,
|
|
85
|
+
`copyConfig`, `configToJSX`. Have Vega-Lite specs? `fromVegaLite(spec)`
|
|
86
|
+
translates them to Semiotic configs — works with `configToJSX()` for
|
|
87
|
+
full round-trip from notebooks and AI-generated specs.
|
|
88
|
+
|
|
60
89
|
### When to use something else
|
|
61
90
|
|
|
62
91
|
Need a standard bar or line chart for a dashboard you'll never need to
|
|
@@ -69,40 +98,10 @@ Semiotic is for projects that outgrow those libraries — when you need
|
|
|
69
98
|
network graphs alongside time series, streaming data alongside static
|
|
70
99
|
snapshots, or coordinated views across chart types.
|
|
71
100
|
|
|
72
|
-
### Bundle size comparison
|
|
73
|
-
|
|
74
|
-
| Library | Packed | Unpacked | What you get |
|
|
75
|
-
|---|---|---|---|
|
|
76
|
-
| Victory | 393 KB | 2.3 MB | Charts only |
|
|
77
|
-
| Lightweight Charts | 586 KB | 3.0 MB | Financial charts only |
|
|
78
|
-
| **Semiotic** | **668 KB** | **2.5 MB** | **Charts + networks + streaming + coordination** |
|
|
79
|
-
| Recharts | 1.4 MB | 6.4 MB | Charts only |
|
|
80
|
-
| Chart.js | 1.6 MB | 6.2 MB | Charts only, no React |
|
|
81
|
-
| ApexCharts | 1.8 MB | 8.4 MB | Charts only |
|
|
82
|
-
| ECharts | 11.4 MB | 57.6 MB | Everything, no React |
|
|
83
|
-
|
|
84
|
-
**AI-ready.** Semiotic ships with structured schemas (`ai/schema.json`), an
|
|
85
|
-
`import from "semiotic/ai"` entry point, and an MCP server — all designed for
|
|
86
|
-
LLM code generation. AI coding assistants can generate correct Semiotic code on
|
|
87
|
-
the first try. Run `npx semiotic-ai --help` for CLI options, `npx semiotic-ai --doctor`
|
|
88
|
-
to validate props from the command line, or add `semiotic-mcp` to your MCP client
|
|
89
|
-
config for tool-based chart rendering. Every HOC chart includes a built-in error
|
|
90
|
-
boundary and dev-mode validation warnings with actionable fix suggestions.
|
|
91
|
-
|
|
92
|
-
**Vega-Lite compatible.** Have existing Vega-Lite specs? `fromVegaLite(spec)`
|
|
93
|
-
translates them to Semiotic chart configs — instant onboarding from notebooks,
|
|
94
|
-
dashboards, or AI-generated specs. Composes with `configToJSX()`,
|
|
95
|
-
`copyConfig()`, and `toURL()` for full round-trip interop.
|
|
96
|
-
|
|
97
|
-
**Streaming system models.** Turn a streaming Sankey into a live system monitor:
|
|
98
|
-
click-to-inspect `DetailsPanel`, particle speed proportional to edge throughput,
|
|
99
|
-
threshold alerting with animated glow, and automatic topology diffing that
|
|
100
|
-
highlights new services as they appear. Visualization as product navigation.
|
|
101
|
-
|
|
102
101
|
## Install
|
|
103
102
|
|
|
104
103
|
```bash
|
|
105
|
-
npm install semiotic
|
|
104
|
+
npm install semiotic
|
|
106
105
|
```
|
|
107
106
|
|
|
108
107
|
Requires React 18.1+ or React 19.
|
|
@@ -166,6 +165,30 @@ import { ForceDirectedGraph, SankeyDiagram } from "semiotic"
|
|
|
166
165
|
/>
|
|
167
166
|
```
|
|
168
167
|
|
|
168
|
+
### Geographic Visualization
|
|
169
|
+
|
|
170
|
+
Choropleth maps, flow maps, and distance cartograms with canvas rendering,
|
|
171
|
+
zoom/pan, tile basemaps, and animated particles:
|
|
172
|
+
|
|
173
|
+
```jsx
|
|
174
|
+
import { ChoroplethMap, FlowMap, DistanceCartogram } from "semiotic/geo"
|
|
175
|
+
|
|
176
|
+
<ChoroplethMap
|
|
177
|
+
areas={geoJsonFeatures} valueAccessor="gdp"
|
|
178
|
+
colorScheme="viridis" projection="equalEarth" zoomable tooltip
|
|
179
|
+
/>
|
|
180
|
+
|
|
181
|
+
<FlowMap
|
|
182
|
+
nodes={airports} flows={routes} valueAccessor="passengers"
|
|
183
|
+
showParticles particleStyle={{ color: "source", speedMultiplier: 1.5 }}
|
|
184
|
+
/>
|
|
185
|
+
|
|
186
|
+
<DistanceCartogram
|
|
187
|
+
points={cities} center="rome" costAccessor="travelDays"
|
|
188
|
+
showRings costLabel="days" lines={routes}
|
|
189
|
+
/>
|
|
190
|
+
```
|
|
191
|
+
|
|
169
192
|
### Streaming System Monitor
|
|
170
193
|
|
|
171
194
|
Live service topology with threshold alerting and click-to-inspect:
|
|
@@ -215,13 +238,14 @@ import { LineChart, BarChart } from "semiotic"
|
|
|
215
238
|
|
|
216
239
|
| Category | Components |
|
|
217
240
|
|---|---|
|
|
218
|
-
| **XY** | `LineChart` `AreaChart` `StackedAreaChart` `Scatterplot` `BubbleChart` `Heatmap` |
|
|
219
|
-
| **Categorical** | `BarChart` `StackedBarChart` `GroupedBarChart` `SwarmPlot` `BoxPlot` `Histogram` `ViolinPlot` `DotPlot` `PieChart` `DonutChart` |
|
|
220
|
-
| **Network** | `ForceDirectedGraph` `ChordDiagram` `SankeyDiagram` `TreeDiagram` `Treemap` `CirclePack` |
|
|
241
|
+
| **XY** | `LineChart` `AreaChart` `StackedAreaChart` `Scatterplot` `ConnectedScatterplot` `BubbleChart` `Heatmap` `QuadrantChart` `MinimapChart` |
|
|
242
|
+
| **Categorical** | `BarChart` `StackedBarChart` `GroupedBarChart` `SwarmPlot` `BoxPlot` `Histogram` `ViolinPlot` `RidgelinePlot` `DotPlot` `PieChart` `DonutChart` |
|
|
243
|
+
| **Network** | `ForceDirectedGraph` `ChordDiagram` `SankeyDiagram` `TreeDiagram` `Treemap` `CirclePack` `OrbitDiagram` |
|
|
244
|
+
| **Geo** | `ChoroplethMap` `ProportionalSymbolMap` `FlowMap` `DistanceCartogram` |
|
|
221
245
|
| **Realtime** | `RealtimeLineChart` `RealtimeHistogram` `RealtimeSwarmChart` `RealtimeWaterfallChart` `RealtimeHeatmap` |
|
|
222
246
|
| **Coordination** | `LinkedCharts` `ScatterplotMatrix` |
|
|
223
247
|
| **Layout** | `ChartGrid` `ContextLayout` `CategoryColorProvider` |
|
|
224
|
-
| **Frames** | `StreamXYFrame` `StreamOrdinalFrame` `StreamNetworkFrame` |
|
|
248
|
+
| **Frames** | `StreamXYFrame` `StreamOrdinalFrame` `StreamNetworkFrame` `StreamGeoFrame` |
|
|
225
249
|
|
|
226
250
|
### Vega-Lite Translation
|
|
227
251
|
|
|
@@ -257,10 +281,11 @@ for color, size, aggregation, and binning.
|
|
|
257
281
|
Import only what you need:
|
|
258
282
|
|
|
259
283
|
```jsx
|
|
260
|
-
import { LineChart } from "semiotic/xy" //
|
|
261
|
-
import { BarChart } from "semiotic/ordinal" //
|
|
262
|
-
import { ForceDirectedGraph } from "semiotic/network" //
|
|
263
|
-
import {
|
|
284
|
+
import { LineChart } from "semiotic/xy" // ~156 KB
|
|
285
|
+
import { BarChart } from "semiotic/ordinal" // ~124 KB
|
|
286
|
+
import { ForceDirectedGraph } from "semiotic/network" // ~123 KB
|
|
287
|
+
import { ChoroplethMap } from "semiotic/geo" // ~102 KB (+ d3-geo peer)
|
|
288
|
+
import { LineChart } from "semiotic/ai" // ~397 KB (all HOCs)
|
|
264
289
|
```
|
|
265
290
|
|
|
266
291
|
Granular entry points export only v3 Stream Frames and HOC charts — no legacy
|
|
@@ -283,7 +308,19 @@ interface Sale { month: number; revenue: number }
|
|
|
283
308
|
|
|
284
309
|
## Server-Side Rendering
|
|
285
310
|
|
|
286
|
-
|
|
311
|
+
All chart components render SVG automatically in server environments — no
|
|
312
|
+
special imports or configuration needed:
|
|
313
|
+
|
|
314
|
+
```jsx
|
|
315
|
+
// Works in Next.js App Router, Remix, Astro — same component, same props
|
|
316
|
+
import { LineChart } from "semiotic"
|
|
317
|
+
|
|
318
|
+
// Server: renders <svg> with path/circle/rect elements
|
|
319
|
+
// Client: renders <canvas> with SVG overlay for axes
|
|
320
|
+
<LineChart data={data} xAccessor="date" yAccessor="value" />
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
For standalone SVG generation (email, OG images, PDF), use the server entry point:
|
|
287
324
|
|
|
288
325
|
```js
|
|
289
326
|
import { renderToStaticSVG } from "semiotic/server"
|
|
@@ -296,14 +333,12 @@ const svg = renderToStaticSVG("xy", {
|
|
|
296
333
|
})
|
|
297
334
|
```
|
|
298
335
|
|
|
299
|
-
Works with Next.js App Router, Remix, and Astro via `"use client"` directives.
|
|
300
|
-
|
|
301
336
|
## Documentation
|
|
302
337
|
|
|
303
338
|
[Interactive docs and examples](https://semiotic.nteract.io)
|
|
304
339
|
|
|
305
340
|
- [Getting Started](https://semiotic.nteract.io/getting-started)
|
|
306
|
-
- [Charts](https://semiotic.nteract.io/charts) — all
|
|
341
|
+
- [Charts](https://semiotic.nteract.io/charts) — all 37 chart types with live examples
|
|
307
342
|
- [Frames](https://semiotic.nteract.io/frames) — full Frame API reference
|
|
308
343
|
- [Features](https://semiotic.nteract.io/features) — axes, annotations, tooltips, styling, Vega-Lite translator
|
|
309
344
|
- [Cookbook](https://semiotic.nteract.io/cookbook) — advanced patterns and recipes
|