Back to "Ενίσχυση Διαγραμμάτων Γοργόνας με Pan/Zoom και Εξαγωγή"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

DaisyUI Javascript Mermaid SVG Tailwind

Ενίσχυση Διαγραμμάτων Γοργόνας με Pan/Zoom και Εξαγωγή

Friday, 07 November 2025

Εισαγωγή

npm πακέτο διαθέσιμο: Η εφαρμογή αυτή είναι τώρα διαθέσιμη ως @mostly διαυγής/mermaid-enhancements - ένα έτοιμο για την παραγωγή πακέτο npm. Publishing Mermaid Βελτιώσεις ως ένα πακέτο npm για λεπτομέρειες σχετικά με το πώς να το χρησιμοποιήσετε στα έργα σας.

Γοργόνα είναι ένα φανταστικό εργαλείο για τη δημιουργία διαγραμμάτων από το κείμενο, αλλά η προεπιλεγμένη απόδοση μπορεί να είναι περιορισμένη για σύνθετα διαγράμματα. Οι χρήστες δεν μπορούν εύκολα να ζουμάρουν για να δείτε λεπτομέρειες, τηγάνι γύρω από μεγάλα διαγράμματα, ή εξαγωγή τους για τεκμηρίωση. Σε αυτό το άρθρο, θα σας δείξω πώς ενίσχυσα διαγράμματα Mermaid σε αυτό το site με διαδραστικό έλεγχο pan / ζουμ, πλήρη οθόνη lightbox προβολή, και εξαγωγή λειτουργικότητα (τόσο PNG και SVG μορφές).

Αυτή η εφαρμογή είναι έτοιμη για παραγωγή, χειρίζεται τη σκοτεινή λειτουργία μεταγωγής χαριτωμένα, και είναι ανθεκτική σε Cloudflare παρεμβολές Rocket Loader.

Με τι μοιάζει;

Μια ωραία στη σελίδα (και ποπ-άουτ) επίδειξη γοργόνας.js που μοιάζει κάπως με του GitHub αλλά *Καλύτερα.*Σημαίνει ότι τα διαγράμματα δεν παίρνουν SCREENS αλλά είναι ακόμα εύκολο να διαβάσετε.

mermaid_pan_zoom.png

Το Πρόβλημα

Έξω από το πλαίσιο, τα διαγράμματα Γοργόνα έχουν αρκετούς περιορισμούς:

  1. Σταθερό μέγεθος - Μεγάλα διαγράμματα είτε κόβονται είτε συρρικνώνονται για να χωρέσουν
  2. Καμία διαδραστική δραστηριότητα - Δεν μπορείς να κάνεις ζουμ για να δεις λεπτομέρειες ή να πας τριγύρω.
  3. Καμία εξαγωγή - Οι χρήστες δεν μπορούν να αποθηκεύσουν διαγράμματα για εξωτερική χρήση
  4. Κακή εμπειρία κινητής τηλεφωνίας - Οι μικρές οθόνες κάνουν τα σύνθετα διαγράμματα άχρηστα
  5. Ζητήματα αλλαγής θέματος - Τα διαγράμματα δεν επαναδιανέμονται πάντα σωστά κατά τη μετάβαση μεταξύ του φωτός και της σκοτεινής λειτουργίας

Η Λύση

Έχω εφαρμόσει ένα ολοκληρωμένο σύστημα βελτίωσης που προσθέτει:

  • Διαδραστικό pan/zoom χρήση της βιβλιοθήκης svg-pan-zoom
  • Πλωτά κουμπιά ελέγχου για ζουμ in/out, επαναφορά, pan toggle, και εξαγωγή
  • Φωτεινή πλήρους οθόνης λειτουργία για καλύτερη προβολή
  • Εξαγωγή PNG και SVG λειτουργικότητα
  • Αυτόματη τοποθέτηση στο φορτίο έτσι ολόκληρο το διάγραμμα είναι ορατό από προεπιλογή Βλέποντας
  • Οθόνη πλήρους πλάτους εξάλειψη των περιορισμών τεχνητόυ μεγέθους

