fix(line): preserve every row in rich markdown tables

This commit is contained in:
Peter Steinberger
2026-08-01 12:44:40 -07:00
parent 3b70c3535f
commit a34dd3eb7f
2 changed files with 76 additions and 2 deletions

View File

@@ -255,6 +255,79 @@ print("done")
expect(result.text).not.toContain("[here]");
});
it.each([
{ name: "plain two-column at the receipt limit", columns: 2, rowCount: 12, styled: false },
{ name: "plain two-column beyond the receipt limit", columns: 2, rowCount: 13, styled: false },
{
name: "styled two-column at the former generic limit",
columns: 2,
rowCount: 10,
styled: true,
},
{
name: "styled two-column beyond the former generic limit",
columns: 2,
rowCount: 11,
styled: true,
},
{ name: "three-column at the former generic limit", columns: 3, rowCount: 10, styled: false },
{
name: "three-column beyond the former generic limit",
columns: 3,
rowCount: 11,
styled: false,
},
])("keeps every row visible for $name", ({ columns, rowCount, styled }) => {
const headers = Array.from({ length: columns }, (_, column) => `Header ${column + 1}`);
const rows = Array.from({ length: rowCount }, (_, row) => {
const cells = Array.from({ length: columns }, (_unused, column) => {
const value = `row-${row + 1}-column-${column + 1}`;
if (styled && column === 0) {
return `**${value}**`;
}
return styled && column === 1 ? `[${value}](https://example.test/rows/${row + 1})` : value;
});
return `| ${cells.join(" | ")} |`;
});
const table = [
`| ${headers.join(" | ")} |`,
`| ${headers.map(() => "---").join(" | ")} |`,
...rows,
].join("\n");
const result = processLineMessage(table);
const delivered = JSON.stringify(result.flexMessages);
expect(result.flexMessages).toHaveLength(1);
for (let row = 1; row <= rowCount; row += 1) {
for (let column = 1; column <= columns; column += 1) {
expect(delivered).toContain(`row-${row}-column-${column}`);
}
if (styled) {
expect(delivered).toContain(`https://example.test/rows/${row}`);
}
}
expect(
Buffer.byteLength(JSON.stringify(result.flexMessages[0]?.contents), "utf8"),
).toBeLessThanOrEqual(30_000);
expect(result.segments).toBeUndefined();
});
it("falls back with every row when a complete table exceeds the provider size limit", () => {
const rows = Array.from(
{ length: 13 },
(_, row) => `| row-${row + 1} | ${row === 12 ? "x".repeat(30_000) : `value-${row + 1}`} |`,
);
const result = processLineMessage(["| Name | Value |", "|---|---|", ...rows].join("\n"));
expect(result.flexMessages).toHaveLength(0);
for (let row = 1; row <= rows.length; row += 1) {
expect(result.text).toContain(`row-${row}`);
}
expect(result.text).toContain("x".repeat(30_000));
expect(result.segments).toEqual([{ type: "text", text: result.text }]);
});
it("keeps an oversized table visible as canonical bullet text instead of an invalid Flex bubble", () => {
const value = "x".repeat(30_000);
const result = processLineMessage(

View File

@@ -329,7 +329,8 @@ export function convertTableToFlexBubble(table: MarkdownTable): FlexBubble {
headerCells.some((cell) => cell.hasMarkup) ||
rowCells.some((row) => row.some((cell) => cell.hasMarkup));
if (table.headers.length === 2 && !hasInlineMarkup) {
// Receipt cards clip after 12 rows; larger tables need the complete generic layout.
if (table.headers.length === 2 && !hasInlineMarkup && rowCells.length <= 12) {
return createReceiptCard({
title: headerCells.map((cell) => cell.text).join(" / "),
items: rowCells.map((row) => ({
@@ -355,7 +356,7 @@ export function convertTableToFlexBubble(table: MarkdownTable): FlexBubble {
paddingBottom: "sm",
} as FlexBox;
const dataRows: FlexComponent[] = rowCells.slice(0, 10).map((row, rowIndex) => ({
const dataRows: FlexComponent[] = rowCells.map((row, rowIndex) => ({
type: "box",
layout: "horizontal",
contents: table.headers.map((_, colIndex) => {