Files
huajishe-tts/docs/dev-log/02-ui-primitives-api-design.md
Claude 24822d0d6a docs(dev-log): add chronological development log
Per user request: split the development process into per-topic docs under
docs/dev-log/. Each entry follows the same structure:
  What was asked → Problems → How solved

Files:
- README.md            index
- 01-initial-scope-and-design.md
- 02-ui-primitives-api-design.md
- 03-monorepo-scaffold.md
- 04-ui-primitive-impl.md
- 05-drag-and-drop.md
- 06-three-games.md
- 07-bugfixes-round-1.md
- lessons-learned.md   all gotchas consolidated
- decisions.md         locked decisions with dates

Total: 1071 lines across 10 files.
2026-08-17 16:49:09 +08:00

82 lines
2.7 KiB
Markdown
Raw Permalink 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.
# 02 · UI 原语 API 设计
## 做什么
在动手写代码之前,先把 UI 组件的 props、game.ui 字段结构、数据流定义清楚,写成 `docs/ui-primitives.md` 供实现参考。
## 遇到什么问题
1. **接口界定**:组件要通用(支持任意 boardgame.io 游戏),又不能过于抽象失去表现力。
2. **game.ui 字段怎么设计**:是松散的"约定"还是强类型的 schema
3. **拖拽 API**TTS 用 Lua 控制每个对象的交互。JS 端怎么抽象?
4. **玩家视角**boardgame.io master 会按 playerID 过滤状态UI 怎么配合?
## 怎么解决
### 5 个设计原则
1. **声明式**:业务代码只描述"是什么",不描述"怎么画"
2. **数据驱动**:所有可见状态都来自 `G`boardgame.io 的 state
3. **服务器权威**:所有 game-affecting 操作都通过 `onMove` 触发 reducer
4. **玩家视角**UI 接收的 `G` 已经是 boardgame.io 过滤后的版本
5. **可扩展**:通过 `game.ui.overrides` 注入自定义组件
### 8 个组件
`<Board>` `<Zone>` `<Hand>` `<Deck>` `<Card>` `<Token>` `<ActionButton>` `<Dice>`
### 6 个 zone type
`hand` / `pile` / `discard` / `grid` / `area` / `free`
### 关键 schema
```ts
interface UISchema {
background?: string;
zones: ZoneDef[];
cards?: CardTemplate;
tokens?: TokenDef[];
actions?: ActionDef[];
overrides?: UIOverrides;
}
interface ZoneDef {
id: string;
type: 'hand' | 'pile' | 'discard' | 'grid' | 'area' | 'free';
position: { x: number; y: number };
size?: { w: number; h: number };
layout?: { fan?: number; spacing?: number; maxWidth?: number };
faceDown?: boolean;
owner?: string | null; // 'self' | 'P0' | null(公开)
collection?: string; // G 中持有 ID 数组的字段名
entityField?: string; // G 中持有 ID → 实体映射的字段名
dropMove?: string; // 接收 drop 时调用的 move
dropArgs?: (cardIds, fromZoneId, toZoneId) => any[];
accepts?: string | ((card, ctx) => boolean); // 接收规则
}
```
### 6 个待决项(已锁定 ✅)
| 待决项 | 决定 |
| --- | --- |
| zone 坐标系统 | 固定像素 + Board 视口缩放 |
| 动画策略 | CSS transition 默认 + 游戏可覆盖 |
| 可访问性 | v1 仅鼠标 |
| 触屏/移动端 | v1 仅桌面 |
| 多选 UI | v1 仅 Shift+click |
| 牌堆数量显示 | 角标显示数量 |
资产格式SVG 内嵌字符串优先fallback DataURL/URL
## 成果
- `docs/ui-primitives.md`15 KB511 行)
- 6 个待决项的设计默认值锁定,避免实现时反复讨论
## 关联
- [04-ui-primitive-impl.md](./04-ui-primitive-impl.md) — 按这份设计实现
- [03-monorepo-scaffold.md](./03-monorepo-scaffold.md) — 同步开始搭骨架