From b0a95c938290032e5e7c357681c271b8cf04bafb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20=C3=85hall?= Date: Tue, 16 Jun 2026 09:37:10 +0200 Subject: [PATCH] Copy code --- static/css/markdown.css | 13 +++++++++++++ static/css/notes2.css | 3 +++ static/js/marked_position.mjs | 29 ++++++++++++++++++++++++++--- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/static/css/markdown.css b/static/css/markdown.css index 1ecbc94..832d4a2 100644 --- a/static/css/markdown.css +++ b/static/css/markdown.css @@ -102,6 +102,11 @@ border: 1px solid #ccc; padding: 2px 4px; border-radius: 4px; + + &.copy { + border: var(--markdown-copy-border); + background-color: var(--markdown-copy-background); + } } pre { @@ -111,6 +116,14 @@ border-radius: 4px; white-space: pre-wrap; + &.copy { + border: var(--markdown-copy-border); + background-color: var(--markdown-copy-background); + code { + background-color: inherit !important; + } + } + code { border: unset; padding: unset; diff --git a/static/css/notes2.css b/static/css/notes2.css index 2d9f074..a48a319 100644 --- a/static/css/notes2.css +++ b/static/css/notes2.css @@ -15,6 +15,9 @@ --menu-item-hover-color: #f4f4f4; --font-monospace: "Liberation Mono", monospace; + + --markdown-copy-border: 1px solid #0a0; + --markdown-copy-background: #e3f4d7; } html { diff --git a/static/js/marked_position.mjs b/static/js/marked_position.mjs index 5f77251..63f76f7 100644 --- a/static/js/marked_position.mjs +++ b/static/js/marked_position.mjs @@ -94,6 +94,7 @@ export class MarkedPosition { constructor() {// {{{ window.marked_setpos = (event) => this.setpos(event) window.marked_changecheckbox = (event) => this.changecheckbox(event) + window.marked_copy_to_clipboard = (event) => this.copy_to_clipboard(event) this.render() }// }}} setpos(event) {// {{{ @@ -119,6 +120,28 @@ export class MarkedPosition { } }) }// }}} + async copy_to_clipboard(event) {// {{{ + if (!event.shiftKey) + return + + try { + // Stop text selections on the page to the mouse pointer. + // Old selections are remove as well to give a cleaner view + // of the copied text/highlighting. + event.preventDefault() + event.stopPropagation() + window.getSelection().removeAllRanges() + + const text = event.target.innerText + await navigator.clipboard.writeText(text) + event.target.classList.add('copy') + setTimeout(()=>event.target.classList.remove('copy'), 250) + } catch (err) { + console.error('Failed to copy: ', err) + alert('Failed to copy: ', err) + } + }// }}} + render() {// {{{ const markedObject = this this.marked = new Marked() @@ -165,12 +188,12 @@ export class MarkedPosition { const code = token.text.replace(other.endingNewline, '') + '\n' if (!langString) { - return `
`
+						return `
`
 							+ (token.escaped ? code : escapeHtmlEntities(code, true))
 							+ '
\n' } - return `
'
 						+ (token.escaped ? code : escapeHtmlEntities(code, true))
@@ -260,7 +283,7 @@ export class MarkedPosition {
 				},
 
 				codespan(token) {
-					return `${escapeHtmlEntities(token.text, true)}`
+					return `${escapeHtmlEntities(token.text, true)}`
 				},
 
 				br(token) {