From 2f28902202a3ea03b540dc9481c402a223e1e101 Mon Sep 17 00:00:00 2001 From: Daniil Karpenko <26330178+dankarization@users.noreply.github.com> Date: Fri, 22 May 2026 18:24:00 +0400 Subject: [PATCH] Prototype Mermaid embeds for markdown preview --- .../markdown/iv_markdown_prepare_blocks.cpp | 164 +++++++++++++++++- 1 file changed, 162 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/iv/markdown/iv_markdown_prepare_blocks.cpp b/Telegram/SourceFiles/iv/markdown/iv_markdown_prepare_blocks.cpp index 883338883bcc8..977f0265e85ac 100644 --- a/Telegram/SourceFiles/iv/markdown/iv_markdown_prepare_blocks.cpp +++ b/Telegram/SourceFiles/iv/markdown/iv_markdown_prepare_blocks.cpp @@ -55,6 +55,161 @@ constexpr auto kMaxVisualQuoteDepth = 3; return text; } +[[nodiscard]] PreparedPlaceholderBlockId GeneratePreparedPlaceholderBlockId( + PrepareState *state) { + return PreparedPlaceholderBlockId(++state->nextGeneratedId); +} + +[[nodiscard]] QByteArray StoreMarkdownEmbedHtml( + QByteArray html, + PrepareState *state) { + const auto resourceId = QByteArray("markdown-embed/") + + QByteArray::number(++state->nextGeneratedId) + + ".html"; + state->result.embedHtmlResources.emplace(resourceId, std::move(html)); + return resourceId; +} + +[[nodiscard]] QByteArray MermaidEmbedHtml(const QString &source) { + const auto escaped = source.toHtmlEscaped(); + const auto body = uR"( + + + + + + + + + +
+
)"_q + escaped + uR"(
+
+ + + +)"_q; + return body.toUtf8(); +} + +[[nodiscard]] PreparedBlock PrepareMermaidBlock( + const MarkdownNode &node, + PrepareState *state) { + auto block = PreparedBlock(); + block.kind = PreparedBlockKind::Placeholder; + block.supplementary = true; + block.placeholder.label = u"Render Mermaid diagram"_q; + block.placeholder.copyText = node.text; + block.placeholder.id = GeneratePreparedPlaceholderBlockId(state); + block.placeholder.embed = EmbedRequest{ + .resourceId = StoreMarkdownEmbedHtml( + MermaidEmbedHtml(node.text), + state), + .width = 900, + .height = 520, + .fullWidth = true, + .fixedHeight = false, + .allowScrolling = true, + }; + return block; +} + [[nodiscard]] int FlowFormulaTextSize( PreparedBlockKind kind, int headingLevel, @@ -325,7 +480,12 @@ void AppendRichBlock( return block; } -[[nodiscard]] PreparedBlock PrepareCodeBlock(const MarkdownNode &node) { +[[nodiscard]] PreparedBlock PrepareCodeBlock( + const MarkdownNode &node, + PrepareState *state) { + if (FirstInfoToken(node.info).compare(u"mermaid"_q, Qt::CaseInsensitive) == 0) { + return PrepareMermaidBlock(node, state); + } auto block = PreparedBlock(); block.kind = PreparedBlockKind::CodeBlock; block.text.text = StripOneTrailingNewline(node.text); @@ -733,7 +893,7 @@ void PrepareFootnotes(PrepareState *state) { case NodeKind::FootnoteDefinition: return {}; case NodeKind::CodeBlock: - return { PrepareCodeBlock(node) }; + return { PrepareCodeBlock(node, state) }; case NodeKind::ThematicBreak: return { PrepareRuleBlock() }; case NodeKind::List: {