Αρχιτεκτονική επισκόπηση

Το διάλυμα αποτελείται από τρία κύρια συστατικά:

graph TB
    A[mermaid_theme_switch.js] -->|Initializes| B[Mermaid Diagrams]
    B -->|Renders SVG| C[mermaid_enhancements.js]
    C -->|Adds| D[Control Buttons]
    C -->|Initializes| E[svg-pan-zoom]
    D -->|Triggers| F[Pan/Zoom Actions]
    D -->|Triggers| G[Export Functions]
    D -->|Triggers| H[Fullscreen Lightbox]

Εφαρμογή

Εγκατάσταση εξαρτήσεων

Πρώτα, εγκαταστήστε τα απαιτούμενα πακέτα npm:

npm install svg-pan-zoom html-to-image

Οι βιβλιοθήκες αυτές παρέχουν:

  • svg-pan-zoom - Διαδραστικό τηγάνι και λειτουργικότητα ζουμ για στοιχεία SVG
  • html-to-image - Εξαγωγή SVG/PNG λειτουργικότητα

Κύρια ενότητα ενίσχυσης

Η κύρια ενότητα ενίσχυσης (mermaid_enhancements.js) χειρίζεται όλα τα διαδραστικά χαρακτηριστικά.

Δημιουργία Κουμπιών Ελέγχου

Κάθε διάγραμμα παίρνει έναν πίνακα ελέγχου με κουμπιά για όλες τις ενέργειες:

function createControlButtons(container, diagramId) {
    // Check if controls already exist
    if (container.querySelector('.mermaid-controls')) {
        return;
    }

    const controlsDiv = document.createElement('div');
    controlsDiv.className = 'mermaid-controls';

    const buttons = [
        { icon: 'bx-fullscreen', title: 'Fullscreen', action: 'fullscreen' },
        { icon: 'bx-zoom-in', title: 'Zoom In', action: 'zoomIn' },
        { icon: 'bx-zoom-out', title: 'Zoom Out', action: 'zoomOut' },
        { icon: 'bx-reset', title: 'Reset View', action: 'reset' },
        { icon: 'bx-move', title: 'Pan', action: 'pan' },
        { icon: 'bx-image', title: 'Export as PNG', action: 'exportPng' },
        { icon: 'bx-code-alt', title: 'Export as SVG', action: 'exportSvg' }
    ];

    buttons.forEach(btn => {
        const button = document.createElement('button');
        button.className = `mermaid-control-btn bx ${btn.icon}`;
        button.setAttribute('title', btn.title);
        button.setAttribute('aria-label', btn.title);
        button.setAttribute('data-action', btn.action);
        button.setAttribute('data-diagram-id', diagramId);
        controlsDiv.appendChild(button);
    });

    container.appendChild(controlsDiv);
}

Έναρξη Pan/Zoom

Η βιβλιοθήκη svg-pan-zoom παρέχει ομαλή, performant αλληλεπίδραση:

function initPanZoom(svgElement, diagramId) {
    // Clean up existing instance if present
    if (panZoomInstances.has(diagramId)) {
        try {
            panZoomInstances.get(diagramId).destroy();
        } catch (e) {
            console.warn('Failed to destroy existing pan-zoom instance:', e);
        }
        panZoomInstances.delete(diagramId);
    }

    try {
        const panZoomInstance = svgPanZoom(svgElement, {
            zoomEnabled: true,
            controlIconsEnabled: false, // We use custom controls
            fit: true,
            center: true,
            minZoom: 0.1,
            maxZoom: 10,
            zoomScaleSensitivity: 0.3,
            dblClickZoomEnabled: true,
            mouseWheelZoomEnabled: true,
            preventMouseEventsDefault: true,
            contain: false
        });

        panZoomInstances.set(diagramId, panZoomInstance);
        return panZoomInstance;
    } catch (error) {
        console.error('Failed to initialize pan-zoom:', error);
        return null;
    }
}

Εξαγωγή Λειτουργικότητας

