fix: mcp
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
||||
import { dirname, join, basename, extname } from 'path';
|
||||
import yaml from 'js-yaml';
|
||||
import type { DeckFrontmatter, DeckConfig } from './frontmatter/read-frontmatter.js';
|
||||
|
||||
/**
|
||||
* 确保 Deck 预览文件的参数
|
||||
*/
|
||||
export interface EnsureDeckPreviewParams {
|
||||
/**
|
||||
* CSV 文件路径
|
||||
*/
|
||||
csv_file: string;
|
||||
/**
|
||||
* Markdown 文件路径(可选,默认与 CSV 同名)
|
||||
*/
|
||||
md_file?: string;
|
||||
/**
|
||||
* 标题(可选,默认从 CSV 文件名推断)
|
||||
*/
|
||||
title?: string;
|
||||
/**
|
||||
* 描述(可选)
|
||||
*/
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 确保 Deck 预览文件的结果
|
||||
*/
|
||||
export interface EnsureDeckPreviewResult {
|
||||
success: boolean;
|
||||
message: string;
|
||||
md_file?: string;
|
||||
created?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 CSV 文件的 frontmatter
|
||||
*/
|
||||
function parseFrontMatter(content: string): { frontmatter?: DeckFrontmatter; csvContent: string } {
|
||||
const parts = content.trim().split(/(?:^|\n)---\s*\n/g);
|
||||
|
||||
if (parts.length !== 3 || parts[0] !== '') {
|
||||
return { csvContent: content };
|
||||
}
|
||||
|
||||
try {
|
||||
const frontmatterStr = parts[1].trim();
|
||||
const frontmatter = yaml.load(frontmatterStr) as DeckFrontmatter | undefined;
|
||||
const csvContent = parts.slice(2).join('---\n').trimStart();
|
||||
return { frontmatter, csvContent };
|
||||
} catch (error) {
|
||||
console.warn('Failed to parse front matter:', error);
|
||||
return { csvContent: content };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 md-deck 组件代码
|
||||
*/
|
||||
function buildDeckComponent(csvPath: string, config?: DeckConfig): string {
|
||||
const parts = [`:md-deck[${csvPath}]`];
|
||||
const attrs: string[] = [];
|
||||
|
||||
if (config?.size) {
|
||||
attrs.push(`size="${config.size}"`);
|
||||
}
|
||||
|
||||
if (config?.grid) {
|
||||
attrs.push(`grid="${config.grid}"`);
|
||||
}
|
||||
|
||||
if (config?.bleed !== undefined && config.bleed !== 1) {
|
||||
attrs.push(`bleed="${config.bleed}"`);
|
||||
}
|
||||
|
||||
if (config?.padding !== undefined && config.padding !== 2) {
|
||||
attrs.push(`padding="${config.padding}"`);
|
||||
}
|
||||
|
||||
if (config?.shape && config.shape !== 'rectangle') {
|
||||
attrs.push(`shape="${config.shape}"`);
|
||||
}
|
||||
|
||||
if (config?.layers) {
|
||||
attrs.push(`layers="${config.layers}"`);
|
||||
}
|
||||
|
||||
if (config?.back_layers) {
|
||||
attrs.push(`back-layers="${config.back_layers}"`);
|
||||
}
|
||||
|
||||
if (attrs.length > 0) {
|
||||
parts.push(`{${attrs.join(' ')}}`);
|
||||
}
|
||||
|
||||
return parts.join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* 确保 Markdown 预览文件存在
|
||||
*/
|
||||
export function ensureDeckPreview(params: EnsureDeckPreviewParams): EnsureDeckPreviewResult {
|
||||
const { csv_file, md_file, title, description } = params;
|
||||
|
||||
// 检查 CSV 文件是否存在
|
||||
if (!existsSync(csv_file)) {
|
||||
return {
|
||||
success: false,
|
||||
message: `CSV 文件不存在:${csv_file}`
|
||||
};
|
||||
}
|
||||
|
||||
// 确定 Markdown 文件路径
|
||||
let mdFilePath = md_file;
|
||||
if (!mdFilePath) {
|
||||
// 使用与 CSV 相同的路径和文件名,只是扩展名不同
|
||||
const dir = dirname(csv_file);
|
||||
const name = basename(csv_file, extname(csv_file));
|
||||
mdFilePath = join(dir, `${name}.md`);
|
||||
}
|
||||
|
||||
const created = !existsSync(mdFilePath);
|
||||
|
||||
// 如果文件已存在,检查是否已有 md-deck 组件
|
||||
if (!created) {
|
||||
try {
|
||||
const existingContent = readFileSync(mdFilePath, 'utf-8');
|
||||
// 如果已有 md-deck 组件引用该 CSV,直接返回
|
||||
if (existingContent.includes(`:md-deck[${csv_file}]`) ||
|
||||
existingContent.includes(`:md-deck[./${basename(csv_file)}]`)) {
|
||||
return {
|
||||
success: true,
|
||||
message: `预览文件 ${mdFilePath} 已存在`,
|
||||
md_file: mdFilePath,
|
||||
created: false
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
// 读取失败,继续创建
|
||||
}
|
||||
}
|
||||
|
||||
// 读取 CSV 的 frontmatter 获取配置
|
||||
let deckConfig: DeckConfig | undefined;
|
||||
try {
|
||||
const csvContent = readFileSync(csv_file, 'utf-8');
|
||||
const { frontmatter } = parseFrontMatter(csvContent);
|
||||
deckConfig = frontmatter?.deck;
|
||||
} catch (error) {
|
||||
// 忽略错误,使用默认配置
|
||||
}
|
||||
|
||||
// 确定标题
|
||||
let deckTitle = title;
|
||||
if (!deckTitle) {
|
||||
// 从 CSV 文件名推断
|
||||
const name = basename(csv_file, extname(csv_file));
|
||||
deckTitle = name.replace(/[-_]/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
|
||||
}
|
||||
|
||||
// 构建 md-deck 组件代码(使用相对路径)
|
||||
const mdDir = dirname(mdFilePath);
|
||||
const csvDir = dirname(csv_file);
|
||||
let relativeCsvPath: string;
|
||||
|
||||
if (mdDir === csvDir) {
|
||||
relativeCsvPath = `./${basename(csv_file)}`;
|
||||
} else {
|
||||
relativeCsvPath = `./${basename(csv_file)}`;
|
||||
}
|
||||
|
||||
const deckComponent = buildDeckComponent(relativeCsvPath, deckConfig);
|
||||
|
||||
// 生成 Markdown 内容
|
||||
const mdLines: string[] = [];
|
||||
|
||||
mdLines.push(`# ${deckTitle}`);
|
||||
mdLines.push('');
|
||||
|
||||
if (description) {
|
||||
mdLines.push(description);
|
||||
mdLines.push('');
|
||||
}
|
||||
|
||||
mdLines.push('## 卡牌预览');
|
||||
mdLines.push('');
|
||||
mdLines.push(deckComponent);
|
||||
mdLines.push('');
|
||||
|
||||
mdLines.push('## 使用说明');
|
||||
mdLines.push('');
|
||||
mdLines.push('- 点击卡牌可以查看详情');
|
||||
mdLines.push('- 使用右上角的按钮可以随机抽取卡牌');
|
||||
mdLines.push('- 可以通过编辑面板调整卡牌样式和布局');
|
||||
mdLines.push('');
|
||||
|
||||
// 写入文件
|
||||
try {
|
||||
writeFileSync(mdFilePath, mdLines.join('\n'), 'utf-8');
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: created
|
||||
? `创建预览文件 ${mdFilePath}`
|
||||
: `更新预览文件 ${mdFilePath}`,
|
||||
md_file: mdFilePath,
|
||||
created
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: `写入失败:${error instanceof Error ? error.message : '未知错误'}`
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user