feat(desktop): add desktop pet window with Synergy state awareness - #1220
feat(desktop): add desktop pet window with Synergy state awareness#1220yzxoi wants to merge 6 commits into
Conversation
Add a transparent always-on-top pet window to the Electron shell that subscribes to the Synergy global event stream (GET /global/event) in the main process, maps session events to a seven-mood animation state machine (idle/working/happy/celebrate/angry/sleepy/dragging), and drives a sandboxed sprite-sheet renderer with drag and click interaction. Introduces pet-window/pet-state/pet-settings/pet-sprite/pet-sse/pet-page/pet-preload modules, a versioned desktop-pet.json settings file, 8:7 sprite-sheet validation with data-URL fallback, and bundled pet-preload in desktop:build. Settings persist position, size, idle timeout, and frame rate; server restarts re-point the SSE subscription and quit stops the window. Co-authored-by: synergy-agent <299070056+synergy-agent@users.noreply.github.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
The pet page's CSS fallback blob used literal product colors (#7dd3fc / #38bdf8 / #0284c7 and rgba(...)), which violates the frontend semantic color contract enforced by the App color-token-contract test. Inject the desktop shell skin's semantic tokens (markBackground, control, focus, border, text) as CSS variables in the pet document and reference only those in the fallback styles, mirroring how the startup page derives its palette from the theme snapshot. Co-authored-by: synergy-agent <299070056+synergy-agent@users.noreply.github.com>
|
All checks are done and the review is complete. I have one blocking integration finding (the SSE endpoint the pet subscribes to does not exist server-side), two non-blocking bugs, and a couple of suggestions — all with file:line evidence. Review — #1220 desktop pet windowReviewed BlockingB1 — The pet subscribes to a non-existent endpoint; it will never receive an event.
The tests can't catch this: Note the docs encode the same wrong endpoint and need updating with the fix: Two fix options:
B1b — even Non-blockingN1 — VP8L (lossless WebP) height is decoded from the wrong bits. N2 — Suggestions
Synergy: review complete — 1 blocking (dead SSE endpoint), 2 non-blocking, 2 suggestions |
The pet window connected to GET /global/event?stream=delta, but the Synergy
server registers the bus event stream as a top-level GET /event SSE route
(server.ts /event, supporting ?stream=delta); /global/event does not exist
and falls through to the SPA fallback, so the pet never received session
events and working/celebrate/angry moods could not trigger. Point
connectSse() at ${base}/event?stream=delta, fix the test assertion that
mirrored the wrong URL, and correct the endpoint references in module
comments, the decision record, and the research proposal.
Co-authored-by: synergy-agent <299070056+synergy-agent@users.noreply.github.com>
Follow-up fix: pet SSE subscription endpointLight Loop review found the pet subscribed to Fix in commit
Verified locally: |
The Coverage CI job failed because src/pet-preload.ts was never loaded by any test (measured 39, missing 1). Add test/pet-preload.test.ts which loads the preload through the electron mock and asserts the synergyPet bridge exposes poke/dragBy/setDragging/getState/onState/onSettings/onSprite with the correct IPC channels and payloads, plus subscription disposer behavior. Co-authored-by: synergy-agent <299070056+synergy-agent@users.noreply.github.com>
…poses The pet page drag gesture invoked bridge.moveBy(dx, dy) while the preload bridge exposes dragBy(dx, dy), so every drag threw a TypeError and the window never moved. Add a jsdom-based gesture test that pins the drag path to dragBy and drop the nonexistent moveBy call. Co-authored-by: synergy-agent <299070056+synergy-agent@users.noreply.github.com>
Hovering over the pet window fired pointermove with no pressed button, and the drag threshold was measured against the initial (0,0) origin, so mere hover instantly entered the dragging state and dragBy chased the cursor, making the window oscillate rapidly under the pointer. Track a pressed flag from pointerdown/pointerup and ignore pointermove until a button is actually pressed. Co-authored-by: synergy-agent <299070056+synergy-agent@users.noreply.github.com>
|
CI Test 红不是这个 PR 引入的:根因是 |
Summary
在 Electron shell 中新增桌面宠物(desktop pet)窗口:透明、置顶、不占任务栏,主进程订阅 Synergy 全局事件流(
GET /global/event?stream=delta),将 session 事件映射为 7 态动画状态机(idle / working / happy / celebrate / angry / sleepy / dragging),驱动沙箱化的精灵图渲染器,支持拖拽与点击交互。Changes
packages/desktop/src/pet-window.ts—DesktopPetWindow创建BrowserWindow{ transparent, frame:false, alwaysOnTop, skipTaskbar, sandbox },主进程建立 SSE 连接并推送pet:state/pet:settings/pet:sprite到渲染器;pet.poke/pet.dragBy/pet.setDragging/pet.getStateIPC 均做 sender 校验。packages/desktop/src/pet-state.ts— 纯PetStateMachine:session.updatedbusy → working、session.completion→ celebrate、session.error→ angry、空闲超时 → sleepy、poke → happy、dragging 覆盖;瞬态情绪到期回落。packages/desktop/src/pet-settings.ts— 版本化desktop-pet.json(userData),含enabled/spritePath/width/height/position/idleTimeoutMs/frameMs,缺省回退。packages/desktop/src/pet-sprite.ts— 8:7 精灵图校验(PNG/JPEG/WebP 头部尺寸解析,无完整解码器),转 base64 data URL;无效素材回退 CSS 占位。packages/desktop/src/pet-page.ts+pet-preload.ts— 严格 CSP(default-src 'none')的内联 HTML,canvas 精灵动画 + 拖拽/点击;窄 preload 桥,渲染器不接触服务器 URL/SDK/Node。packages/desktop/src/main.ts—initializePetWindow()(窗口就绪后)、服务器重启setServerUrl、before-quit停止;desktop:build增加pet-preload.cjs产物。test/pet-state / pet-settings / pet-sprite / pet-page / pet-window五个套件;MockElectronWindow补充 position/bounds/alwaysOnTop 等方法。Verification
Notes
docs/research/desktop-pet-proposal.md。docs/decisions/implemented/feature/2026-08-20-desktop-pet-window.md。