Skip to content

Commit

Permalink
✨ feat: add copy button to code blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
welpo committed Jul 7, 2023
1 parent f710b62 commit 2dec139
Show file tree
Hide file tree
Showing 13 changed files with 190 additions and 112 deletions.
3 changes: 3 additions & 0 deletions config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ language_name.es = "Español"
# The default setting is the light theme.
theme_switcher = true

# Add a "copy" button to codeblocks (loads ~700 bytes of JavaScript).
copy_button = true

# Date format used when listing posts (main page, /blog section, tag posts list…)
# Default is "6th July 2049" in English and "%d %B %Y" in other languages.
long_date_format = "%d %B %Y"
Expand Down
17 changes: 0 additions & 17 deletions content/blog/almost-no-js.ca.md

This file was deleted.

17 changes: 0 additions & 17 deletions content/blog/almost-no-js.es.md

This file was deleted.

17 changes: 0 additions & 17 deletions content/blog/almost-no-js.md

This file was deleted.

22 changes: 22 additions & 0 deletions content/blog/javascript.ca.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
+++
title = "Sense JavaScript obligatori"
date = 2023-01-06
updated = 2023-04-28
description = "JavaScript només s'utilitza quan HTML i CSS no són suficients."

[taxonomies]
tags = ["funcionalitat"]
+++

## JavaScript?

Aquest tema funciona perfectament sense JavaScript. Opcionalment, pot carregar una quantitat mínima per afegir algunes funcionalitats que no són possibles utilitzant només HTML i CSS:

- **Canvi de mode clar/fosc**. S'activa establint `theme_switcher = true`. (~900 bytes)
- **Còpia de blocs de codi amb un sol clic**. S'activa establint `copy_button = true`. (~700 bytes)

Aquestes dues configuracions cal aplicar-les a la secció `[extra]` del fitxer `config.toml`.

La funcionalitat de KaTex, que requereix carregar un fitxer JavaScript de 274 KB, es pot activar per a publicacions específiques. Això es pot fer establint `katex = true` a la secció `[extra]` de l'encapçalament de la publicació.

A part d'això, és un tema ràpid amb HTML i CSS. Tal i com hauria de ser (la major part de) la web :-)
22 changes: 22 additions & 0 deletions content/blog/javascript.es.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
+++
title = "Sin JavaScript obligatorio"
date = 2023-01-06
updated = 2023-04-28
description = "JavaScript solo se utiliza cuando HTML y CSS no son suficientes."

[taxonomies]
tags = ["funcionalidad"]
+++

## ¿JavaScript?

Este tema funciona perfectamente sin JavaScript. Opcionalmente, puede cargar una cantidad mínima para añadir algunas funciones que son imposibles de lograr con HTML y CSS:

- **El cambio de modo claro/oscuro**. Habilitado estableciendo `theme_switcher = true`. (~900 bytes)
- **Copia de bloques de código con un clic**. Se activa configurando `copy_button = true`. (~700 bytes)

Estas dos configuraciones se deben aplicar en la sección `[extra]` de tu archivo `config.toml`.

El soporte de KaTex, que requiere cargar un archivo JavaScript de 274 KB, se puede activar para publicaciones específicas. Esto se puede hacer configurando `katex = true` en la sección `[extra]` del encabezado de la publicación.

Aparte de esto, es un tema rápido con HTML y CSS. Como debería ser (en su mayoría) la web :-)
22 changes: 22 additions & 0 deletions content/blog/javascript.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
+++
title = "No mandatory JavaScript"
date = 2023-01-06
updated = 2023-04-28
description = "JavaScript is only used when HTML and CSS aren't enough."

[taxonomies]
tags = ["showcase"]
+++

## JavaScript?

This theme has no mandatory JavaScript. Optionally, it can load a minimal amount to add some features that are impossible to achieve with HTML and CSS:

- **Light/dark mode switch**. Enabled by setting `theme_switcher = true`. (~900 bytes)
- **One-click copy of code blocks**. Enabled by setting `copy_button = true`. (~700 bytes)

These two settings can be applied in the `[extra]` section of your `config.toml` file.

KaTex support, which requires loading a 274 KB JavaScript file, can be activated for specific posts. This can be done by setting `katex = true` in the post's `[extra]` section of the post's front matter.

Other than that, it's a fast site with HTML and CSS. Just the way (most of) the web should be :-)
112 changes: 74 additions & 38 deletions sass/parts/_code.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,68 +2,104 @@ code {
background-color: var(--bg-1);
padding: 0.1em 0.2em;
font-family: var(--code-font);
font-size: 0.9em;
font-size: 0.9rem;

mark {
background-color: var(--codeblock-highlight);
color: inherit;
filter: brightness(110%);
display: block;
}

table {
width: 100%;
margin: 0rem;
border-collapse: collapse;
border-spacing: 0rem;

td,
th,
tr {
border: none;
padding: 0rem;
}

tbody td:first-child {
user-select: none;
width: 2rem;
text-align: left;
}

tbody tr:nth-child(even) {
background-color: inherit;
}
}
}

