feat: preview uses ObsiGate server-side rendering (Mermaid, highlight, wikilinks)
CI / lint (push) Successful in 15s
CI / security (push) Successful in 7s
CI / test (push) Successful in 23s
CI / build (push) Successful in 3s

Preview now saves content via API then fetches rendered HTML from GET /api/file/{vault}?path=... instead of client-side marked.parse(). This enables Mermaid diagrams, syntax highlighting, wikilink resolution, and frontmatter cards. Demo mode still uses marked.js fallback. Debounce increased to 500ms. Loading spinner during render.
This commit is contained in:
2026-06-06 00:53:40 -04:00
parent 3090fe62fe
commit 88185c70e0
+33 -7
View File
@@ -604,21 +604,47 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
});
}
// ---- Markdown Preview ----
// ---- Markdown Preview (server-side rendering via ObsiGate API) ----
var previewTimer = null;
var _previewLoading = false;
function schedulePreview() {
if (!previewVisible) return;
clearTimeout(previewTimer);
previewTimer = setTimeout(renderPreview, 300);
previewTimer = setTimeout(renderPreview, 500);
}
function renderPreview() {
if (!previewVisible) return;
try {
previewContent.innerHTML = marked.parse(val()) || '<div class="preview-loading">Empty document</div>';
} catch(e) {
previewContent.innerHTML = '<div class="preview-loading">Preview error</div>';
if (_previewLoading) return;
// Demo mode: fallback to client-side marked
if (!fileVault || !filePath) {
try {
previewContent.innerHTML = marked.parse(val()) || '<div class="preview-loading">Empty document</div>';
} catch(e) {
previewContent.innerHTML = '<div class="preview-loading">Preview error</div>';
}
return;
}
// Save first, then fetch server-rendered HTML
_previewLoading = true;
previewContent.innerHTML = '<div class="preview-loading"><span class="spin"></span> Rendering...</div>';
var content = val();
fetch('/api/file/' + encodeURIComponent(fileVault) + '/save?path=' + encodeURIComponent(filePath) + '&backup=false', {
method: 'PUT', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ content: content })
}).then(function(r) {
if (!r.ok) throw new Error('Save failed');
return fetch('/api/file/' + encodeURIComponent(fileVault) + '?path=' + encodeURIComponent(filePath));
}).then(function(r) {
if (!r.ok) throw new Error('Render failed');
return r.json();
}).then(function(data) {
previewContent.innerHTML = data.html || '<div class="preview-loading">Empty document</div>';
_previewLoading = false;
}).catch(function(e) {
previewContent.innerHTML = '<div class="preview-loading">Render error: ' + e.message + '</div>';
_previewLoading = false;
});
}
function togglePreview() {
@@ -627,7 +653,7 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
resizeHandle.style.display = previewVisible ? '' : 'none';
btnPreview.style.color = previewVisible ? 'var(--accent)' : '';
btnPreview.style.background = previewVisible ? 'var(--accent-bg)' : '';
if (previewVisible) renderPreview();
if (previewVisible) { renderPreview(); }
}
// ---- Sync scroll between editor and preview ----