Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
update sidenote, properly remove mermaid zoom
Browse files- app/src/components/Sidenote.astro +97 -11
- app/src/scripts/mermaid-zoom.js +634 -633
app/src/components/Sidenote.astro
CHANGED
|
@@ -86,11 +86,43 @@
|
|
| 86 |
});
|
| 87 |
};
|
| 88 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
// Fonction pour démarrer l'initialisation avec observer si nécessaire
|
| 90 |
const startInit = () => {
|
| 91 |
// Essayer immédiatement
|
| 92 |
initSidenotes();
|
| 93 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
// Si on ne trouve pas de sidenotes, utiliser MutationObserver pour attendre
|
| 95 |
// que le contenu MDX soit rendu (important pour les Spaces Hugging Face)
|
| 96 |
const containers = document.querySelectorAll(".sidenote-container");
|
|
@@ -102,10 +134,14 @@
|
|
| 102 |
if (newContainers.length > 0) {
|
| 103 |
// Des sidenotes sont apparus, initialiser
|
| 104 |
initSidenotes();
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
// Arrêter l'observation après un délai pour éviter les boucles infinies
|
| 106 |
setTimeout(() => {
|
| 107 |
obs.disconnect();
|
| 108 |
-
}, 5000);
|
| 109 |
}
|
| 110 |
});
|
| 111 |
|
|
@@ -125,16 +161,66 @@
|
|
| 125 |
}
|
| 126 |
};
|
| 127 |
|
| 128 |
-
//
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
</script>
|
| 139 |
|
| 140 |
<style is:global>
|
|
|
|
| 86 |
});
|
| 87 |
};
|
| 88 |
|
| 89 |
+
// Vérifier que les styles sont bien appliqués
|
| 90 |
+
const verifyStylesApplied = () => {
|
| 91 |
+
const wrappers = document.querySelectorAll(".sidenote-wrapper");
|
| 92 |
+
let allApplied = true;
|
| 93 |
+
|
| 94 |
+
wrappers.forEach((wrapper) => {
|
| 95 |
+
const sidenote = wrapper.querySelector(
|
| 96 |
+
".sidenote-container",
|
| 97 |
+
) as HTMLElement;
|
| 98 |
+
if (sidenote) {
|
| 99 |
+
const computed = window.getComputedStyle(sidenote);
|
| 100 |
+
// Vérifier que position absolute est bien appliquée
|
| 101 |
+
if (computed.position !== "absolute" && window.innerWidth > 768) {
|
| 102 |
+
allApplied = false;
|
| 103 |
+
// Réappliquer les styles si nécessaire
|
| 104 |
+
sidenote.style.position = "absolute";
|
| 105 |
+
sidenote.style.top = "0";
|
| 106 |
+
sidenote.style.right = "-292px";
|
| 107 |
+
sidenote.style.width = "260px";
|
| 108 |
+
sidenote.style.display = "block";
|
| 109 |
+
}
|
| 110 |
+
}
|
| 111 |
+
});
|
| 112 |
+
|
| 113 |
+
return allApplied;
|
| 114 |
+
};
|
| 115 |
+
|
| 116 |
// Fonction pour démarrer l'initialisation avec observer si nécessaire
|
| 117 |
const startInit = () => {
|
| 118 |
// Essayer immédiatement
|
| 119 |
initSidenotes();
|
| 120 |
|
| 121 |
+
// Vérifier et réappliquer les styles si nécessaire
|
| 122 |
+
setTimeout(() => {
|
| 123 |
+
verifyStylesApplied();
|
| 124 |
+
}, 50);
|
| 125 |
+
|
| 126 |
// Si on ne trouve pas de sidenotes, utiliser MutationObserver pour attendre
|
| 127 |
// que le contenu MDX soit rendu (important pour les Spaces Hugging Face)
|
| 128 |
const containers = document.querySelectorAll(".sidenote-container");
|
|
|
|
| 134 |
if (newContainers.length > 0) {
|
| 135 |
// Des sidenotes sont apparus, initialiser
|
| 136 |
initSidenotes();
|
| 137 |
+
// Vérifier les styles après initialisation
|
| 138 |
+
setTimeout(() => {
|
| 139 |
+
verifyStylesApplied();
|
| 140 |
+
}, 50);
|
| 141 |
// Arrêter l'observation après un délai pour éviter les boucles infinies
|
| 142 |
setTimeout(() => {
|
| 143 |
obs.disconnect();
|
| 144 |
+
}, 5000);
|
| 145 |
}
|
| 146 |
});
|
| 147 |
|
|
|
|
| 161 |
}
|
| 162 |
};
|
| 163 |
|
| 164 |
+
// Attendre que toutes les ressources soient chargées (images, etc.)
|
| 165 |
+
const waitForFullLoad = () => {
|
| 166 |
+
// Helper pour utiliser requestIdleCallback si disponible
|
| 167 |
+
const idleOrTimeout = (callback: () => void, timeout: number) => {
|
| 168 |
+
if (typeof (window as any).requestIdleCallback === "function") {
|
| 169 |
+
(window as any).requestIdleCallback(callback, { timeout });
|
| 170 |
+
} else {
|
| 171 |
+
setTimeout(callback, timeout);
|
| 172 |
+
}
|
| 173 |
+
};
|
| 174 |
+
|
| 175 |
+
if (document.readyState === "loading") {
|
| 176 |
+
// Attendre DOMContentLoaded puis window.load
|
| 177 |
+
document.addEventListener(
|
| 178 |
+
"DOMContentLoaded",
|
| 179 |
+
() => {
|
| 180 |
+
idleOrTimeout(startInit, 300);
|
| 181 |
+
},
|
| 182 |
+
{ once: true },
|
| 183 |
+
);
|
| 184 |
+
|
| 185 |
+
// Aussi attendre window.load pour les ressources
|
| 186 |
+
window.addEventListener(
|
| 187 |
+
"load",
|
| 188 |
+
() => {
|
| 189 |
+
setTimeout(() => {
|
| 190 |
+
initSidenotes();
|
| 191 |
+
verifyStylesApplied();
|
| 192 |
+
}, 100);
|
| 193 |
+
},
|
| 194 |
+
{ once: true },
|
| 195 |
+
);
|
| 196 |
+
} else if (document.readyState === "interactive") {
|
| 197 |
+
// DOM est chargé mais pas toutes les ressources
|
| 198 |
+
idleOrTimeout(startInit, 300);
|
| 199 |
+
|
| 200 |
+
// Aussi attendre window.load
|
| 201 |
+
window.addEventListener(
|
| 202 |
+
"load",
|
| 203 |
+
() => {
|
| 204 |
+
setTimeout(() => {
|
| 205 |
+
initSidenotes();
|
| 206 |
+
verifyStylesApplied();
|
| 207 |
+
}, 100);
|
| 208 |
+
},
|
| 209 |
+
{ once: true },
|
| 210 |
+
);
|
| 211 |
+
} else {
|
| 212 |
+
// Tout est déjà chargé
|
| 213 |
+
idleOrTimeout(startInit, 300);
|
| 214 |
+
|
| 215 |
+
// Vérifier une dernière fois après un délai pour s'assurer que tout est bien rendu
|
| 216 |
+
setTimeout(() => {
|
| 217 |
+
initSidenotes();
|
| 218 |
+
verifyStylesApplied();
|
| 219 |
+
}, 500);
|
| 220 |
+
}
|
| 221 |
+
};
|
| 222 |
+
|
| 223 |
+
waitForFullLoad();
|
| 224 |
</script>
|
| 225 |
|
| 226 |
<style is:global>
|
app/src/scripts/mermaid-zoom.js
CHANGED
|
@@ -19,122 +19,122 @@ if (!MERMAID_ZOOM_ENABLED) {
|
|
| 19 |
openZoom: () => { },
|
| 20 |
closeZoom: () => { }
|
| 21 |
};
|
| 22 |
-
// Arrêter l'exécution ici
|
| 23 |
-
|
| 24 |
-
}
|
| 25 |
-
|
| 26 |
-
// Fonction pour appliquer les styles Mermaid au SVG selon le thème
|
| 27 |
-
function applyMermaidStylesToSvg(svgElement) {
|
| 28 |
-
try {
|
| 29 |
-
const isDark = document.documentElement.getAttribute("data-theme") === "dark";
|
| 30 |
-
console.log(`🎨 Applying Mermaid styles for theme: ${isDark ? 'dark' : 'light'}`);
|
| 31 |
-
|
| 32 |
-
// Couleurs selon le thème - avec plus de contraste pour le zoom
|
| 33 |
-
const colors = isDark ? {
|
| 34 |
-
nodeFill: '#1a1a1a',
|
| 35 |
-
nodeStroke: '#ffffff',
|
| 36 |
-
nodeStrokeWidth: '2', // Plus épais pour plus de visibilité
|
| 37 |
-
clusterFill: '#2a2a2a',
|
| 38 |
-
clusterStroke: '#ffffff',
|
| 39 |
-
clusterStrokeWidth: '2',
|
| 40 |
-
pathStroke: '#ffffff',
|
| 41 |
-
pathStrokeWidth: '2', // Plus épais pour les flèches
|
| 42 |
-
textColor: '#ffffff',
|
| 43 |
-
linkColor: '#ffffff' // Couleur spécifique pour les liens
|
| 44 |
-
} : {
|
| 45 |
-
nodeFill: '#ffffff',
|
| 46 |
-
nodeStroke: '#000000', // Noir pur pour plus de contraste
|
| 47 |
-
nodeStrokeWidth: '2',
|
| 48 |
-
clusterFill: '#f9f9f9',
|
| 49 |
-
clusterStroke: '#000000', // Noir pur
|
| 50 |
-
clusterStrokeWidth: '2',
|
| 51 |
-
pathStroke: '#000000', // Noir pur pour les flèches
|
| 52 |
-
pathStrokeWidth: '2',
|
| 53 |
-
textColor: '#000000', // Noir pur
|
| 54 |
-
linkColor: '#000000' // Noir pur pour les liens
|
| 55 |
-
};
|
| 56 |
-
|
| 57 |
-
// Appliquer border-radius aux rectangles
|
| 58 |
-
const rects = svgElement.querySelectorAll('rect:not(.flowchart-link), .node rect, .nodeLabel rect');
|
| 59 |
-
rects.forEach(rect => {
|
| 60 |
-
rect.setAttribute('rx', '8');
|
| 61 |
-
rect.setAttribute('ry', '8');
|
| 62 |
-
rect.setAttribute('fill', colors.nodeFill);
|
| 63 |
-
rect.setAttribute('stroke', colors.nodeStroke);
|
| 64 |
-
rect.setAttribute('stroke-width', colors.nodeStrokeWidth);
|
| 65 |
-
});
|
| 66 |
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
rect.setAttribute('fill', colors.nodeFill);
|
| 83 |
rect.setAttribute('stroke', colors.nodeStroke);
|
| 84 |
rect.setAttribute('stroke-width', colors.nodeStrokeWidth);
|
| 85 |
-
}
|
| 86 |
-
});
|
| 87 |
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
rect.setAttribute('fill', colors.clusterFill);
|
| 94 |
rect.setAttribute('stroke', colors.clusterStroke);
|
| 95 |
rect.setAttribute('stroke-width', colors.clusterStrokeWidth);
|
| 96 |
-
}
|
| 97 |
-
});
|
| 98 |
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
|
|
|
| 118 |
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
|
| 126 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
|
| 128 |
-
|
| 129 |
-
|
|
|
|
|
|
|
|
|
|
| 130 |
}
|
| 131 |
-
}
|
| 132 |
|
| 133 |
-
// Fonction pour extraire les styles CSS appliqués au diagramme Mermaid
|
| 134 |
-
function getComputedStylesForMermaid(mermaidElement) {
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
.mermaid rect:not(.flowchart-link),
|
| 139 |
.mermaid .node rect,
|
| 140 |
.mermaid .nodeLabel rect {
|
|
@@ -170,194 +170,267 @@ function getComputedStylesForMermaid(mermaidElement) {
|
|
| 170 |
}
|
| 171 |
`;
|
| 172 |
|
| 173 |
-
|
| 174 |
-
|
| 175 |
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
}
|
| 180 |
-
}
|
| 181 |
-
|
| 182 |
-
// Fonction pour forcer Mermaid à se re-rendre avec le bon thème
|
| 183 |
-
function forceMermaidThemeUpdate(mermaidElement) {
|
| 184 |
-
try {
|
| 185 |
-
// Obtenir le thème actuel
|
| 186 |
-
const currentTheme = document.documentElement.getAttribute("data-theme");
|
| 187 |
-
console.log(`🎨 Forcing Mermaid theme update to: ${currentTheme}`);
|
| 188 |
-
|
| 189 |
-
// Trouver le diagramme Mermaid original
|
| 190 |
-
const mermaidCode = mermaidElement.textContent || mermaidElement.innerText;
|
| 191 |
-
if (!mermaidCode) {
|
| 192 |
-
console.log(`❌ No Mermaid code found to re-render`);
|
| 193 |
-
return false;
|
| 194 |
}
|
|
|
|
| 195 |
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 203 |
|
| 204 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 205 |
|
| 206 |
-
|
| 207 |
-
mermaid.init(undefined, mermaidElement);
|
| 208 |
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 212 |
return false;
|
| 213 |
}
|
| 214 |
-
} catch (error) {
|
| 215 |
-
console.error(`❌ Error forcing Mermaid theme update:`, error);
|
| 216 |
-
return false;
|
| 217 |
}
|
| 218 |
-
}
|
| 219 |
|
| 220 |
-
// Fonction pour convertir SVG en image en préservant EXACTEMENT les dimensions
|
| 221 |
-
function convertSvgToImagePreservingDimensions(svgElement, wrapper, originalMermaid) {
|
| 222 |
-
|
| 223 |
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
|
| 230 |
-
|
| 231 |
|
| 232 |
-
|
| 233 |
-
|
| 234 |
|
| 235 |
-
|
| 236 |
-
|
| 237 |
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
|
| 273 |
-
|
| 274 |
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
|
| 279 |
-
|
| 280 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 281 |
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 285 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 286 |
const isDark = document.documentElement.getAttribute("data-theme") === "dark";
|
| 287 |
const background = isDark ? "rgba(0,0,0,.9)" : "rgba(0,0,0,.85)";
|
| 288 |
|
| 289 |
-
// Utiliser
|
| 290 |
-
|
| 291 |
background,
|
| 292 |
margin: 24,
|
| 293 |
scrollOffset: 0,
|
| 294 |
});
|
| 295 |
|
| 296 |
-
console.log(`🎉
|
| 297 |
-
|
| 298 |
-
// Forcer les bonnes couleurs en JavaScript (contournement du CSS global)
|
| 299 |
-
const forceCorrectColors = () => {
|
| 300 |
-
// Trouver l'image zoomée dans le DOM
|
| 301 |
-
const zoomedImage = document.querySelector('.medium-zoom-image--opened');
|
| 302 |
-
if (zoomedImage && zoomedImage.classList.contains('mermaid-zoom-image')) {
|
| 303 |
-
console.log(`🎨 Forcing correct colors for Mermaid zoom image`);
|
| 304 |
-
zoomedImage.style.filter = 'none';
|
| 305 |
-
zoomedImage.style.setProperty('filter', 'none', 'important');
|
| 306 |
-
}
|
| 307 |
-
|
| 308 |
-
// Forcer les z-index élevés
|
| 309 |
-
const overlay = document.querySelector('.medium-zoom-overlay');
|
| 310 |
-
if (overlay) {
|
| 311 |
-
console.log(`🔝 Forcing high z-index for overlay`);
|
| 312 |
-
overlay.style.zIndex = '9999999';
|
| 313 |
-
overlay.style.setProperty('z-index', '9999999', 'important');
|
| 314 |
-
}
|
| 315 |
-
|
| 316 |
-
if (zoomedImage) {
|
| 317 |
-
console.log(`🔝 Forcing high z-index for zoomed image`);
|
| 318 |
-
zoomedImage.style.zIndex = '10000000';
|
| 319 |
-
zoomedImage.style.setProperty('z-index', '10000000', 'important');
|
| 320 |
-
}
|
| 321 |
-
};
|
| 322 |
-
|
| 323 |
-
// Écouter les événements de zoom pour appliquer les bonnes couleurs
|
| 324 |
-
imgElement.addEventListener('zoom:open', forceCorrectColors);
|
| 325 |
-
imgElement.addEventListener('zoom:opened', forceCorrectColors);
|
| 326 |
|
| 327 |
// Observer pour les changements de thème
|
| 328 |
const themeObserver = new MutationObserver(() => {
|
| 329 |
-
console.log(`🎨 Theme changed,
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
const currentTheme = document.documentElement.getAttribute("data-theme");
|
| 333 |
-
console.log(`🎨 Current theme: ${currentTheme}`);
|
| 334 |
-
|
| 335 |
-
// Attendre un peu pour que Mermaid détecte le changement de thème
|
| 336 |
-
setTimeout(() => {
|
| 337 |
-
// Forcer Mermaid à se re-rendre avec le nouveau thème
|
| 338 |
-
const themeUpdated = forceMermaidThemeUpdate(originalMermaid);
|
| 339 |
-
|
| 340 |
-
if (themeUpdated) {
|
| 341 |
-
// Attendre que le re-rendu soit terminé
|
| 342 |
-
setTimeout(() => {
|
| 343 |
-
// Re-obtenir le SVG mis à jour
|
| 344 |
-
const updatedSvgElement = originalMermaid.querySelector("svg");
|
| 345 |
-
if (updatedSvgElement) {
|
| 346 |
-
console.log(`🔄 Re-converting with updated SVG for theme: ${currentTheme}`);
|
| 347 |
-
convertSvgToImagePreservingDimensions(updatedSvgElement, wrapper, originalMermaid);
|
| 348 |
-
} else {
|
| 349 |
-
console.log(`❌ No updated SVG found after theme change`);
|
| 350 |
-
}
|
| 351 |
-
}, 200); // Délai pour laisser Mermaid finir le re-rendu
|
| 352 |
-
} else {
|
| 353 |
-
console.log(`❌ Failed to update Mermaid theme, using current SVG`);
|
| 354 |
-
// Fallback: utiliser le SVG actuel même s'il n'est pas mis à jour
|
| 355 |
-
const currentSvgElement = originalMermaid.querySelector("svg");
|
| 356 |
-
if (currentSvgElement) {
|
| 357 |
-
convertSvgToImagePreservingDimensions(currentSvgElement, wrapper, originalMermaid);
|
| 358 |
-
}
|
| 359 |
-
}
|
| 360 |
-
}, 100); // Petit délai pour laisser Mermaid se mettre à jour
|
| 361 |
});
|
| 362 |
|
| 363 |
themeObserver.observe(document.documentElement, {
|
|
@@ -365,220 +438,147 @@ function convertSvgToImagePreservingDimensions(svgElement, wrapper, originalMerm
|
|
| 365 |
attributeFilter: ["data-theme"],
|
| 366 |
});
|
| 367 |
|
|
|
|
| 368 |
wrapper._themeObserver = themeObserver;
|
| 369 |
-
};
|
| 370 |
|
| 371 |
-
|
| 372 |
-
console.error(
|
| 373 |
// Fallback: utiliser le zoom custom
|
| 374 |
-
wrapper.innerHTML = '';
|
| 375 |
-
wrapper.appendChild(originalMermaid);
|
| 376 |
wrapper.classList.remove("converting");
|
| 377 |
wrapper.addEventListener("click", (e) => {
|
| 378 |
e.preventDefault();
|
| 379 |
e.stopPropagation();
|
| 380 |
openMermaidZoom(wrapper, originalMermaid);
|
| 381 |
});
|
| 382 |
-
}
|
| 383 |
-
|
| 384 |
-
} catch (error) {
|
| 385 |
-
console.error("❌ Error converting SVG to image:", error);
|
| 386 |
-
// Fallback: utiliser le zoom custom
|
| 387 |
-
wrapper.classList.remove("converting");
|
| 388 |
-
wrapper.addEventListener("click", (e) => {
|
| 389 |
-
e.preventDefault();
|
| 390 |
-
e.stopPropagation();
|
| 391 |
-
openMermaidZoom(wrapper, originalMermaid);
|
| 392 |
-
});
|
| 393 |
}
|
| 394 |
-
}
|
| 395 |
-
|
| 396 |
-
// Fonction pour initialiser medium-zoom directement sur le SVG
|
| 397 |
-
function initializeMediumZoomOnSvg(svgElement, wrapper, originalMermaid) {
|
| 398 |
-
try {
|
| 399 |
-
// S'assurer que le SVG a des dimensions fixes pour éviter le flicker
|
| 400 |
-
const bbox = svgElement.getBBox();
|
| 401 |
-
const width = bbox.width || svgElement.clientWidth || 800;
|
| 402 |
-
const height = bbox.height || svgElement.clientHeight || 600;
|
| 403 |
-
|
| 404 |
-
// Fixer les dimensions du SVG pour éviter les changements
|
| 405 |
-
svgElement.setAttribute('width', width);
|
| 406 |
-
svgElement.setAttribute('height', height);
|
| 407 |
-
svgElement.style.width = '100%';
|
| 408 |
-
svgElement.style.height = 'auto';
|
| 409 |
-
svgElement.style.display = 'block';
|
| 410 |
-
|
| 411 |
-
// Retirer la classe "converting" pour restaurer l'opacité normale
|
| 412 |
-
wrapper.classList.remove("converting");
|
| 413 |
-
|
| 414 |
-
console.log(`📏 SVG dimensions fixed:`, { width, height });
|
| 415 |
-
|
| 416 |
-
// Initialiser medium-zoom directement sur le wrapper SVG
|
| 417 |
-
const isDark = document.documentElement.getAttribute("data-theme") === "dark";
|
| 418 |
-
const background = isDark ? "rgba(0,0,0,.9)" : "rgba(0,0,0,.85)";
|
| 419 |
-
|
| 420 |
-
// Utiliser medium-zoom sur le wrapper qui contient le SVG
|
| 421 |
-
window.mediumZoom(wrapper, {
|
| 422 |
-
background,
|
| 423 |
-
margin: 24,
|
| 424 |
-
scrollOffset: 0,
|
| 425 |
-
});
|
| 426 |
-
|
| 427 |
-
console.log(`🎉 Medium-zoom initialized directly on SVG wrapper!`);
|
| 428 |
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
initializeMediumZoomOnSvg(svgElement, wrapper, originalMermaid);
|
| 434 |
-
});
|
| 435 |
-
|
| 436 |
-
themeObserver.observe(document.documentElement, {
|
| 437 |
-
attributes: true,
|
| 438 |
-
attributeFilter: ["data-theme"],
|
| 439 |
-
});
|
| 440 |
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
|
| 447 |
-
|
| 448 |
-
|
| 449 |
-
|
| 450 |
-
e.stopPropagation();
|
| 451 |
-
openMermaidZoom(wrapper, originalMermaid);
|
| 452 |
-
});
|
| 453 |
}
|
| 454 |
-
}
|
| 455 |
-
|
| 456 |
-
// Fonction pour attendre que Mermaid soit stable (évite le flicker)
|
| 457 |
-
function waitForMermaidStable(mermaidEl, wrapper, index) {
|
| 458 |
-
// Approche simplifiée : attendre un délai fixe puis convertir
|
| 459 |
-
console.log(`⏳ Waiting for Mermaid ${index} to stabilize...`);
|
| 460 |
-
|
| 461 |
-
setTimeout(() => {
|
| 462 |
-
const svgElement = mermaidEl.querySelector("svg");
|
| 463 |
-
if (svgElement) {
|
| 464 |
-
console.log(`🎯 Converting Mermaid ${index} to image`);
|
| 465 |
-
convertSvgToImageForMediumZoom(svgElement, wrapper, mermaidEl);
|
| 466 |
-
} else {
|
| 467 |
-
console.log(`❌ No SVG found in Mermaid element ${index}`);
|
| 468 |
-
}
|
| 469 |
-
}, 2000); // Attendre 2 secondes puis convertir
|
| 470 |
-
}
|
| 471 |
|
| 472 |
-
// Fonction pour convertir SVG en image SANS canvas (pour éviter l'erreur de sécurité)
|
| 473 |
-
function convertSvgToImageForMediumZoom(svgElement, wrapper, originalMermaid) {
|
| 474 |
-
|
| 475 |
|
| 476 |
-
|
| 477 |
-
|
| 478 |
-
|
| 479 |
|
| 480 |
-
|
| 481 |
-
|
| 482 |
|
| 483 |
-
|
| 484 |
-
|
| 485 |
-
|
| 486 |
-
|
| 487 |
|
| 488 |
-
|
| 489 |
|
| 490 |
-
|
| 491 |
-
|
| 492 |
|
| 493 |
-
|
| 494 |
-
|
| 495 |
-
|
| 496 |
-
|
| 497 |
|
| 498 |
-
|
| 499 |
-
|
| 500 |
-
|
| 501 |
-
|
| 502 |
-
|
| 503 |
|
| 504 |
-
|
| 505 |
-
|
| 506 |
-
|
| 507 |
-
|
| 508 |
-
|
| 509 |
-
|
| 510 |
-
|
| 511 |
|
| 512 |
-
|
| 513 |
-
|
| 514 |
-
|
| 515 |
|
| 516 |
-
|
| 517 |
-
|
| 518 |
|
| 519 |
-
|
| 520 |
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
|
| 524 |
|
| 525 |
-
|
| 526 |
-
|
| 527 |
|
| 528 |
-
|
| 529 |
-
|
| 530 |
-
|
| 531 |
-
|
| 532 |
-
|
| 533 |
-
|
| 534 |
|
| 535 |
-
|
| 536 |
|
| 537 |
-
|
| 538 |
-
|
| 539 |
-
|
| 540 |
-
|
| 541 |
-
|
| 542 |
-
|
| 543 |
|
| 544 |
-
|
| 545 |
-
|
| 546 |
-
|
| 547 |
-
|
| 548 |
|
| 549 |
-
|
| 550 |
-
|
| 551 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 552 |
|
| 553 |
-
|
| 554 |
-
console.error(
|
| 555 |
// Fallback: utiliser le zoom custom
|
| 556 |
-
wrapper.innerHTML = "";
|
| 557 |
-
wrapper.appendChild(originalMermaid);
|
| 558 |
wrapper.addEventListener("click", (e) => {
|
| 559 |
e.preventDefault();
|
| 560 |
e.stopPropagation();
|
| 561 |
openMermaidZoom(wrapper, originalMermaid);
|
| 562 |
});
|
| 563 |
-
}
|
| 564 |
-
|
| 565 |
-
} catch (error) {
|
| 566 |
-
console.error("❌ Error converting SVG to image:", error);
|
| 567 |
-
// Fallback: utiliser le zoom custom
|
| 568 |
-
wrapper.addEventListener("click", (e) => {
|
| 569 |
-
e.preventDefault();
|
| 570 |
-
e.stopPropagation();
|
| 571 |
-
openMermaidZoom(wrapper, originalMermaid);
|
| 572 |
-
});
|
| 573 |
}
|
| 574 |
-
|
| 575 |
-
|
| 576 |
-
|
| 577 |
-
|
| 578 |
-
|
| 579 |
-
|
| 580 |
-
|
| 581 |
-
overlay.style.cssText = `
|
| 582 |
position: fixed;
|
| 583 |
top: 0;
|
| 584 |
left: 0;
|
|
@@ -594,10 +594,10 @@ function openMermaidZoom(wrapper, mermaidElement) {
|
|
| 594 |
transition: opacity 0.3s ease;
|
| 595 |
`;
|
| 596 |
|
| 597 |
-
|
| 598 |
-
|
| 599 |
-
|
| 600 |
-
|
| 601 |
max-width: 90%;
|
| 602 |
max-height: 90%;
|
| 603 |
background: white;
|
|
@@ -609,249 +609,250 @@ function openMermaidZoom(wrapper, mermaidElement) {
|
|
| 609 |
overflow: auto;
|
| 610 |
`;
|
| 611 |
|
| 612 |
-
|
| 613 |
-
|
| 614 |
-
|
| 615 |
width: 100%;
|
| 616 |
height: auto;
|
| 617 |
max-width: none;
|
| 618 |
display: block;
|
| 619 |
`;
|
| 620 |
|
| 621 |
-
|
| 622 |
-
|
| 623 |
|
| 624 |
-
|
| 625 |
-
|
| 626 |
|
| 627 |
-
|
| 628 |
-
|
| 629 |
-
|
| 630 |
-
|
| 631 |
-
|
| 632 |
|
| 633 |
-
|
| 634 |
-
|
| 635 |
-
|
| 636 |
-
|
| 637 |
-
|
| 638 |
-
|
| 639 |
|
| 640 |
-
|
| 641 |
-
|
| 642 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 643 |
closeMermaidZoom(overlay);
|
| 644 |
-
|
| 645 |
-
}
|
| 646 |
-
|
| 647 |
-
|
| 648 |
|
| 649 |
-
|
| 650 |
-
|
| 651 |
-
closeMermaidZoom(overlay);
|
| 652 |
-
};
|
| 653 |
-
window.addEventListener("wheel", handleScroll, { passive: true });
|
| 654 |
-
window.addEventListener("touchmove", handleScroll, { passive: true });
|
| 655 |
-
window.addEventListener("scroll", handleScroll, { passive: true });
|
| 656 |
-
|
| 657 |
-
// Stocker les handlers pour les nettoyer
|
| 658 |
-
overlay._handlers = { handleEscape, handleScroll };
|
| 659 |
-
}
|
| 660 |
-
|
| 661 |
-
function closeMermaidZoom(overlay) {
|
| 662 |
-
// Animation de fermeture
|
| 663 |
-
overlay.style.opacity = "0";
|
| 664 |
-
const zoomImage = overlay.querySelector(".medium-zoom-image");
|
| 665 |
-
if (zoomImage) {
|
| 666 |
-
zoomImage.style.transform = "scale(0.8)";
|
| 667 |
}
|
| 668 |
|
| 669 |
-
|
| 670 |
-
|
| 671 |
-
|
| 672 |
-
|
| 673 |
-
|
| 674 |
-
|
| 675 |
-
|
| 676 |
|
| 677 |
-
|
| 678 |
-
|
| 679 |
-
|
| 680 |
-
|
|
|
|
|
|
|
| 681 |
}
|
| 682 |
-
|
| 683 |
-
|
| 684 |
-
|
| 685 |
-
|
| 686 |
-
|
| 687 |
-
|
| 688 |
-
|
| 689 |
-
wrapper._themeObserver = null;
|
| 690 |
}
|
| 691 |
-
|
| 692 |
-
|
| 693 |
-
|
| 694 |
-
|
| 695 |
-
|
| 696 |
-
|
| 697 |
-
console.log(`🔍 Found ${mermaidElements.length} Mermaid elements`);
|
| 698 |
-
|
| 699 |
-
let processedCount = 0;
|
| 700 |
-
mermaidElements.forEach((mermaidEl, index) => {
|
| 701 |
-
// Vérifier si déjà wrappé
|
| 702 |
-
if (
|
| 703 |
-
mermaidEl.parentElement &&
|
| 704 |
-
mermaidEl.parentElement.classList.contains("mermaid-zoom-wrapper")
|
| 705 |
-
) {
|
| 706 |
-
console.log(`📦 Mermaid ${index} already wrapped`);
|
| 707 |
-
processedCount++;
|
| 708 |
-
return;
|
| 709 |
}
|
|
|
|
| 710 |
|
| 711 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 712 |
|
| 713 |
-
|
| 714 |
-
const wrapper = document.createElement("div");
|
| 715 |
-
wrapper.className = "mermaid-zoom-wrapper";
|
| 716 |
-
wrapper.setAttribute("data-zoomable", "1");
|
| 717 |
|
| 718 |
-
|
| 719 |
-
|
|
|
|
|
|
|
| 720 |
|
| 721 |
-
|
| 722 |
-
|
| 723 |
|
| 724 |
-
|
| 725 |
-
|
| 726 |
-
wrapper.style.width = "100%";
|
| 727 |
-
wrapper.style.maxWidth = "100%";
|
| 728 |
|
| 729 |
-
|
| 730 |
-
|
|
|
|
|
|
|
| 731 |
|
| 732 |
-
|
| 733 |
-
|
| 734 |
|
| 735 |
-
|
| 736 |
-
|
| 737 |
|
| 738 |
-
|
| 739 |
-
|
| 740 |
-
const svgElement = mermaidEl.querySelector("svg");
|
| 741 |
-
if (svgElement) {
|
| 742 |
-
console.log(`🎯 Converting SVG to image with dimension preservation`);
|
| 743 |
-
convertSvgToImagePreservingDimensions(svgElement, wrapper, mermaidEl);
|
| 744 |
-
} else {
|
| 745 |
-
console.log(`❌ No SVG found in Mermaid element ${index}`);
|
| 746 |
-
}
|
| 747 |
-
}, 1000); // Réduit de 2000ms à 1000ms pour être plus rapide
|
| 748 |
-
});
|
| 749 |
|
| 750 |
-
|
| 751 |
-
|
| 752 |
-
|
| 753 |
-
|
| 754 |
-
|
| 755 |
-
|
| 756 |
-
|
| 757 |
-
|
| 758 |
-
// Vérifier si une image Mermaid est zoomée
|
| 759 |
-
const zoomedMermaidImage = document.querySelector('.medium-zoom-image--opened.mermaid-zoom-image');
|
| 760 |
-
if (zoomedMermaidImage) {
|
| 761 |
-
console.log(`🎨 Global fix: Found zoomed Mermaid image, forcing correct colors and z-index`);
|
| 762 |
-
zoomedMermaidImage.style.filter = 'none';
|
| 763 |
-
zoomedMermaidImage.style.setProperty('filter', 'none', 'important');
|
| 764 |
-
zoomedMermaidImage.style.zIndex = '10000000';
|
| 765 |
-
zoomedMermaidImage.style.setProperty('z-index', '10000000', 'important');
|
| 766 |
}
|
|
|
|
|
|
|
| 767 |
|
| 768 |
-
|
| 769 |
-
|
| 770 |
-
|
| 771 |
-
|
| 772 |
-
|
| 773 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 774 |
}
|
| 775 |
-
}
|
| 776 |
});
|
| 777 |
-
});
|
| 778 |
|
| 779 |
-
|
| 780 |
-
|
| 781 |
-
|
| 782 |
-
|
| 783 |
|
| 784 |
-
|
| 785 |
-
}
|
| 786 |
-
|
| 787 |
-
// Fonction pour initialiser le zoom Mermaid avec retry
|
| 788 |
-
function initMermaidZoom() {
|
| 789 |
-
console.log("🎯 initMermaidZoom called");
|
| 790 |
-
|
| 791 |
-
// Attendre que mediumZoom soit disponible
|
| 792 |
-
const checkMediumZoom = () => {
|
| 793 |
-
if (window.mediumZoom) {
|
| 794 |
-
console.log("✅ mediumZoom found, initializing Mermaid zoom");
|
| 795 |
-
setupMermaidZoom();
|
| 796 |
-
} else {
|
| 797 |
-
console.log("⏳ Waiting for mediumZoom...");
|
| 798 |
-
setTimeout(checkMediumZoom, 100);
|
| 799 |
-
}
|
| 800 |
-
};
|
| 801 |
|
| 802 |
-
|
| 803 |
-
|
| 804 |
-
|
| 805 |
-
// Export des fonctions pour utilisation globale
|
| 806 |
-
window.MermaidZoom = {
|
| 807 |
-
init: initMermaidZoom,
|
| 808 |
-
setup: setupMermaidZoom,
|
| 809 |
-
cleanup: cleanupMermaidObservers,
|
| 810 |
-
convertSvgToImage: convertSvgToImageForMediumZoom,
|
| 811 |
-
openZoom: openMermaidZoom,
|
| 812 |
-
closeZoom: closeMermaidZoom
|
| 813 |
-
};
|
| 814 |
-
|
| 815 |
-
// Auto-initialisation si le DOM est déjà chargé
|
| 816 |
-
if (document.readyState === "loading") {
|
| 817 |
-
document.addEventListener("DOMContentLoaded", () => {
|
| 818 |
-
initMermaidZoom();
|
| 819 |
-
setupGlobalMermaidColorFix();
|
| 820 |
-
});
|
| 821 |
-
} else {
|
| 822 |
-
initMermaidZoom();
|
| 823 |
-
setupGlobalMermaidColorFix();
|
| 824 |
-
}
|
| 825 |
|
| 826 |
-
//
|
| 827 |
-
|
| 828 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 829 |
initMermaidZoom();
|
| 830 |
setupGlobalMermaidColorFix();
|
| 831 |
-
}
|
| 832 |
-
});
|
| 833 |
-
|
| 834 |
-
// Observer simple pour les nouveaux diagrammes Mermaid (avec debounce)
|
| 835 |
-
let resizeTimeout;
|
| 836 |
-
const observer = new MutationObserver(() => {
|
| 837 |
-
// Debounce pour éviter les appels multiples
|
| 838 |
-
clearTimeout(resizeTimeout);
|
| 839 |
-
resizeTimeout = setTimeout(() => {
|
| 840 |
-
// Vérifier s'il y a vraiment de nouveaux diagrammes
|
| 841 |
-
const mermaidElements = document.querySelectorAll(".mermaid");
|
| 842 |
-
const wrappedElements = document.querySelectorAll(".mermaid-zoom-wrapper");
|
| 843 |
|
| 844 |
-
|
| 845 |
-
|
| 846 |
-
|
| 847 |
initMermaidZoom();
|
| 848 |
-
|
| 849 |
-
|
| 850 |
-
});
|
| 851 |
|
| 852 |
-
|
| 853 |
-
|
| 854 |
-
|
| 855 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 856 |
|
| 857 |
-
console.log("🚀 Mermaid Zoom Script v19.0 loaded - DEBOUNCED observer to prevent resize loops");
|
|
|
|
|
|
| 19 |
openZoom: () => { },
|
| 20 |
closeZoom: () => { }
|
| 21 |
};
|
| 22 |
+
// Arrêter l'exécution ici sans lancer d'erreur
|
| 23 |
+
// Le reste du code ne s'exécutera pas car tout est dans le bloc if ci-dessous
|
| 24 |
+
} else {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
+
// Fonction pour appliquer les styles Mermaid au SVG selon le thème
|
| 27 |
+
function applyMermaidStylesToSvg(svgElement) {
|
| 28 |
+
try {
|
| 29 |
+
const isDark = document.documentElement.getAttribute("data-theme") === "dark";
|
| 30 |
+
console.log(`🎨 Applying Mermaid styles for theme: ${isDark ? 'dark' : 'light'}`);
|
| 31 |
+
|
| 32 |
+
// Couleurs selon le thème - avec plus de contraste pour le zoom
|
| 33 |
+
const colors = isDark ? {
|
| 34 |
+
nodeFill: '#1a1a1a',
|
| 35 |
+
nodeStroke: '#ffffff',
|
| 36 |
+
nodeStrokeWidth: '2', // Plus épais pour plus de visibilité
|
| 37 |
+
clusterFill: '#2a2a2a',
|
| 38 |
+
clusterStroke: '#ffffff',
|
| 39 |
+
clusterStrokeWidth: '2',
|
| 40 |
+
pathStroke: '#ffffff',
|
| 41 |
+
pathStrokeWidth: '2', // Plus épais pour les flèches
|
| 42 |
+
textColor: '#ffffff',
|
| 43 |
+
linkColor: '#ffffff' // Couleur spécifique pour les liens
|
| 44 |
+
} : {
|
| 45 |
+
nodeFill: '#ffffff',
|
| 46 |
+
nodeStroke: '#000000', // Noir pur pour plus de contraste
|
| 47 |
+
nodeStrokeWidth: '2',
|
| 48 |
+
clusterFill: '#f9f9f9',
|
| 49 |
+
clusterStroke: '#000000', // Noir pur
|
| 50 |
+
clusterStrokeWidth: '2',
|
| 51 |
+
pathStroke: '#000000', // Noir pur pour les flèches
|
| 52 |
+
pathStrokeWidth: '2',
|
| 53 |
+
textColor: '#000000', // Noir pur
|
| 54 |
+
linkColor: '#000000' // Noir pur pour les liens
|
| 55 |
+
};
|
| 56 |
|
| 57 |
+
// Appliquer border-radius aux rectangles
|
| 58 |
+
const rects = svgElement.querySelectorAll('rect:not(.flowchart-link), .node rect, .nodeLabel rect');
|
| 59 |
+
rects.forEach(rect => {
|
| 60 |
+
rect.setAttribute('rx', '8');
|
| 61 |
+
rect.setAttribute('ry', '8');
|
| 62 |
rect.setAttribute('fill', colors.nodeFill);
|
| 63 |
rect.setAttribute('stroke', colors.nodeStroke);
|
| 64 |
rect.setAttribute('stroke-width', colors.nodeStrokeWidth);
|
| 65 |
+
});
|
|
|
|
| 66 |
|
| 67 |
+
// Appliquer border-radius et couleurs aux clusters
|
| 68 |
+
const clusterRects = svgElement.querySelectorAll('.cluster rect');
|
| 69 |
+
clusterRects.forEach(rect => {
|
| 70 |
+
rect.setAttribute('rx', '8');
|
| 71 |
+
rect.setAttribute('ry', '8');
|
| 72 |
rect.setAttribute('fill', colors.clusterFill);
|
| 73 |
rect.setAttribute('stroke', colors.clusterStroke);
|
| 74 |
rect.setAttribute('stroke-width', colors.clusterStrokeWidth);
|
| 75 |
+
});
|
|
|
|
| 76 |
|
| 77 |
+
// Appliquer les couleurs aux nœuds
|
| 78 |
+
const nodes = svgElement.querySelectorAll('.node');
|
| 79 |
+
nodes.forEach(node => {
|
| 80 |
+
const rect = node.querySelector('rect');
|
| 81 |
+
if (rect) {
|
| 82 |
+
rect.setAttribute('fill', colors.nodeFill);
|
| 83 |
+
rect.setAttribute('stroke', colors.nodeStroke);
|
| 84 |
+
rect.setAttribute('stroke-width', colors.nodeStrokeWidth);
|
| 85 |
+
}
|
| 86 |
+
});
|
| 87 |
|
| 88 |
+
// Appliquer les couleurs aux clusters
|
| 89 |
+
const clusters = svgElement.querySelectorAll('.cluster');
|
| 90 |
+
clusters.forEach(cluster => {
|
| 91 |
+
const rect = cluster.querySelector('rect');
|
| 92 |
+
if (rect) {
|
| 93 |
+
rect.setAttribute('fill', colors.clusterFill);
|
| 94 |
+
rect.setAttribute('stroke', colors.clusterStroke);
|
| 95 |
+
rect.setAttribute('stroke-width', colors.clusterStrokeWidth);
|
| 96 |
+
}
|
| 97 |
+
});
|
| 98 |
|
| 99 |
+
// Appliquer les couleurs aux chemins et flèches
|
| 100 |
+
const paths = svgElement.querySelectorAll('.edgePath, path, .flowchart-link');
|
| 101 |
+
paths.forEach(path => {
|
| 102 |
+
path.setAttribute('stroke', colors.pathStroke);
|
| 103 |
+
path.setAttribute('stroke-width', colors.pathStrokeWidth);
|
| 104 |
+
});
|
| 105 |
|
| 106 |
+
// Appliquer les couleurs aux liens spécifiquement
|
| 107 |
+
const links = svgElement.querySelectorAll('.flowchart-link, .edgeLabel');
|
| 108 |
+
links.forEach(link => {
|
| 109 |
+
link.setAttribute('stroke', colors.linkColor);
|
| 110 |
+
link.setAttribute('fill', colors.linkColor);
|
| 111 |
+
});
|
| 112 |
|
| 113 |
+
// Appliquer les couleurs au texte
|
| 114 |
+
const textElements = svgElement.querySelectorAll('text, .nodeLabel text, .edgeLabel text');
|
| 115 |
+
textElements.forEach(text => {
|
| 116 |
+
text.setAttribute('fill', colors.textColor);
|
| 117 |
+
});
|
| 118 |
+
|
| 119 |
+
// Appliquer les couleurs aux marqueurs de flèches
|
| 120 |
+
const markers = svgElement.querySelectorAll('marker, marker path');
|
| 121 |
+
markers.forEach(marker => {
|
| 122 |
+
marker.setAttribute('fill', colors.pathStroke);
|
| 123 |
+
marker.setAttribute('stroke', colors.pathStroke);
|
| 124 |
+
});
|
| 125 |
|
| 126 |
+
console.log(`🎨 Applied Mermaid styles: ${rects.length} rects, ${clusters.length} clusters, ${paths.length} paths, ${textElements.length} text elements`);
|
| 127 |
+
|
| 128 |
+
} catch (error) {
|
| 129 |
+
console.error('❌ Error applying styles to SVG:', error);
|
| 130 |
+
}
|
| 131 |
}
|
|
|
|
| 132 |
|
| 133 |
+
// Fonction pour extraire les styles CSS appliqués au diagramme Mermaid
|
| 134 |
+
function getComputedStylesForMermaid(mermaidElement) {
|
| 135 |
+
try {
|
| 136 |
+
// Styles CSS essentiels pour Mermaid (hardcodés pour éviter les problèmes CORS)
|
| 137 |
+
const essentialStyles = `
|
| 138 |
.mermaid rect:not(.flowchart-link),
|
| 139 |
.mermaid .node rect,
|
| 140 |
.mermaid .nodeLabel rect {
|
|
|
|
| 170 |
}
|
| 171 |
`;
|
| 172 |
|
| 173 |
+
console.log(`🎨 Using essential CSS styles for Mermaid`);
|
| 174 |
+
return essentialStyles;
|
| 175 |
|
| 176 |
+
} catch (error) {
|
| 177 |
+
console.error('❌ Error getting CSS styles:', error);
|
| 178 |
+
return null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 179 |
}
|
| 180 |
+
}
|
| 181 |
|
| 182 |
+
// Fonction pour forcer Mermaid à se re-rendre avec le bon thème
|
| 183 |
+
function forceMermaidThemeUpdate(mermaidElement) {
|
| 184 |
+
try {
|
| 185 |
+
// Obtenir le thème actuel
|
| 186 |
+
const currentTheme = document.documentElement.getAttribute("data-theme");
|
| 187 |
+
console.log(`🎨 Forcing Mermaid theme update to: ${currentTheme}`);
|
| 188 |
+
|
| 189 |
+
// Trouver le diagramme Mermaid original
|
| 190 |
+
const mermaidCode = mermaidElement.textContent || mermaidElement.innerText;
|
| 191 |
+
if (!mermaidCode) {
|
| 192 |
+
console.log(`❌ No Mermaid code found to re-render`);
|
| 193 |
+
return false;
|
| 194 |
+
}
|
| 195 |
|
| 196 |
+
// Forcer le re-rendu de Mermaid avec le nouveau thème
|
| 197 |
+
if (window.mermaid) {
|
| 198 |
+
// Utiliser l'API Mermaid pour re-rendre avec le bon thème
|
| 199 |
+
const config = {
|
| 200 |
+
theme: currentTheme === 'dark' ? 'dark' : 'neutral',
|
| 201 |
+
autoTheme: true
|
| 202 |
+
};
|
| 203 |
|
| 204 |
+
console.log(`🔄 Re-rendering Mermaid with config:`, config);
|
|
|
|
| 205 |
|
| 206 |
+
// Re-rendre le diagramme
|
| 207 |
+
mermaid.init(undefined, mermaidElement);
|
| 208 |
+
|
| 209 |
+
return true;
|
| 210 |
+
} else {
|
| 211 |
+
console.log(`❌ Mermaid API not available`);
|
| 212 |
+
return false;
|
| 213 |
+
}
|
| 214 |
+
} catch (error) {
|
| 215 |
+
console.error(`❌ Error forcing Mermaid theme update:`, error);
|
| 216 |
return false;
|
| 217 |
}
|
|
|
|
|
|
|
|
|
|
| 218 |
}
|
|
|
|
| 219 |
|
| 220 |
+
// Fonction pour convertir SVG en image en préservant EXACTEMENT les dimensions
|
| 221 |
+
function convertSvgToImagePreservingDimensions(svgElement, wrapper, originalMermaid) {
|
| 222 |
+
console.log(`🔄 Converting SVG to image with EXACT dimension preservation`);
|
| 223 |
|
| 224 |
+
try {
|
| 225 |
+
// Obtenir les dimensions EXACTES du wrapper actuel
|
| 226 |
+
const wrapperRect = wrapper.getBoundingClientRect();
|
| 227 |
+
const wrapperWidth = Math.round(wrapperRect.width);
|
| 228 |
+
const wrapperHeight = Math.round(wrapperRect.height);
|
| 229 |
|
| 230 |
+
console.log(`📏 Wrapper dimensions:`, { width: wrapperWidth, height: wrapperHeight });
|
| 231 |
|
| 232 |
+
// Cloner le SVG
|
| 233 |
+
const clonedSvg = svgElement.cloneNode(true);
|
| 234 |
|
| 235 |
+
// Appliquer les styles Mermaid
|
| 236 |
+
applyMermaidStylesToSvg(clonedSvg);
|
| 237 |
|
| 238 |
+
// Créer une image PLUS GRANDE pour permettre un vrai zoom (2x la taille)
|
| 239 |
+
const zoomFactor = 2;
|
| 240 |
+
const imageWidth = wrapperWidth * zoomFactor;
|
| 241 |
+
const imageHeight = wrapperHeight * zoomFactor;
|
| 242 |
|
| 243 |
+
// Forcer les dimensions plus grandes sur le SVG
|
| 244 |
+
clonedSvg.setAttribute('width', imageWidth);
|
| 245 |
+
clonedSvg.setAttribute('height', imageHeight);
|
| 246 |
+
clonedSvg.style.width = `${imageWidth}px`;
|
| 247 |
+
clonedSvg.style.height = `${imageHeight}px`;
|
| 248 |
|
| 249 |
+
console.log(`🔍 Creating zoomable image:`, {
|
| 250 |
+
original: { width: wrapperWidth, height: wrapperHeight },
|
| 251 |
+
zoomed: { width: imageWidth, height: imageHeight },
|
| 252 |
+
factor: zoomFactor
|
| 253 |
+
});
|
| 254 |
|
| 255 |
+
// Créer une URL data pour le SVG
|
| 256 |
+
const svgData = new XMLSerializer().serializeToString(clonedSvg);
|
| 257 |
+
const svgBlob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' });
|
| 258 |
+
const svgUrl = URL.createObjectURL(svgBlob);
|
| 259 |
|
| 260 |
+
// Créer un élément img avec les dimensions du wrapper (affichage normal)
|
| 261 |
+
const imgElement = document.createElement('img');
|
| 262 |
+
imgElement.src = svgUrl;
|
| 263 |
+
imgElement.style.width = `${wrapperWidth}px`; // Affichage normal
|
| 264 |
+
imgElement.style.height = `${wrapperHeight}px`; // Affichage normal
|
| 265 |
+
imgElement.style.display = 'block';
|
| 266 |
+
imgElement.setAttribute('data-zoomable', '1');
|
| 267 |
+
imgElement.classList.add('mermaid-zoom-image'); // Classe spécifique pour Mermaid
|
| 268 |
|
| 269 |
+
// Ajouter les attributs pour medium-zoom (dimensions réelles de l'image)
|
| 270 |
+
imgElement.setAttribute('data-zoom-width', imageWidth);
|
| 271 |
+
imgElement.setAttribute('data-zoom-height', imageHeight);
|
| 272 |
|
| 273 |
+
console.log(`🖼️ Image created with exact dimensions:`, { width: wrapperWidth, height: wrapperHeight });
|
| 274 |
|
| 275 |
+
// Remplacer le contenu du wrapper par l'image
|
| 276 |
+
wrapper.innerHTML = '';
|
| 277 |
+
wrapper.appendChild(imgElement);
|
| 278 |
|
| 279 |
+
// Retirer la classe "converting" pour restaurer l'opacité normale
|
| 280 |
+
wrapper.classList.remove("converting");
|
| 281 |
+
|
| 282 |
+
// Attendre que l'image soit chargée puis initialiser medium-zoom
|
| 283 |
+
imgElement.onload = () => {
|
| 284 |
+
console.log(`✅ Image loaded, initializing REAL medium-zoom`);
|
| 285 |
+
|
| 286 |
+
const isDark = document.documentElement.getAttribute("data-theme") === "dark";
|
| 287 |
+
const background = isDark ? "rgba(0,0,0,.9)" : "rgba(0,0,0,.85)";
|
| 288 |
+
|
| 289 |
+
// Utiliser VRAI medium-zoom
|
| 290 |
+
const zoomInstance = window.mediumZoom(imgElement, {
|
| 291 |
+
background,
|
| 292 |
+
margin: 24,
|
| 293 |
+
scrollOffset: 0,
|
| 294 |
+
});
|
| 295 |
+
|
| 296 |
+
console.log(`🎉 REAL medium-zoom initialized with exact dimensions!`);
|
| 297 |
+
|
| 298 |
+
// Forcer les bonnes couleurs en JavaScript (contournement du CSS global)
|
| 299 |
+
const forceCorrectColors = () => {
|
| 300 |
+
// Trouver l'image zoomée dans le DOM
|
| 301 |
+
const zoomedImage = document.querySelector('.medium-zoom-image--opened');
|
| 302 |
+
if (zoomedImage && zoomedImage.classList.contains('mermaid-zoom-image')) {
|
| 303 |
+
console.log(`🎨 Forcing correct colors for Mermaid zoom image`);
|
| 304 |
+
zoomedImage.style.filter = 'none';
|
| 305 |
+
zoomedImage.style.setProperty('filter', 'none', 'important');
|
| 306 |
+
}
|
| 307 |
+
|
| 308 |
+
// Forcer les z-index élevés
|
| 309 |
+
const overlay = document.querySelector('.medium-zoom-overlay');
|
| 310 |
+
if (overlay) {
|
| 311 |
+
console.log(`🔝 Forcing high z-index for overlay`);
|
| 312 |
+
overlay.style.zIndex = '9999999';
|
| 313 |
+
overlay.style.setProperty('z-index', '9999999', 'important');
|
| 314 |
+
}
|
| 315 |
+
|
| 316 |
+
if (zoomedImage) {
|
| 317 |
+
console.log(`🔝 Forcing high z-index for zoomed image`);
|
| 318 |
+
zoomedImage.style.zIndex = '10000000';
|
| 319 |
+
zoomedImage.style.setProperty('z-index', '10000000', 'important');
|
| 320 |
+
}
|
| 321 |
+
};
|
| 322 |
+
|
| 323 |
+
// Écouter les événements de zoom pour appliquer les bonnes couleurs
|
| 324 |
+
imgElement.addEventListener('zoom:open', forceCorrectColors);
|
| 325 |
+
imgElement.addEventListener('zoom:opened', forceCorrectColors);
|
| 326 |
+
|
| 327 |
+
// Observer pour les changements de thème
|
| 328 |
+
const themeObserver = new MutationObserver(() => {
|
| 329 |
+
console.log(`🎨 Theme changed, forcing Mermaid re-render with new theme`);
|
| 330 |
|
| 331 |
+
// Forcer Mermaid à se re-rendre avec le nouveau thème
|
| 332 |
+
const currentTheme = document.documentElement.getAttribute("data-theme");
|
| 333 |
+
console.log(`🎨 Current theme: ${currentTheme}`);
|
| 334 |
+
|
| 335 |
+
// Attendre un peu pour que Mermaid détecte le changement de thème
|
| 336 |
+
setTimeout(() => {
|
| 337 |
+
// Forcer Mermaid à se re-rendre avec le nouveau thème
|
| 338 |
+
const themeUpdated = forceMermaidThemeUpdate(originalMermaid);
|
| 339 |
+
|
| 340 |
+
if (themeUpdated) {
|
| 341 |
+
// Attendre que le re-rendu soit terminé
|
| 342 |
+
setTimeout(() => {
|
| 343 |
+
// Re-obtenir le SVG mis à jour
|
| 344 |
+
const updatedSvgElement = originalMermaid.querySelector("svg");
|
| 345 |
+
if (updatedSvgElement) {
|
| 346 |
+
console.log(`🔄 Re-converting with updated SVG for theme: ${currentTheme}`);
|
| 347 |
+
convertSvgToImagePreservingDimensions(updatedSvgElement, wrapper, originalMermaid);
|
| 348 |
+
} else {
|
| 349 |
+
console.log(`❌ No updated SVG found after theme change`);
|
| 350 |
+
}
|
| 351 |
+
}, 200); // Délai pour laisser Mermaid finir le re-rendu
|
| 352 |
+
} else {
|
| 353 |
+
console.log(`❌ Failed to update Mermaid theme, using current SVG`);
|
| 354 |
+
// Fallback: utiliser le SVG actuel même s'il n'est pas mis à jour
|
| 355 |
+
const currentSvgElement = originalMermaid.querySelector("svg");
|
| 356 |
+
if (currentSvgElement) {
|
| 357 |
+
convertSvgToImagePreservingDimensions(currentSvgElement, wrapper, originalMermaid);
|
| 358 |
+
}
|
| 359 |
+
}
|
| 360 |
+
}, 100); // Petit délai pour laisser Mermaid se mettre à jour
|
| 361 |
+
});
|
| 362 |
|
| 363 |
+
themeObserver.observe(document.documentElement, {
|
| 364 |
+
attributes: true,
|
| 365 |
+
attributeFilter: ["data-theme"],
|
| 366 |
+
});
|
| 367 |
+
|
| 368 |
+
wrapper._themeObserver = themeObserver;
|
| 369 |
+
};
|
| 370 |
+
|
| 371 |
+
imgElement.onerror = (error) => {
|
| 372 |
+
console.error(`❌ Error loading SVG as image:`, error);
|
| 373 |
+
// Fallback: utiliser le zoom custom
|
| 374 |
+
wrapper.innerHTML = '';
|
| 375 |
+
wrapper.appendChild(originalMermaid);
|
| 376 |
+
wrapper.classList.remove("converting");
|
| 377 |
+
wrapper.addEventListener("click", (e) => {
|
| 378 |
+
e.preventDefault();
|
| 379 |
+
e.stopPropagation();
|
| 380 |
+
openMermaidZoom(wrapper, originalMermaid);
|
| 381 |
+
});
|
| 382 |
+
};
|
| 383 |
+
|
| 384 |
+
} catch (error) {
|
| 385 |
+
console.error("❌ Error converting SVG to image:", error);
|
| 386 |
+
// Fallback: utiliser le zoom custom
|
| 387 |
+
wrapper.classList.remove("converting");
|
| 388 |
+
wrapper.addEventListener("click", (e) => {
|
| 389 |
+
e.preventDefault();
|
| 390 |
+
e.stopPropagation();
|
| 391 |
+
openMermaidZoom(wrapper, originalMermaid);
|
| 392 |
+
});
|
| 393 |
+
}
|
| 394 |
+
}
|
| 395 |
+
|
| 396 |
+
// Fonction pour initialiser medium-zoom directement sur le SVG
|
| 397 |
+
function initializeMediumZoomOnSvg(svgElement, wrapper, originalMermaid) {
|
| 398 |
+
try {
|
| 399 |
+
// S'assurer que le SVG a des dimensions fixes pour éviter le flicker
|
| 400 |
+
const bbox = svgElement.getBBox();
|
| 401 |
+
const width = bbox.width || svgElement.clientWidth || 800;
|
| 402 |
+
const height = bbox.height || svgElement.clientHeight || 600;
|
| 403 |
+
|
| 404 |
+
// Fixer les dimensions du SVG pour éviter les changements
|
| 405 |
+
svgElement.setAttribute('width', width);
|
| 406 |
+
svgElement.setAttribute('height', height);
|
| 407 |
+
svgElement.style.width = '100%';
|
| 408 |
+
svgElement.style.height = 'auto';
|
| 409 |
+
svgElement.style.display = 'block';
|
| 410 |
+
|
| 411 |
+
// Retirer la classe "converting" pour restaurer l'opacité normale
|
| 412 |
+
wrapper.classList.remove("converting");
|
| 413 |
+
|
| 414 |
+
console.log(`📏 SVG dimensions fixed:`, { width, height });
|
| 415 |
+
|
| 416 |
+
// Initialiser medium-zoom directement sur le wrapper SVG
|
| 417 |
const isDark = document.documentElement.getAttribute("data-theme") === "dark";
|
| 418 |
const background = isDark ? "rgba(0,0,0,.9)" : "rgba(0,0,0,.85)";
|
| 419 |
|
| 420 |
+
// Utiliser medium-zoom sur le wrapper qui contient le SVG
|
| 421 |
+
window.mediumZoom(wrapper, {
|
| 422 |
background,
|
| 423 |
margin: 24,
|
| 424 |
scrollOffset: 0,
|
| 425 |
});
|
| 426 |
|
| 427 |
+
console.log(`🎉 Medium-zoom initialized directly on SVG wrapper!`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 428 |
|
| 429 |
// Observer pour les changements de thème
|
| 430 |
const themeObserver = new MutationObserver(() => {
|
| 431 |
+
console.log(`🎨 Theme changed, updating medium-zoom background`);
|
| 432 |
+
// Re-initialiser medium-zoom avec le nouveau thème
|
| 433 |
+
initializeMediumZoomOnSvg(svgElement, wrapper, originalMermaid);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 434 |
});
|
| 435 |
|
| 436 |
themeObserver.observe(document.documentElement, {
|
|
|
|
| 438 |
attributeFilter: ["data-theme"],
|
| 439 |
});
|
| 440 |
|
| 441 |
+
// Stocker l'observer pour le nettoyer plus tard
|
| 442 |
wrapper._themeObserver = themeObserver;
|
|
|
|
| 443 |
|
| 444 |
+
} catch (error) {
|
| 445 |
+
console.error("❌ Error initializing medium-zoom on SVG:", error);
|
| 446 |
// Fallback: utiliser le zoom custom
|
|
|
|
|
|
|
| 447 |
wrapper.classList.remove("converting");
|
| 448 |
wrapper.addEventListener("click", (e) => {
|
| 449 |
e.preventDefault();
|
| 450 |
e.stopPropagation();
|
| 451 |
openMermaidZoom(wrapper, originalMermaid);
|
| 452 |
});
|
| 453 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 454 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 455 |
|
| 456 |
+
// Fonction pour attendre que Mermaid soit stable (évite le flicker)
|
| 457 |
+
function waitForMermaidStable(mermaidEl, wrapper, index) {
|
| 458 |
+
// Approche simplifiée : attendre un délai fixe puis convertir
|
| 459 |
+
console.log(`⏳ Waiting for Mermaid ${index} to stabilize...`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 460 |
|
| 461 |
+
setTimeout(() => {
|
| 462 |
+
const svgElement = mermaidEl.querySelector("svg");
|
| 463 |
+
if (svgElement) {
|
| 464 |
+
console.log(`🎯 Converting Mermaid ${index} to image`);
|
| 465 |
+
convertSvgToImageForMediumZoom(svgElement, wrapper, mermaidEl);
|
| 466 |
+
} else {
|
| 467 |
+
console.log(`❌ No SVG found in Mermaid element ${index}`);
|
| 468 |
+
}
|
| 469 |
+
}, 2000); // Attendre 2 secondes puis convertir
|
|
|
|
|
|
|
|
|
|
| 470 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 471 |
|
| 472 |
+
// Fonction pour convertir SVG en image SANS canvas (pour éviter l'erreur de sécurité)
|
| 473 |
+
function convertSvgToImageForMediumZoom(svgElement, wrapper, originalMermaid) {
|
| 474 |
+
console.log(`🔄 Converting SVG to image for REAL medium-zoom`);
|
| 475 |
|
| 476 |
+
try {
|
| 477 |
+
// Cloner le SVG
|
| 478 |
+
const clonedSvg = svgElement.cloneNode(true);
|
| 479 |
|
| 480 |
+
// Appliquer directement les styles SVG pour préserver l'apparence
|
| 481 |
+
applyMermaidStylesToSvg(clonedSvg);
|
| 482 |
|
| 483 |
+
// S'assurer que le SVG a des dimensions
|
| 484 |
+
const bbox = clonedSvg.getBBox();
|
| 485 |
+
const width = bbox.width || clonedSvg.clientWidth || 800;
|
| 486 |
+
const height = bbox.height || clonedSvg.clientHeight || 600;
|
| 487 |
|
| 488 |
+
console.log(`📏 SVG dimensions:`, { width, height });
|
| 489 |
|
| 490 |
+
// Créer une image directement à partir du SVG (sans canvas)
|
| 491 |
+
const svgData = new XMLSerializer().serializeToString(clonedSvg);
|
| 492 |
|
| 493 |
+
// Ajouter les dimensions au SVG
|
| 494 |
+
clonedSvg.setAttribute("width", width);
|
| 495 |
+
clonedSvg.setAttribute("height", height);
|
| 496 |
+
const svgWithDimensions = new XMLSerializer().serializeToString(clonedSvg);
|
| 497 |
|
| 498 |
+
// Créer une URL data pour le SVG
|
| 499 |
+
const svgBlob = new Blob([svgWithDimensions], {
|
| 500 |
+
type: "image/svg+xml;charset=utf-8",
|
| 501 |
+
});
|
| 502 |
+
const svgUrl = URL.createObjectURL(svgBlob);
|
| 503 |
|
| 504 |
+
// Créer un élément img avec le SVG
|
| 505 |
+
const imgElement = document.createElement("img");
|
| 506 |
+
imgElement.src = svgUrl;
|
| 507 |
+
imgElement.style.width = "100%";
|
| 508 |
+
imgElement.style.height = "auto";
|
| 509 |
+
imgElement.style.display = "block";
|
| 510 |
+
imgElement.setAttribute("data-zoomable", "1");
|
| 511 |
|
| 512 |
+
// Remplacer le contenu du wrapper par l'image
|
| 513 |
+
wrapper.innerHTML = "";
|
| 514 |
+
wrapper.appendChild(imgElement);
|
| 515 |
|
| 516 |
+
// Retirer la classe "converting" pour restaurer l'opacité normale
|
| 517 |
+
wrapper.classList.remove("converting");
|
| 518 |
|
| 519 |
+
console.log(`🖼️ SVG converted to img element`);
|
| 520 |
|
| 521 |
+
// Attendre que l'image soit chargée puis initialiser medium-zoom
|
| 522 |
+
imgElement.onload = () => {
|
| 523 |
+
console.log(`✅ Image loaded, initializing REAL medium-zoom`);
|
| 524 |
|
| 525 |
+
const isDark = document.documentElement.getAttribute("data-theme") === "dark";
|
| 526 |
+
const background = isDark ? "rgba(0,0,0,.9)" : "rgba(0,0,0,.85)";
|
| 527 |
|
| 528 |
+
// Utiliser VRAI medium-zoom
|
| 529 |
+
window.mediumZoom(imgElement, {
|
| 530 |
+
background,
|
| 531 |
+
margin: 24,
|
| 532 |
+
scrollOffset: 0,
|
| 533 |
+
});
|
| 534 |
|
| 535 |
+
console.log(`🎉 REAL medium-zoom initialized on Mermaid!`);
|
| 536 |
|
| 537 |
+
// Observer pour les changements de thème - reconvertir l'image
|
| 538 |
+
const themeObserver = new MutationObserver(() => {
|
| 539 |
+
console.log(`🎨 Theme changed, reconverting Mermaid image`);
|
| 540 |
+
// Reconverter l'image avec le nouveau thème
|
| 541 |
+
convertSvgToImageForMediumZoom(svgElement, wrapper, originalMermaid);
|
| 542 |
+
});
|
| 543 |
|
| 544 |
+
themeObserver.observe(document.documentElement, {
|
| 545 |
+
attributes: true,
|
| 546 |
+
attributeFilter: ["data-theme"],
|
| 547 |
+
});
|
| 548 |
|
| 549 |
+
// Stocker l'observer pour le nettoyer plus tard
|
| 550 |
+
wrapper._themeObserver = themeObserver;
|
| 551 |
+
};
|
| 552 |
+
|
| 553 |
+
imgElement.onerror = (error) => {
|
| 554 |
+
console.error(`❌ Error loading SVG as image:`, error);
|
| 555 |
+
// Fallback: utiliser le zoom custom
|
| 556 |
+
wrapper.innerHTML = "";
|
| 557 |
+
wrapper.appendChild(originalMermaid);
|
| 558 |
+
wrapper.addEventListener("click", (e) => {
|
| 559 |
+
e.preventDefault();
|
| 560 |
+
e.stopPropagation();
|
| 561 |
+
openMermaidZoom(wrapper, originalMermaid);
|
| 562 |
+
});
|
| 563 |
+
};
|
| 564 |
|
| 565 |
+
} catch (error) {
|
| 566 |
+
console.error("❌ Error converting SVG to image:", error);
|
| 567 |
// Fallback: utiliser le zoom custom
|
|
|
|
|
|
|
| 568 |
wrapper.addEventListener("click", (e) => {
|
| 569 |
e.preventDefault();
|
| 570 |
e.stopPropagation();
|
| 571 |
openMermaidZoom(wrapper, originalMermaid);
|
| 572 |
});
|
| 573 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 574 |
}
|
| 575 |
+
|
| 576 |
+
// Fonction pour ouvrir le zoom custom des diagrammes Mermaid (comme medium-zoom)
|
| 577 |
+
function openMermaidZoom(wrapper, mermaidElement) {
|
| 578 |
+
// Créer l'overlay (comme medium-zoom)
|
| 579 |
+
const overlay = document.createElement("div");
|
| 580 |
+
overlay.className = "medium-zoom-overlay";
|
| 581 |
+
overlay.style.cssText = `
|
|
|
|
| 582 |
position: fixed;
|
| 583 |
top: 0;
|
| 584 |
left: 0;
|
|
|
|
| 594 |
transition: opacity 0.3s ease;
|
| 595 |
`;
|
| 596 |
|
| 597 |
+
// Créer l'image zoomée (comme medium-zoom)
|
| 598 |
+
const zoomImage = document.createElement("div");
|
| 599 |
+
zoomImage.className = "medium-zoom-image";
|
| 600 |
+
zoomImage.style.cssText = `
|
| 601 |
max-width: 90%;
|
| 602 |
max-height: 90%;
|
| 603 |
background: white;
|
|
|
|
| 609 |
overflow: auto;
|
| 610 |
`;
|
| 611 |
|
| 612 |
+
// Cloner le diagramme Mermaid
|
| 613 |
+
const clonedMermaid = mermaidElement.cloneNode(true);
|
| 614 |
+
clonedMermaid.style.cssText = `
|
| 615 |
width: 100%;
|
| 616 |
height: auto;
|
| 617 |
max-width: none;
|
| 618 |
display: block;
|
| 619 |
`;
|
| 620 |
|
| 621 |
+
zoomImage.appendChild(clonedMermaid);
|
| 622 |
+
overlay.appendChild(zoomImage);
|
| 623 |
|
| 624 |
+
// Ajouter l'overlay au body
|
| 625 |
+
document.body.appendChild(overlay);
|
| 626 |
|
| 627 |
+
// Animation d'ouverture (comme medium-zoom)
|
| 628 |
+
requestAnimationFrame(() => {
|
| 629 |
+
overlay.style.opacity = "1";
|
| 630 |
+
zoomImage.style.transform = "scale(1)";
|
| 631 |
+
});
|
| 632 |
|
| 633 |
+
// Fermer au clic sur l'overlay
|
| 634 |
+
overlay.addEventListener("click", (e) => {
|
| 635 |
+
if (e.target === overlay) {
|
| 636 |
+
closeMermaidZoom(overlay);
|
| 637 |
+
}
|
| 638 |
+
});
|
| 639 |
|
| 640 |
+
// Fermer avec Escape
|
| 641 |
+
const handleEscape = (e) => {
|
| 642 |
+
if (e.key === "Escape") {
|
| 643 |
+
closeMermaidZoom(overlay);
|
| 644 |
+
document.removeEventListener("keydown", handleEscape);
|
| 645 |
+
}
|
| 646 |
+
};
|
| 647 |
+
document.addEventListener("keydown", handleEscape);
|
| 648 |
+
|
| 649 |
+
// Fermer au scroll (comme medium-zoom)
|
| 650 |
+
const handleScroll = () => {
|
| 651 |
closeMermaidZoom(overlay);
|
| 652 |
+
};
|
| 653 |
+
window.addEventListener("wheel", handleScroll, { passive: true });
|
| 654 |
+
window.addEventListener("touchmove", handleScroll, { passive: true });
|
| 655 |
+
window.addEventListener("scroll", handleScroll, { passive: true });
|
| 656 |
|
| 657 |
+
// Stocker les handlers pour les nettoyer
|
| 658 |
+
overlay._handlers = { handleEscape, handleScroll };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 659 |
}
|
| 660 |
|
| 661 |
+
function closeMermaidZoom(overlay) {
|
| 662 |
+
// Animation de fermeture
|
| 663 |
+
overlay.style.opacity = "0";
|
| 664 |
+
const zoomImage = overlay.querySelector(".medium-zoom-image");
|
| 665 |
+
if (zoomImage) {
|
| 666 |
+
zoomImage.style.transform = "scale(0.8)";
|
| 667 |
+
}
|
| 668 |
|
| 669 |
+
// Nettoyer les event listeners
|
| 670 |
+
if (overlay._handlers) {
|
| 671 |
+
document.removeEventListener("keydown", overlay._handlers.handleEscape);
|
| 672 |
+
window.removeEventListener("wheel", overlay._handlers.handleScroll);
|
| 673 |
+
window.removeEventListener("touchmove", overlay._handlers.handleScroll);
|
| 674 |
+
window.removeEventListener("scroll", overlay._handlers.handleScroll);
|
| 675 |
}
|
| 676 |
+
|
| 677 |
+
// Supprimer l'overlay après l'animation
|
| 678 |
+
setTimeout(() => {
|
| 679 |
+
if (document.body.contains(overlay)) {
|
| 680 |
+
document.body.removeChild(overlay);
|
| 681 |
+
}
|
| 682 |
+
}, 300);
|
|
|
|
| 683 |
}
|
| 684 |
+
|
| 685 |
+
// Fonction pour nettoyer les observers
|
| 686 |
+
function cleanupMermaidObservers(wrapper) {
|
| 687 |
+
if (wrapper._themeObserver) {
|
| 688 |
+
wrapper._themeObserver.disconnect();
|
| 689 |
+
wrapper._themeObserver = null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 690 |
}
|
| 691 |
+
}
|
| 692 |
|
| 693 |
+
// Fonction principale pour initialiser le zoom Mermaid
|
| 694 |
+
function setupMermaidZoom() {
|
| 695 |
+
console.log(`🎯 setupMermaidZoom called`);
|
| 696 |
+
const mermaidElements = document.querySelectorAll(".mermaid");
|
| 697 |
+
console.log(`🔍 Found ${mermaidElements.length} Mermaid elements`);
|
| 698 |
+
|
| 699 |
+
let processedCount = 0;
|
| 700 |
+
mermaidElements.forEach((mermaidEl, index) => {
|
| 701 |
+
// Vérifier si déjà wrappé
|
| 702 |
+
if (
|
| 703 |
+
mermaidEl.parentElement &&
|
| 704 |
+
mermaidEl.parentElement.classList.contains("mermaid-zoom-wrapper")
|
| 705 |
+
) {
|
| 706 |
+
console.log(`📦 Mermaid ${index} already wrapped`);
|
| 707 |
+
processedCount++;
|
| 708 |
+
return;
|
| 709 |
+
}
|
| 710 |
|
| 711 |
+
console.log(`📦 Wrapping Mermaid element ${index}`);
|
|
|
|
|
|
|
|
|
|
| 712 |
|
| 713 |
+
// Créer le wrapper
|
| 714 |
+
const wrapper = document.createElement("div");
|
| 715 |
+
wrapper.className = "mermaid-zoom-wrapper";
|
| 716 |
+
wrapper.setAttribute("data-zoomable", "1");
|
| 717 |
|
| 718 |
+
// Insérer le wrapper avant l'élément Mermaid
|
| 719 |
+
mermaidEl.parentNode.insertBefore(wrapper, mermaidEl);
|
| 720 |
|
| 721 |
+
// Déplacer l'élément Mermaid dans le wrapper
|
| 722 |
+
wrapper.appendChild(mermaidEl);
|
|
|
|
|
|
|
| 723 |
|
| 724 |
+
// S'assurer que le wrapper a des dimensions
|
| 725 |
+
wrapper.style.display = "block";
|
| 726 |
+
wrapper.style.width = "100%";
|
| 727 |
+
wrapper.style.maxWidth = "100%";
|
| 728 |
|
| 729 |
+
// Ajouter la classe "converting" pour masquer le flicker
|
| 730 |
+
wrapper.classList.add("converting");
|
| 731 |
|
| 732 |
+
// Convertir SVG en image pour utiliser VRAI medium-zoom
|
| 733 |
+
console.log(`✅ Converting Mermaid to image for REAL medium-zoom`);
|
| 734 |
|
| 735 |
+
// Utiliser le VRAI medium-zoom avec conversion SVG → image SANS flicker
|
| 736 |
+
console.log(`✅ Setting up REAL medium-zoom for Mermaid ${index}`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 737 |
|
| 738 |
+
// Attendre que Mermaid soit stable puis convertir avec préservation des dimensions
|
| 739 |
+
setTimeout(() => {
|
| 740 |
+
const svgElement = mermaidEl.querySelector("svg");
|
| 741 |
+
if (svgElement) {
|
| 742 |
+
console.log(`🎯 Converting SVG to image with dimension preservation`);
|
| 743 |
+
convertSvgToImagePreservingDimensions(svgElement, wrapper, mermaidEl);
|
| 744 |
+
} else {
|
| 745 |
+
console.log(`❌ No SVG found in Mermaid element ${index}`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 746 |
}
|
| 747 |
+
}, 1000); // Réduit de 2000ms à 1000ms pour être plus rapide
|
| 748 |
+
});
|
| 749 |
|
| 750 |
+
console.log(`✅ Processed ${processedCount} already wrapped, ${mermaidElements.length - processedCount} new diagrams`);
|
| 751 |
+
}
|
| 752 |
+
|
| 753 |
+
// Observer global pour forcer les bonnes couleurs et z-index sur les images Mermaid zoomées
|
| 754 |
+
function setupGlobalMermaidColorFix() {
|
| 755 |
+
const observer = new MutationObserver((mutations) => {
|
| 756 |
+
mutations.forEach((mutation) => {
|
| 757 |
+
if (mutation.type === 'childList') {
|
| 758 |
+
// Vérifier si une image Mermaid est zoomée
|
| 759 |
+
const zoomedMermaidImage = document.querySelector('.medium-zoom-image--opened.mermaid-zoom-image');
|
| 760 |
+
if (zoomedMermaidImage) {
|
| 761 |
+
console.log(`🎨 Global fix: Found zoomed Mermaid image, forcing correct colors and z-index`);
|
| 762 |
+
zoomedMermaidImage.style.filter = 'none';
|
| 763 |
+
zoomedMermaidImage.style.setProperty('filter', 'none', 'important');
|
| 764 |
+
zoomedMermaidImage.style.zIndex = '10000000';
|
| 765 |
+
zoomedMermaidImage.style.setProperty('z-index', '10000000', 'important');
|
| 766 |
+
}
|
| 767 |
+
|
| 768 |
+
// Vérifier si l'overlay medium-zoom existe
|
| 769 |
+
const overlay = document.querySelector('.medium-zoom-overlay');
|
| 770 |
+
if (overlay) {
|
| 771 |
+
console.log(`🔝 Global fix: Found medium-zoom overlay, forcing high z-index`);
|
| 772 |
+
overlay.style.zIndex = '9999999';
|
| 773 |
+
overlay.style.setProperty('z-index', '9999999', 'important');
|
| 774 |
+
}
|
| 775 |
}
|
| 776 |
+
});
|
| 777 |
});
|
|
|
|
| 778 |
|
| 779 |
+
observer.observe(document.body, {
|
| 780 |
+
childList: true,
|
| 781 |
+
subtree: true
|
| 782 |
+
});
|
| 783 |
|
| 784 |
+
console.log(`🎨 Global Mermaid color and z-index fix observer started`);
|
| 785 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 786 |
|
| 787 |
+
// Fonction pour initialiser le zoom Mermaid avec retry
|
| 788 |
+
function initMermaidZoom() {
|
| 789 |
+
console.log("🎯 initMermaidZoom called");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 790 |
|
| 791 |
+
// Attendre que mediumZoom soit disponible
|
| 792 |
+
const checkMediumZoom = () => {
|
| 793 |
+
if (window.mediumZoom) {
|
| 794 |
+
console.log("✅ mediumZoom found, initializing Mermaid zoom");
|
| 795 |
+
setupMermaidZoom();
|
| 796 |
+
} else {
|
| 797 |
+
console.log("⏳ Waiting for mediumZoom...");
|
| 798 |
+
setTimeout(checkMediumZoom, 100);
|
| 799 |
+
}
|
| 800 |
+
};
|
| 801 |
+
|
| 802 |
+
checkMediumZoom();
|
| 803 |
+
}
|
| 804 |
+
|
| 805 |
+
// Export des fonctions pour utilisation globale
|
| 806 |
+
window.MermaidZoom = {
|
| 807 |
+
init: initMermaidZoom,
|
| 808 |
+
setup: setupMermaidZoom,
|
| 809 |
+
cleanup: cleanupMermaidObservers,
|
| 810 |
+
convertSvgToImage: convertSvgToImageForMediumZoom,
|
| 811 |
+
openZoom: openMermaidZoom,
|
| 812 |
+
closeZoom: closeMermaidZoom
|
| 813 |
+
};
|
| 814 |
+
|
| 815 |
+
// Auto-initialisation si le DOM est déjà chargé
|
| 816 |
+
if (document.readyState === "loading") {
|
| 817 |
+
document.addEventListener("DOMContentLoaded", () => {
|
| 818 |
+
initMermaidZoom();
|
| 819 |
+
setupGlobalMermaidColorFix();
|
| 820 |
+
});
|
| 821 |
+
} else {
|
| 822 |
initMermaidZoom();
|
| 823 |
setupGlobalMermaidColorFix();
|
| 824 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 825 |
|
| 826 |
+
// Aussi après le chargement complet
|
| 827 |
+
window.addEventListener("load", () => {
|
| 828 |
+
setTimeout(() => {
|
| 829 |
initMermaidZoom();
|
| 830 |
+
setupGlobalMermaidColorFix();
|
| 831 |
+
}, 1000);
|
| 832 |
+
});
|
| 833 |
|
| 834 |
+
// Observer simple pour les nouveaux diagrammes Mermaid (avec debounce)
|
| 835 |
+
let resizeTimeout;
|
| 836 |
+
const observer = new MutationObserver(() => {
|
| 837 |
+
// Debounce pour éviter les appels multiples
|
| 838 |
+
clearTimeout(resizeTimeout);
|
| 839 |
+
resizeTimeout = setTimeout(() => {
|
| 840 |
+
// Vérifier s'il y a vraiment de nouveaux diagrammes
|
| 841 |
+
const mermaidElements = document.querySelectorAll(".mermaid");
|
| 842 |
+
const wrappedElements = document.querySelectorAll(".mermaid-zoom-wrapper");
|
| 843 |
+
|
| 844 |
+
// Seulement si il y a plus de diagrammes que de wrappers
|
| 845 |
+
if (mermaidElements.length > wrappedElements.length) {
|
| 846 |
+
console.log(`🔄 New Mermaid diagrams detected: ${mermaidElements.length} total, ${wrappedElements.length} wrapped`);
|
| 847 |
+
initMermaidZoom();
|
| 848 |
+
}
|
| 849 |
+
}, 500); // Délai plus long pour éviter les appels fréquents
|
| 850 |
+
});
|
| 851 |
+
|
| 852 |
+
observer.observe(document.body, {
|
| 853 |
+
childList: true,
|
| 854 |
+
subtree: true,
|
| 855 |
+
});
|
| 856 |
|
| 857 |
+
console.log("🚀 Mermaid Zoom Script v19.0 loaded - DEBOUNCED observer to prevent resize loops");
|
| 858 |
+
} // Fin du bloc else (MERMAID_ZOOM_ENABLED)
|