project-graph-mcp 2.2.4 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/ARCHITECTURE.md +81 -0
  2. package/CHANGELOG.md +57 -0
  3. package/README.md +9 -4
  4. package/package.json +6 -13
  5. package/src/compact/expand.js +2 -4
  6. package/src/core/graph-builder.js +2 -2
  7. package/src/core/parser.js +2 -2
  8. package/src/network/server.js +1 -2
  9. package/src/network/web-server.js +4 -1
  10. package/vendor/symbiote-node/CHANGELOG.md +31 -0
  11. package/vendor/symbiote-node/LICENSE +21 -0
  12. package/vendor/symbiote-node/README.md +206 -0
  13. package/vendor/symbiote-node/canvas/AutoLayout.js +725 -0
  14. package/vendor/symbiote-node/canvas/Breadcrumb/Breadcrumb.css.js +73 -0
  15. package/vendor/symbiote-node/canvas/Breadcrumb/Breadcrumb.js +93 -0
  16. package/vendor/symbiote-node/canvas/Breadcrumb/Breadcrumb.tpl.js +9 -0
  17. package/vendor/symbiote-node/canvas/CanvasConnectionRenderer.js +962 -0
  18. package/vendor/symbiote-node/canvas/ConnectionRenderer.js +1468 -0
  19. package/vendor/symbiote-node/canvas/FlowSimulator.js +323 -0
  20. package/vendor/symbiote-node/canvas/ForceLayout.js +189 -0
  21. package/vendor/symbiote-node/canvas/ForceWorker.js +1325 -0
  22. package/vendor/symbiote-node/canvas/GraphTabs/GraphTabs.css.js +97 -0
  23. package/vendor/symbiote-node/canvas/GraphTabs/GraphTabs.js +176 -0
  24. package/vendor/symbiote-node/canvas/GraphTabs/GraphTabs.tpl.js +12 -0
  25. package/vendor/symbiote-node/canvas/LODManager.js +88 -0
  26. package/vendor/symbiote-node/canvas/Minimap/Minimap.css.js +71 -0
  27. package/vendor/symbiote-node/canvas/Minimap/Minimap.js +207 -0
  28. package/vendor/symbiote-node/canvas/Minimap/Minimap.tpl.js +9 -0
  29. package/vendor/symbiote-node/canvas/NodeCanvas/NodeCanvas.css.js +261 -0
  30. package/vendor/symbiote-node/canvas/NodeCanvas/NodeCanvas.js +1840 -0
  31. package/vendor/symbiote-node/canvas/NodeCanvas/NodeCanvas.tpl.js +22 -0
  32. package/vendor/symbiote-node/canvas/NodeSearch/NodeSearch.css.js +97 -0
  33. package/vendor/symbiote-node/canvas/NodeSearch/NodeSearch.js +132 -0
  34. package/vendor/symbiote-node/canvas/NodeSearch/NodeSearch.tpl.js +21 -0
  35. package/vendor/symbiote-node/canvas/NodeViewManager.js +584 -0
  36. package/vendor/symbiote-node/canvas/PinExpansion.js +131 -0
  37. package/vendor/symbiote-node/canvas/PseudoConnection.js +80 -0
  38. package/vendor/symbiote-node/canvas/SubgraphManager.js +201 -0
  39. package/vendor/symbiote-node/canvas/SubgraphRouter.js +443 -0
  40. package/vendor/symbiote-node/canvas/ViewportActions.js +446 -0
  41. package/vendor/symbiote-node/core/Connection.js +45 -0
  42. package/vendor/symbiote-node/core/Editor.js +451 -0
  43. package/vendor/symbiote-node/core/Frame.js +31 -0
  44. package/vendor/symbiote-node/core/GraphMermaid.js +348 -0
  45. package/vendor/symbiote-node/core/GraphText.js +210 -0
  46. package/vendor/symbiote-node/core/Node.js +143 -0
  47. package/vendor/symbiote-node/core/Portal.js +104 -0
  48. package/vendor/symbiote-node/core/Socket.js +185 -0
  49. package/vendor/symbiote-node/core/SubgraphNode.js +125 -0
  50. package/vendor/symbiote-node/index.js +103 -0
  51. package/vendor/symbiote-node/inspector/InspectorPanel/InspectorPanel.css.js +361 -0
  52. package/vendor/symbiote-node/inspector/InspectorPanel/InspectorPanel.js +332 -0
  53. package/vendor/symbiote-node/inspector/InspectorPanel/InspectorPanel.tpl.js +96 -0
  54. package/vendor/symbiote-node/inspector/TemplatePreview/TemplatePreview.css.js +104 -0
  55. package/vendor/symbiote-node/inspector/TemplatePreview/TemplatePreview.js +133 -0
  56. package/vendor/symbiote-node/inspector/TemplatePreview/TemplatePreview.tpl.js +33 -0
  57. package/vendor/symbiote-node/interactions/ConnectFlow.js +307 -0
  58. package/vendor/symbiote-node/interactions/Drag.js +102 -0
  59. package/vendor/symbiote-node/interactions/Selector.js +132 -0
  60. package/vendor/symbiote-node/interactions/SnapGrid.js +65 -0
  61. package/vendor/symbiote-node/interactions/Zoom.js +140 -0
  62. package/vendor/symbiote-node/layout/ActionZone/ActionZone.css.js +88 -0
  63. package/vendor/symbiote-node/layout/ActionZone/ActionZone.js +254 -0
  64. package/vendor/symbiote-node/layout/ActionZone/ActionZone.tpl.js +11 -0
  65. package/vendor/symbiote-node/layout/Layout/Layout.css.js +88 -0
  66. package/vendor/symbiote-node/layout/Layout/Layout.js +622 -0
  67. package/vendor/symbiote-node/layout/Layout/Layout.tpl.js +25 -0
  68. package/vendor/symbiote-node/layout/LayoutNode/LayoutNode.css.js +293 -0
  69. package/vendor/symbiote-node/layout/LayoutNode/LayoutNode.js +467 -0
  70. package/vendor/symbiote-node/layout/LayoutNode/LayoutNode.tpl.js +33 -0
  71. package/vendor/symbiote-node/layout/LayoutPreview/LayoutPreview.css.js +46 -0
  72. package/vendor/symbiote-node/layout/LayoutPreview/LayoutPreview.js +102 -0
  73. package/vendor/symbiote-node/layout/LayoutPreview/LayoutPreview.tpl.js +6 -0
  74. package/vendor/symbiote-node/layout/LayoutRouter/LayoutRouter.js +156 -0
  75. package/vendor/symbiote-node/layout/LayoutRouter/routerSync.js +250 -0
  76. package/vendor/symbiote-node/layout/LayoutSidebar/LayoutSidebar.css.js +379 -0
  77. package/vendor/symbiote-node/layout/LayoutSidebar/LayoutSidebar.js +263 -0
  78. package/vendor/symbiote-node/layout/LayoutSidebar/LayoutSidebar.tpl.js +20 -0
  79. package/vendor/symbiote-node/layout/LayoutSidebar/SidebarSection.js +183 -0
  80. package/vendor/symbiote-node/layout/LayoutTree.js +246 -0
  81. package/vendor/symbiote-node/layout/PanelMenu/PanelMenu.css.js +43 -0
  82. package/vendor/symbiote-node/layout/PanelMenu/PanelMenu.js +89 -0
  83. package/vendor/symbiote-node/layout/PanelMenu/PanelMenu.tpl.js +14 -0
  84. package/vendor/symbiote-node/layout/index.js +16 -0
  85. package/vendor/symbiote-node/menu/ContextMenu/ContextMenu.css.js +61 -0
  86. package/vendor/symbiote-node/menu/ContextMenu/ContextMenu.js +79 -0
  87. package/vendor/symbiote-node/menu/ContextMenu/ContextMenu.tpl.js +19 -0
  88. package/vendor/symbiote-node/node/CtrlItem/CtrlItem.css.js +41 -0
  89. package/vendor/symbiote-node/node/CtrlItem/CtrlItem.js +24 -0
  90. package/vendor/symbiote-node/node/CtrlItem/CtrlItem.tpl.js +16 -0
  91. package/vendor/symbiote-node/node/GraphFrame/GraphFrame.css.js +65 -0
  92. package/vendor/symbiote-node/node/GraphFrame/GraphFrame.js +29 -0
  93. package/vendor/symbiote-node/node/GraphFrame/GraphFrame.tpl.js +13 -0
  94. package/vendor/symbiote-node/node/GraphNode/GraphNode.css.js +683 -0
  95. package/vendor/symbiote-node/node/GraphNode/GraphNode.js +92 -0
  96. package/vendor/symbiote-node/node/GraphNode/GraphNode.tpl.js +17 -0
  97. package/vendor/symbiote-node/node/NodeSocket/NodeSocket.js +25 -0
  98. package/vendor/symbiote-node/node/NodeSocket/NodeSocket.tpl.js +7 -0
  99. package/vendor/symbiote-node/node/PortItem/PortItem.css.js +90 -0
  100. package/vendor/symbiote-node/node/PortItem/PortItem.js +87 -0
  101. package/vendor/symbiote-node/node/PortItem/PortItem.tpl.js +10 -0
  102. package/vendor/symbiote-node/package.json +59 -0
  103. package/vendor/symbiote-node/palette/PaletteBrowser/PaletteBrowser.css.js +143 -0
  104. package/vendor/symbiote-node/palette/PaletteBrowser/PaletteBrowser.js +131 -0
  105. package/vendor/symbiote-node/palette/PaletteBrowser/PaletteBrowser.tpl.js +16 -0
  106. package/vendor/symbiote-node/plugins/History.js +384 -0
  107. package/vendor/symbiote-node/plugins/Readonly.js +59 -0
  108. package/vendor/symbiote-node/shapes/CircleShape.js +80 -0
  109. package/vendor/symbiote-node/shapes/CommentShape.js +35 -0
  110. package/vendor/symbiote-node/shapes/DiamondShape.js +115 -0
  111. package/vendor/symbiote-node/shapes/NodeShape.js +80 -0
  112. package/vendor/symbiote-node/shapes/PillShape.js +91 -0
  113. package/vendor/symbiote-node/shapes/RectShape.js +72 -0
  114. package/vendor/symbiote-node/shapes/SVGShape.js +494 -0
  115. package/vendor/symbiote-node/shapes/index.js +53 -0
  116. package/vendor/symbiote-node/themes/Palette.js +32 -0
  117. package/vendor/symbiote-node/themes/Skin.js +113 -0
  118. package/vendor/symbiote-node/themes/Theme.js +84 -0
  119. package/vendor/symbiote-node/themes/carbon.js +137 -0
  120. package/vendor/symbiote-node/themes/dark.js +137 -0
  121. package/vendor/symbiote-node/themes/ebook.js +138 -0
  122. package/vendor/symbiote-node/themes/grey.js +137 -0
  123. package/vendor/symbiote-node/themes/light.js +137 -0
  124. package/vendor/symbiote-node/themes/neon.js +138 -0
  125. package/vendor/symbiote-node/themes/pcb.js +273 -0
  126. package/vendor/symbiote-node/themes/synthwave.js +137 -0
  127. package/vendor/symbiote-node/toolbar/QuickToolbar/QuickToolbar.css.js +86 -0
  128. package/vendor/symbiote-node/toolbar/QuickToolbar/QuickToolbar.js +128 -0
  129. package/vendor/symbiote-node/toolbar/QuickToolbar/QuickToolbar.tpl.js +29 -0
  130. package/web/app.js +6 -5
  131. package/web/components/canvas-graph.js +1666 -0
  132. package/web/components/event-feed/CodeWidget.js +32 -0
  133. package/web/components/event-feed/EventWidget.js +97 -0
  134. package/web/components/event-feed/ListWidget.js +57 -0
  135. package/web/components/event-feed/MiniGraphWidget.js +69 -0
  136. package/web/dashboard.js +1 -1
  137. package/web/index.html +4 -0
  138. package/web/panels/ActionBoard/ActionBoard.js +1 -1
  139. package/web/panels/SettingsPanel/SettingsPanel.tpl.js +1 -1
  140. package/web/panels/code-viewer.js +50 -15
  141. package/web/panels/dep-graph.js +2712 -7
  142. package/web/panels/file-tree.js +5 -2
  143. package/web/panels/live-monitor.js +75 -3
  144. package/web/style.css +33 -0
  145. package/docs/img/explorer-compact.jpg +0 -0
  146. package/docs/img/explorer-expanded.jpg +0 -0
  147. package/src/.contextignore +0 -22
  148. package/src/.project-graph-cache.json +0 -1
  149. package/src/compact/.project-graph-cache.json +0 -1
  150. package/web/.project-graph-cache.json +0 -1
  151. package/web/panels/SettingsPanel/.project-graph-cache.json +0 -1
