semiotic 3.7.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 +3 -2
- package/README.md +30 -7
- package/ai/dist/mcp-server.js +417 -5
- package/ai/schema.json +1 -1
- package/ai/system-prompt.md +3 -3
- package/dist/components/AccessibleNavTree.d.ts +2 -1
- package/dist/components/Annotation.d.ts +1 -1
- package/dist/components/CategoryColors.d.ts +1 -1
- 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/qualityScorecard.d.ts +11 -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/RealtimeHistogram.d.ts +1 -1
- package/dist/components/charts/shared/ChartError.d.ts +2 -1
- package/dist/components/charts/shared/withChartWrapper.d.ts +1 -1
- package/dist/components/charts/xy/MinimapChart.d.ts +2 -1
- package/dist/components/charts/xy/ScatterplotMatrix.d.ts +2 -1
- package/dist/components/semiotic-server.d.ts +2 -1
- package/dist/components/server/renderEvidence.d.ts +92 -0
- package/dist/components/server/renderToStaticSVG.d.ts +14 -1
- package/dist/components/stream/AccessibleDataTable.d.ts +5 -5
- 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 +2 -1
- package/dist/components/stream/OrdinalBrushOverlay.d.ts +19 -1
- package/dist/components/stream/OrdinalSVGOverlay.d.ts +2 -2
- package/dist/components/stream/SVGOverlay.d.ts +3 -2
- package/dist/components/stream/XYBrushOverlay.d.ts +21 -1
- 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.min.js +1 -1
- package/dist/semiotic-ai.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.min.js +1 -1
- package/dist/semiotic-utils.module.min.js +1 -1
- package/dist/semiotic-value.min.js +1 -1
- package/dist/semiotic-value.module.min.js +1 -1
- 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 +2 -2
package/CLAUDE.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
- Install: `npm install semiotic`
|
|
5
5
|
<!-- semiotic-bundle-sizes:start -->
|
|
6
6
|
<!-- Auto-generated by scripts/sync-bundle-sizes.mjs — do not edit by hand. -->
|
|
7
|
-
- **Use sub-path imports** — `semiotic/xy` (90KB gz), `semiotic/ordinal` (74KB gz), `semiotic/network` (68KB gz), `semiotic/geo` (55KB gz), `semiotic/realtime` (95KB gz), `semiotic/server` (
|
|
7
|
+
- **Use sub-path imports** — `semiotic/xy` (90KB gz), `semiotic/ordinal` (74KB gz), `semiotic/network` (68KB gz), `semiotic/geo` (55KB gz), `semiotic/realtime` (95KB gz), `semiotic/server` (128KB gz), `semiotic/utils` (38KB gz), `semiotic/recipes` (9KB gz), `semiotic/themes` (4KB gz), `semiotic/data` (3KB gz), `semiotic/value` (6KB gz), `semiotic/ai` (250KB gz). Full `semiotic` is 203KB gz.
|
|
8
8
|
<!-- semiotic-bundle-sizes:end -->
|
|
9
9
|
- CLI: `npx semiotic-ai [--schema|--compact|--examples|--doctor|--audit-a11y]` · MCP: `npx semiotic-mcp`
|
|
10
10
|
|
|
@@ -160,9 +160,10 @@ Also: **ScatterplotMatrix**, **ChartContainer** (`title`, `subtitle`, `actions`)
|
|
|
160
160
|
HOC charts render SVG automatically in server environments. For standalone generation:
|
|
161
161
|
|
|
162
162
|
```ts
|
|
163
|
-
import { renderChart, renderToImage, renderToAnimatedGif, renderDashboard } from "semiotic/server"
|
|
163
|
+
import { renderChart, renderChartWithEvidence, renderToImage, renderToAnimatedGif, renderDashboard } from "semiotic/server"
|
|
164
164
|
|
|
165
165
|
const svg = renderChart("BarChart", { data, categoryAccessor, valueAccessor, theme: "tufte", showLegend, showGrid, annotations })
|
|
166
|
+
const { svg: svg2, evidence } = renderChartWithEvidence("BarChart", { data, categoryAccessor, valueAccessor }) // evidence: { markCount, markCountByType, empty, xDomain?, yDomain?, categories?, nodeCount?, edgeCount?, annotationCount, ariaLabel, warnings } — ground truth from the rendered scene; check `evidence.empty`/`markCount` instead of parsing SVG. MCP renderChart returns the same block.
|
|
166
167
|
const png = await renderToImage("LineChart", { ... }, { format: "png", scale: 2 }) // requires sharp
|
|
167
168
|
const gif = await renderToAnimatedGif("line", data, { xAccessor, yAccessor, theme: "dark" }, { fps: 12, transitionFrames: 4, decay: { type: "linear" } }) // requires sharp + gifenc
|
|
168
169
|
const dashboard = renderDashboard([{ component: "BarChart", props }, { component: "PieChart", colSpan: 2, props }], { title, theme, layout: { columns: 2 } })
|
package/README.md
CHANGED
|
@@ -35,11 +35,11 @@ generate correct code without examples.
|
|
|
35
35
|
Semiotic ships with everything an AI coding assistant needs to generate
|
|
36
36
|
correct visualizations without trial and error:
|
|
37
37
|
|
|
38
|
-
- **`semiotic/ai`** — a single import with the 47-chart capability catalog (XY, ordinal, network, realtime, geo, value), optimized for LLM code generation.
|
|
38
|
+
- **`semiotic/ai`** — a single import with the 47-chart capability catalog (XY, ordinal, network, realtime, geo, value), optimized for LLM code generation. Note: the published entry files are pre-bundled, so importing one chart from `semiotic/ai` still ships most of the bundle — treat it as a codegen/tooling surface and use family subpaths (`semiotic/xy`, `semiotic/geo`, `semiotic/value`, …) in production code, at roughly half the single-chart cost.
|
|
39
39
|
- **`ai/schema.json`** — machine-readable prop schemas for every component
|
|
40
40
|
- **`npx semiotic-mcp`** — an MCP server for tool-based chart rendering in any MCP client
|
|
41
41
|
- **`npx semiotic-ai --doctor`** — validate component + props JSON from the command line with typo suggestions and anti-pattern detection
|
|
42
|
-
- **`diagnoseConfig(component, props)`** — programmatic anti-pattern detector with
|
|
42
|
+
- **`diagnoseConfig(component, props)`** — programmatic anti-pattern detector with actionable fixes, spanning validation, encoding, accessibility, and misleading-design (deception) checks
|
|
43
43
|
- **`CLAUDE.md`** — instruction files auto-synced for Claude, Cursor, Copilot, Windsurf, and Cline
|
|
44
44
|
- **`llms.txt`** — machine-readable documentation following the emerging standard
|
|
45
45
|
|
|
@@ -320,13 +320,13 @@ Semiotic ships 12 entry points. **Don't import from `"semiotic"` unless you need
|
|
|
320
320
|
| `semiotic/network` | **68 KB** | ForceDirectedGraph, SankeyDiagram, ProcessSankey, Treemap, + 4 more |
|
|
321
321
|
| `semiotic/geo` | **55 KB** | ChoroplethMap, FlowMap, DistanceCartogram, ProportionalSymbolMap |
|
|
322
322
|
| `semiotic/realtime` | **95 KB** | RealtimeLineChart, RealtimeHistogram, + 4 streaming charts |
|
|
323
|
-
| `semiotic/server` | **
|
|
324
|
-
| `semiotic/utils` | **
|
|
323
|
+
| `semiotic/server` | **128 KB** | renderChart, renderDashboard, renderToImage, renderToAnimatedGif |
|
|
324
|
+
| `semiotic/utils` | **38 KB** | ThemeProvider, validators, serialization — no chart components |
|
|
325
325
|
| `semiotic/recipes` | **9 KB** | Pure layout functions (waffle, marimekko, flextree, dagre, …) |
|
|
326
326
|
| `semiotic/themes` | **4 KB** | Theme presets only (tufte, carbon, etc.) |
|
|
327
327
|
| `semiotic/data` | **3 KB** | bin, rollup, groupBy, pivot, fromVegaLite |
|
|
328
328
|
| `semiotic/value` | **6 KB** | BigNumber — focal-value KPI / scorecard (SingleValueFrame POC) |
|
|
329
|
-
| `semiotic/ai` | **
|
|
329
|
+
| `semiotic/ai` | **250 KB** | All 47 schema-backed charts + validation — optimized for LLM code generation |
|
|
330
330
|
| `semiotic` | **203 KB** | Everything below (full bundle) |
|
|
331
331
|
|
|
332
332
|
<!-- semiotic-bundle-sizes:end -->
|
|
@@ -411,13 +411,16 @@ Add to your MCP client config (e.g. `claude_desktop_config.json` for Claude Desk
|
|
|
411
411
|
}
|
|
412
412
|
```
|
|
413
413
|
|
|
414
|
-
No API keys or authentication required. The server runs locally via stdio. HTTP mode is also available for inspectors and
|
|
414
|
+
No API keys or authentication required. The server runs locally via stdio. HTTP mode is also available for inspectors, web clients, and ChatGPT Apps SDK experiments: `npx semiotic-mcp --http --port 3001`.
|
|
415
|
+
|
|
416
|
+
For ChatGPT developer mode, expose the HTTP endpoint over HTTPS with a tunnel and create a connector that points at `https://<your-tunnel>/mcp`. The experimental Apps SDK surface is `renderInteractiveChart`, which returns a `text/html;profile=mcp-app` widget template plus a hidden SVG payload rendered by Semiotic on the MCP server.
|
|
415
417
|
|
|
416
418
|
### Tools
|
|
417
419
|
|
|
418
420
|
| Tool | Description |
|
|
419
421
|
|------|-------------|
|
|
420
|
-
| **`renderChart`** | Render a Semiotic chart to static SVG. Supports the components returned by `getSchema` that are marked `[renderable]`. Pass `{ component: "LineChart", props: { data: [...], xAccessor: "x", yAccessor: "y" } }`. Returns SVG string or validation errors with fix suggestions. |
|
|
422
|
+
| **`renderChart`** | Render a Semiotic chart to static SVG. Supports the components returned by `getSchema` that are marked `[renderable]`. Pass `{ component: "LineChart", props: { data: [...], xAccessor: "x", yAccessor: "y" } }`. Returns SVG string plus a "Render evidence" JSON block (mark counts by scene type, resolved axis domains, empty flag, annotation count, accessible name) so agents can verify the chart drew data marks, or validation errors with fix suggestions. |
|
|
423
|
+
| **`renderInteractiveChart`** | Render a static-data chart as a ChatGPT Apps widget. Uses the same Semiotic server render path as `renderChart`, then hydrates an iframe UI with fit, zoom, data, hover, and render-evidence controls. |
|
|
421
424
|
| **`getSchema`** | Return the prop schema for a specific component. Pass `{ component: "LineChart" }` to get its props, or omit `component` to list all 47 chart schemas. Components marked `[renderable]` are available through `renderChart`; realtime charts require a browser/live environment. |
|
|
422
425
|
| **`suggestChart`** | Legacy sample-row recommender. Pass `{ data: [{...}, ...] }` with 1–5 sample objects plus optional broad intent/capability filters. |
|
|
423
426
|
| **`suggestCharts`** | Capability-based recommender for bounded row data. Returns ranked chart suggestions with scores, reasons, caveats, import paths, and ready-to-use props. |
|
|
@@ -439,6 +442,7 @@ No API keys or authentication required. The server runs locally via stdio. HTTP
|
|
|
439
442
|
| **`semiotic://behavior-contracts`** | Agent-visible semantic rules for color precedence, required prop combinations, push refs, and renderability. |
|
|
440
443
|
| **`semiotic://system-prompt`** | Compact AI instructions with import rules, chart props, SSR guidance, and pitfalls. |
|
|
441
444
|
| **`semiotic://examples`** | Copy-paste chart examples by data shape. |
|
|
445
|
+
| **`ui://semiotic/chart-widget.html`** | ChatGPT Apps / MCP Apps widget template used by `renderInteractiveChart`. |
|
|
442
446
|
|
|
443
447
|
### Prompts
|
|
444
448
|
|
|
@@ -490,6 +494,25 @@ Args: {
|
|
|
490
494
|
→ Returns: <svg>...</svg>
|
|
491
495
|
```
|
|
492
496
|
|
|
497
|
+
### Example: render a ChatGPT Apps widget
|
|
498
|
+
|
|
499
|
+
```
|
|
500
|
+
Tool: renderInteractiveChart
|
|
501
|
+
Args: {
|
|
502
|
+
"component": "BarChart",
|
|
503
|
+
"props": {
|
|
504
|
+
"title": "Revenue by Quarter",
|
|
505
|
+
"data": [
|
|
506
|
+
{ "quarter": "Q1", "revenue": 120 },
|
|
507
|
+
{ "quarter": "Q2", "revenue": 180 }
|
|
508
|
+
],
|
|
509
|
+
"categoryAccessor": "quarter",
|
|
510
|
+
"valueAccessor": "revenue"
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
→ Returns: structured chart summary for the model + hidden SVG/widget metadata for ChatGPT.
|
|
514
|
+
```
|
|
515
|
+
|
|
493
516
|
### Example: diagnose a broken config
|
|
494
517
|
|
|
495
518
|
```
|
package/ai/dist/mcp-server.js
CHANGED
|
@@ -32433,6 +32433,7 @@ ${errors.join("\n")}`
|
|
|
32433
32433
|
}
|
|
32434
32434
|
|
|
32435
32435
|
// ai/mcp-server.ts
|
|
32436
|
+
var import_server2 = require("semiotic/server");
|
|
32436
32437
|
var import_ai3 = require("semiotic/ai");
|
|
32437
32438
|
var {
|
|
32438
32439
|
componentIndexFromSchema,
|
|
@@ -32458,6 +32459,8 @@ for (const tool of schema.tools) {
|
|
|
32458
32459
|
var allComponentNames = Object.keys(schemaByComponent).sort();
|
|
32459
32460
|
var componentNames = Object.keys(COMPONENT_REGISTRY).sort();
|
|
32460
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";
|
|
32461
32464
|
function aiFilePath(fileName) {
|
|
32462
32465
|
return path.resolve(__dirname, "..", fileName);
|
|
32463
32466
|
}
|
|
@@ -32476,6 +32479,30 @@ function textResource(uri, mimeType, text) {
|
|
|
32476
32479
|
}]
|
|
32477
32480
|
};
|
|
32478
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
|
+
}
|
|
32479
32506
|
function promptMessage(text) {
|
|
32480
32507
|
return {
|
|
32481
32508
|
messages: [{
|
|
@@ -32487,6 +32514,279 @@ function promptMessage(text) {
|
|
|
32487
32514
|
}]
|
|
32488
32515
|
};
|
|
32489
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
|
+
}
|
|
32490
32790
|
async function getSchemaHandler(args) {
|
|
32491
32791
|
const component = args.component;
|
|
32492
32792
|
if (!component) {
|
|
@@ -32563,6 +32863,17 @@ async function renderChartHandler(args) {
|
|
|
32563
32863
|
};
|
|
32564
32864
|
}
|
|
32565
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
|
+
}
|
|
32566
32877
|
if (theme && Object.keys(theme).length > 0) {
|
|
32567
32878
|
const validVars = Object.entries(theme).filter(([k]) => k.startsWith("--semiotic-")).map(([k, v]) => `${k}: ${v}`).join("; ");
|
|
32568
32879
|
if (validVars) {
|
|
@@ -32576,7 +32887,10 @@ async function renderChartHandler(args) {
|
|
|
32576
32887
|
const pngBuffer = await sharpFn(Buffer.from(svg)).png().toBuffer();
|
|
32577
32888
|
const base643 = pngBuffer.toString("base64");
|
|
32578
32889
|
return {
|
|
32579
|
-
content: [
|
|
32890
|
+
content: [
|
|
32891
|
+
{ type: "text", text: `data:image/png;base64,${base643}` },
|
|
32892
|
+
...evidenceBlock ? [evidenceBlock] : []
|
|
32893
|
+
]
|
|
32580
32894
|
};
|
|
32581
32895
|
} catch (err) {
|
|
32582
32896
|
if (err.code === "MODULE_NOT_FOUND" || err.code === "ERR_MODULE_NOT_FOUND") {
|
|
@@ -32599,7 +32913,52 @@ ${svg}` }],
|
|
|
32599
32913
|
}
|
|
32600
32914
|
}
|
|
32601
32915
|
return {
|
|
32602
|
-
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
|
+
}
|
|
32603
32962
|
};
|
|
32604
32963
|
}
|
|
32605
32964
|
function filterUsageModeDiagnoses(component, usageMode, diagnoses) {
|
|
@@ -33102,6 +33461,27 @@ function createServer2() {
|
|
|
33102
33461
|
},
|
|
33103
33462
|
(uri) => textResource(uri, "text/markdown", readAIFile("examples.md"))
|
|
33104
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
|
+
);
|
|
33105
33485
|
srv.registerPrompt(
|
|
33106
33486
|
"build-semiotic-chart",
|
|
33107
33487
|
{
|
|
@@ -33191,7 +33571,7 @@ function createServer2() {
|
|
|
33191
33571
|
);
|
|
33192
33572
|
srv.tool(
|
|
33193
33573
|
"renderChart",
|
|
33194
|
-
`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(", ")}.`,
|
|
33195
33575
|
{
|
|
33196
33576
|
component: external_exports3.string().describe("Chart component name, e.g. 'LineChart', 'BarChart'"),
|
|
33197
33577
|
props: external_exports3.record(external_exports3.string(), external_exports3.unknown()).optional().describe("Chart props object, e.g. { data: [...], xAccessor: 'x' }."),
|
|
@@ -33200,6 +33580,38 @@ function createServer2() {
|
|
|
33200
33580
|
},
|
|
33201
33581
|
renderChartHandler
|
|
33202
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
|
+
);
|
|
33203
33615
|
srv.tool(
|
|
33204
33616
|
"diagnoseConfig",
|
|
33205
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.",
|
|
@@ -33423,8 +33835,8 @@ async function main() {
|
|
|
33423
33835
|
});
|
|
33424
33836
|
httpServer.listen(port, () => {
|
|
33425
33837
|
console.error(`Semiotic MCP server (HTTP) listening on http://localhost:${port}`);
|
|
33426
|
-
console.error("Tools: getSchema, suggestChart, suggestCharts, proposeChartVariants, suggestStreamCharts, suggestDashboard, suggestStretchCharts, repairChartConfig, renderChart, interrogateChart, groundChart, diagnoseConfig, auditAccessibility, reportIssue, applyTheme");
|
|
33427
|
-
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");
|
|
33428
33840
|
});
|
|
33429
33841
|
} else {
|
|
33430
33842
|
const srv = createServer2();
|
package/ai/schema.json
CHANGED
package/ai/system-prompt.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- semiotic-bundle-sizes:start -->
|
|
4
4
|
<!-- Auto-generated by scripts/sync-bundle-sizes.mjs — do not edit by hand. -->
|
|
5
|
-
**Use sub-path imports** — `semiotic/xy` (90KB gz), `semiotic/ordinal` (74KB gz), `semiotic/network` (68KB gz), `semiotic/geo` (55KB gz), `semiotic/realtime` (95KB gz), `semiotic/server` (
|
|
5
|
+
**Use sub-path imports** — `semiotic/xy` (90KB gz), `semiotic/ordinal` (74KB gz), `semiotic/network` (68KB gz), `semiotic/geo` (55KB gz), `semiotic/realtime` (95KB gz), `semiotic/server` (128KB gz), `semiotic/utils` (38KB gz), `semiotic/recipes` (9KB gz), `semiotic/themes` (4KB gz), `semiotic/data` (3KB gz), `semiotic/value` (6KB gz), `semiotic/ai` (250KB gz). Full `semiotic` is 203KB gz.
|
|
6
6
|
<!-- semiotic-bundle-sizes:end -->
|
|
7
7
|
|
|
8
8
|
## Flat Array Data (`data: object[]`)
|
|
@@ -140,7 +140,7 @@ These rules are generated from `ai/behaviorContracts.cjs` and are consumed by `s
|
|
|
140
140
|
|
|
141
141
|
## Key Patterns
|
|
142
142
|
- **Percentile band + main line**: Layer `<AreaChart yAccessor="p95" y0Accessor="p5" showLine={false} />` + `<LineChart yAccessor="p50" />`. AreaChart's `showLine` only draws the top edge, NOT a separate main line.
|
|
143
|
-
- **SSR**: `renderChart("BarChart", props)` from `semiotic/server` — uses HOC names. Also `"Sparkline"` (no axes, 2px margins). `renderToImage()` (PNG), `renderToAnimatedGif()` (GIF), `renderDashboard()` (multi-chart). All accept `theme`. Required props: StackedBarChart needs `stackBy`, GroupedBarChart needs `groupBy`, StackedAreaChart needs `areaBy`, BubbleChart needs `sizeBy`, FunnelChart uses `stepAccessor`, GaugeChart needs `value` (`thresholds` optional).
|
|
143
|
+
- **SSR**: `renderChart("BarChart", props)` from `semiotic/server` — uses HOC names. Also `"Sparkline"` (no axes, 2px margins). `renderChartWithEvidence()` returns `{ svg, evidence }` (mark counts by scene type, axis domains, empty flag, annotation count, accessible name) so agents can verify the render drew data marks. `renderToImage()` (PNG), `renderToAnimatedGif()` (GIF), `renderDashboard()` (multi-chart). All accept `theme`. Required props: StackedBarChart needs `stackBy`, GroupedBarChart needs `groupBy`, StackedAreaChart needs `areaBy`, BubbleChart needs `sizeBy`, FunnelChart uses `stepAccessor`, GaugeChart needs `value` (`thresholds` optional).
|
|
144
144
|
- **CLI**: `npx semiotic-ai --list` shows components/import paths/renderability; `npx semiotic-ai --schema GaugeChart` prints one component schema with metadata; `--doctor` validates props JSON and behavior contracts.
|
|
145
|
-
- **MCP**: `npx semiotic-mcp` exposes
|
|
145
|
+
- **MCP**: `npx semiotic-mcp` exposes schema, suggestion, diagnosis, accessibility, grounding, issue, theme, static render (`renderChart`), and ChatGPT Apps render (`renderInteractiveChart`) tools. Resources include `semiotic://schema`, `semiotic://components`, `semiotic://behavior-contracts`, `semiotic://system-prompt`, `semiotic://examples`, and the widget template `ui://semiotic/chart-widget.html`. Prompts: `build-semiotic-chart`, `debug-semiotic-chart`.
|
|
146
146
|
- **exportChart**: Pass the wrapper div, not the SVG element: `exportChart(wrapperDiv, { format: "png" })`. It finds canvas+SVG internally.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as React from "react";
|
|
1
2
|
import { type NavTreeNode } from "./ai/navigationTree";
|
|
2
3
|
export interface AccessibleNavTreeProps {
|
|
3
4
|
tree: NavTreeNode;
|
|
@@ -22,4 +23,4 @@ export interface AccessibleNavTreeProps {
|
|
|
22
23
|
*/
|
|
23
24
|
chartId?: string;
|
|
24
25
|
}
|
|
25
|
-
export declare function AccessibleNavTree({ tree, label, visible, className, onActiveChange, activeId: controlledActiveId, chartId }: AccessibleNavTreeProps):
|
|
26
|
+
export declare function AccessibleNavTree({ tree, label, visible, className, onActiveChange, activeId: controlledActiveId, chartId }: AccessibleNavTreeProps): React.JSX.Element;
|
|
@@ -65,5 +65,5 @@ export interface AnnotationProps {
|
|
|
65
65
|
[key: string]: unknown;
|
|
66
66
|
};
|
|
67
67
|
}
|
|
68
|
-
export default function SemioticAnnotation(props: AnnotationProps):
|
|
68
|
+
export default function SemioticAnnotation(props: AnnotationProps): React.JSX.Element;
|
|
69
69
|
export {};
|