fix: improve Claw PDF table print layout
This commit is contained in:
parent
e02dff0b06
commit
934bc56202
@ -45,6 +45,10 @@ function clonePrintableStyles(targetWindow: Window) {
|
||||
function createPrintOverrides(targetWindow: Window) {
|
||||
const style = targetWindow.document.createElement("style");
|
||||
style.textContent = `
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #111827;
|
||||
@ -142,14 +146,32 @@ function createPrintOverrides(targetWindow: Window) {
|
||||
.claw-pdf-content {
|
||||
font-size: 14px;
|
||||
line-height: 1.7;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.claw-pdf-content,
|
||||
.claw-pdf-content * {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
[data-pdf-exclude="true"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.chat-card-x-scroll {
|
||||
.claw-pdf-table-card {
|
||||
break-inside: auto;
|
||||
page-break-inside: auto;
|
||||
overflow: visible !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
border-radius: 10px !important;
|
||||
}
|
||||
|
||||
.chat-card-x-scroll {
|
||||
display: block !important;
|
||||
overflow: visible !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
padding-bottom: 0 !important;
|
||||
scrollbar-width: none !important;
|
||||
}
|
||||
@ -159,13 +181,69 @@ function createPrintOverrides(targetWindow: Window) {
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100% !important;
|
||||
min-width: 0 !important;
|
||||
max-width: 100% !important;
|
||||
table-layout: fixed !important;
|
||||
border-collapse: collapse !important;
|
||||
page-break-inside: auto;
|
||||
break-inside: auto;
|
||||
font-size: 11px !important;
|
||||
line-height: 1.45 !important;
|
||||
}
|
||||
|
||||
.claw-pdf-wide-table {
|
||||
font-size: 10px !important;
|
||||
}
|
||||
|
||||
.claw-pdf-extra-wide-table {
|
||||
font-size: 9px !important;
|
||||
}
|
||||
|
||||
thead {
|
||||
display: table-header-group;
|
||||
}
|
||||
|
||||
tbody {
|
||||
display: table-row-group;
|
||||
}
|
||||
|
||||
tr {
|
||||
page-break-inside: avoid;
|
||||
break-inside: avoid;
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
min-width: 0 !important;
|
||||
width: auto !important;
|
||||
max-width: none !important;
|
||||
white-space: normal !important;
|
||||
overflow-wrap: anywhere !important;
|
||||
word-break: break-word !important;
|
||||
text-overflow: clip !important;
|
||||
vertical-align: top !important;
|
||||
padding: 5px 7px !important;
|
||||
line-height: 1.45 !important;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left !important;
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
.claw-pdf-wide-table th,
|
||||
.claw-pdf-wide-table td,
|
||||
.claw-pdf-extra-wide-table th,
|
||||
.claw-pdf-extra-wide-table td {
|
||||
padding-left: 5px !important;
|
||||
padding-right: 5px !important;
|
||||
}
|
||||
|
||||
tr,
|
||||
svg,
|
||||
img {
|
||||
page-break-inside: avoid;
|
||||
break-inside: avoid;
|
||||
}
|
||||
|
||||
svg {
|
||||
@ -189,6 +267,58 @@ function createPrintOverrides(targetWindow: Window) {
|
||||
targetWindow.document.head.appendChild(style);
|
||||
}
|
||||
|
||||
function getTableColumnCount(table: HTMLTableElement) {
|
||||
return Array.from(table.rows).reduce(
|
||||
(maxColumns, row) => Math.max(maxColumns, row.cells.length),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
function preparePrintableTables(root: HTMLElement) {
|
||||
root.querySelectorAll<HTMLTableElement>("table").forEach((table) => {
|
||||
const columnCount = getTableColumnCount(table);
|
||||
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");
|
||||
});
|
||||
}
|
||||
|
||||
function waitForFrame(targetWindow: Window) {
|
||||
return new Promise<void>((resolve) => {
|
||||
targetWindow.requestAnimationFrame(() => resolve());
|
||||
});
|
||||
}
|
||||
|
||||
async function waitForPrintableAssets(targetWindow: Window, root: HTMLElement) {
|
||||
const fontReady =
|
||||
"fonts" in targetWindow.document
|
||||
? targetWindow.document.fonts.ready.catch(() => undefined)
|
||||
: Promise.resolve();
|
||||
const images = Array.from(root.querySelectorAll<HTMLImageElement>("img"));
|
||||
const imageReady = Promise.all(
|
||||
images.map((image) => {
|
||||
if (image.complete && image.naturalWidth > 0) return Promise.resolve();
|
||||
if (typeof image.decode === "function") {
|
||||
return image.decode().catch(() => undefined);
|
||||
}
|
||||
return new Promise<void>((resolve) => {
|
||||
image.addEventListener("load", () => resolve(), { once: true });
|
||||
image.addEventListener("error", () => resolve(), { once: true });
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
await Promise.race([
|
||||
Promise.all([fontReady, imageReady]),
|
||||
new Promise((resolve) => targetWindow.setTimeout(resolve, 1800)),
|
||||
]);
|
||||
await waitForFrame(targetWindow);
|
||||
await waitForFrame(targetWindow);
|
||||
}
|
||||
|
||||
export function ResponsePdfDownloadButton({
|
||||
contentRef,
|
||||
fileName,
|
||||
@ -273,15 +403,21 @@ export function ResponsePdfDownloadButton({
|
||||
clonedContent
|
||||
.querySelectorAll('[data-pdf-exclude="true"]')
|
||||
.forEach((node) => node.remove());
|
||||
preparePrintableTables(clonedContent);
|
||||
content.appendChild(clonedContent);
|
||||
shell.appendChild(content);
|
||||
printWindow.document.body.appendChild(shell);
|
||||
|
||||
window.setTimeout(() => {
|
||||
setIsPreparing(false);
|
||||
printWindow.focus();
|
||||
printWindow.print();
|
||||
}, 600);
|
||||
void waitForPrintableAssets(printWindow, shell)
|
||||
.catch((error) => {
|
||||
console.warn("[Response PDF] asset readiness check failed:", error);
|
||||
})
|
||||
.finally(() => {
|
||||
setIsPreparing(false);
|
||||
if (printWindow.closed) return;
|
||||
printWindow.focus();
|
||||
printWindow.print();
|
||||
});
|
||||
} catch (error) {
|
||||
setIsPreparing(false);
|
||||
printWindow.close();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user