@webspatial/platform-visionos 0.1.22 → 1.0.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 (39) hide show
  1. package/package.json +1 -1
  2. package/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json +1 -0
  3. package/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/icon-default.png +0 -0
  4. package/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json +1 -0
  5. package/web-spatial/Assets.xcassets/arrow_left.imageset/Contents.json +21 -0
  6. package/web-spatial/Assets.xcassets/arrow_left.imageset/arrow_left.png +0 -0
  7. package/web-spatial/Assets.xcassets/arrow_right.imageset/Contents.json +21 -0
  8. package/web-spatial/Assets.xcassets/arrow_right.imageset/arrow_right.png +0 -0
  9. package/web-spatial/Assets.xcassets/back.imageset/Contents.json +21 -0
  10. package/web-spatial/Assets.xcassets/back.imageset/back.png +0 -0
  11. package/web-spatial/Assets.xcassets/browser.imageset/Contents.json +21 -0
  12. package/web-spatial/Assets.xcassets/browser.imageset/browser.png +0 -0
  13. package/web-spatial/Assets.xcassets/close.imageset/Contents.json +21 -0
  14. package/web-spatial/Assets.xcassets/close.imageset/close.png +0 -0
  15. package/web-spatial/Assets.xcassets/copy.imageset/Contents.json +21 -0
  16. package/web-spatial/Assets.xcassets/copy.imageset/copy.png +0 -0
  17. package/web-spatial/Assets.xcassets/home.imageset/Contents.json +21 -0
  18. package/web-spatial/Assets.xcassets/home.imageset/home.png +0 -0
  19. package/web-spatial/Assets.xcassets/link.imageset/Contents.json +21 -0
  20. package/web-spatial/Assets.xcassets/link.imageset/link.png +0 -0
  21. package/web-spatial/Assets.xcassets/logo.imageset/Contents.json +1 -0
  22. package/web-spatial/Assets.xcassets/logo.imageset/icon-default.png +0 -0
  23. package/web-spatial/Assets.xcassets/more.imageset/Contents.json +21 -0
  24. package/web-spatial/Assets.xcassets/more.imageset/more.png +0 -0
  25. package/web-spatial/Assets.xcassets/nav.imageset/nav.png +0 -0
  26. package/web-spatial/Assets.xcassets/refresh.imageset/Contents.json +21 -0
  27. package/web-spatial/Assets.xcassets/refresh.imageset/refresh.png +0 -0
  28. package/web-spatial/libs/Utils/ColorExtension.swift +32 -0
  29. package/web-spatial/libs/Utils/CommandManager.swift +1 -0
  30. package/web-spatial/libs/Utils/WindowContainerMgr.swift +21 -3
  31. package/web-spatial/libs/webView/backend/NativeWebView.swift +33 -1
  32. package/web-spatial/libs/webView/manifest.swift +1 -1
  33. package/web-spatial/views/LoadingView.swift +0 -1
  34. package/web-spatial/views/PlainWindowContainerView.swift +50 -2
  35. package/web-spatial/views/SpatialWebViewUI.swift +124 -138
  36. package/web-spatial/views/ui/NavView.swift +119 -72
  37. package/web-spatial.xcodeproj/project.pbxproj +4 -4
  38. package/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/icon.png +0 -0
  39. package/web-spatial/nav.png +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webspatial/platform-visionos",
3
- "version": "0.1.22",
3
+ "version": "1.0.0",
4
4
  "description": "Used to publish WebSpatial projects to Apple Vision Pro",
5
5
  "type": "commonjs",
