lecodes-web-host 2.0.1 → 2.0.3
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/api.d.ts +41 -0
- package/dist/index.js +116 -9
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/api.d.ts
CHANGED
|
@@ -46,12 +46,45 @@ export type PreviewError = {
|
|
|
46
46
|
detail?: string;
|
|
47
47
|
nativeFrames?: string[];
|
|
48
48
|
};
|
|
49
|
+
/** A 3D engine in place of the runtime's: the `_creator` contract implemented in TypeScript
|
|
50
|
+
* (lecodes-gl-lite). The host opens and closes its scene, tells it the size, and disposes of it;
|
|
51
|
+
* the engine shows its own canvas. */
|
|
52
|
+
export type Engine3d = Record<string, any> & {
|
|
53
|
+
openScene(sceneId: number): void;
|
|
54
|
+
closeScene(): void;
|
|
55
|
+
_onResize?(width: number, height: number, factor: number): void;
|
|
56
|
+
_dispose?(): void;
|
|
57
|
+
};
|
|
58
|
+
/** The host's fetched buffers and media players: the ids a bundle hands an engine are these. */
|
|
59
|
+
export type HostStores = {
|
|
60
|
+
fetch(url: string, options: any, onComplete: (systemId: number, statusCode: number) => void, onReject: (err: Error) => void): unknown;
|
|
61
|
+
getBuffer(systemId: number): ArrayBuffer;
|
|
62
|
+
getBlob(systemId: number): Blob;
|
|
63
|
+
putBuffer(buffer: ArrayBuffer): number;
|
|
64
|
+
putLocalBuffer(name: string, buffer: ArrayBuffer): void;
|
|
65
|
+
hasLocalBuffer(name: string): boolean;
|
|
66
|
+
getLocalName(systemId: number): string | undefined;
|
|
67
|
+
getPlayer(id: number): HTMLVideoElement | HTMLAudioElement | null | undefined;
|
|
68
|
+
removePlayers(): void;
|
|
69
|
+
};
|
|
70
|
+
/** What the host lends an engine of the embedder's. */
|
|
71
|
+
export type EnginePage = {
|
|
72
|
+
/** A canvas over the whole container, its backing store in device px, hidden until shown. */
|
|
73
|
+
newCanvas(): HTMLCanvasElement;
|
|
74
|
+
preloads: Set<string>;
|
|
75
|
+
/** The painter behind `_creatorCanvas` (a texture made of a canvas of the app's is read here). */
|
|
76
|
+
canvasHost: any;
|
|
77
|
+
stores: HostStores;
|
|
78
|
+
};
|
|
49
79
|
export type HostOptions = {
|
|
50
80
|
/** The header of the bundle the host is for (`parseBundleHeader`): its engines decide the profile,
|
|
51
81
|
* its materials the archives, its fonts the boot faces. */
|
|
52
82
|
header?: BundleHeader;
|
|
53
83
|
/** The runtime profile, in place of the header's (a host with no bundle yet: an editor). */
|
|
54
84
|
profile?: HostProfile;
|
|
85
|
+
/** A 3D engine of the embedder's as `_creator`, on a profile that carries none (`2d`, `ui`):
|
|
86
|
+
* an editor's viewport on the lite engine. Ignored on the `full` profile. */
|
|
87
|
+
engine3d?: (page: EnginePage) => Promise<Engine3d>;
|
|
55
88
|
/** false = an engine-only host: no UI renderer, no fonts. Default true. */
|
|
56
89
|
ui?: boolean;
|
|
57
90
|
/** The project, as the key its storage is kept under. Default: the header's name. */
|
|
@@ -147,6 +180,10 @@ export type Layer = {
|
|
|
147
180
|
export type HostUI = {
|
|
148
181
|
/** The layers the host paints now. */
|
|
149
182
|
frame(): Layer[];
|
|
183
|
+
/** The node the runtime made last. An inspector that is told of an element as the SDK makes it
|
|
184
|
+
* (`__lecodesCaptureSites` as a function) ties the element to this node: the SDK calls straight
|
|
185
|
+
* after the create that made it. */
|
|
186
|
+
readonly lastCreated: number;
|
|
150
187
|
/** The elements named `name`. */
|
|
151
188
|
findAll(name: string): RenderNode[];
|
|
152
189
|
/** The render tree as JSON / as text (what `lecodes render` writes). */
|
|
@@ -167,6 +204,10 @@ export type Host = {
|
|
|
167
204
|
readonly world: Record<string, unknown>;
|
|
168
205
|
/** The UI as the host paints it; null on an engine-only host. */
|
|
169
206
|
readonly ui: HostUI | null;
|
|
207
|
+
/** The embedder's 3D engine (`engine3d`), as it made it; null where the runtime's own runs. */
|
|
208
|
+
engine3d(): Engine3d | null;
|
|
209
|
+
/** Bytes of the host's store, by their id: what a bundle fetched, what an engine captured. */
|
|
210
|
+
getBuffer(systemId: number): ArrayBuffer | undefined;
|
|
170
211
|
/** Evaluate a bundle. Throws when the bundle is of another SDK major than this host. */
|
|
171
212
|
run(code: string, options?: RunOptions): Run;
|
|
172
213
|
/** A plugin's web half, by the id an app lists it under; a bundle gets the ones its header names. */
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ const assetsURL = function(dep, importerUrl) {
|
|
|
6
6
|
return new URL(dep, importerUrl).href;
|
|
7
7
|
};
|
|
8
8
|
const seen = {};
|
|
9
|
-
const
|
|
9
|
+
const __lecodesHostPreload = function preload(baseModule, deps, importerUrl) {
|
|
10
10
|
let promise = Promise.resolve();
|
|
11
11
|
if (deps && deps.length > 0) {
|
|
12
12
|
let allSettled2 = function(promises) {
|
|
@@ -84,9 +84,9 @@ const __vitePreload = function preload(baseModule, deps, importerUrl) {
|
|
|
84
84
|
});
|
|
85
85
|
};
|
|
86
86
|
const factories = {
|
|
87
|
-
full: () =>
|
|
88
|
-
"2d": () =>
|
|
89
|
-
ui: () =>
|
|
87
|
+
full: () => __lecodesHostPreload(() => import("./chunks/creator-full-BYRA3WbP.js"), true ? [] : void 0, import.meta.url),
|
|
88
|
+
"2d": () => __lecodesHostPreload(() => import("./chunks/creator-2d-D13oxDsX.js"), true ? [] : void 0, import.meta.url),
|
|
89
|
+
ui: () => __lecodesHostPreload(() => import("./chunks/creator-ui-Cvoc0NZK.js"), true ? [] : void 0, import.meta.url)
|
|
90
90
|
};
|
|
91
91
|
const wasmUrl = {
|
|
92
92
|
full: () => new URL("" + new URL("assets/creator-full-D2dSDdDj.wasm", import.meta.url).href, import.meta.url).href,
|
|
@@ -767,6 +767,67 @@ const installWasmCrashSymbolizer = () => {
|
|
|
767
767
|
};
|
|
768
768
|
const arrayBuffers = [null];
|
|
769
769
|
new TextDecoder("utf-8");
|
|
770
|
+
const _fetch = async (url, fetchOptions, onComplete, onReject) => {
|
|
771
|
+
let body = void 0;
|
|
772
|
+
if (fetchOptions.body) {
|
|
773
|
+
if (typeof fetchOptions.body === "object" && Array.isArray(fetchOptions.body._entries)) {
|
|
774
|
+
const formData = new FormData();
|
|
775
|
+
for (let [key, value, filename] of fetchOptions.body._entries) {
|
|
776
|
+
if (typeof value === "object") {
|
|
777
|
+
formData.append(key, new Blob([getBuffer(value._id)]), filename);
|
|
778
|
+
} else {
|
|
779
|
+
formData.append(key, value);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
body = formData;
|
|
783
|
+
} else if (typeof fetchOptions.body === "string") {
|
|
784
|
+
body = fetchOptions.body;
|
|
785
|
+
} else {
|
|
786
|
+
onReject(new Error("Invalid body"));
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
const resp = await fetch(url, {
|
|
790
|
+
method: fetchOptions.method,
|
|
791
|
+
body,
|
|
792
|
+
headers: fetchOptions.headers,
|
|
793
|
+
mode: "cors",
|
|
794
|
+
credentials: "omit"
|
|
795
|
+
});
|
|
796
|
+
const onProgress = typeof fetchOptions.onProgress === "function" ? fetchOptions.onProgress : null;
|
|
797
|
+
if (!onProgress) {
|
|
798
|
+
const buffer = await resp.arrayBuffer();
|
|
799
|
+
arrayBuffers.push(buffer);
|
|
800
|
+
onComplete(arrayBuffers.length - 1, resp.status);
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
const reader = resp.body?.getReader();
|
|
804
|
+
if (!reader) {
|
|
805
|
+
const buffer = await resp.arrayBuffer();
|
|
806
|
+
arrayBuffers.push(buffer);
|
|
807
|
+
onComplete(arrayBuffers.length - 1, resp.status);
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
const contentLength = Number(resp.headers.get("content-length")) || void 0;
|
|
811
|
+
let loaded = 0;
|
|
812
|
+
const chunks = [];
|
|
813
|
+
while (true) {
|
|
814
|
+
const { done, value } = await reader.read();
|
|
815
|
+
if (done) break;
|
|
816
|
+
if (value) {
|
|
817
|
+
chunks.push(value);
|
|
818
|
+
loaded += value.length;
|
|
819
|
+
onProgress({ loaded, total: contentLength });
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
const result = new Uint8Array(loaded);
|
|
823
|
+
let offset = 0;
|
|
824
|
+
for (const chunk of chunks) {
|
|
825
|
+
result.set(chunk, offset);
|
|
826
|
+
offset += chunk.length;
|
|
827
|
+
}
|
|
828
|
+
arrayBuffers.push(result.buffer);
|
|
829
|
+
onComplete(arrayBuffers.length - 1, resp.status);
|
|
830
|
+
};
|
|
770
831
|
const getUrl = (systemId) => {
|
|
771
832
|
const blob = new Blob([arrayBuffers[systemId]]);
|
|
772
833
|
return URL.createObjectURL(blob);
|
|
@@ -785,7 +846,17 @@ const putBuffer = (buffer) => {
|
|
|
785
846
|
return arrayBuffers.length - 1;
|
|
786
847
|
};
|
|
787
848
|
const map = /* @__PURE__ */ new Map();
|
|
849
|
+
const putLocalBuffer = (str, buf) => {
|
|
850
|
+
map.set(str, buf);
|
|
851
|
+
const systemId = localResources.get(str);
|
|
852
|
+
if (systemId !== void 0) arrayBuffers[systemId] = buf;
|
|
853
|
+
};
|
|
854
|
+
const hasLocalBuffer = (str) => map.has(str);
|
|
788
855
|
const localResources = /* @__PURE__ */ new Map();
|
|
856
|
+
const getLocalName = (systemId) => {
|
|
857
|
+
for (const [name, id] of localResources) if (id === systemId) return name;
|
|
858
|
+
return void 0;
|
|
859
|
+
};
|
|
789
860
|
const _fetchLocal = (path) => {
|
|
790
861
|
const existsSystemId = localResources.get(path);
|
|
791
862
|
if (existsSystemId !== void 0) {
|
|
@@ -1280,11 +1351,24 @@ class Canvas2DPainter {
|
|
|
1280
1351
|
}
|
|
1281
1352
|
drawImage(image, srcRect, dst, opts) {
|
|
1282
1353
|
if (!image.handle) return;
|
|
1283
|
-
const
|
|
1354
|
+
const fit = srcRect ? { src: srcRect, dst } : computeObjectFit(image.naturalWidth, image.naturalHeight, dst, opts.fit);
|
|
1355
|
+
const d = fit.dst;
|
|
1356
|
+
let src = fit.src;
|
|
1284
1357
|
const dw = Math.max(1, Math.ceil(d.width * this.dpr));
|
|
1285
1358
|
const dh = Math.max(1, Math.ceil(d.height * this.dpr));
|
|
1286
|
-
const handle = image.rasterize ? image.rasterize(dw, dh) : image.handle;
|
|
1287
1359
|
const whole = !srcRect && opts.fit !== "cover";
|
|
1360
|
+
let handle = image.handle;
|
|
1361
|
+
if (image.rasterize) {
|
|
1362
|
+
if (whole) handle = image.rasterize(dw, dh);
|
|
1363
|
+
else {
|
|
1364
|
+
const k = Math.max(dw / src.width, dh / src.height);
|
|
1365
|
+
const rw = Math.max(1, Math.ceil(image.naturalWidth * k));
|
|
1366
|
+
const rh = Math.max(1, Math.ceil(image.naturalHeight * k));
|
|
1367
|
+
handle = image.rasterize(rw, rh);
|
|
1368
|
+
const kx = rw / image.naturalWidth, ky = rh / image.naturalHeight;
|
|
1369
|
+
src = { x: src.x * kx, y: src.y * ky, width: src.width * kx, height: src.height * ky };
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1288
1372
|
if (opts.tint === void 0) {
|
|
1289
1373
|
if (whole) this.ctx.drawImage(handle, d.x, d.y, d.width, d.height);
|
|
1290
1374
|
else this.ctx.drawImage(handle, src.x, src.y, src.width, src.height, d.x, d.y, d.width, d.height);
|
|
@@ -3871,7 +3955,7 @@ const loadBootFonts = async (fonts, doc, budgetMs = 2e3) => {
|
|
|
3871
3955
|
}
|
|
3872
3956
|
await Promise.race([Promise.all(loads), new Promise((res) => setTimeout(res, budgetMs))]);
|
|
3873
3957
|
};
|
|
3874
|
-
const SDK_VERSION = "2.0.
|
|
3958
|
+
const SDK_VERSION = "2.0.3";
|
|
3875
3959
|
const sdkMajor = (version) => {
|
|
3876
3960
|
const m = version?.match(/^(\d+)\.\d+\.\d+/);
|
|
3877
3961
|
return m ? Number(m[1]) : null;
|
|
@@ -4963,6 +5047,7 @@ const makeWorld = () => {
|
|
|
4963
5047
|
};
|
|
4964
5048
|
return { global, evaluate };
|
|
4965
5049
|
};
|
|
5050
|
+
const stores = { fetch: _fetch, getBuffer, getBlob, putBuffer, putLocalBuffer, hasLocalBuffer, getLocalName, getPlayer, removePlayers };
|
|
4966
5051
|
const nullHostUI = (onDestination, onDropped) => ({
|
|
4967
5052
|
nodeFactory() {
|
|
4968
5053
|
},
|
|
@@ -5044,15 +5129,27 @@ const createHost$1 = async (root, options = {}) => {
|
|
|
5044
5129
|
M._free(p);
|
|
5045
5130
|
}
|
|
5046
5131
|
};
|
|
5132
|
+
let engine3d = null;
|
|
5133
|
+
let engine3dScene = -1;
|
|
5047
5134
|
let destination = -1;
|
|
5048
|
-
const onDestination = (kind) => {
|
|
5135
|
+
const onDestination = (kind, id) => {
|
|
5049
5136
|
destination = kind;
|
|
5050
5137
|
if (kind === TREE_DEST_SCENE3D) gl?.show();
|
|
5051
5138
|
if (kind === TREE_DEST_SCENE2D) scene2d?.show();
|
|
5139
|
+
if (engine3d && kind === TREE_DEST_SCENE3D && id !== engine3dScene) {
|
|
5140
|
+
engine3dScene = id;
|
|
5141
|
+
engine3d.openScene(id);
|
|
5142
|
+
}
|
|
5052
5143
|
};
|
|
5053
5144
|
const onDropped = (kind) => {
|
|
5054
5145
|
if (kind === destination) return;
|
|
5055
|
-
if (kind === TREE_DEST_SCENE3D)
|
|
5146
|
+
if (kind === TREE_DEST_SCENE3D) {
|
|
5147
|
+
gl?.hide();
|
|
5148
|
+
if (engine3d && engine3dScene >= 0) {
|
|
5149
|
+
engine3dScene = -1;
|
|
5150
|
+
engine3d.closeScene();
|
|
5151
|
+
}
|
|
5152
|
+
}
|
|
5056
5153
|
if (kind === TREE_DEST_SCENE2D) scene2d?.hide();
|
|
5057
5154
|
};
|
|
5058
5155
|
const canvasHost = createCanvasHost();
|
|
@@ -5110,12 +5207,17 @@ const createHost$1 = async (root, options = {}) => {
|
|
|
5110
5207
|
M._creatorWebInit();
|
|
5111
5208
|
if (options.launchUrl) withStr(options.launchUrl, (p) => M._creatorWebSetLaunchUrl(p));
|
|
5112
5209
|
renderer?.ui.install();
|
|
5210
|
+
if (!has3d && options.engine3d) {
|
|
5211
|
+
engine3d = await options.engine3d({ newCanvas: page.newCanvas, preloads, canvasHost, stores });
|
|
5212
|
+
Object.defineProperty(globals, "_creator", { value: engine3d, configurable: true, writable: true, enumerable: false });
|
|
5213
|
+
}
|
|
5113
5214
|
if (has3d && header?.gl !== "defer") globals._creator?.createEngine();
|
|
5114
5215
|
const safeArea = createSafeArea(root);
|
|
5115
5216
|
if (options.safeArea) safeArea.set(options.safeArea);
|
|
5116
5217
|
const resize = (size) => {
|
|
5117
5218
|
gl?.resize();
|
|
5118
5219
|
scene2d?.resize();
|
|
5220
|
+
engine3d?._onResize?.(size.width, size.height, size.factor);
|
|
5119
5221
|
if (renderer) {
|
|
5120
5222
|
renderer.resize(size.width, size.height, size.factor);
|
|
5121
5223
|
renderer.setSafeArea(safeArea.current());
|
|
@@ -5146,6 +5248,10 @@ const createHost$1 = async (root, options = {}) => {
|
|
|
5146
5248
|
world: globals,
|
|
5147
5249
|
/** The UI as the renderer holds it (the render tree, the pointer routing); null on an engine-only host. */
|
|
5148
5250
|
ui: renderer?.ui ?? null,
|
|
5251
|
+
/** The embedder's 3D engine (`engine3d`), as it made it; null where the runtime's own runs. */
|
|
5252
|
+
engine3d: () => engine3d,
|
|
5253
|
+
/** Bytes of the host's store, by their id: what a bundle fetched, what an engine captured. */
|
|
5254
|
+
getBuffer: (systemId) => getBuffer(systemId),
|
|
5149
5255
|
/** Evaluate a bundle. Throws when the bundle is of another SDK major than this host. */
|
|
5150
5256
|
run(code, runOptions = {}) {
|
|
5151
5257
|
const bundle = runOptions.header ?? parseBundleHeader$1(code);
|
|
@@ -5184,6 +5290,7 @@ const createHost$1 = async (root, options = {}) => {
|
|
|
5184
5290
|
app.dispose();
|
|
5185
5291
|
removePlayers();
|
|
5186
5292
|
safeArea.destroy();
|
|
5293
|
+
engine3d?._dispose?.();
|
|
5187
5294
|
gl?.destroy();
|
|
5188
5295
|
scene2d?.destroy();
|
|
5189
5296
|
page.destroy();
|