evOo-local/packages/ui-web/CHARACTER.md
cynarAI 5ae8f15931 feat(web): Ingo UX — Proaktivität, Oo-Gesicht, dezentes Audio (#76)
* feat(web): Ingo UX — proactivity signals, Oo face richness, soft audio

Priority S: Heute Brief buckets (Needs you ≤3 / Doing / Done), roster doing-verbs
and Braucht-dich chips, sidebar badge on Heute, need-you ping on waiting states.

Priority A/B: Web Audio cues (app-start shimmer, interact tick, success-resolve,
need-you ouh), reaction vocals with cooldown, optional eOo cold-open voice mark.
Oo eyes gain iris/specular detail and asymmetric blink; hands reduced to rest
except punctuation poses; speaking mouth animates.

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* fix(web): biome format and unused import in HeuteBrief

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* fix(web): a11y and import order for Heute badge

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* docs+motion: Motion God craft — pose stagger, audio bus, ducking

Add MOTION-EVOO-CHARACTER.md (Brand Soul brief): eyes lead, mouth ~80%,
hands punctuation, 40–80 ms eye→mouth lag, 220–320 ms springs, SFX LUFS
and duck-under-TTS/PTT, nod mh/Mhh, reduced-motion rules.

Implement motion-tokens, staggered mouthPose in EvooMascot, shared
oo-audio-bus with voice-lane ducking, mint→gold success, hm nod vocal.

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* fix(web): align conversation roster with bot-only sidebar groups after rebase

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
2026-09-22 14:39:40 +02:00

7.2 KiB

evOo Character — Poses & Runtime Mapping

Motion timing, SFX levels, and expressor hierarchy: MOTION-EVOO-CHARACTER.md (Brand Soul / Motion God).

Brand lock (non-negotiable)

Oo trademark = asymmetric Kulleraugen + size difference

All Oo's — EvooMascot, BotAvatar, Wordmark, every pose — must stay instantly recognizable through:

  1. Consistent size asymmetry between the two eyes (big O vs small o). Never symmetrize. Never equal radii.
  2. Kulleraugen — big, round, glossy googly/cute sclera with dark pupils and catchlights.

Personality (mouth, hands, accessories, poses) builds on top of this signature. Never replace with Grok-bot-style emoji faces.

Constant Value Role
EVOO_EYE_BIG_R 30 Capital O sclera
EVOO_EYE_SMALL_R 18.54 Lowercase o sclera (~φ⁻¹ of big)
EVOO_EYE_ASYMMETRY_MIN_RATIO 1.45 CI guard — catches accidental symmetrization

Enforced in evoo-character/brand-lock.ts + brand-lock.test.ts. Poses may adjust eyeOpen (squint), gaze, brows — not eye size parity.


The evOo mascot is not a generic emoji face. Identity comes from the Oo geometry (large O + small o Kulleraugen, capsule-glass hands) shared by EvooMascot and BotAvatar.

Architecture

Runtime signal (status / voice phase / explicit pose)
  → resolveEvooPose / mapVoicePhaseToPose  (evoo-character/pose-map.ts)
    → EvooMascot → EvooCharacterSvg (eyes + mouth + hands + accessory)
BotAvatar = persona shape silhouette + EvooMascot overlay (same soul)
Wordmark = calm EvooMascot + text, trackPointer="none"

Legacy pose names (typing, wave, peek, profit) normalize to canonical states via normalizeEvooPose.

Pose library

Pose Eyes / mouth / hands Accessory Typical trigger
idle soft smile, rest hands — Default; sidebar Wordmark
listening tiny-o mouth, attentive gaze headset Voice phase listening; status waiting_input
speaking speaking mouth, conductor hands — Voice phase speaking; streaming text
thinking flat mouth, upward gaze thought bubble Voice phase thinking
working flat mouth, typing hands — Status running / queued; live message bubble
reading tiny-o, clipboard hold clipboard Status booting; onboarding („liest sich ein“); curator persona idle
analyzing narrowed eyes, magnifier magnifier CTO / quant persona idle
skeptical raised brow, side gaze — Manual / future tool-review moments
curious wide eyes, peek hands — Builder persona idle; legacy peek
delighted grin, sparkle pupil spark Delight / achievement beats
concerned worried brow — Life-inbox persona idle
waiting half-lidded — Work-inbox persona idle
success grin + sparkle, nod hands — Click delight; legacy profit
greeting grin, wave hand — Empty thread hero; legacy wave
asleep closed lids zzz Mascot idle timeout (pointer tracking modes)
error worried mouth spark Status error / failed
collaborating soft smile, dual hands tool Voice awareness (subagent / handoff)

Runtime maps

mapVoicePhaseToPose(phase, collaborating?)

Used by VoiceStage → BotAvatar.pose via personaMascotPose.

Input Pose
listening listening
speaking speaking
thinking thinking (or collaborating when awareness event active)

mapBotStatusToPose(status, streaming?)

Used by BotAvatar when no explicit pose prop.

Status Pose
running, queued, leased working
waiting_input listening
error, failed error
booting reading
idle / unknown idle
streaming flag working

resolveEvooPose({ pose, status, streaming, voicePhase, collaborating })

Priority: voicePhase → explicit pose → streaming → status → idle.

Shell / chrome call sites

Surface Pose source
Oo Stage (1:1 text hero) personaTextPose(persona, resolveTextStagePhase(...), collaborating)
Oo Group Stage (multi-Oo) Active speaker → full hero; ensemble row for the rest (working pulse only — same chrome as 1:1)
Empty thread (legacy / non-encounter) greeting (interactive, hover tracking)
Encounter transcript trail Caption labels only — identity lives on Oo Stage
Live streaming message header working (hidden in encounter mode)
Sidebar Wordmark idle (click cycles greeting → success)
Bot picker / command palette mapBotStatusToPose(status)
Voice Stage hero personaMascotPose(persona, phase, collaborating)

Gesamterfahrung

Stage + transcript-as-trace + sound language form one coherent encounter. Poses on the stage reflect runtime status (working, reading, listening, …); the transcript is the audit trail, not the hero.

No Wunderwuzzi

Every Oo is a specialist. Persona copy and poses assume a defined lane; unmatched bots use the specialist archetype (not a generic companion). Crew collaboration happens in Gruppenunterhaltungen, not one omnibus agent. The ensemble should feel stronger than any single Oo — emergent capability from differences (docs/PRODUCT.md). Group chats need fun factor — friction and banter when useful, never sterile committee tone (docs/GROUP-CONVERSATIONS.md).

Personalization (persona selection)

Lifestyle-agnostic: the same archetype system serves any user. Onboarding infers help & vibe (geek-peer vs quiet-doer) and suggested lanes — spawn/select Oos to match, never a single default personality. Voice copy for specialist models both modes: depth when wanted, execution without lecturing. Details: docs/PERSONA-SELECTION.md.

Accessibility of ambition: skill floor = grandma-simple entry; skill ceiling = expert-grade per specialty (not mediocre-at-everything Wunderwuzzi). Shell and onboarding stay plain; specialists carry domain depth.

Persona idle layer

mapPersonaArchetypeToIdlePose sets the fallback when voice is not in an active phase — e.g. engineer → working, curator → reading.

Design constraints

  • Brand lock — EVOO_OO_TRADEMARK; asymmetric Kulleraugen always; see brand-lock.ts.
  • No Grok-style emoji ripoff — ownable Oo eyes, glass capsule hands, soft motion (evoo-breathe-awake, evoo-wave-harmonic).
  • Wordmark stays pointer-calm (trackPointer="none") for chrome.
  • Design tokens from the shared ui-web palette; persona tint only on Voice bloom, not eye geometry.

Files

Path Role
evoo-character/brand-lock.ts Trademark constants + asymmetry guard
evoo-character/types.ts EvooPose, accessories, mouth styles
evoo-character/poses.ts POSE_DEFINITIONS per pose
evoo-character/EvooCharacterSvg.tsx SVG layers (eyes, mouth, hands, accessories)
evoo-character/pose-map.ts Runtime mappers + resolveEvooPose
evoo-brand.tsx EvooMascot, Wordmark, blink/saccade life
bot-avatar.tsx Shape + mascot composite