semiotic 3.4.2 → 3.5.1

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 (163) hide show
  1. package/CLAUDE.md +114 -9
  2. package/README.md +45 -4
  3. package/ai/behaviorContracts.cjs +311 -0
  4. package/ai/chartSuggestions.cjs +291 -0
  5. package/ai/cli.js +255 -30
  6. package/ai/componentMetadata.cjs +107 -0
  7. package/ai/dist/mcp-server.js +907 -227
  8. package/ai/schema.json +3954 -2537
  9. package/ai/system-prompt.md +23 -4
  10. package/dist/components/LinkedCharts.d.ts +5 -1
  11. package/dist/components/Tooltip/Tooltip.d.ts +1 -1
  12. package/dist/components/charts/custom/NetworkCustomChart.d.ts +64 -0
  13. package/dist/components/charts/custom/OrdinalCustomChart.d.ts +71 -0
  14. package/dist/components/charts/custom/XYCustomChart.d.ts +59 -0
  15. package/dist/components/charts/geo/ChoroplethMap.d.ts +93 -2
  16. package/dist/components/charts/geo/DistanceCartogram.d.ts +51 -4
  17. package/dist/components/charts/geo/FlowMap.d.ts +55 -0
  18. package/dist/components/charts/geo/ProportionalSymbolMap.d.ts +53 -0
  19. package/dist/components/charts/index.d.ts +6 -0
  20. package/dist/components/charts/network/ChordDiagram.d.ts +34 -2
  21. package/dist/components/charts/network/CirclePack.d.ts +36 -1
  22. package/dist/components/charts/network/ForceDirectedGraph.d.ts +130 -2
  23. package/dist/components/charts/network/OrbitDiagram.d.ts +37 -0
  24. package/dist/components/charts/network/SankeyDiagram.d.ts +51 -2
  25. package/dist/components/charts/network/TreeDiagram.d.ts +37 -2
  26. package/dist/components/charts/network/Treemap.d.ts +36 -2
  27. package/dist/components/charts/ordinal/BarChart.d.ts +113 -1
  28. package/dist/components/charts/ordinal/BoxPlot.d.ts +33 -0
  29. package/dist/components/charts/ordinal/DonutChart.d.ts +36 -0
  30. package/dist/components/charts/ordinal/DotPlot.d.ts +33 -0
  31. package/dist/components/charts/ordinal/FunnelChart.d.ts +40 -0
  32. package/dist/components/charts/ordinal/GaugeChart.d.ts +45 -0
  33. package/dist/components/charts/ordinal/GroupedBarChart.d.ts +40 -0
  34. package/dist/components/charts/ordinal/Histogram.d.ts +97 -0
  35. package/dist/components/charts/ordinal/LikertChart.d.ts +44 -0
  36. package/dist/components/charts/ordinal/PieChart.d.ts +90 -1
  37. package/dist/components/charts/ordinal/RidgelinePlot.d.ts +29 -0
  38. package/dist/components/charts/ordinal/StackedBarChart.d.ts +40 -0
  39. package/dist/components/charts/ordinal/SwarmPlot.d.ts +38 -0
  40. package/dist/components/charts/ordinal/SwimlaneChart.d.ts +62 -0
  41. package/dist/components/charts/ordinal/ViolinPlot.d.ts +34 -0
  42. package/dist/components/charts/realtime/RealtimeHeatmap.d.ts +22 -4
  43. package/dist/components/charts/realtime/RealtimeHistogram.d.ts +5 -2
  44. package/dist/components/charts/realtime/RealtimeLineChart.d.ts +24 -3
  45. package/dist/components/charts/realtime/RealtimeSwarmChart.d.ts +12 -0
  46. package/dist/components/charts/realtime/RealtimeWaterfallChart.d.ts +14 -0
  47. package/dist/components/charts/realtime/defaultRealtimeTooltip.d.ts +67 -0
  48. package/dist/components/charts/realtime/resolveWindowSize.d.ts +26 -0
  49. package/dist/components/charts/shared/chartSpecs.d.ts +91 -0
  50. package/dist/components/charts/shared/colorPalettes.d.ts +62 -0
  51. package/dist/components/charts/shared/colorUtils.d.ts +9 -10
  52. package/dist/components/charts/shared/numberFormat.d.ts +58 -0
  53. package/dist/components/charts/shared/sparseArray.d.ts +27 -0
  54. package/dist/components/charts/shared/streamPropsHelpers.d.ts +113 -0
  55. package/dist/components/charts/shared/timeFormat.d.ts +60 -0
  56. package/dist/components/charts/shared/useChartSetup.d.ts +8 -0
  57. package/dist/components/charts/shared/useCustomChartSetup.d.ts +84 -0
  58. package/dist/components/charts/shared/useFrameImperativeHandle.d.ts +28 -0
  59. package/dist/components/charts/shared/useOrdinalStreaming.d.ts +6 -19
  60. package/dist/components/charts/shared/useStreamingLegend.d.ts +27 -11
  61. package/dist/components/charts/shared/validateProps.d.ts +2 -2
  62. package/dist/components/charts/shared/validationMap.d.ts +2 -1
  63. package/dist/components/charts/shared/withChartWrapper.d.ts +13 -4
  64. package/dist/components/charts/xy/AreaChart.d.ts +44 -1
  65. package/dist/components/charts/xy/BubbleChart.d.ts +4 -0
  66. package/dist/components/charts/xy/CandlestickChart.d.ts +37 -6
  67. package/dist/components/charts/xy/ConnectedScatterplot.d.ts +28 -0
  68. package/dist/components/charts/xy/Heatmap.d.ts +4 -0
  69. package/dist/components/charts/xy/LineChart.d.ts +12 -0
  70. package/dist/components/charts/xy/MinimapChart.d.ts +58 -0
  71. package/dist/components/charts/xy/MultiAxisLineChart.d.ts +27 -0
  72. package/dist/components/charts/xy/QuadrantChart.d.ts +21 -0
  73. package/dist/components/charts/xy/Scatterplot.d.ts +38 -2
  74. package/dist/components/charts/xy/ScatterplotMatrix.d.ts +16 -0
  75. package/dist/components/charts/xy/StackedAreaChart.d.ts +61 -1
  76. package/dist/components/realtime/types.d.ts +2 -4
  77. package/dist/components/recipes/bullet.d.ts +86 -0
  78. package/dist/components/recipes/calendar.d.ts +43 -0
  79. package/dist/components/recipes/dagre.d.ts +56 -0
  80. package/dist/components/recipes/flextree.d.ts +55 -0
  81. package/dist/components/recipes/marimekko.d.ts +55 -0
  82. package/dist/components/recipes/parallelCoordinates.d.ts +97 -0
  83. package/dist/components/recipes/recipeUtils.d.ts +27 -0
  84. package/dist/components/recipes/waffle.d.ts +46 -0
  85. package/dist/components/semiotic-ai.d.ts +4 -0
  86. package/dist/components/semiotic-network.d.ts +3 -0
  87. package/dist/components/semiotic-ordinal.d.ts +3 -0
  88. package/dist/components/semiotic-recipes.d.ts +24 -0
  89. package/dist/components/semiotic-xy.d.ts +3 -0
  90. package/dist/components/semiotic.d.ts +2 -2
  91. package/dist/components/server/renderToStaticSVG.d.ts +8 -2
  92. package/dist/components/server/serverChartConfigs.d.ts +47 -1
  93. package/dist/components/server/staticAnnotations.d.ts +6 -0
  94. package/dist/components/store/ObservationStore.d.ts +1 -3
  95. package/dist/components/store/SelectionStore.d.ts +1 -3
  96. package/dist/components/store/ThemeStore.d.ts +4 -4
  97. package/dist/components/store/TooltipStore.d.ts +1 -3
  98. package/dist/components/store/createStore.d.ts +4 -2
  99. package/dist/components/stream/CanvasHitTester.d.ts +10 -8
  100. package/dist/components/stream/DataSourceAdapter.d.ts +9 -0
  101. package/dist/components/stream/GeoPipelineStore.d.ts +9 -0
  102. package/dist/components/stream/GeoTileRenderer.d.ts +14 -0
  103. package/dist/components/stream/NetworkPipelineStore.d.ts +25 -0
  104. package/dist/components/stream/OrdinalPipelineStore.d.ts +12 -0
  105. package/dist/components/stream/PipelineStore.d.ts +51 -0
  106. package/dist/components/stream/SVGOverlay.d.ts +12 -0
  107. package/dist/components/stream/SceneGraph.d.ts +15 -1
  108. package/dist/components/stream/SceneToSVG.d.ts +1 -1
  109. package/dist/components/stream/categoryDomain.d.ts +4 -0
  110. package/dist/components/stream/composeOverlays.d.ts +15 -0
  111. package/dist/components/stream/customLayout.d.ts +76 -0
  112. package/dist/components/stream/customLayoutPalette.d.ts +29 -0
  113. package/dist/components/stream/geoTypes.d.ts +13 -0
  114. package/dist/components/stream/hoverUtils.d.ts +4 -10
  115. package/dist/components/stream/networkCustomLayout.d.ts +67 -0
  116. package/dist/components/stream/networkTypes.d.ts +45 -0
  117. package/dist/components/stream/ordinalCustomLayout.d.ts +84 -0
  118. package/dist/components/stream/ordinalTypes.d.ts +35 -1
  119. package/dist/components/stream/renderers/barFunnelCanvasRenderer.d.ts +9 -1
  120. package/dist/components/stream/renderers/canvasRenderHelpers.d.ts +92 -0
  121. package/dist/components/stream/sampleCurvePath.d.ts +9 -0
  122. package/dist/components/stream/types.d.ts +44 -1
  123. package/dist/components/stream/useHydration.d.ts +89 -0
  124. package/dist/components/stream/useStableShallow.d.ts +1 -0
  125. package/dist/components/stream/xySceneBuilders/types.d.ts +4 -0
  126. package/dist/geo.min.js +2 -1
  127. package/dist/geo.module.min.js +2 -1
  128. package/dist/network.min.js +2 -1
  129. package/dist/network.module.min.js +2 -1
  130. package/dist/ordinal.min.js +2 -1
  131. package/dist/ordinal.module.min.js +2 -1
  132. package/dist/realtime.min.js +2 -1
  133. package/dist/realtime.module.min.js +2 -1
  134. package/dist/semiotic-ai.d.ts +69 -65
  135. package/dist/semiotic-ai.min.js +2 -1
  136. package/dist/semiotic-ai.module.min.js +2 -1
  137. package/dist/semiotic-data.d.ts +4 -4
  138. package/dist/semiotic-geo.d.ts +15 -15
  139. package/dist/semiotic-network.d.ts +19 -16
  140. package/dist/semiotic-ordinal.d.ts +31 -28
  141. package/dist/semiotic-realtime.d.ts +17 -17
  142. package/dist/semiotic-recipes.d.ts +24 -0
  143. package/dist/semiotic-recipes.min.js +1 -0
  144. package/dist/semiotic-recipes.module.min.js +1 -0
  145. package/dist/semiotic-server.d.ts +6 -6
  146. package/dist/semiotic-statisticalOverlays-C3DsOgr_.js +1 -0
  147. package/dist/semiotic-themes.d.ts +3 -3
  148. package/dist/semiotic-themes.min.js +2 -1
  149. package/dist/semiotic-themes.module.min.js +2 -1
  150. package/dist/semiotic-utils.d.ts +23 -23
  151. package/dist/semiotic-utils.min.js +2 -1
  152. package/dist/semiotic-utils.module.min.js +2 -1
  153. package/dist/semiotic-xy.d.ts +27 -24
  154. package/dist/semiotic.d.ts +63 -63
  155. package/dist/semiotic.min.js +2 -1
  156. package/dist/semiotic.module.min.js +2 -1
  157. package/dist/server.min.js +1 -1
  158. package/dist/server.module.min.js +1 -1
  159. package/dist/test-utils/canvasMock.d.ts +34 -5
  160. package/dist/xy.min.js +2 -1
  161. package/dist/xy.module.min.js +2 -1
  162. package/package.json +38 -17
  163. package/dist/semiotic-statisticalOverlays-Ckd_jM8z.js +0 -1
