@zenfg/inspector 0.1.0-beta.2 → 0.1.0-beta.4

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 (172) hide show
  1. package/GUIDE.md +182 -0
  2. package/README.md +60 -26
  3. package/THEMING.md +124 -0
  4. package/dist/FrameGraphInspector.d.ts +24 -8
  5. package/dist/FrameGraphInspector.d.ts.map +1 -1
  6. package/dist/FrameGraphInspector.js +151 -65
  7. package/dist/FrameGraphInspector.js.map +1 -1
  8. package/dist/capture.d.ts +5 -0
  9. package/dist/capture.d.ts.map +1 -0
  10. package/dist/capture.js +2 -0
  11. package/dist/capture.js.map +1 -0
  12. package/dist/debugCaptureModel.d.ts +28 -4
  13. package/dist/debugCaptureModel.d.ts.map +1 -1
  14. package/dist/debugCaptureModel.js +72 -16
  15. package/dist/debugCaptureModel.js.map +1 -1
  16. package/dist/index.d.ts +2 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/panelAliasAnalysis.d.ts +1 -0
  20. package/dist/panelAliasAnalysis.d.ts.map +1 -1
  21. package/dist/panelAliasAnalysis.js +23 -54
  22. package/dist/panelAliasAnalysis.js.map +1 -1
  23. package/dist/panelCytoscapeGraphRenderer.d.ts +18 -3
  24. package/dist/panelCytoscapeGraphRenderer.d.ts.map +1 -1
  25. package/dist/panelCytoscapeGraphRenderer.js +183 -41
  26. package/dist/panelCytoscapeGraphRenderer.js.map +1 -1
  27. package/dist/panelDetailLayout.d.ts +27 -0
  28. package/dist/panelDetailLayout.d.ts.map +1 -0
  29. package/dist/panelDetailLayout.js +174 -0
  30. package/dist/panelDetailLayout.js.map +1 -0
  31. package/dist/panelDetailStyles.d.ts +2 -0
  32. package/dist/panelDetailStyles.d.ts.map +1 -0
  33. package/dist/panelDetailStyles.js +57 -0
  34. package/dist/panelDetailStyles.js.map +1 -0
  35. package/dist/panelDiagnosticsView.d.ts +10 -1
  36. package/dist/panelDiagnosticsView.d.ts.map +1 -1
  37. package/dist/panelDiagnosticsView.js +167 -58
  38. package/dist/panelDiagnosticsView.js.map +1 -1
  39. package/dist/panelDomHelpers.d.ts.map +1 -1
  40. package/dist/panelDomHelpers.js +6 -5
  41. package/dist/panelDomHelpers.js.map +1 -1
  42. package/dist/panelGraphLayout.d.ts +3 -2
  43. package/dist/panelGraphLayout.d.ts.map +1 -1
  44. package/dist/panelGraphLayout.js +20 -5
  45. package/dist/panelGraphLayout.js.map +1 -1
  46. package/dist/panelGraphRenderer.d.ts +10 -0
  47. package/dist/panelGraphRenderer.d.ts.map +1 -1
  48. package/dist/panelGraphRenderer.js.map +1 -1
  49. package/dist/panelGraphScene.d.ts +15 -26
  50. package/dist/panelGraphScene.d.ts.map +1 -1
  51. package/dist/panelGraphScene.js +191 -208
  52. package/dist/panelGraphScene.js.map +1 -1
  53. package/dist/panelGraphSearch.d.ts +16 -0
  54. package/dist/panelGraphSearch.d.ts.map +1 -0
  55. package/dist/panelGraphSearch.js +93 -0
  56. package/dist/panelGraphSearch.js.map +1 -0
  57. package/dist/panelGraphView.d.ts +82 -0
  58. package/dist/panelGraphView.d.ts.map +1 -1
  59. package/dist/panelGraphView.js +42 -9
  60. package/dist/panelGraphView.js.map +1 -1
  61. package/dist/panelGraphVisuals.d.ts +18 -6
  62. package/dist/panelGraphVisuals.d.ts.map +1 -1
  63. package/dist/panelGraphVisuals.js +156 -116
  64. package/dist/panelGraphVisuals.js.map +1 -1
  65. package/dist/panelInspectorView.d.ts +13 -0
  66. package/dist/panelInspectorView.d.ts.map +1 -1
  67. package/dist/panelInspectorView.js +326 -46
  68. package/dist/panelInspectorView.js.map +1 -1
  69. package/dist/panelListStyles.d.ts +3 -0
  70. package/dist/panelListStyles.d.ts.map +1 -0
  71. package/dist/panelListStyles.js +155 -0
  72. package/dist/panelListStyles.js.map +1 -0
  73. package/dist/panelMemoryStyles.d.ts +3 -0
  74. package/dist/panelMemoryStyles.d.ts.map +1 -0
  75. package/dist/panelMemoryStyles.js +42 -0
  76. package/dist/panelMemoryStyles.js.map +1 -0
  77. package/dist/panelMemoryView.d.ts +13 -1
  78. package/dist/panelMemoryView.d.ts.map +1 -1
  79. package/dist/panelMemoryView.js +161 -41
  80. package/dist/panelMemoryView.js.map +1 -1
  81. package/dist/panelPassesView.d.ts +15 -0
  82. package/dist/panelPassesView.d.ts.map +1 -1
  83. package/dist/panelPassesView.js +202 -72
  84. package/dist/panelPassesView.js.map +1 -1
  85. package/dist/panelRawView.d.ts +15 -0
  86. package/dist/panelRawView.d.ts.map +1 -0
  87. package/dist/panelRawView.js +105 -0
  88. package/dist/panelRawView.js.map +1 -0
  89. package/dist/panelResourcesView.d.ts +8 -0
  90. package/dist/panelResourcesView.d.ts.map +1 -1
  91. package/dist/panelResourcesView.js +62 -31
  92. package/dist/panelResourcesView.js.map +1 -1
  93. package/dist/panelSelection.d.ts +9 -1
  94. package/dist/panelSelection.d.ts.map +1 -1
  95. package/dist/panelSelection.js +36 -6
  96. package/dist/panelSelection.js.map +1 -1
  97. package/dist/panelTheme.d.ts +13 -0
  98. package/dist/panelTheme.d.ts.map +1 -0
  99. package/dist/panelTheme.js +104 -0
  100. package/dist/panelTheme.js.map +1 -0
  101. package/dist/panelTypes.d.ts +18 -4
  102. package/dist/panelTypes.d.ts.map +1 -1
  103. package/dist/panelTypes.js.map +1 -1
  104. package/dist/panelVisualTheme.d.ts +176 -71
  105. package/dist/panelVisualTheme.d.ts.map +1 -1
  106. package/dist/panelVisualTheme.js +34 -36
  107. package/dist/panelVisualTheme.js.map +1 -1
  108. package/dist/panelWorkbenchHelpers.d.ts +12 -1
  109. package/dist/panelWorkbenchHelpers.d.ts.map +1 -1
  110. package/dist/panelWorkbenchHelpers.js +63 -2
  111. package/dist/panelWorkbenchHelpers.js.map +1 -1
  112. package/dist/panelWorkbenchStyles.d.ts +2 -0
  113. package/dist/panelWorkbenchStyles.d.ts.map +1 -0
  114. package/dist/panelWorkbenchStyles.js +101 -0
  115. package/dist/panelWorkbenchStyles.js.map +1 -0
  116. package/dist/panelWorkbenchView.d.ts +16 -0
  117. package/dist/panelWorkbenchView.d.ts.map +1 -1
  118. package/dist/panelWorkbenchView.js +228 -50
  119. package/dist/panelWorkbenchView.js.map +1 -1
  120. package/dist/query.js.map +1 -1
  121. package/dist/styles.d.ts.map +1 -1
  122. package/dist/styles.js +161 -205
  123. package/dist/styles.js.map +1 -1
  124. package/dist/theme.d.ts +22 -0
  125. package/dist/theme.d.ts.map +1 -0
  126. package/dist/theme.js +10 -0
  127. package/dist/theme.js.map +1 -0
  128. package/dist/themeDefinitions.d.ts +251 -0
  129. package/dist/themeDefinitions.d.ts.map +1 -0
  130. package/dist/themeDefinitions.js +96 -0
  131. package/dist/themeDefinitions.js.map +1 -0
  132. package/dist/themePalette.d.ts +66 -0
  133. package/dist/themePalette.d.ts.map +1 -0
  134. package/dist/themePalette.js +37 -0
  135. package/dist/themePalette.js.map +1 -0
  136. package/dist/themes.css +165 -0
  137. package/package.json +44 -10
  138. package/src/FrameGraphInspector.ts +145 -68
  139. package/src/capture.ts +4 -0
  140. package/src/debugCaptureModel.ts +97 -21
  141. package/src/index.ts +6 -2
  142. package/src/panelAliasAnalysis.ts +24 -70
  143. package/src/panelCytoscapeGraphRenderer.ts +177 -40
  144. package/src/panelDetailLayout.ts +155 -0
  145. package/src/panelDetailStyles.ts +56 -0
  146. package/src/panelDiagnosticsView.ts +167 -86
  147. package/src/panelDomHelpers.ts +4 -5
  148. package/src/panelGraphLayout.ts +20 -5
  149. package/src/panelGraphRenderer.ts +7 -0
  150. package/src/panelGraphScene.ts +204 -292
  151. package/src/panelGraphSearch.ts +96 -0
  152. package/src/panelGraphView.ts +41 -9
  153. package/src/panelGraphVisuals.ts +152 -118
  154. package/src/panelInspectorView.ts +315 -48
  155. package/src/panelListStyles.ts +154 -0
  156. package/src/panelMemoryStyles.ts +41 -0
  157. package/src/panelMemoryView.ts +178 -53
  158. package/src/panelPassesView.ts +190 -137
  159. package/src/panelRawView.ts +99 -0
  160. package/src/panelResourcesView.ts +69 -56
  161. package/src/panelSelection.ts +39 -7
  162. package/src/panelTheme.ts +85 -0
  163. package/src/panelTypes.ts +16 -4
  164. package/src/panelVisualTheme.ts +38 -38
  165. package/src/panelWorkbenchHelpers.ts +62 -4
  166. package/src/panelWorkbenchStyles.ts +100 -0
  167. package/src/panelWorkbenchView.ts +201 -52
  168. package/src/query.ts +11 -11
  169. package/src/styles.ts +162 -205
  170. package/src/theme.ts +24 -0
  171. package/src/themeDefinitions.ts +103 -0
  172. package/src/themePalette.ts +44 -0