pre {
overflow: hidden;
position: relative;
display: block;
line-height: 1.4;
overflow-x: auto;
padding: 2rem 1rem 1rem;
position: relative;
-webkit-overflow-scrolling: touch;
border-radius: 5px;
}

pre code {
background-color: transparent;
color: inherit;
padding: 0;
border: 0;
border-radius: 4px;
}
code {
display: block;
overflow-x: auto;
white-space: pre;
background-color: transparent;
color: inherit;
padding: 0rem;
border: 0rem;
border-radius: 5px;

pre code[class*="language-"] {
-webkit-overflow-scrolling: touch;
}
&::before {
content: attr(data-lang);
display: block;
background-color: var(--primary-color);
color: var(--hover-color);
padding: 0.3rem;
padding-left: 1rem;
width: calc(100% - 1.3rem);
height: 0.9rem;
font-size: 0.65rem;
position: absolute;
text-align: left;
text-transform: uppercase;
top: 0;
left: 0;
}

pre code[class*="language-"]::before {
content: attr(data-lang);
display: block;
background-color: var(--primary-color);
color: var(--hover-color);
padding: 0.3rem;
padding-left: 1rem;
font-family: var(--code-font);
width: 100%;
font-size: 0.65rem;
width: 100%;
position: absolute;
text-align: left;
text-transform: uppercase;
top: 0;
left: 0;
&[class*="language-"] {
-webkit-overflow-scrolling: touch;
}
}
}

.copy-code {
z-index: 1;
-webkit-mask: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgLTk2MCA5NjAgOTYwIiB3aWR0aD0iMjAiPjxwYXRoIGQ9Ik0yMTcuMDAyLTY3LjY5NHEtMzcuNzMyIDAtNjQuMDItMjYuMjg4LTI2LjI4Ny0yNi4yODctMjYuMjg3LTY0LjAxOVYtNzA3LjY5aDc3Ljk5OXY1NDkuNjg5cTAgNC42MTUgMy44NDYgOC40NjIgMy44NDYgMy44NDYgOC40NjIgMy44NDZoNDUxLjY4OXY3Ny45OTlIMjE3LjAwMlptMTc1Ljk5OS0xNzUuOTk5cS0zNy43MzMgMC02NC4wMi0yNi4yODdUMzAyLjY5NC0zMzR2LTQ2My4zODNxMC0zNy43MzIgMjYuMjg3LTY0LjAyIDI2LjI4Ny0yNi4yODcgNjQuMDItMjYuMjg3aDM2NS4zODNxMzcuNzMyIDAgNjQuMDE5IDI2LjI4NyAyNi4yODggMjYuMjg4IDI2LjI4OCA2NC4wMlYtMzM0cTAgMzcuNzMzLTI2LjI4OCA2NC4wMi0yNi4yODcgMjYuMjg3LTY0LjAxOSAyNi4yODdIMzkzLjAwMVptMC03Ny45OThoMzY1LjM4M3E0LjYxNSAwIDguNDYyLTMuODQ3IDMuODQ2LTMuODQ2IDMuODQ2LTguNDYydi00NjMuMzgzcTAtNC42MTYtMy44NDYtOC40NjItMy44NDctMy44NDYtOC40NjItMy44NDZIMzkzLjAwMXEtNC42MTYgMC04LjQ2MiAzLjg0Ni0zLjg0NyAzLjg0Ni0zLjg0NyA4LjQ2MlYtMzM0cTAgNC42MTYgMy44NDcgOC40NjIgMy44NDYgMy44NDcgOC40NjIgMy44NDdabS0xMi4zMDkgMHYtNDg4Vi0zMjEuNjkxWiIvPjwvc3ZnPg==);
// -webkit-mask: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgLTk2MCA5NjAgOTYwIiB3aWR0aD0iMjAiPjxwYXRoIGQ9Ik0yMjIuNzgzLTEyNC43ODJxLTQwLjQyNiAwLTY5LjIxMy0yOC43ODgtMjguNzg4LTI4Ljc4Ny0yOC43ODgtNjkuMjEzdi01MTQuNDM0cTAtNDAuNDI2IDI4Ljc4OC02OS4yMTMgMjguNzg3LTI4Ljc4OCA2OS4yMTMtMjguNzg4aDE0OS41MjFxMTEuMzkxLTMyLjY5NiA0MC40MzgtNTMuNzYxIDI5LjA0Ni0yMS4wNjYgNjcuMjU4LTIxLjA2NiAzNi44MjYgMCA2Ni4yODMgMjEuMDY2IDI5LjQ1NyAyMS4wNjUgNDEuNDEzIDUzLjc2MWgxNDkuNTIxcTQwLjQyNiAwIDY5LjIxMyAyOC43ODggMjguNzg4IDI4Ljc4NyAyOC43ODggNjkuMjEzdjUxNC40MzRxMCA0MC40MjYtMjguNzg4IDY5LjIxMy0yOC43ODcgMjguNzg4LTY5LjIxMyAyOC43ODhIMjIyLjc4M1ptMC05OC4wMDFoNTE0LjQzNHYtNTE0LjQzNGgtNjguMDQzdjEyOS4wNDRIMjkwLjgyNnYtMTI5LjA0NGgtNjguMDQzdjUxNC40MzRabTI1Ny4wMDYtNTIyLjkxM3ExNS4yMTEgMCAyNS43MTEtMTAuMjg5IDEwLjUtMTAuMjkgMTAuNS0yNS41IDAtMTUuMjExLTEwLjI4OS0yNS43MTEtMTAuMjktMTAuNS0yNS41LTEwLjUtMTUuMjExIDAtMjUuNzExIDEwLjI5LTEwLjUgMTAuMjg5LTEwLjUgMjUuNSAwIDE1LjIxIDEwLjI4OSAyNS43MSAxMC4yOSAxMC41IDI1LjUgMTAuNVoiLz48L3N2Zz4);
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 -960 960 960' %3E%3Cpath d='M217.002-67.694q-37.732 0-64.02-26.288-26.287-26.287-26.287-64.019V-707.69h77.999v549.689q0 4.615 3.846 8.462 3.846 3.846 8.462 3.846h451.689v77.999H217.002Zm175.999-175.999q-37.733 0-64.02-26.287T302.694-334v-463.383q0-37.732 26.287-64.02 26.287-26.287 64.02-26.287h365.383q37.732 0 64.019 26.287 26.288 26.288 26.288 64.02V-334q0 37.733-26.288 64.02-26.287 26.287-64.019 26.287H393.001Zm0-77.998h365.383q4.615 0 8.462-3.847 3.846-3.846 3.846-8.462v-463.383q0-4.616-3.846-8.462-3.847-3.846-8.462-3.846H393.001q-4.616 0-8.462 3.846-3.847 3.846-3.847 8.462V-334q0 4.616 3.847 8.462 3.846 3.847 8.462 3.847Zm-12.309 0v-488V-321.691Z'/%3E%3C/svg%3E");
background: var(--hover-color);
cursor: pointer;
display: inline-block;
position: absolute;
height: 20px;
width: 20px;
height: 0.9rem;
width: 0.9rem;
background-size: contain;
color: white;
right: 0.5rem;
top: 0.2rem;
right: 0.7rem;
top: 0.3rem;
align-self: center;
}

