Date: 2026-08-10
Repository: corp-ui
Status: approved for implementation planning
Render model responses with the readable, continuous-document presentation used by ChatGPT while preserving the existing chat layout, streaming transport, message persistence, retry behavior, and full-response copy action.
The first version is deliberately limited to safe presentation. It does not execute model-produced content or introduce interactive previews.
Only assistant messages use rich formatting. User messages remain plain text in their existing bubbles so user-entered Markdown cannot unexpectedly change the composer or transcript presentation.
Assistant responses render as an unboxed document in the conversation flow:
The empty streaming placeholder remains unchanged. As soon as the first text fragment arrives, the assistant content is rendered as streaming Markdown.
Introduce a focused client component named AssistantMarkdown. ChatMessage
delegates non-empty assistant content to it and continues to own message status,
retry, attachment, and full-response actions.
AssistantMarkdown uses:
streamdown as the streaming-aware Markdown renderer;@streamdown/code as the only optional Streamdown plugin;.assistant-markdown product styles for typography, tables, quotes,
inline code, and overflow containment.Streamdown is selected over a direct react-markdown composition because the
assistant content arrives incrementally. It handles incomplete and unterminated
Markdown during streaming and avoids the avoidable paragraph/code-block flicker
of a renderer designed only for completed documents. The upstream project also
provides GFM support, hardened link handling, memoized streaming updates, and an
optional Shiki-backed code plugin:
https://github.com/vercel/streamdown.
No backend or database change is required. Message content remains the exact Markdown string produced by the model. Reloading a conversation renders the same persisted string through the same component.
AssistantMarkdown receives whether the message is currently streaming.
Streaming mode must:
The renderer must not introduce a second transport buffer or change the plain
text stream produced by /api/chat.
Model output is untrusted content. The renderer must fail closed:
script, style, iframe, object, embed, event attributes, and equivalent
active content are not rendered;noopener noreferrer;Allowed links are limited to absolute http: and https: URLs and ordinary
same-document anchors if Streamdown emits them for Markdown structure. Invalid
or unsupported links remain readable text without navigation.
The approved direction is the visual option ChatGPT-like:
--chat-readable-width;AssistantMarkdownOwns Markdown parsing configuration, safe element overrides, streaming mode, code-block rendering, code-copy state, and scoped class names.
ChatMessageKeeps ownership of user bubbles, attachments, status labels, retry, the empty
streaming placeholder, and full-response copy. It passes assistant content and
streaming status to AssistantMarkdown.
app/globals.css owns the scoped .assistant-markdown presentation and the
minimal Streamdown Tailwind source declaration required by the package. No
generic p, pre, table, or heading selector may leak outside the assistant
renderer.
Malformed or incomplete Markdown must never replace the message with an error surface. It remains visible as the safest text representation Streamdown can produce. A clipboard rejection leaves the code block intact and does not expose internal errors. Existing message-level error and retry handling remains unchanged.
Component tests must cover real rendered behavior rather than implementation details:
After focused component tests, the full corp-ui test suite, lint, TypeScript check, production build, and a real minikube browser smoke must pass.
The corp-ui image is rebuilt and rolled out without changing the Gateway, FinOPS, Model Registry, or database schemas. The deployed smoke uses a model response containing all supported constructs and verifies:
These can be designed separately after the safe renderer is proven in the live chat.