fix: guide - bouton telechargement icone seule, diagramme Architecture rendu en image dans le PDF, emoji couleur (Noto) (#105)
CI / lint (push) Successful in 1m39s
CI / security (push) Successful in 1m5s
CI / test (push) Successful in 3m25s
CI / build (push) Successful in 1m18s
CI / e2e (push) Successful in 11m43s

This commit is contained in:
2026-09-18 15:01:14 -04:00
parent 99a5b735c8
commit b8054665bc
23 changed files with 212 additions and 36 deletions
+49
View File
@@ -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();
})();