# 本地编辑状态的自动保存

草稿与最近表情遵循相同规则：页面只订阅内存快照；恢复不能覆盖更晚的编辑；持久化确认后才认为保存成功。两者使用已有的持久化 owner，不引入数据库或新的 Host API。

| 内容 | 唯一持久化 owner | 页面内协调 |
| --- | --- | --- |
| 桌面文本、附件引用、延展父消息和长文卡片 | Client 原生 Profile/账号目录中的 JSON 文件 | `LocalAutosave` 按 key 合并最新内容，40ms 固定窗口、同 key 单写、总并发 32，最多 1000 个待保存 key |
| 最近使用表情 | Plugin Host 既有 SQLite `recent_emoji` 表 | 每账号共享快照与加载 promise、修订号保护、顺序写入；最多 8 个账号缓存、每账号最多 64 个排队写、每请求 2 秒超时 |

桌面通过可选 `draftPersistence: 1` 明确声明文件持久化能力。支持时，浏览器旧草稿仅用于迁移，不继续同步双写；首次确认原生保存后清除对应旧项。原生快照（包括 null 清空记录）优先，恢复期间新编辑/清空除外。未声明该能力的旧客户端与纯浏览器沿用既有浏览器恢复逻辑。

正常编辑自动保存；同一 key 保存较慢时只保留一份后续最新内容。发送只排空当前正文及长文卡片；退出、切换原生页面、重启沿用一个原生 flush 入口。保存失败保留最新内容，下一次编辑或显式 flush 重试，没有后台重试轮询。清理旧账号内存不等于删除磁盘草稿。空内容通过同一保存路径提交 null，防止旧缓存复活。

原生 broker 给接受的内容变更分配修订号；接收方忽略旧修订。磁盘长文快照标记 `restored`，恢复时释放中断进程的 sending 状态，保留提交身份；存活进程的跨窗口 sending 状态继续同步，不自动重发。

最近表情即时更新共享内存和持久化；面板每次打开读取一份展示顺序，首次加载可补齐，接受选择后不再改变当前顺序。点击、新选择及其他面板的保存结果在下次打开时展示，不会把鼠标下的按钮换成另一表情。共享保存状态与重试提示仍实时更新。面板卸载不撤销排队写入；重新打开仍向 Host 刷新以接收其他窗口的使用记录，同时合并同账号的并发加载。保存失败保留选择及重试状态，重新打开不能用磁盘旧值清掉它。默认表情仍可用，缓存失败不影响插入正文；退出最多等待最近表情 2 秒，偏好缓存不消耗完整的原生草稿退出期限，未完成的偏好写入不阻断退出。缓存只淘汰无订阅、无读写及无未确认选择的账号。

## 能力边界

- UI：草稿恢复、表情选择及面板重建、账号隔离。
- Client：能力声明、现有快照的可选修订与恢复标记；主进程仍不解释消息正文。
- Host：最近表情的已有存储、账号校验和事务不变。
- Tools/SDK：N/A，本次仅收敛内置编辑器状态与原生 UI 桥，不新增 Host 路由或公开业务能力。

## 验证

相关测试覆盖慢保存时合并后续编辑、局部 flush、显式重试、1000 key/32 并发上限、旧修订、新编辑保护、浏览器迁移、账号内存清理、长文恢复，以及最近表情的同账号共享、失败保留、队列上限和账号切换。原有 Host SQLite 重启与失败事务回归继续执行。

正常退出仍需排空最后 40ms 输入。强杀或断电不是正常退出，不承诺未传入原生进程的编辑已落盘。macOS 验证与 Windows/Linux 实机验收分别记录；PR、制品发布、客户端升级与账号验收分别报告。

同一 macOS/Node 环境、200 次每 5ms 编辑、模拟持久化确认耗时 70ms，原协调器（插件 `369efca1`）保存 26 次、同 key 最多 2 笔在途；新版保存 17 次、同 key 最多 1 笔在途。两版最后内容均为第 200 次编辑，总耗时约 1.21/1.24 秒。这是同工作负载的合并行为测量，非 UI 帧率或跨平台性能保证。
