semiotic 3.7.4 → 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 (135) hide show
  1. package/CLAUDE.md +10 -7
  2. package/README.md +36 -19
  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/ai/chartCapabilityTypes.d.ts +16 -0
  8. package/dist/components/ai/dataPitfallsBridge.d.ts +101 -0
  9. package/dist/components/ai/dataQualityBridge.d.ts +201 -0
  10. package/dist/components/ai/describeChart.d.ts +2 -0
  11. package/dist/components/ai/generativeChart.d.ts +145 -0
  12. package/dist/components/charts/custom/NetworkCustomChart.d.ts +14 -1
  13. package/dist/components/charts/ordinal/SwarmPlot.d.ts +7 -0
  14. package/dist/components/charts/realtime/RealtimeLineChart.d.ts +21 -0
  15. package/dist/components/charts/realtime/aggregate.d.ts +51 -0
  16. package/dist/components/charts/realtime/eventTime.d.ts +26 -0
  17. package/dist/components/charts/shared/colorPalettes.d.ts +2 -0
  18. package/dist/components/charts/shared/colorUtils.d.ts +1 -12
  19. package/dist/components/charts/shared/minMax.d.ts +3 -0
  20. package/dist/components/charts/shared/radialGeometry.d.ts +0 -18
  21. package/dist/components/charts/shared/smartTooltip.d.ts +27 -0
  22. package/dist/components/charts/shared/statisticalOverlays.d.ts +35 -0
  23. package/dist/components/charts/shared/tooltipUtils.d.ts +2 -0
  24. package/dist/components/charts/shared/useChartSetup.d.ts +1 -1
  25. package/dist/components/charts/shared/useCustomChartSetup.d.ts +1 -1
  26. package/dist/components/charts/shared/validateChartData.d.ts +1 -1
  27. package/dist/components/charts/xy/Scatterplot.d.ts +9 -0
  28. package/dist/components/data/fromArrow.d.ts +54 -0
  29. package/dist/components/data/fromMermaid.d.ts +47 -0
  30. package/dist/components/data/fromObservablePlot.d.ts +69 -0
  31. package/dist/components/data/portability/index.d.ts +14 -0
  32. package/dist/components/data/portability/spec.d.ts +129 -0
  33. package/dist/components/data/portability/vegaLite.d.ts +62 -0
  34. package/dist/components/realtime/ReorderBuffer.d.ts +54 -0
  35. package/dist/components/realtime/RunningStats.d.ts +66 -0
  36. package/dist/components/realtime/WindowAccumulator.d.ts +105 -0
  37. package/dist/components/realtime/parseWindowDuration.d.ts +12 -0
  38. package/dist/components/recipes/customTooltip.d.ts +31 -0
  39. package/dist/components/recipes/gofish.d.ts +207 -0
  40. package/dist/components/recipes/gofishBoba.d.ts +23 -0
  41. package/dist/components/recipes/gofishIR.d.ts +169 -0
  42. package/dist/components/recipes/gofishIRExamples.d.ts +89 -0
  43. package/dist/components/recipes/gofishInterpreter.d.ts +52 -0
  44. package/dist/components/recipes/gofishLambdas.d.ts +145 -0
  45. package/dist/components/recipes/mermaidDag.d.ts +44 -0
  46. package/dist/components/recipes/packedClusterMatrix.d.ts +166 -0
  47. package/dist/components/recipes/recipeChrome.d.ts +100 -0
  48. package/dist/components/recipes/recipeLegend.d.ts +57 -0
  49. package/dist/components/recipes/recipeUtils.d.ts +105 -0
  50. package/dist/components/semiotic-ai.d.ts +6 -0
  51. package/dist/components/semiotic-data.d.ts +2 -0
  52. package/dist/components/semiotic-experimental.d.ts +20 -0
  53. package/dist/components/semiotic-network.d.ts +3 -1
  54. package/dist/components/semiotic-ordinal.d.ts +2 -0
  55. package/dist/components/semiotic-realtime.d.ts +9 -1
  56. package/dist/components/semiotic-recipes.d.ts +18 -1
  57. package/dist/components/semiotic-themes.d.ts +2 -0
  58. package/dist/components/semiotic-xy.d.ts +2 -0
  59. package/dist/components/semiotic.d.ts +4 -0
  60. package/dist/components/store/ObservationStore.d.ts +21 -2
  61. package/dist/components/store/SelectionStore.d.ts +1 -1
  62. package/dist/components/store/ThemeStore.d.ts +1 -1
  63. package/dist/components/store/TooltipStore.d.ts +1 -1
  64. package/dist/components/store/createStore.d.ts +1 -1
  65. package/dist/components/store/designTokens.d.ts +15 -0
  66. package/dist/components/stream/NetworkHtmlMarksLayer.d.ts +44 -0
  67. package/dist/components/stream/NetworkPipelineStore.d.ts +32 -0
  68. package/dist/components/stream/NetworkSVGOverlay.d.ts +12 -0
  69. package/dist/components/stream/OrdinalPipelineStore.d.ts +18 -0
  70. package/dist/components/stream/PipelineStore.d.ts +27 -0
  71. package/dist/components/stream/SceneGraph.d.ts +5 -1
  72. package/dist/components/stream/customLayout.d.ts +20 -0
  73. package/dist/components/stream/customLayoutDiagnostics.d.ts +11 -0
  74. package/dist/components/stream/customLayoutSelection.d.ts +52 -0
  75. package/dist/components/stream/networkCustomLayout.d.ts +66 -1
  76. package/dist/components/stream/networkTypes.d.ts +29 -1
  77. package/dist/components/stream/ordinalCustomLayout.d.ts +17 -1
  78. package/dist/components/stream/ordinalSceneBuilders/types.d.ts +2 -0
  79. package/dist/components/stream/ordinalTypes.d.ts +19 -3
  80. package/dist/components/stream/renderers/networkSymbolRenderer.d.ts +2 -0
  81. package/dist/components/stream/renderers/symbolCanvasRenderer.d.ts +2 -0
  82. package/dist/components/stream/stalenessBands.d.ts +28 -0
  83. package/dist/components/stream/symbolPath.d.ts +24 -0
  84. package/dist/components/stream/types.d.ts +72 -3
  85. package/dist/components/stream/useStalenessCheck.d.ts +6 -1
  86. package/dist/components/stream/xySceneBuilders/pointScene.d.ts +2 -2
  87. package/dist/components/stream/xySceneBuilders/types.d.ts +5 -0
  88. package/dist/geo.min.js +1 -1
  89. package/dist/geo.module.min.js +1 -1
  90. package/dist/network.min.js +1 -1
  91. package/dist/network.module.min.js +1 -1
  92. package/dist/ordinal.min.js +1 -1
  93. package/dist/ordinal.module.min.js +1 -1
  94. package/dist/realtime.min.js +1 -1
  95. package/dist/realtime.module.min.js +1 -1
  96. package/dist/semiotic-ai-semiotic-ai-u954ylUn.js +2 -0
  97. package/dist/semiotic-ai-statisticalOverlays-CU7jW05Q.js +1 -0
  98. package/dist/semiotic-ai.d.ts +6 -0
  99. package/dist/semiotic-ai.min.js +1 -1
  100. package/dist/semiotic-ai.module.min.js +1 -1
  101. package/dist/semiotic-data.d.ts +2 -0
  102. package/dist/semiotic-data.min.js +1 -1
  103. package/dist/semiotic-data.module.min.js +1 -1
  104. package/dist/semiotic-experimental.d.ts +20 -0
  105. package/dist/semiotic-experimental.min.js +1 -0
  106. package/dist/semiotic-experimental.module.min.js +1 -0
  107. package/dist/semiotic-network.d.ts +3 -1
  108. package/dist/semiotic-ordinal.d.ts +2 -0
  109. package/dist/semiotic-realtime.d.ts +9 -1
  110. package/dist/semiotic-recipes.d.ts +18 -1
  111. package/dist/semiotic-recipes.min.js +1 -1
  112. package/dist/semiotic-recipes.module.min.js +1 -1
  113. package/dist/semiotic-semiotic-BmrYbi99.js +2 -0
  114. package/dist/semiotic-statisticalOverlays-IjZw8Lsu.js +1 -0
  115. package/dist/semiotic-themes.d.ts +2 -0
  116. package/dist/semiotic-themes.min.js +1 -1
  117. package/dist/semiotic-themes.module.min.js +1 -1
  118. package/dist/semiotic-utils.min.js +1 -1
  119. package/dist/semiotic-utils.module.min.js +1 -1
  120. package/dist/semiotic-value.min.js +1 -1
  121. package/dist/semiotic-value.module.min.js +1 -1
  122. package/dist/semiotic-xy.d.ts +2 -0
  123. package/dist/semiotic.d.ts +4 -0
  124. package/dist/semiotic.min.js +1 -1
  125. package/dist/semiotic.module.min.js +1 -1
  126. package/dist/server.min.js +1 -1
  127. package/dist/server.module.min.js +1 -1
  128. package/dist/xy-semiotic-xy-Dufu3D0-.js +2 -0
  129. package/dist/xy-statisticalOverlays-3Ni9bRph.js +1 -0
  130. package/dist/xy.min.js +1 -1
  131. package/dist/xy.module.min.js +1 -1
  132. package/package.json +11 -3
  133. package/dist/semiotic-ai-statisticalOverlays-C2PPlmXv.js +0 -1
  134. package/dist/semiotic-statisticalOverlays-C3DsOgr_.js +0 -1
  135. package/dist/xy-statisticalOverlays-C2PPlmXv.js +0 -1
