const canvas = document.querySelector(".canvas canvas"); const ctx = canvas.getContext("2d", { willReadFrequently: true }); ctx.imageSmoothingEnabled = false; let drawing = false; let lastX, lastY; let strokeSize = 8; let strokeColor = "black"; function drawPixelLine(x0, y0, x1, y1) { const dx = Math.abs(x1 - x0); const dy = Math.abs(y1 - y0); const sx = x0 < x1 ? 1 : -1; const sy = y0 < y1 ? 1 : -1; let err = dx - dy; while (true) { ctx.fillRect(x0, y0, strokeSize, strokeSize); if (x0 === x1 && y0 === y1) break; const e2 = 2 * err; if (e2 > -dy) { err -= dy; x0 += sx; } if (e2 < dx) { err += dx; y0 += sy; } } } // Disable dragging for all images on the page document.querySelectorAll('img').forEach(img => { img.addEventListener('dragstart', (e) => e.preventDefault()); }); canvas.addEventListener("mousedown", start); canvas.addEventListener("mouseup", () => (drawing = false)); canvas.addEventListener("mouseleave", () => (drawing = false)); canvas.addEventListener("mousemove", move); function start(e) { drawing = true; const rect = canvas.getBoundingClientRect(); const scaleX = canvas.width / rect.width; const scaleY = canvas.height / rect.height; lastX = Math.floor((e.clientX - rect.left) * scaleX); lastY = Math.floor((e.clientY - rect.top) * scaleY); } function move(e) { if (!drawing) return; const rect = canvas.getBoundingClientRect(); const scaleX = canvas.width / rect.width; const scaleY = canvas.height / rect.height; const x = Math.floor((e.clientX - rect.left) * scaleX); const y = Math.floor((e.clientY - rect.top) * scaleY); ctx.fillStyle = strokeColor; drawPixelLine(lastX, lastY, x, y); lastX = x; lastY = y; } canvas.addEventListener("touchstart", (e) => { e.preventDefault(); const touch = e.touches[0]; start(touch); }); canvas.addEventListener("touchmove", (e) => { e.preventDefault(); const touch = e.touches[0]; move(touch); }); canvas.addEventListener("touchend", (e) => { e.preventDefault(); drawing = false; }); /* BUTTONS */ let pencilButton = document.querySelector("button#pencil"); let eraserButton = document.querySelector("button#eraser"); let smallButton = document.querySelector("button#small"); let mediumButton = document.querySelector("button#medium"); let clearButton = document.querySelector("button#clear"); pencilButton.addEventListener("click", (e) => { e.preventDefault(); strokeColor = "black"; pencilButton.classList.add("active-tool"); eraserButton.classList.remove("active-tool"); }); eraserButton.addEventListener("click", (e) => { e.preventDefault(); strokeColor = "white"; eraserButton.classList.add("active-tool"); pencilButton.classList.remove("active-tool"); }); smallButton.addEventListener("click", (e) => { e.preventDefault(); strokeSize = 4; smallButton.classList.add("active-tool"); mediumButton.classList.remove("active-tool"); }); mediumButton.addEventListener("click", (e) => { e.preventDefault(); strokeSize = 8; mediumButton.classList.add("active-tool"); smallButton.classList.remove("active-tool"); }); window.resetCanvas = () => { const BASE_HEIGHT = 312; window.currentHeightMultiplier = 1; canvas.height = BASE_HEIGHT; canvas.parentElement.style.aspectRatio = `856 / 312`; ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.lineWidth = strokeSize; ctx.lineCap = "round"; ctx.strokeStyle = strokeColor; }; clearButton.addEventListener("click", () => { window.resetCanvas(); window.clearCurrentText(); }); // Extend Canvas Button const extendCanvasBtn = document.getElementById("extend-canvas-btn"); if (extendCanvasBtn) { extendCanvasBtn.addEventListener("click", (e) => { e.preventDefault(); if ((window.currentHeightMultiplier || 1) >= 3) return; const BASE_HEIGHT = 312; window.currentHeightMultiplier = (window.currentHeightMultiplier || 1) + 1; const newHeight = BASE_HEIGHT * window.currentHeightMultiplier; // Create temporary canvas to hold current drawing const tempCanvas = document.createElement('canvas'); tempCanvas.width = canvas.width; tempCanvas.height = canvas.height; tempCanvas.getContext('2d').drawImage(canvas, 0, 0); canvas.height = newHeight; canvas.parentElement.style.aspectRatio = `856 / ${newHeight}`; // Restore drawing ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.drawImage(tempCanvas, 0, 0); }); } copyButton = document.querySelector("button#copy"); copyButton.addEventListener("click", (e) => { e.preventDefault(); // copy image const images = document.querySelectorAll( "#chat-log .message-box .canvas img" ); const lastImage = images[0]; if (lastImage) { const img = new Image(); img.onload = function () { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.drawImage(img, 0, 0); }; img.src = lastImage.src; } // copy text const texts = document.querySelectorAll( "#chat-log .message-box .canvas .message-text" ); const lastText = texts[0]; if (lastText) { const text = lastText.innerHTML.replace(/
/g, "\n"); window.setCurrentText(text); } }); // Color swaths const swaths = document.querySelectorAll(".swatch"); swaths.forEach(swatch => { swatch.addEventListener("click", (e) => { e.preventDefault(); const color = swatch.getAttribute("data-color"); strokeColor = color; swaths.forEach(s => s.classList.remove("active-swatch")); swatch.classList.add("active-swatch"); // Update user's theme color to match if (window.displayUserColor) { window.userColor = color; window.displayUserColor(color); } // Unset eraser if active pencilButton.classList.add("active-tool"); eraserButton.classList.remove("active-tool"); }); }); // Sync custom color picker with stroke color const picker = document.getElementById("user-color-picker"); if (picker) { picker.addEventListener("input", (e) => { strokeColor = e.target.value; swaths.forEach(s => s.classList.remove("active-swatch")); // We could add an active class to the picker container but it's okay pencilButton.classList.add("active-tool"); eraserButton.classList.remove("active-tool"); }); } // Expand Button const expandBtn = document.getElementById("expand-btn"); if (expandBtn) { expandBtn.addEventListener("click", (e) => { e.preventDefault(); document.body.classList.toggle("fullscreen-mode"); if (document.body.classList.contains("fullscreen-mode")) { expandBtn.innerHTML = "shrink ⤡"; } else { expandBtn.innerHTML = "expand ⤢"; } }); } // Randomize Color Button const randomColorBtn = document.getElementById("random-color-btn"); if (randomColorBtn) { randomColorBtn.addEventListener("click", (e) => { e.preventDefault(); const newColor = '#' + Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0'); strokeColor = newColor; swaths.forEach(s => s.classList.remove("active-swatch")); if (picker) picker.value = newColor; if (window.displayUserColor) { window.userColor = newColor; window.displayUserColor(newColor); } pencilButton.classList.add("active-tool"); eraserButton.classList.remove("active-tool"); }); } // Theme Toggle Button const themeBtn = document.getElementById("theme-btn"); if (themeBtn) { themeBtn.addEventListener("click", (e) => { e.preventDefault(); document.body.classList.toggle("light-mode"); if (document.body.classList.contains("light-mode")) { themeBtn.innerHTML = "dark mode 🌙"; } else { themeBtn.innerHTML = "light mode ☀"; } }); } // Download Archive Button const downloadBtn = document.getElementById("download-btn"); if (downloadBtn) { downloadBtn.addEventListener("click", (e) => { e.preventDefault(); window.location.href = "/api/download_images"; }); } // Palette Toggle for portrait mode const paletteToggleBtn = document.getElementById("palette-toggle"); const colorPalette = document.getElementById("color-palette"); if (paletteToggleBtn && colorPalette) { paletteToggleBtn.addEventListener("click", (e) => { e.preventDefault(); colorPalette.classList.toggle("show-palette"); }); }