@fortemi/graph 2026.7.3 → 2026.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.
- package/README.md +57 -3
- package/dist/controller-m13D5PbF.d.ts +148 -0
- package/dist/controller.d.ts +2 -0
- package/dist/controller.js +184 -0
- package/dist/controller.js.map +1 -0
- package/dist/index.d.ts +219 -139
- package/dist/index.js +453 -164
- package/dist/index.js.map +1 -1
- package/package.json +8 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,68 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
id: string;
|
|
5
|
-
}
|
|
6
|
-
interface GraphEdge {
|
|
7
|
-
source: string;
|
|
8
|
-
target: string;
|
|
9
|
-
weight: number;
|
|
10
|
-
kind?: string;
|
|
11
|
-
}
|
|
12
|
-
interface GraphCommunity {
|
|
13
|
-
id: string;
|
|
14
|
-
nodes: string[];
|
|
15
|
-
}
|
|
16
|
-
interface CommunityGraph {
|
|
17
|
-
nodes: GraphNode[];
|
|
18
|
-
edges: GraphEdge[];
|
|
19
|
-
communities: GraphCommunity[];
|
|
20
|
-
}
|
|
21
|
-
/** Deterministic layout algorithms understood by {@link layoutCommunityGraph}. */
|
|
22
|
-
type GraphLayoutAlgorithm = 'force' | 'radial' | 'community' | 'manual';
|
|
23
|
-
/** A node with computed 2D coordinates, render radius, degree, and community. */
|
|
24
|
-
interface PositionedGraphNode extends GraphNode {
|
|
25
|
-
x: number;
|
|
26
|
-
y: number;
|
|
27
|
-
/** Stable render radius, derived from degree/weight by default. */
|
|
28
|
-
r: number;
|
|
29
|
-
degree: number;
|
|
30
|
-
communityId?: string;
|
|
31
|
-
}
|
|
32
|
-
/** A community with a computed centroid over its positioned member nodes. */
|
|
33
|
-
interface PositionedCommunity {
|
|
34
|
-
id: string;
|
|
35
|
-
x: number;
|
|
36
|
-
y: number;
|
|
37
|
-
/** Number of member nodes that contributed to the centroid. */
|
|
38
|
-
size: number;
|
|
39
|
-
}
|
|
40
|
-
/** Result of laying out a {@link CommunityGraph} into 2D space. */
|
|
41
|
-
interface PositionedGraph {
|
|
42
|
-
nodes: PositionedGraphNode[];
|
|
43
|
-
edges: GraphEdge[];
|
|
44
|
-
/** Lookup from node id to its positioned node. */
|
|
45
|
-
nodeIndex: Map<string, PositionedGraphNode>;
|
|
46
|
-
/** Community centroids over the final positions (empty when none). */
|
|
47
|
-
communities: PositionedCommunity[];
|
|
48
|
-
}
|
|
49
|
-
/** Axis-aligned bounding box around a set of positioned nodes. */
|
|
50
|
-
interface GraphBounds {
|
|
51
|
-
minX: number;
|
|
52
|
-
minY: number;
|
|
53
|
-
maxX: number;
|
|
54
|
-
maxY: number;
|
|
55
|
-
width: number;
|
|
56
|
-
height: number;
|
|
57
|
-
centerX: number;
|
|
58
|
-
centerY: number;
|
|
59
|
-
}
|
|
60
|
-
/** A viewport transform that fits a {@link GraphBounds} into a target rect. */
|
|
61
|
-
interface ViewportTransform {
|
|
62
|
-
scale: number;
|
|
63
|
-
offsetX: number;
|
|
64
|
-
offsetY: number;
|
|
65
|
-
}
|
|
1
|
+
import { C as CommunityGraph, G as GraphNode, a as GraphLayoutAlgorithm, P as PositionedGraph, b as PositionedGraphNode, c as GraphBounds, V as ViewportTransform } from './controller-m13D5PbF.js';
|
|
2
|
+
export { d as GraphCommunity, e as GraphControllerDb, f as GraphControllerListener, g as GraphControllerOptions, h as GraphControllerState, i as GraphControllerStatus, j as GraphEdge, k as GraphLayoutState, l as GraphSourceMode, m as GraphSourceRef, n as GraphTransitionState, o as PositionedCommunity } from './controller-m13D5PbF.js';
|
|
3
|
+
import '@fortemi/core';
|
|
66
4
|
|
|
67
5
|
/** Count the (undirected) degree of every node, including isolated nodes (0). */
|
|
68
6
|
declare function computeDegrees(graph: CommunityGraph): Map<string, number>;
|
|
@@ -151,7 +89,28 @@ interface LayoutOptions {
|
|
|
151
89
|
communityStrength?: number;
|
|
152
90
|
/** Keep every node center at least this many px from each canvas edge. */
|
|
153
91
|
boundsPadding?: number;
|
|
92
|
+
/**
|
|
93
|
+
* Positions to hold FIXED during settlement (drag pins, issue #245). Pinned
|
|
94
|
+
* nodes stay put while the rest of the graph re-settles around them, giving an
|
|
95
|
+
* incremental re-layout when a node is moved. Keyed by node id.
|
|
96
|
+
*/
|
|
97
|
+
pinned?: PositionMap;
|
|
98
|
+
/**
|
|
99
|
+
* Warm-start seed positions. Nodes present here begin the settlement from
|
|
100
|
+
* these coordinates instead of the closed-form ring, so a re-layout resumes
|
|
101
|
+
* from the current arrangement (smooth reshape) rather than re-seeding. Keyed
|
|
102
|
+
* by node id. `pinned` overrides this for pinned nodes.
|
|
103
|
+
*/
|
|
104
|
+
initialPositions?: PositionMap;
|
|
154
105
|
}
|
|
106
|
+
/** A node-id → position lookup, accepted as either a Map or a plain record. */
|
|
107
|
+
type PositionMap = Map<string, {
|
|
108
|
+
x: number;
|
|
109
|
+
y: number;
|
|
110
|
+
}> | Record<string, {
|
|
111
|
+
x: number;
|
|
112
|
+
y: number;
|
|
113
|
+
}>;
|
|
155
114
|
/**
|
|
156
115
|
* Deterministically position the nodes of a {@link CommunityGraph} in 2D space.
|
|
157
116
|
*
|
|
@@ -254,87 +213,208 @@ declare function stringifyGraphSnapshot(graph: CommunityGraph, options?: Seriali
|
|
|
254
213
|
*/
|
|
255
214
|
declare function deserializeGraphSnapshot(input: GraphSnapshot | string): CommunityGraph;
|
|
256
215
|
|
|
257
|
-
/**
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
216
|
+
/**
|
|
217
|
+
* Greyscale community ramp — warm-grey, darkest first, meant to sit on a light
|
|
218
|
+
* ("cream") background. Index 0 (darkest) is the largest community. Strictly
|
|
219
|
+
* greyscale so community tone reads as density, not category.
|
|
220
|
+
*/
|
|
221
|
+
declare const GREYSCALE_COMMUNITY_RAMP: readonly ["#2B2824", "#43403A", "#585149", "#6E665A", "#837A6B", "#968C7C"];
|
|
222
|
+
/** A render-ready node: identity + label + degree-derived size + tone + optional baked position. */
|
|
223
|
+
interface RenderNode {
|
|
224
|
+
id: string;
|
|
225
|
+
label: string;
|
|
226
|
+
/** Degree-derived render size. */
|
|
227
|
+
size: number;
|
|
228
|
+
/** Community tone (see {@link CommunityPalette}). */
|
|
229
|
+
color: string;
|
|
230
|
+
/** Community rank: 0 = largest community, ascending; -1 when unassigned. */
|
|
231
|
+
communityRank: number;
|
|
232
|
+
/** Baked layout coordinate (present only when positions were supplied). */
|
|
233
|
+
x?: number;
|
|
234
|
+
y?: number;
|
|
235
|
+
}
|
|
236
|
+
/** A render-ready link (edge) referencing nodes by id. */
|
|
237
|
+
interface RenderLink {
|
|
238
|
+
source: string;
|
|
239
|
+
target: string;
|
|
240
|
+
weight: number;
|
|
241
|
+
kind?: string;
|
|
265
242
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
243
|
+
/** A render-ready graph: the shape every renderer tier consumes. */
|
|
244
|
+
interface RenderGraph {
|
|
245
|
+
nodes: RenderNode[];
|
|
246
|
+
links: RenderLink[];
|
|
247
|
+
/** Number of communities in the source graph. */
|
|
248
|
+
clusters: number;
|
|
271
249
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
250
|
+
/**
|
|
251
|
+
* Community coloring strategy for {@link mapCommunityGraph}:
|
|
252
|
+
* - `'community'` (default): hash the community id into the qualitative palette
|
|
253
|
+
* (matches the historical `GraphView` coloring).
|
|
254
|
+
* - `'greyscale'`: {@link GREYSCALE_COMMUNITY_RAMP} indexed by community rank
|
|
255
|
+
* (largest cluster = darkest).
|
|
256
|
+
* - a custom array: indexed by community rank.
|
|
257
|
+
*/
|
|
258
|
+
type CommunityPalette = 'community' | 'greyscale' | readonly string[];
|
|
259
|
+
interface MapCommunityGraphOptions {
|
|
260
|
+
/** Resolve a human label for a node id. Defaults to the id itself. */
|
|
261
|
+
labelFor?: (id: string) => string;
|
|
262
|
+
/** Baked layout positions to stamp onto nodes (warm start). */
|
|
263
|
+
positions?: Map<string, {
|
|
264
|
+
x: number;
|
|
265
|
+
y: number;
|
|
266
|
+
}> | Record<string, {
|
|
267
|
+
x: number;
|
|
268
|
+
y: number;
|
|
269
|
+
}>;
|
|
270
|
+
/** Community coloring strategy. Default `'community'`. */
|
|
271
|
+
palette?: CommunityPalette;
|
|
272
|
+
/** Node-radius tuning passed to {@link nodeRadius} (default sizer). */
|
|
273
|
+
radius?: NodeRadiusOptions;
|
|
274
|
+
/** Override the degree → size mapping entirely. */
|
|
275
|
+
sizeFor?: (degree: number, id: string) => number;
|
|
277
276
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
277
|
+
/**
|
|
278
|
+
* Rank communities by member count, largest first. Returns a map from community
|
|
279
|
+
* id to its rank (0 = largest). Ties break on community id for determinism.
|
|
280
|
+
*/
|
|
281
|
+
declare function communityRanks(graph: CommunityGraph): Map<string, number>;
|
|
282
|
+
/**
|
|
283
|
+
* Map a {@link CommunityGraph} to a render-ready {@link RenderGraph}: labels,
|
|
284
|
+
* degree-derived node size, per-community tone (largest cluster = darkest for
|
|
285
|
+
* ramped palettes), and baked positions when supplied. Pure and deterministic —
|
|
286
|
+
* shared by the runtime loader and the build-time snapshot writer so both render
|
|
287
|
+
* identically.
|
|
288
|
+
*/
|
|
289
|
+
declare function mapCommunityGraph(graph: CommunityGraph, options?: MapCommunityGraphOptions): RenderGraph;
|
|
290
|
+
interface BakeRenderGraphOptions extends MapCommunityGraphOptions {
|
|
291
|
+
/** Layout options for the one-shot build-time layout pass. */
|
|
292
|
+
layout?: LayoutOptions;
|
|
293
293
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
294
|
+
/**
|
|
295
|
+
* Build-time snapshot writer: run the layout once and emit a {@link RenderGraph}
|
|
296
|
+
* with baked x/y so hosts can warm-start (skip the layout pass) at runtime.
|
|
297
|
+
* Companion to `serializeGraphSnapshot` — this bakes render-ready positions
|
|
298
|
+
* rather than storing the raw graph + layout intent. Any `positions` passed in
|
|
299
|
+
* options are ignored in favor of the freshly computed layout.
|
|
300
|
+
*/
|
|
301
|
+
declare function bakeRenderGraph(graph: CommunityGraph, options?: BakeRenderGraphOptions): RenderGraph;
|
|
302
|
+
/** Deterministic JSON for a render graph (stable key order for cache-friendly diffs). */
|
|
303
|
+
declare function stringifyRenderGraph(graph: RenderGraph): string;
|
|
304
|
+
/** Structural guard for a {@link RenderGraph} loaded from an untrusted source. */
|
|
305
|
+
declare function isRenderGraph(value: unknown): value is RenderGraph;
|
|
306
|
+
/** Whether every node in a render graph carries a baked position. */
|
|
307
|
+
declare function hasBakedPositions(graph: RenderGraph): boolean;
|
|
308
|
+
interface LoadRenderSnapshotOptions {
|
|
309
|
+
/** `fetch` implementation to use when `source` is a URL. Defaults to `globalThis.fetch`. */
|
|
310
|
+
fetchImpl?: typeof fetch;
|
|
311
|
+
/** Require baked x/y on every node; a snapshot without positions resolves to `null`. Default `true`. */
|
|
312
|
+
requirePositions?: boolean;
|
|
300
313
|
}
|
|
301
|
-
type GraphControllerListener = (state: GraphControllerState) => void;
|
|
302
314
|
/**
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
* initial load. Setters update state and schedule a refresh, mirroring the
|
|
309
|
-
* effect-driven behavior of the original React hook.
|
|
315
|
+
* Snapshot-first loader (issue #264). Load a precomputed, render-ready graph
|
|
316
|
+
* carrying baked positions — instant, no live `GraphController` build and no
|
|
317
|
+
* layout pass. Accepts a URL to fetch, an already-parsed {@link RenderGraph}, or
|
|
318
|
+
* a thunk. Returns `null` (never throws) when the snapshot is absent, malformed,
|
|
319
|
+
* or lacks baked positions, so callers fall back to a live build + `mapCommunityGraph`.
|
|
310
320
|
*/
|
|
311
|
-
declare
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
start(): Promise<void>;
|
|
324
|
-
private setState;
|
|
325
|
-
private beginTransition;
|
|
326
|
-
refresh(): Promise<void>;
|
|
327
|
-
setMode(nextMode: GraphSourceMode): void;
|
|
328
|
-
setEmbeddingSetSelector(selector: EmbeddingSetSelector): void;
|
|
329
|
-
setCommunitySource(sourceId: string | null): void;
|
|
330
|
-
/** Apply dynamic-search filters and switch into that mode (state only). */
|
|
331
|
-
private applyFilters;
|
|
332
|
-
setFilters(nextFilters: CommunityFilterDefinition): void;
|
|
333
|
-
recompute(): Promise<void>;
|
|
334
|
-
previewDynamicCommunity(nextFilters: CommunityFilterDefinition): Promise<void>;
|
|
335
|
-
saveCurrentCommunity(input: CommunityCreateInput): Promise<CommunitySourceDescriptor>;
|
|
321
|
+
declare function loadRenderSnapshot(source: string | RenderGraph | (() => Promise<RenderGraph | null> | RenderGraph | null), options?: LoadRenderSnapshotOptions): Promise<RenderGraph | null>;
|
|
322
|
+
|
|
323
|
+
/** Node/edge visibility filter shared by every renderer tier. */
|
|
324
|
+
interface GraphControlFilters {
|
|
325
|
+
/** Keep only nodes in these communities. */
|
|
326
|
+
communityIds?: string[];
|
|
327
|
+
/** Keep only edges whose `kind` is in this set. */
|
|
328
|
+
edgeKinds?: string[];
|
|
329
|
+
/** Explicit node allow-list. */
|
|
330
|
+
nodeIds?: string[];
|
|
331
|
+
/** Hide nodes with fewer than this many connections (degree) in the visible set. */
|
|
332
|
+
minDegree?: number;
|
|
336
333
|
}
|
|
334
|
+
/**
|
|
335
|
+
* The control surface common to all renderer tiers. A consumer coded against
|
|
336
|
+
* this contract can move a graph between the JS-only renderer, React
|
|
337
|
+
* `GraphView`, and future tiers by passing the same options object.
|
|
338
|
+
*/
|
|
339
|
+
interface GraphControlContract {
|
|
340
|
+
/** Layout algorithm. Default `force`. */
|
|
341
|
+
algorithm?: GraphLayoutAlgorithm;
|
|
342
|
+
/** Visibility filter. */
|
|
343
|
+
filters?: GraphControlFilters;
|
|
344
|
+
/** Selected node id (controlled). */
|
|
345
|
+
selectedNodeId?: string | null;
|
|
346
|
+
/** Selection callback (click / keyboard). */
|
|
347
|
+
onSelectNode?: (nodeId: string) => void;
|
|
348
|
+
/** Navigation callback (double-click / Enter on a selected node). */
|
|
349
|
+
onNavigate?: (nodeId: string) => void;
|
|
350
|
+
/** Human label for a node (popup / a11y). Default: the node id. */
|
|
351
|
+
labelFor?: (nodeId: string) => string;
|
|
352
|
+
/** Community palette override. */
|
|
353
|
+
colors?: readonly string[];
|
|
354
|
+
/** Logical viewport size. */
|
|
355
|
+
width?: number;
|
|
356
|
+
height?: number;
|
|
357
|
+
}
|
|
358
|
+
/** One community-legend row: community id, its color, and member count. */
|
|
359
|
+
interface GraphLegendEntry {
|
|
360
|
+
communityId: string;
|
|
361
|
+
color: string;
|
|
362
|
+
count: number;
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Apply the shared {@link GraphControlFilters} to `graph`. Composes the engine's
|
|
366
|
+
* community / edge-kind / node-allow-list filter with a `minDegree` pass
|
|
367
|
+
* (degree computed over the already-filtered set). Pure — input is not mutated.
|
|
368
|
+
*/
|
|
369
|
+
declare function applyControlFilters(graph: CommunityGraph | null | undefined, filters?: GraphControlFilters): CommunityGraph;
|
|
370
|
+
/**
|
|
371
|
+
* Build community-legend rows for a graph — the shared data that any tier can
|
|
372
|
+
* render as a legend and use to drive the `communityIds` show/hide filter.
|
|
373
|
+
* Sorted by member count (largest first). Colors match `colorForCommunity`.
|
|
374
|
+
*/
|
|
375
|
+
declare function communityLegend(graph: CommunityGraph | null | undefined, colors?: readonly string[]): GraphLegendEntry[];
|
|
376
|
+
|
|
377
|
+
/** Filter shape accepted by the renderer — the shared control-filter contract. */
|
|
378
|
+
type GraphRenderFilters = GraphControlFilters;
|
|
379
|
+
/**
|
|
380
|
+
* Options for {@link renderCommunityGraph}. Extends the shared
|
|
381
|
+
* {@link GraphControlContract} (algorithm, filters, selection, navigation,
|
|
382
|
+
* labels, colors, size — honored by every renderer tier) with renderer-specific
|
|
383
|
+
* knobs. The React `GraphView` honors the same contract, so the tiers are
|
|
384
|
+
* drop-in swappable.
|
|
385
|
+
*/
|
|
386
|
+
interface GraphRenderOptions extends GraphControlContract {
|
|
387
|
+
/** Extra layout tuning forwarded to `layoutCommunityGraph`. */
|
|
388
|
+
layoutOptions?: Omit<LayoutOptions, 'algorithm' | 'width' | 'height'>;
|
|
389
|
+
/** Canvas background. Default `#fafafa`. */
|
|
390
|
+
background?: string;
|
|
391
|
+
/** Enable zoom/pan/hover interactions. Default `true`. */
|
|
392
|
+
interactive?: boolean;
|
|
393
|
+
/** Zoom clamp. Default 0.4 / 3. */
|
|
394
|
+
minScale?: number;
|
|
395
|
+
maxScale?: number;
|
|
396
|
+
}
|
|
397
|
+
/** Mutable subset applied via {@link GraphRenderHandle.update}. */
|
|
398
|
+
type GraphRenderUpdate = Partial<Pick<GraphRenderOptions, 'filters' | 'algorithm' | 'selectedNodeId' | 'labelFor'>> & {
|
|
399
|
+
graph?: CommunityGraph;
|
|
400
|
+
};
|
|
401
|
+
/** Live handle returned by {@link renderCommunityGraph}. */
|
|
402
|
+
interface GraphRenderHandle {
|
|
403
|
+
/** Apply new graph/filters/algorithm/selection without recreating the view. */
|
|
404
|
+
update(next: GraphRenderUpdate): void;
|
|
405
|
+
/** Select and center a node (search/focus). Pass `null` to clear selection. */
|
|
406
|
+
focus(nodeId: string | null): void;
|
|
407
|
+
/** Remove the view, its popup, and all listeners from the container. */
|
|
408
|
+
destroy(): void;
|
|
409
|
+
/** The mounted `<svg>` element. */
|
|
410
|
+
readonly element: SVGSVGElement;
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Render `graph` into `container` as an interactive SVG community graph.
|
|
414
|
+
* Returns a {@link GraphRenderHandle}; call `destroy()` to tear it down.
|
|
415
|
+
*/
|
|
416
|
+
declare function renderCommunityGraph(container: HTMLElement, graph: CommunityGraph, options?: GraphRenderOptions): GraphRenderHandle;
|
|
337
417
|
|
|
338
|
-
declare const VERSION = "2026.7.
|
|
418
|
+
declare const VERSION = "2026.7.5";
|
|
339
419
|
|
|
340
|
-
export { COMMUNITY_COLORS, type
|
|
420
|
+
export { type BakeRenderGraphOptions, COMMUNITY_COLORS, CommunityGraph, type CommunityPalette, type ExpandOptions, type FitOptions, GRAPH_SNAPSHOT_VERSION, GREYSCALE_COMMUNITY_RAMP, GraphBounds, type GraphControlContract, type GraphControlFilters, type GraphFilter, GraphLayoutAlgorithm, type GraphLegendEntry, GraphNode, type GraphRenderFilters, type GraphRenderHandle, type GraphRenderOptions, type GraphRenderUpdate, type GraphSnapshot, type LayoutOptions, type LoadRenderSnapshotOptions, type MapCommunityGraphOptions, type NodeRadiusOptions, type NodeRadiusResolver, type PositionMap, PositionedGraph, PositionedGraphNode, type RenderGraph, type RenderLink, type RenderNode, type SerializeSnapshotOptions, UNASSIGNED_COMMUNITY_COLOR, VERSION, ViewportTransform, applyControlFilters, bakeRenderGraph, buildAdjacency, colorForCommunity, communityLegend, communityRanks, computeDegrees, computeGraphBounds, deserializeGraphSnapshot, expandNeighborhood, filterCommunityGraph, fitGraphToViewport, hasBakedPositions, isRenderGraph, layoutCommunityGraph, loadRenderSnapshot, mapCommunityGraph, neighborhoodSubgraph, neighborsOf, nodeRadius, renderCommunityGraph, serializeGraphSnapshot, stringifyGraphSnapshot, stringifyRenderGraph, subgraphForNodes };
|