@webspatial/platform-visionos 1.2.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/package.json +2 -1
  2. package/web-spatial/EventEmitter.swift +11 -11
  3. package/web-spatial/JSBCommand.swift +38 -3
  4. package/web-spatial/WebMsgCommand.swift +5 -16
  5. package/web-spatial/WebSpatialApp.swift +10 -10
  6. package/web-spatial/Window.swift +2 -2
  7. package/web-spatial/manager/AttachmentManager.swift +84 -0
  8. package/web-spatial/manager/Dynamic3DManager.swift +10 -0
  9. package/web-spatial/manager/JSBManager.swift +1 -2
  10. package/web-spatial/manager/WKWebViewManager.swift +4 -4
  11. package/web-spatial/manifest.swift +11 -6
  12. package/web-spatial/model/SpatialApp.swift +60 -56
  13. package/web-spatial/model/SpatialScene.swift +233 -16
  14. package/web-spatial/model/Spatialized2DElement.swift +4 -5
  15. package/web-spatial/model/SpatializedDynamic3DElement.swift +12 -0
  16. package/web-spatial/model/SpatializedElement.swift +40 -0
  17. package/web-spatial/model/SpatializedStatic3DElement.swift +1 -1
  18. package/web-spatial/model/dynamic3d/SpatialComponent.swift +27 -27
  19. package/web-spatial/model/dynamic3d/SpatialEntity.swift +8 -2
  20. package/web-spatial/model/dynamic3d/SpatialMaterial.swift +15 -15
  21. package/web-spatial/model/dynamic3d/SpatialModelEntity.swift +10 -10
  22. package/web-spatial/model/dynamic3d/SpatialModelResource.swift +1 -1
  23. package/web-spatial/model/dynamic3d/SpatialTextureResource.swift +8 -8
  24. package/web-spatial/view/SceneHandlerUIView.swift +29 -1
  25. package/web-spatial/view/SpatialNavView.swift +52 -47
  26. package/web-spatial/view/SpatializedDynamic3DView.swift +88 -5
  27. package/web-spatial/view/SpatializedElementView.swift +85 -47
  28. package/web-spatial/view/SpatializedStatic3DView.swift +9 -7
  29. package/web-spatial/view/view-modifier/HideViewModifier.swift +2 -2
  30. package/web-spatial/webview/SpatialWebController.swift +42 -25
  31. package/web-spatial/webview/SpatialWebView.swift +5 -1
  32. package/web-spatial/webview/SpatialWebViewModel.swift +13 -7
  33. package/web-spatial.xcodeproj/project.pbxproj +13 -0
  34. package/web-spatialTests/NavigationCleanupTests.swift +33 -0
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@webspatial/platform-visionos",
3
- "version": "1.2.1",
3
+ "version": "1.4.0",
4
4
  "description": "Used to publish WebSpatial projects to Apple Vision Pro",
5
5
  "type": "commonjs",
6
+ "main": "package.json",
6
7
  "engines": {
7
8
  "node": ">=14.15.0"
8
9
  },
