From 46662b7eec5b0f15de94af6da5e04339e6a08f04 Mon Sep 17 00:00:00 2001 From: Ryan Vogel <89211796+R44VC0RP@users.noreply.github.com> Date: Sat, 1 Aug 2026 11:04:55 -0400 Subject: [PATCH] fix(tui): strip markdown from inbox previews --- packages/tui/src/component/session-inbox.tsx | 7 +++--- packages/tui/src/util/markdown-preview.ts | 17 +++++++++++++ .../tui/test/util/markdown-preview.test.ts | 25 +++++++++++++++++++ 3 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 packages/tui/src/util/markdown-preview.ts create mode 100644 packages/tui/test/util/markdown-preview.test.ts diff --git a/packages/tui/src/component/session-inbox.tsx b/packages/tui/src/component/session-inbox.tsx index 5fc15184e0..b9c5a183b6 100644 --- a/packages/tui/src/component/session-inbox.tsx +++ b/packages/tui/src/component/session-inbox.tsx @@ -14,6 +14,7 @@ import { tint } from "../theme/color" import { getScrollAcceleration } from "../util/scroll" import { withTimestampedFallback } from "@opencode-ai/util/session-title-fallback" import { activityVerb } from "../util/activity-verb" +import { markdownPreview } from "../util/markdown-preview" import { Spinner } from "./spinner" import type { SessionTabsStatus } from "./session-tabs" @@ -176,14 +177,12 @@ export function SessionInbox() { const preview = assistant?.content .filter((part) => part.type === "text") .map((part) => part.text) - .join(" ") - .replace(/\s+/g, " ") - .trim() + .join("\n") return { sessionID: tab.sessionID, title: session ? withTimestampedFallback(session) : (tab.title ?? "Loading session…"), updated: session?.time.updated ?? 0, - preview: preview || "No assistant response yet", + preview: markdownPreview(preview ?? "") || "No assistant response yet", status, group: sessionInboxGroup(session?.time.updated ?? 0, status.busy), } diff --git a/packages/tui/src/util/markdown-preview.ts b/packages/tui/src/util/markdown-preview.ts new file mode 100644 index 0000000000..d66b94731b --- /dev/null +++ b/packages/tui/src/util/markdown-preview.ts @@ -0,0 +1,17 @@ +export function markdownPreview(input: string) { + return input + .replace(/```[^\n]*\n?([\s\S]*?)```/g, "$1") + .replace(/`([^`\n]+)`/g, "$1") + .replace(/!\[([^\]]*)\]\((?:\\.|[^)])*\)/g, "$1") + .replace(/\[([^\]]+)\]\((?:\\.|[^)])*\)/g, "$1") + .replace(/\[([^\]]+)\]\[[^\]]*\]/g, "$1") + .replace(/^ {0,3}(?:#{1,6}\s+|>\s?|(?:[-+*]|\d+[.)])\s+)/gm, "") + .replace(/(?|~])/g, "$1") + .replace(/\s+/g, " ") + .trim() +} diff --git a/packages/tui/test/util/markdown-preview.test.ts b/packages/tui/test/util/markdown-preview.test.ts new file mode 100644 index 0000000000..37d88c5cd0 --- /dev/null +++ b/packages/tui/test/util/markdown-preview.test.ts @@ -0,0 +1,25 @@ +import { expect, test } from "bun:test" +import { markdownPreview } from "../../src/util/markdown-preview" + +test("strips common Markdown presentation syntax from previews", () => { + expect(markdownPreview("The capture shows **Reset** and a _short-lived_ **override**.")).toBe( + "The capture shows Reset and a short-lived override.", + ) + expect(markdownPreview("Use [`sessionRename`](https://example.com) with ~~old~~ new behavior.")).toBe( + "Use sessionRename with old new behavior.", + ) + expect(markdownPreview("![diagram](image.png) Review [the result][result].")).toBe("diagram Review the result.") +}) + +test("flattens block syntax and code without losing its content", () => { + expect(markdownPreview("# Result\n\n- First item\n- Second `item`\n> Finished")).toBe( + "Result First item Second item Finished", + ) + expect(markdownPreview("```ts\nconst answer = 42\n```")).toBe("const answer = 42") +}) + +test("preserves literal asterisks, globs, and escaped punctuation", () => { + expect(markdownPreview("Search **/*.ts, calculate 2 * 3, and keep \\*literal\\*. ")).toBe( + "Search **/*.ts, calculate 2 * 3, and keep *literal*.", + ) +})