semiotic 3.0.0-beta.5 → 3.0.0-beta.7
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 +80 -150
- package/README.md +6 -3
- package/ai/cli.js +72 -0
- package/dist/charts/index.d.ts +2 -0
- package/dist/charts/shared/validateChartData.d.ts +3 -0
- package/dist/charts/shared/withChartWrapper.d.ts +18 -0
- package/dist/charts/xy/ConnectedScatterplot.d.ts +60 -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 +1 -0
- package/dist/semiotic-ai.min.js +1 -1
- package/dist/semiotic-ai.module.min.js +1 -1
- package/dist/semiotic-xy.d.ts +1 -0
- package/dist/semiotic.d.ts +3 -3
- package/dist/semiotic.min.js +1 -1
- package/dist/semiotic.module.min.js +1 -1
- package/dist/stream/DataSourceAdapter.d.ts +3 -0
- package/dist/xy.min.js +1 -1
- package/dist/xy.module.min.js +1 -1
- package/package.json +5 -3
package/CLAUDE.md
CHANGED
|
@@ -2,192 +2,122 @@
|
|
|
2
2
|
|
|
3
3
|
## Quick Start
|
|
4
4
|
- Install: `npm install semiotic`
|
|
5
|
-
- Import
|
|
6
|
-
- `semiotic
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
- MCP: `npx semiotic-mcp` — MCP server rendering charts to static SVG
|
|
5
|
+
- Import: `semiotic`, `semiotic/xy`, `semiotic/ordinal`, `semiotic/network`, `semiotic/realtime`, `semiotic/ai`, `semiotic/data`
|
|
6
|
+
- CLI: `npx semiotic-ai [--schema|--compact|--examples|--doctor]`
|
|
7
|
+
- MCP: `npx semiotic-mcp`
|
|
8
|
+
- Every HOC has a built-in error boundary (never blanks the page) and dev-mode validation warnings
|
|
10
9
|
|
|
11
10
|
## Architecture
|
|
12
|
-
- **HOC Charts
|
|
13
|
-
-
|
|
14
|
-
- Every HOC accepts `frameProps` to pass through to the underlying Stream Frame
|
|
15
|
-
- TypeScript `strict: true`; all charts have `role="img"` + `aria-label`
|
|
11
|
+
- **HOC Charts**: Simple props, sensible defaults. **Stream Frames**: Full control.
|
|
12
|
+
- Every HOC accepts `frameProps` to pass through. TypeScript `strict: true`.
|
|
16
13
|
|
|
17
|
-
##
|
|
14
|
+
## Common Props (all HOCs)
|
|
15
|
+
`title`, `width` (600), `height` (400), `responsiveWidth`, `responsiveHeight`, `margin`, `className`, `enableHover` (true), `tooltip`, `showLegend`, `showGrid` (false), `frameProps`, `onObservation`, `chartId`
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
`title` (string), `width` (number, 600), `height` (number, 400), `responsiveWidth` (boolean, false), `responsiveHeight` (boolean, false), `margin` (object), `className` (string), `enableHover` (boolean, true), `tooltip` (fn), `showLegend` (boolean), `showGrid` (boolean, false), `frameProps` (object), `onObservation` (fn), `chartId` (string)
|
|
17
|
+
## XY Charts (`semiotic/xy`)
|
|
21
18
|
|
|
22
|
-
|
|
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)
|
|
23
20
|
|
|
24
|
-
**
|
|
21
|
+
**AreaChart** — LineChart props + `areaBy`, `y0Accessor` (band/ribbon), `gradientFill` (boolean|{topOpacity,bottomOpacity}), `areaOpacity` (0.7), `showLine` (true)
|
|
25
22
|
|
|
26
|
-
**
|
|
23
|
+
**StackedAreaChart** — AreaChart + `normalize` (false)
|
|
27
24
|
|
|
28
|
-
**
|
|
25
|
+
**Scatterplot** — `data`, `xAccessor`, `yAccessor`, `colorBy`, `sizeBy`, `sizeRange`, `pointRadius` (5), `pointOpacity` (0.8), `marginalGraphics`
|
|
29
26
|
|
|
30
|
-
**
|
|
27
|
+
**BubbleChart** — Scatterplot + `sizeBy` (required), `sizeRange` ([5,40]), `bubbleOpacity` (0.6)
|
|
31
28
|
|
|
32
|
-
**
|
|
29
|
+
**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.
|
|
33
30
|
|
|
34
|
-
**Heatmap** — `data
|
|
31
|
+
**Heatmap** — `data`, `xAccessor`, `yAccessor`, `valueAccessor`, `colorScheme`, `showValues`, `cellBorderColor`
|
|
35
32
|
|
|
36
|
-
|
|
33
|
+
## Ordinal Charts (`semiotic/ordinal`)
|
|
37
34
|
|
|
38
|
-
**BarChart** — `data
|
|
35
|
+
**BarChart** — `data`, `categoryAccessor`, `valueAccessor`, `orientation`, `colorBy`, `sort`, `barPadding`
|
|
36
|
+
**StackedBarChart** — + `stackBy` (required), `normalize`
|
|
37
|
+
**GroupedBarChart** — + `groupBy` (required)
|
|
38
|
+
**SwarmPlot** — `data`, `categoryAccessor`, `valueAccessor`, `colorBy`, `sizeBy`, `pointRadius`, `pointOpacity`
|
|
39
|
+
**BoxPlot** — + `showOutliers`, `outlierRadius`
|
|
40
|
+
**Histogram** — + `bins` (25), `relative`. Always horizontal.
|
|
41
|
+
**ViolinPlot** — + `bins`, `curve`, `showIQR`
|
|
42
|
+
**DotPlot** — + `sort` (true), `dotRadius`, `showGrid` default true
|
|
43
|
+
**PieChart** — `data`, `categoryAccessor`, `valueAccessor`, `colorBy`, `startAngle`, `slicePadding`
|
|
44
|
+
**DonutChart** — PieChart + `innerRadius` (60), `centerContent`
|
|
39
45
|
|
|
40
|
-
|
|
46
|
+
## Network Charts (`semiotic/network`)
|
|
41
47
|
|
|
42
|
-
**
|
|
48
|
+
**ForceDirectedGraph** — `nodes`, `edges`, `nodeIDAccessor`, `sourceAccessor`, `targetAccessor`, `colorBy`, `nodeSize`, `edgeWidth`, `iterations`, `showLabels`
|
|
49
|
+
**SankeyDiagram** — `edges`, `nodes`, `valueAccessor`, `edgeColorBy`, `orientation`, `nodeAlign`, `nodeWidth`, `showLabels`, `edgeOpacity`
|
|
50
|
+
**ChordDiagram** — `edges`, `nodes`, `valueAccessor`, `edgeColorBy`, `padAngle`, `groupWidth`, `showLabels`
|
|
51
|
+
**TreeDiagram** — `data` (root), `layout`, `orientation`, `childrenAccessor`, `colorBy`, `colorByDepth`, `edgeStyle`
|
|
52
|
+
**Treemap** — `data` (root), `childrenAccessor`, `valueAccessor`, `colorBy`, `colorByDepth`, `showLabels`, `labelMode`
|
|
53
|
+
**CirclePack** — `data` (root), `childrenAccessor`, `valueAccessor`, `colorBy`, `colorByDepth`, `circleOpacity`
|
|
43
54
|
|
|
44
|
-
|
|
55
|
+
## Realtime Charts (`semiotic/realtime`)
|
|
45
56
|
|
|
46
|
-
|
|
57
|
+
Push API: `chartRef.current.push({ time, value })`
|
|
47
58
|
|
|
48
|
-
**
|
|
59
|
+
**RealtimeLineChart** — `size`, `timeAccessor`, `valueAccessor`, `windowSize` (200), `windowMode`, `stroke`, `strokeWidth`
|
|
60
|
+
**RealtimeTemporalHistogram** — + `binSize` (required), `categoryAccessor`, `colors`
|
|
61
|
+
**RealtimeSwarmChart** — + `categoryAccessor`, `radius`, `opacity`
|
|
62
|
+
**RealtimeWaterfallChart** — + `positiveColor`, `negativeColor`
|
|
63
|
+
**RealtimeHeatmap** — + `heatmapXBins`, `heatmapYBins`, `aggregation`
|
|
64
|
+
**Streaming Sankey** — `StreamNetworkFrame` with `chartType="sankey"`, `showParticles`, `particleStyle`, `tensionConfig`, `thresholds`
|
|
49
65
|
|
|
50
|
-
|
|
66
|
+
Realtime encoding: `decay`, `pulse`, `transition`, `staleness` — compose freely on all streaming charts.
|
|
51
67
|
|
|
52
|
-
|
|
68
|
+
## Coordinated Views
|
|
53
69
|
|
|
54
|
-
**
|
|
70
|
+
**LinkedCharts** — wraps charts. Props: `selections` (resolution: "union"|"intersect"|"crossfilter")
|
|
71
|
+
**CategoryColorProvider** — stable category→color mapping. Props: `colors` (map) or `categories` + `colorScheme`
|
|
72
|
+
Chart props: `selection`, `linkedHover`, `linkedBrush`. Hooks: `useSelection`, `useLinkedHover`, `useBrushSelection`, `useFilteredData`
|
|
73
|
+
**ScatterplotMatrix** — `data`, `fields`, `colorBy`, `cellSize`, `hoverMode`, `brushMode`
|
|
55
74
|
|
|
56
|
-
|
|
75
|
+
## Layout & Composition
|
|
57
76
|
|
|
58
|
-
|
|
77
|
+
**ChartGrid** — CSS Grid layout. `columns` (number|"auto"), `minCellWidth` (300), `gap` (16)
|
|
78
|
+
**ContextLayout** — primary + context panel. `context` (ReactNode), `position`, `contextSize` (250)
|
|
59
79
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
**SankeyDiagram** — `edges` (required), `nodes` (optional), `sourceAccessor`, `targetAccessor`, `valueAccessor` ("value"), `nodeIdAccessor` ("id"), `colorBy`, `edgeColorBy` ("source"|"target"|"gradient"|fn), `orientation` ("horizontal"|"vertical"), `nodeAlign` ("justify"|"left"|"right"|"center"), `nodePaddingRatio` (0.05), `nodeWidth` (15), `nodeLabel`, `showLabels` (true), `edgeOpacity` (0.5), `edgeSort`. Default 800x600.
|
|
63
|
-
|
|
64
|
-
**ChordDiagram** — `edges` (required), `nodes`, `sourceAccessor`, `targetAccessor`, `valueAccessor`, `nodeIdAccessor`, `colorBy`, `edgeColorBy`, `padAngle` (0.01), `groupWidth` (20), `sortGroups`, `nodeLabel`, `showLabels` (true), `edgeOpacity` (0.5)
|
|
65
|
-
|
|
66
|
-
**TreeDiagram** — `data` (required, single root with children), `layout` ("tree"|"cluster"|"partition"|"treemap"|"circlepack"), `orientation` ("vertical"|"horizontal"|"radial"), `childrenAccessor` ("children"), `valueAccessor`, `nodeIdAccessor` ("name"), `colorBy`, `colorByDepth` (false), `edgeStyle` ("line"|"curve"), `nodeLabel`, `showLabels` (true), `nodeSize` (5)
|
|
67
|
-
|
|
68
|
-
**Treemap** — `data` (required, root with children), `childrenAccessor`, `valueAccessor`, `nodeIdAccessor` ("name"), `colorBy`, `colorByDepth` (false), `showLabels` (true), `labelMode` ("leaf"|"parent"|"all"), `nodeLabel`, `padding` (4), `paddingTop` (0, auto 18 for "parent"). Hover shows ancestor breadcrumb.
|
|
69
|
-
|
|
70
|
-
**CirclePack** — `data` (required), `childrenAccessor`, `valueAccessor`, `nodeIdAccessor`, `colorBy`, `colorByDepth` (false), `showLabels` (true), `nodeLabel`, `circleOpacity` (0.7), `padding` (4). Labels hidden below 15px radius. Hover shows ancestor breadcrumb.
|
|
71
|
-
|
|
72
|
-
### Realtime Charts (from "semiotic" or "semiotic/realtime")
|
|
73
|
-
|
|
74
|
-
All use ref-based push API + canvas rendering: `chartRef.current.push({ time, value })`
|
|
75
|
-
|
|
76
|
-
**RealtimeLineChart** — `size` ([500,300]), `timeAccessor`, `valueAccessor`, `windowSize` (200), `windowMode` ("sliding"|"stepping"), `arrowOfTime` ("left"|"right"), `stroke`, `strokeWidth`, `strokeDasharray`, `timeExtent`, `valueExtent`, `extentPadding`, `showAxes`, `background`, `enableHover`, `tooltipContent`, `onHover`, `annotations`, `svgAnnotationRules`, `tickFormatTime`, `tickFormatValue`
|
|
77
|
-
|
|
78
|
-
**RealtimeTemporalHistogram** — RealtimeLineChart props plus `binSize` (required), `categoryAccessor`, `colors`, `fill`, `gap`, `decay`, `pulse`, `staleness`, `transition`
|
|
79
|
-
|
|
80
|
-
**RealtimeSwarmChart** — RealtimeLineChart props plus `categoryAccessor`, `colors`, `radius`, `fill`, `opacity`
|
|
81
|
-
|
|
82
|
-
**RealtimeWaterfallChart** — RealtimeLineChart props plus `positiveColor`, `negativeColor`, `connectorStroke`, `connectorWidth`, `gap`
|
|
83
|
-
|
|
84
|
-
**RealtimeHeatmap** — RealtimeLineChart props plus `heatmapXBins` (20), `heatmapYBins` (20), `aggregation` ("count"|"sum"|"mean"), `linkedHover`, `decay`, `pulse`, `staleness`
|
|
85
|
-
|
|
86
|
-
**Streaming Sankey** — Use `StreamNetworkFrame` with `chartType="sankey"`, `showParticles`, `particleStyle`, `tensionConfig`, `thresholds`, `onTopologyChange`. Ref: `push()`, `pushMany()`, `clear()`, `getTopology()`, `relayout()`, `getTension()`
|
|
87
|
-
|
|
88
|
-
### Realtime Visual Encoding (all streaming charts)
|
|
89
|
-
- `decay` — older data fades (`{ type, halfLife, minOpacity }`)
|
|
90
|
-
- `pulse` — new data flashes (`{ duration, color, glowRadius }`)
|
|
91
|
-
- `transition` — smooth interpolation (`{ duration, easing }`)
|
|
92
|
-
- `staleness` — stale feed detection (`{ threshold, dimOpacity, showBadge }`)
|
|
93
|
-
|
|
94
|
-
### Coordinated Views (from "semiotic" or "semiotic/ai")
|
|
95
|
-
|
|
96
|
-
**LinkedCharts** — Wraps charts for coordination. Props: `selections` (Record with resolution: "union"|"intersect"|"crossfilter")
|
|
97
|
-
|
|
98
|
-
Chart coordination props (on HOCs inside LinkedCharts):
|
|
99
|
-
- `selection` — consume named selection
|
|
100
|
-
- `linkedHover` — produce hover selections
|
|
101
|
-
- `linkedBrush` — produce brush selections (Scatterplot/BubbleChart only)
|
|
102
|
-
|
|
103
|
-
Hooks: `useSelection`, `useLinkedHover`, `useBrushSelection`, `useFilteredData`
|
|
104
|
-
|
|
105
|
-
**ScatterplotMatrix** — `data` (required), `fields` (required), `fieldLabels`, `colorBy`, `cellSize` (150), `cellGap` (4), `pointRadius` (2), `pointOpacity` (0.5), `diagonal` ("histogram"|"density"|"label"), `histogramBins` (20), `hoverMode` (true), `brushMode` ("crossfilter"|"intersect"|false), `unselectedOpacity` (0.1)
|
|
106
|
-
|
|
107
|
-
## Key Usage Patterns
|
|
80
|
+
## Key Patterns
|
|
108
81
|
|
|
109
82
|
```jsx
|
|
110
|
-
//
|
|
111
|
-
<
|
|
112
|
-
|
|
113
|
-
// Hierarchical data (TreeDiagram, Treemap, CirclePack) — single root with children
|
|
114
|
-
<Treemap data={rootNode} childrenAccessor="children" valueAccessor="value" />
|
|
115
|
-
|
|
116
|
-
// Network data
|
|
117
|
-
<SankeyDiagram nodes={nodes} edges={edges} valueAccessor="value" />
|
|
118
|
-
|
|
119
|
-
// Tooltips
|
|
120
|
-
<LineChart ... tooltip={MultiLineTooltip({ title: "name", fields: ["value"] })} />
|
|
121
|
-
|
|
122
|
-
// Coordinated views
|
|
83
|
+
// Cross-highlighting dashboard
|
|
84
|
+
<CategoryColorProvider categories={["North", "South", "East"]}>
|
|
123
85
|
<LinkedCharts>
|
|
124
|
-
<
|
|
125
|
-
|
|
86
|
+
<ChartGrid columns={2}>
|
|
87
|
+
<LineChart data={d} colorBy="region" linkedHover={{ name: "hl", fields: ["region"] }} selection={{ name: "hl" }} responsiveWidth />
|
|
88
|
+
<BarChart data={d} colorBy="region" linkedHover={{ name: "hl", fields: ["region"] }} selection={{ name: "hl" }} responsiveWidth />
|
|
89
|
+
</ChartGrid>
|
|
126
90
|
</LinkedCharts>
|
|
127
|
-
|
|
128
|
-
// Marginal graphics
|
|
129
|
-
<Scatterplot data={d} marginalGraphics={{ top: "histogram", right: "violin" }} />
|
|
130
|
-
|
|
131
|
-
// Theming
|
|
132
|
-
<ThemeProvider theme="dark"><LineChart ... /></ThemeProvider>
|
|
133
|
-
|
|
134
|
-
// Shared category colors across charts
|
|
135
|
-
<CategoryColorProvider colors={{ North: "#e41a1c", South: "#377eb8" }}>
|
|
136
|
-
<LineChart data={d1} colorBy="region" />
|
|
137
|
-
<BarChart data={d2} colorBy="region" /> {/* same colors */}
|
|
138
91
|
</CategoryColorProvider>
|
|
139
92
|
|
|
140
|
-
//
|
|
141
|
-
|
|
93
|
+
// Forecast + anomaly (auto)
|
|
94
|
+
<LineChart data={ts} xAccessor="time" yAccessor="value"
|
|
95
|
+
forecast={{ trainEnd: 60, steps: 15, confidence: 0.95 }}
|
|
96
|
+
anomaly={{ threshold: 2 }} />
|
|
142
97
|
|
|
143
|
-
//
|
|
144
|
-
|
|
98
|
+
// Forecast (pre-computed ML bounds)
|
|
99
|
+
<LineChart data={ml} xAccessor="time" yAccessor="value"
|
|
100
|
+
forecast={{ isTraining: "isTraining", isForecast: "isForecast", isAnomaly: "isAnomaly", upperBounds: "upper", lowerBounds: "lower" }} />
|
|
101
|
+
|
|
102
|
+
// Gradient area + percentile band
|
|
103
|
+
<AreaChart data={d} xAccessor="x" yAccessor="p95" y0Accessor="p5" gradientFill />
|
|
145
104
|
|
|
146
105
|
// Realtime
|
|
147
106
|
const ref = useRef()
|
|
148
107
|
ref.current.push({ time: Date.now(), value: 42 })
|
|
149
108
|
<RealtimeLineChart ref={ref} timeAccessor="time" valueAccessor="value" />
|
|
150
|
-
|
|
151
|
-
// Forecast + anomaly detection (LineChart only)
|
|
152
|
-
// Auto mode: training=dashed, observed=solid, forecast=dotted with confidence envelope
|
|
153
|
-
<LineChart data={timeSeries} xAccessor="time" yAccessor="value"
|
|
154
|
-
forecast={{ trainEnd: 60, steps: 15, confidence: 0.95 }}
|
|
155
|
-
anomaly={{ threshold: 2, anomalyColor: "#ef4444" }} />
|
|
156
|
-
|
|
157
|
-
// Pre-computed mode: bring your own bounds from an ML model
|
|
158
|
-
// Data: { time, value, isTraining?, isForecast?, isAnomaly?, upperBounds?, lowerBounds? }
|
|
159
|
-
// Envelope follows per-point bounds (non-rectilinear)
|
|
160
|
-
<LineChart data={mlOutput} xAccessor="time" yAccessor="value"
|
|
161
|
-
forecast={{
|
|
162
|
-
isTraining: "isTraining", isForecast: "isForecast",
|
|
163
|
-
isAnomaly: "isAnomaly", upperBounds: "upper", lowerBounds: "lower",
|
|
164
|
-
}} />
|
|
165
109
|
```
|
|
166
110
|
|
|
167
111
|
## AI Features
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
**ChartErrorBoundary** — `fallback` (ReactNode), `onError` (fn)
|
|
182
|
-
|
|
183
|
-
**validateProps(componentName, props)** — returns `{ valid, errors }`
|
|
184
|
-
|
|
185
|
-
## What Semiotic Does That Others Don't
|
|
186
|
-
- Network visualization (force, sankey, chord, tree, treemap, circlepack) with same clean API
|
|
187
|
-
- Streaming data (canvas 60fps push API) + streaming Sankey with particles
|
|
188
|
-
- Realtime visual encoding (decay, pulse, transitions, staleness)
|
|
189
|
-
- Coordinated views (LinkedCharts, crossfilter brushing, ScatterplotMatrix)
|
|
190
|
-
- Statistical summaries (box, violin, swarm, histogram, marginal graphics)
|
|
191
|
-
- AI observation hooks + chart state serialization
|
|
192
|
-
- Server-side SVG via `renderToStaticSVG()` (from "semiotic/server")
|
|
193
|
-
- Global theming with ThemeProvider
|
|
112
|
+
- `onObservation` — structured events (hover, click, brush, selection) on all HOCs
|
|
113
|
+
- `useChartObserver` — aggregates observations across LinkedCharts
|
|
114
|
+
- `toConfig`/`fromConfig`/`toURL`/`fromURL`/`copyConfig`/`configToJSX` — chart state serialization
|
|
115
|
+
- `DetailsPanel` — click-driven detail panel inside `ChartContainer`
|
|
116
|
+
- `validateProps(componentName, props)` — prop validation
|
|
117
|
+
- `ChartErrorBoundary` — error boundary
|
|
118
|
+
- `exportChart(el, { format: "png"|"svg" })` — browser export
|
|
119
|
+
- `renderToStaticSVG()` — server-side SVG (from `semiotic/server`)
|
|
120
|
+
- `npx semiotic-ai --doctor` — validate component + props JSON from CLI
|
|
121
|
+
|
|
122
|
+
## Differentiators
|
|
123
|
+
Network viz, streaming canvas, realtime encoding, coordinated views, statistical summaries, AI hooks, chart serialization, global theming, keyboard navigation
|
package/README.md
CHANGED
|
@@ -84,8 +84,10 @@ snapshots, or coordinated views across chart types.
|
|
|
84
84
|
**AI-ready.** Semiotic ships with structured schemas (`ai/schema.json`), an
|
|
85
85
|
`import from "semiotic/ai"` entry point, and an MCP server — all designed for
|
|
86
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
|
|
88
|
-
to
|
|
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.
|
|
89
91
|
|
|
90
92
|
**Vega-Lite compatible.** Have existing Vega-Lite specs? `fromVegaLite(spec)`
|
|
91
93
|
translates them to Semiotic chart configs — instant onboarding from notebooks,
|
|
@@ -100,7 +102,7 @@ highlights new services as they appear. Visualization as product navigation.
|
|
|
100
102
|
## Install
|
|
101
103
|
|
|
102
104
|
```bash
|
|
103
|
-
npm install semiotic@3.0.0-beta.
|
|
105
|
+
npm install semiotic@3.0.0-beta.7
|
|
104
106
|
```
|
|
105
107
|
|
|
106
108
|
Requires React 18.1 or later.
|
|
@@ -218,6 +220,7 @@ import { LineChart, BarChart } from "semiotic"
|
|
|
218
220
|
| **Network** | `ForceDirectedGraph` `ChordDiagram` `SankeyDiagram` `TreeDiagram` `Treemap` `CirclePack` |
|
|
219
221
|
| **Realtime** | `RealtimeLineChart` `RealtimeHistogram` `RealtimeSwarmChart` `RealtimeWaterfallChart` `RealtimeHeatmap` |
|
|
220
222
|
| **Coordination** | `LinkedCharts` `ScatterplotMatrix` |
|
|
223
|
+
| **Layout** | `ChartGrid` `ContextLayout` `CategoryColorProvider` |
|
|
221
224
|
| **Frames** | `StreamXYFrame` `StreamOrdinalFrame` `StreamNetworkFrame` |
|
|
222
225
|
|
|
223
226
|
### Vega-Lite Translation
|
package/ai/cli.js
CHANGED
|
@@ -21,6 +21,7 @@ Usage:
|
|
|
21
21
|
npx semiotic-ai --schema Print ai/schema.json (tool definitions)
|
|
22
22
|
npx semiotic-ai --compact Print ai/system-prompt.md (compact prompt)
|
|
23
23
|
npx semiotic-ai --examples Print ai/examples.md (copy-paste examples)
|
|
24
|
+
npx semiotic-ai --doctor Validate component + props JSON from stdin
|
|
24
25
|
npx semiotic-ai --help Show this help message
|
|
25
26
|
`.trim()
|
|
26
27
|
|
|
@@ -31,6 +32,77 @@ if (flag === "--help" || flag === "-h") {
|
|
|
31
32
|
process.exit(0)
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
// --doctor: validate component + props from stdin or argv
|
|
36
|
+
if (flag === "--doctor") {
|
|
37
|
+
let input = ""
|
|
38
|
+
if (process.argv[3]) {
|
|
39
|
+
// npx semiotic-ai --doctor '{"component":"LineChart","props":{...}}'
|
|
40
|
+
input = process.argv.slice(3).join(" ")
|
|
41
|
+
} else if (!process.stdin.isTTY) {
|
|
42
|
+
// echo '...' | npx semiotic-ai --doctor
|
|
43
|
+
input = fs.readFileSync("/dev/stdin", "utf-8")
|
|
44
|
+
} else {
|
|
45
|
+
console.error("Usage: npx semiotic-ai --doctor '{\"component\":\"LineChart\",\"props\":{\"data\":[...]}}'")
|
|
46
|
+
console.error(" echo '{...}' | npx semiotic-ai --doctor")
|
|
47
|
+
process.exit(1)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const { component, props } = JSON.parse(input)
|
|
52
|
+
if (!component || !props) {
|
|
53
|
+
console.error("Input must be JSON with { component, props } fields.")
|
|
54
|
+
process.exit(1)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Load validateProps from dist
|
|
58
|
+
const distPath = path.join(pkgRoot, "dist", "semiotic-ai.min.js")
|
|
59
|
+
let validateProps
|
|
60
|
+
try {
|
|
61
|
+
const mod = require(distPath)
|
|
62
|
+
validateProps = mod.validateProps
|
|
63
|
+
} catch (e) {
|
|
64
|
+
console.error("Could not load semiotic/ai dist. Run 'npm run dist' first.")
|
|
65
|
+
process.exit(1)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!validateProps) {
|
|
69
|
+
console.error("validateProps not found in semiotic/ai exports.")
|
|
70
|
+
process.exit(1)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const result = validateProps(component, props)
|
|
74
|
+
if (result.valid) {
|
|
75
|
+
console.log(`✓ ${component}: props are valid.`)
|
|
76
|
+
// Additional data shape checks
|
|
77
|
+
if (props.data && Array.isArray(props.data) && props.data.length > 0) {
|
|
78
|
+
const sample = props.data[0]
|
|
79
|
+
console.log(` Data shape: ${props.data.length} items, keys: [${Object.keys(sample).join(", ")}]`)
|
|
80
|
+
if (props.xAccessor && typeof props.xAccessor === "string" && !(props.xAccessor in sample)) {
|
|
81
|
+
console.log(` ⚠ xAccessor "${props.xAccessor}" not in data keys. Available: ${Object.keys(sample).join(", ")}`)
|
|
82
|
+
}
|
|
83
|
+
if (props.yAccessor && typeof props.yAccessor === "string" && !(props.yAccessor in sample)) {
|
|
84
|
+
console.log(` ⚠ yAccessor "${props.yAccessor}" not in data keys. Available: ${Object.keys(sample).join(", ")}`)
|
|
85
|
+
}
|
|
86
|
+
if (props.categoryAccessor && typeof props.categoryAccessor === "string" && !(props.categoryAccessor in sample)) {
|
|
87
|
+
console.log(` ⚠ categoryAccessor "${props.categoryAccessor}" not in data keys. Available: ${Object.keys(sample).join(", ")}`)
|
|
88
|
+
}
|
|
89
|
+
if (props.valueAccessor && typeof props.valueAccessor === "string" && !(props.valueAccessor in sample)) {
|
|
90
|
+
console.log(` ⚠ valueAccessor "${props.valueAccessor}" not in data keys. Available: ${Object.keys(sample).join(", ")}`)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
console.log(`✗ ${component}: validation failed.`)
|
|
95
|
+
for (const err of result.errors) {
|
|
96
|
+
console.log(` • ${err}`)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
} catch (err) {
|
|
100
|
+
console.error(`Failed to parse input: ${err.message}`)
|
|
101
|
+
process.exit(1)
|
|
102
|
+
}
|
|
103
|
+
process.exit(0)
|
|
104
|
+
}
|
|
105
|
+
|
|
34
106
|
const filePath = flag ? FILES[flag] : FILES.default
|
|
35
107
|
|
|
36
108
|
if (!filePath) {
|
package/dist/charts/index.d.ts
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
export { Scatterplot } from "./xy/Scatterplot";
|
|
10
10
|
export type { ScatterplotProps } from "./xy/Scatterplot";
|
|
11
|
+
export { ConnectedScatterplot } from "./xy/ConnectedScatterplot";
|
|
12
|
+
export type { ConnectedScatterplotProps } from "./xy/ConnectedScatterplot";
|
|
11
13
|
export { LineChart } from "./xy/LineChart";
|
|
12
14
|
export type { LineChartProps } from "./xy/LineChart";
|
|
13
15
|
export { AreaChart } from "./xy/AreaChart";
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
* Validates chart data and accessors at render time.
|
|
3
3
|
* Returns null if data is valid, or an error message string if not.
|
|
4
4
|
*
|
|
5
|
+
* Error messages are designed for AI-agent consumption:
|
|
6
|
+
* they include the problem, the available fields, AND the suggested fix.
|
|
7
|
+
*
|
|
5
8
|
* Samples first, last, and a middle element to catch common mistakes
|
|
6
9
|
* (wrong field names, missing data) without iterating the entire dataset.
|
|
7
10
|
*/
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
interface SafeRenderProps {
|
|
3
|
+
componentName: string;
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Wraps a chart's rendered output with an error boundary.
|
|
10
|
+
* If the chart throws during render, shows a visible error box
|
|
11
|
+
* with the component name and error message instead of crashing the page.
|
|
12
|
+
*/
|
|
13
|
+
export declare function SafeRender({ componentName, width, height, children }: SafeRenderProps): React.JSX.Element;
|
|
14
|
+
/** Warn if a string accessor isn't found in the first data element */
|
|
15
|
+
export declare function warnMissingField(componentName: string, data: any[] | undefined, accessorName: string, accessorValue: any): void;
|
|
16
|
+
/** Warn if data looks like the wrong shape for this chart type */
|
|
17
|
+
export declare function warnDataShape(componentName: string, data: any[] | undefined, expectedKeys: string[], hint: string): void;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { StreamXYFrameProps } from "../../stream/types";
|
|
3
|
+
import type { BaseChartProps, AxisConfig, ChartAccessor } from "../shared/types";
|
|
4
|
+
import { type TooltipProp } from "../../Tooltip/Tooltip";
|
|
5
|
+
/**
|
|
6
|
+
* ConnectedScatterplot component props
|
|
7
|
+
*/
|
|
8
|
+
export interface ConnectedScatterplotProps<TDatum extends Record<string, any> = Record<string, any>> extends BaseChartProps, AxisConfig {
|
|
9
|
+
/** Array of data points. Each point needs x and y properties. */
|
|
10
|
+
data: TDatum[];
|
|
11
|
+
/** Field name or function to access x values @default "x" */
|
|
12
|
+
xAccessor?: ChartAccessor<TDatum, number>;
|
|
13
|
+
/** Field name or function to access y values @default "y" */
|
|
14
|
+
yAccessor?: ChartAccessor<TDatum, number>;
|
|
15
|
+
/**
|
|
16
|
+
* Field name or function that determines point ordering.
|
|
17
|
+
* Data is sorted by this value (ascending) before connecting.
|
|
18
|
+
* Supports numbers and Dates. Shown in tooltip. @default undefined (use data array order)
|
|
19
|
+
*/
|
|
20
|
+
orderAccessor?: string | ((d: TDatum) => number | Date);
|
|
21
|
+
/** Label for the ordering metric in tooltips @default "Order" or the accessor field name */
|
|
22
|
+
orderLabel?: string;
|
|
23
|
+
/** Point radius @default 4 */
|
|
24
|
+
pointRadius?: number;
|
|
25
|
+
/** Enable hover annotations @default true */
|
|
26
|
+
enableHover?: boolean;
|
|
27
|
+
/** Show grid lines @default false */
|
|
28
|
+
showGrid?: boolean;
|
|
29
|
+
/** Tooltip configuration */
|
|
30
|
+
tooltip?: TooltipProp;
|
|
31
|
+
/** Accessor for unique point IDs, used by point-anchored annotations */
|
|
32
|
+
pointIdAccessor?: ChartAccessor<TDatum, string>;
|
|
33
|
+
/** Annotation objects to render on the chart */
|
|
34
|
+
annotations?: Record<string, any>[];
|
|
35
|
+
/** Additional StreamXYFrame props for advanced customization */
|
|
36
|
+
frameProps?: Partial<Omit<StreamXYFrameProps, "chartType" | "data" | "size">>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* ConnectedScatterplot — points connected in sequence by lines.
|
|
40
|
+
*
|
|
41
|
+
* Points are colored using viridis from start (purple) to end (yellow).
|
|
42
|
+
* Lines match the color of their source point and have the same width
|
|
43
|
+
* as the point radius, so the 2×radius circle remains distinctive.
|
|
44
|
+
* When fewer than 100 points, a 50% transparent white halo is drawn
|
|
45
|
+
* under each connecting line for legibility.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```tsx
|
|
49
|
+
* <ConnectedScatterplot
|
|
50
|
+
* data={trajectory}
|
|
51
|
+
* xAccessor="gdp"
|
|
52
|
+
* yAccessor="lifeExpectancy"
|
|
53
|
+
* pointRadius={4}
|
|
54
|
+
* />
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare function ConnectedScatterplot<TDatum extends Record<string, any> = Record<string, any>>(props: ConnectedScatterplotProps<TDatum>): React.JSX.Element;
|
|
58
|
+
export declare namespace ConnectedScatterplot {
|
|
59
|
+
var displayName: string;
|
|
60
|
+
}
|