diff --git a/README.md b/README.md index 6c5c6e2..9a33cc8 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,6 @@ npm run autostart:install # (可選)開機自動啟動 | 左鍵點一下 | 揮手(若有非固定的氣泡,先關閉氣泡) | | 左鍵拖曳 | 搬家;往左播 running-left、往右播 running-right;放開後位置寫入設定檔 | | 右鍵 / 系統匣左鍵 | 開啟選單 | -| 滑鼠靠近(40 px 內) | 她縮到 0.9、往下讓出位置,上方出現選單鈕;離開 56 px 外才復原(Codex 的 hover 版面) | | 滑鼠靠近 | 盯著游標看(選單開啟「跟著游標看」後才有) | **選單功能** @@ -125,7 +124,6 @@ npm run autostart:install # (可選)開機自動啟動 | `SessionEnd` | | 移除該 session | 最後一個結束時 👋 掰掰 | | (無事件) | 游標移動(追視開啟時) | 16 方向 `look`,覆蓋所有狀態 | | | (無事件) | 拖曳 | 單次位移 ≥ 4 px 才決定 `running-left` / `running-right` | | -| (無事件) | 游標靠近 40 px 內 | 動畫不變;人物縮 0.9、顯示選單鈕 | | 規則: @@ -259,7 +257,7 @@ Claude Code ──hook(stdin JSON)──▶ hook/claude-pet-hook.js ──PO | `review` | turn 完成且未讀,直到使用者回到該 thread | 直到該 session 下一個動作 | | `waving` | 只在 first-awake(每隻寵物一次,8 秒) | 相同(記在設定檔 `greetedPetIds`) | | `jumping` | 只在 `respondToHover`,而整個 app 沒人開啟它 | 只在選單「動作測試」 | -| hover | 40 px 內進入、56 px 外退出;人物縮 0.9、下移 14 px、上方 24 px 控制鈕、200 ms | 相同;控制鈕是選單 | +| hover | 40 px 內進入、56 px 外退出;人物縮 0.9、下移 14 px、上方 24 px 控制鈕、200 ms | 未採用——控制鈕的功能就是選單,右鍵已經有了 | | 追視 | 看 quick chat 文字游標/computer-use 游標,死區 1 px,覆蓋所有狀態 | 沒有對應來源;改為**選配**看滑鼠,預設關 | | 拖曳方向 | 單次取樣水平位移 ≥ 4 px | 相同 | | 拖曳放開 | 彈簧回彈(spring 220 / damping 26) | 無(直接停在放開處) | diff --git a/package.json b/package.json index 8128dcc..817d4fe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "claude-pet", - "version": "0.1.0", + "version": "2.0.0", "description": "Codex Pets 相容的 Claude Code 桌面寵物(spriteVersionNumber 2:9 個動作列 + 16 方向追視)", "main": "main.js", "private": true, diff --git a/renderer/index.html b/renderer/index.html index e076041..f9a858d 100644 --- a/renderer/index.html +++ b/renderer/index.html @@ -9,7 +9,6 @@
-
diff --git a/renderer/renderer.js b/renderer/renderer.js index 761fc32..3e5cac8 100644 --- a/renderer/renderer.js +++ b/renderer/renderer.js @@ -54,18 +54,12 @@ const EXPIRY = { failed: 3600e3, waiting: 1440 * 60e3, review: 10080 * 60e3 }; const RUNNING_SILENCE = 30 * 60e3; const IDLE_PRUNE = 30 * 60e3; // idle 的 session 閒置多久後從清單移除(純清理,不影響顯示) -// hover 版面,數值與 Codex 的 petControlsAppearance 相同 -const HOVER_SCALE = 0.9; // H_:游標靠近時人物縮到 0.9 -const HOVER_OFFSET_Y = 10 + 8 / 2; // hoverOffsetY + hoverControlGap / 2:人物往下移,讓出上方放控制鈕 -const PROXIMITY_ENTER = 40; // proximityEnterDistance:游標離人物多近算「靠近」 -const PROXIMITY_EXIT = 56; // proximityExitDistance:離多遠才算離開(遲滯,避免邊界抖動) const GREETING_MS = 8000; // first-awake 問候通知的存活時間(Pi = 8 s) const canvas = document.getElementById("sprite"); const ctx = canvas.getContext("2d", { willReadFrequently: true }); const bubble = document.getElementById("bubble"); const bubbleText = document.getElementById("bubble-text"); -const controls = document.getElementById("controls"); let sheet = null; let petInfo = null; @@ -130,7 +124,6 @@ function startAnim(anim, now) { let bubbleTimer = null; let bubbleSticky = false; let ignoringMouse = true; -let near = false; // 游標是否在人物附近(Codex 的 proximity) let pressed = null; let lastReported = ""; @@ -166,10 +159,6 @@ function applyScale(s) { canvas.height = Math.round(PET_H * s); canvas.style.width = `${canvas.width}px`; canvas.style.height = `${canvas.height}px`; - // 控制鈕底邊 = 縮小後人物的頂端再往上 8 px(hoverControlGap)。 - // 人物以中心縮到 0.9 再往下移 HOVER_OFFSET_Y,頂端會下移 h*(1-0.9)/2 + HOVER_OFFSET_Y。 - const shiftedTop = canvas.height * (1 - HOVER_SCALE) / 2 + HOVER_OFFSET_Y; - controls.style.bottom = `${8 + canvas.height - shiftedTop + 8}px`; } // 量一格的人物輪廓(用來偵測圖檔裡整列被畫小的缺陷) @@ -563,31 +552,6 @@ function overSprite(x, y) { } } -function overControls(x, y) { - if (!near) return false; - const r = controls.getBoundingClientRect(); - return x >= r.left && x <= r.right && y >= r.top && y <= r.bottom; -} - -function distanceToRect(x, y, r) { - const dx = Math.max(r.left - x, 0, x - r.right); - const dy = Math.max(r.top - y, 0, y - r.bottom); - return Math.hypot(dx, dy); -} - -// Codex 的 hover:游標進到離人物 40 px 內就切到 hover 版面——人物縮到 0.9、往下移 14 px、 -// 上方出現 24 px 的控制鈕(200 ms 過渡);離開超過 56 px 才退出。動畫狀態本身不變。 -function updateProximity(x, y) { - const d = distanceToRect(x, y, canvas.getBoundingClientRect()); - setNear(near ? d <= PROXIMITY_EXIT : d <= PROXIMITY_ENTER); -} - -function setNear(on) { - if (on === near) return; - near = on; - document.body.classList.toggle("near", on); -} - function setIgnore(ignore) { if (ignore === ignoringMouse) return; ignoringMouse = ignore; @@ -605,18 +569,15 @@ window.addEventListener("mousemove", (e) => { } return; } - updateProximity(e.clientX, e.clientY); - setIgnore(!(overSprite(e.clientX, e.clientY) || overBubble(e.clientX, e.clientY) || overControls(e.clientX, e.clientY))); + setIgnore(!(overSprite(e.clientX, e.clientY) || overBubble(e.clientX, e.clientY))); }); document.addEventListener("mouseout", (e) => { if (e.relatedTarget || pressed) return; - setNear(false); setIgnore(true); }); window.addEventListener("mousedown", (e) => { - if (e.target instanceof Element && e.target.closest("#controls")) return; if (e.button === 0) { pressed = { x: e.clientX, y: e.clientY, sx: e.screenX, sy: e.screenY, moved: false }; } else if (e.button === 2) { @@ -667,11 +628,6 @@ window.pet.on("pet:settings", (st) => { }); window.pet.on("pet:event", handleEvent); -document.getElementById("menu-btn").addEventListener("click", (e) => { - e.stopPropagation(); - window.pet.send("pet:context-menu"); -}); - // 系統的「減少動態效果」切換時,讓目前的動畫重新開始(Codex 的 effect 也把 reducedMotion 列為相依) reducedMotionQuery.addEventListener("change", () => { if (player.anim) startAnim(player.anim, performance.now()); }); diff --git a/renderer/style.css b/renderer/style.css index 783f556..55a8cc3 100644 --- a/renderer/style.css +++ b/renderer/style.css @@ -56,53 +56,6 @@ html, body { #sprite { display: block; - /* Codex 的 hover 版面:以中心縮到 0.9 並往下移,200 ms 過渡(transitionDurationMs) */ - transform-origin: 50% 50%; - transition: transform 200ms ease; -} - -body.near #sprite { - transform: translateY(14px) scale(0.9); -} - -/* 靠近時氣泡讓開控制鈕 */ -body.near #bubble:not(.hidden) { - transform: translateY(-22px); -} - -/* 控制鈕:24 px(hoverControlSize),靠近時才出現 */ -#controls { - position: absolute; - left: 50%; - transform: translateX(-50%); - display: flex; - gap: 8px; - opacity: 0; - pointer-events: none; - transition: opacity 200ms ease; -} - -body.near #controls { - opacity: 1; - pointer-events: auto; -} - -#controls button { - width: 24px; - height: 24px; - padding: 0; - border: none; - border-radius: 50%; - background: rgba(255, 255, 255, 0.97); - color: #222; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28); - font-size: 13px; - line-height: 24px; - cursor: pointer; -} - -#controls button:hover { - background: #fff; } #sprite.grab {