@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.
- package/package.json +2 -2
- package/web-spatial/EventEmitter.swift +56 -0
- package/web-spatial/JSBCommand.swift +348 -0
- package/web-spatial/SpatialObject.swift +108 -0
- package/web-spatial/WebMsgCommand.swift +137 -0
- package/web-spatial/WebSpatialApp.swift +111 -0
- package/web-spatial/{libs/uiKitDelegate/Window.swift → Window.swift} +1 -0
- package/web-spatial/manager/Dynamic3DManager.swift +114 -0
- package/web-spatial/manager/JSBManager.swift +148 -0
- package/web-spatial/manager/WKWebViewManager.swift +39 -0
- package/web-spatial/{libs/webView/manifest.swift → manifest.swift} +16 -5
- package/web-spatial/model/SpatialApp.swift +190 -0
- package/web-spatial/model/SpatialScene.swift +1078 -0
- package/web-spatial/model/Spatialized2DElement.swift +128 -0
- package/web-spatial/model/SpatializedDynamic3DElement.swift +34 -0
- package/web-spatial/model/SpatializedElement.swift +95 -0
- package/web-spatial/model/SpatializedStatic3DElement.swift +19 -0
- package/web-spatial/model/dynamic3d/Geometry.swift +95 -0
- package/web-spatial/model/dynamic3d/SpatialComponent.swift +72 -0
- package/web-spatial/model/dynamic3d/SpatialEntity.swift +211 -0
- package/web-spatial/model/dynamic3d/SpatialMaterial.swift +39 -0
- package/web-spatial/model/dynamic3d/SpatialModelEntity.swift +39 -0
- package/web-spatial/model/dynamic3d/SpatialModelResource.swift +38 -0
- package/web-spatial/model/dynamic3d/SpatialTextureResource.swift +18 -0
- package/web-spatial/protocol/ScrollAbleSpatialElementContainer.swift +1 -0
- package/web-spatial/protocol/SpatialScrollAble.swift +8 -0
- package/web-spatial/protocol/SpatializedElementContainer.swift +8 -0
- package/web-spatial/protocol/WebMsgSender.swift +5 -0
- package/web-spatial/types/Vec2.swift +12 -0
- package/web-spatial/types/Vec3.swift +7 -0
- package/web-spatial/view/SceneHandlerUIView.swift +92 -0
- package/web-spatial/{views/ui/NavView.swift → view/SpatialNavView.swift} +149 -77
- package/web-spatial/view/SpatialSceneContentView.swift +218 -0
- package/web-spatial/view/SpatialSceneView.swift +53 -0
- package/web-spatial/view/Spatialized2DElementView.swift +96 -0
- package/web-spatial/view/SpatializedDynamic3DView.swift +173 -0
- package/web-spatial/view/SpatializedElementView.swift +178 -0
- package/web-spatial/view/SpatializedStatic3DView.swift +72 -0
- package/web-spatial/{views → view/view-modifier}/MaterialWithBorderCornerModifier.swift +28 -8
- package/web-spatial/webview/SpatialWebController.swift +300 -0
- package/web-spatial/webview/SpatialWebView.swift +34 -0
- package/web-spatial/webview/SpatialWebViewModel.swift +307 -0
- package/web-spatial.xcodeproj/project.pbxproj +126 -207
- package/web-spatial/libs/EventEmitter.swift +0 -25
- package/web-spatial/libs/SpatialComponent.swift +0 -24
- package/web-spatial/libs/SpatialEntity.swift +0 -172
- package/web-spatial/libs/SpatialInputComponent.swift +0 -19
- package/web-spatial/libs/SpatialMeshResource.swift +0 -12
- package/web-spatial/libs/SpatialModel3DComponent.swift +0 -51
- package/web-spatial/libs/SpatialModelComponent.swift +0 -25
- package/web-spatial/libs/SpatialObject.swift +0 -140
- package/web-spatial/libs/SpatialPhysicallyBasedMaterial.swift +0 -19
- package/web-spatial/libs/SpatialViewComponent.swift +0 -8
- package/web-spatial/libs/SpatialWindowComponent.swift +0 -454
- package/web-spatial/libs/SpatialWindowContainer.swift +0 -153
- package/web-spatial/libs/Utils/CommandManager.swift +0 -823
- package/web-spatial/libs/Utils/PerfClock.swift +0 -43
- package/web-spatial/libs/Utils/SceneManager.swift +0 -101
- package/web-spatial/libs/Utils/WindowContainerMgr.swift +0 -122
- package/web-spatial/libs/json/JsonParser.swift +0 -45
- package/web-spatial/libs/webView/UpdateSystem.swift +0 -26
- package/web-spatial/libs/webView/backend/NativeWebView.swift +0 -350
- package/web-spatial/views/ImmersiveView.swift +0 -17
- package/web-spatial/views/OpenDismissHandlerUI.swift +0 -45
- package/web-spatial/views/PlainWindowContainerView.swift +0 -132
- package/web-spatial/views/SpatialModel3DView.swift +0 -187
- package/web-spatial/views/SpatialViewUI.swift +0 -168
- package/web-spatial/views/SpatialWebViewUI.swift +0 -179
- package/web-spatial/views/VolumetricWindowContainerView.swift +0 -30
- package/web-spatial/web_spatialApp.swift +0 -141
- /package/web-spatial/{libs/Utils → Utils}/ColorExtension.swift +0 -0
- /package/web-spatial/{libs/Utils → Utils}/Logger.swift +0 -0
- /package/web-spatial/{views → view}/LoadingView.swift +0 -0
- /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
|
+
}
|