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
@@ -0,0 +1,52 @@
1
+ import type { CustomLayout } from "../stream/customLayout";
2
+ import type { OrdinalCustomLayout } from "../stream/ordinalCustomLayout";
3
+ import type { GofishGlyphMark } from "./gofish";
4
+ import type { GofishLambda } from "./gofishLambdas";
5
+ import type { GofishRootIR } from "./gofishIR";
6
+ /**
7
+ * GoFish IR interpreter.
8
+ *
9
+ * Walks a `data → operators → mark` tree and *executes* the grammar —
10
+ * `group`/`spread`/`stack`/`scatter`/`treemap`/`layer`, the `polar`/`unit`
11
+ * coordinate transforms, mark channels through scales, and `connect`/`ref`/
12
+ * `arrow` relations — emitting positioned primitives. This is a real
13
+ * translator, not a recognizer: any spec built from these constructs renders.
14
+ *
15
+ * It is NOT GoFish's full constraint solver. GoFish elaborates `spread` to
16
+ * `layer + distribute + align` over a linear-system bbox; we implement the
17
+ * deterministic allocation/accumulation model the common acyclic specs reduce
18
+ * to. Constructs outside it (`table`, `cut`, `mask`, `over`/`inside`/`xor`,
19
+ * free-form `.constrain`) record a warning and fall back, never silently
20
+ * mis-render. The two sanctioned non-grammar paths — `derive` and `mark-fn`
21
+ * lambdas — resolve through the lambda registry.
22
+ */
23
+ export interface InterpretOptions {
24
+ /** Per-call lambda overrides for `derive`/`mark-fn` (merged over the registry). */
25
+ lambdas?: Record<string, GofishLambda>;
26
+ /** Collected warnings for unsupported constructs / unresolved lambdas. */
27
+ warnings?: string[];
28
+ }
29
+ /** XY binding — the default for free-coordinate GoFish charts. */
30
+ export declare function interpretToXYLayout(root: GofishRootIR, options?: InterpretOptions): CustomLayout;
31
+ /**
32
+ * Ordinal binding — the top-level categorical `spread`/`group` maps to the
33
+ * frame's o-scale (one band per category); each category's subtree is
34
+ * interpreted within its full band column. A pictorial glyph that declares a
35
+ * `unit` coord with `fit: "uniform"` then scales aspect-correctly to fill the
36
+ * column (e.g. boba cups). Emits one data-bearing hit rect per category + the
37
+ * interpreted glyph as overlays.
38
+ *
39
+ * `config.glyphWidthRatio` (alias `cupWidthRatio`) insets the glyph column
40
+ * horizontally so neighboring glyphs breathe; default 0.9.
41
+ */
42
+ export declare function interpretToOrdinalLayout(root: GofishRootIR, categoryAccessor: string, options?: InterpretOptions): OrdinalCustomLayout;
43
+ /** Interpret a root to raw marks (for tests / custom hosts). */
44
+ export declare function interpretToMarks(root: GofishRootIR, frame: {
45
+ x: number;
46
+ y: number;
47
+ width: number;
48
+ height: number;
49
+ }, resolveColor?: (key: string) => string, options?: InterpretOptions): {
50
+ marks: GofishGlyphMark[];
51
+ warnings: string[];
52
+ };
@@ -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,129 @@
1
+ import type { ReactNode } from "react";
2
+ import type { NetworkCustomLayout } from "../stream/networkCustomLayout";
3
+ import type { Datum } from "../charts/shared/datumTypes";
4
+ /**
5
+ * Level of detail for a node glyph.
6
+ * - `full` container + icon + type label + truncated name + store-slot chips
7
+ * - `compact` container + icon + truncated name
8
+ * - `icon` container + icon only
9
+ * - `dot` a single ~5px circle (minimap density)
10
+ */
11
+ export type LineageLod = "full" | "compact" | "icon" | "dot";
12
+ export interface LineageStoreSlot {
13
+ storeName: string;
14
+ slotIndex: number;
15
+ }
16
+ export interface LineageDagConfig {
17
+ /** Number of layers (x domain). Computed from node `x` extents when omitted. */
18
+ layerCount?: number;
19
+ /** Largest layer's row count (y domain). Computed from the data when omitted. */
20
+ maxLayerSize?: number;
21
+ /** Target full-glyph width / height in px. Shrunk to fit the plot. @default 172 / 54 */
22
+ nodeWidth?: number;
23
+ nodeHeight?: number;
24
+ /** Minimum gap reserved between glyphs when fitting. @default 26 / 18 */
25
+ minGapX?: number;
26
+ minGapY?: number;
27
+ /** Force a level of detail; `"auto"` derives it from the fitted glyph size. @default "auto" */
28
+ lod?: LineageLod | "auto";
29
+ /**
30
+ * Caller-supplied "reachable" set. When present, nodes/edges **outside**
31
+ * it dim to `dimOpacity`. This is the controlled-dimming channel: the host
32
+ * computes the set (e.g. downstream BFS of the hovered node) and re-renders.
33
+ * Independent of, and composes with, `NetworkLayoutContext.selection`.
34
+ */
35
+ reachableIds?: Iterable<string> | null;
36
+ /** Currently-selected node id — drawn with the selection ring. Host-owned. */
37
+ selectedId?: string | null;
38
+ /** Opacity for dimmed (out-of-reach / unselected) marks. @default 0.14 */
39
+ dimOpacity?: number;
40
+ /** Logical layer index (0 = leftmost). @default "x" */
41
+ layerAccessor?: string;
42
+ /** Logical row offset within the layer (centered on 0). @default "y" */
43
+ rowAccessor?: string;
44
+ /** Node partition → fill family. @default "partition" */
45
+ partitionAccessor?: string;
46
+ /** Node semantic → icon. @default "semantic" */
47
+ semanticAccessor?: string;
48
+ /** Human label. @default "label" */
49
+ labelAccessor?: string;
50
+ /** Store list — `string[]` or `{storeName,slotIndex}[]`. @default "stores" */
51
+ storesAccessor?: string;
52
+ /** Edge "closes a cycle" flag. @default "isBackEdge" */
53
+ backEdgeAccessor?: string;
54
+ /** Edge type, drives edge color. @default "edgeType" */
55
+ edgeTypeAccessor?: string;
56
+ /** Fill per partition. Overridable; recipe ships dark-theme-friendly defaults. */
57
+ partitionColors?: Partial<Record<string, string>>;
58
+ /** Edge stroke per edgeType, plus `back` for back-edges. */
59
+ edgeColors?: Partial<Record<string, string>>;
60
+ /** Selection-ring stroke. @default var(--semiotic-focus, #ffcc33) */
61
+ accentColor?: string;
62
+ /** Container border stroke. @default var(--semiotic-border, #555) */
63
+ borderColor?: string;
64
+ /** Draw the store-slot chips in `full` LOD. @default true */
65
+ showStoreChips?: boolean;
66
+ /** Chip fill. @default var(--semiotic-info, #6a8caf) */
67
+ storeChipColor?: string;
68
+ /** Base edge opacity (non-dimmed). @default 0.5 */
69
+ edgeOpacity?: number;
70
+ /** Forward-edge stroke width in px. @default 1.25 */
71
+ edgeWidth?: number;
72
+ /** Back-edge stroke width in px. @default `edgeWidth`, else 1.5 */
73
+ backEdgeWidth?: number;
74
+ /**
75
+ * Render the per-node icon. Receives the resolved semantic/partition, the
76
+ * pixel size to draw within, and a color hint. Return any SVG node. When
77
+ * omitted a labelled fallback chip is drawn. The demo passes a KStreams
78
+ * icon set here — keeping the recipe domain-agnostic.
79
+ */
80
+ renderIcon?: (info: {
81
+ semantic: string;
82
+ partition: string;
83
+ size: number;
84
+ color: string;
85
+ node: Datum;
86
+ }) => ReactNode;
87
+ /** Small type label shown above the name in `full` LOD. @default the semantic. */
88
+ typeLabel?: (info: {
89
+ semantic: string;
90
+ partition: string;
91
+ node: Datum;
92
+ }) => string;
93
+ }
94
+ /**
95
+ * `lineageDagLayout` — a reusable layout recipe for **pre-positioned layered
96
+ * lineage / DAG graphs** with rich, composite node glyphs. Used here for the
97
+ * Kafka Streams topology viewer, but domain-agnostic: it reads logical
98
+ * `x` (layer) / `y` (row) coordinates the caller already computed (e.g. from a
99
+ * `dagLayoutFromGraph` pipeline) and maps them into the plot — it never runs a
100
+ * force sim or re-lays-out, so output is deterministic.
101
+ *
102
+ * **Composite glyphs as one hit-testable unit.** Each node emits exactly one
103
+ * `rect` (or `circle` in `dot` LOD) scene node — that single mark owns the
104
+ * canvas hit area and carries the node datum/id. All glyph chrome (semantic
105
+ * icon, type label, truncated name, per-store chips, selection ring) is drawn
106
+ * in the returned `overlays` layer, which is `pointer-events: none`, so it
107
+ * decorates without ever intercepting a hover. Hover/click therefore always
108
+ * resolve to the underlying node as a unit — see §5.2(a) of the spec.
109
+ *
110
+ * **Controlled dimming + selection, from outside.** `config.reachableIds`
111
+ * (host-computed set) dims everything outside it; `config.selectedId` draws
112
+ * the selection ring; both are owned by the host, never by the frame. The
113
+ * shared `NetworkLayoutContext.selection` predicate (from `LinkedCharts`)
114
+ * composes on top — a node dims if excluded by *either* cue.
115
+ *
116
+ * @example
117
+ * ```tsx
118
+ * import { NetworkCustomChart } from "semiotic/network"
119
+ * import { lineageDagLayout } from "semiotic/recipes"
120
+ *
121
+ * <NetworkCustomChart
122
+ * nodes={dagNodes} // each: { id, x: layer, y: row, partition, semantic, stores, label }
123
+ * edges={dagEdges} // each: { source, target, edgeType, isBackEdge }
124
+ * layout={lineageDagLayout}
125
+ * layoutConfig={{ layerCount, maxLayerSize, reachableIds, selectedId }}
126
+ * />
127
+ * ```
128
+ */
129
+ export declare const lineageDagLayout: NetworkCustomLayout<LineageDagConfig>;
@@ -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;