Files
ttrpg-tools/docs/development.md
T
hyper c7f3b3425b fix(dev): proxy content registry to CLI server
Inline spark tables (role=spark-table) live in the content registry, but the
rsbuild dev proxy forwarded only /__COMPLETIONS.json and
/__CONTENT_INDEX.json. The registry fetch fell back to the SPA index.html,
so /roll resolved the inline CSV as HTML and csv-parse failed with an
invalid opening quote.

Load the registry before publishing the loaded completions state so a roll
cannot fire while inline content is still unresolvable.
2026-09-12 20:30:57 +08:00

335 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 开发和项目说明
## 快速开始
```bash
# 安装依赖
npm install
# 开发模式(同时启动 CLI 内容服务器 :3000 与 Web 前端 :5173)
npm run dev
# 也可分别启动
npm run dev:cli
npm run dev:web
# 构建(Web)
npm run build
# 预览构建结果
npm run preview
# CLI 开发模式
npm run cli:dev
# CLI 构建
npm run cli:build
# 运行测试
npm test
```
## 项目结构
```
tttk/
├── src/
│ ├── cli/ # CLI 工具源码
│ │ ├── commands/ # 命令实现 (serve, check, compile)
│ │ ├── validate/ # tttk check 校验器(扫描、规则、诊断)
│ │ ├── content-registry.ts # 内容注册表(CLI / 前端共享)
│ │ ├── content-index.ts # 目录文件递归读取
│ │ ├── index.ts # CLI 入口
│ │ └── types.ts # 类型定义
│ ├── components/ # TTRPG 组件
│ │ ├── md-dice.tsx # 骰子组件
│ │ ├── md-table.tsx # 表格组件
│ │ ├── md-deck/ # 卡牌组件
│ │ ├── md-pins.tsx # 标记组件
│ │ ├── md-commander/ # 命令追踪器
│ │ ├── md-yarn-spinner.tsx # 叙事线组件
│ │ ├── Article.tsx # 文章组件
│ │ ├── Sidebar.tsx # 侧边栏
│ │ ├── FileTree.tsx # 文件树
│ │ └── utils/ # 工具函数
│ ├── markdown/ # Markdown 解析器
│ │ ├── index.ts # marked 配置
│ │ └── mermaid.ts # mermaid 支持
│ ├── data-loader/ # 数据加载器
│ ├── plotcutter/ # 剧情切割工具
│ ├── yarn-spinner/ # 叙事线解析
│ ├── App.tsx # 主应用
│ ├── main.tsx # 入口文件
│ ├── styles.css # 样式
│ ├── index.html # HTML 模板
│ └── global.d.ts # 类型声明
├── .agents/
│ └── skills/
│ └── tttk-authoring/ # 随项目维护的 agent skill(见下文)
├── bin/
│ └── cli/ # CLI 二进制文件
├── content/ # 示例内容
├── docs/ # 文档
├── package.json
├── tsconfig.json
├── tsconfig.cli.json
├── rsbuild.config.ts
└── jest.config.js
```
## Agent skills
`tttk-authoring` skill 由本项目维护,源码位于
`.agents/skills/tttk-authoring/`。要让 agent 加载它,把全局 skill 目录
链接到仓库内的副本:
```bash
# Windows(无需管理员权限的目录联接)
cmd //c mklink /J %USERPROFILE%\.agents\skills\tttk-authoring "%CD%\.agents\skills\tttk-authoring"
# macOS / Linux
ln -s "$PWD/.agents/skills/tttk-authoring" ~/.agents/skills/tttk-authoring
```
`reference.md` 由 `known.ts` 生成,不要手改:
```bash
npm run gen:skill-reference
```
其余文件(`SKILL.md`、`yaml-tag.md`、`csv.md`、`layout.md`、`markdown.md`)
为手写内容。
## 技术栈
| 类别 | 技术 |
|------|------|
| 前端框架 | Solid.js 1.9+ |
| 构建工具 | Rsbuild |
| 样式 | Tailwind CSS 4 + @tailwindcss/typography |
| Markdown | marked + marked-directive + marked-alert + marked-gfm-heading-id |
| 图表 | mermaid |
| 测试 | Jest |
| CLI | commander + chokidar |
## 开发规范
### Solid.js 最佳实践
- **优先使用 `createEffect` 和 `createMemo`** 实现响应式
- 在 JSX 中直接引用 `props` 保持响应式
- 避免滥用 `onMount` + `createSignal`
```tsx
// ✅ 推荐
const doubled = createMemo(() => count() * 2);
createEffect(() => console.log(count()));
// ❌ 避免
onMount(() => {
const [value, setValue] = createSignal(0);
});
```
### 组件开发
- **不使用 Shadow DOM**:使用 `noShadowDOM()`
- **继承 Tailwind CSS 样式系统**
- 使用 `customElement` 注册自定义元素
```tsx
import { customElement, noShadowDOM } from "solid-element";
customElement("md-dice", { key: "" }, (props, { element }) => {
noShadowDOM();
// 组件逻辑
});
```
### 类型检查
开发完成后检查类型错误:
```bash
npx tsc --noEmit
```
### 代码风格
- 使用 TypeScript 严格模式
- 遵循 ESLint 配置(如有)
- 使用 2 空格缩进
## 添加新组件
### 1. 创建组件文件
```tsx
// src/components/md-new-component.tsx
import { customElement, noShadowDOM } from "solid-element";
import { createSignal } from "solid-js";
export interface NewComponentProps {
prop1?: string;
}
customElement("md-new-component", { prop1: "" }, (props, { element }) => {
noShadowDOM();
const [state, setState] = createSignal("");
return (
<div class="new-component">
{/* 组件内容 */}
</div>
);
});
```
### 2. 注册组件
```tsx
// src/components/index.ts
import './md-new-component';
```
### 3. 导出类型(可选)
```tsx
// src/components/index.ts
export type { NewComponentProps } from './md-new-component';
```
## 构建配置
### Rsbuild 配置
```ts
// rsbuild.config.ts
import { defineConfig } from '@rsbuild/core';
import { pluginBabel } from '@rsbuild/plugin-babel';
import { pluginSolid } from '@rsbuild/plugin-solid';
import tailwindcss from '@tailwindcss/postcss';
export default defineConfig({
plugins: [pluginBabel(), pluginSolid()],
tools: {
postcss: {
postcssOptions: {
plugins: [tailwindcss()],
},
},
},
html: {
template: './src/index.html',
},
});
```
### TypeScript 配置
- `tsconfig.json` - 主项目配置(Web)
- `tsconfig.cli.json` - CLI 工具配置(Node.js)
## 测试
```bash
# 运行所有测试
npm test
# 运行特定测试文件
npm test -- src/components/md-dice.test.ts
# 监听模式
npm test -- --watch
```
## 调试
开发环境由两个进程组成,必须使用不同端口:
- Web 前端:rsbuild 开发服务器,端口 **5173**
- CLI 内容服务器:`tttk serve`,端口 **3000**(内容 API + Journal)
rsbuild 会把 `/__CONTENT_INDEX.json`、`/__COMPLETIONS.json`、`/__CONTENT_REGISTRY.json`、`/content`、`/.ttrpg` 代理到 CLI 的
3000 端口,因此必须先启动 CLI,否则前端拿不到内容。
### Web 开发
`npm run dev` 会同时启动两个进程;也可以分开运行:
```bash
# 同时启动 CLI(3000)与 Web(5173)
npm run dev
# 或分别启动
npm run dev:cli # CLI 内容服务器,占用 3000
npm run dev:web # Web 前端,占用 5173
# 访问 http://localhost:5173
```
### CLI 调试
CLI 通过 ts-node 运行,加上 `--inspect` 与 `--enable-source-maps` 后即可在编辑器中附加断点。
如需调试启动/扫描逻辑,用 `--inspect-brk` 在用户代码执行前暂停:
```bash
node --inspect-brk --enable-source-maps --loader ts-node/esm ./src/cli/index.ts serve ./content
# 或使用脚本(不带 --inspect-brk)
npm run tttk:inspect -- serve ./content
```
然后在 Zed / VS Code 中附加到 `localhost:9229`,或打开 `chrome://inspect`。
CLI 只监听内容目录(`.md`/`.csv`/`.yarn`/`.svg`)并重建内存中的注册表,**不会**热重载自身源码;
修改 `src/cli/**` 后需要重启进程。
```bash
# 或构建后运行
npm run cli:build
node dist/cli/index.js serve ./content
```
## 发布流程
1. 更新版本号
2. 运行测试和类型检查
3. 构建 Web 和 CLI
4. 发布到 npm
```bash
npm run build
npm run cli:build
npm test
npx tsc --noEmit
npm publish
```
## 常见问题
### Windows 换行符
开发环境主要在 Windows 上,注意使用 CRLF 换行符。
### 依赖安装失败
```bash
# 清理缓存
npm cache clean --force
rm -rf node_modules package-lock.json
npm install
```
### 构建错误
```bash
# 检查类型
npx tsc --noEmit
# 重新构建
npm run build
```