diff --git a/scripts/autogen_utils.py b/scripts/autogen_utils.py index 38d0c4c5ba..bdec359f6a 100644 --- a/scripts/autogen_utils.py +++ b/scripts/autogen_utils.py @@ -73,9 +73,38 @@ def make_outline(md_source): ) return outline +def add_copy_buttons_to_code(html_content): + def add_button(match): + full_match = match.group(0) + + if 'style="white-space:pre;overflow-x:auto' in full_match: + return full_match + + if "┏━━━━━━" in full_match or "Total params:" in full_match: + return full_match + + copy_button_html = ( + '
]*>.*?)' + + def handle_match(m): + if m.group(1): + return m.group(1) + else: + return add_button(m) + + return re.sub(combined_pattern, handle_match, html_content, flags=re.DOTALL) + def render_markdown_to_html(md_content): - return markdown.markdown( + html_content = markdown.markdown( md_content, extensions=[ "fenced_code", @@ -96,6 +125,8 @@ def render_markdown_to_html(md_content): }, }, ) + html_content = add_copy_buttons_to_code(html_content) + return html_content def set_active_flag_in_nav_entry(entry, relative_url): diff --git a/theme/css/docs.css b/theme/css/docs.css index c6d8c0d6d7..814d7d8cd6 100644 --- a/theme/css/docs.css +++ b/theme/css/docs.css @@ -66,6 +66,71 @@ pre { font-size: 100%; } +.code__container { + position: relative; + display: block; + overflow: visible; +} + +.code__copy--button { + position: absolute; + right: 0; + top: 0; + background-color: rgba(255, 255, 255, 0.1); + border: none; + border-radius: 4px; + cursor: pointer; + width: 32px; + height: 32px; + z-index: 10; + display: flex; + align-items: center; + justify-content: center; + transition: background-color 0.2s; +} + +.code__container pre { + margin: 0; + padding: 28px 45px 24px 24px; + overflow-x: auto; + border-radius: 8px; + line-height: 1.5; +} + +.code__copy--button .icon--copy { + filter: invert(1); + width: 18px; + height: 18px; + opacity: 0.6; +} + +.code__copy--button:hover { + background-color: rgba(255, 255, 255, 0.2); +} + +.code__copy--button:hover .icon--copy { + opacity: 1; +} + +.code__copy--tooltip { + font-size: 11px; + padding: 4px 8px; + position: absolute; + left: 50%; + transform: translateX(-50%); + top: 40px; + display: none; + border-radius: 4px; + z-index: 101; + white-space: nowrap; + background-color: #ffffff; + color: var(--text-red); +} + +.code__copy--button.active .code__copy--tooltip { + display: block; +} + hr { margin-top: 2rem; margin-bottom: 2rem; diff --git a/theme/js/index.js b/theme/js/index.js index 0457277681..0574689c9f 100644 --- a/theme/js/index.js +++ b/theme/js/index.js @@ -23,7 +23,15 @@ const copyButtons = document.querySelectorAll('.code__copy--button'); copyButtons.forEach((button) => { button.addEventListener('click', () => { const parent = button.parentNode; - const text = parent.querySelector('.language-python').innerText; + let text = parent.querySelector('code').innerText; + + if (text.includes('>>>')) { + text = text.split('\n') + .filter(line => /^(>>>|\.\.\.)/.test(line.trim())) + .map(line => line.replace(/^(>>>|\.\.\.) ?/, "")) + .join('\n'); + } + const inputElement = document.createElement('textarea'); console.log('text', text); inputElement.value = text; @@ -34,9 +42,12 @@ copyButtons.forEach((button) => { document.execCommand('copy'); inputElement.remove(); - button.querySelector('.code__copy--tooltip').style.display = 'block'; + const tooltip = button.querySelector('.code__copy--tooltip'); + tooltip.textContent = 'Copied!'; + tooltip.style.display = 'block'; setTimeout(() => { - button.querySelector('.code__copy--tooltip').style.display = 'none'; + tooltip.textContent = 'Copy'; + tooltip.style.display = 'none'; }, 2000); }); });