@webspatial/platform-visionos 1.0.4 → 1.1.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 (74) hide show
  1. package/package.json +2 -2
  2. package/web-spatial/EventEmitter.swift +56 -0
  3. package/web-spatial/JSBCommand.swift +348 -0
  4. package/web-spatial/SpatialObject.swift +108 -0
  5. package/web-spatial/WebMsgCommand.swift +137 -0
  6. package/web-spatial/WebSpatialApp.swift +111 -0
  7. package/web-spatial/{libs/uiKitDelegate/Window.swift → Window.swift} +1 -0
  8. package/web-spatial/manager/Dynamic3DManager.swift +114 -0
  9. package/web-spatial/manager/JSBManager.swift +148 -0
  10. package/web-spatial/manager/WKWebViewManager.swift +39 -0
  11. package/web-spatial/{libs/webView/manifest.swift → manifest.swift} +16 -5
  12. package/web-spatial/model/SpatialApp.swift +190 -0
  13. package/web-spatial/model/SpatialScene.swift +1078 -0
  14. package/web-spatial/model/Spatialized2DElement.swift +128 -0
  15. package/web-spatial/model/SpatializedDynamic3DElement.swift +34 -0
  16. package/web-spatial/model/SpatializedElement.swift +95 -0
  17. package/web-spatial/model/SpatializedStatic3DElement.swift +19 -0
  18. package/web-spatial/model/dynamic3d/Geometry.swift +95 -0
  19. package/web-spatial/model/dynamic3d/SpatialComponent.swift +72 -0
  20. package/web-spatial/model/dynamic3d/SpatialEntity.swift +211 -0
  21. package/web-spatial/model/dynamic3d/SpatialMaterial.swift +39 -0
  22. package/web-spatial/model/dynamic3d/SpatialModelEntity.swift +39 -0
  23. package/web-spatial/model/dynamic3d/SpatialModelResource.swift +38 -0
  24. package/web-spatial/model/dynamic3d/SpatialTextureResource.swift +18 -0
  25. package/web-spatial/protocol/ScrollAbleSpatialElementContainer.swift +1 -0
  26. package/web-spatial/protocol/SpatialScrollAble.swift +8 -0
  27. package/web-spatial/protocol/SpatializedElementContainer.swift +8 -0
  28. package/web-spatial/protocol/WebMsgSender.swift +5 -0
  29. package/web-spatial/types/Vec2.swift +12 -0
  30. package/web-spatial/types/Vec3.swift +7 -0
  31. package/web-spatial/view/SceneHandlerUIView.swift +92 -0
  32. package/web-spatial/{views/ui/NavView.swift → view/SpatialNavView.swift} +149 -77
  33. package/web-spatial/view/SpatialSceneContentView.swift +218 -0
  34. package/web-spatial/view/SpatialSceneView.swift +53 -0
  35. package/web-spatial/view/Spatialized2DElementView.swift +96 -0
  36. package/web-spatial/view/SpatializedDynamic3DView.swift +173 -0
  37. package/web-spatial/view/SpatializedElementView.swift +178 -0
  38. package/web-spatial/view/SpatializedStatic3DView.swift +72 -0
  39. package/web-spatial/{views → view/view-modifier}/MaterialWithBorderCornerModifier.swift +28 -8
  40. package/web-spatial/webview/SpatialWebController.swift +300 -0
  41. package/web-spatial/webview/SpatialWebView.swift +34 -0
  42. package/web-spatial/webview/SpatialWebViewModel.swift +307 -0
  43. package/web-spatial.xcodeproj/project.pbxproj +126 -207
  44. package/web-spatial/libs/EventEmitter.swift +0 -25
  45. package/web-spatial/libs/SpatialComponent.swift +0 -24
  46. package/web-spatial/libs/SpatialEntity.swift +0 -172
  47. package/web-spatial/libs/SpatialInputComponent.swift +0 -19
  48. package/web-spatial/libs/SpatialMeshResource.swift +0 -12
  49. package/web-spatial/libs/SpatialModel3DComponent.swift +0 -51
  50. package/web-spatial/libs/SpatialModelComponent.swift +0 -25
  51. package/web-spatial/libs/SpatialObject.swift +0 -140
  52. package/web-spatial/libs/SpatialPhysicallyBasedMaterial.swift +0 -19
  53. package/web-spatial/libs/SpatialViewComponent.swift +0 -8
  54. package/web-spatial/libs/SpatialWindowComponent.swift +0 -454
  55. package/web-spatial/libs/SpatialWindowContainer.swift +0 -153
  56. package/web-spatial/libs/Utils/CommandManager.swift +0 -823
  57. package/web-spatial/libs/Utils/PerfClock.swift +0 -43
  58. package/web-spatial/libs/Utils/SceneManager.swift +0 -101
  59. package/web-spatial/libs/Utils/WindowContainerMgr.swift +0 -122
  60. package/web-spatial/libs/json/JsonParser.swift +0 -45
  61. package/web-spatial/libs/webView/UpdateSystem.swift +0 -26
  62. package/web-spatial/libs/webView/backend/NativeWebView.swift +0 -350
  63. package/web-spatial/views/ImmersiveView.swift +0 -17
  64. package/web-spatial/views/OpenDismissHandlerUI.swift +0 -45
  65. package/web-spatial/views/PlainWindowContainerView.swift +0 -132
  66. package/web-spatial/views/SpatialModel3DView.swift +0 -187
  67. package/web-spatial/views/SpatialViewUI.swift +0 -168
  68. package/web-spatial/views/SpatialWebViewUI.swift +0 -179
  69. package/web-spatial/views/VolumetricWindowContainerView.swift +0 -30
  70. package/web-spatial/web_spatialApp.swift +0 -141
  71. /package/web-spatial/{libs/Utils → Utils}/ColorExtension.swift +0 -0
  72. /package/web-spatial/{libs/Utils → Utils}/Logger.swift +0 -0
  73. /package/web-spatial/{views → view}/LoadingView.swift +0 -0
  74. /package/web-spatial/{views → view/view-modifier}/HideViewModifier.swift +0 -0
