From b3a5dc0bd0bfb01d9843189a0811505179473c49 Mon Sep 17 00:00:00 2001 From: Kaitlyn Michael Date: Wed, 25 Jun 2025 09:00:35 -0500 Subject: [PATCH 1/4] add link icon to section headers --- assets/css/index.css | 31 ++++++++++ layouts/_default/_markup/render-heading.html | 11 ++++ static/js/index.js | 61 ++++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 layouts/_default/_markup/render-heading.html diff --git a/assets/css/index.css b/assets/css/index.css index bc2c457b6a..a03cd7e92b 100644 --- a/assets/css/index.css +++ b/assets/css/index.css @@ -35,6 +35,37 @@ section.prose { @apply text-lg font-medium pb-3 border-b border-b-redis-pen-700 border-opacity-50; } +/* Header link styles */ +.header-link { + @apply text-slate-400 hover:text-slate-600 transition-all duration-200 no-underline cursor-pointer; + text-decoration: none !important; +} + +.header-link:hover { + @apply text-slate-600; + text-decoration: none !important; +} + +.header-link svg { + @apply w-4 h-4 inline-block; +} + +/* Ensure header links don't interfere with prose styling */ +.prose h1 .header-link, +.prose h2 .header-link, +.prose h3 .header-link, +.prose h4 .header-link, +.prose h5 .header-link, +.prose h6 .header-link { + @apply text-slate-400 hover:text-slate-600; + text-decoration: none !important; +} + +/* Feedback state for copied links */ +.header-link.copied { + @apply text-green-500; +} + .prose p, .prose ol, .prose ul { @apply text-base; } diff --git a/layouts/_default/_markup/render-heading.html b/layouts/_default/_markup/render-heading.html new file mode 100644 index 0000000000..fe5c2f0fc9 --- /dev/null +++ b/layouts/_default/_markup/render-heading.html @@ -0,0 +1,11 @@ +{{- $anchor := .Anchor | safeURL -}} +{{- $level := .Level -}} +{{- $text := .Text | safeHTML -}} + + {{ $text }} + + + + + + diff --git a/static/js/index.js b/static/js/index.js index 61f1f905d3..bc77899c64 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -98,9 +98,70 @@ const mobileMenu = (() => { toggleMenu('products-mobile-menu', 'productsMobileMenuState') } else if (event.target.closest('[data-resources-mobile-menu-toggle]')) { toggleMenu('resources-mobile-menu', 'resourcesMobileMenuState') + } else if (event.target.closest('.header-link')) { + // Handle header link clicks + event.preventDefault() + copyHeaderLinkToClipboard(event.target.closest('.header-link')) } } + // Copy header link URL to clipboard + function copyHeaderLinkToClipboard(linkElement) { + const href = linkElement.getAttribute('href') + const fullUrl = window.location.origin + window.location.pathname + href + + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(fullUrl).then(() => { + showCopyFeedback(linkElement) + }).catch(err => { + console.error('Failed to copy link: ', err) + fallbackCopyToClipboard(fullUrl, linkElement) + }) + } else { + // Fallback for older browsers + fallbackCopyToClipboard(fullUrl, linkElement) + } + } + + // Show visual feedback when link is copied + function showCopyFeedback(linkElement) { + const originalTitle = linkElement.getAttribute('title') + + linkElement.setAttribute('title', 'Copied!') + linkElement.classList.add('copied') + + setTimeout(() => { + linkElement.setAttribute('title', originalTitle) + linkElement.classList.remove('copied') + }, 1500) + } + + // Fallback copy method for older browsers + function fallbackCopyToClipboard(text, linkElement) { + const textArea = document.createElement('textarea') + textArea.value = text + textArea.style.position = 'fixed' + textArea.style.left = '-999999px' + textArea.style.top = '-999999px' + document.body.appendChild(textArea) + textArea.focus() + textArea.select() + + try { + const successful = document.execCommand('copy') + if (successful) { + showCopyFeedback(linkElement) + console.log('Link copied to clipboard (fallback)') + } else { + console.error('Fallback copy failed') + } + } catch (err) { + console.error('Fallback copy failed: ', err) + } + + document.body.removeChild(textArea) + } + function allowFocus(selector, state) { const container = document.querySelector(selector) const focusable = container.querySelectorAll('button, [href], input, select, textarea') From f040e6300917a8a04c2362fe293f7e20451053ca Mon Sep 17 00:00:00 2001 From: Kaitlyn Michael Date: Wed, 25 Jun 2025 11:39:39 -0500 Subject: [PATCH 2/4] review suggestions --- assets/css/index.css | 2 ++ layouts/_default/_markup/render-heading.html | 4 ++-- static/js/index.js | 6 +++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/assets/css/index.css b/assets/css/index.css index a03cd7e92b..f29e269a00 100644 --- a/assets/css/index.css +++ b/assets/css/index.css @@ -39,6 +39,7 @@ section.prose { .header-link { @apply text-slate-400 hover:text-slate-600 transition-all duration-200 no-underline cursor-pointer; text-decoration: none !important; + vertical-align: baseline; } .header-link:hover { @@ -48,6 +49,7 @@ section.prose { .header-link svg { @apply w-4 h-4 inline-block; + vertical-align: baseline; } /* Ensure header links don't interfere with prose styling */ diff --git a/layouts/_default/_markup/render-heading.html b/layouts/_default/_markup/render-heading.html index fe5c2f0fc9..0a12e62794 100644 --- a/layouts/_default/_markup/render-heading.html +++ b/layouts/_default/_markup/render-heading.html @@ -3,8 +3,8 @@ {{- $text := .Text | safeHTML -}} {{ $text }} - - + + diff --git a/static/js/index.js b/static/js/index.js index bc77899c64..fd0c66ffd0 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -110,6 +110,10 @@ const mobileMenu = (() => { const href = linkElement.getAttribute('href') const fullUrl = window.location.origin + window.location.pathname + href + // Update the URL hash to provide immediate visual feedback + window.location.hash = href + + // Copy to clipboard if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(fullUrl).then(() => { showCopyFeedback(linkElement) @@ -133,7 +137,7 @@ const mobileMenu = (() => { setTimeout(() => { linkElement.setAttribute('title', originalTitle) linkElement.classList.remove('copied') - }, 1500) + }, 2000) } // Fallback copy method for older browsers From d592ffb596f38bf84ba57c0e60a0cd60532ba676 Mon Sep 17 00:00:00 2001 From: Kaitlyn Michael Date: Wed, 25 Jun 2025 11:41:59 -0500 Subject: [PATCH 3/4] toc embed fix --- layouts/partials/docs-toc.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/layouts/partials/docs-toc.html b/layouts/partials/docs-toc.html index 63f2a09304..37f625d95a 100644 --- a/layouts/partials/docs-toc.html +++ b/layouts/partials/docs-toc.html @@ -31,7 +31,9 @@

On this page

{{ end }} -
  • {{ $header | plainify | safeHTML }} + + {{ $headerText := $header | replaceRE `]*class="header-link"[^>]*>.*?` "" | plainify | safeHTML }} +
  • {{ $headerText }} {{ $prevLevel = $level }} {{ end }} From bc641105fd47098cde994efb3c57fd620af5b3c2 Mon Sep 17 00:00:00 2001 From: Rachel Elledge Date: Thu, 26 Jun 2025 10:16:53 -0500 Subject: [PATCH 4/4] Attempt to fix the broken on this page ToC for embedded content that includes headers --- layouts/partials/docs-toc.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layouts/partials/docs-toc.html b/layouts/partials/docs-toc.html index 37f625d95a..1146dd839d 100644 --- a/layouts/partials/docs-toc.html +++ b/layouts/partials/docs-toc.html @@ -4,7 +4,7 @@ {{ $showEmbedHeaders := .Params.tocEmbedHeaders }} {{ $headerRange := .Params.headerRange | default "[1-3]" }} -{{ $headers := findRE ( print "(.|\n])+?") .Content }} +{{ $headers := findRE ( print "(.|\n)+?") .Content }} {{ $has_headers := ge (len $headers) 1 }}