From afe670bdd3afc78c1fbf6b02c1e065d2c47e0fde Mon Sep 17 00:00:00 2001 From: bruno Date: Wed, 22 Jul 2026 10:50:02 -0400 Subject: [PATCH] =?UTF-8?q?peek=20panels:=20reusable=20editor=20module=20?= =?UTF-8?q?=E2=80=94=20shared=20=5Fpage=5Feditor=5Fcontent.html=20+=20embe?= =?UTF-8?q?d=20template?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Architecture for zero code duplication: - Extract page editor HTML into _page_editor_content.html (shared include) - page_editor.html includes it (same behavior as before) - page_editor_embed.html includes it with empty topbar (no header in peek) - board.py: ?embed=1 renders page_editor_embed.html Peek panels now load /pages/{id}?embed=1 in iframe: - Editor renders without sidebar or header breadcrumb/actions - Full editing capability (blocks, markdown, slash commands, save) - Same code — one change to _page_editor_content.html updates everywhere --- app/routers/board.py | 6 +- app/templates/_page_editor_content.html | 513 +++++++++++++++++++++++ app/templates/library.html | 42 +- app/templates/local_workspace.html | 32 +- app/templates/page_editor.html | 514 +----------------------- app/templates/page_editor_embed.html | 406 +++++++++++++++++++ 6 files changed, 936 insertions(+), 577 deletions(-) create mode 100644 app/templates/_page_editor_content.html create mode 100644 app/templates/page_editor_embed.html diff --git a/app/routers/board.py b/app/routers/board.py index 3a887aa..b87ddcc 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 (editor only, no header).""" + embed = request.query_params.get("embed") == "1" from jinja2 import Environment, FileSystemLoader env = Environment(loader=FileSystemLoader("app/templates")) with get_conn() as conn: @@ -1004,7 +1006,7 @@ async def view_page(request: Request, page_id: int): "breadcrumb_items": nav_crumbs, "nav_workspace_id": page.get("workspace_id") or 0, "nav_page_id": page_id} - template = env.get_template("page_editor.html") + template = env.get_template("page_editor_embed.html" if embed else "page_editor.html") response = template.render(**ctx) return HTMLResponse(content=response, headers={"Cache-Control": "no-store, max-age=0"}) diff --git a/app/templates/_page_editor_content.html b/app/templates/_page_editor_content.html new file mode 100644 index 0000000..5291c28 --- /dev/null +++ b/app/templates/_page_editor_content.html @@ -0,0 +1,513 @@ +
+ + + + + + +
+
Edited
+ +
+ + +
+
↗ Export
+
+ {{ fd_icon("copy",14) }} Duplicate +
+
↗ Move to
+
+
+ {{ fd_icon("trash",14) }} Move to Trash +
+
+ + +
+

Move to workspace

+
Loading...
+
+ +
+ No workspaces available +
+
+
+ + +
+
+ {% if page.content_format == 'file' %}{{ fd_icon("paperclip",14) }}{% else %}{{ fd_icon("file",14) }}{% endif %} +
+ {{ page.title }} +
+
+
+ +
+ + +
+ + + + + +
+ + +
+ + +
+ Get started with + + + + + +
+ +
+
+ {{ fd_icon("bar-chart",14) }} Table +
+
+ {{ fd_icon("copy",14) }} Board +
+
+ {{ fd_icon('list',14) }} List +
+
+ {{ fd_icon("calendar",14) }} Timeline +
+
+ {{ fd_icon("calendar",14) }} Calendar +
+
+ {{ fd_icon("image",14) }} Gallery +
+
+
+ {{ fd_icon("download",14) }} Import +
+
+
+
+ +
+ Saving... + Unsaved + Saved + +
+
\ No newline at end of file diff --git a/app/templates/library.html b/app/templates/library.html index d8e6954..8cbe4dd 100644 --- a/app/templates/library.html +++ b/app/templates/library.html @@ -446,13 +446,8 @@ -
-
Loading…
-
-
-
{{ fd_icon("file",32) }}
-
No preview available
-
+
+
@@ -1203,40 +1198,15 @@ 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; + var iframe = document.getElementById('lib-peek-iframe'); + if (iframe) iframe.src = '/pages/' + item.id + '?embed=1'; }, closePeek() { this.peekItem = null; - this.peekContent = ''; + var iframe = document.getElementById('lib-peek-iframe'); + if (iframe) iframe.src = ''; }, async toggleFavoritePeek() { diff --git a/app/templates/local_workspace.html b/app/templates/local_workspace.html index b58770c..6e9f90b 100644 --- a/app/templates/local_workspace.html +++ b/app/templates/local_workspace.html @@ -831,20 +831,16 @@ window._wsInitData = (function() { return null; }, - // ── Preview panel (vanilla JS — matches Library peek-overlay) ── + // ── Preview panel (loads editor in iframe via /pages/{id}?embed=1) ── selectForPreview(node) { var panel = document.getElementById('ws-preview-panel'); if (!panel) return; - // Icon + title document.getElementById('ws-preview-icon').innerHTML = this._fileIcon(node.name, node.is_folder, node.content_format); document.getElementById('ws-preview-title').textContent = node.name; - // Open button → navigate to /pages/{id} var openBtn = document.getElementById('ws-preview-open-btn'); openBtn.onclick = function() { window.location = '/pages/' + node.id; }; - // Favorite button var favBtn = document.getElementById('ws-preview-fav-btn'); favBtn.onclick = function() { window.showToast('Favorite toggled', 'success'); }; - // Copy link button var copyBtn = document.getElementById('ws-preview-copy-btn'); copyBtn.onclick = function() { var url = window.location.origin + '/pages/' + node.id; @@ -854,30 +850,16 @@ 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...
'; - // Show panel with slide-in + // Load editor in iframe + var iframe = document.getElementById('ws-preview-iframe'); + if (iframe) iframe.src = '/pages/' + node.id + '?embed=1'; + // Show panel panel.style.display = 'flex'; requestAnimationFrame(function() { requestAnimationFrame(function() { panel.style.transform = 'translateX(0)'; }); }); - // Load content async - this._loadPreviewContentVanilla(node); }, closePreviewPanel() { @@ -2949,9 +2931,7 @@ console.log('FLOWDECK v2.4.6 — IIFE done, _wsInitData keys:', Object.keys(wind -
-
{{ fd_icon("file",32) }}
Select a file to preview
-
+ diff --git a/app/templates/page_editor.html b/app/templates/page_editor.html index e921535..7768a76 100644 --- a/app/templates/page_editor.html +++ b/app/templates/page_editor.html @@ -5,519 +5,7 @@ page_title %}{{ page.title }}{% endblock %} {% block topbar %} {% set right_actions = 'Edited ▾' %} {% include '_header.html' %} {% endblock %} {% block content %} -
- - - - - - -
-
Edited
- -
- - -
-
↗ Export
-
- {{ fd_icon("copy",14) }} Duplicate -
-
↗ Move to
-
-
- {{ fd_icon("trash",14) }} Move to Trash -
-
- - -
-

Move to workspace

-
Loading...
-
- -
- No workspaces available -
-
-
- - -
-
- {% if page.content_format == 'file' %}{{ fd_icon("paperclip",14) }}{% else %}{{ fd_icon("file",14) }}{% endif %} -
- {{ page.title }} -
-
-
- -
- - -
- - - - - -
- - -
- - -
- Get started with - - - - - -
- -
-
- {{ fd_icon("bar-chart",14) }} Table -
-
- {{ fd_icon("copy",14) }} Board -
-
- {{ fd_icon('list',14) }} List -
-
- {{ fd_icon("calendar",14) }} Timeline -
-
- {{ fd_icon("calendar",14) }} Calendar -
-
- {{ fd_icon("image",14) }} Gallery -
-
-
- {{ fd_icon("download",14) }} Import -
-
-
-
- -
- Saving... - Unsaved - Saved - -
-
+{% include '_page_editor_content.html' %} {% endblock %} {% block scripts %} + +{% endblock %}