Markdown Tables Not Rendered #76
Labels
No labels
prio_critical
prio_low
type_bug
type_contact
type_issue
type_lead
type_question
type_story
type_task
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lhumina_code/hero_shrimp#76
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
When the agent outputs a markdown table (| Step | Result | with |------|--------|), it renders as plain pipe-separated text instead of an HTML table. This makes structured output hard to read.
Implementation Spec for Issue #76 — Markdown Tables Not Rendered
Objective
Make agent/assistant-emitted GitHub-Flavored Markdown tables render as real HTML
<table>elements in the chat UI, including the common case where the table is written directly beneath an intro line with no intervening blank line.Root Cause
The chat UI uses a hand-rolled, dependency-free markdown renderer (not marked/markdown-it/comrak). Assistant/user messages render through
ChatThread.tsx->MessageBody.tsx->Markdown.tsx.Markdown.tsxalready has full table support (atableblock type,TABLE_SEP_RE,isTableRow/splitRow, a table-detection branch, and proper<table>/<thead>/<tbody>rendering with Tailwind borders). GFM tables and the issue's exact|------|--------|separator parse correctly.The defect is in the paragraph-accumulation loop in
parseBlocks. When a table is immediately preceded by a non-blank text line with no blank line in between (e.g. an agent writingHere are the results:then the table on the next line), the loop greedily consumes consecutive non-blank lines and stops only on blank lines, code fences, headings, HRs, lists, and blockquotes — but NOT at a table start. So the intro line plus the entire table (header +|---|separator + body rows) get swallowed into a singleparagraphblock and rendered as plain pipe-separated text. This matches the reported symptom exactly. Tables that begin a message or follow a blank line already render correctly.Requirements
innerHTMLapproach.Files to Modify
crates/hero_shrimp_web/ui/src/components/Markdown.tsx— add a table-start guard to the paragraph-accumulation loop (plus a small helper). Only source change required.Implementation Plan
Step 1: Add an
isTableStarthelperFiles:
crates/hero_shrimp_web/ui/src/components/Markdown.tsxsplitRow, add a helper encapsulating the table-start condition:isTableRow(lines[i]) && i + 1 < lines.length && TABLE_SEP_RE.test(lines[i + 1])Dependencies: none
Step 2: Use the helper in the table-detection branch
Files:
crates/hero_shrimp_web/ui/src/components/Markdown.tsxisTableStart(lines, i).Dependencies: Step 1
Step 3: Stop paragraph accumulation at a table boundary
Files:
crates/hero_shrimp_web/ui/src/components/Markdown.tsx&& !isTableStart(lines, i)to the paragraphwhilecondition.Dependencies: Step 1
Step 4: Rebuild the embedded UI bundle
Files: build artifacts served via rust-embed
cd crates/hero_shrimp_web/ui && npm run buildso the new content-hashed bundle is embedded and served.Dependencies: Steps 1-3
Acceptance Criteria
| Step | Result |over|------|--------|) renders as a table, both when preceded by text and when standalone.innerHTML).Notes
innerHTMLsanitization concerns and to integrate file-chip/image linking. Do not swap in a third-party markdown library.|------|) currently failTABLE_SEP_REsince it requires at least two columns; the issue's table has two columns, so this fix resolves the report. Relaxing the regex can be a separate change.Verification Results
The project has no JS unit-test runner configured for the UI (
ui/package.jsonhas notestscript). Verification was done via the production build plus a standalone parser test that replicates the exactparseBlockslogic with the fix applied.UI build
cd crates/hero_shrimp_web/ui && npm run build— succeeded. 99 modules transformed, new content-hashed bundle emitted tocrates/hero_shrimp_web/static/assets/and embedded via rust-embed.Parser block-detection checks
All four cases produce the expected blocks. The reported case — a table written directly under an intro line — now yields a separate paragraph and a real HTML table instead of plain pipe-separated text.
Note: pre-existing TypeScript type errors exist in unrelated components (ChatActivity, CostBadge, DiffViewer, HeroWelcome, store.ts). They are not introduced by this change and do not affect the Vite build (the build does not run
tsc).Markdown.tsxhas no type errors.Implementation Summary
Fixed markdown tables not rendering when written directly beneath an intro line.
Root cause
The hand-rolled markdown parser in
crates/hero_shrimp_web/ui/src/components/Markdown.tsxaccumulated paragraph lines until it hit a blank line or another block type, but did not stop at a table boundary. A table placed directly under a non-blank text line (no blank line in between) was absorbed into the paragraph and rendered as plain pipe-separated text.Changes
crates/hero_shrimp_web/ui/src/components/Markdown.tsxisTableStart(lines, i)helper centralizing the table-start condition (table row followed by a separator line).!isTableStart(lines, i)as a stop condition in the paragraph-accumulation loop so paragraph capture halts at a table boundary, letting the table parse as its own block.npm run build) so the new content-hashed asset is served via rust-embed.Verification
No new dependencies were added and no CSS changes were needed (table styling already existed). The fix is contained to the parser.