fix: docs

This commit is contained in:
hypercross 2026-03-18 13:02:50 +08:00
parent 4f9d295bd5
commit 8213092bb6
3 changed files with 609 additions and 170 deletions

432
docs/csv.md Normal file
View File

@ -0,0 +1,432 @@
# CSV 编写说明
TTRPG Tools 使用带有 YAML Front Matter 的 CSV 格式来定义卡牌/表格数据。这种格式支持在 CSV 文件头部定义元数据、字段配置和共享属性。
## 文件结构
```csv
---
# YAML Front Matter可选
fields:
- name: 字段名
description: 字段描述
deck:
size: 54x86
grid: 5x8
shared_prop: 共享属性值
---
# CSV 数据
label,body,field1,field2
1内容值 1值 2
```
---
## YAML Front Matter
Front Matter 是可选的,位于文件开头,使用 `---` 包裹的 YAML 格式内容。
### 基本结构
```yaml
---
fields:
- name: name
description: 卡牌名称
- name: effect
description: 效果描述
examples: ["造成 5d6 伤害", "恢复 2d4 生命值"]
deck:
size: 63x88
grid: 5x5
bleed: 1
padding: 2
custom_prop: 自定义属性
---
```
### Front Matter 属性
#### `fields` - 字段定义
定义 CSV 中各列的含义和配置。
```yaml
fields:
- name: name # 字段名称(英文,用于 CSV 列名)
description: 卡牌名称 # 字段描述
examples: # 示例值列表(可选)
- 火球术卷轴
- 治疗药水
dataType: word # 数据类型可选word/paragraph/number/symbol/symbol_list
function: identify # 字段功能可选identify/compare/flavor/rule
```
**数据类型说明:**
| 类型 | 说明 | 示例 |
|------|------|------|
| `word` | 词语 - 短文本 | "火球术"、"战士"、"传说" |
| `paragraph` | 文本段落 - 长描述 | "造成 5d6 点火焰伤害,范围内的所有生物进行敏捷豁免" |
| `number` | 数字 - 可比较的数值 | 3、5、10 |
| `symbol` | 单一符号 - 图标/标记 | 🔥、⚔️、🛡️ |
| `symbol_list` | 符号列表 - 多个图标/标记 | 🔥🔥🔥、⚔️🛡️💫 |
**字段功能说明:**
| 功能 | 说明 | 示例 |
|------|------|------|
| `identify` | 辨识 - 用于识别卡牌身份 | 名称、编号、唯一标识 |
| `compare` | 比较 - 用于游戏机制中的数值比较 | 费用、攻击力、防御力 |
| `flavor` | 游戏风味描述 - 提供背景故事/氛围 | 背景故事、引言、lore |
| `rule` | 规则确认 - 明确游戏规则相关的信息 | 效果文本、触发条件、限制 |
#### `deck` - 卡牌显示配置
用于 `:md-deck` 组件的默认配置。
```yaml
deck:
size: 63x88 # 卡牌尺寸mm格式 "宽 x 高"
grid: 5x5 # 网格布局,格式 "列 x 行"
bleed: 1 # 出血边距mm
padding: 2 # 内边距mm
shape: rectangle # 卡牌形状rectangle/circle/hex/diamond
layers: "name:1-2,14" # 图层配置(可选)
back_layers: "" # 背面图层配置(可选)
```
图层配置语法遵循`layer:1,1-5,8f8s`格式:
- `layer`:图层名称,多个图层用空格分隔
- `1,1-5,8`:网格范围,覆盖卡牌排版网格中的指定区域。
- `f8`字体大小使用8mm字体。可选。
- `s`图层朝向有nwse东西南北四种方向描述文字上侧的朝向默认为n北方。
#### 自定义属性
可以在 Front Matter 中定义任意自定义属性,这些属性会被自动应用到所有卡牌行。
```yaml
---
game_system: 龙与地下城 5e
author: DM_Name
version: 1.0
---
```
---
## CSV 数据部分
### 基本格式
```csv
label,body,field1,field2
1内容值 1值 2
```
### 必需列
#### `label`
每行的唯一标识,用于查找和修改卡牌。
- 可以是数字:`1`, `2`, `3`
- 可以是文本:`card_001`, `npc_villager`
- 支持分组前缀:`forest_1`, `dungeon_1`
#### `body`
卡牌的主要内容,支持 Markdown 格式。
```csv
label,body
1,"### 标题
**加粗文本**
- 列表项 1
- 列表项 2"
```
### 可选列
`label``body` 外,可以定义任意自定义列,列名在 Front Matter 的 `fields` 中定义。
```csv
label,body,name,type,cost,effect
1完整内容火球术法术3造成 5d6 火焰伤害
```
---
## 变量语法
`body` 列或 Front Matter 中,可以使用 `{{prop}}` 语法引用其他列的值或 Front Matter 中的属性。
### 引用同行其他列
```csv
label,body,adj,noun
1,"**{{adj}}** 的{{noun}}",高大,战士
2,"{{adj}}的{{noun}}",矮小,法师
```
渲染后:
- 第 1 行:**高大** 的战士
- 第 2 行:矮小的法师
### 引用 Front Matter 属性
```yaml
---
game_system: D&D 5e
author: DM_Name
---
```
```csv
label,body
1,"这是一个{{game_system}}冒险,由{{author}}创建"
```
渲染后:这是一个 D&D 5e 冒险,由 DM_Name 创建
### 随机引用remix 模式)
`:md-table` 组件中,可以使用 `remix` 属性启用随机引用:
```markdown
:md-table[./data.csv]{remix=true}
```
此时 `{{prop}}` 会从所有行中随机选择一行的值,而不是使用当前行的值。
---
## 分组支持
可以使用额外的列来对数据进行分组。
```csv
group,label,body
森林小径1狼群袭击
森林小径2荆棘陷阱
森林小径3迷路
塔楼楼梯1台阶坍塌
塔楼楼梯2落石
```
`:md-table` 组件中,可以使用 `group` 属性来筛选显示特定组的数据:
```markdown
:md-table[./encounters.csv]{group="森林小径"}
```
---
## 特殊字符处理
### 包含逗号的值
使用双引号包裹:
```csv
label,body
1,"Hello, world"
```
### 包含双引号的值
使用两个双引号转义:
```csv
label,body
1,"他说:""你好"""
```
### 包含换行的值
使用双引号包裹,直接写入换行:
```csv
label,body
1,"### 标题
这是第一段
这是第二段"
```
### 包含 `#` 注释
CSV 解析器会将 `#` 开头的行视为注释,但如果 `#` 在双引号内则正常处理:
```csv
label,body
1,"# 这不是注释"
# 这是真正的注释
```
---
## 完整示例
### 示例 1魔法物品卡牌组
```csv
---
fields:
- name: name
description: 物品名称
dataType: word
function: identify
- name: type
description: 物品类型
dataType: word
function: identify
examples: [武器,防具,饰品,消耗品]
- name: rarity
description: 稀有度
dataType: word
function: compare
examples: [普通,稀有,史诗,传说]
- name: cost
description: 价格
dataType: number
function: compare
- name: effect
description: 效果描述
dataType: paragraph
function: rule
deck:
size: 63x88
grid: 5x5
shape: rectangle
game_system: D&D 5e
---
label,body,name,type,rarity,cost,effect
1,"### 火球术卷轴
一张泛黄的羊皮纸,上面绘有火焰符文。",火球术卷轴消耗品稀有150,"投掷后对 20 尺内所有生物造成 8d6 火焰伤害(敏捷豁免减半)"
2,"### 治疗药水
红色液体在玻璃瓶中翻滚,散发着温暖的光芒。",治疗药水消耗品普通50,"饮用后恢复 2d4+2 点生命值"
3,"### +1 长剑
剑刃闪烁着微光,握柄包裹着精致的皮革。",+1 长剑武器稀有300,"攻击和伤害检定 +1"
```
### 示例 2随机遭遇表
```csv
---
fields:
- name: group
description: 遭遇地点分组
- name: title
description: 遭遇标题
- name: type
description: 遭遇类型
deck:
size: 54x86
grid: 4x6
---
group,label,body,title,type
森林小径1,"### 狼群袭击
三只灰狼从灌木丛中扑出。
**交互**战斗3 只巨狼)或逃脱(敏捷检定 3/2
**风险**:失败受到 d8 撕咬伤害",狼群袭击,战斗
森林小径2,"### 荆棘陷阱
带刺藤蔓缠住你的双腿。
**交互**:挣脱(力量检定 3/2
**风险**:失败受到 1 点体质损伤",荆棘陷阱,陷阱
森林小径3,"### 迷路
浓雾弥漫,你失去了方向。
**交互**:寻找路径(感知检定 3/2
**风险**:失败受到 1 点感知损伤",迷路,事件
塔楼楼梯1,"### 台阶坍塌
脚下的石阶突然碎裂。
**交互**:保持平衡(敏捷检定 3/1
**风险**:失败受到 1 点敏捷损伤",台阶坍塌,陷阱
```
### 示例 3NPC 名录
```csv
---
fields:
- name: name
description: NPC 名称
- name: role
description: 角色定位
- name: location
description: 出现地点
deck:
size: 54x86
grid: 4x6
---
label,body,name,role,location
1,"### 老妇人玛拉
**类型**:信息提供者/商人
**位置**:村庄入口
**出售**:草药、治疗药水
**情报**:知道关于森林的警告",老妇人玛拉,商人,村庄入口
2,"### 护卫队长塞里克
**类型**:任务发布者
**位置**:酒馆二楼
**任务**:调查废弃庄园
**秘密**:知道血契的真相",塞里克,任务发布者,酒馆
```
---
## 与组件集成
### `:md-deck` 组件
```markdown
:md-deck[./magic-items.csv]{size="63x88" grid="5x5" roll=true}
```
- `roll=true`:添加随机抽卡按钮
### `:md-table` 组件
```markdown
:md-table[./encounters.csv]{group="森林小径" remix=true}
```
- `group`:筛选特定组的数据
- `remix=true`:启用随机引用模式
---
## 最佳实践
1. **使用有意义的 label**:便于后续查找和修改
2. **在 Front Matter 中定义所有字段**:便于工具理解和处理
3. **使用 `{{prop}}` 变量**:减少重复内容,支持动态组合
4. **合理使用分组**:便于筛选和管理大量数据
5. **保持 body 的 Markdown 格式一致**:确保渲染效果统一
6. **为字段添加 `examples`**:帮助 AI 工具生成内容时参考

