feat: enable rich text display for assistant messages

Add markdown-to-HTML rendering for assistant messages using Markdig with
HTML sanitization. Includes cached rendering to avoid lag during streaming,
styled markdown elements (code blocks, tables, lists, blockquotes) within
chat bubbles, and 18 unit tests covering rendering and XSS prevention.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
local
2026-04-05 01:29:58 +01:00
parent d3300c7db9
commit 7a5c22593a
14 changed files with 735 additions and 5 deletions

View File

@@ -6,17 +6,17 @@ Define the chat interface — message display, input handling, auto-scroll, and
### Requirement: Message display
The chat page SHALL display messages in a vertically scrolling list, with each message showing the sender role (user or assistant), the message content, and a visual distinction between user and assistant messages (e.g., alignment, color, or avatar).
The chat page SHALL display messages in a vertically scrolling list, with each message showing the sender role (user or assistant), the message content, and a visual distinction between user and assistant messages (e.g., alignment, color, or avatar). Assistant messages SHALL render content as formatted HTML converted from markdown; user messages SHALL display as plain text.
#### Scenario: User message displayed
- **WHEN** the user sends a message
- **THEN** the message appears in the message list aligned or styled to indicate it is from the user
- **THEN** the message appears in the message list aligned or styled to indicate it is from the user, rendered as plain text
#### Scenario: Assistant message displayed
- **WHEN** the assistant responds
- **THEN** the response appears in the message list with distinct styling from user messages (different alignment, color, or avatar)
- **THEN** the response appears in the message list with distinct styling from user messages, with markdown content rendered as formatted HTML
#### Scenario: Message ordering

View File

@@ -0,0 +1,95 @@
## Purpose
Define rich text rendering for assistant messages — markdown-to-HTML conversion, sanitization, styling, and streaming compatibility.
## Requirements
### Requirement: Markdown rendering for assistant messages
The system SHALL convert assistant message content from markdown to formatted HTML using Markdig, displaying headings, bold, italic, code blocks, lists, tables, links, and blockquotes with proper visual formatting.
#### Scenario: Markdown bold and italic rendered
- **WHEN** an assistant message contains `**bold**` or `*italic*` text
- **THEN** the text is displayed with bold or italic formatting respectively
#### Scenario: Code block rendered
- **WHEN** an assistant message contains a fenced code block (triple backticks)
- **THEN** the code is displayed in a monospace font within a visually distinct block
#### Scenario: Inline code rendered
- **WHEN** an assistant message contains inline code (single backticks)
- **THEN** the code is displayed in a monospace font with a subtle background
#### Scenario: List rendered
- **WHEN** an assistant message contains a markdown list (ordered or unordered)
- **THEN** the list is displayed with proper indentation and bullet/number markers
#### Scenario: Heading rendered
- **WHEN** an assistant message contains markdown headings (# through ######)
- **THEN** the headings are displayed with appropriate size and weight
#### Scenario: Link rendered
- **WHEN** an assistant message contains a markdown link `[text](url)`
- **THEN** the link is displayed as a clickable hyperlink opening in a new tab
#### Scenario: Table rendered
- **WHEN** an assistant message contains a markdown table
- **THEN** the table is displayed with borders, header row styling, and proper alignment
### Requirement: HTML sanitization
The system SHALL sanitize all HTML output from the markdown renderer to prevent cross-site scripting (XSS) attacks from LLM-generated content.
#### Scenario: Script tags stripped
- **WHEN** assistant message content contains `<script>` tags
- **THEN** the script tags and their content are removed from the rendered output
#### Scenario: Event handlers stripped
- **WHEN** assistant message content contains HTML attributes like `onclick` or `onerror`
- **THEN** the attributes are removed from the rendered output
#### Scenario: Safe tags preserved
- **WHEN** assistant message content contains allowed structural HTML (p, strong, em, code, pre, ul, ol, li, a, table elements, blockquote, br, h1-h6)
- **THEN** the tags are preserved in the rendered output
### Requirement: Markdown styling within chat bubbles
The system SHALL style rendered markdown elements to be visually consistent with the MudBlazor chat bubble theme.
#### Scenario: Code block styled in bubble
- **WHEN** a code block is rendered inside an assistant chat bubble
- **THEN** it has a distinct background color, padding, border-radius, and does not overflow the bubble width (horizontal scroll if needed)
#### Scenario: Paragraph spacing in bubble
- **WHEN** multiple paragraphs are rendered inside an assistant chat bubble
- **THEN** paragraphs have appropriate spacing without excessive gaps
### Requirement: Streaming compatibility
The system SHALL re-render markdown as new tokens arrive during streaming without visual glitches.
#### Scenario: Partial markdown rendered during streaming
- **WHEN** tokens are arriving and the current content contains incomplete markdown (e.g., a code block not yet closed)
- **THEN** the content is rendered with best-effort formatting and updates smoothly as more tokens complete the markdown structure
### Requirement: User messages remain plain text
The system SHALL continue to render user messages as plain text without markdown processing.
#### Scenario: User message not processed as markdown
- **WHEN** a user message contains markdown syntax like `**bold**`
- **THEN** the raw text `**bold**` is displayed, not formatted bold text