@@ -0,0 +1,145 @@
1
+ import type { Datum } from "../charts/shared/datumTypes";
2
+ /**
3
+ * GoFish escape-hatch lambda registry.
4
+ *
5
+ * GoFish's serialized IR cannot carry function bodies, so `derive` (a
6
+ * `(rows) => rows'` data transform) and `mark-fn` (a `(datum) => drawing`
7
+ * custom glyph) travel as a `lambdaId` that the host resolves through a
8
+ * bridge. This module is that bridge for the Semiotic interpreter: register a
9
+ * function under the id the IR references and `interpretGofishIR` will call it
10
+ * where the grammar genuinely can't reach.
11
+ *
12
+ * This is the *only* sanctioned non-grammar path. A spec that resolves to pure
13
+ * operators + marks needs no lambdas; one that reaches a `derive`/`mark-fn`
14
+ * names the computation here.
15
+ */
16
+ export interface DeriveLambda {
17
+ kind: "derive";
18
+ /** Transform the in-scope rows — add columns, or emit new rows. */
19
+ fn: (rows: Datum[]) => Datum[];
20
+ }
21
+ export interface MarkFnLambda {
22
+ kind: "mark-fn";
23
+ /**
24
+ * Draw a bespoke glyph for one datum inside its allocated unit frame
25
+ * (coordinates in [0,1], mapped to pixels by the interpreter). Returns
26
+ * primitive marks the interpreter splices into the scene.
27
+ */
28
+ fn: (datum: Datum, frame: {
29
+ x: number;
30
+ y: number;
31
+ w: number;
32
+ h: number;
33
+ }) => GofishLambdaPrimitive[];
34
+ }
35
+ export type GofishLambda = DeriveLambda | MarkFnLambda;
36
+ /** A primitive a `mark-fn` may emit (unit coords). Mirrors the glyph vocabulary. */
37
+ export type GofishLambdaPrimitive = {
38
+ kind: "polygon";
39
+ points: Array<[number, number]>;
40
+ fill?: string;
41
+ stroke?: string;
42
+ strokeWidth?: number;
43
+ opacity?: number;
44
+ } | {
45
+ kind: "circle";
46
+ x: number;
47
+ y: number;
48
+ r: number;
49
+ fill?: string;
50
+ stroke?: string;
51
+ opacity?: number;
52
+ } | {
53
+ kind: "rect";
54
+ x: number;
55
+ y: number;
56
+ w: number;
57
+ h: number;
58
+ fill?: string;
59
+ stroke?: string;
60
+ opacity?: number;
61
+ rotate?: number;
62
+ } | {
63
+ kind: "text";
64
+ x: number;
65
+ y: number;
66
+ text: string;
67
+ fill?: string;
68
+ fontSize?: number;
69
+ };
70
+ /** Register a `derive`/`mark-fn` implementation under the id the IR references. */
71
+ export declare function registerGofishLambda(id: string, lambda: GofishLambda): () => void;
72
+ export declare function unregisterGofishLambda(id: string): void;
73
+ export declare function getGofishLambda(id: string): GofishLambda | undefined;
74
+ /** Merge a per-call lambda map over the module registry (per-call wins). */
75
+ export declare function resolveLambda(id: string, perCall?: Record<string, GofishLambda>): GofishLambda | undefined;
76
+ export interface BobaCellGeometry {
77
+ cup: Array<[number, number]>;
78
+ tea: Array<[number, number]> | null;
79
+ straw: Array<[number, number]> | null;
80
+ /** Thick rim/lid bar across the cup top. */
81
+ lid: {
82
+ x1: number;
83
+ x2: number;
84
+ y: number;
85
+ };
86
+ pearls: Array<{
87
+ x: number;
88
+ y: number;
89
+ r: number;
90
+ fill: string;
91
+ }>;
92
+ ice: Array<{
93
+ x: number;
94
+ y: number;
95
+ w: number;
96
+ rot: number;
97
+ fill: string;
98
+ }>;
99
+ /** Content box `[bw, bh]` for the `uniform` unit fit; shared across the menu. */
100
+ box: [number, number];
101
+ numBobas: number;
102
+ numIce: number;
103
+ teaVolume: number;
104
+ bobaVolume: number;
105
+ iceVolume: number;
106
+ totalVolume: number;
107
+ cupStroke: string;
108
+ }
109
+ /** Single-cup convenience (normalizes against itself). */
110
+ export declare function computeBobaCellGeometry(row: Datum): BobaCellGeometry;
111
+ /**
112
+ * The built-in `bobaGeometry` derive: solve every cup, then attach `_g`
113
+ * (geometry against the shared menu-wide normalization) plus the fixed fills
114
+ * the marks read.
115
+ */
116
+ export declare const bobaGeometryLambda: DeriveLambda;
117
+ export interface BottleCellGeometry {
118
+ silhouette: Array<[number, number]>;
119
+ imageHref: string;
120
+ fill: {
121
+ x: number;
122
+ y: number;
123
+ w: number;
124
+ h: number;
125
+ };
126
+ fillLine: {
127
+ x1: number;
128
+ x2: number;
129
+ y: number;
130
+ };
131
+ pct: {
132
+ x: number;
133
+ y: number;
134
+ text: string;
135
+ };
136
+ label: {
137
+ x: number;
138
+ y: number;
139
+ text: string;
140
+ };
141
+ fillFill: string;
142
+ }
143
+ export declare function computeBottleCellGeometry(row: Datum): BottleCellGeometry;
144
+ /** The built-in `bottleGeometry` derive: attach `_b` (normalized geometry) per bottle. */
145
+ export declare const bottleGeometryLambda: DeriveLambda;
@@ -0,0 +1,44 @@
1
+ import type { NetworkCustomLayout } from "../stream/networkCustomLayout";
2
+ /**
3
+ * Layered flowchart layout for the Mermaid adapter (and any pre-layered DAG).
4
+ *
5
+ * A Mermaid flowchart is a directed acyclic graph, not a force graph — so it
6
+ * should render as a *layered diagram* with shape-appropriate node glyphs,
7
+ * directional arrows, and edge labels, the way Mermaid draws it. This recipe
8
+ * does exactly that from the `layer`/`row` coordinates `fromMermaid` already
9
+ * computes (longest-path layering), so it needs **no layout engine and no new
10
+ * dependency**. It ships in `semiotic/recipes`, so it tree-shakes — you only
11
+ * pull it in if you use it.
12
+ *
13
+ * For large or dense graphs where crossing-minimization matters, run a real
14
+ * Sugiyama layouter (the BYO `dagre` recipe, or `d3-dag`) to assign
15
+ * `layer`/`row`, then render with this layout — the rendering and the
16
+ * positioning are deliberately separate concerns.
17
+ *
18
+ * Pure, SSR-safe: hit-testable rects are emitted as scene nodes; all visual
19
+ * chrome (shape glyphs, labels, edges, arrowheads, edge labels) is an SVG
20
+ * `overlays` group painted on top, in plot-relative coordinates.
21
+ */
22
+ export type MermaidDirection = "TD" | "TB" | "BT" | "LR" | "RL";
23
+ export interface MermaidDagConfig {
24
+ /** Flow direction. TD/TB top-down, BT bottom-up, LR left-right, RL right-left. @default "TD" */
25
+ direction?: MermaidDirection;
26
+ /** Glyph width / height in px (shrunk to fit). @default 150 / 46 */
27
+ nodeWidth?: number;
28
+ nodeHeight?: number;
29
+ /** Node field accessors. @default "layer" / "row" / "label" / "shape" */
30
+ layerAccessor?: string;
31
+ rowAccessor?: string;
32
+ labelAccessor?: string;
33
+ shapeAccessor?: string;
34
+ /** Edge label field. @default "label" */
35
+ edgeLabelAccessor?: string;
36
+ /** Colors (CSS vars by default so they follow the theme). */
37
+ nodeFill?: string;
38
+ nodeStroke?: string;
39
+ textColor?: string;
40
+ edgeColor?: string;
41
+ /** Stroke for decision (diamond) glyphs. */
42
+ accentColor?: string;
43
+ }
44
+ export declare const mermaidDagLayout: NetworkCustomLayout<MermaidDagConfig>;
@@ -0,0 +1,166 @@
1
+ import type { NetworkCustomLayout } from "../stream/networkCustomLayout";
2
+ import type { NetworkSymbolName } from "../stream/symbolPath";
3
+ /**
4
+ * Config for {@link packedClusterMatrix}. Accessors read `node.data.<field>`
5
+ * (the ingest wrapper) and fall back to the node itself.
6
+ */
7
+ export interface PackedClusterMatrixConfig {
8
+ /** Categorical field → matrix column. @default "column" */
9
+ columnAccessor?: string;
10
+ /** Categorical field → matrix row (stacked within a column). @default "row" */
11
+ rowAccessor?: string;
12
+ /** Numeric field → glyph size (area-encoded). Constant size when omitted. */
13
+ sizeAccessor?: string;
14
+ /** Categorical field → glyph hue (via `ctx.resolveColor`). */
15
+ colorAccessor?: string;
16
+ /** Fixed hue per category value. Wins over `ctx.resolveColor` for mapped keys —
17
+ * the right choice for editorial graphics with a semantic palette. */
18
+ colorMap?: Record<string, string>;
19
+ /** Categorical field → glyph shape. Auto-assigned shapes when no `symbolMap`. */
20
+ symbolAccessor?: string;
21
+ /** Numeric/date field → lightness shade of the hue (the fourth channel). */
22
+ shadeAccessor?: string;
23
+ /** Boolean field → small center dot on the glyph (e.g. a flagged subset). */
24
+ markerAccessor?: string;
25
+ /** Explicit column order; otherwise columns sort by count, largest first. */
26
+ columnOrder?: string[];
27
+ /** Explicit row order **bottom→top**; otherwise rows sort by count, largest at bottom. */
28
+ rowOrder?: string[];
29
+ /** Map a class value to a named glyph (the base mark BECOMES that shape).
30
+ * Unmapped values get auto-assigned. Mutually exclusive with `iconAccessor`. */
31
+ symbolMap?: Record<string, NetworkSymbolName>;
32
+ /** Shape when no `symbolAccessor` is set. @default "circle" */
33
+ defaultSymbol?: NetworkSymbolName;
34
+ /** Categorical field whose value selects an OPTIONAL stroked icon drawn inside
35
+ * the (filled circle) base mark — the "most marks are plain circles, a few
36
+ * carry an icon" model. Only values present in `iconMap` get an icon. */
37
+ iconAccessor?: string;
38
+ /** Map an icon-field value to a glyph. Values omitted here render as a plain
39
+ * circle (no icon) — so a single dominant class stays unmarked. */
40
+ iconMap?: Record<string, NetworkSymbolName>;
41
+ /** Stroke color of the inner icon. @default "rgba(255,255,255,0.92)" */
42
+ iconColor?: string;
43
+ /** Inner-icon stroke width. @default 1 */
44
+ iconStrokeWidth?: number;
45
+ /** Inner-icon size as a fraction of the base circle radius. @default 0.72 */
46
+ iconScale?: number;
47
+ /** Glyph radius range in px (area = πr²). @default [2, 8] */
48
+ sizeRange?: [number, number];
49
+ /** Size value domain. @default data extent */
50
+ sizeDomain?: [number, number];
51
+ /** Cap total glyph area at this fraction of the plot; radii scale down to fit
52
+ * (keeps clusters packable / non-overlapping on smaller canvases). @default 0.4 */
53
+ maxAreaFraction?: number;
54
+ /** How far the shade travels from the base hue (0..1). @default 0.72 */
55
+ shadeStrength?: number;
56
+ /** Shade value domain. @default data extent (dates auto-parsed) */
57
+ shadeDomain?: [number, number];
58
+ /** Flip the shade ramp (default: low → light, high → dark). */
59
+ shadeReverse?: boolean;
60
+ /** Row layout. `"banded"`: orbit rows are aligned global bands (one y-range
61
+ * spanning all columns → row labels align, one enclosure per band spans the
62
+ * columns, a column is only as tall as its highest occupied band). `"stacked"`:
63
+ * each column stacks its own rows independently (cell height ∝ that column's
64
+ * count; rows do NOT align). @default "banded" */
65
+ rowMode?: "banded" | "stacked";
66
+ /** `"proportional"` (mosaic: cell area ∝ count) or `"uniform"` grid. @default "proportional" */
67
+ cellSizing?: "proportional" | "uniform";
68
+ /** Exponent applied to counts when sizing columns/cells. `1` = strict area ∝
69
+ * count; `<1` softens the range so small columns stay legible. @default 1 */
70
+ proportionExponent?: number;
71
+ /** Gap between columns in px. @default 16 */
72
+ columnGap?: number;
73
+ /** Gap between stacked cells in px. @default 12 */
74
+ rowGap?: number;
75
+ /** Collision padding between glyphs in px. @default 1 */
76
+ packPadding?: number;
77
+ /** Relaxation passes for the packing (capped at 14; a near-uniform seed needs
78
+ * few). Higher = tidier/slower. @default 12 */
79
+ iterations?: number;
80
+ /** Draw a rounded enclosure hugging each cluster. @default true */
81
+ showEnclosures?: boolean;
82
+ enclosureColor?: string;
83
+ enclosureRadius?: number;
84
+ enclosureOpacity?: number;
85
+ enclosureWidth?: number;
86
+ /** Column header labels across the top. @default true */
87
+ showColumnHeaders?: boolean;
88
+ /** Row labels down the left. @default true */
89
+ showRowLabels?: boolean;
90
+ columnLabel?: (col: string) => string;
91
+ rowLabel?: (row: string) => string;
92
+ headerColor?: string;
93
+ labelColor?: string;
94
+ headerFontSize?: number;
95
+ labelFontSize?: number;
96
+ /** Top space reserved for headers. @default 26 */
97
+ headerHeight?: number;
98
+ /** Left space reserved for row labels. @default 66 */
99
+ labelWidth?: number;
100
+ /** Bottom margin reserved for callout labels (only when `callouts` is set). @default 30 */
101
+ footerHeight?: number;
102
+ markStroke?: string;
103
+ markStrokeWidth?: number;
104
+ /** Center-dot color for `markerAccessor` hits. @default "#ffffff" */
105
+ markerColor?: string;
106
+ /** Dim every glyph that doesn't match the highlight. A single `{field,value}`
107
+ * or an array (AND — a glyph must match all to stay lit, e.g. a region×orbit
108
+ * cell). Drives legend / nav-tree highlighting. */
109
+ highlight?: {
110
+ field: string;
111
+ value: string;
112
+ } | {
113
+ field: string;
114
+ value: string;
115
+ }[] | null;
116
+ /** Opacity for dimmed glyphs. @default 0.16 */
117
+ dimOpacity?: number;
118
+ /** Editorial leader-line callouts to specific marks. Each picks the first mark
119
+ * whose `datum[field] === value` and draws a ring + connector + label. `at`
120
+ * places the label: a y in [0,1] of the plot ("1" = bottom) and the label
121
+ * tracks the mark's x, or an explicit `{x,y}` in plot px. */
122
+ callouts?: Array<{
123
+ field: string;
124
+ value: string;
125
+ label: string;
126
+ at?: number | {
127
+ x: number;
128
+ y: number;
129
+ };
130
+ connector?: "straight" | "elbow" | "curve";
131
+ }>;
132
+ calloutColor?: string;
133
+ }
134
+ /**
135
+ * `packedClusterMatrix` — a reusable network layout that arranges a flat set of
136
+ * records into a **matrix of densely-packed clusters**: one column per value of
137
+ * `columnAccessor`, cells stacked within a column per value of `rowAccessor`,
138
+ * each cell a beeswarm of variable-radius glyphs. In `"proportional"` sizing the
139
+ * column widths and cell heights track counts (a mosaic), so a cell's area is
140
+ * proportional to its record count and packing density stays roughly uniform.
141
+ *
142
+ * Each record renders as a **four-channel glyph**: shape (`symbolAccessor`),
143
+ * hue (`colorAccessor`), size (`sizeAccessor`, area-encoded), and lightness
144
+ * shade (`shadeAccessor`). Packing is deterministic (seeded `d3-force`) and the
145
+ * geometry is cached, so hover/selection/filter re-runs re-style without
146
+ * re-packing. Built to recreate small-multiples beeswarm graphics (e.g. Nadieh
147
+ * Bremer's "Satellites in Space").
148
+ *
149
+ * @example
150
+ * ```tsx
151
+ * import { NetworkCustomChart } from "semiotic/network"
152
+ * import { packedClusterMatrix } from "semiotic/recipes"
153
+ *
154
+ * <NetworkCustomChart
155
+ * nodes={satellites} // each: { id, region, orbit, mass, category, klass, launch }
156
+ * layout={packedClusterMatrix}
157
+ * colorScheme={categoryColors}
158
+ * layoutConfig={{
159
+ * columnAccessor: "region", rowAccessor: "orbit",
160
+ * sizeAccessor: "mass", colorAccessor: "category",
161
+ * symbolAccessor: "klass", shadeAccessor: "launch",
162
+ * }}
163
+ * />
164
+ * ```
165
+ */
166
+ export declare const packedClusterMatrix: NetworkCustomLayout<PackedClusterMatrixConfig>;
@@ -0,0 +1,100 @@
1
+ import type { ReactElement, ReactNode } from "react";
2
+ /**
3
+ * Recipe chrome kit — small, framework-level building blocks for the decoration
4
+ * that custom-layout recipes draw in their `overlays` layer: group enclosures,
5
+ * band/axis labels, and leader-line callouts to a mark.
6
+ *
7
+ * These were each hand-rolled per recipe (packedClusterMatrix drew enclosures,
8
+ * marimekko/bullet/parallelCoordinates placed band labels, the satellites
9
+ * graphic needed callouts to specific marks). Centralizing them keeps chrome
10
+ * consistent and lets new recipes opt in instead of re-deriving the geometry.
11
+ *
12
+ * All return plain SVG React elements with `pointerEvents: "none"`, so they
13
+ * decorate without intercepting canvas hit-testing. Pure / SSR-safe.
14
+ */
15
+ export interface RoundedEnclosureProps {
16
+ x: number;
17
+ y: number;
18
+ width: number;
19
+ height: number;
20
+ /** Corner radius. @default 10 */
21
+ radius?: number;
22
+ stroke?: string;
23
+ /** @default 1.5 */
24
+ strokeWidth?: number;
25
+ /** @default 0.6 */
26
+ opacity?: number;
27
+ /** @default "none" */
28
+ fill?: string;
29
+ strokeDasharray?: string;
30
+ className?: string;
31
+ keyId?: string | number;
32
+ }
33
+ /** A rounded rectangle around a group of marks (a cell, a row/column band, a cluster). */
34
+ export declare function roundedEnclosure(p: RoundedEnclosureProps): ReactElement;
35
+ /** Bounding box of a set of points (each optionally a circle of radius `r`), padded.
36
+ * The usual precursor to {@link roundedEnclosure}. Returns null for an empty set. */
37
+ export declare function boundsOf(points: ReadonlyArray<{
38
+ x: number;
39
+ y: number;
40
+ r?: number;
41
+ }>, pad?: number): {
42
+ x: number;
43
+ y: number;
44
+ width: number;
45
+ height: number;
46
+ } | null;
47
+ export interface BandLabelProps {
48
+ text: string;
49
+ x: number;
50
+ y: number;
51
+ /** @default "middle" */
52
+ anchor?: "start" | "middle" | "end";
53
+ /** @default "middle" */
54
+ baseline?: "middle" | "hanging" | "auto";
55
+ /** Hide the label if its estimated width exceeds this (px). */
56
+ maxWidth?: number;
57
+ /** @default 12 */
58
+ fontSize?: number;
59
+ fontWeight?: number | string;
60
+ color?: string;
61
+ className?: string;
62
+ keyId?: string | number;
63
+ }
64
+ /** A label placed at a band/axis position, suppressed when it would overflow
65
+ * `maxWidth` (the dedup pattern marimekko/parallelCoordinates hand-roll). */
66
+ export declare function bandLabel(p: BandLabelProps): ReactElement | null;
67
+ export type CalloutConnector = "straight" | "elbow" | "curve";
68
+ export interface MarkCalloutProps {
69
+ /** The anchored mark (a position a custom layout emitted). */
70
+ markX: number;
71
+ markY: number;
72
+ /** Where the label text sits. */
73
+ labelX: number;
74
+ labelY: number;
75
+ label: ReactNode;
76
+ /** @default "straight" */
77
+ connector?: CalloutConnector;
78
+ /** Draw a ring of this radius around the mark (editorial highlight). */
79
+ markRadius?: number;
80
+ stroke?: string;
81
+ /** @default 1 */
82
+ strokeWidth?: number;
83
+ color?: string;
84
+ /** @default 11 */
85
+ fontSize?: number;
86
+ fontWeight?: number | string;
87
+ /** text-anchor for the label. @default "middle" */
88
+ labelAnchor?: "start" | "middle" | "end";
89
+ /** @default "hanging" */
90
+ labelBaseline?: "middle" | "hanging" | "auto";
91
+ className?: string;
92
+ keyId?: string | number;
93
+ }
94
+ /**
95
+ * A leader line connecting a label to a mark, with an optional ring around the
96
+ * mark — the editorial-callout pattern. Custom-layout recipes use it because
97
+ * the annotation system can't anchor to a mark a layout emits at runtime; the
98
+ * recipe knows the packed position and can call this directly.
99
+ */
100
+ export declare function markCallout(p: MarkCalloutProps): ReactElement;
@@ -0,0 +1,57 @@
1
+ import type { LegendGroup } from "../types/legendTypes";
2
+ import { type NetworkSymbolName } from "../stream/symbolPath";
3
+ /**
4
+ * `legendGroupsFrom` — build the `LegendGroup[]` a custom-layout recipe passes
5
+ * through `frameProps.legend` (`{ legendGroups }`). All three custom-chart HOCs
6
+ * disable the built-in legend because the *layout* owns color/shape/size; without
7
+ * this, authors hand-roll the `LegendGroup` shape (its `styleFn`, custom-glyph
8
+ * `type` render functions, swatch sizing). This closes that gap without the frame
9
+ * having to guess the author's encoding — the recipe declares each channel and
10
+ * gets back a ready-to-render array.
11
+ *
12
+ * One group is emitted per provided channel, in order: **color → symbol → size**.
13
+ * Pure / SSR-safe.
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * <NetworkCustomChart
18
+ * layout={packedClusterMatrix}
19
+ * frameProps={{ legend: { legendGroups: legendGroupsFrom({
20
+ * colorMap: CATEGORY_COLORS, colorLabel: "Category",
21
+ * symbolMap: CLASS_SHAPES, symbolLabel: "Class",
22
+ * sizeStops: [200, 2000, 8000], sizeRadius: r, sizeLabel: "Mass (kg)",
23
+ * }) } }}
24
+ * />
25
+ * ```
26
+ */
27
+ export interface LegendGroupsInput {
28
+ /** `{category → color}` map → one swatch per entry, in insertion order. */
29
+ colorMap?: Record<string, string>;
30
+ /** Or explicit category keys, with `color` resolving each to a color. */
31
+ keys?: string[];
32
+ /** Resolve a key → color (e.g. a recipe's `ctx.resolveColor`). Used with `keys`. */
33
+ color?: (key: string) => string;
34
+ /** Color-group heading. @default "" */
35
+ colorLabel?: string;
36
+ /** Color swatch glyph — a filled square or a line stroke. @default "fill" */
37
+ colorType?: "fill" | "line";
38
+ /** `{category → glyph shape}` map → one shape swatch per entry. */
39
+ symbolMap?: Record<string, NetworkSymbolName>;
40
+ /** Fill for the shape swatches (shape, not color, is the encoding here).
41
+ * @default "var(--semiotic-text, currentColor)" */
42
+ symbolColor?: string;
43
+ /** Symbol-group heading. @default "" */
44
+ symbolLabel?: string;
45
+ /** Reference values → circle swatches sized by the mark scale. */
46
+ sizeStops?: number[];
47
+ /** Map a value → radius (px) — pass the recipe's own radius scale so the
48
+ * legend circles match the marks exactly. Required to draw the size group. */
49
+ sizeRadius?: (v: number) => number;
50
+ /** Format a size stop for its label. @default `String` */
51
+ sizeFormat?: (v: number) => string;
52
+ /** Size swatch circle fill. @default "var(--semiotic-text, currentColor)" */
53
+ sizeColor?: string;
54
+ /** Size-group heading. @default "" */
55
+ sizeLabel?: string;
56
+ }
57
+ export declare function legendGroupsFrom(input: LegendGroupsInput): LegendGroup[];
@@ -5,6 +5,21 @@ import type { Datum } from "../charts/shared/datumTypes";
5
5
  * the boundary and lets the layout body call a single function.
