diff --git a/app/routers/board.py b/app/routers/board.py
index 3a887aa..0234be0 100644
--- a/app/routers/board.py
+++ b/app/routers/board.py
@@ -946,7 +946,9 @@ async def delete_page(request: Request, page_id: int):
@router.get("/pages/{page_id}", response_class=HTMLResponse)
async def view_page(request: Request, page_id: int):
- """Render a page as HTML, or a file viewer for uploaded files."""
+ """Render a page as HTML, or a file viewer for uploaded files.
+ ?embed=1 — minimal mode for side peek (no sidebar/topbar)."""
+ embed = request.query_params.get("embed") == "1"
from jinja2 import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader("app/templates"))
with get_conn() as conn:
@@ -1003,7 +1005,8 @@ async def view_page(request: Request, page_id: int):
"page_data": page_data,
"breadcrumb_items": nav_crumbs,
"nav_workspace_id": page.get("workspace_id") or 0,
- "nav_page_id": page_id}
+ "nav_page_id": page_id,
+ "embed_mode": embed}
template = env.get_template("page_editor.html")
response = template.render(**ctx)
return HTMLResponse(content=response, headers={"Cache-Control": "no-store, max-age=0"})
diff --git a/app/templates/base.html b/app/templates/base.html
index d7dee33..704a6be 100644
--- a/app/templates/base.html
+++ b/app/templates/base.html
@@ -88,12 +88,18 @@
font-size: 13px;
}
}
+ /* Embed mode — hide sidebar + topbar for peek panels */
+ body.embed-mode .sidebar, body.embed-mode .topbar, body.embed-mode .app-layout { display: block !important; }
+ body.embed-mode .sidebar { display: none !important; }
+ body.embed-mode .topbar { display: none !important; }
+ body.embed-mode .app-layout { margin-left: 0 !important; padding-top: 0 !important; }
+ body.embed-mode .sidebar-collapsed .app-layout { margin-left: 0 !important; }
-
+
diff --git a/app/templates/library.html b/app/templates/library.html
index 1f74989..7529777 100644
--- a/app/templates/library.html
+++ b/app/templates/library.html
@@ -407,6 +407,7 @@
-
-
Loading…
-
-
-
{{ fd_icon("file",32) }}
-
No preview available
-
+
+
@@ -1202,40 +1198,17 @@ function libraryPage() {
// ── Side peek ──
async openPeek(item) {
this.peekItem = item;
- this.peekContent = '';
- this.peekLoading = true;
this.peekFavorited = item.favorited || false;
- try {
- var r = await fetch('/board/api/pages/' + item.id);
- var d = await r.json();
- var content = d.content || '';
- // Simple preview
- if (d.content_format === 'blocks') {
- try {
- var blocks = typeof content === 'string' ? JSON.parse(content) : content;
- var preview = '';
- var lines = Array.isArray(blocks) ? blocks.slice(0, 30) : ((blocks && blocks.blocks) ? blocks.blocks.slice(0, 30) : []);
- for (var i = 0; i < lines.length; i++) {
- var b = lines[i];
- var txt = b.content || b.text || (typeof b === 'string' ? b : '');
- if (txt && txt.trim()) preview += '
' + this._escHtml(String(txt)) + '
';
- }
- this.peekContent = preview || '
(empty page)
';
- } catch(e) {
- this.peekContent = '
' + this._escHtml(String(content).substring(0, 2000)) + '
';
- }
- } else {
- this.peekContent = content ? '
' + this._escHtml(String(content).substring(0, 2000)) + '
' : '';
- }
- } catch(e) {
- this.peekContent = '';
- }
- this.peekLoading = false;
+ // Load editor in iframe
+ var iframe = document.getElementById('lib-peek-iframe');
+ if (iframe) iframe.src = '/pages/' + item.id + '?embed=1';
},
closePeek() {
this.peekItem = null;
- this.peekContent = '';
+ // Clear iframe
+ var iframe = document.getElementById('lib-peek-iframe');
+ if (iframe) iframe.src = '';
},
async toggleFavoritePeek() {
@@ -1303,5 +1276,41 @@ function libraryPage() {
}
};
}
+// Init peek resize handle (vanilla JS)
+(function() {
+ var panel = document.querySelector('.peek-overlay');
+ var handle = document.getElementById('lib-peek-resize-handle');
+ if (!panel || !handle) return;
+ var startX, startWidth, dragging = false;
+ handle.addEventListener('mousedown', function(e) {
+ e.preventDefault();
+ startX = e.clientX;
+ startWidth = panel.offsetWidth;
+ dragging = true;
+ document.body.style.userSelect = 'none';
+ });
+ document.addEventListener('mousemove', function(e) {
+ if (!dragging) return;
+ var deltaX = startX - e.clientX;
+ var newWidth = Math.max(300, Math.min(window.innerWidth * 0.9, startWidth + deltaX));
+ panel.style.width = newWidth + 'px';
+ });
+ document.addEventListener('mouseup', function(e) {
+ if (!dragging) return;
+ document.body.style.userSelect = '';
+ var moved = Math.abs(e.clientX - startX);
+ if (moved < 3) {
+ // Click — close via Alpine data
+ var libData = document.querySelector('[x-data]')._x_dataStack[0];
+ if (libData && libData.closePeek) libData.closePeek();
+ }
+ dragging = false;
+ try { localStorage.setItem('fd_peek_width', panel.style.width); } catch(ex) {}
+ });
+ try {
+ var saved = localStorage.getItem('fd_peek_width');
+ if (saved) panel.style.width = saved;
+ } catch(ex) {}
+})();
{% endblock %}
diff --git a/app/templates/local_workspace.html b/app/templates/local_workspace.html
index 1236bfb..b44001e 100644
--- a/app/templates/local_workspace.html
+++ b/app/templates/local_workspace.html
@@ -631,6 +631,44 @@ window._wsInitData = (function() {
this.loadWorkspaceTags();
} catch(e) { console.error('Tree load error:', e); this.loaded = true; }
this._recompute();
+ this._initResizeHandle();
+ },
+
+ _initResizeHandle() {
+ var panel = document.getElementById('ws-preview-panel');
+ var handle = document.getElementById('ws-preview-resize-handle');
+ if (!panel || !handle) return;
+ var startX, startWidth, dragging = false;
+ handle.addEventListener('mousedown', function(e) {
+ e.preventDefault();
+ startX = e.clientX;
+ startWidth = panel.offsetWidth;
+ dragging = true;
+ document.body.style.userSelect = 'none';
+ });
+ document.addEventListener('mousemove', function(e) {
+ if (!dragging) return;
+ var deltaX = startX - e.clientX;
+ var newWidth = Math.max(300, Math.min(window.innerWidth * 0.9, startWidth + deltaX));
+ panel.style.width = newWidth + 'px';
+ });
+ document.addEventListener('mouseup', function(e) {
+ if (!dragging) return;
+ document.body.style.userSelect = '';
+ var moved = Math.abs(e.clientX - startX);
+ if (moved < 3) {
+ // Simple click — close panel
+ window._wsData.closePreviewPanel();
+ }
+ dragging = false;
+ // Save width preference
+ try { localStorage.setItem('fd_peek_width', panel.style.width); } catch(ex) {}
+ });
+ // Restore saved width
+ try {
+ var saved = localStorage.getItem('fd_peek_width');
+ if (saved) panel.style.width = saved;
+ } catch(ex) {}
},
_recompute() {
@@ -796,7 +834,7 @@ window._wsInitData = (function() {
return null;
},
- // ── Preview panel (vanilla JS — matches Library peek-overlay) ──
+ // ── Preview panel (vanilla JS — loads /pages/{id}?embed=1 in iframe) ──
selectForPreview(node) {
var panel = document.getElementById('ws-preview-panel');
if (!panel) return;
@@ -819,21 +857,9 @@ window._wsInitData = (function() {
window.showToast('Failed to copy', 'error');
});
};
- // Body: meta info header + loading indicator
- var meta = '
'
- + '' + (node.is_folder ? 'Folder' : this._fileTypeLabel(node.name, node.content_format)) + ''
- + '' + (node.size_display || '-') + ''
- + '' + this._formatDate(node.updated_at) + ''
- + '
';
- if (node.tags && node.tags.length > 0) {
- meta += '
';
- for (var i = 0; i < node.tags.length; i++) {
- var t = node.tags[i];
- meta += '' + this._escHtml(t.name) + '';
- }
- meta += '
';
- }
- document.getElementById('ws-preview-body').innerHTML = meta + '
Loading...
';
+ // Load the full page editor in iframe (reuses /pages/{id} code)
+ var iframe = document.getElementById('ws-preview-iframe');
+ if (iframe) iframe.src = '/pages/' + node.id + '?embed=1';
// Show panel with slide-in
panel.style.display = 'flex';
requestAnimationFrame(function() {
@@ -841,8 +867,6 @@ window._wsInitData = (function() {
panel.style.transform = 'translateX(0)';
});
});
- // Load content async
- this._loadPreviewContentVanilla(node);
},
closePreviewPanel() {
@@ -852,6 +876,9 @@ window._wsInitData = (function() {
var self = this;
setTimeout(function() {
panel.style.display = 'none';
+ // Clear iframe
+ var iframe = document.getElementById('ws-preview-iframe');
+ if (iframe) iframe.src = '';
self.previewItem = null;
}, 200);
},
@@ -2883,6 +2910,7 @@ console.log('FLOWDECK v2.4.6 — IIFE done, _wsInitData keys:', Object.keys(wind
-