hermes(voice): keep one consistent ellipsis on conversation status labels

The overlay status caption already carries a static ellipsis (e.g. 'Thinking…'),
and after ~2.5s of silent thinking the 'working' affordance added an animated
dots ::after on top of it — so the label intermittently rendered as 'Thinking……'
only on longer turns. Drop the animated text dots and keep the orb-halo shimmer
as the liveness cue, so the ellipsis stays a single, consistent static one.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BvMSXH8VH2tMWXanb8SJdf
This commit is contained in:
jenkins 2026-08-25 01:18:23 -03:00
parent 9f49a1ba2c
commit 5b1f832072
2 changed files with 12 additions and 22 deletions

View File

@ -809,38 +809,21 @@
filter: saturate(0.35) brightness(0.8);
}
/* FIX 4: "working" affordance. Armed by atlas-voice.js (.is-awaiting) only
/* FIX 4: "working" affordance. Armed by atlas-voice.js (.is-awaiting) only
after the model has been Thinking a beat with no reply text yet, so dead time
reads as alive. A slow orb shimmer plus animated dots trailing the state
caption cheap, CSS-only, and fully suppressed under reduced motion by the
blanket rule below. */
reads as alive. A slow orb shimmer only the state caption keeps its single
static ellipsis (e.g. "Thinking…") and is never trailed by animated dots, so
the ellipsis stays consistent instead of doubling up. Cheap, CSS-only, and
fully suppressed under reduced motion by the blanket rule below. */
.voice-conversation.is-awaiting .voice-conversation-orb-halo {
animation: voice-conversation-await-shimmer 2.6s ease-in-out infinite;
}
.voice-conversation.is-awaiting .voice-conversation-state::after {
content: "";
display: inline-block;
width: 1.4em;
margin-left: 0.15em;
text-align: left;
vertical-align: bottom;
animation: voice-conversation-await-dots 1.4s steps(4, end) infinite;
}
@keyframes voice-conversation-await-shimmer {
0%, 100% { opacity: 0.6; filter: blur(10px); }
50% { opacity: 0.92; filter: blur(13px); }
}
@keyframes voice-conversation-await-dots {
0% { content: ""; }
25% { content: "·"; }
50% { content: "··"; }
75% { content: "···"; }
100% { content: ""; }
}
@keyframes voice-conversation-breathe {
0%, 100% { opacity: 0.55; transform: scale(calc(0.96 + (var(--conversation-level) * 0.3))); }
50% { opacity: 0.85; transform: scale(calc(1.05 + (var(--conversation-level) * 0.3))); }

View File

@ -629,6 +629,13 @@ def test_conversation_thinking_working_affordance_contract():
# The affordance disarms the instant any reply text arrives.
assert "if(text&&String(text).trim()) clearAwaitingAffordance();" in source
assert ".voice-conversation.is-awaiting" in css
# The affordance is an orb shimmer only: the state caption keeps its single
# static ellipsis and is never trailed by animated dots, so "Thinking…" can
# never render as "Thinking……". (Consistency fix.)
assert "voice-conversation-await-shimmer" in css
assert "voice-conversation-await-dots" not in css
assert ".voice-conversation-state::after" not in css
assert "thinking:'Thinking…'" in source and "transcribing:'Transcribing…'" in source
# Reduced motion suppresses every overlay animation, including this one.
rm_block = css.split("@media (prefers-reduced-motion: reduce)", 1)[1]
assert "animation: none !important" in rm_block