mirror of
https://github.com/muerwre/markdown-home-tab.git
synced 2025-04-24 16:36:41 +07:00
added themes
This commit is contained in:
parent
51576b0309
commit
44d903b6e2
17 changed files with 368 additions and 31 deletions
|
@ -9,5 +9,12 @@
|
|||
"Colors": "Colors",
|
||||
"Inline code": "Inline code",
|
||||
"Ok": "Ok",
|
||||
"Settings": "Settings"
|
||||
"Settings": "Settings",
|
||||
"Color theme": "Color theme",
|
||||
"Custom theme": "Custom theme",
|
||||
"Dark theme": "Dark theme",
|
||||
"Light theme": "Light theme",
|
||||
"Blueberry boar": "Blueberry boar",
|
||||
"User defined theme": "User defined",
|
||||
"Built-in theme": "Built-in"
|
||||
}
|
||||
|
|
|
@ -9,5 +9,12 @@
|
|||
"Colors": "Цвета",
|
||||
"Inline code": "Код в строке",
|
||||
"Ok": "Ok",
|
||||
"Settings": "Настройки"
|
||||
"Settings": "Настройки",
|
||||
"Color theme": "Цветовая схема",
|
||||
"Custom theme": "Своя тема",
|
||||
"Dark theme": "Тёмная",
|
||||
"Light theme": "Светлая",
|
||||
"Blueberry boar": "Черничный кабанчик",
|
||||
"User defined theme": "Пользовательская",
|
||||
"Built-in theme": "Встроенная"
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"name": "Markdown Home Tab",
|
||||
"short_name": "Markdown New Tab",
|
||||
"version": "0.0.1",
|
||||
"description": "Makdown editor for your new tab",
|
||||
"version": "0.0.2",
|
||||
"description": "Markdown right in your home tab! Paste links, pictures, lists and more. You can also customize colors to match your needs.",
|
||||
"manifest_version": 2,
|
||||
"permissions": ["storage"],
|
||||
"chrome_url_overrides": {
|
||||
|
|
|
@ -3,11 +3,20 @@ import styles from "./styles.module.scss";
|
|||
|
||||
type SettingsRowProps = PropsWithChildren & {
|
||||
title: string;
|
||||
subTitle?: string;
|
||||
};
|
||||
|
||||
const SettingsRow: FC<SettingsRowProps> = ({ title, children }) => (
|
||||
const SettingsRow: FC<SettingsRowProps> = ({ title, children, subTitle }) => (
|
||||
<div className={styles.row}>
|
||||
<div className={styles.legend}>
|
||||
<div className={styles.title}>{title}</div>
|
||||
{!!subTitle && (
|
||||
<div
|
||||
className={styles.subTitle}
|
||||
dangerouslySetInnerHTML={{ __html: subTitle }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.item}>{children}</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -6,6 +6,18 @@
|
|||
gap: 20px;
|
||||
}
|
||||
|
||||
.title {
|
||||
.legend {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.subTitle {
|
||||
font-size: 0.8em;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
|
|
@ -13,5 +13,5 @@
|
|||
|
||||
.title {
|
||||
flex: 1;
|
||||
font-size: 1.4em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
|
46
src/modules/settings/components/ThemeSelect/index.tsx
Normal file
46
src/modules/settings/components/ThemeSelect/index.tsx
Normal file
|
@ -0,0 +1,46 @@
|
|||
import { ChangeEvent, FC, useCallback } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { CustomTheme } from "~/modules/theme/types/theme";
|
||||
import { ColorSettings } from "../../context/SettingsContext";
|
||||
|
||||
interface ThemeSelectProps {
|
||||
themes: CustomTheme[];
|
||||
value?: CustomTheme;
|
||||
onChange: (val: ColorSettings) => void;
|
||||
}
|
||||
|
||||
const ThemeSelect: FC<ThemeSelectProps> = ({ themes, value, onChange }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const onChangeTrigger = useCallback(
|
||||
(event: ChangeEvent<HTMLSelectElement>) => {
|
||||
const themeTitle = event.target.value;
|
||||
|
||||
if (!themeTitle) {
|
||||
return;
|
||||
}
|
||||
|
||||
const theme = themes.find((it) => it.title === themeTitle);
|
||||
|
||||
if (!theme) {
|
||||
return;
|
||||
}
|
||||
|
||||
onChange(theme.colors);
|
||||
},
|
||||
[themes, onChange]
|
||||
);
|
||||
|
||||
return (
|
||||
<select onChange={onChangeTrigger} value={value?.title}>
|
||||
{!value && <option selected>{t("Custom theme")}</option>}
|
||||
|
||||
{themes.map((theme) => (
|
||||
<option key={theme.title} value={theme.title}>
|
||||
{theme.title}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
);
|
||||
};
|
||||
export { ThemeSelect };
|
|
@ -1,15 +1,20 @@
|
|||
import { ChangeEvent, FC, useCallback } from "react";
|
||||
import { ChangeEvent, FC, useCallback, useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { RowGroup } from "~/components/containers/RowGroup";
|
||||
import { SettingsRow } from "~/components/containers/SettingsRow";
|
||||
import {
|
||||
ColorSettings,
|
||||
SettingsValue,
|
||||
useSettings,
|
||||
} from "~/modules/settings/context/SettingsContext";
|
||||
import { ThemeSelect } from "../../components/ThemeSelect";
|
||||
import { useBuiltinThemes } from "~/modules/theme/constants/themes";
|
||||
import { useDefaultTheme } from "~/modules/theme/hooks/useDefaultTheme";
|
||||
|
||||
const ColorSettings: FC = () => {
|
||||
const { update, settings } = useSettings();
|
||||
const { t } = useTranslation();
|
||||
const defaultColors = useDefaultTheme();
|
||||
|
||||
const setString = useCallback(
|
||||
(field: keyof SettingsValue) => (event: ChangeEvent<HTMLInputElement>) => {
|
||||
|
@ -18,46 +23,88 @@ const ColorSettings: FC = () => {
|
|||
[update]
|
||||
);
|
||||
|
||||
const themes = useBuiltinThemes();
|
||||
|
||||
const currentTheme = useMemo(
|
||||
() =>
|
||||
themes.find((it) =>
|
||||
Object.entries({ ...defaultColors, ...it.colors }).every(
|
||||
([key, value]) =>
|
||||
(settings as unknown as Record<string, string>)[key] === value
|
||||
)
|
||||
),
|
||||
[themes, defaultColors, settings]
|
||||
);
|
||||
|
||||
const setThemeColors = useCallback(
|
||||
(val: ColorSettings) => {
|
||||
update({ ...defaultColors, ...val });
|
||||
},
|
||||
[defaultColors, update]
|
||||
);
|
||||
|
||||
const themeSubtitle = useMemo(() => {
|
||||
if (!currentTheme) {
|
||||
return t("User defined theme");
|
||||
}
|
||||
|
||||
if (!currentTheme.url) {
|
||||
return t("Built-in theme");
|
||||
}
|
||||
|
||||
return `<a href="${currentTheme.url}" target="__blank">${currentTheme.url}</a>`;
|
||||
}, [currentTheme, t]);
|
||||
|
||||
return (
|
||||
<RowGroup>
|
||||
<label htmlFor="color">
|
||||
<label htmlFor="theme">
|
||||
<SettingsRow title={t("Color theme")} subTitle={themeSubtitle}>
|
||||
<ThemeSelect
|
||||
value={currentTheme}
|
||||
onChange={setThemeColors}
|
||||
themes={themes}
|
||||
/>
|
||||
</SettingsRow>
|
||||
</label>
|
||||
|
||||
<label htmlFor="backgroundColor">
|
||||
<SettingsRow title={t("Background")}>
|
||||
<input
|
||||
type="color"
|
||||
id="color"
|
||||
id="backgroundColor"
|
||||
onChange={setString("backgroundColor")}
|
||||
value={settings.backgroundColor}
|
||||
/>
|
||||
</SettingsRow>
|
||||
</label>
|
||||
|
||||
<label htmlFor="color">
|
||||
<label htmlFor="textColor">
|
||||
<SettingsRow title={t("Text")}>
|
||||
<input
|
||||
type="color"
|
||||
id="color"
|
||||
id="textColor"
|
||||
onChange={setString("textColor")}
|
||||
value={settings.textColor}
|
||||
/>
|
||||
</SettingsRow>
|
||||
</label>
|
||||
|
||||
<label htmlFor="color">
|
||||
<label htmlFor="linkColor">
|
||||
<SettingsRow title={t("Links")}>
|
||||
<input
|
||||
type="color"
|
||||
id="color"
|
||||
id="linkColor"
|
||||
onChange={setString("linkColor")}
|
||||
value={settings.linkColor}
|
||||
/>
|
||||
</SettingsRow>
|
||||
</label>
|
||||
|
||||
<label htmlFor="color">
|
||||
<label htmlFor="codeColor">
|
||||
<SettingsRow title={t("Inline code")}>
|
||||
<input
|
||||
type="color"
|
||||
id="color"
|
||||
id="codeColor"
|
||||
onChange={setString("codeColor")}
|
||||
value={settings.codeColor}
|
||||
/>
|
||||
|
|
|
@ -5,6 +5,11 @@ export interface ColorSettings {
|
|||
textColor: string;
|
||||
linkColor: string;
|
||||
codeColor: string;
|
||||
h1Color?: string;
|
||||
h2Color?: string;
|
||||
h3Color?: string;
|
||||
h4Color?: string;
|
||||
h5Color?: string;
|
||||
}
|
||||
|
||||
export type SettingsValue = ColorSettings & {
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
import { useCallback, useState } from "react";
|
||||
import { defaultSettings } from "../context/SettingsContext";
|
||||
import {
|
||||
Theme,
|
||||
defaultDarkTheme,
|
||||
defaultLightTheme,
|
||||
} from "~/modules/theme/constants/theme";
|
||||
import { useDetectTheme } from "~/modules/theme/hooks/useDetectTheme";
|
||||
import { SettingsValue } from "~/modules/settings/context/SettingsContext";
|
||||
import { useDefaultTheme } from "~/modules/theme/hooks/useDefaultTheme";
|
||||
import { defaultSettings } from "../context/SettingsContext";
|
||||
|
||||
const getLocalStorageSettings = (defaultValue: SettingsValue) => {
|
||||
try {
|
||||
|
@ -20,9 +15,7 @@ const getLocalStorageSettings = (defaultValue: SettingsValue) => {
|
|||
};
|
||||
|
||||
export const usePersistSettings = () => {
|
||||
const theme = useDetectTheme();
|
||||
const defaultColors =
|
||||
theme === Theme.Dark ? defaultDarkTheme : defaultLightTheme;
|
||||
const defaultColors = useDefaultTheme();
|
||||
|
||||
const [settings, setSettings] = useState<SettingsValue>(
|
||||
getLocalStorageSettings({ ...defaultSettings, ...defaultColors })
|
||||
|
|
|
@ -10,6 +10,11 @@ export const defaultDarkTheme: ColorSettings = {
|
|||
textColor: "#eeeeee",
|
||||
linkColor: "#25bfe6",
|
||||
codeColor: "#ff3344",
|
||||
h1Color: "",
|
||||
h2Color: "",
|
||||
h3Color: "",
|
||||
h4Color: "",
|
||||
h5Color: "",
|
||||
};
|
||||
|
||||
export const defaultLightTheme: ColorSettings = {
|
||||
|
@ -17,4 +22,9 @@ export const defaultLightTheme: ColorSettings = {
|
|||
textColor: "#2e2e2e",
|
||||
linkColor: "#25bfe6",
|
||||
codeColor: "#ff3344",
|
||||
h1Color: "",
|
||||
h2Color: "",
|
||||
h3Color: "",
|
||||
h4Color: "",
|
||||
h5Color: "",
|
||||
};
|
||||
|
|
146
src/modules/theme/constants/themes.ts
Normal file
146
src/modules/theme/constants/themes.ts
Normal file
|
@ -0,0 +1,146 @@
|
|||
import { useMemo } from "react";
|
||||
import { CustomTheme } from "../types/theme";
|
||||
import { defaultDarkTheme, defaultLightTheme } from "./theme";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export const useBuiltinThemes = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return useMemo<CustomTheme[]>(
|
||||
() => [
|
||||
{
|
||||
title: t("Dark theme"),
|
||||
colors: defaultDarkTheme,
|
||||
},
|
||||
{
|
||||
title: t("Light theme"),
|
||||
colors: defaultLightTheme,
|
||||
},
|
||||
{
|
||||
title: t("Blueberry boar"),
|
||||
colors: {
|
||||
backgroundColor: "#201d26",
|
||||
textColor: "#eeeeee",
|
||||
linkColor: "#40bfbf",
|
||||
codeColor: "#d24276",
|
||||
h1Color: "#f1b8b8",
|
||||
h2Color: "#e2c79e",
|
||||
h3Color: "#a6d6c2",
|
||||
h4Color: "#a4b8cb",
|
||||
h5Color: "#c0a1da",
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Cattpucin Macchiato",
|
||||
url: "https://github.com/catppuccin/catppuccin",
|
||||
colors: {
|
||||
backgroundColor: "#24273a",
|
||||
textColor: "#cad3f5",
|
||||
linkColor: "#ee99a0",
|
||||
codeColor: "#f5a97f",
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Cattpucin Mocha",
|
||||
url: "https://github.com/catppuccin/catppuccin",
|
||||
colors: {
|
||||
backgroundColor: "#1e1e2e",
|
||||
textColor: "#cdd6f4",
|
||||
linkColor: "#74c7ec",
|
||||
codeColor: "#f38ba8",
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Cattpucin Frappe",
|
||||
url: "https://github.com/catppuccin/catppuccin",
|
||||
colors: {
|
||||
backgroundColor: "#303446",
|
||||
textColor: "#c6d0f5",
|
||||
linkColor: "#a6d189",
|
||||
codeColor: "#ef9f76",
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Cattpucin Latte",
|
||||
url: "https://github.com/catppuccin/catppuccin",
|
||||
colors: {
|
||||
backgroundColor: "#eff1f5",
|
||||
textColor: "#4c4f69",
|
||||
linkColor: "#1e66f5",
|
||||
codeColor: "#e64553",
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Dracula",
|
||||
url: "https://draculatheme.com/",
|
||||
colors: {
|
||||
backgroundColor: "#282a36",
|
||||
textColor: "#f8f8f2",
|
||||
linkColor: "#ff79c6",
|
||||
codeColor: "#6272a4",
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Tokyo Night",
|
||||
url: "https://github.com/enkia/tokyo-night-vscode-theme",
|
||||
colors: {
|
||||
backgroundColor: "#1a1b26",
|
||||
textColor: "#a9b1d6",
|
||||
linkColor: "#7aa2f7",
|
||||
codeColor: "#f7768e",
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Tokyo Night Storm",
|
||||
url: "https://github.com/enkia/tokyo-night-vscode-theme",
|
||||
colors: {
|
||||
backgroundColor: "#24283b",
|
||||
textColor: "#a9b1d6",
|
||||
linkColor: "#7aa2f7",
|
||||
codeColor: "#f7768e",
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Tokyo Night Light",
|
||||
url: "https://github.com/enkia/tokyo-night-vscode-theme",
|
||||
colors: {
|
||||
backgroundColor: "#d5d6db",
|
||||
textColor: "#343b58",
|
||||
linkColor: "#34548a",
|
||||
codeColor: "#8c4351",
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Solarized",
|
||||
url: "https://ethanschoonover.com/solarized/",
|
||||
colors: {
|
||||
backgroundColor: "#073642",
|
||||
textColor: "#eee8d5",
|
||||
linkColor: "#8a8a8a",
|
||||
codeColor: "#b58900",
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Horizon Dark",
|
||||
url: "https://horizontheme.netlify.app/",
|
||||
colors: {
|
||||
backgroundColor: "#232530",
|
||||
textColor: "#FAC29A",
|
||||
linkColor: "#21BFC2",
|
||||
codeColor: "#E95379",
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Horizon Bright",
|
||||
url: "https://horizontheme.netlify.app/",
|
||||
colors: {
|
||||
backgroundColor: "#FDF0ED",
|
||||
textColor: "#2E303E",
|
||||
linkColor: "#1EAEAE",
|
||||
codeColor: "#E84A72",
|
||||
},
|
||||
},
|
||||
],
|
||||
[]
|
||||
);
|
||||
};
|
11
src/modules/theme/hooks/useDefaultTheme.ts
Normal file
11
src/modules/theme/hooks/useDefaultTheme.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import {
|
||||
Theme,
|
||||
defaultDarkTheme,
|
||||
defaultLightTheme,
|
||||
} from "~/modules/theme/constants/theme";
|
||||
import { useDetectTheme } from "~/modules/theme/hooks/useDetectTheme";
|
||||
|
||||
export const useDefaultTheme = () => {
|
||||
const theme = useDetectTheme();
|
||||
return theme === Theme.Dark ? defaultDarkTheme : defaultLightTheme;
|
||||
};
|
|
@ -13,8 +13,11 @@ export const useThemeColors = (settings: ColorSettings) => {
|
|||
|
||||
const code = new Color(settings.codeColor);
|
||||
|
||||
console.log(code.hex());
|
||||
|
||||
// Backgrounds and text
|
||||
document.body.style.setProperty("--color-background", background.hex());
|
||||
document.body.style.setProperty("--color-code", code.hex());
|
||||
document.body.style.setProperty("--color-code-text", code.hex());
|
||||
document.body.style.setProperty(
|
||||
"--color-code-background",
|
||||
code.fade(isDark ? 0.9 : 0.7).toString()
|
||||
|
@ -24,7 +27,16 @@ export const useThemeColors = (settings: ColorSettings) => {
|
|||
document.body.style.setProperty("--color-border", border.hex());
|
||||
document.body.style.setProperty(
|
||||
"--color-pre-background",
|
||||
isDark ? background.lighten(0.2).hex() : background.darken(0.2).hex()
|
||||
isDark ? background.lighten(0.2).hex() : background.darken(0.1).hex()
|
||||
);
|
||||
|
||||
// Headings
|
||||
[...new Array(5)].forEach((_, i) => {
|
||||
document.body.style.setProperty(
|
||||
`--color-h${i + 1}`,
|
||||
(settings as unknown as Record<string, string>)[`h${i + 1}Color`] ||
|
||||
settings.textColor
|
||||
);
|
||||
});
|
||||
}, [settings]);
|
||||
};
|
||||
|
|
7
src/modules/theme/types/theme.ts
Normal file
7
src/modules/theme/types/theme.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { ColorSettings } from "~/modules/settings/context/SettingsContext";
|
||||
|
||||
export interface CustomTheme {
|
||||
title: string;
|
||||
url?: string;
|
||||
colors: ColorSettings;
|
||||
}
|
|
@ -6,4 +6,9 @@
|
|||
--color-code-text: #ff3344;
|
||||
--color-code-background: #{transparentize(#ff3344, 0.9)};
|
||||
--color-pre-background: #{lighten(#111111, 2%)};
|
||||
--color-h1: #ffffff;
|
||||
--color-h2: #ffffff;
|
||||
--color-h3: #ffffff;
|
||||
--color-h4: #ffffff;
|
||||
--color-h5: #ffffff;
|
||||
}
|
||||
|
|
|
@ -28,10 +28,14 @@ a {
|
|||
ul {
|
||||
padding-left: 1em;
|
||||
|
||||
li p {
|
||||
li {
|
||||
margin: 5px 0;
|
||||
|
||||
p {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
code {
|
||||
background: var(--color-code-background);
|
||||
|
@ -72,3 +76,19 @@ img {
|
|||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: var(--color-h1);
|
||||
}
|
||||
h2 {
|
||||
color: var(--color-h2);
|
||||
}
|
||||
h3 {
|
||||
color: var(--color-h3);
|
||||
}
|
||||
h4 {
|
||||
color: var(--color-h4);
|
||||
}
|
||||
h5 {
|
||||
color: var(--color-h5);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue