const btnHeroOpen = document.getElementById('btn-hero-open'); const btnOpen = document.getElementById('btn-open'); const btnPlay = document.getElementById('btn-play'); const btnReset = document.getElementById('btn-reset'); const inputSpeed = document.getElementById('input-speed'); const inputSize = document.getElementById('input-size'); const markdownOutput = document.getElementById('markdown-output'); const placeholder = document.getElementById('placeholder'); const scrollContainer = document.getElementById('scroll-container'); const iconPlay = document.getElementById('icon-play'); const iconPause = document.getElementById('icon-pause'); let isScrolling = false; let scrollAccumulator = 0; let animationId; let scrollSpeed = parseInt(inputSpeed.value, 10); // Initialize function init() { setupEventListeners(); updateFontSize(inputSize.value); } function setupEventListeners() { btnHeroOpen.addEventListener('click', openFile); btnOpen.addEventListener('click', openFile); btnPlay.addEventListener('click', toggleScrolling); // Spacebar to toggle document.addEventListener('keydown', (e) => { if (e.code === 'Space') { e.preventDefault(); // Prevent page down scroll toggleScrolling(); } }); btnReset.addEventListener('click', () => { stopScrolling(); scrollContainer.scrollTo({ top: 0, behavior: 'smooth' }); }); inputSpeed.addEventListener('input', (e) => { scrollSpeed = parseInt(e.target.value, 10); }); inputSize.addEventListener('input', (e) => { updateFontSize(e.target.value); }); } async function openFile() { const filePath = await window.electronAPI.openFileDialog(); if (filePath) { const content = await window.electronAPI.readFile(filePath); renderMarkdown(content); } } function renderMarkdown(content) { if (!content) return; // Using marked from global scope (loaded via script tag) const html = marked.parse(content); markdownOutput.innerHTML = html; // Switch view placeholder.style.display = 'none'; markdownOutput.style.display = 'block'; // Reset scroll scrollContainer.scrollTop = 0; } function updateFontSize(size) { markdownOutput.style.fontSize = `${size}px`; } function toggleScrolling() { if (isScrolling) { stopScrolling(); } else { startScrolling(); } } function startScrolling() { if (!markdownOutput.innerHTML) return; // Don't scroll if empty isScrolling = true; btnPlay.classList.add('active'); iconPlay.style.display = 'none'; iconPause.style.display = 'block'; animationId = requestAnimationFrame(step); } function stopScrolling() { isScrolling = false; btnPlay.classList.remove('active'); iconPlay.style.display = 'block'; iconPause.style.display = 'none'; cancelAnimationFrame(animationId); } function step() { if (!isScrolling) return; // Calculate scroll amount based on speed // Map 1-100 to sensible range. // Let's say max speed (100) is ~300 pixels per second? ~5px/frame at 60fps // Min speed (1) is ~10 pixels per second? ~0.16px/frame // Formula: (speed / 100) * maxPixelsPerFrame // Adding a base speed so 1 isn't 0. const maxSpeedValues = 5.0; const minSpeedValues = 0.2; // Exponential curve for better control at lower speeds // Normalized speed 0-1 const normalizedSpeed = (scrollSpeed - 1) / 99; // Linear interpolation? Or quadratic? Quadratic feels "acceleration" // Let's stick to linear for predictability first, user says "simple" const pixelsPerFrame = minSpeedValues + (normalizedSpeed * (maxSpeedValues - minSpeedValues)); scrollAccumulator += pixelsPerFrame; const pixelsToScroll = Math.floor(scrollAccumulator); if (pixelsToScroll >= 1) { // Stop if at bottom // Use a small buffer (1px) for float inconsistencies if (Math.ceil(scrollContainer.scrollTop + scrollContainer.clientHeight) >= scrollContainer.scrollHeight) { stopScrolling(); return; } scrollContainer.scrollTop += pixelsToScroll; scrollAccumulator -= pixelsToScroll; } animationId = requestAnimationFrame(step); } init();