fix: split wide Claw PDF tables

This commit is contained in:
134 2026-06-25 22:49:12 +08:00
parent 934bc56202
commit b91c40f6df

View File

@ -167,6 +167,36 @@ function createPrintOverrides(targetWindow: Window) {
border-radius: 10px !important;
}
.claw-pdf-split-table-group {
display: block;
width: 100%;
margin: 12px 0;
break-inside: auto;
page-break-inside: auto;
}
.claw-pdf-split-card {
width: 100%;
margin: 0 0 10px;
overflow: hidden;
border: 1px solid #e5e7eb;
border-radius: 10px;
break-inside: auto;
page-break-inside: auto;
background: #ffffff;
}
.claw-pdf-split-caption {
padding: 5px 8px;
border-bottom: 1px solid #e5e7eb;
background: #f8fafc;
color: #6b7280;
font-size: 10px;
line-height: 1.4;
font-weight: 600;
text-align: right;
}
.chat-card-x-scroll {
display: block !important;
overflow: visible !important;
@ -188,16 +218,23 @@ function createPrintOverrides(targetWindow: Window) {
border-collapse: collapse !important;
page-break-inside: auto;
break-inside: auto;
font-size: 11px !important;
line-height: 1.45 !important;
font-size: 12px !important;
line-height: 1.5 !important;
}
.claw-pdf-split-table {
margin: 0 !important;
border: 0 !important;
border-radius: 0 !important;
font-size: 11.5px !important;
}
.claw-pdf-wide-table {
font-size: 10px !important;
font-size: 11px !important;
}
.claw-pdf-extra-wide-table {
font-size: 9px !important;
font-size: 10px !important;
}
thead {
@ -224,7 +261,17 @@ function createPrintOverrides(targetWindow: Window) {
text-overflow: clip !important;
vertical-align: top !important;
padding: 5px 7px !important;
line-height: 1.45 !important;
line-height: 1.5 !important;
}
th *,
td * {
min-width: 0 !important;
max-width: 100% !important;
white-space: normal !important;
overflow-wrap: anywhere !important;
word-break: break-word !important;
text-overflow: clip !important;
}
th {
@ -274,15 +321,157 @@ function getTableColumnCount(table: HTMLTableElement) {
);
}
const PRINTABLE_TABLE_SPLIT_THRESHOLD = 5;
const PRINTABLE_TABLE_PINNED_COLUMNS = 1;
const PRINTABLE_TABLE_MAX_COLUMNS_PER_SEGMENT = 4;
function getPrintableTableColumnGroups(columnCount: number) {
if (columnCount < PRINTABLE_TABLE_SPLIT_THRESHOLD) return null;
const pinnedColumnCount = Math.min(
PRINTABLE_TABLE_PINNED_COLUMNS,
columnCount
);
const pinnedColumns = Array.from(
{ length: pinnedColumnCount },
(_, index) => index
);
const remainingColumns = Array.from(
{ length: columnCount - pinnedColumnCount },
(_, index) => index + pinnedColumnCount
);
const chunkSize = Math.max(
1,
PRINTABLE_TABLE_MAX_COLUMNS_PER_SEGMENT - pinnedColumnCount
);
const columnGroups: number[][] = [];
for (let index = 0; index < remainingColumns.length; index += chunkSize) {
columnGroups.push([
...pinnedColumns,
...remainingColumns.slice(index, index + chunkSize),
]);
}
return columnGroups.length > 1 ? columnGroups : null;
}
function getPrintableTableRows(table: HTMLTableElement) {
let headRows = table.tHead ? Array.from(table.tHead.rows) : [];
const allRows = Array.from(table.rows);
if (!headRows.length && allRows[0]) {
const firstRow = allRows[0];
if (Array.from(firstRow.cells).some((cell) => cell.tagName === "TH")) {
headRows = [firstRow];
}
}
const headRowSet = new Set(headRows);
const bodyRows = allRows.filter((row) => !headRowSet.has(row));
return { headRows, bodyRows };
}
function appendSplitTableRows(
targetDocument: Document,
targetSection: HTMLTableSectionElement,
sourceRows: HTMLTableRowElement[],
columnIndexes: number[],
fallbackCellTag: "td" | "th"
) {
sourceRows.forEach((sourceRow) => {
const targetRow = targetDocument.createElement("tr");
columnIndexes.forEach((columnIndex) => {
const sourceCell = sourceRow.cells[columnIndex];
const cellTag =
sourceCell?.tagName.toLowerCase() === "th" || fallbackCellTag === "th"
? "th"
: "td";
const targetCell = targetDocument.createElement(cellTag);
if (sourceCell) {
targetCell.innerHTML = sourceCell.innerHTML;
}
targetRow.appendChild(targetCell);
});
targetSection.appendChild(targetRow);
});
}
function createSplitPrintableTableGroup(
targetDocument: Document,
table: HTMLTableElement,
columnGroups: number[][]
) {
const { headRows, bodyRows } = getPrintableTableRows(table);
const group = targetDocument.createElement("div");
group.className = "claw-pdf-split-table-group";
columnGroups.forEach((columnIndexes, index) => {
const card = targetDocument.createElement("section");
card.className = "claw-pdf-split-card";
const caption = targetDocument.createElement("div");
caption.className = "claw-pdf-split-caption";
caption.textContent = `续表 ${index + 1}/${columnGroups.length}`;
card.appendChild(caption);
const splitTable = targetDocument.createElement("table");
splitTable.className = "claw-pdf-split-table";
splitTable.dataset.pdfColumnCount = String(columnIndexes.length);
if (headRows.length) {
const thead = targetDocument.createElement("thead");
appendSplitTableRows(
targetDocument,
thead,
headRows,
columnIndexes,
"th"
);
splitTable.appendChild(thead);
}
const tbody = targetDocument.createElement("tbody");
appendSplitTableRows(
targetDocument,
tbody,
bodyRows,
columnIndexes,
"td"
);
splitTable.appendChild(tbody);
card.appendChild(splitTable);
group.appendChild(card);
});
return group;
}
function preparePrintableTables(root: HTMLElement) {
root.querySelectorAll<HTMLTableElement>("table").forEach((table) => {
if (!table.isConnected) return;
const columnCount = getTableColumnCount(table);
const columnGroups = getPrintableTableColumnGroups(columnCount);
const scrollWrapper = table.closest(".chat-card-x-scroll");
const tableCard = scrollWrapper?.parentElement;
if (columnGroups) {
const splitGroup = createSplitPrintableTableGroup(
root.ownerDocument,
table,
columnGroups
);
(tableCard || table).replaceWith(splitGroup);
return;
}
table.dataset.pdfColumnCount = String(columnCount);
table.classList.toggle("claw-pdf-wide-table", columnCount >= 5);
table.classList.toggle("claw-pdf-extra-wide-table", columnCount >= 7);
const scrollWrapper = table.closest(".chat-card-x-scroll");
scrollWrapper?.parentElement?.classList.add("claw-pdf-table-card");
tableCard?.classList.add("claw-pdf-table-card");
});
}