semiotic 3.6.0 → 3.7.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.
- package/CLAUDE.md +192 -228
- package/README.md +70 -17
- package/ai/cli.js +41 -0
- package/ai/componentMetadata.cjs +11 -2
- package/ai/dist/mcp-server.js +625 -10
- package/ai/examples.md +98 -0
- package/ai/schema.json +581 -1
- package/ai/system-prompt.md +7 -4
- package/dist/components/AccessibleNavTree.d.ts +26 -0
- package/dist/components/Annotation.d.ts +41 -15
- package/dist/components/CategoryColors.d.ts +1 -1
- package/dist/components/ChartContainer.d.ts +32 -2
- package/dist/components/ChartGrid.d.ts +1 -1
- package/dist/components/ContextLayout.d.ts +1 -1
- package/dist/components/DataSummaryContext.d.ts +1 -1
- package/dist/components/DetailsPanel.d.ts +1 -1
- package/dist/components/Legend.d.ts +3 -2
- package/dist/components/LinkedCharts.d.ts +1 -1
- package/dist/components/ThemeProvider.d.ts +1 -1
- package/dist/components/Tooltip/FlippingTooltip.d.ts +1 -1
- package/dist/components/Tooltip/Tooltip.d.ts +2 -2
- package/dist/components/ai/annotationProvenance.d.ts +349 -0
- package/dist/components/ai/audienceProfile.d.ts +60 -3
- package/dist/components/ai/chartCapabilityTypes.d.ts +60 -2
- package/dist/components/ai/chartRoles.d.ts +27 -0
- package/dist/components/ai/conversationArc.d.ts +379 -0
- package/dist/components/ai/dataScaleProfile.d.ts +320 -0
- package/dist/components/ai/describeChart.d.ts +114 -0
- package/dist/components/ai/navigationTree.d.ts +45 -0
- package/dist/components/ai/qualityScorecard.d.ts +11 -0
- package/dist/components/ai/readerGrounding.d.ts +70 -0
- package/dist/components/ai/suggestCharts.d.ts +34 -1
- package/dist/components/ai/useConversationArc.d.ts +89 -0
- package/dist/components/ai/useNavigationSync.d.ts +61 -0
- package/dist/components/ai/variantDiscovery.d.ts +168 -0
- package/dist/components/charts/geo/ChoroplethMap.d.ts +2 -1
- package/dist/components/charts/network/CirclePack.d.ts +2 -1
- package/dist/components/charts/network/OrbitDiagram.d.ts +1 -1
- package/dist/components/charts/network/TreeDiagram.d.ts +2 -1
- package/dist/components/charts/network/Treemap.d.ts +2 -1
- package/dist/components/charts/realtime/RealtimeHeatmap.d.ts +3 -0
- package/dist/components/charts/realtime/RealtimeHistogram.d.ts +4 -1
- package/dist/components/charts/realtime/RealtimeLineChart.d.ts +3 -0
- package/dist/components/charts/realtime/RealtimeSwarmChart.d.ts +3 -0
- package/dist/components/charts/realtime/RealtimeWaterfallChart.d.ts +3 -0
- package/dist/components/charts/shared/ChartError.d.ts +2 -1
- package/dist/components/charts/shared/annotationHierarchy.d.ts +42 -0
- package/dist/components/charts/shared/annotationResolvers.d.ts +3 -2
- package/dist/components/charts/shared/annotationRules.d.ts +16 -0
- package/dist/components/charts/shared/annotationTypes.d.ts +14 -0
- package/dist/components/charts/shared/auditAccessibility.d.ts +90 -0
- package/dist/components/charts/shared/chartSpecs.d.ts +2 -3
- package/dist/components/charts/shared/diagnoseConfig.d.ts +4 -6
- package/dist/components/charts/shared/selectionUtils.d.ts +5 -2
- package/dist/components/charts/shared/streamPropsHelpers.d.ts +2 -0
- package/dist/components/charts/shared/types.d.ts +5 -1
- package/dist/components/charts/shared/withChartWrapper.d.ts +1 -1
- package/dist/components/charts/value/BigNumber.capability.d.ts +13 -0
- package/dist/components/charts/value/BigNumber.d.ts +14 -0
- package/dist/components/charts/value/formatting.d.ts +40 -0
- package/dist/components/charts/value/thresholdSparkline.d.ts +40 -0
- package/dist/components/charts/value/types.d.ts +292 -0
- package/dist/components/charts/xy/MinimapChart.d.ts +2 -1
- package/dist/components/charts/xy/ScatterplotMatrix.d.ts +2 -1
- package/dist/components/realtime/lifecycleBands.d.ts +44 -0
- package/dist/components/realtime/types.d.ts +23 -8
- package/dist/components/recipes/annotationDensity.d.ts +69 -0
- package/dist/components/recipes/annotationLayout.d.ts +93 -0
- package/dist/components/semiotic-ai.d.ts +38 -15
- package/dist/components/semiotic-realtime.d.ts +2 -0
- package/dist/components/semiotic-recipes.d.ts +4 -0
- package/dist/components/semiotic-server.d.ts +2 -1
- package/dist/components/semiotic-utils.d.ts +8 -0
- package/dist/components/semiotic-value.d.ts +55 -0
- package/dist/components/semiotic.d.ts +7 -0
- package/dist/components/server/renderEvidence.d.ts +92 -0
- package/dist/components/server/renderToStaticSVG.d.ts +14 -1
- package/dist/components/server/staticAnnotations.d.ts +2 -0
- package/dist/components/stream/AccessibleDataTable.d.ts +15 -6
- package/dist/components/stream/FocusRing.d.ts +2 -1
- package/dist/components/stream/MarginalGraphics.d.ts +2 -1
- package/dist/components/stream/NetworkSVGOverlay.d.ts +13 -6
- package/dist/components/stream/OrdinalBrushOverlay.d.ts +19 -1
- package/dist/components/stream/OrdinalSVGOverlay.d.ts +4 -2
- package/dist/components/stream/SVGOverlay.d.ts +5 -2
- package/dist/components/stream/XYBrushOverlay.d.ts +21 -1
- package/dist/components/stream/geoTypes.d.ts +3 -0
- package/dist/components/stream/networkTypes.d.ts +2 -0
- package/dist/components/stream/ordinalTypes.d.ts +2 -0
- package/dist/components/stream/types.d.ts +2 -0
- package/dist/geo.min.js +1 -1
- package/dist/geo.module.min.js +1 -1
- package/dist/network.min.js +1 -1
- package/dist/network.module.min.js +1 -1
- package/dist/ordinal.min.js +1 -1
- package/dist/ordinal.module.min.js +1 -1
- package/dist/realtime.min.js +1 -1
- package/dist/realtime.module.min.js +1 -1
- package/dist/semiotic-ai.d.ts +38 -15
- package/dist/semiotic-ai.min.js +1 -1
- package/dist/semiotic-ai.module.min.js +1 -1
- package/dist/semiotic-realtime.d.ts +2 -0
- package/dist/semiotic-recipes.d.ts +4 -0
- package/dist/semiotic-recipes.min.js +1 -1
- package/dist/semiotic-recipes.module.min.js +1 -1
- package/dist/semiotic-server.d.ts +2 -1
- package/dist/semiotic-themes.min.js +1 -1
- package/dist/semiotic-themes.module.min.js +1 -1
- package/dist/semiotic-utils.d.ts +8 -0
- package/dist/semiotic-utils.min.js +1 -1
- package/dist/semiotic-utils.module.min.js +1 -1
- package/dist/semiotic-value.d.ts +55 -0
- package/dist/semiotic-value.min.js +2 -0
- package/dist/semiotic-value.module.min.js +2 -0
- package/dist/semiotic.d.ts +7 -0
- package/dist/semiotic.min.js +1 -1
- package/dist/semiotic.module.min.js +1 -1
- package/dist/server.min.js +1 -1
- package/dist/server.module.min.js +1 -1
- package/dist/xy.min.js +1 -1
- package/dist/xy.module.min.js +1 -1
- package/package.json +19 -5
package/ai/dist/mcp-server.js
CHANGED
|
@@ -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
|
}
|
|
@@ -32429,6 +32433,7 @@ ${errors.join("\n")}`
|
|
|
32429
32433
|
}
|
|
32430
32434
|
|
|
32431
32435
|
// ai/mcp-server.ts
|
|
32436
|
+
var import_server2 = require("semiotic/server");
|
|
32432
32437
|
var import_ai3 = require("semiotic/ai");
|
|
32433
32438
|
var {
|
|
32434
32439
|
componentIndexFromSchema,
|
|
@@ -32454,6 +32459,8 @@ for (const tool of schema.tools) {
|
|
|
32454
32459
|
var allComponentNames = Object.keys(schemaByComponent).sort();
|
|
32455
32460
|
var componentNames = Object.keys(COMPONENT_REGISTRY).sort();
|
|
32456
32461
|
var REPO = "nteract/semiotic";
|
|
32462
|
+
var SEMIOTIC_CHART_WIDGET_URI = "ui://semiotic/chart-widget.html";
|
|
32463
|
+
var MCP_APP_MIME_TYPE = "text/html;profile=mcp-app";
|
|
32457
32464
|
function aiFilePath(fileName) {
|
|
32458
32465
|
return path.resolve(__dirname, "..", fileName);
|
|
32459
32466
|
}
|
|
@@ -32472,6 +32479,30 @@ function textResource(uri, mimeType, text) {
|
|
|
32472
32479
|
}]
|
|
32473
32480
|
};
|
|
32474
32481
|
}
|
|
32482
|
+
function appResource(uri, text) {
|
|
32483
|
+
return {
|
|
32484
|
+
contents: [{
|
|
32485
|
+
uri: uri.href,
|
|
32486
|
+
mimeType: MCP_APP_MIME_TYPE,
|
|
32487
|
+
text,
|
|
32488
|
+
_meta: {
|
|
32489
|
+
ui: {
|
|
32490
|
+
prefersBorder: true,
|
|
32491
|
+
csp: {
|
|
32492
|
+
connectDomains: [],
|
|
32493
|
+
resourceDomains: []
|
|
32494
|
+
}
|
|
32495
|
+
},
|
|
32496
|
+
"openai/widgetDescription": "Interactive Semiotic chart preview rendered by the semiotic-mcp server.",
|
|
32497
|
+
"openai/widgetPrefersBorder": true,
|
|
32498
|
+
"openai/widgetCSP": {
|
|
32499
|
+
connect_domains: [],
|
|
32500
|
+
resource_domains: []
|
|
32501
|
+
}
|
|
32502
|
+
}
|
|
32503
|
+
}]
|
|
32504
|
+
};
|
|
32505
|
+
}
|
|
32475
32506
|
function promptMessage(text) {
|
|
32476
32507
|
return {
|
|
32477
32508
|
messages: [{
|
|
@@ -32483,6 +32514,279 @@ function promptMessage(text) {
|
|
|
32483
32514
|
}]
|
|
32484
32515
|
};
|
|
32485
32516
|
}
|
|
32517
|
+
function stripUnsafeSvg(svg) {
|
|
32518
|
+
return svg.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "").replace(/\son[a-z]+\s*=\s*"[^"]*"/gi, "").replace(/\son[a-z]+\s*=\s*'[^']*'/gi, "").replace(/\s(href|xlink:href)\s*=\s*(["'])\s*javascript:[^"']*\2/gi, "");
|
|
32519
|
+
}
|
|
32520
|
+
function parseRenderEvidence(result) {
|
|
32521
|
+
const evidenceText = result.content.find((block) => block.text.startsWith("Render evidence:\n"))?.text;
|
|
32522
|
+
if (!evidenceText) return null;
|
|
32523
|
+
try {
|
|
32524
|
+
return JSON.parse(evidenceText.replace(/^Render evidence:\n/, ""));
|
|
32525
|
+
} catch {
|
|
32526
|
+
return null;
|
|
32527
|
+
}
|
|
32528
|
+
}
|
|
32529
|
+
function chartTitleFromProps(component, props) {
|
|
32530
|
+
return typeof props.title === "string" && props.title.trim() ? props.title.trim() : component;
|
|
32531
|
+
}
|
|
32532
|
+
function chartDatumCount(props) {
|
|
32533
|
+
if (Array.isArray(props.data)) return props.data.length;
|
|
32534
|
+
if (Array.isArray(props.nodes)) return props.nodes.length;
|
|
32535
|
+
if (Array.isArray(props.edges)) return props.edges.length;
|
|
32536
|
+
if (Array.isArray(props.links)) return props.links.length;
|
|
32537
|
+
return null;
|
|
32538
|
+
}
|
|
32539
|
+
function renderSemioticChartWidgetHTML() {
|
|
32540
|
+
return `
|
|
32541
|
+
<!doctype html>
|
|
32542
|
+
<html lang="en">
|
|
32543
|
+
<head>
|
|
32544
|
+
<meta charset="utf-8" />
|
|
32545
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
32546
|
+
<style>
|
|
32547
|
+
:root {
|
|
32548
|
+
color-scheme: light dark;
|
|
32549
|
+
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
32550
|
+
--bg: Canvas;
|
|
32551
|
+
--fg: CanvasText;
|
|
32552
|
+
--muted: color-mix(in srgb, CanvasText 62%, Canvas 38%);
|
|
32553
|
+
--border: color-mix(in srgb, CanvasText 16%, Canvas 84%);
|
|
32554
|
+
--panel: color-mix(in srgb, Canvas 94%, CanvasText 6%);
|
|
32555
|
+
--accent: #2f6fed;
|
|
32556
|
+
}
|
|
32557
|
+
* { box-sizing: border-box; }
|
|
32558
|
+
body { margin: 0; background: var(--bg); color: var(--fg); }
|
|
32559
|
+
main { display: grid; gap: 10px; padding: 12px; min-height: 100vh; }
|
|
32560
|
+
header { display: flex; align-items: start; justify-content: space-between; gap: 10px; }
|
|
32561
|
+
h1 { font-size: 16px; line-height: 1.25; margin: 0; font-weight: 650; }
|
|
32562
|
+
.summary { margin-top: 3px; color: var(--muted); font-size: 12px; line-height: 1.35; }
|
|
32563
|
+
.toolbar { display: flex; align-items: center; justify-content: flex-end; gap: 6px; flex-wrap: wrap; }
|
|
32564
|
+
button {
|
|
32565
|
+
border: 1px solid var(--border);
|
|
32566
|
+
background: var(--panel);
|
|
32567
|
+
color: var(--fg);
|
|
32568
|
+
border-radius: 6px;
|
|
32569
|
+
font: inherit;
|
|
32570
|
+
font-size: 12px;
|
|
32571
|
+
padding: 6px 8px;
|
|
32572
|
+
cursor: pointer;
|
|
32573
|
+
}
|
|
32574
|
+
button[aria-pressed="true"] {
|
|
32575
|
+
border-color: var(--accent);
|
|
32576
|
+
color: var(--accent);
|
|
32577
|
+
}
|
|
32578
|
+
label { display: inline-flex; align-items: center; gap: 6px; color: var(--muted); font-size: 12px; }
|
|
32579
|
+
input[type="range"] { width: 92px; }
|
|
32580
|
+
.chart-shell {
|
|
32581
|
+
overflow: auto;
|
|
32582
|
+
border: 1px solid var(--border);
|
|
32583
|
+
border-radius: 8px;
|
|
32584
|
+
min-height: 260px;
|
|
32585
|
+
background: white;
|
|
32586
|
+
}
|
|
32587
|
+
.chart-shell.fit svg { width: 100%; height: auto; }
|
|
32588
|
+
.chart {
|
|
32589
|
+
min-width: 360px;
|
|
32590
|
+
padding: 10px;
|
|
32591
|
+
transform-origin: top left;
|
|
32592
|
+
}
|
|
32593
|
+
.chart svg { display: block; max-width: none; }
|
|
32594
|
+
.empty {
|
|
32595
|
+
min-height: 240px;
|
|
32596
|
+
display: grid;
|
|
32597
|
+
place-items: center;
|
|
32598
|
+
color: var(--muted);
|
|
32599
|
+
text-align: center;
|
|
32600
|
+
padding: 24px;
|
|
32601
|
+
}
|
|
32602
|
+
.drawer {
|
|
32603
|
+
display: none;
|
|
32604
|
+
border: 1px solid var(--border);
|
|
32605
|
+
border-radius: 8px;
|
|
32606
|
+
overflow: auto;
|
|
32607
|
+
max-height: 220px;
|
|
32608
|
+
}
|
|
32609
|
+
.drawer.open { display: block; }
|
|
32610
|
+
pre {
|
|
32611
|
+
margin: 0;
|
|
32612
|
+
padding: 10px;
|
|
32613
|
+
font-size: 12px;
|
|
32614
|
+
white-space: pre-wrap;
|
|
32615
|
+
overflow-wrap: anywhere;
|
|
32616
|
+
}
|
|
32617
|
+
table { width: 100%; border-collapse: collapse; font-size: 12px; }
|
|
32618
|
+
th, td { border-bottom: 1px solid var(--border); padding: 6px 8px; text-align: left; vertical-align: top; }
|
|
32619
|
+
th { position: sticky; top: 0; background: var(--panel); }
|
|
32620
|
+
.hover {
|
|
32621
|
+
position: fixed;
|
|
32622
|
+
pointer-events: none;
|
|
32623
|
+
z-index: 10;
|
|
32624
|
+
max-width: 280px;
|
|
32625
|
+
padding: 6px 8px;
|
|
32626
|
+
border-radius: 6px;
|
|
32627
|
+
border: 1px solid var(--border);
|
|
32628
|
+
background: var(--bg);
|
|
32629
|
+
color: var(--fg);
|
|
32630
|
+
box-shadow: 0 8px 24px rgb(0 0 0 / 18%);
|
|
32631
|
+
font-size: 12px;
|
|
32632
|
+
display: none;
|
|
32633
|
+
}
|
|
32634
|
+
@media (max-width: 520px) {
|
|
32635
|
+
main { padding: 10px; }
|
|
32636
|
+
header { display: grid; }
|
|
32637
|
+
.toolbar { justify-content: start; }
|
|
32638
|
+
.chart { min-width: 300px; }
|
|
32639
|
+
}
|
|
32640
|
+
</style>
|
|
32641
|
+
</head>
|
|
32642
|
+
<body>
|
|
32643
|
+
<main>
|
|
32644
|
+
<header>
|
|
32645
|
+
<div>
|
|
32646
|
+
<h1 id="title">Semiotic chart</h1>
|
|
32647
|
+
<div class="summary" id="summary">Waiting for a tool result...</div>
|
|
32648
|
+
</div>
|
|
32649
|
+
<div class="toolbar" aria-label="Chart controls">
|
|
32650
|
+
<button id="fit" type="button" aria-pressed="true">Fit</button>
|
|
32651
|
+
<button id="data" type="button" aria-pressed="false">Data</button>
|
|
32652
|
+
<button id="evidence" type="button" aria-pressed="false">Evidence</button>
|
|
32653
|
+
<label>Zoom <input id="zoom" type="range" min="60" max="180" value="100" /></label>
|
|
32654
|
+
</div>
|
|
32655
|
+
</header>
|
|
32656
|
+
<section id="chartShell" class="chart-shell fit" aria-label="Rendered Semiotic chart">
|
|
32657
|
+
<div id="chart" class="chart"><div class="empty">Ask ChatGPT to render a Semiotic chart.</div></div>
|
|
32658
|
+
</section>
|
|
32659
|
+
<section id="dataDrawer" class="drawer" aria-label="Chart data"></section>
|
|
32660
|
+
<section id="evidenceDrawer" class="drawer" aria-label="Render evidence"><pre id="evidenceText">{}</pre></section>
|
|
32661
|
+
</main>
|
|
32662
|
+
<div id="hover" class="hover" role="status" aria-live="polite"></div>
|
|
32663
|
+
<script>
|
|
32664
|
+
const state = { output: null, meta: null };
|
|
32665
|
+
const titleEl = document.getElementById("title");
|
|
32666
|
+
const summaryEl = document.getElementById("summary");
|
|
32667
|
+
const chartEl = document.getElementById("chart");
|
|
32668
|
+
const chartShell = document.getElementById("chartShell");
|
|
32669
|
+
const dataDrawer = document.getElementById("dataDrawer");
|
|
32670
|
+
const evidenceDrawer = document.getElementById("evidenceDrawer");
|
|
32671
|
+
const evidenceText = document.getElementById("evidenceText");
|
|
32672
|
+
const hover = document.getElementById("hover");
|
|
32673
|
+
const fitButton = document.getElementById("fit");
|
|
32674
|
+
const dataButton = document.getElementById("data");
|
|
32675
|
+
const evidenceButton = document.getElementById("evidence");
|
|
32676
|
+
const zoom = document.getElementById("zoom");
|
|
32677
|
+
|
|
32678
|
+
function html(value) {
|
|
32679
|
+
return String(value ?? "").replace(/[&<>"']/g, (char) => ({
|
|
32680
|
+
"&": "&",
|
|
32681
|
+
"<": "<",
|
|
32682
|
+
">": ">",
|
|
32683
|
+
'"': """,
|
|
32684
|
+
"'": "'"
|
|
32685
|
+
})[char]);
|
|
32686
|
+
}
|
|
32687
|
+
|
|
32688
|
+
function currentPayload() {
|
|
32689
|
+
const openai = window.openai || {};
|
|
32690
|
+
const output = state.output || openai.toolOutput || null;
|
|
32691
|
+
const meta = state.meta || openai.toolResultMetadata || openai.toolResponseMetadata || openai._meta || null;
|
|
32692
|
+
return { output, meta };
|
|
32693
|
+
}
|
|
32694
|
+
|
|
32695
|
+
function sampleRows(meta) {
|
|
32696
|
+
const props = meta?.props || {};
|
|
32697
|
+
if (Array.isArray(props.data)) return props.data.slice(0, 50);
|
|
32698
|
+
if (Array.isArray(props.nodes)) return props.nodes.slice(0, 50);
|
|
32699
|
+
if (Array.isArray(props.edges)) return props.edges.slice(0, 50);
|
|
32700
|
+
if (Array.isArray(props.links)) return props.links.slice(0, 50);
|
|
32701
|
+
return [];
|
|
32702
|
+
}
|
|
32703
|
+
|
|
32704
|
+
function renderTable(rows) {
|
|
32705
|
+
if (!rows.length) return '<pre>No row data was provided in the widget metadata.</pre>';
|
|
32706
|
+
const columns = Array.from(rows.reduce((set, row) => {
|
|
32707
|
+
Object.keys(row || {}).forEach((key) => set.add(key));
|
|
32708
|
+
return set;
|
|
32709
|
+
}, new Set()));
|
|
32710
|
+
return '<table><thead><tr>' + columns.map((col) => '<th>' + html(col) + '</th>').join('') +
|
|
32711
|
+
'</tr></thead><tbody>' + rows.map((row) => '<tr>' + columns.map((col) => '<td>' + html(row?.[col]) + '</td>').join('') + '</tr>').join('') + '</tbody></table>';
|
|
32712
|
+
}
|
|
32713
|
+
|
|
32714
|
+
function render(output, meta) {
|
|
32715
|
+
const payload = output || {};
|
|
32716
|
+
const hidden = meta || {};
|
|
32717
|
+
titleEl.textContent = payload.title || payload.component || "Semiotic chart";
|
|
32718
|
+
summaryEl.textContent = payload.summary || "Rendered by semiotic-mcp.";
|
|
32719
|
+
const svg = hidden.svg || payload.svg;
|
|
32720
|
+
if (svg) {
|
|
32721
|
+
chartEl.innerHTML = svg;
|
|
32722
|
+
} else {
|
|
32723
|
+
chartEl.innerHTML = '<div class="empty">No SVG payload received. The model-visible chart summary is still available above.</div>';
|
|
32724
|
+
}
|
|
32725
|
+
const rows = sampleRows(hidden);
|
|
32726
|
+
dataDrawer.innerHTML = renderTable(rows);
|
|
32727
|
+
evidenceText.textContent = JSON.stringify(payload.evidence || hidden.evidence || {}, null, 2);
|
|
32728
|
+
}
|
|
32729
|
+
|
|
32730
|
+
function rerenderFromGlobals() {
|
|
32731
|
+
const payload = currentPayload();
|
|
32732
|
+
render(payload.output, payload.meta);
|
|
32733
|
+
}
|
|
32734
|
+
|
|
32735
|
+
fitButton.addEventListener("click", () => {
|
|
32736
|
+
const enabled = !chartShell.classList.contains("fit");
|
|
32737
|
+
chartShell.classList.toggle("fit", enabled);
|
|
32738
|
+
fitButton.setAttribute("aria-pressed", String(enabled));
|
|
32739
|
+
});
|
|
32740
|
+
dataButton.addEventListener("click", () => {
|
|
32741
|
+
const open = !dataDrawer.classList.contains("open");
|
|
32742
|
+
dataDrawer.classList.toggle("open", open);
|
|
32743
|
+
dataButton.setAttribute("aria-pressed", String(open));
|
|
32744
|
+
});
|
|
32745
|
+
evidenceButton.addEventListener("click", () => {
|
|
32746
|
+
const open = !evidenceDrawer.classList.contains("open");
|
|
32747
|
+
evidenceDrawer.classList.toggle("open", open);
|
|
32748
|
+
evidenceButton.setAttribute("aria-pressed", String(open));
|
|
32749
|
+
});
|
|
32750
|
+
zoom.addEventListener("input", () => {
|
|
32751
|
+
chartEl.style.transform = 'scale(' + Number(zoom.value) / 100 + ')';
|
|
32752
|
+
chartEl.style.width = (10000 / Number(zoom.value)) + '%';
|
|
32753
|
+
});
|
|
32754
|
+
chartEl.addEventListener("mousemove", (event) => {
|
|
32755
|
+
const target = event.target;
|
|
32756
|
+
if (!(target instanceof Element) || target === chartEl) {
|
|
32757
|
+
hover.style.display = "none";
|
|
32758
|
+
return;
|
|
32759
|
+
}
|
|
32760
|
+
const label = target.getAttribute("aria-label") || target.textContent?.trim() || target.tagName.toLowerCase();
|
|
32761
|
+
hover.textContent = label.slice(0, 180);
|
|
32762
|
+
hover.style.left = Math.min(event.clientX + 12, window.innerWidth - 300) + "px";
|
|
32763
|
+
hover.style.top = Math.min(event.clientY + 12, window.innerHeight - 70) + "px";
|
|
32764
|
+
hover.style.display = "block";
|
|
32765
|
+
});
|
|
32766
|
+
chartEl.addEventListener("mouseleave", () => {
|
|
32767
|
+
hover.style.display = "none";
|
|
32768
|
+
});
|
|
32769
|
+
window.addEventListener("message", (event) => {
|
|
32770
|
+
if (event.source !== window.parent) return;
|
|
32771
|
+
const message = event.data;
|
|
32772
|
+
if (!message || message.jsonrpc !== "2.0") return;
|
|
32773
|
+
if (message.method === "ui/notifications/tool-result") {
|
|
32774
|
+
state.output = message.params?.structuredContent || null;
|
|
32775
|
+
state.meta = message.params?._meta || null;
|
|
32776
|
+
render(state.output, state.meta);
|
|
32777
|
+
}
|
|
32778
|
+
}, { passive: true });
|
|
32779
|
+
window.addEventListener("openai:set_globals", (event) => {
|
|
32780
|
+
const globals = event.detail?.globals || {};
|
|
32781
|
+
state.output = globals.toolOutput || state.output;
|
|
32782
|
+
state.meta = globals.toolResultMetadata || globals.toolResponseMetadata || globals._meta || state.meta;
|
|
32783
|
+
rerenderFromGlobals();
|
|
32784
|
+
}, { passive: true });
|
|
32785
|
+
rerenderFromGlobals();
|
|
32786
|
+
</script>
|
|
32787
|
+
</body>
|
|
32788
|
+
</html>`.trim();
|
|
32789
|
+
}
|
|
32486
32790
|
async function getSchemaHandler(args) {
|
|
32487
32791
|
const component = args.component;
|
|
32488
32792
|
if (!component) {
|
|
@@ -32559,6 +32863,17 @@ async function renderChartHandler(args) {
|
|
|
32559
32863
|
};
|
|
32560
32864
|
}
|
|
32561
32865
|
let svg = result.svg;
|
|
32866
|
+
let evidenceBlock = null;
|
|
32867
|
+
try {
|
|
32868
|
+
const { svg: evidenceSvg, evidence } = (0, import_server2.renderChartWithEvidence)(component, props);
|
|
32869
|
+
svg = evidenceSvg;
|
|
32870
|
+
evidenceBlock = {
|
|
32871
|
+
type: "text",
|
|
32872
|
+
text: `Render evidence:
|
|
32873
|
+
${JSON.stringify(evidence, null, 2)}`
|
|
32874
|
+
};
|
|
32875
|
+
} catch {
|
|
32876
|
+
}
|
|
32562
32877
|
if (theme && Object.keys(theme).length > 0) {
|
|
32563
32878
|
const validVars = Object.entries(theme).filter(([k]) => k.startsWith("--semiotic-")).map(([k, v]) => `${k}: ${v}`).join("; ");
|
|
32564
32879
|
if (validVars) {
|
|
@@ -32572,7 +32887,10 @@ async function renderChartHandler(args) {
|
|
|
32572
32887
|
const pngBuffer = await sharpFn(Buffer.from(svg)).png().toBuffer();
|
|
32573
32888
|
const base643 = pngBuffer.toString("base64");
|
|
32574
32889
|
return {
|
|
32575
|
-
content: [
|
|
32890
|
+
content: [
|
|
32891
|
+
{ type: "text", text: `data:image/png;base64,${base643}` },
|
|
32892
|
+
...evidenceBlock ? [evidenceBlock] : []
|
|
32893
|
+
]
|
|
32576
32894
|
};
|
|
32577
32895
|
} catch (err) {
|
|
32578
32896
|
if (err.code === "MODULE_NOT_FOUND" || err.code === "ERR_MODULE_NOT_FOUND") {
|
|
@@ -32595,7 +32913,52 @@ ${svg}` }],
|
|
|
32595
32913
|
}
|
|
32596
32914
|
}
|
|
32597
32915
|
return {
|
|
32598
|
-
content: [
|
|
32916
|
+
content: [
|
|
32917
|
+
{ type: "text", text: svg },
|
|
32918
|
+
...evidenceBlock ? [evidenceBlock] : []
|
|
32919
|
+
]
|
|
32920
|
+
};
|
|
32921
|
+
}
|
|
32922
|
+
async function renderInteractiveChartHandler(args) {
|
|
32923
|
+
const component = args.component;
|
|
32924
|
+
const props = args.props ?? {};
|
|
32925
|
+
const rendered = await renderChartHandler({
|
|
32926
|
+
component,
|
|
32927
|
+
props,
|
|
32928
|
+
theme: args.theme,
|
|
32929
|
+
format: "svg"
|
|
32930
|
+
});
|
|
32931
|
+
if (rendered.isError) return rendered;
|
|
32932
|
+
const svg = stripUnsafeSvg(rendered.content[0]?.text ?? "");
|
|
32933
|
+
const evidence = parseRenderEvidence(rendered);
|
|
32934
|
+
const title = chartTitleFromProps(component || "Semiotic chart", props);
|
|
32935
|
+
const datumCount = chartDatumCount(props);
|
|
32936
|
+
const summary = [
|
|
32937
|
+
`Rendered ${title} with ${component}.`,
|
|
32938
|
+
datumCount == null ? "No row count was inferred from props." : `${datumCount} input row${datumCount === 1 ? "" : "s"} available in the widget data drawer.`,
|
|
32939
|
+
"Use the widget controls to zoom, fit width, inspect data, and inspect render evidence."
|
|
32940
|
+
].join(" ");
|
|
32941
|
+
return {
|
|
32942
|
+
content: [{
|
|
32943
|
+
type: "text",
|
|
32944
|
+
text: `Rendered ${title} (${component}) as an interactive ChatGPT Apps widget.`
|
|
32945
|
+
}],
|
|
32946
|
+
structuredContent: {
|
|
32947
|
+
component: component ?? "SemioticChart",
|
|
32948
|
+
title,
|
|
32949
|
+
summary,
|
|
32950
|
+
datumCount,
|
|
32951
|
+
evidence
|
|
32952
|
+
},
|
|
32953
|
+
_meta: {
|
|
32954
|
+
component,
|
|
32955
|
+
title,
|
|
32956
|
+
props,
|
|
32957
|
+
theme: args.theme ?? null,
|
|
32958
|
+
svg,
|
|
32959
|
+
evidence,
|
|
32960
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
32961
|
+
}
|
|
32599
32962
|
};
|
|
32600
32963
|
}
|
|
32601
32964
|
function filterUsageModeDiagnoses(component, usageMode, diagnoses) {
|
|
@@ -32643,6 +33006,22 @@ ${contracts}` : msg}` }] };
|
|
|
32643
33006
|
isError: true
|
|
32644
33007
|
};
|
|
32645
33008
|
}
|
|
33009
|
+
async function auditAccessibilityHandler(args) {
|
|
33010
|
+
const component = args.component;
|
|
33011
|
+
const props = args.props ?? {};
|
|
33012
|
+
if (!component) {
|
|
33013
|
+
return {
|
|
33014
|
+
content: [{ type: "text", text: "Missing 'component' field. Provide { component: 'LineChart', props: { ... } }." }],
|
|
33015
|
+
isError: true
|
|
33016
|
+
};
|
|
33017
|
+
}
|
|
33018
|
+
const result = (0, import_ai3.auditAccessibility)(component, props, { inChartContainer: args.inChartContainer === true, describe: args.describe === true, navigable: args.navigable === true });
|
|
33019
|
+
return {
|
|
33020
|
+
content: [{ type: "text", text: (0, import_ai3.formatAccessibilityAudit)(result) }],
|
|
33021
|
+
// Only block on provable critical failures; warnings/manual items are advisory.
|
|
33022
|
+
isError: !result.ok
|
|
33023
|
+
};
|
|
33024
|
+
}
|
|
32646
33025
|
async function reportIssueHandler(args) {
|
|
32647
33026
|
const title = args.title;
|
|
32648
33027
|
const body = args.body;
|
|
@@ -32743,6 +33122,99 @@ Dark-mode presets: ${THEME_PRESET_NAMES.filter((n) => n.includes("dark")).join("
|
|
|
32743
33122
|
content: [{ type: "text", text: usage.join("\n") }]
|
|
32744
33123
|
};
|
|
32745
33124
|
}
|
|
33125
|
+
function profileInputFromVariantArgs(args) {
|
|
33126
|
+
const props = args.props ?? {};
|
|
33127
|
+
if (Array.isArray(args.data)) {
|
|
33128
|
+
return { data: args.data };
|
|
33129
|
+
}
|
|
33130
|
+
if (Array.isArray(props.data)) {
|
|
33131
|
+
return { data: props.data };
|
|
33132
|
+
}
|
|
33133
|
+
if (Array.isArray(props.nodes) && (Array.isArray(props.edges) || Array.isArray(props.links))) {
|
|
33134
|
+
return {
|
|
33135
|
+
data: [],
|
|
33136
|
+
rawInput: {
|
|
33137
|
+
nodes: props.nodes,
|
|
33138
|
+
edges: props.edges ?? props.links
|
|
33139
|
+
}
|
|
33140
|
+
};
|
|
33141
|
+
}
|
|
33142
|
+
if (props.data && typeof props.data === "object" && !Array.isArray(props.data)) {
|
|
33143
|
+
return { data: [], rawInput: props.data };
|
|
33144
|
+
}
|
|
33145
|
+
return { data: [] };
|
|
33146
|
+
}
|
|
33147
|
+
function buildVariantProposalProps(proposal, profile, audience) {
|
|
33148
|
+
if (proposal.buildProps) return proposal.buildProps(profile, audience);
|
|
33149
|
+
const capability = (0, import_ai3.getCapability)(proposal.baseComponent);
|
|
33150
|
+
const variant = proposal.variantKey ? capability?.variants?.find((v) => v.key === proposal.variantKey) : void 0;
|
|
33151
|
+
return capability ? capability.buildProps(profile, variant) : {};
|
|
33152
|
+
}
|
|
33153
|
+
async function proposeChartVariantsHandler(args) {
|
|
33154
|
+
const { component, intent, maxResults, audience } = args;
|
|
33155
|
+
const capability = (0, import_ai3.getCapability)(component);
|
|
33156
|
+
if (!capability) {
|
|
33157
|
+
return {
|
|
33158
|
+
content: [{ type: "text", text: `No chart capability registered for "${component}". Call suggestCharts first to pick from known capability components.` }],
|
|
33159
|
+
isError: true
|
|
33160
|
+
};
|
|
33161
|
+
}
|
|
33162
|
+
const { data, rawInput } = profileInputFromVariantArgs(args);
|
|
33163
|
+
const profile = (0, import_ai3.profileData)(data, { rawInput });
|
|
33164
|
+
const intentArg = Array.isArray(intent) ? intent : intent ? [intent] : void 0;
|
|
33165
|
+
const fitReason = capability.fits(profile);
|
|
33166
|
+
const proposals = (0, import_ai3.proposeVariant)(component, capability, {
|
|
33167
|
+
profile,
|
|
33168
|
+
audience,
|
|
33169
|
+
intent: intentArg,
|
|
33170
|
+
existingVariants: capability.variants
|
|
33171
|
+
});
|
|
33172
|
+
const ranked = proposals.map((proposal) => {
|
|
33173
|
+
const score = (0, import_ai3.evaluateVariantProposal)(proposal, profile, audience, {
|
|
33174
|
+
intent: intentArg,
|
|
33175
|
+
baselineComponent: component
|
|
33176
|
+
});
|
|
33177
|
+
const { buildProps: _buildProps, ...proposalMeta } = proposal;
|
|
33178
|
+
return {
|
|
33179
|
+
proposal: proposalMeta,
|
|
33180
|
+
score,
|
|
33181
|
+
props: buildVariantProposalProps(proposal, profile, audience)
|
|
33182
|
+
};
|
|
33183
|
+
}).sort((a, b) => {
|
|
33184
|
+
if (b.score.fit !== a.score.fit) return b.score.fit - a.score.fit;
|
|
33185
|
+
if (a.score.risk !== b.score.risk) return a.score.risk - b.score.risk;
|
|
33186
|
+
return b.score.novelty - a.score.novelty;
|
|
33187
|
+
}).slice(0, maxResults ?? 8);
|
|
33188
|
+
const lines = [
|
|
33189
|
+
`${ranked.length} variant proposal${ranked.length === 1 ? "" : "s"} for ${component}${intentArg ? ` (intent: ${intentArg.join(", ")})` : ""}:`,
|
|
33190
|
+
...fitReason ? [`Base chart fit warning: ${fitReason}`] : [],
|
|
33191
|
+
"",
|
|
33192
|
+
...ranked.map((entry, i) => {
|
|
33193
|
+
const label = entry.proposal.label ?? entry.proposal.variantKey ?? entry.proposal.id;
|
|
33194
|
+
const tags = entry.proposal.tags?.length ? ` [${entry.proposal.tags.join(", ")}]` : "";
|
|
33195
|
+
const reasons = entry.score.reasons.length ? `
|
|
33196
|
+
${entry.score.reasons.join("; ")}` : "";
|
|
33197
|
+
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}`;
|
|
33198
|
+
})
|
|
33199
|
+
];
|
|
33200
|
+
return {
|
|
33201
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
33202
|
+
structuredContent: {
|
|
33203
|
+
component,
|
|
33204
|
+
profile: {
|
|
33205
|
+
rowCount: profile.rowCount,
|
|
33206
|
+
primary: profile.primary,
|
|
33207
|
+
categoryCount: profile.categoryCount ?? null,
|
|
33208
|
+
seriesCount: profile.seriesCount ?? null,
|
|
33209
|
+
hasHierarchy: profile.hasHierarchy,
|
|
33210
|
+
hasNetwork: profile.hasNetwork,
|
|
33211
|
+
hasGeo: profile.hasGeo
|
|
33212
|
+
},
|
|
33213
|
+
fitReason,
|
|
33214
|
+
proposals: ranked
|
|
33215
|
+
}
|
|
33216
|
+
};
|
|
33217
|
+
}
|
|
32746
33218
|
async function suggestChartsHandler(args) {
|
|
32747
33219
|
const { data, intent, maxResults, allow, deny, audience } = args;
|
|
32748
33220
|
const intentArg = Array.isArray(intent) ? intent : intent ? [intent] : void 0;
|
|
@@ -32903,6 +33375,34 @@ Contextual instructions:
|
|
|
32903
33375
|
}
|
|
32904
33376
|
return { content, structuredContent: { summary, component, props } };
|
|
32905
33377
|
}
|
|
33378
|
+
async function groundChartHandler(args) {
|
|
33379
|
+
const component = args.component;
|
|
33380
|
+
const props = args.props ?? {};
|
|
33381
|
+
if (!component) {
|
|
33382
|
+
return {
|
|
33383
|
+
content: [{ type: "text", text: "Missing 'component' field. Provide { component: 'LineChart', props: { ... } }." }],
|
|
33384
|
+
isError: true
|
|
33385
|
+
};
|
|
33386
|
+
}
|
|
33387
|
+
const capability = (0, import_ai3.getCapability)(component);
|
|
33388
|
+
const grounding = (0, import_ai3.buildReaderGrounding)(component, props, { capability });
|
|
33389
|
+
const nodeCount = grounding.structure ? (0, import_ai3.countNodes)(grounding.structure) : 0;
|
|
33390
|
+
const lines = [
|
|
33391
|
+
`Reader grounding for ${component} \u2014 the payload an agent reads to interpret this chart without seeing it:`,
|
|
33392
|
+
"",
|
|
33393
|
+
`L1\u2013L3 (description): ${grounding.description.text}`,
|
|
33394
|
+
grounding.intent ? `L4 (intent \xB7 ${grounding.intent.act}): ${grounding.intent.sentence}` : "L4 (intent): not resolved (no capability for this component).",
|
|
33395
|
+
"",
|
|
33396
|
+
`Structure: ${nodeCount} navigable node(s) (chart \u2192 axes/series \u2192 datum) in structuredContent.structure.`,
|
|
33397
|
+
"",
|
|
33398
|
+
"Combined text:",
|
|
33399
|
+
grounding.text
|
|
33400
|
+
];
|
|
33401
|
+
return {
|
|
33402
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
33403
|
+
structuredContent: grounding
|
|
33404
|
+
};
|
|
33405
|
+
}
|
|
32906
33406
|
function createServer2() {
|
|
32907
33407
|
const srv = new McpServer({
|
|
32908
33408
|
name: "semiotic",
|
|
@@ -32961,6 +33461,27 @@ function createServer2() {
|
|
|
32961
33461
|
},
|
|
32962
33462
|
(uri) => textResource(uri, "text/markdown", readAIFile("examples.md"))
|
|
32963
33463
|
);
|
|
33464
|
+
srv.registerResource(
|
|
33465
|
+
"semiotic-chatgpt-chart-widget",
|
|
33466
|
+
SEMIOTIC_CHART_WIDGET_URI,
|
|
33467
|
+
{
|
|
33468
|
+
title: "Semiotic ChatGPT Chart Widget",
|
|
33469
|
+
description: "MCP Apps widget template for interactive Semiotic chart previews inside ChatGPT.",
|
|
33470
|
+
mimeType: MCP_APP_MIME_TYPE,
|
|
33471
|
+
_meta: {
|
|
33472
|
+
ui: {
|
|
33473
|
+
prefersBorder: true,
|
|
33474
|
+
csp: {
|
|
33475
|
+
connectDomains: [],
|
|
33476
|
+
resourceDomains: []
|
|
33477
|
+
}
|
|
33478
|
+
},
|
|
33479
|
+
"openai/widgetDescription": "Interactive Semiotic chart preview rendered by the semiotic-mcp server.",
|
|
33480
|
+
"openai/widgetPrefersBorder": true
|
|
33481
|
+
}
|
|
33482
|
+
},
|
|
33483
|
+
(uri) => appResource(uri, renderSemioticChartWidgetHTML())
|
|
33484
|
+
);
|
|
32964
33485
|
srv.registerPrompt(
|
|
32965
33486
|
"build-semiotic-chart",
|
|
32966
33487
|
{
|
|
@@ -33050,7 +33571,7 @@ function createServer2() {
|
|
|
33050
33571
|
);
|
|
33051
33572
|
srv.tool(
|
|
33052
33573
|
"renderChart",
|
|
33053
|
-
`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(", ")}.`,
|
|
33574
|
+
`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, plus a "Render evidence" JSON block (mark counts by type, resolved axis domains, empty flag, annotation count, accessible name) \u2014 read the evidence instead of parsing the SVG to verify the chart actually rendered data marks. 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(", ")}.`,
|
|
33054
33575
|
{
|
|
33055
33576
|
component: external_exports3.string().describe("Chart component name, e.g. 'LineChart', 'BarChart'"),
|
|
33056
33577
|
props: external_exports3.record(external_exports3.string(), external_exports3.unknown()).optional().describe("Chart props object, e.g. { data: [...], xAccessor: 'x' }."),
|
|
@@ -33059,6 +33580,38 @@ function createServer2() {
|
|
|
33059
33580
|
},
|
|
33060
33581
|
renderChartHandler
|
|
33061
33582
|
);
|
|
33583
|
+
srv.registerTool(
|
|
33584
|
+
"renderInteractiveChart",
|
|
33585
|
+
{
|
|
33586
|
+
title: "Render interactive Semiotic chart",
|
|
33587
|
+
description: `Render a static-data Semiotic chart as a ChatGPT Apps widget. Use this after suggestCharts/getSchema/diagnoseConfig when the user wants to see an interactive chart inside ChatGPT. The server renders Semiotic to SVG and the widget adds fit, zoom, data, hover, and render-evidence controls. Available components: ${componentNames.join(", ")}.`,
|
|
33588
|
+
inputSchema: {
|
|
33589
|
+
component: external_exports3.string().describe("Renderable chart component name, e.g. 'LineChart', 'BarChart', 'GaugeChart'."),
|
|
33590
|
+
props: external_exports3.record(external_exports3.string(), external_exports3.unknown()).optional().describe("Static Semiotic chart props, including data/accessors where required."),
|
|
33591
|
+
theme: external_exports3.record(external_exports3.string(), external_exports3.string()).optional().describe("CSS custom properties such as { '--semiotic-bg': '#fff', '--semiotic-text': '#111' }. Only --semiotic-* variables are applied.")
|
|
33592
|
+
},
|
|
33593
|
+
outputSchema: {
|
|
33594
|
+
component: external_exports3.string(),
|
|
33595
|
+
title: external_exports3.string(),
|
|
33596
|
+
summary: external_exports3.string(),
|
|
33597
|
+
datumCount: external_exports3.number().nullable(),
|
|
33598
|
+
evidence: external_exports3.record(external_exports3.string(), external_exports3.unknown()).nullable()
|
|
33599
|
+
},
|
|
33600
|
+
annotations: {
|
|
33601
|
+
readOnlyHint: true,
|
|
33602
|
+
destructiveHint: false,
|
|
33603
|
+
idempotentHint: true,
|
|
33604
|
+
openWorldHint: false
|
|
33605
|
+
},
|
|
33606
|
+
_meta: {
|
|
33607
|
+
ui: { resourceUri: SEMIOTIC_CHART_WIDGET_URI },
|
|
33608
|
+
"openai/outputTemplate": SEMIOTIC_CHART_WIDGET_URI,
|
|
33609
|
+
"openai/toolInvocation/invoking": "Rendering Semiotic chart...",
|
|
33610
|
+
"openai/toolInvocation/invoked": "Rendered Semiotic chart."
|
|
33611
|
+
}
|
|
33612
|
+
},
|
|
33613
|
+
renderInteractiveChartHandler
|
|
33614
|
+
);
|
|
33062
33615
|
srv.tool(
|
|
33063
33616
|
"diagnoseConfig",
|
|
33064
33617
|
"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.",
|
|
@@ -33069,6 +33622,18 @@ function createServer2() {
|
|
|
33069
33622
|
},
|
|
33070
33623
|
diagnoseConfigHandler
|
|
33071
33624
|
);
|
|
33625
|
+
srv.tool(
|
|
33626
|
+
"auditAccessibility",
|
|
33627
|
+
"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.",
|
|
33628
|
+
{
|
|
33629
|
+
component: external_exports3.string().describe("Chart component name, e.g. 'LineChart'"),
|
|
33630
|
+
props: external_exports3.record(external_exports3.string(), external_exports3.unknown()).optional().describe("Chart props object, e.g. { data: [...], xAccessor: 'x', title: '...' }."),
|
|
33631
|
+
inChartContainer: external_exports3.boolean().optional().describe("True if the chart is (or will be) wrapped in a ChartContainer exposing data-download/copy-config actions."),
|
|
33632
|
+
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."),
|
|
33633
|
+
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.")
|
|
33634
|
+
},
|
|
33635
|
+
auditAccessibilityHandler
|
|
33636
|
+
);
|
|
33072
33637
|
srv.tool(
|
|
33073
33638
|
"reportIssue",
|
|
33074
33639
|
"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.",
|
|
@@ -33097,6 +33662,15 @@ function createServer2() {
|
|
|
33097
33662
|
},
|
|
33098
33663
|
interrogateChartHandler
|
|
33099
33664
|
);
|
|
33665
|
+
srv.tool(
|
|
33666
|
+
"groundChart",
|
|
33667
|
+
"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).",
|
|
33668
|
+
{
|
|
33669
|
+
component: external_exports3.string().describe("Chart component name, e.g. 'LineChart'"),
|
|
33670
|
+
props: external_exports3.record(external_exports3.string(), external_exports3.unknown()).describe("The full chart props including data")
|
|
33671
|
+
},
|
|
33672
|
+
groundChartHandler
|
|
33673
|
+
);
|
|
33100
33674
|
srv.tool(
|
|
33101
33675
|
"suggestStreamCharts",
|
|
33102
33676
|
"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.",
|
|
@@ -33144,8 +33718,9 @@ function createServer2() {
|
|
|
33144
33718
|
reason: external_exports3.string().optional()
|
|
33145
33719
|
})
|
|
33146
33720
|
).optional(),
|
|
33147
|
-
exposureLevel: external_exports3.union([external_exports3.literal(0), external_exports3.literal(1), external_exports3.literal(2)]).optional()
|
|
33148
|
-
|
|
33721
|
+
exposureLevel: external_exports3.union([external_exports3.literal(0), external_exports3.literal(1), external_exports3.literal(2)]).optional(),
|
|
33722
|
+
receptionModality: external_exports3.enum(["visual", "screen-reader", "sonified", "agent"]).optional().describe("Reception channel \u2014 see suggestCharts.")
|
|
33723
|
+
}).describe("Audience profile \u2014 familiarity, targets, exposure level, reception modality."),
|
|
33149
33724
|
intent: external_exports3.union([external_exports3.string(), external_exports3.array(external_exports3.string())]).optional(),
|
|
33150
33725
|
maxResults: external_exports3.number().int().min(1).max(20).optional()
|
|
33151
33726
|
},
|
|
@@ -33162,6 +33737,32 @@ function createServer2() {
|
|
|
33162
33737
|
},
|
|
33163
33738
|
repairChartConfigHandler
|
|
33164
33739
|
);
|
|
33740
|
+
srv.tool(
|
|
33741
|
+
"proposeChartVariants",
|
|
33742
|
+
"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.",
|
|
33743
|
+
{
|
|
33744
|
+
component: external_exports3.string().describe("Base chart component to vary, e.g. 'LineChart', 'BarChart', or 'BoxPlot'."),
|
|
33745
|
+
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."),
|
|
33746
|
+
data: external_exports3.array(external_exports3.record(external_exports3.string(), external_exports3.unknown())).optional().describe("Row data to profile. Overrides props.data when present."),
|
|
33747
|
+
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."),
|
|
33748
|
+
maxResults: external_exports3.number().int().min(1).max(20).optional().describe("Cap on proposals returned (default 8)."),
|
|
33749
|
+
audience: external_exports3.object({
|
|
33750
|
+
name: external_exports3.string().optional(),
|
|
33751
|
+
familiarity: external_exports3.record(external_exports3.string(), external_exports3.number()).optional(),
|
|
33752
|
+
targets: external_exports3.record(
|
|
33753
|
+
external_exports3.string(),
|
|
33754
|
+
external_exports3.object({
|
|
33755
|
+
direction: external_exports3.enum(["increase", "decrease"]),
|
|
33756
|
+
weight: external_exports3.number().int().min(1).max(3).optional(),
|
|
33757
|
+
reason: external_exports3.string().optional()
|
|
33758
|
+
})
|
|
33759
|
+
).optional(),
|
|
33760
|
+
exposureLevel: external_exports3.union([external_exports3.literal(0), external_exports3.literal(1), external_exports3.literal(2)]).optional(),
|
|
33761
|
+
receptionModality: external_exports3.enum(["visual", "screen-reader", "sonified", "agent"]).optional().describe("Reception channel \u2014 see suggestCharts.")
|
|
33762
|
+
}).optional().describe("Audience profile \u2014 familiarity, adoption targets, exposure level, and reception modality.")
|
|
33763
|
+
},
|
|
33764
|
+
proposeChartVariantsHandler
|
|
33765
|
+
);
|
|
33165
33766
|
srv.tool(
|
|
33166
33767
|
"suggestCharts",
|
|
33167
33768
|
"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.",
|
|
@@ -33170,7 +33771,21 @@ function createServer2() {
|
|
|
33170
33771
|
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."),
|
|
33171
33772
|
maxResults: external_exports3.number().int().min(1).max(40).optional().describe("Cap on suggestions returned (default 8)."),
|
|
33172
33773
|
allow: external_exports3.array(external_exports3.string()).optional().describe("Restrict to these component names."),
|
|
33173
|
-
deny: external_exports3.array(external_exports3.string()).optional().describe("Exclude these component names.")
|
|
33774
|
+
deny: external_exports3.array(external_exports3.string()).optional().describe("Exclude these component names."),
|
|
33775
|
+
audience: external_exports3.object({
|
|
33776
|
+
name: external_exports3.string().optional(),
|
|
33777
|
+
familiarity: external_exports3.record(external_exports3.string(), external_exports3.number()).optional(),
|
|
33778
|
+
targets: external_exports3.record(
|
|
33779
|
+
external_exports3.string(),
|
|
33780
|
+
external_exports3.object({
|
|
33781
|
+
direction: external_exports3.enum(["increase", "decrease"]),
|
|
33782
|
+
weight: external_exports3.number().int().min(1).max(3).optional(),
|
|
33783
|
+
reason: external_exports3.string().optional()
|
|
33784
|
+
})
|
|
33785
|
+
).optional(),
|
|
33786
|
+
exposureLevel: external_exports3.union([external_exports3.literal(0), external_exports3.literal(1), external_exports3.literal(2)]).optional(),
|
|
33787
|
+
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.")
|
|
33788
|
+
}).optional().describe("Audience profile \u2014 familiarity, adoption targets, exposure level, and reception modality.")
|
|
33174
33789
|
},
|
|
33175
33790
|
suggestChartsHandler
|
|
33176
33791
|
);
|
|
@@ -33220,8 +33835,8 @@ async function main() {
|
|
|
33220
33835
|
});
|
|
33221
33836
|
httpServer.listen(port, () => {
|
|
33222
33837
|
console.error(`Semiotic MCP server (HTTP) listening on http://localhost:${port}`);
|
|
33223
|
-
console.error("Tools: getSchema, suggestChart, suggestCharts, suggestStreamCharts, suggestDashboard, suggestStretchCharts, repairChartConfig, renderChart, interrogateChart, diagnoseConfig, reportIssue, applyTheme");
|
|
33224
|
-
console.error("Resources: semiotic://schema, semiotic://components, semiotic://behavior-contracts, semiotic://system-prompt, semiotic://examples");
|
|
33838
|
+
console.error("Tools: getSchema, suggestChart, suggestCharts, proposeChartVariants, suggestStreamCharts, suggestDashboard, suggestStretchCharts, repairChartConfig, renderChart, renderInteractiveChart, interrogateChart, groundChart, diagnoseConfig, auditAccessibility, reportIssue, applyTheme");
|
|
33839
|
+
console.error("Resources: semiotic://schema, semiotic://components, semiotic://behavior-contracts, semiotic://system-prompt, semiotic://examples, ui://semiotic/chart-widget.html");
|
|
33225
33840
|
});
|
|
33226
33841
|
} else {
|
|
33227
33842
|
const srv = createServer2();
|