From 08c05c184177908d7c76796737e83402b54e706b Mon Sep 17 00:00:00 2001 From: Blomios Date: Sun, 13 Sep 2026 19:04:32 +0200 Subject: [PATCH] =?UTF-8?q?fix(frontend):=20expand=20empty-state=20waterma?= =?UTF-8?q?rk=20to=20full=20cell=20and=20tint=20monochrome=20project=20ico?= =?UTF-8?q?ns=20by=20project=20color=20=E2=80=94=20#351=20(QA=20verte)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cause: the empty-state watermark filled only 92% of a centered box and kept residual palette colors from host emoji fonts, non-compliant with the monochrome-first project icon rule (#351 user capture). Fix: illustration spans the cell with 6px/4px insets and an unclipped glyph at min(88cqw, 88cqh); emoji painted as monochrome silhouettes via transparent fill + zero-offset text-shadow tint (--project-illustration-tint) derived from accessibleProjectAccent when a project color exists, neutral muted fallback otherwise; empty label anchored bottom-center and hidden first on constrained containers (@container 280px width / 240px height); overlay priorities, composer and z-layering untouched. QA verte reconfirmée par Git: npm test -- CustomAgentChatView.test.tsx 80/80, git diff --check; QA: ProjectSettingsPanel 12/12, typecheck, build OK (warning Vite chunk >500 kB préexistant). Co-Authored-By: Claude Opus 4.8 --- .../agents/CustomAgentChatView.test.tsx | 9 ++-- .../features/agents/CustomAgentChatView.tsx | 25 +++++++---- frontend/src/shared/styles/theme.css | 45 +++++++++++++++++++ 3 files changed, 67 insertions(+), 12 deletions(-) diff --git a/frontend/src/features/agents/CustomAgentChatView.test.tsx b/frontend/src/features/agents/CustomAgentChatView.test.tsx index 480ef894..0fef93de 100644 --- a/frontend/src/features/agents/CustomAgentChatView.test.tsx +++ b/frontend/src/features/agents/CustomAgentChatView.test.tsx @@ -212,16 +212,19 @@ describe("CustomAgentChatView", () => { const watermark = await screen.findByTestId("project-icon-watermark"); expect(watermark.style.pointerEvents).toBe("none"); expect(watermark.getAttribute("aria-hidden")).toBe("true"); - expect((watermark.firstElementChild as HTMLElement | null)?.style.width).toBe("92%"); - expect((watermark.firstElementChild as HTMLElement | null)?.style.height).toBe("92%"); + expect(watermark.className).toContain("project-cell-illustration"); + expect((watermark.firstElementChild as HTMLElement | null)?.style.width).toBe("100%"); + expect((watermark.firstElementChild as HTMLElement | null)?.style.height).toBe("100%"); + expect((watermark.firstElementChild as HTMLElement | null)?.className).toContain("project-cell-illustration__glyph"); expect(watermark.textContent).toBe("🐍\uFE0E"); expect(watermark.dataset.colorMode).toBe("monochrome"); + expect(watermark.style.getPropertyValue("--project-illustration-tint")).toBe("var(--color-muted, #9aa7b4)"); publishProjectIconChanged({ projectId: "project-1", icon: { set: "emoji", name: "star" } }); await waitFor(() => expect(watermark.textContent).toBe("⭐\uFE0E")); publishProjectThemeChanged({ projectId: "project-1", themeColor: "#7c3aed" }); await waitFor(() => { expect(watermark.dataset.colorMode).toBe("project"); - expect(watermark.style.color).toBe("rgb(124, 58, 237)"); + expect(watermark.style.getPropertyValue("--project-illustration-tint")).toBe("#945DF0"); }); expect(screen.getByTestId("custom-agent-chat-scroll").className).toContain("z-10"); expect(screen.getByTestId("custom-agent-chat-composer").className).toContain("bg-raised/40"); diff --git a/frontend/src/features/agents/CustomAgentChatView.tsx b/frontend/src/features/agents/CustomAgentChatView.tsx index ceb45662..366a5004 100644 --- a/frontend/src/features/agents/CustomAgentChatView.tsx +++ b/frontend/src/features/agents/CustomAgentChatView.tsx @@ -15,6 +15,7 @@ import { useRef, useState, type ClipboardEvent, + type CSSProperties, type KeyboardEvent, type MouseEvent as ReactMouseEvent, type RefObject, @@ -45,7 +46,11 @@ import { } from "@/features/projects/projectCliInteractionActivity"; import { Button, EntryTimestamp, Spinner, cn, normalizeTimestamp } from "@/shared"; import { onProjectIconChanged, ProjectIcon } from "@/features/projects/projectIcons"; -import { onProjectThemeChanged, validThemeColor } from "@/features/projects/projectTheme"; +import { + accessibleProjectAccent, + onProjectThemeChanged, + validThemeColor, +} from "@/features/projects/projectTheme"; import type { ChatAttachmentInput, ChatDraftScope, @@ -2504,26 +2509,28 @@ export function CustomAgentChatView({
) : displayTurns.length === 0 ? ( -

+

{t("custom.empty")}

) : ( diff --git a/frontend/src/shared/styles/theme.css b/frontend/src/shared/styles/theme.css index 11406731..76fa0f02 100644 --- a/frontend/src/shared/styles/theme.css +++ b/frontend/src/shared/styles/theme.css @@ -103,6 +103,51 @@ } @layer components { + .custom-agent-chat-view { + container-type: size; + } + + .project-cell-illustration { + inset: 6px 4px; + color: transparent; + opacity: 0.045; + } + + .project-cell-illustration[data-color-mode="project"] { + opacity: 0.065; + } + + /* Emoji fonts may ignore the text-variation selector and retain their own + palette. Painting a zero-offset text shadow through the glyph alpha mask + guarantees one semantic project tint without depending on the host font. */ + .project-cell-illustration__glyph { + color: transparent; + -webkit-text-fill-color: transparent; + text-shadow: 0 0 0 var(--project-illustration-tint, var(--color-muted)); + } + + .project-cell-empty-label { + position: relative; + z-index: 1; + max-width: min(34rem, 88%); + margin: auto auto 8px; + text-align: center; + } + + /* Preserve the illustration when a split cell becomes constrained; the + explanatory copy is the first element to yield. */ + @container (max-width: 280px) { + .project-cell-empty-label { + display: none; + } + } + + @container (max-height: 240px) { + .project-cell-empty-label { + display: none; + } + } + .stopped-agent-cell { position: absolute; inset: 0;