semiotic 3.7.3 → 3.7.5

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 (138) hide show
  1. package/CLAUDE.md +13 -7
  2. package/README.md +36 -20
  3. package/ai/schema.json +221 -1
  4. package/ai/system-prompt.md +2 -1
  5. package/dist/components/Annotation.d.ts +4 -0
  6. package/dist/components/DataSummaryContext.d.ts +1 -0
  7. package/dist/components/LinkedCharts.d.ts +2 -2
  8. package/dist/components/ai/chartCapabilityTypes.d.ts +16 -0
  9. package/dist/components/ai/dataPitfallsBridge.d.ts +101 -0
  10. package/dist/components/ai/dataQualityBridge.d.ts +201 -0
  11. package/dist/components/ai/describeChart.d.ts +2 -0
  12. package/dist/components/ai/generativeChart.d.ts +145 -0
  13. package/dist/components/charts/custom/NetworkCustomChart.d.ts +15 -2
  14. package/dist/components/charts/ordinal/SwarmPlot.d.ts +7 -0
  15. package/dist/components/charts/realtime/RealtimeLineChart.d.ts +21 -0
  16. package/dist/components/charts/realtime/aggregate.d.ts +51 -0
  17. package/dist/components/charts/realtime/eventTime.d.ts +26 -0
  18. package/dist/components/charts/shared/colorPalettes.d.ts +2 -0
  19. package/dist/components/charts/shared/colorUtils.d.ts +1 -12
  20. package/dist/components/charts/shared/minMax.d.ts +3 -0
  21. package/dist/components/charts/shared/radialGeometry.d.ts +0 -18
  22. package/dist/components/charts/shared/smartTooltip.d.ts +27 -0
  23. package/dist/components/charts/shared/statisticalOverlays.d.ts +35 -0
  24. package/dist/components/charts/shared/tooltipUtils.d.ts +2 -0
  25. package/dist/components/charts/shared/useChartSetup.d.ts +1 -1
  26. package/dist/components/charts/shared/useCustomChartSetup.d.ts +1 -1
  27. package/dist/components/charts/shared/validateChartData.d.ts +1 -1
  28. package/dist/components/charts/xy/Scatterplot.d.ts +9 -0
  29. package/dist/components/data/fromArrow.d.ts +54 -0
  30. package/dist/components/data/fromMermaid.d.ts +47 -0
  31. package/dist/components/data/fromObservablePlot.d.ts +69 -0
  32. package/dist/components/data/portability/index.d.ts +14 -0
  33. package/dist/components/data/portability/spec.d.ts +129 -0
  34. package/dist/components/data/portability/vegaLite.d.ts +62 -0
  35. package/dist/components/realtime/ReorderBuffer.d.ts +54 -0
  36. package/dist/components/realtime/RunningStats.d.ts +66 -0
  37. package/dist/components/realtime/WindowAccumulator.d.ts +105 -0
  38. package/dist/components/realtime/parseWindowDuration.d.ts +12 -0
  39. package/dist/components/recipes/customTooltip.d.ts +31 -0
  40. package/dist/components/recipes/gofish.d.ts +207 -0
  41. package/dist/components/recipes/gofishBoba.d.ts +23 -0
  42. package/dist/components/recipes/gofishIR.d.ts +169 -0
  43. package/dist/components/recipes/gofishIRExamples.d.ts +89 -0
  44. package/dist/components/recipes/gofishInterpreter.d.ts +52 -0
  45. package/dist/components/recipes/gofishLambdas.d.ts +145 -0
  46. package/dist/components/recipes/lineageDag.d.ts +129 -0
  47. package/dist/components/recipes/mermaidDag.d.ts +44 -0
  48. package/dist/components/recipes/packedClusterMatrix.d.ts +166 -0
  49. package/dist/components/recipes/recipeChrome.d.ts +100 -0
  50. package/dist/components/recipes/recipeLegend.d.ts +57 -0
  51. package/dist/components/recipes/recipeUtils.d.ts +105 -0
  52. package/dist/components/semiotic-ai.d.ts +6 -0
  53. package/dist/components/semiotic-data.d.ts +2 -0
  54. package/dist/components/semiotic-experimental.d.ts +20 -0
  55. package/dist/components/semiotic-network.d.ts +3 -1
  56. package/dist/components/semiotic-ordinal.d.ts +2 -0
  57. package/dist/components/semiotic-realtime.d.ts +9 -1
  58. package/dist/components/semiotic-recipes.d.ts +20 -1
  59. package/dist/components/semiotic-themes.d.ts +2 -0
  60. package/dist/components/semiotic-xy.d.ts +2 -0
  61. package/dist/components/semiotic.d.ts +6 -2
  62. package/dist/components/store/ObservationStore.d.ts +21 -2
  63. package/dist/components/store/SelectionStore.d.ts +1 -1
  64. package/dist/components/store/ThemeStore.d.ts +1 -1
  65. package/dist/components/store/TooltipStore.d.ts +1 -1
  66. package/dist/components/store/createStore.d.ts +1 -1
  67. package/dist/components/store/designTokens.d.ts +15 -0
  68. package/dist/components/store/useSelection.d.ts +22 -0
  69. package/dist/components/stream/NetworkHtmlMarksLayer.d.ts +44 -0
  70. package/dist/components/stream/NetworkPipelineStore.d.ts +32 -0
  71. package/dist/components/stream/NetworkSVGOverlay.d.ts +12 -0
  72. package/dist/components/stream/OrdinalPipelineStore.d.ts +18 -0
  73. package/dist/components/stream/PipelineStore.d.ts +27 -0
  74. package/dist/components/stream/SceneGraph.d.ts +5 -1
  75. package/dist/components/stream/customLayout.d.ts +20 -0
  76. package/dist/components/stream/customLayoutDiagnostics.d.ts +11 -0
  77. package/dist/components/stream/customLayoutSelection.d.ts +52 -0
  78. package/dist/components/stream/networkCustomLayout.d.ts +101 -1
  79. package/dist/components/stream/networkTypes.d.ts +38 -1
  80. package/dist/components/stream/ordinalCustomLayout.d.ts +17 -1
  81. package/dist/components/stream/ordinalSceneBuilders/types.d.ts +2 -0
  82. package/dist/components/stream/ordinalTypes.d.ts +19 -3
  83. package/dist/components/stream/renderers/networkSymbolRenderer.d.ts +2 -0
  84. package/dist/components/stream/renderers/symbolCanvasRenderer.d.ts +2 -0
  85. package/dist/components/stream/stalenessBands.d.ts +28 -0
  86. package/dist/components/stream/symbolPath.d.ts +24 -0
  87. package/dist/components/stream/types.d.ts +72 -3
  88. package/dist/components/stream/useStalenessCheck.d.ts +6 -1
  89. package/dist/components/stream/xySceneBuilders/pointScene.d.ts +2 -2
  90. package/dist/components/stream/xySceneBuilders/types.d.ts +5 -0
  91. package/dist/geo.min.js +1 -1
  92. package/dist/geo.module.min.js +1 -1
  93. package/dist/network.min.js +1 -1
  94. package/dist/network.module.min.js +1 -1
  95. package/dist/ordinal.min.js +1 -1
  96. package/dist/ordinal.module.min.js +1 -1
  97. package/dist/realtime.min.js +1 -1
  98. package/dist/realtime.module.min.js +1 -1
  99. package/dist/semiotic-ai-semiotic-ai-u954ylUn.js +2 -0
  100. package/dist/semiotic-ai-statisticalOverlays-CU7jW05Q.js +1 -0
  101. package/dist/semiotic-ai.d.ts +6 -0
  102. package/dist/semiotic-ai.min.js +1 -1
  103. package/dist/semiotic-ai.module.min.js +1 -1
  104. package/dist/semiotic-data.d.ts +2 -0
  105. package/dist/semiotic-data.min.js +1 -1
  106. package/dist/semiotic-data.module.min.js +1 -1
  107. package/dist/semiotic-experimental.d.ts +20 -0
  108. package/dist/semiotic-experimental.min.js +1 -0
  109. package/dist/semiotic-experimental.module.min.js +1 -0
  110. package/dist/semiotic-network.d.ts +3 -1
  111. package/dist/semiotic-ordinal.d.ts +2 -0
  112. package/dist/semiotic-realtime.d.ts +9 -1
  113. package/dist/semiotic-recipes.d.ts +20 -1
  114. package/dist/semiotic-recipes.min.js +1 -1
  115. package/dist/semiotic-recipes.module.min.js +1 -1
  116. package/dist/semiotic-semiotic-BmrYbi99.js +2 -0
  117. package/dist/semiotic-statisticalOverlays-IjZw8Lsu.js +1 -0
  118. package/dist/semiotic-themes.d.ts +2 -0
  119. package/dist/semiotic-themes.min.js +1 -1
  120. package/dist/semiotic-themes.module.min.js +1 -1
  121. package/dist/semiotic-utils.min.js +1 -1
  122. package/dist/semiotic-utils.module.min.js +1 -1
  123. package/dist/semiotic-value.min.js +1 -1
  124. package/dist/semiotic-value.module.min.js +1 -1
  125. package/dist/semiotic-xy.d.ts +2 -0
  126. package/dist/semiotic.d.ts +6 -2
  127. package/dist/semiotic.min.js +1 -1
  128. package/dist/semiotic.module.min.js +1 -1
  129. package/dist/server.min.js +1 -1
  130. package/dist/server.module.min.js +1 -1
  131. package/dist/xy-semiotic-xy-Dufu3D0-.js +2 -0
  132. package/dist/xy-statisticalOverlays-3Ni9bRph.js +1 -0
  133. package/dist/xy.min.js +1 -1
  134. package/dist/xy.module.min.js +1 -1
  135. package/package.json +11 -3
  136. package/dist/semiotic-ai-statisticalOverlays-C2PPlmXv.js +0 -1
  137. package/dist/semiotic-statisticalOverlays-C3DsOgr_.js +0 -1
  138. package/dist/xy-statisticalOverlays-C2PPlmXv.js +0 -1
