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,123 @@
1
+ /**
2
+ * Chat Context — generic keyed context management with pluggable type handlers.
3
+ *
4
+ * Provides context item normalization, merge-by-key, removal, and serialization.
5
+ * Applications register custom type handlers for domain-specific context types.
6
+ *
7
+ * Built-in: 'file' type handler.
8
+ */
9
+
10
+ function shortName(fullPath) {
11
+ let clean = String(fullPath || '')
12
+ let trimmed = clean.endsWith('/') ? clean.slice(0, -1) : clean
13
+ return (trimmed.split('/').pop() || clean) + (clean.endsWith('/') ? '/' : '')
14
+ }
15
+
16
+ /** Built-in file type handler. */
17
+ export const FILE_TYPE_HANDLER = {
18
+ normalize(input) {
19
+ let path = String(input.path || '').trim()
20
+ return {
21
+ type: 'file',
22
+ key: `file:${path}`,
23
+ path,
24
+ name: input.name || shortName(path),
25
+ title: path,
26
+ icon: path.endsWith('/') ? 'folder' : 'description',
27
+ source: input.source || 'manual',
28
+ }
29
+ },
30
+ contextKey(item) {
31
+ return item.key || `file:${item.path}`
32
+ },
33
+ formatPayload(item) {
34
+ return {
35
+ type: 'file',
36
+ path: item.path,
37
+ source: item.source,
38
+ }
39
+ },
40
+ }
41
+
42
+ const DEFAULT_TYPE_HANDLERS = {
43
+ file: FILE_TYPE_HANDLER,
44
+ }
45
+
46
+ function _resolveHandler(type, typeHandlers) {
47
+ return typeHandlers[type] || typeHandlers.file || FILE_TYPE_HANDLER
48
+ }
49
+
50
+ function _contextKey(item, typeHandlers) {
51
+ if (item.key) return String(item.key)
52
+ let handler = _resolveHandler(item.type || 'file', typeHandlers)
53
+ if (handler.contextKey) return handler.contextKey(item)
54
+ if (item.path) return `file:${item.path}`
55
+ return `${item.type || 'context'}:${item.name || item.title || ''}`
56
+ }
57
+
58
+ /**
59
+ * Normalize a context item using the appropriate type handler.
60
+ *
61
+ * @param {object} [input={}]
62
+ * @param {object} [typeHandlers] - Map of type name to handler object.
63
+ * @returns {object} Normalized context item with a `key` property.
64
+ */
65
+ export function normalizeAttachedContextItem(input = {}, typeHandlers = DEFAULT_TYPE_HANDLERS) {
66
+ let type = input.type || 'file'
67
+ let handler = _resolveHandler(type, typeHandlers)
68
+ return handler.normalize(input)
69
+ }
70
+
71
+ /**
72
+ * Add or replace a context item by key.
73
+ *
74
+ * @param {Array} [current=[]] - Current context items.
75
+ * @param {object} [input={}] - Raw input to normalize and merge.
76
+ * @param {object} [typeHandlers] - Map of type name to handler object.
77
+ * @returns {Array} Updated context items.
78
+ */
79
+ export function mergeAttachedContext(current = [], input = {}, typeHandlers = DEFAULT_TYPE_HANDLERS) {
80
+ let item = normalizeAttachedContextItem(input, typeHandlers)
81
+ let key = _contextKey(item, typeHandlers)
82
+ if (!key || key.endsWith(':')) return current
83
+ let withoutExisting = current.filter((ctx) => _contextKey(ctx, typeHandlers) !== key)
84
+ return [...withoutExisting, item]
85
+ }
86
+
87
+ /**
88
+ * Remove a context item by key.
89
+ *
90
+ * @param {Array} [current=[]] - Current context items.
91
+ * @param {string} key - Key of the item to remove.
92
+ * @param {object} [typeHandlers] - Map of type name to handler object.
93
+ * @returns {Array} Updated context items.
94
+ */
95
+ export function removeAttachedContext(current = [], key, typeHandlers = DEFAULT_TYPE_HANDLERS) {
96
+ return current.filter((ctx) => _contextKey(ctx, typeHandlers) !== key)
97
+ }
98
+
99
+ /**
100
+ * Serialize context items into an attached context block string.
101
+ *
102
+ * @param {Array} [context=[]] - Context items to serialize.
103
+ * @param {object} [typeHandlers] - Map of type name to handler object.
104
+ * @returns {string} Serialized context block, or empty string if no items.
105
+ */
106
+ export function formatAttachedContextBlock(context = [], typeHandlers = DEFAULT_TYPE_HANDLERS) {
107
+ let items = context
108
+ .map((c) => normalizeAttachedContextItem(c, typeHandlers))
109
+ .filter((item) => !_contextKey(item, typeHandlers).endsWith(':'))
110
+ if (items.length === 0) return ''
111
+
112
+ let payload = items.map((item) => {
113
+ let handler = _resolveHandler(item.type || 'file', typeHandlers)
114
+ if (handler.formatPayload) return handler.formatPayload(item)
115
+ return {
116
+ type: 'file',
117
+ path: item.path,
118
+ source: item.source,
119
+ }
120
+ })
121
+
122
+ return `[Attached Context]\n${JSON.stringify(payload, null, 2)}\n\n`
123
+ }
@@ -0,0 +1,156 @@
1
+ import { escapeHtml, formatElapsed } from '../display/markdown-formatter.js';
2
+ import { translate } from '../locale/index.js';
3
+
4
+ export function buildSessionMetaHtml(text) {
5
+ if (!text) return '';
6
+ let chips = [];
7
+ let modeMatch = text.match(/- Mode:\s*(.+)/i);
8
+ if (modeMatch) {
9
+ let mode = modeMatch[1].trim();
10
+ let iconName = mode === 'yolo' ? 'bolt' : mode === 'plan' ? 'lock' : 'settings';
11
+ chips.push(`<span class="meta-chip"><span class="material-symbols-outlined meta-chip-icon">${iconName}</span> ${escapeHtml(mode)}</span>`);
12
+ }
13
+ let exitMatch = text.match(/- Exit code:\s*(\d+)/i);
14
+ if (exitMatch) {
15
+ let code = parseInt(exitMatch[1]);
16
+ let cls = code === 0 ? 'meta-ok' : 'meta-err';
17
+ chips.push(`<span class="meta-chip ${cls}">${escapeHtml(translate('chat.meta.exit', { code }))}</span>`);
18
+ }
19
+ let sidMatch = text.match(/- Session ID:\s*`([^`]+)`/i);
20
+ if (sidMatch) {
21
+ chips.push(`<span class="meta-chip meta-sid" title="${escapeHtml(sidMatch[1])}">${escapeHtml(sidMatch[1].substring(0, 12))}...</span>`);
22
+ }
23
+ let tokensMatch = text.match(/- Tokens:\s*(\d+)/i);
24
+ if (tokensMatch) {
25
+ chips.push(`<span class="meta-chip meta-info" title="${escapeHtml(translate('chat.meta.tokens'))}">${tokensMatch[1]} tks</span>`);
26
+ }
27
+ let costMatch = text.match(/- Cost:\s*\$?([\d.]+)/i);
28
+ if (costMatch) {
29
+ chips.push(`<span class="meta-chip meta-info" title="${escapeHtml(translate('chat.meta.cost'))}">$${costMatch[1]}</span>`);
30
+ }
31
+ return chips.join('');
32
+ }
33
+
34
+ export function buildWorkMetaHtml(meta) {
35
+ if (!meta) return '';
36
+ let items = [];
37
+ if (meta.mode) {
38
+ let iconName = meta.mode === 'yolo' ? 'bolt' : 'settings';
39
+ items.push(`<span class="meta-chip"><span class="material-symbols-outlined meta-chip-icon">${iconName}</span> ${escapeHtml(meta.mode)}</span>`);
40
+ }
41
+ if (meta.exitCode != null) {
42
+ let cls = meta.exitCode === 0 ? 'meta-ok' : 'meta-err';
43
+ items.push(`<span class="meta-chip ${cls}">${escapeHtml(translate('chat.meta.exit', { code: meta.exitCode }))}</span>`);
44
+ }
45
+ if (meta.sessionId) {
46
+ items.push(`<span class="meta-chip meta-sid" title="${escapeHtml(meta.sessionId)}">${escapeHtml(meta.sessionId.substring(0, 16))}...</span>`);
47
+ }
48
+ if (meta.tools) {
49
+ let key = meta.tools > 1 ? 'chat.meta.toolCalls' : 'chat.meta.toolCall';
50
+ items.push(`<span class="meta-chip">${escapeHtml(translate(key, { count: meta.tools }))}</span>`);
51
+ }
52
+ if (meta.tokens != null) items.push(`<span class="meta-chip meta-info">${meta.tokens} tks</span>`);
53
+ if (meta.cost != null) items.push(`<span class="meta-chip meta-info">$${meta.cost.toFixed(4)}</span>`);
54
+ if (meta.errors) items.push(`<span class="meta-chip meta-err">${escapeHtml(meta.errors)}</span>`);
55
+ return items.join('');
56
+ }
57
+
58
+ export function findPreviousAgentText(messages, fromIndex) {
59
+ for (let i = fromIndex - 1; i >= 0; i--) {
60
+ let msg = messages[i];
61
+ if (msg?.role === 'agent' && typeof msg.text === 'string' && msg.text.trim()) return msg.text;
62
+ if (msg?.role === 'user') break;
63
+ }
64
+ return '';
65
+ }
66
+
67
+ function compactText(value, maxLength = 96) {
68
+ let text = String(value || '').replace(/\s+/g, ' ').trim();
69
+ if (!text) return '';
70
+ return text.length > maxLength ? `${text.slice(0, maxLength - 3)}...` : text;
71
+ }
72
+
73
+ export function summarizeToolInput(input) {
74
+ if (input == null) return '';
75
+ if (typeof input === 'string') return compactText(input);
76
+ if (typeof input !== 'object') return compactText(input);
77
+ for (let key of ['command', 'description', 'prompt', 'question', 'path', 'file', 'name']) {
78
+ if (typeof input[key] === 'string' && input[key].trim()) return compactText(input[key]);
79
+ }
80
+ return compactText(JSON.stringify(input));
81
+ }
82
+
83
+ export function buildWorkSummaryHtml(msg, copyText) {
84
+ let metaHtml = buildWorkMetaHtml(msg?.meta);
85
+ let bodyHtml = metaHtml ? `<div class="work-body">${metaHtml}</div>` : '';
86
+ let copyBtn = copyText
87
+ ? `<button class="work-copy-btn" type="button" title="${escapeHtml(translate('chat.message.copyResponse'))}" data-copy-text="${escapeHtml(copyText)}"><span class="material-symbols-outlined">content_copy</span></button>`
88
+ : '';
89
+ let elapsed = formatElapsed(msg?.elapsed || 0);
90
+ return `<div class="work-summary-wrap"><details class="work-summary"><summary><span class="material-symbols-outlined work-summary-icon">check_circle</span>${escapeHtml(translate('chat.message.workedFor', { elapsed }))}${copyBtn}</summary>${bodyHtml}</details></div>`;
91
+ }
92
+
93
+ export function toChatMessageItem(msg, options = {}) {
94
+ return {
95
+ type: msg?.type || msg?.role,
96
+ role: msg?.role,
97
+ text: msg?.text || msg?.content || '',
98
+ isStreaming: Boolean(options.isLatestStreaming),
99
+ isLatestTool: Boolean(options.isLatestTool),
100
+ name: msg?.name || '',
101
+ input: msg?.input || null,
102
+ result: msg?.result || null,
103
+ done: Boolean(msg?.done),
104
+ elapsedText: formatElapsed(msg?.elapsed || 0),
105
+ status: msg?.status || '',
106
+ metaHtml: buildWorkMetaHtml(msg?.meta),
107
+ cardItems: Array.isArray(msg?.cardItems) ? msg.cardItems : [],
108
+ workSummaryHtml: '',
109
+ copyText: '',
110
+ };
111
+ }
112
+
113
+ export function buildChatMessageItems(messages = [], options = {}) {
114
+ let source = Array.isArray(messages) ? messages : [];
115
+ let hasActiveStream = Boolean(options.hasActiveStream);
116
+ let items = [];
117
+ let streamingBoards = [];
118
+ let lastAgentItem = null;
119
+ let lastStreamingIndex = -1;
120
+ let lastToolIndex = -1;
121
+
122
+ for (let i = 0; i < source.length; i++) {
123
+ let msg = source[i];
124
+ if (msg?.streaming) lastStreamingIndex = i;
125
+ if (msg?.role === 'tool') lastToolIndex = i;
126
+ }
127
+
128
+ for (let i = 0; i < source.length; i++) {
129
+ let msg = source[i];
130
+ let isLatestStreaming = hasActiveStream && i === lastStreamingIndex && Boolean(msg?.streaming);
131
+
132
+ if (msg?.role === 'thinking' && msg.done) {
133
+ let copyText = findPreviousAgentText(source, i);
134
+ if (lastAgentItem) {
135
+ lastAgentItem.workSummaryHtml = buildWorkSummaryHtml(msg, copyText);
136
+ continue;
137
+ }
138
+ }
139
+
140
+ let item = toChatMessageItem(msg, {
141
+ isLatestStreaming,
142
+ isLatestTool: i === lastToolIndex,
143
+ });
144
+ if (msg?.role === 'thinking' && msg.done) {
145
+ item.copyText = findPreviousAgentText(source, i);
146
+ }
147
+ items.push(item);
148
+
149
+ if (msg?.role === 'agent') lastAgentItem = item;
150
+ if (msg?.role === 'board' && item.isStreaming && item.cardItems?.length) {
151
+ streamingBoards.push(item.cardItems.map((card) => card.id).filter(Boolean));
152
+ }
153
+ }
154
+
155
+ return { items, streamingBoards };
156
+ }
package/cli.js ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ import { cmdDiscover } from './discover.js';
6
+
7
+ let command = process.argv[2] || 'discover';
8
+ let isMain = fileURLToPath(import.meta.url) === process.argv[1];
9
+
10
+ if (isMain && command === 'discover') {
11
+ let result = await cmdDiscover({});
12
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
13
+ } else if (isMain) {
14
+ console.log(`symbiote-ui CLI
15
+
16
+ Commands:
17
+ discover Output provider metadata as JSON
18
+ `);
19
+ if (command && command !== '--help' && command !== '-h') process.exit(1);
20
+ }
@@ -0,0 +1,93 @@
1
+ export default /*css*/ `
2
+ sn-button {
3
+ display: inline-flex;
4
+ box-sizing: border-box;
5
+ align-items: center;
6
+ justify-content: center;
7
+ gap: var(--sn-button-gap);
8
+ min-width: 0;
9
+ min-height: var(--sn-button-min-height);
10
+ padding: var(--sn-button-padding);
11
+ border: 1px solid var(--sn-button-border);
12
+ border-radius: var(--sn-button-radius);
13
+ background: var(--sn-button-bg);
14
+ color: var(--sn-button-color);
15
+ font-family: var(--sn-font);
16
+ font-size: var(--sn-button-font-size);
17
+ font-weight: var(--sn-button-font-weight);
18
+ line-height: var(--sn-button-line-height);
19
+ cursor: pointer;
20
+ user-select: none;
21
+ transition: background 150ms ease, border-color 150ms ease, color 150ms ease, filter 150ms ease;
22
+ }
23
+
24
+ sn-button[hidden] {
25
+ display: none !important;
26
+ }
27
+
28
+ sn-button:hover:not([disabled]) {
29
+ border-color: var(--sn-button-hover-border);
30
+ background: var(--sn-button-hover-bg);
31
+ }
32
+
33
+ sn-button:focus-visible {
34
+ outline: var(--sn-button-focus-ring);
35
+ outline-offset: 2px;
36
+ }
37
+
38
+ sn-button[disabled] {
39
+ cursor: not-allowed;
40
+ opacity: var(--sn-button-disabled-opacity);
41
+ }
42
+
43
+ sn-button[variant="primary"] {
44
+ border-color: var(--sn-button-primary-border);
45
+ background: var(--sn-button-primary-bg);
46
+ color: var(--sn-button-primary-color);
47
+ }
48
+
49
+ sn-button[variant="primary"]:hover:not([disabled]) {
50
+ filter: var(--sn-button-primary-hover-filter);
51
+ }
52
+
53
+ sn-button[variant="success"] {
54
+ border-color: var(--sn-button-success-border);
55
+ background: var(--sn-button-success-bg);
56
+ color: var(--sn-button-success-color);
57
+ }
58
+
59
+ sn-button[variant="success"]:hover:not([disabled]) {
60
+ border-color: var(--sn-button-success-hover-border);
61
+ background: var(--sn-button-success-hover-bg);
62
+ color: var(--sn-button-success-hover-color);
63
+ }
64
+
65
+ sn-button[variant="danger"] {
66
+ border-color: var(--sn-button-danger-border);
67
+ background: var(--sn-button-danger-bg);
68
+ color: var(--sn-button-danger-color);
69
+ }
70
+
71
+ sn-button[variant="danger"]:hover:not([disabled]) {
72
+ border-color: var(--sn-button-danger-hover-border);
73
+ background: var(--sn-button-danger-hover-bg);
74
+ color: var(--sn-button-danger-hover-color);
75
+ }
76
+
77
+ sn-button[variant="icon"] {
78
+ width: var(--sn-button-icon-size);
79
+ min-height: var(--sn-button-icon-size);
80
+ padding: 0;
81
+ border-color: transparent;
82
+ background: transparent;
83
+ color: var(--sn-text-dim);
84
+ }
85
+
86
+ sn-button[variant="icon"]:hover:not([disabled]) {
87
+ color: var(--sn-text);
88
+ }
89
+
90
+ sn-button .material-symbols-outlined {
91
+ font-size: var(--sn-button-icon-font-size);
92
+ }
93
+ `;
@@ -0,0 +1,78 @@
1
+ import Symbiote from '@symbiotejs/symbiote';
2
+ import { slotProcessor } from '@symbiotejs/symbiote/core/slotProcessor.js';
3
+ import template from './Button.tpl.js';
4
+ import css from './Button.css.js';
5
+
6
+ export class ActionButton extends Symbiote {
7
+ static observedAttributes = ['disabled'];
8
+
9
+ #disabled = false;
10
+
11
+ #onClick = (event) => {
12
+ if (!this.disabled) return;
13
+ event.preventDefault();
14
+ event.stopImmediatePropagation();
15
+ };
16
+
17
+ #onKeyDown = (event) => {
18
+ if (this.disabled) return;
19
+ if (event.key !== 'Enter' && event.key !== ' ') return;
20
+ event.preventDefault();
21
+ this.click();
22
+ };
23
+
24
+ constructor() {
25
+ super();
26
+ this.templateProcessors.add(slotProcessor);
27
+ }
28
+
29
+ connectedCallback() {
30
+ super.connectedCallback?.();
31
+ if (!this.hasAttribute('role')) this.setAttribute('role', 'button');
32
+ if (!this.hasAttribute('tabindex')) this.tabIndex = 0;
33
+ this.#disabled = this.hasAttribute('disabled');
34
+ this._syncDisabled(this.#disabled);
35
+ this.addEventListener('click', this.#onClick, { capture: true });
36
+ this.addEventListener('keydown', this.#onKeyDown);
37
+ }
38
+
39
+ disconnectedCallback() {
40
+ this.removeEventListener('click', this.#onClick, { capture: true });
41
+ this.removeEventListener('keydown', this.#onKeyDown);
42
+ super.disconnectedCallback?.();
43
+ }
44
+
45
+ get disabled() {
46
+ return this.#disabled || this.hasAttribute('disabled');
47
+ }
48
+
49
+ set disabled(value) {
50
+ this.#disabled = Boolean(value);
51
+ this._syncDisabled(this.#disabled);
52
+ }
53
+
54
+ attributeChangedCallback(name, oldValue, newValue) {
55
+ if (name !== 'disabled') {
56
+ super.attributeChangedCallback?.(name, oldValue, newValue);
57
+ return;
58
+ }
59
+ if (oldValue === newValue) return;
60
+ this.#disabled = newValue !== null;
61
+ this._syncDisabled(this.#disabled, { reflect: false });
62
+ }
63
+
64
+ _syncDisabled(value, options = {}) {
65
+ let disabled = Boolean(value);
66
+ if (options.reflect !== false) {
67
+ this.toggleAttribute('disabled', disabled);
68
+ }
69
+ this.setAttribute('aria-disabled', disabled ? 'true' : 'false');
70
+ this.tabIndex = disabled ? -1 : 0;
71
+ }
72
+ }
73
+
74
+ ActionButton.template = template;
75
+ ActionButton.rootStyles = css;
76
+ ActionButton.reg('sn-button');
77
+
78
+ export default ActionButton;
@@ -0,0 +1,3 @@
1
+ import { html } from '@symbiotejs/symbiote';
2
+
3
+ export default html`<slot></slot>`;
@@ -0,0 +1,91 @@
1
+ export default /*css*/ `
2
+ sn-field {
3
+ display: block;
4
+ box-sizing: border-box;
5
+ margin-block-end: var(--sn-field-margin-block-end);
6
+ color: var(--sn-text);
7
+ font-family: var(--sn-font);
8
+ }
9
+
10
+ sn-field[hidden] {
11
+ display: none !important;
12
+ }
13
+
14
+ sn-field > label,
15
+ sn-field > [slot="label"],
16
+ sn-field > .sn-field-label {
17
+ display: block;
18
+ margin-block-end: var(--sn-field-label-margin-block-end);
19
+ color: var(--sn-field-label-color);
20
+ font-size: var(--sn-field-label-size);
21
+ font-weight: var(--sn-field-label-weight);
22
+ line-height: var(--sn-field-label-line-height);
23
+ text-transform: var(--sn-field-label-transform);
24
+ }
25
+
26
+ sn-field input,
27
+ sn-field select,
28
+ sn-field textarea {
29
+ box-sizing: border-box;
30
+ width: 100%;
31
+ min-width: 0;
32
+ border: 1px solid var(--sn-field-control-border);
33
+ border-radius: var(--sn-field-control-radius);
34
+ background: var(--sn-field-control-bg);
35
+ color: var(--sn-field-control-color);
36
+ padding: var(--sn-field-control-padding);
37
+ font-family: var(--sn-font);
38
+ font-size: var(--sn-field-control-font-size);
39
+ line-height: var(--sn-field-control-line-height);
40
+ transition: border-color 150ms ease, background 150ms ease, box-shadow 150ms ease;
41
+ }
42
+
43
+ sn-field input::placeholder,
44
+ sn-field textarea::placeholder {
45
+ color: var(--sn-field-placeholder-color);
46
+ }
47
+
48
+ sn-field input:focus,
49
+ sn-field select:focus,
50
+ sn-field textarea:focus {
51
+ outline: none;
52
+ border-color: var(--sn-field-control-focus-border);
53
+ box-shadow: var(--sn-field-control-focus-shadow);
54
+ }
55
+
56
+ sn-field textarea {
57
+ min-height: var(--sn-field-textarea-min-height);
58
+ resize: vertical;
59
+ }
60
+
61
+ sn-field [slot="hint"],
62
+ sn-field .sn-field-hint {
63
+ display: block;
64
+ margin-block-start: var(--sn-field-hint-margin-block-start);
65
+ color: var(--sn-field-hint-color);
66
+ font-size: var(--sn-field-hint-size);
67
+ line-height: var(--sn-field-hint-line-height);
68
+ }
69
+
70
+ sn-field[variant="inline"] {
71
+ display: flex;
72
+ align-items: center;
73
+ gap: var(--sn-field-inline-gap);
74
+ }
75
+
76
+ sn-field[variant="inline"] > label,
77
+ sn-field[variant="inline"] > [slot="label"],
78
+ sn-field[variant="inline"] > .sn-field-label {
79
+ margin-block-end: 0;
80
+ }
81
+
82
+ sn-field[variant="compact"] {
83
+ margin-block-end: var(--sn-field-compact-margin-block-end);
84
+ }
85
+
86
+ sn-field[variant="compact"] input,
87
+ sn-field[variant="compact"] select,
88
+ sn-field[variant="compact"] textarea {
89
+ padding: var(--sn-field-compact-control-padding);
90
+ }
91
+ `;
@@ -0,0 +1,17 @@
1
+ import Symbiote from '@symbiotejs/symbiote';
2
+ import { slotProcessor } from '@symbiotejs/symbiote/core/slotProcessor.js';
3
+ import template from './Field.tpl.js';
4
+ import css from './Field.css.js';
5
+
6
+ export class FormField extends Symbiote {
7
+ constructor() {
8
+ super();
9
+ this.templateProcessors.add(slotProcessor);
10
+ }
11
+ }
12
+
13
+ FormField.template = template;
14
+ FormField.rootStyles = css;
15
+ FormField.reg('sn-field');
16
+
17
+ export default FormField;
@@ -0,0 +1,3 @@
1
+ import { html } from '@symbiotejs/symbiote';
2
+
3
+ export default html`<slot></slot>`;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Connection — link between two node ports
3
+ *
4
+ * Uses symbiote-node naming: from/out/to/in
5
+ * Adds connection ID for selection/history support.
6
+ *
7
+ * @module symbiote-node/core/Connection
8
+ */
9
+
10
+ import { uid } from './Socket.js';
11
+
12
+ export class Connection {
13
+ /**
14
+ * @param {import('./Node.js').Node} sourceNode - Source node
15
+ * @param {string} sourceOutput - Output port key
16
+ * @param {import('./Node.js').Node} targetNode - Target node
17
+ * @param {string} targetInput - Input port key
18
+ */
19
+ constructor(sourceNode, sourceOutput, targetNode, targetInput) {
20
+ if (!sourceNode.outputs[sourceOutput]) {
21
+ throw new Error(`source node doesn't have output '${sourceOutput}'`);
22
+ }
23
+ if (!targetNode.inputs[targetInput]) {
24
+ throw new Error(`target node doesn't have input '${targetInput}'`);
25
+ }
26
+
27
+ /** @type {string} */
28
+ this.id = uid('conn');
29
+
30
+ /** @type {string} - Source node ID (symbiote-node: 'from') */
31
+ this.from = sourceNode.id;
32
+
33
+ /** @type {string} - Source output key (symbiote-node: 'out') */
34
+ this.out = sourceOutput;
35
+
36
+ /** @type {string} - Target node ID (symbiote-node: 'to') */
37
+ this.to = targetNode.id;
38
+
39
+ /** @type {string} - Target input key (symbiote-node: 'in') */
40
+ this.in = targetInput;
41
+
42
+ /** @type {boolean} */
43
+ this.selected = false;
44
+ }
45
+ }
46
+
47
+ export { Connection as default };