@ltm-blueverse/alpha-semantic-hub 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1674 -0
  3. package/explorer/README.md +282 -0
  4. package/explorer/src/App.css +0 -0
  5. package/explorer/src/App.tsx +2035 -0
  6. package/explorer/src/ErrorBoundary.tsx +112 -0
  7. package/explorer/src/ExploreWorkspaceTabs.tsx +29 -0
  8. package/explorer/src/assets/hero.png +0 -0
  9. package/explorer/src/assets/react.svg +1 -0
  10. package/explorer/src/assets/vite.svg +1 -0
  11. package/explorer/src/explorerCapabilities.ts +24 -0
  12. package/explorer/src/index.css +71 -0
  13. package/explorer/src/main.tsx +7 -0
  14. package/explorer/src/store/edgePairKeys.d.ts +2 -0
  15. package/explorer/src/store/edgePairKeys.js +7 -0
  16. package/explorer/src/store/graphStore.ts +183 -0
  17. package/explorer/src/store/registryStore.ts +78 -0
  18. package/explorer/src/types.d.ts +16 -0
  19. package/explorer/src/ui/primitives.css +414 -0
  20. package/explorer/src/ui/primitives.tsx +170 -0
  21. package/explorer/src/workspaces/DecisionWorkspace/DecisionWorkspace.tsx +279 -0
  22. package/explorer/src/workspaces/DiffMergeWorkspace/DiffMergeWorkspace.tsx +121 -0
  23. package/explorer/src/workspaces/EnrichWorkspace/EntityResolutionTab.tsx +454 -0
  24. package/explorer/src/workspaces/EnrichWorkspace/RegistryTab.tsx +285 -0
  25. package/explorer/src/workspaces/GraphWorkspace/GraphCanvas.tsx +2673 -0
  26. package/explorer/src/workspaces/GraphWorkspace/GraphInspectorPanel.tsx +771 -0
  27. package/explorer/src/workspaces/GraphWorkspace/GraphLoadingOverlay.tsx +410 -0
  28. package/explorer/src/workspaces/GraphWorkspace/GraphWorkspace.tsx +3584 -0
  29. package/explorer/src/workspaces/GraphWorkspace/MarkdownContentViewer.tsx +705 -0
  30. package/explorer/src/workspaces/GraphWorkspace/SigmaSceneAdapter.tsx +55 -0
  31. package/explorer/src/workspaces/GraphWorkspace/TimelinePanel.tsx +193 -0
  32. package/explorer/src/workspaces/GraphWorkspace/behaviors/clickSelectionBehavior.ts +29 -0
  33. package/explorer/src/workspaces/GraphWorkspace/behaviors/fitViewBehavior.ts +15 -0
  34. package/explorer/src/workspaces/GraphWorkspace/behaviors/focusCameraBehavior.ts +25 -0
  35. package/explorer/src/workspaces/GraphWorkspace/behaviors/hoverActivationBehavior.ts +15 -0
  36. package/explorer/src/workspaces/GraphWorkspace/behaviors/pathHighlightBehavior.ts +52 -0
  37. package/explorer/src/workspaces/GraphWorkspace/behaviors/searchFocusBehavior.ts +36 -0
  38. package/explorer/src/workspaces/GraphWorkspace/behaviors/types.ts +41 -0
  39. package/explorer/src/workspaces/GraphWorkspace/behaviors/viewModeSwitchBehavior.ts +29 -0
  40. package/explorer/src/workspaces/GraphWorkspace/graphAnalytics.ts +779 -0
  41. package/explorer/src/workspaces/GraphWorkspace/graphColorLegend.ts +36 -0
  42. package/explorer/src/workspaces/GraphWorkspace/graphConfig.ts +102 -0
  43. package/explorer/src/workspaces/GraphWorkspace/graphEntityShape.ts +34 -0
  44. package/explorer/src/workspaces/GraphWorkspace/graphLoading.ts +84 -0
  45. package/explorer/src/workspaces/GraphWorkspace/graphSceneLayers.ts +775 -0
  46. package/explorer/src/workspaces/GraphWorkspace/graphSceneState.ts +2753 -0
  47. package/explorer/src/workspaces/GraphWorkspace/graphStructureLayer.ts +335 -0
  48. package/explorer/src/workspaces/GraphWorkspace/graphTheme.ts +1078 -0
  49. package/explorer/src/workspaces/GraphWorkspace/graphViewCopy.ts +41 -0
  50. package/explorer/src/workspaces/GraphWorkspace/localGraphTransition.ts +9 -0
  51. package/explorer/src/workspaces/GraphWorkspace/markdownEditorState.ts +114 -0
  52. package/explorer/src/workspaces/GraphWorkspace/markdownResourceClient.ts +103 -0
  53. package/explorer/src/workspaces/GraphWorkspace/markdownUrlSafety.ts +29 -0
  54. package/explorer/src/workspaces/GraphWorkspace/nodeMarkdownSync.ts +81 -0
  55. package/explorer/src/workspaces/GraphWorkspace/pluginRegistryPredicates.ts +24 -0
  56. package/explorer/src/workspaces/GraphWorkspace/plugins/explorationEffectsPlugin.tsx +353 -0
  57. package/explorer/src/workspaces/GraphWorkspace/plugins/explorationEffectsPluginPhaseC.tsx +522 -0
  58. package/explorer/src/workspaces/GraphWorkspace/plugins/index.ts +16 -0
  59. package/explorer/src/workspaces/GraphWorkspace/plugins/legendPlugin.tsx +120 -0
  60. package/explorer/src/workspaces/GraphWorkspace/plugins/neighborhoodPanelPlugin.tsx +231 -0
  61. package/explorer/src/workspaces/GraphWorkspace/plugins/temporalOverlayPlugin.tsx +142 -0
  62. package/explorer/src/workspaces/GraphWorkspace/plugins/types.ts +106 -0
  63. package/explorer/src/workspaces/GraphWorkspace/realtimeGraphAttributes.ts +50 -0
  64. package/explorer/src/workspaces/GraphWorkspace/scene.ts +79 -0
  65. package/explorer/src/workspaces/GraphWorkspace/sigmaNativeRendering.ts +421 -0
  66. package/explorer/src/workspaces/GraphWorkspace/smallGraphLayout.ts +135 -0
  67. package/explorer/src/workspaces/GraphWorkspace/temporalLifecyclePredicates.ts +31 -0
  68. package/explorer/src/workspaces/GraphWorkspace/temporalScrubberBounds.ts +46 -0
  69. package/explorer/src/workspaces/GraphWorkspace/temporalSnapshotGuards.ts +113 -0
  70. package/explorer/src/workspaces/GraphWorkspace/types.ts +395 -0
  71. package/explorer/src/workspaces/GraphWorkspace/useLoadGraph.ts +746 -0
  72. package/explorer/src/workspaces/GraphWorkspace/useMarkdownEditor.ts +177 -0
  73. package/explorer/src/workspaces/ImportExportWorkspace/ImportExportWorkspace.tsx +196 -0
  74. package/explorer/src/workspaces/LineageWorkspace/LineageDiagram.tsx +177 -0
  75. package/explorer/src/workspaces/ManageWorkspace/KGOverviewTab.tsx +291 -0
  76. package/explorer/src/workspaces/ManageWorkspace/OntologySummaryTab.tsx +346 -0
  77. package/explorer/src/workspaces/MemoryWorkspace.tsx +464 -0
  78. package/explorer/src/workspaces/OntologyWorkspace/AlignmentsTab.tsx +437 -0
  79. package/explorer/src/workspaces/OntologyWorkspace/HealthTab.tsx +214 -0
  80. package/explorer/src/workspaces/OntologyWorkspace/OntologyEditor.tsx +770 -0
  81. package/explorer/src/workspaces/OntologyWorkspace/OntologyLoader.tsx +913 -0
  82. package/explorer/src/workspaces/OntologyWorkspace/OntologyManager.tsx +930 -0
  83. package/explorer/src/workspaces/OntologyWorkspace/OntologySearch.tsx +590 -0
  84. package/explorer/src/workspaces/OntologyWorkspace/ProposalReview.tsx +429 -0
  85. package/explorer/src/workspaces/OntologyWorkspace/SKOSVocabularyManager.tsx +696 -0
  86. package/explorer/src/workspaces/OntologyWorkspace/ShaclStudio.tsx +363 -0
  87. package/explorer/src/workspaces/OntologyWorkspace/VersionsTab.tsx +546 -0
  88. package/explorer/src/workspaces/OntologyWorkspace/api.ts +164 -0
  89. package/explorer/src/workspaces/OntologyWorkspace/index.tsx +101 -0
  90. package/explorer/src/workspaces/OntologyWorkspace/ontologyEditorModel.ts +87 -0
  91. package/explorer/src/workspaces/OntologyWorkspace/ontologyUrlState.ts +94 -0
  92. package/explorer/src/workspaces/OntologyWorkspace/types.ts +119 -0
  93. package/explorer/src/workspaces/ReasoningWorkspace.tsx +243 -0
  94. package/explorer/src/workspaces/SparqlWorkspace/SparqlWorkspace.tsx +271 -0
  95. package/explorer/src/workspaces/VocabularyWorkspace/ConceptTree.tsx +97 -0
  96. package/explorer/src/workspaces/VocabularyWorkspace/ImportDropzone.tsx +95 -0
  97. package/explorer/src/workspaces/VocabularyWorkspace/PropertyPanel.tsx +116 -0
  98. package/explorer/src/workspaces/VocabularyWorkspace/Sidebar.tsx +112 -0
  99. package/explorer/src/workspaces/VocabularyWorkspace/VocabularyWorkspace.tsx +70 -0
  100. package/explorer/src/workspaces/VocabularyWorkspace/queries.ts +52 -0
  101. package/explorer/src/workspaces/VocabularyWorkspace/types.ts +24 -0
  102. package/package.json +31 -0
  103. package/plugins/.claude-plugin/README.md +262 -0
  104. package/plugins/.claude-plugin/marketplace.json +20 -0
  105. package/plugins/.claude-plugin/plugin.json +33 -0
  106. package/plugins/.cline-plugin/README.md +32 -0
  107. package/plugins/.cline-plugin/marketplace.json +17 -0
  108. package/plugins/.cline-plugin/plugin.json +35 -0
  109. package/plugins/.codex-plugin/marketplace.json +21 -0
  110. package/plugins/.codex-plugin/plugin.json +35 -0
  111. package/plugins/.continue-plugin/README.md +34 -0
  112. package/plugins/.continue-plugin/marketplace.json +17 -0
  113. package/plugins/.continue-plugin/plugin.json +33 -0
  114. package/plugins/.cursor-plugin/marketplace.json +18 -0
  115. package/plugins/.cursor-plugin/plugin.json +32 -0
  116. package/plugins/.openclaw-plugin/README.md +62 -0
  117. package/plugins/.openclaw-plugin/marketplace.json +18 -0
  118. package/plugins/.openclaw-plugin/plugin.json +46 -0
  119. package/plugins/.pi-plugin/README.md +35 -0
  120. package/plugins/.vscode-plugin/README.md +48 -0
  121. package/plugins/.vscode-plugin/marketplace.json +17 -0
  122. package/plugins/.vscode-plugin/plugin.json +35 -0
  123. package/plugins/.windsurf-plugin/README.md +39 -0
  124. package/plugins/.windsurf-plugin/marketplace.json +17 -0
  125. package/plugins/.windsurf-plugin/plugin.json +35 -0
  126. package/plugins/agents/decision-advisor.md +126 -0
  127. package/plugins/agents/explainability.md +129 -0
  128. package/plugins/agents/kg-assistant.md +73 -0
  129. package/plugins/hooks/hooks.json +12 -0
  130. package/plugins/skills/causal/SKILL.md +74 -0
  131. package/plugins/skills/change/SKILL.md +80 -0
  132. package/plugins/skills/decision/SKILL.md +197 -0
  133. package/plugins/skills/deduplicate/SKILL.md +41 -0
  134. package/plugins/skills/embed/SKILL.md +230 -0
  135. package/plugins/skills/explain/SKILL.md +44 -0
  136. package/plugins/skills/export/SKILL.md +67 -0
  137. package/plugins/skills/extract/SKILL.md +93 -0
  138. package/plugins/skills/ingest/SKILL.md +38 -0
  139. package/plugins/skills/ontology/SKILL.md +89 -0
  140. package/plugins/skills/policy/SKILL.md +66 -0
  141. package/plugins/skills/provenance/SKILL.md +70 -0
  142. package/plugins/skills/query/SKILL.md +84 -0
  143. package/plugins/skills/reason/SKILL.md +201 -0
  144. package/plugins/skills/temporal/SKILL.md +164 -0
  145. package/plugins/skills/validate/SKILL.md +228 -0
  146. package/plugins/skills/visualize/SKILL.md +249 -0