Το σύστημα εξαγωγής διατηρεί την ποιότητα του διαγράμματος και χειρίζεται τόσο τις μορφές PNG όσο και SVG:

async function exportDiagram(container, format, diagramId) {
    try {
        const svgElement = container.querySelector('svg');
        if (!svgElement) {
            window.showToast && window.showToast('No diagram found to export', 3000, 'error');
            return;
        }

        // Clone the SVG to avoid modifying the original
        const clonedSvg = svgElement.cloneNode(true);

        // Get the viewBox or calculate from bounding box
        let viewBox = clonedSvg.getAttribute('viewBox');
        if (!viewBox) {
            const bbox = svgElement.getBBox();
            viewBox = `${bbox.x} ${bbox.y} ${bbox.width} ${bbox.height}`;
            clonedSvg.setAttribute('viewBox', viewBox);
        }

        // Parse viewBox to get dimensions
        const [, , vbWidth, vbHeight] = viewBox.split(' ').map(Number);

        // Set explicit dimensions based on viewBox for proper export
        clonedSvg.setAttribute('width', vbWidth);
        clonedSvg.setAttribute('height', vbHeight);

        // Remove inline styles but keep viewBox
        clonedSvg.removeAttribute('style');
        clonedSvg.style.backgroundColor = 'transparent';
        clonedSvg.style.maxWidth = 'none';

        // Create temporary container
        const tempDiv = document.createElement('div');
        tempDiv.style.position = 'absolute';
        tempDiv.style.left = '-9999px';
        tempDiv.appendChild(clonedSvg);
        document.body.appendChild(tempDiv);

        let dataUrl;
        const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
        const filename = `mermaid-diagram-${timestamp}`;

        if (format === 'png') {
            dataUrl = await toPng(clonedSvg, {
                backgroundColor: 'white',
                pixelRatio: 2 // Higher quality
            });
            downloadFile(dataUrl, `${filename}.png`);
        } else {
            dataUrl = await toSvg(clonedSvg, {
                backgroundColor: 'transparent'
            });
            downloadFile(dataUrl, `${filename}.svg`);
        }

        // Clean up
        document.body.removeChild(tempDiv);

        window.showToast && window.showToast(`Diagram exported as ${format.toUpperCase()}`, 3000, 'success');
    } catch (error) {
        console.error('Failed to export diagram:', error);
        window.showToast && window.showToast('Failed to export diagram', 3000, 'error');
    }
}

Βασικές εκτιμήσεις για τις εξαγωγές:

  1. Διατηρήστε την προβολήBox - Κρίσιμη για τη σύλληψη ολόκληρου του διαγράμματος, όχι μόνο το ορατό τμήμα
  2. Υπολογίστε τις διαστάσεις - Θέστε γρήγορα το πλάτος/ύψος από την προβολήBox για συνεπή εξαγωγή
  3. Αφαίρεση μεταμορφώσεων - Στριπτίζ pan-zoom μετασχηματίζεται έτσι εξαγωγή δείχνει το πλήρες διάγραμμα
  4. Χειρισμός ιστορικού - Λευκό φόντο για PNG, διαφανές για SVG
  5. Υψηλή ανάλυση - Χρήση pixelRatio: 2 για τραγανές εξαγωγές PNG

Fullscreen Lightbox

Το lightbox παρέχει μια καταδυτική εμπειρία προβολής:

