@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,22 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!--
3
+ Wordmark asset. Requires Josefin Sans to be loaded on the page (or in the browser cache).
4
+ Use inline or as a React component - NOT as <img src> (SVGs served as images cannot
5
+ load external fonts, and this file intentionally omits the embedded font blob).
6
+ -->
7
+ <svg xmlns="http://www.w3.org/2000/svg" width="200" height="44" viewBox="0 0 200 44">
8
+ <defs>
9
+ <linearGradient id="tg" x1="0" y1="0" x2="1" y2="0">
10
+ <stop offset="0" stop-color="#90cdf4" />
11
+ <stop offset="0.5" stop-color="#63b3ed" />
12
+ <stop offset="1" stop-color="#4299e1" />
13
+ </linearGradient>
14
+ </defs>
15
+ <text
16
+ x="0" y="34"
17
+ font-family="'Josefin Sans', sans-serif"
18
+ font-weight="600"
19
+ font-size="38"
20
+ fill="url(#tg)"
21
+ letter-spacing="-0.5">tlDiagram</text>
22
+ </svg>
@@ -0,0 +1,35 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
3
+ <defs>
4
+ <clipPath id="roundsq">
5
+ <rect width="1024" height="1024" rx="224" ry="224" />
6
+ </clipPath>
7
+ <filter id="dshadow" x="-50%" y="-50%" width="200%" height="200%">
8
+ <feGaussianBlur in="SourceAlpha" stdDeviation="32" result="blur1" />
9
+ <feFlood flood-color="#63b3ed" flood-opacity="0.3" result="c1" />
10
+ <feComposite in="c1" in2="blur1" operator="in" result="glow1" />
11
+ <feGaussianBlur in="SourceAlpha" stdDeviation="10" result="blur2" />
12
+ <feFlood flood-color="#90cdf4" flood-opacity="0.5" result="c2" />
13
+ <feComposite in="c2" in2="blur2" operator="in" result="glow2" />
14
+ <feDropShadow dx="0" dy="20" stdDeviation="20" flood-color="#000" flood-opacity="0.9"
15
+ result="drop" />
16
+ <feMerge>
17
+ <feMergeNode in="glow1" />
18
+ <feMergeNode in="glow2" />
19
+ <feMergeNode in="SourceGraphic" />
20
+ </feMerge>
21
+ </filter>
22
+ <linearGradient id="dgrad" x1="0" y1="0" x2="0.6" y2="1">
23
+ <stop offset="0" stop-color="#90cdf4" />
24
+ <stop offset="0.45" stop-color="#63b3ed" />
25
+ <stop offset="1" stop-color="#2b6cb0" />
26
+ </linearGradient>
27
+ </defs>
28
+ <g clip-path="url(#roundsq)" transform="matrix(1, 0, 0, 1, -1.4210854715202004e-14, 0)">
29
+ <g filter="url(#dshadow)" transform="translate(78, 118) scale(0.8)" fill-rule="evenodd">
30
+ <path
31
+ d="M201 18s169-39 340-2c171 38 342 152 342 457S713 902 544 949c-170 48-344 19-344 19l1-566h302V217H201z M455 715c-67 83-122 141-62 48q23-35 33-62h-4a78 78 0 1 1 78-78q0 24-12 44-11 21-33 48"
32
+ fill="url(#dgrad)" />
33
+ </g>
34
+ </g>
35
+ </svg>
@@ -0,0 +1,85 @@
1
+ /**
2
+ * VS Code variant of CodePreviewPanel.
3
+ * Instead of fetching code from GitHub and rendering it inline, we show a
4
+ * single "Open in Editor" button that navigates VS Code to the linked file.
5
+ * CodeMirror + tree-sitter WASM are excluded from the VS Code bundle entirely.
6
+ */
7
+ import { Box, Button, HStack, Text } from '@chakra-ui/react'
8
+ import { ExternalLinkIcon } from '@chakra-ui/icons'
9
+ import { vscodeBridge } from '../lib/vscodeBridge'
10
+ import type { PlacedElement } from '../types'
11
+
12
+ interface Props {
13
+ element: PlacedElement | null
14
+ onClose: () => void
15
+ }
16
+
17
+ export default function CodePreviewPanel({ element, onClose }: Props) {
18
+ if (!element?.file_path) return null
19
+
20
+ const filePath = element.file_path
21
+ const hashIdx = filePath.indexOf('#')
22
+ const fp = hashIdx >= 0 ? filePath.slice(0, hashIdx) : filePath
23
+ let symbolName: string | undefined
24
+ let symbolKind: string | undefined
25
+ let startLine: number | undefined
26
+ if (hashIdx >= 0) {
27
+ try {
28
+ const p = JSON.parse(filePath.slice(hashIdx + 1))
29
+ symbolName = typeof p.name === 'string' ? p.name : undefined
30
+ symbolKind = typeof p.type === 'string' ? p.type : undefined
31
+ startLine = typeof p.startLine === 'number' ? p.startLine : undefined
32
+ } catch { /* intentionally empty */ }
33
+ }
34
+
35
+ const handleOpen = () => {
36
+ vscodeBridge.postMessage({ type: 'open-file', filePath: fp, startLine, symbolName, symbolKind })
37
+ }
38
+
39
+ return (
40
+ <Box
41
+ position="absolute"
42
+ left={4}
43
+ top={4}
44
+ bottom={4}
45
+ w="280px"
46
+ bg="var(--bg-panel)"
47
+ border="1px solid"
48
+ borderColor="whiteAlpha.100"
49
+ rounded="xl"
50
+ display="flex"
51
+ flexDirection="column"
52
+ zIndex={20}
53
+ overflow="hidden"
54
+ >
55
+ <HStack px={3} py={2} borderBottom="1px solid" borderColor="whiteAlpha.100" justify="space-between">
56
+ <Text fontSize="12px" fontWeight="semibold" color="gray.200" isTruncated>
57
+ Source Link
58
+ </Text>
59
+ <Button variant="ghost" size="xs" onClick={onClose} px={1} color="gray.500">
60
+
61
+ </Button>
62
+ </HStack>
63
+
64
+ <Box px={3} py={3} flex={1} display="flex" flexDirection="column" gap={2}>
65
+ {symbolName && (
66
+ <Text fontSize="12px" color="gray.300" fontWeight="medium">{symbolName}</Text>
67
+ )}
68
+ <Text fontSize="11px" color="gray.500" isTruncated title={fp}>{fp}</Text>
69
+ {typeof startLine === 'number' && (
70
+ <Text fontSize="10px" color="gray.600">Line {startLine}</Text>
71
+ )}
72
+ <Button
73
+ mt={2}
74
+ size="sm"
75
+ variant="outline"
76
+ colorScheme="blue"
77
+ leftIcon={<ExternalLinkIcon />}
78
+ onClick={handleOpen}
79
+ >
80
+ Open in Editor
81
+ </Button>
82
+ </Box>
83
+ </Box>
84
+ )
85
+ }
@@ -0,0 +1,384 @@
1
+ import { useEffect, useState, useRef } from 'react'
2
+ import type { SVGProps } from 'react'
3
+ import { Box, Button, CloseButton, HStack, Icon, Spinner, Text, Tooltip, VStack } from '@chakra-ui/react'
4
+ import { ExternalLinkIcon } from '@chakra-ui/icons'
5
+
6
+ const GithubIcon = (props: SVGProps<SVGSVGElement>) => (
7
+ <svg
8
+ viewBox="0 0 24 24"
9
+ fill="currentColor"
10
+ width="16px"
11
+ height="16px"
12
+ {...props}
13
+ >
14
+ <path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.765 1.11.765 2.235 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" />
15
+ </svg>
16
+ )
17
+
18
+ const customCodeTheme = EditorView.theme({
19
+ "&": {
20
+ backgroundColor: "transparent !important",
21
+ },
22
+ ".cm-gutters": {
23
+ backgroundColor: "transparent !important",
24
+ borderRight: "1px solid var(--chakra-colors-whiteAlpha-100)",
25
+ color: "var(--chakra-colors-whiteAlpha-300)",
26
+ },
27
+ ".cm-activeLine": {
28
+ backgroundColor: "var(--chakra-colors-whiteAlpha-100) !important",
29
+ },
30
+ ".cm-activeLineGutter": {
31
+ backgroundColor: "var(--chakra-colors-whiteAlpha-200) !important",
32
+ },
33
+ ".cm-content": {
34
+ caretColor: "var(--chakra-colors-blue-400)",
35
+ }
36
+ }, { dark: true })
37
+ import CodeMirror, { ReactCodeMirrorRef } from '@uiw/react-codemirror'
38
+ import { EditorView } from '@codemirror/view'
39
+ import { oneDark } from '@codemirror/theme-one-dark'
40
+ import { javascript } from '@codemirror/lang-javascript'
41
+ import { python } from '@codemirror/lang-python'
42
+ import { cpp } from '@codemirror/lang-cpp'
43
+ import { java } from '@codemirror/lang-java'
44
+ import { rust } from '@codemirror/lang-rust'
45
+
46
+ import SlidingPanel from './SlidingPanel'
47
+ import { findSymbolByName, getParser, detectLanguage, type SupportedLanguage } from '../utils/treesitter'
48
+ import type { PlacedElement } from '../types'
49
+
50
+ interface Props {
51
+ isOpen: boolean
52
+ onClose: () => void
53
+ element: PlacedElement | null
54
+ hasBackdrop?: boolean
55
+ }
56
+
57
+ function parseAnchor(anchorStr: string):
58
+ | { kind: 'symbol'; name: string; type: string }
59
+ | { kind: 'lines'; startLine: number; endLine: number }
60
+ | { kind: 'none' } {
61
+ if (!anchorStr) return { kind: 'none' }
62
+ try {
63
+ const p = JSON.parse(anchorStr)
64
+ if (p.name && !p.startLine) return { kind: 'symbol', name: p.name, type: p.type || '' }
65
+ if (p.startLine) return { kind: 'lines', startLine: p.startLine, endLine: p.endLine ?? p.startLine }
66
+ } catch {
67
+ // intentionally empty
68
+ }
69
+ return { kind: 'none' }
70
+ }
71
+
72
+ import { githubCache } from '../utils/githubCache'
73
+ import { getGithubRepoVisibility } from '../utils/githubApi'
74
+ import { parseRepoSlug } from '../utils/url'
75
+
76
+ export default function CodePreviewPanel({ isOpen, onClose, element, hasBackdrop = true }: Props) {
77
+ const [code, setCode] = useState('')
78
+ const [loading, setLoading] = useState(false)
79
+ const [error, setError] = useState<string | null>(null)
80
+ const [resolvedStartLine, setResolvedStartLine] = useState<number | null>(null)
81
+ const [resolvedEndLine, setResolvedEndLine] = useState<number | null>(null)
82
+ const [isPrivateRepo, setIsPrivateRepo] = useState(false)
83
+
84
+ const editorRef = useRef<ReactCodeMirrorRef>(null)
85
+
86
+ const filePath = element?.file_path || ''
87
+ const hashIdx = filePath.indexOf('#')
88
+ const basePath = hashIdx >= 0 ? filePath.slice(0, hashIdx) : filePath
89
+ const symbolInfoStr = hashIdx >= 0 ? filePath.slice(hashIdx + 1) : ''
90
+ const repoSlug = element?.repo ? parseRepoSlug(element.repo) : ''
91
+
92
+ useEffect(() => {
93
+ if (!isOpen || !element || !repoSlug || !basePath) return
94
+
95
+ let cancelled = false
96
+ setLoading(true)
97
+ setError(null)
98
+ setCode('')
99
+ setResolvedStartLine(null)
100
+ setResolvedEndLine(null)
101
+ setIsPrivateRepo(false)
102
+
103
+ const branch = element.branch || 'main'
104
+ const rawUrl = `https://raw.githubusercontent.com/${repoSlug}/refs/heads/${branch}/${basePath}`
105
+
106
+ // Check repo visibility (cached)
107
+ const checkAndFetch = async () => {
108
+ const cachedVisibility = githubCache.getRepoPublic(repoSlug)
109
+ if (cachedVisibility === false) {
110
+ if (!cancelled) { setIsPrivateRepo(true); setLoading(false) }
111
+ return
112
+ }
113
+ if (cachedVisibility === null) {
114
+ try {
115
+ const visibility = await getGithubRepoVisibility(repoSlug)
116
+ if (visibility === 'public') {
117
+ githubCache.setRepoPublic(repoSlug, true)
118
+ } else if (visibility === 'private') {
119
+ githubCache.setRepoPublic(repoSlug, false)
120
+ if (!cancelled) { setIsPrivateRepo(true); setLoading(false) }
121
+ return
122
+ }
123
+ } catch {
124
+ // Network error: proceed optimistically
125
+ }
126
+ }
127
+
128
+ const cached = githubCache.getContent(rawUrl)
129
+ if (cached) {
130
+ if (!cancelled) setCode(cached)
131
+ if (!cancelled) setLoading(false)
132
+ const anchor = parseAnchor(symbolInfoStr)
133
+ const effectiveLanguage = element.language || detectLanguage(basePath)
134
+ if (anchor.kind === 'symbol' && effectiveLanguage) {
135
+ getParser(effectiveLanguage as SupportedLanguage).then(async (parser) => {
136
+ const tree = parser.parse(cached)
137
+ const found = findSymbolByName(tree, effectiveLanguage as SupportedLanguage, anchor.name, anchor.type)
138
+ if (!cancelled && found) {
139
+ setResolvedStartLine(found.startLine)
140
+ setResolvedEndLine(found.endLine)
141
+ }
142
+ }).catch(() => {})
143
+ } else if (anchor.kind === 'lines' && !cancelled) {
144
+ setResolvedStartLine(anchor.startLine)
145
+ setResolvedEndLine(anchor.endLine)
146
+ }
147
+ return
148
+ }
149
+
150
+ try {
151
+ const res = await fetch(rawUrl)
152
+ if (!res.ok) throw new Error(`Failed to fetch: ${res.statusText}`)
153
+ const text = await res.text()
154
+ if (cancelled) return
155
+ githubCache.setContent(rawUrl, text)
156
+ setCode(text)
157
+
158
+ const anchor = parseAnchor(symbolInfoStr)
159
+ const effectiveLanguage = element.language || detectLanguage(basePath)
160
+ if (anchor.kind === 'symbol' && effectiveLanguage) {
161
+ try {
162
+ const parser = await getParser(effectiveLanguage as SupportedLanguage)
163
+ const tree = parser.parse(text)
164
+ const found = findSymbolByName(tree, effectiveLanguage as SupportedLanguage, anchor.name, anchor.type)
165
+ if (!cancelled && found) {
166
+ setResolvedStartLine(found.startLine)
167
+ setResolvedEndLine(found.endLine)
168
+ }
169
+ } catch {
170
+ // intentionally empty
171
+ }
172
+ } else if (anchor.kind === 'lines') {
173
+ if (!cancelled) {
174
+ setResolvedStartLine(anchor.startLine)
175
+ setResolvedEndLine(anchor.endLine)
176
+ }
177
+ }
178
+ } catch (err: unknown) {
179
+ if (!cancelled) setError(err instanceof Error ? err.message : String(err))
180
+ } finally {
181
+ if (!cancelled) setLoading(false)
182
+ }
183
+ }
184
+
185
+ checkAndFetch()
186
+ return () => { cancelled = true }
187
+ }, [isOpen, element, repoSlug, basePath, symbolInfoStr])
188
+
189
+ useEffect(() => {
190
+ if (!code || !resolvedStartLine || !editorRef.current?.view) return
191
+ const view = editorRef.current.view
192
+ try {
193
+ const linePos = view.state.doc.line(resolvedStartLine)
194
+ const endPos = view.state.doc.line(resolvedEndLine ?? resolvedStartLine)
195
+ view.dispatch({
196
+ selection: { anchor: linePos.from, head: endPos.to },
197
+ effects: [
198
+ EditorView.scrollIntoView(linePos.from, { y: 'center' })
199
+ ]
200
+ })
201
+ } catch {
202
+ // intentionally empty
203
+ }
204
+ }, [code, resolvedStartLine, resolvedEndLine])
205
+
206
+ const githubUrl = element?.repo && basePath
207
+ ? `https://github.com/${repoSlug}/blob/${element.branch || 'main'}/${basePath}`
208
+ + (resolvedStartLine ? `#L${resolvedStartLine}-L${resolvedEndLine ?? resolvedStartLine}` : '')
209
+ : null
210
+
211
+ const getLanguageExtension = () => {
212
+ const extensions = [customCodeTheme]
213
+ const effectiveLanguage = element?.language || detectLanguage(basePath)
214
+ switch (effectiveLanguage) {
215
+ case 'javascript':
216
+ case 'typescript':
217
+ extensions.push(javascript({ typescript: effectiveLanguage === 'typescript' }))
218
+ break
219
+ case 'python': extensions.push(python()); break
220
+ case 'cpp': extensions.push(cpp()); break
221
+ case 'java': extensions.push(java()); break
222
+ case 'rust': extensions.push(rust()); break
223
+ }
224
+ return extensions
225
+ }
226
+
227
+ return (
228
+ <SlidingPanel
229
+ isOpen={isOpen}
230
+ onClose={onClose}
231
+ panelKey="code-preview"
232
+ side="left"
233
+ width={{ base: 'calc(100vw - 32px)', md: '45vw' }}
234
+ minWidth="300px"
235
+ height="calc(90vh - 2rem)"
236
+ maxHeight="calc(90vh - 2rem)"
237
+ hasBackdrop={hasBackdrop}
238
+ zIndex={1300}
239
+ >
240
+ {/* Header */}
241
+ <HStack px={4} pt={4} pb={3} justify="space-between" flexShrink={0}
242
+ borderBottom="1px solid" borderColor="whiteAlpha.100">
243
+ <VStack align="start" spacing={0} minW={0} flex={1}>
244
+ <Text fontSize="10px" color="gray.600" letterSpacing="widest" textTransform="uppercase" fontWeight="600">
245
+ Source
246
+ </Text>
247
+ <Text fontSize="sm" fontWeight="700" color="white" isTruncated maxW={{ base: '180px', md: '360px' }} letterSpacing="0.01em">
248
+ {basePath.split('/').pop() || 'Code'}
249
+ </Text>
250
+ {resolvedStartLine && (
251
+ <Text fontSize="10px" color="blue.400" fontFamily="mono">
252
+ L{resolvedStartLine}–L{resolvedEndLine ?? resolvedStartLine}
253
+ </Text>
254
+ )}
255
+ {isPrivateRepo && (
256
+ <Text fontSize="10px" color="orange.400" fontFamily="mono">
257
+ Private repository
258
+ </Text>
259
+ )}
260
+ </VStack>
261
+ <HStack spacing={2} flexShrink={0}>
262
+ {element?.url && (
263
+ <Tooltip label="Open URL" placement="bottom">
264
+ <Button
265
+ as="a"
266
+ href={element.url}
267
+ target="_blank"
268
+ rel="noopener noreferrer"
269
+ aria-label="Open URL"
270
+ leftIcon={<ExternalLinkIcon w="12px" h="12px" />}
271
+ size="xs"
272
+ variant="outline"
273
+ color="whiteAlpha.700"
274
+ borderColor="whiteAlpha.200"
275
+ h="24px"
276
+ px={2.5}
277
+ fontSize="11px"
278
+ fontWeight="600"
279
+ bg="whiteAlpha.50"
280
+ _hover={{
281
+ color: 'white',
282
+ bg: 'whiteAlpha.100',
283
+ borderColor: 'whiteAlpha.400',
284
+ textDecoration: 'none',
285
+ transform: 'translateY(-0.5px)',
286
+ boxShadow: '0 2px 4px rgba(0,0,0,0.2)'
287
+ }}
288
+ _active={{
289
+ bg: 'whiteAlpha.200',
290
+ transform: 'translateY(0)',
291
+ }}
292
+ transition="all 0.1s"
293
+ >
294
+ Open URL
295
+ </Button>
296
+ </Tooltip>
297
+ )}
298
+ {githubUrl && (
299
+ <Tooltip label="Open in GitHub" placement="bottom">
300
+ <Button
301
+ as="a"
302
+ href={isPrivateRepo ? undefined : githubUrl}
303
+ target="_blank"
304
+ rel="noopener noreferrer"
305
+ aria-label="Open in GitHub"
306
+ leftIcon={<Icon as={GithubIcon} boxSize="13px" />}
307
+ size="xs"
308
+ variant="outline"
309
+ color={isPrivateRepo ? 'whiteAlpha.300' : 'whiteAlpha.700'}
310
+ borderColor={isPrivateRepo ? 'whiteAlpha.100' : 'whiteAlpha.200'}
311
+ h="24px"
312
+ px={2.5}
313
+ fontSize="11px"
314
+ fontWeight="600"
315
+ bg={isPrivateRepo ? 'transparent' : 'whiteAlpha.50'}
316
+ pointerEvents={isPrivateRepo ? 'none' : undefined}
317
+ opacity={isPrivateRepo ? 0.4 : undefined}
318
+ _hover={isPrivateRepo ? {} : {
319
+ color: 'white',
320
+ bg: 'whiteAlpha.100',
321
+ borderColor: 'whiteAlpha.400',
322
+ textDecoration: 'none',
323
+ transform: 'translateY(-0.5px)',
324
+ boxShadow: '0 2px 4px rgba(0,0,0,0.2)'
325
+ }}
326
+ _active={{
327
+ bg: 'whiteAlpha.200',
328
+ transform: 'translateY(0)',
329
+ }}
330
+ transition="all 0.1s"
331
+ >
332
+ Open in GitHub
333
+ </Button>
334
+ </Tooltip>
335
+ )}
336
+ <CloseButton size="sm" color="whiteAlpha.500"
337
+ _hover={{ color: 'white', bg: 'whiteAlpha.100' }}
338
+ onClick={onClose} />
339
+ </HStack>
340
+ </HStack>
341
+
342
+ {/* Code area */}
343
+ <Box flex={1} overflow="hidden" position="relative" bg="var(--bg-main)">
344
+ {loading && (
345
+ <VStack position="absolute" inset={0} justify="center" align="center" bg="blackAlpha.600" zIndex={10}>
346
+ <Spinner color="blue.400" />
347
+ <Text color="white" fontSize="sm">Loading source...</Text>
348
+ </VStack>
349
+ )}
350
+ {error && (
351
+ <VStack position="absolute" inset={0} justify="center" align="center" bg="blackAlpha.800" zIndex={10} px={6}>
352
+ <Text color="red.400" fontSize="sm" textAlign="center">{error}</Text>
353
+ </VStack>
354
+ )}
355
+ {isPrivateRepo && !loading && (
356
+ <VStack position="absolute" inset={0} justify="center" align="center" bg="blackAlpha.800" zIndex={10} px={6} spacing={2}>
357
+ <Icon as={GithubIcon} boxSize="32px" color="whiteAlpha.300" />
358
+ <Text color="whiteAlpha.700" fontSize="sm" fontWeight="600">Private repository</Text>
359
+ <Text color="whiteAlpha.400" fontSize="xs" textAlign="center">
360
+ Code preview is unavailable for private repositories.
361
+ </Text>
362
+ </VStack>
363
+ )}
364
+ <Box position="absolute" inset={0}>
365
+ <CodeMirror
366
+ ref={editorRef}
367
+ value={code}
368
+ height="100%"
369
+ extensions={getLanguageExtension()}
370
+ theme={oneDark}
371
+ readOnly={true}
372
+ editable={false}
373
+ basicSetup={{
374
+ lineNumbers: true,
375
+ highlightActiveLineGutter: true,
376
+ highlightSelectionMatches: true,
377
+ }}
378
+ style={{ fontSize: '12px', height: '100%' }}
379
+ />
380
+ </Box>
381
+ </Box>
382
+ </SlidingPanel>
383
+ )
384
+ }
@@ -0,0 +1,66 @@
1
+ import { useRef } from 'react'
2
+ import {
3
+ AlertDialog,
4
+ AlertDialogBody,
5
+ AlertDialogContent,
6
+ AlertDialogFooter,
7
+ AlertDialogHeader,
8
+ AlertDialogOverlay,
9
+ Button,
10
+ } from '@chakra-ui/react'
11
+
12
+ interface Props {
13
+ isOpen: boolean
14
+ onClose: () => void
15
+ onConfirm: () => void
16
+ title: string
17
+ body: string
18
+ confirmLabel?: string
19
+ confirmColorScheme?: string
20
+ isLoading?: boolean
21
+ }
22
+
23
+ export default function ConfirmDialog({
24
+ isOpen,
25
+ onClose,
26
+ onConfirm,
27
+ title,
28
+ body,
29
+ confirmLabel = 'Delete',
30
+ confirmColorScheme = 'red',
31
+ isLoading,
32
+ }: Props) {
33
+ const cancelRef = useRef<HTMLButtonElement>(null)
34
+
35
+ return (
36
+ <AlertDialog isOpen={isOpen} leastDestructiveRef={cancelRef} onClose={onClose} isCentered>
37
+ <AlertDialogOverlay bg="blackAlpha.700" backdropFilter="blur(4px)" />
38
+ <AlertDialogContent mx={4}>
39
+ <AlertDialogHeader>
40
+ {title}
41
+ </AlertDialogHeader>
42
+ <AlertDialogBody fontSize="sm">
43
+ {body}
44
+ </AlertDialogBody>
45
+ <AlertDialogFooter gap={2} pt={4}>
46
+ <Button
47
+ ref={cancelRef}
48
+ onClick={onClose}
49
+ variant="ghost"
50
+ size="sm"
51
+ >
52
+ Cancel
53
+ </Button>
54
+ <Button
55
+ onClick={onConfirm}
56
+ isLoading={isLoading}
57
+ size="sm"
58
+ variant={confirmColorScheme === 'red' ? 'destructive' : undefined}
59
+ >
60
+ {confirmLabel}
61
+ </Button>
62
+ </AlertDialogFooter>
63
+ </AlertDialogContent>
64
+ </AlertDialog>
65
+ )
66
+ }