.copy-code.checked {
-webkit-mask: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgLTk2MCA5NjAgOTYwIiB3aWR0aD0iMjAiPjxwYXRoIGQ9Ik0zOTUtMjUzIDE5NC00NTVsODMtODMgMTE4IDExNyAyODgtMjg3IDgzIDg0LTM3MSAzNzFaIi8+PC9zdmc+);
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 -960 960 960' %3E%3Cpath d='M395-253 194-455l83-83 118 117 288-287 83 84-371 371Z'/%3E%3C/svg%3E");
height: 1rem;
width: 1rem;
}

.copy-code.error {
-webkit-mask: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgLTk2MCA5NjAgOTYwIiB3aWR0aD0iMjAiPjxwYXRoIGQ9Ik00NzkuMzg2LTI0OFE1MDktMjQ4IDUyOS0yNjcuMzg2cTIwLTE5LjM4NiAyMC00OVQ1MjkuNjE0LTM2Ni41cS0xOS4zODYtMjAuNS00OS0yMC41VDQzMS0zNjYuODg2cS0yMCAyMC4xMTQtMjAgNDkuNzI4dDE5LjM4NiA0OS4zODZxMTkuMzg2IDE5Ljc3MiA0OSAxOS43NzJaTTQxNi00MzFoMTI4di0yNjVINDE2djI2NVptNjQuMjc2IDM4MXEtODguOTE2IDAtMTY3Ljc0My0zMy4xMDQtNzguODI4LTMzLjEwMy0xMzcuNTc3LTkxLjg1Mi01OC43NDktNTguNzQ5LTkxLjg1Mi0xMzcuNTM1UTUwLTM5MS4yNzcgNTAtNDgwLjQ1OHEwLTg5LjQzOCAzMy4xNjItMTY3LjQ5MSAzMy4xNjMtNzguMDUzIDkyLjE3NS0xMzYuOTQyIDU5LjAxMS01OC44ODkgMTM3LjUzMy05MS45OTlRMzkxLjM5My05MTAgNDgwLjQ1OC05MTBxODkuNDI4IDAgMTY3LjUxOCAzMy4wOTNUNzg0Ljk0LTc4NC45NHE1OC44NzQgNTguODc0IDkxLjk2NyAxMzcuMjE1UTkxMC01NjkuMzg1IDkxMC00ODAuMTkycTAgODkuMTkyLTMzLjExIDE2Ny41MTgtMzMuMTEgNzguMzI2LTkxLjk5OSAxMzcuMzM3LTU4Ljg4OSA1OS4wMTItMTM3LjE2NyA5Mi4xNzRRNTY5LjQ0Ny01MCA0ODAuMjc2LTUwWiIvPjwvc3ZnPg==);
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 -960 960 960' %3E%3Cpath d='M479.386-248Q509-248 529-267.386q20-19.386 20-49T529.614-366.5q-19.386-20.5-49-20.5T431-366.886q-20 20.114-20 49.728t19.386 49.386q19.386 19.772 49 19.772ZM416-431h128v-265H416v265Zm64.276 381q-88.916 0-167.743-33.104-78.828-33.103-137.577-91.852-58.749-58.749-91.852-137.535Q50-391.277 50-480.458q0-89.438 33.162-167.491 33.163-78.053 92.175-136.942 59.011-58.889 137.533-91.999Q391.393-910 480.458-910q89.428 0 167.518 33.093T784.94-784.94q58.874 58.874 91.967 137.215Q910-569.385 910-480.192q0 89.192-33.11 167.518-33.11 78.326-91.999 137.337-58.889 59.012-137.167 92.174Q569.447-50 480.276-50Z'/%3E%3C/svg%3E");
}
37 changes: 37 additions & 0 deletions static/js/copyCodeToClipboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const changeIcon = (copyDiv, className) => {
copyDiv.classList.add(className);
setTimeout(() => copyDiv.classList.remove(className), 2500);
};

