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
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
import type { ChartRubric } from "./chartCapabilityTypes";
|
|
2
|
+
import type { AccessibilityAuditResult } from "../charts/shared/auditAccessibility";
|
|
3
|
+
/**
|
|
4
|
+
* The channel an audience receives a chart through. Orthogonal to familiarity:
|
|
5
|
+
* a reader can be highly familiar with a chart type yet unable to *receive* it
|
|
6
|
+
* in their channel (an 8-slice pie is familiar but illegible to a screen
|
|
7
|
+
* reader). Defaults to `"visual"` when unset — no receivability bias applied.
|
|
8
|
+
*
|
|
9
|
+
* • `visual` — sighted reader; the historical default, no bias.
|
|
10
|
+
* • `screen-reader` — non-visual; meaning must survive the data table / nav tree.
|
|
11
|
+
* • `sonified` — audio; density and ordering matter more than color.
|
|
12
|
+
* • `agent` — an LLM reading via the grounding payload; same non-visual
|
|
13
|
+
* "is the meaning recoverable without pixels?" question.
|
|
14
|
+
*/
|
|
15
|
+
export type ReceptionModality = "visual" | "screen-reader" | "sonified" | "agent";
|
|
2
16
|
/**
|
|
3
17
|
* A serializable description of who's reading the charts and what the
|
|
4
18
|
* organization is trying to grow.
|
|
@@ -8,8 +22,6 @@ import type { ChartRubric } from "./chartCapabilityTypes";
|
|
|
8
22
|
* telemetry, manager judgment, training records) and pass it to the
|
|
9
23
|
* suggestion APIs. The library applies the bias and returns rankings that
|
|
10
24
|
* reflect the audience instead of a generic data-literate baseline.
|
|
11
|
-
*
|
|
12
|
-
* Strategy memo: docs/strategy/audience-profiles.md
|
|
13
25
|
*/
|
|
14
26
|
export interface AudienceProfile {
|
|
15
27
|
/**
|
|
@@ -47,6 +59,15 @@ export interface AudienceProfile {
|
|
|
47
59
|
* widening the menu
|
|
48
60
|
*/
|
|
49
61
|
exposureLevel?: 0 | 1 | 2;
|
|
62
|
+
/**
|
|
63
|
+
* The channel this audience receives charts through. When set to a non-visual
|
|
64
|
+
* modality, `suggestCharts` runs the accessibility audit on each candidate and
|
|
65
|
+
* `applyAudienceBias` down-ranks charts whose meaning doesn't survive that
|
|
66
|
+
* channel (and surfaces the receivability findings as caveats). Unset /
|
|
67
|
+
* `"visual"` keeps the historical behavior — familiarity and targets only.
|
|
68
|
+
* See {@link receivabilityBias}.
|
|
69
|
+
*/
|
|
70
|
+
receptionModality?: ReceptionModality;
|
|
50
71
|
}
|
|
51
72
|
export interface AudienceTarget {
|
|
52
73
|
direction: "increase" | "decrease";
|
|
@@ -62,7 +83,34 @@ export interface AudienceBiasResult {
|
|
|
62
83
|
rubric: ChartRubric;
|
|
63
84
|
/** Reason string to append to the suggestion when a target fired. */
|
|
64
85
|
appliedReason?: string;
|
|
86
|
+
/** Reason string to append when a receivability penalty fired (non-visual modality). */
|
|
87
|
+
receivabilityReason?: string;
|
|
88
|
+
}
|
|
89
|
+
export interface ReceivabilitySignal {
|
|
90
|
+
/** Score delta (≤ 0) — the receivability penalty for this channel. */
|
|
91
|
+
delta: number;
|
|
92
|
+
/** One-line reason, present only when a penalty applied. */
|
|
93
|
+
reason?: string;
|
|
94
|
+
/**
|
|
95
|
+
* Caveat messages for the findings that drove the penalty — the receivability
|
|
96
|
+
* slice of the audit, ready to merge into a suggestion's `caveats[]` alongside
|
|
97
|
+
* the perceptual ones (so both channels read from the same array).
|
|
98
|
+
*/
|
|
99
|
+
caveats: string[];
|
|
65
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Translate an accessibility audit into a receivability penalty for a non-visual
|
|
103
|
+
* channel. Pure. `fail` findings on receivability-critical heuristics weigh most;
|
|
104
|
+
* `warn` findings weigh less; `manual`/`pass`/`not-applicable` never penalize
|
|
105
|
+
* (we don't punish what we can't prove). Penalty is clamped to a −3 floor so it
|
|
106
|
+
* reorders rankings without overriding data-shape correctness.
|
|
107
|
+
*
|
|
108
|
+
* ```ts
|
|
109
|
+
* const audit = auditAccessibility("PieChart", props)
|
|
110
|
+
* const { delta, reason } = receivabilityBias(audit, "screen-reader")
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export declare function receivabilityBias(audit: AccessibilityAuditResult, modality: ReceptionModality): ReceivabilitySignal;
|
|
66
114
|
/**
|
|
67
115
|
* Apply an AudienceProfile's bias to a chart's composite score and rubric.
|
|
68
116
|
* Pure function — used by both `suggestCharts` and `suggestStretchCharts`.
|
|
@@ -75,8 +123,17 @@ export interface AudienceBiasResult {
|
|
|
75
123
|
* not so strong that it overrides chart correctness for the data shape.
|
|
76
124
|
*
|
|
77
125
|
* Score is left unclamped so internal sorting reflects the magnitude of bias.
|
|
126
|
+
*
|
|
127
|
+
* A third term — *receivability* — composes in when the audience declares a
|
|
128
|
+
* non-visual `receptionModality` and the caller passes a precomputed
|
|
129
|
+
* {@link ReceivabilitySignal} (from {@link receivabilityBias}). Familiarity and
|
|
130
|
+
* receivability are different axes: a chart can be familiar yet unreceivable in
|
|
131
|
+
* the target channel, and this folds that signal into the same score the
|
|
132
|
+
* recommender ranks by. The caller computes the signal once and reuses it for
|
|
133
|
+
* the suggestion's caveats too, so the audit is scanned a single time per
|
|
134
|
+
* candidate.
|
|
78
135
|
*/
|
|
79
|
-
export declare function applyAudienceBias(baseScore: number, baseRubric: ChartRubric, component: string, audience: AudienceProfile | undefined): AudienceBiasResult;
|
|
136
|
+
export declare function applyAudienceBias(baseScore: number, baseRubric: ChartRubric, component: string, audience: AudienceProfile | undefined, receivability?: ReceivabilitySignal): AudienceBiasResult;
|
|
80
137
|
/**
|
|
81
138
|
* Resolve the effective familiarity for a chart under an audience. Used by
|
|
82
139
|
* the stretch surface to decide whether a chart qualifies as "unfamiliar."
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import type { Datum } from "../charts/shared/datumTypes";
|
|
2
2
|
import type { DataSummary } from "../data/DataSummarizer";
|
|
3
3
|
import type { IntentId } from "./intents";
|
|
4
|
+
import type { ScaleFitFn, QualityFitFn, ScaleBand, CardinalityBand, EffectiveScale } from "./dataScaleProfile";
|
|
4
5
|
/**
|
|
5
6
|
* Chart family — high-level taxonomy used for filtering and intent matching.
|
|
6
7
|
*/
|
|
7
|
-
export type ChartFamily = "time-series" | "categorical" | "distribution" | "relationship" | "flow" | "network" | "hierarchy" | "geo" | "realtime" | "custom";
|
|
8
|
+
export type ChartFamily = "time-series" | "categorical" | "distribution" | "relationship" | "flow" | "network" | "hierarchy" | "geo" | "realtime" | "value" | "custom";
|
|
8
9
|
/**
|
|
9
10
|
* Where a chart is imported from. Used by generators to emit correct import paths.
|
|
10
11
|
*/
|
|
11
|
-
export type ChartImportPath = "semiotic/xy" | "semiotic/ordinal" | "semiotic/network" | "semiotic/geo" | "semiotic/realtime" | "semiotic/ai" | "semiotic";
|
|
12
|
+
export type ChartImportPath = "semiotic/xy" | "semiotic/ordinal" | "semiotic/network" | "semiotic/geo" | "semiotic/realtime" | "semiotic/value" | "semiotic/ai" | "semiotic";
|
|
12
13
|
/**
|
|
13
14
|
* Familiarity/accuracy/precision rubric (1-5 each).
|
|
14
15
|
* Familiarity = how well-known the chart is to a general audience.
|
|
@@ -171,6 +172,38 @@ export interface ChartCapability {
|
|
|
171
172
|
* a runnable config (accessor names, etc.) so consumers can `<Component {...props}>`.
|
|
172
173
|
*/
|
|
173
174
|
buildProps: (profile: ChartDataProfile, variant?: ChartVariant) => Record<string, unknown>;
|
|
175
|
+
/**
|
|
176
|
+
* Optional declaration of this chart's sweet spot under scale. Receives the
|
|
177
|
+
* effective scale (declared `DataScaleProfile` merged with measured profile)
|
|
178
|
+
* and returns a score delta plus optional caveats. Use `scaleHints(...)` from
|
|
179
|
+
* `dataScaleProfile.ts` for the common declarative shape.
|
|
180
|
+
*
|
|
181
|
+
* When omitted, the engine assumes the chart is scale-agnostic and applies
|
|
182
|
+
* no scale bias beyond what `intentScores` already encodes in `profile`.
|
|
183
|
+
*/
|
|
184
|
+
scaleFit?: ScaleFitFn;
|
|
185
|
+
/**
|
|
186
|
+
* Optional declaration of this chart's response to data quality (missingness,
|
|
187
|
+
* outliers, type heterogeneity). Returns a score delta plus caveats. When
|
|
188
|
+
* omitted, the engine applies a default heuristic that adds caveats for
|
|
189
|
+
* low completeness on the primary y field.
|
|
190
|
+
*/
|
|
191
|
+
qualityFit?: QualityFitFn;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Effective scale range tag on a suggestion. Lets callers group suggestions
|
|
195
|
+
* by their scale sweet spot, detect threshold crossings as data grows, and
|
|
196
|
+
* narrate why a recommendation changed.
|
|
197
|
+
*/
|
|
198
|
+
export interface SuggestionScaleRange {
|
|
199
|
+
/** Band classification of the row count this suggestion was evaluated against. */
|
|
200
|
+
band: ScaleBand;
|
|
201
|
+
/** Cardinality band, if known. */
|
|
202
|
+
cardinalityBand?: CardinalityBand;
|
|
203
|
+
/** Effective row count the engine reasoned over (declared or measured). */
|
|
204
|
+
rows: number;
|
|
205
|
+
/** Whether the row count came from a user-declared profile or the measured data. */
|
|
206
|
+
rowsSource: "declared" | "measured";
|
|
174
207
|
}
|
|
175
208
|
/**
|
|
176
209
|
* One suggestion produced by `suggestCharts`. Consumers render this as a card,
|
|
@@ -193,4 +226,29 @@ export interface Suggestion {
|
|
|
193
226
|
caveats: ReadonlyArray<string>;
|
|
194
227
|
/** Ready-to-spread props. */
|
|
195
228
|
props: Record<string, unknown>;
|
|
229
|
+
/**
|
|
230
|
+
* Scale tag — present when scale/quality information is available, either
|
|
231
|
+
* through declared `DataScaleProfile` or through the measured profile.
|
|
232
|
+
* Surfaces what band this chart was scored at so consumers can group by
|
|
233
|
+
* scale, detect threshold crossings, or narrate "the chart for now → at 10×."
|
|
234
|
+
*/
|
|
235
|
+
scaleRange?: SuggestionScaleRange;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Multi-tier grouping of suggestions by scale band. Returned by
|
|
239
|
+
* `suggestChartsGrouped()`.
|
|
240
|
+
*
|
|
241
|
+
* Each tier is a ranked list of suggestions whose `scaleRange.band` falls in
|
|
242
|
+
* that tier. A single chart can appear in multiple tiers when its sweet-spot
|
|
243
|
+
* range spans them — that's the "graduation of views" surface: the same data
|
|
244
|
+
* gets a different chart depending on which scale you're optimizing for.
|
|
245
|
+
*/
|
|
246
|
+
export interface ScaledSuggestionGroups {
|
|
247
|
+
tiny: Suggestion[];
|
|
248
|
+
small: Suggestion[];
|
|
249
|
+
medium: Suggestion[];
|
|
250
|
+
large: Suggestion[];
|
|
251
|
+
huge: Suggestion[];
|
|
252
|
+
/** The effective scale view the engine reasoned over. */
|
|
253
|
+
effective: EffectiveScale;
|
|
196
254
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Datum } from "../charts/shared/datumTypes";
|
|
2
|
+
/**
|
|
3
|
+
* Shared chart-family classification + measure/dimension role resolution.
|
|
4
|
+
*
|
|
5
|
+
* `describeChart` (prose) and `buildNavigationTree` (structure) both need to
|
|
6
|
+
* know which family a component belongs to and which props carry its measure
|
|
7
|
+
* (quantitative) and dimension (categorical/ordered) accessors. This is the
|
|
8
|
+
* single source of truth so the two surfaces can't drift apart.
|
|
9
|
+
*
|
|
10
|
+
* Pure, dependency-light (types only).
|
|
11
|
+
*/
|
|
12
|
+
export declare const XY_FAMILY: Set<string>;
|
|
13
|
+
export declare const BAR_FAMILY: Set<string>;
|
|
14
|
+
export declare const PART_TO_WHOLE: Set<string>;
|
|
15
|
+
export declare const DISTRIBUTION: Set<string>;
|
|
16
|
+
export interface ChartRoles {
|
|
17
|
+
measure?: string;
|
|
18
|
+
measureFallback: string;
|
|
19
|
+
dimension?: string;
|
|
20
|
+
dimensionFallback: string;
|
|
21
|
+
}
|
|
22
|
+
/** The measure (quantitative) and dimension (categorical/ordered) accessors, by family. */
|
|
23
|
+
export declare function roles(component: string, props: Datum): ChartRoles;
|
|
24
|
+
/** First string-valued series-splitting accessor among the known channels, if any. */
|
|
25
|
+
export declare function seriesField(props: Datum): string | undefined;
|
|
26
|
+
/** Format a dimension value (the label at an extremum) — dates as ISO day, numbers compactly. */
|
|
27
|
+
export declare function fmtDim(v: unknown, fmtNum: (n: number) => string): string;
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
import type { AnnotationStatus } from "./annotationProvenance";
|
|
2
|
+
export type ConversationArcEventType = "suggestion-shown" | "suggestion-chosen" | "audience-set" | "chart-rendered" | "chart-edited" | "chart-replaced" | "chart-exported" | "chart-abandoned" | "interrogation-asked" | "interrogation-answered" | "nav-node-focused" | "nav-branch-expanded" | "annotation-status-changed";
|
|
3
|
+
interface ConversationArcEventBase {
|
|
4
|
+
/** Discriminator for the event variant. */
|
|
5
|
+
type: ConversationArcEventType;
|
|
6
|
+
/** `Date.now()` at the moment the event was recorded. Stamped by the store. */
|
|
7
|
+
timestamp: number;
|
|
8
|
+
/** Stable ID for the enabled session — survives until `disableConversationArc()` or `reset()`. */
|
|
9
|
+
sessionId: string;
|
|
10
|
+
/** Optional opaque correlation key that threads a single arc together. */
|
|
11
|
+
arcId?: string;
|
|
12
|
+
/** Free-form bag for context the talk-track doesn't need a typed slot for. */
|
|
13
|
+
meta?: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
export interface SuggestionShownEvent extends ConversationArcEventBase {
|
|
16
|
+
type: "suggestion-shown";
|
|
17
|
+
/**
|
|
18
|
+
* Intent label fed into `suggestCharts` (e.g. "trend", "distribution").
|
|
19
|
+
* Accepts a single intent or an array — mirrors `SuggestChartsOptions.intent`.
|
|
20
|
+
*/
|
|
21
|
+
intent?: string | ReadonlyArray<string>;
|
|
22
|
+
/** Ranked component names in the order the suggester returned them. */
|
|
23
|
+
components: string[];
|
|
24
|
+
/** Top suggestion's composite score, if known. */
|
|
25
|
+
topScore?: number;
|
|
26
|
+
/** Audience target active when the suggestion ran. */
|
|
27
|
+
audience?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface SuggestionChosenEvent extends ConversationArcEventBase {
|
|
30
|
+
type: "suggestion-chosen";
|
|
31
|
+
component: string;
|
|
32
|
+
/** 1-based rank in the matching `suggestion-shown` event, if known. */
|
|
33
|
+
rank?: number;
|
|
34
|
+
/** Who picked the suggestion: a human, an agent loop, or a default-fall-through. */
|
|
35
|
+
source?: "user" | "agent" | "auto";
|
|
36
|
+
}
|
|
37
|
+
export interface AudienceSetEvent extends ConversationArcEventBase {
|
|
38
|
+
type: "audience-set";
|
|
39
|
+
audience: string;
|
|
40
|
+
previous?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface ChartRenderedEvent extends ConversationArcEventBase {
|
|
43
|
+
type: "chart-rendered";
|
|
44
|
+
component: string;
|
|
45
|
+
chartId?: string;
|
|
46
|
+
}
|
|
47
|
+
export interface ChartEditedEvent extends ConversationArcEventBase {
|
|
48
|
+
type: "chart-edited";
|
|
49
|
+
component: string;
|
|
50
|
+
chartId?: string;
|
|
51
|
+
/** Names of props that changed in this edit. */
|
|
52
|
+
changedProps?: string[];
|
|
53
|
+
}
|
|
54
|
+
export interface ChartReplacedEvent extends ConversationArcEventBase {
|
|
55
|
+
type: "chart-replaced";
|
|
56
|
+
from: string;
|
|
57
|
+
to: string;
|
|
58
|
+
/** Why the swap happened — `"repair"`, `"variant"`, `"user-rejected"`, etc. */
|
|
59
|
+
reason?: string;
|
|
60
|
+
}
|
|
61
|
+
export interface ChartExportedEvent extends ConversationArcEventBase {
|
|
62
|
+
type: "chart-exported";
|
|
63
|
+
component: string;
|
|
64
|
+
/** What was exported: `"jsx"`, `"svg"`, `"png"`, `"json"`, `"url"`, etc. */
|
|
65
|
+
format: string;
|
|
66
|
+
}
|
|
67
|
+
export interface ChartAbandonedEvent extends ConversationArcEventBase {
|
|
68
|
+
type: "chart-abandoned";
|
|
69
|
+
component?: string;
|
|
70
|
+
reason?: string;
|
|
71
|
+
}
|
|
72
|
+
export interface InterrogationAskedEvent extends ConversationArcEventBase {
|
|
73
|
+
type: "interrogation-asked";
|
|
74
|
+
/** Chart the question was directed at, if known. */
|
|
75
|
+
component?: string;
|
|
76
|
+
/**
|
|
77
|
+
* Question text. The `useChartInterrogation` instrumentation
|
|
78
|
+
* truncates to ~500 chars before recording so the ring buffer
|
|
79
|
+
* stays bounded; callers stamping their own events should do the
|
|
80
|
+
* same.
|
|
81
|
+
*/
|
|
82
|
+
query: string;
|
|
83
|
+
/** Optional payload size hint (e.g. summary token count) for diagnostics. */
|
|
84
|
+
contextSize?: number;
|
|
85
|
+
}
|
|
86
|
+
export interface InterrogationAnsweredEvent extends ConversationArcEventBase {
|
|
87
|
+
type: "interrogation-answered";
|
|
88
|
+
/** Chart the answer was directed at, if known. */
|
|
89
|
+
component?: string;
|
|
90
|
+
/**
|
|
91
|
+
* Answer text. The `useChartInterrogation` instrumentation
|
|
92
|
+
* truncates to ~2000 chars before recording so multi-kilobyte LLM
|
|
93
|
+
* responses don't bloat the ring buffer. Callers stamping their
|
|
94
|
+
* own events should follow the same convention.
|
|
95
|
+
*/
|
|
96
|
+
answer?: string;
|
|
97
|
+
/** Number of annotations the response attached, if known. */
|
|
98
|
+
annotationCount?: number;
|
|
99
|
+
/**
|
|
100
|
+
* Round-trip latency in ms from ask to answer, clamped to ≥ 0.
|
|
101
|
+
* The instrumentation measures via `performance.now()` when
|
|
102
|
+
* available; the `Date.now()` fallback can produce negative
|
|
103
|
+
* deltas under clock changes, hence the clamp.
|
|
104
|
+
*/
|
|
105
|
+
latencyMs?: number;
|
|
106
|
+
/** Set when the response was an error rather than a successful answer. */
|
|
107
|
+
error?: boolean;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* A reader focused a node in an `AccessibleNavTree` (keyboard or click). The
|
|
111
|
+
* first *reception*-side behavioral signal in the arc — which structural nodes
|
|
112
|
+
* a non-visual (or AI) reader actually visits, the dependent measure visualization-
|
|
113
|
+
* literacy studies usually lack. Emitted only on genuine tree interaction, not
|
|
114
|
+
* when the active node is driven externally (canvas → tree sync).
|
|
115
|
+
*/
|
|
116
|
+
export interface NavNodeFocusedEvent extends ConversationArcEventBase {
|
|
117
|
+
type: "nav-node-focused";
|
|
118
|
+
/** `chartId` of the chart the tree describes, when correlated. */
|
|
119
|
+
chartId?: string;
|
|
120
|
+
/** Tree node id that gained focus. */
|
|
121
|
+
nodeId: string;
|
|
122
|
+
/** Node role: `"chart" | "axis" | "series" | "datum"`. */
|
|
123
|
+
role: string;
|
|
124
|
+
/** 1-based depth (aria-level). */
|
|
125
|
+
level: number;
|
|
126
|
+
/** The node's announced label (the emitter truncates to ~200 chars). */
|
|
127
|
+
label?: string;
|
|
128
|
+
}
|
|
129
|
+
/** A reader expanded or collapsed a branch in an `AccessibleNavTree`. */
|
|
130
|
+
export interface NavBranchExpandedEvent extends ConversationArcEventBase {
|
|
131
|
+
type: "nav-branch-expanded";
|
|
132
|
+
/** `chartId` of the chart the tree describes, when correlated. */
|
|
133
|
+
chartId?: string;
|
|
134
|
+
/** Tree node id that was toggled. */
|
|
135
|
+
nodeId: string;
|
|
136
|
+
/** Node role of the toggled branch. */
|
|
137
|
+
role: string;
|
|
138
|
+
/** 1-based depth (aria-level). */
|
|
139
|
+
level: number;
|
|
140
|
+
/** `true` on expand, `false` on collapse. */
|
|
141
|
+
expanded: boolean;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* An annotation's editorial status transitioned (M7). The accept / dispute /
|
|
145
|
+
* retract / propose flow is what turns an annotation into the durable,
|
|
146
|
+
* observable node of the conversation arc (IDID §13.4): the note is the unit
|
|
147
|
+
* the arc is *about*, not chart chrome.
|
|
148
|
+
*
|
|
149
|
+
* `fromStatus`/`toStatus` are deliberately not named `from`/`to` — `summarizeArc`
|
|
150
|
+
* reads `from`/`to` as chart-component names (the `chart-replaced` shape), so a
|
|
151
|
+
* status value there would pollute `componentsSeen`.
|
|
152
|
+
*/
|
|
153
|
+
export interface AnnotationStatusChangedEvent extends ConversationArcEventBase {
|
|
154
|
+
type: "annotation-status-changed";
|
|
155
|
+
/** `provenance.stableId` of the annotation whose status changed, when known. */
|
|
156
|
+
annotationId?: string;
|
|
157
|
+
/** Previous editorial status, if known. */
|
|
158
|
+
fromStatus?: AnnotationStatus;
|
|
159
|
+
/** New editorial status. */
|
|
160
|
+
toStatus: AnnotationStatus;
|
|
161
|
+
/** `chartId` of the chart carrying the annotation, when correlated. */
|
|
162
|
+
chartId?: string;
|
|
163
|
+
}
|
|
164
|
+
export type ConversationArcEvent = SuggestionShownEvent | SuggestionChosenEvent | AudienceSetEvent | ChartRenderedEvent | ChartEditedEvent | ChartReplacedEvent | ChartExportedEvent | ChartAbandonedEvent | InterrogationAskedEvent | InterrogationAnsweredEvent | NavNodeFocusedEvent | NavBranchExpandedEvent | AnnotationStatusChangedEvent;
|
|
165
|
+
/**
|
|
166
|
+
* Input shape accepted by `record()`: the event variant without the
|
|
167
|
+
* stamped fields (`timestamp` and `sessionId`). Callers may still
|
|
168
|
+
* provide them to backfill historical events.
|
|
169
|
+
*
|
|
170
|
+
* Implemented as a distributive conditional so each member of the
|
|
171
|
+
* discriminated union keeps its variant-specific payload (e.g.
|
|
172
|
+
* `SuggestionShownEvent.components`). A non-distributive
|
|
173
|
+
* `Omit<ConversationArcEvent, ...>` collapses to the union's common
|
|
174
|
+
* fields and rejects every variant-specific key.
|
|
175
|
+
*/
|
|
176
|
+
export type ConversationArcEventInput = ConversationArcEvent extends infer E ? E extends ConversationArcEvent ? Omit<E, "timestamp" | "sessionId"> & Partial<Pick<E, "timestamp" | "sessionId">> : never : never;
|
|
177
|
+
export type ConversationArcListener = (event: ConversationArcEvent) => void;
|
|
178
|
+
export interface ConversationArcSink {
|
|
179
|
+
/**
|
|
180
|
+
* Persist a newly-recorded event. Called only after the store is enabled and
|
|
181
|
+
* the event is accepted into the ring buffer.
|
|
182
|
+
*/
|
|
183
|
+
record?(event: ConversationArcEvent): void | Promise<void>;
|
|
184
|
+
/**
|
|
185
|
+
* Optional hook for consumers that treat `flush()` as an export boundary.
|
|
186
|
+
* The in-memory buffer is still cleared by the store after this call.
|
|
187
|
+
*/
|
|
188
|
+
flush?(events: ReadonlyArray<ConversationArcEvent>): void | Promise<void>;
|
|
189
|
+
/** Clear durable state owned by this sink. */
|
|
190
|
+
clear?(): void | Promise<void>;
|
|
191
|
+
/** Load previously persisted events for replay / hydration. */
|
|
192
|
+
load?(): ReadonlyArray<ConversationArcEvent> | Promise<ReadonlyArray<ConversationArcEvent>>;
|
|
193
|
+
}
|
|
194
|
+
export interface ConversationArcStorageLike {
|
|
195
|
+
getItem(key: string): string | null;
|
|
196
|
+
setItem(key: string, value: string): void;
|
|
197
|
+
removeItem(key: string): void;
|
|
198
|
+
}
|
|
199
|
+
export interface LocalStorageConversationArcSinkOptions {
|
|
200
|
+
/** Storage key. Defaults to `semiotic:conversation-arc`. */
|
|
201
|
+
key?: string;
|
|
202
|
+
/** Test hook or alternate Storage implementation. Defaults to `window.localStorage`. */
|
|
203
|
+
storage?: ConversationArcStorageLike;
|
|
204
|
+
/** Maximum events retained in storage. Defaults to 1000. */
|
|
205
|
+
maxEvents?: number;
|
|
206
|
+
}
|
|
207
|
+
export interface IndexedDBConversationArcSinkOptions {
|
|
208
|
+
/** Database name. Defaults to `semiotic-conversation-arc`. */
|
|
209
|
+
dbName?: string;
|
|
210
|
+
/** Object store name. Defaults to `events`. */
|
|
211
|
+
storeName?: string;
|
|
212
|
+
/** Test hook or alternate IndexedDB factory. Defaults to `globalThis.indexedDB`. */
|
|
213
|
+
indexedDB?: IDBFactory;
|
|
214
|
+
/** Maximum events retained in the object store. Defaults to 1000. */
|
|
215
|
+
maxEvents?: number;
|
|
216
|
+
}
|
|
217
|
+
export type ConversationArcWebhookFetch = (input: string, init?: RequestInit) => Promise<unknown>;
|
|
218
|
+
export interface WebhookConversationArcSinkOptions {
|
|
219
|
+
url: string;
|
|
220
|
+
method?: "POST" | "PUT";
|
|
221
|
+
headers?: Record<string, string>;
|
|
222
|
+
fetch?: ConversationArcWebhookFetch;
|
|
223
|
+
mapEvent?: (event: ConversationArcEvent) => unknown;
|
|
224
|
+
}
|
|
225
|
+
export interface LoadConversationArcOptions {
|
|
226
|
+
/** Capacity of the hydrated ring buffer. Defaults to max(existing, events.length, 1000). */
|
|
227
|
+
capacity?: number;
|
|
228
|
+
/** Active session id for future recordings. Replayed event session ids are preserved. */
|
|
229
|
+
sessionId?: string;
|
|
230
|
+
/**
|
|
231
|
+
* Whether the store should accept new events after hydration. Defaults to false
|
|
232
|
+
* so replaying an artifact cannot accidentally start telemetry.
|
|
233
|
+
*/
|
|
234
|
+
enabled?: boolean;
|
|
235
|
+
/** Append to the current buffer instead of replacing it. Defaults to false. */
|
|
236
|
+
append?: boolean;
|
|
237
|
+
}
|
|
238
|
+
export interface ConversationArcStore {
|
|
239
|
+
readonly enabled: boolean;
|
|
240
|
+
readonly sessionId: string | null;
|
|
241
|
+
readonly capacity: number;
|
|
242
|
+
/**
|
|
243
|
+
* Records an event. Returns the stamped event on success, or `null`
|
|
244
|
+
* if the store is disabled. Stamps `timestamp` and `sessionId` if
|
|
245
|
+
* the caller didn't.
|
|
246
|
+
*/
|
|
247
|
+
record(input: ConversationArcEventInput): ConversationArcEvent | null;
|
|
248
|
+
/** Returns the current buffer (newest last) and clears it. */
|
|
249
|
+
flush(): ConversationArcEvent[];
|
|
250
|
+
/**
|
|
251
|
+
* Returns a frozen, referentially-stable snapshot of the current
|
|
252
|
+
* buffer. Stable across consecutive calls until the next mutation,
|
|
253
|
+
* so it can drive `useSyncExternalStore`. Read-only — callers that
|
|
254
|
+
* need a mutable copy should `.slice()` the result.
|
|
255
|
+
*/
|
|
256
|
+
getEvents(): ReadonlyArray<ConversationArcEvent>;
|
|
257
|
+
/**
|
|
258
|
+
* Subscribe to new events. Returns an unsubscribe function.
|
|
259
|
+
*
|
|
260
|
+
* Subscriptions persist across enable/disable transitions — a
|
|
261
|
+
* subscriber registered before `enableConversationArc()` still
|
|
262
|
+
* receives events once recording starts. Cleared by `reset()`.
|
|
263
|
+
*/
|
|
264
|
+
subscribe(listener: ConversationArcListener): () => void;
|
|
265
|
+
/** Empties the buffer without disabling the store. */
|
|
266
|
+
clear(): void;
|
|
267
|
+
/** Disables the store and drops the buffer + subscribers. */
|
|
268
|
+
reset(): void;
|
|
269
|
+
}
|
|
270
|
+
export interface EnableConversationArcOptions {
|
|
271
|
+
/** Maximum events retained in the ring buffer. Defaults to 1000. */
|
|
272
|
+
capacity?: number;
|
|
273
|
+
/** Override the generated session ID. Useful for cross-tab correlation. */
|
|
274
|
+
sessionId?: string;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Subscribe to *any* state mutation in the conversation-arc store —
|
|
278
|
+
* including `clear`, `flush`, `reset`, and `enable`, in addition to
|
|
279
|
+
* recorded events. Intended for React hooks that need to re-render
|
|
280
|
+
* on snapshot changes; event sinks should use `subscribe()` instead.
|
|
281
|
+
*
|
|
282
|
+
* Returns an unsubscribe callback.
|
|
283
|
+
*/
|
|
284
|
+
export declare function subscribeToConversationArcChange(listener: () => void): () => void;
|
|
285
|
+
/**
|
|
286
|
+
* Register an opt-in persistence sink. Sinks receive only accepted events:
|
|
287
|
+
* disabled telemetry remains a no-op and replay hydration does not re-emit
|
|
288
|
+
* historical events into durable stores.
|
|
289
|
+
*/
|
|
290
|
+
export declare function registerConversationArcSink(sink: ConversationArcSink): () => void;
|
|
291
|
+
/**
|
|
292
|
+
* Browser-local durable sink backed by `localStorage`. It appends each accepted
|
|
293
|
+
* event to a JSON array and exposes `load()` so a session can be replayed later.
|
|
294
|
+
* When `localStorage` is unavailable (SSR, private browser failures), operations
|
|
295
|
+
* degrade to no-ops and `load()` returns `[]`.
|
|
296
|
+
*/
|
|
297
|
+
export declare function createLocalStorageConversationArcSink(options?: LocalStorageConversationArcSinkOptions): ConversationArcSink & {
|
|
298
|
+
load(): ConversationArcEvent[];
|
|
299
|
+
};
|
|
300
|
+
/**
|
|
301
|
+
* Browser durable sink backed by IndexedDB. Writes are asynchronous and
|
|
302
|
+
* fire-and-forget from the recorder; callers that need replay can await
|
|
303
|
+
* `sink.load()`.
|
|
304
|
+
*/
|
|
305
|
+
export declare function createIndexedDBConversationArcSink(options?: IndexedDBConversationArcSinkOptions): ConversationArcSink & {
|
|
306
|
+
load(): Promise<ConversationArcEvent[]>;
|
|
307
|
+
};
|
|
308
|
+
/**
|
|
309
|
+
* Minimal webhook sink for app-owned analytics ingestion. The sink posts one
|
|
310
|
+
* JSON payload per accepted event; retry, batching, authentication refresh, and
|
|
311
|
+
* sampling policy remain application concerns.
|
|
312
|
+
*/
|
|
313
|
+
export declare function createWebhookConversationArcSink(options: WebhookConversationArcSinkOptions): ConversationArcSink;
|
|
314
|
+
/**
|
|
315
|
+
* Hydrate the in-memory store from a saved arc without replaying side effects.
|
|
316
|
+
* The loaded events become visible through `getEvents()` / `useConversationArc`,
|
|
317
|
+
* but listeners and sinks are not called. Pass `{ enabled: true }` when you want
|
|
318
|
+
* to resume recording after loading; the default is replay-only.
|
|
319
|
+
*/
|
|
320
|
+
export declare function loadConversationArc(events: ReadonlyArray<ConversationArcEvent>, options?: LoadConversationArcOptions): ReadonlyArray<ConversationArcEvent>;
|
|
321
|
+
/** Alias with the word replay for fixture-driven callers. */
|
|
322
|
+
export declare function replayConversationArc(events: ReadonlyArray<ConversationArcEvent>, options?: LoadConversationArcOptions): ReadonlyArray<ConversationArcEvent>;
|
|
323
|
+
/**
|
|
324
|
+
* Record an audience-set event. Convenience wrapper around `record`
|
|
325
|
+
* for the common pattern: "the user picked a new audience profile
|
|
326
|
+
* and I want to put that in the arc."
|
|
327
|
+
*
|
|
328
|
+
* `previous` is optional but recommended — it lets downstream
|
|
329
|
+
* analytics see the transition rather than just the new state. Pass
|
|
330
|
+
* `null` when there was no prior audience.
|
|
331
|
+
*
|
|
332
|
+
* Returns the stamped event, or `null` if recording is disabled.
|
|
333
|
+
*
|
|
334
|
+
* ```ts
|
|
335
|
+
* import { recordAudienceChange } from "semiotic/ai"
|
|
336
|
+
*
|
|
337
|
+
* function AudiencePicker({ value, onChange }) {
|
|
338
|
+
* return (
|
|
339
|
+
* <select value={value} onChange={(e) => {
|
|
340
|
+
* const next = e.target.value
|
|
341
|
+
* recordAudienceChange(next, value)
|
|
342
|
+
* onChange(next)
|
|
343
|
+
* }} />
|
|
344
|
+
* )
|
|
345
|
+
* }
|
|
346
|
+
* ```
|
|
347
|
+
*/
|
|
348
|
+
export declare function recordAudienceChange(audience: string, previous?: string | null, extra?: {
|
|
349
|
+
arcId?: string;
|
|
350
|
+
meta?: Record<string, unknown>;
|
|
351
|
+
}): ConversationArcEvent | null;
|
|
352
|
+
/**
|
|
353
|
+
* Sugar for an `annotation-status-changed` event (M7). Call it from the
|
|
354
|
+
* accept / dispute / retract / propose UI so the arc records how an
|
|
355
|
+
* annotation's editorial standing moved — the durable, observable node of the
|
|
356
|
+
* conversation. No-op (returns null) until `enableConversationArc()`.
|
|
357
|
+
*/
|
|
358
|
+
export declare function recordAnnotationStatusChange(toStatus: AnnotationStatus, opts?: {
|
|
359
|
+
annotationId?: string;
|
|
360
|
+
fromStatus?: AnnotationStatus;
|
|
361
|
+
chartId?: string;
|
|
362
|
+
arcId?: string;
|
|
363
|
+
meta?: Record<string, unknown>;
|
|
364
|
+
}): ConversationArcEvent | null;
|
|
365
|
+
/**
|
|
366
|
+
* Opt in to conversation-arc telemetry. Safe to call multiple times —
|
|
367
|
+
* subsequent calls reuse the existing session unless `sessionId` is
|
|
368
|
+
* explicitly provided.
|
|
369
|
+
*/
|
|
370
|
+
export declare function enableConversationArc(options?: EnableConversationArcOptions): ConversationArcStore;
|
|
371
|
+
/** Turn the store off without dropping the buffered events. */
|
|
372
|
+
export declare function disableConversationArc(): void;
|
|
373
|
+
/**
|
|
374
|
+
* Always returns a store façade. Methods are safe to call when
|
|
375
|
+
* disabled — `record()` returns null, `getEvents()`/`flush()` return
|
|
376
|
+
* empty arrays, `subscribe()` returns a no-op unsubscriber.
|
|
377
|
+
*/
|
|
378
|
+
export declare function getConversationArcStore(): ConversationArcStore;
|
|
379
|
+
export {};
|