Commit Graph
7 Commits
Author SHA1 Message Date
JianMiauandClaude Fable 5 93f635dd01 氣泡改成兩行,第一行顯示對話名稱或專案名
摘要:
氣泡上方加一行標題(/rename 設的對話名稱,沒設就用專案資料夾名),訊息移到第二行。

根本原因:
原本把專案名以「(專案名)」的形式接在訊息尾巴,訊息本身就短,
加了括號後更容易被氣泡的單行省略號截掉,而且同時開多個 session 時
要看到最後才知道是哪一個在叫你。

影響:
多 session 並行時不容易分辨氣泡是哪個專案發出的;訊息也被括號擠掉。

修法:
- index.html 氣泡內加 #bubble-title;style.css 把單行限制從 #bubble 移到
  #bubble-title 與 #bubble-text 各自身上,兩行各自省略,
  並以 :empty 讓沒有標題時整行收起來,不留空白。
- say() 增加第四個參數 title,事件處理處的八個呼叫點改傳標題、移除 suffix。
- main.js 的 latestNarration 改名為 readTranscript,同一次反向掃描同時取出
  助理說明與 custom-title(/rename 會把它寫進 transcript),兩者都拿到就提早結束;
  收到事件時把 sessionTitle 一併併進 payload。
- 標題優先序:對話名稱 > 專案資料夾名 > 不顯示。

驗證:
- 單元測試以真實 transcript 執行 readTranscript:取到對話名稱 "claude-pet"、
  同時取到助理說明、快取一致、路徑不存在或為 null 時回 null,6 項全過。
- 實機截圖確認兩行版面正確。截圖當下剛好收到另一個 session 的真實事件,
  標題顯示 "telegram-codex-bot"、第二行是該 session 的助理說明,
  等於用實際流量驗證了多 session 下的標題辨識。
- 以 ELECTRON_RUN_AS_NODE 讀打包後 app.asar,確認版本 2.0.5 與十項改動都在出貨檔案裡,
  包含舊的 suffix 已完全移除。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 12:09:01 +08:00
JianMiauandClaude Fable 5 95c9b97eb7 工作中氣泡顯示工具動作與助理說明
摘要:
running 狀態的氣泡改成有東西就播報:優先顯示助理剛說那句話,否則顯示正在用的工具與對象。

根本原因:
使用者希望氣泡能像 telegram bot 那樣顯示 AI 的思考過程。
實際查證後思考文字拿不到——Claude Code 從 2.1.238 起不再把 thinking 寫進 transcript,
只留加密簽章(本 session 169 個 thinking 區塊文字全為空字串);
比對同樣用 claude-opus-5 但版本為 2.1.237 的舊 session 則存得到,
確認是 Claude Code 版本差異而非模型差異。Claude Code 也沒有對外事件串流,
所以 hook 拿不到思考內容,這點與 Codex CLI 的 --json reasoning 事件不同。
而參考的 cluemarket-tg-bot 其實也沒顯示思考:它的變數雖名為 lastThinkingText,
抓的卻是 text 區塊,另外兩個是寫死的「思考中」字串與工具標籤。
原本 running 狀態除了關閉舊氣泡外沒有任何資訊。

影響:
工作期間看不出 Claude Code 正在做什麼,只知道她在忙。

修法:
- hook 補送 toolTarget(依 command / file_path / pattern / url 等順序取一個欄位並截斷,
  避免 Write 這類工具把整份檔案內容塞進 payload)、agentType 與 transcriptPath。
- main.js 新增 latestNarration:由 transcript 尾端 192 KB 反向找最後一個非空的
  assistant text 區塊,依檔案大小快取,transcript 長到數 MB 也不會變慢;
  收到事件時把 narration 與其 uuid 併進 payload 再轉給 renderer。
- renderer 新增 sayActivity:有新的助理說明就顯示第一句,否則顯示工具標籤加對象。
  同一段說明只播報一次;waiting 時不播報以免蓋掉固定氣泡。
  另加 stripMarkdown,否則助理文字的 **粗體**、反引號、標題與連結語法會直接出現在氣泡裡。
- README 新增「能顯示什麼、不能顯示什麼」一節說明思考文字為何拿不到。

驗證:
- 單元測試以 DOM stub 載入完整 renderer.js 並抽出 main.js 的 latestNarration,15 項全過:
  對真實 transcript 取得助理說明與 uuid、快取一致、路徑不存在或為 null 時回 null 不拋錯、
  firstSentence 取第一句與忽略空行、Bash 取指令前段、檔案路徑只取檔名、過長截斷、
  新說明更新 id、同段不重複、waiting 時不播報、空 payload 不拋錯。
