fix: eliminate x-data quoting bug by moving page data to JSON script tag
FlowDeck CI / test (push) Failing after 4s
FlowDeck CI / docker (push) Has been skipped

Root cause: x-data HTML attribute with tojson creates quote conflicts
that survive even Cache-Control: no-store (likely nginx proxy cache).

Solution: page data is now in a <script type=application/json> tag,
completely decoupled from Alpine x-data HTML attribute. Zero quoting
issues regardless of page content.

- x-data="editorState()" + x-init="loadPage()" replaces editor(...)
- Server passes page_data dict, template renders as JSON script tag
- Same approach used for page_share_mode and page_published
- All 73 tests pass
This commit is contained in:
2026-07-10 14:11:01 -04:00
parent 79b0bdd22d
commit be98f9d504
2 changed files with 8 additions and 6 deletions
+2 -1
View File
@@ -752,7 +752,8 @@ async def view_page(request: Request, page_id: int):
).fetchone()
ctx = {**sidebar, "page": page, "page_favorited": fav is not None,
"page_share_mode": page.get("share_mode", "private"),
"page_published": bool(page.get("published", 0))}
"page_published": bool(page.get("published", 0)),
"page_data": {"id": page_id, "title": page.get("title"), "content_format": page.get("content_format", "blocks"), "content": page.get("content", ""), "favorited": fav is not None, "share_mode": page.get("share_mode", "private"), "published": bool(page.get("published", 0))}}
template = env.get_template("page_editor.html")
response = template.render(**ctx)
return HTMLResponse(content=response, headers={"Cache-Control": "no-store, max-age=0"})
+6 -5
View File
@@ -7,7 +7,7 @@
{% endblock %}
{% block content %}
<div class="page-editor-wrapper" x-data='editor({{ page.id }}, {{ page.title | tojson }}, {{ page.content_format | tojson }}, {{ page.content | tojson }}, {{ page_favorited | tojson }})' x-init="init()">
<div class="page-editor-wrapper" x-data="editorState()" x-init="loadPage()">
<!-- ═══════════ Top Action Bar ═══════════ -->
<div class="page-topbar">
@@ -261,6 +261,7 @@
{% endblock %}
{% block scripts %}
<script type="application/json" id="page-data">{{ page_data | tojson }}</script>
<script>
const CMDS=[{name:'BASIC',items:[
{id:'paragraph',name:'Text',icon:'¶'},{id:'heading_1',name:'Heading 1',icon:'H1'},{id:'heading_2',name:'Heading 2',icon:'H2'},
@@ -329,12 +330,12 @@ function renderBlock(b,idx){
}
window.E=null;
function editor(pid,title,fmt,raw,favorited){
const self={
pid,pageTitle:title,blocks:[],dirty:false,saving:false,lastSaved:'',st:null,
function editorState(){
return {
pid:null,pageTitle:'',blocks:[],dirty:false,saving:false,lastSaved:'',st:null,
fmt:{open:false,top:0,left:0,idx:-1},
shareOpen:false,moreOpen:false,gsMoreOpen:false,accessMenuOpen:false,
shareTab:'share',favorited:!!favorited,
shareTab:'share',favorited:false,
pagePublished:{{ page_published | tojson }},
generalAccess:'{{ page_share_mode }}',publicPerm:'viewer',
pubAllowEdit:false,pubAllowComments:true,