From e6bd84965b68383e87c8edd6eb28765d8a2ef229 Mon Sep 17 00:00:00 2001 From: Luke Parker <10430890+Hona@users.noreply.github.com> Date: Mon, 19 Oct 2026 17:41:54 +1000 Subject: [PATCH] fix(session-ui): restore blue user bubbles with grey pending steers (#57001) --- .../app/e2e/regression/new-session.spec.ts | 6 +-- .../app/e2e/regression/session-queue.spec.ts | 50 ++++++++++++------- packages/app/e2e/utils/prompt-motion.ts | 2 +- .../src/components/message-part.css | 45 +++++++++-------- 4 files changed, 61 insertions(+), 42 deletions(-) diff --git a/packages/app/e2e/regression/new-session.spec.ts b/packages/app/e2e/regression/new-session.spec.ts index 72f2966d428..3b9d8730f29 100644 --- a/packages/app/e2e/regression/new-session.spec.ts +++ b/packages/app/e2e/regression/new-session.spec.ts @@ -418,7 +418,7 @@ for (const row of [ accent: false, }, ]) { - test(`keeps the user message grey for ${row.name}`, async ({ page }) => { + test(`keeps the user message blue for ${row.name}`, async ({ page }) => { const inventory: WorktreeDirectory[] = [{ directory: accentRoot }, { directory: accentWorkspace, strategy: "git" }] const listed = page.waitForResponse( @@ -464,8 +464,8 @@ for (const row of [ await expectBackground(send, "icon-button-contrast") const message = page.locator('[data-slot="user-message-text"]') await expect(message).toHaveText("Check this fixture workspace.") - await expectToken(message, "background-color", "--v2-background-bg-layer-02") - await expectToken(message, "color", "--v2-text-text-base") + await expectToken(message, "background-color", "--v2-blue-100") + await expectToken(message, "color", "--v2-blue-700") if (!row.accent) return diff --git a/packages/app/e2e/regression/session-queue.spec.ts b/packages/app/e2e/regression/session-queue.spec.ts index c03d834086d..5e88ec91001 100644 --- a/packages/app/e2e/regression/session-queue.spec.ts +++ b/packages/app/e2e/regression/session-queue.spec.ts @@ -946,20 +946,36 @@ for (const delivery of ["steer", "queue"] as const) { const colors = delivery === "steer" - ? await userRow(page, userID) - .locator('[data-slot="user-message-text"]') - .evaluate((element) => ({ - background: getComputedStyle(element).backgroundColor, - text: getComputedStyle(element).color, - })) + ? { + delivered: await userRow(page, userID) + .locator('[data-slot="user-message-text"]') + .evaluate((element) => ({ + background: getComputedStyle(element).backgroundColor, + text: getComputedStyle(element).color, + })), + pending: await bubble.evaluate((element) => { + const probe = document.createElement("span") + probe.style.backgroundColor = "var(--v2-background-bg-layer-02)" + probe.style.color = "var(--v2-text-text-base)" + element.appendChild(probe) + + const colors = { + background: getComputedStyle(probe).backgroundColor, + text: getComputedStyle(probe).color, + } + + probe.remove() + + return colors + }), + } : undefined if (colors) { - // A pending steer shares the delivered bubble, dimmed, and keeps its Pending row in view without hover. - await expect(bubble).toHaveCSS("background-color", colors.background) - await expect(bubble).toHaveCSS("color", colors.text) - await expect(pending.locator('[data-slot="user-message-body"]')).toHaveCSS("opacity", "0.6") - await expect(pending.locator('[data-slot="user-message-body"]')).toHaveCSS("transition-duration", "0s") + // A pending steer is grey, switches to it without a transition, and keeps its Pending row in view without hover. + await expect(bubble).toHaveCSS("background-color", colors.pending.background) + await expect(bubble).toHaveCSS("color", colors.pending.text) + await expect(bubble).toHaveCSS("transition-duration", "0s") await expect(pending.locator('[data-slot="user-message-copy-wrapper"]')).toHaveCSS("opacity", "1") await expect(pending.locator('[data-slot="user-message-meta"]')).toHaveText( /^Pending\s*·\s*Build\s*·\s*Queue Model$/, @@ -1046,15 +1062,13 @@ for (const delivery of ["steer", "queue"] as const) { if (colors) { await expect(bubble).toHaveAttribute("data-delivery-marker", "pending") await expect(pending.locator('[data-slot="user-message-meta"]')).toHaveText(/^Build\s*·\s*Queue Model$/) - await expect(bubble).toHaveCSS("background-color", colors.background) - await expect(bubble).toHaveCSS("color", colors.text) - // Only delivery animates the dim away; reduced motion switches instantly. - const body = pending.locator('[data-slot="user-message-body"]') - await expect(body).toHaveCSS("transition-property", "opacity") - await expect(body).toHaveCSS("opacity", "1") + await expect(bubble).toHaveCSS("background-color", colors.delivered.background) + await expect(bubble).toHaveCSS("color", colors.delivered.text) + // Only delivery animates the colour; reduced motion switches instantly. + await expect(bubble).toHaveCSS("transition-property", "background-color, color") await transcript.screenshot({ path: testInfo.outputPath("delivered-steer.png") }) await page.emulateMedia({ reducedMotion: "reduce" }) - await expect(body).toHaveCSS("transition-duration", "0s") + await expect(bubble).toHaveCSS("transition-duration", "0s") } await expect(transcript.locator('[data-timeline-row="UserMessage"]')).toHaveCount(2) diff --git a/packages/app/e2e/utils/prompt-motion.ts b/packages/app/e2e/utils/prompt-motion.ts index e4d15b0594c..f48c75f5e76 100644 --- a/packages/app/e2e/utils/prompt-motion.ts +++ b/packages/app/e2e/utils/prompt-motion.ts @@ -2,7 +2,7 @@ import type { Page } from "@playwright/test" export type PromptPosition = { frame: number; top: number | null } -// User bubbles are neutral grey, so paint them a test-only blue that the frame reader can tell from everything else. +// Paint user bubbles a test-only blue that the frame reader can tell from everything else, whatever the theme colour. export async function markPrompt(page: Page) { await page.addStyleTag({ content: '[data-slot="user-message-text"] { background: rgb(0 0 255) !important; }' }) } diff --git a/packages/session-ui/src/components/message-part.css b/packages/session-ui/src/components/message-part.css index 2872ba16d8e..f30c1d85ee5 100644 --- a/packages/session-ui/src/components/message-part.css +++ b/packages/session-ui/src/components/message-part.css @@ -175,6 +175,13 @@ border: none; padding: 8px 12px; border-radius: 10px; + transition: + background-color 150ms ease-out, + color 150ms ease-out; + + @media (prefers-reduced-motion: reduce) { + transition: none; + } [data-highlight="file"], [data-highlight="agent"] { @@ -185,7 +192,7 @@ } [data-slot="user-message-mention-prefix"] { - color: var(--v2-text-text-accent); + color: var(--v2-blue-500); } max-width: 100%; @@ -247,23 +254,7 @@ pointer-events: auto; } - [data-slot="user-message-attachments"], - [data-slot="user-message-body"] { - transition: opacity 150ms ease-out; - - @media (prefers-reduced-motion: reduce) { - transition: none; - } - } - - /* A pending steer is dimmed and keeps its Pending label and actions in view. Only delivery animates: a steer can - render once before its admission marks it pending, and must not fade down from the delivered look. */ - &[data-pending] [data-slot="user-message-attachments"], - &[data-pending] [data-slot="user-message-body"] { - opacity: 0.6; - transition: none; - } - + /* A pending steer keeps its Pending label and actions in view. */ &[data-pending] [data-slot="user-message-copy-wrapper"] { opacity: 1; pointer-events: auto; @@ -1459,7 +1450,8 @@ font-weight: 440; [data-slot="user-message-text"] { - background: var(--v2-background-bg-layer-01); + background: var(--v2-blue-1200); + color: var(--v2-blue-300); } [data-slot="user-message-comments"] { @@ -1501,5 +1493,18 @@ } :root[data-color-scheme="light"] body [data-component="user-message"] [data-slot="user-message-text"] { - background: var(--v2-background-bg-layer-02); + background: var(--v2-blue-100); + color: var(--v2-blue-700); +} + +/* Only delivery animates. A steer can render once before its admission marks it pending, and must not fade from the + delivered color. */ +:root body [data-component="user-message"][data-pending] [data-slot="user-message-text"] { + background: var(--v2-background-bg-layer-02); + color: var(--v2-text-text-base); + transition: none; + + [data-slot="user-message-mention-prefix"] { + color: currentColor; + } }