feat: preview uses ObsiGate server-side rendering (Mermaid, highlight, wikilinks)
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:
@@ -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 previewTimer = null;
|
||||||
|
var _previewLoading = false;
|
||||||
function schedulePreview() {
|
function schedulePreview() {
|
||||||
if (!previewVisible) return;
|
if (!previewVisible) return;
|
||||||
clearTimeout(previewTimer);
|
clearTimeout(previewTimer);
|
||||||
previewTimer = setTimeout(renderPreview, 300);
|
previewTimer = setTimeout(renderPreview, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderPreview() {
|
function renderPreview() {
|
||||||
if (!previewVisible) return;
|
if (!previewVisible) return;
|
||||||
try {
|
if (_previewLoading) return;
|
||||||
previewContent.innerHTML = marked.parse(val()) || '<div class="preview-loading">Empty document</div>';
|
// Demo mode: fallback to client-side marked
|
||||||
} catch(e) {
|
if (!fileVault || !filePath) {
|
||||||
previewContent.innerHTML = '<div class="preview-loading">Preview error</div>';
|
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() {
|
function togglePreview() {
|
||||||
@@ -627,7 +653,7 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
|
|||||||
resizeHandle.style.display = previewVisible ? '' : 'none';
|
resizeHandle.style.display = previewVisible ? '' : 'none';
|
||||||
btnPreview.style.color = previewVisible ? 'var(--accent)' : '';
|
btnPreview.style.color = previewVisible ? 'var(--accent)' : '';
|
||||||
btnPreview.style.background = previewVisible ? 'var(--accent-bg)' : '';
|
btnPreview.style.background = previewVisible ? 'var(--accent-bg)' : '';
|
||||||
if (previewVisible) renderPreview();
|
if (previewVisible) { renderPreview(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Sync scroll between editor and preview ----
|
// ---- Sync scroll between editor and preview ----
|
||||||
|
|||||||
Reference in New Issue
Block a user