// Initial state of the canvas let scale, panOffsetX, panOffsetY; const ZOOM_SPEED = 0.1; const minScale = 0.35; const maxScale = 1.25; const container = document.getElementById('canvas-nodes'); let isDragging = false; let isSpacePressed = false; let isPanning = false; let startX = 0; let startY = 0; let lastTouchX = 0; let lastTouchY = 0; let touchStartPanX = 0; let touchStartPanY = 0; function adjustCanvasToViewport() { const nodes = document.querySelectorAll('.node'); let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity; nodes.forEach(node => { const x = parseInt(node.style.left, 10); const y = parseInt(node.style.top, 10); const width = node.offsetWidth; const height = node.offsetHeight; minX = Math.min(minX, x); maxX = Math.max(maxX, x + width); minY = Math.min(minY, y); maxY = Math.max(maxY, y + height); }); const boundingBoxWidth = maxX - minX; const boundingBoxHeight = maxY - minY; const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; const scaleX = viewportWidth / (boundingBoxWidth + 80); const scaleY = viewportHeight / (boundingBoxHeight + 80); scale = Math.min(scaleX, scaleY, 1); // Ensure the scale is not more than 1 panOffsetX = (viewportWidth - boundingBoxWidth * scale) / 2 - minX * scale; panOffsetY = (viewportHeight - boundingBoxHeight * scale) / 2 - minY * scale; // Apply the calculated scale and pan offsets applyPanAndZoom(); document.getElementById('canvas-nodes').style.opacity = 1; document.getElementById('canvas-edges').style.opacity = 1; } document.addEventListener('DOMContentLoaded', adjustCanvasToViewport); // Zoom window.addEventListener('wheel', (e) => { if (e.ctrlKey || e.metaKey) { if (e.deltaY > 0) { scale = Math.max(scale - ZOOM_SPEED, minScale); } else { scale = Math.min(scale + ZOOM_SPEED, maxScale); } document.body.style.setProperty('--scale', scale); e.preventDefault(); } }, {passive: false}); // Buttons document.getElementById('zoom-in').addEventListener('click', function() { scale = Math.min(scale + ZOOM_SPEED, maxScale); document.body.style.setProperty('--scale', scale); }); document.getElementById('zoom-out').addEventListener('click', function() { scale = Math.max(scale - ZOOM_SPEED, minScale); document.body.style.setProperty('--scale', scale); }); document.getElementById('zoom-reset').addEventListener('click', function() { adjustCanvasToViewport(); }); document.getElementById('toggle-output').addEventListener('click', function() { const output = document.getElementById('output'); output.classList.toggle('hidden'); }); document.querySelector('.close-output').addEventListener('click', function() { const output = document.getElementById('output'); output.classList.toggle('hidden'); }); document.querySelector('.button-copy').addEventListener('click', function() { const positionsOutput = document.getElementById('positionsOutput').textContent; navigator.clipboard.writeText(positionsOutput).catch(err => { console.error('Error copying canvas data: ', err); }); }); document.querySelector('.button-download').addEventListener('click', function() { const positionsOutput = document.getElementById('positionsOutput').textContent; const blob = new Blob([positionsOutput], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'sample.canvas'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }); // Very simplified Markdown conversion function htmlToMarkdown(html) { let markdown = html.replace(//gi, "\n"); markdown = markdown.replace(/([^<]+)<\/a>/gi, "[$2]($1)"); markdown = markdown.replace(/