(table: Table, state: RenderState)
| 705 | } |
| 706 | |
| 707 | const renderTable = (table: Table, state: RenderState): ReactNode[] => { |
| 708 | const { palette, nextKey, codeBlockWidth } = state |
| 709 | const nodes: ReactNode[] = [] |
| 710 | |
| 711 | // Extract all rows and their plain text content |
| 712 | const rows = table.children.map((row) => { |
| 713 | const cells = (row as TableRow).children as TableCell[] |
| 714 | return cells.map((cell) => nodeToPlainText(cell).trim()) |
| 715 | }) |
| 716 | |
| 717 | if (rows.length === 0) return nodes |
| 718 | |
| 719 | // Determine number of columns |
| 720 | const numCols = Math.max(...rows.map((r) => r.length)) |
| 721 | if (numCols === 0) return nodes |
| 722 | |
| 723 | // Calculate natural column widths (minimum 3 chars per column) |
| 724 | const naturalWidths: number[] = Array(numCols).fill(3) |
| 725 | for (const row of rows) { |
| 726 | for (let i = 0; i < row.length; i++) { |
| 727 | const cellWidth = stringWidth(row[i] || '') |
| 728 | naturalWidths[i] = Math.max(naturalWidths[i], cellWidth) |
| 729 | } |
| 730 | } |
| 731 | |
| 732 | // Calculate total width needed: |
| 733 | // Each column has its content width |
| 734 | // Separators: " │ " between columns (3 chars each), none at edges |
| 735 | const separatorWidth = 3 // ' │ ' |
| 736 | const numSeparators = numCols - 1 |
| 737 | const totalNaturalWidth = |
| 738 | naturalWidths.reduce((a, b) => a + b, 0) + numSeparators * separatorWidth |
| 739 | |
| 740 | // Available width for the table (leave some margin) |
| 741 | const availableWidth = Math.max(20, codeBlockWidth - 2) |
| 742 | |
| 743 | // Calculate final column widths |
| 744 | let columnWidths: number[] |
| 745 | if (totalNaturalWidth <= availableWidth) { |
| 746 | // Table fits - use natural widths |
| 747 | columnWidths = naturalWidths |
| 748 | } else { |
| 749 | // Table too wide - proportionally shrink columns |
| 750 | const availableForContent = availableWidth - numSeparators * separatorWidth |
| 751 | const totalNaturalContent = naturalWidths.reduce((a, b) => a + b, 0) |
| 752 | const scale = availableForContent / totalNaturalContent |
| 753 | |
| 754 | columnWidths = naturalWidths.map((w) => { |
| 755 | // Minimum 3 chars, scale the rest |
| 756 | return Math.max(3, Math.floor(w * scale)) |
| 757 | }) |
| 758 | |
| 759 | // Distribute any remaining width to columns that were clamped |
| 760 | let usedWidth = columnWidths.reduce((a, b) => a + b, 0) |
| 761 | let remaining = availableForContent - usedWidth |
| 762 | for (let i = 0; i < columnWidths.length && remaining > 0; i++) { |
| 763 | if (columnWidths[i] < naturalWidths[i]) { |
| 764 | const add = Math.min(remaining, naturalWidths[i] - columnWidths[i]) |
no test coverage detected