mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-17 02:05:57 +00:00
feat(pets): gateway pet.scale RPC + per-state frames; TUI live rescale
pet.info now ships framesByState; pet.cells returns the active scale. New pet.scale RPC persists display.pet.scale for the desktop slider. The TUI busts its frame cache when the scale changes on its existing poll — no new polling.
This commit is contained in:
parent
52134078e3
commit
eceb91cffd
2 changed files with 45 additions and 1 deletions
|
|
@ -4993,6 +4993,20 @@ def _(rid, params: dict) -> dict:
|
|||
return _ok(rid, usage)
|
||||
|
||||
|
||||
def _pet_frame_counts(spritesheet) -> dict:
|
||||
"""Real (padding-trimmed) frame count per state, for the desktop canvas.
|
||||
|
||||
Fail-open: a decode hiccup returns ``{}`` and the canvas falls back to its
|
||||
static ``framesPerState`` rather than breaking the (cosmetic) pet.
|
||||
"""
|
||||
try:
|
||||
from agent.pet import render
|
||||
|
||||
return render.state_frame_counts(str(spritesheet))
|
||||
except Exception: # noqa: BLE001 - cosmetic, never break the surface
|
||||
return {}
|
||||
|
||||
|
||||
@method("pet.info")
|
||||
def _(rid, params: dict) -> dict:
|
||||
"""Return the active petdex pet for surfaces that render sprites.
|
||||
|
|
@ -5041,6 +5055,7 @@ def _(rid, params: dict) -> dict:
|
|||
"frameW": constants.FRAME_W,
|
||||
"frameH": constants.FRAME_H,
|
||||
"framesPerState": constants.FRAMES_PER_STATE,
|
||||
"framesByState": _pet_frame_counts(pet.spritesheet),
|
||||
"loopMs": constants.LOOP_MS,
|
||||
"scale": float(pet_cfg.get("scale", constants.DEFAULT_SCALE) or constants.DEFAULT_SCALE),
|
||||
"stateRows": list(constants.STATE_ROWS),
|
||||
|
|
@ -5119,6 +5134,7 @@ def _(rid, params: dict) -> dict:
|
|||
"placeholder": payload["placeholder"],
|
||||
"frames": payload["frames"],
|
||||
"frameMs": constants.LOOP_MS / max(1, kcount),
|
||||
"scale": scale,
|
||||
},
|
||||
)
|
||||
|
||||
|
|
@ -5146,6 +5162,7 @@ def _(rid, params: dict) -> dict:
|
|||
"cols": cols,
|
||||
"frameMs": constants.LOOP_MS / max(1, count),
|
||||
"frames": frames,
|
||||
"scale": scale,
|
||||
},
|
||||
)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
|
|
@ -5320,6 +5337,26 @@ def _(rid, params: dict) -> dict:
|
|||
return _err(rid, 5031, f"pet.disable failed: {exc}")
|
||||
|
||||
|
||||
@method("pet.scale")
|
||||
def _(rid, params: dict) -> dict:
|
||||
"""Persist ``display.pet.scale`` from the desktop slider. Params: ``scale``.
|
||||
|
||||
Clamped to the engine bounds. The renderer updates its own ``$petInfo`` for
|
||||
instant feedback; this just makes the change durable + visible to the other
|
||||
terminal surfaces on their next read.
|
||||
"""
|
||||
try:
|
||||
from hermes_cli.pets import set_pet_scale
|
||||
|
||||
scale, err = set_pet_scale(params.get("scale"))
|
||||
if err:
|
||||
return _err(rid, 4004, err)
|
||||
return _ok(rid, {"ok": True, "scale": scale})
|
||||
except Exception as exc: # noqa: BLE001
|
||||
logger.debug("pet.scale failed: %s", exc)
|
||||
return _err(rid, 5031, f"pet.scale failed: {exc}")
|
||||
|
||||
|
||||
@method("credits.view")
|
||||
def _(rid, params: dict) -> dict:
|
||||
"""Structured Nous credit view for the TUI /credits command.
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ interface PetCellsResult {
|
|||
graphics?: string
|
||||
imageId?: number
|
||||
placeholder?: string[]
|
||||
scale?: number
|
||||
slug?: string
|
||||
state?: string
|
||||
}
|
||||
|
|
@ -98,6 +99,7 @@ export function usePet(): PetRender {
|
|||
|
||||
const cache = useRef<Map<string, CacheEntry>>(new Map())
|
||||
const slugRef = useRef('')
|
||||
const scaleRef = useRef(0)
|
||||
const imageIdRef = useRef(0)
|
||||
const stateRef = useRef<PetState>('idle')
|
||||
const frameRef = useRef(0)
|
||||
|
|
@ -185,10 +187,15 @@ export function usePet(): PetRender {
|
|||
}
|
||||
|
||||
const slug = res.slug ?? ''
|
||||
const scale = res.scale ?? 0
|
||||
|
||||
if (slug !== slugRef.current) {
|
||||
// A switch OR a live `/pet scale` change invalidates the cached frames
|
||||
// (they're rendered at the old size), so the steady poll repaints at the
|
||||
// new scale without a restart.
|
||||
if (slug !== slugRef.current || (scale > 0 && scale !== scaleRef.current)) {
|
||||
releaseKitty()
|
||||
slugRef.current = slug
|
||||
scaleRef.current = scale
|
||||
cache.current.clear()
|
||||
frameRef.current = 0
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue