Merge main (light palette) into claude/phase2-coach

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
This commit is contained in:
Claude 2026-10-04 03:54:55 +00:00
commit aea604f7ca
No known key found for this signature in database
10 changed files with 56 additions and 49 deletions

View File

@ -4,6 +4,10 @@ import { captureScreenshot } from "./helpers";
const fixture = "/e2e/fixtures/chart-links.html"; const fixture = "/e2e/fixtures/chart-links.html";
test("chart links cannot execute script when clicked or expanded", async ({ page }, testInfo) => { test("chart links cannot execute script when clicked or expanded", async ({ page }, testInfo) => {
test.setTimeout(120_000);
// ChartCanvas lazy-loads Observable Plot; pay the import cost once before the matrix below.
await page.goto(`${fixture}?${new URLSearchParams({ href: "#details", inspect: "0" })}`);
await expect(page.locator("svg circle").first()).toBeVisible({ timeout: 90_000 });
for (const inspect of ["0", "1"]) { for (const inspect of ["0", "1"]) {
for (const href of [ for (const href of [
'javascript:void(document.body.dataset.plotExecuted="yes")', 'javascript:void(document.body.dataset.plotExecuted="yes")',

View File

@ -67,6 +67,9 @@ test("account settings appearance control switches to light mode", async ({ page
await settings.getByRole("button", { name: "Close user settings" }).click(); await settings.getByRole("button", { name: "Close user settings" }).click();
await expect(settings).toBeHidden(); await expect(settings).toBeHidden();
await expect(page.locator("html")).toHaveAttribute("data-theme", "light"); await expect(page.locator("html")).toHaveAttribute("data-theme", "light");
// Light surfaces stay light: no dark panel or white-on-paper text survives the switch.
await expect(page.locator("main").first()).toHaveCSS("background-color", "rgb(255, 255, 255)");
await expect(page.locator("aside").first()).toHaveCSS("background-color", "rgb(245, 245, 242)");
await captureScreenshot(page, testInfo, "ui-appearance-light-shell"); await captureScreenshot(page, testInfo, "ui-appearance-light-shell");
await captureSidebarSearchSelected(page, testInfo, "sidebar-search-selected-light"); await captureSidebarSearchSelected(page, testInfo, "sidebar-search-selected-light");

View File

@ -27,7 +27,7 @@
document.documentElement.dataset.theme = theme; document.documentElement.dataset.theme = theme;
document.documentElement.style.colorScheme = theme; document.documentElement.style.colorScheme = theme;
const meta = document.querySelector('meta[name="theme-color"]'); const meta = document.querySelector('meta[name="theme-color"]');
if (meta) meta.setAttribute("content", theme === "light" ? "#fafaf8" : "#07080a"); if (meta) meta.setAttribute("content", theme === "light" ? "#ffffff" : "#07080a");
})(); })();
</script> </script>
<script> <script>

View File

@ -55,22 +55,22 @@ export function StageHero({
) : null} ) : null}
<div <div
className={`relative flex ${orbSize} items-center justify-center rounded-full border border-border bg-muted/45 shadow-[0_16px_48px_rgba(0,0,0,0.45),inset_0_1px_0_rgba(255,255,255,0.12)] backdrop-blur-xl`} className={`relative flex ${orbSize} items-center justify-center rounded-full border border-border bg-muted/45 shadow-[0_12px_32px_-12px_rgba(24,24,22,0.18)] backdrop-blur-xl dark:shadow-[0_16px_48px_rgba(0,0,0,0.45),inset_0_1px_0_rgba(255,255,255,0.12)]`}
style={
reducedMotion
? undefined
: {
boxShadow: `0 16px 48px rgba(0,0,0,0.45), inset 0 1px 0 rgba(255,255,255,0.12), 0 0 ${Math.round(18 + energy * 32)}px ${glowRgb}`,
}
}
> >
{reducedMotion ? null : (
<div
aria-hidden
className="pointer-events-none absolute inset-0 rounded-full"
style={{ boxShadow: `0 0 ${Math.round(18 + energy * 32)}px ${glowRgb}` }}
/>
)}
<BotAvatar <BotAvatar
color={botColor} color={botColor}
identity={botId} identity={botId}
status={botStatus} status={botStatus}
pose={pose} pose={pose}
size={avatarSize} size={avatarSize}
className="drop-shadow-[0_6px_24px_rgba(0,0,0,0.4)]" className="drop-shadow-[0_6px_16px_rgba(0,0,0,0.12)] dark:drop-shadow-[0_6px_24px_rgba(0,0,0,0.4)]"
/> />
</div> </div>

View File

@ -41,7 +41,7 @@ export function TextStageCanvas({
style={{ style={{
background: `radial-gradient(circle, ${phaseTint} 0%, ${personaTint} 42%, transparent 72%)`, background: `radial-gradient(circle, ${phaseTint} 0%, ${personaTint} 42%, transparent 72%)`,
opacity: 0.55 + energy * 0.2, opacity: 0.55 + energy * 0.2,
transform: `translateX(-50%) scale(${bloomScale})`, transform: `scale(${bloomScale})`,
transition: reducedMotion transition: reducedMotion
? undefined ? undefined
: "transform 140ms ease-out, opacity 140ms ease-out", : "transform 140ms ease-out, opacity 140ms ease-out",

View File

@ -2,7 +2,7 @@
export function rosterRowClass(selected: boolean): string { export function rosterRowClass(selected: boolean): string {
return `flex w-full items-center gap-3 rounded-2xl border px-3 py-3 mb-[2px] text-start transition-all duration-150 cursor-pointer min-h-[52px] ${ return `flex w-full items-center gap-3 rounded-2xl border px-3 py-3 mb-[2px] text-start transition-all duration-150 cursor-pointer min-h-[52px] ${
selected selected
? "border-sidebar-border bg-sidebar-accent shadow-[inset_0_1px_0_rgba(255,255,255,0.14),0_4px_16px_rgba(0,0,0,0.4)]" ? "border-sidebar-border bg-sidebar-accent shadow-xs dark:shadow-[inset_0_1px_0_rgba(255,255,255,0.14),0_4px_16px_rgba(0,0,0,0.4)]"
: "border-transparent hover:bg-sidebar-accent/50" : "border-transparent hover:bg-sidebar-accent/50"
}`; }`;
} }

View File

@ -82,38 +82,38 @@ export const darkTokens = {
} as const satisfies ColorTokens; } as const satisfies ColorTokens;
export const lightTokens = { export const lightTokens = {
background: "#FAFAF8", background: "#FFFFFF",
foreground: "#1A1A1A", foreground: "#1A1A1A",
card: "#FFFFFF", card: "#FFFFFF",
cardForeground: "#1A1A1A", cardForeground: "#1A1A1A",
popover: "#FFFFFF", popover: "#FFFFFF",
popoverForeground: "#1A1A1A", popoverForeground: "#1A1A1A",
primary: "#1A1A1A", primary: "#1A1A1A",
primaryForeground: "#F1F1EF", primaryForeground: "#FFFFFF",
secondary: "#F0F0ED", secondary: "#F4F4F1",
secondaryForeground: "#1A1A1A", secondaryForeground: "#1A1A1A",
chatUser: "#E2E2DC", chatUser: "#EFEFEB",
chatUserForeground: "#1A1A1A", chatUserForeground: "#1A1A1A",
muted: "#F0F0ED", muted: "#F4F4F1",
mutedForeground: "#6C6C70", mutedForeground: "#67676C",
accent: "#EAEAE6", accent: "#ECECE8",
accentForeground: "#1A1A1A", accentForeground: "#1A1A1A",
destructive: "#DC2626", destructive: "#DC2626",
destructiveForeground: "#FFFFFF", destructiveForeground: "#FFFFFF",
border: "#F0F0ED", border: "#E5E5E0",
input: "#EAEAE6", input: "#DADAD4",
ring: "#6C6C70", ring: "#9C9C97",
sidebar: "#ECECE9", sidebar: "#F5F5F2",
sidebarForeground: "#1A1A1A", sidebarForeground: "#1A1A1A",
sidebarBorder: "#E8E8E4", sidebarBorder: "#E8E8E3",
sidebarAccent: "#FFFFFF", sidebarAccent: "#FFFFFF",
sidebarAccentForeground: "#1A1A1A", sidebarAccentForeground: "#1A1A1A",
link: "#2563EB", link: "#2563EB",
success: "#228B3B", success: "#1E7F37",
warning: "#B7791F", warning: "#A16207",
overlay: "rgba(20, 20, 22, 0.45)", overlay: "rgba(24, 24, 22, 0.28)",
scrollbar: "#C8C8C4", scrollbar: "#D3D3CD",
scrollbarHover: "#A8A8A4", scrollbarHover: "#B3B3AC",
} as const satisfies ColorTokens; } as const satisfies ColorTokens;
/** Dark palette. Prefer `tokensForAppearance` when theme-aware. */ /** Dark palette. Prefer `tokensForAppearance` when theme-aware. */

View File

@ -65,38 +65,38 @@
[data-theme="light"] { [data-theme="light"] {
color-scheme: light; color-scheme: light;
--background: #fafaf8; --background: #ffffff;
--foreground: #1a1a1a; --foreground: #1a1a1a;
--card: #ffffff; --card: #ffffff;
--card-foreground: #1a1a1a; --card-foreground: #1a1a1a;
--popover: #ffffff; --popover: #ffffff;
--popover-foreground: #1a1a1a; --popover-foreground: #1a1a1a;
--primary: #1a1a1a; --primary: #1a1a1a;
--primary-foreground: #f1f1ef; --primary-foreground: #ffffff;
--secondary: #f0f0ed; --secondary: #f4f4f1;
--secondary-foreground: #1a1a1a; --secondary-foreground: #1a1a1a;
--chat-user: #e2e2dc; --chat-user: #efefeb;
--chat-user-foreground: #1a1a1a; --chat-user-foreground: #1a1a1a;
--muted: #f0f0ed; --muted: #f4f4f1;
--muted-foreground: #6c6c70; --muted-foreground: #67676c;
--accent: #eaeae6; --accent: #ecece8;
--accent-foreground: #1a1a1a; --accent-foreground: #1a1a1a;
--destructive: #dc2626; --destructive: #dc2626;
--destructive-foreground: #ffffff; --destructive-foreground: #ffffff;
--border: #f0f0ed; --border: #e5e5e0;
--input: #eaeae6; --input: #dadad4;
--ring: #6c6c70; --ring: #9c9c97;
--sidebar: #ecece9; --sidebar: #f5f5f2;
--sidebar-foreground: #1a1a1a; --sidebar-foreground: #1a1a1a;
--sidebar-border: #e8e8e4; --sidebar-border: #e8e8e3;
--sidebar-accent: #ffffff; --sidebar-accent: #ffffff;
--sidebar-accent-foreground: #1a1a1a; --sidebar-accent-foreground: #1a1a1a;
--link: #2563eb; --link: #2563eb;
--success: #228b3b; --success: #1e7f37;
--warning: #b7791f; --warning: #a16207;
--overlay: rgba(20, 20, 22, 0.45); --overlay: rgba(24, 24, 22, 0.28);
--scrollbar: #c8c8c4; --scrollbar: #d3d3cd;
--scrollbar-hover: #a8a8a4; --scrollbar-hover: #b3b3ac;
--radius: 0.75rem; --radius: 0.75rem;
--font-size-caption: 0.75rem; --font-size-caption: 0.75rem;
--font-size-label: 0.84375rem; --font-size-label: 0.84375rem;

View File

@ -459,8 +459,8 @@ export function GrokShapePreview({
className={cn( className={cn(
"relative flex size-11 items-center justify-center rounded-xl transition-transform hover:scale-105 active:scale-95 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-popover", "relative flex size-11 items-center justify-center rounded-xl transition-transform hover:scale-105 active:scale-95 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-popover",
selected selected
? "ring-2 ring-primary ring-offset-2 ring-offset-popover bg-white/10" ? "ring-2 ring-primary ring-offset-2 ring-offset-popover bg-foreground/10"
: "hover:bg-white/5", : "hover:bg-foreground/5",
)} )}
> >
<EvooMascot pose="idle" size={32} trackPointer="none" /> <EvooMascot pose="idle" size={32} trackPointer="none" />

View File

@ -295,7 +295,7 @@ export const EvooMascot = memo(function EvooMascot({
style={{ height: size * 0.86, paddingBottom: size * 0.23 }} style={{ height: size * 0.86, paddingBottom: size * 0.23 }}
> >
<span <span
className="font-['EvOo_Display',sans-serif] font-bold tracking-tight text-emerald-400 leading-none inline-flex items-baseline select-none" className="font-['EvOo_Display',sans-serif] font-bold tracking-tight text-emerald-500 leading-none dark:text-emerald-400 inline-flex items-baseline select-none"
style={{ fontSize: size * 0.65 }} style={{ fontSize: size * 0.65 }}
> >
<span className="tracking-[-0.03em]">e</span> <span className="tracking-[-0.03em]">e</span>