semiotic 3.5.1 → 3.5.2

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 (92) hide show
  1. package/CLAUDE.md +21 -19
  2. package/README.md +22 -16
  3. package/ai/chartSuggestions.cjs +191 -3
  4. package/ai/componentMetadata.cjs +3 -3
  5. package/ai/dist/mcp-server.js +266 -48
  6. package/ai/examples.md +68 -0
  7. package/ai/schema.json +900 -1
  8. package/ai/system-prompt.md +4 -1
  9. package/dist/components/Tooltip/FlippingTooltip.d.ts +16 -1
  10. package/dist/components/charts/geo/FlowMap.d.ts +13 -4
  11. package/dist/components/charts/index.d.ts +6 -0
  12. package/dist/components/charts/network/OrbitDiagram.d.ts +5 -5
  13. package/dist/components/charts/network/ProcessSankey.d.ts +141 -0
  14. package/dist/components/charts/network/processSankey/algorithm.d.ts +136 -0
  15. package/dist/components/charts/network/processSankey/buildScenes.d.ts +45 -0
  16. package/dist/components/charts/network/processSankey/ribbonInputs.d.ts +32 -0
  17. package/dist/components/charts/network/processSankey/streamingLayout.d.ts +58 -0
  18. package/dist/components/charts/network/processSankey/tooltipUtils.d.ts +41 -0
  19. package/dist/components/charts/ordinal/BarChart.d.ts +12 -0
  20. package/dist/components/charts/ordinal/DotPlot.d.ts +9 -0
  21. package/dist/components/charts/ordinal/GaugeChart.d.ts +20 -0
  22. package/dist/components/charts/ordinal/SwimlaneChart.d.ts +5 -0
  23. package/dist/components/charts/realtime/RealtimeHistogram.d.ts +14 -11
  24. package/dist/components/charts/realtime/defaultRealtimeTooltip.d.ts +19 -0
  25. package/dist/components/charts/shared/axisExtent.d.ts +59 -0
  26. package/dist/components/charts/shared/chartSpecs.d.ts +75 -0
  27. package/dist/components/charts/shared/colorUtils.d.ts +8 -2
  28. package/dist/components/charts/shared/networkUtils.d.ts +3 -5
  29. package/dist/components/charts/shared/radialGeometry.d.ts +99 -0
  30. package/dist/components/charts/shared/regressionUtils.d.ts +59 -0
  31. package/dist/components/charts/shared/selectionUtils.d.ts +8 -1
  32. package/dist/components/charts/shared/streamPropsHelpers.d.ts +5 -0
  33. package/dist/components/charts/shared/types.d.ts +13 -0
  34. package/dist/components/charts/shared/useAreaSeriesSetup.d.ts +75 -0
  35. package/dist/components/charts/shared/useEncodingDomain.d.ts +48 -0
  36. package/dist/components/charts/shared/useFrameImperativeHandle.d.ts +1 -1
  37. package/dist/components/charts/shared/useNetworkChartSetup.d.ts +148 -0
  38. package/dist/components/charts/shared/useOrdinalPieceStyle.d.ts +87 -0
  39. package/dist/components/charts/shared/useSeriesFeatures.d.ts +57 -0
  40. package/dist/components/charts/shared/useStreamStatus.d.ts +33 -0
  41. package/dist/components/charts/shared/useXYLineStyle.d.ts +69 -0
  42. package/dist/components/charts/shared/useXYPointStyle.d.ts +87 -0
  43. package/dist/components/charts/xy/AreaChart.d.ts +24 -0
  44. package/dist/components/charts/xy/BubbleChart.d.ts +9 -0
  45. package/dist/components/charts/xy/ConnectedScatterplot.d.ts +16 -0
  46. package/dist/components/charts/xy/DifferenceChart.d.ts +172 -0
  47. package/dist/components/charts/xy/Scatterplot.d.ts +34 -0
  48. package/dist/components/geometry/ribbonGeometry.d.ts +76 -0
  49. package/dist/components/semiotic-ai.d.ts +2 -0
  50. package/dist/components/semiotic-network.d.ts +4 -0
  51. package/dist/components/semiotic-realtime.d.ts +2 -0
  52. package/dist/components/semiotic-utils.d.ts +4 -0
  53. package/dist/components/semiotic-xy.d.ts +2 -0
  54. package/dist/components/semiotic.d.ts +3 -3
  55. package/dist/components/server/serverChartConfigs.d.ts +2 -0
  56. package/dist/components/stream/GeoPipelineStore.d.ts +21 -0
  57. package/dist/components/stream/OrdinalSVGOverlay.d.ts +8 -0
  58. package/dist/components/stream/PipelineStore.d.ts +5 -0
  59. package/dist/components/stream/SVGOverlay.d.ts +18 -0
  60. package/dist/components/stream/annotationAccessorResolver.d.ts +39 -0
  61. package/dist/components/stream/geoTypes.d.ts +12 -0
  62. package/dist/components/stream/ordinalTypes.d.ts +12 -0
  63. package/dist/components/stream/renderers/cornerRadii.d.ts +33 -0
  64. package/dist/components/stream/types.d.ts +23 -0
  65. package/dist/components/types/legendTypes.d.ts +1 -1
  66. package/dist/geo.min.js +1 -1
  67. package/dist/geo.module.min.js +1 -1
  68. package/dist/network.min.js +1 -1
  69. package/dist/network.module.min.js +1 -1
  70. package/dist/ordinal.min.js +1 -1
  71. package/dist/ordinal.module.min.js +1 -1
  72. package/dist/realtime.min.js +1 -1
  73. package/dist/realtime.module.min.js +1 -1
  74. package/dist/semiotic-ai.d.ts +2 -0
  75. package/dist/semiotic-ai.min.js +1 -1
  76. package/dist/semiotic-ai.module.min.js +1 -1
  77. package/dist/semiotic-network.d.ts +4 -0
  78. package/dist/semiotic-realtime.d.ts +2 -0
  79. package/dist/semiotic-recipes.min.js +1 -1
  80. package/dist/semiotic-recipes.module.min.js +1 -1
  81. package/dist/semiotic-utils.d.ts +4 -0
  82. package/dist/semiotic-utils.min.js +1 -1
  83. package/dist/semiotic-utils.module.min.js +1 -1
  84. package/dist/semiotic-xy.d.ts +2 -0
  85. package/dist/semiotic.d.ts +3 -3
  86. package/dist/semiotic.min.js +1 -1
  87. package/dist/semiotic.module.min.js +1 -1
  88. package/dist/server.min.js +1 -1
  89. package/dist/server.module.min.js +1 -1
  90. package/dist/xy.min.js +1 -1
  91. package/dist/xy.module.min.js +1 -1
  92. package/package.json +10 -5
