@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.
Files changed (272) hide show
  1. package/dist/App.d.ts +1 -0
  2. package/dist/api/client.d.ts +143 -0
  3. package/dist/api/transport-vscode.d.ts +8 -0
  4. package/dist/api/transport.d.ts +1 -0
  5. package/dist/components/CodePreviewPanel-vscode.d.ts +7 -0
  6. package/dist/components/CodePreviewPanel.d.ts +9 -0
  7. package/dist/components/ConfirmDialog.d.ts +12 -0
  8. package/dist/components/ConnectorPanel.d.ts +21 -0
  9. package/dist/components/ContextBoundaryElement.d.ts +11 -0
  10. package/dist/components/ContextNeighborElement.d.ts +29 -0
  11. package/dist/components/ContextStraightConnector.d.ts +4 -0
  12. package/dist/components/CrossBranchControls.d.ts +9 -0
  13. package/dist/components/DependenciesOnboarding.d.ts +5 -0
  14. package/dist/components/DrawingCanvas.d.ts +39 -0
  15. package/dist/components/ElementLibrary-vscode.d.ts +7 -0
  16. package/dist/components/ElementLibrary.d.ts +22 -0
  17. package/dist/components/ElementNode.d.ts +36 -0
  18. package/dist/components/ElementPanel.d.ts +25 -0
  19. package/dist/components/ExploreOnboarding.d.ts +5 -0
  20. package/dist/components/ExplorePageOnboarding.d.ts +5 -0
  21. package/dist/components/ExportModal.d.ts +16 -0
  22. package/dist/components/FloatingEdge.d.ts +9 -0
  23. package/dist/components/GitSourceLinker.d.ts +8 -0
  24. package/dist/components/HeaderContext.d.ts +16 -0
  25. package/dist/components/Icons.d.ts +95 -0
  26. package/dist/components/ImportModal.d.ts +10 -0
  27. package/dist/components/InlineElementAdder.d.ts +17 -0
  28. package/dist/components/LayoutSection.d.ts +7 -0
  29. package/dist/components/LocalSourceLinker.d.ts +8 -0
  30. package/dist/components/MiniZoomOnboarding.d.ts +5 -0
  31. package/dist/components/NavBreadcrumb.d.ts +6 -0
  32. package/dist/components/NodeBody.d.ts +12 -0
  33. package/dist/components/NodeContainer.d.ts +8 -0
  34. package/dist/components/NodeHoverCard.d.ts +10 -0
  35. package/dist/components/PanelHeader.d.ts +8 -0
  36. package/dist/components/PanelUI.d.ts +3 -0
  37. package/dist/components/ProxyConnectorEdge.d.ts +4 -0
  38. package/dist/components/ProxyConnectorPanel.d.ts +9 -0
  39. package/dist/components/SafeBackground.d.ts +13 -0
  40. package/dist/components/ScrollIndicatorWrapper.d.ts +8 -0
  41. package/dist/components/SetChildModal.d.ts +10 -0
  42. package/dist/components/SetParentModal.d.ts +10 -0
  43. package/dist/components/SlidingPanel.d.ts +16 -0
  44. package/dist/components/TagUpsert.d.ts +8 -0
  45. package/dist/components/TopMenuBar.d.ts +8 -0
  46. package/dist/components/ViewBezierConnector.d.ts +4 -0
  47. package/dist/components/ViewDrawMenu.d.ts +22 -0
  48. package/dist/components/ViewEditorEdgeLabelLayout.d.ts +16 -0
  49. package/dist/components/ViewEditorOnboarding.d.ts +5 -0
  50. package/dist/components/ViewExplorer/TagManager/ColorPicker.d.ts +7 -0
  51. package/dist/components/ViewExplorer/TagManager/GroupNamingPopover.d.ts +10 -0
  52. package/dist/components/ViewExplorer/TagManager/LayerItem.d.ts +27 -0
  53. package/dist/components/ViewExplorer/TagManager/TagItem.d.ts +25 -0
  54. package/dist/components/ViewExplorer/TagManager/index.d.ts +21 -0
  55. package/dist/components/ViewExplorer/ViewNavigator.d.ts +11 -0
  56. package/dist/components/ViewExplorer/ViewSearch.d.ts +8 -0
  57. package/dist/components/ViewExplorer/ViewTree.d.ts +18 -0
  58. package/dist/components/ViewExplorer/index.d.ts +31 -0
  59. package/dist/components/ViewExplorer/types.d.ts +11 -0
  60. package/dist/components/ViewExplorer/utils.d.ts +6 -0
  61. package/dist/components/ViewExplorer-vscode.d.ts +6 -0
  62. package/dist/components/ViewFloatingMenu-vscode.d.ts +27 -0
  63. package/dist/components/ViewFloatingMenu.d.ts +39 -0
  64. package/dist/components/ViewGridNode.d.ts +29 -0
  65. package/dist/components/ViewHeaderButton.d.ts +11 -0
  66. package/dist/components/ViewPanel.d.ts +18 -0
  67. package/dist/components/ViewsGridOnboarding.d.ts +5 -0
  68. package/dist/components/ZUI/ZUICanvas.d.ts +18 -0
  69. package/dist/components/ZUI/index.d.ts +2 -0
  70. package/dist/components/ZUI/layout.d.ts +18 -0
  71. package/dist/components/ZUI/proxy.d.ts +25 -0
  72. package/dist/components/ZUI/renderer.d.ts +30 -0
  73. package/dist/components/ZUI/types.d.ts +140 -0
  74. package/dist/components/ZUI/useZUIInteraction.d.ts +21 -0
  75. package/dist/config/runtime-vscode.d.ts +22 -0
  76. package/dist/config/runtime.d.ts +5 -0
  77. package/dist/constants/colors.d.ts +27 -0
  78. package/dist/constants/diagramColors.d.ts +1 -0
  79. package/dist/context/ThemeContext.d.ts +27 -0
  80. package/dist/crossBranch/graph.d.ts +13 -0
  81. package/dist/crossBranch/resolve.d.ts +22 -0
  82. package/dist/crossBranch/settings.d.ts +6 -0
  83. package/dist/crossBranch/store.d.ts +11 -0
  84. package/dist/crossBranch/types.d.ts +96 -0
  85. package/dist/demo/DemoPage.d.ts +9 -0
  86. package/dist/demo/seed.d.ts +9 -0
  87. package/dist/demo/store.d.ts +137 -0
  88. package/dist/demo/viewEditor.d.ts +26 -0
  89. package/dist/favicon.svg +35 -0
  90. package/dist/hooks/useSafeFitView.d.ts +16 -0
  91. package/dist/index.css +1 -0
  92. package/dist/index.d.ts +115 -0
  93. package/dist/index.js +19966 -0
  94. package/dist/lib/vscodeBridge-vscode.d.ts +13 -0
  95. package/dist/lib/vscodeBridge.d.ts +5 -0
  96. package/dist/logo-120.png +0 -0
  97. package/dist/logo-bw.png +0 -0
  98. package/dist/logo-bw.svg +15 -0
  99. package/dist/logo-text.svg +51 -0
  100. package/dist/logo.svg +35 -0
  101. package/dist/pages/AppearanceSettings.d.ts +3 -0
  102. package/dist/pages/Dependencies.d.ts +1 -0
  103. package/dist/pages/InfiniteZoom.d.ts +7 -0
  104. package/dist/pages/Settings.d.ts +7 -0
  105. package/dist/pages/ViewEditor/components/EditorMenus.d.ts +24 -0
  106. package/dist/pages/ViewEditor/components/EditorOverlays.d.ts +30 -0
  107. package/dist/pages/ViewEditor/components/EmptyCanvasState.d.ts +7 -0
  108. package/dist/pages/ViewEditor/context.d.ts +13 -0
  109. package/dist/pages/ViewEditor/hooks/useCanvasInteractions.d.ts +201 -0
  110. package/dist/pages/ViewEditor/hooks/useDrawingEngine.d.ts +40 -0
  111. package/dist/pages/ViewEditor/hooks/useViewContextNeighbours.d.ts +20 -0
  112. package/dist/pages/ViewEditor/hooks/useViewData.d.ts +74 -0
  113. package/dist/pages/ViewEditor/index.d.ts +8 -0
  114. package/dist/pages/ViewEditor/utils.d.ts +14 -0
  115. package/dist/pages/Views.d.ts +6 -0
  116. package/dist/pages/ViewsGrid.d.ts +6 -0
  117. package/dist/pkg/importer/mermaid.d.ts +7 -0
  118. package/dist/pkg/importer/mermaid.test.d.ts +1 -0
  119. package/dist/platform/PlatformContext.d.ts +6 -0
  120. package/dist/platform/context.d.ts +3 -0
  121. package/dist/platform/local.d.ts +2 -0
  122. package/dist/platform/types.d.ts +17 -0
  123. package/dist/slots.d.ts +67 -0
  124. package/dist/theme.d.ts +2 -0
  125. package/dist/types/index.d.ts +193 -0
  126. package/dist/types/vscode-messages.d.ts +60 -0
  127. package/dist/utils/edgeDistribution.d.ts +34 -0
  128. package/dist/utils/githubApi.d.ts +4 -0
  129. package/dist/utils/githubCache.d.ts +17 -0
  130. package/dist/utils/ids.d.ts +2 -0
  131. package/dist/utils/technologyCatalog.d.ts +15 -0
  132. package/dist/utils/toast.d.ts +15 -0
  133. package/dist/utils/treesitter.d.ts +13 -0
  134. package/dist/utils/url.d.ts +12 -0
  135. package/package.json +159 -0
  136. package/src/App.tsx +141 -0
  137. package/src/api/client.ts +618 -0
  138. package/src/api/transport-vscode.ts +28 -0
  139. package/src/api/transport.ts +7 -0
  140. package/src/assets/logo-mark.svg +31 -0
  141. package/src/assets/logo-wordmark.svg +22 -0
  142. package/src/assets/logo.svg +35 -0
  143. package/src/components/CodePreviewPanel-vscode.tsx +85 -0
  144. package/src/components/CodePreviewPanel.tsx +384 -0
  145. package/src/components/ConfirmDialog.tsx +66 -0
  146. package/src/components/ConnectorPanel.tsx +403 -0
  147. package/src/components/ContextBoundaryElement.tsx +35 -0
  148. package/src/components/ContextNeighborElement.tsx +282 -0
  149. package/src/components/ContextStraightConnector.tsx +144 -0
  150. package/src/components/CrossBranchControls.tsx +105 -0
  151. package/src/components/DependenciesOnboarding.tsx +427 -0
  152. package/src/components/DrawingCanvas.tsx +391 -0
  153. package/src/components/ElementLibrary-vscode.tsx +9 -0
  154. package/src/components/ElementLibrary.tsx +512 -0
  155. package/src/components/ElementNode.tsx +1033 -0
  156. package/src/components/ElementPanel.tsx +928 -0
  157. package/src/components/ExploreOnboarding.tsx +347 -0
  158. package/src/components/ExplorePageOnboarding.tsx +383 -0
  159. package/src/components/ExportModal.tsx +132 -0
  160. package/src/components/FloatingEdge.tsx +115 -0
  161. package/src/components/GitSourceLinker.tsx +1053 -0
  162. package/src/components/HeaderContext.tsx +30 -0
  163. package/src/components/Icons.tsx +245 -0
  164. package/src/components/ImportModal.tsx +219 -0
  165. package/src/components/InlineElementAdder.tsx +216 -0
  166. package/src/components/LayoutSection.tsx +624 -0
  167. package/src/components/LocalSourceLinker.tsx +330 -0
  168. package/src/components/MiniZoomOnboarding.tsx +78 -0
  169. package/src/components/NavBreadcrumb.tsx +24 -0
  170. package/src/components/NodeBody.tsx +89 -0
  171. package/src/components/NodeContainer.tsx +58 -0
  172. package/src/components/NodeHoverCard.tsx +135 -0
  173. package/src/components/PanelHeader.tsx +36 -0
  174. package/src/components/PanelUI.tsx +24 -0
  175. package/src/components/ProxyConnectorEdge.tsx +169 -0
  176. package/src/components/ProxyConnectorPanel.tsx +130 -0
  177. package/src/components/SafeBackground.tsx +19 -0
  178. package/src/components/ScrollIndicatorWrapper.tsx +117 -0
  179. package/src/components/SetChildModal.tsx +191 -0
  180. package/src/components/SetParentModal.tsx +187 -0
  181. package/src/components/SlidingPanel.tsx +114 -0
  182. package/src/components/TagUpsert.tsx +142 -0
  183. package/src/components/TopMenuBar.tsx +380 -0
  184. package/src/components/ViewBezierConnector.tsx +143 -0
  185. package/src/components/ViewDrawMenu.tsx +270 -0
  186. package/src/components/ViewEditorEdgeLabelLayout.ts +189 -0
  187. package/src/components/ViewEditorOnboarding.tsx +445 -0
  188. package/src/components/ViewExplorer/TagManager/ColorPicker.tsx +49 -0
  189. package/src/components/ViewExplorer/TagManager/GroupNamingPopover.tsx +96 -0
  190. package/src/components/ViewExplorer/TagManager/LayerItem.tsx +228 -0
  191. package/src/components/ViewExplorer/TagManager/TagItem.tsx +242 -0
  192. package/src/components/ViewExplorer/TagManager/index.tsx +418 -0
  193. package/src/components/ViewExplorer/ViewNavigator.tsx +121 -0
  194. package/src/components/ViewExplorer/ViewSearch.tsx +33 -0
  195. package/src/components/ViewExplorer/ViewTree.tsx +98 -0
  196. package/src/components/ViewExplorer/index.tsx +384 -0
  197. package/src/components/ViewExplorer/types.ts +13 -0
  198. package/src/components/ViewExplorer/utils.ts +56 -0
  199. package/src/components/ViewExplorer-vscode.tsx +8 -0
  200. package/src/components/ViewFloatingMenu-vscode.tsx +248 -0
  201. package/src/components/ViewFloatingMenu.tsx +379 -0
  202. package/src/components/ViewGridNode.tsx +451 -0
  203. package/src/components/ViewHeaderButton.tsx +60 -0
  204. package/src/components/ViewPanel.tsx +162 -0
  205. package/src/components/ViewsGridOnboarding.tsx +400 -0
  206. package/src/components/ZUI/ZUICanvas.tsx +853 -0
  207. package/src/components/ZUI/index.ts +3 -0
  208. package/src/components/ZUI/layout.ts +323 -0
  209. package/src/components/ZUI/proxy.ts +278 -0
  210. package/src/components/ZUI/renderer.ts +1189 -0
  211. package/src/components/ZUI/types.ts +150 -0
  212. package/src/components/ZUI/useZUIInteraction.ts +720 -0
  213. package/src/config/runtime-vscode.ts +46 -0
  214. package/src/config/runtime.ts +30 -0
  215. package/src/constants/colors.ts +80 -0
  216. package/src/constants/diagramColors.ts +9 -0
  217. package/src/context/ThemeContext.tsx +158 -0
  218. package/src/crossBranch/graph.ts +207 -0
  219. package/src/crossBranch/resolve.ts +643 -0
  220. package/src/crossBranch/settings.ts +59 -0
  221. package/src/crossBranch/store.ts +71 -0
  222. package/src/crossBranch/types.ts +102 -0
  223. package/src/demo/DemoPage.tsx +184 -0
  224. package/src/demo/seed.ts +67 -0
  225. package/src/demo/store.ts +536 -0
  226. package/src/demo/viewEditor.ts +110 -0
  227. package/src/hooks/useSafeFitView.ts +60 -0
  228. package/src/index.css +309 -0
  229. package/src/index.ts +184 -0
  230. package/src/kafka-ss.png +0 -0
  231. package/src/lib/vscodeBridge-vscode.ts +27 -0
  232. package/src/lib/vscodeBridge.ts +7 -0
  233. package/src/main.tsx +46 -0
  234. package/src/pages/AppearanceSettings.tsx +135 -0
  235. package/src/pages/Dependencies.tsx +926 -0
  236. package/src/pages/InfiniteZoom.tsx +404 -0
  237. package/src/pages/Settings.tsx +91 -0
  238. package/src/pages/ViewEditor/EDGE_DISTRIBUTION.md +64 -0
  239. package/src/pages/ViewEditor/components/EditorMenus.tsx +112 -0
  240. package/src/pages/ViewEditor/components/EditorOverlays.tsx +172 -0
  241. package/src/pages/ViewEditor/components/EmptyCanvasState.tsx +42 -0
  242. package/src/pages/ViewEditor/context.tsx +21 -0
  243. package/src/pages/ViewEditor/hooks/useCanvasInteractions.ts +1349 -0
  244. package/src/pages/ViewEditor/hooks/useDrawingEngine.ts +127 -0
  245. package/src/pages/ViewEditor/hooks/useViewContextNeighbours.ts +501 -0
  246. package/src/pages/ViewEditor/hooks/useViewData.ts +491 -0
  247. package/src/pages/ViewEditor/index.tsx +1366 -0
  248. package/src/pages/ViewEditor/utils.ts +88 -0
  249. package/src/pages/Views.tsx +171 -0
  250. package/src/pages/ViewsGrid.tsx +1310 -0
  251. package/src/pkg/importer/mermaid.test.ts +141 -0
  252. package/src/pkg/importer/mermaid.ts +76 -0
  253. package/src/platform/PlatformContext.tsx +17 -0
  254. package/src/platform/context.ts +9 -0
  255. package/src/platform/local.tsx +15 -0
  256. package/src/platform/types.ts +19 -0
  257. package/src/slots.ts +92 -0
  258. package/src/styles/editor-panels.css +66 -0
  259. package/src/styles/theme.css +56 -0
  260. package/src/theme.ts +336 -0
  261. package/src/types/index.ts +234 -0
  262. package/src/types/offline-ambient.d.ts +14 -0
  263. package/src/types/vscode-messages.ts +32 -0
  264. package/src/utils/edgeDistribution.ts +103 -0
  265. package/src/utils/githubApi.ts +121 -0
  266. package/src/utils/githubCache.ts +108 -0
  267. package/src/utils/ids.ts +9 -0
  268. package/src/utils/technologyCatalog.ts +143 -0
  269. package/src/utils/toast.ts +100 -0
  270. package/src/utils/treesitter.ts +147 -0
  271. package/src/utils/url.ts +72 -0
  272. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,282 @@
