@@ -33,10 +33,7 @@
|
|
33
33
|
return {
|
34
34
|
name: "needle-alias",
|
35
35
|
config(config) {
|
36
|
-
|
37
|
-
console.log('[needle-alias] ProjectDirectory: ' + projectDir);
|
38
|
-
}, 150);
|
39
|
-
|
36
|
+
console.log('[needle-alias] ProjectDirectory: ' + projectDir);
|
40
37
|
if (!config.resolve) config.resolve = {};
|
41
38
|
if (!config.resolve.alias) config.resolve.alias = {};
|
42
39
|
const aliasDict = config.resolve.alias;
|
@@ -11,57 +11,63 @@
|
|
11
11
|
return;
|
12
12
|
}
|
13
13
|
|
14
|
-
const copyIncludesFromEngine = config?.copyIncludesFromEngine ?? true;
|
15
|
-
|
16
14
|
return {
|
17
15
|
name: 'needle-copy-files',
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
buildStart() {
|
17
|
+
return run(false, config);
|
18
|
+
},
|
19
|
+
closeBundle() {
|
20
|
+
return run(true, config);
|
21
|
+
},
|
22
|
+
}
|
23
|
+
}
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
+
async function run(isBuild, config) {
|
26
|
+
const copyIncludesFromEngine = config?.copyIncludesFromEngine ?? true;
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
-
assetsDirName = needleConfig.assetsDirectory;
|
29
|
-
}
|
28
|
+
const baseDir = process.cwd();
|
29
|
+
const pluginName = "needle-copy-files";
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
mkdirSync(outDir);
|
34
|
-
}
|
31
|
+
let assetsDirName = "assets";
|
32
|
+
let outdirName = "dist";
|
35
33
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
console.log(`[${pluginName}] - Copy engine include to ${baseDir}/include`)
|
41
|
-
const targetDir = resolve(baseDir, 'include');
|
42
|
-
copyRecursiveSync(engineIncludeDir, targetDir);
|
43
|
-
}
|
44
|
-
}
|
34
|
+
const needleConfig = tryLoadProjectConfig();
|
35
|
+
if (needleConfig) {
|
36
|
+
assetsDirName = needleConfig.assetsDirectory;
|
37
|
+
}
|
45
38
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
// copy include dir
|
54
|
-
const includeDir = resolve(baseDir, 'include');
|
55
|
-
if (existsSync(includeDir)) {
|
56
|
-
console.log(`[${pluginName}] - Copy include to ${outdirName}/include`)
|
57
|
-
const targetDir = resolve(outDir, 'include');
|
58
|
-
copyRecursiveSync(includeDir, targetDir);
|
59
|
-
}
|
39
|
+
if (copyIncludesFromEngine !== false) {
|
40
|
+
// copy include from engine
|
41
|
+
const engineIncludeDir = resolve(baseDir, 'node_modules', '@needle-tools', 'engine', 'src', 'include');
|
42
|
+
if (existsSync(engineIncludeDir)) {
|
43
|
+
console.log(`[${pluginName}] - Copy engine include to ${baseDir}/include`)
|
44
|
+
const projectIncludeDir = resolve(baseDir, 'include');
|
45
|
+
copyRecursiveSync(engineIncludeDir, projectIncludeDir);
|
60
46
|
}
|
61
47
|
}
|
48
|
+
|
49
|
+
if (isBuild) {
|
50
|
+
const outDir = resolve(baseDir, outdirName);
|
51
|
+
if (!existsSync(outDir)) {
|
52
|
+
mkdirSync(outDir);
|
53
|
+
}
|
54
|
+
// copy assets dir
|
55
|
+
const assetsDir = resolve(baseDir, assetsDirName);
|
56
|
+
if (existsSync(assetsDir)) {
|
57
|
+
console.log(`[${pluginName}] - Copy assets to ${outdirName}/${builtAssetsDirectory()}`)
|
58
|
+
const targetDir = resolve(outDir, 'assets');
|
59
|
+
copyRecursiveSync(assetsDir, targetDir);
|
60
|
+
}
|
61
|
+
// copy include dir
|
62
|
+
const includeDir = resolve(baseDir, 'include');
|
63
|
+
if (existsSync(includeDir)) {
|
64
|
+
console.log(`[${pluginName}] - Copy include to ${outdirName}/include`)
|
65
|
+
const targetDir = resolve(outDir, 'include');
|
66
|
+
copyRecursiveSync(includeDir, targetDir);
|
67
|
+
}
|
68
|
+
}
|
62
69
|
}
|
63
70
|
|
64
|
-
|
65
71
|
function copyRecursiveSync(src, dest) {
|
66
72
|
var exists = existsSync(src);
|
67
73
|
var stats = exists && statSync(src);
|
@@ -8,6 +8,7 @@
|
|
8
8
|
import { needleViteAlias } from "./alias.js";
|
9
9
|
import { needleTransformCodegen } from "./transform-codegen.js";
|
10
10
|
import { needleLicense } from "./license.js";
|
11
|
+
import { needlePeerjs } from "./peer.js";
|
11
12
|
|
12
13
|
export * from "./gzip.js";
|
13
14
|
export * from "./config.js";
|
@@ -30,6 +31,7 @@
|
|
30
31
|
needleCopyFiles(command, config, userSettings),
|
31
32
|
needleTransformCodegen(command, config, userSettings),
|
32
33
|
needleDrop(command, config, userSettings),
|
34
|
+
needlePeerjs(command, config, userSettings)
|
33
35
|
];
|
34
36
|
array.push(await editorConnection(command, config, userSettings, array));
|
35
37
|
return array;
|
@@ -91,6 +91,9 @@
|
|
91
91
|
}
|
92
92
|
}
|
93
93
|
|
94
|
+
// if(!tags.filter(t => t.attrs?.name === "generator"))
|
95
|
+
tags.push({ tag: 'meta', attrs: { name: 'generator', content: 'Needle' } });
|
96
|
+
|
94
97
|
return { html, tags }
|
95
98
|
},
|
96
99
|
}
|
@@ -124,8 +127,8 @@
|
|
124
127
|
function insertNeedleCredits(html) {
|
125
128
|
const needleCredits = `<!-- 🌵 Made with Needle — https://needle.tools -->`;
|
126
129
|
html = html.replace(
|
127
|
-
/<head
|
128
|
-
needleCredits + "\n<head
|
130
|
+
/<head/,
|
131
|
+
needleCredits + "\n<head",
|
129
132
|
);
|
130
133
|
return html;
|
131
134
|
}
|
@@ -108,6 +108,7 @@
|
|
108
108
|
import { PointerEventData } from "../../engine-components/ui/PointerEvents";
|
109
109
|
import { PostProcessingHandler } from "../../engine-components/postprocessing/PostProcessingHandler";
|
110
110
|
import { PresentationMode } from "../../engine-components-experimental/Presentation";
|
111
|
+
import { QuickLookOverlay } from "../../engine-components/export/usdz/USDZExporter";
|
111
112
|
import { RawImage } from "../../engine-components/ui/Image";
|
112
113
|
import { Raycaster } from "../../engine-components/ui/Raycaster";
|
113
114
|
import { Rect } from "../../engine-components/ui/RectTransform";
|
@@ -296,6 +297,7 @@
|
|
296
297
|
TypeStore.add("PointerEventData", PointerEventData);
|
297
298
|
TypeStore.add("PostProcessingHandler", PostProcessingHandler);
|
298
299
|
TypeStore.add("PresentationMode", PresentationMode);
|
300
|
+
TypeStore.add("QuickLookOverlay", QuickLookOverlay);
|
299
301
|
TypeStore.add("RawImage", RawImage);
|
300
302
|
TypeStore.add("Raycaster", Raycaster);
|
301
303
|
TypeStore.add("Rect", Rect);
|
@@ -61,22 +61,26 @@
|
|
61
61
|
transformIndexHtml: {
|
62
62
|
enforce: 'pre',
|
63
63
|
transform(html, _) {
|
64
|
-
if (config?.allowHotReload === false) return
|
65
|
-
if (userSettings?.allowHotReload === false) return
|
64
|
+
if (config?.allowHotReload === false) return html;
|
65
|
+
if (userSettings?.allowHotReload === false) return html;
|
66
66
|
const file = path.join(__dirname, 'reload-client.js');
|
67
|
-
return
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
67
|
+
return {
|
68
|
+
html,
|
69
|
+
tags: [
|
70
|
+
{
|
71
|
+
tag: 'script',
|
72
|
+
attrs: {
|
73
|
+
type: 'module',
|
74
|
+
},
|
75
|
+
children: readFileSync(file, 'utf8'),
|
76
|
+
injectTo: 'body',
|
72
77
|
},
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
];
|
78
|
+
]
|
79
|
+
}
|
80
|
+
|
77
81
|
},
|
78
|
-
}
|
79
|
-
}
|
82
|
+
}
|
83
|
+
}
|
80
84
|
}
|
81
85
|
|
82
86
|
|
@@ -103,6 +103,7 @@
|
|
103
103
|
export { PlayerColor } from "../PlayerColor";
|
104
104
|
export { PointerEventData } from "../ui/PointerEvents";
|
105
105
|
export { PostProcessingHandler } from "../postprocessing/PostProcessingHandler";
|
106
|
+
export { QuickLookOverlay } from "../export/usdz/USDZExporter";
|
106
107
|
export { RawImage } from "../ui/Image";
|
107
108
|
export { Raycaster } from "../ui/Raycaster";
|
108
109
|
export { Rect } from "../ui/RectTransform";
|
@@ -279,8 +279,8 @@
|
|
279
279
|
for (const key in typeInfo) {
|
280
280
|
const serializedEntryInfo = typeInfo[key];
|
281
281
|
const data = serializedData[key];
|
282
|
+
if(debug) console.log(key, data, obj, serializedEntryInfo)
|
282
283
|
|
283
|
-
|
284
284
|
if (obj[key] !== undefined && data === undefined) {
|
285
285
|
// if a field is marked as serialized and has some default value
|
286
286
|
// but no data was serialized do not override the default value with undefined
|
@@ -97,12 +97,12 @@
|
|
97
97
|
url.append(paramName, paramValue.toString());
|
98
98
|
}
|
99
99
|
|
100
|
-
export function pushState(title: string, urlParams: URLSearchParams) {
|
101
|
-
window.history.pushState(
|
100
|
+
export function pushState(title: string, urlParams: URLSearchParams, state?: any) {
|
101
|
+
window.history.pushState(state, title, "?" + urlParams.toString());
|
102
102
|
}
|
103
103
|
|
104
|
-
export function setState(title: string, urlParams: URLSearchParams) {
|
105
|
-
window.history.replaceState(
|
104
|
+
export function setState(title: string, urlParams: URLSearchParams, state?: any) {
|
105
|
+
window.history.replaceState(state, title, "?" + urlParams.toString());
|
106
106
|
}
|
107
107
|
|
108
108
|
// for room id
|
@@ -212,7 +212,7 @@
|
|
212
212
|
export const relativePathPrefix = "rel:";
|
213
213
|
|
214
214
|
/** @deprecated use resolveUrl instead */
|
215
|
-
export function getPath(source:SourceIdentifier|undefined, uri:string)
|
215
|
+
export function getPath(source: SourceIdentifier | undefined, uri: string): string {
|
216
216
|
return resolveUrl(source, uri);
|
217
217
|
}
|
218
218
|
/**
|
@@ -226,7 +226,7 @@
|
|
226
226
|
if (debugGetPath) console.warn("getPath: uri is undefined, returning uri", uri);
|
227
227
|
return uri;
|
228
228
|
}
|
229
|
-
if(uri.startsWith("./")) {
|
229
|
+
if (uri.startsWith("./")) {
|
230
230
|
return uri;
|
231
231
|
}
|
232
232
|
if (uri.startsWith("http")) {
|
@@ -237,7 +237,7 @@
|
|
237
237
|
if (debugGetPath) console.warn("getPath: source is undefined, returning uri", uri);
|
238
238
|
return uri;
|
239
239
|
}
|
240
|
-
if(uri.startsWith(relativePathPrefix)){
|
240
|
+
if (uri.startsWith(relativePathPrefix)) {
|
241
241
|
uri = uri.substring(4);
|
242
242
|
}
|
243
243
|
const pathIndex = source.lastIndexOf("/");
|
@@ -14,7 +14,7 @@
|
|
14
14
|
// We need to defer import to not get issues with circular dependencies
|
15
15
|
import("../engine/engine_element").then(res => {
|
16
16
|
const webcomponent = res.NeedleEngineHTMLElement;
|
17
|
-
if(debug) console.log("SceneSwitcher: registering scene attribute", webcomponent.observedAttributes);
|
17
|
+
if (debug) console.log("SceneSwitcher: registering scene attribute", webcomponent.observedAttributes);
|
18
18
|
if (!webcomponent.observedAttributes.includes(ENGINE_ELEMENT_SCENE_ATTRIBUTE_NAME))
|
19
19
|
webcomponent.observedAttributes.push(ENGINE_ELEMENT_SCENE_ATTRIBUTE_NAME);
|
20
20
|
});
|
@@ -22,6 +22,12 @@
|
|
22
22
|
|
23
23
|
const couldNotLoadScenePromise = Promise.resolve(false);
|
24
24
|
|
25
|
+
export type LoadSceneEvent = {
|
26
|
+
switcher: SceneSwitcher;
|
27
|
+
scene: AssetReference;
|
28
|
+
index: number;
|
29
|
+
}
|
30
|
+
|
25
31
|
export class SceneSwitcher extends Behaviour {
|
26
32
|
|
27
33
|
@serializable(AssetReference)
|
@@ -108,7 +114,17 @@
|
|
108
114
|
let wasUsingHistory = this.useHistory;
|
109
115
|
try {
|
110
116
|
this.useHistory = false;
|
111
|
-
|
117
|
+
let didResolve = false;
|
118
|
+
if (this.queryParameterName)
|
119
|
+
didResolve = await this.tryLoadFromQueryParam();
|
120
|
+
if (!didResolve) {
|
121
|
+
const state = _state.state;
|
122
|
+
if (state !== null && state.startsWith(this.guid)) {
|
123
|
+
const value = state.substr(this.guid.length + 2);
|
124
|
+
console.log(value);
|
125
|
+
await this.trySelectSceneFromValue(value);
|
126
|
+
}
|
127
|
+
}
|
112
128
|
}
|
113
129
|
finally {
|
114
130
|
this.useHistory = wasUsingHistory;
|
@@ -192,7 +208,15 @@
|
|
192
208
|
const index = this._currentIndex = this.scenes?.indexOf(scene) ?? -1;
|
193
209
|
this._currentScene = scene;
|
194
210
|
try {
|
211
|
+
const loadStartEvt = new CustomEvent<LoadSceneEvent>("loadscene-start", { detail: { scene: scene, switcher: this, index: index } })
|
212
|
+
this.dispatchEvent(loadStartEvt);
|
195
213
|
await scene.loadAssetAsync();
|
214
|
+
const finishedEvt = new CustomEvent<LoadSceneEvent>("loadscene-finished", { detail: { scene: scene, switcher: this, index: index } });
|
215
|
+
this.dispatchEvent(finishedEvt);
|
216
|
+
if (finishedEvt.defaultPrevented) {
|
217
|
+
if (debug) console.warn("Adding loaded scene prevented:", scene, finishedEvt);
|
218
|
+
return false;
|
219
|
+
}
|
196
220
|
if (!scene.asset) {
|
197
221
|
if (debug) console.warn("Failed loading scene:", scene);
|
198
222
|
return false;
|
@@ -201,9 +225,18 @@
|
|
201
225
|
GameObject.add(scene.asset, this.gameObject);
|
202
226
|
if (this.useSceneLighting)
|
203
227
|
this.context.sceneLighting.enable(scene)
|
204
|
-
|
205
|
-
|
206
|
-
|
228
|
+
if (this.useHistory) {
|
229
|
+
// save the loaded scene as an url parameter
|
230
|
+
if (this.queryParameterName?.length)
|
231
|
+
setParamWithoutReload(this.queryParameterName, index.toString(), this.useHistory);
|
232
|
+
// or set the history state without updating the url parameter
|
233
|
+
else {
|
234
|
+
const lastState = history.state;
|
235
|
+
const stateName = this.guid + "::" + index;
|
236
|
+
if (lastState !== stateName)
|
237
|
+
history.pushState(stateName, "unused", location.href);
|
238
|
+
}
|
239
|
+
}
|
207
240
|
return true;
|
208
241
|
}
|
209
242
|
}
|
@@ -10,16 +10,23 @@
|
|
10
10
|
import { Behaviour, GameObject } from "../../Component";
|
11
11
|
import { WebXR } from "../../webxr/WebXR"
|
12
12
|
import { serializable } from "../../../engine/engine_serialization";
|
13
|
-
import { showBalloonWarning } from "../../../engine/debug/debug";
|
13
|
+
import { isDevEnvironment, showBalloonMessage, showBalloonWarning } from "../../../engine/debug/debug";
|
14
14
|
import { Context } from "../../../engine/engine_setup";
|
15
15
|
import { WebARSessionRoot } from "../../webxr/WebARSessionRoot";
|
16
16
|
|
17
17
|
const debug = getParam("debugusdz");
|
18
18
|
|
19
|
-
export
|
19
|
+
export class QuickLookOverlay {
|
20
|
+
@serializable()
|
20
21
|
callToAction?: string;
|
22
|
+
@serializable()
|
21
23
|
checkoutTitle?: string;
|
24
|
+
@serializable()
|
22
25
|
checkoutSubtitle?: string;
|
26
|
+
|
27
|
+
/** if assigned the call to action button in quicklook will open the URL. Otherwise it will just close quicklook. */
|
28
|
+
@serializable()
|
29
|
+
callToActionURL?: string;
|
23
30
|
}
|
24
31
|
|
25
32
|
export class USDZExporter extends Behaviour {
|
@@ -30,6 +37,9 @@
|
|
30
37
|
@serializable()
|
31
38
|
autoExportAnimations: boolean = false;
|
32
39
|
|
40
|
+
@serializable(QuickLookOverlay)
|
41
|
+
overlay?: QuickLookOverlay;
|
42
|
+
|
33
43
|
extensions: IUSDZExporterExtension[] = [];
|
34
44
|
|
35
45
|
private link!: HTMLAnchorElement;
|
@@ -39,6 +49,8 @@
|
|
39
49
|
|
40
50
|
start() {
|
41
51
|
if (debug) {
|
52
|
+
console.log(this);
|
53
|
+
console.log("Debug USDZ, press 't' to export")
|
42
54
|
window.addEventListener("keydown", (evt) => {
|
43
55
|
switch (evt.key) {
|
44
56
|
case "t":
|
@@ -58,6 +70,12 @@
|
|
58
70
|
});
|
59
71
|
|
60
72
|
if (!this.objectToExport) this.objectToExport = this.gameObject;
|
73
|
+
|
74
|
+
|
75
|
+
if (isDevEnvironment() && (!this.objectToExport || this.objectToExport.children.length <= 0)) {
|
76
|
+
showBalloonWarning("USDZ Exporter has nothing to export");
|
77
|
+
console.warn("USDZExporter has no objects to export assigned:", this)
|
78
|
+
}
|
61
79
|
}
|
62
80
|
|
63
81
|
|
@@ -66,16 +84,25 @@
|
|
66
84
|
const ios = isiOS()
|
67
85
|
const safari = isSafari();
|
68
86
|
if (debug || (ios && safari)) {
|
69
|
-
this.
|
87
|
+
this.addQuicklookButton();
|
70
88
|
this.webARSessionRoot = GameObject.findObjectOfType(WebARSessionRoot) ?? undefined;
|
71
89
|
this.lastCallback = this.quicklookCallback.bind(this);
|
72
90
|
this.link = ensureQuicklookLinkIsCreated(this.context);
|
73
91
|
this.link.addEventListener('message', this.lastCallback);
|
74
92
|
}
|
93
|
+
if (debug)
|
94
|
+
showBalloonMessage("USDZ Exporter enabled: " + this.name);
|
75
95
|
}
|
76
96
|
|
77
97
|
onDisable() {
|
78
98
|
this.link?.removeEventListener('message', this.lastCallback);
|
99
|
+
const ios = isiOS()
|
100
|
+
const safari = isSafari();
|
101
|
+
if (debug || (ios && safari)) {
|
102
|
+
this.removeQuicklookButton();
|
103
|
+
}
|
104
|
+
if (debug)
|
105
|
+
showBalloonMessage("USDZ Exporter disabled: " + this.name);
|
79
106
|
}
|
80
107
|
|
81
108
|
async exportAsync() {
|
@@ -125,14 +152,15 @@
|
|
125
152
|
|
126
153
|
// see https://developer.apple.com/documentation/arkit/adding_an_apple_pay_button_or_a_custom_action_in_ar_quick_look
|
127
154
|
const overlay = this.buildQuicklookOverlay();
|
155
|
+
console.log(overlay);
|
128
156
|
const callToAction = overlay.callToAction ? encodeURIComponent(overlay.callToAction) : "";
|
129
157
|
const checkoutTitle = overlay.checkoutTitle ? encodeURIComponent(overlay.checkoutTitle) : "";
|
130
158
|
const checkoutSubtitle = overlay.checkoutSubtitle ? encodeURIComponent(overlay.checkoutSubtitle) : "";
|
131
|
-
this.link.href = URL.createObjectURL(blob) + `#callToAction=${callToAction}&checkoutTitle=${checkoutTitle}&checkoutSubtitle=${checkoutSubtitle}
|
159
|
+
this.link.href = URL.createObjectURL(blob) + `#callToAction=${callToAction}&checkoutTitle=${checkoutTitle}&checkoutSubtitle=${checkoutSubtitle}&callToActionURL=${overlay.callToActionURL}`;
|
132
160
|
|
133
161
|
|
134
162
|
if (!this.lastCallback) {
|
135
|
-
this.lastCallback = this.quicklookCallback.bind(this)
|
163
|
+
this.lastCallback = this.quicklookCallback.bind(this)
|
136
164
|
this.link.addEventListener('message', this.lastCallback);
|
137
165
|
}
|
138
166
|
|
@@ -146,24 +174,44 @@
|
|
146
174
|
|
147
175
|
private lastCallback?: any;
|
148
176
|
|
149
|
-
private quicklookCallback(event) {
|
177
|
+
private quicklookCallback(event: Event) {
|
150
178
|
if ((event as any)?.data == '_apple_ar_quicklook_button_tapped') {
|
151
179
|
if (debug) showBalloonWarning("Quicklook closed via call to action button");
|
152
|
-
|
180
|
+
var evt = new CustomEvent("quicklook-button-tapped", { detail: this });
|
181
|
+
this.dispatchEvent(evt);
|
182
|
+
if (!evt.defaultPrevented) {
|
183
|
+
const url = new URLSearchParams(this.link.href);
|
184
|
+
if (url) {
|
185
|
+
const callToActionURL = url.get("callToActionURL");
|
186
|
+
if (debug)
|
187
|
+
showBalloonMessage("Quicklook url: " + callToActionURL);
|
188
|
+
if (callToActionURL) {
|
189
|
+
globalThis.open(callToActionURL, "_blank");
|
190
|
+
}
|
191
|
+
}
|
192
|
+
}
|
153
193
|
}
|
154
194
|
}
|
155
195
|
|
156
196
|
private buildQuicklookOverlay(): QuickLookOverlay {
|
157
197
|
const obj: QuickLookOverlay = {};
|
158
|
-
|
159
|
-
obj.
|
160
|
-
|
198
|
+
if (this.overlay) Object.assign(obj, this.overlay);
|
199
|
+
if (!obj.callToAction?.length)
|
200
|
+
obj.callToAction = "Close";
|
201
|
+
if (!obj.checkoutTitle?.length)
|
202
|
+
obj.checkoutTitle = "🌵 Made with Needle";
|
203
|
+
if (!obj.checkoutSubtitle?.length)
|
204
|
+
obj.checkoutSubtitle = "_";
|
161
205
|
// Use the quicklook-overlay event to customize the overlay
|
162
206
|
this.dispatchEvent(new CustomEvent("quicklook-overlay", { detail: obj }));
|
163
207
|
return obj;
|
164
208
|
}
|
165
209
|
|
166
|
-
|
210
|
+
|
211
|
+
|
212
|
+
|
213
|
+
private _quicklookButton?: HTMLElement;
|
214
|
+
|
167
215
|
private async createQuicklookButton() {
|
168
216
|
if (!this.webxr) {
|
169
217
|
await delay(1);
|
@@ -171,8 +219,8 @@
|
|
171
219
|
if (this.webxr) {
|
172
220
|
if (this.webxr.VRButton) this.webxr.VRButton.parentElement?.removeChild(this.webxr.VRButton);
|
173
221
|
// check if we have an AR button already and re-use that
|
174
|
-
if (this.webxr.ARButton && this.
|
175
|
-
this.
|
222
|
+
if (this.webxr.ARButton && this._quicklookButton !== this.webxr.ARButton) {
|
223
|
+
this._quicklookButton = this.webxr.ARButton;
|
176
224
|
// Hack to remove the immersiveweb link
|
177
225
|
const linkInButton = this.webxr.ARButton.parentElement?.querySelector("a");
|
178
226
|
if (linkInButton) {
|
@@ -185,6 +233,7 @@
|
|
185
233
|
this.exportAsync();
|
186
234
|
});
|
187
235
|
this.webxr.ARButton.classList.add("quicklook-ar-button");
|
236
|
+
this._quicklookButtonContainer = this.webxr.ARButton.parentElement;
|
188
237
|
this.dispatchEvent(new CustomEvent("created-button", { detail: this.webxr.ARButton }))
|
189
238
|
}
|
190
239
|
// create a button if WebXR didnt create one yet
|
@@ -205,6 +254,7 @@
|
|
205
254
|
button.classList.add('webxr-button');
|
206
255
|
button.classList.add("quicklook-ar-button");
|
207
256
|
container.appendChild(button);
|
257
|
+
this._quicklookButtonContainer = container;
|
208
258
|
this.dispatchEvent(new CustomEvent("created-button", { detail: button }))
|
209
259
|
}
|
210
260
|
}
|
@@ -214,20 +264,15 @@
|
|
214
264
|
}
|
215
265
|
}
|
216
266
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
el.style.padding = "";
|
225
|
-
el.style.border = "";
|
226
|
-
el.style.background = "";
|
227
|
-
el.style.color = "";
|
228
|
-
el.style.font = "";
|
229
|
-
el.style.textAlign = "";
|
230
|
-
el.style.opacity = "";
|
231
|
-
el.style.zIndex = "";
|
267
|
+
|
268
|
+
private _quicklookButtonContainer: Element | null = null;
|
269
|
+
private async addQuicklookButton() {
|
270
|
+
await this.createQuicklookButton();
|
271
|
+
if (this._quicklookButton && this._quicklookButtonContainer) {
|
272
|
+
this._quicklookButtonContainer.appendChild(this._quicklookButton);
|
273
|
+
}
|
232
274
|
}
|
275
|
+
private removeQuicklookButton() {
|
276
|
+
this._quicklookButton?.remove();
|
277
|
+
}
|
233
278
|
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
const peerjsString = `/* needle: injected fix for peerjs */
|
3
|
+
window.global = window;
|
4
|
+
var parcelRequire;`
|
5
|
+
|
6
|
+
export const needlePeerjs = (command, config, userSettings) => {
|
7
|
+
|
8
|
+
if (userSettings.noPeer === true) return;
|
9
|
+
|
10
|
+
return {
|
11
|
+
name: 'needle-peerjs',
|
12
|
+
transformIndexHtml: {
|
13
|
+
enforce: 'pre',
|
14
|
+
transform(html, _ctx) {
|
15
|
+
return {
|
16
|
+
html,
|
17
|
+
tags: [
|
18
|
+
{
|
19
|
+
tag: 'script',
|
20
|
+
children: peerjsString,
|
21
|
+
injectTo: 'body-prepend',
|
22
|
+
},
|
23
|
+
]
|
24
|
+
}
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
28
|
+
}
|