@kgdiff/g6 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +149 -2
- package/dist/color.d.ts +24 -0
- package/dist/color.d.ts.map +1 -0
- package/dist/color.js +95 -0
- package/dist/color.js.map +1 -0
- package/dist/constants.d.ts +81 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +81 -0
- package/dist/constants.js.map +1 -0
- package/dist/controller.d.ts +126 -0
- package/dist/controller.d.ts.map +1 -0
- package/dist/controller.js +422 -0
- package/dist/controller.js.map +1 -0
- package/dist/geometry.d.ts +19 -0
- package/dist/geometry.d.ts.map +1 -0
- package/dist/geometry.js +43 -0
- package/dist/geometry.js.map +1 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +99 -0
- package/dist/index.js.map +1 -0
- package/dist/locator.d.ts +24 -0
- package/dist/locator.d.ts.map +1 -0
- package/dist/locator.js +69 -0
- package/dist/locator.js.map +1 -0
- package/dist/map-context.d.ts +70 -0
- package/dist/map-context.d.ts.map +1 -0
- package/dist/map-context.js +44 -0
- package/dist/map-context.js.map +1 -0
- package/dist/map-edge.d.ts +19 -0
- package/dist/map-edge.d.ts.map +1 -0
- package/dist/map-edge.js +399 -0
- package/dist/map-edge.js.map +1 -0
- package/dist/map-node.d.ts +10 -0
- package/dist/map-node.d.ts.map +1 -0
- package/dist/map-node.js +243 -0
- package/dist/map-node.js.map +1 -0
- package/dist/map.d.ts +47 -0
- package/dist/map.d.ts.map +1 -0
- package/dist/map.js +92 -0
- package/dist/map.js.map +1 -0
- package/dist/node.d.ts +68 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +189 -0
- package/dist/node.js.map +1 -0
- package/dist/options.d.ts +33 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +172 -0
- package/dist/options.js.map +1 -0
- package/dist/patch.d.ts +45 -0
- package/dist/patch.d.ts.map +1 -0
- package/dist/patch.js +120 -0
- package/dist/patch.js.map +1 -0
- package/dist/replay.d.ts +20 -0
- package/dist/replay.d.ts.map +1 -0
- package/dist/replay.js +36 -0
- package/dist/replay.js.map +1 -0
- package/package.json +54 -3
- package/src/color.ts +101 -0
- package/src/constants.ts +96 -0
- package/src/controller.ts +517 -0
- package/src/geometry.ts +44 -0
- package/src/index.ts +140 -0
- package/src/locator.ts +72 -0
- package/src/map-context.ts +119 -0
- package/src/map-edge.ts +481 -0
- package/src/map-node.ts +272 -0
- package/src/map.ts +130 -0
- package/src/node.ts +240 -0
- package/src/options.ts +215 -0
- package/src/patch.ts +133 -0
- package/src/replay.ts +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 iiwish and kgdiff contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,150 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @kgdiff/g6
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Render kgdiff's semantic graph diffs inside [AntV G6 v5](https://g6.antv.antgroup.com/), with the
|
|
4
|
+
same visual language as the kgdiff viewer: five meaning families, hollow dashed ghosts, rings,
|
|
5
|
+
glows, struck-through old names, merge / split arcs, a selection spotlight and a hunk-by-hunk
|
|
6
|
+
replay.
|
|
7
|
+
|
|
8
|
+
kgdiff computes the diff (`@kgdiff/core`), lays both versions out on one map (`@kgdiff/layout`)
|
|
9
|
+
and resolves every frame into visual primitives (`@kgdiff/scene`). This package turns those
|
|
10
|
+
primitives into G6 data and drives a G6 `Graph` with them.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
pnpm add @kgdiff/g6 @kgdiff/core @kgdiff/layout @kgdiff/scene @antv/g6
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`@antv/g6` (≥ 5.1) is a peer dependency: use the one your product already ships.
|
|
19
|
+
|
|
20
|
+
## Quick start
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { diff } from '@kgdiff/core';
|
|
24
|
+
import { layoutDiff } from '@kgdiff/layout';
|
|
25
|
+
import { buildScene } from '@kgdiff/scene';
|
|
26
|
+
import { createDiffGraph } from '@kgdiff/g6';
|
|
27
|
+
|
|
28
|
+
// before / after: GraphInput ({ nodes, edges }) — or parse files with @kgdiff/formats.
|
|
29
|
+
const result = diff(before, after);
|
|
30
|
+
const scene = buildScene(result, layoutDiff(result), { theme: 'dark', locale: 'en' });
|
|
31
|
+
|
|
32
|
+
const diffGraph = await createDiffGraph(document.getElementById('graph') as HTMLElement, scene);
|
|
33
|
+
|
|
34
|
+
await diffGraph.selectHunk(0); // spotlight the first change and pan to it
|
|
35
|
+
await diffGraph.setLayer('after'); // 'union' (overlay, default) | 'before' | 'after'
|
|
36
|
+
await diffGraph.playReplay({ speed: 2 }); // before → after, hunk by hunk
|
|
37
|
+
diffGraph.destroy();
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The container needs a size: G6 rewrites its inline `position` / `display`, so size it (or a
|
|
41
|
+
wrapper) with CSS rather than relying on `position: fixed` on the container itself.
|
|
42
|
+
|
|
43
|
+
## Controller
|
|
44
|
+
|
|
45
|
+
`createDiffGraph(container, scene, options?)` resolves to a controller:
|
|
46
|
+
|
|
47
|
+
| Member | |
|
|
48
|
+
| ----------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
49
|
+
| `graph` | The G6 `Graph`, for your own plugins, events and camera calls. |
|
|
50
|
+
| `setLayer(layer)` | Show `'union'`, `'before'` or `'after'`; animates over `theme.motion.base`. |
|
|
51
|
+
| `selectHunk(index)` | Spotlight a hunk (`-1` clears) and pan to its nodes. In the replay layer it seeks to the end of the hunk's window. |
|
|
52
|
+
| `playReplay({ speed })` | Play the replay (default 1×). Resolves when finished or stopped. |
|
|
53
|
+
| `stop()` | Stop a running replay; its promise resolves at once. |
|
|
54
|
+
| `setTheme(theme)` | Switch colours, e.g. `getTheme('light', 'colorblind')` from `@kgdiff/scene`. |
|
|
55
|
+
| `destroy()` | Destroy the graph. Pending calls settle; later calls reject. |
|
|
56
|
+
|
|
57
|
+
Options:
|
|
58
|
+
|
|
59
|
+
- `view` — the initial `ViewState` (layer, selection, family filter, focus, `reducedMotion`…).
|
|
60
|
+
- `interactive` (default `true`) — zoom / drag the canvas, hover highlights, click a change to
|
|
61
|
+
select its hunk, click an unchanged node to select it, click the canvas to clear.
|
|
62
|
+
- `g6` — extra G6 options merged over the generated ones: `behaviors`, `plugins` and
|
|
63
|
+
`transforms` are appended, plain objects merge recursively, other values replace.
|
|
64
|
+
`animation: false` turns animations off (the replay keeps its pace).
|
|
65
|
+
|
|
66
|
+
Calls made while a draw is still running are merged (latest wins): five quick clicks cost one
|
|
67
|
+
G6 update. Every update sends G6 only the elements that changed, with only the changed style
|
|
68
|
+
keys.
|
|
69
|
+
|
|
70
|
+
The promises resolve when G6's animation has finished, or at most 1 s after its duration:
|
|
71
|
+
browsers pause animation frames in background tabs, and G6 only settles a draw once its
|
|
72
|
+
animation has run.
|
|
73
|
+
|
|
74
|
+
## Using `toG6Data` with your own graph
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import { createViewState, createVisualResolver } from '@kgdiff/scene';
|
|
78
|
+
import { toG6Data } from '@kgdiff/g6';
|
|
79
|
+
|
|
80
|
+
const resolver = createVisualResolver(scene);
|
|
81
|
+
const frame = resolver.resolve(createViewState({ layer: 'union', selectedHunk: 3 }));
|
|
82
|
+
graph.setData(toG6Data(scene, frame, { idPrefix: 'diff/' }));
|
|
83
|
+
await graph.draw();
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`toG6Data` is pure. Nodes have no `type` and render as G6 circles. The ring, glow, replay pulse,
|
|
87
|
+
struck-through labels and old names are drawn by the `kgdiff-node` type, which only
|
|
88
|
+
`createDiffGraph` registers, so a plain circle shows a simpler G6 halo instead. Level of detail
|
|
89
|
+
is evaluated at zoom 1. Every element's `data` is a `KgdiffDatumData`:
|
|
90
|
+
`{ kgdiff: 'node' | 'edge' | 'glyph' | 'anchor' | 'collapsed', index, hunk, family, changed,
|
|
91
|
+
label, glyph }`. `index` is the scene index, so your handlers can map a G6 id back to the diff.
|
|
92
|
+
|
|
93
|
+
Every style key is always present, switched off with `false` / `0` / `''` when unused, because
|
|
94
|
+
G6 merges updates into the previous style.
|
|
95
|
+
|
|
96
|
+
## Notes
|
|
97
|
+
|
|
98
|
+
- **Your own elements.** The controller adds, updates and removes only the elements it
|
|
99
|
+
generated, so elements you add with `graph.addData` stay. Don't call `graph.setData`,
|
|
100
|
+
`graph.clear` or `removeData` on kgdiff's elements: the controller patches the graph
|
|
101
|
+
incrementally and expects its elements to be there. Avoid ids that start with `kgdiff-` or
|
|
102
|
+
equal scene union ids. Edges you attach to a kgdiff node are removed by G6 when that node is
|
|
103
|
+
hidden (for example an added node in the `before` layer).
|
|
104
|
+
- **Screen-constant sizes.** G6 scales strokes, labels and arrowheads with the camera; kgdiff's
|
|
105
|
+
sizes are exact at zoom 1. For readable overviews, add G6's `fix-element-size` behaviour:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
createDiffGraph(container, scene, {
|
|
109
|
+
g6: {
|
|
110
|
+
behaviors: [
|
|
111
|
+
{
|
|
112
|
+
type: 'fix-element-size',
|
|
113
|
+
enable: (event) => event.data.scale < 1,
|
|
114
|
+
node: [{ shape: 'key', fields: ['lineWidth'] }, { shape: 'label' }],
|
|
115
|
+
edge: [{ shape: 'key', fields: ['lineWidth'] }, { shape: 'label' }],
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
- **Theme.** The graph runs with `theme: false`, so no G6 theme styles, palettes or state
|
|
123
|
+
styles mix with kgdiff's encodings. The canvas colour comes from a `background` plugin, and
|
|
124
|
+
hover uses a private state (`kgdiff-hover`).
|
|
125
|
+
|
|
126
|
+
## Limitations
|
|
127
|
+
|
|
128
|
+
- Labels, badges and arrowheads switch on at their zoom threshold. The native viewer fades them
|
|
129
|
+
in over the last 10% of zoom.
|
|
130
|
+
- There is no label collision avoidance. G6's `auto-adapt-label` is O(n²) and reorders
|
|
131
|
+
elements, so it is not enabled.
|
|
132
|
+
- Replay animations are piecewise linear between four samples per hunk (spotlight, mid-move,
|
|
133
|
+
settle, end). Merge travel follows two straight segments instead of an arc.
|
|
134
|
+
- Turning the spotlight on or off changes the opacity of every element, and G6 5.1 re-renders
|
|
135
|
+
each changed element in full. At ~5k nodes / 12k edges that took about 1.7 s in our tests
|
|
136
|
+
(happy-dom, G6 5.1.1). Moving between hunks, or switching layers on a mostly unchanged graph,
|
|
137
|
+
only touches what changed.
|
|
138
|
+
- The camera pans to a selection (`focusElement`) but does not zoom.
|
|
139
|
+
- Glyph lines start at the node outline, not 4 units outside it like the native viewer.
|
|
140
|
+
|
|
141
|
+
## Development
|
|
142
|
+
|
|
143
|
+
```sh
|
|
144
|
+
pnpm vitest run packages/g6
|
|
145
|
+
pnpm --filter @kgdiff/g6 typecheck
|
|
146
|
+
pnpm vite --config packages/g6/dev/vite.config.ts # harness on http://localhost:5197
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The harness renders the example datasets (`?dataset=<id>`) with layer, replay, theme and hunk
|
|
150
|
+
controls, for comparison with the native renderer's harness.
|
package/dist/color.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The few color operations G6 cannot do for us: compositing translucent badge tints over the
|
|
3
|
+
* canvas (the native renderer paints an opaque base first) and building glow gradients.
|
|
4
|
+
* Scene tokens are `#rgb` / `#rrggbb` / `#rrggbbaa` or `rgb()` / `rgba()`; anything else is
|
|
5
|
+
* passed through unchanged.
|
|
6
|
+
*/
|
|
7
|
+
/** [r, g, b] in 0–255 and alpha in [0, 1]. */
|
|
8
|
+
export type Rgba = readonly [number, number, number, number];
|
|
9
|
+
/** Parse a hex or `rgb()` / `rgba()` color; null for anything else. */
|
|
10
|
+
export declare function parseColor(color: string): Rgba | null;
|
|
11
|
+
/** `rgba(r, g, b, a)` with `a` multiplied into the color's own alpha. */
|
|
12
|
+
export declare function withAlpha(color: Rgba, alpha: number): string;
|
|
13
|
+
/**
|
|
14
|
+
* `top` painted over `bottom` (source-over) as an opaque `rgb()` color. Returns `top` unchanged
|
|
15
|
+
* when either color cannot be parsed, and `top` itself when it is already opaque.
|
|
16
|
+
*/
|
|
17
|
+
export declare function composite(top: string, bottom: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* A CSS radial gradient (parsed by AntV G) filling a circle with the native glow profile in
|
|
20
|
+
* `color`; `closest-side` makes the gradient end exactly at the circle's radius. Memoized: the
|
|
21
|
+
* string doubles as G's gradient cache key. Falls back to the plain color when unparsable.
|
|
22
|
+
*/
|
|
23
|
+
export declare function glowGradient(color: string): string;
|
|
24
|
+
//# sourceMappingURL=color.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color.d.ts","sourceRoot":"","sources":["../src/color.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,8CAA8C;AAC9C,MAAM,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAa7D,uEAAuE;AACvE,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CA8BrD;AAED,yEAAyE;AACzE,wBAAgB,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG5D;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAO7D;AAcD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUlD"}
|
package/dist/color.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The few color operations G6 cannot do for us: compositing translucent badge tints over the
|
|
3
|
+
* canvas (the native renderer paints an opaque base first) and building glow gradients.
|
|
4
|
+
* Scene tokens are `#rgb` / `#rrggbb` / `#rrggbbaa` or `rgb()` / `rgba()`; anything else is
|
|
5
|
+
* passed through unchanged.
|
|
6
|
+
*/
|
|
7
|
+
const HEX = /^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
|
|
8
|
+
const FUNC = /^rgba?\(\s*([^)]*)\)$/i;
|
|
9
|
+
function clampByte(v) {
|
|
10
|
+
return Math.min(255, Math.max(0, Math.round(v)));
|
|
11
|
+
}
|
|
12
|
+
function clampUnit(v) {
|
|
13
|
+
return Math.min(1, Math.max(0, v));
|
|
14
|
+
}
|
|
15
|
+
/** Parse a hex or `rgb()` / `rgba()` color; null for anything else. */
|
|
16
|
+
export function parseColor(color) {
|
|
17
|
+
const s = color.trim();
|
|
18
|
+
const hex = HEX.exec(s);
|
|
19
|
+
if (hex !== null) {
|
|
20
|
+
let h = hex[1];
|
|
21
|
+
if (h.length <= 4)
|
|
22
|
+
h = [...h].map((c) => c + c).join('');
|
|
23
|
+
const n = (i) => Number.parseInt(h.slice(i, i + 2), 16);
|
|
24
|
+
return [n(0), n(2), n(4), h.length === 8 ? n(6) / 255 : 1];
|
|
25
|
+
}
|
|
26
|
+
const fn = FUNC.exec(s);
|
|
27
|
+
if (fn === null)
|
|
28
|
+
return null;
|
|
29
|
+
const parts = fn[1].split(/[\s,/]+/).filter((p) => p.length > 0);
|
|
30
|
+
if (parts.length !== 3 && parts.length !== 4)
|
|
31
|
+
return null;
|
|
32
|
+
const nums = parts.map((p) => p.endsWith('%') ? (Number.parseFloat(p) / 100) * 255 : Number.parseFloat(p));
|
|
33
|
+
if (nums.some((v) => !Number.isFinite(v)))
|
|
34
|
+
return null;
|
|
35
|
+
const alphaPart = parts[3];
|
|
36
|
+
const alpha = alphaPart === undefined
|
|
37
|
+
? 1
|
|
38
|
+
: alphaPart.endsWith('%')
|
|
39
|
+
? Number.parseFloat(alphaPart) / 100
|
|
40
|
+
: Number.parseFloat(alphaPart);
|
|
41
|
+
return [
|
|
42
|
+
clampByte(nums[0]),
|
|
43
|
+
clampByte(nums[1]),
|
|
44
|
+
clampByte(nums[2]),
|
|
45
|
+
clampUnit(alpha),
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
/** `rgba(r, g, b, a)` with `a` multiplied into the color's own alpha. */
|
|
49
|
+
export function withAlpha(color, alpha) {
|
|
50
|
+
const a = Math.round(clampUnit(color[3] * alpha) * 1000) / 1000;
|
|
51
|
+
return `rgba(${color[0]}, ${color[1]}, ${color[2]}, ${a})`;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* `top` painted over `bottom` (source-over) as an opaque `rgb()` color. Returns `top` unchanged
|
|
55
|
+
* when either color cannot be parsed, and `top` itself when it is already opaque.
|
|
56
|
+
*/
|
|
57
|
+
export function composite(top, bottom) {
|
|
58
|
+
const t = parseColor(top);
|
|
59
|
+
const b = parseColor(bottom);
|
|
60
|
+
if (t === null || b === null || t[3] >= 1)
|
|
61
|
+
return top;
|
|
62
|
+
const a = t[3];
|
|
63
|
+
const mix = (i) => clampByte(t[i] * a + b[i] * (1 - a));
|
|
64
|
+
return `rgb(${mix(0)}, ${mix(1)}, ${mix(2)})`;
|
|
65
|
+
}
|
|
66
|
+
/** Glow falloff of the native renderer: (radius fraction, alpha) — a soft, roughly Gaussian profile. */
|
|
67
|
+
const GLOW_STOPS = [
|
|
68
|
+
[0, 1],
|
|
69
|
+
[0.3, 0.9],
|
|
70
|
+
[0.5, 0.6],
|
|
71
|
+
[0.7, 0.28],
|
|
72
|
+
[0.86, 0.08],
|
|
73
|
+
[1, 0],
|
|
74
|
+
];
|
|
75
|
+
const gradients = new Map();
|
|
76
|
+
/**
|
|
77
|
+
* A CSS radial gradient (parsed by AntV G) filling a circle with the native glow profile in
|
|
78
|
+
* `color`; `closest-side` makes the gradient end exactly at the circle's radius. Memoized: the
|
|
79
|
+
* string doubles as G's gradient cache key. Falls back to the plain color when unparsable.
|
|
80
|
+
*/
|
|
81
|
+
export function glowGradient(color) {
|
|
82
|
+
let g = gradients.get(color);
|
|
83
|
+
if (g !== undefined)
|
|
84
|
+
return g;
|
|
85
|
+
const rgba = parseColor(color);
|
|
86
|
+
if (rgba === null)
|
|
87
|
+
return color;
|
|
88
|
+
const stops = GLOW_STOPS.map(([at, alpha]) => `${withAlpha(rgba, alpha)} ${at * 100}%`);
|
|
89
|
+
g = `radial-gradient(circle closest-side, ${stops.join(', ')})`;
|
|
90
|
+
if (gradients.size > 256)
|
|
91
|
+
gradients.clear();
|
|
92
|
+
gradients.set(color, g);
|
|
93
|
+
return g;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=color.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color.js","sourceRoot":"","sources":["../src/color.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Numbers and names shared by the mapping, the custom node and the controller.
|
|
3
|
+
*
|
|
4
|
+
* Scene sizes marked "px" (stroke widths, gaps, font sizes, label offsets) become G6 world
|
|
5
|
+
* units: G6 scales everything with the camera, so they read as specified at zoom 1.
|
|
6
|
+
*/
|
|
7
|
+
/** Element type of the custom node registered by `createDiffGraph` (see node.ts). */
|
|
8
|
+
export declare const NODE_TYPE = "kgdiff-node";
|
|
9
|
+
/** Element state set by the hover behavior (a private name, so G6 theme states never apply). */
|
|
10
|
+
export declare const HOVER_STATE = "kgdiff-hover";
|
|
11
|
+
/** Key of the hover behavior and of the background plugin in the generated G6 options. */
|
|
12
|
+
export declare const HOVER_BEHAVIOR_KEY = "kgdiff-hover";
|
|
13
|
+
export declare const BACKGROUND_PLUGIN_KEY = "kgdiff-background";
|
|
14
|
+
/** Id prefixes of generated elements that are not scene nodes / edges. */
|
|
15
|
+
export declare const GLYPH_ID_PREFIX = "kgdiff-glyph-";
|
|
16
|
+
export declare const COLLAPSED_ID_PREFIX = "kgdiff-collapsed-";
|
|
17
|
+
export declare const ANCHOR_ID_PREFIX = "kgdiff-anchor-";
|
|
18
|
+
/**
|
|
19
|
+
* zIndex bands. G6 draws nodes and edges in one container ordered by zIndex, so every element
|
|
20
|
+
* gets an explicit band: collapsed pills < edges < glyph arcs < nodes < pivot dots (the native
|
|
21
|
+
* renderer's pass order). The scene's `z` (0–4) orders elements inside their band.
|
|
22
|
+
*/
|
|
23
|
+
export declare const Z: {
|
|
24
|
+
readonly collapsed: 0;
|
|
25
|
+
readonly edge: 10;
|
|
26
|
+
readonly glyph: 20;
|
|
27
|
+
readonly node: 30;
|
|
28
|
+
readonly pivot: 40;
|
|
29
|
+
/** Width of a band: scene `z` values are clamped to [0, span). */
|
|
30
|
+
readonly span: 10;
|
|
31
|
+
};
|
|
32
|
+
/** Arrowheads (docs/visual-language.md §Geometry): 7 long, 5 wide. */
|
|
33
|
+
export declare const ARROW_LENGTH = 7;
|
|
34
|
+
export declare const ARROW_WIDTH = 5;
|
|
35
|
+
/** Gap between an arrow tip and the node outline. */
|
|
36
|
+
export declare const ARROW_GAP = 1.5;
|
|
37
|
+
/** Text halo: stroked with the canvas background, 3 wide, alpha 0.9, round joins. */
|
|
38
|
+
export declare const TEXT_HALO_WIDTH = 3;
|
|
39
|
+
export declare const TEXT_HALO_ALPHA = 0.9;
|
|
40
|
+
/** Labels wider than this are truncated with an ellipsis (as in the native renderer). */
|
|
41
|
+
export declare const LABEL_MAX_WIDTH = 240;
|
|
42
|
+
/** Light-theme halo: an offset ring `r + 5` out, 2 wide. */
|
|
43
|
+
export declare const HALO_RING_OFFSET = 5;
|
|
44
|
+
export declare const HALO_RING_WIDTH = 2;
|
|
45
|
+
/** Dark-theme glows are never smaller than this multiple of r. */
|
|
46
|
+
export declare const GLOW_MIN_SCALE = 1.6;
|
|
47
|
+
/** Badges: 10 px mono, weight 600, 15 px pills with 5 px horizontal padding. */
|
|
48
|
+
export declare const BADGE_FONT_SIZE = 10;
|
|
49
|
+
export declare const BADGE_HEIGHT = 15;
|
|
50
|
+
export declare const BADGE_PAD_X = 5;
|
|
51
|
+
/** Collapsed-group pills (“+38 unchanged”): 20 px high, 8 px padding, 11 px / 500 text. */
|
|
52
|
+
export declare const PILL_HEIGHT = 20;
|
|
53
|
+
export declare const PILL_PAD_X = 8;
|
|
54
|
+
export declare const PILL_FONT_SIZE = 11;
|
|
55
|
+
/** Suggestion `?` mark: a 7-radius disc at the curve midpoint. */
|
|
56
|
+
export declare const MARK_RADIUS = 7;
|
|
57
|
+
/** Pivot dots get a background outline this wide so they stand out from what is under them. */
|
|
58
|
+
export declare const PIVOT_OUTLINE = 1.5;
|
|
59
|
+
/** Hovered strokes (STROKE.hoveredNode / STROKE.selectedEdge in @kgdiff/scene). */
|
|
60
|
+
export declare const HOVER_NODE_WIDTH = 2;
|
|
61
|
+
export declare const HOVER_EDGE_WIDTH = 2.5;
|
|
62
|
+
/** Edge label size used when hovering reveals a label the frame does not carry. */
|
|
63
|
+
export declare const EDGE_LABEL_SIZE = 10.5;
|
|
64
|
+
/** Extra hit width around thin edges so they can be hovered and clicked. */
|
|
65
|
+
export declare const EDGE_HIT_WIDTH = 6;
|
|
66
|
+
/**
|
|
67
|
+
* Replay sampling: each timeline window (chapter in chapter mode) is resolved at these fractions
|
|
68
|
+
* of its duration: end of the spotlight, middle of the transform, start of the settle, end.
|
|
69
|
+
* G6 animates linearly between consecutive samples.
|
|
70
|
+
*/
|
|
71
|
+
export declare const REPLAY_SAMPLES: readonly number[];
|
|
72
|
+
/**
|
|
73
|
+
* A draw is awaited at most its animation duration plus this long (ms): G6 settles a draw when
|
|
74
|
+
* its animation ends, and browsers pause animations in background pages.
|
|
75
|
+
*/
|
|
76
|
+
export declare const DRAW_GRACE_MS = 1000;
|
|
77
|
+
/** Replay steps shorter than this (ms, after speed scaling) are merged into the next one. */
|
|
78
|
+
export declare const MIN_STEP_MS = 16;
|
|
79
|
+
/** The camera follows a replay unit when less than this share of its bounds is in view. */
|
|
80
|
+
export declare const FOLLOW_VISIBLE_SHARE = 0.7;
|
|
81
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,qFAAqF;AACrF,eAAO,MAAM,SAAS,gBAAgB,CAAC;AAEvC,gGAAgG;AAChG,eAAO,MAAM,WAAW,iBAAiB,CAAC;AAE1C,0FAA0F;AAC1F,eAAO,MAAM,kBAAkB,iBAAiB,CAAC;AACjD,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;AAEzD,0EAA0E;AAC1E,eAAO,MAAM,eAAe,kBAAkB,CAAC;AAC/C,eAAO,MAAM,mBAAmB,sBAAsB,CAAC;AACvD,eAAO,MAAM,gBAAgB,mBAAmB,CAAC;AAEjD;;;;GAIG;AACH,eAAO,MAAM,CAAC;aACZ,SAAS,EAAE,CAAC;aACZ,IAAI,EAAE,EAAE;aACR,KAAK,EAAE,EAAE;aACT,IAAI,EAAE,EAAE;aACR,KAAK,EAAE,EAAE;IACT,kEAAkE;aAClE,IAAI,EAAE,EAAE;CACA,CAAC;AAEX,sEAAsE;AACtE,eAAO,MAAM,YAAY,IAAI,CAAC;AAC9B,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,qDAAqD;AACrD,eAAO,MAAM,SAAS,MAAM,CAAC;AAE7B,qFAAqF;AACrF,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,eAAO,MAAM,eAAe,MAAM,CAAC;AAEnC,yFAAyF;AACzF,eAAO,MAAM,eAAe,MAAM,CAAC;AAEnC,4DAA4D;AAC5D,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,kEAAkE;AAClE,eAAO,MAAM,cAAc,MAAM,CAAC;AAElC,gFAAgF;AAChF,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,eAAO,MAAM,YAAY,KAAK,CAAC;AAC/B,eAAO,MAAM,WAAW,IAAI,CAAC;AAE7B,2FAA2F;AAC3F,eAAO,MAAM,WAAW,KAAK,CAAC;AAC9B,eAAO,MAAM,UAAU,IAAI,CAAC;AAC5B,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC,kEAAkE;AAClE,eAAO,MAAM,WAAW,IAAI,CAAC;AAE7B,+FAA+F;AAC/F,eAAO,MAAM,aAAa,MAAM,CAAC;AAEjC,mFAAmF;AACnF,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,eAAO,MAAM,gBAAgB,MAAM,CAAC;AACpC,mFAAmF;AACnF,eAAO,MAAM,eAAe,OAAO,CAAC;AAEpC,4EAA4E;AAC5E,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,MAAM,EAAwC,CAAC;AACrF;;;GAGG;AACH,eAAO,MAAM,aAAa,OAAO,CAAC;AAClC,6FAA6F;AAC7F,eAAO,MAAM,WAAW,KAAK,CAAC;AAC9B,2FAA2F;AAC3F,eAAO,MAAM,oBAAoB,MAAM,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Numbers and names shared by the mapping, the custom node and the controller.
|
|
3
|
+
*
|
|
4
|
+
* Scene sizes marked "px" (stroke widths, gaps, font sizes, label offsets) become G6 world
|
|
5
|
+
* units: G6 scales everything with the camera, so they read as specified at zoom 1.
|
|
6
|
+
*/
|
|
7
|
+
/** Element type of the custom node registered by `createDiffGraph` (see node.ts). */
|
|
8
|
+
export const NODE_TYPE = 'kgdiff-node';
|
|
9
|
+
/** Element state set by the hover behavior (a private name, so G6 theme states never apply). */
|
|
10
|
+
export const HOVER_STATE = 'kgdiff-hover';
|
|
11
|
+
/** Key of the hover behavior and of the background plugin in the generated G6 options. */
|
|
12
|
+
export const HOVER_BEHAVIOR_KEY = 'kgdiff-hover';
|
|
13
|
+
export const BACKGROUND_PLUGIN_KEY = 'kgdiff-background';
|
|
14
|
+
/** Id prefixes of generated elements that are not scene nodes / edges. */
|
|
15
|
+
export const GLYPH_ID_PREFIX = 'kgdiff-glyph-';
|
|
16
|
+
export const COLLAPSED_ID_PREFIX = 'kgdiff-collapsed-';
|
|
17
|
+
export const ANCHOR_ID_PREFIX = 'kgdiff-anchor-';
|
|
18
|
+
/**
|
|
19
|
+
* zIndex bands. G6 draws nodes and edges in one container ordered by zIndex, so every element
|
|
20
|
+
* gets an explicit band: collapsed pills < edges < glyph arcs < nodes < pivot dots (the native
|
|
21
|
+
* renderer's pass order). The scene's `z` (0–4) orders elements inside their band.
|
|
22
|
+
*/
|
|
23
|
+
export const Z = {
|
|
24
|
+
collapsed: 0,
|
|
25
|
+
edge: 10,
|
|
26
|
+
glyph: 20,
|
|
27
|
+
node: 30,
|
|
28
|
+
pivot: 40,
|
|
29
|
+
/** Width of a band: scene `z` values are clamped to [0, span). */
|
|
30
|
+
span: 10,
|
|
31
|
+
};
|
|
32
|
+
/** Arrowheads (docs/visual-language.md §Geometry): 7 long, 5 wide. */
|
|
33
|
+
export const ARROW_LENGTH = 7;
|
|
34
|
+
export const ARROW_WIDTH = 5;
|
|
35
|
+
/** Gap between an arrow tip and the node outline. */
|
|
36
|
+
export const ARROW_GAP = 1.5;
|
|
37
|
+
/** Text halo: stroked with the canvas background, 3 wide, alpha 0.9, round joins. */
|
|
38
|
+
export const TEXT_HALO_WIDTH = 3;
|
|
39
|
+
export const TEXT_HALO_ALPHA = 0.9;
|
|
40
|
+
/** Labels wider than this are truncated with an ellipsis (as in the native renderer). */
|
|
41
|
+
export const LABEL_MAX_WIDTH = 240;
|
|
42
|
+
/** Light-theme halo: an offset ring `r + 5` out, 2 wide. */
|
|
43
|
+
export const HALO_RING_OFFSET = 5;
|
|
44
|
+
export const HALO_RING_WIDTH = 2;
|
|
45
|
+
/** Dark-theme glows are never smaller than this multiple of r. */
|
|
46
|
+
export const GLOW_MIN_SCALE = 1.6;
|
|
47
|
+
/** Badges: 10 px mono, weight 600, 15 px pills with 5 px horizontal padding. */
|
|
48
|
+
export const BADGE_FONT_SIZE = 10;
|
|
49
|
+
export const BADGE_HEIGHT = 15;
|
|
50
|
+
export const BADGE_PAD_X = 5;
|
|
51
|
+
/** Collapsed-group pills (“+38 unchanged”): 20 px high, 8 px padding, 11 px / 500 text. */
|
|
52
|
+
export const PILL_HEIGHT = 20;
|
|
53
|
+
export const PILL_PAD_X = 8;
|
|
54
|
+
export const PILL_FONT_SIZE = 11;
|
|
55
|
+
/** Suggestion `?` mark: a 7-radius disc at the curve midpoint. */
|
|
56
|
+
export const MARK_RADIUS = 7;
|
|
57
|
+
/** Pivot dots get a background outline this wide so they stand out from what is under them. */
|
|
58
|
+
export const PIVOT_OUTLINE = 1.5;
|
|
59
|
+
/** Hovered strokes (STROKE.hoveredNode / STROKE.selectedEdge in @kgdiff/scene). */
|
|
60
|
+
export const HOVER_NODE_WIDTH = 2;
|
|
61
|
+
export const HOVER_EDGE_WIDTH = 2.5;
|
|
62
|
+
/** Edge label size used when hovering reveals a label the frame does not carry. */
|
|
63
|
+
export const EDGE_LABEL_SIZE = 10.5;
|
|
64
|
+
/** Extra hit width around thin edges so they can be hovered and clicked. */
|
|
65
|
+
export const EDGE_HIT_WIDTH = 6;
|
|
66
|
+
/**
|
|
67
|
+
* Replay sampling: each timeline window (chapter in chapter mode) is resolved at these fractions
|
|
68
|
+
* of its duration: end of the spotlight, middle of the transform, start of the settle, end.
|
|
69
|
+
* G6 animates linearly between consecutive samples.
|
|
70
|
+
*/
|
|
71
|
+
export const REPLAY_SAMPLES = Object.freeze([0.18, 0.5, 0.82, 1]);
|
|
72
|
+
/**
|
|
73
|
+
* A draw is awaited at most its animation duration plus this long (ms): G6 settles a draw when
|
|
74
|
+
* its animation ends, and browsers pause animations in background pages.
|
|
75
|
+
*/
|
|
76
|
+
export const DRAW_GRACE_MS = 1000;
|
|
77
|
+
/** Replay steps shorter than this (ms, after speed scaling) are merged into the next one. */
|
|
78
|
+
export const MIN_STEP_MS = 16;
|
|
79
|
+
/** The camera follows a replay unit when less than this share of its bounds is in view. */
|
|
80
|
+
export const FOLLOW_VISIBLE_SHARE = 0.7;
|
|
81
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,qFAAqF;AACrF,MAAM,CAAC,MAAM,SAAS,GAAG,aAAa,CAAC;AAEvC,gGAAgG;AAChG,MAAM,CAAC,MAAM,WAAW,GAAG,cAAc,CAAC;AAE1C,0FAA0F;AAC1F,MAAM,CAAC,MAAM,kBAAkB,GAAG,cAAc,CAAC;AACjD,MAAM,CAAC,MAAM,qBAAqB,GAAG,mBAAmB,CAAC;AAEzD,0EAA0E;AAC1E,MAAM,CAAC,MAAM,eAAe,GAAG,eAAe,CAAC;AAC/C,MAAM,CAAC,MAAM,mBAAmB,GAAG,mBAAmB,CAAC;AACvD,MAAM,CAAC,MAAM,gBAAgB,GAAG,gBAAgB,CAAC;AAEjD;;;;GAIG;AACH,MAAM,CAAC,MAAM,CAAC,GAAG;IACf,SAAS,EAAE,CAAC;IACZ,IAAI,EAAE,EAAE;IACR,KAAK,EAAE,EAAE;IACT,IAAI,EAAE,EAAE;IACR,KAAK,EAAE,EAAE;IACT,kEAAkE;IAClE,IAAI,EAAE,EAAE;CACA,CAAC;AAEX,sEAAsE;AACtE,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC;AAC9B,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC;AAC7B,qDAAqD;AACrD,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,CAAC;AAE7B,qFAAqF;AACrF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC;AACjC,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,CAAC;AAEnC,yFAAyF;AACzF,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,CAAC;AAEnC,4DAA4D;AAC5D,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAClC,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC;AACjC,kEAAkE;AAClE,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,CAAC;AAElC,gFAAgF;AAChF,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAC;AAClC,MAAM,CAAC,MAAM,YAAY,GAAG,EAAE,CAAC;AAC/B,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC;AAE7B,2FAA2F;AAC3F,MAAM,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAC9B,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC;AAC5B,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AAEjC,kEAAkE;AAClE,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC;AAE7B,+FAA+F;AAC/F,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC;AAEjC,mFAAmF;AACnF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAClC,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,CAAC;AACpC,mFAAmF;AACnF,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC;AAEpC,4EAA4E;AAC5E,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC;AAEhC;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAsB,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AACrF;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAC;AAClC,6FAA6F;AAC7F,MAAM,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAC9B,2FAA2F;AAC3F,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,CAAC"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The controller behind `createDiffGraph`: owns the view state and the resolver, turns every
|
|
3
|
+
* view change into one resolved frame → mapped data → a patch of only what changed (`addData` /
|
|
4
|
+
* `updateData` with the changed keys / `removeData`) + `graph.draw()` (G6 animates the
|
|
5
|
+
* difference), and keeps G6's level of detail in step with the camera.
|
|
6
|
+
*
|
|
7
|
+
* All graph mutations go through one queue, so G6 never receives a new frame while it is still
|
|
8
|
+
* starting the previous draw. View requests (layer, selection, theme, LOD) that pile up while a
|
|
9
|
+
* draw runs collapse into one job, latest wins. Every queued task, draw and camera move is raced
|
|
10
|
+
* against `destroy()`, so no promise outlives the graph. The graph is reached through
|
|
11
|
+
* `GraphLike`, the subset of G6's `Graph` used here, so the logic is testable with a fake.
|
|
12
|
+
*/
|
|
13
|
+
import type { FitViewOptions, GraphData, GraphOptions, ID, IEvent, Point, ViewportAnimationEffectTiming } from '@antv/g6';
|
|
14
|
+
import { type Layer, type Scene, type Theme, type ViewState } from '@kgdiff/scene';
|
|
15
|
+
/** The part of G6's `Graph` the controller uses. */
|
|
16
|
+
export interface GraphLike {
|
|
17
|
+
readonly destroyed: boolean;
|
|
18
|
+
render(): Promise<void>;
|
|
19
|
+
draw(): Promise<void>;
|
|
20
|
+
addData(data: GraphData): void;
|
|
21
|
+
updateData(data: GraphData): void;
|
|
22
|
+
removeData(ids: {
|
|
23
|
+
nodes?: ID[];
|
|
24
|
+
edges?: ID[];
|
|
25
|
+
}): void;
|
|
26
|
+
setOptions(options: GraphOptions): void;
|
|
27
|
+
fitView(options?: FitViewOptions, animation?: ViewportAnimationEffectTiming): Promise<void>;
|
|
28
|
+
focusElement(id: ID | ID[], animation?: ViewportAnimationEffectTiming): Promise<void>;
|
|
29
|
+
getZoom(): number;
|
|
30
|
+
getSize(): [number, number];
|
|
31
|
+
getCanvasByViewport(point: Point): Point;
|
|
32
|
+
updatePlugin(plugin: {
|
|
33
|
+
key: string;
|
|
34
|
+
[key: string]: unknown;
|
|
35
|
+
}): void;
|
|
36
|
+
on(eventName: string, callback: (event: IEvent) => void): unknown;
|
|
37
|
+
off(eventName: string, callback: (event: IEvent) => void): unknown;
|
|
38
|
+
destroy(): void;
|
|
39
|
+
}
|
|
40
|
+
export interface ControllerOptions {
|
|
41
|
+
readonly view?: Partial<ViewState>;
|
|
42
|
+
readonly g6?: Readonly<Record<string, unknown>>;
|
|
43
|
+
readonly interactive?: boolean;
|
|
44
|
+
}
|
|
45
|
+
export interface ControllerDeps<G extends GraphLike> {
|
|
46
|
+
readonly createGraph: (options: GraphOptions) => G;
|
|
47
|
+
/** Resolves after `ms` (replay pacing when G6 animations are off). */
|
|
48
|
+
readonly wait: (ms: number) => Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Resolves after `ms`: the longest a draw is awaited. Browsers pause animation frames in
|
|
51
|
+
* background pages, and G6's draw promise only settles when its animation has run.
|
|
52
|
+
*/
|
|
53
|
+
readonly deadline: (ms: number) => Promise<void>;
|
|
54
|
+
}
|
|
55
|
+
export declare class DiffGraphControllerImpl<G extends GraphLike> {
|
|
56
|
+
private readonly options;
|
|
57
|
+
private readonly deps;
|
|
58
|
+
private graphRef;
|
|
59
|
+
private scene;
|
|
60
|
+
private resolver;
|
|
61
|
+
private view;
|
|
62
|
+
private frame;
|
|
63
|
+
private data;
|
|
64
|
+
private meta;
|
|
65
|
+
private lod;
|
|
66
|
+
private lodZoom;
|
|
67
|
+
private job;
|
|
68
|
+
private queue;
|
|
69
|
+
private replay;
|
|
70
|
+
private destroyed;
|
|
71
|
+
private readonly dead;
|
|
72
|
+
private kill;
|
|
73
|
+
private readonly handlers;
|
|
74
|
+
constructor(scene: Scene, options: ControllerOptions, deps: ControllerDeps<G>);
|
|
75
|
+
get graph(): G;
|
|
76
|
+
/** Create, render and fit the graph. */
|
|
77
|
+
start(container: HTMLElement | string): Promise<void>;
|
|
78
|
+
setLayer(layer: Exclude<Layer, 'replay'>): Promise<void>;
|
|
79
|
+
selectHunk(index: number): Promise<void>;
|
|
80
|
+
playReplay(options?: {
|
|
81
|
+
readonly speed?: number;
|
|
82
|
+
}): Promise<void>;
|
|
83
|
+
/** Stop a running replay (its promise resolves at once); no-op otherwise. */
|
|
84
|
+
stop(): void;
|
|
85
|
+
setTheme(theme: Theme): Promise<void>;
|
|
86
|
+
destroy(): void;
|
|
87
|
+
private assertAlive;
|
|
88
|
+
private listen;
|
|
89
|
+
/**
|
|
90
|
+
* Run `task` after the queued ones. Rejects at once after `destroy()`; a task still queued or
|
|
91
|
+
* running when the graph is destroyed settles (resolves) instead of waiting on G6.
|
|
92
|
+
*/
|
|
93
|
+
private enqueue;
|
|
94
|
+
/**
|
|
95
|
+
* Schedule one view update: resolve the current view, patch the graph, optionally switch the
|
|
96
|
+
* theme and pan. Requests made while an update is waiting in the queue join it (latest wins),
|
|
97
|
+
* so a burst of clicks costs one G6 draw.
|
|
98
|
+
*/
|
|
99
|
+
private request;
|
|
100
|
+
private runJob;
|
|
101
|
+
/** Failures of fire-and-forget work (event handlers): surfaced, unless the graph is gone. */
|
|
102
|
+
private report;
|
|
103
|
+
/** G6's global animation option for one draw: false, or the theme easing over `duration`. */
|
|
104
|
+
private timing;
|
|
105
|
+
private viewportTiming;
|
|
106
|
+
/** Resolve the current view and map it for `zoom`; records what was mapped. */
|
|
107
|
+
private map;
|
|
108
|
+
/** Map the current view and hand G6 only the difference to what it shows. */
|
|
109
|
+
private apply;
|
|
110
|
+
private select;
|
|
111
|
+
/** Ids of the hunks' nodes that are currently drawn. */
|
|
112
|
+
private hunkNodeIds;
|
|
113
|
+
/**
|
|
114
|
+
* Pan to the hunks' nodes. Called before the next patch, so G6 measures elements that still
|
|
115
|
+
* exist; resolves when the camera has arrived — or when the graph is destroyed, since G6 then
|
|
116
|
+
* cancels the camera animation without ever settling its promise.
|
|
117
|
+
*/
|
|
118
|
+
private focus;
|
|
119
|
+
/** Share of the hunks' bounds inside the viewport, in [0, 1]. */
|
|
120
|
+
private visibleShare;
|
|
121
|
+
private runReplay;
|
|
122
|
+
private onTransform;
|
|
123
|
+
private onElementClick;
|
|
124
|
+
private onCanvasClick;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"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"}
|