@webspatial/platform-visionos 0.1.19 → 0.1.20
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 +1 -1
- package/web-spatial/Assets.xcassets/Contents.json +2 -2
- package/web-spatial/Assets.xcassets/logo.imageset/Contents.json +20 -0
- package/web-spatial/Assets.xcassets/nav.imageset/Contents.json +21 -0
- package/web-spatial/Assets.xcassets/nav.imageset/nav.png +0 -0
- package/web-spatial/libs/Utils/WindowContainerMgr.swift +3 -2
- package/web-spatial/libs/webView/backend/NativeWebView.swift +9 -5
- package/web-spatial/libs/webView/manifest.swift +5 -0
- package/web-spatial/nav.png +0 -0
- package/web-spatial/views/SpatialWebViewUI.swift +136 -131
- package/web-spatial/views/ui/NavView.swift +112 -88
- package/web-spatial/web_spatialApp.swift +1 -1
- package/web-spatial.xcodeproj/project.pbxproj +4 -4
- package/web-spatial/libs/Utils/version.swift +0 -25
package/package.json
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"images": [
|
|
3
|
+
{
|
|
4
|
+
"filename": "nav.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
|
+
}
|
|
Binary file
|
|
@@ -78,7 +78,7 @@ class WindowContainerMgr: ObservableObject {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
private var wgSetting: WindowContainerPlainDefaultValues = .init(
|
|
81
|
-
defaultSize: CGSize(width: 1080, height: 720),
|
|
81
|
+
defaultSize: CGSize(width: 1080, height: 720 + (pwaManager.display != .fullscreen ? NavView.navHeight : 0)),
|
|
82
82
|
windowResizability: .automatic
|
|
83
83
|
)
|
|
84
84
|
|
|
@@ -93,7 +93,8 @@ class WindowContainerMgr: ObservableObject {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
func updateWindowContainerPlainDefaultValues(_ data: WindowContainerPlainDefaultValues) {
|
|
96
|
-
if
|
|
96
|
+
if var newSize = data.defaultSize {
|
|
97
|
+
newSize.height += (pwaManager.display != .fullscreen ? NavView.navHeight : 0)
|
|
97
98
|
wgSetting.defaultSize = newSize
|
|
98
99
|
}
|
|
99
100
|
if let newResizability = data.windowResizability {
|
|
@@ -175,9 +175,13 @@ class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler, WKUID
|
|
|
175
175
|
return nil
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
178
|
+
var wvNative = WebViewNative()
|
|
179
|
+
var needsUpdate = false
|
|
180
|
+
if resource.starts(with: "file://") {
|
|
181
|
+
wvNative.url = URL(string: resource)!
|
|
182
|
+
needsUpdate = true
|
|
183
|
+
}
|
|
184
|
+
_ = wvNative.createResources(configuration: configuration, needsUpdate: needsUpdate)
|
|
181
185
|
|
|
182
186
|
webViewRef!.didSpawnWebView(wv: wvNative)
|
|
183
187
|
|
|
@@ -229,7 +233,7 @@ struct WebViewNative: UIViewRepresentable {
|
|
|
229
233
|
return c
|
|
230
234
|
}
|
|
231
235
|
|
|
232
|
-
func createResources(configuration: WKWebViewConfiguration? = nil) -> WKWebView {
|
|
236
|
+
func createResources(configuration: WKWebViewConfiguration? = nil, needsUpdate: Bool = false) -> WKWebView {
|
|
233
237
|
if webViewHolder.appleWebView == nil {
|
|
234
238
|
webViewHolder.webViewCoordinator = makeCoordinator()
|
|
235
239
|
let userContentController = WKUserContentController()
|
|
@@ -259,7 +263,7 @@ struct WebViewNative: UIViewRepresentable {
|
|
|
259
263
|
webViewHolder.appleWebView!.allowsLinkPreview = true
|
|
260
264
|
webViewHolder.appleWebView!.navigationDelegate = webViewHolder.webViewCoordinator
|
|
261
265
|
webViewHolder.appleWebView!.scrollView.delegate = webViewHolder.webViewCoordinator
|
|
262
|
-
webViewHolder.needsUpdate = (configuration != nil) ? false : true
|
|
266
|
+
webViewHolder.needsUpdate = (configuration != nil && !needsUpdate) ? false : true
|
|
263
267
|
}
|
|
264
268
|
|
|
265
269
|
return webViewHolder.appleWebView!
|
|
@@ -24,6 +24,7 @@ struct PWAManager: Codable {
|
|
|
24
24
|
resizability: "automatic"
|
|
25
25
|
)
|
|
26
26
|
var useMainScene: Bool = true
|
|
27
|
+
private var version: String = "PACKAGE_VERSION"
|
|
27
28
|
|
|
28
29
|
mutating func _init() {
|
|
29
30
|
let urlType = start_url.split(separator: "://").first
|
|
@@ -86,6 +87,10 @@ struct PWAManager: Codable {
|
|
|
86
87
|
}
|
|
87
88
|
return resource
|
|
88
89
|
}
|
|
90
|
+
|
|
91
|
+
func getVersion() -> String {
|
|
92
|
+
return version
|
|
93
|
+
}
|
|
89
94
|
}
|
|
90
95
|
|
|
91
96
|
enum PWADisplayMode: Codable {
|
|
Binary file
|
|
@@ -28,34 +28,115 @@ 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()
|
|
31
98
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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] {
|
|
40
126
|
if e.coordinateSpace == .DOM {
|
|
41
|
-
let
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
.frame(width: width, height: height)
|
|
52
|
-
// use .offset(smallVal) to workaround for glassEffect not working and small width/height spatialDiv not working
|
|
53
|
-
.offset(z: 0.0001)
|
|
54
|
-
.scaleEffect(
|
|
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(
|
|
55
137
|
x: CGFloat(e.modelEntity.scale.x),
|
|
56
138
|
y: CGFloat(e.modelEntity.scale.y),
|
|
57
|
-
z: CGFloat(e.modelEntity.scale.z)
|
|
58
|
-
anchor: anchor
|
|
139
|
+
z: CGFloat(e.modelEntity.scale.z)
|
|
59
140
|
)
|
|
60
141
|
.rotation3DEffect(
|
|
61
142
|
Rotation3D(simd_quatf(
|
|
@@ -63,125 +144,49 @@ struct SpatialWebViewUI: View {
|
|
|
63
144
|
iy: e.modelEntity.orientation.vector.y,
|
|
64
145
|
iz: e.modelEntity.orientation.vector.z,
|
|
65
146
|
r: e.modelEntity.orientation.vector.w
|
|
66
|
-
))
|
|
67
|
-
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
.position(x: x, y: y)
|
|
147
|
+
))
|
|
148
|
+
).position(x: x, y: y)
|
|
71
149
|
.offset(z: z)
|
|
72
|
-
|
|
73
|
-
DragGesture()
|
|
74
|
-
.onChanged { gesture in
|
|
75
|
-
let scrollEnabled = view.isScrollEnabled()
|
|
76
|
-
if !scrollEnabled {
|
|
77
|
-
// Check if there is a nearest scroll-enabled SpatialWindowComponent in the current SpatialEntity
|
|
78
|
-
// and scroll it if it exists
|
|
79
|
-
if let targetScrollWV = wv.findNearestScrollEnabledSpatialWindowComponent() {
|
|
80
|
-
if !view.dragStarted {
|
|
81
|
-
view.dragStarted = true
|
|
82
|
-
view.dragStart = (gesture.translation.height)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// TODO: this should have velocity
|
|
86
|
-
let delta = view.dragStart - gesture.translation.height
|
|
87
|
-
view.dragStart = gesture.translation.height
|
|
88
|
-
targetScrollWV.updateScrollOffset(delta: delta)
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
.onEnded { _ in
|
|
93
|
-
let scrollEnabled = view.isScrollEnabled()
|
|
94
|
-
|
|
95
|
-
if !scrollEnabled {
|
|
96
|
-
if let targetScrollWV = wv.findNearestScrollEnabledSpatialWindowComponent() {
|
|
97
|
-
view.dragStarted = false
|
|
98
|
-
view.dragStart = 0
|
|
99
|
-
|
|
100
|
-
targetScrollWV.stopScrolling()
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
)
|
|
150
|
+
}
|
|
105
151
|
}
|
|
106
152
|
}
|
|
107
153
|
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Model3D content
|
|
111
|
-
ForEach(Array(childEntities.keys), id: \.self) { key in
|
|
112
|
-
if let e = childEntities[key] {
|
|
113
|
-
let _ = e.forceUpdate ? 0 : 0
|
|
114
|
-
SpatialModel3DView(parentYOffset: parentYOffset)
|
|
115
|
-
.environment(e)
|
|
116
|
-
}
|
|
117
|
-
}
|
|
154
|
+
}.frame(maxWidth: .infinity, maxHeight: .infinity).frame(maxDepth: 0, alignment: .back).offset(z: 0)
|
|
155
|
+
}
|
|
118
156
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
let width = CGFloat(viewComponent.resolutionX)
|
|
130
|
-
let height = CGFloat(viewComponent.resolutionY)
|
|
131
|
-
|
|
132
|
-
SpatialViewUI().environment(e).frame(width: width, height: height).scaleEffect(
|
|
133
|
-
x: CGFloat(e.modelEntity.scale.x),
|
|
134
|
-
y: CGFloat(e.modelEntity.scale.y),
|
|
135
|
-
z: CGFloat(e.modelEntity.scale.z)
|
|
136
|
-
)
|
|
137
|
-
.rotation3DEffect(
|
|
138
|
-
Rotation3D(simd_quatf(
|
|
139
|
-
ix: e.modelEntity.orientation.vector.x,
|
|
140
|
-
iy: e.modelEntity.orientation.vector.y,
|
|
141
|
-
iz: e.modelEntity.orientation.vector.z,
|
|
142
|
-
r: e.modelEntity.orientation.vector.w
|
|
143
|
-
))
|
|
144
|
-
).position(x: x, y: y)
|
|
145
|
-
.offset(z: z)
|
|
146
|
-
}
|
|
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")
|
|
147
167
|
}
|
|
148
168
|
}
|
|
149
|
-
}
|
|
150
|
-
}.frame(maxWidth: .infinity, maxHeight: .infinity).frame(maxDepth: 0, alignment: .back).offset(z: 0)
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Display the main webview
|
|
154
|
-
if wv.didFailLoad {
|
|
155
|
-
VStack {
|
|
156
|
-
Text("Failed to load webpage. Is the server running?")
|
|
157
169
|
.foregroundColor(.white)
|
|
158
|
-
Button("Reload") {
|
|
159
|
-
if let url = wv.getURL() {
|
|
160
|
-
wv.navigateToURL(url: url)
|
|
161
|
-
} else {
|
|
162
|
-
logger.warning("Unable to reload URL")
|
|
163
|
-
}
|
|
164
170
|
}
|
|
165
|
-
.
|
|
166
|
-
}
|
|
167
|
-
.frame(maxWidth: .infinity, maxHeight: .infinity).glassBackgroundEffect()
|
|
171
|
+
.frame(maxWidth: .infinity, maxHeight: .infinity).glassBackgroundEffect()
|
|
168
172
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
173
|
+
} else {
|
|
174
|
+
wv.getView()
|
|
175
|
+
.materialWithBorderCorner(
|
|
176
|
+
wv.backgroundMaterial,
|
|
177
|
+
wv.cornerRadius
|
|
178
|
+
)
|
|
175
179
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
.opacity(wv.opacity)
|
|
180
|
-
.hidden(!ent.visible)
|
|
181
|
-
.ornament(attachmentAnchor: .scene(.bottomTrailing), contentAlignment: .bottomTrailing) {
|
|
182
|
-
if wv.isRootWebview() && pwaManager.display != .fullscreen {
|
|
183
|
-
NavView(swc: wv, navInfo: wv.navInfo)
|
|
180
|
+
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
181
|
+
}
|
|
184
182
|
}
|
|
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
|
+
// }
|
|
185
190
|
}
|
|
186
191
|
}
|
|
187
192
|
}
|
|
@@ -2,113 +2,137 @@ import SwiftUI
|
|
|
2
2
|
import WebKit
|
|
3
3
|
|
|
4
4
|
struct NavView: View {
|
|
5
|
+
static let navHeight: CGFloat = 80
|
|
5
6
|
@State var swc: SpatialWindowComponent?
|
|
6
7
|
@StateObject var navInfo: NavInfo
|
|
7
|
-
@State var showUrl: Bool =
|
|
8
|
+
@State var showUrl: Bool = false
|
|
9
|
+
@State private var showNav: Bool = false
|
|
8
10
|
@State private var showCopyTip = false
|
|
9
11
|
@State private var navWidth: CGFloat = 0
|
|
10
|
-
@State private var
|
|
12
|
+
@State private var contentHeight: CGFloat = 68
|
|
11
13
|
@State private var texWidth: CGFloat = 0
|
|
12
14
|
@State private var firstGetSize: Bool = true
|
|
15
|
+
@Namespace var hoverNamespace
|
|
16
|
+
var navHoverGroup: HoverEffectGroup {
|
|
17
|
+
HoverEffectGroup(hoverNamespace)
|
|
18
|
+
}
|
|
13
19
|
|
|
14
20
|
var body: some View {
|
|
15
|
-
ZStack
|
|
16
|
-
ZStack {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
})
|
|
27
|
-
.disabled(!(swc?.canGoBack ?? false))
|
|
28
|
-
Button(action: {
|
|
29
|
-
swc?.goForward()
|
|
30
|
-
}, label: {
|
|
31
|
-
Image(systemName: "arrow.right")
|
|
32
|
-
})
|
|
33
|
-
.disabled(!(swc?.canGoBack ?? false))
|
|
34
|
-
Button(action: {
|
|
35
|
-
swc?.reload()
|
|
36
|
-
}, label: {
|
|
37
|
-
Image(systemName: "arrow.clockwise")
|
|
38
|
-
})
|
|
39
|
-
Button(
|
|
40
|
-
action: {
|
|
41
|
-
swc?
|
|
42
|
-
.navigateToURL(
|
|
43
|
-
url: URL(string: pwaManager.start_url)!
|
|
44
|
-
)
|
|
45
|
-
},
|
|
46
|
-
label: {
|
|
47
|
-
Image(systemName: "house.fill")
|
|
48
|
-
}
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
}
|
|
21
|
+
ZStack {
|
|
22
|
+
ZStack {
|
|
23
|
+
HStack {
|
|
24
|
+
Image("logo").resizable().frame(width: 44, height: 44)
|
|
25
|
+
.cornerRadius(100)
|
|
26
|
+
NavButton(action: { print("click"); showNav = true }, children: Image(systemName: "link"))
|
|
27
|
+
}
|
|
28
|
+
.padding(12)
|
|
29
|
+
.background(.thinMaterial)
|
|
30
|
+
.hoverEffect(in: navHoverGroup) { effect, isActive, _ in
|
|
31
|
+
effect.opacity((isActive && !showNav) ? 1 : 0)
|
|
52
32
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
33
|
+
Image("nav").resizable().frame(width: 29, height: 29).hoverEffect(.highlight).hoverEffect(in: navHoverGroup) { effect, isActive, _ in
|
|
34
|
+
effect.opacity((isActive && !showNav) ? 0 : 1)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
.frame(height: contentHeight)
|
|
38
|
+
.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 {
|
|
48
|
+
HStack(spacing: 14) {
|
|
49
|
+
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
|
+
}
|
|
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
|
+
}
|
|
58
|
+
.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
|
+
}
|
|
64
72
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
Button(action: {
|
|
73
|
+
return AnyView(EmptyView())
|
|
74
|
+
})
|
|
75
|
+
.frame(width: .maximum(300, texWidth))
|
|
76
|
+
NavButton(action: {
|
|
70
77
|
UIPasteboard.general.string = swc?.getURL()?.absoluteString ?? ""
|
|
71
78
|
showCopyTip = true
|
|
72
79
|
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
|
73
80
|
showCopyTip = false
|
|
74
81
|
}
|
|
75
|
-
},
|
|
76
|
-
|
|
77
|
-
})
|
|
82
|
+
}, children: Image(systemName: "square.on.square"))
|
|
83
|
+
NavButton(action: { showUrl = false }, children: Image(systemName: "xmark"))
|
|
78
84
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
Image(systemName: showUrl ? "xmark" : "info")
|
|
86
|
-
})
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
.padding()
|
|
90
|
-
.glassBackgroundEffect(in: RoundedRectangle(cornerRadius: 15))
|
|
91
|
-
.popover(isPresented: $showCopyTip) {
|
|
92
|
-
Text("copied!")
|
|
93
|
-
.padding()
|
|
94
|
-
.cornerRadius(10)
|
|
95
|
-
}
|
|
96
|
-
.overlay(GeometryReader { geo -> AnyView in
|
|
97
|
-
DispatchQueue.main.async {
|
|
98
|
-
if firstGetSize {
|
|
99
|
-
navWidth = geo.size.width
|
|
100
|
-
navHeight = geo.size.height
|
|
101
|
-
firstGetSize.toggle()
|
|
102
|
-
showUrl.toggle()
|
|
103
|
-
print(navWidth)
|
|
85
|
+
.padding(12)
|
|
86
|
+
.glassBackgroundEffect(in: RoundedRectangle(cornerRadius: 100))
|
|
87
|
+
.popover(isPresented: $showCopyTip) {
|
|
88
|
+
Text("copied!")
|
|
89
|
+
.padding()
|
|
90
|
+
.cornerRadius(10)
|
|
104
91
|
}
|
|
105
92
|
}
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
93
|
+
}
|
|
94
|
+
// }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// else{
|
|
98
|
+
|
|
99
|
+
// }
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
struct NavButtonStyle: ButtonStyle {
|
|
104
|
+
func makeBody(configuration: Configuration) -> some View {
|
|
105
|
+
configuration.label
|
|
106
|
+
.background(Color.clear)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
struct NavButton: View {
|
|
111
|
+
var action: () -> Void
|
|
112
|
+
var children: Image
|
|
113
|
+
var size: CGFloat = 44
|
|
114
|
+
var padding: CGFloat = 10
|
|
115
|
+
var clearBackGround: Bool = false
|
|
116
|
+
var body: some View {
|
|
117
|
+
if clearBackGround {
|
|
118
|
+
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)
|
|
126
|
+
} else {
|
|
127
|
+
Button(action: action, label: {
|
|
128
|
+
Circle()
|
|
129
|
+
.fill(Color.white.opacity(0))
|
|
130
|
+
.frame(width: size, height: size)
|
|
131
|
+
.overlay(
|
|
132
|
+
children.frame(width: size, height: size).padding(padding)
|
|
133
|
+
)
|
|
134
|
+
}).frame(width: size, height: size).hoverEffect(.highlight)
|
|
109
135
|
}
|
|
110
|
-
.frame(width: firstGetSize ? .infinity : navWidth, height: firstGetSize ? .infinity : navHeight)
|
|
111
|
-
.offset(z: 30)
|
|
112
136
|
}
|
|
113
137
|
}
|
|
114
138
|
|
|
@@ -11,7 +11,7 @@ let clock = PerfClock()
|
|
|
11
11
|
let logger = Logger()
|
|
12
12
|
|
|
13
13
|
// To load a local path, remove http:// eg. "static-web/"
|
|
14
|
-
let nativeAPIVersion =
|
|
14
|
+
let nativeAPIVersion = pwaManager.getVersion()
|
|
15
15
|
|
|
16
16
|
// start URL
|
|
17
17
|
let startURL = pwaManager.start_url
|
|
@@ -22,6 +22,7 @@
|
|
|
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
24
|
2B67BBAC2D151C1A00BBC689 /* manifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B67BBAB2D151C1500BBC689 /* manifest.swift */; };
|
|
25
|
+
2B6BA5DF2DF0607400D40337 /* nav.png in Resources */ = {isa = PBXBuildFile; fileRef = 2B6BA5DE2DF0607400D40337 /* nav.png */; };
|
|
25
26
|
2B85209C2BFD5FDB0038FE29 /* UpdateSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B85209B2BFD5FDB0038FE29 /* UpdateSystem.swift */; };
|
|
26
27
|
2B9909932C3605A9004826D1 /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B9909922C3605A9004826D1 /* Window.swift */; };
|
|
27
28
|
2BA06D712D4380C60020B505 /* LoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BA06D702D4380C30020B505 /* LoadingView.swift */; };
|
|
@@ -31,7 +32,6 @@
|
|
|
31
32
|
2BC261222D38F1DA00BCA977 /* NavView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BC261202D38F1DA00BCA977 /* NavView.swift */; };
|
|
32
33
|
2BC9A2212D1E829B00912170 /* HideViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BC9A2202D1E829B00912170 /* HideViewModifier.swift */; };
|
|
33
34
|
2BD510562D54A30D0001E5E6 /* SceneManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BD510552D54A2FF0001E5E6 /* SceneManager.swift */; };
|
|
34
|
-
2BD5880E2DB1237F00C0E13B /* version.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BD5880D2DB1236000C0E13B /* version.swift */; };
|
|
35
35
|
2BDBED5F2D3F885A0065443F /* SpatialModel3DComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BDBED5E2D3F885A0065443F /* SpatialModel3DComponent.swift */; };
|
|
36
36
|
2BDBED632D3FE8EC0065443F /* SpatialModel3DView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BDBED622D3FE8EA0065443F /* SpatialModel3DView.swift */; };
|
|
37
37
|
2BDBF9B62C4ED9F600D269D7 /* static-web in Resources */ = {isa = PBXBuildFile; fileRef = 2BDBF9B52C4ED9F600D269D7 /* static-web */; };
|
|
@@ -77,6 +77,7 @@
|
|
|
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
79
|
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>"; };
|
|
80
81
|
2B85209B2BFD5FDB0038FE29 /* UpdateSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateSystem.swift; sourceTree = "<group>"; };
|
|
81
82
|
2B9909922C3605A9004826D1 /* Window.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Window.swift; sourceTree = "<group>"; };
|
|
82
83
|
2BA06D702D4380C30020B505 /* LoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingView.swift; sourceTree = "<group>"; };
|
|
@@ -86,7 +87,6 @@
|
|
|
86
87
|
2BC261202D38F1DA00BCA977 /* NavView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavView.swift; sourceTree = "<group>"; };
|
|
87
88
|
2BC9A2202D1E829B00912170 /* HideViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HideViewModifier.swift; sourceTree = "<group>"; };
|
|
88
89
|
2BD510552D54A2FF0001E5E6 /* SceneManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneManager.swift; sourceTree = "<group>"; };
|
|
89
|
-
2BD5880D2DB1236000C0E13B /* version.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = version.swift; sourceTree = "<group>"; };
|
|
90
90
|
2BDBED5E2D3F885A0065443F /* SpatialModel3DComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpatialModel3DComponent.swift; sourceTree = "<group>"; };
|
|
91
91
|
2BDBED622D3FE8EA0065443F /* SpatialModel3DView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpatialModel3DView.swift; sourceTree = "<group>"; };
|
|
92
92
|
2BDBF9B52C4ED9F600D269D7 /* static-web */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "static-web"; sourceTree = "<group>"; };
|
|
@@ -126,7 +126,6 @@
|
|
|
126
126
|
isa = PBXGroup;
|
|
127
127
|
children = (
|
|
128
128
|
2B0FF4F32DD2711800C3F20A /* PerfClock.swift */,
|
|
129
|
-
2BD5880D2DB1236000C0E13B /* version.swift */,
|
|
130
129
|
2BD510552D54A2FF0001E5E6 /* SceneManager.swift */,
|
|
131
130
|
2B0B43092CBE21540003CEF3 /* CommandManager.swift */,
|
|
132
131
|
2B0B1C0F2C494E5400E644F9 /* Logger.swift */,
|
|
@@ -161,6 +160,7 @@
|
|
|
161
160
|
2B2F1D8A2BED4CD0006897EE /* libs */,
|
|
162
161
|
2B2F1D6A2BEBFAAA006897EE /* web_spatialApp.swift */,
|
|
163
162
|
2B2F1D702BEBFAAC006897EE /* Assets.xcassets */,
|
|
163
|
+
2B6BA5DE2DF0607400D40337 /* nav.png */,
|
|
164
164
|
2B2F1D752BEBFAAC006897EE /* Info.plist */,
|
|
165
165
|
2B2F1D722BEBFAAC006897EE /* Preview Content */,
|
|
166
166
|
);
|
|
@@ -354,6 +354,7 @@
|
|
|
354
354
|
files = (
|
|
355
355
|
2BDBF9B62C4ED9F600D269D7 /* static-web in Resources */,
|
|
356
356
|
2B2F1D742BEBFAAC006897EE /* Preview Assets.xcassets in Resources */,
|
|
357
|
+
2B6BA5DF2DF0607400D40337 /* nav.png in Resources */,
|
|
357
358
|
2B2F1D712BEBFAAC006897EE /* Assets.xcassets in Resources */,
|
|
358
359
|
);
|
|
359
360
|
runOnlyForDeploymentPostprocessing = 0;
|
|
@@ -391,7 +392,6 @@
|
|
|
391
392
|
2BDBED5F2D3F885A0065443F /* SpatialModel3DComponent.swift in Sources */,
|
|
392
393
|
2B2F1D6B2BEBFAAA006897EE /* web_spatialApp.swift in Sources */,
|
|
393
394
|
2B0B1C102C494E5400E644F9 /* Logger.swift in Sources */,
|
|
394
|
-
2BD5880E2DB1237F00C0E13B /* version.swift in Sources */,
|
|
395
395
|
2B37E8E12D002C4D0096749A /* MaterialWithBorderCornerModifier.swift in Sources */,
|
|
396
396
|
2BC261222D38F1DA00BCA977 /* NavView.swift in Sources */,
|
|
397
397
|
2BAC1BE22CDC49040022E29B /* SpatialViewUI.swift in Sources */,
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import Foundation
|
|
2
|
-
|
|
3
|
-
struct PackageJSON: Codable {
|
|
4
|
-
let version: String
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
func getPackageVersion() -> String {
|
|
8
|
-
let currentFileURL = URL(fileURLWithPath: #file) // path of version
|
|
9
|
-
let currentDirectoryURL = currentFileURL.deletingLastPathComponent()
|
|
10
|
-
|
|
11
|
-
let packageJSONURL = currentDirectoryURL
|
|
12
|
-
.deletingLastPathComponent()
|
|
13
|
-
.deletingLastPathComponent()
|
|
14
|
-
.deletingLastPathComponent() // .. project root
|
|
15
|
-
.appendingPathComponent("package.json")
|
|
16
|
-
|
|
17
|
-
do {
|
|
18
|
-
// read file
|
|
19
|
-
let data = try Data(contentsOf: packageJSONURL)
|
|
20
|
-
let package = try JSONDecoder().decode(PackageJSON.self, from: data)
|
|
21
|
-
return package.version
|
|
22
|
-
} catch {
|
|
23
|
-
fatalError("⚠️ Error reading package.json at \(packageJSONURL.path): \(error)")
|
|
24
|
-
}
|
|
25
|
-
}
|