Compare commits
No commits in common. "0aaf81057a3f9ad0ae4d2f1ba71b0790bbc6fd2e" and "228327913e95bacd58593445e9670a8542c5fe7d" have entirely different histories.
0aaf81057a
...
228327913e
|
|
@ -22,8 +22,6 @@ ttrpg serve ./content
|
|||
| [📖 CLI 使用说明](./docs/cli.md) | CLI 安装、命令和用法 |
|
||||
| [🛠️ 开发指南](./docs/development.md) | 项目结构、开发规范和构建 |
|
||||
| [📝 Markdown 编写说明](./docs/markdown.md) | Markdown 语法和组件用法 |
|
||||
| [📊 CSV 编写说明](./docs/csv.md) | CSV 文件格式、字段定义、变量语法 |
|
||||
| [🤖 MCP 服务器说明](./docs/mcp.md) | AI 助手集成、卡牌生成工具 |
|
||||
|
||||
## 功能概览
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ ttrpg serve [dir] -p 3000
|
|||
| 选项 | 说明 | 默认值 |
|
||||
|------|------|--------|
|
||||
| `-p, --port <port>` | 端口号 | `3000` |
|
||||
| `-h, --host <host>` | 主机地址 | `0.0.0.0` |
|
||||
|
||||
**功能:**
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
| `[content]` | 内容参数 | `[2d6+d8]` |
|
||||
| `{attrs}` | 属性对象 | `{key="attack"}` |
|
||||
|
||||
支持多个嵌套指令:
|
||||
### 嵌套指令
|
||||
|
||||
```markdown
|
||||
::: container
|
||||
|
|
@ -36,40 +36,6 @@
|
|||
:::
|
||||
```
|
||||
|
||||
## 分栏布局
|
||||
|
||||
使用简单的分隔符语法创建多栏布局:
|
||||
|
||||
```markdown
|
||||
|---
|
||||
左侧栏内容
|
||||
-|-
|
||||
右侧栏内容
|
||||
---
|
||||
```
|
||||
|
||||
**语法说明:**
|
||||
|
||||
| 分隔符 | 说明 |
|
||||
|--------|------|
|
||||
| `\|---` | 开始分栏容器 |
|
||||
| `-|-` | 分隔各栏 |
|
||||
| `---` | 结束分栏容器 |
|
||||
|
||||
**多栏示例:**
|
||||
|
||||
```markdown
|
||||
|---
|
||||
第一栏
|
||||
-|-
|
||||
第二栏
|
||||
-|--
|
||||
第三栏(更宽)
|
||||
---
|
||||
```
|
||||
|
||||
`-|--` 中的额外 `-` 会添加 `-N` 后缀到 CSS 类名(如 `col-2`),可用于自定义宽度。
|
||||
|
||||
## 图标语法
|
||||
|
||||
使用简单的 `:[icon-name]` 语法插入图标:
|
||||
|
|
@ -307,27 +273,6 @@ label,name,description
|
|||
:md-table[./quests.csv]{roll=true remix=true}
|
||||
```
|
||||
|
||||
**自动表格转换:**
|
||||
|
||||
标准 Markdown 表格会自动转换为 `md-table` 组件,当表头包含 `label` 或 `md-table-label` 列时:
|
||||
|
||||
```markdown
|
||||
| label | name | description |
|
||||
|-------|------|-------------|
|
||||
| 1 | 战士 | 近战专家 |
|
||||
| 2 | 法师 | 奥术施法者 |
|
||||
```
|
||||
|
||||
自动转换为 `:md-table` 组件。
|
||||
|
||||
**特殊表头标识:**
|
||||
|
||||
| 表头 | 效果 |
|
||||
|------|------|
|
||||
| `label` 或 `md-table-label` | 转换为 md-table |
|
||||
| `md-roll-label` 或骰子格式(如 `1d6`) | 添加 `roll=true` |
|
||||
| `md-remix-label` | 添加 `roll=true remix=true` |
|
||||
|
||||
### 🃏 卡牌组件 (md-deck)
|
||||
|
||||
```markdown
|
||||
|
|
@ -373,19 +318,6 @@ layers="字段:起始行,起始列 - 结束列,字体大小"
|
|||
:md-token[./token.png]
|
||||
```
|
||||
|
||||
### 🎨 代币预览组件 (md-token-viewer)
|
||||
|
||||
用于 3D 预览 3MF 格式的代币模型。
|
||||
|
||||
```markdown
|
||||
:md-token-viewer[./token.3mf]
|
||||
```
|
||||
|
||||
**功能:**
|
||||
- 使用 Three.js 渲染 3D 模型
|
||||
- 支持鼠标拖拽旋转
|
||||
- 自动旋转展示
|
||||
|
||||
### 📋 命令追踪器 (md-commander)
|
||||
|
||||
支持命令历史和状态追踪。
|
||||
|
|
|
|||
17
docs/mcp.md
17
docs/mcp.md
|
|
@ -230,9 +230,9 @@ ttrpg mcp generate-card-deck \
|
|||
}
|
||||
```
|
||||
|
||||
#### `deck_preview` - 保存并预览卡牌组
|
||||
#### `deck_ensure_preview` - 确保 Markdown 预览文件存在
|
||||
|
||||
保存 CSV 对应的 Markdown 预览文件并打开浏览器预览。
|
||||
确保 CSV 对应的 Markdown 预览文件存在,如果不存在则创建。
|
||||
|
||||
| 参数 | 类型 | 必需 | 说明 |
|
||||
|------|------|------|------|
|
||||
|
|
@ -241,15 +241,6 @@ ttrpg mcp generate-card-deck \
|
|||
| `title` | string | ✗ | 标题(可选,默认从 CSV 文件名推断) |
|
||||
| `description` | string | ✗ | 描述(可选) |
|
||||
|
||||
**返回示例:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "预览文件已创建",
|
||||
"preview_url": "http://localhost:3000/#/content/cards.md"
|
||||
}
|
||||
```
|
||||
|
||||
## CSV 文件格式
|
||||
|
||||
CSV 文件使用 YAML frontmatter 定义模板和配置:
|
||||
|
|
@ -484,7 +475,7 @@ Prompt 会引导 AI 助手配置:
|
|||
|
||||
1. **设计模板**:调用 `design-card-game` Prompt 引导设计字段结构
|
||||
2. **定义模板**:调用 `deck_frontmatter_write` 创建 CSV 和 frontmatter
|
||||
3. **创建预览**:调用 `deck_preview` 创建 Markdown 预览文件
|
||||
3. **创建预览**:调用 `deck_ensure_preview` 创建 Markdown 预览文件
|
||||
4. **填充内容**:调用 `populate-deck` Prompt 引导生成卡牌内容
|
||||
5. **添加卡牌**:调用 `deck_card_crud`(action=create)添加卡牌
|
||||
6. **配置显示**:调用 `setup-deck-display` Prompt 配置显示参数
|
||||
|
|
@ -495,7 +486,7 @@ Prompt 会引导 AI 助手配置:
|
|||
1. **读取模板**:调用 `deck_frontmatter_read` 获取当前配置
|
||||
2. **读取卡牌**:调用 `deck_card_crud`(action=read)获取卡牌数据
|
||||
3. **修改卡牌**:调用 `deck_card_crud`(action=update)更新卡牌
|
||||
4. **更新预览**:调用 `deck_preview` 更新 Markdown 文件
|
||||
4. **更新预览**:调用 `deck_ensure_preview` 更新 Markdown 文件
|
||||
|
||||
### 快捷生成
|
||||
|
||||
|
|
|
|||
|
|
@ -61,13 +61,7 @@ const marked = new Marked()
|
|||
const match = rule.exec(src);
|
||||
if (match) {
|
||||
const yamlContent = match[1]?.trim() || '';
|
||||
let props: Record<string, unknown> = {};
|
||||
try {
|
||||
props = (yaml.load(yamlContent) as Record<string, unknown>) || {};
|
||||
} catch (e) {
|
||||
console.error("YAML Parse Error in code-block-yaml-tag:", e);
|
||||
props = { error: "Invalid YAML content" };
|
||||
}
|
||||
const props = yaml.load(yamlContent) as Record<string, unknown> || {};
|
||||
|
||||
// 提取 tag 名称,默认为 tag-unknown
|
||||
const tagName = (props.tag as string) || 'tag-unknown';
|
||||
|
|
|
|||
|
|
@ -87,17 +87,24 @@ class Parser {
|
|||
while (!this.at("NODE_START")) {
|
||||
const keyTok = this.take("HEADER_KEY", "Expected node header before '---'");
|
||||
const valTok = this.take("HEADER_VALUE", "Expected header value");
|
||||
|
||||
if (keyTok.text === "title") title = valTok.text.trim();
|
||||
if (keyTok.text === "tags") {
|
||||
const raw = valTok.text.trim();
|
||||
nodeTags = raw.split(/\s+/).filter(Boolean);
|
||||
}
|
||||
if (keyTok.text === "when") {
|
||||
// Each when: header adds one condition (can have multiple when: headers)
|
||||
const raw = valTok.text.trim();
|
||||
whenConditions.push(raw);
|
||||
}
|
||||
// Capture &css{ ... } styles in any header value
|
||||
const rawVal = valTok.text.trim();
|
||||
let finalVal = valTok.text;
|
||||
if (rawVal.startsWith("&css{")) {
|
||||
// Collect until closing '}' possibly spanning multiple lines before '---'
|
||||
let cssContent = rawVal.replace(/^&css\{/, "");
|
||||
let closed = cssContent.includes("}");
|
||||
if (closed) {
|
||||
cssContent = cssContent.split("}")[0];
|
||||
finalVal = rawVal.replace(/^&css\{[^}]*\}/, "").trim();
|
||||
} else {
|
||||
// Consume subsequent TEXT or HEADER_VALUE tokens until we find a '}'
|
||||
while (!this.at("NODE_START") && !this.at("EOF")) {
|
||||
|
|
@ -107,7 +114,6 @@ class Parser {
|
|||
if (t.includes("}")) {
|
||||
cssContent += (cssContent ? "\n" : "") + t.split("}")[0];
|
||||
closed = true;
|
||||
finalVal = t.split("}").slice(1).join("}").trim();
|
||||
break;
|
||||
} else {
|
||||
cssContent += (cssContent ? "\n" : "") + t;
|
||||
|
|
@ -121,18 +127,7 @@ class Parser {
|
|||
}
|
||||
nodeCss = (cssContent || "").trim();
|
||||
}
|
||||
|
||||
if (keyTok.text === "title") title = finalVal.trim();
|
||||
if (keyTok.text === "tags") {
|
||||
const raw = finalVal.trim();
|
||||
nodeTags = raw.split(/\s+/).filter(Boolean);
|
||||
}
|
||||
if (keyTok.text === "when") {
|
||||
// Each when: header adds one condition (can have multiple when: headers)
|
||||
const raw = finalVal.trim();
|
||||
whenConditions.push(raw);
|
||||
}
|
||||
headers[keyTok.text] = finalVal;
|
||||
headers[keyTok.text] = valTok.text;
|
||||
// allow empty lines
|
||||
while (this.at("EMPTY")) this.i++;
|
||||
}
|
||||
|
|
@ -161,26 +156,6 @@ class Parser {
|
|||
while (this.at("EMPTY")) this.i++;
|
||||
if (this.at(endType) || this.at("EOF")) break;
|
||||
|
||||
// Handle plain indentation seamlessly within blocks
|
||||
if (this.at("INDENT")) {
|
||||
this.take("INDENT");
|
||||
while (!this.at("DEDENT") && !this.at(endType) && !this.at("EOF")) {
|
||||
while (this.at("EMPTY")) this.i++;
|
||||
if (this.at("DEDENT") || this.at(endType) || this.at("EOF")) break;
|
||||
|
||||
if (this.at("OPTION")) {
|
||||
out.push(this.parseOptionGroup());
|
||||
continue;
|
||||
}
|
||||
out.push(this.parseStatement());
|
||||
}
|
||||
if (this.at("DEDENT")) {
|
||||
this.take("DEDENT");
|
||||
while (this.at("EMPTY")) this.i++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.at("OPTION")) {
|
||||
out.push(this.parseOptionGroup());
|
||||
continue;
|
||||
|
|
|
|||
Loading…
Reference in New Issue