Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
fix url, fix mermaid zoom perfs, fix model perfs hover
Browse files
app/public/hf-space-parent-listener.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* Script pour la fenêtre parente des Spaces Hugging Face
|
| 3 |
+
* Ce script écoute les messages de l'iframe et met à jour l'URL de la fenêtre parente
|
| 4 |
+
*
|
| 5 |
+
* Instructions d'utilisation :
|
| 6 |
+
* 1. Ajoutez ce script à votre Space Hugging Face dans le fichier app.py ou dans un composant Gradio
|
| 7 |
+
* 2. Ou utilisez-le dans une page HTML qui contient votre iframe
|
| 8 |
+
*/
|
| 9 |
+
|
| 10 |
+
(function () {
|
| 11 |
+
'use strict';
|
| 12 |
+
|
| 13 |
+
console.log('HF Space Parent Listener initialized');
|
| 14 |
+
|
| 15 |
+
// Écouter les messages de l'iframe
|
| 16 |
+
window.addEventListener('message', function (event) {
|
| 17 |
+
console.log('Received message from iframe:', event.data);
|
| 18 |
+
|
| 19 |
+
// Vérifier le type de message
|
| 20 |
+
if (event.data && event.data.type) {
|
| 21 |
+
switch (event.data.type) {
|
| 22 |
+
case 'urlChange':
|
| 23 |
+
case 'anchorChange':
|
| 24 |
+
case 'HF_SPACE_URL_UPDATE':
|
| 25 |
+
handleUrlChange(event.data);
|
| 26 |
+
break;
|
| 27 |
+
default:
|
| 28 |
+
console.log('Unknown message type:', event.data.type);
|
| 29 |
+
}
|
| 30 |
+
}
|
| 31 |
+
});
|
| 32 |
+
|
| 33 |
+
function handleUrlChange(data) {
|
| 34 |
+
try {
|
| 35 |
+
const hash = data.hash || data.anchorId;
|
| 36 |
+
const url = data.url;
|
| 37 |
+
|
| 38 |
+
if (hash) {
|
| 39 |
+
// Mettre à jour l'URL avec la nouvelle ancre
|
| 40 |
+
const newUrl = new URL(window.location);
|
| 41 |
+
newUrl.hash = hash;
|
| 42 |
+
|
| 43 |
+
// Utiliser replaceState pour éviter d'ajouter une entrée dans l'historique
|
| 44 |
+
window.history.replaceState(null, '', newUrl.toString());
|
| 45 |
+
|
| 46 |
+
console.log('Updated parent URL to:', newUrl.toString());
|
| 47 |
+
|
| 48 |
+
// Optionnel : faire défiler vers l'élément correspondant dans la page parente
|
| 49 |
+
const targetElement = document.querySelector(hash);
|
| 50 |
+
if (targetElement) {
|
| 51 |
+
targetElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
| 52 |
+
}
|
| 53 |
+
}
|
| 54 |
+
} catch (error) {
|
| 55 |
+
console.error('Error updating parent URL:', error);
|
| 56 |
+
}
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
// Fonction utilitaire pour tester la communication
|
| 60 |
+
window.testIframeCommunication = function () {
|
| 61 |
+
console.log('Testing iframe communication...');
|
| 62 |
+
const iframe = document.querySelector('iframe');
|
| 63 |
+
if (iframe) {
|
| 64 |
+
iframe.contentWindow.postMessage({ type: 'test' }, '*');
|
| 65 |
+
} else {
|
| 66 |
+
console.log('No iframe found');
|
| 67 |
+
}
|
| 68 |
+
};
|
| 69 |
+
|
| 70 |
+
})();
|
app/src/components/TableOfContents.astro
CHANGED
|
@@ -513,20 +513,67 @@ const { tableOfContentAutoCollapse = false } = Astro.props as Props;
|
|
| 513 |
if (window.location.href !== newUrl) {
|
| 514 |
history.pushState(null, null, newUrl);
|
| 515 |
|
| 516 |
-
//
|
| 517 |
if (window.parent !== window) {
|
| 518 |
try {
|
| 519 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 520 |
{
|
| 521 |
type: "urlChange",
|
| 522 |
url: newUrl,
|
| 523 |
hash: headingId,
|
| 524 |
},
|
| 525 |
-
|
| 526 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 527 |
} catch (e) {
|
| 528 |
-
|
| 529 |
-
|
|
|
|
|
|
|
| 530 |
}
|
| 531 |
}
|
| 532 |
}
|
|
|
|
| 513 |
if (window.location.href !== newUrl) {
|
| 514 |
history.pushState(null, null, newUrl);
|
| 515 |
|
| 516 |
+
// Essayer différentes méthodes pour communiquer avec la fenêtre parente
|
| 517 |
if (window.parent !== window) {
|
| 518 |
try {
|
| 519 |
+
// Méthode 1: Essayer de modifier directement l'URL du parent (si autorisé)
|
| 520 |
+
try {
|
| 521 |
+
window.parent.location.hash = headingId;
|
| 522 |
+
console.debug("Successfully updated parent URL directly");
|
| 523 |
+
return;
|
| 524 |
+
} catch (e) {
|
| 525 |
+
console.debug("Direct parent URL update blocked:", e.message);
|
| 526 |
+
}
|
| 527 |
+
|
| 528 |
+
// Méthode 2: Utiliser postMessage avec différents formats
|
| 529 |
+
const messages = [
|
| 530 |
{
|
| 531 |
type: "urlChange",
|
| 532 |
url: newUrl,
|
| 533 |
hash: headingId,
|
| 534 |
},
|
| 535 |
+
{
|
| 536 |
+
type: "anchorChange",
|
| 537 |
+
anchorId: headingId,
|
| 538 |
+
url: newUrl,
|
| 539 |
+
},
|
| 540 |
+
{
|
| 541 |
+
type: "HF_SPACE_URL_UPDATE",
|
| 542 |
+
hash: headingId,
|
| 543 |
+
url: newUrl,
|
| 544 |
+
},
|
| 545 |
+
];
|
| 546 |
+
|
| 547 |
+
messages.forEach((msg) => {
|
| 548 |
+
try {
|
| 549 |
+
window.parent.postMessage(msg, "*");
|
| 550 |
+
} catch (e) {
|
| 551 |
+
console.debug("PostMessage failed:", e.message);
|
| 552 |
+
}
|
| 553 |
+
});
|
| 554 |
+
|
| 555 |
+
// Méthode 3: Essayer avec l'origine spécifique si on peut la détecter
|
| 556 |
+
try {
|
| 557 |
+
const parentOrigin = window.parent.location.origin;
|
| 558 |
+
window.parent.postMessage(
|
| 559 |
+
{
|
| 560 |
+
type: "urlChange",
|
| 561 |
+
url: newUrl,
|
| 562 |
+
hash: headingId,
|
| 563 |
+
},
|
| 564 |
+
parentOrigin,
|
| 565 |
+
);
|
| 566 |
+
} catch (e) {
|
| 567 |
+
console.debug(
|
| 568 |
+
"PostMessage with specific origin failed:",
|
| 569 |
+
e.message,
|
| 570 |
+
);
|
| 571 |
+
}
|
| 572 |
} catch (e) {
|
| 573 |
+
console.debug(
|
| 574 |
+
"All parent communication methods failed:",
|
| 575 |
+
e.message,
|
| 576 |
+
);
|
| 577 |
}
|
| 578 |
}
|
| 579 |
}
|
app/src/content/embeds/base-model-performance.html
CHANGED
|
@@ -455,7 +455,7 @@
|
|
| 455 |
tooltip.innerHTML = `
|
| 456 |
<div class="tooltip-title">${model.name}</div>
|
| 457 |
<div class="tooltip-row">
|
| 458 |
-
<span class="tooltip-label">
|
| 459 |
<span class="tooltip-value">${model.size.toFixed(1)}B params</span>
|
| 460 |
</div>
|
| 461 |
<div class="tooltip-row">
|
|
@@ -463,7 +463,7 @@
|
|
| 463 |
<span class="tooltip-value">${model.winRate.toFixed(1)}%</span>
|
| 464 |
</div>
|
| 465 |
<div class="tooltip-row">
|
| 466 |
-
<span class="tooltip-label">
|
| 467 |
<span class="tooltip-value">${(model.winRate / model.size).toFixed(2)} pts/B</span>
|
| 468 |
</div>
|
| 469 |
`;
|
|
|
|
| 455 |
tooltip.innerHTML = `
|
| 456 |
<div class="tooltip-title">${model.name}</div>
|
| 457 |
<div class="tooltip-row">
|
| 458 |
+
<span class="tooltip-label">Size:</span>
|
| 459 |
<span class="tooltip-value">${model.size.toFixed(1)}B params</span>
|
| 460 |
</div>
|
| 461 |
<div class="tooltip-row">
|
|
|
|
| 463 |
<span class="tooltip-value">${model.winRate.toFixed(1)}%</span>
|
| 464 |
</div>
|
| 465 |
<div class="tooltip-row">
|
| 466 |
+
<span class="tooltip-label">Efficiency:</span>
|
| 467 |
<span class="tooltip-value">${(model.winRate / model.size).toFixed(2)} pts/B</span>
|
| 468 |
</div>
|
| 469 |
`;
|
app/src/scripts/mermaid-zoom-optimized.js
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* Mermaid Zoom Integration - Version Optimisée
|
| 3 |
+
* Système léger pour beaucoup de diagrammes Mermaid
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
console.log("🚀 Mermaid Zoom Script v16.0 loaded - OPTIMIZED for many diagrams");
|
| 7 |
+
|
| 8 |
+
// Cache pour les images générées
|
| 9 |
+
const imageCache = new Map();
|
| 10 |
+
|
| 11 |
+
// Fonction pour appliquer les styles Mermaid au SVG selon le thème
|
| 12 |
+
function applyMermaidStylesToSvg(svgElement) {
|
| 13 |
+
try {
|
| 14 |
+
const isDark = document.documentElement.getAttribute("data-theme") === "dark";
|
| 15 |
+
|
| 16 |
+
// Couleurs selon le thème
|
| 17 |
+
const colors = isDark ? {
|
| 18 |
+
nodeFill: '#1a1a1a',
|
| 19 |
+
nodeStroke: '#ffffff',
|
| 20 |
+
clusterFill: '#2a2a2a',
|
| 21 |
+
clusterStroke: '#ffffff',
|
| 22 |
+
pathStroke: '#ffffff',
|
| 23 |
+
textColor: '#ffffff'
|
| 24 |
+
} : {
|
| 25 |
+
nodeFill: '#ffffff',
|
| 26 |
+
nodeStroke: '#333333',
|
| 27 |
+
clusterFill: '#f9f9f9',
|
| 28 |
+
clusterStroke: '#333333',
|
| 29 |
+
pathStroke: '#333333',
|
| 30 |
+
textColor: '#333333'
|
| 31 |
+
};
|
| 32 |
+
|
| 33 |
+
// Appliquer border-radius et couleurs
|
| 34 |
+
const rects = svgElement.querySelectorAll('rect:not(.flowchart-link), .node rect, .nodeLabel rect');
|
| 35 |
+
rects.forEach(rect => {
|
| 36 |
+
rect.setAttribute('rx', '8');
|
| 37 |
+
rect.setAttribute('ry', '8');
|
| 38 |
+
rect.setAttribute('fill', colors.nodeFill);
|
| 39 |
+
rect.setAttribute('stroke', colors.nodeStroke);
|
| 40 |
+
});
|
| 41 |
+
|
| 42 |
+
const clusterRects = svgElement.querySelectorAll('.cluster rect');
|
| 43 |
+
clusterRects.forEach(rect => {
|
| 44 |
+
rect.setAttribute('rx', '8');
|
| 45 |
+
rect.setAttribute('ry', '8');
|
| 46 |
+
rect.setAttribute('fill', colors.clusterFill);
|
| 47 |
+
rect.setAttribute('stroke', colors.clusterStroke);
|
| 48 |
+
});
|
| 49 |
+
|
| 50 |
+
const paths = svgElement.querySelectorAll('.edgePath');
|
| 51 |
+
paths.forEach(path => {
|
| 52 |
+
path.setAttribute('stroke', colors.pathStroke);
|
| 53 |
+
});
|
| 54 |
+
|
| 55 |
+
const textElements = svgElement.querySelectorAll('text, .nodeLabel text, .edgeLabel text');
|
| 56 |
+
textElements.forEach(text => {
|
| 57 |
+
text.setAttribute('fill', colors.textColor);
|
| 58 |
+
});
|
| 59 |
+
|
| 60 |
+
} catch (error) {
|
| 61 |
+
console.error('❌ Error applying styles to SVG:', error);
|
| 62 |
+
}
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
// Fonction pour générer une clé de cache unique
|
| 66 |
+
function getCacheKey(svgElement, theme) {
|
| 67 |
+
const svgContent = svgElement.outerHTML;
|
| 68 |
+
const hash = svgContent.length + svgContent.slice(0, 100);
|
| 69 |
+
return `${hash}-${theme}`;
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
// Fonction pour convertir SVG en image (avec cache)
|
| 73 |
+
function convertSvgToImageCached(svgElement, wrapper, theme) {
|
| 74 |
+
const cacheKey = getCacheKey(svgElement, theme);
|
| 75 |
+
|
| 76 |
+
// Vérifier le cache
|
| 77 |
+
if (imageCache.has(cacheKey)) {
|
| 78 |
+
console.log(`📦 Using cached image for theme: ${theme}`);
|
| 79 |
+
return imageCache.get(cacheKey);
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
try {
|
| 83 |
+
const wrapperRect = wrapper.getBoundingClientRect();
|
| 84 |
+
const wrapperWidth = Math.round(wrapperRect.width);
|
| 85 |
+
const wrapperHeight = Math.round(wrapperRect.height);
|
| 86 |
+
|
| 87 |
+
// Cloner le SVG
|
| 88 |
+
const clonedSvg = svgElement.cloneNode(true);
|
| 89 |
+
applyMermaidStylesToSvg(clonedSvg);
|
| 90 |
+
|
| 91 |
+
// Créer une image 2x plus grande pour le zoom
|
| 92 |
+
const zoomFactor = 2;
|
| 93 |
+
const imageWidth = wrapperWidth * zoomFactor;
|
| 94 |
+
const imageHeight = wrapperHeight * zoomFactor;
|
| 95 |
+
|
| 96 |
+
clonedSvg.setAttribute('width', imageWidth);
|
| 97 |
+
clonedSvg.setAttribute('height', imageHeight);
|
| 98 |
+
|
| 99 |
+
// Créer l'URL de l'image
|
| 100 |
+
const svgData = new XMLSerializer().serializeToString(clonedSvg);
|
| 101 |
+
const svgBlob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' });
|
| 102 |
+
const svgUrl = URL.createObjectURL(svgBlob);
|
| 103 |
+
|
| 104 |
+
// Créer l'élément image
|
| 105 |
+
const imgElement = document.createElement('img');
|
| 106 |
+
imgElement.src = svgUrl;
|
| 107 |
+
imgElement.style.width = `${wrapperWidth}px`;
|
| 108 |
+
imgElement.style.height = `${wrapperHeight}px`;
|
| 109 |
+
imgElement.style.display = 'block';
|
| 110 |
+
imgElement.setAttribute('data-zoomable', '1');
|
| 111 |
+
imgElement.classList.add('mermaid-zoom-image');
|
| 112 |
+
|
| 113 |
+
const result = {
|
| 114 |
+
imgElement,
|
| 115 |
+
svgUrl,
|
| 116 |
+
dimensions: { wrapperWidth, wrapperHeight, imageWidth, imageHeight }
|
| 117 |
+
};
|
| 118 |
+
|
| 119 |
+
// Mettre en cache
|
| 120 |
+
imageCache.set(cacheKey, result);
|
| 121 |
+
console.log(`💾 Cached image for theme: ${theme}`);
|
| 122 |
+
|
| 123 |
+
return result;
|
| 124 |
+
|
| 125 |
+
} catch (error) {
|
| 126 |
+
console.error("❌ Error converting SVG to image:", error);
|
| 127 |
+
return null;
|
| 128 |
+
}
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
// Fonction pour initialiser le zoom sur un diagramme Mermaid
|
| 132 |
+
function initMermaidZoom(mermaidEl, index) {
|
| 133 |
+
// Vérifier si déjà wrappé
|
| 134 |
+
if (mermaidEl.parentElement?.classList.contains("mermaid-zoom-wrapper")) {
|
| 135 |
+
return;
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
console.log(`📦 Setting up zoom for Mermaid ${index}`);
|
| 139 |
+
|
| 140 |
+
// Créer le wrapper
|
| 141 |
+
const wrapper = document.createElement("div");
|
| 142 |
+
wrapper.className = "mermaid-zoom-wrapper";
|
| 143 |
+
wrapper.setAttribute("data-zoomable", "1");
|
| 144 |
+
wrapper.style.display = "block";
|
| 145 |
+
wrapper.style.width = "100%";
|
| 146 |
+
wrapper.style.maxWidth = "100%";
|
| 147 |
+
|
| 148 |
+
// Insérer le wrapper avant l'élément Mermaid
|
| 149 |
+
mermaidEl.parentNode.insertBefore(wrapper, mermaidEl);
|
| 150 |
+
wrapper.appendChild(mermaidEl);
|
| 151 |
+
|
| 152 |
+
// Ajouter l'événement de clic (conversion à la demande)
|
| 153 |
+
wrapper.addEventListener("click", (e) => {
|
| 154 |
+
e.preventDefault();
|
| 155 |
+
e.stopPropagation();
|
| 156 |
+
|
| 157 |
+
const svgElement = mermaidEl.querySelector("svg");
|
| 158 |
+
if (!svgElement) return;
|
| 159 |
+
|
| 160 |
+
const currentTheme = document.documentElement.getAttribute("data-theme");
|
| 161 |
+
console.log(`🖱️ Mermaid ${index} clicked, converting for zoom`);
|
| 162 |
+
|
| 163 |
+
// Convertir l'image (avec cache)
|
| 164 |
+
const result = convertSvgToImageCached(svgElement, wrapper, currentTheme);
|
| 165 |
+
if (!result) return;
|
| 166 |
+
|
| 167 |
+
// Remplacer le contenu par l'image
|
| 168 |
+
wrapper.innerHTML = '';
|
| 169 |
+
wrapper.appendChild(result.imgElement);
|
| 170 |
+
|
| 171 |
+
// Initialiser medium-zoom
|
| 172 |
+
const isDark = currentTheme === "dark";
|
| 173 |
+
const background = isDark ? "rgba(0,0,0,.9)" : "rgba(0,0,0,.85)";
|
| 174 |
+
|
| 175 |
+
const zoomInstance = window.mediumZoom(result.imgElement, {
|
| 176 |
+
background,
|
| 177 |
+
margin: 24,
|
| 178 |
+
scrollOffset: 0,
|
| 179 |
+
});
|
| 180 |
+
|
| 181 |
+
console.log(`🎉 Zoom initialized for Mermaid ${index}`);
|
| 182 |
+
|
| 183 |
+
// Forcer les z-index élevés
|
| 184 |
+
const forceZIndex = () => {
|
| 185 |
+
const overlay = document.querySelector('.medium-zoom-overlay');
|
| 186 |
+
const zoomedImage = document.querySelector('.medium-zoom-image--opened');
|
| 187 |
+
|
| 188 |
+
if (overlay) {
|
| 189 |
+
overlay.style.zIndex = '9999999';
|
| 190 |
+
overlay.style.setProperty('z-index', '9999999', 'important');
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
if (zoomedImage) {
|
| 194 |
+
zoomedImage.style.zIndex = '10000000';
|
| 195 |
+
zoomedImage.style.setProperty('z-index', '10000000', 'important');
|
| 196 |
+
zoomedImage.style.filter = 'none';
|
| 197 |
+
zoomedImage.style.setProperty('filter', 'none', 'important');
|
| 198 |
+
}
|
| 199 |
+
};
|
| 200 |
+
|
| 201 |
+
// Écouter les événements de zoom
|
| 202 |
+
result.imgElement.addEventListener('zoom:open', forceZIndex);
|
| 203 |
+
result.imgElement.addEventListener('zoom:opened', forceZIndex);
|
| 204 |
+
|
| 205 |
+
// Observer pour forcer les styles
|
| 206 |
+
const observer = new MutationObserver(forceZIndex);
|
| 207 |
+
observer.observe(document.body, { childList: true, subtree: true });
|
| 208 |
+
|
| 209 |
+
// Nettoyer l'observer après 5 secondes
|
| 210 |
+
setTimeout(() => observer.disconnect(), 5000);
|
| 211 |
+
});
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
// Fonction principale optimisée
|
| 215 |
+
function setupMermaidZoomOptimized() {
|
| 216 |
+
const mermaidElements = document.querySelectorAll(".mermaid");
|
| 217 |
+
console.log(`🔍 Found ${mermaidElements.length} Mermaid elements`);
|
| 218 |
+
|
| 219 |
+
mermaidElements.forEach((mermaidEl, index) => {
|
| 220 |
+
initMermaidZoom(mermaidEl, index);
|
| 221 |
+
});
|
| 222 |
+
}
|
| 223 |
+
|
| 224 |
+
// Observer global optimisé (un seul observer pour tout)
|
| 225 |
+
function setupGlobalObserver() {
|
| 226 |
+
const observer = new MutationObserver((mutations) => {
|
| 227 |
+
let shouldUpdate = false;
|
| 228 |
+
|
| 229 |
+
mutations.forEach((mutation) => {
|
| 230 |
+
if (mutation.type === 'childList') {
|
| 231 |
+
// Vérifier si de nouveaux diagrammes Mermaid ont été ajoutés
|
| 232 |
+
mutation.addedNodes.forEach(node => {
|
| 233 |
+
if (node.nodeType === 1) { // Element node
|
| 234 |
+
if (node.classList?.contains('mermaid') ||
|
| 235 |
+
node.querySelector?.('.mermaid')) {
|
| 236 |
+
shouldUpdate = true;
|
| 237 |
+
}
|
| 238 |
+
}
|
| 239 |
+
});
|
| 240 |
+
}
|
| 241 |
+
});
|
| 242 |
+
|
| 243 |
+
if (shouldUpdate) {
|
| 244 |
+
console.log(`🔄 New Mermaid diagrams detected, updating...`);
|
| 245 |
+
setTimeout(setupMermaidZoomOptimized, 100);
|
| 246 |
+
}
|
| 247 |
+
});
|
| 248 |
+
|
| 249 |
+
observer.observe(document.body, {
|
| 250 |
+
childList: true,
|
| 251 |
+
subtree: true
|
| 252 |
+
});
|
| 253 |
+
|
| 254 |
+
console.log(`👁️ Global observer started`);
|
| 255 |
+
}
|
| 256 |
+
|
| 257 |
+
// Initialisation optimisée
|
| 258 |
+
function initMermaidZoomOptimized() {
|
| 259 |
+
console.log("🎯 Initializing optimized Mermaid zoom");
|
| 260 |
+
|
| 261 |
+
// Attendre que mediumZoom soit disponible
|
| 262 |
+
const checkMediumZoom = () => {
|
| 263 |
+
if (window.mediumZoom) {
|
| 264 |
+
console.log("✅ mediumZoom found, initializing optimized Mermaid zoom");
|
| 265 |
+
setupMermaidZoomOptimized();
|
| 266 |
+
setupGlobalObserver();
|
| 267 |
+
} else {
|
| 268 |
+
console.log("⏳ Waiting for mediumZoom...");
|
| 269 |
+
setTimeout(checkMediumZoom, 100);
|
| 270 |
+
}
|
| 271 |
+
};
|
| 272 |
+
|
| 273 |
+
checkMediumZoom();
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
// Auto-initialisation
|
| 277 |
+
if (document.readyState === "loading") {
|
| 278 |
+
document.addEventListener("DOMContentLoaded", initMermaidZoomOptimized);
|
| 279 |
+
} else {
|
| 280 |
+
initMermaidZoomOptimized();
|
| 281 |
+
}
|
| 282 |
+
|
| 283 |
+
// Aussi après le chargement complet
|
| 284 |
+
window.addEventListener("load", () => {
|
| 285 |
+
setTimeout(initMermaidZoomOptimized, 1000);
|
| 286 |
+
});
|
| 287 |
+
|
| 288 |
+
// Nettoyer le cache périodiquement (éviter les fuites mémoire)
|
| 289 |
+
setInterval(() => {
|
| 290 |
+
if (imageCache.size > 20) {
|
| 291 |
+
console.log(`🧹 Cleaning image cache (${imageCache.size} items)`);
|
| 292 |
+
imageCache.clear();
|
| 293 |
+
}
|
| 294 |
+
}, 60000); // Toutes les minutes
|
app/src/scripts/mermaid-zoom.js
CHANGED
|
@@ -9,27 +9,29 @@ function applyMermaidStylesToSvg(svgElement) {
|
|
| 9 |
const isDark = document.documentElement.getAttribute("data-theme") === "dark";
|
| 10 |
console.log(`🎨 Applying Mermaid styles for theme: ${isDark ? 'dark' : 'light'}`);
|
| 11 |
|
| 12 |
-
// Couleurs selon le thème
|
| 13 |
const colors = isDark ? {
|
| 14 |
nodeFill: '#1a1a1a',
|
| 15 |
nodeStroke: '#ffffff',
|
| 16 |
-
nodeStrokeWidth: '
|
| 17 |
clusterFill: '#2a2a2a',
|
| 18 |
clusterStroke: '#ffffff',
|
| 19 |
-
clusterStrokeWidth: '
|
| 20 |
pathStroke: '#ffffff',
|
| 21 |
-
pathStrokeWidth: '
|
| 22 |
-
textColor: '#ffffff'
|
|
|
|
| 23 |
} : {
|
| 24 |
nodeFill: '#ffffff',
|
| 25 |
-
nodeStroke: '#
|
| 26 |
-
nodeStrokeWidth: '
|
| 27 |
clusterFill: '#f9f9f9',
|
| 28 |
-
clusterStroke: '#
|
| 29 |
-
clusterStrokeWidth: '
|
| 30 |
-
pathStroke: '#
|
| 31 |
-
pathStrokeWidth: '
|
| 32 |
-
textColor: '#
|
|
|
|
| 33 |
};
|
| 34 |
|
| 35 |
// Appliquer border-radius aux rectangles
|
|
@@ -74,19 +76,33 @@ function applyMermaidStylesToSvg(svgElement) {
|
|
| 74 |
}
|
| 75 |
});
|
| 76 |
|
| 77 |
-
// Appliquer les couleurs aux chemins
|
| 78 |
-
const paths = svgElement.querySelectorAll('.edgePath');
|
| 79 |
paths.forEach(path => {
|
| 80 |
path.setAttribute('stroke', colors.pathStroke);
|
| 81 |
path.setAttribute('stroke-width', colors.pathStrokeWidth);
|
| 82 |
});
|
| 83 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
// Appliquer les couleurs au texte
|
| 85 |
const textElements = svgElement.querySelectorAll('text, .nodeLabel text, .edgeLabel text');
|
| 86 |
textElements.forEach(text => {
|
| 87 |
text.setAttribute('fill', colors.textColor);
|
| 88 |
});
|
| 89 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
console.log(`🎨 Applied Mermaid styles: ${rects.length} rects, ${clusters.length} clusters, ${paths.length} paths, ${textElements.length} text elements`);
|
| 91 |
|
| 92 |
} catch (error) {
|
|
@@ -199,26 +215,41 @@ function convertSvgToImagePreservingDimensions(svgElement, wrapper, originalMerm
|
|
| 199 |
// Appliquer les styles Mermaid
|
| 200 |
applyMermaidStylesToSvg(clonedSvg);
|
| 201 |
|
| 202 |
-
//
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 207 |
|
| 208 |
// Créer une URL data pour le SVG
|
| 209 |
const svgData = new XMLSerializer().serializeToString(clonedSvg);
|
| 210 |
const svgBlob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' });
|
| 211 |
const svgUrl = URL.createObjectURL(svgBlob);
|
| 212 |
|
| 213 |
-
// Créer un élément img avec les
|
| 214 |
const imgElement = document.createElement('img');
|
| 215 |
imgElement.src = svgUrl;
|
| 216 |
-
imgElement.style.width = `${wrapperWidth}px`;
|
| 217 |
-
imgElement.style.height = `${wrapperHeight}px`;
|
| 218 |
imgElement.style.display = 'block';
|
| 219 |
imgElement.setAttribute('data-zoomable', '1');
|
| 220 |
imgElement.classList.add('mermaid-zoom-image'); // Classe spécifique pour Mermaid
|
| 221 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 222 |
console.log(`🖼️ Image created with exact dimensions:`, { width: wrapperWidth, height: wrapperHeight });
|
| 223 |
|
| 224 |
// Remplacer le contenu du wrapper par l'image
|
|
@@ -691,7 +722,7 @@ function setupMermaidZoom() {
|
|
| 691 |
} else {
|
| 692 |
console.log(`❌ No SVG found in Mermaid element ${index}`);
|
| 693 |
}
|
| 694 |
-
},
|
| 695 |
});
|
| 696 |
}
|
| 697 |
|
|
@@ -778,7 +809,7 @@ window.addEventListener("load", () => {
|
|
| 778 |
|
| 779 |
// Observer simple pour les nouveaux diagrammes Mermaid
|
| 780 |
const observer = new MutationObserver(() => {
|
| 781 |
-
setTimeout(initMermaidZoom,
|
| 782 |
});
|
| 783 |
|
| 784 |
observer.observe(document.body, {
|
|
@@ -786,4 +817,4 @@ observer.observe(document.body, {
|
|
| 786 |
subtree: true,
|
| 787 |
});
|
| 788 |
|
| 789 |
-
console.log("🚀 Mermaid Zoom Script
|
|
|
|
| 9 |
const isDark = document.documentElement.getAttribute("data-theme") === "dark";
|
| 10 |
console.log(`🎨 Applying Mermaid styles for theme: ${isDark ? 'dark' : 'light'}`);
|
| 11 |
|
| 12 |
+
// Couleurs selon le thème - avec plus de contraste pour le zoom
|
| 13 |
const colors = isDark ? {
|
| 14 |
nodeFill: '#1a1a1a',
|
| 15 |
nodeStroke: '#ffffff',
|
| 16 |
+
nodeStrokeWidth: '2', // Plus épais pour plus de visibilité
|
| 17 |
clusterFill: '#2a2a2a',
|
| 18 |
clusterStroke: '#ffffff',
|
| 19 |
+
clusterStrokeWidth: '2',
|
| 20 |
pathStroke: '#ffffff',
|
| 21 |
+
pathStrokeWidth: '2', // Plus épais pour les flèches
|
| 22 |
+
textColor: '#ffffff',
|
| 23 |
+
linkColor: '#ffffff' // Couleur spécifique pour les liens
|
| 24 |
} : {
|
| 25 |
nodeFill: '#ffffff',
|
| 26 |
+
nodeStroke: '#000000', // Noir pur pour plus de contraste
|
| 27 |
+
nodeStrokeWidth: '2',
|
| 28 |
clusterFill: '#f9f9f9',
|
| 29 |
+
clusterStroke: '#000000', // Noir pur
|
| 30 |
+
clusterStrokeWidth: '2',
|
| 31 |
+
pathStroke: '#000000', // Noir pur pour les flèches
|
| 32 |
+
pathStrokeWidth: '2',
|
| 33 |
+
textColor: '#000000', // Noir pur
|
| 34 |
+
linkColor: '#000000' // Noir pur pour les liens
|
| 35 |
};
|
| 36 |
|
| 37 |
// Appliquer border-radius aux rectangles
|
|
|
|
| 76 |
}
|
| 77 |
});
|
| 78 |
|
| 79 |
+
// Appliquer les couleurs aux chemins et flèches
|
| 80 |
+
const paths = svgElement.querySelectorAll('.edgePath, path, .flowchart-link');
|
| 81 |
paths.forEach(path => {
|
| 82 |
path.setAttribute('stroke', colors.pathStroke);
|
| 83 |
path.setAttribute('stroke-width', colors.pathStrokeWidth);
|
| 84 |
});
|
| 85 |
|
| 86 |
+
// Appliquer les couleurs aux liens spécifiquement
|
| 87 |
+
const links = svgElement.querySelectorAll('.flowchart-link, .edgeLabel');
|
| 88 |
+
links.forEach(link => {
|
| 89 |
+
link.setAttribute('stroke', colors.linkColor);
|
| 90 |
+
link.setAttribute('fill', colors.linkColor);
|
| 91 |
+
});
|
| 92 |
+
|
| 93 |
// Appliquer les couleurs au texte
|
| 94 |
const textElements = svgElement.querySelectorAll('text, .nodeLabel text, .edgeLabel text');
|
| 95 |
textElements.forEach(text => {
|
| 96 |
text.setAttribute('fill', colors.textColor);
|
| 97 |
});
|
| 98 |
|
| 99 |
+
// Appliquer les couleurs aux marqueurs de flèches
|
| 100 |
+
const markers = svgElement.querySelectorAll('marker, marker path');
|
| 101 |
+
markers.forEach(marker => {
|
| 102 |
+
marker.setAttribute('fill', colors.pathStroke);
|
| 103 |
+
marker.setAttribute('stroke', colors.pathStroke);
|
| 104 |
+
});
|
| 105 |
+
|
| 106 |
console.log(`🎨 Applied Mermaid styles: ${rects.length} rects, ${clusters.length} clusters, ${paths.length} paths, ${textElements.length} text elements`);
|
| 107 |
|
| 108 |
} catch (error) {
|
|
|
|
| 215 |
// Appliquer les styles Mermaid
|
| 216 |
applyMermaidStylesToSvg(clonedSvg);
|
| 217 |
|
| 218 |
+
// Créer une image PLUS GRANDE pour permettre un vrai zoom (2x la taille)
|
| 219 |
+
const zoomFactor = 2;
|
| 220 |
+
const imageWidth = wrapperWidth * zoomFactor;
|
| 221 |
+
const imageHeight = wrapperHeight * zoomFactor;
|
| 222 |
+
|
| 223 |
+
// Forcer les dimensions plus grandes sur le SVG
|
| 224 |
+
clonedSvg.setAttribute('width', imageWidth);
|
| 225 |
+
clonedSvg.setAttribute('height', imageHeight);
|
| 226 |
+
clonedSvg.style.width = `${imageWidth}px`;
|
| 227 |
+
clonedSvg.style.height = `${imageHeight}px`;
|
| 228 |
+
|
| 229 |
+
console.log(`🔍 Creating zoomable image:`, {
|
| 230 |
+
original: { width: wrapperWidth, height: wrapperHeight },
|
| 231 |
+
zoomed: { width: imageWidth, height: imageHeight },
|
| 232 |
+
factor: zoomFactor
|
| 233 |
+
});
|
| 234 |
|
| 235 |
// Créer une URL data pour le SVG
|
| 236 |
const svgData = new XMLSerializer().serializeToString(clonedSvg);
|
| 237 |
const svgBlob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' });
|
| 238 |
const svgUrl = URL.createObjectURL(svgBlob);
|
| 239 |
|
| 240 |
+
// Créer un élément img avec les dimensions du wrapper (affichage normal)
|
| 241 |
const imgElement = document.createElement('img');
|
| 242 |
imgElement.src = svgUrl;
|
| 243 |
+
imgElement.style.width = `${wrapperWidth}px`; // Affichage normal
|
| 244 |
+
imgElement.style.height = `${wrapperHeight}px`; // Affichage normal
|
| 245 |
imgElement.style.display = 'block';
|
| 246 |
imgElement.setAttribute('data-zoomable', '1');
|
| 247 |
imgElement.classList.add('mermaid-zoom-image'); // Classe spécifique pour Mermaid
|
| 248 |
|
| 249 |
+
// Ajouter les attributs pour medium-zoom (dimensions réelles de l'image)
|
| 250 |
+
imgElement.setAttribute('data-zoom-width', imageWidth);
|
| 251 |
+
imgElement.setAttribute('data-zoom-height', imageHeight);
|
| 252 |
+
|
| 253 |
console.log(`🖼️ Image created with exact dimensions:`, { width: wrapperWidth, height: wrapperHeight });
|
| 254 |
|
| 255 |
// Remplacer le contenu du wrapper par l'image
|
|
|
|
| 722 |
} else {
|
| 723 |
console.log(`❌ No SVG found in Mermaid element ${index}`);
|
| 724 |
}
|
| 725 |
+
}, 1000); // Réduit de 2000ms à 1000ms pour être plus rapide
|
| 726 |
});
|
| 727 |
}
|
| 728 |
|
|
|
|
| 809 |
|
| 810 |
// Observer simple pour les nouveaux diagrammes Mermaid
|
| 811 |
const observer = new MutationObserver(() => {
|
| 812 |
+
setTimeout(initMermaidZoom, 200); // Réduit de 500ms à 200ms
|
| 813 |
});
|
| 814 |
|
| 815 |
observer.observe(document.body, {
|
|
|
|
| 817 |
subtree: true,
|
| 818 |
});
|
| 819 |
|
| 820 |
+
console.log("🚀 Mermaid Zoom Script v18.0 loaded - Enhanced contrast for arrows and links");
|