/
dayekb
/
vibecoding_mermaideditor
Обзор
Документация
Войти
/
dayekb
/
vibecoding_mermaideditor
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
script.js
522 строки
17 KB
dayekb
upload files
05 сен 2025, 20:14
05 сен 2025, 20:14
e30abc8
Код
Авторство
О чём код?
// Инициализация Mermaid mermaid.initialize({ startOnLoad: false, theme: 'default', securityLevel: 'loose', fontFamily: 'Arial, sans-serif', gantt: { axisFormat: '%m/%d', leftPadding: 75, gridLineStartPadding: 10, fontSize: 11, sectionFontSize: 24, numberSectionStyles: 4 }, flowchart: { useMaxWidth: false, htmlLabels: true }, sequence: { useMaxWidth: false, wrap: true }, class: { useMaxWidth: false }, pie: { useMaxWidth: false }, journey: { useMaxWidth: false }, timeline: { useMaxWidth: false } }); // Глобальные переменные let currentDiagram = null; let isRendering = false; let currentZoom = 1.5; // Примеры диаграмм const examples = { flowchart: `graph TD A[Начало] --> B{Условие} B -->|Да| C[Действие 1] B -->|Нет| D[Действие 2] C --> E[Конец] D --> E`, sequence: `sequenceDiagram participant A as Пользователь participant B as Система participant C as База данных A->>B: Запрос данных B->>C: SELECT * FROM users C-->>B: Результат запроса B-->>A: Отображение данных`, class: `classDiagram class Animal { +String name +int age +makeSound() } class Dog { +String breed +bark() } class Cat { +String color +meow() } Animal <|-- Dog Animal <|-- Cat`, gantt: `gantt title План разработки проекта dateFormat YYYY-MM-DD section Фаза 1 Анализ требований :done, des1, 2024-01-01, 2024-01-07 Проектирование :active, des2, 2024-01-08, 2024-01-14 section Фаза 2 Разработка : des3, 2024-01-15, 2024-01-28 Тестирование : des4, 2024-01-29, 2024-02-04`, pie: `pie title Распределение времени "Разработка" : 40 "Тестирование" : 25 "Документация" : 15 "Встречи" : 20`, gitgraph: `journey title Процесс разработки section Планирование Анализ требований: 5: Планирование Создание архитектуры: 4: Планирование section Разработка Реализация функций: 5: Разработка Unit тестирование: 4: Разработка section Интеграция API интеграция: 3: Интеграция UI разработка: 4: Интеграция section Тестирование Интеграционные тесты: 4: Тестирование Пользовательские тесты: 5: Тестирование section Релиз Деплой в продакшн: 5: Релиз Мониторинг: 3: Релиз`, timeline: `timeline title История разработки section 2024 Q1 Планирование проекта : Анализ требований : Создание архитектуры Начало разработки : Настройка CI/CD : Первые коммиты section 2024 Q2 Основная разработка : Реализация функций : Unit тестирование Интеграция : API интеграция : UI разработка` }; // DOM элементы const mermaidCode = document.getElementById('mermaidCode'); const mermaidPreview = document.getElementById('mermaidPreview'); const clearBtn = document.getElementById('clearBtn'); const loadExampleBtn = document.getElementById('loadExampleBtn'); const exportPngBtn = document.getElementById('exportPngBtn'); const exportSvgBtn = document.getElementById('exportSvgBtn'); const zoomInBtn = document.getElementById('zoomInBtn'); const zoomOutBtn = document.getElementById('zoomOutBtn'); const resetZoomBtn = document.getElementById('resetZoomBtn'); const zoomLevel = document.getElementById('zoomLevel'); const themeToggle = document.getElementById('themeToggle'); const exampleBtns = document.querySelectorAll('.example-btn'); // Инициализация document.addEventListener('DOMContentLoaded', function() { initializeApp(); }); function initializeApp() { // Обработчики событий mermaidCode.addEventListener('input', debounce(renderDiagram, 500)); clearBtn.addEventListener('click', clearEditor); loadExampleBtn.addEventListener('click', loadRandomExample); exportPngBtn.addEventListener('click', exportToPng); exportSvgBtn.addEventListener('click', exportToSvg); zoomInBtn.addEventListener('click', zoomIn); zoomOutBtn.addEventListener('click', zoomOut); resetZoomBtn.addEventListener('click', resetZoom); themeToggle.addEventListener('click', toggleTheme); // Обработчики для примеров exampleBtns.forEach(btn => { btn.addEventListener('click', function() { const type = this.dataset.type; loadExample(type); }); }); // Загрузка сохраненной темы loadTheme(); // Первоначальный рендеринг renderDiagram(); } // Функция рендеринга диаграммы async function renderDiagram() { const code = mermaidCode.value.trim(); if (!code) { mermaidPreview.innerHTML = '<div class="loading">Введите код Mermaid...</div>'; return; } if (isRendering) return; isRendering = true; try { mermaidPreview.innerHTML = '<div class="loading">Рендеринг диаграммы...</div>'; // Очистка предыдущей диаграммы const existingDiagram = mermaidPreview.querySelector('.mermaid'); if (existingDiagram) { existingDiagram.remove(); } // Создание уникального ID для диаграммы const diagramId = 'mermaid-' + Date.now(); // Рендеринг диаграммы const { svg } = await mermaid.render(diagramId, code); // Вставка SVG в контейнер mermaidPreview.innerHTML = ''; const svgContainer = document.createElement('div'); svgContainer.className = 'mermaid'; svgContainer.innerHTML = svg; mermaidPreview.appendChild(svgContainer); // Принудительное обновление размеров setTimeout(() => { const svgElement = svgContainer.querySelector('svg'); if (svgElement) { svgElement.style.width = '100%'; svgElement.style.height = 'auto'; svgElement.style.minWidth = '600px'; svgElement.style.minHeight = '400px'; } // Применение текущего масштаба applyZoom(); }, 100); currentDiagram = svgContainer; // Удаление сообщения об ошибке, если оно есть const errorMsg = mermaidPreview.querySelector('.error'); if (errorMsg) { errorMsg.remove(); } } catch (error) { console.error('Ошибка рендеринга Mermaid:', error); showError('Ошибка в синтаксисе Mermaid: ' + error.message); } finally { isRendering = false; } } // Функция показа ошибки function showError(message) { mermaidPreview.innerHTML = `<div class="error">${message}</div>`; } // Функция очистки редактора function clearEditor() { mermaidCode.value = ''; mermaidPreview.innerHTML = '<div class="loading">Введите код Mermaid...</div>'; currentDiagram = null; } // Функция загрузки случайного примера function loadRandomExample() { const types = Object.keys(examples); const randomType = types[Math.floor(Math.random() * types.length)]; loadExample(randomType); } // Функция загрузки примера по типу function loadExample(type) { if (examples[type]) { mermaidCode.value = examples[type]; renderDiagram(); } } // Функция экспорта в PNG async function exportToPng() { if (!currentDiagram) { alert('Нет диаграммы для экспорта'); return; } try { exportPngBtn.textContent = 'Экспорт...'; exportPngBtn.disabled = true; // Создание контейнера для экспорта const exportContainer = document.createElement('div'); exportContainer.className = 'export-container'; exportContainer.style.position = 'absolute'; exportContainer.style.left = '-9999px'; exportContainer.style.top = '-9999px'; exportContainer.style.background = 'white'; exportContainer.style.padding = '20px'; // Клонирование диаграммы const clonedDiagram = currentDiagram.cloneNode(true); exportContainer.appendChild(clonedDiagram); document.body.appendChild(exportContainer); // Конвертация в canvas const canvas = await html2canvas(exportContainer, { backgroundColor: 'white', scale: 2, useCORS: true }); // Создание ссылки для скачивания canvas.toBlob(function(blob) { const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'mermaid-diagram.png'; a.click(); URL.revokeObjectURL(url); }); // Очистка document.body.removeChild(exportContainer); } catch (error) { console.error('Ошибка экспорта PNG:', error); alert('Ошибка при экспорте в PNG: ' + error.message); } finally { exportPngBtn.textContent = '📷 PNG'; exportPngBtn.disabled = false; } } // Функция экспорта в SVG function exportToSvg() { if (!currentDiagram) { alert('Нет диаграммы для экспорта'); return; } try { const svgElement = currentDiagram.querySelector('svg'); if (!svgElement) { alert('SVG элемент не найден'); return; } // Клонирование SVG const clonedSvg = svgElement.cloneNode(true); // Установка стилей для экспорта clonedSvg.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); clonedSvg.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink'); // Создание SVG строки const svgString = new XMLSerializer().serializeToString(clonedSvg); const svgBlob = new Blob([svgString], { type: 'image/svg+xml;charset=utf-8' }); // Скачивание файла saveAs(svgBlob, 'mermaid-diagram.svg'); } catch (error) { console.error('Ошибка экспорта SVG:', error); alert('Ошибка при экспорте в SVG: ' + error.message); } } // Функция debounce для оптимизации function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // Обработка ошибок window.addEventListener('error', function(e) { console.error('Глобальная ошибка:', e.error); }); // Обработка необработанных промисов window.addEventListener('unhandledrejection', function(e) { console.error('Необработанное отклонение промиса:', e.reason); }); // Горячие клавиши document.addEventListener('keydown', function(e) { // Ctrl+Enter для рендеринга if (e.ctrlKey && e.key === 'Enter') { e.preventDefault(); renderDiagram(); } // Ctrl+S для сохранения PNG if (e.ctrlKey && e.key === 's') { e.preventDefault(); exportToPng(); } // Ctrl+Plus для увеличения if (e.ctrlKey && (e.key === '+' || e.key === '=')) { e.preventDefault(); zoomIn(); } // Ctrl+Minus для уменьшения if (e.ctrlKey && e.key === '-') { e.preventDefault(); zoomOut(); } // Ctrl+0 для сброса зума if (e.ctrlKey && e.key === '0') { e.preventDefault(); resetZoom(); } }); // Функции для работы с темой function toggleTheme() { const currentTheme = document.documentElement.getAttribute('data-theme'); const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; document.documentElement.setAttribute('data-theme', newTheme); localStorage.setItem('theme', newTheme); // Обновление текста кнопки themeToggle.textContent = newTheme === 'dark' ? '☀️ Светлая тема' : '🌙 Темная тема'; // Обновление Mermaid темы updateMermaidTheme(newTheme); } function loadTheme() { const savedTheme = localStorage.getItem('theme') || 'light'; document.documentElement.setAttribute('data-theme', savedTheme); themeToggle.textContent = savedTheme === 'dark' ? '☀️ Светлая тема' : '🌙 Темная тема'; updateMermaidTheme(savedTheme); } function updateMermaidTheme(theme) { const themeConfig = theme === 'dark' ? 'dark' : 'default'; mermaid.initialize({ startOnLoad: false, theme: themeConfig, securityLevel: 'loose', fontFamily: 'Arial, sans-serif', gantt: { axisFormat: '%m/%d', leftPadding: 75, gridLineStartPadding: 10, fontSize: 11, sectionFontSize: 24, numberSectionStyles: 4 }, flowchart: { useMaxWidth: false, htmlLabels: true }, sequence: { useMaxWidth: false, wrap: true }, class: { useMaxWidth: false }, pie: { useMaxWidth: false }, journey: { useMaxWidth: false }, timeline: { useMaxWidth: false } }); // Перерендеринг диаграммы с новой темой if (mermaidCode.value.trim()) { renderDiagram(); } } // Функции масштабирования function zoomIn() { currentZoom = Math.min(currentZoom + 0.2, 3.0); applyZoom(); } function zoomOut() { currentZoom = Math.max(currentZoom - 0.2, 0.5); applyZoom(); } function resetZoom() { currentZoom = 1.5; applyZoom(); } function applyZoom() { if (currentDiagram) { currentDiagram.style.transform = `scale(${currentZoom})`; currentDiagram.style.transformOrigin = 'center'; // Обновляем индикатор зума if (zoomLevel) { zoomLevel.textContent = Math.round(currentZoom * 100) + '%'; } // Обновляем размеры контейнера для корректной прокрутки const previewContainer = document.getElementById('mermaidPreview'); if (previewContainer) { if (currentZoom > 1.2) { previewContainer.style.overflow = 'auto'; previewContainer.style.alignItems = 'flex-start'; previewContainer.style.justifyContent = 'flex-start'; } else { previewContainer.style.overflow = 'visible'; previewContainer.style.alignItems = 'center'; previewContainer.style.justifyContent = 'center'; } } } } // Информация о горячих клавишах console.log('Горячие клавиши:'); console.log('Ctrl+Enter - Рендеринг диаграммы'); console.log('Ctrl+S - Экспорт в PNG'); console.log('Ctrl++ - Увеличение зума'); console.log('Ctrl+- - Уменьшение зума'); console.log('Ctrl+0 - Сброс зума');