1
+ import { memo, useEffect, useMemo, useState } from 'react'
2
+ import { Handle, Position } from 'reactflow'
3
+ import {
4
+ Badge,
5
+ Box,
6
+ Button,
7
+ Divider,
8
+ HStack,
9
+ Popover,
10
+ PopoverArrow,
11
+ PopoverBody,
12
+ PopoverContent,
13
+ PopoverHeader,
14
+ PopoverTrigger,
15
+ Portal,
16
+ Tag,
17
+ Text,
18
+ VStack,
19
+ } from '@chakra-ui/react'
20
+ import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, LinkIcon } from '@chakra-ui/icons'
21
+ import type { PlacedElement } from '../types'
22
+ import { TYPE_COLORS } from '../types'
23
+ import { resolveIconPath } from '../utils/url'
24
+ import { ElementBody } from './NodeBody'
25
+ import { ElementContainer } from './NodeContainer'
26
+
27
+ interface ContextNeighborData {
28
+ element_id: number
29
+ name: string
30
+ kind: string | null
31
+ description: string | null
32
+ technology: string | null
33
+ logo_url: string | null
34
+ technology_connectors: PlacedElement['technology_connectors']
35
+ ownerViewIds: number[]
36
+ ownerViewNames: string[]
37
+ commonAncestorViewId: number | null
38
+ commonAncestorViewName: string | null
39
+ connectorCount: number
40
+ onNavigateToView: (viewId: number) => void
41
+ onSelectDetails?: () => void
42
+ isCanvasMoving?: boolean
43
+ isGroupAnchor?: boolean
44
+ groupChildCount?: number
45
+ isGroupExpanded?: boolean
46
+ onToggleGroup?: () => void
47
+ side?: 'top' | 'bottom' | 'left' | 'right'
48
+ }
49
+
50
+ interface Props {
51
+ data: ContextNeighborData
52
+ }
53
+
54
+ function ContextNeighborNode({ data }: Props) {
55
+ const [isHovered, setIsHovered] = useState(false)
56
+
57
+ useEffect(() => {
58
+ if (data.isCanvasMoving) setIsHovered(false)
59
+ }, [data.isCanvasMoving])
60
+
61
+ const color = TYPE_COLORS[data.kind ?? ''] ?? 'gray'
62
+
63
+ const logoUrl = useMemo(() => {
64
+ if (data.logo_url) return resolveIconPath(data.logo_url)
65
+ const selected = data.technology_connectors?.find((link) => link.type === 'catalog' && !!link.is_primary_icon && !!link.slug)
66
+ if (!selected?.slug) return undefined
67
+ return resolveIconPath(`/icons/${selected.slug}.png`)
68
+ }, [data.logo_url, data.technology_connectors])
69
+
70
+ const primaryOwnerViewId = data.ownerViewIds[0] ?? data.commonAncestorViewId ?? null
71
+ const isGroupAnchor = data.isGroupAnchor ?? false
72
+ const groupChildCount = data.groupChildCount ?? 0
73
+ const isGroupExpanded = data.isGroupExpanded ?? false
74
+ const side = data.side ?? 'right'
75
+
76
+ // Position chevron just past the visual node edge (node is scale(0.5) with center origin,
77
+ // so the visual edge is at 75% of the layout box dimension on the outward side).
78
+ const chevronStyle = (() => {
79
+ switch (side) {
80
+ case 'right': return { left: 'calc(75% + 6px)', top: '50%', transform: 'translateY(-50%)' }
81
+ case 'left': return { right: 'calc(75% + 6px)', top: '50%', transform: 'translateY(-50%)' }
82
+ case 'bottom': return { top: 'calc(75% + 6px)', left: '50%', transform: 'translateX(-50%)' }
83
+ case 'top': return { bottom: 'calc(75% + 6px)', left: '50%', transform: 'translateX(-50%)' }
84
+ }
85
+ })()
86
+
87
+ const ChevronExpandIcon = (() => {
88
+ switch (side) {
89
+ case 'right': return isGroupExpanded ? ChevronLeftIcon : ChevronRightIcon
90
+ case 'left': return isGroupExpanded ? ChevronRightIcon : ChevronLeftIcon
91
+ case 'bottom': return isGroupExpanded ? ChevronUpIcon : ChevronDownIcon
92
+ case 'top': return isGroupExpanded ? ChevronDownIcon : ChevronUpIcon
93
+ }
94
+ })()
95
+
96
+ return (
97
+ <Box pointerEvents="none" position="relative">
98
+ <Handle type="source" position={Position.Top} id="top" style={{ opacity: 0, pointerEvents: 'none' }} isConnectable={false} />
99
+ <Handle type="source" position={Position.Bottom} id="bottom" style={{ opacity: 0, pointerEvents: 'none' }} isConnectable={false} />
100
+ <Handle type="source" position={Position.Left} id="left" style={{ opacity: 0, pointerEvents: 'none' }} isConnectable={false} />
101
+ <Handle type="source" position={Position.Right} id="right" style={{ opacity: 0, pointerEvents: 'none' }} isConnectable={false} />
102
+ <Handle type="target" position={Position.Top} id="top" style={{ opacity: 0, pointerEvents: 'none' }} isConnectable={false} />
103
+ <Handle type="target" position={Position.Bottom} id="bottom" style={{ opacity: 0, pointerEvents: 'none' }} isConnectable={false} />
104
+ <Handle type="target" position={Position.Left} id="left" style={{ opacity: 0, pointerEvents: 'none' }} isConnectable={false} />
105
+ <Handle type="target" position={Position.Right} id="right" style={{ opacity: 0, pointerEvents: 'none' }} isConnectable={false} />
106
+
107
+ <Popover isOpen={isHovered && !data.isCanvasMoving} placement="right-start" closeOnBlur={false} gutter={12} isLazy>
108
+ <PopoverTrigger>
109
+ <Box transform="scale(0.5)" transformOrigin="center center">
110
+ <Box position="relative">
111
+ {/* Transparent ghost cards indicating a collapsed stack */}
112
+ {isGroupAnchor && !isGroupExpanded && groupChildCount > 0 && (
113
+ <>
114
+ <Box
115
+ position="absolute"
116
+ top="10px"
117
+ left="10px"
118
+ right="-10px"
119
+ bottom="-10px"
120
+ borderRadius="md"
121
+ border="1px solid"
122
+ borderColor="whiteAlpha.200"
123
+ zIndex={-2}
124
+ />
125
+ <Box
126
+ position="absolute"
127
+ top="5px"
128
+ left="5px"
129
+ right="-5px"
130
+ bottom="-5px"
131
+ borderRadius="md"
132
+ border="1px solid"
133
+ borderColor="whiteAlpha.300"
134
+ zIndex={-1}
135
+ />
136
+ </>
137
+ )}
138
+ <ElementContainer
139
+ minW="180px"
140
+ maxW="230px"
141
+ cursor={data.onSelectDetails || primaryOwnerViewId ? 'pointer' : 'default'}
142
+ pointerEvents="auto"
143
+ onMouseEnter={() => setIsHovered(true)}
144
+ onMouseLeave={() => setIsHovered(false)}
145
+ onClick={() => {
146
+ if (data.onSelectDetails) {
147
+ data.onSelectDetails()
148
+ return
149
+ }
150
+ if (primaryOwnerViewId) data.onNavigateToView(primaryOwnerViewId)
151
+ }}
152
+ opacity={0.82}
153
+ _hover={{ opacity: 1, transform: 'scale(1.05)' }}
154
+ transition="all 0.2s"
155
+ bg="transparent"
156
+ >
157
+ <ElementBody
158
+ name={data.name}
159
+ type={data.kind ?? ''}
160
+ logoUrl={logoUrl}
161
+ nameSize="xl"
162
+ minH="85px"
163
+ pt={2}
164
+ pb={2}
165
+ />
166
+ </ElementContainer>
167
+ </Box>
168
+ </Box>
169
+ </PopoverTrigger>
170
+
171
+ <Portal>
172
+ <PopoverContent
173
+ bg="gray.900"
174
+ borderColor="whiteAlpha.300"
175
+ boxShadow="2xl"
176
+ width="300px"
177
+ _focus={{ boxShadow: 'none' }}
178
+ pointerEvents="auto"
179
+ >
180
+ <PopoverArrow bg="gray.900" />
181
+ <PopoverHeader borderBottom="1px solid" borderColor="whiteAlpha.200" px={4} py={3}>
182
+ <HStack spacing={3}>
183
+ {logoUrl && (
184
+ <Box flexShrink={0}>
185
+ <Box as="img" src={logoUrl} w="24px" h="24px" objectFit="contain" />
186
+ </Box>
187
+ )}
188
+ <VStack align="start" spacing={0} flex={1} overflow="hidden">
189
+ <Text fontWeight="600" fontSize="sm" isTruncated width="100%" color="white">
190
+ {data.name}
191
+ </Text>
192
+ <Tag colorScheme={color} variant="subtle" fontSize="2xs">
193
+ {data.kind || 'branch'}
194
+ </Tag>
195
+ </VStack>
196
+ <Badge colorScheme="blue" variant="subtle">
197
+ {data.connectorCount}
198
+ </Badge>
199
+ </HStack>
200
+ </PopoverHeader>
201
+ <PopoverBody px={4} py={3}>
202
+ <VStack align="start" spacing={3}>
203
+ {data.technology && (
204
+ <Box>
205
+ <Text color="gray.400" fontSize="xs" fontWeight="600" mb={0.5} letterSpacing="wider">TECHNOLOGY</Text>
206
+ <Text fontSize="xs" color="gray.200">{data.technology}</Text>
207
+ </Box>
208
+ )}
209
+ {data.description && (
210
+ <Box>
211
+ <Text color="gray.400" fontSize="xs" fontWeight="600" mb={0.5} letterSpacing="wider">DESCRIPTION</Text>
212
+ <Text fontSize="xs" color="gray.200" noOfLines={4}>{data.description}</Text>
213
+ </Box>
214
+ )}
215
+ <Box>
216
+ <Text color="gray.400" fontSize="xs" fontWeight="600" mb={0.5} letterSpacing="wider">BRANCH CONTEXT</Text>
217
+ <Text fontSize="xs" color="gray.200">
218
+ {data.commonAncestorViewName
219
+ ? `Branch diverges around ${data.commonAncestorViewName}.`
220
+ : 'Branch lives outside the current visible scope.'}
221
+ </Text>
222
+ </Box>
223
+ <Divider borderColor="whiteAlpha.200" />
224
+ <VStack align="stretch" spacing={2} width="full">
225
+ {data.ownerViewIds.slice(0, 4).map((viewId, index) => (
226
+ <Button
227
+ key={`${viewId}-${index}`}
228
+ size="xs"
229
+ justifyContent="space-between"
230
+ variant="ghost"
231
+ color="gray.200"
232
+ _hover={{ bg: 'whiteAlpha.100', color: 'white' }}
233
+ rightIcon={<LinkIcon />}
234
+ onClick={() => data.onNavigateToView(viewId)}
235
+ >
236
+ <Text fontSize="xs" isTruncated>{data.ownerViewNames[index] ?? `View ${viewId}`}</Text>
237
+ </Button>
238
+ ))}
239
+ </VStack>
240
+ </VStack>
241
+ </PopoverBody>
242
+ </PopoverContent>
243
+ </Portal>
244
+ </Popover>
245
+
246
+ {/* Expand/collapse chevron positioned on the outward-facing side of the stack */}
247
+ {isGroupAnchor && groupChildCount > 0 && (
248
+ <Box
249
+ position="absolute"
250
+ {...chevronStyle}
251
+ pointerEvents="auto"
252
+ zIndex={10}
253
+ >
254
+ <Button
255
+ variant="unstyled"
256
+ display="inline-flex"
257
+ alignItems="center"
258
+ gap={0.5}
259
+ color="whiteAlpha.400"
260
+ _hover={{ color: 'whiteAlpha.700' }}
261
+ height="auto"
262
+ minW="auto"
263
+ p={0}
264
+ lineHeight={1}
265
+ transition="color 0.15s"
266
+ onClick={(e) => {
267
+ e.stopPropagation()
268
+ data.onToggleGroup?.()
269
+ }}
270
+ >
271
+ <ChevronExpandIcon boxSize={2} />
272
+ {!isGroupExpanded && (
273
+ <Text as="span" fontSize="8px" letterSpacing="0.02em">{groupChildCount}</Text>
274
+ )}
275
+ </Button>
276
+ </Box>
277
+ )}
278
+ </Box>
279
+ )
280
+ }
281
+
282
+ export default memo(ContextNeighborNode)
@@ -0,0 +1,144 @@
1
+ import { memo } from 'react'
2
+ import { EdgeProps, getStraightPath, useStore } from 'reactflow'
3
+
4
+ function getIntersectionPoint(
5
+ nx: number, ny: number, nw: number, nh: number,
6
+ tx: number, ty: number, tw: number, th: number
7
+ ) {
8
+ // Center of node
9
+ const cx = nx + nw / 2
10
+ const cy = ny + nh / 2
11
+ // Center of target
12
+ const cx2 = tx + tw / 2
13
+ const cy2 = ty + th / 2
14
+
15
+ const dx = cx2 - cx
16
+ const dy = cy2 - cy
17
+
18
+ // Intersection with the boundary of node (nx, ny, nw, nh)
19
+ const hw = nw / 2
20
+ const hh = nh / 2
21
+
22
+ if (dx === 0 && dy === 0) return { x: cx, y: cy }
23
+
24
+ const tanTheta = Math.abs(dy / dx)
25
+ const boxRatio = hh / hw
26
+
27
+ let ix, iy
28
+ if (tanTheta < boxRatio) {
29
+ // Left or right border
30
+ ix = cx + Math.sign(dx) * hw
31
+ iy = cy + Math.sign(dx) * hw * (dy / dx)
32
+ } else {
33
+ // Top or bottom border
34
+ iy = cy + Math.sign(dy) * hh
35
+ ix = cx + Math.sign(dy) * hh * (dx / dy)
36
+ }
37
+
38
+ return { x: ix, y: iy }
39
+ }
40
+ function ContextStraightConnector({
41
+ id,
42
+ source,
43
+ target,
44
+ style,
45
+ markerEnd,
46
+ markerStart,
47
+ data,
48
+ }: EdgeProps) {
49
+ const sourceNode = useStore((s) => s.nodeInternals.get(source))
50
+ const targetNode = useStore((s) => s.nodeInternals.get(target))
51
+
52
+ if (!sourceNode || !targetNode) {
53
+ return null
54
+ }
55
+
56
+ let sx = sourceNode.positionAbsolute?.x ?? sourceNode.position.x
57
+ let sy = sourceNode.positionAbsolute?.y ?? sourceNode.position.y
58
+ let sw = sourceNode.width ?? 0
59
+ let sh = sourceNode.height ?? 0
60
+
61
+ let tx = targetNode.positionAbsolute?.x ?? targetNode.position.x
62
+ let ty = targetNode.positionAbsolute?.y ?? targetNode.position.y
63
+ let tw = targetNode.width ?? 0
64
+ let th = targetNode.height ?? 0
65
+
66
+ if (!sw || !sh || !tw || !th) return null
67
+
68
+ if (sourceNode.type === 'contextNeighborNode') {
69
+ const scale = 0.5
70
+ sx = sx + sw * ((1 - scale) / 2)
71
+ sy = sy + sh * ((1 - scale) / 2)
72
+ sw = sw * scale
73
+ sh = sh * scale
74
+ }
75
+
76
+ if (targetNode.type === 'contextNeighborNode') {
77
+ const scale = 0.5
78
+ tx = tx + tw * ((1 - scale) / 2)
79
+ ty = ty + th * ((1 - scale) / 2)
80
+ tw = tw * scale
81
+ th = th * scale
82
+ }
83
+
84
+ // Shortest distance: from center of source to center of target, intersect with their borders.
85
+ const startPoint = getIntersectionPoint(sx, sy, sw, sh, tx, ty, tw, th)
86
+ const endPoint = getIntersectionPoint(tx, ty, tw, th, sx, sy, sw, sh)
87
+
88
+
89
+ let finalStartPoint = startPoint
90
+ let finalEndPoint = endPoint
91
+
92
+ const { sourceGroupIndex, sourceGroupCount, targetGroupIndex, targetGroupCount } = (data as {
93
+ sourceGroupIndex?: number; sourceGroupCount?: number;
94
+ targetGroupIndex?: number; targetGroupCount?: number;
95
+ }) || {}
96
+
97
+ const dx = endPoint.x - startPoint.x
98
+ const dy = endPoint.y - startPoint.y
99
+ const len = Math.hypot(dx, dy)
100
+ const PADDING = 12
101
+
102
+ if (len > 0) {
103
+ const nx = -dy / len
104
+ const ny = dx / len
105
+
106
+ if (typeof sourceGroupIndex === 'number' && typeof sourceGroupCount === 'number' && sourceGroupCount > 1) {
107
+ const offset = (sourceGroupIndex - (sourceGroupCount - 1) / 2) * PADDING
108
+ finalStartPoint = { x: startPoint.x + nx * offset, y: startPoint.y + ny * offset }
109
+ }
110
+
111
+ if (typeof targetGroupIndex === 'number' && typeof targetGroupCount === 'number' && targetGroupCount > 1) {
112
+ const offset = (targetGroupIndex - (targetGroupCount - 1) / 2) * PADDING
113
+ finalEndPoint = { x: endPoint.x + nx * offset, y: endPoint.y + ny * offset }
114
+ }
115
+ }
116
+
117
+
118
+ const [connectorPath] = getStraightPath({
119
+ sourceX: finalStartPoint.x,
120
+ sourceY: finalStartPoint.y,
121
+ targetX: finalEndPoint.x,
122
+ targetY: finalEndPoint.y,
123
+ })
124
+
125
+
126
+ return (
127
+ <>
128
+ <path
129
+ id={id}
130
+ className="react-flow__connector-path"
131
+ d={connectorPath}
132
+ markerEnd={markerEnd}
133
+ markerStart={markerStart}
134
+ style={{
135
+ ...style,
136
+ pointerEvents: 'none',
137
+ strokeDasharray: '5,5',
138
+ }}
139
+ />
140
+ </>
141
+ )
142
+ }
143
+
144
+ export default memo(ContextStraightConnector)
@@ -0,0 +1,105 @@
1
+ import {
2
+ Box,
3
+ Button,
4
+ HStack,
5
+ Popover,
6
+ PopoverArrow,
7
+ PopoverBody,
8
+ PopoverContent,
9
+ PopoverTrigger,
10
+ Portal,
11
+ Slider,
12
+ SliderFilledTrack,
13
+ SliderThumb,
14
+ SliderTrack,
15
+ Switch,
16
+ Text,
17
+ VStack,
18
+ } from '@chakra-ui/react'
19
+ import { CROSS_BRANCH_DEPTH_ALL, CROSS_BRANCH_DEPTH_MAX, CROSS_BRANCH_DEPTH_MIN } from '../crossBranch/types'
20
+ import type { CrossBranchContextSettings } from '../crossBranch/types'
21
+
22
+ interface Props {
23
+ settings: CrossBranchContextSettings
24
+ onEnabledChange: (enabled: boolean) => void
25
+ onDepthChange: (depth: number) => void
26
+ label?: string
27
+ }
28
+
29
+ function depthLabel(depth: number) {
30
+ return depth >= CROSS_BRANCH_DEPTH_ALL ? 'All' : String(depth)
31
+ }
32
+
33
+ export default function CrossBranchControls({
34
+ settings,
35
+ onEnabledChange,
36
+ onDepthChange,
37
+ label = 'Cross-Branch',
38
+ }: Props) {
39
+ return (
40
+ <Popover placement="top-start" isLazy>
41
+ <PopoverTrigger>
42
+ <Button
43
+ variant="ghost"
44
+ h="28px"
45
+ px={2.5}
46
+ color={settings.enabled ? 'var(--accent)' : 'gray.300'}
47
+ bg={settings.enabled ? 'rgba(var(--accent-rgb), 0.12)' : 'transparent'}
48
+ _hover={{ bg: 'rgba(var(--accent-rgb), 0.12)', color: 'var(--accent)' }}
49
+ >
50
+ <HStack spacing={1.5}>
51
+ <Box w="7px" h="7px" rounded="full" bg={settings.enabled ? 'var(--accent)' : 'gray.500'} />
52
+ <Text fontSize="11px" fontWeight="normal">{label}</Text>
53
+ <Text fontSize="10px" color="gray.400">{settings.enabled ? depthLabel(settings.depth) : 'Off'}</Text>
54
+ </HStack>
55
+ </Button>
56
+ </PopoverTrigger>
57
+ <Portal>
58
+ <PopoverContent
59
+ bg="glass.bg"
60
+ backdropFilter="blur(16px)"
61
+ borderColor="glass.border"
62
+ boxShadow="panel"
63
+ borderRadius="lg"
64
+ width="240px"
65
+ _focus={{ boxShadow: 'none' }}
66
+ >
67
+ <PopoverArrow bg="glass.bg" />
68
+ <PopoverBody p={3}>
69
+ <VStack align="stretch" spacing={3}>
70
+ <HStack justify="space-between">
71
+ <Text fontSize="xs" fontWeight="600" color="white">Show cross-branch context</Text>
72
+ <Switch isChecked={settings.enabled} onChange={(event) => onEnabledChange(event.target.checked)} colorScheme="blue" />
73
+ </HStack>
74
+ <Box opacity={settings.enabled ? 1 : 0.4}>
75
+ <HStack justify="space-between" mb={2}>
76
+ <Text fontSize="10px" fontWeight="700" color="gray.400" letterSpacing="0.08em" textTransform="uppercase">
77
+ Descendant Depth
78
+ </Text>
79
+ <Text fontSize="xs" color="gray.300">{depthLabel(settings.depth)}</Text>
80
+ </HStack>
81
+ <Slider
82
+ isDisabled={!settings.enabled}
83
+ min={CROSS_BRANCH_DEPTH_MIN}
84
+ max={CROSS_BRANCH_DEPTH_MAX}
85
+ step={1}
86
+ value={settings.depth}
87
+ onChange={onDepthChange}
88
+ >
89
+ <SliderTrack bg="whiteAlpha.200">
90
+ <SliderFilledTrack bg="var(--accent)" />
91
+ </SliderTrack>
92
+ <SliderThumb boxSize={4} />
93
+ </Slider>
94
+ <HStack justify="space-between" mt={1}>
95
+ <Text fontSize="10px" color="gray.500">Near</Text>
96
+ <Text fontSize="10px" color="gray.500">All</Text>
97
+ </HStack>
98
+ </Box>
99
+ </VStack>
100
+ </PopoverBody>
101
+ </PopoverContent>
102
+ </Portal>
103
+ </Popover>
104
+ )
105
+ }