mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-29 19:43:41 +00:00
fix(tui): preserve interrupted Mermaid diagrams (#45102)
This commit is contained in:
parent
690ad8e8bd
commit
73d7b1d4c1
3 changed files with 57 additions and 7 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1666,7 +1666,11 @@ function SessionPartView(props: { partRef: PartRef; message: (messageID: string)
|
|||
{(item) => (
|
||||
<Switch>
|
||||
<Match when={item().type === "text"}>
|
||||
<TextPart part={item() as SessionMessageAssistantText} last={false} />
|
||||
<TextPart
|
||||
part={item() as SessionMessageAssistantText}
|
||||
message={message() as SessionMessageAssistant}
|
||||
last={false}
|
||||
/>
|
||||
</Match>
|
||||
<Match when={item().type === "reasoning"}>
|
||||
<ReasoningPart
|
||||
|
|
@ -2483,7 +2487,7 @@ function ReasoningHeader(props: {
|
|||
)
|
||||
}
|
||||
|
||||
function TextPart(props: { last: boolean; part: SessionMessageAssistantText }) {
|
||||
function TextPart(props: { last: boolean; part: SessionMessageAssistantText; message: SessionMessageAssistant }) {
|
||||
const ctx = use()
|
||||
const theme = useTheme()
|
||||
const { currentSyntax: syntax } = useThemes()
|
||||
|
|
@ -2493,7 +2497,7 @@ function TextPart(props: { last: boolean; part: SessionMessageAssistantText }) {
|
|||
<box paddingLeft={3} flexShrink={0}>
|
||||
<markdown
|
||||
syntaxStyle={syntax()}
|
||||
streaming={true}
|
||||
streaming={props.message.time.completed === undefined}
|
||||
internalBlockMode="top-level"
|
||||
content={props.part.text.trim()}
|
||||
tableOptions={{ style: "grid", cellPaddingX: 1 }}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue