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.
Files changed (94) hide show
  1. package/CLAUDE.md +190 -227
  2. package/README.md +44 -14
  3. package/ai/cli.js +41 -0
  4. package/ai/componentMetadata.cjs +11 -2
  5. package/ai/dist/mcp-server.js +209 -6
  6. package/ai/examples.md +98 -0
  7. package/ai/schema.json +581 -1
  8. package/ai/system-prompt.md +5 -2
  9. package/dist/components/AccessibleNavTree.d.ts +25 -0
  10. package/dist/components/Annotation.d.ts +40 -14
  11. package/dist/components/ChartContainer.d.ts +32 -2
  12. package/dist/components/ai/annotationProvenance.d.ts +349 -0
  13. package/dist/components/ai/audienceProfile.d.ts +60 -3
  14. package/dist/components/ai/chartCapabilityTypes.d.ts +60 -2
  15. package/dist/components/ai/chartRoles.d.ts +27 -0
  16. package/dist/components/ai/conversationArc.d.ts +379 -0
  17. package/dist/components/ai/dataScaleProfile.d.ts +320 -0
  18. package/dist/components/ai/describeChart.d.ts +114 -0
  19. package/dist/components/ai/navigationTree.d.ts +45 -0
  20. package/dist/components/ai/readerGrounding.d.ts +70 -0
  21. package/dist/components/ai/suggestCharts.d.ts +34 -1
  22. package/dist/components/ai/useConversationArc.d.ts +89 -0
  23. package/dist/components/ai/useNavigationSync.d.ts +61 -0
  24. package/dist/components/ai/variantDiscovery.d.ts +168 -0
  25. package/dist/components/charts/realtime/RealtimeHeatmap.d.ts +3 -0
  26. package/dist/components/charts/realtime/RealtimeHistogram.d.ts +3 -0
  27. package/dist/components/charts/realtime/RealtimeLineChart.d.ts +3 -0
  28. package/dist/components/charts/realtime/RealtimeSwarmChart.d.ts +3 -0
  29. package/dist/components/charts/realtime/RealtimeWaterfallChart.d.ts +3 -0
  30. package/dist/components/charts/shared/annotationHierarchy.d.ts +42 -0
  31. package/dist/components/charts/shared/annotationResolvers.d.ts +3 -2
  32. package/dist/components/charts/shared/annotationRules.d.ts +16 -0
  33. package/dist/components/charts/shared/annotationTypes.d.ts +14 -0
  34. package/dist/components/charts/shared/auditAccessibility.d.ts +90 -0
  35. package/dist/components/charts/shared/chartSpecs.d.ts +2 -3
  36. package/dist/components/charts/shared/diagnoseConfig.d.ts +4 -6
  37. package/dist/components/charts/shared/selectionUtils.d.ts +5 -2
  38. package/dist/components/charts/shared/streamPropsHelpers.d.ts +2 -0
  39. package/dist/components/charts/shared/types.d.ts +5 -1
  40. package/dist/components/charts/value/BigNumber.capability.d.ts +13 -0
  41. package/dist/components/charts/value/BigNumber.d.ts +14 -0
  42. package/dist/components/charts/value/formatting.d.ts +40 -0
  43. package/dist/components/charts/value/thresholdSparkline.d.ts +40 -0
  44. package/dist/components/charts/value/types.d.ts +292 -0
  45. package/dist/components/realtime/lifecycleBands.d.ts +44 -0
  46. package/dist/components/realtime/types.d.ts +23 -8
  47. package/dist/components/recipes/annotationDensity.d.ts +69 -0
  48. package/dist/components/recipes/annotationLayout.d.ts +93 -0
  49. package/dist/components/semiotic-ai.d.ts +38 -15
  50. package/dist/components/semiotic-realtime.d.ts +2 -0
  51. package/dist/components/semiotic-recipes.d.ts +4 -0
  52. package/dist/components/semiotic-utils.d.ts +8 -0
  53. package/dist/components/semiotic-value.d.ts +55 -0
  54. package/dist/components/semiotic.d.ts +7 -0
  55. package/dist/components/server/staticAnnotations.d.ts +2 -0
  56. package/dist/components/stream/AccessibleDataTable.d.ts +10 -1
  57. package/dist/components/stream/NetworkSVGOverlay.d.ts +11 -5
  58. package/dist/components/stream/OrdinalSVGOverlay.d.ts +2 -0
  59. package/dist/components/stream/SVGOverlay.d.ts +2 -0
  60. package/dist/components/stream/geoTypes.d.ts +3 -0
  61. package/dist/components/stream/networkTypes.d.ts +2 -0
  62. package/dist/components/stream/ordinalTypes.d.ts +2 -0
  63. package/dist/components/stream/types.d.ts +2 -0
  64. package/dist/geo.min.js +1 -1
  65. package/dist/geo.module.min.js +1 -1
  66. package/dist/network.min.js +1 -1
  67. package/dist/network.module.min.js +1 -1
  68. package/dist/ordinal.min.js +1 -1
  69. package/dist/ordinal.module.min.js +1 -1
  70. package/dist/realtime.min.js +1 -1
  71. package/dist/realtime.module.min.js +1 -1
  72. package/dist/semiotic-ai.d.ts +38 -15
  73. package/dist/semiotic-ai.min.js +1 -1
  74. package/dist/semiotic-ai.module.min.js +1 -1
  75. package/dist/semiotic-realtime.d.ts +2 -0
  76. package/dist/semiotic-recipes.d.ts +4 -0
  77. package/dist/semiotic-recipes.min.js +1 -1
  78. package/dist/semiotic-recipes.module.min.js +1 -1
  79. package/dist/semiotic-themes.min.js +1 -1
  80. package/dist/semiotic-themes.module.min.js +1 -1
  81. package/dist/semiotic-utils.d.ts +8 -0
  82. package/dist/semiotic-utils.min.js +1 -1
  83. package/dist/semiotic-utils.module.min.js +1 -1
  84. package/dist/semiotic-value.d.ts +55 -0
  85. package/dist/semiotic-value.min.js +2 -0
  86. package/dist/semiotic-value.module.min.js +2 -0
  87. package/dist/semiotic.d.ts +7 -0
  88. package/dist/semiotic.min.js +1 -1
  89. package/dist/semiotic.module.min.js +1 -1
  90. package/dist/server.min.js +1 -1
  91. package/dist/server.module.min.js +1 -1
  92. package/dist/xy.min.js +1 -1
  93. package/dist/xy.module.min.js +1 -1
  94. package/package.json +18 -4
