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.
Files changed (322) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/LICENSE +21 -0
  3. package/README.md +76 -0
  4. package/canvas/AutoLayout.js +731 -0
  5. package/canvas/Breadcrumb/Breadcrumb.css.js +75 -0
  6. package/canvas/Breadcrumb/Breadcrumb.js +96 -0
  7. package/canvas/Breadcrumb/Breadcrumb.tpl.js +7 -0
  8. package/canvas/CanvasConnectionRenderer.js +971 -0
  9. package/canvas/CanvasGraph/CanvasGraph.css.js +29 -0
  10. package/canvas/CanvasGraph/CanvasGraph.js +1697 -0
  11. package/canvas/CanvasGraph/CanvasGraphDrawState.js +280 -0
  12. package/canvas/CanvasGraph/CanvasGraphGeometry.js +194 -0
  13. package/canvas/CanvasViewport.js +550 -0
  14. package/canvas/ConnectionRenderer.js +1283 -0
  15. package/canvas/FlowSimulator.js +326 -0
  16. package/canvas/ForceLayout.js +226 -0
  17. package/canvas/ForceWorker.js +1303 -0
  18. package/canvas/FrameManager.js +223 -0
  19. package/canvas/GraphExplorerShell/GraphExplorerShell.css.js +136 -0
  20. package/canvas/GraphExplorerShell/GraphExplorerShell.js +129 -0
  21. package/canvas/GraphExplorerShell/GraphExplorerShell.tpl.js +12 -0
  22. package/canvas/GraphTabs/GraphTabs.css.js +101 -0
  23. package/canvas/GraphTabs/GraphTabs.js +189 -0
  24. package/canvas/GraphTabs/GraphTabs.tpl.js +12 -0
  25. package/canvas/LODManager.js +88 -0
  26. package/canvas/Minimap/Minimap.css.js +73 -0
  27. package/canvas/Minimap/Minimap.js +210 -0
  28. package/canvas/Minimap/Minimap.tpl.js +7 -0
  29. package/canvas/NodeCanvas/NodeCanvas.css.js +398 -0
  30. package/canvas/NodeCanvas/NodeCanvas.js +1499 -0
  31. package/canvas/NodeCanvas/NodeCanvas.tpl.js +22 -0
  32. package/canvas/NodeSearch/NodeSearch.css.js +97 -0
  33. package/canvas/NodeSearch/NodeSearch.js +140 -0
  34. package/canvas/NodeSearch/NodeSearch.tpl.js +25 -0
  35. package/canvas/NodeViewManager.js +748 -0
  36. package/canvas/PcbRouteDiagnostics.js +463 -0
  37. package/canvas/PcbRouter.js +1127 -0
  38. package/canvas/PinExpansion.js +134 -0
  39. package/canvas/PseudoConnection.js +84 -0
  40. package/canvas/SelectionSync.js +163 -0
  41. package/canvas/SubgraphManager.js +203 -0
  42. package/canvas/SubgraphRouter.js +452 -0
  43. package/canvas/ViewportActions.js +473 -0
  44. package/canvas/graph-explorer.js +339 -0
  45. package/canvas/graph-layout.js +148 -0
  46. package/canvas/graph-model.js +68 -0
  47. package/canvas/html-in-canvas.js +202 -0
  48. package/canvas/project-graph-builder.js +440 -0
  49. package/canvas/project-graph-model.js +183 -0
  50. package/chat/ChatComposer/ChatComposer.css.js +652 -0
  51. package/chat/ChatComposer/ChatComposer.js +304 -0
  52. package/chat/ChatList/ChatList.css.js +102 -0
  53. package/chat/ChatList/ChatList.js +99 -0
  54. package/chat/ChatList/ChatList.tpl.js +20 -0
  55. package/chat/ChatListItem/ChatListItem.css.js +117 -0
  56. package/chat/ChatListItem/ChatListItem.js +32 -0
  57. package/chat/ChatListItem/ChatListItem.tpl.js +17 -0
  58. package/chat/ChatMessageItem/ChatMessageItem.css.js +628 -0
  59. package/chat/ChatMessageItem/ChatMessageItem.js +156 -0
  60. package/chat/ChatSidebar/ChatSidebar.css.js +150 -0
  61. package/chat/ChatSidebar/ChatSidebar.js +230 -0
  62. package/chat/ChatSidebar/ChatSidebar.tpl.js +18 -0
  63. package/chat/ChatSidebar/constants.js +11 -0
  64. package/chat/ChatSidebarItem/ChatSidebarItem.css.js +445 -0
  65. package/chat/ChatSidebarItem/ChatSidebarItem.js +304 -0
  66. package/chat/ChatTranscript/ChatTranscript.css.js +90 -0
  67. package/chat/ChatTranscript/ChatTranscript.js +244 -0
  68. package/chat/chat-context.js +123 -0
  69. package/chat/message-model.js +156 -0
  70. package/cli.js +20 -0
  71. package/control/Button/Button.css.js +93 -0
  72. package/control/Button/Button.js +78 -0
  73. package/control/Button/Button.tpl.js +3 -0
  74. package/control/Field/Field.css.js +91 -0
  75. package/control/Field/Field.js +17 -0
  76. package/control/Field/Field.tpl.js +3 -0
  77. package/core/Connection.js +47 -0
  78. package/core/Editor.js +449 -0
  79. package/core/Frame.js +33 -0
  80. package/core/GraphMermaid.js +348 -0
  81. package/core/GraphText.js +228 -0
  82. package/core/Node.js +145 -0
  83. package/core/Portal.js +106 -0
  84. package/core/Socket.js +187 -0
  85. package/core/SubgraphNode.js +121 -0
  86. package/core/base-path.js +55 -0
  87. package/core/dom-utils.js +14 -0
  88. package/core/index.js +18 -0
  89. package/core/local-cache.js +26 -0
  90. package/core/state-sync.js +227 -0
  91. package/custom-elements.json +6380 -0
  92. package/discover.js +240 -0
  93. package/display/Badge/Badge.css.js +44 -0
  94. package/display/Badge/Badge.js +17 -0
  95. package/display/Badge/Badge.tpl.js +3 -0
  96. package/display/Banner/Banner.css.js +61 -0
  97. package/display/Banner/Banner.js +17 -0
  98. package/display/Banner/Banner.tpl.js +3 -0
  99. package/display/CodeBlock/CodeBlock.css.js +194 -0
  100. package/display/CodeBlock/CodeBlock.js +220 -0
  101. package/display/CodeBlock/CodeBlock.tpl.js +11 -0
  102. package/display/DataTable/DataTable.css.js +101 -0
  103. package/display/DataTable/DataTable.js +136 -0
  104. package/display/DataTable/DataTable.tpl.js +13 -0
  105. package/display/EmptyState/EmptyState.css.js +33 -0
  106. package/display/EmptyState/EmptyState.js +17 -0
  107. package/display/EmptyState/EmptyState.tpl.js +3 -0
  108. package/display/EventFeed/EventFeed.css.js +145 -0
  109. package/display/EventFeed/EventFeed.js +64 -0
  110. package/display/EventFeed/EventFeed.tpl.js +14 -0
  111. package/display/EventFeed/EventFeedItem.js +116 -0
  112. package/display/EventFeed/EventFeedItem.tpl.js +22 -0
  113. package/display/LoadingOverlay/LoadingOverlay.css.js +91 -0
  114. package/display/LoadingOverlay/LoadingOverlay.js +48 -0
  115. package/display/LoadingOverlay/LoadingOverlay.tpl.js +12 -0
  116. package/display/Metric/Metric.css.js +60 -0
  117. package/display/Metric/Metric.js +17 -0
  118. package/display/Metric/Metric.tpl.js +6 -0
  119. package/display/OutputGraphPreview/OutputGraphPreview.css.js +122 -0
  120. package/display/OutputGraphPreview/OutputGraphPreview.js +89 -0
  121. package/display/OutputGraphPreview/OutputGraphPreview.tpl.js +13 -0
  122. package/display/OutputListPreview/OutputListPreview.css.js +109 -0
  123. package/display/OutputListPreview/OutputListPreview.js +77 -0
  124. package/display/OutputListPreview/OutputListPreview.tpl.js +13 -0
  125. package/display/SourceEditor/SourceEditor.css.js +39 -0
  126. package/display/SourceEditor/SourceEditor.js +129 -0
  127. package/display/SourceEditor/SourceEditor.tpl.js +10 -0
  128. package/display/SourceViewer/SourceViewer.css.js +80 -0
  129. package/display/SourceViewer/SourceViewer.js +418 -0
  130. package/display/SourceViewer/SourceViewer.tpl.js +17 -0
  131. package/display/StatusRibbon/StatusRibbon.css.js +73 -0
  132. package/display/StatusRibbon/StatusRibbon.js +87 -0
  133. package/display/StatusRibbon/StatusRibbon.tpl.js +7 -0
  134. package/display/event-feed-adapter.js +72 -0
  135. package/display/format-utils.js +29 -0
  136. package/display/highlight.js +659 -0
  137. package/display/icons.js +37 -0
  138. package/display/markdown-formatter.js +60 -0
  139. package/display/network-approval-page.js +487 -0
  140. package/display/output-preview.js +261 -0
  141. package/effects/CellBg/CellBg.css.js +33 -0
  142. package/effects/CellBg/CellBg.js +410 -0
  143. package/effects/CellBg/CellBg.tpl.js +5 -0
  144. package/graph/canvas-adapter.js +223 -0
  145. package/graph/graph-algorithms.js +31 -0
  146. package/graph/index.js +46 -0
  147. package/graph/model.js +176 -0
  148. package/graph/project-graph-build.js +66 -0
  149. package/graph/project-graph-metadata.js +253 -0
  150. package/graph/project-package.js +128 -0
  151. package/graph/project-runtime.js +116 -0
  152. package/graph/project-transaction.js +284 -0
  153. package/graph/skeleton-utils.js +84 -0
  154. package/graph/theme-contract.js +36 -0
  155. package/graph/transaction-parser.js +56 -0
  156. package/icons/MaterialSymbols.js +69 -0
  157. package/icons/material-symbols-outlined-400.ttf +0 -0
  158. package/icons/material-symbols.css +24 -0
  159. package/index.js +95 -0
  160. package/inspector/InspectorPanel/InspectorPanel.css.js +375 -0
  161. package/inspector/InspectorPanel/InspectorPanel.js +368 -0
  162. package/inspector/InspectorPanel/InspectorPanel.tpl.js +96 -0
  163. package/inspector/TemplatePreview/TemplatePreview.css.js +104 -0
  164. package/inspector/TemplatePreview/TemplatePreview.js +145 -0
  165. package/inspector/TemplatePreview/TemplatePreview.tpl.js +33 -0
  166. package/interactions/ConnectFlow.js +304 -0
  167. package/interactions/Drag.js +104 -0
  168. package/interactions/Selector.js +133 -0
  169. package/interactions/SnapGrid.js +66 -0
  170. package/interactions/Zoom.js +139 -0
  171. package/layout/ActionZone/ActionZone.css.js +88 -0
  172. package/layout/ActionZone/ActionZone.js +261 -0
  173. package/layout/ActionZone/ActionZone.tpl.js +11 -0
  174. package/layout/CrossLayoutPortalBridge/CrossLayoutPortalBridge.js +255 -0
  175. package/layout/Layout/Layout.css.js +91 -0
  176. package/layout/Layout/Layout.js +637 -0
  177. package/layout/Layout/Layout.tpl.js +27 -0
  178. package/layout/LayoutNode/LayoutNode.css.js +302 -0
  179. package/layout/LayoutNode/LayoutNode.js +509 -0
  180. package/layout/LayoutNode/LayoutNode.tpl.js +39 -0
  181. package/layout/LayoutPreview/LayoutPreview.css.js +46 -0
  182. package/layout/LayoutPreview/LayoutPreview.js +102 -0
  183. package/layout/LayoutPreview/LayoutPreview.tpl.js +6 -0
  184. package/layout/LayoutRouter/LayoutRouter.js +274 -0
  185. package/layout/LayoutRouter/SectionRegistry.js +135 -0
  186. package/layout/LayoutRouter/routerSync.js +250 -0
  187. package/layout/LayoutSidebar/LayoutSidebar.css.js +411 -0
  188. package/layout/LayoutSidebar/LayoutSidebar.js +368 -0
  189. package/layout/LayoutSidebar/LayoutSidebar.tpl.js +26 -0
  190. package/layout/LayoutSidebar/SidebarSection.css.js +20 -0
  191. package/layout/LayoutSidebar/SidebarSection.js +184 -0
  192. package/layout/LayoutSidebar/SidebarSection.tpl.js +22 -0
  193. package/layout/LayoutTree.js +373 -0
  194. package/layout/PanelMenu/PanelMenu.css.js +43 -0
  195. package/layout/PanelMenu/PanelMenu.js +95 -0
  196. package/layout/PanelMenu/PanelMenu.tpl.js +17 -0
  197. package/layout/ProjectTabs/ProjectTabs.css.js +188 -0
  198. package/layout/ProjectTabs/ProjectTabs.js +77 -0
  199. package/layout/ProjectTabs/ProjectTabs.tpl.js +15 -0
  200. package/layout/index.js +40 -0
  201. package/list/ListDetailShell/ListDetailShell.css.js +128 -0
  202. package/list/ListDetailShell/ListDetailShell.js +72 -0
  203. package/list/ListDetailShell/ListDetailShell.tpl.js +36 -0
  204. package/list/ListItem/ListItem.css.js +111 -0
  205. package/list/ListItem/ListItem.js +66 -0
  206. package/list/ListItem/ListItem.tpl.js +18 -0
  207. package/locale/index.js +503 -0
  208. package/manifest/component-registry.js +2446 -0
  209. package/manifest/graph-schema.js +285 -0
  210. package/manifest/index.js +6 -0
  211. package/manifest/project-schema-catalog.js +246 -0
  212. package/manifest/rule-catalog.js +201 -0
  213. package/manifest/theme-catalog.js +2149 -0
  214. package/manifest/ui-schema-catalog.js +334 -0
  215. package/menu/ContextMenu/ContextMenu.css.js +61 -0
  216. package/menu/ContextMenu/ContextMenu.js +82 -0
  217. package/menu/ContextMenu/ContextMenu.tpl.js +19 -0
  218. package/navigation/QuickOpen/QuickOpen.css.js +92 -0
  219. package/navigation/QuickOpen/QuickOpen.js +185 -0
  220. package/navigation/QuickOpen/QuickOpen.tpl.js +15 -0
  221. package/navigation/quick-open-utils.js +101 -0
  222. package/node/CtrlItem/CtrlItem.css.js +41 -0
  223. package/node/CtrlItem/CtrlItem.js +24 -0
  224. package/node/CtrlItem/CtrlItem.tpl.js +17 -0
  225. package/node/GraphFrame/GraphFrame.css.js +66 -0
  226. package/node/GraphFrame/GraphFrame.js +32 -0
  227. package/node/GraphFrame/GraphFrame.tpl.js +13 -0
  228. package/node/GraphNode/GraphNode.css.js +815 -0
  229. package/node/GraphNode/GraphNode.js +173 -0
  230. package/node/GraphNode/GraphNode.tpl.js +33 -0
  231. package/node/NodeCallout/NodeCallout.css.js +91 -0
  232. package/node/NodeCallout/NodeCallout.js +281 -0
  233. package/node/NodeCallout/NodeCallout.tpl.js +8 -0
  234. package/node/NodeSocket/NodeSocket.css.js +68 -0
  235. package/node/NodeSocket/NodeSocket.js +26 -0
  236. package/node/NodeSocket/NodeSocket.tpl.js +7 -0
  237. package/node/PortItem/PortItem.css.js +93 -0
  238. package/node/PortItem/PortItem.js +87 -0
  239. package/node/PortItem/PortItem.tpl.js +10 -0
  240. package/package.json +165 -0
  241. package/palette/PaletteBrowser/PaletteBrowser.css.js +143 -0
  242. package/palette/PaletteBrowser/PaletteBrowser.js +152 -0
  243. package/palette/PaletteBrowser/PaletteBrowser.tpl.js +23 -0
  244. package/plugins/History.js +408 -0
  245. package/plugins/Readonly.js +60 -0
  246. package/rules/symbiote-3x.json +170 -0
  247. package/schemas/component-descriptor-v1.json +91 -0
  248. package/schemas/component-descriptor-v2.json +145 -0
  249. package/schemas/graph-model-v1.json +179 -0
  250. package/schemas/graph-v1.json +91 -0
  251. package/schemas/project-package-v1.json +102 -0
  252. package/schemas/project-transaction-v1.json +114 -0
  253. package/schemas/runtime-ui-v1.json +80 -0
  254. package/schemas/theme-rule-block-v1.json +73 -0
  255. package/shapes/CircleShape.js +79 -0
  256. package/shapes/CommentShape.js +35 -0
  257. package/shapes/DiamondShape.js +130 -0
  258. package/shapes/NodeShape.js +79 -0
  259. package/shapes/PillShape.js +91 -0
  260. package/shapes/RectShape.js +84 -0
  261. package/shapes/SVGShape.js +525 -0
  262. package/shapes/index.js +63 -0
  263. package/surface/Card/Card.css.js +57 -0
  264. package/surface/Card/Card.js +17 -0
  265. package/surface/Card/Card.tpl.js +3 -0
  266. package/themes/Palette.js +30 -0
  267. package/themes/Skin.js +113 -0
  268. package/themes/Theme.js +82 -0
  269. package/themes/carbon.js +135 -0
  270. package/themes/dark.js +140 -0
  271. package/themes/default-dark.js +714 -0
  272. package/themes/default-provider.css +635 -0
  273. package/themes/default-provider.js +718 -0
  274. package/themes/ebook.js +136 -0
  275. package/themes/grey.js +137 -0
  276. package/themes/light.js +139 -0
  277. package/themes/neon.js +138 -0
  278. package/themes/pcb.js +273 -0
  279. package/themes/synthwave.js +138 -0
  280. package/tokens/base.json +29 -0
  281. package/tokens/themes/carbon.json +11 -0
  282. package/tokens/themes/dark.json +12 -0
  283. package/tokens/themes/default-dark.json +1543 -0
  284. package/tokens/themes/default-provider.json +1543 -0
  285. package/tokens/themes/ebook.json +11 -0
  286. package/tokens/themes/grey.json +11 -0
  287. package/tokens/themes/light.json +12 -0
  288. package/tokens/themes/neon.json +11 -0
  289. package/tokens/themes/pcb.json +11 -0
  290. package/tokens/themes/synthwave.json +11 -0
  291. package/toolbar/QuickToolbar/QuickToolbar.css.js +152 -0
  292. package/toolbar/QuickToolbar/QuickToolbar.js +529 -0
  293. package/toolbar/QuickToolbar/QuickToolbar.tpl.js +34 -0
  294. package/tree/TreePanel/TreePanel.css.js +112 -0
  295. package/tree/TreePanel/TreePanel.js +147 -0
  296. package/tree/TreePanel/TreePanel.tpl.js +18 -0
  297. package/tree/TreeView/TreeView.css.js +122 -0
  298. package/tree/TreeView/TreeView.js +365 -0
  299. package/tree/TreeView/TreeView.tpl.js +10 -0
  300. package/ui/dialogs.js +221 -0
  301. package/ui/host-adapters.js +114 -0
  302. package/ui/index.js +660 -0
  303. package/ui/locale.js +50 -0
  304. package/ui/overlay-stack.js +89 -0
  305. package/ui/shared-styles.js +26 -0
  306. package/webmcp.js +37 -0
  307. package/xr/deep-graph.js +646 -0
  308. package/xr/emulation.js +198 -0
  309. package/xr/gesture.js +228 -0
  310. package/xr/html-canvas-renderer.js +472 -0
  311. package/xr/index.js +15 -0
  312. package/xr/layout-projection.js +1046 -0
  313. package/xr/panel-frame.js +128 -0
  314. package/xr/panel-host.js +267 -0
  315. package/xr/pointer.js +258 -0
  316. package/xr/scene-controller.js +242 -0
  317. package/xr/spatial-scene.js +212 -0
  318. package/xr/theme-bridge.js +105 -0
  319. package/xr/three-webxr-adapter.js +3439 -0
  320. package/xr/webgl-layer-renderer.js +419 -0
  321. package/xr/webxr.js +679 -0
  322. package/xr/workbench.js +516 -0
