mini: monochrome rendered markdown only (#38656)

This commit is contained in:
Simon Klee 2026-07-24 11:14:48 +02:00 committed by GitHub
commit 4184149b90
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 322 additions and 45 deletions

View file

@ -231,29 +231,28 @@ describe("run entry body", () => {
})
test("promotes subagent results to markdown and falls back to structured summaries", () => {
expect(
entryBody(
toolCommit({
tool: "subagent",
state: {
status: "completed",
input: {
description: "Inspect reducer",
agent: "explore",
},
content: [{ type: "text", text: "# Findings\n\n- Footer stays live" }],
metadata: {
sessionID: "ses-child-1",
status: "completed",
output: "# Findings\n\n- Footer stays live",
},
},
}),
),
).toEqual({
const result = toolCommit({
tool: "subagent",
state: {
status: "completed",
input: {
description: "Inspect reducer",
agent: "explore",
},
content: [{ type: "text", text: "# Findings\n\n- Footer stays live" }],
metadata: {
sessionID: "ses-child-1",
status: "completed",
output: "# Findings\n\n- Footer stays live",
},
},
})
const markdown = {
type: "markdown",
content: "# Findings\n\n- Footer stays live",
})
} as const
expect(entryBody(result)).toEqual(markdown)
expect(entryBody(result, { mono: true })).toEqual(markdown)
expect(
structured(

View file

@ -381,6 +381,83 @@ test("run entry content updates when live commit text changes", async () => {
}
})
test("run entry content preserves monochrome markdown grammar", async () => {
const [commit, setCommit] = createSignal<StreamCommit>({
kind: "assistant",
text: "• literal\n\n———\n\narrow →",
phase: "progress",
source: "assistant",
messageID: "msg-1",
partID: "part-1",
})
const app = await testRender(
() => (
<box width={60} height={8}>
<RunEntryContent commit={commit()} theme={RUN_THEME_FALLBACK} opts={{ mono: true }} />
</box>
),
{ width: 60, height: 8 },
)
try {
await app.renderOnce()
const rows = app
.captureCharFrame()
.split("\n")
.map((row) => row.trimEnd())
expect(rows).toContain("* literal")
expect(rows).toContain("------")
expect(rows).toContain("arrow ->")
expect(rows.join("\n")).not.toMatch(/[^\x00-\x7f]/)
setCommit({ ...commit(), text: "- Café\n- arrow →\n- third …" })
await app.renderOnce()
expect(app.captureCharFrame()).toContain("- arrow ->")
expect(app.captureCharFrame()).toContain("- third ...")
setCommit({ ...commit(), text: "| A | B |\n| - | - |\n| Café | → |" })
await app.renderOnce()
expect(app.captureCharFrame()).toContain("Caf?")
expect(app.captureCharFrame()).toContain("->")
setCommit({ ...commit(), text: "| A | B |\n| - | - |\n| Café | … |" })
await app.renderOnce()
expect(app.captureCharFrame()).toContain("...")
setCommit({ ...commit(), text: "```\nCafé → …\n```" })
await app.renderOnce()
expect(app.captureCharFrame()).toContain("Caf? -> ...")
expect(app.captureCharFrame()).not.toMatch(/[^\x00-\x7f]/)
} finally {
app.renderer.destroy()
}
})
test("run entry content eagerly renders final monochrome markdown", async () => {
const app = await testRender(
() => (
<box width={60} height={6}>
<RunEntryContent
commit={{ kind: "tool", text: "", phase: "final", source: "tool", tool: "subagent" }}
body={{ type: "markdown", content: "# Café →\n\n```markdown\nCafé →\n```" }}
theme={RUN_THEME_FALLBACK}
opts={{ mono: true }}
/>
</box>
),
{ width: 60, height: 6 },
)
try {
await app.renderOnce()
const frame = app.captureCharFrame()
expect(frame).toContain("# Caf? ->")
expect(frame).toContain("Caf? ->")
expect(frame).not.toMatch(/[^\x00-\x7f]/)
} finally {
app.renderer.destroy()
}
})
test("direct command panel renders grouped actions without catalog commands", async () => {
const [commands] = createSignal<RunCommand[] | undefined>([
command({ name: "review", description: "Review code" }),

View file

@ -69,6 +69,7 @@ async function setup(
theme?: RunTheme
onThemeRelease?: (theme: RunTheme) => void
mono?: boolean
failHighlight?: boolean
} = {},
) {
const out = await createTestRenderer({
@ -83,6 +84,11 @@ async function setup(
const treeSitterClient = new MockTreeSitterClient({ autoResolveTimeout: 0 })
treeSitterClient.setMockResult({ highlights: [] })
if (input.failHighlight) {
treeSitterClient.highlightOnce = async () => {
throw new Error("highlight failed")
}
}
return {
renderer: out.renderer,
@ -217,7 +223,25 @@ test("renders monochrome scrollback as ASCII markdown", async () => {
try {
await out.scrollback.append(assistant("# H"))
expect(Reflect.get(out.scrollback, "active")?.renderable).toBeInstanceOf(MarkdownRenderable)
await out.scrollback.append(assistant("éading →\n\n> “quote”\n\n---\n\n| A | B |\n| - | - |\n| α | β |"))
await out.scrollback.append(
assistant(
"éading →\n\n> “quote”\n\n---\n\n| A | B |\n| - | - |\n| α | β |\n\n• literal\n\n———\n\n[café](https://example.com/café)",
),
)
const active: unknown = Reflect.get(out.scrollback, "active")
const renderable =
active && typeof active === "object" && "renderable" in active && active.renderable instanceof MarkdownRenderable
? active.renderable
: undefined
expect(renderable?._blockStates.slice(-3).map((state) => state.token.type)).toEqual([
"paragraph",
"paragraph",
"paragraph",
])
const link = renderable?._blockStates.at(-1)?.token
const tokens = link && "tokens" in link && Array.isArray(link.tokens) ? link.tokens : []
const href = tokens.find((token) => "href" in token)
expect(href && "href" in href ? href.href : undefined).toBe("https://example.com/café")
await out.scrollback.complete()
out.renderer.writeToScrollback((ctx) => ({
root: new TextRenderable(ctx.renderContext, {
@ -235,6 +259,8 @@ test("renders monochrome scrollback as ASCII markdown", async () => {
expect(rendered).toContain('| "quote"')
expect(rendered).toContain("------------------------------------------------------------")
expect(rendered).toContain("? ?")
expect(rendered).toContain("* literal")
expect(rendered).toContain("------")
expect(rendered).toContain("plain ? emoji ?")
expect(rendered).not.toMatch(/[^\x00-\x7f]/)
} finally {
@ -243,6 +269,64 @@ test("renders monochrome scrollback as ASCII markdown", async () => {
}
})
test("renders completed subagent markdown in monochrome mode", async () => {
const out = await setup({ mono: true, width: 60 })
try {
await out.scrollback.append(
toolCommit({
tool: "subagent",
phase: "final",
toolState: "completed",
state: {
status: "completed",
input: { description: "Inspect reducer", agent: "explore" },
content: [{ type: "text", text: "# Findings\n\n- Café → stable" }],
metadata: {
sessionID: "ses-child-1",
status: "completed",
output: "# Findings\n\n- Café → stable",
},
},
}),
)
const commits = claim(out.renderer)
try {
expect(commits).toHaveLength(1)
expect(commits[0]?.trailingNewline).toBe(true)
const output = render(commits)
expect(output).toContain("# Findings")
expect(output).toContain("- Caf? -> stable")
expect(output).not.toMatch(/[^\x00-\x7f]/)
} finally {
destroy(commits)
}
} finally {
out.scrollback.destroy()
}
})
test("keeps fenced code monochrome when highlighting fails", async () => {
const out = await setup({ mono: true, failHighlight: true })
try {
await out.scrollback.append(assistant("```ts\nCafé → …\n```"))
await out.scrollback.complete()
const commits = claim(out.renderer)
try {
const output = render(commits)
expect(output).toContain("Caf? -> ...")
expect(output).not.toMatch(/[^\x00-\x7f]/)
} finally {
destroy(commits)
}
} finally {
out.scrollback.destroy()
}
})
function user(text: string): StreamCommit {
return {
kind: "user",