- markdown 清理另 7 項全過:粗體、反引號、標題、清單、連結、程式碼區塊、無標記純文字。
- 實機啟動並以真實事件驗證:events.log 顯示 hook 確實帶出 toolTarget 與 transcriptPath;
  截圖確認氣泡顯示「⚙️ 執行 npm run dist」,以及傳入真實 transcript 後顯示「💬 助理說明」。
  markdown 殘留問題就是在這一步的截圖中發現並修掉的。
- 以 ELECTRON_RUN_AS_NODE 讀打包後 app.asar 與 app.asar.unpacked,確認版本 2.0.4
  與九項改動都在出貨檔案裡。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 10:16:43 +08:00
JianMiauandClaude Fable 5 d78f99fcc7 游標離開人物時立刻中斷跳躍,不再播完三次
摘要:
hover 跳躍改為「游標一離開就停」,而不是不管游標在哪都把 3 次播完。

根本原因:
把 hover 跳躍實作成一次性 overlay,播放長度固定為 STATE_REPEATS 輪,
setHovered(false) 只更新 hovered 旗標、不動 overlay,所以游標即使馬上移開,
跳躍仍會自顧自播完約 2.5 秒才回到基礎狀態。
這其實也偏離 Codex:Codex 的顯示狀態是 `hovered ? "jumping" : state`,
游標一離開就翻回原狀態並重跑動畫,本來就不會把跳躍播完。

影響:
只是游標經過或很快移開時,寵物還會繼續跳完,反應顯得遲滯、不跟手。

修法:
- play() 之後把該 overlay 標記 fromHover。
- setHovered(false) 時若目前 overlay 是 hover 觸發的就直接清掉,
  下一個 tick 會依 resolveAnim 回到當下的基礎狀態。
  只清 fromHover 的,初次問候揮手等其他一次性動作不受影響。
- README 的日常使用、行為對照表、與 Codex 的對照三處同步更新,
  對照表新增「hover 離開」一列。

保持不變:
進入時最多跳 3 次、游標持續停留不重複觸發(使用者已確認要這個行為)。

驗證:
以 DOM stub 載入完整 renderer.js 執行 13 項測試全過,涵蓋:
進入觸發 jumping 且序列為 15 幀、標記 fromHover、離開後 overlay 立刻為 null、
resolveAnim 立刻回 idle、工作中離開回到 running 而非 idle、
問候揮手不會被離開中斷、既有 overlay 期間不觸發跳躍、
持續停留不重複觸發、拖曳中不觸發。
另以 ELECTRON_RUN_AS_NODE 讀打包後 app.asar 確認版本 2.0.3 與六項改動都在出貨檔案裡,
exe FileVersion 為 2.0.3。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 16:58:57 +08:00
JianMiauandClaude Fable 5 7e2f689f26 修正開啟追視後 renderer 崩潰,追視改為只在待機時生效
摘要:
startAnim 對 look 會丟 TypeError 導致動畫迴圈整個死掉;另把追視改成只在 idle 時覆蓋。

根本原因:
1. 追視是單一靜態擺姿,TIMINGS 裡沒有 look 這個動作。但序列模型改寫後,
   tick 在動畫改變時無條件呼叫 startAnim(anim),傳入 "look" 時會走到
   cycleOf("look") → TIMINGS["look"].map → TypeError。
   例外從 requestAnimationFrame 的 callback 拋出,迴圈就此中止,
   整隻寵物凍結,也不再回應任何 hook 事件——就是使用者回報的「直接沒反應」。
2. 對照 Codex 時把 lookFrame 改成覆蓋所有狀態。但 Codex 的追視來源是 quick chat 的
   文字游標,只有使用者真的在打字時才存在;滑鼠卻是一直都在,照搬會讓工作中的
   running / waiting / review 動畫永遠被靜態擺姿蓋掉,寵物失去反映狀態的作用。

影響:
選單一勾「跟著游標看」,游標一動寵物就完全凍結,且不會恢復。

修法:
- startAnim 開頭對 look 早退,直接設成空的幀序列(靜態擺姿),
  不進入 cycleOf / baseSequence。draw 本來就單獨處理 look,tick 的推幀也已排除 look。
- resolveAnim 先取 baseState(),只有 base 為 idle 時才回傳 look;
  一併避免原本重複呼叫 baseState() 的副作用。
- README 的特色、行為對照表、與 Codex 的對照三處同步更新,
  對照表把「追視」拆成來源與優先序兩列並說明為何刻意不同。

不動的部分:
hover 跳躍維持進入時播 3 次、之後停住不再跳(使用者確認就是要這個行為)。
另已確認跳躍結束後會回到當下的基礎狀態而非一律 idle。

