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,261 @@
1
+ const DEFAULT_LIST_LIMIT = 50;
2
+ const DEFAULT_GRAPH_NODE_LIMIT = 80;
3
+ const DEFAULT_GRAPH_EDGE_LIMIT = 160;
4
+
5
+ function isRecord(value) {
6
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
7
+ }
8
+
9
+ function stringifyPrimitive(value) {
10
+ if (value === null) return 'null';
11
+ if (value === undefined) return 'undefined';
12
+ if (typeof value === 'string') return value;
13
+ if (typeof value === 'number' || typeof value === 'bigint' || typeof value === 'boolean') {
14
+ return String(value);
15
+ }
16
+ return '';
17
+ }
18
+
19
+ function valueKind(value) {
20
+ if (Array.isArray(value)) return 'array';
21
+ if (value === null) return 'null';
22
+ return typeof value;
23
+ }
24
+
25
+ function stableId(prefix, index) {
26
+ return `${prefix}-${index + 1}`;
27
+ }
28
+
29
+ function truncateText(value, maxLength) {
30
+ let text = String(value || '');
31
+ if (!Number.isFinite(maxLength) || maxLength <= 0 || text.length <= maxLength) return text;
32
+ return `${text.slice(0, Math.max(0, maxLength - 1))}...`;
33
+ }
34
+
35
+ function readFirst(record, keys) {
36
+ for (let key of keys) {
37
+ let value = record[key];
38
+ if (value !== undefined && value !== null && value !== '') return value;
39
+ }
40
+ return undefined;
41
+ }
42
+
43
+ function describeValue(value, maxLength) {
44
+ if (!isRecord(value) && !Array.isArray(value)) {
45
+ return truncateText(stringifyPrimitive(value), maxLength);
46
+ }
47
+ try {
48
+ return truncateText(JSON.stringify(value), maxLength);
49
+ } catch {
50
+ return Object.prototype.toString.call(value);
51
+ }
52
+ }
53
+
54
+ function coerceArray(value) {
55
+ if (Array.isArray(value)) return value;
56
+ if (value === undefined || value === null) return [];
57
+ if (isRecord(value)) {
58
+ if (Array.isArray(value.items)) return value.items;
59
+ if (Array.isArray(value.results)) return value.results;
60
+ if (Array.isArray(value.entries)) return value.entries;
61
+ return Object.entries(value).map(([key, itemValue]) => ({ key, value: itemValue }));
62
+ }
63
+ return [value];
64
+ }
65
+
66
+ function normalizeListItem(item, index, options) {
67
+ let maxTextLength = options.maxTextLength ?? 160;
68
+ if (!isRecord(item)) {
69
+ return {
70
+ id: stableId('item', index),
71
+ label: describeValue(item, maxTextLength) || `Item ${index + 1}`,
72
+ description: '',
73
+ meta: valueKind(item),
74
+ kind: valueKind(item),
75
+ status: '',
76
+ value: item,
77
+ };
78
+ }
79
+
80
+ let id = readFirst(item, ['id', 'key', 'name', 'label']);
81
+ let label = readFirst(item, ['label', 'title', 'name', 'key', 'id']);
82
+ let description = readFirst(item, ['description', 'summary', 'text', 'message', 'value']);
83
+ let meta = readFirst(item, ['meta', 'detail', 'type', 'kind']);
84
+ let kind = readFirst(item, ['kind', 'type']);
85
+ let status = readFirst(item, ['status', 'state']);
86
+
87
+ return {
88
+ id: id === undefined ? stableId('item', index) : String(id),
89
+ label: truncateText(label === undefined ? `Item ${index + 1}` : label, maxTextLength),
90
+ description: truncateText(description === undefined ? '' : description, maxTextLength),
91
+ meta: truncateText(meta === undefined ? '' : meta, maxTextLength),
92
+ kind: kind === undefined ? 'item' : String(kind),
93
+ status: status === undefined ? '' : String(status),
94
+ value: item.value === undefined ? item : item.value,
95
+ };
96
+ }
97
+
98
+ /**
99
+ * @param {*} value
100
+ * @param {Object} [options]
101
+ * @param {number} [options.limit]
102
+ * @param {number} [options.maxTextLength]
103
+ * @returns {{ items: Array<object>, total: number, visible: number, truncated: boolean, empty: boolean }}
104
+ */
105
+ export function normalizeOutputList(value, options = {}) {
106
+ let sourceItems = coerceArray(value);
107
+ let limit = Number.isFinite(options.limit) ? Math.max(0, options.limit) : DEFAULT_LIST_LIMIT;
108
+ let items = sourceItems
109
+ .slice(0, limit)
110
+ .map((item, index) => normalizeListItem(item, index, options));
111
+
112
+ return {
113
+ items,
114
+ total: sourceItems.length,
115
+ visible: items.length,
116
+ truncated: sourceItems.length > items.length,
117
+ empty: items.length === 0,
118
+ };
119
+ }
120
+
121
+ function readNodeId(node, index) {
122
+ if (!isRecord(node)) return stableId('node', index);
123
+ let id = readFirst(node, ['id', 'key', 'name']);
124
+ return id === undefined ? stableId('node', index) : String(id);
125
+ }
126
+
127
+ function normalizeGraphNode(node, index, options) {
128
+ let maxTextLength = options.maxTextLength ?? 120;
129
+ if (!isRecord(node)) {
130
+ return {
131
+ id: stableId('node', index),
132
+ label: describeValue(node, maxTextLength) || `Node ${index + 1}`,
133
+ kind: valueKind(node),
134
+ description: '',
135
+ status: '',
136
+ meta: '',
137
+ x: null,
138
+ y: null,
139
+ value: node,
140
+ };
141
+ }
142
+
143
+ let position = isRecord(node.position) ? node.position : {};
144
+ let id = readNodeId(node, index);
145
+ let label = readFirst(node, ['label', 'title', 'name', 'id']);
146
+ let kind = readFirst(node, ['kind', 'type']);
147
+ let description = readFirst(node, ['description', 'summary', 'text']);
148
+ let status = readFirst(node, ['status', 'state']);
149
+ let meta = readFirst(node, ['meta', 'detail']);
150
+ let x = Number.isFinite(node.x) ? node.x : Number.isFinite(position.x) ? position.x : null;
151
+ let y = Number.isFinite(node.y) ? node.y : Number.isFinite(position.y) ? position.y : null;
152
+
153
+ return {
154
+ id,
155
+ label: truncateText(label === undefined ? id : label, maxTextLength),
156
+ kind: kind === undefined ? 'node' : String(kind),
157
+ description: truncateText(description === undefined ? '' : description, maxTextLength),
158
+ status: status === undefined ? '' : String(status),
159
+ meta: truncateText(meta === undefined ? '' : meta, maxTextLength),
160
+ x,
161
+ y,
162
+ value: node.value === undefined ? node : node.value,
163
+ };
164
+ }
165
+
166
+ function readEndpoint(endpoint, fallbackKeys) {
167
+ if (typeof endpoint === 'string' || typeof endpoint === 'number') return String(endpoint);
168
+ if (isRecord(endpoint)) {
169
+ let id = readFirst(endpoint, ['id', 'nodeId', 'node', ...fallbackKeys]);
170
+ if (id !== undefined) return String(id);
171
+ }
172
+ return '';
173
+ }
174
+
175
+ function normalizeGraphEdge(edge, index, options) {
176
+ let maxTextLength = options.maxTextLength ?? 120;
177
+ if (!isRecord(edge)) {
178
+ return {
179
+ id: stableId('edge', index),
180
+ source: '',
181
+ target: '',
182
+ label: describeValue(edge, maxTextLength),
183
+ kind: valueKind(edge),
184
+ value: edge,
185
+ };
186
+ }
187
+
188
+ let source = readEndpoint(edge.source, ['from']);
189
+ let target = readEndpoint(edge.target, ['to']);
190
+ if (!source) source = readEndpoint(edge.from, ['source']);
191
+ if (!target) target = readEndpoint(edge.to, ['target']);
192
+
193
+ let id = readFirst(edge, ['id', 'key']);
194
+ let label = readFirst(edge, ['label', 'title', 'name']);
195
+ let kind = readFirst(edge, ['kind', 'type']);
196
+
197
+ return {
198
+ id: id === undefined ? stableId('edge', index) : String(id),
199
+ source,
200
+ target,
201
+ label: truncateText(label === undefined ? '' : label, maxTextLength),
202
+ kind: kind === undefined ? 'edge' : String(kind),
203
+ value: edge.value === undefined ? edge : edge.value,
204
+ };
205
+ }
206
+
207
+ function readGraphArrays(value) {
208
+ if (!isRecord(value)) {
209
+ return {
210
+ nodes: coerceArray(value),
211
+ edges: [],
212
+ };
213
+ }
214
+
215
+ return {
216
+ nodes: Array.isArray(value.nodes) ? value.nodes : coerceArray(value.items || []),
217
+ edges: Array.isArray(value.edges)
218
+ ? value.edges
219
+ : Array.isArray(value.connections)
220
+ ? value.connections
221
+ : Array.isArray(value.links)
222
+ ? value.links
223
+ : [],
224
+ };
225
+ }
226
+
227
+ /**
228
+ * @param {*} value
229
+ * @param {Object} [options]
230
+ * @param {number} [options.nodeLimit]
231
+ * @param {number} [options.edgeLimit]
232
+ * @param {number} [options.maxTextLength]
233
+ * @returns {{ nodes: Array<object>, edges: Array<object>, totalNodes: number, totalEdges: number, truncated: boolean, empty: boolean }}
234
+ */
235
+ export function normalizePreviewGraph(value, options = {}) {
236
+ let graph = readGraphArrays(value);
237
+ let nodeLimit = Number.isFinite(options.nodeLimit)
238
+ ? Math.max(0, options.nodeLimit)
239
+ : DEFAULT_GRAPH_NODE_LIMIT;
240
+ let edgeLimit = Number.isFinite(options.edgeLimit)
241
+ ? Math.max(0, options.edgeLimit)
242
+ : DEFAULT_GRAPH_EDGE_LIMIT;
243
+
244
+ let nodes = graph.nodes
245
+ .slice(0, nodeLimit)
246
+ .map((node, index) => normalizeGraphNode(node, index, options));
247
+ let nodeIds = new Set(nodes.map((node) => node.id));
248
+ let edges = graph.edges
249
+ .slice(0, edgeLimit)
250
+ .map((edge, index) => normalizeGraphEdge(edge, index, options))
251
+ .filter((edge) => edge.source && edge.target && nodeIds.has(edge.source) && nodeIds.has(edge.target));
252
+
253
+ return {
254
+ nodes,
255
+ edges,
256
+ totalNodes: graph.nodes.length,
257
+ totalEdges: graph.edges.length,
258
+ truncated: graph.nodes.length > nodes.length || graph.edges.length > edges.length,
259
+ empty: nodes.length === 0,
260
+ };
261
+ }
@@ -0,0 +1,33 @@
1
+ export default `
2
+ :host {
3
+ display: block;
4
+ }
5
+
6
+ cell-bg {
7
+ display: block;
8
+ position: absolute;
9
+ top: 0;
10
+ left: 0;
11
+ width: 100%;
12
+ height: 100%;
13
+ z-index: 0;
14
+ pointer-events: none;
15
+ overflow: hidden;
16
+ }
17
+ cell-bg canvas {
18
+ width: 100%;
19
+ height: 100%;
20
+ display: block;
21
+ background: var(--sn-cell-bg);
22
+ }
23
+ cell-bg::after {
24
+ content: '';
25
+ position: absolute;
26
+ inset: 0;
27
+ pointer-events: none;
28
+ background:
29
+ radial-gradient(ellipse 80% 50% at 50% -10%, var(--sn-cell-glare) 0%, transparent 100%),
30
+ radial-gradient(ellipse at 50% 50%, transparent 20%, var(--sn-cell-vignette-mid) 70%, var(--sn-cell-vignette-edge) 100%),
31
+ var(--sn-cell-noise);
32
+ }
33
+ `;
@@ -0,0 +1,410 @@
1
+ import Symbiote from '@symbiotejs/symbiote';
2
+ import template from './CellBg.tpl.js';
3
+ import css from './CellBg.css.js';
4
+
5
+ /**
6
+ * Cellular Automaton Background Component
7
+ * Parameters based on user config:
8
+ * Rule: Conway B3/S23
9
+ * Cell Size: 14px
10
+ * Speed: 75ms
11
+ * Min Radius: 2px
12
+ * Max Radius: 4px
13
+ * Fade Rate: 4%
14
+ */
15
+
16
+ const RULE_B = [3];
17
+ const RULE_S = [2, 3];
18
+ const CELL_SIZE = 14;
19
+ const STEP_MS = 75;
20
+ const MIN_RADIUS = 2;
21
+ const MAX_RADIUS = 5;
22
+ const FADE_RATE = 0.04;
23
+
24
+ const PALETTE_SIZE = 32;
25
+ const now = () => globalThis.performance?.now?.() ?? Date.now();
26
+ const requestFrame = (callback) => {
27
+ if (typeof globalThis.requestAnimationFrame === 'function') {
28
+ return globalThis.requestAnimationFrame(callback);
29
+ }
30
+ return setTimeout(() => callback(now()), 16);
31
+ };
32
+
33
+ function normalizeCssColor(source, value) {
34
+ let doc = source?.ownerDocument || globalThis.document;
35
+ if (!doc || !value) return null;
36
+ let probe = doc.createElement('span');
37
+ probe.style.color = value;
38
+ let target = typeof source?.append === 'function' ? source : doc.documentElement;
39
+ target.append(probe);
40
+ let normalized = globalThis.getComputedStyle(probe).color.trim();
41
+ probe.remove();
42
+ return normalized || null;
43
+ }
44
+
45
+ function parseCssRgb(source, value) {
46
+ let normalized = normalizeCssColor(source, value);
47
+ if (!normalized) return null;
48
+
49
+ let rgbMatch = normalized.match(/rgba?\(([^)]+)\)/);
50
+ if (rgbMatch) {
51
+ let channels = rgbMatch[1]
52
+ .replaceAll(',', ' ')
53
+ .split(/[ /\t]+/)
54
+ .filter(Boolean)
55
+ .slice(0, 3)
56
+ .map((part) => (part.endsWith('%') ? Number.parseFloat(part) * 2.55 : Number.parseFloat(part)));
57
+ return channels.every(Number.isFinite) ? channels : null;
58
+ }
59
+
60
+ let srgbMatch = normalized.match(/color\(\s*srgb\s+([^)]+)\)/);
61
+ if (!srgbMatch) return null;
62
+ let channels = srgbMatch[1]
63
+ .split(/[ /\t]+/)
64
+ .filter(Boolean)
65
+ .slice(0, 3)
66
+ .map((part) => (part.endsWith('%') ? Number.parseFloat(part) * 2.55 : Number.parseFloat(part) * 255));
67
+ return channels.every(Number.isFinite) ? channels : null;
68
+ }
69
+
70
+ function readCssToken(source, token) {
71
+ let computed = globalThis.getComputedStyle?.(source);
72
+ return computed?.getPropertyValue(token).trim() || '';
73
+ }
74
+
75
+ export class CellBg extends Symbiote {
76
+ init$ = {
77
+ active: false,
78
+ };
79
+
80
+ initCallback() {
81
+ this.canvas = this.ref.canvas;
82
+ this.ctx = this.canvas?.getContext?.('2d') || null;
83
+ this._available = Boolean(this.canvas && this.ctx);
84
+
85
+ this._buildPalette();
86
+
87
+ this.cols = 0;
88
+ this.rows = 0;
89
+ this.grid = new Uint8Array(0);
90
+ this.radii = new Float32Array(0);
91
+ this.running = false;
92
+ this.currentSpeed = 0;
93
+ this.accumulator = 0;
94
+ this.lastTime = now();
95
+ this.isAnimating = false;
96
+ this._stagnantCount = 0;
97
+
98
+ // We only redraw on rAF if running, or if a single frame is needed after resize
99
+ this.resize = this.resize.bind(this);
100
+ this.renderLoop = this.renderLoop.bind(this);
101
+
102
+ // Use ResizeObserver to catch layout panel resizing (not just window resizes)
103
+ // Debounce: resize canvas immediately (prevents flash), pulse only after settle
104
+ const onResize = () => {
105
+ this.resize();
106
+ if (this._resizeDebounce) clearTimeout(this._resizeDebounce);
107
+ this._resizeDebounce = setTimeout(() => {
108
+ this._resizeDebounce = null;
109
+ this.pulse(10000);
110
+ }, 300);
111
+ };
112
+
113
+ if (typeof globalThis.ResizeObserver === 'function') {
114
+ this.ro = new ResizeObserver(onResize);
115
+ } else if (typeof globalThis.addEventListener === 'function') {
116
+ globalThis.addEventListener('resize', onResize);
117
+ this._removeResizeFallback = () => globalThis.removeEventListener?.('resize', onResize);
118
+ }
119
+
120
+ // Defer observation to allow DOM to settle
121
+ setTimeout(() => {
122
+ this.ro?.observe(this);
123
+ this.resize();
124
+ this._seedRandom();
125
+ this.pulse(10000);
126
+ }, 0);
127
+
128
+ this.sub('active', (val) => {
129
+ this.toggle(!!val);
130
+ });
131
+ }
132
+
133
+ /**
134
+ * Persistent on/off state for host-controlled activity.
135
+ * toggle(true) keeps animation running until explicit toggle(false).
136
+ * @param {boolean} state
137
+ */
138
+ toggle(state) {
139
+ this._toggled = !!state;
140
+ if (state) {
141
+ this._start();
142
+ } else if (!this._pulseTimer) {
143
+ // Only stop if no active pulse timer
144
+ this._stop();
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Timed pulse: start animation for `duration` ms then auto-stop.
150
+ * Does NOT start if toggle is already active (animation already running).
151
+ * Does NOT stop if toggle is active when timer expires.
152
+ * @param {number} [duration=10000]
153
+ */
154
+ pulse(duration = 10000) {
155
+ if (this._toggled) return; // Already running persistently
156
+ if (this._pulseTimer) clearTimeout(this._pulseTimer);
157
+ this._start();
158
+ this._pulseTimer = setTimeout(() => {
159
+ this._pulseTimer = null;
160
+ if (!this._toggled) this._stop();
161
+ }, duration);
162
+ }
163
+
164
+ disconnectedCallback() {
165
+ super.disconnectedCallback();
166
+ if (this.ro) this.ro.disconnect();
167
+ this._removeResizeFallback?.();
168
+ this._stop();
169
+ }
170
+
171
+ _buildPalette() {
172
+ let bg = readCssToken(this, '--sn-cell-bg') || readCssToken(this, '--sn-bg');
173
+ let dot = readCssToken(this, '--sn-cell-dot') || readCssToken(this, '--sn-text-dim');
174
+ let bgRgb = parseCssRgb(this, bg) || [0, 0, 0];
175
+ let dotRgb = parseCssRgb(this, dot) || bgRgb;
176
+ let baseAlpha = Number.parseFloat(readCssToken(this, '--sn-cell-base-alpha')) || 0;
177
+ let alphaSpan = Number.parseFloat(readCssToken(this, '--sn-cell-alpha-span')) || 0;
178
+
179
+ this._bgFill = normalizeCssColor(this, bg) || 'transparent';
180
+
181
+ this.palette = [];
182
+ for (let i = 0; i < PALETTE_SIZE; i++) {
183
+ let t = i / (PALETTE_SIZE - 1);
184
+ let alpha = baseAlpha + t * alphaSpan;
185
+ let r = Math.round(bgRgb[0] * (1 - alpha) + dotRgb[0] * alpha);
186
+ let g = Math.round(bgRgb[1] * (1 - alpha) + dotRgb[1] * alpha);
187
+ let b = Math.round(bgRgb[2] * (1 - alpha) + dotRgb[2] * alpha);
188
+ this.palette.push(`#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`);
189
+ }
190
+ }
191
+
192
+ resize() {
193
+ if (!this._available || !this.canvas.parentElement) return;
194
+ let dpr = globalThis.devicePixelRatio || 1;
195
+ let w = this.canvas.parentElement.clientWidth;
196
+ let h = this.canvas.parentElement.clientHeight;
197
+
198
+ if (w === 0 || h === 0) return; // Hidden or not attached
199
+
200
+ this.canvas.width = w * dpr;
201
+ this.canvas.height = h * dpr;
202
+ this.ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
203
+ this.canvas._w = w;
204
+ this.canvas._h = h;
205
+
206
+ this._buildPalette();
207
+ this.ctx.fillStyle = this._bgFill;
208
+ this.ctx.fillRect(0, 0, w, h);
209
+
210
+ let oldGrid = this.grid;
211
+ let oldRadii = this.radii;
212
+ let oldCols = this.cols;
213
+ let oldRows = this.rows;
214
+
215
+ this.cols = Math.ceil(w / CELL_SIZE) + 1;
216
+ this.rows = Math.ceil(h / CELL_SIZE) + 1;
217
+
218
+ this.grid = new Uint8Array(this.cols * this.rows);
219
+ this.radii = new Float32Array(this.cols * this.rows);
220
+ this.radii.fill(MIN_RADIUS);
221
+
222
+ if (oldGrid && oldGrid.length > 0) {
223
+ let mc = Math.min(this.cols, oldCols);
224
+ let mr = Math.min(this.rows, oldRows);
225
+ for (let y = 0; y < mr; y++) {
226
+ for (let x = 0; x < mc; x++) {
227
+ this.grid[y * this.cols + x] = oldGrid[y * oldCols + x];
228
+ this.radii[y * this.cols + x] = oldRadii[y * oldCols + x];
229
+ }
230
+ }
231
+ } else {
232
+ this._seedRandom();
233
+ }
234
+
235
+ // Draw one frame if not running
236
+ if (!this.running) {
237
+ this._draw();
238
+ }
239
+ }
240
+
241
+ _seedRandom() {
242
+ if (!this.grid || this.grid.length === 0) return;
243
+ for (let i = 0; i < this.grid.length; i++) {
244
+ this.grid[i] = Math.random() < 0.15 ? 1 : 0;
245
+ if (this.grid[i]) this.radii[i] = MIN_RADIUS;
246
+ }
247
+ }
248
+
249
+ _start() {
250
+ if (!this._available) return;
251
+ if (this.running) return;
252
+ this.running = true;
253
+ if (!this.isAnimating) {
254
+ this.lastTime = now();
255
+ this.isAnimating = true;
256
+ requestFrame(this.renderLoop);
257
+ }
258
+ }
259
+
260
+ _stop() {
261
+ if (!this.running) return;
262
+ this.running = false;
263
+ // Loop will smoothly decelerate and stop in renderLoop
264
+ }
265
+
266
+ _step() {
267
+ if (!this.cols || !this.rows) return;
268
+ let len = this.cols * this.rows;
269
+ let next = new Uint8Array(len);
270
+ let changed = 0;
271
+
272
+ for (let y = 0; y < this.rows; y++) {
273
+ for (let x = 0; x < this.cols; x++) {
274
+ let neighbors = 0;
275
+ for (let dy = -1; dy <= 1; dy++) {
276
+ for (let dx = -1; dx <= 1; dx++) {
277
+ if (dx === 0 && dy === 0) continue;
278
+ let nx = (x + dx + this.cols) % this.cols;
279
+ let ny = (y + dy + this.rows) % this.rows;
280
+ neighbors += this.grid[ny * this.cols + nx];
281
+ }
282
+ }
283
+
284
+ let idx = y * this.cols + x;
285
+ let alive = this.grid[idx];
286
+
287
+ if (alive) {
288
+ next[idx] = RULE_S.includes(neighbors) ? 1 : 0;
289
+ } else {
290
+ next[idx] = RULE_B.includes(neighbors) ? 1 : 0;
291
+ }
292
+ if (next[idx] !== alive) changed++;
293
+ }
294
+ }
295
+ this.grid = next;
296
+
297
+ // Stagnation detector: if < 2% of cells changed for 5 consecutive steps, inject noise
298
+ let threshold = len * 0.02;
299
+ if (changed < threshold) {
300
+ this._stagnantCount++;
301
+ if (this._stagnantCount >= 5) {
302
+ this._injectNoise();
303
+ this._stagnantCount = 0;
304
+ }
305
+ } else {
306
+ this._stagnantCount = 0;
307
+ }
308
+ }
309
+
310
+ /**
311
+ * Inject random live cells into a random rectangular region
312
+ * to break stagnation without resetting the entire grid.
313
+ */
314
+ _injectNoise() {
315
+ let regionW = Math.max(4, (this.cols * 0.3) | 0);
316
+ let regionH = Math.max(4, (this.rows * 0.3) | 0);
317
+ let startX = (Math.random() * (this.cols - regionW)) | 0;
318
+ let startY = (Math.random() * (this.rows - regionH)) | 0;
319
+ for (let y = startY; y < startY + regionH; y++) {
320
+ for (let x = startX; x < startX + regionW; x++) {
321
+ if (Math.random() < 0.15) {
322
+ let idx = y * this.cols + x;
323
+ this.grid[idx] = 1;
324
+ this.radii[idx] = MIN_RADIUS;
325
+ }
326
+ }
327
+ }
328
+ }
329
+
330
+ renderLoop(ts) {
331
+ if (!this.isAnimating) return;
332
+
333
+ let time = now();
334
+ let dt = Math.min(time - this.lastTime, 100); // clamp dt to prevent huge jumps
335
+ this.lastTime = time;
336
+
337
+ // Smoothly accelerate/decelerate
338
+ let targetSpeed = this.running ? 1.0 : 0.0;
339
+ this.currentSpeed += (targetSpeed - this.currentSpeed) * 0.03; // Smooth transition factor
340
+
341
+ // If we're fully stopped and radii have faded (we just wait for speed to drop near 0)
342
+ if (!this.running && this.currentSpeed < 0.005) {
343
+ this.currentSpeed = 0;
344
+ this.isAnimating = false;
345
+ }
346
+
347
+ // Accumulate effective time for cellular automaton steps
348
+ this.accumulator += dt * this.currentSpeed;
349
+
350
+ let maxSteps = 5;
351
+ while (this.accumulator >= STEP_MS && maxSteps > 0) {
352
+ this._step();
353
+ this.accumulator -= STEP_MS;
354
+ maxSteps--;
355
+ }
356
+
357
+ this._draw();
358
+
359
+ if (this.isAnimating) {
360
+ requestFrame(this.renderLoop);
361
+ }
362
+ }
363
+
364
+ _draw() {
365
+ if (!this._available || !this.canvas._w) return;
366
+ let w = this.canvas._w;
367
+ let h = this.canvas._h;
368
+
369
+ this.ctx.clearRect(0, 0, w, h);
370
+ this.ctx.fillStyle = this._bgFill;
371
+ this.ctx.fillRect(0, 0, w, h);
372
+
373
+ let maxIdx = PALETTE_SIZE - 1;
374
+
375
+ for (let y = 0; y < this.rows; y++) {
376
+ for (let x = 0; x < this.cols; x++) {
377
+ let idx = y * this.cols + x;
378
+ let alive = this.grid[idx];
379
+
380
+ let targetR = alive ? MAX_RADIUS : MIN_RADIUS;
381
+ let currentR = this.radii[idx];
382
+
383
+ if (alive) {
384
+ this.radii[idx] = currentR + (targetR - currentR) * 0.2;
385
+ } else {
386
+ this.radii[idx] = currentR + (targetR - currentR) * FADE_RATE;
387
+ }
388
+
389
+ let r = this.radii[idx];
390
+ let cx = x * CELL_SIZE;
391
+ let cy = y * CELL_SIZE;
392
+
393
+ let t = (r - MIN_RADIUS) / (MAX_RADIUS - MIN_RADIUS);
394
+ if (t < 0) t = 0;
395
+ if (t > 1) t = 1;
396
+
397
+ let pi = (t * maxIdx + 0.5) | 0;
398
+
399
+ this.ctx.beginPath();
400
+ this.ctx.arc(cx, cy, r, 0, Math.PI * 2);
401
+ this.ctx.fillStyle = this.palette[pi];
402
+ this.ctx.fill();
403
+ }
404
+ }
405
+ }
406
+ }
407
+
408
+ CellBg.template = template;
409
+ CellBg.rootStyles = css;
410
+ CellBg.reg('cell-bg');
@@ -0,0 +1,5 @@
1
+ import { html } from '@symbiotejs/symbiote';
2
+
3
+ export default html`
4
+ <canvas ref="canvas"></canvas>
5
+ `;