@zenfg/inspector 0.1.0-beta.1
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 +74 -0
- package/dist/FrameGraphInspector.d.ts +153 -0
- package/dist/FrameGraphInspector.d.ts.map +1 -0
- package/dist/FrameGraphInspector.js +580 -0
- package/dist/FrameGraphInspector.js.map +1 -0
- package/dist/debugCaptureModel.d.ts +166 -0
- package/dist/debugCaptureModel.d.ts.map +1 -0
- package/dist/debugCaptureModel.js +364 -0
- package/dist/debugCaptureModel.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/panelAliasAnalysis.d.ts +20 -0
- package/dist/panelAliasAnalysis.d.ts.map +1 -0
- package/dist/panelAliasAnalysis.js +113 -0
- package/dist/panelAliasAnalysis.js.map +1 -0
- package/dist/panelCytoscapeGraphRenderer.d.ts +71 -0
- package/dist/panelCytoscapeGraphRenderer.d.ts.map +1 -0
- package/dist/panelCytoscapeGraphRenderer.js +559 -0
- package/dist/panelCytoscapeGraphRenderer.js.map +1 -0
- package/dist/panelDiagnosticsView.d.ts +22 -0
- package/dist/panelDiagnosticsView.d.ts.map +1 -0
- package/dist/panelDiagnosticsView.js +123 -0
- package/dist/panelDiagnosticsView.js.map +1 -0
- package/dist/panelDomHelpers.d.ts +26 -0
- package/dist/panelDomHelpers.d.ts.map +1 -0
- package/dist/panelDomHelpers.js +129 -0
- package/dist/panelDomHelpers.js.map +1 -0
- package/dist/panelGraphLayout.d.ts +28 -0
- package/dist/panelGraphLayout.d.ts.map +1 -0
- package/dist/panelGraphLayout.js +223 -0
- package/dist/panelGraphLayout.js.map +1 -0
- package/dist/panelGraphRenderer.d.ts +20 -0
- package/dist/panelGraphRenderer.d.ts.map +1 -0
- package/dist/panelGraphRenderer.js +2 -0
- package/dist/panelGraphRenderer.js.map +1 -0
- package/dist/panelGraphScene.d.ts +98 -0
- package/dist/panelGraphScene.d.ts.map +1 -0
- package/dist/panelGraphScene.js +559 -0
- package/dist/panelGraphScene.js.map +1 -0
- package/dist/panelGraphView.d.ts +9 -0
- package/dist/panelGraphView.d.ts.map +1 -0
- package/dist/panelGraphView.js +88 -0
- package/dist/panelGraphView.js.map +1 -0
- package/dist/panelGraphVisuals.d.ts +38 -0
- package/dist/panelGraphVisuals.d.ts.map +1 -0
- package/dist/panelGraphVisuals.js +330 -0
- package/dist/panelGraphVisuals.js.map +1 -0
- package/dist/panelIcons.d.ts +4 -0
- package/dist/panelIcons.d.ts.map +1 -0
- package/dist/panelIcons.js +43 -0
- package/dist/panelIcons.js.map +1 -0
- package/dist/panelInspectorView.d.ts +33 -0
- package/dist/panelInspectorView.d.ts.map +1 -0
- package/dist/panelInspectorView.js +357 -0
- package/dist/panelInspectorView.js.map +1 -0
- package/dist/panelMemoryView.d.ts +21 -0
- package/dist/panelMemoryView.d.ts.map +1 -0
- package/dist/panelMemoryView.js +134 -0
- package/dist/panelMemoryView.js.map +1 -0
- package/dist/panelPassesView.d.ts +31 -0
- package/dist/panelPassesView.d.ts.map +1 -0
- package/dist/panelPassesView.js +183 -0
- package/dist/panelPassesView.js.map +1 -0
- package/dist/panelResourcesView.d.ts +19 -0
- package/dist/panelResourcesView.d.ts.map +1 -0
- package/dist/panelResourcesView.js +89 -0
- package/dist/panelResourcesView.js.map +1 -0
- package/dist/panelSelection.d.ts +5 -0
- package/dist/panelSelection.d.ts.map +1 -0
- package/dist/panelSelection.js +88 -0
- package/dist/panelSelection.js.map +1 -0
- package/dist/panelTypes.d.ts +40 -0
- package/dist/panelTypes.d.ts.map +1 -0
- package/dist/panelTypes.js +2 -0
- package/dist/panelTypes.js.map +1 -0
- package/dist/panelVisualTheme.d.ts +76 -0
- package/dist/panelVisualTheme.d.ts.map +1 -0
- package/dist/panelVisualTheme.js +39 -0
- package/dist/panelVisualTheme.js.map +1 -0
- package/dist/panelWorkbenchHelpers.d.ts +34 -0
- package/dist/panelWorkbenchHelpers.d.ts.map +1 -0
- package/dist/panelWorkbenchHelpers.js +162 -0
- package/dist/panelWorkbenchHelpers.js.map +1 -0
- package/dist/panelWorkbenchView.d.ts +77 -0
- package/dist/panelWorkbenchView.d.ts.map +1 -0
- package/dist/panelWorkbenchView.js +409 -0
- package/dist/panelWorkbenchView.js.map +1 -0
- package/dist/query.d.ts +11 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +21 -0
- package/dist/query.js.map +1 -0
- package/dist/styles.d.ts +8 -0
- package/dist/styles.d.ts.map +1 -0
- package/dist/styles.js +1073 -0
- package/dist/styles.js.map +1 -0
- package/package.json +29 -0
- package/src/FrameGraphInspector.ts +632 -0
- package/src/debugCaptureModel.ts +583 -0
- package/src/index.ts +21 -0
- package/src/panelAliasAnalysis.ts +164 -0
- package/src/panelCytoscapeGraphRenderer.ts +617 -0
- package/src/panelDiagnosticsView.ts +160 -0
- package/src/panelDomHelpers.ts +151 -0
- package/src/panelGraphLayout.ts +281 -0
- package/src/panelGraphRenderer.ts +21 -0
- package/src/panelGraphScene.ts +787 -0
- package/src/panelGraphView.ts +108 -0
- package/src/panelGraphVisuals.ts +364 -0
- package/src/panelIcons.ts +56 -0
- package/src/panelInspectorView.ts +377 -0
- package/src/panelMemoryView.ts +154 -0
- package/src/panelPassesView.ts +222 -0
- package/src/panelResourcesView.ts +115 -0
- package/src/panelSelection.ts +99 -0
- package/src/panelTypes.ts +28 -0
- package/src/panelVisualTheme.ts +39 -0
- package/src/panelWorkbenchHelpers.ts +216 -0
- package/src/panelWorkbenchView.ts +465 -0
- package/src/query.ts +22 -0
- package/src/styles.ts +1076 -0
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
import { applyGraphLayout, layoutGraphScene, syncExpandedGroupLabelGeometry, } from "./panelGraphLayout.js";
|
|
2
|
+
import { selectionKey, } from "./panelGraphScene.js";
|
|
3
|
+
import { createGraphStyles, expandedGroupLabelMaxWidth, GRAPH_GEOMETRY, graphEdgeDisplayLabel, graphLayoutGeometryKey, isOverviewGraphScale, nodeDimensions, } from "./panelGraphVisuals.js";
|
|
4
|
+
export class CytoscapeGraphRenderer {
|
|
5
|
+
host;
|
|
6
|
+
environment;
|
|
7
|
+
canvasHost;
|
|
8
|
+
tooltip;
|
|
9
|
+
status;
|
|
10
|
+
statusMessage;
|
|
11
|
+
retryButton;
|
|
12
|
+
resizeSubscription;
|
|
13
|
+
core;
|
|
14
|
+
elk;
|
|
15
|
+
loading;
|
|
16
|
+
processing;
|
|
17
|
+
latestRequest;
|
|
18
|
+
appliedScene;
|
|
19
|
+
requestVersion = 0;
|
|
20
|
+
processedVersion = 0;
|
|
21
|
+
destroyed = false;
|
|
22
|
+
fitTargetContentKey;
|
|
23
|
+
forceRelayout = false;
|
|
24
|
+
anchorElementId;
|
|
25
|
+
anchorTargetContentKey;
|
|
26
|
+
failedSceneContentKey;
|
|
27
|
+
overview = false;
|
|
28
|
+
lastTap;
|
|
29
|
+
constructor(host, environment = browserGraphRendererEnvironment) {
|
|
30
|
+
this.host = host;
|
|
31
|
+
this.environment = environment;
|
|
32
|
+
this.canvasHost = environment.createElement('div');
|
|
33
|
+
this.canvasHost.className = 'zenfg-inspector-graph-canvas';
|
|
34
|
+
this.tooltip = environment.createElement('div');
|
|
35
|
+
this.tooltip.className = 'zenfg-inspector-graph-tooltip';
|
|
36
|
+
this.tooltip.hidden = true;
|
|
37
|
+
this.status = environment.createElement('div');
|
|
38
|
+
this.status.className = 'zenfg-inspector-graph-status';
|
|
39
|
+
this.status.hidden = true;
|
|
40
|
+
this.status.setAttribute('role', 'status');
|
|
41
|
+
this.status.setAttribute('aria-live', 'polite');
|
|
42
|
+
this.statusMessage = environment.createElement('span');
|
|
43
|
+
this.retryButton = environment.createElement('button');
|
|
44
|
+
this.retryButton.type = 'button';
|
|
45
|
+
this.retryButton.textContent = 'Retry';
|
|
46
|
+
this.retryButton.hidden = true;
|
|
47
|
+
this.retryButton.addEventListener('click', () => this.relayout());
|
|
48
|
+
this.status.append(this.statusMessage, this.retryButton);
|
|
49
|
+
this.host.replaceChildren(this.canvasHost, this.status, this.tooltip);
|
|
50
|
+
this.resizeSubscription = environment.observeResize?.(host, () => this.core?.resize());
|
|
51
|
+
}
|
|
52
|
+
render(request) {
|
|
53
|
+
if (this.destroyed)
|
|
54
|
+
return;
|
|
55
|
+
this.latestRequest = request;
|
|
56
|
+
if (request.fit) {
|
|
57
|
+
this.fitTargetContentKey = request.scene.contentKey;
|
|
58
|
+
}
|
|
59
|
+
else if (this.fitTargetContentKey !== request.scene.contentKey) {
|
|
60
|
+
this.fitTargetContentKey = undefined;
|
|
61
|
+
}
|
|
62
|
+
if (request.anchorElementId !== undefined) {
|
|
63
|
+
this.anchorElementId = request.anchorElementId;
|
|
64
|
+
this.anchorTargetContentKey = request.scene.contentKey;
|
|
65
|
+
}
|
|
66
|
+
else if (this.anchorTargetContentKey !== request.scene.contentKey) {
|
|
67
|
+
this.anchorElementId = undefined;
|
|
68
|
+
this.anchorTargetContentKey = undefined;
|
|
69
|
+
}
|
|
70
|
+
if (this.failedSceneContentKey && this.failedSceneContentKey !== request.scene.contentKey) {
|
|
71
|
+
this.failedSceneContentKey = undefined;
|
|
72
|
+
this.hideStatus();
|
|
73
|
+
}
|
|
74
|
+
const version = ++this.requestVersion;
|
|
75
|
+
if (this.failedSceneContentKey === request.scene.contentKey) {
|
|
76
|
+
this.syncInteraction(request);
|
|
77
|
+
this.processedVersion = version;
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (!this.processing && this.core && this.appliedScene?.contentKey === request.scene.contentKey && !this.forceRelayout) {
|
|
81
|
+
this.appliedScene = request.scene;
|
|
82
|
+
this.syncInteraction(request);
|
|
83
|
+
this.consumeFitIfCurrentTarget();
|
|
84
|
+
this.consumeAnchorIfCurrentTarget();
|
|
85
|
+
this.processedVersion = version;
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
this.scheduleProcessing();
|
|
89
|
+
}
|
|
90
|
+
resize() {
|
|
91
|
+
this.core?.resize();
|
|
92
|
+
}
|
|
93
|
+
fit() {
|
|
94
|
+
const targetContentKey = this.latestRequest?.scene.contentKey;
|
|
95
|
+
if (targetContentKey)
|
|
96
|
+
this.fitTargetContentKey = targetContentKey;
|
|
97
|
+
if (!this.core || !this.appliedScene)
|
|
98
|
+
return;
|
|
99
|
+
this.core.resize();
|
|
100
|
+
this.core.fit(undefined, GRAPH_GEOMETRY.fitPadding);
|
|
101
|
+
this.updateSemanticZoom();
|
|
102
|
+
if (!this.targetNeedsApply())
|
|
103
|
+
this.fitTargetContentKey = undefined;
|
|
104
|
+
}
|
|
105
|
+
relayout() {
|
|
106
|
+
if (!this.latestRequest || this.destroyed)
|
|
107
|
+
return;
|
|
108
|
+
this.failedSceneContentKey = undefined;
|
|
109
|
+
this.forceRelayout = true;
|
|
110
|
+
this.fitTargetContentKey = this.latestRequest.scene.contentKey;
|
|
111
|
+
this.anchorElementId = undefined;
|
|
112
|
+
this.anchorTargetContentKey = undefined;
|
|
113
|
+
this.hideTooltip();
|
|
114
|
+
++this.requestVersion;
|
|
115
|
+
this.scheduleProcessing();
|
|
116
|
+
}
|
|
117
|
+
destroy() {
|
|
118
|
+
if (this.destroyed)
|
|
119
|
+
return;
|
|
120
|
+
this.destroyed = true;
|
|
121
|
+
this.requestVersion++;
|
|
122
|
+
this.resizeSubscription?.disconnect();
|
|
123
|
+
this.hideTooltip();
|
|
124
|
+
this.core?.destroy();
|
|
125
|
+
this.core = undefined;
|
|
126
|
+
this.elk = undefined;
|
|
127
|
+
this.loading = undefined;
|
|
128
|
+
this.latestRequest = undefined;
|
|
129
|
+
this.appliedScene = undefined;
|
|
130
|
+
this.fitTargetContentKey = undefined;
|
|
131
|
+
this.anchorElementId = undefined;
|
|
132
|
+
this.anchorTargetContentKey = undefined;
|
|
133
|
+
this.host.replaceChildren();
|
|
134
|
+
}
|
|
135
|
+
scheduleProcessing() {
|
|
136
|
+
if (this.processing || this.destroyed)
|
|
137
|
+
return;
|
|
138
|
+
this.processing = this.processRequests().finally(() => {
|
|
139
|
+
this.processing = undefined;
|
|
140
|
+
if (!this.destroyed
|
|
141
|
+
&& !this.failedSceneContentKey
|
|
142
|
+
&& this.processedVersion !== this.requestVersion) {
|
|
143
|
+
this.scheduleProcessing();
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
async processRequests() {
|
|
148
|
+
while (!this.destroyed && this.processedVersion !== this.requestVersion) {
|
|
149
|
+
let version = this.requestVersion;
|
|
150
|
+
let request = this.latestRequest;
|
|
151
|
+
if (!request)
|
|
152
|
+
return;
|
|
153
|
+
if (request.scene.nodes.length === 0) {
|
|
154
|
+
this.hideTooltip();
|
|
155
|
+
if (this.core)
|
|
156
|
+
replaceElements(this.core, request.scene);
|
|
157
|
+
this.appliedScene = request.scene;
|
|
158
|
+
this.forceRelayout = false;
|
|
159
|
+
if (this.fitTargetContentKey === request.scene.contentKey)
|
|
160
|
+
this.fitTargetContentKey = undefined;
|
|
161
|
+
this.consumeAnchorIfCurrentTarget();
|
|
162
|
+
this.syncInteraction(request);
|
|
163
|
+
this.showStatus('empty', 'No graph elements in this view.');
|
|
164
|
+
this.processedVersion = version;
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
if (!this.core)
|
|
169
|
+
this.showStatus('loading', 'Loading graph runtime…');
|
|
170
|
+
await this.ensureRuntime();
|
|
171
|
+
const runtimeRequest = this.resolveCurrentRequest(version, request.scene.contentKey);
|
|
172
|
+
if (!runtimeRequest)
|
|
173
|
+
continue;
|
|
174
|
+
({ version, request } = runtimeRequest);
|
|
175
|
+
const core = this.core;
|
|
176
|
+
if (this.appliedScene?.contentKey === request.scene.contentKey && !this.forceRelayout) {
|
|
177
|
+
this.appliedScene = request.scene;
|
|
178
|
+
this.syncInteraction(request);
|
|
179
|
+
this.consumeFitIfCurrentTarget();
|
|
180
|
+
this.consumeAnchorIfCurrentTarget();
|
|
181
|
+
this.hideStatus();
|
|
182
|
+
this.processedVersion = version;
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
this.hideTooltip();
|
|
186
|
+
const previousViewport = viewport(core);
|
|
187
|
+
const anchorElementId = this.anchorTargetContentKey === request.scene.contentKey
|
|
188
|
+
? this.anchorElementId
|
|
189
|
+
: undefined;
|
|
190
|
+
const anchorBefore = anchorElementId === undefined
|
|
191
|
+
? undefined
|
|
192
|
+
: renderedPosition(core, anchorElementId);
|
|
193
|
+
const topologyChanged = this.appliedScene?.topologyKey !== request.scene.topologyKey;
|
|
194
|
+
const geometryChanged = !this.appliedScene
|
|
195
|
+
|| graphLayoutGeometryKey(this.appliedScene) !== graphLayoutGeometryKey(request.scene);
|
|
196
|
+
let laidOut = false;
|
|
197
|
+
if (geometryChanged || this.forceRelayout) {
|
|
198
|
+
this.showStatus('layout', 'Laying out graph…');
|
|
199
|
+
const result = await this.environment.layoutScene(this.elk, request.scene);
|
|
200
|
+
const layoutRequest = this.resolveCurrentRequest(version, request.scene.contentKey);
|
|
201
|
+
if (!layoutRequest)
|
|
202
|
+
continue;
|
|
203
|
+
({ version, request } = layoutRequest);
|
|
204
|
+
if (topologyChanged || !this.appliedScene)
|
|
205
|
+
replaceElements(core, request.scene);
|
|
206
|
+
else
|
|
207
|
+
updateElementData(core, request.scene);
|
|
208
|
+
applyGraphLayout(core, request.scene, result);
|
|
209
|
+
laidOut = true;
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
updateElementData(core, request.scene);
|
|
213
|
+
}
|
|
214
|
+
syncExpandedGroupLabelGeometry(core);
|
|
215
|
+
this.appliedScene = request.scene;
|
|
216
|
+
this.forceRelayout = false;
|
|
217
|
+
this.failedSceneContentKey = undefined;
|
|
218
|
+
this.syncInteraction(request);
|
|
219
|
+
core.resize();
|
|
220
|
+
if (this.fitTargetContentKey === request.scene.contentKey) {
|
|
221
|
+
core.fit(undefined, GRAPH_GEOMETRY.fitPadding);
|
|
222
|
+
this.fitTargetContentKey = undefined;
|
|
223
|
+
}
|
|
224
|
+
else if (laidOut && anchorBefore && anchorElementId) {
|
|
225
|
+
restoreViewport(core, previousViewport);
|
|
226
|
+
const anchorAfter = renderedPosition(core, anchorElementId);
|
|
227
|
+
if (anchorAfter)
|
|
228
|
+
core.panBy({ x: anchorBefore.x - anchorAfter.x, y: anchorBefore.y - anchorAfter.y });
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
restoreViewport(core, previousViewport);
|
|
232
|
+
}
|
|
233
|
+
this.consumeAnchorIfCurrentTarget();
|
|
234
|
+
this.updateSemanticZoom(true);
|
|
235
|
+
this.hideStatus();
|
|
236
|
+
this.processedVersion = version;
|
|
237
|
+
}
|
|
238
|
+
catch (error) {
|
|
239
|
+
const failedRequest = this.resolveCurrentRequest(version, request.scene.contentKey);
|
|
240
|
+
if (!failedRequest)
|
|
241
|
+
continue;
|
|
242
|
+
({ version, request } = failedRequest);
|
|
243
|
+
this.forceRelayout = false;
|
|
244
|
+
this.failedSceneContentKey = request.scene.contentKey;
|
|
245
|
+
this.processedVersion = version;
|
|
246
|
+
this.showStatus('error', `Failed to render graph: ${error instanceof Error ? error.message : String(error)}`, true);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
async ensureRuntime() {
|
|
252
|
+
if (this.core || this.destroyed)
|
|
253
|
+
return;
|
|
254
|
+
this.loading ??= this.loadRuntime().catch((error) => {
|
|
255
|
+
this.loading = undefined;
|
|
256
|
+
throw error;
|
|
257
|
+
});
|
|
258
|
+
await this.loading;
|
|
259
|
+
}
|
|
260
|
+
async loadRuntime() {
|
|
261
|
+
const runtime = await this.environment.loadRuntime();
|
|
262
|
+
if (this.destroyed)
|
|
263
|
+
return;
|
|
264
|
+
this.canvasHost.textContent = '';
|
|
265
|
+
this.elk = runtime.elk;
|
|
266
|
+
this.core = runtime.createCore({
|
|
267
|
+
container: this.canvasHost,
|
|
268
|
+
elements: [],
|
|
269
|
+
style: createGraphStyles(),
|
|
270
|
+
layout: { name: 'preset' },
|
|
271
|
+
minZoom: 0.03,
|
|
272
|
+
maxZoom: 4,
|
|
273
|
+
boxSelectionEnabled: false,
|
|
274
|
+
autoungrabify: true,
|
|
275
|
+
autounselectify: true,
|
|
276
|
+
});
|
|
277
|
+
this.bindEvents(this.core);
|
|
278
|
+
}
|
|
279
|
+
bindEvents(core) {
|
|
280
|
+
const selectElement = (event) => {
|
|
281
|
+
const request = this.interactiveRequest();
|
|
282
|
+
if (!request)
|
|
283
|
+
return;
|
|
284
|
+
const id = event.target.id();
|
|
285
|
+
const selection = request.scene.interaction.selectionByElementId.get(id);
|
|
286
|
+
if (selection)
|
|
287
|
+
request.onSelect(selection);
|
|
288
|
+
const now = performance.now();
|
|
289
|
+
if (this.lastTap?.id === id && now - this.lastTap.at <= 350 && selection?.kind === 'group') {
|
|
290
|
+
request.onToggleGroup(selection.pathKey);
|
|
291
|
+
this.lastTap = undefined;
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
this.lastTap = { id, at: now };
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
core.on('tap', 'node', selectElement);
|
|
298
|
+
core.on('tap', 'edge', selectElement);
|
|
299
|
+
core.on('tap', (event) => {
|
|
300
|
+
if (event.target === core)
|
|
301
|
+
this.lastTap = undefined;
|
|
302
|
+
});
|
|
303
|
+
const hoverElement = (event) => {
|
|
304
|
+
const request = this.interactiveRequest();
|
|
305
|
+
if (!request)
|
|
306
|
+
return;
|
|
307
|
+
const id = event.target.id();
|
|
308
|
+
const selection = request.scene.interaction.selectionByElementId.get(id);
|
|
309
|
+
if (selection)
|
|
310
|
+
request.onHover(selection);
|
|
311
|
+
const title = event.target.data('tooltip');
|
|
312
|
+
if (title) {
|
|
313
|
+
this.tooltip.textContent = title;
|
|
314
|
+
this.tooltip.hidden = false;
|
|
315
|
+
this.positionTooltip(event.renderedPosition);
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
core.on('mouseover', 'node', hoverElement);
|
|
319
|
+
core.on('mouseover', 'edge', hoverElement);
|
|
320
|
+
const moveTooltip = (event) => this.positionTooltip(event.renderedPosition);
|
|
321
|
+
core.on('mousemove', 'node', moveTooltip);
|
|
322
|
+
core.on('mousemove', 'edge', moveTooltip);
|
|
323
|
+
const clearHover = () => {
|
|
324
|
+
this.hideTooltip();
|
|
325
|
+
this.latestRequest?.onHover(undefined);
|
|
326
|
+
};
|
|
327
|
+
core.on('mouseout', 'node', clearHover);
|
|
328
|
+
core.on('mouseout', 'edge', clearHover);
|
|
329
|
+
core.on('zoom', () => this.updateSemanticZoom());
|
|
330
|
+
}
|
|
331
|
+
interactiveRequest() {
|
|
332
|
+
const request = this.latestRequest;
|
|
333
|
+
return request && this.appliedScene?.contentKey === request.scene.contentKey ? request : undefined;
|
|
334
|
+
}
|
|
335
|
+
syncInteraction(request) {
|
|
336
|
+
const core = this.core;
|
|
337
|
+
if (!core || this.appliedScene?.topologyKey !== request.scene.topologyKey)
|
|
338
|
+
return;
|
|
339
|
+
core.batch(() => {
|
|
340
|
+
core.elements().removeClass('semantic-selected semantic-hover');
|
|
341
|
+
if (request.hovered) {
|
|
342
|
+
for (const id of request.scene.interaction.relatedElementIdsBySelection.get(selectionKey(request.hovered)) ?? []) {
|
|
343
|
+
core.getElementById(id).addClass('semantic-hover');
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
if (request.selected) {
|
|
347
|
+
for (const id of request.scene.interaction.primaryElementIdsBySelection.get(selectionKey(request.selected)) ?? []) {
|
|
348
|
+
core.getElementById(id).addClass('semantic-selected');
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
consumeFitIfCurrentTarget() {
|
|
354
|
+
const targetContentKey = this.latestRequest?.scene.contentKey;
|
|
355
|
+
if (!targetContentKey
|
|
356
|
+
|| this.fitTargetContentKey !== targetContentKey
|
|
357
|
+
|| !this.core
|
|
358
|
+
|| this.targetNeedsApply())
|
|
359
|
+
return;
|
|
360
|
+
this.core.resize();
|
|
361
|
+
this.core.fit(undefined, GRAPH_GEOMETRY.fitPadding);
|
|
362
|
+
this.fitTargetContentKey = undefined;
|
|
363
|
+
this.updateSemanticZoom();
|
|
364
|
+
}
|
|
365
|
+
consumeAnchorIfCurrentTarget() {
|
|
366
|
+
const targetContentKey = this.latestRequest?.scene.contentKey;
|
|
367
|
+
if (!targetContentKey || this.anchorTargetContentKey !== targetContentKey)
|
|
368
|
+
return;
|
|
369
|
+
this.anchorElementId = undefined;
|
|
370
|
+
this.anchorTargetContentKey = undefined;
|
|
371
|
+
}
|
|
372
|
+
targetNeedsApply() {
|
|
373
|
+
const target = this.latestRequest?.scene;
|
|
374
|
+
if (!target || !this.appliedScene)
|
|
375
|
+
return target !== undefined;
|
|
376
|
+
return this.forceRelayout || this.appliedScene.contentKey !== target.contentKey;
|
|
377
|
+
}
|
|
378
|
+
updateSemanticZoom(force = false) {
|
|
379
|
+
const core = this.core;
|
|
380
|
+
if (!core)
|
|
381
|
+
return;
|
|
382
|
+
const overview = isOverviewGraphScale(core.zoom());
|
|
383
|
+
if (!force && overview === this.overview)
|
|
384
|
+
return;
|
|
385
|
+
this.overview = overview;
|
|
386
|
+
core.batch(() => {
|
|
387
|
+
for (const node of core.nodes()) {
|
|
388
|
+
node.data('displayLabel', overview ? node.data('overviewLabel') : node.data('detailLabel'));
|
|
389
|
+
}
|
|
390
|
+
for (const edge of core.edges()) {
|
|
391
|
+
edge.data('displayLabel', overview ? '' : edge.data('detailLabel'));
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
positionTooltip(position) {
|
|
396
|
+
const maxX = Math.max(8, this.host.clientWidth - this.tooltip.offsetWidth - 8);
|
|
397
|
+
const maxY = Math.max(8, this.host.clientHeight - this.tooltip.offsetHeight - 8);
|
|
398
|
+
this.tooltip.style.left = `${Math.max(8, Math.min(maxX, position.x + 12))}px`;
|
|
399
|
+
this.tooltip.style.top = `${Math.max(8, Math.min(maxY, position.y + 12))}px`;
|
|
400
|
+
}
|
|
401
|
+
hideTooltip() {
|
|
402
|
+
this.tooltip.hidden = true;
|
|
403
|
+
this.tooltip.textContent = '';
|
|
404
|
+
this.lastTap = undefined;
|
|
405
|
+
}
|
|
406
|
+
showStatus(kind, message, retry = false) {
|
|
407
|
+
this.status.dataset.state = kind;
|
|
408
|
+
this.statusMessage.textContent = message;
|
|
409
|
+
this.retryButton.hidden = !retry;
|
|
410
|
+
this.status.hidden = false;
|
|
411
|
+
this.host.setAttribute('aria-busy', kind === 'loading' || kind === 'layout' ? 'true' : 'false');
|
|
412
|
+
}
|
|
413
|
+
hideStatus() {
|
|
414
|
+
this.status.hidden = true;
|
|
415
|
+
this.retryButton.hidden = true;
|
|
416
|
+
this.host.setAttribute('aria-busy', 'false');
|
|
417
|
+
}
|
|
418
|
+
resolveCurrentRequest(version, contentKey) {
|
|
419
|
+
if (this.destroyed)
|
|
420
|
+
return undefined;
|
|
421
|
+
const request = this.latestRequest;
|
|
422
|
+
if (!request)
|
|
423
|
+
return undefined;
|
|
424
|
+
if (version === this.requestVersion || request.scene.contentKey === contentKey) {
|
|
425
|
+
return { version: this.requestVersion, request };
|
|
426
|
+
}
|
|
427
|
+
return undefined;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
const browserGraphRendererEnvironment = {
|
|
431
|
+
createElement: (tagName) => document.createElement(tagName),
|
|
432
|
+
loadRuntime: async () => {
|
|
433
|
+
const [cytoscapeModule, elkModule] = await Promise.all([
|
|
434
|
+
import('cytoscape'),
|
|
435
|
+
import('elkjs/lib/elk.bundled.js'),
|
|
436
|
+
]);
|
|
437
|
+
return {
|
|
438
|
+
createCore: cytoscapeModule.default,
|
|
439
|
+
elk: new elkModule.default(),
|
|
440
|
+
};
|
|
441
|
+
},
|
|
442
|
+
layoutScene: layoutGraphScene,
|
|
443
|
+
observeResize: (element, onResize) => {
|
|
444
|
+
if (typeof ResizeObserver === 'undefined')
|
|
445
|
+
return { disconnect: () => undefined };
|
|
446
|
+
const view = element.ownerDocument.defaultView;
|
|
447
|
+
let animationFrame;
|
|
448
|
+
const observer = new ResizeObserver(() => {
|
|
449
|
+
if (!view || animationFrame !== undefined)
|
|
450
|
+
return;
|
|
451
|
+
animationFrame = view.requestAnimationFrame(() => {
|
|
452
|
+
animationFrame = undefined;
|
|
453
|
+
onResize();
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
observer.observe(element);
|
|
457
|
+
return {
|
|
458
|
+
disconnect: () => {
|
|
459
|
+
observer.disconnect();
|
|
460
|
+
if (animationFrame !== undefined) {
|
|
461
|
+
view?.cancelAnimationFrame(animationFrame);
|
|
462
|
+
animationFrame = undefined;
|
|
463
|
+
}
|
|
464
|
+
},
|
|
465
|
+
};
|
|
466
|
+
},
|
|
467
|
+
};
|
|
468
|
+
function replaceElements(core, scene) {
|
|
469
|
+
const definitions = [
|
|
470
|
+
...scene.nodes.map(nodeDefinition),
|
|
471
|
+
...scene.edges.map(edgeDefinition),
|
|
472
|
+
];
|
|
473
|
+
core.startBatch();
|
|
474
|
+
core.elements().remove();
|
|
475
|
+
core.add(definitions);
|
|
476
|
+
core.endBatch();
|
|
477
|
+
}
|
|
478
|
+
export function updateElementData(core, scene) {
|
|
479
|
+
core.batch(() => {
|
|
480
|
+
for (const node of scene.nodes) {
|
|
481
|
+
const element = core.getElementById(node.id);
|
|
482
|
+
if (element.nonempty())
|
|
483
|
+
element.data(nodeRenderableData(node));
|
|
484
|
+
}
|
|
485
|
+
for (const edge of scene.edges) {
|
|
486
|
+
const element = core.getElementById(edge.id);
|
|
487
|
+
if (element.nonempty())
|
|
488
|
+
element.data(edgeRenderableData(edge));
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
function nodeDefinition(node) {
|
|
493
|
+
return {
|
|
494
|
+
group: 'nodes',
|
|
495
|
+
data: {
|
|
496
|
+
id: node.id,
|
|
497
|
+
parent: node.parentId,
|
|
498
|
+
...nodeRenderableData(node),
|
|
499
|
+
},
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
function nodeRenderableData(node) {
|
|
503
|
+
const dimensions = nodeDimensions(node);
|
|
504
|
+
const labelMaxWidth = node.kind === 'group' && node.collapsed
|
|
505
|
+
? expandedGroupLabelMaxWidth(dimensions.width)
|
|
506
|
+
: 160;
|
|
507
|
+
return {
|
|
508
|
+
kind: node.kind,
|
|
509
|
+
passKind: node.kind === 'pass' || node.kind === 'culled-pass' ? node.passKind : undefined,
|
|
510
|
+
resourceKind: node.kind === 'resource' ? node.resourceKind : undefined,
|
|
511
|
+
collapsed: node.kind === 'group' && node.collapsed ? 1 : 0,
|
|
512
|
+
hasCulled: node.kind === 'group' && node.culledNodeCount > 0 ? 1 : 0,
|
|
513
|
+
depthBand: node.kind === 'group' ? node.depthBand : undefined,
|
|
514
|
+
detailLabel: node.label,
|
|
515
|
+
overviewLabel: node.overviewLabel,
|
|
516
|
+
displayLabel: node.label,
|
|
517
|
+
tooltip: node.title,
|
|
518
|
+
labelMaxWidth,
|
|
519
|
+
width: dimensions.width,
|
|
520
|
+
height: dimensions.height,
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
function edgeDefinition(edge) {
|
|
524
|
+
return {
|
|
525
|
+
group: 'edges',
|
|
526
|
+
data: {
|
|
527
|
+
id: edge.id,
|
|
528
|
+
source: edge.from,
|
|
529
|
+
target: edge.to,
|
|
530
|
+
...edgeRenderableData(edge),
|
|
531
|
+
},
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
function edgeRenderableData(edge) {
|
|
535
|
+
const detailLabel = graphEdgeDisplayLabel(edge);
|
|
536
|
+
return {
|
|
537
|
+
kind: edge.kind,
|
|
538
|
+
dependencyKind: edge.kind === 'dependency' ? edge.dependencyKind : undefined,
|
|
539
|
+
accessMode: edge.kind === 'access' ? edge.accessMode : undefined,
|
|
540
|
+
dashed: edge.kind === 'access' && edge.dashed ? 1 : 0,
|
|
541
|
+
detailLabel,
|
|
542
|
+
displayLabel: detailLabel,
|
|
543
|
+
tooltip: edge.title,
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
function viewport(core) {
|
|
547
|
+
return { pan: { ...core.pan() }, zoom: core.zoom() };
|
|
548
|
+
}
|
|
549
|
+
function restoreViewport(core, state) {
|
|
550
|
+
core.zoom(state.zoom);
|
|
551
|
+
core.pan(state.pan);
|
|
552
|
+
}
|
|
553
|
+
function renderedPosition(core, id) {
|
|
554
|
+
const element = core.getElementById(id);
|
|
555
|
+
return element.nonempty() && element.isNode() ? { ...element.renderedPosition() } : undefined;
|
|
556
|
+
}
|
|
557
|
+
export { createCytoscapeEdgeRouteData, createElkLayoutGraph, layoutGraphScene, } from "./panelGraphLayout.js";
|
|
558
|
+
export { createGraphStyles, expandedGroupLabelMaxWidth, graphEdgeDisplayLabel, graphLayoutGeometryKey, isOverviewGraphScale, } from "./panelGraphVisuals.js";
|
|
559
|
+
//# sourceMappingURL=panelCytoscapeGraphRenderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"panelCytoscapeGraphRenderer.js","sourceRoot":"","sources":["../src/panelCytoscapeGraphRenderer.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,gBAAgB,EAChB,gBAAgB,EAChB,8BAA8B,GAGjC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACH,YAAY,GAKf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACH,iBAAiB,EACjB,0BAA0B,EAC1B,cAAc,EACd,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,GACjB,MAAM,wBAAwB,CAAC;AAkBhC,MAAM,OAAO,sBAAsB;IAyBV;IACA;IAzBJ,UAAU,CAAc;IACxB,OAAO,CAAc;IACrB,MAAM,CAAc;IACpB,aAAa,CAAc;IAC3B,WAAW,CAAoB;IAC/B,kBAAkB,CAAsC;IACjE,IAAI,CAA6B;IACjC,GAAG,CAAkB;IACrB,OAAO,CAA4B;IACnC,UAAU,CAA4B;IACtC,aAAa,CAAiC;IAC9C,YAAY,CAAyB;IACrC,cAAc,GAAG,CAAC,CAAC;IACnB,gBAAgB,GAAG,CAAC,CAAC;IACrB,SAAS,GAAG,KAAK,CAAC;IAClB,mBAAmB,CAAqB;IACxC,aAAa,GAAG,KAAK,CAAC;IACtB,eAAe,CAAkC;IACjD,sBAAsB,CAAqB;IAC3C,qBAAqB,CAAqB;IAC1C,QAAQ,GAAG,KAAK,CAAC;IACjB,OAAO,CAA2D;IAE1E,YACqB,IAAiB,EACjB,cAAwC,+BAA+B;QADvE,SAAI,GAAJ,IAAI,CAAa;QACjB,gBAAW,GAAX,WAAW,CAA4D;QAExF,IAAI,CAAC,UAAU,GAAG,WAAW,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QACnD,IAAI,CAAC,UAAU,CAAC,SAAS,GAAG,8BAA8B,CAAC;QAC3D,IAAI,CAAC,OAAO,GAAG,WAAW,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAChD,IAAI,CAAC,OAAO,CAAC,SAAS,GAAG,+BAA+B,CAAC;QACzD,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,WAAW,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC/C,IAAI,CAAC,MAAM,CAAC,SAAS,GAAG,8BAA8B,CAAC;QACvD,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC;QAC1B,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC3C,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;QAChD,IAAI,CAAC,aAAa,GAAG,WAAW,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QACvD,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC,aAAa,CAAC,QAAQ,CAAsB,CAAC;QAC5E,IAAI,CAAC,WAAW,CAAC,IAAI,GAAG,QAAQ,CAAC;QACjC,IAAI,CAAC,WAAW,CAAC,WAAW,GAAG,OAAO,CAAC;QACvC,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,IAAI,CAAC;QAC/B,IAAI,CAAC,WAAW,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAClE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACzD,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACtE,IAAI,CAAC,kBAAkB,GAAG,WAAW,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAC3F,CAAC;IAED,MAAM,CAAC,OAA2B;QAC9B,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QAC3B,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC;QAC7B,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;YACd,IAAI,CAAC,mBAAmB,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC;QACxD,CAAC;aAAM,IAAI,IAAI,CAAC,mBAAmB,KAAK,OAAO,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;YAC/D,IAAI,CAAC,mBAAmB,GAAG,SAAS,CAAC;QACzC,CAAC;QACD,IAAI,OAAO,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;YACxC,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;YAC/C,IAAI,CAAC,sBAAsB,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC;QAC3D,CAAC;aAAM,IAAI,IAAI,CAAC,sBAAsB,KAAK,OAAO,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;YAClE,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;YACjC,IAAI,CAAC,sBAAsB,GAAG,SAAS,CAAC;QAC5C,CAAC;QACD,IAAI,IAAI,CAAC,qBAAqB,IAAI,IAAI,CAAC,qBAAqB,KAAK,OAAO,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;YACxF,IAAI,CAAC,qBAAqB,GAAG,SAAS,CAAC;YACvC,IAAI,CAAC,UAAU,EAAE,CAAC;QACtB,CAAC;QAED,MAAM,OAAO,GAAG,EAAE,IAAI,CAAC,cAAc,CAAC;QACtC,IAAI,IAAI,CAAC,qBAAqB,KAAK,OAAO,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;YAC1D,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;YAC9B,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC;YAChC,OAAO;QACX,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,YAAY,EAAE,UAAU,KAAK,OAAO,CAAC,KAAK,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACrH,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC;YAClC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;YAC9B,IAAI,CAAC,yBAAyB,EAAE,CAAC;YACjC,IAAI,CAAC,4BAA4B,EAAE,CAAC;YACpC,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC;YAChC,OAAO;QACX,CAAC;QACD,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAC9B,CAAC;IAED,MAAM;QACF,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IACxB,CAAC;IAED,GAAG;QACC,MAAM,gBAAgB,GAAG,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,UAAU,CAAC;QAC9D,IAAI,gBAAgB;YAAE,IAAI,CAAC,mBAAmB,GAAG,gBAAgB,CAAC;QAClE,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,YAAY;YAAE,OAAO;QAC7C,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACnB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;QACpD,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC1B,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE;YAAE,IAAI,CAAC,mBAAmB,GAAG,SAAS,CAAC;IACvE,CAAC;IAED,QAAQ;QACJ,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QAClD,IAAI,CAAC,qBAAqB,GAAG,SAAS,CAAC;QACvC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,UAAU,CAAC;QAC/D,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;QACjC,IAAI,CAAC,sBAAsB,GAAG,SAAS,CAAC;QACxC,IAAI,CAAC,WAAW,EAAE,CAAC;QACnB,EAAE,IAAI,CAAC,cAAc,CAAC;QACtB,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAC9B,CAAC;IAED,OAAO;QACH,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QAC3B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,IAAI,CAAC,kBAAkB,EAAE,UAAU,EAAE,CAAC;QACtC,IAAI,CAAC,WAAW,EAAE,CAAC;QACnB,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC;QACtB,IAAI,CAAC,GAAG,GAAG,SAAS,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;QACzB,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;QAC/B,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC;QAC9B,IAAI,CAAC,mBAAmB,GAAG,SAAS,CAAC;QACrC,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;QACjC,IAAI,CAAC,sBAAsB,GAAG,SAAS,CAAC;QACxC,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;IAChC,CAAC;IAEO,kBAAkB;QACtB,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QAC9C,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;YAClD,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;YAC5B,IAAI,CAAC,IAAI,CAAC,SAAS;mBACZ,CAAC,IAAI,CAAC,qBAAqB;mBAC3B,IAAI,CAAC,gBAAgB,KAAK,IAAI,CAAC,cAAc,EAAE,CAAC;gBACnD,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC9B,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC;IAEO,KAAK,CAAC,eAAe;QACzB,OAAO,CAAC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,gBAAgB,KAAK,IAAI,CAAC,cAAc,EAAE,CAAC;YACtE,IAAI,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC;YAClC,IAAI,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC;YACjC,IAAI,CAAC,OAAO;gBAAE,OAAO;YAErB,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACnC,IAAI,CAAC,WAAW,EAAE,CAAC;gBACnB,IAAI,IAAI,CAAC,IAAI;oBAAE,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;gBACzD,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC;gBAClC,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;gBAC3B,IAAI,IAAI,CAAC,mBAAmB,KAAK,OAAO,CAAC,KAAK,CAAC,UAAU;oBAAE,IAAI,CAAC,mBAAmB,GAAG,SAAS,CAAC;gBAChG,IAAI,CAAC,4BAA4B,EAAE,CAAC;gBACpC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;gBAC9B,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,iCAAiC,CAAC,CAAC;gBAC5D,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC;gBAChC,SAAS;YACb,CAAC;YAED,IAAI,CAAC;gBACD,IAAI,CAAC,IAAI,CAAC,IAAI;oBAAE,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,wBAAwB,CAAC,CAAC;gBACrE,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;gBAC3B,MAAM,cAAc,GAAG,IAAI,CAAC,qBAAqB,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;gBACrF,IAAI,CAAC,cAAc;oBAAE,SAAS;gBAC9B,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,CAAC,CAAC;gBACxC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAK,CAAC;gBACxB,IAAI,IAAI,CAAC,YAAY,EAAE,UAAU,KAAK,OAAO,CAAC,KAAK,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;oBACpF,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC;oBAClC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;oBAC9B,IAAI,CAAC,yBAAyB,EAAE,CAAC;oBACjC,IAAI,CAAC,4BAA4B,EAAE,CAAC;oBACpC,IAAI,CAAC,UAAU,EAAE,CAAC;oBAClB,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC;oBAChC,SAAS;gBACb,CAAC;gBAED,IAAI,CAAC,WAAW,EAAE,CAAC;gBACnB,MAAM,gBAAgB,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;gBACxC,MAAM,eAAe,GAAG,IAAI,CAAC,sBAAsB,KAAK,OAAO,CAAC,KAAK,CAAC,UAAU;oBAC5E,CAAC,CAAC,IAAI,CAAC,eAAe;oBACtB,CAAC,CAAC,SAAS,CAAC;gBAChB,MAAM,YAAY,GAAG,eAAe,KAAK,SAAS;oBAC9C,CAAC,CAAC,SAAS;oBACX,CAAC,CAAC,gBAAgB,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;gBAC9C,MAAM,eAAe,GAAG,IAAI,CAAC,YAAY,EAAE,WAAW,KAAK,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC;gBACrF,MAAM,eAAe,GAAG,CAAC,IAAI,CAAC,YAAY;uBACnC,sBAAsB,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,sBAAsB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;gBAC3F,IAAI,OAAO,GAAG,KAAK,CAAC;gBAEpB,IAAI,eAAe,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;oBACxC,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;oBAC/C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,GAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;oBAC5E,MAAM,aAAa,GAAG,IAAI,CAAC,qBAAqB,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;oBACpF,IAAI,CAAC,aAAa;wBAAE,SAAS;oBAC7B,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,aAAa,CAAC,CAAC;oBACvC,IAAI,eAAe,IAAI,CAAC,IAAI,CAAC,YAAY;wBAAE,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;;wBAC3E,iBAAiB,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;oBAC5C,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;oBAC9C,OAAO,GAAG,IAAI,CAAC;gBACnB,CAAC;qBAAM,CAAC;oBACJ,iBAAiB,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;gBAC3C,CAAC;gBAED,8BAA8B,CAAC,IAAI,CAAC,CAAC;gBACrC,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC;gBAClC,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;gBAC3B,IAAI,CAAC,qBAAqB,GAAG,SAAS,CAAC;gBACvC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;gBAC9B,IAAI,CAAC,MAAM,EAAE,CAAC;gBACd,IAAI,IAAI,CAAC,mBAAmB,KAAK,OAAO,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;oBACxD,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;oBAC/C,IAAI,CAAC,mBAAmB,GAAG,SAAS,CAAC;gBACzC,CAAC;qBAAM,IAAI,OAAO,IAAI,YAAY,IAAI,eAAe,EAAE,CAAC;oBACpD,eAAe,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;oBACxC,MAAM,WAAW,GAAG,gBAAgB,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;oBAC5D,IAAI,WAAW;wBAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,YAAY,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,YAAY,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC;gBAC1G,CAAC;qBAAM,CAAC;oBACJ,eAAe,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;gBAC5C,CAAC;gBACD,IAAI,CAAC,4BAA4B,EAAE,CAAC;gBACpC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;gBAC9B,IAAI,CAAC,UAAU,EAAE,CAAC;gBAClB,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC;YACpC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACb,MAAM,aAAa,GAAG,IAAI,CAAC,qBAAqB,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;gBACpF,IAAI,CAAC,aAAa;oBAAE,SAAS;gBAC7B,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,aAAa,CAAC,CAAC;gBACvC,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;gBAC3B,IAAI,CAAC,qBAAqB,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC;gBACtD,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC;gBAChC,IAAI,CAAC,UAAU,CACX,OAAO,EACP,2BAA2B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EACnF,IAAI,CACP,CAAC;gBACF,OAAO;YACX,CAAC;QACL,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,aAAa;QACvB,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QACxC,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YAChD,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;YACzB,MAAM,KAAK,CAAC;QAChB,CAAC,CAAC,CAAC;QACH,MAAM,IAAI,CAAC,OAAO,CAAC;IACvB,CAAC;IAEO,KAAK,CAAC,WAAW;QACrB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC;QACrD,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QAC3B,IAAI,CAAC,UAAU,CAAC,WAAW,GAAG,EAAE,CAAC;QACjC,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC;YAC3B,SAAS,EAAE,IAAI,CAAC,UAAU;YAC1B,QAAQ,EAAE,EAAE;YACZ,KAAK,EAAE,iBAAiB,EAAE;YAC1B,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC1B,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,CAAC;YACV,mBAAmB,EAAE,KAAK;YAC1B,aAAa,EAAE,IAAI;YACnB,eAAe,EAAE,IAAI;SACxB,CAAC,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAEO,UAAU,CAAC,IAAoB;QACnC,MAAM,aAAa,GAAG,CAAC,KAA4B,EAAE,EAAE;YACnD,MAAM,OAAO,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC1C,IAAI,CAAC,OAAO;gBAAE,OAAO;YACrB,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,EAAE,EAAyB,CAAC;YACpD,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,oBAAoB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACzE,IAAI,SAAS;gBAAE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;YAC3C,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;YAC9B,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,GAAG,IAAI,SAAS,EAAE,IAAI,KAAK,OAAO,EAAE,CAAC;gBACzF,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;gBACzC,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;YAC7B,CAAC;iBAAM,CAAC;gBACJ,IAAI,CAAC,OAAO,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC;YACnC,CAAC;QACL,CAAC,CAAC;QACF,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;QACtC,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;QACtC,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE;YACrB,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI;gBAAE,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;QACxD,CAAC,CAAC,CAAC;QACH,MAAM,YAAY,GAAG,CAAC,KAA4B,EAAE,EAAE;YAClD,MAAM,OAAO,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC1C,IAAI,CAAC,OAAO;gBAAE,OAAO;YACrB,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,EAAE,EAAyB,CAAC;YACpD,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,oBAAoB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACzE,IAAI,SAAS;gBAAE,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAuB,CAAC;YACjE,IAAI,KAAK,EAAE,CAAC;gBACR,IAAI,CAAC,OAAO,CAAC,WAAW,GAAG,KAAK,CAAC;gBACjC,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC;gBAC5B,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;YACjD,CAAC;QACL,CAAC,CAAC;QACF,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC;QAC3C,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC;QAC3C,MAAM,WAAW,GAAG,CAAC,KAA4B,EAAE,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;QACnG,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;QAC1C,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;QAC1C,MAAM,UAAU,GAAG,GAAG,EAAE;YACpB,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QAC3C,CAAC,CAAC;QACF,IAAI,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;QACxC,IAAI,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;QACxC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC;IACrD,CAAC;IAEO,kBAAkB;QACtB,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC;QACnC,OAAO,OAAO,IAAI,IAAI,CAAC,YAAY,EAAE,UAAU,KAAK,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IACvG,CAAC;IAEO,eAAe,CAAC,OAA2B;QAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACvB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,YAAY,EAAE,WAAW,KAAK,OAAO,CAAC,KAAK,CAAC,WAAW;YAAE,OAAO;QAClF,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE;YACZ,IAAI,CAAC,QAAQ,EAAE,CAAC,WAAW,CAAC,kCAAkC,CAAC,CAAC;YAChE,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;gBAClB,KAAK,MAAM,EAAE,IAAI,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,4BAA4B,CAAC,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;oBAC/G,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;gBACvD,CAAC;YACL,CAAC;YACD,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;gBACnB,KAAK,MAAM,EAAE,IAAI,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,4BAA4B,CAAC,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;oBAChH,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC;gBAC1D,CAAC;YACL,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC;IAEO,yBAAyB;QAC7B,MAAM,gBAAgB,GAAG,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,UAAU,CAAC;QAC9D,IAAI,CAAC,gBAAgB;eACd,IAAI,CAAC,mBAAmB,KAAK,gBAAgB;eAC7C,CAAC,IAAI,CAAC,IAAI;eACV,IAAI,CAAC,gBAAgB,EAAE;YAAE,OAAO;QACvC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACnB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;QACpD,IAAI,CAAC,mBAAmB,GAAG,SAAS,CAAC;QACrC,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAC9B,CAAC;IAEO,4BAA4B;QAChC,MAAM,gBAAgB,GAAG,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,UAAU,CAAC;QAC9D,IAAI,CAAC,gBAAgB,IAAI,IAAI,CAAC,sBAAsB,KAAK,gBAAgB;YAAE,OAAO;QAClF,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;QACjC,IAAI,CAAC,sBAAsB,GAAG,SAAS,CAAC;IAC5C,CAAC;IAEO,gBAAgB;QACpB,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC;QACzC,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY;YAAE,OAAO,MAAM,KAAK,SAAS,CAAC;QAC/D,OAAO,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,YAAY,CAAC,UAAU,KAAK,MAAM,CAAC,UAAU,CAAC;IACpF,CAAC;IAEO,kBAAkB,CAAC,KAAK,GAAG,KAAK;QACpC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACvB,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,MAAM,QAAQ,GAAG,oBAAoB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACnD,IAAI,CAAC,KAAK,IAAI,QAAQ,KAAK,IAAI,CAAC,QAAQ;YAAE,OAAO;QACjD,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE;YACZ,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;gBAC9B,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;YAChG,CAAC;YACD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;gBAC9B,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;YACxE,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC;IAEO,eAAe,CAAC,QAAuB;QAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC;QAC/E,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC;QACjF,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC;QAC9E,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC;IACjF,CAAC;IAEO,WAAW;QACf,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;QAC3B,IAAI,CAAC,OAAO,CAAC,WAAW,GAAG,EAAE,CAAC;QAC9B,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;IAC7B,CAAC;IAEO,UAAU,CAAC,IAA8C,EAAE,OAAe,EAAE,KAAK,GAAG,KAAK;QAC7F,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;QACjC,IAAI,CAAC,aAAa,CAAC,WAAW,GAAG,OAAO,CAAC;QACzC,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC;QAC3B,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IACpG,CAAC;IAEO,UAAU;QACd,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC;QAC1B,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,IAAI,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IACjD,CAAC;IAEO,qBAAqB,CACzB,OAAe,EACf,UAAkB;QAElB,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO,SAAS,CAAC;QACrC,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC;QACnC,IAAI,CAAC,OAAO;YAAE,OAAO,SAAS,CAAC;QAC/B,IAAI,OAAO,KAAK,IAAI,CAAC,cAAc,IAAI,OAAO,CAAC,KAAK,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;YAC7E,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,OAAO,EAAE,CAAC;QACrD,CAAC;QACD,OAAO,SAAS,CAAC;IACrB,CAAC;CACJ;AAED,MAAM,+BAA+B,GAA6B;IAC9D,aAAa,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC;IAC3D,WAAW,EAAE,KAAK,IAAI,EAAE;QACpB,MAAM,CAAC,eAAe,EAAE,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACnD,MAAM,CAAC,WAAW,CAAC;YACnB,MAAM,CAAC,0BAA0B,CAAC;SACrC,CAAC,CAAC;QACH,OAAO;YACH,UAAU,EAAE,eAAe,CAAC,OAAO;YACnC,GAAG,EAAE,IAAI,SAAS,CAAC,OAAO,EAAE;SAC/B,CAAC;IACN,CAAC;IACD,WAAW,EAAE,gBAAgB;IAC7B,aAAa,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE;QACjC,IAAI,OAAO,cAAc,KAAK,WAAW;YAAE,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,SAAS,EAAE,CAAC;QAClF,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,WAAW,CAAC;QAC/C,IAAI,cAAkC,CAAC;QACvC,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,GAAG,EAAE;YACrC,IAAI,CAAC,IAAI,IAAI,cAAc,KAAK,SAAS;gBAAE,OAAO;YAClD,cAAc,GAAG,IAAI,CAAC,qBAAqB,CAAC,GAAG,EAAE;gBAC7C,cAAc,GAAG,SAAS,CAAC;gBAC3B,QAAQ,EAAE,CAAC;YACf,CAAC,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;QACH,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC1B,OAAO;YACH,UAAU,EAAE,GAAG,EAAE;gBACb,QAAQ,CAAC,UAAU,EAAE,CAAC;gBACtB,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;oBAC/B,IAAI,EAAE,oBAAoB,CAAC,cAAc,CAAC,CAAC;oBAC3C,cAAc,GAAG,SAAS,CAAC;gBAC/B,CAAC;YACL,CAAC;SACJ,CAAC;IACN,CAAC;CACJ,CAAC;AAEF,SAAS,eAAe,CAAC,IAAoB,EAAE,KAAiB;IAC5D,MAAM,WAAW,GAAkC;QAC/C,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC;QAClC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC;KACrC,CAAC;IACF,IAAI,CAAC,UAAU,EAAE,CAAC;IAClB,IAAI,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,CAAC;IACzB,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACtB,IAAI,CAAC,QAAQ,EAAE,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAoB,EAAE,KAAiB;IACrE,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE;QACZ,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC7C,IAAI,OAAO,CAAC,QAAQ,EAAE;gBAAE,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;QACnE,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC7C,IAAI,OAAO,CAAC,QAAQ,EAAE;gBAAE,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;QACnE,CAAC;IACL,CAAC,CAAC,CAAC;AACP,CAAC;AAED,SAAS,cAAc,CAAC,IAAoB;IACxC,OAAO;QACH,KAAK,EAAE,OAAO;QACd,IAAI,EAAE;YACF,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,MAAM,EAAE,IAAI,CAAC,QAAQ;YACrB,GAAG,kBAAkB,CAAC,IAAI,CAAC;SAC9B;KACJ,CAAC;AACN,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAoB;IAC5C,MAAM,UAAU,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,SAAS;QACzD,CAAC,CAAC,0BAA0B,CAAC,UAAU,CAAC,KAAK,CAAC;QAC9C,CAAC,CAAC,GAAG,CAAC;IACV,OAAO;QACH,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,QAAQ,EAAE,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;QACzF,YAAY,EAAE,IAAI,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS;QACtE,SAAS,EAAE,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1D,SAAS,EAAE,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACpE,SAAS,EAAE,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;QAC7D,WAAW,EAAE,IAAI,CAAC,KAAK;QACvB,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,YAAY,EAAE,IAAI,CAAC,KAAK;QACxB,OAAO,EAAE,IAAI,CAAC,KAAK;QACnB,aAAa;QACb,KAAK,EAAE,UAAU,CAAC,KAAK;QACvB,MAAM,EAAE,UAAU,CAAC,MAAM;KAC5B,CAAC;AACN,CAAC;AAED,SAAS,cAAc,CAAC,IAAoB;IACxC,OAAO;QACH,KAAK,EAAE,OAAO;QACd,IAAI,EAAE;YACF,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,MAAM,EAAE,IAAI,CAAC,IAAI;YACjB,MAAM,EAAE,IAAI,CAAC,EAAE;YACf,GAAG,kBAAkB,CAAC,IAAI,CAAC;SAC9B;KACJ,CAAC;AACN,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAoB;IAC5C,MAAM,WAAW,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;IAChD,OAAO;QACH,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,cAAc,EAAE,IAAI,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS;QAC5E,UAAU,EAAE,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;QAChE,MAAM,EAAE,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACrD,WAAW;QACX,YAAY,EAAE,WAAW;QACzB,OAAO,EAAE,IAAI,CAAC,KAAK;KACtB,CAAC;AACN,CAAC;AAED,SAAS,QAAQ,CAAC,IAAoB;IAClC,OAAO,EAAE,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;AACzD,CAAC;AAED,SAAS,eAAe,CAAC,IAAoB,EAAE,KAAe;IAC1D,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACtB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AACxB,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAoB,EAAE,EAAuB;IACnE,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;IACxC,OAAO,OAAO,CAAC,QAAQ,EAAE,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAClG,CAAC;AAED,OAAO,EACH,4BAA4B,EAC5B,oBAAoB,EACpB,gBAAgB,GACnB,MAAM,uBAAuB,CAAC;AAO/B,OAAO,EACH,iBAAiB,EACjB,0BAA0B,EAC1B,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,GACvB,MAAM,wBAAwB,CAAC","sourcesContent":["import type cytoscape from 'cytoscape';\nimport type { ELK } from 'elkjs/lib/elk-api';\n\nimport type { GraphRenderer, GraphRenderRequest } from './panelGraphRenderer.ts';\nimport {\n applyGraphLayout,\n layoutGraphScene,\n syncExpandedGroupLabelGeometry,\n type GraphLayoutResult,\n type GraphPosition,\n} from './panelGraphLayout.ts';\nimport {\n selectionKey,\n type GraphScene,\n type GraphSceneEdge,\n type GraphSceneElementId,\n type GraphSceneNode,\n} from './panelGraphScene.ts';\nimport {\n createGraphStyles,\n expandedGroupLabelMaxWidth,\n GRAPH_GEOMETRY,\n graphEdgeDisplayLabel,\n graphLayoutGeometryKey,\n isOverviewGraphScale,\n nodeDimensions,\n} from './panelGraphVisuals.ts';\n\ntype Viewport = { readonly pan: GraphPosition; readonly zoom: number };\n\ntype GraphRendererRuntime = {\n readonly createCore: (options: cytoscape.CytoscapeOptions) => cytoscape.Core;\n readonly elk: ELK;\n};\n\nexport type GraphResizeSubscription = { disconnect(): void };\n\nexport type GraphRendererEnvironment = {\n readonly createElement: (tagName: string) => HTMLElement;\n readonly loadRuntime: () => Promise<GraphRendererRuntime>;\n readonly layoutScene: (elk: ELK, scene: GraphScene) => Promise<GraphLayoutResult>;\n readonly observeResize?: (element: HTMLElement, onResize: () => void) => GraphResizeSubscription;\n};\n\nexport class CytoscapeGraphRenderer implements GraphRenderer {\n private readonly canvasHost: HTMLElement;\n private readonly tooltip: HTMLElement;\n private readonly status: HTMLElement;\n private readonly statusMessage: HTMLElement;\n private readonly retryButton: HTMLButtonElement;\n private readonly resizeSubscription: GraphResizeSubscription | undefined;\n private core: cytoscape.Core | undefined;\n private elk: ELK | undefined;\n private loading: Promise<void> | undefined;\n private processing: Promise<void> | undefined;\n private latestRequest: GraphRenderRequest | undefined;\n private appliedScene: GraphScene | undefined;\n private requestVersion = 0;\n private processedVersion = 0;\n private destroyed = false;\n private fitTargetContentKey: string | undefined;\n private forceRelayout = false;\n private anchorElementId: GraphSceneElementId | undefined;\n private anchorTargetContentKey: string | undefined;\n private failedSceneContentKey: string | undefined;\n private overview = false;\n private lastTap: { readonly id: string; readonly at: number } | undefined;\n\n constructor(\n private readonly host: HTMLElement,\n private readonly environment: GraphRendererEnvironment = browserGraphRendererEnvironment,\n ) {\n this.canvasHost = environment.createElement('div');\n this.canvasHost.className = 'zenfg-inspector-graph-canvas';\n this.tooltip = environment.createElement('div');\n this.tooltip.className = 'zenfg-inspector-graph-tooltip';\n this.tooltip.hidden = true;\n this.status = environment.createElement('div');\n this.status.className = 'zenfg-inspector-graph-status';\n this.status.hidden = true;\n this.status.setAttribute('role', 'status');\n this.status.setAttribute('aria-live', 'polite');\n this.statusMessage = environment.createElement('span');\n this.retryButton = environment.createElement('button') as HTMLButtonElement;\n this.retryButton.type = 'button';\n this.retryButton.textContent = 'Retry';\n this.retryButton.hidden = true;\n this.retryButton.addEventListener('click', () => this.relayout());\n this.status.append(this.statusMessage, this.retryButton);\n this.host.replaceChildren(this.canvasHost, this.status, this.tooltip);\n this.resizeSubscription = environment.observeResize?.(host, () => this.core?.resize());\n }\n\n render(request: GraphRenderRequest): void {\n if (this.destroyed) return;\n this.latestRequest = request;\n if (request.fit) {\n this.fitTargetContentKey = request.scene.contentKey;\n } else if (this.fitTargetContentKey !== request.scene.contentKey) {\n this.fitTargetContentKey = undefined;\n }\n if (request.anchorElementId !== undefined) {\n this.anchorElementId = request.anchorElementId;\n this.anchorTargetContentKey = request.scene.contentKey;\n } else if (this.anchorTargetContentKey !== request.scene.contentKey) {\n this.anchorElementId = undefined;\n this.anchorTargetContentKey = undefined;\n }\n if (this.failedSceneContentKey && this.failedSceneContentKey !== request.scene.contentKey) {\n this.failedSceneContentKey = undefined;\n this.hideStatus();\n }\n\n const version = ++this.requestVersion;\n if (this.failedSceneContentKey === request.scene.contentKey) {\n this.syncInteraction(request);\n this.processedVersion = version;\n return;\n }\n if (!this.processing && this.core && this.appliedScene?.contentKey === request.scene.contentKey && !this.forceRelayout) {\n this.appliedScene = request.scene;\n this.syncInteraction(request);\n this.consumeFitIfCurrentTarget();\n this.consumeAnchorIfCurrentTarget();\n this.processedVersion = version;\n return;\n }\n this.scheduleProcessing();\n }\n\n resize(): void {\n this.core?.resize();\n }\n\n fit(): void {\n const targetContentKey = this.latestRequest?.scene.contentKey;\n if (targetContentKey) this.fitTargetContentKey = targetContentKey;\n if (!this.core || !this.appliedScene) return;\n this.core.resize();\n this.core.fit(undefined, GRAPH_GEOMETRY.fitPadding);\n this.updateSemanticZoom();\n if (!this.targetNeedsApply()) this.fitTargetContentKey = undefined;\n }\n\n relayout(): void {\n if (!this.latestRequest || this.destroyed) return;\n this.failedSceneContentKey = undefined;\n this.forceRelayout = true;\n this.fitTargetContentKey = this.latestRequest.scene.contentKey;\n this.anchorElementId = undefined;\n this.anchorTargetContentKey = undefined;\n this.hideTooltip();\n ++this.requestVersion;\n this.scheduleProcessing();\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n this.requestVersion++;\n this.resizeSubscription?.disconnect();\n this.hideTooltip();\n this.core?.destroy();\n this.core = undefined;\n this.elk = undefined;\n this.loading = undefined;\n this.latestRequest = undefined;\n this.appliedScene = undefined;\n this.fitTargetContentKey = undefined;\n this.anchorElementId = undefined;\n this.anchorTargetContentKey = undefined;\n this.host.replaceChildren();\n }\n\n private scheduleProcessing(): void {\n if (this.processing || this.destroyed) return;\n this.processing = this.processRequests().finally(() => {\n this.processing = undefined;\n if (!this.destroyed\n && !this.failedSceneContentKey\n && this.processedVersion !== this.requestVersion) {\n this.scheduleProcessing();\n }\n });\n }\n\n private async processRequests(): Promise<void> {\n while (!this.destroyed && this.processedVersion !== this.requestVersion) {\n let version = this.requestVersion;\n let request = this.latestRequest;\n if (!request) return;\n\n if (request.scene.nodes.length === 0) {\n this.hideTooltip();\n if (this.core) replaceElements(this.core, request.scene);\n this.appliedScene = request.scene;\n this.forceRelayout = false;\n if (this.fitTargetContentKey === request.scene.contentKey) this.fitTargetContentKey = undefined;\n this.consumeAnchorIfCurrentTarget();\n this.syncInteraction(request);\n this.showStatus('empty', 'No graph elements in this view.');\n this.processedVersion = version;\n continue;\n }\n\n try {\n if (!this.core) this.showStatus('loading', 'Loading graph runtime…');\n await this.ensureRuntime();\n const runtimeRequest = this.resolveCurrentRequest(version, request.scene.contentKey);\n if (!runtimeRequest) continue;\n ({ version, request } = runtimeRequest);\n const core = this.core!;\n if (this.appliedScene?.contentKey === request.scene.contentKey && !this.forceRelayout) {\n this.appliedScene = request.scene;\n this.syncInteraction(request);\n this.consumeFitIfCurrentTarget();\n this.consumeAnchorIfCurrentTarget();\n this.hideStatus();\n this.processedVersion = version;\n continue;\n }\n\n this.hideTooltip();\n const previousViewport = viewport(core);\n const anchorElementId = this.anchorTargetContentKey === request.scene.contentKey\n ? this.anchorElementId\n : undefined;\n const anchorBefore = anchorElementId === undefined\n ? undefined\n : renderedPosition(core, anchorElementId);\n const topologyChanged = this.appliedScene?.topologyKey !== request.scene.topologyKey;\n const geometryChanged = !this.appliedScene\n || graphLayoutGeometryKey(this.appliedScene) !== graphLayoutGeometryKey(request.scene);\n let laidOut = false;\n\n if (geometryChanged || this.forceRelayout) {\n this.showStatus('layout', 'Laying out graph…');\n const result = await this.environment.layoutScene(this.elk!, request.scene);\n const layoutRequest = this.resolveCurrentRequest(version, request.scene.contentKey);\n if (!layoutRequest) continue;\n ({ version, request } = layoutRequest);\n if (topologyChanged || !this.appliedScene) replaceElements(core, request.scene);\n else updateElementData(core, request.scene);\n applyGraphLayout(core, request.scene, result);\n laidOut = true;\n } else {\n updateElementData(core, request.scene);\n }\n\n syncExpandedGroupLabelGeometry(core);\n this.appliedScene = request.scene;\n this.forceRelayout = false;\n this.failedSceneContentKey = undefined;\n this.syncInteraction(request);\n core.resize();\n if (this.fitTargetContentKey === request.scene.contentKey) {\n core.fit(undefined, GRAPH_GEOMETRY.fitPadding);\n this.fitTargetContentKey = undefined;\n } else if (laidOut && anchorBefore && anchorElementId) {\n restoreViewport(core, previousViewport);\n const anchorAfter = renderedPosition(core, anchorElementId);\n if (anchorAfter) core.panBy({ x: anchorBefore.x - anchorAfter.x, y: anchorBefore.y - anchorAfter.y });\n } else {\n restoreViewport(core, previousViewport);\n }\n this.consumeAnchorIfCurrentTarget();\n this.updateSemanticZoom(true);\n this.hideStatus();\n this.processedVersion = version;\n } catch (error) {\n const failedRequest = this.resolveCurrentRequest(version, request.scene.contentKey);\n if (!failedRequest) continue;\n ({ version, request } = failedRequest);\n this.forceRelayout = false;\n this.failedSceneContentKey = request.scene.contentKey;\n this.processedVersion = version;\n this.showStatus(\n 'error',\n `Failed to render graph: ${error instanceof Error ? error.message : String(error)}`,\n true,\n );\n return;\n }\n }\n }\n\n private async ensureRuntime(): Promise<void> {\n if (this.core || this.destroyed) return;\n this.loading ??= this.loadRuntime().catch((error) => {\n this.loading = undefined;\n throw error;\n });\n await this.loading;\n }\n\n private async loadRuntime(): Promise<void> {\n const runtime = await this.environment.loadRuntime();\n if (this.destroyed) return;\n this.canvasHost.textContent = '';\n this.elk = runtime.elk;\n this.core = runtime.createCore({\n container: this.canvasHost,\n elements: [],\n style: createGraphStyles(),\n layout: { name: 'preset' },\n minZoom: 0.03,\n maxZoom: 4,\n boxSelectionEnabled: false,\n autoungrabify: true,\n autounselectify: true,\n });\n this.bindEvents(this.core);\n }\n\n private bindEvents(core: cytoscape.Core): void {\n const selectElement = (event: cytoscape.EventObject) => {\n const request = this.interactiveRequest();\n if (!request) return;\n const id = event.target.id() as GraphSceneElementId;\n const selection = request.scene.interaction.selectionByElementId.get(id);\n if (selection) request.onSelect(selection);\n const now = performance.now();\n if (this.lastTap?.id === id && now - this.lastTap.at <= 350 && selection?.kind === 'group') {\n request.onToggleGroup(selection.pathKey);\n this.lastTap = undefined;\n } else {\n this.lastTap = { id, at: now };\n }\n };\n core.on('tap', 'node', selectElement);\n core.on('tap', 'edge', selectElement);\n core.on('tap', (event) => {\n if (event.target === core) this.lastTap = undefined;\n });\n const hoverElement = (event: cytoscape.EventObject) => {\n const request = this.interactiveRequest();\n if (!request) return;\n const id = event.target.id() as GraphSceneElementId;\n const selection = request.scene.interaction.selectionByElementId.get(id);\n if (selection) request.onHover(selection);\n const title = event.target.data('tooltip') as string | undefined;\n if (title) {\n this.tooltip.textContent = title;\n this.tooltip.hidden = false;\n this.positionTooltip(event.renderedPosition);\n }\n };\n core.on('mouseover', 'node', hoverElement);\n core.on('mouseover', 'edge', hoverElement);\n const moveTooltip = (event: cytoscape.EventObject) => this.positionTooltip(event.renderedPosition);\n core.on('mousemove', 'node', moveTooltip);\n core.on('mousemove', 'edge', moveTooltip);\n const clearHover = () => {\n this.hideTooltip();\n this.latestRequest?.onHover(undefined);\n };\n core.on('mouseout', 'node', clearHover);\n core.on('mouseout', 'edge', clearHover);\n core.on('zoom', () => this.updateSemanticZoom());\n }\n\n private interactiveRequest(): GraphRenderRequest | undefined {\n const request = this.latestRequest;\n return request && this.appliedScene?.contentKey === request.scene.contentKey ? request : undefined;\n }\n\n private syncInteraction(request: GraphRenderRequest): void {\n const core = this.core;\n if (!core || this.appliedScene?.topologyKey !== request.scene.topologyKey) return;\n core.batch(() => {\n core.elements().removeClass('semantic-selected semantic-hover');\n if (request.hovered) {\n for (const id of request.scene.interaction.relatedElementIdsBySelection.get(selectionKey(request.hovered)) ?? []) {\n core.getElementById(id).addClass('semantic-hover');\n }\n }\n if (request.selected) {\n for (const id of request.scene.interaction.primaryElementIdsBySelection.get(selectionKey(request.selected)) ?? []) {\n core.getElementById(id).addClass('semantic-selected');\n }\n }\n });\n }\n\n private consumeFitIfCurrentTarget(): void {\n const targetContentKey = this.latestRequest?.scene.contentKey;\n if (!targetContentKey\n || this.fitTargetContentKey !== targetContentKey\n || !this.core\n || this.targetNeedsApply()) return;\n this.core.resize();\n this.core.fit(undefined, GRAPH_GEOMETRY.fitPadding);\n this.fitTargetContentKey = undefined;\n this.updateSemanticZoom();\n }\n\n private consumeAnchorIfCurrentTarget(): void {\n const targetContentKey = this.latestRequest?.scene.contentKey;\n if (!targetContentKey || this.anchorTargetContentKey !== targetContentKey) return;\n this.anchorElementId = undefined;\n this.anchorTargetContentKey = undefined;\n }\n\n private targetNeedsApply(): boolean {\n const target = this.latestRequest?.scene;\n if (!target || !this.appliedScene) return target !== undefined;\n return this.forceRelayout || this.appliedScene.contentKey !== target.contentKey;\n }\n\n private updateSemanticZoom(force = false): void {\n const core = this.core;\n if (!core) return;\n const overview = isOverviewGraphScale(core.zoom());\n if (!force && overview === this.overview) return;\n this.overview = overview;\n core.batch(() => {\n for (const node of core.nodes()) {\n node.data('displayLabel', overview ? node.data('overviewLabel') : node.data('detailLabel'));\n }\n for (const edge of core.edges()) {\n edge.data('displayLabel', overview ? '' : edge.data('detailLabel'));\n }\n });\n }\n\n private positionTooltip(position: GraphPosition): void {\n const maxX = Math.max(8, this.host.clientWidth - this.tooltip.offsetWidth - 8);\n const maxY = Math.max(8, this.host.clientHeight - this.tooltip.offsetHeight - 8);\n this.tooltip.style.left = `${Math.max(8, Math.min(maxX, position.x + 12))}px`;\n this.tooltip.style.top = `${Math.max(8, Math.min(maxY, position.y + 12))}px`;\n }\n\n private hideTooltip(): void {\n this.tooltip.hidden = true;\n this.tooltip.textContent = '';\n this.lastTap = undefined;\n }\n\n private showStatus(kind: 'loading' | 'layout' | 'empty' | 'error', message: string, retry = false): void {\n this.status.dataset.state = kind;\n this.statusMessage.textContent = message;\n this.retryButton.hidden = !retry;\n this.status.hidden = false;\n this.host.setAttribute('aria-busy', kind === 'loading' || kind === 'layout' ? 'true' : 'false');\n }\n\n private hideStatus(): void {\n this.status.hidden = true;\n this.retryButton.hidden = true;\n this.host.setAttribute('aria-busy', 'false');\n }\n\n private resolveCurrentRequest(\n version: number,\n contentKey: string,\n ): { readonly version: number; readonly request: GraphRenderRequest } | undefined {\n if (this.destroyed) return undefined;\n const request = this.latestRequest;\n if (!request) return undefined;\n if (version === this.requestVersion || request.scene.contentKey === contentKey) {\n return { version: this.requestVersion, request };\n }\n return undefined;\n }\n}\n\nconst browserGraphRendererEnvironment: GraphRendererEnvironment = {\n createElement: (tagName) => document.createElement(tagName),\n loadRuntime: async () => {\n const [cytoscapeModule, elkModule] = await Promise.all([\n import('cytoscape'),\n import('elkjs/lib/elk.bundled.js'),\n ]);\n return {\n createCore: cytoscapeModule.default,\n elk: new elkModule.default(),\n };\n },\n layoutScene: layoutGraphScene,\n observeResize: (element, onResize) => {\n if (typeof ResizeObserver === 'undefined') return { disconnect: () => undefined };\n const view = element.ownerDocument.defaultView;\n let animationFrame: number | undefined;\n const observer = new ResizeObserver(() => {\n if (!view || animationFrame !== undefined) return;\n animationFrame = view.requestAnimationFrame(() => {\n animationFrame = undefined;\n onResize();\n });\n });\n observer.observe(element);\n return {\n disconnect: () => {\n observer.disconnect();\n if (animationFrame !== undefined) {\n view?.cancelAnimationFrame(animationFrame);\n animationFrame = undefined;\n }\n },\n };\n },\n};\n\nfunction replaceElements(core: cytoscape.Core, scene: GraphScene): void {\n const definitions: cytoscape.ElementDefinition[] = [\n ...scene.nodes.map(nodeDefinition),\n ...scene.edges.map(edgeDefinition),\n ];\n core.startBatch();\n core.elements().remove();\n core.add(definitions);\n core.endBatch();\n}\n\nexport function updateElementData(core: cytoscape.Core, scene: GraphScene): void {\n core.batch(() => {\n for (const node of scene.nodes) {\n const element = core.getElementById(node.id);\n if (element.nonempty()) element.data(nodeRenderableData(node));\n }\n for (const edge of scene.edges) {\n const element = core.getElementById(edge.id);\n if (element.nonempty()) element.data(edgeRenderableData(edge));\n }\n });\n}\n\nfunction nodeDefinition(node: GraphSceneNode): cytoscape.ElementDefinition {\n return {\n group: 'nodes',\n data: {\n id: node.id,\n parent: node.parentId,\n ...nodeRenderableData(node),\n },\n };\n}\n\nfunction nodeRenderableData(node: GraphSceneNode): Record<string, unknown> {\n const dimensions = nodeDimensions(node);\n const labelMaxWidth = node.kind === 'group' && node.collapsed\n ? expandedGroupLabelMaxWidth(dimensions.width)\n : 160;\n return {\n kind: node.kind,\n passKind: node.kind === 'pass' || node.kind === 'culled-pass' ? node.passKind : undefined,\n resourceKind: node.kind === 'resource' ? node.resourceKind : undefined,\n collapsed: node.kind === 'group' && node.collapsed ? 1 : 0,\n hasCulled: node.kind === 'group' && node.culledNodeCount > 0 ? 1 : 0,\n depthBand: node.kind === 'group' ? node.depthBand : undefined,\n detailLabel: node.label,\n overviewLabel: node.overviewLabel,\n displayLabel: node.label,\n tooltip: node.title,\n labelMaxWidth,\n width: dimensions.width,\n height: dimensions.height,\n };\n}\n\nfunction edgeDefinition(edge: GraphSceneEdge): cytoscape.ElementDefinition {\n return {\n group: 'edges',\n data: {\n id: edge.id,\n source: edge.from,\n target: edge.to,\n ...edgeRenderableData(edge),\n },\n };\n}\n\nfunction edgeRenderableData(edge: GraphSceneEdge): Record<string, unknown> {\n const detailLabel = graphEdgeDisplayLabel(edge);\n return {\n kind: edge.kind,\n dependencyKind: edge.kind === 'dependency' ? edge.dependencyKind : undefined,\n accessMode: edge.kind === 'access' ? edge.accessMode : undefined,\n dashed: edge.kind === 'access' && edge.dashed ? 1 : 0,\n detailLabel,\n displayLabel: detailLabel,\n tooltip: edge.title,\n };\n}\n\nfunction viewport(core: cytoscape.Core): Viewport {\n return { pan: { ...core.pan() }, zoom: core.zoom() };\n}\n\nfunction restoreViewport(core: cytoscape.Core, state: Viewport): void {\n core.zoom(state.zoom);\n core.pan(state.pan);\n}\n\nfunction renderedPosition(core: cytoscape.Core, id: GraphSceneElementId): GraphPosition | undefined {\n const element = core.getElementById(id);\n return element.nonempty() && element.isNode() ? { ...element.renderedPosition() } : undefined;\n}\n\nexport {\n createCytoscapeEdgeRouteData,\n createElkLayoutGraph,\n layoutGraphScene,\n} from './panelGraphLayout.ts';\nexport type {\n CytoscapeEdgeRouteData,\n GraphEdgeRoute,\n GraphLayoutResult,\n GraphPosition,\n} from './panelGraphLayout.ts';\nexport {\n createGraphStyles,\n expandedGroupLabelMaxWidth,\n graphEdgeDisplayLabel,\n graphLayoutGeometryKey,\n isOverviewGraphScale,\n} from './panelGraphVisuals.ts';\n"]}
|