From 300db73f00e2c07cfc7ae69684f7a7a0347867c7 Mon Sep 17 00:00:00 2001 From: Quentin ADAM Date: Fri, 8 Aug 2025 03:14:30 +0500 Subject: [PATCH 1/5] feat(search): replace theme search with Algolia DocSearch (PoC) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hide theme search wrapper; render DocSearch button styled like the theme. – Inject #docsearch next to the original wrapper – Style DocSearch button for similar look & feel – Keyboard shortcuts (/ and Cmd/Ctrl+K), deferred init, prefill – Keep DocSearch params in config; enable theme search for header slot This is a proof of concept; visual polish and template-friendly integration still needed. --- hugo.yaml | 11 +- layouts/partials/docsearch.html | 256 ++++++++++++++++++++++++++++++++ 2 files changed, 261 insertions(+), 6 deletions(-) create mode 100644 layouts/partials/docsearch.html diff --git a/hugo.yaml b/hugo.yaml index 9bc944898..2661c2142 100644 --- a/hugo.yaml +++ b/hugo.yaml @@ -176,13 +176,12 @@ params: search: enable: true - type: flexsearch + type: hextra - flexsearch: - # index page by: content | summary | heading | title - index: content - # full | forward | reverse | strict - tokenize: forward + docsearch: + appId: RCNP6XZZWS + apiKey: 6909e93402b6d4522a90f43ad144e233 + indexName: documentation clever toc: displayTags: true diff --git a/layouts/partials/docsearch.html b/layouts/partials/docsearch.html new file mode 100644 index 000000000..59c57ad04 --- /dev/null +++ b/layouts/partials/docsearch.html @@ -0,0 +1,256 @@ + + + + + + From 836eeb956e8c4f0901cd03ddc5510820718acdcf Mon Sep 17 00:00:00 2001 From: Quentin ADAM Date: Fri, 8 Aug 2025 03:15:56 +0500 Subject: [PATCH 2/5] chore(search): include missing tracked files (custom head hook, theme component scaffolding) --- layouts/partials/custom/head-end.html | 2 + .../layouts/partials/docsearch.html | 59 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 layouts/partials/custom/head-end.html create mode 100644 themes/hugo-docsearch/layouts/partials/docsearch.html diff --git a/layouts/partials/custom/head-end.html b/layouts/partials/custom/head-end.html new file mode 100644 index 000000000..c19b462ff --- /dev/null +++ b/layouts/partials/custom/head-end.html @@ -0,0 +1,2 @@ +{{/* Load Algolia DocSearch (project-level partial) */}} +{{ partial "docsearch.html" . }} diff --git a/themes/hugo-docsearch/layouts/partials/docsearch.html b/themes/hugo-docsearch/layouts/partials/docsearch.html new file mode 100644 index 000000000..c774dd7c3 --- /dev/null +++ b/themes/hugo-docsearch/layouts/partials/docsearch.html @@ -0,0 +1,59 @@ + + + + + From 6092eeb67bcd237fb01a41b7b754d63715463296 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Fri, 8 Aug 2025 01:20:14 +0200 Subject: [PATCH 3/5] chore: dedicated CSS/JS for docSearch --- assets/css/docsearch.css | 548 +++++++++++++++++++++++++++++++++++++++ assets/js/docsearch.js | 241 +++++++++++++++++ 2 files changed, 789 insertions(+) create mode 100644 assets/css/docsearch.css create mode 100644 assets/js/docsearch.js diff --git a/assets/css/docsearch.css b/assets/css/docsearch.css new file mode 100644 index 000000000..d09d77659 --- /dev/null +++ b/assets/css/docsearch.css @@ -0,0 +1,548 @@ +/* Algolia DocSearch integration with Clever Cloud theme consistency */ + +/* Hide theme's built-in search UI content and show DocSearch in its place */ +.search-wrapper input, +.search-wrapper form, +.search-wrapper .hextra-search, +.search-wrapper .flexsearch-wrapper { + display: none !important; +} + +.search-wrapper { + position: relative; +} + +#docsearch { + position: static; + width: auto; + height: auto; + overflow: visible; + display: inline-block; +} + +/* DocSearch button styling using Hextra theme variables */ +#docsearch .DocSearch-Button { + opacity: 1; + pointer-events: auto; + border-radius: var(--hx-radius-md); /* Use theme's radius */ + padding: calc(var(--hx-spacing) * 2) calc(var(--hx-spacing) * 3); + line-height: var(--hx-leading-tight); + font-family: var(--hx-default-font-family); + font-size: var(--hx-text-sm); + font-weight: var(--hx-font-weight-medium); + transition: all var(--hx-default-transition-duration) var(--hx-default-transition-timing-function); + min-width: 200px; + justify-content: flex-start; +} + +/* Light theme styling - better contrast */ +:root:not(.dark) #docsearch .DocSearch-Button { + background-color: var(--hx-color-white); + color: var(--hx-color-gray-700); + border: 1px solid var(--hx-color-gray-300); + box-shadow: var(--hx-shadow-sm); +} + +/* Dark theme styling */ +.dark #docsearch .DocSearch-Button { + background-color: var(--hx-color-neutral-800); + color: var(--hx-color-gray-300); + border: 1px solid var(--hx-color-neutral-600); + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.3); +} + +/* Focus states */ +#docsearch .DocSearch-Button:focus { + outline: none; + ring: 2px solid var(--hx-color-primary-500); +} + +:root:not(.dark) #docsearch .DocSearch-Button:focus { + border-color: var(--hx-color-primary-500); + box-shadow: 0 0 0 3px var(--hx-color-primary-100); +} + +.dark #docsearch .DocSearch-Button:focus { + border-color: var(--hx-color-primary-400); + box-shadow: 0 0 0 3px var(--hx-color-primary-900); +} + +/* Hover states */ +:root:not(.dark) #docsearch .DocSearch-Button:hover { + background-color: var(--hx-color-gray-50); + border-color: var(--hx-color-gray-400); + box-shadow: var(--hx-shadow-md); +} + +.dark #docsearch .DocSearch-Button:hover { + background-color: var(--hx-color-neutral-700); + border-color: var(--hx-color-neutral-500); +} + +/* Button placeholder text */ +.DocSearch-Button-Placeholder { + opacity: .7; + font-family: var(--hx-default-font-family); + font-size: var(--hx-text-sm); +} + +/* Search icon styling */ +#docsearch .DocSearch-Search-Icon { + width: 16px; + height: 16px; + color: var(--hx-color-gray-500); +} + +.dark #docsearch .DocSearch-Search-Icon { + color: var(--hx-color-gray-400); +} + +/* Keyboard shortcut styling */ +#docsearch .DocSearch-Button-Keys { + display: flex; + gap: 2px; +} + +#docsearch .DocSearch-Button-Key { + background: var(--hx-color-gray-100); + border: 1px solid var(--hx-color-gray-300); + border-radius: var(--hx-radius-sm); + color: var(--hx-color-gray-600); + font-size: 11px; + padding: 2px 6px; + font-family: var(--hx-font-mono); +} + +.dark #docsearch .DocSearch-Button-Key { + background: var(--hx-color-neutral-700); + border-color: var(--hx-color-neutral-600); + color: var(--hx-color-gray-300); +} + +/* Modal styling with Hextra theme consistency - Reduced size */ +.DocSearch-Container { + z-index: 200; /* Ensure it appears above everything */ +} + +.DocSearch-Container .DocSearch-Modal { + max-width: 600px; /* Reduced from 6xl to a more manageable size */ + width: 90vw; + max-height: 70vh; /* Limit height to prevent taking too much space */ + background-color: var(--hx-color-white); + border: 1px solid var(--hx-color-gray-200); + box-shadow: var(--hx-shadow-xl); + border-radius: var(--hx-radius-lg); + margin: 10vh auto; /* Center with reasonable top margin */ +} + +.dark .DocSearch-Container .DocSearch-Modal { + background-color: var(--hx-color-neutral-900); + border-color: var(--hx-color-neutral-700); + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); +} + +@media (max-width: 768px) { + .DocSearch-Container .DocSearch-Modal { + max-width: 95vw; + max-height: 80vh; + margin: 5vh auto; + } +} + +/* Form elements with Hextra styling */ +.DocSearch-Form { + border: 1px solid var(--hx-color-gray-300); + border-radius: var(--hx-radius-md); + background-color: var(--hx-color-white); + box-shadow: none; +} + +.dark .DocSearch-Form { + border-color: var(--hx-color-neutral-600); + background-color: var(--hx-color-neutral-800); +} + +.DocSearch-Input { + color: var(--hx-color-gray-900); + font-size: var(--hx-text-base); + font-family: var(--hx-default-font-family); + background: transparent; +} + +.dark .DocSearch-Input { + color: var(--hx-color-gray-100); +} + +.DocSearch-Input::placeholder { + color: var(--hx-color-gray-500); +} + +.dark .DocSearch-Input::placeholder { + color: var(--hx-color-gray-400); +} + + +/* Search results container - pure white/black backgrounds by theme */ +.DocSearch-Container .DocSearch-Modal .DocSearch-Dropdown { + max-height: 400px !important; /* Limit results height */ + overflow-y: auto !important; + background-color: white !important; + border-radius: var(--hx-radius-md) !important; + padding: 0 !important; +} + +.dark .DocSearch-Container .DocSearch-Modal .DocSearch-Dropdown { + background-color: black !important; +} + +/* Individual search result styling - pure white/black backgrounds */ +.DocSearch-Container .DocSearch-Modal .DocSearch-Dropdown .DocSearch-Hits .DocSearch-Hit { + border-radius: var(--hx-radius-sm) !important; + margin-bottom: 1px !important; + background-color: white !important; + border: none !important; +} + +.dark .DocSearch-Container .DocSearch-Modal .DocSearch-Dropdown .DocSearch-Hits .DocSearch-Hit { + background-color: black !important; +} + +.DocSearch-Container .DocSearch-Modal .DocSearch-Dropdown .DocSearch-Hits .DocSearch-Hit a { + padding: 12px 16px !important; + border-radius: var(--hx-radius-sm) !important; + color: black !important; + background-color: white !important; + display: block !important; + text-decoration: none !important; + transition: all var(--hx-default-transition-duration) var(--hx-default-transition-timing-function) !important; + border: none !important; + box-shadow: none !important; +} + +.dark .DocSearch-Container .DocSearch-Modal .DocSearch-Dropdown .DocSearch-Hits .DocSearch-Hit a { + color: white !important; + background-color: black !important; +} + +/* Hover state for results - Better contrast */ +.DocSearch-Container .DocSearch-Modal .DocSearch-Dropdown .DocSearch-Hits .DocSearch-Hit a:hover { + background-color: var(--hx-color-gray-50) !important; + color: var(--hx-color-gray-900) !important; +} + +.dark .DocSearch-Container .DocSearch-Modal .DocSearch-Dropdown .DocSearch-Hits .DocSearch-Hit a:hover { + background-color: var(--hx-color-neutral-700) !important; + color: var(--hx-color-gray-50) !important; +} + +/* Selected/highlighted result - Better contrast */ +.DocSearch-Container .DocSearch-Modal .DocSearch-Dropdown .DocSearch-Hits .DocSearch-Hit[aria-selected="true"] a { + background-color: var(--hx-color-primary-50) !important; + color: var(--hx-color-primary-900) !important; +} + +.dark .DocSearch-Container .DocSearch-Modal .DocSearch-Dropdown .DocSearch-Hits .DocSearch-Hit[aria-selected="true"] a { + background-color: var(--hx-color-primary-800) !important; + color: var(--hx-color-primary-50) !important; +} + +/* Result content wrapper - Force inheritance */ +.DocSearch-Container .DocSearch-Modal .DocSearch-Hit-Container { + background-color: inherit !important; +} + +/* Result titles and text - pure black/white text */ +.DocSearch-Container .DocSearch-Modal .DocSearch-Hit-Title { + color: black !important; + font-weight: var(--hx-font-weight-medium) !important; + font-family: var(--hx-default-font-family) !important; + margin-bottom: 4px !important; +} + +.dark .DocSearch-Container .DocSearch-Modal .DocSearch-Hit-Title { + color: white !important; +} + +.DocSearch-Container .DocSearch-Modal .DocSearch-Hit-Path { + color: #666666 !important; + font-size: var(--hx-text-sm) !important; + font-family: var(--hx-default-font-family) !important; + opacity: 1 !important; +} + +.dark .DocSearch-Container .DocSearch-Modal .DocSearch-Hit-Path { + color: #cccccc !important; +} + +.DocSearch-Container .DocSearch-Modal .DocSearch-Hit-Text { + color: #333333 !important; + font-size: var(--hx-text-sm) !important; + font-family: var(--hx-default-font-family) !important; + line-height: 1.4 !important; + margin-top: 4px !important; + opacity: 1 !important; +} + +.dark .DocSearch-Container .DocSearch-Modal .DocSearch-Hit-Text { + color: #dddddd !important; +} + +/* Search term highlights - Force styling */ +.DocSearch-Container .DocSearch-Modal .DocSearch-Hit mark { + background-color: var(--hx-color-yellow-100) !important; + color: var(--hx-color-yellow-900) !important; + font-weight: var(--hx-font-weight-semibold) !important; + padding: 1px 2px !important; + border-radius: 2px !important; +} + +.dark .DocSearch-Container .DocSearch-Modal .DocSearch-Hit mark { + background-color: var(--hx-color-yellow-700) !important; + color: var(--hx-color-yellow-200) !important; +} + + +/* Hit icons */ +.DocSearch-Container .DocSearch-Modal .DocSearch-Hit-icon { + color: var(--hx-color-gray-500) !important; +} + +.dark .DocSearch-Container .DocSearch-Modal .DocSearch-Hit-icon { + color: var(--hx-color-gray-400) !important; +} + +/* Hit action (tree/hierarchy indicator) */ +.DocSearch-Container .DocSearch-Modal .DocSearch-Hit-action { + color: var(--hx-color-gray-400) !important; +} + +.dark .DocSearch-Container .DocSearch-Modal .DocSearch-Hit-action { + color: var(--hx-color-gray-500) !important; +} + +/* Results list container - pure white/black backgrounds */ +.DocSearch-Container .DocSearch-Modal .DocSearch-Dropdown .DocSearch-Hits { + background-color: white !important; + padding: 4px !important; +} + +.dark .DocSearch-Container .DocSearch-Modal .DocSearch-Dropdown .DocSearch-Hits { + background-color: black !important; +} + +/* Section headers spacing - Better visual separation */ +.DocSearch-Container .DocSearch-Modal .DocSearch-Hit-source { + background-color: var(--hx-color-gray-50) !important; + color: var(--hx-color-gray-800) !important; + font-size: var(--hx-text-xs) !important; + font-weight: var(--hx-font-weight-semibold) !important; + text-transform: uppercase !important; + letter-spacing: 0.05em !important; + padding: 6px 16px !important; + border-radius: 0 !important; + margin: 8px 0 4px 0 !important; + font-family: var(--hx-default-font-family) !important; + border-bottom: 1px solid var(--hx-color-gray-200) !important; +} + +.dark .DocSearch-Container .DocSearch-Modal .DocSearch-Hit-source { + background-color: var(--hx-color-neutral-700) !important; + color: var(--hx-color-gray-100) !important; + border-bottom-color: var(--hx-color-neutral-600) !important; +} + +/* Remove Algolia branding elements */ +.DocSearch-Footer, +.DocSearch-Logo { + display: none; +} + +/* Action buttons styling */ +.DocSearch-Commands { + border-top: 1px solid var(--hx-color-gray-200); + background-color: var(--hx-color-gray-50); +} + +.dark .DocSearch-Commands { + border-top-color: var(--hx-color-neutral-700); + background-color: var(--hx-color-neutral-800); +} + +/* Cancel/reset button styling */ +.DocSearch-Cancel, +.DocSearch-Reset { + color: var(--hx-color-gray-600); + font-family: var(--hx-default-font-family); + border-radius: var(--hx-radius-sm); + padding: 4px 8px; +} + +.dark .DocSearch-Cancel, +.dark .DocSearch-Reset { + color: var(--hx-color-gray-400); +} + +.DocSearch-Reset:hover, +.DocSearch-Cancel:hover { + color: var(--hx-color-gray-900); + background-color: var(--hx-color-gray-100); +} + +.dark .DocSearch-Reset:hover, +.dark .DocSearch-Cancel:hover { + color: var(--hx-color-gray-100); + background-color: var(--hx-color-neutral-700); +} + +/* No results state */ +.DocSearch-NoResults { + text-align: center; + padding: 32px 16px; + color: var(--hx-color-gray-600); +} + +.dark .DocSearch-NoResults { + color: var(--hx-color-gray-400); +} + +/* Search icon in modal */ +.DocSearch-MagnifierLabel { + color: var(--hx-color-gray-500); +} + +.dark .DocSearch-MagnifierLabel { + color: var(--hx-color-gray-400); +} + +/* Scrollbar styling for dropdown */ +.DocSearch-Dropdown::-webkit-scrollbar { + width: 6px; +} + +.DocSearch-Dropdown::-webkit-scrollbar-track { + background: transparent; +} + +.DocSearch-Dropdown::-webkit-scrollbar-thumb { + background: var(--hx-color-gray-300); + border-radius: 3px; +} + +.dark .DocSearch-Dropdown::-webkit-scrollbar-thumb { + background: var(--hx-color-neutral-600); +} + +/* Additional fixes for remaining transparent/default elements */ + +/* Start screen / empty state */ +.DocSearch-StartScreen, +.DocSearch-EmptyResults { + background-color: var(--hx-color-white); + color: var(--hx-color-gray-700); + padding: 32px 16px; + text-align: center; +} + +.dark .DocSearch-StartScreen, +.dark .DocSearch-EmptyResults { + background-color: var(--hx-color-neutral-900); + color: var(--hx-color-gray-300); +} + +/* Error states */ +.DocSearch-ErrorScreen { + background-color: var(--hx-color-white); + color: var(--hx-color-red-600); + padding: 32px 16px; + text-align: center; +} + +.dark .DocSearch-ErrorScreen { + background-color: var(--hx-color-neutral-900); + color: var(--hx-color-red-400); +} + +/* Loading state */ +.DocSearch-LoadingIndicator { + color: var(--hx-color-primary-600) !important; +} + +.dark .DocSearch-LoadingIndicator { + color: var(--hx-color-primary-400) !important; +} + +/* Suggestion links in start screen */ +.DocSearch-Help { + color: var(--hx-color-gray-600) !important; + font-size: var(--hx-text-sm); + background-color: var(--hx-color-white); + padding: 16px; +} + +.dark .DocSearch-Help { + color: var(--hx-color-gray-400) !important; + background-color: var(--hx-color-neutral-900); +} + +/* Recent searches */ +.DocSearch-Title { + color: var(--hx-color-gray-900) !important; + font-weight: var(--hx-font-weight-medium); + font-family: var(--hx-default-font-family); +} + +.dark .DocSearch-Title { + color: var(--hx-color-gray-100) !important; +} + +/* Hit content wrapper - force inheritance */ +.DocSearch-Hit-Container, +.DocSearch-Hit-content { + background-color: inherit !important; +} + +/* Force all text elements to use theme colors */ +.DocSearch-Hit-Tree { + color: var(--hx-color-gray-500) !important; +} + +.dark .DocSearch-Hit-Tree { + color: var(--hx-color-gray-400) !important; +} + +/* Result separators */ +.DocSearch-Dropdown-Container::before { + background: var(--hx-color-gray-200); +} + +.dark .DocSearch-Dropdown-Container::before { + background: var(--hx-color-neutral-700); +} + +/* Override any remaining Algolia defaults that might cause transparency issues */ +.DocSearch-Container * { + box-sizing: border-box; +} + +/* Ensure proper background for all interactive elements */ +.DocSearch-Button-Container, +.DocSearch-Modal-Container { + background-color: inherit; +} + +/* Footer commands - ensure proper styling */ +.DocSearch-Commands-Key { + background-color: var(--hx-color-gray-100) !important; + border: 1px solid var(--hx-color-gray-300) !important; + color: var(--hx-color-gray-700) !important; + border-radius: var(--hx-radius-sm); + padding: 2px 6px; + font-family: var(--hx-font-mono); + font-size: 11px; +} + +.dark .DocSearch-Commands-Key { + background-color: var(--hx-color-neutral-700) !important; + border-color: var(--hx-color-neutral-600) !important; + color: var(--hx-color-gray-300) !important; +} \ No newline at end of file diff --git a/assets/js/docsearch.js b/assets/js/docsearch.js new file mode 100644 index 000000000..9c33f4ca5 --- /dev/null +++ b/assets/js/docsearch.js @@ -0,0 +1,241 @@ +(function() { + 'use strict'; + + function ready(fn) { + if (document.readyState !== 'loading') { + fn(); + } else { + document.addEventListener('DOMContentLoaded', fn); + } + } + + ready(function() { + // Ensure DocSearch container exists and place it inside the theme search wrapper + var containerId = 'docsearch'; + var container = document.getElementById(containerId); + if (!container) { + container = document.createElement('div'); + container.id = containerId; + var themeWrapper = document.querySelector('.search-wrapper'); + if (themeWrapper) { + // Place DocSearch inside the search wrapper + themeWrapper.appendChild(container); + } else { + // Fallback + var header = document.querySelector('header'); + if (header) { + header.appendChild(container); + } else { + document.body.appendChild(container); + } + } + } + + // Initialize DocSearch (wait if script not loaded yet) + var openDocSearch; + var initDocSearch = function() { + if (!window.docsearch) return false; + try { + var docSearchConfig = window.docSearchConfig || {}; + window.docsearch({ + appId: docSearchConfig.appId || '', + apiKey: docSearchConfig.apiKey || '', + indexName: docSearchConfig.indexName || '', + container: '#docsearch', + debug: false + }); + openDocSearch = function() { + var btn = document.querySelector('#docsearch .DocSearch-Button'); + if (btn) { + btn.click(); + } + }; + return true; + } catch (e) { + console.error('DocSearch initialization error:', e); + return false; + } + }; + + if (!initDocSearch()) { + var tries = 0; + (function waitForDocSearch(){ + if (initDocSearch()) return; + if (tries++ < 30) setTimeout(waitForDocSearch, 100); + })(); + } + + // Hook the existing theme Search input/button to open DocSearch with prefill + var tryAttach = function() { + var header = document.querySelector('header'); + var found = false; + + // 1) Buttons or anchors acting as search triggers + var btns = header ? header.querySelectorAll([ + 'button[aria-label="Search"]', + 'button[aria-label*="search" i]', + 'a[aria-label*="search" i]', + '.hx-search', + 'button[class*="search" i]', + 'a[class*="search" i]', + 'a[href="#search"]', + '[role="search"] button', + '[data-testid*="search" i]', + '[data-track*="search" i]' + ].join(',')) : []; + + btns.forEach(function(el){ + if (el.__ccDocSearchBound) return; + el.__ccDocSearchBound = true; + found = true; + el.addEventListener('click', function(ev){ + ev.preventDefault(); + if (openDocSearch) openDocSearch(); + }); + el.addEventListener('keydown', function(ev){ + if (ev.key === 'Enter' || ev.key === ' ') { + ev.preventDefault(); + if (openDocSearch) openDocSearch(); + } + }); + }); + + // 2) The actual input/form used by the theme + var input = header ? header.querySelector('input[type="search"], input[placeholder*="search" i], .hx-search input, [role="search"] input') + : document.querySelector('input.search-input'); + var form = input ? input.closest('form') : (header ? header.querySelector('form[role="search"], form.hx-search, .hx-search form') : null); + + var ensureOpen = function(cb){ + if (openDocSearch) { + cb(); + return; + } + var t = 0; + var tick = function(){ + if (openDocSearch) { + cb(); + return; + } + if (t++ < 30) setTimeout(tick, 100); + }; + tick(); + }; + + var openWithPrefill = function(extraChar) { + var prefill = ''; + if (input) prefill = (input.value || ''); + if (extraChar && extraChar.length === 1) prefill += extraChar; + ensureOpen(function(){ + openDocSearch(); + }); + // After modal mounts, set its input value and propagate event + setTimeout(function(){ + var modalInput = document.querySelector('.DocSearch-Input'); + if (modalInput) { + modalInput.value = prefill; + var evt = new Event('input', { bubbles: true }); + modalInput.dispatchEvent(evt); + modalInput.focus(); + // Move caret to end + try { + modalInput.setSelectionRange(modalInput.value.length, modalInput.value.length); + } catch (e) {} + } + }, 20); + }; + + if (input && !input.__ccDocSearchBound) { + input.__ccDocSearchBound = true; + found = true; + // Prevent typing into the old input; route into DocSearch + input.addEventListener('focus', function(e){ + e.preventDefault(); + openWithPrefill(); + input.blur(); + }); + input.addEventListener('keydown', function(e){ + // Allow Tab to move focus + if (e.key === 'Tab') return; + e.preventDefault(); + var ch = (e.key && e.key.length === 1) ? e.key : ''; + if (e.key === 'Backspace') { + if (openDocSearch) openWithPrefill(''); + return; + } + openWithPrefill(ch); + }); + } + + if (form && !form.__ccDocSearchBound) { + form.__ccDocSearchBound = true; + found = true; + form.addEventListener('submit', function(e){ + e.preventDefault(); + openWithPrefill(); + }); + } + + // Overlay approach: add a transparent clickable layer above the input + var wrapper = (input && input.closest('.search-wrapper')) || document.querySelector('.search-wrapper'); + if (wrapper && !wrapper.querySelector('.cc-docsearch-overlay')) { + found = true; + var overlay = document.createElement('button'); + overlay.type = 'button'; + overlay.className = 'cc-docsearch-overlay'; + overlay.setAttribute('aria-label', 'Open search'); + overlay.style.position = 'absolute'; + overlay.style.inset = '0'; + overlay.style.background = 'transparent'; + overlay.style.cursor = 'text'; + overlay.style.zIndex = '5'; + overlay.style.border = '0'; + overlay.style.padding = '0'; + overlay.style.margin = '0'; + // Let the kbd hint remain clickable-less + var kbd = wrapper.querySelector('kbd'); + if (kbd) { + kbd.style.pointerEvents = 'none'; + } + overlay.addEventListener('click', function(e){ + e.preventDefault(); + openWithPrefill(); + }); + overlay.addEventListener('keydown', function(e){ + if (e.key === 'Tab') return; + e.preventDefault(); + var ch = (e.key && e.key.length === 1) ? e.key : ''; + openWithPrefill(ch); + }); + wrapper.style.position = wrapper.style.position || 'relative'; + wrapper.appendChild(overlay); + } + + return found; + }; + + if (!tryAttach()) { + // Retry shortly after to give the theme time to render header + setTimeout(tryAttach, 200); + setTimeout(tryAttach, 500); + // Observe header mutations to rebind when menu/header changes (e.g., responsive) + var header = document.querySelector('header'); + if (header && !header.__ccDocSearchObserved) { + header.__ccDocSearchObserved = true; + var mo = new MutationObserver(function(){ + tryAttach(); + }); + mo.observe(header, { childList: true, subtree: true }); + } + } + + // Global keyboard shortcuts to match common UX: '/' and Cmd/Ctrl+K + window.addEventListener('keydown', function(e){ + var activeTag = (document.activeElement && document.activeElement.tagName) || ''; + var typingInInput = /INPUT|TEXTAREA|SELECT/.test(activeTag); + if (!typingInInput && (e.key === '/' || (e.key.toLowerCase() === 'k' && (e.metaKey || e.ctrlKey)))) { + e.preventDefault(); + if (openDocSearch) openDocSearch(); + } + }); + }); +})(); \ No newline at end of file From a19b184b249cbf25f938b36a52a872fe479e98d6 Mon Sep 17 00:00:00 2001 From: David Legrand Date: Fri, 8 Aug 2025 01:20:27 +0200 Subject: [PATCH 4/5] fix: hugo config --- hugo.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugo.yaml b/hugo.yaml index 2661c2142..4270afec3 100644 --- a/hugo.yaml +++ b/hugo.yaml @@ -176,7 +176,7 @@ params: search: enable: true - type: hextra + type: flexsearch docsearch: appId: RCNP6XZZWS From 34710c0cd79406f2d1f8e6a860ea5f1235bbea8d Mon Sep 17 00:00:00 2001 From: David Legrand Date: Fri, 8 Aug 2025 01:20:50 +0200 Subject: [PATCH 5/5] chore: clean docSearch layouts --- layouts/partials/docsearch.html | 272 ++---------------- .../layouts/partials/docsearch.html | 62 +--- 2 files changed, 25 insertions(+), 309 deletions(-) diff --git a/layouts/partials/docsearch.html b/layouts/partials/docsearch.html index 59c57ad04..05813b376 100644 --- a/layouts/partials/docsearch.html +++ b/layouts/partials/docsearch.html @@ -1,256 +1,28 @@ + + +{{ $docSearchCSS := resources.Get "css/docsearch.css" }} +{{ if $docSearchCSS }} + {{ $docSearchCSS = $docSearchCSS | minify }} + +{{ end }} + + - + + + +{{ $docSearchJS := resources.Get "js/docsearch.js" }} +{{ if $docSearchJS }} + {{ $docSearchJS = $docSearchJS | minify }} + +{{ end }} diff --git a/themes/hugo-docsearch/layouts/partials/docsearch.html b/themes/hugo-docsearch/layouts/partials/docsearch.html index c774dd7c3..cd6d3cb41 100644 --- a/themes/hugo-docsearch/layouts/partials/docsearch.html +++ b/themes/hugo-docsearch/layouts/partials/docsearch.html @@ -1,59 +1,3 @@ - - - - - + + +{{ partial "docsearch.html" . }}