diff --git a/README.md b/README.md
index 084be39..f22e352 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@
- [x] 支持 Markdown 所有基础语法
- [x] 支持自定义 CSS 样式
-- [ ] 支持主题选择 & 配置。
+- [x] 支持主题选择 & 配置。
- [x] 支持明暗两种主题预览。
- [ ] 支持色盘取色,快速替换文章整体色调
@@ -100,6 +100,54 @@ Inline Code `{code: 0}`
内容在微信 Markdown 编辑器预览中不显示。在其它预览工具中展示内容。
```
+## 主题定制
+
+在目录 `src/themes` 中存放默认主题,主题使用 css 定义样式,不支持复杂的选择器。
+
+```css
+/* 1~6 标题样式定义 */
+h1 {} h2 {} h3 {} h4 {} h5 {} h6 {}
+a { color: red; } /* 超链接样式定义 */
+del {} /* 删除线样式定义 */
+em {} /* 下划线样式定义 */
+strong {} /* 加粗样式定义 */
+u {} /* 下划线样式定义 */
+p {} /* 段落样式定义 */
+ul {} /* 无序列表样式定义 */
+ol {} /* 有序列表样式定义 */
+li {} /* 列表条目样式定义 */
+blockquote {} /* 块级引用样式定义 */
+table {}
+td {}
+th {}
+pre {} /* 样式定义 */
+.code-highlight {} /* 代码块样式定义 */
+.code-line {} /* 代码块行样式定义 */
+.code-spans {} /* 代码块行样式定义 */
+
+sup {} /* GFM 脚注样式定义 */
+.footnotes-title {} /* GFM 脚注,参考标题样式定义 */
+.footnotes-list {} /* GFM 脚注,参考列表样式定义 */
+
+.image-warpper {} /* 图片父节点样式定义 */
+.image {} /* 图片样式定义 */
+
+/* 部分代码高亮样式 */
+.comment {}
+.property {}
+.function {}
+.keyword {}
+.punctuation {}
+.unit {}
+.tag {}
+.color {}
+.selector {}
+.quote {}
+.number {}
+.attr-name {}
+.attr-value {}
+```
+
## 部署
[](https://hub.docker.com/r/wcjiang/wxmp) [](https://hub.docker.com/r/wcjiang/wxmp) [](https://hub.docker.com/r/wcjiang/wxmp)
diff --git a/package.json b/package.json
index 05bee47..d1e31ed 100644
--- a/package.json
+++ b/package.json
@@ -27,8 +27,7 @@
"@uiw/codemirror-theme-xcode": "^4.11.6",
"@uiw/react-back-to-top": "^1.2.0",
"@uiw/react-github-corners": "^1.5.15",
- "@uiw/react-markdown-editor": "^5.3.2",
- "@uiw/react-markdown-preview": "^4.1.0",
+ "@uiw/react-markdown-editor": "^5.5.1",
"@wcj/dark-mode": "^1.0.15",
"css-tree": "^2.2.1",
"react": "^18.2.0",
diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx
index b8471d3..f7f9ce0 100644
--- a/src/components/Layout.tsx
+++ b/src/components/Layout.tsx
@@ -48,7 +48,9 @@ const Section = styled.section`
align-items: center;
gap: 0.8rem;
dark-mode {
- font-size: 1.2rem;
+ font-size: 1.05rem;
+ display: block;
+ line-height: 12px;
}
a svg {
display: block;
diff --git a/src/conf/default.md.css b/src/conf/default.md.css
deleted file mode 100644
index e8c5db6..0000000
--- a/src/conf/default.md.css
+++ /dev/null
@@ -1,195 +0,0 @@
-a {
- color: #576b95;
- text-decoration: none;
- font-size: 14px;
-}
-
-h1 {
- text-align:center;
- color:#3f3f3f;
- line-height:1.75;
- font-family:-apple-system-font,BlinkMacSystemFont,"Helvetica Neue","PingFang SC","Hiragino Sans GB","Microsoft YaHei UI","Microsoft YaHei",Arial,sans-serif;
- font-size:1.2em;
- font-weight:bold;
- display:table;
- margin:2em auto 1em;
- padding:0 1em;
- border-bottom:2px solid #009874;
- margin-top: 0;
-}
-
-h2 {
- text-align:center;
- color:#fff;
- line-height:1.75;
- font-family:-apple-system-font,BlinkMacSystemFont,"Helvetica Neue","PingFang SC","Hiragino Sans GB","Microsoft YaHei UI","Microsoft YaHei",Arial,sans-serif;
- font-size:1.2em;
- font-weight:bold;
- display:table;
- margin:4em auto 2em;
- padding:0 0.3em;
- border-radius: 0.3rem;
- background:#009874;
-}
-
-p {
- font-size: 14px;
-}
-
-h3 {
- text-align:left;
- color:#3f3f3f;
- line-height:1.2;
- font-family:-apple-system-font,BlinkMacSystemFont,"Helvetica Neue","PingFang SC","Hiragino Sans GB","Microsoft YaHei UI","Microsoft YaHei",Arial,sans-serif;
- font-size:1.1em;
- font-weight:bold;
- margin:2em 8px 0.75em 0;
- padding-left:8px;
- border-left:3px solid #009874;
-}
-
-ul {
- padding-left: 1.2em;
- font-size: 14px;
-}
-
-ol {
- padding-left: 1.2em;
- font-size: 14px;
-}
-
-li {
- font-size: 16px;
- margin: 0;
- line-height: 26px;
- color: rgb(30 41 59);
- font-size: 14px;
-}
-
-blockquote {
- text-align:left;
- line-height:1.75;
- font-family:-apple-system-font,BlinkMacSystemFont,"Helvetica Neue","PingFang SC","Hiragino Sans GB","Microsoft YaHei UI","Microsoft YaHei",Arial,sans-serif;
- font-size:14px;
- font-style:normal;
- border-left:none;
- padding:0.1rem 1rem;
- border-radius:4px;
- background:rgba(27,31,35,.05);
- margin: 1rem 0;
-}
-
-pre {
- display: block;
- overflow-x: auto;
- padding: 1em;
- color: rgb(51, 51, 51);
- background: rgb(248, 248, 248);
- font-size: 14px;
- font-style: normal;
- font-variant-ligatures: normal;
- font-variant-caps: normal;
- font-weight: 400;
- letter-spacing: normal;
- orphans: 2;
- text-indent: 0px;
- text-transform: none;
- widows: 2;
- word-spacing: 0px;
- text-decoration-style: initial;
- text-decoration-color: initial;
- text-align: left;
- line-height: 1.5;
- font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;
- border-radius: 5px;
- margin: 0.9rem 0;
- white-space: pre;
-}
-
-table {
- width: 100% !important;
- border-collapse: collapse;
- line-height: 1.35;
- font-size: 90%;
-}
-
-td {
- border: 1px solid #DDD;
- padding: 0.25em 0.5em;
-}
-
-th {
- background: rgb(0 0 0 / 5%);
- border: 1px solid #DDD;
- padding: 0.25em 0.5em;
-}
-
-.code-highlight {
- text-align: left;
- line-height: 1.75;
- font-family: Menlo, "Operator Mono", Consolas, Monaco, monospace;
- font-size: 14px;
- margin: 0px;
- white-space: nowrap;
-}
-
-.code-line {
- display: block;
- line-height: 1.3;
-}
-
-.code-spans {
- text-align:left;
- line-height:1;
- white-space:pre;
- color: #009874;
- background:rgba(27,31,35,.05);
- padding: 0.2rem 0.3rem;
- border-radius:4px;
- font-weight: bold;
- font-size: 14px;
-}
-
-.footnotes-title {
- font-family:-apple-system-font,BlinkMacSystemFont,"Helvetica Neue","PingFang SC","Hiragino Sans GB","Microsoft YaHei UI","Microsoft YaHei",Arial,sans-serif;
- font-size:1.0rem;
- font-weight:bold;
- display:table;
- margin:3rem 0 0.6rem 0;
- padding-left: 0.2rem;
-}
-
-.footnotes-list {
- font-size: 12px;
- font-style: italic;
- line-height: 1.2;
- margin: 0.4rem 0;
-}
-
-figure {
- margin: 0;
-}
-
-.image-warpper {
- text-align: center;
- margin-bottom: 0rem;
- visibility: visible;
-}
-
-.image {
- display: initial;
- max-width: 100%;
-}
-
-.comment { color: #6a737d; }
-.property { color: #6f42c1; }
-.function { color: #6f42c1; }
-.keyword { color: #d73a49; }
-.punctuation { color: #0550ae; }
-.unit { color: #0550ae; }
-.tag { color: #22863a; }
-.selector { color: #22863a; }
-.quote { color: #22863a; }
-.number { color: #005cc5; }
-.attr-name { color: #005cc5; }
-.attr-value { color: #005cc5; }
\ No newline at end of file
diff --git a/src/pages/home/Preview.tsx b/src/pages/home/Preview.tsx
index 184d045..5ef250c 100644
--- a/src/pages/home/Preview.tsx
+++ b/src/pages/home/Preview.tsx
@@ -1,6 +1,7 @@
import { MarkdownPreviewProps } from '@uiw/react-markdown-preview';
import styled from 'styled-components';
-import def from '../../conf/default.md.css';
+import { useContext } from 'react';
+import { Context, previewThemes } from '../../store/context';
import { markdownToHTML } from '../../utils/markdownToHTML';
@@ -11,7 +12,8 @@ const Warpper = styled.div`
min-height: 100%;
`;
-export const Preview = (props: MarkdownPreviewProps, visible: boolean) => {
- const html = markdownToHTML(props.source || '', def);
+export const Preview = (props: MarkdownPreviewProps) => {
+ const { css } = useContext(Context);
+ const html = markdownToHTML(props.source || '', previewThemes[css].value);
return ;
};
diff --git a/src/pages/home/index.tsx b/src/pages/home/index.tsx
index 2d478c0..be43183 100644
--- a/src/pages/home/index.tsx
+++ b/src/pages/home/index.tsx
@@ -4,7 +4,7 @@ import { EditorView } from '@codemirror/view';
import styled from 'styled-components';
import { Preview } from './Preview';
import { copy } from './copy';
-import { theme as themeCommand } from './theme';
+import { theme as themeCommand, previeTheme } from './theme';
import { Context, themes } from '../../store/context';
import data from '../../../README.md';
@@ -15,13 +15,14 @@ const Warpper = styled.div`
export const HomePage = () => {
const commands = [...getCommands(), themeCommand];
const { theme } = useContext(Context);
+ const value = themes[theme].value;
return (
) => setTheme(ev.target.value as any);
return (
);
};
@@ -53,10 +43,26 @@ export const theme: ICommand = {
name: 'theme',
keyCommand: 'theme',
button: (command, props, opts) => ,
- icon: (
-
- ),
+};
+
+const ThemePreviewView: React.FC<{}> = () => {
+ const { css, setCss } = useContext(Context);
+ const handleChange = (ev: React.ChangeEvent) => setCss(ev.target.value as any);
+ return (
+
+ );
+};
+
+export const previeTheme: ICommand = {
+ name: 'previewTtheme',
+ keyCommand: 'previewTtheme',
+ button: () => ,
};
diff --git a/src/store/context.tsx b/src/store/context.tsx
index 1a38c4b..bca25c4 100644
--- a/src/store/context.tsx
+++ b/src/store/context.tsx
@@ -1,4 +1,5 @@
import React from 'react';
+import { defaultTheme } from '@uiw/react-markdown-editor';
import { abcdef } from '@uiw/codemirror-theme-abcdef';
import { androidstudio } from '@uiw/codemirror-theme-androidstudio';
import { atomone } from '@uiw/codemirror-theme-atomone';
@@ -12,45 +13,116 @@ import { githubLight, githubDark } from '@uiw/codemirror-theme-github';
import { okaidia } from '@uiw/codemirror-theme-okaidia';
import { sublime } from '@uiw/codemirror-theme-sublime';
import { xcodeLight, xcodeDark } from '@uiw/codemirror-theme-xcode';
+import defStyle from '../themes/default.md.css';
+import simpleStyle from '../themes/simple.md.css';
+import underscoreStyle from '../themes/underscore.md.css';
export const themes = {
- abcdef,
- androidstudio,
- atomone,
- bbedit,
- bespin,
- darcula,
- dracula,
- duotoneLight,
- duotoneDark,
- eclipse,
- githubLight,
- githubDark,
- okaidia,
- sublime,
- xcodeLight,
- xcodeDark,
+ default: {
+ label: '默认主题',
+ value: defaultTheme,
+ },
+ abcdef: {
+ label: 'Abcdef Theme',
+ value: abcdef,
+ },
+ androidstudio: {
+ label: 'Android Studio Theme',
+ value: androidstudio,
+ },
+ atomone: {
+ label: 'Atomone Theme',
+ value: atomone,
+ },
+ bbedit: {
+ label: 'Bbedit Theme',
+ value: bbedit,
+ },
+ bespin: {
+ label: 'Bespin Theme',
+ value: bespin,
+ },
+ darcula: {
+ label: 'Darcula Theme',
+ value: darcula,
+ },
+ dracula: {
+ label: 'Dracula Theme',
+ value: dracula,
+ },
+ duotoneLight: {
+ label: 'Duotone Light Theme',
+ value: duotoneLight,
+ },
+ duotoneDark: {
+ label: 'Duotone Dark Theme',
+ value: duotoneDark,
+ },
+ eclipse: {
+ label: 'Eclipse Theme',
+ value: eclipse,
+ },
+ githubLight: {
+ label: 'Github Light Theme',
+ value: githubLight,
+ },
+ githubDark: {
+ label: 'Github Dark Theme',
+ value: githubDark,
+ },
+ okaidia: {
+ label: 'Okaidia Theme',
+ value: okaidia,
+ },
+ sublime: {
+ label: 'Sublime Theme',
+ value: sublime,
+ },
+ xcodeLight: {
+ label: 'Xcode Light Theme',
+ value: xcodeLight,
+ },
+ xcodeDark: {
+ label: 'Xcode Dark Theme',
+ value: xcodeDark,
+ },
+};
+
+export const previewThemes = {
+ default: {
+ label: '翡翠绿',
+ value: defStyle,
+ },
+ simple: {
+ label: '简洁蓝',
+ value: simpleStyle,
+ },
+ underscore: {
+ label: '下划线黄',
+ value: underscoreStyle,
+ },
};
export type ThemeValue = keyof typeof themes;
+export type PreviewThemeValue = keyof typeof previewThemes;
export interface CreateContext {
- css: string;
- setCss: React.Dispatch>;
+ css: PreviewThemeValue;
+ setCss: React.Dispatch>;
theme: ThemeValue;
setTheme: React.Dispatch>;
}
export const Context = React.createContext({
- css: '',
+ css: 'default',
setCss: () => {},
- theme: 'githubLight',
+ theme: 'default',
setTheme: () => {},
});
export const Provider: React.FC = ({ children }) => {
- const [css, setCss] = React.useState('');
- const [theme, setTheme] = React.useState('githubLight');
+ const [css, setCss] = React.useState('underscore');
+ const [theme, setTheme] = React.useState('default');
return (