Commit 0ed579c0 authored by Grant's avatar Grant
Browse files

add debug menu & flags

parent d262be82
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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 = () => {
        <CanvasWrapper />
        <Pallete />

        <DebugModal />
        <SettingsSidebar />
      </TemplateContext>
    </AppContext>
+66 −0
Original line number Diff line number Diff line
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>
  );
};
+2 −0
Original line number Diff line number Diff line
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 = () => {
      <div className="box">
        <User />
        <Button onClick={() => setSettingsSidebar(true)}>Settings</Button>
        <Button onClick={() => Debug.openDebugTools()}>debug</Button>
      </div>
    </header>
  );
+185 −0
Original line number Diff line number Diff line
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();
+2 −1
Original line number Diff line number Diff line
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