diff --git a/src/styles/app.scss b/src/styles/app.scss index 5265df2c8..f79da66fe 100644 --- a/src/styles/app.scss +++ b/src/styles/app.scss @@ -119,6 +119,74 @@ code.copyable { user-select: all; } +.pre-copy-wrap { + display: flex; + align-items: stretch; + + pre { + flex: 1; + min-width: 0; + margin: 0; + } + + // Square off the right edge of the code block so it meets the button flush + pre > code { + margin: 0; + border-right: none; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + + .copy-code-btn { + display: inline-flex; + align-items: center; + gap: 6px; + flex-shrink: 0; + padding: 0 14px; + // Match the a11y-dark highlight.js theme used on the code block + background: #2b2b2b; + border: 1px solid #e1e1e1; + border-left: 1px solid rgba(248, 248, 242, 0.15); + border-radius: 0 $border-radius $border-radius 0; + color: #d4d0ab; + font-family: $body-font; + font-size: 0.75em; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; + cursor: pointer; + white-space: nowrap; + transition: background 0.15s ease, color 0.15s ease; + + // Checkmark icon only shown in .copied state + .check-icon { + display: none; + } + + &:hover { + background: #3c3c3c; + color: #f8f8f2; + } + + &:focus-visible { + outline: 2px solid white; + outline-offset: 2px; + } + + &.copied { + color: #7ee787; + + .copy-icon { + display: none; + } + + .check-icon { + display: inline-block; + } + } + } +} + header h1, section h2 { z-index: 999999; diff --git a/static/images/check.svg b/static/images/check.svg new file mode 100644 index 000000000..3afbd39ff --- /dev/null +++ b/static/images/check.svg @@ -0,0 +1,3 @@ + diff --git a/static/images/copy.svg b/static/images/copy.svg new file mode 100644 index 000000000..75fe25f03 --- /dev/null +++ b/static/images/copy.svg @@ -0,0 +1,4 @@ + diff --git a/static/scripts/tools-install.js b/static/scripts/tools-install.js index 8f3c78ff5..4955ab196 100644 --- a/static/scripts/tools-install.js +++ b/static/scripts/tools-install.js @@ -177,8 +177,38 @@ function check_initial_override() { } } + +function set_up_copy_buttons() { + var buttons = document.querySelectorAll('.copy-code-btn'); + buttons.forEach(function(btn) { + btn.addEventListener('click', function() { + var wrap = btn.closest('.pre-copy-wrap'); + var code = wrap && wrap.querySelector('code'); + if (!code) { return; } + var text = code.textContent; + + var original_label = btn.getAttribute('aria-label'); + + function mark_copied() { + var text_el = btn.querySelector('.copy-code-btn__text'); + if (text_el) { text_el.textContent = 'Copied!'; } + btn.setAttribute('aria-label', 'Copied!'); + btn.classList.add('copied'); + setTimeout(function() { + if (text_el) { text_el.textContent = 'Copy'; } + btn.setAttribute('aria-label', original_label); + btn.classList.remove('copied'); + }, 2000); + } + + navigator.clipboard.writeText(text).then(mark_copied).catch(function() {}); + }); + }); +} + (function () { check_initial_override(); adjust_for_platform(); set_up_cycle_button(); + set_up_copy_buttons(); }()); diff --git a/templates/components/tools/rustup.html.hbs b/templates/components/tools/rustup.html.hbs index fc7e909b1..c5300d075 100644 --- a/templates/components/tools/rustup.html.hbs +++ b/templates/components/tools/rustup.html.hbs @@ -1,7 +1,14 @@
{{fluent "tools-rustup-unixy"}}
-curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
+ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
+
+ {{fluent "tools-rustup-windows-2"}}
@@ -20,14 +27,28 @@{{fluent "tools-rustup-wsl"}}
-curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
+ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
+
+ {{fluent "tools-rustup-manual-default"}}
-curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
+ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
+
+