Skip to content

Commit a8e096f

Browse files
feat: sync App Inspector theme with VS Code color theme
Detects VS Code's current theme from the webview body's `data-vscode-theme-kind` attribute (and `vscode-*` classes) and forwards changes to LocalStackThemeProvider via the integrations event system, so the App Inspector follows the user's VS Code theme instead of only the OS `prefers-color-scheme` default. DRG-725 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 5cab2f6 commit a8e096f

1 file changed

Lines changed: 62 additions & 3 deletions

File tree

src/app-inspector/main.tsx

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@ import {
33
AppInspectorContextProvider,
44
pages,
55
} from "@localstack/appinspector-ui";
6-
import { LocalStackThemeProvider } from "@localstack/integrations";
7-
import { StrictMode } from "react";
6+
import {
7+
eventSystem,
8+
LocalStackEventType,
9+
LocalStackThemeProvider,
10+
ThemeType,
11+
} from "@localstack/integrations";
12+
import { StrictMode, useEffect } from "react";
813
import { render } from "react-dom";
914
import {
1015
HashRouter,
@@ -15,6 +20,56 @@ import {
1520
Routes,
1621
} from "react-router-dom";
1722

23+
/**
24+
* Reads VS Code's current theme from the webview's body element. VS Code
25+
* tags the body with `data-vscode-theme-kind` (and matching `vscode-*`
26+
* classes) and updates them when the user changes their color theme.
27+
* Returns undefined outside a VS Code webview, so the theme provider can
28+
* fall back to the OS `prefers-color-scheme` default.
29+
*/
30+
function detectVSCodeTheme(): ThemeType | undefined {
31+
const kind =
32+
document.body.dataset.vscodeThemeKind ??
33+
(document.body.classList.contains("vscode-dark")
34+
? "vscode-dark"
35+
: document.body.classList.contains("vscode-high-contrast")
36+
? "vscode-high-contrast"
37+
: document.body.classList.contains("vscode-high-contrast-light")
38+
? "vscode-high-contrast-light"
39+
: document.body.classList.contains("vscode-light")
40+
? "vscode-light"
41+
: undefined);
42+
if (kind === "vscode-dark" || kind === "vscode-high-contrast") {
43+
return ThemeType.DARK;
44+
}
45+
if (kind === "vscode-light" || kind === "vscode-high-contrast-light") {
46+
return ThemeType.LIGHT;
47+
}
48+
return undefined;
49+
}
50+
51+
const VSCodeThemeSync = () => {
52+
useEffect(() => {
53+
let lastTheme = detectVSCodeTheme();
54+
const observer = new MutationObserver(() => {
55+
const next = detectVSCodeTheme();
56+
if (next && next !== lastTheme) {
57+
lastTheme = next;
58+
eventSystem.notify({
59+
eventType: LocalStackEventType.THEME_UPDATE,
60+
data: { theme: next },
61+
});
62+
}
63+
});
64+
observer.observe(document.body, {
65+
attributes: true,
66+
attributeFilter: ["class", "data-vscode-theme-kind"],
67+
});
68+
return () => observer.disconnect();
69+
}, []);
70+
return null;
71+
};
72+
1873
/* Passed by the VS Code extension when App Inspector is launched. */
1974
declare global {
2075
interface Window {
@@ -46,7 +101,11 @@ render(
46101
padding: "24px",
47102
}}
48103
>
49-
<LocalStackThemeProvider useExtensionLayout={false}>
104+
<VSCodeThemeSync />
105+
<LocalStackThemeProvider
106+
themeType={detectVSCodeTheme()}
107+
useExtensionLayout={false}
108+
>
50109
<AppInspectorContextProvider
51110
deploymentContainer={deploymentContainer}
52111
linkComponent={(props) => (

0 commit comments

Comments
 (0)