@kgdiff/g6 0.0.0-stage → 0.2.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.
Files changed (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +149 -2
  3. package/dist/color.d.ts +24 -0
  4. package/dist/color.d.ts.map +1 -0
  5. package/dist/color.js +95 -0
  6. package/dist/color.js.map +1 -0
  7. package/dist/constants.d.ts +81 -0
  8. package/dist/constants.d.ts.map +1 -0
  9. package/dist/constants.js +81 -0
  10. package/dist/constants.js.map +1 -0
  11. package/dist/controller.d.ts +126 -0
  12. package/dist/controller.d.ts.map +1 -0
  13. package/dist/controller.js +422 -0
  14. package/dist/controller.js.map +1 -0
  15. package/dist/geometry.d.ts +19 -0
  16. package/dist/geometry.d.ts.map +1 -0
  17. package/dist/geometry.js +43 -0
  18. package/dist/geometry.js.map +1 -0
  19. package/dist/index.d.ts +101 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +99 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/locator.d.ts +24 -0
  24. package/dist/locator.d.ts.map +1 -0
  25. package/dist/locator.js +69 -0
  26. package/dist/locator.js.map +1 -0
  27. package/dist/map-context.d.ts +70 -0
  28. package/dist/map-context.d.ts.map +1 -0
  29. package/dist/map-context.js +44 -0
  30. package/dist/map-context.js.map +1 -0
  31. package/dist/map-edge.d.ts +19 -0
  32. package/dist/map-edge.d.ts.map +1 -0
  33. package/dist/map-edge.js +399 -0
  34. package/dist/map-edge.js.map +1 -0
  35. package/dist/map-node.d.ts +10 -0
  36. package/dist/map-node.d.ts.map +1 -0
  37. package/dist/map-node.js +243 -0
  38. package/dist/map-node.js.map +1 -0
  39. package/dist/map.d.ts +47 -0
  40. package/dist/map.d.ts.map +1 -0
  41. package/dist/map.js +92 -0
  42. package/dist/map.js.map +1 -0
  43. package/dist/node.d.ts +68 -0
  44. package/dist/node.d.ts.map +1 -0
  45. package/dist/node.js +189 -0
  46. package/dist/node.js.map +1 -0
  47. package/dist/options.d.ts +33 -0
  48. package/dist/options.d.ts.map +1 -0
  49. package/dist/options.js +172 -0
  50. package/dist/options.js.map +1 -0
  51. package/dist/patch.d.ts +45 -0
  52. package/dist/patch.d.ts.map +1 -0
  53. package/dist/patch.js +120 -0
  54. package/dist/patch.js.map +1 -0
  55. package/dist/replay.d.ts +20 -0
  56. package/dist/replay.d.ts.map +1 -0
  57. package/dist/replay.js +36 -0
  58. package/dist/replay.js.map +1 -0
  59. package/package.json +54 -3
  60. package/src/color.ts +101 -0
  61. package/src/constants.ts +96 -0
  62. package/src/controller.ts +517 -0
  63. package/src/geometry.ts +44 -0
  64. package/src/index.ts +140 -0
  65. package/src/locator.ts +72 -0
  66. package/src/map-context.ts +119 -0
  67. package/src/map-edge.ts +481 -0
  68. package/src/map-node.ts +272 -0
  69. package/src/map.ts +130 -0
  70. package/src/node.ts +240 -0
  71. package/src/options.ts +215 -0
  72. package/src/patch.ts +133 -0
  73. package/src/replay.ts +59 -0
@@ -0,0 +1,172 @@
1
+ import { BACKGROUND_PLUGIN_KEY, EDGE_LABEL_SIZE, HOVER_BEHAVIOR_KEY, HOVER_EDGE_WIDTH, HOVER_NODE_WIDTH, HOVER_STATE, NODE_TYPE, TEXT_HALO_ALPHA, Z, } from './constants.js';
2
+ import { kgdiffData } from './map.js';
3
+ const CHANGE_FAMILIES = new Set([
4
+ 'added',
5
+ 'removed',
6
+ 'content',
7
+ 'structure',
8
+ 'identity',
9
+ ]);
10
+ function num(v, fallback) {
11
+ return typeof v === 'number' && Number.isFinite(v) ? v : fallback;
12
+ }
13
+ /**
14
+ * Hover on a node, as the resolver does it: a 2-wide stroke in the family's strong color
15
+ * (secondary text color for context), drawn on top, label revealed even below its LOD zoom.
16
+ */
17
+ export function hoverNodeStyle(datum, theme) {
18
+ const d = kgdiffData(datum.data);
19
+ if (d === null || d.kgdiff !== 'node')
20
+ return {};
21
+ const style = datum.style ?? {};
22
+ const fam = d.family;
23
+ const opacity = Math.max(num(style.labelOpacity, 0), num(style.opacity, 1));
24
+ return {
25
+ lineWidth: HOVER_NODE_WIDTH,
26
+ stroke: d.changed && CHANGE_FAMILIES.has(fam)
27
+ ? theme.family[fam].strong
28
+ : theme.text.secondary,
29
+ zIndex: Z.node + Z.span - 1,
30
+ label: typeof style.labelText === 'string' && style.labelText.length > 0,
31
+ labelOpacity: opacity,
32
+ labelStrokeOpacity: TEXT_HALO_ALPHA * opacity,
33
+ };
34
+ }
35
+ /** Hover on an edge: at least 2.5 wide, arrowhead, on top, its label shown. */
36
+ export function hoverEdgeStyle(datum, theme) {
37
+ const d = kgdiffData(datum.data);
38
+ if (d === null || (d.kgdiff !== 'edge' && d.kgdiff !== 'glyph'))
39
+ return {};
40
+ const style = datum.style ?? {};
41
+ const own = typeof style.labelText === 'string' && style.labelText.length > 0;
42
+ const text = own ? style.labelText : d.label;
43
+ const opacity = Math.max(num(style.labelOpacity, 0), num(style.opacity, 1));
44
+ return {
45
+ lineWidth: Math.max(num(style.lineWidth, 1), HOVER_EDGE_WIDTH),
46
+ endArrow: datum.source !== datum.target,
47
+ zIndex: (d.kgdiff === 'glyph' ? Z.glyph : Z.edge) + Z.span - 1,
48
+ label: text.length > 0,
49
+ labelText: text,
50
+ labelFill: own ? style.labelFill : theme.neutral.label,
51
+ labelFontSize: own ? num(style.labelFontSize, EDGE_LABEL_SIZE) : EDGE_LABEL_SIZE,
52
+ labelOpacity: opacity,
53
+ labelStrokeOpacity: TEXT_HALO_ALPHA * opacity,
54
+ };
55
+ }
56
+ /** Update animations: what interpolates when a frame replaces the previous one. */
57
+ const NODE_ANIMATION = {
58
+ enter: 'fade',
59
+ exit: 'fade',
60
+ show: 'fade',
61
+ hide: 'fade',
62
+ update: [
63
+ {
64
+ fields: [
65
+ 'x',
66
+ 'y',
67
+ 'size',
68
+ 'fill',
69
+ 'stroke',
70
+ 'lineWidth',
71
+ 'opacity',
72
+ 'labelOpacity',
73
+ 'ringOpacity',
74
+ 'haloStrokeOpacity',
75
+ 'sublabelOpacity',
76
+ ],
77
+ },
78
+ ],
79
+ translate: [{ fields: ['x', 'y'] }],
80
+ };
81
+ const EDGE_ANIMATION = {
82
+ enter: 'fade',
83
+ exit: 'fade',
84
+ show: 'fade',
85
+ hide: 'fade',
86
+ update: [
87
+ {
88
+ fields: [
89
+ 'sourceNode',
90
+ 'targetNode',
91
+ 'stroke',
92
+ 'lineWidth',
93
+ 'opacity',
94
+ 'labelOpacity',
95
+ 'curveOffset',
96
+ ],
97
+ },
98
+ ],
99
+ translate: [{ fields: ['sourceNode', 'targetNode'] }],
100
+ };
101
+ export function generatedOptions(input) {
102
+ const theme = input.theme;
103
+ return {
104
+ container: input.container,
105
+ autoResize: true,
106
+ theme: false,
107
+ padding: 32,
108
+ data: input.data,
109
+ node: {
110
+ type: NODE_TYPE,
111
+ state: { [HOVER_STATE]: (datum) => hoverNodeStyle(datum, theme()) },
112
+ animation: NODE_ANIMATION,
113
+ },
114
+ edge: {
115
+ state: { [HOVER_STATE]: (datum) => hoverEdgeStyle(datum, theme()) },
116
+ animation: EDGE_ANIMATION,
117
+ },
118
+ behaviors: input.interactive
119
+ ? [
120
+ 'zoom-canvas',
121
+ 'drag-canvas',
122
+ {
123
+ type: 'hover-activate',
124
+ key: HOVER_BEHAVIOR_KEY,
125
+ state: HOVER_STATE,
126
+ degree: 0,
127
+ enable: input.hoverable,
128
+ },
129
+ ]
130
+ : [],
131
+ plugins: [
132
+ { type: 'background', key: BACKGROUND_PLUGIN_KEY, background: theme().canvas.background },
133
+ ],
134
+ };
135
+ }
136
+ const CONCAT = new Set(['behaviors', 'plugins', 'transforms']);
137
+ function isPlain(v) {
138
+ return (v !== null &&
139
+ typeof v === 'object' &&
140
+ !Array.isArray(v) &&
141
+ Object.getPrototypeOf(v) === Object.prototype);
142
+ }
143
+ function deepMerge(a, b) {
144
+ const out = { ...a };
145
+ for (const [key, value] of Object.entries(b)) {
146
+ const base = out[key];
147
+ out[key] = isPlain(base) && isPlain(value) ? deepMerge(base, value) : value;
148
+ }
149
+ return out;
150
+ }
151
+ /**
152
+ * User options over generated ones: `behaviors`, `plugins` and `transforms` are appended to the
153
+ * generated lists; plain objects (`node`, `edge`, `node.state`…) merge recursively; everything
154
+ * else (arrays, functions, primitives) from `user` replaces the generated value.
155
+ */
156
+ export function mergeG6Options(generated, user) {
157
+ if (user === undefined)
158
+ return generated;
159
+ const out = { ...generated };
160
+ for (const [key, value] of Object.entries(user)) {
161
+ const base = out[key];
162
+ if (CONCAT.has(key) && Array.isArray(base) && Array.isArray(value))
163
+ out[key] = [...base, ...value];
164
+ else if (isPlain(base) && isPlain(value))
165
+ out[key] = deepMerge(base, value);
166
+ else
167
+ out[key] = value;
168
+ }
169
+ // The merged object is GraphOptions plus whatever the caller added; G6 validates it.
170
+ return out;
171
+ }
172
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Frame-to-frame patches. G6's `setData` re-reads and compares the whole graph and merges full
3
+ * styles into every changed element; sending G6 only what changed — new elements, removed ids,
4
+ * and for updated elements just the changed style keys — keeps its data layer proportional to
5
+ * the change. Pure.
6
+ */
7
+ import type { EdgeData, ID, NodeData } from '@antv/g6';
8
+ export type Datum = NodeData | EdgeData;
9
+ export interface Patch {
10
+ readonly add: {
11
+ nodes: NodeData[];
12
+ edges: EdgeData[];
13
+ };
14
+ /** Partial data: `id` (edges also `source` / `target`), the changed style keys, changed `data`. */
15
+ readonly update: {
16
+ nodes: NodeData[];
17
+ edges: EdgeData[];
18
+ };
19
+ readonly remove: {
20
+ nodes: ID[];
21
+ edges: ID[];
22
+ };
23
+ /**
24
+ * Elements whose id switched between node and edge (only possible through id collisions):
25
+ * added after `remove`, since G6 rejects a duplicate id.
26
+ */
27
+ readonly readd: {
28
+ nodes: NodeData[];
29
+ edges: EdgeData[];
30
+ };
31
+ /** Number of elements added, updated, removed or re-added. */
32
+ readonly size: number;
33
+ }
34
+ /** Structural equality for style values (primitives, arrays, plain objects such as badges). */
35
+ export declare function deepEqual(a: unknown, b: unknown): boolean;
36
+ /**
37
+ * The patch turning `prev` (by id) into `next`. Edge updates always carry `zIndex`: without a
38
+ * numeric one, G6's `updateData` recomputes an edge's zIndex from its endpoints. O(elements ×
39
+ * style keys).
40
+ */
41
+ export declare function diffData(prev: ReadonlyMap<ID, Datum>, next: {
42
+ readonly nodes: readonly NodeData[];
43
+ readonly edges: readonly EdgeData[];
44
+ }): Patch;
45
+ //# sourceMappingURL=patch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch.d.ts","sourceRoot":"","sources":["../src/patch.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEvD,MAAM,MAAM,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAExC,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,GAAG,EAAE;QAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;QAAC,KAAK,EAAE,QAAQ,EAAE,CAAA;KAAE,CAAC;IACvD,mGAAmG;IACnG,QAAQ,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;QAAC,KAAK,EAAE,QAAQ,EAAE,CAAA;KAAE,CAAC;IAC1D,QAAQ,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,EAAE,EAAE,CAAC;QAAC,KAAK,EAAE,EAAE,EAAE,CAAA;KAAE,CAAC;IAC9C;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;QAAC,KAAK,EAAE,QAAQ,EAAE,CAAA;KAAE,CAAC;IACzD,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,+FAA+F;AAC/F,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAUzD;AA6BD;;;;GAIG;AACH,wBAAgB,QAAQ,CACtB,IAAI,EAAE,WAAW,CAAC,EAAE,EAAE,KAAK,CAAC,EAC5B,IAAI,EAAE;IAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAA;CAAE,GACjF,KAAK,CA4DP"}
package/dist/patch.js ADDED
@@ -0,0 +1,120 @@
1
+ /** Structural equality for style values (primitives, arrays, plain objects such as badges). */
2
+ export function deepEqual(a, b) {
3
+ if (a === b)
4
+ return true;
5
+ if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null)
6
+ return false;
7
+ if (Array.isArray(a) !== Array.isArray(b))
8
+ return false;
9
+ const ka = Object.keys(a);
10
+ const kb = Object.keys(b);
11
+ if (ka.length !== kb.length)
12
+ return false;
13
+ const ra = a;
14
+ const rb = b;
15
+ return ka.every((k) => deepEqual(ra[k], rb[k]));
16
+ }
17
+ /** Keys of `next` whose values differ from `prev` (structurally), or null when none do. */
18
+ function changedKeys(prev, next) {
19
+ if (next === undefined)
20
+ return null;
21
+ let out = null;
22
+ for (const key of Object.keys(next)) {
23
+ const value = next[key];
24
+ const old = prev?.[key];
25
+ if (old === value || (typeof value === 'object' && deepEqual(old, value)))
26
+ continue;
27
+ if (out === null)
28
+ out = {};
29
+ out[key] = value;
30
+ }
31
+ return out;
32
+ }
33
+ function shallowEqual(a, b) {
34
+ if (a === b)
35
+ return true;
36
+ if (a === undefined || b === undefined)
37
+ return false;
38
+ const ka = Object.keys(a);
39
+ return ka.length === Object.keys(b).length && ka.every((k) => a[k] === b[k]);
40
+ }
41
+ /**
42
+ * The patch turning `prev` (by id) into `next`. Edge updates always carry `zIndex`: without a
43
+ * numeric one, G6's `updateData` recomputes an edge's zIndex from its endpoints. O(elements ×
44
+ * style keys).
45
+ */
46
+ export function diffData(prev, next) {
47
+ const add = { nodes: [], edges: [] };
48
+ const update = { nodes: [], edges: [] };
49
+ const remove = { nodes: [], edges: [] };
50
+ const readd = { nodes: [], edges: [] };
51
+ const seen = new Set();
52
+ for (const node of next.nodes) {
53
+ const old = prev.get(node.id);
54
+ if (old === undefined || 'source' in old) {
55
+ if (old === undefined)
56
+ add.nodes.push(node);
57
+ else
58
+ readd.nodes.push(node);
59
+ continue;
60
+ }
61
+ seen.add(node.id);
62
+ const style = changedKeys(old.style, node.style);
63
+ const dataChanged = !shallowEqual(old.data, node.data);
64
+ const typeChanged = old.type !== node.type;
65
+ if (style === null && !dataChanged && !typeChanged)
66
+ continue;
67
+ const patch = { id: node.id };
68
+ if (style !== null)
69
+ patch.style = style;
70
+ if (dataChanged)
71
+ patch.data = node.data;
72
+ if (typeChanged)
73
+ patch.type = node.type;
74
+ update.nodes.push(patch);
75
+ }
76
+ for (const edge of next.edges) {
77
+ const id = edge.id;
78
+ const old = prev.get(id);
79
+ if (old === undefined || !('source' in old)) {
80
+ if (old === undefined)
81
+ add.edges.push(edge);
82
+ else
83
+ readd.edges.push(edge);
84
+ continue;
85
+ }
86
+ seen.add(id);
87
+ const style = changedKeys(old.style, edge.style);
88
+ const dataChanged = !shallowEqual(old.data, edge.data);
89
+ const typeChanged = old.type !== edge.type;
90
+ const endsChanged = old.source !== edge.source || old.target !== edge.target;
91
+ if (style === null && !dataChanged && !typeChanged && !endsChanged)
92
+ continue;
93
+ const patch = { id, source: edge.source, target: edge.target };
94
+ patch.style = { ...style, zIndex: edge.style?.zIndex };
95
+ if (dataChanged)
96
+ patch.data = edge.data;
97
+ if (typeChanged)
98
+ patch.type = edge.type;
99
+ update.edges.push(patch);
100
+ }
101
+ for (const [id, old] of prev) {
102
+ // Kept elements were marked seen; added ones were not in `prev` at all.
103
+ if (seen.has(id))
104
+ continue;
105
+ if ('source' in old)
106
+ remove.edges.push(id);
107
+ else
108
+ remove.nodes.push(id);
109
+ }
110
+ const size = add.nodes.length +
111
+ add.edges.length +
112
+ update.nodes.length +
113
+ update.edges.length +
114
+ remove.nodes.length +
115
+ remove.edges.length +
116
+ readd.nodes.length +
117
+ readd.edges.length;
118
+ return { add, update, remove, readd, size };
119
+ }
120
+ //# sourceMappingURL=patch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch.js","sourceRoot":"","sources":["../src/patch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Replay plan: the timeline times at which the controller resolves a frame and hands it to G6,
3
+ * which animates linearly from one sample to the next. Pure.
4
+ */
5
+ import type { Timeline } from '@kgdiff/scene';
6
+ export interface ReplayStep {
7
+ /** Timeline time to resolve (ms at 1×). */
8
+ readonly t: number;
9
+ /** Time since the previous step (ms at 1×); the first step counts from 0. */
10
+ readonly dt: number;
11
+ /** On the first step of a unit (window, or chapter in chapter mode): its hunks, for the camera. */
12
+ readonly focus: readonly number[] | null;
13
+ }
14
+ /**
15
+ * Sample every unit of `timeline` at `samples` (fractions of its duration). Units are the
16
+ * hunk windows, or the chapters in chapter mode (whose windows overlap and play together).
17
+ * Steps are sorted by time; samples closer than 1e-6 ms are merged. O(U log U).
18
+ */
19
+ export declare function planReplay(timeline: Timeline, samples?: readonly number[]): ReplayStep[];
20
+ //# sourceMappingURL=replay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAG9C,MAAM,WAAW,UAAU;IACzB,2CAA2C;IAC3C,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,mGAAmG;IACnG,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;CAC1C;AAQD;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,QAAQ,EAAE,QAAQ,EAClB,OAAO,GAAE,SAAS,MAAM,EAAmB,GAC1C,UAAU,EAAE,CA4Bd"}
package/dist/replay.js ADDED
@@ -0,0 +1,36 @@
1
+ import { REPLAY_SAMPLES } from './constants.js';
2
+ /**
3
+ * Sample every unit of `timeline` at `samples` (fractions of its duration). Units are the
4
+ * hunk windows, or the chapters in chapter mode (whose windows overlap and play together).
5
+ * Steps are sorted by time; samples closer than 1e-6 ms are merged. O(U log U).
6
+ */
7
+ export function planReplay(timeline, samples = REPLAY_SAMPLES) {
8
+ const units = timeline.mode === 'chapters'
9
+ ? timeline.chapters.map((c) => ({ start: c.start, end: c.end, hunks: c.hunks }))
10
+ : timeline.windows.map((w) => ({ start: w.start, end: w.end, hunks: [w.hunk] }));
11
+ const raw = [];
12
+ units.forEach((u, k) => {
13
+ if (!Number.isFinite(u.start) || !Number.isFinite(u.end))
14
+ return;
15
+ const length = Math.max(0, u.end - u.start);
16
+ samples.forEach((f, s) => {
17
+ raw.push({ t: u.start + f * length, focus: s === 0 ? u.hunks : null, order: k });
18
+ });
19
+ });
20
+ raw.sort((a, b) => a.t - b.t || a.order - b.order);
21
+ const steps = [];
22
+ let prev = 0;
23
+ for (const r of raw) {
24
+ const last = steps[steps.length - 1];
25
+ if (last !== undefined && r.t - last.t < 1e-6) {
26
+ if (r.focus !== null && last.focus === null) {
27
+ steps[steps.length - 1] = { ...last, focus: r.focus };
28
+ }
29
+ continue;
30
+ }
31
+ steps.push({ t: r.t, dt: Math.max(0, r.t - prev), focus: r.focus });
32
+ prev = r.t;
33
+ }
34
+ return steps;
35
+ }
36
+ //# sourceMappingURL=replay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay.js","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAiBhD;;;;GAIG;AACH,MAAM,UAAU,UAAU,CACxB,QAAkB,EAClB,OAAO,GAAsB,cAAc;IAE3C,MAAM,KAAK,GACT,QAAQ,CAAC,IAAI,KAAK,UAAU;QAC1B,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QAChF,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IACrF,MAAM,GAAG,GAAoE,EAAE,CAAC;IAChF,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACrB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC;YAAE,OAAO;QACjE,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QAC5C,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YACvB,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QACnF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IACnD,MAAM,KAAK,GAAiB,EAAE,CAAC;IAC/B,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;QACpB,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACrC,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC;YAC9C,IAAI,CAAC,CAAC,KAAK,KAAK,IAAI,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;gBAC5C,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;YACxD,CAAC;YACD,SAAS;QACX,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QACpE,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;IACb,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,57 @@
1
1
  {
2
2
  "name": "@kgdiff/g6",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.2.0",
4
+ "description": "AntV G6 v5 adapter for kgdiff: render semantic graph diffs inside your G6 graphs.",
5
+ "keywords": [
6
+ "kgdiff",
7
+ "antv",
8
+ "g6",
9
+ "adapter"
10
+ ],
11
+ "homepage": "https://github.com/iiwish/kgdiff/tree/main/packages/g6#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/iiwish/kgdiff/issues"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/iiwish/kgdiff.git",
18
+ "directory": "packages/g6"
19
+ },
20
+ "license": "MIT",
21
+ "author": "iiwish",
22
+ "type": "module",
23
+ "sideEffects": false,
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.ts",
27
+ "default": "./dist/index.js"
28
+ }
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "src",
33
+ "!src/**/*.test.ts"
34
+ ],
35
+ "engines": {
36
+ "node": ">=22"
37
+ },
38
+ "publishConfig": {
39
+ "access": "public",
40
+ "registry": "https://registry.npmjs.org/"
41
+ },
42
+ "dependencies": {
43
+ "@kgdiff/core": "0.2.0",
44
+ "@kgdiff/scene": "0.2.0"
45
+ },
46
+ "peerDependencies": {
47
+ "@antv/g6": "^5.1.0"
48
+ },
49
+ "devDependencies": {
50
+ "@antv/g6": "5.1.1"
51
+ },
52
+ "scripts": {
53
+ "build": "tsc -p tsconfig.build.json",
54
+ "typecheck": "tsc -p tsconfig.json",
55
+ "test": "vitest run"
56
+ }
6
57
  }
