Skip to content

feat(web): Room v2 — in-client interactions, living-space upgrade, themes & feminine figure#225

Merged
oratis merged 11 commits into
mainfrom
claude/room-slim
Jul 14, 2026
Merged

feat(web): Room v2 — in-client interactions, living-space upgrade, themes & feminine figure#225
oratis merged 11 commits into
mainfrom
claude/room-slim

Conversation

@oratis

@oratis oratis commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Room v2 — the complete living-space upgrade. Consolidates the reviewed + optimized Room v2 stack (originally split across #221 core, #222 lived-in, #224 themes, #225 figure) into one clean integration onto latest main. The stack was consolidated because main already shipped Room v1 (#214, squash-merged), so the four PRs conflicted when merged individually — this branch merges main in and reconciles it (see below). Design + BSide research + pro/con debate: docs/PLAN_ROOM_v2.0.md.

What's in it

  • In-client interactions (no browser) — every room widget closes its loop inside the WKWebView via a same-origin {type:'lisa-room', action, prefill} bridge → parent GUI (lisaShowView / lisaPrefillComposer); window.open removed. Supersedes v1's lighter room_open_chat listener (removed here). SW shell → network-first (fixes the two-refresh papercut).
  • Presence beat — she looks up and meets your eyes on room-open / focus / hover (BSide's most-praised aliveness moment).
  • Autonomous ambient life — she drifts through at-home activities (read / tea / listen / stretch / window-gaze) on her own when idle, time-of-day weighted. Honesty line held: neutral captions, never fabricated work or mood; any real signal (work / thinking / Reve) wins immediately.
  • The room remembers her day — accumulating ★ letter pile (capped 12), evening pajamas at night, bookshelf → her real current_desire.
  • 换景 themes — a second "sunroom" theme + a ❖ switcher (data-driven .bg, localStorage).
  • Figure — slimmed then given a feminine hourglass (defined waist, fitted hoodie), every pose rebuilt for consistency; sit/sleep switched to contain so nothing stretches.
  • Perf — parallax rAF pauses when the room is hidden.

Verification

Full suite green (914 pass / 0 fail, snapshot refreshed), builds clean, and each behaviour was verified on isolated instances (screenshots in the thread). Deployed to the local backend throughout.

🤖 Generated with Claude Code

oratis and others added 11 commits July 12, 2026 19:21
…/room)

A new immersive scene where the full-body Lisa character "lives", reachable
from the GUI sidebar (⌂ Room) and at GET /room. Unlike a scripted companion
wallpaper, every layer is driven by Lisa's REAL state — not a canned loop.

Character (full-body animatable spritesheet, not a bust):
- lisa-idle.png — 2-frame sheet (eyes open|closed): breathing via CSS transform,
  blink flips the frame via a JS timer.
- lisa-sit.png / lisa-sleep.png — pose sprites, swapped by mood/state
  (working/reading → sit w/ glowing laptop; napping / Reve dreaming → sleep).
- Generated via an anchor → keyframes → chroma-key + foot-anchor pipeline
  (cf. Ludo.ai / chongdashu/ai-game-spritesheets), gemini-2.5-flash-image on
  Vertex; keyed + normalized with PIL. Fixes the earlier floating-bust look.

Scene:
- room-{day,dusk,night}.png — one pixel room, three time-of-day variants
  (image-to-image kept furniture identical); crossfaded by the LOCAL clock.
- Layered DOM/CSS 2.5D diorama (no canvas/WebGL/bundler): backdrop-fill,
  contained square stage foot-anchored to the floor, mood lighting, weather +
  firefly particles, monitor "thinking" glow, dreaming dim + floating Z's,
  a desk "letter" that reads Lisa's ★ while-you-were-away note.

Wiring (reuses existing signals, adds no backend state, read-only projection):
- GET /room serves a self-contained page (mirrors src/web/island.ts).
- Subscribes to /events (mood / chat_start·end / idle_*) + polls
  /api/island/ping (mood, current_desire, unread) — shared with the Island.
- GUI: ⌂ Room nav tab + #viewRoom iframe, lazily loaded on first open.

Verified end-to-end on a real `lisa serve --web`: route, ping (real soul
desire), SSE, the GUI tab → iframe → /room, full-body render, real-clock dusk,
no console errors. State transitions (sit/sleep/thinking/letter/blink/day-night)
verified via faithful SSE replay. Design + build notes in docs/PLAN_ROOM_v1.0.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…sign doc

Room widgets now close their loop INSIDE the client (this WKWebView) and never
spawn a browser window. Root cause of the old browser pop: room.ts called
window.webkit.messageHandlers.island, but the main Lisa.app registers its
bridge as `lisa` (only the Island window registers `island`), so inside the
#viewRoom iframe that handler was undefined and openChat fell back to
window.open('/') → a browser window.

Fix — a 3-tier in-client action bridge (roomAction), window.open removed:
  1. embedded (iframe): postMessage({type:'lisa-room',action}) → parent GUI,
     which now has a same-origin-guarded listener that calls lisaShowView /
     lisaPrefillComposer — all in the same WKWebView.
  2. standalone native window: the webkit bridge (island|lisa).
  3. plain-browser standalone /room: same-tab location.assign (never a new win).

Also fixes the "a GUI update needs two refreshes" papercut: the service worker
served the app shell (/) stale-while-revalidate, defeating the server's
no-store. Switched `/` + manifest to network-first (fresh shell online, cached
only offline) and bumped the SW cache (lisa-v3-vision → lisa-v4-room) so the
stale shell is purged on activate.

Design: docs/PLAN_ROOM_v2.0.md — full current-Room review, deep BSide (Olivia
Lin) research, an animation + life-system alignment plan, the in-client
interaction architecture, and a pro/con debate with verdicts (phased: A
in-client interactions [this] → B presence beat / eye-contact → C activity
library + autonomous ambient life → D room-changes-over-time + memory recall).

Verified end-to-end: clicking "Talk to her" in the Room iframe switches the GUI
to Chat with zero new windows/tabs and window.open never fires.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…fe, memory recall

Builds on Phase A (in-client interactions) toward the BSide-aligned life system
in docs/PLAN_ROOM_v2.0.md — but on the axis BSide left empty (BSide has 3 idle
scenes, no clock/weather; this is state-driven from her real self).

Phase B — presence beat (BSide's most-praised "aliveness" moment, cheap in
pixel art): a lisa-lookup sprite where she looks up and meets your eyes, swapped
in for ~1.6s on room open / window focus / hover-on-her, only while standing
idle (gated out of activities, sit/sleep, and blink).

Phase C — autonomous ambient life: five new full-body pose sprites (read / tea /
listen / stretch, + lookup) generated via the anchor→keyframes→chroma-key
pipeline. An ambient scheduler drifts her through at-home activities every
~22-48s WHEN IDLE (no real work/Reve signal), time-of-day weighted (night →
read/tea/listen, day → stretch/upright rest). Honesty line held (doc §0/§5.6):
captions stay neutral ("reading" / "having some tea" / "listening to music") —
she performs "at home, resting", never fabricated work or a fake mood; any real
signal (work mood / thinking / dreaming) clears the activity immediately.

Phase D-lite — memory recall: the bookshelf is now a clickable hotspot
("object-as-interface", in-client) that surfaces her REAL current_desire in an
"✦ on her mind" card. No fabrication — reads the same soul desire as the ticker.

Verified on an isolated instance: lookup renders composited; the scheduler
autonomously picked "listen" (headphones sprite + "listening to music" caption);
the shelf card shows the real desire. Deployed to :5757.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… letter pile)

