diff --git a/packages/app/e2e/browser/mermaid-fullscreen.spec.ts b/packages/app/e2e/browser/mermaid-fullscreen.spec.ts
index 72b05ed83e..27eacf1b7b 100644
--- a/packages/app/e2e/browser/mermaid-fullscreen.spec.ts
+++ b/packages/app/e2e/browser/mermaid-fullscreen.spec.ts
@@ -1,3 +1,4 @@
+import { expect } from "@playwright/test";
import { test } from "../support/fixtures";
import {
closeDiagramFullscreen,
@@ -21,6 +22,38 @@ const MERMAID_DIAGRAM = [
"```",
].join("\n");
+test("bounds a tall inline diagram", async ({ page }) => {
+ test.setTimeout(120_000);
+ const diagram = [
+ "```mermaid",
+ "flowchart TD",
+ ...Array.from({ length: 30 }, (_, i) => `N${i} --> N${i + 1}`),
+ "```",
+ ].join("\n");
+ const agent = await seedMockAgentWorkspace({
+ repoPrefix: "mermaid-tall-",
+ title: "Tall Mermaid",
+ featureValues: {
+ mockStreamingAssistantResponse: diagram,
+ mockStreamingAssistantIntervalMs: 10,
+ },
+ });
+ try {
+ await openAgentRoute(page, agent);
+ await requestDiagram(agent);
+ await waitForDiagramTurnToComplete(agent);
+ await expectCompletedDiagram(page, ["N0", "N30"]);
+ const viewport = page.getByTestId("mermaid-viewport").last();
+ expect((await viewport.boundingBox())!.height).toBe(480);
+ await page.screenshot({ path: test.info().outputPath("tall-preview.png") });
+ await openDiagramFullscreen(page);
+ await expectFullscreenDiagram(page, ["N0", "N30"]);
+ await closeDiagramFullscreen(page);
+ } finally {
+ await agent.cleanup();
+ }
+});
+
test("opens a Mermaid diagram fullscreen and closes it again", async ({ page }) => {
test.setTimeout(120_000);
@@ -40,6 +73,37 @@ test("opens a Mermaid diagram fullscreen and closes it again", async ({ page })
await expectCompletedDiagram(page, ["Start", "Ship"]);
});
+ await test.step("Source toggles preserve the iframe and exclude hidden controls", async () => {
+ const viewport = page.getByTestId("mermaid-viewport").last();
+ const iframe = await viewport.locator("iframe").elementHandle();
+ for (let i = 0; i < 3; i++) {
+ await viewport.hover();
+ await page.getByRole("button", { name: "View source" }).last().click();
+ const viewDiagram = page.getByRole("button", { name: "View diagram" }).last();
+ if (i === 0) await page.screenshot({ path: test.info().outputPath("source.png") });
+ await viewDiagram.click();
+ await expect(viewDiagram).toHaveCount(0);
+ if (i === 0) await page.screenshot({ path: test.info().outputPath("diagram.png") });
+ await expect(page.getByTestId("mermaid-fullscreen-viewport")).toHaveCount(0);
+ expect(
+ await viewport.locator("iframe").evaluate((node, original) => node === original, iframe),
+ ).toBe(true);
+ }
+ await viewport.hover();
+ await page.getByRole("button", { name: "View source" }).last().click();
+ await viewport.getByTestId("mermaid-fullscreen").focus();
+ await expect(viewport.getByTestId("mermaid-fullscreen")).not.toBeFocused();
+ await page.getByRole("button", { name: "View diagram" }).last().click();
+ const box = await viewport.boundingBox();
+ expect(box!.height).toBeGreaterThanOrEqual(56);
+ expect(box!.height).toBeLessThan(480);
+ await viewport.hover();
+ await page.getByRole("button", { name: "Zoom in" }).last().click();
+ await expect(page.getByRole("button", { name: "Reset view" }).last()).toBeEnabled();
+ await page.getByRole("button", { name: "Reset view" }).last().click();
+ await expect(page.getByRole("button", { name: "Reset view" }).last()).toBeDisabled();
+ });
+
await test.step("The toolbar opens the diagram fullscreen", async () => {
await openDiagramFullscreen(page);
await expectFullscreenDiagram(page, ["Start", "Review", "Ship"]);
diff --git a/packages/app/src/components/markdown/fence/mermaid/host.web.tsx b/packages/app/src/components/markdown/fence/mermaid/host.web.tsx
index 9370bc90c0..94d6ccc666 100644
--- a/packages/app/src/components/markdown/fence/mermaid/host.web.tsx
+++ b/packages/app/src/components/markdown/fence/mermaid/host.web.tsx
@@ -72,7 +72,7 @@ function MermaidFenceHostImpl({
() => [
getDiagramBoxStyle(textStyle),
containerStyle,
- { height: runtimeHeight, minHeight: MIN_DIAGRAM_BOX_HEIGHT },
+ { height: Math.min(runtimeHeight, MAX_PREVIEW_HEIGHT), minHeight: MIN_DIAGRAM_BOX_HEIGHT },
],
[runtimeHeight, textStyle],
);
@@ -111,20 +111,22 @@ function MermaidFenceHostImpl({
) : null}
) : null}
-