From dde3da60fca63151fb10e37a779ad7f8ba2c5008 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Sun, 11 Oct 2026 17:50:47 +0300 Subject: [PATCH] Make Compliance follow VS Code themes --- .changeset/tidy-compliance-theme.md | 6 ++++ .../changes/compliance-vscode-theme/design.md | 29 +++++++++++++++++ .../compliance-vscode-theme/proposal.md | 29 +++++++++++++++++ .../specs/vscode-extension/spec.md | 32 +++++++++++++++++++ .../changes/compliance-vscode-theme/tasks.md | 19 +++++++++++ packages/webui/src/compliance-entry.tsx | 8 ++--- .../src/components/ComplianceApp.test.tsx | 31 ++++++++++++++++++ .../webui/src/components/ComplianceApp.tsx | 16 ++++++++++ 8 files changed, 165 insertions(+), 5 deletions(-) create mode 100644 .changeset/tidy-compliance-theme.md create mode 100644 openspec/changes/compliance-vscode-theme/design.md create mode 100644 openspec/changes/compliance-vscode-theme/proposal.md create mode 100644 openspec/changes/compliance-vscode-theme/specs/vscode-extension/spec.md create mode 100644 openspec/changes/compliance-vscode-theme/tasks.md create mode 100644 packages/webui/src/components/ComplianceApp.test.tsx create mode 100644 packages/webui/src/components/ComplianceApp.tsx diff --git a/.changeset/tidy-compliance-theme.md b/.changeset/tidy-compliance-theme.md new file mode 100644 index 00000000..b5ca4408 --- /dev/null +++ b/.changeset/tidy-compliance-theme.md @@ -0,0 +1,6 @@ +--- +"@openspec-ui/webui": patch +"openspec-ui-vscode": patch +--- + +Make the Compliance webview follow VS Code light, dark and high-contrast themes. \ No newline at end of file diff --git a/openspec/changes/compliance-vscode-theme/design.md b/openspec/changes/compliance-vscode-theme/design.md new file mode 100644 index 00000000..3fccf59b --- /dev/null +++ b/openspec/changes/compliance-vscode-theme/design.md @@ -0,0 +1,29 @@ +# Design + +## Decisions + +- Reuse `useEditorDarkTheme`, `metroRootClassName`, `metroCss`, + `metroIconsCss`, `shellThemeCss` and `vscodeThemeCss`, exactly as the + Harness Settings and Pipeline webviews do. +- Put theme ownership in a small shared Compliance webview root component so + the real entrypoint and the theme test exercise the same wrapper. +- Keep `ComplianceView` transport-agnostic and leave the standalone shell's + theme selection untouched. + +### Rejected Alternatives + +- Duplicating CSS variables or adding fixed light/dark colors is rejected: + ADR-0030 maps colors to the active editor theme, including custom themes. +- Fixing only the panel HTML is rejected: it cannot add the Metro scope class + or react to theme-class changes without duplicating the existing hook. + +## Non-Goals + +- No change to Compliance checks, policy, persistence, REST, or message bridge. +- No change to the standalone theme, Metro component selection, or npm publish. + +## Risks / Trade-offs + +- A wrapper regression could affect CSS scope. The focused test asserts both + the Metro root class and light/dark/high-contrast transitions, and the live + VS Code smoke continues to verify the panel opens. \ No newline at end of file diff --git a/openspec/changes/compliance-vscode-theme/proposal.md b/openspec/changes/compliance-vscode-theme/proposal.md new file mode 100644 index 00000000..2a3b170a --- /dev/null +++ b/openspec/changes/compliance-vscode-theme/proposal.md @@ -0,0 +1,29 @@ +# Proposal: Compliance Follows the VS Code Theme + +## Why + +The Compliance webview is the only VS Code webview that does not mount the +Metro root/theme adapter. It therefore misses the editor's light/dark/high- +contrast state even though ADR-0030 requires every VS Code webview to map its +palette from `--vscode-*` variables and follow theme changes while open. + +## What Changes + +- Wrap the Compliance webview in the shared VS Code Metro root and theme hook. +- Verify light, dark, high-contrast and live theme-switch behavior in a focused + webview component test and the existing isolated VS Code smoke. + +## Capabilities + +### New Capabilities + +None. + +### Modified Capabilities + +- `vscode-extension`: the Compliance webview follows the active editor theme. + +## Impact + +Only the extension's Compliance webview root and its focused tests change. +The standalone Compliance dialog continues to inherit its existing shell theme. \ No newline at end of file diff --git a/openspec/changes/compliance-vscode-theme/specs/vscode-extension/spec.md b/openspec/changes/compliance-vscode-theme/specs/vscode-extension/spec.md new file mode 100644 index 00000000..c61f2ce6 --- /dev/null +++ b/openspec/changes/compliance-vscode-theme/specs/vscode-extension/spec.md @@ -0,0 +1,32 @@ +## ADDED Requirements + +### Requirement: VS Code webviews follow the editor theme + +Every VS Code webview SHALL mount the shared Metro root class and map its +palette from VS Code theme variables. The webview SHALL follow editor light, +dark and high-contrast theme changes while it remains open. It SHALL NOT use +fixed product colors in place of the editor palette. + +#### Scenario: Compliance opens in a light editor theme + +- **WHEN** the Compliance webview opens with `vscode-light` on the body +- **THEN** its root includes `openspec-metro` and excludes `dark-side` +- **AND** its controls use the mapped VS Code theme variables + +#### Scenario: Compliance opens or changes to a dark editor theme + +- **WHEN** the Compliance webview opens with `vscode-dark` or the body changes + from light to dark while it is open +- **THEN** its root includes `dark-side` without reloading the panel + +#### Scenario: Compliance changes to a light or high-contrast-light theme + +- **WHEN** the editor body changes to `vscode-light` or + `vscode-high-contrast-light` +- **THEN** `dark-side` is removed while `openspec-metro` remains + +#### Scenario: Compliance changes to high-contrast dark + +- **WHEN** the editor body changes to `vscode-high-contrast` +- **THEN** `dark-side` is present and contrast colors remain sourced from + VS Code variables \ No newline at end of file diff --git a/openspec/changes/compliance-vscode-theme/tasks.md b/openspec/changes/compliance-vscode-theme/tasks.md new file mode 100644 index 00000000..2be68d7b --- /dev/null +++ b/openspec/changes/compliance-vscode-theme/tasks.md @@ -0,0 +1,19 @@ +## 1. Webview Theme + +- [x] 1.1 Add `packages/webui/src/components/ComplianceApp.tsx` to mount the + shared Metro root/theme wrapper; verify it renders `openspec-metro` and the + expected theme class in `ComplianceApp.test.tsx`. Evidence: the focused test + verifies light, dark, high-contrast and high-contrast-light class changes. +- [x] 1.2 Update `packages/webui/src/compliance-entry.tsx` to render + `ComplianceApp`; verify the Compliance browser bundle builds. +- [x] 1.3 Verify live body-class transitions for light, dark, high contrast and + high-contrast-light in `ComplianceApp.test.tsx` without reloading the app. +- [x] 1.4 Run the existing isolated VS Code Compliance smoke and record its + command and result; do not run the full extension suite locally. + Evidence: `node packages/extension/src/test/compliance-run.mjs ` + passed with exit code 0, verifying activation, command, panel tab and disposal + against the built theme-aware bundle. +- [x] 1.5 Run strict OpenSpec validation and affected webui/extension typechecks. + Evidence: 18 focused theme tests, both package typechecks, changeset check and + strict OpenSpec validation passed on 2026-10-11. `complianceWebviewBuildOptions` + also built `packages/extension/dist/compliance.js` successfully. \ No newline at end of file diff --git a/packages/webui/src/compliance-entry.tsx b/packages/webui/src/compliance-entry.tsx index 4fd337a8..17746ff2 100644 --- a/packages/webui/src/compliance-entry.tsx +++ b/packages/webui/src/compliance-entry.tsx @@ -1,8 +1,6 @@ import { createRoot } from "react-dom/client"; -import { ComplianceView, type ComplianceApi } from "./components/ComplianceView.js"; -import { shellThemeCss, vscodeThemeCss } from "./shell-ui.js"; -import { metroCss } from "./metro-css.generated.js"; -import { metroIconsCss } from "./metro-icons.generated.js"; +import { ComplianceApp } from "./components/ComplianceApp.js"; +import type { ComplianceApi } from "./components/ComplianceView.js"; import type { ComplianceReading } from "@openspec-ui/core/browser"; declare function acquireVsCodeApi(): { postMessage: (value: unknown) => void }; @@ -22,4 +20,4 @@ const api: ComplianceApi = request => new Promise((resolve, reject) => { }); const root = document.getElementById("root"); if (!root) throw new Error("Compliance root is missing"); -createRoot(root).render(<>); \ No newline at end of file +createRoot(root).render(); \ No newline at end of file diff --git a/packages/webui/src/components/ComplianceApp.test.tsx b/packages/webui/src/components/ComplianceApp.test.tsx new file mode 100644 index 00000000..eb69338f --- /dev/null +++ b/packages/webui/src/components/ComplianceApp.test.tsx @@ -0,0 +1,31 @@ +import { act, render, screen } from "@testing-library/react"; +import { expect, it, vi } from "vitest"; +import { ComplianceApp } from "./ComplianceApp.js"; +import type { ComplianceReading } from "@openspec-ui/core/browser"; + +const reading: ComplianceReading = { + generation: 0, + policy: { backgroundEnabled: false, global: {}, topics: {}, rules: {} }, + rules: [], + observations: [], + pending: 0, + nextAllowedAtMs: 0, +}; + +it("mounts the shared Metro root and follows live light, dark and high-contrast themes", async () => { + document.body.className = "vscode-light"; + render( reading)} />); + const root = screen.getByTestId("compliance-webview-root"); + + expect(root).toHaveClass("openspec-metro"); + expect(root).not.toHaveClass("dark-side"); + + await act(async () => { document.body.className = "vscode-dark"; }); + expect(root).toHaveClass("dark-side"); + + await act(async () => { document.body.className = "vscode-high-contrast"; }); + expect(root).toHaveClass("dark-side"); + + await act(async () => { document.body.className = "vscode-high-contrast vscode-high-contrast-light"; }); + expect(root).not.toHaveClass("dark-side"); +}); \ No newline at end of file diff --git a/packages/webui/src/components/ComplianceApp.tsx b/packages/webui/src/components/ComplianceApp.tsx new file mode 100644 index 00000000..8134e90a --- /dev/null +++ b/packages/webui/src/components/ComplianceApp.tsx @@ -0,0 +1,16 @@ +import { useEditorDarkTheme, metroRootClassName } from "../vscode-theme.js"; +import { shellThemeCss, vscodeThemeCss } from "../shell-ui.js"; +import { metroCss } from "../metro-css.generated.js"; +import { metroIconsCss } from "../metro-icons.generated.js"; +import { ComplianceView, type ComplianceApi } from "./ComplianceView.js"; + +export function ComplianceApp({ api }: { api: ComplianceApi }) { + const editorDark = useEditorDarkTheme(); + + return ( +
+ + +
+ ); +} \ No newline at end of file