@webspatial/builder 0.0.1 → 0.0.2

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 (113) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +1 -0
  3. package/dist/lib/Cli.d.ts +3 -0
  4. package/dist/lib/Cli.js +1 -0
  5. package/dist/lib/cmds/build.d.ts +4 -0
  6. package/dist/lib/cmds/build.js +1 -0
  7. package/dist/lib/cmds/check.d.ts +3 -0
  8. package/dist/lib/cmds/check.js +1 -0
  9. package/dist/lib/cmds/help.d.ts +3 -0
  10. package/dist/lib/cmds/help.js +1 -0
  11. package/dist/lib/cmds/version.d.ts +2 -0
  12. package/dist/lib/cmds/version.js +1 -0
  13. package/dist/lib/pwa/config.d.ts +7 -0
  14. package/dist/lib/pwa/config.js +1 -0
  15. package/dist/lib/pwa/index.d.ts +16 -0
  16. package/dist/lib/pwa/index.js +1 -0
  17. package/dist/lib/pwa/validate.d.ts +4 -0
  18. package/dist/lib/pwa/validate.js +1 -0
  19. package/dist/lib/resource/file.d.ts +2 -0
  20. package/dist/lib/resource/file.js +1 -0
  21. package/dist/lib/resource/imageHelper.d.ts +7 -0
  22. package/dist/lib/resource/imageHelper.js +1 -0
  23. package/dist/lib/resource/index.d.ts +13 -0
  24. package/dist/lib/resource/index.js +1 -0
  25. package/dist/lib/resource/load.d.ts +6 -0
  26. package/dist/lib/resource/load.js +1 -0
  27. package/dist/lib/utils/CustomError.d.ts +13 -0
  28. package/dist/lib/utils/CustomError.js +1 -0
  29. package/dist/lib/utils/FetchUtils-1.d.ts +12 -0
  30. package/dist/lib/utils/FetchUtils-1.js +1 -0
  31. package/dist/lib/utils/Log.d.ts +88 -0
  32. package/dist/lib/utils/Log.js +1 -0
  33. package/dist/lib/utils/fetch.d.ts +5 -0
  34. package/dist/lib/utils/fetch.js +1 -0
  35. package/dist/lib/utils/messages.d.ts +50 -0
  36. package/dist/lib/utils/messages.js +1 -0
  37. package/dist/lib/utils/utils.d.ts +1 -0
  38. package/dist/lib/utils/utils.js +1 -0
  39. package/dist/lib/xcode/index.d.ts +5 -0
  40. package/dist/lib/xcode/index.js +1 -0
  41. package/dist/lib/xcode/manifestSwiftTemplate.d.ts +1 -0
  42. package/dist/lib/xcode/manifestSwiftTemplate.js +1 -0
  43. package/dist/lib/xcode/xcodebuild.d.ts +16 -0
  44. package/dist/lib/xcode/xcodebuild.js +1 -0
  45. package/dist/lib/xcode/xcodeproject.d.ts +11 -0
  46. package/dist/lib/xcode/xcodeproject.js +1 -0
  47. package/dist/lib/xcode/xcrun.d.ts +7 -0
  48. package/dist/lib/xcode/xcrun.js +1 -0
  49. package/package.json +2 -1
  50. package/template/visionOSApp/Packages/RealityKitContent/.build/workspace-state.json +7 -0
  51. package/template/visionOSApp/Packages/RealityKitContent/.swiftpm/xcode/xcuserdata/bytedance.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
  52. package/template/visionOSApp/Packages/RealityKitContent/Package.realitycomposerpro/ProjectData/main.json +11 -0
  53. package/template/visionOSApp/Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/SceneMetadataList.json +112 -0
  54. package/template/visionOSApp/Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/Settings.rcprojectdata +17 -0
  55. package/template/visionOSApp/Packages/RealityKitContent/Package.swift +27 -0
  56. package/template/visionOSApp/Packages/RealityKitContent/README.md +3 -0
  57. package/template/visionOSApp/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Immersive.usda +50 -0
  58. package/template/visionOSApp/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Materials/GridMaterial.usda +216 -0
  59. package/template/visionOSApp/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Scene.usda +59 -0
  60. package/template/visionOSApp/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.swift +4 -0
  61. package/template/visionOSApp/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json +12 -0
  62. package/template/visionOSApp/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json +6 -0
  63. package/template/visionOSApp/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Contents.json +17 -0
  64. package/template/visionOSApp/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json +12 -0
  65. package/template/visionOSApp/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json +6 -0
  66. package/template/visionOSApp/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json +12 -0
  67. package/template/visionOSApp/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json +6 -0
  68. package/template/visionOSApp/web-spatial/Assets.xcassets/Contents.json +6 -0
  69. package/template/visionOSApp/web-spatial/Info.plist +33 -0
  70. package/template/visionOSApp/web-spatial/Preview Content/Preview Assets.xcassets/Contents.json +6 -0
  71. package/template/visionOSApp/web-spatial/libs/EventEmitter.swift +32 -0
  72. package/template/visionOSApp/web-spatial/libs/SpatialComponent.swift +31 -0
  73. package/template/visionOSApp/web-spatial/libs/SpatialEntity.swift +179 -0
  74. package/template/visionOSApp/web-spatial/libs/SpatialInputComponent.swift +26 -0
  75. package/template/visionOSApp/web-spatial/libs/SpatialMeshResource.swift +19 -0
  76. package/template/visionOSApp/web-spatial/libs/SpatialModel3DComponent.swift +51 -0
  77. package/template/visionOSApp/web-spatial/libs/SpatialModelComponent.swift +32 -0
  78. package/template/visionOSApp/web-spatial/libs/SpatialObject.swift +144 -0
  79. package/template/visionOSApp/web-spatial/libs/SpatialPhysicallyBasedMaterial.swift +19 -0
  80. package/template/visionOSApp/web-spatial/libs/SpatialViewComponent.swift +15 -0
  81. package/template/visionOSApp/web-spatial/libs/SpatialWindowComponent.swift +420 -0
  82. package/template/visionOSApp/web-spatial/libs/SpatialWindowContainer.swift +132 -0
  83. package/template/visionOSApp/web-spatial/libs/Utils/CommandManager.swift +799 -0
  84. package/template/visionOSApp/web-spatial/libs/Utils/Logger.swift +36 -0
  85. package/template/visionOSApp/web-spatial/libs/Utils/SceneManager.swift +108 -0
  86. package/template/visionOSApp/web-spatial/libs/Utils/WindowContainerMgr.swift +113 -0
  87. package/template/visionOSApp/web-spatial/libs/json/JsonParser.swift +52 -0
  88. package/template/visionOSApp/web-spatial/libs/uiKitDelegate/Window.swift +34 -0
  89. package/template/visionOSApp/web-spatial/libs/webView/UpdateSystem.swift +33 -0
  90. package/template/visionOSApp/web-spatial/libs/webView/backend/NativeWebView.swift +319 -0
  91. package/template/visionOSApp/web-spatial/libs/webView/manifest.swift +91 -0
  92. package/template/visionOSApp/web-spatial/static-web/index.html +9 -0
  93. package/template/visionOSApp/web-spatial/views/HideViewModifier.swift +17 -0
  94. package/template/visionOSApp/web-spatial/views/ImmersiveView.swift +24 -0
  95. package/template/visionOSApp/web-spatial/views/LoadingView.swift +25 -0
  96. package/template/visionOSApp/web-spatial/views/MaterialWithBorderCornerModifier.swift +82 -0
  97. package/template/visionOSApp/web-spatial/views/OpenDismissHandlerUI.swift +52 -0
  98. package/template/visionOSApp/web-spatial/views/PlainWindowContainerView.swift +84 -0
  99. package/template/visionOSApp/web-spatial/views/SpatialModel3DView.swift +193 -0
  100. package/template/visionOSApp/web-spatial/views/SpatialViewUI.swift +168 -0
  101. package/template/visionOSApp/web-spatial/views/SpatialWebViewUI.swift +187 -0
  102. package/template/visionOSApp/web-spatial/views/VolumetricWindowContainerView.swift +34 -0
  103. package/template/visionOSApp/web-spatial/views/ui/NavView.swift +88 -0
  104. package/template/visionOSApp/web-spatial/web_spatialApp.swift +134 -0
  105. package/template/visionOSApp/web-spatial.xcodeproj/project.pbxproj +686 -0
  106. package/template/visionOSApp/web-spatial.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  107. package/template/visionOSApp/web-spatial.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  108. package/template/visionOSApp/web-spatial.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +5 -0
  109. package/template/visionOSApp/web-spatial.xcodeproj/project.xcworkspace/xcuserdata/bytedance.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  110. package/template/visionOSApp/web-spatial.xcodeproj/project.xcworkspace/xcuserdata/bytedance.xcuserdatad/WorkspaceSettings.xcsettings +14 -0
  111. package/template/visionOSApp/web-spatial.xcodeproj/xcshareddata/xcschemes/web-spatial.xcscheme +115 -0
  112. package/template/visionOSApp/web-spatial.xcodeproj/xcuserdata/bytedance.xcuserdatad/xcschemes/xcschememanagement.plist +27 -0
  113. package/template/visionOSApp/web-spatialTests/web_spatialTests.swift +34 -0
