incanto 0.42.0 → 0.44.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.
Files changed (41) hide show
  1. package/bin/incanto-feel.mjs +20 -1
  2. package/bin/incanto-frame.mjs +55 -11
  3. package/dist/2d.js +1 -1
  4. package/dist/3d.d.ts +2 -40
  5. package/dist/3d.js +6 -5
  6. package/dist/{create-game-DItIRyXH.js → create-game-D90pyPMx.js} +17 -81
  7. package/dist/{create-game-9KPppR0L.js → create-game-DFRbG0Bj.js} +3 -2
  8. package/dist/debug.d.ts +44 -3
  9. package/dist/debug.js +562 -4
  10. package/dist/{environment-presets-Vl5xBsXp.js → environment-presets-Ds5kXLoF.js} +1 -1
  11. package/dist/frame-report-Ct8XgsmV.d.ts +80 -0
  12. package/dist/frame-report-Lr3VO24R.js +197 -0
  13. package/dist/{gameplay-DRi9524r.js → gameplay-BQOeAid6.js} +37 -0
  14. package/dist/gameplay.d.ts +17 -0
  15. package/dist/gameplay.js +1 -1
  16. package/dist/index.js +1 -1
  17. package/dist/{physics-3d-C2G604O1.js → physics-3d-CLPFv99o.js} +2 -2
  18. package/dist/react.js +1 -1
  19. package/dist/{src-cU57Uwdw.js → src-B3n06SsL.js} +1 -1
  20. package/dist/{teardown-CCtAMDLB.js → teardown-yePMOE1K.js} +32 -1
  21. package/dist/{test-DuOD1DO8.js → test-BMgiiD5i.js} +117 -5
  22. package/dist/test.d.ts +35 -1
  23. package/dist/test.js +2 -2
  24. package/dist/vite.d.ts +15 -1
  25. package/dist/vite.js +49 -6
  26. package/editor/assets/{agent8-Csw0T7jh.js → agent8-MgX6kYWH.js} +1 -1
  27. package/editor/assets/debug-Czri9cgX.js +3 -0
  28. package/editor/assets/{index-B1rUkWxB.js → index-D9MX3UHF.js} +60 -60
  29. package/editor/index.html +1 -1
  30. package/package.json +1 -1
  31. package/skills/incanto-3d-character.md +19 -0
  32. package/skills/incanto-gameplay-behaviors.md +27 -0
  33. package/skills/incanto-node-reference.md +2 -0
  34. package/skills/incanto-verifying-your-game.md +60 -4
  35. package/templates-app/beacon-isle-3d/package.json +1 -1
  36. package/templates-app/beacon-isle-3d/src/game.scene.json +47 -0
  37. package/templates-app/tps-3d/package.json +1 -1
  38. package/templates-app/tps-3d/src/game.scene.json +53 -0
  39. package/templates-app/village-quest-3d/package.json +1 -1
  40. package/templates-app/village-quest-3d/src/grove.scene.json +43 -0
  41. package/editor/assets/debug-C3qeBD4X.js +0 -3
package/dist/test.d.ts CHANGED
@@ -3,6 +3,40 @@ import { c as JsonValue, i as SceneJson } from "./schema-CFeioQRE.js";
3
3
  import { t as LoadSceneOptions } from "./loader-CeyU_bm1.js";
4
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
1
  import { r as auditScene } from "./replay-DilbZgQI.js";
2
- import { a as registerAllNodes, c as feelReport, d as findPlayer, f as playtest, h as framingText, i as findFloatingProps, l as feelText, m as describeFraming, n as createPlaySession, o as runScript, p as playtestText, r as describeCapture, s as validateScene, t as captureScene, u as failingReplays } from "./test-DuOD1DO8.js";
3
- export { auditScene, captureScene, createPlaySession, describeCapture, describeFraming, failingReplays, feelReport, feelText, findFloatingProps, findPlayer, framingText, playtest, playtestText, registerAllNodes, runScript, validateScene };
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,5 @@
1
+ import { n as FrameSignature } from "./frame-report-Ct8XgsmV.js";
2
+
1
3
  //#region src/vite/discover.d.ts
2
4
  /**
3
5
  * The listening TCP ports in `/proc/net/tcp` (and `tcp6`) format.
@@ -25,7 +27,7 @@ interface FrameHost {
25
27
  root?: string;
26
28
  };
27
29
  middlewares: {
28
- use(path: string, handler: (req: unknown, res: FrameRes) => void): void;
30
+ use(path: string, handler: (req: FrameReq, res: FrameRes) => void): void;
29
31
  };
30
32
  /** Vite's HMR channel. `ws` on v5, `hot` on v6+ — both are accepted. */
31
33
  ws?: FrameChannel;
@@ -44,10 +46,22 @@ interface FrameRes {
44
46
  setHeader(name: string, value: string): void;
45
47
  end(body?: string): void;
46
48
  }
