📖

Markdown Editor

Editor
Live preview on the right
Preview

Write and preview Markdown with live rendering. Export as Markdown or HTML. All processing happens locally.

${html} `; const blob = new Blob([fullHtml], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'document.html'; a.click(); URL.revokeObjectURL(url); } function clearEditor() { markdownEditor.value = ''; updatePreview(); } markdownEditor.addEventListener('input', updatePreview); exportMdBtn.addEventListener('click', exportMarkdown); exportHtmlBtn.addEventListener('click', exportHtml); clearBtn.addEventListener('click', clearEditor); // Keyboard shortcuts markdownEditor.addEventListener('keydown', (e) => { if (e.ctrlKey || e.metaKey) { switch (e.key) { case 'b': e.preventDefault(); insertText('**', '**'); break; case 'i': e.preventDefault(); insertText('*', '*'); break; case 'k': e.preventDefault(); insertText('[', '](url)'); const selStart = markdownEditor.selectionStart; markdownEditor.setSelectionRange(selStart - 4, selStart - 1); break; case 'e': e.preventDefault(); exportMarkdown(); break; } } // Tab for indentation if (e.key === 'Tab' && !e.ctrlKey && !e.metaKey) { e.preventDefault(); const start = markdownEditor.selectionStart; const end = markdownEditor.selectionEnd; if (e.shiftKey) { // Unindent const lines = markdownEditor.value.split('\n'); const lineStart = markdownEditor.value.lastIndexOf('\n', start - 1) + 1; const lineNum = markdownEditor.value.substring(0, start).split('\n').length - 1; if (lines[lineNum] && lines[lineNum].startsWith(' ')) { lines[lineNum] = lines[lineNum].substring(2); markdownEditor.value = lines.join('\n'); markdownEditor.setSelectionRange(Math.max(0, start - 2), Math.max(0, end - 2)); updatePreview(); } } else { // Indent insertText(' ', ''); } } }); // Initial preview updatePreview();