Skip to content

Commit

Permalink
Merge pull request #22 from selemondev/docs/shiki-copy-button
Browse files Browse the repository at this point in the history
docs: add Shiki transformer - Copy button
  • Loading branch information
selemondev authored Jan 4, 2025
2 parents 1226367 + 8889b75 commit fc62dc0
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 8 deletions.
1 change: 1 addition & 0 deletions examples/vue3-marquee/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
},
"dependencies": {
"@selemondev/vue3-marquee": "0.0.8",
"hastscript": "^9.0.0",
"vue": "^3.4.15"
},
"devDependencies": {
Expand Down
5 changes: 3 additions & 2 deletions examples/vue3-marquee/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ import { installCmd, globalImportSnippet, localImportSnippet, nuxtPluginSnippet,
</div>
<div class="space-y-1">
<h3 class="font-semibold">Code</h3>
<CodeBlock :code="fadeCodeSnippet" lang="html" theme="vitesse-light" />
<CodeBlock :code="fadeCodeSnippet" lang="vue-html" theme="vitesse-light" />
</div>
<hr class="border-stone-200" />
<div class="space-y-1">
Expand Down Expand Up @@ -178,6 +178,7 @@ pre {
border-radius: 10px;
overflow: scroll;
-ms-overflow-style: none;
scrollbar-width: none;
scrollbar-width: none;
position: relative;
}
</style>
12 changes: 8 additions & 4 deletions examples/vue3-marquee/src/components/CodeBlock.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ const props = defineProps<{
const codeToHtml = ref('');
watch(props, async (val: { code: string, lang: string, theme: string}) => {
if(val) {
watch(props, async (val: { code: string, lang: string, theme: string }) => {
if (val) {
return codeToHtml.value = await convertCodeToHtml(val.code, val.lang, val.theme)
}
}, {
Expand All @@ -19,5 +19,9 @@ watch(props, async (val: { code: string, lang: string, theme: string}) => {
</script>

<template>
<div v-html="codeToHtml"/>
</template>
<div v-html="codeToHtml" />
</template>

<style>
</style>
10 changes: 9 additions & 1 deletion examples/vue3-marquee/src/lib/utils/codeToHtml.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import { codeToHtml } from "shiki"
import { transformerCopyButton } from "./transformerCopyButton"
export const convertCodeToHtml = async (code: string, lang: string, theme: string) => {
return await codeToHtml(code, {
lang,
theme
theme,
transformers: [
transformerCopyButton({
duration: 3000,
successIcon: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='rgba(128,128,128,1)' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M16 3h2.6A2.4 2.4 0 0 1 21 5.4v15.2a2.4 2.4 0 0 1-2.4 2.4H5.4A2.4 2.4 0 0 1 3 20.6V5.4A2.4 2.4 0 0 1 5.4 3H8m0 11l3 3l5-7M8.8 1h6.4a.8.8 0 0 1 .8.8v2.4a.8.8 0 0 1-.8.8H8.8a.8.8 0 0 1-.8-.8V1.8a.8.8 0 0 1 .8-.8'/%3E%3C/svg%3E",
copyIcon: "data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20fill='none'%20stroke='rgba(128,128,128,1)'%20stroke-linecap='round'%20stroke-linejoin='round'%20stroke-width='2'%20viewBox='0%200%2024%2024'%3E%3Crect%20width='8'%20height='4'%20x='8'%20y='2'%20rx='1'%20ry='1'/%3E%3Cpath%20d='M16%204h2a2%202%200%200%201%202%202v14a2%202%200%200%201-2%202H6a2%202%200%200%201-2-2V6a2%202%200%200%201%202-2h2'/%3E%3C/svg%3E",
})
]
})
}
118 changes: 118 additions & 0 deletions examples/vue3-marquee/src/lib/utils/transformerCopyButton.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Credits - Rehype pretty & JoshNuss

import type { CopyButtonOptions } from "@/types/CopyButtonOptions.interface";
import type { ShikiTransformer } from "shiki";
import { h } from "hastscript";

export const transformerCopyButton = (
options: CopyButtonOptions = {
duration: 3000
}
): ShikiTransformer => {
return {
name: 'shiki-transformer-copy-button',
code(node) {
const button = h('button', {
class: 'shiki-transformer-button-copy',
'data-code': this.source,
onclick: `
navigator.clipboard.writeText(this.dataset.code);
this.classList.add('shiki-transformer-button-copied');
setTimeout(() => this.classList.remove('shiki-transformer-button-copied'), ${options.duration})
`
}, [
h('span', { class: 'ready' }),
h('span', { class: 'success' })
]);
node.children.push(button)
node.children.push({
type: 'element',
tagName: 'style',
properties: {},
children: [
{
type: 'text',
value: buttonStyles({
successIcon: options.successIcon,
copyIcon: options.copyIcon
})
}
]
})
}
}
}

function buttonStyles({
successIcon = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='rgba(128,128,128,1)' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M16 3h2.6A2.4 2.4 0 0 1 21 5.4v15.2a2.4 2.4 0 0 1-2.4 2.4H5.4A2.4 2.4 0 0 1 3 20.6V5.4A2.4 2.4 0 0 1 5.4 3H8m0 11l3 3l5-7M8.8 1h6.4a.8.8 0 0 1 .8.8v2.4a.8.8 0 0 1-.8.8H8.8a.8.8 0 0 1-.8-.8V1.8a.8.8 0 0 1 .8-.8'/%3E%3C/svg%3E",
copyIcon = "data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20fill='none'%20stroke='rgba(128,128,128,1)'%20stroke-linecap='round'%20stroke-linejoin='round'%20stroke-width='2'%20viewBox='0%200%2024%2024'%3E%3Crect%20width='8'%20height='4'%20x='8'%20y='2'%20rx='1'%20ry='1'/%3E%3Cpath%20d='M16%204h2a2%202%200%200%201%202%202v14a2%202%200%200%201-2%202H6a2%202%200%200%201-2-2V6a2%202%200%200%201%202-2h2'/%3E%3C/svg%3E",
}: {
successIcon?: string,
copyIcon?: string
}) {
let buttonStyle =
`
:root {
--border-color: #e2e2e3;
--background-color: #f6f6f7;
--hover-background-color: #ffff
}
pre:has(code) {
position: relative;
}
pre button.shiki-transformer-button-copy {
position: absolute;
top: 12px;
right: 12px;
z-index: 3;
border: 1px solid var(--border-color);
border-radius: 4px;
width: 30px;
height: 30px;
display: flex;
justify-content: center;
place-items: center;
background-color: var(--background-color);
cursor: pointer;
background-repeat: no-repeat;
transition: var(--border-color) .25s, var(--background-color) .25s, opacity .25s;
&:hover {
background-color: var(--hover-background-color);
}
& span {
width: 100%;
aspect-ratio: 1 / 1;
background-repeat: no-repeat;
background-position: center;
background-size: cover;
}
& .ready {
width: 20px;
height: 20px;
background-image: url("${copyIcon}");
}
& .success {
display: none;
width: 20px;
height: 20px;
background-image: url("${successIcon}");
}
&.shiki-transformer-button-copied {
& .success {
display: block;
}
& .ready {
display: none;
}
}
}`
return buttonStyle
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface CopyButtonOptions {
duration?: number;
copyIcon?: string;
successIcon?: string
}
2 changes: 1 addition & 1 deletion examples/vue3-marquee/tsconfig.app.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",

"lib": ["ES2021"],
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
Expand Down
21 changes: 21 additions & 0 deletions pnpm-lock.yaml

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

0 comments on commit fc62dc0

Please sign in to comment.