49
+ /** Connect strips the mount path off `url`; `originalUrl` keeps the query. */
50
+ interface FrameReq {
51
+ url?: string;
52
+ originalUrl?: string;
53
+ }
47
54
  interface FrameEndpointOptions {
48
55
  /** Overridden in tests; defaults to the real clock. */
49
56
  now?: () => number;
50
57
  timeoutMs?: number;
58
+ /**
59
+ * Remembered frames, by label. Lives in the dev server's MEMORY on purpose:
60
+ * a baseline is only valid for the browser and the window that made it, so it
61
+ * should die with the server rather than linger in the project as a file
62
+ * nobody asked for.
63
+ */
64
+ store?: Map<string, FrameSignature>;
51
65
  }
52
66
  /**
53
67
  * Wire `/__incanto/ping` and `/__incanto/frame` onto a dev server.
package/dist/vite.js CHANGED
@@ -1,5 +1,6 @@
1
- import { t as VERSION } from "./src-cU57Uwdw.js";
2
- import { s as validateScene } from "./test-DuOD1DO8.js";
1
+ import { t as VERSION } from "./src-B3n06SsL.js";
2
+ import { n as diffSignatures } from "./frame-report-Lr3VO24R.js";
3
+ import { s as validateScene } from "./test-BMgiiD5i.js";
3
4
  import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
4
5
  import { basename, dirname, join, normalize, relative, resolve, sep } from "node:path";
5
6
  //#region src/vite/frame-endpoint.ts
@@ -18,6 +19,8 @@ import { basename, dirname, join, normalize, relative, resolve, sep } from "node
18
19
  */
19
20
  /** How long to wait for a page to answer before calling it absent. */
20
21
  const CLIENT_TIMEOUT_MS = 2e3;
22
+ /** Where a remembered frame goes when nobody named one. */
23
+ const DEFAULT_LABEL = "last";
21
24
  /**
22
25
  * Wire `/__incanto/ping` and `/__incanto/frame` onto a dev server.
23
26
  *
@@ -28,6 +31,7 @@ const CLIENT_TIMEOUT_MS = 2e3;
28
31
  function serveFrameEndpoints(host, version, opts = {}) {
29
32
  const channel = host.hot ?? host.ws;
30
33
  const timeoutMs = opts.timeoutMs ?? CLIENT_TIMEOUT_MS;
34
+ const store = opts.store ?? /* @__PURE__ */ new Map();
31
35
  let nextId = 1;
32
36
  const waiting = /* @__PURE__ */ new Map();
33
37
  channel?.on("incanto:frame-report", (data) => {
@@ -48,11 +52,16 @@ function serveFrameEndpoints(host, version, opts = {}) {
48
52
  frame: Boolean(channel)
49
53
  });
50
54
  });
