diff --git a/.gitignore b/.gitignore index 9c387af..7b3f102 100644 --- a/.gitignore +++ b/.gitignore @@ -43,5 +43,8 @@ yarn-error.log* # mockm httpData + +package-lock.json public/upload/** -!public/upload/*.gitkeep \ No newline at end of file +!public/upload/*.gitkeep + diff --git a/package.json b/package.json index 25650a3..53f336e 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "crypto-js": "^4.1.1", "element-ui": "^2.15.6", "form-data": "4.0.0", + "highlight.js": "^11.3.1", "jquery": "^3.6.0", "juice": "^8.0.0", "marked": "^4.0.5", @@ -39,6 +40,7 @@ "@vue/cli-service": "~4.5.15", "async-validator": "^4.0.7", "babel-plugin-import": "^1.13.3", + "cache-loader": "^4.1.0", "cross-env": "^7.0.3", "jest": "^27.4.0", "less": "^4.1.2", diff --git a/src/App.vue b/src/App.vue index 3c99dfd..58c415c 100644 --- a/src/App.vue +++ b/src/App.vue @@ -20,7 +20,7 @@ body, /* 每个页面公共css */ @import url("./assets/less/style-mirror.css"); @import url("./assets/less/theme.less"); -@import url("./assets/less/code-theme.less"); + ::-webkit-scrollbar { width: 6px; height: 6px; diff --git a/src/assets/less/code-theme.less b/src/assets/less/code-theme.less deleted file mode 100644 index 2315e23..0000000 --- a/src/assets/less/code-theme.less +++ /dev/null @@ -1,2 +0,0 @@ -@import url("./codeTheme/wechat-code-block.less"); -@import url("./codeTheme/github-code-block.less"); diff --git a/src/assets/less/codeTheme/github-code-block.less b/src/assets/less/codeTheme/github-code-block.less deleted file mode 100644 index 0fb6e11..0000000 --- a/src/assets/less/codeTheme/github-code-block.less +++ /dev/null @@ -1,49 +0,0 @@ -@import url("../github-v2.min.css"); -/*github code block*/ -.code-snippet__github { - display: flex; - font-size: 12px; - margin: 10px 8px; - position: relative; - height: auto; - background-color: #f7f7f7; - border-radius: 8px; - - .code-snippet__line-index { - display: none; - } - - .code__pre { - display: grid; - position: relative; - counter-reset: line; - overflow-x: auto; - padding: 1em; - white-space: normal; - flex: 1; - line-height: 20px; - font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; - -webkit-overflow-scrolling: touch; - } - - pre { - display: inline-block; - font-size: 12px; - } - - code { - display: flex; - position: relative; - padding-right: 8px; - text-align: left; - white-space: pre; - font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; - &::before { - display: none; - } - } - - ul li { - list-style: none; - } -} diff --git a/src/assets/less/codeTheme/wechat-code-block.less b/src/assets/less/codeTheme/wechat-code-block.less deleted file mode 100644 index 526559d..0000000 --- a/src/assets/less/codeTheme/wechat-code-block.less +++ /dev/null @@ -1,62 +0,0 @@ -/*wechat code block*/ -.rich_media_content .code-snippet *, -.rich_media_content .code-snippet__wechat * { - max-width: 1000% !important; -} - -.code-snippet__wechat { - word-wrap: break-word !important; - font-size: 14px; - margin: 10px 8px; - color: #333; - position: relative; - background-color: rgba(27, 31, 35, 0.05); - border: 1px solid #f0f0f0; - border-radius: 2px; - display: flex; - line-height: 24px; -} - -.code-snippet__wechat .code-snippet__line-index { - counter-reset: line; - flex-shrink: 0; - height: 100%; - padding: 1em; - list-style-type: none; -} - -.code-snippet__wechat .code-snippet__line-index li { - list-style-type: none; - text-align: right; -} - -.code-snippet__wechat .code-snippet__line-index li::before { - min-width: 1.5em; - text-align: right; - left: -2.5em; - counter-increment: line; - content: counter(line); - display: inline; - color: rgba(0, 0, 0, 0.15); -} - -.code-snippet__wechat pre { - overflow-x: auto; - padding: 1em 1em 1em 1em; - white-space: normal; - flex: 1; - -webkit-overflow-scrolling: touch; -} - -.code-snippet__wechat code { - text-align: left; - font-size: 14px; - white-space: pre; - display: flex; - position: relative; - font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; -} - -.code-snippet__wechat ul li { - list-style: none; -} diff --git a/src/assets/less/github-v2.min.css b/src/assets/less/github-v2.min.css deleted file mode 100644 index 3ff324c..0000000 --- a/src/assets/less/github-v2.min.css +++ /dev/null @@ -1,72 +0,0 @@ -/*! Color themes for Google Code Prettify | MIT License | github.com/jmblog/color-themes-for-google-code-prettify */ -.prettyprint { - font-family: Menlo, Bitstream Vera Sans Mono, DejaVu Sans Mono, Monaco, - Consolas, monospace; - border: 0 !important; -} -.pln { - color: #333; -} -ol.linenums { - margin-top: 0; - margin-bottom: 0; - color: #ccc; -} -li.L0, -li.L1, -li.L2, -li.L3, -li.L4, -li.L5, -li.L6, -li.L7, -li.L8, -li.L9 { - padding-left: 1em; - background-color: #fff; - list-style-type: decimal; -} -@media screen { - .str { - color: #183691; - } - .kwd { - color: #a71d5d; - } - .com { - color: #969896; - } - .typ { - color: #0086b3; - } - .lit { - color: #0086b3; - } - .pun { - color: #333; - } - .opn { - color: #333; - } - .clo { - color: #333; - } - .tag { - color: navy; - } - .atn { - color: #795da3; - } - .atv { - color: #183691; - } - .dec { - color: #333; - } - .var { - color: teal; - } - .fun { - color: #900; - } -} diff --git a/src/assets/scripts/config.js b/src/assets/scripts/config.js index ded88d7..f6b794a 100644 --- a/src/assets/scripts/config.js +++ b/src/assets/scripts/config.js @@ -57,14 +57,29 @@ export default { ], codeThemeOption: [ { - label: "微信", - value: "wechat", - desc: "默认样式", + label: "github", + value: "https://lib.baomitu.com/highlight.js/10.7.3/styles/github.min.css", + desc: "light", }, { - label: "GitHub", - value: "github", - desc: "精简风格", + label: "solarized-light", + value: "https://lib.baomitu.com/highlight.js/11.3.1/styles/base16/solarized-light.min.css", + desc: "light", + }, + { + label: "atom-one-dark", + value: "https://lib.baomitu.com/highlight.js/11.3.1/styles/atom-one-dark.min.css", + desc: "dark", + }, + { + label: "obsidian", + value: "https://lib.baomitu.com/highlight.js/11.3.1/styles/obsidian.min.css", + desc: "dark", + }, + { + label: "vs2015", + value: "https://lib.baomitu.com/highlight.js/11.3.1/styles/vs2015.min.css", + desc: "dark", }, ], form: { diff --git a/src/assets/scripts/renderers/wx-renderer.js b/src/assets/scripts/renderers/wx-renderer.js index 46a1bdf..07cb9e0 100644 --- a/src/assets/scripts/renderers/wx-renderer.js +++ b/src/assets/scripts/renderers/wx-renderer.js @@ -1,4 +1,5 @@ import { Renderer } from "marked"; +import hljs from 'highlight.js'; class WxRenderer { constructor(opts) { @@ -7,9 +8,6 @@ class WxRenderer { let footnoteIndex = 0; let styleMapping = new Map(); - const CODE_FONT_FAMILY = - "Menlo, Operator Mono, Consolas, Monaco, monospace"; - let merge = (base, extend) => Object.assign({}, base, extend); this.buildTheme = (themeTpl) => { @@ -25,13 +23,10 @@ class WxRenderer { } } - let base_block = merge(base, {}); + let base_block = merge(base, {}); for (let ele in themeTpl.block) { if (themeTpl.block.hasOwnProperty(ele)) { let style = themeTpl.block[ele]; - if (ele === "code") { - style["font-family"] = CODE_FONT_FAMILY; - } mapping[ele] = merge(base_block, style); } } @@ -126,23 +121,17 @@ class WxRenderer { return `
${text}
`; }; renderer.code = (text, lang) => { - text = text.replace(//g, ">"); - const codeLines = text - .split("\n") - .map( - (line) => - `${ - line || " " - }` - ); - const codeTheme = "github"; - return ` -
-
-                        ${codeLines.join("")}
-                    
-
- `; + lang = hljs.getLanguage(lang) ? lang : 'plaintext'; + + text = hljs.highlight(text, {language: lang}).value; + + text = text.replace(/\r\n/g,"
") + .replace(/\n/g,"
") + .replace(/(>[^<]+)|(^[^<]+)/g, function(str) { + return str.replace(/\s/g, ' ') + }); + + return `
${text}
` }; renderer.codespan = (text, lang) => `${text}`; diff --git a/src/assets/scripts/themes/default-theme.js b/src/assets/scripts/themes/default-theme.js index ccb2266..f42d115 100644 --- a/src/assets/scripts/themes/default-theme.js +++ b/src/assets/scripts/themes/default-theme.js @@ -1,11 +1,9 @@ +let baseColor = "#3f3f3f" + export default { BASE: { "text-align": "left", - color: "#3f3f3f", - "line-height": "1.75", - }, - BASE_BLOCK: { - margin: "1em 8px", + "line-height": "1.75" }, block: { // 一级标题样式 @@ -17,6 +15,7 @@ export default { margin: "2em auto 1em", padding: "0 1em", "border-bottom": "2px solid rgba(0, 152, 116, 0.9)", + color: baseColor, }, // 二级标题样式 @@ -39,6 +38,7 @@ export default { "line-height": "1.2", "padding-left": "8px", "border-left": "3px solid rgba(0, 152, 116, 0.9)", + color: baseColor, }, // 四级标题样式 @@ -53,6 +53,7 @@ export default { p: { margin: "1.5em 8px", "letter-spacing": "0.1em", + color: baseColor, }, // 引用样式 @@ -72,20 +73,20 @@ export default { "font-size": "1em", display: "block", }, - - code: { - "font-size": "80%", - overflow: "auto", - color: "#333", - "white-space": "pre", - background: "rgb(247, 247, 247)", + code_pre: { + "font-size": "14px", + "overflow-x": "auto", "border-radius": "8px", - padding: "10px", + padding: "1em", "line-height": "1.5", - border: "1px solid rgb(236,236,236)", - margin: "20px 0", + margin: "10px 8px" }, - + code: { + "margin": 0, + "white-space": "nowrap", + "font-family": "Menlo, Operator Mono, Consolas, Monaco, monospace" + }, + image: { "border-radius": "4px", display: "block", @@ -96,21 +97,25 @@ export default { ol: { "margin-left": "0", "padding-left": "1em", + color: baseColor, }, ul: { "margin-left": "0", "padding-left": "1em", "list-style": "circle", + color: baseColor, }, footnotes: { margin: "0.5em 8px", "font-size": "80%", + color: baseColor, }, figure: { margin: "1.5em 8px", + color: baseColor, }, hr: { "border-style": "solid", @@ -127,6 +132,7 @@ export default { "text-indent": "-1em", display: "block", margin: "0.2em 8px", + color: baseColor, }, codespan: { @@ -157,20 +163,24 @@ export default { "border-collapse": "collapse", "text-align": "center", margin: "1em 8px", + color: baseColor, }, thead: { background: "rgba(0, 0, 0, 0.05)", "font-weight": "bold", + color: baseColor, }, td: { border: "1px solid #dfdfdf", padding: "0.25em 0.5em", + color: baseColor, }, footnote: { "font-size": "12px", + color: baseColor, }, figcaption: { diff --git a/src/assets/scripts/util.js b/src/assets/scripts/util.js index d8f1754..44b48f8 100644 --- a/src/assets/scripts/util.js +++ b/src/assets/scripts/util.js @@ -215,15 +215,6 @@ export function formatCss(content) { return doc; } -export function fixCodeWhiteSpace(value = "pre") { - const preDomList = document.getElementsByClassName("code__pre"); - if (preDomList.length > 0) { - preDomList.forEach((pre) => { - pre.style.whiteSpace = value; - }); - } -} - /** * 导出原始 Markdown 文档 * @param {文档内容} doc @@ -264,7 +255,6 @@ export function exportHTML() { function setStyles(element) { switch (true) { - case isSection(element): case isPre(element): case isCode(element): case isSpan(element): @@ -275,13 +265,6 @@ export function exportHTML() { Array.from(element.children).forEach((child) => setStyles(child)); } - // 判断是否是包裹代码块的 section 元素 - function isSection(element) { - return ( - element.tagName === "SECTION" && - Array.from(element.classList).includes("code-snippet__github") - ); - } // 判断是否是包裹代码块的 pre 元素 function isPre(element) { return ( diff --git a/src/components/CodemirrorEditor/header.vue b/src/components/CodemirrorEditor/header.vue index 4d7e610..11c1392 100644 --- a/src/components/CodemirrorEditor/header.vue +++ b/src/components/CodemirrorEditor/header.vue @@ -109,6 +109,22 @@ {{ color.desc }} + + + {{ code.label }} + {{ code.desc }} + + { let clipboardDiv = document.getElementById("output"); solveWeChatImage(); - fixCodeWhiteSpace(); solveHtml(); clipboardDiv.focus(); window.getSelection().removeAllRanges(); @@ -286,7 +301,6 @@ export default { window.getSelection().addRange(range); document.execCommand("copy"); window.getSelection().removeAllRanges(); - fixCodeWhiteSpace("normal"); clipboardDiv.innerHTML = this.output; // 输出提示 this.$notify({ @@ -326,11 +340,13 @@ export default { this.fontChanged(this.config.builtinFonts[0].value); this.colorChanged(this.config.colorOption[0].value); this.sizeChanged(this.config.sizeOption[2].value); + this.codeThemeChanged(this.config.codeThemeOption[0].value) this.$emit("cssChanged"); this.selectFont = this.currentFont; this.selectSize = this.currentSize; this.selectColor = this.currentColor; this.showResetConfirm = false; + this.selectCodeTheme = this.codeTheme; }, cancelReset() { this.showResetConfirm = false; diff --git a/src/pages/index/view/CodemirrorEditor.vue b/src/pages/index/view/CodemirrorEditor.vue index 98f0ae6..716ce55 100644 --- a/src/pages/index/view/CodemirrorEditor.vue +++ b/src/pages/index/view/CodemirrorEditor.vue @@ -149,6 +149,7 @@ export default { currentColor: (state) => state.currentColor, nightMode: (state) => state.nightMode, rightClickMenuVisible: (state) => state.rightClickMenuVisible, + codeTheme: (state) => state.codeTheme, }), }, created() { @@ -223,6 +224,21 @@ export default { }); this.onEditorRefresh(); }, + // 切换 highlight.js 代码主题 + codeThemeChanged() { + let cssUrl = this.codeTheme; + let el = document.getElementById('hljs') + if (el != undefined) { + el.setAttribute('href', cssUrl); + } else { + var link = document.createElement('link'); + link.setAttribute('type','text/css'); + link.setAttribute('rel','stylesheet'); + link.setAttribute('href',cssUrl); + link.setAttribute('id','hljs'); + document.head.appendChild(link); + } + }, beforeUpload(file) { // validate image const checkResult = checkImage(file); @@ -318,6 +334,7 @@ export default { }, // 更新编辑器 onEditorRefresh() { + this.codeThemeChanged(this.codeTheme); this.editorRefresh(); setTimeout(() => PR.prettyPrint(), 0); }, @@ -532,8 +549,10 @@ export default { transform: none; } } +.codeMirror-wrapper { + overflow-x: auto; +} diff --git a/src/store/index.js b/src/store/index.js index 77b3c3f..d38456b 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -21,7 +21,7 @@ const state = { currentColor: "", citeStatus: 0, nightMode: false, - codeTheme: "github", + codeTheme: config.codeThemeOption[0].value, rightClickMenuVisible: false, }; const mutations = {