import type { MarkedExtension, Tokens } from "marked"; import Slugger from "github-slugger"; /** * 将表格数据转换为 CSV 格式字符串 * @param headers 表头数组 * @param rows 表格数据行 * @returns CSV 格式字符串 */ function tableToCSV(headers: string[], rows: string[][]): string { const escapeCell = (cell: string) => { // 如果单元格包含逗号、换行或引号,需要转义 if ( cell.includes(",") || cell.includes("\n") || cell.includes('"') || cell.includes("#") ) { return `"${cell.replace(/"/g, '""')}"`; } return cell; }; const headerLine = headers.map(escapeCell).join(","); const dataLines = rows.map((row) => row.map(escapeCell).join(",")); return [headerLine, ...dataLines].join("\n"); } /** Spark table: first column header is a pure dice formula (d6, d20, etc.) */ const SPARK_DICE_RE = /^\d*d\d+$/i; export default function markedTable(): MarkedExtension { return { renderer: { table(token: Tokens.Table) { // 检查表头是否包含 md-table-label const header = token.header; let roll = ""; let spark = ""; const labelIndex = header.findIndex((cell) => { if (cell.text === "md-roll-label" || cell.text.match(/(\d+)?d\d+/)) { roll = " roll=true"; // If this is a spark table (first column is a pure dice formula), // compute the data-column slug for DOM injection if (SPARK_DICE_RE.test(cell.text)) { const slugger = new Slugger(); const dataHeaders = header .filter((_, i) => i !== 0) .map((h) => h.text); const slug = dataHeaders .map((h) => slugger.slug(h.toLowerCase())) .join("-"); spark = ` data-spark="${slug}"`; } return true; } else if (cell.text === "md-remix-label") { roll = " roll=true remix=true"; return true; } return cell.text === "md-table-label" || cell.text === "label"; }); // 默认表格渲染 - 使用 marked 默认行为 if (labelIndex === -1) return false; const headers = token.header.map((cell) => cell.text); headers[labelIndex] = "label"; const rows = token.rows.map((row) => row.map((cell) => cell.text)); if (header.findIndex((header) => header.text === "body") < 0) { // 收集所有非 label 列的表头 const bodyColumns = headers.filter((cell) => cell !== "label"); // 构建 body 列的模板:**列名**:{{列名}}\n\n const bodyTemplate = bodyColumns .map((col) => `**${col}**:{{${col}}}`) .join("\n\n"); headers.push("body"); rows.forEach((row) => { row.push(bodyTemplate); }); } // 生成 CSV 数据 const csvData = tableToCSV(headers, rows); // 渲染为 md-table 组件,内联 CSV 数据 return `${csvData}\n`; }, }, }; }