diff --git a/packages/merman/src/markdown.ts b/packages/merman/src/markdown.ts index 5b550a14f2d..86c8a635c07 100644 --- a/packages/merman/src/markdown.ts +++ b/packages/merman/src/markdown.ts @@ -288,10 +288,30 @@ export function createMermaidCodeBlockRenderer( } catch (error) { if (error instanceof MermaidSyntaxError) { const previous = key ? lastGood.get(key) : undefined - if (!previous || previous.kind !== kind) return undefined - const diagram = new StaticDiagramRenderable(ctx, previous) - claimLastGood(key!, previous, diagram, lastGood) - return diagram + if (previous?.kind === kind) { + const diagram = new StaticDiagramRenderable(ctx, previous) + claimLastGood(key!, previous, diagram, lastGood) + return diagram + } + + const lines = token.text.split("\n") + if (error.lineNumber <= 2 || lines.slice(error.lineNumber).some((line) => line.trim())) return undefined + + try { + const prepared = prepareDiagram( + kind, + lines.slice(0, error.lineNumber - 1).join("\n"), + options, + layoutMaxWidth, + ) + if (!prepared.height) return undefined + const diagram = new StaticDiagramRenderable(ctx, prepared) + if (key) claimLastGood(key, prepared, diagram, lastGood) + return diagram + } catch (error) { + if (error instanceof MermaidSyntaxError || error instanceof DiagramCanvasSizeError) return undefined + throw error + } } if (error instanceof DiagramCanvasSizeError) return undefined throw error diff --git a/packages/merman/src/test/markdown.test.ts b/packages/merman/src/test/markdown.test.ts index 878a83802eb..c0a98670f18 100644 --- a/packages/merman/src/test/markdown.test.ts +++ b/packages/merman/src/test/markdown.test.ts @@ -211,6 +211,32 @@ flowchart LR expect(testRenderer.captureCharFrame()).toContain("Current") }) +test("renders the valid prefix of an interrupted Mermaid fence", async () => { + const testRenderer = await createTestRenderer({ width: 100, height: 18 }) + renderer = testRenderer.renderer + const markdown = new MarkdownRenderable(renderer, { + id: "markdown-interrupted-mermaid", + content: `\`\`\`mermaid +flowchart TD + A[Resolve Project] --> B[Current directory] + B --> C[Search ancestors] + C --> D[Marker found] + C --> E[No marker found] + E --> G[Project root is`, + syntaxStyle, + internalBlockMode: "top-level", + renderNode: createMermaidMarkdownRenderer(renderer), + }) + + renderer.root.add(markdown) + await renderMarkdown(markdown, testRenderer.renderOnce) + + const frame = testRenderer.captureCharFrame() + expect(frame).toContain("Resolve Project") + expect(frame).toContain("No marker found") + expect(frame).not.toContain("flowchart TD") +}) + test("renders a Mermaid sequence fence inside MarkdownRenderable", async () => { const testRenderer = await createTestRenderer({ width: 80, height: 14 }) renderer = testRenderer.renderer diff --git a/packages/tui/src/routes/session/index.tsx b/packages/tui/src/routes/session/index.tsx index 2a605252e3e..e579b43f7fc 100644 --- a/packages/tui/src/routes/session/index.tsx +++ b/packages/tui/src/routes/session/index.tsx @@ -1666,7 +1666,11 @@ function SessionPartView(props: { partRef: PartRef; message: (messageID: string) {(item) => ( - +