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

<!--category-- Mermaid, Javascript, SVG, DaisyUI, Tailwind -->
<datetime class="hidden">2025-11-07T11:15</datetime>

# Εισαγωγή

> **npm πακέτο διαθέσιμο:** Η εφαρμογή αυτή είναι τώρα διαθέσιμη ως [@mostly διαυγής/mermaid-enhancements](https://www.npmjs.com/package/@mostlylucid/mermaid-enhancements) - ένα έτοιμο για την παραγωγή πακέτο npm. [Publishing Mermaid Βελτιώσεις ως ένα πακέτο npm](/blog/publishingmermaidenhancementsnpm) για λεπτομέρειες σχετικά με το πώς να το χρησιμοποιήσετε στα έργα σας.

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

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

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

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

![mermaid_pan_zoom.png](mermaid_pan_zoom.png)

[TOC]

# Το Πρόβλημα

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

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

# Η Λύση

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

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

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

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

```mermaid
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:

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

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

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

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

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

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

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

```javascript
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 αλληλεπίδραση:

```javascript
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:

```javascript
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 παρέχει μια καταδυτική εμπειρία προβολής:

```javascript
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);
}
```

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

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

```javascript
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, η οποία αποτρέπει την πλήρη οθόνη πλάτους. Αφαίρεση αυτού είναι απαραίτητη για την κατάλληλη συμπεριφορά ανταπόκρισης.

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

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

```javascript
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, σπάζοντας την αρχικοποίηση.

```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:

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

## Στυλ με Tailwind/DaisyUI

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

```css
/* 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:

```bash
npm install @mostlylucid/mermaid-enhancements
```

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

await init();
```

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

Ο πλήρης πηγαίος κώδικας είναι επίσης διαθέσιμος στο αποθετήριο αυτού του blog στο `Mostlylucid/src/js/mermaid_enhancements.js` και ως πακέτο open-source npm [ως επί το πλείστον διαυγής ιστός/ ως επί το πλείστον διαυγής-χρήστης](https://github.com/scottgal/mostlylucidweb/tree/main/mostlylucid-mermaid).

## Παράδειγμα

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

```mermaid
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
```

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