Add butterfly/parrot/wiggle-dog companions; scale mascot + tips

- Three new Lottie companions: Wiggle Dog (摇尾狗), Butterfly (蝴蝶),
  Parrot (鹦鹉). Parrot is mirrored via a new `flip` flag on Companion,
  threaded through LottiePlayer (scaleX(-1)) since the asset faces left.
- Mascot size now scales with the viewport: --petal-companion-size
  clamp(10rem, 17vw, 20rem); art, emoji fallback, and the sleep "z" all
  derive from it.
- Larger, more legible tip bubble (1.4rem zh / 1.15rem en, wider bubble).
- Bubbles linger longer for a bilingual ESL read: baseline 9s→14s,
  cheer 6s→9s, per-char 45→55ms, cap 20s→32s.

Claude-Session: https://claude.ai/code/session_016Yr6jELuRc7hyzYLccQKZd
This commit is contained in:
prosolis
2026-06-26 11:08:55 -07:00
parent 9d6698dcc6
commit adc0cdff1c
8 changed files with 91 additions and 16 deletions

View File

@@ -11,6 +11,8 @@ interface Props {
animationData?: object
loop?: boolean
className?: string
// Mirror the animation left↔right (for assets drawn facing the wrong way).
flip?: boolean
fallback: React.ReactNode
}
@@ -48,7 +50,7 @@ function fitToContent(anim: AnimationItem, svg: SVGSVGElement) {
// A thin wrapper over lottie-web (framework-agnostic, no React peer deps, fully
// offline). Reloads the animation whenever the data changes — moods swap by
// passing a different `animationData`.
export function LottiePlayer({ animationData, loop = true, className, fallback }: Props) {
export function LottiePlayer({ animationData, loop = true, className, flip, fallback }: Props) {
const ref = useRef<HTMLDivElement>(null)
const anim = useRef<AnimationItem | null>(null)
@@ -74,5 +76,12 @@ export function LottiePlayer({ animationData, loop = true, className, fallback }
}, [animationData, loop])
if (!animationData) return <>{fallback}</>
return <div ref={ref} className={className} aria-hidden />
return (
<div
ref={ref}
className={className}
style={flip ? { transform: 'scaleX(-1)' } : undefined}
aria-hidden
/>
)
}