import { readFileSync, writeFileSync, existsSync } from "fs"; import { relative } from "path"; import { execSync } from "child_process"; import { buildDeckComponent, titleFromCsvPath, defaultMdPath, readDeckConfig, generateDeckPreviewMd, } from "./deck-utils.js"; /** * 预览 Deck 的参数 */ export interface PreviewDeckParams { /** * CSV 文件路径 */ csv_file: string; /** * Markdown 文件路径(可选,默认与 CSV 同名) */ md_file?: string; /** * 标题(可选,默认从 CSV 文件名推断) */ title?: string; /** * 描述(可选) */ description?: string; } /** * 预览 Deck 的结果 */ export interface PreviewDeckResult { success: boolean; message: string; md_file?: string; created?: boolean; preview_url?: string; } /** * 打开浏览器 */ function openBrowser(url: string) { try { if (process.platform === "win32") { execSync(`start "" "${url}"`, { stdio: "ignore" }); } else if (process.platform === "darwin") { execSync(`open "${url}"`, { stdio: "ignore" }); } else { execSync(`xdg-open "${url}"`, { stdio: "ignore" }); } console.error(`已打开浏览器:${url}`); } catch (error) { console.error("打开浏览器失败:", error); } } /** * 计算预览 URL */ function computePreviewUrl(mdFilePath: string): string { const relativeMdPath = relative(process.cwd(), mdFilePath) .split("\\") .join("/") .replace(/\.md$/, ""); return `http://localhost:3000/${relativeMdPath}`; } /** * 预览 Deck - 保存 Markdown 并打开浏览器 */ export function previewDeck(params: PreviewDeckParams): PreviewDeckResult { const { csv_file, md_file, title, description } = params; // 检查 CSV 文件是否存在 if (!existsSync(csv_file)) { return { success: false, message: `CSV 文件不存在:${csv_file}`, }; } // 确定 Markdown 文件路径 const mdFilePath = md_file || defaultMdPath(csv_file); const created = !existsSync(mdFilePath); // 如果文件已存在,检查是否已有 md-deck 组件 if (!created) { try { const existingContent = readFileSync(mdFilePath, "utf-8"); if ( existingContent.includes(`:md-deck[${csv_file}]`) || existingContent.includes(`:md-deck[./${csv_file.split("/").pop()}]`) ) { const previewUrl = computePreviewUrl(mdFilePath); openBrowser(previewUrl); return { success: true, message: `预览文件 ${mdFilePath} 已存在`, md_file: mdFilePath, created: false, preview_url: previewUrl, }; } } catch { // 读取失败,继续创建 } } // 读取 CSV 的 frontmatter 获取配置 const deckConfig = readDeckConfig(csv_file); // 确定标题 const deckTitle = title || titleFromCsvPath(csv_file); // 构建 md-deck 组件代码 const deckComponent = buildDeckComponent( `./${csv_file.split("/").pop()}`, deckConfig, ); // 生成 Markdown 内容 const mdContent = generateDeckPreviewMd( deckTitle, deckComponent, description, ); // 写入文件 try { writeFileSync(mdFilePath, mdContent, "utf-8"); const previewUrl = computePreviewUrl(mdFilePath); openBrowser(previewUrl); return { success: true, message: created ? `创建预览文件 ${mdFilePath}` : `更新预览文件 ${mdFilePath}`, md_file: mdFilePath, created, preview_url: previewUrl, }; } catch (error) { return { success: false, message: `写入失败:${error instanceof Error ? error.message : "未知错误"}`, }; } }