Files
Void-Homelab/public/components/markdown_editor.js
root 6d01cb34a7 feat(ui): page header actions — Edit (accent) + Revisions menu (view/restore) + Export, top-right
Editor refactored to expose controls; Edit moved into the doc header as the orange
primary action; new Revisions dropdown lists page_revisions with a modal preview + Restore.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 23:26:36 +10:00

56 lines
2.0 KiB
JavaScript

// Single-pane Markdown view with an Edit/Done toggle. Returns the pane plus its
// controls (toggle + save) so the page can place them in the header. Edit is the
// accent (orange) action; Save appears only in edit mode.
import { marked } from '../vendor/marked.esm.js';
import DOMPurify from '../vendor/purify.es.mjs';
import { el } from '../dom.js';
marked.setOptions({ gfm: true, breaks: true });
export function markdownEditor({ initial = '', save }) {
let editing = false;
const ta = el('textarea', {
style: {
width: '100%', minHeight: '460px', resize: 'vertical',
fontFamily: 'var(--font-mono)', fontSize: '13px', lineHeight: '1.5'
}
});
ta.value = initial;
const preview = el('div', { class: 'md-preview' });
const rerender = () => { preview.innerHTML = DOMPurify.sanitize(marked.parse(ta.value)); };
rerender();
const pane = el('div', {}, preview);
const stamp = el('span', { class: 'muted', style: { fontSize: '11px' } }, '');
const saveBtn = el('button', {
class: 'ghost', style: { display: 'none' },
onclick: async () => {
saveBtn.disabled = true;
try {
const updated = await save(ta.value);
stamp.textContent = updated?.updated_at ? 'Saved ' + new Date(updated.updated_at).toLocaleTimeString() : 'Saved';
} catch (e) { stamp.textContent = 'Save failed: ' + e.message; }
finally { saveBtn.disabled = false; }
}
}, 'Save');
const toggle = el('button', {
class: 'primary',
onclick: () => {
editing = !editing;
if (editing) { pane.replaceChild(ta, preview); ta.focus(); toggle.textContent = 'Done'; saveBtn.style.display = ''; }
else { rerender(); pane.replaceChild(preview, ta); toggle.textContent = 'Edit'; saveBtn.style.display = 'none'; }
}
}, 'Edit');
return {
pane,
controls: el('span', { class: 'ed-controls' }, toggle, saveBtn, stamp),
setValue: (md) => { ta.value = md; if (!editing) rerender(); },
value: () => ta.value
};
}