@webspatial/platform-visionos 1.2.0 → 1.3.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 (29) hide show
  1. package/package.json +1 -1
  2. package/web-spatial/EventEmitter.swift +11 -11
  3. package/web-spatial/JSBCommand.swift +15 -3
  4. package/web-spatial/WebMsgCommand.swift +7 -3
  5. package/web-spatial/WebSpatialApp.swift +10 -10
  6. package/web-spatial/Window.swift +2 -2
  7. package/web-spatial/manager/AttachmentManager.swift +81 -0
  8. package/web-spatial/manager/JSBManager.swift +1 -2
  9. package/web-spatial/manifest.swift +1 -1
  10. package/web-spatial/model/SpatialApp.swift +59 -55
  11. package/web-spatial/model/SpatialScene.swift +97 -14
  12. package/web-spatial/model/Spatialized2DElement.swift +4 -5
  13. package/web-spatial/model/SpatializedStatic3DElement.swift +1 -1
  14. package/web-spatial/model/dynamic3d/SpatialComponent.swift +27 -27
  15. package/web-spatial/model/dynamic3d/SpatialEntity.swift +2 -2
  16. package/web-spatial/model/dynamic3d/SpatialMaterial.swift +15 -15
  17. package/web-spatial/model/dynamic3d/SpatialModelEntity.swift +10 -10
  18. package/web-spatial/model/dynamic3d/SpatialModelResource.swift +1 -1
  19. package/web-spatial/model/dynamic3d/SpatialTextureResource.swift +8 -8
  20. package/web-spatial/view/SpatialNavView.swift +52 -47
  21. package/web-spatial/view/SpatializedDynamic3DView.swift +68 -4
  22. package/web-spatial/view/SpatializedElementView.swift +28 -13
  23. package/web-spatial/view/SpatializedStatic3DView.swift +4 -6
  24. package/web-spatial/view/view-modifier/HideViewModifier.swift +2 -2
  25. package/web-spatial/webview/SpatialWebController.swift +27 -24
  26. package/web-spatial/webview/SpatialWebView.swift +5 -1
  27. package/web-spatial/webview/SpatialWebViewModel.swift +13 -7
  28. package/web-spatial.xcodeproj/project.pbxproj +13 -0
  29. package/web-spatialTests/NavigationCleanupTests.swift +33 -0
@@ -76,6 +76,12 @@ class SpatialWebViewModel {
76
76
  }
77
77
 
78
78
  func getController() -> SpatialWebController {
79
+ // See TODO in AttachmentManager — async destroy() during SwiftUI teardown
80
+ // can nil the controller while the view is still being removed.
81
+ if controller == nil {
82
+ controller = SpatialWebController()
83
+ controller!.model = self
84
+ }
79
85
  return controller!
80
86
  }
81
87
 
@@ -103,8 +109,8 @@ class SpatialWebViewModel {
103
109
  controller?.setWebViewTitle(title)
104
110
  }
105
111
 
106
- // events
107
- // navigation event
112
+ /// events
113
+ /// navigation event
108
114
  func addNavigationListener(protocal: String, event: @escaping (_ data: URL) -> Bool) {
109
115
  navigationList[protocal] = event
110
116
  }
@@ -113,7 +119,7 @@ class SpatialWebViewModel {
113
119
  navigationList = [:]
114
120
  }
115
121
 
116
- // open window event
122
+ /// open window event
117
123
  func addOpenWindowListener(protocal: String, _ event: @escaping (_ data: URL) -> WebViewElementInfo?) {
118
124
  openWindowList[protocal] = event
119
125
  }
@@ -122,7 +128,7 @@ class SpatialWebViewModel {
122
128
  openWindowList = [:]
123
129
  }
124
130
 
125
- // jsb event
131
+ /// jsb event
126
132
  func addJSBListener<T: CommandDataProtocol>(_ dataClass: T.Type, _ event: @escaping (T, @escaping JSBManager.ResolveHandler<Encodable>) -> Void) {
127
133
  controller?.registeJSBHandler(dataClass, event)
128
134
  }
@@ -143,7 +149,7 @@ class SpatialWebViewModel {
143
149
  controller?.mockJSB(command)
144
150
  }
145
151
 
146
- // webview state event
152
+ /// webview state event
147
153
  func addStateListener(_ event: @escaping (_ type: SpatialWebViewState) -> Void) {
148
154
  stateChangeListeners.append(event)
149
155
  }
@@ -176,7 +182,7 @@ class SpatialWebViewModel {
176
182
  stateListeners[state] = nil
177
183
  }
178
184
 
179
- // scroll update event
185
+ /// scroll update event
180
186
  func addScrollUpdateListener(_ event: @escaping (_ type: ScrollState, _ point: CGPoint) -> Void) {
181
187
  scrollUpdateListeners.append(event)
182
188
  }
@@ -199,7 +205,7 @@ class SpatialWebViewModel {
199
205
  removeAllScrollUpdateListener()
200
206
  }
201
207
 
202
- // invokes
208
+ /// invokes
203
209
  private func onNavigationInvoke(_ url: URL) -> Bool {
204
210
  var matchProtocol = false
205
211
  for key in navigationList.keys {
@@ -13,6 +13,7 @@
13
13
  2B06AEE42E4C1AE8000327E9 /* manifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B06AEC12E4C1AE8000327E9 /* manifest.swift */; };
14
14
  2B06AEE62E4C1AE8000327E9 /* JSBCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B06AEC02E4C1AE8000327E9 /* JSBCommand.swift */; };