@@ -1,6 +1,9 @@
1
1
  # Semiotic — React Data Visualization
2
2
 
3
- Use sub-path imports: `semiotic/xy` (143KB gz), `semiotic/ordinal` (109KB), `semiotic/network` (98KB), `semiotic/geo` (93KB), `semiotic/realtime` (145KB). Or `semiotic/ai` for all 39 non-geo HOCs in one import (269KB).
3
+ <!-- semiotic-bundle-sizes:start -->
4
+ <!-- Auto-generated by scripts/sync-bundle-sizes.mjs — do not edit by hand. -->
5
+ **Use sub-path imports** — `semiotic/xy` (83KB gz), `semiotic/ordinal` (67KB gz), `semiotic/network` (62KB gz), `semiotic/geo` (50KB gz), `semiotic/realtime` (88KB gz), `semiotic/server` (114KB gz), `semiotic/utils` (22KB gz), `semiotic/recipes` (5KB gz), `semiotic/themes` (4KB gz), `semiotic/data` (3KB gz), `semiotic/ai` (184KB gz). Full `semiotic` is 183KB gz.
6
+ <!-- semiotic-bundle-sizes:end -->
4
7
 
5
8
  ## Flat Array Data (`data: object[]`)
6
9
  - **LineChart** — `xAccessor`, `yAccessor`, `lineBy` (multi-line), `curve`
@@ -29,6 +29,21 @@ interface FlippingTooltipProps {
29
29
  * On first render, uses a heuristic (similar to the old 70%/30% thresholds).
30
30
  * After measuring the actual tooltip size via ref, repositions precisely to
31
31
  * prevent clipping against container edges.
32
+ *
33
+ * Two defensive behaviors:
34
+ *
35
+ * - **Chrome guarantee.** If the rendered tooltip content lacks the
36
+ * `semiotic-tooltip` className on its root, the wrapper applies
37
+ * `defaultTooltipStyle` to itself so the tooltip always has a
38
+ * visible background, padding, and shadow. Shared tooltip helpers
39
+ * keep working unchanged (their `semiotic-tooltip` class causes the
40
+ * wrapper to stay transparent).
41
+ * - **Non-finite position guard.** Returns `null` when `x` or `y` is
42
+ * `NaN` / `Infinity`. The frame's hover plumbing can occasionally
43
+ * produce a non-finite hit-test result during a scale rebuild or
44
+ * when a custom layout emits a degenerate vertex; without the
45
+ * guard, React throws `'NaN' is an invalid value for the 'top' css
46
+ * style property` and the entire frame stops rendering.
32
47
  */
33
- export declare function FlippingTooltip({ x, y, containerWidth, containerHeight, margin, children, className, zIndex }: FlippingTooltipProps): import("react/jsx-runtime").JSX.Element;
48
+ export declare function FlippingTooltip({ x, y, containerWidth, containerHeight, margin, children, className, zIndex }: FlippingTooltipProps): import("react/jsx-runtime").JSX.Element | null;
34
49
  export {};
@@ -1,5 +1,7 @@
1
1
  import type { Datum } from "../shared/datumTypes";
2
+ import * as React from "react";
2
3
  import type { StreamGeoFrameProps, ProjectionProp } from "../../stream/geoTypes";
4
+ import type { RealtimeFrameHandle } from "../../realtime/types";
3
5
  import type { BaseChartProps, ChartAccessor } from "../shared/types";
4
6
  import { type TooltipProp } from "../../Tooltip/Tooltip";
5
7
  import type { LegendInteractionMode, LegendPosition } from "../shared/hooks";
@@ -79,6 +81,13 @@ export interface FlowMapProps<TDatum extends Datum = Datum> extends BaseChartPro
79
81
  tileCacheSize?: number;
80
82
  /** Annotations */
81
83
  annotations?: Datum[];
84
+ /**
85
+ * ID accessor on flow records — required for `ref.current.remove(id)`
86
+ * and `ref.current.update(id, …)`. The accessor reads the same field
87
+ * the user supplies on each flow; the field is preserved on the
88
+ * resolved line entry so the frame's `removeLine` can match by id.
89
+ */
90
+ lineIdAccessor?: string | ((d: Datum) => string);
82
91
  /** Passthrough */
83
92
  frameProps?: Partial<Omit<StreamGeoFrameProps, "projection">>;
84
93
  }
