@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.
- package/package.json +2 -1
- package/web-spatial/EventEmitter.swift +11 -11
- package/web-spatial/JSBCommand.swift +38 -3
- package/web-spatial/WebMsgCommand.swift +5 -16
- package/web-spatial/WebSpatialApp.swift +10 -10
- package/web-spatial/Window.swift +2 -2
- package/web-spatial/manager/AttachmentManager.swift +84 -0
- package/web-spatial/manager/Dynamic3DManager.swift +10 -0
- package/web-spatial/manager/JSBManager.swift +1 -2
- package/web-spatial/manager/WKWebViewManager.swift +4 -4
- package/web-spatial/manifest.swift +11 -6
- package/web-spatial/model/SpatialApp.swift +60 -56
- package/web-spatial/model/SpatialScene.swift +233 -16
- package/web-spatial/model/Spatialized2DElement.swift +4 -5
- package/web-spatial/model/SpatializedDynamic3DElement.swift +12 -0
- package/web-spatial/model/SpatializedElement.swift +40 -0
- package/web-spatial/model/SpatializedStatic3DElement.swift +1 -1
- package/web-spatial/model/dynamic3d/SpatialComponent.swift +27 -27
- package/web-spatial/model/dynamic3d/SpatialEntity.swift +8 -2
- package/web-spatial/model/dynamic3d/SpatialMaterial.swift +15 -15
- package/web-spatial/model/dynamic3d/SpatialModelEntity.swift +10 -10
- package/web-spatial/model/dynamic3d/SpatialModelResource.swift +1 -1
- package/web-spatial/model/dynamic3d/SpatialTextureResource.swift +8 -8
- package/web-spatial/view/SceneHandlerUIView.swift +29 -1
- package/web-spatial/view/SpatialNavView.swift +52 -47
- package/web-spatial/view/SpatializedDynamic3DView.swift +88 -5
- package/web-spatial/view/SpatializedElementView.swift +85 -47
- package/web-spatial/view/SpatializedStatic3DView.swift +9 -7
- package/web-spatial/view/view-modifier/HideViewModifier.swift +2 -2
- package/web-spatial/webview/SpatialWebController.swift +42 -25
- package/web-spatial/webview/SpatialWebView.swift +5 -1
- package/web-spatial/webview/SpatialWebViewModel.swift +13 -7
- package/web-spatial.xcodeproj/project.pbxproj +13 -0
- package/web-spatialTests/NavigationCleanupTests.swift +33 -0
package/package.json
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
1
|
class EventEmitter {
|
|
2
2
|
private var listeners: [String: [(_ object: Any, _ data: Any) -> Void]] = [:]
|
|
3
3
|
|
|
4
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
201
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: {
|
package/web-spatial/Window.swift
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
import SwiftUI
|
|
3
3
|
|
|
4
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
12
|
-
let userScript = WKUserScript(source: "window.WebSpatailEnabled = true; window.WebSpatailNativeVersion = '
|
|
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
|
|
29
|
-
controller.webview!.customUserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7; wv) AppleWebKit\(webviewVersion)WebSpatial/\(
|
|
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
|
-
//
|
|
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
|
|
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
|
-
|
|
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
|
|
106
|
-
return
|
|
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
|
-
|
|
7
|
-
let nativeAPIVersion = pwaManager.
|
|
6
|
+
/// To load a local path, remove http:// eg. "static-web/"
|
|
7
|
+
let nativeAPIVersion = pwaManager.getShellVersion()
|
|
8
8
|
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
159
|
-
|
|
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
|
-
|
|
173
|
-
|
|
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
|
-
|
|
183
|
+
|
|
184
|
+
func focusScene(_ targetSpatialScene: SpatialScene) {
|
|
181
185
|
// only work when fully visible
|
|
182
186
|
if targetSpatialScene.state != .visible {
|
|
183
187
|
return
|