Browse Source

Add code copy button

master
Riyyi 3 weeks ago
parent
commit
c26c74a2fb
  1. 5
      src/assets/css/style.css
  2. 77
      src/components/content/ProsePre.vue
  3. 4
      src/pages/articles/[slug].vue

5
src/assets/css/style.css

@ -1,6 +1,11 @@
/*----------------------------------------*/ /*----------------------------------------*/
/* General */ /* General */
:root {
--w-code-background-color: #818b981f;
--w-pre-background-color: #f6f8fa;
}
html { html {
font-family: "Segoe UI", "DejaVu Sans", sans-serif; font-family: "Segoe UI", "DejaVu Sans", sans-serif;
scroll-behavior: smooth; scroll-behavior: smooth;

77
src/components/content/ProsePre.vue

@ -0,0 +1,77 @@
<template>
<div class="position-relative">
<div @click="copyCode" class="position-absolute" style="top: 10px; right: 10px;">
<template v-if="copied">
<code>Copied!</code>
</template>
<button class="copy text-secondary" title="Copy code">
<IFaClone />
</button>
</div>
<pre :class="$props.class"><code class="language-{{ language }}"><slot /></code></pre>
</div>
</template>
<style scoped>
pre code .line {
display: block;
}
button.copy {
background: 0;
border: 0;
z-index: 10;
}
button.copy:hover {
background-color: var(--w-code-background-color);
border-radius: var(--bs-border-radius);
}
</style>
<script setup lang="ts">
import { ref } from "vue"
defineProps({
code: {
type: String,
default: ""
},
language: {
type: String,
default: null
},
filename: {
type: String,
default: null
},
highlights: {
type: Array as () => number[],
default: () => []
},
meta: {
type: String,
default: null
},
class: {
type: String,
default: null
}
});
const copied = ref(false);
const copyCode = async (e: Event) => {
try {
const target = e.currentTarget as HTMLElement;
const element = target.nextElementSibling as HTMLPreElement | null;
const textToCopy = element ? element.textContent.trim() : "";
await navigator.clipboard.writeText(textToCopy);
copied.value = true;
setTimeout(() => (copied.value = false), 2000);
} catch (err) {
console.error('Failed to copy:', err)
}
}
</script>

4
src/pages/articles/[slug].vue

@ -27,7 +27,7 @@
justify-content: space-between; justify-content: space-between;
margin-bottom: 1rem; margin-bottom: 1rem;
padding: 1rem; padding: 1rem;
background-color: #f6f8fa; background-color: var(--w-pre-background-color);
} }
/* Select <code> and <code class="shiki"> */ /* Select <code> and <code class="shiki"> */
@ -36,7 +36,7 @@
margin: 0; margin: 0;
font-size: 85%; font-size: 85%;
white-space: break-spaces; white-space: break-spaces;
background-color: #818b981f; background-color: var(--w-code-background-color);
border-radius: 6px; border-radius: 6px;
} }

Loading…
Cancel
Save