Files
goodbuddy/docs/features/desktop-pet/logic-design.md
T
mesalogo 374968d50b docs: add desktop pet feature design
Adds the full documentation set for the planned desktop pet feature under
docs/features/desktop-pet: README, PRD, user stories, logic design, UI
design, and technical design. The pet is scoped as a lightweight always-on
entry point for quick drafting, drag-and-drop staging, and task status at a
glance; it does not introduce a second Ask/Execute channel, task execution
path, or independent task-state store. Design only, no implementation yet.
2026-09-03 23:22:33 +08:00

9.1 KiB

桌面宠物功能逻辑设计

文档信息

项目 内容
状态 设计中
版本 0.1
日期 2026-09-03
关联 PRD 桌面宠物 PRD
User Stories 桌面宠物 User Stories
任务模型 Task 与 Job 统一领域模型

本文负责桌面宠物自身的状态、不变量和决策规则,不重新定义 Task、Conversation、Ask/ Execute 或附件的既有语义;这些语义以各自权威文档为准。

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 总开关与可见性

disabled -> enabled_visible
enabled_visible <-> enabled_hidden        (暂时隐藏 / 重新显示)
enabled_visible|enabled_hidden -> disabled (关闭总开关)

disabled 是唯一没有宠物窗口存在的状态;enabled_hidden 窗口不可见但设置全部保留。

2.2 快速起草弹出框

closed -> drafting -> submitted | discarded
drafting -> closed (Escape / 点击外部 / 关闭按钮)

submitted 是终态转换动作,不是持续状态:提交后弹出框立即回到 closed,草稿数据已经 转交给主窗口。

2.3 拖放交互

idle -> drag_over -> dropped_accepted | dropped_rejected | drag_cancelled

drag_over 只影响视觉反馈,不产生任何持久数据;只有 dropped_accepted 才产生暂存 数据。dropped_rejected 用于文件夹或全部文件超限的场景,不产生暂存也不打开主窗口。

2.4 状态镜面

unavailable | idle | completed_recent | running | waiting_approval | failed_recent

取值由当前活跃与最近终态 Task 集合计算得出,属于派生状态,不单独持久化。

2.5 提示气泡

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 以上条件都不满足

“最近失败”“最近完成”窗口的具体时长和“已查看”判定属于技术选择,由 技术设计基于既有 Task 已读/未读语义确定,不在本文固定具体 数值。

4. 提示归并决策表

事件 主窗口状态 安静模式 结果
任务完成 不可见或未聚焦 关闭 显示宠物气泡;不触发原生通知
任务完成 不可见或未聚焦 开启 不显示气泡;不触发原生通知;仅更新状态镜面
任务完成 可见并聚焦 任意 不显示气泡;反馈通过主窗口既有机制呈现
任务失败 不可见或未聚焦 关闭 显示宠物气泡并保持可见直到用户查看或关闭
任务失败 不可见或未聚焦 开启 不显示气泡;仅更新状态镜面为 failed_recent
任务失败 可见并聚焦 任意 不显示气泡;反馈通过主窗口既有机制呈现

“主窗口可见并聚焦”与现有 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. 逻辑完整性评估

本设计覆盖了启用/关闭、暂时隐藏、快速起草、拖放(含超限和文件夹)、状态镜面优先级、 提示与原生通知的归并、位置越界恢复、以及主窗口不可用时的失败路径。

未在本文固定、留给技术设计选择的实现细节:

  • “最近完成/最近失败”窗口的具体时长和“已查看”判定的确切事件来源。
  • 贴边吸附阈值和默认回退位置的具体像素值。
  • 状态查询的具体轮询或事件驱动机制。

这些是技术选择而非未解决的产品决策:本文的优先级和决策表已经足以在任何取值下保持 行为一致。