semiotic 3.5.4 → 3.7.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 (164) hide show
  1. package/CLAUDE.md +196 -175
  2. package/README.md +52 -15
  3. package/ai/cli.js +41 -0
  4. package/ai/componentMetadata.cjs +11 -2
  5. package/ai/dist/mcp-server.js +454 -4
  6. package/ai/examples.md +98 -0
  7. package/ai/schema.json +614 -9
  8. package/ai/system-prompt.md +5 -2
  9. package/dist/components/AccessibleNavTree.d.ts +25 -0
  10. package/dist/components/Annotation.d.ts +40 -14
  11. package/dist/components/ChartContainer.d.ts +32 -2
  12. package/dist/components/ai/annotationProvenance.d.ts +349 -0
  13. package/dist/components/ai/audienceProfile.d.ts +147 -0
  14. package/dist/components/ai/audiences.d.ts +31 -0
  15. package/dist/components/ai/chartCapabilities.d.ts +55 -0
  16. package/dist/components/ai/chartCapabilityTypes.d.ts +254 -0
  17. package/dist/components/ai/chartRoles.d.ts +27 -0
  18. package/dist/components/ai/conversationArc.d.ts +379 -0
  19. package/dist/components/ai/dataScaleProfile.d.ts +320 -0
  20. package/dist/components/ai/describeChart.d.ts +114 -0
  21. package/dist/components/ai/diffProfile.d.ts +51 -0
  22. package/dist/components/ai/inferIntent.d.ts +24 -0
  23. package/dist/components/ai/intents.d.ts +34 -0
  24. package/dist/components/ai/navigationTree.d.ts +45 -0
  25. package/dist/components/ai/profileData.d.ts +16 -0
  26. package/dist/components/ai/qualityFixtures.d.ts +2 -0
  27. package/dist/components/ai/qualityScorecard.d.ts +82 -0
  28. package/dist/components/ai/readerGrounding.d.ts +70 -0
  29. package/dist/components/ai/repairChartConfig.d.ts +73 -0
  30. package/dist/components/ai/streamingTypes.d.ts +64 -0
  31. package/dist/components/ai/suggestCharts.d.ts +109 -0
  32. package/dist/components/ai/suggestDashboard.d.ts +92 -0
  33. package/dist/components/ai/suggestStreamCharts.d.ts +34 -0
  34. package/dist/components/ai/suggestStretchCharts.d.ts +60 -0
  35. package/dist/components/ai/useChartSuggestions.d.ts +22 -0
  36. package/dist/components/ai/useConversationArc.d.ts +89 -0
  37. package/dist/components/ai/useNavigationSync.d.ts +61 -0
  38. package/dist/components/ai/variantDiscovery.d.ts +168 -0
  39. package/dist/components/charts/geo/ChoroplethMap.capability.d.ts +2 -0
  40. package/dist/components/charts/geo/DistanceCartogram.capability.d.ts +2 -0
  41. package/dist/components/charts/geo/FlowMap.capability.d.ts +2 -0
  42. package/dist/components/charts/geo/ProportionalSymbolMap.capability.d.ts +2 -0
  43. package/dist/components/charts/index.d.ts +1 -1
  44. package/dist/components/charts/network/ChordDiagram.capability.d.ts +2 -0
  45. package/dist/components/charts/network/CirclePack.capability.d.ts +2 -0
  46. package/dist/components/charts/network/ForceDirectedGraph.capability.d.ts +2 -0
  47. package/dist/components/charts/network/OrbitDiagram.capability.d.ts +2 -0
  48. package/dist/components/charts/network/ProcessSankey.capability.d.ts +2 -0
  49. package/dist/components/charts/network/SankeyDiagram.capability.d.ts +2 -0
  50. package/dist/components/charts/network/TreeDiagram.capability.d.ts +2 -0
  51. package/dist/components/charts/network/Treemap.capability.d.ts +2 -0
  52. package/dist/components/charts/ordinal/BarChart.capability.d.ts +2 -0
  53. package/dist/components/charts/ordinal/BoxPlot.capability.d.ts +2 -0
  54. package/dist/components/charts/ordinal/DonutChart.capability.d.ts +2 -0
  55. package/dist/components/charts/ordinal/DotPlot.capability.d.ts +2 -0
  56. package/dist/components/charts/ordinal/FunnelChart.capability.d.ts +2 -0
  57. package/dist/components/charts/ordinal/GaugeChart.capability.d.ts +2 -0
  58. package/dist/components/charts/ordinal/GroupedBarChart.capability.d.ts +2 -0
  59. package/dist/components/charts/ordinal/Histogram.capability.d.ts +2 -0
  60. package/dist/components/charts/ordinal/Histogram.d.ts +4 -2
  61. package/dist/components/charts/ordinal/LikertChart.capability.d.ts +2 -0
  62. package/dist/components/charts/ordinal/LikertChart.d.ts +1 -1
  63. package/dist/components/charts/ordinal/LikertChart.defaults.d.ts +1 -0
  64. package/dist/components/charts/ordinal/PieChart.capability.d.ts +2 -0
  65. package/dist/components/charts/ordinal/RidgelinePlot.capability.d.ts +2 -0
  66. package/dist/components/charts/ordinal/StackedBarChart.capability.d.ts +2 -0
  67. package/dist/components/charts/ordinal/SwarmPlot.capability.d.ts +2 -0
  68. package/dist/components/charts/ordinal/SwimlaneChart.capability.d.ts +2 -0
  69. package/dist/components/charts/ordinal/ViolinPlot.capability.d.ts +2 -0
  70. package/dist/components/charts/realtime/RealtimeHeatmap.capability.d.ts +2 -0
  71. package/dist/components/charts/realtime/RealtimeHeatmap.d.ts +3 -0
  72. package/dist/components/charts/realtime/RealtimeHistogram.capability.d.ts +2 -0
  73. package/dist/components/charts/realtime/RealtimeHistogram.d.ts +3 -0
  74. package/dist/components/charts/realtime/RealtimeLineChart.capability.d.ts +2 -0
  75. package/dist/components/charts/realtime/RealtimeLineChart.d.ts +3 -0
  76. package/dist/components/charts/realtime/RealtimeSwarmChart.capability.d.ts +2 -0
  77. package/dist/components/charts/realtime/RealtimeSwarmChart.d.ts +3 -0
  78. package/dist/components/charts/realtime/RealtimeWaterfallChart.capability.d.ts +2 -0
  79. package/dist/components/charts/realtime/RealtimeWaterfallChart.d.ts +3 -0
  80. package/dist/components/charts/realtime/TemporalHistogram.capability.d.ts +7 -0
  81. package/dist/components/charts/shared/annotationHierarchy.d.ts +42 -0
  82. package/dist/components/charts/shared/annotationResolvers.d.ts +3 -2
  83. package/dist/components/charts/shared/annotationRules.d.ts +16 -0
  84. package/dist/components/charts/shared/annotationTypes.d.ts +14 -0
  85. package/dist/components/charts/shared/auditAccessibility.d.ts +90 -0
  86. package/dist/components/charts/shared/chartSpecs.d.ts +2 -37
  87. package/dist/components/charts/shared/diagnoseConfig.d.ts +4 -6
  88. package/dist/components/charts/shared/selectionUtils.d.ts +5 -2
  89. package/dist/components/charts/shared/streamPropsHelpers.d.ts +2 -0
  90. package/dist/components/charts/shared/types.d.ts +5 -1
  91. package/dist/components/charts/value/BigNumber.capability.d.ts +13 -0
  92. package/dist/components/charts/value/BigNumber.d.ts +14 -0
  93. package/dist/components/charts/value/formatting.d.ts +40 -0
  94. package/dist/components/charts/value/thresholdSparkline.d.ts +40 -0
  95. package/dist/components/charts/value/types.d.ts +292 -0
  96. package/dist/components/charts/xy/AreaChart.capability.d.ts +10 -0
  97. package/dist/components/charts/xy/BubbleChart.capability.d.ts +2 -0
  98. package/dist/components/charts/xy/CandlestickChart.capability.d.ts +2 -0
  99. package/dist/components/charts/xy/ConnectedScatterplot.capability.d.ts +2 -0
  100. package/dist/components/charts/xy/DifferenceChart.capability.d.ts +8 -0
  101. package/dist/components/charts/xy/Heatmap.capability.d.ts +9 -0
  102. package/dist/components/charts/xy/LineChart.capability.d.ts +9 -0
  103. package/dist/components/charts/xy/MinimapChart.capability.d.ts +2 -0
  104. package/dist/components/charts/xy/MultiAxisLineChart.capability.d.ts +2 -0
  105. package/dist/components/charts/xy/QuadrantChart.capability.d.ts +2 -0
  106. package/dist/components/charts/xy/QuadrantChart.d.ts +5 -2
  107. package/dist/components/charts/xy/QuadrantChart.defaults.d.ts +2 -0
  108. package/dist/components/charts/xy/Scatterplot.capability.d.ts +2 -0
  109. package/dist/components/charts/xy/StackedAreaChart.capability.d.ts +2 -0
  110. package/dist/components/data/DataSummarizer.d.ts +45 -0
  111. package/dist/components/realtime/lifecycleBands.d.ts +44 -0
  112. package/dist/components/realtime/types.d.ts +23 -8
  113. package/dist/components/recipes/annotationDensity.d.ts +69 -0
  114. package/dist/components/recipes/annotationLayout.d.ts +93 -0
  115. package/dist/components/semiotic-ai.d.ts +58 -0
  116. package/dist/components/semiotic-realtime.d.ts +2 -0
  117. package/dist/components/semiotic-recipes.d.ts +4 -0
  118. package/dist/components/semiotic-utils.d.ts +8 -0
  119. package/dist/components/semiotic-value.d.ts +55 -0
  120. package/dist/components/semiotic-xy.d.ts +1 -1
  121. package/dist/components/semiotic.d.ts +8 -1
  122. package/dist/components/server/staticAnnotations.d.ts +2 -0
  123. package/dist/components/store/useChartFocus.d.ts +43 -0
  124. package/dist/components/store/useChartInterrogation.d.ts +141 -0
  125. package/dist/components/stream/AccessibleDataTable.d.ts +10 -1
  126. package/dist/components/stream/NetworkSVGOverlay.d.ts +11 -5
  127. package/dist/components/stream/OrdinalSVGOverlay.d.ts +2 -0
  128. package/dist/components/stream/SVGOverlay.d.ts +2 -0
  129. package/dist/components/stream/geoTypes.d.ts +3 -0
  130. package/dist/components/stream/networkTypes.d.ts +2 -0
  131. package/dist/components/stream/ordinalTypes.d.ts +2 -0
  132. package/dist/components/stream/types.d.ts +2 -0
  133. package/dist/geo.min.js +1 -1
  134. package/dist/geo.module.min.js +1 -1
  135. package/dist/network.min.js +1 -1
  136. package/dist/network.module.min.js +1 -1
  137. package/dist/ordinal.min.js +1 -1
  138. package/dist/ordinal.module.min.js +1 -1
  139. package/dist/realtime.min.js +1 -1
  140. package/dist/realtime.module.min.js +1 -1
  141. package/dist/semiotic-ai.d.ts +58 -0
  142. package/dist/semiotic-ai.min.js +1 -1
  143. package/dist/semiotic-ai.module.min.js +1 -1
  144. package/dist/semiotic-realtime.d.ts +2 -0
  145. package/dist/semiotic-recipes.d.ts +4 -0
  146. package/dist/semiotic-recipes.min.js +1 -1
  147. package/dist/semiotic-recipes.module.min.js +1 -1
  148. package/dist/semiotic-themes.min.js +1 -1
  149. package/dist/semiotic-themes.module.min.js +1 -1
  150. package/dist/semiotic-utils.d.ts +8 -0
  151. package/dist/semiotic-utils.min.js +1 -1
  152. package/dist/semiotic-utils.module.min.js +1 -1
  153. package/dist/semiotic-value.d.ts +55 -0
  154. package/dist/semiotic-value.min.js +2 -0
  155. package/dist/semiotic-value.module.min.js +2 -0
  156. package/dist/semiotic-xy.d.ts +1 -1
  157. package/dist/semiotic.d.ts +8 -1
  158. package/dist/semiotic.min.js +1 -1
  159. package/dist/semiotic.module.min.js +1 -1
  160. package/dist/server.min.js +1 -1
  161. package/dist/server.module.min.js +1 -1
  162. package/dist/xy.min.js +1 -1
  163. package/dist/xy.module.min.js +1 -1
  164. package/package.json +28 -5