6
6
  "engines": {
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "images": [
3
3
  {
4
+ "filename": "icon-default.png",
4
5
  "idiom": "vision",
5
6
  "scale": "2x"
6
7
  }
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "images": [
3
3
  {
4
+ "filename": "icon.png",
4
5
  "idiom": "vision",
5
6
  "scale": "2x"
6
7
  }
@@ -0,0 +1,21 @@
1
+ {
2
+ "images": [
3
+ {
4
+ "filename": "arrow_left.png",
5
+ "idiom": "universal",
6
+ "scale": "1x"
7
+ },
8
+ {
9
+ "idiom": "universal",
10
+ "scale": "2x"
11
+ },
12
+ {
13
+ "idiom": "universal",
14
+ "scale": "3x"
15
+ }
16
+ ],
17
+ "info": {
18
+ "author": "xcode",
19
+ "version": 1
20
+ }
21
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "images": [
3
+ {
4
+ "filename": "arrow_right.png",
5
+ "idiom": "universal",
6
+ "scale": "1x"
7
+ },
8
+ {
9
+ "idiom": "universal",
10
+ "scale": "2x"
11
+ },
12
+ {
13
+ "idiom": "universal",
14
+ "scale": "3x"
15
+ }
16
+ ],
17
+ "info": {
18
+ "author": "xcode",
19
+ "version": 1
20
+ }
21
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "images": [
3
+ {
4
+ "filename": "back.png",
5
+ "idiom": "universal",
6
+ "scale": "1x"
7
+ },
8
+ {
9
+ "idiom": "universal",
10
+ "scale": "2x"
11
+ },
12
+ {
13
+ "idiom": "universal",
14
+ "scale": "3x"
15
+ }
16
+ ],
17
+ "info": {
18
+ "author": "xcode",
19
+ "version": 1
20
+ }
21
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "images": [
3
+ {
4
+ "filename": "browser.png",
5
+ "idiom": "universal",
6
+ "scale": "1x"
7
+ },
8
+ {
9
+ "idiom": "universal",
10
+ "scale": "2x"
11
+ },
12
+ {
13
+ "idiom": "universal",
14
+ "scale": "3x"
15
+ }
16
+ ],
17
+ "info": {
18
+ "author": "xcode",
19
+ "version": 1
20
+ }
21
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "images": [
3
+ {
4
+ "filename": "close.png",
5
+ "idiom": "universal",
6
+ "scale": "1x"
7
+ },
8
+ {
9
+ "idiom": "universal",
10
+ "scale": "2x"
11
+ },
12
+ {
13
+ "idiom": "universal",
14
+ "scale": "3x"
15
+ }
16
+ ],
17
+ "info": {
18
+ "author": "xcode",
19
+ "version": 1
20
+ }
21
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "images": [
3
+ {
4
+ "filename": "copy.png",
5
+ "idiom": "universal",
6
+ "scale": "1x"
7
+ },
8
+ {
9
+ "idiom": "universal",
10
+ "scale": "2x"
11
+ },
12
+ {
13
+ "idiom": "universal",
14
+ "scale": "3x"
15
+ }
16
+ ],
17
+ "info": {
18
+ "author": "xcode",
19
+ "version": 1
20
+ }
21
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "images": [
3
+ {
4
+ "filename": "home.png",
5
+ "idiom": "universal",
6
+ "scale": "1x"
7
+ },
8
+ {
9
+ "idiom": "universal",
10
+ "scale": "2x"
11
+ },
12
+ {
13
+ "idiom": "universal",
14
+ "scale": "3x"
15
+ }
16
+ ],
17
+ "info": {
18
+ "author": "xcode",
19
+ "version": 1
20
+ }
21
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "images": [
3
+ {
4
+ "filename": "link.png",
5
+ "idiom": "universal",
6
+ "scale": "1x"
7
+ },
8
+ {
9
+ "idiom": "universal",
10
+ "scale": "2x"
11
+ },
12
+ {
13
+ "idiom": "universal",
14
+ "scale": "3x"
15
+ }
16
+ ],
17
+ "info": {
18
+ "author": "xcode",
19
+ "version": 1
20
+ }
21
+ }
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "images": [
3
3
  {
4
+ "filename": "icon-default.png",
4
5
  "idiom": "universal",
5
6
  "scale": "1x"
6
7
  },
@@ -0,0 +1,21 @@
1
+ {
2
+ "images": [
3
+ {
4
+ "filename": "more.png",
5
+ "idiom": "universal",
6
+ "scale": "1x"
7
+ },
8
+ {
9
+ "idiom": "universal",
10
+ "scale": "2x"
11
+ },
12
+ {
13
+ "idiom": "universal",
14
+ "scale": "3x"
15
+ }
16
+ ],
17
+ "info": {
18
+ "author": "xcode",
19
+ "version": 1
20
+ }
21
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "images": [
3
+ {
4
+ "filename": "refresh.png",
5
+ "idiom": "universal",
6
+ "scale": "1x"
7
+ },
8
+ {
9
+ "idiom": "universal",
10
+ "scale": "2x"
11
+ },
12
+ {
13
+ "idiom": "universal",
14
+ "scale": "3x"
15
+ }
16
+ ],
17
+ "info": {
18
+ "author": "xcode",
19
+ "version": 1
20
+ }
21
+ }
@@ -0,0 +1,32 @@
1
+ import SwiftUI
2
+
3
+ extension Color {
4
+ init(hex: String) {
5
+ let hex = hex.trimmingCharacters(in: .whitespacesAndNewlines)
6
+ .replacingOccurrences(of: "#", with: "")
7
+
8
+ guard hex.count == 6 || hex.count == 8 else {
9
+ self.init(.sRGB, red: 0, green: 0, blue: 0, opacity: 1)
10
+ return
11
+ }
12
+
13
+ let scanner = Scanner(string: hex)
14
+ var hexNumber: UInt64 = 0
15
+ scanner.scanHexInt64(&hexNumber)
16
+
17
+ let r, g, b, a: Double
18
+ if hex.count == 8 {
19
+ r = Double((hexNumber & 0xFF00_0000) >> 24) / 255
20
+ g = Double((hexNumber & 0x00FF_0000) >> 16) / 255
21
+ b = Double((hexNumber & 0x0000_FF00) >> 8) / 255
22
+ a = Double(hexNumber & 0x0000_00FF) / 255
23
+ } else {
24
+ a = 1.0
25
+ r = Double((hexNumber & 0xFF0000) >> 16) / 255
26
+ g = Double((hexNumber & 0x00FF00) >> 8) / 255
27
+ b = Double(hexNumber & 0x0000FF) / 255
28
+ }
29
+
30
+ self.init(.sRGB, red: r, green: g, blue: b, opacity: a)
31
+ }
32
+ }
@@ -617,6 +617,7 @@ class CommandManager {
617
617
  // Update scene state
618
618
  var cfg = WindowContainerPlainDefaultValues()
619
619
  cfg.defaultSize = CGSize(width: resolution.width, height: resolution.height)
620
+ // TODO: need set resizeRange?
620
621
  WindowContainerMgr.Instance.updateWindowContainerPlainDefaultValues(cfg)
621
622
  return
622
623
  }
@@ -16,6 +16,10 @@ struct WindowContainerData: Decodable, Hashable, Encodable {
16
16
  let windowContainerID: String
17
17
  }
18
18
 
19
+ struct WindowContainerResizability: Decodable, Encodable {
20
+ let resizeRange: ResizeRange?
21
+ }
22
+
19
23
  enum LoadingMethod: String, Decodable, Encodable, Hashable {
20
24
  case show
21
25
  case hide
@@ -29,6 +33,7 @@ struct LoadingWindowContainerData: Decodable, Hashable, Encodable {
29
33
  struct WindowContainerPlainDefaultValues {
30
34
  var defaultSize: CGSize?
31
35
  var windowResizability: WindowResizability?
36
+ var resizeRange: ResizeRange?
32
37
  }
33
38
 
34
39
  // support WindowContainerOptions => WindowContainerPlainDefaultValues
@@ -38,19 +43,28 @@ extension WindowContainerPlainDefaultValues {
38
43
  width: options.defaultSize?.width ?? DefaultPlainWindowContainerSize.width,
39
44
  height: options.defaultSize?.height ?? DefaultPlainWindowContainerSize.height
40
45
  )
41
- windowResizability = getWindowResizability(options.resizability)
46
+ windowResizability = getWindowResizability(nil)
47
+ resizeRange = options.resizability
42
48
  }
43
49
  }
44
50
 
51
+ struct ResizeRange: Codable {
52
+ var minWidth: Double?
53
+ var minHeight: Double?
54
+ var maxWidth: Double?
55
+ var maxHeight: Double?
56
+ }
57
+
45
58
  // incomming JSB data
46
59
  struct WindowContainerOptions: Codable {
47
60
  // windowContainer
48
61
  let defaultSize: Size?
49
- let resizability: String?
50
62
  struct Size: Codable {
51
63
  var width: Double
52
64
  var height: Double
53
65
  }
66
+
67
+ let resizability: ResizeRange?
54
68
  }
55
69
 
56
70
  func getWindowResizability(_ windowResizability: String?) -> WindowResizability {
@@ -79,7 +93,8 @@ class WindowContainerMgr: ObservableObject {
79
93
 
80
94
  private var wgSetting: WindowContainerPlainDefaultValues = .init(
81
95
  defaultSize: CGSize(width: 1080, height: 720 + (pwaManager.display != .fullscreen ? NavView.navHeight : 0)),
82
- windowResizability: .automatic
96
+ windowResizability: .automatic,
97
+ resizeRange: nil
83
98
  )
84
99
 
85
100
  func getValue() -> WindowContainerPlainDefaultValues {
@@ -100,5 +115,8 @@ class WindowContainerMgr: ObservableObject {
100
115
  if let newResizability = data.windowResizability {
101
116
  wgSetting.windowResizability = newResizability
102
117
  }
118
+ if let newResizeRange = data.resizeRange {
119
+ wgSetting.resizeRange = newResizeRange
120
+ }
103
121
  }
104
122
  }
@@ -125,7 +125,6 @@ class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler, WKUID
125
125
  // backward/forward
126
126
  webViewRef?.didNavBackForward()
127
127
  }
128
- webViewRef?.navInfo.url = resource
129
128
  decisionHandler(.allow)
130
129
  } else {
131
130
  decisionHandler(.cancel)
@@ -211,6 +210,34 @@ class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler, WKUID
211
210
  wg.updateFrame = !(wg.updateFrame)
212
211
  }
213
212
  }
213
+
214
+ private var isObserving = false
215
+ func startObserving(webView: WKWebView) {
216
+ guard !isObserving else { return }
217
+ webView.addObserver(self, forKeyPath: #keyPath(WKWebView.url), options: .new, context: nil)
218
+ isObserving = true
219
+ }
220
+
221
+ func stopObserving(webView: WKWebView) {
222
+ guard isObserving else { return }
223
+ webView.removeObserver(self, forKeyPath: #keyPath(WKWebView.url))
224
+ isObserving = false
225
+ }
226
+
227
+ override func observeValue(
228
+ forKeyPath keyPath: String?,
229
+ of object: Any?,
230
+ change: [NSKeyValueChangeKey: Any]?,
231
+ context: UnsafeMutableRawPointer?
232
+ ) {
233
+ if keyPath == #keyPath(WKWebView.url),
234
+ let url = (object as? WKWebView)?.url?.absoluteString
235
+ {
236
+ DispatchQueue.main.async {
237
+ self.webViewRef?.navInfo.url = url
238
+ }
239
+ }
240
+ }
214
241
  }
215
242
 
216
243
  struct WebViewNative: UIViewRepresentable {
@@ -250,6 +277,7 @@ struct WebViewNative: UIViewRepresentable {
250
277
  myConfig.setURLSchemeHandler(webViewHolder.webViewCoordinator, forURLScheme: "file")
251
278
  }
252
279
  webViewHolder.appleWebView = WKWebView(frame: .zero, configuration: myConfig)
280
+ webViewHolder.webViewCoordinator!.startObserving(webView: webViewHolder.appleWebView!)
253
281
  let configUA = myConfig.applicationNameForUserAgent as? String ?? ""
254
282
 
255
283
  // change webview ua
@@ -284,6 +312,10 @@ struct WebViewNative: UIViewRepresentable {
284
312
  func updateUIView(_ webView: WKWebView, context: Context) {
285
313
  initialLoad()
286
314
  }
315
+
316
+ static func dismantleUIView(_ uiView: WKWebView, coordinator: Coordinator) {
317
+ coordinator.stopObserving(webView: uiView)
318
+ }
287
319
  }
288
320
 
289
321
  // extend webview to support file://
@@ -21,7 +21,7 @@ struct PWAManager: Codable {
21
21
  width: 1280,
22
22
  height: 720
23
23
  ),
24
- resizability: "automatic"
24
+ resizability: nil
25
25
  )
26
26
  var useMainScene: Bool = true
27
27
  private var version: String = "PACKAGE_VERSION"
@@ -1,5 +1,4 @@
1
1
  import SwiftUI
2
- import SwiftUICore
3
2
 
4
3
  struct LoadingView: View {
5
4
  var body: some View {
@@ -9,7 +9,38 @@ struct PlainWindowContainerView: View {
9
9
  @State private var timer: Timer?
10
10
 
11
11
  private func setSize(size: CGSize) {
12
- sceneDelegate.window?.windowScene?.requestGeometryUpdate(.Vision(size: size))
12
+ sceneDelegate.window?.windowScene?
13
+ .requestGeometryUpdate(
14
+ .Vision(
15
+ size: size
16
+ )
17
+ )
18
+ }
19
+
20
+ private func setResizibility(resizingRestrictions: UIWindowScene.ResizingRestrictions) {
21
+ sceneDelegate.window?.windowScene?
22
+ .requestGeometryUpdate(
23
+ .Vision(
24
+ resizingRestrictions: resizingRestrictions
25
+ )
26
+ )
27
+ }
28
+
29
+ private func setResizeRange(resizeRange: ResizeRange) {
30
+ sceneDelegate.window?.windowScene?
31
+ .requestGeometryUpdate(
32
+ .Vision(
33
+ minimumSize: CGSize(
34
+ width: resizeRange.minWidth ?? 0,
35
+ height: resizeRange
36
+ .minHeight ?? 0
37
+ ),
38
+ maximumSize: CGSize(
39
+ width: resizeRange.maxWidth ?? .infinity,
40
+ height: resizeRange.maxHeight ?? .infinity
41
+ )
42
+ )
43
+ )
13
44
  }
14
45
 
15
46
  var body: some View {
@@ -39,7 +70,6 @@ struct PlainWindowContainerView: View {
39
70
  // Avoid showing webview until its loading completes
40
71
  let wc = e.getComponent(SpatialWindowComponent.self)
41
72
  let didFinishFirstLoad = wc != nil ? wc!.didFinishFirstLoad : false
42
-
43
73
  SpatialWebViewUI().environment(e)
44
74
  .frame(width: width, height: height).padding3D(.front, -100_000)
45
75
  .rotation3DEffect(Rotation3D(simd_quatf(ix: e.modelEntity.orientation.vector.x, iy: e.modelEntity.orientation.vector.y, iz: e.modelEntity.orientation.vector.z, r: e.modelEntity.orientation.vector.w)))
@@ -47,12 +77,30 @@ struct PlainWindowContainerView: View {
47
77
  .offset(z: z)
48
78
  .opacity(didFinishFirstLoad ? 1.0 : 0.0)
49
79
  .animation(.linear(duration: 0.2), value: didFinishFirstLoad)
80
+ .ornament(attachmentAnchor: .scene(.top), contentAlignment: .center) {
81
+ if pwaManager.display != .fullscreen {
82
+ ZStack {
83
+ NavView(swc: wc, navInfo: wc!.navInfo).offset(y: -15)
84
+ }.frame(height: 100)
85
+ }
86
+ }
50
87
  }
51
88
  }
52
89
  }
53
90
  .onReceive(windowContainerContent.setSize) { newSize in
54
91
  setSize(size: newSize)
55
92
  }
93
+ .onAppear {
94
+ let wd = WindowContainerMgr.Instance.getValue()
95
+ if let range = wd.resizeRange {
96
+ self.setResizeRange(resizeRange: range)
97
+ if (range.minWidth != nil || range.minWidth != nil) && range.minWidth == range.maxWidth && range.minHeight == range.maxHeight {
98
+ self.setResizibility(resizingRestrictions: .none)
99
+ } else {
100
+ self.setResizibility(resizingRestrictions: .freeform)
101
+ }
102
+ }
103
+ }
56
104
  .onChange(of: proxy3D.size) {
57
105
  // WkWebview has an issue where it doesn't resize while the swift window is resized
58
106
  // Treid to call didMoveToWindow to force redraw to occur but that seemed to cause rendering artifacts so that solution was rejected
@@ -28,115 +28,33 @@ struct SpatialWebViewUI: View {
28
28
  let parentYOffset = Float(wv.scrollOffset.y)
29
29
 
30
30
  let childEntities = ent.getEntities()
31
- VStack(spacing: 0) {
32
- if wv.isRootWebview() && pwaManager.display != .fullscreen {
33
- NavView(swc: wv, navInfo: wv.navInfo)
34
- Spacer()
35
- }
36
- // Display child entities of the webview
37
- ZStack {
38
- OptionalClip(clipEnabled: ent.coordinateSpace != .ROOT && wv.isScrollEnabled()) {
39
- ZStack {
40
- ForEach(Array(childEntities.keys), id: \.self) { key in
41
- if let e = childEntities[key] {
42
- let _ = e.forceUpdate ? 0 : 0
43
- if let childWindowcomponent = e.getComponent(SpatialWindowComponent.self) {
44
- if e.coordinateSpace == .DOM {
45
- let view = childWindowcomponent
46
- let x = CGFloat(e.modelEntity.position.x)
47
- let y = CGFloat(e.modelEntity.position.y - (view.scrollWithParent ? parentYOffset : 0))
48
- let z = CGFloat(e.modelEntity.position.z) + (e.zIndex * zOrderBias)
49
- let width = CGFloat(view.resolutionX)
50
- let height = CGFloat(view.resolutionY)
51
- let anchor = view.rotationAnchor
52
-
53
- // Matrix = MTranslate X MRotate X MScale
54
- SpatialWebViewUI().environment(e)
55
- .frame(width: width, height: height)
56
- // use .offset(smallVal) to workaround for glassEffect not working and small width/height spatialDiv not working
57
- .offset(z: 0.0001)
58
- .scaleEffect(
59
- x: CGFloat(e.modelEntity.scale.x),
60
- y: CGFloat(e.modelEntity.scale.y),
61
- z: CGFloat(e.modelEntity.scale.z),
62
- anchor: anchor
63
- )
64
- .rotation3DEffect(
65
- Rotation3D(simd_quatf(
66
- ix: e.modelEntity.orientation.vector.x,
67
- iy: e.modelEntity.orientation.vector.y,
68
- iz: e.modelEntity.orientation.vector.z,
69
- r: e.modelEntity.orientation.vector.w
70
- )),
71
- anchor: anchor
72
- )
73
-
74
- .position(x: x, y: y)
75
- .offset(z: z)
76
- .gesture(
77
- DragGesture()
78
- .onChanged { gesture in
79
- let scrollEnabled = view.isScrollEnabled()
80
- if !scrollEnabled {
81
- // Check if there is a nearest scroll-enabled SpatialWindowComponent in the current SpatialEntity
82
- // and scroll it if it exists
83
- if let targetScrollWV = wv.findNearestScrollEnabledSpatialWindowComponent() {
84
- if !view.dragStarted {
85
- view.dragStarted = true
86
- view.dragStart = (gesture.translation.height)
87
- }
88
-
89
- // TODO: this should have velocity
90
- let delta = view.dragStart - gesture.translation.height
91
- view.dragStart = gesture.translation.height
92
- targetScrollWV.updateScrollOffset(delta: delta)
93
- }
94
- }
95
- }
96
- .onEnded { _ in
97
- let scrollEnabled = view.isScrollEnabled()
98
-
99
- if !scrollEnabled {
100
- if let targetScrollWV = wv.findNearestScrollEnabledSpatialWindowComponent() {
101
- view.dragStarted = false
102
- view.dragStart = 0
103
-
104
- targetScrollWV.stopScrolling()
105
- }
106
- }
107
- }
108
- )
109
- }
110
- }
111
- }
112
- }
113
-
114
- // Model3D content
115
- ForEach(Array(childEntities.keys), id: \.self) { key in
116
- if let e = childEntities[key] {
117
- let _ = e.forceUpdate ? 0 : 0
118
- SpatialModel3DView(parentYOffset: parentYOffset)
119
- .environment(e)
120
- }
121
- }
122
-
123
- // SpatialView content
124
- ForEach(Array(childEntities.keys), id: \.self) { key in
125
- if let e = childEntities[key] {
31
+ // Display child entities of the webview
32
+ ZStack {
33
+ OptionalClip(clipEnabled: ent.coordinateSpace != .ROOT && wv.isScrollEnabled()) {
34
+ ZStack {
35
+ ForEach(Array(childEntities.keys), id: \.self) { key in
36
+ if let e = childEntities[key] {
37
+ let _ = e.forceUpdate ? 0 : 0
38
+ if let childWindowcomponent = e.getComponent(SpatialWindowComponent.self) {
126
39
  if e.coordinateSpace == .DOM {
127
- if let viewComponent = e.getComponent(SpatialViewComponent.self) {
128
- let _ = e.forceUpdate ? 0 : 0
129
- let x = CGFloat(e.modelEntity.position.x)
130
- let y = CGFloat(e.modelEntity.position.y - parentYOffset)
131
- let z = CGFloat(e.modelEntity.position.z)
132
-
133
- let width = CGFloat(viewComponent.resolutionX)
134
- let height = CGFloat(viewComponent.resolutionY)
135
-
136
- SpatialViewUI().environment(e).frame(width: width, height: height).scaleEffect(
40
+ let view = childWindowcomponent
41
+ let x = CGFloat(e.modelEntity.position.x)
42
+ let y = CGFloat(e.modelEntity.position.y - (view.scrollWithParent ? parentYOffset : 0))
43
+ let z = CGFloat(e.modelEntity.position.z) + (e.zIndex * zOrderBias)
44
+ let width = CGFloat(view.resolutionX)
45
+ let height = CGFloat(view.resolutionY)
46
+ let anchor = view.rotationAnchor
47
+
48
+ // Matrix = MTranslate X MRotate X MScale
49
+ SpatialWebViewUI().environment(e)
50
+ .frame(width: width, height: height)
51
+ // use .offset(smallVal) to workaround for glassEffect not working and small width/height spatialDiv not working
52
+ .offset(z: 0.0001)
53
+ .scaleEffect(
137
54
  x: CGFloat(e.modelEntity.scale.x),
138
55
  y: CGFloat(e.modelEntity.scale.y),
139
- z: CGFloat(e.modelEntity.scale.z)
56
+ z: CGFloat(e.modelEntity.scale.z),
57
+ anchor: anchor
140
58
  )
141
59
  .rotation3DEffect(
142
60
  Rotation3D(simd_quatf(
@@ -144,50 +62,118 @@ struct SpatialWebViewUI: View {
144
62
  iy: e.modelEntity.orientation.vector.y,
145
63
  iz: e.modelEntity.orientation.vector.z,
146
64
  r: e.modelEntity.orientation.vector.w
147
- ))
148
- ).position(x: x, y: y)
65
+ )),
66
+ anchor: anchor
67
+ )
68
+
69
+ .position(x: x, y: y)
149
70
  .offset(z: z)
150
- }
71
+ .gesture(
72
+ DragGesture()
73
+ .onChanged { gesture in
74
+ let scrollEnabled = view.isScrollEnabled()
75
+ if !scrollEnabled {
76
+ // Check if there is a nearest scroll-enabled SpatialWindowComponent in the current SpatialEntity
77
+ // and scroll it if it exists
78
+ if let targetScrollWV = wv.findNearestScrollEnabledSpatialWindowComponent() {
79
+ if !view.dragStarted {
80
+ view.dragStarted = true
81
+ view.dragStart = (gesture.translation.height)
82
+ }
83
+
84
+ // TODO: this should have velocity
85
+ let delta = view.dragStart - gesture.translation.height
86
+ view.dragStart = gesture.translation.height
87
+ targetScrollWV.updateScrollOffset(delta: delta)
88
+ }
89
+ }
90
+ }
91
+ .onEnded { _ in
92
+ let scrollEnabled = view.isScrollEnabled()
93
+ if !scrollEnabled {
94
+ if let targetScrollWV = wv.findNearestScrollEnabledSpatialWindowComponent() {
95
+ view.dragStarted = false
96
+ view.dragStart = 0
97
+ targetScrollWV.stopScrolling()
98
+ }
99
+ }
100
+ }
101
+ )
151
102
  }
152
103
  }
153
104
  }
154
- }.frame(maxWidth: .infinity, maxHeight: .infinity).frame(maxDepth: 0, alignment: .back).offset(z: 0)
155
- }
105
+ }
156
106
 
157
- // Display the main webview
158
- if wv.didFailLoad {
159
- VStack {
160
- Text("Failed to load webpage. Is the server running?")
161
- .foregroundColor(.white)
162
- Button("Reload") {
163
- if let url = wv.getURL() {
164
- wv.navigateToURL(url: url)
165
- } else {
166
- logger.warning("Unable to reload URL")
167
- }
107
+ // Model3D content
108
+ ForEach(Array(childEntities.keys), id: \.self) { key in
109
+ if let e = childEntities[key] {
110
+ let _ = e.forceUpdate ? 0 : 0
111
+ SpatialModel3DView(parentYOffset: parentYOffset)
112
+ .environment(e)
168
113
  }
169
- .foregroundColor(.white)
170
114
  }
171
- .frame(maxWidth: .infinity, maxHeight: .infinity).glassBackgroundEffect()
172
115
 
173
- } else {
174
- wv.getView()
175
- .materialWithBorderCorner(
176
- wv.backgroundMaterial,
177
- wv.cornerRadius
178
- )
116
+ // SpatialView content
117
+ ForEach(Array(childEntities.keys), id: \.self) { key in
118
+ if let e = childEntities[key] {
119
+ if e.coordinateSpace == .DOM {
120
+ if let viewComponent = e.getComponent(SpatialViewComponent.self) {
121
+ let _ = e.forceUpdate ? 0 : 0
122
+ let x = CGFloat(e.modelEntity.position.x)
123
+ let y = CGFloat(e.modelEntity.position.y - parentYOffset)
124
+ let z = CGFloat(e.modelEntity.position.z)
125
+
126
+ let width = CGFloat(viewComponent.resolutionX)
127
+ let height = CGFloat(viewComponent.resolutionY)
128
+
129
+ SpatialViewUI().environment(e).frame(width: width, height: height).scaleEffect(
130
+ x: CGFloat(e.modelEntity.scale.x),
131
+ y: CGFloat(e.modelEntity.scale.y),
132
+ z: CGFloat(e.modelEntity.scale.z)
133
+ )
134
+ .rotation3DEffect(
135
+ Rotation3D(simd_quatf(
136
+ ix: e.modelEntity.orientation.vector.x,
137
+ iy: e.modelEntity.orientation.vector.y,
138
+ iz: e.modelEntity.orientation.vector.z,
139
+ r: e.modelEntity.orientation.vector.w
140
+ ))
141
+ ).position(x: x, y: y)
142
+ .offset(z: z)
143
+ }
144
+ }
145
+ }
146
+ }
147
+ }.frame(maxWidth: .infinity, maxHeight: .infinity).frame(maxDepth: 0, alignment: .back).offset(z: 0)
148
+ }
179
149
 
180
- .frame(maxWidth: .infinity, maxHeight: .infinity)
150
+ // Display the main webview
151
+ if wv.didFailLoad {
152
+ VStack {
153
+ Text("Failed to load webpage. Is the server running?")
154
+ .foregroundColor(.white)
155
+ Button("Reload") {
156
+ if let url = wv.getURL() {
157
+ wv.navigateToURL(url: url)
158
+ } else {
159
+ logger.warning("Unable to reload URL")
160
+ }
161
+ }
162
+ .foregroundColor(.white)
181
163
  }
164
+ .frame(maxWidth: .infinity, maxHeight: .infinity).glassBackgroundEffect()
165
+
166
+ } else {
167
+ wv.getView()
168
+ .materialWithBorderCorner(
169
+ wv.backgroundMaterial,
170
+ wv.cornerRadius
171
+ )
172
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
182
173
  }
183
- .opacity(wv.opacity)
184
- .hidden(!ent.visible)
185
- // .ornament(attachmentAnchor: .scene(.top), contentAlignment: .top) {
186
- // if wv.isRootWebview() && pwaManager.display != .fullscreen {
187
- // NavView(swc: wv, navInfo: wv.navInfo)
188
- // }
189
- // }
190
174
  }
175
+ .opacity(wv.opacity)
176
+ .hidden(!ent.visible)
191
177
  }
192
178
  }
193
179
  }
@@ -2,16 +2,20 @@ import SwiftUI
2
2
  import WebKit
3
3
 
4
4
  struct NavView: View {
5
- static let navHeight: CGFloat = 80
5
+ static let navHeight: CGFloat = 60
6
+ static let minWidth: CGFloat = 400
6
7
  @State var swc: SpatialWindowComponent?
7
8
  @StateObject var navInfo: NavInfo
8
- @State var showUrl: Bool = false
9
- @State private var showNav: Bool = false
9
+ @State var navWidth: CGFloat = 0
10
10
  @State private var showCopyTip = false
11
- @State private var navWidth: CGFloat = 0
12
- @State private var contentHeight: CGFloat = 68
11
+ @State private var contentHeight: CGFloat = 60
13
12
  @State private var texWidth: CGFloat = 0
14
13
  @State private var firstGetSize: Bool = true
14
+ @State private var timer: Timer?
15
+ @State private var timeRemaining = 5
16
+ @State private var showEnter: Double = 1
17
+ @State private var showNav: Double = 0
18
+ @State private var showUrl: Double = 0
15
19
  @Namespace var hoverNamespace
16
20
  var navHoverGroup: HoverEffectGroup {
17
21
  HoverEffectGroup(hoverNamespace)
@@ -20,83 +24,108 @@ struct NavView: View {
20
24
  var body: some View {
21
25
  ZStack {
22
26
  ZStack {
23
- HStack {
24
- Image("logo").resizable().frame(width: 44, height: 44)
27
+ HStack(spacing: 10) {
28
+ Image("logo").resizable().frame(width: 32, height: 32)
25
29
  .cornerRadius(100)
26
- NavButton(action: { print("click"); showNav = true }, children: Image(systemName: "link"))
30
+ if pwaManager.display == .minimal {
31
+ NavButton(action: { swc?.goBack() }, children: Image("arrow_left"), clearBackGround: true).disabled(!(swc?.canGoBack ?? false))
32
+ NavButton(action: { swc?.goForward() }, children: Image("arrow_right"), clearBackGround: true).disabled(!(swc?.canGoBack ?? false))
33
+ NavButton(action: { swc?.reload() }, children: Image("refresh"), clearBackGround: true)
34
+ NavDivider()
35
+ }
36
+ NavButton(action: { print("click"); withAnimation(.easeInOut(duration: 0.5)) { showNav = 1.0; showEnter = 0 } }, children: Image("more"), clearBackGround: true)
27
37
  }
28
38
  .padding(12)
29
- .background(.thinMaterial)
39
+ .background(Color(hex: "#161616E5"))
30
40
  .hoverEffect(in: navHoverGroup) { effect, isActive, _ in
31
- effect.opacity((isActive && !showNav) ? 1 : 0)
41
+ effect.opacity((isActive && showNav == 0 && showUrl == 0) ? 1 : 0)
32
42
  }
33
- Image("nav").resizable().frame(width: 29, height: 29).hoverEffect(.highlight).hoverEffect(in: navHoverGroup) { effect, isActive, _ in
34
- effect.opacity((isActive && !showNav) ? 0 : 1)
43
+ Image("nav").resizable().frame(width: 32, height: 32).hoverEffect(.highlight).hoverEffect(in: navHoverGroup) { effect, isActive, _ in
44
+ effect.opacity((isActive && showNav == 0 && showUrl == 0) ? 0 : 1)
35
45
  }
36
46
  }
37
47
  .frame(height: contentHeight)
38
48
  .cornerRadius(100)
39
- .hoverEffect(in: navHoverGroup) { effect, isActive, proxy in
40
- effect.clipShape(.capsule.size(
41
- width: isActive ? proxy.size.width : proxy.size.height,
42
- height: proxy.size.height,
43
- anchor: .center
44
- ))
45
- }
46
- .opacity(showNav ? 0 : 1)
47
- // if showNav {
49
+ .opacity(showEnter)
48
50
  HStack(spacing: 14) {
49
51
  if pwaManager.display == .minimal {
50
- NavButton(action: { swc?.goBack() }, children: Image(systemName: "arrow.left"), clearBackGround: true).disabled(!(swc?.canGoBack ?? false))
51
- NavButton(action: { swc?.goForward() }, children: Image(systemName: "arrow.right"), clearBackGround: true).disabled(!(swc?.canGoBack ?? false))
52
+ NavButton(action: { swc?.goBack() }, children: Image("arrow_left")).disabled(!(swc?.canGoBack ?? false))
53
+ NavButton(action: { swc?.goForward() }, children: Image("arrow_right")).disabled(!(swc?.canGoBack ?? false))
54
+ NavButton(action: { swc?.reload() }, children: Image("refresh"))
55
+ NavDivider()
52
56
  }
53
- NavButton(action: { swc?.reload() }, children: Image(systemName: "arrow.clockwise"), clearBackGround: true)
54
- NavButton(action: { swc?.navigateToURL(url: URL(string: pwaManager.start_url)!) }, children: Image(systemName: "house.fill"), clearBackGround: true)
55
- NavButton(action: { withAnimation(.easeInOut(duration: 0.5)) { showUrl = true } }, children: Image(systemName: "link"), clearBackGround: true)
56
- NavButton(action: { showNav = false }, children: Image(systemName: "chevron.up"))
57
+ NavButton(action: { swc?.navigateToURL(url: URL(string: pwaManager.start_url)!) }, children: Image("home"))
58
+ NavButton(action: { withAnimation(.easeInOut(duration: 0.5)) { showUrl = 1; showNav = 0 } }, children: Image("link"))
59
+ NavButton(action: { showNav = 0; showEnter = 1 }, children: Image("back"))
57
60
  }
58
61
  .padding(12)
59
- .glassBackgroundEffect(in: RoundedRectangle(cornerRadius: 100))
60
- .opacity(withAnimation(.easeInOut(duration: 0.5)) { showNav ? 1 : 0 })
61
- .ornament(attachmentAnchor: .scene(.top), contentAlignment: .top) {
62
- if showUrl {
63
- HStack(spacing: 14) {
64
- Text(navInfo.url.count > 0 ? navInfo.url : (swc?.getURL()?.absoluteString ?? ""))
65
- .padding()
66
- .lineLimit(1)
67
- .overlay(GeometryReader { geo -> AnyView in
68
- DispatchQueue.main.async {
69
- if geo.size.width > 0 {
70
- texWidth = .minimum(300, geo.size.width)
71
- }
72
- }
73
- return AnyView(EmptyView())
74
- })
75
- .frame(width: .maximum(300, texWidth))
76
- NavButton(action: {
77
- UIPasteboard.general.string = swc?.getURL()?.absoluteString ?? ""
78
- showCopyTip = true
79
- DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
80
- showCopyTip = false
81
- }
82
- }, children: Image(systemName: "square.on.square"))
83
- NavButton(action: { showUrl = false }, children: Image(systemName: "xmark"))
84
- }
62
+ .background(Color(hex: "#161616E5"))
63
+ .cornerRadius(100)
64
+ .opacity(showNav)
65
+ .onChange(of: showNav) { _, newValue in
66
+ if newValue == 1 {
67
+ startTimer()
68
+ } else {
69
+ stopTimer()
70
+ }
71
+ }
72
+ HStack(spacing: 6) {
73
+ Text(navInfo.url.count > 0 ? navInfo.url : (swc?.getURL()?.absoluteString ?? ""))
74
+ .lineLimit(1)
75
+ .textSelection(.enabled)
85
76
  .padding(12)
86
- .glassBackgroundEffect(in: RoundedRectangle(cornerRadius: 100))
87
- .popover(isPresented: $showCopyTip) {
88
- Text("copied!")
89
- .padding()
90
- .cornerRadius(10)
77
+ .frame(minWidth: 200)
78
+ .frame(maxWidth: 500)
79
+ .frame(height: 44)
80
+ .background(.black)
81
+ .cornerRadius(100)
82
+ NavButton(action: {
83
+ UIPasteboard.general.string = swc?.getURL()?.absoluteString ?? ""
84
+ showCopyTip = true
85
+ DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
86
+ showCopyTip = false
91
87
  }
92
- }
88
+ withAnimation(.easeInOut(duration: 0.5)) { showUrl = 0; showNav = 1 }
89
+ }, children: Image("copy"))
90
+ NavButton(action: {
91
+ print("open browser")
92
+ UIApplication.shared.open(URL(string: navInfo.url.count > 0 ? navInfo.url : (swc?.getURL()?.absoluteString ?? ""))!, options: [:], completionHandler: nil)
93
+ withAnimation(.easeInOut(duration: 0.5)) { showUrl = 0; showNav = 1 }
94
+ }, children: Image("browser"))
95
+ NavButton(action: { withAnimation(.easeInOut(duration: 0.5)) { showUrl = 0; showNav = 1 } }, children: Image("close"))
93
96
  }
94
- // }
97
+ .popover(isPresented: $showCopyTip) {
98
+ Text("copied!")
99
+ .padding()
100
+ .background(Color(hex: "#161616E5"))
101
+ .cornerRadius(10)
102
+ }
103
+ .padding(8)
104
+ .background(Color(hex: "#161616E5"))
105
+ .cornerRadius(100)
106
+ .opacity(showUrl)
95
107
  }
108
+ }
96
109
 
97
- // else{
110
+ private func startTimer() {
111
+ timeRemaining = 5
112
+ timer = Timer.scheduledTimer(
113
+ withTimeInterval: 1,
114
+ repeats: true
115
+ ) { _ in
116
+ timeRemaining -= 1
98
117
 
99
- // }
118
+ if timeRemaining <= 0 {
119
+ showNav = 0
120
+ showEnter = 1
121
+ stopTimer()
122
+ }
123
+ }
124
+ }
125
+
126
+ private func stopTimer() {
127
+ timer?.invalidate()
128
+ timer = nil
100
129
  }
101
130
  }
102
131
 
@@ -112,27 +141,45 @@ struct NavButton: View {
112
141
  var children: Image
113
142
  var size: CGFloat = 44
114
143
  var padding: CGFloat = 10
115
- var clearBackGround: Bool = false
144
+ var clearBackGround: Bool = true
116
145
  var body: some View {
117
146
  if clearBackGround {
118
147
  Button(action: action, label: {
119
- Circle()
120
- .fill(Color.white.opacity(0))
121
- .frame(width: size, height: size)
122
- .overlay(
123
- children.frame(width: size, height: size).padding(padding)
124
- )
125
- }).frame(width: size, height: size).buttonStyle(NavButtonStyle()).hoverEffect(.highlight)
148
+ ZStack {
149
+ Circle()
150
+ .fill(Color.white.opacity(0))
151
+ .frame(width: size, height: size)
152
+ .overlay(
153
+ children.resizable().frame(width: size - padding * 2, height: size - padding * 2)
154
+ )
155
+ .contentShape(Circle())
156
+ }
157
+ }).buttonStyle(NavButtonStyle()).frame(width: size, height: size).hoverEffect(.highlight)
126
158
  } else {
127
159
  Button(action: action, label: {
128
160
  Circle()
129
161
  .fill(Color.white.opacity(0))
130
162
  .frame(width: size, height: size)
131
163
  .overlay(
132
- children.frame(width: size, height: size).padding(padding)
164
+ children.resizable().frame(width: size - padding * 2, height: size - padding * 2)
133
165
  )
134
- }).frame(width: size, height: size).hoverEffect(.highlight)
166
+ .contentShape(Circle())
167
+ }).hoverEffect(.highlight)
168
+ }
169
+ }
170
+ }
171
+
172
+ struct NavDivider: View {
173
+ var width: CGFloat = 1
174
+ var height: CGFloat = 16
175
+ var paddingLR: CGFloat = 4
176
+ var paddingTB: CGFloat = 12
177
+ var body: some View {
178
+ VStack {
179
+ Rectangle().fill(Color(hex: "#FFFFFF3D")).frame(width: width, height: height)
135
180
  }
181
+ .padding([.top, .bottom], paddingTB)
182
+ .padding([.leading, .trailing], paddingLR)
136
183
  }
137
184
  }
138
185
 
@@ -21,8 +21,8 @@
21
21
  2B2F1D992BEDA8EF006897EE /* VolumetricWindowContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B2F1D982BEDA8EF006897EE /* VolumetricWindowContainerView.swift */; };
22
22
  2B2F1D9B2BEDA975006897EE /* PlainWindowContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B2F1D9A2BEDA975006897EE /* PlainWindowContainerView.swift */; };
23
23
  2B37E8E12D002C4D0096749A /* MaterialWithBorderCornerModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B37E8E02D002C2B0096749A /* MaterialWithBorderCornerModifier.swift */; };
24
+ 2B48B3F12DF9866700826889 /* ColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B48B3F02DF9866100826889 /* ColorExtension.swift */; };
24
25
  2B67BBAC2D151C1A00BBC689 /* manifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B67BBAB2D151C1500BBC689 /* manifest.swift */; };
25
- 2B6BA5DF2DF0607400D40337 /* nav.png in Resources */ = {isa = PBXBuildFile; fileRef = 2B6BA5DE2DF0607400D40337 /* nav.png */; };
26
26
  2B85209C2BFD5FDB0038FE29 /* UpdateSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B85209B2BFD5FDB0038FE29 /* UpdateSystem.swift */; };
27
27
  2B9909932C3605A9004826D1 /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B9909922C3605A9004826D1 /* Window.swift */; };
28
28
  2BA06D712D4380C60020B505 /* LoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BA06D702D4380C30020B505 /* LoadingView.swift */; };
@@ -76,8 +76,8 @@
76
76
  2B2F1D982BEDA8EF006897EE /* VolumetricWindowContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VolumetricWindowContainerView.swift; sourceTree = "<group>"; };
77
77
  2B2F1D9A2BEDA975006897EE /* PlainWindowContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlainWindowContainerView.swift; sourceTree = "<group>"; };
78
78
  2B37E8E02D002C2B0096749A /* MaterialWithBorderCornerModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaterialWithBorderCornerModifier.swift; sourceTree = "<group>"; };
79
+ 2B48B3F02DF9866100826889 /* ColorExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorExtension.swift; sourceTree = "<group>"; };
79
80
  2B67BBAB2D151C1500BBC689 /* manifest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = manifest.swift; sourceTree = "<group>"; };
80
- 2B6BA5DE2DF0607400D40337 /* nav.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = nav.png; sourceTree = "<group>"; };
81
81
  2B85209B2BFD5FDB0038FE29 /* UpdateSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateSystem.swift; sourceTree = "<group>"; };
82
82
  2B9909922C3605A9004826D1 /* Window.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Window.swift; sourceTree = "<group>"; };
83
83
  2BA06D702D4380C30020B505 /* LoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingView.swift; sourceTree = "<group>"; };
@@ -125,6 +125,7 @@
125
125
  2B0B1C0E2C494E4100E644F9 /* Utils */ = {
126
126
  isa = PBXGroup;
127
127
  children = (
128
+ 2B48B3F02DF9866100826889 /* ColorExtension.swift */,
128
129
  2B0FF4F32DD2711800C3F20A /* PerfClock.swift */,
129
130
  2BD510552D54A2FF0001E5E6 /* SceneManager.swift */,
130
131
  2B0B43092CBE21540003CEF3 /* CommandManager.swift */,
@@ -160,7 +161,6 @@
160
161
  2B2F1D8A2BED4CD0006897EE /* libs */,
161
162
  2B2F1D6A2BEBFAAA006897EE /* web_spatialApp.swift */,
162
163
  2B2F1D702BEBFAAC006897EE /* Assets.xcassets */,
163
- 2B6BA5DE2DF0607400D40337 /* nav.png */,
164
164
  2B2F1D752BEBFAAC006897EE /* Info.plist */,
165
165
  2B2F1D722BEBFAAC006897EE /* Preview Content */,
166
166
  );
@@ -354,7 +354,6 @@
354
354
  files = (
355
355
  2BDBF9B62C4ED9F600D269D7 /* static-web in Resources */,
356
356
  2B2F1D742BEBFAAC006897EE /* Preview Assets.xcassets in Resources */,
357
- 2B6BA5DF2DF0607400D40337 /* nav.png in Resources */,
358
357
  2B2F1D712BEBFAAC006897EE /* Assets.xcassets in Resources */,
359
358
  );
360
359
  runOnlyForDeploymentPostprocessing = 0;
@@ -392,6 +391,7 @@
392
391
  2BDBED5F2D3F885A0065443F /* SpatialModel3DComponent.swift in Sources */,
393
392
  2B2F1D6B2BEBFAAA006897EE /* web_spatialApp.swift in Sources */,
394
393
  2B0B1C102C494E5400E644F9 /* Logger.swift in Sources */,
394
+ 2B48B3F12DF9866700826889 /* ColorExtension.swift in Sources */,
395
395
  2B37E8E12D002C4D0096749A /* MaterialWithBorderCornerModifier.swift in Sources */,
396
396
  2BC261222D38F1DA00BCA977 /* NavView.swift in Sources */,
397
397
  2BAC1BE22CDC49040022E29B /* SpatialViewUI.swift in Sources */,
Binary file