Skip to content

fix(cat): polish cat/model transition behavio#1666

Merged
wehos merged 7 commits into
Project-N-E-K-O:mainfrom
yiyiyiyiGKY:cute_cat
Jun 6, 2026
Merged

fix(cat): polish cat/model transition behavio#1666
wehos merged 7 commits into
Project-N-E-K-O:mainfrom
yiyiyiyiGKY:cute_cat

Conversation

@yiyiyiyiGKY
Copy link
Copy Markdown
Contributor

@yiyiyiyiGKY yiyiyiyiGKY commented Jun 6, 2026

变更内容

  • 优化猫形态与人形态切换过渡动画节奏,并更新 cat_model_change.gif 资源。
  • 修复 CAT1 蹲在网页 compact 对话框上方时,叫声没有切换到点击态的问题。
  • 修复网页端 compact 对话框拖动时,蹲在对话框上方的猫可能消失的问题。
  • 修复猫/模型靠近屏幕边界时,猫形态与人形态切换遮罩偏离当前位置的问题;遮罩现在允许部分出屏,确保中心始终对准猫或模型。

验证

  • node --check static/avatar-ui-buttons.js
  • node --check static/app-ui.js
  • .venv/bin/pytest tests/unit/test_avatar_return_button_idle_tiers_static.py -q
  • git diff --check

Summary by CodeRabbit

  • Bug Fixes

    • 稳定角色过渡矩形定位:使用四舍五入坐标计算,减少位置抖动/偏移并改进渲染稳定性
    • 优化紧凑镜像失活隐藏时序,减少视觉残留
  • New Features

    • 增强猫1环境音与返回按钮交互:基于播放状态触发悬停反馈并同步镜像激活与资源回退
    • 阻止来自特定平台本地事件误触发紧凑镜像显示/隐藏
  • Tests

    • 新增并强化测试,覆盖定位、声音反应、事件派发与镜像状态契约

yiyiyiyiGKY and others added 4 commits June 5, 2026 17:01
调整 cat_model_change.gif 的帧时长分布,让烟圈到散开的关键阶段更均匀连贯。\n\n保持动画总时长、帧数和代码引用不变,仅更新过渡 GIF 资源。
修复 CAT1 蹲在网页 compact 对话框上缘时的表现问题。\n\n- 叫声触发时切换到 CAT1 点击态并在播放结束后恢复\n- compact mirror 状态先本页派发再广播,避免同页拖动时原图隐藏但镜像不显示\n- 补充静态契约测试覆盖声音反应和本页 mirror 派发
修复猫形态和人形态切换时靠近屏幕边界的过渡遮罩偏移。\n\n- 过渡遮罩按猫/模型中心直接定位,允许遮罩部分出屏\n- 保持猫/模型实体原有边界夹取不变\n- 补充静态契约测试,防止重新把遮罩夹回视口
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 6, 2026

PR changed again? Review this PR in Change Stack to compare snapshots and stay oriented.

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7cdb8fb3-f457-42e9-9a20-31ed49b0337e

📥 Commits

Reviewing files that changed from the base of the PR and between b7c9932 and 97448a7.

📒 Files selected for processing (2)
  • static/app-react-chat-window.js
  • tests/unit/test_react_chat_window_static.py

Walkthrough

该 PR 移除猫1过渡矩形的视口夹紧,新增猫1环保音触发的声音反应与紧凑镜像同步(含本地 CustomEvent 与 broadcast),并在 React 中为紧凑导出历史关闭期间缓存消息快照以避免新消息闪烁喵。

Changes

猫1声音联动与镜像同步流程完善

Layer / File(s) Summary
过渡矩形坐标计算简化
static/app-ui.js, tests/unit/test_avatar_return_button_idle_tiers_static.py
normalizeNekoTransitionRect 移除视口边界夹紧,直接使用 Math.round(centerX - size/2)Math.round(centerY - size/2) 返回 left/top,测试断言不包含 maxLeft/maxTop 喵。
猫1声音反应触发与表情播放
static/avatar-ui-buttons.js, tests/unit/test_avatar_return_button_idle_tiers_static.py
_playNekoIdleCat1AmbientSound 路径触发 _playNekoIdleCat1SoundReaction(),遍历符合条件的返回按钮(排除拖拽与 compact 顶边目标)、播放悬停艺术并基于 GIF 时长延迟调用 _syncNekoIdleCat1CompactMirrorReaction(),测试增加触发条件与播放/结束回调契约断言喵。
紧凑镜像本地派发与跨窗口广播重构
static/avatar-ui-buttons.js, tests/unit/test_avatar_return_button_idle_tiers_static.py
_postNekoIdleCat1CompactMirrorState 先派发本地 CustomEvent('neko:idle-cat1-compact-mirror-state', {detail:{via:'local',...}}),再尝试通过 nekoBroadcastChannel.postMessage 广播;对失败添加更细粒度 warning 并返回本地/远端结果组合;新增 settle 隐藏延迟常量与 options.assetUrl 覆盖支持,测试校验 payload 字段、计时与清理时序喵。

