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

7.8 KiB
Raw Blame History

开发和项目说明

快速开始

# 安装依赖
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 目录 链接到仓库内的副本:

# 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 生成,不要手改:

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
// ✅ 推荐
const doubled = createMemo(() => count() * 2);
createEffect(() => console.log(count()));

// ❌ 避免
onMount(() => {
  const [value, setValue] = createSignal(0);
});

组件开发

  • 不使用 Shadow DOM:使用 noShadowDOM()
  • 继承 Tailwind CSS 样式系统
  • 使用 customElement 注册自定义元素
import { customElement, noShadowDOM } from "solid-element";

customElement("md-dice", { key: "" }, (props, { element }) => {
  noShadowDOM();
  // 组件逻辑
});

类型检查

开发完成后检查类型错误:

npx tsc --noEmit

代码风格

  • 使用 TypeScript 严格模式
  • 遵循 ESLint 配置(如有)
  • 使用 2 空格缩进

添加新组件

1. 创建组件文件

// 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. 注册组件

// src/components/index.ts
import './md-new-component';

3. 导出类型(可选)

// src/components/index.ts
export type { NewComponentProps } from './md-new-component';

构建配置

Rsbuild 配置

// 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)

测试

# 运行所有测试
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 会同时启动两个进程;也可以分开运行:

# 同时启动 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 在用户代码执行前暂停:

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/** 后需要重启进程。

# 或构建后运行
npm run cli:build
node dist/cli/index.js serve ./content

发布流程

  1. 更新版本号
  2. 运行测试和类型检查
  3. 构建 Web 和 CLI
  4. 发布到 npm
npm run build
npm run cli:build
npm test
npx tsc --noEmit
npm publish

常见问题

Windows 换行符

开发环境主要在 Windows 上,注意使用 CRLF 换行符。

依赖安装失败

# 清理缓存
npm cache clean --force
rm -rf node_modules package-lock.json
npm install

构建错误

# 检查类型
npx tsc --noEmit

# 重新构建
npm run build