@@ -0,0 +1,408 @@
1
+ /**
2
+ * History — undo/redo action stack for NodeEditor
3
+ *
4
+ * Records editor actions (addNode, removeNode, move, connect, disconnect)
5
+ * and provides undo()/redo() with Ctrl+Z / Ctrl+Shift+Z keyboard bindings.
6
+ * Plugin pattern: attach to editor via history.listen(editor).
7
+ *
8
+ * @module symbiote-node/plugins/History
9
+ */
10
+
11
+ /**
12
+ * @typedef {object} HistoryAction
13
+ * @property {'addNode'|'removeNode'|'moveNode'|'addConnection'|'removeConnection'|'addFrame'|'removeFrame'} type
14
+ * @property {object} data - Serialized action data for undo/redo
15
+ */
16
+
17
+ const MAX_STACK = 200;
18
+
19
+ export class History {
20
+ /** @type {HistoryAction[]} */
21
+ #undoStack = [];
22
+
23
+ /** @type {HistoryAction[]} */
24
+ #redoStack = [];
25
+
26
+ /** @type {import('../core/Editor.js').NodeEditor|null} */
27
+ #editor = null;
28
+
29
+ /** @type {function|null} */
30
+ #getCanvas = null;
31
+
32
+ /** @type {boolean} - prevent recording actions triggered by undo/redo itself */
33
+ #isApplying = false;
34
+
35
+ /** @type {function[]} - unsubscribe functions */
36
+ #unsubs = [];
37
+
38
+ /** @type {object} - injected class constructors */
39
+ #classes = {};
40
+
41
+ /**
42
+ * Attach history tracking to an editor
43
+ * @param {import('../core/Editor.js').NodeEditor} editor
44
+ * @param {object} [options]
45
+ * @param {function} [options.getCanvas] - returns canvas element for position tracking
46
+ * @param {object} [options.classes] - class constructors: { Node, Connection, Frame, Socket, Input, Output, InputControl }
47
+ */
48
+ listen(editor, options = {}) {
49
+ this.#editor = editor;
50
+ this.#getCanvas = options.getCanvas || null;
51
+ this.#classes = options.classes || {};
52
+
53
+
54
+ this.#unsubs.push(
55
+ editor.on('nodecreated', (node) => {
56
+ if (this.#isApplying) return;
57
+ let canvas = this.#getCanvas?.();
58
+ let pos = canvas ? this.#getNodePosition(canvas, node.id) : [0, 0];
59
+ this.#push({
60
+ type: 'addNode',
61
+ data: { node: this.#serializeNode(node), position: pos },
62
+ });
63
+ })
64
+ );
65
+
66
+
67
+ this.#unsubs.push(
68
+ editor.on('noderemove', (node) => {
69
+ if (this.#isApplying) return;
70
+ let canvas = this.#getCanvas?.();
71
+ let pos = canvas ? this.#getNodePosition(canvas, node.id) : [0, 0];
72
+
73
+ let conns = editor.getNodeConnections(node.id).map((c) => this.#serializeConnection(c));
74
+ this.#push({
75
+ type: 'removeNode',
76
+ data: { node: this.#serializeNode(node), position: pos, connections: conns },
77
+ });
78
+ })
79
+ );
80
+
81
+
82
+ this.#unsubs.push(
83
+ editor.on('nodepicked', (node) => {
84
+ if (this.#isApplying) return;
85
+ let canvas = this.#getCanvas?.();
86
+ if (!canvas) return;
87
+ let pos = this.#getNodePosition(canvas, node.id);
88
+ node._historyStartPos = pos;
89
+ })
90
+ );
91
+
92
+ this.#unsubs.push(
93
+ editor.on('nodedragged', ({ id }) => {
94
+ if (this.#isApplying) return;
95
+ let node = editor.getNode(id);
96
+ if (!node?._historyStartPos) return;
97
+ let canvas = this.#getCanvas?.();
98
+ let endPos = canvas ? this.#getNodePosition(canvas, id) : [0, 0];
99
+ let startPos = node._historyStartPos;
100
+
101
+ if (startPos[0] !== endPos[0] || startPos[1] !== endPos[1]) {
102
+ this.#push({
103
+ type: 'moveNode',
104
+ data: { nodeId: id, from: startPos, to: endPos },
105
+ });
106
+ }
107
+ delete node._historyStartPos;
108
+ })
109
+ );
110
+
111
+
112
+ this.#unsubs.push(
113
+ editor.on('connectioncreated', (conn) => {
114
+ if (this.#isApplying) return;
115
+ this.#push({
116
+ type: 'addConnection',
117
+ data: { connection: this.#serializeConnection(conn) },
118
+ });
119
+ })
120
+ );
121
+
122
+ this.#unsubs.push(
123
+ editor.on('connectionremove', (conn) => {
124
+ if (this.#isApplying) return;
125
+ this.#push({
126
+ type: 'removeConnection',
127
+ data: { connection: this.#serializeConnection(conn) },
128
+ });
129
+ })
130
+ );
131
+
132
+
133
+ this.#unsubs.push(
134
+ editor.on('framecreated', (frame) => {
135
+ if (this.#isApplying) return;
136
+ this.#push({
137
+ type: 'addFrame',
138
+ data: { frame: { ...frame } },
139
+ });
140
+ })
141
+ );
142
+
143
+ this.#unsubs.push(
144
+ editor.on('frameremove', (frame) => {
145
+ if (this.#isApplying) return;
146
+ this.#push({
147
+ type: 'removeFrame',
148
+ data: { frame: { ...frame } },
149
+ });
150
+ })
151
+ );
152
+ }
153
+
154
+ bindKeyboard(target) {
155
+ let handler = (e) => {
156
+ if ((e.ctrlKey || e.metaKey) && e.key === 'z') {
157
+ e.preventDefault();
158
+ if (e.shiftKey) this.redo();
159
+ else this.undo();
160
+ }
161
+ if ((e.ctrlKey || e.metaKey) && e.key === 'y') {
162
+ e.preventDefault();
163
+ this.redo();
164
+ }
165
+ };
166
+ target.addEventListener('keydown', handler);
167
+ this.#unsubs.push(() => target.removeEventListener('keydown', handler));
168
+ }
169
+
170
+ /** Undo last action */
171
+ undo() {
172
+ let action = this.#undoStack.pop();
173
+ if (!action) return;
174
+ this.#isApplying = true;
175
+ try {
176
+ this.#applyReverse(action);
177
+ this.#redoStack.push(action);
178
+ } finally {
179
+ this.#isApplying = false;
180
+ }
181
+ }
182
+
183
+ /** Redo last undone action */
184
+ redo() {
185
+ let action = this.#redoStack.pop();
186
+ if (!action) return;
187
+ this.#isApplying = true;
188
+ try {
189
+ this.#applyForward(action);
190
+ this.#undoStack.push(action);
191
+ } finally {
192
+ this.#isApplying = false;
193
+ }
194
+ }
195
+
196
+ /** @returns {number} */
197
+ get undoCount() {
198
+ return this.#undoStack.length;
199
+ }
200
+
201
+ /** @returns {number} */
202
+ get redoCount() {
203
+ return this.#redoStack.length;
204
+ }
205
+
206
+ /** Clear all history */
207
+ clear() {
208
+ this.#undoStack = [];
209
+ this.#redoStack = [];
210
+ }
211
+
212
+ /** Destroy and unsubscribe */
213
+ destroy() {
214
+ for (const unsub of this.#unsubs) {
215
+ if (typeof unsub === 'function') unsub();
216
+ }
217
+ this.#unsubs = [];
218
+ this.clear();
219
+ this.#editor = null;
220
+ }
221
+
222
+
223
+ #push(action) {
224
+ this.#undoStack.push(action);
225
+ if (this.#undoStack.length > MAX_STACK) this.#undoStack.shift();
226
+ this.#redoStack = [];
227
+ }
228
+
229
+ #applyReverse(action) {
230
+ let editor = this.#editor;
231
+ let canvas = this.#getCanvas?.();
232
+ if (!editor) return;
233
+
234
+ let reverseMap = {
235
+ addNode: () => editor.removeNode(action.data.node.id),
236
+ removeNode: () => {
237
+ let { node: nodeData, position, connections } = action.data;
238
+ let restoredNode = this.#deserializeNode(nodeData);
239
+ editor.addNode(restoredNode);
240
+ if (canvas && position) canvas.setNodePosition(restoredNode.id, position[0], position[1]);
241
+ for (let connData of connections) {
242
+ let conn = new this.#classes.Connection(
243
+ connData.from,
244
+ connData.out,
245
+ connData.to,
246
+ connData.in
247
+ );
248
+ conn.id = connData.id;
249
+ try {
250
+ editor.addConnection(conn);
251
+ } catch {
252
+
253
+ }
254
+ }
255
+ },
256
+ moveNode: () =>
257
+ canvas?.setNodePosition(action.data.nodeId, action.data.from[0], action.data.from[1]),
258
+ addConnection: () => editor.removeConnection(action.data.connection.id),
259
+ removeConnection: () => {
260
+ let connData = action.data.connection;
261
+ let conn = new this.#classes.Connection(
262
+ connData.from,
263
+ connData.out,
264
+ connData.to,
265
+ connData.in
266
+ );
267
+ conn.id = connData.id;
268
+ try {
269
+ editor.addConnection(conn);
270
+ } catch {
271
+
272
+ }
273
+ },
274
+ addFrame: () => editor.removeFrame(action.data.frame.id),
275
+ removeFrame: () => {
276
+ let frame = new this.#classes.Frame(action.data.frame.label, action.data.frame);
277
+ frame.id = action.data.frame.id;
278
+ editor.addFrame(frame);
279
+ },
280
+ };
281
+
282
+ reverseMap[action.type]?.();
283
+ }
284
+
285
+ #applyForward(action) {
286
+ let editor = this.#editor;
287
+ let canvas = this.#getCanvas?.();
288
+ if (!editor) return;
289
+
290
+ let forwardMap = {
291
+ addNode: () => {
292
+ let restoredNode = this.#deserializeNode(action.data.node);
293
+ editor.addNode(restoredNode);
294
+ if (canvas && action.data.position)
295
+ canvas.setNodePosition(restoredNode.id, action.data.position[0], action.data.position[1]);
296
+ },
297
+ removeNode: () => editor.removeNode(action.data.node.id),
298
+ moveNode: () =>
299
+ canvas?.setNodePosition(action.data.nodeId, action.data.to[0], action.data.to[1]),
300
+ addConnection: () => {
301
+ let connData = action.data.connection;
302
+ let conn = new this.#classes.Connection(
303
+ connData.from,
304
+ connData.out,
305
+ connData.to,
306
+ connData.in
307
+ );
308
+ conn.id = connData.id;
309
+ try {
310
+ editor.addConnection(conn);
311
+ } catch {
312
+
313
+ }
314
+ },
315
+ removeConnection: () => editor.removeConnection(action.data.connection.id),
316
+ addFrame: () => {
317
+ let frame = new this.#classes.Frame(action.data.frame.label, action.data.frame);
318
+ frame.id = action.data.frame.id;
319
+ editor.addFrame(frame);
320
+ },
321
+ removeFrame: () => editor.removeFrame(action.data.frame.id),
322
+ };
323
+
324
+ forwardMap[action.type]?.();
325
+ }
326
+
327
+ #serializeNode(node) {
328
+ return {
329
+ id: node.id,
330
+ label: node.label,
331
+ type: node.type,
332
+ category: node.category,
333
+ shape: node.shape,
334
+ params: { ...node.params },
335
+ inputs: Object.fromEntries(
336
+ Object.entries(node.inputs).map(([k, v]) => [
337
+ k,
338
+ {
339
+ socket: v.socket ? { type: v.socket.type, color: v.socket.color } : null,
340
+ label: v.label,
341
+ },
342
+ ])
343
+ ),
344
+ outputs: Object.fromEntries(
345
+ Object.entries(node.outputs).map(([k, v]) => [
346
+ k,
347
+ {
348
+ socket: v.socket ? { type: v.socket.type, color: v.socket.color } : null,
349
+ label: v.label,
350
+ },
351
+ ])
352
+ ),
353
+ controls: Object.fromEntries(
354
+ Object.entries(node.controls).map(([k, v]) => [
355
+ k,
356
+ {
357
+ label: v.label,
358
+ value: v.value,
359
+ type: v.type,
360
+ },
361
+ ])
362
+ ),
363
+ };
364
+ }
365
+
366
+ #serializeConnection(conn) {
367
+ return { id: conn.id, from: conn.from, out: conn.out, to: conn.to, in: conn.in };
368
+ }
369
+
370
+ #deserializeNode(data) {
371
+ let { Node, Socket, Input, Output, InputControl } = this.#classes;
372
+ let node = new Node(data.label, {
373
+ type: data.type,
374
+ category: data.category,
375
+ shape: data.shape,
376
+ });
377
+ node.id = data.id;
378
+ node.params = { ...data.params };
379
+
380
+ for (const [key, inp] of Object.entries(data.inputs)) {
381
+ let socket = inp.socket
382
+ ? new Socket(inp.socket.type, { color: inp.socket.color })
383
+ : new Socket('any');
384
+ node.addInput(key, new Input(socket, inp.label));
385
+ }
386
+ for (const [key, out] of Object.entries(data.outputs)) {
387
+ let socket = out.socket
388
+ ? new Socket(out.socket.type, { color: out.socket.color })
389
+ : new Socket('any');
390
+ node.addOutput(key, new Output(socket, out.label));
391
+ }
392
+ for (const [key, ctrl] of Object.entries(data.controls)) {
393
+ node.addControl(
394
+ key,
395
+ new InputControl(ctrl.type || 'text', { label: ctrl.label, initial: ctrl.value })
396
+ );
397
+ }
398
+
399
+ return node;
400
+ }
401
+
402
+ #getNodePosition(canvas, nodeId) {
403
+ let positions = canvas.getPositions();
404
+ return positions[nodeId] || [0, 0];
405
+ }
406
+ }
407
+
408
+ export { History as default };
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Readonly — toggle readonly mode for node editor
3
+ *
4
+ * When enabled, blocks node creation, deletion, connection
5
+ * creation/removal, and node dragging.
6
+ *
7
+ * Adapted from Rete.js readonly plugin (63 LOC).
8
+ * @module symbiote-node/plugins/Readonly
9
+ */
10
+
11
+ export class Readonly {
12
+ /** @type {boolean} */
13
+ #enabled = false;
14
+
15
+ /** @type {import('../core/Editor.js').NodeEditor|null} */
16
+ #editor = null;
17
+
18
+ /**
19
+ * @param {import('../core/Editor.js').NodeEditor} editor
20
+ */
21
+ constructor(editor) {
22
+ this.#editor = editor;
23
+ }
24
+
25
+ /** Enable readonly mode */
26
+ enable() {
27
+ this.#enabled = true;
28
+ this.#editor.emit('readonlychanged', true);
29
+ }
30
+
31
+ /** Disable readonly mode */
32
+ disable() {
33
+ this.#enabled = false;
34
+ this.#editor.emit('readonlychanged', false);
35
+ }
36
+
37
+ /** Toggle readonly mode */
38
+ toggle() {
39
+ this.#enabled ? this.disable() : this.enable();
40
+ }
41
+
42
+ /**
43
+ * Whether readonly is currently enabled
44
+ * @returns {boolean}
45
+ */
46
+ get isEnabled() {
47
+ return this.#enabled;
48
+ }
49
+
50
+ /**
51
+ * Guard check — throws if readonly
52
+ * Use before mutation operations
53
+ * @returns {boolean} true if operation should be blocked
54
+ */
55
+ shouldBlock() {
56
+ return this.#enabled;
57
+ }
58
+ }
59
+
60
+ export { Readonly as default };
@@ -0,0 +1,170 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "name": "symbiote-3x",
4
+ "version": "1.0.0",
5
+ "description": "Machine-readable Symbiote.js 3.x rules for reusable package, UI, and provider contracts.",
6
+ "rules": [
7
+ {
8
+ "id": "SYM-001",
9
+ "name": "triple-file-standard",
10
+ "description": "Production components are split into .js, .tpl.js, and .css.js files.",
11
+ "severity": "error",
12
+ "filePattern": "**/*.js",
13
+ "check": "component_has_triple_files",
14
+ "tags": ["symbiote", "components", "architecture"]
15
+ },
16
+ {
17
+ "id": "SYM-002",
18
+ "name": "no-inline-templates",
19
+ "description": "Templates are defined in .tpl.js files instead of inline component bodies.",
20
+ "severity": "error",
21
+ "filePattern": "**/*.js",
22
+ "check": "no_inline_html_template",
23
+ "tags": ["symbiote", "templates"]
24
+ },
25
+ {
26
+ "id": "SYM-003",
27
+ "name": "reg-call-required",
28
+ "description": "Every custom element component calls .reg('kebab-case-tag').",
29
+ "severity": "error",
30
+ "filePattern": "**/*.js",
31
+ "check": "has_reg_call",
32
+ "tags": ["symbiote", "web-components"]
33
+ },
34
+ {
35
+ "id": "SYM-004",
36
+ "name": "css-token-usage",
37
+ "description": "Component CSS uses --sn-* custom properties for themeable values.",
38
+ "severity": "warning",
39
+ "filePattern": "**/*.css.js",
40
+ "check": "uses_sn_css_tokens",
41
+ "tags": ["symbiote", "theming", "tokens"]
42
+ },
43
+ {
44
+ "id": "SYM-005",
45
+ "name": "node-safe-core",
46
+ "description": "Root, core, manifest, rules, schemas, and tokens remain importable without DOM globals.",
47
+ "severity": "error",
48
+ "filePattern": ["index.js", "core/**/*.js", "manifest/**/*.js"],
49
+ "check": "no_dom_side_effects",
50
+ "tags": ["symbiote", "node-safe", "library"]
51
+ },
52
+ {
53
+ "id": "SYM-006",
54
+ "name": "single-binding-block",
55
+ "description": "Each template element has at most one Symbiote binding block.",
56
+ "severity": "error",
57
+ "filePattern": "**/*.tpl.js",
58
+ "check": "no_multiple_binding_blocks",
59
+ "tags": ["symbiote", "templates", "bindings"]
60
+ },
61
+ {
62
+ "id": "SYM-007",
63
+ "name": "published-library-contract",
64
+ "description": "Public metadata, rules, schemas, and token files are reachable from package exports and files[].",
65
+ "severity": "error",
66
+ "filePattern": "package.json",
67
+ "check": "published_contract_in_exports_and_files",
68
+ "tags": ["package", "exports", "library"]
69
+ },
70
+ {
71
+ "id": "SYM-008",
72
+ "name": "agent-boundary",
73
+ "description": "Agent-facing manifests describe modules and rules without importing browser components.",
74
+ "severity": "error",
75
+ "filePattern": "manifest/**/*.js",
76
+ "check": "manifest_is_side_effect_free",
77
+ "tags": ["agents", "mcp", "architecture"]
78
+ },
79
+ {
80
+ "id": "SYM-009",
81
+ "name": "public-subpath-imports",
82
+ "description": "Consumers import symbiote-node through package exports instead of relative package internals or deep implementation paths.",
83
+ "severity": "error",
84
+ "filePattern": "**/*.js",
85
+ "check": "uses_public_package_exports",
86
+ "tags": ["package", "exports", "library", "agents"]
87
+ },
88
+ {
89
+ "id": "SYM-010",
90
+ "name": "light-dom-host-selector",
91
+ "description": "Light DOM rootStyles use the registered custom element selector as the host selector; Shadow DOM styles use :host. Shared CSS that must run in both modes includes both selectors.",
92
+ "severity": "error",
93
+ "filePattern": "**/*.css.js",
94
+ "check": "root_styles_use_registered_host_selector",
95
+ "tags": ["symbiote", "styles", "light-dom", "theming"]
96
+ },
97
+ {
98
+ "id": "SYM-011",
99
+ "name": "theme-cascade-contract",
100
+ "description": "Themeable components consume inherited CSS custom properties and do not require per-component applyTheme calls or local token resets.",
101
+ "severity": "error",
102
+ "filePattern": ["**/*.css.js", "themes/**/*.js", "tokens/**/*.json"],
103
+ "check": "theme_values_cascade_from_stable_root",
104
+ "tags": ["symbiote", "theming", "tokens", "library"]
105
+ },
106
+ {
107
+ "id": "SYM-012",
108
+ "name": "css-data-for-visual-tokens",
109
+ "description": "Static visual tokens such as colors, spacing, radius, typography, and icon fonts are carried by CSS custom properties; PubSub and JS setters are reserved for application state or explicit dynamic measurements.",
110
+ "severity": "warning",
111
+ "filePattern": "**/*.js",
112
+ "check": "visual_tokens_use_css_custom_properties",
113
+ "tags": ["symbiote", "css-data", "theming", "state"]
114
+ },
115
+ {
116
+ "id": "SYM-013",
117
+ "name": "data-driven-runtime-ui",
118
+ "description": "Reusable UI primitives expose clean data, props, events, and manifest metadata so agents can assemble layouts and functionality at runtime without product-specific coupling.",
119
+ "severity": "error",
120
+ "filePattern": ["**/*.js", "manifest/**/*.js", "custom-elements.json"],
121
+ "check": "component_contract_is_data_driven",
122
+ "tags": ["symbiote", "agents", "runtime-ui", "library"]
123
+ },
124
+ {
125
+ "id": "SYM-014",
126
+ "name": "theme-rule-block-composition",
127
+ "description": "Themes are composed from named rule blocks: source accents, derived color formulas, geometry formulas, typography formulas, motion/effect formulas, semantic aliases, and component aliases.",
128
+ "severity": "error",
129
+ "filePattern": ["themes/**/*.js", "tokens/**/*.json", "manifest/**/*.js"],
130
+ "check": "theme_exposes_composable_rule_blocks",
131
+ "tags": ["symbiote", "themes", "agents", "design-rules"]
132
+ },
133
+ {
134
+ "id": "SYM-015",
135
+ "name": "geometry-cascade-formulas",
136
+ "description": "Spacing, radius, stroke, density, panel gaps, and component sizing derive from root geometry tokens through documented formulas instead of unrelated fixed values.",
137
+ "severity": "error",
138
+ "filePattern": ["themes/**/*.js", "tokens/**/*.json", "**/*.css.js"],
139
+ "check": "geometry_tokens_derive_from_root_scale",
140
+ "tags": ["symbiote", "themes", "geometry", "tokens"]
141
+ },
142
+ {
143
+ "id": "SYM-016",
144
+ "name": "color-cascade-formulas",
145
+ "description": "Surface, text, border, accent, state, and overlay tokens derive from source palettes or accent tokens through documented formulas with contrast-aware roles.",
146
+ "severity": "error",
147
+ "filePattern": ["themes/**/*.js", "tokens/**/*.json", "**/*.css.js"],
148
+ "check": "color_tokens_derive_from_source_palette",
149
+ "tags": ["symbiote", "themes", "color", "tokens", "accessibility"]
150
+ },
151
+ {
152
+ "id": "SYM-017",
153
+ "name": "interaction-effect-formulas",
154
+ "description": "Hover, focus, active, selected, loading, error, and transition effects derive from semantic state tokens and motion formulas, not per-component bespoke effects.",
155
+ "severity": "warning",
156
+ "filePattern": ["themes/**/*.js", "tokens/**/*.json", "**/*.css.js"],
157
+ "check": "interaction_effects_derive_from_state_tokens",
158
+ "tags": ["symbiote", "themes", "motion", "effects", "interaction"]
159
+ },
160
+ {
161
+ "id": "SYM-018",
162
+ "name": "no-legacy-ui-shell-classes",
163
+ "description": "Reusable and provider-owned UI must not introduce legacy .ui-* shared shell classes; use provider custom elements, host selectors, or component-namespaced classes.",
164
+ "severity": "error",
165
+ "filePattern": ["**/*.tpl.js", "**/*.css.js", "**/*.js"],
166
+ "check": "no_legacy_ui_shell_classes",
167
+ "tags": ["symbiote", "styles", "theming", "components"]
168
+ }
169
+ ]
170
+ }