React:紧凑导出历史关闭期间快照

Layer / File(s) Summary
关闭期间消息快照与渲染控制
frontend/react-neko-chat/src/App.tsx, frontend/react-neko-chat/src/App.test.tsx
新增 compactExportHistoryClosingMessages 缓存关闭期间的 messages;关闭时写入快照并在动画结束后卸载面板并清空缓存;CompactExportHistoryPanel 在 closing 期间优先使用缓存渲染,测试在 closing 期间 rerender 注入新消息并断言不渲染喵。

Sequence Diagram(s)

sequenceDiagram
  participant Ambient as Cat1AmbientSound
  participant SoundReaction as _playNekoIdleCat1SoundReaction
  participant HoverArt as _playNekoIdleHoverArt
  participant SyncMirror as _syncNekoIdleCat1CompactMirrorReaction
  participant PostState as _postNekoIdleCat1CompactMirrorState
  participant LocalEvent as CustomEvent(neko:idle-cat1-compact-mirror-state)
  participant Broadcast as nekoBroadcastChannel

  Ambient->>SoundReaction: 触发声音反应
  SoundReaction->>HoverArt: 播放悬停艺术资源
  SoundReaction->>SyncMirror: 延迟(基于 GIF 时长)调用同步
  SyncMirror->>PostState: 发送镜像状态载荷
  PostState->>LocalEvent: 先派发本地 CustomEvent (via:'local')
  PostState->>Broadcast: 再尝试 postMessage 广播
  Broadcast-->>PostState: 返回发送结果
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Project-N-E-K-O/N.E.K.O#1649: 与本 PR 在 frontend/react-neko-chat/src/App.tsx/App.test.tsx 关于紧凑历史关闭期行为的修改高度相关喵。
  • Project-N-E-K-O/N.E.K.O#1622: 涉及 neko:idle-cat1-compact-mirror-state 的广播与监听,可能与本次本地派发与广播变更配合使用喵。
  • Project-N-E-K-O/N.E.K.O#1604: 与本次在 React 侧为紧凑导出历史关闭期引入缓存与时序控制的改动存在功能链路重合喵。

