Skip to content

Commit b2b9432

Browse files
authored
fix terminal theming when applying global defaults #1287 (#1332)
1 parent 9bbe87f commit b2b9432

5 files changed

Lines changed: 48 additions & 61 deletions

File tree

docs/docs/releasenotes.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ New minor release that introduces Wave's connected computing extensions. We've i
2525
- [bugfix] Presets directory was not loading correctly on Windows
2626
- [bugfix] Magnified blocks were not showing correct on startup
2727
- [bugfix] Window opacity and background color was not getting applied properly in all cases
28+
- [bugfix] Fix terminal theming when applying global defaults [#1287](https://github.com/wavetermdev/waveterm/issues/1287)
2829
- MacOS 10.15 (Catalina) is no longer supported
2930
- Other bug fixes, docs improvements, and dependency bumps
3031

frontend/app/store/global.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ function useBlockMetaKeyAtom<T extends keyof MetaType>(blockId: string, key: T):
246246

247247
const settingsAtomCache = new Map<string, Atom<any>>();
248248

249-
function makeOverrideConfigAtom<T extends keyof SettingsType>(blockId: string, key: T): Atom<SettingsType[T]> {
249+
function getOverrideConfigAtom<T extends keyof SettingsType>(blockId: string, key: T): Atom<SettingsType[T]> {
250250
const blockCache = getSingleBlockAtomCache(blockId);
251251
const overrideAtomName = "#settingsoverride-" + key;
252252
let overrideAtom = blockCache.get(overrideAtomName);
@@ -271,7 +271,7 @@ function makeOverrideConfigAtom<T extends keyof SettingsType>(blockId: string, k
271271
}
272272

273273
function useOverrideConfigAtom<T extends keyof SettingsType>(blockId: string, key: T): SettingsType[T] {
274-
return useAtomValue(makeOverrideConfigAtom(blockId, key));
274+
return useAtomValue(getOverrideConfigAtom(blockId, key));
275275
}
276276

277277
function getSettingsKeyAtom<T extends keyof SettingsType>(key: T): Atom<SettingsType[T]> {
@@ -636,14 +636,14 @@ export {
636636
getConnStatusAtom,
637637
getHostName,
638638
getObjectId,
639+
getOverrideConfigAtom,
639640
getSettingsKeyAtom,
640641
getUserName,
641642
globalStore,
642643
initGlobal,
643644
initGlobalWaveEventSubs,
644645
isDev,
645646
loadConnStatus,
646-
makeOverrideConfigAtom,
647647
openLink,
648648
PLATFORM,
649649
pushFlashError,

frontend/app/view/term/term.tsx

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@ import { DefaultRouter, TabRpcClient } from "@/app/store/wshrpcutil";
1111
import { TermWshClient } from "@/app/view/term/term-wsh";
1212
import { VDomModel } from "@/app/view/vdom/vdom-model";
1313
import {
14-
WOS,
1514
atoms,
1615
getBlockComponentModel,
16+
getBlockMetaKeyAtom,
1717
getConnStatusAtom,
18+
getOverrideConfigAtom,
1819
getSettingsKeyAtom,
1920
globalStore,
2021
useBlockAtom,
2122
useSettingsPrefixAtom,
23+
WOS,
2224
} from "@/store/global";
2325
import * as services from "@/store/services";
2426
import * as keyutil from "@/util/keyutil";
@@ -28,7 +30,7 @@ import * as jotai from "jotai";
2830
import * as React from "react";
2931
import { TermStickers } from "./termsticker";
3032
import { TermThemeUpdater } from "./termtheme";
31-
import { computeTheme } from "./termutil";
33+
import { computeTheme, DefaultTermTheme } from "./termutil";
3234
import { TermWrap } from "./termwrap";
3335
import "./xterm.css";
3436

@@ -138,16 +140,17 @@ class TermViewModel {
138140
}
139141
return true;
140142
});
143+
this.termThemeNameAtom = useBlockAtom(blockId, "termthemeatom", () => {
144+
return jotai.atom<string>((get) => {
145+
return get(getOverrideConfigAtom(this.blockId, "term:theme")) ?? DefaultTermTheme;
146+
});
147+
});
141148
this.blockBg = jotai.atom((get) => {
142-
const blockData = get(this.blockAtom);
143149
const fullConfig = get(atoms.fullConfigAtom);
144-
let themeName: string = get(getSettingsKeyAtom("term:theme"));
145-
if (blockData?.meta?.["term:theme"]) {
146-
themeName = blockData.meta["term:theme"];
147-
}
148-
const theme = computeTheme(fullConfig, themeName);
149-
if (theme != null && theme.background != null) {
150-
return { bg: theme.background };
150+
const themeName = get(this.termThemeNameAtom);
151+
const [_, bgcolor] = computeTheme(fullConfig, themeName);
152+
if (bgcolor != null) {
153+
return { bg: bgcolor };
151154
}
152155
return null;
153156
});
@@ -169,13 +172,6 @@ class TermViewModel {
169172
return rtnFontSize;
170173
});
171174
});
172-
this.termThemeNameAtom = useBlockAtom(blockId, "termthemeatom", () => {
173-
return jotai.atom<string>((get) => {
174-
const blockData = get(this.blockAtom);
175-
const settingsKeyAtom = getSettingsKeyAtom("term:theme");
176-
return blockData?.meta?.["term:theme"] ?? get(settingsKeyAtom) ?? "default-dark";
177-
});
178-
});
179175
this.noPadding = jotai.atom(true);
180176
this.endIconButtons = jotai.atom((get) => {
181177
const blockData = get(this.blockAtom);
@@ -329,7 +325,7 @@ class TermViewModel {
329325
const fullConfig = globalStore.get(atoms.fullConfigAtom);
330326
const termThemes = fullConfig?.termthemes ?? {};
331327
const termThemeKeys = Object.keys(termThemes);
332-
const curThemeName = globalStore.get(this.termThemeNameAtom);
328+
const curThemeName = globalStore.get(getBlockMetaKeyAtom(this.blockId, "term:theme"));
333329
const defaultFontSize = globalStore.get(getSettingsKeyAtom("term:fontsize")) ?? 12;
334330
const blockData = globalStore.get(this.blockAtom);
335331
const overrideFontSize = blockData?.meta?.["term:fontsize"];
@@ -346,6 +342,12 @@ class TermViewModel {
346342
click: () => this.setTerminalTheme(themeName),
347343
};
348344
});
345+
submenu.unshift({
346+
label: "Default",
347+
type: "checkbox",
348+
checked: curThemeName == null,
349+
click: () => this.setTerminalTheme(null),
350+
});
349351
const fontSizeSubMenu: ContextMenuItem[] = [6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18].map(
350352
(fontSize: number) => {
351353
return {
@@ -551,9 +553,8 @@ const TerminalView = ({ blockId, model }: TerminalViewProps) => {
551553

552554
React.useEffect(() => {
553555
const fullConfig = globalStore.get(atoms.fullConfigAtom);
554-
const termTheme = computeTheme(fullConfig, blockData?.meta?.["term:theme"]);
555-
const themeCopy = { ...termTheme };
556-
themeCopy.background = "#00000000";
556+
const termThemeName = globalStore.get(model.termThemeNameAtom);
557+
const [termTheme, _] = computeTheme(fullConfig, termThemeName);
557558
let termScrollback = 1000;
558559
if (termSettings?.["term:scrollback"]) {
559560
termScrollback = Math.floor(termSettings["term:scrollback"]);
@@ -572,7 +573,7 @@ const TerminalView = ({ blockId, model }: TerminalViewProps) => {
572573
blockId,
573574
connectElemRef.current,
574575
{
575-
theme: themeCopy,
576+
theme: termTheme,
576577
fontSize: termFontSize,
577578
fontFamily: termSettings?.["term:fontfamily"] ?? "Hack",
578579
drawBoldTextInBrightColors: false,
@@ -650,7 +651,7 @@ const TerminalView = ({ blockId, model }: TerminalViewProps) => {
650651
return (
651652
<div className={clsx("view-term", "term-mode-" + termMode)} ref={viewRef}>
652653
<TermResyncHandler blockId={blockId} model={model} />
653-
<TermThemeUpdater blockId={blockId} termRef={termRef} />
654+
<TermThemeUpdater blockId={blockId} model={model} termRef={termRef} />
654655
<TermStickers config={stickerConfig} />
655656
<TermToolbarVDomNode key="vdom-toolbar" blockId={blockId} model={model} />
656657
<TermVDomNode key="vdom" blockId={blockId} model={model} />
@@ -659,4 +660,4 @@ const TerminalView = ({ blockId, model }: TerminalViewProps) => {
659660
);
660661
};
661662

662-
export { TermViewModel, TerminalView, makeTerminalModel };
663+
export { makeTerminalModel, TerminalView, TermViewModel };

frontend/app/view/term/termtheme.ts

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,28 @@
11
// Copyright 2024, Command Line Inc.
22
// SPDX-License-Identifier: Apache-2.0
33

4+
import { TermViewModel } from "@/app/view/term/term";
5+
import { computeTheme } from "@/app/view/term/termutil";
46
import { TermWrap } from "@/app/view/term/termwrap";
5-
import { atoms, WOS } from "@/store/global";
6-
import * as util from "@/util/util";
7+
import { atoms } from "@/store/global";
78
import { useAtomValue } from "jotai";
89
import { useEffect } from "react";
910

1011
interface TermThemeProps {
1112
blockId: string;
1213
termRef: React.RefObject<TermWrap>;
14+
model: TermViewModel;
1315
}
1416

15-
const TermThemeUpdater = ({ blockId, termRef }: TermThemeProps) => {
17+
const TermThemeUpdater = ({ blockId, model, termRef }: TermThemeProps) => {
1618
const fullConfig = useAtomValue(atoms.fullConfigAtom);
17-
const termthemes = fullConfig?.termthemes;
18-
const [blockData] = WOS.useWaveObjectValue<Block>(WOS.makeORef("block", blockId));
19-
let defaultThemeName = "default-dark";
20-
let themeName = blockData.meta?.["term:theme"] ?? "default-dark";
21-
22-
const defaultTheme: TermThemeType = termthemes?.[defaultThemeName] || ({} as any);
23-
const theme: TermThemeType = termthemes?.[themeName] || ({} as any);
24-
19+
const blockTermTheme = useAtomValue(model.termThemeNameAtom);
20+
const [theme, _] = computeTheme(fullConfig, blockTermTheme);
2521
useEffect(() => {
26-
const combinedTheme = { ...defaultTheme };
27-
for (const key in theme) {
28-
if (!util.isBlank(theme[key])) {
29-
combinedTheme[key] = theme[key];
30-
}
31-
}
3222
if (termRef.current?.terminal) {
33-
let themeCopy = { ...combinedTheme };
34-
themeCopy.background = "#00000000";
35-
termRef.current.terminal.options.theme = themeCopy;
23+
termRef.current.terminal.options.theme = theme;
3624
}
37-
}, [defaultTheme, theme]);
38-
25+
}, [theme]);
3926
return null;
4027
};
4128

frontend/app/view/term/termutil.ts

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
// Copyright 2024, Command Line Inc.
22
// SPDX-License-Identifier: Apache-2.0
33

4-
import * as util from "@/util/util";
4+
export const DefaultTermTheme = "default-dark";
55

6-
function computeTheme(fullConfig: FullConfigType, themeName: string): TermThemeType {
7-
let defaultThemeName = "default-dark";
8-
themeName = themeName ?? "default-dark";
9-
const defaultTheme: TermThemeType = fullConfig?.termthemes?.[defaultThemeName] || ({} as any);
10-
const theme: TermThemeType = fullConfig?.termthemes?.[themeName] || ({} as any);
11-
const combinedTheme = { ...defaultTheme };
12-
for (const key in theme) {
13-
if (!util.isBlank(theme[key])) {
14-
combinedTheme[key] = theme[key];
15-
}
6+
// returns (theme, bgcolor)
7+
function computeTheme(fullConfig: FullConfigType, themeName: string): [TermThemeType, string] {
8+
let theme: TermThemeType = fullConfig?.termthemes?.[themeName];
9+
if (theme == null) {
10+
theme = fullConfig?.termthemes?.[DefaultTermTheme] || ({} as any);
1611
}
17-
return combinedTheme;
12+
const themeCopy = { ...theme };
13+
let bgcolor = themeCopy.background;
14+
themeCopy.background = "#00000000";
15+
return [themeCopy, bgcolor];
1816
}
1917

2018
export { computeTheme };

0 commit comments

Comments
 (0)