@@ -1,38 +1,38 @@
1
1
  class EventEmitter {
2
2
  private var listeners: [String: [(_ object: Any, _ data: Any) -> Void]] = [:]
3
3
 
4
- public func on(event: String, listener: @escaping (_ object: Any, _ data: Any) -> Void) {
4
+ func on(event: String, listener: @escaping (_ object: Any, _ data: Any) -> Void) {
5
5
  if listeners[event] == nil {
6
6
  listeners[event] = []
7
7
  }
8
8
  listeners[event]?.append(listener)
9
9
  }
10
10
 
11
- public func emit(event: String, data: Any) {
11
+ func emit(event: String, data: Any) {
12
12
  listeners[event]?.forEach { listener in
13
13
  listener(self, data)
14
14
  }
15
15
  }
16
16
 
17
- public func off(event: String, listener: @escaping (_ object: Any, _ data: Any) -> Void) {
17
+ func off(event: String, listener: @escaping (_ object: Any, _ data: Any) -> Void) {
18
18
  listeners[event]?.removeAll(where: { $0 as AnyObject === listener as AnyObject })
19
19
  }
20
-
21
- public func reset(){
20
+
21
+ func reset() {
22
22
  listeners = [:]
23
23
  }
24
24
  }
25
25
 
26
- protocol EventEmitterProtocol{
27
- var listeners: [String: [(_ object: Any, _ data: Any) -> Void]] {get set}
28
-
26
+ protocol EventEmitterProtocol {
27
+ var listeners: [String: [(_ object: Any, _ data: Any) -> Void]] { get set }
28
+
29
29
  mutating func on(event: String, listener: @escaping (_ object: Any, _ data: Any) -> Void)
30
30
  func emit(event: String, data: Any)
31
31
  mutating func off(event: String, listener: @escaping (_ object: Any, _ data: Any) -> Void)
32
32
  mutating func reset()
33
33
  }
34
34
 
35
- extension EventEmitterProtocol{
35
+ extension EventEmitterProtocol {
36
36
  mutating func on(event: String, listener: @escaping (_ object: Any, _ data: Any) -> Void) {
37
37
  if listeners[event] == nil {
38
38
  listeners[event] = []
@@ -49,8 +49,8 @@ extension EventEmitterProtocol{
49
49
  mutating func off(event: String, listener: @escaping (_ object: Any, _ data: Any) -> Void) {
50
50
  listeners[event]?.removeAll(where: { $0 as AnyObject === listener as AnyObject })
51
51
  }
52
-
53
- mutating func reset(){
52
+
53
+ mutating func reset() {
54
54
  listeners = [:]
55
55
  }
56
56
  }
@@ -129,6 +129,13 @@ struct ConvertFromSceneToEntity: CommandDataProtocol {
129
129
  let position: Vec3
130
130
  }
131
131
 
132
+ struct ConvertCoordinate: CommandDataProtocol {
133
+ static let commandType: String = "ConvertCoordinate"
134
+ let position: Vec3
135
+ let fromId: String
136
+ let toId: String
137
+ }
138
+
132
139
  struct InspectCommand: CommandDataProtocol {
133
140
  static let commandType: String = "Inspect"
134
141
  var id: String?
@@ -166,6 +173,7 @@ protocol SpatializedElementProperties: SpatialObjectCommand {
166
173
  var enableMagnifyGesture: Bool? { get }
167
174
  var enableMagnifyEndGesture: Bool? { get }
168
175
  var enableTapGesture: Bool? { get }
176
+ var rotateConstrainedToAxis: Vec3? { get }
169
177
  }
170
178
 
171
179
  struct UpdateSpatialized2DElementProperties: SpatializedElementProperties {
@@ -193,12 +201,14 @@ struct UpdateSpatialized2DElementProperties: SpatializedElementProperties {
193
201
  var enableMagnifyEndGesture: Bool?
194
202
  var enableTapGesture: Bool?
195
203
 
204
+ let rotateConstrainedToAxis: Vec3?
205
+
196
206
  let scrollPageEnabled: Bool?
197
207
  let material: BackgroundMaterial?
198
208
  let cornerRadius: CornerRadius?
199
209
 
200
- // this value is used by previous WebSpatial code, keep it here only for Compatibility consideration
201
- // may delete it when we think it's not needed
210
+ /// this value is used by previous WebSpatial code, keep it here only for Compatibility consideration
211
+ /// may delete it when we think it's not needed
202
212
  let scrollEdgeInsetsMarginRight: Double?
203
213
  }
204
214
 
@@ -227,6 +237,8 @@ struct UpdateSpatializedStatic3DElementProperties: SpatializedElementProperties
227
237
  let enableMagnifyEndGesture: Bool?
228
238
  let enableTapGesture: Bool?
229
239
 
240
+ let rotateConstrainedToAxis: Vec3?
241
+
230
242
  let modelURL: String?
231
243
  let modelTransform: [Double]?
232
244
  }
@@ -255,6 +267,8 @@ struct UpdateSpatializedDynamic3DElementProperties: SpatializedElementProperties
255
267
  let enableMagnifyGesture: Bool?
256
268
  let enableMagnifyEndGesture: Bool?
257
269
  let enableTapGesture: Bool?
270
+
271
+ let rotateConstrainedToAxis: Vec3?
258
272
  }
259
273
 
260
274
  struct UpdateSpatializedElementTransform: SpatialObjectCommand {
@@ -269,7 +283,7 @@ struct AddSpatializedElementToSpatialized2DElement: SpatialObjectCommand {
269
283
  let spatializedElementId: String
270
284
  }
271
285
 
272
- // incomming JSB data
286
+ /// incomming JSB data
273
287
  struct XSceneOptionsJSB: Codable {
274
288
  let defaultSize: Size?
275
289
  let type: SpatialScene.WindowStyle?
@@ -338,3 +352,24 @@ struct FocusSceneCommand: CommandDataProtocol {
338
352
  struct GetSpatialSceneStateCommand: CommandDataProtocol {
339
353
  static let commandType = "GetSpatialSceneState"
340
354
  }
355
+
356
+ struct InitializeAttachmentCommand: CommandDataProtocol {
357
+ static let commandType = "InitializeAttachment"
358
+ let id: String
359
+ let parentEntityId: String
360
+ let position: [Float]?
361
+ let size: AttachmentSize?
362
+ let ownerViewId: String
363
+ }
364
+
365
+ struct UpdateAttachmentEntityCommand: CommandDataProtocol {
366
+ static let commandType = "UpdateAttachmentEntity"
367
+ let id: String
368
+ let position: [Float]?
369
+ let size: AttachmentSize?
370
+ }
371
+
372
+ struct AttachmentSize: Codable {
373
+ let width: Double
374
+ let height: Double
375
+ }
@@ -14,8 +14,6 @@ enum WebSpatialGestureType: String, Encodable {
14
14
  }
15
15
 
16
16
  enum SpatialWebMsgType: String, Encodable {
17
- case cubeInfo
18
- case transform
19
17
  case modelloaded
20
18
  case modelloadfailed
21
19
  case spatialtap
@@ -30,24 +28,13 @@ enum SpatialWebMsgType: String, Encodable {
30
28
  case objectdestroy
31
29
  }
32
30
 
33
- // notify Spatialized3DElement Container Cube, used for ref.current.getBoundingClientCube()
34
- struct SpatiaizedContainerClientCube: Encodable {
35
- let type: SpatialWebMsgType = .cubeInfo
36
- let origin: Point3D
37
- let size: Size3D
38
- }
39
-
40
- // notify Spatialized3DElement Container Transform to SpatialScene, used for ref.current.convertToSpatialScene()
41
- struct SpatiaizedContainerTransform: Encodable {
42
- let type: SpatialWebMsgType = .transform
43
- let detail: AffineTransform3D
44
- }
45
-
46
31
  struct WebSpatialTapGuestureEventDetail: Encodable {
47
32
  let location3D: Point3D
33
+ /// Global scene location (maps to clientX/clientY/clientZ on the web side).
34
+ let globalLocation3D: Point3D?
48
35
  }
49
36
 
50
- // notify SpatializedElement/SpatialEntity tapped
37
+ /// notify SpatializedElement/SpatialEntity tapped
51
38
  struct WebSpatialTapGuestureEvent: Encodable {
52
39
  let type: SpatialWebMsgType = .spatialtap
53
40
  let detail: WebSpatialTapGuestureEventDetail
@@ -55,6 +42,8 @@ struct WebSpatialTapGuestureEvent: Encodable {
55
42
 
56
43
  struct WebSpatialDragStartGuestureEventDetail: Encodable {
57
44
  let startLocation3D: Point3D
45
+ /// Global scene location for the drag start point.
46
+ let globalLocation3D: Point3D?
58
47
  }
59
48
 
60
49
  struct WebSpatialDragStartGuestureEvent: Encodable {
@@ -22,26 +22,22 @@ struct WebSpatialApp: App {
22
22
  @State var app = SpatialApp.Instance
23
23
 
24
24
  func getDefaultSize() -> CGSize {
25
- let ans = CGSize(
25
+ return CGSize(
26
26
  width: app
27
27
  .getSceneOptions().defaultSize!.width,
28
28
  height: app
29
29
  .getSceneOptions().defaultSize!.height
30
30
  )
31
-
32
- return ans
33
31
  }
34
32
 
35
33
  func getDefaultSize3D() -> Size3D {
36
- let ans = Size3D(
34
+ return Size3D(
37
35
  width: app
38
36
  .getSceneOptions().defaultSize!.width,
39
37
  height: app
40
38
  .getSceneOptions().defaultSize!.height,
41
39
  depth: app.getSceneOptions().defaultSize!.depth ?? 0
42
40
  )
43
-
44
- return ans
45
41
  }
46
42
 
47
43
  var body: some Scene {
@@ -74,13 +70,17 @@ struct WebSpatialApp: App {
74
70
  SpatialSceneView(spatialScene: spatialScene!)
75
71
  .frame(
76
72
  minWidth: getCGFloat(
77
- app.getSceneOptions(windowData)?.resizeRange?.minWidth),
73
+ app.getSceneOptions(windowData)?.resizeRange?.minWidth
74
+ ),
78
75
  maxWidth: getCGFloat(
79
- app.getSceneOptions(windowData)?.resizeRange?.maxWidth),
76
+ app.getSceneOptions(windowData)?.resizeRange?.maxWidth
77
+ ),
80
78
  minHeight: getCGFloat(
81
- app.getSceneOptions(windowData)?.resizeRange?.minHeight),
79
+ app.getSceneOptions(windowData)?.resizeRange?.minHeight
80
+ ),
82
81
  maxHeight: getCGFloat(
83
- app.getSceneOptions(windowData)?.resizeRange?.maxHeight)
82
+ app.getSceneOptions(windowData)?.resizeRange?.maxHeight
83
+ )
84
84
  )
85
85
  }
86
86
  defaultValue: {
@@ -1,7 +1,7 @@
1
1
  import Foundation
2
2
  import SwiftUI
3
3
 
4
- // Access window (https://stackoverflow.com/questions/60359808/how-to-access-own-window-within-swiftui-view/60359809#60359809)
4
+ /// Access window (https://stackoverflow.com/questions/60359808/how-to-access-own-window-within-swiftui-view/60359809#60359809)
5
5
  class SceneDelegate: NSObject, ObservableObject, UIWindowSceneDelegate {
6
6
  var window: UIWindow? // << contract of `UIWindowSceneDelegate`
7
7
 
@@ -11,7 +11,7 @@ class SceneDelegate: NSObject, ObservableObject, UIWindowSceneDelegate {
11
11
  window?.overrideUserInterfaceStyle = .light
12
12
  }
13
13
 
14
- // do memory cleanup after scene removed, otherwise windowContainer cannot destroy content after being dismissed
14
+ /// do memory cleanup after scene removed, otherwise windowContainer cannot destroy content after being dismissed
15
15
  func sceneDidDisconnect(_ scene: UIScene) {
16
16
  window = nil
17
17
  }
@@ -0,0 +1,84 @@
1
+ import Foundation
2
+ import SwiftUI
3
+
4
+ struct AttachmentInfo: Identifiable, Equatable {
5
+ let id: String
6
+ var parentEntityId: String
7
+ var position: SIMD3<Float>
8
+ var size: CGSize
9
+ var webViewModel: SpatialWebViewModel
10
+
11
+ static func == (lhs: AttachmentInfo, rhs: AttachmentInfo) -> Bool {
12
+ lhs.id == rhs.id
13
+ }
14
+ }
15
+
16
+ @Observable
17
+ class AttachmentManager {
18
+ var attachments: [String: AttachmentInfo] = [:]
19
+
20
+ // TODO: AttachmentManager.remove() dispatches destroy() asynchronously while
21
+ // SwiftUI tears down the outgoing SpatialWebView from the RealityView's
22
+ // ForEach. Both happen on the main queue but ordering isn't guaranteed — if
23
+ // destroy() nils the controller before SwiftUI finishes teardown,
24
+ // getController() re-creates it with only `model` set, missing the four
25
+ // callback registrations from init(url:). Refactor to give attachments a
26
+ // dedicated view path (e.g. AttachmentWebView) that doesn't depend on
27
+ // SpatialWebViewModel's lazy re-init.
28
+ func create(
29
+ id: String,
30
+ parentEntityId: String,
31
+ position: SIMD3<Float>,
32
+ size: CGSize,
33
+ webViewModel: SpatialWebViewModel
34
+ ) -> AttachmentInfo {
35
+ webViewModel.setBackgroundTransparent(true)
36
+ // webViewModel.scrollEnabled = false
37
+
38
+ let info = AttachmentInfo(
39
+ id: id,
40
+ parentEntityId: parentEntityId,
41
+ position: position,
42
+ size: size,
43
+ webViewModel: webViewModel
44
+ )
45
+ attachments[id] = info
46
+ return info
47
+ }
48
+
49
+ func update(id: String, position: SIMD3<Float>?, size: CGSize?) {
50
+ guard var info = attachments[id] else { return }
51
+
52
+ if let position = position {
53
+ info.position = position
54
+ }
55
+ if let size = size {
56
+ info.size = size
57
+ }
58
+
59
+ attachments[id] = info
60
+ }
61
+
62
+ func remove(id: String) {
63
+ if let info = attachments.removeValue(forKey: id) {
64
+ DispatchQueue.main.async {
65
+ info.webViewModel.destroy()
66
+ }
67
+ }
68
+ }
69
+
70
+ func get(id: String) -> AttachmentInfo? {
71
+ attachments[id]
72
+ }
73
+
74
+ func destroyAll() {
75
+ let toDestroy = Array(attachments.values)
76
+ attachments.removeAll()
77
+
78
+ DispatchQueue.main.async {
79
+ for info in toDestroy {
80
+ info.webViewModel.destroy()
81
+ }
82
+ }
83
+ }
84
+ }
@@ -71,6 +71,16 @@ class Dynamic3DManager {
71
71
  }
72
72
 
73
73
  static func loadResourceToLocal(_ urlString: String, loadComplete: @escaping (Result<URL, Error>) -> Void) {
74
+ // load local file
75
+ if urlString.starts(with: "file://") {
76
+ guard let localUrl = URL(string: pwaManager.getLocalResourceURL(url: urlString)) else {
77
+ loadComplete(.failure(NSError(domain: "Download Error", code: 0, userInfo: [NSLocalizedDescriptionKey: "Local file is not found"])))
78
+ return
79
+ }
80
+ loadComplete(.success(localUrl))
81
+ return
82
+ }
83
+ // load net file
74
84
  guard let url = URL(string: urlString) else {
75
85
  loadComplete(.failure(NSError(domain: "Invalid URL", code: 0, userInfo: [NSLocalizedDescriptionKey: "Failed to create URL from string: \(urlString)"])))
76
86
  return
@@ -123,8 +123,7 @@ class JSBManager {
123
123
  if cmdContent == nil {
124
124
  return nil
125
125
  }
126
- let concreteData = try decoder.decode(type.self, from: cmdContent!.data(using: .utf8)!)
127
- return concreteData
126
+ return try decoder.decode(type.self, from: cmdContent!.data(using: .utf8)!)
128
127
  }
129
128
 
130
129
  private func typeof(for key: String) -> CommandDataProtocol.Type? {
@@ -8,8 +8,8 @@ class WKWebViewManager {
8
8
 
9
9
  func create(controller: SpatialWebController, configuration: WKWebViewConfiguration? = nil, spatialId: String? = "") -> WKWebView {
10
10
  let userContentController = WKUserContentController()
11
- // TODO: get native api instead of PACKAGE_VERSION
12
- let userScript = WKUserScript(source: "window.WebSpatailEnabled = true; window.WebSpatailNativeVersion = 'PACKAGE_VERSION';", injectionTime: .atDocumentStart, forMainFrameOnly: false)
11
+ // TODO: get native api instead of using the injected WS_SDK_VERSION placeholder
12
+ let userScript = WKUserScript(source: "window.WebSpatailEnabled = true; window.WebSpatailNativeVersion = 'WS_SDK_VERSION';", injectionTime: .atDocumentStart, forMainFrameOnly: false)
13
13
  userContentController.addUserScript(userScript)
14
14
  // userContentController.add(controller, name: "bridge")
15
15
  userContentController.addScriptMessageHandler(controller, contentWorld: .page, name: "bridge")
@@ -25,8 +25,8 @@ class WKWebViewManager {
25
25
  // change webview ua
26
26
  let ua = controller.webview!.value(forKey: "userAgent") as? String ?? ""
27
27
  let webviewVersion = ua.split(separator: configUA)[0].split(separator: "AppleWebKit")[1]
28
- // TODO: get native api instead of PACKAGE_VERSION
29
- controller.webview!.customUserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7; wv) AppleWebKit\(webviewVersion)WebSpatial/\("PACKAGE_VERSION") SpatialID/\(spatialId!)"
28
+ // TODO: get native api instead of relying on injected shell/sdk versions
29
+ controller.webview!.customUserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7; wv) AppleWebKit\(webviewVersion)WSAppShell/\(pwaManager.getShellVersion()) WebSpatial/\(pwaManager.getSdkVersion()) SpatialID/\(spatialId!)"
30
30
  controller.webview!.uiDelegate = controller
31
31
  controller.webview!.allowsBackForwardNavigationGestures = false
32
32
  controller.webview!.isInspectable = true
@@ -6,9 +6,9 @@ var pwaManager = PWAManager()
6
6
  struct PWAManager: Codable {
7
7
  var isLocal: Bool = false
8
8
 
9
- var start_url: String = "http://localhost:5173"
9
+ var start_url: String = "http://localhost:5173/#/geometry-verify"
10
10
 
11
- // var start_url: String = "http://localhost:5173/webspatial/avp/materialApiTest"
11
+ // var start_url: String = "http://localhost:5173/#/spatial-drag-gesture"
12
12
 
13
13
  var scope: String = ""
14
14
  var id: String = "com.webspatial.pico"
@@ -38,7 +38,8 @@ struct PWAManager: Codable {
38
38
  baseplateVisibility: nil
39
39
  )
40
40
  var useMainScene: Bool = true
41
- private var version: String = "PACKAGE_VERSION"
41
+ private var shellVersion: String = "WS_SHELL_VERSION"
42
+ private var sdkVersion: String = "WS_SDK_VERSION"
42
43
 
43
44
  mutating func _init() {
44
45
  let urlType = start_url.split(separator: "://").first
@@ -66,7 +67,7 @@ struct PWAManager: Codable {
66
67
  return url.starts(with: scope)
67
68
  }
68
69
 
69
- // web+spatial://test
70
+ /// web+spatial://test
70
71
  func checkInDeeplink(url: String) -> String {
71
72
  var linkUrl: String = url
72
73
  for item in protocol_handlers {
@@ -102,8 +103,12 @@ struct PWAManager: Codable {
102
103
  return resource
103
104
  }
104
105
 
105
- func getVersion() -> String {
106
- return version
106
+ func getShellVersion() -> String {
107
+ return shellVersion
108
+ }
109
+
110
+ func getSdkVersion() -> String {
111
+ return sdkVersion
107
112
  }
108
113
  }
109
114
 
@@ -3,10 +3,10 @@ import SwiftUI
3
3
 
4
4
  let logger = Logger()
5
5
 
6
- // To load a local path, remove http:// eg. "static-web/"
7
- let nativeAPIVersion = pwaManager.getVersion()
6
+ /// To load a local path, remove http:// eg. "static-web/"
7
+ let nativeAPIVersion = pwaManager.getShellVersion()
8
8
 
9
- // start URL
9
+ /// start URL
10
10
  let startURL = pwaManager.start_url
11
11
 
12
12
  let DefaultPlainWindowContainerSize = CGSize(width: 1280, height: 720)
@@ -37,7 +37,6 @@ struct Size: Codable {
37
37
  var depth: Double?
38
38
  }
39
39
 
40
-
41
40
  extension SceneOptions {
42
41
  init(_ options: XSceneOptionsJSB) {
43
42
  defaultSize = Size(
@@ -47,7 +46,7 @@ extension SceneOptions {
47
46
  )
48
47
  windowResizability = decodeWindowResizability(nil)
49
48
  resizeRange = options.resizability
50
- /// volume only
49
+ // volume only
51
50
  worldScaling = options.worldScaling?.toSDK ?? .automatic
52
51
  worldAlignment = options.worldAlignment?.toSDK ?? .automatic
53
52
  baseplateVisibility = options.baseplateVisibility?.toSDK ?? .automatic
@@ -56,31 +55,44 @@ extension SceneOptions {
56
55
 
57
56
  func decodeWindowResizability(_ windowResizability: String?) -> WindowResizability {
58
57
  switch windowResizability {
59
- case "automatic":
60
- return .automatic
61
- case "contentSize":
62
- return .contentSize
63
- case "contentMinSize":
64
- return .contentMinSize
65
- default:
66
- return .automatic
58
+ case "automatic":
59
+ return .automatic
60
+ case "contentSize":
61
+ return .contentSize
62
+ case "contentMinSize":
63
+ return .contentMinSize
64
+ default:
65
+ return .automatic
67
66
  }
68
67
  }
69
68
 
70
69
  @Observable
71
70
  class SpatialApp {
72
71
  private var scenes = [String: SpatialScene]()
73
-
74
- // delegate properties to pwaManager
75
- var name: String { pwaManager.name }
76
- var scope: String { pwaManager.scope }
77
- var displayMode: PWADisplayMode { pwaManager.display }
78
- var version: String { pwaManager.getVersion() }
79
- var startURL: String { pwaManager.start_url }
80
-
81
- // used to cache scene config
82
- private var sceneOptions: SceneOptions
83
72
 
73
+ /// delegate properties to pwaManager
74
+ var name: String {
75
+ pwaManager.name
76
+ }
77
+
78
+ var scope: String {
79
+ pwaManager.scope
80
+ }
81
+
82
+ var displayMode: PWADisplayMode {
83
+ pwaManager.display
84
+ }
85
+
86
+ var version: String {
87
+ pwaManager.getShellVersion()
88
+ }
89
+
90
+ var startURL: String {
91
+ pwaManager.start_url
92
+ }
93
+
94
+ /// used to cache scene config
95
+ private var sceneOptions: SceneOptions
84
96
 
85
97
  static let Instance: SpatialApp = .init()
86
98
 
@@ -90,14 +102,12 @@ class SpatialApp {
90
102
 
91
103
  Logger.initLogger()
92
104
 
93
- sceneOptions = SceneOptions(pwaManager.mainScene);
94
-
95
- print("plainSceneOptions",sceneOptions)
96
-
105
+ sceneOptions = SceneOptions(pwaManager.mainScene)
106
+
107
+ print("plainSceneOptions", sceneOptions)
108
+
97
109
  logger.debug("WebSpatial App Started -------- rootURL: " + startURL)
98
110
  }
99
-
100
-
101
111
 
102
112
  func createScene(_ url: String, _ style: SpatialScene.WindowStyle, _ state: SpatialScene.SceneStateKind, _ sceneOptions: SceneOptions? = nil) -> SpatialScene {
103
113
  var scene = SpatialScene(url, style, state, sceneOptions)
@@ -105,15 +115,14 @@ class SpatialApp {
105
115
  scene
106
116
  .on(event: SpatialObject.Events.Destroyed.rawValue, listener: onSceneDestroyed)
107
117
 
108
-
109
118
  return scene
110
119
  }
111
-
120
+
112
121
  private func onSceneDestroyed(_ object: Any, _ data: Any) {
113
122
  var spatialObject = object as! SpatialObject
114
123
  spatialObject
115
124
  .off(event: SpatialObject.Events.Destroyed.rawValue, listener: onSceneDestroyed)
116
-
125
+
117
126
  scenes.removeValue(forKey: spatialObject.id)
118
127
  }
119
128
 
@@ -121,63 +130,58 @@ class SpatialApp {
121
130
  return scenes[id]
122
131
  }
123
132
 
124
-
125
133
  func getSceneOptions() -> SceneOptions {
126
134
  return sceneOptions
127
135
  }
128
-
129
- func getSceneOptions(_ sceneId:String) -> SceneOptions? {
136
+
137
+ func getSceneOptions(_ sceneId: String) -> SceneOptions? {
130
138
  let spatialScene = getScene(sceneId)
131
139
  return spatialScene?.sceneConfig
132
140
  }
133
-
134
-
135
- // used form window.open logic
136
- public func openWindowGroup(
141
+
142
+ /// used form window.open logic
143
+ func openWindowGroup(
137
144
  _ targetSpatialScene: SpatialScene,
138
145
  _ sceneData: SceneOptions
139
146
  ) {
140
147
  if let activeScene = firstActiveScene {
141
148
  // cache scene config
142
149
  sceneOptions = sceneData
143
-
144
- DispatchQueue.main.async() {
150
+
151
+ DispatchQueue.main.async {
145
152
  activeScene.openWindowData.send(targetSpatialScene.id)
146
153
  }
147
-
148
154
  }
149
155
  }
150
-
151
- public func closeWindowGroup(_ targetSpatialScene: SpatialScene) {
156
+
157
+ func closeWindowGroup(_ targetSpatialScene: SpatialScene) {
152
158
  if let activeScene = firstActiveScene {
153
159
  activeScene.closeWindowData
154
160
  .send(targetSpatialScene.id)
155
161
  }
156
162
  }
157
-
158
- // used form window.open logic with loading ui
159
- public func openLoadingUI(_ targetSpatialScene: SpatialScene,_ open: Bool) {
163
+
164
+ /// used form window.open logic with loading ui
165
+ func openLoadingUI(_ targetSpatialScene: SpatialScene, _ open: Bool) {
160
166
  let lwgdata = XLoadingViewData(
161
167
  sceneID: targetSpatialScene.id,
162
168
  method: open ? .show : .hide,
163
169
  windowStyle: nil
164
170
  )
165
-
171
+
166
172
  if let activeScene = firstActiveScene {
167
173
  activeScene.setLoadingWindowData.send(lwgdata)
168
174
  }
169
175
  }
170
-
176
+
171
177
  private var firstActiveScene: SpatialScene? {
172
- get {
173
- let activeKV = scenes.first() { kv in
174
- kv.value.state == .visible
175
- }
176
- return (activeKV?.value)
178
+ let activeKV = scenes.first { kv in
179
+ kv.value.state == .visible
177
180
  }
181
+ return (activeKV?.value)
178
182
  }
179
-
180
- public func focusScene(_ targetSpatialScene: SpatialScene) {
183
+
184
+ func focusScene(_ targetSpatialScene: SpatialScene) {
181
185
  // only work when fully visible
182
186
  if targetSpatialScene.state != .visible {
183
187
  return