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.
Files changed (219) hide show
  1. package/CLAUDE.md +242 -29
  2. package/README.md +101 -66
  3. package/ai/cli.js +34 -21
  4. package/ai/dist/componentRegistry.js +2 -0
  5. package/ai/dist/mcp-server.js +54 -0
  6. package/ai/examples.md +433 -18
  7. package/ai/schema.json +134 -1
  8. package/ai/system-prompt.md +51 -10
  9. package/dist/{ChartGrid.d.ts → components/ChartGrid.d.ts} +9 -7
  10. package/dist/components/Legend.d.ts +9 -0
  11. package/dist/{LinkedCharts.d.ts → components/LinkedCharts.d.ts} +34 -1
  12. package/dist/{Tooltip → components/Tooltip}/Tooltip.d.ts +9 -2
  13. package/dist/components/charts/geo/ChoroplethMap.d.ts +53 -0
  14. package/dist/components/charts/geo/DistanceCartogram.d.ts +90 -0
  15. package/dist/components/charts/geo/FlowMap.d.ts +83 -0
  16. package/dist/components/charts/geo/ProportionalSymbolMap.d.ts +67 -0
  17. package/dist/components/charts/geo/index.d.ts +8 -0
  18. package/dist/{charts → components/charts}/index.d.ts +4 -0
  19. package/dist/{charts → components/charts}/network/ChordDiagram.d.ts +8 -5
  20. package/dist/{charts → components/charts}/network/CirclePack.d.ts +4 -2
  21. package/dist/{charts → components/charts}/network/ForceDirectedGraph.d.ts +10 -6
  22. package/dist/components/charts/network/OrbitDiagram.d.ts +79 -0
  23. package/dist/{charts → components/charts}/network/SankeyDiagram.d.ts +8 -5
  24. package/dist/{charts → components/charts}/network/TreeDiagram.d.ts +4 -2
  25. package/dist/{charts → components/charts}/network/Treemap.d.ts +4 -2
  26. package/dist/{charts → components/charts}/ordinal/BarChart.d.ts +9 -5
  27. package/dist/{charts → components/charts}/ordinal/BoxPlot.d.ts +9 -5
  28. package/dist/{charts → components/charts}/ordinal/DonutChart.d.ts +9 -5
  29. package/dist/{charts → components/charts}/ordinal/DotPlot.d.ts +9 -5
  30. package/dist/{charts → components/charts}/ordinal/GroupedBarChart.d.ts +9 -5
  31. package/dist/{charts → components/charts}/ordinal/Histogram.d.ts +8 -5
  32. package/dist/{charts → components/charts}/ordinal/PieChart.d.ts +9 -5
  33. package/dist/{charts → components/charts}/ordinal/RidgelinePlot.d.ts +2 -0
  34. package/dist/{charts → components/charts}/ordinal/StackedBarChart.d.ts +9 -5
  35. package/dist/{charts → components/charts}/ordinal/SwarmPlot.d.ts +9 -5
  36. package/dist/{charts → components/charts}/ordinal/ViolinPlot.d.ts +8 -5
  37. package/dist/{charts → components/charts}/realtime/RealtimeHeatmap.d.ts +24 -6
  38. package/dist/{charts → components/charts}/realtime/RealtimeHistogram.d.ts +28 -7
  39. package/dist/{charts → components/charts}/realtime/RealtimeLineChart.d.ts +23 -5
  40. package/dist/{charts → components/charts}/realtime/RealtimeSwarmChart.d.ts +24 -6
  41. package/dist/{charts → components/charts}/realtime/RealtimeWaterfallChart.d.ts +23 -5
  42. package/dist/{charts → components/charts}/shared/colorUtils.d.ts +5 -0
  43. package/dist/components/charts/shared/diagnoseConfig.d.ts +23 -0
  44. package/dist/{charts → components/charts}/shared/hooks.d.ts +36 -2
  45. package/dist/{charts → components/charts}/shared/legendUtils.d.ts +2 -3
  46. package/dist/{charts → components/charts}/shared/statisticalOverlays.d.ts +1 -2
  47. package/dist/components/charts/shared/statisticalOverlaysLazy.d.ts +10 -0
  48. package/dist/components/charts/shared/stringDistance.d.ts +11 -0
  49. package/dist/{charts → components/charts}/shared/tooltipUtils.d.ts +2 -2
  50. package/dist/{charts → components/charts}/shared/types.d.ts +16 -4
  51. package/dist/components/charts/shared/useChartSetup.d.ts +112 -0
  52. package/dist/components/charts/shared/useStreamingLegend.d.ts +65 -0
  53. package/dist/{charts → components/charts}/shared/withChartWrapper.d.ts +10 -0
  54. package/dist/{charts → components/charts}/xy/AreaChart.d.ts +18 -5
  55. package/dist/{charts → components/charts}/xy/BubbleChart.d.ts +18 -5
  56. package/dist/{charts → components/charts}/xy/ConnectedScatterplot.d.ts +10 -6
  57. package/dist/{charts → components/charts}/xy/Heatmap.d.ts +24 -5
  58. package/dist/{charts → components/charts}/xy/LineChart.d.ts +47 -5
  59. package/dist/{charts → components/charts}/xy/MinimapChart.d.ts +3 -0
  60. package/dist/components/charts/xy/QuadrantChart.d.ts +120 -0
  61. package/dist/{charts → components/charts}/xy/Scatterplot.d.ts +11 -5
  62. package/dist/{charts → components/charts}/xy/StackedAreaChart.d.ts +18 -5
  63. package/dist/{export → components/export}/exportChart.d.ts +6 -1
  64. package/dist/components/geo/mergeData.d.ts +18 -0
  65. package/dist/components/geo/referenceGeography.d.ts +10 -0
  66. package/dist/components/geo/useReferenceAreas.d.ts +13 -0
  67. package/dist/{realtime → components/realtime}/RingBuffer.d.ts +1 -0
  68. package/dist/{realtime → components/realtime}/types.d.ts +17 -0
  69. package/dist/components/semiotic-ai.d.ts +61 -0
  70. package/dist/components/semiotic-data.d.ts +8 -0
  71. package/dist/components/semiotic-geo.d.ts +16 -0
  72. package/dist/components/semiotic-network.d.ts +14 -0
  73. package/dist/components/semiotic-ordinal.d.ts +18 -0
  74. package/dist/components/semiotic-realtime.d.ts +22 -0
  75. package/dist/components/semiotic-server.d.ts +1 -0
  76. package/dist/components/semiotic-xy.d.ts +17 -0
  77. package/dist/components/semiotic.d.ts +57 -0
  78. package/dist/{server → components/server}/renderToStaticSVG.d.ts +11 -2
  79. package/dist/components/stream/AccessibleDataTable.d.ts +50 -0
  80. package/dist/{stream → components/stream}/CanvasHitTester.d.ts +8 -2
  81. package/dist/components/stream/DataSourceAdapter.d.ts +64 -0
  82. package/dist/components/stream/GeoCanvasHitTester.d.ts +19 -0
  83. package/dist/components/stream/GeoParticlePool.d.ts +46 -0
  84. package/dist/components/stream/GeoPipelineStore.d.ts +81 -0
  85. package/dist/components/stream/GeoTileRenderer.d.ts +31 -0
  86. package/dist/{stream → components/stream}/NetworkPipelineStore.d.ts +16 -4
  87. package/dist/{stream → components/stream}/NetworkSVGOverlay.d.ts +24 -1
  88. package/dist/{stream → components/stream}/OrdinalPipelineStore.d.ts +8 -4
  89. package/dist/{stream → components/stream}/OrdinalSVGOverlay.d.ts +31 -1
  90. package/dist/{stream → components/stream}/PipelineStore.d.ts +64 -5
  91. package/dist/components/stream/SVGOverlay.d.ts +98 -0
  92. package/dist/{stream → components/stream}/SceneGraph.d.ts +7 -3
  93. package/dist/components/stream/SceneToSVG.d.ts +22 -0
  94. package/dist/components/stream/StreamGeoFrame.d.ts +4 -0
  95. package/dist/{stream → components/stream}/accessorUtils.d.ts +1 -0
  96. package/dist/components/stream/canvasSetup.d.ts +26 -0
  97. package/dist/components/stream/geoTypes.d.ts +186 -0
  98. package/dist/components/stream/hitTestUtils.d.ts +23 -0
  99. package/dist/components/stream/layouts/forceLayoutPlugin.d.ts +2 -0
  100. package/dist/{stream → components/stream}/layouts/index.d.ts +2 -1
  101. package/dist/components/stream/layouts/orbitLayoutPlugin.d.ts +2 -0
  102. package/dist/components/stream/legendRenderer.d.ts +33 -0
  103. package/dist/{stream → components/stream}/networkTypes.d.ts +59 -3
  104. package/dist/{stream → components/stream}/ordinalTypes.d.ts +26 -10
  105. package/dist/components/stream/pipelineTransitionUtils.d.ts +42 -0
  106. package/dist/components/stream/renderers/areaCanvasRenderer.d.ts +2 -0
  107. package/dist/components/stream/renderers/geoCanvasRenderer.d.ts +9 -0
  108. package/dist/{stream → components/stream}/renderers/heatmapCanvasRenderer.d.ts +2 -1
  109. package/dist/{stream → components/stream}/renderers/lineCanvasRenderer.d.ts +1 -0
  110. package/dist/components/stream/renderers/renderPulse.d.ts +50 -0
  111. package/dist/{stream → components/stream}/types.d.ts +89 -3
  112. package/dist/components/stream/useStalenessCheck.d.ts +16 -0
  113. package/dist/components/types/legendTypes.d.ts +49 -0
  114. package/dist/geo.min.js +1 -0
  115. package/dist/geo.module.min.js +1 -0
  116. package/dist/network.min.js +1 -1
  117. package/dist/network.module.min.js +1 -1
  118. package/dist/ordinal.min.js +1 -1
  119. package/dist/ordinal.module.min.js +1 -1
  120. package/dist/realtime.min.js +1 -1
  121. package/dist/realtime.module.min.js +1 -1
  122. package/dist/semiotic-ai.d.ts +3 -0
  123. package/dist/semiotic-ai.min.js +1 -1
  124. package/dist/semiotic-ai.module.min.js +1 -1
  125. package/dist/semiotic-data.d.ts +1 -0
  126. package/dist/semiotic-data.min.js +1 -1
  127. package/dist/semiotic-data.module.min.js +1 -1
  128. package/dist/semiotic-geo.d.ts +16 -0
  129. package/dist/semiotic-network.d.ts +1 -0
  130. package/dist/semiotic-ordinal.d.ts +1 -0
  131. package/dist/semiotic-server.d.ts +1 -1
  132. package/dist/semiotic-xy.d.ts +1 -0
  133. package/dist/semiotic.d.ts +4 -4
  134. package/dist/semiotic.min.js +1 -1
  135. package/dist/semiotic.module.min.js +1 -1
  136. package/dist/server.min.js +1 -1
  137. package/dist/server.module.min.js +1 -1
  138. package/dist/test-utils/canvasMock.d.ts +23 -0
  139. package/dist/test-utils/frameMock.d.ts +78 -0
  140. package/dist/xy.min.js +1 -1
  141. package/dist/xy.module.min.js +1 -1
  142. package/package.json +34 -20
  143. package/dist/Legend.d.ts +0 -3
  144. package/dist/stream/DataSourceAdapter.d.ts +0 -35
  145. package/dist/stream/SVGOverlay.d.ts +0 -56
  146. package/dist/stream/layouts/forceLayoutPlugin.d.ts +0 -9
  147. package/dist/stream/renderers/areaCanvasRenderer.d.ts +0 -7
  148. package/dist/types/legendTypes.d.ts +0 -20
  149. /package/dist/{Annotation.d.ts → components/Annotation.d.ts} +0 -0
  150. /package/dist/{CategoryColors.d.ts → components/CategoryColors.d.ts} +0 -0
  151. /package/dist/{ChartContainer.d.ts → components/ChartContainer.d.ts} +0 -0
  152. /package/dist/{ChartErrorBoundary.d.ts → components/ChartErrorBoundary.d.ts} +0 -0
  153. /package/dist/{ContextLayout.d.ts → components/ContextLayout.d.ts} +0 -0
  154. /package/dist/{DetailsPanel.d.ts → components/DetailsPanel.d.ts} +0 -0
  155. /package/dist/{ThemeProvider.d.ts → components/ThemeProvider.d.ts} +0 -0
  156. /package/dist/{charts → components/charts}/shared/ChartError.d.ts +0 -0
  157. /package/dist/{charts → components/charts}/shared/annotationRules.d.ts +0 -0
  158. /package/dist/{charts → components/charts}/shared/formatUtils.d.ts +0 -0
  159. /package/dist/{charts → components/charts}/shared/loess.d.ts +0 -0
  160. /package/dist/{charts → components/charts}/shared/networkUtils.d.ts +0 -0
  161. /package/dist/{charts → components/charts}/shared/selectionUtils.d.ts +0 -0
  162. /package/dist/{charts → components/charts}/shared/validateChartData.d.ts +0 -0
  163. /package/dist/{charts → components/charts}/shared/validateProps.d.ts +0 -0
  164. /package/dist/{charts → components/charts}/xy/ScatterplotMatrix.d.ts +0 -0
  165. /package/dist/{data → components/data}/fromVegaLite.d.ts +0 -0
  166. /package/dist/{data → components/data}/transforms.d.ts +0 -0
  167. /package/dist/{export → components/export}/chartConfig.d.ts +0 -0
  168. /package/dist/{export → components/export}/selectionSerializer.d.ts +0 -0
  169. /package/dist/{geometry → components/geometry}/sankeyLinks.d.ts +0 -0
  170. /package/dist/{realtime → components/realtime}/BinAccumulator.d.ts +0 -0
  171. /package/dist/{realtime → components/realtime}/IncrementalExtent.d.ts +0 -0
  172. /package/dist/{realtime → components/realtime}/renderers/types.d.ts +0 -0
  173. /package/dist/{realtime → components/realtime}/renderers/waterfallRenderer.d.ts +0 -0
  174. /package/dist/{store → components/store}/ObservationStore.d.ts +0 -0
  175. /package/dist/{store → components/store}/SelectionStore.d.ts +0 -0
  176. /package/dist/{store → components/store}/ThemeStore.d.ts +0 -0
  177. /package/dist/{store → components/store}/TooltipStore.d.ts +0 -0
  178. /package/dist/{store → components/store}/createStore.d.ts +0 -0
  179. /package/dist/{store → components/store}/useObservation.d.ts +0 -0
  180. /package/dist/{store → components/store}/useSelection.d.ts +0 -0
  181. /package/dist/{stream → components/stream}/MarginalGraphics.d.ts +0 -0
  182. /package/dist/{stream → components/stream}/NetworkCanvasHitTester.d.ts +0 -0
  183. /package/dist/{stream → components/stream}/OrdinalCanvasHitTester.d.ts +0 -0
  184. /package/dist/{stream → components/stream}/ParticlePool.d.ts +0 -0
  185. /package/dist/{stream → components/stream}/StreamNetworkFrame.d.ts +0 -0
  186. /package/dist/{stream → components/stream}/StreamOrdinalFrame.d.ts +0 -0
  187. /package/dist/{stream → components/stream}/StreamXYFrame.d.ts +0 -0
  188. /package/dist/{stream → components/stream}/keyboardNav.d.ts +0 -0
  189. /package/dist/{stream → components/stream}/layouts/chordLayoutPlugin.d.ts +0 -0
  190. /package/dist/{stream → components/stream}/layouts/hierarchyLayoutPlugin.d.ts +0 -0
  191. /package/dist/{stream → components/stream}/layouts/sankeyLayoutPlugin.d.ts +0 -0
  192. /package/dist/{stream → components/stream}/ordinalSceneBuilders/barScene.d.ts +0 -0
  193. /package/dist/{stream → components/stream}/ordinalSceneBuilders/connectorScene.d.ts +0 -0
  194. /package/dist/{stream → components/stream}/ordinalSceneBuilders/pieScene.d.ts +0 -0
  195. /package/dist/{stream → components/stream}/ordinalSceneBuilders/pointScene.d.ts +0 -0
  196. /package/dist/{stream → components/stream}/ordinalSceneBuilders/statisticalScene.d.ts +0 -0
  197. /package/dist/{stream → components/stream}/ordinalSceneBuilders/timelineScene.d.ts +0 -0
  198. /package/dist/{stream → components/stream}/ordinalSceneBuilders/types.d.ts +0 -0
  199. /package/dist/{stream → components/stream}/renderers/barCanvasRenderer.d.ts +0 -0
  200. /package/dist/{stream → components/stream}/renderers/boxplotCanvasRenderer.d.ts +0 -0
  201. /package/dist/{stream → components/stream}/renderers/candlestickCanvasRenderer.d.ts +0 -0
  202. /package/dist/{stream → components/stream}/renderers/connectorCanvasRenderer.d.ts +0 -0
  203. /package/dist/{stream → components/stream}/renderers/networkArcRenderer.d.ts +0 -0
  204. /package/dist/{stream → components/stream}/renderers/networkCircleRenderer.d.ts +0 -0
  205. /package/dist/{stream → components/stream}/renderers/networkEdgeRenderer.d.ts +0 -0
  206. /package/dist/{stream → components/stream}/renderers/networkParticleRenderer.d.ts +0 -0
  207. /package/dist/{stream → components/stream}/renderers/networkRectRenderer.d.ts +0 -0
  208. /package/dist/{stream → components/stream}/renderers/pointCanvasRenderer.d.ts +0 -0
  209. /package/dist/{stream → components/stream}/renderers/swarmCanvasRenderer.d.ts +0 -0
  210. /package/dist/{stream → components/stream}/renderers/types.d.ts +0 -0
  211. /package/dist/{stream → components/stream}/renderers/violinCanvasRenderer.d.ts +0 -0
  212. /package/dist/{stream → components/stream}/renderers/waterfallCanvasRenderer.d.ts +0 -0
  213. /package/dist/{stream → components/stream}/renderers/wedgeCanvasRenderer.d.ts +0 -0
  214. /package/dist/{stream → components/stream}/useResponsiveSize.d.ts +0 -0
  215. /package/dist/{types → components/types}/annotationTypes.d.ts +0 -0
  216. /package/dist/{types → components/types}/generalTypes.d.ts +0 -0
  217. /package/dist/{types → components/types}/interactionTypes.d.ts +0 -0
  218. /package/dist/{types → components/types}/networkTypes.d.ts +0 -0
  219. /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 validateProps from dist
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
- const result = validateProps(component, props)
74
- if (result.valid) {
75
- console.log(`✓ ${component}: props are valid.`)
76
- // Additional data shape checks
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
- 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(", ")}`)
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
- 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(", ")}`)
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
- console.log(`✗ ${component}: validation failed.`)
95
- for (const err of result.errors) {
96
- console.log(` • ${err}`)
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
  };
@@ -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={10}
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`, `sourceAccessor`, `targetAccessor`
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
- ### Streaming Sankey (StreamNetworkFrame)
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
- const edges = [
470
- { source: "Salary", target: "Budget", value: 5000 },
471
- { source: "Freelance", target: "Budget", value: 1500 },
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
- Key props: push via ref (`push`, `pushMany`, `clear`), `chartType="sankey"`, uses `StreamNetworkFrame` from `semiotic`
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.