@@ -0,0 +1,89 @@
1
+ import { type ConversationArcEvent, type ConversationArcEventInput, type ConversationArcEventType, type EnableConversationArcOptions } from "./conversationArc";
2
+ /**
3
+ * Talk-friendly summary of an arc. Computed from the current buffer
4
+ * each time it's read. `summarizeArc` is exported separately for
5
+ * server-side or batch use.
6
+ */
7
+ export interface ConversationArcSummary {
8
+ /** Total events buffered. */
9
+ total: number;
10
+ /** Per-type counts. Missing types implicitly zero. */
11
+ byType: Partial<Record<ConversationArcEventType, number>>;
12
+ /** Component names referenced across the buffer (deduplicated). */
13
+ componentsSeen: string[];
14
+ /** Audience labels referenced across the buffer (most recent last). */
15
+ audiencesSeen: string[];
16
+ /** Latest event's `arcId`, if any. */
17
+ latestArcId?: string;
18
+ /** Timestamp of the first event (epoch ms), or `null` if empty. */
19
+ startedAt: number | null;
20
+ /** Timestamp of the most recent event (epoch ms), or `null` if empty. */
21
+ lastAt: number | null;
22
+ /** Wall-clock duration covered by the buffer in ms, or `0` if empty. */
23
+ durationMs: number;
24
+ }
25
+ /**
26
+ * Reduce an event array to the talk-friendly `ConversationArcSummary`.
27
+ *
28
+ * Pure function — no `Date.now()` calls, no I/O. Safe to use on
29
+ * the server, in batch jobs, or against a replayed arc fixture.
30
+ *
31
+ * Walks the events once, so cost scales with buffer size; the default
32
+ * 1000-event capacity makes this trivially fast in practice.
33
+ */
34
+ export declare function summarizeArc(events: ReadonlyArray<ConversationArcEvent>): ConversationArcSummary;
35
+ /**
36
+ * Options for {@link useConversationArc}. Mirrors the underlying
37
+ * `enableConversationArc` options, plus a `disableOnUnmount` escape
38
+ * hatch for consumers that own the entire arc lifecycle.
39
+ */
40
+ export interface UseConversationArcOptions extends EnableConversationArcOptions {
41
+ /**
42
+ * Auto-enable the arc store on mount. Defaults to `true` — the
43
+ * common case is "I'm rendering the arc inspector / live dashboard,
44
+ * I want recording on while I'm mounted." Set to `false` if you
45
+ * want the hook to read but not flip the enable flag.
46
+ */
47
+ enableOnMount?: boolean;
48
+ /**
49
+ * Auto-disable on unmount. Defaults to `false` — disabling on
50
+ * unmount would cut off any other consumer that depends on
51
+ * recording staying live (the audience picker, an inspector panel
52
+ * in a sibling tree). Set to `true` if you own the whole session.
53
+ */
54
+ disableOnUnmount?: boolean;
55
+ }
56
+ /**
57
+ * Result returned from {@link useConversationArc}.
58
+ */
59
+ export interface UseConversationArcResult {
60
+ /** Live event buffer, refreshed on every recorded event. */
61
+ /** Frozen snapshot of the live event buffer. Read-only; `.slice()` for a mutable copy. */
62
+ history: ReadonlyArray<ConversationArcEvent>;
63
+ /** Reduced summary of the current buffer. Recomputed on each event. */
64
+ summary: ConversationArcSummary;
65
+ /** `true` while the store is actively recording. */
66
+ enabled: boolean;
67
+ /** Current session ID, or `null` before any `enable*` call. */
68
+ sessionId: string | null;
69
+ /** Record an event — stable across renders. Returns the stamped event, or `null` if disabled. */
70
+ record: (input: ConversationArcEventInput) => ConversationArcEvent | null;
71
+ /** Clear the buffered events without disabling the store. */
72
+ clear: () => void;
73
+ }
74
+ /**
75
+ * React hook that participates in the conversation-arc session.
76
+ *
77
+ * Subscribes via `useSyncExternalStore` so re-renders are coordinated
78
+ * with React's concurrent rendering (no tearing between the buffer
79
+ * snapshot and the rendered tree). Auto-enables the store on mount
80
+ * by default; the underlying store is module-scoped, so all hook
81
+ * instances share the same buffer.
82
+ *
83
+ * ```tsx
84
+ * const { history, summary, record } = useConversationArc()
85
+ * // Later, in a click handler:
86
+ * record({ type: "chart-exported", component: "LineChart", format: "jsx" })
87
+ * ```
88
+ */
89
+ export declare function useConversationArc(options?: UseConversationArcOptions): UseConversationArcResult;
@@ -0,0 +1,61 @@
1
+ import type { NavTreeNode } from "./navigationTree";
2
+ import type { Datum } from "../charts/shared/datumTypes";
3
+ /**
4
+ * useNavigationSync — keep an `AccessibleNavTree` and a chart in sync, both ways:
5
+ *
6
+ * tree → canvas : landing on a datum node highlights the matching mark (via
7
+ * the selection store — pass `selection` to the chart).
8
+ * canvas → tree : hovering/clicking a mark moves the tree's active node (via
9
+ * the observation store — give the chart a `chartId`).
10
+ *
11
+ * Both directions ride the existing module-global stores, so **no provider is
12
+ * required** — the chart just needs `chartId` and `selection={sync.selection}`,
13
+ * and the tree needs `activeId`/`onActiveChange`. Headless: BYO layout.
14
+ *
15
+ * @example
16
+ * const sync = useNavigationSync({ tree, chartId: "sales", matchFields: ["month"] })
17
+ * <LineChart chartId="sales" selection={sync.selection} {...props} />
18
+ * <AccessibleNavTree tree={tree} activeId={sync.activeId} onActiveChange={sync.onActiveChange} />
19
+ */
20
+ export interface UseNavigationSyncOptions {
21
+ /** The navigation tree (from `buildNavigationTree`). */
22
+ tree: NavTreeNode;
23
+ /** `chartId` set on the chart, so its hover/click can be matched back to a leaf. */
24
+ chartId?: string;
25
+ /**
26
+ * Fields that identify a datum for highlighting + matching. Defaults to the
27
+ * primitive-valued keys of the first leaf's datum.
28
+ */
29
+ matchFields?: string[];
30
+ /** Selection name shared with the chart's `selection={{ name }}`. Default "__semiotic-nav-sync". */
31
+ selectionName?: string;
32
+ /** Which canvas observations move the tree. Default ["hover", "click"]. */
33
+ observe?: Array<"hover" | "click">;
34
+ /**
35
+ * The chart's annotations. An annotation anchored to a datum carries that
36
+ * datum's identifying fields (the same `matchFields` used for hover sync), so
37
+ * each one resolves to a nav-tree leaf. Lets a non-visual reader *reach* an
38
+ * anchored point — e.g. an AI's "anchored conversation" note — via
39
+ * `focusAnnotation`, and lets the tree mark annotated nodes via `annotatedIds`.
40
+ */
41
+ annotations?: ReadonlyArray<Datum>;
42
+ }
43
+ export interface UseNavigationSyncResult {
44
+ /** Controlled active node id — pass to `AccessibleNavTree`. */
45
+ activeId: string;
46
+ /** Change handler — pass to `AccessibleNavTree` (drives the canvas highlight). */
47
+ onActiveChange: (node: NavTreeNode) => void;
48
+ /** Pass to the chart as `selection={sync.selection}`. */
49
+ selection: {
50
+ name: string;
51
+ };
52
+ /** Nav-tree leaf ids that an annotation anchors to — mark these as "has a note". */
53
+ annotatedIds: Set<string>;
54
+ /**
55
+ * Move the tree (and canvas highlight) to an annotation's anchored node.
56
+ * Accepts an annotation object or its index in `annotations`. Returns `true`
57
+ * if the anchor resolved to a leaf. The reader lands on the anchored datum.
58
+ */
59
+ focusAnnotation: (annotation: Datum | number) => boolean;
60
+ }
61
+ export declare function useNavigationSync(options: UseNavigationSyncOptions): UseNavigationSyncResult;
@@ -0,0 +1,168 @@
1
+ import type { ChartCapability, ChartDataProfile, ChartRubric, ChartVariant } from "./chartCapabilityTypes";
2
+ import type { IntentId } from "./intents";
3
+ import { type AudienceProfile } from "./audienceProfile";
4
+ /**
5
+ * Where a proposal came from. Used by scoring + ranking to weight
6
+ * trusted sources (manual hand-curated variants) above heuristic
7
+ * suggestions and model-generated proposals that need verification.
8
+ */
9
+ export type VariantProposalSource = "manual" | "heuristic" | "model";
10
+ /**
11
+ * A "what if we tried this configuration?" candidate. Closely
12
+ * related to `ChartVariant`, but with explicit provenance and an
13
+ * optional `buildProps` so model-generated proposals can carry
14
+ * their own prop-construction logic without registering a full
15
+ * capability.
16
+ *
17
+ * The `id` is the durable identifier — the scoring side keys
18
+ * results by it so consumers can correlate proposals across
19
+ * propose/evaluate rounds.
20
+ */
21
+ export interface VariantProposal {
22
+ /** Stable identifier — `<component>:<key>` is the conventional shape. */
23
+ id: string;
24
+ /** Component this proposal would render. */
25
+ baseComponent: string;
26
+ /** Human-facing label, usually a registered variant label or generated heuristic label. */
27
+ label?: string;
28
+ /**
29
+ * Per-intent score deltas applied on top of the base capability's
30
+ * `intentScores`. Use sparingly — over-large deltas are a sign the
31
+ * proposal should be a separate chart, not a variant.
32
+ */
33
+ intentDeltas?: Partial<Record<IntentId, number>>;
34
+ /** Rubric deltas applied on top of the base capability's rubric. */
35
+ rubricDeltas?: Partial<ChartRubric>;
36
+ /**
37
+ * Build the props this proposal would pass to the component. When
38
+ * omitted, the engine falls back to the capability's own
39
+ * `buildProps(profile, variant)`. Model-generated proposals
40
+ * typically provide their own.
41
+ */
42
+ buildProps?: (profile: ChartDataProfile, audience?: AudienceProfile) => Record<string, unknown>;
43
+ /** Free-form natural-language rationale for the proposal. */
44
+ rationale?: string;
45
+ /** Where the proposal originated. */
46
+ source: VariantProposalSource;
47
+ /** Optional reference to a registered variant key when the proposal mirrors one. */
48
+ variantKey?: string;
49
+ /** Optional tags consumers can filter on (mirrors `ChartVariant.tags`). */
50
+ tags?: ReadonlyArray<string>;
51
+ }
52
+ /**
53
+ * Why we'd reject a proposal: missing required field, conflicts with
54
+ * data type, audience mismatch above threshold, etc. Surfaced through
55
+ * `VariantScore.reasons`.
56
+ */
57
+ export type VariantRejectionReason = string;
58
+ /**
59
+ * Result of evaluating a single proposal against a data profile and
60
+ * (optionally) an audience. Composes with the existing
61
+ * `suggestCharts` scoring vocabulary.
62
+ */
63
+ export interface VariantScore {
64
+ /** Echoes `VariantProposal.id`. */
65
+ proposalId: string;
66
+ /**
67
+ * How well this proposal matches the profile + audience, 0..5.
68
+ * Drawn from the same 0..5 scale `suggestCharts` uses so consumers
69
+ * can mix variant proposals into a unified ranked list.
70
+ */
71
+ fit: number;
72
+ /**
73
+ * How surprising the proposal is relative to the obvious
74
+ * recommendation, 0..1. Higher = more novel. The talk-track use
75
+ * is "a discovery model proposed a Ridgeline where the audience
76
+ * expected a BoxPlot."
77
+ */
78
+ novelty: number;
79
+ /**
80
+ * Risk of misleading the audience, 0..1. Higher = more risky.
81
+ * Drives the visual treatment (warning chip, expanded caveats).
82
+ */
83
+ risk: number;
84
+ /**
85
+ * Narrative reasons assembled from the capability `fits()` gate,
86
+ * rubric deltas, audience bias, and the discovery source. Suitable
87
+ * for tooltips and LLM context.
88
+ */
89
+ reasons: ReadonlyArray<string>;
90
+ }
91
+ export interface EvaluateVariantProposalOptions {
92
+ /** Ranking intent(s). When omitted, fit uses the mean non-zero intent score. */
93
+ intent?: IntentId | ReadonlyArray<IntentId>;
94
+ /** Component the user started from — used to estimate cross-family novelty. */
95
+ baselineComponent?: string;
96
+ }
97
+ /**
98
+ * Context handed to a discovery function. Carries everything a
99
+ * proposer needs to inspect the existing recommendation surface:
100
+ * the dataset profile, the audience, the already-considered
101
+ * variants, and the intent driving the recommendation.
102
+ */
103
+ export interface VariantDiscoveryContext {
104
+ profile: ChartDataProfile;
105
+ audience?: AudienceProfile;
106
+ intent?: IntentId | ReadonlyArray<IntentId>;
107
+ /**
108
+ * Variants already attached to the base capability — proposers
109
+ * should avoid re-emitting these unless they have meaningful
110
+ * deltas a registered variant doesn't.
111
+ */
112
+ existingVariants?: ReadonlyArray<ChartVariant>;
113
+ }
114
+ /**
115
+ * Signature for a proposal generator. Implementations may be
116
+ * heuristic, LLM-driven, or hand-coded. The engine collects
117
+ * proposals from every registered discovery function and ranks
118
+ * the union through `evaluateVariantProposal`.
119
+ */
120
+ export type ProposeVariantFn = (component: string, capability: ChartCapability, context: VariantDiscoveryContext) => ReadonlyArray<VariantProposal>;
121
+ /**
122
+ * Signature for a scoring function. The default scorer composes the
123
+ * capability's `fits()` gate, rubric deltas, and audience bias. External
124
+ * scorers may add their own novelty/risk computations.
125
+ */
126
+ export type EvaluateVariantProposalFn = (proposal: VariantProposal, profile: ChartDataProfile, audience?: AudienceProfile, options?: EvaluateVariantProposalOptions) => VariantScore;
127
+ /**
128
+ * Aggregates proposals from every registered discovery function.
129
+ *
130
+ * Each registered function is invoked with the same `(component,
131
+ * capability, context)` tuple; their results are concatenated and
132
+ * deduplicated by `VariantProposal.id` (first proposer wins). A
133
+ * proposer that throws is isolated — the error surfaces through
134
+ * `console.warn` and the remaining proposers still run.
135
+ *
136
+ * The built-in proposer first emits registered `capability.variants` as
137
+ * manual proposals, then adds conservative heuristic transforms and
138
+ * same-intent cross-family alternatives. Registered discovery functions
139
+ * run after that and are deduplicated by `VariantProposal.id`.
140
+ */
141
+ export declare const proposeVariant: ProposeVariantFn;
142
+ /**
143
+ * Evaluate a proposal against the same ingredients the capability
144
+ * recommender uses: fits gate, intent scores, rubric deltas, and audience
145
+ * profile. `novelty` and `risk` are discovery-specific side channels.
146
+ */
147
+ export declare const evaluateVariantProposal: EvaluateVariantProposalFn;
148
+ /**
149
+ * Register a discovery function. Returns an unregister callback.
150
+ *
151
+ * `proposeVariant` invokes every registered function and deduplicates
152
+ * by `VariantProposal.id`. External ML-driven proposers plug in here
153
+ * without API change.
154
+ *
155
+ * Built-in heuristic discovery lives in `proposeVariant` directly. This
156
+ * registry is the extension surface for consumers and model integrations.
157
+ */
158
+ export declare function registerVariantDiscovery(fn: ProposeVariantFn): () => void;
159
+ /**
160
+ * Snapshot the registered discovery functions. Primarily for testing
161
+ * and for the engine implementation to iterate registered proposers.
162
+ */
163
+ export declare function getRegisteredVariantDiscovery(): ReadonlyArray<ProposeVariantFn>;
164
+ /**
165
+ * Drop every registered discovery function. Exposed for tests and
166
+ * for consumers who need to swap discovery models between sessions.
167
+ */
168
+ export declare function clearVariantDiscovery(): void;
@@ -6,6 +6,7 @@ import type { LegendInteractionMode, LegendPosition } from "../shared/hooks";
6
6
  import type { ChartMode, ChartAccessor, SelectionConfig } from "../shared/types";