@@ -0,0 +1,307 @@
1
+ import SwiftUI
2
+ @preconcurrency import WebKit
3
+
4
+ @Observable
5
+ class SpatialWebViewModel {
6
+ var url = ""
7
+ private(set) var title: String?
8
+ private var view: SpatialWebView?
9
+ private var controller: SpatialWebController?
10
+ private var navigationList: [String: (_ data: URL) -> Bool] = [:]
11
+ private var openWindowList: [String: (_ data: URL) -> WebViewElementInfo?] = [:]
12
+ private var stateListeners: [SpatialWebViewState: [() -> Void]] = [:]
13
+ private var stateChangeListeners: [(_ type: SpatialWebViewState) -> Void] = []
14
+ private var scrollUpdateListeners: [(_ type: ScrollState, _ point: CGPoint) -> Void] = []
15
+ private var backgroundTransparent: Bool = false
16
+
17
+ private var _scrollEnabled = true
18
+ var scrollEnabled: Bool {
19
+ get {
20
+ return _scrollEnabled
21
+ }
22
+ set(newValue) {
23
+ _scrollEnabled = newValue
24
+ controller?.webview?.scrollView.isScrollEnabled = newValue
25
+ }
26
+ }
27
+
28
+ var scrollOffset: CGPoint = .zero
29
+
30
+ init(url: String?) {
31
+ controller = SpatialWebController()
32
+ self.url = url ?? ""
33
+ controller!.model = self
34
+ controller?.registerNavigationInvoke(invoke: onNavigationInvoke)
35
+ controller?.registerOpenWindowInvoke(invoke: onOpenWindowInvoke)
36
+ controller?.registerWebviewStateChangeInvoke(invoke: onStateChangeInvoke)
37
+ controller?.registerScrollUpdateInvoke(invoke: onScrollUpdateInvoke)
38
+ }
39
+
40
+ func load(_ configuration: WKWebViewConfiguration? = nil, _ spatialId: String? = "") {
41
+ load(url, configuration, spatialId)
42
+ }
43
+
44
+ func load(_ url: String, _ configuration: WKWebViewConfiguration? = nil, _ spatialId: String? = "") {
45
+ if controller?.webview == nil {
46
+ _ = WKWebViewManager.Instance.create(controller: controller!, configuration: configuration, spatialId: spatialId)
47
+ controller!.webview?.scrollView.isScrollEnabled = scrollEnabled
48
+ controller!.webview?.isOpaque = backgroundTransparent
49
+ }
50
+ if url.count > 0 {
51
+ controller?.webview!.load(URLRequest(url: URL(string: url)!))
52
+ controller?.startObserving()
53
+ } else {
54
+ controller!.webview?.scrollView.isScrollEnabled = scrollEnabled
55
+ controller!.webview?.isOpaque = backgroundTransparent
56
+ }
57
+ }
58
+
59
+ func loadHTML(_ htmlText: String) {
60
+ if controller?.webview == nil {
61
+ _ = WKWebViewManager.Instance.create(controller: controller!)
62
+ controller!.webview?.scrollView.isScrollEnabled = scrollEnabled
63
+ controller!.webview?.isOpaque = backgroundTransparent
64
+ }
65
+ controller?.webview!.loadHTMLString(htmlText, baseURL: nil)
66
+ }
67
+
68
+ func getView() -> SpatialWebView {
69
+ if view == nil {
70
+ // print("get spatial webview", id)
71
+ view = SpatialWebView()
72
+ view!.model = self
73
+ view?.registerWebviewStateChangeInvoke(invoke: onStateChangeInvoke)
74
+ }
75
+ return view!
76
+ }
77
+
78
+ func getController() -> SpatialWebController {
79
+ return controller!
80
+ }
81
+
82
+ func setBackgroundTransparent(_ transparent: Bool) {
83
+ controller!.webview?.isOpaque = !transparent
84
+ backgroundTransparent = !transparent
85
+ }
86
+
87
+ func stopScrolling() {
88
+ controller?.webview?.scrollView.stopScrollingAndZooming()
89
+ }
90
+
91
+ func setScrollOffset(_ offset: Vec2) {
92
+ controller?.webview?.scrollView.contentOffset.x = offset.x
93
+ controller?.webview?.scrollView.contentOffset.y = offset.y
94
+ }
95
+
96
+ func getScrollOffset() -> Vec2 {
97
+ let contentOffset = controller!.webview!.scrollView.contentOffset
98
+ return Vec2(x: contentOffset.x, y: contentOffset.y)
99
+ }
100
+
101
+ func setTitle(_ title: String) {
102
+ self.title = title
103
+ controller?.setWebViewTitle(title)
104
+ }
105
+
106
+ // events
107
+ // navigation event
108
+ func addNavigationListener(protocal: String, event: @escaping (_ data: URL) -> Bool) {
109
+ navigationList[protocal] = event
110
+ }
111
+
112
+ func removeAllNavigationListener() {
113
+ navigationList = [:]
114
+ }
115
+
116
+ // open window event
117
+ func addOpenWindowListener(protocal: String, _ event: @escaping (_ data: URL) -> WebViewElementInfo?) {
118
+ openWindowList[protocal] = event
119
+ }
120
+
121
+ func removeAllOpenWindowListener() {
122
+ openWindowList = [:]
123
+ }
124
+
125
+ // jsb event
126
+ func addJSBListener<T: CommandDataProtocol>(_ dataClass: T.Type, _ event: @escaping (T, @escaping JSBManager.ResolveHandler<Encodable>) -> Void) {
127
+ controller?.registeJSBHandler(dataClass, event)
128
+ }
129
+
130
+ func addJSBListener<T: CommandDataProtocol>(_ dataClass: T.Type, _ event: @escaping (@escaping JSBManager.ResolveHandler<Encodable>) -> Void) {
131
+ controller?.registeJSBHandler(dataClass, event)
132
+ }
133
+
134
+ func removeJSBListener<T: CommandDataProtocol>(_ dataClass: T.Type) {
135
+ controller?.unregisterJSBHandler(dataClass)
136
+ }
137
+
138
+ func removeAllJSBListener() {
139
+ controller?.clearJSBHandler()
140
+ }
141
+
142
+ func fireMockJSB(_ command: String) {
143
+ controller?.mockJSB(command)
144
+ }
145
+
146
+ // webview state event
147
+ func addStateListener(_ event: @escaping (_ type: SpatialWebViewState) -> Void) {
148
+ stateChangeListeners.append(event)
149
+ }
150
+
151
+ func addStateListener(_ state: SpatialWebViewState, _ event: @escaping () -> Void) {
152
+ if stateListeners[state] == nil {
153
+ stateListeners[state] = []
154
+ }
155
+ stateListeners[state]?.append(event)
156
+ }
157
+
158
+ func removeStateListener(_ event: @escaping (_ type: String) -> Void) {
159
+ stateChangeListeners.removeAll(where: {
160
+ $0 as AnyObject === event as AnyObject
161
+ })
162
+ }
163
+
164
+ func removeStateListener(_ state: SpatialWebViewState, _ event: @escaping (_ object: Any, _ data: Any) -> Void) {
165
+ stateListeners[state]?.removeAll(where: {
166
+ $0 as AnyObject === event as AnyObject
167
+ })
168
+ }
169
+
170
+ func removeAllStateListener() {
171
+ stateChangeListeners.removeAll()
172
+ stateListeners = [:]
173
+ }
174
+
175
+ func removeStateListener(_ state: SpatialWebViewState) {
176
+ stateListeners[state] = nil
177
+ }
178
+
179
+ // scroll update event
180
+ func addScrollUpdateListener(_ event: @escaping (_ type: ScrollState, _ point: CGPoint) -> Void) {
181
+ scrollUpdateListeners.append(event)
182
+ }
183
+
184
+ func removeScrollUpdateListener(_ event: @escaping (_ type: ScrollState, _ point: CGPoint) -> Void) {
185
+ scrollUpdateListeners.removeAll(where: {
186
+ $0 as AnyObject === event as AnyObject
187
+ })
188
+ }
189
+
190
+ func removeAllScrollUpdateListener() {
191
+ scrollUpdateListeners.removeAll()
192
+ }
193
+
194
+ func removeAllListener() {
195
+ removeAllJSBListener()
196
+ removeAllNavigationListener()
197
+ removeAllOpenWindowListener()
198
+ removeAllStateListener()
199
+ removeAllScrollUpdateListener()
200
+ }
201
+
202
+ // invokes
203
+ private func onNavigationInvoke(_ url: URL) -> Bool {
204
+ var matchProtocol = false
205
+ for key in navigationList.keys {
206
+ if url.absoluteString.starts(with: key),
207
+ let res = navigationList[key]?(url)
208
+ {
209
+ matchProtocol = res
210
+ }
211
+ }
212
+ return matchProtocol
213
+ }
214
+
215
+ private func onOpenWindowInvoke(_ url: URL) -> WebViewElementInfo? {
216
+ var protocolRes: WebViewElementInfo?
217
+ for key in openWindowList.keys {
218
+ if url.absoluteString.starts(with: key),
219
+ let res = openWindowList[key]?(url)
220
+ {
221
+ protocolRes = res
222
+ }
223
+ }
224
+ return protocolRes
225
+ }
226
+
227
+ private func onStateChangeInvoke(_ state: SpatialWebViewState) {
228
+ if state == .didMakeView, controller?.webview == nil {
229
+ load()
230
+ }
231
+ for onStateChange in stateChangeListeners {
232
+ onStateChange(state)
233
+ }
234
+ stateListeners[state]?.forEach { onStateChange in
235
+ onStateChange()
236
+ }
237
+ }
238
+
239
+ private func onScrollUpdateInvoke(_ type: ScrollState, _ point: CGPoint) {
240
+ for onScrollUpdate in scrollUpdateListeners {
241
+ onScrollUpdate(type, point)
242
+ }
243
+ }
244
+
245
+ func destroy() {
246
+ removeAllListener()
247
+ view?.destroy()
248
+ controller?.destroy()
249
+ controller = nil
250
+ view = nil
251
+ }
252
+
253
+ func sendWebEvent<T: Encodable>(_ id: String, _ data: T) {
254
+ let encoder = JSONEncoder()
255
+ if let jsonData = try? encoder.encode(data) {
256
+ let dataString = String(data: jsonData, encoding: .utf8)
257
+ controller?.callJS("window.__SpatialWebEvent && window.__SpatialWebEvent({id:'" + id + "', data: " + dataString! + "})")
258
+ }
259
+ }
260
+
261
+ func updateWindowKV(_ dict: [String: Any]) {
262
+ var js = ""
263
+ for (key, value) in dict {
264
+ if let num = value as? Double {
265
+ js += "window.\(key)=\(num);"
266
+ } else if let str = value as? String {
267
+ js += "window.\(key)='\(str)';"
268
+ } else if let bool = value as? Bool {
269
+ js += "window.\(key)=\(bool ? "true" : "false");"
270
+ }
271
+ }
272
+ controller?.callJS(js)
273
+ }
274
+
275
+ func evaluateJS(_ js: String, completion: @escaping (Any?) -> Void) {
276
+ guard let webview = controller?.webview else {
277
+ completion(nil)
278
+ return
279
+ }
280
+
281
+ webview.evaluateJavaScript(js) { result, error in
282
+ if let error = error {
283
+ print("❌ evaluateJavaScript JS error:", error.localizedDescription)
284
+ completion(nil)
285
+ return
286
+ }
287
+ completion(result)
288
+ }
289
+ }
290
+ }
291
+
292
+ enum SpatialWebViewState: String, CaseIterable {
293
+ case didStartLoad
294
+ case didReceive
295
+ case didFinishLoad
296
+ case didFailLoad
297
+ case didUnload
298
+ case didClose
299
+ case didMakeView
300
+ case didUpdateView
301
+ case didDestroyView
302
+ }
303
+
304
+ struct WebViewElementInfo {
305
+ var id: String
306
+ var element: SpatialWebViewModel
307
+ }