add debug menu & flags

This commit is contained in:
Grant 2024-04-25 19:22:41 -06:00
parent d262be82dd
commit 0ed579c0d7
5 changed files with 257 additions and 1 deletions

View File

@ -4,6 +4,7 @@ import { CanvasWrapper } from "./CanvasWrapper";
import { Pallete } from "./Pallete"; import { Pallete } from "./Pallete";
import { TemplateContext } from "../contexts/TemplateContext"; import { TemplateContext } from "../contexts/TemplateContext";
import { SettingsSidebar } from "./Settings/SettingsSidebar"; import { SettingsSidebar } from "./Settings/SettingsSidebar";
import { DebugModal } from "./Debug/DebugModal";
const App = () => { const App = () => {
return ( return (
@ -13,6 +14,7 @@ const App = () => {
<CanvasWrapper /> <CanvasWrapper />
<Pallete /> <Pallete />
<DebugModal />
<SettingsSidebar /> <SettingsSidebar />
</TemplateContext> </TemplateContext>
</AppContext> </AppContext>

View File

@ -0,0 +1,66 @@
import { useEffect, useState } from "react";
import { Debug, FlagCategory } from "@sc07-canvas/lib/src/debug";
import {
Button,
Modal,
ModalBody,
ModalContent,
ModalFooter,
ModalHeader,
Switch,
useDisclosure,
} from "@nextui-org/react";
export const DebugModal = () => {
const { isOpen, onOpen, onOpenChange } = useDisclosure();
useEffect(() => {
const handleOpen = () => {
onOpen();
};
Debug.on("openTools", handleOpen);
return () => {
Debug.off("openTools", handleOpen);
};
}, []);
return (
<Modal isOpen={isOpen} onOpenChange={onOpenChange} placement="center">
<ModalContent>
{(onClose) => (
<>
<ModalHeader className="flex flex-col gap-1">
Debug Tools
</ModalHeader>
<ModalBody>
<Button onPress={() => Debug.openDebug()}>
Open Debug Information
</Button>
{Debug.flags.getAll().map((flag, i, arr) => (
<>
{arr[i - 1]?.category !== flag.category && (
<p>{FlagCategory[flag.category]}</p>
)}
<div key={flag.id}>
<Switch
size="sm"
defaultSelected={flag.enabled}
onValueChange={(v) => Debug.flags.setEnabled(flag.id, v)}
>
{flag.id}
</Switch>
</div>
</>
))}
</ModalBody>
<ModalFooter>
<Button onPress={onClose}>Close</Button>
</ModalFooter>
</>
)}
</ModalContent>
</Modal>
);
};

View File

@ -1,6 +1,7 @@
import { Button } from "@nextui-org/react"; import { Button } from "@nextui-org/react";
import { useAppContext } from "../contexts/AppContext"; import { useAppContext } from "../contexts/AppContext";
import { User } from "./Header/User"; import { User } from "./Header/User";
import { Debug } from "@sc07-canvas/lib/src/debug";
export const Header = () => { export const Header = () => {
const { setSettingsSidebar } = useAppContext(); const { setSettingsSidebar } = useAppContext();
@ -12,6 +13,7 @@ export const Header = () => {
<div className="box"> <div className="box">
<User /> <User />
<Button onClick={() => setSettingsSidebar(true)}>Settings</Button> <Button onClick={() => setSettingsSidebar(true)}>Settings</Button>
<Button onClick={() => Debug.openDebugTools()}>debug</Button>
</div> </div>
</header> </header>
); );

185
packages/lib/src/debug.ts Normal file
View File

@ -0,0 +1,185 @@
import EventEmitter from "eventemitter3";
interface DebugEvents {
openTools(): void;
}
interface DebugArgs {
point: [x: number, y: number, id?: string];
text: [str: any];
}
export enum FlagCategory {
"Renderer",
"DebugMessages",
"Uncategorized",
}
class ExperimentFlag {
id: string;
enabled: boolean;
category: FlagCategory = FlagCategory.Uncategorized;
constructor(id: string, defaultEnabled = false, category?: FlagCategory) {
this.id = id;
this.enabled = defaultEnabled;
if (category) this.category = category;
}
}
interface FlagEvents {
enable(flag_id: string): void;
disable(flag_id: string): void;
}
class FlagManager extends EventEmitter<FlagEvents> {
flags: ExperimentFlag[];
constructor() {
super();
this.flags = [];
this.register(
// RENDERER
new ExperimentFlag(
"PANZOOM_PINCH_TRANSFORM_1",
false,
FlagCategory.Renderer
),
new ExperimentFlag(
"PANZOOM_PINCH_TRANSFORM_2",
false,
FlagCategory.Renderer
),
// DEBUG MESSAGES
new ExperimentFlag(
"PANZOOM_PINCH_DEBUG_MESSAGES",
false,
FlagCategory.DebugMessages
)
);
}
register(...flags: ExperimentFlag[]) {
this.flags.push(...flags);
}
getFlag(flag: string) {
return this.flags.find((f) => f.id === flag);
}
enabled(flag: string) {
return this.getFlag(flag)?.enabled;
}
setEnabled(flagID: string, enabled: boolean) {
const flag = this.flags.find((f) => f.id === flagID);
if (!flag) throw new Error("Unknown flag " + flagID);
flag.enabled = enabled;
if (enabled) {
this.emit("enable", flagID);
} else {
this.emit("disable", flagID);
}
}
getAll() {
return [...this.flags].sort((a, b) => a.category - b.category);
}
}
/**
* Debug wrapper
*
* Goals:
* - toggle debug flags (similar to Discord experiments)
* - open blank debug tab with useragent and any flags
*/
class Debugcl extends EventEmitter<DebugEvents> {
readonly flags = new FlagManager();
constructor() {
super();
}
openDebug() {
const wind = window.open("about:blank", "_blank");
if (!wind) {
alert(
"Failed to open debug tab. Is your anti-popup too powerful? Or did this get triggered from not a trusted event"
);
return;
}
wind.document.write(`
<h1>debug menu</h1>
<pre>${JSON.stringify(
{
userAgent: navigator.userAgent,
flags: this.flags
.getAll()
.filter((f) => f.enabled)
.map((f) => f.id),
},
null,
2
)}</pre>
`);
wind.document.close();
}
openDebugTools() {
this.emit("openTools");
}
/**
* Create debug marker
*
* Useful on touchscreen devices
*
* @param type
* @param args
* @returns
*/
debug<T extends keyof DebugArgs>(type: T, ...args: DebugArgs[T]) {
switch (type) {
case "point": {
const [x, y, id] = args;
if (document.getElementById("debug-" + id)) {
document.getElementById("debug-" + id)!.style.top = y + "px";
document.getElementById("debug-" + id)!.style.left = x + "px";
return;
}
let el = document.createElement("div");
if (id) el.id = "debug-" + id;
el.classList.add("debug-point");
el.style.setProperty("top", y + "px");
el.style.setProperty("left", x + "px");
document.body.appendChild(el);
break;
}
case "text": {
const [str] = args;
// create debug box in canvas-meta if it doesn't exist
if (!document.getElementById("canvas-meta-debug")) {
let debugBox = document.createElement("div");
debugBox.id = "canvas-meta-debug";
debugBox.style.whiteSpace = "pre";
debugBox.style.unicodeBidi = "embed";
document.getElementById("canvas-meta")!.prepend(debugBox);
}
document.getElementById("canvas-meta-debug")!.innerText =
typeof str === "string" ? str : JSON.stringify(str, null, 2);
break;
}
}
}
}
export const Debug = new Debugcl();

View File

@ -1,3 +1,4 @@
import { Debug } from "../../debug";
import { PanZoom } from "../PanZoom"; import { PanZoom } from "../PanZoom";
export function handleCalculateZoomPositions( export function handleCalculateZoomPositions(
@ -17,7 +18,7 @@ export function handleCalculateZoomPositions(
const calculatedPositionX = x - mouseX * scaleDifference; const calculatedPositionX = x - mouseX * scaleDifference;
const calculatedPositionY = y - mouseY * scaleDifference; const calculatedPositionY = y - mouseY * scaleDifference;
contextInstance.debug(calculatedPositionX, calculatedPositionY, "zoom"); // Debug.debug("point", calculatedPositionX, calculatedPositionY, "zoom");
// do not limit to bounds when there is padding animation, // do not limit to bounds when there is padding animation,
// it causes animation strange behaviour // it causes animation strange behaviour