diff --git a/README.md b/README.md index a7b1904..cd8a26b 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ - **完整實作 Codex V2 pet contract** — 8 欄 × 11 列、192×208 cell 的 spritesheet,9 個標準動作列 + 16 個看向方向,**每一幀的時長都照 Codex 規格**,動起來跟在 Codex app 裡一模一樣。 - **行為邏輯逐項對照 Codex** — 狀態優先序、各狀態到期時間、動畫時序、hover 版面、初次問候、拖曳方向判定……都是從 Codex app 的 `app.asar` 讀出來的數值(見[與 Codex 的對照](#與-codex-的對照))。 -- **16 方向追視(選配,預設關)** — 開啟後游標在哪她就看哪(000 = 正上方、順時針 22.5° 一格),追視時是靜態擺姿。Codex 的寵物**不看滑鼠**——它看的是 quick chat 的文字游標與 computer-use 的虛擬游標,所以預設關閉以求一致;喜歡的話在選單打開。 +- **16 方向追視(選配,預設關)** — 開啟後、且目前是待機狀態時,游標在哪她就看哪(000 = 正上方、順時針 22.5° 一格),追視時是靜態擺姿;Claude Code 在工作時以狀態動畫優先。Codex 的寵物**不看滑鼠**——它看的是 quick chat 的文字游標與 computer-use 的虛擬游標,所以預設關閉以求一致;喜歡的話在選單打開。 - **自動修正圖檔缺陷** — Codex 的 hatch 產生器有時會把整列 look 格子畫得比 idle 小(內附的小念第 10 列就小了 22%,游標一往下移人物就縮水)。以整列中位數偵測、用腳底當支點放大回正確身高;單格的姿勢差異不動。Codex 本身沒有做這件事。 - **與 Codex 同尺寸** — 100% 就是 Codex 桌面寵物的大小(126 × 137),高度用 Codex 的 `ceil(寬 × 208/192)` 算,兩邊並排看起來一樣大。 - **真正的桌面寵物** — 透明、無邊框、永遠置頂;透明區域**點穿**,只有壓在人物像素上滑鼠才會被吃掉,不擋你操作底下的視窗。 @@ -123,7 +123,7 @@ npm run autostart:install # (可選)開機自動啟動 | `SubagentStart` / `SubagentStop` | | `running` | | | `PreCompact` | | `running` | 🗜️ 整理記憶中… | | `SessionEnd` | | 移除該 session | 最後一個結束時 👋 掰掰 | -| (無事件) | 游標移動(追視開啟時) | 16 方向 `look`,覆蓋所有狀態 | | +| (無事件) | 游標移動(追視開啟時、且目前為 idle) | 16 方向 `look` | | | (無事件) | 拖曳 | 單次位移 ≥ 4 px 才決定 `running-left` / `running-right` | | | (無事件) | 游標進入人物方框 | `jumping` × 3 → 回原狀態 | | @@ -261,7 +261,8 @@ Claude Code ──hook(stdin JSON)──▶ hook/claude-pet-hook.js ──PO | `jumping` | 定義在 `respondToHover ? "jumping" : state`,但整個 app 沒人開啟那個 prop | **選擇打開**:游標進入人物方框就播 3 次 | | hover 版面 | 40 px 內進入、56 px 外退出;人物縮 0.9、下移 14 px、上方 24 px 控制鈕、200 ms | 未採用——控制鈕的功能就是選單,右鍵已經有了 | | hover 判定 | 用矩形(`pet-area-hover-changed` 由 rect 算) | 相同(逐像素會因每格輪廓不同而抖動) | -| 追視 | 看 quick chat 文字游標/computer-use 游標,死區 1 px,覆蓋所有狀態 | 沒有對應來源;改為**選配**看滑鼠,預設關 | +| 追視來源 | quick chat 的文字游標/computer-use 游標 | 沒有對應來源;改為**選配**看滑鼠,預設關 | +| 追視優先序 | `lookFrame` 覆蓋所有狀態 | 只在 idle 時追視。Codex 的文字游標只有你在打字時才存在,滑鼠卻是一直都在,照搬會讓工作中的動畫永遠被靜態擺姿蓋掉 | | 拖曳方向 | 單次取樣水平位移 ≥ 4 px | 相同 | | 拖曳放開 | 彈簧回彈(spring 220 / damping 26) | 無(直接停在放開處) | | 點擊 | 開 quick chat | 揮手(沒有對應功能) | diff --git a/package-lock.json b/package-lock.json index 7b2554d..5a2071e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "claude-pet", - "version": "2.0.1", + "version": "2.0.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "claude-pet", - "version": "2.0.1", + "version": "2.0.2", "devDependencies": { "electron": "^43.4.1", "electron-builder": "^26.0.12" diff --git a/package.json b/package.json index 93cd8b9..2458cae 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "claude-pet", - "version": "2.0.1", + "version": "2.0.2", "description": "Codex Pets 相容的 Claude Code 桌面寵物(spriteVersionNumber 2:9 個動作列 + 16 方向追視)", "main": "main.js", "private": true, diff --git a/renderer/renderer.js b/renderer/renderer.js index 07bc45e..84f7518 100644 --- a/renderer/renderer.js +++ b/renderer/renderer.js @@ -103,6 +103,12 @@ function baseSequence(anim) { const reducedMotionQuery = window.matchMedia("(prefers-reduced-motion: reduce)"); function startAnim(anim, now) { + // 追視是單一靜態擺姿,沒有幀序列(TIMINGS 裡沒有 look)。 + // 少了這道判斷會走進 cycleOf("look") 而丟 TypeError,requestAnimationFrame 迴圈整個死掉。 + if (anim === "look") { + player = { anim, frames: [], loopStart: null, step: 0, stepStart: now }; + return; + } let seq; if (reducedMotionQuery.matches) { // 與 Codex 相同:系統關閉動畫時只顯示該狀態的第一格 @@ -287,9 +293,12 @@ function resolveAnim() { // 一次性動作(點擊揮手、初次問候)。Codex 的 lookFrame 會蓋過這些,但它的追視來源不是滑鼠; // 滑鼠永遠在畫面上,若照搬會讓點擊揮手永遠看不到,所以這裡讓手勢優先。 if (overlay) return overlay.anim; - // Codex:lookFrame 一旦有值就覆蓋所有狀態,靜態擺姿 - if (followCursor && petInfo?.version === 2 && look.idx !== null) return "look"; - return baseState(); + const base = baseState(); + // Codex 的 lookFrame 會覆蓋所有狀態,但它的追視來源是 quick chat 的文字游標, + // 只有你真的在那裡打字時才存在。滑鼠則是一直都在,照搬會讓工作中的動畫永遠被靜態擺姿蓋掉, + // 寵物就失去反映 Claude Code 狀態的作用,所以只在 idle 時追視。 + if (base === "idle" && followCursor && petInfo?.version === 2 && look.idx !== null) return "look"; + return base; } function projectName(cwd) {