const addCopyEventListenerToDiv = (copyDiv, block) => {
copyDiv.addEventListener("click", () => copyCodeAndChangeIcon(copyDiv, block));
};

const copyCodeAndChangeIcon = async (copyDiv, block) => {
const code = block.querySelector('table') ? getTableCode(block) : getNonTableCode(block);
try {
await navigator.clipboard.writeText(code);
changeIcon(copyDiv, "checked");
} catch (error) {
changeIcon(copyDiv, "error");
}
};

const getNonTableCode = (block) => {
return [...block.querySelectorAll('code')]
.map(code => code.textContent)
.join('');
};

const getTableCode = (block) => {
return [...block.querySelectorAll('tr')]
.map(row => row.querySelector('td:last-child')?.innerText ?? '')
.join('');
};

document.querySelectorAll("pre").forEach((block) => {
const copyDiv = document.createElement("div");
copyDiv.className = "copy-code";
block.prepend(copyDiv);
addCopyEventListenerToDiv(copyDiv, block);
});
1 change: 1 addition & 0 deletions static/js/copyCodeToClipboard_min.js

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

19 changes: 0 additions & 19 deletions static/js/copy_button.js

This file was deleted.

11 changes: 8 additions & 3 deletions templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,16 @@
</div>
{% include "partials/footer.html" %}

{% if page.extra.katex and page.extra.katex == true %}
{# Add KaTeX functionality (loads CSS and JS) #}
{%- if page.extra.katex and page.extra.katex == true -%}
<link rel="stylesheet" href="{{ get_url(path='katex_min.css', trailing_slash=false) | safe }}">

<script defer src="{{ get_url(path='js/katex_min.js', trailing_slash=false) | safe }}"></script>
{% endif %}
{%- endif -%}

{# Add copy button to codeblocks #}
{%- if config.extra.copy_button and config.extra.copy_button == true -%}
<script defer src="{{ get_url(path='js/copyCodeToClipboard_min.js', trailing_slash=false) | safe }}"/></script>
{%- endif -%}
</body>

</html>
2 changes: 1 addition & 1 deletion templates/partials/header.html
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
{%- endfor -%}
{%- endif -%}">

{%- if config.extra.theme_switcher == true -%}
{%- if config.extra.theme_switcher and config.extra.theme_switcher == true -%}
<script type="text/javascript" src="{{ get_url(path='js/initialize_theme_min.js') | safe }}"></script>
<script defer src="{{ get_url(path='js/main_min.js', trailing_slash=false) | safe }}"/></script>
{%- endif -%}
Expand Down

0 comments on commit 2dec139

Please sign in to comment.