refactor: move spark table parsing to directive scanner

Relocate spark table logic from the block processor to a dedicated
directive scanner. This allows spark tables to be treated as
`:md-table` directives and ensures they are processed in a second pass
after the content index is fully populated.
This commit is contained in:
2026-07-10 15:55:45 +08:00
parent 89b32ef15e
commit d4ebca5fd0
7 changed files with 419 additions and 100 deletions
+5 -19
View File
@@ -1,5 +1,4 @@
import type { MarkedExtension, Tokens } from "marked";
import Slugger from "github-slugger";
/**
* 将表格数据转换为 CSV 格式字符串
@@ -27,32 +26,17 @@ function tableToCSV(headers: string[], rows: string[][]): string {
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 = "";
let remix = "";
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";
@@ -87,7 +71,9 @@ export default function markedTable(): MarkedExtension {
const csvData = tableToCSV(headers, rows);
// 渲染为 md-table 组件,内联 CSV 数据
return `<md-table ${roll}${spark}>${csvData}</md-table>\n`;
// data-spark attribute is injected by the CLI directive scanner,
// not computed here.
return `<md-table ${roll}${remix}>${csvData}</md-table>\n`;
},
},
};