Skip to content
Merged
26 changes: 15 additions & 11 deletions packages/app-bundle/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"fork_sha": "d161eb0cfc6d03a53311e083b590005b4161c13a",
"upstream_base": "v1.18.29",
"upstream_base_sha": "16747470f976aca3d362ad730bcd3fe82ecc2c9a",
"extracted_at": "2026-09-10T07:01:02.938Z",
"extracted_at": "2026-09-10T07:35:41.469Z",
"per_package": {
"packages/app": {
"A": 146,
Expand Down Expand Up @@ -34,13 +34,13 @@
"D": 0
},
"packages/ui": {
"A": 153,
"A": 155,
"M": 62,
"D": 0
}
},
"counts": {
"overlay_total": 615,
"overlay_total": 617,
"deletions": 7,
"server_coupled": 47
},
Expand Down Expand Up @@ -639,6 +639,7 @@
"packages/ui/src/amicode/context-tree-engine.test.ts": "A",
"packages/ui/src/amicode/context-tree-engine.ts": "A",
"packages/ui/src/amicode/device-view.tsx": "A",
"packages/ui/src/amicode/docket.ts": "A",
"packages/ui/src/amicode/edit-row.test.ts": "A",
"packages/ui/src/amicode/edit-row.ts": "A",
"packages/ui/src/amicode/entity-rail.tsx": "A",
Expand Down Expand Up @@ -721,6 +722,7 @@
"packages/ui/src/components/amicode-bridge.tsx": "A",
"packages/ui/src/components/amicode-card.tsx": "A",
"packages/ui/src/components/amicode-connections-tab.tsx": "A",
"packages/ui/src/components/amicode-docket.tsx": "A",
"packages/ui/src/components/amicode-edit-row.tsx": "A",
"packages/ui/src/components/amicode-entity-rail.tsx": "A",
"packages/ui/src/components/amicode-entity-view.tsx": "A",
Expand Down Expand Up @@ -973,7 +975,7 @@
"packages/app/public/amico.svg": "a14b9d543d895bcdf0758f7b9ef5908ee0acaac794446494af059b159247db8f",
"packages/app/public/oc-theme-preload.js": "27227e802b3494e7c545da903e679efdb30ccc754cd4eb5cdf08005a40d560b6",
"packages/app/src/app.tsx": "a72e7cef35d5de80980fbb1fc26c14d8551d1677821e72798c624842927b55fd",
"packages/app/src/design-polish.css": "42cc6efaefe9a71dedd12fcb0bf2549453d9a46097cb025cf087064b29d3ccce",
"packages/app/src/design-polish.css": "5c86194cadbb6de279920f99254f82a175d26650041ff9c32fdc8fdd2eaaa75f",
"packages/app/src/entry.tsx": "f35e1017f4c9d478d254b2a38043e5750064b6bef25169c3e07ae9f72ff1049c",
"packages/app/src/index.css": "08179e06ce2d419a2d98acc96025f91c7709062ea9f3ad245e88dc35e75ff9f7",
"packages/app/src/theme-preload.test.ts": "d9e4e96dd39a3491493637682611bedf6ddf4b6e4dbdddcb60bf006211b137a1",
Expand Down Expand Up @@ -1198,17 +1200,17 @@
"packages/session-ui/src/components/markdown-shiki.worker.ts": "0f438e40aee871d3da81488e21f42c0fefcd9af4a0eec9c599cd969566c2610e",
"packages/session-ui/src/components/markdown-stream.test.ts": "b73b1a15ffa2cfea5358b2d6313ce17d26a5a11302b0e3b7d823cde0c98cbc83",
"packages/session-ui/src/components/markdown-stream.ts": "50e38abd5d4ff919564655799678be10463dec58502fc5487c1c97a9a8db5399",
"packages/session-ui/src/components/markdown.css": "24e7c19218620386647656a5d27e116cdeca911f1916c5d5b8609edca11ff165",
"packages/session-ui/src/components/markdown.css": "9c7954d524e62c34e5f3391dd0174da1535bf3efeffd00d2ff092f781e0439e8",
"packages/session-ui/src/components/markdown.tsx": "89e132e62d20c873311c9781d0ca4d7cd1a7ff882d99f19c8696aafaf4a8f298",
"packages/session-ui/src/components/message-part-css.test.ts": "ffab7d2c94e8d40ca35e4a8d4383cc6efd805b133764176b70046fba874a5f06",
"packages/session-ui/src/components/message-part-groups.ts": "37be069bf46e926fcde83d3744ee706911ff002e6b532e279290d841aeee96da",
"packages/session-ui/src/components/message-part-skill.test.ts": "453bdcb25e4df91ad1004635b8766070a60421355adf18ee53f43cd2fa7d0486",
"packages/session-ui/src/components/message-part-skill.ts": "d65a15dc39b8c0dece13aebe5e308426ae2dd758b07740251525ba9257654cf7",
"packages/session-ui/src/components/message-part-text.ts": "e38d68224f1743ccc5e0e0a8023e11977e3b761ec67dd5364cde9cb34e97ea4b",
"packages/session-ui/src/components/message-part-user.ts": "1f2c8cb4efff5274863a133f369a7397cac804678e953740f356ae03b71d475c",
"packages/session-ui/src/components/message-part.css": "ae592deb33739738f6a7fc6a3460b7146803bdd05cbbf46f1cfd2c5cec421392",
"packages/session-ui/src/components/message-part.css": "23bd2abcff0ab2993f1a23f399512fe02a3a81e748e55aa931ba6c27d1d277e7",
"packages/session-ui/src/components/message-part.test.ts": "7c66430837fa3d2607657e57e35720ae68dc5e84662e12826713a938301eeb7b",
"packages/session-ui/src/components/message-part.tsx": "e94f064b10030622250c8e9bd0c869796cc6dda20fbcb643736c8b4802b1b2b7",
"packages/session-ui/src/components/message-part.tsx": "1c0befaade3eb0452d09463e1a2dbe5fe158ab2e03373d18b07fe4a5726b8d02",
"packages/session-ui/src/components/session-diff.ts": "97f1ca7f80cb316c3fd2b228a8f8d4db3a6b78d155216148e0a3198c500ac6d7",
"packages/session-ui/src/components/session-retry.tsx": "2cb1513d73275a269894f601e8c1d29bc790b14927134fe36177f2b371888721",
"packages/session-ui/src/components/session-review.css": "ec8c5b359dd4b7ff80d7fad8906359678adade9ce62c9d74aedc000b646db341",
Expand Down Expand Up @@ -1256,6 +1258,7 @@
"packages/ui/src/amicode/context-tree-engine.test.ts": "afa3199c18c7ca05a19fdf0663a5b4d6e1fd2303e038cf25641c29e254e610f4",
"packages/ui/src/amicode/context-tree-engine.ts": "2177d01855d58c5ee7c164c2ed1dac6f4a8a5cc04daea67566f195b02ed1ca06",
"packages/ui/src/amicode/device-view.tsx": "1842053e9e25cd5c8076598449e44abf06008940a8f3f39403cbf435b1b8aa8d",
"packages/ui/src/amicode/docket.ts": "d157d2a7a0a8e716c0520c135b24af7eff84841248a50f76e6e5f02f6a83b8f1",
"packages/ui/src/amicode/edit-row.test.ts": "7c68fcf083d3854bfb70768b648110e001843a290dcfdc25869c1a4681445b94",
"packages/ui/src/amicode/edit-row.ts": "28d5f39470376968e7eabc3f50807a779177e9c9f2081302e4b5eb15306aef1b",
"packages/ui/src/amicode/entity-rail.tsx": "fee936650d0f05ab00e46ebb70333b2c92a57ab0da1e7fcee942d7e6b64004b7",
Expand Down Expand Up @@ -1295,8 +1298,8 @@
"packages/ui/src/amicode/run-series.ts": "c94496068ae05f4600b9dd9fab4247b678dae7aaba31ef48e4389b0e7fefc52c",
"packages/ui/src/amicode/run-view.tsx": "5098ef006cec0f354dde8589df1a730b5663c05300c1cf298569e62eb633f1be",
"packages/ui/src/amicode/run-window.tsx": "411abb6064ca76a1866b91914be1788f78173844f5e963562733348f92d982b4",
"packages/ui/src/amicode/shell-row.test.ts": "48ec6f0786edf81f163e350904bc2a93b1a55375b8115e688237ce8c2f1a7e64",
"packages/ui/src/amicode/shell-row.ts": "b39dc05d2b77ba6d44456248782b31f6219c891d56700203186a65ccb48319ad",
"packages/ui/src/amicode/shell-row.test.ts": "30d1da7e9bd065c1c93d894b91a17ca73f8e8136c4cc6e1a8dedd95afdfdb13b",
"packages/ui/src/amicode/shell-row.ts": "1c95e2a8bc7ceca53f671429e5f35351188da26e7cb3aa0f9f4fa9fa6a38a0b2",
"packages/ui/src/amicode/solver-switch.test.ts": "c9cc10d657d7adf0c388e56cb6058affed5a255bdeb9b8f2fea5c02f4daff1fe",
"packages/ui/src/amicode/solver-switch.ts": "041c2ad3cfa63770cdc09ee5fb2cda0c7d09c55e7e53c83927822476716c6b92",
"packages/ui/src/amicode/solver-toggle.test.ts": "2c5ede8eff8a60f1b5e6bb4423545438ebed6d5d60306d5bdc6f557144ede5e6",
Expand Down Expand Up @@ -1338,6 +1341,7 @@
"packages/ui/src/components/amicode-bridge.tsx": "c00ab12a45b9d092abc63a9b259e71bdf3582132aa1c288dcf8a2a520e5a5e80",
"packages/ui/src/components/amicode-card.tsx": "a09c4a734c55bf6fef0d04dcc0f0d4fce437e44a8745aa2ac2c936442d9a7d6e",
"packages/ui/src/components/amicode-connections-tab.tsx": "d184dfcd5c645332c2cc1c79356e551cd53b67a6b61ed16e78d041de5d13fafe",
"packages/ui/src/components/amicode-docket.tsx": "060f2be2b0a91b38cc122f2a77baf9939a8eda19bae683c7814cfbf281ec9b3c",
"packages/ui/src/components/amicode-edit-row.tsx": "99c8c4e90ea02be21f74603c5a5ac1e2c030f487d62ce9da972c99dff505d892",
"packages/ui/src/components/amicode-entity-rail.tsx": "5377561b952d9e6bd9bd6a16cec91a37770f14195bb7bd1cd294eb20a1e78a3d",
"packages/ui/src/components/amicode-entity-view.tsx": "1ff46b6abdca65992606755e0d5e516483bbd8f3f8cef54b81cb6faa08d895dc",
Expand All @@ -1353,7 +1357,7 @@
"packages/ui/src/components/amicode-run-card.tsx": "e9295cf59d46a71de0ee5b91c74eea3b4e42b70a9358f39a9a28c2578627f6b3",
"packages/ui/src/components/amicode-run-gallery.tsx": "366919dc8b1fc48d4aeb454bc8f92e5e23621a4570c9084a2889c88952d6f692",
"packages/ui/src/components/amicode-run-window.tsx": "5e5c623adc5d4e3fd35740e9b9cb5bae77e6f3f694850c5828f06a2616b4181f",
"packages/ui/src/components/amicode-shell-row.tsx": "0ce6525dd2cb8c1b04c7779ae477417a7776ec83768b8b028d52817e734587a9",
"packages/ui/src/components/amicode-shell-row.tsx": "fba08c4342bb775c099b1d71549708b7509ea29d83ac57401c99210a274f72c1",
"packages/ui/src/components/amicode-solver-switch.tsx": "ea1e5d187fcbd505046af8c2bdcddeb7b30dd98a7d2ee9a4775e25be2c581270",
"packages/ui/src/components/amicode-solver-toggle.tsx": "b09c870bab880bfdf2557fb870fb6af13a065f90353f59d609f37a6054db858d",
"packages/ui/src/components/amicode-splash.tsx": "0f69ebdd0e4e6595f38b1492c31a28bd1320706c7f1e26411ec4d25d1ab2921f",
Expand Down Expand Up @@ -1552,7 +1556,7 @@
"packages/app/src/pages/session/composer/session-tour.tsx": "da5d807c7efe7aed1140d65d7eddd7982a8c17348d928f5d0a887d1b2a8d3a50",
"packages/app/src/pages/session/composer/todo-panel-motion.stories.tsx": "70a8267c725af320d78d266b85fd0ff005d067c4d09a21bef824126602e831e7",
"packages/app/src/pages/session/timeline/last-prompt-bubble.test.ts": "83753b3ec7227570523c2e0b9e82401fae7a20f3a23980bb40deaeb13304327b",
"packages/app/src/pages/session/timeline/message-timeline.tsx": "718264c48becae24f8c55b0ef61684a26a9a12762edad9526c0b8caf9f6b097f",
"packages/app/src/pages/session/timeline/message-timeline.tsx": "2c0214ee9ee240340c4917287650da8d299c9bcc4eabd2fcc73a04c5bfa4749b",
"packages/app/src/pages/session/timeline/model-pure.ts": "50be4ba12d747dd8df9932044ceee29c5f3b6fa6d6ac36791207f91a91067cd4",
"packages/app/src/pages/session/timeline/model.test.ts": "459e0694409ffcf2530252a65491e1006f982b8f5c78a15573ed41646b2c85cf",
"packages/app/src/pages/session/timeline/model.ts": "76cbb00ef94133db33b69851ae8eb5cc974b5f2414551cb48ba7b61f3cfa025e",
Expand Down
42 changes: 27 additions & 15 deletions packages/app-bundle/overlay/packages/app/src/design-polish.css
Original file line number Diff line number Diff line change
Expand Up @@ -151,27 +151,28 @@
}

