incanto 0.41.0 → 0.43.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-feel.mjs +20 -1
- package/bin/incanto-frame.mjs +144 -0
- package/dist/2d.d.ts +2 -2
- package/dist/2d.js +3 -3
- package/dist/3d.d.ts +76 -4
- package/dist/3d.js +5 -5
- package/dist/{behavior-TA8nySqb.d.ts → behavior-62q0HWBO.d.ts} +3 -1
- package/dist/{create-game-u7TDbvln.js → create-game-B3vBWgVD.js} +160 -6
- package/dist/{create-game-CZpENyin.js → create-game-DFRbG0Bj.js} +6 -5
- package/dist/debug.d.ts +1 -1
- package/dist/{environment-presets-pGTHb_V4.js → environment-presets-Ds5kXLoF.js} +2 -2
- package/dist/{gameplay-By8mslMc.js → gameplay-BQOeAid6.js} +50 -0
- package/dist/gameplay.d.ts +30 -1
- package/dist/gameplay.js +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -167
- package/dist/{loader-CvSDKC3v.d.ts → loader-CeyU_bm1.d.ts} +1 -1
- package/dist/net.d.ts +1 -1
- package/dist/net.js +2 -2
- package/dist/{pathfinding-_UlKUoox.d.ts → pathfinding-C49JSNNq.d.ts} +1 -1
- package/dist/{physics-2d-CEnZFcpM.js → physics-2d-BmgXBNDB.js} +1 -1
- package/dist/{physics-3d-CumBBvQo.js → physics-3d-CLPFv99o.js} +2 -2
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{register-DuTlY56W.js → register-BSXV8T9F.js} +3 -1
- package/dist/{register-DnycZ91Q.js → register-D651it1J.js} +1 -1
- package/dist/{register-MSZvnHkp.js → register-R2JTnIMw.js} +1 -1
- package/dist/{replay-DN-kG4va.d.ts → replay-CAphXMyM.d.ts} +1 -1
- package/dist/{replay-DcWg4LT5.js → replay-DilbZgQI.js} +1 -1
- package/dist/src-CwYxzZKl.js +166 -0
- package/dist/{teardown-CCtAMDLB.js → teardown-yePMOE1K.js} +32 -1
- package/dist/{test-B4kSwQzq.js → test-BMgiiD5i.js} +135 -12
- package/dist/test.d.ts +38 -4
- package/dist/test.js +3 -3
- package/dist/vite.d.ts +61 -1
- package/dist/vite.js +149 -2
- package/editor/assets/{agent8-DCHkff44.js → agent8-Cl3qFuBB.js} +1 -1
- package/editor/assets/{debug-uHHxqj0a.js → debug-u3yLCciO.js} +1 -1
- package/editor/assets/{index-i1mIkyMA.js → index-Dk2ZlO68.js} +60 -60
- package/editor/index.html +1 -1
- package/package.json +3 -2
- package/skills/incanto-3d-character.md +19 -0
- package/skills/incanto-audio.md +6 -2
- package/skills/incanto-gameplay-behaviors.md +27 -0
- package/skills/incanto-node-reference.md +2 -0
- package/skills/incanto-verifying-your-game.md +41 -0
- package/templates-app/beacon-isle-3d/package.json +1 -1
- package/templates-app/beacon-isle-3d/src/game.scene.json +47 -0
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/tps-3d/src/game.scene.json +53 -0
- package/templates-app/village-quest-3d/package.json +1 -1
- package/templates-app/village-quest-3d/src/grove.scene.json +43 -0
|
@@ -238,6 +238,37 @@ function poseFromRenderer(renderer, fallbackDistance = 8) {
|
|
|
238
238
|
};
|
|
239
239
|
}
|
|
240
240
|
//#endregion
|
|
241
|
+
//#region src/core/pause-when-hidden.ts
|
|
242
|
+
function pauseWhenHidden(engine, doc = typeof document === "undefined" ? null : document) {
|
|
243
|
+
if (!doc) return () => {};
|
|
244
|
+
/** The scale to come back to — null when we did not pause anything. */
|
|
245
|
+
let heldScale = null;
|
|
246
|
+
let mutedByUs = false;
|
|
247
|
+
const onChange = () => {
|
|
248
|
+
if (doc.hidden) {
|
|
249
|
+
if (heldScale === null && engine.timeScale > 0) {
|
|
250
|
+
heldScale = engine.timeScale;
|
|
251
|
+
engine.timeScale = 0;
|
|
252
|
+
}
|
|
253
|
+
if (!engine.audio.muted) {
|
|
254
|
+
engine.audio.muted = true;
|
|
255
|
+
mutedByUs = true;
|
|
256
|
+
}
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
if (heldScale !== null) {
|
|
260
|
+
if (engine.timeScale === 0) engine.timeScale = heldScale;
|
|
261
|
+
heldScale = null;
|
|
262
|
+
}
|
|
263
|
+
if (mutedByUs) {
|
|
264
|
+
engine.audio.muted = false;
|
|
265
|
+
mutedByUs = false;
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
doc.addEventListener("visibilitychange", onChange);
|
|
269
|
+
return () => doc.removeEventListener("visibilitychange", onChange);
|
|
270
|
+
}
|
|
271
|
+
//#endregion
|
|
241
272
|
//#region src/core/teardown.ts
|
|
242
273
|
function teardown(steps, onError = defaultReport) {
|
|
243
274
|
const failures = [];
|
|
@@ -262,4 +293,4 @@ function defaultReport(what, error) {
|
|
|
262
293
|
console.error(`[incanto] teardown: ${what} failed (continuing)`, error);
|
|
263
294
|
}
|
|
264
295
|
//#endregion
|
|
265
|
-
export {
|
|
296
|
+
export { openBundledEditor as a, devServerLibrary as i, pauseWhenHidden as n, poseFromRenderer as o, crossFade as r, teardown as t };
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { n as loadScene, w as registerBehavior } from "./loader-r49nDwB4.js";
|
|
2
|
-
import { h as Engine } from "./register-
|
|
2
|
+
import { h as Engine } from "./register-BSXV8T9F.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-DilbZgQI.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-BQOeAid6.js";
|
|
8
|
+
import { t as registerNodes2D } from "./register-R2JTnIMw.js";
|
|
9
|
+
import { n as registerNodes3D, t as resolveEnvironmentHdri } from "./environment-presets-Ds5kXLoF.js";
|
|
10
|
+
import { t as registerNodesNet } from "./register-D651it1J.js";
|
|
11
11
|
import { Box3, Euler, Matrix4, PerspectiveCamera, Quaternion, Vector3 } from "three";
|
|
12
12
|
//#region src/test/framing.ts
|
|
13
13
|
/**
|
|
@@ -651,6 +651,118 @@ function failingReplays(report) {
|
|
|
651
651
|
}));
|
|
652
652
|
}
|
|
653
653
|
//#endregion
|
|
654
|
+
//#region src/test/facing.ts
|
|
655
|
+
const DEG2RAD = Math.PI / 180;
|
|
656
|
+
/** The forward vector of a yaw (degrees) for a given art convention. */
|
|
657
|
+
function forwardOf(yawDeg, axis) {
|
|
658
|
+
const yaw = yawDeg * DEG2RAD;
|
|
659
|
+
const base = [Math.sin(yaw), Math.cos(yaw)];
|
|
660
|
+
switch (axis) {
|
|
661
|
+
case "-z": return [-base[0], -base[1]];
|
|
662
|
+
case "+x": return [base[1], -base[0]];
|
|
663
|
+
case "-x": return [-base[1], base[0]];
|
|
664
|
+
default: return base;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* The skin, and the yaw the art is DECLARED to be rotated by.
|
|
669
|
+
*
|
|
670
|
+
* `skinYawOffset` is how a model that does not face +Z says so, and it is the
|
|
671
|
+
* supported fix rather than a bug. Subtracting it is what stops this check
|
|
672
|
+
* crying wolf on every correctly-configured non-+Z model.
|
|
673
|
+
*/
|
|
674
|
+
function findSkin(player, explicit) {
|
|
675
|
+
if (explicit) return {
|
|
676
|
+
skin: player.getNodeOrNull(explicit) ?? null,
|
|
677
|
+
declaredOffset: 0
|
|
678
|
+
};
|
|
679
|
+
for (const child of player.children) {
|
|
680
|
+
const ctl = child;
|
|
681
|
+
if (typeof ctl.skinPath === "string" && ctl.skinPath !== "") {
|
|
682
|
+
const skin = child.getNodeOrNull(ctl.skinPath);
|
|
683
|
+
if (skin) return {
|
|
684
|
+
skin,
|
|
685
|
+
declaredOffset: ctl.skinYawOffset ?? 0
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return {
|
|
690
|
+
skin: player.getNodeOrNull("%Skin") ?? null,
|
|
691
|
+
declaredOffset: 0
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Drive the player forward and compare where the skin points with where it went.
|
|
696
|
+
*
|
|
697
|
+
* The travel direction is measured, not assumed: whatever the input map means by
|
|
698
|
+
* "up", the character ends up going somewhere, and that somewhere is the truth
|
|
699
|
+
* the art has to agree with.
|
|
700
|
+
*/
|
|
701
|
+
async function facingReport(json, opts = {}) {
|
|
702
|
+
const session = await createPlaySession(json, {
|
|
703
|
+
seed: opts.seed ?? 1,
|
|
704
|
+
...opts.behaviors ? { behaviors: opts.behaviors } : {},
|
|
705
|
+
stubMissingBehaviors: opts.stubMissingBehaviors ?? true,
|
|
706
|
+
...opts.resolveScene ? { resolveScene: opts.resolveScene } : {}
|
|
707
|
+
});
|
|
708
|
+
const player = findPlayer(session.scene.root);
|
|
709
|
+
if (!player) {
|
|
710
|
+
session.dispose();
|
|
711
|
+
return {
|
|
712
|
+
player: null,
|
|
713
|
+
skin: null,
|
|
714
|
+
dot: null,
|
|
715
|
+
backwards: false,
|
|
716
|
+
travelled: 0
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
const declared = session.engine.input.declaredActions();
|
|
720
|
+
const move = opts.moveAction ?? declared.find((a) => a.type === "vector2")?.name ?? null;
|
|
721
|
+
const posOf = () => {
|
|
722
|
+
const p = player.position;
|
|
723
|
+
return [p[0] ?? 0, p[2] ?? 0];
|
|
724
|
+
};
|
|
725
|
+
session.step(500);
|
|
726
|
+
const from = posOf();
|
|
727
|
+
if (move) session.engine.input.setActionVector(move, 1, 0);
|
|
728
|
+
session.step(700);
|
|
729
|
+
const to = posOf();
|
|
730
|
+
if (move) session.engine.input.setActionVector(move, 0, 0);
|
|
731
|
+
const dx = to[0] - from[0];
|
|
732
|
+
const dz = to[1] - from[1];
|
|
733
|
+
const travelled = Math.hypot(dx, dz);
|
|
734
|
+
const { skin, declaredOffset } = findSkin(player, opts.skinPath);
|
|
735
|
+
const skinPath = skin ? skin.getPath() : null;
|
|
736
|
+
if (!skin || travelled < .05) {
|
|
737
|
+
session.dispose();
|
|
738
|
+
return {
|
|
739
|
+
player: player.getPath(),
|
|
740
|
+
skin: skinPath,
|
|
741
|
+
dot: null,
|
|
742
|
+
backwards: false,
|
|
743
|
+
travelled
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
const [fx, fz] = forwardOf((skin.rotation?.[1] ?? 0) - declaredOffset, opts.forwardAxis ?? "+z");
|
|
747
|
+
const dot = (fx * dx + fz * dz) / travelled;
|
|
748
|
+
session.dispose();
|
|
749
|
+
return {
|
|
750
|
+
player: player.getPath(),
|
|
751
|
+
skin: skinPath,
|
|
752
|
+
dot: Math.round(dot * 1e3) / 1e3,
|
|
753
|
+
backwards: dot < -.5,
|
|
754
|
+
travelled: Math.round(travelled * 1e3) / 1e3
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
/** One line for a report, or the failure spelled out. */
|
|
758
|
+
function facingText(report) {
|
|
759
|
+
if (!report.player) return "facing: no player to drive";
|
|
760
|
+
if (report.skin === null) return `facing: ${report.player} has no skin to check`;
|
|
761
|
+
if (report.dot === null) return `facing: ${report.player} did not move — nothing to compare`;
|
|
762
|
+
if (report.backwards) return `facing: ${report.skin} RUNS BACKWARDS (dot ${report.dot.toFixed(2)}) — the skin points away from the direction of travel. The rule is +Z forward, yaw = atan2(dx, dz), with NO +180; for art that faces another way set skinYawOffset rather than adding to the yaw.`;
|
|
763
|
+
return `facing: ${report.skin} faces its travel (dot ${report.dot.toFixed(2)})`;
|
|
764
|
+
}
|
|
765
|
+
//#endregion
|
|
654
766
|
//#region src/test/feel.ts
|
|
655
767
|
const STEP_MS = 1e3 / 60;
|
|
656
768
|
/** Drop height for the buffer probe — long enough to outlast a coyote window. */
|
|
@@ -663,8 +775,19 @@ function positionOf(node) {
|
|
|
663
775
|
p?.[2] ?? 0
|
|
664
776
|
];
|
|
665
777
|
}
|
|
778
|
+
/**
|
|
779
|
+
* A body's velocity, whichever name it keeps it under.
|
|
780
|
+
*
|
|
781
|
+
* `RigidBody2D/3D` call it `linearVelocity` (the solver owns it);
|
|
782
|
+
* `CharacterBody2D/3D` call it `velocity` (Godot semantics — the game owns it
|
|
783
|
+
* and calls moveAndSlide). Reading only the first reported **zero for every
|
|
784
|
+
* CharacterBody game**, so `incanto-feel` answered `topSpeed 0` and every
|
|
785
|
+
* derived window as null — a feel report that cannot see the character move.
|
|
786
|
+
* It went unnoticed because every test in feel.test.ts drives a RigidBody3D.
|
|
787
|
+
*/
|
|
666
788
|
function velocityOf(node) {
|
|
667
|
-
const
|
|
789
|
+
const body = node;
|
|
790
|
+
const v = body.linearVelocity ?? body.velocity;
|
|
668
791
|
return [
|
|
669
792
|
v?.[0] ?? 0,
|
|
670
793
|
v?.[1] ?? 0,
|
|
@@ -1244,10 +1367,10 @@ async function runScript(json, opts) {
|
|
|
1244
1367
|
engine.setScene(scene);
|
|
1245
1368
|
const physics = opts.physics ?? "auto";
|
|
1246
1369
|
if (physics === "2d" || physics === "auto" && scene.dimension === "2d") {
|
|
1247
|
-
const { enablePhysics2D } = await import("./physics-2d-
|
|
1370
|
+
const { enablePhysics2D } = await import("./physics-2d-BmgXBNDB.js").then((n) => n.r);
|
|
1248
1371
|
await enablePhysics2D(engine);
|
|
1249
1372
|
} else if (physics === "3d" || physics === "auto" && scene.dimension === "3d") {
|
|
1250
|
-
const { enablePhysics3D } = await import("./physics-3d-
|
|
1373
|
+
const { enablePhysics3D } = await import("./physics-3d-CLPFv99o.js").then((n) => n.r);
|
|
1251
1374
|
await enablePhysics3D(engine);
|
|
1252
1375
|
}
|
|
1253
1376
|
const failures = [];
|
|
@@ -1360,10 +1483,10 @@ async function createPlaySession(json, opts = {}) {
|
|
|
1360
1483
|
engine.setScene(scene);
|
|
1361
1484
|
const physics = opts.physics ?? "auto";
|
|
1362
1485
|
if (physics === "2d" || physics === "auto" && scene.dimension === "2d") {
|
|
1363
|
-
const { enablePhysics2D } = await import("./physics-2d-
|
|
1486
|
+
const { enablePhysics2D } = await import("./physics-2d-BmgXBNDB.js").then((n) => n.r);
|
|
1364
1487
|
await enablePhysics2D(engine);
|
|
1365
1488
|
} else if (physics === "3d" || physics === "auto" && scene.dimension === "3d") {
|
|
1366
|
-
const { enablePhysics3D } = await import("./physics-3d-
|
|
1489
|
+
const { enablePhysics3D } = await import("./physics-3d-CLPFv99o.js").then((n) => n.r);
|
|
1367
1490
|
await enablePhysics3D(engine);
|
|
1368
1491
|
}
|
|
1369
1492
|
const stepMs = 1e3 / (opts.fixedHz ?? 60);
|
|
@@ -1401,4 +1524,4 @@ async function createPlaySession(json, opts = {}) {
|
|
|
1401
1524
|
};
|
|
1402
1525
|
}
|
|
1403
1526
|
//#endregion
|
|
1404
|
-
export { registerAllNodes as a, feelReport as c,
|
|
1527
|
+
export { framingText as _, registerAllNodes as a, feelReport as c, facingText as d, failingReplays as f, describeFraming as g, playtestText as h, findFloatingProps as i, feelText as l, playtest as m, createPlaySession as n, runScript as o, findPlayer as p, describeCapture as r, validateScene as s, captureScene as t, facingReport as u };
|
package/dist/test.d.ts
CHANGED
|
@@ -1,8 +1,42 @@
|
|
|
1
|
-
import { Et as Node, P as Scene, b as Engine, kt as LogEntry, n as BehaviorCtor } from "./behavior-
|
|
1
|
+
import { Et as Node, P as Scene, b as Engine, kt as LogEntry, n as BehaviorCtor } from "./behavior-62q0HWBO.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-CeyU_bm1.js";
|
|
4
|
+
import { l as auditScene, o as IncantoError, r as ReplayJson } from "./replay-CAphXMyM.js";
|
|
5
5
|
|
|
6
|
+
//#region src/test/facing.d.ts
|
|
7
|
+
interface FacingOptions {
|
|
8
|
+
behaviors?: Record<string, BehaviorCtor>;
|
|
9
|
+
stubMissingBehaviors?: boolean;
|
|
10
|
+
resolveScene?: LoadSceneOptions["resolveScene"];
|
|
11
|
+
seed?: number;
|
|
12
|
+
moveAction?: string;
|
|
13
|
+
/** Which local axis the ART faces. Default `+z`, the engine's convention. */
|
|
14
|
+
forwardAxis?: "+z" | "-z" | "+x" | "-x";
|
|
15
|
+
/** Where the visual skin is, relative to the player. Default: found by name. */
|
|
16
|
+
skinPath?: string;
|
|
17
|
+
}
|
|
18
|
+
interface FacingReport {
|
|
19
|
+
player: string | null;
|
|
20
|
+
/** The node whose rotation was measured, or null when the player has no skin. */
|
|
21
|
+
skin: string | null;
|
|
22
|
+
/** Skin forward · travel direction. +1 faces forward, −1 runs backwards. */
|
|
23
|
+
dot: number | null;
|
|
24
|
+
/** True only when the two genuinely oppose — the reportable failure. */
|
|
25
|
+
backwards: boolean;
|
|
26
|
+
/** How far it actually moved, so a report on a stuck character is not trusted. */
|
|
27
|
+
travelled: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Drive the player forward and compare where the skin points with where it went.
|
|
31
|
+
*
|
|
32
|
+
* The travel direction is measured, not assumed: whatever the input map means by
|
|
33
|
+
* "up", the character ends up going somewhere, and that somewhere is the truth
|
|
34
|
+
* the art has to agree with.
|
|
35
|
+
*/
|
|
36
|
+
declare function facingReport(json: unknown, opts?: FacingOptions): Promise<FacingReport>;
|
|
37
|
+
/** One line for a report, or the failure spelled out. */
|
|
38
|
+
declare function facingText(report: FacingReport): string;
|
|
39
|
+
//#endregion
|
|
6
40
|
//#region src/test/framing.d.ts
|
|
7
41
|
/** Where a node's origin sits relative to the current camera. */
|
|
8
42
|
type Where = "onScreen" | "offscreen" | "behind";
|
|
@@ -337,4 +371,4 @@ interface PlaySession {
|
|
|
337
371
|
*/
|
|
338
372
|
declare function createPlaySession(json: unknown, opts?: PlaySessionOptions): Promise<PlaySession>;
|
|
339
373
|
//#endregion
|
|
340
|
-
export { type FeelOptions, type FeelReport, type FramingEntry, type FramingOptions, type FramingReport, GroundingIssue, GroundingOptions, NodeCapture, type Outcome, PlaySession, PlaySessionOptions, type PlaytestOptions, type PlaytestReport, type PlaytestRun, RunContext, RunFailure, RunResult, RunScriptOptions, SceneCapture, ScriptStep, ValidateSceneOptions, ValidationResult, type Where, auditScene, captureScene, createPlaySession, describeCapture, describeFraming, failingReplays, feelReport, feelText, findFloatingProps, findPlayer, framingText, playtest, playtestText, registerAllNodes, runScript, validateScene };
|
|
374
|
+
export { type FacingOptions, type FacingReport, type FeelOptions, type FeelReport, type FramingEntry, type FramingOptions, type FramingReport, GroundingIssue, GroundingOptions, NodeCapture, type Outcome, PlaySession, PlaySessionOptions, type PlaytestOptions, type PlaytestReport, type PlaytestRun, RunContext, RunFailure, RunResult, RunScriptOptions, SceneCapture, ScriptStep, ValidateSceneOptions, ValidationResult, type Where, auditScene, captureScene, createPlaySession, describeCapture, describeFraming, facingReport, facingText, failingReplays, feelReport, feelText, findFloatingProps, findPlayer, framingText, playtest, playtestText, registerAllNodes, runScript, validateScene };
|
package/dist/test.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { r as auditScene } from "./replay-
|
|
2
|
-
import { a as registerAllNodes, c as feelReport, d as
|
|
3
|
-
export { auditScene, captureScene, createPlaySession, describeCapture, describeFraming, failingReplays, feelReport, feelText, findFloatingProps, findPlayer, framingText, playtest, playtestText, registerAllNodes, runScript, validateScene };
|
|
1
|
+
import { r as auditScene } from "./replay-DilbZgQI.js";
|
|
2
|
+
import { _ as framingText, a as registerAllNodes, c as feelReport, d as facingText, f as failingReplays, g as describeFraming, h as playtestText, i as findFloatingProps, l as feelText, m as playtest, n as createPlaySession, o as runScript, p as findPlayer, r as describeCapture, s as validateScene, t as captureScene, u as facingReport } from "./test-BMgiiD5i.js";
|
|
3
|
+
export { auditScene, captureScene, createPlaySession, describeCapture, describeFraming, facingReport, facingText, failingReplays, feelReport, feelText, findFloatingProps, findPlayer, framingText, playtest, playtestText, registerAllNodes, runScript, validateScene };
|
package/dist/vite.d.ts
CHANGED
|
@@ -1,3 +1,63 @@
|
|
|
1
|
+
//#region src/vite/discover.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The listening TCP ports in `/proc/net/tcp` (and `tcp6`) format.
|
|
4
|
+
*
|
|
5
|
+
* Linux-only and dependency-free ON PURPOSE: `lsof` and `ss` are routinely
|
|
6
|
+
* absent from a slim container, and this has to work in the container a
|
|
7
|
+
* vibe-coding service actually ships, not in a developer's laptop.
|
|
8
|
+
*/
|
|
9
|
+
declare function parseProcNetTcp(text: string): number[];
|
|
10
|
+
interface DiscoverDeps {
|
|
11
|
+
/** Reads a proc file; returns null when it is not there (macOS, Windows). */
|
|
12
|
+
read(path: string): string | null;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Ports worth asking, in the order worth asking them.
|
|
16
|
+
*
|
|
17
|
+
* Observed first — those are facts. The usual suspects after, because a
|
|
18
|
+
* container that hides `/proc` should still find a vite server on 5173.
|
|
19
|
+
*/
|
|
20
|
+
declare function listeningPorts(deps?: Partial<DiscoverDeps>): Promise<number[]>;
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/vite/frame-endpoint.d.ts
|
|
23
|
+
interface FrameHost {
|
|
24
|
+
config?: {
|
|
25
|
+
root?: string;
|
|
26
|
+
};
|
|
27
|
+
middlewares: {
|
|
28
|
+
use(path: string, handler: (req: unknown, res: FrameRes) => void): void;
|
|
29
|
+
};
|
|
30
|
+
/** Vite's HMR channel. `ws` on v5, `hot` on v6+ — both are accepted. */
|
|
31
|
+
ws?: FrameChannel;
|
|
32
|
+
hot?: FrameChannel;
|
|
33
|
+
}
|
|
34
|
+
interface FrameChannel {
|
|
35
|
+
send(payload: {
|
|
36
|
+
type: "custom";
|
|
37
|
+
event: string;
|
|
38
|
+
data?: unknown;
|
|
39
|
+
}): void;
|
|
40
|
+
on(event: string, cb: (data: unknown) => void): void;
|
|
41
|
+
}
|
|
42
|
+
interface FrameRes {
|
|
43
|
+
statusCode: number;
|
|
44
|
+
setHeader(name: string, value: string): void;
|
|
45
|
+
end(body?: string): void;
|
|
46
|
+
}
|
|
47
|
+
interface FrameEndpointOptions {
|
|
48
|
+
/** Overridden in tests; defaults to the real clock. */
|
|
49
|
+
now?: () => number;
|
|
50
|
+
timeoutMs?: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Wire `/__incanto/ping` and `/__incanto/frame` onto a dev server.
|
|
54
|
+
*
|
|
55
|
+
* `ping` is how the CLI tells our server apart from anything else listening in
|
|
56
|
+
* the container: it enumerates the ports that are actually open and asks each
|
|
57
|
+
* one. That answer has to be cheap and unmistakable.
|
|
58
|
+
*/
|
|
59
|
+
declare function serveFrameEndpoints(host: FrameHost, version: string, opts?: FrameEndpointOptions): void;
|
|
60
|
+
//#endregion
|
|
1
61
|
//#region src/vite/index.d.ts
|
|
2
62
|
interface HotUpdateContext {
|
|
3
63
|
file: string;
|
|
@@ -161,4 +221,4 @@ declare function incantoLibrary(opts?: IncantoLibraryOptions): {
|
|
|
161
221
|
/** @internal Exported for tests — the whole request/response behaviour. */
|
|
162
222
|
declare function serveLibrary(req: LibraryReq, res: LibraryRes, opts?: IncantoLibraryOptions, doFetch?: typeof fetch): Promise<void>;
|
|
163
223
|
//#endregion
|
|
164
|
-
export { IncantoLibraryOptions, IncantoScenesOptions, ProjectFileEntry, discoverScenes, incantoLibrary, incantoScenes, resolveSceneFile, sceneFacts, serveLibrary, serveSceneFile, serveSceneList };
|
|
224
|
+
export { type FrameHost, IncantoLibraryOptions, IncantoScenesOptions, ProjectFileEntry, discoverScenes, incantoLibrary, incantoScenes, listeningPorts, parseProcNetTcp, resolveSceneFile, sceneFacts, serveFrameEndpoints, serveLibrary, serveSceneFile, serveSceneList };
|
package/dist/vite.js
CHANGED
|
@@ -1,6 +1,152 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as VERSION } from "./src-CwYxzZKl.js";
|
|
2
|
+
import { s as validateScene } from "./test-BMgiiD5i.js";
|
|
2
3
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
3
4
|
import { basename, dirname, join, normalize, relative, resolve, sep } from "node:path";
|
|
5
|
+
//#region src/vite/frame-endpoint.ts
|
|
6
|
+
/**
|
|
7
|
+
* The dev server's half of `incanto-frame`.
|
|
8
|
+
*
|
|
9
|
+
* The server does not have the pixels — the BROWSER does. Vite already keeps a
|
|
10
|
+
* websocket to every connected client (that is how HMR works), so the frame
|
|
11
|
+
* request rides that channel: ask the page, wait, answer the CLI. No new
|
|
12
|
+
* transport, no file, nothing written anywhere.
|
|
13
|
+
*
|
|
14
|
+
* Three outcomes, and they must be told apart, because the fix differs:
|
|
15
|
+
* no server → the preview was never started
|
|
16
|
+
* server, no client → started, but nobody opened the page
|
|
17
|
+
* server + client → the frame
|
|
18
|
+
*/
|
|
19
|
+
/** How long to wait for a page to answer before calling it absent. */
|
|
20
|
+
const CLIENT_TIMEOUT_MS = 2e3;
|
|
21
|
+
/**
|
|
22
|
+
* Wire `/__incanto/ping` and `/__incanto/frame` onto a dev server.
|
|
23
|
+
*
|
|
24
|
+
* `ping` is how the CLI tells our server apart from anything else listening in
|
|
25
|
+
* the container: it enumerates the ports that are actually open and asks each
|
|
26
|
+
* one. That answer has to be cheap and unmistakable.
|
|
27
|
+
*/
|
|
28
|
+
function serveFrameEndpoints(host, version, opts = {}) {
|
|
29
|
+
const channel = host.hot ?? host.ws;
|
|
30
|
+
const timeoutMs = opts.timeoutMs ?? CLIENT_TIMEOUT_MS;
|
|
31
|
+
let nextId = 1;
|
|
32
|
+
const waiting = /* @__PURE__ */ new Map();
|
|
33
|
+
channel?.on("incanto:frame-report", (data) => {
|
|
34
|
+
const payload = data;
|
|
35
|
+
const id = payload?.id;
|
|
36
|
+
if (typeof id !== "number") return;
|
|
37
|
+
const resolve = waiting.get(id);
|
|
38
|
+
if (!resolve) return;
|
|
39
|
+
waiting.delete(id);
|
|
40
|
+
resolve({
|
|
41
|
+
ok: true,
|
|
42
|
+
report: payload?.report
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
host.middlewares.use("/__incanto/ping", (_req, res) => {
|
|
46
|
+
json$1(res, 200, {
|
|
47
|
+
incanto: version,
|
|
48
|
+
frame: Boolean(channel)
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
host.middlewares.use("/__incanto/frame", (_req, res) => {
|
|
52
|
+
if (!channel) {
|
|
53
|
+
json$1(res, 503, { error: "no-hmr-channel" });
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const id = nextId++;
|
|
57
|
+
const timer = setTimeout(() => {
|
|
58
|
+
waiting.delete(id);
|
|
59
|
+
json$1(res, 504, { error: "no-page-connected" });
|
|
60
|
+
}, timeoutMs);
|
|
61
|
+
waiting.set(id, (value) => {
|
|
62
|
+
clearTimeout(timer);
|
|
63
|
+
if (value.ok) json$1(res, 200, { report: value.report });
|
|
64
|
+
else json$1(res, 500, { error: "capture-failed" });
|
|
65
|
+
});
|
|
66
|
+
channel.send({
|
|
67
|
+
type: "custom",
|
|
68
|
+
event: "incanto:frame-request",
|
|
69
|
+
data: { id }
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function json$1(res, status, body) {
|
|
74
|
+
res.statusCode = status;
|
|
75
|
+
res.setHeader("content-type", "application/json");
|
|
76
|
+
res.end(JSON.stringify(body));
|
|
77
|
+
}
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region src/vite/discover.ts
|
|
80
|
+
/**
|
|
81
|
+
* Finding the dev server — the generic half of the frame capture.
|
|
82
|
+
*
|
|
83
|
+
* Nothing here knows what incanto is. It answers one question: which local
|
|
84
|
+
* ports might be serving something, best candidates first. Deciding which of
|
|
85
|
+
* them is OURS is a ping, and that belongs to the caller.
|
|
86
|
+
*
|
|
87
|
+
* This is deliberately code and not documentation. An agent told "check the
|
|
88
|
+
* screen" runs one command; anything it has to work out for itself — which
|
|
89
|
+
* port, whether the preview is even up — is a coin flip that lands differently
|
|
90
|
+
* every session.
|
|
91
|
+
*/
|
|
92
|
+
/** Where a vite dev server usually is, tried after anything actually observed. */
|
|
93
|
+
const LIKELY = [
|
|
94
|
+
5173,
|
|
95
|
+
5174,
|
|
96
|
+
5175,
|
|
97
|
+
5176,
|
|
98
|
+
4173,
|
|
99
|
+
3e3,
|
|
100
|
+
8080
|
|
101
|
+
];
|
|
102
|
+
/** `/proc/net/tcp` state code for LISTEN. */
|
|
103
|
+
const TCP_LISTEN = "0A";
|
|
104
|
+
/**
|
|
105
|
+
* The listening TCP ports in `/proc/net/tcp` (and `tcp6`) format.
|
|
106
|
+
*
|
|
107
|
+
* Linux-only and dependency-free ON PURPOSE: `lsof` and `ss` are routinely
|
|
108
|
+
* absent from a slim container, and this has to work in the container a
|
|
109
|
+
* vibe-coding service actually ships, not in a developer's laptop.
|
|
110
|
+
*/
|
|
111
|
+
function parseProcNetTcp(text) {
|
|
112
|
+
const ports = /* @__PURE__ */ new Set();
|
|
113
|
+
for (const line of text.split("\n")) {
|
|
114
|
+
const cols = line.trim().split(/\s+/);
|
|
115
|
+
if (cols.length < 4) continue;
|
|
116
|
+
const local = cols[1];
|
|
117
|
+
if (cols[3] !== TCP_LISTEN || !local?.includes(":")) continue;
|
|
118
|
+
const hex = local.slice(local.lastIndexOf(":") + 1);
|
|
119
|
+
const port = Number.parseInt(hex, 16);
|
|
120
|
+
if (Number.isFinite(port) && port > 0 && port < 65536) ports.add(port);
|
|
121
|
+
}
|
|
122
|
+
return [...ports];
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Ports worth asking, in the order worth asking them.
|
|
126
|
+
*
|
|
127
|
+
* Observed first — those are facts. The usual suspects after, because a
|
|
128
|
+
* container that hides `/proc` should still find a vite server on 5173.
|
|
129
|
+
*/
|
|
130
|
+
async function listeningPorts(deps) {
|
|
131
|
+
const read = deps?.read ?? ((path) => {
|
|
132
|
+
try {
|
|
133
|
+
const fs = globalThis["__incantoFs"];
|
|
134
|
+
return fs ? fs.readFileSync(path, "utf-8") : null;
|
|
135
|
+
} catch {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
const observed = [...parseProcNetTcp(read("/proc/net/tcp") ?? ""), ...parseProcNetTcp(read("/proc/net/tcp6") ?? "")];
|
|
140
|
+
const seen = /* @__PURE__ */ new Set();
|
|
141
|
+
const out = [];
|
|
142
|
+
for (const port of [...observed, ...LIKELY]) {
|
|
143
|
+
if (seen.has(port)) continue;
|
|
144
|
+
seen.add(port);
|
|
145
|
+
out.push(port);
|
|
146
|
+
}
|
|
147
|
+
return out;
|
|
148
|
+
}
|
|
149
|
+
//#endregion
|
|
4
150
|
//#region src/vite/index.ts
|
|
5
151
|
/**
|
|
6
152
|
* incanto/vite — dev-server integration: validate every `*.scene.json` edit
|
|
@@ -30,6 +176,7 @@ function incantoScenes(opts = {}) {
|
|
|
30
176
|
* `incanto-editor` does. Dev-only by construction (`configureServer`).
|
|
31
177
|
*/
|
|
32
178
|
configureServer(server) {
|
|
179
|
+
serveFrameEndpoints(server, VERSION);
|
|
33
180
|
const root = opts.root ?? server.config?.root ?? process.cwd();
|
|
34
181
|
server.middlewares.use("/api/scenes", (req, res) => {
|
|
35
182
|
serveSceneList(req, res, root);
|
|
@@ -405,4 +552,4 @@ function libraryItem(row) {
|
|
|
405
552
|
};
|
|
406
553
|
}
|
|
407
554
|
//#endregion
|
|
408
|
-
export { discoverScenes, incantoLibrary, incantoScenes, resolveSceneFile, sceneFacts, serveLibrary, serveSceneFile, serveSceneList };
|
|
555
|
+
export { discoverScenes, incantoLibrary, incantoScenes, listeningPorts, parseProcNetTcp, resolveSceneFile, sceneFacts, serveFrameEndpoints, serveLibrary, serveSceneFile, serveSceneList };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e}from"./index-
|
|
1
|
+
import{n as e}from"./index-Dk2ZlO68.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-Dk2ZlO68.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
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)}var h=2;function g(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 y(e,r,n,t.statsSource,t.actions)}var _=300,v=[`log`,`info`,`warn`,`error`,`debug`],y=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;resumeScale=1;timeEls=null;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(S).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===`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===`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 v){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(S).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`],[`time`,`Time`],[`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 ${x(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;b(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;b(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,w(`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,w(`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,w(`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>_&&this.logRows.splice(0,this.logRows.length-_),this.renderLogs()}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,h)),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;b(e.body),this.timeEls=null;let t=this.doc.createElement(`div`);i(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(h),n.step=`0.05`,n.value=String(Math.min(this.engine.timeScale,h)),i(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`);i(r,{display:`flex`,gap:`6px`,alignItems:`center`,marginBottom:`8px`});let a=this.doc.createElement(`input`);a.type=`number`,a.min=`0`,a.step=`0.05`,a.value=String(this.engine.timeScale),i(a,w(`72px`)),this.trackEditing(a),a.addEventListener(`change`,()=>{let e=Number(a.value);Number.isFinite(e)&&this.setTimeScale(e),this.syncTimeAfterEdit()}),r.appendChild(a);for(let e of[.25,.5,1,2]){let t=this.doc.createElement(`div`);t.textContent=`${e}×`,i(t,{cursor:`pointer`,opacity:`0.7`,padding:`2px 4px`}),t.addEventListener(`click`,()=>this.setTimeScale(e)),r.appendChild(t)}e.body.appendChild(r);let o=this.doc.createElement(`div`);i(o,{display:`flex`,gap:`8px`});let s=this.doc.createElement(`div`);i(s,C()),s.addEventListener(`click`,()=>this.setPaused(!this.paused)),o.appendChild(s);let c=this.doc.createElement(`div`);c.textContent=`⏭ Next frame`,c.title=`Pauses, then advances one fixed step`,i(c,C()),c.addEventListener(`click`,()=>this.nextFrame()),o.appendChild(c),e.body.appendChild(o),this.timeEls={slider:n,box:a,readout:t,pause:s},this.syncTime()}syncTimeAfterEdit(){let e=this.timeEls;if(!e)return;let t=this.engine.timeScale;e.slider.value=String(Math.min(t,h)),e.box.value=String(Math.round(t*1e3)/1e3)}renderLogs(){let e=this.panels.get(`logs`);if(!e)return;b(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 b(e){for(let t of[...e.children])t.remove()}function x(e){return e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}k`:String(e)}function S(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 C(){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 w(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{g as attachDebugOverlay};
|