Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
334bac7
fix(webview): preserve contrast in themed content
roomote Aug 22, 2026
8e52cc2
test(webview): add rendered theme baselines
roomote Aug 22, 2026
7ad34f7
test(webview): add screen theme baselines
roomote Aug 22, 2026
aadaf9f
fix(webview): keep syntax colors above WCAG threshold
roomote Aug 22, 2026
3cd89a3
fix(webview): keep code backgrounds deterministic
roomote Aug 22, 2026
b154a37
test(webview): audit syntax tokens directly
roomote Aug 22, 2026
dcf46af
test(webview): refresh rendered content baselines
roomote Aug 22, 2026
a696af1
fix(webview): refresh code colors after theme changes
roomote Aug 22, 2026
ba56dbc
test(webview): prevent visual snapshots from clipping
roomote Aug 22, 2026
e6eebe5
test(webview): refresh unclipped visual baselines
roomote Aug 22, 2026
96c3727
fix(webview): keep code and buttons visible in contrast themes
roomote Aug 22, 2026
43832c8
test(webview): refresh visible contrast baselines
roomote Aug 22, 2026
b5cfe48
test(webview): refresh focused control baselines
roomote Aug 22, 2026
be5416d
fix(webview): keep controls and unchanged diffs visible
roomote Aug 22, 2026
3815d71
test(webview): refresh readable diff baselines
roomote Aug 22, 2026
0842347
test(webview): frame control snapshots with panel padding
roomote Aug 23, 2026
0f90a56
test(webview): add padded control baselines
roomote Aug 23, 2026
7cb202d
test(webview): cover layout drift modes
roomote Aug 23, 2026
9f75096
test(webview): add layout drift baselines
roomote Aug 23, 2026
248b076
test(webview): defer layout drift matrix
roomote Aug 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions webview-ui/playwright/vscode-theme-base.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,20 @@ body {
padding: 16px;
}

.vscode-text-field-mock {
box-sizing: border-box;
background: var(--vscode-input-background);
border: 1px solid var(--vscode-input-border, transparent);
color: var(--vscode-input-foreground);
font: inherit;
padding: 4px 6px;
}

.vscode-text-field-mock::placeholder {
color: var(--vscode-input-placeholderForeground, var(--vscode-descriptionForeground));
opacity: 1;
}

/* @vscode/webview-ui-toolkit/react renders VSCodeButton as a bare
<button appearance="primary|secondary"> outside VS Code's web-component
runtime. Style it here so button screenshots match the real appearance. */
Expand Down
12 changes: 9 additions & 3 deletions webview-ui/src/__mocks__/@vscode/webview-ui-toolkit/react.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,15 @@ export const VSCodeLink = ({ children, href, ...props }: any) => (
</a>
)

