refactor(publish): downgrade baijiahao tables to paragraph rows
Baijiahao does not consistently render the previous flex-based faux table markup, so reduce tables to plain <p> rows separated by three nbsp gaps to keep tabular content visible after publish. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -22,50 +22,11 @@ const BAIJIAHAO_TABLE_HEADER_CELL_STYLE = [
|
||||
"font-weight: 600",
|
||||
].join("; ");
|
||||
|
||||
const BAIJIAHAO_FAUX_TABLE_STYLE = [
|
||||
"width: 100%",
|
||||
"margin: 12px 0",
|
||||
"border-top: 1px solid #d9d9d9",
|
||||
"border-left: 1px solid #d9d9d9",
|
||||
"overflow-x: auto",
|
||||
].join("; ");
|
||||
|
||||
const BAIJIAHAO_FAUX_TABLE_ROW_STYLE = [
|
||||
"display: flex",
|
||||
"align-items: stretch",
|
||||
"width: 100%",
|
||||
].join("; ");
|
||||
|
||||
const BAIJIAHAO_FAUX_TABLE_CELL_STYLE = [
|
||||
"flex: 1 1 0",
|
||||
"min-width: 96px",
|
||||
"box-sizing: border-box",
|
||||
"border-right: 1px solid #d9d9d9",
|
||||
"border-bottom: 1px solid #d9d9d9",
|
||||
"padding: 8px 10px",
|
||||
"vertical-align: middle",
|
||||
"word-break: break-word",
|
||||
"font-size: 15px",
|
||||
"line-height: 1.6",
|
||||
"color: #1f2937",
|
||||
"background: #ffffff",
|
||||
].join("; ");
|
||||
|
||||
const BAIJIAHAO_FAUX_TABLE_HEADER_CELL_STYLE = [
|
||||
BAIJIAHAO_FAUX_TABLE_CELL_STYLE,
|
||||
"background: #f5f7fa",
|
||||
"font-weight: 600",
|
||||
].join("; ");
|
||||
const TABLE_PARAGRAPH_CELL_SEPARATOR = " ";
|
||||
|
||||
const BAIJIAHAO_TABLE_ALLOWED_ATTRS = ["colspan", "rowspan", "align", "valign", "width"];
|
||||
|
||||
interface ParsedTableCell {
|
||||
content: string;
|
||||
header: boolean;
|
||||
colSpan: number;
|
||||
}
|
||||
|
||||
type ParsedTableRow = ParsedTableCell[];
|
||||
type ParsedParagraphTableRow = string[];
|
||||
|
||||
function hasUnescapedPipe(value: string): boolean {
|
||||
let escaped = false;
|
||||
@@ -216,12 +177,6 @@ function normalizeCellContent(value: string): string {
|
||||
return trimmed ? trimmed : "<br/>";
|
||||
}
|
||||
|
||||
function normalizeSpanAttribute(sourceTag: string, attributeName: "colspan" | "rowspan"): number {
|
||||
const value = extractAttribute(sourceTag, attributeName);
|
||||
const parsed = value ? Number.parseInt(value, 10) : 1;
|
||||
return Number.isFinite(parsed) && parsed > 1 ? Math.min(parsed, 12) : 1;
|
||||
}
|
||||
|
||||
function normalizeTableCell(sourceTag: string, content: string, header: boolean): string {
|
||||
const attrs = allowedTableAttributes(sourceTag);
|
||||
const attrPrefix = attrs ? `${attrs} ` : "";
|
||||
@@ -263,18 +218,25 @@ export function normalizeBaijiahaoTables(html: string): string {
|
||||
return html.replace(/<table\b[^>]*>[\s\S]*?<\/table>/gi, (tableHtml) => normalizeSingleTable(tableHtml));
|
||||
}
|
||||
|
||||
function parseTableRows(tableHtml: string): ParsedTableRow[] {
|
||||
const rows: ParsedTableRow[] = [];
|
||||
function normalizeParagraphCellContent(value: string): string {
|
||||
return value
|
||||
.trim()
|
||||
.replace(/<br\s*\/?>/gi, " ")
|
||||
.replace(/<\/?(?:p|div|section|article|header|footer|ul|ol|li|blockquote|h[1-6])\b[^>]*>/gi, " ")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
}
|
||||
|
||||
function parseParagraphTableRows(tableHtml: string): ParsedParagraphTableRow[] {
|
||||
const rows: ParsedParagraphTableRow[] = [];
|
||||
for (const rowMatch of tableHtml.matchAll(/<tr\b[^>]*>([\s\S]*?)<\/tr>/gi)) {
|
||||
const cells: ParsedTableRow = [];
|
||||
const cells: ParsedParagraphTableRow = [];
|
||||
const rowContent = rowMatch[1] ?? "";
|
||||
for (const cellMatch of rowContent.matchAll(/<(th|td)\b([^>]*)>([\s\S]*?)<\/\1>/gi)) {
|
||||
const sourceTag = `<${cellMatch[1]}${cellMatch[2] ?? ""}>`;
|
||||
cells.push({
|
||||
content: normalizeCellContent(cellMatch[3] ?? ""),
|
||||
header: cellMatch[1]?.toLowerCase() === "th",
|
||||
colSpan: normalizeSpanAttribute(sourceTag, "colspan"),
|
||||
});
|
||||
for (const cellMatch of rowContent.matchAll(/<(?:th|td)\b[^>]*>([\s\S]*?)<\/(?:th|td)>/gi)) {
|
||||
const content = normalizeParagraphCellContent(cellMatch[1] ?? "");
|
||||
if (content) {
|
||||
cells.push(content);
|
||||
}
|
||||
}
|
||||
if (cells.length > 0) {
|
||||
rows.push(cells);
|
||||
@@ -283,36 +245,30 @@ function parseTableRows(tableHtml: string): ParsedTableRow[] {
|
||||
return rows;
|
||||
}
|
||||
|
||||
function renderFauxTableCell(cell: ParsedTableCell, rowIndex: number): string {
|
||||
const header = cell.header || rowIndex === 0;
|
||||
const baseStyle = header ? BAIJIAHAO_FAUX_TABLE_HEADER_CELL_STYLE : BAIJIAHAO_FAUX_TABLE_CELL_STYLE;
|
||||
const style = `${baseStyle}; flex: ${cell.colSpan} ${cell.colSpan} 0`;
|
||||
const content = header ? `<strong>${cell.content}</strong>` : cell.content;
|
||||
return `<div style="${style}">${content}</div>`;
|
||||
}
|
||||
|
||||
function renderSingleTableAsBlocks(tableHtml: string): string {
|
||||
const rows = parseTableRows(tableHtml);
|
||||
if (rows.length === 0) {
|
||||
function renderSingleTableAsParagraphRows(tableHtml: string): string {
|
||||
const rows = parseParagraphTableRows(tableHtml);
|
||||
if (!rows.length) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return `<div style="${BAIJIAHAO_FAUX_TABLE_STYLE}">${rows
|
||||
.map((row, rowIndex) => `<div style="${BAIJIAHAO_FAUX_TABLE_ROW_STYLE}">${row
|
||||
.map((cell) => renderFauxTableCell(cell, rowIndex))
|
||||
.join("")}</div>`)
|
||||
.join("")}</div>`;
|
||||
return rows
|
||||
.map((row) => `<p>${row.join(TABLE_PARAGRAPH_CELL_SEPARATOR)}</p>`)
|
||||
.join("");
|
||||
}
|
||||
|
||||
export function renderBaijiahaoTablesAsBlocks(html: string): string {
|
||||
export function renderTablesAsParagraphRows(html: string): string {
|
||||
if (!/<table\b/i.test(html)) {
|
||||
return html;
|
||||
}
|
||||
return html.replace(/<table\b[^>]*>[\s\S]*?<\/table>/gi, (tableHtml) => renderSingleTableAsBlocks(tableHtml));
|
||||
return html.replace(/<table\b[^>]*>[\s\S]*?<\/table>/gi, (tableHtml) =>
|
||||
renderSingleTableAsParagraphRows(tableHtml),
|
||||
);
|
||||
}
|
||||
|
||||
export function renderBaijiahaoTablesAsBlocks(html: string): string {
|
||||
return renderTablesAsParagraphRows(html);
|
||||
}
|
||||
|
||||
export function prepareBaijiahaoArticleHtml(html: string): string {
|
||||
// Baijiahao's preview/final article page does not consistently render native table tags,
|
||||
// so publish uses ordinary div blocks to preserve visible tabular content.
|
||||
return renderBaijiahaoTablesAsBlocks(html).trim();
|
||||
return renderTablesAsParagraphRows(html).trim();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user