diff --git a/assets/static/css/bcarlin.css b/assets/static/css/bcarlin.css index e0c627b..dfdab98 100644 --- a/assets/static/css/bcarlin.css +++ b/assets/static/css/bcarlin.css @@ -58,6 +58,9 @@ --pico-background-color: rgb(247 249 252 / 100%); --pico-card-background-color: rgb(255 255 255 / 100%); --pico-card-sectioning-background-color: transparent; + --pico-code-color: inherit; + + --color-codeblock-meta-background: rgb(255 255 255 / 100%); } .admonition.warning { @@ -72,6 +75,11 @@ } @media only screen and (prefers-color-scheme: dark) { + :root:not([data-theme]) { + --pico-code-color: inherit; + + --color-codeblock-meta-background: rgb(0 0 0 / 30%); + } .admonition.warning { --admonition-background-color: rgb(52 46 38 / 100%); --admonition-border-color: rgb(255 208 143 / 100%); @@ -444,3 +452,47 @@ main { } } } + +/* + * Code blocks + */ + +.highlight { + position: relative; + + > div { + display: inline-block; + position: absolute; + right: 0; + top: 0; + padding: 0.5em 1.2em; + color: var(--pico-muted-color); + background-color: var(--color-codeblock-meta-background); + font-family: var(--pico-font-family-monospace); + font-size: 0.7em; + line-height: 1em; + border-end-start-radius: var(--pico-border-radius); + + span { + border-inline-start: inset 1px var(--pico-muted-color); + padding-inline-start: 1em; + margin-inline-start: 1em; + cursor: pointer; + + &::before { + content: "\ecd5"; + font-family: "remixicon"; + font-weight: normal; + margin-inline-end: 0.5em; + } + + &.copied::before { + content: "\eb7b"; + } + + &.not-copied::before { + content: "\eca1"; + } + } + } +} diff --git a/assets/static/js/bcarlin.js b/assets/static/js/bcarlin.js index d728169..8812e9d 100644 --- a/assets/static/js/bcarlin.js +++ b/assets/static/js/bcarlin.js @@ -2,21 +2,68 @@ // Listens for clicks on the mobile menu toggle and the main menu close toggle. window.addEventListener('click', function(e) { + if (e.target.matches('#menu-toggle, #menu-toggle i')) { + openMenu(); + } else if (e.target.matches('#menu-close, #menu-close i')) { + closeMenu(); + } else if (e.target.matches('[data-action="copy"]')) { + copyCodeToClipboard(e); + } +}); + +function openMenu() { const menu = document.getElementById('menu'), menuToggle = document.getElementById('menu-toggle'), body = document.body; - if (e.target.matches('#menu-toggle, #menu-toggle i')) { - menu.classList.toggle('active'); - menu.ariaHidden = false; - menuToggle.ariaExpanded = true; - body.style.overflow = 'hidden'; + menu.classList.toggle('active'); + menu.ariaHidden = false; + menuToggle.ariaExpanded = true; + body.style.overflow = 'hidden'; +} + +function closeMenu() { + const menu = document.getElementById('menu'), + menuToggle = document.getElementById('menu-toggle'), + body = document.body; + + menu.classList.remove('active'); + menu.ariaHidden = true; + menuToggle.ariaExpanded = false; + body.style.overflow = 'auto'; +} + +function copyCodeToClipboard(e) { + const codeTag = e.target.closest('.highlight').querySelector("code"); + + if (!codeTag) return; + + const tw = document.createTreeWalker( + codeTag, + NodeFilter.SHOW_TEXT, + (node) => { + if (node.parentElement.matches(".ln")) { + return NodeFilter.FILTER_REJECT; + } + + return NodeFilter.FILTER_ACCEPT; + }, + ); + + let acc = ""; + while (tw.nextNode()) { + acc += tw.currentNode.textContent; } - if (e.target.matches('#menu-close, #menu-close i')) { - menu.classList.remove('active'); - menu.ariaHidden = true; - menuToggle.ariaExpanded = false; - body.style.overflow = 'auto'; - } -}); + navigator.clipboard.writeText(acc) + .then(() => { + e.target.classList.add('copied'); + + setTimeout(() => { + e.target.classList.remove('copied'); + }, 5000); + }) + .catch(() => { + e.target.classList.add('not-copied'); + }); +} diff --git a/layouts/_markup/render-codeblock.html b/layouts/_markup/render-codeblock.html new file mode 100644 index 0000000..0d31dec --- /dev/null +++ b/layouts/_markup/render-codeblock.html @@ -0,0 +1,13 @@ +{{- $result := transform.HighlightCodeBlock . }} +
+
+ {{- .Type -}} + copy +
+
+    {{- /* chomp newline */ -}}
+    {{ $result.Inner }}
+    {{- /* chomp newline */ -}}
+  
+
+{{- /* chomp trailing newline */ -}}