@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,135 @@
1
+ import { Box, Divider, Flex, HStack, Tag, Text, VStack } from '@chakra-ui/react'
2
+ import { TYPE_COLORS, type PlacedElement } from '../types'
3
+
4
+ interface Props {
5
+ data: PlacedElement & { hasChildLink?: boolean }
6
+ /** Bounding rect of the node element in screen (viewport) coordinates */
7
+ anchorRect: DOMRect
8
+ }
9
+
10
+ export default function NodeHoverCard({ data, anchorRect }: Props) {
11
+ const color = TYPE_COLORS[data.kind ?? ''] ?? 'gray'
12
+
13
+ // Position the card centred above the node using fixed coordinates so it
14
+ // escapes React Flow's stacking context and always renders on top.
15
+ const centreX = anchorRect.left + anchorRect.width / 2
16
+ const topY = anchorRect.top - 12 // 12px gap above the node
17
+
18
+ return (
19
+ <Box
20
+ position="fixed"
21
+ // Temporarily off-screen width to measure; real centering via transform
22
+ left={`${centreX}px`}
23
+ top={`${topY}px`}
24
+ transform="translate(-50%, -100%)"
25
+ zIndex={99999}
26
+ pointerEvents="none"
27
+ minW="220px"
28
+ maxW="300px"
29
+ // Soft Clay styles
30
+ bg="clay.bg"
31
+ border="1px solid rgba(255,255,255,0.08)"
32
+ rounded="xl"
33
+ boxShadow="clay-out"
34
+ px={4}
35
+ py={3}
36
+ // Fade-in animation
37
+ animation="nodeCardIn 0.15s ease-out"
38
+ sx={{
39
+ '@keyframes nodeCardIn': {
40
+ from: { opacity: 0, transform: 'translate(-50%, calc(-100% + 6px))' },
41
+ to: { opacity: 1, transform: 'translate(-50%, -100%)' },
42
+ },
43
+ }}
44
+ >
45
+ {/* Caret pointing down toward the node */}
46
+ <Box
47
+ position="absolute"
48
+ bottom="-6px"
49
+ left="50%"
50
+ transform="translateX(-50%)"
51
+ w={0}
52
+ h={0}
53
+ borderLeft="6px solid transparent"
54
+ borderRight="6px solid transparent"
55
+ borderTop="6px solid"
56
+ borderTopColor="clay.bg"
57
+ />
58
+
59
+ <VStack align="stretch" spacing={2}>
60
+ {/* Header: name + type badge */}
61
+ <Flex align="flex-start" justify="space-between" gap={2}>
62
+ <Text
63
+ fontSize="sm"
64
+ fontWeight="semibold"
65
+ color="gray.100"
66
+ lineHeight="tight"
67
+ >
68
+ {data.name}
69
+ </Text>
70
+ <Tag
71
+ size="sm"
72
+ colorScheme={color}
73
+ variant="subtle"
74
+ flexShrink={0}
75
+ fontSize="9px"
76
+ textTransform="uppercase"
77
+ letterSpacing="0.06em"
78
+ mt="1px"
79
+ >
80
+ {data.kind}
81
+ </Tag>
82
+ </Flex>
83
+
84
+ {/* Technology */}
85
+ {data.technology && (
86
+ <Text fontSize="xs" color="gray.500">
87
+ {data.technology}
88
+ </Text>
89
+ )}
90
+
91
+ {/* Description */}
92
+ {data.description && (
93
+ <>
94
+ <Divider borderColor="rgba(255,255,255,0.06)" />
95
+ <Text fontSize="xs" color="gray.400" noOfLines={4}>
96
+ {data.description}
97
+ </Text>
98
+ </>
99
+ )}
100
+
101
+ {/* Tags */}
102
+ {data.tags && data.tags.length > 0 && (
103
+ <HStack spacing={1} flexWrap="wrap">
104
+ {data.tags.map((tag) => (
105
+ <Tag
106
+ key={tag}
107
+ size="sm"
108
+ variant="outline"
109
+ colorScheme="gray"
110
+ fontSize="9px"
111
+ borderColor="rgba(255,255,255,0.1)"
112
+ color="gray.500"
113
+ >
114
+ {tag}
115
+ </Tag>
116
+ ))}
117
+ </HStack>
118
+ )}
119
+
120
+ {/* Child view hint */}
121
+ {data.hasChildLink && (
122
+ <>
123
+ <Divider borderColor="rgba(255,255,255,0.06)" />
124
+ <Flex align="center" gap={1.5}>
125
+ <Box w="5px" h="5px" rounded="full" bg="blue.400" opacity={0.8} />
126
+ <Text fontSize="9px" color="blue.400" letterSpacing="0.04em">
127
+ Has child view
128
+ </Text>
129
+ </Flex>
130
+ </>
131
+ )}
132
+ </VStack>
133
+ </Box>
134
+ )
135
+ }
@@ -0,0 +1,36 @@
1
+ import { CloseButton, Divider, HStack, Text } from '@chakra-ui/react'
2
+ import type { ReactNode } from 'react'
3
+
4
+ interface Props {
5
+ title: ReactNode
6
+ onClose?: () => void
7
+ hasCloseButton?: boolean
8
+ }
9
+
10
+ export default function PanelHeader({ title, onClose, hasCloseButton = true }: Props) {
11
+ return (
12
+ <>
13
+ <HStack
14
+ px={4}
15
+ pt={4}
16
+ pb={3}
17
+ justify="space-between"
18
+ flexShrink={0}
19
+ bgGradient="linear(to-b, whiteAlpha.50, transparent)"
20
+ >
21
+ <Text fontSize="xs" fontWeight="700" color="white" letterSpacing="0.02em" textTransform="uppercase">
22
+ {title}
23
+ </Text>
24
+ {hasCloseButton && onClose && (
25
+ <CloseButton
26
+ size="sm"
27
+ color="whiteAlpha.600"
28
+ _hover={{ color: 'white', bg: 'whiteAlpha.100' }}
29
+ onClick={onClose}
30
+ />
31
+ )}
32
+ </HStack>
33
+ <Divider borderColor="whiteAlpha.100" />
34
+ </>
35
+ )
36
+ }
@@ -0,0 +1,24 @@
1
+ import React from 'react'
2
+ import { Box } from '@chakra-ui/react'
3
+
4
+ export function KbdHint({ children }: { children: string }) {
5
+ return (
6
+ <Box
7
+ as="span"
8
+ display="inline-flex"
9
+ alignItems="center"
10
+ justifyContent="center"
11
+ px={1.5}
12
+ py={0.5}
13
+ bg="whiteAlpha.300"
14
+ rounded="sm"
15
+ fontSize="8px"
16
+ fontWeight="bold"
17
+ color="whiteAlpha.900"
18
+ flexShrink={0}
19
+ ml={2}
20
+ >
21
+ {children}
22
+ </Box>
23
+ )
24
+ }
@@ -0,0 +1,169 @@
1
+ import { memo } from 'react'
2
+ import { BaseEdge, EdgeLabelRenderer, useStore, type EdgeProps } from 'reactflow'
3
+ import { useEdgeLabelLayout } from './ViewEditorEdgeLabelLayout'
4
+
5
+ function getIntersectionPoint(
6
+ nx: number, ny: number, nw: number, nh: number,
7
+ tx: number, ty: number, tw: number, th: number,
8
+ ) {
9
+ const cx = nx + nw / 2
10
+ const cy = ny + nh / 2
11
+ const cx2 = tx + tw / 2
12
+ const cy2 = ty + th / 2
13
+ const dx = cx2 - cx
14
+ const dy = cy2 - cy
15
+ const hw = nw / 2
16
+ const hh = nh / 2
17
+
18
+ if (dx === 0 && dy === 0) return { x: cx, y: cy }
19
+
20
+ const tanTheta = Math.abs(dy / dx)
21
+ const boxRatio = hh / hw
22
+
23
+ if (tanTheta < boxRatio) {
24
+ return {
25
+ x: cx + Math.sign(dx) * hw,
26
+ y: cy + Math.sign(dx) * hw * (dy / dx),
27
+ }
28
+ }
29
+
30
+ return {
31
+ y: cy + Math.sign(dy) * hh,
32
+ x: cx + Math.sign(dy) * hh * (dx / dy),
33
+ }
34
+ }
35
+
36
+ function scaleContextNodeBox(node: {
37
+ positionAbsolute?: { x: number; y: number }
38
+ position: { x: number; y: number }
39
+ width?: number | null
40
+ height?: number | null
41
+ type?: string
42
+ } | undefined) {
43
+ let x = node?.positionAbsolute?.x ?? node?.position.x ?? 0
44
+ let y = node?.positionAbsolute?.y ?? node?.position.y ?? 0
45
+ let w = node?.width ?? 0
46
+ let h = node?.height ?? 0
47
+
48
+ if (node?.type === 'contextNeighborNode') {
49
+ const scale = 0.5
50
+ x += w * ((1 - scale) / 2)
51
+ y += h * ((1 - scale) / 2)
52
+ w *= scale
53
+ h *= scale
54
+ }
55
+
56
+ return { x, y, w, h }
57
+ }
58
+
59
+ function ProxyConnectorEdge({ id, source, target, selected, style }: EdgeProps) {
60
+ const sourceNode = useStore((s) => s.nodeInternals.get(source))
61
+ const targetNode = useStore((s) => s.nodeInternals.get(target))
62
+ const edge = useStore((s) => s.edges.find((candidate) => candidate.id === id))
63
+ const sourceBox = scaleContextNodeBox(sourceNode)
64
+ const targetBox = scaleContextNodeBox(targetNode)
65
+ const hasBoxes = !!(sourceBox.w && sourceBox.h && targetBox.w && targetBox.h)
66
+ const start = hasBoxes
67
+ ? getIntersectionPoint(sourceBox.x, sourceBox.y, sourceBox.w, sourceBox.h, targetBox.x, targetBox.y, targetBox.w, targetBox.h)
68
+ : { x: 0, y: 0 }
69
+ const end = hasBoxes
70
+ ? getIntersectionPoint(targetBox.x, targetBox.y, targetBox.w, targetBox.h, sourceBox.x, sourceBox.y, sourceBox.w, sourceBox.h)
71
+ : { x: 1, y: 0 }
72
+
73
+ let finalStart = start
74
+ let finalEnd = end
75
+
76
+ const { sourceGroupIndex, sourceGroupCount, targetGroupIndex, targetGroupCount } = (edge?.data as {
77
+ sourceGroupIndex?: number; sourceGroupCount?: number;
78
+ targetGroupIndex?: number; targetGroupCount?: number;
79
+ }) || {}
80
+
81
+ const dx = end.x - start.x
82
+ const dy = end.y - start.y
83
+ const len = Math.hypot(dx, dy)
84
+ const PADDING = 12
85
+
86
+ if (len > 0) {
87
+ const nx = -dy / len
88
+ const ny = dx / len
89
+
90
+ if (typeof sourceGroupIndex === 'number' && typeof sourceGroupCount === 'number' && sourceGroupCount > 1) {
91
+ const offset = (sourceGroupIndex - (sourceGroupCount - 1) / 2) * PADDING
92
+ finalStart = { x: start.x + nx * offset, y: start.y + ny * offset }
93
+ }
94
+
95
+ if (typeof targetGroupIndex === 'number' && typeof targetGroupCount === 'number' && targetGroupCount > 1) {
96
+ const offset = (targetGroupIndex - (targetGroupCount - 1) / 2) * PADDING
97
+ finalEnd = { x: end.x + nx * offset, y: end.y + ny * offset }
98
+ }
99
+ }
100
+
101
+
102
+ const labelLayout = useEdgeLabelLayout({
103
+ id,
104
+ preferredX: (finalStart.x + finalEnd.x) / 2,
105
+ preferredY: (finalStart.y + finalEnd.y) / 2,
106
+ width: 24,
107
+ height: 24,
108
+ dx: finalEnd.x - finalStart.x,
109
+ dy: finalEnd.y - finalStart.y,
110
+ alongLineOnly: true,
111
+ })
112
+
113
+ if (!sourceNode || !targetNode) return null
114
+ if (!sourceBox.w || !sourceBox.h || !targetBox.w || !targetBox.h) return null
115
+
116
+ const path = `M ${finalStart.x},${finalStart.y} L ${finalEnd.x},${finalEnd.y}`
117
+
118
+ const count = edge?.data && typeof (edge.data as { details?: { count?: number } }).details?.count === 'number'
119
+ ? (edge.data as { details: { count: number } }).details.count
120
+ : 1
121
+
122
+ return (
123
+ <>
124
+ <BaseEdge
125
+ id={id}
126
+ path={path}
127
+ style={{
128
+ ...style,
129
+ stroke: 'rgba(var(--accent-rgb), 0.8)',
130
+ strokeWidth: selected ? 1 : 1,
131
+ strokeDasharray: '1 4',
132
+ animation: 'none',
133
+ }}
134
+ />
135
+ <EdgeLabelRenderer>
136
+ <div
137
+ style={{
138
+ position: 'absolute',
139
+ transform: `translate(-50%, -50%) translate(${labelLayout.x}px, ${labelLayout.y}px)`,
140
+ pointerEvents: 'none',
141
+ opacity: style?.opacity,
142
+ zIndex: 2,
143
+ }}
144
+ >
145
+ <div
146
+ style={{
147
+ width: 24,
148
+ height: 24,
149
+ borderRadius: 999,
150
+ background: 'var(--bg-element)',
151
+ border: '1px dashed rgba(var(--accent-rgb), 0.8)',
152
+ color: 'white',
153
+ display: 'flex',
154
+ alignItems: 'center',
155
+ justifyContent: 'center',
156
+ fontSize: 11,
157
+ fontWeight: 600,
158
+ boxShadow: selected ? '0 0 0 1px rgba(255,255,255,0.2)' : 'none',
159
+ }}
160
+ >
161
+ {count}
162
+ </div>
163
+ </div>
164
+ </EdgeLabelRenderer>
165
+ </>
166
+ )
167
+ }
168
+
169
+ export default memo(ProxyConnectorEdge)
@@ -0,0 +1,130 @@
1
+ import { Box, Button, HStack, Icon, Text, VStack, Divider, Flex } from '@chakra-ui/react'
2
+ import { useNavigate } from 'react-router-dom'
3
+ import type { ProxyConnectorDetails } from '../crossBranch/types'
4
+ import SlidingPanel from './SlidingPanel'
5
+ import PanelHeader from './PanelHeader'
6
+ import { ChevronRightIcon, NavigationIcon } from './Icons'
7
+
8
+ interface Props {
9
+ isOpen: boolean
10
+ onClose: () => void
11
+ details: ProxyConnectorDetails | null
12
+ hasBackdrop?: boolean
13
+ }
14
+
15
+ export default function ProxyConnectorPanel({
16
+ isOpen,
17
+ onClose,
18
+ details,
19
+ hasBackdrop = true,
20
+ }: Props) {
21
+ const navigate = useNavigate()
22
+
23
+ return (
24
+ <SlidingPanel
25
+ isOpen={isOpen}
26
+ onClose={onClose}
27
+ panelKey="proxy-connector-panel"
28
+ width={{ base: 'calc(100vw - 24px)', md: '300px' }}
29
+ hasBackdrop={hasBackdrop}
30
+ >
31
+ <PanelHeader title="Relationships" onClose={onClose} />
32
+
33
+ <Box flex={1} overflowY="auto" px={4} py={4}>
34
+ {details ? (
35
+ <VStack align="stretch" spacing={6}>
36
+ {/* Header info */}
37
+ <Box>
38
+ <HStack spacing={2} mb={1}>
39
+ <Text color="white" fontSize="s" letterSpacing="-0.01em">
40
+ {details.sourceAnchorName}
41
+ </Text>
42
+ <Icon as={ChevronRightIcon} color="whiteAlpha.400" />
43
+ <Text color="white" fontSize="s" letterSpacing="-0.01em">
44
+ {details.targetAnchorName}
45
+ </Text>
46
+ </HStack>
47
+ <Text color="blue.300" fontSize="xs" fontWeight="600" textTransform="uppercase" letterSpacing="0.05em">
48
+ {details.label}
49
+ </Text>
50
+ </Box>
51
+
52
+ <Divider borderColor="whiteAlpha.100" />
53
+
54
+ <VStack align="stretch" spacing={4}>
55
+ <Text color="gray.500" fontSize="10px" fontWeight="800" letterSpacing="0.1em" textTransform="uppercase">
56
+ Underlying Connectors
57
+ </Text>
58
+
59
+ <VStack align="stretch" spacing={3}>
60
+ {details.connectors.map((leaf, idx) => (
61
+ <Box
62
+ key={`${leaf.connector.id}-${idx}`}
63
+ px={3}
64
+ py={3}
65
+ rounded="xl"
66
+ bg="whiteAlpha.50"
67
+ border="1px solid"
68
+ borderColor="whiteAlpha.100"
69
+ _hover={{ bg: 'whiteAlpha.100', borderColor: 'whiteAlpha.200' }}
70
+ transition="all 0.2s"
71
+ >
72
+ <VStack align="stretch" spacing={3}>
73
+ <Box>
74
+ <HStack spacing={2} mb={1}>
75
+ <Text color="white" fontSize="sm" fontWeight="semibold" isTruncated>
76
+ {leaf.source.actualElementName}
77
+ </Text>
78
+ <Icon as={ChevronRightIcon} color="whiteAlpha.400" />
79
+ <Text color="white" fontSize="sm" fontWeight="semibold" isTruncated>
80
+ {leaf.target.actualElementName}
81
+ </Text>
82
+ </HStack>
83
+
84
+ {(leaf.connector.label || leaf.connector.relationship) && (
85
+ <Text color="gray.400" fontSize="xs" fontStyle={!leaf.connector.label ? 'italic' : 'normal'}>
86
+ {leaf.connector.label || leaf.connector.relationship}
87
+ </Text>
88
+ )}
89
+ </Box>
90
+
91
+ {leaf.connector.description && (
92
+ <Text color="gray.500" fontSize="xs" lineHeight="tall" pb={1}>
93
+ {leaf.connector.description}
94
+ </Text>
95
+ )}
96
+
97
+ <Button
98
+ size="xs"
99
+ variant="clay"
100
+ colorScheme="blue"
101
+ color="blue.100"
102
+ leftIcon={<NavigationIcon size={12} />}
103
+ onClick={(e) => {
104
+ e.preventDefault();
105
+ e.stopPropagation();
106
+ navigate(`/views/${leaf.ownerViewId}`);
107
+ onClose();
108
+ }}
109
+ w="full"
110
+ justifyContent="flex-start"
111
+ h="28px"
112
+ fontSize="11px"
113
+ >
114
+ Open {leaf.ownerViewName}
115
+ </Button>
116
+ </VStack>
117
+ </Box>
118
+ ))}
119
+ </VStack>
120
+ </VStack>
121
+ </VStack>
122
+ ) : (
123
+ <Flex h="full" align="center" justify="center" direction="column" gap={3}>
124
+ <Text color="gray.500" fontSize="sm">Select a connector to inspect it.</Text>
125
+ </Flex>
126
+ )}
127
+ </Box>
128
+ </SlidingPanel>
129
+ )
130
+ }
@@ -0,0 +1,19 @@
1
+ import { type ComponentPropsWithoutRef } from 'react'
2
+ import { Background } from 'reactflow'
3
+ import { useStore } from 'reactflow'
4
+
5
+ /**
6
+ * Guards React Flow's Background against NaN zoom.
7
+ *
8
+ * In @reactflow/background, `scaledSize = patternSize * transform[2]` has no
9
+ * NaN guard (unlike scaledGap which uses `|| 1`). When d3-zoom produces a NaN
10
+ * transform during fitView on a zero-size container, scaledSize becomes NaN,
11
+ * propagating to patternOffset and the circle cx/cy attributes.
12
+ *
13
+ * This wrapper simply skips rendering until zoom is a positive finite number.
14
+ */
15
+ export function SafeBackground(props: ComponentPropsWithoutRef<typeof Background>) {
16
+ const [x, y, zoom] = useStore((s) => s.transform)
17
+ if (!isFinite(zoom) || zoom <= 0 || !isFinite(x) || !isFinite(y)) return null
18
+ return <Background {...props} />
19
+ }
@@ -0,0 +1,117 @@
1
+ import { Box, type BoxProps } from '@chakra-ui/react'
2
+ import { useCallback, useEffect, useRef, useState, type ReactNode, forwardRef, useImperativeHandle } from 'react'
3
+
4
+ interface ScrollIndicatorWrapperProps extends BoxProps {
5
+ children: ReactNode
6
+ scrollId?: string
7
+ }
8
+
9
+ const ScrollIndicatorWrapper = forwardRef<HTMLDivElement, ScrollIndicatorWrapperProps>(({
10
+ children,
11
+ scrollId,
12
+ onScroll,
13
+ ...props
14
+ }, ref) => {
15
+ const [scrollIndicators, setScrollIndicators] = useState({ showTop: false, showBottom: false })
16
+ const internalRef = useRef<HTMLDivElement>(null)
17
+
18
+ // Use the provided ref if any, otherwise use our internal ref
19
+ useImperativeHandle(ref, () => internalRef.current as HTMLDivElement)
20
+
21
+ const updateScrollIndicators = useCallback(() => {
22
+ const el = internalRef.current
23
+ if (!el) return
24
+
25
+ const { scrollTop, scrollHeight, clientHeight } = el
26
+ const isScrollable = scrollHeight > clientHeight
27
+ const isAtBottom = Math.abs(scrollHeight - clientHeight - scrollTop) < 1.5
28
+ const isAtTop = scrollTop < 1.5
29
+
30
+ const nextShowTop = isScrollable && !isAtTop
31
+ const nextShowBottom = isScrollable && !isAtBottom
32
+
33
+ setScrollIndicators((prev) => (
34
+ prev.showTop === nextShowTop && prev.showBottom === nextShowBottom
35
+ ? prev
36
+ : { showTop: nextShowTop, showBottom: nextShowBottom }
37
+ ))
38
+ }, [])
39
+
40
+ useEffect(() => {
41
+ const el = internalRef.current
42
+ if (!el) return
43
+
44
+ // Initial check
45
+ updateScrollIndicators()
46
+
47
+ // Observe size changes (e.g. content loading, filtering)
48
+ const observer = new ResizeObserver(updateScrollIndicators)
49
+ observer.observe(el)
50
+ if (el.firstElementChild) {
51
+ observer.observe(el.firstElementChild)
52
+ }
53
+
54
+ return () => observer.disconnect()
55
+ }, [updateScrollIndicators])
56
+
57
+ return (
58
+ <Box position="relative" flex={1} minH={0} overflow="hidden" display="flex" flexDir="column">
59
+ {/* Top Scroll indicator shadow */}
60
+ <Box
61
+ position="absolute"
62
+ top={0}
63
+ left={0}
64
+ right={0}
65
+ h="24px"
66
+ bgGradient="linear(to-b, rgba(0,0,0,0.1), transparent)"
67
+ pointerEvents="none"
68
+ transition="opacity 0.2s ease"
69
+ opacity={scrollIndicators.showTop ? 1 : 0}
70
+ zIndex={2}
71
+ />
72
+
73
+ <Box
74
+ ref={internalRef}
75
+ id={scrollId}
76
+ overflowY="auto"
77
+ minH={0}
78
+ onScroll={(e) => {
79
+ updateScrollIndicators()
80
+ onScroll?.(e)
81
+ }}
82
+ css={{
83
+ '&::-webkit-scrollbar': { width: '4px' },
84
+ '&::-webkit-scrollbar-track': { background: 'transparent' },
85
+ '&::-webkit-scrollbar-thumb': {
86
+ background: 'rgba(255,255,255,0.1)',
87
+ borderRadius: '10px',
88
+ },
89
+ '&::-webkit-scrollbar-thumb:hover': {
90
+ background: 'rgba(255,255,255,0.2)',
91
+ },
92
+ }}
93
+ {...props}
94
+ >
95
+ {children}
96
+ </Box>
97
+
98
+ {/* Bottom Scroll indicator shadow */}
99
+ <Box
100
+ position="absolute"
101
+ bottom={0}
102
+ left={0}
103
+ right={0}
104
+ h="40px"
105
+ bgGradient="linear(to-t, rgba(0,0,0,0.15), transparent)"
106
+ pointerEvents="none"
107
+ transition="opacity 0.2s ease"
108
+ opacity={scrollIndicators.showBottom ? 1 : 0}
109
+ zIndex={2}
110
+ />
111
+ </Box>
112
+ )
113
+ })
114
+
115
+ ScrollIndicatorWrapper.displayName = 'ScrollIndicatorWrapper'
116
+
117
+ export default ScrollIndicatorWrapper