@@ -6799,6 +6799,697 @@ var require_dist = __commonJS({
6799
6799
  }
6800
6800
  });
6801
6801
 
6802
+ // ai/componentMetadata.cjs
6803
+ var require_componentMetadata = __commonJS({
6804
+ "ai/componentMetadata.cjs"(exports2, module2) {
6805
+ "use strict";
6806
+ var CATEGORY_ORDER = ["xy", "ordinal", "network", "geo", "realtime"];
6807
+ var COMPONENTS_BY_CATEGORY = {
6808
+ xy: [
6809
+ "LineChart",
6810
+ "AreaChart",
6811
+ "StackedAreaChart",
6812
+ "Scatterplot",
6813
+ "QuadrantChart",
6814
+ "MultiAxisLineChart",
6815
+ "CandlestickChart",
6816
+ "BubbleChart",
6817
+ "Heatmap",
6818
+ "ConnectedScatterplot",
6819
+ "ScatterplotMatrix",
6820
+ "MinimapChart"
6821
+ ],
6822
+ ordinal: [
6823
+ "BarChart",
6824
+ "StackedBarChart",
6825
+ "LikertChart",
6826
+ "GroupedBarChart",
6827
+ "SwarmPlot",
6828
+ "BoxPlot",
6829
+ "Histogram",
6830
+ "ViolinPlot",
6831
+ "RidgelinePlot",
6832
+ "DotPlot",
6833
+ "PieChart",
6834
+ "DonutChart",
6835
+ "GaugeChart",
6836
+ "FunnelChart",
6837
+ "SwimlaneChart"
6838
+ ],
6839
+ network: [
6840
+ "ForceDirectedGraph",
6841
+ "SankeyDiagram",
6842
+ "ChordDiagram",
6843
+ "TreeDiagram",
6844
+ "Treemap",
6845
+ "CirclePack",
6846
+ "OrbitDiagram"
6847
+ ],
6848
+ geo: [
6849
+ "ChoroplethMap",
6850
+ "ProportionalSymbolMap",
6851
+ "FlowMap",
6852
+ "DistanceCartogram"
6853
+ ],
6854
+ realtime: [
6855
+ "RealtimeLineChart",
6856
+ "RealtimeHistogram",
6857
+ "RealtimeSwarmChart",
6858
+ "RealtimeWaterfallChart",
6859
+ "RealtimeHeatmap"
6860
+ ]
6861
+ };
6862
+ var COMPONENT_TO_CATEGORY = /* @__PURE__ */ new Map();
6863
+ for (const [category, names] of Object.entries(COMPONENTS_BY_CATEGORY)) {
6864
+ for (const name of names) {
6865
+ COMPONENT_TO_CATEGORY.set(name, category);
6866
+ }
6867
+ }
6868
+ function schemaEntries(schema2) {
6869
+ return schema2.tools.map((tool) => tool.function);
6870
+ }
6871
+ function categoryForComponent(name) {
6872
+ const category = COMPONENT_TO_CATEGORY.get(name);
6873
+ if (!category) {
6874
+ throw new Error(`No AI component metadata category for "${name}"`);
6875
+ }
6876
+ return category;
6877
+ }
6878
+ function importPathForCategory(category) {
6879
+ return category === "geo" ? "semiotic/geo" : `semiotic/${category}`;
6880
+ }
6881
+ function metadataForComponent2(entryOrName) {
6882
+ const name = typeof entryOrName === "string" ? entryOrName : entryOrName.name;
6883
+ const category = categoryForComponent(name);
6884
+ return {
6885
+ name,
6886
+ category,
6887
+ importPath: importPathForCategory(category),
6888
+ renderable: category !== "realtime",
6889
+ description: typeof entryOrName === "string" ? void 0 : entryOrName.description
6890
+ };
6891
+ }
6892
+ function findComponent(schema2, name) {
6893
+ const entries = schemaEntries(schema2);
6894
+ const exact = entries.find((entry) => entry.name === name);
6895
+ if (exact) return exact;
6896
+ const lower = name.toLowerCase();
6897
+ return entries.find((entry) => entry.name.toLowerCase() === lower);
6898
+ }
6899
+ function componentIndexFromSchema2(schema2) {
6900
+ const components = schemaEntries(schema2).map(metadataForComponent2);
6901
+ const categories = {};
6902
+ for (const category of CATEGORY_ORDER) {
6903
+ categories[category] = [];
6904
+ }
6905
+ for (const component of components) {
6906
+ categories[component.category].push(component.name);
6907
+ }
6908
+ for (const names of Object.values(categories)) {
6909
+ names.sort();
6910
+ }
6911
+ return {
6912
+ version: schema2.version,
6913
+ totalComponents: components.length,
6914
+ renderableComponents: components.filter((component) => component.renderable).length,
6915
+ browserOnlyComponents: components.filter((component) => !component.renderable).length,
6916
+ categories,
6917
+ components
6918
+ };
6919
+ }
6920
+ module2.exports = {
6921
+ CATEGORY_ORDER,
6922
+ COMPONENTS_BY_CATEGORY,
6923
+ categoryForComponent,
6924
+ componentIndexFromSchema: componentIndexFromSchema2,
6925
+ findComponent,
6926
+ importPathForCategory,
6927
+ metadataForComponent: metadataForComponent2,
6928
+ schemaEntries
6929
+ };
6930
+ }
6931
+ });
6932
+
6933
+ // ai/chartSuggestions.cjs
6934
+ var require_chartSuggestions = __commonJS({
6935
+ "ai/chartSuggestions.cjs"(exports2, module2) {
6936
+ "use strict";
6937
+ var VALID_INTENTS = [
6938
+ "comparison",
6939
+ "trend",
6940
+ "distribution",
6941
+ "relationship",
6942
+ "composition",
6943
+ "geographic",
6944
+ "network",
6945
+ "hierarchy"
6946
+ ];
6947
+ var MAX_SAMPLE_SIZE = 5;
6948
+ function summarizeFields(data, keys) {
6949
+ const numericFields = [];
6950
+ const stringFields = [];
6951
+ const dateFields = [];
6952
+ const geoFields = {};
6953
+ const networkFields = {};
6954
+ const hierarchyFields = {};
6955
+ for (const key of keys) {
6956
+ const values = data.map((d) => d[key]).filter((v) => v != null);
6957
+ if (values.length === 0) continue;
6958
+ const first = values[0];
6959
+ if (typeof first === "number") {
6960
+ numericFields.push(key);
6961
+ } else if (typeof first === "string") {
6962
+ if (/^\d{4}[-/]\d{2}/.test(first) && !Number.isNaN(Date.parse(first))) {
6963
+ dateFields.push(key);
6964
+ } else {
6965
+ stringFields.push(key);
6966
+ }
6967
+ }
6968
+ const kl = key.toLowerCase();
6969
+ if (kl === "lat" || kl === "latitude") geoFields.lat = key;
6970
+ if (kl === "lon" || kl === "lng" || kl === "longitude") geoFields.lon = key;
6971
+ if (kl === "source" || kl === "from") networkFields.source = key;
6972
+ if (kl === "target" || kl === "to") networkFields.target = key;
6973
+ if (kl === "value" || kl === "weight" || kl === "amount") networkFields.value = key;
6974
+ if (kl === "children" || kl === "values") hierarchyFields.children = key;
6975
+ if (kl === "parent") hierarchyFields.parent = key;
6976
+ }
6977
+ return {
6978
+ keys,
6979
+ numericFields,
6980
+ stringFields,
6981
+ dateFields,
6982
+ geoFields,
6983
+ networkFields,
6984
+ hierarchyFields
6985
+ };
6986
+ }
6987
+ function jsxString(value) {
6988
+ return JSON.stringify(String(value));
6989
+ }
6990
+ function jsxExpression(value) {
6991
+ return `{${value}}`;
6992
+ }
6993
+ function uniqueNetworkNodes(data, sourceField, targetField) {
6994
+ const ids = /* @__PURE__ */ new Set();
6995
+ for (const datum of data) {
6996
+ const source = datum[sourceField];
6997
+ const target = datum[targetField];
6998
+ if (source != null) ids.add(source);
6999
+ if (target != null) ids.add(target);
7000
+ }
7001
+ return Array.from(ids).map((id) => ({ id }));
7002
+ }
7003
+ function suggestCharts2(args = {}) {
7004
+ const data = args.data;
7005
+ const intent = args.intent;
7006
+ if (intent && !VALID_INTENTS.includes(intent)) {
7007
+ return {
7008
+ ok: false,
7009
+ error: `Unknown intent "${intent}". Expected one of: ${VALID_INTENTS.join(", ")}.`
7010
+ };
7011
+ }
7012
+ if (!data || !Array.isArray(data) || data.length === 0) {
7013
+ return {
7014
+ ok: false,
7015
+ error: "Pass { data: [{ ... }, ...] } with 1-5 sample data objects. Optionally include intent: 'comparison' | 'trend' | 'distribution' | 'relationship' | 'composition' | 'geographic' | 'network' | 'hierarchy'."
7016
+ };
7017
+ }
7018
+ if (data.length > MAX_SAMPLE_SIZE) {
7019
+ return {
7020
+ ok: false,
7021
+ error: `Pass 1-${MAX_SAMPLE_SIZE} sample data objects; received ${data.length}. Use a representative sample instead of the full dataset.`
7022
+ };
7023
+ }
7024
+ const sample = data[0];
7025
+ if (!sample || typeof sample !== "object" || Array.isArray(sample)) {
7026
+ return {
7027
+ ok: false,
7028
+ error: "Data items must be objects with key-value pairs."
7029
+ };
7030
+ }
7031
+ const keys = Object.keys(sample);
7032
+ const fields = summarizeFields(data, keys);
7033
+ const suggestions = [];
7034
+ const { numericFields, stringFields, dateFields, geoFields, networkFields, hierarchyFields } = fields;
7035
+ const hasTime = dateFields.length > 0;
7036
+ const hasCat = stringFields.length > 0;
7037
+ const hasNum = numericFields.length > 0;
7038
+ const hasGeo = Boolean(geoFields.lat && geoFields.lon);
7039
+ const hasNetwork = Boolean(networkFields.source && networkFields.target);
7040
+ const hasHierarchy = Boolean(hierarchyFields.children || hierarchyFields.parent);
7041
+ if (hasNetwork && (!intent || intent === "network")) {
7042
+ const src = networkFields.source;
7043
+ const tgt = networkFields.target;
7044
+ if (networkFields.value) {
7045
+ suggestions.push({
7046
+ component: "SankeyDiagram",
7047
+ confidence: "high",
7048
+ reason: `Data has ${src}->${tgt} with ${networkFields.value} - ideal for flow visualization`,
7049
+ props: { edges: jsxExpression("data"), sourceAccessor: jsxString(src), targetAccessor: jsxString(tgt), valueAccessor: jsxString(networkFields.value) }
7050
+ });
7051
+ }
7052
+ const nodes = uniqueNetworkNodes(data, src, tgt);
7053
+ suggestions.push({
7054
+ component: "ForceDirectedGraph",
7055
+ confidence: networkFields.value ? "medium" : "high",
7056
+ reason: `Data has ${src}->${tgt} edges - force layout shows network structure. ForceDirectedGraph requires explicit nodes, derived here from unique source/target IDs.`,
7057
+ setup: [`const nodes = ${JSON.stringify(nodes, null, 2)}`],
7058
+ derivedData: { nodes },
7059
+ props: { nodes: jsxExpression("nodes"), edges: jsxExpression("data"), nodeIDAccessor: jsxString("id"), sourceAccessor: jsxString(src), targetAccessor: jsxString(tgt) }
7060
+ });
7061
+ }
7062
+ if (hasHierarchy && hierarchyFields.children && Array.isArray(sample[hierarchyFields.children]) && (!intent || intent === "hierarchy")) {
7063
+ const childrenAccessor = hierarchyFields.children;
7064
+ suggestions.push({
7065
+ component: "Treemap",
7066
+ confidence: "high",
7067
+ reason: `Data has nested ${childrenAccessor} structure - treemap shows hierarchical proportions. Use data[0] as the root node from the provided sample.`,
7068
+ props: { data: jsxExpression("data[0]"), childrenAccessor: jsxString(childrenAccessor), ...numericFields[0] ? { valueAccessor: jsxString(numericFields[0]) } : {} }
7069
+ });
7070
+ suggestions.push({
7071
+ component: "TreeDiagram",
7072
+ confidence: "medium",
7073
+ reason: `Tree layout shows hierarchical relationships. Use data[0] as the root node from the provided sample.`,
7074
+ props: { data: jsxExpression("data[0]"), childrenAccessor: jsxString(childrenAccessor) }
7075
+ });
7076
+ }
7077
+ if (hasGeo && (!intent || intent === "geographic")) {
7078
+ const sizeField = numericFields.find((f) => f !== geoFields.lat && f !== geoFields.lon);
7079
+ suggestions.push({
7080
+ component: "ProportionalSymbolMap",
7081
+ confidence: "high",
7082
+ reason: `Data has ${geoFields.lat}/${geoFields.lon} coordinates - map shows spatial distribution`,
7083
+ props: { points: jsxExpression("data"), xAccessor: jsxString(geoFields.lon), yAccessor: jsxString(geoFields.lat), ...sizeField ? { sizeBy: jsxString(sizeField) } : {} }
7084
+ });
7085
+ }
7086
+ if (hasTime && hasNum && (!intent || intent === "trend")) {
7087
+ const timeField = dateFields[0];
7088
+ const valueField = numericFields[0];
7089
+ suggestions.push({
7090
+ component: "LineChart",
7091
+ confidence: "high",
7092
+ reason: `Data has dates (${timeField}) and numeric values (${valueField}) - line chart shows trends over time`,
7093
+ props: { data: jsxExpression("data"), xAccessor: jsxString(timeField), yAccessor: jsxString(valueField), ...hasCat ? { lineBy: jsxString(stringFields[0]), colorBy: jsxString(stringFields[0]) } : {} }
7094
+ });
7095
+ if (hasCat) {
7096
+ suggestions.push({
7097
+ component: "StackedAreaChart",
7098
+ confidence: "medium",
7099
+ reason: `Multiple categories (${stringFields[0]}) over time - stacked area shows composition trends`,
7100
+ props: { data: jsxExpression("data"), xAccessor: jsxString(timeField), yAccessor: jsxString(valueField), areaBy: jsxString(stringFields[0]), colorBy: jsxString(stringFields[0]) }
7101
+ });
7102
+ }
7103
+ }
7104
+ if (hasCat && hasNum && (!intent || intent === "comparison" || intent === "composition" || intent === "distribution")) {
7105
+ const catField = stringFields[0];
7106
+ const valField = numericFields[0];
7107
+ if (!intent || intent === "comparison") {
7108
+ suggestions.push({
7109
+ component: "BarChart",
7110
+ confidence: hasTime ? "medium" : "high",
7111
+ reason: `Categorical field (${catField}) with values (${valField}) - bar chart for comparison`,
7112
+ props: { data: jsxExpression("data"), categoryAccessor: jsxString(catField), valueAccessor: jsxString(valField) }
7113
+ });
7114
+ }
7115
+ if (stringFields.length >= 2 && (!intent || intent === "composition")) {
7116
+ suggestions.push({
7117
+ component: "StackedBarChart",
7118
+ confidence: "medium",
7119
+ reason: `Two categorical fields (${stringFields.join(", ")}) - stacked bar shows composition within categories`,
7120
+ props: { data: jsxExpression("data"), categoryAccessor: jsxString(catField), valueAccessor: jsxString(valField), stackBy: jsxString(stringFields[1]) }
7121
+ });
7122
+ }
7123
+ if (!intent || intent === "distribution") {
7124
+ suggestions.push({
7125
+ component: "Histogram",
7126
+ confidence: "medium",
7127
+ reason: `Numeric distribution of ${valField} - histogram shows value spread`,
7128
+ props: { data: jsxExpression("data"), categoryAccessor: jsxString(catField), valueAccessor: jsxString(valField) }
7129
+ });
7130
+ }
7131
+ if (!intent || intent === "composition") {
7132
+ const uniqueCats = new Set(data.map((d) => d[catField])).size;
7133
+ if (uniqueCats <= 8) {
7134
+ suggestions.push({
7135
+ component: "DonutChart",
7136
+ confidence: "medium",
7137
+ reason: `${uniqueCats} categories - donut chart shows proportional composition`,
7138
+ props: { data: jsxExpression("data"), categoryAccessor: jsxString(catField), valueAccessor: jsxString(valField) }
7139
+ });
7140
+ }
7141
+ }
7142
+ }
7143
+ if (numericFields.length >= 2 && (!intent || intent === "relationship")) {
7144
+ const xField = numericFields[0];
7145
+ const yField = numericFields[1];
7146
+ suggestions.push({
7147
+ component: "Scatterplot",
7148
+ confidence: "high",
7149
+ reason: `Two numeric fields (${xField}, ${yField}) - scatterplot shows relationships`,
7150
+ props: { data: jsxExpression("data"), xAccessor: jsxString(xField), yAccessor: jsxString(yField), ...hasCat ? { colorBy: jsxString(stringFields[0]) } : {}, ...numericFields[2] ? { sizeBy: jsxString(numericFields[2]) } : {} }
7151
+ });
7152
+ if (numericFields.length >= 3) {
7153
+ suggestions.push({
7154
+ component: "BubbleChart",
7155
+ confidence: "medium",
7156
+ reason: "Three numeric fields - bubble chart adds size dimension to scatter",
7157
+ props: { data: jsxExpression("data"), xAccessor: jsxString(xField), yAccessor: jsxString(yField), sizeBy: jsxString(numericFields[2]) }
7158
+ });
7159
+ }
7160
+ }
7161
+ if (stringFields.length >= 2 && numericFields.length >= 1 && (!intent || intent === "relationship" || intent === "distribution" || intent === "composition")) {
7162
+ const xField = stringFields[0];
7163
+ const yField = stringFields[1];
7164
+ const valueField = numericFields[0];
7165
+ suggestions.push({
7166
+ component: "Heatmap",
7167
+ confidence: "medium",
7168
+ reason: `Two categorical fields (${xField}, ${yField}) plus numeric values (${valueField}) - heatmap shows intensity across dimensions`,
7169
+ props: { data: jsxExpression("data"), xAccessor: jsxString(xField), yAccessor: jsxString(yField), valueAccessor: jsxString(valueField) }
7170
+ });
7171
+ }
7172
+ return {
7173
+ ok: true,
7174
+ intent,
7175
+ fieldSummary: `Fields: ${keys.join(", ")} (${numericFields.length} numeric, ${stringFields.length} categorical, ${dateFields.length} date)`,
7176
+ fields,
7177
+ suggestions
7178
+ };
7179
+ }
7180
+ function formatSuggestionReport2(result) {
7181
+ if (!result.ok) return result.error;
7182
+ if (result.suggestions.length === 0) {
7183
+ return `Could not confidently recommend a chart type.
7184
+
7185
+ ${result.fieldSummary}
7186
+
7187
+ Try providing intent ('${VALID_INTENTS.join("', '")}') to narrow recommendations, or use getSchema to browse available components.`;
7188
+ }
7189
+ const lines = result.suggestions.map((suggestion, i) => {
7190
+ const propsStr = Object.entries(suggestion.props).map(([k, v]) => `${k}=${v}`).join(" ");
7191
+ const setup = suggestion.setup ? `${suggestion.setup.join("\n")}
7192
+ ` : "";
7193
+ return `${i + 1}. **${suggestion.component}** (${suggestion.confidence} confidence)
7194
+ ${suggestion.reason}
7195
+ \`\`\`tsx
7196
+ ${setup}<${suggestion.component} ${propsStr} />
7197
+ \`\`\``;
7198
+ });
7199
+ const themingTip = `
7200
+ ---
7201
+ **Styling**: All charts respond to CSS custom properties on any ancestor element:
7202
+ \`\`\`css
7203
+ .my-theme {
7204
+ --semiotic-bg: #fff;
7205
+ --semiotic-text: #333;
7206
+ --semiotic-text-secondary: #666;
7207
+ --semiotic-grid: #e0e0e0;
7208
+ --semiotic-border: #e0e0e0;
7209
+ --semiotic-font-family: sans-serif;
7210
+ --semiotic-tooltip-bg: rgba(0,0,0,0.85);
7211
+ --semiotic-tooltip-text: white;
7212
+ --semiotic-tooltip-radius: 6px;
7213
+ }
7214
+ \`\`\`
7215
+ Or use \`<ThemeProvider theme="dark">\` / \`<ThemeProvider theme={{ colors: {...}, typography: {...} }}>\`.
7216
+ For accessibility, use \`colorScheme={COLOR_BLIND_SAFE_CATEGORICAL}\` (import from \`semiotic/themes\`) - 8-color palette safe for all forms of color blindness.`;
7217
+ return lines.join("\n\n") + themingTip;
7218
+ }
7219
+ module2.exports = {
7220
+ VALID_INTENTS,
7221
+ formatSuggestionReport: formatSuggestionReport2,
7222
+ suggestCharts: suggestCharts2
7223
+ };
7224
+ }
7225
+ });
7226
+
7227
+ // ai/behaviorContracts.cjs
7228
+ var require_behaviorContracts = __commonJS({
7229
+ "ai/behaviorContracts.cjs"(exports2, module2) {
7230
+ "use strict";
7231
+ var path2 = require("path");
7232
+ var fs2 = require("fs");
7233
+ var DOC_MARKER_START = "<!-- semiotic-behavior-contracts:start -->";
7234
+ var DOC_MARKER_END = "<!-- semiotic-behavior-contracts:end -->";
7235
+ function loadStaticDataComponentsFromSchema() {
7236
+ const candidates = [
7237
+ path2.join(__dirname, "schema.json"),
7238
+ path2.join(__dirname, "..", "schema.json")
7239
+ ];
7240
+ for (const schemaPath2 of candidates) {
7241
+ try {
7242
+ const schema2 = JSON.parse(fs2.readFileSync(schemaPath2, "utf8"));
7243
+ const out = /* @__PURE__ */ new Set();
7244
+ for (const tool of schema2.tools || []) {
7245
+ const required2 = tool.function?.parameters?.required || [];
7246
+ if (required2.includes("data")) out.add(tool.function.name);
7247
+ }
7248
+ if (out.size > 0) return out;
7249
+ } catch {
7250
+ }
7251
+ }
7252
+ return /* @__PURE__ */ new Set();
7253
+ }
7254
+ var REQUIRED_COMBINATIONS = [
7255
+ {
7256
+ component: "StackedAreaChart",
7257
+ required: ["areaBy"],
7258
+ staticRequired: ["data", "areaBy"],
7259
+ pushRequired: ["areaBy"],
7260
+ summary: "Stacked areas need a flat data array plus areaBy to identify the stacked series."
7261
+ },
7262
+ {
7263
+ component: "BubbleChart",
7264
+ required: ["sizeBy"],
7265
+ staticRequired: ["data", "sizeBy"],
7266
+ pushRequired: ["sizeBy"],
7267
+ summary: "Bubbles need sizeBy in addition to x/y accessors so radius encodes data rather than a constant point size."
7268
+ },
7269
+ {
7270
+ component: "StackedBarChart",
7271
+ required: ["stackBy"],
7272
+ staticRequired: ["data", "stackBy"],
7273
+ pushRequired: ["stackBy"],
7274
+ summary: "Stacked bars need stackBy to split each category into stack segments."
7275
+ },
7276
+ {
7277
+ component: "GroupedBarChart",
7278
+ required: ["groupBy"],
7279
+ staticRequired: ["data", "groupBy"],
7280
+ pushRequired: ["groupBy"],
7281
+ summary: "Grouped bars need groupBy to split each category into side-by-side bars."
7282
+ },
7283
+ {
7284
+ component: "SwimlaneChart",
7285
+ required: ["subcategoryAccessor"],
7286
+ staticRequired: ["data", "subcategoryAccessor"],
7287
+ pushRequired: ["subcategoryAccessor"],
7288
+ summary: "Swimlanes need subcategoryAccessor; colorBy defaults to the same field when not provided."
7289
+ },
7290
+ {
7291
+ component: "GaugeChart",
7292
+ required: ["value"],
7293
+ staticRequired: ["value"],
7294
+ pushRequired: [],
7295
+ summary: "GaugeChart is value-only. thresholds, min, max, sweep, and arcWidth are optional."
7296
+ },
7297
+ {
7298
+ component: "ForceDirectedGraph",
7299
+ required: ["nodes", "edges"],
7300
+ staticRequired: ["nodes", "edges"],
7301
+ pushRequired: ["nodes", "edges"],
7302
+ summary: "ForceDirectedGraph schema/rendering requires nodes and edges. If an agent infers nodes from edge endpoints, it must materialize a nodes array before returning code."
7303
+ }
7304
+ ];
7305
+ var PUSH_MODE_COMPONENTS = [
7306
+ "LineChart",
7307
+ "AreaChart",
7308
+ "StackedAreaChart",
7309
+ "Scatterplot",
7310
+ "BubbleChart",
7311
+ "ConnectedScatterplot",
7312
+ "BarChart",
7313
+ "StackedBarChart",
7314
+ "GroupedBarChart",
7315
+ "SwarmPlot",
7316
+ "BoxPlot",
7317
+ "Histogram",
7318
+ "ViolinPlot",
7319
+ "RidgelinePlot",
7320
+ "DotPlot",
7321
+ "PieChart",
7322
+ "DonutChart",
7323
+ "LikertChart",
7324
+ "SwimlaneChart",
7325
+ "ForceDirectedGraph",
7326
+ "SankeyDiagram",
7327
+ "ChordDiagram",
7328
+ "ProportionalSymbolMap",
7329
+ "DistanceCartogram"
7330
+ ];
7331
+ var STATIC_DATA_COMPONENTS = loadStaticDataComponentsFromSchema();
7332
+ var BEHAVIOR_CONTRACTS2 = [
7333
+ {
7334
+ id: "props.data-required-by-usage-mode",
7335
+ category: "required-props",
7336
+ title: "Data required by usage mode",
7337
+ severity: "error",
7338
+ appliesTo: {
7339
+ components: PUSH_MODE_COMPONENTS
7340
+ },
7341
+ summary: "Static usage (`renderChart`, MCP previews, SSR snapshots, and copy/paste examples with immediate data) requires data in props. React push mode selects live ingestion by omitting data and mutating through a ref.",
7342
+ agentAction: 'Pass usageMode="push" to `semiotic-ai --doctor` when validating ref-based JSX with no data prop. Keep usageMode="static" or omit it for renderChart/MCP/static configs where data must be present.'
7343
+ },
7344
+ {
7345
+ id: "color.category-precedence",
7346
+ category: "color",
7347
+ title: "Categorical color precedence",
7348
+ severity: "info",
7349
+ appliesTo: {
7350
+ propsAny: ["colorBy", "colorScheme"]
7351
+ },
7352
+ summary: "When colorBy is set, CategoryColorProvider/LinkedCharts category maps win for mapped categories. Unmapped categories fall back to explicit colorScheme, then ThemeProvider colors.categorical, then the built-in categorical fallback.",
7353
+ agentAction: "Use colorBy for categorical encodings. Use CategoryColorProvider or LinkedCharts for cross-chart consistency, colorScheme for per-chart fallback palettes, and avoid frameProps style functions unless intentionally bypassing HOC color resolution."
7354
+ },
7355
+ {
7356
+ id: "props.required-combinations",
7357
+ category: "required-props",
7358
+ title: "Required prop combinations",
7359
+ severity: "error",
7360
+ appliesTo: {
7361
+ components: REQUIRED_COMBINATIONS.map((entry) => entry.component)
7362
+ },
7363
+ summary: "Some chart families need semantic props beyond data. These combinations are enforced by validation/schema for static configs and remain required in push mode unless explicitly noted.",
7364
+ agentAction: "Before returning code, check the selected component against the required combinations list. For push mode, omit data but keep semantic props such as areaBy, sizeBy, stackBy, and groupBy.",
7365
+ combinations: REQUIRED_COMBINATIONS
7366
+ },
7367
+ {
7368
+ id: "streaming.push-mode-data",
7369
+ category: "streaming",
7370
+ title: "Push mode omits data",
7371
+ severity: "warning",
7372
+ appliesTo: {
7373
+ components: PUSH_MODE_COMPONENTS
7374
+ },
7375
+ summary: "HOC push mode is selected by omitting the data prop entirely. Passing data={[]} is static empty data and can clear/reinitialize the frame on render.",
7376
+ agentAction: "For live charts, create a ref, omit data, then call ref.current.push() or pushMany(). For static renderChart/MCP snapshots, provide data because renderChart cannot push later."
7377
+ },
7378
+ {
7379
+ id: "streaming.ref-mutations-require-id-accessors",
7380
+ category: "streaming",
7381
+ title: "Ref mutations need stable IDs",
7382
+ severity: "warning",
7383
+ appliesTo: {
7384
+ components: PUSH_MODE_COMPONENTS
7385
+ },
7386
+ summary: "push() and pushMany() can append without IDs, but remove(id) and update(id, updater) require a stable ID accessor: pointIdAccessor for XY/realtime charts, dataIdAccessor for ordinal charts, and nodeIDAccessor/edgeIdAccessor for network operations.",
7387
+ agentAction: "When generating code that calls remove() or update(), include the matching ID accessor and make sure pushed rows carry that ID field."
7388
+ },
7389
+ {
7390
+ id: "rendering.renderchart-static-props",
7391
+ category: "rendering",
7392
+ title: "renderChart uses static props only",
7393
+ severity: "warning",
7394
+ appliesTo: {},
7395
+ summary: "MCP renderChart and semiotic/server renderChart render a single static SVG/PNG snapshot. Browser-only realtime components and future ref pushes are not renderable through that path.",
7396
+ agentAction: "Use renderChart only with renderable HOC components and complete static data. For live behavior, return React code with a ref and do not promise MCP-rendered output."
7397
+ }
7398
+ ];
7399
+ function hasOwn(value, key) {
7400
+ return Object.prototype.hasOwnProperty.call(value, key);
7401
+ }
7402
+ function normalizeProps(props) {
7403
+ return props && typeof props === "object" && !Array.isArray(props) ? props : {};
7404
+ }
7405
+ function appliesToComponent(contract, component) {
7406
+ if (!component) return !contract.appliesTo?.components;
7407
+ const components = contract.appliesTo?.components;
7408
+ return !components || components.includes(component);
7409
+ }
7410
+ function appliesToProps(contract, props) {
7411
+ const propsAny = contract.appliesTo?.propsAny;
7412
+ if (!propsAny || propsAny.length === 0) return true;
7413
+ return propsAny.some((prop) => hasOwn(props, prop) && props[prop] !== void 0);
7414
+ }
7415
+ function behaviorContractsFor2({ component, props } = {}) {
7416
+ const normalizedProps = normalizeProps(props);
7417
+ return BEHAVIOR_CONTRACTS2.filter(
7418
+ (contract) => appliesToComponent(contract, component) && appliesToProps(contract, normalizedProps)
7419
+ );
7420
+ }
7421
+ function normalizeUsageMode2(usageMode) {
7422
+ if (usageMode === "push") return "push";
7423
+ if (usageMode === "static" || usageMode === "renderChart" || usageMode === "server") return "static";
7424
+ return "static";
7425
+ }
7426
+ function dataRequiredForUsageMode2(component, usageMode) {
7427
+ if (!STATIC_DATA_COMPONENTS.has(component)) return false;
7428
+ if (normalizeUsageMode2(usageMode) === "push" && PUSH_MODE_COMPONENTS.includes(component)) return false;
7429
+ return true;
7430
+ }
7431
+ function requiredCombinationsFor(component) {
7432
+ return REQUIRED_COMBINATIONS.filter((entry) => !component || entry.component === component);
7433
+ }
7434
+ function formatRequiredCombination(entry) {
7435
+ const staticRequired = entry.staticRequired || entry.required;
7436
+ const pushRequired = entry.pushRequired || entry.required.filter((prop) => prop !== "data");
7437
+ const pushText = pushRequired.length > 0 ? pushRequired.join(" + ") : "not supported";
7438
+ return `${entry.component}: static ${staticRequired.join(" + ")}; push ${pushText}. ${entry.summary}`;
7439
+ }
7440
+ function formatDoctorBehaviorContracts2(contracts) {
7441
+ if (!contracts || contracts.length === 0) return "";
7442
+ const lines = ["Behavior contracts:"];
7443
+ for (const contract of contracts) {
7444
+ lines.push(` - [${contract.id}] ${contract.summary}`);
7445
+ if (contract.combinations) {
7446
+ for (const combo of contract.combinations) {
7447
+ lines.push(` ${formatRequiredCombination(combo)}`);
7448
+ }
7449
+ }
7450
+ lines.push(` Action: ${contract.agentAction}`);
7451
+ }
7452
+ return lines.join("\n");
7453
+ }
7454
+ function formatBehaviorContractsMarkdown({ compact = false } = {}) {
7455
+ const lines = [
7456
+ compact ? "## Behavior Contracts" : "## AI Behavior Contracts",
7457
+ "",
7458
+ DOC_MARKER_START,
7459
+ "",
7460
+ "These rules are generated from `ai/behaviorContracts.cjs` and are consumed by `semiotic-ai --doctor`, MCP resources, and docs checks.",
7461
+ ""
7462
+ ];
7463
+ for (const contract of BEHAVIOR_CONTRACTS2) {
7464
+ lines.push(`- **${contract.title}** (\`${contract.id}\`): ${contract.summary}`);
7465
+ if (!compact) {
7466
+ lines.push(` Agent action: ${contract.agentAction}`);
7467
+ }
7468
+ if (contract.combinations) {
7469
+ const combos = contract.combinations.map(formatRequiredCombination).join(" ");
7470
+ lines.push(` Required combinations: ${combos}`);
7471
+ }
7472
+ }
7473
+ lines.push("", DOC_MARKER_END);
7474
+ return lines.join("\n");
7475
+ }
7476
+ module2.exports = {
7477
+ BEHAVIOR_CONTRACTS: BEHAVIOR_CONTRACTS2,
7478
+ DOC_MARKER_END,
7479
+ DOC_MARKER_START,
7480
+ PUSH_MODE_COMPONENTS,
7481
+ REQUIRED_COMBINATIONS,
7482
+ STATIC_DATA_COMPONENTS,
7483
+ behaviorContractsFor: behaviorContractsFor2,
7484
+ dataRequiredForUsageMode: dataRequiredForUsageMode2,
7485
+ formatBehaviorContractsMarkdown,
7486
+ formatDoctorBehaviorContracts: formatDoctorBehaviorContracts2,
7487
+ normalizeUsageMode: normalizeUsageMode2,
7488
+ requiredCombinationsFor
7489
+ };
7490
+ }
7491
+ });
7492
+
6802
7493
  // node_modules/zod/v3/helpers/util.js
