Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8054665bc | ||
|
|
99a5b735c8 |
+17
-1
@@ -6,7 +6,7 @@ Format basé sur [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/),
|
||||
et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
> **En cours de développement** : les changements à venir sont listés dans la section
|
||||
> [Unreleased](#unreleased). La dernière version livrée est **2.13.0**.
|
||||
> [Unreleased](#unreleased). La dernière version livrée est **2.13.1**.
|
||||
|
||||
---
|
||||
|
||||
@@ -14,6 +14,10 @@ et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
---
|
||||
|
||||
## [2.13.1] — 2026-09-18
|
||||
|
||||
---
|
||||
|
||||
## [2.13.0] — 2026-09-18
|
||||
|
||||
### Ajouté
|
||||
@@ -41,6 +45,18 @@ et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
Source de vérité du nouveau contenu : `scripts/guide_content.py` (locales générées,
|
||||
jamais éditées à la main). Tests : `tests/test_guide.py` (11).
|
||||
|
||||
### Modifié (ajustements retour utilisateur sur #105)
|
||||
|
||||
- Boutons de téléchargement du guide : **icônes seules** (plus de libellé « Markdown »/
|
||||
« PDF »), tooltip i18n explicite sur chaque bouton.
|
||||
- PDF du guide : le bloc Mermaid de la section Architecture est converti en **diagramme
|
||||
rendu** (PNG pré-rendu commité via `scripts/build_guide_diagrams.py` +
|
||||
`scripts/render_guide_diagram.mjs`, inséré par `diagram_png_for()`), au lieu du code
|
||||
brut ; le Markdown garde le fenced ` ```mermaid ` (copiable).
|
||||
- PDF du guide : emoji rendus **en couleur** au lieu de rectangles — `fonts-noto-color-emoji`
|
||||
ajouté à l'image Docker et `"Noto Color Emoji"` en fin de pile de polices du PDF
|
||||
(la TOC était correcte car elle utilise DejaVu, qui n'a pas les emoji pleine chasse).
|
||||
|
||||
### Corrigé
|
||||
|
||||
- **BUG-067 - Guide : entrée « 📱 Mobile » morte** : le sommaire pointait vers
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ COPY --from=builder /install /usr/local
|
||||
|
||||
# WeasyPrint runtime dependencies
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends libpango-1.0-0 libpangocairo-1.0-0 shared-mime-info \
|
||||
&& apt-get install -y --no-install-recommends libpango-1.0-0 libpangocairo-1.0-0 shared-mime-info fonts-noto-color-emoji \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
+3
-3
@@ -4,7 +4,7 @@
|
||||
|
||||
**Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.
|
||||
|
||||
[]()
|
||||
[]()
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://www.docker.com/)
|
||||
[](https://www.python.org/)
|
||||
@@ -927,8 +927,8 @@ Ce projet est sous licence **MIT** — voir le fichier [LICENSE](LICENSE) pour l
|
||||
|
||||
## 📝 Changelog
|
||||
|
||||
Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.13.0).
|
||||
Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.13.1).
|
||||
|
||||
---
|
||||
|
||||
*Projet : ObsiGate | Version : 2.13.0 | Dernière mise à jour : Juin 2026*
|
||||
*Projet : ObsiGate | Version : 2.13.1 | Dernière mise à jour : Juin 2026*
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
**Ultra-light web gateway for your Obsidian vaults** — Access, browse, and search all your Obsidian notes from any device via a modern, responsive web interface.
|
||||
|
||||
[]()
|
||||
[]()
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://www.docker.com/)
|
||||
[](https://www.python.org/)
|
||||
@@ -1096,8 +1096,8 @@ This project is licensed under the **MIT License** - see the [LICENSE](LICENSE)
|
||||
|
||||
## 📝 Changelog
|
||||
|
||||
See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.13.0).
|
||||
See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.13.1).
|
||||
|
||||
---
|
||||
|
||||
*Project: ObsiGate | Version: 2.13.0 | Last updated: May 2026*
|
||||
*Project: ObsiGate | Version: 2.13.1 | Last updated: May 2026*
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 186 KiB |
+21
-2
@@ -13,6 +13,7 @@ quand les bibliothèques natives GTK manquent (Windows).
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
import hashlib
|
||||
import html
|
||||
import json
|
||||
import logging
|
||||
@@ -26,6 +27,17 @@ ROOT = Path(__file__).resolve().parent.parent
|
||||
INDEX_HTML = ROOT / "frontend" / "index.html"
|
||||
LOCALES_DIR = ROOT / "frontend" / "locales"
|
||||
VERSION_FILE = ROOT / "VERSION"
|
||||
DIAGRAMS_DIR = ROOT / "backend" / "assets" / "guide_diagrams"
|
||||
|
||||
|
||||
def diagram_png_for(code: str) -> Path | None:
|
||||
"""Chemin du PNG pré-rendu (scripts/build_guide_diagrams.py) pour un code
|
||||
Mermaid, ou None. Le hash doit rester synchrone avec le script de build :
|
||||
sha1(unescape(code).strip())[:16]."""
|
||||
normalized = html.unescape(code).strip()
|
||||
sha = hashlib.sha1(normalized.encode("utf-8")).hexdigest()[:16]
|
||||
png = DIAGRAMS_DIR / (sha + ".png")
|
||||
return png if png.exists() else None
|
||||
|
||||
# Éléments décoratifs exclus des exports
|
||||
_SKIP_CLASSES = {"help-hero-visual", "editor-modal", "help-nav"}
|
||||
@@ -388,8 +400,15 @@ def _html_block(node: Node | str, out: list[str], loc: dict[str, str]) -> None:
|
||||
tag = node.tag
|
||||
|
||||
if tag == "pre":
|
||||
raw = html.escape(_pre_text(node), quote=False)
|
||||
out.append(f"<pre><code>{raw}</code></pre>")
|
||||
raw = _pre_text(node)
|
||||
classes = _pre_classes(node)
|
||||
if "language-mermaid" in classes:
|
||||
png = diagram_png_for(raw)
|
||||
if png is not None:
|
||||
url = "file:///" + str(png).replace("\\", "/").lstrip("/")
|
||||
out.append(f'<img src="{url}" style="max-width: 100%" />')
|
||||
return
|
||||
out.append(f"<pre><code>{html.escape(raw, quote=False)}</code></pre>")
|
||||
return
|
||||
|
||||
kids = _resolve_i18n(node, loc)
|
||||
|
||||
@@ -43,7 +43,7 @@ def build_pdf_html(body_html: str, title: str, theme: str = "light") -> str:
|
||||
<head><meta charset="utf-8"><title>{title}</title>
|
||||
<style>
|
||||
body {{
|
||||
font-family: Georgia, "Times New Roman", serif;
|
||||
font-family: Georgia, "Times New Roman", serif, "Noto Color Emoji";
|
||||
max-width: 720px;
|
||||
margin: 40px auto;
|
||||
padding: 0 20px;
|
||||
|
||||
Generated
+1
-1
@@ -2626,7 +2626,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "obsigate-desktop"
|
||||
version = "2.13.0"
|
||||
version = "2.13.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"env_logger",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "obsigate-desktop"
|
||||
version = "2.13.0"
|
||||
version = "2.13.1"
|
||||
description = "ObsiGate Desktop — Porte d'entrée native pour vos vaults Obsidian"
|
||||
authors = ["Bruno Charest"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/nicedoc/obsigate/main/desktop/tauri.conf.schema.json",
|
||||
"productName": "ObsiGate",
|
||||
"version": "2.13.0",
|
||||
"version": "2.13.1",
|
||||
"identifier": "com.obsigate.desktop",
|
||||
"build": {
|
||||
"frontendDist": "../frontend",
|
||||
|
||||
@@ -248,6 +248,7 @@ Avant de corriger quoi que ce soit, un agent IA doit :
|
||||
| 2026-09-17 | #78 (complément) | UI | `frontend/excalidraw-editor.html`, `docs/features/excalidraw.md`, `CHANGELOG.md` | **#78 (complément)** : la barre d'outils de l'éditeur Excalidraw passe en **colonne d'icônes** (34×34 px, SVG seuls), **collée au bord droit** (`right: 0` ; `top: 45%` ; empilement vertical), avec `title`/`aria-label`. L'icône du bouton Save est remplacée par une coche pendant 1,2 s après une sauvegarde réussie. Badge « Modifié » réduit à une pastille. Vérifié Playwright : bord droit au bord de l'iframe, haut 45 %, 4 boutons empilés ; bascule plein écran OK, cycle d'icône Save + `PUT save` observés. | 🟢 livré (en attente vérif utilisateur) |
|
||||
| 2026-09-18 | BUG-066 | Correction | `frontend/locales/fr.json`, `frontend/locales/en.json`, `tests/frontend/unit.test.mjs`, `CHANGELOG.md`, `docs/ISSUES_TODOLIST.md` | **BUG-066** : la table des matières de la page de configuration n'affichait aucune icône pour « Fichiers cachés » et « Partages publics ». Les libellés du sommaire proviennent de clés i18n (`config.section_hidden`, `config.section_shares`) distinctes des titres de section qui, eux, portaient déjà l'icône. Alignement : 🗂️ / 📤 en FR **et** EN. Test de non-régression : `unit.test.mjs` vérifie que **toutes** les entrées `.help-nav-link` du sommaire portent une icône dans les deux langues (17/17). Vérifié : `unit.test.mjs` 10/10, `validate-imports` 38 modules. | 🟢 corrigé (en attente vérif utilisateur) |
|
||||
| 2026-09-18 | #105, BUG-067 | Documentation + correction | `frontend/index.html`, `frontend/js/config.js`, `frontend/style.css`, `frontend/locales/{fr,en}.json`, `backend/guide_export.py`, `backend/main.py`, `tests/test_guide.py`, `docs/features/guide-coverage-105.md`, `CHANGELOG.md`, `docs/ROADMAP.md`, `docs/ISSUES_TODOLIST.md` | **#105** : audit complet de couverture du Guide d'utilisation — 8 nouvelles sections (Architecture + diagramme Mermaid, API & intégrations, Diagrammes Mermaid & Excalidraw, Hors-ligne & synchronisation, Collaboration temps réel, Application desktop, Bibliothèque & signets, Multilingue) et compléments (recherche sémantique, MFA/WebAuthn, notifications push, exports HTML/ePub/ZIP, PDF, vue multi-panneaux, admin). Téléchargement du guide en Markdown et PDF (`GET /api/guide/download?format=md|pdf`, FR/EN, rendu par le moteur d'export existant). Guide plus large en desktop. **BUG-067** : ancre morte `#help-mobile-editor` → section dédiée créée. | 🟢 corrigé (en attente vérif utilisateur)
|
||||
| 2026-09-18 | #105 (ajustements) | Amélioration | `frontend/index.html`, `frontend/js/config.js`, `frontend/sw.js`, `frontend/locales/{fr,en}.json`, `backend/guide_export.py`, `backend/pdf_export.py`, `Dockerfile`, `scripts/build_guide_diagrams.py`, `scripts/render_guide_diagram.mjs`, `scripts/guide_content.py`, `backend/assets/guide_diagrams/df7366a40db6a5a2.png`, `tests/test_guide.py`, `docs/features/guide-coverage-105.md`, `CHANGELOG.md` | **#105 (retour utilisateur)** : 1) boutons de téléchargement du guide passés en icônes seules (tooltips i18n conservés) ; 2) le diagramme Mermaid de la section Architecture est désormais rendu en **vraie image** dans le PDF (pipeline de pré-rendu PNG Chromium+mermaid v11, PNG commité sous `backend/assets/guide_diagrams/<sha1>.png`, résolu par `diagram_png_for()` ; le Markdown garde le fenced mermaid) ; 3) emoji du PDF rendus **en couleur** au lieu de rectangles : `fonts-noto-color-emoji` ajouté au Dockerfile + `"Noto Color Emoji"` en fin de pile de polices PDF. Vérifié : pytest 1218 (test_guide ×13), ruff/mypy 0, validate-imports 38, unit 10/10 ; PDF live conteneur 2020 : 24 pages, 0 glyphes tofu, diagramme 3568x1174 embarqué. | 🟢 livré
|
||||
|
||||
---
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# ObsiGate — Roadmap
|
||||
|
||||
> **Version :** 2.13.0 | **Dernière mise à jour :** 2026-09-18
|
||||
> **Version :** 2.13.1 | **Dernière mise à jour :** 2026-09-18
|
||||
> **Ce fichier ne contient que le travail à venir** (🔵 En cours + ⚪ Backlog) et un index compact
|
||||
> vers les fonctionnalités livrées.
|
||||
> - **Méthode de livraison à appliquer pour toute tâche : [DELIVERY_WORKFLOW.md](./DELIVERY_WORKFLOW.md)**
|
||||
|
||||
@@ -47,7 +47,8 @@ le viewer ne rend que les vues document, la modale est donc enrichie ici. Si le
|
||||
CDN Mermaid n'est pas prêt, le bloc reste du code lisible et le rendu est
|
||||
retenté à l'ouverture suivante (`data-mermaid-rendered` ne se pose qu'après
|
||||
succès). Le MD exporté embarque le diagramme en fenced block ` ```mermaid `
|
||||
(copiable, rendu par GitHub/VS Code/Obsidian) ; le PDF contient le même bloc.
|
||||
(copiable, rendu par GitHub/VS Code/Obsidian) ; le PDF affiche le PNG pré-rendu
|
||||
(section 4 — WeasyPrint n'exécute pas Mermaid).
|
||||
|
||||
## 4. Téléchargement (MD + PDF)
|
||||
|
||||
@@ -63,13 +64,26 @@ nu) — même stratégie que le bouton « PDF » des documents (#92).
|
||||
`index.html` et `fr.json`) pour éviter de re-générer à chaque requête.
|
||||
|
||||
Endpoint `GET /api/guide/download?format=md|pdf&lang=fr|en`
|
||||
(`require_auth`, tag OpenAPI **« Guide »**), `Content-Disposition: attachment`.
|
||||
Côté UI : boutons « Markdown » / « PDF » dans l'en-tête de la modale
|
||||
(`#help-download-md`/`#help-download-pdf`), handler `downloadGuide()` dans
|
||||
(`require_auth`, tag OpenAPI « Guide »), `Content-Disposition: attachment`.
|
||||
Côté UI : boutons **icônes seules** 📄/⬇ dans l'en-tête de la modale
|
||||
(`#help-download-md`/`#help-download-pdf`, tooltip i18n), handler `downloadGuide()` dans
|
||||
`frontend/js/config.js` — fetch avec `AuthManager.getAuthHeaders()` +
|
||||
credentials (identique à `viewer.downloadExport()`), blob → lien
|
||||
téléchargeable, toasts i18n réutilisés (`viewer.export_*`).
|
||||
|
||||
Diagramme d'architecture dans le PDF : WeasyPrint n'exécute pas Mermaid → le code
|
||||
Mermaid est **pré-rendu en PNG** (`scripts/build_guide_diagrams.py` +
|
||||
`scripts/render_guide_diagram.mjs`, Chromium + mermaid v11 CDN, scale 2) et le PNG
|
||||
est **commité** dans `backend/assets/guide_diagrams/<sha1>.png` ; `diagram_png_for()`
|
||||
(hash sha1[:16] du code normalisé, même algorithme des deux côtés) remplace le
|
||||
bloc par `<img src="file:///…">` dans le HTML d'export. Après modification du
|
||||
diagramme dans `guide_content.py` + réinsertion : relancer
|
||||
`python scripts/build_guide_diagrams.py` et committer le nouveau PNG.
|
||||
Emoji : l'image Docker installe `fonts-noto-color-emoji` et la pile de polices
|
||||
PDF finit par `"Noto Color Emoji"` — sans cela les emoji pleine chasse des titres
|
||||
de section s'affichent en rectangles (la TOC n'était pas touchée car elle passe
|
||||
par DejaVu/Sans).
|
||||
|
||||
## 5. Guide desktop élargi
|
||||
|
||||
`frontend/js/desktop.js::initDesktopIntegration()` ajoute `body.desktop-mode`
|
||||
@@ -106,14 +120,15 @@ même layout est accordé aux viewports ≥1400 px web via media-query (contenu
|
||||
|
||||
## 7. Tests & vérifications
|
||||
|
||||
- `tests/test_guide.py` (11) : ancre TOC→sections (garde-fou BUG-067),
|
||||
- `tests/test_guide.py` (13) : ancre TOC→sections (garde-fou BUG-067),
|
||||
sections #105 présentes, parité/présence des clés `guide105.*` FR=EN,
|
||||
Markdown FR/EN (titres, mermaid, absence de balises résiduelles), PDF
|
||||
(`%PDF`, taille), metadata `get_guide_document`, endpoint MD/PDF/400 via
|
||||
TestClient, OpenAPI (path + tag « Guide »).
|
||||
- Suite backend pytest : 1216 passed · ruff/mypy 0 erreur ·
|
||||
(`%PDF`, taille), **diagramme Architecture rendu en `<img>` PNG dans le HTML
|
||||
d'export**, MD garde le fenced mermaid, metadata `get_guide_document`,
|
||||
endpoint MD/PDF/400 via TestClient, OpenAPI (path + tag « Guide »).
|
||||
- Suite backend pytest : 1218 passed · ruff/mypy 0 erreur ·
|
||||
`validate-imports` 38 modules · `unit.test.mjs` 10/10 · E2E complet CI.
|
||||
- SW cache busting : `SW_VERSION` v21→v22.
|
||||
- SW cache busting : `SW_VERSION` v21→v23 (guides + boutons icônes).
|
||||
|
||||
## 8. Limites assumées
|
||||
|
||||
|
||||
@@ -2879,7 +2879,6 @@
|
||||
data-lucide="file-code-2"
|
||||
style="width: 16px; height: 16px"
|
||||
></i>
|
||||
<span data-i18n="guide105.dl_md">Markdown</span>
|
||||
</button>
|
||||
<button
|
||||
class="editor-btn"
|
||||
@@ -2891,7 +2890,6 @@
|
||||
data-lucide="file-down"
|
||||
style="width: 16px; height: 16px"
|
||||
></i>
|
||||
<span data-i18n="guide105.dl_pdf">PDF</span>
|
||||
</button>
|
||||
<button
|
||||
class="editor-btn"
|
||||
|
||||
@@ -2051,8 +2051,6 @@
|
||||
"guide105.admin_p": "The admin role unlocks a dedicated <code>/admin.html</code> page (Options menu button): live server status, users, vaults, active sessions and the audit log. User CRUD also lives in Configuration.",
|
||||
"guide105.dl_md_title": "Download this guide as Markdown",
|
||||
"guide105.dl_pdf_title": "Download this guide as PDF",
|
||||
"guide105.dl_md": "Markdown",
|
||||
"guide105.dl_pdf": "PDF",
|
||||
"guide105.export_title": "ObsiGate User Guide",
|
||||
"guide105.export_footer": "Generated from ObsiGate {version} — {date}. This document mirrors the in-app guide; the latest version always lives in the application."
|
||||
}
|
||||
|
||||
@@ -2051,8 +2051,6 @@
|
||||
"guide105.admin_p": "Le rôle admin ouvre une page dédiée <code>/admin.html</code> (bouton du menu Options) : statut du serveur en direct, utilisateurs, vaults, sessions actives et journal d'audit. Le CRUD utilisateurs est aussi disponible dans les Configurations.",
|
||||
"guide105.dl_md_title": "Télécharger ce guide en Markdown",
|
||||
"guide105.dl_pdf_title": "Télécharger ce guide en PDF",
|
||||
"guide105.dl_md": "Markdown",
|
||||
"guide105.dl_pdf": "PDF",
|
||||
"guide105.export_title": "Guide d'utilisation ObsiGate",
|
||||
"guide105.export_footer": "Généré depuis ObsiGate {version} — {date}. Ce document est la copie du guide intégré ; la version la plus récente est toujours dans l'application."
|
||||
}
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
* cache or Cloudflare does NOT clear the Service Worker Cache Storage, which is
|
||||
* a separate store. Bumping SW_VERSION invalidates it on every release.
|
||||
*/
|
||||
const SW_VERSION = 'v22';
|
||||
const SW_VERSION = 'v23';
|
||||
const CODE_CACHE = `obsigate-code-${SW_VERSION}`;
|
||||
const RUNTIME_CACHE = `obsigate-runtime-${SW_VERSION}`;
|
||||
const API_CACHE = `obsigate-api-${SW_VERSION}`;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "obsigate",
|
||||
"version": "2.13.0",
|
||||
"version": "2.13.1",
|
||||
"description": "**Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.",
|
||||
"main": "patch.js",
|
||||
"directories": {
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
"""Pré-rend les diagrammes Mermaid du guide intégré en PNG (#105).
|
||||
|
||||
Scanne les blocs ``<pre class="mermaid-code">`` de frontend/index.html,
|
||||
extrait leur code, et appelle scripts/render_guide_diagram.mjs (Chromium +
|
||||
mermaid v11) pour produire ``backend/assets/guide_diagrams/<sha1>.png``.
|
||||
Ces PNG sont commités : le PDF du guide les embarque comme vraies images
|
||||
(WeasyPrint ne sait pas exécuter Mermaid).
|
||||
|
||||
Usage : python scripts/build_guide_diagrams.py
|
||||
"""
|
||||
import hashlib
|
||||
import html
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
ASSETS = ROOT / "backend" / "assets" / "guide_diagrams"
|
||||
|
||||
|
||||
def extract_mermaid() -> dict[str, str]:
|
||||
page_html = (ROOT / "frontend" / "index.html").read_text(encoding="utf-8")
|
||||
out: dict[str, str] = {}
|
||||
for m in re.finditer(
|
||||
r'<pre class="mermaid-code"><code class="language-mermaid">(.*?)</code></pre>',
|
||||
page_html,
|
||||
re.DOTALL,
|
||||
):
|
||||
code = m.group(1)
|
||||
code = html.unescape(code).strip()
|
||||
sha = hashlib.sha1(code.encode("utf-8")).hexdigest()[:16]
|
||||
out[sha] = code
|
||||
return out
|
||||
|
||||
|
||||
def main() -> int:
|
||||
jobs = extract_mermaid()
|
||||
if not jobs:
|
||||
print("aucun bloc mermaid dans index.html")
|
||||
return 1
|
||||
todo = {k: v for k, v in jobs.items() if not (ASSETS / f"{k}.png").exists()}
|
||||
print("diagrammes:", len(jobs), "| à rendre:", len(todo))
|
||||
if not todo:
|
||||
return 0
|
||||
with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as f:
|
||||
json.dump(todo, f)
|
||||
path = f.name
|
||||
r = subprocess.run(
|
||||
["node", str(ROOT / "scripts" / "render_guide_diagram.mjs"), path],
|
||||
cwd=ROOT,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
text=True,
|
||||
timeout=300,
|
||||
)
|
||||
print(r.stdout[-2000:])
|
||||
if r.returncode != 0:
|
||||
print(r.stderr[-2000:])
|
||||
return r.returncode
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -427,9 +427,6 @@ CONTENT: dict[str, tuple[str, str]] = {
|
||||
# -- Boutons de téléchargement ---------------------------------------------------------------
|
||||
"dl_md_title": ("Télécharger ce guide en Markdown", "Download this guide as Markdown"),
|
||||
"dl_pdf_title": ("Télécharger ce guide en PDF", "Download this guide as PDF"),
|
||||
"dl_md": ("Markdown", "Markdown"),
|
||||
"dl_pdf": ("PDF", "PDF"),
|
||||
|
||||
# -- Export MD/PDF (côté serveur) -------------------------------------------------------------
|
||||
"export_title": ("Guide d'utilisation ObsiGate", "ObsiGate User Guide"),
|
||||
"export_footer": (
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// Pré-rend les diagrammes Mermaid du guide (#105 PDF) en PNG.
|
||||
// Usage: node scripts/render_guide_diagram.mjs
|
||||
// Lit le(s) code(s) Mermaid via --input JSON {sha: code} et écrit
|
||||
// backend/assets/guide_diagrams/<sha>.png (rendu chromium + mermaid v11 CDN,
|
||||
// scale 2, fond blanc).
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { createRequire } from 'module';
|
||||
const require = createRequire(import.meta.url);
|
||||
const { chromium } = require('playwright');
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const ROOT = path.resolve(__dirname, '..');
|
||||
const OUT_DIR = path.join(ROOT, 'backend', 'assets', 'guide_diagrams');
|
||||
|
||||
(async () => {
|
||||
const jobs = JSON.parse(fs.readFileSync(process.argv[2], 'utf8')); // {sha: code}
|
||||
fs.mkdirSync(OUT_DIR, { recursive: true });
|
||||
const browser = await chromium.launch();
|
||||
const page = await browser.newPage({ viewport: { width: 1800, height: 1400 }, deviceScaleFactor: 2 });
|
||||
await page.goto('about:blank');
|
||||
await page.addScriptTag({ url: 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js' });
|
||||
await page.waitForFunction(() => typeof window.mermaid !== 'undefined');
|
||||
for (const [sha, code] of Object.entries(jobs)) {
|
||||
const out = await page.evaluate(async (src) => {
|
||||
window.mermaid.initialize({ startOnLoad: false, theme: 'default', securityLevel: 'loose', fontFamily: 'DejaVu Sans, Arial, sans-serif' });
|
||||
try {
|
||||
const { svg } = await window.mermaid.render('d_' + Math.random().toString(36).slice(2), src);
|
||||
const box = document.createElement('div');
|
||||
box.innerHTML = svg;
|
||||
box.style.background = 'white';
|
||||
document.body.replaceChildren(box);
|
||||
const svgEl = box.querySelector('svg');
|
||||
if (!svgEl) return { err: 'no svg' };
|
||||
svgEl.style.maxWidth = 'none';
|
||||
const r = svgEl.getBoundingClientRect();
|
||||
return { w: Math.min(Math.ceil(r.width), 2000), h: Math.min(Math.ceil(r.height), 2600), ok: true };
|
||||
} catch (e) {
|
||||
return { err: String(e).slice(0, 300) };
|
||||
}
|
||||
}, code);
|
||||
if (out.err) { console.error(sha, 'RENDER ERROR', out.err); process.exitCode = 1; continue; }
|
||||
const file = path.join(OUT_DIR, sha + '.png');
|
||||
await page.screenshot({ path: file, clip: { x: 0, y: 0, width: out.w, height: out.h } });
|
||||
console.log(sha, '->', file, out.w + 'x' + out.h);
|
||||
}
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -116,6 +116,25 @@ def test_build_guide_pdf():
|
||||
assert len(pdf) > 20_000
|
||||
|
||||
|
||||
def test_guide_pdf_embeds_rendered_diagram():
|
||||
# #105 feedback : le bloc Mermaid de la section Architecture doit devenir
|
||||
# une VRAIE image dans le PDF (PNG pré-rendu commité), pas du code brut.
|
||||
import re
|
||||
|
||||
from backend.guide_export import build_guide_html
|
||||
|
||||
html = build_guide_html("fr")
|
||||
imgs = re.findall(r'<img src="file://[^"]+\.png"', html)
|
||||
assert imgs, "aucun diagramme PNG pré-rendu dans le HTML du guide"
|
||||
|
||||
|
||||
def test_guide_download_md_has_mermaid_fenced():
|
||||
# cote Markdown : le diagramme reste du code mermaid copiable
|
||||
from backend.guide_export import build_guide_markdown
|
||||
|
||||
assert "```mermaid" in build_guide_markdown("fr").decode("utf-8")
|
||||
|
||||
|
||||
def test_guide_document_metadata():
|
||||
from backend.guide_export import get_guide_document
|
||||
|
||||
|
||||
Reference in New Issue
Block a user