mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Add 'Sync with computer' theme option (#4973)
This commit is contained in:
parent
a4954b9f8d
commit
dd5dfb393d
8
src/common/ipc/native-theme.ts
Normal file
8
src/common/ipc/native-theme.ts
Normal file
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
|
||||
export const setNativeThemeChannel = "theme:set-native-theme";
|
||||
export const getNativeThemeChannel = "theme:get-native-theme";
|
||||
@ -29,6 +29,10 @@ jest.mock("electron", () => ({
|
||||
on: jest.fn(),
|
||||
handle: jest.fn(),
|
||||
},
|
||||
ipcRenderer: {
|
||||
on: jest.fn(),
|
||||
invoke: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
console = new Console(stdout, stderr);
|
||||
|
||||
@ -56,6 +56,7 @@ import routerInjectable from "./router/router.injectable";
|
||||
import shellApiRequestInjectable from "./proxy-functions/shell-api-request/shell-api-request.injectable";
|
||||
import userStoreInjectable from "../common/user-store/user-store.injectable";
|
||||
import trayMenuItemsInjectable from "./tray/tray-menu-items.injectable";
|
||||
import { broadcastNativeThemeOnUpdate } from "./native-theme";
|
||||
|
||||
const di = getDi();
|
||||
|
||||
@ -109,6 +110,8 @@ di.runSetups().then(() => {
|
||||
}
|
||||
}
|
||||
|
||||
broadcastNativeThemeOnUpdate();
|
||||
|
||||
app.on("second-instance", (event, argv) => {
|
||||
logger.debug("second-instance message");
|
||||
|
||||
|
||||
@ -24,6 +24,8 @@ import { onLocationChange, handleWindowAction } from "../../ipc/window";
|
||||
import { openFilePickingDialogChannel } from "../../../common/ipc/dialog";
|
||||
import { showOpenDialog } from "../../ipc/dialog";
|
||||
import { windowActionHandleChannel, windowLocationChangedChannel, windowOpenAppMenuAsContextMenuChannel } from "../../../common/ipc/window";
|
||||
import { getNativeColorTheme } from "../../native-theme";
|
||||
import { getNativeThemeChannel } from "../../../common/ipc/native-theme";
|
||||
|
||||
interface Dependencies {
|
||||
electronMenuItems: IComputedValue<MenuRegistration[]>;
|
||||
@ -158,4 +160,8 @@ export const initIpcMainHandlers = ({ electronMenuItems, directoryForLensLocalSt
|
||||
y: 20,
|
||||
});
|
||||
});
|
||||
|
||||
ipcMainHandle(getNativeThemeChannel, () => {
|
||||
return getNativeColorTheme();
|
||||
});
|
||||
};
|
||||
|
||||
18
src/main/native-theme.ts
Normal file
18
src/main/native-theme.ts
Normal file
@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { nativeTheme } from "electron";
|
||||
import { broadcastMessage } from "../common/ipc";
|
||||
import { setNativeThemeChannel } from "../common/ipc/native-theme";
|
||||
|
||||
export function broadcastNativeThemeOnUpdate() {
|
||||
nativeTheme.on("updated", () => {
|
||||
broadcastMessage(setNativeThemeChannel, getNativeColorTheme());
|
||||
});
|
||||
}
|
||||
|
||||
export function getNativeColorTheme() {
|
||||
return nativeTheme.shouldUseDarkColors ? "dark" : "light";
|
||||
}
|
||||
@ -41,6 +41,10 @@ jest.mock("electron", () => ({
|
||||
on: jest.fn(),
|
||||
handle: jest.fn(),
|
||||
},
|
||||
ipcRenderer: {
|
||||
on: jest.fn(),
|
||||
invoke: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock("./hotbar-toggle-menu-item", () => ({
|
||||
|
||||
@ -45,7 +45,10 @@ const NonInjectedApplication: React.FC<Dependencies> = ({ appPreferenceItems })
|
||||
<section id="appearance">
|
||||
<SubTitle title="Theme" />
|
||||
<Select
|
||||
options={themeStore.themeOptions}
|
||||
options={[
|
||||
{ label: "Sync with computer", value: "system" },
|
||||
...themeStore.themeOptions,
|
||||
]}
|
||||
value={userStore.colorTheme}
|
||||
onChange={({ value }) => userStore.colorTheme = value}
|
||||
themeName="lens"
|
||||
|
||||
@ -33,6 +33,10 @@ jest.mock("electron", () => ({
|
||||
on: jest.fn(),
|
||||
handle: jest.fn(),
|
||||
},
|
||||
ipcRenderer: {
|
||||
on: jest.fn(),
|
||||
invoke: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
const initialTabs: DockTab[] = [
|
||||
|
||||
@ -36,6 +36,10 @@ jest.mock("electron", () => ({
|
||||
on: jest.fn(),
|
||||
handle: jest.fn(),
|
||||
},
|
||||
ipcRenderer: {
|
||||
on: jest.fn(),
|
||||
invoke: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
function mockLogTabViewModel(tabId: TabId, deps: Partial<LogTabViewModelDependencies>): LogTabViewModel {
|
||||
|
||||
@ -26,6 +26,13 @@ const mockHotbars: { [id: string]: any } = {
|
||||
},
|
||||
};
|
||||
|
||||
jest.mock("electron", () => ({
|
||||
ipcRenderer: {
|
||||
on: jest.fn(),
|
||||
invoke: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe("<HotbarRemoveCommand />", () => {
|
||||
let di: DiContainer;
|
||||
let render: DiRender;
|
||||
|
||||
@ -17,6 +17,12 @@ import rendererExtensionsInjectable from "../../../extensions/renderer-extension
|
||||
import { computed } from "mobx";
|
||||
import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
|
||||
|
||||
jest.mock("electron", () => ({
|
||||
ipcRenderer: {
|
||||
on: jest.fn(),
|
||||
invoke: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe("<Select />", () => {
|
||||
let di: DiContainer;
|
||||
|
||||
@ -13,6 +13,8 @@ import type { SelectOption } from "./components/select";
|
||||
import type { MonacoEditorProps } from "./components/monaco-editor";
|
||||
import { defaultTheme } from "../common/vars";
|
||||
import { camelCase } from "lodash";
|
||||
import { ipcRenderer } from "electron";
|
||||
import { getNativeThemeChannel, setNativeThemeChannel } from "../common/ipc/native-theme";
|
||||
|
||||
export type ThemeId = string;
|
||||
|
||||
@ -34,6 +36,8 @@ export class ThemeStore extends Singleton {
|
||||
"lens-light": lensLightThemeJson as Theme,
|
||||
});
|
||||
|
||||
@observable osNativeTheme: "dark" | "light" | undefined;
|
||||
|
||||
@computed get activeThemeId(): ThemeId {
|
||||
return UserStore.getInstance().colorTheme;
|
||||
}
|
||||
@ -43,7 +47,7 @@ export class ThemeStore extends Singleton {
|
||||
}
|
||||
|
||||
@computed get activeTheme(): Theme {
|
||||
return this.themes.get(this.activeThemeId) ?? this.themes.get(defaultTheme);
|
||||
return this.systemTheme ?? this.themes.get(this.activeThemeId) ?? this.themes.get(defaultTheme);
|
||||
}
|
||||
|
||||
@computed get terminalColors(): [string, string][] {
|
||||
@ -72,11 +76,25 @@ export class ThemeStore extends Singleton {
|
||||
}));
|
||||
}
|
||||
|
||||
@computed get systemTheme() {
|
||||
if (this.activeThemeId == "system" && this.osNativeTheme) {
|
||||
return this.themes.get(`lens-${this.osNativeTheme}`);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
makeObservable(this);
|
||||
autoBind(this);
|
||||
this.init();
|
||||
}
|
||||
|
||||
async init() {
|
||||
await this.setNativeTheme();
|
||||
this.bindNativeThemeUpdateEvent();
|
||||
|
||||
// auto-apply active theme
|
||||
reaction(() => ({
|
||||
@ -95,12 +113,26 @@ export class ThemeStore extends Singleton {
|
||||
});
|
||||
}
|
||||
|
||||
bindNativeThemeUpdateEvent() {
|
||||
ipcRenderer.on(setNativeThemeChannel, (event, theme: "dark" | "light") => {
|
||||
this.osNativeTheme = theme;
|
||||
this.applyTheme(theme);
|
||||
});
|
||||
}
|
||||
|
||||
async setNativeTheme() {
|
||||
const theme: "dark" | "light" = await ipcRenderer.invoke(getNativeThemeChannel);
|
||||
|
||||
this.osNativeTheme = theme;
|
||||
}
|
||||
|
||||
getThemeById(themeId: ThemeId): Theme {
|
||||
return this.themes.get(themeId);
|
||||
}
|
||||
|
||||
protected applyTheme(themeId: ThemeId) {
|
||||
const theme = this.getThemeById(themeId);
|
||||
const theme = this.systemTheme ?? this.getThemeById(themeId);
|
||||
|
||||
const colors = Object.entries({
|
||||
...theme.colors,
|
||||
...Object.fromEntries(this.terminalColors),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user