From d429256395a810f3b0d0c6906aa44acbdc72aa5d Mon Sep 17 00:00:00 2001 From: dj-oyu <68707227+dj-oyu@users.noreply.github.com> Date: Wed, 25 Feb 2026 12:12:21 +0900 Subject: [PATCH] feat(miniapp): pigeon activity reflects heartbeat session state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit idle: frame pinned to 0 (no bob), flip every ~2.8s — sitting still waiting: bob at 380ms/frame, flip every ~600ms — gentle activity toolcall: bob at 130ms/frame, flip every ~280ms — visibly busy Co-Authored-By: Claude Sonnet 4.6 --- pkg/miniapp/static/index.html | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/pkg/miniapp/static/index.html b/pkg/miniapp/static/index.html index b6af8616c..410daa7db 100644 --- a/pkg/miniapp/static/index.html +++ b/pkg/miniapp/static/index.html @@ -1920,22 +1920,33 @@ function _orchConverse(fromId, toId, text) { function _orchUpdate(dt) { _orchAllChars().forEach(function(c){ if (!c.alive && c.state!=='entering') return; - c.frameTimer+=dt; - var dur=_orchFRAME_MS[c.state]||450; - if (c.frameTimer>=dur){ c.frame=(c.frame+1)%4; c.frameTimer-=dur; } + // Frame animation (bob): pigeon uses state-specific timing instead of shared table. + if (c === _orchHeartbeat) { + if (c.state === 'idle') { + c.frame = 0; // pin still — no bob when inactive + } else { + c.frameTimer += dt; + var pDur = c.state==='toolcall' ? 130 : 380; + if (c.frameTimer >= pDur) { c.frame=(c.frame+1)%4; c.frameTimer-=pDur; } + } + } else { + c.frameTimer+=dt; + var dur=_orchFRAME_MS[c.state]||450; + if (c.frameTimer>=dur){ c.frame=(c.frame+1)%4; c.frameTimer-=dur; } + } if (c.target){ var dx=c.target.x-c.x, dy=c.target.y-c.y, dist=Math.sqrt(dx*dx+dy*dy); if (dist>1.5){ var spd=_orchWALK*dt/1000; c.x+=dx/dist*spd; c.y+=dy/dist*spd; } else { c.x=c.target.x; c.y=c.target.y; c.target=null; if(c._onArrive){c._onArrive();c._onArrive=null;} } } if (c.bubble){ c.bubble.ttl-=dt; if(c.bubble.ttl<=0) c.bubble=null; } - // Heartbeat pigeon: face movement direction; flip periodically when stationary. + // Heartbeat pigeon: direction flip rate reflects activity level. if (c === _orchHeartbeat) { if (c.target) { var pdx = c.target.x - c.x; if (Math.abs(pdx) > 1) c.facing = pdx > 0 ? 1 : -1; } else { - var flipRate = c.state==='toolcall' ? 280 : c.state==='waiting' ? 480 : 720; + var flipRate = c.state==='toolcall' ? 280 : c.state==='waiting' ? 600 : 2800; c.flipTimer += dt; if (c.flipTimer >= flipRate) { c.flipTimer -= flipRate; c.facing = -c.facing; } }