From 38a188e6e2986c7ccc484de0939a7feabf00d35e Mon Sep 17 00:00:00 2001 From: bruno Date: Wed, 22 Jul 2026 09:27:15 -0400 Subject: [PATCH] local-workspace: fix preview content rendering for all document types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- app/templates/local_workspace.html | 89 ++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 30 deletions(-) diff --git a/app/templates/local_workspace.html b/app/templates/local_workspace.html index d11423d..ca30ab8 100644 --- a/app/templates/local_workspace.html +++ b/app/templates/local_workspace.html @@ -849,64 +849,93 @@ window._wsInitData = (function() { var body = document.getElementById('ws-preview-body'); if (!body) return; if (node.is_folder) { - body.innerHTML = '
' + body.innerHTML = '
' + '' + getSvgIcon('folder',32) + '' - + '
' + (node.child_count||0) + ' item(s)
' - + 'Open folder' + + '
' + (node.child_count||0) + ' item(s)
' + + 'Open folder' + '
'; 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 = '
' - + '' + body.innerHTML = '
' + + '' + '
'; return; } + // PDF: show icon + open link if (/\.pdf$/.test(name)) { - body.innerHTML = '
' - + '' + getSvgIcon('file',32) + '' - + '
' + this._escHtml(node.name) + '
' - + '
' + (node.size_display || '') + '
' - + 'Open in viewer' + body.innerHTML = '
' + + '' + getSvgIcon('file',32) + '' + + '
' + this._escHtml(node.name) + '
' + + '
' + (node.size_display || '') + '
' + + 'Open in viewer' + '
'; 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 = ''; - return; - } - } catch(e) {} - preview = '
' - + '' + getSvgIcon('file',32) + '' - + (node.size_display || '') + ' — open to view
'; + // Uploaded file — show metadata + preview = '
' + + '
' + getSvgIcon('file',32) + '
' + + '
' + this._escHtml(node.name) + '
' + + '
' + (node.size_display || '') + '
' + + 'Open file' + + '
'; + } 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 '
' + this._escHtml(txt) + '
'; + 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) : '
') + ''; }, this).join(''); + } else { + preview = '
Empty document
'; } - } catch(e) { preview = '
' + this._escHtml(String(content).substring(0, 2000)) + '
'; } + } catch(e) { + preview = '
' + this._escHtml(String(content).substring(0, 3000)) + '
'; + } + + } else if (format === 'markdown') { + // Markdown — render as preformatted for now + preview = '
' + this._escHtml(String(content).substring(0, 5000)) + '
'; + } else { - preview = '
' + this._escHtml(String(content).substring(0, 2000)) + '
'; + // Unknown format — show as text + var display = String(content || '').trim(); + if (display) { + preview = '
' + this._escHtml(display.substring(0, 5000)) + '
'; + } else { + preview = '
No content
'; + } } - body.innerHTML = preview || '
(empty)
'; + body.innerHTML = preview || '
(empty)
'; } catch(e) { - body.innerHTML = '
Preview not available
'; + body.innerHTML = '
Preview not available
'; } },