Needle Engine

Changes between version 3.2.12-alpha and 3.2.13-alpha
Files changed (12) hide show
  1. plugins/vite/alias.js +1 -4
  2. plugins/vite/copyfiles.js +46 -40
  3. plugins/vite/index.js +2 -0
  4. plugins/vite/meta.js +5 -2
  5. src/engine/codegen/register_types.js +2 -0
  6. plugins/vite/reload.js +17 -13
  7. src/engine-components/codegen/components.ts +1 -0
  8. src/engine/engine_serialization_core.ts +1 -1
  9. src/engine/engine_utils.ts +7 -7
  10. src/engine-components/SceneSwitcher.ts +38 -5
  11. src/engine-components/export/usdz/USDZExporter.ts +73 -28
  12. plugins/vite/peer.js +28 -0
plugins/vite/alias.js CHANGED
@@ -33,10 +33,7 @@
33
33
  return {
34
34
  name: "needle-alias",
35
35
  config(config) {
36
- setTimeout(() => {
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;
plugins/vite/copyfiles.js CHANGED
@@ -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
- apply: 'build',
19
- async closeBundle() {
20
- const baseDir = process.cwd();
21
- const pluginName = "needle-copy-files";
16
+ buildStart() {
17
+ return run(false, config);
18
+ },
19
+ closeBundle() {
20
+ return run(true, config);
21
+ },
22
+ }
23
+ }
22
24
 
23
- let assetsDirName = "assets";
24
- let outdirName = "dist";
25
+ async function run(isBuild, config) {
26
+ const copyIncludesFromEngine = config?.copyIncludesFromEngine ?? true;
25
27
 
26
- const needleConfig = tryLoadProjectConfig();
27
- if(needleConfig){
28
- assetsDirName = needleConfig.assetsDirectory;
29
- }
28
+ const baseDir = process.cwd();
29
+ const pluginName = "needle-copy-files";
30
30
 
31
- const outDir = resolve(baseDir, outdirName);
32
- if (!existsSync(outDir)) {
33
- mkdirSync(outDir);
34
- }
31
+ let assetsDirName = "assets";
32
+ let outdirName = "dist";
35
33
 
36
- if (copyIncludesFromEngine !== false) {
37
- // copy include from engine
38
- const engineIncludeDir = resolve(baseDir, 'node_modules', '@needle-tools', 'engine', 'src', 'include');
39
- if (existsSync(engineIncludeDir)) {
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
- // copy assets dir
47
- const assetsDir = resolve(baseDir, assetsDirName);
48
- if (existsSync(assetsDir)) {
49
- console.log(`[${pluginName}] - Copy assets to ${outdirName}/${builtAssetsDirectory()}`)
50
- const targetDir = resolve(outDir, 'assets');
51
- copyRecursiveSync(assetsDir, targetDir);
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);
plugins/vite/index.js CHANGED
@@ -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;
plugins/vite/meta.js CHANGED
@@ -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
  }
src/engine/codegen/register_types.js CHANGED
@@ -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);
plugins/vite/reload.js CHANGED
@@ -61,22 +61,26 @@
61
61
  transformIndexHtml: {
62
62
  enforce: 'pre',
63
63
  transform(html, _) {
64
- if (config?.allowHotReload === false) return [html];
65
- if (userSettings?.allowHotReload === false) return [html];
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
- tag: 'script',
70
- attrs: {
71
- type: 'module',
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
- children: readFileSync(file, 'utf8'),
74
- injectTo: 'body',
75
- },
76
- ];
78
+ ]
79
+ }
80
+
77
81
  },
78
- },
79
- };
82
+ }
83
+ }
80
84
  }
81
85
 
82
86
 
src/engine-components/codegen/components.ts CHANGED
@@ -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";
src/engine/engine_serialization_core.ts CHANGED
@@ -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
src/engine/engine_utils.ts CHANGED
@@ -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(null, title, "?" + urlParams.toString());
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(null, title, "?" + urlParams.toString());
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) : 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("/");
src/engine-components/SceneSwitcher.ts CHANGED
@@ -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
- await this.tryLoadFromQueryParam();
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
- // save the loaded scene as an url parameter
205
- if (this.queryParameterName?.length)
206
- setParamWithoutReload(this.queryParameterName, index.toString(), this.useHistory);
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
  }
src/engine-components/export/usdz/USDZExporter.ts CHANGED
@@ -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 type QuickLookOverlay = {
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.createQuicklookButton();
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
- this.dispatchEvent(new CustomEvent("quicklook-button-tapped", { detail: this }));
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
- obj.callToAction = "Close";
159
- obj.checkoutTitle = "🌵 Made with Needle";
160
- obj.checkoutSubtitle = "_";
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
- private _arButton?: HTMLElement;
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._arButton !== this.webxr.ARButton) {
175
- this._arButton = this.webxr.ARButton;
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
- private resetStyles(el: HTMLElement) {
218
- el.style.position = "";
219
- el.style.top = "";
220
- el.style.left = "";
221
- el.style.width = "";
222
- el.style.height = "";
223
- el.style.margin = "";
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
  }
plugins/vite/peer.js ADDED
@@ -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
+ }