From d194704d576f121db5f15fef16deaca6e6c04127 Mon Sep 17 00:00:00 2001 From: terwer Date: Mon, 10 Oct 2022 13:19:31 +0800 Subject: [PATCH] =?UTF-8?q?feat:#1=20=E6=B7=BB=E5=8A=A0=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E9=AB=98=E4=BA=AE=E6=8F=92=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/scss/index.dark.scss | 35 ++++++- package.json | 1 + pages/post/[postid].vue | 4 +- .../{fontawesomePlugin.ts => fontawesome.ts} | 0 plugins/hljs/codecopy/codecopy.css | 54 +++++++++++ plugins/hljs/codecopy/index.js | 95 +++++++++++++++++++ plugins/hljs/vue-hljs/main.js | 78 +++++++++++++++ plugins/hljs/vue-hljs/vs.css | 52 ++++++++++ plugins/hljs/vue-hljs/vs2015.css | 95 +++++++++++++++++++ plugins/vuehljs.ts | 9 ++ yarn.lock | 5 + 11 files changed, 423 insertions(+), 5 deletions(-) rename plugins/{fontawesomePlugin.ts => fontawesome.ts} (100%) create mode 100644 plugins/hljs/codecopy/codecopy.css create mode 100644 plugins/hljs/codecopy/index.js create mode 100644 plugins/hljs/vue-hljs/main.js create mode 100644 plugins/hljs/vue-hljs/vs.css create mode 100644 plugins/hljs/vue-hljs/vs2015.css create mode 100644 plugins/vuehljs.ts diff --git a/assets/scss/index.dark.scss b/assets/scss/index.dark.scss index 72fed23..7409f57 100644 --- a/assets/scss/index.dark.scss +++ b/assets/scss/index.dark.scss @@ -1,6 +1,6 @@ -html.dark body{ +html.dark body { /* 自定义深色背景颜色 */ - --custom-app-color:#ffffff; + --custom-app-color: #ffffff; --custom-app-bg-color: #000000; } @@ -18,4 +18,35 @@ html.dark .post-detail-content-box table th { html.dark .post-detail-content-box table td { border-color: #ccc; background-color: #161616; +} + +/** dark code */ +html.dark .post-detail-content-box .hljs { + color: #ffffff; + background-color: #161616 !important; + border: 1px solid #ccc !important; +} + +html.dark code { + color: #2fd945 !important; +} + +html.dark .hljs-keyword, +html.dark .hljs-selector-tag, +html.dark .hljs-built_in, +html.dark .hljs-name, +html.dark .hljs-tag { + color: #539dc5 !important; +} + +html.dark .post-detail-content-box .hljs-string, +html.dark .post-detail-content-box .hljs-title, +html.dark .post-detail-content-box .hljs-section, +html.dark .post-detail-content-box .hljs-attribute, +html.dark .post-detail-content-box .hljs-literal, +html.dark .post-detail-content-box .hljs-template-tag, +html.dark .post-detail-content-box .hljs-template-variable, +html.dark .post-detail-content-box .hljs-type, +html.dark .post-detail-content-box .hljs-addition { + color: #2fd945 !important; } \ No newline at end of file diff --git a/package.json b/package.json index e174f62..150e4d0 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@fortawesome/vue-fontawesome": "^3.0.1", "arraybuffer-xml-parser": "^0.6.0", "element-plus": "^2.2.15", + "highlight.js": "^11.6.0", "pinia": "^2.0.23", "showdown": "^2.1.0", "vue": "^3.2.40", diff --git a/pages/post/[postid].vue b/pages/post/[postid].vue index e844586..dd88ada 100644 --- a/pages/post/[postid].vue +++ b/pages/post/[postid].vue @@ -36,11 +36,9 @@ --> -
diff --git a/plugins/fontawesomePlugin.ts b/plugins/fontawesome.ts similarity index 100% rename from plugins/fontawesomePlugin.ts rename to plugins/fontawesome.ts diff --git a/plugins/hljs/codecopy/codecopy.css b/plugins/hljs/codecopy/codecopy.css new file mode 100644 index 0000000..9208d60 --- /dev/null +++ b/plugins/hljs/codecopy/codecopy.css @@ -0,0 +1,54 @@ +.hljs-copy-wrapper { + position: relative; + overflow: hidden; +} +.hljs-copy-wrapper:hover .hljs-copy-button, +.hljs-copy-button:focus { + transform: translateX(0); +} +.hljs-copy-button { + position: absolute; + transform: translateX(calc(100% + 1.125em)); + top: 1em; + right: 1em; + width: 2rem; + height: 2rem; + text-indent: -9999px; /* Hide the inner text */ + color: #fff; + border-radius: 0.25rem; + border: 1px solid #ffffff22; + /*background-color: #2d2b57;*/ + background-color: #0d6efd; + background-image: url('data:image/svg+xml;utf-8,'); + background-repeat: no-repeat; + background-position: center; + transition: background-color 200ms ease, transform 200ms ease-out; + cursor: pointer; +} +.hljs-copy-button:hover { + border-color: #ffffff44; +} +.hljs-copy-button:active { + border-color: #ffffff66; +} +.hljs-copy-button[data-copied="true"] { + text-indent: 0px; /* Shows the inner text */ + width: auto; + background-image: none; +} +@media (prefers-reduced-motion) { + .hljs-copy-button { + transition: none; + } +} + +/* visually-hidden */ +.hljs-copy-alert { + clip: rect(0 0 0 0); + clip-path: inset(50%); + height: 1px; + overflow: hidden; + position: absolute; + white-space: nowrap; + width: 1px; +} diff --git a/plugins/hljs/codecopy/index.js b/plugins/hljs/codecopy/index.js new file mode 100644 index 0000000..e84cd75 --- /dev/null +++ b/plugins/hljs/codecopy/index.js @@ -0,0 +1,95 @@ +/** + * @file highlight-copy.js + * @author Arron Hunt + * @copyright Copyright 2021. All rights reserved. + */ + +/** + * Adds a copy button to highlightjs code blocks + */ +export class CopyButtonPlugin { + /** + * Create a new CopyButtonPlugin class instance + * @param {Object} [options] - Functions that will be called when a copy event fires + * @param {CopyCallback} [options.callback] + * @param {Hook} [options.hook] + */ + constructor(options = {}) { + // this.hook = options.hook; + // this.callback = options.callback; + } + + // @ts-ignore + "after:highlightElement"({el, text}) { + // Create the copy button and append it to the codeblock. + const button = Object.assign(document.createElement("button"), { + innerHTML: "Copy", + className: "hljs-copy-button" + }); + // @ts-ignore + button.dataset.copied = false; + el.parentElement.classList.add("hljs-copy-wrapper"); + el.parentElement.appendChild(button); + + // Add a custom proprety to the code block so that the copy button can reference and match its background-color value. + el.parentElement.style.setProperty( + "--hljs-theme-background", + window.getComputedStyle(el).backgroundColor + ); + + button.onclick = function () { + if (!navigator.clipboard) return; + + let newText = text; + // @ts-ignore + // eslint-disable-next-line no-undef + // if (hook && typeof hook === "function") { + // // @ts-ignore + // // eslint-disable-next-line no-undef + // newText = hook(text, el) || text; + // } + + navigator.clipboard + .writeText(newText) + .then(function () { + button.innerHTML = "复制成功"; + // @ts-ignore + button.dataset.copied = true; + + let alert = Object.assign(document.createElement("div"), { + role: "status", + className: "hljs-copy-alert", + innerHTML: "复制到剪贴板" + }); + el.parentElement.appendChild(alert); + + setTimeout(() => { + button.innerHTML = "Copy"; + // @ts-ignore + button.dataset.copied = false; + el.parentElement.removeChild(alert); + // @ts-ignore + alert = null; + }, 2000); + }) + .then(function () { + // @ts-ignore + // eslint-disable-next-line no-undef + if (typeof callback === "function") return callback(newText, el); + }); + }; + } +} + +/** + * @typedef {function} CopyCallback + * @param {string} text - The raw text copied to the clipboard. + * @param {HTMLElement} el - The code block element that was copied from. + * @returns {undefined} + */ +/** + * @typedef {function} Hook + * @param {string} text - The raw text copied to the clipboard. + * @param {HTMLElement} el - The code block element that was copied from. + * @returns {string|undefined} + */ diff --git a/plugins/hljs/vue-hljs/main.js b/plugins/hljs/vue-hljs/main.js new file mode 100644 index 0000000..a82046a --- /dev/null +++ b/plugins/hljs/vue-hljs/main.js @@ -0,0 +1,78 @@ +import Hljs from "highlight.js"; +import {CopyButtonPlugin} from "../codecopy"; +import "../codecopy/codecopy.css"; +import "./vs.css"; + +const vueHljs = {}; + +vueHljs.install = Vue => { + // 代码复制 + Hljs.addPlugin( + new CopyButtonPlugin() + ); + + Vue.directive("highlight", el => { + const blocks = el.querySelectorAll("pre code"); + Array.prototype.forEach.call(blocks, Hljs.highlightBlock); + + // 代码选项卡 + // 代码块 + const codeGroups = el.querySelectorAll("code-group"); + // 处理每个代码块 + codeGroups.forEach(group => { + // 防止重复添加 + if (group.getElementsByTagName("ul").length === 0) { + const newNode = document.createElement("ul"); + newNode.setAttribute("class", "code-tab"); + + const codeBlocks = group.querySelectorAll("code-block"); + codeBlocks.forEach(block => { + const title = block.attributes.getNamedItem("title")?.value; + const active = block.attributes.getNamedItem("active")?.value; + const isActive = active !== undefined; + // console.log(block.attributes.length) + // console.log(title) + // console.log(isActive) + + const item = document.createElement("li"); + item.setAttribute( + "class", + isActive ? "code-tab-item current" : "code-tab-item" + ); + item.innerHTML = title || ""; + item.addEventListener("click", function (event) { + const targetElement = event.target; + // 选择状态 + // console.log(codeBlocks[0].innerHTML) + const allLis = targetElement.parentElement.querySelectorAll("li"); + allLis.forEach(li => { + li.setAttribute("class", "code-tab-item"); + }); + targetElement.setAttribute("class", "code-tab-item current"); + + // 设置tab + codeBlocks.forEach(cb => { + if ( + cb.attributes.getNamedItem("title")?.value === + targetElement.innerHTML + ) { + cb.setAttribute("active", ""); + } else { + cb.removeAttribute("active"); + } + }); + // console.log(targetElement.innerHTML); + }); + + newNode.append(item); + }); + + const firstBlock = codeBlocks[0]; + firstBlock?.parentNode?.insertBefore(newNode, firstBlock); + // console.log("tab") + } + }); + }); +}; + +export default vueHljs; diff --git a/plugins/hljs/vue-hljs/vs.css b/plugins/hljs/vue-hljs/vs.css new file mode 100644 index 0000000..50897d3 --- /dev/null +++ b/plugins/hljs/vue-hljs/vs.css @@ -0,0 +1,52 @@ +code{ + font-family: "LXGW WenKai","Wenquanyi Micro Hei","Wenquanyi Micro Hei Mono","Microsoft YaHei", "PT Sans", "-apple-system", "Liberation Mono", monospace, dejavu sans mono,Fira Code,Microsoft Yahei,Consolas,Courier New,monospace,Menlo,Monaco !important; + font-size: 14px; color: red; +} +.hljs { + font-family: "LXGW WenKai","Wenquanyi Micro Hei","Wenquanyi Micro Hei Mono","Microsoft YaHei", "PT Sans", "-apple-system", "Liberation Mono", monospace, dejavu sans mono,Fira Code,Microsoft Yahei,Consolas,Courier New,monospace,Menlo,Monaco !important; + font-size: 14px; + display: block; + overflow-x: auto; + padding: .5em; + line-height: 1.6; + color: black; + background-color: #f5f5f5!important; + /*border: 1px solid #ccc!important;*/ + border-radius: 3px!important; +} + +.hljs-comment, .hljs-quote, .hljs-variable { + color: #008000 +} + +.hljs-keyword, .hljs-selector-tag, .hljs-built_in, .hljs-name, .hljs-tag { + color: #00f +} + +.hljs-string, .hljs-title, .hljs-section, .hljs-attribute, .hljs-literal, .hljs-template-tag, .hljs-template-variable, .hljs-type, .hljs-addition { + color: #a31515 +} + +.hljs-deletion, .hljs-selector-attr, .hljs-selector-pseudo, .hljs-meta { + color: #2b91af +} + +.hljs-doctag { + color: #808080 +} + +.hljs-attr { + color: #f00 +} + +.hljs-symbol, .hljs-bullet, .hljs-link { + color: #00b0e8 +} + +.hljs-emphasis { + font-style: italic +} + +.hljs-strong { + font-weight: bold +} diff --git a/plugins/hljs/vue-hljs/vs2015.css b/plugins/hljs/vue-hljs/vs2015.css new file mode 100644 index 0000000..f6a1f38 --- /dev/null +++ b/plugins/hljs/vue-hljs/vs2015.css @@ -0,0 +1,95 @@ +code{ + font-family: "LXGW WenKai","Wenquanyi Micro Hei","Wenquanyi Micro Hei Mono","Microsoft YaHei", "PT Sans", "-apple-system", "Liberation Mono", monospace, dejavu sans mono,Fira Code,Microsoft Yahei,Consolas,Courier New,monospace,Menlo,Monaco !important; + color: #D69D85; + font-size: 14px; +} +.hljs { + font-family: "LXGW WenKai","Wenquanyi Micro Hei","Wenquanyi Micro Hei Mono","Microsoft YaHei", "PT Sans", "-apple-system", "Liberation Mono", monospace, dejavu sans mono,Fira Code,Microsoft Yahei,Consolas,Courier New,monospace,Menlo,Monaco !important; + font-size: 14px; + display: block; + overflow-x: auto; + padding: .5em; + background: #181818; + color: #DCDCDC; + border: solid 1px #181818; + border-radius: 5px; + line-height: 1.6; +} + +.hljs-keyword, .hljs-literal, .hljs-symbol, .hljs-name { + color: #569CD6 +} + +.hljs-link { + color: #569CD6; + text-decoration: underline +} + +.hljs-built_in, .hljs-type { + color: #4EC9B0 +} + +.hljs-number, .hljs-class { + color: #B8D7A3 +} + +.hljs-string, .hljs-meta-string { + color: #D69D85 +} + +.hljs-regexp, .hljs-template-tag { + color: #9A5334 +} + +.hljs-subst, .hljs-function, .hljs-title, .hljs-params, .hljs-formula { + color: #DCDCDC +} + +.hljs-comment, .hljs-quote { + color: #57A64A; + font-style: italic +} + +.hljs-doctag { + color: #608B4E +} + +.hljs-meta, .hljs-meta-keyword, .hljs-tag { + color: #9B9B9B +} + +.hljs-variable, .hljs-template-variable { + color: #BD63C5 +} + +.hljs-attr, .hljs-attribute, .hljs-builtin-name { + color: #9CDCFE +} + +.hljs-section { + color: gold +} + +.hljs-emphasis { + font-style: italic +} + +.hljs-strong { + font-weight: bold +} + +.hljs-bullet, .hljs-selector-tag, .hljs-selector-id, .hljs-selector-class, .hljs-selector-attr, .hljs-selector-pseudo { + color: #D7BA7D +} + +.hljs-addition { + background-color: #144212; + display: inline-block; + width: 100% +} + +.hljs-deletion { + background-color: #600; + display: inline-block; + width: 100% +} diff --git a/plugins/vuehljs.ts b/plugins/vuehljs.ts new file mode 100644 index 0000000..82e24b0 --- /dev/null +++ b/plugins/vuehljs.ts @@ -0,0 +1,9 @@ +import vueHljs from "~/plugins/hljs/vue-hljs/main.js"; + +/** + * 代码高亮插件 + */ +export default defineNuxtPlugin((nuxtApp) => { + // vueHljs + nuxtApp.vueApp.use(vueHljs) +}); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index c866e7d..fb5ae00 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2547,6 +2547,11 @@ hash-sum@^2.0.0: resolved "https://registry.npmmirror.com/hash-sum/-/hash-sum-2.0.0.tgz#81d01bb5de8ea4a214ad5d6ead1b523460b0b45a" integrity sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg== +highlight.js@^11.6.0: + version "11.6.0" + resolved "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.6.0.tgz#a50e9da05763f1bb0c1322c8f4f755242cff3f5a" + integrity sha512-ig1eqDzJaB0pqEvlPVIpSSyMaO92bH1N2rJpLMN/nX396wTpDA4Eq0uK+7I/2XG17pFaaKE0kjV/XPeGt7Evjw== + hookable@^5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/hookable/-/hookable-5.3.0.tgz#eabdd7bef9e04cb3505c49153b669d5d53974e7d"