import { Ft as LogLevel, T as RendererStats, b as Engine } from "./behavior-B_245qRy.js"; //#region src/debug/panel.d.ts /** Minimal document surface the overlay needs (injectable for tests). */ interface DocumentLike { /** Optional: present on a real document, absent in the test double. */ addEventListener?(type: string, cb: (event: unknown) => void): void; removeEventListener?(type: string, cb: (event: unknown) => void): void; createElement(tag: string): HTMLElement; /** * The viewport, when the overlay's own container cannot supply one — see * {@link overlayViewport}. Optional because the test double is not a * document and a headless run has no window at all. */ documentElement?: { clientWidth?: number; clientHeight?: number; }; } //#endregion //#region src/debug/index.d.ts type DebugPanelId = "explorer" | "inspector" | "logs" | "stats" | "time"; /** Menu entries that DO something once rather than opening a panel. */ type DebugCommandId = "copyScene" | "captureRegion"; /** Menu entries that are switches rather than panels. */ type DebugSwitchId = "colliders"; /** `all` is every collider in the world; `selected` follows the Explorer. */ type ColliderMode = "off" | "all" | "selected"; type DebugMenuId = DebugPanelId | DebugSwitchId | DebugCommandId; /** An extra ☰ menu entry the host app supplies (e.g. "edit this scene"). */ interface DebugAction { /** Menu label. Keep it a verb — the menu is a list of things you can do. */ label: string; /** Runs on click. The menu closes first, so a slow action can await freely. */ run(): void | Promise; } interface DebugOverlayOptions { /** Overlay host (default: document.body). Give it position: relative. */ container?: HTMLElement; /** * Extra menu entries, listed under the panels with a separator. * * This is how a page hangs its own tooling off the overlay without the * overlay having to know what tooling exists — `createGame3D`'s live editor * switch arrives through here. */ actions?: DebugAction[]; /** * Renderer counters for the Stats chip — the overlay only holds the engine, * so createGame wires `() => renderer.stats()` here. Omitted (headless/stub * renderers): the chip shows engine stats only. */ statsSource?: () => Partial; /** * The renderer's pixels, for the region capture. Wired by createGame like * `statsSource`, because the overlay holds the engine and not the renderer. * * It must be `captureFrame()` and not `canvas.toDataURL()`: the drawing * buffer is gone by the time anything outside a render can read it, and the * image comes back black. That trap has been hit here before. */ frameSource?: () => Promise<{ pixels: Uint8Array; width: number; height: number; }>; /** @internal Test seam — replaces `document`. */ doc?: DocumentLike; } /** Attach the overlay; null when no DOM is available (headless). */ declare function attachDebugOverlay(engine: Engine, opts?: DebugOverlayOptions): DebugOverlay | null; declare class DebugOverlay { private readonly engine; private readonly container; private readonly doc; private readonly statsSource?; private readonly actions; private readonly frameSource?; private readonly panels; private readonly cleanups; private readonly menuButton; private dropdown; private dismissDropdown; /** Where the ☰ button sits, in container CSS px. Drag moves it; see bindMenuDrag. */ private menuPos; private selected; /** Explorer subtrees the user collapsed (nodes keep identity across frames). */ private readonly collapsedFlags; private statsChip; private readonly logRows; private readonly levelEnabled; private consoleCapture; private consolePatched; private frame; private editing; /** Which bulky values are unfolded, per node ('transforms', 'collider.vertices'). */ private readonly detailOpen; /** * The scale to come back to when a pause is lifted. * * Captured only from a RUNNING scale: pausing an already-frozen game (a * hit-stop, a previous pause) must not record 0 as the thing to restore, or * Resume does nothing and the game looks hung. */ private resumeScale; private timeEls; /** Pointer inside the inspector — it stops refreshing under your hand. */ private hovering; constructor(engine: Engine, container: HTMLElement, doc: DocumentLike, statsSource?: (() => Partial) | undefined, actions?: DebugAction[], frameSource?: DebugOverlayOptions["frameSource"] | undefined); isOpen(id: DebugMenuId): boolean; /** How much of the physics world the wireframes show. */ colliderMode: ColliderMode; /** * Physics wireframes, from the menu instead of from the console. What you see * is the shape data the solver holds — the fastest way to catch a collider * that disagrees with the art it belongs to. * * `'selected'` narrows it to the Explorer's selection, because `'all'` on a * real map is a terrain grid burying the one shape you came to look at. */ setColliders(mode: ColliderMode): void; private applyColliderScope; open(id: DebugPanelId): void; close(id: DebugPanelId): void; toggle(id: DebugMenuId): void; setLevelEnabled(level: LogLevel, on: boolean): void; /** * Patch console.* into the Logs panel (restored on disable/dispose). * Standard monkey-patch hazard: a tool that patches console AFTER capture * enables will be clobbered by our restore — enable capture last. */ setConsoleCapture(on: boolean): void; dispose(): void; /** `✓ Colliders · selected` — check for on, suffix for the collider mode. */ private menuLabel; /** * The ☰ button is draggable, because top-left is exactly where a game puts * its own menu and a debug chip pinned on top of it is a debug chip you * cannot use. * * Drag and click share one pointer, so they are told apart by distance: under * the threshold the press was a click and the menu opens, over it the press * was a grab and the menu must NOT open under the finger that just let go. */ private bindMenuDrag; private moveMenuButton; private closeDropdown; private toggleDropdown; private openStatsChip; private renderStats; private renderExplorer; private renderInspector; /** * One editable row. `read`/`write` are closures rather than a (node, key) * pair so the SAME renderer serves a node prop and a key INSIDE an object * prop — `collider.radius` gets a real number field instead of being a * character in an ellipsised JSON dump. */ private renderValueRow; private trackEditing; private pushLog; /** * The scene, on the clipboard, ready to paste into a conversation. * * This is half of "here is what I am looking at" — the half a screenshot * cannot carry. The other half is the region capture below. */ copyScene(): Promise; /** * Drag a rectangle over the game; the selection copies as an image. * * The pixels come from `frameSource` — the renderer's own end-of-frame read — * and never from `canvas.toDataURL()`, which returns black once the frame has * composited unless `preserveDrawingBuffer` is on (it is not, because it costs * bandwidth on every frame). */ captureRegion(): void; private capture; private captureScale; /** Crop the last frame to `drag` and put the image on the clipboard. */ private copyRegion; /** A line that says what happened and gets out of the way. */ private toast; /** * Set game time, defensively. `timeScale` multiplies every dt in the engine, * so a NaN from a text field would poison physics, timers and animation in * one frame, and a negative would run the simulation backwards through code * that has never been asked to. */ setTimeScale(next: number): void; /** Freeze or unfreeze game time. Rendering keeps running — this is a pause, * not a stop: the camera still moves and the overlay stays interactive. */ setPaused(paused: boolean): void; get paused(): boolean; /** * Advance exactly one frame, pausing first. * * Stepping a RUNNING game is meaningless — the next real frame lands a * millisecond later and swamps the step — so this pauses and then steps, * which is also the only way the button is useful twice in a row. */ nextFrame(): void; /** Push the engine's current time state into the panel, without rebuilding it. */ private syncTime; private renderTime; /** After a committed edit the field must show what the ENGINE took — a * rejected 'abc' or a clamped -2 has to be visible, not left on screen. */ private syncTimeAfterEdit; private renderLogs; } //#endregion export { ColliderMode, DebugAction, DebugCommandId, DebugMenuId, DebugOverlay, DebugOverlayOptions, DebugPanelId, DebugSwitchId, attachDebugOverlay };