@@ -0,0 +1,335 @@
1
+ import type Graph from "graphology";
2
+ import type Sigma from "sigma";
3
+
4
+ import type { EdgeAttributes, NodeAttributes } from "../../store/graphStore";
5
+ import { GRAPH_THEME, withAlpha } from "./graphTheme";
6
+ import type {
7
+ GraphFullEdgeClass,
8
+ GraphFullEdgeClassDiagnostics,
9
+ GraphInteractionState,
10
+ GraphStructureLayerDiagnostics,
11
+ GraphStructureLayerDisabledReason,
12
+ GraphViewMode,
13
+ } from "./types";
14
+
15
+ type GraphRef = Graph;
16
+
17
+ type StructureLayerMode = typeof GRAPH_THEME.edges.fullGraphStructureLayer.mode;
18
+
19
+ export type GraphStructureCurve = {
20
+ edgeId: string;
21
+ sourceId: string;
22
+ targetId: string;
23
+ source: { x: number; y: number };
24
+ target: { x: number; y: number };
25
+ edgeClass: Extract<GraphFullEdgeClass, "backbone" | "bridge">;
26
+ priority: number;
27
+ curvature: number;
28
+ };
29
+
30
+ export type GraphStructureCurveCache = {
31
+ cacheKey: string;
32
+ curves: GraphStructureCurve[];
33
+ bridgeCurveCount: number;
34
+ backboneCurveCount: number;
35
+ };
36
+
37
+ export type GraphStructureLayerGateInput = {
38
+ mode: StructureLayerMode;
39
+ viewMode: GraphViewMode;
40
+ isLayoutRunning: boolean;
41
+ edgeDiagnostics?: GraphFullEdgeClassDiagnostics;
42
+ minimumLiteralEdges: number;
43
+ };
44
+
45
+ export type GraphStructureLayerGate = {
46
+ enabled: boolean;
47
+ disabledReason: GraphStructureLayerDisabledReason | null;
48
+ };
49
+
50
+ export function evaluateGraphStructureLayerGate({
51
+ mode,
52
+ viewMode,
53
+ isLayoutRunning,
54
+ edgeDiagnostics,
55
+ minimumLiteralEdges,
56
+ }: GraphStructureLayerGateInput): GraphStructureLayerGate {
57
+ if (mode === "off") {
58
+ return { enabled: false, disabledReason: "disabled" };
59
+ }
60
+
61
+ if (viewMode !== "full") {
62
+ return { enabled: false, disabledReason: "non-full-mode" };
63
+ }
64
+
65
+ if (isLayoutRunning) {
66
+ return { enabled: false, disabledReason: "layout-running" };
67
+ }
68
+
69
+ if (mode === "auto") {
70
+ const literalEdges = (edgeDiagnostics?.counts.backbone ?? 0) + (edgeDiagnostics?.counts.bridge ?? 0);
71
+ if (literalEdges >= minimumLiteralEdges) {
72
+ return { enabled: false, disabledReason: "enough-literal-edges" };
73
+ }
74
+ }
75
+
76
+ return { enabled: true, disabledReason: null };
77
+ }
78
+
79
+ function isFinitePoint(attrs: NodeAttributes) {
80
+ return Number.isFinite(Number(attrs.x)) && Number.isFinite(Number(attrs.y));
81
+ }
82
+
83
+ function getEdgePriority(attrs: EdgeAttributes) {
84
+ return Math.max(0, Math.min(1, Number(attrs.visualPriority ?? attrs.weight ?? 0)));
85
+ }
86
+
87
+ function getCurveSortRank(edgeClass: GraphFullEdgeClass, priority: number) {
88
+ return (edgeClass === "bridge" ? 2 : 1) + priority;
89
+ }
90
+
91
+ function getDeterministicCurveSign(sourceId: string, targetId: string, edgeId: string) {
92
+ const seed = `${sourceId}|${targetId}|${edgeId}`;
93
+ let hash = 0;
94
+ for (let index = 0; index < seed.length; index += 1) {
95
+ hash = (hash * 31 + seed.charCodeAt(index)) | 0;
96
+ }
97
+ return hash % 2 === 0 ? 1 : -1;
98
+ }
99
+
100
+ export function createGraphStructureCacheKey({
101
+ graphVersion,
102
+ zoomTier,
103
+ layoutSettledEpoch,
104
+ overviewBackboneEdgeIds,
105
+ }: {
106
+ graphVersion: number;
107
+ zoomTier: GraphInteractionState["zoomTier"];
108
+ layoutSettledEpoch: number;
109
+ overviewBackboneEdgeIds: Set<string>;
110
+ }) {
111
+ return [
112
+ graphVersion,
113
+ zoomTier,
114
+ layoutSettledEpoch,
115
+ Array.from(overviewBackboneEdgeIds).sort().join(","),
116
+ ].join("|");
117
+ }
118
+
119
+ export function buildGraphStructureCurveCache({
120
+ graphRef,
121
+ cacheKey,
122
+ classifyEdge,
123
+ maxCurves,
124
+ curveStrength,
125
+ }: {
126
+ graphRef: GraphRef;
127
+ cacheKey: string;
128
+ classifyEdge: (edgeId: string) => GraphFullEdgeClass;
129
+ maxCurves: number;
130
+ curveStrength: number;
131
+ }): GraphStructureCurveCache {
132
+ const candidates: Array<GraphStructureCurve & { rank: number }> = [];
133
+
134
+ graphRef.forEachEdge((edgeId, attrs, source, target) => {
135
+ const stableEdgeId = String(edgeId);
136
+ const edgeClass = classifyEdge(stableEdgeId);
137
+ if (edgeClass !== "bridge" && edgeClass !== "backbone") {
138
+ return;
139
+ }
140
+
141
+ const sourceId = String(source);
142
+ const targetId = String(target);
143
+ if (!graphRef.hasNode(sourceId) || !graphRef.hasNode(targetId)) {
144
+ return;
145
+ }
146
+
147
+ const sourceAttrs = graphRef.getNodeAttributes(sourceId) as NodeAttributes;
148
+ const targetAttrs = graphRef.getNodeAttributes(targetId) as NodeAttributes;
149
+ if (!isFinitePoint(sourceAttrs) || !isFinitePoint(targetAttrs)) {
150
+ return;
151
+ }
152
+
153
+ const priority = getEdgePriority(attrs as EdgeAttributes);
154
+ candidates.push({
155
+ edgeId: stableEdgeId,
156
+ sourceId,
157
+ targetId,
158
+ source: { x: Number(sourceAttrs.x), y: Number(sourceAttrs.y) },
159
+ target: { x: Number(targetAttrs.x), y: Number(targetAttrs.y) },
160
+ edgeClass,
161
+ priority,
162
+ curvature: getDeterministicCurveSign(sourceId, targetId, stableEdgeId) * curveStrength,
163
+ rank: getCurveSortRank(edgeClass, priority),
164
+ });
165
+ });
166
+
167
+ candidates.sort((left, right) => {
168
+ if (right.rank !== left.rank) {
169
+ return right.rank - left.rank;
170
+ }
171
+ return left.edgeId.localeCompare(right.edgeId);
172
+ });
173
+
174
+ const curves = candidates.slice(0, maxCurves).map(({ rank: _rank, ...curve }) => curve);
175
+ return {
176
+ cacheKey,
177
+ curves,
178
+ bridgeCurveCount: curves.filter((curve) => curve.edgeClass === "bridge").length,
179
+ backboneCurveCount: curves.filter((curve) => curve.edgeClass === "backbone").length,
180
+ };
181
+ }
182
+
183
+ export function getGraphStructureLayerDiagnostics({
184
+ gate,
185
+ cache,
186
+ minimumCurves,
187
+ canvasAvailable,
188
+ lastDrawAt,
189
+ }: {
190
+ gate: GraphStructureLayerGate;
191
+ cache: GraphStructureCurveCache | null;
192
+ minimumCurves: number;
193
+ canvasAvailable: boolean;
194
+ lastDrawAt: number | null;
195
+ }): GraphStructureLayerDiagnostics {
196
+ if (!gate.enabled) {
197
+ return {
198
+ enabled: false,
199
+ disabledReason: gate.disabledReason,
200
+ curveCount: 0,
201
+ bridgeCurveCount: 0,
202
+ backboneCurveCount: 0,
203
+ cacheKey: cache?.cacheKey ?? "",
204
+ lastDrawAt,
205
+ };
206
+ }
207
+
208
+ if (!canvasAvailable) {
209
+ return {
210
+ enabled: false,
211
+ disabledReason: "invalid-layer",
212
+ curveCount: 0,
213
+ bridgeCurveCount: 0,
214
+ backboneCurveCount: 0,
215
+ cacheKey: cache?.cacheKey ?? "",
216
+ lastDrawAt,
217
+ };
218
+ }
219
+
220
+ if (!cache || cache.curves.length === 0) {
221
+ return {
222
+ enabled: false,
223
+ disabledReason: "no-eligible-edges",
224
+ curveCount: 0,
225
+ bridgeCurveCount: 0,
226
+ backboneCurveCount: 0,
227
+ cacheKey: cache?.cacheKey ?? "",
228
+ lastDrawAt,
229
+ };
230
+ }
231
+
232
+ if (cache.curves.length < minimumCurves) {
233
+ return {
234
+ enabled: false,
235
+ disabledReason: "cache-empty",
236
+ curveCount: cache.curves.length,
237
+ bridgeCurveCount: cache.bridgeCurveCount,
238
+ backboneCurveCount: cache.backboneCurveCount,
239
+ cacheKey: cache.cacheKey,
240
+ lastDrawAt,
241
+ };
242
+ }
243
+
244
+ return {
245
+ enabled: true,
246
+ disabledReason: null,
247
+ curveCount: cache.curves.length,
248
+ bridgeCurveCount: cache.bridgeCurveCount,
249
+ backboneCurveCount: cache.backboneCurveCount,
250
+ cacheKey: cache.cacheKey,
251
+ lastDrawAt,
252
+ };
253
+ }
254
+
255
+ export function clearGraphStructureLayer(canvas: HTMLCanvasElement | null) {
256
+ if (!canvas) {
257
+ return;
258
+ }
259
+ const context = canvas.getContext("2d");
260
+ if (!context) {
261
+ return;
262
+ }
263
+ context.setTransform(1, 0, 0, 1, 0, 0);
264
+ context.clearRect(0, 0, canvas.width, canvas.height);
265
+ }
266
+
267
+ export function drawGraphStructureLayer({
268
+ sigma,
269
+ canvas,
270
+ cache,
271
+ }: {
272
+ sigma: Sigma;
273
+ canvas: HTMLCanvasElement;
274
+ cache: GraphStructureCurveCache;
275
+ }) {
276
+ const context = canvas.getContext("2d");
277
+ if (!context) {
278
+ return false;
279
+ }
280
+
281
+ const { width, height } = sigma.getDimensions();
282
+ const pixelRatio = window.devicePixelRatio || 1;
283
+ context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
284
+ context.clearRect(0, 0, width, height);
285
+ context.lineCap = "round";
286
+ context.lineJoin = "round";
287
+
288
+ let drawn = 0;
289
+ for (const curve of cache.curves) {
290
+ const sourceData = sigma.getNodeDisplayData(curve.sourceId);
291
+ const targetData = sigma.getNodeDisplayData(curve.targetId);
292
+ if (!sourceData || !targetData || sourceData.hidden || targetData.hidden) {
293
+ continue;
294
+ }
295
+
296
+ const sourcePoint = sigma.graphToViewport(curve.source);
297
+ const targetPoint = sigma.graphToViewport(curve.target);
298
+ if (
299
+ !Number.isFinite(sourcePoint.x)
300
+ || !Number.isFinite(sourcePoint.y)
301
+ || !Number.isFinite(targetPoint.x)
302
+ || !Number.isFinite(targetPoint.y)
303
+ ) {
304
+ continue;
305
+ }
306
+
307
+ const dx = targetPoint.x - sourcePoint.x;
308
+ const dy = targetPoint.y - sourcePoint.y;
309
+ const distance = Math.hypot(dx, dy);
310
+ if (distance <= 0) {
311
+ continue;
312
+ }
313
+
314
+ const nx = -dy / distance;
315
+ const ny = dx / distance;
316
+ const offset = distance * curve.curvature;
317
+ const controlX = (sourcePoint.x + targetPoint.x) / 2 + nx * offset;
318
+ const controlY = (sourcePoint.y + targetPoint.y) / 2 + ny * offset;
319
+ const layerTheme = GRAPH_THEME.edges.fullGraphStructureLayer;
320
+
321
+ context.beginPath();
322
+ context.strokeStyle = curve.edgeClass === "bridge"
323
+ ? withAlpha(GRAPH_THEME.palette.muted.edgeFocus, layerTheme.bridgeAlpha)
324
+ : withAlpha(GRAPH_THEME.palette.muted.edgeStructure, layerTheme.backboneAlpha);
325
+ context.lineWidth = curve.edgeClass === "bridge"
326
+ ? layerTheme.bridgeLineWidth
327
+ : layerTheme.backboneLineWidth;
328
+ context.moveTo(sourcePoint.x, sourcePoint.y);
329
+ context.quadraticCurveTo(controlX, controlY, targetPoint.x, targetPoint.y);
330
+ context.stroke();
331
+ drawn += 1;
332
+ }
333
+
334
+ return drawn > 0;
335
+ }