6803
7494
  var util;
6804
7495
  (function(util2) {
@@ -31460,9 +32151,12 @@ var COMPONENT_REGISTRY = {
31460
32151
  Scatterplot: { component: import_ai.Scatterplot, category: "xy" },
31461
32152
  BubbleChart: { component: import_ai.BubbleChart, category: "xy" },
31462
32153
  Heatmap: { component: import_ai.Heatmap, category: "xy" },
32154
+ ScatterplotMatrix: { component: import_ai.ScatterplotMatrix, category: "xy" },
32155
+ MinimapChart: { component: import_ai.MinimapChart, category: "xy" },
31463
32156
  ConnectedScatterplot: { component: import_ai.ConnectedScatterplot, category: "xy" },
31464
32157
  QuadrantChart: { component: import_ai.QuadrantChart, category: "xy" },
31465
32158
  MultiAxisLineChart: { component: import_ai.MultiAxisLineChart, category: "xy" },
32159
+ CandlestickChart: { component: import_ai.CandlestickChart, category: "xy" },
31466
32160
  BarChart: { component: import_ai.BarChart, category: "ordinal" },
31467
32161
  StackedBarChart: { component: import_ai.StackedBarChart, category: "ordinal" },
31468
32162
  GroupedBarChart: { component: import_ai.GroupedBarChart, category: "ordinal" },
@@ -31474,7 +32168,10 @@ var COMPONENT_REGISTRY = {
31474
32168
  RidgelinePlot: { component: import_ai.RidgelinePlot, category: "ordinal" },
31475
32169
  PieChart: { component: import_ai.PieChart, category: "ordinal" },
31476
32170
  DonutChart: { component: import_ai.DonutChart, category: "ordinal" },
32171
+ GaugeChart: { component: import_ai.GaugeChart, category: "ordinal" },
31477
32172
  FunnelChart: { component: import_ai.FunnelChart, category: "ordinal" },
32173
+ LikertChart: { component: import_ai.LikertChart, category: "ordinal" },
32174
+ SwimlaneChart: { component: import_ai.SwimlaneChart, category: "ordinal" },
31478
32175
  ForceDirectedGraph: { component: import_ai.ForceDirectedGraph, category: "network" },
31479
32176
  ChordDiagram: { component: import_ai.ChordDiagram, category: "network" },
31480
32177
  SankeyDiagram: { component: import_ai.SankeyDiagram, category: "network" },
@@ -31523,27 +32220,72 @@ ${errors.join("\n")}`
31523
32220
 
31524
32221
  // ai/mcp-server.ts
31525
32222
  var import_ai3 = require("semiotic/ai");
32223
+ var {
32224
+ componentIndexFromSchema,
32225
+ metadataForComponent
32226
+ } = require_componentMetadata();
32227
+ var {
32228
+ formatSuggestionReport,
32229
+ suggestCharts
32230
+ } = require_chartSuggestions();
32231
+ var {
32232
+ BEHAVIOR_CONTRACTS,
32233
+ behaviorContractsFor,
32234
+ dataRequiredForUsageMode,
32235
+ formatDoctorBehaviorContracts,
32236
+ normalizeUsageMode
32237
+ } = require_behaviorContracts();
31526
32238
  var schemaPath = path.resolve(__dirname, "../schema.json");
31527
32239
  var schema = JSON.parse(fs.readFileSync(schemaPath, "utf-8"));
31528
32240
  var schemaByComponent = {};
31529
32241
  for (const tool of schema.tools) {
31530
32242
  schemaByComponent[tool.function.name] = tool.function;
31531
32243
  }
32244
+ var allComponentNames = Object.keys(schemaByComponent).sort();
31532
32245
  var componentNames = Object.keys(COMPONENT_REGISTRY).sort();
31533
32246
  var REPO = "nteract/semiotic";
32247
+ function aiFilePath(fileName) {
32248
+ return path.resolve(__dirname, "..", fileName);
32249
+ }
32250
+ function readAIFile(fileName) {
32251
+ return fs.readFileSync(aiFilePath(fileName), "utf-8");
32252
+ }
32253
+ function componentIndexJSON() {
32254
+ return JSON.stringify(componentIndexFromSchema(schema), null, 2);
32255
+ }
32256
+ function textResource(uri, mimeType, text) {
32257
+ return {
32258
+ contents: [{
32259
+ uri: uri.href,
32260
+ mimeType,
32261
+ text
32262
+ }]
32263
+ };
32264
+ }
32265
+ function promptMessage(text) {
32266
+ return {
32267
+ messages: [{
32268
+ role: "user",
32269
+ content: {
32270
+ type: "text",
32271
+ text
32272
+ }
32273
+ }]
32274
+ };
32275
+ }
31534
32276
  async function getSchemaHandler(args) {
31535
32277
  const component = args.component;
31536
32278
  if (!component) {
31537
- const all = Object.keys(schemaByComponent).sort();
31538
- const renderable = new Set(Object.keys(COMPONENT_REGISTRY));
31539
- const list = all.map((name) => renderable.has(name) ? `${name} [renderable]` : name);
32279
+ const list = allComponentNames.map((name) => metadataForComponent(name).renderable ? `${name} [renderable]` : name);
31540
32280
  return {
31541
- content: [{ type: "text", text: `Available components (${all.length}):
32281
+ content: [{ type: "text", text: `Available components (${allComponentNames.length}):
31542
32282
  ${list.join(", ")}
31543
32283
 
31544
32284
  Components marked [renderable] can be rendered to SVG via renderChart (pass theme parameter for styled output). Others (Realtime*) require a browser environment.
31545
32285
 
31546
- All charts support CSS custom properties for theming (--semiotic-bg, --semiotic-text, --semiotic-grid, etc.) and <ThemeProvider>. Use COLOR_BLIND_SAFE_CATEGORICAL (import from semiotic) for accessible color palettes.
32286
+ For full agent context, read MCP resources: semiotic://schema, semiotic://components, semiotic://behavior-contracts, semiotic://system-prompt, semiotic://examples.
32287
+
32288
+ All charts support CSS custom properties for theming (--semiotic-bg, --semiotic-text, --semiotic-grid, etc.) and <ThemeProvider>. Use COLOR_BLIND_SAFE_CATEGORICAL (import from semiotic/themes) for accessible color palettes.
31547
32289
 
31548
32290
  Pass { component: '<name>' } to get the prop schema for a specific component.` }]
31549
32291
  };
@@ -31556,226 +32298,25 @@ Pass { component: '<name>' } to get the prop schema for a specific component.` }
31556
32298
  isError: true
31557
32299
  };
31558
32300
  }
31559
- const renderableNote = COMPONENT_REGISTRY[component] ? "This component can be rendered to SVG via renderChart." : "This component requires a browser environment and cannot be rendered via renderChart.";
32301
+ const renderableNote = metadataForComponent(component).renderable ? "This component can be rendered to SVG via renderChart." : "This component requires a browser environment and cannot be rendered via renderChart.";
32302
+ const contracts = behaviorContractsFor({ component, props: {} });
32303
+ const contractText = contracts.length > 0 ? `
32304
+
32305
+ Behavior contracts:
32306
+ ${JSON.stringify(contracts, null, 2)}` : "";
31560
32307
  return {
31561
32308
  content: [{ type: "text", text: `${renderableNote}
31562
32309
 
31563
- ${JSON.stringify(entry, null, 2)}` }]
32310
+ ${JSON.stringify(entry, null, 2)}${contractText}` }]
31564
32311
  };
