diff --git a/desktop/playwright.config.ts b/desktop/playwright.config.ts index 7d06c4da91..fb2d2b85a0 100644 --- a/desktop/playwright.config.ts +++ b/desktop/playwright.config.ts @@ -21,6 +21,7 @@ export default defineConfig({ testMatch: [ "**/smoke.spec.ts", "**/sidebar-offcanvas-rail.spec.ts", + "**/tooltip-semantics.spec.ts", "**/search-scope-screenshots.spec.ts", "**/onboarding-docked-cta-screenshots.spec.ts", "**/identity-key-help.spec.ts", diff --git a/desktop/src/features/agents/ui/RestartDiffBadge.tsx b/desktop/src/features/agents/ui/RestartDiffBadge.tsx index 1bdb781226..e15a57fde4 100644 --- a/desktop/src/features/agents/ui/RestartDiffBadge.tsx +++ b/desktop/src/features/agents/ui/RestartDiffBadge.tsx @@ -88,8 +88,8 @@ function ChangeDescription({ change }: { change: RestartChange }) { const TOOLTIP_CAP = 6; /** - * `tooltip` — renders inside the dark `bg-primary` tooltip; uses - * `text-primary-foreground` variants for contrast there. + * `tooltip` — renders inside the semantic secondary tooltip surface; uses + * `text-secondary-foreground` variants for contrast there. * `inline` — renders inside the amber Runtime banner or other light * surfaces; inherits foreground from the container instead. */ @@ -107,10 +107,10 @@ function DiffList({ cap !== undefined && entries.length > cap ? entries.length - cap : 0; const valueClass = - variant === "tooltip" ? "text-primary-foreground/80" : "text-foreground"; + variant === "tooltip" ? "text-secondary-foreground/80" : "text-foreground"; const overflowClass = variant === "tooltip" - ? "text-primary-foreground/60" + ? "text-secondary-foreground/60" : "text-muted-foreground"; return ( @@ -180,7 +180,7 @@ export function RestartDiffBadge({

Config changed since last start:

-

+

{autoRestartEnabled ? AUTO_RESTART_ON_BLURB : AUTO_RESTART_OFF_BLURB}

diff --git a/desktop/src/features/channels/ui/AddChannelBotTeamsSection.tsx b/desktop/src/features/channels/ui/AddChannelBotTeamsSection.tsx index 512ee6899f..a3bad85629 100644 --- a/desktop/src/features/channels/ui/AddChannelBotTeamsSection.tsx +++ b/desktop/src/features/channels/ui/AddChannelBotTeamsSection.tsx @@ -143,7 +143,7 @@ export function AddChannelBotTeamsSection({

{team.name}

{team.description ? ( -

+

{team.description}

) : null} @@ -153,15 +153,15 @@ export function AddChannelBotTeamsSection({ inChannelPersonaIds?.has(persona.id) ?? false; return (
- + {persona.displayName} {personaInChannel ? ( diff --git a/desktop/src/features/projects/ui/ProjectAuthorIdentity.tsx b/desktop/src/features/projects/ui/ProjectAuthorIdentity.tsx index 8126292894..21a70d6dcb 100644 --- a/desktop/src/features/projects/ui/ProjectAuthorIdentity.tsx +++ b/desktop/src/features/projects/ui/ProjectAuthorIdentity.tsx @@ -60,7 +60,7 @@ export function ProjectAuthorIdentity({ /> {label} - + {roleLabel} diff --git a/desktop/src/shared/ui/tooltip.tsx b/desktop/src/shared/ui/tooltip.tsx index 6af9e48188..6fa5891973 100644 --- a/desktop/src/shared/ui/tooltip.tsx +++ b/desktop/src/shared/ui/tooltip.tsx @@ -34,7 +34,7 @@ const TooltipContent = React.forwardRef< ref={ref} sideOffset={sideOffset} className={cn( - "z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-tooltip-content-transform-origin)", + "z-50 overflow-hidden rounded-md bg-secondary px-3 py-1.5 text-xs text-secondary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-tooltip-content-transform-origin)", className, )} {...props} diff --git a/desktop/tests/e2e/tooltip-semantics.spec.ts b/desktop/tests/e2e/tooltip-semantics.spec.ts new file mode 100644 index 0000000000..2b6f68348e --- /dev/null +++ b/desktop/tests/e2e/tooltip-semantics.spec.ts @@ -0,0 +1,88 @@ +import { expect, test, type Locator, type Page } from "@playwright/test"; + +import { installMockBridge } from "../helpers/bridge"; +import { waitForAnimations } from "../helpers/animations"; + +const THEMES = ["buzz", "catppuccin-mocha"] as const; + +async function seedTheme(page: Page, theme: (typeof THEMES)[number]) { + await page.addInitScript((value) => { + window.localStorage.setItem("buzz-theme", value); + }, theme); +} + +async function expectSecondarySurface(tooltip: Locator) { + const colors = await tooltip.evaluate((element) => { + const sample = document.createElement("div"); + sample.style.cssText = + "position:fixed;visibility:hidden;background:hsl(var(--secondary));color:hsl(var(--secondary-foreground))"; + document.body.append(sample); + const tooltipStyle = getComputedStyle(element); + const sampleStyle = getComputedStyle(sample); + const result = { + background: tooltipStyle.backgroundColor, + color: tooltipStyle.color, + expectedBackground: sampleStyle.backgroundColor, + expectedColor: sampleStyle.color, + }; + sample.remove(); + return result; + }); + + expect(colors.background).toBe(colors.expectedBackground); + expect(colors.color).toBe(colors.expectedColor); +} + +for (const theme of THEMES) { + test(`simple and rich tooltips use the secondary surface — ${theme}`, async ({ + page, + }) => { + await seedTheme(page, theme); + await installMockBridge(page, { + personas: [ + { + id: "tooltip-reviewer", + displayName: "Tooltip Reviewer", + systemPrompt: "Review tooltip contrast.", + }, + ], + teams: [ + { + id: "tooltip-team", + name: "Contrast Crew", + description: "Checks semantic surface contrast", + personaIds: ["tooltip-reviewer"], + }, + ], + }); + await page.goto("/"); + + await page.getByTestId("channel-random").click(); + await page.getByTestId("channel-members-trigger").hover(); + const simpleTooltip = page.getByRole("tooltip"); + await expect(simpleTooltip).toHaveText("Channel members"); + await expectSecondarySurface(simpleTooltip); + + await page.mouse.move(0, 0); + await page.getByTestId("channel-intro-action-create-agent").click(); + const dialog = page.getByTestId("add-channel-bot-dialog"); + await expect(dialog).toBeVisible(); + await dialog.getByRole("button", { name: "Contrast Crew" }).hover(); + + const richTooltip = page.getByRole("tooltip"); + await expect(richTooltip).toContainText("Checks semantic surface contrast"); + await expect(richTooltip).toContainText("Tooltip Reviewer"); + await expectSecondarySurface(richTooltip); + await expect( + richTooltip.getByText("Checks semantic surface contrast"), + ).toHaveClass(/text-secondary-foreground\/80/); + await expect(richTooltip.getByText("Tooltip Reviewer")).toHaveClass( + /text-secondary-foreground/, + ); + + await waitForAnimations(page); + await dialog.screenshot({ + path: `test-results/tooltip-semantics/${theme}-team-tooltip.png`, + }); + }); +}