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,253 @@
|
|
|
1
|
+
import { isGraphColorReference, normalizeGraphColorReference } from './theme-contract.js'
|
|
2
|
+
|
|
3
|
+
export const EMPTY_PROJECT_GRAPH_METADATA = Object.freeze({
|
|
4
|
+
version: 1,
|
|
5
|
+
clusters: [],
|
|
6
|
+
nodeDescriptions: {},
|
|
7
|
+
stories: [],
|
|
8
|
+
layoutPins: {},
|
|
9
|
+
hiddenNodes: [],
|
|
10
|
+
focusPresets: [],
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
function slugify(value) {
|
|
14
|
+
return String(value || '')
|
|
15
|
+
.trim()
|
|
16
|
+
.toLowerCase()
|
|
17
|
+
.replace(/[^a-z0-9._/-]+/g, '-')
|
|
18
|
+
.replace(/^-+|-+$/g, '') || 'cluster'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function normalizePatterns(cluster) {
|
|
22
|
+
let values = [
|
|
23
|
+
...(Array.isArray(cluster.paths) ? cluster.paths : []),
|
|
24
|
+
...(Array.isArray(cluster.patterns) ? cluster.patterns : []),
|
|
25
|
+
...(Array.isArray(cluster.nodes) ? cluster.nodes : []),
|
|
26
|
+
...(Array.isArray(cluster.path) ? cluster.path : []),
|
|
27
|
+
...(Array.isArray(cluster.pattern) ? cluster.pattern : []),
|
|
28
|
+
...(Array.isArray(cluster.node) ? cluster.node : []),
|
|
29
|
+
...(typeof cluster.path === 'string' ? [cluster.path] : []),
|
|
30
|
+
...(typeof cluster.pattern === 'string' ? [cluster.pattern] : []),
|
|
31
|
+
...(typeof cluster.node === 'string' ? [cluster.node] : []),
|
|
32
|
+
...(typeof cluster.match === 'string' ? [cluster.match] : []),
|
|
33
|
+
]
|
|
34
|
+
return [...new Set(values.map((value) => String(value || '').trim()).filter(Boolean))]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function normalizeStringArray(value) {
|
|
38
|
+
return Array.isArray(value) ? value.map((item) => String(item || '').trim()).filter(Boolean) : []
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function normalizeStoryBeats(story) {
|
|
42
|
+
let beats = Array.isArray(story.beats) ? story.beats : []
|
|
43
|
+
return beats
|
|
44
|
+
.filter((beat) => beat && typeof beat === 'object')
|
|
45
|
+
.map((beat, index) => {
|
|
46
|
+
let id = slugify(beat.id || beat.label || beat.title || `beat-${index + 1}`)
|
|
47
|
+
let label = String(beat.label || beat.title || id).trim()
|
|
48
|
+
return {
|
|
49
|
+
id,
|
|
50
|
+
label,
|
|
51
|
+
narrative: String(beat.narrative || beat.description || '').trim(),
|
|
52
|
+
nodes: normalizeStringArray(beat.nodes),
|
|
53
|
+
edges: normalizeStringArray(beat.edges),
|
|
54
|
+
clusterId: String(beat.clusterId || beat.cluster || '').trim(),
|
|
55
|
+
focusPath: String(beat.focusPath || beat.path || '').trim(),
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function normalizeStories(input) {
|
|
61
|
+
let stories = Array.isArray(input) ? input : []
|
|
62
|
+
return stories
|
|
63
|
+
.filter((story) => story && typeof story === 'object')
|
|
64
|
+
.map((story, index) => {
|
|
65
|
+
let id = slugify(story.id || story.label || story.title || `story-${index + 1}`)
|
|
66
|
+
let label = String(story.label || story.title || id).trim()
|
|
67
|
+
return {
|
|
68
|
+
id,
|
|
69
|
+
label,
|
|
70
|
+
description: String(story.description || '').trim(),
|
|
71
|
+
beats: normalizeStoryBeats(story),
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
.filter((story) => story.beats.length > 0)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function assertStringArray(value, pathName) {
|
|
78
|
+
if (value === undefined) return
|
|
79
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== 'string' || item.trim() === '')) {
|
|
80
|
+
throw new Error(`Invalid project graph metadata: ${pathName} must be an array of non-empty strings`)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function validateStoriesInput(stories) {
|
|
85
|
+
if (stories === undefined) return
|
|
86
|
+
if (!Array.isArray(stories)) {
|
|
87
|
+
throw new Error('Invalid project graph metadata: "stories" must be an array')
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
for (let storyIndex = 0; storyIndex < stories.length; storyIndex++) {
|
|
91
|
+
let story = stories[storyIndex]
|
|
92
|
+
if (!story || typeof story !== 'object' || Array.isArray(story)) {
|
|
93
|
+
throw new Error(`Invalid project graph metadata: stories[${storyIndex}] must be an object`)
|
|
94
|
+
}
|
|
95
|
+
for (let key of ['id', 'label', 'title', 'description']) {
|
|
96
|
+
if (story[key] !== undefined && typeof story[key] !== 'string') {
|
|
97
|
+
throw new Error(`Invalid project graph metadata: stories[${storyIndex}].${key} must be a string`)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (!Array.isArray(story.beats) || story.beats.length === 0) {
|
|
101
|
+
throw new Error(`Invalid project graph metadata: stories[${storyIndex}].beats must be a non-empty array`)
|
|
102
|
+
}
|
|
103
|
+
for (let beatIndex = 0; beatIndex < story.beats.length; beatIndex++) {
|
|
104
|
+
let beat = story.beats[beatIndex]
|
|
105
|
+
if (!beat || typeof beat !== 'object' || Array.isArray(beat)) {
|
|
106
|
+
throw new Error(`Invalid project graph metadata: stories[${storyIndex}].beats[${beatIndex}] must be an object`)
|
|
107
|
+
}
|
|
108
|
+
for (let key of ['id', 'label', 'title', 'narrative', 'description', 'clusterId', 'cluster', 'focusPath', 'path']) {
|
|
109
|
+
if (beat[key] !== undefined && typeof beat[key] !== 'string') {
|
|
110
|
+
throw new Error(`Invalid project graph metadata: stories[${storyIndex}].beats[${beatIndex}].${key} must be a string`)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
assertStringArray(beat.nodes, `stories[${storyIndex}].beats[${beatIndex}].nodes`)
|
|
114
|
+
assertStringArray(beat.edges, `stories[${storyIndex}].beats[${beatIndex}].edges`)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function normalizeProjectGraphMetadata(input = {}) {
|
|
120
|
+
let source = input && typeof input === 'object' ? input : {}
|
|
121
|
+
let clusters = Array.isArray(source.clusters) ? source.clusters : []
|
|
122
|
+
let normalizedClusters = clusters
|
|
123
|
+
.filter((cluster) => cluster && typeof cluster === 'object')
|
|
124
|
+
.map((cluster, index) => {
|
|
125
|
+
let id = slugify(cluster.id || cluster.label || cluster.name || `cluster-${index + 1}`)
|
|
126
|
+
let label = String(cluster.label || cluster.name || id).trim()
|
|
127
|
+
return {
|
|
128
|
+
id,
|
|
129
|
+
label,
|
|
130
|
+
color: normalizeGraphColorReference(cluster.color, index),
|
|
131
|
+
description: String(cluster.description || '').trim(),
|
|
132
|
+
paths: normalizePatterns(cluster),
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
.filter((cluster) => cluster.paths.length > 0)
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
version: Number(source.version) || 1,
|
|
139
|
+
clusters: normalizedClusters,
|
|
140
|
+
nodeDescriptions: source.nodeDescriptions && typeof source.nodeDescriptions === 'object' ? source.nodeDescriptions : {},
|
|
141
|
+
stories: normalizeStories(source.stories),
|
|
142
|
+
layoutPins: source.layoutPins && typeof source.layoutPins === 'object' ? source.layoutPins : {},
|
|
143
|
+
hiddenNodes: Array.isArray(source.hiddenNodes) ? source.hiddenNodes : [],
|
|
144
|
+
focusPresets: Array.isArray(source.focusPresets) ? source.focusPresets : [],
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function validateProjectGraphMetadata(input = {}) {
|
|
149
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) {
|
|
150
|
+
throw new Error('Invalid project graph metadata: expected a JSON object')
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (input.version !== undefined && (!Number.isFinite(Number(input.version)) || Number(input.version) < 1)) {
|
|
154
|
+
throw new Error('Invalid project graph metadata: "version" must be a positive number')
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (input.clusters !== undefined && !Array.isArray(input.clusters)) {
|
|
158
|
+
throw new Error('Invalid project graph metadata: "clusters" must be an array')
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
for (let index = 0; index < (input.clusters || []).length; index++) {
|
|
162
|
+
let cluster = input.clusters[index]
|
|
163
|
+
if (!cluster || typeof cluster !== 'object' || Array.isArray(cluster)) {
|
|
164
|
+
throw new Error(`Invalid project graph metadata: clusters[${index}] must be an object`)
|
|
165
|
+
}
|
|
166
|
+
let paths = normalizePatterns(cluster)
|
|
167
|
+
if (paths.length === 0) {
|
|
168
|
+
throw new Error(`Invalid project graph metadata: clusters[${index}] must define at least one path`)
|
|
169
|
+
}
|
|
170
|
+
if (cluster.color !== undefined && !isGraphColorReference(cluster.color)) {
|
|
171
|
+
throw new Error(`Invalid project graph metadata: clusters[${index}].color must be a hex color or symbiote-node CSS token reference`)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
for (let key of ['nodeDescriptions', 'layoutPins']) {
|
|
176
|
+
if (input[key] !== undefined && (!input[key] || typeof input[key] !== 'object' || Array.isArray(input[key]))) {
|
|
177
|
+
throw new Error(`Invalid project graph metadata: "${key}" must be an object`)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
validateStoriesInput(input.stories)
|
|
182
|
+
|
|
183
|
+
for (let key of ['hiddenNodes', 'focusPresets']) {
|
|
184
|
+
if (input[key] !== undefined && !Array.isArray(input[key])) {
|
|
185
|
+
throw new Error(`Invalid project graph metadata: "${key}" must be an array`)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return normalizeProjectGraphMetadata(input)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function escapeRegex(value) {
|
|
193
|
+
return value.replace(/[|\\{}()[\]^$+?.]/g, '\\$&')
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function pathMatchesPattern(path, pattern) {
|
|
197
|
+
if (!path || !pattern) return false
|
|
198
|
+
let cleanPath = String(path).replace(/^\.\//, '')
|
|
199
|
+
let cleanPattern = String(pattern).replace(/^\.\//, '')
|
|
200
|
+
|
|
201
|
+
if (cleanPattern.includes('*')) {
|
|
202
|
+
let regex = '^' + escapeRegex(cleanPattern)
|
|
203
|
+
.replace(/\*\*/g, '\u0000')
|
|
204
|
+
.replace(/\*/g, '[^/]*') + '$'
|
|
205
|
+
return new RegExp(regex.replace(/\u0000/g, '.*')).test(cleanPath)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (cleanPattern.endsWith('/')) {
|
|
209
|
+
return cleanPath.startsWith(cleanPattern)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return cleanPath === cleanPattern || cleanPath.startsWith(`${cleanPattern}/`)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function findClusterForPath(path, metadata) {
|
|
216
|
+
let graphMetadata = normalizeProjectGraphMetadata(metadata)
|
|
217
|
+
return graphMetadata.clusters.find((cluster) => (
|
|
218
|
+
cluster.paths.some((pattern) => pathMatchesPattern(path, pattern))
|
|
219
|
+
)) || null
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function buildSemanticGroups(fileMap, metadata) {
|
|
223
|
+
let graphMetadata = normalizeProjectGraphMetadata(metadata)
|
|
224
|
+
let assigned = new Set()
|
|
225
|
+
let groups = {}
|
|
226
|
+
|
|
227
|
+
for (let cluster of graphMetadata.clusters) {
|
|
228
|
+
let memberIds = []
|
|
229
|
+
for (let [filePath, nodeId] of fileMap.entries()) {
|
|
230
|
+
if (assigned.has(nodeId)) continue
|
|
231
|
+
if (!cluster.paths.some((pattern) => pathMatchesPattern(filePath, pattern))) continue
|
|
232
|
+
assigned.add(nodeId)
|
|
233
|
+
memberIds.push(nodeId)
|
|
234
|
+
}
|
|
235
|
+
if (memberIds.length > 0) groups[`cluster:${cluster.id}`] = memberIds
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return groups
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export function parseHexColor(color) {
|
|
242
|
+
let value = String(color || '').trim()
|
|
243
|
+
let match = value.match(/^#?([0-9a-f]{3}|[0-9a-f]{6})$/i)
|
|
244
|
+
if (!match) return null
|
|
245
|
+
let hex = match[1].length === 3
|
|
246
|
+
? match[1].split('').map((ch) => ch + ch).join('')
|
|
247
|
+
: match[1]
|
|
248
|
+
return [
|
|
249
|
+
parseInt(hex.slice(0, 2), 16),
|
|
250
|
+
parseInt(hex.slice(2, 4), 16),
|
|
251
|
+
parseInt(hex.slice(4, 6), 16),
|
|
252
|
+
]
|
|
253
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { normalizeGraphModel } from './model.js';
|
|
2
|
+
import { getThemeControls } from '../manifest/theme-catalog.js';
|
|
3
|
+
|
|
4
|
+
const LOCAL_PATH_PATTERN = /(^|[\s"'=:])(?:\/Users\/|\/home\/|[A-Za-z]:\\)/;
|
|
5
|
+
const PROVIDER_THEME_ALIASES = new Map([
|
|
6
|
+
['symbiote-default', 'default-provider'],
|
|
7
|
+
['symbiote-default-provider', 'default-provider'],
|
|
8
|
+
['default-provider', 'default-provider'],
|
|
9
|
+
]);
|
|
10
|
+
|
|
11
|
+
function asObject(value) {
|
|
12
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function normalizeId(value, fieldName) {
|
|
16
|
+
const id = String(value ?? '').trim();
|
|
17
|
+
if (!id) throw new Error(`${fieldName} is required`);
|
|
18
|
+
return id;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function assertNoLocalPaths(value) {
|
|
22
|
+
const text = JSON.stringify(value);
|
|
23
|
+
if (LOCAL_PATH_PATTERN.test(text)) {
|
|
24
|
+
throw new Error('project package contains an absolute local path');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function normalizeEntry(entry = {}) {
|
|
29
|
+
const data = asObject(entry);
|
|
30
|
+
return {
|
|
31
|
+
graph: normalizeId(data.graph ?? 'main', 'entry.graph'),
|
|
32
|
+
layout: normalizeId(data.layout ?? 'main', 'entry.layout'),
|
|
33
|
+
theme: normalizeId(data.theme ?? 'default', 'entry.theme'),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function normalizeNamedRecord(record, normalizer) {
|
|
38
|
+
return Object.fromEntries(
|
|
39
|
+
Object.entries(asObject(record)).map(([id, value]) => [id, normalizer(value, id)])
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function normalizeLayout(layout, id) {
|
|
44
|
+
const data = asObject(layout);
|
|
45
|
+
return {
|
|
46
|
+
...data,
|
|
47
|
+
id,
|
|
48
|
+
version: String(data.version ?? 'runtime-ui-v1'),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function normalizeTheme(theme, id) {
|
|
53
|
+
const data = asObject(theme);
|
|
54
|
+
const extendsTheme = String(data.extends ?? 'symbiote-default');
|
|
55
|
+
const modifiers = asObject(data.modifiers);
|
|
56
|
+
validateThemeModifiers(extendsTheme, modifiers, id);
|
|
57
|
+
return {
|
|
58
|
+
...data,
|
|
59
|
+
id,
|
|
60
|
+
extends: extendsTheme,
|
|
61
|
+
modifiers,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function validateThemeModifiers(extendsTheme, modifiers, themeId) {
|
|
66
|
+
const providerTheme = PROVIDER_THEME_ALIASES.get(extendsTheme);
|
|
67
|
+
if (!providerTheme) return;
|
|
68
|
+
|
|
69
|
+
const controls = getThemeControls(providerTheme);
|
|
70
|
+
const controlNames = new Set(controls.map((control) => control.name));
|
|
71
|
+
for (const name of Object.keys(modifiers)) {
|
|
72
|
+
if (!controlNames.has(name)) {
|
|
73
|
+
throw new Error(`theme "${themeId}" modifier "${name}" is not defined by ${extendsTheme}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function normalizePacks(packs) {
|
|
79
|
+
if (!Array.isArray(packs)) return [];
|
|
80
|
+
return packs.map((pack) => {
|
|
81
|
+
const data = asObject(pack);
|
|
82
|
+
return {
|
|
83
|
+
...data,
|
|
84
|
+
id: normalizeId(data.id, 'pack.id'),
|
|
85
|
+
kind: String(data.kind ?? 'provider'),
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function normalizeProjectPackage(rawProject = {}) {
|
|
91
|
+
assertNoLocalPaths(rawProject);
|
|
92
|
+
|
|
93
|
+
const data = asObject(rawProject);
|
|
94
|
+
const version = String(data.version ?? 'project-package-v1');
|
|
95
|
+
if (version !== 'project-package-v1') {
|
|
96
|
+
throw new Error(`unsupported project package version "${version}"`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const graphs = normalizeNamedRecord(data.graphs, (graph) => normalizeGraphModel(graph));
|
|
100
|
+
const layouts = normalizeNamedRecord(data.layouts, normalizeLayout);
|
|
101
|
+
const themes = normalizeNamedRecord(data.themes, normalizeTheme);
|
|
102
|
+
const entry = normalizeEntry(data.entry);
|
|
103
|
+
|
|
104
|
+
if (!graphs[entry.graph]) throw new Error(`entry graph "${entry.graph}" is not defined`);
|
|
105
|
+
if (!layouts[entry.layout]) throw new Error(`entry layout "${entry.layout}" is not defined`);
|
|
106
|
+
if (!themes[entry.theme]) throw new Error(`entry theme "${entry.theme}" is not defined`);
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
version,
|
|
110
|
+
id: normalizeId(data.id, 'project.id'),
|
|
111
|
+
name: data.name == null ? normalizeId(data.id, 'project.id') : String(data.name),
|
|
112
|
+
entry,
|
|
113
|
+
packs: normalizePacks(data.packs),
|
|
114
|
+
graphs,
|
|
115
|
+
layouts,
|
|
116
|
+
themes,
|
|
117
|
+
dataSources: asObject(data.dataSources),
|
|
118
|
+
agents: {
|
|
119
|
+
rules: Array.isArray(data.agents?.rules) ? data.agents.rules.map(String) : [],
|
|
120
|
+
allowedTransactions: Array.isArray(data.agents?.allowedTransactions)
|
|
121
|
+
? data.agents.allowedTransactions.map(String)
|
|
122
|
+
: [],
|
|
123
|
+
},
|
|
124
|
+
graphsById: new Map(Object.entries(graphs)),
|
|
125
|
+
layoutsById: new Map(Object.entries(layouts)),
|
|
126
|
+
themesById: new Map(Object.entries(themes)),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { normalizeProjectPackage } from './project-package.js';
|
|
2
|
+
import { applyProjectTransaction, normalizeProjectTransaction } from './project-transaction.js';
|
|
3
|
+
|
|
4
|
+
let runtimeIdCounter = 0;
|
|
5
|
+
|
|
6
|
+
function nextTransactionId(prefix) {
|
|
7
|
+
runtimeIdCounter += 1;
|
|
8
|
+
return `${prefix}:${runtimeIdCounter}`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function assertTargetProject(project, transaction) {
|
|
12
|
+
if (transaction.targetProject && transaction.targetProject !== project.id) {
|
|
13
|
+
throw new Error(`transaction target "${transaction.targetProject}" does not match project "${project.id}"`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function createProjectRuntime(rawProject, options = {}) {
|
|
18
|
+
let project = normalizeProjectPackage(rawProject);
|
|
19
|
+
const listeners = new Set();
|
|
20
|
+
|
|
21
|
+
function notify(transaction) {
|
|
22
|
+
const event = { project, transaction };
|
|
23
|
+
for (const listener of listeners) listener(event);
|
|
24
|
+
options.onChange?.(event);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
getProject() {
|
|
29
|
+
return project;
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
getGraph(id = project.entry.graph) {
|
|
33
|
+
return project.graphs[id];
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
getLayout(id = project.entry.layout) {
|
|
37
|
+
return project.layouts[id];
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
getTheme(id = project.entry.theme) {
|
|
41
|
+
return project.themes[id];
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
subscribe(listener) {
|
|
45
|
+
listeners.add(listener);
|
|
46
|
+
return () => listeners.delete(listener);
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
applyTransaction(rawTransaction) {
|
|
50
|
+
const transaction = normalizeProjectTransaction(rawTransaction);
|
|
51
|
+
assertTargetProject(project, transaction);
|
|
52
|
+
project = applyProjectTransaction(project, transaction);
|
|
53
|
+
notify(transaction);
|
|
54
|
+
return project;
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
addGraphNode(graph, node, id = nextTransactionId('tx:add-node')) {
|
|
58
|
+
return this.applyTransaction({
|
|
59
|
+
version: 'project-transaction-v1',
|
|
60
|
+
id,
|
|
61
|
+
targetProject: project.id,
|
|
62
|
+
operations: [{ type: 'graph.addNode', graph, node }],
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
addGraphEdge(graph, edge, id = nextTransactionId('tx:add-edge')) {
|
|
67
|
+
return this.applyTransaction({
|
|
68
|
+
version: 'project-transaction-v1',
|
|
69
|
+
id,
|
|
70
|
+
targetProject: project.id,
|
|
71
|
+
operations: [{ type: 'graph.addEdge', graph, edge }],
|
|
72
|
+
});
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
addLayoutPanel(layout, panel, options = {}) {
|
|
76
|
+
return this.applyTransaction({
|
|
77
|
+
version: 'project-transaction-v1',
|
|
78
|
+
id: options.id || nextTransactionId('tx:add-panel'),
|
|
79
|
+
targetProject: project.id,
|
|
80
|
+
operations: [{
|
|
81
|
+
type: 'layout.addPanel',
|
|
82
|
+
layout,
|
|
83
|
+
parentId: options.parentId,
|
|
84
|
+
panel,
|
|
85
|
+
}],
|
|
86
|
+
});
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
setLayoutRoot(layout, root, id = nextTransactionId('tx:set-root')) {
|
|
90
|
+
return this.applyTransaction({
|
|
91
|
+
version: 'project-transaction-v1',
|
|
92
|
+
id,
|
|
93
|
+
targetProject: project.id,
|
|
94
|
+
operations: [{ type: 'layout.setRoot', layout, root }],
|
|
95
|
+
});
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
updateLayoutNode(layout, nodeId, patch, options = {}) {
|
|
99
|
+
return this.applyTransaction({
|
|
100
|
+
version: 'project-transaction-v1',
|
|
101
|
+
id: options.id || nextTransactionId('tx:update-node'),
|
|
102
|
+
targetProject: project.id,
|
|
103
|
+
operations: [{ type: 'layout.updateNode', layout, nodeId, patch }],
|
|
104
|
+
});
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
setThemeModifier(theme, name, value, id = nextTransactionId('tx:set-theme')) {
|
|
108
|
+
return this.applyTransaction({
|
|
109
|
+
version: 'project-transaction-v1',
|
|
110
|
+
id,
|
|
111
|
+
targetProject: project.id,
|
|
112
|
+
operations: [{ type: 'theme.setModifier', theme, name, value }],
|
|
113
|
+
});
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|