Drop docx export to fix CRA beta build
Unit Tests / test (push) Successful in 11s
Deploy Beta / unit-tests (push) Successful in 12s
Deploy Beta / deploy-beta (push) Failing after 2m28s

docx@9 ESM trips Babel during react-scripts build; keep PDF/CSV/XLSX/TXT.
This commit is contained in:
2026-08-04 06:22:54 -05:00
parent 3313738990
commit a7c5d1a2aa
5 changed files with 24 additions and 336 deletions
@@ -309,7 +309,6 @@ const MessageActions = ({
{(
[
['pdf', 'PDF'],
['docx', 'Word (.docx)'],
['csv', 'CSV'],
['xlsx', 'Excel (.xlsx)'],
['txt', 'Plain text'],
@@ -575,7 +575,6 @@ const AsyncDashboardInner = (): JSX.Element => {
{(
[
['pdf', 'PDF'],
['docx', 'Word (.docx)'],
['csv', 'CSV'],
['xlsx', 'Excel (.xlsx)'],
['txt', 'Plain text'],
+2 -168
View File
@@ -8,7 +8,7 @@ import {
type MdBlock,
} from './markdownUtils';
export type ExportFormat = 'pdf' | 'docx' | 'csv' | 'xlsx' | 'txt';
export type ExportFormat = 'pdf' | 'csv' | 'xlsx' | 'txt';
export type ExportTurn = {
role: 'user' | 'assistant';
@@ -52,7 +52,7 @@ function citationFootnotes(citations?: Citation[]): string {
export async function exportChat(options: ExportOptions): Promise<void> {
const { format, scope, title, turns } = options;
const filename = buildExportFilename(title, format === 'docx' ? 'docx' : format);
const filename = buildExportFilename(title, format);
const exportedAt = new Date().toISOString();
// Yield so large conversations don't freeze the main thread (#97).
@@ -71,9 +71,6 @@ export async function exportChat(options: ExportOptions): Promise<void> {
case 'pdf':
await exportPdf(title, turns, exportedAt, filename);
break;
case 'docx':
await exportDocx(title, turns, exportedAt, filename);
break;
default:
throw new Error(`Unsupported export format: ${format}`);
}
@@ -304,169 +301,6 @@ async function exportPdf(
});
}
async function exportDocx(
title: string,
turns: ExportTurn[],
exportedAt: string,
filename: string,
): Promise<void> {
const {
Document,
Packer,
Paragraph,
TextRun,
HeadingLevel,
Table,
TableRow,
TableCell,
WidthType,
Header,
} = await import('docx');
const children: InstanceType<typeof Paragraph | typeof Table>[] = [
new Paragraph({
text: title,
heading: HeadingLevel.TITLE,
}),
new Paragraph({
children: [
new TextRun({ text: `Exported ${exportedAt}`, italics: true, size: 18, color: '666666' }),
],
}),
];
for (let i = 0; i < turns.length; i += 1) {
if (i > 0 && i % 20 === 0) await yieldToUi();
const turn = turns[i];
if (turns.length > 1) {
children.push(
new Paragraph({
children: [
new TextRun({
text: `${roleLabel(turn.role)}${turn.timestamp ? ` · ${formatTs(turn.timestamp)}` : ''}`,
bold: true,
}),
],
spacing: { before: 240 },
}),
);
}
for (const block of parseMarkdownBlocks(turn.message)) {
switch (block.type) {
case 'heading': {
const level =
block.level === 1
? HeadingLevel.HEADING_1
: block.level === 2
? HeadingLevel.HEADING_2
: HeadingLevel.HEADING_3;
children.push(new Paragraph({ text: block.text, heading: level }));
break;
}
case 'paragraph':
children.push(new Paragraph({ text: block.text }));
break;
case 'code':
children.push(
new Paragraph({
children: [new TextRun({ text: block.text, font: 'Courier New', size: 18 })],
}),
);
break;
case 'list':
block.items.forEach((item, idx) => {
children.push(
new Paragraph({
text: block.ordered ? `${idx + 1}. ${item}` : `${item}`,
}),
);
});
break;
case 'table':
children.push(
new Table({
width: { size: 100, type: WidthType.PERCENTAGE },
rows: [
new TableRow({
children: block.headers.map(
(h) =>
new TableCell({
children: [
new Paragraph({
children: [new TextRun({ text: h, bold: true })],
}),
],
}),
),
}),
...block.rows.map(
(row) =>
new TableRow({
children: block.headers.map(
(_, col) =>
new TableCell({
children: [new Paragraph({ text: row[col] ?? '' })],
}),
),
}),
),
],
}),
);
break;
default:
break;
}
}
if (turn.citations?.length) {
children.push(
new Paragraph({
text: 'Sources',
heading: HeadingLevel.HEADING_3,
}),
);
turn.citations
.slice()
.sort((a, b) => a.index - b.index)
.forEach((c) => {
children.push(
new Paragraph({
text: `[${c.index}] ${c.title}${c.url ? `${c.url}` : ''}`,
}),
);
});
}
}
const doc = new Document({
sections: [
{
headers: {
default: new Header({
children: [
new Paragraph({
children: [
new TextRun({
text: `${title} · ${exportedAt}`,
size: 16,
color: '888888',
}),
],
}),
],
}),
},
children,
},
],
});
const blob = await Packer.toBlob(doc);
downloadBlob(blob, filename);
}
/** Decide CSV/XLSX matrix strategy — exported for unit tests. */
export function selectTabularStrategy(
scope: ExportScope,