Skip to content

Commit

Permalink
feat: code copy plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
orangelckc committed Mar 20, 2023
1 parent 3380725 commit 8062b7c
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 60 deletions.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@
"@multiavatar/multiavatar": "^1.0.7",
"@tauri-apps/api": "^1.2.0",
"@types/marked": "^4.0.8",
"clipboard": "^2.0.11",
"dayjs": "^1.11.7",
"highlight.js": "^11.7.0",
"html-to-image": "^1.11.11",
"markdown-it": "^13.0.1",
"markdown-it-code-copy": "^0.1.1",
"markdown-it-highlightjs": "^4.0.1",
"pinia": "^2.0.33",
"pinia-plugin-persistedstate": "^3.1.0",
Expand All @@ -40,6 +40,7 @@
"@arco-design/web-vue": "^2.44.1",
"@commitlint/cli": "^17.4.4",
"@commitlint/config-conventional": "^17.4.4",
"@iconify-json/mdi": "^1.1.50",
"@kidonng/daisyui": "^2.51.3",
"@release-it/conventional-changelog": "^5.1.1",
"@tauri-apps/cli": "^1.2.2",
Expand Down Expand Up @@ -67,4 +68,4 @@
"vite": "^4.0.0",
"vue-tsc": "^1.0.11"
}
}
}
20 changes: 11 additions & 9 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

53 changes: 4 additions & 49 deletions src/components/Session/index.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import MarkdownIt from 'markdown-it'
import MarkdownItHighlight from 'markdown-it-highlightjs'
import MarkdownItCodeCopy from 'markdown-it-code-copy'
import CopyCode from '@/utils/copyCode'
import { IconImage } from '@arco-design/web-vue/es/icon'
import { saveImage } from '@/utils'
import { useSettingsStore, useSessionStore, useRoleStore } from '@/stores'
Expand All @@ -10,7 +10,7 @@ import utc from 'dayjs/plugin/utc'
dayjs.extend(utc)
const marked = new MarkdownIt().use(MarkdownItHighlight).use(MarkdownItCodeCopy)
const marked = new MarkdownIt().use(MarkdownItHighlight).use(CopyCode)
const { uuid } = storeToRefs(useSettingsStore())
const { sessionDataList } = storeToRefs(useSessionStore())
Expand Down Expand Up @@ -48,7 +48,7 @@ watchEffect(() => {
<Avatar class="w-12!" :value="item.is_ask ? uuid : currentRole?.name" />
<div
class="flex w-[calc(100%-8rem)] flex-col gap-2"
class="relative flex w-[calc(100%-8rem)] flex-col gap-2"
:class="item.is_ask && 'items-end'"
>
<span class="text-true-gray text-xs">{{
Expand All @@ -57,7 +57,7 @@ watchEffect(() => {
<div class="blink-block" v-if="!item.message.content"></div>
<div
class="session-item flex w-fit flex-col gap-4 rounded-md p-4"
class="session-item relative flex w-fit flex-col gap-4 rounded-md p-4"
:class="
item.is_ask
? 'bg-[rgb(var(--blue-6))] text-white'
Expand All @@ -82,51 +82,6 @@ watchEffect(() => {
--uno: absolute h-6 w-1 bg-[var(--color-text-2)] content-none;
}
::v-deep(pre) {
margin: 0;
code {
--uno: rounded-md;
}
pre {
margin: 0;
code {
--uno: rounded-md leading-6;
}
+ .markdown-it-code-copy {
width: 20px;
height: 20px;
transition: all 0.3s;
opacity: 0;
border: 0;
outline: 0;
background: url('@/assets/image/copy.svg') no-repeat center;
background-size: contain;
&:hover {
opacity: 1;
}
}
&:hover {
+ .markdown-it-code-copy {
opacity: 0.7;
}
}
}
ol,
ul {
li {
--uno: flex flex-col gap-4;
&:not(:last-child) {
--uno: pb-4;
}
}
}
}
@keyframes blink {
0% {
opacity: 1;
Expand Down
61 changes: 61 additions & 0 deletions src/utils/copyCode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// @unocss-include
import Clipboard from 'clipboard'

const PLUGIN_CLASS = 'code-copy'

new Clipboard(`.${PLUGIN_CLASS}`, {
text: (trigger: HTMLButtonElement) => {
trigger.classList.add('i-mdi-checkbox-multiple-marked-outline')
setTimeout(() => {
trigger.classList.remove('i-mdi-checkbox-multiple-marked-outline')
}, 2000)
return trigger.getAttribute('data-clipboard-text') ?? ''
}
})

interface Options {
buttonClass?: string
}

type RulesArgs = [Array<{ content: string }>, number]

const defaultOptions: Options = {
buttonClass:
'text-white h-5 absolute cursor-pointer i-mdi-checkbox-multiple-blank-outline top-6 right-2'
}

const renderCode = (
origRule: (...args: RulesArgs) => string,
options: Options
) => {
options = { ...defaultOptions, ...options }
return (...args: RulesArgs) => {
const [tokens, idx] = args
const content = tokens[idx].content
.replaceAll('"', '&quot;')
.replaceAll("'", '&lt;')
const origRendered = origRule(...args)

if (content.length === 0) {
return origRendered
}

return `
<div style="position: relative">
${origRendered}
<button class="${PLUGIN_CLASS} ${
options.buttonClass ?? ''
}" data-clipboard-text="${content}" title="Copy code">
</button>
</div>
`
}
}

export default (md: any, options: Options) => {
md.renderer.rules.code_block = renderCode(
md.renderer.rules.code_block,
options
)
md.renderer.rules.fence = renderCode(md.renderer.rules.fence, options)
}

0 comments on commit 8062b7c

Please sign in to comment.