diff --git a/lib/LaTeXML/resources/CSS/ltx-listings.css b/lib/LaTeXML/resources/CSS/ltx-listings.css index 3dcf678ce9..11a00068c9 100644 --- a/lib/LaTeXML/resources/CSS/ltx-listings.css +++ b/lib/LaTeXML/resources/CSS/ltx-listings.css @@ -1,5 +1,56 @@ - .ltx_listing_data { - float:right; } + float: right; +} .ltx_listing_data a { - text-decoration:none; } \ No newline at end of file + text-decoration: none; +} +.ltx_listing_copy2clip { + position: absolute; +} +.ltx_listing_button { + appearance: none; + background-color: #FAFBFC; + border: 1px solid rgba(27, 31, 35, 0.15); + border-radius: 6px; + box-shadow: rgba(27, 31, 35, 0.04) 0 1px 0, rgba(255, 255, 255, 0.25) 0 1px 0 inset; + box-sizing: border-box; + color: #24292E; + cursor: pointer; + height: 36px; + padding: 6px; + position: relative; + overflow: hidden; + width: 36px +} +.ltx_listing_button::before, +.ltx_listing_button::after { + content: ""; + position: absolute; + inset: 0; + background-position: center; + background-repeat: no-repeat; + background-size: 60%; + transition: opacity 0.6s ease-in-out; +} +.ltx_listing_button::before { + opacity: 1; + /* copy icon */ + background-image: url("data:image/svg+xml;utf8,"); +} +.ltx_listing_button::after { + opacity: 0; +} +.ltx_listing_button.success::after { + opacity: 1; + /* success icon */ + background-image: url("data:image/svg+xml;utf8,"); +} +.ltx_listing_button.error::after { + opacity: 1; + /* error icon */ + background-image: url("data:image/svg+xml;utf8,"); +} +.ltx_listing_button.success::before, +.ltx_listing_button.error::before { + opacity: 0; +} \ No newline at end of file diff --git a/lib/LaTeXML/resources/javascript/lstlisting.js b/lib/LaTeXML/resources/javascript/lstlisting.js new file mode 100644 index 0000000000..a0dd3ca520 --- /dev/null +++ b/lib/LaTeXML/resources/javascript/lstlisting.js @@ -0,0 +1,39 @@ +// replace download links in lstlisting environments by copy to clipboard button +// requires images/copy.svg and images/ok.svg +// (c) Christoph Hauert 2025 + +function copy2clip(button) { + try { + // temporarily hide line numbers in code listing + const style = document.createElement('style'); + document.head.appendChild(style); + const styleSheet = style.sheet; + var hideLine = styleSheet.insertRule(".ltx_tag_listingline { display: none; }"); + var hideCopy = styleSheet.insertRule(".ltx_listing_data { display: none; }"); + const textToCopy = button.parentNode.parentNode.parentNode.innerText; + navigator.clipboard.writeText(textToCopy); + styleSheet.deleteRule(hideCopy); + styleSheet.deleteRule(hideLine); + button.classList.remove("error"); + button.classList.add("success"); + } catch (e) { + button.classList.remove("success"); + button.classList.add("error"); + } + setTimeout(() => { + button.classList.remove("success", "error"); + }, 1200); +} + +window.addEventListener("load", () => { + const elements = document.getElementsByClassName("ltx_listing_data"); + const copyButton = '
' + + '
'; + Array.from(elements).forEach(el => { + if (el.firstChild) { + el.removeChild(el.firstChild); + } + el.insertAdjacentHTML('afterbegin', copyButton); + }); +});