6
6
  */
7
7
  export declare function resolveAccessor<T = unknown>(a: string | ((d: Datum) => T)): (d: Datum) => T;
8
+ /**
9
+ * Read a field off a network-ingest wrapper (`node.data.<key>`) or the node
10
+ * itself, with a fallback. Network recipes receive `RealtimeNode`/`RealtimeEdge`
11
+ * wrappers whose user data lives under `.data`; several recipes (lineageDag,
12
+ * mermaidDag, packedClusterMatrix, …) each re-declared this identical reader, so
13
+ * it lives here once. Returns `unknown` — narrow at the call site.
14
+ */
15
+ export declare function readField(d: unknown, key: string, fallback: unknown): unknown;
16
+ /**
17
+ * Group items into a `Map` keyed by a derived string. Insertion order is
18
+ * preserved both for keys and within each bucket — recipes rely on stable
19
+ * ordering for deterministic layout. Several recipes re-declared this; it lives
20
+ * here once.
21
+ */
22
+ export declare function groupBy<T>(items: readonly T[], key: (item: T) => string): Map<string, T[]>;
8
23
  /**
9
24
  * Build a datum object whose own-properties are safe to set from
10
25
  * user-supplied accessor names. `Object.create(null)` produces a
@@ -25,3 +40,93 @@ export declare function resolveAccessor<T = unknown>(a: string | ((d: Datum) =>
25
40
  * scattered across each recipe's inner loops.
26
41
  */