15
15
  2B06AEE92E4C1AE8000327E9 /* EventEmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B06AEBF2E4C1AE8000327E9 /* EventEmitter.swift */; };
16
+ 2B1008A22F0B000800000002 /* NavigationCleanupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B1008A12F0B000800000001 /* NavigationCleanupTests.swift */; };
16
17
  2B2F1D692BEBFAAA006897EE /* RealityKitContent in Frameworks */ = {isa = PBXBuildFile; productRef = 2B2F1D682BEBFAAA006897EE /* RealityKitContent */; };
17
18
  2B2F1D712BEBFAAC006897EE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2B2F1D702BEBFAAC006897EE /* Assets.xcassets */; };
18
19
  2B2F1D742BEBFAAC006897EE /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2B2F1D732BEBFAAC006897EE /* Preview Assets.xcassets */; };
@@ -37,6 +38,7 @@
37
38
  2B06AEC22E4C1AE8000327E9 /* SpatialObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpatialObject.swift; sourceTree = "<group>"; };
38
39
  2B06AEC32E4C1AE8000327E9 /* WebSpatialApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSpatialApp.swift; sourceTree = "<group>"; };
39
40
  2B06AEC42E4C1AE8000327E9 /* Window.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Window.swift; sourceTree = "<group>"; };
41
+ 2B1008A12F0B000800000001 /* NavigationCleanupTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationCleanupTests.swift; sourceTree = "<group>"; };
40
42
  2B2F1D632BEBFAAA006897EE /* WebSpatial.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WebSpatial.app; sourceTree = BUILT_PRODUCTS_DIR; };
41
43
  2B2F1D672BEBFAAA006897EE /* RealityKitContent */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = RealityKitContent; sourceTree = "<group>"; };
42
44
  2B2F1D702BEBFAAC006897EE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
@@ -51,6 +53,7 @@
51
53
  2B06AF302E4C1AF8000327E9 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
52
54
  isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
53
55
  membershipExceptions = (
56
+ AttachmentManager.swift,
54
57
  Dynamic3DManager.swift,
55
58
  FileCoordinator.swift,
56
59
  JSBManager.swift,
@@ -156,11 +159,20 @@
156
159
  isa = PBXGroup;
157
160
  children = (
158
161
  2B2F1D652BEBFAAA006897EE /* web-spatial */,
162
+ 2B1008A32F0B000800000003 /* web-spatialTests */,
159
163
  2B2F1D662BEBFAAA006897EE /* Packages */,
160
164
  2B2F1D642BEBFAAA006897EE /* Products */,
161
165
  );
162
166
  sourceTree = "<group>";
163
167
  };
168
+ 2B1008A32F0B000800000003 /* web-spatialTests */ = {
169
+ isa = PBXGroup;
170
+ children = (
171
+ 2B1008A12F0B000800000001 /* NavigationCleanupTests.swift */,
172
+ );
173
+ path = "web-spatialTests";
174
+ sourceTree = "<group>";
175
+ };
164
176
  2B2F1D642BEBFAAA006897EE /* Products */ = {
165
177
  isa = PBXGroup;
166
178
  children = (
@@ -334,6 +346,7 @@
334
346
  isa = PBXSourcesBuildPhase;
335
347
  buildActionMask = 2147483647;
336
348
  files = (
349
+ 2B1008A22F0B000800000002 /* NavigationCleanupTests.swift in Sources */,
337
350
  );
338
351
  runOnlyForDeploymentPostprocessing = 0;
339
352
  };
@@ -0,0 +1,33 @@
1
+ @testable import WebSpatial
2
+ import XCTest
3
+
4
+ final class NavigationCleanupTests: XCTestCase {
5
+ func test_resetForNavigation_destroysSceneSpatialObjectsAndAttachments() {
6
+ // Given: a scene with spatial objects and attachments created by the current page
7
+ let scene = SpatialApp.Instance.createScene(
8
+ "http://localhost:5173/",
9
+ .window,
10
+ .visible
11
+ )
12
+
13
+ let panel: Spatialized2DElement = scene.createSpatializedElement(.Spatialized2DElement)
14
+ panel.setParent(scene)
15
+ XCTAssertNotNil(scene.findSpatialObject(panel.id) as Spatialized2DElement?)
16
+
17
+ _ = scene.attachmentManager.create(
18
+ id: "test-attachment",
19
+ parentEntityId: "test-parent-entity",
20
+ position: SIMD3<Float>(0, 0, 0),
21
+ size: CGSize(width: 100, height: 100)
22
+ )
23
+ XCTAssertFalse(scene.attachmentManager.attachments.isEmpty)
24
+
25
+ // When: navigation happens (reload/back/forward/new URL)
26
+ scene.resetForNavigation()
27
+
28
+ // Then: all existing spatial objects and attachments should be cleaned up
29
+ XCTAssertNil(scene.findSpatialObject(panel.id) as Spatialized2DElement?)
30
+ XCTAssertTrue(scene.attachmentManager.attachments.isEmpty)
31
+ XCTAssertNil(SpatialObject.get(panel.id))
32
+ }
33
+ }