"喵~环保音响起小猫舞,喵,
坐标四舍五入不再躲,喵,
本地先跳舞再把歌传,喵,
关闭历史有快照不闪现,喵,
小猫同步成功大家笑,喵~"

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning 标题关联了 PR 的主要变更——猫咪/模型转换行为的优化与修复,但表述不完整,'behavio' 明显是拼写错误(应为 'behavior')。 建议修正拼写错误为 'fix(cat): polish cat/model transition behavior',使标题更专业且准确表达 PR 的核心目标。
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1a38e9504a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread static/avatar-ui-buttons.js Outdated
const token = art.__nekoIdleHoverToken || 0;
_getNekoIdleGifDurationMs(clickSrc).then((durationMs) => {
window.setTimeout(() => {
if ((art.__nekoIdleHoverToken || 0) !== token) return;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore compact mirror after sound reaction

When CAT1 is perched on the compact top edge and the ambient sound reaction plays, this token check prevents the mirror from switching back from the click GIF. _finishNekoIdleHoverArtAfterPlayback() is scheduled just above and, at the end of the same playback, calls _clearNekoIdleHoverPlayback(), which increments art.__nekoIdleHoverToken; because that timeout is registered before this mirror-restore timeout, this guard usually returns and leaves the compact mirror on clickSrc until another mirror update or timeout occurs, while the hidden original art has already returned to idle.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@static/avatar-ui-buttons.js`:
- Around line 664-699: 在 _playNekoIdleIdleCat1SoundReaction 中的问题是镜像同步仍然使用硬编码
clickSrc 并等待 fetch 得到完整 GIF 时长,导致本地按钮与 compact mirror 播放资源/时长不同步;修复方法是在调用
_playNekoIdleHoverArt(art, _NEKO_IDLE_TIER_CAT1) 之后立即读取 art.__nekoIdleHoverSrc 和
art.__nekoIdleHoverStartedAt,使用实际播放的资源(art.__nekoIdleHoverSrc)立即调用
_syncNekoIdleCat1CompactMirrorReaction(...) 做“开始/切换”同步,随后用
_getNekoIdleGifDurationMs(art.__nekoIdleHoverSrc) 减去 (Date.now() -
art.__nekoIdleHoverStartedAt) 计算剩余播放时长并在该剩余时长到期后再次调用
_syncNekoIdleCat1CompactMirrorReaction(...) 做“恢复/结束”同步;不要再使用 clickSrc 或等待 fetch
的全长作为延迟基准,并保留对 art.__nekoIdleHoverToken 检查逻辑以防竞态。
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 48f414cd-d79f-4043-abb5-3fb1d9358bea

📥 Commits

Reviewing files that changed from the base of the PR and between 406a9ca and 1a38e95.

⛔ Files ignored due to path filters (1)
  • static/assets/neko-idle/cat_model_change.gif is excluded by !**/*.gif
📒 Files selected for processing (3)
  • static/app-ui.js
  • static/avatar-ui-buttons.js
  • tests/unit/test_avatar_return_button_idle_tiers_static.py

Comment thread static/avatar-ui-buttons.js
修复 CAT1 蹲在 compact 对话框上时的桌面镜像拖拽闪烁和叫声反应同步问题。\n\n关闭历史对话时冻结 closing 动画期间的消息快照,避免新消息在关闭态历史区域闪现;重新打开后仍按最新消息渲染。\n\n补充对应静态与 React 回归测试。
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@static/avatar-ui-buttons.js`:
- Around line 1417-1438: The settle timer can fire later and flip a
newly-changed state back to inactive; ensure any existing
container.__nekoIdleCat1CompactMirrorSettleTimer is cleared whenever we take any
!nextActive path (not only when nextActive becomes true) before creating a new
timer or performing immediate deactivation. Concretely: in the !nextActive
branch clearTimeout(container.__nekoIdleCat1CompactMirrorSettleTimer) and set it
to 0 at the start (or immediately before any early returns), then proceed to
create a new settle timer only if none exists; also ensure any early
immediate-deactivate paths (calls to _setNekoIdleCat1CompactMirrorActive with
forceImmediate) clear the timer first so stale callbacks cannot later override
the new state.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ff783ae3-3a2f-428c-828f-0fad57313052

📥 Commits

Reviewing files that changed from the base of the PR and between 1a38e95 and 46c10ad.

📒 Files selected for processing (4)
  • frontend/react-neko-chat/src/App.test.tsx
  • frontend/react-neko-chat/src/App.tsx
  • static/avatar-ui-buttons.js
  • tests/unit/test_avatar_return_button_idle_tiers_static.py

Comment thread static/avatar-ui-buttons.js
在 CAT1 compact mirror 的立即关闭路径中清理 pending settled timer,避免延迟回调在状态变化后再次触发 inactive。\n\n保留 compact-surface-settled 的一次性延迟关闭语义,补充静态契约测试覆盖 timer 清理顺序。
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b7c9932958

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +1392 to +1395
window.dispatchEvent(new CustomEvent('neko:idle-cat1-compact-mirror-state', {
detail: Object.assign({
via: 'local'
}, message)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don't mirror compact CAT1 locally from the pet window

When the compact chat runs in the separate Electron chat window, templates/index.html loads both avatar-ui-buttons.js and app-react-chat-window.js, so this new local dispatch is consumed by the pet window's handleIdleCat1CompactMirrorState before the broadcast reaches the chat window. In that context the original cat is hidden by is-cat1-compact-mirror-active, while the pet window also creates a compact mirror using the chat window's screen rect, which can leave a duplicate/mispositioned cat in the pet window; the local event should be gated to same-page compact surfaces or ignored by the pet window.

Useful? React with 👍 / 👎.

修复桌面端 pet 主窗口误消费 CAT1 compact mirror 本地事件的问题。\n\n仅让 Electron pet 主窗口忽略 via=local 且 source=pet-window 的 mirror 状态,避免主窗口创建错位镜像或隐藏原猫;独立聊天窗口仍通过 BroadcastChannel 同步,网页端同页 compact 路径保持不变。\n\n补充静态回归测试,锁定 pet 主窗口本地事件过滤边界。
Copy link
Copy Markdown
Contributor

@wehos wehos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

祝我们幸福!

@wehos wehos merged commit 03cbce3 into Project-N-E-K-O:main Jun 6, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants