Skip to content

Commit 7dfdb79

Browse files
authored
Theme presets (darkreader#3118)
- Ability to add up to 3 theme presets.
1 parent 5665327 commit 7dfdb79

16 files changed

Lines changed: 235 additions & 51 deletions

File tree

src/background/extension.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -372,42 +372,43 @@ export class Extension {
372372
const urlInfo = this.getURLInfo(url);
373373
if (this.isEnabled() && isURLEnabled(url, this.user.settings, urlInfo)) {
374374
const custom = this.user.settings.customThemes.find(({url: urlList}) => isURLInList(url, urlList));
375-
const filterConfig = custom ? custom.theme : this.user.settings.theme;
375+
const preset = custom ? null : this.user.settings.presets.find(({urls}) => isURLInList(url, urls));
376+
const theme = custom ? custom.theme : preset ? preset.theme : this.user.settings.theme;
376377

377378
console.log(`Creating CSS for url: ${url}`);
378-
switch (filterConfig.engine) {
379+
switch (theme.engine) {
379380
case ThemeEngines.cssFilter: {
380381
return {
381382
type: 'add-css-filter',
382-
data: createCSSFilterStylesheet(filterConfig, url, frameURL, this.config.INVERSION_FIXES),
383+
data: createCSSFilterStylesheet(theme, url, frameURL, this.config.INVERSION_FIXES),
383384
};
384385
}
385386
case ThemeEngines.svgFilter: {
386387
if (isFirefox()) {
387388
return {
388389
type: 'add-css-filter',
389-
data: createSVGFilterStylesheet(filterConfig, url, frameURL, this.config.INVERSION_FIXES),
390+
data: createSVGFilterStylesheet(theme, url, frameURL, this.config.INVERSION_FIXES),
390391
};
391392
}
392393
return {
393394
type: 'add-svg-filter',
394395
data: {
395-
css: createSVGFilterStylesheet(filterConfig, url, frameURL, this.config.INVERSION_FIXES),
396-
svgMatrix: getSVGFilterMatrixValue(filterConfig),
396+
css: createSVGFilterStylesheet(theme, url, frameURL, this.config.INVERSION_FIXES),
397+
svgMatrix: getSVGFilterMatrixValue(theme),
397398
svgReverseMatrix: getSVGReverseFilterMatrixValue(),
398399
},
399400
};
400401
}
401402
case ThemeEngines.staticTheme: {
402403
return {
403404
type: 'add-static-theme',
404-
data: filterConfig.stylesheet && filterConfig.stylesheet.trim() ?
405-
filterConfig.stylesheet :
406-
createStaticStylesheet(filterConfig, url, frameURL, this.config.STATIC_THEMES),
405+
data: theme.stylesheet && theme.stylesheet.trim() ?
406+
theme.stylesheet :
407+
createStaticStylesheet(theme, url, frameURL, this.config.STATIC_THEMES),
407408
};
408409
}
409410
case ThemeEngines.dynamicTheme: {
410-
const filter = {...filterConfig};
411+
const filter = {...theme};
411412
delete filter.engine;
412413
const fixes = getDynamicThemeFixesFor(url, frameURL, this.config.DYNAMIC_THEME_FIXES, this.user.settings.enableForPDF);
413414
const isIFrame = frameURL != null;
@@ -417,7 +418,7 @@ export class Extension {
417418
};
418419
}
419420
default: {
420-
throw new Error(`Unknown engine ${filterConfig.engine}`);
421+
throw new Error(`Unknown engine ${theme.engine}`);
421422
}
422423
}
423424
} else {

src/background/user-storage.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ export default class UserStorage {
4343
}
4444
sync.theme = {...DEFAULT_SETTINGS.theme, ...sync.theme};
4545
sync.time = {...DEFAULT_SETTINGS.time, ...sync.time};
46+
sync.presets.forEach((preset) => {
47+
preset.theme = {...DEFAULT_SETTINGS.theme, ...preset.theme};
48+
});
4649
sync.customThemes.forEach((site) => {
4750
site.theme = {...DEFAULT_SETTINGS.theme, ...site.theme};
4851
});

src/defaults.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export const DEFAULT_THEME: Theme = {
3535
export const DEFAULT_SETTINGS: UserSettings = {
3636
enabled: true,
3737
theme: DEFAULT_THEME,
38+
presets: [],
3839
customThemes: [],
3940
siteList: [],
4041
siteListEnabled: [],

src/definitions.d.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export interface ExtWrapper {
3636
actions: ExtensionActions;
3737
}
3838

39-
export interface FilterConfig {
39+
export interface Theme {
4040
mode: FilterMode;
4141
brightness: number;
4242
contrast: number;
@@ -55,16 +55,24 @@ export interface FilterConfig {
5555
selectionColor: '' | 'auto' | string;
5656
}
5757

58-
export type Theme = FilterConfig;
58+
export type FilterConfig = Theme;
5959

6060
export interface CustomSiteConfig {
6161
url: string[];
6262
theme: FilterConfig;
6363
}
6464

65+
export interface ThemePreset {
66+
id: string;
67+
name: string;
68+
urls: string[];
69+
theme: Theme;
70+
}
71+
6572
export interface UserSettings {
6673
enabled: boolean;
6774
theme: FilterConfig;
75+
presets: ThemePreset[];
6876
customThemes: CustomSiteConfig[];
6977
siteList: string[];
7078
siteListEnabled: string[];

src/inject/dynamic-theme/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ import {getCSSFilterValue} from '../../generators/css-filter';
1414
import {modifyColor} from '../../generators/modify-colors';
1515
import {createTextStyle} from '../../generators/text-style';
1616
import {FilterConfig, DynamicThemeFix} from '../../definitions';
17-
import {generateInstanceId} from './uuid';
17+
import {generateUID} from '../../utils/uid';
1818
import {createAdoptedStyleSheetOverride, AdoptedStyleSheetManager} from './adopted-style-manger';
1919

2020
const variables = new Map<string, string>();
21-
const INSTANCE_ID = generateInstanceId();
21+
const INSTANCE_ID = generateUID();
2222
const styleManagers = new Map<StyleElement, StyleManager>();
2323
const adoptedStyleManagers = [] as Array<AdoptedStyleSheetManager>;
2424
let filter: FilterConfig = null;

src/ui/controls/color-dropdown/index.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ export default function ColorDropDown(props: ColorDropDownProps) {
2929
};
3030

3131
const dropDownOptions = [
32-
props.hasDefaultOption ? {id: 'default', label: labels.DEFAULT} : null,
33-
props.hasAutoOption ? {id: 'auto', label: labels.AUTO} : null,
34-
{id: 'custom', label: labels.CUSTOM},
32+
props.hasDefaultOption ? {id: 'default', content: labels.DEFAULT} : null,
33+
props.hasAutoOption ? {id: 'auto', content: labels.AUTO} : null,
34+
{id: 'custom', content: labels.CUSTOM},
3535
].filter((v) => v);
3636

3737
const selectedDropDownValue = (

src/ui/controls/dropdown/index.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {m} from 'malevic';
22
import {getContext} from 'malevic/dom';
33

4-
type DropDownOption<T> = {id: T; label: string};
4+
type DropDownOption<T> = {id: T; content: Malevic.Child};
55

66
interface DropDownProps<T> {
77
class?: string;
@@ -18,6 +18,14 @@ export default function DropDown<T>(props: DropDownProps<T>) {
1818
selectedNode: HTMLElement;
1919
};
2020

21+
if (context.prev) {
22+
const currOptions = props.options.map((o) => o.id);
23+
const prevOptions = (context.prev.props.options as DropDownOption<T>[]).map((o) => o.id);
24+
if (currOptions.length !== prevOptions.length || currOptions.some((o, i) => o !== prevOptions[i])) {
25+
store.isOpen = false;
26+
}
27+
}
28+
2129
function saveListNode(el: HTMLElement) {
2230
store.listNode = el;
2331
}
@@ -65,12 +73,12 @@ export default function DropDown<T>(props: DropDownProps<T>) {
6573
props.onChange(value.id);
6674
}}
6775
>
68-
{value.label}
76+
{value.content}
6977
</span>
7078
);
7179
}
7280

73-
const selectedLabel = props.options.find((value) => value.id === props.selected).label;
81+
const selectedContent = props.options.find((value) => value.id === props.selected).content;
7482

7583
return (
7684
<span
@@ -95,7 +103,7 @@ export default function DropDown<T>(props: DropDownProps<T>) {
95103
onclick={onSelectedClick}
96104
>
97105
<span class="dropdown__selected__text">
98-
{selectedLabel}
106+
{selectedContent}
99107
</span>
100108
</span>
101109
</span >

src/ui/controls/dropdown/style.less

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,21 @@
1111
width: 100%;
1212

1313
&__selected {
14+
align-items: center;
1415
background-color: @color-control-active;
1516
background-image: @icon-down;
1617
background-position: center right 0.25rem;
1718
background-repeat: no-repeat;
1819
background-size: 1rem;
1920
border-radius: @dropdown-item-height / 2;
2021
color: @color-control-fore;
21-
display: inline-block;
22+
display: inline-flex;
2223
height: @dropdown-item-height;
24+
justify-content: center;
2325
line-height: @dropdown-item-height;
2426
margin-top: (@dropdown-height - @dropdown-item-height) / 2;
25-
text-align: center;
2627
position: absolute;
28+
text-align: center;
2729
width: 100%;
2830

2931
&__text {
@@ -36,7 +38,7 @@
3638
}
3739

3840
&__list {
39-
background-color: @color-control-hover;
41+
background-color: @color-control-back;
4042
border-radius: @dropdown-item-height / 2;
4143
box-shadow: 0 0 0.25rem black;
4244
display: none;
@@ -45,10 +47,12 @@
4547
width: 100%;
4648

4749
&__item {
50+
align-items: center;
51+
background-color: @color-control-back;
4852
color: @color-control-fore;
49-
display: inline-block;
50-
height: @dropdown-item-height;
51-
line-height: @dropdown-item-height;
53+
display: inline-flex;
54+
justify-content: center;
55+
min-height: @dropdown-item-height;
5256
text-align: center;
5357
width: 100%;
5458

@@ -72,15 +76,13 @@
7276
z-index: 1;
7377

7478
&__item {
75-
height: @dropdown-item-active-height;
76-
line-height: @dropdown-item-active-height;
79+
min-height: @dropdown-item-active-height;
7780
}
7881
}
7982

8083
&--open &__selected {
8184
border-radius: @dropdown-item-active-height / 2;
8285
height: @dropdown-item-active-height;
83-
line-height: @dropdown-item-active-height;
8486
margin-top: (@dropdown-height - @dropdown-item-active-height) / 2;
8587
z-index: 2;
8688
}

src/ui/controls/overlay/style.less

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
position: fixed;
1212
top: 0;
1313
width: 100%;
14+
z-index: 2;
1415

1516
&:empty {
1617
display: none;

src/ui/popup/theme/controls/mode.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ export default function Mode(props: {mode: string; onChange: (mode: string) => v
1717
}
1818

1919
const modes = [
20-
{id: ThemeEngines.dynamicTheme, label: getLocalMessage('engine_dynamic')},
21-
{id: ThemeEngines.cssFilter, label: getLocalMessage('engine_filter')},
22-
{id: ThemeEngines.svgFilter, label: getLocalMessage('engine_filter_plus')},
23-
{id: ThemeEngines.staticTheme, label: getLocalMessage('engine_static')},
20+
{id: ThemeEngines.dynamicTheme, content: getLocalMessage('engine_dynamic')},
21+
{id: ThemeEngines.cssFilter, content: getLocalMessage('engine_filter')},
22+
{id: ThemeEngines.svgFilter, content: getLocalMessage('engine_filter_plus')},
23+
{id: ThemeEngines.staticTheme, content: getLocalMessage('engine_static')},
2424
];
2525
return (
2626
<ThemeControl label="Mode">

0 commit comments

Comments
 (0)