From 1f7af047bbbfe268a736a90578d05ee8c2df1171 Mon Sep 17 00:00:00 2001 From: Ryan Wang Date: Fri, 13 Sep 2024 11:58:25 +0800 Subject: [PATCH] feat: improve compatibility of the copy code feature (#28) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优化复制代码功能的兼容性。 ```release-note 优化复制代码功能的兼容性。 ``` --- .../highlightjs/HighlightJSHeadProcessor.java | 4 +- .../static/plugins/highlightjs-copy.css | 61 ++++++++++++++ .../static/plugins/highlightjs-copy.js | 82 +++++++++++++++++++ .../static/plugins/highlightjs-copy.min.css | 1 - .../static/plugins/highlightjs-copy.min.js | 1 - 5 files changed, 145 insertions(+), 4 deletions(-) create mode 100644 src/main/resources/static/plugins/highlightjs-copy.css create mode 100644 src/main/resources/static/plugins/highlightjs-copy.js delete mode 100644 src/main/resources/static/plugins/highlightjs-copy.min.css delete mode 100644 src/main/resources/static/plugins/highlightjs-copy.min.js diff --git a/src/main/java/run/halo/highlightjs/HighlightJSHeadProcessor.java b/src/main/java/run/halo/highlightjs/HighlightJSHeadProcessor.java index f17604d..1288f75 100644 --- a/src/main/java/run/halo/highlightjs/HighlightJSHeadProcessor.java +++ b/src/main/java/run/halo/highlightjs/HighlightJSHeadProcessor.java @@ -75,8 +75,8 @@ private String highlightJsScript(BasicConfig basicConfig) { - - + + diff --git a/src/main/resources/static/plugins/highlightjs-copy.css b/src/main/resources/static/plugins/highlightjs-copy.css new file mode 100644 index 0000000..cd3a93f --- /dev/null +++ b/src/main/resources/static/plugins/highlightjs-copy.css @@ -0,0 +1,61 @@ +.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: 0.5em; + right: 0.5em; + width: 2rem; + height: 2rem; + text-indent: -9999px; + color: var(--hljs-theme-color); + border-radius: 0.25rem; + border: 1px solid; + border-color: color-mix(in srgb, var(--hljs-theme-color), transparent 80%); + background-color: var(--hljs-theme-background); + transition: background-color 200ms ease, transform 200ms ease-out; + overflow: hidden; +} +.hljs-copy-button:not([data-copied="true"])::before { + content: ""; + top: 0; + left: 0; + width: 100%; + height: 100%; + position: absolute; + background-color: currentColor; + mask: url('data:image/svg+xml;utf-8,'); + mask-repeat: no-repeat; + mask-size: 1rem; + mask-position: center center; +} +.hljs-copy-button:hover { + background-color: color-mix(in srgb, var(--hljs-theme-color), transparent 90%); +} +.hljs-copy-button:active { + border-color: color-mix(in srgb, var(--hljs-theme-color), transparent 60%); +} +.hljs-copy-button[data-copied="true"] { + text-indent: 0; + width: auto; +} +@media (prefers-reduced-motion) { + .hljs-copy-button { + transition: none; + } +} +.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/src/main/resources/static/plugins/highlightjs-copy.js b/src/main/resources/static/plugins/highlightjs-copy.js new file mode 100644 index 0000000..a257c0b --- /dev/null +++ b/src/main/resources/static/plugins/highlightjs-copy.js @@ -0,0 +1,82 @@ +class CopyButtonPlugin { + constructor(options = {}) { + self.hook = options.hook; + self.callback = options.callback; + self.lang = options.lang || document.documentElement.lang || "en"; + } + "after:highlightElement"({ el, text }) { + let button = Object.assign(document.createElement("button"), { + innerHTML: locales[lang]?.[0] || "Copy", + className: "hljs-copy-button", + }); + button.dataset.copied = false; + el.parentElement.classList.add("hljs-copy-wrapper"); + el.parentElement.appendChild(button); + el.parentElement.style.setProperty("--hljs-theme-background", window.getComputedStyle(el).backgroundColor); + el.parentElement.style.setProperty("--hljs-theme-color", window.getComputedStyle(el).color); + button.onclick = function () { + let newText = text; + + if (hook && typeof hook === "function") { + newText = hook(text, el) || text; + } + + const copyText = () => { + if (navigator.clipboard) { + return navigator.clipboard.writeText(newText); + } else { + const textArea = document.createElement("textarea"); + textArea.value = newText; + textArea.style.position = "fixed"; + document.body.appendChild(textArea); + textArea.focus(); + textArea.select(); + + try { + const successful = document.execCommand("copy"); + document.body.removeChild(textArea); + return successful ? Promise.resolve() : Promise.reject(); + } catch (err) { + document.body.removeChild(textArea); + return Promise.reject(err); + } + } + }; + + copyText() + .then(function () { + button.innerHTML = locales[lang]?.[1] || "Copied!"; + button.dataset.copied = true; + let alert = Object.assign(document.createElement("div"), { + role: "status", + className: "hljs-copy-alert", + innerHTML: locales[lang]?.[2] || "Copied to clipboard", + }); + el.parentElement.appendChild(alert); + setTimeout(() => { + button.innerHTML = locales[lang]?.[0] || "Copy"; + button.dataset.copied = false; + el.parentElement.removeChild(alert); + alert = null; + }, 2e3); + }) + .then(function () { + if (typeof callback === "function") return callback(newText, el); + }); + }; + } +} +if (typeof module != "undefined") { + module.exports = CopyButtonPlugin; +} +const locales = { + en: ["Copy", "Copied!", "Copied to clipboard"], + es: ["Copiar", "¡Copiado!", "Copiado al portapapeles"], + fr: ["Copier", "Copié !", "Copié dans le presse-papier"], + de: ["Kopieren", "Kopiert!", "In die Zwischenablage kopiert"], + ja: ["コピー", "コピーしました!", "クリップボードにコピーしました"], + ko: ["복사", "복사됨!", "클립보드에 복사됨"], + ru: ["Копировать", "Скопировано!", "Скопировано в буфер обмена"], + zh: ["复制", "已复制!", "已复制到剪贴板"], + "zh-tw": ["複製", "已複製!", "已複製到剪貼簿"], +}; diff --git a/src/main/resources/static/plugins/highlightjs-copy.min.css b/src/main/resources/static/plugins/highlightjs-copy.min.css deleted file mode 100644 index 6ea1128..0000000 --- a/src/main/resources/static/plugins/highlightjs-copy.min.css +++ /dev/null @@ -1 +0,0 @@ -.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:.5em;right:.5em;width:2rem;height:2rem;text-indent:-9999px;color:var(--hljs-theme-color);border-radius:.25rem;border:1px solid;border-color:color-mix(in srgb,var(--hljs-theme-color),transparent 80%);background-color:var(--hljs-theme-background);transition:background-color 200ms ease,transform 200ms ease-out;overflow:hidden}.hljs-copy-button:not([data-copied="true"])::before{content:"";top:0;left:0;width:100%;height:100%;position:absolute;background-color:currentColor;mask:url('data:image/svg+xml;utf-8,');mask-repeat:no-repeat;mask-size:1rem;mask-position:center center}.hljs-copy-button:hover{background-color:color-mix(in srgb,var(--hljs-theme-color),transparent 90%)}.hljs-copy-button:active{border-color:color-mix(in srgb,var(--hljs-theme-color),transparent 60%)}.hljs-copy-button[data-copied="true"]{text-indent:0;width:auto}@media(prefers-reduced-motion){.hljs-copy-button{transition:none}}.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/src/main/resources/static/plugins/highlightjs-copy.min.js b/src/main/resources/static/plugins/highlightjs-copy.min.js deleted file mode 100644 index 8621c5e..0000000 --- a/src/main/resources/static/plugins/highlightjs-copy.min.js +++ /dev/null @@ -1 +0,0 @@ -class CopyButtonPlugin{constructor(options={}){self.hook=options.hook;self.callback=options.callback;self.lang=options.lang||document.documentElement.lang||"en"}"after:highlightElement"({el,text}){let button=Object.assign(document.createElement("button"),{innerHTML:locales[lang]?.[0]||"Copy",className:"hljs-copy-button"});button.dataset.copied=false;el.parentElement.classList.add("hljs-copy-wrapper");el.parentElement.appendChild(button);el.parentElement.style.setProperty("--hljs-theme-background",window.getComputedStyle(el).backgroundColor);el.parentElement.style.setProperty("--hljs-theme-color",window.getComputedStyle(el).color);button.onclick=function(){if(!navigator.clipboard)return;let newText=text;if(hook&&typeof hook==="function"){newText=hook(text,el)||text}navigator.clipboard.writeText(newText).then(function(){button.innerHTML=locales[lang]?.[1]||"Copied!";button.dataset.copied=true;let alert=Object.assign(document.createElement("div"),{role:"status",className:"hljs-copy-alert",innerHTML:locales[lang]?.[2]||"Copied to clipboard"});el.parentElement.appendChild(alert);setTimeout(()=>{button.innerHTML=locales[lang]?.[0]||"Copy";button.dataset.copied=false;el.parentElement.removeChild(alert);alert=null},2e3)}).then(function(){if(typeof callback==="function")return callback(newText,el)})}}}if(typeof module!="undefined"){module.exports=CopyButtonPlugin}const locales={en:["Copy","Copied!","Copied to clipboard"],es:["Copiar","¡Copiado!","Copiado al portapapeles"],fr:["Copier","Copié !","Copié dans le presse-papier"],de:["Kopieren","Kopiert!","In die Zwischenablage kopiert"],ja:["コピー","コピーしました!","クリップボードにコピーしました"],ko:["복사","복사됨!","클립보드에 복사됨"],ru:["Копировать","Скопировано!","Скопировано в буфер обмена"],zh:["复制","已复制!","已复制到剪贴板"],"zh-tw":["複製","已複製!","已複製到剪貼簿"]}; \ No newline at end of file