@tldiagram/core-ui 1.87.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/App.d.ts +1 -0
- package/dist/api/client.d.ts +143 -0
- package/dist/api/transport-vscode.d.ts +8 -0
- package/dist/api/transport.d.ts +1 -0
- package/dist/components/CodePreviewPanel-vscode.d.ts +7 -0
- package/dist/components/CodePreviewPanel.d.ts +9 -0
- package/dist/components/ConfirmDialog.d.ts +12 -0
- package/dist/components/ConnectorPanel.d.ts +21 -0
- package/dist/components/ContextBoundaryElement.d.ts +11 -0
- package/dist/components/ContextNeighborElement.d.ts +29 -0
- package/dist/components/ContextStraightConnector.d.ts +4 -0
- package/dist/components/CrossBranchControls.d.ts +9 -0
- package/dist/components/DependenciesOnboarding.d.ts +5 -0
- package/dist/components/DrawingCanvas.d.ts +39 -0
- package/dist/components/ElementLibrary-vscode.d.ts +7 -0
- package/dist/components/ElementLibrary.d.ts +22 -0
- package/dist/components/ElementNode.d.ts +36 -0
- package/dist/components/ElementPanel.d.ts +25 -0
- package/dist/components/ExploreOnboarding.d.ts +5 -0
- package/dist/components/ExplorePageOnboarding.d.ts +5 -0
- package/dist/components/ExportModal.d.ts +16 -0
- package/dist/components/FloatingEdge.d.ts +9 -0
- package/dist/components/GitSourceLinker.d.ts +8 -0
- package/dist/components/HeaderContext.d.ts +16 -0
- package/dist/components/Icons.d.ts +95 -0
- package/dist/components/ImportModal.d.ts +10 -0
- package/dist/components/InlineElementAdder.d.ts +17 -0
- package/dist/components/LayoutSection.d.ts +7 -0
- package/dist/components/LocalSourceLinker.d.ts +8 -0
- package/dist/components/MiniZoomOnboarding.d.ts +5 -0
- package/dist/components/NavBreadcrumb.d.ts +6 -0
- package/dist/components/NodeBody.d.ts +12 -0
- package/dist/components/NodeContainer.d.ts +8 -0
- package/dist/components/NodeHoverCard.d.ts +10 -0
- package/dist/components/PanelHeader.d.ts +8 -0
- package/dist/components/PanelUI.d.ts +3 -0
- package/dist/components/ProxyConnectorEdge.d.ts +4 -0
- package/dist/components/ProxyConnectorPanel.d.ts +9 -0
- package/dist/components/SafeBackground.d.ts +13 -0
- package/dist/components/ScrollIndicatorWrapper.d.ts +8 -0
- package/dist/components/SetChildModal.d.ts +10 -0
- package/dist/components/SetParentModal.d.ts +10 -0
- package/dist/components/SlidingPanel.d.ts +16 -0
- package/dist/components/TagUpsert.d.ts +8 -0
- package/dist/components/TopMenuBar.d.ts +8 -0
- package/dist/components/ViewBezierConnector.d.ts +4 -0
- package/dist/components/ViewDrawMenu.d.ts +22 -0
- package/dist/components/ViewEditorEdgeLabelLayout.d.ts +16 -0
- package/dist/components/ViewEditorOnboarding.d.ts +5 -0
- package/dist/components/ViewExplorer/TagManager/ColorPicker.d.ts +7 -0
- package/dist/components/ViewExplorer/TagManager/GroupNamingPopover.d.ts +10 -0
- package/dist/components/ViewExplorer/TagManager/LayerItem.d.ts +27 -0
- package/dist/components/ViewExplorer/TagManager/TagItem.d.ts +25 -0
- package/dist/components/ViewExplorer/TagManager/index.d.ts +21 -0
- package/dist/components/ViewExplorer/ViewNavigator.d.ts +11 -0
- package/dist/components/ViewExplorer/ViewSearch.d.ts +8 -0
- package/dist/components/ViewExplorer/ViewTree.d.ts +18 -0
- package/dist/components/ViewExplorer/index.d.ts +31 -0
- package/dist/components/ViewExplorer/types.d.ts +11 -0
- package/dist/components/ViewExplorer/utils.d.ts +6 -0
- package/dist/components/ViewExplorer-vscode.d.ts +6 -0
- package/dist/components/ViewFloatingMenu-vscode.d.ts +27 -0
- package/dist/components/ViewFloatingMenu.d.ts +39 -0
- package/dist/components/ViewGridNode.d.ts +29 -0
- package/dist/components/ViewHeaderButton.d.ts +11 -0
- package/dist/components/ViewPanel.d.ts +18 -0
- package/dist/components/ViewsGridOnboarding.d.ts +5 -0
- package/dist/components/ZUI/ZUICanvas.d.ts +18 -0
- package/dist/components/ZUI/index.d.ts +2 -0
- package/dist/components/ZUI/layout.d.ts +18 -0
- package/dist/components/ZUI/proxy.d.ts +25 -0
- package/dist/components/ZUI/renderer.d.ts +30 -0
- package/dist/components/ZUI/types.d.ts +140 -0
- package/dist/components/ZUI/useZUIInteraction.d.ts +21 -0
- package/dist/config/runtime-vscode.d.ts +22 -0
- package/dist/config/runtime.d.ts +5 -0
- package/dist/constants/colors.d.ts +27 -0
- package/dist/constants/diagramColors.d.ts +1 -0
- package/dist/context/ThemeContext.d.ts +27 -0
- package/dist/crossBranch/graph.d.ts +13 -0
- package/dist/crossBranch/resolve.d.ts +22 -0
- package/dist/crossBranch/settings.d.ts +6 -0
- package/dist/crossBranch/store.d.ts +11 -0
- package/dist/crossBranch/types.d.ts +96 -0
- package/dist/demo/DemoPage.d.ts +9 -0
- package/dist/demo/seed.d.ts +9 -0
- package/dist/demo/store.d.ts +137 -0
- package/dist/demo/viewEditor.d.ts +26 -0
- package/dist/favicon.svg +35 -0
- package/dist/hooks/useSafeFitView.d.ts +16 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +115 -0
- package/dist/index.js +19966 -0
- package/dist/lib/vscodeBridge-vscode.d.ts +13 -0
- package/dist/lib/vscodeBridge.d.ts +5 -0
- package/dist/logo-120.png +0 -0
- package/dist/logo-bw.png +0 -0
- package/dist/logo-bw.svg +15 -0
- package/dist/logo-text.svg +51 -0
- package/dist/logo.svg +35 -0
- package/dist/pages/AppearanceSettings.d.ts +3 -0
- package/dist/pages/Dependencies.d.ts +1 -0
- package/dist/pages/InfiniteZoom.d.ts +7 -0
- package/dist/pages/Settings.d.ts +7 -0
- package/dist/pages/ViewEditor/components/EditorMenus.d.ts +24 -0
- package/dist/pages/ViewEditor/components/EditorOverlays.d.ts +30 -0
- package/dist/pages/ViewEditor/components/EmptyCanvasState.d.ts +7 -0
- package/dist/pages/ViewEditor/context.d.ts +13 -0
- package/dist/pages/ViewEditor/hooks/useCanvasInteractions.d.ts +201 -0
- package/dist/pages/ViewEditor/hooks/useDrawingEngine.d.ts +40 -0
- package/dist/pages/ViewEditor/hooks/useViewContextNeighbours.d.ts +20 -0
- package/dist/pages/ViewEditor/hooks/useViewData.d.ts +74 -0
- package/dist/pages/ViewEditor/index.d.ts +8 -0
- package/dist/pages/ViewEditor/utils.d.ts +14 -0
- package/dist/pages/Views.d.ts +6 -0
- package/dist/pages/ViewsGrid.d.ts +6 -0
- package/dist/pkg/importer/mermaid.d.ts +7 -0
- package/dist/pkg/importer/mermaid.test.d.ts +1 -0
- package/dist/platform/PlatformContext.d.ts +6 -0
- package/dist/platform/context.d.ts +3 -0
- package/dist/platform/local.d.ts +2 -0
- package/dist/platform/types.d.ts +17 -0
- package/dist/slots.d.ts +67 -0
- package/dist/theme.d.ts +2 -0
- package/dist/types/index.d.ts +193 -0
- package/dist/types/vscode-messages.d.ts +60 -0
- package/dist/utils/edgeDistribution.d.ts +34 -0
- package/dist/utils/githubApi.d.ts +4 -0
- package/dist/utils/githubCache.d.ts +17 -0
- package/dist/utils/ids.d.ts +2 -0
- package/dist/utils/technologyCatalog.d.ts +15 -0
- package/dist/utils/toast.d.ts +15 -0
- package/dist/utils/treesitter.d.ts +13 -0
- package/dist/utils/url.d.ts +12 -0
- package/package.json +159 -0
- package/src/App.tsx +141 -0
- package/src/api/client.ts +618 -0
- package/src/api/transport-vscode.ts +28 -0
- package/src/api/transport.ts +7 -0
- package/src/assets/logo-mark.svg +31 -0
- package/src/assets/logo-wordmark.svg +22 -0
- package/src/assets/logo.svg +35 -0
- package/src/components/CodePreviewPanel-vscode.tsx +85 -0
- package/src/components/CodePreviewPanel.tsx +384 -0
- package/src/components/ConfirmDialog.tsx +66 -0
- package/src/components/ConnectorPanel.tsx +403 -0
- package/src/components/ContextBoundaryElement.tsx +35 -0
- package/src/components/ContextNeighborElement.tsx +282 -0
- package/src/components/ContextStraightConnector.tsx +144 -0
- package/src/components/CrossBranchControls.tsx +105 -0
- package/src/components/DependenciesOnboarding.tsx +427 -0
- package/src/components/DrawingCanvas.tsx +391 -0
- package/src/components/ElementLibrary-vscode.tsx +9 -0
- package/src/components/ElementLibrary.tsx +512 -0
- package/src/components/ElementNode.tsx +1033 -0
- package/src/components/ElementPanel.tsx +928 -0
- package/src/components/ExploreOnboarding.tsx +347 -0
- package/src/components/ExplorePageOnboarding.tsx +383 -0
- package/src/components/ExportModal.tsx +132 -0
- package/src/components/FloatingEdge.tsx +115 -0
- package/src/components/GitSourceLinker.tsx +1053 -0
- package/src/components/HeaderContext.tsx +30 -0
- package/src/components/Icons.tsx +245 -0
- package/src/components/ImportModal.tsx +219 -0
- package/src/components/InlineElementAdder.tsx +216 -0
- package/src/components/LayoutSection.tsx +624 -0
- package/src/components/LocalSourceLinker.tsx +330 -0
- package/src/components/MiniZoomOnboarding.tsx +78 -0
- package/src/components/NavBreadcrumb.tsx +24 -0
- package/src/components/NodeBody.tsx +89 -0
- package/src/components/NodeContainer.tsx +58 -0
- package/src/components/NodeHoverCard.tsx +135 -0
- package/src/components/PanelHeader.tsx +36 -0
- package/src/components/PanelUI.tsx +24 -0
- package/src/components/ProxyConnectorEdge.tsx +169 -0
- package/src/components/ProxyConnectorPanel.tsx +130 -0
- package/src/components/SafeBackground.tsx +19 -0
- package/src/components/ScrollIndicatorWrapper.tsx +117 -0
- package/src/components/SetChildModal.tsx +191 -0
- package/src/components/SetParentModal.tsx +187 -0
- package/src/components/SlidingPanel.tsx +114 -0
- package/src/components/TagUpsert.tsx +142 -0
- package/src/components/TopMenuBar.tsx +380 -0
- package/src/components/ViewBezierConnector.tsx +143 -0
- package/src/components/ViewDrawMenu.tsx +270 -0
- package/src/components/ViewEditorEdgeLabelLayout.ts +189 -0
- package/src/components/ViewEditorOnboarding.tsx +445 -0
- package/src/components/ViewExplorer/TagManager/ColorPicker.tsx +49 -0
- package/src/components/ViewExplorer/TagManager/GroupNamingPopover.tsx +96 -0
- package/src/components/ViewExplorer/TagManager/LayerItem.tsx +228 -0
- package/src/components/ViewExplorer/TagManager/TagItem.tsx +242 -0
- package/src/components/ViewExplorer/TagManager/index.tsx +418 -0
- package/src/components/ViewExplorer/ViewNavigator.tsx +121 -0
- package/src/components/ViewExplorer/ViewSearch.tsx +33 -0
- package/src/components/ViewExplorer/ViewTree.tsx +98 -0
- package/src/components/ViewExplorer/index.tsx +384 -0
- package/src/components/ViewExplorer/types.ts +13 -0
- package/src/components/ViewExplorer/utils.ts +56 -0
- package/src/components/ViewExplorer-vscode.tsx +8 -0
- package/src/components/ViewFloatingMenu-vscode.tsx +248 -0
- package/src/components/ViewFloatingMenu.tsx +379 -0
- package/src/components/ViewGridNode.tsx +451 -0
- package/src/components/ViewHeaderButton.tsx +60 -0
- package/src/components/ViewPanel.tsx +162 -0
- package/src/components/ViewsGridOnboarding.tsx +400 -0
- package/src/components/ZUI/ZUICanvas.tsx +853 -0
- package/src/components/ZUI/index.ts +3 -0
- package/src/components/ZUI/layout.ts +323 -0
- package/src/components/ZUI/proxy.ts +278 -0
- package/src/components/ZUI/renderer.ts +1189 -0
- package/src/components/ZUI/types.ts +150 -0
- package/src/components/ZUI/useZUIInteraction.ts +720 -0
- package/src/config/runtime-vscode.ts +46 -0
- package/src/config/runtime.ts +30 -0
- package/src/constants/colors.ts +80 -0
- package/src/constants/diagramColors.ts +9 -0
- package/src/context/ThemeContext.tsx +158 -0
- package/src/crossBranch/graph.ts +207 -0
- package/src/crossBranch/resolve.ts +643 -0
- package/src/crossBranch/settings.ts +59 -0
- package/src/crossBranch/store.ts +71 -0
- package/src/crossBranch/types.ts +102 -0
- package/src/demo/DemoPage.tsx +184 -0
- package/src/demo/seed.ts +67 -0
- package/src/demo/store.ts +536 -0
- package/src/demo/viewEditor.ts +110 -0
- package/src/hooks/useSafeFitView.ts +60 -0
- package/src/index.css +309 -0
- package/src/index.ts +184 -0
- package/src/kafka-ss.png +0 -0
- package/src/lib/vscodeBridge-vscode.ts +27 -0
- package/src/lib/vscodeBridge.ts +7 -0
- package/src/main.tsx +46 -0
- package/src/pages/AppearanceSettings.tsx +135 -0
- package/src/pages/Dependencies.tsx +926 -0
- package/src/pages/InfiniteZoom.tsx +404 -0
- package/src/pages/Settings.tsx +91 -0
- package/src/pages/ViewEditor/EDGE_DISTRIBUTION.md +64 -0
- package/src/pages/ViewEditor/components/EditorMenus.tsx +112 -0
- package/src/pages/ViewEditor/components/EditorOverlays.tsx +172 -0
- package/src/pages/ViewEditor/components/EmptyCanvasState.tsx +42 -0
- package/src/pages/ViewEditor/context.tsx +21 -0
- package/src/pages/ViewEditor/hooks/useCanvasInteractions.ts +1349 -0
- package/src/pages/ViewEditor/hooks/useDrawingEngine.ts +127 -0
- package/src/pages/ViewEditor/hooks/useViewContextNeighbours.ts +501 -0
- package/src/pages/ViewEditor/hooks/useViewData.ts +491 -0
- package/src/pages/ViewEditor/index.tsx +1366 -0
- package/src/pages/ViewEditor/utils.ts +88 -0
- package/src/pages/Views.tsx +171 -0
- package/src/pages/ViewsGrid.tsx +1310 -0
- package/src/pkg/importer/mermaid.test.ts +141 -0
- package/src/pkg/importer/mermaid.ts +76 -0
- package/src/platform/PlatformContext.tsx +17 -0
- package/src/platform/context.ts +9 -0
- package/src/platform/local.tsx +15 -0
- package/src/platform/types.ts +19 -0
- package/src/slots.ts +92 -0
- package/src/styles/editor-panels.css +66 -0
- package/src/styles/theme.css +56 -0
- package/src/theme.ts +336 -0
- package/src/types/index.ts +234 -0
- package/src/types/offline-ambient.d.ts +14 -0
- package/src/types/vscode-messages.ts +32 -0
- package/src/utils/edgeDistribution.ts +103 -0
- package/src/utils/githubApi.ts +121 -0
- package/src/utils/githubCache.ts +108 -0
- package/src/utils/ids.ts +9 -0
- package/src/utils/technologyCatalog.ts +143 -0
- package/src/utils/toast.ts +100 -0
- package/src/utils/treesitter.ts +147 -0
- package/src/utils/url.ts +72 -0
- package/src/vite-env.d.ts +1 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
// src/components/ZUI/layout.ts
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
DiagramGroupLayout,
|
|
5
|
+
LayoutNode,
|
|
6
|
+
ZUILayout,
|
|
7
|
+
} from './types'
|
|
8
|
+
import type {
|
|
9
|
+
PlacedElement,
|
|
10
|
+
ExploreData,
|
|
11
|
+
ViewConnector,
|
|
12
|
+
} from '../../types'
|
|
13
|
+
import { resolveIconPath } from '../../utils/url'
|
|
14
|
+
|
|
15
|
+
// ── Constants ──────────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
export const NODE_W = 200
|
|
18
|
+
export const NODE_H = 100
|
|
19
|
+
const GROUP_PAD = 80 // padding inside a diagram group box
|
|
20
|
+
const GROUP_SPACING = 400 // horizontal gap between root diagrams
|
|
21
|
+
const CHILD_PAD = 1 // padding inside a node when rendering children
|
|
22
|
+
|
|
23
|
+
// ── Helpers ────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
function nodeId(diagramId: number, elementId: number): string {
|
|
26
|
+
return `d${diagramId}-o${elementId}`
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function getPos(obj: PlacedElement, axis: 'x' | 'y'): number {
|
|
30
|
+
const val = axis === 'x' ? obj.position_x : obj.position_y
|
|
31
|
+
return typeof val === 'number' && isFinite(val) ? val : 0
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function calcBBox(elements: PlacedElement[]): {
|
|
35
|
+
minX: number; minY: number; width: number; height: number
|
|
36
|
+
} {
|
|
37
|
+
if (elements.length === 0) {
|
|
38
|
+
return { minX: 0, minY: 0, width: NODE_W, height: NODE_H }
|
|
39
|
+
}
|
|
40
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity
|
|
41
|
+
for (const o of elements) {
|
|
42
|
+
const px = getPos(o, 'x')
|
|
43
|
+
const py = getPos(o, 'y')
|
|
44
|
+
minX = Math.min(minX, px)
|
|
45
|
+
minY = Math.min(minY, py)
|
|
46
|
+
maxX = Math.max(maxX, px + NODE_W)
|
|
47
|
+
maxY = Math.max(maxY, py + NODE_H)
|
|
48
|
+
}
|
|
49
|
+
if (!isFinite(minX)) return { minX: 0, minY: 0, width: NODE_W, height: NODE_H }
|
|
50
|
+
return { minX, minY, width: maxX - minX, height: maxY - minY }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Build a lookup: elementId → ElementDiagramLink for child connectors only. */
|
|
54
|
+
function buildChildLinkMap(
|
|
55
|
+
links: ViewConnector[],
|
|
56
|
+
fromDiagramId: number,
|
|
57
|
+
): Map<number, ViewConnector> {
|
|
58
|
+
const m = new Map<number, ViewConnector>()
|
|
59
|
+
for (const l of links) {
|
|
60
|
+
if (l.from_view_id === fromDiagramId && l.relation_type === 'child' && l.element_id != null) {
|
|
61
|
+
m.set(l.element_id, l)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return m
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Recursively build LayoutNode[] for all elements in a diagram.
|
|
69
|
+
* `worldOffsetX/Y` are the world-space origin of the diagram that
|
|
70
|
+
* contains these elements.
|
|
71
|
+
* `pad` is added to each element's local position (GROUP_PAD for root
|
|
72
|
+
* diagrams, 0 for children stored in raw editor coords).
|
|
73
|
+
*
|
|
74
|
+
* `visited` is a Set of diagram IDs to prevent infinite recursion for cyclic links.
|
|
75
|
+
*/
|
|
76
|
+
function buildNodes(
|
|
77
|
+
elements: PlacedElement[],
|
|
78
|
+
views: ExploreData['views'],
|
|
79
|
+
links: ViewConnector[],
|
|
80
|
+
diagramId: number,
|
|
81
|
+
worldOffsetX: number,
|
|
82
|
+
worldOffsetY: number,
|
|
83
|
+
bboxMinX: number,
|
|
84
|
+
bboxMinY: number,
|
|
85
|
+
visited: Set<number>,
|
|
86
|
+
pad: number = GROUP_PAD,
|
|
87
|
+
diagramX: number = 0,
|
|
88
|
+
_ignoredPortalsXOffset: number = 0, // kept to minimize recursive signature change
|
|
89
|
+
_diagramW: number = 0,
|
|
90
|
+
_diagramH: number = 0,
|
|
91
|
+
tree: ExploreData['tree'] = [],
|
|
92
|
+
ancestorElementIds: number[] = [],
|
|
93
|
+
): LayoutNode[] {
|
|
94
|
+
const childLinkMap = buildChildLinkMap(links, diagramId)
|
|
95
|
+
const _treeMap = new Map(tree.map((d) => [d.id, d]))
|
|
96
|
+
|
|
97
|
+
const realNodes: LayoutNode[] = elements.map((obj) => {
|
|
98
|
+
const localX = getPos(obj, 'x') - bboxMinX + pad
|
|
99
|
+
const localY = getPos(obj, 'y') - bboxMinY + pad
|
|
100
|
+
|
|
101
|
+
const link = childLinkMap.get(obj.element_id)
|
|
102
|
+
|
|
103
|
+
// ── Build children if this element connects to a child diagram ──
|
|
104
|
+
let children: LayoutNode[] = []
|
|
105
|
+
let childScale = 1
|
|
106
|
+
let childOffsetX = 0
|
|
107
|
+
let childOffsetY = 0
|
|
108
|
+
let linkedDiagramId: number | undefined
|
|
109
|
+
let linkedDiagramLabel: string | undefined
|
|
110
|
+
let isCircular = false
|
|
111
|
+
|
|
112
|
+
if (link) {
|
|
113
|
+
linkedDiagramId = link.to_view_id
|
|
114
|
+
linkedDiagramLabel = link.to_view_name
|
|
115
|
+
|
|
116
|
+
// Check for cycle before recursing
|
|
117
|
+
if (visited.has(link.to_view_id)) {
|
|
118
|
+
isCircular = true
|
|
119
|
+
} else {
|
|
120
|
+
const childDiagData = views[String(link.to_view_id)]
|
|
121
|
+
|
|
122
|
+
if (childDiagData && childDiagData.placements.length > 0) {
|
|
123
|
+
const cBBox = calcBBox(childDiagData.placements)
|
|
124
|
+
|
|
125
|
+
const contentW = cBBox.width + CHILD_PAD * 2
|
|
126
|
+
const contentH = cBBox.height + CHILD_PAD * 2
|
|
127
|
+
const fittedH = Math.min(contentH, contentW)
|
|
128
|
+
childScale = Math.min(
|
|
129
|
+
(NODE_W - CHILD_PAD * 2) / contentW,
|
|
130
|
+
(Math.min(NODE_H, fittedH) - CHILD_PAD * 2) / Math.max(contentH, 1),
|
|
131
|
+
0.45,
|
|
132
|
+
)
|
|
133
|
+
const scaledW = cBBox.width * childScale
|
|
134
|
+
const scaledH = cBBox.height * childScale
|
|
135
|
+
const marginX = (NODE_W - scaledW) / 2
|
|
136
|
+
const marginY = (NODE_H - scaledH) / 2
|
|
137
|
+
childOffsetX = cBBox.minX - marginX / childScale
|
|
138
|
+
childOffsetY = cBBox.minY - marginY / childScale
|
|
139
|
+
|
|
140
|
+
const nextVisited = new Set(visited)
|
|
141
|
+
nextVisited.add(link.to_view_id)
|
|
142
|
+
|
|
143
|
+
children = buildNodes(
|
|
144
|
+
childDiagData.placements,
|
|
145
|
+
views,
|
|
146
|
+
links,
|
|
147
|
+
link.to_view_id,
|
|
148
|
+
0, 0, 0, 0,
|
|
149
|
+
nextVisited,
|
|
150
|
+
0,
|
|
151
|
+
0, 0, 0, 0,
|
|
152
|
+
tree,
|
|
153
|
+
[...ancestorElementIds, obj.element_id],
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const edgesOut = (views[String(diagramId)]?.connectors ?? [])
|
|
160
|
+
.filter((e) => e.source_element_id === obj.element_id)
|
|
161
|
+
.map((e) => ({
|
|
162
|
+
targetId: nodeId(diagramId, e.target_element_id),
|
|
163
|
+
label: e.label ?? '',
|
|
164
|
+
direction: e.direction ?? 'forward',
|
|
165
|
+
sourceHandle: e.source_handle ?? null,
|
|
166
|
+
targetHandle: e.target_handle ?? null,
|
|
167
|
+
type: e.style || 'bezier',
|
|
168
|
+
}))
|
|
169
|
+
|
|
170
|
+
const derivedPrimaryIconPath = (() => {
|
|
171
|
+
const selected = obj.technology_connectors?.find((link) => link.type === 'catalog' && !!link.is_primary_icon && !!link.slug)
|
|
172
|
+
if (!selected?.slug) return null
|
|
173
|
+
return resolveIconPath(`/icons/${selected.slug}.png`)
|
|
174
|
+
})()
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
id: nodeId(diagramId, obj.element_id),
|
|
178
|
+
elementId: obj.element_id,
|
|
179
|
+
diagramId,
|
|
180
|
+
worldX: worldOffsetX + diagramX + localX,
|
|
181
|
+
worldY: worldOffsetY + localY,
|
|
182
|
+
worldW: NODE_W,
|
|
183
|
+
worldH: NODE_H,
|
|
184
|
+
label: obj.name,
|
|
185
|
+
type: obj.kind ?? 'system',
|
|
186
|
+
logoUrl: obj.logo_url ? resolveIconPath(obj.logo_url) : derivedPrimaryIconPath,
|
|
187
|
+
description: obj.description ?? null,
|
|
188
|
+
technology: obj.technology ?? null,
|
|
189
|
+
tags: obj.tags ?? [],
|
|
190
|
+
ancestorElementIds,
|
|
191
|
+
pathElementIds: [...ancestorElementIds, obj.element_id],
|
|
192
|
+
linkedDiagramId,
|
|
193
|
+
linkedDiagramLabel,
|
|
194
|
+
isCircular,
|
|
195
|
+
isPortal: false,
|
|
196
|
+
children,
|
|
197
|
+
childScale,
|
|
198
|
+
childOffsetX,
|
|
199
|
+
childOffsetY,
|
|
200
|
+
edgesOut,
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
return realNodes
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ── Public API ──────────────────────────────────────────────────────
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Compute the full world-space layout for all diagrams in `data`.
|
|
211
|
+
* Root diagrams are placed side-by-side horizontally.
|
|
212
|
+
*/
|
|
213
|
+
export function computeLayout(data: ExploreData): ZUILayout {
|
|
214
|
+
const rootDiagrams = (data.tree ?? []).filter((d) => !d.parent_view_id)
|
|
215
|
+
const groups: DiagramGroupLayout[] = []
|
|
216
|
+
let xCursor = 0
|
|
217
|
+
|
|
218
|
+
for (const diag of rootDiagrams) {
|
|
219
|
+
const diagData = data.views[String(diag.id)]
|
|
220
|
+
if (!diagData) continue
|
|
221
|
+
|
|
222
|
+
const bbox = calcBBox(diagData.placements ?? [])
|
|
223
|
+
const diagramW = bbox.width + GROUP_PAD * 2
|
|
224
|
+
const diagramH = bbox.height + GROUP_PAD * 2
|
|
225
|
+
|
|
226
|
+
const TOP_PAD = 80
|
|
227
|
+
const worldW = diagramW
|
|
228
|
+
const worldH = diagramH + TOP_PAD
|
|
229
|
+
const diagramX = 0
|
|
230
|
+
const diagramY = TOP_PAD
|
|
231
|
+
|
|
232
|
+
const visited = new Set<number>()
|
|
233
|
+
visited.add(diag.id)
|
|
234
|
+
|
|
235
|
+
const nodes = buildNodes(
|
|
236
|
+
diagData.placements ?? [],
|
|
237
|
+
data.views,
|
|
238
|
+
data.navigations ?? [],
|
|
239
|
+
diag.id,
|
|
240
|
+
xCursor,
|
|
241
|
+
diagramY, // Note: this acts as worldOffsetY in buildNodes, effectively shifting everything down
|
|
242
|
+
bbox.minX,
|
|
243
|
+
bbox.minY,
|
|
244
|
+
visited,
|
|
245
|
+
GROUP_PAD,
|
|
246
|
+
diagramX, // Replacing portalYOffset placeholder with diagram parameters for proper circle tracing
|
|
247
|
+
0, // Replacing portalsXOffset
|
|
248
|
+
diagramW, // passing these so buildNodes can compute diagram center in world
|
|
249
|
+
diagramH,
|
|
250
|
+
data.tree ?? [],
|
|
251
|
+
[],
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
// Edges within the same diagram (world-level, not children)
|
|
255
|
+
const edges = (diagData.connectors ?? []).map((e) => ({
|
|
256
|
+
sourceId: nodeId(diag.id, e.source_element_id),
|
|
257
|
+
targetId: nodeId(diag.id, e.target_element_id),
|
|
258
|
+
label: e.label ?? '',
|
|
259
|
+
direction: e.direction ?? 'forward',
|
|
260
|
+
sourceHandle: e.source_handle ?? null,
|
|
261
|
+
targetHandle: e.target_handle ?? null,
|
|
262
|
+
type: e.style || 'bezier',
|
|
263
|
+
}))
|
|
264
|
+
|
|
265
|
+
groups.push({
|
|
266
|
+
diagramId: diag.id,
|
|
267
|
+
label: diag.name,
|
|
268
|
+
description: diag.description ?? null,
|
|
269
|
+
level: diag.level,
|
|
270
|
+
levelLabel: diag.level_label,
|
|
271
|
+
worldX: xCursor,
|
|
272
|
+
worldY: 0,
|
|
273
|
+
worldW,
|
|
274
|
+
worldH,
|
|
275
|
+
diagramW,
|
|
276
|
+
diagramH,
|
|
277
|
+
diagramX,
|
|
278
|
+
diagramY,
|
|
279
|
+
nodes,
|
|
280
|
+
edges,
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
xCursor += worldW + GROUP_SPACING
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Compute overall bounding box
|
|
287
|
+
const allX = groups.flatMap((g) => [g.worldX, g.worldX + g.worldW])
|
|
288
|
+
const allY = groups.flatMap((g) => [g.worldY, g.worldY + g.worldH])
|
|
289
|
+
const bbox = {
|
|
290
|
+
minX: Math.min(...allX, 0),
|
|
291
|
+
minY: Math.min(...allY, 0),
|
|
292
|
+
maxX: Math.max(...allX, 100),
|
|
293
|
+
maxY: Math.max(...allY, 100),
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return { groups, bbox }
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Compute the initial viewport that fits the entire layout on screen.
|
|
301
|
+
* Returns a ZUIViewState (x=panX, y=panY, zoom).
|
|
302
|
+
*/
|
|
303
|
+
export function fitViewport(
|
|
304
|
+
layout: ZUILayout,
|
|
305
|
+
canvasW: number,
|
|
306
|
+
canvasH: number,
|
|
307
|
+
padding = 0.1,
|
|
308
|
+
): { x: number; y: number; zoom: number } {
|
|
309
|
+
const { bbox } = layout
|
|
310
|
+
const bboxW = bbox.maxX - bbox.minX
|
|
311
|
+
const bboxH = bbox.maxY - bbox.minY
|
|
312
|
+
if (bboxW <= 0 || bboxH <= 0) return { x: 0, y: 0, zoom: 1 }
|
|
313
|
+
|
|
314
|
+
const pad = padding
|
|
315
|
+
const zoom = Math.min(
|
|
316
|
+
(canvasW * (1 - pad * 2)) / bboxW,
|
|
317
|
+
(canvasH * (1 - pad * 2)) / bboxH,
|
|
318
|
+
4,
|
|
319
|
+
)
|
|
320
|
+
const x = (canvasW - bboxW * zoom) / 2 - bbox.minX * zoom
|
|
321
|
+
const y = (canvasH - bboxH * zoom) / 2 - bbox.minY * zoom
|
|
322
|
+
return { x, y, zoom }
|
|
323
|
+
}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { resolveZUIProxyConnectors, type ZUIResolvedConnector } from '../../crossBranch/resolve'
|
|
2
|
+
import type { WorkspaceGraphSnapshot } from '../../crossBranch/types'
|
|
3
|
+
import type { LayoutNode, ZUIViewState, HoveredItem } from './types'
|
|
4
|
+
import { getExpandThresholds, pickEdgeLabelPosition, type ScreenRect } from './renderer'
|
|
5
|
+
import type { CrossBranchContextSettings } from '../../crossBranch/types'
|
|
6
|
+
|
|
7
|
+
export interface VisibleNodeAnchor {
|
|
8
|
+
nodeId: string
|
|
9
|
+
elementId: number
|
|
10
|
+
label: string
|
|
11
|
+
worldX: number
|
|
12
|
+
worldY: number
|
|
13
|
+
worldW: number
|
|
14
|
+
worldH: number
|
|
15
|
+
pathDepth: number
|
|
16
|
+
renderAlpha: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function clamp(value: number, min: number, max: number) {
|
|
20
|
+
return value < min ? min : value > max ? max : value
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function transitionT(screenW: number, start: number, end: number): number {
|
|
24
|
+
return clamp((screenW - start) / (end - start), 0, 1)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function collectVisibleAnchorsInNodes(
|
|
28
|
+
nodes: LayoutNode[],
|
|
29
|
+
view: ZUIViewState,
|
|
30
|
+
thresholds: { start: number; end: number },
|
|
31
|
+
hiddenTags: Set<string>,
|
|
32
|
+
visibleAnchors: Map<number, VisibleNodeAnchor>,
|
|
33
|
+
byNodeId: Map<string, VisibleNodeAnchor>,
|
|
34
|
+
inheritedAlpha: number,
|
|
35
|
+
parentAbsX: number,
|
|
36
|
+
parentAbsY: number,
|
|
37
|
+
parentAbsScale: number,
|
|
38
|
+
parentChildOffsetX: number,
|
|
39
|
+
parentChildOffsetY: number,
|
|
40
|
+
) {
|
|
41
|
+
for (const node of nodes) {
|
|
42
|
+
if (hiddenTags.size > 0 && node.tags.some((tag) => hiddenTags.has(tag))) continue
|
|
43
|
+
|
|
44
|
+
const absX = parentAbsX + (node.worldX - parentChildOffsetX) * parentAbsScale
|
|
45
|
+
const absY = parentAbsY + (node.worldY - parentChildOffsetY) * parentAbsScale
|
|
46
|
+
const absScale = parentAbsScale
|
|
47
|
+
const absW = node.worldW * absScale
|
|
48
|
+
const absH = node.worldH * absScale
|
|
49
|
+
const screenW = absW * view.zoom
|
|
50
|
+
if (screenW < 2) continue
|
|
51
|
+
|
|
52
|
+
const hasChildren = node.children && node.children.length > 0
|
|
53
|
+
const t = hasChildren ? transitionT(screenW, thresholds.start, thresholds.end) : 0
|
|
54
|
+
const parentAlpha = inheritedAlpha * (1 - t)
|
|
55
|
+
const childAlpha = inheritedAlpha * t
|
|
56
|
+
|
|
57
|
+
if (!hasChildren || t <= 0.95) {
|
|
58
|
+
const anchor: VisibleNodeAnchor = {
|
|
59
|
+
nodeId: node.id,
|
|
60
|
+
elementId: node.elementId,
|
|
61
|
+
label: node.label,
|
|
62
|
+
worldX: absX,
|
|
63
|
+
worldY: absY,
|
|
64
|
+
worldW: absW,
|
|
65
|
+
worldH: absH,
|
|
66
|
+
pathDepth: node.pathElementIds.length,
|
|
67
|
+
renderAlpha: hasChildren ? parentAlpha : inheritedAlpha,
|
|
68
|
+
}
|
|
69
|
+
const existing = visibleAnchors.get(node.elementId)
|
|
70
|
+
if (!existing || existing.pathDepth < anchor.pathDepth) visibleAnchors.set(node.elementId, anchor)
|
|
71
|
+
byNodeId.set(node.id, anchor)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (hasChildren && t > 0.05) {
|
|
75
|
+
collectVisibleAnchorsInNodes(
|
|
76
|
+
node.children,
|
|
77
|
+
view,
|
|
78
|
+
thresholds,
|
|
79
|
+
hiddenTags,
|
|
80
|
+
visibleAnchors,
|
|
81
|
+
byNodeId,
|
|
82
|
+
childAlpha,
|
|
83
|
+
absX,
|
|
84
|
+
absY,
|
|
85
|
+
absScale * node.childScale,
|
|
86
|
+
node.childOffsetX,
|
|
87
|
+
node.childOffsetY,
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function collectVisibleNodeAnchors(
|
|
94
|
+
groups: Array<{ nodes: LayoutNode[] }>,
|
|
95
|
+
view: ZUIViewState,
|
|
96
|
+
canvasW: number,
|
|
97
|
+
hiddenTags: string[] = [],
|
|
98
|
+
) {
|
|
99
|
+
const thresholds = getExpandThresholds(canvasW)
|
|
100
|
+
const visibleAnchors = new Map<number, VisibleNodeAnchor>()
|
|
101
|
+
const byNodeId = new Map<string, VisibleNodeAnchor>()
|
|
102
|
+
const hiddenTagSet = new Set(hiddenTags)
|
|
103
|
+
|
|
104
|
+
for (const group of groups) {
|
|
105
|
+
collectVisibleAnchorsInNodes(
|
|
106
|
+
group.nodes,
|
|
107
|
+
view,
|
|
108
|
+
thresholds,
|
|
109
|
+
hiddenTagSet,
|
|
110
|
+
visibleAnchors,
|
|
111
|
+
byNodeId,
|
|
112
|
+
1,
|
|
113
|
+
0,
|
|
114
|
+
0,
|
|
115
|
+
1,
|
|
116
|
+
0,
|
|
117
|
+
0,
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return { visibleAnchors, byNodeId }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function getDirectAnchorPoint(anchor: VisibleNodeAnchor, towards: VisibleNodeAnchor) {
|
|
125
|
+
const cx = anchor.worldX + anchor.worldW / 2
|
|
126
|
+
const cy = anchor.worldY + anchor.worldH / 2
|
|
127
|
+
const tx = towards.worldX + towards.worldW / 2
|
|
128
|
+
const ty = towards.worldY + towards.worldH / 2
|
|
129
|
+
const dx = tx - cx
|
|
130
|
+
const dy = ty - cy
|
|
131
|
+
const hw = anchor.worldW / 2
|
|
132
|
+
const hh = anchor.worldH / 2
|
|
133
|
+
|
|
134
|
+
if (dx === 0 && dy === 0) return { x: cx, y: cy }
|
|
135
|
+
|
|
136
|
+
const tanTheta = Math.abs(dy / dx)
|
|
137
|
+
const boxRatio = hh / hw
|
|
138
|
+
if (tanTheta < boxRatio) {
|
|
139
|
+
return {
|
|
140
|
+
x: cx + Math.sign(dx) * hw,
|
|
141
|
+
y: cy + Math.sign(dx) * hw * (dy / dx),
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
y: cy + Math.sign(dy) * hh,
|
|
147
|
+
x: cx + Math.sign(dy) * hh * (dx / dy),
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function buildVisibleProxyConnectors(
|
|
152
|
+
snapshot: WorkspaceGraphSnapshot | null,
|
|
153
|
+
visibleAnchors: Map<number, VisibleNodeAnchor>,
|
|
154
|
+
settings: CrossBranchContextSettings,
|
|
155
|
+
): ZUIResolvedConnector[] {
|
|
156
|
+
return resolveZUIProxyConnectors(
|
|
157
|
+
snapshot,
|
|
158
|
+
new Map(Array.from(visibleAnchors.entries()).map(([elementId, anchor]) => [elementId, anchor.nodeId])),
|
|
159
|
+
settings,
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function drawVisibleProxyConnectors(
|
|
164
|
+
ctx: CanvasRenderingContext2D,
|
|
165
|
+
connectors: ZUIResolvedConnector[],
|
|
166
|
+
visibleAnchorsByNodeId: Map<string, VisibleNodeAnchor>,
|
|
167
|
+
zoom: number,
|
|
168
|
+
labelBg: string,
|
|
169
|
+
occupiedLabelRects: ScreenRect[],
|
|
170
|
+
) {
|
|
171
|
+
for (const connector of connectors) {
|
|
172
|
+
const source = visibleAnchorsByNodeId.get(connector.sourceNodeId)
|
|
173
|
+
const target = visibleAnchorsByNodeId.get(connector.targetNodeId)
|
|
174
|
+
if (!source || !target) continue
|
|
175
|
+
const alpha = Math.min(source.renderAlpha, target.renderAlpha)
|
|
176
|
+
if (alpha < 0.01) continue
|
|
177
|
+
|
|
178
|
+
const sourcePoint = getDirectAnchorPoint(source, target)
|
|
179
|
+
const targetPoint = getDirectAnchorPoint(target, source)
|
|
180
|
+
const midX = (sourcePoint.x + targetPoint.x) / 2
|
|
181
|
+
const midY = (sourcePoint.y + targetPoint.y) / 2
|
|
182
|
+
const label = String(connector.details.count)
|
|
183
|
+
|
|
184
|
+
ctx.save()
|
|
185
|
+
ctx.globalAlpha = alpha
|
|
186
|
+
ctx.strokeStyle = 'rgba(255, 255, 255, 0.2)'
|
|
187
|
+
ctx.lineWidth = 2 / zoom
|
|
188
|
+
ctx.beginPath()
|
|
189
|
+
ctx.moveTo(sourcePoint.x, sourcePoint.y)
|
|
190
|
+
ctx.lineTo(targetPoint.x, targetPoint.y)
|
|
191
|
+
ctx.stroke()
|
|
192
|
+
const fontSize = 11 / zoom
|
|
193
|
+
ctx.font = `${fontSize}px Inter, system-ui, sans-serif`
|
|
194
|
+
const textMetrics = ctx.measureText(label)
|
|
195
|
+
const textW = textMetrics.width
|
|
196
|
+
const textH = fontSize
|
|
197
|
+
const labelPos = pickEdgeLabelPosition(
|
|
198
|
+
ctx.getTransform(),
|
|
199
|
+
midX,
|
|
200
|
+
midY,
|
|
201
|
+
textW,
|
|
202
|
+
textH,
|
|
203
|
+
targetPoint.x - sourcePoint.x,
|
|
204
|
+
targetPoint.y - sourcePoint.y,
|
|
205
|
+
occupiedLabelRects,
|
|
206
|
+
)
|
|
207
|
+
const px = 6 / zoom
|
|
208
|
+
const py = 4 / zoom
|
|
209
|
+
const badgeW = textW + px * 2
|
|
210
|
+
const badgeH = textH + py * 2
|
|
211
|
+
const badgeRadius = badgeH / 2
|
|
212
|
+
ctx.fillStyle = labelBg
|
|
213
|
+
ctx.beginPath()
|
|
214
|
+
ctx.roundRect(
|
|
215
|
+
labelPos.x - badgeW / 2,
|
|
216
|
+
labelPos.y - badgeH / 2,
|
|
217
|
+
badgeW,
|
|
218
|
+
badgeH,
|
|
219
|
+
badgeRadius,
|
|
220
|
+
)
|
|
221
|
+
ctx.fill()
|
|
222
|
+
ctx.strokeStyle = 'rgba(255, 255, 255, 0.2)'
|
|
223
|
+
ctx.lineWidth = 1 / zoom
|
|
224
|
+
ctx.stroke()
|
|
225
|
+
ctx.fillStyle = 'white'
|
|
226
|
+
ctx.textAlign = 'center'
|
|
227
|
+
ctx.textBaseline = 'middle'
|
|
228
|
+
ctx.fillText(label, labelPos.x, labelPos.y)
|
|
229
|
+
|
|
230
|
+
ctx.restore()
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export function findHoveredProxyConnector(
|
|
235
|
+
worldX: number,
|
|
236
|
+
worldY: number,
|
|
237
|
+
connectors: ZUIResolvedConnector[],
|
|
238
|
+
visibleAnchorsByNodeId: Map<string, VisibleNodeAnchor>,
|
|
239
|
+
view: ZUIViewState,
|
|
240
|
+
): HoveredItem | null {
|
|
241
|
+
const threshold = 18 / view.zoom
|
|
242
|
+
for (const connector of connectors) {
|
|
243
|
+
const source = visibleAnchorsByNodeId.get(connector.sourceNodeId)
|
|
244
|
+
const target = visibleAnchorsByNodeId.get(connector.targetNodeId)
|
|
245
|
+
if (!source || !target) continue
|
|
246
|
+
const x1 = source.worldX + source.worldW / 2
|
|
247
|
+
const y1 = source.worldY + source.worldH / 2
|
|
248
|
+
const x2 = target.worldX + target.worldW / 2
|
|
249
|
+
const y2 = target.worldY + target.worldH / 2
|
|
250
|
+
const dx = x2 - x1
|
|
251
|
+
const dy = y2 - y1
|
|
252
|
+
const l2 = dx * dx + dy * dy
|
|
253
|
+
if (l2 === 0) continue
|
|
254
|
+
let t = ((worldX - x1) * dx + (worldY - y1) * dy) / l2
|
|
255
|
+
t = Math.max(0, Math.min(1, t))
|
|
256
|
+
const nearestX = x1 + t * dx
|
|
257
|
+
const nearestY = y1 + t * dy
|
|
258
|
+
const dist = Math.sqrt((worldX - nearestX) ** 2 + (worldY - nearestY) ** 2)
|
|
259
|
+
if (dist > threshold) continue
|
|
260
|
+
|
|
261
|
+
return {
|
|
262
|
+
type: 'edge',
|
|
263
|
+
data: {
|
|
264
|
+
sourceId: connector.details.sourceAnchorName,
|
|
265
|
+
targetId: connector.details.targetAnchorName,
|
|
266
|
+
label: connector.details.label || 'Cross-branch connector',
|
|
267
|
+
diagramId: connector.details.ownerViewIds[0] ?? 0,
|
|
268
|
+
sourceObjId: connector.sourceAnchorElementId,
|
|
269
|
+
targetObjId: connector.targetAnchorElementId,
|
|
270
|
+
isProxy: true,
|
|
271
|
+
details: connector.details,
|
|
272
|
+
},
|
|
273
|
+
absX: (x1 + x2) / 2,
|
|
274
|
+
absY: (y1 + y2) / 2,
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return null
|
|
278
|
+
}
|