155 lines
3.4 KiB
TypeScript
155 lines
3.4 KiB
TypeScript
import { readFileSync, existsSync } from 'fs';
|
||
import yaml from 'js-yaml';
|
||
|
||
/**
|
||
* 读取 CSV frontmatter 的参数
|
||
*/
|
||
export interface ReadFrontmatterParams {
|
||
/**
|
||
* CSV 文件路径(相对路径相对于 MCP 服务器工作目录)
|
||
*/
|
||
csv_file: string;
|
||
}
|
||
|
||
/**
|
||
* Frontmatter 数据结构
|
||
*/
|
||
export interface DeckFrontmatter {
|
||
/**
|
||
* 字段定义
|
||
*/
|
||
fields?: CardField[];
|
||
/**
|
||
* Deck 配置
|
||
*/
|
||
deck?: DeckConfig;
|
||
/**
|
||
* 其他自定义属性
|
||
*/
|
||
[key: string]: unknown;
|
||
}
|
||
|
||
/**
|
||
* 字段样式配置
|
||
*
|
||
* 位置按照卡牌网格,安排 x,y,w,h 四个整数
|
||
* 如 5x8 网格中 1,1,5,8 表示覆盖整个卡牌的区域
|
||
*
|
||
* 字体使用 f:8 来表示 8mm 字体
|
||
*
|
||
* 朝向使用 u:n/w/s/e 表示字段的上侧朝向北西南东
|
||
*/
|
||
export interface CardFieldStyle {
|
||
/**
|
||
* 位置:[x, y, w, h] 网格坐标和尺寸
|
||
* 例如:[1, 1, 5, 8] 表示从 (1,1) 开始,宽 5 格,高 8 格
|
||
*/
|
||
pos?: [number, number, number, number];
|
||
/**
|
||
* 字体大小:格式 "f:8" 表示 8mm 字体
|
||
*/
|
||
font?: string;
|
||
/**
|
||
* 朝向:上侧朝向 "n" | "w" | "s" | "e"(北/西/南/东)
|
||
*/
|
||
up?: 'n' | 'w' | 's' | 'e';
|
||
}
|
||
|
||
/**
|
||
* 卡牌字段定义
|
||
*/
|
||
export interface CardField {
|
||
name: string;
|
||
description?: string;
|
||
examples?: string[];
|
||
style?: CardFieldStyle;
|
||
[key: string]: unknown;
|
||
}
|
||
|
||
/**
|
||
* Deck 配置
|
||
*/
|
||
export interface DeckConfig {
|
||
size?: string;
|
||
grid?: string;
|
||
bleed?: number;
|
||
padding?: number;
|
||
shape?: 'rectangle' | 'circle' | 'hex' | 'diamond';
|
||
layers?: string;
|
||
back_layers?: string;
|
||
[key: string]: unknown;
|
||
}
|
||
|
||
/**
|
||
* 读取 CSV frontmatter 的结果
|
||
*/
|
||
export interface ReadFrontmatterResult {
|
||
success: boolean;
|
||
frontmatter?: DeckFrontmatter;
|
||
message: string;
|
||
}
|
||
|
||
/**
|
||
* 解析 CSV 文件的 frontmatter
|
||
*/
|
||
function parseFrontMatter(content: string): { frontmatter?: DeckFrontmatter; csvContent: string } {
|
||
const parts = content.trim().split(/(?:^|\n)---\s*\n/g);
|
||
|
||
// 至少需要三个部分:空字符串、front matter、CSV 内容
|
||
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 };
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 读取 CSV 文件的 frontmatter
|
||
*/
|
||
export function readFrontmatter(params: ReadFrontmatterParams): ReadFrontmatterResult {
|
||
const { csv_file } = params;
|
||
|
||
// 检查文件是否存在
|
||
if (!existsSync(csv_file)) {
|
||
return {
|
||
success: false,
|
||
message: `文件不存在:${csv_file}`
|
||
};
|
||
}
|
||
|
||
try {
|
||
// 读取文件内容
|
||
const content = readFileSync(csv_file, 'utf-8');
|
||
|
||
// 解析 frontmatter
|
||
const { frontmatter } = parseFrontMatter(content);
|
||
|
||
if (!frontmatter) {
|
||
return {
|
||
success: true,
|
||
frontmatter: {},
|
||
message: `文件 ${csv_file} 没有 frontmatter,返回空对象`
|
||
};
|
||
}
|
||
|
||
return {
|
||
success: true,
|
||
frontmatter,
|
||
message: `成功读取 ${csv_file} 的 frontmatter`
|
||
};
|
||
} catch (error) {
|
||
return {
|
||
success: false,
|
||
message: `读取失败:${error instanceof Error ? error.message : '未知错误'}`
|
||
};
|
||
}
|
||
}
|