# 桌面宠物功能逻辑设计 ## 文档信息 | 项目 | 内容 | | --- | --- | | 状态 | 设计中 | | 版本 | 0.1 | | 日期 | 2026-09-03 | | 关联 PRD | [桌面宠物 PRD](./prd.md) | | User Stories | [桌面宠物 User Stories](./user-stories.md) | | 任务模型 | [Task 与 Job 统一领域模型](../task-and-job/task-and-job-model.md) | 本文负责桌面宠物自身的状态、不变量和决策规则,不重新定义 Task、Conversation、工具执行 或附件的既有语义;这些语义以各自权威文档为准。 ## 1. 核心不变量 ### INV-1 宠物不是第二条执行通道 快速起草和拖放暂存只产生主窗口会话输入区的草稿数据(文本、附件引用),不产生消息、 不产生 Task、不触发模型调用。发送和 Runtime 选择继续完全由主窗口既有流程 拥有。 ### INV-2 状态镜面是既有状态的投影 宠物状态镜面的取值只能从当前 Task 集合的既有状态推导,不允许引入宠物专属的任务状态 字段或独立于 Task Center 的状态存储。宠物状态与 Task Center 展示的状态出现分歧即视为 缺陷。 ### INV-3 总开关是唯一的存在性开关 “启用桌面宠物”关闭时,宠物窗口、气泡和托盘条目文案必须整体消失;除该开关外,任何 子设置(外观、位置、安静模式)的变化都不得单独创建或销毁宠物窗口。 ### INV-4 暂时隐藏不影响启用状态 “暂时隐藏”只影响窗口可见性,不修改总开关持久化值,也不清除位置、外观或安静模式设置。 ### INV-5 提示与原生通知互斥 同一个任务终态事件(完成或失败)只能产生宠物气泡或现有原生系统通知中的一种,不产生 两种同时可见的独立提示。 ### INV-6 位置始终落在可用显示器内 宠物窗口的持久化位置在应用于当前显示器拓扑之前必须被校验;不能得出宠物窗口完全位于 任何当前可见显示器之外的最终坐标。 ### INV-7 拖放与起草共享暂存规则 拖放产生的文本和文件必须满足与快速起草附件相同的类型、大小、数量限制;两者不允许存在 两套互不一致的限制值。 ## 2. 状态维度 ### 2.1 总开关与可见性 ```text disabled -> enabled_visible enabled_visible <-> enabled_hidden (暂时隐藏 / 重新显示) enabled_visible|enabled_hidden -> disabled (关闭总开关) ``` `disabled` 是唯一没有宠物窗口存在的状态;`enabled_hidden` 窗口不可见但设置全部保留。 ### 2.2 快速起草弹出框 ```text closed -> drafting -> submitted | discarded drafting -> closed (Escape / 点击外部 / 关闭按钮) ``` `submitted` 是终态转换动作,不是持续状态:提交后弹出框立即回到 `closed`,草稿数据已经 转交给主窗口。 ### 2.3 拖放交互 ```text idle -> drag_over -> dropped_accepted | dropped_rejected | drag_cancelled ``` `drag_over` 只影响视觉反馈,不产生任何持久数据;只有 `dropped_accepted` 才产生暂存 数据。`dropped_rejected` 用于文件夹或全部文件超限的场景,不产生暂存也不打开主窗口。 ### 2.4 状态镜面 ```text unavailable | idle | completed_recent | running | waiting_approval | failed_recent ``` 取值由当前活跃与最近终态 Task 集合计算得出,属于派生状态,不单独持久化。 ### 2.5 提示气泡 ```text none -> pending -> shown -> dismissed ``` `pending` 表示事件已发生但由于主窗口可见或安静模式而被抑制,不进入 `shown`。 ## 3. 状态镜面优先级 宠物在任意时刻只呈现一个状态镜面取值。存在多个活跃 Task 时按以下优先级取最高项: | 优先级 | 状态 | 条件 | | --- | --- | --- | | 1 | `unavailable` | 用于确认状态的 Runtime 或网络当前不可达 | | 2 | `waiting_approval` | 至少一个 Task 处于等待批准 | | 3 | `failed_recent` | 至少一个 Task 在最近的“最近失败”窗口内失败,且用户尚未查看 | | 4 | `running` | 至少一个 Task 处于执行中 | | 5 | `completed_recent` | 至少一个 Task 在最近的“最近完成”窗口内完成,且用户尚未查看 | | 6 | `idle` | 以上条件都不满足 | “最近失败”“最近完成”窗口的具体时长和“已查看”判定属于技术选择,由 [技术设计](./technical-design.md)基于既有 Task 已读/未读语义确定,不在本文固定具体 数值。 ## 4. 提示归并决策表 | 事件 | 主窗口状态 | 安静模式 | 结果 | | --- | --- | --- | --- | | 任务完成 | 不可见或未聚焦 | 关闭 | 显示宠物气泡;不触发原生通知 | | 任务完成 | 不可见或未聚焦 | 开启 | 不显示气泡;不触发原生通知;仅更新状态镜面 | | 任务完成 | 可见并聚焦 | 任意 | 不显示气泡;反馈通过主窗口既有机制呈现 | | 任务失败 | 不可见或未聚焦 | 关闭 | 显示宠物气泡并保持可见直到用户查看或关闭 | | 任务失败 | 不可见或未聚焦 | 开启 | 不显示气泡;仅更新状态镜面为 `failed_recent` | | 任务失败 | 可见并聚焦 | 任意 | 不显示气泡;反馈通过主窗口既有机制呈现 | “主窗口可见并聚焦”与现有 [`desktop-notification.ts`](../../../src/main/desktop-notification.ts) 判定原生通知是否触发所使用的条件保持一致,避免宠物和原生通知各自使用不同的可见性 判定导致同一事件在两处呈现或同时缺失。 ## 5. 快速起草与拖放的目标会话决策表 | 场景 | 目标会话 | | --- | --- | | 主窗口存在当前活跃会话,用户未在弹出框内更改目标 | 该活跃会话 | | 用户在弹出框内选择“新建对话” | 提交时新建并激活一个会话 | | 主窗口不存在任何会话 | 提交时新建并激活一个会话 | | 目标会话在提交前被用户在主窗口中删除 | 视为“不存在任何会话”,回退到新建会话 | 拖放没有目标切换界面,始终采用与快速起草相同的默认目标会话规则。 ## 6. 附件与文本限制 快速起草和拖放暂存复用现有聊天附件规则,不定义新的限制数值: - 单条草稿的附件数量上限、单个附件大小上限、支持的文本与图片类型,与主窗口聊天附件 的现有限制一致。 - 拖放同时包含多个文件时,逐个按上述规则校验;不合规文件被拒绝,不影响合规文件继续 暂存。 - 拖放文件夹被整体拒绝,不做递归展开或部分接受。 ## 7. 位置与显示器校验 1. 加载持久化位置后,检查该坐标是否位于当前任一可见显示器的可用区域内。 2. 位于范围内:直接使用。 3. 不在范围内(例如记忆的显示器已断开):改用主显示器上一个默认可见位置,并在下一次 用户拖动宠物后覆盖持久化位置。 4. 开启“贴边”时,拖动释放位置与最近边缘的距离小于吸附阈值即吸附到该边缘;阈值和默认 位置的具体数值属于技术选择。 ## 8. 失败与恢复 | 场景 | 行为 | | --- | --- | | 主窗口进程尚未创建或已被销毁 | 提交快速起草或拖放时先创建/恢复主窗口,再写入草稿 | | 写入草稿时主窗口窗口对象不可用 | 提交失败,弹出框保留草稿内容供用户重试,不静默丢弃 | | 状态查询所需的 Runtime/网络不可达 | 状态镜面显示 `unavailable`,不回退显示 `idle` | | 显示器拓扑变化导致位置越界 | 按第 7 节回退到默认可见位置,不隐藏窗口 | | 用户在气泡展示期间关闭总开关 | 气泡与宠物窗口一并立即消失 | | 应用启动时总开关为开启但窗口创建失败 | 记录失败原因,不阻塞主窗口正常启动,托盘菜单提供重试入口 | ## 9. 需求追踪 | 需求 | 主要逻辑 | | --- | --- | | `FR-1` | `INV-3`、总开关与可见性状态 | | `FR-2` | 位置与显示器校验、`INV-6` | | `FR-3` | `INV-1`、快速起草状态、目标会话决策表 | | `FR-4` | `INV-1`、`INV-7`、拖放交互状态、附件与文本限制 | | `FR-5` | `INV-2`、状态镜面优先级 | | `FR-6` | `INV-5`、提示归并决策表 | | `FR-7` | 提示归并决策表中安静模式列 | | `FR-8` | 位置与显示器校验之外的技术选择,逻辑仅约束持久化立即生效 | | `FR-9` | `INV-4`、总开关与可见性状态 | | `FR-10` | 快速起草状态中的键盘转换,具体按键映射见 UI 设计 | | `FR-11` | `INV-1`、`INV-2`、`INV-7` | ## 10. 逻辑完整性评估 本设计覆盖了启用/关闭、暂时隐藏、快速起草、拖放(含超限和文件夹)、状态镜面优先级、 提示与原生通知的归并、位置越界恢复、以及主窗口不可用时的失败路径。 未在本文固定、留给技术设计选择的实现细节: - “最近完成/最近失败”窗口的具体时长和“已查看”判定的确切事件来源。 - 贴边吸附阈值和默认回退位置的具体像素值。 - 状态查询的具体轮询或事件驱动机制。 这些是技术选择而非未解决的产品决策:本文的优先级和决策表已经足以在任何取值下保持 行为一致。