# ChatGPT-like Assistant Markdown Design **Date:** 2026-08-10 **Repository:** `corp-ui` **Status:** approved for implementation planning ## Goal 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. ## User experience 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: - headings establish hierarchy without oversized page-title typography; - paragraphs use comfortable ChatGPT-like vertical rhythm and line height; - ordered and unordered lists have clear indentation and item spacing; - task lists, strikethrough, autolinks, and tables follow GitHub Flavored Markdown behavior; - blockquotes and horizontal rules remain visually subordinate to the answer; - inline code uses a muted compact surface; - fenced code uses a dark code surface with a language label, syntax highlighting, horizontal scrolling, and a dedicated copy action; - wide tables and code blocks scroll within the message instead of widening the chat column; - the existing full-answer copy action continues to copy the original Markdown source, not the rendered text. The empty streaming placeholder remains unchanged. As soon as the first text fragment arrives, the assistant content is rendered as streaming Markdown. ## Rendering architecture 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; - the repository's existing shadcn color tokens and light/dark theme variables; - scoped `.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: . 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. ## Streaming behavior `AssistantMarkdown` receives whether the message is currently streaming. Streaming mode must: - render partial paragraphs immediately; - tolerate an unfinished emphasis marker, list item, table, link, or fenced code block without throwing; - preserve the user's current auto-scroll behavior; - settle into the same semantic output as a completed message once the final fragment arrives; - avoid animation for users who prefer reduced motion. The renderer must not introduce a second transport buffer or change the plain text stream produced by `/api/chat`. ## Security boundary Model output is untrusted content. The renderer must fail closed: - raw HTML is never executed; - `script`, `style`, iframe, object, embed, event attributes, and equivalent active content are not rendered; - JavaScript, data, file, blob, and other non-approved link schemes are removed; - external links use a new browsing context plus `noopener noreferrer`; - remote images are not rendered in this version, preventing tracking requests and unreviewed external media loads; - Mermaid, HTML/React preview, LaTeX, executable Python, and other active or plugin-driven renderers are not installed; - code is always inert text even when its language is HTML, SVG, JavaScript, or shell; - code-copy and full-answer-copy actions copy text only. 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. ## Styling The approved direction is the visual option **ChatGPT-like**: - no card or border around the whole assistant response; - assistant avatar and action row remain as they are; - readable width continues to come from `--chat-readable-width`; - body text remains visually close to the current 14px chat scale with a relaxed line height; - headings use the existing sans-serif font and restrained weight/size; - code uses the existing mono font token; - colors derive from shadcn variables so both light and dark themes remain coherent; - focus indicators, selectable text, and minimum control targets remain accessible; - the formatting does not create horizontal page scrolling at mobile widths. ## Components and ownership ### `AssistantMarkdown` Owns Markdown parsing configuration, safe element overrides, streaming mode, code-block rendering, code-copy state, and scoped class names. ### `ChatMessage` Keeps 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`. ### Global product styles `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. ## Error handling 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. ## Testing strategy Component tests must cover real rendered behavior rather than implementation details: 1. headings, emphasis, paragraphs, lists, quotes, separators, inline code, GFM task lists, and tables render with semantic elements; 2. fenced code shows an inert code block, language presentation, highlighting, and a code-specific copy action that copies only the source code; 3. the existing message copy action still copies the original Markdown string; 4. incomplete streaming Markdown renders without throwing and converges to the completed semantic result; 5. raw HTML, scripts, event attributes, dangerous URL schemes, and remote images cannot create active DOM or network-triggering elements; 6. approved HTTP(S) links have safe target and relationship attributes; 7. user messages continue to display Markdown characters literally; 8. the empty streaming placeholder, stopped state, retry action, attachments, and download error behavior remain unchanged; 9. wide code and table content is contained by horizontal scrolling classes; 10. light/dark token usage and reduced-motion behavior remain covered by static class/style assertions where browser layout is unavailable. After focused component tests, the full corp-ui test suite, lint, TypeScript check, production build, and a real minikube browser smoke must pass. ## Deployment and verification 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: - progressive rendering while the response streams; - final Markdown structure; - code and full-response copy actions; - safe links; - no remote image request; - no horizontal viewport overflow on desktop and mobile widths; - existing retry and response-placeholder behavior. ## Out of scope - Markdown formatting for user messages; - model-controlled HTML; - remote or data-URL images; - Mermaid diagrams; - LaTeX/math rendering; - executable or editable code blocks; - HTML, React, SVG, chart, or document previews; - writing blocks, Canvas-like editing, and full-screen artifacts; - server-side Markdown conversion or storing rendered HTML. These can be designed separately after the safe renderer is proven in the live chat.