27
42
  export declare function createSafeDatum(populate: (set: (key: string, value: unknown) => void) => void): Datum;
43
+ /**
44
+ * A `{field, value}` highlight target — or an array of them, AND-combined (a
45
+ * datum must match every entry to count as a match, e.g. a region×orbit cell).
46
+ * `value` is coerced to a string before comparison; a nullish `value` matches
47
+ * anything (so `{ field, value: null }` is a no-op constraint).
48
+ */
49
+ export type HighlightMatch = {
50
+ field: string;
51
+ value: unknown;
52
+ } | ReadonlyArray<{
53
+ field: string;
54
+ value: unknown;
55
+ }>;
56
+ /**
57
+ * Whether `datum` satisfies a `{field, value}` highlight (or every entry of an
58
+ * array). A nullish/empty `highlight` matches everything — the "no highlight
59
+ * active" case. Several recipes hand-rolled this exact field/value loop.
60
+ */
61
+ export declare function matchesHighlight(datum: Datum, highlight: HighlightMatch | null | undefined): boolean;
62
+ export interface DimOptions {
63
+ /** A datum is lit only if this predicate passes (e.g. a shared-selection
64
+ * predicate, or `parallelCoordinates`'s `highlightFn`). Omit ⇒ no constraint. */
65
+ predicate?: ((d: Datum) => boolean) | null;
66
+ /** A datum is lit only if it matches this `{field, value}` (or all, for an
67
+ * array). Omit ⇒ no constraint. Composes with `predicate` (both must pass). */
68
+ highlight?: HighlightMatch | null;
69
+ /** Opacity used when no cue is active (nothing is dimmed). @default 1 */
70
+ baseOpacity?: number;
71
+ /** Opacity for marks that don't match an active cue. @default 0.16 */
72
+ dimOpacity?: number;
73
+ /** Additive opacity boost for matching marks when a cue is active, capped at 1
74
+ * (e.g. `parallelCoordinates` lifts its 0.45 line opacity by 0.4 on match). @default 0 */
75
+ brighten?: number;
76
+ }
77
+ /**
78
+ * The highlight/dim opacity rule, shared across custom-layout recipes. A mark is
79
+ * "lit" when it satisfies **every** active cue (`highlight` AND `predicate`); if
80
+ * no cue is active nothing is dimmed and every mark gets `baseOpacity`. Lit marks
81
+ * get `min(1, baseOpacity + brighten)`, dimmed marks get `dimOpacity`.
82
+ *
83
+ * This is the one rule `packedClusterMatrix` and `parallelCoordinates` each
84
+ * hand-rolled; centralizing it keeps the "matching marks stay lit, the rest dim"
85
+ * behavior identical. (Recipes whose dimming composes several cues with priority
86
+ * and rescue rules — e.g. `lineageDag`'s reach-set-over-selection — keep their
87
+ * bespoke logic; this helper is for the common AND-of-cues case.)
88
+ */
89
+ export declare function dimFor(datum: Datum, opts?: DimOptions): number;
90
+ /**
91
+ * Join layout-affecting inputs into a stable content-signature string. Pass the
92
+ * primitives that change the geometry (dimensions, gaps, orders, a per-datum
93
+ * fingerprint) — NOT styling/interaction inputs (color, shade, highlight) — so a
94
+ * re-style or a returning resize reuses a cached layout instead of recomputing it.
95
+ * Pairs with {@link LayoutCache}.
96
+ */
97
+ export declare function signatureKey(parts: ReadonlyArray<string | number | boolean | null | undefined>): string;
98
+ /**
99
+ * A tiny bounded geometry cache for expensive layouts (force packing, tree/DAG
100
+ * positioning). Key it with a {@link signatureKey} of the layout-affecting inputs
101
+ * so re-styling on interaction never re-runs the layout.
102
+ *
103
+ * **The sharp edge:** on `NetworkCustomChart`/`*CustomChart`, `ctx.nodes` is a
104
+ * *fresh array each `buildScene`* and a `layoutConfig` change re-runs the layout
105
+ * without re-ingesting topology — so cache by *content* (a fingerprint of the
106
+ * data), never by array identity. Over `maxSize` distinct keys it clears wholesale
107
+ * (these layouts churn a handful of signatures, not thousands).
108
+ */
109
+ export declare class LayoutCache<V> {
110
+ private readonly maxSize;
111
+ private store;
112
+ constructor(maxSize?: number);
113
+ get(key: string): V | undefined;
114
+ set(key: string, value: V): void;
115
+ /** Return the cached value for `key`, or compute + store it on a miss. */
116
+ getOrCompute(key: string, compute: () => V): V;
117
+ clear(): void;
118
+ get size(): number;
119
+ }
120
+ /**
121
+ * A reusable lightness shader for one base color. Interpolating in CIELAB keeps
122
+ * the hue and chroma roughly fixed while only lightness moves, so a categorical
123
+ * hue can carry a second *continuous* channel as shade (e.g. age, recency).
124
+ *
125
+ * The returned function takes `t ∈ [0,1]`: `0` lightens toward white, `1`
126
+ * darkens toward black, `0.5` returns the base. `strength` (0..1) caps how far
127
+ * each direction travels. Pure / SSR-safe — no DOM. Build once per base color
128
+ * (it captures two interpolators) and call per datum.
129
+ */
130
+ export declare function makeShade(baseColor: string, strength?: number): (t: number) => string;
131
+ /** One-shot form of {@link makeShade}: perceptual lightness shade of `baseColor`. */
132
+ export declare function shade(baseColor: string, t: number, strength?: number): string;
@@ -78,6 +78,12 @@ export { describeChart, resolveCommunicativeAct, communicativeActForIntent } fro
78
78
  export type { DescribeChartResult, DescribeChartOptions, DescribeLevel, CommunicativeAct, DescribeCapabilityContext } from "./ai/describeChart";
