From 0ed579c0d772c24be8523a454e8e5f4957efe413 Mon Sep 17 00:00:00 2001 From: Grant Date: Thu, 25 Apr 2024 19:22:41 -0600 Subject: [PATCH] add debug menu & flags --- packages/client/src/components/App.tsx | 2 + .../src/components/Debug/DebugModal.tsx | 66 +++++++ packages/client/src/components/Header.tsx | 2 + packages/lib/src/debug.ts | 185 ++++++++++++++++++ packages/lib/src/renderer/lib/zoom.utils.ts | 3 +- 5 files changed, 257 insertions(+), 1 deletion(-) create mode 100644 packages/client/src/components/Debug/DebugModal.tsx create mode 100644 packages/lib/src/debug.ts diff --git a/packages/client/src/components/App.tsx b/packages/client/src/components/App.tsx index 63eb065..38494e6 100644 --- a/packages/client/src/components/App.tsx +++ b/packages/client/src/components/App.tsx @@ -4,6 +4,7 @@ import { CanvasWrapper } from "./CanvasWrapper"; import { Pallete } from "./Pallete"; import { TemplateContext } from "../contexts/TemplateContext"; import { SettingsSidebar } from "./Settings/SettingsSidebar"; +import { DebugModal } from "./Debug/DebugModal"; const App = () => { return ( @@ -13,6 +14,7 @@ const App = () => { + diff --git a/packages/client/src/components/Debug/DebugModal.tsx b/packages/client/src/components/Debug/DebugModal.tsx new file mode 100644 index 0000000..f629e75 --- /dev/null +++ b/packages/client/src/components/Debug/DebugModal.tsx @@ -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 ( + + + {(onClose) => ( + <> + + Debug Tools + + + + {Debug.flags.getAll().map((flag, i, arr) => ( + <> + {arr[i - 1]?.category !== flag.category && ( +

{FlagCategory[flag.category]}

+ )} +
+ Debug.flags.setEnabled(flag.id, v)} + > + {flag.id} + +
+ + ))} +
+ + + + + )} +
+
+ ); +}; diff --git a/packages/client/src/components/Header.tsx b/packages/client/src/components/Header.tsx index af3c1f3..2f2788e 100644 --- a/packages/client/src/components/Header.tsx +++ b/packages/client/src/components/Header.tsx @@ -1,6 +1,7 @@ import { Button } from "@nextui-org/react"; import { useAppContext } from "../contexts/AppContext"; import { User } from "./Header/User"; +import { Debug } from "@sc07-canvas/lib/src/debug"; export const Header = () => { const { setSettingsSidebar } = useAppContext(); @@ -12,6 +13,7 @@ export const Header = () => {
+
); diff --git a/packages/lib/src/debug.ts b/packages/lib/src/debug.ts new file mode 100644 index 0000000..e615768 --- /dev/null +++ b/packages/lib/src/debug.ts @@ -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 { + 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 { + 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(` +

debug menu

+
${JSON.stringify(
+      {
+        userAgent: navigator.userAgent,
+        flags: this.flags
+          .getAll()
+          .filter((f) => f.enabled)
+          .map((f) => f.id),
+      },
+      null,
+      2
+    )}
+ `); + wind.document.close(); + } + + openDebugTools() { + this.emit("openTools"); + } + + /** + * Create debug marker + * + * Useful on touchscreen devices + * + * @param type + * @param args + * @returns + */ + debug(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(); diff --git a/packages/lib/src/renderer/lib/zoom.utils.ts b/packages/lib/src/renderer/lib/zoom.utils.ts index 3d2752a..5c45f0c 100644 --- a/packages/lib/src/renderer/lib/zoom.utils.ts +++ b/packages/lib/src/renderer/lib/zoom.utils.ts @@ -1,3 +1,4 @@ +import { Debug } from "../../debug"; import { PanZoom } from "../PanZoom"; export function handleCalculateZoomPositions( @@ -17,7 +18,7 @@ export function handleCalculateZoomPositions( const calculatedPositionX = x - mouseX * 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, // it causes animation strange behaviour