fix: A27 phase 2a — éditeur 2 516 L extrait via page-data JSON (v7.22.0)
FlowDeck CI / lint (push) Canceled after 0s
FlowDeck CI / test (push) Canceled after 0s
FlowDeck CI / docker (push) Canceled after 0s

- `_page_editor_scripts.html` : le gros bloc interpolé (2 516 L) part vers
  `static/js/page_editor_scripts.js` — recette « config JSON » : les 8
  interpolations Jinja lisent `PD = JSON.parse(#page-data)`, bloc JSON qui
  EXISTAIT DÉJÀ juste avant le script (même ordre d'exécution), garde
  `__fdEditorScriptsLoaded` préservée, node --check vert.
- Route `view_page_root` : page_data enrichi de updated_at, created_at,
  user_id, is_shared (dérivé HOISTÉ : une seule expression sert le ctx ET le
  JSON) et clip_icon (macro fd_icon rendue côté serveur). workspace_key reste
  vide comme avant (jamais défini dans ce ctx → parité stricte).

8 tests adaptés à l'extraction (ils lisaient le template SOURCE) :
- test_ai_writing ×2 (+ helper _read_js), test_pwa_offline,
  test_v511 front_end_wired, test_v55 ×3 → lisent le JS extrait
- test_page_editor_renders_page_is_shared → parsing du JSON #page-data
  (`is_shared is True`) — la valeur sert toujours à la page

Cumul A27 : 6 759 L extraites (13 904 → 7 145 inline). Reste : local_workspace
2 031, base 1 523 (structurel {% for %}/{% block %}), database_table 1 323,
settings 1 093, realtime 531, board 146 ≈ 6 653 L + 120 warnings eslint.

suite **1089/1089** · ruff OK · docs à jour
This commit is contained in:
2026-10-01 16:12:26 -04:00
parent 587ec8d61b
commit ee1d46e965
14 changed files with 2599 additions and 2531 deletions
+9 -2
View File
@@ -270,8 +270,15 @@ def _read_template(name: str) -> str:
return fh.read()
def _read_js(name: str) -> str:
"""A27 : le JS de l'éditeur vit dans static/js depuis l'extraction."""
base = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
with open(os.path.join(base, "static", "js", name), encoding="utf-8") as fh:
return fh.read()
def test_editor_has_ai_slash_commands(client):
src = _read_template("_page_editor_scripts.html")
src = _read_js("page_editor_scripts.js")
for cmd in ("ai_write", "ai_summarize", "ai_translate", "ai_continue"):
assert cmd in src
assert "AI writing" in src
@@ -280,7 +287,7 @@ def test_editor_has_ai_slash_commands(client):
def test_editor_has_autocomplete(client):
src = _read_template("_page_editor_scripts.html")
src = _read_js("page_editor_scripts.js")
assert "AIAC" in src
assert "autocomplete" in src
assert "AIAC.schedule" in src
+1 -1
View File
@@ -98,7 +98,7 @@ def test_workspace_offline_banner_shows_pending(client):
def test_editor_save_has_offline_hook():
src = (ROOT / "app" / "templates" / "_page_editor_scripts.html").read_text(encoding="utf-8")
src = (ROOT / "static" / "js" / "page_editor_scripts.js").read_text(encoding="utf-8")
assert "FlowOffline" in src
assert "savePageOffline" in src
assert "navigator.onLine" in src
+8 -1
View File
@@ -295,7 +295,14 @@ def test_page_editor_renders_page_is_shared(client):
r = client.get(f"/pages/{a}")
assert r.status_code == 200
assert "pageIsShared:true" in r.text
# A27 : la valeur n'est plus interpolée dans le JS inline mais exposée
# dans le JSON #page-data (le JS lit PD.is_shared)
import json as _json
import re as _re
m = _re.search(r'id="page-data"[^>]*>(.*?)</script>', r.text, _re.S)
assert m, "bloc #page-data absent de la page"
assert _json.loads(m.group(1))["is_shared"] is True
def test_tree_is_shared_includes_link_shared_pages(client):
+1 -1
View File
@@ -391,7 +391,7 @@ def test_block_templates_service_shapes():
def test_editor_front_end_wired(client):
import pathlib
src = pathlib.Path("app/templates/_page_editor_scripts.html").read_text(encoding="utf-8")
src = pathlib.Path("static/js/page_editor_scripts.js").read_text(encoding="utf-8")
base = pathlib.Path("app/templates/base.html").read_text(encoding="utf-8")
assert "gtTok" in src and "fd-wiki-chip" in src # chip render + reader
assert "wiki/pages" in src and "wiki/titles" in src # picker + label resolution
+3 -3
View File
@@ -316,7 +316,7 @@ class TestBookmarkPersistence:
class TestLightbox:
def test_editor_lightbox_supports_keyboard_navigation(self):
tpl = Path("app/templates/_page_editor_scripts.html").read_text(encoding="utf-8")
tpl = Path("static/js/page_editor_scripts.js").read_text(encoding="utf-8")
assert "_openLightbox" in tpl
assert "fd-lightbox-nav" in tpl
assert "ArrowRight" in tpl and "ArrowLeft" in tpl
@@ -343,7 +343,7 @@ class TestLightbox:
class TestInlinePreviews:
def test_editor_renders_video_audio_pdf(self):
tpl = Path("app/templates/_page_editor_scripts.html").read_text(encoding="utf-8")
tpl = Path("static/js/page_editor_scripts.js").read_text(encoding="utf-8")
assert "<video controls" in tpl
assert "<audio controls" in tpl
assert "embed_type==='pdf'" in tpl
@@ -542,7 +542,7 @@ class TestCoverIcon:
class TestEditorEndpointPaths:
def test_editor_uses_board_prefixed_endpoints(self):
tpl = Path("app/templates/_page_editor_scripts.html").read_text(encoding="utf-8")
tpl = Path("static/js/page_editor_scripts.js").read_text(encoding="utf-8")
for endpoint in (
"/board/api/pages/'+this.pid+'/cover",
"/board/api/pages/'+this.pid+'/icon",