7
7
  import type { OnObservationCallback } from "../../store/ObservationStore";
8
8
  import type { Datum } from "../shared/datumTypes";
9
+ import type { AutoPlaceAnnotations } from "../../recipes/annotationLayout";
9
10
  export interface RealtimeHeatmapProps<TDatum extends Datum = Datum> {
10
11
  /** Display mode: "primary" (full chrome), "context" (compact), "sparkline" (inline) */
11
12
  mode?: ChartMode;
@@ -64,6 +65,8 @@ export interface RealtimeHeatmapProps<TDatum extends Datum = Datum> {
64
65
  onHover?: (d: HoverData | null) => void;
65
66
  /** Annotation objects */
66
67
  annotations?: Datum[];
68
+ /** Opt into automatic placement for note-like annotations without manual offsets. */
69
+ autoPlaceAnnotations?: AutoPlaceAnnotations;
67
70
  /** SVG annotation render function */
68
71
  svgAnnotationRules?: (annotation: Datum, index: number, context: AnnotationContext) => ReactNode;
69
72
  /** Custom formatter for time axis ticks */
@@ -6,6 +6,7 @@ import type { LegendInteractionMode, LegendPosition } from "../shared/hooks";
6
6
  import type { ChartMode, ChartAccessor, SelectionConfig } from "../shared/types";
7
7
  import type { OnObservationCallback } from "../../store/ObservationStore";
8
8
  import type { Datum } from "../shared/datumTypes";
9
+ import type { AutoPlaceAnnotations } from "../../recipes/annotationLayout";
9
10
  export type RealtimeHistogramDirection = "up" | "down";
10
11
  export interface RealtimeHistogramProps<TDatum extends Datum = Datum> {
11
12
  /** Display mode: "primary" (full chrome), "context" (compact), "sparkline" (inline) */
@@ -87,6 +88,8 @@ export interface RealtimeHistogramProps<TDatum extends Datum = Datum> {
87
88
  onHover?: (d: HoverData | null) => void;
88
89
  /** Annotation objects */
89
90
  annotations?: Datum[];
91
+ /** Opt into automatic placement for note-like annotations without manual offsets. */
92
+ autoPlaceAnnotations?: AutoPlaceAnnotations;
90
93
  /** SVG annotation render function */
91
94
  svgAnnotationRules?: (annotation: Datum, index: number, context: AnnotationContext) => ReactNode;
92
95
  /** Custom formatter for time axis ticks */
@@ -6,6 +6,7 @@ import type { LegendInteractionMode, LegendPosition } from "../shared/hooks";
6
6
  import type { ChartMode, ChartAccessor, SelectionConfig } from "../shared/types";
7
7
  import type { OnObservationCallback } from "../../store/ObservationStore";
8
8
  import type { Datum } from "../shared/datumTypes";
9
+ import type { AutoPlaceAnnotations } from "../../recipes/annotationLayout";
9
10
  export interface RealtimeLineChartProps<TDatum extends Datum = Datum> {
10
11
  /** Display mode: "primary" (full chrome), "context" (compact), "sparkline" (inline) */
11
12
  mode?: ChartMode;
@@ -64,6 +65,8 @@ export interface RealtimeLineChartProps<TDatum extends Datum = Datum> {
64
65
  onHover?: (d: HoverData | null) => void;
65
66
  /** Annotation objects */
66
67
  annotations?: Datum[];
68
+ /** Opt into automatic placement for note-like annotations without manual offsets. */
69
+ autoPlaceAnnotations?: AutoPlaceAnnotations;
67
70
  /** SVG annotation render function */
68
71
  svgAnnotationRules?: (annotation: Datum, index: number, context: AnnotationContext) => ReactNode;
69
72
  /** Custom formatter for time axis ticks */
@@ -6,6 +6,7 @@ import type { LegendInteractionMode, LegendPosition } from "../shared/hooks";
6
6
  import type { ChartMode, ChartAccessor, SelectionConfig } from "../shared/types";
7
7
  import type { OnObservationCallback } from "../../store/ObservationStore";
8
8
  import type { Datum } from "../shared/datumTypes";
9
+ import type { AutoPlaceAnnotations } from "../../recipes/annotationLayout";
9
10
  export interface RealtimeSwarmChartProps<TDatum extends Datum = Datum> {
10
11
  /** Display mode: "primary" (full chrome), "context" (compact), "sparkline" (inline) */
11
12
  mode?: ChartMode;
@@ -70,6 +71,8 @@ export interface RealtimeSwarmChartProps<TDatum extends Datum = Datum> {
70
71
  onHover?: (d: HoverData | null) => void;
71
72
  /** Annotation objects (including threshold coloring) */
72
73
  annotations?: Datum[];
74
+ /** Opt into automatic placement for note-like annotations without manual offsets. */
75
+ autoPlaceAnnotations?: AutoPlaceAnnotations;
73
76
  /** SVG annotation render function */
74
77
  svgAnnotationRules?: (annotation: Datum, index: number, context: AnnotationContext) => ReactNode;
75
78
  /** Custom formatter for time axis ticks */
@@ -6,6 +6,7 @@ import type { LegendInteractionMode, LegendPosition } from "../shared/hooks";
6
6
  import type { ChartMode, ChartAccessor, SelectionConfig } from "../shared/types";
7
7
  import type { OnObservationCallback } from "../../store/ObservationStore";
8
8
  import type { Datum } from "../shared/datumTypes";
9
+ import type { AutoPlaceAnnotations } from "../../recipes/annotationLayout";
9
10
  export interface RealtimeWaterfallChartProps<TDatum extends Datum = Datum> {
10
11
  /** Display mode: "primary" (full chrome), "context" (compact), "sparkline" (inline) */
11
12
  mode?: ChartMode;
@@ -72,6 +73,8 @@ export interface RealtimeWaterfallChartProps<TDatum extends Datum = Datum> {
72
73
  onHover?: (d: HoverData | null) => void;
73
74
  /** Annotation objects */
74
75
  annotations?: Datum[];
76
+ /** Opt into automatic placement for note-like annotations without manual offsets. */
77
+ autoPlaceAnnotations?: AutoPlaceAnnotations;
75
78
  /** SVG annotation render function */
76
79
  svgAnnotationRules?: (annotation: Datum, index: number, context: AnnotationContext) => ReactNode;
77
80
  /** Custom formatter for time axis ticks */
@@ -0,0 +1,42 @@
1
+ import * as React from "react";
2
+ import type { Datum } from "./datumTypes";
3
+ export type AnnotationEmphasis = "primary" | "secondary";
4
+ /** A rendered annotation node paired with the source annotation it came
5
+ * from, so hierarchy treatment can read metadata after the per-type rule has
6
+ * produced the node. */
7
+ export interface AnnotationRenderPair {
8
+ node: React.ReactNode;
9
+ annotation: Datum;
10
+ }
11
+ /**
12
+ * Reveal CSS for M3 progressive disclosure. Density-deferred notes are hidden
13
+ * (opacity 0) until the chart they belong to is hovered or focused. The
14
+ * selectors are scoped to each frame's stable wrapper class so a chart only
15
+ * reveals its *own* deferred notes; `:focus-within` covers keyboard users who
16
+ * focus the canvas (the canvas lives inside the wrapper). Style rules are
17
+ * document-global regardless of where the `<style>` tag lands, so injecting it
18
+ * inside the (pointer-events:none) overlay svg is fine. Identical across charts
19
+ * — duplicate injections are harmless.
20
+ */
21
+ export declare const ANNOTATION_DISCLOSURE_REVEAL_CSS: string;
22
+ /**
23
+ * Apply annotation hierarchy — Rahman et al.'s "Hierarchy" consideration,
24
+ * reusing the same `emphasis` token charts already accept (`"primary"` /
25
+ * `"secondary"`). A `secondary` annotation dims, shrinks note text through
26
+ * inherited SVG font-size, and yields z-order; a `primary` one paints at full
27
+ * weight and on top.
28
+ *
29
+ * If no explicit emphasis is declared, annotations with
30
+ * `provenance.confidence` get an inferred reading order: confidence
31
+ * descending, then authored array order. The visual output uses that order as
32
+ * a subtle opacity gradient, with higher-confidence notes painted later.
33
+ *
34
+ * Type-agnostic: it wraps whatever the per-type rule produced, so all
35
+ * annotation types get hierarchy without each rule knowing about it.
36
+ *
37
+ * Zero-overhead and structure-preserving when no annotation declares an
38
+ * explicit emphasis or provenance confidence: the original nodes are returned
39
+ * untouched (same keys, same order), so existing charts render identically.
40
+ * Opacity composes multiplicatively with lifecycle freshness opacity.
41
+ */
42
+ export declare function applyAnnotationEmphasis(pairs: ReadonlyArray<AnnotationRenderPair>): React.ReactNode[];
@@ -3,8 +3,8 @@ import type { Datum } from "./datumTypes";
3
3
  * Coordinate resolution helpers for annotations.
4
4
  *
5
5
  * Resolves data coordinates to pixel positions, respecting anchor modes
6
- * (fixed, latest, sticky), pointId matching, and bounds checking for
7
- * streaming charts where data scrolls off-screen.
6
+ * (fixed, latest, sticky, semantic), pointId / stableId matching, and
7
+ * bounds checking for streaming charts where data scrolls off-screen.
8
8
  *
9
9
  * Dependencies: types (AnnotationContext)
10
10
  * Consumed by: annotationRules.tsx (all annotation type renderers)
@@ -17,6 +17,7 @@ export declare function resolveY(ann: Datum, context: AnnotationContext): number
17
17
  * - "fixed" (default): resolve from annotation's own fields
18
18
  * - "latest": resolve from the most recent datum in the buffer
19
19
  * - "sticky": resolve from annotation fields; if not found, use cached position
20
+ * - "semantic": resolve from provenance.stableId; if not found, use recorded fields
20
21
  */
21
22
  export declare function resolveAnchoredPosition(ann: Datum, index: number, context: AnnotationContext): {
22
23
  x: number;
@@ -1,4 +1,20 @@
1
1
  import * as React from "react";
2
2
  import type { AnnotationContext } from "../../realtime/types";
3
3
  import type { Datum } from "./datumTypes";
4
+ export { applyAnnotationEmphasis, type AnnotationRenderPair } from "./annotationHierarchy";
5
+ type AnnotationRule = (annotation: Datum, index: number, context: AnnotationContext) => React.ReactNode | null;
6
+ /**
7
+ * Run the SVG-overlay annotation pass: dispatch each annotation through the
8
+ * user's `svgAnnotationRules` (falling back to the default rules), drop the
9
+ * ones that render nothing, then apply emphasis hierarchy. Shared verbatim by
10
+ * the XY and ordinal overlays so the dispatch/filter/hierarchy logic lives in
11
+ * one place.
12
+ *
13
+ * Falsy-node semantics match the pre-emphasis `.filter(Boolean)` exactly: a
14
+ * rule returning `null`/`undefined` ("skip", e.g. the default rules' out-of-
15
+ * bounds path) — or any other falsy node (`0`/`""`/`false`) — produces no
16
+ * annotation and is dropped. A user rule that returns `null`/`undefined` falls
17
+ * through to the default rule, preserving the existing override contract.
18
+ */
19
+ export declare function renderAnnotationPass(annotations: ReadonlyArray<Datum>, defaultRule: AnnotationRule, userRule: AnnotationRule | undefined, context: AnnotationContext): React.ReactNode[];
4
20
  export declare function createDefaultAnnotationRules(_frameType: "xy" | "ordinal" | "network"): (annotation: Datum, index: number, context: AnnotationContext) => React.ReactNode | null;
@@ -0,0 +1,14 @@
1
+ import type { Datum } from "./datumTypes";
2
+ /**
3
+ * Note-like annotations compete for placement and density budgets. Keep this
4
+ * taxonomy centralized because layout, diagnostics, and accessibility checks
5
+ * all need to agree on what counts as a note.
6
+ */
7
+ export declare const NOTE_ANNOTATION_TYPES: readonly ["label", "callout", "callout-circle", "callout-rect", "text", "widget"];
8
+ export type NoteAnnotationType = (typeof NOTE_ANNOTATION_TYPES)[number];
9
+ /** Note types whose default renderer draws a connector unless disabled. */
10
+ export declare const CONNECTOR_ANNOTATION_TYPES: readonly ["label", "callout", "callout-circle", "callout-rect"];
11
+ export declare function annotationType(annotation: Datum): string;
12
+ export declare function isNoteAnnotation(annotation: Datum): boolean;
13
+ export declare function annotationDisablesConnector(annotation: Datum): boolean;
14
+ export declare function annotationDrawsConnector(annotation: Datum): boolean;
@@ -0,0 +1,90 @@
1
+ import type { Datum } from "./datumTypes";
2
+ export type A11yPrinciple = "perceivable" | "operable" | "understandable" | "robust" | "compromising" | "assistive" | "flexible";
3
+ /**
4
+ * - `pass` — satisfied (by config or by Semiotic's built-ins)
5
+ * - `fail` — a problem we can prove from the config
6
+ * - `warn` — a likely problem or a default worth revisiting
7
+ * - `manual`— can't be settled statically; run the named Chartability test
8
+ * - `not-applicable` — heuristic doesn't apply to this chart
9
+ */
10
+ export type A11yStatus = "pass" | "fail" | "warn" | "manual" | "not-applicable";
11
+ export interface A11yFinding {
12
+ /** Stable id, `principle.heuristic-slug` (e.g. "perceivable.low-contrast"). */
13
+ id: string;
14
+ principle: A11yPrinciple;
15
+ /** The Chartability heuristic name, verbatim. */
16
+ heuristic: string;
17
+ /** Chartability flags 14 of 50 heuristics as critical. */
18
+ critical: boolean;
19
+ status: A11yStatus;
20
+ message: string;
21
+ /** How to fix or, for `manual`, the test to run. */
22
+ fix?: string;
23
+ }
24
+ export interface AccessibilityAuditResult {
25
+ component: string;
26
+ /** No critical heuristic is in a `fail` state. (warn/manual don't break this.) */
27
+ ok: boolean;
28
+ summary: {
29
+ criticalsPassed: number;
30
+ /** Critical heuristics actually evaluated (excludes not-applicable). */
31
+ criticalsEvaluated: number;
32
+ fails: number;
33
+ warnings: number;
34
+ manual: number;
35
+ passes: number;
36
+ };
37
+ findings: A11yFinding[];
38
+ /** Always points back to the source framework. */
39
+ reference: string;
40
+ }
41
+ export interface AuditAccessibilityOptions {
42
+ /**
43
+ * Whether the chart is wrapped in a ChartContainer (or otherwise exposes a
44
+ * data-download / copy-config affordance). Lets the audit credit the
45
+ * "downloadable table" and "shareable state" heuristics. ChartContainer is
46
+ * the opt-in layer for full-accessibility chrome (title, caption,
47
+ * description), so several heuristics soften their remediation toward it.
48
+ * Default false.
49
+ */
50
+ inChartContainer?: boolean;
51
+ /**
52
+ * Whether ChartContainer's `describe` option (auto-generated L1–L3 description
53
+ * via describeChart) is enabled. Lets the "features described" heuristic pass.
54
+ * Default false.
55
+ */
56
+ describe?: boolean;
57
+ /**
58
+ * Whether ChartContainer's `navigable` option (a structured, screen-reader-
59
+ * navigable tree via buildNavigationTree/AccessibleNavTree) is enabled. Lets
60
+ * the "navigable structure" heuristic pass — including for hierarchy charts.
61
+ * Default false.
62
+ */
63
+ navigable?: boolean;
64
+ }
65
+ /**
66
+ * Audit a Semiotic chart configuration against the Chartability heuristics.
67
+ * Pure, SSR-safe, no DOM. See module docstring for what static analysis can
68
+ * and cannot determine.
69
+ */
70
+ export declare function auditAccessibility(component: string, props: Datum, options?: AuditAccessibilityOptions): AccessibilityAuditResult;
71
+ /**
72
+ * Distil an audit result into terse caveat strings — the author-actionable
73
+ * `fail` and `warn` findings, one line each. Lets the recommender (and an AI
74
+ * agent) read *receivability* caveats ("8 slices is too many," "color-only
75
+ * encoding") from the same channel as the perceptual caveats a capability
76
+ * descriptor already declares, instead of two disconnected surfaces.
77
+ *
78
+ * Pure. Pair with {@link auditAccessibility}:
79
+ * ```ts
80
+ * const caveats = accessibilityCaveats(auditAccessibility("PieChart", props))
81
+ * ```
82
+ *
83
+ * @param onlyCritical When true, restrict to critical heuristics (the 14 that
84
+ * Chartability flags). Default false — include all fail/warn findings.
85
+ */
86
+ export declare function accessibilityCaveats(result: AccessibilityAuditResult, { onlyCritical }?: {
87
+ onlyCritical?: boolean;
88
+ }): string[];
89
+ /** Render an audit result as a human-readable report (used by --audit-a11y + MCP). */
90
+ export declare function formatAccessibilityAudit(result: AccessibilityAuditResult): string;
@@ -1,9 +1,8 @@
1
1
  export type PropType = "string" | "number" | "boolean" | "array" | "object" | "function";
2
2
  export type DataShape = "array" | "object" | "network" | "realtime" | "none";
3
- export type ChartCategory = "xy" | "ordinal" | "network" | "geo" | "realtime";
3
+ export type ChartCategory = "xy" | "ordinal" | "network" | "geo" | "realtime" | "value";
4
4
  /**
5
- * Capability tags for runtime behavior. Driven by the
6
- * `hoc-frame-architecture-audit` Phase 1: each chart declares which
5
+ * Capability tags for runtime behavior. Each chart declares which
7
6
  * features it actually supports so docs, AI/MCP tools, and CI gates
8
7
  * can read structured truth instead of inferring it from the source.
9
8
  *
@@ -9,10 +9,8 @@ export interface DiagnosisResult {
9
9
  ok: boolean;
10
10
  diagnoses: Diagnosis[];
11
11
  }
12
- /**
13
- * Run anti-pattern diagnostics on a Semiotic chart configuration.
14
- *
15
- * Returns actionable diagnoses with severity, code, message, and fix instruction.
16
- * Runs validateProps internally — validation errors are included as diagnoses.
17
- */
12
+ /** WCAG contrast ratio between two hex colors. Exported for reuse by the
13
+ * accessibility audit (auditAccessibility.ts) single source of truth for the
14
+ * contrast math. Returns null if either color isn't a parseable hex. */
15
+ export declare function contrastRatio(hex1: string, hex2: string): number | null;
18
16
  export declare function diagnoseConfig(componentName: string, props: Datum): DiagnosisResult;
@@ -8,8 +8,10 @@ import type { Datum } from "./datumTypes";
8
8
  export interface NormalizedLinkedHover {
9
9
  name: string;
10
10
  fields: string[];
11
- mode?: "field" | "x-position";
11
+ mode?: "field" | "x-position" | "series";
12
12
  xField?: string;
13
+ /** Explicit series-identity field for `mode: "series"`; overrides the auto-resolved one. */
14
+ seriesField?: string;
13
15
  }
14
16
  export interface NormalizedLinkedBrush {
15
17
  name: string;
@@ -26,8 +28,9 @@ export interface NormalizedLinkedBrush {
26
28
  export declare function normalizeLinkedHover(prop: boolean | string | {
27
29
  name?: string;
28
30
  fields?: string[];
29
- mode?: "field" | "x-position";
31
+ mode?: "field" | "x-position" | "series";
30
32
  xField?: string;
33
+ seriesField?: string;
31
34
  } | undefined, fallbackFields?: string[]): NormalizedLinkedHover | null;
32
35
  /**
33
36
  * Normalize the linkedBrush prop into a consistent config object.