semiotic 3.2.2 → 3.3.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 +118 -222
- package/README.md +37 -17
- package/ai/schema.json +64 -1
- package/ai/system-prompt.md +3 -3
- package/dist/components/Tooltip/Tooltip.d.ts +6 -1
- package/dist/components/charts/index.d.ts +2 -0
- package/dist/components/charts/ordinal/BarChart.d.ts +2 -0
- package/dist/components/charts/ordinal/GaugeChart.d.ts +55 -0
- package/dist/components/charts/ordinal/GroupedBarChart.d.ts +1 -0
- package/dist/components/charts/ordinal/StackedBarChart.d.ts +1 -0
- package/dist/components/charts/ordinal/SwimlaneChart.d.ts +4 -0
- package/dist/components/charts/realtime/RealtimeHeatmap.d.ts +2 -0
- package/dist/components/charts/realtime/RealtimeHistogram.d.ts +2 -0
- package/dist/components/charts/realtime/RealtimeLineChart.d.ts +2 -0
- package/dist/components/charts/realtime/RealtimeSwarmChart.d.ts +2 -0
- package/dist/components/charts/realtime/RealtimeWaterfallChart.d.ts +2 -0
- package/dist/components/charts/shared/hooks.d.ts +5 -1
- package/dist/components/charts/shared/types.d.ts +14 -5
- package/dist/components/charts/shared/useChartSetup.d.ts +2 -0
- package/dist/components/charts/xy/AreaChart.d.ts +17 -2
- package/dist/components/charts/xy/LineChart.d.ts +14 -3
- package/dist/components/realtime/RingBuffer.d.ts +11 -0
- package/dist/components/realtime/types.d.ts +4 -0
- package/dist/components/semiotic-ordinal.d.ts +2 -0
- package/dist/components/semiotic-server.d.ts +6 -1
- package/dist/components/semiotic-utils.d.ts +2 -1
- package/dist/components/semiotic.d.ts +3 -3
- package/dist/components/server/animatedGif.d.ts +78 -0
- package/dist/components/server/renderToStaticSVG.d.ts +85 -5
- package/dist/components/server/staticAnnotations.d.ts +40 -0
- package/dist/components/server/staticLegend.d.ts +39 -0
- package/dist/components/server/svgHatchPattern.d.ts +26 -0
- package/dist/components/server/themeResolver.d.ts +35 -0
- package/dist/components/store/LinkedCrosshairStore.d.ts +6 -2
- package/dist/components/stream/CanvasHitTester.d.ts +13 -0
- package/dist/components/stream/GeoPipelineStore.d.ts +6 -1
- package/dist/components/stream/NetworkPipelineStore.d.ts +20 -0
- package/dist/components/stream/OrdinalPipelineStore.d.ts +11 -0
- package/dist/components/stream/OrdinalSVGOverlay.d.ts +6 -1
- package/dist/components/stream/PipelineStore.d.ts +28 -1
- package/dist/components/stream/SVGOverlay.d.ts +11 -5
- package/dist/components/stream/accessorUtils.d.ts +3 -3
- package/dist/components/stream/geoTypes.d.ts +2 -0
- package/dist/components/stream/hitTestUtils.d.ts +15 -0
- package/dist/components/stream/networkTypes.d.ts +33 -0
- package/dist/components/stream/ordinalTypes.d.ts +37 -4
- package/dist/components/stream/renderers/resolveCSSColor.d.ts +17 -0
- package/dist/components/stream/types.d.ts +58 -5
- package/dist/components/stream/xySceneBuilders/mixedScene.d.ts +12 -0
- package/dist/components/stream/xySceneBuilders/types.d.ts +15 -0
- package/dist/geo.min.js +1 -1
- package/dist/geo.module.min.js +1 -1
- 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.min.js +1 -1
- package/dist/semiotic-ai.module.min.js +1 -1
- package/dist/semiotic-ordinal.d.ts +2 -0
- package/dist/semiotic-server.d.ts +6 -1
- package/dist/semiotic-utils.d.ts +2 -1
- package/dist/semiotic-utils.min.js +1 -1
- package/dist/semiotic-utils.module.min.js +1 -1
- package/dist/semiotic.d.ts +3 -3
- 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/xy.min.js +1 -1
- package/dist/xy.module.min.js +1 -1
- package/package.json +21 -7
package/CLAUDE.md
CHANGED
|
@@ -2,295 +2,191 @@
|
|
|
2
2
|
|
|
3
3
|
## Quick Start
|
|
4
4
|
- Install: `npm install semiotic`
|
|
5
|
-
-
|
|
5
|
+
- **Use sub-path imports** — `semiotic/xy` (143KB gz), `semiotic/ordinal` (109KB), `semiotic/network` (98KB), `semiotic/geo` (93KB), `semiotic/realtime` (145KB), `semiotic/server` (100KB), `semiotic/utils` (31KB), `semiotic/themes` (5KB), `semiotic/data` (5KB). Full `semiotic` is 278KB gz.
|
|
6
6
|
- CLI: `npx semiotic-ai [--schema|--compact|--examples|--doctor]`
|
|
7
7
|
- MCP: `npx semiotic-mcp`
|
|
8
|
-
- Every HOC has a built-in error boundary and dev-mode validation warnings
|
|
9
8
|
|
|
10
9
|
## Architecture
|
|
11
10
|
- **HOC Charts**: Simple props, sensible defaults. **Stream Frames**: Full control.
|
|
12
|
-
- **Always use HOC charts** unless you need control they don't expose. Stream Frames
|
|
13
|
-
- Every HOC accepts `frameProps`
|
|
11
|
+
- **Always use HOC charts** unless you need control they don't expose. Stream Frames pass `RealtimeNode`/`RealtimeEdge` wrappers in callbacks, not your data.
|
|
12
|
+
- Every HOC accepts `frameProps` for pass-through. TypeScript `strict: true`. Every HOC has error boundary + dev-mode validation.
|
|
14
13
|
|
|
15
14
|
## Common Props (all HOCs)
|
|
16
|
-
`title`, `description` (
|
|
15
|
+
`title`, `description` (aria-label), `summary` (sr-only), `width` (600), `height` (400), `responsiveWidth`, `responsiveHeight`, `margin`, `className`, `color` (uniform fill), `enableHover` (true), `tooltip` (boolean | "multi" | function | config object), `showLegend`, `showGrid` (false), `frameProps`, `onObservation`, `onClick`, `chartId`, `loading` (false), `emptyContent`, `legendInteraction` ("none"|"highlight"|"isolate"), `legendPosition` ("right"|"left"|"top"|"bottom"), `emphasis` ("primary"|"secondary"), `annotations` (array), `accessibleTable` (true), `hoverHighlight` (boolean — dims non-hovered series, requires `colorBy`), `hoverRadius` (30)
|
|
17
16
|
|
|
18
|
-
`onClick` receives `(datum, { x, y })`
|
|
19
|
-
|
|
20
|
-
`onObservation` receives `{ type: "hover"|"hover-end"|"click"|"brush"|"selection", datum?, x?, y?, timestamp, chartType, chartId }`. The `datum` is your original data object.
|
|
17
|
+
`onClick` receives `(datum, { x, y })`. `onObservation` receives `{ type, datum?, x?, y?, timestamp, chartType, chartId }`.
|
|
21
18
|
|
|
22
19
|
## XY Charts (`semiotic/xy`)
|
|
23
20
|
|
|
24
|
-
**LineChart** — `data`, `xAccessor` ("x"), `yAccessor` ("y"), `lineBy`, `lineDataAccessor
|
|
25
|
-
**AreaChart** — LineChart props + `areaBy`, `y0Accessor
|
|
26
|
-
**StackedAreaChart** — flat array + `areaBy` (required), `colorBy`, `normalize`.
|
|
21
|
+
**LineChart** — `data`, `xAccessor` ("x"), `yAccessor` ("y"), `lineBy`, `lineDataAccessor`, `colorBy`, `colorScheme`, `curve`, `lineWidth` (2), `showPoints`, `pointRadius` (3), `fillArea` (boolean|string[]), `areaOpacity` (0.3), `lineGradient`, `anomaly`, `forecast`, `directLabel`, `gapStrategy`, `xScaleType`/`yScaleType` ("linear"|"log"|"time")
|
|
22
|
+
**AreaChart** — LineChart props + `areaBy`, `y0Accessor`, `gradientFill`, `areaOpacity` (0.7), `showLine` (true)
|
|
23
|
+
**StackedAreaChart** — flat array + `areaBy` (required), `colorBy`, `normalize`. No `lineBy`/`lineDataAccessor`.
|
|
27
24
|
**Scatterplot** — `data`, `xAccessor`, `yAccessor`, `colorBy`, `sizeBy`, `sizeRange`, `pointRadius` (5), `pointOpacity` (0.8), `marginalGraphics`
|
|
28
|
-
**BubbleChart** — Scatterplot + `sizeBy` (required), `sizeRange` ([5,40])
|
|
29
|
-
**ConnectedScatterplot** —
|
|
30
|
-
**QuadrantChart** — Scatterplot + `quadrants` (required
|
|
31
|
-
**MultiAxisLineChart** — Dual Y-axis. `
|
|
32
|
-
**Heatmap** — `data`, `xAccessor`, `yAccessor`, `valueAccessor`, `colorScheme
|
|
25
|
+
**BubbleChart** — Scatterplot + `sizeBy` (required), `sizeRange` ([5,40])
|
|
26
|
+
**ConnectedScatterplot** — + `orderAccessor`
|
|
27
|
+
**QuadrantChart** — Scatterplot + `quadrants` (required), `xCenter`, `yCenter`
|
|
28
|
+
**MultiAxisLineChart** — Dual Y-axis. `series` (required: `[{ yAccessor, label?, color?, format?, extent? }]`). Falls back to multi-line if not 2 series.
|
|
29
|
+
**Heatmap** — `data`, `xAccessor`, `yAccessor`, `valueAccessor`, `colorScheme`, `showValues`, `cellBorderColor`
|
|
33
30
|
|
|
34
31
|
## Ordinal Charts (`semiotic/ordinal`)
|
|
35
32
|
|
|
36
33
|
**BarChart** — `data`, `categoryAccessor`, `valueAccessor`, `orientation`, `colorBy`, `sort`, `barPadding` (40)
|
|
37
|
-
**StackedBarChart** — + `stackBy` (required), `normalize
|
|
34
|
+
**StackedBarChart** — + `stackBy` (required), `normalize`
|
|
38
35
|
**GroupedBarChart** — + `groupBy` (required), `barPadding` (60)
|
|
39
|
-
**SwarmPlot** — `
|
|
36
|
+
**SwarmPlot** — `colorBy`, `sizeBy`, `pointRadius`, `pointOpacity`
|
|
40
37
|
**BoxPlot** — + `showOutliers`, `outlierRadius`
|
|
41
|
-
**Histogram** — + `bins` (25), `relative`. Always horizontal.
|
|
38
|
+
**Histogram** — + `bins` (25), `relative`. Always horizontal.
|
|
42
39
|
**ViolinPlot** — + `bins`, `curve`, `showIQR`
|
|
43
|
-
**RidgelinePlot** — + `bins`, `amplitude` (1.5
|
|
40
|
+
**RidgelinePlot** — + `bins`, `amplitude` (1.5)
|
|
44
41
|
**DotPlot** — + `sort` (true), `dotRadius`, `showGrid` default true
|
|
45
|
-
**PieChart** — `
|
|
46
|
-
**DonutChart** — PieChart + `innerRadius` (60), `centerContent`
|
|
47
|
-
**FunnelChart** — `
|
|
48
|
-
**SwimlaneChart** — `
|
|
49
|
-
|
|
50
|
-
**
|
|
42
|
+
**PieChart** — `categoryAccessor`, `valueAccessor`, `colorBy`, `startAngle`
|
|
43
|
+
**DonutChart** — PieChart + `innerRadius` (60), `centerContent`
|
|
44
|
+
**FunnelChart** — `stepAccessor`, `valueAccessor`, `categoryAccessor` (optional), `connectorOpacity`, `orientation`
|
|
45
|
+
**SwimlaneChart** — `categoryAccessor`, `subcategoryAccessor` (required), `valueAccessor`, `colorBy` (defaults to subcategoryAccessor), `orientation`
|
|
46
|
+
**LikertChart** — `categoryAccessor`, `valueAccessor`|`levelAccessor`+`countAccessor`, `levels` (required), `orientation`, `colorScheme`
|
|
47
|
+
**GaugeChart** — `value` (required), `min`, `max`, `thresholds`, `arcWidth`, `sweep`, `fillZones`, `showNeedle`, `centerContent`
|
|
51
48
|
|
|
52
|
-
All ordinal
|
|
49
|
+
All ordinal: `colorBy`, `colorScheme`, `categoryFormat` (string|ReactNode), `showCategoryTicks` (true).
|
|
53
50
|
|
|
54
51
|
## Network Charts (`semiotic/network`)
|
|
55
52
|
|
|
56
|
-
**ForceDirectedGraph** — `nodes`, `edges`, `nodeIDAccessor`, `sourceAccessor`, `targetAccessor`, `colorBy`, `
|
|
57
|
-
**SankeyDiagram** — `edges`, `nodes`, `valueAccessor`, `nodeIdAccessor
|
|
58
|
-
**ChordDiagram** — `edges`, `nodes`, `valueAccessor`, `edgeColorBy`, `padAngle`, `
|
|
59
|
-
**TreeDiagram** — `data` (root), `layout`, `orientation`, `childrenAccessor`, `colorBy`, `colorByDepth
|
|
60
|
-
**Treemap** — `data` (root), `childrenAccessor`, `valueAccessor`, `colorBy`, `colorByDepth`, `showLabels
|
|
61
|
-
**CirclePack** — `data` (root), `childrenAccessor`, `valueAccessor`, `colorBy`, `colorByDepth
|
|
62
|
-
**OrbitDiagram** —
|
|
53
|
+
**ForceDirectedGraph** — `nodes`, `edges`, `nodeIDAccessor`, `sourceAccessor`, `targetAccessor`, `colorBy`, `nodeSize`, `nodeSizeRange`, `edgeWidth`, `iterations` (300), `forceStrength` (0.1), `showLabels`, `nodeLabel`
|
|
54
|
+
**SankeyDiagram** — `edges`, `nodes`, `valueAccessor`, `nodeIdAccessor`, `colorBy`, `edgeColorBy`, `orientation`, `nodeAlign`, `nodeWidth`, `nodePaddingRatio`, `showLabels`
|
|
55
|
+
**ChordDiagram** — `edges`, `nodes`, `valueAccessor`, `edgeColorBy`, `padAngle`, `showLabels`
|
|
56
|
+
**TreeDiagram** — `data` (root), `layout`, `orientation`, `childrenAccessor`, `colorBy`, `colorByDepth`
|
|
57
|
+
**Treemap** — `data` (root), `childrenAccessor`, `valueAccessor`, `colorBy`, `colorByDepth`, `showLabels`
|
|
58
|
+
**CirclePack** — `data` (root), `childrenAccessor`, `valueAccessor`, `colorBy`, `colorByDepth`
|
|
59
|
+
**OrbitDiagram** — `data` (root), `childrenAccessor`, `orbitMode`, `speed`, `animated` (true), `colorBy`
|
|
63
60
|
|
|
64
61
|
## Geo Charts (`semiotic/geo`)
|
|
65
62
|
|
|
66
63
|
Import from `semiotic/geo` — NOT `semiotic` — to avoid pulling d3-geo into non-geo bundles.
|
|
67
64
|
|
|
68
|
-
**ChoroplethMap** — `areas` (GeoJSON Feature[] or "world-110m"), `valueAccessor`, `colorScheme`, `
|
|
69
|
-
**ProportionalSymbolMap** — `points`, `xAccessor` ("lon"), `yAccessor` ("lat"), `sizeBy`, `sizeRange
|
|
70
|
-
**FlowMap** — `flows`, `nodes`, `
|
|
71
|
-
**DistanceCartogram** — `points`, `center
|
|
72
|
-
|
|
73
|
-
All geo HOCs: `fitPadding` (0–1), `zoomable` (defaults true with tileURL), `zoomExtent` ([1,8]), `onZoom`, `dragRotate` (true for orthographic), `graticule`, `tileURL`, `tileAttribution`, `tileCacheSize`, `selection`, `linkedHover`, `onObservation`
|
|
74
|
-
|
|
75
|
-
**Tiles**: `tileURL` accepts string template (`{z}/{x}/{y}`) or function. Mercator only. OSM tiles are dev-only — use commercial provider with env var key in production.
|
|
76
|
-
**Zoom**: Imperative: `ref.current.getZoom()`, `ref.current.resetZoom()`.
|
|
77
|
-
**Reference geography**: `resolveReferenceGeography("world-110m"|"world-50m"|"land-110m"|"land-50m")` returns GeoJSON features.
|
|
78
|
-
**mergeData(features, data, { featureKey, dataKey })** — join data into GeoJSON by key. World-atlas uses ISO numeric codes as `id`.
|
|
79
|
-
|
|
80
|
-
```jsx
|
|
81
|
-
import { ChoroplethMap, resolveReferenceGeography, mergeData } from "semiotic/geo"
|
|
82
|
-
const world = await resolveReferenceGeography("world-110m")
|
|
83
|
-
const areas = mergeData(world, gdpData, { featureKey: "id", dataKey: "id" })
|
|
84
|
-
<ChoroplethMap areas={areas} valueAccessor="gdpPerCapita" colorScheme="viridis"
|
|
85
|
-
projection="equalEarth" zoomable tooltip />
|
|
86
|
-
```
|
|
65
|
+
**ChoroplethMap** — `areas` (GeoJSON Feature[] or "world-110m"), `valueAccessor`, `colorScheme`, `projection` ("equalEarth"), `graticule`, `tooltip`, `showLegend`
|
|
66
|
+
**ProportionalSymbolMap** — `points`, `xAccessor` ("lon"), `yAccessor` ("lat"), `sizeBy`, `sizeRange`, `colorBy`, `areas` (optional background)
|
|
67
|
+
**FlowMap** — `flows`, `nodes`, `valueAccessor`, `edgeColorBy`, `lineType`, `showParticles`
|
|
68
|
+
**DistanceCartogram** — `points`, `center`, `costAccessor`, `strength`, `showRings`
|
|
87
69
|
|
|
88
|
-
|
|
70
|
+
All geo: `fitPadding`, `zoomable`, `zoomExtent`, `onZoom`, `dragRotate`, `graticule`, `tileURL`, `tileAttribution`
|
|
71
|
+
Helpers: `resolveReferenceGeography("world-110m"|"world-50m")`, `mergeData(features, data, { featureKey, dataKey })`
|
|
89
72
|
|
|
90
73
|
## Realtime Charts (`semiotic/realtime`)
|
|
91
74
|
|
|
92
|
-
Push API: `
|
|
93
|
-
|
|
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.
|
|
75
|
+
Push API: `ref.current.push({ time, value })`. All pushed data **must** include a time field.
|
|
95
76
|
|
|
96
|
-
**RealtimeLineChart**
|
|
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)
|
|
98
|
-
**RealtimeSwarmChart** — `timeAccessor`, `valueAccessor`, `categoryAccessor`, `radius`, `opacity`
|
|
99
|
-
**RealtimeWaterfallChart** — `timeAccessor`, `valueAccessor`, `positiveColor`, `negativeColor`
|
|
100
|
-
**RealtimeHeatmap** — `timeAccessor`, `valueAccessor`, `heatmapXBins`, `heatmapYBins`, `aggregation`
|
|
101
|
-
**Streaming Sankey** — `StreamNetworkFrame` with `chartType="sankey"`, `showParticles`, `particleStyle`. Push individual edges: `ref.current.push({ source, target, value })`.
|
|
77
|
+
**RealtimeLineChart**, **RealtimeHistogram** (+ `brush`, `onBrush`, `linkedBrush`), **RealtimeSwarmChart**, **RealtimeWaterfallChart**, **RealtimeHeatmap**, **Streaming Sankey** (StreamNetworkFrame + `showParticles`)
|
|
102
78
|
|
|
103
|
-
Encoding: `decay`, `pulse`, `transition`, `staleness` — compose freely
|
|
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).
|
|
79
|
+
Encoding: `decay`, `pulse`, `transition`, `staleness` — compose freely.
|
|
106
80
|
|
|
107
81
|
### Push API on HOC charts
|
|
108
|
-
Most
|
|
82
|
+
Most HOCs support push via `forwardRef`. **Omit** `data` — do NOT pass `data={[]}`.
|
|
109
83
|
```jsx
|
|
110
84
|
const ref = useRef()
|
|
111
|
-
ref.current.push({ x: 1, y: 2 })
|
|
112
|
-
ref.current.pushMany([...points])
|
|
113
|
-
ref.current.
|
|
114
|
-
ref.current.
|
|
115
|
-
|
|
85
|
+
ref.current.push({ id: "p1", x: 1, y: 2 })
|
|
86
|
+
ref.current.pushMany([...points])
|
|
87
|
+
ref.current.remove("p1") // by ID — requires pointIdAccessor
|
|
88
|
+
ref.current.remove(["p1", "p2"]) // batch remove
|
|
89
|
+
ref.current.update("p1", d => ({ ...d, y: 99 })) // in-place update — requires pointIdAccessor
|
|
90
|
+
ref.current.clear()
|
|
91
|
+
ref.current.getData()
|
|
92
|
+
<Scatterplot ref={ref} xAccessor="x" yAccessor="y" pointIdAccessor="id" />
|
|
116
93
|
```
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
## Stream Frame Callbacks (advanced)
|
|
120
|
-
Frame callbacks (`nodeStyle`, `edgeStyle`, `nodeSize` as fn) receive `RealtimeNode`/`RealtimeEdge` wrappers. Access original data via `.data`:
|
|
121
|
-
```jsx
|
|
122
|
-
// WRONG: nodeSize={(d) => d.weight} — d.weight is undefined
|
|
123
|
-
// RIGHT: nodeSize={(d) => d.data?.weight} — or use string: nodeSize="weight"
|
|
124
|
-
```
|
|
125
|
-
Same applies to `frameProps` style functions on HOCs. `customHoverBehavior`/`customClickBehavior` receive `{ type, data, x, y } | null`. `tooltipContent` receives `{ type, data }`.
|
|
126
|
-
|
|
127
|
-
## Hover Indicator
|
|
128
|
-
The hover dot automatically matches the hovered element's color (line stroke, point fill, etc.). Override via `frameProps`:
|
|
129
|
-
```jsx
|
|
130
|
-
<LineChart frameProps={{ hoverAnnotation: { pointColor: "#ff0000" } }} />
|
|
131
|
-
```
|
|
132
|
-
Fallback chain: `pointColor` → element color → `--semiotic-primary` CSS var → `#007bff`.
|
|
94
|
+
`remove()` and `update()` require an ID accessor: `pointIdAccessor` on XY/realtime charts, `dataIdAccessor` on ordinal charts. Network HOC refs also use `remove(id)`/`update(id, updater)` (operates on nodes). For edge-level operations, use `StreamNetworkFrameHandle` directly: `removeNode(id)`, `removeEdge(sourceId, targetId)`, `updateNode(id, updater)`, `updateEdge(sourceId, targetId, updater)`.
|
|
95
|
+
Not supported: Tree, Treemap, CirclePack, Orbit, ChoroplethMap, FlowMap, ScatterplotMatrix.
|
|
133
96
|
|
|
134
97
|
## Coordinated Views
|
|
135
98
|
|
|
136
|
-
**LinkedCharts** — `selections
|
|
137
|
-
|
|
138
|
-
|
|
99
|
+
**LinkedCharts** — `selections`, **CategoryColorProvider** — `colors`|`categories` + `colorScheme`
|
|
100
|
+
Chart props: `selection`, `linkedHover`, `linkedBrush`. Hooks: `useSelection`, `useLinkedHover`, `useBrushSelection`
|
|
101
|
+
**Linked crosshair**: `linkedHover={{ name: "sync", mode: "x-position", xField: "time" }}`. Click-to-lock: click locks crosshair (dashed white), click/Escape unlocks.
|
|
102
|
+
**ScatterplotMatrix**, **ChartContainer** (`title`, `subtitle`, `actions`), **ChartGrid** (`columns`, `gap`), **ContextLayout**
|
|
139
103
|
|
|
140
|
-
|
|
141
|
-
**ScatterplotMatrix** — `data`, `fields`, `colorBy`, `cellSize`, `hoverMode`, `brushMode`
|
|
142
|
-
**ChartContainer** — `title`, `subtitle`, `height` (400), `width` ("100%"), `status`, `loading`, `error`, `errorBoundary`, `actions` ({ export, fullscreen, copyConfig, dataSummary }), `controls`
|
|
143
|
-
**ChartGrid** — `columns` (number|"auto"), `minCellWidth` (300), `gap` (16). `emphasis="primary"` spans two columns.
|
|
144
|
-
**ContextLayout** — `context` (ReactNode), `position`, `contextSize` (250)
|
|
104
|
+
## Server-Side Rendering (`semiotic/server`)
|
|
145
105
|
|
|
146
|
-
|
|
106
|
+
HOC charts render SVG automatically in server environments. For standalone generation:
|
|
147
107
|
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
<CategoryColorProvider categories={["North", "South", "East"]}>
|
|
151
|
-
<LinkedCharts>
|
|
152
|
-
<ChartGrid columns={2}>
|
|
153
|
-
<LineChart data={d} colorBy="region" linkedHover={{ name: "hl", fields: ["region"] }} selection={{ name: "hl" }} emphasis="primary" responsiveWidth />
|
|
154
|
-
<BarChart data={d} colorBy="region" linkedHover={{ name: "hl", fields: ["region"] }} selection={{ name: "hl" }} responsiveWidth />
|
|
155
|
-
</ChartGrid>
|
|
156
|
-
</LinkedCharts>
|
|
157
|
-
</CategoryColorProvider>
|
|
158
|
-
|
|
159
|
-
// Forecast + anomaly
|
|
160
|
-
<LineChart data={ts} xAccessor="time" yAccessor="value"
|
|
161
|
-
forecast={{ trainEnd: 60, steps: 15, confidence: 0.95 }}
|
|
162
|
-
anomaly={{ threshold: 2 }} />
|
|
163
|
-
|
|
164
|
-
// Pre-computed forecast bounds
|
|
165
|
-
<LineChart data={ml} xAccessor="time" yAccessor="value"
|
|
166
|
-
forecast={{ isTraining: "isTraining", isForecast: "isForecast", isAnomaly: "isAnomaly", upperBounds: "upper", lowerBounds: "lower" }} />
|
|
167
|
-
|
|
168
|
-
// Percentile band — layer AreaChart + LineChart
|
|
169
|
-
<>
|
|
170
|
-
<AreaChart data={d} xAccessor="x" yAccessor="p95" y0Accessor="p5"
|
|
171
|
-
showLine={false} areaOpacity={0.3} gradientFill />
|
|
172
|
-
<LineChart data={d} xAccessor="x" yAccessor="p50" lineWidth={2} />
|
|
173
|
-
</>
|
|
174
|
-
|
|
175
|
-
// Streaming sankey with particles
|
|
176
|
-
const sankeyRef = useRef()
|
|
177
|
-
sankeyRef.current.push({ source: "Web", target: "API", value: 1 })
|
|
178
|
-
<StreamNetworkFrame ref={sankeyRef} chartType="sankey"
|
|
179
|
-
showParticles particleStyle={{ radius: 2, colorBy: "source" }}
|
|
180
|
-
width={600} height={400} />
|
|
181
|
-
|
|
182
|
-
// SSR
|
|
183
|
-
import { renderOrdinalToStaticSVG } from "semiotic/server"
|
|
184
|
-
const svg = renderOrdinalToStaticSVG({ data, categoryAccessor: "cat", valueAccessor: "val", width: 600, height: 400 })
|
|
185
|
-
```
|
|
108
|
+
```ts
|
|
109
|
+
import { renderChart, renderToImage, renderToAnimatedGif, renderDashboard } from "semiotic/server"
|
|
186
110
|
|
|
187
|
-
|
|
111
|
+
const svg = renderChart("BarChart", { data, categoryAccessor: "region", valueAccessor: "revenue", theme: "tufte", showLegend: true, showGrid: true, annotations: [...] })
|
|
112
|
+
const png = await renderToImage("LineChart", { data, ... }, { format: "png", scale: 2 }) // requires sharp
|
|
113
|
+
const gif = await renderToAnimatedGif("line", data, { xAccessor: "x", yAccessor: "y", theme: "dark" }, { fps: 12, transitionFrames: 4, decay: { type: "linear" } }) // requires sharp + gifenc
|
|
114
|
+
const dashboard = renderDashboard([{ component: "BarChart", props: {...} }, { component: "PieChart", colSpan: 2, props: {...} }], { title: "Q1", theme: "dark", layout: { columns: 2 } })
|
|
115
|
+
```
|
|
188
116
|
|
|
189
|
-
All
|
|
117
|
+
All render functions accept `theme` (preset name or object). Theme categorical colors flow to data marks automatically. `generateFrameSVGs()` returns frame SVGs without sharp/gifenc (sync, for client preview).
|
|
118
|
+
AnimatedGifOptions: `fps`, `stepSize`, `windowSize`, `frameCount`, `xExtent`/`yExtent` (lock axes), `transitionFrames`, `easing`, `decay`, `loop`, `scale`.
|
|
119
|
+
Server SVGs include `role="img"`, `<title>`, `<desc>`, grid, legend, annotations (y-threshold, x-threshold, band, label, text, category-highlight). SVG groups have `id` attributes for Figma layer naming: `data-area`, `axes`, `grid`, `annotations`, `legend`, `chart-title`.
|
|
120
|
+
|
|
121
|
+
**`renderChart` required props by component:**
|
|
122
|
+
- **Sparkline** — `data`, `xAccessor`, `yAccessor`. No axes/grid/legend/title by default. Margin defaults to 2px.
|
|
123
|
+
- **LineChart/AreaChart** — `data`, `xAccessor`, `yAccessor`. Optional: `lineBy`/`areaBy`, `colorBy`, `colorScheme`.
|
|
124
|
+
- **StackedAreaChart** — `data`, `xAccessor`, `yAccessor`, `areaBy` (required).
|
|
125
|
+
- **Scatterplot/BubbleChart** — `data`, `xAccessor`, `yAccessor`. BubbleChart requires `sizeBy`.
|
|
126
|
+
- **Heatmap** — `data`, `xAccessor`, `yAccessor`, `valueAccessor`.
|
|
127
|
+
- **BarChart** — `data`, `categoryAccessor`, `valueAccessor`.
|
|
128
|
+
- **StackedBarChart** — `data`, `categoryAccessor`, `valueAccessor`, `stackBy` (required).
|
|
129
|
+
- **GroupedBarChart** — `data`, `categoryAccessor`, `valueAccessor`, `groupBy` (required).
|
|
130
|
+
- **PieChart/DonutChart** — `data`, `categoryAccessor`, `valueAccessor`.
|
|
131
|
+
- **FunnelChart** — `data`, `stepAccessor` ("step"), `valueAccessor` ("value"). Renders with trapezoid connectors, no axes.
|
|
132
|
+
- **GaugeChart** — `value`, `thresholds` (array of `{value, color, label}`). Optional: `min`, `max`, `sweep`, `arcWidth`.
|
|
133
|
+
- **SwimlaneChart** — `data`, `categoryAccessor`, `subcategoryAccessor` (required), `valueAccessor`.
|
|
134
|
+
- **ForceDirectedGraph** — `edges` (required). `nodes` optional (inferred from edges).
|
|
135
|
+
- **SankeyDiagram** — `edges` (required), `valueAccessor`.
|
|
136
|
+
- **ChoroplethMap** — `areas` (GeoJSON features, pre-resolved).
|
|
137
|
+
|
|
138
|
+
All components accept: `width`, `height`, `theme`, `title`, `description`, `showLegend`, `showGrid`, `background`, `annotations`, `margin`, `colorScheme`, `colorBy`, `legendPosition`. Pass additional frame-level props via `frameProps`.
|
|
190
139
|
|
|
191
|
-
|
|
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.
|
|
194
|
-
**Enclosures**: `enclose` (circle around `coordinates`), `rect-enclose`, `highlight` (`filter` fn or `field`+`value`)
|
|
195
|
-
**Statistical** (XY): `trend` (`method`: linear/polynomial/loess), `envelope`, `anomaly-band`, `forecast`
|
|
196
|
-
**Streaming anchors**: `"fixed"` (default), `"latest"` (tracks newest datum), `"sticky"` (freezes when evicted)
|
|
140
|
+
## Annotations
|
|
197
141
|
|
|
198
|
-
|
|
142
|
+
All HOCs accept `annotations` (array). Coordinates use data field names.
|
|
199
143
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
```
|
|
144
|
+
**Positioning**: `widget`, `label`, `callout`, `text`, `bracket`
|
|
145
|
+
**Reference lines**: `y-threshold` (`value`, `label`, `color`, `labelPosition`), `x-threshold`, `band` (`y0`, `y1`)
|
|
146
|
+
**Ordinal**: `category-highlight`
|
|
147
|
+
**Enclosures**: `enclose`, `rect-enclose`, `highlight`
|
|
148
|
+
**Statistical**: `trend`, `envelope`, `anomaly-band`, `forecast`
|
|
149
|
+
**Streaming anchors**: `"fixed"` | `"latest"` | `"sticky"`
|
|
207
150
|
|
|
208
151
|
## Theming
|
|
209
152
|
|
|
210
|
-
|
|
153
|
+
CSS custom properties: `--semiotic-bg`, `--semiotic-text`, `--semiotic-text-secondary`, `--semiotic-border`, `--semiotic-grid`, `--semiotic-primary`, `--semiotic-focus`, `--semiotic-font-family`, `--semiotic-tooltip-bg`/`text`/`radius`/`font-size`/`shadow`.
|
|
211
154
|
|
|
212
155
|
```jsx
|
|
213
|
-
import { ThemeProvider } from "semiotic"
|
|
214
156
|
<ThemeProvider theme="tufte"> {/* Named preset */}
|
|
215
|
-
<ThemeProvider theme={{
|
|
157
|
+
<ThemeProvider theme={{ mode: "dark", colors: { categorical: [...] } }}> {/* Merge onto dark base */}
|
|
216
158
|
```
|
|
217
159
|
|
|
218
|
-
**Color
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
Serialization (`semiotic/themes`): `themeToCSS(theme, selector)`, `themeToTokens(theme)`, `resolveThemePreset(name)`.
|
|
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`
|
|
236
|
-
|
|
237
|
-
Key: `ThemeProvider` sets CSS vars on a wrapper div (no React context). Canvas charts read vars via `getComputedStyle`. `exportChart` inlines computed styles.
|
|
238
|
-
|
|
239
|
-
**Dark/light mode merge rules:** String preset (e.g. `"dark"`) → full replacement with that preset's theme. Object with `mode` (e.g. `{ mode: "dark", colors: { categorical: [...] } }`) → merges onto the matching base theme (`DARK_THEME` or `LIGHT_THEME`), so background/text/grid adapt while your overrides are preserved. Object without `mode` → shallow-merges onto the current theme (partial override). ThemeProvider is reactive — changing the `theme` prop re-applies immediately.
|
|
240
|
-
|
|
241
|
-
**CSS interop:** Host app `--semiotic-*` vars on `:root` are overridden by ThemeProvider's closer wrapper div. To let app tokens flow through, either skip ThemeProvider and set `--semiotic-*` vars in CSS, or use the hybrid approach (ThemeProvider for palette only, CSS vars for chrome).
|
|
242
|
-
|
|
243
|
-
## Server-Side Rendering
|
|
244
|
-
- HOC charts and Frames render SVG automatically in server environments
|
|
245
|
-
- `renderXYToStaticSVG(props)`, `renderOrdinalToStaticSVG(props)`, `renderNetworkToStaticSVG(props)`, `renderGeoToStaticSVG(props)` from `semiotic/server`
|
|
246
|
-
- `frameType` is `"xy"|"ordinal"|"network"|"geo"` (NOT component names)
|
|
247
|
-
- Geo SSR requires pre-resolved features (synchronous — call `resolveReferenceGeography` first)
|
|
248
|
-
- Works with Next.js App Router, Remix, Astro
|
|
160
|
+
**Color priority** (with `colorBy`): explicit `colorScheme` > ThemeProvider `colors.categorical` > `"category10"`.
|
|
161
|
+
Presets: `light`, `dark`, `high-contrast`, `pastels`(-dark), `bi-tool`(-dark), `italian`(-dark), `tufte`(-dark), `journalist`(-dark), `playful`(-dark), `carbon`(-dark).
|
|
162
|
+
Serialization: `themeToCSS(theme, selector)`, `themeToTokens(theme)`, `resolveThemePreset(name)`.
|
|
249
163
|
|
|
250
164
|
## AI Features
|
|
251
|
-
|
|
252
|
-
- `toConfig`/`fromConfig`/`toURL`/`fromURL`/`copyConfig`/`configToJSX` — serialization
|
|
253
|
-
- `DetailsPanel` — click-driven detail panel in `ChartContainer`
|
|
254
|
-
- `validateProps(componentName, props)` — prop validation with typo suggestions
|
|
255
|
-
- `diagnoseConfig(componentName, props)` — anti-pattern detector (13+ checks)
|
|
256
|
-
- `exportChart(containerDiv, { format: "png"|"svg" })` — pass wrapper div, composites canvas+SVG
|
|
257
|
-
- `npx semiotic-ai --doctor` — CLI validation
|
|
258
|
-
|
|
259
|
-
## Canvas Pattern Fills
|
|
260
|
-
|
|
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.
|
|
165
|
+
`onObservation`/`useChartObserver`, `toConfig`/`fromConfig`/`toURL`/`fromURL`/`copyConfig`/`configToJSX`, `validateProps(component, props)`, `diagnoseConfig(component, props)`, `exportChart(div, { format })`, `npx semiotic-ai --doctor`
|
|
262
166
|
|
|
263
167
|
## Accessibility
|
|
264
168
|
|
|
265
|
-
|
|
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.
|
|
169
|
+
`role="group"` (outer) + `role="img"` (inner canvas). Keyboard: arrows navigate points, Enter cycles neighbors, Home/End/PageUp/PageDown. Shape-adaptive focus ring (`--semiotic-focus`). `accessibleTable` (default true) for sr-only data summary. Auto-detects `prefers-reduced-motion` and `forced-colors`. Hooks: `useReducedMotion()`, `useHighContrast()`.
|
|
278
170
|
|
|
279
171
|
## Known Pitfalls
|
|
280
172
|
|
|
281
|
-
- **Tooltip datum shape**: HOC
|
|
282
|
-
- **
|
|
283
|
-
- **
|
|
284
|
-
- **
|
|
285
|
-
- **
|
|
286
|
-
- **
|
|
287
|
-
- **
|
|
288
|
-
- **
|
|
289
|
-
- **
|
|
290
|
-
- **
|
|
291
|
-
- **
|
|
292
|
-
- **
|
|
173
|
+
- **Tooltip datum shape**: HOC tooltips get raw data. Frame `tooltipContent` gets wrapped — use `d.data`.
|
|
174
|
+
- **Legend**: "bottom" expands margin ~80px. MultiAxisLineChart: use `legendPosition="bottom"`.
|
|
175
|
+
- **Log scale**: Domain min clamped to 1e-6.
|
|
176
|
+
- **barPadding**: Pixel value (40/60 default). Reduce for small charts.
|
|
177
|
+
- **Horizontal bars**: Need wider left margin: `margin={{ left: 120 }}`.
|
|
178
|
+
- **Push API**: Omit `data` entirely. `data={[]}` clears on every render.
|
|
179
|
+
- **frameProps style functions**: Bypass HOC color resolution — use `colorBy` prop instead.
|
|
180
|
+
- **Geo imports**: Always `semiotic/geo`, never `semiotic`, to avoid d3-geo in non-geo bundles.
|
|
181
|
+
- **fillArea**: `fillArea={["seriesA"]}` fills named series only. Names must match `lineBy`/`colorBy` keys.
|
|
182
|
+
- **hoverHighlight**: Requires `colorBy` as a string field.
|
|
183
|
+
- **tooltip="multi"**: Shows all series at hovered X. Custom fn receives `datum.allSeries`.
|
|
184
|
+
- **Axis config**: `frameProps.axes: [{ orient, includeMax, autoRotate, gridStyle, landmarkTicks }]`
|
|
185
|
+
- **xScaleType: "time"**: Creates `scaleTime`. Required for landmark ticks with timestamps.
|
|
186
|
+
- **scalePadding**: Pixel inset on scale ranges. Pass via `frameProps={{ scalePadding: 12 }}`.
|
|
187
|
+
- **categoryFormat/xFormat/yFormat**: Can return ReactNode (renders in `<foreignObject>`).
|
|
188
|
+
- **Tick deduplication**: Adjacent identical labels auto-removed.
|
|
293
189
|
|
|
294
190
|
## Performance
|
|
295
191
|
|
|
296
|
-
Prefer string accessors (`xAccessor="value"`)
|
|
192
|
+
Prefer string accessors (`xAccessor="value"`) — always referentially stable. Memoize function accessors with `useCallback`.
|
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ dashboards when you need them. Structured schemas and an MCP server so
|
|
|
12
12
|
AI coding assistants generate correct chart code on the first try.
|
|
13
13
|
|
|
14
14
|
```jsx
|
|
15
|
-
import { LineChart } from "semiotic"
|
|
15
|
+
import { LineChart } from "semiotic/xy" // 143 KB gzip
|
|
16
16
|
|
|
17
17
|
<LineChart
|
|
18
18
|
data={salesData}
|
|
@@ -277,20 +277,35 @@ configToJSX(config)
|
|
|
277
277
|
Supports bar, line, area, point, rect, arc, tick marks with encoding translation
|
|
278
278
|
for color, size, aggregation, and binning.
|
|
279
279
|
|
|
280
|
-
##
|
|
280
|
+
## Bundle Sizes
|
|
281
|
+
|
|
282
|
+
Semiotic ships 11 entry points. **Don't import from `"semiotic"` unless you need everything** — use the sub-path that matches your chart type:
|
|
281
283
|
|
|
282
|
-
|
|
284
|
+
| Entry Point | gzip | What's inside |
|
|
285
|
+
|---|---|---|
|
|
286
|
+
| `semiotic/xy` | **143 KB** | LineChart, AreaChart, Scatterplot, Heatmap, + 7 more XY charts |
|
|
287
|
+
| `semiotic/ordinal` | **109 KB** | BarChart, PieChart, BoxPlot, Histogram, + 11 more categorical charts |
|
|
288
|
+
| `semiotic/network` | **98 KB** | ForceDirectedGraph, SankeyDiagram, Treemap, + 4 more |
|
|
289
|
+
| `semiotic/geo` | **93 KB** | ChoroplethMap, FlowMap, DistanceCartogram, ProportionalSymbolMap |
|
|
290
|
+
| `semiotic/realtime` | **145 KB** | RealtimeLineChart, RealtimeHistogram, + 3 streaming charts |
|
|
291
|
+
| `semiotic/server` | **100 KB** | renderChart, renderDashboard, renderToImage, renderToAnimatedGif |
|
|
292
|
+
| `semiotic/utils` | **31 KB** | ThemeProvider, validators, serialization — no chart components |
|
|
293
|
+
| `semiotic/themes` | **5 KB** | Theme presets only (tufte, carbon, etc.) |
|
|
294
|
+
| `semiotic/data` | **5 KB** | bin, rollup, groupBy, pivot, fromVegaLite |
|
|
295
|
+
| `semiotic/ai` | **269 KB** | All 38 HOCs + validation — optimized for LLM code generation |
|
|
296
|
+
| `semiotic` | **278 KB** | Everything above (full bundle) |
|
|
283
297
|
|
|
284
298
|
```jsx
|
|
285
|
-
|
|
286
|
-
import {
|
|
287
|
-
import {
|
|
288
|
-
import {
|
|
289
|
-
import {
|
|
299
|
+
// Import from the sub-path, not from "semiotic"
|
|
300
|
+
import { LineChart } from "semiotic/xy"
|
|
301
|
+
import { BarChart } from "semiotic/ordinal"
|
|
302
|
+
import { SankeyDiagram } from "semiotic/network"
|
|
303
|
+
import { ChoroplethMap } from "semiotic/geo"
|
|
290
304
|
```
|
|
291
305
|
|
|
292
|
-
|
|
293
|
-
|
|
306
|
+
**Tree-shaking**: Each sub-path is a self-contained bundle with `"sideEffects": false`. Bundlers (webpack, Rollup, Vite, esbuild) will tree-shake unused exports. If you only use `LineChart` from `semiotic/xy`, the bar/pie/network code is never included.
|
|
307
|
+
|
|
308
|
+
**When to use `"semiotic"`**: Only if your app uses charts from 3+ categories (XY + ordinal + network) and you'd rather have one import than three. The full bundle is 278 KB gzip — comparable to a single D3 import.
|
|
294
309
|
|
|
295
310
|
## TypeScript
|
|
296
311
|
|
|
@@ -321,17 +336,22 @@ import { LineChart } from "semiotic"
|
|
|
321
336
|
<LineChart data={data} xAccessor="date" yAccessor="value" />
|
|
322
337
|
```
|
|
323
338
|
|
|
324
|
-
For standalone SVG generation (email, OG images, PDF), use the server entry point:
|
|
339
|
+
For standalone SVG/PNG/GIF generation (email, OG images, PDF, Slack), use the server entry point:
|
|
325
340
|
|
|
326
341
|
```js
|
|
327
|
-
import {
|
|
342
|
+
import { renderChart, renderToImage, renderToAnimatedGif } from "semiotic/server"
|
|
328
343
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
xAccessor: "date",
|
|
332
|
-
|
|
333
|
-
size: [600, 400],
|
|
344
|
+
// SVG — sync, no dependencies
|
|
345
|
+
const svg = renderChart("LineChart", {
|
|
346
|
+
data, xAccessor: "date", yAccessor: "value",
|
|
347
|
+
theme: "tufte", title: "Revenue Trend",
|
|
334
348
|
})
|
|
349
|
+
|
|
350
|
+
// PNG — async, requires sharp
|
|
351
|
+
const png = await renderToImage("BarChart", { data, ... }, { format: "png", scale: 2 })
|
|
352
|
+
|
|
353
|
+
// Animated GIF — async, requires sharp + gifenc
|
|
354
|
+
const gif = await renderToAnimatedGif("line", data, { ... }, { fps: 12 })
|
|
335
355
|
```
|
|
336
356
|
|
|
337
357
|
## MCP Server
|
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.
|
|
4
|
+
"version": "3.3.0",
|
|
5
5
|
"description": "React data visualization library for charts, networks, and beyond",
|
|
6
6
|
"tools": [
|
|
7
7
|
{
|
|
@@ -2778,6 +2778,69 @@
|
|
|
2778
2778
|
}
|
|
2779
2779
|
}
|
|
2780
2780
|
},
|
|
2781
|
+
{
|
|
2782
|
+
"type": "function",
|
|
2783
|
+
"function": {
|
|
2784
|
+
"name": "GaugeChart",
|
|
2785
|
+
"description": "Single-value gauge with threshold zones, needle indicator, and configurable sweep angle. Built on StreamOrdinalFrame radial projection.",
|
|
2786
|
+
"parameters": {
|
|
2787
|
+
"type": "object",
|
|
2788
|
+
"properties": {
|
|
2789
|
+
"value": {
|
|
2790
|
+
"type": "number",
|
|
2791
|
+
"description": "Current gauge value"
|
|
2792
|
+
},
|
|
2793
|
+
"min": {
|
|
2794
|
+
"type": "number",
|
|
2795
|
+
"default": 0
|
|
2796
|
+
},
|
|
2797
|
+
"max": {
|
|
2798
|
+
"type": "number",
|
|
2799
|
+
"default": 100
|
|
2800
|
+
},
|
|
2801
|
+
"thresholds": {
|
|
2802
|
+
"type": "array",
|
|
2803
|
+
"description": "Array of { value, color, label? } defining threshold zones. Last value should equal max.",
|
|
2804
|
+
"items": { "type": "object" }
|
|
2805
|
+
},
|
|
2806
|
+
"arcWidth": {
|
|
2807
|
+
"type": "number",
|
|
2808
|
+
"description": "Arc thickness as fraction of radius (0-1)",
|
|
2809
|
+
"default": 0.3
|
|
2810
|
+
},
|
|
2811
|
+
"sweep": {
|
|
2812
|
+
"type": "number",
|
|
2813
|
+
"description": "Arc sweep angle in degrees (gap centered at bottom)",
|
|
2814
|
+
"default": 240
|
|
2815
|
+
},
|
|
2816
|
+
"showNeedle": {
|
|
2817
|
+
"type": "boolean",
|
|
2818
|
+
"default": true
|
|
2819
|
+
},
|
|
2820
|
+
"needleColor": {
|
|
2821
|
+
"type": "string"
|
|
2822
|
+
},
|
|
2823
|
+
"valueFormat": {
|
|
2824
|
+
"type": "string",
|
|
2825
|
+
"description": "Format function for center value label"
|
|
2826
|
+
},
|
|
2827
|
+
"showScaleLabels": {
|
|
2828
|
+
"type": "boolean",
|
|
2829
|
+
"default": true
|
|
2830
|
+
},
|
|
2831
|
+
"width": {
|
|
2832
|
+
"type": "number",
|
|
2833
|
+
"default": 300
|
|
2834
|
+
},
|
|
2835
|
+
"height": {
|
|
2836
|
+
"type": "number",
|
|
2837
|
+
"default": 250
|
|
2838
|
+
}
|
|
2839
|
+
},
|
|
2840
|
+
"required": ["value"]
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
},
|
|
2781
2844
|
{
|
|
2782
2845
|
"type": "function",
|
|
2783
2846
|
"function": {
|