@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,330 @@
1
+ /**
2
+ * VS Code variant of GitSourceLinker.
3
+ * Uses the postMessage bridge to query workspace files and LSP symbols
4
+ * instead of fetching from GitHub.
5
+ */
6
+ import { useState, useEffect, useCallback, useRef } from 'react'
7
+ import {
8
+ Box,
9
+ Button,
10
+ HStack,
11
+ Input,
12
+ InputGroup,
13
+ InputRightElement,
14
+ Spinner,
15
+ Text,
16
+ VStack,
17
+ } from '@chakra-ui/react'
18
+ import { CheckIcon, SearchIcon } from '@chakra-ui/icons'
19
+ import { vscodeBridge } from '../lib/vscodeBridge'
20
+ import type { WorkspaceSymbol, ExtensionToWebviewMessage } from '../types/vscode-messages'
21
+ import type { LibraryElement } from '../types'
22
+
23
+ interface Props {
24
+ element: LibraryElement
25
+ isReadOnly: boolean
26
+ onUpdate: (updates: Partial<LibraryElement>) => void
27
+ }
28
+
29
+ function randomId() {
30
+ return Math.random().toString(36).slice(2, 10)
31
+ }
32
+
33
+ function buildSymbolAnchor(sym: Pick<WorkspaceSymbol, 'filePath' | 'name' | 'kind'>): string {
34
+ return `${sym.filePath}#${JSON.stringify({ name: sym.name, type: sym.kind.toLowerCase() })}`
35
+ }
36
+
37
+ function parseSourceAnchor(link: string): { name?: string; type?: string; startLine?: number } {
38
+ const hashIdx = link.indexOf('#')
39
+ if (hashIdx < 0) {
40
+ return {}
41
+ }
42
+
43
+ try {
44
+ const parsed = JSON.parse(link.slice(hashIdx + 1))
45
+ return {
46
+ name: typeof parsed.name === 'string' ? parsed.name : undefined,
47
+ type: typeof parsed.type === 'string' ? parsed.type : undefined,
48
+ startLine: typeof parsed.startLine === 'number' ? parsed.startLine : undefined,
49
+ }
50
+ } catch {
51
+ return {}
52
+ }
53
+ }
54
+
55
+ export default function LocalSourceLinker({ element, isReadOnly, onUpdate }: Props) {
56
+ const [fileQuery, setFileQuery] = useState('')
57
+ const [files, setFiles] = useState<string[]>([])
58
+ const [filesLoading, setFilesLoading] = useState(false)
59
+ const [selectedFile, setSelectedFile] = useState<string | null>(null)
60
+ const [symbols, setSymbols] = useState<WorkspaceSymbol[]>([])
61
+ const [symbolsLoading, setSymbolsLoading] = useState(false)
62
+
63
+ const pendingRef = useRef<Map<string, (data: unknown) => void>>(new Map())
64
+
65
+ // Register message listener once
66
+ useEffect(() => {
67
+ const unsubscribe = vscodeBridge.onMessage((msg: ExtensionToWebviewMessage) => {
68
+ if (msg.type === 'workspace-files') {
69
+ const resolve = pendingRef.current.get(msg.requestId)
70
+ if (resolve) {
71
+ pendingRef.current.delete(msg.requestId)
72
+ resolve(msg.files)
73
+ }
74
+ } else if (msg.type === 'workspace-symbols') {
75
+ const resolve = pendingRef.current.get(msg.requestId)
76
+ if (resolve) {
77
+ pendingRef.current.delete(msg.requestId)
78
+ resolve(msg.symbols)
79
+ }
80
+ }
81
+ })
82
+ return unsubscribe
83
+ }, [])
84
+
85
+ // Pre-populate from existing file_path
86
+ useEffect(() => {
87
+ if (!element.file_path) return
88
+ const hashIdx = element.file_path.indexOf('#')
89
+ const fp = hashIdx >= 0 ? element.file_path.slice(0, hashIdx) : element.file_path
90
+ if (fp) {
91
+ setSelectedFile(fp)
92
+ setFileQuery(fp)
93
+ }
94
+ }, [element.file_path])
95
+
96
+ const requestFiles = useCallback(
97
+ (query: string) => {
98
+ if (!query.trim()) {
99
+ setFiles([])
100
+ return
101
+ }
102
+ const requestId = randomId()
103
+ setFilesLoading(true)
104
+ const pattern = query.includes('*') ? query : `**/*${query}*`
105
+ const promise = new Promise<string[]>((resolve) => {
106
+ pendingRef.current.set(requestId, (data) => resolve(data as string[]))
107
+ })
108
+ vscodeBridge.postMessage({ type: 'request-workspace-files', requestId, pattern })
109
+ void promise.then((result) => {
110
+ setFiles(result.slice(0, 50))
111
+ setFilesLoading(false)
112
+ })
113
+ },
114
+ [],
115
+ )
116
+
117
+ const requestSymbols = useCallback((filePath: string) => {
118
+ const requestId = randomId()
119
+ setSymbolsLoading(true)
120
+ const promise = new Promise<WorkspaceSymbol[]>((resolve) => {
121
+ pendingRef.current.set(requestId, (data) => resolve(data as WorkspaceSymbol[]))
122
+ })
123
+ vscodeBridge.postMessage({ type: 'request-symbol-list-for-file', requestId, filePath })
124
+ void promise.then((result) => {
125
+ setSymbols(result)
126
+ setSymbolsLoading(false)
127
+ })
128
+ }, [])
129
+
130
+ // Debounce file search
131
+ useEffect(() => {
132
+ if (isReadOnly) return
133
+ const timer = setTimeout(() => requestFiles(fileQuery), 300)
134
+ return () => clearTimeout(timer)
135
+ }, [fileQuery, isReadOnly, requestFiles])
136
+
137
+ const handleSelectFile = (fp: string) => {
138
+ setSelectedFile(fp)
139
+ setFileQuery(fp)
140
+ setFiles([])
141
+ setSymbols([])
142
+ requestSymbols(fp)
143
+ }
144
+
145
+ const handleSelectSymbol = (sym: WorkspaceSymbol) => {
146
+ onUpdate({ file_path: buildSymbolAnchor(sym) })
147
+ }
148
+
149
+ const handleClear = () => {
150
+ setSelectedFile(null)
151
+ setFileQuery('')
152
+ setFiles([])
153
+ setSymbols([])
154
+ onUpdate({ file_path: '' })
155
+ }
156
+
157
+ const currentLink = element.file_path || ''
158
+ const hasLink = !!currentLink
159
+
160
+ if (isReadOnly && hasLink) {
161
+ const hashIdx = currentLink.indexOf('#')
162
+ const filePart = hashIdx >= 0 ? currentLink.slice(0, hashIdx) : currentLink
163
+ const anchor = parseSourceAnchor(currentLink)
164
+ const symbolName = anchor.name ?? ''
165
+ return (
166
+ <Box>
167
+ <HStack spacing={1} mb={1}>
168
+ <CheckIcon color="green.400" boxSize={3} />
169
+ <Text fontSize="11px" color="gray.300" fontWeight="medium" isTruncated>
170
+ {symbolName ? `${symbolName} ${filePart}` : filePart}
171
+ </Text>
172
+ </HStack>
173
+ <Button
174
+ size="xs"
175
+ variant="ghost"
176
+ color="blue.400"
177
+ px={0}
178
+ h="auto"
179
+ onClick={() => {
180
+ if (!element.file_path) return
181
+ const hashIdx2 = element.file_path.indexOf('#')
182
+ const fp = hashIdx2 >= 0 ? element.file_path.slice(0, hashIdx2) : element.file_path
183
+ const parsedAnchor = parseSourceAnchor(element.file_path)
184
+ vscodeBridge.postMessage({
185
+ type: 'open-file',
186
+ filePath: fp,
187
+ startLine: parsedAnchor.startLine,
188
+ symbolName: parsedAnchor.name,
189
+ symbolKind: parsedAnchor.type,
190
+ })
191
+ }}
192
+ >
193
+ Open in Editor
194
+ </Button>
195
+ </Box>
196
+ )
197
+ }
198
+
199
+ return (
200
+ <VStack align="stretch" spacing={2}>
201
+ <Box>
202
+ <Text fontSize="10px" color="gray.500" mb={1} textTransform="uppercase" letterSpacing="wider">
203
+ Workspace File
204
+ </Text>
205
+ <InputGroup size="sm">
206
+ <Input
207
+ value={fileQuery}
208
+ onChange={(e) => setFileQuery(e.target.value)}
209
+ placeholder="Search files…"
210
+ bg="whiteAlpha.50"
211
+ border="1px solid"
212
+ borderColor="whiteAlpha.100"
213
+ fontSize="12px"
214
+ isDisabled={isReadOnly}
215
+ />
216
+ <InputRightElement>
217
+ {filesLoading ? <Spinner size="xs" /> : <SearchIcon boxSize={3} color="gray.500" />}
218
+ </InputRightElement>
219
+ </InputGroup>
220
+
221
+ {files.length > 0 && (
222
+ <Box
223
+ mt={1}
224
+ maxH="140px"
225
+ overflowY="auto"
226
+ bg="#0d1117"
227
+ border="1px solid"
228
+ borderColor="whiteAlpha.100"
229
+ rounded="md"
230
+ className="custom-scrollbar"
231
+ >
232
+ {files.map((f) => (
233
+ <Box
234
+ key={f}
235
+ px={2}
236
+ py={1}
237
+ fontSize="11px"
238
+ color="gray.300"
239
+ cursor="pointer"
240
+ _hover={{ bg: 'whiteAlpha.100', color: 'white' }}
241
+ onClick={() => handleSelectFile(f)}
242
+ isTruncated
243
+ >
244
+ {f}
245
+ </Box>
246
+ ))}
247
+ </Box>
248
+ )}
249
+ </Box>
250
+
251
+ {selectedFile && (
252
+ <Box>
253
+ <Text fontSize="10px" color="gray.500" mb={1} textTransform="uppercase" letterSpacing="wider">
254
+ Symbol
255
+ </Text>
256
+ {symbolsLoading ? (
257
+ <HStack spacing={2} py={1}>
258
+ <Spinner size="xs" />
259
+ <Text fontSize="11px" color="gray.500">Loading symbols…</Text>
260
+ </HStack>
261
+ ) : symbols.length === 0 ? (
262
+ <Text fontSize="11px" color="gray.600">No symbols found</Text>
263
+ ) : (
264
+ <Box
265
+ maxH="140px"
266
+ overflowY="auto"
267
+ bg="#0d1117"
268
+ border="1px solid"
269
+ borderColor="whiteAlpha.100"
270
+ rounded="md"
271
+ className="custom-scrollbar"
272
+ >
273
+ {symbols.map((sym) => {
274
+ const anchor = buildSymbolAnchor(sym)
275
+ const isLinked = currentLink === anchor
276
+ return (
277
+ <HStack
278
+ key={`${sym.name}-${sym.startLine}`}
279
+ px={2}
280
+ py={1}
281
+ spacing={2}
282
+ cursor="pointer"
283
+ bg={isLinked ? 'rgba(49,130,206,0.2)' : 'transparent'}
284
+ _hover={{ bg: isLinked ? 'rgba(49,130,206,0.3)' : 'whiteAlpha.100' }}
285
+ onClick={() => handleSelectSymbol(sym)}
286
+ >
287
+ {isLinked && <CheckIcon color="blue.400" boxSize={2.5} flexShrink={0} />}
288
+ <Text fontSize="11px" color="gray.300" isTruncated>{sym.name}</Text>
289
+ <Text fontSize="10px" color="gray.600" flexShrink={0}>{sym.kind}</Text>
290
+ </HStack>
291
+ )
292
+ })}
293
+ </Box>
294
+ )}
295
+ </Box>
296
+ )}
297
+
298
+ {hasLink && (
299
+ <HStack spacing={2}>
300
+ <Button
301
+ size="xs"
302
+ variant="ghost"
303
+ color="blue.400"
304
+ px={0}
305
+ h="auto"
306
+ onClick={() => {
307
+ const hashIdx2 = currentLink.indexOf('#')
308
+ const fp = hashIdx2 >= 0 ? currentLink.slice(0, hashIdx2) : currentLink
309
+ const parsedAnchor = parseSourceAnchor(currentLink)
310
+ vscodeBridge.postMessage({
311
+ type: 'open-file',
312
+ filePath: fp,
313
+ startLine: parsedAnchor.startLine,
314
+ symbolName: parsedAnchor.name,
315
+ symbolKind: parsedAnchor.type,
316
+ })
317
+ }}
318
+ >
319
+ Open in Editor
320
+ </Button>
321
+ {!isReadOnly && (
322
+ <Button size="xs" variant="ghost" color="red.400" px={0} h="auto" onClick={handleClear}>
323
+ Clear
324
+ </Button>
325
+ )}
326
+ </HStack>
327
+ )}
328
+ </VStack>
329
+ )
330
+ }
@@ -0,0 +1,78 @@
1
+ import { Box, HStack, Text } from '@chakra-ui/react'
2
+ import { keyframes } from '@emotion/react'
3
+ import { ZoomInIcon } from './Icons'
4
+
5
+ interface Props {
6
+ isVisible: boolean
7
+ }
8
+
9
+ const pulseGlow = keyframes`
10
+ 0% { box-shadow: 0 0 0 0 rgba(var(--accent-rgb), 0.4); }
11
+ 70% { box-shadow: 0 0 0 20px rgba(var(--accent-rgb), 0); }
12
+ 100% { box-shadow: 0 0 0 0 rgba(var(--accent-rgb), 0); }
13
+ `
14
+
15
+ export default function MiniZoomOnboarding({ isVisible }: Props) {
16
+ return (
17
+ <Box
18
+ position="absolute"
19
+ top={{ base: '30px', md: '50px' }}
20
+ left="50%"
21
+ transform={isVisible ? 'translateX(-50%) translateY(0) scale(1)' : 'translateX(-50%) translateY(-20px) scale(0.95)'}
22
+ zIndex={100}
23
+ opacity={isVisible ? 1 : 0}
24
+ transition="all 0.8s cubic-bezier(0.16, 1, 0.3, 1)"
25
+ pointerEvents="none"
26
+ >
27
+ <Box
28
+ className="glass"
29
+ px={6}
30
+ py={4}
31
+ borderRadius="12px"
32
+ animation={isVisible ? `${pulseGlow} 3s infinite` : 'none'}
33
+ position="relative"
34
+ overflow="hidden"
35
+ border="1.5px solid rgba(var(--accent-rgb), 0.3)"
36
+ >
37
+ {/* Subtle accent bar for visual continuity */}
38
+ <Box
39
+ position="absolute"
40
+ top={0}
41
+ left={0}
42
+ w="4px"
43
+ h="100%"
44
+ bg="var(--accent)"
45
+ opacity={0.8}
46
+ />
47
+
48
+ <HStack spacing={5} pl={3}>
49
+ <Box color="var(--accent)">
50
+ <ZoomInIcon size={24} />
51
+ </Box>
52
+ <Box>
53
+ <Text
54
+ fontSize="10px"
55
+ color="var(--accent)"
56
+ fontWeight="900"
57
+ letterSpacing="0.15em"
58
+ textTransform="uppercase"
59
+ mb={0.5}
60
+ opacity={0.9}
61
+ >
62
+ Pro Tip
63
+ </Text>
64
+ <Text
65
+ fontSize="15px"
66
+ color="white"
67
+ fontWeight="600"
68
+ whiteSpace="nowrap"
69
+ letterSpacing="-0.01em"
70
+ >
71
+ Scroll or pinch to dive into nodes
72
+ </Text>
73
+ </Box>
74
+ </HStack>
75
+ </Box>
76
+ </Box>
77
+ )
78
+ }
@@ -0,0 +1,24 @@
1
+ import { Breadcrumb, BreadcrumbItem, BreadcrumbLink } from '@chakra-ui/react'
2
+ import { useNavigate } from 'react-router-dom'
3
+ import type { ViewTreeNode } from '../types'
4
+
5
+ interface Props {
6
+ diagram: ViewTreeNode
7
+ }
8
+
9
+ export default function NavBreadcrumb({ diagram }: Props) {
10
+ const navigate = useNavigate()
11
+
12
+ return (
13
+ <Breadcrumb fontSize="sm" color="gray.500" sx={{ '& a': { color: 'gray.400', _hover: { color: 'gray.200' } } }}>
14
+ <BreadcrumbItem>
15
+ <BreadcrumbLink onClick={() => navigate('/views')} cursor="pointer">
16
+ Diagrams
17
+ </BreadcrumbLink>
18
+ </BreadcrumbItem>
19
+ <BreadcrumbItem isCurrentPage>
20
+ <BreadcrumbLink>{diagram.name}</BreadcrumbLink>
21
+ </BreadcrumbItem>
22
+ </Breadcrumb>
23
+ )
24
+ }
@@ -0,0 +1,89 @@
1
+ import { Box, Flex, Text, FlexProps } from '@chakra-ui/react'
2
+ import { TYPE_COLORS } from '../types'
3
+
4
+ interface ElementBodyProps extends FlexProps {
5
+ name: string
6
+ type: string
7
+ technology?: string
8
+ logoUrl?: string
9
+ nameSize?: string
10
+ typeSize?: string
11
+ techSize?: string
12
+ }
13
+
14
+ export const ElementBody = ({
15
+ name,
16
+ type,
17
+ technology,
18
+ logoUrl,
19
+ nameSize = 'sm',
20
+ typeSize = '2xs',
21
+ techSize = 'xs',
22
+ children,
23
+ ...props
24
+ }: ElementBodyProps) => {
25
+ const color = TYPE_COLORS[type] ?? 'gray'
26
+
27
+ const hasLogo = !!logoUrl
28
+
29
+ return (
30
+ <Flex
31
+ flexDir={hasLogo ? 'row' : 'column'}
32
+ align={hasLogo ? 'center' : 'center'}
33
+ justify="center"
34
+ p={3}
35
+ gap={hasLogo ? 2 : 1}
36
+ {...props}
37
+ >
38
+ {hasLogo && (
39
+ <Flex
40
+ w="38px"
41
+ h="38px"
42
+ align="center"
43
+ justify="center"
44
+ flexShrink={0}
45
+ >
46
+ <Box
47
+ as="img"
48
+ src={logoUrl}
49
+ alt="technology icon"
50
+ maxW="100%"
51
+ maxH="100%"
52
+ objectFit="contain"
53
+ />
54
+ </Flex>
55
+ )}
56
+
57
+ <Flex flexDir="column" align={hasLogo ? 'flex-start' : 'center'} justify="center" flex={1} minW={0}>
58
+ <Text
59
+ fontWeight="semibold"
60
+ fontSize={nameSize}
61
+ noOfLines={2}
62
+ textAlign={hasLogo ? 'left' : 'center'}
63
+ color="gray.100"
64
+ lineHeight={1.15}
65
+ >
66
+ {name}
67
+ </Text>
68
+ {!!type && (
69
+ <Text
70
+ fontSize={typeSize}
71
+ color={`${color}.400`}
72
+ textAlign={hasLogo ? 'left' : 'center'}
73
+ textTransform="uppercase"
74
+ letterSpacing="0.06em"
75
+ lineHeight={1.1}
76
+ >
77
+ {type}
78
+ </Text>
79
+ )}
80
+ {technology && (
81
+ <Text fontSize={techSize} color="gray.500" textAlign={hasLogo ? 'left' : 'center'} lineHeight={1.1}>
82
+ [{technology}]
83
+ </Text>
84
+ )}
85
+ {children}
86
+ </Flex>
87
+ </Flex>
88
+ )
89
+ }
@@ -0,0 +1,58 @@
1
+ import { memo } from 'react'
2
+ import { Box, BoxProps, forwardRef } from '@chakra-ui/react'
3
+ import { useAccentColor } from '../context/ThemeContext'
4
+ import { hexToRgba } from '../constants/colors'
5
+
6
+ export interface ElementContainerProps extends BoxProps {
7
+ isSelected?: boolean
8
+ isSource?: boolean
9
+ isTarget?: boolean
10
+ isConnectorHighlighted?: boolean
11
+ }
12
+
13
+ export const ElementContainer = memo(forwardRef<ElementContainerProps, 'div'>(({
14
+ isSelected,
15
+ isSource,
16
+ isTarget,
17
+ isConnectorHighlighted,
18
+ children,
19
+ ...props
20
+ }, ref) => {
21
+ const { accent } = useAccentColor()
22
+
23
+ const borderColor = isSource
24
+ ? accent
25
+ : isTarget
26
+ ? 'teal.300'
27
+ : isSelected || isConnectorHighlighted
28
+ ? accent
29
+ : 'gray.600'
30
+
31
+ const selectionShadow = `0 0 0 3px ${hexToRgba(accent, 0.35)}, 0 10px 36px rgba(0,0,0,0.55), 0 3px 10px rgba(0,0,0,0.4)`
32
+ const sourceShadow = `0 0 0 3px ${hexToRgba(accent, 0.55)}, 0 0 24px ${hexToRgba(accent, 0.25)}`
33
+ const edgeHighlightShadow = `0 0 0 2px ${hexToRgba(accent, 0.2)}, 0 8px 32px rgba(0,0,0,0.55), 0 2px 8px rgba(0,0,0,0.35)`
34
+ const restingShadow = '0 8px 32px rgba(0,0,0,0.55), 0 2px 8px rgba(0,0,0,0.35)'
35
+ const hoverShadow = isSource ? sourceShadow : isSelected ? selectionShadow : '0 12px 40px rgba(0,0,0,0.6), 0 4px 12px rgba(0,0,0,0.4)'
36
+
37
+ const boxShadow = isSource ? sourceShadow : isSelected ? selectionShadow : isConnectorHighlighted ? edgeHighlightShadow : restingShadow
38
+
39
+ return (
40
+ <Box
41
+ ref={ref}
42
+ bg="var(--bg-element)"
43
+ borderColor={borderColor}
44
+ borderWidth="1px"
45
+ rounded="lg"
46
+ boxShadow={boxShadow}
47
+ transition="all var(--chakra-transitions-duration-fast) var(--chakra-transitions-easing-pop)"
48
+ position="relative"
49
+ _hover={{
50
+ borderColor: isSource ? accent : isTarget ? 'teal.200' : accent,
51
+ boxShadow: hoverShadow,
52
+ }}
53
+ {...props}
54
+ >
55
+ {children}
56
+ </Box>
57
+ )
58
+ }))