mirror of
https://github.com/doocs/md.git
synced 2024-11-24 19:10:34 +08:00
feat: support for theme options (#326)
close #322 --------- Co-Authored-By: brzhang <1595819400@qq.com>
This commit is contained in:
parent
900a7a1ca9
commit
c4c7b7fed7
@ -18,12 +18,13 @@ import {
|
||||
HoverCardTrigger,
|
||||
} from '@/components/ui/hover-card'
|
||||
|
||||
import { codeBlockThemeOptions, colorOptions, fontFamilyOptions, fontSizeOptions, githubConfig, legendOptions } from '@/config'
|
||||
import { codeBlockThemeOptions, colorOptions, fontFamilyOptions, fontSizeOptions, githubConfig, legendOptions, themeOptions } from '@/config'
|
||||
import { useStore } from '@/stores'
|
||||
|
||||
const store = useStore()
|
||||
|
||||
const {
|
||||
theme,
|
||||
fontFamily,
|
||||
fontSize,
|
||||
fontColor,
|
||||
@ -35,6 +36,7 @@ const {
|
||||
|
||||
const {
|
||||
resetStyleConfirm,
|
||||
themeChanged,
|
||||
fontChanged,
|
||||
sizeChanged,
|
||||
colorChanged,
|
||||
@ -73,6 +75,8 @@ function customStyle() {
|
||||
</el-icon>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent class="w-56">
|
||||
<StyleOptionMenu title="主题" :options="themeOptions" :current="theme" :change="themeChanged" />
|
||||
<DropdownMenuSeparator />
|
||||
<StyleOptionMenu title="字体" :options="fontFamilyOptions" :current="fontFamily" :change="fontChanged" />
|
||||
<StyleOptionMenu title="字号" :options="fontSizeOptions" :current="fontSize" :change="sizeChanged" />
|
||||
<StyleOptionMenu
|
||||
@ -133,7 +137,7 @@ function customStyle() {
|
||||
Mac 代码块
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem divided class="leading-8" @click="resetStyleConfirm">
|
||||
<DropdownMenuItem divided @click="resetStyleConfirm">
|
||||
<el-icon class="mr-2 h-4 w-4" />
|
||||
重置
|
||||
</DropdownMenuItem>
|
||||
|
@ -176,6 +176,7 @@ export const giteeConfig = {
|
||||
}
|
||||
|
||||
const baseColor = `#3f3f3f`
|
||||
const baseBorderColor = `rgba(215, 16, 166, 0.8)`
|
||||
|
||||
export const defaultTheme = {
|
||||
BASE: {
|
||||
@ -368,3 +369,270 @@ export const defaultTheme = {
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const graceTheme = {
|
||||
BASE: {
|
||||
'text-align': `left`,
|
||||
'line-height': `1.75`,
|
||||
},
|
||||
block: {
|
||||
'h1': {
|
||||
'font-size': `1.4em`,
|
||||
'text-align': `center`,
|
||||
'font-weight': `bold`,
|
||||
'display': `table`,
|
||||
'margin': `2em auto 1em`,
|
||||
'padding': `0.5em 1em`,
|
||||
'border-bottom': `2px solid ${baseBorderColor}`,
|
||||
'color': `var(--el-text-color-regular)`,
|
||||
'text-shadow': `2px 2px 4px rgba(0,0,0,0.1)`,
|
||||
'transition': `all 0.3s ease`,
|
||||
'&:hover': {
|
||||
'transform': `translateY(-3px)`,
|
||||
'box-shadow': `0 10px 20px rgba(0,0,0,0.1)`,
|
||||
},
|
||||
},
|
||||
|
||||
'h2': {
|
||||
'font-size': `1.3em`,
|
||||
'text-align': `center`,
|
||||
'font-weight': `bold`,
|
||||
'display': `table`,
|
||||
'margin': `4em auto 2em`,
|
||||
'padding': `0.3em 1em`,
|
||||
'background': `${baseBorderColor}`,
|
||||
'color': `#fff`,
|
||||
'border-radius': `8px`,
|
||||
'box-shadow': `0 4px 6px rgba(0,0,0,0.1)`,
|
||||
'transition': `all 0.3s ease`,
|
||||
'&:hover': {
|
||||
'transform': `scale(1.05)`,
|
||||
'box-shadow': `0 6px 8px rgba(0,0,0,0.15)`,
|
||||
},
|
||||
},
|
||||
|
||||
'h3': {
|
||||
'font-weight': `bold`,
|
||||
'font-size': `1.2em`,
|
||||
'margin': `2em 8px 0.75em 0`,
|
||||
'line-height': `1.2`,
|
||||
'padding-left': `12px`,
|
||||
'border-left': `4px solid ${baseBorderColor}`,
|
||||
'border-bottom': `1px solid ${baseBorderColor}`,
|
||||
'color': `var(--el-text-color-regular)`,
|
||||
'transition': `all 0.3s ease`,
|
||||
'&:hover': {
|
||||
'padding-left': `16px`,
|
||||
'border-left-width': `6px`,
|
||||
},
|
||||
},
|
||||
|
||||
'h4': {
|
||||
'font-weight': `bold`,
|
||||
'font-size': `1.1em`,
|
||||
'margin': `2em 8px 0.5em`,
|
||||
'color': `rgba(66, 185, 131, 0.9)`,
|
||||
'transition': `color 0.3s ease`,
|
||||
'&:hover': {
|
||||
color: `rgba(66, 185, 131, 1)`,
|
||||
},
|
||||
},
|
||||
|
||||
'p': {
|
||||
'margin': `1.5em 8px`,
|
||||
'letter-spacing': `0.1em`,
|
||||
'color': `var(--el-text-color-regular)`,
|
||||
'text-align': `justify`,
|
||||
'transition': `all 0.3s ease`,
|
||||
'&:hover': {
|
||||
transform: `translateX(3px)`,
|
||||
},
|
||||
},
|
||||
|
||||
'blockquote': {
|
||||
'font-style': `italic`,
|
||||
'border-left': `4px solid ${baseBorderColor}`,
|
||||
'padding': `1em 1em 1em 2em`,
|
||||
'border-radius': `6px`,
|
||||
'color': `rgba(0,0,0,0.6)`,
|
||||
'background': `linear-gradient(to right, #f7f7f7, #ffffff)`,
|
||||
'margin': `2em 8px`,
|
||||
'box-shadow': `0 4px 6px rgba(0,0,0,0.05)`,
|
||||
'transition': `all 0.3s ease`,
|
||||
'&:hover': {
|
||||
'transform': `translateY(-3px)`,
|
||||
'box-shadow': `0 6px 8px rgba(0,0,0,0.1)`,
|
||||
},
|
||||
},
|
||||
|
||||
'blockquote_p': {
|
||||
'letter-spacing': `0.1em`,
|
||||
'color': `rgb(80, 80, 80)`,
|
||||
'font-size': `1em`,
|
||||
'display': `block`,
|
||||
},
|
||||
|
||||
'code_pre': {
|
||||
'font-size': `14px`,
|
||||
'overflow-x': `auto`,
|
||||
'border-radius': `8px`,
|
||||
'padding': `1em`,
|
||||
'line-height': `1.5`,
|
||||
'margin': `10px 8px`,
|
||||
'box-shadow': `inset 0 0 10px rgba(0,0,0,0.05)`,
|
||||
'transition': `all 0.3s ease`,
|
||||
'&:hover': {
|
||||
'box-shadow': `inset 0 0 15px rgba(0,0,0,0.1)`,
|
||||
},
|
||||
},
|
||||
|
||||
'code': {
|
||||
'margin': 0,
|
||||
'white-space': `pre-wrap`,
|
||||
'font-family': `'Fira Code', Menlo, Operator Mono, Consolas, Monaco, monospace`,
|
||||
},
|
||||
|
||||
'image': {
|
||||
'border-radius': `8px`,
|
||||
'display': `block`,
|
||||
'margin': `0.1em auto 0.5em`,
|
||||
'width': `100% !important`,
|
||||
'box-shadow': `0 4px 8px rgba(0,0,0,0.1)`,
|
||||
'transition': `all 0.3s ease`,
|
||||
'&:hover': {
|
||||
'transform': `scale(1.02)`,
|
||||
'box-shadow': `0 6px 12px rgba(0,0,0,0.15)`,
|
||||
},
|
||||
},
|
||||
|
||||
'ol': {
|
||||
'margin-left': `0`,
|
||||
'padding-left': `1.5em`,
|
||||
'color': `var(--el-text-color-regular)`,
|
||||
},
|
||||
|
||||
'ul': {
|
||||
'margin-left': `0`,
|
||||
'padding-left': `1.5em`,
|
||||
'list-style': `none`,
|
||||
'color': `var(--el-text-color-regular)`,
|
||||
},
|
||||
|
||||
'ul li::before': {
|
||||
'content': `"•"`,
|
||||
'color': `rgba(0, 152, 116, 0.9)`,
|
||||
'font-weight': `bold`,
|
||||
'display': `inline-block`,
|
||||
'width': `1em`,
|
||||
'margin-left': `-1em`,
|
||||
},
|
||||
|
||||
'hr': {
|
||||
border: `none`,
|
||||
height: `1px`,
|
||||
background: `linear-gradient(to right, rgba(0,0,0,0), rgba(0,0,0,0.1), rgba(0,0,0,0))`,
|
||||
margin: `2em 0`,
|
||||
},
|
||||
},
|
||||
inline: {
|
||||
'listitem': {
|
||||
'text-indent': `-1em`,
|
||||
'display': `block`,
|
||||
'margin': `0.5em 8px`,
|
||||
'color': `var(--el-text-color-regular)`,
|
||||
'transition': `all 0.3s ease`,
|
||||
'&:hover': {
|
||||
transform: `translateX(5px)`,
|
||||
},
|
||||
},
|
||||
|
||||
'codespan': {
|
||||
'font-size': `90%`,
|
||||
'color': `#d14`,
|
||||
'background': `rgba(27,31,35,.05)`,
|
||||
'padding': `3px 5px`,
|
||||
'border-radius': `4px`,
|
||||
'transition': `all 0.3s ease`,
|
||||
'&:hover': {
|
||||
background: `rgba(27,31,35,.1)`,
|
||||
},
|
||||
},
|
||||
|
||||
'link': {
|
||||
'color': `#576b95`,
|
||||
'transition': `all 0.3s ease`,
|
||||
'&:hover': {
|
||||
'color': `#1a3f6f`,
|
||||
'text-decoration': `underline`,
|
||||
},
|
||||
},
|
||||
|
||||
'wx_link': {
|
||||
},
|
||||
|
||||
'strong': {
|
||||
'color': `rgba(15, 76, 129, 0.9)`,
|
||||
'font-weight': `bold`,
|
||||
'transition': `all 0.3s ease`,
|
||||
'&:hover': {
|
||||
'color': `rgba(15, 76, 129, 1)`,
|
||||
'text-shadow': `1px 1px 2px rgba(15, 76, 129, 0.2)`,
|
||||
},
|
||||
},
|
||||
|
||||
'table': {
|
||||
'border-collapse': `separate`,
|
||||
'border-spacing': `0`,
|
||||
'text-align': `center`,
|
||||
'margin': `1em 8px`,
|
||||
'color': `var(--el-text-color-regular)`,
|
||||
'box-shadow': `0 4px 6px rgba(0,0,0,0.1)`,
|
||||
'border-radius': `8px`,
|
||||
'overflow': `hidden`,
|
||||
},
|
||||
|
||||
'thead': {
|
||||
'background': `linear-gradient(45deg, rgba(0, 152, 116, 0.9), rgba(0, 192, 146, 0.9))`,
|
||||
'color': `#fff`,
|
||||
'font-weight': `bold`,
|
||||
},
|
||||
|
||||
'td': {
|
||||
border: `1px solid #dfdfdf`,
|
||||
padding: `0.5em 1em`,
|
||||
color: baseColor,
|
||||
transition: `all 0.3s ease`,
|
||||
},
|
||||
|
||||
'tr:hover td': {
|
||||
background: `rgba(0, 152, 116, 0.05)`,
|
||||
},
|
||||
|
||||
'footnote': {
|
||||
'font-size': `12px`,
|
||||
'color': `rgba(0,0,0,0.5)`,
|
||||
'transition': `all 0.3s ease`,
|
||||
'&:hover': {
|
||||
color: `rgba(0,0,0,0.7)`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const themeOptions = [
|
||||
{
|
||||
label: `经典`,
|
||||
value: `default`,
|
||||
desc: ``,
|
||||
},
|
||||
{
|
||||
label: `优雅`,
|
||||
value: `grace`,
|
||||
desc: ``,
|
||||
},
|
||||
]
|
||||
|
||||
export const themeMap = {
|
||||
default: defaultTheme,
|
||||
grace: graceTheme,
|
||||
}
|
||||
|
@ -5,11 +5,11 @@ import CodeMirror from 'codemirror'
|
||||
import { useDark, useStorage, useToggle } from '@vueuse/core'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
|
||||
import { codeBlockThemeOptions, colorOptions, fontFamilyOptions, fontSizeOptions, legendOptions } from '@/config'
|
||||
import { codeBlockThemeOptions, colorOptions, fontFamilyOptions, fontSizeOptions, legendOptions, themeMap, themeOptions } from '@/config'
|
||||
import WxRenderer from '@/utils/wx-renderer'
|
||||
import DEFAULT_CONTENT from '@/assets/example/markdown.md?raw'
|
||||
import DEFAULT_CSS_CONTENT from '@/assets/example/theme-css.txt?raw'
|
||||
import { addPrefix, css2json, customCssWithTemplate, downloadMD, exportHTML, formatCss, formatDoc, setColor, setColorWithCustomTemplate, setFontSize } from '@/utils'
|
||||
import { addPrefix, css2json, customCssWithTemplate, downloadMD, exportHTML, formatCss, formatDoc, setColorWithCustomTemplate, setFontSizeWithTemplate, setTheme } from '@/utils'
|
||||
|
||||
const defaultKeyMap = CodeMirror.keyMap.default
|
||||
const modPrefix
|
||||
@ -34,6 +34,8 @@ export const useStore = defineStore(`store`, () => {
|
||||
|
||||
const output = ref(``)
|
||||
|
||||
// 文本字体
|
||||
const theme = useStorage(addPrefix(`theme`), themeOptions[0].value)
|
||||
// 文本字体
|
||||
const fontFamily = useStorage(`fonts`, fontFamilyOptions[0].value)
|
||||
// 文本大小
|
||||
@ -45,8 +47,10 @@ export const useStore = defineStore(`store`, () => {
|
||||
// 图注格式
|
||||
const legend = useStorage(`legend`, legendOptions[3].value)
|
||||
|
||||
const fontSizeNumber = fontSize.value.replace(`px`, ``)
|
||||
|
||||
const wxRenderer = new WxRenderer({
|
||||
theme: setColor(fontColor.value),
|
||||
theme: setTheme(themeMap[theme.value], fontSizeNumber, fontColor.value, theme.value === `default`),
|
||||
fonts: fontFamily.value,
|
||||
size: fontSize.value,
|
||||
})
|
||||
@ -174,11 +178,11 @@ export const useStore = defineStore(`store`, () => {
|
||||
// 更新 CSS
|
||||
const updateCss = () => {
|
||||
const json = css2json(cssEditor.value.getValue())
|
||||
let theme = setFontSize(fontSize.value.replace(`px`, ``))
|
||||
let t = setTheme(themeMap[theme.value], fontSizeNumber, fontColor.value, theme.value === `default`)
|
||||
|
||||
theme = customCssWithTemplate(json, fontColor.value, theme)
|
||||
t = customCssWithTemplate(json, fontColor.value, t)
|
||||
wxRenderer.setOptions({
|
||||
theme,
|
||||
theme: t,
|
||||
})
|
||||
editorRefresh()
|
||||
}
|
||||
@ -224,6 +228,8 @@ export const useStore = defineStore(`store`, () => {
|
||||
isCiteStatus.value = false
|
||||
isMacCodeBlock.value = true
|
||||
|
||||
theme.value = themeOptions[0].value
|
||||
fontFamily.value = fontFamilyOptions[0].value
|
||||
fontFamily.value = fontFamilyOptions[0].value
|
||||
fontSize.value = fontSizeOptions[2].value
|
||||
fontColor.value = colorOptions[0].value
|
||||
@ -255,10 +261,18 @@ export const useStore = defineStore(`store`, () => {
|
||||
}
|
||||
|
||||
const getTheme = (size, color) => {
|
||||
const theme = setFontSize(size.replace(`px`, ``))
|
||||
return setColorWithCustomTemplate(theme, color)
|
||||
const t = setFontSizeWithTemplate(themeMap[theme.value])(size.replace(`px`, ``), theme.value === `default`)
|
||||
return setColorWithCustomTemplate(t, color, theme.value === `default`)
|
||||
}
|
||||
|
||||
const themeChanged = withAfterRefresh((newTheme) => {
|
||||
wxRenderer.setOptions({
|
||||
theme: setTheme(themeMap[newTheme], fontSizeNumber, fontColor.value, newTheme === `default`),
|
||||
})
|
||||
|
||||
theme.value = newTheme
|
||||
})
|
||||
|
||||
const fontChanged = withAfterRefresh((fonts) => {
|
||||
wxRenderer.setOptions({
|
||||
fonts,
|
||||
@ -393,6 +407,7 @@ export const useStore = defineStore(`store`, () => {
|
||||
output,
|
||||
editor,
|
||||
cssEditor,
|
||||
theme,
|
||||
fontFamily,
|
||||
fontSize,
|
||||
fontColor,
|
||||
@ -401,6 +416,7 @@ export const useStore = defineStore(`store`, () => {
|
||||
|
||||
editorRefresh,
|
||||
|
||||
themeChanged,
|
||||
fontChanged,
|
||||
sizeChanged,
|
||||
colorChanged,
|
||||
|
@ -9,13 +9,19 @@ export function addPrefix(str) {
|
||||
return `${prefix}__${str}`
|
||||
}
|
||||
|
||||
function createCustomTheme(theme, color) {
|
||||
function createCustomTheme(theme, color, isDefault = true) {
|
||||
const customTheme = JSON.parse(JSON.stringify(theme))
|
||||
customTheme.block.h1[`border-bottom`] = `2px solid ${color}`
|
||||
customTheme.block.h2.background = color
|
||||
customTheme.block.h3[`border-left`] = `3px solid ${color}`
|
||||
customTheme.block.h4.color = color
|
||||
customTheme.inline.strong.color = color
|
||||
|
||||
if (!isDefault) {
|
||||
customTheme.block.h3[`border-bottom`] = `1px dashed ${color}`
|
||||
customTheme.block.blockquote[`border-left`] = `4px solid ${color}`
|
||||
}
|
||||
|
||||
return customTheme
|
||||
}
|
||||
|
||||
@ -26,24 +32,34 @@ export function setColorWithTemplate(theme) {
|
||||
}
|
||||
}
|
||||
|
||||
export function setColorWithCustomTemplate(theme, color) {
|
||||
return createCustomTheme(theme, color)
|
||||
export function setColorWithCustomTemplate(theme, color, isDefault = true) {
|
||||
return createCustomTheme(theme, color, isDefault)
|
||||
}
|
||||
|
||||
// 设置自定义字体大小
|
||||
export function setFontSizeWithTemplate(template) {
|
||||
return function (fontSize) {
|
||||
return function (fontSize, isDefault = true) {
|
||||
const customTheme = JSON.parse(JSON.stringify(template))
|
||||
if (isDefault) {
|
||||
customTheme.block.h1[`font-size`] = `${fontSize * 1.2}px`
|
||||
customTheme.block.h2[`font-size`] = `${fontSize * 1.2}px`
|
||||
customTheme.block.h3[`font-size`] = `${fontSize * 1.1}px`
|
||||
customTheme.block.h4[`font-size`] = `${fontSize}px`
|
||||
}
|
||||
else {
|
||||
customTheme.block.h1[`font-size`] = `${fontSize * 1.4}px`
|
||||
customTheme.block.h2[`font-size`] = `${fontSize * 1.3}px`
|
||||
customTheme.block.h3[`font-size`] = `${fontSize * 1.2}px`
|
||||
customTheme.block.h4[`font-size`] = `${fontSize * 1.1}px`
|
||||
}
|
||||
|
||||
return customTheme
|
||||
}
|
||||
}
|
||||
|
||||
export const setColor = setColorWithTemplate(defaultTheme)
|
||||
export const setFontSize = setFontSizeWithTemplate(defaultTheme)
|
||||
export function setTheme(theme, fontSize, color, isDefault) {
|
||||
return setColorWithCustomTemplate(setFontSizeWithTemplate(theme)(fontSize, isDefault), color, isDefault)
|
||||
}
|
||||
|
||||
export function customCssWithTemplate(jsonString, color, theme) {
|
||||
// block
|
||||
|
Loading…
Reference in New Issue
Block a user