51
- host.middlewares.use("/__incanto/frame", (_req, res) => {
55
+ host.middlewares.use("/__incanto/frame", (req, res) => {
52
56
  if (!channel) {
53
57
  json$1(res, 503, { error: "no-hmr-channel" });
54
58
  return;
55
59
  }
60
+ const params = query(req);
61
+ const remember = params.get("remember");
62
+ const diff = params.get("diff");
63
+ const threshold = Number(params.get("threshold"));
64
+ const grid = parseGrid(params.get("grid"));
56
65
  const id = nextId++;
57
66
  const timer = setTimeout(() => {
58
67
  waiting.delete(id);
@@ -60,16 +69,50 @@ function serveFrameEndpoints(host, version, opts = {}) {
60
69
  }, timeoutMs);
61
70
  waiting.set(id, (value) => {
62
71
  clearTimeout(timer);
63
- if (value.ok) json$1(res, 200, { report: value.report });
64
- else json$1(res, 500, { error: "capture-failed" });
72
+ if (!value.ok) {
73
+ json$1(res, 500, { error: "capture-failed" });
74
+ return;
75
+ }
76
+ const full = value.report;
77
+ const signature = full?.signature;
78
+ const { signature: _drop, ...report } = full ?? {};
79
+ const body = { report };
80
+ if (diff !== null) {
81
+ const before = store.get(diff || DEFAULT_LABEL);
82
+ if (!signature) body.diffError = "no-signature";
83
+ else if (!before) body.diffError = "no-baseline";
84
+ else body.diff = diffSignatures(before, signature, { threshold: threshold > 0 ? threshold : void 0 });
85
+ }
86
+ if (remember !== null) if (!signature) body.rememberError = "no-signature";
87
+ else {
88
+ store.set(remember || DEFAULT_LABEL, signature);
89
+ body.remembered = remember || DEFAULT_LABEL;
90
+ }
91
+ json$1(res, 200, body);
65
92
  });
66
93
  channel.send({
67
94
  type: "custom",
68
95
  event: "incanto:frame-request",
69
- data: { id }
96
+ data: {
97
+ id,
98
+ grid
99
+ }
70
100
  });
71
101
  });
72
102
  }
103
+ function query(req) {
104
+ const url = req?.originalUrl ?? req?.url ?? "";
105
+ const q = url.indexOf("?");
106
+ return new URLSearchParams(q >= 0 ? url.slice(q + 1) : "");
107
+ }
108
+ /** `16x9` → `[16, 9]`. Anything else is not a grid and is ignored. */
109
+ function parseGrid(text) {
110
+ const m = text?.match(/^(\d+)x(\d+)$/i);
111
+ if (!m) return void 0;
112
+ const cols = Number(m[1]);
113
+ const rows = Number(m[2]);
114
+ return cols > 0 && rows > 0 ? [cols, rows] : void 0;
115
+ }
73
116
  function json$1(res, status, body) {
74
117
  res.statusCode = status;
75
118
  res.setHeader("content-type", "application/json");
@@ -1 +1 @@
1
- import{n as e}from"./index-B1rUkWxB.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
+ import{n as e}from"./index-D9MX3UHF.js";async function t(t){return new n((await e(()=>import(`./GameServer-C56iOUgF.js`),[],import.meta.url)).GameServer,t)}var n=class{raw;active=new Map;reconnecting=!1;disposed=!1;constructor(e,t){this.raw=new e({...t})}get account(){return this.raw.account}get connected(){return this.raw.connected}connect(){return this.raw.connected?Promise.resolve(!0):(this.disposed=!1,this.rawConnect())}rawConnect(){return this.raw.connect({onDisconnect:()=>void this.reconnect()})}disconnect(){this.disposed=!0;for(let e of this.active.values())e.off();return this.active.clear(),this.raw.disconnect()}remoteFunction(e,t,n){return this.raw.remoteFunction(e,t,n)}track(e){let t=Symbol(`sub`),n={make:e,off:e()};return this.active.set(t,n),()=>{n.off(),this.active.delete(t)}}async reconnect(){if(!this.disposed&&!this.reconnecting){this.reconnecting=!0;try{await this.rawConnect();for(let e of this.active.values())e.off(),e.off=e.make()}finally{this.reconnecting=!1}}}subscribeRoomState(e,t){return this.track(()=>this.raw.subscribeRoomState(e,t))}subscribeRoomMyState(e,t){return this.track(()=>this.raw.subscribeRoomMyState(e,t))}subscribeRoomAllUserStates(e,t){return this.track(()=>this.raw.subscribeRoomAllUserStates(e,e=>{let n={};for(let t of e??[]){if(!t||typeof t.account!=`string`||t.__leaved)continue;let{account:e,__updated:r,__leaved:i,...a}=t;n[e]=a}t(n)}))}subscribeRoomCollection(e,t,n){return this.track(()=>this.raw.subscribeRoomCollection(e,t,({items:e})=>{let t={};for(let n of e??[])n&&typeof n.__id==`string`&&(t[n.__id]=n);n(t)}))}onRoomMessage(e,t,n){return this.track(()=>this.raw.onRoomMessage(e,t,n))}onRoomUserJoin(e,t){return this.track(()=>this.raw.onRoomUserJoin(e,t))}onRoomUserLeave(e,t){return this.track(()=>this.raw.onRoomUserLeave(e,t))}subscribeGlobalState(e){return this.track(()=>this.raw.subscribeGlobalState(e))}subscribeGlobalMyState(e){return this.track(()=>this.raw.subscribeGlobalMyState(e))}subscribeGlobalUserState(e,t){return this.track(()=>this.raw.subscribeGlobalUserState(e,t))}subscribeGlobalCollection(e,t){return this.track(()=>this.raw.subscribeGlobalCollection(e,({items:e})=>{let n={};for(let t of e??[])t&&typeof t.__id==`string`&&(n[t.__id]=t);t(n)}))}subscribeAsset(e,t){return this.track(()=>this.raw.subscribeAsset(e,t))}onGlobalMessage(e,t){return this.track(()=>this.raw.onGlobalMessage(e,t))}};export{t as createAgent8Server};
@@ -0,0 +1,3 @@
1
+ import{i as e,r as t,t as n}from"./index-D9MX3UHF.js";function r(e,t,n,r=1){let i=Math.min(e.x0,e.x1)*r,a=Math.max(e.x0,e.x1)*r,o=Math.min(e.y0,e.y1)*r,s=Math.max(e.y0,e.y1)*r,c=Math.max(0,Math.min(Math.round(i),t-1)),l=Math.max(0,Math.min(Math.round(o),n-1));return{x:c,y:l,w:Math.max(1,Math.min(Math.round(a-i),t-c)),h:Math.max(1,Math.min(Math.round(s-o),n-l))}}function i(e,t,n,r){let{x:i,y:a,w:o,h:s}=r,c=new Uint8ClampedArray(o*s*4);for(let n=0;n<s;n++){let r=((a+n)*t+i)*4,s=n*o*4;c.set(e.subarray(r,r+o*4),s)}return{pixels:c,width:o,height:s}}function a(e){if(!e)return`// no scene loaded`;let t=e.source??e;return JSON.stringify(t,null,2)}async function o(e){let t=globalThis.navigator;if(!t?.clipboard?.writeText)return!1;try{return await t.clipboard.writeText(e),!0}catch{return!1}}async function s(e,t){let n=globalThis,r=n.navigator?.clipboard?.write;if(!r||!n.ImageData||!n.ClipboardItem)return!1;try{let i=e.createElement(`canvas`);i.width=t.width,i.height=t.height;let a=i.getContext(`2d`);if(!a)return!1;a.putImageData(new n.ImageData(t.pixels,t.width,t.height),0,0);let o=await new Promise(e=>i.toBlob(e,`image/png`));return o?(await r.call(n.navigator?.clipboard,[new n.ClipboardItem({"image/png":o})]),!0):!1}catch{return!1}}var c=[`nw`,`n`,`ne`,`e`,`se`,`s`,`sw`,`w`],l={nw:[0,0],n:[.5,0],ne:[1,0],e:[1,.5],se:[1,1],s:[.5,1],sw:[0,1],w:[0,.5]},u={nw:`nwse-resize`,se:`nwse-resize`,ne:`nesw-resize`,sw:`nesw-resize`,n:`ns-resize`,s:`ns-resize`,e:`ew-resize`,w:`ew-resize`},d=(e,t,n)=>Math.max(t,Math.min(n,e));function f(e){return{x:Math.min(e.x0,e.x1),y:Math.min(e.y0,e.y1),w:Math.abs(e.x1-e.x0),h:Math.abs(e.y1-e.y0)}}function p(e,t,n,r,i,a){let o=e.x,s=e.y,c=e.x+e.w,l=e.y+e.h;return t.includes(`w`)&&(o=d(o+n,0,i)),t.includes(`e`)&&(c=d(c+n,0,i)),t.includes(`n`)&&(s=d(s+r,0,a)),t.includes(`s`)&&(l=d(l+r,0,a)),{x:Math.min(o,c),y:Math.min(s,l),w:Math.max(1,Math.abs(c-o)),h:Math.max(1,Math.abs(l-s))}}function m(e,t,n,r,i){return{...e,x:d(e.x+t,0,Math.max(0,r-e.w)),y:d(e.y+n,0,Math.max(0,i-e.h))}}function h(e,t,n,r,i,a=8){let o=d(e.x+e.w/2-t/2,a,Math.max(a,r-t-a)),s=e.y+e.h+a,c=e.y-n-a;return{x:o,y:s+n<=i-a?s:c>=a?c:d(s,a,Math.max(a,i-n-a))}}function g(e,t,n,r,i,a,o=180,s=120){let c=Math.max(o,n),l=Math.max(s,r);return{x:Math.min(Math.max(0,e),Math.max(0,i-c)),y:Math.min(Math.max(0,t),Math.max(0,a-l)),w:c,h:l}}function _(e,t){for(let[n,r]of Object.entries(t))e.style[n]=r}var v=`rgba(18, 20, 26, 0.92)`,y=`1px solid rgba(255,255,255,0.14)`,b=`12px ui-monospace, SFMono-Regular, Menlo, monospace`,x=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`),_(this.el,{position:`absolute`,background:v,border:y,borderRadius:`8px`,color:`rgba(255,255,255,0.88)`,font:b,display:`flex`,flexDirection:`column`,overflow:`hidden`,zIndex:`40`,pointerEvents:`auto`,boxShadow:`0 8px 28px rgba(0,0,0,0.45)`});let i=e.createElement(`div`);i.textContent=n,_(i,{padding:`6px 28px 6px 10px`,background:`rgba(255,255,255,0.07)`,cursor:`move`,userSelect:`none`,touchAction:`none`,fontWeight:`700`}),this.el.appendChild(i);let a=e.createElement(`div`);a.textContent=`×`,a.title=`close`,_(a,{position:`absolute`,top:`2px`,right:`8px`,cursor:`pointer`,fontSize:`16px`,lineHeight:`20px`,opacity:`0.7`}),a.addEventListener(`click`,()=>this.onClose()),this.el.appendChild(a),this.body=e.createElement(`div`),_(this.body,{flex:`1`,overflow:`auto`,padding:`8px 10px`}),this.el.appendChild(this.body);let o=e.createElement(`div`);o.textContent=`◢`,_(o,{position:`absolute`,right:`2px`,bottom:`0`,cursor:`nwse-resize`,opacity:`0.5`,userSelect:`none`,touchAction:`none`}),this.el.appendChild(o),this.wireDrag(i,(e,t)=>{this.x+=e,this.y+=t,this.layout()}),this.wireDrag(o,(e,t)=>{this.w+=e,this.h+=t,this.layout()}),this.layout(),t.appendChild(this.el)}remove(){this.el.remove()}layout(){let e=this.host.getBoundingClientRect(),t=g(this.x,this.y,this.w,this.h,e.width,e.height);this.x=t.x,this.y=t.y,this.w=t.w,this.h=t.h,_(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)}},S=4,C=`#6ee7dc`,w=`rgba(18,20,26,0.94)`,T=`12px ui-monospace, Menlo, monospace`;function E(e){let{doc:t,container:n}=e,r=t.createElement(`div`);_(r,{position:`absolute`,inset:`0`,cursor:`crosshair`,zIndex:`80`,pointerEvents:`auto`,userSelect:`none`,touchAction:`none`});let i=t.createElement(`div`);_(i,{position:`absolute`,inset:`0`,background:`rgba(10,14,20,0.35)`,pointerEvents:`none`}),r.appendChild(i);let a=t.createElement(`div`);_(a,{position:`absolute`,outline:`1px solid ${C}`,boxShadow:`0 0 0 9999px rgba(10,14,20,0.35)`,cursor:`grab`,display:`none`,pointerEvents:`auto`}),r.appendChild(a);let o=t.createElement(`div`);_(o,{position:`absolute`,padding:`2px 6px`,borderRadius:`4px`,background:w,color:C,font:T,pointerEvents:`none`,display:`none`,whiteSpace:`nowrap`}),r.appendChild(o);let s=t.createElement(`div`);s.textContent=`drag to select · Esc to cancel`,_(s,{position:`absolute`,top:`10px`,left:`50%`,transform:`translateX(-50%)`,padding:`4px 10px`,borderRadius:`6px`,background:w,color:`rgba(255,255,255,0.85)`,font:T,pointerEvents:`none`,whiteSpace:`nowrap`}),r.appendChild(s);let d=null,g=`idle`,v={x:0,y:0},y={x:0,y:0,w:0,h:0},b=()=>({w:n.clientWidth||1,h:n.clientHeight||1});for(let e of c){let n=t.createElement(`div`),[r,i]=l[e];_(n,{position:`absolute`,left:`${r*100}%`,top:`${i*100}%`,width:`10px`,height:`10px`,marginLeft:`-5px`,marginTop:`-5px`,borderRadius:`2px`,background:C,border:`1px solid rgba(10,14,20,0.75)`,cursor:u[e],pointerEvents:`auto`}),n.addEventListener(`pointerdown`,t=>{k(t,e)}),a.appendChild(n)}let x=t.createElement(`div`);_(x,{position:`absolute`,display:`none`,gap:`6px`,padding:`6px`,borderRadius:`8px`,background:w,border:`1px solid rgba(255,255,255,0.18)`,font:T,pointerEvents:`auto`,whiteSpace:`nowrap`,boxShadow:`0 4px 16px rgba(0,0,0,0.45)`});let E=(e,n,r)=>{let i=t.createElement(`div`);return i.textContent=e,_(i,{padding:`5px 10px`,borderRadius:`5px`,cursor:`pointer`,color:n?`#08121a`:`rgba(255,255,255,0.85)`,background:n?C:`rgba(255,255,255,0.08)`,userSelect:`none`}),i.addEventListener(`pointerdown`,e=>{D(e),r()}),i};x.appendChild(E(`Copy to clipboard`,!0,()=>N())),x.appendChild(E(`Cancel`,!1,()=>I())),r.appendChild(x);function D(e){e.preventDefault?.(),e.stopPropagation?.()}function O(e){let t=e,r=n.getBoundingClientRect?.()??{left:0,top:0};return{x:(t.clientX??0)-r.left,y:(t.clientY??0)-r.top}}function k(e,t){D(e),v=O(e),g=t,t===`create`?d={x:v.x,y:v.y,w:0,h:0}:d&&(y=d),_(x,{display:`none`}),_(a,{cursor:t===`move`?`grabbing`:`crosshair`});let n=e.pointerId;n!==void 0&&r.setPointerCapture?.(n),A()}function A(){if(!d){_(a,{display:`none`}),_(o,{display:`none`}),_(i,{display:`block`});return}_(i,{display:`none`}),_(a,{display:`block`,left:`${d.x}px`,top:`${d.y}px`,width:`${d.w}px`,height:`${d.h}px`});let t=e.scale();o.textContent=`${Math.round(d.w*t)}×${Math.round(d.h*t)}`;let n=d.y-24;_(o,{display:`block`,left:`${Math.max(2,d.x)}px`,top:`${n>=2?n:d.y+4}px`})}function j(){if(!d||d.w<S||d.h<S)return;_(x,{display:`flex`});let e=b(),t=x.offsetWidth||200,n=x.offsetHeight||36,r=h(d,t,n,e.w,e.h);_(x,{left:`${r.x}px`,top:`${r.y}px`})}r.addEventListener(`pointerdown`,e=>{k(e,`create`)}),a.addEventListener(`pointerdown`,e=>{k(e,`move`)}),r.addEventListener(`pointermove`,e=>{if(g===`idle`||!d)return;let t=O(e),n=b();if(g===`create`){let e=f({x0:v.x,y0:v.y,x1:t.x,y1:t.y});d={x:Math.max(0,Math.min(e.x,n.w)),y:Math.max(0,Math.min(e.y,n.h)),w:Math.min(e.w,n.w-Math.max(0,Math.min(e.x,n.w))),h:Math.min(e.h,n.h-Math.max(0,Math.min(e.y,n.h)))}}else d=g===`move`?m(y,t.x-v.x,t.y-v.y,n.w,n.h):p(y,g,t.x-v.x,t.y-v.y,n.w,n.h);A()});let M=()=>{g!==`idle`&&(g===`create`&&d&&(d.w<S||d.h<S)&&(d=null),g=`idle`,_(a,{cursor:`grab`}),s.textContent=d?`drag the handles or the middle · Enter to copy · Esc to cancel`:`drag to select · Esc to cancel`,A(),j())};r.addEventListener(`pointerup`,M),r.addEventListener(`pointercancel`,M);function N(){let t=d;I(),t&&e.onCopy(t)}let P=e=>{let t=e.key;t===`Escape`?I():t===`Enter`&&d&&N()};t.addEventListener?.(`keydown`,P);let F=!1;function I(){F||(F=!0,t.removeEventListener?.(`keydown`,P),r.remove(),e.onClose())}return n.appendChild(r),{element:r,close:I}}var D=96,O=200,k=8192;function A(e){return Array.isArray(e)?`Array(${e.length})`:`Object(${Object.keys(e).length} keys)`}function j(e,t){if(Array.isArray(e)&&e.some(e=>typeof e==`object`&&!!e)){let t=e.slice(0,O).map(e=>JSON.stringify(e)),n=e.length-O;return t.join(`
2
+ `)+(n>0?`\n… ${n} more`:``)}return t.length>k?`${t.slice(0,k)} … (${t.length-k} more chars)`:t}function M(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}var N=2;function P(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 L(e,r,n,t.statsSource,t.actions,t.frameSource)}var F=300,I=[`log`,`info`,`warn`,`error`,`debug`],L=class{engine;container;doc;statsSource;actions;frameSource;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,i=[],a){this.engine=e,this.container=t,this.doc=n,this.statsSource=r,this.actions=i,this.frameSource=a,this.menuButton=n.createElement(`div`),this.menuButton.textContent=`☰ debug`,_(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(B).join(` `)})})),this.cleanups.push(e.sceneChanged.connect(()=>{this.selected=null,this.applyColliderScope(),this.renderExplorer(),this.renderInspector()})),this.cleanups.push(e.updated.connect(()=>{this.frame+=1,this.frame%30==0&&(this.renderExplorer(),this.renderStats(),this.editing===0&&!this.hovering&&this.renderInspector(),this.syncTime())}))}isOpen(e){return e===`copyScene`||e===`captureRegion`?!1:e===`colliders`?this.colliderMode!==`off`:e===`stats`?this.statsChip!==null:this.panels.has(e)}colliderMode=`off`;setColliders(e){this.colliderMode=e,this.applyColliderScope()}applyColliderScope(){let e=this.colliderMode;for(let t of n(`2d`).concat(n(`3d`)))t.debugDraw=e!==`off`,t.debugScope=e===`selected`?this.selected:null}open(e){if(e===`stats`){this.openStatsChip();return}if(this.panels.has(e)){e===`explorer`&&this.renderExplorer(),e===`inspector`&&this.renderInspector(),e===`logs`&&this.renderLogs(),e===`time`&&this.renderTime();return}let t=new x(this.doc,this.container,{explorer:`Explorer`,inspector:`Inspector`,logs:`Logs`,time:`Time`}[e],{explorer:{x:12,y:44,w:240,h:320},inspector:{x:264,y:44,w:280,h:320},logs:{x:12,y:380,w:532,h:200},time:{x:556,y:44,w:260,h:150}}[e]);t.onClose=()=>this.close(e),e===`inspector`&&(t.body.addEventListener(`pointerenter`,()=>{this.hovering=!0}),t.body.addEventListener(`pointerleave`,()=>{this.hovering=!1})),this.panels.set(e,t),e===`explorer`&&this.renderExplorer(),e===`inspector`&&this.renderInspector(),e===`logs`&&this.renderLogs(),e===`time`&&this.renderTime()}close(e){if(e===`inspector`&&(this.hovering=!1),e===`stats`){this.statsChip?.remove(),this.statsChip=null;return}let t=this.panels.get(e);t&&(t.remove(),this.panels.delete(e))}toggle(e){if(e===`copyScene`){this.copyScene();return}if(e===`captureRegion`){this.captureRegion();return}if(e===`colliders`){this.setColliders({off:`all`,all:`selected`,selected:`off`}[this.colliderMode]);return}this.isOpen(e)?this.close(e):this.open(e)}setLevelEnabled(e,t){this.levelEnabled[e]=t,this.renderLogs()}setConsoleCapture(e){if(e!==this.consoleCapture)if(this.consoleCapture=e,e){this.renderLogs();let e=console;for(let t of I){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(B).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`);_(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`],[`copyScene`,`Copy scene JSON`],[`captureRegion`,`Capture region`],[`stats`,`Stats`],[`colliders`,`Colliders`]]){let r=this.doc.createElement(`div`);r.textContent=this.menuLabel(t,n),_(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,_(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`);_(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 ${z(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;R(e.body);let n=this.engine.scene?.root;if(!n){e.body.scrollTop=t;return}let r=(e,t)=>{let n=e.constructor,i=e.children.length>0,a=this.collapsedFlags.get(e)===!0,o=this.doc.createElement(`div`);_(o,{display:`flex`,alignItems:`center`,cursor:`pointer`,padding:`1px 2px`,borderRadius:`3px`,background:e===this.selected?`rgba(110,160,255,0.25)`:`transparent`});let s=this.doc.createElement(`span`);s.textContent=i?a?`▸`:`▾`:`·`,_(s,{width:`14px`,flex:`none`,textAlign:`center`,opacity:i?`0.85`:`0.25`,userSelect:`none`}),i&&s.addEventListener(`click`,t=>{t.stopPropagation?.(),this.collapsedFlags.set(e,!a),this.renderExplorer()}),o.appendChild(s);let c=this.doc.createElement(`span`);c.textContent=e.name,_(c,{whiteSpace:`nowrap`}),o.appendChild(c);let l=this.doc.createElement(`span`);if(l.textContent=` ${n.typeName}`,_(l,{opacity:`0.45`,whiteSpace:`nowrap`,fontSize:`10px`}),o.appendChild(l),i&&a){let t=this.doc.createElement(`span`);t.textContent=` (${e.children.length})`,_(t,{opacity:`0.35`,fontSize:`10px`}),o.appendChild(t)}if(o.addEventListener(`click`,()=>{this.selected=e,this.engine.debugSelection=e,this.applyColliderScope(),this.open(`inspector`),this.renderExplorer(),this.renderInspector()}),t.appendChild(o),i&&!a){let n=this.doc.createElement(`div`);_(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;R(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`,_(t,{opacity:`0.6`}),e.body.appendChild(t);return}let i=r.constructor,a=this.doc.createElement(`div`);if(a.textContent=`${r.getPath()} · ${i.typeName}${r.uid?` · ${r.uid}`:``}`,_(a,{fontWeight:`700`,marginBottom:`6px`,whiteSpace:`pre-wrap`}),e.body.appendChild(a),r.groups.size>0){let t=this.doc.createElement(`div`);t.textContent=`groups: ${[...r.groups].join(`, `)}`,_(t,{opacity:`0.7`,marginBottom:`6px`}),e.body.appendChild(t)}let o=t(i),s=r;for(let t of Object.keys(o)){let n=o[t];this.renderValueRow(e.body,r,t,{read:()=>s[t],write:e=>{s[t]=e},options:n?.options,variants:n?.variants})}e.body.scrollTop=n}renderValueRow(t,n,r,i){let a=i.read(),o=i.options,s={get[r](){return i.read()},set[r](e){i.write(e)}},c=this.doc.createElement(`div`);_(c,{display:`flex`,gap:`6px`,alignItems:`center`,margin:`2px 0`});let l=this.doc.createElement(`div`);l.textContent=r,_(l,{minWidth:`84px`,opacity:`0.75`}),c.appendChild(l);let u=(e,t)=>{let i=this.doc.createElement(`input`);return i.type=`number`,i.value=String(e),_(i,H(`70px`)),this.trackEditing(i),i.addEventListener(`change`,()=>{let e=Number(i.value);Number.isFinite(e)?t(e):i.value=String(n[r])}),i};if(typeof a==`number`)c.appendChild(u(a,e=>{s[r]=e}));else if(typeof a==`boolean`){let e=this.doc.createElement(`input`);e.type=`checkbox`,e.checked=a,this.trackEditing(e),e.addEventListener(`change`,()=>{s[r]=e.checked}),c.appendChild(e)}else if(typeof a==`string`&&o&&o.length>0){let e=this.doc.createElement(`select`);for(let t of o.includes(a)?o:[a,...o]){let n=this.doc.createElement(`option`);n.value=t,n.textContent=t,t===a&&(n.selected=!0),e.appendChild(n)}e.value=a,this.trackEditing(e),e.addEventListener(`change`,()=>{s[r]=e.value}),_(e,H(`110px`)),c.appendChild(e)}else if(typeof a==`string`){let e=this.doc.createElement(`input`);e.type=`text`,e.value=a,this.trackEditing(e),_(e,H(`140px`)),e.addEventListener(`change`,()=>{s[r]=e.value}),c.appendChild(e)}else if(Array.isArray(a)&&a.length<=8&&a.every(e=>typeof e==`number`))for(let e=0;e<a.length;e++)c.appendChild(u(a[e],t=>{let n=[...s[r]];n[e]=t,s[r]=n}));else if(M(a)){let o=this.doc.createElement(`div`);_(o,{marginLeft:`10px`,paddingLeft:`8px`,borderLeft:`1px solid rgba(255,255,255,0.14)`});let s=i.variants?.tag,l=Object.keys(a);s&&!l.includes(s)&&l.unshift(s);let u=i.path??r;for(let t of l){let r=t===s;this.renderValueRow(o,n,t,{path:`${u}.${t}`,read:()=>i.read()[t]??(r?``:null),write:n=>{if(r&&i.variants){let t=i.variants.byTag[String(n)];if(t!==void 0){i.write(e(t));return}}i.write({...i.read(),[t]:n})},options:r&&i.variants?Object.keys(i.variants.byTag):void 0})}t.appendChild(c),t.appendChild(o);return}else{let o=JSON.stringify(e(a));if(o.length<=D){let e=this.doc.createElement(`div`);e.textContent=o,_(e,{opacity:`0.65`,whiteSpace:`pre-wrap`,wordBreak:`break-all`}),c.appendChild(e)}else{let e=i.path??r,s=this.detailOpen.get(n)?.has(e)??!1,l=this.doc.createElement(`div`);if(l.textContent=`${s?`▾`:`▸`} ${A(a)}`,_(l,{opacity:`0.75`,cursor:`pointer`,userSelect:`none`}),l.addEventListener(`click`,()=>{let t=this.detailOpen.get(n);t||(t=new Set,this.detailOpen.set(n,t)),s?t.delete(e):t.add(e),this.renderInspector()}),c.appendChild(l),t.appendChild(c),s){let e=this.doc.createElement(`div`);e.textContent=j(a,o),_(e,{opacity:`0.6`,whiteSpace:`pre-wrap`,wordBreak:`break-all`,marginLeft:`10px`,paddingLeft:`8px`,borderLeft:`1px solid rgba(255,255,255,0.14)`}),t.appendChild(e)}return}}t.appendChild(c)}trackEditing(e){e.addEventListener(`focus`,()=>{this.editing+=1}),e.addEventListener(`blur`,()=>{this.editing=Math.max(0,this.editing-1)})}pushLog(e){this.logRows.push(e),this.logRows.length>F&&this.logRows.splice(0,this.logRows.length-F),this.renderLogs()}async copyScene(){let e=await o(a(this.engine.scene??null));return this.toast(e?`scene JSON copied`:`could not reach the clipboard`),e}captureRegion(){this.capture||=(this.captureScale=globalThis.devicePixelRatio??1,this.frameSource?.().then(e=>{this.captureScale=e.width/Math.max(1,this.container.clientWidth||e.width)}).catch(()=>{}),E({doc:this.doc,container:this.container,scale:()=>this.captureScale,onCopy:e=>void this.copyRegion({x0:e.x,y0:e.y,x1:e.x+e.w,y1:e.y+e.h}),onClose:()=>{this.capture=null}}))}capture=null;captureScale=1;async copyRegion(e){let t=this.frameSource;if(!t)return this.toast(`no renderer to capture from`),!1;try{let n=await t(),a=n.width/Math.max(1,this.container.clientWidth||n.width),o=r(e,n.width,n.height,a),c=i(n.pixels,n.width,n.height,o),l=await s(this.doc,c);return this.toast(l?`copied ${c.width}×${c.height}`:`could not copy the image`),l}catch(e){return this.toast(`capture failed: ${e instanceof Error?e.message:String(e)}`),!1}}toast(e){let t=this.doc.createElement(`div`);t.textContent=e,_(t,{position:`absolute`,bottom:`16px`,left:`50%`,transform:`translateX(-50%)`,padding:`6px 12px`,borderRadius:`6px`,background:`rgba(18,20,26,0.92)`,color:`rgba(255,255,255,0.9)`,font:`12px ui-monospace, Menlo, monospace`,zIndex:`90`,pointerEvents:`none`}),this.container.appendChild(t),setTimeout(()=>t.remove(),1800)}setTimeScale(e){Number.isFinite(e)&&(this.engine.timeScale=Math.max(0,e),this.syncTime())}setPaused(e){e?(this.engine.timeScale>0&&(this.resumeScale=this.engine.timeScale),this.engine.timeScale=0):this.engine.timeScale=this.resumeScale>0?this.resumeScale:1,this.syncTime()}get paused(){return this.engine.timeScale===0}nextFrame(){this.paused||this.setPaused(!0),this.engine.step(),this.syncTime()}syncTime(){let e=this.timeEls;if(!e)return;let t=this.engine.timeScale,n=String(Math.round(t*1e3)/1e3);this.editing===0&&(e.slider.value=String(Math.min(t,N)),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;R(e.body),this.timeEls=null;let t=this.doc.createElement(`div`);_(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(N),n.step=`0.05`,n.value=String(Math.min(this.engine.timeScale,N)),_(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`);_(r,{display:`flex`,gap:`6px`,alignItems:`center`,marginBottom:`8px`});let i=this.doc.createElement(`input`);i.type=`number`,i.min=`0`,i.step=`0.05`,i.value=String(this.engine.timeScale),_(i,H(`72px`)),this.trackEditing(i),i.addEventListener(`change`,()=>{let e=Number(i.value);Number.isFinite(e)&&this.setTimeScale(e),this.syncTimeAfterEdit()}),r.appendChild(i);for(let e of[.25,.5,1,2]){let t=this.doc.createElement(`div`);t.textContent=`${e}×`,_(t,{cursor:`pointer`,opacity:`0.7`,padding:`2px 4px`}),t.addEventListener(`click`,()=>this.setTimeScale(e)),r.appendChild(t)}e.body.appendChild(r);let a=this.doc.createElement(`div`);_(a,{display:`flex`,gap:`8px`});let o=this.doc.createElement(`div`);_(o,V()),o.addEventListener(`click`,()=>this.setPaused(!this.paused)),a.appendChild(o);let s=this.doc.createElement(`div`);s.textContent=`⏭ Next frame`,s.title=`Pauses, then advances one fixed step`,_(s,V()),s.addEventListener(`click`,()=>this.nextFrame()),a.appendChild(s),e.body.appendChild(a),this.timeEls={slider:n,box:i,readout:t,pause:o},this.syncTime()}syncTimeAfterEdit(){let e=this.timeEls;if(!e)return;let t=this.engine.timeScale;e.slider.value=String(Math.min(t,N)),e.box.value=String(Math.round(t*1e3)/1e3)}renderLogs(){let e=this.panels.get(`logs`);if(!e)return;R(e.body);let t=this.doc.createElement(`div`);_(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}`,_(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`,_(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}`,_(n,{color:r[t.level],whiteSpace:`pre-wrap`}),e.body.appendChild(n)}}};function R(e){for(let t of[...e.children])t.remove()}function z(e){return e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}k`:String(e)}function B(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 V(){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 H(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{P as attachDebugOverlay};