function openFullscreenLightbox(container, diagramId) {
    const svgElement = container.querySelector('svg');
    if (!svgElement) return;

    // Create lightbox overlay
    const lightbox = document.createElement('div');
    lightbox.className = 'mermaid-lightbox';
    lightbox.innerHTML = `
        <div class="mermaid-lightbox-content">
            <button class="mermaid-lightbox-close bx bx-x" aria-label="Close"></button>
            <div class="mermaid-lightbox-diagram-wrapper">
                <div class="mermaid-lightbox-diagram"></div>
            </div>
        </div>
    `;

    // Clone and prepare SVG
    const clonedSvg = svgElement.cloneNode(true);
    clonedSvg.removeAttribute('width');
    clonedSvg.removeAttribute('height');
    clonedSvg.style.width = '100%';
    clonedSvg.style.height = '100%';

    const diagramContainer = lightbox.querySelector('.mermaid-lightbox-diagram');
    diagramContainer.appendChild(clonedSvg);

    // Add controls to lightbox
    const wrapper = lightbox.querySelector('.mermaid-lightbox-diagram-wrapper');
    const lightboxDiagramId = `${diagramId}-lightbox`;
    createControlButtons(wrapper, lightboxDiagramId);

    document.body.appendChild(lightbox);

    // Initialize pan-zoom after layout completes
    setTimeout(() => {
        const panZoom = initPanZoom(clonedSvg, lightboxDiagramId);
        if (panZoom) {
            panZoom.resize();
            panZoom.fit();
            panZoom.center();
        }
    }, 100);

    // Close handlers
    const closeLightbox = () => {
        if (panZoomInstances.has(lightboxDiagramId)) {
            try {
                panZoomInstances.get(lightboxDiagramId).destroy();
            } catch (e) {
                console.warn('Failed to destroy lightbox pan-zoom:', e);
            }
            panZoomInstances.delete(lightboxDiagramId);
        }
        lightbox.remove();
    };

    lightbox.querySelector('.mermaid-lightbox-close').addEventListener('click', closeLightbox);
    lightbox.addEventListener('click', (e) => {
        if (e.target === lightbox) closeLightbox();
    });

    // ESC key to close
    const escHandler = (e) => {
        if (e.key === 'Escape') {
            closeLightbox();
            document.removeEventListener('keydown', escHandler);
        }
    };
    document.addEventListener('keydown', escHandler);
}

Κύρια λειτουργία ενίσχυσης

Αυτό δένει τα πάντα μαζί και καλείται αφού η Γοργόνα αποδώσει:

export function enhanceMermaidDiagrams() {
    const diagrams = document.querySelectorAll('.mermaid[data-processed="true"]');

    diagrams.forEach(diagram => {
        const svgElement = diagram.querySelector('svg');
        if (!svgElement) return;

        // CRITICAL: Remove inline max-width constraint that Mermaid adds
        svgElement.style.maxWidth = 'none';

        // Wrap diagram with controls
        const diagramId = wrapDiagramWithControls(diagram);

        // Initialize pan/zoom and auto-fit
        const panZoom = initPanZoom(svgElement, diagramId);
        if (panZoom) {
            // Fit diagram to container by default
            setTimeout(() => {
                panZoom.resize();
                panZoom.fit();
                panZoom.center();
            }, 100);
        }
    });

    // Set up event delegation for control buttons (only once)
    if (!document.body.hasAttribute('data-mermaid-controls-initialized')) {
        document.body.addEventListener('click', handleControlClick);
        document.body.setAttribute('data-mermaid-controls-initialized', 'true');
    }
}

Κρίσιμη διόρθωση: Η Γοργόνα εφαρμόζει μια εσωτερική γραμμή style="max-width: 1020px" σε στοιχεία SVG, η οποία αποτρέπει την πλήρη οθόνη πλάτους. Αφαίρεση αυτού είναι απαραίτητη για την κατάλληλη συμπεριφορά ανταπόκρισης.

Ενσωμάτωση θέματος

Ο διακόπτης θέματος εξασφαλίζει ότι τα διαγράμματα επανεναλλάσσονται σωστά κατά τη μετάβαση μεταξύ φωτεινών και σκοτεινών τρόπων:

import { enhanceMermaidDiagrams } from './mermaid_enhancements';

const loadMermaid = async (theme) => {
    if (!window.mermaid) return;
    try {
        window.mermaid.initialize({
            startOnLoad: false,
            theme,
            themeVariables: {
                background: 'transparent'
            }
        });
        await window.mermaid.run({
            querySelector: elementSelector,
        });

        // Enhance diagrams after rendering completes
        // Use requestAnimationFrame for better timing
        await new Promise(resolve => {
            requestAnimationFrame(() => {
                requestAnimationFrame(() => {
                    enhanceMermaidDiagrams();
                    resolve();
                });
            });
        });
    } catch (err) {
        console.error('Mermaid render error:', err);
    }
};

