Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline (Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/* packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
7.1 KiB
evOo Character — Poses & Runtime Mapping
Brand lock (non-negotiable)
Oo trademark = asymmetric Kulleraugen + size difference
All Oo's — EvooMascot, BotAvatar, Wordmark, every pose — must stay instantly recognizable through:
- Consistent size asymmetry between the two eyes (big O vs small o). Never symmetrize. Never equal radii.
- 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; seebrand-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 |