semiotic 3.6.0 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +190 -227
- package/README.md +44 -14
- package/ai/cli.js +41 -0
- package/ai/componentMetadata.cjs +11 -2
- package/ai/dist/mcp-server.js +209 -6
- package/ai/examples.md +98 -0
- package/ai/schema.json +581 -1
- package/ai/system-prompt.md +5 -2
- package/dist/components/AccessibleNavTree.d.ts +25 -0
- package/dist/components/Annotation.d.ts +40 -14
- package/dist/components/ChartContainer.d.ts +32 -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/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/realtime/RealtimeHeatmap.d.ts +3 -0
- package/dist/components/charts/realtime/RealtimeHistogram.d.ts +3 -0
- 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/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/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/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-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/staticAnnotations.d.ts +2 -0
- package/dist/components/stream/AccessibleDataTable.d.ts +10 -1
- package/dist/components/stream/NetworkSVGOverlay.d.ts +11 -5
- package/dist/components/stream/OrdinalSVGOverlay.d.ts +2 -0
- package/dist/components/stream/SVGOverlay.d.ts +2 -0
- 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-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 +18 -4
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ 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
|
|
38
|
+
- **`semiotic/ai`** — a single import with the 47-chart capability catalog (XY, ordinal, network, realtime, geo, value), optimized for LLM code generation. Prefer family subpaths such as `semiotic/xy`, `semiotic/geo`, and `semiotic/value` when bundle size matters.
|
|
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
|
|
@@ -71,6 +71,13 @@ with d3-geo projections, zoom/pan, tile basemaps, and drag-rotate globe spinning
|
|
|
71
71
|
LOESS smoothing, forecast with confidence envelopes, and anomaly detection.
|
|
72
72
|
Marginal distribution graphics on scatterplot axes with a single prop.
|
|
73
73
|
|
|
74
|
+
**First-class annotations.** Annotations are data-bound objects, not post-hoc
|
|
75
|
+
artwork. Labels, callouts, thresholds, enclosures, statistical overlays, and
|
|
76
|
+
React widgets move with the chart and render through browser, SSR, and export
|
|
77
|
+
paths. Opt into placement, hierarchy, density, progressive disclosure,
|
|
78
|
+
audience-aware amount, provenance, and editorial lifecycle when the chart
|
|
79
|
+
needs to communicate more than its encoding alone.
|
|
80
|
+
|
|
74
81
|
### Start simple, go deep
|
|
75
82
|
|
|
76
83
|
| Layer | For | Example |
|
|
@@ -278,6 +285,27 @@ configToJSX(config)
|
|
|
278
285
|
Supports bar, line, area, point, rect, arc, tick marks with encoding translation
|
|
279
286
|
for color, size, aggregation, and binning.
|
|
280
287
|
|
|
288
|
+
### Conversation Arc Telemetry
|
|
289
|
+
|
|
290
|
+
Capture and replay the path an AI-assisted chart session took:
|
|
291
|
+
|
|
292
|
+
```ts
|
|
293
|
+
import {
|
|
294
|
+
createLocalStorageConversationArcSink,
|
|
295
|
+
enableConversationArc,
|
|
296
|
+
getConversationArcStore,
|
|
297
|
+
loadConversationArc,
|
|
298
|
+
registerConversationArcSink,
|
|
299
|
+
} from "semiotic/ai"
|
|
300
|
+
|
|
301
|
+
const sink = createLocalStorageConversationArcSink({ key: "my-app:arc" })
|
|
302
|
+
registerConversationArcSink(sink)
|
|
303
|
+
enableConversationArc({ sessionId: "session-abc" })
|
|
304
|
+
|
|
305
|
+
getConversationArcStore().record({ type: "chart-rendered", component: "LineChart" })
|
|
306
|
+
loadConversationArc(sink.load(), { enabled: false })
|
|
307
|
+
```
|
|
308
|
+
|
|
281
309
|
## Bundle Sizes
|
|
282
310
|
|
|
283
311
|
Semiotic ships 12 entry points. **Don't import from `"semiotic"` unless you need everything** — use the sub-path that matches your chart type:
|
|
@@ -287,18 +315,19 @@ Semiotic ships 12 entry points. **Don't import from `"semiotic"` unless you need
|
|
|
287
315
|
|
|
288
316
|
| Entry Point | gzip | What's inside |
|
|
289
317
|
|---|---|---|
|
|
290
|
-
| `semiotic/xy` | **
|
|
291
|
-
| `semiotic/ordinal` | **
|
|
292
|
-
| `semiotic/network` | **
|
|
293
|
-
| `semiotic/geo` | **
|
|
294
|
-
| `semiotic/realtime` | **
|
|
295
|
-
| `semiotic/server` | **
|
|
296
|
-
| `semiotic/utils` | **
|
|
297
|
-
| `semiotic/recipes` | **
|
|
318
|
+
| `semiotic/xy` | **90 KB** | LineChart, AreaChart, Scatterplot, Heatmap, + 8 more XY charts |
|
|
319
|
+
| `semiotic/ordinal` | **74 KB** | BarChart, PieChart, BoxPlot, Histogram, + 11 more categorical charts |
|
|
320
|
+
| `semiotic/network` | **68 KB** | ForceDirectedGraph, SankeyDiagram, ProcessSankey, Treemap, + 4 more |
|
|
321
|
+
| `semiotic/geo` | **55 KB** | ChoroplethMap, FlowMap, DistanceCartogram, ProportionalSymbolMap |
|
|
322
|
+
| `semiotic/realtime` | **95 KB** | RealtimeLineChart, RealtimeHistogram, + 4 streaming charts |
|
|
323
|
+
| `semiotic/server` | **127 KB** | renderChart, renderDashboard, renderToImage, renderToAnimatedGif |
|
|
324
|
+
| `semiotic/utils` | **37 KB** | ThemeProvider, validators, serialization — no chart components |
|
|
325
|
+
| `semiotic/recipes` | **9 KB** | Pure layout functions (waffle, marimekko, flextree, dagre, …) |
|
|
298
326
|
| `semiotic/themes` | **4 KB** | Theme presets only (tufte, carbon, etc.) |
|
|
299
327
|
| `semiotic/data` | **3 KB** | bin, rollup, groupBy, pivot, fromVegaLite |
|
|
300
|
-
| `semiotic/
|
|
301
|
-
| `semiotic` | **
|
|
328
|
+
| `semiotic/value` | **6 KB** | BigNumber — focal-value KPI / scorecard (SingleValueFrame POC) |
|
|
329
|
+
| `semiotic/ai` | **246 KB** | All 47 schema-backed charts + validation — optimized for LLM code generation |
|
|
330
|
+
| `semiotic` | **203 KB** | Everything below (full bundle) |
|
|
302
331
|
|
|
303
332
|
<!-- semiotic-bundle-sizes:end -->
|
|
304
333
|
|
|
@@ -389,7 +418,7 @@ No API keys or authentication required. The server runs locally via stdio. HTTP
|
|
|
389
418
|
| Tool | Description |
|
|
390
419
|
|------|-------------|
|
|
391
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. |
|
|
392
|
-
| **`getSchema`** | Return the prop schema for a specific component. Pass `{ component: "LineChart" }` to get its props, or omit `component` to list all
|
|
421
|
+
| **`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. |
|
|
393
422
|
| **`suggestChart`** | Legacy sample-row recommender. Pass `{ data: [{...}, ...] }` with 1–5 sample objects plus optional broad intent/capability filters. |
|
|
394
423
|
| **`suggestCharts`** | Capability-based recommender for bounded row data. Returns ranked chart suggestions with scores, reasons, caveats, import paths, and ready-to-use props. |
|
|
395
424
|
| **`suggestStreamCharts`** | Recommend realtime charts from a stream schema, throughput, and retention hints. |
|
|
@@ -509,7 +538,7 @@ Semiotic is indexed by AI-coding-agent documentation tools so your assistant (Cl
|
|
|
509
538
|
|
|
510
539
|
Agent-facing API surface:
|
|
511
540
|
|
|
512
|
-
- **`CLAUDE.md`**, **`ai/schema.json`**, **`ai/behaviorContracts.cjs`** — bundled in the npm tarball (see `package.json#files`); agents that install Semiotic locally read these directly. `CLAUDE.md` is the quick-start cheat sheet (HOC props, push API, theming, usage notes); `ai/schema.json` is the JSON Schema for every chart's prop surface (
|
|
541
|
+
- **`CLAUDE.md`**, **`ai/schema.json`**, **`ai/behaviorContracts.cjs`** — bundled in the npm tarball (see `package.json#files`); agents that install Semiotic locally read these directly. `CLAUDE.md` is the quick-start cheat sheet (HOC props, push API, theming, usage notes); `ai/schema.json` is the JSON Schema for every chart's prop surface (47 charts); `ai/behaviorContracts.cjs` carries the agent-visible semantic rules (color precedence, push-mode requirements, ID-accessor contracts).
|
|
513
542
|
- [**`semiotic.nteract.io/llms.txt`**](https://semiotic.nteract.io/llms.txt) + [**`/llms-full.txt`**](https://semiotic.nteract.io/llms-full.txt) — deployed at the docs site per the [llms.txt standard](https://llmstxt.org). Agents fetch the navigation map (`llms.txt`) or the full inlined docs (`llms-full.txt`) over HTTP; they're not part of the npm package itself.
|
|
514
543
|
|
|
515
544
|
## Documentation
|
|
@@ -519,7 +548,8 @@ Agent-facing API surface:
|
|
|
519
548
|
- [Getting Started](https://semiotic.nteract.io/getting-started)
|
|
520
549
|
- [Charts](https://semiotic.nteract.io/charts) — chart types with live examples
|
|
521
550
|
- [Frames](https://semiotic.nteract.io/frames) — full Frame API reference
|
|
522
|
-
- [Features](https://semiotic.nteract.io/features) — axes,
|
|
551
|
+
- [Features](https://semiotic.nteract.io/features) — axes, tooltips, interaction, responsive behavior, and composition
|
|
552
|
+
- [Annotations](https://semiotic.nteract.io/annotations) — first-class annotation types, design guidance, provenance, and lifecycle
|
|
523
553
|
- [Cookbook](https://semiotic.nteract.io/cookbook) — advanced patterns and recipes
|
|
524
554
|
- [Playground](https://semiotic.nteract.io/playground) — interactive prop exploration
|
|
525
555
|
|
package/ai/cli.js
CHANGED
|
@@ -48,6 +48,8 @@ Usage:
|
|
|
48
48
|
npx semiotic-ai --compact Print ai/system-prompt.md (compact prompt)
|
|
49
49
|
npx semiotic-ai --examples Print ai/examples.md (copy-paste examples)
|
|
50
50
|
npx semiotic-ai --doctor Validate { component, props, usageMode? } JSON from stdin
|
|
51
|
+
npx semiotic-ai --audit-a11y Audit { component, props, inChartContainer?, describe?, navigable? }
|
|
52
|
+
JSON against Chartability (POUR-CAF) accessibility heuristics
|
|
51
53
|
npx semiotic-ai --help Show this help message
|
|
52
54
|
`.trim()
|
|
53
55
|
|
|
@@ -341,6 +343,45 @@ if (flag === "--doctor") {
|
|
|
341
343
|
process.exit(0)
|
|
342
344
|
}
|
|
343
345
|
|
|
346
|
+
// --audit-a11y: grade component + props against Chartability heuristics
|
|
347
|
+
if (flag === "--audit-a11y") {
|
|
348
|
+
const input = readJSONInput("Usage: npx semiotic-ai --audit-a11y '{\"component\":\"LineChart\",\"props\":{\"data\":[...],\"xAccessor\":\"x\",\"yAccessor\":\"y\"}}'\n echo '{\"component\":\"BarChart\",\"props\":{...},\"inChartContainer\":true}' | npx semiotic-ai --audit-a11y")
|
|
349
|
+
|
|
350
|
+
try {
|
|
351
|
+
const { component, props, inChartContainer, describe, navigable } = JSON.parse(input)
|
|
352
|
+
if (!component || !props) {
|
|
353
|
+
console.error("Input must be JSON with { component, props } fields.")
|
|
354
|
+
process.exit(1)
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Load the audit from dist (same strategy as --doctor). It lives in the
|
|
358
|
+
// semiotic/ai bundle; a clean source checkout without a build can't run it.
|
|
359
|
+
const distPath = path.join(pkgRoot, "dist", "semiotic-ai.min.js")
|
|
360
|
+
let auditAccessibility, formatAccessibilityAudit
|
|
361
|
+
try {
|
|
362
|
+
if (!process.env.SEMIOTIC_AI_SCHEMA_ONLY) {
|
|
363
|
+
const mod = require(distPath)
|
|
364
|
+
auditAccessibility = mod.auditAccessibility
|
|
365
|
+
formatAccessibilityAudit = mod.formatAccessibilityAudit
|
|
366
|
+
}
|
|
367
|
+
} catch (e) {
|
|
368
|
+
// Dist unavailable.
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if (!auditAccessibility || !formatAccessibilityAudit) {
|
|
372
|
+
console.error("Accessibility audit requires the built library. Run `npm run dist` first, or use the MCP `auditAccessibility` tool.")
|
|
373
|
+
process.exit(2)
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const result = auditAccessibility(component, props, { inChartContainer: inChartContainer === true, describe: describe === true, navigable: navigable === true })
|
|
377
|
+
console.log(formatAccessibilityAudit(result))
|
|
378
|
+
process.exit(result.ok ? 0 : 1)
|
|
379
|
+
} catch (err) {
|
|
380
|
+
console.error(`Failed to parse input: ${errorMessage(err)}`)
|
|
381
|
+
process.exit(1)
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
344
385
|
const filePath = flag ? FILES[flag] : FILES.default
|
|
345
386
|
|
|
346
387
|
if (!filePath) {
|
package/ai/componentMetadata.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict"
|
|
2
2
|
|
|
3
|
-
const CATEGORY_ORDER = ["xy", "ordinal", "network", "geo", "realtime"]
|
|
3
|
+
const CATEGORY_ORDER = ["xy", "ordinal", "network", "geo", "realtime", "value"]
|
|
4
4
|
|
|
5
5
|
const COMPONENTS_BY_CATEGORY = {
|
|
6
6
|
xy: [
|
|
@@ -24,6 +24,9 @@ const COMPONENTS_BY_CATEGORY = {
|
|
|
24
24
|
"RealtimeLineChart", "RealtimeHistogram", "TemporalHistogram", "RealtimeSwarmChart",
|
|
25
25
|
"RealtimeWaterfallChart", "RealtimeHeatmap",
|
|
26
26
|
],
|
|
27
|
+
value: [
|
|
28
|
+
"BigNumber",
|
|
29
|
+
],
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
const COMPONENT_TO_CATEGORY = new Map()
|
|
@@ -59,11 +62,17 @@ function metadataForComponent(entryOrName) {
|
|
|
59
62
|
// any other static HOC. Matches the name-prefix exclusion the
|
|
60
63
|
// check-surface-parity script applies.
|
|
61
64
|
const isPushOnly = category === "realtime" && name.startsWith("Realtime")
|
|
65
|
+
// Value-family charts (BigNumber, future SingleValueFrame HOCs)
|
|
66
|
+
// render via react-dom/server in a normal React tree, but they do
|
|
67
|
+
// not route through the frame-driven `renderChart` path in
|
|
68
|
+
// serverChartConfigs.ts — so they are not MCP-renderable. Mirrors
|
|
69
|
+
// the `hoc-ssr-only` special feature documented in chartSpecs.ts.
|
|
70
|
+
const isValueCategory = category === "value"
|
|
62
71
|
return {
|
|
63
72
|
name,
|
|
64
73
|
category,
|
|
65
74
|
importPath: importPathForCategory(category),
|
|
66
|
-
renderable: !isPushOnly,
|
|
75
|
+
renderable: !isPushOnly && !isValueCategory,
|
|
67
76
|
description: typeof entryOrName === "string" ? undefined : entryOrName.description,
|
|
68
77
|
}
|
|
69
78
|
}
|
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
|
}
|
|
@@ -32643,6 +32647,22 @@ ${contracts}` : msg}` }] };
|
|
|
32643
32647
|
isError: true
|
|
32644
32648
|
};
|
|
32645
32649
|
}
|
|
32650
|
+
async function auditAccessibilityHandler(args) {
|
|
32651
|
+
const component = args.component;
|
|
32652
|
+
const props = args.props ?? {};
|
|
32653
|
+
if (!component) {
|
|
32654
|
+
return {
|
|
32655
|
+
content: [{ type: "text", text: "Missing 'component' field. Provide { component: 'LineChart', props: { ... } }." }],
|
|
32656
|
+
isError: true
|
|
32657
|
+
};
|
|
32658
|
+
}
|
|
32659
|
+
const result = (0, import_ai3.auditAccessibility)(component, props, { inChartContainer: args.inChartContainer === true, describe: args.describe === true, navigable: args.navigable === true });
|
|
32660
|
+
return {
|
|
32661
|
+
content: [{ type: "text", text: (0, import_ai3.formatAccessibilityAudit)(result) }],
|
|
32662
|
+
// Only block on provable critical failures; warnings/manual items are advisory.
|
|
32663
|
+
isError: !result.ok
|
|
32664
|
+
};
|
|
32665
|
+
}
|
|
32646
32666
|
async function reportIssueHandler(args) {
|
|
32647
32667
|
const title = args.title;
|
|
32648
32668
|
const body = args.body;
|
|
@@ -32743,6 +32763,99 @@ Dark-mode presets: ${THEME_PRESET_NAMES.filter((n) => n.includes("dark")).join("
|
|
|
32743
32763
|
content: [{ type: "text", text: usage.join("\n") }]
|
|
32744
32764
|
};
|
|
32745
32765
|
}
|
|
32766
|
+
function profileInputFromVariantArgs(args) {
|
|
32767
|
+
const props = args.props ?? {};
|
|
32768
|
+
if (Array.isArray(args.data)) {
|
|
32769
|
+
return { data: args.data };
|
|
32770
|
+
}
|
|
32771
|
+
if (Array.isArray(props.data)) {
|
|
32772
|
+
return { data: props.data };
|
|
32773
|
+
}
|
|
32774
|
+
if (Array.isArray(props.nodes) && (Array.isArray(props.edges) || Array.isArray(props.links))) {
|
|
32775
|
+
return {
|
|
32776
|
+
data: [],
|
|
32777
|
+
rawInput: {
|
|
32778
|
+
nodes: props.nodes,
|
|
32779
|
+
edges: props.edges ?? props.links
|
|
32780
|
+
}
|
|
32781
|
+
};
|
|
32782
|
+
}
|
|
32783
|
+
if (props.data && typeof props.data === "object" && !Array.isArray(props.data)) {
|
|
32784
|
+
return { data: [], rawInput: props.data };
|
|
32785
|
+
}
|
|
32786
|
+
return { data: [] };
|
|
32787
|
+
}
|
|
32788
|
+
function buildVariantProposalProps(proposal, profile, audience) {
|
|
32789
|
+
if (proposal.buildProps) return proposal.buildProps(profile, audience);
|
|
32790
|
+
const capability = (0, import_ai3.getCapability)(proposal.baseComponent);
|
|
32791
|
+
const variant = proposal.variantKey ? capability?.variants?.find((v) => v.key === proposal.variantKey) : void 0;
|
|
32792
|
+
return capability ? capability.buildProps(profile, variant) : {};
|
|
32793
|
+
}
|
|
32794
|
+
async function proposeChartVariantsHandler(args) {
|
|
32795
|
+
const { component, intent, maxResults, audience } = args;
|
|
32796
|
+
const capability = (0, import_ai3.getCapability)(component);
|
|
32797
|
+
if (!capability) {
|
|
32798
|
+
return {
|
|
32799
|
+
content: [{ type: "text", text: `No chart capability registered for "${component}". Call suggestCharts first to pick from known capability components.` }],
|
|
32800
|
+
isError: true
|
|
32801
|
+
};
|
|
32802
|
+
}
|
|
32803
|
+
const { data, rawInput } = profileInputFromVariantArgs(args);
|
|
32804
|
+
const profile = (0, import_ai3.profileData)(data, { rawInput });
|
|
32805
|
+
const intentArg = Array.isArray(intent) ? intent : intent ? [intent] : void 0;
|
|
32806
|
+
const fitReason = capability.fits(profile);
|
|
32807
|
+
const proposals = (0, import_ai3.proposeVariant)(component, capability, {
|
|
32808
|
+
profile,
|
|
32809
|
+
audience,
|
|
32810
|
+
intent: intentArg,
|
|
32811
|
+
existingVariants: capability.variants
|
|
32812
|
+
});
|
|
32813
|
+
const ranked = proposals.map((proposal) => {
|
|
32814
|
+
const score = (0, import_ai3.evaluateVariantProposal)(proposal, profile, audience, {
|
|
32815
|
+
intent: intentArg,
|
|
32816
|
+
baselineComponent: component
|
|
32817
|
+
});
|
|
32818
|
+
const { buildProps: _buildProps, ...proposalMeta } = proposal;
|
|
32819
|
+
return {
|
|
32820
|
+
proposal: proposalMeta,
|
|
32821
|
+
score,
|
|
32822
|
+
props: buildVariantProposalProps(proposal, profile, audience)
|
|
32823
|
+
};
|
|
32824
|
+
}).sort((a, b) => {
|
|
32825
|
+
if (b.score.fit !== a.score.fit) return b.score.fit - a.score.fit;
|
|
32826
|
+
if (a.score.risk !== b.score.risk) return a.score.risk - b.score.risk;
|
|
32827
|
+
return b.score.novelty - a.score.novelty;
|
|
32828
|
+
}).slice(0, maxResults ?? 8);
|
|
32829
|
+
const lines = [
|
|
32830
|
+
`${ranked.length} variant proposal${ranked.length === 1 ? "" : "s"} for ${component}${intentArg ? ` (intent: ${intentArg.join(", ")})` : ""}:`,
|
|
32831
|
+
...fitReason ? [`Base chart fit warning: ${fitReason}`] : [],
|
|
32832
|
+
"",
|
|
32833
|
+
...ranked.map((entry, i) => {
|
|
32834
|
+
const label = entry.proposal.label ?? entry.proposal.variantKey ?? entry.proposal.id;
|
|
32835
|
+
const tags = entry.proposal.tags?.length ? ` [${entry.proposal.tags.join(", ")}]` : "";
|
|
32836
|
+
const reasons = entry.score.reasons.length ? `
|
|
32837
|
+
${entry.score.reasons.join("; ")}` : "";
|
|
32838
|
+
return `${i + 1}. ${entry.proposal.baseComponent} / ${label}${tags} (fit ${entry.score.fit.toFixed(1)}/5, novelty ${entry.score.novelty.toFixed(2)}, risk ${entry.score.risk.toFixed(2)})${reasons}`;
|
|
32839
|
+
})
|
|
32840
|
+
];
|
|
32841
|
+
return {
|
|
32842
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
32843
|
+
structuredContent: {
|
|
32844
|
+
component,
|
|
32845
|
+
profile: {
|
|
32846
|
+
rowCount: profile.rowCount,
|
|
32847
|
+
primary: profile.primary,
|
|
32848
|
+
categoryCount: profile.categoryCount ?? null,
|
|
32849
|
+
seriesCount: profile.seriesCount ?? null,
|
|
32850
|
+
hasHierarchy: profile.hasHierarchy,
|
|
32851
|
+
hasNetwork: profile.hasNetwork,
|
|
32852
|
+
hasGeo: profile.hasGeo
|
|
32853
|
+
},
|
|
32854
|
+
fitReason,
|
|
32855
|
+
proposals: ranked
|
|
32856
|
+
}
|
|
32857
|
+
};
|
|
32858
|
+
}
|
|
32746
32859
|
async function suggestChartsHandler(args) {
|
|
32747
32860
|
const { data, intent, maxResults, allow, deny, audience } = args;
|
|
32748
32861
|
const intentArg = Array.isArray(intent) ? intent : intent ? [intent] : void 0;
|
|
@@ -32903,6 +33016,34 @@ Contextual instructions:
|
|
|
32903
33016
|
}
|
|
32904
33017
|
return { content, structuredContent: { summary, component, props } };
|
|
32905
33018
|
}
|
|
33019
|
+
async function groundChartHandler(args) {
|
|
33020
|
+
const component = args.component;
|
|
33021
|
+
const props = args.props ?? {};
|
|
33022
|
+
if (!component) {
|
|
33023
|
+
return {
|
|
33024
|
+
content: [{ type: "text", text: "Missing 'component' field. Provide { component: 'LineChart', props: { ... } }." }],
|
|
33025
|
+
isError: true
|
|
33026
|
+
};
|
|
33027
|
+
}
|
|
33028
|
+
const capability = (0, import_ai3.getCapability)(component);
|
|
33029
|
+
const grounding = (0, import_ai3.buildReaderGrounding)(component, props, { capability });
|
|
33030
|
+
const nodeCount = grounding.structure ? (0, import_ai3.countNodes)(grounding.structure) : 0;
|
|
33031
|
+
const lines = [
|
|
33032
|
+
`Reader grounding for ${component} \u2014 the payload an agent reads to interpret this chart without seeing it:`,
|
|
33033
|
+
"",
|
|
33034
|
+
`L1\u2013L3 (description): ${grounding.description.text}`,
|
|
33035
|
+
grounding.intent ? `L4 (intent \xB7 ${grounding.intent.act}): ${grounding.intent.sentence}` : "L4 (intent): not resolved (no capability for this component).",
|
|
33036
|
+
"",
|
|
33037
|
+
`Structure: ${nodeCount} navigable node(s) (chart \u2192 axes/series \u2192 datum) in structuredContent.structure.`,
|
|
33038
|
+
"",
|
|
33039
|
+
"Combined text:",
|
|
33040
|
+
grounding.text
|
|
33041
|
+
];
|
|
33042
|
+
return {
|
|
33043
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
33044
|
+
structuredContent: grounding
|
|
33045
|
+
};
|
|
33046
|
+
}
|
|
32906
33047
|
function createServer2() {
|
|
32907
33048
|
const srv = new McpServer({
|
|
32908
33049
|
name: "semiotic",
|
|
@@ -33069,6 +33210,18 @@ function createServer2() {
|
|
|
33069
33210
|
},
|
|
33070
33211
|
diagnoseConfigHandler
|
|
33071
33212
|
);
|
|
33213
|
+
srv.tool(
|
|
33214
|
+
"auditAccessibility",
|
|
33215
|
+
"Audit a Semiotic chart configuration against the Chartability (POUR-CAF) accessibility framework \u2014 Perceivable, Operable, Understandable, Robust, Compromising, Assistive, Flexible. Statically grades the config (no DOM/AT): credits the built-ins every HOC ships (keyboard nav, focus ring, skip link, screen-reader data table, reduced-motion + forced-colors, shareable state), flags author-actionable gaps (missing title/description/summary, low contrast, small text, color-only encoding, undescribed trends, data density), and routes everything that needs real assistive-technology testing to a 'manual' item. Returns a per-principle report with the 14 critical heuristics marked. Pass inChartContainer=true to credit data-download/share affordances. Pair with manual NVDA/JAWS/VoiceOver testing \u2014 Chartability is not a pass/fail certification.",
|
|
33216
|
+
{
|
|
33217
|
+
component: external_exports3.string().describe("Chart component name, e.g. 'LineChart'"),
|
|
33218
|
+
props: external_exports3.record(external_exports3.string(), external_exports3.unknown()).optional().describe("Chart props object, e.g. { data: [...], xAccessor: 'x', title: '...' }."),
|
|
33219
|
+
inChartContainer: external_exports3.boolean().optional().describe("True if the chart is (or will be) wrapped in a ChartContainer exposing data-download/copy-config actions."),
|
|
33220
|
+
describe: external_exports3.boolean().optional().describe("True if ChartContainer's describe option (auto-generated L1\u2013L3 description via describeChart) is enabled \u2014 passes the 'features described' heuristic."),
|
|
33221
|
+
navigable: external_exports3.boolean().optional().describe("True if ChartContainer's navigable option (structured navigation tree via buildNavigationTree) is enabled \u2014 passes the 'navigable structure' heuristic.")
|
|
33222
|
+
},
|
|
33223
|
+
auditAccessibilityHandler
|
|
33224
|
+
);
|
|
33072
33225
|
srv.tool(
|
|
33073
33226
|
"reportIssue",
|
|
33074
33227
|
"Generate a GitHub issue URL for Semiotic bug reports or feature requests. Returns a URL the user can open to submit. For rendering bugs, include the component name, props summary, and any diagnoseConfig output in the body.",
|
|
@@ -33097,6 +33250,15 @@ function createServer2() {
|
|
|
33097
33250
|
},
|
|
33098
33251
|
interrogateChartHandler
|
|
33099
33252
|
);
|
|
33253
|
+
srv.tool(
|
|
33254
|
+
"groundChart",
|
|
33255
|
+
"Build the agent-reader grounding payload for a Semiotic chart: the layered L1\u2013L3 natural-language description, the L4 communicative-act sentence (what the chart is asking the reader to do \u2014 'this is an alerting chart; the spike warrants a closer look'), and a structured navigation tree (chart \u2192 axes/series \u2192 datum). This is the documented thing an LLM reads to interpret a chart faithfully without seeing the pixels \u2014 the reader-side complement to a capability descriptor. The L4 act is resolved from the chart's registered capability. Returns prose plus the full structured payload (description/intent/structure/text).",
|
|
33256
|
+
{
|
|
33257
|
+
component: external_exports3.string().describe("Chart component name, e.g. 'LineChart'"),
|
|
33258
|
+
props: external_exports3.record(external_exports3.string(), external_exports3.unknown()).describe("The full chart props including data")
|
|
33259
|
+
},
|
|
33260
|
+
groundChartHandler
|
|
33261
|
+
);
|
|
33100
33262
|
srv.tool(
|
|
33101
33263
|
"suggestStreamCharts",
|
|
33102
33264
|
"Recommend realtime/streaming Semiotic charts for a schema (not row data). Pass a schema describing field types plus optional throughput ('low'|'medium'|'high') and retention ('windowed'|'cumulative') hints; the engine ranks realtime charts (RealtimeLineChart, RealtimeHistogram, RealtimeHeatmap, RealtimeWaterfallChart, RealtimeSwarmChart, TemporalHistogram) by their fit. Use when the user is wiring up a live dashboard or monitoring view rather than visualizing a bounded dataset.",
|
|
@@ -33144,8 +33306,9 @@ function createServer2() {
|
|
|
33144
33306
|
reason: external_exports3.string().optional()
|
|
33145
33307
|
})
|
|
33146
33308
|
).optional(),
|
|
33147
|
-
exposureLevel: external_exports3.union([external_exports3.literal(0), external_exports3.literal(1), external_exports3.literal(2)]).optional()
|
|
33148
|
-
|
|
33309
|
+
exposureLevel: external_exports3.union([external_exports3.literal(0), external_exports3.literal(1), external_exports3.literal(2)]).optional(),
|
|
33310
|
+
receptionModality: external_exports3.enum(["visual", "screen-reader", "sonified", "agent"]).optional().describe("Reception channel \u2014 see suggestCharts.")
|
|
33311
|
+
}).describe("Audience profile \u2014 familiarity, targets, exposure level, reception modality."),
|
|
33149
33312
|
intent: external_exports3.union([external_exports3.string(), external_exports3.array(external_exports3.string())]).optional(),
|
|
33150
33313
|
maxResults: external_exports3.number().int().min(1).max(20).optional()
|
|
33151
33314
|
},
|
|
@@ -33162,6 +33325,32 @@ function createServer2() {
|
|
|
33162
33325
|
},
|
|
33163
33326
|
repairChartConfigHandler
|
|
33164
33327
|
);
|
|
33328
|
+
srv.tool(
|
|
33329
|
+
"proposeChartVariants",
|
|
33330
|
+
"Propose and score chart variants for a selected Semiotic component. Uses the capability registry plus heuristic variant discovery: registered variants, conservative transforms, and same-intent cross-family alternatives. Returns ranked proposals with fit/novelty/risk scores, rationale, and ready-to-use props. Use after suggestCharts when an agent wants to actively explore variants rather than stop at the first chart recommendation.",
|
|
33331
|
+
{
|
|
33332
|
+
component: external_exports3.string().describe("Base chart component to vary, e.g. 'LineChart', 'BarChart', or 'BoxPlot'."),
|
|
33333
|
+
props: external_exports3.record(external_exports3.string(), external_exports3.unknown()).optional().describe("Existing chart props. If props.data is present it is profiled; network/hierarchy/geo object data can be passed here as raw input."),
|
|
33334
|
+
data: external_exports3.array(external_exports3.record(external_exports3.string(), external_exports3.unknown())).optional().describe("Row data to profile. Overrides props.data when present."),
|
|
33335
|
+
intent: external_exports3.union([external_exports3.string(), external_exports3.array(external_exports3.string())]).optional().describe("Ranking intent(s), e.g. trend, distribution, rank, compare-categories, composition-over-time."),
|
|
33336
|
+
maxResults: external_exports3.number().int().min(1).max(20).optional().describe("Cap on proposals returned (default 8)."),
|
|
33337
|
+
audience: external_exports3.object({
|
|
33338
|
+
name: external_exports3.string().optional(),
|
|
33339
|
+
familiarity: external_exports3.record(external_exports3.string(), external_exports3.number()).optional(),
|
|
33340
|
+
targets: external_exports3.record(
|
|
33341
|
+
external_exports3.string(),
|
|
33342
|
+
external_exports3.object({
|
|
33343
|
+
direction: external_exports3.enum(["increase", "decrease"]),
|
|
33344
|
+
weight: external_exports3.number().int().min(1).max(3).optional(),
|
|
33345
|
+
reason: external_exports3.string().optional()
|
|
33346
|
+
})
|
|
33347
|
+
).optional(),
|
|
33348
|
+
exposureLevel: external_exports3.union([external_exports3.literal(0), external_exports3.literal(1), external_exports3.literal(2)]).optional(),
|
|
33349
|
+
receptionModality: external_exports3.enum(["visual", "screen-reader", "sonified", "agent"]).optional().describe("Reception channel \u2014 see suggestCharts.")
|
|
33350
|
+
}).optional().describe("Audience profile \u2014 familiarity, adoption targets, exposure level, and reception modality.")
|
|
33351
|
+
},
|
|
33352
|
+
proposeChartVariantsHandler
|
|
33353
|
+
);
|
|
33165
33354
|
srv.tool(
|
|
33166
33355
|
"suggestCharts",
|
|
33167
33356
|
"Recommend Semiotic charts for a dataset using heuristic capability descriptors. Each chart declares which data shapes it serves and which intents (trend, compare-categories, distribution, correlation, part-to-whole, etc.) it answers \u2014 the engine returns a ranked list with scores, reasons, caveats, and ready-to-use props. Heuristic only; no LLM call. Use the result as structured context when answering 'what chart should I use?' or generating chart code.",
|
|
@@ -33170,7 +33359,21 @@ function createServer2() {
|
|
|
33170
33359
|
intent: external_exports3.union([external_exports3.string(), external_exports3.array(external_exports3.string())]).optional().describe("Ranking intent. One of: trend, compare-series, compare-categories, rank, part-to-whole, distribution, correlation, flow, hierarchy, geo, outlier-detection, composition-over-time, change-detection. Custom intents accepted."),
|
|
33171
33360
|
maxResults: external_exports3.number().int().min(1).max(40).optional().describe("Cap on suggestions returned (default 8)."),
|
|
33172
33361
|
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.")
|
|
33362
|
+
deny: external_exports3.array(external_exports3.string()).optional().describe("Exclude these component names."),
|
|
33363
|
+
audience: external_exports3.object({
|
|
33364
|
+
name: external_exports3.string().optional(),
|
|
33365
|
+
familiarity: external_exports3.record(external_exports3.string(), external_exports3.number()).optional(),
|
|
33366
|
+
targets: external_exports3.record(
|
|
33367
|
+
external_exports3.string(),
|
|
33368
|
+
external_exports3.object({
|
|
33369
|
+
direction: external_exports3.enum(["increase", "decrease"]),
|
|
33370
|
+
weight: external_exports3.number().int().min(1).max(3).optional(),
|
|
33371
|
+
reason: external_exports3.string().optional()
|
|
33372
|
+
})
|
|
33373
|
+
).optional(),
|
|
33374
|
+
exposureLevel: external_exports3.union([external_exports3.literal(0), external_exports3.literal(1), external_exports3.literal(2)]).optional(),
|
|
33375
|
+
receptionModality: external_exports3.enum(["visual", "screen-reader", "sonified", "agent"]).optional().describe("Reception channel. A non-visual value down-ranks charts the audience can't receive in that channel (e.g. a many-slice pie for a screen reader) and adds receivability caveats.")
|
|
33376
|
+
}).optional().describe("Audience profile \u2014 familiarity, adoption targets, exposure level, and reception modality.")
|
|
33174
33377
|
},
|
|
33175
33378
|
suggestChartsHandler
|
|
33176
33379
|
);
|
|
@@ -33220,7 +33423,7 @@ async function main() {
|
|
|
33220
33423
|
});
|
|
33221
33424
|
httpServer.listen(port, () => {
|
|
33222
33425
|
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");
|
|
33426
|
+
console.error("Tools: getSchema, suggestChart, suggestCharts, proposeChartVariants, suggestStreamCharts, suggestDashboard, suggestStretchCharts, repairChartConfig, renderChart, interrogateChart, groundChart, diagnoseConfig, auditAccessibility, reportIssue, applyTheme");
|
|
33224
33427
|
console.error("Resources: semiotic://schema, semiotic://components, semiotic://behavior-contracts, semiotic://system-prompt, semiotic://examples");
|
|
33225
33428
|
});
|
|
33226
33429
|
} else {
|
package/ai/examples.md
CHANGED
|
@@ -1181,3 +1181,101 @@ import { BarChart } from "semiotic/ai"
|
|
|
1181
1181
|
```
|
|
1182
1182
|
|
|
1183
1183
|
Key props: `y-threshold` works on vertical ordinal charts. `category-highlight` highlights a category column. `labelPosition` controls label placement.
|
|
1184
|
+
|
|
1185
|
+
---
|
|
1186
|
+
|
|
1187
|
+
## Value Charts — One Number Is The Visualization
|
|
1188
|
+
|
|
1189
|
+
### BigNumber (KPI tile — comparison + target + threshold zones)
|
|
1190
|
+
|
|
1191
|
+
```jsx
|
|
1192
|
+
import { BigNumber } from "semiotic/value"
|
|
1193
|
+
|
|
1194
|
+
<BigNumber
|
|
1195
|
+
value={1284900}
|
|
1196
|
+
label="Q3 Revenue"
|
|
1197
|
+
caption="Year-to-date bookings"
|
|
1198
|
+
format="currency"
|
|
1199
|
+
precision={0}
|
|
1200
|
+
comparison={{ value: 980000, label: "vs Q2" }}
|
|
1201
|
+
target={{ value: 1500000, label: "Q3 plan" }}
|
|
1202
|
+
thresholds={[
|
|
1203
|
+
{ at: -Infinity, level: "danger" },
|
|
1204
|
+
{ at: 1000000, level: "warning" },
|
|
1205
|
+
{ at: 1300000, level: "success" },
|
|
1206
|
+
]}
|
|
1207
|
+
/>
|
|
1208
|
+
```
|
|
1209
|
+
|
|
1210
|
+
Key props: `value` (the one number), `format` ("number"|"currency"|"percent"|"compact"|"duration"|fn), `comparison` derives a delta with auto-sentiment, `target` renders "X% of goal", `thresholds` map value to a semantic theme role (`--semiotic-{success|warning|danger|info}`). Suppress chrome via `mode="thumbnail"` for dense grids or `mode="inline"` for prose. Stream via `ref.current.push({ value, time })` — pair with `stalenessThreshold` to dim the card when updates stop.
|
|
1211
|
+
|
|
1212
|
+
### BigNumber with a Semiotic chart embedded via `trendSlot` (wide / rectangular)
|
|
1213
|
+
|
|
1214
|
+
```jsx
|
|
1215
|
+
import { BigNumber } from "semiotic/value"
|
|
1216
|
+
import { LineChart } from "semiotic/xy"
|
|
1217
|
+
|
|
1218
|
+
<BigNumber
|
|
1219
|
+
value={1284900}
|
|
1220
|
+
label="Q3 Revenue"
|
|
1221
|
+
format="currency"
|
|
1222
|
+
comparison={{ value: 980000, label: "vs Q2" }}
|
|
1223
|
+
trendSlot={(ctx) => (
|
|
1224
|
+
<LineChart
|
|
1225
|
+
data={[820000, 870000, 920000, 1010000, 1120000, 1284900].map((y, x) => ({ x, y }))}
|
|
1226
|
+
xAccessor="x"
|
|
1227
|
+
yAccessor="y"
|
|
1228
|
+
mode="sparkline"
|
|
1229
|
+
width={260}
|
|
1230
|
+
height={32}
|
|
1231
|
+
color={ctx.color}
|
|
1232
|
+
/>
|
|
1233
|
+
)}
|
|
1234
|
+
/>
|
|
1235
|
+
```
|
|
1236
|
+
|
|
1237
|
+
Key props: BigNumber ships NO built-in chart renderer. `trendSlot` accepts any ReactNode (or `(ctx) => ReactNode`); pass a `LineChart`/`AreaChart` in `mode="sparkline"` for wide / rectangular charts. The slot context exposes `ctx.color` (resolved threshold colour) so the embedded chart picks up the BigNumber's level for cohesive theming.
|
|
1238
|
+
|
|
1239
|
+
### BigNumber with a square Semiotic chart via `chartSlot`
|
|
1240
|
+
|
|
1241
|
+
```jsx
|
|
1242
|
+
import { BigNumber } from "semiotic/value"
|
|
1243
|
+
import { DonutChart } from "semiotic/ordinal"
|
|
1244
|
+
|
|
1245
|
+
<BigNumber
|
|
1246
|
+
value={1284900}
|
|
1247
|
+
label="Q3 Revenue by region"
|
|
1248
|
+
format="currency"
|
|
1249
|
+
chartSlot={
|
|
1250
|
+
<DonutChart
|
|
1251
|
+
data={[
|
|
1252
|
+
{ region: "NA", revenue: 540000 },
|
|
1253
|
+
{ region: "EU", revenue: 420000 },
|
|
1254
|
+
{ region: "APAC", revenue: 324900 },
|
|
1255
|
+
]}
|
|
1256
|
+
categoryAccessor="region"
|
|
1257
|
+
valueAccessor="revenue"
|
|
1258
|
+
width={120}
|
|
1259
|
+
height={120}
|
|
1260
|
+
innerRadius={32}
|
|
1261
|
+
/>
|
|
1262
|
+
}
|
|
1263
|
+
/>
|
|
1264
|
+
```
|
|
1265
|
+
|
|
1266
|
+
Key props: `chartSlot` is the square-aspect counterpart to `trendSlot`. The card splits horizontally — text content on the left, square chart on the right (DonutChart / PieChart / Scatterplot / Treemap / CirclePack). Pass both `trendSlot` and `chartSlot` to get the square chart on the right and the wide sparkline at the bottom.
|
|
1267
|
+
|
|
1268
|
+
### BigNumber (inverted direction — lower is better)
|
|
1269
|
+
|
|
1270
|
+
```jsx
|
|
1271
|
+
import { BigNumber } from "semiotic/value"
|
|
1272
|
+
|
|
1273
|
+
<BigNumber
|
|
1274
|
+
value={486}
|
|
1275
|
+
label="P99 latency"
|
|
1276
|
+
suffix=" ms"
|
|
1277
|
+
comparison={{ value: 410, label: "vs last week", direction: "lower-is-better" }}
|
|
1278
|
+
/>
|
|
1279
|
+
```
|
|
1280
|
+
|
|
1281
|
+
Key props: `direction: "lower-is-better"` flips sentiment colouring — a value that went UP now reads as negative (danger). Same pattern for error rate, churn, complaint count, etc.
|