mirror of https://github.com/vector-im/riot-web
Merge pull request #3503 from matrix-org/bwindels/custom-themes-mvp
Custom theming MVPpull/21833/head
commit
c8c4dc29d1
|
@ -15,7 +15,7 @@ order of prioirty, are:
|
|||
* `room-account` - The current user's account, but only when in a specific room
|
||||
* `account` - The current user's account
|
||||
* `room` - A specific room (setting for all members of the room)
|
||||
* `config` - Values are defined by `config.json`
|
||||
* `config` - Values are defined by the `settingDefaults` key (usually) in `config.json`
|
||||
* `default` - The hardcoded default for the settings
|
||||
|
||||
Individual settings may control which levels are appropriate for them as part of the defaults. This is often to ensure
|
||||
|
|
|
@ -70,10 +70,10 @@ limitations under the License.
|
|||
.mx_RoomSubList_badge {
|
||||
flex: 0 0 auto;
|
||||
border-radius: 8px;
|
||||
color: $accent-fg-color;
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
padding: 0 5px;
|
||||
color: $roomtile-badge-fg-color;
|
||||
background-color: $roomtile-name-color;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -104,6 +104,7 @@ limitations under the License.
|
|||
}
|
||||
|
||||
.mx_RoomSubList_badgeHighlight {
|
||||
color: $accent-fg-color;
|
||||
background-color: $warning-color;
|
||||
}
|
||||
|
||||
|
|
|
@ -110,7 +110,7 @@ limitations under the License.
|
|||
flex: 0 1 content;
|
||||
border-radius: 0.8em;
|
||||
padding: 0 0.4em;
|
||||
color: $accent-fg-color;
|
||||
color: $roomtile-badge-fg-color;
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
@ -156,6 +156,7 @@ limitations under the License.
|
|||
|
||||
.mx_RoomTile_highlight .mx_RoomTile_badge,
|
||||
.mx_RoomTile_badge.mx_RoomTile_badgeRed {
|
||||
color: $accent-fg-color;
|
||||
background-color: $warning-color;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
@import "../../light/css/_paths.scss";
|
||||
@import "../../light/css/_fonts.scss";
|
||||
@import "../../light/css/_light.scss";
|
||||
@import "../../dark/css/_dark.scss";
|
||||
@import "../../light-custom/css/_custom.scss";
|
||||
@import "../../../../res/css/_components.scss";
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
//
|
||||
// --accent-color
|
||||
$accent-color: var(--accent-color);
|
||||
$button-bg-color: var(--accent-color);
|
||||
$button-link-fg-color: var(--accent-color);
|
||||
$button-primary-bg-color: var(--accent-color);
|
||||
$input-valid-border-color: var(--accent-color);
|
||||
$reaction-row-button-selected-border-color: var(--accent-color);
|
||||
$roomsublist-chevron-color: var(--accent-color);
|
||||
$tab-label-active-bg-color: var(--accent-color);
|
||||
$togglesw-on-color: var(--accent-color);
|
||||
$username-variant3-color: var(--accent-color);
|
||||
$accent-color-50pct: var(--accent-color-50pct); //still needs alpha at .5
|
||||
//
|
||||
// --timeline-background-color
|
||||
$authpage-body-bg-color: var(--timeline-background-color);
|
||||
$button-secondary-bg-color: var(--timeline-background-color);
|
||||
$field-focused-label-bg-color: var(--timeline-background-color);
|
||||
$lightbox-border-color: var(--timeline-background-color);
|
||||
$menu-bg-color: var(--timeline-background-color);
|
||||
$avatar-bg-color: var(--timeline-background-color);
|
||||
$message-action-bar-bg-color: var(--timeline-background-color);
|
||||
$primary-bg-color: var(--timeline-background-color);
|
||||
$roomtile-focused-bg-color: var(--timeline-background-color);
|
||||
$togglesw-ball-color: var(--timeline-background-color);
|
||||
$droptarget-bg-color: var(--timeline-background-color-50pct); //still needs alpha at .5
|
||||
$authpage-modal-bg-color: var(--timeline-background-color-50pct); //still needs alpha at .59
|
||||
//
|
||||
// --roomlist-highlights-color
|
||||
$roomtile-selected-bg-color: var(--roomlist-highlights-color);
|
||||
//
|
||||
// --sidebar-color
|
||||
$interactive-tooltip-bg-color: var(--sidebar-color);
|
||||
$tagpanel-bg-color: var(--sidebar-color);
|
||||
$tooltip-timeline-bg-color: var(--sidebar-color);
|
||||
$dialog-backdrop-color: var(--sidebar-color-50pct);
|
||||
//
|
||||
// --roomlist-background-color
|
||||
$event-selected-color: var(--roomlist-background-color);
|
||||
$header-panel-bg-color: var(--roomlist-background-color);
|
||||
$reaction-row-button-bg-color: var(--roomlist-background-color);
|
||||
$panel-gradient: var(--roomlist-background-color-0pct), var(--roomlist-background-color);
|
||||
// these were #f2f5f8 instead of #f3f8fd, but close enough
|
||||
$dark-panel-bg-color: var(--roomlist-background-color);
|
||||
$input-lighter-bg-color: var(--roomlist-background-color);
|
||||
$plinth-bg-color: var(--roomlist-background-color);
|
||||
$roomsublist-background: var(--roomlist-background-color);
|
||||
$secondary-accent-color: var(--roomlist-background-color);
|
||||
$selected-color: var(--roomlist-background-color);
|
||||
$widget-menu-bar-bg-color: var(--roomlist-background-color);
|
||||
$roomtile-badge-fg-color: var(--roomlist-background-color);
|
||||
//
|
||||
// --timeline-text-color
|
||||
$message-action-bar-fg-color: var(--timeline-text-color);
|
||||
$primary-fg-color: var(--timeline-text-color);
|
||||
$settings-profile-overlay-placeholder-fg-color: var(--timeline-text-color);
|
||||
$roomtopic-color: var(--timeline-text-color-50pct);
|
||||
$tab-label-fg-color: var(--timeline-text-color);
|
||||
$tab-label-icon-bg-color: var(--timeline-text-color); //was #454545
|
||||
// was #212121
|
||||
$topleftmenu-color: var(--timeline-text-color);
|
||||
// was #45474a
|
||||
$dialog-title-fg-color: var(--timeline-text-color);
|
||||
$tab-label-fg-color: var(--timeline-text-color);
|
||||
// was #4e5054
|
||||
$authpage-lang-color: var(--timeline-text-color);
|
||||
$roomheader-color: var(--timeline-text-color);
|
||||
//
|
||||
// --roomlist-text-color
|
||||
$roomtile-notified-color: var(--roomlist-text-color);
|
||||
$roomtile-selected-color: var(--roomlist-text-color);
|
||||
//
|
||||
// --roomlist-text-secondary-color
|
||||
$roomsublist-label-fg-color: var(--roomlist-text-secondary-color);
|
||||
$roomtile-name-color: var(--roomlist-text-secondary-color);
|
||||
//
|
||||
// --roomlist-separator-color
|
||||
$input-darker-bg-color: var(--roomlist-separator-color);
|
||||
$panel-divider-color: var(--roomlist-separator-color);// originally #dee1f3, but close enough
|
||||
$primary-hairline-color: var(--roomlist-separator-color);// originally #e5e5e5, but close enough
|
||||
//
|
||||
// --timeline-text-secondary-color
|
||||
$authpage-secondary-color: var(--timeline-text-secondary-color);
|
||||
$memberstatus-placeholder-color: var(--timeline-text-secondary-color);
|
||||
$notice-secondary-color: var(--timeline-text-secondary-color);
|
||||
$pinned-color: var(--timeline-text-secondary-color);
|
||||
$settings-subsection-fg-color: var(--timeline-text-secondary-color);
|
||||
$roomheader-addroom-bg-color: var(--timeline-text-secondary-color);
|
||||
// was #747474
|
||||
$light-fg-color: var(--timeline-text-secondary-color);
|
||||
// was #777777
|
||||
$blockquote-fg-color: var(--timeline-text-secondary-color);
|
||||
// was #888888
|
||||
$greyed-fg-color: var(--timeline-text-secondary-color);
|
||||
$info-plinth-fg-color: var(--timeline-text-secondary-color);
|
||||
$preview-widget-fg-color: var(--timeline-text-secondary-color);
|
||||
//
|
||||
// --primary-color
|
||||
$accent-color-alt: var(--primary-color);
|
||||
$input-focused-border-color: var(--primary-color);
|
||||
//
|
||||
// --warning-color
|
||||
$button-danger-bg-color: var(--warning-color);
|
||||
$event-highlight-fg-color: var(--warning-color);
|
||||
$input-invalid-border-color: var(--warning-color);
|
||||
$mention-user-pill-bg-color: var(--warning-color);
|
||||
$notice-primary-color: var(--warning-color);
|
||||
$pinned-unread-color: var(--warning-color);
|
||||
$warning-color: var(--warning-color);
|
||||
$button-danger-disabled-bg-color: var(--warning-color-50pct); // still needs alpha at 0.5
|
|
@ -0,0 +1,5 @@
|
|||
@import "../../light/css/_paths.scss";
|
||||
@import "../../light/css/_fonts.scss";
|
||||
@import "../../light/css/_light.scss";
|
||||
@import "_custom.scss";
|
||||
@import "../../../../res/css/_components.scss";
|
|
@ -167,6 +167,7 @@ $header-divider-color: #91A1C0;
|
|||
// ********************
|
||||
|
||||
$roomtile-name-color: #61708b;
|
||||
$roomtile-badge-fg-color: $accent-fg-color;
|
||||
$roomtile-selected-color: #212121;
|
||||
$roomtile-notified-color: #212121;
|
||||
$roomtile-selected-bg-color: #fff;
|
||||
|
@ -234,7 +235,7 @@ $tab-label-active-fg-color: #ffffff;
|
|||
$tab-label-bg-color: transparent;
|
||||
$tab-label-active-bg-color: $accent-color;
|
||||
$tab-label-icon-bg-color: #454545;
|
||||
$tab-label-active-icon-bg-color: #ffffff;
|
||||
$tab-label-active-icon-bg-color: $tab-label-active-fg-color;
|
||||
|
||||
// Buttons
|
||||
$button-primary-fg-color: #ffffff;
|
||||
|
|
|
@ -59,6 +59,7 @@ import { ValidatedServerConfig } from "../../utils/AutoDiscoveryUtils";
|
|||
import AutoDiscoveryUtils from "../../utils/AutoDiscoveryUtils";
|
||||
import DMRoomMap from '../../utils/DMRoomMap';
|
||||
import { countRoomsWithNotif } from '../../RoomNotifs';
|
||||
import { setTheme } from "../../theme";
|
||||
|
||||
// Disable warnings for now: we use deprecated bluebird functions
|
||||
// and need to migrate, but they spam the console with warnings.
|
||||
|
@ -661,7 +662,7 @@ export default createReactClass({
|
|||
break;
|
||||
}
|
||||
case 'set_theme':
|
||||
this._onSetTheme(payload.value);
|
||||
setTheme(payload.value);
|
||||
break;
|
||||
case 'on_logging_in':
|
||||
// We are now logging in, so set the state to reflect that
|
||||
|
@ -1105,82 +1106,6 @@ export default createReactClass({
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Called whenever someone changes the theme
|
||||
*
|
||||
* @param {string} theme new theme
|
||||
*/
|
||||
_onSetTheme: function(theme) {
|
||||
if (!theme) {
|
||||
theme = SettingsStore.getValue("theme");
|
||||
}
|
||||
|
||||
// look for the stylesheet elements.
|
||||
// styleElements is a map from style name to HTMLLinkElement.
|
||||
const styleElements = Object.create(null);
|
||||
let a;
|
||||
for (let i = 0; (a = document.getElementsByTagName("link")[i]); i++) {
|
||||
const href = a.getAttribute("href");
|
||||
// shouldn't we be using the 'title' tag rather than the href?
|
||||
const match = href.match(/^bundles\/.*\/theme-(.*)\.css$/);
|
||||
if (match) {
|
||||
styleElements[match[1]] = a;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(theme in styleElements)) {
|
||||
throw new Error("Unknown theme " + theme);
|
||||
}
|
||||
|
||||
// disable all of them first, then enable the one we want. Chrome only
|
||||
// bothers to do an update on a true->false transition, so this ensures
|
||||
// that we get exactly one update, at the right time.
|
||||
//
|
||||
// ^ This comment was true when we used to use alternative stylesheets
|
||||
// for the CSS. Nowadays we just set them all as disabled in index.html
|
||||
// and enable them as needed. It might be cleaner to disable them all
|
||||
// at the same time to prevent loading two themes simultaneously and
|
||||
// having them interact badly... but this causes a flash of unstyled app
|
||||
// which is even uglier. So we don't.
|
||||
|
||||
styleElements[theme].disabled = false;
|
||||
|
||||
const switchTheme = function() {
|
||||
// we re-enable our theme here just in case we raced with another
|
||||
// theme set request as per https://github.com/vector-im/riot-web/issues/5601.
|
||||
// We could alternatively lock or similar to stop the race, but
|
||||
// this is probably good enough for now.
|
||||
styleElements[theme].disabled = false;
|
||||
Object.values(styleElements).forEach((a) => {
|
||||
if (a == styleElements[theme]) return;
|
||||
a.disabled = true;
|
||||
});
|
||||
Tinter.setTheme(theme);
|
||||
};
|
||||
|
||||
// turns out that Firefox preloads the CSS for link elements with
|
||||
// the disabled attribute, but Chrome doesn't.
|
||||
|
||||
let cssLoaded = false;
|
||||
|
||||
styleElements[theme].onload = () => {
|
||||
switchTheme();
|
||||
};
|
||||
|
||||
for (let i = 0; i < document.styleSheets.length; i++) {
|
||||
const ss = document.styleSheets[i];
|
||||
if (ss && ss.href === styleElements[theme].href) {
|
||||
cssLoaded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cssLoaded) {
|
||||
styleElements[theme].onload = undefined;
|
||||
switchTheme();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts a chat with the welcome user, if the user doesn't already have one
|
||||
* @returns {string} The room ID of the new room, or null if no room was created
|
||||
|
|
|
@ -27,7 +27,7 @@ import LanguageDropdown from "../../../elements/LanguageDropdown";
|
|||
import AccessibleButton from "../../../elements/AccessibleButton";
|
||||
import DeactivateAccountDialog from "../../../dialogs/DeactivateAccountDialog";
|
||||
import PropTypes from "prop-types";
|
||||
import {THEMES} from "../../../../../themes";
|
||||
import {enumerateThemes} from "../../../../../theme";
|
||||
import PlatformPeg from "../../../../../PlatformPeg";
|
||||
import MatrixClientPeg from "../../../../../MatrixClientPeg";
|
||||
import sdk from "../../../../..";
|
||||
|
@ -275,8 +275,8 @@ export default class GeneralUserSettingsTab extends React.Component {
|
|||
<span className="mx_SettingsTab_subheading">{_t("Theme")}</span>
|
||||
<Field id="theme" label={_t("Theme")} element="select"
|
||||
value={this.state.theme} onChange={this._onThemeChange}>
|
||||
{Object.entries(THEMES).map(([theme, text]) => {
|
||||
return <option key={theme} value={theme}>{_t(text)}</option>;
|
||||
{Object.entries(enumerateThemes()).map(([theme, text]) => {
|
||||
return <option key={theme} value={theme}>{text}</option>;
|
||||
})}
|
||||
</Field>
|
||||
<SettingsFlag name="useCompactLayout" level={SettingLevel.ACCOUNT} />
|
||||
|
|
|
@ -245,6 +245,10 @@ export const SETTINGS = {
|
|||
default: "light",
|
||||
controller: new ThemeController(),
|
||||
},
|
||||
"custom_themes": {
|
||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||
default: [],
|
||||
},
|
||||
"webRtcAllowPeerToPeer": {
|
||||
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
|
||||
displayName: _td('Allow Peer-to-Peer for 1:1 calls'),
|
||||
|
|
|
@ -16,12 +16,13 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import SettingController from "./SettingController";
|
||||
import {DEFAULT_THEME, THEMES} from "../../themes";
|
||||
import {DEFAULT_THEME, enumerateThemes} from "../../theme";
|
||||
|
||||
export default class ThemeController extends SettingController {
|
||||
getValueOverride(level, roomId, calculatedValue, calculatedAtLevel) {
|
||||
const themes = enumerateThemes();
|
||||
// Override in case some no longer supported theme is stored here
|
||||
if (!THEMES[calculatedValue]) {
|
||||
if (!themes[calculatedValue]) {
|
||||
return DEFAULT_THEME;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {_t} from "./languageHandler";
|
||||
|
||||
export const DEFAULT_THEME = "light";
|
||||
import Tinter from "./Tinter";
|
||||
import SettingsStore from "./settings/SettingsStore";
|
||||
|
||||
export function enumerateThemes() {
|
||||
const BUILTIN_THEMES = {
|
||||
"light": _t("Light theme"),
|
||||
"dark": _t("Dark theme"),
|
||||
};
|
||||
const customThemes = SettingsStore.getValue("custom_themes");
|
||||
const customThemeNames = {};
|
||||
for (const {name} of customThemes) {
|
||||
customThemeNames[`custom-${name}`] = name;
|
||||
}
|
||||
return Object.assign({}, customThemeNames, BUILTIN_THEMES);
|
||||
}
|
||||
|
||||
function setCustomThemeVars(customTheme) {
|
||||
const {style} = document.body;
|
||||
if (customTheme.colors) {
|
||||
for (const [name, hexColor] of Object.entries(customTheme.colors)) {
|
||||
style.setProperty(`--${name}`, hexColor);
|
||||
// uses #rrggbbaa to define the color with alpha values at 0% and 50%
|
||||
style.setProperty(`--${name}-0pct`, hexColor + "00");
|
||||
style.setProperty(`--${name}-50pct`, hexColor + "7F");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getCustomTheme(themeName) {
|
||||
// set css variables
|
||||
const customThemes = SettingsStore.getValue("custom_themes");
|
||||
if (!customThemes) {
|
||||
throw new Error(`No custom themes set, can't set custom theme "${themeName}"`);
|
||||
}
|
||||
const customTheme = customThemes.find(t => t.name === themeName);
|
||||
if (!customTheme) {
|
||||
const knownNames = customThemes.map(t => t.name).join(", ");
|
||||
throw new Error(`Can't find custom theme "${themeName}", only know ${knownNames}`);
|
||||
}
|
||||
return customTheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever someone changes the theme
|
||||
*
|
||||
* @param {string} theme new theme
|
||||
*/
|
||||
export function setTheme(theme) {
|
||||
if (!theme) {
|
||||
theme = SettingsStore.getValue("theme");
|
||||
}
|
||||
let stylesheetName = theme;
|
||||
if (theme.startsWith("custom-")) {
|
||||
const customTheme = getCustomTheme(theme.substr(7));
|
||||
stylesheetName = customTheme.is_dark ? "dark-custom" : "light-custom";
|
||||
setCustomThemeVars(customTheme);
|
||||
}
|
||||
|
||||
// look for the stylesheet elements.
|
||||
// styleElements is a map from style name to HTMLLinkElement.
|
||||
const styleElements = Object.create(null);
|
||||
let a;
|
||||
for (let i = 0; (a = document.getElementsByTagName("link")[i]); i++) {
|
||||
const href = a.getAttribute("href");
|
||||
// shouldn't we be using the 'title' tag rather than the href?
|
||||
const match = href.match(/^bundles\/.*\/theme-(.*)\.css$/);
|
||||
if (match) {
|
||||
styleElements[match[1]] = a;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(stylesheetName in styleElements)) {
|
||||
throw new Error("Unknown theme " + stylesheetName);
|
||||
}
|
||||
|
||||
// disable all of them first, then enable the one we want. Chrome only
|
||||
// bothers to do an update on a true->false transition, so this ensures
|
||||
// that we get exactly one update, at the right time.
|
||||
//
|
||||
// ^ This comment was true when we used to use alternative stylesheets
|
||||
// for the CSS. Nowadays we just set them all as disabled in index.html
|
||||
// and enable them as needed. It might be cleaner to disable them all
|
||||
// at the same time to prevent loading two themes simultaneously and
|
||||
// having them interact badly... but this causes a flash of unstyled app
|
||||
// which is even uglier. So we don't.
|
||||
|
||||
styleElements[stylesheetName].disabled = false;
|
||||
|
||||
const switchTheme = function() {
|
||||
// we re-enable our theme here just in case we raced with another
|
||||
// theme set request as per https://github.com/vector-im/riot-web/issues/5601.
|
||||
// We could alternatively lock or similar to stop the race, but
|
||||
// this is probably good enough for now.
|
||||
styleElements[stylesheetName].disabled = false;
|
||||
Object.values(styleElements).forEach((a) => {
|
||||
if (a == styleElements[stylesheetName]) return;
|
||||
a.disabled = true;
|
||||
});
|
||||
Tinter.setTheme(theme);
|
||||
};
|
||||
|
||||
// turns out that Firefox preloads the CSS for link elements with
|
||||
// the disabled attribute, but Chrome doesn't.
|
||||
|
||||
let cssLoaded = false;
|
||||
|
||||
styleElements[stylesheetName].onload = () => {
|
||||
switchTheme();
|
||||
};
|
||||
|
||||
for (let i = 0; i < document.styleSheets.length; i++) {
|
||||
const ss = document.styleSheets[i];
|
||||
if (ss && ss.href === styleElements[stylesheetName].href) {
|
||||
cssLoaded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cssLoaded) {
|
||||
styleElements[stylesheetName].onload = undefined;
|
||||
switchTheme();
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {_td} from "./languageHandler";
|
||||
|
||||
export const DEFAULT_THEME = "light";
|
||||
|
||||
export const THEMES = {
|
||||
"light": _td("Light theme"),
|
||||
"dark": _td("Dark theme"),
|
||||
};
|
Loading…
Reference in New Issue