๐Ÿ”’ preview://howtohtml/live-output
LIVE
preview://howtohtml/fullscreen
๐ŸŒฟ main
โฑ ready
Ln 1, Col 1
HTML
UTF-8
Powered by PiDice
โœ… Ready
\n' + ''; } // ============================================================ // AUTO-RESIZE IFRAME TO FIT CONTENT // This makes the iframe grow to the full height of the // rendered content so the parent wrapper can scroll it. // ============================================================ function autoResizeFrame(iframe) { try { // Wait for iframe to load its content iframe.addEventListener('load', function handleLoad() { // Remove listener after first run to avoid loops iframe.removeEventListener('load', handleLoad); try { const doc = iframe.contentDocument || iframe.contentWindow.document; if (!doc || !doc.documentElement) return; // Function to set height based on content const setHeight = function() { const body = doc.body; const html = doc.documentElement; if (!body || !html) return; const height = Math.max( body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight ); iframe.style.height = Math.max(height, 100) + 'px'; }; // Initial resize setHeight(); // Resize again after a short delay (for fonts, images, animations) setTimeout(setHeight, 100); setTimeout(setHeight, 500); setTimeout(setHeight, 1200); // Watch for size changes using ResizeObserver if (typeof ResizeObserver !== 'undefined' && doc.body) { const ro = new ResizeObserver(function() { setHeight(); }); ro.observe(doc.body); } } catch (e) { // Cross-origin restrictions โ€” fallback console.warn('Auto-resize limited:', e); } }); } catch (e) { console.warn('Auto-resize error:', e); } } // ============================================================ // UPDATE PREVIEW // ============================================================ function updatePreview(showFeedback) { showFeedback = showFeedback || false; try { const doc = buildSrcDoc(); // Reset height so auto-resize starts fresh previewFrame.style.height = '100%'; previewFrame.srcdoc = doc; // Attach auto-resize handler autoResizeFrame(previewFrame); // Also update fullscreen frame if active if (isPreviewFullscreen) { fullscreenFrame.style.height = '100%'; fullscreenFrame.srcdoc = doc; autoResizeFrame(fullscreenFrame); } const now = new Date(); lastUpdate.textContent = now.toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' }); if (showFeedback) showToast('Preview updated successfully', 'โœ…'); } catch (err) { console.error('Build error:', err); } } // ============================================================ // DEBOUNCED UPDATE // ============================================================ function scheduleUpdate() { clearTimeout(updateTimer); updateTimer = setTimeout(function() { updatePreview(false); }, 300); } // ============================================================ // TOAST // ============================================================ function showToast(message, icon) { icon = icon || 'โœ…'; toastMsg.textContent = message; toast.querySelector('.toast-icon').textContent = icon; toast.classList.add('show'); clearTimeout(toastTimer); toastTimer = setTimeout(function() { toast.classList.remove('show'); }, 2400); } // ============================================================ // ACTIVE TEXTAREA HELPER // ============================================================ function getActiveTextarea() { if (activePanel === 'html') return htmlArea; if (activePanel === 'css') return cssArea; if (activePanel === 'js') return jsArea; return null; } // ============================================================ // PREVIEW FULLSCREEN // ============================================================ function enterPreviewFullscreen() { // Sync current preview content const doc = buildSrcDoc(); fullscreenFrame.style.height = '100%'; fullscreenFrame.srcdoc = doc; autoResizeFrame(fullscreenFrame); previewFullscreenOverlay.classList.add('active'); isPreviewFullscreen = true; showToast('Preview fullscreen mode โ€” scroll to see full page', 'โ›ถ'); } function exitPreviewFullscreen() { previewFullscreenOverlay.classList.remove('active'); isPreviewFullscreen = false; setTimeout(function() { fullscreenFrame.srcdoc = ''; fullscreenFrame.style.height = '100%'; }, 200); } previewFullscreenBtn.addEventListener('click', function() { if (isPreviewFullscreen) { exitPreviewFullscreen(); } else { enterPreviewFullscreen(); } }); previewExitBtn.addEventListener('click', exitPreviewFullscreen); document.addEventListener('keydown', function(e) { if (e.key === 'Escape' && isPreviewFullscreen) { e.preventDefault(); exitPreviewFullscreen(); } }); // ============================================================ // DOWNLOAD FUNCTIONALITY // ============================================================ function downloadFile(filename, content) { const blob = new Blob([content], { type: 'text/plain;charset=utf-8' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; a.style.display = 'none'; document.body.appendChild(a); a.click(); setTimeout(function() { document.body.removeChild(a); URL.revokeObjectURL(url); }, 100); } downloadBtn.addEventListener('click', function() { const htmlContent = htmlArea.value; const cssContent = cssArea.value; const jsContent = jsArea.value; const htmlHasContent = htmlContent.trim().length > 0; const cssHasContent = cssContent.trim().length > 0; const jsHasContent = jsContent.trim().length > 0; const downloaded = []; if (htmlHasContent) { downloadFile('index.html', htmlContent); downloaded.push('index.html'); } if (cssHasContent) { downloadFile('style.css', cssContent); downloaded.push('style.css'); } if (jsHasContent) { downloadFile('script.js', jsContent); downloaded.push('script.js'); } if (downloaded.length === 0) { showToast('Nothing to download โ€” all editors are empty', 'โš ๏ธ'); } else if (downloaded.length === 1) { showToast('Downloaded: ' + downloaded[0], 'โฌ‡๏ธ'); } else { showToast('Downloaded ' + downloaded.length + ' files: ' + downloaded.join(', '), 'โฌ‡๏ธ'); } }); // ============================================================ // INPUT LISTENERS // ============================================================ [htmlArea, cssArea, jsArea].forEach(function(area) { area.addEventListener('input', function() { scheduleUpdate(); updateAllLineNumbers(); }); area.addEventListener('keydown', function(e) { if (e.key === 'Tab') { e.preventDefault(); const start = area.selectionStart; const end = area.selectionEnd; area.value = area.value.substring(0, start) + ' ' + area.value.substring(end); area.selectionStart = area.selectionEnd = start + 2; scheduleUpdate(); updateAllLineNumbers(); } }); area.addEventListener('keyup', updateCursorPosition); area.addEventListener('click', updateCursorPosition); }); function updateCursorPosition() { const area = getActiveTextarea(); if (!area) { lineColInfo.innerHTML = 'โ€”'; return; } const pos = area.selectionStart; const text = area.value.substring(0, pos); const lines = text.split('\n'); const line = lines.length; const col = lines[lines.length - 1].length + 1; lineColInfo.innerHTML = 'Ln ' + line + ', Col ' + col + ''; } // ============================================================ // KEYBOARD SHORTCUTS // ============================================================ document.addEventListener('keydown', function(e) { if (e.ctrlKey || e.metaKey) { if (e.key === '1') { e.preventDefault(); switchTab('html'); } if (e.key === '2') { e.preventDefault(); switchTab('css'); } if (e.key === '3') { e.preventDefault(); switchTab('js'); } if (e.key === '4') { e.preventDefault(); switchTab('preview'); } if (e.key === 'Enter') { e.preventDefault(); updatePreview(true); } if (e.key === 's') { e.preventDefault(); downloadBtn.click(); } } }); // ============================================================ // RUN BUTTON // ============================================================ runBtn.addEventListener('click', function() { updatePreview(true); runBtn.style.transform = 'scale(0.85)'; setTimeout(function() { runBtn.style.transform = ''; }, 150); }); // ============================================================ // CLEAR BUTTON // ============================================================ clearBtn.addEventListener('click', function() { if (!htmlArea.value && !cssArea.value && !jsArea.value) { showToast('Editors are already empty', 'โ„น๏ธ'); return; } if (confirm('Clear all editors? This will reset HTML, CSS and JavaScript.')) { htmlArea.value = ''; cssArea.value = ''; jsArea.value = ''; updatePreview(false); updateAllLineNumbers(); showToast('All editors cleared', '๐Ÿ—‘๏ธ'); switchTab('html'); htmlArea.focus(); } }); // ============================================================ // LOCALSTORAGE PERSISTENCE // ============================================================ function saveToLocalStorage() { try { localStorage.setItem('howtohtml_html', htmlArea.value); localStorage.setItem('howtohtml_css', cssArea.value); localStorage.setItem('howtohtml_js', jsArea.value); } catch (e) { /* ignore */ } } function loadFromLocalStorage() { try { const savedHtml = localStorage.getItem('howtohtml_html'); const savedCss = localStorage.getItem('howtohtml_css'); const savedJs = localStorage.getItem('howtohtml_js'); if (savedHtml !== null) htmlArea.value = savedHtml; if (savedCss !== null) cssArea.value = savedCss; if (savedJs !== null) jsArea.value = savedJs; } catch (e) { /* ignore */ } } // ============================================================ // INITIALIZATION // ============================================================ window.addEventListener('load', function() { loadFromLocalStorage(); updateAllLineNumbers(); updatePreview(false); updateCursorPosition(); setTimeout(function() { showToast('How To Html by PiDice ยท ready to code', '๐Ÿš€'); }, 400); }); setInterval(saveToLocalStorage, 3000); window.addEventListener('beforeunload', saveToLocalStorage); window.addEventListener('pageshow', function(e) { if (e.persisted) { updatePreview(false); updateAllLineNumbers(); } }); // ============================================================ // EXPOSE API // ============================================================ window.__howtohtml = { updatePreview: updatePreview, switchTab: switchTab, buildSrcDoc: buildSrcDoc, enterPreviewFullscreen: enterPreviewFullscreen, exitPreviewFullscreen: exitPreviewFullscreen, getState: function() { return { html: htmlArea.value, css: cssArea.value, js: jsArea.value }; } }; })();