add mermaid

This commit is contained in:
thek4n 2026-02-20 03:14:32 +03:00
parent d6a1a3e67b
commit 700fc80bc5

View File

@ -218,52 +218,67 @@ fn markdown_to_html(markdown: &str, ss: &SyntaxSet, ts: &ThemeSet, file_path: &s
Event::End(Tag::CodeBlock(_)) => { Event::End(Tag::CodeBlock(_)) => {
in_code_block = false; in_code_block = false;
// Определяем отображаемое имя языка // Проверка на Mermaid
let lang_display = current_lang.as_deref().unwrap_or("text"); let is_mermaid = current_lang.as_deref() == Some("mermaid");
// Экранируем имя языка для HTML атрибута и текста if is_mermaid {
let lang_escaped = escape_html(lang_display); // Для Mermaid просто экранируем контент и оборачиваем в div
// Кнопка копирования тоже нужна
let escaped_code = escape_html(&current_code);
let mermaid_html = format!(
r#"<div class="code-block-wrapper mermaid-wrapper">
<div class="code-header">
<span class="code-lang">Mermaid Diagram</span>
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
</div>
<div class="mermaid" style="background: transparent; padding: 20px; text-align: center;">{}</div>
</div>"#,
escaped_code
);
processed_events.push(Event::Html(mermaid_html.into()));
} else {
// Обычная обработка кода с подсветкой
let lang_display = current_lang.as_deref().unwrap_or("text");
let lang_escaped = escape_html(lang_display);
// Подсветка синтаксиса (построчно) let highlighted_html = if let Some(lang) = &current_lang {
let highlighted_html = if let Some(lang) = &current_lang { if let Some(syntax) = ss.find_syntax_by_token(lang) {
if let Some(syntax) = ss.find_syntax_by_token(lang) { let mut h = HighlightLines::new(syntax, theme);
let mut h = HighlightLines::new(syntax, theme); let mut result_html = String::new();
let mut result_html = String::new();
for line in current_code.lines() { for line in current_code.lines() {
let line_with_newline = format!("{}\n", line); let line_with_newline = format!("{}\n", line);
match h.highlight_line(&line_with_newline, ss) { match h.highlight_line(&line_with_newline, ss) {
Ok(regions) => { Ok(regions) => {
let html_line = styled_line_to_highlighted_html(&regions[..], IncludeBackground::No) let html_line = styled_line_to_highlighted_html(&regions[..], IncludeBackground::No)
.unwrap_or_else(|_| escape_html(&line_with_newline)); .unwrap_or_else(|_| escape_html(&line_with_newline));
result_html.push_str(&html_line); result_html.push_str(&html_line);
}, },
Err(_) => result_html.push_str(&escape_html(&line_with_newline)), Err(_) => result_html.push_str(&escape_html(&line_with_newline)),
}
} }
result_html
} else {
escape_html(&current_code)
} }
result_html
} else { } else {
escape_html(&current_code) escape_html(&current_code)
} };
} else {
escape_html(&current_code)
};
// Формируем HTML с заголовком и кнопкой копирования let code_container = format!(
// Мы экранируем current_code еще раз для data-атрибута, хотя для копирования будем брать текст из pre r#"<div class="code-block-wrapper">
let code_container = format!( <div class="code-header">
r#"<div class="code-block-wrapper"> <span class="code-lang">{}</span>
<div class="code-header"> <button class="copy-btn" onclick="copyCode(this)">Copy</button>
<span class="code-lang">{}</span> </div>
<button class="copy-btn" onclick="copyCode(this)">Copy</button> <pre style="margin: 0; border-radius: 0 0 6px 6px;"><code>{}</code></pre>
</div> </div>"#,
<pre style="margin: 0; border-radius: 0 0 6px 6px;"><code>{}</code></pre> lang_escaped,
</div>"#, highlighted_html
lang_escaped, );
highlighted_html
);
processed_events.push(Event::Html(code_container.into())); processed_events.push(Event::Html(code_container.into()));
}
}, },
Event::Text(text) if in_code_block => { Event::Text(text) if in_code_block => {
current_code.push_str(&text); current_code.push_str(&text);
@ -288,6 +303,8 @@ fn markdown_to_html(markdown: &str, ss: &SyntaxSet, ts: &ThemeSet, file_path: &s
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Markdown Preview</title> <title>Markdown Preview</title>
<!-- Подключаем Mermaid JS -->
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
<style> <style>
body {{ background-color: #121212; color: #e0e0e0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; margin: 0; padding: 40px 20px; display: flex; justify-content: center; line-height: 1.6; }} body {{ background-color: #121212; color: #e0e0e0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; margin: 0; padding: 40px 20px; display: flex; justify-content: center; line-height: 1.6; }}
.content {{ max-width: 800px; width: 100%; background-color: #1e1e1e; padding: 40px; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.5); }} .content {{ max-width: 800px; width: 100%; background-color: #1e1e1e; padding: 40px; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.5); }}
@ -356,6 +373,16 @@ fn markdown_to_html(markdown: &str, ss: &SyntaxSet, ts: &ThemeSet, file_path: &s
color: inherit; color: inherit;
}} }}
/* Специфичные стили для Mermaid */
.mermaid-wrapper .mermaid {{
background-color: #f9f9f9; /* Светлый фон для контраста диаграммы */
border-radius: 0 0 6px 6px;
display: flex;
justify-content: center;
}}
/* Принудительная темная тема для SVG внутри mermaid, если нужно,
но лучше использовать конфиг mermaid.init ниже */
#status {{ position: fixed; top: 10px; right: 10px; padding: 5px 10px; border-radius: 4px; font-size: 12px; font-weight: bold; }} #status {{ position: fixed; top: 10px; right: 10px; padding: 5px 10px; border-radius: 4px; font-size: 12px; font-weight: bold; }}
.connected {{ background-color: #2ecc71; color: #000; }} .connected {{ background-color: #2ecc71; color: #000; }}
.disconnected {{ background-color: #e74c3c; color: #fff; }} .disconnected {{ background-color: #e74c3c; color: #fff; }}
@ -387,15 +414,14 @@ fn markdown_to_html(markdown: &str, ss: &SyntaxSet, ts: &ThemeSet, file_path: &s
connect(); connect();
function copyCode(button) {{ function copyCode(button) {{
// Находим обертку, затем pre внутри неё
const wrapper = button.closest('.code-block-wrapper'); const wrapper = button.closest('.code-block-wrapper');
if (!wrapper) return; if (!wrapper) return;
const pre = wrapper.querySelector('pre'); // Для mermaid берем текст из div.mermaid, для кода из pre
if (!pre) return; const target = wrapper.querySelector('pre') || wrapper.querySelector('.mermaid');
if (!target) return;
// Получаем текстовое содержимое (без HTML тегов подсветки) const codeText = target.innerText;
const codeText = pre.innerText;
navigator.clipboard.writeText(codeText).then(() => {{ navigator.clipboard.writeText(codeText).then(() => {{
const originalText = button.innerText; const originalText = button.innerText;
@ -413,6 +439,13 @@ fn markdown_to_html(markdown: &str, ss: &SyntaxSet, ts: &ThemeSet, file_path: &s
button.innerText = 'Error'; button.innerText = 'Error';
}}); }});
}} }}
// Инициализация Mermaid с темной темой
mermaid.initialize({{
startOnLoad: true,
theme: 'dark',
securityLevel: 'loose',
}});
</script> </script>
</body> </body>
</html>"#, </html>"#,