31565
32312
  }
31566
32313
  async function suggestChartHandler(args) {
31567
- const data = args.data;
31568
- const intent = args.intent;
31569
- if (!data || !Array.isArray(data) || data.length === 0) {
31570
- return {
31571
- content: [{ type: "text", text: "Pass { data: [{ ... }, ...] } with 1-5 sample data objects. Optionally include intent: 'comparison' | 'trend' | 'distribution' | 'relationship' | 'composition' | 'geographic' | 'network' | 'hierarchy'." }],
31572
- isError: true
31573
- };
31574
- }
31575
- const sample = data[0];
31576
- if (!sample || typeof sample !== "object") {
31577
- return {
31578
- content: [{ type: "text", text: "Data items must be objects with key-value pairs." }],
31579
- isError: true
31580
- };
31581
- }
31582
- const keys = Object.keys(sample);
31583
- const suggestions = [];
31584
- const numericFields = [];
31585
- const stringFields = [];
31586
- const dateFields = [];
31587
- const geoFields = {};
31588
- const networkFields = {};
31589
- const hierarchyFields = {};
31590
- for (const key of keys) {
31591
- const values = data.map((d) => d[key]).filter((v) => v != null);
31592
- if (values.length === 0) continue;
31593
- const first = values[0];
31594
- if (typeof first === "number") {
31595
- numericFields.push(key);
31596
- } else if (typeof first === "string") {
31597
- if (/^\d{4}[-/]\d{2}/.test(first) && !isNaN(Date.parse(first))) {
31598
- dateFields.push(key);
31599
- } else {
31600
- stringFields.push(key);
31601
- }
31602
- }
31603
- const kl = key.toLowerCase();
31604
- if (kl === "lat" || kl === "latitude") geoFields.lat = key;
31605
- if (kl === "lon" || kl === "lng" || kl === "longitude") geoFields.lon = key;
31606
- if (kl === "source" || kl === "from") networkFields.source = key;
31607
- if (kl === "target" || kl === "to") networkFields.target = key;
31608
- if (kl === "value" || kl === "weight" || kl === "amount") networkFields.value = key;
31609
- if (kl === "children" || kl === "values") hierarchyFields.children = key;
31610
- if (kl === "parent") hierarchyFields.parent = key;
31611
- }
31612
- const hasTime = dateFields.length > 0;
31613
- const hasCat = stringFields.length > 0;
31614
- const hasNum = numericFields.length > 0;
31615
- const hasGeo = geoFields.lat && geoFields.lon;
31616
- const hasNetwork = networkFields.source && networkFields.target;
31617
- const hasHierarchy = hierarchyFields.children || hierarchyFields.parent;
31618
- if (hasNetwork && (!intent || intent === "network")) {
31619
- const src = networkFields.source;
31620
- const tgt = networkFields.target;
31621
- if (networkFields.value) {
31622
- suggestions.push({
31623
- component: "SankeyDiagram",
31624
- confidence: "high",
31625
- reason: `Data has ${src}\u2192${tgt} with ${networkFields.value} \u2014 ideal for flow visualization`,
31626
- props: { edges: "data", sourceAccessor: `"${src}"`, targetAccessor: `"${tgt}"`, valueAccessor: `"${networkFields.value}"` }
31627
- });
31628
- }
31629
- suggestions.push({
31630
- component: "ForceDirectedGraph",
31631
- confidence: networkFields.value ? "medium" : "high",
31632
- reason: `Data has ${src}\u2192${tgt} edges \u2014 force layout shows network structure. Nodes are auto-inferred from edges when not provided.`,
31633
- props: { edges: "data", sourceAccessor: `"${src}"`, targetAccessor: `"${tgt}"` }
31634
- });
31635
- }
31636
- if (hasHierarchy && (!intent || intent === "hierarchy")) {
31637
- suggestions.push({
31638
- component: "Treemap",
31639
- confidence: "high",
31640
- reason: `Data has nested ${hierarchyFields.children || "parent"} structure \u2014 treemap shows hierarchical proportions`,
31641
- props: { data: "rootObject", childrenAccessor: `"${hierarchyFields.children || "children"}"`, ...numericFields[0] ? { valueAccessor: `"${numericFields[0]}"` } : {} }
31642
- });
31643
- suggestions.push({
31644
- component: "TreeDiagram",
31645
- confidence: "medium",
31646
- reason: "Tree layout shows hierarchical relationships",
31647
- props: { data: "rootObject", childrenAccessor: `"${hierarchyFields.children || "children"}"` }
31648
- });
31649
- }
31650
- if (hasGeo && (!intent || intent === "geographic")) {
31651
- const sizeField = numericFields.find((f) => f !== geoFields.lat && f !== geoFields.lon);
31652
- suggestions.push({
31653
- component: "ProportionalSymbolMap",
31654
- confidence: "high",
31655
- reason: `Data has ${geoFields.lat}/${geoFields.lon} coordinates \u2014 map shows spatial distribution`,
31656
- props: { points: "data", xAccessor: `"${geoFields.lon}"`, yAccessor: `"${geoFields.lat}"`, ...sizeField ? { sizeBy: `"${sizeField}"` } : {} }
31657
- });
31658
- }
31659
- if (hasTime && hasNum && (!intent || intent === "trend")) {
31660
- const timeField = dateFields[0];
31661
- const valueField = numericFields[0];
31662
- suggestions.push({
31663
- component: "LineChart",
31664
- confidence: "high",
31665
- reason: `Data has dates (${timeField}) and numeric values (${valueField}) \u2014 line chart shows trends over time`,
31666
- props: { data: "data", xAccessor: `"${timeField}"`, yAccessor: `"${valueField}"`, ...hasCat ? { lineBy: `"${stringFields[0]}"`, colorBy: `"${stringFields[0]}"` } : {} }
31667
- });
31668
- if (hasCat) {
31669
- suggestions.push({
31670
- component: "StackedAreaChart",
31671
- confidence: "medium",
31672
- reason: `Multiple categories (${stringFields[0]}) over time \u2014 stacked area shows composition trends`,
31673
- props: { data: "data", xAccessor: `"${timeField}"`, yAccessor: `"${valueField}"`, areaBy: `"${stringFields[0]}"`, colorBy: `"${stringFields[0]}"` }
31674
- });
31675
- }
32314
+ const result = suggestCharts(args);
32315
+ const content = [{ type: "text", text: formatSuggestionReport(result) }];
32316
+ if (!result.ok) {
32317
+ return { content, isError: true, structuredContent: result };
31676
32318
  }
31677
- if (hasCat && hasNum && (!intent || intent === "comparison" || intent === "composition" || intent === "distribution")) {
31678
- const catField = stringFields[0];
31679
- const valField = numericFields[0];
31680
- if (!intent || intent === "comparison") {
31681
- suggestions.push({
31682
- component: "BarChart",
31683
- confidence: hasTime ? "medium" : "high",
31684
- reason: `Categorical field (${catField}) with values (${valField}) \u2014 bar chart for comparison`,
31685
- props: { data: "data", categoryAccessor: `"${catField}"`, valueAccessor: `"${valField}"` }
31686
- });
31687
- }
31688
- if (stringFields.length >= 2 && (!intent || intent === "composition")) {
31689
- suggestions.push({
31690
- component: "StackedBarChart",
31691
- confidence: "medium",
31692
- reason: `Two categorical fields (${stringFields.join(", ")}) \u2014 stacked bar shows composition within categories`,
31693
- props: { data: "data", categoryAccessor: `"${catField}"`, valueAccessor: `"${valField}"`, stackBy: `"${stringFields[1]}"` }
31694
- });
31695
- }
31696
- if (!intent || intent === "distribution") {
31697
- suggestions.push({
31698
- component: "Histogram",
31699
- confidence: "medium",
31700
- reason: `Numeric distribution of ${valField} \u2014 histogram shows value spread`,
31701
- props: { data: "data", categoryAccessor: `"${catField}"`, valueAccessor: `"${valField}"` }
31702
- });
31703
- }
31704
- if (!intent || intent === "composition") {
31705
- const uniqueCats = new Set(data.map((d) => d[catField])).size;
31706
- if (uniqueCats <= 8) {
31707
- suggestions.push({
31708
- component: "DonutChart",
31709
- confidence: "medium",
31710
- reason: `${uniqueCats} categories \u2014 donut chart shows proportional composition`,
31711
- props: { data: "data", categoryAccessor: `"${catField}"`, valueAccessor: `"${valField}"` }
31712
- });
31713
- }
31714
- }
31715
- }
31716
- if (numericFields.length >= 2 && (!intent || intent === "relationship")) {
31717
- const xField = numericFields[0];
31718
- const yField = numericFields[1];
31719
- suggestions.push({
31720
- component: "Scatterplot",
31721
- confidence: "high",
31722
- reason: `Two numeric fields (${xField}, ${yField}) \u2014 scatterplot shows relationships`,
31723
- props: { data: "data", xAccessor: `"${xField}"`, yAccessor: `"${yField}"`, ...hasCat ? { colorBy: `"${stringFields[0]}"` } : {}, ...numericFields[2] ? { sizeBy: `"${numericFields[2]}"` } : {} }
31724
- });
31725
- if (numericFields.length >= 3) {
31726
- suggestions.push({
31727
- component: "BubbleChart",
31728
- confidence: "medium",
31729
- reason: `Three numeric fields \u2014 bubble chart adds size dimension to scatter`,
31730
- props: { data: "data", xAccessor: `"${xField}"`, yAccessor: `"${yField}"`, sizeBy: `"${numericFields[2]}"` }
31731
- });
31732
- }
31733
- if (numericFields.length >= 2 && hasCat) {
31734
- suggestions.push({
31735
- component: "Heatmap",
31736
- confidence: "medium",
31737
- reason: `Numeric values across dimensions \u2014 heatmap shows density/intensity`,
31738
- props: { data: "data", xAccessor: `"${xField}"`, yAccessor: `"${hasCat ? stringFields[0] : yField}"`, valueAccessor: `"${hasCat ? numericFields[0] : numericFields[2] || yField}"` }
31739
- });
31740
- }
31741
- }
31742
- if (suggestions.length === 0) {
31743
- const fieldSummary = `Fields: ${keys.join(", ")} (${numericFields.length} numeric, ${stringFields.length} categorical, ${dateFields.length} date)`;
31744
- return {
31745
- content: [{ type: "text", text: `Could not confidently recommend a chart type.
31746
-
31747
- ${fieldSummary}
31748
-
31749
- Try providing intent ('comparison', 'trend', 'distribution', 'relationship', 'composition', 'geographic', 'network', 'hierarchy') to narrow recommendations, or use getSchema to browse available components.` }]
31750
- };
31751
- }
31752
- const lines = suggestions.map((s, i) => {
31753
- const propsStr = Object.entries(s.props).map(([k, v]) => `${k}=${v}`).join(" ");
31754
- return `${i + 1}. **${s.component}** (${s.confidence} confidence)
31755
- ${s.reason}
31756
- \`<${s.component} ${propsStr} />\``;
31757
- });
31758
- const themingTip = `
31759
- ---
31760
- **Styling**: All charts respond to CSS custom properties on any ancestor element:
31761
- \`\`\`css
31762
- .my-theme {
31763
- --semiotic-bg: #fff; /* chart background */
31764
- --semiotic-text: #333; /* primary text */
31765
- --semiotic-text-secondary: #666; /* tick labels */
31766
- --semiotic-grid: #e0e0e0; /* grid lines */
31767
- --semiotic-border: #e0e0e0; /* axis lines, borders */
31768
- --semiotic-font-family: sans-serif;
31769
- --semiotic-tooltip-bg: rgba(0,0,0,0.85);
31770
- --semiotic-tooltip-text: white;
31771
- --semiotic-tooltip-radius: 6px;
31772
- }
31773
- \`\`\`
31774
- Or use \`<ThemeProvider theme="dark">\` / \`<ThemeProvider theme={{ colors: {...}, typography: {...} }}>\`.
31775
- For accessibility, use \`colorScheme={COLOR_BLIND_SAFE_CATEGORICAL}\` (import from \`semiotic\`) \u2014 8-color palette safe for all forms of color blindness.`;
31776
- return {
31777
- content: [{ type: "text", text: lines.join("\n\n") + themingTip }]
31778
- };
32319
+ return { content, structuredContent: result };
31779
32320
  }
31780
32321
  async function renderChartHandler(args) {
31781
32322
  const component = args.component;
@@ -31789,6 +32330,12 @@ async function renderChartHandler(args) {
31789
32330
  };
31790
32331
  }
31791
32332
  if (!COMPONENT_REGISTRY[component]) {
32333
+ if (schemaByComponent[component]) {
32334
+ return {
32335
+ content: [{ type: "text", text: `Component "${component}" is known but cannot be rendered via renderChart. It requires a browser/live environment. Renderable components: ${componentNames.join(", ")}` }],
32336
+ isError: true
32337
+ };
32338
+ }
31792
32339
  return {
31793
32340
  content: [{ type: "text", text: `Unknown component "${component}". Available: ${componentNames.join(", ")}` }],
31794
32341
  isError: true
@@ -31841,9 +32388,16 @@ ${svg}` }],
31841
32388
  content: [{ type: "text", text: svg }]
31842
32389
  };
31843
32390
  }
32391
+ function filterUsageModeDiagnoses(component, usageMode, diagnoses) {
32392
+ if (dataRequiredForUsageMode(component, usageMode)) return diagnoses;
32393
+ return diagnoses.filter(
32394
+ (d) => d.code !== "VALIDATION" || d.message !== `"data" is required for ${component}.`
32395
+ );
32396
+ }
31844
32397
  async function diagnoseConfigHandler(args) {
31845
32398
  const component = args.component;
31846
32399
  const props = args.props ?? {};
32400
+ const usageMode = normalizeUsageMode(args.usageMode);
31847
32401
  if (!component) {
31848
32402
  return {
31849
32403
  content: [{ type: "text", text: "Missing 'component' field. Provide { component: 'LineChart', props: { ... } }." }],
@@ -31851,21 +32405,31 @@ async function diagnoseConfigHandler(args) {
31851
32405
  };
31852
32406
  }
31853
32407
  const result = (0, import_ai3.diagnoseConfig)(component, props);
31854
- if (result.ok) {
31855
- const warnings = result.diagnoses.filter((d) => d.severity === "warning");
32408
+ const diagnoses = filterUsageModeDiagnoses(component, usageMode, result.diagnoses);
32409
+ const ok = diagnoses.every((d) => d.severity === "warning");
32410
+ const usageModeNote = usageMode === "push" ? "Usage mode: push (data prop may be omitted; use a ref to push data).\n\n" : "";
32411
+ if (ok) {
32412
+ const warnings = diagnoses.filter((d) => d.severity === "warning");
31856
32413
  const msg = warnings.length > 0 ? `Configuration looks good with ${warnings.length} warning(s):
31857
32414
  ${warnings.map((w) => `\u26A0 [${w.code}] ${w.message}
31858
32415
  Fix: ${w.fix}`).join("\n")}` : `\u2713 Configuration looks good \u2014 no issues detected.`;
31859
- return { content: [{ type: "text", text: msg }] };
32416
+ const contracts = formatDoctorBehaviorContracts(behaviorContractsFor({ component, props }));
32417
+ return { content: [{ type: "text", text: `${usageModeNote}${contracts ? `${msg}
32418
+
32419
+ ${contracts}` : msg}` }] };
31860
32420
  }
31861
- const lines = result.diagnoses.map((d) => {
32421
+ const lines = diagnoses.map((d) => {
31862
32422
  const icon = d.severity === "error" ? "\u2717" : "\u26A0";
31863
32423
  const fixLine = d.fix ? `
31864
32424
  Fix: ${d.fix}` : "";
31865
32425
  return `${icon} [${d.code}] ${d.message}${fixLine}`;
31866
32426
  });
31867
32427
  return {
31868
- content: [{ type: "text", text: lines.join("\n") }],
32428
+ content: [{ type: "text", text: [
32429
+ usageModeNote.trim(),
32430
+ lines.join("\n"),
32431
+ formatDoctorBehaviorContracts(behaviorContractsFor({ component, props }))
32432
+ ].filter(Boolean).join("\n\n") }],
31869
32433
  isError: true
31870
32434
  };
31871
32435
  }
@@ -31974,6 +32538,120 @@ function createServer2() {
31974
32538
  name: "semiotic",
31975
32539
  version: schema.version || "3.0.0"
31976
32540
  });
32541
+ srv.registerResource(
32542
+ "semiotic-schema",
32543
+ "semiotic://schema",
32544
+ {
32545
+ title: "Semiotic Component Schema",
32546
+ description: "Machine-readable JSON schema for all Semiotic AI chart components.",
32547
+ mimeType: "application/json"
32548
+ },
32549
+ (uri) => textResource(uri, "application/json", JSON.stringify(schema, null, 2))
32550
+ );
32551
+ srv.registerResource(
32552
+ "semiotic-components",
32553
+ "semiotic://components",
32554
+ {
32555
+ title: "Semiotic Component Index",
32556
+ description: "Renderable/browser-only component index with MCP categories.",
32557
+ mimeType: "application/json"
32558
+ },
32559
+ (uri) => textResource(uri, "application/json", componentIndexJSON())
32560
+ );
32561
+ srv.registerResource(
32562
+ "semiotic-behavior-contracts",
32563
+ "semiotic://behavior-contracts",
32564
+ {
32565
+ title: "Semiotic AI Behavior Contracts",
32566
+ description: "Agent-visible semantic rules for color precedence, required prop combinations, streaming refs, and renderability.",
32567
+ mimeType: "application/json"
32568
+ },
32569
+ (uri) => textResource(uri, "application/json", JSON.stringify({
32570
+ version: schema.version,
32571
+ contracts: BEHAVIOR_CONTRACTS
32572
+ }, null, 2))
32573
+ );
32574
+ srv.registerResource(
32575
+ "semiotic-system-prompt",
32576
+ "semiotic://system-prompt",
32577
+ {
32578
+ title: "Semiotic AI System Prompt",
32579
+ description: "Compact implementation guidance for AI assistants building with Semiotic.",
32580
+ mimeType: "text/markdown"
32581
+ },
32582
+ (uri) => textResource(uri, "text/markdown", readAIFile("system-prompt.md"))
32583
+ );
32584
+ srv.registerResource(
32585
+ "semiotic-examples",
32586
+ "semiotic://examples",
32587
+ {
32588
+ title: "Semiotic AI Examples",
32589
+ description: "Copy-paste examples for common Semiotic chart data shapes.",
32590
+ mimeType: "text/markdown"
32591
+ },
32592
+ (uri) => textResource(uri, "text/markdown", readAIFile("examples.md"))
32593
+ );
32594
+ srv.registerPrompt(
32595
+ "build-semiotic-chart",
32596
+ {
32597
+ title: "Build a Semiotic chart",
32598
+ description: "Workflow for choosing a chart, validating props, and rendering a preview.",
32599
+ argsSchema: {
32600
+ intent: external_exports3.string().optional().describe("Visualization intent, e.g. trend, comparison, distribution, relationship, composition, network, hierarchy."),
32601
+ dataDescription: external_exports3.string().optional().describe("Brief description of the data fields and sample rows."),
32602
+ component: external_exports3.string().optional().describe("Optional preferred Semiotic component name.")
32603
+ }
32604
+ },
32605
+ (args) => promptMessage([
32606
+ "Build a production-ready Semiotic visualization.",
32607
+ "",
32608
+ `Intent: ${args.intent || "not specified"}`,
32609
+ `Data: ${args.dataDescription || "not specified"}`,
32610
+ `Preferred component: ${args.component || "not specified"}`,
32611
+ "",
32612
+ "Use this MCP workflow:",
32613
+ "1. Read semiotic://system-prompt for compact API rules and pitfalls.",
32614
+ "2. Read semiotic://behavior-contracts for semantic rules that schema shape alone cannot express.",
32615
+ "3. If no component is specified, call suggestChart with 1-5 representative sample rows and the intent.",
32616
+ "4. Call getSchema for the selected component before writing JSX or renderChart props.",
32617
+ '5. Call diagnoseConfig with usageMode="static" for renderChart/static data, or usageMode="push" for ref-based React code that intentionally omits data.',
32618
+ "6. Fix all diagnoseConfig errors before presenting code.",
32619
+ "7. If the component is renderable and has static data, call renderChart once to verify it returns SVG.",
32620
+ "8. Prefer sub-path imports such as semiotic/xy, semiotic/ordinal, semiotic/network, semiotic/geo, or semiotic/ai depending on the surrounding code.",
32621
+ "",
32622
+ "Return the final JSX or renderChart call plus any assumptions about fields, accessors, or aggregation."
32623
+ ].join("\n"))
32624
+ );
32625
+ srv.registerPrompt(
32626
+ "debug-semiotic-chart",
32627
+ {
32628
+ title: "Debug a Semiotic chart",
32629
+ description: "Workflow for diagnosing bad props, rendering failures, and chart-quality issues.",
32630
+ argsSchema: {
32631
+ component: external_exports3.string().optional().describe("Semiotic component name, e.g. BarChart."),
32632
+ problem: external_exports3.string().optional().describe("Observed failure, warning, or visual issue."),
32633
+ props: external_exports3.string().optional().describe("Relevant chart props as JSON or a short summary.")
32634
+ }
32635
+ },
32636
+ (args) => promptMessage([
32637
+ "Debug this Semiotic chart with the MCP server.",
32638
+ "",
32639
+ `Component: ${args.component || "not specified"}`,
32640
+ `Problem: ${args.problem || "not specified"}`,
32641
+ `Props: ${args.props || "not provided"}`,
32642
+ "",
32643
+ "Use this MCP workflow:",
32644
+ "1. Call getSchema for the component and compare the provided props against required props and accessor names.",
32645
+ "2. Read semiotic://behavior-contracts for semantic rules around colors, required combinations, streaming refs, and renderability.",
32646
+ '3. Call diagnoseConfig with usageMode="push" if the code intentionally omits data for a ref-push HOC; otherwise use usageMode="static".',
32647
+ "4. Treat diagnoseConfig errors as blockers and warnings as review items.",
32648
+ "5. If renderable and static data is available, call renderChart with a minimal reproduction to separate configuration issues from rendering bugs.",
32649
+ "6. Check semiotic://examples for a nearby working pattern before inventing new props.",
32650
+ "7. If the result looks like a Semiotic bug, call reportIssue with the component, props summary, diagnoseConfig output, and renderChart result.",
32651
+ "",
32652
+ "Return the smallest safe fix first, then mention any follow-up cleanup or issue-reporting step."
32653
+ ].join("\n"))
32654
+ );
31977
32655
  srv.tool(
31978
32656
  "getSchema",
31979
32657
  `Return the prop schema for a Semiotic chart component. Pass { component: '<name>' } to get its props, or omit component to list all available components. Components marked [renderable] can be passed to renderChart for static SVG output.`,
@@ -31991,7 +32669,7 @@ function createServer2() {
31991
32669
  );
31992
32670
  srv.tool(
31993
32671
  "renderChart",
31994
- `Render a Semiotic chart to static SVG or PNG. Returns SVG string (default) or Base64-encoded PNG image. Optionally pass theme CSS custom properties (--semiotic-bg, --semiotic-text, etc.) to style the output. PNG requires the 'sharp' package to be installed. Available components: ${componentNames.join(", ")}.`,
32672
+ `Render a Semiotic chart to static SVG or PNG. This is a static snapshot path: props must include data immediately, and ref/push-mode charts cannot be rendered through this tool. Returns SVG string (default) or Base64-encoded PNG image. Optionally pass theme CSS custom properties (--semiotic-bg, --semiotic-text, etc.) to style the output. PNG requires the 'sharp' package to be installed. Available components: ${componentNames.join(", ")}.`,
31995
32673
  {
31996
32674
  component: external_exports3.string().describe("Chart component name, e.g. 'LineChart', 'BarChart'"),
31997
32675
  props: external_exports3.record(external_exports3.string(), external_exports3.unknown()).optional().describe("Chart props object, e.g. { data: [...], xAccessor: 'x' }."),
@@ -32002,10 +32680,11 @@ function createServer2() {
32002
32680
  );
32003
32681
  srv.tool(
32004
32682
  "diagnoseConfig",
32005
- "Diagnose a Semiotic chart configuration for common problems (empty data, bad dimensions, missing accessors, wrong data shape, color contrast issues, etc). Checks WCAG color contrast ratios and suggests COLOR_BLIND_SAFE_CATEGORICAL for accessibility. Returns a human-readable diagnostic report with actionable fixes.",
32683
+ "Diagnose a Semiotic chart configuration for common problems (empty data, bad dimensions, missing accessors, wrong data shape, color contrast issues, etc). Pass usageMode='push' for ref-based React HOCs that intentionally omit data; omit usageMode or pass 'static' for renderChart/MCP/server configs where data is required. Checks WCAG color contrast ratios and suggests COLOR_BLIND_SAFE_CATEGORICAL for accessibility. Returns a human-readable diagnostic report with actionable fixes.",
32006
32684
  {
32007
32685
  component: external_exports3.string().describe("Chart component name, e.g. 'LineChart'"),
32008
- props: external_exports3.record(external_exports3.string(), external_exports3.unknown()).optional().describe("Chart props object, e.g. { data: [...], xAccessor: 'x' }.")
32686
+ props: external_exports3.record(external_exports3.string(), external_exports3.unknown()).optional().describe("Chart props object, e.g. { data: [...], xAccessor: 'x' }."),
32687
+ usageMode: external_exports3.enum(["static", "push", "renderChart", "server"]).optional().describe("Validation mode. Use 'push' for ref-based React HOCs that omit data; use 'static' or omit for renderChart/MCP/static data configs.")
32009
32688
  },
32010
32689
  diagnoseConfigHandler
32011
32690
  );
@@ -32073,7 +32752,8 @@ async function main() {
32073
32752
  });
32074
32753
  httpServer.listen(port, () => {
32075
32754
  console.error(`Semiotic MCP server (HTTP) listening on http://localhost:${port}`);
32076
- console.error("Tools: getSchema, suggestChart, renderChart, diagnoseConfig, reportIssue");
32755
+ console.error("Tools: getSchema, suggestChart, renderChart, diagnoseConfig, reportIssue, applyTheme");
32756
+ console.error("Resources: semiotic://schema, semiotic://components, semiotic://behavior-contracts, semiotic://system-prompt, semiotic://examples");
32077
32757
  });
32078
32758
  } else {
32079
32759
  const srv = createServer2();