79
79
  export { buildReaderGrounding } from "./ai/readerGrounding";
80
80
  export type { ChartReaderGrounding, ChartReaderGroundingOptions, ChartReaderGroundingIntent } from "./ai/readerGrounding";
81
+ export { buildDataPitfallsBridge, toDataPitfallsChain } from "./ai/dataPitfallsBridge";
82
+ export type { DataPitfallsBridgeOptions, DataPitfallsBridgeResult, DataPitfallsChainInput, DataPitfallsChainStage, DataPitfallsDocumentInput, DataPitfallsImageInput, DataPitfallsImageMediaType, DataPitfallsImageSource, DataPitfallsRenderedChart, DataPitfallsSlideContent, DataPitfallsSlidesInput, DataPitfallsSingleArtifactInput, DataPitfallsTextInput } from "./ai/dataPitfallsBridge";
83
+ export { dataQualityToAnnotations, fromDbtArtifacts, fromGreatExpectations } from "./ai/dataQualityBridge";
84
+ export type { DataQualityStatus, DataQualityCheckKind, DataQualityResult, DataQualityAnnotationOptions, DataQualityAnnotationsResult, UnplacedDataQualityResult, DbtArtifacts, GEValidationResult } from "./ai/dataQualityBridge";
85
+ export { prepareChart, chartGenerationTool, toAnthropicTool, toOpenAITool, createChartToolHandler } from "./ai/generativeChart";
86
+ export type { RenderFn, PrepareChartInput, PrepareChartOptions, PrepareChartResult, ChartToolDefinition, ChartToolOptions } from "./ai/generativeChart";
81
87
  export { buildNavigationTree, flattenVisible, countNodes } from "./ai/navigationTree";
82
88
  export type { NavTreeNode, NavTreeRole, BuildNavigationTreeOptions } from "./ai/navigationTree";
83
89
  export { AccessibleNavTree } from "./AccessibleNavTree";
@@ -5,4 +5,6 @@
5
5
  export { bin, rollup, groupBy, pivot } from "./data/transforms";
6
6
  export { fromVegaLite } from "./data/fromVegaLite";
7
7
  export type { VegaLiteSpec, VegaLiteEncoding } from "./data/fromVegaLite";
8
+ export { fromArrow } from "./data/fromArrow";
9
+ export type { ArrowTableLike, ArrowColumnLike, ArrowFieldLike, ArrowSchemaLike, FromArrowOptions, } from "./data/fromArrow";
8
10
  export { mergeData } from "./geo/mergeData";