feat: v5.14.0 Synced blocks - block created once, edited everywhere
FlowDeck CI / lint (push) Failing after 57s
FlowDeck CI / test (push) Successful in 6m34s
FlowDeck CI / docker (push) Successful in 59s

- Table synced_blocks (source of truth) + page_synced_blocks (references)
- Migration 15 + service app/services/synced_blocks.py
- API endpoints: CRUD synced blocks, add/remove page references
- Editor: /synced slash command, synced block rendering with badge
- Realtime: _propagate_synced broadcasts updates to all referencing rooms
- Export: synced blocks resolved in Markdown/HTML/PDF
- 18 tests in tests/test_v514_synced_blocks.py

Co-authored-by: FlowDeck <[email protected]>
This commit is contained in:
2026-09-17 20:08:09 -04:00
co-authored by FlowDeck
parent f9da57c9e0
commit bf3d1ac0cc
9 changed files with 744 additions and 15 deletions
+28
View File
@@ -529,6 +529,20 @@ def blocks_to_markdown(blocks: list) -> str:
out.append(f"[{url}]({url})" if url else "[embed]")
elif t == "table":
out.append(_table_to_markdown(b))
elif t == "synced":
synced_id = b.get("synced_id")
if synced_id:
try:
from app.services.synced_blocks import get_synced_block
sb = get_synced_block(synced_id)
if sb and sb.get("content"):
resolved = json.loads(sb["content"])
if isinstance(resolved, list):
out.append(blocks_to_markdown(resolved))
else:
out.append(str(resolved))
except Exception:
out.append(f"[Synced block {synced_id}]")
else:
out.append(c)
return "\n\n".join(filter(None, out))
@@ -678,6 +692,20 @@ def blocks_to_html(blocks: list) -> str:
)
elif t == "table":
parts.append(_table_to_html(b))
elif t == "synced":
synced_id = b.get("synced_id")
if synced_id:
try:
from app.services.synced_blocks import get_synced_block
sb = get_synced_block(synced_id)
if sb and sb.get("content"):
resolved = json.loads(sb["content"])
if isinstance(resolved, list):
parts.append(blocks_to_html(resolved))
else:
parts.append(f"<p>{_text(resolved)}</p>")
except Exception:
parts.append(f"<p>[Synced block {synced_id}]</p>")
else:
parts.append(f"<p>{c}</p>")
return "\n".join(parts)