Χρήση requestAnimationFrame δύο φορές εξασφαλίζει ότι το πρόγραμμα περιήγησης έχει ολοκληρώσει τη ζωγραφική του SVG πριν προσπαθήσουμε να το ενισχύσουμε.

Συμβιβάσιμο Rocket Rocket Rocket Loader Cloudflare

Το Rocket Loader του Cloudflare μπορεί να καθυστερήσει την εκτέλεση JavaScript, σπάζοντας την αρχικοποίηση.

// Wait for all dependencies to load with exponential backoff
function waitForDependencies(maxAttempts = 50) {
    return new Promise((resolve) => {
        let attempts = 0;

        const checkDependencies = () => {
            attempts++;

            const depsReady =
                typeof window.hljs !== 'undefined' &&
                typeof window.mermaid !== 'undefined' &&
                typeof window.Alpine !== 'undefined' &&
                typeof window.htmx !== 'undefined';

            if (depsReady) {
                console.log('All dependencies loaded after', attempts, 'attempts');

                // Start Alpine.js now that it's loaded
                if (window.Alpine && !window.Alpine.version) {
                    try {
                        window.Alpine.start();
                        console.log('Alpine.js started');
                    } catch (err) {
                        console.error('Failed to start Alpine:', err);
                    }
                }

                resolve();
            } else if (attempts >= maxAttempts) {
                console.warn('Timeout waiting for dependencies');
                resolve(); // Continue anyway
            } else {
                // Retry with exponential backoff
                const delay = Math.min(50 * Math.pow(1.2, attempts), 500);
                setTimeout(checkDependencies, delay);
            }
        };

        checkDependencies();
    });
}

// Robust initialization
async function safeInitialize() {
    try {
        await waitForDependencies();

        if (document.readyState === 'loading') {
            await new Promise(resolve => {
                document.addEventListener('DOMContentLoaded', resolve, { once: true });
            });
        }

        await initializePage();
    } catch (err) {
        console.error('Failed to initialize page:', err);
        // Retry once after delay
        setTimeout(() => {
            initializePage().catch(e => console.error('Retry failed:', e));
        }, 1000);
    }
}

safeInitialize();

Επίσης, βεβαιωθείτε ότι το κύριο σενάριο σας έχει το data-cfasync="false" χαρακτηριστικό για να το αποκλείσει από Rocket Loader:

<script src="~/js/dist/main.js" type="module" asp-append-version="true" data-cfasync="false"></script>

Στυλ με Tailwind/DaisyUI

Το CSS χρησιμοποιεί κατηγορίες χρησιμότητας Tailwind και προσαρμοσμένο στυλ για βερνίκι:

/* Mermaid diagram wrapper */
.mermaid-wrapper {
    @apply relative rounded-lg overflow-hidden w-full;
    margin: 1rem 0;
}

.mermaid-wrapper .mermaid {
    @apply m-0 w-full;
    min-height: 500px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
}

.mermaid-wrapper .mermaid svg {
    width: 100% !important;
    height: auto !important;
    min-height: 450px;
}

