incanto 0.50.0 → 0.51.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/dist/2d.d.ts +3 -2
- package/dist/2d.js +3 -3
- package/dist/3d.d.ts +4 -3
- package/dist/3d.js +4 -4
- package/dist/{behavior-62q0HWBO.d.ts → behavior-rZNfzVbH.d.ts} +37 -3
- package/dist/{create-game-DqqxEax1.js → create-game-BNaOC3Px.js} +5 -5
- package/dist/{create-game-BLDjy_PW.js → create-game-CkzKZ4v5.js} +5 -5
- package/dist/debug.d.ts +1 -1
- package/dist/{duplicate-CI9WF_bg.js → duplicate-EybyYeyL.js} +1 -1
- package/dist/{environment-presets-XFuqu5jv.js → environment-presets-C08pOC6H.js} +2 -2
- package/dist/{gameplay-DbaI313d.js → gameplay-bStgtZBV.js} +159 -4
- package/dist/gameplay.d.ts +79 -2
- package/dist/gameplay.js +2 -2
- package/dist/index.d.ts +14 -5
- package/dist/index.js +6 -6
- package/dist/{loader-DwazzlQb.js → loader-B2asghWa.js} +350 -1
- package/dist/{loader-CeyU_bm1.d.ts → loader-Dkbn56KC.d.ts} +1 -1
- package/dist/net.d.ts +1 -1
- package/dist/net.js +3 -3
- package/dist/{pathfinding-C49JSNNq.d.ts → pathfinding-Bz34pvQD.d.ts} +1 -1
- package/dist/{physics-2d-_9VBOHn6.js → physics-2d-Ccq2L9R0.js} +7 -2
- package/dist/{physics-3d-DrpF5hcG.js → physics-3d-B2c5KNYh.js} +8 -3
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{register-BNPZYJmd.js → register-B_BaaGx-.js} +2 -2
- package/dist/{register-uvaZj1KX.js → register-CGq-Hee8.js} +19 -265
- package/dist/{register-CB11yp21.js → register-CLVhzWcI.js} +2 -2
- package/dist/{replay-j-m6lJ4W.js → replay-BgKxGcXH.js} +20 -1
- package/dist/{replay-DYdy1wb0.d.ts → replay-DIP2_as4.d.ts} +1 -1
- package/dist/{src-B3HrKuAi.js → src-C3UwzYXl.js} +1 -1
- package/dist/{test-D16igj4C.js → test-D2gRpk5V.js} +11 -11
- package/dist/test.d.ts +3 -3
- package/dist/test.js +2 -2
- package/dist/vite.js +2 -2
- package/editor/assets/{agent8-t3kl5q9K.js → agent8-DbX_msaO.js} +1 -1
- package/editor/assets/{debug-Bu3eeAlO.js → debug-C-kMxdl1.js} +1 -1
- package/editor/assets/{index-Df5g8ofT.js → index-CUc1U7wm.js} +91 -91
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/skills/incanto-building-2d-games.md +6 -1
- package/skills/incanto-node-reference.md +13 -0
- package/skills/incanto-save-slots.md +134 -12
- package/templates-app/beacon-isle-3d/package.json +1 -1
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/village-quest-3d/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as InputMap, M as Signal, S as resolveOrderGroups, _ as restoreBehaviors, b as ORDER_GROUP_BASE, f as Node, g as captureBehaviors, w as diagnose, y as createSaveStore } from "./loader-B2asghWa.js";
|
|
2
2
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
3
3
|
import { t as Rng } from "./rng-DP-SR7eg.js";
|
|
4
4
|
import { l as registerNode } from "./registry-IyWCGe4q.js";
|
|
@@ -1134,267 +1134,6 @@ var LogManager = class {
|
|
|
1134
1134
|
}
|
|
1135
1135
|
};
|
|
1136
1136
|
//#endregion
|
|
1137
|
-
//#region src/core/save.ts
|
|
1138
|
-
const memoryStores = /* @__PURE__ */ new Map();
|
|
1139
|
-
function createSaveStore(namespace) {
|
|
1140
|
-
const prefix = `incanto:${namespace}:`;
|
|
1141
|
-
const local = storageOrNull();
|
|
1142
|
-
if (!local) {
|
|
1143
|
-
let mem = memoryStores.get(namespace);
|
|
1144
|
-
if (!mem) {
|
|
1145
|
-
mem = /* @__PURE__ */ new Map();
|
|
1146
|
-
memoryStores.set(namespace, mem);
|
|
1147
|
-
}
|
|
1148
|
-
const backing = mem;
|
|
1149
|
-
return {
|
|
1150
|
-
get: (key, fallback) => parse(backing.get(key), fallback),
|
|
1151
|
-
set: (key, value) => void backing.set(key, JSON.stringify(value)),
|
|
1152
|
-
remove: (key) => void backing.delete(key),
|
|
1153
|
-
clear: () => backing.clear()
|
|
1154
|
-
};
|
|
1155
|
-
}
|
|
1156
|
-
return {
|
|
1157
|
-
get: (key, fallback) => parse(local.getItem(prefix + key) ?? void 0, fallback),
|
|
1158
|
-
set: (key, value) => local.setItem(prefix + key, JSON.stringify(value)),
|
|
1159
|
-
remove: (key) => local.removeItem(prefix + key),
|
|
1160
|
-
clear: () => {
|
|
1161
|
-
const doomed = [];
|
|
1162
|
-
for (let i = 0; i < local.length; i++) {
|
|
1163
|
-
const k = local.key(i);
|
|
1164
|
-
if (k?.startsWith(prefix)) doomed.push(k);
|
|
1165
|
-
}
|
|
1166
|
-
for (const k of doomed) local.removeItem(k);
|
|
1167
|
-
}
|
|
1168
|
-
};
|
|
1169
|
-
}
|
|
1170
|
-
function parse(raw, fallback) {
|
|
1171
|
-
if (raw === void 0 || raw === null) return fallback;
|
|
1172
|
-
try {
|
|
1173
|
-
return JSON.parse(raw);
|
|
1174
|
-
} catch {
|
|
1175
|
-
return fallback;
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
function storageOrNull() {
|
|
1179
|
-
try {
|
|
1180
|
-
if (typeof localStorage === "undefined") return null;
|
|
1181
|
-
const probe = "__incanto_probe__";
|
|
1182
|
-
localStorage.setItem(probe, "1");
|
|
1183
|
-
localStorage.removeItem(probe);
|
|
1184
|
-
return localStorage;
|
|
1185
|
-
} catch {
|
|
1186
|
-
return null;
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
//#endregion
|
|
1190
|
-
//#region src/core/save-slots.ts
|
|
1191
|
-
/**
|
|
1192
|
-
* Save slots — "continue where you left off", which no game on this engine could
|
|
1193
|
-
* do.
|
|
1194
|
-
*
|
|
1195
|
-
* `createSaveStore` has been here a long time and `Scene.toJSON()` round-trips a
|
|
1196
|
-
* live tree, so two thirds of the work existed. The missing third was that
|
|
1197
|
-
* BEHAVIOR state had nowhere to go: `Behavior` declared `onEnterTree`, `onReady`,
|
|
1198
|
-
* `onExitTree`, `update` and `fixedUpdate` and nothing for "write yourself down",
|
|
1199
|
-
* so `Health.current`, `ScoreKeeper.score` and every quest flag were
|
|
1200
|
-
* unrecoverable. Both quest templates this repo ships were RPGs you could not
|
|
1201
|
-
* resume.
|
|
1202
|
-
*
|
|
1203
|
-
* ## What a save is, and what it deliberately is NOT
|
|
1204
|
-
*
|
|
1205
|
-
* A save is **which scene, plus every behavior's state, keyed by node uid**. It
|
|
1206
|
-
* is not a snapshot of the live tree.
|
|
1207
|
-
*
|
|
1208
|
-
* That is a design decision, not a shortcut. Restoring a live tree by walking
|
|
1209
|
-
* paths breaks on this engine specifically:
|
|
1210
|
-
*
|
|
1211
|
-
* - `Spawner.onReady` DETACHES its prefab template from the tree (see
|
|
1212
|
-
* `SpawnSource.resolveTemplate`), so a captured tree and a freshly booted one
|
|
1213
|
-
* legitimately disagree about which nodes exist, every time.
|
|
1214
|
-
* Every scene with a spawner would report phantom removals.
|
|
1215
|
-
* - A spawned enemy carrying its own spawner has already lost ITS template, so
|
|
1216
|
-
* re-adding that subtree runs `onReady` against a node that is not there and
|
|
1217
|
-
* throws — and `onReady` is not quarantined, so it kills the whole load.
|
|
1218
|
-
* - Prop deltas cannot be applied with `applySchemaProps`, which resets every
|
|
1219
|
-
* prop to its default before writing.
|
|
1220
|
-
*
|
|
1221
|
-
* Reloading the scene from source sidesteps all three: the structure comes from
|
|
1222
|
-
* the file (which is authoritative and already validated), and the save carries
|
|
1223
|
-
* only what the file cannot know. A fresh boot detaches the same templates the
|
|
1224
|
-
* captured one did, so there is nothing to reconcile.
|
|
1225
|
-
*
|
|
1226
|
-
* The cost, stated plainly so nobody discovers it: **spawned enemies and
|
|
1227
|
-
* mid-level positions are not restored.** You resume at the scene's start with
|
|
1228
|
-
* your stats, inventory, unlocks and quest flags intact. That is what a
|
|
1229
|
-
* checkpoint save is, and it is what both quest templates need. A game that
|
|
1230
|
-
* wants a position saves it — `serialize()` can return anything.
|
|
1231
|
-
*
|
|
1232
|
-
* Node uids are the join key because they are the one identifier the engine
|
|
1233
|
-
* already promises survives a rename or a reparent.
|
|
1234
|
-
*/
|
|
1235
|
-
/**
|
|
1236
|
-
* Every behavior in the tree that has something to say, by uid.
|
|
1237
|
-
*
|
|
1238
|
-
* A behavior with no `serialize` contributes nothing — the hook is optional in
|
|
1239
|
-
* exactly the way the other five are, so adding save support to a game is
|
|
1240
|
-
* additive and a game that never saves pays nothing.
|
|
1241
|
-
*
|
|
1242
|
-
* A node with no `uid` is skipped and reported, because state keyed by nothing
|
|
1243
|
-
* cannot be given back. The editor assigns a uid to every node it touches; a
|
|
1244
|
-
* hand-written scene may not have.
|
|
1245
|
-
*/
|
|
1246
|
-
function captureBehaviors(root) {
|
|
1247
|
-
const state = {};
|
|
1248
|
-
const unaddressable = [];
|
|
1249
|
-
const walk = (node) => {
|
|
1250
|
-
const behavior = node.behavior;
|
|
1251
|
-
if (behavior && typeof behavior.serialize === "function") {
|
|
1252
|
-
const value = behavior.serialize();
|
|
1253
|
-
if (value !== void 0) if (node.uid) state[node.uid] = value;
|
|
1254
|
-
else unaddressable.push(node.getPath());
|
|
1255
|
-
}
|
|
1256
|
-
for (const child of node.children) walk(child);
|
|
1257
|
-
};
|
|
1258
|
-
walk(root);
|
|
1259
|
-
return {
|
|
1260
|
-
state,
|
|
1261
|
-
unaddressable
|
|
1262
|
-
};
|
|
1263
|
-
}
|
|
1264
|
-
/**
|
|
1265
|
-
* Hand each behavior its state back.
|
|
1266
|
-
*
|
|
1267
|
-
* Call this AFTER the scene has loaded and `onReady` has run: `onReady` is where
|
|
1268
|
-
* a behavior sets its starting values, so restoring first would be overwritten
|
|
1269
|
-
* by the fresh start.
|
|
1270
|
-
*
|
|
1271
|
-
* Never throws on a mismatch. A save from an older build of the game will name
|
|
1272
|
-
* uids that no longer exist, and refusing to load it would mean a patch that
|
|
1273
|
-
* moves one node deletes everyone's progress. It reports instead.
|
|
1274
|
-
*/
|
|
1275
|
-
function restoreBehaviors(root, state) {
|
|
1276
|
-
const report = {
|
|
1277
|
-
missing: [],
|
|
1278
|
-
skipped: [],
|
|
1279
|
-
restored: 0
|
|
1280
|
-
};
|
|
1281
|
-
for (const [uid, value] of Object.entries(state)) {
|
|
1282
|
-
const node = root.getNodeByUid(uid);
|
|
1283
|
-
if (!node) {
|
|
1284
|
-
report.missing.push(uid);
|
|
1285
|
-
continue;
|
|
1286
|
-
}
|
|
1287
|
-
const behavior = node.behavior;
|
|
1288
|
-
if (!behavior || typeof behavior.deserialize !== "function") {
|
|
1289
|
-
report.skipped.push(uid);
|
|
1290
|
-
continue;
|
|
1291
|
-
}
|
|
1292
|
-
try {
|
|
1293
|
-
behavior.deserialize(value);
|
|
1294
|
-
report.restored += 1;
|
|
1295
|
-
} catch {
|
|
1296
|
-
report.skipped.push(uid);
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
return report;
|
|
1300
|
-
}
|
|
1301
|
-
/** Slot ids, so a "3 save slots" menu is a list and not a naming convention. */
|
|
1302
|
-
const INDEX_KEY = "slots";
|
|
1303
|
-
/**
|
|
1304
|
-
* The slot layer: a named list of saves over the existing store.
|
|
1305
|
-
*
|
|
1306
|
-
* Slot ids are yours (`'auto'`, `'1'`, `'2'`, `'3'`) — the engine does not
|
|
1307
|
-
* decide how many you have or whether one of them autosaves.
|
|
1308
|
-
*/
|
|
1309
|
-
var SaveSlots = class {
|
|
1310
|
-
store;
|
|
1311
|
-
constructor(namespace = "saves", store) {
|
|
1312
|
-
this.store = store ?? createSaveStore(namespace);
|
|
1313
|
-
}
|
|
1314
|
-
/** Slot ids that have a save, newest first. */
|
|
1315
|
-
list() {
|
|
1316
|
-
const rows = this.store.get(INDEX_KEY, []).map((id) => ({
|
|
1317
|
-
id,
|
|
1318
|
-
slot: this.read(id)
|
|
1319
|
-
})).filter((row) => row.slot !== null);
|
|
1320
|
-
rows.sort((a, b) => b.slot.savedAt - a.slot.savedAt);
|
|
1321
|
-
return rows.map((row) => row.id);
|
|
1322
|
-
}
|
|
1323
|
-
/** Every slot with its contents — what a load menu actually renders. */
|
|
1324
|
-
all() {
|
|
1325
|
-
return this.list().map((id) => ({
|
|
1326
|
-
id,
|
|
1327
|
-
...this.read(id)
|
|
1328
|
-
}));
|
|
1329
|
-
}
|
|
1330
|
-
read(id) {
|
|
1331
|
-
const raw = this.store.get(key(id), null);
|
|
1332
|
-
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
1333
|
-
const slot = raw;
|
|
1334
|
-
if (typeof slot.scene !== "string" || typeof slot.state !== "object" || slot.state === null) return null;
|
|
1335
|
-
return {
|
|
1336
|
-
scene: slot.scene,
|
|
1337
|
-
state: slot.state,
|
|
1338
|
-
label: typeof slot.label === "string" ? slot.label : "",
|
|
1339
|
-
savedAt: typeof slot.savedAt === "number" ? slot.savedAt : 0,
|
|
1340
|
-
playtime: typeof slot.playtime === "number" ? slot.playtime : 0,
|
|
1341
|
-
data: slot.data ?? {}
|
|
1342
|
-
};
|
|
1343
|
-
}
|
|
1344
|
-
/**
|
|
1345
|
-
* Write a slot.
|
|
1346
|
-
*
|
|
1347
|
-
* `savedAt` is a parameter rather than `Date.now()` so a test can assert an
|
|
1348
|
-
* ordering without sleeping, and so a game that wants server time can use it.
|
|
1349
|
-
*/
|
|
1350
|
-
write(id, slot) {
|
|
1351
|
-
const full = {
|
|
1352
|
-
scene: slot.scene,
|
|
1353
|
-
state: slot.state,
|
|
1354
|
-
label: slot.label ?? "",
|
|
1355
|
-
savedAt: slot.savedAt ?? 0,
|
|
1356
|
-
playtime: slot.playtime ?? 0,
|
|
1357
|
-
data: slot.data ?? {}
|
|
1358
|
-
};
|
|
1359
|
-
this.store.set(key(id), full);
|
|
1360
|
-
const ids = this.store.get(INDEX_KEY, []);
|
|
1361
|
-
if (!ids.includes(id)) this.store.set(INDEX_KEY, [...ids, id]);
|
|
1362
|
-
}
|
|
1363
|
-
remove(id) {
|
|
1364
|
-
this.store.remove(key(id));
|
|
1365
|
-
this.store.set(INDEX_KEY, this.store.get(INDEX_KEY, []).filter((other) => other !== id));
|
|
1366
|
-
}
|
|
1367
|
-
/** Wipe every slot — a "delete all data" button, and test isolation. */
|
|
1368
|
-
clear() {
|
|
1369
|
-
for (const id of this.store.get(INDEX_KEY, [])) this.store.remove(key(id));
|
|
1370
|
-
this.store.set(INDEX_KEY, []);
|
|
1371
|
-
}
|
|
1372
|
-
};
|
|
1373
|
-
function key(id) {
|
|
1374
|
-
return `slot:${id}`;
|
|
1375
|
-
}
|
|
1376
|
-
/**
|
|
1377
|
-
* Behaviors in this tree that hold state and cannot save it — the audit's
|
|
1378
|
-
* question, answerable before a player loses an hour.
|
|
1379
|
-
*
|
|
1380
|
-
* A behavior with props but no `serialize` is not necessarily wrong (an
|
|
1381
|
-
* `Oscillate` derives everything from time), so this reports rather than warns,
|
|
1382
|
-
* and the caller decides.
|
|
1383
|
-
*/
|
|
1384
|
-
function behaviorsWithoutSave(root) {
|
|
1385
|
-
const out = [];
|
|
1386
|
-
const walk = (node) => {
|
|
1387
|
-
const behavior = node.behavior;
|
|
1388
|
-
const name = node.script?.name;
|
|
1389
|
-
if (behavior && typeof name === "string" && typeof behavior.serialize !== "function") {
|
|
1390
|
-
if (Object.keys(behaviorSchema(name)).length > 0) out.push(`${node.getPath()} (${name})`);
|
|
1391
|
-
}
|
|
1392
|
-
for (const child of node.children) walk(child);
|
|
1393
|
-
};
|
|
1394
|
-
walk(root);
|
|
1395
|
-
return out;
|
|
1396
|
-
}
|
|
1397
|
-
//#endregion
|
|
1398
1137
|
//#region src/core/settings.ts
|
|
1399
1138
|
const DEFAULTS = {
|
|
1400
1139
|
master: 1,
|
|
@@ -1890,7 +1629,7 @@ var Engine = class {
|
|
|
1890
1629
|
captureState() {
|
|
1891
1630
|
const root = this.scene?.root;
|
|
1892
1631
|
if (!root) return {};
|
|
1893
|
-
const { state, unaddressable } = captureBehaviors(root);
|
|
1632
|
+
const { state, unaddressable } = captureBehaviors(root, this.scene?.source);
|
|
1894
1633
|
for (const path of unaddressable) diagnose(this, "warn", `save: ${path} has state to save and no uid to key it under.`);
|
|
1895
1634
|
return state;
|
|
1896
1635
|
}
|
|
@@ -1900,7 +1639,9 @@ var Engine = class {
|
|
|
1900
1639
|
if (!root) return {
|
|
1901
1640
|
missing: Object.keys(state),
|
|
1902
1641
|
skipped: [],
|
|
1903
|
-
restored: 0
|
|
1642
|
+
restored: 0,
|
|
1643
|
+
expected: 0,
|
|
1644
|
+
freed: 0
|
|
1904
1645
|
};
|
|
1905
1646
|
return restoreBehaviors(root, state);
|
|
1906
1647
|
}
|
|
@@ -2399,6 +2140,19 @@ function widgetFromElement(el) {
|
|
|
2399
2140
|
let dragging = null;
|
|
2400
2141
|
/** Shared plumbing: mount into the parent HudLayer's anchor slot. */
|
|
2401
2142
|
var HudWidgetBase = class extends Node {
|
|
2143
|
+
/**
|
|
2144
|
+
* Wireable visibility: `noSave → Continue.hide`.
|
|
2145
|
+
*
|
|
2146
|
+
* `visible` is a prop, and a `connections[]` handler has to be a METHOD — the
|
|
2147
|
+
* same wall that kept a score off the screen until widgets grew `setText`. A
|
|
2148
|
+
* title screen greying out its own Continue button is the case that asked.
|
|
2149
|
+
*/
|
|
2150
|
+
show(..._args) {
|
|
2151
|
+
this.visible = true;
|
|
2152
|
+
}
|
|
2153
|
+
hide() {
|
|
2154
|
+
this.visible = false;
|
|
2155
|
+
}
|
|
2402
2156
|
static props = {
|
|
2403
2157
|
anchor: {
|
|
2404
2158
|
default: "topLeft",
|
|
@@ -3630,4 +3384,4 @@ function registerCoreNodes() {
|
|
|
3630
3384
|
registerNode(UiRenderScaleSelect);
|
|
3631
3385
|
}
|
|
3632
3386
|
//#endregion
|
|
3633
|
-
export {
|
|
3387
|
+
export { ROLLOFF_MODELS as A, AudioBuses as B, BASE_LOCALE as C, translationKey as D, suggestLocale as E, synthSfx as F, MusicManager as I, WebAudioMusicBackend as L, spatialPan as M, SFX_PRESETS as N, SfxEngine as O, SFX_PRESET_NAMES as P, crossfadeGains as R, LogManager as S, T_PREFIX as T, qualityCaps as _, UiSlider as a, readDeviceHints as b, UiButton as c, UiBanner as d, UiBar as f, Settings as g, Engine as h, UiSelect as i, spatialGain as j, isAudioContextAvailable as k, UiDialogue as l, AudioPlayer as m, UiImage as n, UiToggle as o, UiText as p, UiPanel as r, Timer as s, registerCoreNodes as t, HudLayer as u, qualityEnvironment as v, Localization as w, suggestQuality as x, qualityRendering as y, fadeGain as z };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as registerCoreNodes } from "./register-
|
|
1
|
+
import { M as Signal, f as Node, t as buildNodeJson } from "./loader-B2asghWa.js";
|
|
2
|
+
import { t as registerCoreNodes } from "./register-CGq-Hee8.js";
|
|
3
3
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
4
4
|
import { n as jsonEquals, t as jsonClone } from "./json-BLk7H2Qa.js";
|
|
5
5
|
import { l as registerNode } from "./registry-IyWCGe4q.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { D as translationKey } from "./register-CGq-Hee8.js";
|
|
2
2
|
//#region src/core/audit.ts
|
|
3
3
|
const ANIMATED_TYPES = new Set([
|
|
4
4
|
"Particles2D",
|
|
@@ -31,6 +31,21 @@ const LIGHT_TYPES = new Set([
|
|
|
31
31
|
"OmniLight3D",
|
|
32
32
|
"SpotLight3D"
|
|
33
33
|
]);
|
|
34
|
+
/**
|
|
35
|
+
* Built-ins that put something in a save. A save is keyed by node uid, so one
|
|
36
|
+
* of these on a node without one is state that silently goes nowhere: the save
|
|
37
|
+
* comes back `{}` and the load reports a clean bill, because "nothing was
|
|
38
|
+
* missing" is true of an empty save.
|
|
39
|
+
*
|
|
40
|
+
* A JSON walk cannot ask a behavior whether it serializes, so this is the list
|
|
41
|
+
* of the ones that do. Your own behavior with a `serialize()` is caught at
|
|
42
|
+
* runtime instead, by `captureState`.
|
|
43
|
+
*/
|
|
44
|
+
const SAVEABLE_SCRIPTS = new Set([
|
|
45
|
+
"Health",
|
|
46
|
+
"ScoreKeeper",
|
|
47
|
+
"Collector"
|
|
48
|
+
]);
|
|
34
49
|
/** Does the scene header light anything by itself (sky, hdri, ambient, preset)? */
|
|
35
50
|
function isLit(environment) {
|
|
36
51
|
if (!environment) return false;
|
|
@@ -47,9 +62,12 @@ function auditScene(scene) {
|
|
|
47
62
|
let currentCameras = 0;
|
|
48
63
|
let cameras = 0;
|
|
49
64
|
let lights = 0;
|
|
65
|
+
const needUid = [];
|
|
50
66
|
const walk = (node, path, insideHud, staticRoot) => {
|
|
51
67
|
const type = node.type ?? "";
|
|
52
68
|
const props = node.props ?? {};
|
|
69
|
+
const script = node.script?.name;
|
|
70
|
+
if (script && SAVEABLE_SCRIPTS.has(script) && !node.uid) needUid.push(`${path} (${script})`);
|
|
53
71
|
if (type === "Camera2D" || type === "Camera3D") {
|
|
54
72
|
cameras += 1;
|
|
55
73
|
if (props.current === true) currentCameras += 1;
|
|
@@ -66,6 +84,7 @@ function auditScene(scene) {
|
|
|
66
84
|
for (const child of node.children ?? []) walk(child, `${path}/${child.name ?? "?"}`, nextHud, nextStatic);
|
|
67
85
|
};
|
|
68
86
|
walk(root, root.name ?? "root", false, root.props?.static === true ? root.name ?? "root" : null);
|
|
87
|
+
if (needUid.length > 0) warnings.push(`these carry state a save keeps and have no uid to key it under, so the save comes back EMPTY and the load reports no problem: ${needUid.join(", ")}. Give each one a "uid" from newUid().`);
|
|
69
88
|
if (cameras > 0 && currentCameras === 0) warnings.push("no camera has \"current\": true — the screen renders from a default view, probably not what you framed.");
|
|
70
89
|
if (currentCameras > 1) warnings.push(`${currentCameras} cameras claim "current": true — only the first found wins.`);
|
|
71
90
|
if (scene.dimension === "3d" && lights === 0 && !isLit(scene.environment)) warnings.push("nothing lights this 3D scene — no light node, no environment sky/hdri, and no ambient intensity. It will render black. Add a DirectionalLight3D, or an \"environment\": { \"sky\": { \"type\": \"atmosphere\" } } / { \"preset\": \"...\" }.");
|
|
@@ -161,6 +161,6 @@ function newUid() {
|
|
|
161
161
|
//#endregion
|
|
162
162
|
//#region src/index.ts
|
|
163
163
|
/** Engine version. Kept in sync with package.json by the release pipeline. */
|
|
164
|
-
const VERSION = "0.
|
|
164
|
+
const VERSION = "0.51.0";
|
|
165
165
|
//#endregion
|
|
166
166
|
export { findPath as a, preloadUrls as i, newUid as n, gridFromRows as o, assetUrls as r, VERSION as t };
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { h as Engine } from "./register-
|
|
1
|
+
import { A as registerBehavior, n as loadScene, s as resolveViewport } from "./loader-B2asghWa.js";
|
|
2
|
+
import { h as Engine } from "./register-CGq-Hee8.js";
|
|
3
3
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
4
|
-
import { n as startRecording } from "./replay-
|
|
4
|
+
import { n as startRecording } from "./replay-BgKxGcXH.js";
|
|
5
5
|
import { n as jsonEquals, t as jsonClone } from "./json-BLk7H2Qa.js";
|
|
6
6
|
import { i as getNodeSchema, s as mergeStaticProps } from "./registry-IyWCGe4q.js";
|
|
7
|
-
import { n as registerGameplayBehaviors } from "./gameplay-
|
|
8
|
-
import { t as registerNodes2D } from "./register-
|
|
9
|
-
import { n as registerNodes3D, t as resolveEnvironmentHdri } from "./environment-presets-
|
|
10
|
-
import { t as registerNodesNet } from "./register-
|
|
7
|
+
import { n as registerGameplayBehaviors } from "./gameplay-bStgtZBV.js";
|
|
8
|
+
import { t as registerNodes2D } from "./register-B_BaaGx-.js";
|
|
9
|
+
import { n as registerNodes3D, t as resolveEnvironmentHdri } from "./environment-presets-C08pOC6H.js";
|
|
10
|
+
import { t as registerNodesNet } from "./register-CLVhzWcI.js";
|
|
11
11
|
import { Box3, Euler, Matrix4, PerspectiveCamera, Quaternion, Vector3 } from "three";
|
|
12
12
|
//#region src/test/framing.ts
|
|
13
13
|
/**
|
|
@@ -1621,10 +1621,10 @@ async function runScript(json, opts) {
|
|
|
1621
1621
|
engine.setScene(scene);
|
|
1622
1622
|
const physics = opts.physics ?? "auto";
|
|
1623
1623
|
if (physics === "2d" || physics === "auto" && scene.dimension === "2d") {
|
|
1624
|
-
const { enablePhysics2D } = await import("./physics-2d-
|
|
1624
|
+
const { enablePhysics2D } = await import("./physics-2d-Ccq2L9R0.js").then((n) => n.r);
|
|
1625
1625
|
await enablePhysics2D(engine);
|
|
1626
1626
|
} else if (physics === "3d" || physics === "auto" && scene.dimension === "3d") {
|
|
1627
|
-
const { enablePhysics3D } = await import("./physics-3d-
|
|
1627
|
+
const { enablePhysics3D } = await import("./physics-3d-B2c5KNYh.js").then((n) => n.r);
|
|
1628
1628
|
await enablePhysics3D(engine);
|
|
1629
1629
|
}
|
|
1630
1630
|
const failures = [];
|
|
@@ -1737,10 +1737,10 @@ async function createPlaySession(json, opts = {}) {
|
|
|
1737
1737
|
engine.setScene(scene);
|
|
1738
1738
|
const physics = opts.physics ?? "auto";
|
|
1739
1739
|
if (physics === "2d" || physics === "auto" && scene.dimension === "2d") {
|
|
1740
|
-
const { enablePhysics2D } = await import("./physics-2d-
|
|
1740
|
+
const { enablePhysics2D } = await import("./physics-2d-Ccq2L9R0.js").then((n) => n.r);
|
|
1741
1741
|
await enablePhysics2D(engine);
|
|
1742
1742
|
} else if (physics === "3d" || physics === "auto" && scene.dimension === "3d") {
|
|
1743
|
-
const { enablePhysics3D } = await import("./physics-3d-
|
|
1743
|
+
const { enablePhysics3D } = await import("./physics-3d-B2c5KNYh.js").then((n) => n.r);
|
|
1744
1744
|
await enablePhysics3D(engine);
|
|
1745
1745
|
}
|
|
1746
1746
|
const stepMs = 1e3 / (opts.fixedHz ?? 60);
|
package/dist/test.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { At as LogEntry, Dt as Node, P as Scene, b as Engine, n as BehaviorCtor } from "./behavior-rZNfzVbH.js";
|
|
2
2
|
import { c as JsonValue, i as SceneJson } from "./schema-CFeioQRE.js";
|
|
3
|
-
import { t as LoadSceneOptions } from "./loader-
|
|
4
|
-
import { l as auditScene, o as IncantoError, r as ReplayJson } from "./replay-
|
|
3
|
+
import { t as LoadSceneOptions } from "./loader-Dkbn56KC.js";
|
|
4
|
+
import { l as auditScene, o as IncantoError, r as ReplayJson } from "./replay-DIP2_as4.js";
|
|
5
5
|
|
|
6
6
|
//#region src/test/facing.d.ts
|
|
7
7
|
interface FacingOptions {
|
package/dist/test.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { r as auditScene } from "./replay-
|
|
2
|
-
import { _ as playtestText, a as registerAllNodes, c as ladderText, d as feelText, f as facingReport, g as playtest, h as findPlayer, i as findFloatingProps, l as ladderVerdict, m as failingReplays, n as createPlaySession, o as runScript, p as facingText, r as describeCapture, s as validateScene, t as captureScene, u as feelReport, v as describeFraming, y as framingText } from "./test-
|
|
1
|
+
import { r as auditScene } from "./replay-BgKxGcXH.js";
|
|
2
|
+
import { _ as playtestText, a as registerAllNodes, c as ladderText, d as feelText, f as facingReport, g as playtest, h as findPlayer, i as findFloatingProps, l as ladderVerdict, m as failingReplays, n as createPlaySession, o as runScript, p as facingText, r as describeCapture, s as validateScene, t as captureScene, u as feelReport, v as describeFraming, y as framingText } from "./test-D2gRpk5V.js";
|
|
3
3
|
export { auditScene, captureScene, createPlaySession, describeCapture, describeFraming, facingReport, facingText, failingReplays, feelReport, feelText, findFloatingProps, findPlayer, framingText, ladderText, ladderVerdict, playtest, playtestText, registerAllNodes, runScript, validateScene };
|
package/dist/vite.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as VERSION } from "./src-
|
|
1
|
+
import { t as VERSION } from "./src-C3UwzYXl.js";
|
|
2
2
|
import { n as diffSignatures } from "./frame-report-njybhZon.js";
|
|
3
|
-
import { s as validateScene } from "./test-
|
|
3
|
+
import { s as validateScene } from "./test-D2gRpk5V.js";
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { basename, dirname, join, normalize, relative, resolve, sep } from "node:path";
|
|
6
6
|
//#region src/vite/frame-endpoint.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e}from"./index-
|
|
1
|
+
import{n as e}from"./index-CUc1U7wm.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};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{i as e,r as t,t as n}from"./index-
|
|
1
|
+
import{i as e,r as t,t as n}from"./index-CUc1U7wm.js";function r(e,t,n,r=1){let i=Math.min(e.x0,e.x1)*r,a=Math.max(e.x0,e.x1)*r,o=Math.min(e.y0,e.y1)*r,s=Math.max(e.y0,e.y1)*r,c=Math.max(0,Math.min(Math.round(i),t-1)),l=Math.max(0,Math.min(Math.round(o),n-1));return{x:c,y:l,w:Math.max(1,Math.min(Math.round(a-i),t-c)),h:Math.max(1,Math.min(Math.round(s-o),n-l))}}function i(e,t,n,r){let{x:i,y:a,w:o,h:s}=r,c=new Uint8ClampedArray(o*s*4);for(let n=0;n<s;n++){let r=((a+n)*t+i)*4,s=n*o*4;c.set(e.subarray(r,r+o*4),s)}return{pixels:c,width:o,height:s}}function a(e){if(!e)return`// no scene loaded`;let t=e.source??e;return JSON.stringify(t,null,2)}async function o(e){let t=globalThis.navigator;if(!t?.clipboard?.writeText)return!1;try{return await t.clipboard.writeText(e),!0}catch{return!1}}async function s(e,t){let n=globalThis,r=n.navigator?.clipboard?.write;if(!r||!n.ImageData||!n.ClipboardItem)return!1;try{let i=e.createElement(`canvas`);i.width=t.width,i.height=t.height;let a=i.getContext(`2d`);if(!a)return!1;a.putImageData(new n.ImageData(t.pixels,t.width,t.height),0,0);let o=await new Promise(e=>i.toBlob(e,`image/png`));return o?(await r.call(n.navigator?.clipboard,[new n.ClipboardItem({"image/png":o})]),!0):!1}catch{return!1}}var c=[`nw`,`n`,`ne`,`e`,`se`,`s`,`sw`,`w`],l={nw:[0,0],n:[.5,0],ne:[1,0],e:[1,.5],se:[1,1],s:[.5,1],sw:[0,1],w:[0,.5]},u={nw:`nwse-resize`,se:`nwse-resize`,ne:`nesw-resize`,sw:`nesw-resize`,n:`ns-resize`,s:`ns-resize`,e:`ew-resize`,w:`ew-resize`},d=(e,t,n)=>Math.max(t,Math.min(n,e));function f(e){return{x:Math.min(e.x0,e.x1),y:Math.min(e.y0,e.y1),w:Math.abs(e.x1-e.x0),h:Math.abs(e.y1-e.y0)}}function p(e,t,n,r,i,a){let o=e.x,s=e.y,c=e.x+e.w,l=e.y+e.h;return t.includes(`w`)&&(o=d(o+n,0,i)),t.includes(`e`)&&(c=d(c+n,0,i)),t.includes(`n`)&&(s=d(s+r,0,a)),t.includes(`s`)&&(l=d(l+r,0,a)),{x:Math.min(o,c),y:Math.min(s,l),w:Math.max(1,Math.abs(c-o)),h:Math.max(1,Math.abs(l-s))}}function m(e,t,n,r,i){return{...e,x:d(e.x+t,0,Math.max(0,r-e.w)),y:d(e.y+n,0,Math.max(0,i-e.h))}}function h(e,t,n,r,i,a=8){let o=d(e.x+e.w/2-t/2,a,Math.max(a,r-t-a)),s=e.y+e.h+a,c=e.y-n-a;return{x:o,y:s+n<=i-a?s:c>=a?c:d(s,a,Math.max(a,i-n-a))}}function g(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 _(e,t,n,r,i,a=4){let o=Math.min(Math.max(0,e.x),Math.max(0,r-t)),s=e.y+e.h+a,c=e.y-n-a;return s+n<=i?{x:o,y:s}:c>=0?{x:o,y:c}:{x:o,y:Math.min(Math.max(0,s),Math.max(0,i-n))}}function v(e,t,n){let r=e=>{e.key===`Escape`&&n()},i=e=>{let r=e.target;if(r){for(let e of t)if(e&&(e===r||e.contains?.(r)))return;n()}};return e.addEventListener?.(`keydown`,r),e.addEventListener?.(`pointerdown`,i),()=>{e.removeEventListener?.(`keydown`,r),e.removeEventListener?.(`pointerdown`,i)}}function y(e,t){for(let[n,r]of Object.entries(t))e.style[n]=r}var b=`rgba(18, 20, 26, 0.92)`,x=`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`),y(this.el,{position:`absolute`,background:b,border:x,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 i=e.createElement(`div`);i.textContent=n,y(i,{padding:`6px 28px 6px 10px`,background:`rgba(255,255,255,0.07)`,cursor:`move`,userSelect:`none`,touchAction:`none`,fontWeight:`700`}),this.el.appendChild(i);let a=e.createElement(`div`);a.textContent=`×`,a.title=`close`,y(a,{position:`absolute`,top:`2px`,right:`8px`,cursor:`pointer`,fontSize:`16px`,lineHeight:`20px`,opacity:`0.7`}),a.addEventListener(`click`,()=>this.onClose()),this.el.appendChild(a),this.body=e.createElement(`div`),y(this.body,{flex:`1`,overflow:`auto`,padding:`8px 10px`}),this.el.appendChild(this.body);let o=e.createElement(`div`);o.textContent=`◢`,y(o,{position:`absolute`,right:`2px`,bottom:`0`,cursor:`nwse-resize`,opacity:`0.5`,userSelect:`none`,touchAction:`none`}),this.el.appendChild(o),this.wireDrag(i,(e,t)=>{this.x+=e,this.y+=t,this.layout()}),this.wireDrag(o,(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=g(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,y(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)}},w=4,T=`#6ee7dc`,E=`rgba(18,20,26,0.94)`,D=`12px ui-monospace, Menlo, monospace`;function O(e){let{doc:t,container:n}=e,r=t.createElement(`div`);y(r,{position:`absolute`,inset:`0`,cursor:`crosshair`,zIndex:`80`,pointerEvents:`auto`,userSelect:`none`,touchAction:`none`});let i=t.createElement(`div`);y(i,{position:`absolute`,inset:`0`,background:`rgba(10,14,20,0.35)`,pointerEvents:`none`}),r.appendChild(i);let a=t.createElement(`div`);y(a,{position:`absolute`,outline:`1px solid ${T}`,boxShadow:`0 0 0 9999px rgba(10,14,20,0.35)`,cursor:`grab`,display:`none`,pointerEvents:`auto`}),r.appendChild(a);let o=t.createElement(`div`);y(o,{position:`absolute`,padding:`2px 6px`,borderRadius:`4px`,background:E,color:T,font:D,pointerEvents:`none`,display:`none`,whiteSpace:`nowrap`}),r.appendChild(o);let s=t.createElement(`div`);s.textContent=`drag to select · Esc to cancel`,y(s,{position:`absolute`,top:`10px`,left:`50%`,transform:`translateX(-50%)`,padding:`4px 10px`,borderRadius:`6px`,background:E,color:`rgba(255,255,255,0.85)`,font:D,pointerEvents:`none`,whiteSpace:`nowrap`}),r.appendChild(s);let d=null,g=`idle`,_={x:0,y:0},v={x:0,y:0,w:0,h:0},b=()=>({w:n.clientWidth||1,h:n.clientHeight||1});for(let e of c){let n=t.createElement(`div`),[r,i]=l[e];y(n,{position:`absolute`,left:`${r*100}%`,top:`${i*100}%`,width:`10px`,height:`10px`,marginLeft:`-5px`,marginTop:`-5px`,borderRadius:`2px`,background:T,border:`1px solid rgba(10,14,20,0.75)`,cursor:u[e],pointerEvents:`auto`}),n.addEventListener(`pointerdown`,t=>{k(t,e)}),a.appendChild(n)}let x=t.createElement(`div`);y(x,{position:`absolute`,display:`none`,gap:`6px`,padding:`6px`,borderRadius:`8px`,background:E,border:`1px solid rgba(255,255,255,0.18)`,font:D,pointerEvents:`auto`,whiteSpace:`nowrap`,boxShadow:`0 4px 16px rgba(0,0,0,0.45)`});let S=(e,n,r)=>{let i=t.createElement(`div`);return i.textContent=e,y(i,{padding:`5px 10px`,borderRadius:`5px`,cursor:`pointer`,color:n?`#08121a`:`rgba(255,255,255,0.85)`,background:n?T:`rgba(255,255,255,0.08)`,userSelect:`none`}),i.addEventListener(`pointerdown`,e=>{C(e),r()}),i};x.appendChild(S(`Copy to clipboard`,!0,()=>N())),x.appendChild(S(`Cancel`,!1,()=>I())),r.appendChild(x);function C(e){e.preventDefault?.(),e.stopPropagation?.()}function O(e){let t=e,r=n.getBoundingClientRect?.()??{left:0,top:0};return{x:(t.clientX??0)-r.left,y:(t.clientY??0)-r.top}}function k(e,t){C(e),_=O(e),g=t,t===`create`?d={x:_.x,y:_.y,w:0,h:0}:d&&(v=d),y(x,{display:`none`}),y(a,{cursor:t===`move`?`grabbing`:`crosshair`});let n=e.pointerId;n!==void 0&&r.setPointerCapture?.(n),A()}function A(){if(!d){y(a,{display:`none`}),y(o,{display:`none`}),y(i,{display:`block`});return}y(i,{display:`none`}),y(a,{display:`block`,left:`${d.x}px`,top:`${d.y}px`,width:`${d.w}px`,height:`${d.h}px`});let t=e.scale();o.textContent=`${Math.round(d.w*t)}×${Math.round(d.h*t)}`;let n=d.y-24;y(o,{display:`block`,left:`${Math.max(2,d.x)}px`,top:`${n>=2?n:d.y+4}px`})}function j(){if(!d||d.w<w||d.h<w)return;y(x,{display:`flex`});let e=b(),t=x.offsetWidth||200,n=x.offsetHeight||36,r=h(d,t,n,e.w,e.h);y(x,{left:`${r.x}px`,top:`${r.y}px`})}r.addEventListener(`pointerdown`,e=>{k(e,`create`)}),a.addEventListener(`pointerdown`,e=>{k(e,`move`)}),r.addEventListener(`pointermove`,e=>{if(g===`idle`||!d)return;let t=O(e),n=b();if(g===`create`){let e=f({x0:_.x,y0:_.y,x1:t.x,y1:t.y});d={x:Math.max(0,Math.min(e.x,n.w)),y:Math.max(0,Math.min(e.y,n.h)),w:Math.min(e.w,n.w-Math.max(0,Math.min(e.x,n.w))),h:Math.min(e.h,n.h-Math.max(0,Math.min(e.y,n.h)))}}else d=g===`move`?m(v,t.x-_.x,t.y-_.y,n.w,n.h):p(v,g,t.x-_.x,t.y-_.y,n.w,n.h);A()});let M=()=>{g!==`idle`&&(g===`create`&&d&&(d.w<w||d.h<w)&&(d=null),g=`idle`,y(a,{cursor:`grab`}),s.textContent=d?`drag the handles or the middle · Enter to copy · Esc to cancel`:`drag to select · Esc to cancel`,A(),j())};r.addEventListener(`pointerup`,M),r.addEventListener(`pointercancel`,M);function N(){let t=d;I(),t&&e.onCopy(t)}let P=e=>{let t=e.key;t===`Escape`?I():t===`Enter`&&d&&N()};t.addEventListener?.(`keydown`,P);let F=!1;function I(){F||(F=!0,t.removeEventListener?.(`keydown`,P),r.remove(),e.onClose())}return n.appendChild(r),{element:r,close:I}}var k=96,A=200,j=8192;function M(e){return Array.isArray(e)?`Array(${e.length})`:`Object(${Object.keys(e).length} keys)`}function N(e,t){if(Array.isArray(e)&&e.some(e=>typeof e==`object`&&!!e)){let t=e.slice(0,A).map(e=>JSON.stringify(e)),n=e.length-A;return t.join(`
|
|
2
2
|
`)+(n>0?`\n… ${n} more`:``)}return t.length>j?`${t.slice(0,j)} … (${t.length-j} more chars)`:t}function P(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}var F=2,I=4;function L(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 B(e,r,n,t.statsSource,t.actions,t.frameSource)}var R=300,z=[`log`,`info`,`warn`,`error`,`debug`],B=class{engine;container;doc;statsSource;actions;frameSource;panels=new Map;cleanups=[];menuButton;dropdown=null;dismissDropdown=null;menuPos={x:8,y:8};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;resumeScale=1;timeEls=null;hovering=!1;constructor(e,t,n,r,i=[],a){this.engine=e,this.container=t,this.doc=n,this.statsSource=r,this.actions=i,this.frameSource=a,this.menuButton=n.createElement(`div`),this.menuButton.textContent=`☰ debug`,y(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.bindMenuDrag(),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(U).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(),this.syncTime())}))}isOpen(e){return e===`copyScene`||e===`captureRegion`?!1: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)){e===`explorer`&&this.renderExplorer(),e===`inspector`&&this.renderInspector(),e===`logs`&&this.renderLogs(),e===`time`&&this.renderTime();return}let t=new C(this.doc,this.container,{explorer:`Explorer`,inspector:`Inspector`,logs:`Logs`,time:`Time`}[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},time:{x:556,y:44,w:260,h:150}}[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(),e===`time`&&this.renderTime()}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===`copyScene`){this.copyScene();return}if(e===`captureRegion`){this.captureRegion();return}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 z){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(U).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.closeDropdown(),this.menuButton.remove()}menuLabel(e,t){let n=e===`colliders`&&this.colliderMode!==`off`?` · ${this.colliderMode}`:``;return`${this.isOpen(e)?`✓ `:``}${t}${n}`}bindMenuDrag(){let e=null,t=!1;this.menuButton.addEventListener(`pointerdown`,n=>{let r=n;e={x:r.clientX??0,y:r.clientY??0,bx:this.menuPos.x,by:this.menuPos.y},t=!1,r.pointerId!==void 0&&this.menuButton.setPointerCapture?.(r.pointerId)}),this.menuButton.addEventListener(`pointermove`,n=>{if(!e)return;let r=n,i=(r.clientX??0)-e.x,a=(r.clientY??0)-e.y;!t&&Math.hypot(i,a)<I||(t||(t=!0,this.closeDropdown(),y(this.menuButton,{cursor:`grabbing`})),this.moveMenuButton(e.bx+i,e.by+a))});let n=()=>{if(!e)return;let n=t;e=null,t=!1,y(this.menuButton,{cursor:`pointer`}),n||this.toggleDropdown()};this.menuButton.addEventListener(`pointerup`,n),this.menuButton.addEventListener(`pointercancel`,n)}moveMenuButton(e,t){let n=this.menuButton,r=g(e,t,n.offsetWidth??0,n.offsetHeight??0,this.container.clientWidth||0,this.container.clientHeight||0,0,0);this.menuPos={x:r.x,y:r.y},y(this.menuButton,{left:`${r.x}px`,top:`${r.y}px`})}closeDropdown(){this.dismissDropdown?.(),this.dismissDropdown=null,this.dropdown?.remove(),this.dropdown=null}toggleDropdown(){if(this.dropdown){this.closeDropdown();return}let e=this.doc.createElement(`div`);y(e,{position:`absolute`,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`],[`time`,`Time`],[`copyScene`,`Copy scene JSON`],[`captureRegion`,`Capture region`],[`stats`,`Stats`],[`colliders`,`Colliders`]]){let r=this.doc.createElement(`div`);r.textContent=this.menuLabel(t,n),y(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.closeDropdown()}),e.appendChild(r)}for(let t of this.actions){let n=this.doc.createElement(`div`);n.textContent=t.label,y(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.closeDropdown(),t.run()}),e.appendChild(n)}this.container.appendChild(e),this.dropdown=e;let t=e,n=this.menuButton,r=_({x:this.menuPos.x,y:this.menuPos.y,w:n.offsetWidth??0,h:n.offsetHeight??26},t.offsetWidth??0,t.offsetHeight??0,this.container.clientWidth||0,this.container.clientHeight||0);y(e,{left:`${r.x}px`,top:`${r.y}px`}),this.dismissDropdown=v(this.doc,[e,this.menuButton],()=>this.closeDropdown())}openStatsChip(){if(this.statsChip)return;let e=this.doc.createElement(`div`);y(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 ${H(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;V(e.body);let n=this.engine.scene?.root;if(!n){e.body.scrollTop=t;return}let r=(e,t)=>{let n=e.constructor,i=e.children.length>0,a=this.collapsedFlags.get(e)===!0,o=this.doc.createElement(`div`);y(o,{display:`flex`,alignItems:`center`,cursor:`pointer`,padding:`1px 2px`,borderRadius:`3px`,background:e===this.selected?`rgba(110,160,255,0.25)`:`transparent`});let s=this.doc.createElement(`span`);s.textContent=i?a?`▸`:`▾`:`·`,y(s,{width:`14px`,flex:`none`,textAlign:`center`,opacity:i?`0.85`:`0.25`,userSelect:`none`}),i&&s.addEventListener(`click`,t=>{t.stopPropagation?.(),this.collapsedFlags.set(e,!a),this.renderExplorer()}),o.appendChild(s);let c=this.doc.createElement(`span`);c.textContent=e.name,y(c,{whiteSpace:`nowrap`}),o.appendChild(c);let l=this.doc.createElement(`span`);if(l.textContent=` ${n.typeName}`,y(l,{opacity:`0.45`,whiteSpace:`nowrap`,fontSize:`10px`}),o.appendChild(l),i&&a){let t=this.doc.createElement(`span`);t.textContent=` (${e.children.length})`,y(t,{opacity:`0.35`,fontSize:`10px`}),o.appendChild(t)}if(o.addEventListener(`click`,()=>{this.selected=e,this.engine.debugSelection=e,this.applyColliderScope(),this.open(`inspector`),this.renderExplorer(),this.renderInspector()}),t.appendChild(o),i&&!a){let n=this.doc.createElement(`div`);y(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;V(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`,y(t,{opacity:`0.6`}),e.body.appendChild(t);return}let i=r.constructor,a=this.doc.createElement(`div`);if(a.textContent=`${r.getPath()} · ${i.typeName}${r.uid?` · ${r.uid}`:``}`,y(a,{fontWeight:`700`,marginBottom:`6px`,whiteSpace:`pre-wrap`}),e.body.appendChild(a),r.groups.size>0){let t=this.doc.createElement(`div`);t.textContent=`groups: ${[...r.groups].join(`, `)}`,y(t,{opacity:`0.7`,marginBottom:`6px`}),e.body.appendChild(t)}let o=t(i),s=r;for(let t of Object.keys(o)){let n=o[t];this.renderValueRow(e.body,r,t,{read:()=>s[t],write:e=>{s[t]=e},options:n?.options,variants:n?.variants})}e.body.scrollTop=n}renderValueRow(t,n,r,i){let a=i.read(),o=i.options,s={get[r](){return i.read()},set[r](e){i.write(e)}},c=this.doc.createElement(`div`);y(c,{display:`flex`,gap:`6px`,alignItems:`center`,margin:`2px 0`});let l=this.doc.createElement(`div`);l.textContent=r,y(l,{minWidth:`84px`,opacity:`0.75`}),c.appendChild(l);let u=(e,t)=>{let i=this.doc.createElement(`input`);return i.type=`number`,i.value=String(e),y(i,G(`70px`)),this.trackEditing(i),i.addEventListener(`change`,()=>{let e=Number(i.value);Number.isFinite(e)?t(e):i.value=String(n[r])}),i};if(typeof a==`number`)c.appendChild(u(a,e=>{s[r]=e}));else if(typeof a==`boolean`){let e=this.doc.createElement(`input`);e.type=`checkbox`,e.checked=a,this.trackEditing(e),e.addEventListener(`change`,()=>{s[r]=e.checked}),c.appendChild(e)}else if(typeof a==`string`&&o&&o.length>0){let e=this.doc.createElement(`select`);for(let t of o.includes(a)?o:[a,...o]){let n=this.doc.createElement(`option`);n.value=t,n.textContent=t,t===a&&(n.selected=!0),e.appendChild(n)}e.value=a,this.trackEditing(e),e.addEventListener(`change`,()=>{s[r]=e.value}),y(e,G(`110px`)),c.appendChild(e)}else if(typeof a==`string`){let e=this.doc.createElement(`input`);e.type=`text`,e.value=a,this.trackEditing(e),y(e,G(`140px`)),e.addEventListener(`change`,()=>{s[r]=e.value}),c.appendChild(e)}else if(Array.isArray(a)&&a.length<=8&&a.every(e=>typeof e==`number`))for(let e=0;e<a.length;e++)c.appendChild(u(a[e],t=>{let n=[...s[r]];n[e]=t,s[r]=n}));else if(P(a)){let o=this.doc.createElement(`div`);y(o,{marginLeft:`10px`,paddingLeft:`8px`,borderLeft:`1px solid rgba(255,255,255,0.14)`});let s=i.variants?.tag,l=Object.keys(a);s&&!l.includes(s)&&l.unshift(s);let u=i.path??r;for(let t of l){let r=t===s;this.renderValueRow(o,n,t,{path:`${u}.${t}`,read:()=>i.read()[t]??(r?``:null),write:n=>{if(r&&i.variants){let t=i.variants.byTag[String(n)];if(t!==void 0){i.write(e(t));return}}i.write({...i.read(),[t]:n})},options:r&&i.variants?Object.keys(i.variants.byTag):void 0})}t.appendChild(c),t.appendChild(o);return}else{let o=JSON.stringify(e(a));if(o.length<=k){let e=this.doc.createElement(`div`);e.textContent=o,y(e,{opacity:`0.65`,whiteSpace:`pre-wrap`,wordBreak:`break-all`}),c.appendChild(e)}else{let e=i.path??r,s=this.detailOpen.get(n)?.has(e)??!1,l=this.doc.createElement(`div`);if(l.textContent=`${s?`▾`:`▸`} ${M(a)}`,y(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)),s?t.delete(e):t.add(e),this.renderInspector()}),c.appendChild(l),t.appendChild(c),s){let e=this.doc.createElement(`div`);e.textContent=N(a,o),y(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(c)}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>R&&this.logRows.splice(0,this.logRows.length-R),this.renderLogs()}async copyScene(){let e=await o(a(this.engine.scene??null));return this.toast(e?`scene JSON copied`:`could not reach the clipboard`),e}captureRegion(){this.capture||=(this.captureScale=globalThis.devicePixelRatio??1,this.frameSource?.().then(e=>{this.captureScale=e.width/Math.max(1,this.container.clientWidth||e.width)}).catch(()=>{}),O({doc:this.doc,container:this.container,scale:()=>this.captureScale,onCopy:e=>void this.copyRegion({x0:e.x,y0:e.y,x1:e.x+e.w,y1:e.y+e.h}),onClose:()=>{this.capture=null}}))}capture=null;captureScale=1;async copyRegion(e){let t=this.frameSource;if(!t)return this.toast(`no renderer to capture from`),!1;try{let n=await t(),a=n.width/Math.max(1,this.container.clientWidth||n.width),o=r(e,n.width,n.height,a),c=i(n.pixels,n.width,n.height,o),l=await s(this.doc,c);return this.toast(l?`copied ${c.width}×${c.height}`:`could not copy the image`),l}catch(e){return this.toast(`capture failed: ${e instanceof Error?e.message:String(e)}`),!1}}toast(e){let t=this.doc.createElement(`div`);t.textContent=e,y(t,{position:`absolute`,bottom:`16px`,left:`50%`,transform:`translateX(-50%)`,padding:`6px 12px`,borderRadius:`6px`,background:`rgba(18,20,26,0.92)`,color:`rgba(255,255,255,0.9)`,font:`12px ui-monospace, Menlo, monospace`,zIndex:`90`,pointerEvents:`none`}),this.container.appendChild(t),setTimeout(()=>t.remove(),1800)}setTimeScale(e){Number.isFinite(e)&&(this.engine.timeScale=Math.max(0,e),this.syncTime())}setPaused(e){e?(this.engine.timeScale>0&&(this.resumeScale=this.engine.timeScale),this.engine.timeScale=0):this.engine.timeScale=this.resumeScale>0?this.resumeScale:1,this.syncTime()}get paused(){return this.engine.timeScale===0}nextFrame(){this.paused||this.setPaused(!0),this.engine.step(),this.syncTime()}syncTime(){let e=this.timeEls;if(!e)return;let t=this.engine.timeScale,n=String(Math.round(t*1e3)/1e3);this.editing===0&&(e.slider.value=String(Math.min(t,F)),e.box.value=n),e.readout.textContent=`timeScale ${n}${this.paused?` · paused`:``}`,e.pause.textContent=this.paused?`▶ Resume`:`⏸ Pause`}renderTime(){let e=this.panels.get(`time`);if(!e)return;V(e.body),this.timeEls=null;let t=this.doc.createElement(`div`);y(t,{marginBottom:`6px`,opacity:`0.85`}),e.body.appendChild(t);let n=this.doc.createElement(`input`);n.type=`range`,n.min=`0`,n.max=String(F),n.step=`0.05`,n.value=String(Math.min(this.engine.timeScale,F)),y(n,{width:`100%`,marginBottom:`6px`}),this.trackEditing(n),n.addEventListener(`input`,()=>this.setTimeScale(Number(n.value))),e.body.appendChild(n);let r=this.doc.createElement(`div`);y(r,{display:`flex`,gap:`6px`,alignItems:`center`,marginBottom:`8px`});let i=this.doc.createElement(`input`);i.type=`number`,i.min=`0`,i.step=`0.05`,i.value=String(this.engine.timeScale),y(i,G(`72px`)),this.trackEditing(i),i.addEventListener(`change`,()=>{let e=Number(i.value);Number.isFinite(e)&&this.setTimeScale(e),this.syncTimeAfterEdit()}),r.appendChild(i);for(let e of[.25,.5,1,2]){let t=this.doc.createElement(`div`);t.textContent=`${e}×`,y(t,{cursor:`pointer`,opacity:`0.7`,padding:`2px 4px`}),t.addEventListener(`click`,()=>this.setTimeScale(e)),r.appendChild(t)}e.body.appendChild(r);let a=this.doc.createElement(`div`);y(a,{display:`flex`,gap:`8px`});let o=this.doc.createElement(`div`);y(o,W()),o.addEventListener(`click`,()=>this.setPaused(!this.paused)),a.appendChild(o);let s=this.doc.createElement(`div`);s.textContent=`⏭ Next frame`,s.title=`Pauses, then advances one fixed step`,y(s,W()),s.addEventListener(`click`,()=>this.nextFrame()),a.appendChild(s),e.body.appendChild(a),this.timeEls={slider:n,box:i,readout:t,pause:o},this.syncTime()}syncTimeAfterEdit(){let e=this.timeEls;if(!e)return;let t=this.engine.timeScale;e.slider.value=String(Math.min(t,F)),e.box.value=String(Math.round(t*1e3)/1e3)}renderLogs(){let e=this.panels.get(`logs`);if(!e)return;V(e.body);let t=this.doc.createElement(`div`);y(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}`,y(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`,y(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}`,y(n,{color:r[t.level],whiteSpace:`pre-wrap`}),e.body.appendChild(n)}}};function V(e){for(let t of[...e.children])t.remove()}function H(e){return e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}k`:String(e)}function U(e){if(typeof e==`string`)return e;if(e instanceof Error){let t=e.stack?.split(`
|
|
3
3
|
`)[1]?.trim();return`${e.name}: ${e.message}${t?` (${t})`:``}`}try{let t=JSON.stringify(e);return t===`{}`||t===void 0?String(e):t}catch{return String(e)}}function W(){return{background:`rgba(255,255,255,0.08)`,border:`1px solid rgba(255,255,255,0.2)`,borderRadius:`4px`,padding:`4px 8px`,cursor:`pointer`,userSelect:`none`}}function G(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{L as attachDebugOverlay};
|