Skip to content

feat(tui): let injected text parts opt into markdown rendering - #43319

Open
Zaldaryon wants to merge 3 commits into
anomalyco:devfrom
Zaldaryon:feat/opt-in-markdown-for-injected-user-parts
Open

feat(tui): let injected text parts opt into markdown rendering#43319
Zaldaryon wants to merge 3 commits into
anomalyco:devfrom
Zaldaryon:feat/opt-in-markdown-for-injected-user-parts

Conversation

@Zaldaryon

@Zaldaryon Zaldaryon commented Aug 19, 2026

Copy link
Copy Markdown

Issue for this PR

Closes #43318

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

A user message renders its visible text through a plain <text> node, while the assistant path renders through <markdown>:

packages/tui/src/routes/session/index.tsx
  1420  <text fg={theme.text}>{text()}</text>             // UserMessage
  1700  <markdown ... content={props.part.text.trim()} />  // TextPart, typed message: AssistantMessage

That split is right for text a person typed and wrong for text a plugin injects. Anything sent through session.prompt becomes a text part on a user message, so a plugin reporting status back into the session can only show raw markup. The case I hit is a background agent plugin posting its completion notice into the parent session, which lands as literal headings, asterisks and backticks.

A text part can now set metadata.render to "markdown" and get the markdown component. Parts without the flag render as before, so a typed prompt containing markdown syntax is still echoed back the way it was entered. That keeps the behavior a maintainer called intentional on #5675 while giving programmatic senders a way to ask.

Why it needs no schema or server work: TextPartInput already carries metadata, and resolvePart at packages/opencode/src/session/prompt.ts:992 returns [{ ...part, messageID, sessionID }], so the flag already survives the trip from the plugin to the stored part. The TUI was the only place that had to learn to read it.

The splitting logic sits in packages/tui/src/routes/session/user-message-text.ts so it can be tested without standing up the route.

How did you verify your code works?

bun turbo typecheck --filter=@opencode-ai/tui   # pass
bun test          (packages/tui)                # 198 pass, 1 skip, 0 fail
oxlint            (the three touched files)     # 0 errors, no new warnings

Five new cases cover typed text staying literal, an opted-in part reaching the markdown body, one message mixing both kinds, synthetic and empty parts staying hidden, and an unrecognized render value falling back to literal text.

I also ran the suite on the base commit to confirm the counts move by exactly the five tests added, and that the one flaky flock ENOENT I saw on a first run is unrelated and does not reproduce.

Screenshots / recordings

A terminal screenshot would carry the color attributes, so this is the character frame from the TUI test renderer instead, same content through each path at width 64. The markup characters are what the frame can show:

----- BEFORE (plain text node) -----
**Background agent complete** `wasteful-chocolate-shrimp`

## 1. Research/Plan Doc

- artifact: `delegations/wasteful-chocolate-shrimp.md`
- 1 task still running

----- AFTER (markdown component) -----
Background agent complete wasteful-chocolate-shrimp

1. Research/Plan Doc

- artifact: delegations/wasteful-chocolate-shrimp.md
- 1 task still running

Say the word if you want a recording of the real TUI and I will attach one.

Writer side

The first caller is ready on the other side of the wire: code-yeongyu/oh-my-openagent#7042 tags its background task notifications with the same hint. Neither change depends on the other landing first. A client that ignores metadata keeps its current behavior, and a part without the flag renders the way it does today.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@github-actions

Copy link
Copy Markdown
Contributor

Hey! Your PR title tui: let injected text parts opt into markdown rendering doesn't follow conventional commit format.

Please update it to start with one of:

  • feat: or feat(scope): new feature
  • fix: or fix(scope): bug fix
  • docs: or docs(scope): documentation changes
  • chore: or chore(scope): maintenance tasks
  • refactor: or refactor(scope): code refactoring
  • test: or test(scope): adding or updating tests

Where scope is the package name (e.g., app, desktop, opencode).

See CONTRIBUTING.md for details.

@Zaldaryon Zaldaryon changed the title tui: let injected text parts opt into markdown rendering feat(tui): let injected text parts opt into markdown rendering Aug 19, 2026
A user message renders its text through a plain text node, so a part that a
plugin or a tool injects with session.prompt shows its markup literally. The
assistant path already renders through the markdown component.

Text parts can now set metadata.render to "markdown" to pick the markdown
component instead. Anything a person typed keeps rendering literally, so a
prompt containing markdown syntax is still echoed back as it was entered.

TextPartInput already carries metadata and resolvePart spreads the input part
into the stored part, so no schema or server change is needed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE]: Let injected text parts opt into markdown rendering in the TUI

1 participant