symbiote-ui 0.3.0-alpha.4
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/CHANGELOG.md +46 -0
- package/LICENSE +21 -0
- package/README.md +76 -0
- package/canvas/AutoLayout.js +731 -0
- package/canvas/Breadcrumb/Breadcrumb.css.js +75 -0
- package/canvas/Breadcrumb/Breadcrumb.js +96 -0
- package/canvas/Breadcrumb/Breadcrumb.tpl.js +7 -0
- package/canvas/CanvasConnectionRenderer.js +971 -0
- package/canvas/CanvasGraph/CanvasGraph.css.js +29 -0
- package/canvas/CanvasGraph/CanvasGraph.js +1697 -0
- package/canvas/CanvasGraph/CanvasGraphDrawState.js +280 -0
- package/canvas/CanvasGraph/CanvasGraphGeometry.js +194 -0
- package/canvas/CanvasViewport.js +550 -0
- package/canvas/ConnectionRenderer.js +1283 -0
- package/canvas/FlowSimulator.js +326 -0
- package/canvas/ForceLayout.js +226 -0
- package/canvas/ForceWorker.js +1303 -0
- package/canvas/FrameManager.js +223 -0
- package/canvas/GraphExplorerShell/GraphExplorerShell.css.js +136 -0
- package/canvas/GraphExplorerShell/GraphExplorerShell.js +129 -0
- package/canvas/GraphExplorerShell/GraphExplorerShell.tpl.js +12 -0
- package/canvas/GraphTabs/GraphTabs.css.js +101 -0
- package/canvas/GraphTabs/GraphTabs.js +189 -0
- package/canvas/GraphTabs/GraphTabs.tpl.js +12 -0
- package/canvas/LODManager.js +88 -0
- package/canvas/Minimap/Minimap.css.js +73 -0
- package/canvas/Minimap/Minimap.js +210 -0
- package/canvas/Minimap/Minimap.tpl.js +7 -0
- package/canvas/NodeCanvas/NodeCanvas.css.js +398 -0
- package/canvas/NodeCanvas/NodeCanvas.js +1499 -0
- package/canvas/NodeCanvas/NodeCanvas.tpl.js +22 -0
- package/canvas/NodeSearch/NodeSearch.css.js +97 -0
- package/canvas/NodeSearch/NodeSearch.js +140 -0
- package/canvas/NodeSearch/NodeSearch.tpl.js +25 -0
- package/canvas/NodeViewManager.js +748 -0
- package/canvas/PcbRouteDiagnostics.js +463 -0
- package/canvas/PcbRouter.js +1127 -0
- package/canvas/PinExpansion.js +134 -0
- package/canvas/PseudoConnection.js +84 -0
- package/canvas/SelectionSync.js +163 -0
- package/canvas/SubgraphManager.js +203 -0
- package/canvas/SubgraphRouter.js +452 -0
- package/canvas/ViewportActions.js +473 -0
- package/canvas/graph-explorer.js +339 -0
- package/canvas/graph-layout.js +148 -0
- package/canvas/graph-model.js +68 -0
- package/canvas/html-in-canvas.js +202 -0
- package/canvas/project-graph-builder.js +440 -0
- package/canvas/project-graph-model.js +183 -0
- package/chat/ChatComposer/ChatComposer.css.js +652 -0
- package/chat/ChatComposer/ChatComposer.js +304 -0
- package/chat/ChatList/ChatList.css.js +102 -0
- package/chat/ChatList/ChatList.js +99 -0
- package/chat/ChatList/ChatList.tpl.js +20 -0
- package/chat/ChatListItem/ChatListItem.css.js +117 -0
- package/chat/ChatListItem/ChatListItem.js +32 -0
- package/chat/ChatListItem/ChatListItem.tpl.js +17 -0
- package/chat/ChatMessageItem/ChatMessageItem.css.js +628 -0
- package/chat/ChatMessageItem/ChatMessageItem.js +156 -0
- package/chat/ChatSidebar/ChatSidebar.css.js +150 -0
- package/chat/ChatSidebar/ChatSidebar.js +230 -0
- package/chat/ChatSidebar/ChatSidebar.tpl.js +18 -0
- package/chat/ChatSidebar/constants.js +11 -0
- package/chat/ChatSidebarItem/ChatSidebarItem.css.js +445 -0
- package/chat/ChatSidebarItem/ChatSidebarItem.js +304 -0
- package/chat/ChatTranscript/ChatTranscript.css.js +90 -0
- package/chat/ChatTranscript/ChatTranscript.js +244 -0
- package/chat/chat-context.js +123 -0
- package/chat/message-model.js +156 -0
- package/cli.js +20 -0
- package/control/Button/Button.css.js +93 -0
- package/control/Button/Button.js +78 -0
- package/control/Button/Button.tpl.js +3 -0
- package/control/Field/Field.css.js +91 -0
- package/control/Field/Field.js +17 -0
- package/control/Field/Field.tpl.js +3 -0
- package/core/Connection.js +47 -0
- package/core/Editor.js +449 -0
- package/core/Frame.js +33 -0
- package/core/GraphMermaid.js +348 -0
- package/core/GraphText.js +228 -0
- package/core/Node.js +145 -0
- package/core/Portal.js +106 -0
- package/core/Socket.js +187 -0
- package/core/SubgraphNode.js +121 -0
- package/core/base-path.js +55 -0
- package/core/dom-utils.js +14 -0
- package/core/index.js +18 -0
- package/core/local-cache.js +26 -0
- package/core/state-sync.js +227 -0
- package/custom-elements.json +6380 -0
- package/discover.js +240 -0
- package/display/Badge/Badge.css.js +44 -0
- package/display/Badge/Badge.js +17 -0
- package/display/Badge/Badge.tpl.js +3 -0
- package/display/Banner/Banner.css.js +61 -0
- package/display/Banner/Banner.js +17 -0
- package/display/Banner/Banner.tpl.js +3 -0
- package/display/CodeBlock/CodeBlock.css.js +194 -0
- package/display/CodeBlock/CodeBlock.js +220 -0
- package/display/CodeBlock/CodeBlock.tpl.js +11 -0
- package/display/DataTable/DataTable.css.js +101 -0
- package/display/DataTable/DataTable.js +136 -0
- package/display/DataTable/DataTable.tpl.js +13 -0
- package/display/EmptyState/EmptyState.css.js +33 -0
- package/display/EmptyState/EmptyState.js +17 -0
- package/display/EmptyState/EmptyState.tpl.js +3 -0
- package/display/EventFeed/EventFeed.css.js +145 -0
- package/display/EventFeed/EventFeed.js +64 -0
- package/display/EventFeed/EventFeed.tpl.js +14 -0
- package/display/EventFeed/EventFeedItem.js +116 -0
- package/display/EventFeed/EventFeedItem.tpl.js +22 -0
- package/display/LoadingOverlay/LoadingOverlay.css.js +91 -0
- package/display/LoadingOverlay/LoadingOverlay.js +48 -0
- package/display/LoadingOverlay/LoadingOverlay.tpl.js +12 -0
- package/display/Metric/Metric.css.js +60 -0
- package/display/Metric/Metric.js +17 -0
- package/display/Metric/Metric.tpl.js +6 -0
- package/display/OutputGraphPreview/OutputGraphPreview.css.js +122 -0
- package/display/OutputGraphPreview/OutputGraphPreview.js +89 -0
- package/display/OutputGraphPreview/OutputGraphPreview.tpl.js +13 -0
- package/display/OutputListPreview/OutputListPreview.css.js +109 -0
- package/display/OutputListPreview/OutputListPreview.js +77 -0
- package/display/OutputListPreview/OutputListPreview.tpl.js +13 -0
- package/display/SourceEditor/SourceEditor.css.js +39 -0
- package/display/SourceEditor/SourceEditor.js +129 -0
- package/display/SourceEditor/SourceEditor.tpl.js +10 -0
- package/display/SourceViewer/SourceViewer.css.js +80 -0
- package/display/SourceViewer/SourceViewer.js +418 -0
- package/display/SourceViewer/SourceViewer.tpl.js +17 -0
- package/display/StatusRibbon/StatusRibbon.css.js +73 -0
- package/display/StatusRibbon/StatusRibbon.js +87 -0
- package/display/StatusRibbon/StatusRibbon.tpl.js +7 -0
- package/display/event-feed-adapter.js +72 -0
- package/display/format-utils.js +29 -0
- package/display/highlight.js +659 -0
- package/display/icons.js +37 -0
- package/display/markdown-formatter.js +60 -0
- package/display/network-approval-page.js +487 -0
- package/display/output-preview.js +261 -0
- package/effects/CellBg/CellBg.css.js +33 -0
- package/effects/CellBg/CellBg.js +410 -0
- package/effects/CellBg/CellBg.tpl.js +5 -0
- package/graph/canvas-adapter.js +223 -0
- package/graph/graph-algorithms.js +31 -0
- package/graph/index.js +46 -0
- package/graph/model.js +176 -0
- package/graph/project-graph-build.js +66 -0
- package/graph/project-graph-metadata.js +253 -0
- package/graph/project-package.js +128 -0
- package/graph/project-runtime.js +116 -0
- package/graph/project-transaction.js +284 -0
- package/graph/skeleton-utils.js +84 -0
- package/graph/theme-contract.js +36 -0
- package/graph/transaction-parser.js +56 -0
- package/icons/MaterialSymbols.js +69 -0
- package/icons/material-symbols-outlined-400.ttf +0 -0
- package/icons/material-symbols.css +24 -0
- package/index.js +95 -0
- package/inspector/InspectorPanel/InspectorPanel.css.js +375 -0
- package/inspector/InspectorPanel/InspectorPanel.js +368 -0
- package/inspector/InspectorPanel/InspectorPanel.tpl.js +96 -0
- package/inspector/TemplatePreview/TemplatePreview.css.js +104 -0
- package/inspector/TemplatePreview/TemplatePreview.js +145 -0
- package/inspector/TemplatePreview/TemplatePreview.tpl.js +33 -0
- package/interactions/ConnectFlow.js +304 -0
- package/interactions/Drag.js +104 -0
- package/interactions/Selector.js +133 -0
- package/interactions/SnapGrid.js +66 -0
- package/interactions/Zoom.js +139 -0
- package/layout/ActionZone/ActionZone.css.js +88 -0
- package/layout/ActionZone/ActionZone.js +261 -0
- package/layout/ActionZone/ActionZone.tpl.js +11 -0
- package/layout/CrossLayoutPortalBridge/CrossLayoutPortalBridge.js +255 -0
- package/layout/Layout/Layout.css.js +91 -0
- package/layout/Layout/Layout.js +637 -0
- package/layout/Layout/Layout.tpl.js +27 -0
- package/layout/LayoutNode/LayoutNode.css.js +302 -0
- package/layout/LayoutNode/LayoutNode.js +509 -0
- package/layout/LayoutNode/LayoutNode.tpl.js +39 -0
- package/layout/LayoutPreview/LayoutPreview.css.js +46 -0
- package/layout/LayoutPreview/LayoutPreview.js +102 -0
- package/layout/LayoutPreview/LayoutPreview.tpl.js +6 -0
- package/layout/LayoutRouter/LayoutRouter.js +274 -0
- package/layout/LayoutRouter/SectionRegistry.js +135 -0
- package/layout/LayoutRouter/routerSync.js +250 -0
- package/layout/LayoutSidebar/LayoutSidebar.css.js +411 -0
- package/layout/LayoutSidebar/LayoutSidebar.js +368 -0
- package/layout/LayoutSidebar/LayoutSidebar.tpl.js +26 -0
- package/layout/LayoutSidebar/SidebarSection.css.js +20 -0
- package/layout/LayoutSidebar/SidebarSection.js +184 -0
- package/layout/LayoutSidebar/SidebarSection.tpl.js +22 -0
- package/layout/LayoutTree.js +373 -0
- package/layout/PanelMenu/PanelMenu.css.js +43 -0
- package/layout/PanelMenu/PanelMenu.js +95 -0
- package/layout/PanelMenu/PanelMenu.tpl.js +17 -0
- package/layout/ProjectTabs/ProjectTabs.css.js +188 -0
- package/layout/ProjectTabs/ProjectTabs.js +77 -0
- package/layout/ProjectTabs/ProjectTabs.tpl.js +15 -0
- package/layout/index.js +40 -0
- package/list/ListDetailShell/ListDetailShell.css.js +128 -0
- package/list/ListDetailShell/ListDetailShell.js +72 -0
- package/list/ListDetailShell/ListDetailShell.tpl.js +36 -0
- package/list/ListItem/ListItem.css.js +111 -0
- package/list/ListItem/ListItem.js +66 -0
- package/list/ListItem/ListItem.tpl.js +18 -0
- package/locale/index.js +503 -0
- package/manifest/component-registry.js +2446 -0
- package/manifest/graph-schema.js +285 -0
- package/manifest/index.js +6 -0
- package/manifest/project-schema-catalog.js +246 -0
- package/manifest/rule-catalog.js +201 -0
- package/manifest/theme-catalog.js +2149 -0
- package/manifest/ui-schema-catalog.js +334 -0
- package/menu/ContextMenu/ContextMenu.css.js +61 -0
- package/menu/ContextMenu/ContextMenu.js +82 -0
- package/menu/ContextMenu/ContextMenu.tpl.js +19 -0
- package/navigation/QuickOpen/QuickOpen.css.js +92 -0
- package/navigation/QuickOpen/QuickOpen.js +185 -0
- package/navigation/QuickOpen/QuickOpen.tpl.js +15 -0
- package/navigation/quick-open-utils.js +101 -0
- package/node/CtrlItem/CtrlItem.css.js +41 -0
- package/node/CtrlItem/CtrlItem.js +24 -0
- package/node/CtrlItem/CtrlItem.tpl.js +17 -0
- package/node/GraphFrame/GraphFrame.css.js +66 -0
- package/node/GraphFrame/GraphFrame.js +32 -0
- package/node/GraphFrame/GraphFrame.tpl.js +13 -0
- package/node/GraphNode/GraphNode.css.js +815 -0
- package/node/GraphNode/GraphNode.js +173 -0
- package/node/GraphNode/GraphNode.tpl.js +33 -0
- package/node/NodeCallout/NodeCallout.css.js +91 -0
- package/node/NodeCallout/NodeCallout.js +281 -0
- package/node/NodeCallout/NodeCallout.tpl.js +8 -0
- package/node/NodeSocket/NodeSocket.css.js +68 -0
- package/node/NodeSocket/NodeSocket.js +26 -0
- package/node/NodeSocket/NodeSocket.tpl.js +7 -0
- package/node/PortItem/PortItem.css.js +93 -0
- package/node/PortItem/PortItem.js +87 -0
- package/node/PortItem/PortItem.tpl.js +10 -0
- package/package.json +165 -0
- package/palette/PaletteBrowser/PaletteBrowser.css.js +143 -0
- package/palette/PaletteBrowser/PaletteBrowser.js +152 -0
- package/palette/PaletteBrowser/PaletteBrowser.tpl.js +23 -0
- package/plugins/History.js +408 -0
- package/plugins/Readonly.js +60 -0
- package/rules/symbiote-3x.json +170 -0
- package/schemas/component-descriptor-v1.json +91 -0
- package/schemas/component-descriptor-v2.json +145 -0
- package/schemas/graph-model-v1.json +179 -0
- package/schemas/graph-v1.json +91 -0
- package/schemas/project-package-v1.json +102 -0
- package/schemas/project-transaction-v1.json +114 -0
- package/schemas/runtime-ui-v1.json +80 -0
- package/schemas/theme-rule-block-v1.json +73 -0
- package/shapes/CircleShape.js +79 -0
- package/shapes/CommentShape.js +35 -0
- package/shapes/DiamondShape.js +130 -0
- package/shapes/NodeShape.js +79 -0
- package/shapes/PillShape.js +91 -0
- package/shapes/RectShape.js +84 -0
- package/shapes/SVGShape.js +525 -0
- package/shapes/index.js +63 -0
- package/surface/Card/Card.css.js +57 -0
- package/surface/Card/Card.js +17 -0
- package/surface/Card/Card.tpl.js +3 -0
- package/themes/Palette.js +30 -0
- package/themes/Skin.js +113 -0
- package/themes/Theme.js +82 -0
- package/themes/carbon.js +135 -0
- package/themes/dark.js +140 -0
- package/themes/default-dark.js +714 -0
- package/themes/default-provider.css +635 -0
- package/themes/default-provider.js +718 -0
- package/themes/ebook.js +136 -0
- package/themes/grey.js +137 -0
- package/themes/light.js +139 -0
- package/themes/neon.js +138 -0
- package/themes/pcb.js +273 -0
- package/themes/synthwave.js +138 -0
- package/tokens/base.json +29 -0
- package/tokens/themes/carbon.json +11 -0
- package/tokens/themes/dark.json +12 -0
- package/tokens/themes/default-dark.json +1543 -0
- package/tokens/themes/default-provider.json +1543 -0
- package/tokens/themes/ebook.json +11 -0
- package/tokens/themes/grey.json +11 -0
- package/tokens/themes/light.json +12 -0
- package/tokens/themes/neon.json +11 -0
- package/tokens/themes/pcb.json +11 -0
- package/tokens/themes/synthwave.json +11 -0
- package/toolbar/QuickToolbar/QuickToolbar.css.js +152 -0
- package/toolbar/QuickToolbar/QuickToolbar.js +529 -0
- package/toolbar/QuickToolbar/QuickToolbar.tpl.js +34 -0
- package/tree/TreePanel/TreePanel.css.js +112 -0
- package/tree/TreePanel/TreePanel.js +147 -0
- package/tree/TreePanel/TreePanel.tpl.js +18 -0
- package/tree/TreeView/TreeView.css.js +122 -0
- package/tree/TreeView/TreeView.js +365 -0
- package/tree/TreeView/TreeView.tpl.js +10 -0
- package/ui/dialogs.js +221 -0
- package/ui/host-adapters.js +114 -0
- package/ui/index.js +660 -0
- package/ui/locale.js +50 -0
- package/ui/overlay-stack.js +89 -0
- package/ui/shared-styles.js +26 -0
- package/webmcp.js +37 -0
- package/xr/deep-graph.js +646 -0
- package/xr/emulation.js +198 -0
- package/xr/gesture.js +228 -0
- package/xr/html-canvas-renderer.js +472 -0
- package/xr/index.js +15 -0
- package/xr/layout-projection.js +1046 -0
- package/xr/panel-frame.js +128 -0
- package/xr/panel-host.js +267 -0
- package/xr/pointer.js +258 -0
- package/xr/scene-controller.js +242 -0
- package/xr/spatial-scene.js +212 -0
- package/xr/theme-bridge.js +105 -0
- package/xr/three-webxr-adapter.js +3439 -0
- package/xr/webgl-layer-renderer.js +419 -0
- package/xr/webxr.js +679 -0
- package/xr/workbench.js +516 -0
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
export const GRAPH_PATH_STYLES = ['pcb', 'bezier', 'orthogonal', 'straight'];
|
|
2
|
+
|
|
3
|
+
export const GRAPH_DIRECTORY_FRAME_COLORS = [
|
|
4
|
+
'var(--sn-graph-cluster-0)',
|
|
5
|
+
'var(--sn-graph-cluster-1)',
|
|
6
|
+
'var(--sn-graph-cluster-2)',
|
|
7
|
+
'var(--sn-graph-cluster-3)',
|
|
8
|
+
'var(--sn-graph-cluster-4)',
|
|
9
|
+
'var(--sn-graph-cluster-5)',
|
|
10
|
+
'var(--sn-graph-cluster-6)',
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
export function resolveInitialGraphViewMode(urlParams) {
|
|
14
|
+
const modeParam = urlParams.get('mode');
|
|
15
|
+
return modeParam === 'flat' ? 'flat' : 'structured';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function setGraphButtonVisual(button, icon, label) {
|
|
19
|
+
const iconElement = button.querySelector?.('.material-symbols-outlined');
|
|
20
|
+
if (!iconElement) {
|
|
21
|
+
button.textContent = label;
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
iconElement.textContent = icon;
|
|
26
|
+
const labelNode = [...(button.childNodes || [])]
|
|
27
|
+
.find((node) => node !== iconElement && node.nodeType === 3 && node.textContent.trim());
|
|
28
|
+
|
|
29
|
+
if (labelNode) {
|
|
30
|
+
labelNode.textContent = ` ${label}`;
|
|
31
|
+
} else {
|
|
32
|
+
button.append?.(` ${label}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function renderGraphViewModeButton(button, viewMode) {
|
|
37
|
+
if (!button) return;
|
|
38
|
+
const label = viewMode === 'flat' ? 'FLAT' : 'TREE';
|
|
39
|
+
const icon = viewMode === 'flat' ? 'account_tree' : 'grid_view';
|
|
40
|
+
setGraphButtonVisual(button, icon, label);
|
|
41
|
+
if (viewMode === 'structured') {
|
|
42
|
+
button.setAttribute('data-active', '');
|
|
43
|
+
} else {
|
|
44
|
+
button.removeAttribute('data-active');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function getNextGraphPathStyle(currentStyle) {
|
|
49
|
+
const index = GRAPH_PATH_STYLES.indexOf(currentStyle);
|
|
50
|
+
return GRAPH_PATH_STYLES[(index + 1) % GRAPH_PATH_STYLES.length] || 'pcb';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function getGraphPathStyleDisplay(style) {
|
|
54
|
+
switch (style) {
|
|
55
|
+
case 'bezier':
|
|
56
|
+
return { icon: 'timeline', text: 'BEZIER', active: false };
|
|
57
|
+
case 'orthogonal':
|
|
58
|
+
return { icon: 'polyline', text: 'ORTHO', active: false };
|
|
59
|
+
case 'straight':
|
|
60
|
+
return { icon: 'horizontal_rule', text: 'STRAIGHT', active: false };
|
|
61
|
+
case 'pcb':
|
|
62
|
+
default:
|
|
63
|
+
return { icon: 'route', text: 'PCB', active: true };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function renderGraphPathStyleButton(button, style) {
|
|
68
|
+
if (!button) return;
|
|
69
|
+
const { icon, text, active } = getGraphPathStyleDisplay(style);
|
|
70
|
+
setGraphButtonVisual(button, icon, text);
|
|
71
|
+
if (active) {
|
|
72
|
+
button.setAttribute('data-active', '');
|
|
73
|
+
} else {
|
|
74
|
+
button.removeAttribute('data-active');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function addGraphDirectoryFrames({
|
|
79
|
+
editor,
|
|
80
|
+
fileMap,
|
|
81
|
+
dirFiles,
|
|
82
|
+
positions,
|
|
83
|
+
FrameClass,
|
|
84
|
+
colors = GRAPH_DIRECTORY_FRAME_COLORS,
|
|
85
|
+
}) {
|
|
86
|
+
if (!dirFiles || dirFiles.size < 2) return;
|
|
87
|
+
|
|
88
|
+
const padding = 30;
|
|
89
|
+
const nodeWidth = 120;
|
|
90
|
+
const nodeHeight = 80;
|
|
91
|
+
let colorIdx = 0;
|
|
92
|
+
|
|
93
|
+
for (const [dir, files] of dirFiles) {
|
|
94
|
+
if (files.length < 2) continue;
|
|
95
|
+
|
|
96
|
+
let minX = Infinity;
|
|
97
|
+
let minY = Infinity;
|
|
98
|
+
let maxX = -Infinity;
|
|
99
|
+
let maxY = -Infinity;
|
|
100
|
+
let hasPositions = false;
|
|
101
|
+
|
|
102
|
+
for (const file of files) {
|
|
103
|
+
const nodeId = fileMap.get(file);
|
|
104
|
+
if (!nodeId) continue;
|
|
105
|
+
const pos = positions[nodeId];
|
|
106
|
+
if (!pos) continue;
|
|
107
|
+
hasPositions = true;
|
|
108
|
+
|
|
109
|
+
if (pos.x < minX) minX = pos.x;
|
|
110
|
+
if (pos.y < minY) minY = pos.y;
|
|
111
|
+
if (pos.x + nodeWidth > maxX) maxX = pos.x + nodeWidth;
|
|
112
|
+
if (pos.y + nodeHeight > maxY) maxY = pos.y + nodeHeight;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (!hasPositions) continue;
|
|
116
|
+
|
|
117
|
+
const dirLabel = dir.replace(/\/$/, '').split('/').pop() || 'root';
|
|
118
|
+
const color = colors[colorIdx % colors.length];
|
|
119
|
+
colorIdx++;
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
const frame = new FrameClass(dirLabel, {
|
|
123
|
+
x: minX - padding,
|
|
124
|
+
y: minY - padding,
|
|
125
|
+
width: (maxX - minX) + padding * 2,
|
|
126
|
+
height: (maxY - minY) + padding * 2,
|
|
127
|
+
color,
|
|
128
|
+
});
|
|
129
|
+
editor.addFrame(frame);
|
|
130
|
+
} catch {
|
|
131
|
+
// Invalid frame geometry should not prevent rendering the graph.
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function setGraphLayerVisible(canvas, layer, visible) {
|
|
137
|
+
if (!canvas) return;
|
|
138
|
+
|
|
139
|
+
if (layer === 'zones') {
|
|
140
|
+
const frames = canvas.querySelectorAll('graph-frame');
|
|
141
|
+
for (const frame of frames) {
|
|
142
|
+
frame.style.display = visible ? '' : 'none';
|
|
143
|
+
frame.hidden = !visible;
|
|
144
|
+
}
|
|
145
|
+
} else if (layer === 'vias') {
|
|
146
|
+
if (visible) {
|
|
147
|
+
canvas.removeAttribute('data-hide-vias');
|
|
148
|
+
} else {
|
|
149
|
+
canvas.setAttribute('data-hide-vias', '');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function toggleGraphLayerButtonState(button) {
|
|
155
|
+
const isActive = button.hasAttribute('data-active');
|
|
156
|
+
if (isActive) {
|
|
157
|
+
button.removeAttribute('data-active');
|
|
158
|
+
button.setAttribute('data-hidden', '');
|
|
159
|
+
} else {
|
|
160
|
+
button.setAttribute('data-active', '');
|
|
161
|
+
button.removeAttribute('data-hidden');
|
|
162
|
+
}
|
|
163
|
+
return !isActive;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function buildFlatPathHash(path, params = new URLSearchParams()) {
|
|
167
|
+
const nextParams = new URLSearchParams(params);
|
|
168
|
+
const hash = path ? `#graph/${path}` : '#graph';
|
|
169
|
+
|
|
170
|
+
if (!path) {
|
|
171
|
+
nextParams.delete('focus');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const query = nextParams.toString();
|
|
175
|
+
return query ? `${hash}?${query}` : hash;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function selectGraphLabelMode(buttons, selectedButton, canvas) {
|
|
179
|
+
for (const button of buttons) {
|
|
180
|
+
button.removeAttribute('data-active');
|
|
181
|
+
}
|
|
182
|
+
selectedButton.setAttribute('data-active', '');
|
|
183
|
+
|
|
184
|
+
const mode = selectedButton.getAttribute('data-mode');
|
|
185
|
+
if (mode) canvas?.setAttribute('data-label-mode', mode);
|
|
186
|
+
|
|
187
|
+
return mode;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function getFileSelectionNodeId(filePath) {
|
|
191
|
+
return filePath?.endsWith('/') ? filePath.replace(/\/$/, '') : filePath;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function shouldClearFocusOnSelection({ selectedNodes = [], initialViewRestored, hash = '' }) {
|
|
195
|
+
return selectedNodes.length === 0 && Boolean(initialViewRestored) && hash.includes('focus=');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function resolveFlatHashChange(hash) {
|
|
199
|
+
if (!hash.startsWith('#graph')) return null;
|
|
200
|
+
|
|
201
|
+
const [hashBase, queryStr] = hash.replace('#', '').split('?');
|
|
202
|
+
const hashParams = hashBase.split('/');
|
|
203
|
+
if (hashParams[0] === 'graph') hashParams.shift();
|
|
204
|
+
|
|
205
|
+
const path = hashParams.join('/');
|
|
206
|
+
const params = new URLSearchParams(queryStr || '');
|
|
207
|
+
const focus = params.get('focus');
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
path,
|
|
211
|
+
focus: focus ? decodeURIComponent(focus) : null,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function getFlatFocusRestoreKey({ path = '', focus = null } = {}) {
|
|
216
|
+
return `${path || ''}::${focus || ''}`;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function shouldRestoreFlatFocus({ lastKey = null, path = '', focus = null } = {}) {
|
|
220
|
+
if (!focus) return false;
|
|
221
|
+
return getFlatFocusRestoreKey({ path, focus }) !== lastKey;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function getGraphHashNavigationState(hash = '') {
|
|
225
|
+
const hasPath = /^#graph\//.test(hash);
|
|
226
|
+
const hasParams = hash.includes('?');
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
hasPath,
|
|
230
|
+
hasParams,
|
|
231
|
+
shouldRestore: hasPath || hasParams,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function shouldFitForceLayoutInitialTick(hash = '') {
|
|
236
|
+
return !(hash.includes('?') || hash.includes('focus='));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function resolveGraphNodeClick({ nodeId, path, symbol, depth = 0, hash = '' }) {
|
|
240
|
+
if (symbol) {
|
|
241
|
+
return {
|
|
242
|
+
hashUpdates: [['symbol', symbol.name]],
|
|
243
|
+
fileEvent: symbol.file ? { path: symbol.file, source: 'canvas' } : null,
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (!path) return null
|
|
248
|
+
|
|
249
|
+
if (depth === 0) {
|
|
250
|
+
return {
|
|
251
|
+
hashUpdates: [['focus', path], ['in', null]],
|
|
252
|
+
fileEvent: { path, source: 'canvas' },
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const drillBase = hash.split('?')[0]
|
|
257
|
+
const drillPath = drillBase.replace('#graph/', '')
|
|
258
|
+
const relativeName = path.startsWith(drillPath) ? path.slice(drillPath.length) : path
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
hashUpdates: [['focus', relativeName], ['in', '1']],
|
|
262
|
+
fileEvent: { path, source: 'canvas' },
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export function resolveToolbarAction({ action, nodeId, viewMode, path, symbol }) {
|
|
267
|
+
if (action === 'explore') {
|
|
268
|
+
return viewMode === 'flat'
|
|
269
|
+
? { type: 'fly-to-node', nodeId }
|
|
270
|
+
: { type: 'explore-node', nodeId }
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (action === 'view-code') {
|
|
274
|
+
const file = viewMode === 'flat' ? nodeId : (symbol ? symbol.file : path)
|
|
275
|
+
return file ? { type: 'open-file', hash: `#explorer/${file}` } : null
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (action === 'enter' && viewMode === 'flat') {
|
|
279
|
+
return { type: 'drill-node', nodeId }
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return null
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export function renderClusterPanel({
|
|
286
|
+
panel,
|
|
287
|
+
toggle,
|
|
288
|
+
clusters = [],
|
|
289
|
+
viewMode,
|
|
290
|
+
isOpen,
|
|
291
|
+
doc = typeof document !== 'undefined' ? document : null,
|
|
292
|
+
}) {
|
|
293
|
+
if (!panel || !doc) return
|
|
294
|
+
let hasFlatLegend = clusters.length > 0 && viewMode === 'flat'
|
|
295
|
+
|
|
296
|
+
if (toggle) {
|
|
297
|
+
toggle.hidden = !hasFlatLegend
|
|
298
|
+
toggle.toggleAttribute('data-active', hasFlatLegend && isOpen)
|
|
299
|
+
toggle.setAttribute(
|
|
300
|
+
'title',
|
|
301
|
+
isOpen ? 'Hide semantic color legend' : 'Show semantic color legend',
|
|
302
|
+
)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (!hasFlatLegend || !isOpen) {
|
|
306
|
+
panel.hidden = true
|
|
307
|
+
panel.replaceChildren()
|
|
308
|
+
return
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
panel.hidden = false
|
|
312
|
+
panel.replaceChildren(...clusters.map((cluster) => {
|
|
313
|
+
let row = doc.createElement('div')
|
|
314
|
+
let swatch = doc.createElement('span')
|
|
315
|
+
let label = doc.createElement('span')
|
|
316
|
+
let pathCount = cluster.paths.length
|
|
317
|
+
|
|
318
|
+
row.className = 'pcb-cluster-row'
|
|
319
|
+
row.title = cluster.description || `${cluster.label}: ${pathCount} paths`
|
|
320
|
+
swatch.className = 'pcb-cluster-swatch'
|
|
321
|
+
swatch.style.background = cluster.color
|
|
322
|
+
label.className = 'pcb-cluster-label'
|
|
323
|
+
label.textContent = cluster.label
|
|
324
|
+
row.replaceChildren(swatch, label)
|
|
325
|
+
return row
|
|
326
|
+
}))
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export function renderGraphStats(statsEl, items, doc = typeof document !== 'undefined' ? document : null) {
|
|
330
|
+
if (!statsEl || !doc) return
|
|
331
|
+
statsEl.replaceChildren(...items.map(([value, label]) => {
|
|
332
|
+
let item = doc.createElement('span')
|
|
333
|
+
let valueEl = doc.createElement('span')
|
|
334
|
+
valueEl.className = 'graph-explorer-stat-val'
|
|
335
|
+
valueEl.textContent = String(value)
|
|
336
|
+
item.append(valueEl, ` ${label}`)
|
|
337
|
+
return item
|
|
338
|
+
}))
|
|
339
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
export function getGraphCacheKey(isStructured) {
|
|
2
|
+
return isStructured ? 'structured' : 'flat';
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function getOrBuildGraph({
|
|
6
|
+
cache,
|
|
7
|
+
skeleton,
|
|
8
|
+
isStructured,
|
|
9
|
+
buildStructuredGraphFn,
|
|
10
|
+
buildFileGraphFn,
|
|
11
|
+
}) {
|
|
12
|
+
const cacheKey = getGraphCacheKey(isStructured);
|
|
13
|
+
const cached = cache[cacheKey];
|
|
14
|
+
|
|
15
|
+
if (cached?.skeleton === skeleton) {
|
|
16
|
+
return { cacheKey, cached: true, graph: cached };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const graph = isStructured
|
|
20
|
+
? buildStructuredGraphFn(skeleton)
|
|
21
|
+
: buildFileGraphFn(skeleton);
|
|
22
|
+
|
|
23
|
+
if (!graph.symbolMap) graph.symbolMap = new Map();
|
|
24
|
+
cache[cacheKey] = { skeleton, ...graph };
|
|
25
|
+
|
|
26
|
+
return { cacheKey, cached: false, graph: cache[cacheKey] };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function getDrillableFiles(symbolMap = new Map()) {
|
|
30
|
+
return new Set([...symbolMap.values()].map((symbol) => symbol.file));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function findForceNodeGroup(groups = {}, nodeId) {
|
|
34
|
+
return Object.entries(groups).find(([, ids]) => ids.includes(nodeId))?.[0] ?? null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function getForceLayoutOptions(nodeCount, { continuous = false } = {}) {
|
|
38
|
+
const options = {
|
|
39
|
+
chargeStrength: nodeCount > 500 ? -300 : -150,
|
|
40
|
+
linkDistance: nodeCount > 500 ? 100 : 150,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
if (continuous) {
|
|
44
|
+
options.nodeWidth = 260;
|
|
45
|
+
options.nodeHeight = 40;
|
|
46
|
+
options.mode = 'continuous';
|
|
47
|
+
options.brownian = 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return options;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function createForceLayoutPayload({
|
|
54
|
+
nodes,
|
|
55
|
+
connections,
|
|
56
|
+
positions = {},
|
|
57
|
+
groups = {},
|
|
58
|
+
nodeSizes = {},
|
|
59
|
+
continuous = false,
|
|
60
|
+
}) {
|
|
61
|
+
return {
|
|
62
|
+
nodes: nodes.map((node) => ({
|
|
63
|
+
id: node.id,
|
|
64
|
+
x: positions[node.id]?.x ?? 0,
|
|
65
|
+
y: positions[node.id]?.y ?? 0,
|
|
66
|
+
group: findForceNodeGroup(groups, node.id),
|
|
67
|
+
w: nodeSizes[node.id]?.w || node.params?.calculatedWidth || 260,
|
|
68
|
+
h: nodeSizes[node.id]?.h || node.params?.calculatedHeight || 60,
|
|
69
|
+
})),
|
|
70
|
+
edges: connections.map((connection) => ({
|
|
71
|
+
from: connection.from,
|
|
72
|
+
to: connection.to,
|
|
73
|
+
})),
|
|
74
|
+
groups: groups || {},
|
|
75
|
+
options: getForceLayoutOptions(nodes.length, { continuous }),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function computeInitialGraphPositions({
|
|
80
|
+
editor,
|
|
81
|
+
isStructured = false,
|
|
82
|
+
dirFiles = null,
|
|
83
|
+
dirNodeMap = new Map(),
|
|
84
|
+
groups = {},
|
|
85
|
+
computeTreeLayoutFn,
|
|
86
|
+
random = Math.random,
|
|
87
|
+
}) {
|
|
88
|
+
if (isStructured && dirFiles) {
|
|
89
|
+
if (typeof computeTreeLayoutFn !== 'function') {
|
|
90
|
+
throw new TypeError('computeTreeLayoutFn is required for structured graph layout');
|
|
91
|
+
}
|
|
92
|
+
const dirPaths = {};
|
|
93
|
+
const rootNodeIds = new Set(editor.getNodes().map((node) => node.id));
|
|
94
|
+
for (const [dir, nodeId] of dirNodeMap.entries()) {
|
|
95
|
+
if (rootNodeIds.has(nodeId)) {
|
|
96
|
+
dirPaths[nodeId] = dir;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return computeTreeLayoutFn(editor, {
|
|
101
|
+
dirPaths,
|
|
102
|
+
nodeWidth: 250,
|
|
103
|
+
nodeHeight: 100,
|
|
104
|
+
gapX: 40,
|
|
105
|
+
gapY: 60,
|
|
106
|
+
startX: 60,
|
|
107
|
+
startY: 60,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const allNodes = [...editor.getNodes()];
|
|
112
|
+
const totalNodes = allNodes.length;
|
|
113
|
+
const groupEntries = Object.entries(groups);
|
|
114
|
+
const positions = {};
|
|
115
|
+
|
|
116
|
+
if (groupEntries.length > 1) {
|
|
117
|
+
const globalRadius = Math.sqrt(totalNodes) * 80;
|
|
118
|
+
let groupIdx = 0;
|
|
119
|
+
for (const [, memberIds] of groupEntries) {
|
|
120
|
+
const angle = (2 * Math.PI * groupIdx) / groupEntries.length;
|
|
121
|
+
const radius = globalRadius * (0.3 + 0.7 * (groupIdx / groupEntries.length));
|
|
122
|
+
const cx = Math.cos(angle) * radius;
|
|
123
|
+
const cy = Math.sin(angle) * radius;
|
|
124
|
+
const memberRadius = Math.sqrt(memberIds.length) * 60;
|
|
125
|
+
for (let memberIdx = 0; memberIdx < memberIds.length; memberIdx++) {
|
|
126
|
+
const memberAngle = (2 * Math.PI * memberIdx) / memberIds.length;
|
|
127
|
+
positions[memberIds[memberIdx]] = {
|
|
128
|
+
x: cx + Math.cos(memberAngle) * memberRadius + (random() - 0.5) * 20,
|
|
129
|
+
y: cy + Math.sin(memberAngle) * memberRadius + (random() - 0.5) * 20,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
groupIdx++;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
for (const node of allNodes) {
|
|
137
|
+
if (!positions[node.id]) {
|
|
138
|
+
const angle = random() * 2 * Math.PI;
|
|
139
|
+
const radius = Math.sqrt(totalNodes) * 50 + random() * 200;
|
|
140
|
+
positions[node.id] = {
|
|
141
|
+
x: Math.cos(angle) * radius,
|
|
142
|
+
y: Math.sin(angle) * radius,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return positions;
|
|
148
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
function normalizeNode(rawNode) {
|
|
2
|
+
if (!rawNode || typeof rawNode !== 'object') return null;
|
|
3
|
+
const id = String(rawNode.id || '').trim();
|
|
4
|
+
if (!id) return null;
|
|
5
|
+
const children = Array.isArray(rawNode.children) ? rawNode.children.map(String) : [];
|
|
6
|
+
return {
|
|
7
|
+
...rawNode,
|
|
8
|
+
id,
|
|
9
|
+
label: rawNode.label == null ? id : String(rawNode.label),
|
|
10
|
+
type: rawNode.type || 'data',
|
|
11
|
+
parentId: rawNode.parentId == null ? null : String(rawNode.parentId),
|
|
12
|
+
isGroup: Boolean(rawNode.isGroup),
|
|
13
|
+
children,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function normalizeEdge(rawEdge) {
|
|
18
|
+
if (!rawEdge || typeof rawEdge !== 'object') return null;
|
|
19
|
+
const rawFrom = rawEdge.from ?? rawEdge.source?.node ?? rawEdge.source?.id ?? rawEdge.source;
|
|
20
|
+
const rawTo = rawEdge.to ?? rawEdge.target?.node ?? rawEdge.target?.id ?? rawEdge.target;
|
|
21
|
+
const from = String(rawFrom || '').trim();
|
|
22
|
+
const to = String(rawTo || '').trim();
|
|
23
|
+
if (!from || !to) return null;
|
|
24
|
+
return { ...rawEdge, from, to };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function normalizeCanvasGraphModel(model = {}) {
|
|
28
|
+
const nodes = [];
|
|
29
|
+
const nodesById = new Map();
|
|
30
|
+
const edges = [];
|
|
31
|
+
const rootNodes = [];
|
|
32
|
+
|
|
33
|
+
for (const rawNode of model.nodes || []) {
|
|
34
|
+
const node = normalizeNode(rawNode);
|
|
35
|
+
if (!node || nodesById.has(node.id)) continue;
|
|
36
|
+
nodes.push(node);
|
|
37
|
+
nodesById.set(node.id, node);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const rawEdges = Array.isArray(model.connections) ? model.connections : model.edges || [];
|
|
41
|
+
for (const rawEdge of rawEdges) {
|
|
42
|
+
const edge = normalizeEdge(rawEdge);
|
|
43
|
+
if (!edge || !nodesById.has(edge.from) || !nodesById.has(edge.to)) continue;
|
|
44
|
+
edges.push(edge);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const explicitRoots = Array.isArray(model.rootNodes) ? model.rootNodes.map(String) : [];
|
|
48
|
+
for (const id of explicitRoots) {
|
|
49
|
+
if (nodesById.has(id) && !rootNodes.includes(id)) rootNodes.push(id);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (rootNodes.length === 0) {
|
|
53
|
+
for (const node of nodes) {
|
|
54
|
+
if (!node.parentId || !nodesById.has(node.parentId)) rootNodes.push(node.id);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return { nodes, edges, rootNodes };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function createCanvasGraphStore(model = {}) {
|
|
62
|
+
const normalized = normalizeCanvasGraphModel(model);
|
|
63
|
+
return {
|
|
64
|
+
nodes: new Map(normalized.nodes.map((node) => [node.id, node])),
|
|
65
|
+
edges: normalized.edges,
|
|
66
|
+
rootNodes: normalized.rootNodes,
|
|
67
|
+
};
|
|
68
|
+
}
|