@@ -132,7 +141,7 @@ export interface FlowMapProps<TDatum extends Datum = Datum> extends BaseChartPro
132
141
  * />
133
142
  * ```
134
143
  */
135
- export declare function FlowMap<TDatum extends Datum = Datum>(props: FlowMapProps<TDatum>): import("react/jsx-runtime").JSX.Element;
136
- export declare namespace FlowMap {
137
- var displayName: string;
138
- }
144
+ export declare const FlowMap: {
145
+ <TDatum extends Datum = Datum>(props: FlowMapProps<TDatum> & React.RefAttributes<RealtimeFrameHandle>): React.ReactElement | null;
146
+ displayName?: string;
147
+ };
@@ -14,6 +14,8 @@ export { LineChart } from "./xy/LineChart";
14
14
  export type { LineChartProps } from "./xy/LineChart";
15
15
  export { AreaChart } from "./xy/AreaChart";
16
16
  export type { AreaChartProps } from "./xy/AreaChart";
17
+ export { DifferenceChart } from "./xy/DifferenceChart";
18
+ export type { DifferenceChartProps } from "./xy/DifferenceChart";
17
19
  export { StackedAreaChart } from "./xy/StackedAreaChart";
18
20
  export type { StackedAreaChartProps } from "./xy/StackedAreaChart";
19
21
  export { Heatmap } from "./xy/Heatmap";
@@ -72,6 +74,10 @@ export { ChordDiagram } from "./network/ChordDiagram";
72
74
  export type { ChordDiagramProps } from "./network/ChordDiagram";
73
75
  export { SankeyDiagram } from "./network/SankeyDiagram";
74
76
  export type { SankeyDiagramProps } from "./network/SankeyDiagram";
77
+ export { ProcessSankey } from "./network/ProcessSankey";
78
+ export type { ProcessSankeyProps, ProcessSankeyTick } from "./network/ProcessSankey";
79
+ export { validateProcessSankey, formatProcessSankeyIssue, } from "./network/processSankey/algorithm";
80
+ export type { ProcessSankeyNode as ProcessSankeyValidatorNode, ProcessSankeyEdge as ProcessSankeyValidatorEdge, ProcessSankeyIssue, } from "./network/processSankey/algorithm";
75
81
  export { TreeDiagram } from "./network/TreeDiagram";
76
82
  export type { TreeDiagramProps } from "./network/TreeDiagram";
77
83
  export { Treemap } from "./network/Treemap";
@@ -4,7 +4,7 @@ import type { StreamNetworkFrameProps } from "../../stream/networkTypes";
4
4
  import type { BaseChartProps } from "../shared/types";
5
5
  import { type TooltipProp } from "../../Tooltip/Tooltip";
6
6
  export interface OrbitNode {
7
- datum: any;
7
+ datum: Datum;
8
8
  x: number;
9
9
  y: number;
10
10
  ring: number;
@@ -38,11 +38,11 @@ export interface OrbitDiagramProps<TDatum extends Datum = Datum> extends BaseCha
38
38
  */
39
39
  orbitMode?: OrbitMode;
40
40
  /** Ring size divisor per depth. Larger = tighter orbits. @default 2.95 */
41
- orbitSize?: number | ((node: any) => number);
41
+ orbitSize?: number | ((node: Datum) => number);
42
42
  /** Orbit speed in degrees per frame @default 0.25 */
43
43
  speed?: number;
44
44
  /** Per-node speed modifier @default (node) => 1 / (node.depth + 1) */
45
- revolution?: (node: any) => number;
45
+ revolution?: (node: Datum) => number;
46
46
  /**
47
47
  * Built-in revolution style presets:
48
48
  * - "locked": children rotate with parent at decreasing speed (default)
@@ -53,11 +53,11 @@ export interface OrbitDiagramProps<TDatum extends Datum = Datum> extends BaseCha
53
53
  */
54
54
  revolutionStyle?: "locked" | "decay" | "alternate";
55
55
  /** Vertical squash for elliptical orbits. 1 = circle, 0.5 = ellipse @default 1 */
56
- eccentricity?: number | ((node: any) => number);
56
+ eccentricity?: number | ((node: Datum) => number);
57
57
  /** Show orbital ring paths @default true */
58
58
  showRings?: boolean;
59
59
  /** Node radius. Number or function of node. @default 6 */
60
- nodeRadius?: number | ((node: any) => number);
60
+ nodeRadius?: number | ((node: Datum) => number);
61
61
  /** Show node labels @default false */
62
62
  showLabels?: boolean;
63
63
  /** Enable animation @default true */
@@ -0,0 +1,141 @@
1
+ import * as React from "react";
2
+ import type { Datum } from "../shared/datumTypes";
3
+ import type { BaseChartProps, ChartAccessor } from "../shared/types";
4
+ import type { RealtimeFrameHandle } from "../../realtime/types";
5
+ import type { StreamNetworkFrameProps, ParticleStyle } from "../../stream/networkTypes";
6
+ import { type TooltipProp } from "../../Tooltip/Tooltip";
7
+ type TimeLike = number | Date | string;
8
+ export interface ProcessSankeyTick {
9
+ date: TimeLike;
10
+ label: string;
11
+ }
12
+ export interface ProcessSankeyProps<TNode extends Datum = Datum, TEdge extends Datum = Datum> extends BaseChartProps {
13
+ nodes?: TNode[];
14
+ edges?: TEdge[];
15
+ /** [tStart, tEnd] of the chart's x-axis. Required. */
16
+ domain: [TimeLike, TimeLike];
17
+ /** Optional axis ticks. Each tick: { date, label }. */
18
+ axisTicks?: ProcessSankeyTick[];
19
+ nodeIdAccessor?: ChartAccessor<TNode, string>;
20
+ sourceAccessor?: ChartAccessor<TEdge, string>;
21
+ targetAccessor?: ChartAccessor<TEdge, string>;
22
+ valueAccessor?: ChartAccessor<TEdge, number>;
23
+ startTimeAccessor?: ChartAccessor<TEdge, TimeLike>;
24
+ endTimeAccessor?: ChartAccessor<TEdge, TimeLike>;
25
+ /**
26
+ * Accessor for a node's explicit lifetime extent — a `[start, end]`
27
+ * tuple of time-likes. Lane spans
28
+ * `min(xExtent[0], earliestEdge)` to `max(xExtent[1], latestEdge)`.
29
+ */
30
+ xExtentAccessor?: ChartAccessor<TNode, [TimeLike, TimeLike]>;
31
+ edgeIdAccessor?: ChartAccessor<TEdge, string>;
32
+ colorBy?: ChartAccessor<TNode, string>;
33
+ colorScheme?: string | string[];
34
+ /** Show a swatch + label legend. Defaults to `true` when `colorBy` is set. */
35
+ showLegend?: boolean;
36
+ /** Legend position. Default `"right"`. */
37
+ legendPosition?: "right" | "left" | "top" | "bottom";
38
+ /**
39
+ * Format function for time values — applied to axis tick labels and
40
+ * to time fields in the default tooltip. Same convention as
41
+ * `xFormat` on XY charts.
42
+ */
43
+ timeFormat?: (d: number | Date) => string | React.ReactNode;
44
+ /** Format function for the `value` field. Mirrors `yFormat` on XY charts. */
45
+ valueFormat?: (d: number) => string | React.ReactNode;
46
+ pairing?: "value" | "temporal";
47
+ packing?: "off" | "reuse";
48
+ laneOrder?: "insertion" | "crossing-min" | "inside-out" | "crossing-min+inside-out";
49
+ ribbonLane?: "source" | "target" | "both";
50
+ lifetimeMode?: "full" | "half";
51
+ showLaneRails?: boolean;
52
+ showQualityReadout?: boolean;
53
+ edgeOpacity?: number;
54
+ /** Tooltip content. `false` disables, `true` uses the default,
55
+ * or pass a `Tooltip(...)` / custom function for full control. */
56
+ tooltip?: TooltipProp;
57
+ enableHover?: boolean;
58
+ onClick?: (datum: Datum, position?: {
59
+ x: number;
60
+ y: number;
61
+ }) => void;
62
+ showParticles?: boolean;
63
+ /** Style config for the particle overlay — same shape
64
+ * StreamNetworkFrame consumes from SankeyDiagram. Defaults
65
+ * (radius 3, opacity 0.7, spawnRate 0.1, maxPerEdge 50) live in
66
+ * `DEFAULT_PARTICLE_STYLE`. */
67
+ particleStyle?: ParticleStyle;
68
+ /** Pass-through to the underlying StreamNetworkFrame. */
69
+ frameProps?: Partial<Omit<StreamNetworkFrameProps, "nodes" | "edges" | "chartType" | "size" | "customNetworkLayout" | "layoutConfig">>;
70
+ }
71
+ /**
72
+ * ProcessSankey — temporal flow between nodes with an actual time x-axis.
73
+ *
74
+ * Built on top of `StreamNetworkFrame` via the `customNetworkLayout`
75
+ * escape hatch. Bands and ribbons emit as `bezier` scene-edges; the
76
+ * Frame handles canvas painting, hit testing, accessibility, theme
77
+ * cascade, and the push API.
78
+ *
79
+ * **Differs from SankeyDiagram in three ways:**
80
+ *
81
+ * 1. **Edges carry time.** Each edge has `startTime` / `endTime`.
82
+ * 2. **Nodes have lifetimes, not ranks.** A node's vertical lane spans
83
+ * `min(xExtent[0], earliestEdge)` to `max(xExtent[1], latestEdge)`.
84
+ * 3. **Static-graph cycles are valid** as long as edges move forward in time.
85
+ *
86
+ * @example
87
+ * ```tsx
88
+ * // Static fixture: pre-built nodes + timed edges, categorical colors.
89
+ * <ProcessSankey
90
+ * nodes={[
91
+ * { id: "Alice", category: "Person", xExtent: ["2026-01-06", "2026-01-06"] },
92
+ * { id: "Bob", category: "Person", xExtent: ["2026-02-01", "2026-02-01"] },
93
+ * { id: "Eng", category: "Team" },
94
+ * { id: "Release", category: "Milestone", xExtent: ["2026-04-15", "2026-05-30"] },
95
+ * ]}
96
+ * edges={[
97
+ * { id: "alice-eng", source: "Alice", target: "Eng", value: 8,
98
+ * startTime: "2026-01-20", endTime: "2026-02-10" },
99
+ * { id: "bob-eng", source: "Bob", target: "Eng", value: 5,
100
+ * startTime: "2026-02-15", endTime: "2026-03-15" },
101
+ * { id: "eng-rel", source: "Eng", target: "Release", value: 13,
102
+ * startTime: "2026-04-15", endTime: "2026-05-15" },
103
+ * ]}
104
+ * domain={["2026-01-01", "2026-05-31"]}
105
+ * colorBy="category"
106
+ * showLegend
107
+ * />
108
+ * ```
109
+ *
110
+ * @example
111
+ * ```tsx
112
+ * // Push mode: omit `edges`, mutate via the ref. Same chart shape, but
113
+ * // edges arrive over time. Particles depict throughput.
114
+ * const ref = useRef<RealtimeFrameHandle>(null)
115
+ *
116
+ * useEffect(() => {
117
+ * const id = setInterval(() => {
118
+ * ref.current?.push({
119
+ * source: "API", target: "DB", value: Math.random() * 10,
120
+ * startTime: Date.now(), endTime: Date.now() + 1500,
121
+ * })
122
+ * }, 800)
123
+ * return () => clearInterval(id)
124
+ * }, [])
125
+ *
126
+ * return (
127
+ * <ProcessSankey
128
+ * ref={ref}
129
+ * domain={[t0, t0 + 60_000]}
130
+ * showParticles
131
+ * colorBy="category"
132
+ * showLegend
133
+ * />
134
+ * )
135
+ * ```
136
+ */
137
+ export declare const ProcessSankey: {
138
+ <TNode extends Datum = Datum, TEdge extends Datum = Datum>(props: ProcessSankeyProps<TNode, TEdge> & React.RefAttributes<RealtimeFrameHandle>): React.ReactElement | null;
139
+ displayName?: string;
140
+ };
141
+ export default ProcessSankey;
@@ -0,0 +1,136 @@
1
+ export interface ProcessSankeyNode {
2
+ id: string;
3
+ /** Optional explicit lifetime bound [start, end]. Lifetime is
4
+ * `min(xExtent[0], earliestEdge)` to `max(xExtent[1], latestEdge)`. */
5
+ xExtent?: [number, number];
6
+ }
7
+ export interface ProcessSankeyEdge {
8
+ id: string;
9
+ source: string;
10
+ target: string;
11
+ value: number;
12
+ startTime: number;
13
+ endTime: number;
14
+ }
15
+ export interface ProcessSankeyIssue {
16
+ kind: string;
17
+ id?: string;
18
+ source?: string;
19
+ target?: string;
20
+ endpoint?: string;
21
+ nodeId?: string;
22
+ }
23
+ export interface ProcessSankeySample {
24
+ t: number;
25
+ topMass: number;
26
+ botMass: number;
27
+ }
28
+ export type AttachmentSide = "top" | "bot";
29
+ export type AttachmentKind = "in" | "out";
30
+ export interface ProcessSankeyAttachment {
31
+ side: AttachmentSide;
32
+ time: number;
33
+ sideMassBefore: number;
34
+ sideMassAfter: number;
35
+ kind: AttachmentKind;
36
+ value: number;
37
+ }
38
+ export interface ProcessSankeyNodeData {
39
+ samples: ProcessSankeySample[];
40
+ peak: number;
41
+ topPeak: number;
42
+ botPeak: number;
43
+ localAttachments: Map<string, ProcessSankeyAttachment>;
44
+ }
45
+ export interface ProcessSankeySlotPeak {
46
+ topPeak: number;
47
+ botPeak: number;
48
+ }
49
+ export interface ProcessSankeySlotOccupant {
50
+ id: string;
51
+ end: number;
52
+ }
53
+ export interface ProcessSankeySlot {
54
+ peak: ProcessSankeySlotPeak;
55
+ occupants: ProcessSankeySlotOccupant[];
56
+ }
57
+ export interface ProcessSankeyLaneLifetime {
58
+ start: number | null;
59
+ end: number | null;
60
+ }
61
+ export interface ProcessSankeySideRecord {
62
+ sourceSide?: AttachmentSide;
63
+ targetSide?: AttachmentSide;
64
+ }
65
+ export interface ProcessSankeyLayout {
66
+ nodeData: Record<string, ProcessSankeyNodeData>;
67
+ sides: Map<string, ProcessSankeySideRecord>;
68
+ valueScale: number;
69
+ padding: number;
70
+ compressedPadding: boolean;
71
+ centerlines: Record<string, number>;
72
+ laneLifetime: Record<string, ProcessSankeyLaneLifetime>;
73
+ slots: ProcessSankeySlot[];
74
+ slotByNode: Record<string, number>;
75
+ crossingsBefore: number | null;
76
+ crossingsAfter: number | null;
77
+ lengthBefore: number | null;
78
+ lengthAfter: number | null;
79
+ }
80
+ export interface ProcessSankeyOptions {
81
+ plotH: number;
82
+ pairing?: "value" | "temporal";
83
+ packing?: "off" | "reuse";
84
+ laneOrder?: "insertion" | "crossing-min" | "inside-out" | "crossing-min+inside-out";
85
+ lifetimeMode?: "full" | "half";
86
+ }
87
+ export interface ProcessSankeyEdgeIndex {
88
+ incoming: Record<string, ProcessSankeyEdge[]>;
89
+ outgoing: Record<string, ProcessSankeyEdge[]>;
90
+ }
91
+ type Domain = [number, number] | null | undefined;
92
+ export declare function validateProcessSankey(nodes: ProcessSankeyNode[], edges: ProcessSankeyEdge[], domain: [number, number]): ProcessSankeyIssue[];
93
+ export declare function formatProcessSankeyIssue(issue: ProcessSankeyIssue): string;
94
+ export declare function buildEdgeIndex(nodes: ProcessSankeyNode[], edges: ProcessSankeyEdge[]): ProcessSankeyEdgeIndex;
95
+ export declare function assignSides(nodes: ProcessSankeyNode[], edges: ProcessSankeyEdge[], edgeIndex: ProcessSankeyEdgeIndex, pairing?: "value" | "temporal"): Map<string, ProcessSankeySideRecord>;
96
+ export declare function computeNode(node: ProcessSankeyNode, edgeIndex: ProcessSankeyEdgeIndex, sides: Map<string, ProcessSankeySideRecord>): ProcessSankeyNodeData;
97
+ export declare function clampTime(t: number, domain: Domain): number;
98
+ export declare function clampSamples(samples: ProcessSankeySample[], domain: Domain): ProcessSankeySample[];
99
+ export declare function attachmentYRange(att: ProcessSankeyAttachment, cl: number, S: number): [number, number];
100
+ export declare function buildBandPath(samples: ProcessSankeySample[], cl: number, S: number, xScale: (t: number) => number, domain: Domain): string | null;
101
+ type SlotByNode = Record<string, number>;
102
+ export declare function countCrossings(slotByNode: SlotByNode, edges: ProcessSankeyEdge[]): number;
103
+ export declare function totalEdgeLength(slotByNode: SlotByNode, edges: ProcessSankeyEdge[]): number;
104
+ interface LaneLayoutOptions {
105
+ plotH: number;
106
+ padding: number;
107
+ valueScale: number;
108
+ packing?: "off" | "reuse";
109
+ laneOrder?: "insertion" | "crossing-min" | "inside-out" | "crossing-min+inside-out";
110
+ lifetimeMode?: "full" | "half";
111
+ }
112
+ interface LaneLayoutResult {
113
+ effectiveSlotsHeight: number;
114
+ centerlines: Record<string, number>;
115
+ laneLifetime: Record<string, ProcessSankeyLaneLifetime>;
116
+ slots: ProcessSankeySlot[];
117
+ slotByNode: SlotByNode;
118
+ slotCenter: number[];
119
+ crossingsBefore: number | null;
120
+ crossingsAfter: number | null;
121
+ lengthBefore: number | null;
122
+ lengthAfter: number | null;
123
+ }
124
+ export declare function computeLaneLayout(nodes: ProcessSankeyNode[], edges: ProcessSankeyEdge[], nodeData: Record<string, ProcessSankeyNodeData>, edgeIndex: ProcessSankeyEdgeIndex, opts: LaneLayoutOptions): LaneLayoutResult;
125
+ /**
126
+ * Compute the full Process Sankey layout for a given dataset and
127
+ * configuration. Pure function, no side effects.
128
+ *
129
+ * The chart's time domain isn't a layout opt — domain handling lives
130
+ * in the geometry helpers (`buildBandPath`, `buildRibbonGeometry`,
131
+ * `clampSamples`) which receive an `xScale` and a domain pair from
132
+ * the caller. The layout itself is timeless apart from the per-node
133
+ * sample/event timestamps.
134
+ */
135
+ export declare function computeProcessSankeyLayout(nodes: ProcessSankeyNode[], edges: ProcessSankeyEdge[], opts: ProcessSankeyOptions): ProcessSankeyLayout;
136
+ export {};
@@ -0,0 +1,45 @@
1
+ import { scaleTime } from "d3-scale";
2
+ import { type ProcessSankeyOptions, type ProcessSankeyLayout, type ProcessSankeyIssue } from "./algorithm";
3
+ import type { ProcessSankeyLayoutConfig } from "./streamingLayout";
4
+ import type { Datum } from "../../shared/datumTypes";
5
+ export interface ProcessSankeyNormalizedNode {
6
+ id: string;
7
+ xExtent?: [number, number];
8
+ __raw?: Datum;
9
+ }
10
+ export interface ProcessSankeyNormalizedEdge {
11
+ id: string;
12
+ source: string;
13
+ target: string;
14
+ value: number;
15
+ startTime: number;
16
+ endTime: number;
17
+ __raw?: Datum;
18
+ }
19
+ export interface BuildScenesInput {
20
+ nodes: ProcessSankeyNormalizedNode[];
21
+ edges: ProcessSankeyNormalizedEdge[];
22
+ domain: [number, number];
23
+ plotW: number;
24
+ plotH: number;
25
+ ribbonLane: "source" | "target" | "both";
26
+ edgeOpacity: number;
27
+ /** Resolves a node's color by id+index (lets the caller plug in
28
+ * the same theme/colorScheme/colorBy resolution the HOC uses). */
29
+ colorOf: (id: string, idx: number) => string;
30
+ layoutOpts: Pick<ProcessSankeyOptions, "pairing" | "packing" | "laneOrder" | "lifetimeMode">;
31
+ }
32
+ export interface BuildScenesResult {
33
+ layout: ProcessSankeyLayout | null;
34
+ layoutConfig: ProcessSankeyLayoutConfig;
35
+ issues: ProcessSankeyIssue[];
36
+ /** Used downstream for tooltips (mass-history) and overlays. */
37
+ xScale: ReturnType<typeof scaleTime>;
38
+ }
39
+ /**
40
+ * Run the full ProcessSankey layout pipeline. Returns the algorithm
41
+ * output, the bands/ribbons specs ready for `customNetworkLayout`, and
42
+ * the validation issues (caller decides whether to render an error
43
+ * gate or fall through). Pure: no DOM, no React, no rAF.
44
+ */
45
+ export declare function buildProcessSankeyScenes(input: BuildScenesInput): BuildScenesResult;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Compute the `RibbonGeometryInput` shape for a ProcessSankey ribbon
3
+ * from its source/target attachment data. Both the HOC (CSR) and the
4
+ * pure scene builder (SSR) call this so the coords feeding into the
5
+ * shared `buildRibbonGeometry` helper match between the two paths.
6
+ *
7
+ * Replaces `algorithm.js`'s `buildRibbonPath` — the path-D formula
8
+ * itself moved into `buildRibbonGeometry` so SankeyDiagram and
9
+ * ProcessSankey emit identical M-C-L-C-Z shapes.
10
+ */
11
+ import type { RibbonGeometryInput } from "../../../geometry/ribbonGeometry";
12
+ type Side = "top" | "bot";
13
+ type Kind = "in" | "out";
14
+ interface AttachmentLike {
15
+ side: Side;
16
+ time: number;
17
+ sideMassBefore: number;
18
+ sideMassAfter: number;
19
+ kind: Kind;
20
+ value: number;
21
+ }
22
+ type RibbonLane = "source" | "target" | "both";
23
+ type XScale = (t: number) => number;
24
+ /**
25
+ * Build the geometry inputs for a single ProcessSankey ribbon. The
26
+ * source attachment is assumed to be `kind: "out"` (the value leaves
27
+ * the source on its outgoing side); the target attachment is
28
+ * `kind: "in"`. attachmentYRange's formula is inlined here to keep
29
+ * this module pure TS (the `algorithm.js` version is JS-only).
30
+ */
31
+ export declare function computeProcessSankeyRibbonInputs(srcAtt: AttachmentLike, srcCenterline: number, tgtAtt: AttachmentLike, tgtCenterline: number, valueScale: number, xScale: XScale, lane: RibbonLane, domain: [number, number] | null): RibbonGeometryInput;
32
+ export {};
@@ -0,0 +1,58 @@
1
+ import type { NetworkCustomLayout } from "../../../stream/networkCustomLayout";
2
+ import type { BezierCache } from "../../../stream/networkTypes";
3
+ import type { Datum } from "../../shared/datumTypes";
4
+ export interface ProcessSankeyBandSpec {
5
+ id: string;
6
+ pathD: string;
7
+ fill: string;
8
+ stroke?: string;
9
+ strokeWidth?: number;
10
+ /** The user's raw node datum, surfaced as `data` in HoverData. */
11
+ rawDatum: Datum;
12
+ /** Pre-computed label x/y for the node band. */
13
+ labelX: number;
14
+ labelY: number;
15
+ labelText: string;
16
+ }
17
+ export interface ProcessSankeyRibbonSpec {
18
+ id: string;
19
+ pathD: string;
20
+ fill: string;
21
+ opacity: number;
22
+ /** The user's raw edge datum, surfaced as `data` in HoverData. */
23
+ rawDatum: Datum;
24
+ /**
25
+ * Pre-computed cubic bezier control points + halfWidth for the
26
+ * shared particle pipeline. ProcessSankey writes these alongside
27
+ * the ribbon's path-D string so the frame's particle pool can
28
+ * spawn / step / render against them without re-deriving the
29
+ * ribbon geometry. Optional — when omitted the ribbon paints
30
+ * normally but no particles flow along it.
31
+ */
32
+ bezier?: BezierCache;
33
+ }
34
+ export interface ProcessSankeyLayoutConfig {
35
+ bands: ProcessSankeyBandSpec[];
36
+ ribbons: ProcessSankeyRibbonSpec[];
37
+ /** Optional dim opacity for unselected bands/ribbons (linkedHover). */
38
+ showLabels?: boolean;
39
+ }
40
+ /**
41
+ * Marker attached to scene-edge datums so the HOC's `tooltipContent`
42
+ * can route node bands vs. flow ribbons through different default
43
+ * bodies. `data` still carries the user's original node/edge datum.
44
+ */
45
+ export interface SceneDatumPayload {
46
+ __kind: "band" | "ribbon";
47
+ /** Original node/edge record, as the user pushed it. */
48
+ data: Datum;
49
+ /** Stable id for hit-deduplication and ref operations. */
50
+ id: string;
51
+ }
52
+ export declare const emitProcessSankeyScenes: NetworkCustomLayout<ProcessSankeyLayoutConfig>;
53
+ /**
54
+ * Test whether an arbitrary HoverData/datum-shaped value carries the
55
+ * ProcessSankey scene marker. Lets the HOC's tooltipContent narrow to
56
+ * the band/ribbon variants without leaking the marker key everywhere.
57
+ */
58
+ export declare function isProcessSankeyScenePayload(d: unknown): d is SceneDatumPayload;
@@ -0,0 +1,41 @@
1
+ import type { ProcessSankeyNodeData } from "./algorithm";
2
+ export interface MassHistoryRow {
3
+ t: number;
4
+ total: number;
5
+ }
6
+ export interface MassHistoryRowMarked extends MassHistoryRow {
7
+ /** "min" | "q25" | "median" | "q75" | "max" — present only when the
8
+ * full series was condensed via `pickMassQuantiles`. */
9
+ mark?: string;
10
+ }
11
+ /**
12
+ * Distinct (time, total-mass) tuples drawn from a node's sample series.
13
+ * The same `(t, total)` pair never appears twice — the layout's
14
+ * same-time pre/post sample collapse is preserved here. Returns an
15
+ * empty array when `data` has no samples (or is undefined).
16
+ */
17
+ export declare function massHistoryRows(data: ProcessSankeyNodeData | undefined): MassHistoryRow[];
18
+ /** Number of quantile picks emitted on truncation. Fixed at five —
19
+ * `min`, `q25`, `median`, `q75`, `max` — because the picks are
20
+ * semantic (named labels), not cap-driven. The `truncateAt` parameter
21
+ * on `pickMassQuantiles` controls *when* to truncate, not how many to
22
+ * return. */
23
+ export declare const QUANTILE_PICK_COUNT = 5;
24
+ /**
25
+ * Condense a row series down to the five mass-quantile picks —
26
+ * `min`, `q25`, `median`, `q75`, `max` — re-sorted by time so the
27
+ * tooltip table reads chronologically. Returns the input unchanged
28
+ * when its length is at or below `truncateAt` (default 5).
29
+ *
30
+ * Same-time collisions are deduplicated, so very small or very flat
31
+ * series may yield fewer than five output rows even when truncation
32
+ * fires; the marks attached are the first ones encountered for each
33
+ * surviving timestamp, which keeps `min` and `max` stable when ties
34
+ * are present.
35
+ *
36
+ * Note: `truncateAt` is the *trigger threshold*, not the output cap —
37
+ * once truncation fires, the output always emits the five quantile
38
+ * picks. To raise/lower the threshold without changing the picks,
39
+ * pass a different `truncateAt`; to vary the picks, fork this util.
40
+ */
41
+ export declare function pickMassQuantiles(rows: MassHistoryRow[], truncateAt?: number): MassHistoryRowMarked[];
@@ -5,6 +5,7 @@ import type { LegendInteractionMode } from "../shared/hooks";
5
5
  import type { BaseChartProps, ChartAccessor, CategoryFormatFn } from "../shared/types";
6
6
  import { type TooltipProp } from "../../Tooltip/Tooltip";
7
7
  import type { RealtimeFrameHandle } from "../../realtime/types";
8
+ import { type RegressionProp } from "../shared/regressionUtils";
8
9
  /**
9
10
  * BarChart component props
10
11
  */
@@ -90,6 +91,17 @@ export interface BarChartProps<TDatum extends Datum = Datum> extends BaseChartPr
90
91
  legendPosition?: "right" | "left" | "top" | "bottom";
91
92
  tooltip?: TooltipProp;
92
93
  annotations?: Datum[];
94
+ /**
95
+ * Overlay a regression line through the bar tops. Accepts `true`
96
+ * (linear), a method name (`"linear"` | `"polynomial"` | `"loess"`),
97
+ * or a full `RegressionConfig`. The regression treats categories
98
+ * as a numeric category-index for fit input; pixel positions are
99
+ * resolved through the band scale, so the line passes through bar
100
+ * centers. Best for ordered categories (months, quarters, score
101
+ * buckets) where a slope reads as a meaningful trend. Sugar over
102
+ * the `trend` annotation.
103
+ */
104
+ regression?: RegressionProp;
93
105
  /** Custom formatter for category tick labels */
94
106
  categoryFormat?: CategoryFormatFn;
95
107
  /** Fixed value-axis domain `[min, max]`. Either bound may be `undefined` to leave that side data-derived. */
@@ -5,6 +5,7 @@ import type { LegendInteractionMode, LegendPosition } from "../shared/hooks";
5
5
  import type { BaseChartProps, ChartAccessor, CategoryFormatFn } from "../shared/types";
6
6
  import { type TooltipProp } from "../../Tooltip/Tooltip";
7
7
  import type { RealtimeFrameHandle } from "../../realtime/types";
8
+ import { type RegressionProp } from "../shared/regressionUtils";
8
9
  export interface DotPlotProps<TDatum extends Datum = Datum> extends BaseChartProps {
9
10
  data?: TDatum[];
10
11
  categoryAccessor?: ChartAccessor<TDatum, string>;
@@ -34,6 +35,14 @@ export interface DotPlotProps<TDatum extends Datum = Datum> extends BaseChartPro
34
35
  legendPosition?: LegendPosition;
35
36
  tooltip?: TooltipProp;
36
37
  annotations?: Datum[];
38
+ /**
39
+ * Overlay a regression line through the dots. Same shape as
40
+ * Scatterplot's regression prop — accepts boolean, method-string,
41
+ * or full `RegressionConfig`. Pixel positions resolve through the
42
+ * band scale, so the line passes through dot centers. Sugar over
43
+ * the `trend` annotation.
44
+ */
45
+ regression?: RegressionProp;
37
46
  /** Custom formatter for category tick labels */
38
47
  categoryFormat?: CategoryFormatFn;
39
48
  /** Fixed value-axis domain `[min, max]`. Either bound may be `undefined` to leave that side data-derived. */