@tldiagram/core-ui 1.87.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/App.d.ts +1 -0
- package/dist/api/client.d.ts +143 -0
- package/dist/api/transport-vscode.d.ts +8 -0
- package/dist/api/transport.d.ts +1 -0
- package/dist/components/CodePreviewPanel-vscode.d.ts +7 -0
- package/dist/components/CodePreviewPanel.d.ts +9 -0
- package/dist/components/ConfirmDialog.d.ts +12 -0
- package/dist/components/ConnectorPanel.d.ts +21 -0
- package/dist/components/ContextBoundaryElement.d.ts +11 -0
- package/dist/components/ContextNeighborElement.d.ts +29 -0
- package/dist/components/ContextStraightConnector.d.ts +4 -0
- package/dist/components/CrossBranchControls.d.ts +9 -0
- package/dist/components/DependenciesOnboarding.d.ts +5 -0
- package/dist/components/DrawingCanvas.d.ts +39 -0
- package/dist/components/ElementLibrary-vscode.d.ts +7 -0
- package/dist/components/ElementLibrary.d.ts +22 -0
- package/dist/components/ElementNode.d.ts +36 -0
- package/dist/components/ElementPanel.d.ts +25 -0
- package/dist/components/ExploreOnboarding.d.ts +5 -0
- package/dist/components/ExplorePageOnboarding.d.ts +5 -0
- package/dist/components/ExportModal.d.ts +16 -0
- package/dist/components/FloatingEdge.d.ts +9 -0
- package/dist/components/GitSourceLinker.d.ts +8 -0
- package/dist/components/HeaderContext.d.ts +16 -0
- package/dist/components/Icons.d.ts +95 -0
- package/dist/components/ImportModal.d.ts +10 -0
- package/dist/components/InlineElementAdder.d.ts +17 -0
- package/dist/components/LayoutSection.d.ts +7 -0
- package/dist/components/LocalSourceLinker.d.ts +8 -0
- package/dist/components/MiniZoomOnboarding.d.ts +5 -0
- package/dist/components/NavBreadcrumb.d.ts +6 -0
- package/dist/components/NodeBody.d.ts +12 -0
- package/dist/components/NodeContainer.d.ts +8 -0
- package/dist/components/NodeHoverCard.d.ts +10 -0
- package/dist/components/PanelHeader.d.ts +8 -0
- package/dist/components/PanelUI.d.ts +3 -0
- package/dist/components/ProxyConnectorEdge.d.ts +4 -0
- package/dist/components/ProxyConnectorPanel.d.ts +9 -0
- package/dist/components/SafeBackground.d.ts +13 -0
- package/dist/components/ScrollIndicatorWrapper.d.ts +8 -0
- package/dist/components/SetChildModal.d.ts +10 -0
- package/dist/components/SetParentModal.d.ts +10 -0
- package/dist/components/SlidingPanel.d.ts +16 -0
- package/dist/components/TagUpsert.d.ts +8 -0
- package/dist/components/TopMenuBar.d.ts +8 -0
- package/dist/components/ViewBezierConnector.d.ts +4 -0
- package/dist/components/ViewDrawMenu.d.ts +22 -0
- package/dist/components/ViewEditorEdgeLabelLayout.d.ts +16 -0
- package/dist/components/ViewEditorOnboarding.d.ts +5 -0
- package/dist/components/ViewExplorer/TagManager/ColorPicker.d.ts +7 -0
- package/dist/components/ViewExplorer/TagManager/GroupNamingPopover.d.ts +10 -0
- package/dist/components/ViewExplorer/TagManager/LayerItem.d.ts +27 -0
- package/dist/components/ViewExplorer/TagManager/TagItem.d.ts +25 -0
- package/dist/components/ViewExplorer/TagManager/index.d.ts +21 -0
- package/dist/components/ViewExplorer/ViewNavigator.d.ts +11 -0
- package/dist/components/ViewExplorer/ViewSearch.d.ts +8 -0
- package/dist/components/ViewExplorer/ViewTree.d.ts +18 -0
- package/dist/components/ViewExplorer/index.d.ts +31 -0
- package/dist/components/ViewExplorer/types.d.ts +11 -0
- package/dist/components/ViewExplorer/utils.d.ts +6 -0
- package/dist/components/ViewExplorer-vscode.d.ts +6 -0
- package/dist/components/ViewFloatingMenu-vscode.d.ts +27 -0
- package/dist/components/ViewFloatingMenu.d.ts +39 -0
- package/dist/components/ViewGridNode.d.ts +29 -0
- package/dist/components/ViewHeaderButton.d.ts +11 -0
- package/dist/components/ViewPanel.d.ts +18 -0
- package/dist/components/ViewsGridOnboarding.d.ts +5 -0
- package/dist/components/ZUI/ZUICanvas.d.ts +18 -0
- package/dist/components/ZUI/index.d.ts +2 -0
- package/dist/components/ZUI/layout.d.ts +18 -0
- package/dist/components/ZUI/proxy.d.ts +25 -0
- package/dist/components/ZUI/renderer.d.ts +30 -0
- package/dist/components/ZUI/types.d.ts +140 -0
- package/dist/components/ZUI/useZUIInteraction.d.ts +21 -0
- package/dist/config/runtime-vscode.d.ts +22 -0
- package/dist/config/runtime.d.ts +5 -0
- package/dist/constants/colors.d.ts +27 -0
- package/dist/constants/diagramColors.d.ts +1 -0
- package/dist/context/ThemeContext.d.ts +27 -0
- package/dist/crossBranch/graph.d.ts +13 -0
- package/dist/crossBranch/resolve.d.ts +22 -0
- package/dist/crossBranch/settings.d.ts +6 -0
- package/dist/crossBranch/store.d.ts +11 -0
- package/dist/crossBranch/types.d.ts +96 -0
- package/dist/demo/DemoPage.d.ts +9 -0
- package/dist/demo/seed.d.ts +9 -0
- package/dist/demo/store.d.ts +137 -0
- package/dist/demo/viewEditor.d.ts +26 -0
- package/dist/favicon.svg +35 -0
- package/dist/hooks/useSafeFitView.d.ts +16 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +115 -0
- package/dist/index.js +19966 -0
- package/dist/lib/vscodeBridge-vscode.d.ts +13 -0
- package/dist/lib/vscodeBridge.d.ts +5 -0
- package/dist/logo-120.png +0 -0
- package/dist/logo-bw.png +0 -0
- package/dist/logo-bw.svg +15 -0
- package/dist/logo-text.svg +51 -0
- package/dist/logo.svg +35 -0
- package/dist/pages/AppearanceSettings.d.ts +3 -0
- package/dist/pages/Dependencies.d.ts +1 -0
- package/dist/pages/InfiniteZoom.d.ts +7 -0
- package/dist/pages/Settings.d.ts +7 -0
- package/dist/pages/ViewEditor/components/EditorMenus.d.ts +24 -0
- package/dist/pages/ViewEditor/components/EditorOverlays.d.ts +30 -0
- package/dist/pages/ViewEditor/components/EmptyCanvasState.d.ts +7 -0
- package/dist/pages/ViewEditor/context.d.ts +13 -0
- package/dist/pages/ViewEditor/hooks/useCanvasInteractions.d.ts +201 -0
- package/dist/pages/ViewEditor/hooks/useDrawingEngine.d.ts +40 -0
- package/dist/pages/ViewEditor/hooks/useViewContextNeighbours.d.ts +20 -0
- package/dist/pages/ViewEditor/hooks/useViewData.d.ts +74 -0
- package/dist/pages/ViewEditor/index.d.ts +8 -0
- package/dist/pages/ViewEditor/utils.d.ts +14 -0
- package/dist/pages/Views.d.ts +6 -0
- package/dist/pages/ViewsGrid.d.ts +6 -0
- package/dist/pkg/importer/mermaid.d.ts +7 -0
- package/dist/pkg/importer/mermaid.test.d.ts +1 -0
- package/dist/platform/PlatformContext.d.ts +6 -0
- package/dist/platform/context.d.ts +3 -0
- package/dist/platform/local.d.ts +2 -0
- package/dist/platform/types.d.ts +17 -0
- package/dist/slots.d.ts +67 -0
- package/dist/theme.d.ts +2 -0
- package/dist/types/index.d.ts +193 -0
- package/dist/types/vscode-messages.d.ts +60 -0
- package/dist/utils/edgeDistribution.d.ts +34 -0
- package/dist/utils/githubApi.d.ts +4 -0
- package/dist/utils/githubCache.d.ts +17 -0
- package/dist/utils/ids.d.ts +2 -0
- package/dist/utils/technologyCatalog.d.ts +15 -0
- package/dist/utils/toast.d.ts +15 -0
- package/dist/utils/treesitter.d.ts +13 -0
- package/dist/utils/url.d.ts +12 -0
- package/package.json +159 -0
- package/src/App.tsx +141 -0
- package/src/api/client.ts +618 -0
- package/src/api/transport-vscode.ts +28 -0
- package/src/api/transport.ts +7 -0
- package/src/assets/logo-mark.svg +31 -0
- package/src/assets/logo-wordmark.svg +22 -0
- package/src/assets/logo.svg +35 -0
- package/src/components/CodePreviewPanel-vscode.tsx +85 -0
- package/src/components/CodePreviewPanel.tsx +384 -0
- package/src/components/ConfirmDialog.tsx +66 -0
- package/src/components/ConnectorPanel.tsx +403 -0
- package/src/components/ContextBoundaryElement.tsx +35 -0
- package/src/components/ContextNeighborElement.tsx +282 -0
- package/src/components/ContextStraightConnector.tsx +144 -0
- package/src/components/CrossBranchControls.tsx +105 -0
- package/src/components/DependenciesOnboarding.tsx +427 -0
- package/src/components/DrawingCanvas.tsx +391 -0
- package/src/components/ElementLibrary-vscode.tsx +9 -0
- package/src/components/ElementLibrary.tsx +512 -0
- package/src/components/ElementNode.tsx +1033 -0
- package/src/components/ElementPanel.tsx +928 -0
- package/src/components/ExploreOnboarding.tsx +347 -0
- package/src/components/ExplorePageOnboarding.tsx +383 -0
- package/src/components/ExportModal.tsx +132 -0
- package/src/components/FloatingEdge.tsx +115 -0
- package/src/components/GitSourceLinker.tsx +1053 -0
- package/src/components/HeaderContext.tsx +30 -0
- package/src/components/Icons.tsx +245 -0
- package/src/components/ImportModal.tsx +219 -0
- package/src/components/InlineElementAdder.tsx +216 -0
- package/src/components/LayoutSection.tsx +624 -0
- package/src/components/LocalSourceLinker.tsx +330 -0
- package/src/components/MiniZoomOnboarding.tsx +78 -0
- package/src/components/NavBreadcrumb.tsx +24 -0
- package/src/components/NodeBody.tsx +89 -0
- package/src/components/NodeContainer.tsx +58 -0
- package/src/components/NodeHoverCard.tsx +135 -0
- package/src/components/PanelHeader.tsx +36 -0
- package/src/components/PanelUI.tsx +24 -0
- package/src/components/ProxyConnectorEdge.tsx +169 -0
- package/src/components/ProxyConnectorPanel.tsx +130 -0
- package/src/components/SafeBackground.tsx +19 -0
- package/src/components/ScrollIndicatorWrapper.tsx +117 -0
- package/src/components/SetChildModal.tsx +191 -0
- package/src/components/SetParentModal.tsx +187 -0
- package/src/components/SlidingPanel.tsx +114 -0
- package/src/components/TagUpsert.tsx +142 -0
- package/src/components/TopMenuBar.tsx +380 -0
- package/src/components/ViewBezierConnector.tsx +143 -0
- package/src/components/ViewDrawMenu.tsx +270 -0
- package/src/components/ViewEditorEdgeLabelLayout.ts +189 -0
- package/src/components/ViewEditorOnboarding.tsx +445 -0
- package/src/components/ViewExplorer/TagManager/ColorPicker.tsx +49 -0
- package/src/components/ViewExplorer/TagManager/GroupNamingPopover.tsx +96 -0
- package/src/components/ViewExplorer/TagManager/LayerItem.tsx +228 -0
- package/src/components/ViewExplorer/TagManager/TagItem.tsx +242 -0
- package/src/components/ViewExplorer/TagManager/index.tsx +418 -0
- package/src/components/ViewExplorer/ViewNavigator.tsx +121 -0
- package/src/components/ViewExplorer/ViewSearch.tsx +33 -0
- package/src/components/ViewExplorer/ViewTree.tsx +98 -0
- package/src/components/ViewExplorer/index.tsx +384 -0
- package/src/components/ViewExplorer/types.ts +13 -0
- package/src/components/ViewExplorer/utils.ts +56 -0
- package/src/components/ViewExplorer-vscode.tsx +8 -0
- package/src/components/ViewFloatingMenu-vscode.tsx +248 -0
- package/src/components/ViewFloatingMenu.tsx +379 -0
- package/src/components/ViewGridNode.tsx +451 -0
- package/src/components/ViewHeaderButton.tsx +60 -0
- package/src/components/ViewPanel.tsx +162 -0
- package/src/components/ViewsGridOnboarding.tsx +400 -0
- package/src/components/ZUI/ZUICanvas.tsx +853 -0
- package/src/components/ZUI/index.ts +3 -0
- package/src/components/ZUI/layout.ts +323 -0
- package/src/components/ZUI/proxy.ts +278 -0
- package/src/components/ZUI/renderer.ts +1189 -0
- package/src/components/ZUI/types.ts +150 -0
- package/src/components/ZUI/useZUIInteraction.ts +720 -0
- package/src/config/runtime-vscode.ts +46 -0
- package/src/config/runtime.ts +30 -0
- package/src/constants/colors.ts +80 -0
- package/src/constants/diagramColors.ts +9 -0
- package/src/context/ThemeContext.tsx +158 -0
- package/src/crossBranch/graph.ts +207 -0
- package/src/crossBranch/resolve.ts +643 -0
- package/src/crossBranch/settings.ts +59 -0
- package/src/crossBranch/store.ts +71 -0
- package/src/crossBranch/types.ts +102 -0
- package/src/demo/DemoPage.tsx +184 -0
- package/src/demo/seed.ts +67 -0
- package/src/demo/store.ts +536 -0
- package/src/demo/viewEditor.ts +110 -0
- package/src/hooks/useSafeFitView.ts +60 -0
- package/src/index.css +309 -0
- package/src/index.ts +184 -0
- package/src/kafka-ss.png +0 -0
- package/src/lib/vscodeBridge-vscode.ts +27 -0
- package/src/lib/vscodeBridge.ts +7 -0
- package/src/main.tsx +46 -0
- package/src/pages/AppearanceSettings.tsx +135 -0
- package/src/pages/Dependencies.tsx +926 -0
- package/src/pages/InfiniteZoom.tsx +404 -0
- package/src/pages/Settings.tsx +91 -0
- package/src/pages/ViewEditor/EDGE_DISTRIBUTION.md +64 -0
- package/src/pages/ViewEditor/components/EditorMenus.tsx +112 -0
- package/src/pages/ViewEditor/components/EditorOverlays.tsx +172 -0
- package/src/pages/ViewEditor/components/EmptyCanvasState.tsx +42 -0
- package/src/pages/ViewEditor/context.tsx +21 -0
- package/src/pages/ViewEditor/hooks/useCanvasInteractions.ts +1349 -0
- package/src/pages/ViewEditor/hooks/useDrawingEngine.ts +127 -0
- package/src/pages/ViewEditor/hooks/useViewContextNeighbours.ts +501 -0
- package/src/pages/ViewEditor/hooks/useViewData.ts +491 -0
- package/src/pages/ViewEditor/index.tsx +1366 -0
- package/src/pages/ViewEditor/utils.ts +88 -0
- package/src/pages/Views.tsx +171 -0
- package/src/pages/ViewsGrid.tsx +1310 -0
- package/src/pkg/importer/mermaid.test.ts +141 -0
- package/src/pkg/importer/mermaid.ts +76 -0
- package/src/platform/PlatformContext.tsx +17 -0
- package/src/platform/context.ts +9 -0
- package/src/platform/local.tsx +15 -0
- package/src/platform/types.ts +19 -0
- package/src/slots.ts +92 -0
- package/src/styles/editor-panels.css +66 -0
- package/src/styles/theme.css +56 -0
- package/src/theme.ts +336 -0
- package/src/types/index.ts +234 -0
- package/src/types/offline-ambient.d.ts +14 -0
- package/src/types/vscode-messages.ts +32 -0
- package/src/utils/edgeDistribution.ts +103 -0
- package/src/utils/githubApi.ts +121 -0
- package/src/utils/githubCache.ts +108 -0
- package/src/utils/ids.ts +9 -0
- package/src/utils/technologyCatalog.ts +143 -0
- package/src/utils/toast.ts +100 -0
- package/src/utils/treesitter.ts +147 -0
- package/src/utils/url.ts +72 -0
- package/src/vite-env.d.ts +1 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VS Code webview runtime configuration.
|
|
3
|
+
*
|
|
4
|
+
* Replaces runtime.ts for the vscode webview build:
|
|
5
|
+
* - Reads server URL from window.__TLD_SERVER_URL__ instead of import.meta.env
|
|
6
|
+
* - No Capacitor dependency (isNativeApp = false)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
declare global {
|
|
10
|
+
interface Window {
|
|
11
|
+
__TLD_SERVER_URL__?: string
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function trimTrailingSlash(value: string): string {
|
|
16
|
+
return value.replace(/\/+$/, '')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const serverUrl = trimTrailingSlash(window.__TLD_SERVER_URL__ ?? 'https://tldiagram.com')
|
|
20
|
+
|
|
21
|
+
export const appBase = '/app/'
|
|
22
|
+
export const routerBasename = undefined
|
|
23
|
+
export const isNativeApp = false
|
|
24
|
+
|
|
25
|
+
export const apiBase = `${serverUrl}/api`
|
|
26
|
+
|
|
27
|
+
export function apiUrl(path: string): string {
|
|
28
|
+
return `${apiBase}${path.startsWith('/') ? path : `/${path}`}`
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function oauthGoogleStartUrl(): string {
|
|
32
|
+
return apiUrl('/auth/oauth/google')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function oauthGithubStartUrl(): string {
|
|
36
|
+
return apiUrl('/auth/oauth/github')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function oauthAppleStartUrl(): string {
|
|
40
|
+
return apiUrl('/auth/oauth/apple')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Web OAuth client ID - also used as serverClientId for native Google Sign-In
|
|
44
|
+
export const googleClientId = '945690634753-lcrtd97c5hnqdo5shkoaetstmtrqbk5t.apps.googleusercontent.com'
|
|
45
|
+
|
|
46
|
+
export const turnstileSiteKey = '0x4AAAAAACyQUcIpN2Yuy8-a'
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const DEFAULT_WEB_BASE = "/"
|
|
2
|
+
|
|
3
|
+
function trimTrailingSlash(value: string): string {
|
|
4
|
+
return value.replace(/\/+$/, "")
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function trim(value: string | undefined): string | undefined {
|
|
8
|
+
if (!value) return undefined
|
|
9
|
+
const cleaned = value.trim()
|
|
10
|
+
return cleaned.length > 0 ? cleaned : undefined
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const configuredApiBase = trim(import.meta.env.VITE_API_BASE_URL)
|
|
14
|
+
|
|
15
|
+
export const appBase = trim(import.meta.env.VITE_APP_BASE) ?? DEFAULT_WEB_BASE
|
|
16
|
+
export const routerBasename = (() => {
|
|
17
|
+
const configuredBase = trim(import.meta.env.VITE_ROUTER_BASENAME)
|
|
18
|
+
if (configuredBase && configuredBase !== "/") return configuredBase
|
|
19
|
+
return undefined
|
|
20
|
+
})()
|
|
21
|
+
|
|
22
|
+
export const isNativeApp = false
|
|
23
|
+
|
|
24
|
+
export const apiBase = trimTrailingSlash(
|
|
25
|
+
configuredApiBase ?? "/api",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
export function apiUrl(path: string): string {
|
|
29
|
+
return `${apiBase}${path.startsWith("/") ? path : `/${path}`}`
|
|
30
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// ── Accent color ────────────────────────────────────────────────────────────
|
|
2
|
+
// The interactive brand color. Drives edges, selection highlights, drawing
|
|
3
|
+
// stroke default, and focus indicators. Users can customize via Appearance
|
|
4
|
+
// settings; the chosen value is stored in localStorage and exposed as the
|
|
5
|
+
// CSS variable --accent on :root.
|
|
6
|
+
|
|
7
|
+
export const ACCENT_DEFAULT = '#63b3ed'
|
|
8
|
+
|
|
9
|
+
export const ACCENT_OPTIONS: { name: string; value: string }[] = [
|
|
10
|
+
{ name: 'Blue', value: '#63b3ed' },
|
|
11
|
+
{ name: 'Teal', value: '#4fd1c5' },
|
|
12
|
+
{ name: 'Purple', value: '#b794f4' },
|
|
13
|
+
{ name: 'Green', value: '#68d391' },
|
|
14
|
+
{ name: 'Orange', value: '#f6ad55' },
|
|
15
|
+
{ name: 'Pink', value: '#f687b3' },
|
|
16
|
+
{ name: 'White', value: '#ffffff' },
|
|
17
|
+
{ name: 'Black', value: '#000000' },
|
|
18
|
+
{ name: 'Red', value: '#f56565' },
|
|
19
|
+
{ name: 'Yellow', value: '#ecc94b' },
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
// ── Background color ─────────────────────────────────────────────────────────
|
|
23
|
+
export const BACKGROUND_DEFAULT = '#0d121e'
|
|
24
|
+
|
|
25
|
+
export const BACKGROUND_OPTIONS: { name: string; value: string }[] = [
|
|
26
|
+
{ name: 'Midnight', value: '#0d121e' }, // Original dark blue/purplish
|
|
27
|
+
{ name: 'Deep Sea', value: '#0a192f' },
|
|
28
|
+
{ name: 'Obsidian', value: '#0f172a' },
|
|
29
|
+
{ name: 'Coal', value: '#111111' },
|
|
30
|
+
{ name: 'Space', value: '#0b0e14' },
|
|
31
|
+
{ name: 'Asphalt', value: '#1a202c' },
|
|
32
|
+
{ name: 'Dark', value: '#1e1e1e' },
|
|
33
|
+
{ name: 'Ebony', value: '#121212' },
|
|
34
|
+
{ name: 'Pitch', value: '#000000' },
|
|
35
|
+
{ name: 'Charcoal', value: '#242424' },
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
// ── Element color ────────────────────────────────────────────────────────────
|
|
39
|
+
export const ELEMENT_DEFAULT = '#2d3748'
|
|
40
|
+
|
|
41
|
+
export const ELEMENT_OPTIONS: { name: string; value: string }[] = [
|
|
42
|
+
{ name: 'Slate', value: '#2d3748' },
|
|
43
|
+
{ name: 'Navy', value: '#1a365d' },
|
|
44
|
+
{ name: 'Deep Purple', value: '#322659' },
|
|
45
|
+
{ name: 'Dark Grey', value: '#171923' },
|
|
46
|
+
{ name: 'Steel', value: '#4a5568' },
|
|
47
|
+
{ name: 'Cobalt', value: '#2c5282' },
|
|
48
|
+
{ name: 'Forest', value: '#22543d' },
|
|
49
|
+
{ name: 'Rust', value: '#742a2a' },
|
|
50
|
+
{ name: 'Bronze', value: '#744210' },
|
|
51
|
+
{ name: 'Amethyst', value: '#553c9a' },
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
// ── View hierarchy ───────────────────────────────────────────────────────────
|
|
55
|
+
// Blue = parent / zoom-out direction (equivalent to Chakra blue.400)
|
|
56
|
+
// Teal = child / zoom-in direction (equivalent to Chakra teal.400)
|
|
57
|
+
|
|
58
|
+
export const PARENT_VIEW_COLOR = '#63b3ed'
|
|
59
|
+
export const CHILD_VIEW_COLOR = '#4fd1c5'
|
|
60
|
+
|
|
61
|
+
export const PARENT_VIEW_BG = 'rgba(99,179,237,0.12)'
|
|
62
|
+
export const PARENT_VIEW_BORDER = 'rgba(99,179,237,0.25)'
|
|
63
|
+
export const CHILD_VIEW_BG = 'rgba(79,209,197,0.12)'
|
|
64
|
+
export const CHILD_VIEW_BORDER = 'rgba(79,209,197,0.25)'
|
|
65
|
+
|
|
66
|
+
// ── Drawing toolbar palette ───────────────────────────────────────────────────
|
|
67
|
+
export const DRAWING_COLORS = ['#63b3ed', '#f56565', '#48bb78', '#ecc94b', '#ed64a6', '#ffffff']
|
|
68
|
+
|
|
69
|
+
// ── Utility ───────────────────────────────────────────────────────────────────
|
|
70
|
+
/**
|
|
71
|
+
* Convert a 6-char hex color to `rgba(r, g, b, alpha)`.
|
|
72
|
+
* Used to compute dynamic shadows from the accent value.
|
|
73
|
+
*/
|
|
74
|
+
export function hexToRgba(hex: string, alpha: number): string {
|
|
75
|
+
const h = hex.replace('#', '')
|
|
76
|
+
const r = parseInt(h.substring(0, 2), 16)
|
|
77
|
+
const g = parseInt(h.substring(2, 4), 16)
|
|
78
|
+
const b = parseInt(h.substring(4, 6), 16)
|
|
79
|
+
return `rgba(${r},${g},${b},${alpha})`
|
|
80
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Re-exports - all values have moved to constants/colors.ts.
|
|
2
|
+
export {
|
|
3
|
+
PARENT_VIEW_COLOR as PARENT_DIAGRAM_COLOR,
|
|
4
|
+
CHILD_VIEW_COLOR as CHILD_DIAGRAM_COLOR,
|
|
5
|
+
PARENT_VIEW_BG as PARENT_DIAGRAM_BG,
|
|
6
|
+
PARENT_VIEW_BORDER as PARENT_DIAGRAM_BORDER,
|
|
7
|
+
CHILD_VIEW_BG as CHILD_DIAGRAM_BG,
|
|
8
|
+
CHILD_VIEW_BORDER as CHILD_DIAGRAM_BORDER,
|
|
9
|
+
} from './colors'
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/* eslint-disable react-refresh/only-export-components */
|
|
2
|
+
import { createContext, useContext, useEffect, useState, type ReactNode } from 'react'
|
|
3
|
+
import { ACCENT_DEFAULT, BACKGROUND_DEFAULT, ELEMENT_DEFAULT, hexToRgba } from '../constants/colors'
|
|
4
|
+
import { api } from '../api/client'
|
|
5
|
+
|
|
6
|
+
const ACCENT_KEY = 'diag:accent-color'
|
|
7
|
+
const BG_KEY = 'diag:background-color'
|
|
8
|
+
const ELEMENT_COLOR_KEY = 'diag:element-color'
|
|
9
|
+
|
|
10
|
+
/** Convert hex to "r,g,b" triplet for use in rgba(var(--rgb), alpha). */
|
|
11
|
+
function toRgbTriplet(hex: string): string {
|
|
12
|
+
const rgba = hexToRgba(hex, 1)
|
|
13
|
+
// hexToRgba returns "rgba(r,g,b,1)" - extract "r,g,b"
|
|
14
|
+
return rgba.slice(5, -3)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function applyAccentVars(hex: string) {
|
|
18
|
+
document.documentElement.style.setProperty('--accent', hex)
|
|
19
|
+
document.documentElement.style.setProperty('--accent-rgb', toRgbTriplet(hex))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function applyBgVars(hex: string) {
|
|
23
|
+
document.documentElement.style.setProperty('--bg-main', hex)
|
|
24
|
+
document.documentElement.style.setProperty('--bg-main-rgb', toRgbTriplet(hex))
|
|
25
|
+
|
|
26
|
+
// Also derive canvas background (slightly darker)
|
|
27
|
+
// For simplicity, we just use the same or a hardcoded variant for now,
|
|
28
|
+
// but we could use color-mix if we wanted true derivation.
|
|
29
|
+
// document.documentElement.style.setProperty('--bg-canvas', `color-mix(in srgb, ${hex}, black 20%)`)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function applyElementVars(hex: string) {
|
|
33
|
+
document.documentElement.style.setProperty('--bg-element', hex)
|
|
34
|
+
document.documentElement.style.setProperty('--bg-element-rgb', toRgbTriplet(hex))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface ThemeContextValue {
|
|
38
|
+
accent: string
|
|
39
|
+
setAccent: (value: string) => void
|
|
40
|
+
background: string
|
|
41
|
+
setBackground: (value: string) => void
|
|
42
|
+
elementColor: string
|
|
43
|
+
setElementColor: (value: string) => void
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const ThemeContext = createContext<ThemeContextValue>({
|
|
47
|
+
accent: ACCENT_DEFAULT,
|
|
48
|
+
setAccent: () => { },
|
|
49
|
+
background: BACKGROUND_DEFAULT,
|
|
50
|
+
setBackground: () => { },
|
|
51
|
+
elementColor: ELEMENT_DEFAULT,
|
|
52
|
+
setElementColor: () => { },
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
export function ThemeProvider({
|
|
56
|
+
children,
|
|
57
|
+
isAuthenticated,
|
|
58
|
+
defaultAccent,
|
|
59
|
+
defaultBackground,
|
|
60
|
+
defaultElementColor,
|
|
61
|
+
storagePrefix,
|
|
62
|
+
}: {
|
|
63
|
+
children: ReactNode
|
|
64
|
+
isAuthenticated?: boolean
|
|
65
|
+
defaultAccent?: string
|
|
66
|
+
defaultBackground?: string
|
|
67
|
+
defaultElementColor?: string
|
|
68
|
+
storagePrefix?: string
|
|
69
|
+
}) {
|
|
70
|
+
const accentKey = storagePrefix ? `${storagePrefix}:accent-color` : ACCENT_KEY
|
|
71
|
+
const bgKey = storagePrefix ? `${storagePrefix}:background-color` : BG_KEY
|
|
72
|
+
const elementKey = storagePrefix ? `${storagePrefix}:element-color` : ELEMENT_COLOR_KEY
|
|
73
|
+
|
|
74
|
+
const [accent, setAccentState] = useState<string>(
|
|
75
|
+
() => localStorage.getItem(accentKey) ?? defaultAccent ?? ACCENT_DEFAULT,
|
|
76
|
+
)
|
|
77
|
+
const [background, setBackgroundState] = useState<string>(
|
|
78
|
+
() => localStorage.getItem(bgKey) ?? defaultBackground ?? BACKGROUND_DEFAULT,
|
|
79
|
+
)
|
|
80
|
+
const [elementColor, setElementColorState] = useState<string>(
|
|
81
|
+
() => localStorage.getItem(elementKey) ?? defaultElementColor ?? ELEMENT_DEFAULT,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
// Apply CSS vars whenever accent or background changes
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
applyAccentVars(accent)
|
|
87
|
+
}, [accent])
|
|
88
|
+
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
applyBgVars(background)
|
|
91
|
+
}, [background])
|
|
92
|
+
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
applyElementVars(elementColor)
|
|
95
|
+
}, [elementColor])
|
|
96
|
+
|
|
97
|
+
// Fetch server preferences only when authenticated and NOT in namespaced/demo mode
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
if (!isAuthenticated || storagePrefix) return
|
|
100
|
+
api.user.getPreferences().then((prefs) => {
|
|
101
|
+
if (prefs.accent_color && prefs.accent_color !== accent) {
|
|
102
|
+
localStorage.setItem(accentKey, prefs.accent_color)
|
|
103
|
+
setAccentState(prefs.accent_color)
|
|
104
|
+
}
|
|
105
|
+
if (prefs.background_color && prefs.background_color !== background) {
|
|
106
|
+
localStorage.setItem(bgKey, prefs.background_color)
|
|
107
|
+
setBackgroundState(prefs.background_color)
|
|
108
|
+
}
|
|
109
|
+
if (prefs.element_color && prefs.element_color !== elementColor) {
|
|
110
|
+
localStorage.setItem(elementKey, prefs.element_color)
|
|
111
|
+
setElementColorState(prefs.element_color)
|
|
112
|
+
}
|
|
113
|
+
}).catch(() => { })
|
|
114
|
+
}, [isAuthenticated, storagePrefix]) // eslint-disable-line react-hooks/exhaustive-deps
|
|
115
|
+
|
|
116
|
+
function setAccent(value: string) {
|
|
117
|
+
localStorage.setItem(accentKey, value)
|
|
118
|
+
setAccentState(value)
|
|
119
|
+
if (!storagePrefix) {
|
|
120
|
+
api.user.updatePreferences({ accent_color: value }).catch(() => { })
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function setBackground(value: string) {
|
|
125
|
+
localStorage.setItem(bgKey, value)
|
|
126
|
+
setBackgroundState(value)
|
|
127
|
+
if (!storagePrefix) {
|
|
128
|
+
api.user.updatePreferences({ background_color: value }).catch(() => { })
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function setElementColor(value: string) {
|
|
133
|
+
localStorage.setItem(elementKey, value)
|
|
134
|
+
setElementColorState(value)
|
|
135
|
+
if (!storagePrefix) {
|
|
136
|
+
api.user.updatePreferences({ element_color: value }).catch(() => { })
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<ThemeContext.Provider value={{ accent, setAccent, background, setBackground, elementColor, setElementColor }}>
|
|
142
|
+
{children}
|
|
143
|
+
</ThemeContext.Provider>
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function useTheme() {
|
|
148
|
+
return useContext(ThemeContext)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Backward compatibility alias
|
|
153
|
+
* @deprecated Use useTheme() instead
|
|
154
|
+
*/
|
|
155
|
+
export function useAccentColor() {
|
|
156
|
+
const { accent, setAccent } = useTheme()
|
|
157
|
+
return { accent, setAccent }
|
|
158
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import type { Connector, ExploreData, PlacedElement, ViewTreeNode } from '../types'
|
|
2
|
+
import type { GraphPlacementRef, WorkspaceGraphSnapshot } from './types'
|
|
3
|
+
|
|
4
|
+
function cloneViewTree(nodes: ViewTreeNode[]): ViewTreeNode[] {
|
|
5
|
+
return nodes.map((node) => ({
|
|
6
|
+
...node,
|
|
7
|
+
children: cloneViewTree(node.children ?? []),
|
|
8
|
+
}))
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function flattenTree(nodes: ViewTreeNode[], out: ViewTreeNode[] = []): ViewTreeNode[] {
|
|
12
|
+
for (const node of nodes) {
|
|
13
|
+
out.push(node)
|
|
14
|
+
flattenTree(node.children ?? [], out)
|
|
15
|
+
}
|
|
16
|
+
return out
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function collectDescendants(viewById: Record<number, ViewTreeNode>, parentId: number): number[] {
|
|
20
|
+
const result: number[] = []
|
|
21
|
+
const visit = (viewId: number) => {
|
|
22
|
+
result.push(viewId)
|
|
23
|
+
const node = viewById[viewId]
|
|
24
|
+
for (const child of node?.children ?? []) visit(child.id)
|
|
25
|
+
}
|
|
26
|
+
visit(parentId)
|
|
27
|
+
return result
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function viewNameOf(viewById: Record<number, ViewTreeNode>, viewId: number): string {
|
|
31
|
+
return viewById[viewId]?.name ?? `View ${viewId}`
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function deepCloneExploreData(data: ExploreData): ExploreData {
|
|
35
|
+
return {
|
|
36
|
+
tree: cloneViewTree(data.tree ?? []),
|
|
37
|
+
navigations: [...(data.navigations ?? [])],
|
|
38
|
+
views: Object.fromEntries(
|
|
39
|
+
Object.entries(data.views ?? {}).map(([key, value]) => [
|
|
40
|
+
key,
|
|
41
|
+
{
|
|
42
|
+
placements: [...(value.placements ?? [])],
|
|
43
|
+
connectors: [...(value.connectors ?? [])],
|
|
44
|
+
},
|
|
45
|
+
]),
|
|
46
|
+
),
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function buildWorkspaceGraphSnapshot(data: ExploreData): WorkspaceGraphSnapshot {
|
|
51
|
+
const tree = cloneViewTree(data.tree ?? [])
|
|
52
|
+
const flatTree = flattenTree(tree)
|
|
53
|
+
const viewById = Object.fromEntries(flatTree.map((view) => [view.id, view])) as Record<number, ViewTreeNode>
|
|
54
|
+
|
|
55
|
+
const placementsByViewId: Record<number, PlacedElement[]> = {}
|
|
56
|
+
const connectorsByViewId: Record<number, Connector[]> = {}
|
|
57
|
+
const placementsByElementId: Record<number, GraphPlacementRef[]> = {}
|
|
58
|
+
|
|
59
|
+
for (const view of flatTree) {
|
|
60
|
+
const content = data.views?.[String(view.id)]
|
|
61
|
+
const placements = [...(content?.placements ?? [])]
|
|
62
|
+
const connectors = [...(content?.connectors ?? [])]
|
|
63
|
+
placementsByViewId[view.id] = placements
|
|
64
|
+
connectorsByViewId[view.id] = connectors
|
|
65
|
+
|
|
66
|
+
for (const placement of placements) {
|
|
67
|
+
placementsByElementId[placement.element_id] ??= []
|
|
68
|
+
placementsByElementId[placement.element_id].push({
|
|
69
|
+
viewId: view.id,
|
|
70
|
+
viewName: view.name,
|
|
71
|
+
element: placement,
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const childViewIdByOwnerElementId = flatTree.reduce<Record<number, number>>((acc, view) => {
|
|
77
|
+
if (view.owner_element_id != null) acc[view.owner_element_id] = view.id
|
|
78
|
+
return acc
|
|
79
|
+
}, {})
|
|
80
|
+
|
|
81
|
+
const ancestorsByViewId = flatTree.reduce<Record<number, number[]>>((acc, view) => {
|
|
82
|
+
const lineage: number[] = []
|
|
83
|
+
let cursor: ViewTreeNode | undefined = view
|
|
84
|
+
while (cursor) {
|
|
85
|
+
lineage.push(cursor.id)
|
|
86
|
+
cursor = cursor.parent_view_id != null ? viewById[cursor.parent_view_id] : undefined
|
|
87
|
+
}
|
|
88
|
+
acc[view.id] = lineage.reverse()
|
|
89
|
+
return acc
|
|
90
|
+
}, {})
|
|
91
|
+
|
|
92
|
+
const descendantsByViewId = flatTree.reduce<Record<number, number[]>>((acc, view) => {
|
|
93
|
+
acc[view.id] = collectDescendants(viewById, view.id)
|
|
94
|
+
return acc
|
|
95
|
+
}, {})
|
|
96
|
+
|
|
97
|
+
const views = flatTree.reduce<Record<number, { view: ViewTreeNode; placements: PlacedElement[]; connectors: Connector[] }>>((acc, view) => {
|
|
98
|
+
acc[view.id] = {
|
|
99
|
+
view,
|
|
100
|
+
placements: placementsByViewId[view.id] ?? [],
|
|
101
|
+
connectors: connectorsByViewId[view.id] ?? [],
|
|
102
|
+
}
|
|
103
|
+
return acc
|
|
104
|
+
}, {})
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
source: deepCloneExploreData(data),
|
|
108
|
+
tree,
|
|
109
|
+
views,
|
|
110
|
+
viewById,
|
|
111
|
+
placementsByViewId,
|
|
112
|
+
connectorsByViewId,
|
|
113
|
+
placementsByElementId,
|
|
114
|
+
childViewIdByOwnerElementId,
|
|
115
|
+
descendantsByViewId,
|
|
116
|
+
ancestorsByViewId,
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function isDescendantView(snapshot: WorkspaceGraphSnapshot, maybeDescendantId: number | null | undefined, ancestorId: number | null | undefined): boolean {
|
|
121
|
+
if (maybeDescendantId == null || ancestorId == null) return false
|
|
122
|
+
return snapshot.ancestorsByViewId[maybeDescendantId]?.includes(ancestorId) ?? false
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function findLowestCommonAncestorViewId(snapshot: WorkspaceGraphSnapshot, leftViewId: number | null | undefined, rightViewId: number | null | undefined): number | null {
|
|
126
|
+
if (leftViewId == null || rightViewId == null) return null
|
|
127
|
+
const leftAncestors = snapshot.ancestorsByViewId[leftViewId] ?? []
|
|
128
|
+
const rightAncestors = new Set(snapshot.ancestorsByViewId[rightViewId] ?? [])
|
|
129
|
+
let best: number | null = null
|
|
130
|
+
for (const candidate of leftAncestors) {
|
|
131
|
+
if (rightAncestors.has(candidate)) best = candidate
|
|
132
|
+
}
|
|
133
|
+
return best
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function relativeOwnerElementPath(snapshot: WorkspaceGraphSnapshot, ancestorViewId: number, descendantViewId: number): number[] {
|
|
137
|
+
const descendantAncestors = snapshot.ancestorsByViewId[descendantViewId] ?? []
|
|
138
|
+
const ancestorIndex = descendantAncestors.indexOf(ancestorViewId)
|
|
139
|
+
if (ancestorIndex === -1) return []
|
|
140
|
+
const relativeViewIds = descendantAncestors.slice(ancestorIndex + 1)
|
|
141
|
+
return relativeViewIds
|
|
142
|
+
.map((viewId) => snapshot.viewById[viewId]?.owner_element_id ?? null)
|
|
143
|
+
.filter((elementId): elementId is number => elementId != null)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function viewName(snapshot: WorkspaceGraphSnapshot, viewId: number | null | undefined): string | null {
|
|
147
|
+
if (viewId == null) return null
|
|
148
|
+
return viewNameOf(snapshot.viewById, viewId)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function allConnectors(snapshot: WorkspaceGraphSnapshot): Connector[] {
|
|
152
|
+
return Object.values(snapshot.connectorsByViewId).flat()
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function upsertConnectorInSnapshot(snapshot: WorkspaceGraphSnapshot | null, connector: Connector): WorkspaceGraphSnapshot | null {
|
|
156
|
+
if (!snapshot) return snapshot
|
|
157
|
+
const next = deepCloneExploreData(snapshot.source)
|
|
158
|
+
const viewKey = String(connector.view_id)
|
|
159
|
+
next.views[viewKey] ??= { placements: [], connectors: [] }
|
|
160
|
+
next.views[viewKey].connectors = [
|
|
161
|
+
connector,
|
|
162
|
+
...next.views[viewKey].connectors.filter((existing) => existing.id !== connector.id),
|
|
163
|
+
]
|
|
164
|
+
return buildWorkspaceGraphSnapshot(next)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function removeConnectorFromSnapshot(snapshot: WorkspaceGraphSnapshot | null, viewId: number, connectorId: number): WorkspaceGraphSnapshot | null {
|
|
168
|
+
if (!snapshot) return snapshot
|
|
169
|
+
const next = deepCloneExploreData(snapshot.source)
|
|
170
|
+
const viewKey = String(viewId)
|
|
171
|
+
if (next.views[viewKey]) {
|
|
172
|
+
next.views[viewKey].connectors = next.views[viewKey].connectors.filter((connector) => connector.id !== connectorId)
|
|
173
|
+
}
|
|
174
|
+
return buildWorkspaceGraphSnapshot(next)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function upsertPlacementInSnapshot(snapshot: WorkspaceGraphSnapshot | null, viewId: number, placement: PlacedElement): WorkspaceGraphSnapshot | null {
|
|
178
|
+
if (!snapshot) return snapshot
|
|
179
|
+
const next = deepCloneExploreData(snapshot.source)
|
|
180
|
+
const viewKey = String(viewId)
|
|
181
|
+
next.views[viewKey] ??= { placements: [], connectors: [] }
|
|
182
|
+
next.views[viewKey].placements = [
|
|
183
|
+
placement,
|
|
184
|
+
...next.views[viewKey].placements.filter((existing) => existing.element_id !== placement.element_id),
|
|
185
|
+
]
|
|
186
|
+
return buildWorkspaceGraphSnapshot(next)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function removePlacementFromSnapshot(snapshot: WorkspaceGraphSnapshot | null, viewId: number, elementId: number): WorkspaceGraphSnapshot | null {
|
|
190
|
+
if (!snapshot) return snapshot
|
|
191
|
+
const next = deepCloneExploreData(snapshot.source)
|
|
192
|
+
const viewKey = String(viewId)
|
|
193
|
+
if (next.views[viewKey]) {
|
|
194
|
+
next.views[viewKey].placements = next.views[viewKey].placements.filter((placement) => placement.element_id !== elementId)
|
|
195
|
+
}
|
|
196
|
+
return buildWorkspaceGraphSnapshot(next)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function overrideViewContentInSnapshot(snapshot: WorkspaceGraphSnapshot | null, viewId: number, placements: PlacedElement[], connectors: Connector[]): WorkspaceGraphSnapshot | null {
|
|
200
|
+
if (!snapshot) return snapshot
|
|
201
|
+
const next = deepCloneExploreData(snapshot.source)
|
|
202
|
+
next.views[String(viewId)] = {
|
|
203
|
+
placements: [...placements],
|
|
204
|
+
connectors: [...connectors],
|
|
205
|
+
}
|
|
206
|
+
return buildWorkspaceGraphSnapshot(next)
|
|
207
|
+
}
|