cmux NEXT Home: Chiefs' avatars move, slow at idle and livelier while working

Merged
TE
2d ago
+541-29

Leo, 2026-11-01: "i also want them animated like how chatgpt animates their dots".

A Chief's own avatar in Home's list and pinned grid now moves: a slow loop at idle, and a quicker, deeper one while the Chief works (its turn is running, InboxRow.isTyping). Conversations without their own avatar, people and the CH monogram rows only move when they are a Chief's.

Versions (gallery)

ChiefAvatarMotionGalleryTests draws each style at idle and while working, one period in eight frames, to Packages/Shared/CmuxMessagesLab/.build/chief-avatar-motion-gallery.png (motion exaggerated 4x so a point reads on the sheet):

StyleIdleWorking
Float (default)bobs ±1 pt, 3.2 s±1.6 pt, 0.9 s
Breathegrows up to 3.5%, 3.6 sa double beat of 7%, 1.1 s (closest to ChatGPT's dot)
Swaytilts ±2.5°, 4.0 s±5°, 1.0 s
Stillnonenone

I picked Float. It reads as "alive" at row size without changing the avatar's outline, and in a dense list it is the least distracting. Switch styles in Debug Settings > Home > Chief avatar motion (home.avatarMotion). Each avatar starts at its own phase (from its conversation id), so a list of Chiefs never moves in lockstep.

Stop conditions

Each one removes the loop and leaves the avatar at rest:

  • Reduce Motion (system, or the cmux pin) and calm mode: Home's existing reduceMotionDidChange path.
  • ui.animationSpeed off, or Home's motion switch off: the host's gate is Motion.home.animatesLoops, re-read on .speed changes.
  • App inactive, or the window not key, minimized, hidden or covered. The vendored list already tracks its window's key state (setWindowActive), and that tracking re-applies every avatar. Applying also reads isVisible, isMiniaturized and occlusionState. No new notification observers were added.
  • Off screen: a row or tile going back to the pool (scrolled away, or the conversation removed) stops its loop in recycle.
  • Style Still.

Performance

  • 0 main-thread work per frame. Each avatar's motion is one CAKeyframeAnimation (48 steps, repeatCount = .infinity) added to its layer and run by the render server. There is no timer, display link or redraw.
  • A reload that keeps the same mode and style keeps the running loop (asserted by test), so a list update adds no animation work.
  • Per configure, the cost is one animation(forKey:) lookup and an equality check, O(1) per visible row.
  • No same-harness before/after frame numbers yet. That is a follow-up on the Home perf harness.

Design

  • No module statics or KVC: the host's gate and style live on the list's controller (CmuxSidebarView.avatarLoopGate and avatarMotionStyle are instance properties that forward to it), and each SidebarRowLayer records what its avatar shows.
  • The vendored edits are marked // cmux:. Patches/ was regenerated by reverse-applying the base patch to recover the pin, then diffing again (no MessagesLab checkout on this host); sync-messageslab.sh --check was not run.

Tests

ChiefAvatarMotionTests (MessagesLab package) covers:

  • idle vs working loops on rows and tiles;
  • no motion without an avatar of its own, or with no motion asked;
  • the same animation object kept across a reload;
  • Reduce Motion, and the host's gate stopping and restarting loops;
  • resigning key stopping them, and the real off-screen check;
  • a pooled row stopping, and Still never starting;
  • every loop closing (first frame equals last frame) and seeds differing in phase.

Observed / Not run

  • Fleet: MessagesLab ChiefAvatarMotionTests, ChiefAvatarMotionGalleryTests, and CmuxNext ChiefHeaderMonogramTests, HomeChatHeaderDefaultTests, PerChiefPetTests at the head (results in a comment below).
  • Not run: a DEV build watching the list live; the fresh-home launch (Chief area) follows in a comment.

No capture: the motion is in the gallery sheet; Leo tries the build.

Changelog

Added: Chiefs' avatars in Home's list move gently, and a little more while the Chief is working.

Agent session: agent session

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Chiefs' avatars in Home's list and pinned grid now move — a slow loop at idle and a livelier one while the Chief works (its turn is running). Each avatar's motion is one CAKeyframeAnimation run by the render server, so there's no main-thread work per frame.

Motion styles

  • Float (default) bobs the avatar ±1 pt at 3.2 s; working is ±1.6 pt at 0.9 s.
  • Breathe grows it up to 3.5%; working is a double beat of 7% (closest to ChatGPT's dot).
  • Sway tilts ±2.5°; working is ±5°.
  • Switch styles in Debug Settings > Home > Chief avatar motion; a style change restyles every avatar at once. Each avatar starts at its own phase (from its conversation id), so a list of Chiefs doesn't move in lockstep.

Stop conditions

  • Reduce Motion, calm mode, ui.animationSpeed off, and Home's motion switch stop the loops.
  • Loops also stop when the app is inactive, the window is not key, minimized, hidden, or off screen, and when a row returns to the pool.
  • A reload that keeps the same avatar keeps its running loop, so list updates add no animation work.
  • Style Still never starts a loop.