Skip to content

Commit 51a2993

Browse files
committed
feat(web): sync workspace background material system on develop
1 parent 4f2e4df commit 51a2993

13 files changed

Lines changed: 292 additions & 213 deletions

File tree

packages/web/src/features/code-editor/components/monaco-diff-host.test.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { render, screen } from "@testing-library/react";
22
import { createStore, Provider } from "jotai";
33
import { describe, expect, it, vi } from "vitest";
4+
import { getThemeById } from "../../../theme";
45
import { MonacoDiffHost } from "./monaco-diff-host";
56

67
const {
@@ -78,6 +79,16 @@ describe("MonacoDiffHost", () => {
7879
);
7980

8081
expect(mockCreateDiffEditor).toHaveBeenCalled();
82+
expect(mockDefineTheme).toHaveBeenCalledWith(
83+
"coder-studio-workspace-mint-dark",
84+
expect.objectContaining({
85+
...getThemeById("mint-dark").monaco,
86+
colors: expect.objectContaining({
87+
...getThemeById("mint-dark").monaco.colors,
88+
"editor.background": "#00000000",
89+
}),
90+
})
91+
);
8192
expect(mockSetModel).toHaveBeenCalledWith({
8293
original: mockOriginalModel,
8394
modified: mockModifiedModel,

packages/web/src/features/code-editor/components/monaco-diff-host.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import tsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker"
88
import type { FC } from "react";
99
import { useEffect, useRef } from "react";
1010
import { themeAtom } from "../../../atoms/app-ui";
11-
import { getThemeById } from "../../../theme";
11+
import { createWorkspaceMonacoTheme, getThemeById } from "../../../theme";
1212

1313
const monacoGlobal = globalThis as typeof globalThis & {
1414
MonacoEnvironment?: monaco.Environment;
@@ -43,14 +43,15 @@ export const MonacoDiffHost: FC<MonacoDiffHostProps> = ({
4343
const originalModelRef = useRef<monaco.editor.ITextModel | null>(null);
4444
const modifiedModelRef = useRef<monaco.editor.ITextModel | null>(null);
4545
const resolvedTheme = getThemeById(uiTheme);
46-
const editorTheme = `coder-studio-${resolvedTheme.id}`;
46+
const editorTheme = `coder-studio-workspace-${resolvedTheme.id}`;
47+
const monacoTheme = createWorkspaceMonacoTheme(resolvedTheme.monaco);
4748

4849
useEffect(() => {
4950
monaco.editor.defineTheme(
5051
editorTheme,
51-
resolvedTheme.monaco as Parameters<typeof monaco.editor.defineTheme>[1]
52+
monacoTheme as Parameters<typeof monaco.editor.defineTheme>[1]
5253
);
53-
}, [editorTheme, resolvedTheme]);
54+
}, [editorTheme, monacoTheme]);
5455

5556
useEffect(() => {
5657
if (!containerRef.current || editorRef.current) {

packages/web/src/features/code-editor/components/monaco-host.test.tsx

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -339,11 +339,42 @@ describe("MonacoHost", () => {
339339
);
340340

341341
await waitFor(() => {
342-
expect(mockDefineTheme).toHaveBeenCalledWith("coder-studio-mint-light", theme.monaco);
342+
expect(mockDefineTheme).toHaveBeenCalledWith(
343+
"coder-studio-workspace-mint-light",
344+
expect.objectContaining({
345+
...theme.monaco,
346+
colors: expect.objectContaining({
347+
...theme.monaco.colors,
348+
"editor.background": "#00000000",
349+
}),
350+
})
351+
);
343352
expect(mockCreateEditor).toHaveBeenCalledWith(
344353
expect.any(HTMLDivElement),
345354
expect.objectContaining({
346355
readOnly: false,
356+
theme: "coder-studio-workspace-mint-light",
357+
})
358+
);
359+
});
360+
});
361+
362+
it("keeps standalone Monaco themes opaque for non-workspace editors", async () => {
363+
const store = createStore();
364+
store.set(themeAtom, "mint-light");
365+
const theme = getThemeById("mint-light");
366+
367+
render(
368+
<Provider store={store}>
369+
<MonacoHost filePath="settings.json" content='{"theme":"mint-light"}' standalone />
370+
</Provider>
371+
);
372+
373+
await waitFor(() => {
374+
expect(mockDefineTheme).toHaveBeenCalledWith("coder-studio-mint-light", theme.monaco);
375+
expect(mockCreateEditor).toHaveBeenCalledWith(
376+
expect.any(HTMLDivElement),
377+
expect.objectContaining({
347378
theme: "coder-studio-mint-light",
348379
})
349380
);
@@ -432,10 +463,16 @@ describe("MonacoHost", () => {
432463

433464
await waitFor(() => {
434465
expect(mockDefineTheme).toHaveBeenCalledWith(
435-
"coder-studio-graphite-light",
436-
getThemeById("graphite-light").monaco
466+
"coder-studio-workspace-graphite-light",
467+
expect.objectContaining({
468+
...getThemeById("graphite-light").monaco,
469+
colors: expect.objectContaining({
470+
...getThemeById("graphite-light").monaco.colors,
471+
"editor.background": "#00000000",
472+
}),
473+
})
437474
);
438-
expect(mockSetTheme).toHaveBeenCalledWith("coder-studio-graphite-light");
475+
expect(mockSetTheme).toHaveBeenCalledWith("coder-studio-workspace-graphite-light");
439476
});
440477
});
441478

packages/web/src/features/code-editor/components/monaco-host.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import type { FC } from "react";
1818
import { useEffect, useRef, useState } from "react";
1919
import { themeAtom } from "../../../atoms/app-ui";
2020
import { dispatchCommandAtom, wsClientAtom } from "../../../atoms/connection";
21-
import { getThemeById } from "../../../theme";
21+
import { createWorkspaceMonacoTheme, getThemeById } from "../../../theme";
2222
import { useOpenLocation } from "../actions/use-open-location";
2323
import { type PendingEditorNavigation, pendingEditorNavigationAtomFamily } from "../atoms";
2424
import { globalLspBridge, type LspBridgeState } from "../lsp/bridge";
@@ -156,17 +156,22 @@ export const MonacoHost: FC<MonacoHostProps> = ({
156156
const editorLanguage = detectEditorLanguage(filePath);
157157
const lspLanguage = detectLspLanguage(filePath, editorLanguage);
158158
const resolvedTheme = getThemeById(uiTheme);
159-
const editorTheme = `coder-studio-${resolvedTheme.id}`;
159+
const editorTheme = isWorkspaceBacked
160+
? `coder-studio-workspace-${resolvedTheme.id}`
161+
: `coder-studio-${resolvedTheme.id}`;
162+
const monacoTheme = isWorkspaceBacked
163+
? createWorkspaceMonacoTheme(resolvedTheme.monaco)
164+
: resolvedTheme.monaco;
160165

161166
useEffect(() => {
162167
if (!registeredMonacoThemeIds.has(editorTheme)) {
163168
monaco.editor.defineTheme(
164169
editorTheme,
165-
resolvedTheme.monaco as Parameters<typeof monaco.editor.defineTheme>[1]
170+
monacoTheme as Parameters<typeof monaco.editor.defineTheme>[1]
166171
);
167172
registeredMonacoThemeIds.add(editorTheme);
168173
}
169-
}, [editorTheme, resolvedTheme]);
174+
}, [editorTheme, monacoTheme]);
170175

171176
useEffect(() => {
172177
if (!containerRef.current || editorRef.current) return;

packages/web/src/features/terminal-panel/__tests__/xterm-host.test.tsx

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,7 +1202,10 @@ describe("XtermHost", () => {
12021202
const { Terminal } = await import("@xterm/xterm");
12031203
expect(Terminal).toHaveBeenCalledWith(
12041204
expect.objectContaining({
1205-
theme: expect.objectContaining(getThemeById("mint-light").terminalTheme),
1205+
theme: expect.objectContaining({
1206+
...getThemeById("mint-light").terminalTheme,
1207+
background: "transparent",
1208+
}),
12061209
})
12071210
);
12081211
});
@@ -2700,7 +2703,10 @@ describe("XtermHost", () => {
27002703

27012704
expect(Terminal).toHaveBeenCalledWith(
27022705
expect.objectContaining({
2703-
theme: expect.objectContaining(getThemeById("mint-dark").terminalTheme),
2706+
theme: expect.objectContaining({
2707+
...getThemeById("mint-dark").terminalTheme,
2708+
background: "transparent",
2709+
}),
27042710
})
27052711
);
27062712
});
@@ -2718,7 +2724,10 @@ describe("XtermHost", () => {
27182724

27192725
expect(Terminal).toHaveBeenCalledWith(
27202726
expect.objectContaining({
2721-
theme: expect.objectContaining(getThemeById("mint-light").terminalTheme),
2727+
theme: expect.objectContaining({
2728+
...getThemeById("mint-light").terminalTheme,
2729+
background: "transparent",
2730+
}),
27222731
})
27232732
);
27242733
});
@@ -2775,7 +2784,10 @@ describe("XtermHost", () => {
27752784
await waitFor(() => {
27762785
expect(mockTerminal.options).toEqual(
27772786
expect.objectContaining({
2778-
theme: expect.objectContaining(getThemeById("graphite-light").terminalTheme),
2787+
theme: expect.objectContaining({
2788+
...getThemeById("graphite-light").terminalTheme,
2789+
background: "transparent",
2790+
}),
27792791
})
27802792
);
27812793
});
@@ -2835,7 +2847,10 @@ describe("XtermHost", () => {
28352847

28362848
expect(Terminal).toHaveBeenCalledWith(
28372849
expect.objectContaining({
2838-
theme: expect.objectContaining(getThemeById("hc-dark").terminalTheme),
2850+
theme: expect.objectContaining({
2851+
...getThemeById("hc-dark").terminalTheme,
2852+
background: "transparent",
2853+
}),
28392854
})
28402855
);
28412856
});

packages/web/src/features/terminal-panel/views/shared/xterm-host.tsx

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ import {
2525
useRef,
2626
useState,
2727
} from "react";
28-
import { resolveAppearancePersonalizationForViewport } from "../../../../appearance";
29-
import { appearancePersonalizationAtom, themeAtom } from "../../../../atoms/app-ui";
28+
import { themeAtom } from "../../../../atoms/app-ui";
3029
import { dispatchCommandAtom, wsClientAtom } from "../../../../atoms/connection";
3130
import { Button, LocalOverlay, Notice } from "../../../../components/ui";
3231
import { useViewport } from "../../../../hooks/use-viewport";
@@ -326,12 +325,8 @@ export function trimWrittenChunks(buffer: OutputBuffer, writtenChunkCount: numbe
326325
};
327326
}
328327

329-
function resolveXtermTheme(themeId: string, glassEnabled: boolean): TerminalThemeDefinition {
328+
function resolveXtermTheme(themeId: string): TerminalThemeDefinition {
330329
const terminalTheme = getThemeById(themeId).terminalTheme;
331-
if (!glassEnabled) {
332-
return terminalTheme;
333-
}
334-
335330
return {
336331
...terminalTheme,
337332
background: "transparent",
@@ -416,7 +411,6 @@ export function XtermHost({
416411
const t = useTranslation();
417412
const viewport = useViewport();
418413
const uiTheme = useAtomValue(themeAtom);
419-
const appearancePersonalization = useAtomValue(appearancePersonalizationAtom);
420414
const terminalPreferences = useAtomValue(terminalPreferencesAtom);
421415
const terminalFontSize = getTerminalFontSizeForViewport(terminalPreferences, viewport);
422416
const wsClient = useAtomValue(wsClientAtom);
@@ -525,13 +519,7 @@ export function XtermHost({
525519

526520
return wsClient.getStatus();
527521
});
528-
const effectiveAppearance = resolveAppearancePersonalizationForViewport(
529-
appearancePersonalization,
530-
viewport
531-
);
532-
const glassEnabled =
533-
effectiveAppearance.glassEnabled && uiTheme !== "hc-dark" && uiTheme !== "hc-light";
534-
const resolvedTerminalTheme = resolveXtermTheme(uiTheme, glassEnabled);
522+
const resolvedTerminalTheme = resolveXtermTheme(uiTheme);
535523

536524
// Latest copies of callback identities used inside the mount effect, exposed
537525
// via refs so the effect's cleanup/re-creation is not tied to their churn.
@@ -1380,7 +1368,7 @@ export function XtermHost({
13801368
// characters used by TUIs (claude, codex) render as a continuous frame
13811369
// with no gaps between rows.
13821370
const terminal = new Terminal({
1383-
theme: resolveXtermTheme(initialThemeRef.current, glassEnabled),
1371+
theme: resolveXtermTheme(initialThemeRef.current),
13841372
fontFamily: "JetBrains Mono, Fira Code, SF Mono, monospace",
13851373
fontSize: terminalFontSize,
13861374
scrollback: 5000,

0 commit comments

Comments
 (0)