diff --git a/backend/main.py b/backend/main.py
index fb3fcc4..71422c2 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -735,6 +735,22 @@ FRONTEND_DIR = Path(__file__).resolve().parent.parent / "frontend"
# Path safety helper
# ---------------------------------------------------------------------------
+def _content_disposition(disposition: str, filename: str) -> str:
+ """Build a header-safe Content-Disposition value.
+
+ HTTP header values must be ASCII. Unicode filenames are sent per
+ RFC 5987 via ``filename*`` (percent-encoded UTF-8) with a pure-ASCII
+ ``filename`` fallback. This avoids a UnicodeDecodeError / HTTP 500 when
+ the filename contains accented characters (e.g. 'Bière blonde…pdf').
+ """
+ from urllib.parse import quote
+ ascii_name = "".join(c for c in filename if c.isascii() and (c.isalnum() or c in " _-.")).strip() or "file"
+ ext = Path(filename).suffix
+ if ext and not Path(ascii_name).suffix:
+ ascii_name = ascii_name + ext
+ return f"{disposition}; filename=\"{ascii_name}\"; filename*=UTF-8''{quote(filename)}"
+
+
def _resolve_safe_path(vault_root: Path, relative_path: str) -> Path:
"""Resolve a relative path safely within the vault root.
@@ -2801,13 +2817,13 @@ async def api_pdf_stream(
"Content-Range": f"bytes {start}-{end}/{file_size}",
"Accept-Ranges": "bytes",
"Content-Length": str(chunk_size),
- "Content-Disposition": f'inline; filename="{file_path.name}"',
+ "Content-Disposition": _content_disposition("inline", file_path.name),
},
)
return FileResponse(str(file_path), media_type="application/pdf", headers={
"Accept-Ranges": "bytes",
- "Content-Disposition": f'inline; filename="{file_path.name}"'})
+ "Content-Disposition": _content_disposition("inline", file_path.name)})
@app.get("/api/file/{vault_name}/pdf/info")
diff --git a/docs/ISSUES_TODOLIST.md b/docs/ISSUES_TODOLIST.md
index 10d92b2..14d7643 100644
--- a/docs/ISSUES_TODOLIST.md
+++ b/docs/ISSUES_TODOLIST.md
@@ -107,9 +107,8 @@ Avant de corriger quoi que ce soit, un agent IA doit :
| # | Titre | Statut | Priorité | Scope | Assigné | Zone (fichier) | Cmd de repro | Correctif / Commit | Notes |
|---|---|---|---|---|---|---|---|---|---|
-| *BUG-001* | L'ouverture des fichier PDF ne fonctionne pas et donne l'erreur Internal Server Error | 🔴 ouvert | P1 | fichier PDF | IA | |
-https://og.dracodev.net/api/file/Recettes/pdf/stream?path=98_Boite_Outils%2F98.2_Attachments%2FBi%C3%A8re%20blonde%20envoy%C3%A9%20par%20Desja.pdf| — | l'erreur se retrouve dans la section network de la console web et dans l'interface web lors du chargement d'un pdf |
-| *BUG-002* | l'ouverture d'un fichier .excalidraw ne fonctionne pas et affiche toujours Loading *Excalidraw…* | 🔴 ouvert | P1 | fichier .excalidaw | IA | | | — | test d'accès réaliser via cloudflare et réseau local démontre que ce problème est au 2 endroits |
+| *BUG-001* | L'ouverture des fichier PDF ne fonctionne pas et donne l'erreur Internal Server Error | 🟢 corrigé | P1 | fichier PDF | IA | `backend/main.py` | `GET /api/file/{vault}/pdf/stream?path=…(pdf à nom accentué)` | `backend/main.py` : Content-Disposition encodé RFC 5987 (helper `_content_disposition`) | 500 car nom Unicode brut dans l'en-tête → header invalide. Vérifié: stream 200 / Range 206 + test `tests/test_pdf_stream.py` |
+| *BUG-002* | l'ouverture d'un fichier .excalidraw ne fonctionne pas et affiche toujours Loading *Excalidraw…* | 🟢 corrigé | P1 | fichier .excalidraw | IA | `frontend/excalidraw-editor.html` | Ouvrir un fichier `.excalidraw` | `frontend/excalidraw-editor.html` : alias esm.sh supprimé (408 jotai) + React 19 cohérent + prop `excalidrawAPI` | 2 causes: 408 esm.sh sur `?alias` + prop legacy `excalidrawRef` inopérante en 0.18. Vérifié navigateur: Loading masqué + cycle save OK |
| | | | | | | | | | |
### TODOs techniques (améliorations / nouvelles tâches)
@@ -130,6 +129,7 @@ https://og.dracodev.net/api/file/Recettes/pdf/stream?path=98_Boite_Outils%2F98.2
| Date | ID(s) traité(s) | Action | Fichiers modifiés | Résumé | Statut après |
|---|---|---|---|---|---|
| *(exemple)* 2026-06-15 | BUG-001 | Correction | `frontend/app.js` | Réécriture de `renderFile()` pour préserver le DOM dashboard | 🟢 corrigé (en attente vérif) |
+| 2026-09-09 | BUG-001, BUG-002 | Correction | `backend/main.py`, `frontend/excalidraw-editor.html`, `tests/test_pdf_stream.py` | BUG-001: Content-Disposition RFC 5987 (nom PDF accentué ne casse plus l'en-tête → plus de 500). BUG-002: suppression alias esm.sh (408 jotai) + React 19 cohérent + prop `excalidrawAPI` → Loading masqué, save OK. Vérifié: 534 tests backend verts + E2E navigateur. | 🟢 corrigé (en attente vérif utilisateur) |
---
diff --git a/frontend/excalidraw-editor.html b/frontend/excalidraw-editor.html
index 57eeaef..a9faddd 100644
--- a/frontend/excalidraw-editor.html
+++ b/frontend/excalidraw-editor.html
@@ -92,26 +92,20 @@
-
-
-
-
+