diff options
Diffstat (limited to 'packages/cli/src/ui/utils/MarkdownDisplay.tsx')
| -rw-r--r-- | packages/cli/src/ui/utils/MarkdownDisplay.tsx | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/packages/cli/src/ui/utils/MarkdownDisplay.tsx b/packages/cli/src/ui/utils/MarkdownDisplay.tsx index d78360b5..55f1ce57 100644 --- a/packages/cli/src/ui/utils/MarkdownDisplay.tsx +++ b/packages/cli/src/ui/utils/MarkdownDisplay.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { Text, Box } from 'ink'; import { Colors } from '../colors.js'; import { colorizeCode } from './CodeColorizer.js'; +import { TableRenderer } from './TableRenderer.js'; interface MarkdownDisplayProps { text: string; @@ -43,12 +44,17 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({ const ulItemRegex = /^([ \t]*)([-*+]) +(.*)/; const olItemRegex = /^([ \t]*)(\d+)\. +(.*)/; const hrRegex = /^ *([-*_] *){3,} *$/; + const tableRowRegex = /^\s*\|(.+)\|\s*$/; + const tableSeparatorRegex = /^\s*\|?\s*(:?-+:?)\s*(\|\s*(:?-+:?)\s*)+\|?\s*$/; const contentBlocks: React.ReactNode[] = []; let inCodeBlock = false; let codeBlockContent: string[] = []; let codeBlockLang: string | null = null; let codeBlockFence = ''; + let inTable = false; + let tableRows: string[][] = []; + let tableHeaders: string[] = []; lines.forEach((line, index) => { const key = `line-${index}`; @@ -85,11 +91,71 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({ const ulMatch = line.match(ulItemRegex); const olMatch = line.match(olItemRegex); const hrMatch = line.match(hrRegex); + const tableRowMatch = line.match(tableRowRegex); + const tableSeparatorMatch = line.match(tableSeparatorRegex); if (codeFenceMatch) { inCodeBlock = true; codeBlockFence = codeFenceMatch[1]; codeBlockLang = codeFenceMatch[2] || null; + } else if (tableRowMatch && !inTable) { + // Potential table start - check if next line is separator + if ( + index + 1 < lines.length && + lines[index + 1].match(tableSeparatorRegex) + ) { + inTable = true; + tableHeaders = tableRowMatch[1].split('|').map((cell) => cell.trim()); + tableRows = []; + } else { + // Not a table, treat as regular text + contentBlocks.push( + <Box key={key}> + <Text wrap="wrap"> + <RenderInline text={line} /> + </Text> + </Box>, + ); + } + } else if (inTable && tableSeparatorMatch) { + // Skip separator line - already handled + } else if (inTable && tableRowMatch) { + // Add table row + const cells = tableRowMatch[1].split('|').map((cell) => cell.trim()); + // Ensure row has same column count as headers + while (cells.length < tableHeaders.length) { + cells.push(''); + } + if (cells.length > tableHeaders.length) { + cells.length = tableHeaders.length; + } + tableRows.push(cells); + } else if (inTable && !tableRowMatch) { + // End of table + if (tableHeaders.length > 0 && tableRows.length > 0) { + contentBlocks.push( + <RenderTable + key={`table-${contentBlocks.length}`} + headers={tableHeaders} + rows={tableRows} + terminalWidth={terminalWidth} + />, + ); + } + inTable = false; + tableRows = []; + tableHeaders = []; + + // Process current line as normal + if (line.trim().length > 0) { + contentBlocks.push( + <Box key={key}> + <Text wrap="wrap"> + <RenderInline text={line} /> + </Text> + </Box>, + ); + } } else if (hrMatch) { contentBlocks.push( <Box key={key}> @@ -194,6 +260,18 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({ ); } + // Handle table at end of content + if (inTable && tableHeaders.length > 0 && tableRows.length > 0) { + contentBlocks.push( + <RenderTable + key={`table-${contentBlocks.length}`} + headers={tableHeaders} + rows={tableRows} + terminalWidth={terminalWidth} + />, + ); + } + return <>{contentBlocks}</>; }; @@ -443,4 +521,20 @@ const RenderListItemInternal: React.FC<RenderListItemProps> = ({ const RenderListItem = React.memo(RenderListItemInternal); +interface RenderTableProps { + headers: string[]; + rows: string[][]; + terminalWidth: number; +} + +const RenderTableInternal: React.FC<RenderTableProps> = ({ + headers, + rows, + terminalWidth, +}) => ( + <TableRenderer headers={headers} rows={rows} terminalWidth={terminalWidth} /> +); + +const RenderTable = React.memo(RenderTableInternal); + export const MarkdownDisplay = React.memo(MarkdownDisplayInternal); |
