@preference-sl/pref-viewer 3.1.0-beta.0 → 3.1.0-beta.1
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/README.md +17 -0
- package/index.d.ts +19 -0
- package/package.json +1 -1
- package/src/babylonjs-controller.js +63 -1
- package/src/pref-viewer-3d.js +18 -0
- package/src/pref-viewer.js +19 -0
- package/src/quick-look-action.js +51 -10
package/README.md
CHANGED
|
@@ -54,6 +54,22 @@ On eligible Safari, the AR control uses the existing XR appearance and position.
|
|
|
54
54
|
|
|
55
55
|
The link uses `rel="ar"`, `download="model.usdz"` and a `model/vnd.usdz+zip` Blob URL with the `#allowsContentScaling=0` fragment, so its native navigation runs directly on the user tap and Quick Look keeps the authored real-world size (no pinch scaling, as on Android WebXR). Only model meshes with supported materials and current visibility are exported. Environment and viewer overlays are excluded. Physical transmission glass is exported with an equivalent alpha opacity, because USDZ has no transmission. The viewer bundles the `fflate` helper the exporter needs, so no request to `unpkg.com` is made.
|
|
56
56
|
|
|
57
|
+
#### Host-supplied Quick Look asset
|
|
58
|
+
|
|
59
|
+
A host that produces the AR asset elsewhere (for example a server-generated USDZ or USD) can hand it to the Quick Look action with `setQuickLookAssetProvider(provider)`. The provider is a function returning `Promise<{ data: Uint8Array | ArrayBuffer | Blob, mimeType?: string, fileName?: string }>`; `mimeType` defaults to `model/vnd.usdz+zip` and `fileName` to `model.usdz`. Pass `null` to clear it. The viewer never learns how the asset is made.
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
viewer.setQuickLookAssetProvider(async () => {
|
|
63
|
+
const response = await fetch("/my-server/ar-model");
|
|
64
|
+
return { data: await response.arrayBuffer(), mimeType: "model/vnd.usdz+zip", fileName: "model.usdz" };
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
- While a provider is set, the Quick Look link uses its asset instead of the Babylon USDZ export. It is called each time the link is prepared (after model/material loading and changes), only on the Quick Look route (eligible iOS/iPadOS Safari without WebXR AR).
|
|
69
|
+
- Setting or clearing the provider re-prepares the link when Quick Look is active; a provider set earlier (even before the element is connected) is retained.
|
|
70
|
+
- The ZIP signature check applies only when `mimeType` is `model/vnd.usdz+zip`. Any other MIME type (for example `model/vnd.usd`, or `text/plain` for USDA) is passed through unchanged as the Blob type, with `fileName` as the link's `download` attribute. AR Quick Look officially opens only `.usdz`/`.reality`, so what iOS does with other types is the host's experiment.
|
|
71
|
+
- If the provider throws, rejects or returns empty data, the viewer logs `PrefViewer: Quick Look asset provider failed` with `console.warn` and falls back to its own USDZ export.
|
|
72
|
+
|
|
57
73
|
Manual iOS Quick Look validation has been reported: its AR button remains usable even when the Babylon 3D view is empty. The empty scene/model failure has also been reproduced on an iPhone and remains unresolved; Quick Look validation does not establish successful in-page 3D rendering. Device/browser versions and itemized scale, glass, texture, close/reopen, iPad and production-iframe results were not supplied, so those details remain unconfirmed. Safari remote inspection is unavailable in the current test setup because no macOS system is available. An iframe must permit the browser's native AR/navigation behavior; no automatic top-level redirect or parent-message protocol is added. Used Blob URLs are retained through model replacement/detach until an observed browser return; if Safari emits no return signal, their lifetime ends with the document.
|
|
58
74
|
|
|
59
75
|
## Installation
|
|
@@ -438,6 +454,7 @@ Dialogs opened with `openDialog()`, including the **Ctrl+Alt+D** download dialog
|
|
|
438
454
|
| `downloadModelGLB()` / `downloadModelGLTF()` / `downloadModelUSDZ()` | Exports the current model. |
|
|
439
455
|
| `downloadSceneGLB()` / `downloadSceneGLTF()` / `downloadSceneUSDZ()` | Exports the current scene/environment. |
|
|
440
456
|
| `downloadModelAndSceneGLB()` / `downloadModelAndSceneGLTF()` / `downloadModelAndSceneUSDZ()` | Exports the combined model and scene. |
|
|
457
|
+
| `setQuickLookAssetProvider(provider \| null)` | Lets the host supply the asset opened by the iOS AR Quick Look action instead of the viewer's client-generated USDZ (for example a file produced by a server). See [AR and VR support](#ar-and-vr-support). |
|
|
441
458
|
|
|
442
459
|
All viewer dialogs share this priority, including the model-download dialog opened with **Ctrl+Alt+D** while the 3D canvas receives keyboard input. Existing backdrop dismissal remains available: a click over a covered control may close the dialog, but does not also activate that control. Closing the dialog restores access to the underlying controls.
|
|
443
460
|
|
package/index.d.ts
CHANGED
|
@@ -139,6 +139,18 @@ export interface PrefViewerElementClickDetail {
|
|
|
139
139
|
ids: string[];
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
/** Asset a host hands to the iOS AR Quick Look action. */
|
|
143
|
+
export interface PrefViewerQuickLookAsset {
|
|
144
|
+
data: Uint8Array | ArrayBuffer | Blob;
|
|
145
|
+
/** Defaults to `model/vnd.usdz+zip`; the ZIP signature is checked only for that type. */
|
|
146
|
+
mimeType?: string;
|
|
147
|
+
/** Defaults to `model.usdz`; used as the link's `download` attribute. */
|
|
148
|
+
fileName?: string;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Async factory of the Quick Look asset, called each time the Quick Look link is prepared. */
|
|
152
|
+
export type PrefViewerQuickLookAssetProvider = () => Promise<PrefViewerQuickLookAsset>;
|
|
153
|
+
|
|
142
154
|
/** Optional speed/direction for `startTurnable` / `toggleTurnable`. */
|
|
143
155
|
export interface PrefViewerTurnableOptions {
|
|
144
156
|
speed?: number;
|
|
@@ -356,6 +368,13 @@ export declare class PrefViewer extends HTMLElement {
|
|
|
356
368
|
setAnimationProgress(name: string, progress: number): boolean;
|
|
357
369
|
setAnimationLoop(name: string, loop: boolean): boolean;
|
|
358
370
|
|
|
371
|
+
// ── AR Quick Look (iOS) ──
|
|
372
|
+
/**
|
|
373
|
+
* Supplies (or clears with `null`) the asset opened by the iOS AR Quick Look action instead of the
|
|
374
|
+
* viewer's client-generated USDZ. A failing provider falls back to the built-in export with a console warning.
|
|
375
|
+
*/
|
|
376
|
+
setQuickLookAssetProvider(provider: PrefViewerQuickLookAssetProvider | null): void;
|
|
377
|
+
|
|
359
378
|
// ── Downloads ──
|
|
360
379
|
downloadModelGLB(): void;
|
|
361
380
|
downloadModelGLTF(): void;
|
package/package.json
CHANGED
|
@@ -72,6 +72,7 @@ import QuickLookAction from "./quick-look-action.js";
|
|
|
72
72
|
* - enable() / disable()
|
|
73
73
|
* - load(force?) / reloadWithCurrentSettings()
|
|
74
74
|
* - downloadGLB(content) / downloadGLTF(content) / downloadUSDZ(content)
|
|
75
|
+
* - setQuickLookAssetProvider(provider)
|
|
75
76
|
* - getRenderSettings() / scheduleRenderSettingsReload(settings)
|
|
76
77
|
* - getDebugInfo()
|
|
77
78
|
* - setContainerVisibility(name, show)
|
|
@@ -133,6 +134,7 @@ export default class BabylonJSController {
|
|
|
133
134
|
#xrButtons = null;
|
|
134
135
|
#xrGeneration = 0;
|
|
135
136
|
#quickLookAction = null;
|
|
137
|
+
#quickLookAssetProvider = null;
|
|
136
138
|
#quickLookGlass = new BabylonJSARGlass();
|
|
137
139
|
#quickLookLoads = 0;
|
|
138
140
|
#quickLookValid = { model: false, materials: true };
|
|
@@ -1221,6 +1223,7 @@ export default class BabylonJSController {
|
|
|
1221
1223
|
* Prepares Quick Look from completed, visible model/material state without changing download helpers.
|
|
1222
1224
|
* Invalid or absent content invalidates the link. Only supported visible model meshes are serialized;
|
|
1223
1225
|
* asynchronous export ownership, stale results and preparation errors are handled by QuickLookAction.
|
|
1226
|
+
* The prepared asset comes from the host provider when one is set (see setQuickLookAssetProvider), otherwise from the export.
|
|
1224
1227
|
* @private
|
|
1225
1228
|
* @returns {void} Starts preparation without waiting for it or changing public task-queue ordering.
|
|
1226
1229
|
*/
|
|
@@ -1248,7 +1251,66 @@ export default class BabylonJSController {
|
|
|
1248
1251
|
}
|
|
1249
1252
|
// AssetContainer contains only model meshes; applied materials and world transforms are retained.
|
|
1250
1253
|
// No environment, XR ground, ViewCube or utility-scene overlays enter this export.
|
|
1251
|
-
void this.#quickLookAction.update(() => this.#
|
|
1254
|
+
void this.#quickLookAction.update(() => this.#prepareQuickLookAsset(asset, eligible));
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
/**
|
|
1258
|
+
* Installs or clears the host-supplied provider of the asset opened by the iOS Quick Look action.
|
|
1259
|
+
* The provider is retained even before the Quick Look route exists. While Quick Look is active, changing it
|
|
1260
|
+
* re-prepares the link at once, so clearing returns to the in-browser USDZ export.
|
|
1261
|
+
* @public
|
|
1262
|
+
* @param {?(function(): Promise<{data: (Uint8Array|ArrayBuffer|Blob), mimeType?: string, fileName?: string}>)} provider
|
|
1263
|
+
* Async asset factory (defaults: MIME `model/vnd.usdz+zip`, file name `model.usdz`), or null to clear.
|
|
1264
|
+
* @returns {void} Ignores any value that is neither a function nor null by clearing the provider.
|
|
1265
|
+
*/
|
|
1266
|
+
setQuickLookAssetProvider(provider) {
|
|
1267
|
+
this.#quickLookAssetProvider = typeof provider === "function" ? provider : null;
|
|
1268
|
+
this.#refreshQuickLook();
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
/**
|
|
1272
|
+
* Prepares the Quick Look asset: the host provider's asset when one is set, otherwise (or when the provider
|
|
1273
|
+
* fails or returns no data) the in-browser USDZ export of the current model.
|
|
1274
|
+
* @private
|
|
1275
|
+
* @param {import("@babylonjs/core").AssetContainer} asset - Model container used by the fallback export.
|
|
1276
|
+
* @param {function(import("@babylonjs/core").AbstractMesh): boolean} eligible - Mesh filter for the fallback export.
|
|
1277
|
+
* @returns {Promise<Uint8Array|{data: Uint8Array, mimeType?: string, fileName?: string}>} Asset for QuickLookAction.
|
|
1278
|
+
*/
|
|
1279
|
+
async #prepareQuickLookAsset(asset, eligible) {
|
|
1280
|
+
const provider = this.#quickLookAssetProvider;
|
|
1281
|
+
if (provider) {
|
|
1282
|
+
try {
|
|
1283
|
+
return await BabylonJSController.#readQuickLookAsset(provider);
|
|
1284
|
+
} catch (error) {
|
|
1285
|
+
console.warn("PrefViewer: Quick Look asset provider failed", error);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
return this.#exportQuickLookUSDZ(asset, eligible);
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
/**
|
|
1292
|
+
* Invokes a host asset provider and converts its data to bytes.
|
|
1293
|
+
* @private
|
|
1294
|
+
* @static
|
|
1295
|
+
* @param {function(): Promise<{data: (Uint8Array|ArrayBuffer|Blob), mimeType?: string, fileName?: string}>} provider - Host asset factory.
|
|
1296
|
+
* @returns {Promise<{data: Uint8Array, mimeType?: string, fileName?: string}>} Nonempty asset with byte data.
|
|
1297
|
+
* @throws {Error} When the provider rejects, or returns no data or empty/unsupported data.
|
|
1298
|
+
*/
|
|
1299
|
+
static async #readQuickLookAsset(provider) {
|
|
1300
|
+
const result = await provider();
|
|
1301
|
+
const raw = result?.data;
|
|
1302
|
+
let data = null;
|
|
1303
|
+
if (raw instanceof Uint8Array) {
|
|
1304
|
+
data = raw;
|
|
1305
|
+
} else if (raw instanceof ArrayBuffer) {
|
|
1306
|
+
data = new Uint8Array(raw);
|
|
1307
|
+
} else if (typeof Blob !== "undefined" && raw instanceof Blob) {
|
|
1308
|
+
data = new Uint8Array(await raw.arrayBuffer());
|
|
1309
|
+
}
|
|
1310
|
+
if (!data || data.length === 0) {
|
|
1311
|
+
throw new Error("PrefViewer: Quick Look asset provider returned no data");
|
|
1312
|
+
}
|
|
1313
|
+
return { data, mimeType: result.mimeType, fileName: result.fileName };
|
|
1252
1314
|
}
|
|
1253
1315
|
|
|
1254
1316
|
/**
|
package/src/pref-viewer-3d.js
CHANGED
|
@@ -51,6 +51,7 @@ import FileStorage from "./file-storage.js";
|
|
|
51
51
|
* - downloadModelGLB(): Downloads the current 3D model as a GLB file.
|
|
52
52
|
* - downloadModelGLTF(): Downloads the current 3D model as a glTF ZIP file.
|
|
53
53
|
* - downloadModelUSDZ(): Downloads the current 3D model as a USDZ file.
|
|
54
|
+
* - setQuickLookAssetProvider(provider): Supplies (or clears) the asset opened by the iOS AR Quick Look action.
|
|
54
55
|
* - downloadModelAndSceneGLB(): Downloads both the model and scene as a GLB file.
|
|
55
56
|
* - downloadModelAndSceneGLTF(): Downloads both the model and scene as a glTF ZIP file.
|
|
56
57
|
* - downloadModelAndSceneUSDZ(): Downloads both the model and scene as a USDZ file.
|
|
@@ -102,6 +103,7 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
102
103
|
#data = null; // Component data
|
|
103
104
|
|
|
104
105
|
#babylonJSController = null; // BabylonJSController instance
|
|
106
|
+
#quickLookAssetProvider = null; // Host asset provider, reapplied to every controller this element creates
|
|
105
107
|
|
|
106
108
|
/** @returns {"svg"|"canvas"} Per-instance normalized renderer; missing/invalid attributes use Canvas. */
|
|
107
109
|
get viewCubeRenderer() {
|
|
@@ -283,6 +285,7 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
283
285
|
* Initializes the Babylon controller with asset/option state and the selected visibility-aware cube contract.
|
|
284
286
|
* Supplies current host culture and dispatches measured view-cube-layout events for render-menu positioning.
|
|
285
287
|
* Starts asynchronous engine initialization without waiting for asset loading.
|
|
288
|
+
* Reapplies any retained Quick Look asset provider to the new controller before it starts.
|
|
286
289
|
* @private
|
|
287
290
|
* @returns {void}
|
|
288
291
|
*/
|
|
@@ -303,6 +306,7 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
303
306
|
*/
|
|
304
307
|
onLayout: (offset, height) => this.dispatchEvent(new CustomEvent("view-cube-layout", { detail: { offset, height } })),
|
|
305
308
|
});
|
|
309
|
+
this.#babylonJSController.setQuickLookAssetProvider(this.#quickLookAssetProvider);
|
|
306
310
|
this.#babylonJSController.enable();
|
|
307
311
|
|
|
308
312
|
}
|
|
@@ -875,6 +879,20 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
875
879
|
this.#babylonJSController.downloadGLTF(1);
|
|
876
880
|
}
|
|
877
881
|
|
|
882
|
+
/**
|
|
883
|
+
* Sets or clears the host-supplied asset opened by the iOS AR Quick Look action instead of the in-browser USDZ export.
|
|
884
|
+
* The provider is retained across connection cycles, even when set before the element is connected.
|
|
885
|
+
* @public
|
|
886
|
+
* @param {?(function(): Promise<{data: (Uint8Array|ArrayBuffer|Blob), mimeType?: string, fileName?: string}>)} provider
|
|
887
|
+
* Async asset factory (defaults: MIME `model/vnd.usdz+zip`, file name `model.usdz`), or null to restore the built-in export.
|
|
888
|
+
* A failing provider falls back to the built-in export with a console warning.
|
|
889
|
+
* @returns {void}
|
|
890
|
+
*/
|
|
891
|
+
setQuickLookAssetProvider(provider) {
|
|
892
|
+
this.#quickLookAssetProvider = typeof provider === "function" ? provider : null;
|
|
893
|
+
this.#babylonJSController?.setQuickLookAssetProvider(this.#quickLookAssetProvider);
|
|
894
|
+
}
|
|
895
|
+
|
|
878
896
|
/**
|
|
879
897
|
* Downloads the current 3D model as a USDZ file.
|
|
880
898
|
* @public
|
package/src/pref-viewer.js
CHANGED
|
@@ -43,6 +43,7 @@ import I18n from "./localization/i18n.js";
|
|
|
43
43
|
* - showModel / hideModel / showScene / hideScene: Toggle visibility using the internal task queue for consistent sequencing.
|
|
44
44
|
* - zoomCenter / zoomExtentsAll / zoomIn / zoomOut: Forward zoom commands to the 2D controller when active.
|
|
45
45
|
* - downloadModelGLB / downloadModelGLTF / downloadModelUSDZ: Export the loaded model in the requested format.
|
|
46
|
+
* - setQuickLookAssetProvider(provider): Supplies (or clears) the asset opened by the iOS AR Quick Look action.
|
|
46
47
|
* - downloadSceneGLB / downloadSceneGLTF / downloadSceneUSDZ: Export the loaded environment in the requested format.
|
|
47
48
|
* - downloadModelAndSceneGLB / downloadModelAndSceneGLTF / downloadModelAndSceneUSDZ: Export the combined scene in the requested format.
|
|
48
49
|
* - openDialog / closeDialog: Manage PrefViewerDialog instances rendered inside the shadow tree.
|
|
@@ -115,6 +116,7 @@ export default class PrefViewer extends HTMLElement {
|
|
|
115
116
|
#wrapper = null;
|
|
116
117
|
#component2D = null;
|
|
117
118
|
#component3D = null;
|
|
119
|
+
#quickLookAssetProvider = null; // Host asset provider, reapplied to every 3D component this element creates
|
|
118
120
|
#dialog = null;
|
|
119
121
|
#menu3D = null;
|
|
120
122
|
|
|
@@ -394,6 +396,7 @@ export default class PrefViewer extends HTMLElement {
|
|
|
394
396
|
* Creates and appends the 3D viewer component to the shadow DOM, initially hidden.
|
|
395
397
|
* Forwards renderer selection and show-view-cube, stores its layout handler in #handlers and subscribes before activation
|
|
396
398
|
* so the first measured layout positions the render menu correctly.
|
|
399
|
+
* Hands any retained Quick Look asset provider to the new component.
|
|
397
400
|
* @private
|
|
398
401
|
* @returns {void}
|
|
399
402
|
*/
|
|
@@ -401,6 +404,7 @@ export default class PrefViewer extends HTMLElement {
|
|
|
401
404
|
this.#handlers.on3DElementClick = this.#on3DElementClick.bind(this);
|
|
402
405
|
this.#component3D = document.createElement("pref-viewer-3d");
|
|
403
406
|
this.#component3D.setAttribute("visible", "false");
|
|
407
|
+
this.#component3D.setQuickLookAssetProvider?.(this.#quickLookAssetProvider);
|
|
404
408
|
this.#component3D.setAttribute("view-cube-renderer", this.viewCubeRenderer);
|
|
405
409
|
this.#component3D.setAttribute("show-view-cube", this.getAttribute("show-view-cube") === "false" ? "false" : "true");
|
|
406
410
|
this.#handlers.onViewCubeLayout = (event) => {
|
|
@@ -1289,6 +1293,21 @@ export default class PrefViewer extends HTMLElement {
|
|
|
1289
1293
|
this.#component3D.downloadModelGLTF();
|
|
1290
1294
|
}
|
|
1291
1295
|
|
|
1296
|
+
/**
|
|
1297
|
+
* Sets or clears the host-supplied asset opened by the iOS AR Quick Look action instead of the in-browser USDZ export.
|
|
1298
|
+
* The provider is retained across connection cycles, even when set before the element is connected.
|
|
1299
|
+
* The viewer knows nothing about how the host produces the asset.
|
|
1300
|
+
* @public
|
|
1301
|
+
* @param {?(function(): Promise<{data: (Uint8Array|ArrayBuffer|Blob), mimeType?: string, fileName?: string}>)} provider
|
|
1302
|
+
* Async asset factory (defaults: MIME `model/vnd.usdz+zip`, file name `model.usdz`), or null to restore the built-in export.
|
|
1303
|
+
* A failing provider falls back to the built-in export with a console warning.
|
|
1304
|
+
* @returns {void}
|
|
1305
|
+
*/
|
|
1306
|
+
setQuickLookAssetProvider(provider) {
|
|
1307
|
+
this.#quickLookAssetProvider = typeof provider === "function" ? provider : null;
|
|
1308
|
+
this.#component3D?.setQuickLookAssetProvider?.(this.#quickLookAssetProvider);
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1292
1311
|
/**
|
|
1293
1312
|
* Initiates download of the current 3D model in USDZ format.
|
|
1294
1313
|
* @public
|
package/src/quick-look-action.js
CHANGED
|
@@ -17,6 +17,22 @@ export default class QuickLookAction {
|
|
|
17
17
|
*/
|
|
18
18
|
static QUICK_LOOK_FRAGMENT = "allowsContentScaling=0";
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* MIME type of a USDZ archive; the only type whose bytes are validated as a ZIP before being exposed.
|
|
22
|
+
* @public
|
|
23
|
+
* @static
|
|
24
|
+
* @type {string}
|
|
25
|
+
*/
|
|
26
|
+
static USDZ_MIME_TYPE = "model/vnd.usdz+zip";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* File name given to the anchor's `download` attribute when the prepared asset does not name itself.
|
|
30
|
+
* @public
|
|
31
|
+
* @static
|
|
32
|
+
* @type {string}
|
|
33
|
+
*/
|
|
34
|
+
static DEFAULT_FILE_NAME = "model.usdz";
|
|
35
|
+
|
|
20
36
|
#anchor;
|
|
21
37
|
#style;
|
|
22
38
|
#window;
|
|
@@ -55,7 +71,7 @@ export default class QuickLookAction {
|
|
|
55
71
|
this.#anchor = document.createElement("a");
|
|
56
72
|
this.#anchor.className = "babylonVRicon pref-viewer-quick-look";
|
|
57
73
|
this.#anchor.rel = "ar";
|
|
58
|
-
this.#anchor.download =
|
|
74
|
+
this.#anchor.download = QuickLookAction.DEFAULT_FILE_NAME;
|
|
59
75
|
this.#anchor.setAttribute("role", "button");
|
|
60
76
|
this.#anchor.tabIndex = 0;
|
|
61
77
|
this.#anchor.hidden = true;
|
|
@@ -204,11 +220,38 @@ export default class QuickLookAction {
|
|
|
204
220
|
}
|
|
205
221
|
|
|
206
222
|
/**
|
|
207
|
-
*
|
|
223
|
+
* Normalizes an exporter result into the bytes, MIME type and file name of the asset to expose.
|
|
224
|
+
* A bare `Uint8Array` is a USDZ with default metadata; an object carries its own optional metadata.
|
|
225
|
+
* USDZ content must be a nonempty ZIP-signature archive; other MIME types are passed through as-is.
|
|
226
|
+
* @private
|
|
227
|
+
* @static
|
|
228
|
+
* @param {Uint8Array|{data: Uint8Array, mimeType?: string, fileName?: string}} result - Exporter result.
|
|
229
|
+
* @returns {{data: Uint8Array, mimeType: string, fileName: string}} Validated asset description.
|
|
230
|
+
* @throws {Error} When the result is not byte content or a USDZ lacks the ZIP signature.
|
|
231
|
+
*/
|
|
232
|
+
static #resolveAsset(result) {
|
|
233
|
+
const isDescribed = !!result && typeof result === "object" && !(result instanceof Uint8Array);
|
|
234
|
+
const data = isDescribed ? result.data : result;
|
|
235
|
+
const mimeType = (isDescribed && result.mimeType) || QuickLookAction.USDZ_MIME_TYPE;
|
|
236
|
+
const fileName = (isDescribed && result.fileName) || QuickLookAction.DEFAULT_FILE_NAME;
|
|
237
|
+
if (!(data instanceof Uint8Array) || data.length === 0) {
|
|
238
|
+
throw new Error("PrefViewer: invalid Quick Look export");
|
|
239
|
+
}
|
|
240
|
+
// USDZ is a ZIP archive; never expose empty, JSON or glTF results as native AR.
|
|
241
|
+
if (mimeType === QuickLookAction.USDZ_MIME_TYPE
|
|
242
|
+
&& (data.length < 4 || data[0] !== 0x50 || data[1] !== 0x4b || data[2] !== 3 || data[3] !== 4)) {
|
|
243
|
+
throw new Error("PrefViewer: invalid USDZ export");
|
|
244
|
+
}
|
|
245
|
+
return { data, mimeType, fileName };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Queues current-model export and exposes only a valid asset as a native link: a nonempty ZIP-signature USDZ,
|
|
250
|
+
* or, when the exporter describes another MIME type, that asset unchanged.
|
|
208
251
|
* Obsolete revisions are ignored. Export failures are logged and shown as a retry action;
|
|
209
252
|
* they do not reject the returned work promise or expose an older model's content.
|
|
210
253
|
* @public
|
|
211
|
-
* @param {?(function(): Promise<Uint8Array>)} exporter - Current-model serializer, or null to invalidate without preparation.
|
|
254
|
+
* @param {?(function(): Promise<Uint8Array|{data: Uint8Array, mimeType?: string, fileName?: string}>)} exporter - Current-model serializer returning USDZ bytes or a described asset (defaults: USDZ MIME type, `model.usdz`), or null to invalidate without preparation.
|
|
212
255
|
* @returns {Promise<void>} Resolves after this queued preparation settles or becomes obsolete.
|
|
213
256
|
*/
|
|
214
257
|
update(exporter) {
|
|
@@ -231,17 +274,15 @@ export default class QuickLookAction {
|
|
|
231
274
|
return;
|
|
232
275
|
}
|
|
233
276
|
try {
|
|
234
|
-
const
|
|
277
|
+
const result = await exporter();
|
|
235
278
|
if (this.#disposed || revision !== this.#revision) {
|
|
236
279
|
return;
|
|
237
280
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
242
|
-
const blob = new this.#window.Blob([bytes], {
|
|
243
|
-
type: "model/vnd.usdz+zip"
|
|
281
|
+
const asset = QuickLookAction.#resolveAsset(result);
|
|
282
|
+
const blob = new this.#window.Blob([asset.data], {
|
|
283
|
+
type: asset.mimeType
|
|
244
284
|
});
|
|
285
|
+
this.#anchor.download = asset.fileName;
|
|
245
286
|
this.#url = this.#urlAPI.createObjectURL(blob);
|
|
246
287
|
// Quick Look reads the fragment: content scaling off keeps the authored 1:1 metre scale,
|
|
247
288
|
// matching the Android WebXR path. The bare URL is kept for revocation.
|