Files
SyncCalendar/entry
laoyang d9db8b476a feat(待办): 子任务(RELATED-TO)+ 附件(内联 base64 ATTACH)(#43 #44)
补齐待办模块最后两项协议能力。

## #43 子任务(父子结构)

### 协议
`RELATED-TO;RELTYPE=PARENT:<父uid>`,值用**父任务 UID**(不是本地 id——
父子关系是服务器资源属性,跨设备同步时 UID 才稳定)。

### 数据层
- `LocalEvent` / `RemoteEvent` / `DisplayEvent` 加 `parentUid`
- 表加 `parent_uid` 列,`toBucket`/`fromRow`/`applyRemote` 双向映射

### UI
- 列表:子任务缩进 34vp + 标题前「∟」连线符
- 父任务副行追加子任务进度「N/M」
- 编辑弹层加「父任务」行 → 覆盖层选择(候选 = 未完成顶层任务,
  排除自己及后代,防成环)
- **父任务勾选完成 → 级联完成所有未完成子任务**(用户选定的语义)

## #44 附件(内联 base64)

### 查证 + 实测(已更新 #44 工单)
- iCalendar RFC 5545 原生支持 ATTACH,两种形式:URI / **内联 base64**
- RFC 8607 托管附件需服务器特殊支持(群晖没有)
- **实测群晖**:PUT 带 `ATTACH;...;ENCODING=BASE64;VALUE=BINARY:<b64>` → 201,
  GET 回原文原样保留(4558 字符整行、含中文文件名「报告.pdf」都不丢)

### 实现(按标准协议,直接上传同步)
- 附件 = VTODO 的 ATTACH 行(内联 base64),随 .ics 一起 PUT 同步
- 不需要自己传文件贴链接
- `buildTodo` / `rewriteTodoFields` 读写 ATTACH;`parseTodos` 解析回填
- 数据层 `attachments` 存 JSON([{name,mime,base64}]),base64 直接存库零转换
- UI:弹层加「附件」区,文件选择器选文件 → 读二进制 → base64 编码 → 随待办保存

### 限制
最多 3 个附件、单文件 ≤5MB(内联 base64 会让 .ics 膨胀,限制防爆)。

### 折行决策
**不折行**。原因:① 工程现有 SUMMARY/DESCRIPTION/LOCATION 一贯整行不折;
② 群晖实测 4558 字符整行也能存;③ RFC 折行是 SHOULD 非 MUST,一致性优先;
④ 折行若按 75 octets 硬切会把 UTF-8 中文名(「报告.pdf」)拦腰切断成乱码。

## 兼容性
`rewriteTodoFields` 新增 `touchParent` / `touchAttach` 哨兵:
编辑只改标题时传 false,服务器上**其他客户端写的 RELATED-TO / ATTACH 原样保留**;
只有用户实际编辑了父任务/附件才 touch=true 重写。

## 验证
- 编译 `BUILD SUCCESSFUL`
- 协议往返(Python 等价复刻):生成→解析 无损,附件 base64 还原正确
- 服务器端到端:PUT(201) + GET 原样返回 RELATED-TO + 中文文件名 ATTACH

## 未验证
真机 UI(设备离线)。需人工验:子任务缩进、级联完成、文件选择器上传、
附件随待办同步到另一客户端。
2026-11-04 17:19:43 +08:00
..