From 3a5ba057f98d97f7c706ef1cd23f92b297cecb0b Mon Sep 17 00:00:00 2001 From: Vincent Jousse Date: Tue, 16 Jul 2024 09:38:47 +0200 Subject: [PATCH] feat: add copy paste shortcut --- src/codeblock/fence.rs | 6 +++-- src/content.rs | 16 ++++++++++-- static/css/emmett.css | 42 +++++++++++++++++++++++++++---- static/js/emmett.js | 57 ++++++++++++++++++++++++++++++++++++++++++ templates/base.html | 1 + 5 files changed, 113 insertions(+), 9 deletions(-) create mode 100644 static/js/emmett.js diff --git a/src/codeblock/fence.rs b/src/codeblock/fence.rs index 3658ce5..cb8e39d 100644 --- a/src/codeblock/fence.rs +++ b/src/codeblock/fence.rs @@ -17,7 +17,7 @@ fn parse_range(s: &str) -> Option> { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct FenceSettings<'a> { pub language: Option<&'a str>, pub line_numbers: bool, @@ -65,7 +65,9 @@ struct FenceIter<'a> { impl<'a> FenceIter<'a> { fn new(fence_info: &'a str) -> Self { - Self { split: fence_info.split(',') } + Self { + split: fence_info.split(','), + } } fn parse_ranges(token: Option<&str>) -> Vec> { diff --git a/src/content.rs b/src/content.rs index 413cb25..bfdde73 100644 --- a/src/content.rs +++ b/src/content.rs @@ -239,14 +239,26 @@ pub fn convert_md_to_html(md_content: &str, settings: &Settings, path: Option<&s cmark::CodeBlockKind::Fenced(fence_info) => FenceSettings::new(fence_info), _ => FenceSettings::new(""), }; - let (block, begin) = CodeBlock::new(fence, settings, path); + let (block, begin) = CodeBlock::new(fence.clone(), settings, path); code_block = Some(block); + + events.push(Event::Html("
".into())); + + let language = &fence.language.unwrap_or("code"); + + events.push(Event::Html( + format!("
{}
", language).into(), + )); + + events.push(Event::Html( + "\n" .into(), + )); events.push(Event::Html(begin.into())); } Event::End(TagEnd::CodeBlock) => { // reset highlight and close the code block code_block = None; - events.push(Event::Html("\n".into())); + events.push(Event::Html("
\n".into())); } event => { if let Some(heading) = current.as_mut() { diff --git a/static/css/emmett.css b/static/css/emmett.css index bd244b9..5c8300a 100644 --- a/static/css/emmett.css +++ b/static/css/emmett.css @@ -251,7 +251,6 @@ article img { /** Code **/ -div code, li code, p code, h1 code, @@ -274,12 +273,15 @@ samp { } pre { + margin-top: 0px; padding: 0.625rem; font-style: monospace; white-space: pre-wrap; font-size: 1rem; - border-radius: var(--standard-border-radius); + border-bottom-left-radius: var(--standard-border-radius); + border-bottom-right-radius: var(--standard-border-radius); + border-top-right-radius: var(--standard-border-radius); box-shadow: 0.3rem 0.3rem 0.3rem rgba(0, 0, 0, 0.2); } @@ -324,7 +326,7 @@ footer p { padding-left: 2.1rem; } .markdown-heading:not(:hover) a.anchor { - display: none; + opacity: 0; } .markdown-heading a.anchor { @@ -335,15 +337,45 @@ footer p { width: 2.1rem; height: 2.1rem; margin: auto; - border-radius: var(--borderRadius-small); justify-content: center; align-items: center; transform: translateY(-50%); float: left; - padding-right: var(--base-size-4); line-height: 1; + opacity: 1; } .markdown-heading a:hover.anchor { background-color: transparent; } +.code-block { + position: relative; +} +.code-block button { + background-color: transparent; + border-radius: var(--standard-border-radius); + cursor: pointer; + border: none; +} + +.code-block .language-name { + display: inline-block; + background-color: #2b303b; + color: #ffffff; + padding-left: 0.5rem; + padding-right: 0.5rem; + + box-shadow: 0.3rem 0rem 0.3rem -0.05rem rgba(0, 0, 0, 0.2); + border-bottom: 1px solid white; +} + +.copy-to-clipboard { + position: absolute; + right: 0.5rem; + top: 2.2rem; +} +.copy-to-clipboard svg { + width: 1rem; + height: 1rem; + fill: white; +} diff --git a/static/js/emmett.js b/static/js/emmett.js new file mode 100644 index 0000000..f86bcb4 --- /dev/null +++ b/static/js/emmett.js @@ -0,0 +1,57 @@ +function fallbackCopyTextToClipboard(text) { + var textArea = document.createElement("textarea"); + textArea.value = text; + + // Avoid scrolling to bottom + textArea.style.top = "0"; + textArea.style.left = "0"; + textArea.style.position = "fixed"; + + document.body.appendChild(textArea); + textArea.focus(); + textArea.select(); + + try { + var successful = document.execCommand("copy"); + var msg = successful ? "successful" : "unsuccessful"; + console.log("Fallback: Copying text command was " + msg); + } catch (err) { + console.error("Fallback: Oops, unable to copy", err); + } + + document.body.removeChild(textArea); +} +function copyTextToClipboard(text, clickedElement) { + if (!navigator.clipboard) { + fallbackCopyTextToClipboard(text); + return; + } + navigator.clipboard.writeText(text).then( + function () { + console.log("Async: Copying to clipboard was successful!"); + clickedElement.innerHTML = + ' '; + }, + function (err) { + console.error("Async: Could not copy text: ", err); + } + ); +} +document.addEventListener( + "DOMContentLoaded", + function () { + const divs = document.querySelectorAll(".copy-to-clipboard"); + + divs.forEach((el) => + el.addEventListener("click", (event) => { + console.log("CLIC"); + const code = event.currentTarget.nextElementSibling; + console.log(event.currentTarget); + console.log(code); + console.log(code.textContent); + copyTextToClipboard(code.textContent, event.currentTarget); + }) + ); + }, + false +); diff --git a/templates/base.html b/templates/base.html index 6386ba2..ff777c6 100644 --- a/templates/base.html +++ b/templates/base.html @@ -18,6 +18,7 @@ content="https://vincent.jousse.org/{% block ogurl %}{% endblock ogurl %}" /> +