@@ -0,0 +1,683 @@
1
+ import { css } from '@symbiotejs/symbiote';
2
+
3
+ export const styles = css`
4
+ graph-node {
5
+ display: block;
6
+ min-width: var(--sn-node-min-width, 180px);
7
+ max-width: var(--sn-node-max-width, 280px);
8
+ border-radius: var(--sn-node-radius, 10px);
9
+ background: var(--sn-node-bg, #16213e);
10
+ border: var(--sn-node-border-width, 2px) solid var(--sn-node-border, #2a2a4a);
11
+ box-shadow: var(--sn-node-shadow, 0 4px 16px var(--sn-shadow-color, rgba(0, 0, 0, 0.3)));
12
+ user-select: none;
13
+ cursor: move;
14
+ transition: border-color 0.2s ease-out, box-shadow 0.2s ease-out, opacity 0.2s ease-out;
15
+ overflow: visible;
16
+ font-family: var(--sn-font, 'Inter', sans-serif);
17
+ font-size: var(--sn-node-font-size, 13px);
18
+ -webkit-font-smoothing: antialiased;
19
+
20
+ /* Symbiote animateOut: CSS-driven exit transition */
21
+ &[leaving] {
22
+ opacity: 0;
23
+ transform: scale(0.92);
24
+ transition: border-color 0.2s ease-out, box-shadow 0.2s ease-out, opacity 0.2s ease-out, transform 0.2s ease-out;
25
+ }
26
+
27
+ &[data-selected] {
28
+ border-color: var(--sn-node-selected, #4a9eff);
29
+ box-shadow: 0 0 20px color-mix(in srgb, var(--sn-node-selected, #4a9eff) 30%, transparent);
30
+ }
31
+
32
+ &[data-collapsed] {
33
+ & .controls {
34
+ display: none;
35
+ }
36
+ & .sn-node-body {
37
+ padding: 4px 0;
38
+ }
39
+ & .port-label {
40
+ display: none;
41
+ }
42
+ }
43
+
44
+ &[data-muted] {
45
+ opacity: 0.45;
46
+ filter: saturate(0.3);
47
+
48
+ & .sn-node-label {
49
+ text-decoration: line-through;
50
+ }
51
+ }
52
+
53
+ /* Compact mode — hide node body (ports & controls).
54
+ Activated by canvas.setCompactMode(true) which sets data-compact on the canvas.
55
+ SubgraphNodes (node-type="subgraph") keep body visible for inner graph preview. */
56
+ node-canvas[data-compact] &:not([node-type="subgraph"]) .sn-node-body {
57
+ display: none;
58
+ }
59
+
60
+ /* LOD: medium — strip heavy rendering for performance */
61
+ &[data-lod="medium"] {
62
+ box-shadow: none;
63
+ transition: none;
64
+ will-change: auto;
65
+ pointer-events: auto;
66
+
67
+ &:hover {
68
+ border-color: var(--sn-node-border, #2a2a4a);
69
+ }
70
+
71
+ &[data-selected] {
72
+ box-shadow: none;
73
+ }
74
+
75
+ &[data-processing] {
76
+ box-shadow: none;
77
+ animation: none;
78
+ }
79
+
80
+ &[data-completed] {
81
+ box-shadow: none;
82
+ }
83
+
84
+ &[data-muted] {
85
+ filter: none;
86
+ opacity: 0.5;
87
+ }
88
+
89
+ & .controls,
90
+ & .port-label,
91
+ & .sn-subgraph-preview,
92
+ & .error-frame,
93
+ & .sn-quick-toolbar {
94
+ display: none;
95
+ }
96
+
97
+ & .sn-node-body {
98
+ padding: 2px 0;
99
+ }
100
+
101
+ & node-socket {
102
+ transition: none;
103
+ cursor: default;
104
+
105
+ &:hover {
106
+ transform: none;
107
+ box-shadow: none;
108
+ }
109
+ }
110
+ }
111
+
112
+
113
+ &[node-type="subgraph"] .sn-subgraph-preview {
114
+ display: block;
115
+ width: 100%;
116
+ height: 80px;
117
+ border-top: 1px solid color-mix(in srgb, currentColor 6%, transparent);
118
+ opacity: 0.7;
119
+ }
120
+
121
+ &[data-processing] {
122
+ border-color: var(--sn-node-accent, var(--sn-node-selected, #4a9eff));
123
+ box-shadow: 0 0 16px color-mix(in srgb, var(--sn-node-accent, #4a9eff) 40%, transparent),
124
+ 0 0 4px color-mix(in srgb, var(--sn-node-accent, #4a9eff) 60%, transparent);
125
+ animation: sn-node-pulse 1s ease-in-out infinite;
126
+ }
127
+
128
+ &[data-completed] {
129
+ border-color: var(--sn-success-color, #5cd87a);
130
+ box-shadow: 0 0 8px color-mix(in srgb, var(--sn-success-color, #5cd87a) 30%, transparent);
131
+ }
132
+
133
+ &[data-error] {
134
+ border-color: color-mix(in srgb, var(--sn-danger-color, #ef4444) 60%, transparent);
135
+ position: relative;
136
+ }
137
+
138
+ &[data-error] .sn-node-header {
139
+ background: color-mix(in srgb, var(--sn-danger-color, #ef4444) 10%, transparent);
140
+ }
141
+
142
+ & .error-frame {
143
+ position: absolute;
144
+ bottom: calc(100% + 10px);
145
+ left: 50%;
146
+ transform: translateX(-50%);
147
+ min-width: 140px;
148
+ max-width: 320px;
149
+ border: 2px solid color-mix(in srgb, var(--sn-danger-color, #ef4444) 60%, transparent);
150
+ border-radius: 12px;
151
+ background: color-mix(in srgb, var(--sn-danger-color, #ef4444) 8%, transparent);
152
+ pointer-events: none;
153
+ z-index: 10;
154
+ transition: bottom 0.15s ease;
155
+ }
156
+
157
+ & .error-frame-header {
158
+ display: flex;
159
+ align-items: center;
160
+ gap: 6px;
161
+ padding: 5px 10px;
162
+ font-size: 12px;
163
+ font-weight: 600;
164
+ color: color-mix(in srgb, var(--sn-danger-color, #ef4444) 90%, white);
165
+ border-bottom: 1px solid color-mix(in srgb, var(--sn-danger-color, #ef4444) 20%, transparent);
166
+ user-select: none;
167
+ }
168
+
169
+ & .error-frame-header .material-symbols-outlined {
170
+ font-size: 14px;
171
+ opacity: 0.8;
172
+ }
173
+
174
+ & .error-frame-body {
175
+ padding: 6px 10px;
176
+ font-size: 11px;
177
+ line-height: 1.4;
178
+ color: color-mix(in srgb, var(--sn-danger-color, #ef4444) 75%, white);
179
+ word-wrap: break-word;
180
+ }
181
+
182
+
183
+
184
+ &[data-selected] .error-frame {
185
+ bottom: calc(100% + 46px);
186
+ }
187
+
188
+ &:hover {
189
+ border-color: var(--sn-node-hover, #3a3a6a);
190
+ }
191
+
192
+ &[node-category="server"] {
193
+ --sn-node-accent: var(--sn-cat-server, #4a9eff);
194
+ }
195
+ &[node-category="instance"] {
196
+ --sn-node-accent: var(--sn-cat-instance, #4ade80);
197
+ }
198
+ &[node-category="control"] {
199
+ --sn-node-accent: var(--sn-cat-control, #fbbf24);
200
+ }
201
+ &[node-category="data"] {
202
+ --sn-node-accent: var(--sn-cat-data, #a78bfa);
203
+ }
204
+ &[node-category="default"] {
205
+ --sn-node-accent: var(--sn-cat-default, #94a3b8);
206
+ }
207
+ /* Codebase categories */
208
+ &[node-category="directory"] {
209
+ --sn-node-accent: var(--sn-cat-directory, #f0b840);
210
+ }
211
+ &[node-category="file"] {
212
+ --sn-node-accent: var(--sn-cat-file, #5cb8ff);
213
+ }
214
+ &[node-category="function"] {
215
+ --sn-node-accent: var(--sn-cat-function, #4ade80);
216
+ }
217
+ &[node-category="class"] {
218
+ --sn-node-accent: var(--sn-cat-class, #a78bfa);
219
+ }
220
+ &[node-category="module"] {
221
+ --sn-node-accent: var(--sn-cat-module, #ff6b9d);
222
+ }
223
+
224
+ /* Shape: pill — compact horizontal capsule */
225
+ &[node-shape="pill"] {
226
+ min-width: 100px;
227
+ max-width: 200px;
228
+ border-radius: 999px;
229
+
230
+ & .sn-node-header {
231
+ display: none;
232
+ }
233
+ & .sn-node-body {
234
+ padding: 8px 20px;
235
+ align-items: center;
236
+ justify-content: center;
237
+ flex-direction: row;
238
+ gap: 8px;
239
+ }
240
+ & .inputs, & .outputs {
241
+ padding: 0;
242
+ }
243
+ & .sn-port-in node-socket {
244
+ margin-left: -26px;
245
+ }
246
+ & .sn-port-out node-socket {
247
+ margin-right: -26px;
248
+ }
249
+ & .controls {
250
+ display: none;
251
+ }
252
+ }
253
+
254
+ /* Shape: circle — hub/connector node */
255
+ &[node-shape="circle"] {
256
+ min-width: 100px;
257
+ min-height: 100px;
258
+ border-radius: 50%;
259
+ aspect-ratio: 1;
260
+ display: flex;
261
+ flex-direction: column;
262
+ align-items: center;
263
+ justify-content: center;
264
+
265
+ & .sn-node-header {
266
+ background: transparent;
267
+ border-bottom: none;
268
+ justify-content: center;
269
+ padding: 6px;
270
+ }
271
+ & .sn-node-body {
272
+ padding: 0 8px 8px;
273
+ flex-direction: row;
274
+ align-items: center;
275
+ gap: 0;
276
+ }
277
+ & .inputs {
278
+ position: absolute;
279
+ left: -6px;
280
+ top: 50%;
281
+ transform: translateY(-50%);
282
+ }
283
+ & .outputs {
284
+ position: absolute;
285
+ right: -6px;
286
+ top: 50%;
287
+ transform: translateY(-50%);
288
+ }
289
+ & .port-label {
290
+ display: none;
291
+ }
292
+ & .controls {
293
+ display: none;
294
+ }
295
+ }
296
+
297
+ /* Shape: diamond — condition/decision */
298
+ &[node-shape="diamond"] {
299
+ min-width: 100px;
300
+ min-height: 100px;
301
+ border-radius: 4px;
302
+ transform-origin: center;
303
+ clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
304
+ display: flex;
305
+ align-items: center;
306
+ justify-content: center;
307
+
308
+ & .sn-node-header {
309
+ display: none;
310
+ }
311
+ & .sn-node-body {
312
+ text-align: center;
313
+ padding: 25% 10%;
314
+ }
315
+ & .controls {
316
+ display: none;
317
+ }
318
+ }
319
+
320
+ /* Shape: comment — annotation banner */
321
+ &[node-shape="comment"] {
322
+ min-width: 200px;
323
+ max-width: 400px;
324
+ border-radius: var(--sn-comment-radius, 6px);
325
+ background: var(--sn-comment-bg, rgba(255, 255, 255, 0.05));
326
+ border-color: var(--sn-comment-border, rgba(255, 255, 255, 0.1));
327
+ cursor: default;
328
+ box-shadow: none;
329
+
330
+ & .sn-node-header {
331
+ display: none;
332
+ }
333
+ & .sn-node-body {
334
+ padding: 12px 16px;
335
+ }
336
+ & .inputs, & .outputs {
337
+ display: none;
338
+ }
339
+ }
340
+
341
+ /* Shape: SVG custom — node with SVG background path */
342
+ &[data-svg-shape] {
343
+ position: relative;
344
+ min-width: 100px;
345
+ min-height: 100px;
346
+ background: transparent;
347
+ border: none;
348
+ box-shadow: none;
349
+ border-radius: 0;
350
+ overflow: visible;
351
+ cursor: default;
352
+
353
+ /* Move cursor only over SVG path fill area */
354
+ & > svg {
355
+ pointer-events: none;
356
+ & > path {
357
+ pointer-events: visibleFill;
358
+ cursor: move;
359
+ transition: stroke 0.2s ease;
360
+ }
361
+ }
362
+
363
+ & .sn-node-header {
364
+ position: absolute;
365
+ top: -28px;
366
+ left: 50%;
367
+ transform: translateX(-50%);
368
+ background: var(--sn-node-header-bg, color-mix(in srgb, var(--sn-node-accent, #4a9eff) 20%, var(--sn-node-bg, #16213e)));
369
+ border: 2px solid var(--sn-node-border, #2a2a4a);
370
+ border-radius: 20px;
371
+ padding: 3px 12px;
372
+ white-space: nowrap;
373
+ z-index: 1;
374
+ opacity: 0;
375
+ pointer-events: none;
376
+ transition: opacity 0.2s ease;
377
+ }
378
+ &:hover .sn-node-header {
379
+ opacity: 1;
380
+ }
381
+ &[data-selected] .sn-node-header {
382
+ opacity: 0;
383
+ }
384
+ & .sn-node-body {
385
+ padding: 4px 0;
386
+ align-items: center;
387
+ }
388
+ & .controls {
389
+ display: none;
390
+ }
391
+ & .sn-shape-watermark {
392
+ position: absolute;
393
+ top: 50%;
394
+ left: 50%;
395
+ transform: translate(-50%, -50%);
396
+ font-size: 40px;
397
+ color: var(--sn-node-accent, var(--sn-text-dim, #888));
398
+ pointer-events: none;
399
+ z-index: 0;
400
+ }
401
+ /* Hide DOM port items — ConnectionRenderer computes positions mathematically */
402
+ & .inputs, & .outputs {
403
+ display: none;
404
+ }
405
+ }
406
+
407
+ /* SVG shape states — target inline svg > path directly.
408
+ border/box-shadow have no effect on SVG nodes (border: none).
409
+ Only stroke COLOR changes on state — width stays the same as inactive,
410
+ matching HTML nodes where border-width stays 1px on selection. */
411
+ &[data-svg-shape][data-selected] > svg > path {
412
+ stroke: var(--sn-node-selected, #4a9eff);
413
+ transition: stroke 0.2s ease;
414
+ }
415
+
416
+ &[data-svg-shape][data-error] > svg > path {
417
+ stroke: var(--sn-danger-color, #ef4444);
418
+ transition: stroke 0.2s ease;
419
+ }
420
+
421
+ &[data-svg-shape][data-muted] > svg > path {
422
+ opacity: 0.35;
423
+ filter: saturate(0.2);
424
+ transition: opacity 0.2s ease, filter 0.2s ease;
425
+ }
426
+
427
+ &[data-svg-shape][data-processing] > svg > path {
428
+ stroke: var(--sn-node-accent, var(--sn-node-selected, #4a9eff));
429
+ animation: sn-svg-pulse 1s ease-in-out infinite;
430
+ }
431
+
432
+ &[data-svg-shape][data-completed] > svg > path {
433
+ stroke: var(--sn-success-color, #5cd87a);
434
+ transition: stroke 0.2s ease;
435
+ }
436
+
437
+ & .sn-node-header {
438
+ display: flex;
439
+ align-items: center;
440
+ gap: 6px;
441
+ padding: 8px 12px;
442
+ background: var(--sn-node-header-bg, color-mix(in srgb, var(--sn-node-accent) 15%, transparent));
443
+ border-bottom: 1px solid color-mix(in srgb, currentColor 6%, transparent);
444
+ border-radius: var(--sn-node-radius, 10px) var(--sn-node-radius, 10px) 0 0;
445
+ }
446
+
447
+ & .sn-node-icon {
448
+ font-size: 18px;
449
+ color: var(--sn-node-accent);
450
+ opacity: 1;
451
+ }
452
+
453
+ & .sn-node-label {
454
+ font-weight: 600;
455
+ font-size: var(--sn-node-label-size, inherit);
456
+ color: var(--sn-text, #e2e8f0);
457
+ opacity: 1;
458
+ white-space: nowrap;
459
+ overflow: hidden;
460
+ text-overflow: ellipsis;
461
+ }
462
+
463
+ & .sn-node-body {
464
+ padding: 8px 0;
465
+ display: flex;
466
+ flex-direction: column;
467
+ gap: 4px;
468
+ }
469
+
470
+ & .inputs, & .outputs {
471
+ display: flex;
472
+ flex-direction: column;
473
+ transition: transform 0.15s ease;
474
+ }
475
+
476
+ /* Port hint: slide ports to nearest side during connector drag */
477
+ &[data-port-hint="right"] .inputs {
478
+ transform: translateX(calc(100% - 12px));
479
+ }
480
+
481
+ &[data-port-hint="left"] .outputs {
482
+ transform: translateX(calc(-100% + 12px));
483
+ }
484
+
485
+ /* Compatible SVG node during drag: subtle outline hint only — dots handle highlight */
486
+ &[data-svg-shape][data-port-hint] > svg > path {
487
+ stroke: var(--sn-node-selected, #4a9eff);
488
+ stroke-width: 0.5;
489
+ opacity: 0.8;
490
+ transition: stroke 0.15s ease, opacity 0.15s ease;
491
+ }
492
+
493
+ /* Incompatible dimming for nodes without compatible ports */
494
+ &[data-port-hint="none"] {
495
+ opacity: 0.4;
496
+ pointer-events: none;
497
+ }
498
+
499
+ & .sn-port {
500
+ display: flex;
501
+ align-items: center;
502
+ gap: 6px;
503
+ padding: 3px 12px;
504
+ min-height: 24px;
505
+ }
506
+
507
+ & .sn-port-in {
508
+ flex-direction: row;
509
+
510
+ & node-socket {
511
+ margin-left: -18px;
512
+ }
513
+ }
514
+
515
+ & .sn-port-out {
516
+ flex-direction: row;
517
+ justify-content: flex-end;
518
+
519
+ & node-socket {
520
+ margin-right: -18px;
521
+ }
522
+ }
523
+
524
+ & .port-label {
525
+ color: var(--sn-text-dim, #94a3b8);
526
+ font-size: 12px;
527
+ white-space: nowrap;
528
+ }
529
+
530
+ & .controls {
531
+ padding: 0 12px;
532
+ }
533
+
534
+ & .sn-control-item {
535
+ display: flex;
536
+ flex-direction: column;
537
+ gap: 2px;
538
+ margin: 4px 0;
539
+ }
540
+
541
+ & .sn-control-label {
542
+ font-size: 10px;
543
+ text-transform: uppercase;
544
+ color: var(--sn-text-dim, #94a3b8);
545
+ letter-spacing: 0.5px;
546
+ }
547
+
548
+ & .sn-control-input {
549
+ background: rgba(0, 0, 0, 0.3);
550
+ border: 1px solid color-mix(in srgb, currentColor 10%, transparent);
551
+ border-radius: 4px;
552
+ padding: 4px 8px;
553
+ color: var(--sn-text, #e2e8f0);
554
+ font-size: 12px;
555
+ outline: none;
556
+ font-family: 'JetBrains Mono', 'Fira Code', 'SF Mono', monospace;
557
+
558
+ &:focus {
559
+ border-color: var(--sn-node-accent);
560
+ }
561
+
562
+ &[readonly] {
563
+ opacity: 0.6;
564
+ cursor: default;
565
+ }
566
+ }
567
+ }
568
+
569
+ /* Preview Area — image/text preview at bottom of node */
570
+ .sn-preview {
571
+ border-top: 1px solid var(--sn-node-border, rgba(255, 255, 255, 0.06));
572
+ border-radius: 0 0 8px 8px;
573
+ overflow: hidden;
574
+ max-height: 120px;
575
+ background: rgba(0, 0, 0, 0.2);
576
+
577
+ &[hidden] {
578
+ display: none;
579
+ }
580
+
581
+ & img {
582
+ width: 100%;
583
+ height: auto;
584
+ display: block;
585
+ object-fit: cover;
586
+ max-height: 120px;
587
+ }
588
+
589
+ & .sn-preview-text {
590
+ padding: 6px 10px;
591
+ font-size: 11px;
592
+ color: var(--sn-text-dim, #94a3b8);
593
+ font-family: 'JetBrains Mono', 'Fira Code', monospace;
594
+ white-space: pre-wrap;
595
+ word-break: break-all;
596
+ line-height: 1.4;
597
+ }
598
+ }
599
+
600
+ node-socket {
601
+ display: block;
602
+ width: 12px;
603
+ height: 12px;
604
+ border-radius: 50%;
605
+ background: var(--socket-color, var(--sn-node-accent, #4a9eff));
606
+ border: 2px solid var(--sn-node-bg, #16213e);
607
+ cursor: crosshair;
608
+ flex-shrink: 0;
609
+ transition: transform 0.2s ease-out, box-shadow 0.2s ease-out;
610
+ z-index: 10;
611
+ position: relative;
612
+
613
+ /* 44×44px invisible touch target */
614
+ &::before {
615
+ content: '';
616
+ position: absolute;
617
+ top: 50%;
618
+ left: 50%;
619
+ width: 44px;
620
+ height: 44px;
621
+ transform: translate(-50%, -50%);
622
+ }
623
+
624
+ &:hover {
625
+ transform: scale(1.3);
626
+ box-shadow: 0 0 8px var(--socket-color, var(--sn-node-accent));
627
+ }
628
+
629
+ /* Port shape: square — array/object types */
630
+ &[data-socket-shape="square"] {
631
+ border-radius: 2px;
632
+ }
633
+
634
+ /* Port shape: diamond — execution/trigger */
635
+ &[data-socket-shape="diamond"] {
636
+ border-radius: 1px;
637
+ transform: rotate(45deg) scale(0.85);
638
+
639
+ &:hover {
640
+ transform: rotate(45deg) scale(1.1);
641
+ }
642
+ }
643
+
644
+ /* Port shape: triangle — trigger/event */
645
+ &[data-socket-shape="triangle"] {
646
+ border-radius: 0;
647
+ background: transparent;
648
+ width: 0;
649
+ height: 0;
650
+ border: 6px solid transparent;
651
+ border-left: 10px solid var(--socket-color, var(--sn-node-accent));
652
+ border-right: none;
653
+ }
654
+ }
655
+
656
+ @keyframes sn-node-pulse {
657
+ 0%, 100% { opacity: 1; }
658
+ 50% { opacity: 0.7; }
659
+ }
660
+
661
+ @keyframes sn-node-error-pulse {
662
+ 0%, 100% { box-shadow: 0 0 16px color-mix(in srgb, var(--sn-danger-color, #ef4444) 35%, transparent), 0 0 4px color-mix(in srgb, var(--sn-danger-color, #ef4444) 50%, transparent); }
663
+ 50% { box-shadow: 0 0 24px color-mix(in srgb, var(--sn-danger-color, #ef4444) 50%, transparent), 0 0 8px color-mix(in srgb, var(--sn-danger-color, #ef4444) 70%, transparent); }
664
+ }
665
+
666
+ @keyframes sn-svg-pulse {
667
+ 0%, 100% { opacity: 0.7; transform: scale(1); }
668
+ 50% { opacity: 1; transform: scale(1.05); }
669
+ }
670
+
671
+
672
+
673
+ /* Accessibility: reduced motion */
674
+ @media (prefers-reduced-motion: reduce) {
675
+ graph-node {
676
+ transition: none;
677
+ animation: none;
678
+ }
679
+ node-socket {
680
+ transition: none;
681
+ }
682
+ }
683
+ `;