From 9c0a63d054fcb7f41c525e03d459e6de7bcefd45 Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Mon, 21 Jul 2025 22:47:57 +0800 Subject: [PATCH] Add code block copy button with toast --- open-isle-cli/src/assets/global.css | 18 +++++++++++++ open-isle-cli/src/components/CommentItem.vue | 9 ++++--- open-isle-cli/src/utils/markdown.js | 28 ++++++++++++++++++-- open-isle-cli/src/views/AboutPageView.vue | 10 ++++--- open-isle-cli/src/views/PostPageView.vue | 9 ++++--- 5 files changed, 61 insertions(+), 13 deletions(-) diff --git a/open-isle-cli/src/assets/global.css b/open-isle-cli/src/assets/global.css index 3e8af32c8..f0ae8ce8c 100644 --- a/open-isle-cli/src/assets/global.css +++ b/open-isle-cli/src/assets/global.css @@ -101,6 +101,24 @@ body { padding: 8px 12px; border-radius: 4px; line-height: 0.8; + position: relative; +} + +.copy-code-btn { + position: absolute; + top: 4px; + right: 4px; + font-size: 12px; + padding: 2px 6px; + border: none; + border-radius: 4px; + background-color: var(--primary-color); + color: #fff; + cursor: pointer; +} + +.copy-code-btn:hover { + background-color: var(--primary-color-hover); } .info-content-text code { diff --git a/open-isle-cli/src/components/CommentItem.vue b/open-isle-cli/src/components/CommentItem.vue index 7613d83ab..724fb5038 100644 --- a/open-isle-cli/src/components/CommentItem.vue +++ b/open-isle-cli/src/components/CommentItem.vue @@ -21,7 +21,7 @@ -
+
@@ -69,7 +69,7 @@ import { ref, watch, computed } from 'vue' import VueEasyLightbox from 'vue-easy-lightbox' import { useRouter } from 'vue-router' import CommentEditor from './CommentEditor.vue' -import { renderMarkdown } from '../utils/markdown' +import { renderMarkdown, handleMarkdownClick } from '../utils/markdown' import TimeManager from '../utils/time' import BaseTimeline from './BaseTimeline.vue' import { API_BASE_URL, toast } from '../main' @@ -196,7 +196,8 @@ const CommentItem = { toast.success('已复制') }) } - const handleImageClick = e => { + const handleContentClick = e => { + handleMarkdownClick(e) if (e.target.tagName === 'IMG') { const container = e.target.parentNode const imgs = [...container.querySelectorAll('img')].map(i => i.src) @@ -205,7 +206,7 @@ const CommentItem = { lightboxVisible.value = true } } - return { showReplies, toggleReplies, showEditor, toggleEditor, submitReply, copyCommentLink, renderMarkdown, isWaitingForReply, commentMenuItems, deleteComment, lightboxVisible, lightboxIndex, lightboxImgs, handleImageClick, loggedIn } + return { showReplies, toggleReplies, showEditor, toggleEditor, submitReply, copyCommentLink, renderMarkdown, isWaitingForReply, commentMenuItems, deleteComment, lightboxVisible, lightboxIndex, lightboxImgs, handleContentClick, loggedIn } } } diff --git a/open-isle-cli/src/utils/markdown.js b/open-isle-cli/src/utils/markdown.js index c0cd7291d..c8534057a 100644 --- a/open-isle-cli/src/utils/markdown.js +++ b/open-isle-cli/src/utils/markdown.js @@ -1,15 +1,39 @@ import MarkdownIt from 'markdown-it' +import hljs from 'highlight.js' +import 'highlight.js/styles/github.css' +import { toast } from '../main' const md = new MarkdownIt({ html: false, linkify: true, - breaks: true + breaks: true, + highlight: (str, lang) => { + let code = '' + if (lang && hljs.getLanguage(lang)) { + code = hljs.highlight(str, { language: lang, ignoreIllegals: true }).value + } else { + code = hljs.highlightAuto(str).value + } + return `
${code}
` + } }) export function renderMarkdown(text) { return md.render(text || '') } +export function handleMarkdownClick(e) { + if (e.target.classList.contains('copy-code-btn')) { + const pre = e.target.closest('pre') + const codeEl = pre && pre.querySelector('code') + if (codeEl) { + navigator.clipboard.writeText(codeEl.innerText).then(() => { + toast.success('已复制') + }) + } + } +} + export function stripMarkdown(text) { const html = md.render(text || '') const el = document.createElement('div') @@ -24,4 +48,4 @@ export function stripMarkdownLength(text, length) { } // 截断并加省略号 return plain.slice(0, length) + '...' -} \ No newline at end of file +} diff --git a/open-isle-cli/src/views/AboutPageView.vue b/open-isle-cli/src/views/AboutPageView.vue index 5145d31b1..d08326885 100644 --- a/open-isle-cli/src/views/AboutPageView.vue +++ b/open-isle-cli/src/views/AboutPageView.vue @@ -10,7 +10,7 @@
{{ tab.label }}
-
+
@@ -19,7 +19,7 @@ diff --git a/open-isle-cli/src/views/PostPageView.vue b/open-isle-cli/src/views/PostPageView.vue index dd3966efc..fca654538 100644 --- a/open-isle-cli/src/views/PostPageView.vue +++ b/open-isle-cli/src/views/PostPageView.vue @@ -51,7 +51,7 @@
{{ author.username }}
{{ postTime }}
-
+
@@ -113,7 +113,7 @@ import ArticleTags from '../components/ArticleTags.vue' import ArticleCategory from '../components/ArticleCategory.vue' import ReactionsGroup from '../components/ReactionsGroup.vue' import DropdownMenu from '../components/DropdownMenu.vue' -import { renderMarkdown } from '../utils/markdown' +import { renderMarkdown, handleMarkdownClick } from '../utils/markdown' import { API_BASE_URL, toast } from '../main' import { getToken, authState } from '../utils/auth' import TimeManager from '../utils/time' @@ -237,7 +237,8 @@ export default { return false } - const handleImageClick = e => { + const handleContentClick = e => { + handleMarkdownClick(e) if (e.target.tagName === 'IMG') { const container = e.target.parentNode const imgs = [...container.querySelectorAll('img')].map(i => i.src) @@ -511,7 +512,7 @@ export default { lightboxVisible, lightboxIndex, lightboxImgs, - handleImageClick, + handleContentClick, isMobile } }