|
1 | | -import { transformerCopyButton } from 'https://esm.sh/@rehype-pretty/transformers' |
2 | 1 | import { h } from 'https://esm.sh/hastscript' |
3 | 2 | import rehypeKatex from 'https://esm.sh/rehype-katex' |
4 | 3 | import rehypeMermaid from 'https://esm.sh/rehype-mermaid' |
@@ -36,6 +35,58 @@ function rehypeAddDaisyuiClass() { |
36 | 35 | } |
37 | 36 | } |
38 | 37 |
|
| 38 | +const ShikiCopyButtonPlugin = { |
| 39 | + name: 'copy-button', |
| 40 | + root(hast) { |
| 41 | + const rawCode = this.tokens.map(line => line.map(token => token.content).join('')).join('\n') |
| 42 | + |
| 43 | + const copyIconSrc = 'https://api.iconify.design/line-md/clipboard.svg' |
| 44 | + const successIconSrc = 'https://api.iconify.design/line-md/clipboard-check.svg' |
| 45 | + |
| 46 | + const buttonNode = h('div', { |
| 47 | + class: 'absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity duration-200' |
| 48 | + }, [ |
| 49 | + h('div', { |
| 50 | + class: 'tooltip tooltip-left', |
| 51 | + 'data-i18n': 'copy_button.copy', |
| 52 | + }, [ |
| 53 | + h('button', { |
| 54 | + class: 'btn btn-ghost btn-square btn-sm', |
| 55 | + onclick: `(async () => { |
| 56 | + const { getSvgIcon } = await import('/scripts/svgInliner.mjs') |
| 57 | + const tooltip = this.parentElement |
| 58 | + try { |
| 59 | + await navigator.clipboard.writeText(${JSON.stringify(rawCode)}) |
| 60 | + const successIcon = await getSvgIcon('${successIconSrc}', { class: 'w-5 h-5' }) |
| 61 | + tooltip.setAttribute('data-i18n', 'copy_button.copied') |
| 62 | + const img = this.querySelector('svg') |
| 63 | + img.replaceWith(successIcon) |
| 64 | + } catch (e) { |
| 65 | + const { showToastI18n } = await import('/scripts/toast.mjs') |
| 66 | + showToastI18n('error', 'copy_button.copy_failed', { error: e.message }) |
| 67 | + } |
| 68 | + setTimeout(async () => { |
| 69 | + tooltip.setAttribute('data-i18n', 'copy_button.copy') |
| 70 | + const img = this.querySelector('svg') |
| 71 | + img.replaceWith(await getSvgIcon('${copyIconSrc}', { class: 'w-5 h-5' })) |
| 72 | + }, 2000) |
| 73 | + })()`, |
| 74 | + }, [ |
| 75 | + h('img', { |
| 76 | + src: copyIconSrc, |
| 77 | + class: 'w-5 h-5' |
| 78 | + }) |
| 79 | + ]) |
| 80 | + ]) |
| 81 | + ]) |
| 82 | + |
| 83 | + return h('div', { class: 'group', style: 'position: relative;' }, [ |
| 84 | + hast, |
| 85 | + buttonNode |
| 86 | + ]) |
| 87 | + } |
| 88 | +} |
| 89 | + |
39 | 90 | const convertor = unified() |
40 | 91 | .use(remarkParse) |
41 | 92 | .use(remarkDisable, { disable: ['codeIndented'] }) |
@@ -68,10 +119,7 @@ ${diagram}` |
68 | 119 | light: 'github-light', |
69 | 120 | }, |
70 | 121 | transformers: [ |
71 | | - transformerCopyButton({ |
72 | | - visibility: 'always', |
73 | | - feedbackDuration: 3_000, |
74 | | - }), |
| 122 | + ShikiCopyButtonPlugin, |
75 | 123 | ], |
76 | 124 | }) |
77 | 125 | .use(rehypeKatex) |
|
0 commit comments