@webspatial/builder 0.0.9 → 0.0.11
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/dist/index.js +1 -1
- package/dist/lib/Cli.js +1 -1
- package/dist/lib/cmds/build.d.ts +1 -1
- package/dist/lib/cmds/build.js +1 -1
- package/dist/lib/cmds/check.js +1 -1
- package/dist/lib/cmds/help.js +1 -1
- package/dist/lib/cmds/version.js +1 -1
- package/dist/lib/pwa/config.js +1 -1
- package/dist/lib/pwa/index.js +1 -1
- package/dist/lib/pwa/validate.js +1 -1
- package/dist/lib/resource/file.js +1 -1
- package/dist/lib/resource/imageHelper.js +1 -1
- package/dist/lib/resource/index.d.ts +10 -3
- package/dist/lib/resource/index.js +1 -1
- package/dist/lib/resource/load.js +1 -1
- package/dist/lib/utils/CustomError.js +1 -1
- package/dist/lib/utils/FetchUtils-1.js +1 -1
- package/dist/lib/utils/Log.js +1 -1
- package/dist/lib/utils/fetch.js +1 -1
- package/dist/lib/utils/messages.js +1 -1
- package/dist/lib/utils/utils.d.ts +9 -0
- package/dist/lib/utils/utils.js +1 -1
- package/dist/lib/xcode/index.js +1 -1
- package/dist/lib/xcode/manifestSwiftTemplate.d.ts +1 -1
- package/dist/lib/xcode/manifestSwiftTemplate.js +1 -1
- package/dist/lib/xcode/xcodebuild.js +1 -1
- package/dist/lib/xcode/xcodeproject.js +1 -1
- package/dist/lib/xcode/xcrun.js +1 -1
- package/package.json +3 -2
- package/template/visionOSApp/Packages/RealityKitContent/.build/workspace-state.json +0 -7
- package/template/visionOSApp/Packages/RealityKitContent/.swiftpm/xcode/xcuserdata/bytedance.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
- package/template/visionOSApp/Packages/RealityKitContent/Package.realitycomposerpro/ProjectData/main.json +0 -11
- package/template/visionOSApp/Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/SceneMetadataList.json +0 -112
- package/template/visionOSApp/Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/Settings.rcprojectdata +0 -17
- package/template/visionOSApp/Packages/RealityKitContent/Package.swift +0 -27
- package/template/visionOSApp/Packages/RealityKitContent/README.md +0 -3
- package/template/visionOSApp/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Immersive.usda +0 -50
- package/template/visionOSApp/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Materials/GridMaterial.usda +0 -216
- package/template/visionOSApp/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Scene.usda +0 -59
- package/template/visionOSApp/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.swift +0 -4
- package/template/visionOSApp/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json +0 -12
- package/template/visionOSApp/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json +0 -6
- package/template/visionOSApp/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Contents.json +0 -17
- package/template/visionOSApp/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json +0 -12
- package/template/visionOSApp/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json +0 -6
- package/template/visionOSApp/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json +0 -12
- package/template/visionOSApp/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json +0 -6
- package/template/visionOSApp/web-spatial/Assets.xcassets/Contents.json +0 -6
- package/template/visionOSApp/web-spatial/Info.plist +0 -33
- package/template/visionOSApp/web-spatial/Preview Content/Preview Assets.xcassets/Contents.json +0 -6
- package/template/visionOSApp/web-spatial/libs/EventEmitter.swift +0 -32
- package/template/visionOSApp/web-spatial/libs/SpatialComponent.swift +0 -31
- package/template/visionOSApp/web-spatial/libs/SpatialEntity.swift +0 -179
- package/template/visionOSApp/web-spatial/libs/SpatialInputComponent.swift +0 -26
- package/template/visionOSApp/web-spatial/libs/SpatialMeshResource.swift +0 -19
- package/template/visionOSApp/web-spatial/libs/SpatialModel3DComponent.swift +0 -51
- package/template/visionOSApp/web-spatial/libs/SpatialModelComponent.swift +0 -32
- package/template/visionOSApp/web-spatial/libs/SpatialObject.swift +0 -144
- package/template/visionOSApp/web-spatial/libs/SpatialPhysicallyBasedMaterial.swift +0 -19
- package/template/visionOSApp/web-spatial/libs/SpatialViewComponent.swift +0 -15
- package/template/visionOSApp/web-spatial/libs/SpatialWindowComponent.swift +0 -420
- package/template/visionOSApp/web-spatial/libs/SpatialWindowContainer.swift +0 -149
- package/template/visionOSApp/web-spatial/libs/Utils/CommandManager.swift +0 -800
- package/template/visionOSApp/web-spatial/libs/Utils/Logger.swift +0 -36
- package/template/visionOSApp/web-spatial/libs/Utils/SceneManager.swift +0 -108
- package/template/visionOSApp/web-spatial/libs/Utils/WindowContainerMgr.swift +0 -113
- package/template/visionOSApp/web-spatial/libs/json/JsonParser.swift +0 -52
- package/template/visionOSApp/web-spatial/libs/uiKitDelegate/Window.swift +0 -34
- package/template/visionOSApp/web-spatial/libs/webView/UpdateSystem.swift +0 -33
- package/template/visionOSApp/web-spatial/libs/webView/backend/NativeWebView.swift +0 -319
- package/template/visionOSApp/web-spatial/libs/webView/manifest.swift +0 -92
- package/template/visionOSApp/web-spatial/static-web/index.html +0 -9
- package/template/visionOSApp/web-spatial/views/HideViewModifier.swift +0 -17
- package/template/visionOSApp/web-spatial/views/ImmersiveView.swift +0 -24
- package/template/visionOSApp/web-spatial/views/LoadingView.swift +0 -25
- package/template/visionOSApp/web-spatial/views/MaterialWithBorderCornerModifier.swift +0 -82
- package/template/visionOSApp/web-spatial/views/OpenDismissHandlerUI.swift +0 -52
- package/template/visionOSApp/web-spatial/views/PlainWindowContainerView.swift +0 -84
- package/template/visionOSApp/web-spatial/views/SpatialModel3DView.swift +0 -193
- package/template/visionOSApp/web-spatial/views/SpatialViewUI.swift +0 -168
- package/template/visionOSApp/web-spatial/views/SpatialWebViewUI.swift +0 -186
- package/template/visionOSApp/web-spatial/views/VolumetricWindowContainerView.swift +0 -38
- package/template/visionOSApp/web-spatial/views/ui/NavView.swift +0 -125
- package/template/visionOSApp/web-spatial/web_spatialApp.swift +0 -158
- package/template/visionOSApp/web-spatial.xcodeproj/project.pbxproj +0 -686
- package/template/visionOSApp/web-spatial.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/template/visionOSApp/web-spatial.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- package/template/visionOSApp/web-spatial.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +0 -5
- package/template/visionOSApp/web-spatial.xcodeproj/project.xcworkspace/xcuserdata/bytedance.xcuserdatad/WorkspaceSettings.xcsettings +0 -14
- package/template/visionOSApp/web-spatial.xcodeproj/xcshareddata/xcschemes/web-spatial.xcscheme +0 -115
- package/template/visionOSApp/web-spatial.xcodeproj/xcuserdata/bytedance.xcuserdatad/xcschemes/xcschememanagement.plist +0 -27
- package/template/visionOSApp/web-spatialTests/web_spatialTests.swift +0 -34
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// SpatialViewUI.swift
|
|
3
|
-
// web-spatial
|
|
4
|
-
//
|
|
5
|
-
// Created by ByteDance on 11/6/24.
|
|
6
|
-
//
|
|
7
|
-
import RealityKit
|
|
8
|
-
import SwiftUI
|
|
9
|
-
|
|
10
|
-
extension View {
|
|
11
|
-
@ViewBuilder
|
|
12
|
-
func `if`<Content: View>(_ condition: Bool, transform: (Self) -> Content) -> some View {
|
|
13
|
-
if condition {
|
|
14
|
-
transform(self)
|
|
15
|
-
} else {
|
|
16
|
-
self
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
struct SpatialViewUI: View {
|
|
22
|
-
@Environment(SpatialEntity.self) var ent: SpatialEntity
|
|
23
|
-
@State var isRoot = false
|
|
24
|
-
|
|
25
|
-
// Entity which will contain all the content of this realityView and scale to fit frame
|
|
26
|
-
@State var world = Entity()
|
|
27
|
-
@State var portal = Entity()
|
|
28
|
-
@State var light = PointLight()
|
|
29
|
-
@State var portalModel = ModelComponent(
|
|
30
|
-
mesh: .generatePlane(width: 1.0, height: 1.0, cornerRadius: 0.0),
|
|
31
|
-
materials: [PortalMaterial()]
|
|
32
|
-
)
|
|
33
|
-
@State var worldComponent = WorldComponent()
|
|
34
|
-
|
|
35
|
-
private func toJson(val: SIMD3<Float>) -> String {
|
|
36
|
-
return "{x: " + String(val.x) + ",y: " + String(val.y) + ",z: " + String(val.z) + "}"
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
var dragGesture: some Gesture {
|
|
40
|
-
DragGesture(minimumDistance: 0).handActivationBehavior(.automatic)
|
|
41
|
-
.targetedToAnyEntity()
|
|
42
|
-
.onChanged { value in
|
|
43
|
-
let startPos = value.convert(value.startLocation3D, from: .local, to: .scene)
|
|
44
|
-
let translate = value.convert(value.location3D, from: .local, to: .scene)
|
|
45
|
-
let spatialEntity = value.entity.components[SpatialBridgeComponent.self]!.spatialEntity
|
|
46
|
-
let ic = spatialEntity.getComponent(SpatialInputComponent.self)!
|
|
47
|
-
|
|
48
|
-
if !ic.isDragging {
|
|
49
|
-
ic.isDragging = true
|
|
50
|
-
ic.trackedPosition = startPos
|
|
51
|
-
let delta = translate - ic.trackedPosition
|
|
52
|
-
ic.trackedPosition = translate
|
|
53
|
-
|
|
54
|
-
ic.wv!.fireComponentEvent(componentId: ic.id, data: "{eventType: 'dragstart', translate: " + toJson(val: delta) + "}")
|
|
55
|
-
} else {
|
|
56
|
-
let delta = translate - ic.trackedPosition
|
|
57
|
-
ic.trackedPosition = translate
|
|
58
|
-
ic.wv!.fireComponentEvent(componentId: ic.id, data: "{eventType: 'drag', translate: " + toJson(val: delta) + "}")
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
.onEnded { value in
|
|
62
|
-
let spatialEntity = value.entity.components[SpatialBridgeComponent.self]!.spatialEntity
|
|
63
|
-
let ic = spatialEntity.getComponent(SpatialInputComponent.self)!
|
|
64
|
-
ic.wv!.fireComponentEvent(componentId: ic.id, data: "{eventType: 'dragend'}")
|
|
65
|
-
ic.isDragging = false
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
var body: some View {
|
|
70
|
-
if let viewComponent = ent.getComponent(SpatialViewComponent.self) {
|
|
71
|
-
GeometryReader3D { proxy in
|
|
72
|
-
// Get dimensions of the frame
|
|
73
|
-
let proxySize3d = proxy.frame(in: .local)
|
|
74
|
-
|
|
75
|
-
RealityView { _, _ in
|
|
76
|
-
} update: { content, attachments in
|
|
77
|
-
// Scale content so it will be a 1x1x1 space and not exceed the frame
|
|
78
|
-
let viewSpaceDimensions = content.convert(proxySize3d, from: .local, to: content)
|
|
79
|
-
let newScale = min(viewSpaceDimensions.extents.x, viewSpaceDimensions.extents.y)
|
|
80
|
-
|
|
81
|
-
world.transform.scale.x = newScale
|
|
82
|
-
world.transform.scale.y = newScale
|
|
83
|
-
world.transform.scale.z = newScale
|
|
84
|
-
portal.transform.scale.x = newScale
|
|
85
|
-
portal.transform.scale.y = newScale
|
|
86
|
-
portal.transform.scale.z = newScale
|
|
87
|
-
|
|
88
|
-
if !isRoot {
|
|
89
|
-
// Pull out content so volume sits in front of the page
|
|
90
|
-
world.transform.translation.z = world.transform.scale.z / 2
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
for (_, entity) in ent.getEntities() {
|
|
94
|
-
world.addChild(entity.modelEntity)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Add attachments for window entities
|
|
98
|
-
let entities = ent.getEntities().filter { _, entity in
|
|
99
|
-
entity.coordinateSpace == .APP && entity.hasComponent(SpatialWindowComponent.self)
|
|
100
|
-
}
|
|
101
|
-
for key in Array(entities.keys) {
|
|
102
|
-
let e = entities[key]!
|
|
103
|
-
let windowComponent = e.getComponent(SpatialWindowComponent.self)
|
|
104
|
-
if windowComponent != nil && e.coordinateSpace == .APP {
|
|
105
|
-
if let windowAttachment = attachments.entity(for: key) {
|
|
106
|
-
if e.modelEntity.children.count == 0 {
|
|
107
|
-
e.modelEntity.addChild(windowAttachment, preservingWorldTransform: false)
|
|
108
|
-
|
|
109
|
-
// Scale the window to fit the resolution to unit ratio as defined by setResolution API
|
|
110
|
-
let b = windowAttachment.attachment.bounds
|
|
111
|
-
let wv = e.getComponent(SpatialWindowComponent.self)!
|
|
112
|
-
let scaleFact = (Float(wv.resolutionX) / 1360.0) / (b.max.x - b.min.x)
|
|
113
|
-
windowAttachment.scale.x = scaleFact
|
|
114
|
-
windowAttachment.scale.y = scaleFact
|
|
115
|
-
windowAttachment.scale.z = scaleFact
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
if viewComponent.isPortal {
|
|
121
|
-
// Setup portal
|
|
122
|
-
portal.components.set(portalModel)
|
|
123
|
-
portal.transform.translation.z = 0.0001 // avoid z fighting
|
|
124
|
-
if !portal.components.has(PortalComponent.self) {
|
|
125
|
-
portal.components.set(PortalComponent(target: world))
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Setup default light
|
|
129
|
-
light.light.intensity = 5000
|
|
130
|
-
light.position.z = 2
|
|
131
|
-
light.position.y = 1
|
|
132
|
-
light.position.x = 0.5
|
|
133
|
-
world.addChild(light)
|
|
134
|
-
|
|
135
|
-
// Position volume behind portal instead of in front
|
|
136
|
-
world.transform.translation.z *= -1
|
|
137
|
-
|
|
138
|
-
// Add portal to scene
|
|
139
|
-
world.components.set(worldComponent)
|
|
140
|
-
content.add(portal)
|
|
141
|
-
} else {
|
|
142
|
-
// Remove portal elements/components
|
|
143
|
-
content.remove(portal)
|
|
144
|
-
world.components.remove(WorldComponent.self)
|
|
145
|
-
world.removeChild(light)
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
content.add(world)
|
|
149
|
-
}
|
|
150
|
-
attachments: {
|
|
151
|
-
// Create an attachment for each window component
|
|
152
|
-
let entities = ent.getEntities().filter { _, entity in
|
|
153
|
-
entity.coordinateSpace == .APP && entity.hasComponent(SpatialWindowComponent.self)
|
|
154
|
-
}
|
|
155
|
-
ForEach(Array(entities.keys), id: \.self) { key in
|
|
156
|
-
let entity = entities[key]!
|
|
157
|
-
let wv = entity.getComponent(SpatialWindowComponent.self)!
|
|
158
|
-
Attachment(id: key) {
|
|
159
|
-
SpatialWebViewUI().environment(entity).frame(width: wv.resolutionX, height: wv.resolutionY)
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}.gesture(dragGesture).if(!isRoot) { view in
|
|
163
|
-
view.clipped()
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// SpatialWebViewUI.swift
|
|
3
|
-
// web-spatial
|
|
4
|
-
//
|
|
5
|
-
// Created by ByteDance on 5/9/24.
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
import RealityKit
|
|
9
|
-
import SwiftUI
|
|
10
|
-
|
|
11
|
-
// Using scrollview has some side effects so only use it on elements we want to clip the edges of
|
|
12
|
-
// Seems only scrollview has this clipping property so far on visionOS otherwise we would use ZStack
|
|
13
|
-
struct OptionalClip<Content: View>: View {
|
|
14
|
-
var clipEnabled = true
|
|
15
|
-
let viewBuilder: () -> Content
|
|
16
|
-
|
|
17
|
-
var body: some View {
|
|
18
|
-
if clipEnabled {
|
|
19
|
-
ScrollView {
|
|
20
|
-
viewBuilder()
|
|
21
|
-
}.offset(z: CGFloat(0)).frame(maxWidth: .infinity, maxHeight: .infinity).scrollDisabled(true)
|
|
22
|
-
} else {
|
|
23
|
-
viewBuilder()
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
struct SpatialWebViewUI: View {
|
|
29
|
-
@Environment(SpatialEntity.self) var ent: SpatialEntity
|
|
30
|
-
var body: some View {
|
|
31
|
-
if let wv = ent.getComponent(SpatialWindowComponent.self) {
|
|
32
|
-
let parentYOffset = Float(wv.scrollOffset.y)
|
|
33
|
-
|
|
34
|
-
let childEntities = ent.getEntities()
|
|
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)
|
|
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
|
-
.zIndex(e.zIndex)
|
|
77
|
-
.gesture(
|
|
78
|
-
DragGesture()
|
|
79
|
-
.onChanged { gesture in
|
|
80
|
-
let scrollEnabled = view.isScrollEnabled()
|
|
81
|
-
if !scrollEnabled, wv.isScrollEnabled() {
|
|
82
|
-
if !view.dragStarted {
|
|
83
|
-
view.dragStarted = true
|
|
84
|
-
view.dragStart = (gesture.translation.height)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// TODO: this should have velocity
|
|
88
|
-
let delta = view.dragStart - gesture.translation.height
|
|
89
|
-
view.dragStart = gesture.translation.height
|
|
90
|
-
wv.updateScrollOffset(delta: delta)
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
.onEnded { _ in
|
|
94
|
-
let scrollEnabled = view.isScrollEnabled()
|
|
95
|
-
if !scrollEnabled, wv.isScrollEnabled() {
|
|
96
|
-
view.dragStarted = false
|
|
97
|
-
view.dragStart = 0
|
|
98
|
-
|
|
99
|
-
wv.stopScrolling()
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
)
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Model3D content
|
|
109
|
-
ForEach(Array(childEntities.keys), id: \.self) { key in
|
|
110
|
-
if let e = childEntities[key] {
|
|
111
|
-
let _ = e.forceUpdate ? 0 : 0
|
|
112
|
-
SpatialModel3DView(parentYOffset: parentYOffset)
|
|
113
|
-
.environment(e)
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// SpatialView content
|
|
118
|
-
ForEach(Array(childEntities.keys), id: \.self) { key in
|
|
119
|
-
if let e = childEntities[key] {
|
|
120
|
-
if e.coordinateSpace == .DOM {
|
|
121
|
-
if let viewComponent = e.getComponent(SpatialViewComponent.self) {
|
|
122
|
-
let _ = e.forceUpdate ? 0 : 0
|
|
123
|
-
let x = CGFloat(e.modelEntity.position.x)
|
|
124
|
-
let y = CGFloat(e.modelEntity.position.y - parentYOffset)
|
|
125
|
-
let z = CGFloat(e.modelEntity.position.z)
|
|
126
|
-
|
|
127
|
-
let width = CGFloat(viewComponent.resolutionX)
|
|
128
|
-
let height = CGFloat(viewComponent.resolutionY)
|
|
129
|
-
|
|
130
|
-
SpatialViewUI().environment(e).frame(width: width, height: height).scaleEffect(
|
|
131
|
-
x: CGFloat(e.modelEntity.scale.x),
|
|
132
|
-
y: CGFloat(e.modelEntity.scale.y),
|
|
133
|
-
z: CGFloat(e.modelEntity.scale.z)
|
|
134
|
-
)
|
|
135
|
-
.rotation3DEffect(
|
|
136
|
-
Rotation3D(simd_quatf(
|
|
137
|
-
ix: e.modelEntity.orientation.vector.x,
|
|
138
|
-
iy: e.modelEntity.orientation.vector.y,
|
|
139
|
-
iz: e.modelEntity.orientation.vector.z,
|
|
140
|
-
r: e.modelEntity.orientation.vector.w
|
|
141
|
-
))
|
|
142
|
-
).position(x: x, y: y)
|
|
143
|
-
.offset(z: z)
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}.frame(maxWidth: .infinity, maxHeight: .infinity).frame(maxDepth: 0, alignment: .back).offset(z: 0)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Display the main webview
|
|
152
|
-
if wv.didFailLoad {
|
|
153
|
-
VStack {
|
|
154
|
-
Text("Failed to load webpage. Is the server running?")
|
|
155
|
-
.foregroundColor(.white)
|
|
156
|
-
Button("Reload") {
|
|
157
|
-
if let url = wv.getURL() {
|
|
158
|
-
wv.navigateToURL(url: url)
|
|
159
|
-
} else {
|
|
160
|
-
logger.warning("Unable to reload URL")
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
.foregroundColor(.white)
|
|
164
|
-
}
|
|
165
|
-
.frame(maxWidth: .infinity, maxHeight: .infinity).glassBackgroundEffect()
|
|
166
|
-
|
|
167
|
-
} else {
|
|
168
|
-
wv.getView()
|
|
169
|
-
.materialWithBorderCorner(
|
|
170
|
-
wv.backgroundMaterial,
|
|
171
|
-
wv.cornerRadius
|
|
172
|
-
)
|
|
173
|
-
|
|
174
|
-
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
.opacity(wv.opacity)
|
|
178
|
-
.hidden(!ent.visible)
|
|
179
|
-
.ornament(attachmentAnchor: .scene(.bottomTrailing), contentAlignment: .bottomTrailing) {
|
|
180
|
-
if wv.isRootWebview() {
|
|
181
|
-
NavView(swc: wv)
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
//
|
|
3
|
-
// VolumetricWindowContainerView.swift
|
|
4
|
-
// web-spatial
|
|
5
|
-
//
|
|
6
|
-
// Created by ByteDance on 5/9/24.
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
import typealias RealityKit.Attachment
|
|
10
|
-
import typealias RealityKit.Entity
|
|
11
|
-
import typealias RealityKit.MeshResource
|
|
12
|
-
import typealias RealityKit.Model3D
|
|
13
|
-
import typealias RealityKit.ModelEntity
|
|
14
|
-
import typealias RealityKit.RealityView
|
|
15
|
-
import typealias RealityKit.SimpleMaterial
|
|
16
|
-
import SwiftUI
|
|
17
|
-
|
|
18
|
-
struct VolumetricWindowContainerView: View {
|
|
19
|
-
@Environment(SpatialWindowContainer.self) var windowContainerContent: SpatialWindowContainer
|
|
20
|
-
|
|
21
|
-
var body: some View {
|
|
22
|
-
OpenDismissHandlerUI().environment(windowContainerContent).onDisappear {
|
|
23
|
-
// Don't destroy immersive space content as it is reused next time its opened
|
|
24
|
-
if windowContainerContent.id != SpatialWindowContainer.ImmersiveID {
|
|
25
|
-
windowContainerContent.destroy()
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
let entities = windowContainerContent.getEntities().filter { _, entity in
|
|
30
|
-
entity.coordinateSpace == .ROOT && entity.hasComponent(SpatialViewComponent.self)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
ForEach(Array(entities.keys), id: \.self) { key in
|
|
34
|
-
let entity = entities[key]!
|
|
35
|
-
SpatialViewUI(isRoot: true).environment(entity)
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// NavView.swift
|
|
3
|
-
// web-spatial
|
|
4
|
-
//
|
|
5
|
-
// Created by ByteDance on 2025/1/8.
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
import SwiftUI
|
|
9
|
-
import WebKit
|
|
10
|
-
|
|
11
|
-
struct NavView: View {
|
|
12
|
-
@State var swc: SpatialWindowComponent?
|
|
13
|
-
@State var showUrl: Bool = true
|
|
14
|
-
@State private var showCopyTip = false
|
|
15
|
-
@State private var navWidth: CGFloat = 0
|
|
16
|
-
@State private var navHeight: CGFloat = 0
|
|
17
|
-
@State private var texWidth: CGFloat = 0
|
|
18
|
-
@State private var firstGetSize: Bool = true
|
|
19
|
-
|
|
20
|
-
var body: some View {
|
|
21
|
-
ZStack(alignment: .bottomTrailing) {
|
|
22
|
-
ZStack {}.frame(width: navWidth, height: navHeight)
|
|
23
|
-
VStack(alignment: .trailing, spacing: 5) {
|
|
24
|
-
if showUrl {
|
|
25
|
-
Text(pwaManager.name).padding(10)
|
|
26
|
-
if pwaManager.display == .minimal {
|
|
27
|
-
HStack(spacing: 5) {
|
|
28
|
-
Button(action: {
|
|
29
|
-
swc?.goBack()
|
|
30
|
-
}, label: {
|
|
31
|
-
Image(systemName: "arrow.left")
|
|
32
|
-
})
|
|
33
|
-
.disabled(!(swc?.canGoBack ?? false))
|
|
34
|
-
Button(action: {
|
|
35
|
-
swc?.goForward()
|
|
36
|
-
}, label: {
|
|
37
|
-
Image(systemName: "arrow.right")
|
|
38
|
-
})
|
|
39
|
-
.disabled(!(swc?.canGoBack ?? false))
|
|
40
|
-
Button(action: {
|
|
41
|
-
swc?.reload()
|
|
42
|
-
}, label: {
|
|
43
|
-
Image(systemName: "arrow.clockwise")
|
|
44
|
-
})
|
|
45
|
-
Button(
|
|
46
|
-
action: {
|
|
47
|
-
swc?
|
|
48
|
-
.navigateToURL(
|
|
49
|
-
url: URL(string: pwaManager.start_url)!
|
|
50
|
-
)
|
|
51
|
-
},
|
|
52
|
-
label: {
|
|
53
|
-
Image(systemName: "house.fill")
|
|
54
|
-
}
|
|
55
|
-
)
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
HStack(spacing: 5) {
|
|
60
|
-
if showUrl {
|
|
61
|
-
Text(
|
|
62
|
-
swc?.getURL()?.absoluteString ?? "http://localhost:5173/"
|
|
63
|
-
)
|
|
64
|
-
.padding()
|
|
65
|
-
.lineLimit(1)
|
|
66
|
-
.overlay(GeometryReader { geo -> AnyView in
|
|
67
|
-
DispatchQueue.main.async {
|
|
68
|
-
if geo.size.width > 0 {
|
|
69
|
-
texWidth = .minimum(300, geo.size.width)
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return AnyView(EmptyView())
|
|
73
|
-
})
|
|
74
|
-
.frame(width: texWidth == 0 ? .infinity : texWidth)
|
|
75
|
-
Button(action: {
|
|
76
|
-
UIPasteboard.general.string = swc?.getURL()?.absoluteString ?? ""
|
|
77
|
-
showCopyTip = true
|
|
78
|
-
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
|
79
|
-
showCopyTip = false
|
|
80
|
-
}
|
|
81
|
-
}, label: {
|
|
82
|
-
Text("copy")
|
|
83
|
-
})
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
Button(action: {
|
|
87
|
-
withAnimation(.easeInOut(duration: 0.5)) {
|
|
88
|
-
showUrl.toggle()
|
|
89
|
-
}
|
|
90
|
-
}, label: {
|
|
91
|
-
Image(systemName: showUrl ? "xmark" : "info")
|
|
92
|
-
})
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
.padding()
|
|
96
|
-
.glassBackgroundEffect(in: RoundedRectangle(cornerRadius: 15))
|
|
97
|
-
.popover(isPresented: $showCopyTip) {
|
|
98
|
-
Text("copied!")
|
|
99
|
-
.padding()
|
|
100
|
-
.cornerRadius(10)
|
|
101
|
-
}
|
|
102
|
-
.overlay(GeometryReader { geo -> AnyView in
|
|
103
|
-
DispatchQueue.main.async {
|
|
104
|
-
if firstGetSize {
|
|
105
|
-
navWidth = geo.size.width
|
|
106
|
-
navHeight = geo.size.height
|
|
107
|
-
firstGetSize.toggle()
|
|
108
|
-
showUrl.toggle()
|
|
109
|
-
print(navWidth)
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return AnyView(EmptyView())
|
|
113
|
-
})
|
|
114
|
-
.opacity(firstGetSize ? 0 : 1)
|
|
115
|
-
}
|
|
116
|
-
.frame(width: firstGetSize ? .infinity : navWidth, height: firstGetSize ? .infinity : navHeight)
|
|
117
|
-
.offset(z: 30)
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
struct NavView_Previews: PreviewProvider {
|
|
122
|
-
static var previews: some View {
|
|
123
|
-
NavView()
|
|
124
|
-
}
|
|
125
|
-
}
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// web_spatialApp.swift
|
|
3
|
-
// web-spatial
|
|
4
|
-
//
|
|
5
|
-
// Created by ByteDance on 5/8/24.
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
import typealias RealityKit.Attachment
|
|
9
|
-
import typealias RealityKit.Entity
|
|
10
|
-
import typealias RealityKit.MeshResource
|
|
11
|
-
import typealias RealityKit.Model3D
|
|
12
|
-
import typealias RealityKit.ModelEntity
|
|
13
|
-
import typealias RealityKit.RealityView
|
|
14
|
-
import typealias RealityKit.SimpleMaterial
|
|
15
|
-
import SwiftUI
|
|
16
|
-
|
|
17
|
-
let logger = Logger()
|
|
18
|
-
|
|
19
|
-
// To load a local path, remove http:// eg. "static-web/"
|
|
20
|
-
let nativeAPIVersion = "0.0.1"
|
|
21
|
-
|
|
22
|
-
// detect when app properties like defaultSize change so we can avoid race condition of setting default values and then opening window container
|
|
23
|
-
var sceneStateChangedCB: ((Any) -> Void) = { _ in
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
@main
|
|
27
|
-
struct web_spatialApp: App {
|
|
28
|
-
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
|
29
|
-
@State var root: SpatialWindowComponent? = nil
|
|
30
|
-
@State var rootWGD: SpatialWindowContainer
|
|
31
|
-
@State var initialLaunch = true
|
|
32
|
-
|
|
33
|
-
@ObservedObject var wgm = WindowContainerMgr.Instance
|
|
34
|
-
|
|
35
|
-
@Environment(\.scenePhase) private var scenePhase
|
|
36
|
-
|
|
37
|
-
init() {
|
|
38
|
-
logger.debug("WebSpatial App Started --------")
|
|
39
|
-
|
|
40
|
-
// init global logger
|
|
41
|
-
Logger.initLogger()
|
|
42
|
-
|
|
43
|
-
// init pwa manager
|
|
44
|
-
pwaManager._init()
|
|
45
|
-
|
|
46
|
-
// create root SpatialWindowContainer and Immersive SpatialWindowContainer
|
|
47
|
-
rootWGD = SpatialWindowContainer.createRootWindowContainer()
|
|
48
|
-
let _ = SpatialWindowContainer.createImmersiveWindowContainer()
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
func getFileUrl() -> URL {
|
|
52
|
-
return URL(string: pwaManager.start_url)!
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// There seems to be a bug in WKWebView where it needs to be initialized after the app has loaded so we do this here instead of init()
|
|
56
|
-
// https://forums.developer.apple.com/forums/thread/61432
|
|
57
|
-
func initAppOnViewMount() {
|
|
58
|
-
if root == nil {
|
|
59
|
-
let fileUrl = getFileUrl()
|
|
60
|
-
|
|
61
|
-
// Create a default entity with webview resource
|
|
62
|
-
let rootEntity = SpatialEntity()
|
|
63
|
-
rootEntity.coordinateSpace = CoordinateSpaceMode.ROOT
|
|
64
|
-
let windowComponent = SpatialWindowComponent(parentWindowContainerID: rootWGD.id, url: fileUrl)
|
|
65
|
-
rootEntity.addComponent(windowComponent)
|
|
66
|
-
rootEntity.setParentWindowContainer(wg: rootWGD)
|
|
67
|
-
|
|
68
|
-
root = windowComponent
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
func getDefaultSize() -> CGSize {
|
|
73
|
-
sceneStateChangedCB("")
|
|
74
|
-
return wgm.getValue().defaultSize!
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
var body: some Scene {
|
|
78
|
-
WindowGroup(id: "Plain", for: WindowContainerData.self) { $windowData in
|
|
79
|
-
if windowData.windowContainerID == SpatialWindowContainer.getRootID() {
|
|
80
|
-
VStack {}.onAppear { initAppOnViewMount() }
|
|
81
|
-
|
|
82
|
-
PlainWindowContainerView().environment(rootWGD).background(Color.clear.opacity(0)).onOpenURL { myURL in
|
|
83
|
-
initAppOnViewMount()
|
|
84
|
-
let urlToLoad = pwaManager.checkInDeeplink(url: myURL.absoluteString)
|
|
85
|
-
|
|
86
|
-
if let url = URL(string: urlToLoad) {
|
|
87
|
-
root!.navigateToURL(url: url)
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
} else {
|
|
91
|
-
let wg = SpatialWindowContainer.getOrCreateSpatialWindowContainer(
|
|
92
|
-
windowData.windowContainerID, windowData
|
|
93
|
-
)
|
|
94
|
-
PlainWindowContainerView().environment(wg)
|
|
95
|
-
// we no longer need the initial windowGroup to live all the time
|
|
96
|
-
// https://stackoverflow.com/questions/78567737/how-to-get-initial-windowgroup-to-reopen-on-launch-visionos
|
|
97
|
-
// .handlesExternalEvents(preferring: [], allowing: [])
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
defaultValue: {
|
|
101
|
-
WindowContainerData(
|
|
102
|
-
windowStyle: "Plain",
|
|
103
|
-
windowContainerID: SpatialWindowContainer.getRootID()
|
|
104
|
-
)
|
|
105
|
-
}
|
|
106
|
-
.windowStyle(.plain).onChange(of: scenePhase) {
|
|
107
|
-
oldPhase,
|
|
108
|
-
newPhase in
|
|
109
|
-
if oldPhase == .background && newPhase == .inactive {
|
|
110
|
-
if initialLaunch {
|
|
111
|
-
// App initial open
|
|
112
|
-
initialLaunch = false
|
|
113
|
-
} else {
|
|
114
|
-
// App reopened
|
|
115
|
-
|
|
116
|
-
let fileUrl = getFileUrl()
|
|
117
|
-
if let awid = SpatialWindowContainer.firstActivePlainWindowContainerId,
|
|
118
|
-
let wc = SpatialWindowContainer.getSpatialWindowContainer(
|
|
119
|
-
awid
|
|
120
|
-
)
|
|
121
|
-
{
|
|
122
|
-
let rootEntity = wc.getEntities().filter {
|
|
123
|
-
$0.value.getComponent(SpatialWindowComponent.self) != nil && $0.value.coordinateSpace == .ROOT
|
|
124
|
-
}.first?.value
|
|
125
|
-
|
|
126
|
-
if let wv = rootEntity?.getComponent(SpatialWindowComponent.self) {
|
|
127
|
-
wv.navigateToURL(url: fileUrl)
|
|
128
|
-
}
|
|
129
|
-
// reset to mainScene size
|
|
130
|
-
wgm.setToMainSceneCfg()
|
|
131
|
-
wc.setSize.send(getDefaultSize())
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
}.defaultSize(
|
|
137
|
-
getDefaultSize()
|
|
138
|
-
).windowResizability(
|
|
139
|
-
wgm.getValue().windowResizability!
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
WindowGroup(id: "Volumetric", for: WindowContainerData.self) { $windowData in
|
|
143
|
-
let wg = SpatialWindowContainer.getOrCreateSpatialWindowContainer(windowData!.windowContainerID, windowData!)
|
|
144
|
-
VolumetricWindowContainerView().environment(wg).handlesExternalEvents(preferring: [], allowing: [])
|
|
145
|
-
|
|
146
|
-
}.windowStyle(.volumetric).defaultSize(width: 1, height: 1, depth: 1, in: .meters)
|
|
147
|
-
|
|
148
|
-
ImmersiveSpace(id: "ImmersiveSpace") {
|
|
149
|
-
if let wg = SpatialWindowContainer.getImmersiveWindowContainer() {
|
|
150
|
-
VolumetricWindowContainerView().environment(wg).handlesExternalEvents(preferring: [], allowing: [])
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
WindowGroup(id: "loading") {
|
|
155
|
-
LoadingView()
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|