驗證:
- 單元測試(抽出實際程式碼執行):修正前 startAnim("look") 拋
  TypeError: Cannot read properties of undefined (reading 'map'),修正後回傳空序列;
  idle 6 幀、running 24 幀正常;resolveAnim 在追視開啟下
  idle→look、running→running、waiting→waiting、review→review,
  hover 跳躍優先於 look 與 running,跳躍結束回到 running 而非 idle,共 11 項全過。
- 實機開啟 followCursor 啟動:/state 持續回應且 anim 為 look(修正前此處即崩潰),
  log 無任何例外;灌入事件後 running / waiting 正確蓋過追視。
- 以 ELECTRON_RUN_AS_NODE 讀打包後 app.asar:版本 2.0.2、崩潰修正與追視優先序都在,
  hover 跳躍、矩形判定、右鍵選單未受影響;exe FileVersion 為 2.0.2。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 16:48:43 +08:00
JianMiauandClaude Fable 5 538b379dff package-lock 版號同步為 2.0.1
根本原因:
npm version patch 會同時改 package.json 與 package-lock.json,
上一個 commit 只提交了 package.json,lock 的版號留在工作區未提交。

影響:
lock 與 package.json 的版號不一致,且工作區殘留未提交變更。

修法:
補上 package-lock.json 的版號變更。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 16:29:32 +08:00
JianMiauandClaude Fable 5 56fd8448ba 用 electron-builder 打包成 Windows exe
摘要:
加上 electron-builder 設定,`npm run dist` 產出 NSIS 安裝檔與免安裝版,並修正打包後才會出現的路徑問題。

根本原因:
原本三處路徑都假設程式是攤在資料夾裡跑的,打包成 asar 後會壞:
1. hook 腳本路徑會落在 app.asar 內,而 Claude Code 是用 `node <路徑>` 執行它,node 讀不到 asar 裡的檔案,hook 會直接失敗。
2. pets 資料夾同樣在 asar 內,使用者無法自己丟寵物進去,選單「開啟寵物資料夾」也開不起來。
3. 開機自動啟動寫入的是 `electron.exe + 專案路徑`,打包後沒有 node_modules,登錄值會指向不存在的檔案。

影響:
沒有可散佈的執行檔;直接打包的話 hook、換寵物、開機啟動三個功能都會壞掉。

修法:
- 新增 lib/paths.js:unpacked() 把 app.asar 換成 app.asar.unpacked,isPackaged() 以 process.defaultApp 判斷是否為打包版。
- build.asarUnpack 把 hook/ 與 pets/ 解到 asar 外,hooks-installer 與 main.js 的 PETS_DIR 都改走 unpacked()。
- autostart 打包後改用 process.execPath,並跳過開發版才需要的 electron.exe 存在檢查。
- NSIS 設為 per-user、可改安裝路徑、建立桌面與開始選單捷徑,解除安裝不刪 ~/.claude-pet。
- 新增 build/icon.ico(16–256,七種尺寸),取自小念 spritesheet 第 0 列第 6 欄的頭肩方形裁切。
- dist/ 加入 .gitignore;README 補上「打包成 exe」一節與安裝檔的使用方式。

驗證:
以 ELECTRON_RUN_AS_NODE 執行打包後的 exe(不開視窗)載入打包內的 lib,確認 isPackaged 為 true、hook 路徑指到 app.asar.unpacked 且檔案存在、autostart 指到 exe 本身、pets 掃得到 xiao-nian、renderer 讀得到 asar 內的 index.html;另確認 exe 版本資訊與七種尺寸圖示皆已嵌入。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 11:03:11 +08:00
JianMiauandClaude Fable 5 b1d57249f8 初始提交:Codex Pets 相容的 Claude Code 桌面寵物
摘要:
Electron 桌面寵物,透過 Claude Code hooks 即時反映工作狀態(思考、等待授權、失敗、完成),並支援 16 方向追視游標。

根本原因:
專案尚未納入版控,需要建立初始 repo 以便後續追蹤與協作。

影響:
建立 main 分支的第一個版本,包含完整可執行的程式碼與內附寵物「小念」。

修法:
納入以下內容:
- main.js / preload.js:Electron 主程序、透明置頂視窗、系統匣選單、HTTP 事件伺服器、游標輪詢與拖曳
- renderer/:Codex V2 spritesheet 幀動畫播放器、session 狀態機、氣泡、alpha 點穿判定
- hook/claude-pet-hook.js:Claude Code hook 端,stdin → POST /event,永遠 exit 0
- lib/、scripts/:hooks 安裝/移除、Windows 開機自動啟動
- pets/xiao-nian:內附寵物包(pet.json + spritesheet.webp)
- README.md、docs/states.png:使用說明與狀態總覽圖

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 10:32:39 +08:00