fix: A27 phase 2a — éditeur 2 516 L extrait via page-data JSON (v7.22.0)
- `_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:
@@ -1,5 +1,39 @@
|
||||
# Changelog - FlowDeck
|
||||
|
||||
## v7.22.0 (2026-10-01) — Audit : A27 phase 2a (l'éditeur, 2 516 L)
|
||||
|
||||
### Changed
|
||||
|
||||
- **A27 (phase 2a)** — `_page_editor_scripts.html` : les **2 516 lignes** du
|
||||
gros bloc interpolé partent vers `static/js/page_editor_scripts.js`
|
||||
- Recette « config JSON » : les **8 interpolations Jinja** lisent maintenant
|
||||
`PD = JSON.parse(document.getElementById('page-data'))` — le bloc JSON
|
||||
`#page-data` **existait déjà** juste avant le script, même ordre
|
||||
d'exécution, garde `__fdEditorScriptsLoaded` préservée
|
||||
- Côté route (`view_page_root`) : `page_data` enrichi de `updated_at`,
|
||||
`created_at`, `user_id`, `is_shared` (le dérivé est **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 contexte → parité stricte)
|
||||
- `node --check` vert ; template : 2 tags restants (JSON config + src)
|
||||
|
||||
### Fixed
|
||||
|
||||
- **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 (lightbox, previews, endpoints)
|
||||
→ lisent `static/js/page_editor_scripts.js` ; `test_page_editor_renders_
|
||||
page_is_shared` → **parsing du JSON `#page-data`** (`is_shared is True`,
|
||||
la valeur sert toujours à la page)
|
||||
|
||||
### Notes
|
||||
|
||||
- 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 (if/else), settings 1 093, realtime 531, board 146
|
||||
≈ 6 653 L + 120 warnings eslint à nettoyer
|
||||
- Suite complète : **1089/1089**
|
||||
|
||||
## v7.21.0 (2026-10-01) — Audit : A27 phase 1 (4 243 L de JS extraites)
|
||||
|
||||
### Changed
|
||||
|
||||
+2
-2
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,6 +1,6 @@
|
||||
# WORKLOAD — FlowDeck Notion Clone
|
||||
|
||||
> **Début**: 2026-07-08 | **Version**: v7.21.0 (audit — A27 partiel : 4 243 L extraites, 0 erreur eslint) | **Statut**: EN COURS 🔄
|
||||
> **Début**: 2026-07-08 | **Version**: v7.22.0 (audit — A27 phase 2 : éditeur 2 516 L via page-data JSON) | **Statut**: EN COURS 🔄
|
||||
> **Cible**: parité Notion + intégration forge · **Follow-ups v7.3 livrés**: sidebar teamspaces, notif `page.updated`, charts `number` + dashboards multi-DB, unfurl forge, UI Settings → Audit — voir `ROADMAP.md § v7.3.0`
|
||||
|
||||
## Avancement Global
|
||||
|
||||
+1
-1
@@ -185,7 +185,7 @@ async def lifespan(_app: FastAPI):
|
||||
|
||||
app = FastAPI(
|
||||
title="FlowDeck",
|
||||
version="7.21.0",
|
||||
version="7.22.0",
|
||||
docs_url="/docs",
|
||||
redoc_url="/redoc",
|
||||
lifespan=lifespan,
|
||||
|
||||
@@ -413,17 +413,36 @@ def view_page_root(request: Request, page_id: int):
|
||||
|
||||
with get_conn() as conn:
|
||||
nav_crumbs = _nav_breadcrumb(conn, page_id)
|
||||
# dérivé calculé UNE fois : ctx ET page_data (JSON) l'utilisent (A27)
|
||||
page_is_shared = (
|
||||
bool(page.get("is_shared", 0))
|
||||
or page.get("share_mode", "private") != "private"
|
||||
or bool(page.get("published", 0))
|
||||
)
|
||||
ctx = {**sidebar, "page": page, "sub_pages": [dict(s) for s in subs],
|
||||
"page_favorited": fav is not None,
|
||||
"page_share_mode": page.get("share_mode", "private"),
|
||||
"page_published": bool(page.get("published", 0)),
|
||||
"page_is_shared": bool(page.get("is_shared", 0)) or page.get("share_mode", "private") != "private" or bool(page.get("published", 0)),
|
||||
"page_is_shared": page_is_shared,
|
||||
"page_data": page_data,
|
||||
"collection_data": collection_data,
|
||||
"breadcrumb_items": nav_crumbs,
|
||||
"nav_workspace_id": page.get("workspace_id") or 0,
|
||||
"nav_page_id": page_id,
|
||||
"embed_mode": embed}
|
||||
# A27 phase 2 : le JS de l'éditeur lit ces valeurs dans page-data (JSON)
|
||||
# au lieu des interpolations Jinja — une seule source, même calculs que le
|
||||
# ctx ci-dessus.
|
||||
from app.templating import ENV as _ENV27
|
||||
page_data.update(
|
||||
updated_at=page.get("updated_at", ""),
|
||||
created_at=page.get("created_at", ""),
|
||||
user_id=_uid or 0,
|
||||
is_shared=page_is_shared,
|
||||
clip_icon=_ENV27.from_string(
|
||||
"{% from '_icons.html' import fd_icon %}{{ fd_icon('paperclip', 14) }}"
|
||||
).render(),
|
||||
)
|
||||
# Select template: collection pages use database table view
|
||||
if page.get("content_format") == "collection" and not embed:
|
||||
template = env.get_template("page_editor_collection.html")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"title": "FlowDeck",
|
||||
"version": "7.21.0"
|
||||
"version": "7.22.0"
|
||||
},
|
||||
"paths": {
|
||||
"/auth/register": {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user