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,242 @@
1
+ import {
2
+ WEBXR_FEATURES,
3
+ WEBXR_MODES,
4
+ createWebXRLayer,
5
+ createWebXRRenderLoop,
6
+ endWebXRSession,
7
+ listWebXRInputSources,
8
+ requestWebXRReferenceSpace,
9
+ requestWebXRSession,
10
+ syncWebXRCanvas,
11
+ } from './webxr.js';
12
+ import { applyXRThemeToPanel, createXRThemeSnapshot } from './theme-bridge.js';
13
+
14
+ function initialState(options = {}) {
15
+ return {
16
+ status: 'idle',
17
+ mode: null,
18
+ scene: options.scene || null,
19
+ session: null,
20
+ referenceSpace: null,
21
+ layer: null,
22
+ inputSources: [],
23
+ frameCount: 0,
24
+ lastFrameTime: null,
25
+ reason: null,
26
+ themeSnapshot: options.themeSnapshot || null,
27
+ renderMode: 'dom-fallback',
28
+ };
29
+ }
30
+
31
+ export function createXRSceneDiagnostics(state = {}, options = {}) {
32
+ let scene = state.scene || null;
33
+ let panels = Array.isArray(scene?.panels) ? scene.panels : [];
34
+ let layer = state.layer || null;
35
+ let canvas = options.canvas || null;
36
+ let gl = options.gl || null;
37
+ return {
38
+ version: 'xr-scene-diagnostics-v1',
39
+ status: state.status || 'unknown',
40
+ mode: state.mode || null,
41
+ renderMode: state.renderMode || 'unknown',
42
+ reason: state.reason || null,
43
+ frameCount: Number(state.frameCount || 0),
44
+ lastFrameTime: state.lastFrameTime || null,
45
+ inputSources: Array.isArray(state.inputSources) ? state.inputSources.length : 0,
46
+ scene: scene
47
+ ? {
48
+ version: scene.version || null,
49
+ coordinateSystem: scene.coordinateSystem || null,
50
+ themeScope: scene.themeScope || null,
51
+ panelCount: panels.length,
52
+ panels: panels.map((panel) => ({
53
+ id: panel.id || null,
54
+ component: panel.component || panel.panelType || null,
55
+ anchor: panel.anchor || null,
56
+ size: Array.isArray(panel.size) ? [...panel.size] : null,
57
+ position: Array.isArray(panel.position) ? [...panel.position] : null,
58
+ rotation: Array.isArray(panel.rotation) ? [...panel.rotation] : null,
59
+ contentViewport: panel.contentViewport
60
+ ? {
61
+ width: panel.contentViewport.width,
62
+ height: panel.contentViewport.height,
63
+ scale: panel.contentViewport.scale,
64
+ source: panel.contentViewport.source,
65
+ }
66
+ : null,
67
+ })),
68
+ }
69
+ : null,
70
+ layer: layer
71
+ ? {
72
+ type: layer.constructor?.name || 'XRWebGLLayer',
73
+ framebufferWidth: Number(layer.framebufferWidth || 0),
74
+ framebufferHeight: Number(layer.framebufferHeight || 0),
75
+ hasFramebuffer: Boolean(layer.framebuffer),
76
+ }
77
+ : null,
78
+ canvas: canvas
79
+ ? {
80
+ width: Number(canvas.width || 0),
81
+ height: Number(canvas.height || 0),
82
+ clientWidth: Number(canvas.clientWidth || 0),
83
+ clientHeight: Number(canvas.clientHeight || 0),
84
+ }
85
+ : null,
86
+ gl: gl
87
+ ? {
88
+ hasContext: true,
89
+ drawingBufferWidth: Number(gl.drawingBufferWidth || 0),
90
+ drawingBufferHeight: Number(gl.drawingBufferHeight || 0),
91
+ }
92
+ : { hasContext: false },
93
+ };
94
+ }
95
+
96
+ export function createXRSceneController(options = {}) {
97
+ let target = options.globalThis || globalThis;
98
+ let referenceSpaceType = options.referenceSpaceType || WEBXR_FEATURES.localFloor;
99
+ let renderLoop = null;
100
+ let onFrame = options.onFrame || null;
101
+ let state = initialState(options);
102
+
103
+ function snapshotState() {
104
+ return {
105
+ ...state,
106
+ inputSources: [...state.inputSources],
107
+ scene: state.scene,
108
+ session: state.session,
109
+ referenceSpace: state.referenceSpace,
110
+ layer: state.layer,
111
+ themeSnapshot: state.themeSnapshot,
112
+ };
113
+ }
114
+
115
+ function setScene(scene, sceneOptions = {}) {
116
+ let themeSnapshot = sceneOptions.themeSnapshot || state.themeSnapshot || createXRThemeSnapshot(sceneOptions.themeRoot, {
117
+ themeScope: scene?.themeScope,
118
+ });
119
+ state = {
120
+ ...state,
121
+ scene: scene
122
+ ? {
123
+ ...scene,
124
+ panels: scene.panels.map((panel) => applyXRThemeToPanel(panel, themeSnapshot)),
125
+ themeScope: scene.themeScope || themeSnapshot.themeScope,
126
+ }
127
+ : null,
128
+ themeSnapshot,
129
+ };
130
+ return snapshotState();
131
+ }
132
+
133
+ async function start(mode = WEBXR_MODES.immersiveVr, sessionOptions = {}) {
134
+ if (state.status === 'running') {
135
+ return { ok: true, state: snapshotState() };
136
+ }
137
+
138
+ let sessionResult = await requestWebXRSession(target, mode, sessionOptions);
139
+ if (!sessionResult.ok) {
140
+ state = {
141
+ ...state,
142
+ status: 'fallback',
143
+ mode,
144
+ reason: sessionResult.reason || 'unsupported',
145
+ renderMode: 'dom-fallback',
146
+ };
147
+ return { ok: false, reason: state.reason, state: snapshotState() };
148
+ }
149
+
150
+ let layerResult = { ok: true, layer: null, reason: null };
151
+ if (sessionOptions.gl || sessionOptions.canvas) {
152
+ let gl = sessionOptions.gl || sessionOptions.canvas?.getContext?.('webgl', {
153
+ xrCompatible: true,
154
+ alpha: true,
155
+ antialias: true,
156
+ });
157
+ if (gl?.makeXRCompatible) await gl.makeXRCompatible();
158
+ layerResult = createWebXRLayer(target, sessionResult.session, gl, sessionOptions.layerOptions || {});
159
+ if (layerResult.ok) {
160
+ await sessionResult.session.updateRenderState?.({ baseLayer: layerResult.layer });
161
+ syncWebXRCanvas(sessionOptions.canvas, gl, sessionResult.session);
162
+ } else if (sessionOptions.requireLayer !== false) {
163
+ await endWebXRSession(sessionResult.session);
164
+ state = {
165
+ ...state,
166
+ status: 'fallback',
167
+ mode,
168
+ reason: layerResult.reason || 'layer-failed',
169
+ renderMode: 'dom-fallback',
170
+ };
171
+ return { ok: false, reason: state.reason, state: snapshotState() };
172
+ }
173
+ }
174
+
175
+ let referenceResult = await requestWebXRReferenceSpace(
176
+ sessionResult.session,
177
+ sessionOptions.referenceSpaceType || referenceSpaceType,
178
+ );
179
+ if (!referenceResult.ok) {
180
+ await endWebXRSession(sessionResult.session);
181
+ state = {
182
+ ...state,
183
+ status: 'fallback',
184
+ mode,
185
+ reason: referenceResult.reason || 'reference-space-failed',
186
+ renderMode: 'dom-fallback',
187
+ };
188
+ return { ok: false, reason: state.reason, state: snapshotState() };
189
+ }
190
+
191
+ state = {
192
+ ...state,
193
+ status: 'running',
194
+ mode,
195
+ session: sessionResult.session,
196
+ referenceSpace: referenceResult.referenceSpace,
197
+ layer: layerResult.layer,
198
+ inputSources: listWebXRInputSources(sessionResult.session),
199
+ reason: layerResult.ok ? null : layerResult.reason,
200
+ renderMode: 'webxr-session',
201
+ };
202
+
203
+ renderLoop = createWebXRRenderLoop(sessionResult.session, (time, frame, session) => {
204
+ state = {
205
+ ...state,
206
+ frameCount: state.frameCount + 1,
207
+ lastFrameTime: time,
208
+ inputSources: listWebXRInputSources(session),
209
+ };
210
+ onFrame?.(time, frame, snapshotState());
211
+ });
212
+
213
+ return { ok: true, state: snapshotState() };
214
+ }
215
+
216
+ async function stop() {
217
+ renderLoop?.stop();
218
+ renderLoop = null;
219
+ let session = state.session;
220
+ let ended = await endWebXRSession(session);
221
+ state = {
222
+ ...state,
223
+ status: 'stopped',
224
+ session: null,
225
+ referenceSpace: null,
226
+ layer: null,
227
+ inputSources: [],
228
+ renderMode: 'dom-fallback',
229
+ };
230
+ return { ok: ended, state: snapshotState() };
231
+ }
232
+
233
+ return {
234
+ setScene,
235
+ start,
236
+ stop,
237
+ getState: snapshotState,
238
+ getDiagnostics(options = {}) {
239
+ return createXRSceneDiagnostics(snapshotState(), options);
240
+ },
241
+ };
242
+ }
@@ -0,0 +1,212 @@
1
+ import {
2
+ adjustXRPanelPoseForComfort,
3
+ adjustXRPanelRotationForViewer,
4
+ createXRPanelContentViewport,
5
+ projectLayoutToXR,
6
+ } from './layout-projection.js';
7
+
8
+ export const XR_SPATIAL_SCENE_VERSION = 'xr-spatial-scene-v1';
9
+
10
+ export const XR_SPATIAL_SPACE = Object.freeze({
11
+ localFloor: 'webxr-local-floor',
12
+ viewer: 'webxr-viewer',
13
+ });
14
+
15
+ export const XR_SCENE_ROOT_TRANSFORM_VERSION = 'xr-scene-root-transform-v1';
16
+
17
+ const DEFAULT_USER_SPACE = Object.freeze({
18
+ eyeHeight: 1.6,
19
+ comfortRadius: 1.8,
20
+ near: 0.35,
21
+ far: 3.6,
22
+ });
23
+
24
+ const DEFAULT_PREVIEW = Object.freeze({
25
+ renderer: 'dom-perspective-preview',
26
+ pixelsPerMeter: 118,
27
+ origin: [0.5, 0.5],
28
+ });
29
+
30
+ function numberOr(value, fallback) {
31
+ let number = Number(value);
32
+ return Number.isFinite(number) ? number : fallback;
33
+ }
34
+
35
+ function vectorOr(value, fallback) {
36
+ if (!Array.isArray(value)) return [...fallback];
37
+ return fallback.map((item, index) => numberOr(value[index], item));
38
+ }
39
+
40
+ function vectorLikeOr(value, fallback) {
41
+ if (Array.isArray(value)) return vectorOr(value, fallback);
42
+ if (!value || typeof value !== 'object') return [...fallback];
43
+ return fallback.map((item, index) => {
44
+ let key = index === 0 ? 'x' : index === 1 ? 'y' : 'z';
45
+ return numberOr(value[key], item);
46
+ });
47
+ }
48
+
49
+ function firstVector(...values) {
50
+ for (let value of values) {
51
+ if (Array.isArray(value)) return value;
52
+ if (value && typeof value === 'object' && ['x', 'y', 'z'].some((key) => key in value)) return value;
53
+ }
54
+ return null;
55
+ }
56
+
57
+ function yawDegreesFromQuaternion(value) {
58
+ if (!value || typeof value !== 'object') return null;
59
+ let x = numberOr(value.x, 0);
60
+ let y = numberOr(value.y, 0);
61
+ let z = numberOr(value.z, 0);
62
+ let w = numberOr(value.w, 1);
63
+ let siny = 2 * (w * y + x * z);
64
+ let cosy = 1 - 2 * (y * y + x * x);
65
+ return Math.atan2(siny, cosy) * 180 / Math.PI;
66
+ }
67
+
68
+ function normalizeViewerPose(input = {}) {
69
+ let transform = input.transform || {};
70
+ let position = firstVector(input.position, transform.position, input.translation);
71
+ let rotation = firstVector(input.rotation, transform.rotation);
72
+ let yaw = input.yawDegrees ?? input.yaw ?? transform.yawDegrees ?? transform.yaw ?? yawDegreesFromQuaternion(input.orientation || transform.orientation);
73
+ if (!rotation && yaw != null) {
74
+ rotation = [0, numberOr(yaw, 0), 0];
75
+ }
76
+ return {
77
+ position: position ? vectorLikeOr(position, [0, 0, 0]) : null,
78
+ rotation: rotation ? vectorLikeOr(rotation, [0, 0, 0]) : null,
79
+ };
80
+ }
81
+
82
+ export function createXRViewerPoseSnapshot(viewerPose = {}, options = {}) {
83
+ let normalized = normalizeViewerPose(viewerPose);
84
+ let hasPose = Boolean(normalized.position || normalized.rotation);
85
+ return {
86
+ version: 'xr-viewer-pose-snapshot-v1',
87
+ source: hasPose ? options.source || 'xr-frame-viewer-pose' : 'missing-viewer-pose',
88
+ position: normalized.position,
89
+ rotation: normalized.rotation,
90
+ };
91
+ }
92
+
93
+ export function createXRSceneRootTransform(scene = {}, options = {}) {
94
+ let viewerPose = normalizeViewerPose(options.viewerPose || {});
95
+ let origin = scene.origin || {};
96
+ let hasViewerPose = Boolean(viewerPose.position || viewerPose.rotation);
97
+ let lockFloorY = options.lockFloorY !== false;
98
+ let position = viewerPose.position ||
99
+ vectorOr(options.position || origin.position, [0, 0, 0]);
100
+ let rotation = viewerPose.rotation ||
101
+ vectorOr(options.rotation || origin.rotation, [0, 0, 0]);
102
+
103
+ if (lockFloorY) {
104
+ position = [position[0], 0, position[2]];
105
+ rotation = [0, rotation[1], 0];
106
+ }
107
+
108
+ return {
109
+ version: XR_SCENE_ROOT_TRANSFORM_VERSION,
110
+ policy: options.policy || 'body-space-front',
111
+ mode: options.mode || scene.mode || null,
112
+ referenceSpaceType: options.referenceSpaceType || options.referenceSpace || scene.referenceSpaceType || null,
113
+ originSource: hasViewerPose ? 'viewer-pose' : origin.type ? `scene-origin:${origin.type}` : 'provider-default',
114
+ lockFloorY,
115
+ position,
116
+ rotation,
117
+ };
118
+ }
119
+
120
+ function normalizeUserSpace(input = {}) {
121
+ return {
122
+ eyeHeight: numberOr(input.eyeHeight, DEFAULT_USER_SPACE.eyeHeight),
123
+ comfortRadius: numberOr(input.comfortRadius, DEFAULT_USER_SPACE.comfortRadius),
124
+ near: numberOr(input.near, DEFAULT_USER_SPACE.near),
125
+ far: numberOr(input.far, DEFAULT_USER_SPACE.far),
126
+ };
127
+ }
128
+
129
+ function normalizePreview(input = {}) {
130
+ return {
131
+ renderer: input.renderer || DEFAULT_PREVIEW.renderer,
132
+ pixelsPerMeter: numberOr(input.pixelsPerMeter, DEFAULT_PREVIEW.pixelsPerMeter),
133
+ origin: vectorOr(input.origin, DEFAULT_PREVIEW.origin),
134
+ };
135
+ }
136
+
137
+ export function createXRSpatialScene(root, options = {}) {
138
+ let layout = projectLayoutToXR(root, options);
139
+ let userSpace = normalizeUserSpace(options.userSpace);
140
+ let preview = normalizePreview(options.preview);
141
+
142
+ return {
143
+ version: XR_SPATIAL_SCENE_VERSION,
144
+ unit: 'meter',
145
+ coordinateSystem: options.coordinateSystem || XR_SPATIAL_SPACE.localFloor,
146
+ origin: {
147
+ type: 'viewer',
148
+ position: vectorOr(options.origin?.position, [0, 0, 0]),
149
+ rotation: vectorOr(options.origin?.rotation, [0, 0, 0]),
150
+ },
151
+ userSpace,
152
+ preview,
153
+ layout,
154
+ panels: layout.panels.map((sourcePanel) => {
155
+ let panel = options.adjustComfort === false
156
+ ? sourcePanel
157
+ : adjustXRPanelPoseForComfort(sourcePanel, { userSpace });
158
+ panel = options.adjustFacing === false
159
+ ? panel
160
+ : adjustXRPanelRotationForViewer(panel, { userSpace });
161
+ let previewPixels = {
162
+ width: panel.size[0] * preview.pixelsPerMeter,
163
+ height: panel.size[1] * preview.pixelsPerMeter,
164
+ };
165
+ return {
166
+ ...panel,
167
+ contentViewport: createXRPanelContentViewport(panel, { previewPixels }),
168
+ spatialRole: panel.anchor === 'front' ? 'primary-surface' : 'support-surface',
169
+ distanceFromUser: Math.abs(panel.position[2] || 0),
170
+ };
171
+ }),
172
+ interaction: {
173
+ pointerModel: 'ray-to-panel-normalized',
174
+ eventSpace: 'panel-normalized-0-1',
175
+ supportsMouseFallback: true,
176
+ },
177
+ placement: createXRSceneRootTransform({
178
+ origin: options.origin,
179
+ referenceSpaceType: options.referenceSpaceType,
180
+ }, {
181
+ mode: options.mode,
182
+ referenceSpaceType: options.referenceSpaceType,
183
+ viewerPose: options.viewerPose,
184
+ }),
185
+ themeScope: options.themeScope || layout.themeScope,
186
+ };
187
+ }
188
+
189
+ export function createXRSpatialPreview(panel, scene, options = {}) {
190
+ let pixelsPerMeter = numberOr(options.pixelsPerMeter, scene?.preview?.pixelsPerMeter || DEFAULT_PREVIEW.pixelsPerMeter);
191
+ let depthScale = numberOr(options.depthScale, 1);
192
+ let eyeHeight = numberOr(scene?.userSpace?.eyeHeight, DEFAULT_USER_SPACE.eyeHeight);
193
+ let left = panel.position[0] * pixelsPerMeter;
194
+ let top = (eyeHeight - panel.position[1]) * pixelsPerMeter;
195
+ let depth = panel.position[2] * pixelsPerMeter * depthScale;
196
+
197
+ return {
198
+ panelId: panel.id,
199
+ left,
200
+ top,
201
+ depth,
202
+ width: panel.size[0] * pixelsPerMeter,
203
+ height: panel.size[1] * pixelsPerMeter,
204
+ opacity: panel.opacity,
205
+ transform: [
206
+ `translate3d(calc(-50% + ${left}px), calc(-50% + ${top}px), ${depth}px)`,
207
+ `rotateX(${panel.rotation[0]}deg)`,
208
+ `rotateY(${panel.rotation[1]}deg)`,
209
+ `rotateZ(${panel.rotation[2]}deg)`,
210
+ ].join(' '),
211
+ };
212
+ }
@@ -0,0 +1,105 @@
1
+ const XR_THEME_TOKEN_MAP = Object.freeze({
2
+ panelBackground: '--sn-xr-panel-bg',
3
+ panelBorder: '--sn-xr-panel-border',
4
+ panelRadius: '--sn-xr-panel-radius',
5
+ panelShadow: '--sn-xr-panel-shadow',
6
+ pointerColor: '--sn-xr-pointer-color',
7
+ textColor: '--sn-text',
8
+ mutedTextColor: '--sn-text-dim',
9
+ motionDuration: '--sn-duration-fast',
10
+ motionEasing: '--sn-ease-standard',
11
+ gap: '--sn-layout-resizer-size',
12
+ });
13
+
14
+ const XR_THEME_FALLBACKS = Object.freeze({
15
+ '--sn-xr-panel-bg': 'var(--sn-panel-bg)',
16
+ '--sn-xr-panel-border': 'var(--sn-node-border)',
17
+ '--sn-xr-panel-radius': 'var(--sn-node-radius)',
18
+ '--sn-xr-panel-shadow': 'var(--sn-node-shadow)',
19
+ '--sn-xr-pointer-color': 'var(--sn-node-selected)',
20
+ '--sn-text': 'var(--sn-text)',
21
+ '--sn-text-dim': 'var(--sn-text-dim)',
22
+ '--sn-duration-fast': '120ms',
23
+ '--sn-ease-standard': 'ease',
24
+ '--sn-layout-resizer-size': '6px',
25
+ });
26
+
27
+ function resolveThemeRoot(rootOrDocument) {
28
+ if (!rootOrDocument) return null;
29
+ if (rootOrDocument.documentElement) return rootOrDocument.documentElement;
30
+ return rootOrDocument;
31
+ }
32
+
33
+ function readCssToken(root, cssVar, computedStyle) {
34
+ if (!root || !computedStyle) return XR_THEME_FALLBACKS[cssVar] || '';
35
+ let value = computedStyle.getPropertyValue(cssVar).trim();
36
+ return value || XR_THEME_FALLBACKS[cssVar] || '';
37
+ }
38
+
39
+ function resolveCssProperty(root, cssVar, property) {
40
+ let documentRef = root?.ownerDocument || root?.documentElement?.ownerDocument || null;
41
+ let parent = documentRef?.body || root;
42
+ if (!documentRef?.createElement || !parent?.append || typeof globalThis.getComputedStyle !== 'function') return '';
43
+ let probe = documentRef.createElement('span');
44
+ probe.style.position = 'absolute';
45
+ probe.style.pointerEvents = 'none';
46
+ probe.style.opacity = '0';
47
+ probe.style[property] = `var(${cssVar})`;
48
+ parent.append(probe);
49
+ let value = globalThis.getComputedStyle(probe).getPropertyValue(property).trim();
50
+ probe.remove?.();
51
+ return value;
52
+ }
53
+
54
+ export function createXRThemeSnapshot(rootOrDocument = globalThis.document, options = {}) {
55
+ let root = resolveThemeRoot(rootOrDocument);
56
+ let computedStyle = root && typeof globalThis.getComputedStyle === 'function'
57
+ ? globalThis.getComputedStyle(root)
58
+ : null;
59
+ let tokens = {};
60
+
61
+ for (let cssVar of Object.values(XR_THEME_TOKEN_MAP)) {
62
+ tokens[cssVar] = readCssToken(root, cssVar, computedStyle);
63
+ }
64
+
65
+ return {
66
+ version: 'xr-theme-snapshot-v1',
67
+ themeScope: options.themeScope || root?.dataset?.themeScope || 'default-provider',
68
+ tokens,
69
+ material: {
70
+ background: tokens['--sn-xr-panel-bg'],
71
+ backgroundColor: resolveCssProperty(root, '--sn-xr-panel-bg', 'background-color') || tokens['--sn-xr-panel-bg'],
72
+ border: tokens['--sn-xr-panel-border'],
73
+ borderColor: resolveCssProperty(root, '--sn-xr-panel-border', 'border-color') || tokens['--sn-xr-panel-border'],
74
+ radius: tokens['--sn-xr-panel-radius'],
75
+ shadow: tokens['--sn-xr-panel-shadow'],
76
+ pointer: tokens['--sn-xr-pointer-color'],
77
+ pointerColor: resolveCssProperty(root, '--sn-xr-pointer-color', 'color') || tokens['--sn-xr-pointer-color'],
78
+ text: tokens['--sn-text'],
79
+ textColor: resolveCssProperty(root, '--sn-text', 'color') || tokens['--sn-text'],
80
+ textDim: tokens['--sn-text-dim'],
81
+ textDimColor: resolveCssProperty(root, '--sn-text-dim', 'color') || tokens['--sn-text-dim'],
82
+ gap: tokens['--sn-layout-resizer-size'],
83
+ motion: {
84
+ duration: tokens['--sn-duration-fast'],
85
+ easing: tokens['--sn-ease-standard'],
86
+ },
87
+ },
88
+ };
89
+ }
90
+
91
+ export function applyXRThemeToPanel(panel, themeSnapshot) {
92
+ let snapshot = themeSnapshot || createXRThemeSnapshot(null);
93
+ return {
94
+ ...panel,
95
+ themeScope: panel.themeScope || snapshot.themeScope,
96
+ material: {
97
+ ...snapshot.material,
98
+ ...(panel.material || {}),
99
+ },
100
+ };
101
+ }
102
+
103
+ export function getXRThemeTokenMap() {
104
+ return { ...XR_THEME_TOKEN_MAP };
105
+ }