Skip to content

Commit c3b811f

Browse files
committed
Add Custom Theme and Color Customization Feature
1 parent 3719622 commit c3b811f

23 files changed

+1634
-8
lines changed

packages/core/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
"@freelensapp/button": "workspace:^",
7979
"@freelensapp/cluster-settings": "workspace:^",
8080
"@freelensapp/cluster-sidebar": "workspace:^",
81+
"@freelensapp/color-picker": "workspace:^",
8182
"@freelensapp/error-boundary": "workspace:^",
8283
"@freelensapp/event-emitter": "workspace:^",
8384
"@freelensapp/feature-core": "workspace:^",

packages/core/src/common/vars.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// App's common configuration for any process (main, renderer, build pipeline, etc.)
88
import type { ThemeId } from "../renderer/themes/lens-theme";
99

10-
export const defaultThemeId: ThemeId = "lens-dark";
10+
export const defaultThemeId: ThemeId = "Dark";
1111
export const defaultFontSize = 12;
1212
export const defaultTerminalFontFamily = "RobotoMono";
1313
export const defaultEditorFontFamily = "RobotoMono";

packages/core/src/extensions/renderer-api/theming.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import { asLegacyGlobalForExtensionApi } from "@freelensapp/legacy-global-di";
88
import activeThemeInjectable from "../../renderer/themes/active.injectable";
9+
import customThemesStorageInjectable from "../../renderer/themes/custom-themes-storage.injectable";
910

1011
import type { LensTheme } from "../../renderer/themes/lens-theme";
1112

@@ -18,4 +19,56 @@ export function getActiveTheme() {
1819
return activeTheme.get();
1920
}
2021

22+
const customThemesStorage = asLegacyGlobalForExtensionApi(customThemesStorageInjectable);
23+
24+
/**
25+
* Register a custom theme that extensions can use to provide additional themes.
26+
* @param theme The theme to register
27+
* @returns true if the theme was successfully registered, false if a theme with that name already exists
28+
*/
29+
export function registerTheme(theme: LensTheme): boolean {
30+
try {
31+
const existingTheme = customThemesStorage.getTheme(theme.name);
32+
33+
if (existingTheme) {
34+
return false;
35+
}
36+
37+
customThemesStorage.addTheme({ ...theme, isCustom: true });
38+
return true;
39+
} catch (error) {
40+
console.error("Failed to register theme:", error);
41+
return false;
42+
}
43+
}
44+
45+
/**
46+
* Unregister a custom theme by name.
47+
* @param themeName The name of the theme to unregister
48+
* @returns true if the theme was successfully unregistered, false otherwise
49+
*/
50+
export function unregisterTheme(themeName: string): boolean {
51+
try {
52+
const theme = customThemesStorage.getTheme(themeName);
53+
54+
if (!theme || !theme.isCustom) {
55+
return false;
56+
}
57+
58+
customThemesStorage.removeTheme(themeName);
59+
return true;
60+
} catch (error) {
61+
console.error("Failed to unregister theme:", error);
62+
return false;
63+
}
64+
}
65+
66+
/**
67+
* Get all registered themes (built-in and custom).
68+
* @returns Array of all themes
69+
*/
70+
export function getThemes(): LensTheme[] {
71+
return customThemesStorage.themes;
72+
}
73+
2174
export type { LensTheme };
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/**
2+
* Copyright (c) Freelens Authors. All rights reserved.
3+
* Copyright (c) OpenLens Authors. All rights reserved.
4+
* Licensed under MIT License. See LICENSE in root directory for more information.
5+
*/
6+
7+
.editor {
8+
display: flex;
9+
flex-direction: column;
10+
background: var(--contentColor);
11+
border-radius: 8px;
12+
overflow: hidden;
13+
max-width: 900px;
14+
margin: 20px auto;
15+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
16+
}
17+
18+
.header {
19+
padding: 20px 24px;
20+
background: var(--layoutBackground);
21+
border-bottom: 1px solid var(--borderColor);
22+
23+
h3 {
24+
margin: 0;
25+
font-size: 18px;
26+
color: var(--textColorAccent);
27+
}
28+
}
29+
30+
.content {
31+
padding: 24px;
32+
max-height: 600px;
33+
overflow-y: auto;
34+
}
35+
36+
.basicInfo {
37+
margin-bottom: 32px;
38+
padding-bottom: 24px;
39+
border-bottom: 1px solid var(--borderFaintColor);
40+
}
41+
42+
.field {
43+
margin-bottom: 16px;
44+
45+
label {
46+
display: block;
47+
margin-bottom: 8px;
48+
font-size: 13px;
49+
font-weight: 500;
50+
color: var(--textColorPrimary);
51+
}
52+
}
53+
54+
.input,
55+
.textarea,
56+
.select {
57+
width: 100%;
58+
padding: 8px 12px;
59+
border: 1px solid var(--borderColor);
60+
border-radius: 4px;
61+
background: var(--inputControlBackground);
62+
color: var(--textColorPrimary);
63+
font-size: 13px;
64+
font-family: inherit;
65+
66+
&:focus {
67+
outline: none;
68+
border-color: var(--primary);
69+
}
70+
}
71+
72+
.textarea {
73+
resize: vertical;
74+
font-family: inherit;
75+
}
76+
77+
.colorEditor {
78+
display: flex;
79+
flex-direction: column;
80+
gap: 20px;
81+
}
82+
83+
.categoryTabs {
84+
display: flex;
85+
gap: 8px;
86+
flex-wrap: wrap;
87+
margin-bottom: 16px;
88+
}
89+
90+
.categoryTab {
91+
padding: 8px 16px;
92+
border: 1px solid var(--borderColor);
93+
border-radius: 4px;
94+
background: var(--mainBackground);
95+
color: var(--textColorPrimary);
96+
font-size: 12px;
97+
cursor: pointer;
98+
transition: all 0.2s ease;
99+
100+
&:hover {
101+
background: var(--sidebarItemHoverBackground);
102+
}
103+
104+
&.active {
105+
background: var(--primary);
106+
color: white;
107+
border-color: var(--primary);
108+
}
109+
}
110+
111+
.colorGrid {
112+
display: grid;
113+
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
114+
gap: 16px;
115+
}
116+
117+
.footer {
118+
display: flex;
119+
justify-content: flex-end;
120+
gap: 12px;
121+
padding: 16px 24px;
122+
background: var(--layoutBackground);
123+
border-top: 1px solid var(--borderColor);
124+
}
125+
126+
.cancelButton,
127+
.saveButton {
128+
padding: 10px 20px;
129+
border: none;
130+
border-radius: 4px;
131+
font-size: 13px;
132+
font-weight: 500;
133+
cursor: pointer;
134+
transition: all 0.2s ease;
135+
}
136+
137+
.cancelButton {
138+
background: var(--buttonDefaultBackground);
139+
color: var(--textColorPrimary);
140+
141+
&:hover {
142+
opacity: 0.8;
143+
}
144+
}
145+
146+
.saveButton {
147+
background: var(--buttonPrimaryBackground);
148+
color: white;
149+
150+
&:hover {
151+
opacity: 0.9;
152+
}
153+
}

0 commit comments

Comments
 (0)