From 308812a82ec6b0a372e0881bb786d2dc8cff3919 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sat, 9 Nov 2024 19:03:55 +0100
Subject: [PATCH] Fix mermaid diagram height when initially hidden (#32457)

In a hidden iframe, `document.body.clientHeight` is not reliable. Use
`IntersectionObserver` to detect the visibility change and update the
height there.

Fixes: https://github.com/go-gitea/gitea/issues/32392

<img width="885" alt="image"
src="https://github.com/user-attachments/assets/a95ef6aa-27e7-443f-9d06-400ef27919ae">

(cherry picked from commit b55a31eb6a894feb5508e350ff5e9548b2531bd6)
---
 web_src/js/markup/mermaid.js | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/web_src/js/markup/mermaid.js b/web_src/js/markup/mermaid.js
index 0549fb3e31..e420ff12a2 100644
--- a/web_src/js/markup/mermaid.js
+++ b/web_src/js/markup/mermaid.js
@@ -56,10 +56,21 @@ export async function renderMermaid() {
       btn.setAttribute('data-clipboard-text', source);
       mermaidBlock.append(btn);
 
+      const updateIframeHeight = () => {
+        iframe.style.height = `${iframe.contentWindow.document.body.clientHeight}px`;
+      };
+
+      // update height when element's visibility state changes, for example when the diagram is inside
+      // a <details> + <summary> block and the <details> block becomes visible upon user interaction, it
+      // would initially set a incorrect height and the correct height is set during this callback.
+      (new IntersectionObserver(() => {
+        updateIframeHeight();
+      }, {root: document.documentElement})).observe(iframe);
+
       iframe.addEventListener('load', () => {
         pre.replaceWith(mermaidBlock);
         mermaidBlock.classList.remove('tw-hidden');
-        iframe.style.height = `${iframe.contentWindow.document.body.clientHeight}px`;
+        updateIframeHeight();
         setTimeout(() => { // avoid flash of iframe background
           mermaidBlock.classList.remove('is-loading');
           iframe.classList.remove('tw-invisible');