@bloopjs/web 0.0.56 → 0.0.57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/App.d.ts.map +1 -1
- package/dist/debugui/DebugUi.d.ts.map +1 -1
- package/dist/debugui/components/BottomBar.d.ts +8 -0
- package/dist/debugui/components/BottomBar.d.ts.map +1 -0
- package/dist/debugui/components/DebugToggle.d.ts.map +1 -1
- package/dist/debugui/components/Root.d.ts.map +1 -1
- package/dist/debugui/components/TopBar.d.ts +7 -0
- package/dist/debugui/components/TopBar.d.ts.map +1 -0
- package/dist/debugui/components/VerticalBar.d.ts +9 -0
- package/dist/debugui/components/VerticalBar.d.ts.map +1 -0
- package/dist/debugui/state.d.ts +9 -1
- package/dist/debugui/state.d.ts.map +1 -1
- package/dist/debugui/styles.d.ts +1 -1
- package/dist/debugui/styles.d.ts.map +1 -1
- package/dist/mod.js +571 -26
- package/dist/mod.js.map +12 -9
- package/package.json +3 -3
- package/src/App.ts +32 -1
- package/src/debugui/DebugUi.ts +7 -7
- package/src/debugui/components/BottomBar.tsx +68 -0
- package/src/debugui/components/DebugToggle.tsx +2 -6
- package/src/debugui/components/Root.tsx +81 -17
- package/src/debugui/components/TopBar.tsx +36 -0
- package/src/debugui/components/VerticalBar.tsx +26 -0
- package/src/debugui/state.ts +42 -5
- package/src/debugui/styles.ts +232 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bloopjs/web",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.57",
|
|
4
4
|
"author": "Neil Sarkar",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"typescript": "^5"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@bloopjs/bloop": "0.0.
|
|
37
|
-
"@bloopjs/engine": "0.0.
|
|
36
|
+
"@bloopjs/bloop": "0.0.57",
|
|
37
|
+
"@bloopjs/engine": "0.0.57",
|
|
38
38
|
"@preact/signals": "^1.3.1",
|
|
39
39
|
"partysocket": "^1.1.6",
|
|
40
40
|
"preact": "^10.25.4"
|
package/src/App.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from "./netcode/broker";
|
|
8
8
|
import { logger } from "./netcode/logs.ts";
|
|
9
9
|
import { DebugUi, type DebugUiOptions } from "./debugui/mod.ts";
|
|
10
|
+
import { debugState } from "./debugui/state.ts";
|
|
10
11
|
|
|
11
12
|
export type StartOptions = {
|
|
12
13
|
/** A bloop game instance */
|
|
@@ -208,8 +209,38 @@ export class App {
|
|
|
208
209
|
};
|
|
209
210
|
window.addEventListener("keydown", playbarHotkeys);
|
|
210
211
|
|
|
212
|
+
// FPS calculation
|
|
213
|
+
let fpsFrames = 0;
|
|
214
|
+
let fpsLastTime = performance.now();
|
|
215
|
+
|
|
211
216
|
const frame = () => {
|
|
212
|
-
|
|
217
|
+
const stepStart = performance.now();
|
|
218
|
+
const ticks = this.sim.step(stepStart - this.#now);
|
|
219
|
+
|
|
220
|
+
// Update debug metrics only when we actually ran simulation
|
|
221
|
+
if (ticks > 0) {
|
|
222
|
+
const stepEnd = performance.now();
|
|
223
|
+
debugState.frameTime.value = stepEnd - stepStart;
|
|
224
|
+
debugState.frameNumber.value = this.sim.time.frame;
|
|
225
|
+
|
|
226
|
+
// Measure snapshot size when debug UI is visible (letterboxed mode)
|
|
227
|
+
if (debugState.layoutMode.value === "letterboxed") {
|
|
228
|
+
const bag = this.game.bag;
|
|
229
|
+
if (bag) {
|
|
230
|
+
debugState.snapshotSize.value = JSON.stringify(bag).length;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Calculate FPS every second
|
|
235
|
+
fpsFrames++;
|
|
236
|
+
const elapsed = stepEnd - fpsLastTime;
|
|
237
|
+
if (elapsed >= 1000) {
|
|
238
|
+
debugState.fps.value = Math.round((fpsFrames * 1000) / elapsed);
|
|
239
|
+
fpsFrames = 0;
|
|
240
|
+
fpsLastTime = stepEnd;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
213
244
|
if (!this.sim.isPaused) {
|
|
214
245
|
try {
|
|
215
246
|
this.afterFrame.notify(this.sim.time.frame);
|
package/src/debugui/DebugUi.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ComponentChild, render } from "preact";
|
|
2
2
|
import { Root } from "./components/Root.tsx";
|
|
3
|
-
import { type DebugState, debugState } from "./state.ts";
|
|
3
|
+
import { type DebugState, cycleLayout, debugState } from "./state.ts";
|
|
4
4
|
import { styles } from "./styles.ts";
|
|
5
5
|
|
|
6
6
|
export type DebugUiOptions = {
|
|
@@ -30,7 +30,7 @@ export class DebugUi {
|
|
|
30
30
|
// Create host element
|
|
31
31
|
this.#host = document.createElement("bloop-debug-ui");
|
|
32
32
|
this.#host.style.cssText =
|
|
33
|
-
"display:block;width:100%;height:100%;position:absolute;top:0;left:0;";
|
|
33
|
+
"display:block;width:100%;height:100%;position:absolute;top:0;left:0;overflow:hidden;overscroll-behavior:none;";
|
|
34
34
|
|
|
35
35
|
// Attach shadow DOM
|
|
36
36
|
this.#shadow = this.#host.attachShadow({ mode: "open" });
|
|
@@ -47,7 +47,7 @@ export class DebugUi {
|
|
|
47
47
|
this.#shadow.appendChild(this.#mountPoint);
|
|
48
48
|
|
|
49
49
|
// Initialize state
|
|
50
|
-
debugState.
|
|
50
|
+
debugState.layoutMode.value = initiallyVisible ? "letterboxed" : "off";
|
|
51
51
|
|
|
52
52
|
// Create canvas element (game renders here)
|
|
53
53
|
this.#canvas = document.createElement("canvas");
|
|
@@ -61,8 +61,8 @@ export class DebugUi {
|
|
|
61
61
|
// Set up hotkey listener
|
|
62
62
|
this.#cleanup = this.#setupHotkey();
|
|
63
63
|
|
|
64
|
-
// Re-render when
|
|
65
|
-
debugState.
|
|
64
|
+
// Re-render when layoutMode changes
|
|
65
|
+
debugState.layoutMode.subscribe(() => {
|
|
66
66
|
this.#render();
|
|
67
67
|
});
|
|
68
68
|
}
|
|
@@ -77,7 +77,7 @@ export class DebugUi {
|
|
|
77
77
|
#setupHotkey(): () => void {
|
|
78
78
|
const handler = (e: KeyboardEvent) => {
|
|
79
79
|
if (e.key === this.#hotkey) {
|
|
80
|
-
|
|
80
|
+
cycleLayout();
|
|
81
81
|
}
|
|
82
82
|
};
|
|
83
83
|
window.addEventListener("keydown", handler);
|
|
@@ -100,7 +100,7 @@ export class DebugUi {
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
set isVisible(value: boolean) {
|
|
103
|
-
debugState.
|
|
103
|
+
debugState.layoutMode.value = value ? "letterboxed" : "off";
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
unmount(): void {
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
type BottomBarProps = {
|
|
2
|
+
tapeUtilization: number; // 0-1, how full the tape is
|
|
3
|
+
playheadPosition: number; // 0-1, current position in tape
|
|
4
|
+
isPlaying: boolean;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export function BottomBar({
|
|
8
|
+
tapeUtilization = 0,
|
|
9
|
+
playheadPosition = 0,
|
|
10
|
+
isPlaying = true,
|
|
11
|
+
}: BottomBarProps) {
|
|
12
|
+
// Placeholder handlers - behavior not wired up yet
|
|
13
|
+
const handleJumpBack = () => {};
|
|
14
|
+
const handleStepBack = () => {};
|
|
15
|
+
const handlePlayPause = () => {};
|
|
16
|
+
const handleStepForward = () => {};
|
|
17
|
+
const handleJumpForward = () => {};
|
|
18
|
+
const handleSeek = () => {};
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div className="bottom-bar">
|
|
22
|
+
<div className="playbar-controls">
|
|
23
|
+
<button className="playbar-btn" onClick={handleJumpBack}>
|
|
24
|
+
{"<<"}
|
|
25
|
+
<span className="tooltip tooltip-left">
|
|
26
|
+
Jump back <kbd>4</kbd>
|
|
27
|
+
</span>
|
|
28
|
+
</button>
|
|
29
|
+
<button className="playbar-btn" onClick={handleStepBack}>
|
|
30
|
+
{"<"}
|
|
31
|
+
<span className="tooltip">
|
|
32
|
+
Step back <kbd>5</kbd>
|
|
33
|
+
</span>
|
|
34
|
+
</button>
|
|
35
|
+
<button className="playbar-btn" onClick={handlePlayPause}>
|
|
36
|
+
{isPlaying ? "||" : ">"}
|
|
37
|
+
<span className="tooltip">
|
|
38
|
+
{isPlaying ? "Pause" : "Play"} <kbd>6</kbd>
|
|
39
|
+
</span>
|
|
40
|
+
</button>
|
|
41
|
+
<button className="playbar-btn" onClick={handleStepForward}>
|
|
42
|
+
{">"}
|
|
43
|
+
<span className="tooltip">
|
|
44
|
+
Step forward <kbd>7</kbd>
|
|
45
|
+
</span>
|
|
46
|
+
</button>
|
|
47
|
+
<button className="playbar-btn" onClick={handleJumpForward}>
|
|
48
|
+
{">>"}
|
|
49
|
+
<span className="tooltip">
|
|
50
|
+
Jump forward <kbd>8</kbd>
|
|
51
|
+
</span>
|
|
52
|
+
</button>
|
|
53
|
+
</div>
|
|
54
|
+
<div className="seek-bar" onClick={handleSeek}>
|
|
55
|
+
<div
|
|
56
|
+
className="seek-bar-fill"
|
|
57
|
+
style={{ width: `${tapeUtilization * 100}%` }}
|
|
58
|
+
/>
|
|
59
|
+
{tapeUtilization > 0 && (
|
|
60
|
+
<div
|
|
61
|
+
className="seek-bar-position"
|
|
62
|
+
style={{ left: `${playheadPosition * tapeUtilization * 100}%` }}
|
|
63
|
+
/>
|
|
64
|
+
)}
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { debugState } from "../state.ts";
|
|
1
|
+
import { cycleLayout, debugState } from "../state.ts";
|
|
2
2
|
|
|
3
3
|
type DebugToggleProps = {
|
|
4
4
|
hotkey?: string;
|
|
@@ -7,14 +7,10 @@ type DebugToggleProps = {
|
|
|
7
7
|
export function DebugToggle({ hotkey = "Escape" }: DebugToggleProps) {
|
|
8
8
|
const isVisible = debugState.isVisible.value;
|
|
9
9
|
|
|
10
|
-
const toggle = () => {
|
|
11
|
-
debugState.isVisible.value = !debugState.isVisible.value;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
10
|
return (
|
|
15
11
|
<button
|
|
16
12
|
className="debug-toggle"
|
|
17
|
-
onClick={
|
|
13
|
+
onClick={cycleLayout}
|
|
18
14
|
onMouseDown={(e) => e.stopPropagation()}
|
|
19
15
|
onMouseUp={(e) => e.stopPropagation()}
|
|
20
16
|
title={isVisible ? `Hide debug (${hotkey})` : `Show debug (${hotkey})`}
|
|
@@ -3,6 +3,9 @@ import { debugState } from "../state.ts";
|
|
|
3
3
|
import { Stats } from "./Stats.tsx";
|
|
4
4
|
import { Logs } from "./Logs.tsx";
|
|
5
5
|
import { DebugToggle } from "./DebugToggle.tsx";
|
|
6
|
+
import { TopBar } from "./TopBar.tsx";
|
|
7
|
+
import { VerticalBar } from "./VerticalBar.tsx";
|
|
8
|
+
import { BottomBar } from "./BottomBar.tsx";
|
|
6
9
|
|
|
7
10
|
type RootProps = {
|
|
8
11
|
canvas: HTMLCanvasElement;
|
|
@@ -10,32 +13,93 @@ type RootProps = {
|
|
|
10
13
|
};
|
|
11
14
|
|
|
12
15
|
export function Root({ canvas, hotkey = "Escape" }: RootProps) {
|
|
13
|
-
const
|
|
16
|
+
const layoutMode = debugState.layoutMode.value;
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
<main className="layout">
|
|
19
|
-
<section className="game">
|
|
20
|
-
<GameCanvas canvas={canvas} />
|
|
21
|
-
</section>
|
|
22
|
-
<section className="stats">
|
|
23
|
-
<Stats />
|
|
24
|
-
</section>
|
|
25
|
-
<section className="logs">
|
|
26
|
-
<Logs />
|
|
27
|
-
</section>
|
|
28
|
-
</main>
|
|
29
|
-
) : (
|
|
18
|
+
if (layoutMode === "off") {
|
|
19
|
+
return (
|
|
20
|
+
<>
|
|
30
21
|
<main className="fullscreen">
|
|
31
22
|
<GameCanvas canvas={canvas} />
|
|
32
23
|
</main>
|
|
33
|
-
|
|
24
|
+
<DebugToggle hotkey={hotkey} />
|
|
25
|
+
</>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (layoutMode === "letterboxed") {
|
|
30
|
+
return (
|
|
31
|
+
<>
|
|
32
|
+
<LetterboxedLayout canvas={canvas} />
|
|
33
|
+
<DebugToggle hotkey={hotkey} />
|
|
34
|
+
</>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Full layout (netcode debug)
|
|
39
|
+
return (
|
|
40
|
+
<>
|
|
41
|
+
<main className="layout">
|
|
42
|
+
<section className="game">
|
|
43
|
+
<GameCanvas canvas={canvas} />
|
|
44
|
+
</section>
|
|
45
|
+
<section className="stats">
|
|
46
|
+
<Stats />
|
|
47
|
+
</section>
|
|
48
|
+
<section className="logs">
|
|
49
|
+
<Logs />
|
|
50
|
+
</section>
|
|
51
|
+
</main>
|
|
34
52
|
<DebugToggle hotkey={hotkey} />
|
|
35
53
|
</>
|
|
36
54
|
);
|
|
37
55
|
}
|
|
38
56
|
|
|
57
|
+
function LetterboxedLayout({ canvas }: { canvas: HTMLCanvasElement }) {
|
|
58
|
+
const isOnline = debugState.netStatus.value.peers.length > 0;
|
|
59
|
+
const advantage = debugState.advantage.value ?? 0;
|
|
60
|
+
const frameTime = debugState.frameTime.value;
|
|
61
|
+
const snapshotSize = debugState.snapshotSize.value;
|
|
62
|
+
|
|
63
|
+
// Left bar: frame advantage (online) or frame time % (offline)
|
|
64
|
+
const leftValue = isOnline ? Math.abs(advantage) : frameTime;
|
|
65
|
+
const leftMax = isOnline ? 10 : 16.67; // 10 frames advantage or 16.67ms budget
|
|
66
|
+
const leftLabel = isOnline ? "ADV" : "MS";
|
|
67
|
+
const leftColor = isOnline
|
|
68
|
+
? advantage >= 0
|
|
69
|
+
? "#4a9eff"
|
|
70
|
+
: "#ff4a4a"
|
|
71
|
+
: frameTime > 16.67
|
|
72
|
+
? "#ff4a4a"
|
|
73
|
+
: "#4aff4a";
|
|
74
|
+
|
|
75
|
+
// Right bar: rollback depth (online) or snapshot size (offline)
|
|
76
|
+
// For now, we don't have rollback depth exposed, so use a placeholder
|
|
77
|
+
const rightValue = isOnline ? 0 : snapshotSize;
|
|
78
|
+
const rightMax = isOnline ? 10 : 10000; // 10 frames rollback or 10KB
|
|
79
|
+
const rightLabel = isOnline ? "RB" : "KB";
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<main className="layout-letterboxed">
|
|
83
|
+
<TopBar leftLabel={leftLabel} rightLabel={rightLabel} />
|
|
84
|
+
<VerticalBar
|
|
85
|
+
value={leftValue}
|
|
86
|
+
max={leftMax}
|
|
87
|
+
side="left"
|
|
88
|
+
color={leftColor}
|
|
89
|
+
/>
|
|
90
|
+
<div className="letterboxed-game">
|
|
91
|
+
<GameCanvas canvas={canvas} />
|
|
92
|
+
</div>
|
|
93
|
+
<VerticalBar
|
|
94
|
+
value={rightValue}
|
|
95
|
+
max={rightMax}
|
|
96
|
+
side="right"
|
|
97
|
+
/>
|
|
98
|
+
<BottomBar tapeUtilization={0.67} playheadPosition={0.8} isPlaying={true} />
|
|
99
|
+
</main>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
39
103
|
function GameCanvas({ canvas }: { canvas: HTMLCanvasElement }) {
|
|
40
104
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
41
105
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { debugState } from "../state.ts";
|
|
2
|
+
|
|
3
|
+
type TopBarProps = {
|
|
4
|
+
leftLabel: string;
|
|
5
|
+
rightLabel: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export function TopBar({ leftLabel, rightLabel }: TopBarProps) {
|
|
9
|
+
const fps = debugState.fps.value;
|
|
10
|
+
const frameNumber = debugState.frameNumber.value;
|
|
11
|
+
const rtt = debugState.netStatus.value.rtt;
|
|
12
|
+
const isOnline = debugState.netStatus.value.peers.length > 0;
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<div className="top-bar">
|
|
16
|
+
<span className="top-bar-side-label">{leftLabel}</span>
|
|
17
|
+
<div className="top-bar-center">
|
|
18
|
+
<div className="top-bar-item">
|
|
19
|
+
<span className="top-bar-label">FPS</span>
|
|
20
|
+
<span className="top-bar-value">{fps}</span>
|
|
21
|
+
</div>
|
|
22
|
+
<div className="top-bar-item">
|
|
23
|
+
<span className="top-bar-label">Frame</span>
|
|
24
|
+
<span className="top-bar-value">{frameNumber}</span>
|
|
25
|
+
</div>
|
|
26
|
+
{isOnline && rtt !== null && (
|
|
27
|
+
<div className="top-bar-item">
|
|
28
|
+
<span className="top-bar-label">Ping</span>
|
|
29
|
+
<span className="top-bar-value">{rtt}ms</span>
|
|
30
|
+
</div>
|
|
31
|
+
)}
|
|
32
|
+
</div>
|
|
33
|
+
<span className="top-bar-side-label">{rightLabel}</span>
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
type VerticalBarProps = {
|
|
2
|
+
value: number;
|
|
3
|
+
max: number;
|
|
4
|
+
side: "left" | "right";
|
|
5
|
+
color?: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export function VerticalBar({
|
|
9
|
+
value,
|
|
10
|
+
max,
|
|
11
|
+
side,
|
|
12
|
+
color = "#4a9eff",
|
|
13
|
+
}: VerticalBarProps) {
|
|
14
|
+
const percentage = max > 0 ? Math.min(100, (value / max) * 100) : 0;
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div className={`${side}-bar`}>
|
|
18
|
+
<div className="vertical-bar">
|
|
19
|
+
<div
|
|
20
|
+
className="vertical-bar-fill"
|
|
21
|
+
style={{ height: `${percentage}%`, background: color }}
|
|
22
|
+
/>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
);
|
|
26
|
+
}
|
package/src/debugui/state.ts
CHANGED
|
@@ -8,6 +8,8 @@ import type { Log } from "../netcode/logs.ts";
|
|
|
8
8
|
|
|
9
9
|
export type FrameNumber = number;
|
|
10
10
|
|
|
11
|
+
export type LayoutMode = "off" | "letterboxed" | "full";
|
|
12
|
+
|
|
11
13
|
export type Peer = {
|
|
12
14
|
id: string;
|
|
13
15
|
nickname: string;
|
|
@@ -24,14 +26,20 @@ export type NetStatus = {
|
|
|
24
26
|
};
|
|
25
27
|
|
|
26
28
|
export type DebugState = {
|
|
27
|
-
|
|
29
|
+
layoutMode: Signal<LayoutMode>;
|
|
30
|
+
isVisible: ReadonlySignal<boolean>;
|
|
28
31
|
netStatus: Signal<NetStatus>;
|
|
29
32
|
logs: Signal<Log[]>;
|
|
30
33
|
peer: ReadonlySignal<Peer | null>;
|
|
31
34
|
advantage: ReadonlySignal<number | null>;
|
|
35
|
+
// Metrics for letterboxed layout
|
|
36
|
+
fps: Signal<number>;
|
|
37
|
+
frameTime: Signal<number>; // ms per frame
|
|
38
|
+
snapshotSize: Signal<number>; // bytes
|
|
39
|
+
frameNumber: Signal<number>;
|
|
32
40
|
};
|
|
33
41
|
|
|
34
|
-
const
|
|
42
|
+
const layoutMode = signal<LayoutMode>("off");
|
|
35
43
|
const netStatus = signal<NetStatus>({
|
|
36
44
|
ourId: null,
|
|
37
45
|
remoteId: null,
|
|
@@ -39,10 +47,17 @@ const netStatus = signal<NetStatus>({
|
|
|
39
47
|
peers: [],
|
|
40
48
|
});
|
|
41
49
|
const logs = signal<Log[]>([]);
|
|
50
|
+
const fps = signal(0);
|
|
51
|
+
const frameTime = signal(0);
|
|
52
|
+
const snapshotSize = signal(0);
|
|
53
|
+
const frameNumber = signal(0);
|
|
42
54
|
|
|
43
55
|
export const debugState: DebugState = {
|
|
44
|
-
/**
|
|
45
|
-
|
|
56
|
+
/** Layout mode: off, letterboxed, or full */
|
|
57
|
+
layoutMode,
|
|
58
|
+
|
|
59
|
+
/** Whether debug UI is visible (derived from layoutMode) */
|
|
60
|
+
isVisible: computed(() => layoutMode.value !== "off"),
|
|
46
61
|
|
|
47
62
|
/** Network status */
|
|
48
63
|
netStatus,
|
|
@@ -58,8 +73,26 @@ export const debugState: DebugState = {
|
|
|
58
73
|
const peer = netStatus.value.peers[0];
|
|
59
74
|
return peer ? peer.seq - peer.ack : null;
|
|
60
75
|
}),
|
|
76
|
+
|
|
77
|
+
/** Metrics for letterboxed layout */
|
|
78
|
+
fps,
|
|
79
|
+
frameTime,
|
|
80
|
+
snapshotSize,
|
|
81
|
+
frameNumber,
|
|
61
82
|
};
|
|
62
83
|
|
|
84
|
+
/** Cycle through layout modes: off -> letterboxed -> full -> off */
|
|
85
|
+
export function cycleLayout(): void {
|
|
86
|
+
const current = layoutMode.value;
|
|
87
|
+
if (current === "off") {
|
|
88
|
+
layoutMode.value = "letterboxed";
|
|
89
|
+
} else if (current === "letterboxed") {
|
|
90
|
+
layoutMode.value = "full";
|
|
91
|
+
} else {
|
|
92
|
+
layoutMode.value = "off";
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
63
96
|
export function addLog(log: Log): void {
|
|
64
97
|
debugState.logs.value = [...debugState.logs.value, log];
|
|
65
98
|
}
|
|
@@ -116,7 +149,7 @@ export function clearLogs(): void {
|
|
|
116
149
|
}
|
|
117
150
|
|
|
118
151
|
export function resetState(): void {
|
|
119
|
-
debugState.
|
|
152
|
+
debugState.layoutMode.value = "off";
|
|
120
153
|
debugState.logs.value = [];
|
|
121
154
|
debugState.netStatus.value = {
|
|
122
155
|
ourId: null,
|
|
@@ -124,4 +157,8 @@ export function resetState(): void {
|
|
|
124
157
|
rtt: null,
|
|
125
158
|
peers: [],
|
|
126
159
|
};
|
|
160
|
+
debugState.fps.value = 0;
|
|
161
|
+
debugState.frameTime.value = 0;
|
|
162
|
+
debugState.snapshotSize.value = 0;
|
|
163
|
+
debugState.frameNumber.value = 0;
|
|
127
164
|
}
|