class Whiteboard { constructor() { this.canvas = document.getElementById('whiteboard'); this.ctx = this.canvas.getContext('2d'); this.isDrawing = false; this.history = new History(); this.tools = new DrawingTools(this.canvas, this.ctx); this.pages = new Pages(); this.setupCanvas(); this.setupEventListeners(); this.setupBrushPreview(); this.setupToolbarToggle(); } setupCanvas() { const resize = () => { this.canvas.width = window.innerWidth; this.canvas.height = window.innerHeight; this.setupDrawingStyle(); // Restore the current page const pageContent = this.pages.getPageContent(); if (pageContent) { this.ctx.putImageData(pageContent, 0, 0); // Use the drawing content from the current page } }; window.addEventListener('resize', resize); resize(); } setupToolbarToggle() { const toolbar = document.querySelector('.bottom-toolbar'); const toggleBtn = document.getElementById('toggleToolbar'); toggleBtn.addEventListener('click', () => { toolbar.classList.toggle('hidden'); }); } setupBrushPreview() { this.brushPreview = document.getElementById('brushPreview'); this.canvas.addEventListener('mousemove', (e) => { const { x, y } = getMousePos(this.canvas, e); this.updateBrushPreview(x, y); }); this.canvas.addEventListener('mouseenter', () => { this.brushPreview.style.display = 'block'; }); this.canvas.addEventListener('mouseleave', () => { this.brushPreview.style.display = 'none'; }); } updateBrushPreview(x, y) { const size = this.tools.brushSize; this.brushPreview.style.width = size + 'px'; this.brushPreview.style.height = size + 'px'; this.brushPreview.style.left = (x - size/2) + 'px'; this.brushPreview.style.top = (y - size/2) + 'px'; this.brushPreview.style.borderColor = this.tools.isEraser ? '#000' : this.tools.color; } setupDrawingStyle() { this.ctx.lineCap = 'round'; this.ctx.lineJoin = 'round'; this.ctx.font = '24px Arial'; this.tools.applyToolSettings(); } setupEventListeners() { // Mouse events this.canvas.addEventListener('mousedown', this.startDrawing.bind(this)); this.canvas.addEventListener('mousemove', this.draw.bind(this)); this.canvas.addEventListener('mouseup', this.stopDrawing.bind(this)); this.canvas.addEventListener('mouseout', this.stopDrawing.bind(this)); // Touch events this.canvas.addEventListener('touchstart', this.handleTouch.bind(this)); this.canvas.addEventListener('touchmove', this.handleTouch.bind(this)); this.canvas.addEventListener('touchend', this.handleTouchEnd.bind(this)); // Button events document.getElementById('aiBtn').addEventListener('click', this.processDrawing.bind(this)); document.getElementById('submitBtn').addEventListener('click', this.generatePDF.bind(this)); document.getElementById('clearBtn').addEventListener('click', this.clearCanvas.bind(this)); document.getElementById('undoBtn').addEventListener('click', this.undo.bind(this)); document.getElementById('redoBtn').addEventListener('click', this.redo.bind(this)); // Keyboard shortcuts document.addEventListener('keydown', (e) => { if (e.ctrlKey || e.metaKey) { if (e.key === 'z') { e.preventDefault(); this.undo(); } else if (e.key === 'y') { e.preventDefault(); this.redo(); } } }); } handleTouch(e) { e.preventDefault(); const touch = e.touches[0]; const eventType = e.type === 'touchstart' ? 'mousedown' : 'mousemove'; const mouseEvent = new MouseEvent(eventType, { clientX: touch.clientX, clientY: touch.clientY }); this.canvas.dispatchEvent(mouseEvent); } handleTouchEnd(e) { e.preventDefault(); const mouseEvent = new MouseEvent('mouseup', {}); this.canvas.dispatchEvent(mouseEvent); } startDrawing(e) { this.isDrawing = true; const { x, y } = getMousePos(this.canvas, e); this.tools.applyToolSettings(); this.ctx.beginPath(); this.ctx.moveTo(x, y); } draw(e) { if (!this.isDrawing) return; const { x, y } = getMousePos(this.canvas, e); this.ctx.lineTo(x, y); this.ctx.stroke(); } stopDrawing() { if (this.isDrawing) { this.isDrawing = false; this.saveState(); } } saveState() { const imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height); this.history.push(new DrawingState(imageData)); this.pages.setPageContent(this.history.states); } undo() { const state = this.history.undo(); if (state) { this.ctx.putImageData(state.imageData, 0, 0); this.pages.setPageContent(this.history.states); } } redo() { const state = this.history.redo(); if (state) { this.ctx.putImageData(state.imageData, 0, 0); this.pages.setPageContent(this.history.states); } } clearCanvas() { this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); this.saveState(); } processDrawing() { const tempCanvas = document.createElement('canvas'); const tempContext = tempCanvas.getContext('2d'); tempCanvas.width = this.canvas.width; tempCanvas.height = this.canvas.height; tempContext.fillStyle = 'white'; tempContext.fillRect(0, 0, tempCanvas.width, tempCanvas.height); tempContext.drawImage(this.canvas, 0, 0); const imageData = tempCanvas.toDataURL('image/png'); console.log('Processing drawing with AI...'); console.log(imageData.substring(0, 100) + '...'); fetch('/whitebai', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ image: imageData }) }) .then(response => response.json()) .then(data => { if (data.status && Array.isArray(data.data)) { data.data.forEach(item => { const result = item.result; const x = item.location.x; const y = item.location.y; const size = item.size; const sizePx = `${size}px`; writeOnCanvasDynamic(whiteboard.pages, result, x, y, null, "Arial", sizePx, "black", false); }); } else { console.error("Invalid data object or empty data array"); } document.getElementById('result').innerText = data.text; }) .catch(error => { console.error('Error:', error); }); } async generatePDF() { const pages = this.pages.getAllPages(); for (let i = 0; i < pages.length; i++) { const pageContent = pages[i]; const tempCanvas = document.createElement('canvas'); tempCanvas.width = this.canvas.width; tempCanvas.height = this.canvas.height; const tempCtx = tempCanvas.getContext('2d'); tempCtx.fillStyle = 'white'; tempCtx.fillRect(0, 0, tempCanvas.width, tempCanvas.height); if (pageContent && pageContent.drawing) { const imageData = pageContent.drawing; if (imageData) { try { tempCtx.putImageData(imageData, 0, 0); console.log("Image applied to canvas for page " + i); } catch (error) { console.log("Error putting image data on page " + i, error); } } else { console.log("No image data for page " + i); } } const imgData = tempCanvas.toDataURL('image/png'); console.log(`Base64 data for page ${i}:`, imgData); } } writeText(text, x = 20, y = 40) { this.setupDrawingStyle(); this.ctx.fillStyle = this.tools.color; this.ctx.fillText(text.toString(), x, y); this.saveState(); } } const whiteboard = new Whiteboard(); const canvas = document.getElementById('whiteboard'); document.addEventListener('keydown', function(event) { if (event.ctrlKey && event.key === 's') { event.preventDefault(); const tempCanvas = document.createElement('canvas'); const tempContext = tempCanvas.getContext('2d'); tempCanvas.width = canvas.width; tempCanvas.height = canvas.height; tempContext.fillStyle = 'white'; tempContext.fillRect(0, 0, tempCanvas.width, tempCanvas.height); tempContext.drawImage(canvas, 0, 0); const imageData = tempCanvas.toDataURL("image/png"); const link = document.createElement('a'); link.href = imageData; link.download = 'canvas-image.png'; link.click(); console.log("Canvas saved as image with white background!"); } }); function writeOnCanvasDynamic(pagesInstance, text, x, y, fontUrl, fontFamily, fontSize = "16px", color = "black", clear = false) { const ctx = pagesInstance.ctx; if (!ctx) return; const applyFontAndWrite = () => { ctx.globalCompositeOperation = 'source-over'; if (clear) { const textWidth = ctx.measureText(text).width; const lineHeight = parseInt(fontSize, 10) || 16; ctx.clearRect(x, y - lineHeight, textWidth, lineHeight); return; } ctx.font = `${fontSize} ${fontFamily}`; ctx.fillStyle = color; ctx.fillText(text, x, y); }; if (fontUrl) { const fontFace = new FontFace(fontFamily, `url(${fontUrl})`); fontFace.load().then((loadedFont) => { document.fonts.add(loadedFont); applyFontAndWrite(); }).catch((err) => { console.error("Font loading failed:", err); }); } else { applyFontAndWrite(); } }