From 89947681d12d37d821664c3bfc994637e09e737b Mon Sep 17 00:00:00 2001 From: eth3lbert Date: Wed, 31 Jul 2024 22:52:02 +0800 Subject: [PATCH] Improve copy of console command examples (#5397) ## Summary This PR improves the copy of the console command example by: - Preventing the selection of generic prompts and generic output - Lazily setting copy content by leveraging intersection observer Most of the changes are inspired by https://github.com/opensafely/documentation/pull/1461 Some other useful refs: - https://github.com/squidfunk/mkdocs-material/issues/3647 - https://mkdocstrings.github.io/recipes/#prevent-selection-of-prompts-and-output-in-python-code-blocks Resolves #5355 ## Test Plan - ``` shell-session mkdocs serve -f mkdocs.public.yml ``` - Navigate to http://localhost:8000/uv/first-steps/#viewing-the-version - Try clicking the copy button - Try copying by selecting the content --------- Co-authored-by: Zanie Blue --- docs/js/extra.js | 53 ++++++++++++++++++++++++++++++++++++++ docs/stylesheets/extra.css | 4 +++ mkdocs.template.yml | 2 ++ 3 files changed, 59 insertions(+) create mode 100644 docs/js/extra.js diff --git a/docs/js/extra.js b/docs/js/extra.js new file mode 100644 index 000000000..ba4f71d20 --- /dev/null +++ b/docs/js/extra.js @@ -0,0 +1,53 @@ +function cleanupClipboardText(targetSelector) { + const targetElement = document.querySelector(targetSelector); + + // exclude "Generic Prompt" and "Generic Output" spans from copy + const excludedClasses = ["gp", "go"]; + + const clipboardText = Array.from(targetElement.childNodes) + .filter( + (node) => + !excludedClasses.some((className) => + node?.classList?.contains(className), + ), + ) + .map((node) => node.textContent) + .filter((s) => s != ""); + return clipboardText.join("").trim(); +} + +// Sets copy text to attributes lazily using an Intersection Observer. +function setCopyText() { + // The `data-clipboard-text` attribute allows for customized content in the copy + // See: https://www.npmjs.com/package/clipboard#copy-text-from-attribute + const attr = "clipboardText"; + // all "copy" buttons whose target selector is a element + const elements = document.querySelectorAll( + 'button[data-clipboard-target$="code"]', + ); + const observer = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + // target in the viewport that have not been patched + if ( + entry.intersectionRatio > 0 && + entry.target.dataset[attr] === undefined + ) { + entry.target.dataset[attr] = cleanupClipboardText( + entry.target.dataset.clipboardTarget, + ); + } + }); + }); + + elements.forEach((elt) => { + observer.observe(elt); + }); +} + +// Using the document$ observable is particularly important if you are using instant loading since +// it will not result in a page refresh in the browser +// See `How to integrate with third-party JavaScript libraries` guideline: +// https://squidfunk.github.io/mkdocs-material/customization/?h=javascript#additional-javascript +document$.subscribe(function () { + setCopyText(); +}); diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css index e339ba4a4..565a76059 100644 --- a/docs/stylesheets/extra.css +++ b/docs/stylesheets/extra.css @@ -124,3 +124,7 @@ This is a consequence of the reduced nav spacing below. */ .md-nav--secondary .md-nav__link { margin-top: 0.5em; } +/* See: https://mkdocstrings.github.io/recipes/#prevent-selection-of-prompts-and-output-in-python-code-blocks */ +.highlight .gp, .highlight .go { /* Generic.Prompt, Generic.Output */ + user-select: none; +} diff --git a/mkdocs.template.yml b/mkdocs.template.yml index 26d01d0d0..52aefe365 100644 --- a/mkdocs.template.yml +++ b/mkdocs.template.yml @@ -66,6 +66,8 @@ plugins: - typeset extra_css: - stylesheets/extra.css +extra_javascript: + - js/extra.js extra: analytics: provider: fathom