lecodes-web-host 2.0.1 → 2.0.2

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 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 __vitePreload = function preload(baseModule, deps, importerUrl) {
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: () => __vitePreload(() => import("./chunks/creator-full-BYRA3WbP.js"), true ? [] : void 0, import.meta.url),
88
- "2d": () => __vitePreload(() => import("./chunks/creator-2d-D13oxDsX.js"), true ? [] : void 0, import.meta.url),
89
- ui: () => __vitePreload(() => import("./chunks/creator-ui-Cvoc0NZK.js"), true ? [] : void 0, import.meta.url)
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) {
@@ -3871,7 +3942,7 @@ const loadBootFonts = async (fonts, doc, budgetMs = 2e3) => {
3871
3942
  }
3872
3943
  await Promise.race([Promise.all(loads), new Promise((res) => setTimeout(res, budgetMs))]);
3873
3944
  };
3874
- const SDK_VERSION = "2.0.1";
3945
+ const SDK_VERSION = "2.0.2";
3875
3946
  const sdkMajor = (version) => {
3876
3947
  const m = version?.match(/^(\d+)\.\d+\.\d+/);
3877
3948
  return m ? Number(m[1]) : null;
@@ -4963,6 +5034,7 @@ const makeWorld = () => {
4963
5034
  };
4964
5035
  return { global, evaluate };
4965
5036
  };
5037
+ const stores = { fetch: _fetch, getBuffer, getBlob, putBuffer, putLocalBuffer, hasLocalBuffer, getLocalName, getPlayer, removePlayers };
4966
5038
  const nullHostUI = (onDestination, onDropped) => ({
4967
5039
  nodeFactory() {
4968
5040
  },
@@ -5044,15 +5116,27 @@ const createHost$1 = async (root, options = {}) => {
5044
5116
  M._free(p);
5045
5117
  }
5046
5118
  };
5119
+ let engine3d = null;
5120
+ let engine3dScene = -1;
5047
5121
  let destination = -1;
5048
- const onDestination = (kind) => {
5122
+ const onDestination = (kind, id) => {
5049
5123
  destination = kind;
5050
5124
  if (kind === TREE_DEST_SCENE3D) gl?.show();
5051
5125
  if (kind === TREE_DEST_SCENE2D) scene2d?.show();
5126
+ if (engine3d && kind === TREE_DEST_SCENE3D && id !== engine3dScene) {
5127
+ engine3dScene = id;
5128
+ engine3d.openScene(id);
5129
+ }
5052
5130
  };
5053
5131
  const onDropped = (kind) => {
5054
5132
  if (kind === destination) return;
5055
- if (kind === TREE_DEST_SCENE3D) gl?.hide();
5133
+ if (kind === TREE_DEST_SCENE3D) {
5134
+ gl?.hide();
5135
+ if (engine3d && engine3dScene >= 0) {
5136
+ engine3dScene = -1;
5137
+ engine3d.closeScene();
5138
+ }
5139
+ }
5056
5140
  if (kind === TREE_DEST_SCENE2D) scene2d?.hide();
5057
5141
  };
5058
5142
  const canvasHost = createCanvasHost();
@@ -5110,12 +5194,17 @@ const createHost$1 = async (root, options = {}) => {
5110
5194
  M._creatorWebInit();
5111
5195
  if (options.launchUrl) withStr(options.launchUrl, (p) => M._creatorWebSetLaunchUrl(p));
5112
5196
  renderer?.ui.install();
5197
+ if (!has3d && options.engine3d) {
5198
+ engine3d = await options.engine3d({ newCanvas: page.newCanvas, preloads, canvasHost, stores });
5199
+ Object.defineProperty(globals, "_creator", { value: engine3d, configurable: true, writable: true, enumerable: false });
5200
+ }
5113
5201
  if (has3d && header?.gl !== "defer") globals._creator?.createEngine();
5114
5202
  const safeArea = createSafeArea(root);
5115
5203
  if (options.safeArea) safeArea.set(options.safeArea);
5116
5204
  const resize = (size) => {
5117
5205
  gl?.resize();
5118
5206
  scene2d?.resize();
5207
+ engine3d?._onResize?.(size.width, size.height, size.factor);
5119
5208
  if (renderer) {
5120
5209
  renderer.resize(size.width, size.height, size.factor);
5121
5210
  renderer.setSafeArea(safeArea.current());
@@ -5146,6 +5235,10 @@ const createHost$1 = async (root, options = {}) => {
5146
5235
  world: globals,
5147
5236
  /** The UI as the renderer holds it (the render tree, the pointer routing); null on an engine-only host. */
5148
5237
  ui: renderer?.ui ?? null,
5238
+ /** The embedder's 3D engine (`engine3d`), as it made it; null where the runtime's own runs. */
5239
+ engine3d: () => engine3d,
5240
+ /** Bytes of the host's store, by their id: what a bundle fetched, what an engine captured. */
5241
+ getBuffer: (systemId) => getBuffer(systemId),
5149
5242
  /** Evaluate a bundle. Throws when the bundle is of another SDK major than this host. */
5150
5243
  run(code, runOptions = {}) {
5151
5244
  const bundle = runOptions.header ?? parseBundleHeader$1(code);
@@ -5184,6 +5277,7 @@ const createHost$1 = async (root, options = {}) => {
5184
5277
  app.dispose();
5185
5278
  removePlayers();
5186
5279
  safeArea.destroy();
5280
+ engine3d?._dispose?.();
5187
5281
  gl?.destroy();
5188
5282
  scene2d?.destroy();
5189
5283
  page.destroy();