Commit b96c48c
feat(cat-idle): add three-tier return cat idle flow (#1581)
* feat(idle): add auto-goodbye controller with task guard
Phase 1:新增首页自动 goodbye 控制器
- 新增 app-auto-goodbye.js,实现首页闲置检测与自动 goodbye
- 控制器只在首页启动,不注入 /chat;启动前等待 storage location barrier
- 到点后仅派发现有 live2d-goodbye-click,不复制 goodbye 业务逻辑
- 阻断条件收紧为三类:真实对话 / 持续任务(queued+running)/ 真实执行操作
- 教程接管和 yui-taking-over 期间不触发
- app-interpage.js 补充 /chat 真实交互回传首页,刷新闲置基线
- 开发阈值暂为 5s / 10s / 15s,最终验收后恢复 20min / 30min / 40min
- 补充 Phase 1 harness 测试
* feat(idle): replace return-ball visual with CAT1/CAT2/CAT3 tiers
Phase 2+3:return-ball 显示层替换为 CAT1/CAT2/CAT3 占位表现
- avatar-ui-buttons.js 新增 visualTier 桥接,监听 neko:auto-goodbye:state-change
切换 cat-idle-cat1/2/3.png,保留原有 DOM ID、事件、拖拽协议
- CSS 为 CAT1/CAT2/CAT3 提供逐级更小的尺寸和更淡的阴影
- 占位资源暂为白底文字图,正式资源后续替换
- 补充 Phase 2/3 静态契约测试,锁住视觉已接入、旧 rest_off/rest_on 已退出
- Phase 3 harness 断言 5s→cat1→10s→cat2→15s→cat3,不重复派发 goodbye
* feat(idle): dock minimized chat ball next to CAT2/CAT3 return-ball
Phase 4:聊天球与 CAT2/CAT3 表现层停靠
- idle-dock 作为完全独立的编排模块,不修改 setMinimized() 函数体
- setMinimized 保持原始签名,内部零 idle-dock 分支
- CAT2/CAT3 激活时:如已最小化直接停靠,否则先调用原始 setMinimized(true)
再通过 MutationObserver 等动画完成后停靠
- return 时恢复到停靠前保存的位置
- 用户直接展开停靠球时,toggleMinimized 在展开前恢复位置,避免错位
- /chat 独立窗口通过 isElectronChatWindow() 全程排除
- 不新增对外公开桥接
- CSS .is-idle-docked 提供停靠态轻量 glow 和位置过渡
- 补充 Phase 4 静态契约测试
* test(idle): add regression boundary tests and asset version tracking
Phase 5:回归与边界验证
- 新增 test_phase5_regression_boundary.py(10 条断言)锁住核心约束:
手动 goodbye 未改、auto-goodbye 只派发事件、return 走 handleReturnClick、
start_session 未误触、queued/running 阻断、deferred reminder 无豁免、
旧 chat-container 不引用、教程/拖拽 guard 存在
- 新增 test_auto_goodbye_goodbye_return_contract.py 锁住 goodbye/return 语义:
auto-goodbye 复用现有 goodbye 链路,return 不走 start_session
- pages_router.py 补充所有新增资源的版本跟踪
- .gitignore 排除 .agent/notes/
* fix(idle): prevent duplicate goodbye dispatch and idle-dock expand fight
Code review 修复两个实际 bug:
1. tryAutoGoodbye() 补 autoGoodbyeTriggered 前置守卫:
如果 goodbye manager 没设上 _goodbyeClicked,
旧代码每 500ms tick 会重复派发 live2d-goodbye-click。
现在在派发前检查 autoGoodbyeTriggered 阻止重复。
2. toggleMinimized() 补 idleDockTier 清理:
用户直接展开停靠球时,旧代码只清 idleDockActive,
没清 idleDockTier。展开动画期间如果 tier 事件到达,
isIdleDockTierActive() 仍为 true,会重新进入 idle-dock
和展开动画打架。现在同步清理 tier 和所有 observer。
* feat(idle): replace placeholders with GIF-based tier images and strip all CSS effects
将占位图片替换为基于 sleep_cat_wait3.gif 的新图片,并为各层级添加中文描述文字:
- CAT1 非点击态"摸摸我~",点击态"我在这!"
- CAT2 非点击态"好无聊...",点击态"还在吗?"
- CAT3 非点击态原始无文字 GIF,点击态"别吵..."
- 删除旧的 SVG 占位文件和 cat-idle-cat3.png
彻底清除所有按钮视觉效果:
- CSS 移除所有 border、background、box-shadow、backdrop-filter、::before 伪元素
- 移除 vrm-ui-buttons.js 独立注入的呼吸灯动画
- 移除 common-ui-hud.js 中 #live2d-btn-return 呼吸灯样式
- 悬停动效改为图片切换(非点击态↔点击态),不再使用 CSS 变换
- 删除所有 CAT3 特判代码
* revert: remove .agent/ from .gitignore (undo from 251b48a)
取消 251b48a 中对 .gitignore 的修改,不再排除 .agent 目录。
* fix(idle): normalize cat return button size
统一 CAT 空闲回来入口的展示尺寸,移除按 cat2/cat3 分档缩放的覆盖规则。\n\n避免不同空闲档位切换时按钮尺寸跳变,并保持移动端尺寸与当前设计口径一致。
* feat(ui): use gif idle return cat assets
* fix(ui): smooth cat idle state transitions
统一猫 idle 资源版本跟踪与测试口径为 GIF。\n\n为 CAT1/CAT2/CAT3 阶段切换增加交叉淡入淡出、轻微位移缩放和 reduced-motion 兼容,避免直接换图造成生硬跳变。\n\n补充 return 猫球首次出现的淡入上浮效果,并用静态测试锁定平滑切换契约。\n\n验证:node --check static/avatar-ui-buttons.js && node --check static/app-ui.js;uv run python -m pytest tests/unit/test_avatar_return_button_cat1_static.py tests/unit/test_avatar_return_button_idle_tiers_static.py tests/unit/test_auto_goodbye_goodbye_return_contract.py tests/unit/test_app_auto_goodbye_phase1.py tests/unit/test_react_chat_idle_dock_static.py -q。
* fix(ui): preserve cat idle interactions
修复 CAT2/CAT3 下拖拽和松手会刷新 idle 基线、导致状态退回 CAT1 的问题。\n\n优化 return 猫形象 hover 反馈:根据对应 click GIF 的帧延迟解析一轮播放时长,鼠标离开后等播放完成再恢复默认态;同一 GIF 反复进入不重复设置 src,避免一直从头播放。\n\n同步更新 cat idle 设计文档,记录当前 goodbye 后交互不重置 tier、hover GIF 播放策略和相关测试覆盖。\n\n验证:node --check static/avatar-ui-buttons.js;node --check static/app-auto-goodbye.js;uv run python -m pytest tests/unit/test_app_auto_goodbye_phase1.py tests/unit/test_auto_goodbye_goodbye_return_contract.py tests/unit/test_avatar_return_button_idle_tiers_static.py tests/unit/test_avatar_return_button_cat1_static.py tests/unit/test_react_chat_idle_dock_static.py -q。
* feat: bridge cat idle states to desktop chat
更新猫娘空闲态链路:goodbye 后 CAT1/CAT2/CAT3 可在 suppression 存在时继续推进。\n\n补齐 return-ball 桌面状态广播、Electron 聊天窗折叠停靠、拖拽跟随和退出竞态保护。\n\n同步更新设计文档与回归测试,明确 GIF 资源、桌面桥接和边界约束。
* feat: 优化猫猫 idle 阶段子动作
- 增加 CAT1 走向聊天框、停下伸懒腰和 hover 子动作资源
- 同步桌面 return-ball 拖拽坐标与聊天窗跟随逻辑
- 将联调 idle 阈值调整为 20s/25s/30s,并更新测试与设计文档
* fix: bridge desktop chat target for cat idle walk
- 让 Electron /chat 折叠态发布聊天小球 screen rect
- 通过 app-interpage 转发给 pet 页,CAT1 寻路时换算为当前窗口坐标
- 补充静态合同测试和设计文档,避免桌面端只停留在默认 CAT1
* fix: keep desktop cat walk target fresh during hover
- 为 Electron 聊天小球 minimized screen rect 增加低频心跳刷新
- 避免 CAT1 走路暂停播放点击态后,因目标 rect 过期回到默认猫
- 补充 idle-dock 静态合同测试
* fix: preserve cat art on desktop drag start
- 桌面 return-ball drag start 只取消 CAT1 自动移动,不再重置当前 GIF
- 保留 observer 和当前视觉,避免按下瞬间闪回 CAT1 默认猫
- 补充 CAT1 拖拽合同测试
* feat(idle): add drag action gifs for return cat
为 CAT1/CAT2/CAT3 return 猫接入独立拖拽临时态,拖拽越过阈值后切换到对应 move GIF,松手后恢复当前真实 tier。\n\n补齐普通网页端 return-ball 拖拽初始化,桌面多窗口仍沿用 app-ui 拖拽桥接,避免双重监听;同时加入可开关诊断日志、静态资源版本跟踪、设计文档和契约测试。\n\n验证:node --check static/avatar-ui-buttons.js;node --check static/app-ui.js;uv run python -m pytest tests/unit/test_avatar_return_button_idle_tiers_static.py tests/unit/test_react_chat_idle_dock_static.py tests/unit/test_app_auto_goodbye_phase1.py tests/unit/test_auto_goodbye_goodbye_return_contract.py tests/unit/test_phase5_regression_boundary.py -q;Playwright 最小 DOM 模拟拖拽切换与恢复。
* feat(idle): vary cat walk timing and gif speed
为 CAT1 走向最小化聊天框增加随机起步等待:大多数情况下立刻或短等待,少量等待几十秒到几分钟;已经在走路时更新目标不重新等待。\n\n走路中如果猫与聊天球距离被拉大,移动倍率最高提升到 1.5x,并通过运行时 patch GIF 帧 delay 生成加速 Blob URL,让走路 GIF 本身随倍率加速。\n\n同步更新猫 idle 设计文档和静态契约测试。\n\n验证:node --check static/avatar-ui-buttons.js;uv run python -m pytest tests/unit/test_avatar_return_button_idle_tiers_static.py tests/unit/test_react_chat_idle_dock_static.py tests/unit/test_app_auto_goodbye_phase1.py tests/unit/test_auto_goodbye_goodbye_return_contract.py tests/unit/test_phase5_regression_boundary.py -q;Playwright 最小 DOM 模拟长等待、即时起步、距离拉大后的 1.5x Blob 加速。
* fix(cat-idle): resume interrupted walk after drag
修复 CAT1 猫在走向最小化聊天框时被拖拽打断后,松手可能停回默认态的问题。
记录拖拽前是否处于 walking 子状态,只在该场景下跳过重新抽取 start delay;真正重新开始走路仍保留原有随机等待设计,并补充静态契约测试。
* fix(chat): stabilize desktop idle cat dragging
修复桌面端三态猫拖拽视口恢复时的闪烁问题:在恢复窗口前先提交隐藏帧,避免拖拽态 GIF 出现在左上角。\n\n同时保留 128px 拖拽视口以避免 GIF 被裁切,并合并 return-ball 拖拽广播、缓存 Electron idle-dock bounds/workArea,减少最小化聊天球跟随卡顿。\n\n补充静态回归,覆盖桌面拖拽视口尺寸、隐藏帧刷新顺序和 Electron idle-dock 缓存契约。
* fix(chat): end native cat drag synchronously
修复上一版在 return-ball 松手时等待 rAF 才调用 nekoPetDrag.stop 的生命周期错误。\n\n现在松手后只同步隐藏并强制样式落地,然后立即停止主进程拖拽,避免桌面窗口停留在 128px 小窗并继续黏在鼠标上。\n\n更新静态测试,锁定 stop 不能被动画帧等待阻塞。
* fix(cat-idle): align desktop return cat drag lifecycle
按三态猫设计收口拖拽生命周期:手动拖拽取消当前 CAT1 自动走路,松手后恢复真实 tier 并走正常距离重判与随机 start-delay,不再通过 resumeWalkAfterDrag 跳过等待。\n\n真正开始 walking 时同步推进第一步,避免 walking GIF 在首帧原地播放;同时将桌面 return-ball 拖拽视口调整为 160px,给切换图和拖拽图留出渲染余量。\n\n验证:node --check static/avatar-ui-buttons.js;node --check static/app-ui.js;node --check static/app-react-chat-window.js;uv run python -m pytest tests/unit/test_avatar_return_button_idle_tiers_static.py tests/unit/test_react_chat_idle_dock_static.py tests/unit/test_app_auto_goodbye_phase1.py tests/unit/test_auto_goodbye_goodbye_return_contract.py tests/unit/test_phase5_regression_boundary.py -q;git diff --check。
* fix(cat-idle): stabilize return ball drag lifecycle
修复三态猫桌面 return-ball 拖拽收尾:小位移不再触发 CAT1 重新寻路,拖拽态在 renderer 完成 viewport 和 DOM 清理后再恢复显示。\n\n同时收口非桌面 return 按钮重复拖拽绑定,补齐 VRM 拖拽生命周期事件,并提交更新后的拖拽态 GIF 资产。
* fix(cat): stabilize idle drag demotion
按三态猫设计更新拖拽回退规则:CAT3 前两次拖拽保持,第三次回退到 CAT2,CAT2 拖拽回退到 CAT1,CAT1 保持原时间推进。补充桌面端 idle-dock 拖拽降级后的保位退出逻辑,避免 viewport-resize 抢先结束生命周期,并在 collapsed bounds 提交 busy 时进行受 generation 保护的短重试和兜底。同步功能/参考文档、后续交互扩展文档和相关回归测试。
* fix(cat): stabilize idle cat pair movement
修复 CAT1 小移动在真实点击返回后被遗留 hover 状态卡住的问题。\n\n同步三态自然推进发布阈值为 10min / 15min / 18min,并将 CAT1 小移动随机间隔调整为 5s-5min。\n\n更新猫 idle 三态设计文档与静态契约测试,明确 pair-move 生命周期、hover 收尾和当前时间参数。
* docs(cat): consolidate idle state design docs
清理 cat idle feature 文档中的文档连接。
删除已不再保留的 interaction expansion 和 reference 设计文档,让远端只保留当前收敛后的功能说明入口。
* fix(cat): harden idle cat desktop sync
完善三态猫桌面端 CAT1 小移动同步,补齐 BroadcastChannel 内的 pair-move bounds 转发与 Electron 折叠聊天窗消费,并防止 CAT1 同步覆盖 CAT2/CAT3 idle-dock。\n\n修复 auto-goodbye socket 断开后的 primed 状态、pending idle-dock 退出清理、return-ball viewport 等待兜底、VRM 拖拽结束判定顺序和 CAT1 pair-move 定位锚点问题。\n\n补充静态回归测试,锁定脚本加载顺序、资产版本追踪、拖拽生命周期和跨窗口同步边界。
* fix(cat): align drag demotion timing
修正 CAT2 拖拽降级到 CAT1 后的视觉阶段计时,从 CAT1 阈值开始重新等待完整 CAT1 显示窗口,避免错误保持 15 分钟。\n\n收紧 return button CSS 静态测试 helper,要求精确匹配顶层 .neko-idle-return-btn 基础规则,并在未找到规则时显式失败。\n\n验证:pytest tests/unit/test_app_auto_goodbye_phase1.py tests/unit/test_auto_goodbye_goodbye_return_contract.py tests/unit/test_react_chat_idle_dock_static.py;pytest tests/unit/test_avatar_return_button_idle_tiers_static.py tests/unit/test_app_auto_goodbye_phase1.py;node --check static/app-auto-goodbye.js。
* fix(cat): align idle cat dock movement
- 支持 CAT1 settled 后在聊天框展开时只移动猫、聊天框最小化时猫和小球一起随机小移动。
- 收口桌面 compact/full 进入 CAT2/CAT3 的折叠、恢复和拖拽降级保留位置链路。
- 更新三态猫设计文档与静态回归测试,锁住展开/最小化/桌面 compact 边界。
* test(cat): clarify idle dock contract failures
- 为 idle-dock 静态测试的 _between 辅助函数补充明确的分隔符缺失错误。
- 在比较 renderWindow 与 setMinimized 顺序前先校验 token 存在,避免失败时只抛难读的 ValueError。
* feat(neko-idle): add cat idle voice cues
为 CAT1/CAT2/CAT3 空闲猫形态接入轻量音效。
CAT1 非拖拽状态按三分钟窗口随机播放轻声音效,CAT1 拖拽态播放专属音效并在拖拽结束后淡出。CAT2/CAT3 睡觉形态按五分钟窗口随机播放对应睡觉音效,所有音量均保持较轻。
同步静态资源版本跟踪和三态猫静态契约测试。
* Fix idle dock static restore assertion
* Allow auto goodbye on character routes
* Avoid duplicate Live2D return drag wiring
---------
Co-authored-by: Krabbypattyl <2531551606@qq.com>
Co-authored-by: Hongzhi Wen <cartabio.coder1@gmail.com>1 parent 7378ec9 commit b96c48c
38 files changed
Lines changed: 6740 additions & 224 deletions
File tree
- docs/design
- main_routers
- plugin/plugins/galgame_plugin
- static
- assets/neko-idle
- css
- templates
- tests/unit
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
| 29 | + | |
29 | 30 | | |
30 | 31 | | |
31 | 32 | | |
| |||
36 | 37 | | |
37 | 38 | | |
38 | 39 | | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
39 | 64 | | |
40 | 65 | | |
41 | 66 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
671 | 671 | | |
672 | 672 | | |
673 | 673 | | |
674 | | - | |
0 commit comments