package/src/color.ts ADDED
@@ -0,0 +1,101 @@
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
+
8
+ /** [r, g, b] in 0–255 and alpha in [0, 1]. */
9
+ export type Rgba = readonly [number, number, number, number];
10
+
11
+ const HEX = /^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
12
+ const FUNC = /^rgba?\(\s*([^)]*)\)$/i;
13
+
14
+ function clampByte(v: number): number {
15
+ return Math.min(255, Math.max(0, Math.round(v)));
16
+ }
17
+
18
+ function clampUnit(v: number): number {
19
+ return Math.min(1, Math.max(0, v));
20
+ }
21
+
22
+ /** Parse a hex or `rgb()` / `rgba()` color; null for anything else. */
23
+ export function parseColor(color: string): Rgba | null {
24
+ const s = color.trim();
25
+ const hex = HEX.exec(s);
26
+ if (hex !== null) {
27
+ let h = hex[1] as string;
28
+ if (h.length <= 4) h = [...h].map((c) => c + c).join('');
29
+ const n = (i: number): number => Number.parseInt(h.slice(i, i + 2), 16);
30
+ return [n(0), n(2), n(4), h.length === 8 ? n(6) / 255 : 1];
31
+ }
32
+ const fn = FUNC.exec(s);
33
+ if (fn === null) return null;
34
+ const parts = (fn[1] as string).split(/[\s,/]+/).filter((p) => p.length > 0);
35
+ if (parts.length !== 3 && parts.length !== 4) return null;
36
+ const nums = parts.map((p) =>
37
+ p.endsWith('%') ? (Number.parseFloat(p) / 100) * 255 : Number.parseFloat(p),
38
+ );
39
+ if (nums.some((v) => !Number.isFinite(v))) return null;
40
+ const alphaPart = parts[3];
41
+ const alpha =
42
+ alphaPart === undefined
43
+ ? 1
44
+ : alphaPart.endsWith('%')
45
+ ? Number.parseFloat(alphaPart) / 100
46
+ : Number.parseFloat(alphaPart);
47
+ return [
48
+ clampByte(nums[0] as number),
49
+ clampByte(nums[1] as number),
50
+ clampByte(nums[2] as number),
51
+ clampUnit(alpha),
52
+ ];
53
+ }
54
+
55
+ /** `rgba(r, g, b, a)` with `a` multiplied into the color's own alpha. */
56
+ export function withAlpha(color: Rgba, alpha: number): string {
57
+ const a = Math.round(clampUnit(color[3] * alpha) * 1000) / 1000;
58
+ return `rgba(${color[0]}, ${color[1]}, ${color[2]}, ${a})`;
59
+ }
60
+
61
+ /**
62
+ * `top` painted over `bottom` (source-over) as an opaque `rgb()` color. Returns `top` unchanged
63
+ * when either color cannot be parsed, and `top` itself when it is already opaque.
64
+ */
65
+ export function composite(top: string, bottom: string): string {
66
+ const t = parseColor(top);
67
+ const b = parseColor(bottom);
68
+ if (t === null || b === null || t[3] >= 1) return top;
69
+ const a = t[3];
70
+ const mix = (i: 0 | 1 | 2): number => clampByte(t[i] * a + b[i] * (1 - a));
71
+ return `rgb(${mix(0)}, ${mix(1)}, ${mix(2)})`;
72
+ }
73
+
74
+ /** Glow falloff of the native renderer: (radius fraction, alpha) — a soft, roughly Gaussian profile. */
75
+ const GLOW_STOPS: readonly (readonly [number, number])[] = [
76
+ [0, 1],
77
+ [0.3, 0.9],
78
+ [0.5, 0.6],
79
+ [0.7, 0.28],
80
+ [0.86, 0.08],
81
+ [1, 0],
82
+ ];
83
+
84
+ const gradients = new Map<string, string>();
85
+
86
+ /**
87
+ * A CSS radial gradient (parsed by AntV G) filling a circle with the native glow profile in
88
+ * `color`; `closest-side` makes the gradient end exactly at the circle's radius. Memoized: the
89
+ * string doubles as G's gradient cache key. Falls back to the plain color when unparsable.
90
+ */
91
+ export function glowGradient(color: string): string {
92
+ let g = gradients.get(color);
93
+ if (g !== undefined) return g;
94
+ const rgba = parseColor(color);
95
+ if (rgba === null) return color;
96
+ const stops = GLOW_STOPS.map(([at, alpha]) => `${withAlpha(rgba, alpha)} ${at * 100}%`);
97
+ g = `radial-gradient(circle closest-side, ${stops.join(', ')})`;
98
+ if (gradients.size > 256) gradients.clear();
99
+ gradients.set(color, g);
100
+ return g;
101
+ }