local-workspace: fix preview content rendering for all document types
Root cause: blocks format content is a direct array [{id,type,content}],
not wrapped in {blocks:[...]}. Old code checked blocks.blocks which was
always undefined, so internal pages showed nothing.
Fixes:
- Handle blocks as raw array or {blocks:[...]} wrapper (both formats)
- Add heading styling (h3-h6 based on heading_1/2/3), code blocks (pre)
- Add proper markdown format handler (renders as pre-wrapped text)
- Add format=file handler showing file metadata + Open file link
- Add unknown format fallback as pre-wrapped text with char limit
- Clean up escaped quotes — no more double-escaped \" in strings
- Better empty state messages (Empty document, No content)
This commit is contained in:
@@ -849,64 +849,93 @@ window._wsInitData = (function() {
|
||||
var body = document.getElementById('ws-preview-body');
|
||||
if (!body) return;
|
||||
if (node.is_folder) {
|
||||
body.innerHTML = '<div style=\"text-align:center;padding:40px;color:var(--text-secondary)\">'
|
||||
body.innerHTML = '<div style="text-align:center;padding:40px;color:var(--text-secondary)">'
|
||||
+ '<span>' + getSvgIcon('folder',32) + '</span>'
|
||||
+ '<div style=\"font-size:14px\">' + (node.child_count||0) + ' item(s)</div>'
|
||||
+ '<a href=\"/local-workspace?folder=' + node.id + '\" class=\"btn btn-secondary\" style=\"display:inline-block;text-decoration:none;margin-top:16px\">Open folder</a>'
|
||||
+ '<div style="font-size:14px">' + (node.child_count||0) + ' item(s)</div>'
|
||||
+ '<a href="/local-workspace?folder=' + node.id + '" class="btn btn-secondary" style="display:inline-block;text-decoration:none;margin-top:16px">Open folder</a>'
|
||||
+ '</div>';
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var name = (node.name || '').toLowerCase();
|
||||
|
||||
// Images: try direct file URL
|
||||
if (/\.(png|jpe?g|gif|webp|svg|bmp|ico)$/.test(name)) {
|
||||
var wsId = 1;
|
||||
var imgUrl = '/api/files/' + wsId + '/' + encodeURIComponent(node.name);
|
||||
body.innerHTML = '<div style=\"text-align:center;padding:12px;\">'
|
||||
+ '<img src=\"' + imgUrl + '\" style=\"max-width:100%;max-height:50vh;border-radius:8px;\">'
|
||||
body.innerHTML = '<div style="text-align:center;padding:12px;">'
|
||||
+ '<img src="/api/files/1/' + encodeURIComponent(node.name) + '" style="max-width:100%;max-height:50vh;border-radius:8px;">'
|
||||
+ '</div>';
|
||||
return;
|
||||
}
|
||||
// PDF: show icon + open link
|
||||
if (/\.pdf$/.test(name)) {
|
||||
body.innerHTML = '<div style=\"text-align:center;padding:24px;color:var(--text-secondary);\">'
|
||||
+ '<span style=\"font-size:48px;display:block;margin-bottom:8px;\">' + getSvgIcon('file',32) + '</span>'
|
||||
+ '<div style=\"font-size:14px;margin-bottom:4px;\">' + this._escHtml(node.name) + '</div>'
|
||||
+ '<div style=\"font-size:12px;color:var(--text-tertiary);margin-bottom:16px;\">' + (node.size_display || '') + '</div>'
|
||||
+ '<a href=\"/pages/' + node.id + '\" class=\"btn btn-secondary\" style=\"display:inline-block;text-decoration:none;\">Open in viewer</a>'
|
||||
body.innerHTML = '<div style="text-align:center;padding:24px;color:var(--text-secondary);">'
|
||||
+ '<span style="font-size:48px;display:block;margin-bottom:8px;">' + getSvgIcon('file',32) + '</span>'
|
||||
+ '<div style="font-size:14px;margin-bottom:4px;">' + this._escHtml(node.name) + '</div>'
|
||||
+ '<div style="font-size:12px;color:var(--text-tertiary);margin-bottom:16px;">' + (node.size_display || '') + '</div>'
|
||||
+ '<a href="/pages/' + node.id + '" class="btn btn-secondary" style="display:inline-block;text-decoration:none;">Open in viewer</a>'
|
||||
+ '</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch content from API
|
||||
var r = await this._fetchRetry('/api/local-workspace/page-content/' + node.id, true);
|
||||
var content = r.content || '';
|
||||
var format = r.format || '';
|
||||
var preview = '';
|
||||
|
||||
if (format === 'file') {
|
||||
try {
|
||||
var meta = JSON.parse(content);
|
||||
if (meta.file_path && /\.(png|jpe?g|gif|webp|svg|bmp|ico)$/i.test(meta.file_path)) {
|
||||
var imgUrl2 = '/api/files/' + 1 + '/' + encodeURIComponent(meta.file_path);
|
||||
body.innerHTML = '<img src=\"' + imgUrl2 + '\" style=\"max-width:100%;max-height:50vh;border-radius:8px;\">';
|
||||
return;
|
||||
}
|
||||
} catch(e) {}
|
||||
preview = '<div style=\"text-align:center;padding:20px;color:var(--text-secondary)\">'
|
||||
+ '<span>' + getSvgIcon('file',32) + '</span>'
|
||||
+ (node.size_display || '') + ' — open to view</div>';
|
||||
// Uploaded file — show metadata
|
||||
preview = '<div style="text-align:center;padding:30px;color:var(--text-secondary)">'
|
||||
+ '<div style="font-size:48px;margin-bottom:12px;">' + getSvgIcon('file',32) + '</div>'
|
||||
+ '<div style="font-size:15px;font-weight:600;margin-bottom:4px;">' + this._escHtml(node.name) + '</div>'
|
||||
+ '<div style="font-size:13px;color:var(--text-tertiary);margin-bottom:16px;">' + (node.size_display || '') + '</div>'
|
||||
+ '<a href="/pages/' + node.id + '" class="btn btn-secondary" style="display:inline-block;text-decoration:none;">Open file</a>'
|
||||
+ '</div>';
|
||||
|
||||
} else if (format === 'blocks') {
|
||||
// FlowDeck internal page — blocks can be array or {blocks:[...]}
|
||||
try {
|
||||
var blocks = typeof content === 'string' ? JSON.parse(content) : content;
|
||||
if (blocks && blocks.blocks) {
|
||||
preview = blocks.blocks.slice(0, 30).map(function(b) {
|
||||
var blockList = Array.isArray(blocks) ? blocks : (blocks && blocks.blocks ? blocks.blocks : []);
|
||||
if (blockList.length > 0) {
|
||||
preview = blockList.slice(0, 50).map(function(b) {
|
||||
var txt = b.content || b.text || (typeof b === 'string' ? b : '');
|
||||
return '<div style=\"margin-bottom:6px;line-height:1.5\">' + this._escHtml(txt) + '</div>';
|
||||
var tag = 'div';
|
||||
var style = 'margin-bottom:6px;line-height:1.5;';
|
||||
if (b.type && b.type.indexOf('heading') === 0) {
|
||||
var lvl = parseInt(b.type.split('_')[1]) || 1;
|
||||
tag = 'h' + Math.min(lvl + 2, 6);
|
||||
style += 'font-weight:600;';
|
||||
}
|
||||
if (b.type === 'code' || b.type === 'code_block') {
|
||||
tag = 'pre';
|
||||
style += 'background:var(--bg-tertiary);padding:10px 14px;border-radius:6px;font-family:monospace;font-size:12px;white-space:pre-wrap;';
|
||||
}
|
||||
return '<' + tag + ' style="' + style + '">' + (txt ? this._escHtml(txt) : '<br>') + '</' + tag + '>';
|
||||
}, this).join('');
|
||||
} else {
|
||||
preview = '<div style="text-align:center;padding:30px;color:var(--text-tertiary)">Empty document</div>';
|
||||
}
|
||||
} catch(e) { preview = '<pre>' + this._escHtml(String(content).substring(0, 2000)) + '</pre>'; }
|
||||
} catch(e) {
|
||||
preview = '<pre style="background:var(--bg-tertiary);padding:14px;border-radius:6px;overflow:auto;font-size:12px;font-family:monospace;white-space:pre-wrap;">' + this._escHtml(String(content).substring(0, 3000)) + '</pre>';
|
||||
}
|
||||
|
||||
} else if (format === 'markdown') {
|
||||
// Markdown — render as preformatted for now
|
||||
preview = '<div style="white-space:pre-wrap;line-height:1.6;font-size:13px;">' + this._escHtml(String(content).substring(0, 5000)) + '</div>';
|
||||
|
||||
} else {
|
||||
preview = '<pre>' + this._escHtml(String(content).substring(0, 2000)) + '</pre>';
|
||||
// Unknown format — show as text
|
||||
var display = String(content || '').trim();
|
||||
if (display) {
|
||||
preview = '<pre style="background:var(--bg-tertiary);padding:14px;border-radius:6px;overflow:auto;font-size:12px;font-family:monospace;white-space:pre-wrap;">' + this._escHtml(display.substring(0, 5000)) + '</pre>';
|
||||
} else {
|
||||
preview = '<div style="text-align:center;padding:30px;color:var(--text-tertiary)">No content</div>';
|
||||
}
|
||||
}
|
||||
body.innerHTML = preview || '<div style=\"color:var(--text-tertiary);text-align:center;padding:20px\">(empty)</div>';
|
||||
body.innerHTML = preview || '<div style="color:var(--text-tertiary);text-align:center;padding:20px">(empty)</div>';
|
||||
} catch(e) {
|
||||
body.innerHTML = '<div style=\"text-align:center;padding:20px;color:var(--text-tertiary)\">Preview not available</div>';
|
||||
body.innerHTML = '<div style="text-align:center;padding:20px;color:var(--text-tertiary)">Preview not available</div>';
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user