/* Control buttons */
.mermaid-controls {
    @apply absolute top-2 right-2 flex gap-1 z-10;
    background: rgba(255, 255, 255, 0.9);
    border-radius: 0.5rem;
    padding: 0.25rem;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.dark .mermaid-controls {
    background: rgba(31, 41, 55, 0.95);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}

.mermaid-control-btn {
    @apply p-2 rounded cursor-pointer transition-all duration-200;
    background: transparent;
    border: none;
    color: #4b5563;
    font-size: 1.25rem;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
}

.mermaid-control-btn:hover {
    background: rgba(37, 99, 235, 0.1);
    color: #2563eb;
    transform: scale(1.1);
}

.dark .mermaid-control-btn {
    color: #9ca3af;
}

.dark .mermaid-control-btn:hover {
    background: rgba(55, 65, 81, 0.8);
    color: #60a5fa;
}

/* Lightbox */
.mermaid-lightbox {
    @apply fixed inset-0 z-50 flex items-center justify-center;
    background: rgba(0, 0, 0, 0.85);
    backdrop-filter: blur(4px);
    animation: fadeIn 0.2s ease-out;
}

.dark .mermaid-lightbox {
    background: rgba(0, 0, 0, 0.95);
}

.mermaid-lightbox-content {
    @apply relative w-11/12 h-5/6 bg-white rounded-lg shadow-2xl;
    max-width: 1400px;
}

.dark .mermaid-lightbox-content {
    @apply bg-gray-800;
}

.mermaid-lightbox-close {
    @apply absolute top-4 right-4 z-10 p-2 rounded-full cursor-pointer transition-all;
    background: rgba(0, 0, 0, 0.5);
    border: none;
    color: white;
    font-size: 2rem;
    width: 3rem;
    height: 3rem;
    display: flex;
    align-items: center;
    justify-content: center;
}

.mermaid-lightbox-close:hover {
    background: rgba(220, 38, 38, 0.8);
    transform: scale(1.1);
}

@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

Δοκιμή και αποσφαλμάτωση

Εδώ είναι πώς να ελέγξει τα πάντα λειτουργεί:

Έξοδος κονσόλας

Με την κατάλληλη αρχικοποίηση, θα πρέπει να δείτε:

All dependencies loaded after 1 attempts
Alpine.js started
Highlight.js copy plugin registered
Highlight.js initialized on page load
Mermaid initialized on page load
Document is ready - all initializations complete
HTMX event listener registered successfully

HTMX Dynamic Content

Μετά τις ανταλλαγές HTMX, θα πρέπει να δείτε:

HTMX afterSettle triggered for: contentcontainer
Highlight.js applied after HTMX swap
Mermaid initialized
Mermaid applied after HTMX swap
HTMX afterSettle complete for: contentcontainer

Κατάλογος ελέγχου δοκιμών

  • Τα διαγράμματα αποδίδουν στο αρχικό φορτίο σελίδας
  • Εργασίες ελέγχου Pan/zoom
  • Fullscreen lightbox ανοίγει και κλείνει (κουμπί X, κάντε κλικ έξω, πλήκτρο ESC)
  • PNG εξαγωγή συλλαμβάνει πλήρες διάγραμμα (όχι μόνο γωνία)
  • SVG εξαγωγή διατηρεί φορείς
  • Λειτουργεί μετά από ανταλλαγή περιεχομένου HTMX
  • Σωστά τα διαγράμματα αλλαγής θέματος
  • Κινητή απόκριση (τα χειριστήρια παραμένουν ορατά, κλίμακα διαγραμμάτων)
  • Σκούρο styling λειτουργία εφαρμόζεται σωστά
  • Προσβασιμότητα πληκτρολογίου (ταμπ σε χειριστήρια, εισάγετε για να ενεργοποιήσετε)

Εκτιμήσεις Απόδοσης

  1. Τεμπέλης αρχικοποίησης - Αυξήστε μόνο τα διαγράμματα που υπάρχουν στη σελίδα
  2. Καθαρισμός περιστατικού - Καταστρέψτε pan-zoom περιπτώσεις όταν τα διαγράμματα αφαιρούνται
  3. Εκδήλωση εξουσιοδότησης - Ένας ακροατής κλικ χειρίζεται όλα τα κουμπιά ελέγχου
  4. requestAnimationFrame - Καλύτερος συγχρονισμός από αυθαίρετες τιμές timeout
  5. Εκπεστέες εξαγωγές - Αποτρέψτε τα κλικ εξαγωγής ταχείας φωτιάς

Συμβιβάσιμο του προγράμματος περιήγησης

Δοκιμασμένος και εργάζονται σε:

  • Chrome/Edge 90+
  • Firefox 88+
  • Safari 14+
  • Κινητά προγράμματα περιήγησης (iOS Safari, Chrome Mobile)

Το IE11 δεν υποστηρίζεται λόγω σύγχρονων χαρακτηριστικών JavaScript (const, λειτουργίες βέλους, async/wait, requestAnimationFrame).

Συμπέρασμα

Αυτή η ολοκληρωμένη ενίσχυση μετατρέπει στατικά διαγράμματα Γοργόνας σε διαδραστικές, εξαγώγιμες οπτικοποιήσεις. Η εφαρμογή είναι έτοιμη για παραγωγή, ανθεκτική σε ακραίες περιπτώσεις, και παρέχει μια εξαιρετική εμπειρία χρήστη.

Κλειδί των πακέτων:

  • Απομακρύνετε τη γραμμή της Γοργόνας max-width Περιορισμός των διαγραμμάτων πλήρους πλάτους
  • Διατηρήστε το πλαίσιο προβολής κατά την εξαγωγή για να συλλάβει ολόκληρο το διάγραμμα
  • Χρήση αιτήματοςAnimationFrame για συγχρονισμό αντί για αυθαίρετες καθυστερήσεις
  • Χειρισμός Cloudflare Rocket Loader με έλεγχο εξάρτησης και ξαναδοκιμάστε λογική
  • Παρέχετε πολλαπλές κλειστές μεθόδους φωτισμού (κουμπί, κάντε κλικ έξω, ESC)
  • Χρησιμοποιήστε ανάθεση εκδήλωση για την απόδοση με πολλά διαγράμματα

Χρήση του πακέτου npm

Αντί να αντιγράψετε τον κώδικα, μπορείτε τώρα να εγκαταστήσετε αυτή τη λειτουργία ως ένα πακέτο npm:

npm install @mostlylucid/mermaid-enhancements
import { init } from '@mostlylucid/mermaid-enhancements';
import '@mostlylucid/mermaid-enhancements/styles.css';

await init();

Βλέπεις; Publishing Mermaid Βελτιώσεις ως ένα πακέτο npm για πλήρη τεκμηρίωση, παραδείγματα ενσωμάτωσης πλαισίου και προηγμένες επιλογές διαμόρφωσης.

Ο πλήρης πηγαίος κώδικας είναι επίσης διαθέσιμος στο αποθετήριο αυτού του blog στο Mostlylucid/src/js/mermaid_enhancements.js και ως πακέτο open-source npm ως επί το πλείστον διαυγής ιστός/ ως επί το πλείστον διαυγής-χρήστης.

Παράδειγμα

Εδώ είναι ένα σύνθετο παράδειγμα που δείχνει την αρχιτεκτονική του συστήματος περιεχομένου αυτού του blog:

graph TB
    subgraph Client["Client Browser"]
        A[User Request] -->|HTMX| B[Blog Controller]
        B -->|Cache Miss| C[Blog Service]
        C -->|File Mode| D[Markdown Service]
        C -->|DB Mode| E[EF Core Context]
        D -->|Parse| F[Markdig Pipeline]
        F -->|Render| G[HTML + Mermaid]
        E -->|Query| H[PostgreSQL]
        H -->|Full-Text Search| I[GIN Index]
        G -->|Enhance| J[mermaid_enhancements.js]
        J -->|Initialize| K[svg-pan-zoom]
        J -->|Add| L[Control Buttons]
        L -->|Export| M[html-to-image]
    end

    subgraph Background["Background Services"]
        N[File Watcher] -->|Change Detected| O[Saves to DB]
        O -->|Trigger| P[Translation Service]
        P -->|Batch| Q[EasyNMT API]
        Q -->|12 Languages| R[Translated Files]
    end 

    style A stroke:#22c55e,stroke-width:3px,color:#4ade80
    style G stroke:#3b82f6,stroke-width:3px,color:#60a5fa
    style J stroke:#f59e0b,stroke-width:3px,color:#f59e0b
    style K stroke:#ec4899,stroke-width:3px,color:#ec4899
    style M stroke:#8b5cf6,stroke-width:3px,color:#8b5cf6

Προσπαθήστε να κάνετε κλικ στα χειριστήρια του παραπάνω διαγράμματος!

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.