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/ai/cli.js
CHANGED
|
@@ -54,46 +54,59 @@ if (flag === "--doctor") {
|
|
|
54
54
|
process.exit(1)
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
// Load
|
|
57
|
+
// Load diagnoseConfig from dist (falls back to validateProps)
|
|
58
58
|
const distPath = path.join(pkgRoot, "dist", "semiotic-ai.min.js")
|
|
59
|
-
let validateProps
|
|
59
|
+
let diagnoseConfig, validateProps
|
|
60
60
|
try {
|
|
61
61
|
const mod = require(distPath)
|
|
62
|
+
diagnoseConfig = mod.diagnoseConfig
|
|
62
63
|
validateProps = mod.validateProps
|
|
63
64
|
} catch (e) {
|
|
64
65
|
console.error("Could not load semiotic/ai dist. Run 'npm run dist' first.")
|
|
65
66
|
process.exit(1)
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
if (!validateProps) {
|
|
69
|
-
console.error("validateProps not found in semiotic/ai exports.")
|
|
69
|
+
if (!diagnoseConfig && !validateProps) {
|
|
70
|
+
console.error("diagnoseConfig/validateProps not found in semiotic/ai exports.")
|
|
70
71
|
process.exit(1)
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
if (diagnoseConfig) {
|
|
75
|
+
// Use the full anti-pattern detector
|
|
76
|
+
const result = diagnoseConfig(component, props)
|
|
77
|
+
|
|
78
|
+
// Show data shape summary
|
|
77
79
|
if (props.data && Array.isArray(props.data) && props.data.length > 0) {
|
|
78
80
|
const sample = props.data[0]
|
|
79
81
|
console.log(` Data shape: ${props.data.length} items, keys: [${Object.keys(sample).join(", ")}]`)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
console.log(` ⚠
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (result.ok && result.diagnoses.length === 0) {
|
|
85
|
+
console.log(`✓ ${component}: configuration looks good.`)
|
|
86
|
+
} else if (result.ok) {
|
|
87
|
+
console.log(`✓ ${component}: configuration OK with warnings:`)
|
|
88
|
+
for (const d of result.diagnoses) {
|
|
89
|
+
console.log(` ⚠ [${d.code}] ${d.message}`)
|
|
90
|
+
if (d.fix) console.log(` Fix: ${d.fix}`)
|
|
88
91
|
}
|
|
89
|
-
|
|
90
|
-
|
|
92
|
+
} else {
|
|
93
|
+
console.log(`✗ ${component}: issues detected.`)
|
|
94
|
+
for (const d of result.diagnoses) {
|
|
95
|
+
const icon = d.severity === "error" ? "✗" : "⚠"
|
|
96
|
+
console.log(` ${icon} [${d.code}] ${d.message}`)
|
|
97
|
+
if (d.fix) console.log(` Fix: ${d.fix}`)
|
|
91
98
|
}
|
|
92
99
|
}
|
|
93
100
|
} else {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
101
|
+
// Fallback to validateProps only
|
|
102
|
+
const result = validateProps(component, props)
|
|
103
|
+
if (result.valid) {
|
|
104
|
+
console.log(`✓ ${component}: props are valid.`)
|
|
105
|
+
} else {
|
|
106
|
+
console.log(`✗ ${component}: validation failed.`)
|
|
107
|
+
for (const err of result.errors) {
|
|
108
|
+
console.log(` • ${err}`)
|
|
109
|
+
}
|
|
97
110
|
}
|
|
98
111
|
}
|
|
99
112
|
} catch (err) {
|
|
@@ -9,6 +9,7 @@ exports.COMPONENT_REGISTRY = {
|
|
|
9
9
|
Scatterplot: { component: ai_1.Scatterplot, category: "xy" },
|
|
10
10
|
BubbleChart: { component: ai_1.BubbleChart, category: "xy" },
|
|
11
11
|
Heatmap: { component: ai_1.Heatmap, category: "xy" },
|
|
12
|
+
ConnectedScatterplot: { component: ai_1.ConnectedScatterplot, category: "xy" },
|
|
12
13
|
BarChart: { component: ai_1.BarChart, category: "ordinal" },
|
|
13
14
|
StackedBarChart: { component: ai_1.StackedBarChart, category: "ordinal" },
|
|
14
15
|
GroupedBarChart: { component: ai_1.GroupedBarChart, category: "ordinal" },
|
|
@@ -23,4 +24,5 @@ exports.COMPONENT_REGISTRY = {
|
|
|
23
24
|
TreeDiagram: { component: ai_1.TreeDiagram, category: "network" },
|
|
24
25
|
Treemap: { component: ai_1.Treemap, category: "network" },
|
|
25
26
|
CirclePack: { component: ai_1.CirclePack, category: "network" },
|
|
27
|
+
OrbitDiagram: { component: ai_1.OrbitDiagram, category: "network" },
|
|
26
28
|
};
|
package/ai/dist/mcp-server.js
CHANGED
|
@@ -57,6 +57,7 @@ const fs = __importStar(require("fs"));
|
|
|
57
57
|
const path = __importStar(require("path"));
|
|
58
58
|
const renderHOCToSVG_1 = require("./renderHOCToSVG");
|
|
59
59
|
const componentRegistry_1 = require("./componentRegistry");
|
|
60
|
+
const ai_1 = require("semiotic/ai");
|
|
60
61
|
// Load schema.json for tool definitions
|
|
61
62
|
const schemaPath = path.resolve(__dirname, "../schema.json");
|
|
62
63
|
const schema = JSON.parse(fs.readFileSync(schemaPath, "utf-8"));
|
|
@@ -88,6 +89,59 @@ for (const toolDef of schema.tools) {
|
|
|
88
89
|
};
|
|
89
90
|
});
|
|
90
91
|
}
|
|
92
|
+
// ── Generic renderChart tool ─────────────────────────────────────────────
|
|
93
|
+
// Accepts { component, props } — closes the agent feedback loop by letting
|
|
94
|
+
// an LLM render any chart type in a single tool call.
|
|
95
|
+
server.tool("renderChart", "Render any Semiotic chart to static SVG. Pass { component: 'LineChart', props: { data: [...], ... } }. Returns SVG string or validation errors.", {}, async (args) => {
|
|
96
|
+
const component = args.component;
|
|
97
|
+
const props = (args.props || args);
|
|
98
|
+
if (!component) {
|
|
99
|
+
return {
|
|
100
|
+
content: [{ type: "text", text: "Missing 'component' field. Provide { component: 'LineChart', props: { ... } }." }],
|
|
101
|
+
isError: true,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const result = (0, renderHOCToSVG_1.renderHOCToSVG)(component, props);
|
|
105
|
+
if (result.error) {
|
|
106
|
+
return {
|
|
107
|
+
content: [{ type: "text", text: result.error }],
|
|
108
|
+
isError: true,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
content: [{ type: "text", text: result.svg }],
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
// ── diagnoseConfig tool ──────────────────────────────────────────────────
|
|
116
|
+
// Anti-pattern detector: checks for common failure modes and returns
|
|
117
|
+
// actionable fix instructions.
|
|
118
|
+
server.tool("diagnoseConfig", "Diagnose a Semiotic chart configuration for common problems (empty data, bad dimensions, missing accessors, wrong data shape, etc). Pass { component: 'LineChart', props: { ... } }. Returns structured diagnoses with fix instructions.", {}, async (args) => {
|
|
119
|
+
const component = args.component;
|
|
120
|
+
const props = (args.props || args);
|
|
121
|
+
if (!component) {
|
|
122
|
+
return {
|
|
123
|
+
content: [{ type: "text", text: "Missing 'component' field. Provide { component: 'LineChart', props: { ... } }." }],
|
|
124
|
+
isError: true,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const result = (0, ai_1.diagnoseConfig)(component, props);
|
|
128
|
+
if (result.ok) {
|
|
129
|
+
const warnings = result.diagnoses.filter(d => d.severity === "warning");
|
|
130
|
+
const msg = warnings.length > 0
|
|
131
|
+
? `Configuration looks good with ${warnings.length} warning(s):\n${warnings.map(w => `⚠ [${w.code}] ${w.message}\n Fix: ${w.fix}`).join("\n")}`
|
|
132
|
+
: `✓ Configuration looks good — no issues detected.`;
|
|
133
|
+
return { content: [{ type: "text", text: msg }] };
|
|
134
|
+
}
|
|
135
|
+
const lines = result.diagnoses.map(d => {
|
|
136
|
+
const icon = d.severity === "error" ? "✗" : "⚠";
|
|
137
|
+
const fixLine = d.fix ? `\n Fix: ${d.fix}` : "";
|
|
138
|
+
return `${icon} [${d.code}] ${d.message}${fixLine}`;
|
|
139
|
+
});
|
|
140
|
+
return {
|
|
141
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
142
|
+
isError: true,
|
|
143
|
+
};
|
|
144
|
+
});
|
|
91
145
|
// Start the server
|
|
92
146
|
async function main() {
|
|
93
147
|
const transport = new stdio_js_1.StdioServerTransport();
|
package/ai/examples.md
CHANGED
|
@@ -111,6 +111,151 @@ const data = [
|
|
|
111
111
|
|
|
112
112
|
Key props: `valueAccessor`, `colorScheme` ("blues"|"reds"|"greens"|"viridis"), `showValues`
|
|
113
113
|
|
|
114
|
+
### Heatmap with Gradient Legend
|
|
115
|
+
|
|
116
|
+
```jsx
|
|
117
|
+
import { Heatmap } from "semiotic/ai"
|
|
118
|
+
|
|
119
|
+
<Heatmap
|
|
120
|
+
data={data}
|
|
121
|
+
xAccessor="hour"
|
|
122
|
+
yAccessor="day"
|
|
123
|
+
valueAccessor="count"
|
|
124
|
+
colorScheme="viridis"
|
|
125
|
+
showLegend
|
|
126
|
+
legendPosition="right"
|
|
127
|
+
/>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Key props: `showLegend` enables gradient legend, `legendPosition` ("right"|"left"|"top"|"bottom")
|
|
131
|
+
|
|
132
|
+
### AreaChart
|
|
133
|
+
|
|
134
|
+
```jsx
|
|
135
|
+
import { AreaChart } from "semiotic/ai"
|
|
136
|
+
|
|
137
|
+
const data = [
|
|
138
|
+
{ month: 1, revenue: 4200 },
|
|
139
|
+
{ month: 2, revenue: 5800 },
|
|
140
|
+
{ month: 3, revenue: 5200 },
|
|
141
|
+
{ month: 4, revenue: 7100 },
|
|
142
|
+
{ month: 5, revenue: 6800 },
|
|
143
|
+
{ month: 6, revenue: 7500 }
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
<AreaChart
|
|
147
|
+
data={data}
|
|
148
|
+
xAccessor="month"
|
|
149
|
+
yAccessor="revenue"
|
|
150
|
+
gradientFill
|
|
151
|
+
xLabel="Month"
|
|
152
|
+
yLabel="Revenue ($)"
|
|
153
|
+
/>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Key props: `areaBy` (group into multiple areas), `y0Accessor` (band/ribbon), `gradientFill`, `areaOpacity` (0.7)
|
|
157
|
+
|
|
158
|
+
### AreaChart — Percentile Band with Main Line
|
|
159
|
+
|
|
160
|
+
**IMPORTANT**: `showLine` on AreaChart only draws the TOP edge of the area (the `yAccessor` line). To render a separate main line (e.g., p50 median), you must layer two charts.
|
|
161
|
+
|
|
162
|
+
```jsx
|
|
163
|
+
import { AreaChart, LineChart } from "semiotic/ai"
|
|
164
|
+
|
|
165
|
+
const data = [
|
|
166
|
+
{ x: 0, p5: 10, p50: 25, p95: 45 },
|
|
167
|
+
{ x: 1, p5: 12, p50: 28, p95: 50 },
|
|
168
|
+
{ x: 2, p5: 11, p50: 30, p95: 52 },
|
|
169
|
+
{ x: 3, p5: 14, p50: 32, p95: 55 },
|
|
170
|
+
{ x: 4, p5: 13, p50: 35, p95: 58 },
|
|
171
|
+
{ x: 5, p5: 15, p50: 37, p95: 62 },
|
|
172
|
+
]
|
|
173
|
+
|
|
174
|
+
// Band (p5–p95) + main line (p50): two separate charts layered
|
|
175
|
+
<div style={{ position: "relative" }}>
|
|
176
|
+
<AreaChart
|
|
177
|
+
data={data}
|
|
178
|
+
xAccessor="x"
|
|
179
|
+
yAccessor="p95"
|
|
180
|
+
y0Accessor="p5"
|
|
181
|
+
showLine={false}
|
|
182
|
+
areaOpacity={0.3}
|
|
183
|
+
gradientFill
|
|
184
|
+
width={600}
|
|
185
|
+
height={400}
|
|
186
|
+
/>
|
|
187
|
+
<div style={{ position: "absolute", top: 0, left: 0 }}>
|
|
188
|
+
<LineChart
|
|
189
|
+
data={data}
|
|
190
|
+
xAccessor="x"
|
|
191
|
+
yAccessor="p50"
|
|
192
|
+
lineWidth={2}
|
|
193
|
+
width={600}
|
|
194
|
+
height={400}
|
|
195
|
+
/>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Key props: `y0Accessor` defines band bottom, `yAccessor` defines band top, `showLine={false}` hides the top edge stroke. Layer a `LineChart` on top for the main metric.
|
|
201
|
+
|
|
202
|
+
### StackedAreaChart
|
|
203
|
+
|
|
204
|
+
```jsx
|
|
205
|
+
import { StackedAreaChart } from "semiotic/ai"
|
|
206
|
+
|
|
207
|
+
// IMPORTANT: Use a flat array with an areaBy field for grouping.
|
|
208
|
+
// Do NOT use lineBy or lineDataAccessor — those are LineChart props.
|
|
209
|
+
const data = [
|
|
210
|
+
{ month: 1, value: 20, category: "Product" },
|
|
211
|
+
{ month: 2, value: 25, category: "Product" },
|
|
212
|
+
{ month: 3, value: 30, category: "Product" },
|
|
213
|
+
{ month: 1, value: 15, category: "Service" },
|
|
214
|
+
{ month: 2, value: 12, category: "Service" },
|
|
215
|
+
{ month: 3, value: 18, category: "Service" },
|
|
216
|
+
{ month: 1, value: 8, category: "Consulting" },
|
|
217
|
+
{ month: 2, value: 10, category: "Consulting" },
|
|
218
|
+
{ month: 3, value: 7, category: "Consulting" }
|
|
219
|
+
]
|
|
220
|
+
|
|
221
|
+
<StackedAreaChart
|
|
222
|
+
data={data}
|
|
223
|
+
xAccessor="month"
|
|
224
|
+
yAccessor="value"
|
|
225
|
+
areaBy="category"
|
|
226
|
+
colorBy="category"
|
|
227
|
+
showLegend
|
|
228
|
+
/>
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Key props: **`areaBy`** (required — groups flat data into stacked areas), `colorBy`, `normalize` (100% stacked). Data must be a flat array, not pre-grouped objects.
|
|
232
|
+
|
|
233
|
+
### ConnectedScatterplot
|
|
234
|
+
|
|
235
|
+
```jsx
|
|
236
|
+
import { ConnectedScatterplot } from "semiotic/ai"
|
|
237
|
+
|
|
238
|
+
const data = [
|
|
239
|
+
{ year: 2018, unemployment: 3.9, inflation: 2.4 },
|
|
240
|
+
{ year: 2019, unemployment: 3.7, inflation: 1.8 },
|
|
241
|
+
{ year: 2020, unemployment: 8.1, inflation: 1.2 },
|
|
242
|
+
{ year: 2021, unemployment: 5.4, inflation: 4.7 },
|
|
243
|
+
{ year: 2022, unemployment: 3.6, inflation: 8.0 },
|
|
244
|
+
{ year: 2023, unemployment: 3.6, inflation: 4.1 }
|
|
245
|
+
]
|
|
246
|
+
|
|
247
|
+
<ConnectedScatterplot
|
|
248
|
+
data={data}
|
|
249
|
+
xAccessor="unemployment"
|
|
250
|
+
yAccessor="inflation"
|
|
251
|
+
orderAccessor="year"
|
|
252
|
+
xLabel="Unemployment Rate (%)"
|
|
253
|
+
yLabel="Inflation Rate (%)"
|
|
254
|
+
/>
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Key props: `orderAccessor` sequences points along the path, Viridis gradient from start→end
|
|
258
|
+
|
|
114
259
|
---
|
|
115
260
|
|
|
116
261
|
## Flat Array — Ordinal Charts
|
|
@@ -369,6 +514,55 @@ Key props: `valueAccessor` controls circle size, nested circles for hierarchy
|
|
|
369
514
|
|
|
370
515
|
---
|
|
371
516
|
|
|
517
|
+
### OrbitDiagram
|
|
518
|
+
|
|
519
|
+
```jsx
|
|
520
|
+
import { OrbitDiagram } from "semiotic/ai"
|
|
521
|
+
|
|
522
|
+
const pipeline = {
|
|
523
|
+
name: "ML Pipeline",
|
|
524
|
+
children: [
|
|
525
|
+
{
|
|
526
|
+
name: "Data Ingestion",
|
|
527
|
+
children: [
|
|
528
|
+
{ name: "API Feed" },
|
|
529
|
+
{ name: "CSV Upload" },
|
|
530
|
+
{ name: "DB Connector" }
|
|
531
|
+
]
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
name: "Processing",
|
|
535
|
+
children: [
|
|
536
|
+
{ name: "Clean" },
|
|
537
|
+
{ name: "Feature Eng" },
|
|
538
|
+
{ name: "Normalize" }
|
|
539
|
+
]
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
name: "Model",
|
|
543
|
+
children: [
|
|
544
|
+
{ name: "Train" },
|
|
545
|
+
{ name: "Evaluate" },
|
|
546
|
+
{ name: "Deploy" }
|
|
547
|
+
]
|
|
548
|
+
}
|
|
549
|
+
]
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
<OrbitDiagram
|
|
553
|
+
data={pipeline}
|
|
554
|
+
childrenAccessor="children"
|
|
555
|
+
nodeIdAccessor="name"
|
|
556
|
+
orbitMode="solar"
|
|
557
|
+
showLabels
|
|
558
|
+
colorByDepth
|
|
559
|
+
/>
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
Key props: `orbitMode` ("flat"|"solar"|"atomic"|number[]), `speed`, `animated`, `showRings`
|
|
563
|
+
|
|
564
|
+
---
|
|
565
|
+
|
|
372
566
|
## Network Data — Nodes + Edges Arrays
|
|
373
567
|
|
|
374
568
|
### ForceDirectedGraph
|
|
@@ -377,11 +571,11 @@ Key props: `valueAccessor` controls circle size, nested circles for hierarchy
|
|
|
377
571
|
import { ForceDirectedGraph } from "semiotic/ai"
|
|
378
572
|
|
|
379
573
|
const nodes = [
|
|
380
|
-
{ id: "Alice", team: "Engineering" },
|
|
381
|
-
{ id: "Bob", team: "Engineering" },
|
|
382
|
-
{ id: "Carol", team: "Design" },
|
|
383
|
-
{ id: "Dave", team: "Design" },
|
|
384
|
-
{ id: "Eve", team: "Product" }
|
|
574
|
+
{ id: "Alice", team: "Engineering", influence: 10 },
|
|
575
|
+
{ id: "Bob", team: "Engineering", influence: 6 },
|
|
576
|
+
{ id: "Carol", team: "Design", influence: 8 },
|
|
577
|
+
{ id: "Dave", team: "Design", influence: 4 },
|
|
578
|
+
{ id: "Eve", team: "Product", influence: 12 }
|
|
385
579
|
]
|
|
386
580
|
|
|
387
581
|
const edges = [
|
|
@@ -397,12 +591,51 @@ const edges = [
|
|
|
397
591
|
nodes={nodes}
|
|
398
592
|
edges={edges}
|
|
399
593
|
colorBy="team"
|
|
400
|
-
nodeSize=
|
|
594
|
+
nodeSize="influence"
|
|
595
|
+
nodeSizeRange={[5, 25]}
|
|
401
596
|
showLabels
|
|
597
|
+
showLegend
|
|
598
|
+
edgeOpacity={0.4}
|
|
599
|
+
tooltip={(d) => <div><strong>{d.data.id}</strong><br/>Team: {d.data.team}</div>}
|
|
402
600
|
/>
|
|
403
601
|
```
|
|
404
602
|
|
|
405
|
-
Key props: **`nodes`** and **`edges`** (both required), `nodeIDAccessor
|
|
603
|
+
Key props: **`nodes`** and **`edges`** (both required), `nodeIDAccessor` (default "id"), `sourceAccessor` (default "source"), `targetAccessor` (default "target"), `colorBy`, `nodeSize` (number, string field name, or function), `nodeSizeRange`, `showLabels`, `showLegend`, `tooltip`
|
|
604
|
+
|
|
605
|
+
**Note**: Always use `ForceDirectedGraph` (not `StreamNetworkFrame`) unless you need sophisticated control it doesn't expose. `StreamNetworkFrame` is a low-level escape hatch whose callbacks receive internal `RealtimeNode` wrappers — access your data via `.data` (e.g., `nodeStyle={(d) => ({ fill: d.data?.color })}`). HOC components like `ForceDirectedGraph` handle this automatically.
|
|
606
|
+
|
|
607
|
+
### ForceDirectedGraph with custom click/hover
|
|
608
|
+
|
|
609
|
+
```jsx
|
|
610
|
+
import { useState } from "react"
|
|
611
|
+
import { ForceDirectedGraph } from "semiotic/ai"
|
|
612
|
+
|
|
613
|
+
const [selected, setSelected] = useState(null)
|
|
614
|
+
|
|
615
|
+
<ForceDirectedGraph
|
|
616
|
+
nodes={nodes}
|
|
617
|
+
edges={edges}
|
|
618
|
+
colorBy="team"
|
|
619
|
+
nodeSize="influence"
|
|
620
|
+
nodeSizeRange={[5, 25]}
|
|
621
|
+
showLabels
|
|
622
|
+
width={800}
|
|
623
|
+
height={600}
|
|
624
|
+
frameProps={{
|
|
625
|
+
customClickBehavior: (d) => {
|
|
626
|
+
// d is { type: "node"|"edge", data: <your raw node/edge>, x, y } or null
|
|
627
|
+
if (d?.type === "node") setSelected(d.data)
|
|
628
|
+
},
|
|
629
|
+
customHoverBehavior: (d) => {
|
|
630
|
+
// same shape as click — d.data is your original object
|
|
631
|
+
if (d?.type === "node") console.log("Hovering:", d.data.id)
|
|
632
|
+
},
|
|
633
|
+
background: "#1a1a2e",
|
|
634
|
+
}}
|
|
635
|
+
/>
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
Key props: `frameProps` passes through to the underlying `StreamNetworkFrame` for advanced control (custom click/hover, background, annotations). Callback `d.data` is always your original node/edge object.
|
|
406
639
|
|
|
407
640
|
### SankeyDiagram
|
|
408
641
|
|
|
@@ -456,7 +689,98 @@ Key props: **`edges`** (required), shows bidirectional relationships in a circle
|
|
|
456
689
|
|
|
457
690
|
## Realtime — Push API via Ref
|
|
458
691
|
|
|
459
|
-
###
|
|
692
|
+
### RealtimeLineChart
|
|
693
|
+
|
|
694
|
+
```jsx
|
|
695
|
+
import { useRef, useEffect } from "react"
|
|
696
|
+
import { RealtimeLineChart } from "semiotic/ai"
|
|
697
|
+
|
|
698
|
+
const chartRef = useRef()
|
|
699
|
+
|
|
700
|
+
// Push data — MUST include a time field
|
|
701
|
+
useEffect(() => {
|
|
702
|
+
const interval = setInterval(() => {
|
|
703
|
+
chartRef.current?.push({ time: Date.now(), value: Math.random() * 100 })
|
|
704
|
+
}, 500)
|
|
705
|
+
return () => clearInterval(interval)
|
|
706
|
+
}, [])
|
|
707
|
+
|
|
708
|
+
<RealtimeLineChart
|
|
709
|
+
ref={chartRef}
|
|
710
|
+
size={[600, 300]}
|
|
711
|
+
timeAccessor="time"
|
|
712
|
+
valueAccessor="value"
|
|
713
|
+
windowSize={120}
|
|
714
|
+
stroke="#76b7b2"
|
|
715
|
+
/>
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
Key props: `timeAccessor`, `valueAccessor`, `windowSize`, `stroke`, `strokeWidth`
|
|
719
|
+
|
|
720
|
+
### RealtimeHistogram
|
|
721
|
+
|
|
722
|
+
```jsx
|
|
723
|
+
import { useRef, useEffect } from "react"
|
|
724
|
+
import { RealtimeHistogram } from "semiotic/ai"
|
|
725
|
+
|
|
726
|
+
const chartRef = useRef()
|
|
727
|
+
|
|
728
|
+
// IMPORTANT: Include time field even though this shows a distribution — it's used for windowing
|
|
729
|
+
useEffect(() => {
|
|
730
|
+
const interval = setInterval(() => {
|
|
731
|
+
chartRef.current?.push({
|
|
732
|
+
time: Date.now(),
|
|
733
|
+
value: Math.random() * 1000
|
|
734
|
+
})
|
|
735
|
+
}, 200)
|
|
736
|
+
return () => clearInterval(interval)
|
|
737
|
+
}, [])
|
|
738
|
+
|
|
739
|
+
<RealtimeHistogram
|
|
740
|
+
ref={chartRef}
|
|
741
|
+
size={[400, 250]}
|
|
742
|
+
timeAccessor="time"
|
|
743
|
+
valueAccessor="value"
|
|
744
|
+
binSize={100}
|
|
745
|
+
/>
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
Key props: **`binSize`** (required), `timeAccessor` ("time"), `valueAccessor` ("value"). Without a time field in your data, the chart renders blank.
|
|
749
|
+
|
|
750
|
+
### RealtimeHeatmap
|
|
751
|
+
|
|
752
|
+
```jsx
|
|
753
|
+
import { useRef, useEffect } from "react"
|
|
754
|
+
import { RealtimeHeatmap } from "semiotic/ai"
|
|
755
|
+
|
|
756
|
+
const chartRef = useRef()
|
|
757
|
+
|
|
758
|
+
// IMPORTANT: Data must have fields matching timeAccessor and valueAccessor (defaults: "time" and "value")
|
|
759
|
+
useEffect(() => {
|
|
760
|
+
const interval = setInterval(() => {
|
|
761
|
+
chartRef.current?.push({
|
|
762
|
+
time: Date.now(),
|
|
763
|
+
value: Math.random() * 500
|
|
764
|
+
})
|
|
765
|
+
}, 100)
|
|
766
|
+
return () => clearInterval(interval)
|
|
767
|
+
}, [])
|
|
768
|
+
|
|
769
|
+
<RealtimeHeatmap
|
|
770
|
+
ref={chartRef}
|
|
771
|
+
size={[400, 250]}
|
|
772
|
+
timeAccessor="time"
|
|
773
|
+
valueAccessor="value"
|
|
774
|
+
heatmapXBins={30}
|
|
775
|
+
heatmapYBins={10}
|
|
776
|
+
/>
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
Key props: `timeAccessor` ("time"), `valueAccessor` ("value"), `heatmapXBins`, `heatmapYBins`. Both accessors must match your data fields or the chart renders blank.
|
|
780
|
+
|
|
781
|
+
### Streaming Sankey with Particles (StreamNetworkFrame)
|
|
782
|
+
|
|
783
|
+
Use `StreamNetworkFrame` only when you need low-level control that HOC charts don't expose. For most cases, use `ForceDirectedGraph`, `SankeyDiagram`, or `ChordDiagram` instead.
|
|
460
784
|
|
|
461
785
|
```jsx
|
|
462
786
|
import { useRef, useEffect } from "react"
|
|
@@ -464,18 +788,18 @@ import { StreamNetworkFrame } from "semiotic"
|
|
|
464
788
|
|
|
465
789
|
const chartRef = useRef()
|
|
466
790
|
|
|
467
|
-
// Push edges at any frequency
|
|
468
791
|
useEffect(() => {
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
792
|
+
// Push individual edges — NOT a full snapshot.
|
|
793
|
+
// Each push adds one edge event to the streaming sankey.
|
|
794
|
+
chartRef.current.push({ source: "Salary", target: "Budget", value: 5000 })
|
|
795
|
+
chartRef.current.push({ source: "Freelance", target: "Budget", value: 1500 })
|
|
796
|
+
|
|
797
|
+
// Or batch multiple edges at once:
|
|
798
|
+
chartRef.current.pushMany([
|
|
472
799
|
{ source: "Budget", target: "Rent", value: 2000 },
|
|
473
800
|
{ source: "Budget", target: "Food", value: 800 },
|
|
474
|
-
{ source: "Budget", target: "Savings", value: 1500 }
|
|
475
|
-
]
|
|
476
|
-
for (const edge of edges) {
|
|
477
|
-
chartRef.current.push(edge)
|
|
478
|
-
}
|
|
801
|
+
{ source: "Budget", target: "Savings", value: 1500 },
|
|
802
|
+
])
|
|
479
803
|
}, [])
|
|
480
804
|
|
|
481
805
|
<StreamNetworkFrame
|
|
@@ -483,7 +807,98 @@ useEffect(() => {
|
|
|
483
807
|
chartType="sankey"
|
|
484
808
|
size={[800, 400]}
|
|
485
809
|
edgeOpacity={0.4}
|
|
810
|
+
showParticles={true}
|
|
811
|
+
particleStyle={{
|
|
812
|
+
radius: 2,
|
|
813
|
+
opacity: 0.8,
|
|
814
|
+
speedMultiplier: 1.5,
|
|
815
|
+
maxPerEdge: 4,
|
|
816
|
+
colorBy: "source"
|
|
817
|
+
}}
|
|
818
|
+
/>
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
Key props: `chartType="sankey"`, `showParticles` (boolean — enables animated particles flowing along edges), `particleStyle` (`{ radius, opacity, speedMultiplier, maxPerEdge, colorBy }`), push via ref (`push` for single edge, `pushMany` for batch, `clear` to reset)
|
|
822
|
+
|
|
823
|
+
### Streaming any chart type via Stream Frames
|
|
824
|
+
|
|
825
|
+
The Realtime* HOCs are convenience wrappers. For streaming versions of scatter, stacked area, bar, or any other chart, use the corresponding Stream Frame with `runtimeMode="streaming"`:
|
|
826
|
+
|
|
827
|
+
```jsx
|
|
828
|
+
import { useRef } from "react"
|
|
829
|
+
import { StreamXYFrame } from "semiotic/xy"
|
|
830
|
+
|
|
831
|
+
const chartRef = useRef()
|
|
832
|
+
|
|
833
|
+
// Push data via ref — same push API as Realtime* HOCs
|
|
834
|
+
chartRef.current?.push({ time: Date.now(), size: 42, category: "A" })
|
|
835
|
+
|
|
836
|
+
<StreamXYFrame
|
|
837
|
+
ref={chartRef}
|
|
838
|
+
chartType="scatter"
|
|
839
|
+
runtimeMode="streaming"
|
|
840
|
+
xAccessor="time"
|
|
841
|
+
yAccessor="size"
|
|
842
|
+
colorAccessor="category"
|
|
843
|
+
size={[600, 300]}
|
|
844
|
+
margin={{ top: 10, bottom: 30, left: 40, right: 10 }}
|
|
486
845
|
/>
|
|
487
846
|
```
|
|
488
847
|
|
|
489
|
-
|
|
848
|
+
Available Stream Frames: `StreamXYFrame` (line, area, stackedArea, scatter), `StreamOrdinalFrame` (bar, grouped bar), `StreamNetworkFrame` (sankey, force, chord)
|
|
849
|
+
|
|
850
|
+
---
|
|
851
|
+
|
|
852
|
+
## Server-Side Rendering
|
|
853
|
+
|
|
854
|
+
### renderToStaticSVG (Node.js)
|
|
855
|
+
|
|
856
|
+
```ts
|
|
857
|
+
// IMPORTANT: frameType is "xy" | "ordinal" | "network" — NOT a component name like "BarChart"
|
|
858
|
+
import { renderOrdinalToStaticSVG } from "semiotic/server"
|
|
859
|
+
|
|
860
|
+
const data = [
|
|
861
|
+
{ category: "Q1", value: 120 },
|
|
862
|
+
{ category: "Q2", value: 148 },
|
|
863
|
+
{ category: "Q3", value: 135 },
|
|
864
|
+
{ category: "Q4", value: 162 },
|
|
865
|
+
]
|
|
866
|
+
|
|
867
|
+
const svg: string = renderOrdinalToStaticSVG({
|
|
868
|
+
data,
|
|
869
|
+
categoryAccessor: "category",
|
|
870
|
+
valueAccessor: "value",
|
|
871
|
+
width: 600,
|
|
872
|
+
height: 400,
|
|
873
|
+
})
|
|
874
|
+
|
|
875
|
+
// Or use the generic function:
|
|
876
|
+
import { renderToStaticSVG } from "semiotic/server"
|
|
877
|
+
const svg2 = renderToStaticSVG("ordinal", { data, categoryAccessor: "category", valueAccessor: "value" })
|
|
878
|
+
```
|
|
879
|
+
|
|
880
|
+
Key: `renderToStaticSVG(frameType, props)` where frameType is `"xy"` | `"ordinal"` | `"network"`. Type-specific shortcuts: `renderXYToStaticSVG`, `renderOrdinalToStaticSVG`, `renderNetworkToStaticSVG`.
|
|
881
|
+
|
|
882
|
+
---
|
|
883
|
+
|
|
884
|
+
## Export
|
|
885
|
+
|
|
886
|
+
### exportChart (browser)
|
|
887
|
+
|
|
888
|
+
```jsx
|
|
889
|
+
import { useRef } from "react"
|
|
890
|
+
import { Scatterplot } from "semiotic/ai"
|
|
891
|
+
import { exportChart } from "semiotic"
|
|
892
|
+
|
|
893
|
+
const containerRef = useRef<HTMLDivElement>(null)
|
|
894
|
+
|
|
895
|
+
// Pass the WRAPPER DIV, not the SVG element — exportChart finds canvas+SVG internally
|
|
896
|
+
<div ref={containerRef}>
|
|
897
|
+
<Scatterplot data={data} xAccessor="x" yAccessor="y" width={600} height={400} />
|
|
898
|
+
</div>
|
|
899
|
+
<button onClick={() => exportChart(containerRef.current!, { format: "png" })}>
|
|
900
|
+
Download PNG
|
|
901
|
+
</button>
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
Key: `exportChart(wrapperDiv, { format, filename, scale, background })`. It queries the wrapper for canvas and SVG elements internally. Default format: PNG with 2x scale.
|