- Passerelle WebSocket WS /ws/pages/{page_id} (auth cookie, close 4401/4404), rooms par page en mémoire
- Protocole hello/sync/op/ack/title/sel/ping/peer_join/peer_leave ; version de page + stale => resync
- Merge des ops de blocs insert/update/delete/move, last-write-wins par bloc, ordre d'arrivée
- Client éditeur : diff local -> ops (debounce), application distante discret (LWW sur bloc focalisé), re-focus du bloc actif
- Présence (avatars topbar) + curseurs live (calque dédié, positions à l'édition/au scroll)
- Titre synchronisé (debounce) sans écraser le titre en cours d'édition
- Fallback polling 10 s si WS indisponible (adopté seulement sans brouillon local) + reconnexion auto
- Persistance debounce ~1 s (content/title) + flush à la déconnexion du dernier client
- CSP connect-src étendu à ws: ; peers sans données sensibles (id/login/full_name/couleur)
- 12 tests tests/test_realtime.py (auth, page absente, hello->sync, LWW 2 clients + persistance, présence, curseurs, titre, stale resync, apply_op/merge_ops) ; suite 298 verte (3 PDF pré-existants)
- Bump v5.3.0 (VERSION, main.py, CHANGELOG) ; ROADMAP v5.13.0 livré
15 lines
1.7 KiB
HTML
15 lines
1.7 KiB
HTML
{% extends "base.html" %} {% block page_icon %}{% if page.content_format == 'file' %}{{ fd_icon("paperclip",14) }}{% else %}{{ fd_icon("file",14) }}{% endif %}{% endblock %} {% block
|
|
page_title %}{{ page.title }}{% endblock %} {% block topbar %}
|
|
{% set page_icon = "file" if page.content_format != 'file' else "paperclip" %}
|
|
{% set page_title = page.title %}
|
|
{% set right_actions = '<span class="topbar-edited" style="cursor:pointer;" @click="window.E && window.E.toggleActivityOpen()">Edited <span x-text="window.E && window.E.timeAgo || \'\'"></span> ▾</span><button class="topbar-btn" @click="window.E && window.E.toggleComments()" title="Comments"><span class="fd-comment-btn-ico">💬</span><span class="fd-comment-count" x-text="window.E && window.E.commentCount>0 ? window.E.commentCount : \'\'"></span></button><button class="topbar-btn share-btn" @click="window.E && window.E.toggleShareOpen()">' ~ fd_icon("lock",14) ~ ' Share ▾</button><button class="topbar-btn" @click="window.E && window.E.copyPageLink()" title="Copy link">' ~ fd_icon("link",14) ~ '</button><button class="topbar-btn star-btn" @click="window.E && window.E.toggleFavorite()" x-html="(window.E && window.E.favorited) ? getSvgIcon(\'star\',14) : getSvgIcon(\'star\',14)"></button><button class="topbar-btn relative" @click="window.E && window.E.toggleMoreOpen()">⋯</button>' %}
|
|
{% include '_header.html' %}
|
|
{% endblock %} {% block content %}
|
|
{% include "_page_editor_content.html" %}
|
|
{% endblock %} {% block scripts %}
|
|
<script src="/static/js/katex.min.js?v=0.16.11" defer></script>
|
|
{% include "_page_editor_scripts.html" %}
|
|
{% include "_page_editor_realtime.html" %}
|
|
{% include "_database_table_scripts.html" %}
|
|
{% endblock %}
|