feat: Unify spark table detection and content resolution

Export isSparkTableHeader from content-registry and use it in both CLI
and frontend table parsing. Add resolveContentRef to resolve content
references consistently. Write processed registry content back to the
file index so browser mode renders identically to CLI mode.
This commit is contained in:
hyper
2026-08-07 13:15:51 +08:00
parent 0a68122efd
commit c03528a293
7 changed files with 114 additions and 18 deletions
+10 -3
View File
@@ -239,7 +239,14 @@ export function buildRegistryFromIndex(
// Spark table coercion
// ---------------------------------------------------------------------------
const DICE_HEADER_RE = /^\d*d\d+$/i;
/**
* Whether a table header cell is a dice formula (a "spark table" first
* column). Single source of truth shared by the CLI scanner and the frontend
* `markedTable` renderer so both agree on what counts as a spark table.
*/
export function isSparkTableHeader(header: string): boolean {
return /^\d*d\d+(?:[+-]\d+)?$/i.test(header.trim());
}
/** Split a markdown table row into cells. */
function splitTableRow(row: string): string[] {
@@ -310,7 +317,7 @@ function coerceSparkTables(
while ((mdMatch = mdTableRegex.exec(content)) !== null) {
const [, headerRow, separatorRow, bodyRowsText] = mdMatch;
const headers = splitTableRow(headerRow);
if (!DICE_HEADER_RE.test(headers[0])) continue;
if (!isSparkTableHeader(headers[0])) continue;
const bodyRows = bodyRowsText
.trim()
@@ -674,7 +681,7 @@ export function inspectSparkTableCsv(csv: string): string[] | null {
const headers = lines[0].split(",").map((h) => h.trim());
if (headers.length < 2) return null;
if (!DICE_HEADER_RE.test(headers[0])) return null;
if (!isSparkTableHeader(headers[0])) return null;
return headers.slice(1);
}