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:
@@ -13,6 +13,7 @@ import { createSignal } from "solid-js";
|
||||
import {
|
||||
getPathsByExtension,
|
||||
getIndexedData,
|
||||
setIndexedData,
|
||||
setInlineResolver,
|
||||
} from "../../data-loader/file-index";
|
||||
import {
|
||||
@@ -119,6 +120,14 @@ async function scanClientSide(): Promise<JournalCompletions> {
|
||||
|
||||
const registry = buildRegistryFromIndex(index);
|
||||
activeRegistry = registry;
|
||||
|
||||
// Write the processed (stripped) content back into the file index so
|
||||
// Article/md-embed render the same content as CLI mode (spark tables
|
||||
// coerced, attributed blocks processed, data-spark injected).
|
||||
for (const [path, content] of Object.entries(registry.pathIndex)) {
|
||||
setIndexedData(path, content);
|
||||
}
|
||||
|
||||
return deriveCompletions(registry);
|
||||
}
|
||||
|
||||
|
||||
+18
-13
@@ -7,9 +7,9 @@ import {
|
||||
createMemo,
|
||||
createResource,
|
||||
} from "solid-js";
|
||||
import { marked } from "../markdown";
|
||||
import { loadCSV, CSV, processVariables, isCSV } from "./utils/csv-loader";
|
||||
import { resolvePath } from "./utils/path";
|
||||
import { parseMarkdown } from "../markdown";
|
||||
import { loadCSV, CSV, processVariables } from "./utils/csv-loader";
|
||||
import { resolveContentRef } from "./utils/resolve-content";
|
||||
import {
|
||||
areAllLabelsNumeric,
|
||||
weightedRandomIndex,
|
||||
@@ -51,13 +51,17 @@ customElement(
|
||||
const articleEl = element?.closest("article[data-src]");
|
||||
const articlePath = articleEl?.getAttribute("data-src") || "";
|
||||
|
||||
// 如果是 inline CSV,直接使用;否则解析相对路径
|
||||
const contentOrPath = isCSV(rawContent)
|
||||
? rawContent
|
||||
: resolvePath(articlePath, rawContent);
|
||||
|
||||
// 使用 createResource 加载 CSV,自动响应路径变化并避免重复加载
|
||||
const [csvData] = createResource(() => contentOrPath, loadCSV);
|
||||
// 解析引用:inline CSV 直接使用,否则通过 registry 解析(含内联内容 id)
|
||||
const [csvData] = createResource(
|
||||
() => ({ ref: rawContent, docPath: articlePath }),
|
||||
async ({ ref, docPath }) => {
|
||||
const content = await resolveContentRef(ref, docPath);
|
||||
if (content === null) {
|
||||
throw new Error(`Failed to resolve table content: "${ref}"`);
|
||||
}
|
||||
return loadCSV(content);
|
||||
},
|
||||
);
|
||||
|
||||
// 当数据加载完成后更新 rows
|
||||
createEffect(() => {
|
||||
@@ -97,10 +101,11 @@ customElement(
|
||||
|
||||
// 处理 body 内容中的 {{prop}} 语法并解析 markdown
|
||||
const processBody = (body: string, currentRow: TableRow): string => {
|
||||
// 使用 marked 解析 markdown
|
||||
return marked.parse(
|
||||
// 使用 parseMarkdown 统一入口(设置图标 base path 等)
|
||||
return parseMarkdown(
|
||||
processVariables(body, currentRow, rows(), filteredRows(), props.remix),
|
||||
) as string;
|
||||
articlePath,
|
||||
);
|
||||
};
|
||||
|
||||
// 更新 body 内容
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { resolveContent } from "../../cli/content-registry";
|
||||
import { getRegistry } from "../journal/completions";
|
||||
import { getIndexedData } from "../../data-loader/file-index";
|
||||
import { resolvePath } from "./path";
|
||||
|
||||
/**
|
||||
* Resolve a content reference (a directive body) to raw content.
|
||||
*
|
||||
* Prefers the shared content-registry resolution, which understands:
|
||||
* - inline CSV bodies (returned as-is)
|
||||
* - inline doc content refs like `./csv_abc123` (scoped to the current doc)
|
||||
* - absolute paths and relative paths resolved against the current doc
|
||||
*
|
||||
* Falls back to path resolution + the file index when the registry isn't
|
||||
* populated yet (e.g. before completions load) or the ref isn't indexed.
|
||||
*
|
||||
* Returns `null` when nothing matches.
|
||||
*/
|
||||
export async function resolveContentRef(
|
||||
ref: string,
|
||||
docPath: string,
|
||||
): Promise<string | null> {
|
||||
const trimmed = ref.trim();
|
||||
if (!trimmed) return null;
|
||||
|
||||
const resolved = resolveContent(getRegistry(), docPath, trimmed);
|
||||
if (resolved !== null) return resolved;
|
||||
|
||||
// Fallback: resolve relative path and fetch through the file index.
|
||||
const path = resolvePath(docPath, trimmed);
|
||||
try {
|
||||
return await getIndexedData(path);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user