@@ -2,10 +2,12 @@ import type { Datum } from "../charts/shared/datumTypes";
2
2
  import { type Quadtree } from "d3-quadtree";
3
3
  import { RingBuffer } from "../realtime/RingBuffer";
4
4
  import type { Changeset, StreamChartType, StreamScales, StreamLayout, SceneNode, PointSceneNode, CandlestickStyle, Style, ArrowOfTime, WindowMode, DecayConfig, PulseConfig, TransitionConfig, StalenessConfig, CurveType, BarStyle, ThemeSemanticColors, BandConfig } from "./types";
5
+ import type { SymbolName } from "./symbolPath";
5
6
  import { type CoercibleNumber } from "./accessorUtils";
6
7
  import type { ActiveTransition } from "./pipelineTransitionUtils";
7
8
  import type { ResolvedRibbon } from "./xySceneBuilders/ribbonScene";
8
9
  import type { CustomLayout } from "./customLayout";
10
+ import type { CustomLayoutSelection } from "./customLayoutSelection";
9
11
  import type { MarginType } from "../types/marginType";
10
12
  export interface PipelineConfig {
11
13
  chartType: StreamChartType;
@@ -28,6 +30,10 @@ export interface PipelineConfig {
28
30
  valueAccessor?: string | ((d: Datum) => CoercibleNumber);
29
31
  colorAccessor?: string | ((d: Datum) => string);
30
32
  sizeAccessor?: string | ((d: Datum) => CoercibleNumber);
33
+ /** Categorical accessor → glyph shape (scatter/bubble). Emits SymbolSceneNodes. */
34
+ symbolAccessor?: string | ((d: Datum) => string);
35
+ /** Explicit `{category → shape}` map; unmapped categories auto-assign. */
36
+ symbolMap?: Record<string, SymbolName>;
31
37
  groupAccessor?: string | ((d: Datum) => string);
32
38
  categoryAccessor?: string | ((d: Datum) => string);
33
39
  lineDataAccessor?: string;
@@ -139,6 +145,9 @@ export interface PipelineConfig {
139
145
  layoutConfig?: object;
140
146
  /** Resolved margin — passed through so LayoutContext.dimensions.margin reflects what the frame actually used. */
141
147
  layoutMargin?: MarginType;
148
+ /** Resolved shared selection projected into LayoutContext.selection. Owned by
149
+ * a dedicated frame effect (kept off the rebuild-triggering path). */
150
+ layoutSelection?: CustomLayoutSelection | null;
142
151
  }
143
152
  export declare class PipelineStore {
144
153
  private buffer;
@@ -152,6 +161,7 @@ export declare class PipelineStore {
152
161
  private getCategory;
153
162
  private getSize;
154
163
  private getColor;
164
+ private getSymbol;
155
165
  private getY0;
156
166
  /** Unified ribbon list — `boundsAccessor` + `band` both compose into
157
167
  * this single array (see `resolveRibbons`). Read by the scene
@@ -207,6 +217,13 @@ export declare class PipelineStore {
207
217
  version: number;
208
218
  /** Overlays returned from customLayout (consumed by StreamXYFrame for SVGOverlay). */
209
219
  customLayoutOverlays: import("react").ReactNode;
220
+ private _customLayoutDiagnosticsWarned;
221
+ /** Per-frame restyle callback from the custom layout result (see LayoutResult.restyle). */
222
+ private _customRestyle;
223
+ /** True when the active custom layout supplied a `restyle`. */
224
+ hasCustomRestyle: boolean;
225
+ /** Base (as-emitted) style per node, so restyle passes don't compound. */
226
+ private _baseStyles;
210
227
  /** True when the x accessor returns Date objects (auto-detected on first data ingestion) */
211
228
  xIsDate: boolean;
212
229
  private _quadtree;
@@ -339,5 +356,15 @@ export declare class PipelineStore {
339
356
  getXAccessor(): (d: Datum) => number;
340
357
  getYAccessor(): (d: Datum) => number;
341
358
  getCategoryAccessor(): ((d: Datum) => string) | undefined;
359
+ /** Update the selection the layout reads at the next rebuild, without
360
+ * triggering one. The frame then either repaints (restyle) or rebuilds. */
361
+ setLayoutSelection(selection: CustomLayoutSelection | null): void;
362
+ private applyCustomRestyle;
363
+ /**
364
+ * Re-apply the custom layout's `restyle` to the existing scene for
365
+ * `selection`, off each node's base style — no relayout, no quadtree rebuild
366
+ * (positions are unchanged). No-op when the layout supplied no `restyle`.
367
+ */
368
+ restyleScene(selection: CustomLayoutSelection | null): void;
342
369
  updateConfig(config: Partial<PipelineConfig>): void;
343
370
  }
@@ -1,4 +1,5 @@
1
- import type { SceneNode, LineSceneNode, AreaSceneNode, PointSceneNode, RectSceneNode, HeatcellSceneNode, Style, StreamScales, CurveType } from "./types";
1
+ import type { SceneNode, LineSceneNode, AreaSceneNode, PointSceneNode, SymbolSceneNode, RectSceneNode, HeatcellSceneNode, Style, StreamScales, CurveType } from "./types";
2
+ import type { SymbolName } from "./symbolPath";
2
3
  import type { Datum } from "../charts/shared/datumTypes";
3
4
  export declare function buildLineNode(data: Datum[], scales: StreamScales, xGet: (d: Datum) => number, yGet: (d: Datum) => number, style: Style, group?: string): LineSceneNode;
4
5
  export declare function buildAreaNode(data: Datum[], scales: StreamScales, xGet: (d: Datum) => number, yGet: (d: Datum) => number, baselineY: number, style: Style, group?: string, y0Get?: (d: Datum) => number): AreaSceneNode;
@@ -26,6 +27,9 @@ export declare function buildStackedAreaNodes(groups: {
26
27
  stackedTops: StackedTops;
27
28
  };
28
29
  export declare function buildPointNode(datum: Datum, scales: StreamScales, xGet: (d: Datum) => number, yGet: (d: Datum) => number, r: number, style: Style, pointId?: string): PointSceneNode | null;
30
+ /** Build a {@link SymbolSceneNode} — the glyph sibling of {@link buildPointNode}.
31
+ * `size` is the d3-symbol area (px²); convert from a target radius with πr². */
32
+ export declare function buildSymbolNode(datum: Datum, scales: StreamScales, xGet: (d: Datum) => number, yGet: (d: Datum) => number, size: number, symbolType: SymbolName, style: Style, pointId?: string): SymbolSceneNode | null;
29
33
  export declare function buildRectNode(x: number, y: number, w: number, h: number, style: Style, datum: any, group?: string): RectSceneNode;
30
34
  export declare function buildHeatcellNode(x: number, y: number, w: number, h: number, fill: string, datum: any, options?: {
31
35
  value?: number;
@@ -2,6 +2,7 @@ import type { ReactNode } from "react";
2
2
  import type { Datum } from "../charts/shared/datumTypes";
3
3
  import type { MarginType } from "../types/marginType";
4
4
  import type { SceneNode, StreamScales, Style, ThemeSemanticColors } from "./types";
5
+ import type { CustomLayoutSelection } from "./customLayoutSelection";
5
6
  /**
6
7
  * customLayout — escape hatch for bespoke chart geometry.
7
8
  *
@@ -57,12 +58,31 @@ export interface LayoutContext<C extends object = Record<string, unknown>> {
57
58
  resolveColor: (group: string, datum?: Datum) => string;
58
59
  /** User-supplied config blob threaded through `layoutConfig`. */
59
60
  config: C;
61
+ /**
62
+ * Shared-selection projection (from `selection` / `linkedHover`). `null` when
63
+ * the chart isn't wired to a selection store. Dim/highlight by
64
+ * `selection.predicate(datum)`. For selection-driven styling that shouldn't
65
+ * pay a relayout, express it via {@link LayoutResult.restyle} (canvas) /
66
+ * `useCustomLayoutSelection` (overlays) instead of recomputing geometry here.
67
+ */
68
+ selection?: CustomLayoutSelection | null;
60
69
  }
61
70
  export interface LayoutResult {
62
71
  /** Scene nodes to render. Get hit testing, transitions, decay, SSR for free. */
63
72
  nodes?: SceneNode[];
64
73
  /** SVG overlays composited above the canvas (labels, annotations). */
65
74
  overlays?: ReactNode;
75
+ /**
76
+ * **Per-frame restyle of canvas marks, without re-positioning.** When present,
77
+ * a selection/hover change re-applies styles to the existing scene nodes and
78
+ * repaints — it does NOT re-run the layout or rebuild the point quadtree.
79
+ * Return a style patch merged onto the node's *base* style; return nothing to
80
+ * leave it unchanged. Providing it opts the chart into the cheap selection
81
+ * path (hover stays O(nodes) paint, not O(nodes) relayout); omit it and
82
+ * selection changes re-run the layout. Pairs with `useCustomLayoutSelection`
83
+ * for the `overlays`.
84
+ */
85
+ restyle?: (node: SceneNode, selection: CustomLayoutSelection | null) => Partial<Style> | void;
66
86
  }
67
87
  /**
68
88
  * Note on extents: customLayout v1 does not return extents. Layouts that
@@ -0,0 +1,11 @@
1
+ import type { ReactNode } from "react";
2
+ export interface CustomLayoutDiagnosticNode {
3
+ datum?: unknown;
4
+ }
5
+ export interface CustomLayoutDiagnosticsOptions {
6
+ label: string;
7
+ nodes: readonly CustomLayoutDiagnosticNode[];
8
+ overlays?: ReactNode;
9
+ warned: Set<string>;
10
+ }
11
+ export declare function warnCustomLayoutDiagnostics(options: CustomLayoutDiagnosticsOptions): void;
@@ -0,0 +1,52 @@
1
+ import * as React from "react";
2
+ import type { Datum } from "../charts/shared/datumTypes";
3
+ /**
4
+ * The shared-selection state projected to a custom layout — both into the layout
5
+ * context (`ctx.selection`) and, via {@link useCustomLayoutSelection}, into the
6
+ * layout's React `overlays` subtree.
7
+ *
8
+ * `isActive` is `false` when no selection clause is present (treat every mark as
9
+ * selected). `predicate(datum)` returns `true` when the raw datum matches the
10
+ * active selection.
11
+ */
12
+ export interface CustomLayoutSelection {
13
+ isActive: boolean;
14
+ predicate: (datum: Datum) => boolean;
15
+ }
16
+ /**
17
+ * Provided by the Stream Frames around a custom layout's `overlays`. The value
18
+ * is the chart's resolved selection; it updates on hover/selection change
19
+ * **without** the frame re-running the layout — so an overlay that subscribes
20
+ * via {@link useCustomLayoutSelection} restyles itself while sceneNodes, the
21
+ * canvas paint, and the quadtree stay untouched.
22
+ */
23
+ export declare function CustomLayoutSelectionProvider({ value, children, }: {
24
+ value: CustomLayoutSelection | null;
25
+ children: React.ReactNode;
26
+ }): React.ReactElement;
27
+ /**
28
+ * Read the chart's resolved selection from inside a custom layout's `overlays`.
29
+ *
30
+ * Subscribing components re-render on selection/hover change **without a
31
+ * relayout** — the frame swaps only the context value, never rebuilding the
32
+ * scene. This is the read side of the styling channel; pair it with the
33
+ * `restyle` callback on the layout result to dim/highlight **canvas** marks
34
+ * without re-positioning.
35
+ *
36
+ * Returns an inactive selection (`isActive: false`, predicate always `true`)
37
+ * when called outside a custom chart, so overlays render fully-lit by default.
38
+ *
39
+ * @example
40
+ * ```tsx
41
+ * function ClusterLabel({ datum, x, y }) {
42
+ * const { isActive, predicate } = useCustomLayoutSelection()
43
+ * const dim = isActive && !predicate(datum)
44
+ * return <text x={x} y={y} opacity={dim ? 0.15 : 1}>{datum.label}</text>
45
+ * }
46
+ * ```
47
+ */
48
+ export declare function useCustomLayoutSelection(): CustomLayoutSelection;
49
+ /** Wrap a custom layout's overlay node in the selection provider (no-op for a
50
+ * null node, so an empty overlay layer stays empty rather than becoming a
51
+ * provider element). Used by the Stream Frames. */
52
+ export declare function wrapWithCustomLayoutSelection(node: React.ReactNode, selection: CustomLayoutSelection | null): React.ReactNode;
@@ -1,6 +1,34 @@
1
1
  import type { ReactNode } from "react";
2
2
  import type { NetworkSceneNode, NetworkSceneEdge, NetworkLabel, RealtimeNode, RealtimeEdge } from "./networkTypes";
3
- import type { ThemeSemanticColors } from "./types";
3
+ import type { Style, ThemeSemanticColors } from "./types";
4
+ import type { Datum } from "../charts/shared/datumTypes";
5
+ import type { CustomLayoutSelection } from "./customLayoutSelection";
6
+ /**
7
+ * The shared selection state, projected into the custom-layout context.
8
+ *
9
+ * When the chart participates in a `LinkedCharts` / selection store (via
10
+ * `NetworkCustomChart`'s `selection` / `linkedHover` props), the frame
11
+ * threads the resolved predicate here so a custom layout can dim or
12
+ * highlight marks by the *shared* selection — the same predicate the
13
+ * built-in HOCs apply to `nodeStyle`. Mirrors `SelectionHookResult`.
14
+ *
15
+ * `predicate` receives the **raw** datum (the user object you passed in
16
+ * `nodes`, i.e. `node.data ?? node`), matching the `d.data || d`
17
+ * convention the built-in network charts use. `isActive` is `false`
18
+ * when no selection clause is present — when `false`, treat every mark
19
+ * as selected (draw at full weight).
20
+ *
21
+ * This is orthogonal to `config` (your `layoutConfig` blob): use `config`
22
+ * for host-owned highlight sets you compute yourself (e.g. a graph
23
+ * reachability set), and `selection` for cross-chart coordination that
24
+ * rides the shared store.
25
+ */
26
+ export interface NetworkLayoutSelection {
27
+ /** Whether a selection clause is currently active. */
28
+ isActive: boolean;
29
+ /** Returns `true` when the raw datum matches the active selection. */
30
+ predicate: (datum: Datum) => boolean;
31
+ }
4
32
  /**
5
33
  * customLayout escape hatch for `StreamNetworkFrame`.
6
34
  *
@@ -54,6 +82,51 @@ export interface NetworkLayoutContext<C extends object = Record<string, unknown>
54
82
  resolveColor: (key: string) => string;
55
83
  /** User-supplied config blob threaded through `layoutConfig`. */
56
84
  config: C;
85
+ /**
86
+ * Shared-selection projection. Present when the chart is wired to a
87
+ * `LinkedCharts` / selection store; `null` otherwise. Use
88
+ * `selection.isActive` + `selection.predicate(node.data ?? node)` to
89
+ * dim/highlight marks by the cross-chart selection. See
90
+ * {@link NetworkLayoutSelection}.
91
+ */
92
+ selection?: NetworkLayoutSelection | null;
93
+ }
94
+ /**
95
+ * An HTML/React node positioned in plot space, rendered into a real DOM layer
96
+ * above the canvas (and above the SVG `overlays`) — **not** an SVG
97
+ * `<foreignObject>`.
98
+ *
99
+ * Reach for this over `overlays` when a mark is text-heavy or a rich component
100
+ * (a labelled card, an Axon widget) **and** it dims/animates on interaction.
101
+ * HTML-in-SVG (`<foreignObject>`) gets no compositor layer, so an `opacity`
102
+ * change — the common hover-dim — re-rasterizes the text; on a large graph that
103
+ * stalls the interaction. A real DOM element composites `opacity` / `transform`
104
+ * / `visibility` without re-painting its contents.
105
+ *
106
+ * The framework owns placement: each mark is wrapped in an absolutely-positioned
107
+ * element that tracks the same margin (and any future zoom/pan) transform the
108
+ * canvas and `overlays` receive, so a mark at `(x, y)` lands exactly where a
109
+ * `sceneNode` at `(x, y)` does. The consumer owns the content's appearance.
110
+ *
111
+ * Marks are non-interactive by default (`pointer-events: none`) — pointer events
112
+ * fall through to the canvas, so existing `sceneNodes` hit-testing
113
+ * (`onObservation` / `onClick`) is unaffected. Emit a transparent hit-rect
114
+ * `sceneNode` per mark to keep the canvas authoritative for interaction.
115
+ */
116
+ export interface NetworkHtmlMark {
117
+ /** Stable identity for keying / reconciliation across layout runs. A
118
+ * position-only update repositions without remounting the content. */
119
+ id: string;
120
+ /** Top-left x in plot coordinates — the same space as `sceneNodes`. */
121
+ x: number;
122
+ /** Top-left y in plot coordinates — the same space as `sceneNodes`. */
123
+ y: number;
124
+ /** Wrapper width in plot units. */
125
+ width: number;
126
+ /** Wrapper height in plot units. */
127
+ height: number;
128
+ /** Arbitrary HTML/React rendered inside the positioned wrapper. */
129
+ content: ReactNode;
57
130
  }
58
131
  export interface NetworkLayoutResult {
59
132
  /** Positioned scene primitives. Circles, rects, or arcs. */
@@ -64,4 +137,31 @@ export interface NetworkLayoutResult {
64
137
  labels?: NetworkLabel[];
65
138
  /** SVG overlays composited above the canvas. */
66
139
  overlays?: ReactNode;
140
+ /**
141
+ * HTML/React nodes positioned in plot space, rendered into one real DOM layer
142
+ * above the canvas and SVG `overlays`. Use for rich-text / component marks that
143
+ * dim or animate on hover — they composite `opacity`/`transform` changes
144
+ * instead of re-rasterizing text the way an SVG `<foreignObject>` does. The
145
+ * framework owns positioning + transform so marks stay pixel-aligned with
146
+ * `sceneNodes`. Additive: a layout that omits it renders no extra DOM. See
147
+ * {@link NetworkHtmlMark}.
148
+ */
149
+ htmlMarks?: NetworkHtmlMark[];
150
+ /**
151
+ * **Per-frame restyle of canvas marks, without re-positioning.** When present,
152
+ * a selection/hover change re-applies styles to the existing scene nodes and
153
+ * repaints — it does **not** re-run the layout or rebuild the quadtree. Return
154
+ * a style patch merged onto the node's *base* style (the style it was emitted
155
+ * with); return nothing to leave it unchanged.
156
+ *
157
+ * This is the canvas counterpart to {@link useCustomLayoutSelection} (which
158
+ * restyles `overlays`): compute geometry once in the layout body, and express
159
+ * selection-driven dimming/highlighting here so hover stays O(nodes) paint
160
+ * instead of O(nodes+edges) relayout. Providing it opts the chart into the
161
+ * cheap selection path; omit it and selection changes re-run the layout (the
162
+ * pre-existing behavior).
163
+ */
164
+ restyle?: (node: NetworkSceneNode, selection: CustomLayoutSelection | null) => Partial<Style> | void;
165
+ /** Per-frame restyle of edges — same contract as {@link NetworkLayoutResult.restyle}. */
166
+ restyleEdge?: (edge: NetworkSceneEdge, selection: CustomLayoutSelection | null) => Partial<Style> | void;
67
167
  }
@@ -6,6 +6,7 @@ import type { Style, DecayConfig, PulseConfig, TransitionConfig, StalenessConfig
6
6
  import type { AnimateProp } from "./pipelineTransitionUtils";
7
7
  import type { Datum } from "../charts/shared/datumTypes";
8
8
  import type { AutoPlaceAnnotations } from "../recipes/annotationLayout";
9
+ import type { NetworkSymbolName } from "./symbolPath";
9
10
  export interface TensionConfig {
10
11
  weightChange: number;
11
12
  newEdge: number;
@@ -247,6 +248,33 @@ export interface NetworkArcNode {
247
248
  _pulseColor?: string;
248
249
  _pulseGlowRadius?: number;
249
250
  }
251
+ /**
252
+ * Symbol node — a glyph rendered from a `d3-shape` symbol path (or a custom
253
+ * path). The per-datum shape channel: recipes that encode a categorical field
254
+ * as marker shape (e.g. `packedClusterMatrix`) emit these. Hit-tests as a
255
+ * circle of the symbol's effective radius; renders on canvas and in SVG/SSR.
256
+ */
257
+ export interface NetworkSymbolNode {
258
+ type: "symbol";
259
+ cx: number;
260
+ cy: number;
261
+ /** d3-symbol area in px² — drives the glyph's drawn size. */
262
+ size: number;
263
+ /** Named shape. Ignored when `path` is set. @default "circle" */
264
+ symbolType?: NetworkSymbolName;
265
+ /** Pre-built SVG path string, origin-centered — overrides `symbolType`. */
266
+ path?: string;
267
+ /** Rotation in radians about (cx, cy). */
268
+ rotation?: number;
269
+ style: Style;
270
+ datum: SceneDatum;
271
+ id?: string;
272
+ label?: string;
273
+ depth?: number;
274
+ _pulseIntensity?: number;
275
+ _pulseColor?: string;
276
+ _pulseGlowRadius?: number;
277
+ }
250
278
  /** Line edge — used by force */
251
279
  export interface NetworkLineEdge {
252
280
  type: "line";
@@ -305,7 +333,7 @@ export interface NetworkCurvedEdge {
305
333
  _cachedPath2D?: Path2D;
306
334
  _cachedPath2DSource?: string;
307
335
  }
308
- export type NetworkSceneNode = NetworkCircleNode | NetworkRectNode | NetworkArcNode;
336
+ export type NetworkSceneNode = NetworkCircleNode | NetworkRectNode | NetworkArcNode | NetworkSymbolNode;
309
337
  export type NetworkSceneEdge = NetworkLineEdge | NetworkBezierEdge | NetworkRibbonEdge | NetworkCurvedEdge;
310
338
  /** Label data for the SVG overlay */
311
339
  export interface NetworkLabel {
@@ -453,6 +481,11 @@ export interface NetworkPipelineConfig {
453
481
  customNetworkLayout?: import("./networkCustomLayout").NetworkCustomLayout;
454
482
  /** User-supplied config blob threaded through to NetworkLayoutContext.config. */
455
483
  layoutConfig?: object;
484
+ /** Resolved shared-selection predicate, surfaced to a custom layout as
485
+ * `NetworkLayoutContext.selection`. Render-only — deliberately kept out of
486
+ * the layout/ingest-affecting signature so a selection change re-runs
487
+ * `buildScene` (re-emitting dimmed marks) without a re-ingest or re-layout. */
488
+ layoutSelection?: import("./networkCustomLayout").NetworkLayoutSelection | null;
456
489
  }
457
490
  export interface StreamNetworkFrameProps<T = Datum> {
458
491
  chartType: NetworkChartType;
@@ -566,6 +599,10 @@ export interface StreamNetworkFrameProps<T = Datum> {
566
599
  customNetworkLayout?: import("./networkCustomLayout").NetworkCustomLayout;
567
600
  /** User-supplied config blob threaded through to NetworkLayoutContext.config. */
568
601
  layoutConfig?: object;
602
+ /** Resolved shared-selection predicate, surfaced to a custom layout as
603
+ * `NetworkLayoutContext.selection`. Set by `NetworkCustomChart` from its
604
+ * `selection` / `linkedHover` wiring; render-only (no re-ingest on change). */
605
+ layoutSelection?: import("./networkCustomLayout").NetworkLayoutSelection | null;
569
606
  }
570
607
  export interface StreamNetworkFrameHandle {
571
608
  push(edge: EdgePush): void;
@@ -3,7 +3,8 @@ import type { ScaleBand, ScaleLinear } from "d3-scale";
3
3
  import type { Datum } from "../charts/shared/datumTypes";
4
4
  import type { MarginType } from "../types/marginType";
5
5
  import type { OrdinalSceneNode, OrdinalScales } from "./ordinalTypes";
6
- import type { ThemeSemanticColors } from "./types";
6
+ import type { Style, ThemeSemanticColors } from "./types";
7
+ import type { CustomLayoutSelection } from "./customLayoutSelection";
7
8
  /**
8
9
  * customLayout escape hatch for `StreamOrdinalFrame`.
9
10
  *
@@ -75,10 +76,25 @@ export interface OrdinalLayoutContext<C extends object = Record<string, unknown>
75
76
  resolveColor: (key: string) => string;
76
77
  /** User-supplied config blob threaded through `layoutConfig`. */
77
78
  config: C;
79
+ /**
80
+ * Shared-selection projection (from `selection` / `linkedHover`). `null` when
81
+ * unwired. For selection-driven styling without a relayout, use
82
+ * {@link OrdinalLayoutResult.restyle} (canvas) / `useCustomLayoutSelection`
83
+ * (overlays) rather than recomputing geometry here.
84
+ */
85
+ selection?: CustomLayoutSelection | null;
78
86
  }
79
87
  export interface OrdinalLayoutResult {
80
88
  /** Scene nodes to render. Get hit testing, transitions, decay, SSR for free. */
81
89
  nodes?: OrdinalSceneNode[];
82
90
  /** SVG overlays composited above the canvas (labels, axis lines, annotations). */
83
91
  overlays?: ReactNode;
92
+ /**
93
+ * **Per-frame restyle of canvas marks, without re-positioning.** When present,
94
+ * a selection/hover change re-applies styles to the existing scene nodes +
95
+ * repaints — no relayout, no quadtree rebuild. Return a style patch merged onto
96
+ * the node's *base* style. Providing it opts into the cheap selection path.
97
+ * Pairs with `useCustomLayoutSelection` for the `overlays`.
98
+ */
99
+ restyle?: (node: OrdinalSceneNode, selection: CustomLayoutSelection | null) => Partial<Style> | void;
84
100
  }
@@ -10,6 +10,8 @@ export interface OrdinalSceneContext {
10
10
  getStack?: (d: Datum) => string;
11
11
  getGroup?: (d: Datum) => string;
12
12
  getColor?: (d: Datum) => string;
13
+ /** Categorical accessor → glyph shape (swarm/dot symbolBy). */
14
+ getSymbol?: (d: Datum) => string;
13
15
  getConnector?: (d: Datum) => string;
14
16
  getO: (d: Datum) => string;
15
17
  multiScales: ScaleLinear<number, number>[];
@@ -2,6 +2,7 @@ import type { ReactNode } from "react";
2
2
  import type { ScaleLinear, ScaleBand } from "d3-scale";
3
3
  import type { WindowMode, HoverAnnotationConfig, HoverData, AnnotationContext } from "../realtime/types";
4
4
  import type { AutoPlaceAnnotations } from "../recipes/annotationLayout";
5
+ import type { SymbolName } from "./symbolPath";
5
6
  import type { Style, SceneDatum, DecayConfig, PulseConfig, TransitionConfig, StalenessConfig, ThemeSemanticColors } from "./types";
6
7
  import type { AnimateProp } from "./pipelineTransitionUtils";
7
8
  import type { GradientLegendConfig, LegendGroup, LegendLayout } from "../types/legendTypes";
@@ -162,9 +163,9 @@ export interface TrapezoidSceneNode {
162
163
  _targetOpacity?: number;
163
164
  _transitionKey?: string;
164
165
  }
165
- export type { Style, PointSceneNode, RectSceneNode } from "./types";
166
- import type { PointSceneNode, RectSceneNode } from "./types";
167
- export type OrdinalSceneNode = RectSceneNode | PointSceneNode | WedgeSceneNode | BoxplotSceneNode | ViolinSceneNode | ConnectorSceneNode | TrapezoidSceneNode;
166
+ export type { Style, PointSceneNode, RectSceneNode, SymbolSceneNode } from "./types";
167
+ import type { PointSceneNode, RectSceneNode, SymbolSceneNode } from "./types";
168
+ export type OrdinalSceneNode = RectSceneNode | PointSceneNode | SymbolSceneNode | WedgeSceneNode | BoxplotSceneNode | ViolinSceneNode | ConnectorSceneNode | TrapezoidSceneNode;
168
169
  export interface OrdinalColumn {
169
170
  name: string;
170
171
  x: number;
@@ -193,6 +194,10 @@ export interface OrdinalPipelineConfig {
193
194
  categoryAccessor?: string | ((d: Datum) => string);
194
195
  valueAccessor?: string | ((d: Datum) => number) | Array<string | ((d: Datum) => number)>;
195
196
  colorAccessor?: string | ((d: Datum) => string);
197
+ /** Categorical accessor → glyph shape (swarm/dot). Emits SymbolSceneNodes. */
198
+ symbolAccessor?: string | ((d: Datum) => string);
199
+ /** Explicit `{category → shape}` map; unmapped categories auto-assign. */
200
+ symbolMap?: Record<string, SymbolName>;
196
201
  stackBy?: string | ((d: Datum) => string);
197
202
  groupBy?: string | ((d: Datum) => string);
198
203
  timeAccessor?: string | ((d: Datum) => number);
@@ -275,6 +280,9 @@ export interface OrdinalPipelineConfig {
275
280
  /** Resolved margin — passed through so OrdinalLayoutContext.dimensions.margin
276
281
  * reflects what the frame actually used. */
277
282
  layoutMargin?: import("../types/marginType").MarginType;
283
+ /** Resolved shared selection projected into OrdinalLayoutContext.selection.
284
+ * Owned by a dedicated frame effect (kept off the rebuild path). */
285
+ layoutSelection?: import("./customLayoutSelection").CustomLayoutSelection | null;
278
286
  }
279
287
  export interface StreamOrdinalFrameProps<T = Datum> {
280
288
  chartType: OrdinalChartType;
@@ -285,6 +293,10 @@ export interface StreamOrdinalFrameProps<T = Datum> {
285
293
  /** Value field — the quantitative dimension (replaces rAccessor). Can be array for multiAxis. */
286
294
  valueAccessor?: string | ((d: T) => number) | Array<string | ((d: T) => number)>;
287
295
  colorAccessor?: string | ((d: T) => string);
296
+ /** Categorical accessor → glyph shape (swarm/dot). */
297
+ symbolAccessor?: string | ((d: T) => string);
298
+ /** Explicit `{category → shape}` map for `symbolAccessor`; unmapped auto-assign. */
299
+ symbolMap?: Record<string, SymbolName>;
288
300
  stackBy?: string | ((d: T) => string);
289
301
  groupBy?: string | ((d: T) => string);
290
302
  timeAccessor?: string | ((d: T) => number);
@@ -440,6 +452,10 @@ export interface StreamOrdinalFrameProps<T = Datum> {
440
452
  customLayout?: import("./ordinalCustomLayout").OrdinalCustomLayout;
441
453
  /** User-supplied config blob threaded through to OrdinalLayoutContext.config. */
442
454
  layoutConfig?: object;
455
+ /** Resolved shared selection projected into `OrdinalLayoutContext.selection`.
456
+ * Kept off the rebuild path — restyles (if the layout returned `restyle`) or
457
+ * rebuilds, never re-ingests. */
458
+ layoutSelection?: import("./customLayoutSelection").CustomLayoutSelection | null;
443
459
  }
444
460
  export interface StreamOrdinalFrameHandle<T = Datum> {
445
461
  push(datum: T): void;
@@ -0,0 +1,2 @@
1
+ import type { NetworkSceneNode } from "../networkTypes";
2
+ export declare function networkSymbolRenderer(ctx: CanvasRenderingContext2D, nodes: NetworkSceneNode[]): void;
@@ -0,0 +1,2 @@
1
+ import type { StreamRendererFn } from "./types";
2
+ export declare const symbolCanvasRenderer: StreamRendererFn;
@@ -0,0 +1,28 @@
1
+ import { type LifecycleBand, type LifecycleBandThresholds } from "../realtime/lifecycleBands";
2
+ import type { StalenessConfig } from "./types";
3
+ /**
4
+ * Default canvas alpha per band in graded mode. `fresh` is full; each
5
+ * older band dims further. Override individually via
6
+ * `staleness.graded.opacities`.
7
+ */
8
+ export declare const DEFAULT_STALENESS_BAND_OPACITY: Record<LifecycleBand, number>;
9
+ export interface ResolvedStaleness {
10
+ /** Canvas alpha to apply (1 = no dimming). */
11
+ alpha: number;
12
+ /**
13
+ * Classified band. In graded mode this is the `bandFromAge` result;
14
+ * in binary mode it collapses to `"fresh"` or `"stale"`.
15
+ */
16
+ band: LifecycleBand;
17
+ /** Whether the chart should read as stale (drives the badge). */
18
+ isStale: boolean;
19
+ }
20
+ /**
21
+ * Resolve the dimming + band for a given idle duration. Pure — the
22
+ * render path computes idle from `now - lastIngestTime` each paint and
23
+ * the staleness poll uses the same function to decide when to repaint.
24
+ *
25
+ * `idleMs` of 0 (no data yet) is treated as fresh.
26
+ */
27
+ export declare function resolveStaleness(staleness: StalenessConfig | undefined, idleMs: number): ResolvedStaleness;
28
+ export type { LifecycleBand, LifecycleBandThresholds };
@@ -0,0 +1,24 @@
1
+ /** Named glyph shapes a `symbol` scene node can request. */
2
+ export type NetworkSymbolName = "circle" | "square" | "triangle" | "diamond" | "star" | "cross" | "wye" | "chevron";
3
+ /** Pipeline-neutral alias — the symbol mark is shared by network, XY, and
4
+ * ordinal scenes, so callers outside the network pipeline read this name. */
5
+ export type SymbolName = NetworkSymbolName;
6
+ /** Default shape assignment order when a recipe auto-maps distinct categories. */
7
+ export declare const SYMBOL_SEQUENCE: NetworkSymbolName[];
8
+ /**
9
+ * SVG path `d` string for a glyph, centered at the origin. `size` follows the
10
+ * d3-symbol area convention (px²) for named shapes; the custom `chevron`
11
+ * derives a comparable radius from it. `customPath`, when provided, is returned
12
+ * verbatim (already origin-centered and sized).
13
+ */
14
+ export declare function symbolPathString(symbolType: NetworkSymbolName | string | undefined, size: number, customPath?: string): string;
15
+ /** Effective radius of a glyph with the given d3-symbol `size` (used for hit testing). */
16
+ export declare function symbolRadius(size: number): number;
17
+ /**
18
+ * Maximum extent (circumradius) of a glyph from its center — the radius that
19
+ * actually bounds the drawn shape. For `circle` this equals {@link symbolRadius},
20
+ * but pointy shapes (triangle/star/chevron) reach further for the same area, so
21
+ * collision-packing must use this to avoid visual overlap. Polygonal shapes are
22
+ * measured from their path vertices; `circle` is the area formula.
23
+ */
24
+ export declare function symbolExtent(symbolType: NetworkSymbolName | string | undefined, size: number, customPath?: string): number;