View File

@ -90,111 +90,6 @@ async function mcpServeAction(host: string, options: MCPOptions) {
server.setRequestHandler(ListToolsRequestSchema, async () => { server.setRequestHandler(ListToolsRequestSchema, async () => {
return { return {
tools: [ tools: [
{
name: 'generate_card_deck',
description: '生成 TTRPG 卡牌组内容,包括 Markdown 介绍文件、CSV 数据文件和 md-deck 组件配置(一站式快捷工具)',
inputSchema: {
type: 'object',
properties: {
deck_name: {
type: 'string',
description: '卡牌组名称,将用于生成文件名',
},
output_dir: {
type: 'string',
description: '输出目录路径(相对路径相对于 MCP 服务器工作目录)',
},
card_count: {
type: 'number',
description: '卡牌数量',
default: 10,
},
card_template: {
type: 'object',
description: '卡牌模板定义',
properties: {
fields: {
type: 'array',
description: '卡牌字段列表',
items: {
type: 'object',
properties: {
name: {
type: 'string',
description: '字段名称(英文,用于 CSV 列名)',
},
description: {
type: 'string',
description: '字段描述',
},
examples: {
type: 'array',
description: '示例值列表',
items: { type: 'string' },
},
},
required: ['name'],
},
},
examples: {
type: 'array',
description: '完整的卡牌示例数据',
items: {
type: 'object',
additionalProperties: { type: 'string' },
},
},
},
required: ['fields'],
},
deck_config: {
type: 'object',
description: 'md-deck 组件配置',
properties: {
size: {
type: 'string',
description: '卡牌尺寸,格式 "宽 x 高"(单位 mm',
default: '54x86',
},
grid: {
type: 'string',
description: '网格布局,格式 "列 x 行"',
default: '5x8',
},
bleed: {
type: 'number',
description: '出血边距mm',
default: 1,
},
padding: {
type: 'number',
description: '内边距mm',
default: 2,
},
shape: {
type: 'string',
description: '卡牌形状',
enum: ['rectangle', 'circle', 'hex', 'diamond'],
default: 'rectangle',
},
layers: {
type: 'string',
description: '正面图层配置,格式 "字段:行,列范围,字体大小"',
},
back_layers: {
type: 'string',
description: '背面图层配置',
},
},
},
description: {
type: 'string',
description: '卡牌组的介绍描述(可选)',
},
},
required: ['deck_name', 'output_dir'],
},
},
{ {
name: 'deck_frontmatter_read', name: 'deck_frontmatter_read',
description: '读取 CSV 文件的 frontmatter包含模板定义和 deck 配置)', description: '读取 CSV 文件的 frontmatter包含模板定义和 deck 配置)',

View File

@ -55,6 +55,48 @@ export interface ResourceContent {
}; };
} }
/**
*
*/
export type DataType = 'word' | 'paragraph' | 'number' | 'symbol' | 'symbol_list';
/**
*
*/
export type FieldFunction = 'identify' | 'compare' | 'flavor' | 'rule';
/**
*
*/
export interface FieldDefinition {
name: string;
description: string;
dataType: DataType;
function: FieldFunction;
examples?: string[];
}
/**
*
*/
const DATA_TYPE_DESCRIPTIONS: Record<DataType, string> = {
word: '词语 - 短文本(如名称、类型、职业等)',
paragraph: '文本段落 - 长描述(如效果说明、背景故事等)',
number: '数字 - 可比较的数值(如费用、强度、等级等)',
symbol: '单一符号 - 图标/标记(如阵营符号、元素标志等)',
symbol_list: '符号列表 - 多个图标/标记(如属性加成列表、技能图标等)'
};
/**
*
*/
const FIELD_FUNCTION_DESCRIPTIONS: Record<FieldFunction, string> = {
identify: '辨识 - 用于识别卡牌身份(如名称、编号等)',
compare: '比较 - 用于游戏机制中的数值比较(如费用、攻击力等)',
flavor: '游戏风味描述 - 提供背景故事/氛围(如背景故事、引言等)',
rule: '规则确认 - 明确游戏规则相关的信息(如效果文本、触发条件等)'
};
/** /**
* design-card-game prompt * design-card-game prompt
*/ */
@ -77,80 +119,150 @@ export function designCardGame(options?: DesignCardGameOptions): DesignCardGameR
type: 'text', type: 'text',
text: `好的!我来帮你设计"${deckName}"卡牌组的字段结构。 text: `好的!我来帮你设计"${deckName}"卡牌组的字段结构。
## 1.
- [ ] Characters-
- [ ] Items-
- [ ] /Spells/Abilities-
- [ ] Locations-
- [ ] Events-
- [ ] _______
## 2.
- /
-
- /
- /
## 3.
-
- /
- /
- _______
--- ---
**** ## 1
### ****
\`\`\`json -
{ -
"fields": [ -
{"name": "name", "description": "物品名称"}, -
{"name": "type", "description": "物品类型", "examples": ["武器", "防具", "饰品", "消耗品"]},
{"name": "rarity", "description": "稀有度", "examples": ["普通", "稀有", "史诗", "传说"]}, ****
{"name": "cost", "description": "价格/费用"}, - ...
{"name": "effect", "description": "效果描述"}
] ---
}
## 2
| | | |
|------|------|------|
| word | | "火球术""战士""传说" |
| paragraph | | "造成 5d6 点火焰伤害,范围内的所有生物进行敏捷豁免" |
| number | | 3510 |
| symbol | / | 🔥🛡 |
| symbol_list | / | 🔥🔥🔥🛡💫 |
---
## 3
| | | |
|------|------|------|
| identify | | |
| compare | | |
| flavor | / | lore |
| rule | | |
---
## 4/
****
使 symbol symbol_list
---
##
\`\`\`
### 1[]
- ****[]
- ****[word/paragraph/number/symbol/symbol_list]
- ****[identify/compare/flavor/rule]
- ****[2-3 ]
### 2[]
...
\`\`\` \`\`\`
### NPC ---
\`\`\`json
{ ##
"fields": [
{"name": "name", "description": "角色名称"}, ###
{"name": "class", "description": "职业", "examples": ["战士", "法师", "盗贼", "牧师"]},
{"name": "level", "description": "等级"}, \`\`\`
{"name": "background", "description": "背景故事"}, ### 1name
{"name": "ability", "description": "特殊能力"} - ****
] - ****word
} - ****identify
- ****
### 2cost
- ****使
- ****number
- ****compare
- ****352
### 3type
- ****
- ****word
- ****identify
- ****
### 4rarity
- ****
- ****word
- ****compare
- ****
### 5effect
- ****使
- ****paragraph
- ****rule
- **** 5d6 2d4+2
\`\`\` \`\`\`
### ###
\`\`\`json
{ \`\`\`
"fields": [ ### 1name
{"name": "name", "description": "牌名"}, - ****
{"name": "number", "description": "编号"}, - ****word
{"name": "element", "description": "元素", "examples": ["火", "水", "风", "土"]}, - ****identify
{"name": "meaning", "description": "含义"}, - ****
{"name": "reversed", "description": "逆位含义"}
] ### 2number
} - ****
\`\`\`` - ****number
- ****identify
- ****012
### 3element
- ****
- ****symbol
- ****flavor
- ****🔥💧💨🌍
### 4meaning
- ****
- ****paragraph
- ****rule
- ****
### 5reversed
- ****
- ****paragraph
- ****rule
- ****
\`\`\`
---
`
} }
} }
]; ];