Phase D "lived-in" — the room visibly changes over her day, the axis BSide's
fixed room leaves empty:

- Window-gaze ambient activity: a lisa-window sprite (she stands daydreaming,
  gazing off) joins the autonomous rotation, weighted toward day/dusk.
- Evening pajamas: a lisa-pajamas sprite; at NIGHT her plain standing idle
  changes into cozy pajamas (activities keep the hoodie), blink gated off for
  the single frame. A small "she changed for bed" beat.
- Letter pile: multiple ★ while-you-were-away notes now accumulate on the desk
  (a count badge when >1); opening reads them all newest-first; dismiss clears
  the pile. Seeded from the server's latest unread note, then live idle_message
  events stack on top within the session.

Honesty line held (docs/PLAN_ROOM_v2.0.md §0/§5.6): window-gaze is a neutral
at-home activity; pajamas is time-of-day, not a claim about work or mood.

Verified on an isolated instance: pajamas + the "3" letter badge + the
window-gaze pose all composite cleanly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase E themes from docs/PLAN_ROOM_v2.0.md — the room is now re-decoratable,
another axis BSide's single fixed room leaves empty.

- A second full room theme, "sunroom" (room2-{day,dusk,night}.png): a warm
  terracotta + sage plant-filled loft with a rattan hanging egg-chair and an
  arched glass-door window — generated via the same base→relight pipeline that
  keeps the three time-of-day variants furniture-identical. Same center-open
  staging as the study room, so Lisa composites onto its rug unchanged.
