|
| 1 | +# 0002 — The Brain becomes the Chat's living background; components float on legibility-floored glass |
| 2 | + |
| 3 | +- Status: Proposed |
| 4 | +- Date: 2026-07-24 |
| 5 | +- Deciders: Kate Bonner (Head of Product) |
| 6 | +- Tracking: harmoniqs/opencode#56 |
| 7 | + |
| 8 | +## Context |
| 9 | + |
| 10 | +The amico **Brain** — the data-true, live map of a session's thought — currently renders |
| 11 | +as an inline strip *inside* the message timeline (a native canvas engine; the `/brain.html` |
| 12 | +iframe was already retired). It is one row among many, easy to scroll past, and it competes |
| 13 | +with the messages rather than framing them. |
| 14 | + |
| 15 | +An earlier redesign (`kate/chat-brain-atmosphere`, six commits, never pushed or merged) |
| 16 | +explored promoting the Brain to a chat-wide background behind *tiered per-bubble glass* |
| 17 | +("atmosphere"), reaching a resolved design and a Phase-5 build. Kate reopened the design |
| 18 | +fresh rather than continuing that branch. The open question: does the Brain stay an inline |
| 19 | +timeline element, or become the **permanent background of the Chat** with every component |
| 20 | +floating on frosted glass — and if so, how is legibility *guaranteed* when text sits on a |
| 21 | +translucent surface over a moving graph? "Movement comes through" (translucency) and |
| 22 | +"highly legible" (contrast) are in direct tension, and the earlier build hit exactly one |
| 23 | +legibility failure: muted grey text not clearing WCAG-AA on its most translucent tier. |
| 24 | + |
| 25 | +This decision governs the **Amicode app UI** (the opencode fork). It sits under ADR 0001 |
| 26 | +(chat-first shell): the Chat is the one persistent hub, and this ADR settles what the Chat |
| 27 | +*looks like*. |
| 28 | + |
| 29 | +## Decision |
| 30 | + |
| 31 | +**The Brain becomes the permanent, full-bleed background of the Chat** — Landing, |
| 32 | +conversation, and composer all sit on it. There is **one Brain**: the inline timeline strip |
| 33 | +is absorbed into the background (one render loop, promoted from a timeline row to the room). |
| 34 | +The **Rail, titlebar, and Panels stay solid**, framing the living pane. The Brain is a fixed, |
| 35 | +viewport-anchored backdrop; cards scroll over a stationary Brain. |
| 36 | + |
| 37 | +**Every chat component floats as a frosted-glass card, and legibility is guaranteed by |
| 38 | +construction — not by eye:** |
| 39 | + |
| 40 | +- **Blur** — a *high, constant* gaussian blur on every card. It provides perceptual calm and |
| 41 | + kills the high-frequency motion detail that hurts reading. It is cheap (no per-frame logic) |
| 42 | + and it is *not* what makes text legible. |
| 43 | +- **Tint** — contrast comes entirely from a tint whose **opacity is pinned to clear the WCAG-AA |
| 44 | + floor (with a small safety margin, not exactly at the floor) over a fixed reference frame** — |
| 45 | + a synthetic Brain at peak bloom in which the sampled backdrop is the brightest palette value |
| 46 | + **the Brain actually paints in that theme** (peak `#fff676` on dark; peak `#8f8000` on light — |
| 47 | + the light-mode Brain never paints `#fff676`, so validating against it there would derive a |
| 48 | + near-transparent tint that silently fails over the real light frame). Blur smooths the backdrop but does not darken it; a card over a bright-yellow |
| 49 | + cluster is still on bright yellow until the tint pulls the composite toward the ink. The tint |
| 50 | + set is **keyed to the chat's own theme** — the chat runs a theme independent of the app shell |
| 51 | + (default oc-2) — so a chat-theme change re-keys the Glass and re-validates contrast. **Two |
| 52 | + tints:** *standard* (prose, bubbles, composer) holds body text ≥4.5:1; *dense* (code, diffs, |
| 53 | + run-plots) is more opaque and holds code/diff text ≥4.5:1 and graphical marks — syntax colors, |
| 54 | + diff fills, plot lines — ≥3:1 (WCAG 1.4.11), so colored content stays crisp. |
| 55 | + |
| 56 | +**The Brain is data-true with an adaptive heartbeat.** Nodes light up as amico reads files, |
| 57 | +runs tools, and touches the vault; the graph grows with the conversation, **densifying in place |
| 58 | +within the fixed, viewport-anchored frame** — bounded by a node cap / recency window so a long |
| 59 | +session neither scrolls the Brain nor grows its per-frame cost without limit. Motion follows |
| 60 | +activity: a lively tempo when amico is working, ~8fps breathing at rest, and a **hard pause** |
| 61 | +when the window is hidden or `prefers-reduced-motion` is set. At rest it shows a **sparse |
| 62 | +seed** — a near-empty Landing whose graph grows *only* from the current session's real |
| 63 | +thought, with **no cross-session persistence** (a fresh session opens nearly empty). One Brain |
| 64 | +per window shows the **active** session; switching the titlebar session tab swaps the Brain to |
| 65 | +that session's graph. |
| 66 | + |
| 67 | +**Pre-agreed perf fallback (non-negotiable mid-build):** the frosted look is protected. At |
| 68 | +rest, full blur and full motion always apply. A frame-time monitor trips the ease when p95 |
| 69 | +frame time stays over budget for a sustained window (with hysteresis before restoring, to |
| 70 | +avoid oscillation): the Brain's *motion* eases (calm/slow) and returns to full tempo at rest. |
| 71 | +If easing motion to a full stop still misses budget, the Brain **hard-pauses to a static |
| 72 | +blurred field** — the terminal valve — but **blur fidelity never degrades.** |
| 73 | + |
| 74 | +**Build by reuse.** A new, well-named branch off `local/amicode` reuses the de-risked native |
| 75 | +plumbing — the native Brain engine, the glass-token generator, and the mount wiring from the |
| 76 | +earlier branch (design-neutral, already hardened by an adversarial port-fidelity review) — and |
| 77 | +builds the *design* layer fresh. No iframe. Both light and dark themes are first-class from day |
| 78 | +one. |
| 79 | + |
| 80 | +See `packages/app/CONTEXT.md` for the new vocabulary (**Brain**, **Glass**). |
| 81 | + |
| 82 | +## Consequences |
| 83 | + |
| 84 | +- The **inline brain strip** is retired as a timeline row; the timeline keeps only its text |
| 85 | + shimmer (the existing working indicator — the cycling gerund + live elapsed shown while a turn |
| 86 | + works). One Brain, one render loop. |
| 87 | +- **No node-level interactivity is lost.** The strip's only affordance was a click-to-expand/ |
| 88 | + collapse toggle (it was a collapsible row); the Brain engine has no node hit-testing. As an |
| 89 | + always-present background the expand toggle is obviated, not removed from a capable surface. |
| 90 | +- When a solid **Panel** covers the Chat, the Brain need not animate behind the occluded area — |
| 91 | + a cheap perf saving, since Panels are opaque. |
| 92 | +- The *standard* tint's opacity is **bounded below** by the contrast floor — it cannot be made |
| 93 | + ultra-transparent, or muted grey text fails AA over the reference frame (the exact |
| 94 | + failure the earlier build hit). Secondary/muted text therefore rides the *dense* tint or a |
| 95 | + locally-dimmed zone. |
| 96 | +- The Chat's cost profile changes: high-radius backdrop-blur recomputed per frame over an |
| 97 | + animating canvas, across many cards, while scrolling, is the expensive combination. It must |
| 98 | + pass a **laptop-webview 60fps gate** (the real target — headless Chromium is only a proxy); |
| 99 | + the motion-ease fallback is the release valve. |
| 100 | +- The **Rail, titlebar, and Panels are deliberately excluded.** Nav chrome stays rock-solid, |
| 101 | + and dense Panel lists (Chats history, Run gallery) don't fight a moving background. |
| 102 | +- The Landing (a fresh, empty Chat per ADR 0001) now opens on the sparse-seed Brain rather |
| 103 | + than a blank surface — the window's first impression is the living background, calm and |
| 104 | + nearly empty, not a widget wall. |
| 105 | + |
| 106 | +## Alternatives considered |
| 107 | + |
| 108 | +- **Keep the inline brain strip** (a timeline row). Rejected: it is one element among many; |
| 109 | + the redesign's intent is the Brain as the room, not a row. |
| 110 | +- **One calm reading lane / unified glass sheet** (Brain only in the margins). Rejected in |
| 111 | + favor of per-component floating cards — motion lives in the gaps between cards, which is |
| 112 | + where "movement comes through" *without* the cards themselves having to be see-through to it. |
| 113 | +- **Dynamic contrast glass** (each card samples its backdrop per frame and adapts its tint). |
| 114 | + Rejected: per-frame readback is the biggest perf risk and AA is hard to prove against every |
| 115 | + frame; the static AA-floor tint is deterministic and cheap. |
| 116 | +- **Quiet zone behind cards** (dim/slow the Brain under each card's footprint via per-card |
| 117 | + masking). Rejected: real engine complexity for translucency the gaps already provide. |
| 118 | +- **Breathing skeleton atlas at rest** (the full latent graph, dim, breathing). Rejected in |
| 119 | + favor of the sparse seed — calmer, more literal, and it lets the floating cards dominate. |
| 120 | +- **Protect motion and ease blur under load**, or **degrade only while scrolling**. Rejected: |
| 121 | + the frosted look is the aesthetic being protected; motion is the acceptable give under load. |
| 122 | +- **Rebuild the engine clean**, or **evolve the old `kate/chat-brain-atmosphere` branch**. |
| 123 | + Rejected: reuse the de-risked plumbing (including its already-fixed port defects), but build |
| 124 | + the design fresh on a new branch rather than inherit the old design structure or re-pay for |
| 125 | + the engine port. |
0 commit comments