@@ -6890,7 +6890,7 @@ var require_dist = __commonJS({
6890
6890
  var require_componentMetadata = __commonJS({
6891
6891
  "ai/componentMetadata.cjs"(exports2, module2) {
6892
6892
  "use strict";
6893
- var CATEGORY_ORDER = ["xy", "ordinal", "network", "geo", "realtime"];
6893
+ var CATEGORY_ORDER = ["xy", "ordinal", "network", "geo", "realtime", "value"];
6894
6894
  var COMPONENTS_BY_CATEGORY = {
6895
6895
  xy: [
6896
6896
  "LineChart",
@@ -6947,6 +6947,9 @@ var require_componentMetadata = __commonJS({
6947
6947
  "RealtimeSwarmChart",
6948
6948
  "RealtimeWaterfallChart",
6949
6949
  "RealtimeHeatmap"
6950
+ ],
6951
+ value: [
6952
+ "BigNumber"
6950
6953
  ]
6951
6954
  };
6952
6955
  var COMPONENT_TO_CATEGORY = /* @__PURE__ */ new Map();
@@ -6972,11 +6975,12 @@ var require_componentMetadata = __commonJS({
6972
6975
  const name = typeof entryOrName === "string" ? entryOrName : entryOrName.name;
6973
6976
  const category = categoryForComponent(name);
6974
6977
  const isPushOnly = category === "realtime" && name.startsWith("Realtime");
6978
+ const isValueCategory = category === "value";
6975
6979
  return {
6976
6980
  name,
6977
6981
  category,
6978
6982
  importPath: importPathForCategory(category),
6979
- renderable: !isPushOnly,
6983
+ renderable: !isPushOnly && !isValueCategory,
6980
6984
  description: typeof entryOrName === "string" ? void 0 : entryOrName.description
6981
6985
  };
6982
6986
  }
@@ -32643,6 +32647,22 @@ ${contracts}` : msg}` }] };
32643
32647
  isError: true
32644
32648
  };
32645
32649
  }
32650
+ async function auditAccessibilityHandler(args) {
32651
+ const component = args.component;
32652
+ const props = args.props ?? {};
32653
+ if (!component) {
32654
+ return {
32655
+ content: [{ type: "text", text: "Missing 'component' field. Provide { component: 'LineChart', props: { ... } }." }],
32656
+ isError: true
32657
+ };
32658
+ }
32659
+ const result = (0, import_ai3.auditAccessibility)(component, props, { inChartContainer: args.inChartContainer === true, describe: args.describe === true, navigable: args.navigable === true });
32660
+ return {
32661
+ content: [{ type: "text", text: (0, import_ai3.formatAccessibilityAudit)(result) }],
32662
+ // Only block on provable critical failures; warnings/manual items are advisory.
32663
+ isError: !result.ok
32664
+ };
32665
+ }
32646
32666
  async function reportIssueHandler(args) {
32647
32667
  const title = args.title;
32648
32668
  const body = args.body;
@@ -32743,6 +32763,287 @@ Dark-mode presets: ${THEME_PRESET_NAMES.filter((n) => n.includes("dark")).join("
32743
32763
  content: [{ type: "text", text: usage.join("\n") }]
32744
32764
  };
32745
32765
  }
32766
+ function profileInputFromVariantArgs(args) {
32767
+ const props = args.props ?? {};
32768
+ if (Array.isArray(args.data)) {
32769
+ return { data: args.data };
32770
+ }
32771
+ if (Array.isArray(props.data)) {
32772
+ return { data: props.data };
32773
+ }
32774
+ if (Array.isArray(props.nodes) && (Array.isArray(props.edges) || Array.isArray(props.links))) {
32775
+ return {
32776
+ data: [],
32777
+ rawInput: {
32778
+ nodes: props.nodes,
32779
+ edges: props.edges ?? props.links
32780
+ }
32781
+ };
32782
+ }
32783
+ if (props.data && typeof props.data === "object" && !Array.isArray(props.data)) {
32784
+ return { data: [], rawInput: props.data };
32785
+ }
32786
+ return { data: [] };
32787
+ }
32788
+ function buildVariantProposalProps(proposal, profile, audience) {
32789
+ if (proposal.buildProps) return proposal.buildProps(profile, audience);
32790
+ const capability = (0, import_ai3.getCapability)(proposal.baseComponent);
32791
+ const variant = proposal.variantKey ? capability?.variants?.find((v) => v.key === proposal.variantKey) : void 0;
32792
+ return capability ? capability.buildProps(profile, variant) : {};
32793
+ }
32794
+ async function proposeChartVariantsHandler(args) {
32795
+ const { component, intent, maxResults, audience } = args;
32796
+ const capability = (0, import_ai3.getCapability)(component);
32797
+ if (!capability) {
32798
+ return {
32799
+ content: [{ type: "text", text: `No chart capability registered for "${component}". Call suggestCharts first to pick from known capability components.` }],
32800
+ isError: true
32801
+ };
32802
+ }
32803
+ const { data, rawInput } = profileInputFromVariantArgs(args);
32804
+ const profile = (0, import_ai3.profileData)(data, { rawInput });
32805
+ const intentArg = Array.isArray(intent) ? intent : intent ? [intent] : void 0;
32806
+ const fitReason = capability.fits(profile);
32807
+ const proposals = (0, import_ai3.proposeVariant)(component, capability, {
32808
+ profile,
32809
+ audience,
32810
+ intent: intentArg,
32811
+ existingVariants: capability.variants
32812
+ });
32813
+ const ranked = proposals.map((proposal) => {
32814
+ const score = (0, import_ai3.evaluateVariantProposal)(proposal, profile, audience, {
32815
+ intent: intentArg,
32816
+ baselineComponent: component
32817
+ });
32818
+ const { buildProps: _buildProps, ...proposalMeta } = proposal;
32819
+ return {
32820
+ proposal: proposalMeta,
32821
+ score,
32822
+ props: buildVariantProposalProps(proposal, profile, audience)
32823
+ };
32824
+ }).sort((a, b) => {
32825
+ if (b.score.fit !== a.score.fit) return b.score.fit - a.score.fit;
32826
+ if (a.score.risk !== b.score.risk) return a.score.risk - b.score.risk;
32827
+ return b.score.novelty - a.score.novelty;
32828
+ }).slice(0, maxResults ?? 8);
32829
+ const lines = [
32830
+ `${ranked.length} variant proposal${ranked.length === 1 ? "" : "s"} for ${component}${intentArg ? ` (intent: ${intentArg.join(", ")})` : ""}:`,
32831
+ ...fitReason ? [`Base chart fit warning: ${fitReason}`] : [],
32832
+ "",
32833
+ ...ranked.map((entry, i) => {
32834
+ const label = entry.proposal.label ?? entry.proposal.variantKey ?? entry.proposal.id;
32835
+ const tags = entry.proposal.tags?.length ? ` [${entry.proposal.tags.join(", ")}]` : "";
32836
+ const reasons = entry.score.reasons.length ? `
32837
+ ${entry.score.reasons.join("; ")}` : "";
32838
+ return `${i + 1}. ${entry.proposal.baseComponent} / ${label}${tags} (fit ${entry.score.fit.toFixed(1)}/5, novelty ${entry.score.novelty.toFixed(2)}, risk ${entry.score.risk.toFixed(2)})${reasons}`;
32839
+ })
32840
+ ];
32841
+ return {
32842
+ content: [{ type: "text", text: lines.join("\n") }],
32843
+ structuredContent: {
32844
+ component,
32845
+ profile: {
32846
+ rowCount: profile.rowCount,
32847
+ primary: profile.primary,
32848
+ categoryCount: profile.categoryCount ?? null,
32849
+ seriesCount: profile.seriesCount ?? null,
32850
+ hasHierarchy: profile.hasHierarchy,
32851
+ hasNetwork: profile.hasNetwork,
32852
+ hasGeo: profile.hasGeo
32853
+ },
32854
+ fitReason,
32855
+ proposals: ranked
32856
+ }
32857
+ };
32858
+ }
32859
+ async function suggestChartsHandler(args) {
32860
+ const { data, intent, maxResults, allow, deny, audience } = args;
32861
+ const intentArg = Array.isArray(intent) ? intent : intent ? [intent] : void 0;
32862
+ const suggestions = (0, import_ai3.suggestCharts)(data, {
32863
+ intent: intentArg,
32864
+ allow,
32865
+ deny,
32866
+ maxResults: maxResults ?? 8,
32867
+ audience
32868
+ });
32869
+ const lines = [
32870
+ `${suggestions.length} suggestion${suggestions.length === 1 ? "" : "s"} for ${data.length} rows${intentArg ? ` (intent: ${intentArg.join(", ")})` : ""}:`,
32871
+ "",
32872
+ ...suggestions.map((s, i) => {
32873
+ const variantTag = s.variant ? ` / ${s.variant.label}` : "";
32874
+ const reasons = s.reasons.length ? ` \u2014 ${s.reasons.join("; ")}` : "";
32875
+ const caveats = s.caveats.length ? `
32876
+ caveats: ${s.caveats.join("; ")}` : "";
32877
+ return `${i + 1}. ${s.component}${variantTag} (score ${s.score.toFixed(1)}/5, familiarity ${s.rubric.familiarity}, accuracy ${s.rubric.accuracy})${reasons}${caveats}`;
32878
+ })
32879
+ ];
32880
+ return {
32881
+ content: [{ type: "text", text: lines.join("\n") }],
32882
+ structuredContent: { suggestions }
32883
+ };
32884
+ }
32885
+ async function suggestStreamChartsHandler(args) {
32886
+ const { schema: schema2, intent, maxResults } = args;
32887
+ const intentArg = Array.isArray(intent) ? intent : intent ? [intent] : void 0;
32888
+ const suggestions = (0, import_ai3.suggestStreamCharts)(schema2, {
32889
+ intent: intentArg,
32890
+ maxResults: maxResults ?? 8
32891
+ });
32892
+ const lines = [
32893
+ `${suggestions.length} stream chart suggestion${suggestions.length === 1 ? "" : "s"}${intentArg ? ` (intent: ${intentArg.join(", ")})` : ""}`,
32894
+ ...schema2.throughput ? [`throughput: ${schema2.throughput}`] : [],
32895
+ ...schema2.retention ? [`retention: ${schema2.retention}`] : [],
32896
+ "",
32897
+ ...suggestions.map((s, i) => {
32898
+ const reasons = s.reasons.length ? ` \u2014 ${s.reasons.join("; ")}` : "";
32899
+ const caveats = s.caveats.length ? `
32900
+ caveats: ${s.caveats.join("; ")}` : "";
32901
+ return `${i + 1}. ${s.component} (score ${s.score.toFixed(1)}/5)${reasons}${caveats}`;
32902
+ })
32903
+ ];
32904
+ return {
32905
+ content: [{ type: "text", text: lines.join("\n") }],
32906
+ structuredContent: { suggestions, schema: schema2 }
32907
+ };
32908
+ }
32909
+ async function suggestDashboardHandler(args) {
32910
+ const { data, intents, maxPanels, diversifyByFamily, audience } = args;
32911
+ const dashboard = (0, import_ai3.suggestDashboard)(data, {
32912
+ intents,
32913
+ maxPanels: maxPanels ?? 6,
32914
+ diversifyByFamily: diversifyByFamily !== false,
32915
+ audience
32916
+ });
32917
+ const lines = [];
32918
+ lines.push(`Dashboard: ${dashboard.panels.length} panels covering ${dashboard.intentsCovered.join(", ") || "\u2014"}`);
32919
+ if (dashboard.intentsMissing.length) {
32920
+ lines.push(`Intents this data couldn't fill: ${dashboard.intentsMissing.join(", ")}`);
32921
+ }
32922
+ lines.push("");
32923
+ for (let i = 0; i < dashboard.panels.length; i++) {
32924
+ const { intent, suggestion } = dashboard.panels[i];
32925
+ const variantTag = suggestion.variant ? ` / ${suggestion.variant.label}` : "";
32926
+ lines.push(`${i + 1}. [${intent}] ${suggestion.component}${variantTag} (score ${suggestion.score.toFixed(1)}/5)`);
32927
+ if (suggestion.reasons.length) lines.push(` ${suggestion.reasons.join("; ")}`);
32928
+ }
32929
+ if (dashboard.stretchPanels.length > 0) {
32930
+ lines.push("");
32931
+ lines.push(`Stretch picks (audience-unfamiliar but fitting):`);
32932
+ for (const stretch of dashboard.stretchPanels) {
32933
+ const variantTag = stretch.suggestion.variant ? ` / ${stretch.suggestion.variant.label}` : "";
32934
+ lines.push(` ${stretch.suggestion.component}${variantTag} (familiarity ${stretch.familiarity}) \u2014 ${stretch.rationale}`);
32935
+ }
32936
+ }
32937
+ return {
32938
+ content: [{ type: "text", text: lines.join("\n") }],
32939
+ structuredContent: dashboard
32940
+ };
32941
+ }
32942
+ async function suggestStretchChartsHandler(args) {
32943
+ const { data, audience, intent, maxResults } = args;
32944
+ const intentArg = Array.isArray(intent) ? intent : intent ? [intent] : void 0;
32945
+ const stretches = (0, import_ai3.suggestStretchCharts)(data, {
32946
+ audience,
32947
+ intent: intentArg,
32948
+ maxResults: maxResults ?? 5
32949
+ });
32950
+ const lines = [
32951
+ `${stretches.length} stretch pick${stretches.length === 1 ? "" : "s"} for "${audience.name ?? "audience"}":`,
32952
+ "",
32953
+ ...stretches.map((s, i) => {
32954
+ const variantTag = s.suggestion.variant ? ` / ${s.suggestion.variant.label}` : "";
32955
+ const replacing = s.replacing ? ` (could replace ${s.replacing})` : "";
32956
+ return `${i + 1}. ${s.suggestion.component}${variantTag} (familiarity ${s.familiarity}/5)${replacing}
32957
+ ${s.rationale}`;
32958
+ })
32959
+ ];
32960
+ return {
32961
+ content: [{ type: "text", text: lines.join("\n") }],
32962
+ structuredContent: { stretches, audience: audience.name ?? null }
32963
+ };
32964
+ }
32965
+ async function repairChartConfigHandler(args) {
32966
+ const { component, data, intent, maxAlternatives } = args;
32967
+ const intentArg = Array.isArray(intent) ? intent : intent ? [intent] : void 0;
32968
+ const result = (0, import_ai3.repairChartConfig)(component, data, {
32969
+ intent: intentArg,
32970
+ maxAlternatives: maxAlternatives ?? 3
32971
+ });
32972
+ const lines = [];
32973
+ if (result.status === "ok") {
32974
+ lines.push(`\u2705 ${component} fits this dataset \u2014 no repair needed.`);
32975
+ } else if (result.status === "alternative") {
32976
+ lines.push(`\u26A0 ${component} doesn't fit: ${result.reason}`);
32977
+ lines.push("");
32978
+ lines.push(`Alternatives that fit${intentArg ? ` (ranked by intent: ${intentArg.join(", ")})` : ""}:`);
32979
+ for (let i = 0; i < result.alternatives.length; i++) {
32980
+ const s = result.alternatives[i];
32981
+ const variantTag = s.variant ? ` / ${s.variant.label}` : "";
32982
+ const reasons = s.reasons.length ? ` \u2014 ${s.reasons.join("; ")}` : "";
32983
+ lines.push(`${i + 1}. ${s.component}${variantTag} (score ${s.score.toFixed(1)}/5)${reasons}`);
32984
+ }
32985
+ } else {
32986
+ lines.push(`\u2753 No capability registered for "${component}". Closest matches:`);
32987
+ for (let i = 0; i < result.alternatives.length; i++) {
32988
+ const s = result.alternatives[i];
32989
+ lines.push(`${i + 1}. ${s.component} (${s.family}, score ${s.score.toFixed(1)}/5)`);
32990
+ }
32991
+ }
32992
+ return {
32993
+ content: [{ type: "text", text: lines.join("\n") }],
32994
+ structuredContent: result
32995
+ };
32996
+ }
32997
+ async function interrogateChartHandler(args) {
32998
+ const { component, props, query } = args;
32999
+ const data = props.data || props.nodes || [];
33000
+ const summary = (0, import_ai3.summarizeData)(data);
33001
+ const content = [
33002
+ { type: "text", text: `Statistical summary for ${component}:
33003
+ ${JSON.stringify(summary, null, 2)}` }
33004
+ ];
33005
+ if (query) {
33006
+ content.push({
33007
+ type: "text",
33008
+ text: `User Question: "${query}"
33009
+
33010
+ Contextual instructions:
33011
+ 1. Analyze the statistical summary to answer the question.
33012
+ 2. Return a natural language response.
33013
+ 3. Optionally suggest a JSON array of Semiotic annotations to visually highlight the answer on the chart (e.g. { type: "callout", x: "Mar", y: 1500, label: "Peak month" }).
33014
+ 4. Use the accessor names from the provided props (e.g. xAccessor, yAccessor).`
33015
+ });
33016
+ }
33017
+ return { content, structuredContent: { summary, component, props } };
33018
+ }
33019
+ async function groundChartHandler(args) {
33020
+ const component = args.component;
33021
+ const props = args.props ?? {};
33022
+ if (!component) {
33023
+ return {
33024
+ content: [{ type: "text", text: "Missing 'component' field. Provide { component: 'LineChart', props: { ... } }." }],
33025
+ isError: true
33026
+ };
33027
+ }
33028
+ const capability = (0, import_ai3.getCapability)(component);
33029
+ const grounding = (0, import_ai3.buildReaderGrounding)(component, props, { capability });
33030
+ const nodeCount = grounding.structure ? (0, import_ai3.countNodes)(grounding.structure) : 0;
33031
+ const lines = [
33032
+ `Reader grounding for ${component} \u2014 the payload an agent reads to interpret this chart without seeing it:`,
33033
+ "",
33034
+ `L1\u2013L3 (description): ${grounding.description.text}`,
33035
+ grounding.intent ? `L4 (intent \xB7 ${grounding.intent.act}): ${grounding.intent.sentence}` : "L4 (intent): not resolved (no capability for this component).",
33036
+ "",
33037
+ `Structure: ${nodeCount} navigable node(s) (chart \u2192 axes/series \u2192 datum) in structuredContent.structure.`,
33038
+ "",
33039
+ "Combined text:",
33040
+ grounding.text
33041
+ ];
33042
+ return {
33043
+ content: [{ type: "text", text: lines.join("\n") }],
33044
+ structuredContent: grounding
33045
+ };
33046
+ }
32746
33047
  function createServer2() {
32747
33048
  const srv = new McpServer({
32748
33049
  name: "semiotic",
@@ -32822,7 +33123,7 @@ function createServer2() {
32822
33123
  "Use this MCP workflow:",
32823
33124
  "1. Read semiotic://system-prompt for compact API rules and pitfalls.",
32824
33125
  "2. Read semiotic://behavior-contracts for semantic rules that schema shape alone cannot express.",
32825
- "3. If no component is specified, call suggestChart with 1-5 representative sample rows and the intent.",
33126
+ "3. If no component is specified, call suggestCharts with representative rows and the intent.",
32826
33127
  "4. Call getSchema for the selected component before writing JSX or renderChart props.",
32827
33128
  '5. Call diagnoseConfig with usageMode="static" for renderChart/static data, or usageMode="push" for ref-based React code that intentionally omits data.',
32828
33129
  "6. Fix all diagnoseConfig errors before presenting code.",
@@ -32909,6 +33210,18 @@ function createServer2() {
32909
33210
  },
32910
33211
  diagnoseConfigHandler
32911
33212
  );
33213
+ srv.tool(
33214
+ "auditAccessibility",
33215
+ "Audit a Semiotic chart configuration against the Chartability (POUR-CAF) accessibility framework \u2014 Perceivable, Operable, Understandable, Robust, Compromising, Assistive, Flexible. Statically grades the config (no DOM/AT): credits the built-ins every HOC ships (keyboard nav, focus ring, skip link, screen-reader data table, reduced-motion + forced-colors, shareable state), flags author-actionable gaps (missing title/description/summary, low contrast, small text, color-only encoding, undescribed trends, data density), and routes everything that needs real assistive-technology testing to a 'manual' item. Returns a per-principle report with the 14 critical heuristics marked. Pass inChartContainer=true to credit data-download/share affordances. Pair with manual NVDA/JAWS/VoiceOver testing \u2014 Chartability is not a pass/fail certification.",
33216
+ {
33217
+ component: external_exports3.string().describe("Chart component name, e.g. 'LineChart'"),
33218
+ props: external_exports3.record(external_exports3.string(), external_exports3.unknown()).optional().describe("Chart props object, e.g. { data: [...], xAccessor: 'x', title: '...' }."),
33219
+ inChartContainer: external_exports3.boolean().optional().describe("True if the chart is (or will be) wrapped in a ChartContainer exposing data-download/copy-config actions."),
33220
+ describe: external_exports3.boolean().optional().describe("True if ChartContainer's describe option (auto-generated L1\u2013L3 description via describeChart) is enabled \u2014 passes the 'features described' heuristic."),
33221
+ navigable: external_exports3.boolean().optional().describe("True if ChartContainer's navigable option (structured navigation tree via buildNavigationTree) is enabled \u2014 passes the 'navigable structure' heuristic.")
33222
+ },
33223
+ auditAccessibilityHandler
33224
+ );
32912
33225
  srv.tool(
32913
33226
  "reportIssue",
32914
33227
  "Generate a GitHub issue URL for Semiotic bug reports or feature requests. Returns a URL the user can open to submit. For rendering bugs, include the component name, props summary, and any diagnoseConfig output in the body.",
@@ -32927,6 +33240,143 @@ function createServer2() {
32927
33240
  },
32928
33241
  applyThemeHandler
32929
33242
  );
33243
+ srv.tool(
33244
+ "interrogateChart",
33245
+ "Conversational interrogation of a Semiotic chart. Extract a statistical summary and answer natural language questions about the data, trends, and outliers. Returns a summary and guidance for an AI to generate a textual answer and visual annotations.",
33246
+ {
33247
+ component: external_exports3.string().describe("Chart component name, e.g. 'LineChart'"),
33248
+ props: external_exports3.record(external_exports3.string(), external_exports3.unknown()).describe("The full chart props including data"),
33249
+ query: external_exports3.string().optional().describe("A natural language question about the chart data")
33250
+ },
33251
+ interrogateChartHandler
33252
+ );
33253
+ srv.tool(
33254
+ "groundChart",
33255
+ "Build the agent-reader grounding payload for a Semiotic chart: the layered L1\u2013L3 natural-language description, the L4 communicative-act sentence (what the chart is asking the reader to do \u2014 'this is an alerting chart; the spike warrants a closer look'), and a structured navigation tree (chart \u2192 axes/series \u2192 datum). This is the documented thing an LLM reads to interpret a chart faithfully without seeing the pixels \u2014 the reader-side complement to a capability descriptor. The L4 act is resolved from the chart's registered capability. Returns prose plus the full structured payload (description/intent/structure/text).",
33256
+ {
33257
+ component: external_exports3.string().describe("Chart component name, e.g. 'LineChart'"),
33258
+ props: external_exports3.record(external_exports3.string(), external_exports3.unknown()).describe("The full chart props including data")
33259
+ },
33260
+ groundChartHandler
33261
+ );
33262
+ srv.tool(
33263
+ "suggestStreamCharts",
33264
+ "Recommend realtime/streaming Semiotic charts for a schema (not row data). Pass a schema describing field types plus optional throughput ('low'|'medium'|'high') and retention ('windowed'|'cumulative') hints; the engine ranks realtime charts (RealtimeLineChart, RealtimeHistogram, RealtimeHeatmap, RealtimeWaterfallChart, RealtimeSwarmChart, TemporalHistogram) by their fit. Use when the user is wiring up a live dashboard or monitoring view rather than visualizing a bounded dataset.",
33265
+ {
33266
+ schema: external_exports3.object({
33267
+ fields: external_exports3.array(
33268
+ external_exports3.object({
33269
+ name: external_exports3.string(),
33270
+ kind: external_exports3.enum(["numeric", "categorical", "date", "boolean"]),
33271
+ role: external_exports3.enum(["x", "y", "value", "category", "series", "size"]).optional()
33272
+ })
33273
+ ),
33274
+ throughput: external_exports3.enum(["low", "medium", "high"]).optional(),
33275
+ retention: external_exports3.enum(["windowed", "cumulative"]).optional()
33276
+ }).describe("Stream schema \u2014 fields plus throughput/retention hints. No row data."),
33277
+ intent: external_exports3.union([external_exports3.string(), external_exports3.array(external_exports3.string())]).optional().describe("Ranking intent."),
33278
+ maxResults: external_exports3.number().int().min(1).max(20).optional()
33279
+ },
33280
+ suggestStreamChartsHandler
33281
+ );
33282
+ srv.tool(
33283
+ "suggestDashboard",
33284
+ "Generate a dashboard of complementary chart panels for a dataset \u2014 each panel answers a distinct analytical intent (trend, rank, distribution, correlation, etc.) and the engine diversifies by chart family by default. Heuristic only; no LLM call. Use when the user asks 'show me this data' or 'build me a dashboard' rather than picking one chart.",
33285
+ {
33286
+ data: external_exports3.array(external_exports3.record(external_exports3.string(), external_exports3.unknown())).describe("Row data \u2014 array of objects."),
33287
+ intents: external_exports3.array(external_exports3.string()).optional().describe("Intents to cover. Omit to let the engine pick based on the data shape."),
33288
+ maxPanels: external_exports3.number().int().min(1).max(12).optional().describe("Maximum panels (default 6)."),
33289
+ diversifyByFamily: external_exports3.boolean().optional().describe("Prefer not to repeat chart families across panels (default true).")
33290
+ },
33291
+ suggestDashboardHandler
33292
+ );
33293
+ srv.tool(
33294
+ "suggestStretchCharts",
33295
+ "Recommend literacy-growth chart picks for a dataset given an AudienceProfile. Returns charts the data supports but the audience is unfamiliar with (familiarity \u2264 3, or \u2264 4 at exposureLevel 2), each paired with the familiar chart it could substitute for and a rationale. Use when the consumer wants to gently expose users to less familiar but more analytically appropriate visualizations.",
33296
+ {
33297
+ data: external_exports3.array(external_exports3.record(external_exports3.string(), external_exports3.unknown())).describe("Row data."),
33298
+ audience: external_exports3.object({
33299
+ name: external_exports3.string().optional(),
33300
+ familiarity: external_exports3.record(external_exports3.string(), external_exports3.number()).optional(),
33301
+ targets: external_exports3.record(
33302
+ external_exports3.string(),
33303
+ external_exports3.object({
33304
+ direction: external_exports3.enum(["increase", "decrease"]),
33305
+ weight: external_exports3.number().int().min(1).max(3).optional(),
33306
+ reason: external_exports3.string().optional()
33307
+ })
33308
+ ).optional(),
33309
+ exposureLevel: external_exports3.union([external_exports3.literal(0), external_exports3.literal(1), external_exports3.literal(2)]).optional(),
33310
+ receptionModality: external_exports3.enum(["visual", "screen-reader", "sonified", "agent"]).optional().describe("Reception channel \u2014 see suggestCharts.")
33311
+ }).describe("Audience profile \u2014 familiarity, targets, exposure level, reception modality."),
33312
+ intent: external_exports3.union([external_exports3.string(), external_exports3.array(external_exports3.string())]).optional(),
33313
+ maxResults: external_exports3.number().int().min(1).max(20).optional()
33314
+ },
33315
+ suggestStretchChartsHandler
33316
+ );
33317
+ srv.tool(
33318
+ "repairChartConfig",
33319
+ "Validate that a chart component is a sensible choice for a dataset, and if not, propose alternatives that fit. Use when a user asks for a specific chart and you want to confirm it's appropriate, or when you've drafted a config and want to verify it. Returns either ok (no change needed), alternative (chart doesn't fit; here are ranked replacements with rationale), or unknown (no capability registered).",
33320
+ {
33321
+ component: external_exports3.string().describe("Chart component name to validate, e.g. 'PieChart'"),
33322
+ data: external_exports3.array(external_exports3.record(external_exports3.string(), external_exports3.unknown())).describe("Row data \u2014 array of objects."),
33323
+ intent: external_exports3.union([external_exports3.string(), external_exports3.array(external_exports3.string())]).optional().describe("User intent \u2014 informs ranking of alternatives when the chart doesn't fit."),
33324
+ maxAlternatives: external_exports3.number().int().min(1).max(10).optional().describe("Cap on alternatives returned (default 3).")
33325
+ },
33326
+ repairChartConfigHandler
33327
+ );
33328
+ srv.tool(
33329
+ "proposeChartVariants",
33330
+ "Propose and score chart variants for a selected Semiotic component. Uses the capability registry plus heuristic variant discovery: registered variants, conservative transforms, and same-intent cross-family alternatives. Returns ranked proposals with fit/novelty/risk scores, rationale, and ready-to-use props. Use after suggestCharts when an agent wants to actively explore variants rather than stop at the first chart recommendation.",
33331
+ {
33332
+ component: external_exports3.string().describe("Base chart component to vary, e.g. 'LineChart', 'BarChart', or 'BoxPlot'."),
33333
+ props: external_exports3.record(external_exports3.string(), external_exports3.unknown()).optional().describe("Existing chart props. If props.data is present it is profiled; network/hierarchy/geo object data can be passed here as raw input."),
33334
+ data: external_exports3.array(external_exports3.record(external_exports3.string(), external_exports3.unknown())).optional().describe("Row data to profile. Overrides props.data when present."),
33335
+ intent: external_exports3.union([external_exports3.string(), external_exports3.array(external_exports3.string())]).optional().describe("Ranking intent(s), e.g. trend, distribution, rank, compare-categories, composition-over-time."),
33336
+ maxResults: external_exports3.number().int().min(1).max(20).optional().describe("Cap on proposals returned (default 8)."),
33337
+ audience: external_exports3.object({
33338
+ name: external_exports3.string().optional(),
33339
+ familiarity: external_exports3.record(external_exports3.string(), external_exports3.number()).optional(),
33340
+ targets: external_exports3.record(
33341
+ external_exports3.string(),
33342
+ external_exports3.object({
33343
+ direction: external_exports3.enum(["increase", "decrease"]),
33344
+ weight: external_exports3.number().int().min(1).max(3).optional(),
33345
+ reason: external_exports3.string().optional()
33346
+ })
33347
+ ).optional(),
33348
+ exposureLevel: external_exports3.union([external_exports3.literal(0), external_exports3.literal(1), external_exports3.literal(2)]).optional(),
33349
+ receptionModality: external_exports3.enum(["visual", "screen-reader", "sonified", "agent"]).optional().describe("Reception channel \u2014 see suggestCharts.")
33350
+ }).optional().describe("Audience profile \u2014 familiarity, adoption targets, exposure level, and reception modality.")
33351
+ },
33352
+ proposeChartVariantsHandler
33353
+ );
33354
+ srv.tool(
33355
+ "suggestCharts",
33356
+ "Recommend Semiotic charts for a dataset using heuristic capability descriptors. Each chart declares which data shapes it serves and which intents (trend, compare-categories, distribution, correlation, part-to-whole, etc.) it answers \u2014 the engine returns a ranked list with scores, reasons, caveats, and ready-to-use props. Heuristic only; no LLM call. Use the result as structured context when answering 'what chart should I use?' or generating chart code.",
33357
+ {
33358
+ data: external_exports3.array(external_exports3.record(external_exports3.string(), external_exports3.unknown())).describe("Row data \u2014 array of objects."),
33359
+ intent: external_exports3.union([external_exports3.string(), external_exports3.array(external_exports3.string())]).optional().describe("Ranking intent. One of: trend, compare-series, compare-categories, rank, part-to-whole, distribution, correlation, flow, hierarchy, geo, outlier-detection, composition-over-time, change-detection. Custom intents accepted."),
33360
+ maxResults: external_exports3.number().int().min(1).max(40).optional().describe("Cap on suggestions returned (default 8)."),
33361
+ allow: external_exports3.array(external_exports3.string()).optional().describe("Restrict to these component names."),
33362
+ deny: external_exports3.array(external_exports3.string()).optional().describe("Exclude these component names."),
33363
+ audience: external_exports3.object({
33364
+ name: external_exports3.string().optional(),
33365
+ familiarity: external_exports3.record(external_exports3.string(), external_exports3.number()).optional(),
33366
+ targets: external_exports3.record(
33367
+ external_exports3.string(),
33368
+ external_exports3.object({
33369
+ direction: external_exports3.enum(["increase", "decrease"]),
33370
+ weight: external_exports3.number().int().min(1).max(3).optional(),
33371
+ reason: external_exports3.string().optional()
33372
+ })
33373
+ ).optional(),
33374
+ exposureLevel: external_exports3.union([external_exports3.literal(0), external_exports3.literal(1), external_exports3.literal(2)]).optional(),
33375
+ receptionModality: external_exports3.enum(["visual", "screen-reader", "sonified", "agent"]).optional().describe("Reception channel. A non-visual value down-ranks charts the audience can't receive in that channel (e.g. a many-slice pie for a screen reader) and adds receivability caveats.")
33376
+ }).optional().describe("Audience profile \u2014 familiarity, adoption targets, exposure level, and reception modality.")
33377
+ },
33378
+ suggestChartsHandler
33379
+ );
32930
33380
  return srv;
32931
33381
  }
32932
33382
  var cliArgs = process.argv.slice(2);
@@ -32973,7 +33423,7 @@ async function main() {
32973
33423
  });
32974
33424
  httpServer.listen(port, () => {
32975
33425
  console.error(`Semiotic MCP server (HTTP) listening on http://localhost:${port}`);
32976
- console.error("Tools: getSchema, suggestChart, renderChart, diagnoseConfig, reportIssue, applyTheme");
33426
+ console.error("Tools: getSchema, suggestChart, suggestCharts, proposeChartVariants, suggestStreamCharts, suggestDashboard, suggestStretchCharts, repairChartConfig, renderChart, interrogateChart, groundChart, diagnoseConfig, auditAccessibility, reportIssue, applyTheme");
32977
33427
  console.error("Resources: semiotic://schema, semiotic://components, semiotic://behavior-contracts, semiotic://system-prompt, semiotic://examples");
32978
33428
  });
32979
33429
  } else {
package/ai/examples.md CHANGED
@@ -1181,3 +1181,101 @@ import { BarChart } from "semiotic/ai"
1181
1181
  ```
1182
1182
 
1183
1183
  Key props: `y-threshold` works on vertical ordinal charts. `category-highlight` highlights a category column. `labelPosition` controls label placement.
1184
+
1185
+ ---
1186
+
1187
+ ## Value Charts — One Number Is The Visualization
1188
+
1189
+ ### BigNumber (KPI tile — comparison + target + threshold zones)
1190
+
1191
+ ```jsx
1192
+ import { BigNumber } from "semiotic/value"
1193
+
1194
+ <BigNumber
1195
+ value={1284900}
1196
+ label="Q3 Revenue"
1197
+ caption="Year-to-date bookings"
1198
+ format="currency"
1199
+ precision={0}
1200
+ comparison={{ value: 980000, label: "vs Q2" }}
1201
+ target={{ value: 1500000, label: "Q3 plan" }}
1202
+ thresholds={[
1203
+ { at: -Infinity, level: "danger" },
1204
+ { at: 1000000, level: "warning" },
1205
+ { at: 1300000, level: "success" },
1206
+ ]}
1207
+ />
1208
+ ```
1209
+
1210
+ Key props: `value` (the one number), `format` ("number"|"currency"|"percent"|"compact"|"duration"|fn), `comparison` derives a delta with auto-sentiment, `target` renders "X% of goal", `thresholds` map value to a semantic theme role (`--semiotic-{success|warning|danger|info}`). Suppress chrome via `mode="thumbnail"` for dense grids or `mode="inline"` for prose. Stream via `ref.current.push({ value, time })` — pair with `stalenessThreshold` to dim the card when updates stop.
1211
+
1212
+ ### BigNumber with a Semiotic chart embedded via `trendSlot` (wide / rectangular)
1213
+
1214
+ ```jsx
1215
+ import { BigNumber } from "semiotic/value"
1216
+ import { LineChart } from "semiotic/xy"
1217
+
1218
+ <BigNumber
1219
+ value={1284900}
1220
+ label="Q3 Revenue"
1221
+ format="currency"
1222
+ comparison={{ value: 980000, label: "vs Q2" }}
1223
+ trendSlot={(ctx) => (
1224
+ <LineChart
1225
+ data={[820000, 870000, 920000, 1010000, 1120000, 1284900].map((y, x) => ({ x, y }))}
1226
+ xAccessor="x"
1227
+ yAccessor="y"
1228
+ mode="sparkline"
1229
+ width={260}
1230
+ height={32}
1231
+ color={ctx.color}
1232
+ />
1233
+ )}
1234
+ />
1235
+ ```
1236
+
1237
+ Key props: BigNumber ships NO built-in chart renderer. `trendSlot` accepts any ReactNode (or `(ctx) => ReactNode`); pass a `LineChart`/`AreaChart` in `mode="sparkline"` for wide / rectangular charts. The slot context exposes `ctx.color` (resolved threshold colour) so the embedded chart picks up the BigNumber's level for cohesive theming.
1238
+
1239
+ ### BigNumber with a square Semiotic chart via `chartSlot`
1240
+
1241
+ ```jsx
1242
+ import { BigNumber } from "semiotic/value"
1243
+ import { DonutChart } from "semiotic/ordinal"
1244
+
1245
+ <BigNumber
1246
+ value={1284900}
1247
+ label="Q3 Revenue by region"
1248
+ format="currency"
1249
+ chartSlot={
1250
+ <DonutChart
1251
+ data={[
1252
+ { region: "NA", revenue: 540000 },
1253
+ { region: "EU", revenue: 420000 },
1254
+ { region: "APAC", revenue: 324900 },
1255
+ ]}
1256
+ categoryAccessor="region"
1257
+ valueAccessor="revenue"
1258
+ width={120}
1259
+ height={120}
1260
+ innerRadius={32}
1261
+ />
1262
+ }
1263
+ />
1264
+ ```
1265
+
1266
+ Key props: `chartSlot` is the square-aspect counterpart to `trendSlot`. The card splits horizontally — text content on the left, square chart on the right (DonutChart / PieChart / Scatterplot / Treemap / CirclePack). Pass both `trendSlot` and `chartSlot` to get the square chart on the right and the wide sparkline at the bottom.
1267
+
1268
+ ### BigNumber (inverted direction — lower is better)
1269
+
1270
+ ```jsx
1271
+ import { BigNumber } from "semiotic/value"
1272
+
1273
+ <BigNumber
1274
+ value={486}
1275
+ label="P99 latency"
1276
+ suffix=" ms"
1277
+ comparison={{ value: 410, label: "vs last week", direction: "lower-is-better" }}
1278
+ />
1279
+ ```
1280
+
1281
+ Key props: `direction: "lower-is-better"` flips sentiment colouring — a value that went UP now reads as negative (danger). Same pattern for error rate, churn, complaint count, etc.