- Theme is data-driven: the .bg layer images are now built in JS (renderBg from
  a theme prefix) instead of hardcoded CSS, so adding a theme = drop in three
  images + one array entry.
- A ❖ topbar chip cycles the theme; the choice persists in localStorage.

Per docs §4/5 verdicts, a full outfit wardrobe and ambient sound are declined
(art cost / no clean asset path); object-level room traces (mugs/book stacks)
are deferred — "letter pile + evening pajamas" already carry "her day".

Verified on an isolated instance: ❖ swaps the study for the sunroom, Lisa's
feet land on the new rug, and the choice is stored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Feedback: she read a bit chubby — the baggy oversized hoodie + loose joggers
gave a rounded silhouette. Regenerated the whole sprite set from a slimmer
anchor: a slender build with a fitted (not oversized) circuit hoodie and slim
tapered pants, same cyan bob / face / hoodie identity. Every pose redone for
consistency — idle (open+closed eyes), look-up, sit, sleep, read, tea, stretch,
listen, window-gaze, pajamas.

The sleep pose is now a proper wide lying-down composition, and sit/sleep switch
to `background-size: contain` + bottom-anchor so no pose is ever stretched wider
than its art (which had also been padding the silhouette).

Verified on an isolated instance: idle / stretch / window / read / sleep all
render slim with no distortion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ed old sprites

Room sprites keep the same filenames, and the SW serves /assets/* cache-first,
so without a cache bump clients would keep the old (chubby) sprites. Bumping the
cache name purges them on activate; the slim sprites are re-fetched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…glass)

Feedback: give her body some feminine appeal — wider top + hips, narrow waist.
Rebuilt every pose from a feminine anchor: a slender HOURGLASS silhouette with a
clearly defined narrow waist and soft curves, the circuit hoodie now FITTED and
gently cinched at the waist (kept full-length / wholesome — cozy homebody, not a
bare-midriff crop) over slim high-waisted pants. Same cyan bob / face / hoodie
identity. All poses redone for consistency (idle, look-up, sit, sleep, read,
tea, stretch, listen, window-gaze, pajamas).

SW cache bumped (lisa-v5-slim → lisa-v6-fem) so the cache-first /assets don't
mask the new sprites.

Verified: the standing idle composites with a clear feminine waistline, no
distortion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Review pass on the Room v2 animation loops: the parallax requestAnimationFrame
kept writing layout every frame even while the room ran as a hidden iframe
(another GUI view) or backgrounded. Early-return when document.hidden. Also cap
the accumulated ★ letter array at 12 so a long session can't grow it unbounded.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
# Conflicts:
#	src/web/assets/room/lisa-idle.png
#	src/web/assets/room/lisa-sit.png
#	src/web/assets/room/lisa-sleep.png
#	src/web/room.ts
…esh MAIN_HTML snapshot

Integrating main (which shipped Room v1 #214 with a room_open_chat message
bridge) with Room v2 (which replaced it with the richer, same-origin-guarded
{type:'lisa-room', action, prefill} protocol + window.open removed). Kept the v2
bridge, removed the now-dead room_open_chat listener, and refreshed the
lisa-html snapshot hash to match. Full suite green (914 pass / 0 fail).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@oratis oratis changed the title fix(web): Room — slimmer Lisa (slender rebuild of every pose) feat(web): Room v2 — in-client interactions, living-space upgrade, themes & feminine figure Jul 14, 2026
@oratis oratis changed the base branch from claude/room-themes to main July 14, 2026 04:33
@oratis oratis merged commit 9d60593 into main Jul 14, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant