@webspatial/platform-visionos 1.0.4 → 1.0.5

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 +119 -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 +1042 -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 +104 -0
  37. package/web-spatial/view/SpatializedElementView.swift +178 -0
  38. package/web-spatial/view/SpatializedStatic3DView.swift +70 -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,70 @@
1
+ import RealityKit
2
+ import SwiftUI
3
+
4
+ struct SpatializedStatic3DView: View {
5
+ @Environment(SpatializedElement.self) var spatializedElement: SpatializedElement
6
+ @Environment(SpatialScene.self) var spatialScene: SpatialScene
7
+
8
+ private var spatializedStatic3DElement: SpatializedStatic3DElement {
9
+ return spatializedElement as! SpatializedStatic3DElement
10
+ }
11
+
12
+ func onLoadSuccess() {
13
+ spatialScene.sendWebMsg(spatializedElement.id, ModelLoadSuccess())
14
+ }
15
+
16
+ func onLoadFailure() {
17
+ spatialScene.sendWebMsg(spatializedElement.id, ModelLoadFailure())
18
+ }
19
+
20
+ @ViewBuilder
21
+ var body: some View {
22
+ let transform = spatializedStatic3DElement.modelTransform
23
+ let translation = transform.translation
24
+ let scale = transform.scale
25
+ let rotation = transform.rotation!
26
+ let x = translation.x
27
+ let y = translation.y
28
+ let z = translation.z
29
+
30
+ let enableGesture = spatializedElement.enableGesture
31
+ if let url = URL(string: spatializedStatic3DElement.modelURL) {
32
+ Model3D(url: url) { newPhase in
33
+ switch newPhase {
34
+ case .empty:
35
+ ProgressView()
36
+
37
+ case let .success(resolvedModel3D):
38
+ resolvedModel3D
39
+ .resizable(true)
40
+ .aspectRatio(
41
+ nil,
42
+ contentMode: .fit
43
+ )
44
+ .onAppear {
45
+ self.onLoadSuccess()
46
+ }
47
+ .if(enableGesture) { view in view.hoverEffect()}
48
+ case .failure:
49
+ Text("").onAppear {
50
+ self.onLoadFailure()
51
+ }
52
+ @unknown default:
53
+ EmptyView()
54
+ }
55
+ }
56
+ .scaleEffect(
57
+ x: scale.width,
58
+ y: scale.height,
59
+ z: scale.depth
60
+ )
61
+ .rotation3DEffect(
62
+ rotation
63
+ )
64
+ .offset(x: x, y: y)
65
+ .offset(z: z)
66
+ } else {
67
+ EmptyView()
68
+ }
69
+ }
70
+ }
@@ -49,10 +49,17 @@ struct CornerRadius: Codable {
49
49
  struct MaterialWithBorderCornerModifier: ViewModifier {
50
50
  let backgroundMaterial: BackgroundMaterial
51
51
  let cornerRadius: CornerRadius
52
+ let windowStyle: SpatialScene.WindowStyle
52
53
 
53
- init(_ backgroundMaterial: BackgroundMaterial, _ cornerRadius: CornerRadius) {
54
+ init(
55
+ _ backgroundMaterial: BackgroundMaterial,
56
+ _ cornerRadius: CornerRadius,
57
+ _ windowStyle: SpatialScene
58
+ .WindowStyle
59
+ ) {
54
60
  self.backgroundMaterial = backgroundMaterial
55
61
  self.cornerRadius = cornerRadius
62
+ self.windowStyle = windowStyle
56
63
  }
57
64
 
58
65
  func body(content: Content) -> some View {
@@ -60,11 +67,20 @@ struct MaterialWithBorderCornerModifier: ViewModifier {
60
67
 
61
68
  switch backgroundMaterial {
62
69
  case .GlassMaterial:
63
- content
64
- .glassBackgroundEffect(
65
- in: .rect(cornerRadii: radii),
66
- displayMode: .always
67
- )
70
+ if windowStyle == .volume {
71
+ content
72
+ .glassBackgroundEffect(
73
+ in: .rect(cornerRadii: radii),
74
+ displayMode: .always
75
+ )
76
+ } else {
77
+ content
78
+ .glassBackgroundEffect(
79
+ in: .rect(cornerRadii: radii),
80
+ displayMode: .always
81
+ )
82
+ .frame(depth: 0)
83
+ }
68
84
 
69
85
  case .RegularMaterial:
70
86
  content
@@ -89,9 +105,13 @@ struct MaterialWithBorderCornerModifier: ViewModifier {
89
105
  }
90
106
 
91
107
  extension View {
92
- func materialWithBorderCorner(_ backgroundMaterial: BackgroundMaterial, _ cornerRadius: CornerRadius) -> some View {
108
+ func materialWithBorderCorner(_ backgroundMaterial: BackgroundMaterial, _ cornerRadius: CornerRadius, _ windowStyle: SpatialScene.WindowStyle) -> some View {
93
109
  return modifier(
94
- MaterialWithBorderCornerModifier(backgroundMaterial, cornerRadius)
110
+ MaterialWithBorderCornerModifier(
111
+ backgroundMaterial,
112
+ cornerRadius,
113
+ windowStyle
114
+ )
95
115
  )
96
116
  }
97
117
  }
@@ -0,0 +1,300 @@
1
+ import SwiftUI
2
+ @preconcurrency import WebKit
3
+
4
+ class SpatialWebController: NSObject, WKNavigationDelegate, WKScriptMessageHandlerWithReply, WKUIDelegate, UIScrollViewDelegate, WKURLSchemeHandler {
5
+ weak var model: SpatialWebViewModel?
6
+ var webview: WKWebView?
7
+ private var isObserving = false
8
+ private var navigationInvoke: ((_ data: URL) -> Bool)?
9
+ private var openWindowInvoke: ((_ data: URL) -> WebViewElementInfo?)?
10
+ private var webviewStateChangeInvoke: ((_ type: SpatialWebViewState) -> Void)?
11
+ private var scorllUpdateInvoke: ((_ type: ScrollState, _ point: CGPoint) -> Void)?
12
+ private var webviewTitle: String? = nil
13
+ private var firstLoad = true
14
+ private var jsbManager = JSBManager()
15
+
16
+ override init() {
17
+ WKWebView.enableFileScheme() // ensure the handler is usable
18
+ }
19
+
20
+ deinit {}
21
+
22
+ func registerNavigationInvoke(invoke: @escaping (_ data: URL) -> Bool) {
23
+ navigationInvoke = invoke
24
+ }
25
+
26
+ func registerOpenWindowInvoke(invoke: @escaping (_ data: URL) -> WebViewElementInfo?) {
27
+ openWindowInvoke = invoke
28
+ }
29
+
30
+ func registeJSBHandler<T: CommandDataProtocol>(_ type: T.Type, _ event: @escaping (T, @escaping JSBManager.ResolveHandler<Encodable>) -> Void) {
31
+ jsbManager.register(type, event)
32
+ }
33
+
34
+ func registeJSBHandler<T: CommandDataProtocol>(_ type: T.Type, _ event: @escaping (@escaping JSBManager.ResolveHandler<Encodable>) -> Void) {
35
+ jsbManager.register(type, event)
36
+ }
37
+
38
+ func unregisterJSBHandler<T: CommandDataProtocol>(_ type: T.Type) {
39
+ jsbManager.remove(type)
40
+ }
41
+
42
+ func clearJSBHandler() {
43
+ jsbManager.clear()
44
+ }
45
+
46
+ func mockJSB(_ command: String) {
47
+ jsbManager.handlerMessage(command)
48
+ }
49
+
50
+ func registerWebviewStateChangeInvoke(invoke: @escaping (_ type: SpatialWebViewState) -> Void) {
51
+ webviewStateChangeInvoke = invoke
52
+ }
53
+
54
+ func registerScrollUpdateInvoke(invoke: @escaping (_ type: ScrollState, _ point: CGPoint) -> Void) {
55
+ scorllUpdateInvoke = invoke
56
+ }
57
+
58
+ func setWebViewTitle(_ title: String) {
59
+ webviewTitle = title
60
+ if webview != nil {
61
+ callJS("document.title='\(title)'")
62
+ }
63
+ }
64
+
65
+ // navigation request
66
+ // SpatialDiv/forcestyle/normal web link protocol
67
+ func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Swift.Void) {
68
+ let deciside = navigationInvoke?(navigationAction.request.url!)
69
+ if deciside == true {
70
+ if !firstLoad{
71
+ webviewStateChangeInvoke?(.didUnload)
72
+ }
73
+ firstLoad = false
74
+ }
75
+ var needAllow = deciside ?? false
76
+
77
+ if !needAllow{
78
+ UIApplication.shared.open(navigationAction.request.url!, options: [:], completionHandler: nil)
79
+ }
80
+ decisionHandler(needAllow ? .allow : .cancel)
81
+ }
82
+
83
+ // open window request
84
+ func webView(
85
+ _ webView: WKWebView,
86
+ createWebViewWith configuration: WKWebViewConfiguration,
87
+ for navigationAction: WKNavigationAction,
88
+ windowFeatures: WKWindowFeatures
89
+ ) -> WKWebView? {
90
+ if let modelInfo = openWindowInvoke?(navigationAction.request.url!) {
91
+ modelInfo.element.load(configuration, modelInfo.id)
92
+ return modelInfo.element.getController().webview
93
+ }
94
+ print("no webview")
95
+ return nil
96
+ }
97
+
98
+ // invoke jsb
99
+ func userContentController(
100
+ _ userContentController: WKUserContentController,
101
+ didReceive message: WKScriptMessage, replyHandler: @escaping (Any?, String?) -> Void
102
+ ) {
103
+ // let promise = JSBManager.Promise(replyHandler)
104
+ jsbManager.handlerMessage(message.body as! String, replyHandler)
105
+ }
106
+
107
+ // custom scheme request
108
+ func webView(_ webView: WKWebView, start urlSchemeTask: any WKURLSchemeTask) {
109
+ print("urlSchemeTask")
110
+ let url = urlSchemeTask.request.url
111
+ if url!.absoluteString.starts(with: "file://") {
112
+ let urlRequest = urlSchemeTask.request
113
+
114
+ let session = URLSession(configuration: URLSessionConfiguration.default)
115
+ let dataTask = session.dataTask(with: urlRequest) { [task = urlSchemeTask as AnyObject] data, response, _ in
116
+ guard let task = task as? WKURLSchemeTask else { return }
117
+
118
+ task.didReceive(response!)
119
+ task.didReceive(data!)
120
+ task.didFinish()
121
+ }
122
+ dataTask.resume()
123
+ }
124
+ }
125
+
126
+ func webView(_ webView: WKWebView, stop urlSchemeTask: any WKURLSchemeTask) {}
127
+ func webView(_ webView: WKWebView, didStartProvisionalNavigation: WKNavigation!) {
128
+ webviewStateChangeInvoke?(.didStartLoad)
129
+ }
130
+
131
+ func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
132
+ webviewStateChangeInvoke?(.didReceive)
133
+ }
134
+
135
+ func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
136
+ webviewStateChangeInvoke?(.didFinishLoad)
137
+ if webviewTitle != nil {
138
+ callJS("document.title='\(webviewTitle!)'")
139
+ }
140
+ // flush pending calljs comand
141
+ isPageLoaded = true
142
+ flushJSQueue()
143
+ }
144
+
145
+ func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Swift.Void) {
146
+ decisionHandler(.allow)
147
+ }
148
+
149
+ func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
150
+ if let urlError = (error as? URLError) {
151
+ if urlError.code == .cannotConnectToHost {
152
+ webviewStateChangeInvoke?(.didFailLoad)
153
+ }
154
+ }
155
+ }
156
+
157
+ func webViewDidClose(_ webView: WKWebView) {
158
+ webviewStateChangeInvoke?(.didClose)
159
+ }
160
+
161
+ func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
162
+ guard let serverTrust = challenge.protectionSpace.serverTrust else { return completionHandler(.useCredential, nil) }
163
+ let exceptions = SecTrustCopyExceptions(serverTrust)
164
+ SecTrustSetExceptions(serverTrust, exceptions)
165
+ completionHandler(.useCredential, URLCredential(trust: serverTrust))
166
+ }
167
+
168
+ func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
169
+ scorllUpdateInvoke?(.start, scrollView.contentOffset)
170
+ }
171
+
172
+ func scrollViewDidScroll(_ scrollView: UIScrollView) {
173
+ scorllUpdateInvoke?(.update, scrollView.contentOffset)
174
+ }
175
+
176
+ func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
177
+ scorllUpdateInvoke?(.end, scrollView.contentOffset)
178
+ }
179
+
180
+ func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
181
+ if !decelerate {
182
+ scorllUpdateInvoke?(.end, scrollView.contentOffset)
183
+ } else {
184
+ scorllUpdateInvoke?(.release, scrollView.contentOffset)
185
+ }
186
+ }
187
+
188
+ func startObserving() {
189
+ guard !isObserving else { return }
190
+ webview?.addObserver(self, forKeyPath: #keyPath(WKWebView.url), options: .new, context: nil)
191
+ isObserving = true
192
+ }
193
+
194
+ func stopObserving() {
195
+ guard isObserving else { return }
196
+ webview?.removeObserver(self, forKeyPath: #keyPath(WKWebView.url))
197
+ isObserving = false
198
+ }
199
+
200
+ override func observeValue(
201
+ forKeyPath keyPath: String?,
202
+ of object: Any?,
203
+ change: [NSKeyValueChangeKey: Any]?,
204
+ context: UnsafeMutableRawPointer?
205
+ ) {
206
+ if keyPath == #keyPath(WKWebView.url),
207
+ let url = (object as? WKWebView)?.url?.absoluteString
208
+ {
209
+ DispatchQueue.main.async {
210
+ // print("url change", url)
211
+ self.model?.url = url
212
+ }
213
+ }
214
+ }
215
+
216
+ func destroy() {
217
+ destroyView()
218
+ navigationInvoke = nil
219
+ openWindowInvoke = nil
220
+ webviewStateChangeInvoke = nil
221
+ scorllUpdateInvoke = nil
222
+ model = nil
223
+ }
224
+
225
+ private var state:SpatialWebViewState?
226
+
227
+ func destroyView() {
228
+ stopObserving()
229
+ if webview != nil {
230
+ webview?.stopLoading()
231
+ webview?.configuration.userContentController.removeScriptMessageHandler(forName: "bridge")
232
+ webview?.uiDelegate = nil
233
+ webview?.navigationDelegate = nil
234
+ webview?.scrollView.delegate = nil
235
+ webview = nil
236
+ webviewStateChangeInvoke?(.didDestroyView)
237
+ }
238
+ }
239
+
240
+ private var isPageLoaded = false
241
+
242
+ private var jsQueue: [String] = []
243
+
244
+ private func enqueueJS(_ js: String) {
245
+ jsQueue.append(js)
246
+ }
247
+
248
+ private func flushJSQueue() {
249
+ guard !jsQueue.isEmpty else { return }
250
+ let combined = jsQueue.joined(separator: ";")
251
+ callJS(combined)
252
+ jsQueue.removeAll()
253
+ }
254
+
255
+ func callJS(_ js: String) {
256
+ if webview != nil && isPageLoaded {
257
+ webview!.evaluateJavaScript(js)
258
+ } else {
259
+ enqueueJS(js)
260
+ }
261
+ }
262
+ }
263
+
264
+ enum ScrollState {
265
+ case start
266
+ case update
267
+ case release
268
+ case end
269
+ }
270
+
271
+ // extend webview to support file://
272
+ @available(iOS 11.0, *)
273
+ extension WKWebView {
274
+ /// WKWebView, Support setting file scheme in configuration
275
+ public private(set) static var isEnableFileSupport = false
276
+ public static func enableFileScheme() {
277
+ /// This method supports adapting supported files through Configuration, but cannot be cancelled (Configuration is immutable).
278
+ if !isEnableFileSupport {
279
+ switchHandlesURLScheme()
280
+ }
281
+ }
282
+
283
+ private static func switchHandlesURLScheme() {
284
+ if
285
+ case let cls = WKWebView.self,
286
+ let m1 = class_getClassMethod(cls, NSSelectorFromString("handlesURLScheme:")),
287
+ let m2 = class_getClassMethod(cls, #selector(WKWebView.wrapHandles(urlScheme:)))
288
+ {
289
+ method_exchangeImplementations(m1, m2)
290
+ isEnableFileSupport = !isEnableFileSupport
291
+ }
292
+ }
293
+
294
+ /// Return true if WKWebview supports handling this protocol, but WKWebview supports HTTP by default, so return false to support using custom HTTP Handler
295
+ @objc private dynamic
296
+ static func wrapHandles(urlScheme: String) -> Bool {
297
+ if urlScheme == "file" { return false }
298
+ return wrapHandles(urlScheme: urlScheme)
299
+ }
300
+ }
@@ -0,0 +1,34 @@
1
+ import SwiftUI
2
+ @preconcurrency import WebKit
3
+
4
+ struct SpatialWebView: UIViewRepresentable {
5
+ weak var model: SpatialWebViewModel? = nil
6
+ var url: URL = .init(filePath: "/")
7
+ private var webviewStateChangeInvoke: ((_ type: SpatialWebViewState) -> Void)?
8
+
9
+ func makeUIView(context: Context) -> WKWebView {
10
+ webviewStateChangeInvoke?(.didMakeView)
11
+ return model!.getController().webview!
12
+ }
13
+
14
+ func makeCoordinator() -> SpatialWebController {
15
+ return model!.getController()
16
+ }
17
+
18
+ func updateUIView(_ webView: WKWebView, context: Context) {
19
+ webviewStateChangeInvoke?(.didUpdateView)
20
+ }
21
+
22
+ mutating func registerWebviewStateChangeInvoke(invoke: @escaping (_ type: SpatialWebViewState) -> Void) {
23
+ webviewStateChangeInvoke = invoke
24
+ }
25
+
26
+ mutating func destroy() {
27
+ webviewStateChangeInvoke = nil
28
+ model = nil
29
+ }
30
+
31
+ static func dismantleUIView(_ uiView: WKWebView, coordinator: SpatialWebController) {
32
+ // print("dismantleUIView", coordinator.model?.id)
33
+ }
34
+ }