From 963651ef4b7660c094885cb5cba9bdcc6709fb2f Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 5 Dec 2022 12:04:25 -0500 Subject: [PATCH] Make LensTheme fully injectable and runnable Signed-off-by: Sebastian Malton --- src/common/ipc/native-theme.ts | 8 - .../user-store/lens-color-theme.injectable.ts | 37 +++++ .../user-store/terminal-theme.injectable.ts | 37 +++++ src/common/vars.ts | 2 +- src/extensions/renderer-api/theming.ts | 2 +- .../application/theme/theme.tsx | 40 ++--- .../terminal-theme/terminal-theme.tsx | 74 ++++----- .../theme/system-type/common/channels.ts | 17 ++ .../main/emit-update.injectable.ts | 21 +++ .../main/handle-initial.injectable.ts | 19 +++ .../main/setup-update-emitter.injectable.ts | 33 ++++ .../renderer/initialize.injectable.ts | 28 ++++ .../renderer/request-initial.injectable.ts | 21 +++ .../renderer/update-listener.injectable.ts | 19 +++ .../setup-ipc-main-handlers.ts | 8 - src/renderer/bootstrap.tsx | 5 - .../components/+cluster/cluster-issues.tsx | 2 +- .../+cluster/cluster-pie-charts.tsx | 2 +- .../release-details-model.injectable.tsx | 2 +- .../components/+nodes/node-charts.tsx | 2 +- .../volume-claim-disk-chart.tsx | 2 +- .../overview-workload-status.tsx | 2 +- .../+workloads-pods/container-charts.tsx | 2 +- src/renderer/components/chart/bar-chart.tsx | 2 +- src/renderer/components/chart/pie-chart.tsx | 2 +- .../terminal/create-terminal.injectable.ts | 4 +- .../components/dock/terminal/terminal.ts | 16 +- .../components/dock/terminal/view.tsx | 2 +- .../components/item-object-list/content.tsx | 2 +- .../monaco-editor/monaco-editor.tsx | 2 +- src/renderer/components/select/select.tsx | 2 +- src/renderer/themes/active-type.injectable.ts | 8 +- src/renderer/themes/active.injectable.ts | 28 +++- .../themes/apply-lens-theme.injectable.ts | 38 +++++ ...ly-theme.global-override-for-injectable.ts | 9 + src/renderer/themes/declaration.ts | 12 ++ .../themes/default-theme.injectable.ts | 26 +++ src/renderer/themes/lens-dark.injectable.ts | 156 ++++++++++++++++++ src/renderer/themes/lens-dark.ts | 150 ----------------- src/renderer/themes/lens-light.injectable.ts | 154 +++++++++++++++++ src/renderer/themes/lens-light.ts | 149 ----------------- src/renderer/themes/lens-theme.ts | 18 ++ .../setup-apply-active-theme.injectable.ts | 35 ++++ src/renderer/themes/store.injectable.ts | 19 --- src/renderer/themes/store.ts | 150 ----------------- .../themes/system-theme.injectable.ts | 13 ++ .../themes/terminal-colors.injectable.ts | 43 +++++ src/renderer/themes/themes.injectable.ts | 17 ++ 48 files changed, 863 insertions(+), 579 deletions(-) delete mode 100644 src/common/ipc/native-theme.ts create mode 100644 src/common/user-store/lens-color-theme.injectable.ts create mode 100644 src/common/user-store/terminal-theme.injectable.ts create mode 100644 src/features/theme/system-type/common/channels.ts create mode 100644 src/features/theme/system-type/main/emit-update.injectable.ts create mode 100644 src/features/theme/system-type/main/handle-initial.injectable.ts create mode 100644 src/features/theme/system-type/main/setup-update-emitter.injectable.ts create mode 100644 src/features/theme/system-type/renderer/initialize.injectable.ts create mode 100644 src/features/theme/system-type/renderer/request-initial.injectable.ts create mode 100644 src/features/theme/system-type/renderer/update-listener.injectable.ts create mode 100644 src/renderer/themes/apply-lens-theme.injectable.ts create mode 100644 src/renderer/themes/apply-theme.global-override-for-injectable.ts create mode 100644 src/renderer/themes/declaration.ts create mode 100644 src/renderer/themes/default-theme.injectable.ts create mode 100644 src/renderer/themes/lens-dark.injectable.ts delete mode 100644 src/renderer/themes/lens-dark.ts create mode 100644 src/renderer/themes/lens-light.injectable.ts delete mode 100644 src/renderer/themes/lens-light.ts create mode 100644 src/renderer/themes/lens-theme.ts create mode 100644 src/renderer/themes/setup-apply-active-theme.injectable.ts delete mode 100644 src/renderer/themes/store.injectable.ts delete mode 100644 src/renderer/themes/store.ts create mode 100644 src/renderer/themes/system-theme.injectable.ts create mode 100644 src/renderer/themes/terminal-colors.injectable.ts create mode 100644 src/renderer/themes/themes.injectable.ts diff --git a/src/common/ipc/native-theme.ts b/src/common/ipc/native-theme.ts deleted file mode 100644 index 4708a3c9b3..0000000000 --- a/src/common/ipc/native-theme.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * 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"; diff --git a/src/common/user-store/lens-color-theme.injectable.ts b/src/common/user-store/lens-color-theme.injectable.ts new file mode 100644 index 0000000000..5b48de1a37 --- /dev/null +++ b/src/common/user-store/lens-color-theme.injectable.ts @@ -0,0 +1,37 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { computed } from "mobx"; +import userStoreInjectable from "./user-store.injectable"; + +export type LensColorThemePreference = { + useSystemTheme: true; +} | { + useSystemTheme: false; + lensThemeId: string; +}; + +const lensColorThemePreferenceInjectable = getInjectable({ + id: "lens-color-theme-preference", + instantiate: (di) => { + const userStore = di.inject(userStoreInjectable); + + return computed((): LensColorThemePreference => { + // TODO: remove magic strings + if (userStore.colorTheme === "system") { + return { + useSystemTheme: true, + }; + } + + return { + useSystemTheme: false, + lensThemeId: userStore.colorTheme, + }; + }); + }, +}); + +export default lensColorThemePreferenceInjectable; diff --git a/src/common/user-store/terminal-theme.injectable.ts b/src/common/user-store/terminal-theme.injectable.ts new file mode 100644 index 0000000000..a0a00c3253 --- /dev/null +++ b/src/common/user-store/terminal-theme.injectable.ts @@ -0,0 +1,37 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { computed } from "mobx"; +import userStoreInjectable from "./user-store.injectable"; + +export type TerminalThemePreference = { + matchLensTheme: true; +} | { + matchLensTheme: false; + themeId: string; +}; + +const terminalThemePreferenceInjectable = getInjectable({ + id: "terminal-theme-preference", + instantiate: (di) => { + const userStore = di.inject(userStoreInjectable); + + return computed((): TerminalThemePreference => { + // NOTE: remove use of magic strings + if (!userStore.terminalTheme) { + return { + matchLensTheme: true, + }; + } + + return { + matchLensTheme: false, + themeId: userStore.terminalTheme, + }; + }); + }, +}); + +export default terminalThemePreferenceInjectable; diff --git a/src/common/vars.ts b/src/common/vars.ts index 0f697755e4..cbde12ff5d 100644 --- a/src/common/vars.ts +++ b/src/common/vars.ts @@ -4,7 +4,7 @@ */ // App's common configuration for any process (main, renderer, build pipeline, etc.) -import type { ThemeId } from "../renderer/themes/store"; +import type { ThemeId } from "../renderer/themes/lens-theme"; /** * @deprecated Switch to using isMacInjectable diff --git a/src/extensions/renderer-api/theming.ts b/src/extensions/renderer-api/theming.ts index 435cf23504..3e4c9fe97b 100644 --- a/src/extensions/renderer-api/theming.ts +++ b/src/extensions/renderer-api/theming.ts @@ -4,7 +4,7 @@ */ import activeThemeInjectable from "../../renderer/themes/active.injectable"; -import type { LensTheme } from "../../renderer/themes/store"; +import type { LensTheme } from "../../renderer/themes/lens-theme"; import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; export const activeTheme = asLegacyGlobalForExtensionApi(activeThemeInjectable); diff --git a/src/features/preferences/renderer/preference-items/application/theme/theme.tsx b/src/features/preferences/renderer/preference-items/application/theme/theme.tsx index 851cc638d4..fe247b9491 100644 --- a/src/features/preferences/renderer/preference-items/application/theme/theme.tsx +++ b/src/features/preferences/renderer/preference-items/application/theme/theme.tsx @@ -8,25 +8,30 @@ import { Select } from "../../../../../../renderer/components/select"; import { withInjectables } from "@ogre-tools/injectable-react"; import { observer } from "mobx-react"; import type { UserStore } from "../../../../../../common/user-store"; -import type { ThemeStore } from "../../../../../../renderer/themes/store"; -import { defaultThemeId } from "../../../../../../common/vars"; +import type { LensTheme } from "../../../../../../renderer/themes/lens-theme"; import userStoreInjectable from "../../../../../../common/user-store/user-store.injectable"; -import themeStoreInjectable from "../../../../../../renderer/themes/store.injectable"; +import defaultLensThemeInjectable from "../../../../../../renderer/themes/default-theme.injectable"; +import { lensThemeDeclarationInjectionToken } from "../../../../../../renderer/themes/declaration"; interface Dependencies { userStore: UserStore; - themeStore: ThemeStore; + defaultTheme: LensTheme; + themes: LensTheme[]; } -const NonInjectedTheme = observer(({ userStore, themeStore }: Dependencies) => { +const NonInjectedTheme = observer(({ + userStore, + themes, + defaultTheme, +}: Dependencies) => { const themeOptions = [ { value: "system", // TODO: replace with a sentinal value that isn't string (and serialize it differently) label: "Sync with computer", }, - ...Array.from(themeStore.themes, ([themeId, { name }]) => ({ - value: themeId, - label: name, + ...themes.map(theme => ({ + value: theme.name, + label: theme.name, })), ]; @@ -38,7 +43,7 @@ const NonInjectedTheme = observer(({ userStore, themeStore }: Dependencies) => { options={themeOptions} value={userStore.colorTheme} onChange={(value) => - (userStore.colorTheme = value?.value ?? defaultThemeId) + (userStore.colorTheme = value?.value ?? defaultTheme.name) } themeName="lens" /> @@ -46,13 +51,10 @@ const NonInjectedTheme = observer(({ userStore, themeStore }: Dependencies) => { ); }); -export const Theme = withInjectables( - NonInjectedTheme, - - { - getProps: (di) => ({ - userStore: di.inject(userStoreInjectable), - themeStore: di.inject(themeStoreInjectable), - }), - }, -); +export const Theme = withInjectables(NonInjectedTheme, { + getProps: (di) => ({ + userStore: di.inject(userStoreInjectable), + defaultTheme: di.inject(defaultLensThemeInjectable), + themes: di.injectMany(lensThemeDeclarationInjectionToken), + }), +}); diff --git a/src/features/preferences/renderer/preference-items/terminal/terminal-theme/terminal-theme.tsx b/src/features/preferences/renderer/preference-items/terminal/terminal-theme/terminal-theme.tsx index 227296887c..bea6b1279e 100644 --- a/src/features/preferences/renderer/preference-items/terminal/terminal-theme/terminal-theme.tsx +++ b/src/features/preferences/renderer/preference-items/terminal/terminal-theme/terminal-theme.tsx @@ -9,50 +9,48 @@ import type { UserStore } from "../../../../../../common/user-store"; import userStoreInjectable from "../../../../../../common/user-store/user-store.injectable"; import { observer } from "mobx-react"; import { Select } from "../../../../../../renderer/components/select"; -import themeStoreInjectable from "../../../../../../renderer/themes/store.injectable"; -import type { ThemeStore } from "../../../../../../renderer/themes/store"; +import type { LensTheme } from "../../../../../../renderer/themes/lens-theme"; +import { lensThemeDeclarationInjectionToken } from "../../../../../../renderer/themes/declaration"; interface Dependencies { userStore: UserStore; - themeStore: ThemeStore; + themes: LensTheme[]; } -const NonInjectedTerminalTheme = observer( - ({ userStore, themeStore }: Dependencies) => { +const NonInjectedTerminalTheme = observer(({ + userStore, + themes, +}: Dependencies) => { - const themeOptions = [ - { - value: "", // TODO: replace with a sentinal value that isn't string (and serialize it differently) - label: "Match Lens Theme", - }, - ...Array.from(themeStore.themes, ([themeId, { name }]) => ({ - value: themeId, - label: name, - })), - ]; + const themeOptions = [ + { + value: "", // TODO: replace with a sentinal value that isn't string (and serialize it differently) + label: "Match Lens Theme", + }, + ...themes.map(theme => ({ + value: theme.name, + label: theme.name, + })), + ]; - return ( -
- - userStore.terminalTheme = option?.value ?? ""} + /> +
+ ); +}, ); -export const TerminalTheme = withInjectables( - NonInjectedTerminalTheme, - - { - getProps: (di) => ({ - userStore: di.inject(userStoreInjectable), - themeStore: di.inject(themeStoreInjectable), - }), - }, -); +export const TerminalTheme = withInjectables(NonInjectedTerminalTheme, { + getProps: (di) => ({ + userStore: di.inject(userStoreInjectable), + themes: di.injectMany(lensThemeDeclarationInjectionToken), + }), +}); diff --git a/src/features/theme/system-type/common/channels.ts b/src/features/theme/system-type/common/channels.ts new file mode 100644 index 0000000000..3134f20378 --- /dev/null +++ b/src/features/theme/system-type/common/channels.ts @@ -0,0 +1,17 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { MessageChannel } from "../../../../common/utils/channel/message-channel-listener-injection-token"; +import type { RequestChannel } from "../../../../common/utils/channel/request-channel-listener-injection-token"; + +export type SystemThemeType = "dark" | "light"; + +export const initialSystemThemeTypeChannel: RequestChannel = { + id: "initial-system-theme-type", +}; + +export const systemThemeTypeUpdateChannel: MessageChannel = { + id: "system-theme-type-update", +}; diff --git a/src/features/theme/system-type/main/emit-update.injectable.ts b/src/features/theme/system-type/main/emit-update.injectable.ts new file mode 100644 index 0000000000..c085e6615a --- /dev/null +++ b/src/features/theme/system-type/main/emit-update.injectable.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import type { MessageChannelHandler } from "../../../../common/utils/channel/message-channel-listener-injection-token"; +import { sendMessageToChannelInjectionToken } from "../../../../common/utils/channel/message-to-channel-injection-token"; +import { systemThemeTypeUpdateChannel } from "../common/channels"; + +export type EmitSystemThemeTypeUpdate = MessageChannelHandler; + +const emitSystemThemeTypeUpdateInjectable = getInjectable({ + id: "emit-system-theme-type-update", + instantiate: (di): EmitSystemThemeTypeUpdate => { + const sendMessageToChannel = di.inject(sendMessageToChannelInjectionToken); + + return (type) => sendMessageToChannel(systemThemeTypeUpdateChannel, type); + }, +}); + +export default emitSystemThemeTypeUpdateInjectable; diff --git a/src/features/theme/system-type/main/handle-initial.injectable.ts b/src/features/theme/system-type/main/handle-initial.injectable.ts new file mode 100644 index 0000000000..1468b1eeda --- /dev/null +++ b/src/features/theme/system-type/main/handle-initial.injectable.ts @@ -0,0 +1,19 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import operatingSystemThemeInjectable from "../../../../main/theme/operating-system-theme.injectable"; +import { getRequestChannelListenerInjectable } from "../../../../main/utils/channel/channel-listeners/listener-tokens"; +import { initialSystemThemeTypeChannel } from "../common/channels"; + +const initialSystemThemeTypeHandler = getRequestChannelListenerInjectable({ + channel: initialSystemThemeTypeChannel, + handler: (di) => { + const operatingSystemTheme = di.inject(operatingSystemThemeInjectable); + + return () => operatingSystemTheme.get(); + }, +}); + +export default initialSystemThemeTypeHandler; diff --git a/src/features/theme/system-type/main/setup-update-emitter.injectable.ts b/src/features/theme/system-type/main/setup-update-emitter.injectable.ts new file mode 100644 index 0000000000..75129fa128 --- /dev/null +++ b/src/features/theme/system-type/main/setup-update-emitter.injectable.ts @@ -0,0 +1,33 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { reaction } from "mobx"; +import { onLoadOfApplicationInjectionToken } from "../../../../main/start-main-application/runnable-tokens/on-load-of-application-injection-token"; +import operatingSystemThemeInjectable from "../../../../main/theme/operating-system-theme.injectable"; +import emitSystemThemeTypeUpdateInjectable from "./emit-update.injectable"; + +const setupSystemThemeTypeUpdaterEmitterInjectable = getInjectable({ + id: "setup-system-theme-type-updater-emitter", + instantiate: (di) => { + const operatingSystemTheme = di.inject(operatingSystemThemeInjectable); + const emitSystemThemeTypeUpdate = di.inject(emitSystemThemeTypeUpdateInjectable); + + return { + id: "setup-system-theme-type-updater-emitter", + run: () => { + reaction( + () => operatingSystemTheme.get(), + emitSystemThemeTypeUpdate, + { + fireImmediately: true, + }, + ); + }, + }; + }, + injectionToken: onLoadOfApplicationInjectionToken, +}); + +export default setupSystemThemeTypeUpdaterEmitterInjectable; diff --git a/src/features/theme/system-type/renderer/initialize.injectable.ts b/src/features/theme/system-type/renderer/initialize.injectable.ts new file mode 100644 index 0000000000..a80563b7b1 --- /dev/null +++ b/src/features/theme/system-type/renderer/initialize.injectable.ts @@ -0,0 +1,28 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { beforeFrameStartsInjectionToken } from "../../../../renderer/before-frame-starts/tokens"; +import initUserStoreInjectable from "../../../../renderer/stores/init-user-store.injectable"; +import systemThemeConfigurationInjectable from "../../../../renderer/themes/system-theme.injectable"; +import requestInitialSystemThemeTypeInjectable from "./request-initial.injectable"; + +const initializeSystemThemeTypeInjectable = getInjectable({ + id: "initialize-system-theme-type", + instantiate: (di) => { + const systemThemeConfiguration = di.inject(systemThemeConfigurationInjectable); + const requestInitialSystemThemeType = di.inject(requestInitialSystemThemeTypeInjectable); + + return { + id: "initialize-system-theme-type", + run: async () => { + systemThemeConfiguration.set(await requestInitialSystemThemeType()); + }, + runAfter: di.inject(initUserStoreInjectable), + }; + }, + injectionToken: beforeFrameStartsInjectionToken, +}); + +export default initializeSystemThemeTypeInjectable; diff --git a/src/features/theme/system-type/renderer/request-initial.injectable.ts b/src/features/theme/system-type/renderer/request-initial.injectable.ts new file mode 100644 index 0000000000..74fe73cf03 --- /dev/null +++ b/src/features/theme/system-type/renderer/request-initial.injectable.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import type { RequestChannelHandler } from "../../../../main/utils/channel/channel-listeners/listener-tokens"; +import requestFromChannelInjectable from "../../../../renderer/utils/channel/request-from-channel.injectable"; +import { initialSystemThemeTypeChannel } from "../common/channels"; + +export type RequestInitialSystemThemeType = RequestChannelHandler; + +const requestInitialSystemThemeTypeInjectable = getInjectable({ + id: "request-initial-system-theme-type", + instantiate: (di): RequestInitialSystemThemeType => { + const requestFromChannel = di.inject(requestFromChannelInjectable); + + return () => requestFromChannel(initialSystemThemeTypeChannel); + }, +}); + +export default requestInitialSystemThemeTypeInjectable; diff --git a/src/features/theme/system-type/renderer/update-listener.injectable.ts b/src/features/theme/system-type/renderer/update-listener.injectable.ts new file mode 100644 index 0000000000..4eb57c9b85 --- /dev/null +++ b/src/features/theme/system-type/renderer/update-listener.injectable.ts @@ -0,0 +1,19 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getMessageChannelListenerInjectable } from "../../../../common/utils/channel/message-channel-listener-injection-token"; +import systemThemeConfigurationInjectable from "../../../../renderer/themes/system-theme.injectable"; +import { systemThemeTypeUpdateChannel } from "../common/channels"; + +const systemThemeTypeUpdateListenerInjectable = getMessageChannelListenerInjectable({ + channel: systemThemeTypeUpdateChannel, + id: "main", + handler: (di) => { + const systemThemeConfiguration = di.inject(systemThemeConfigurationInjectable); + + return (type) => systemThemeConfiguration.set(type); + }, +}); + +export default systemThemeTypeUpdateListenerInjectable; diff --git a/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts b/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts index 15daf73383..aef5616687 100644 --- a/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts +++ b/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts @@ -14,8 +14,6 @@ import { pushCatalogToRenderer } from "../../../catalog-pusher"; import type { IComputedValue } from "mobx"; import { windowActionHandleChannel, windowLocationChangedChannel, windowOpenAppMenuAsContextMenuChannel } from "../../../../common/ipc/window"; import { handleWindowAction, onLocationChange } from "../../../ipc/window"; -import { getNativeThemeChannel } from "../../../../common/ipc/native-theme"; -import type { Theme } from "../../../theme/operating-system-theme-state.injectable"; import type { ApplicationMenuItemTypes } from "../../../../features/application-menu/main/menu-items/application-menu-item-injection-token"; import type { Composite } from "../../../../common/utils/composite/get-composite/get-composite"; import { getApplicationMenuTemplate } from "../../../../features/application-menu/main/populate-application-menu.injectable"; @@ -26,7 +24,6 @@ interface Dependencies { applicationMenuItemComposite: IComputedValue>; catalogEntityRegistry: CatalogEntityRegistry; clusterStore: ClusterStore; - operatingSystemTheme: IComputedValue; emitAppEvent: EmitAppEvent; getClusterById: GetClusterById; } @@ -35,7 +32,6 @@ export const setupIpcMainHandlers = ({ applicationMenuItemComposite, catalogEntityRegistry, clusterStore, - operatingSystemTheme, emitAppEvent, getClusterById, }: Dependencies) => { @@ -83,10 +79,6 @@ export const setupIpcMainHandlers = ({ }); }); - ipcMainHandle(getNativeThemeChannel, () => { - return operatingSystemTheme.get(); - }); - ipcMainHandle(clusterStates, () => ( clusterStore.clustersList.map(cluster => ({ id: cluster.id, diff --git a/src/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx index 5002f63eab..34474515fe 100644 --- a/src/renderer/bootstrap.tsx +++ b/src/renderer/bootstrap.tsx @@ -24,7 +24,6 @@ import initRootFrameInjectable from "./frames/root-frame/init-root-frame/init-ro import initClusterFrameInjectable from "./frames/cluster-frame/init-cluster-frame/init-cluster-frame.injectable"; import { Router } from "react-router"; import historyInjectable from "./navigation/history.injectable"; -import themeStoreInjectable from "./themes/store.injectable"; import assert from "assert"; import startFrameInjectable from "./start-frame/start-frame.injectable"; @@ -45,10 +44,6 @@ export async function bootstrap(di: DiContainer) { extensionDiscovery.init(); - // ThemeStore depends on: UserStore - // TODO: Remove temporal dependencies - di.inject(themeStoreInjectable); - const extensionInstallationStateStore = di.inject(extensionInstallationStateStoreInjectable); extensionInstallationStateStore.bindIpcListeners(); diff --git a/src/renderer/components/+cluster/cluster-issues.tsx b/src/renderer/components/+cluster/cluster-issues.tsx index 48f36e2168..a574874d61 100644 --- a/src/renderer/components/+cluster/cluster-issues.tsx +++ b/src/renderer/components/+cluster/cluster-issues.tsx @@ -27,7 +27,7 @@ import type { PageParam } from "../../navigation"; import type { ToggleKubeDetailsPane } from "../kube-detail-params/toggle-details.injectable"; import kubeSelectedUrlParamInjectable from "../kube-detail-params/kube-selected-url.injectable"; import toggleKubeDetailsPaneInjectable from "../kube-detail-params/toggle-details.injectable"; -import type { LensTheme } from "../../themes/store"; +import type { LensTheme } from "../../themes/lens-theme"; import activeThemeInjectable from "../../themes/active.injectable"; export interface ClusterIssuesProps { diff --git a/src/renderer/components/+cluster/cluster-pie-charts.tsx b/src/renderer/components/+cluster/cluster-pie-charts.tsx index 732712de9b..e0203dd878 100644 --- a/src/renderer/components/+cluster/cluster-pie-charts.tsx +++ b/src/renderer/components/+cluster/cluster-pie-charts.tsx @@ -16,7 +16,7 @@ import type { PieChartData } from "../chart"; import { PieChart } from "../chart"; import { ClusterNoMetrics } from "./cluster-no-metrics"; import { bytesToUnits, cssNames } from "../../utils"; -import type { LensTheme } from "../../themes/store"; +import type { LensTheme } from "../../themes/lens-theme"; import { getMetricLastPoints } from "../../../common/k8s-api/endpoints/metrics.api"; import { withInjectables } from "@ogre-tools/injectable-react"; import clusterOverviewStoreInjectable from "./cluster-overview-store/cluster-overview-store.injectable"; diff --git a/src/renderer/components/+helm-releases/release-details/release-details-model/release-details-model.injectable.tsx b/src/renderer/components/+helm-releases/release-details/release-details-model/release-details-model.injectable.tsx index acd65374b9..80df7a323c 100644 --- a/src/renderer/components/+helm-releases/release-details/release-details-model/release-details-model.injectable.tsx +++ b/src/renderer/components/+helm-releases/release-details/release-details-model/release-details-model.injectable.tsx @@ -8,7 +8,7 @@ import { runInAction, action, observable, computed } from "mobx"; import type { TargetHelmRelease } from "../target-helm-release.injectable"; import type { RequestDetailedHelmRelease, DetailedHelmRelease } from "./request-detailed-helm-release.injectable"; import requestDetailedHelmReleaseInjectable from "./request-detailed-helm-release.injectable"; -import type { LensTheme } from "../../../../themes/store"; +import type { LensTheme } from "../../../../themes/lens-theme"; import type { RequestHelmReleaseConfiguration } from "../../../../../common/k8s-api/endpoints/helm-releases.api/request-configuration.injectable"; import requestHelmReleaseConfigurationInjectable from "../../../../../common/k8s-api/endpoints/helm-releases.api/request-configuration.injectable"; import { pipeline } from "@ogre-tools/fp"; diff --git a/src/renderer/components/+nodes/node-charts.tsx b/src/renderer/components/+nodes/node-charts.tsx index 36ec85227e..4468b8400e 100644 --- a/src/renderer/components/+nodes/node-charts.tsx +++ b/src/renderer/components/+nodes/node-charts.tsx @@ -12,7 +12,7 @@ import { ResourceMetricsContext } from "../resource-metrics"; import { observer } from "mobx-react"; import { mapValues } from "lodash"; import { type MetricsTab, metricTabOptions } from "../chart/options"; -import type { LensTheme } from "../../themes/store"; +import type { LensTheme } from "../../themes/lens-theme"; import { withInjectables } from "@ogre-tools/injectable-react"; import type { IComputedValue } from "mobx"; import activeThemeInjectable from "../../themes/active.injectable"; diff --git a/src/renderer/components/+storage-volume-claims/volume-claim-disk-chart.tsx b/src/renderer/components/+storage-volume-claims/volume-claim-disk-chart.tsx index 044bb3399d..6d2f059ddc 100644 --- a/src/renderer/components/+storage-volume-claims/volume-claim-disk-chart.tsx +++ b/src/renderer/components/+storage-volume-claims/volume-claim-disk-chart.tsx @@ -10,7 +10,7 @@ import { BarChart, memoryOptions } from "../chart"; import { isMetricsEmpty, normalizeMetrics } from "../../../common/k8s-api/endpoints/metrics.api"; import { NoMetrics } from "../resource-metrics/no-metrics"; import { ResourceMetricsContext } from "../resource-metrics"; -import type { LensTheme } from "../../themes/store"; +import type { LensTheme } from "../../themes/lens-theme"; import { withInjectables } from "@ogre-tools/injectable-react"; import type { IComputedValue } from "mobx"; import activeThemeInjectable from "../../themes/active.injectable"; diff --git a/src/renderer/components/+workloads-overview/overview-workload-status.tsx b/src/renderer/components/+workloads-overview/overview-workload-status.tsx index 57422fe461..41d947b326 100644 --- a/src/renderer/components/+workloads-overview/overview-workload-status.tsx +++ b/src/renderer/components/+workloads-overview/overview-workload-status.tsx @@ -11,7 +11,7 @@ import { observer } from "mobx-react"; import type { PieChartData } from "../chart"; import { PieChart } from "../chart"; import { object } from "../../utils"; -import type { LensTheme } from "../../themes/store"; +import type { LensTheme } from "../../themes/lens-theme"; import { withInjectables } from "@ogre-tools/injectable-react"; import type { PascalCase } from "type-fest"; import type { IComputedValue } from "mobx"; diff --git a/src/renderer/components/+workloads-pods/container-charts.tsx b/src/renderer/components/+workloads-pods/container-charts.tsx index 718f05420c..5c77f22389 100644 --- a/src/renderer/components/+workloads-pods/container-charts.tsx +++ b/src/renderer/components/+workloads-pods/container-charts.tsx @@ -10,7 +10,7 @@ import { BarChart } from "../chart"; import { isMetricsEmpty, normalizeMetrics } from "../../../common/k8s-api/endpoints/metrics.api"; import { NoMetrics } from "../resource-metrics/no-metrics"; import { ResourceMetricsContext } from "../resource-metrics"; -import type { LensTheme } from "../../themes/store"; +import type { LensTheme } from "../../themes/lens-theme"; import { mapValues } from "lodash"; import { type MetricsTab, metricTabOptions } from "../chart/options"; import { withInjectables } from "@ogre-tools/injectable-react"; diff --git a/src/renderer/components/chart/bar-chart.tsx b/src/renderer/components/chart/bar-chart.tsx index b159520309..e5c0e901d8 100644 --- a/src/renderer/components/chart/bar-chart.tsx +++ b/src/renderer/components/chart/bar-chart.tsx @@ -13,7 +13,7 @@ import type { ChartProps } from "./chart"; import { Chart, ChartKind } from "./chart"; import { bytesToUnits, cssNames, isObject } from "../../utils"; import { ZebraStripesPlugin } from "./zebra-stripes.plugin"; -import type { LensTheme } from "../../themes/store"; +import type { LensTheme } from "../../themes/lens-theme"; import { NoMetrics } from "../resource-metrics/no-metrics"; import assert from "assert"; import { withInjectables } from "@ogre-tools/injectable-react"; diff --git a/src/renderer/components/chart/pie-chart.tsx b/src/renderer/components/chart/pie-chart.tsx index 2e4cd1bb0f..de1461631a 100644 --- a/src/renderer/components/chart/pie-chart.tsx +++ b/src/renderer/components/chart/pie-chart.tsx @@ -11,7 +11,7 @@ import ChartJS from "chart.js"; import type { ChartProps } from "./chart"; import { Chart } from "./chart"; import { cssNames } from "../../utils"; -import type { LensTheme } from "../../themes/store"; +import type { LensTheme } from "../../themes/lens-theme"; import { withInjectables } from "@ogre-tools/injectable-react"; import type { IComputedValue } from "mobx"; import activeThemeInjectable from "../../themes/active.injectable"; diff --git a/src/renderer/components/dock/terminal/create-terminal.injectable.ts b/src/renderer/components/dock/terminal/create-terminal.injectable.ts index 878d88f091..cdf2e95f94 100644 --- a/src/renderer/components/dock/terminal/create-terminal.injectable.ts +++ b/src/renderer/components/dock/terminal/create-terminal.injectable.ts @@ -10,9 +10,9 @@ import type { TerminalApi } from "../../../api/terminal-api"; import terminalSpawningPoolInjectable from "./terminal-spawning-pool.injectable"; import terminalConfigInjectable from "../../../../common/user-store/terminal-config.injectable"; import terminalCopyOnSelectInjectable from "../../../../common/user-store/terminal-copy-on-select.injectable"; -import themeStoreInjectable from "../../../themes/store.injectable"; import isMacInjectable from "../../../../common/vars/is-mac.injectable"; import openLinkInBrowserInjectable from "../../../../common/utils/open-link-in-browser.injectable"; +import xtermColorThemeInjectable from "../../../themes/terminal-colors.injectable"; export type CreateTerminal = (tabId: TabId, api: TerminalApi) => Terminal; @@ -23,9 +23,9 @@ const createTerminalInjectable = getInjectable({ spawningPool: di.inject(terminalSpawningPoolInjectable), terminalConfig: di.inject(terminalConfigInjectable), terminalCopyOnSelect: di.inject(terminalCopyOnSelectInjectable), - themeStore: di.inject(themeStoreInjectable), isMac: di.inject(isMacInjectable), openLinkInBrowser: di.inject(openLinkInBrowserInjectable), + xtermColorTheme: di.inject(xtermColorThemeInjectable), }; return (tabId, api) => new Terminal(dependencies, { tabId, api }); diff --git a/src/renderer/components/dock/terminal/terminal.ts b/src/renderer/components/dock/terminal/terminal.ts index cf89a572c1..541e2b2947 100644 --- a/src/renderer/components/dock/terminal/terminal.ts +++ b/src/renderer/components/dock/terminal/terminal.ts @@ -10,7 +10,6 @@ import { Terminal as XTerm } from "xterm"; import { FitAddon } from "xterm-addon-fit"; import type { TabId } from "../dock/store"; import type { TerminalApi } from "../../../api/terminal-api"; -import type { ThemeStore } from "../../../themes/store"; import { disposer } from "../../../utils"; import { once } from "lodash"; import { clipboard } from "electron"; @@ -25,8 +24,8 @@ export interface TerminalDependencies { readonly spawningPool: HTMLElement; readonly terminalConfig: IComputedValue; readonly terminalCopyOnSelect: IComputedValue; - readonly themeStore: ThemeStore; readonly isMac: boolean; + readonly xtermColorTheme: IComputedValue>; openLinkInBrowser: OpenLinkInBrowser; } @@ -75,10 +74,6 @@ export class Terminal { return this.dependencies.terminalConfig.get().fontSize; } - get theme(): Record { - return this.dependencies.themeStore.xtermColors; - } - constructor(protected readonly dependencies: TerminalDependencies, { tabId, api, @@ -120,9 +115,12 @@ export class Terminal { this.disposer.push( this.xterm.registerLinkProvider(linkProvider), - reaction(() => this.theme, colors => this.xterm.setOption("theme", colors), { - fireImmediately: true, - }), + reaction(() => this.dependencies.xtermColorTheme.get(), + colors => this.xterm.options.theme = colors, + { + fireImmediately: true, + }, + ), reaction(() => this.fontSize, this.setFontSize, { fireImmediately: true }), reaction(() => this.fontFamily, this.setFontFamily, { fireImmediately: true }), () => onDataHandler.dispose(), diff --git a/src/renderer/components/dock/terminal/view.tsx b/src/renderer/components/dock/terminal/view.tsx index 6fa9e157c8..b85824cc30 100644 --- a/src/renderer/components/dock/terminal/view.tsx +++ b/src/renderer/components/dock/terminal/view.tsx @@ -10,7 +10,7 @@ import { disposeOnUnmount, observer } from "mobx-react"; import { cssNames } from "../../../utils"; import type { Terminal } from "./terminal"; import type { TerminalStore } from "./store"; -import type { LensTheme } from "../../../themes/store"; +import type { LensTheme } from "../../../themes/lens-theme"; import type { DockTab, DockStore } from "../dock/store"; import { withInjectables } from "@ogre-tools/injectable-react"; import dockStoreInjectable from "../dock/store.injectable"; diff --git a/src/renderer/components/item-object-list/content.tsx b/src/renderer/components/item-object-list/content.tsx index 530d6255cb..e338f74514 100644 --- a/src/renderer/components/item-object-list/content.tsx +++ b/src/renderer/components/item-object-list/content.tsx @@ -21,7 +21,7 @@ import { NoItems } from "../no-items"; import { Spinner } from "../spinner"; import type { ItemObject } from "../../../common/item.store"; import type { Filter, PageFiltersStore } from "./page-filters/store"; -import type { LensTheme } from "../../themes/store"; +import type { LensTheme } from "../../themes/lens-theme"; import { MenuActions } from "../menu/menu-actions"; import { MenuItem } from "../menu"; import { Checkbox } from "../checkbox"; diff --git a/src/renderer/components/monaco-editor/monaco-editor.tsx b/src/renderer/components/monaco-editor/monaco-editor.tsx index 09032bbf7e..93aa3c70ce 100644 --- a/src/renderer/components/monaco-editor/monaco-editor.tsx +++ b/src/renderer/components/monaco-editor/monaco-editor.tsx @@ -14,7 +14,7 @@ import { type MonacoValidator, monacoValidators } from "./monaco-validators"; import { debounce, merge } from "lodash"; import { autoBind, cssNames, disposer } from "../../utils"; import type { UserStore } from "../../../common/user-store"; -import type { LensTheme } from "../../themes/store"; +import type { LensTheme } from "../../themes/lens-theme"; import { withInjectables } from "@ogre-tools/injectable-react"; import userStoreInjectable from "../../../common/user-store/user-store.injectable"; import activeThemeInjectable from "../../themes/active.injectable"; diff --git a/src/renderer/components/select/select.tsx b/src/renderer/components/select/select.tsx index 629c11cecf..2d07442b1f 100644 --- a/src/renderer/components/select/select.tsx +++ b/src/renderer/components/select/select.tsx @@ -13,7 +13,7 @@ import { action, computed, makeObservable } from "mobx"; import { observer } from "mobx-react"; import ReactSelect, { components, createFilter } from "react-select"; import type { Props as ReactSelectProps, GroupBase, MultiValue, OptionsOrGroups, PropsValue, SingleValue } from "react-select"; -import type { LensTheme } from "../../themes/store"; +import type { LensTheme } from "../../themes/lens-theme"; import { autoBind, cssNames } from "../../utils"; import { withInjectables } from "@ogre-tools/injectable-react"; import activeThemeInjectable from "../../themes/active.injectable"; diff --git a/src/renderer/themes/active-type.injectable.ts b/src/renderer/themes/active-type.injectable.ts index 9c67454d50..483a1474ca 100644 --- a/src/renderer/themes/active-type.injectable.ts +++ b/src/renderer/themes/active-type.injectable.ts @@ -5,8 +5,8 @@ import { getInjectable } from "@ogre-tools/injectable"; import type { IComputedValue } from "mobx"; import { computed } from "mobx"; -import type { LensThemeType } from "./store"; -import themeStoreInjectable from "./store.injectable"; +import activeThemeInjectable from "./active.injectable"; +import type { LensThemeType } from "./lens-theme"; export type ActiveThemeType = IComputedValue; @@ -14,9 +14,9 @@ const activeThemeTypeInjectable = getInjectable({ id: "active-theme-type", instantiate: (di) => { - const store = di.inject(themeStoreInjectable); + const activeTheme = di.inject(activeThemeInjectable); - return computed(() => store.activeTheme.type); + return computed(() => activeTheme.get().type); }, }); diff --git a/src/renderer/themes/active.injectable.ts b/src/renderer/themes/active.injectable.ts index e83c4ff5fd..e22aee237b 100644 --- a/src/renderer/themes/active.injectable.ts +++ b/src/renderer/themes/active.injectable.ts @@ -3,15 +3,37 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; +import assert from "assert"; import { computed } from "mobx"; -import themeStoreInjectable from "./store.injectable"; +import lensColorThemePreferenceInjectable from "../../common/user-store/lens-color-theme.injectable"; +import { lensThemeDeclarationInjectionToken } from "./declaration"; +import defaultLensThemeInjectable from "./default-theme.injectable"; +import systemThemeConfigurationInjectable from "./system-theme.injectable"; +import lensThemesInjectable from "./themes.injectable"; const activeThemeInjectable = getInjectable({ id: "active-theme", instantiate: (di) => { - const store = di.inject(themeStoreInjectable); + const lensThemes = di.inject(lensThemesInjectable); + const themeDecls = di.injectMany(lensThemeDeclarationInjectionToken); + const lensColorThemePreference = di.inject(lensColorThemePreferenceInjectable); + const systemThemeConfiguration = di.inject(systemThemeConfigurationInjectable); + const defaultLensTheme = di.inject(defaultLensThemeInjectable); - return computed(() => store.activeTheme); + return computed(() => { + const pref = lensColorThemePreference.get(); + + if (pref.useSystemTheme) { + const systemThemeType = systemThemeConfiguration.get(); + const matchingTheme = themeDecls.find(theme => theme.type === systemThemeType); + + assert(matchingTheme, `Missing theme declaration for system theme "${systemThemeType}"`); + + return matchingTheme; + } + + return lensThemes.get(pref.lensThemeId) ?? defaultLensTheme; + }); }, }); diff --git a/src/renderer/themes/apply-lens-theme.injectable.ts b/src/renderer/themes/apply-lens-theme.injectable.ts new file mode 100644 index 0000000000..72b721aa14 --- /dev/null +++ b/src/renderer/themes/apply-lens-theme.injectable.ts @@ -0,0 +1,38 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import loggerInjectable from "../../common/logger.injectable"; +import userStoreInjectable from "../../common/user-store/user-store.injectable"; +import { object } from "../utils"; +import type { LensTheme } from "./lens-theme"; + +export type ApplyLensTheme = (theme: LensTheme) => void; + +const applyLensThemeInjectable = getInjectable({ + id: "apply-lens-theme", + instantiate: (di): ApplyLensTheme => { + const logger = di.inject(loggerInjectable); + const userStore = di.inject(userStoreInjectable); + + return (theme) => { + try { + const colors = object.entries(theme.colors); + + for (const [name, value] of colors) { + document.documentElement.style.setProperty(`--${name}`, value); + } + + // Adding universal theme flag which can be used in component styles + document.body.classList.toggle("theme-light", theme.type === "light"); + } catch (error) { + logger.error("[THEME]: Failed to apply active theme", error); + userStore.resetTheme(); + } + }; + }, + causesSideEffects: true, +}); + +export default applyLensThemeInjectable; diff --git a/src/renderer/themes/apply-theme.global-override-for-injectable.ts b/src/renderer/themes/apply-theme.global-override-for-injectable.ts new file mode 100644 index 0000000000..c8283843e1 --- /dev/null +++ b/src/renderer/themes/apply-theme.global-override-for-injectable.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getGlobalOverride } from "../../common/test-utils/get-global-override"; +import applyLensThemeInjectable from "./apply-lens-theme.injectable"; + +export default getGlobalOverride(applyLensThemeInjectable, () => () => {}); diff --git a/src/renderer/themes/declaration.ts b/src/renderer/themes/declaration.ts new file mode 100644 index 0000000000..f5dff2d842 --- /dev/null +++ b/src/renderer/themes/declaration.ts @@ -0,0 +1,12 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getInjectionToken } from "@ogre-tools/injectable"; +import type { ReadonlyDeep } from "type-fest"; +import type { LensTheme } from "./lens-theme"; + +export const lensThemeDeclarationInjectionToken = getInjectionToken>({ + id: "lens-theme-declaration", +}); diff --git a/src/renderer/themes/default-theme.injectable.ts b/src/renderer/themes/default-theme.injectable.ts new file mode 100644 index 0000000000..dafed0210a --- /dev/null +++ b/src/renderer/themes/default-theme.injectable.ts @@ -0,0 +1,26 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { lensThemeDeclarationInjectionToken } from "./declaration"; + +const defaultLensThemeInjectable = getInjectable({ + id: "default-lens-theme", + instantiate: (di) => { + const themes = di.injectMany(lensThemeDeclarationInjectionToken); + const [defaultTheme, ...rest] = themes.filter(theme => theme.isDefault); + + if (rest.length > 0) { + throw new Error("Multiple LensTheme's are declared as the default"); + } + + if (!defaultTheme) { + throw new Error("No LensTheme is declared as the default"); + } + + return defaultTheme; + }, +}); + +export default defaultLensThemeInjectable; diff --git a/src/renderer/themes/lens-dark.injectable.ts b/src/renderer/themes/lens-dark.injectable.ts new file mode 100644 index 0000000000..99929d6a5b --- /dev/null +++ b/src/renderer/themes/lens-dark.injectable.ts @@ -0,0 +1,156 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { lensThemeDeclarationInjectionToken } from "./declaration"; + +const lensDarkThemeInjectable = getInjectable({ + id: "lens-dark-theme", + instantiate: () => ({ + "name": "Dark", + "type": "dark" as const, + "description": "Original Lens dark theme", + "author": "Mirantis", + "monacoTheme": "clouds-midnight" as const, + "colors": { + "blue": "#3d90ce", + "magenta": "#c93dce", + "golden": "#ffc63d", + "halfGray": "#87909c80", + "primary": "#3d90ce", + "textColorPrimary": "#8e9297", + "textColorSecondary": "#a0a0a0", + "textColorTertiary": "#909ba6", + "textColorAccent": "#ffffff", + "textColorDimmed": "#8e92978c", + "borderColor": "#4c5053", + "borderFaintColor": "#373a3e", + "mainBackground": "#1e2124", + "secondaryBackground": "#1e2125", + "contentColor": "#262b2f", + "layoutBackground": "#2e3136", + "layoutTabsBackground": "#252729", + "layoutTabsActiveColor": "#ffffff", + "layoutTabsLineColor": "#87909c80", + "sidebarBackground": "#36393e", + "sidebarLogoBackground": "#414448", + "sidebarActiveColor": "#ffffff", + "sidebarSubmenuActiveColor": "#ffffff", + "sidebarItemHoverBackground": "#3a3e44", + "badgeBackgroundColor": "#ffba44", + "buttonPrimaryBackground": "#3d90ce", + "buttonDefaultBackground": "#414448", + "buttonLightBackground": "#f1f1f1", + "buttonAccentBackground": "#e85555", + "buttonDisabledBackground": "#808080", + "tableBgcStripe": "#2a2d33", + "tableBgcSelected": "#383c42", + "tableHeaderBackground": "#262b2f", + "tableHeaderBorderWidth": "1px", + "tableHeaderBorderColor": "#36393e", + "tableHeaderColor": "#ffffff", + "tableSelectedRowColor": "#ffffff", + "helmLogoBackground": "#ffffff", + "helmStableRepo": "#3d90ce", + "helmIncubatorRepo": "#ff7043", + "helmDescriptionHr": "#41474a", + "helmDescriptionBlockquoteColor": "#bbb", + "helmDescriptionBlockquoteBorder": "#8a8f93", + "helmDescriptionBlockquoteBackground": "#3b4348", + "helmDescriptionHeaders": "#3e4147", + "helmDescriptionH6": "#6a737d", + "helmDescriptionTdBorder": "#47494a", + "helmDescriptionTrBackground": "#1c2125", + "helmDescriptionCodeBackground": "#ffffff1a", + "helmDescriptionPreBackground": "#1b1f21", + "helmDescriptionPreColor": "#b4b5b4", + "colorSuccess": "#43a047", + "colorOk": "#4caf50", + "colorInfo": "#3d90ce", + "colorError": "#ce3933", + "colorSoftError": "#e85555", + "colorWarning": "#ff9800", + "colorVague": "#36393e", + "colorTerminated": "#4c5053", + "dockHeadBackground": "#2e3136", + "dockInfoBackground": "#1e2125", + "dockInfoBorderColor": "#303136", + "dockEditorBackground": "#000000", + "dockEditorTag": "#8e97a3", + "dockEditorKeyword": "#ffffff", + "dockEditorComment": "#808080", + "dockEditorActiveLineBackground": "#3a3d41", + "dockBadgeBackground": "#36393e", + "dockTabBorderColor": "#43424d", + "dockTabActiveBackground": "#3a3e45", + "logsBackground": "#000000", + "logsForeground": "#ffffff", + "logRowHoverBackground": "#35373a", + "terminalBackground": "#000000", + "terminalForeground": "#ffffff", + "terminalCursor": "#ffffff", + "terminalCursorAccent": "#000000", + "terminalSelection": "#ffffff77", + "terminalBlack": "#2e3436", + "terminalRed": "#cc0000", + "terminalGreen": "#4e9a06", + "terminalYellow": "#c4a000", + "terminalBlue": "#3465a4", + "terminalMagenta": "#75507b", + "terminalCyan": "#06989a", + "terminalWhite": "#d3d7cf", + "terminalBrightBlack": "#555753", + "terminalBrightRed": "#ef2929", + "terminalBrightGreen": "#8ae234", + "terminalBrightYellow": "#fce94f", + "terminalBrightBlue": "#729fcf", + "terminalBrightMagenta": "#ad7fa8", + "terminalBrightCyan": "#34e2e2", + "terminalBrightWhite": "#eeeeec", + "dialogTextColor": "#87909c", + "dialogBackground": "#ffffff", + "dialogHeaderBackground": "#36393e", + "dialogFooterBackground": "#f4f4f4", + "drawerTogglerBackground": "#2f343a", + "drawerTitleText": "#ffffff", + "drawerSubtitleBackground": "#373a3e", + "drawerItemNameColor": "#87909c", + "drawerItemValueColor": "#a0a0a0", + "clusterMenuBackground": "#252729", + "clusterMenuBorderColor": "#252729", + "clusterMenuCellBackground": "#2e3136", + "clusterSettingsBackground": "#1e2124", + "addClusterIconColor": "#252729", + "boxShadow": "#0000003a", + "iconActiveColor": "#ffffff", + "iconActiveBackground": "#ffffff18", + "filterAreaBackground": "#23272b", + "chartLiveBarBackground": "#00000033", + "chartStripesColor": "#ffffff08", + "chartCapacityColor": "#4c545f", + "pieChartDefaultColor": "#30353a", + "inputOptionHoverColor": "#87909c", + "inputControlBackground": "#1e2125", + "inputControlBorder": "#414448", + "inputControlHoverBorder": "#474a4f", + "lineProgressBackground": "#414448", + "radioActiveBackground": "#36393e", + "menuActiveBackground": "#3d90ce", + "menuSelectedOptionBgc": "#36393e", + "canvasBackground": "#24292e", + "scrollBarColor": "#5f6064", + "settingsBackground": "#262b2e", + "settingsColor": "#909ba6", + "navSelectedBackground": "#262b2e", + "navHoverColor": "#dcddde", + "hrColor": "#ffffff0f", + "tooltipBackground": "#18191c", + }, + isDefault: true, + }), + injectionToken: lensThemeDeclarationInjectionToken, +}); + +export default lensDarkThemeInjectable; + diff --git a/src/renderer/themes/lens-dark.ts b/src/renderer/themes/lens-dark.ts deleted file mode 100644 index da4d9dc0c9..0000000000 --- a/src/renderer/themes/lens-dark.ts +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { LensTheme } from "./store"; - -const lensDarkTheme: LensTheme = { - "name": "Dark", - "type": "dark", - "description": "Original Lens dark theme", - "author": "Mirantis", - "monacoTheme": "clouds-midnight", - "colors": { - "blue": "#3d90ce", - "magenta": "#c93dce", - "golden": "#ffc63d", - "halfGray": "#87909c80", - "primary": "#3d90ce", - "textColorPrimary": "#8e9297", - "textColorSecondary": "#a0a0a0", - "textColorTertiary": "#909ba6", - "textColorAccent": "#ffffff", - "textColorDimmed": "#8e92978c", - "borderColor": "#4c5053", - "borderFaintColor": "#373a3e", - "mainBackground": "#1e2124", - "secondaryBackground": "#1e2125", - "contentColor": "#262b2f", - "layoutBackground": "#2e3136", - "layoutTabsBackground": "#252729", - "layoutTabsActiveColor": "#ffffff", - "layoutTabsLineColor": "#87909c80", - "sidebarBackground": "#36393e", - "sidebarLogoBackground": "#414448", - "sidebarActiveColor": "#ffffff", - "sidebarSubmenuActiveColor": "#ffffff", - "sidebarItemHoverBackground": "#3a3e44", - "badgeBackgroundColor": "#ffba44", - "buttonPrimaryBackground": "#3d90ce", - "buttonDefaultBackground": "#414448", - "buttonLightBackground": "#f1f1f1", - "buttonAccentBackground": "#e85555", - "buttonDisabledBackground": "#808080", - "tableBgcStripe": "#2a2d33", - "tableBgcSelected": "#383c42", - "tableHeaderBackground": "#262b2f", - "tableHeaderBorderWidth": "1px", - "tableHeaderBorderColor": "#36393e", - "tableHeaderColor": "#ffffff", - "tableSelectedRowColor": "#ffffff", - "helmLogoBackground": "#ffffff", - "helmStableRepo": "#3d90ce", - "helmIncubatorRepo": "#ff7043", - "helmDescriptionHr": "#41474a", - "helmDescriptionBlockquoteColor": "#bbb", - "helmDescriptionBlockquoteBorder": "#8a8f93", - "helmDescriptionBlockquoteBackground": "#3b4348", - "helmDescriptionHeaders": "#3e4147", - "helmDescriptionH6": "#6a737d", - "helmDescriptionTdBorder": "#47494a", - "helmDescriptionTrBackground": "#1c2125", - "helmDescriptionCodeBackground": "#ffffff1a", - "helmDescriptionPreBackground": "#1b1f21", - "helmDescriptionPreColor": "#b4b5b4", - "colorSuccess": "#43a047", - "colorOk": "#4caf50", - "colorInfo": "#3d90ce", - "colorError": "#ce3933", - "colorSoftError": "#e85555", - "colorWarning": "#ff9800", - "colorVague": "#36393e", - "colorTerminated": "#4c5053", - "dockHeadBackground": "#2e3136", - "dockInfoBackground": "#1e2125", - "dockInfoBorderColor": "#303136", - "dockEditorBackground": "#000000", - "dockEditorTag": "#8e97a3", - "dockEditorKeyword": "#ffffff", - "dockEditorComment": "#808080", - "dockEditorActiveLineBackground": "#3a3d41", - "dockBadgeBackground": "#36393e", - "dockTabBorderColor": "#43424d", - "dockTabActiveBackground": "#3a3e45", - "logsBackground": "#000000", - "logsForeground": "#ffffff", - "logRowHoverBackground": "#35373a", - "terminalBackground": "#000000", - "terminalForeground": "#ffffff", - "terminalCursor": "#ffffff", - "terminalCursorAccent": "#000000", - "terminalSelection": "#ffffff77", - "terminalBlack": "#2e3436", - "terminalRed": "#cc0000", - "terminalGreen": "#4e9a06", - "terminalYellow": "#c4a000", - "terminalBlue": "#3465a4", - "terminalMagenta": "#75507b", - "terminalCyan": "#06989a", - "terminalWhite": "#d3d7cf", - "terminalBrightBlack": "#555753", - "terminalBrightRed": "#ef2929", - "terminalBrightGreen": "#8ae234", - "terminalBrightYellow": "#fce94f", - "terminalBrightBlue": "#729fcf", - "terminalBrightMagenta": "#ad7fa8", - "terminalBrightCyan": "#34e2e2", - "terminalBrightWhite": "#eeeeec", - "dialogTextColor": "#87909c", - "dialogBackground": "#ffffff", - "dialogHeaderBackground": "#36393e", - "dialogFooterBackground": "#f4f4f4", - "drawerTogglerBackground": "#2f343a", - "drawerTitleText": "#ffffff", - "drawerSubtitleBackground": "#373a3e", - "drawerItemNameColor": "#87909c", - "drawerItemValueColor": "#a0a0a0", - "clusterMenuBackground": "#252729", - "clusterMenuBorderColor": "#252729", - "clusterMenuCellBackground": "#2e3136", - "clusterSettingsBackground": "#1e2124", - "addClusterIconColor": "#252729", - "boxShadow": "#0000003a", - "iconActiveColor": "#ffffff", - "iconActiveBackground": "#ffffff18", - "filterAreaBackground": "#23272b", - "chartLiveBarBackground": "#00000033", - "chartStripesColor": "#ffffff08", - "chartCapacityColor": "#4c545f", - "pieChartDefaultColor": "#30353a", - "inputOptionHoverColor": "#87909c", - "inputControlBackground": "#1e2125", - "inputControlBorder": "#414448", - "inputControlHoverBorder": "#474a4f", - "lineProgressBackground": "#414448", - "radioActiveBackground": "#36393e", - "menuActiveBackground": "#3d90ce", - "menuSelectedOptionBgc": "#36393e", - "canvasBackground": "#24292e", - "scrollBarColor": "#5f6064", - "settingsBackground": "#262b2e", - "settingsColor": "#909ba6", - "navSelectedBackground": "#262b2e", - "navHoverColor": "#dcddde", - "hrColor": "#ffffff0f", - "tooltipBackground": "#18191c", - }, -}; - -export default lensDarkTheme; diff --git a/src/renderer/themes/lens-light.injectable.ts b/src/renderer/themes/lens-light.injectable.ts new file mode 100644 index 0000000000..d7e4618c4c --- /dev/null +++ b/src/renderer/themes/lens-light.injectable.ts @@ -0,0 +1,154 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { lensThemeDeclarationInjectionToken } from "./declaration"; + +const lensLightThemeInjectable = getInjectable({ + id: "lens-light-theme", + instantiate: () => ({ + "name": "Light", + "type": "light" as const, + "description": "Original Lens light theme", + "author": "Mirantis", + "monacoTheme": "vs" as const, + "colors": { + "blue": "#3d90ce", + "magenta": "#c93dce", + "golden": "#ffc63d", + "halfGray": "#87909c80", + "primary": "#3d90ce", + "textColorPrimary": "#555555", + "textColorSecondary": "#51575d", + "textColorTertiary": "#555555", + "textColorAccent": "#222222", + "textColorDimmed": "#5557598c", + "borderColor": "#c9cfd3", + "borderFaintColor": "#dfdfdf", + "mainBackground": "#f1f1f1", + "secondaryBackground": "#f2f3f5", + "contentColor": "#ffffff", + "layoutBackground": "#e8e8e8", + "layoutTabsBackground": "#f8f8f8", + "layoutTabsActiveColor": "#333333", + "layoutTabsLineColor": "#87909c80", + "sidebarLogoBackground": "#f1f1f1", + "sidebarActiveColor": "#ffffff", + "sidebarSubmenuActiveColor": "#3d90ce", + "sidebarBackground": "#e8e8e8", + "sidebarItemHoverBackground": "#f0f2f5", + "badgeBackgroundColor": "#ffba44", + "buttonPrimaryBackground": "#3d90ce", + "buttonDefaultBackground": "#414448", + "buttonLightBackground": "#f1f1f1", + "buttonAccentBackground": "#e85555", + "buttonDisabledBackground": "#808080", + "tableBgcStripe": "#f8f8f8", + "tableBgcSelected": "#f4f5f5", + "tableHeaderBackground": "#f1f1f1", + "tableHeaderBorderWidth": "2px", + "tableHeaderBorderColor": "#3d90ce", + "tableHeaderColor": "#555555", + "tableSelectedRowColor": "#222222", + "helmLogoBackground": "#ffffff", + "helmStableRepo": "#3d90ce", + "helmIncubatorRepo": "#ff7043", + "helmDescriptionHr": "#dddddd", + "helmDescriptionBlockquoteColor": "#555555", + "helmDescriptionBlockquoteBorder": "#8a8f93", + "helmDescriptionBlockquoteBackground": "#eeeeee", + "helmDescriptionHeaders": "#3e4147", + "helmDescriptionH6": "#6a737d", + "helmDescriptionTdBorder": "#c6c6c6", + "helmDescriptionTrBackground": "#1c2125", + "helmDescriptionCodeBackground": "#ffffff1a", + "helmDescriptionPreBackground": "#eeeeee", + "helmDescriptionPreColor": "#555555", + "colorSuccess": "#206923", + "colorOk": "#399c3d", + "colorInfo": "#2d71a4", + "colorError": "#ce3933", + "colorSoftError": "#e85555", + "colorWarning": "#ff9800", + "colorVague": "#ededed", + "colorTerminated": "#9dabb5", + "dockHeadBackground": "#e8e8e8", + "dockInfoBackground": "#f3f3f3", + "dockInfoBorderColor": "#c9cfd3", + "dockEditorBackground": "#24292e", + "dockEditorTag": "#8e97a3", + "dockEditorKeyword": "#ffffff", + "dockEditorComment": "#808080", + "dockEditorActiveLineBackground": "#3a3d41", + "dockBadgeBackground": "#dedede", + "dockTabBorderColor": "#d5d4de", + "dockTabActiveBackground": "#ffffff", + "logsBackground": "#24292e", + "logsForeground": "#ffffff", + "logRowHoverBackground": "#35373a", + "terminalBackground": "#ffffff", + "terminalForeground": "#2d2d2d", + "terminalCursor": "#2d2d2d", + "terminalCursorAccent": "#ffffff", + "terminalSelection": "#bfbfbf", + "terminalBlack": "#2d2d2d", + "terminalRed": "#cd3734 ", + "terminalGreen": "#18cf12", + "terminalYellow": "#acb300", + "terminalBlue": "#3d90ce", + "terminalMagenta": "#c100cd", + "terminalCyan": "#07c4b9", + "terminalWhite": "#d3d7cf", + "terminalBrightBlack": "#a8a8a8", + "terminalBrightRed": "#ff6259", + "terminalBrightGreen": "#5cdb59", + "terminalBrightYellow": "#f8c000", + "terminalBrightBlue": "#008db6", + "terminalBrightMagenta": "#ee55f8", + "terminalBrightCyan": "#50e8df", + "terminalBrightWhite": "#eeeeec", + "dialogTextColor": "#87909c", + "dialogBackground": "#ffffff", + "dialogHeaderBackground": "#36393e", + "dialogFooterBackground": "#f4f4f4", + "drawerTogglerBackground": "#eaeced", + "drawerTitleText": "#ffffff", + "drawerSubtitleBackground": "#f1f1f1", + "drawerItemNameColor": "#727272", + "drawerItemValueColor": "#555555", + "clusterMenuBackground": "#d7d8da", + "clusterMenuBorderColor": "#c9cfd3", + "clusterMenuCellBackground": "#bbbbbb", + "clusterSettingsBackground": "#ffffff", + "addClusterIconColor": "#8d8d8d", + "boxShadow": "#0000003a", + "iconActiveColor": "#ffffff", + "iconActiveBackground": "#a6a6a694", + "filterAreaBackground": "#f7f7f7", + "chartLiveBarBackground": "#00000033", + "chartStripesColor": "#00000009", + "chartCapacityColor": "#cccccc", + "pieChartDefaultColor": "#efefef", + "inputOptionHoverColor": "#ffffff", + "inputControlBackground": "#f6f6f7", + "inputControlBorder": "#cccdcf", + "inputControlHoverBorder": "#b9bbbe", + "lineProgressBackground": "#e8e8e8", + "radioActiveBackground": "#f1f1f1", + "menuActiveBackground": "#3d90ce", + "menuSelectedOptionBgc": "#e8e8e8", + "canvasBackground": "#24292e", + "scrollBarColor": "#bbbbbb", + "settingsBackground": "#ffffff", + "settingsColor": "#555555", + "navSelectedBackground": "#ffffff", + "navHoverColor": "#2e3135", + "hrColor": "#06060714", + "tooltipBackground": "#ffffff", + }, + }), + injectionToken: lensThemeDeclarationInjectionToken, +}); + +export default lensLightThemeInjectable; diff --git a/src/renderer/themes/lens-light.ts b/src/renderer/themes/lens-light.ts deleted file mode 100644 index f7fcbc23e2..0000000000 --- a/src/renderer/themes/lens-light.ts +++ /dev/null @@ -1,149 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { LensTheme } from "./store"; - -const lensLightTheme: LensTheme = { - "name": "Light", - "type": "light", - "description": "Original Lens light theme", - "author": "Mirantis", - "monacoTheme": "vs", - "colors": { - "blue": "#3d90ce", - "magenta": "#c93dce", - "golden": "#ffc63d", - "halfGray": "#87909c80", - "primary": "#3d90ce", - "textColorPrimary": "#555555", - "textColorSecondary": "#51575d", - "textColorTertiary": "#555555", - "textColorAccent": "#222222", - "textColorDimmed": "#5557598c", - "borderColor": "#c9cfd3", - "borderFaintColor": "#dfdfdf", - "mainBackground": "#f1f1f1", - "secondaryBackground": "#f2f3f5", - "contentColor": "#ffffff", - "layoutBackground": "#e8e8e8", - "layoutTabsBackground": "#f8f8f8", - "layoutTabsActiveColor": "#333333", - "layoutTabsLineColor": "#87909c80", - "sidebarLogoBackground": "#f1f1f1", - "sidebarActiveColor": "#ffffff", - "sidebarSubmenuActiveColor": "#3d90ce", - "sidebarBackground": "#e8e8e8", - "sidebarItemHoverBackground": "#f0f2f5", - "badgeBackgroundColor": "#ffba44", - "buttonPrimaryBackground": "#3d90ce", - "buttonDefaultBackground": "#414448", - "buttonLightBackground": "#f1f1f1", - "buttonAccentBackground": "#e85555", - "buttonDisabledBackground": "#808080", - "tableBgcStripe": "#f8f8f8", - "tableBgcSelected": "#f4f5f5", - "tableHeaderBackground": "#f1f1f1", - "tableHeaderBorderWidth": "2px", - "tableHeaderBorderColor": "#3d90ce", - "tableHeaderColor": "#555555", - "tableSelectedRowColor": "#222222", - "helmLogoBackground": "#ffffff", - "helmStableRepo": "#3d90ce", - "helmIncubatorRepo": "#ff7043", - "helmDescriptionHr": "#dddddd", - "helmDescriptionBlockquoteColor": "#555555", - "helmDescriptionBlockquoteBorder": "#8a8f93", - "helmDescriptionBlockquoteBackground": "#eeeeee", - "helmDescriptionHeaders": "#3e4147", - "helmDescriptionH6": "#6a737d", - "helmDescriptionTdBorder": "#c6c6c6", - "helmDescriptionTrBackground": "#1c2125", - "helmDescriptionCodeBackground": "#ffffff1a", - "helmDescriptionPreBackground": "#eeeeee", - "helmDescriptionPreColor": "#555555", - "colorSuccess": "#206923", - "colorOk": "#399c3d", - "colorInfo": "#2d71a4", - "colorError": "#ce3933", - "colorSoftError": "#e85555", - "colorWarning": "#ff9800", - "colorVague": "#ededed", - "colorTerminated": "#9dabb5", - "dockHeadBackground": "#e8e8e8", - "dockInfoBackground": "#f3f3f3", - "dockInfoBorderColor": "#c9cfd3", - "dockEditorBackground": "#24292e", - "dockEditorTag": "#8e97a3", - "dockEditorKeyword": "#ffffff", - "dockEditorComment": "#808080", - "dockEditorActiveLineBackground": "#3a3d41", - "dockBadgeBackground": "#dedede", - "dockTabBorderColor": "#d5d4de", - "dockTabActiveBackground": "#ffffff", - "logsBackground": "#24292e", - "logsForeground": "#ffffff", - "logRowHoverBackground": "#35373a", - "terminalBackground": "#ffffff", - "terminalForeground": "#2d2d2d", - "terminalCursor": "#2d2d2d", - "terminalCursorAccent": "#ffffff", - "terminalSelection": "#bfbfbf", - "terminalBlack": "#2d2d2d", - "terminalRed": "#cd3734 ", - "terminalGreen": "#18cf12", - "terminalYellow": "#acb300", - "terminalBlue": "#3d90ce", - "terminalMagenta": "#c100cd", - "terminalCyan": "#07c4b9", - "terminalWhite": "#d3d7cf", - "terminalBrightBlack": "#a8a8a8", - "terminalBrightRed": "#ff6259", - "terminalBrightGreen": "#5cdb59", - "terminalBrightYellow": "#f8c000", - "terminalBrightBlue": "#008db6", - "terminalBrightMagenta": "#ee55f8", - "terminalBrightCyan": "#50e8df", - "terminalBrightWhite": "#eeeeec", - "dialogTextColor": "#87909c", - "dialogBackground": "#ffffff", - "dialogHeaderBackground": "#36393e", - "dialogFooterBackground": "#f4f4f4", - "drawerTogglerBackground": "#eaeced", - "drawerTitleText": "#ffffff", - "drawerSubtitleBackground": "#f1f1f1", - "drawerItemNameColor": "#727272", - "drawerItemValueColor": "#555555", - "clusterMenuBackground": "#d7d8da", - "clusterMenuBorderColor": "#c9cfd3", - "clusterMenuCellBackground": "#bbbbbb", - "clusterSettingsBackground": "#ffffff", - "addClusterIconColor": "#8d8d8d", - "boxShadow": "#0000003a", - "iconActiveColor": "#ffffff", - "iconActiveBackground": "#a6a6a694", - "filterAreaBackground": "#f7f7f7", - "chartLiveBarBackground": "#00000033", - "chartStripesColor": "#00000009", - "chartCapacityColor": "#cccccc", - "pieChartDefaultColor": "#efefef", - "inputOptionHoverColor": "#ffffff", - "inputControlBackground": "#f6f6f7", - "inputControlBorder": "#cccdcf", - "inputControlHoverBorder": "#b9bbbe", - "lineProgressBackground": "#e8e8e8", - "radioActiveBackground": "#f1f1f1", - "menuActiveBackground": "#3d90ce", - "menuSelectedOptionBgc": "#e8e8e8", - "canvasBackground": "#24292e", - "scrollBarColor": "#bbbbbb", - "settingsBackground": "#ffffff", - "settingsColor": "#555555", - "navSelectedBackground": "#ffffff", - "navHoverColor": "#2e3135", - "hrColor": "#06060714", - "tooltipBackground": "#ffffff", - }, -}; - -export default lensLightTheme; diff --git a/src/renderer/themes/lens-theme.ts b/src/renderer/themes/lens-theme.ts new file mode 100644 index 0000000000..951f3e4206 --- /dev/null +++ b/src/renderer/themes/lens-theme.ts @@ -0,0 +1,18 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { MonacoTheme } from "../components/monaco-editor"; + +export type ThemeId = string; +export type LensThemeType = "dark" | "light"; +export interface LensTheme { + name: string; + type: LensThemeType; + colors: Record; + description: string; + author: string; + monacoTheme: MonacoTheme; + isDefault?: boolean; +} diff --git a/src/renderer/themes/setup-apply-active-theme.injectable.ts b/src/renderer/themes/setup-apply-active-theme.injectable.ts new file mode 100644 index 0000000000..ee8444a0b7 --- /dev/null +++ b/src/renderer/themes/setup-apply-active-theme.injectable.ts @@ -0,0 +1,35 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { reaction } from "mobx"; +import initializeSystemThemeTypeInjectable from "../../features/theme/system-type/renderer/initialize.injectable"; +import { beforeFrameStartsInjectionToken } from "../before-frame-starts/tokens"; +import activeThemeInjectable from "./active.injectable"; +import applyLensThemeInjectable from "./apply-lens-theme.injectable"; + +const setupApplyActiveThemeInjectable = getInjectable({ + id: "setup-apply-active-theme", + instantiate: (di) => { + const activeTheme = di.inject(activeThemeInjectable); + const applyLensTheme = di.inject(applyLensThemeInjectable); + + return { + id: "setup-apply-active-theme", + run: () => { + reaction( + () => activeTheme.get(), + applyLensTheme, + { + fireImmediately: true, + }, + ); + }, + runAfter: di.inject(initializeSystemThemeTypeInjectable), + }; + }, + injectionToken: beforeFrameStartsInjectionToken, +}); + +export default setupApplyActiveThemeInjectable; diff --git a/src/renderer/themes/store.injectable.ts b/src/renderer/themes/store.injectable.ts deleted file mode 100644 index a67075584e..0000000000 --- a/src/renderer/themes/store.injectable.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import userStoreInjectable from "../../common/user-store/user-store.injectable"; -import ipcRendererInjectable from "../utils/channel/ipc-renderer.injectable"; -import { ThemeStore } from "./store"; - -const themeStoreInjectable = getInjectable({ - id: "theme-store", - - instantiate: (di) => new ThemeStore({ - ipcRenderer: di.inject(ipcRendererInjectable), - userStore: di.inject(userStoreInjectable), - }), -}); - -export default themeStoreInjectable; diff --git a/src/renderer/themes/store.ts b/src/renderer/themes/store.ts deleted file mode 100644 index 1845d17f95..0000000000 --- a/src/renderer/themes/store.ts +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { comparer, computed, makeObservable, observable, reaction } from "mobx"; -import { autoBind } from "../utils"; -import logger from "../../main/logger"; -import lensDarkTheme from "./lens-dark"; -import lensLightTheme from "./lens-light"; -import type { MonacoTheme } from "../components/monaco-editor"; -import { defaultThemeId } from "../../common/vars"; -import { camelCase } from "lodash"; -import type { IpcRenderer } from "electron"; -import { getNativeThemeChannel, setNativeThemeChannel } from "../../common/ipc/native-theme"; -import type { ReadonlyDeep } from "type-fest/source/readonly-deep"; -import assert from "assert"; - -export type ThemeId = string; -export type LensThemeType = "dark" | "light"; -export interface LensTheme { - name: string; - type: LensThemeType; - colors: Record; - description: string; - author: string; - monacoTheme: MonacoTheme; -} - -interface Dependencies { - readonly userStore: { - colorTheme: string; - terminalTheme: ThemeId; - resetTheme(): void; - }; - readonly ipcRenderer: IpcRenderer; -} - -export class ThemeStore { - private terminalColorPrefix = "terminal"; - - #themes = observable.map({ - "lens-dark": lensDarkTheme, - "lens-light": lensLightTheme, - }); - - @observable private osNativeThemeType: "dark" | "light" | undefined; - - @computed private get colorThemePreference(): ThemeId | "system" { - return this.dependencies.userStore.colorTheme; - } - - @computed private get activeThemeId(): ThemeId { - if (this.colorThemePreference === "system") { - if (this.osNativeThemeType) { - return `lens-${this.osNativeThemeType}`; - } else { - return defaultThemeId; - } - } else { - return this.colorThemePreference; - } - } - - @computed private get terminalThemeId(): ThemeId { - return this.dependencies.userStore.terminalTheme; - } - - private readonly defaultTheme: LensTheme; - - @computed get activeTheme(): LensTheme { - return this.themes.get(this.activeThemeId) ?? this.defaultTheme; - } - - @computed private get terminalColors(): [string, string][] { - const theme = this.themes.get(this.terminalThemeId) ?? this.activeTheme; - - return Object - .entries(theme.colors) - .filter(([name]) => name.startsWith(this.terminalColorPrefix)); - } - - // Replacing keys stored in styles to format accepted by terminal - // E.g. terminalBrightBlack -> brightBlack - @computed get xtermColors(): Record { - return Object.fromEntries( - this.terminalColors.map(([name, color]) => [ - camelCase(name.replace(this.terminalColorPrefix, "")), - color, - ]), - ); - } - - get themes() { - return this.#themes as ReadonlyDeep>; - } - - constructor(protected readonly dependencies: Dependencies) { - makeObservable(this); - autoBind(this); - this.init(); - - const defaultTheme = this.#themes.get(defaultThemeId); - - assert(defaultTheme, `${defaultThemeId} is invalid as there is no corresponding theme`); - - this.defaultTheme = defaultTheme; - } - - async init() { - this.osNativeThemeType = await this.dependencies.ipcRenderer.invoke(getNativeThemeChannel); - this.dependencies.ipcRenderer.on(setNativeThemeChannel, (event, theme: "dark" | "light") => { - this.osNativeThemeType = theme; - }); - - // auto-apply active theme - reaction(() => ({ - themeId: this.activeThemeId, - terminalThemeId: this.terminalThemeId, - }), () => { - try { - this.applyActiveTheme(); - } catch (err) { - logger.error(`Failed to apply active theme: ${err}`); - this.dependencies.userStore.resetTheme(); - } - }, { - fireImmediately: true, - equals: comparer.shallow, - }); - } - - getThemeById(themeId: ThemeId): LensTheme | undefined { - return this.themes.get(themeId); - } - - protected applyActiveTheme() { - const colors = Object.entries({ - ...this.activeTheme.colors, - ...Object.fromEntries(this.terminalColors), - }); - - colors.forEach(([name, value]) => { - document.documentElement.style.setProperty(`--${name}`, value); - }); - - // Adding universal theme flag which can be used in component styles - document.body.classList.toggle("theme-light", this.activeTheme.type === "light"); - } -} diff --git a/src/renderer/themes/system-theme.injectable.ts b/src/renderer/themes/system-theme.injectable.ts new file mode 100644 index 0000000000..74b1d6ab04 --- /dev/null +++ b/src/renderer/themes/system-theme.injectable.ts @@ -0,0 +1,13 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { observable } from "mobx"; + +const systemThemeConfigurationInjectable = getInjectable({ + id: "system-theme-configuration", + instantiate: () => observable.box<"dark" | "light">("dark"), +}); + +export default systemThemeConfigurationInjectable; diff --git a/src/renderer/themes/terminal-colors.injectable.ts b/src/renderer/themes/terminal-colors.injectable.ts new file mode 100644 index 0000000000..24de4a6f05 --- /dev/null +++ b/src/renderer/themes/terminal-colors.injectable.ts @@ -0,0 +1,43 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { camelCase } from "lodash"; +import { computed } from "mobx"; +import terminalThemePreferenceInjectable from "../../common/user-store/terminal-theme.injectable"; +import { object } from "../utils"; +import activeThemeInjectable from "./active.injectable"; +import lensThemesInjectable from "./themes.injectable"; + +const terminalColorPrefix = "terminal"; + +const xtermColorThemeInjectable = getInjectable({ + id: "terminal-colors", + instantiate: (di) => { + const activeTheme = di.inject(activeThemeInjectable); + const terminalThemePreference = di.inject(terminalThemePreferenceInjectable); + const themes = di.inject(lensThemesInjectable); + + const terminalTheme = computed(() => { + const themePref = terminalThemePreference.get(); + + if (themePref.matchLensTheme) { + return activeTheme.get(); + } + + return themes.get(themePref.themeId) ?? activeTheme.get(); + }); + + return computed(() => object.fromEntries( + object.entries(terminalTheme.get().colors) + .filter(([name]) => name.startsWith(terminalColorPrefix)) + .map(([name, color]) => [ + camelCase(name.replace(terminalColorPrefix, "")), + color, + ]), + )); + }, +}); + +export default xtermColorThemeInjectable; diff --git a/src/renderer/themes/themes.injectable.ts b/src/renderer/themes/themes.injectable.ts new file mode 100644 index 0000000000..008a744f49 --- /dev/null +++ b/src/renderer/themes/themes.injectable.ts @@ -0,0 +1,17 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { lensThemeDeclarationInjectionToken } from "./declaration"; + +const lensThemesInjectable = getInjectable({ + id: "lens-themes", + instantiate: (di) => { + const themes = di.injectMany(lensThemeDeclarationInjectionToken); + + return new Map(themes.map(theme => [theme.name, theme])); + }, +}); + +export default lensThemesInjectable;