Commit acc18e54 authored by Ategon Dev's avatar Ategon Dev Committed by Grant
Browse files

Add grid and copy link button

parent 760c5346
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ import { CanvasUtils } from "../lib/canvas.utils";
import { ModCanvasOverlay } from "../Moderator/ModCanvasOverlay";
import { useHasRole } from "../hooks/useHasRole";
import { toast } from "react-toastify";
import { GridOverlay } from "./Overlay/GridOverlay";

export const CanvasWrapper = () => {
  const hasMod = useHasRole("MOD");
@@ -63,6 +64,7 @@ export const CanvasWrapper = () => {
        {hasMod && <ModCanvasOverlay />}
        <BlankOverlay />
        <HeatmapOverlay />
        <GridOverlay />
        <PixelPulses />
        {config && <Template />}
        <CanvasInner />
+60 −0
Original line number Diff line number Diff line
import { useEffect, useRef } from "react";
import { useAppContext } from "../../contexts/AppContext";
import { KeybindManager } from "../../lib/keybinds";

const GRID_SPACING = 8;

export const GridOverlay = () => {
  const { gridOverlay, setGridOverlay, config } = useAppContext();
  const canvasRef = useRef<HTMLCanvasElement | null>(null);

  useEffect(() => {
    const handleKeybind = () => {
      setGridOverlay((v) => ({ ...v, enabled: !v.enabled }));
    };

    KeybindManager.on("TOGGLE_GRID", handleKeybind);
    return () => {
      KeybindManager.off("TOGGLE_GRID", handleKeybind);
    };
  }, [setGridOverlay]);

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas || !gridOverlay.enabled) return;

    const ctx = canvas.getContext("2d");
    if (!ctx) return;

    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = "black"; // full black

    for (let x = 0; x <= canvas.width; x += GRID_SPACING) {
      ctx.fillRect(x, 0, 1, canvas.height); // vertical line as 1px wide column
    }

    for (let y = 0; y <= canvas.height; y += GRID_SPACING) {
      ctx.fillRect(0, y, canvas.width, 1); // horizontal line as 1px tall row
    }
  }, [gridOverlay.enabled]);

  return (
    <canvas
      id="grid-overlay"
      className="no-interact pixelate"
      ref={canvasRef}
      width={config.canvas.size[0] * 8}
      height={config.canvas.size[1] * 8}
      style={{
        position: "absolute",
        top: 0,
        left: 0,
        width: config.canvas.size[0],
        height: config.canvas.size[1],
        display: gridOverlay.enabled ? "block" : "none",
        opacity: gridOverlay.opacity?.toFixed(1) ?? "1",
        pointerEvents: "none",
      }}
    />
  );
};
+25 −0
Original line number Diff line number Diff line
@@ -10,6 +10,8 @@ export const OverlaySettings = () => {
    setHeatmapOverlay,
    pixelPulses,
    setPixelPulses,
    gridOverlay,
    setGridOverlay,
  } = useAppContext();

  return (
@@ -66,6 +68,29 @@ export const OverlaySettings = () => {
          />
        )}

        <Switch
          isSelected={gridOverlay.enabled}
          onValueChange={(v) => {
            setGridOverlay((vv) => ({ ...vv, enabled: v }));
          }}
        >
          {gridOverlay.loading && <Spinner size="sm" />}
          Grid Overlay
        </Switch>
        {gridOverlay.enabled && (
          <Slider
            label="Grid Opacity"
            step={0.025}
            minValue={0}
            maxValue={1}
            value={gridOverlay.opacity}
            onChange={(v) =>
              setGridOverlay((vv) => ({ ...vv, opacity: v as number }))
            }
            getValue={(v) => (v as number) * 100 + "%"}
          />
        )}

        <Switch
          isSelected={pixelPulses}
          onValueChange={(v) => {
+7 −7
Original line number Diff line number Diff line
@@ -187,7 +187,7 @@ export const AudioSettings = () => {
      </header>
      <section className="flex flex-col gap-2">
        <Switch
          stopSound
          silent
          isSelected={pixelAvailableSound}
          onValueChange={(v) => {
            setPixelAvailableSound(v);
@@ -199,7 +199,7 @@ export const AudioSettings = () => {
          Pixel Available
        </Switch>
        <Switch
          stopSound
          silent
          isSelected={placeSound}
          onValueChange={(v) => {
            setPlaceSound(v);
@@ -211,7 +211,7 @@ export const AudioSettings = () => {
          Pixel Place
        </Switch>
        <Switch
          stopSound
          silent
          isSelected={pixelUndoSound}
          onValueChange={(v) => {
            setPixelUndoSound(v);
@@ -223,7 +223,7 @@ export const AudioSettings = () => {
          Pixel Undo
        </Switch>
        <Switch
          stopSound
          silent
          isSelected={uiClickSound}
          onValueChange={(v) => {
            setUiClickSound(v);
@@ -235,7 +235,7 @@ export const AudioSettings = () => {
          UI Click
        </Switch>
        <Switch
          stopSound
          silent
          isSelected={disconnectSound}
          onValueChange={(v) => {
            setDisconnectSound(v);
@@ -247,7 +247,7 @@ export const AudioSettings = () => {
          Disconnect
        </Switch>
        <Switch
          stopSound
          silent
          isSelected={reconnectSound}
          onValueChange={(v) => {
            setReconnectSound(v);
@@ -259,7 +259,7 @@ export const AudioSettings = () => {
          Reconnect
        </Switch>
        <Switch
          stopSound
          silent
          isSelected={globalPlaceSound}
          onValueChange={(v) => {
            setGlobalPlaceSound(v);
+19 −0
Original line number Diff line number Diff line
import { useTemplateContext } from "../../contexts/TemplateContext";
import { Input, Select, SelectItem, Slider } from "@nextui-org/react";
import { Switch } from "../core/Switch";
import { Button } from "../core/Button";
import { toast } from "react-toastify";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faLink } from "@fortawesome/free-solid-svg-icons";

export const TemplateSettings = () => {
  const {
@@ -109,6 +113,21 @@ export const TemplateSettings = () => {
          >
            Show Mobile Tools
          </Switch>
          {url && (
            <Button
              onPress={() => {
                const copied = `${window.location.origin}${window.location.pathname}#tu=${url}${
                  width != null ? `&tw=${width}` : ""
                }&tx=${x}&ty=${y}&ts=${style}&x=${x + (width || 0) / 2}&y=${y + (width || 0) / 2}&zoom=1`;

                navigator.clipboard.writeText(copied);
                toast.success("Link copied to clipboard!");
              }}
              startContent={<FontAwesomeIcon icon={faLink} />}
            >
              Copy Shareable Link
            </Button>
          )}
        </section>
      )}
    </div>
Loading