export const VSCodeTextField = ({ value, onInput, "data-testid": dataTestId, children, ...props }: any) => (
<div>
<input data-testid={dataTestId} value={value} onInput={onInput} {...props} />
export const VSCodeTextField = ({ value, onInput, "data-testid": dataTestId, children, className, ...props }: any) => (
<div className={className}>
<input
className="vscode-text-field-mock w-full"
data-testid={dataTestId}
value={value}
onInput={onInput}
{...props}
/>
{children}
</div>
)
Expand Down
17 changes: 11 additions & 6 deletions webview-ui/src/components/chat/CommandPatternSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,15 @@ export const CommandPatternSelector: React.FC<CommandPatternSelectorProps> = ({
<span
className={cn(
"text-sm flex-1 group-hover:opacity-100",
isExpanded ? "opacity-100" : "opacity-40",
isExpanded ? "text-vscode-foreground" : "text-vscode-descriptionForeground",
)}>
<CheckCheck className="size-3 inline-block mr-2" />
{t("chat:commandExecution.manageCommands")}
</span>
<ChevronUp
className={cn(
"group-hover:opacity-100 size-4 transition-transform",
isExpanded ? "opacity-100" : "opacity-40 -rotate-180",
isExpanded ? "text-vscode-foreground" : "text-vscode-descriptionForeground -rotate-180",
)}
/>
</div>
Expand Down Expand Up @@ -135,12 +135,14 @@ export const CommandPatternSelector: React.FC<CommandPatternSelectorProps> = ({
)}>
<button
className={cn("p-1 rounded transition-all cursor-pointer", {
"bg-green-500/20 text-green-500 hover:bg-green-500/30":
"bg-vscode-list-activeSelectionBackground text-vscode-list-activeSelectionForeground hover:bg-vscode-list-focusBackground":
status === "allowed",
"text-vscode-descriptionForeground hover:text-green-500 hover:bg-green-500/10":
"text-vscode-descriptionForeground hover:text-vscode-foreground hover:bg-vscode-list-hoverBackground":
status !== "allowed",
})}
onClick={() => onAllowPatternChange(editState.value)}
aria-pressed={status === "allowed"}
data-testid="allow-command-pattern"
aria-label={t(
status === "allowed"
? "chat:commandExecution.removeFromAllowed"
Expand All @@ -157,11 +159,14 @@ export const CommandPatternSelector: React.FC<CommandPatternSelectorProps> = ({
)}>
<button
className={cn("p-1 rounded transition-all cursor-pointer", {
"bg-red-500/20 text-red-500 hover:bg-red-500/30": status === "denied",
"text-vscode-descriptionForeground hover:text-red-500 hover:bg-red-500/10":
"bg-vscode-list-activeSelectionBackground text-vscode-list-activeSelectionForeground hover:bg-vscode-list-focusBackground":
status === "denied",
"text-vscode-descriptionForeground hover:text-vscode-foreground hover:bg-vscode-list-hoverBackground":
status !== "denied",
})}
onClick={() => onDenyPatternChange(editState.value)}
aria-pressed={status === "denied"}
data-testid="deny-command-pattern"
aria-label={t(
status === "denied"
? "chat:commandExecution.removeFromDenied"
Expand Down
12 changes: 6 additions & 6 deletions webview-ui/src/components/chat/ContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ const ContextMenu: React.FC<ContextMenuProps> = ({
style={{
fontWeight: "bold",
fontSize: "0.85em",
opacity: 0.8,
color: "var(--vscode-descriptionForeground)",
}}>
{option.label}
</span>
Expand All @@ -95,7 +95,7 @@ const ContextMenu: React.FC<ContextMenuProps> = ({
{option.description && (
<span
style={{
opacity: 0.5,
color: "var(--vscode-descriptionForeground)",
fontSize: "0.9em",
lineHeight: "1.2",
whiteSpace: "nowrap",
Expand All @@ -115,7 +115,7 @@ const ContextMenu: React.FC<ContextMenuProps> = ({
{option.argumentHint && (
<span
style={{
opacity: 0.5,
color: "var(--vscode-descriptionForeground)",
fontSize: "0.9em",
lineHeight: "1.2",
}}>
Expand All @@ -126,7 +126,7 @@ const ContextMenu: React.FC<ContextMenuProps> = ({
{option.description && (
<span
style={{
opacity: 0.5,
color: "var(--vscode-descriptionForeground)",
fontSize: "0.9em",
lineHeight: "1.2",
whiteSpace: "nowrap",
Expand Down Expand Up @@ -154,7 +154,7 @@ const ContextMenu: React.FC<ContextMenuProps> = ({
<span
style={{
fontSize: "0.85em",
opacity: 0.7,
color: "var(--vscode-descriptionForeground)",
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
Expand Down Expand Up @@ -197,7 +197,7 @@ const ContextMenu: React.FC<ContextMenuProps> = ({
direction: "rtl",
textAlign: "right",
flex: 1,
opacity: 0.75,
color: "var(--vscode-descriptionForeground)",
fontSize: "0.75em",
}}>
{folderPath}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from "react"

import McpToolRow from "../../mcp/McpToolRow"
import { CommandPatternSelector } from "../CommandPatternSelector"

export function ThemeSensitiveStatusFixture() {
return (
<main
data-testid="status-surface"
className="w-[480px] space-y-5 rounded-lg border border-vscode-panel-border bg-vscode-editor-background p-4 text-vscode-editor-foreground">
<section aria-labelledby="commands-title" className="space-y-2">
<h2 id="commands-title" className="m-0 text-base font-semibold">
Command permissions
</h2>
<CommandPatternSelector
patterns={[
{ pattern: "pnpm test", description: "Run the test suite" },
{ pattern: "rm -rf", description: "Remove files recursively" },
]}
allowedCommands={["pnpm test"]}
deniedCommands={["rm -rf"]}
onAllowPatternChange={() => undefined}
onDenyPatternChange={() => undefined}
/>
</section>

<section aria-labelledby="mcp-title" className="space-y-2">
<h2 id="mcp-title" className="m-0 text-base font-semibold">
MCP tools
</h2>
<McpToolRow
tool={{
name: "search_workspace",
description: "Search files and symbols in the current workspace",
enabledForPrompt: false,
inputSchema: { type: "object", properties: {} },
}}
serverName="workspace-tools"
/>
</section>
</main>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from "react"

import { expect, test } from "../../../../playwright/coverage-fixture"
import { AppProviders } from "../../../../playwright/AppProviders"
import { expectContrast } from "../../../../playwright/contrast"
import { applyVisualTheme, visualThemes } from "../../../../playwright/themes"
import { ThemeSensitiveStatusFixture } from "./ThemeSensitiveStatus.visual.fixture"

for (const theme of visualThemes) {
test(`audits status controls in the VS Code ${theme.name} theme`, async ({ mount, page }) => {
await applyVisualTheme(page, theme)
const component = await mount(
<AppProviders>
<ThemeSensitiveStatusFixture />
</AppProviders>,
)
const surface = component.getByTestId("status-surface")

await component.getByRole("button", { name: /auto-approved commands/i }).click()
const allowButton = component.getByTestId("allow-command-pattern").first()
const denyButton = component.getByTestId("deny-command-pattern").nth(1)
await expect(allowButton).toHaveAttribute("aria-pressed", "true")
await expect(denyButton).toHaveAttribute("aria-pressed", "true")
await expectContrast(allowButton, {
background: allowButton,
minimum: 3,
label: `${theme.name} allowed command indicator`,
})
await expectContrast(denyButton, {
background: denyButton,
minimum: 3,
label: `${theme.name} denied command indicator`,
})
await expectContrast(component.getByText("Search files and symbols in the current workspace"), {
background: surface,
label: `${theme.name} disabled MCP description`,
})

await expect(component).toHaveScreenshot(`theme-sensitive-status-${theme.name}.png`)
})
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 5 additions & 8 deletions webview-ui/src/components/chat/checkpoints/CheckpointSaved.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,14 @@ export const CheckpointSaved = ({
className="flex items-center justify-between gap-2 pt-2 pb-3"
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}>
<div className="flex items-center gap-2 text-blue-400 whitespace-nowrap">
<div className="flex items-center gap-2 text-vscode-textLink-foreground whitespace-nowrap">
<GitCommitVertical className="w-4" />
<span className="font-semibold">{t("chat:checkpoint.regular")}</span>
{isCurrent && <span className="text-muted">({t("chat:checkpoint.current")})</span>}
{isCurrent && (
<span className="text-vscode-descriptionForeground">({t("chat:checkpoint.current")})</span>
)}
</div>
<span
className="block w-full h-[2px] mt-[2px] text-xs"
style={{
backgroundImage:
"linear-gradient(90deg, rgba(0, 188, 255, .65), rgba(0, 188, 255, .65) 80%, rgba(0, 188, 255, 0) 99%)",
}}></span>
<span className="block w-full h-[2px] mt-[2px] bg-vscode-textLink-foreground text-xs" aria-hidden="true" />

{/* Keep menu visible while hovering, popover is open, or briefly after close to prevent jump */}
<div data-testid="checkpoint-menu-container" className={cn("h-4 -mt-2", menuVisible ? "block" : "hidden")}>
Expand Down
Loading
Loading