From 29d026b61839907f1a09429e97d0216fedb43b70 Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Fri, 28 Mar 2025 21:42:51 +0800 Subject: [PATCH 01/15] feat(plugin-copy-code): add inlineSelector option --- docs/plugins/features/copy-code.md | 12 +++++++- docs/zh/plugins/features/copy-code.md | 11 ++++++++ .../src/client/composables/useCopyCode.ts | 28 +++++++++++-------- .../src/node/copyCodePlugin.ts | 21 +++++++++----- .../plugin-copy-code/src/node/options.ts | 17 ++++++++++- 5 files changed, 69 insertions(+), 20 deletions(-) diff --git a/docs/plugins/features/copy-code.md b/docs/plugins/features/copy-code.md index dd2b139102..2257d90938 100644 --- a/docs/plugins/features/copy-code.md +++ b/docs/plugins/features/copy-code.md @@ -56,13 +56,23 @@ export default { ### ignoreSelector -- Type: `string[]` +- Type: `string[] | string` - Details: Elements selector in code blocks, used to ignore related elements when copying. For example, `['.token.comment']` will ignore nodes with the class name `.token.comment` in code blocks (which in `prismjs` refers to ignoring comments). +### inlineSelector + +- Type: `string[] | string | boolean` +- Default: `false` + + Whether to copy inline code content when double click. + + - `boolean`: Whether to copy inline code content when double click. + - `string | string[]`: The selector of inline code. + ### transform - Type: `(preElement: HTMLPreElement) => void` diff --git a/docs/zh/plugins/features/copy-code.md b/docs/zh/plugins/features/copy-code.md index b3ce3d0549..80b9140c21 100644 --- a/docs/zh/plugins/features/copy-code.md +++ b/docs/zh/plugins/features/copy-code.md @@ -63,6 +63,17 @@ export default { 例如: `['.token.comment']` 将忽略代码块中类名为 `.token.comment` 的节点 (这会在 `prismjs` 中忽略注释)。 +### inlineSelector + +- 类型:`string[] | string | boolean` +- 默认值:`false` +- 详情: + + 是否在双击时复制行内代码内容。 + + - `boolean`: 是否在双击时复制行内代码内容。 + - `string[] | string`: 选择器,表示需要复制的行内代码内容。 + ### transform - 类型:`(preElement: HTMLPreElement) => void` diff --git a/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts b/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts index 02647eedb3..a196fdb659 100644 --- a/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts +++ b/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts @@ -14,14 +14,13 @@ import '../styles/vars.css' export interface UseCopyCodeOptions { locales: CopyCodePluginLocaleConfig - selector: string[] + selector: string + ignoreSelector?: string + inlineSelector?: string /** @default 2000 */ duration: number /** @default false */ showInMobile?: boolean - /** @default [] */ - ignoreSelector?: string[] - /** * Transform pre element before copy * @@ -47,11 +46,12 @@ export interface UseCopyCodeOptions { const SHELL_RE = /language-(shellscript|shell|bash|sh|zsh)/ export const useCopyCode = ({ + selector, + ignoreSelector, + inlineSelector, duration = 2000, locales, - selector, showInMobile, - ignoreSelector = [], transform, }: UseCopyCodeOptions): void => { if (__VUEPRESS_SSR__) return @@ -83,9 +83,7 @@ export const useCopyCode = ({ document.body.classList.toggle('no-copy-code', !enabled.value) if (!enabled.value) return - document - .querySelectorAll(selector.join(',')) - .forEach(insertCopyButton) + document.querySelectorAll(selector).forEach(insertCopyButton) } watchImmediate(enabled, appendCopyButton, { @@ -106,8 +104,8 @@ export const useCopyCode = ({ ): Promise => { const clone = codeContent.cloneNode(true) as HTMLPreElement - if (ignoreSelector.length) { - clone.querySelectorAll(ignoreSelector.join(',')).forEach((node) => { + if (ignoreSelector) { + clone.querySelectorAll(ignoreSelector).forEach((node) => { node.remove() }) } @@ -148,4 +146,12 @@ export const useCopyCode = ({ void copyContent(codeContainer, preBlock, el as HTMLButtonElement) } }) + + if (inlineSelector) + useEventListener('dblclick', (event) => { + const el = event.target as HTMLElement + + if (enabled.value && el.matches(inlineSelector)) + void copy(el.textContent || '') + }) } diff --git a/plugins/features/plugin-copy-code/src/node/copyCodePlugin.ts b/plugins/features/plugin-copy-code/src/node/copyCodePlugin.ts index 871232d9c7..4cec6a3bcc 100644 --- a/plugins/features/plugin-copy-code/src/node/copyCodePlugin.ts +++ b/plugins/features/plugin-copy-code/src/node/copyCodePlugin.ts @@ -21,19 +21,26 @@ export const copyCodePlugin = name: PLUGIN_NAME, define: () => ({ - __CC_DURATION__: options.duration ?? 2000, - __CC_IGNORE_SELECTOR__: options.ignoreSelector ?? [], + __CC_SELECTOR__: isArray(options.selector) + ? options.selector.join(',') + : (options.selector ?? '[vp-content] div[class*="language-"] pre'), + __CC_IGNORE_SELECTOR__: Array.isArray(options.ignoreSelector) + ? options.ignoreSelector.join(',') + : (options.ignoreSelector ?? ''), + __CC_INLINE_SELECTOR__: Array.isArray(options.inline) + ? options.inline.join(',') + : isString(options.inline) + ? options.inline + : options.inline + ? '[vp-content] :not(pre) > code' + : '', __CC_LOCALES__: getFullLocaleConfig({ app, name: PLUGIN_NAME, default: copyCodeLocaleInfo, config: options.locales, }), - __CC_SELECTOR__: isArray(options.selector) - ? options.selector - : isString(options.selector) - ? [options.selector] - : ['[vp-content] div[class*="language-"] pre'], + __CC_DURATION__: options.duration ?? 2000, __CC_SHOW_IN_MOBILE__: options.showInMobile ?? false, }), diff --git a/plugins/features/plugin-copy-code/src/node/options.ts b/plugins/features/plugin-copy-code/src/node/options.ts index ac6577d9f8..ea6301b4cd 100644 --- a/plugins/features/plugin-copy-code/src/node/options.ts +++ b/plugins/features/plugin-copy-code/src/node/options.ts @@ -40,7 +40,7 @@ export interface CopyCodePluginOptions { * * @default [] */ - ignoreSelector?: string[] + ignoreSelector?: string[] | string /** * Locale config @@ -48,4 +48,19 @@ export interface CopyCodePluginOptions { * 国际化配置 */ locales?: LocaleConfig + + /** + * Whether to copy inline code content when double click. + * + * - boolean: Whether to copy inline code content when double click. + * - string | string[]: The selector of inline code. + * + * 是否在双击时复制内联代码内容 + * + * - boolean: 是否在双击时复制内联代码内容 + * - string | string[]: 内联代码的选择器 + * + * @default '[vp-content] :not(pre) > code' + */ + inline?: string[] | boolean | string } From 5a03e4fc8a3317ed0021085cd4cfe402283f54ce Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Fri, 28 Mar 2025 21:45:47 +0800 Subject: [PATCH 02/15] fix: fix config entry --- plugins/features/plugin-copy-code/src/client/config.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/features/plugin-copy-code/src/client/config.ts b/plugins/features/plugin-copy-code/src/client/config.ts index 5283213d78..d62f4fe9c7 100644 --- a/plugins/features/plugin-copy-code/src/client/config.ts +++ b/plugins/features/plugin-copy-code/src/client/config.ts @@ -4,8 +4,9 @@ import type { CopyCodePluginLocaleConfig } from './types.js' declare const __CC_DURATION__: number declare const __CC_LOCALES__: CopyCodePluginLocaleConfig -declare const __CC_SELECTOR__: string[] -declare const __CC_IGNORE_SELECTOR__: string[] +declare const __CC_SELECTOR__: string +declare const __CC_IGNORE_SELECTOR__: string +declare const __CC_INLINE_SELECTOR__: string declare const __CC_SHOW_IN_MOBILE__: boolean export default defineClientConfig({ @@ -13,6 +14,7 @@ export default defineClientConfig({ useCopyCode({ selector: __CC_SELECTOR__, ignoreSelector: __CC_IGNORE_SELECTOR__, + inlineSelector: __CC_INLINE_SELECTOR__, locales: __CC_LOCALES__, duration: __CC_DURATION__, showInMobile: __CC_SHOW_IN_MOBILE__, From 82100a8994d26bb2b164d059efed1cdbdee774f2 Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Fri, 28 Mar 2025 22:03:53 +0800 Subject: [PATCH 03/15] docs: update docs --- docs/zh/plugins/features/copy-code.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/zh/plugins/features/copy-code.md b/docs/zh/plugins/features/copy-code.md index 80b9140c21..bd7bfe06e0 100644 --- a/docs/zh/plugins/features/copy-code.md +++ b/docs/zh/plugins/features/copy-code.md @@ -56,7 +56,7 @@ export default { ### ignoreSelector -- 类型:`string[]` +- 类型:`string[] | string` - 详情: 代码块中的元素选择器,用于在复制时忽略相关元素。 From 49f0197d1db411b0623b0a85652d31c296b6d7cd Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Fri, 28 Mar 2025 22:28:56 +0800 Subject: [PATCH 04/15] feat(helper): add Message class to handle message --- eslint.config.js | 4 ++ tools/helper/src/client/styles/message.scss | 58 +++++++++++++++++++ tools/helper/src/client/utils/index.ts | 1 + tools/helper/src/client/utils/message.ts | 63 +++++++++++++++++++++ 4 files changed, 126 insertions(+) create mode 100644 tools/helper/src/client/styles/message.scss create mode 100644 tools/helper/src/client/utils/message.ts diff --git a/eslint.config.js b/eslint.config.js index 16e834e950..a50bd2bad5 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -99,6 +99,10 @@ export default vuepress( ], '@typescript-eslint/no-dynamic-delete': 'off', '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/no-empty-function': [ + 'error', + { allow: ['private-constructors'] }, + ], '@typescript-eslint/no-floating-promises': [ 'error', { diff --git a/tools/helper/src/client/styles/message.scss b/tools/helper/src/client/styles/message.scss new file mode 100644 index 0000000000..22ce46498e --- /dev/null +++ b/tools/helper/src/client/styles/message.scss @@ -0,0 +1,58 @@ +@keyframes message-move-in { + 0% { + opacity: 0; + transform: translateY(-100%); + } + + 100% { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes message-move-out { + 0% { + opacity: 1; + transform: translateY(0); + } + + 100% { + opacity: 0; + transform: translateY(-100%); + } +} + +#message-container { + position: fixed; + inset: calc(var(--message-offset, 3.6rem) + 1rem) 0 auto; + z-index: 75; + text-align: center; +} + +.message-item { + display: inline-block; + + padding: 8px 10px; + border-radius: 3px; + + background: var(--vp-c-bg); + color: var(--vp-c-text); + box-shadow: 0 0 10px 0 var(--vp-c-shadow); + + font-size: 14px; + + &.move-in { + animation: message-move-in 0.3s ease-in-out; + } + + &.move-out { + animation: message-move-out 0.3s ease-in-out; + animation-fill-mode: forwards; + } + + svg { + position: relative; + bottom: -0.125em; + margin-inline-end: 5px; + } +} diff --git a/tools/helper/src/client/utils/index.ts b/tools/helper/src/client/utils/index.ts index 452cc47b3f..8babf00fb9 100644 --- a/tools/helper/src/client/utils/index.ts +++ b/tools/helper/src/client/utils/index.ts @@ -5,4 +5,5 @@ export * from './getHeaders.js' export * from './isFocusingTextControl.js' export * from './isKeyMatched.js' export * from './hasGlobalComponent.js' +export * from './message.js' export * from './wait.js' diff --git a/tools/helper/src/client/utils/message.ts b/tools/helper/src/client/utils/message.ts new file mode 100644 index 0000000000..7eb3daf3d0 --- /dev/null +++ b/tools/helper/src/client/utils/message.ts @@ -0,0 +1,63 @@ +import { keys } from '../../shared/index.js' + +const containerId = 'message-container' + +export class Message { + private elements: Record = {} + + private constructor() {} + + public static get containerElement(): HTMLElement { + let containerElement = document.getElementById(containerId) + + if (containerElement) return containerElement + + containerElement = document.createElement('div') + + containerElement.id = containerId + document.body.appendChild(containerElement) + + return containerElement + } + + public pop(html: string, duration = 2000): number { + const messageElement = document.createElement('div') + const messageId = Date.now() + + messageElement.className = 'message-item move-in' + messageElement.innerHTML = html + Message.containerElement.appendChild(messageElement) + this.elements[messageId] = messageElement + + if (duration > 0) + setTimeout(() => { + this.close(messageId) + }, duration) + + return messageId + } + + public close(messageId?: number): void { + if (messageId) { + const messageElement = this.elements[messageId] + + messageElement.classList.remove('move-in') + messageElement.classList.add('move-out') + messageElement.addEventListener('animationend', () => { + messageElement.remove() + delete this.elements[messageId] + }) + } else { + keys(this.elements).forEach((id) => { + this.close(Number(id)) + }) + } + } + + public destroy(): void { + const containerElement = document.getElementById(containerId) + + if (containerElement) document.body.removeChild(containerElement) + this.elements = {} + } +} From b0d0c5400ade213b5cee9fa93a569cd5c46ad27d Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Fri, 28 Mar 2025 22:33:57 +0800 Subject: [PATCH 05/15] feat: add clickToClose --- tools/helper/src/client/utils/message.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tools/helper/src/client/utils/message.ts b/tools/helper/src/client/utils/message.ts index 7eb3daf3d0..7ccc9f36bf 100644 --- a/tools/helper/src/client/utils/message.ts +++ b/tools/helper/src/client/utils/message.ts @@ -20,7 +20,11 @@ export class Message { return containerElement } - public pop(html: string, duration = 2000): number { + public getElement(messageId: number): HTMLDivElement { + return this.elements[messageId] + } + + public pop(html: string, duration = 2000, clickToClose = true): number { const messageElement = document.createElement('div') const messageId = Date.now() @@ -29,6 +33,11 @@ export class Message { Message.containerElement.appendChild(messageElement) this.elements[messageId] = messageElement + if (clickToClose) + messageElement.addEventListener('click', () => { + this.close(messageId) + }) + if (duration > 0) setTimeout(() => { this.close(messageId) From 1cf9d9c7963da22482e1b1663649f14c6cd5fc13 Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Fri, 28 Mar 2025 22:40:48 +0800 Subject: [PATCH 06/15] chore: tweaks --- eslint.config.js | 2 +- tools/helper/package.json | 1 + tools/helper/src/client/utils/message.ts | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index a50bd2bad5..46effc413e 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -101,7 +101,7 @@ export default vuepress( '@typescript-eslint/no-non-null-assertion': 'off', '@typescript-eslint/no-empty-function': [ 'error', - { allow: ['private-constructors'] }, + { allow: ['protected-constructors'] }, ], '@typescript-eslint/no-floating-promises': [ 'error', diff --git a/tools/helper/package.json b/tools/helper/package.json index 8240e7f589..02a76f1d84 100644 --- a/tools/helper/package.json +++ b/tools/helper/package.json @@ -31,6 +31,7 @@ "./noopComponent": "./lib/client/noopComponent.js", "./noopModule": "./lib/client/noopModule.js", "./colors.css": "./lib/client/styles/colors.css", + "./message.css": "./lib/client/styles/message.css", "./normalize.css": "./lib/client/styles/normalize.css", "./sr-only.css": "./lib/client/styles/sr-only.css", "./transition/*.css": "./lib/client/styles/transition/*.css", diff --git a/tools/helper/src/client/utils/message.ts b/tools/helper/src/client/utils/message.ts index 7ccc9f36bf..6f2d17d47c 100644 --- a/tools/helper/src/client/utils/message.ts +++ b/tools/helper/src/client/utils/message.ts @@ -5,7 +5,7 @@ const containerId = 'message-container' export class Message { private elements: Record = {} - private constructor() {} + protected constructor() {} public static get containerElement(): HTMLElement { let containerElement = document.getElementById(containerId) From 8dea7cfde84e75369c390a9b4048bcf513dc573d Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Fri, 28 Mar 2025 22:43:30 +0800 Subject: [PATCH 07/15] chore: tweaks --- tools/helper/src/client/utils/message.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/helper/src/client/utils/message.ts b/tools/helper/src/client/utils/message.ts index 6f2d17d47c..3601c0e4ed 100644 --- a/tools/helper/src/client/utils/message.ts +++ b/tools/helper/src/client/utils/message.ts @@ -3,9 +3,11 @@ import { keys } from '../../shared/index.js' const containerId = 'message-container' export class Message { - private elements: Record = {} + private elements: Record - protected constructor() {} + public constructor() { + this.elements = {} + } public static get containerElement(): HTMLElement { let containerElement = document.getElementById(containerId) From a0708c62dff3f46f60dda8b5a87294f0df05e7ba Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Fri, 28 Mar 2025 22:46:55 +0800 Subject: [PATCH 08/15] feat: add hint --- docs/.vuepress/theme.ts | 3 +++ .../src/client/composables/useCopyCode.ts | 13 +++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/.vuepress/theme.ts b/docs/.vuepress/theme.ts index f6365c864e..2c3e2b8f36 100644 --- a/docs/.vuepress/theme.ts +++ b/docs/.vuepress/theme.ts @@ -59,6 +59,9 @@ export default defaultTheme({ }, themePlugins: { + copyCode: { + inline: true, + }, git: { contributors: { avatar: true, diff --git a/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts b/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts index a196fdb659..936fece202 100644 --- a/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts +++ b/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts @@ -1,4 +1,4 @@ -import { useLocaleConfig } from '@vuepress/helper/client' +import { Message, useLocaleConfig } from '@vuepress/helper/client' import { useClipboard, useEventListener, @@ -9,6 +9,7 @@ import { computed } from 'vue' import { onContentUpdated } from 'vuepress/client' import type { CopyCodePluginLocaleConfig } from '../types.js' +import '@vuepress/helper/message.css' import '../styles/copy-code.css' import '../styles/vars.css' @@ -43,6 +44,8 @@ export interface UseCopyCodeOptions { transform?: (preElement: HTMLElement) => void } +const CHECK_ICON = + '' const SHELL_RE = /language-(shellscript|shell|bash|sh|zsh)/ export const useCopyCode = ({ @@ -96,6 +99,7 @@ export const useCopyCode = ({ const { copy } = useClipboard({ legacy: true }) const timeoutIdMap = new WeakMap>() + let message: Message | null = null const copyContent = async ( codeContainer: HTMLDivElement, @@ -151,7 +155,12 @@ export const useCopyCode = ({ useEventListener('dblclick', (event) => { const el = event.target as HTMLElement - if (enabled.value && el.matches(inlineSelector)) + if (enabled.value && el.matches(inlineSelector)) { void copy(el.textContent || '') + ;(message ??= new Message()).pop( + `${CHECK_ICON}${locale.value.copied} 🎉`, + duration, + ) + } }) } From 676fb00f5a9db74b50c0f3c291f82286888bbf2c Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Fri, 28 Mar 2025 22:47:33 +0800 Subject: [PATCH 09/15] chore: tweaks --- .../plugin-copy-code/src/client/composables/useCopyCode.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts b/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts index 936fece202..e4eb920236 100644 --- a/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts +++ b/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts @@ -156,6 +156,7 @@ export const useCopyCode = ({ const el = event.target as HTMLElement if (enabled.value && el.matches(inlineSelector)) { + event.preventDefault() void copy(el.textContent || '') ;(message ??= new Message()).pop( `${CHECK_ICON}${locale.value.copied} 🎉`, From 70ad5d71c6c3c2052aa9a27557d79b1abe336262 Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Fri, 28 Mar 2025 23:19:30 +0800 Subject: [PATCH 10/15] chore: update --- .../src/client/composables/useCopyCode.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts b/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts index e4eb920236..e3024d1f4a 100644 --- a/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts +++ b/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts @@ -151,12 +151,11 @@ export const useCopyCode = ({ } }) - if (inlineSelector) + if (inlineSelector) { useEventListener('dblclick', (event) => { const el = event.target as HTMLElement if (enabled.value && el.matches(inlineSelector)) { - event.preventDefault() void copy(el.textContent || '') ;(message ??= new Message()).pop( `${CHECK_ICON}${locale.value.copied} 🎉`, @@ -164,4 +163,17 @@ export const useCopyCode = ({ ) } }) + useEventListener('selectstart', (event) => { + const el = event.target as HTMLElement | Text + + if ( + enabled.value && + (el instanceof HTMLElement + ? el.matches(inlineSelector) + : el.parentElement?.matches(inlineSelector)) + ) { + event.preventDefault() + } + }) + } } From c5979a3702afb3cdf78dd7b0ad7a31463adcbe1a Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Fri, 28 Mar 2025 23:19:56 +0800 Subject: [PATCH 11/15] feat: update style --- tools/helper/src/client/styles/message.scss | 22 +++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/tools/helper/src/client/styles/message.scss b/tools/helper/src/client/styles/message.scss index 22ce46498e..88e0094fa3 100644 --- a/tools/helper/src/client/styles/message.scss +++ b/tools/helper/src/client/styles/message.scss @@ -1,7 +1,13 @@ +:root { + --message-offset: calc(var(--vp-header-offset, 3.6rem) + 1rem); + --message-timing-duration: 0.3s; + --message-timing-function: ease-in-out; +} + @keyframes message-move-in { 0% { opacity: 0; - transform: translateY(-100%); + transform: translateY(-10px); } 100% { @@ -24,8 +30,14 @@ #message-container { position: fixed; - inset: calc(var(--message-offset, 3.6rem) + 1rem) 0 auto; + inset: var(--message-offset) 0 auto; z-index: 75; + + display: flex; + flex-flow: column; + gap: 0.5rem; + align-items: center; + text-align: center; } @@ -42,11 +54,13 @@ font-size: 14px; &.move-in { - animation: message-move-in 0.3s ease-in-out; + animation: message-move-in var(--message-timing-duration) + var(--message-timing-function); } &.move-out { - animation: message-move-out 0.3s ease-in-out; + animation: message-move-out var(--message-timing-duration) + var(--message-timing-function); animation-fill-mode: forwards; } From 005d83fdd0015729ebebc42b45f6fb6d4df9ea25 Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Fri, 28 Mar 2025 23:20:24 +0800 Subject: [PATCH 12/15] feat: update style --- tools/helper/src/client/styles/message.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/helper/src/client/styles/message.scss b/tools/helper/src/client/styles/message.scss index 88e0094fa3..7e44a34c5f 100644 --- a/tools/helper/src/client/styles/message.scss +++ b/tools/helper/src/client/styles/message.scss @@ -2,6 +2,7 @@ --message-offset: calc(var(--vp-header-offset, 3.6rem) + 1rem); --message-timing-duration: 0.3s; --message-timing-function: ease-in-out; + --message-gap: 0.5rem; } @keyframes message-move-in { @@ -35,7 +36,7 @@ display: flex; flex-flow: column; - gap: 0.5rem; + gap: var(--message-gap); align-items: center; text-align: center; From 2c5eb21a2dae07489ffbba3880101600752d2080 Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Fri, 28 Mar 2025 23:21:08 +0800 Subject: [PATCH 13/15] chore: tweaks --- .../plugin-copy-code/src/client/composables/useCopyCode.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts b/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts index e3024d1f4a..e5c81554ab 100644 --- a/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts +++ b/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts @@ -158,7 +158,7 @@ export const useCopyCode = ({ if (enabled.value && el.matches(inlineSelector)) { void copy(el.textContent || '') ;(message ??= new Message()).pop( - `${CHECK_ICON}${locale.value.copied} 🎉`, + `${CHECK_ICON}${locale.value.copied} `, duration, ) } From 380b4c1fe12b7077b60c43ead7796fd359110046 Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Sat, 29 Mar 2025 00:31:30 +0800 Subject: [PATCH 14/15] chore: tweaks --- plugins/features/plugin-copy-code/rollup.config.ts | 2 +- .../src/client/composables/useCopyCode.ts | 14 ++------------ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/plugins/features/plugin-copy-code/rollup.config.ts b/plugins/features/plugin-copy-code/rollup.config.ts index 4a44353832..c168c8ced6 100644 --- a/plugins/features/plugin-copy-code/rollup.config.ts +++ b/plugins/features/plugin-copy-code/rollup.config.ts @@ -10,7 +10,7 @@ export default [ files: ['config', 'index'], }, { - dtsExternal: ['@vuepress/helper/shared'], + dtsExternal: ['@vuepress/helper/client', '@vuepress/helper/shared'], }, ), ] diff --git a/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts b/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts index e5c81554ab..0a3294bb24 100644 --- a/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts +++ b/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts @@ -3,6 +3,7 @@ import { useClipboard, useEventListener, useMediaQuery, + useStyleTag, watchImmediate, } from '@vueuse/core' import { computed } from 'vue' @@ -163,17 +164,6 @@ export const useCopyCode = ({ ) } }) - useEventListener('selectstart', (event) => { - const el = event.target as HTMLElement | Text - - if ( - enabled.value && - (el instanceof HTMLElement - ? el.matches(inlineSelector) - : el.parentElement?.matches(inlineSelector)) - ) { - event.preventDefault() - } - }) + useStyleTag(`${inlineSelector}{cursor:copy;user-select:none;}`) } } From 918663cb6799d95e9108612432b8f76c0683eb09 Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Sat, 29 Mar 2025 01:03:18 +0800 Subject: [PATCH 15/15] feat: use selection api --- .../src/client/composables/useCopyCode.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts b/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts index 0a3294bb24..22e817c58e 100644 --- a/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts +++ b/plugins/features/plugin-copy-code/src/client/composables/useCopyCode.ts @@ -3,7 +3,6 @@ import { useClipboard, useEventListener, useMediaQuery, - useStyleTag, watchImmediate, } from '@vueuse/core' import { computed } from 'vue' @@ -157,6 +156,16 @@ export const useCopyCode = ({ const el = event.target as HTMLElement if (enabled.value && el.matches(inlineSelector)) { + const selection = window.getSelection() + + if ( + selection && + (el.contains(selection.anchorNode) || + el.contains(selection.focusNode)) + ) { + selection.removeAllRanges() + } + void copy(el.textContent || '') ;(message ??= new Message()).pop( `${CHECK_ICON}${locale.value.copied} `, @@ -164,6 +173,5 @@ export const useCopyCode = ({ ) } }) - useStyleTag(`${inlineSelector}{cursor:copy;user-select:none;}`) } }