incanto 0.30.0 → 0.32.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/bin/incanto-play.mjs +7 -2
- package/dist/2d.d.ts +12 -5
- package/dist/2d.js +3 -3
- package/dist/3d.d.ts +67 -6
- package/dist/3d.js +4 -4
- package/dist/{audit-C6rMyict.js → audit-C4kmDK0o.js} +16 -0
- package/dist/{behavior-BAc0erXF.d.ts → behavior-D_jMpFh8.d.ts} +117 -55
- package/dist/{create-game-5z_QVtLx.js → create-game-bKHgHcsZ.js} +19 -10
- package/dist/{create-game-DuBTv2zI.js → create-game-gXI7PYl0.js} +61 -46
- package/dist/debug.d.ts +1 -1
- package/dist/debug.js +7 -2
- package/dist/{duplicate-BgtFrFo4.js → duplicate-KRPtUtzl.js} +1 -1
- package/dist/{editor-switch-B0wB_DSr.d.ts → editor-switch-BJb-CWfA.d.ts} +12 -1
- package/dist/{register-DJ0SByQg.js → environment-presets--DigHNg4.js} +42 -11
- package/dist/{gameplay-Cfr6aFZ1.js → gameplay-BftxM_It.js} +2 -2
- package/dist/gameplay.d.ts +1 -1
- package/dist/gameplay.js +1 -1
- package/dist/index.d.ts +13 -3
- package/dist/index.js +7 -7
- package/dist/{loader-B242FF6N.d.ts → loader-BYBrqTxP.d.ts} +1 -1
- package/dist/{loader-BZqOKfI2.js → loader-BlaRQGaA.js} +482 -6
- package/dist/net.d.ts +1 -1
- package/dist/net.js +3 -3
- package/dist/{pathfinding-BwD974Ss.d.ts → pathfinding-BwhqPD3i.d.ts} +1 -1
- package/dist/{physics-2d-3kOQCtgd.js → physics-2d-D9wquBvK.js} +4 -3
- package/dist/{physics-3d-CeRH-Ff_.js → physics-3d-CnPygVGo.js} +5 -4
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{register-MelqEdza.js → register-CUY284Is.js} +3 -3
- package/dist/{register-BTg0EM7s.js → register-Dzkd6-os.js} +56 -380
- package/dist/{register-DWcWq4QG.js → register-tkR_8tWg.js} +4 -4
- package/dist/{registry-BVJ2HbCn.js → registry-CJdGpT2V.js} +6 -2
- package/dist/test-WwRIlXsK.js +642 -0
- package/dist/test.d.ts +55 -8
- package/dist/test.js +3 -364
- package/dist/{touch-031PxtCR.js → touch-BoNg_MnF.js} +2 -1
- package/dist/vite.js +1 -1
- package/editor/assets/{agent8-CAp0i5qn.js → agent8-C5k1nTCH.js} +1 -1
- package/editor/assets/{debug-BoEYfbqK.js → debug-BT_0mjk-.js} +3 -2
- package/editor/assets/{index-BO6WU8by.js → index-gfyrByWw.js} +90 -90
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/skills/README.md +14 -2
- package/skills/incanto-building-2d-games.md +16 -0
- package/skills/incanto-building-3d-games.md +16 -0
- package/skills/incanto-verifying-your-game.md +118 -0
- 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
package/dist/test.d.ts
CHANGED
|
@@ -1,8 +1,52 @@
|
|
|
1
|
+
import { $ as LogEntry, X as Node, n as BehaviorCtor, v as Engine, w as Scene } from "./behavior-D_jMpFh8.js";
|
|
1
2
|
import { c as JsonValue, i as SceneJson } from "./schema-CcoWb32N.js";
|
|
2
|
-
import {
|
|
3
|
-
import { t as LoadSceneOptions } from "./loader-B242FF6N.js";
|
|
3
|
+
import { t as LoadSceneOptions } from "./loader-BYBrqTxP.js";
|
|
4
4
|
import { i as auditScene, t as IncantoError } from "./errors-1dXlIwoR.js";
|
|
5
5
|
|
|
6
|
+
//#region src/test/framing.d.ts
|
|
7
|
+
/** Where a node's origin sits relative to the current camera. */
|
|
8
|
+
type Where = "onScreen" | "offscreen" | "behind";
|
|
9
|
+
interface FramingEntry {
|
|
10
|
+
path: string;
|
|
11
|
+
type: string;
|
|
12
|
+
/** World-space origin. */
|
|
13
|
+
at: [number, number, number];
|
|
14
|
+
where: Where;
|
|
15
|
+
/** Normalised device coords, x/y in −1..1 (right/up). Absent when `behind`. */
|
|
16
|
+
screen?: [number, number];
|
|
17
|
+
/** Metres from the camera's eye. */
|
|
18
|
+
distance: number;
|
|
19
|
+
}
|
|
20
|
+
interface FramingReport {
|
|
21
|
+
/** Null when the scene declares no 3D camera — nothing frames anything. */
|
|
22
|
+
camera: {
|
|
23
|
+
path: string;
|
|
24
|
+
at: [number, number, number]; /** Unit vector the camera points along. */
|
|
25
|
+
forward: [number, number, number];
|
|
26
|
+
fovDeg: number;
|
|
27
|
+
} | null;
|
|
28
|
+
entries: FramingEntry[];
|
|
29
|
+
/**
|
|
30
|
+
* What lights the scene: light nodes, plus the environment header when it
|
|
31
|
+
* carries a sky/hdri/preset/ambient. Empty means the render is BLACK.
|
|
32
|
+
*/
|
|
33
|
+
lights: string[];
|
|
34
|
+
/** Pairs whose declared colliders intersect — "the gem is inside the platform". */
|
|
35
|
+
overlaps: Array<{
|
|
36
|
+
a: string;
|
|
37
|
+
b: string;
|
|
38
|
+
}>;
|
|
39
|
+
}
|
|
40
|
+
interface FramingOptions {
|
|
41
|
+
/** Viewport aspect. Default 16/9 — only affects the horizontal edge test. */
|
|
42
|
+
aspect?: number;
|
|
43
|
+
/** Report only these node types (default: everything that draws). */
|
|
44
|
+
types?: readonly string[];
|
|
45
|
+
}
|
|
46
|
+
declare function describeFraming(scene: Scene, opts?: FramingOptions): FramingReport;
|
|
47
|
+
/** The report as something to read in a terminal. */
|
|
48
|
+
declare function framingText(report: FramingReport): string;
|
|
49
|
+
//#endregion
|
|
6
50
|
//#region src/test/index.d.ts
|
|
7
51
|
/** One thing that is not standing where it should be. */
|
|
8
52
|
interface GroundingIssue {
|
|
@@ -74,11 +118,6 @@ interface ValidationResult {
|
|
|
74
118
|
/** The hard load error, when ok is false — `details` is machine-readable. */
|
|
75
119
|
error?: IncantoError;
|
|
76
120
|
}
|
|
77
|
-
/**
|
|
78
|
-
* Run every hard load-time check headlessly. Unregistered behaviors are
|
|
79
|
-
* stubbed by default (structure-only validation, no TypeScript needed) —
|
|
80
|
-
* pass the real classes via `behaviors` to validate script props too.
|
|
81
|
-
*/
|
|
82
121
|
declare function validateScene(json: unknown, opts?: ValidateSceneOptions): ValidationResult;
|
|
83
122
|
interface RunContext {
|
|
84
123
|
engine: Engine;
|
|
@@ -169,6 +208,14 @@ interface PlaySession {
|
|
|
169
208
|
capture(): SceneJson;
|
|
170
209
|
/** Grep-able one-line-per-node text view of the current state. */
|
|
171
210
|
describe(): string;
|
|
211
|
+
/**
|
|
212
|
+
* What the camera can SEE right now: on screen / off screen / behind, screen
|
|
213
|
+
* position, what lights the scene, and colliders that intersect. No GPU.
|
|
214
|
+
*
|
|
215
|
+
* `describe()` tells you a gem is at [4, 1, -2]. This tells you whether you
|
|
216
|
+
* can see it — which is the question you actually had.
|
|
217
|
+
*/
|
|
218
|
+
framing(): string;
|
|
172
219
|
/** Engine log entries since the last drain. */
|
|
173
220
|
drainLogs(): LogEntry[];
|
|
174
221
|
dispose(): void;
|
|
@@ -181,4 +228,4 @@ interface PlaySession {
|
|
|
181
228
|
*/
|
|
182
229
|
declare function createPlaySession(json: unknown, opts?: PlaySessionOptions): Promise<PlaySession>;
|
|
183
230
|
//#endregion
|
|
184
|
-
export { GroundingIssue, GroundingOptions, NodeCapture, PlaySession, PlaySessionOptions, RunContext, RunFailure, RunResult, RunScriptOptions, SceneCapture, ScriptStep, ValidateSceneOptions, ValidationResult, auditScene, captureScene, createPlaySession, describeCapture, findFloatingProps, registerAllNodes, runScript, validateScene };
|
|
231
|
+
export { type FramingEntry, type FramingOptions, type FramingReport, GroundingIssue, GroundingOptions, NodeCapture, PlaySession, PlaySessionOptions, RunContext, RunFailure, RunResult, RunScriptOptions, SceneCapture, ScriptStep, ValidateSceneOptions, ValidationResult, type Where, auditScene, captureScene, createPlaySession, describeCapture, describeFraming, findFloatingProps, framingText, registerAllNodes, runScript, validateScene };
|
package/dist/test.js
CHANGED
|
@@ -1,364 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import { t as auditScene } from "./audit-C6rMyict.js";
|
|
5
|
-
import { n as jsonEquals, t as jsonClone } from "./json-BLk7H2Qa.js";
|
|
6
|
-
import { i as getNodeSchema, s as mergeStaticProps } from "./registry-BVJ2HbCn.js";
|
|
7
|
-
import { n as registerGameplayBehaviors } from "./gameplay-Cfr6aFZ1.js";
|
|
8
|
-
import { t as registerNodes2D } from "./register-DWcWq4QG.js";
|
|
9
|
-
import { t as registerNodes3D } from "./register-DJ0SByQg.js";
|
|
10
|
-
import { t as registerNodesNet } from "./register-MelqEdza.js";
|
|
11
|
-
//#region src/test/index.ts
|
|
12
|
-
/**
|
|
13
|
-
* incanto/test — the browserless verification harness.
|
|
14
|
-
*
|
|
15
|
-
* The agent loop is "author → VERIFY → fix", and production environments
|
|
16
|
-
* (e.g. the agent8 VM) have no browser. This module makes the scene state
|
|
17
|
-
* itself the screenshot:
|
|
18
|
-
*
|
|
19
|
-
* - `validateScene(json)` — every hard load error, without a browser
|
|
20
|
-
* - `runScript(json, ...)` — scripted play by ACTION intent at a fixed
|
|
21
|
-
* timestep, with inline assertions and periodic snapshots
|
|
22
|
-
* - `captureScene(scene)` / `describeCapture(capture)` — a structural,
|
|
23
|
-
* grep-able "text screenshot" of the live tree
|
|
24
|
-
*
|
|
25
|
-
* Deterministic by construction: fixed timestep, seeded `engine.rng`,
|
|
26
|
-
* no requestAnimationFrame.
|
|
27
|
-
*/
|
|
28
|
-
/** Node types whose whole job is to sit on the ground. Characters, water,
|
|
29
|
-
* clouds, birds and lights are all legitimately off it. */
|
|
30
|
-
const GROUNDED_TYPES = ["MeshInstance3D", "InstancedMesh3D"];
|
|
31
|
-
/** Under one of these, the BODY owns where the thing stands — a character's
|
|
32
|
-
* skin hangs at whatever offset its rig wants, and is not a floating prop. */
|
|
33
|
-
const BODY_OWNED = new Set([
|
|
34
|
-
"CharacterBody3D",
|
|
35
|
-
"RigidBody3D",
|
|
36
|
-
"StaticBody3D",
|
|
37
|
-
"AreaBody3D"
|
|
38
|
-
]);
|
|
39
|
-
/**
|
|
40
|
-
* Every prop that is floating or buried.
|
|
41
|
-
*
|
|
42
|
-
* THE most repeated mistake in scene authoring, by humans and agents alike, is
|
|
43
|
-
* a hand-computed Y: correct when it is written and wrong the moment anything
|
|
44
|
-
* reshapes the ground under it — a river carving its bed, a new seed, a
|
|
45
|
-
* generator that mirrors a carve slightly differently. This is the check that
|
|
46
|
-
* catches it, and `snapToGround` is the fix that stops it happening.
|
|
47
|
-
*
|
|
48
|
-
* Anything explicitly marked `snapToGround: false` is skipped: that is the
|
|
49
|
-
* author saying "yes, this hangs in the air", which a bridge deck, a lantern
|
|
50
|
-
* and a cloud all legitimately do.
|
|
51
|
-
*
|
|
52
|
-
* ```ts
|
|
53
|
-
* const off = findFloatingProps(gameJson);
|
|
54
|
-
* ok(`nothing floats (${off.length})`, off.length === 0);
|
|
55
|
-
* ```
|
|
56
|
-
*/
|
|
57
|
-
function findFloatingProps(json, opts = {}) {
|
|
58
|
-
registerAllNodes();
|
|
59
|
-
const tolerance = opts.tolerance ?? .1;
|
|
60
|
-
const types = new Set(opts.types ?? GROUNDED_TYPES);
|
|
61
|
-
let scene;
|
|
62
|
-
try {
|
|
63
|
-
scene = loadScene(json);
|
|
64
|
-
} catch {
|
|
65
|
-
return [];
|
|
66
|
-
}
|
|
67
|
-
let ground = null;
|
|
68
|
-
const findGround = (node) => {
|
|
69
|
-
if (!ground && node.constructor.typeName === "Terrain3D" && typeof node.heightAt === "function") ground = node;
|
|
70
|
-
for (const child of node.children) findGround(child);
|
|
71
|
-
};
|
|
72
|
-
findGround(scene.root);
|
|
73
|
-
if (!ground) return [];
|
|
74
|
-
const out = [];
|
|
75
|
-
const walk = (node, path, ox, oy, oz, yaw, bodyOwned) => {
|
|
76
|
-
const n = node;
|
|
77
|
-
let wx = ox;
|
|
78
|
-
let wy = oy;
|
|
79
|
-
let wz = oz;
|
|
80
|
-
let ownYaw = yaw;
|
|
81
|
-
if (Array.isArray(n.position)) {
|
|
82
|
-
const c = Math.cos(yaw);
|
|
83
|
-
const s = Math.sin(yaw);
|
|
84
|
-
const lx = n.position[0] ?? 0;
|
|
85
|
-
const lz = n.position[2] ?? 0;
|
|
86
|
-
wx = ox + lx * c + lz * s;
|
|
87
|
-
wz = oz - lx * s + lz * c;
|
|
88
|
-
wy = oy + (n.position[1] ?? 0);
|
|
89
|
-
ownYaw = yaw + (n.rotation?.[1] ?? 0) * Math.PI / 180;
|
|
90
|
-
}
|
|
91
|
-
const type = node.constructor.typeName ?? "";
|
|
92
|
-
const soil = ground;
|
|
93
|
-
if (types.has(type) && n.visible !== false && n.snapToGround !== false && !bodyOwned) {
|
|
94
|
-
const rows = node.transforms;
|
|
95
|
-
if (Array.isArray(rows) && rows.length > 0) {
|
|
96
|
-
let worst = null;
|
|
97
|
-
for (const row of rows) {
|
|
98
|
-
const scale = row[4] ?? 1;
|
|
99
|
-
const half = (n.size?.[1] ?? 0) * scale / 2;
|
|
100
|
-
const gap = wy + (row[1] ?? 0) - half - soil.heightAt(wx + (row[0] ?? 0), wz + (row[2] ?? 0));
|
|
101
|
-
if (Math.abs(gap) > tolerance && (!worst || Math.abs(gap) > Math.abs(worst.gap))) worst = {
|
|
102
|
-
path,
|
|
103
|
-
gap: Math.round(gap * 100) / 100
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
if (worst) out.push(worst);
|
|
107
|
-
} else {
|
|
108
|
-
const half = (n.size?.[1] ?? 0) / 2;
|
|
109
|
-
const gap = wy - half - soil.heightAt(wx, wz);
|
|
110
|
-
if (Math.abs(gap) > tolerance) out.push({
|
|
111
|
-
path,
|
|
112
|
-
gap: Math.round(gap * 100) / 100
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
const childBodyOwned = bodyOwned || BODY_OWNED.has(type);
|
|
117
|
-
for (const child of node.children) walk(child, `${path}/${child.name}`, wx, wy, wz, ownYaw, childBodyOwned);
|
|
118
|
-
};
|
|
119
|
-
walk(scene.root, `/${scene.root.name}`, 0, 0, 0, 0, false);
|
|
120
|
-
return out;
|
|
121
|
-
}
|
|
122
|
-
function registerAllNodes() {
|
|
123
|
-
registerNodes2D();
|
|
124
|
-
registerNodes3D();
|
|
125
|
-
registerNodesNet();
|
|
126
|
-
registerGameplayBehaviors();
|
|
127
|
-
}
|
|
128
|
-
/** Snapshot the live tree: every node, every schema prop's current value. */
|
|
129
|
-
function captureScene(scene) {
|
|
130
|
-
const nodes = [];
|
|
131
|
-
const walk = (node) => {
|
|
132
|
-
const ctor = node.constructor;
|
|
133
|
-
const schema = mergeStaticProps(ctor);
|
|
134
|
-
const props = {};
|
|
135
|
-
for (const key of Object.keys(schema)) props[key] = jsonClone(node[key]);
|
|
136
|
-
const capture = {
|
|
137
|
-
path: node.getPath(),
|
|
138
|
-
type: ctor.typeName,
|
|
139
|
-
props
|
|
140
|
-
};
|
|
141
|
-
if (node.uid) capture.uid = node.uid;
|
|
142
|
-
if (node.groups.size > 0) capture.groups = [...node.groups];
|
|
143
|
-
if (Object.keys(node.tags).length > 0) capture.tags = jsonClone(node.tags);
|
|
144
|
-
if (typeof node.script?.name === "string") capture.script = node.script.name;
|
|
145
|
-
nodes.push(capture);
|
|
146
|
-
for (const child of node.children) walk(child);
|
|
147
|
-
};
|
|
148
|
-
walk(scene.root);
|
|
149
|
-
const out = {
|
|
150
|
-
name: scene.name,
|
|
151
|
-
nodes
|
|
152
|
-
};
|
|
153
|
-
if (scene.dimension) out.dimension = scene.dimension;
|
|
154
|
-
return out;
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Render a capture as one grep-able line per node, showing only NON-DEFAULT
|
|
158
|
-
* props — the same delta discipline as scene JSON, so the interesting state
|
|
159
|
-
* stands out.
|
|
160
|
-
*/
|
|
161
|
-
function describeCapture(capture) {
|
|
162
|
-
const lines = [`scene ${capture.name}${capture.dimension ? ` (${capture.dimension})` : ""}`];
|
|
163
|
-
for (const node of capture.nodes) {
|
|
164
|
-
const schema = defaultsFor(node.type);
|
|
165
|
-
const deltas = Object.entries(node.props).filter(([key, value]) => !schema || !jsonEquals(value, schema[key]?.default ?? null)).map(([key, value]) => `${key}=${JSON.stringify(value)}`);
|
|
166
|
-
const extras = [
|
|
167
|
-
node.script ? `script=${node.script}` : "",
|
|
168
|
-
node.groups ? `groups=${node.groups.join(",")}` : "",
|
|
169
|
-
...deltas
|
|
170
|
-
].filter(Boolean);
|
|
171
|
-
lines.push(`${node.path} ${node.type}${extras.length ? ` ${extras.join(" ")}` : ""}`);
|
|
172
|
-
}
|
|
173
|
-
return lines.join("\n");
|
|
174
|
-
}
|
|
175
|
-
function defaultsFor(typeName) {
|
|
176
|
-
try {
|
|
177
|
-
return getNodeSchema(typeName);
|
|
178
|
-
} catch {
|
|
179
|
-
return null;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
/**
|
|
183
|
-
* Run every hard load-time check headlessly. Unregistered behaviors are
|
|
184
|
-
* stubbed by default (structure-only validation, no TypeScript needed) —
|
|
185
|
-
* pass the real classes via `behaviors` to validate script props too.
|
|
186
|
-
*/
|
|
187
|
-
function validateScene(json, opts = {}) {
|
|
188
|
-
registerAllNodes();
|
|
189
|
-
for (const [name, ctor] of Object.entries(opts.behaviors ?? {})) registerBehavior(name, ctor, { replace: true });
|
|
190
|
-
try {
|
|
191
|
-
loadScene(json, {
|
|
192
|
-
resolveScene: opts.resolveScene,
|
|
193
|
-
stubMissingBehaviors: !opts.strictBehaviors
|
|
194
|
-
}).root.free();
|
|
195
|
-
return { ok: true };
|
|
196
|
-
} catch (e) {
|
|
197
|
-
if (e instanceof IncantoError) return {
|
|
198
|
-
ok: false,
|
|
199
|
-
error: e
|
|
200
|
-
};
|
|
201
|
-
throw e;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* Load a scene and play it headlessly at a fixed timestep, driving input by
|
|
206
|
-
* ACTION intent. This is the e2e loop without a browser: script → simulate →
|
|
207
|
-
* snapshot → assert.
|
|
208
|
-
*/
|
|
209
|
-
async function runScript(json, opts) {
|
|
210
|
-
registerAllNodes();
|
|
211
|
-
for (const [name, ctor] of Object.entries(opts.behaviors ?? {})) registerBehavior(name, ctor, { replace: true });
|
|
212
|
-
const engine = new Engine({
|
|
213
|
-
seed: opts.seed,
|
|
214
|
-
fixedHz: opts.fixedHz,
|
|
215
|
-
scheduler: () => () => {}
|
|
216
|
-
});
|
|
217
|
-
const scene = loadScene(structuredClone(json), { resolveScene: opts.resolveScene });
|
|
218
|
-
engine.setScene(scene);
|
|
219
|
-
const physics = opts.physics ?? "auto";
|
|
220
|
-
if (physics === "2d" || physics === "auto" && scene.dimension === "2d") {
|
|
221
|
-
const { enablePhysics2D } = await import("./physics-2d-3kOQCtgd.js").then((n) => n.r);
|
|
222
|
-
await enablePhysics2D(engine);
|
|
223
|
-
} else if (physics === "3d" || physics === "auto" && scene.dimension === "3d") {
|
|
224
|
-
const { enablePhysics3D } = await import("./physics-3d-CeRH-Ff_.js").then((n) => n.r);
|
|
225
|
-
await enablePhysics3D(engine);
|
|
226
|
-
}
|
|
227
|
-
const failures = [];
|
|
228
|
-
const snapshots = [];
|
|
229
|
-
const ctx = {
|
|
230
|
-
engine,
|
|
231
|
-
scene,
|
|
232
|
-
timeMs: 0,
|
|
233
|
-
getNode: (path) => scene.root.getNode(path),
|
|
234
|
-
capture: () => captureScene(scene)
|
|
235
|
-
};
|
|
236
|
-
const applyStep = (step) => {
|
|
237
|
-
if (step.press) engine.input.pressAction(step.press);
|
|
238
|
-
if (step.release) engine.input.releaseAction(step.release);
|
|
239
|
-
if (step.vector) engine.input.setActionVector(...step.vector);
|
|
240
|
-
if (step.key) engine.input.handleKey(step.key[0], step.key[1]);
|
|
241
|
-
step.do?.(ctx);
|
|
242
|
-
if (step.assert) try {
|
|
243
|
-
if (step.assert(ctx) === false) failures.push({
|
|
244
|
-
atMs: step.atMs,
|
|
245
|
-
label: step.label,
|
|
246
|
-
message: "assert returned false"
|
|
247
|
-
});
|
|
248
|
-
} catch (e) {
|
|
249
|
-
failures.push({
|
|
250
|
-
atMs: step.atMs,
|
|
251
|
-
label: step.label,
|
|
252
|
-
message: e instanceof Error ? e.message : String(e)
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
};
|
|
256
|
-
const stepMs = 1e3 / (opts.fixedHz ?? 60);
|
|
257
|
-
const totalTicks = Math.round(opts.durationMs / stepMs);
|
|
258
|
-
const steps = [...opts.steps ?? []].sort((a, b) => a.atMs - b.atMs);
|
|
259
|
-
let stepIndex = 0;
|
|
260
|
-
let nextSnapshot = opts.snapshotEveryMs ?? Number.POSITIVE_INFINITY;
|
|
261
|
-
for (let i = 1; i <= totalTicks; i++) {
|
|
262
|
-
const t = i * stepMs;
|
|
263
|
-
ctx.timeMs = t;
|
|
264
|
-
while (stepIndex < steps.length && steps[stepIndex].atMs <= t) {
|
|
265
|
-
applyStep(steps[stepIndex]);
|
|
266
|
-
stepIndex += 1;
|
|
267
|
-
}
|
|
268
|
-
engine.step();
|
|
269
|
-
if (t >= nextSnapshot - 1e-6) {
|
|
270
|
-
snapshots.push({
|
|
271
|
-
atMs: Math.round(t),
|
|
272
|
-
capture: captureScene(scene)
|
|
273
|
-
});
|
|
274
|
-
nextSnapshot += opts.snapshotEveryMs;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
for (; stepIndex < steps.length; stepIndex++) {
|
|
278
|
-
const missed = steps[stepIndex];
|
|
279
|
-
failures.push({
|
|
280
|
-
atMs: missed.atMs,
|
|
281
|
-
label: missed.label,
|
|
282
|
-
message: `step never executed — atMs ${missed.atMs} is beyond the ${opts.durationMs}ms run`
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
const finalCapture = captureScene(scene);
|
|
286
|
-
const logs = [...engine.log.entries()];
|
|
287
|
-
engine.dispose();
|
|
288
|
-
const ok = failures.length === 0;
|
|
289
|
-
return {
|
|
290
|
-
ok,
|
|
291
|
-
failures,
|
|
292
|
-
snapshots,
|
|
293
|
-
logs,
|
|
294
|
-
finalCapture,
|
|
295
|
-
describe: () => {
|
|
296
|
-
return [
|
|
297
|
-
`run ${ok ? "OK" : "FAILED"} — ${opts.durationMs}ms simulated, ${failures.length} failure(s), ${logs.length} log(s)`,
|
|
298
|
-
...failures.map((f) => ` ✗ at ${f.atMs}ms${f.label ? ` [${f.label}]` : ""}: ${f.message}`),
|
|
299
|
-
describeCapture(finalCapture)
|
|
300
|
-
].join("\n");
|
|
301
|
-
}
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
/**
|
|
305
|
-
* The headless play loop behind `incanto-play`: load → feed inputs by intent
|
|
306
|
-
* (`session.engine.input.pressAction(...)`) → `step(ms)` → `capture()` the
|
|
307
|
-
* state AS A SCENE FILE. What a screenshot is to humans, the capture is to
|
|
308
|
-
* agents: complete, structured, and in the exact format they already read.
|
|
309
|
-
*/
|
|
310
|
-
async function createPlaySession(json, opts = {}) {
|
|
311
|
-
registerAllNodes();
|
|
312
|
-
for (const [name, ctor] of Object.entries(opts.behaviors ?? {})) registerBehavior(name, ctor, { replace: true });
|
|
313
|
-
const engine = new Engine({
|
|
314
|
-
seed: opts.seed,
|
|
315
|
-
fixedHz: opts.fixedHz,
|
|
316
|
-
scheduler: () => () => {}
|
|
317
|
-
});
|
|
318
|
-
const scene = loadScene(structuredClone(json), {
|
|
319
|
-
resolveScene: opts.resolveScene,
|
|
320
|
-
stubMissingBehaviors: opts.stubMissingBehaviors,
|
|
321
|
-
engine
|
|
322
|
-
});
|
|
323
|
-
engine.setScene(scene);
|
|
324
|
-
const physics = opts.physics ?? "auto";
|
|
325
|
-
if (physics === "2d" || physics === "auto" && scene.dimension === "2d") {
|
|
326
|
-
const { enablePhysics2D } = await import("./physics-2d-3kOQCtgd.js").then((n) => n.r);
|
|
327
|
-
await enablePhysics2D(engine);
|
|
328
|
-
} else if (physics === "3d" || physics === "auto" && scene.dimension === "3d") {
|
|
329
|
-
const { enablePhysics3D } = await import("./physics-3d-CeRH-Ff_.js").then((n) => n.r);
|
|
330
|
-
await enablePhysics3D(engine);
|
|
331
|
-
}
|
|
332
|
-
const stepMs = 1e3 / (opts.fixedHz ?? 60);
|
|
333
|
-
let timeMs = 0;
|
|
334
|
-
let logCursor = 0;
|
|
335
|
-
return {
|
|
336
|
-
engine,
|
|
337
|
-
scene,
|
|
338
|
-
get timeMs() {
|
|
339
|
-
return timeMs;
|
|
340
|
-
},
|
|
341
|
-
step(ms) {
|
|
342
|
-
const ticks = Math.max(1, Math.round(ms / stepMs));
|
|
343
|
-
for (let i = 0; i < ticks; i++) engine.step();
|
|
344
|
-
timeMs += ticks * stepMs;
|
|
345
|
-
},
|
|
346
|
-
capture() {
|
|
347
|
-
return scene.toJSON();
|
|
348
|
-
},
|
|
349
|
-
describe() {
|
|
350
|
-
return describeCapture(captureScene(scene));
|
|
351
|
-
},
|
|
352
|
-
drainLogs() {
|
|
353
|
-
const all = engine.log.entries();
|
|
354
|
-
const fresh = all.filter((e) => e.seq > logCursor);
|
|
355
|
-
if (all.length > 0) logCursor = all[all.length - 1].seq;
|
|
356
|
-
return [...fresh];
|
|
357
|
-
},
|
|
358
|
-
dispose() {
|
|
359
|
-
engine.dispose();
|
|
360
|
-
}
|
|
361
|
-
};
|
|
362
|
-
}
|
|
363
|
-
//#endregion
|
|
364
|
-
export { auditScene, captureScene, createPlaySession, describeCapture, findFloatingProps, registerAllNodes, runScript, validateScene };
|
|
1
|
+
import { t as auditScene } from "./audit-C4kmDK0o.js";
|
|
2
|
+
import { a as registerAllNodes, c as describeFraming, i as findFloatingProps, l as framingText, n as createPlaySession, o as runScript, r as describeCapture, s as validateScene, t as captureScene } from "./test-WwRIlXsK.js";
|
|
3
|
+
export { auditScene, captureScene, createPlaySession, describeCapture, describeFraming, findFloatingProps, framingText, registerAllNodes, runScript, validateScene };
|
|
@@ -4,7 +4,8 @@ function resolveRendering(environment, fallback, devicePixelRatio, explicit) {
|
|
|
4
4
|
const scenePixelRatio = rendering.pixelRatio === "device" ? devicePixelRatio : rendering.pixelRatio;
|
|
5
5
|
return {
|
|
6
6
|
antialias: explicit?.antialias ?? rendering.antialias ?? fallback.antialias,
|
|
7
|
-
pixelRatio: explicit?.pixelRatio ?? scenePixelRatio ?? fallback.pixelRatio
|
|
7
|
+
pixelRatio: explicit?.pixelRatio ?? scenePixelRatio ?? fallback.pixelRatio,
|
|
8
|
+
preserveDrawingBuffer: explicit?.preserveDrawingBuffer ?? rendering.preserveDrawingBuffer ?? fallback.preserveDrawingBuffer
|
|
8
9
|
};
|
|
9
10
|
}
|
|
10
11
|
//#endregion
|
package/dist/vite.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { validateScene } from "./test.js";
|
|
1
|
+
import { s as validateScene } from "./test-WwRIlXsK.js";
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { basename, dirname, join, normalize, relative, resolve, sep } from "node:path";
|
|
4
4
|
//#region src/vite/index.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e}from"./index-
|
|
1
|
+
import{n as e}from"./index-gfyrByWw.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,2 +1,3 @@
|
|
|
1
|
-
import{i as e,r as t,t as n}from"./index-
|
|
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};
|
|
1
|
+
import{i as e,r as t,t as n}from"./index-gfyrByWw.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;if(e instanceof Error){let t=e.stack?.split(`
|
|
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 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};
|