2026-03-18 12:08:28 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-18 13:48:19 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 字段样式配置
|
|
|
|
|
|
*
|
|
|
|
|
|
* 位置按照卡牌网格,安排 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';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-18 12:08:28 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 卡牌字段定义
|
|
|
|
|
|
*/
|
|
|
|
|
|
export interface CardField {
|
|
|
|
|
|
name: string;
|
|
|
|
|
|
description?: string;
|
|
|
|
|
|
examples?: string[];
|
2026-03-18 13:48:19 +08:00
|
|
|
|
style?: CardFieldStyle;
|
2026-03-18 12:08:28 +08:00
|
|
|
|
[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 : '未知错误'}`
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|