@@ -0,0 +1,420 @@
1
+
2
+ //
3
+ // SpatialWindowComponent.swift
4
+ // web-spatial
5
+ //
6
+ // Created by ByteDance on 5/9/24.
7
+ //
8
+
9
+ import Combine
10
+ import Foundation
11
+ import RealityKit
12
+ import SwiftUI
13
+ import WebKit
14
+
15
+ let DefaultPlainWindowContainerSize = CGSize(width: 1280, height: 720)
16
+
17
+ func getDocumentsDirectory() -> URL {
18
+ let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
19
+ let documentsDirectory = paths[0]
20
+ return documentsDirectory
21
+ }
22
+
23
+ struct LoadingStyles {
24
+ var cornerRadius: CornerRadius = .init()
25
+ var windowContainerSize = DefaultPlainWindowContainerSize
26
+ var backgroundMaterial: BackgroundMaterial = .None
27
+ }
28
+
29
+ @Observable
30
+ class SpatialWindowComponent: SpatialComponent {
31
+ override func inspect() -> [String: Any] {
32
+ let childEntitiesInfo = childResources.mapValues { spatialObject in
33
+ spatialObject.inspect()
34
+ }
35
+
36
+ let encoder = JSONEncoder()
37
+ encoder.outputFormatting = .prettyPrinted
38
+
39
+ var inspectInfo: [String: Any] = [
40
+ "scrollWithParent": scrollWithParent,
41
+ "resolutionX": resolutionX,
42
+ "resolutionY": resolutionY,
43
+ "parentWebviewID": parentWebviewID,
44
+ "parentWindowContainerID": parentWindowContainerID,
45
+ "childWindowContainers": childWindowContainers,
46
+ "spawnedNativeWebviewsCount": spawnedNativeWebviews.count,
47
+ "childResources": childEntitiesInfo,
48
+ "cornerRadius": cornerRadius.toJson(),
49
+ "backgroundMaterial": backgroundMaterial.rawValue,
50
+ "isOpaque": webViewNative!.webViewHolder.appleWebView!.isOpaque,
51
+ ]
52
+
53
+ let baseInspectInfo = super.inspect()
54
+ for (key, value) in baseInspectInfo {
55
+ inspectInfo[key] = value
56
+ }
57
+ return inspectInfo
58
+ }
59
+
60
+ var scrollOffset = CGPoint()
61
+ private var webViewNative: WebViewNative?
62
+ var resolutionX: Double = 0
63
+ var resolutionY: Double = 0
64
+ var scrollWithParent = false
65
+
66
+ var rotationAnchor: UnitPoint3D = .center
67
+
68
+ // Track the first load event of the webview so we don't see a flash of white before the page loads
69
+ var didFinishFirstLoad = false
70
+
71
+ // ID of the webview that created this or empty if its root
72
+ var parentWebviewID: String = ""
73
+ var parentWindowContainerID: String
74
+ var childWindowContainers = [String: WindowContainerData]()
75
+ var spawnedNativeWebviews = [String: WebViewNative]()
76
+
77
+ // Resources that will be destroyed when this webpage is destoryed or if it is navigated away from
78
+ private var childResources = [String: SpatialObject]()
79
+ public func addChildSpatialObject(_ spatialObject: SpatialObject) {
80
+ childResources[spatialObject.id] = spatialObject
81
+ spatialObject
82
+ .on(
83
+ event: SpatialObject.Events.BeforeDestroyed.rawValue,
84
+ listener: onSptatialObjectDestroyed
85
+ )
86
+ }
87
+
88
+ public func removeChildSpatialObject(_ spatialObject: SpatialObject) {
89
+ spatialObject
90
+ .off(
91
+ event: SpatialObject.Events.BeforeDestroyed.rawValue,
92
+ listener: onSptatialObjectDestroyed
93
+ )
94
+ childResources.removeValue(forKey: spatialObject.id)
95
+ }
96
+
97
+ public func getChildSpatialObject(name: String) -> SpatialObject? {
98
+ return childResources[name]
99
+ }
100
+
101
+ public func destroyChild(name: String) {
102
+ childResources[name]?.destroy()
103
+ }
104
+
105
+ private func onSptatialObjectDestroyed(_ object: Any, _ data: Any) {
106
+ let spatialObject = object as! SpatialObject
107
+ removeChildSpatialObject(spatialObject)
108
+ }
109
+
110
+ private func onWindowContainerDestroyed(_ object: Any, _ data: Any) {
111
+ if let spatialObject = object as? SpatialWindowContainer {
112
+ spatialObject
113
+ .off(
114
+ event: SpatialObject.Events.BeforeDestroyed.rawValue,
115
+ listener: onWindowContainerDestroyed
116
+ )
117
+ childWindowContainers.removeValue(forKey: spatialObject.id)
118
+ }
119
+ }
120
+
121
+ /// Determines whether the current webview is a root webview.
122
+ ///
123
+ /// A root webview is created when the Scene is initialized.
124
+ /// If the webview is created by another `SpatialWebview`, it is not considered a root webview.
125
+ /// For example, a `SpatialDiv` is not a root webview.
126
+ ///
127
+ /// - Returns: `true` if the webview is a root webview (i.e., `parentWebviewID` is empty), otherwise `false`.
128
+ public func isRootWebview() -> Bool {
129
+ return parentWebviewID == ""
130
+ }
131
+
132
+ public func setWindowContainer(uuid: String, wgd: WindowContainerData) {
133
+ childWindowContainers[uuid] = wgd
134
+ SpatialWindowContainer.getSpatialWindowContainer(uuid)!.on(
135
+ event: SpatialObject.Events.BeforeDestroyed.rawValue,
136
+ listener: onWindowContainerDestroyed
137
+ )
138
+ }
139
+
140
+ // Drag event handling
141
+ var dragStarted = false
142
+ var dragStart = 0.0
143
+ var dragVelocity = 0.0
144
+
145
+ var gotStyle = false
146
+ var opacity = 1.0
147
+ var cornerRadius: CornerRadius = .init()
148
+
149
+ private var _backgroundMaterial = BackgroundMaterial.None
150
+ var backgroundMaterial: BackgroundMaterial {
151
+ get {
152
+ return _backgroundMaterial
153
+ }
154
+ set(newValue) {
155
+ _backgroundMaterial = newValue
156
+ if isRootWebview() {
157
+ webViewNative?.webViewHolder.appleWebView?.isOpaque = _backgroundMaterial == .None
158
+ } else {
159
+ // it's spatial div
160
+ webViewNative?.webViewHolder.appleWebView?.isOpaque = false
161
+ }
162
+ }
163
+ }
164
+
165
+ var loadingStyles = LoadingStyles()
166
+ var isLoading = true
167
+
168
+ var didFailLoad = false
169
+
170
+ private var cancellables = Set<AnyCancellable>() // save subscriptions
171
+
172
+ init(parentWindowContainerID: String) {
173
+ self.parentWindowContainerID = parentWindowContainerID
174
+ super.init()
175
+ webViewNative = WebViewNative()
176
+ webViewNative?.webViewRef = self
177
+ _ = webViewNative?.createResources()
178
+ registerForceStyle()
179
+ }
180
+
181
+ init(parentWindowContainerID: String, url: URL) {
182
+ self.parentWindowContainerID = parentWindowContainerID
183
+ super.init()
184
+
185
+ webViewNative = WebViewNative(url: url)
186
+ webViewNative?.webViewRef = self
187
+ _ = webViewNative?.createResources()
188
+ registerForceStyle()
189
+ }
190
+
191
+ func initFromURL(url: URL) {
192
+ webViewNative = WebViewNative(url: url)
193
+ webViewNative?.webViewRef = self
194
+ _ = webViewNative?.createResources()
195
+ registerForceStyle()
196
+ }
197
+
198
+ // the url schema handler for forcestyle cannot bind seperately for every webview due to apple limitation. So this is a workaround like eventbus, webview will handle the message that has matched webview.
199
+ func registerForceStyle() {
200
+ webviewGetEarlyStyleData
201
+ .filter { [weak self] event in
202
+ self?.getView()?.webViewHolder.appleWebView == event.webview
203
+ }
204
+ .sink { [weak self] event in
205
+ self?.didGetEarlyStyle(style: event.style)
206
+ }
207
+ .store(in: &cancellables)
208
+ }
209
+
210
+ func goBack() {
211
+ webViewNative?.webViewHolder.appleWebView?.goBack()
212
+ }
213
+
214
+ func goForward() {
215
+ webViewNative?.webViewHolder.appleWebView?.goForward()
216
+ }
217
+
218
+ func reload() {
219
+ webViewNative?.webViewHolder.appleWebView?.reload()
220
+ }
221
+
222
+ var canGoBack: Bool = false
223
+
224
+ var canGoForward: Bool = false
225
+
226
+ func navigateToURL(url: URL) {
227
+ webViewNative!.url = url
228
+ webViewNative!.webViewHolder.needsUpdate = true
229
+ webViewNative!.initialLoad()
230
+ }
231
+
232
+ func isScrollEnabled() -> Bool {
233
+ return webViewNative!.webViewHolder.appleWebView!.scrollView.isScrollEnabled
234
+ }
235
+
236
+ func updateScrollOffset(delta: CGFloat) {
237
+ webViewNative!.webViewHolder.appleWebView!.scrollView.contentOffset.y += delta
238
+ }
239
+
240
+ func stopScrolling() {
241
+ webViewNative!.webViewHolder.appleWebView!.scrollView.stopScrollingAndZooming()
242
+ }
243
+
244
+ func getView() -> WebViewNative? {
245
+ return webViewNative
246
+ }
247
+
248
+ func setView(wv: WebViewNative) {
249
+ webViewNative = wv
250
+ webViewNative!.webViewRef = self
251
+ }
252
+
253
+ func evaluateJS(js: String) {
254
+ webViewNative!.webViewHolder.appleWebView!.evaluateJavaScript(js)
255
+ }
256
+
257
+ func getURL() -> URL? {
258
+ return webViewNative?.url
259
+ }
260
+
261
+ func setURL(url: URL) {
262
+ webViewNative!.url = url
263
+ }
264
+
265
+ func parseURL(url: String) -> String {
266
+ // Compute target url depending if the url is relative or not
267
+ var targetUrl = url
268
+ if !pwaManager.isLocal {
269
+ if url[...url.index(url.startIndex, offsetBy: 0)] == "/" {
270
+ // Absolute path
271
+ var port = ""
272
+ if let p = webViewNative?.url.port {
273
+ port = ":" + String(p)
274
+ }
275
+ let domain = webViewNative!.url.scheme! + "://" + webViewNative!.url.host()! + port + "/"
276
+ targetUrl = domain + String(url[url.index(url.startIndex, offsetBy: 1)...])
277
+ } else {
278
+ // Full url eg. http://domain.com
279
+ if let parsed = URL(string: url) {
280
+ if parsed.scheme != nil {
281
+ return url
282
+ }
283
+ }
284
+ // Reletive path
285
+ let localDir = NSString(string: webViewNative!.url.absoluteString)
286
+ let relPath = String(localDir.deletingLastPathComponent) + "/" + targetUrl
287
+ return relPath
288
+ }
289
+ } else {
290
+ if !(targetUrl.starts(with: "http://") || targetUrl.starts(with: "https://")) {
291
+ targetUrl = pwaManager.getLocalResourceURL(url: targetUrl)
292
+ }
293
+ }
294
+ return targetUrl
295
+ }
296
+
297
+ func readWindowContainerID(id: String) -> String {
298
+ if id == "current" {
299
+ return parentWindowContainerID
300
+ } else {
301
+ return id
302
+ }
303
+ }
304
+
305
+ deinit {
306
+ webViewNative!.destroy()
307
+ cancellables.removeAll()
308
+ }
309
+
310
+ func completeEvent(requestID: Int, data: String = "{}") {
311
+ webViewNative?.webViewHolder.appleWebView?.evaluateJavaScript("window.__SpatialWebEvent({success: true, requestID:" + String(requestID) + ", data: " + data + "})")
312
+ }
313
+
314
+ func fireComponentEvent(componentId: String, data: String = "{}") {
315
+ webViewNative?.webViewHolder.appleWebView?.evaluateJavaScript("window.__SpatialWebEvent({resourceId:'" + componentId + "', data: " + data + "})")
316
+ }
317
+
318
+ func failEvent(requestID: Int, data: String = "{}") {
319
+ webViewNative?.webViewHolder.appleWebView?.evaluateJavaScript("window.__SpatialWebEvent({success: false, requestID:" + String(requestID) + ", data: " + data + "})")
320
+ }
321
+
322
+ // Request information of webview that request this webview to load
323
+ weak var loadRequestWV: SpatialWindowComponent?
324
+ var loadRequestID = -1
325
+
326
+ // A load request of a child webview was loaded
327
+ func didLoadChild(loadRequestID: Int, resourceID: String) {
328
+ completeEvent(requestID: loadRequestID, data: "{createdID: '" + id + "'}")
329
+ }
330
+
331
+ func didFailLoadPage() {
332
+ didFailLoad = true
333
+ didFinishFirstLoad = true
334
+ }
335
+
336
+ func releaseChildResources() {
337
+ let spatialObjects = childResources.map { $0.value }
338
+ for spatialObject in spatialObjects {
339
+ spatialObject.destroy()
340
+ }
341
+ childResources = [String: SpatialObject]()
342
+ spawnedNativeWebviews = [String: WebViewNative]()
343
+
344
+ let wgkeys = childWindowContainers.map { $0.key }
345
+ for k in wgkeys {
346
+ SpatialWindowContainer.getSpatialWindowContainer(k)!.closeWindowData.send(childWindowContainers[k]!)
347
+ }
348
+ }
349
+
350
+ func didStartLoadPage() {
351
+ if didFinishFirstLoad {
352
+ webViewNative!.webViewHolder.appleWebView!.evaluateJavaScript("window.__WebSpatialUnloaded = true")
353
+ }
354
+
355
+ releaseChildResources()
356
+ let url = webViewNative?.webViewHolder.appleWebView?.url
357
+ webViewNative!.url = url!
358
+
359
+ // Mark that we havn't gotten a style update
360
+ gotStyle = false
361
+ isLoading = true
362
+ loadingStyles = LoadingStyles()
363
+
364
+ // FIXME:
365
+ // This is a workaround to force run UIViewRepresentable.update()
366
+ // SwiftUI not trigger it when go back from example page.
367
+ // Warning of `AttributeGraph: cycle detected through attribute` fired when goes to example page
368
+ webViewNative?.initialLoad()
369
+ }
370
+
371
+ func didSpawnWebView(wv: WebViewNative) {
372
+ let uuid = UUID().uuidString
373
+ wv.webViewHolder.appleWebView!.evaluateJavaScript("window._webSpatialID = '" + uuid + "'")
374
+ spawnedNativeWebviews[uuid] = wv
375
+ }
376
+
377
+ func didCloseWebView() {
378
+ // if need
379
+ if isRootWebview() {
380
+ SceneManager.Instance.closeRoot(self)
381
+ }
382
+ }
383
+
384
+ func didStartReceivePageContent() {}
385
+
386
+ func didGetEarlyStyle(style: PreloadStyleSettings) {
387
+ if let cornerRadius = style.cornerRadius {
388
+ loadingStyles.cornerRadius = cornerRadius
389
+ }
390
+
391
+ if let backgroundMaterial = style.backgroundMaterial {
392
+ loadingStyles.backgroundMaterial = backgroundMaterial
393
+ }
394
+ }
395
+
396
+ func didFinishLoadPage() {
397
+ didFinishFirstLoad = true
398
+ didFailLoad = false
399
+ cornerRadius = loadingStyles.cornerRadius
400
+ backgroundMaterial = loadingStyles.backgroundMaterial
401
+
402
+ isLoading = false
403
+
404
+ // update navinfo
405
+ if let wv = webViewNative?.webViewHolder.appleWebView {
406
+ canGoBack = wv.canGoBack
407
+ canGoForward = wv.canGoForward
408
+ }
409
+ }
410
+
411
+ override func onDestroy() {
412
+ releaseChildResources()
413
+ didCloseWebView()
414
+ }
415
+
416
+ func didNavBackForward() {
417
+ // in JS calling history.go(-1) we should set needUpdate=true
418
+ webViewNative?.webViewHolder.needsUpdate = true
419
+ }
420
+ }
@@ -0,0 +1,132 @@
1
+ //
2
+ // SpatialWindowContainer.swift
3
+ // web-spatial
4
+ //
5
+ // Created by ByteDance on 9/10/24.
6
+ //
7
+
8
+ import Combine
9
+ import Foundation
10
+ import RealityKit
11
+ import typealias RealityKit.Entity
12
+
13
+ @Observable
14
+ class SpatialWindowContainer: SpatialObject {
15
+ // Resources that will be destroyed when this window group is removed
16
+ private var childResources = [String: SpatialObject]()
17
+ public var childContainers = [String: SpatialWindowContainer]()
18
+
19
+ var wgd: WindowContainerData
20
+ static func getSpatialWindowContainer(_ name: String) -> SpatialWindowContainer? {
21
+ return SpatialObject.get(name) as? SpatialWindowContainer
22
+ }
23
+
24
+ static func getOrCreateSpatialWindowContainer(_ name: String, _ data: WindowContainerData) -> SpatialWindowContainer? {
25
+ if let windowContainer = getSpatialWindowContainer(name) {
26
+ return windowContainer
27
+ }
28
+ let newWindowContainer = SpatialWindowContainer(name, data)
29
+ return newWindowContainer
30
+ }
31
+
32
+ init(_ name: String, _ data: WindowContainerData) {
33
+ wgd = data
34
+ super.init(name)
35
+ }
36
+
37
+ // Resources
38
+ private var childEntities = [String: SpatialEntity]()
39
+
40
+ public func getEntities() -> [String: SpatialEntity] {
41
+ return childEntities
42
+ }
43
+
44
+ public func addEntity(_ spatialEntity: SpatialEntity) {
45
+ childEntities[spatialEntity.id] = spatialEntity
46
+ }
47
+
48
+ public func removeEntity(_ spatialEntity: SpatialEntity) {
49
+ childEntities.removeValue(forKey: spatialEntity.id)
50
+ }
51
+
52
+ // Global state
53
+ var toggleImmersiveSpace = PassthroughSubject<Bool, Never>()
54
+
55
+ var setSize = PassthroughSubject<CGSize, Never>()
56
+
57
+ var updateFrame = false
58
+ var openWindowData = PassthroughSubject<WindowContainerData, Never>()
59
+ var closeWindowData = PassthroughSubject<WindowContainerData, Never>()
60
+
61
+ var setLoadingWindowData = PassthroughSubject<LoadingWindowContainerData, Never>()
62
+
63
+ override func onDestroy() {
64
+ childEntities.forEach { $0.value.destroy() }
65
+ childEntities = [:]
66
+
67
+ // Destroy resources
68
+ let spatialObjects = childResources.map { $0.value }
69
+ for spatialObject in spatialObjects {
70
+ spatialObject.destroy()
71
+ }
72
+ childResources = [String: SpatialObject]()
73
+
74
+ // Destroy spatial containers
75
+ let spatialContainers = childContainers.map { $0.value }
76
+ for spatialObject in spatialContainers {
77
+ if spatialObject != self {
78
+ spatialObject.destroy()
79
+ }
80
+ }
81
+ childContainers = [String: SpatialWindowContainer]()
82
+
83
+ // Close the window group when this object is destroyed
84
+ SpatialWindowContainer.getSpatialWindowContainer(id)!.closeWindowData.send(wgd)
85
+ }
86
+
87
+ override func inspect() -> [String: Any] {
88
+ let childEntitiesInfo = childEntities.mapValues { entity in
89
+ entity.inspect()
90
+ }
91
+
92
+ let baseInspectInfo = super.inspect()
93
+ var inspectInfo: [String: Any] = ["childEntities": childEntitiesInfo]
94
+ for (key, value) in baseInspectInfo {
95
+ inspectInfo[key] = value
96
+ }
97
+ return inspectInfo
98
+ }
99
+ }
100
+
101
+ extension SpatialWindowContainer {
102
+ private static let RootID = "root"
103
+ static func getRootID() -> String {
104
+ return RootID
105
+ }
106
+
107
+ static func createRootWindowContainer() -> SpatialWindowContainer {
108
+ if let rootWindowContainer = getSpatialWindowContainer(RootID) {
109
+ logger.warning("Root already created! ")
110
+ return rootWindowContainer
111
+ }
112
+ let wgd = WindowContainerData(windowStyle: "Plain", windowContainerID: "root")
113
+ return SpatialWindowContainer(RootID, wgd)
114
+ }
115
+ }
116
+
117
+ extension SpatialWindowContainer {
118
+ private static let ImmersiveID = "Immersive"
119
+
120
+ static func getImmersiveWindowContainer() -> SpatialWindowContainer {
121
+ return getSpatialWindowContainer(ImmersiveID)!
122
+ }
123
+
124
+ static func createImmersiveWindowContainer() -> SpatialWindowContainer {
125
+ if let windowContainer = getSpatialWindowContainer(ImmersiveID) {
126
+ logger.warning("Immersive already created! ")
127
+ return windowContainer
128
+ }
129
+ let wgd = WindowContainerData(windowStyle: "ImmersiveSpace", windowContainerID: "ImmersiveSpace")
130
+ return SpatialWindowContainer(ImmersiveID, wgd)
131
+ }
132
+ }