/* ── prompt bubble ground — per-scheme REALISATION ──
On LIGHT the user's words are the INVERSE of the ground — the website's
ink bubble, verbatim (the site is light-only, so the inverse grammar is a
light-mode truth). On DARK any light bubble read harsh at chat scale:
Kate walked the inverse down 100%→85%→70% luminance (7b1ff4e, 1ef7c41)
and it still glared — dark wants LOW contrast, not an inverse. The dark
bubble is therefore the theme's own quiet elevation: layer-02 on the
ground (≈1.5:1, a whisper of lift — stock #2e2e2e on #161616) under full
base ink (≈12:1, unchanged readability). */
The user's words, both schemes (Aaron 2026-09-05): on LIGHT the site's
solid yellow chip — --accent fill, --accent-ink, the ink border (--accent-
edge-ink; yellow can't define itself on the near-white ground). On DARK
the chip inverts into a dark box with the FULL-strength yellow border —
the loud yellow outline is the user's signature, one step louder than the
assistant fragments' --accent-edge (45%) hairline — under base ink. */
:root,
[data-color-scheme="light"] {
--prompt-bubble-bg: var(--v2-background-bg-inverse);
--prompt-bubble-ink: var(--v2-text-text-inverse);
--prompt-bubble-bg: var(--accent);
--prompt-bubble-ink: var(--accent-ink);
--prompt-bubble-edge: var(--accent-edge-ink);
}
[data-color-scheme="dark"] {
--prompt-bubble-bg: var(--v2-background-bg-layer-02);
--prompt-bubble-bg: var(--v2-background-bg-layer-01);
--prompt-bubble-ink: var(--v2-text-text-base);
--prompt-bubble-edge: var(--accent);
}
@media (prefers-color-scheme: dark) {
:root:not([data-color-scheme="light"]) {
--prompt-bubble-bg: var(--v2-background-bg-layer-02);
--prompt-bubble-bg: var(--v2-background-bg-layer-01);
--prompt-bubble-ink: var(--v2-text-text-base);
--prompt-bubble-edge: var(--accent);
}
}

Expand Down Expand Up @@ -232,9 +233,14 @@
/* ── prose fragment cards ── every fragment Amico relays is its OWN bordered
card, a message within the chat (Kate 2026-08-25; supersedes the
one-container-per-reply segment). Each chunk of a streamed reply arrives
as its own card with its own entrance; history splits identically so the
cards persist. Chips/receipts keep their borderless chip grammar. The
hairline is the theme's default border, the radius the brand 4px. */
as its own card with its own entrance; history splits identically so the
cards persist. Chips/receipts keep their borderless chip grammar. The
hairline is the theme's default border, the radius the brand 4px.

(A 2026-09-05 pass seated a dark ground + yellow edge on these cards in
dark mode; reverted the same day at Aaron's call — the dark-box-yellow-
border treatment belongs to the USER's prompt bubble only. The assistant
fragments stay on the theme hairline in both schemes.) */
[data-slot="text-part-body"]:has([data-prose-fragment]) {
display: flex;
flex-direction: column;
Expand All @@ -258,6 +264,12 @@
theme scope and the virtualizer relayouts under the captured rect. The
--motion-exit-* tokens above stay as the documented grammar should a real
FLIP-based exit ever be built. */

/* The sticky prompt bubble's lock-in hand-off (one past prompt taking the top
slot) is a WAAPI merge pass in message-timeline.tsx, not CSS: the chip
element must persist across hand-offs — a keyed remount + CSS entrance
strobed under smooth scrolling (Aaron 2026-09-04, reverted same day). */

@media (prefers-reduced-motion: reduce) {
:root {
--motion-enter-rise: 0px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,34 @@ export function MessageTimeline(props: {
// What the bubble actually shows: override (from click) takes priority
const activeBubble = () => bubbleOverride() ?? visiblePromptBubble()

// The bubble lock — a smooth MERGE on hand-off (Aaron 2026-09-04). The chip
// element persists; when the message locked at top changes (scroll hand-off,
// click, send), the text swap underneath is masked by a gentle opacity +
// translate dip via WAAPI — the new prompt glides over the old in one
// soft 240ms pass. No remount, no exit machinery, nothing to strobe under
// smooth scrolling. Honors prefers-reduced-motion.
let bubbleChip: HTMLButtonElement | undefined
let lockedBubbleId: string | undefined
createEffect(() => {
const bubble = activeBubble()
if (!bubble) {
lockedBubbleId = undefined
return
}
if (bubble.messageId === lockedBubbleId) return
lockedBubbleId = bubble.messageId
const el = bubbleChip
if (!el || typeof el.animate !== "function") return
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return
el.animate(
[
{ opacity: 0.45, transform: "translateY(2px)" },
{ opacity: 1, transform: "none" },
],
{ duration: 240, easing: "cubic-bezier(0.2, 0, 0.2, 1)" },
)
})

// Find the previous user message with text before a given message ID
const findPreviousBubble = (currentMessageId: string): { text: string; messageId: string } | undefined => {
const messages = props.userMessages
Expand Down Expand Up @@ -2575,7 +2603,8 @@ export function MessageTimeline(props: {
}}
/>
{/* amicode#271: bubble inside the header — naturally below the
title row + chip rail */}
title row + chip rail. The element PERSISTS across hand-offs;
the lock-in merge runs via WAAPI in the effect below. */}
<Show when={activeBubble()}>
{(bubble) => (
<div
Expand All @@ -2585,28 +2614,30 @@ export function MessageTimeline(props: {
>
<button
type="button"
ref={(el) => (bubbleChip = el)}
class="ml-auto block w-fit max-w-[min(75%,56ch)] text-left cursor-pointer border-none rounded-lg px-3 py-1.5 text-[13px] leading-[18px] font-normal truncate backdrop-blur-[2px]"
style={{
// the ghost of the prompt bubble keeps the bubble's own
// ground, translucent — one grammar for the user's
// words on every surface (--prompt-bubble-*: the
// inverse, seated per scheme in design-polish.css)
// yellow chip, seated per scheme in design-polish.css)
background: "color-mix(in srgb, var(--prompt-bubble-bg) 90%, transparent)",
color: "var(--prompt-bubble-ink)",
border: "var(--border-width) solid var(--prompt-bubble-edge, transparent)",
"box-shadow": "0 1px 3px color-mix(in srgb, var(--v2-background-bg-base) 40%, transparent)",
}}
onClick={scrollToBubbleMessage}
title={bubble().text}
>
<span class="opacity-60 text-[11px] font-medium uppercase tracking-wider mr-2">You</span>
{bubble().text}
</button>
</div>
)}
</Show>
</div>
</Show>
{/* amicode#271: no-header fallback (new untitled sessions only) */}
{/* amicode#271: no-header fallback (new untitled sessions only) —
same persistent chip, same WAAPI merge */}
<Show when={!showHeader() && activeBubble()}>
{(_) => {
const bubble = () => activeBubble()!
Expand All @@ -2618,20 +2649,21 @@ export function MessageTimeline(props: {
>
<button
type="button"
ref={(el) => (bubbleChip = el)}
class="ml-auto block w-fit max-w-[min(75%,56ch)] text-left cursor-pointer border-none rounded-lg px-3 py-1.5 text-[13px] leading-[18px] font-normal truncate backdrop-blur-[2px]"
style={{
// the ghost of the prompt bubble keeps the bubble's own
// ground, translucent — one grammar for the user's
// words on every surface (--prompt-bubble-*: the
// inverse, seated per scheme in design-polish.css)
// yellow chip, seated per scheme in design-polish.css)
background: "color-mix(in srgb, var(--prompt-bubble-bg) 90%, transparent)",
color: "var(--prompt-bubble-ink)",
border: "var(--border-width) solid var(--prompt-bubble-edge, transparent)",
"box-shadow": "0 1px 3px color-mix(in srgb, var(--v2-background-bg-base) 40%, transparent)",
}}
onClick={scrollToBubbleMessage}
title={bubble().text}
>
<span class="opacity-60 text-[11px] font-medium uppercase tracking-wider mr-2">You</span>
{bubble().text}
</button>
</div>
Expand Down
Loading
Loading