@@ -1,6 +1,7 @@
1
+ import { GRAPH_VISUAL_THEME } from "./panelVisualTheme.js";
1
2
  import { applyGraphLayout, layoutGraphScene, syncExpandedGroupLabelGeometry, } from "./panelGraphLayout.js";
2
3
  import { selectionKey, } from "./panelGraphScene.js";
3
- import { createGraphStyles, expandedGroupLabelMaxWidth, GRAPH_GEOMETRY, graphEdgeDisplayLabel, graphLayoutGeometryKey, isOverviewGraphScale, nodeDimensions, } from "./panelGraphVisuals.js";
4
+ import { createGraphStyles, fitGraphLabel, fitCollapsedGroupLabel, graphLabelFontSize, expandedGroupLabelMaxWidth, GRAPH_GEOMETRY, graphEdgeDisplayLabel, graphLayoutGeometryKey, isOverviewGraphScale, nodeDimensions, } from "./panelGraphVisuals.js";
4
5
  export class CytoscapeGraphRenderer {
5
6
  host;
6
7
  environment;
@@ -10,6 +11,9 @@ export class CytoscapeGraphRenderer {
10
11
  statusMessage;
11
12
  retryButton;
12
13
  resizeSubscription;
14
+ theme = GRAPH_VISUAL_THEME;
15
+ geometryRevision = 0;
16
+ wasVisible = false;
13
17
  core;
14
18
  elk;
15
19
  loading;
@@ -25,15 +29,30 @@ export class CytoscapeGraphRenderer {
25
29
  anchorTargetContentKey;
26
30
  failedSceneContentKey;
27
31
  overview = false;
28
- lastTap;
32
+ dragPanning = false;
33
+ pointerInside = false;
34
+ pointerOverElement = false;
35
+ cancelPointerInteraction = () => {
36
+ this.dragPanning = false;
37
+ this.cancelGroupTap();
38
+ this.clearHover();
39
+ };
40
+ pendingReveal;
41
+ pendingGroupTap;
29
42
  constructor(host, environment = browserGraphRendererEnvironment) {
30
43
  this.host = host;
31
44
  this.environment = environment;
32
45
  this.canvasHost = environment.createElement('div');
33
46
  this.canvasHost.className = 'zenfg-inspector-graph-canvas';
47
+ this.updateCursor();
34
48
  this.tooltip = environment.createElement('div');
35
49
  this.tooltip.className = 'zenfg-inspector-graph-tooltip';
36
50
  this.tooltip.hidden = true;
51
+ // Cytoscape's node mouseout is not guaranteed when leaving its canvas directly.
52
+ this.canvasHost.addEventListener('pointerenter', () => { this.pointerInside = true; });
53
+ this.canvasHost.addEventListener('pointerleave', () => { this.pointerInside = false; this.clearHover(); });
54
+ this.canvasHost.addEventListener('pointercancel', this.cancelPointerInteraction);
55
+ this.canvasHost.ownerDocument?.defaultView?.addEventListener('blur', this.cancelPointerInteraction);
37
56
  this.status = environment.createElement('div');
38
57
  this.status.className = 'zenfg-inspector-graph-status';
39
58
  this.status.hidden = true;
@@ -47,12 +66,36 @@ export class CytoscapeGraphRenderer {
47
66
  this.retryButton.addEventListener('click', () => this.relayout());
48
67
  this.status.append(this.statusMessage, this.retryButton);
49
68
  this.host.replaceChildren(this.canvasHost, this.status, this.tooltip);
50
- this.resizeSubscription = environment.observeResize?.(host, () => this.core?.resize());
69
+ this.resizeSubscription = environment.observeResize?.(host, () => {
70
+ const visible = host.clientWidth > 0 && host.clientHeight > 0;
71
+ if (visible && !this.wasVisible)
72
+ this.latestRequest?.onVisible?.();
73
+ this.wasVisible = visible;
74
+ this.core?.resize();
75
+ });
51
76
  }
52
77
  render(request) {
53
78
  if (this.destroyed)
54
79
  return;
80
+ if (request.theme)
81
+ this.setTheme(request.theme);
82
+ if (this.latestRequest?.scene !== request.scene
83
+ || (this.latestRequest?.selected && selectionKey(this.latestRequest.selected)) !== (request.selected && selectionKey(request.selected))) {
84
+ this.cancelGroupTap();
85
+ }
86
+ if ((!request.hovered && !this.tooltip.hidden) || this.latestRequest?.scene.contentKey !== request.scene.contentKey) {
87
+ this.hideTooltip();
88
+ this.core?.elements().removeClass('semantic-hover');
89
+ }
55
90
  this.latestRequest = request;
91
+ if (request.reveal) {
92
+ this.pendingReveal = { request: request.reveal, contentKey: request.scene.contentKey, captureRevision: request.captureRevision };
93
+ }
94
+ else if (this.pendingReveal && (this.pendingReveal.captureRevision !== request.captureRevision
95
+ || this.pendingReveal.contentKey !== request.scene.contentKey
96
+ || !request.selected || selectionKey(request.selected) !== selectionKey(this.pendingReveal.request.selection))) {
97
+ this.pendingReveal = undefined;
98
+ }
56
99
  if (request.fit) {
57
100
  this.fitTargetContentKey = request.scene.contentKey;
58
101
  }
@@ -82,14 +125,46 @@ export class CytoscapeGraphRenderer {
82
125
  this.syncInteraction(request);
83
126
  this.consumeFitIfCurrentTarget();
84
127
  this.consumeAnchorIfCurrentTarget();
128
+ this.consumeReveal();
85
129
  this.processedVersion = version;
86
130
  return;
87
131
  }
88
132
  this.scheduleProcessing();
89
133
  }
134
+ setTheme(theme) {
135
+ if (this.destroyed || JSON.stringify(theme) === JSON.stringify(this.theme))
136
+ return;
137
+ const typographyChanged = theme.fontFamily !== this.theme.fontFamily || theme.fontSize !== this.theme.fontSize;
138
+ this.theme = theme;
139
+ this.core?.style().fromJson(createGraphStyles(theme)).update();
140
+ if (!typographyChanged)
141
+ return;
142
+ this.geometryRevision++;
143
+ if (this.core && this.latestRequest && this.appliedScene) {
144
+ const selected = this.core.nodes('.semantic-selected').first();
145
+ const center = this.core.extent();
146
+ const target = selected.nonempty() ? selected : this.core.nodes().filter(node => !node.isParent()).sort((a, b) => {
147
+ const distance = (node) => Math.hypot(node.position('x') - center.x1 - center.w / 2, node.position('y') - center.y1 - center.h / 2);
148
+ return distance(a) - distance(b);
149
+ }).first();
150
+ if (target.nonempty()) {
151
+ this.anchorElementId = target.id();
152
+ this.anchorTargetContentKey = this.latestRequest.scene.contentKey;
153
+ }
154
+ this.forceRelayout = true;
155
+ ++this.requestVersion;
156
+ this.scheduleProcessing();
157
+ }
158
+ this.updateSemanticZoom(true);
159
+ }
90
160
  resize() {
91
161
  this.core?.resize();
92
162
  }
163
+ cancelReveal() {
164
+ this.pendingReveal = undefined;
165
+ this.cancelGroupTap();
166
+ this.clearHover();
167
+ }
93
168
  fit() {
94
169
  const targetContentKey = this.latestRequest?.scene.contentKey;
95
170
  if (targetContentKey)
@@ -105,6 +180,7 @@ export class CytoscapeGraphRenderer {
105
180
  relayout() {
106
181
  if (!this.latestRequest || this.destroyed)
107
182
  return;
183
+ this.cancelGroupTap();
108
184
  this.failedSceneContentKey = undefined;
109
185
  this.forceRelayout = true;
110
186
  this.fitTargetContentKey = this.latestRequest.scene.contentKey;
@@ -118,8 +194,10 @@ export class CytoscapeGraphRenderer {
118
194
  if (this.destroyed)
119
195
  return;
120
196
  this.destroyed = true;
197
+ this.cancelGroupTap();
121
198
  this.requestVersion++;
122
199
  this.resizeSubscription?.disconnect();
200
+ this.canvasHost.ownerDocument?.defaultView?.removeEventListener('blur', this.cancelPointerInteraction);
123
201
  this.hideTooltip();
124
202
  this.core?.destroy();
125
203
  this.core = undefined;
@@ -153,7 +231,7 @@ export class CytoscapeGraphRenderer {
153
231
  if (request.scene.nodes.length === 0) {
154
232
  this.hideTooltip();
155
233
  if (this.core)
156
- replaceElements(this.core, request.scene);
234
+ replaceElements(this.core, request.scene, this.theme);
157
235
  this.appliedScene = request.scene;
158
236
  this.forceRelayout = false;
159
237
  if (this.fitTargetContentKey === request.scene.contentKey)
@@ -178,6 +256,7 @@ export class CytoscapeGraphRenderer {
178
256
  this.syncInteraction(request);
179
257
  this.consumeFitIfCurrentTarget();
180
258
  this.consumeAnchorIfCurrentTarget();
259
+ this.consumeReveal();
181
260
  this.hideStatus();
182
261
  this.processedVersion = version;
183
262
  continue;
@@ -196,20 +275,23 @@ export class CytoscapeGraphRenderer {
196
275
  let laidOut = false;
197
276
  if (geometryChanged || this.forceRelayout) {
198
277
  this.showStatus('layout', 'Laying out graph…');
199
- const result = await this.environment.layoutScene(this.elk, request.scene);
278
+ const geometryRevision = this.geometryRevision;
279
+ const result = await this.environment.layoutScene(this.elk, request.scene, this.theme);
280
+ if (geometryRevision !== this.geometryRevision)
281
+ continue;
200
282
  const layoutRequest = this.resolveCurrentRequest(version, request.scene.contentKey);
201
283
  if (!layoutRequest)
202
284
  continue;
203
285
  ({ version, request } = layoutRequest);
204
286
  if (topologyChanged || !this.appliedScene)
205
- replaceElements(core, request.scene);
287
+ replaceElements(core, request.scene, this.theme);
206
288
  else
207
- updateElementData(core, request.scene);
289
+ updateElementData(core, request.scene, this.theme);
208
290
  applyGraphLayout(core, request.scene, result);
209
291
  laidOut = true;
210
292
  }
211
293
  else {
212
- updateElementData(core, request.scene);
294
+ updateElementData(core, request.scene, this.theme);
213
295
  }
214
296
  syncExpandedGroupLabelGeometry(core);
215
297
  this.appliedScene = request.scene;
@@ -231,6 +313,7 @@ export class CytoscapeGraphRenderer {
231
313
  restoreViewport(core, previousViewport);
232
314
  }
233
315
  this.consumeAnchorIfCurrentTarget();
316
+ this.consumeReveal();
234
317
  this.updateSemanticZoom(true);
235
318
  this.hideStatus();
236
319
  this.processedVersion = version;
@@ -266,7 +349,7 @@ export class CytoscapeGraphRenderer {
266
349
  this.core = runtime.createCore({
267
350
  container: this.canvasHost,
268
351
  elements: [],
269
- style: createGraphStyles(),
352
+ style: createGraphStyles(this.theme),
270
353
  layout: { name: 'preset' },
271
354
  minZoom: 0.03,
272
355
  maxZoom: 4,
@@ -284,29 +367,38 @@ export class CytoscapeGraphRenderer {
284
367
  return;
285
368
  const id = event.target.id();
286
369
  const selection = request.scene.interaction.selectionByElementId.get(id);
287
- if (selection)
288
- request.onSelect(selection);
289
- const now = performance.now();
290
- if (this.lastTap?.id === id && now - this.lastTap.at <= 350 && selection?.kind === 'group') {
370
+ const doubleTap = this.pendingGroupTap?.id === id;
371
+ this.cancelGroupTap();
372
+ if (selection?.kind !== 'group') {
373
+ if (selection)
374
+ request.onSelect(selection);
375
+ }
376
+ else if (doubleTap) {
291
377
  request.onToggleGroup(selection.pathKey);
292
- this.lastTap = undefined;
293
378
  }
294
379
  else {
295
- this.lastTap = { id, at: now };
380
+ // Defer group selection so expansion does not replace the inspected resource.
381
+ this.pendingGroupTap = { id, timer: setTimeout(() => {
382
+ this.pendingGroupTap = undefined;
383
+ this.interactiveRequest()?.onSelect(selection);
384
+ }, 350) };
296
385
  }
297
386
  };
298
387
  core.on('tap', 'node', selectElement);
299
388
  core.on('tap', 'edge', selectElement);
300
389
  core.on('tap', (event) => {
301
390
  if (event.target === core)
302
- this.lastTap = undefined;
391
+ this.cancelGroupTap();
303
392
  });
304
393
  const hoverElement = (event) => {
305
394
  const request = this.interactiveRequest();
306
- if (!request)
395
+ if (!request || this.dragPanning)
307
396
  return;
308
397
  const id = event.target.id();
309
398
  const selection = request.scene.interaction.selectionByElementId.get(id);
399
+ this.pointerInside = true;
400
+ this.pointerOverElement = !!selection;
401
+ this.updateCursor();
310
402
  if (selection)
311
403
  request.onHover(selection);
312
404
  const title = event.target.data('tooltip');
@@ -321,12 +413,22 @@ export class CytoscapeGraphRenderer {
321
413
  const moveTooltip = (event) => this.positionTooltip(event.renderedPosition);
322
414
  core.on('mousemove', 'node', moveTooltip);
323
415
  core.on('mousemove', 'edge', moveTooltip);
324
- const clearHover = () => {
325
- this.hideTooltip();
326
- this.latestRequest?.onHover(undefined);
327
- };
416
+ const clearHover = () => this.clearHover();
328
417
  core.on('mouseout', 'node', clearHover);
329
418
  core.on('mouseout', 'edge', clearHover);
419
+ core.on('dragpan', () => {
420
+ if (this.dragPanning)
421
+ return;
422
+ this.dragPanning = true;
423
+ this.cancelGroupTap();
424
+ this.clearHover();
425
+ });
426
+ core.on('tapend', (event) => {
427
+ this.dragPanning = false;
428
+ this.pointerOverElement = this.pointerInside && event.target !== core
429
+ && !!this.interactiveRequest()?.scene.interaction.selectionByElementId.get(event.target.id());
430
+ this.updateCursor();
431
+ });
330
432
  core.on('zoom', () => this.updateSemanticZoom());
331
433
  }
332
434
  interactiveRequest() {
@@ -340,7 +442,7 @@ export class CytoscapeGraphRenderer {
340
442
  core.batch(() => {
341
443
  core.elements().removeClass('semantic-selected semantic-hover');
342
444
  if (request.hovered) {
343
- for (const id of request.scene.interaction.relatedElementIdsBySelection.get(selectionKey(request.hovered)) ?? []) {
445
+ for (const id of request.scene.interaction.hoverElementIdsBySelection.get(selectionKey(request.hovered)) ?? []) {
344
446
  core.getElementById(id).addClass('semantic-hover');
345
447
  }
346
448
  }
@@ -370,6 +472,27 @@ export class CytoscapeGraphRenderer {
370
472
  this.anchorElementId = undefined;
371
473
  this.anchorTargetContentKey = undefined;
372
474
  }
475
+ consumeReveal() {
476
+ const pending = this.pendingReveal;
477
+ const request = this.latestRequest;
478
+ if (!pending || !request || !this.core || this.targetNeedsApply())
479
+ return;
480
+ this.pendingReveal = undefined;
481
+ if (pending.captureRevision !== request.captureRevision || pending.contentKey !== request.scene.contentKey)
482
+ return;
483
+ const ids = request.scene.interaction.primaryElementIdsBySelection.get(selectionKey(pending.request.selection)) ?? [];
484
+ const targets = this.core.collection();
485
+ for (const id of ids)
486
+ targets.merge(this.core.getElementById(id));
487
+ if (!targets.length)
488
+ return;
489
+ this.core.resize();
490
+ this.core.fit(targets, 64);
491
+ if (this.core.zoom() > 1.2)
492
+ this.core.zoom(1.2);
493
+ this.core.center(targets);
494
+ this.updateSemanticZoom();
495
+ }
373
496
  targetNeedsApply() {
374
497
  const target = this.latestRequest?.scene;
375
498
  if (!target || !this.appliedScene)
@@ -380,7 +503,7 @@ export class CytoscapeGraphRenderer {
380
503
  const core = this.core;
381
504
  if (!core)
382
505
  return;
383
- const overview = isOverviewGraphScale(core.zoom());
506
+ const overview = isOverviewGraphScale(core.zoom(), this.theme);
384
507
  if (!force && overview === this.overview)
385
508
  return;
386
509
  this.overview = overview;
@@ -402,7 +525,20 @@ export class CytoscapeGraphRenderer {
402
525
  hideTooltip() {
403
526
  this.tooltip.hidden = true;
404
527
  this.tooltip.textContent = '';
405
- this.lastTap = undefined;
528
+ }
529
+ cancelGroupTap() {
530
+ if (this.pendingGroupTap)
531
+ clearTimeout(this.pendingGroupTap.timer);
532
+ this.pendingGroupTap = undefined;
533
+ }
534
+ clearHover() {
535
+ this.pointerOverElement = false;
536
+ this.updateCursor();
537
+ this.hideTooltip();
538
+ this.latestRequest?.onHover(undefined);
539
+ }
540
+ updateCursor() {
541
+ this.canvasHost.style.cursor = this.dragPanning ? 'grabbing' : this.pointerOverElement ? 'pointer' : 'grab';
406
542
  }
407
543
  showStatus(kind, message, retry = false) {
408
544
  this.status.dataset.state = kind;
@@ -466,9 +602,9 @@ const browserGraphRendererEnvironment = {
466
602
  };
467
603
  },
468
604
  };
469
- function replaceElements(core, scene) {
605
+ function replaceElements(core, scene, theme = GRAPH_VISUAL_THEME) {
470
606
  const definitions = [
471
- ...scene.nodes.map(nodeDefinition),
607
+ ...scene.nodes.map(node => nodeDefinition(node, theme)),
472
608
  ...scene.edges.map(edgeDefinition),
473
609
  ];
474
610
  core.startBatch();
@@ -476,12 +612,12 @@ function replaceElements(core, scene) {
476
612
  core.add(definitions);
477
613
  core.endBatch();
478
614
  }
479
- export function updateElementData(core, scene) {
615
+ export function updateElementData(core, scene, theme = GRAPH_VISUAL_THEME) {
480
616
  core.batch(() => {
481
617
  for (const node of scene.nodes) {
482
618
  const element = core.getElementById(node.id);
483
619
  if (element.nonempty())
484
- element.data(nodeRenderableData(node));
620
+ element.data(nodeRenderableData(node, theme));
485
621
  }
486
622
  for (const edge of scene.edges) {
487
623
  const element = core.getElementById(edge.id);
@@ -490,31 +626,40 @@ export function updateElementData(core, scene) {
490
626
  }
491
627
  });
492
628
  }
493
- function nodeDefinition(node) {
629
+ function nodeDefinition(node, theme) {
494
630
  return {
495
631
  group: 'nodes',
632
+ pannable: true,
496
633
  data: {
497
634
  id: node.id,
498
635
  parent: node.parentId,
499
- ...nodeRenderableData(node),
636
+ ...nodeRenderableData(node, theme),
500
637
  },
501
638
  };
502
639
  }
503
- function nodeRenderableData(node) {
504
- const dimensions = nodeDimensions(node);
640
+ function nodeRenderableData(node, theme) {
641
+ const dimensions = nodeDimensions(node, theme);
505
642
  const labelMaxWidth = node.kind === 'group' && node.collapsed
506
643
  ? expandedGroupLabelMaxWidth(dimensions.width)
507
- : 160;
644
+ : 160 * theme.fontSize / GRAPH_GEOMETRY.baseFontSize;
645
+ const fitLabel = (label) => node.kind !== 'group'
646
+ ? fitGraphLabel(label, labelMaxWidth, theme)
647
+ : node.collapsed ? fitCollapsedGroupLabel(label, labelMaxWidth, theme) : label;
648
+ const detailLabel = fitLabel(node.label);
649
+ const labelFontSize = node.kind === 'group' && !node.collapsed
650
+ ? theme.fontSize * 1.2
651
+ : graphLabelFontSize(detailLabel, labelMaxWidth, dimensions.height - 12 * theme.fontSize / GRAPH_GEOMETRY.baseFontSize, theme);
508
652
  return {
509
653
  kind: node.kind,
510
- passKind: node.kind === 'pass' || node.kind === 'culled-pass' ? node.passKind : undefined,
511
- resourceKind: node.kind === 'resource' ? node.resourceKind : undefined,
654
+ passKind: node.kind === 'pass' ? node.passKind : undefined,
655
+ resourceKind: (node.kind === 'resource' || node.kind === 'root') ? node.resourceKind : undefined,
512
656
  collapsed: node.kind === 'group' && node.collapsed ? 1 : 0,
513
657
  hasCulled: node.kind === 'group' && node.culledNodeCount > 0 ? 1 : 0,
514
658
  depthBand: node.kind === 'group' ? node.depthBand : undefined,
515
- detailLabel: node.label,
516
- overviewLabel: node.overviewLabel,
517
- displayLabel: node.label,
659
+ detailLabel,
660
+ labelFontSize,
661
+ overviewLabel: fitLabel(node.overviewLabel),
662
+ displayLabel: detailLabel,
518
663
  tooltip: node.title,
519
664
  labelMaxWidth,
520
665
  width: dimensions.width,
@@ -536,9 +681,6 @@ function edgeRenderableData(edge) {
536
681
  const detailLabel = graphEdgeDisplayLabel(edge);
537
682
  return {
538
683
  kind: edge.kind,
539
- dependencyKind: edge.kind === 'dependency' ? edge.dependencyKind : undefined,
540
- accessMode: edge.kind === 'access' ? edge.accessMode : undefined,
541
- dashed: edge.kind === 'access' && edge.dashed ? 1 : 0,
542
684
  detailLabel,
543
685
  displayLabel: detailLabel,
544
686
  tooltip: edge.title,