incanto 0.28.0 → 0.30.0
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 +44 -0
- package/bin/incanto-editor.mjs +219 -29
- package/dist/2d.d.ts +54 -6
- package/dist/2d.js +3 -3
- package/dist/3d.d.ts +353 -16
- package/dist/3d.js +5 -5
- package/dist/{behavior-CPibUfnH.d.ts → behavior-BAc0erXF.d.ts} +21 -0
- package/dist/{create-game-71TJjW1T.js → create-game-5z_QVtLx.js} +59 -16
- package/dist/{create-game-BRgWpNsa.js → create-game-DuBTv2zI.js} +276 -49
- package/dist/debug-draw-BM3DsvtT.js +18 -0
- package/dist/debug.d.ts +51 -6
- package/dist/debug.js +242 -19
- package/dist/editor-switch-B0wB_DSr.d.ts +59 -0
- package/dist/editor.d.ts +49 -0
- package/dist/editor.js +8554 -0
- package/dist/{gameplay-DEG-TP7D.js → gameplay-Cfr6aFZ1.js} +219 -40
- package/dist/gameplay.d.ts +1 -1
- package/dist/gameplay.js +1 -1
- package/dist/index.d.ts +31 -3
- package/dist/index.js +2 -2
- package/dist/{loader-DILt9PGC.d.ts → loader-B242FF6N.d.ts} +1 -1
- package/dist/net.d.ts +1 -1
- package/dist/net.js +2 -2
- package/dist/{pathfinding-RWYkNKx9.d.ts → pathfinding-BwD974Ss.d.ts} +1 -1
- package/dist/{physics-2d-DiVFFlH3.js → physics-2d-3kOQCtgd.js} +88 -3
- package/dist/{physics-3d--y5clE2j.js → physics-3d-CeRH-Ff_.js} +475 -9
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{register-nObreUQR.js → register-BTg0EM7s.js} +35 -3
- package/dist/{register-6R75AC7-.js → register-DJ0SByQg.js} +5550 -5073
- package/dist/{register-CvpSUU3O.js → register-DWcWq4QG.js} +22 -2
- package/dist/{register-BFFE1Mh1.js → register-MelqEdza.js} +1 -1
- package/dist/teardown-ByzfDPyu.js +240 -0
- package/dist/test.d.ts +35 -3
- package/dist/test.js +104 -10
- package/dist/vite.d.ts +139 -1
- package/dist/vite.js +355 -4
- package/editor/assets/{agent8-CojUfCXN.js → agent8-CAp0i5qn.js} +1 -1
- package/editor/assets/debug-BoEYfbqK.js +2 -0
- package/editor/assets/index-BO6WU8by.js +10696 -0
- package/editor/index.html +3 -157
- package/package.json +3 -2
- package/schemas/scene.schema.json +102 -4
- package/skills/incanto-3d-models.md +38 -0
- package/skills/incanto-assets.md +13 -0
- package/skills/incanto-building-3d-games.md +83 -6
- package/skills/incanto-editor.md +212 -8
- package/skills/incanto-environment.md +65 -1
- package/skills/incanto-node-reference.md +33 -1
- package/skills/incanto-physics-and-input.md +60 -6
- package/skills/incanto-verifying-your-game.md +57 -2
- package/templates-app/beacon-isle-3d/docs/project-3d-rules.md +5 -0
- package/templates-app/beacon-isle-3d/package.json +1 -1
- package/templates-app/beacon-isle-3d/vite.config.ts +7 -0
- package/templates-app/tps-3d/docs/project-3d-rules.md +5 -0
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/tps-3d/vite.config.ts +7 -0
- package/templates-app/village-quest-3d/docs/project-3d-rules.md +5 -0
- package/templates-app/village-quest-3d/package.json +1 -1
- package/templates-app/village-quest-3d/vite.config.ts +7 -0
- package/dist/debug-draw-CZmOYjL2.js +0 -13
- package/editor/assets/index-D6RQgROR.js +0 -8330
- package/editor/assets/index-D8QvwvOm.css +0 -1
package/dist/vite.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { validateScene } from "./test.js";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
|
-
import { dirname, resolve } from "node:path";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { basename, dirname, join, normalize, relative, resolve, sep } from "node:path";
|
|
4
4
|
//#region src/vite/index.ts
|
|
5
5
|
/**
|
|
6
6
|
* incanto/vite — dev-server integration: validate every `*.scene.json` edit
|
|
@@ -13,12 +13,31 @@ import { dirname, resolve } from "node:path";
|
|
|
13
13
|
* export default defineConfig({ plugins: [incantoScenes()] });
|
|
14
14
|
* ```
|
|
15
15
|
*
|
|
16
|
-
*
|
|
16
|
+
* `incantoLibrary()` is the second half: it puts the agent8 asset catalog on
|
|
17
|
+
* your own dev server, so the editor opened from a running game
|
|
18
|
+
* (`createGame3D({ editor: { library: true } })`) has the same 📚 the
|
|
19
|
+
* standalone `incanto-editor` has.
|
|
20
|
+
*
|
|
21
|
+
* Typed structurally (no vite dependency) — the returned objects satisfy
|
|
17
22
|
* vite's Plugin interface.
|
|
18
23
|
*/
|
|
19
24
|
function incantoScenes(opts = {}) {
|
|
20
25
|
return {
|
|
21
26
|
name: "incanto-scenes",
|
|
27
|
+
/**
|
|
28
|
+
* The project's scenes, over HTTP, so the editor opened from a running
|
|
29
|
+
* game can browse and load them the same way the standalone
|
|
30
|
+
* `incanto-editor` does. Dev-only by construction (`configureServer`).
|
|
31
|
+
*/
|
|
32
|
+
configureServer(server) {
|
|
33
|
+
const root = opts.root ?? server.config?.root ?? process.cwd();
|
|
34
|
+
server.middlewares.use("/api/scenes", (req, res) => {
|
|
35
|
+
serveSceneList(req, res, root);
|
|
36
|
+
});
|
|
37
|
+
server.middlewares.use("/api/scene", (req, res) => {
|
|
38
|
+
serveSceneFile(req, res, root);
|
|
39
|
+
});
|
|
40
|
+
},
|
|
22
41
|
async handleHotUpdate(ctx) {
|
|
23
42
|
if (!ctx.file.endsWith(".scene.json")) return void 0;
|
|
24
43
|
let json;
|
|
@@ -53,5 +72,337 @@ function report(ctx, message) {
|
|
|
53
72
|
}
|
|
54
73
|
});
|
|
55
74
|
}
|
|
75
|
+
const SKIP_DIRS = new Set([
|
|
76
|
+
"node_modules",
|
|
77
|
+
"dist",
|
|
78
|
+
"build",
|
|
79
|
+
"out",
|
|
80
|
+
".git",
|
|
81
|
+
"coverage",
|
|
82
|
+
".vite",
|
|
83
|
+
".next"
|
|
84
|
+
]);
|
|
85
|
+
/**
|
|
86
|
+
* Is this JSON an incanto scene? The file itself answers.
|
|
87
|
+
*
|
|
88
|
+
* A scene declares what it is — `{"format": 1, "type": "scene", "name", "root"}`
|
|
89
|
+
* — and those are exactly the four keys the loader demands before it will build
|
|
90
|
+
* anything (`validateHeader`). So "is this a scene" is not a guess from the file
|
|
91
|
+
* name; it is a fact readable from the first object in the file. That is what
|
|
92
|
+
* lets the browser mark a row ✓ instead of hoping.
|
|
93
|
+
*/
|
|
94
|
+
function sceneFacts(json) {
|
|
95
|
+
if (json === null || typeof json !== "object" || Array.isArray(json)) return {
|
|
96
|
+
ok: false,
|
|
97
|
+
why: "the file is JSON, but not a JSON object"
|
|
98
|
+
};
|
|
99
|
+
const j = json;
|
|
100
|
+
if (j.type !== "scene") return {
|
|
101
|
+
ok: false,
|
|
102
|
+
why: `"type" must be "scene"${j.type === void 0 ? ", and this file has no \"type\"" : `, not ${JSON.stringify(j.type)}`}`
|
|
103
|
+
};
|
|
104
|
+
if (j.format !== 1) return {
|
|
105
|
+
ok: false,
|
|
106
|
+
why: `"format" must be 1, not ${JSON.stringify(j.format)}`
|
|
107
|
+
};
|
|
108
|
+
if (typeof j.root !== "object" || j.root === null) return {
|
|
109
|
+
ok: false,
|
|
110
|
+
why: "\"root\" must be a node object"
|
|
111
|
+
};
|
|
112
|
+
return {
|
|
113
|
+
ok: true,
|
|
114
|
+
name: typeof j.name === "string" ? j.name : "",
|
|
115
|
+
...j.dimension === "2d" || j.dimension === "3d" ? { dimension: j.dimension } : {},
|
|
116
|
+
nodes: countNodes(j.root)
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function countNodes(node) {
|
|
120
|
+
const children = node.children;
|
|
121
|
+
if (!Array.isArray(children)) return 1;
|
|
122
|
+
let n = 1;
|
|
123
|
+
for (const child of children) n += countNodes(child);
|
|
124
|
+
return n;
|
|
125
|
+
}
|
|
126
|
+
/** Reading a 300 MB "json" to label a row is not worth it — say so instead. */
|
|
127
|
+
const SNIFF_LIMIT = 16e6;
|
|
128
|
+
function inspect(abs, size) {
|
|
129
|
+
if (size > SNIFF_LIMIT) return { notScene: "too large to check without opening it" };
|
|
130
|
+
let parsed;
|
|
131
|
+
try {
|
|
132
|
+
parsed = JSON.parse(readFileSync(abs, "utf-8"));
|
|
133
|
+
} catch (error) {
|
|
134
|
+
return { notScene: `not valid JSON: ${message(error)}` };
|
|
135
|
+
}
|
|
136
|
+
const facts = sceneFacts(parsed);
|
|
137
|
+
return facts.ok ? { scene: {
|
|
138
|
+
name: facts.name,
|
|
139
|
+
...facts.dimension ? { dimension: facts.dimension } : {},
|
|
140
|
+
nodes: facts.nodes
|
|
141
|
+
} } : { notScene: facts.why };
|
|
142
|
+
}
|
|
143
|
+
/** @internal Exported for tests. Root-bounded walk, deepest 6 levels. */
|
|
144
|
+
function discoverScenes(root) {
|
|
145
|
+
const found = [];
|
|
146
|
+
const walk = (dir, depth) => {
|
|
147
|
+
if (depth > 6) return;
|
|
148
|
+
let entries;
|
|
149
|
+
try {
|
|
150
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
151
|
+
} catch {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
for (const entry of entries) if (entry.isDirectory()) {
|
|
155
|
+
if (!SKIP_DIRS.has(entry.name) && !entry.name.startsWith(".")) walk(join(dir, entry.name), depth + 1);
|
|
156
|
+
} else if (entry.name.endsWith(".json")) {
|
|
157
|
+
const abs = join(dir, entry.name);
|
|
158
|
+
let size = 0;
|
|
159
|
+
let modified = 0;
|
|
160
|
+
try {
|
|
161
|
+
const stat = statSync(abs);
|
|
162
|
+
size = stat.size;
|
|
163
|
+
modified = stat.mtimeMs;
|
|
164
|
+
} catch {}
|
|
165
|
+
found.push({
|
|
166
|
+
rel: relative(root, abs).split(sep).join("/"),
|
|
167
|
+
abs,
|
|
168
|
+
size,
|
|
169
|
+
modified,
|
|
170
|
+
...inspect(abs, size)
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
walk(root, 0);
|
|
175
|
+
return found.sort((a, b) => a.rel.localeCompare(b.rel));
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* @internal The one place a request's path becomes a file path.
|
|
179
|
+
*
|
|
180
|
+
* `.json` under the project, nothing else — the file browser offers every JSON
|
|
181
|
+
* and the loader decides, but a dev server still must not be a way to read
|
|
182
|
+
* `.env` or somebody's key file. Content is gated separately: a JSON that is
|
|
183
|
+
* not a scene is never sent to the page, only the reason it is not one.
|
|
184
|
+
*/
|
|
185
|
+
function resolveSceneFile(root, rel) {
|
|
186
|
+
if (typeof rel !== "string" || !rel.endsWith(".json")) return null;
|
|
187
|
+
const abs = normalize(resolve(root, rel));
|
|
188
|
+
return abs.startsWith(root.endsWith(sep) ? root : root + sep) ? abs : null;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Same-origin assertion for state-changing requests.
|
|
192
|
+
*
|
|
193
|
+
* A dev server is reachable from any page the developer happens to have open:
|
|
194
|
+
* `fetch('http://localhost:5173/api/scenes', {method:'POST', ...})` from
|
|
195
|
+
* evil.example needs no CORS permission to be SENT — only to be read — so
|
|
196
|
+
* without this a visited page can write `*.scene.json` files into the repo.
|
|
197
|
+
* Browsers attach `Origin` to every POST/PUT (including cross-site form posts,
|
|
198
|
+
* the one path that used to omit it), so requiring it to match the host we were
|
|
199
|
+
* addressed as costs a real editor nothing. A request with no `Origin` at all is
|
|
200
|
+
* not a browser, and a non-browser client has no CSRF exposure to exploit —
|
|
201
|
+
* same rule the `incanto-editor` server uses.
|
|
202
|
+
*/
|
|
203
|
+
function originAllowed(req) {
|
|
204
|
+
const raw = req.headers.origin;
|
|
205
|
+
const origin = Array.isArray(raw) ? raw[0] : raw;
|
|
206
|
+
if (!origin) return true;
|
|
207
|
+
const rawHost = req.headers.host;
|
|
208
|
+
try {
|
|
209
|
+
return new URL(origin).host === (Array.isArray(rawHost) ? rawHost[0] : rawHost ?? "");
|
|
210
|
+
} catch {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function json(res, status, body) {
|
|
215
|
+
res.statusCode = status;
|
|
216
|
+
res.setHeader("content-type", "application/json; charset=utf-8");
|
|
217
|
+
res.end(typeof body === "string" ? body : JSON.stringify(body));
|
|
218
|
+
}
|
|
219
|
+
function readBody(req) {
|
|
220
|
+
return new Promise((done, fail) => {
|
|
221
|
+
let raw = "";
|
|
222
|
+
req.on("data", (chunk) => {
|
|
223
|
+
raw += String(chunk);
|
|
224
|
+
if (raw.length > 64e6) fail(/* @__PURE__ */ new Error("scene too large"));
|
|
225
|
+
});
|
|
226
|
+
req.on("end", () => done(raw));
|
|
227
|
+
req.on("error", () => fail(/* @__PURE__ */ new Error("request failed")));
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
/** @internal Exported for tests — `GET`/`POST /api/scenes`. */
|
|
231
|
+
async function serveSceneList(req, res, root) {
|
|
232
|
+
const base = normalize(resolve(root));
|
|
233
|
+
if (req.method === void 0 || req.method === "GET") return json(res, 200, discoverScenes(base));
|
|
234
|
+
if (req.method !== "POST") return json(res, 405, { error: "method not allowed" });
|
|
235
|
+
if (!originAllowed(req)) return json(res, 403, { error: "cross-origin write refused" });
|
|
236
|
+
let body;
|
|
237
|
+
try {
|
|
238
|
+
body = JSON.parse(await readBody(req));
|
|
239
|
+
} catch (error) {
|
|
240
|
+
return json(res, 400, { error: `bad request body: ${message(error)}` });
|
|
241
|
+
}
|
|
242
|
+
const wanted = typeof body.path === "string" && body.path && !body.path.endsWith(".json") ? `${body.path}.scene.json` : body.path;
|
|
243
|
+
const abs = resolveSceneFile(base, wanted);
|
|
244
|
+
if (!abs) return json(res, 400, { error: "path must be a .json file inside the project" });
|
|
245
|
+
if (existsSync(abs)) return json(res, 409, { error: `already exists: ${String(wanted)}` });
|
|
246
|
+
const name = basename(abs).replace(/\.scene\.json$|\.json$/, "") || "Scene";
|
|
247
|
+
try {
|
|
248
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
249
|
+
writeFileSync(abs, `${JSON.stringify({
|
|
250
|
+
format: 1,
|
|
251
|
+
type: "scene",
|
|
252
|
+
dimension: "3d",
|
|
253
|
+
name,
|
|
254
|
+
root: {
|
|
255
|
+
name,
|
|
256
|
+
type: "Node3D"
|
|
257
|
+
}
|
|
258
|
+
}, null, 2)}\n`);
|
|
259
|
+
} catch (error) {
|
|
260
|
+
return json(res, 400, { error: message(error) });
|
|
261
|
+
}
|
|
262
|
+
return json(res, 201, {
|
|
263
|
+
rel: relative(base, abs).split(sep).join("/"),
|
|
264
|
+
abs,
|
|
265
|
+
size: 0,
|
|
266
|
+
modified: 0
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
/** @internal Exported for tests — `GET`/`PUT /api/scene?file=`. */
|
|
270
|
+
async function serveSceneFile(req, res, root) {
|
|
271
|
+
const base = normalize(resolve(root));
|
|
272
|
+
const file = new URL(req.url ?? "/", "http://localhost").searchParams.get("file");
|
|
273
|
+
const abs = resolveSceneFile(base, file);
|
|
274
|
+
if (!abs) return json(res, 400, { error: "?file= must be a .json file inside the project" });
|
|
275
|
+
if (req.method === void 0 || req.method === "GET") {
|
|
276
|
+
let text;
|
|
277
|
+
let parsed;
|
|
278
|
+
try {
|
|
279
|
+
text = readFileSync(abs, "utf-8");
|
|
280
|
+
parsed = JSON.parse(text);
|
|
281
|
+
} catch (error) {
|
|
282
|
+
return json(res, 422, { error: `${file}: ${message(error)}` });
|
|
283
|
+
}
|
|
284
|
+
const facts = sceneFacts(parsed);
|
|
285
|
+
if (!facts.ok) return json(res, 422, {
|
|
286
|
+
error: `${file} is not an incanto scene — ${facts.why}.`,
|
|
287
|
+
notScene: true
|
|
288
|
+
});
|
|
289
|
+
return json(res, 200, text);
|
|
290
|
+
}
|
|
291
|
+
if (req.method !== "PUT") return json(res, 405, { error: "method not allowed" });
|
|
292
|
+
if (!originAllowed(req)) return json(res, 403, { error: "cross-origin write refused" });
|
|
293
|
+
let raw;
|
|
294
|
+
try {
|
|
295
|
+
raw = await readBody(req);
|
|
296
|
+
} catch (error) {
|
|
297
|
+
return json(res, 400, { error: message(error) });
|
|
298
|
+
}
|
|
299
|
+
let parsed;
|
|
300
|
+
try {
|
|
301
|
+
parsed = JSON.parse(raw);
|
|
302
|
+
} catch (error) {
|
|
303
|
+
return json(res, 422, { error: `not JSON — the file was NOT written: ${message(error)}` });
|
|
304
|
+
}
|
|
305
|
+
const shape = sceneFacts(parsed);
|
|
306
|
+
if (!shape.ok) return json(res, 422, { error: `not an incanto scene — ${shape.why}. The file was NOT written.` });
|
|
307
|
+
try {
|
|
308
|
+
writeFileSync(abs, raw.endsWith("\n") ? raw : `${raw}\n`);
|
|
309
|
+
} catch (error) {
|
|
310
|
+
return json(res, 400, { error: message(error) });
|
|
311
|
+
}
|
|
312
|
+
return json(res, 200, {
|
|
313
|
+
ok: true,
|
|
314
|
+
output: abs
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
function message(error) {
|
|
318
|
+
return error instanceof Error ? error.message : String(error);
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Serve the agent8 asset catalog at `/api/library` on the dev server.
|
|
322
|
+
*
|
|
323
|
+
* The catalog wants a bearer token and does not CORS-allow localhost, so a page
|
|
324
|
+
* cannot call it — but the dev server can, and it is already running. This is
|
|
325
|
+
* the same proxy `incanto-editor` ships, so the editor's 📚 works identically
|
|
326
|
+
* whether it opened as a page or over a running game.
|
|
327
|
+
*
|
|
328
|
+
* DEV ONLY (vite `configureServer`): nothing about it reaches a build, and the
|
|
329
|
+
* token stays in the terminal that started the server.
|
|
330
|
+
*
|
|
331
|
+
* ```ts
|
|
332
|
+
* // vite.config.ts
|
|
333
|
+
* import { incantoLibrary, incantoScenes } from 'incanto/vite';
|
|
334
|
+
* export default defineConfig({ plugins: [incantoScenes(), incantoLibrary()] });
|
|
335
|
+
* ```
|
|
336
|
+
* ```ts
|
|
337
|
+
* // main.ts — the editor then offers the library
|
|
338
|
+
* createGame3D({ ..., debug: DEV, editor: { library: true } });
|
|
339
|
+
* ```
|
|
340
|
+
*/
|
|
341
|
+
function incantoLibrary(opts = {}) {
|
|
342
|
+
return {
|
|
343
|
+
name: "incanto-library",
|
|
344
|
+
apply: "serve",
|
|
345
|
+
configureServer(server) {
|
|
346
|
+
server.middlewares.use("/api/library", (req, res) => {
|
|
347
|
+
serveLibrary(req, res, opts);
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
/** @internal Exported for tests — the whole request/response behaviour. */
|
|
353
|
+
async function serveLibrary(req, res, opts = {}, doFetch = fetch) {
|
|
354
|
+
const send = (status, body) => {
|
|
355
|
+
res.statusCode = status;
|
|
356
|
+
res.setHeader("content-type", "application/json; charset=utf-8");
|
|
357
|
+
res.end(JSON.stringify(body));
|
|
358
|
+
};
|
|
359
|
+
if (req.method && req.method !== "GET") return send(405, { error: "method not allowed" });
|
|
360
|
+
const env = globalThis.process?.env;
|
|
361
|
+
const header = req.headers["x-incanto-v8-token"];
|
|
362
|
+
const token = (Array.isArray(header) ? header[0] : header)?.trim() || opts.token || env?.INCANTO_V8_TOKEN || env?.V8_ACCESS_TOKEN || "";
|
|
363
|
+
if (!token) return send(401, {
|
|
364
|
+
error: "no Verse8 access token. Pass one to incantoLibrary({ token }) or set INCANTO_V8_TOKEN, or paste one into the library panel.",
|
|
365
|
+
needsToken: true
|
|
366
|
+
});
|
|
367
|
+
const query = new URL(req.url ?? "/", "http://localhost").searchParams;
|
|
368
|
+
const params = new URLSearchParams({
|
|
369
|
+
tags: query.get("tags") ?? "",
|
|
370
|
+
page: query.get("page") ?? "1",
|
|
371
|
+
limit: query.get("limit") ?? "24"
|
|
372
|
+
});
|
|
373
|
+
const keyword = query.get("keyword");
|
|
374
|
+
if (keyword) params.set("keyword", keyword);
|
|
375
|
+
const base = opts.url ?? env?.INCANTO_LIBRARY_URL ?? "https://agent8-backend.verse8.io/api/resources";
|
|
376
|
+
try {
|
|
377
|
+
const upstream = await doFetch(`${base}?${params}`, { headers: {
|
|
378
|
+
authorization: `Bearer ${token}`,
|
|
379
|
+
accept: "application/json"
|
|
380
|
+
} });
|
|
381
|
+
if (upstream.status === 401 || upstream.status === 403) return send(401, {
|
|
382
|
+
error: "the asset library rejected that token (expired or wrong account)",
|
|
383
|
+
needsToken: true
|
|
384
|
+
});
|
|
385
|
+
if (!upstream.ok) return send(502, { error: `asset library: HTTP ${upstream.status}` });
|
|
386
|
+
const body = await upstream.json();
|
|
387
|
+
if (body.success === false) return send(502, { error: String(body.error ?? "library error") });
|
|
388
|
+
send(200, {
|
|
389
|
+
items: (Array.isArray(body.data) ? body.data : []).map(libraryItem).filter((i) => i.url),
|
|
390
|
+
page: body.pagination?.page ?? Number(params.get("page")),
|
|
391
|
+
hasNext: body.pagination?.hasNext === true,
|
|
392
|
+
...typeof body.pagination?.total === "number" ? { total: body.pagination.total } : {}
|
|
393
|
+
});
|
|
394
|
+
} catch (error) {
|
|
395
|
+
send(502, { error: `asset library unreachable: ${error instanceof Error ? error.message : String(error)}` });
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
function libraryItem(row) {
|
|
399
|
+
const r = row ?? {};
|
|
400
|
+
return {
|
|
401
|
+
id: String(r.id ?? r.url ?? ""),
|
|
402
|
+
url: typeof r.url === "string" ? r.url : "",
|
|
403
|
+
description: typeof r.description === "string" ? r.description : "",
|
|
404
|
+
metadata: r.metadata && typeof r.metadata === "object" ? r.metadata : {}
|
|
405
|
+
};
|
|
406
|
+
}
|
|
56
407
|
//#endregion
|
|
57
|
-
export { incantoScenes };
|
|
408
|
+
export { discoverScenes, incantoLibrary, incantoScenes, resolveSceneFile, sceneFacts, serveLibrary, serveSceneFile, serveSceneList };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{n as e}from"./index-BO6WU8by.js";async function t(t){return new n((await e(()=>import(`./GameServer-C56iOUgF.js`),[],import.meta.url)).GameServer,t)}var n=class{raw;active=new Map;reconnecting=!1;disposed=!1;constructor(e,t){this.raw=new e({...t})}get account(){return this.raw.account}get connected(){return this.raw.connected}connect(){return this.raw.connected?Promise.resolve(!0):(this.disposed=!1,this.rawConnect())}rawConnect(){return this.raw.connect({onDisconnect:()=>void this.reconnect()})}disconnect(){this.disposed=!0;for(let e of this.active.values())e.off();return this.active.clear(),this.raw.disconnect()}remoteFunction(e,t,n){return this.raw.remoteFunction(e,t,n)}track(e){let t=Symbol(`sub`),n={make:e,off:e()};return this.active.set(t,n),()=>{n.off(),this.active.delete(t)}}async reconnect(){if(!this.disposed&&!this.reconnecting){this.reconnecting=!0;try{await this.rawConnect();for(let e of this.active.values())e.off(),e.off=e.make()}finally{this.reconnecting=!1}}}subscribeRoomState(e,t){return this.track(()=>this.raw.subscribeRoomState(e,t))}subscribeRoomMyState(e,t){return this.track(()=>this.raw.subscribeRoomMyState(e,t))}subscribeRoomAllUserStates(e,t){return this.track(()=>this.raw.subscribeRoomAllUserStates(e,e=>{let n={};for(let t of e??[]){if(!t||typeof t.account!=`string`||t.__leaved)continue;let{account:e,__updated:r,__leaved:i,...a}=t;n[e]=a}t(n)}))}subscribeRoomCollection(e,t,n){return this.track(()=>this.raw.subscribeRoomCollection(e,t,({items:e})=>{let t={};for(let n of e??[])n&&typeof n.__id==`string`&&(t[n.__id]=n);n(t)}))}onRoomMessage(e,t,n){return this.track(()=>this.raw.onRoomMessage(e,t,n))}onRoomUserJoin(e,t){return this.track(()=>this.raw.onRoomUserJoin(e,t))}onRoomUserLeave(e,t){return this.track(()=>this.raw.onRoomUserLeave(e,t))}subscribeGlobalState(e){return this.track(()=>this.raw.subscribeGlobalState(e))}subscribeGlobalMyState(e){return this.track(()=>this.raw.subscribeGlobalMyState(e))}subscribeGlobalUserState(e,t){return this.track(()=>this.raw.subscribeGlobalUserState(e,t))}subscribeGlobalCollection(e,t){return this.track(()=>this.raw.subscribeGlobalCollection(e,({items:e})=>{let n={};for(let t of e??[])t&&typeof t.__id==`string`&&(n[t.__id]=t);t(n)}))}subscribeAsset(e,t){return this.track(()=>this.raw.subscribeAsset(e,t))}onGlobalMessage(e,t){return this.track(()=>this.raw.onGlobalMessage(e,t))}};export{t as createAgent8Server};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{i as e,r as t,t as n}from"./index-BO6WU8by.js";function r(e,t,n,r,i,a,o=180,s=120){let c=Math.max(o,n),l=Math.max(s,r);return{x:Math.min(Math.max(0,e),Math.max(0,i-c)),y:Math.min(Math.max(0,t),Math.max(0,a-l)),w:c,h:l}}function i(e,t){for(let[n,r]of Object.entries(t))e.style[n]=r}var a=`rgba(18, 20, 26, 0.92)`,o=`1px solid rgba(255,255,255,0.14)`,s=`12px ui-monospace, SFMono-Regular, Menlo, monospace`,c=class{host;el;body;onClose=()=>{};x;y;w;h;constructor(e,t,n,r){this.host=t,this.x=r.x,this.y=r.y,this.w=r.w,this.h=r.h,this.el=e.createElement(`div`),i(this.el,{position:`absolute`,background:a,border:o,borderRadius:`8px`,color:`rgba(255,255,255,0.88)`,font:s,display:`flex`,flexDirection:`column`,overflow:`hidden`,zIndex:`40`,pointerEvents:`auto`,boxShadow:`0 8px 28px rgba(0,0,0,0.45)`});let c=e.createElement(`div`);c.textContent=n,i(c,{padding:`6px 28px 6px 10px`,background:`rgba(255,255,255,0.07)`,cursor:`move`,userSelect:`none`,touchAction:`none`,fontWeight:`700`}),this.el.appendChild(c);let l=e.createElement(`div`);l.textContent=`×`,l.title=`close`,i(l,{position:`absolute`,top:`2px`,right:`8px`,cursor:`pointer`,fontSize:`16px`,lineHeight:`20px`,opacity:`0.7`}),l.addEventListener(`click`,()=>this.onClose()),this.el.appendChild(l),this.body=e.createElement(`div`),i(this.body,{flex:`1`,overflow:`auto`,padding:`8px 10px`}),this.el.appendChild(this.body);let u=e.createElement(`div`);u.textContent=`◢`,i(u,{position:`absolute`,right:`2px`,bottom:`0`,cursor:`nwse-resize`,opacity:`0.5`,userSelect:`none`,touchAction:`none`}),this.el.appendChild(u),this.wireDrag(c,(e,t)=>{this.x+=e,this.y+=t,this.layout()}),this.wireDrag(u,(e,t)=>{this.w+=e,this.h+=t,this.layout()}),this.layout(),t.appendChild(this.el)}remove(){this.el.remove()}layout(){let e=this.host.getBoundingClientRect(),t=r(this.x,this.y,this.w,this.h,e.width,e.height);this.x=t.x,this.y=t.y,this.w=t.w,this.h=t.h,i(this.el,{left:`${t.x}px`,top:`${t.y}px`,width:`${t.w}px`,height:`${t.h}px`})}wireDrag(e,t){let n=null,r=0,i=0;e.addEventListener(`pointerdown`,t=>{n=t.pointerId,r=t.clientX,i=t.clientY,e.setPointerCapture?.(t.pointerId)}),e.addEventListener(`pointermove`,e=>{e.pointerId===n&&(t(e.clientX-r,e.clientY-i),r=e.clientX,i=e.clientY)});let a=e=>{e.pointerId===n&&(n=null)};e.addEventListener(`pointerup`,a),e.addEventListener(`pointercancel`,a)}},l=96,u=200,d=8192;function f(e){return Array.isArray(e)?`Array(${e.length})`:`Object(${Object.keys(e).length} keys)`}function p(e,t){if(Array.isArray(e)&&e.some(e=>typeof e==`object`&&!!e)){let t=e.slice(0,u).map(e=>JSON.stringify(e)),n=e.length-u;return t.join(`
|
|
2
|
+
`)+(n>0?`\n… ${n} more`:``)}return t.length>d?`${t.slice(0,d)} … (${t.length-d} more chars)`:t}function m(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function h(e,t={}){let n=t.doc??(typeof document<`u`?document:null);if(!n)return null;let r=t.container??(typeof document<`u`?document.body:null);return!r||typeof r.appendChild!=`function`?null:new v(e,r,n,t.statsSource,t.actions)}var g=300,_=[`log`,`info`,`warn`,`error`,`debug`],v=class{engine;container;doc;statsSource;actions;panels=new Map;cleanups=[];menuButton;dropdown=null;selected=null;collapsedFlags=new Map;statsChip=null;logRows=[];levelEnabled={debug:!0,info:!0,warn:!0,error:!0};consoleCapture=!1;consolePatched=[];frame=0;editing=0;detailOpen=new WeakMap;hovering=!1;constructor(e,t,n,r,a=[]){this.engine=e,this.container=t,this.doc=n,this.statsSource=r,this.actions=a,this.menuButton=n.createElement(`div`),this.menuButton.textContent=`☰ debug`,i(this.menuButton,{position:`absolute`,top:`8px`,left:`8px`,padding:`4px 10px`,background:`rgba(18,20,26,0.85)`,border:`1px solid rgba(255,255,255,0.18)`,borderRadius:`6px`,color:`rgba(255,255,255,0.85)`,font:`12px ui-monospace, Menlo, monospace`,cursor:`pointer`,userSelect:`none`,zIndex:`50`,pointerEvents:`auto`}),this.menuButton.addEventListener(`click`,()=>this.toggleDropdown()),t.style.position||(t.style.position=`relative`),t.appendChild(this.menuButton),this.cleanups.push(e.log.added.connect(e=>{this.pushLog({level:e.level,source:`engine`,text:e.parts.map(x).join(` `)})})),this.cleanups.push(e.sceneChanged.connect(()=>{this.selected=null,this.applyColliderScope(),this.renderExplorer(),this.renderInspector()})),this.cleanups.push(e.updated.connect(()=>{this.frame+=1,this.frame%30==0&&(this.renderExplorer(),this.renderStats(),this.editing===0&&!this.hovering&&this.renderInspector())}))}isOpen(e){return e===`colliders`?this.colliderMode!==`off`:e===`stats`?this.statsChip!==null:this.panels.has(e)}colliderMode=`off`;setColliders(e){this.colliderMode=e,this.applyColliderScope()}applyColliderScope(){let e=this.colliderMode;for(let t of n(`2d`).concat(n(`3d`)))t.debugDraw=e!==`off`,t.debugScope=e===`selected`?this.selected:null}open(e){if(e===`stats`){this.openStatsChip();return}if(this.panels.has(e))return;let t=new c(this.doc,this.container,{explorer:`Explorer`,inspector:`Inspector`,logs:`Logs`}[e],{explorer:{x:12,y:44,w:240,h:320},inspector:{x:264,y:44,w:280,h:320},logs:{x:12,y:380,w:532,h:200}}[e]);t.onClose=()=>this.close(e),e===`inspector`&&(t.body.addEventListener(`pointerenter`,()=>{this.hovering=!0}),t.body.addEventListener(`pointerleave`,()=>{this.hovering=!1})),this.panels.set(e,t),e===`explorer`&&this.renderExplorer(),e===`inspector`&&this.renderInspector(),e===`logs`&&this.renderLogs()}close(e){if(e===`inspector`&&(this.hovering=!1),e===`stats`){this.statsChip?.remove(),this.statsChip=null;return}let t=this.panels.get(e);t&&(t.remove(),this.panels.delete(e))}toggle(e){if(e===`colliders`){this.setColliders({off:`all`,all:`selected`,selected:`off`}[this.colliderMode]);return}this.isOpen(e)?this.close(e):this.open(e)}setLevelEnabled(e,t){this.levelEnabled[e]=t,this.renderLogs()}setConsoleCapture(e){if(e!==this.consoleCapture)if(this.consoleCapture=e,e){this.renderLogs();let e=console;for(let t of _){let n=e[t];e[t]=(...e)=>{n.apply(console,e);let r=t===`log`?`info`:t;this.pushLog({level:r,source:`console`,text:e.map(x).join(` `)})},this.consolePatched.push(()=>{e[t]=n})}}else{for(let e of this.consolePatched)e();this.consolePatched=[],this.renderLogs()}}dispose(){this.engine.debugSelection=null,this.setColliders(`off`),this.setConsoleCapture(!1);for(let e of this.cleanups)e();for(let e of[...this.panels.keys()])this.close(e);this.close(`stats`),this.dropdown?.remove(),this.dropdown=null,this.menuButton.remove()}menuLabel(e,t){let n=e===`colliders`&&this.colliderMode!==`off`?` · ${this.colliderMode}`:``;return`${this.isOpen(e)?`✓ `:``}${t}${n}`}toggleDropdown(){if(this.dropdown){this.dropdown.remove(),this.dropdown=null;return}let e=this.doc.createElement(`div`);i(e,{position:`absolute`,top:`34px`,left:`8px`,background:`rgba(18,20,26,0.95)`,border:`1px solid rgba(255,255,255,0.18)`,borderRadius:`6px`,font:`12px ui-monospace, Menlo, monospace`,color:`rgba(255,255,255,0.85)`,zIndex:`60`,pointerEvents:`auto`,overflow:`hidden`});for(let[t,n]of[[`explorer`,`Explorer`],[`inspector`,`Inspector`],[`logs`,`Logs`],[`stats`,`Stats`],[`colliders`,`Colliders`]]){let r=this.doc.createElement(`div`);r.textContent=this.menuLabel(t,n),i(r,{padding:`6px 14px`,cursor:`pointer`,userSelect:`none`}),r.addEventListener(`click`,()=>{if(this.toggle(t),t===`colliders`){r.textContent=this.menuLabel(t,n);return}this.dropdown?.remove(),this.dropdown=null}),e.appendChild(r)}for(let t of this.actions){let n=this.doc.createElement(`div`);n.textContent=t.label,i(n,{padding:`6px 14px`,cursor:`pointer`,userSelect:`none`,borderTop:`1px solid rgba(255,255,255,0.14)`,color:`rgba(158,232,220,0.95)`}),n.addEventListener(`click`,()=>{this.dropdown?.remove(),this.dropdown=null,t.run()}),e.appendChild(n)}this.container.appendChild(e),this.dropdown=e}openStatsChip(){if(this.statsChip)return;let e=this.doc.createElement(`div`);i(e,{position:`absolute`,top:`8px`,right:`8px`,padding:`4px 10px`,background:`rgba(18,20,26,0.85)`,border:`1px solid rgba(255,255,255,0.18)`,borderRadius:`6px`,color:`rgba(255,255,255,0.85)`,font:`12px ui-monospace, Menlo, monospace`,textAlign:`right`,whiteSpace:`pre`,userSelect:`none`,pointerEvents:`none`,zIndex:`70`}),this.container.appendChild(e),this.statsChip=e,this.renderStats()}renderStats(){let e=this.statsChip;if(!e)return;let t=this.engine.stats(),n=this.statsSource?.()??{},r=[`nodes ${t.nodes}`,...n.triangles===void 0?[]:[`tris ${b(n.triangles)}`],...n.drawCalls===void 0?[]:[`calls ${n.drawCalls}`]].join(` · `);e.textContent=`${Math.round(t.fps)} fps · ${t.frameMs.toFixed(1)} ms\n${r}`}renderExplorer(){let e=this.panels.get(`explorer`);if(!e)return;let t=e.body.scrollTop;y(e.body);let n=this.engine.scene?.root;if(!n){e.body.scrollTop=t;return}let r=(e,t)=>{let n=e.constructor,a=e.children.length>0,o=this.collapsedFlags.get(e)===!0,s=this.doc.createElement(`div`);i(s,{display:`flex`,alignItems:`center`,cursor:`pointer`,padding:`1px 2px`,borderRadius:`3px`,background:e===this.selected?`rgba(110,160,255,0.25)`:`transparent`});let c=this.doc.createElement(`span`);c.textContent=a?o?`▸`:`▾`:`·`,i(c,{width:`14px`,flex:`none`,textAlign:`center`,opacity:a?`0.85`:`0.25`,userSelect:`none`}),a&&c.addEventListener(`click`,t=>{t.stopPropagation?.(),this.collapsedFlags.set(e,!o),this.renderExplorer()}),s.appendChild(c);let l=this.doc.createElement(`span`);l.textContent=e.name,i(l,{whiteSpace:`nowrap`}),s.appendChild(l);let u=this.doc.createElement(`span`);if(u.textContent=` ${n.typeName}`,i(u,{opacity:`0.45`,whiteSpace:`nowrap`,fontSize:`10px`}),s.appendChild(u),a&&o){let t=this.doc.createElement(`span`);t.textContent=` (${e.children.length})`,i(t,{opacity:`0.35`,fontSize:`10px`}),s.appendChild(t)}if(s.addEventListener(`click`,()=>{this.selected=e,this.engine.debugSelection=e,this.applyColliderScope(),this.open(`inspector`),this.renderExplorer(),this.renderInspector()}),t.appendChild(s),a&&!o){let n=this.doc.createElement(`div`);i(n,{marginLeft:`8px`,paddingLeft:`8px`,borderLeft:`1px solid rgba(255,255,255,0.14)`});for(let t of e.children)r(t,n);t.appendChild(n)}};r(n,e.body),e.body.scrollTop=t}renderInspector(){let e=this.panels.get(`inspector`);if(!e)return;let n=e.body.scrollTop;y(e.body);let r=this.selected;if(r&&r.tree===null&&(this.selected=null,this.engine.debugSelection=null,this.applyColliderScope(),r=null),!r){let t=this.doc.createElement(`div`);t.textContent=`select a node in the Explorer`,i(t,{opacity:`0.6`}),e.body.appendChild(t);return}let a=r.constructor,o=this.doc.createElement(`div`);if(o.textContent=`${r.getPath()} · ${a.typeName}${r.uid?` · ${r.uid}`:``}`,i(o,{fontWeight:`700`,marginBottom:`6px`,whiteSpace:`pre-wrap`}),e.body.appendChild(o),r.groups.size>0){let t=this.doc.createElement(`div`);t.textContent=`groups: ${[...r.groups].join(`, `)}`,i(t,{opacity:`0.7`,marginBottom:`6px`}),e.body.appendChild(t)}let s=t(a),c=r;for(let t of Object.keys(s)){let n=s[t];this.renderValueRow(e.body,r,t,{read:()=>c[t],write:e=>{c[t]=e},options:n?.options,variants:n?.variants})}e.body.scrollTop=n}renderValueRow(t,n,r,a){let o=a.read(),s=a.options,c={get[r](){return a.read()},set[r](e){a.write(e)}},u=this.doc.createElement(`div`);i(u,{display:`flex`,gap:`6px`,alignItems:`center`,margin:`2px 0`});let d=this.doc.createElement(`div`);d.textContent=r,i(d,{minWidth:`84px`,opacity:`0.75`}),u.appendChild(d);let h=(e,t)=>{let a=this.doc.createElement(`input`);return a.type=`number`,a.value=String(e),i(a,S(`70px`)),this.trackEditing(a),a.addEventListener(`change`,()=>{let e=Number(a.value);Number.isFinite(e)?t(e):a.value=String(n[r])}),a};if(typeof o==`number`)u.appendChild(h(o,e=>{c[r]=e}));else if(typeof o==`boolean`){let e=this.doc.createElement(`input`);e.type=`checkbox`,e.checked=o,this.trackEditing(e),e.addEventListener(`change`,()=>{c[r]=e.checked}),u.appendChild(e)}else if(typeof o==`string`&&s&&s.length>0){let e=this.doc.createElement(`select`);for(let t of s.includes(o)?s:[o,...s]){let n=this.doc.createElement(`option`);n.value=t,n.textContent=t,t===o&&(n.selected=!0),e.appendChild(n)}e.value=o,this.trackEditing(e),e.addEventListener(`change`,()=>{c[r]=e.value}),i(e,S(`110px`)),u.appendChild(e)}else if(typeof o==`string`){let e=this.doc.createElement(`input`);e.type=`text`,e.value=o,this.trackEditing(e),i(e,S(`140px`)),e.addEventListener(`change`,()=>{c[r]=e.value}),u.appendChild(e)}else if(Array.isArray(o)&&o.length<=8&&o.every(e=>typeof e==`number`))for(let e=0;e<o.length;e++)u.appendChild(h(o[e],t=>{let n=[...c[r]];n[e]=t,c[r]=n}));else if(m(o)){let s=this.doc.createElement(`div`);i(s,{marginLeft:`10px`,paddingLeft:`8px`,borderLeft:`1px solid rgba(255,255,255,0.14)`});let c=a.variants?.tag,l=Object.keys(o);c&&!l.includes(c)&&l.unshift(c);let d=a.path??r;for(let t of l){let r=t===c;this.renderValueRow(s,n,t,{path:`${d}.${t}`,read:()=>a.read()[t]??(r?``:null),write:n=>{if(r&&a.variants){let t=a.variants.byTag[String(n)];if(t!==void 0){a.write(e(t));return}}a.write({...a.read(),[t]:n})},options:r&&a.variants?Object.keys(a.variants.byTag):void 0})}t.appendChild(u),t.appendChild(s);return}else{let s=JSON.stringify(e(o));if(s.length<=l){let e=this.doc.createElement(`div`);e.textContent=s,i(e,{opacity:`0.65`,whiteSpace:`pre-wrap`,wordBreak:`break-all`}),u.appendChild(e)}else{let e=a.path??r,c=this.detailOpen.get(n)?.has(e)??!1,l=this.doc.createElement(`div`);if(l.textContent=`${c?`▾`:`▸`} ${f(o)}`,i(l,{opacity:`0.75`,cursor:`pointer`,userSelect:`none`}),l.addEventListener(`click`,()=>{let t=this.detailOpen.get(n);t||(t=new Set,this.detailOpen.set(n,t)),c?t.delete(e):t.add(e),this.renderInspector()}),u.appendChild(l),t.appendChild(u),c){let e=this.doc.createElement(`div`);e.textContent=p(o,s),i(e,{opacity:`0.6`,whiteSpace:`pre-wrap`,wordBreak:`break-all`,marginLeft:`10px`,paddingLeft:`8px`,borderLeft:`1px solid rgba(255,255,255,0.14)`}),t.appendChild(e)}return}}t.appendChild(u)}trackEditing(e){e.addEventListener(`focus`,()=>{this.editing+=1}),e.addEventListener(`blur`,()=>{this.editing=Math.max(0,this.editing-1)})}pushLog(e){this.logRows.push(e),this.logRows.length>g&&this.logRows.splice(0,this.logRows.length-g),this.renderLogs()}renderLogs(){let e=this.panels.get(`logs`);if(!e)return;y(e.body);let t=this.doc.createElement(`div`);i(t,{display:`flex`,gap:`8px`,marginBottom:`4px`,flexWrap:`wrap`});for(let e of[`debug`,`info`,`warn`,`error`]){let n=this.doc.createElement(`div`);n.textContent=`${this.levelEnabled[e]?`✓`:`·`}${e}`,i(n,{cursor:`pointer`,opacity:this.levelEnabled[e]?`1`:`0.45`}),n.addEventListener(`click`,()=>this.setLevelEnabled(e,!this.levelEnabled[e])),t.appendChild(n)}let n=this.doc.createElement(`div`);n.textContent=`${this.consoleCapture?`✓`:`·`}console`,i(n,{cursor:`pointer`,marginLeft:`auto`}),n.addEventListener(`click`,()=>this.setConsoleCapture(!this.consoleCapture)),t.appendChild(n),e.body.appendChild(t);let r={debug:`rgba(255,255,255,0.5)`,info:`rgba(255,255,255,0.85)`,warn:`#ffc861`,error:`#ff6b6b`};for(let t of this.logRows){if(!this.levelEnabled[t.level])continue;let n=this.doc.createElement(`div`);n.textContent=`[${t.source===`console`?`console`:t.level}] ${t.text}`,i(n,{color:r[t.level],whiteSpace:`pre-wrap`}),e.body.appendChild(n)}}};function y(e){for(let t of[...e.children])t.remove()}function b(e){return e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}k`:String(e)}function x(e){if(typeof e==`string`)return e;try{return JSON.stringify(e)}catch{return String(e)}}function S(e){return{width:e,background:`rgba(255,255,255,0.08)`,border:`1px solid rgba(255,255,255,0.2)`,borderRadius:`4px`,color:`inherit`,font:`inherit`,padding:`2px 4px`}}export{h as attachDebugOverlay};
|