diff --git a/.eleventy.js b/.eleventy.js
index 4851d2a..f35eec6 100644
--- a/.eleventy.js
+++ b/.eleventy.js
@@ -32,8 +32,8 @@ async function do_minifyhtml(source, output_path) {
module.exports = function(eleventyConfig) {
// Plugins
- eleventyConfig.addPlugin(require("./src/libs/shiki.js"), { });
- eleventyConfig.addPlugin(require("./src/libs/mermaid.js"), { mermaid_config: {'startOnLoad': false, 'theme': 'default' }});
+ eleventyConfig.addPlugin(require("./src/libs/shiki.js"));
+ eleventyConfig.addPlugin(require("./src/libs/mermaid.js"));
// To enable merging of tags
eleventyConfig.setDataDeepMerge(true)
diff --git a/package.json b/package.json
index c78fa23..21a84af 100644
--- a/package.json
+++ b/package.json
@@ -26,6 +26,7 @@
"markdown-it": "14.1.0",
"markdown-it-anchor": "9.0.1",
"shiki": "1.10.3",
+ "@shikijs/transformers": "1.10.3",
"tailwindcss": "3.4.4",
"html-minifier-terser": "7.2.0",
"terser": "5.31.2"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f0fb760..8d16bd3 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -8,6 +8,9 @@ importers:
.:
dependencies:
+ '@shikijs/transformers':
+ specifier: 1.10.3
+ version: 1.10.3
'@tailwindcss/typography':
specifier: 0.5.13
version: 0.5.13(tailwindcss@3.4.4)
@@ -171,6 +174,9 @@ packages:
'@shikijs/core@1.10.3':
resolution: {integrity: sha512-D45PMaBaeDHxww+EkcDQtDAtzv00Gcsp72ukBtaLSmqRvh0WgGMq3Al0rl1QQBZfuneO75NXMIzEZGFitThWbg==}
+ '@shikijs/transformers@1.10.3':
+ resolution: {integrity: sha512-MNjsyye2WHVdxfZUSr5frS97sLGe6G1T+1P41QjyBFJehZphMcr4aBlRLmq6OSPBslYe9byQPVvt/LJCOfxw8Q==}
+
'@sindresorhus/slugify@1.1.2':
resolution: {integrity: sha512-V9nR/W0Xd9TSGXpZ4iFUcFGhuOJtZX82Fzxj1YISlbSgKvIiNa7eLEZrT0vAraPOt++KHauIVNYgGRgjc13dXA==}
engines: {node: '>=10'}
@@ -1663,6 +1669,10 @@ snapshots:
dependencies:
'@types/hast': 3.0.4
+ '@shikijs/transformers@1.10.3':
+ dependencies:
+ shiki: 1.10.3
+
'@sindresorhus/slugify@1.1.2':
dependencies:
'@sindresorhus/transliterate': 0.1.2
diff --git a/src/assets/styles/index.css b/src/assets/styles/index.css
index aa5d9cb..dbc93ed 100644
--- a/src/assets/styles/index.css
+++ b/src/assets/styles/index.css
@@ -21,6 +21,19 @@ html.dark .shiki span {
text-decoration: var(--shiki-dark-text-decoration) !important;
}
+/* Shiki code line diffs */
+.shiki span.line.diff.add,
+.shiki span.line.diff.add span
+{
+ background-color: rgba(16, 185, 129, 0.14) !important
+}
+
+.shiki span.line.diff.remove,
+.shiki span.line.diff.remove span
+{
+ background-color: rgba(244, 63, 94, 0.14) !important
+}
+
/* Copy code buttons on code blocks */
/* https://junges.dev/2-how-to-add-github-copy-to-clipboard-button-on-your-docsblog */
pre[class*="shiki"] {
@@ -30,9 +43,6 @@ pre[class*="shiki"] {
}
pre > .button-copy-code {
- @apply rounded;
- @apply bg-gray-300;
- @apply py-2 px-2;
position: absolute;
top: 32px;
right: 16px;
@@ -43,21 +53,3 @@ pre > .button-copy-code {
display: none;
}
}
-
-pre > .button-copy-code:hover {
- @apply border-2 border-gray-600;
- @apply bg-gray-200;
-}
-
-pre > .button-copy-code:focus {
- @apply bg-gray-300;
-}
-
-.copy-docs-icon {
- fill: #0a001f;
-}
-
-.docs-copied-icon {
- color: #148a25 !important;
- fill: #148a25 !important;
-}
diff --git a/src/libs/mermaid.js b/src/libs/mermaid.js
index e1c8a21..bfd6be0 100644
--- a/src/libs/mermaid.js
+++ b/src/libs/mermaid.js
@@ -1,5 +1,9 @@
module.exports = (eleventyConfig, options) => {
- let mermaid_config = {...options?.mermaid_config || {}, ...{loadOnSave: true}};
+ let mermaid_config = {
+ startOnLoad: false,
+ theme: 'default',
+ loadOnSave: true
+ };
let src = options?.mermaid_js_src || "https://unpkg.com/mermaid/dist/mermaid.esm.min.mjs";
eleventyConfig.addLiquidShortcode("mermaid_js_scripts", () => {
diff --git a/src/libs/shiki.js b/src/libs/shiki.js
index b3eeb16..725e55d 100644
--- a/src/libs/shiki.js
+++ b/src/libs/shiki.js
@@ -8,6 +8,7 @@ module.exports = (eleventyConfig, options) => {
eleventyConfig.on('eleventy.before', async () => {
const shiki = await import('shiki');
const fs = await import('fs');
+ const { transformerNotationDiff } = await import('@shikijs/transformers');
// Load the theme object from a file, a network request, or anywhere
const darkColourTheme = JSON.parse(fs.readFileSync('src/libs/nomos-black-colour-theme.json', 'utf8'))
@@ -23,7 +24,9 @@ module.exports = (eleventyConfig, options) => {
'sql',
'csharp',
'fsharp',
- 'xml'
+ 'xml',
+ 'javascript',
+ 'css'
],
});
@@ -43,7 +46,8 @@ module.exports = (eleventyConfig, options) => {
themes: {
light: "light-plus",
dark: "NomosBlack"
- }
+ },
+ transformers: [ transformerNotationDiff() ]
});
}
}
diff --git a/src/posts/2024/07/shiki-and-mermaid-in-11ty.md b/src/posts/2024/07/shiki-and-mermaid-in-11ty.md
new file mode 100644
index 0000000..64c856b
--- /dev/null
+++ b/src/posts/2024/07/shiki-and-mermaid-in-11ty.md
@@ -0,0 +1,326 @@
+---
+title: Shiki and Mermaid in 11ty
+date: 2024-07-16
+published: true
+enableMermaid: true
+tags:
+- in english
+- 11ty
+- eleventy
+- shiki
+- mermaid
+---
+See how to integrate Shiki to your 11ty project, with support for Mermaid diagrams.
+
+[Ler em português](../shiki-e-mermaid-no-11ty)
+
+## 11ty, Shiki and Mermaid
+
+[11ty](https://www.11ty.dev/) (Eleventy) is a static-site generator tool that uses templating engines to generate websites. Eleventy has a pipeline execution that makes it easy to add plugins and transformations to HTML, JS and CSS files.
+
+[Shiki](https://shiki.style/) is a npm package that renders HTML code blocks with syntax highlighting for a specified language. Among its interesting features, it allows dual theming (light and dark switching), code focusing and diffing notations, and custom themes.
+
+While I was making this blog, which uses 11ty, I wanted to have support for [Mermaid](https://mermaid.js.org) diagrams in my blog posts, alongside code blocks. In this post, let's see how to integrate Shiki and Mermaid into your 11ty project.
+
+* [Shiki integration](#shiki-integration)
+* [Mermaid integration](#mermaid-integration)
+* [Final results](#final-results)
+* [Copy code buttons](#copy-code-buttons)
+
+## Shiki integration
+
+### Add Shiki to your project
+
+```sh [npm]
+npm install -D shiki
+```
+
+### Shiki custom plug-in
+
+Create a Javascript file to configure Shiki for your project, for example, at `src/libs/shiki.js`:
+
+```javascript
+module.exports = (eleventyConfig, options) => {
+ // empty call to notify 11ty that we use this feature
+ // eslint-disable-next-line no-empty-function
+ eleventyConfig.amendLibrary('md', () => { });
+
+ eleventyConfig.on('eleventy.before', async () => {
+ const shiki = await import('shiki');
+
+ // highlighter config
+ const highlighter = await shiki.createHighlighter(
+ {
+ themes: ["light-plus", "dark-plus"],
+ langs: [
+ 'shell', 'html', 'yaml',
+ 'sql', 'xml', 'javascript'
+ ]
+ });
+
+ eleventyConfig.amendLibrary('md', (mdLib) =>
+ mdLib.set({
+ highlight: (code, lang) => {
+ return highlighter.codeToHtml(code,
+ {
+ lang: lang,
+ themes: {
+ light: "light-plus",
+ dark: "dark-plus"
+ }
+ });
+ }
+ })
+ );
+ });
+};
+```
+
+### Call Shiki custom plug-in in `.eleventy.js`
+
+```javascript
+module.exports = function(eleventyConfig) {
+ ...
+
+ // IMPORTANT!
+ // remove 11ty syntax highlighter plugin, if present:
+ eleventyConfig.addPlugin(syntaxHighlight) // [!code --]
+
+ // Add:
+ eleventyConfig.addPlugin(require("./src/libs/shiki.js")); // [!code ++]
+
+ ...
+}
+```
+
+## Mermaid integration
+
+If you want to use Shiki and also want to render Mermaid diagrams, you need to change the Shiki plug-in to render Mermaid as HTML divs, instead of code blocks.
+
+### Add htmlencode to your project
+
+```sh [npm]
+npm install -D htmlencode
+```
+
+### Modify Shiki plug-in
+
+```javascript
+const htmlencode = require('htmlencode'); // [!code ++]
+
+module.exports = (eleventyConfig, options) => {
+ // empty call to notify 11ty that we use this feature
+ // eslint-disable-next-line no-empty-function
+ eleventyConfig.amendLibrary('md', () => { });
+
+ eleventyConfig.on('eleventy.before', async () => {
+ const shiki = await import('shiki');
+
+ // highlighter config
+ const highlighter = await shiki.createHighlighter(
+ {
+ themes: ["light-plus", "dark-plus"],
+ langs: [
+ 'shell', 'html', 'yaml',
+ 'sql', 'xml', 'javascript'
+ ]
+ });
+
+ eleventyConfig.amendLibrary('md', (mdLib) =>
+ mdLib.set({
+ highlight: (code, lang) => {
+ if (lang === "mermaid") { // [!code ++]
+ const extra_classes = options?.extra_classes ? ' ' + options.extra_classes : ''; // [!code ++]
+ return `
`; // [!code ++]
+ } // [!code ++]
+ else { // [!code ++]
+ return highlighter.codeToHtml(code,
+ {
+ lang: lang,
+ themes: {
+ light: "light-plus",
+ dark: "dark-plus"
+ }
+ });
+ } // [!code ++]
+ }
+ })
+ );
+ });
+};
+```
+
+### Mermaid custom plug-in
+
+Create a Javascript file to configure Mermaid for your project, for example, at `src/libs/mermaid.js`:
+
+```javascript
+module.exports = (eleventyConfig, options) => {
+ let mermaid_config = {
+ startOnLoad: false,
+ theme: "default",
+ loadOnSave: true
+ };
+ let src = options?.mermaid_js_src || "https://unpkg.com/mermaid/dist/mermaid.esm.min.mjs";
+
+ eleventyConfig.addShortcode("mermaid_js_scripts", () => {
+ return ``
+ });
+ return {}
+};
+```
+### Call Mermaid custom plug-in in `.eleventy.js`
+```javascript
+module.exports = function(eleventyConfig) {
+ ...
+
+ eleventyConfig.addPlugin(require("./src/libs/shiki.js"));
+ eleventyConfig.addPlugin(require("./src/libs/mermaid.js")); // [!code ++]
+
+ ...
+}
+```
+
+### Include Mermaid script in pages
+
+Add the `mermaid_js_scripts` shortcode to pages and templates that will have diagrams:
+
+```
+{{ '{%' }} mermaid_js_scripts {{ '%}' }}
+```
+
+## Final results
+
+Let's see it working.
+
+SQL code block with syntax highlight:
+
+```sql
+SELECT * INTO dbo.NewProducts
+FROM Production.Product
+WHERE ListPrice > 25
+AND ListPrice < 100;
+```
+
+Mermaid flowchart:
+
+```
+flowchart TD
+ A[Christmas] -->|Get money| B(Go shopping)
+ B --> C{Let me think}
+ C -->|One| D[Laptop]
+ C -->|Two| E[iPhone]
+ C -->|Three| F[fa:fa-car Car]
+```
+
+```mermaid
+flowchart TD
+ A[Christmas] -->|Get money| B(Go shopping)
+ B --> C{Let me think}
+ C -->|One| D[Laptop]
+ C -->|Two| E[iPhone]
+ C -->|Three| F[fa:fa-car Car]
+```
+
+If you want to check a full source code example for this blog project, visit our [GitHub repo](https://github.com/alexandrehtrb/alexandrehtrb.github.io). It includes switching between light and dark themes for both Shiki and Mermaid.
+
+## Copy code buttons
+
+Shiki doesn't provide copy code buttons by default, but we can add them through a client-side script. I use a slightly modified version of the script shown in [this blog post](https://junges.dev/2-how-to-add-github-copy-to-clipboard-button-on-your-docsblog).
+
+### Reference the script in templates
+
+Add the `
+```
+
+### Add script to insert copy code buttons
+
+Create a Javascript file for a client-side script that will find all code blocks and add copy code buttons to them. The icons SVGs are declared inline in this example.
+
+For example, create your file at `src/scripts/addCopyCodeButtons.js`:
+
+```javascript
+let blocks = document.querySelectorAll("pre.shiki");
+const copyCodeIconSvg = '';
+const codeCopiedIconSvg = '';
+
+blocks.forEach((block) => {
+ if (!navigator.clipboard) {
+ return;
+ }
+
+ let button = document.createElement("button");
+ button.className = "button-copy-code";
+ button.ariaLabel = button.title = "Copy";
+ button.innerHTML = copyCodeIconSvg;
+ block.appendChild(button);
+
+ button.addEventListener("click", async () => {
+ await copyCode(button, block);
+ });
+});
+
+async function copyCode(button, block) {
+ let copiedCode = block.cloneNode(true);
+ copiedCode.removeChild(copiedCode.querySelector("button.button-copy-code"));
+
+ const html = copiedCode.outerHTML.replace(/<[^>]*>?/gm, "");
+
+ block.querySelector("button.button-copy-code").innerHTML = codeCopiedIconSvg;
+ button.ariaLabel = button.title = "Copied!";
+ setTimeout(function () {
+ block.querySelector("button.button-copy-code").innerHTML = copyCodeIconSvg;
+ button.ariaLabel = button.title = "Copy";
+ }, 2000);
+
+ const parsedHTML = htmlDecode(html);
+
+ await navigator.clipboard.writeText(parsedHTML);
+}
+
+function htmlDecode(input) {
+ const doc = new DOMParser().parseFromString(input, "text/html");
+ return doc.documentElement.textContent;
+}
+```
+
+### CSS for copy code buttons
+
+Add the CSS code below to style the button position and visibility:
+
+```css
+pre[class*="shiki"] {
+ position: relative;
+ margin: 5px 0;
+ padding: 1.75rem 0 1.75rem 1rem;
+}
+
+pre > .button-copy-code {
+ position: absolute;
+ top: 32px;
+ right: 16px;
+}
+
+@media only screen and (max-device-width: 480px) {
+ pre > .button-copy-code {
+ display: none;
+ }
+}
+```
+
+## References
+
+* [11ty docs](https://www.11ty.dev/docs/)
+* [Shiki docs](https://shiki.style/guide/)
+* [Mermaid docs](https://mermaid.js.org/intro/)
+* [Junges.dev - How to add GitHub Copy to Clipboard button on your docs/blog ](https://junges.dev/2-how-to-add-github-copy-to-clipboard-button-on-your-docsblog)
diff --git a/src/posts/2024/07/shiki-e-mermaid-no-11ty.md b/src/posts/2024/07/shiki-e-mermaid-no-11ty.md
new file mode 100644
index 0000000..1acf460
--- /dev/null
+++ b/src/posts/2024/07/shiki-e-mermaid-no-11ty.md
@@ -0,0 +1,326 @@
+---
+title: Shiki e Mermaid no 11ty
+date: 2024-07-16
+published: true
+enableMermaid: true
+tags:
+- em português
+- 11ty
+- eleventy
+- shiki
+- mermaid
+---
+Veja como integrar o Shiki no seu projeto 11ty, com suporte a diagramas Mermaid.
+
+[Read in english](../shiki-and-mermaid-in-11ty)
+
+## 11ty, Shiki e Mermaid
+
+O [11ty](https://www.11ty.dev/) (Eleventy) é um gerador de sites estáticos que usa templates. Ele tem uma pipeline de execução que torna fácil adicionar plug-ins e transformações a arquivos HTML, JS e CSS.
+
+O [Shiki](https://shiki.style/) é um pacote npm que renderiza blocos de código HTML com coloração de sintaxe de acordo com a linguagem do código. Dentre suas vantagens, ele permite temas duais (que alternam entre claro e escuro), notações de código com diferenças e foco, e temas customizados.
+
+Neste post, vamos ver como integrar o Shiki e diagramas [Mermaid](https://mermaid.js.org) em um projeto 11ty.
+
+* [Integrando com o Shiki](#integrando-com-o-shiki)
+* [Integrando com o Mermaid](#integrando-com-o-mermaid)
+* [Resultados finais](#resultados-finais)
+* [Botões para copiar código](#botões-para-copiar-código)
+
+## Integrando com o Shiki
+
+### Adicionar o Shiki ao projeto
+
+```sh [npm]
+npm install -D shiki
+```
+
+### Plug-in para o Shiki
+
+Criar um arquivo Javascript para configuração do Shiki no seu projeto, por exemplo, no caminho `src/libs/shiki.js`:
+
+```javascript
+module.exports = (eleventyConfig, options) => {
+ // empty call to notify 11ty that we use this feature
+ // eslint-disable-next-line no-empty-function
+ eleventyConfig.amendLibrary('md', () => { });
+
+ eleventyConfig.on('eleventy.before', async () => {
+ const shiki = await import('shiki');
+
+ // highlighter config
+ const highlighter = await shiki.createHighlighter(
+ {
+ themes: ["light-plus", "dark-plus"],
+ langs: [
+ 'shell', 'html', 'yaml',
+ 'sql', 'xml', 'javascript'
+ ]
+ });
+
+ eleventyConfig.amendLibrary('md', (mdLib) =>
+ mdLib.set({
+ highlight: (code, lang) => {
+ return highlighter.codeToHtml(code,
+ {
+ lang: lang,
+ themes: {
+ light: "light-plus",
+ dark: "dark-plus"
+ }
+ });
+ }
+ })
+ );
+ });
+};
+```
+
+### Chamar plug-in do Shiki no `.eleventy.js`
+
+```javascript
+module.exports = function(eleventyConfig) {
+ ...
+
+ // IMPORTANTE!
+ // remover plug-in de syntax highlight padrão do 11ty
+ eleventyConfig.addPlugin(syntaxHighlight) // [!code --]
+
+ // Adicionar:
+ eleventyConfig.addPlugin(require("./src/libs/shiki.js")); // [!code ++]
+
+ ...
+}
+```
+
+## Integrando com o Mermaid
+
+Se você quer usar o Shiki e também quer ter diagramas Mermaid, é necessário alterar o plug-in do Shiki para renderizar blocos Mermaid como divs HTML, ao invés de blocos de código.
+
+### Adicionar o htmlencode ao projeto
+
+```sh [npm]
+npm install -D htmlencode
+```
+
+### Modificar o plug-in do Shiki
+
+```javascript
+const htmlencode = require('htmlencode'); // [!code ++]
+
+module.exports = (eleventyConfig, options) => {
+ // empty call to notify 11ty that we use this feature
+ // eslint-disable-next-line no-empty-function
+ eleventyConfig.amendLibrary('md', () => { });
+
+ eleventyConfig.on('eleventy.before', async () => {
+ const shiki = await import('shiki');
+
+ // highlighter config
+ const highlighter = await shiki.createHighlighter(
+ {
+ themes: ["light-plus", "dark-plus"],
+ langs: [
+ 'shell', 'html', 'yaml',
+ 'sql', 'xml', 'javascript'
+ ]
+ });
+
+ eleventyConfig.amendLibrary('md', (mdLib) =>
+ mdLib.set({
+ highlight: (code, lang) => {
+ if (lang === "mermaid") { // [!code ++]
+ const extra_classes = options?.extra_classes ? ' ' + options.extra_classes : ''; // [!code ++]
+ return ``; // [!code ++]
+ } // [!code ++]
+ else { // [!code ++]
+ return highlighter.codeToHtml(code,
+ {
+ lang: lang,
+ themes: {
+ light: "light-plus",
+ dark: "dark-plus"
+ }
+ });
+ } // [!code ++]
+ }
+ })
+ );
+ });
+};
+```
+
+### Plug-in para o Mermaid
+
+Criar um arquivo Javascript para configuração do Mermaid no seu projeto, por exemplo, no caminho `src/libs/mermaid.js`:
+
+```javascript
+module.exports = (eleventyConfig, options) => {
+ let mermaid_config = {
+ startOnLoad: false,
+ theme: "default",
+ loadOnSave: true
+ };
+ let src = options?.mermaid_js_src || "https://unpkg.com/mermaid/dist/mermaid.esm.min.mjs";
+
+ eleventyConfig.addShortcode("mermaid_js_scripts", () => {
+ return ``
+ });
+ return {}
+};
+```
+### Chamar plug-in do Mermaid no `.eleventy.js`
+```javascript
+module.exports = function(eleventyConfig) {
+ ...
+
+ eleventyConfig.addPlugin(require("./src/libs/shiki.js"));
+ eleventyConfig.addPlugin(require("./src/libs/mermaid.js")); // [!code ++]
+
+ ...
+}
+```
+
+### Incluir script do Mermaid nas páginas
+
+Adicionar o shortcode `mermaid_js_scripts` nas páginas e templates que vão ter diagramas:
+
+```
+{{ '{%' }} mermaid_js_scripts {{ '%}' }}
+```
+
+## Resultados finais
+
+Vamos ver funcionando.
+
+Bloco de código SQL com coloração de sintaxe:
+
+```sql
+SELECT * INTO dbo.NewProducts
+FROM Production.Product
+WHERE ListPrice > 25
+AND ListPrice < 100;
+```
+
+Diagrama de fluxo do Mermaid:
+
+```
+flowchart TD
+ A[Christmas] -->|Get money| B(Go shopping)
+ B --> C{Let me think}
+ C -->|One| D[Laptop]
+ C -->|Two| E[iPhone]
+ C -->|Three| F[fa:fa-car Car]
+```
+
+```mermaid
+flowchart TD
+ A[Christmas] -->|Get money| B(Go shopping)
+ B --> C{Let me think}
+ C -->|One| D[Laptop]
+ C -->|Two| E[iPhone]
+ C -->|Three| F[fa:fa-car Car]
+```
+
+Se quiser conferir um exemplo completo de código para este blog, visite nosso [repo no GitHub](https://github.com/alexandrehtrb/alexandrehtrb.github.io). Lá você encontra como alternar entre tema claro e escuro, tanto para o Shiki como para o Mermaid.
+
+## Botões para copiar código
+
+O Shiki não provê de fábrica botões para copiar código, porém, podemos adicioná-los através de um script do lado do cliente. Aqui, usaremos uma versão modificada do exemplo [deste post de blog](https://junges.dev/2-how-to-add-github-copy-to-clipboard-button-on-your-docsblog).
+
+### Referenciar o script nos templates
+
+Adicionar a tag `
+```
+
+### Script para adicionar botões de copiar código
+
+Vamos criar um script de lado de cliente que vai pegar todos os blocos de código e inserir botões de copiar neles. Os ícones estão declarados em linha neste exemplo, como SVGs.
+
+Exemplo de arquivo no caminho `src/scripts/addCopyCodeButtons.js`:
+
+```javascript
+let blocks = document.querySelectorAll("pre.shiki");
+const copyCodeIconSvg = '';
+const codeCopiedIconSvg = '';
+
+blocks.forEach((block) => {
+ if (!navigator.clipboard) {
+ return;
+ }
+
+ let button = document.createElement("button");
+ button.className = "button-copy-code";
+ button.ariaLabel = button.title = "Copiar";
+ button.innerHTML = copyCodeIconSvg;
+ block.appendChild(button);
+
+ button.addEventListener("click", async () => {
+ await copyCode(button, block);
+ });
+});
+
+async function copyCode(button, block) {
+ let copiedCode = block.cloneNode(true);
+ copiedCode.removeChild(copiedCode.querySelector("button.button-copy-code"));
+
+ const html = copiedCode.outerHTML.replace(/<[^>]*>?/gm, "");
+
+ block.querySelector("button.button-copy-code").innerHTML = codeCopiedIconSvg;
+ button.ariaLabel = button.title = "Copiado!";
+ setTimeout(function () {
+ block.querySelector("button.button-copy-code").innerHTML = copyCodeIconSvg;
+ button.ariaLabel = button.title = "Copiar";
+ }, 2000);
+
+ const parsedHTML = htmlDecode(html);
+
+ await navigator.clipboard.writeText(parsedHTML);
+}
+
+function htmlDecode(input) {
+ const doc = new DOMParser().parseFromString(input, "text/html");
+ return doc.documentElement.textContent;
+}
+```
+
+### CSS para os botões de copiar código
+
+O CSS abaixo controla a posição e visibilidade dos botões:
+
+```css
+pre[class*="shiki"] {
+ position: relative;
+ margin: 5px 0;
+ padding: 1.75rem 0 1.75rem 1rem;
+}
+
+pre > .button-copy-code {
+ position: absolute;
+ top: 32px;
+ right: 16px;
+}
+
+@media only screen and (max-device-width: 480px) {
+ pre > .button-copy-code {
+ display: none;
+ }
+}
+```
+
+## Referências
+
+* [11ty docs](https://www.11ty.dev/docs/)
+* [Shiki docs](https://shiki.style/guide/)
+* [Mermaid docs](https://mermaid.js.org/intro/)
+* [Junges.dev - How to add GitHub Copy to Clipboard button on your docs/blog ](https://junges.dev/2-how-to-add-github-copy-to-clipboard-button-on-your-docsblog)