Virtual Keyboard
Typed Log
Your typed log will appear here...
[${entry.timestamp}] - Key: ${escapeHTML(entry.key)}
` ).join(''); ui.logArea.scrollTop = ui.logArea.scrollHeight; // Auto-scroll } function logEntry(key) { const timestamp = new Date().toLocaleString('en-US'); typedLog.push({ timestamp, key }); renderLog(); } // --- EVENT HANDLERS & LOGIC --- function handleKeyClick(e) { const keyEl = e.target.closest('.kb-key'); if (!keyEl) return; const { key, shift, code, special } = keyEl.dataset; let charToType = ''; if (!special) { const isLetter = key.length === 1 && key.match(/[a-z]/i); if (isShift) { charToType = shift; } else if (isCaps && isLetter) { charToType = shift; } else { charToType = key; } } switch(code) { case 'Backspace': ui.outputArea.value = ui.outputArea.value.slice(0, -1); logEntry('Backspace'); break; case 'Tab': ui.outputArea.value += ' '; // 4 spaces logEntry('Tab (4 spaces)'); break; case 'CapsLock': isCaps = !isCaps; updateKeyboard(); logEntry(isCaps ? 'Caps Lock ON' : 'Caps Lock OFF'); break; case 'Enter': ui.outputArea.value += '\n'; logEntry('Enter'); break; case 'ShiftLeft': case 'ShiftRight': isShift = !isShift; updateKeyboard(); logEntry(isShift ? 'Shift ON' : 'Shift OFF'); break; case 'Space': ui.outputArea.value += ' '; logEntry('Space'); break; default: // This handles all normal keys (a, 1, `, etc.) ui.outputArea.value += charToType; logEntry(charToType); // If shift was on, turn it off after one keypress if (isShift) { isShift = false; updateKeyboard(); } break; } } function handleClearLog() { typedLog = []; renderLog(); } function handleDownloadPdf() { const { jsPDF } = window.jspdf; if (!jsPDF || !window.jspdf.autoTable) { alert("PDF library not loaded!"); return; } if (typedLog.length === 0) { alert("Log is empty. Type something first."); return; } const doc = new jsPDF(); doc.setFontSize(18); doc.text("Keyboard Typed Log", 14, 22); doc.autoTable({ startY: 30, head: [['Timestamp', 'Key Pressed']], body: typedLog.map(entry => [entry.timestamp, entry.key]), theme: 'striped', headStyles: { fillColor: [59, 130, 246] } // theme-active-bg }); doc.save("keyboard-log.pdf"); } // --- TABBING LOGIC --- function switchTab(targetTabId) { ui.tabContents.forEach(c => c.classList.remove('active')); ui.tabButtons.forEach(b => b.classList.remove('active')); document.getElementById(`kb-tab-${targetTabId}`).classList.add('active'); document.querySelector(`.kb-tab-button[data-tab="${targetTabId}"]`).classList.add('active'); } function navigateTabs(dir) { const tabs = Array.from(ui.tabButtons); const active = tabs.find(t => t.classList.contains('active')); let index = tabs.indexOf(active) + dir; if (index < 0) index = 0; if (index >= tabs.length) index = tabs.length - 1; switchTab(tabs[index].dataset.tab); } // --- UTILITY --- function escapeHTML(str) { if (!str) return ''; const p = document.createElement('p'); p.textContent = str; return p.innerHTML; } // --- MAIN INITIALIZATION --- document.addEventListener('DOMContentLoaded', function () { // Populate UI object ui = { outputArea: document.getElementById('kb-output-area'), keyboardContainer: document.getElementById('kb-keyboard-container'), logArea: document.getElementById('kb-log-area'), clearLogBtn: document.getElementById('kb-clear-log-btn'), downloadPdfBtn: document.getElementById('kb-download-pdf-btn'), tabButtons: document.querySelectorAll('.kb-tab-button'), tabContents: document.querySelectorAll('.kb-tab-content'), nextTabBtn: document.getElementById('kb-next-tab-btn'), prevTabBtn: document.getElementById('kb-prev-tab-btn'), allKeys: [] // Will be populated by createKeyboard() }; // Attach Event Listeners ui.keyboardContainer.addEventListener('click', handleKeyClick); ui.clearLogBtn.addEventListener('click', handleClearLog); ui.downloadPdfBtn.addEventListener('click', handleDownloadPdf); ui.tabButtons.forEach(button => button.addEventListener('click', () => switchTab(button.dataset.tab))); ui.nextTabBtn.addEventListener('click', () => navigateTabs(1)); ui.prevTabBtn.addEventListener('click', () => navigateTabs(-1)); // --- INITIALIZATION --- createKeyboard(); renderLog(); });