From c38d6663660e024ba41d386096e39ef3cecd13e1 Mon Sep 17 00:00:00 2001 From: Aoran Zeng Date: Wed, 28 Jan 2026 21:28:06 +0800 Subject: [PATCH 1/8] Hide search results on Escape key on desktop for aliki --- lib/rdoc/generator/template/aliki/js/aliki.js | 8 ++++++++ lib/rdoc/generator/template/aliki/js/search_controller.js | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/lib/rdoc/generator/template/aliki/js/aliki.js b/lib/rdoc/generator/template/aliki/js/aliki.js index cf13889bae..3abec5fb6a 100644 --- a/lib/rdoc/generator/template/aliki/js/aliki.js +++ b/lib/rdoc/generator/template/aliki/js/aliki.js @@ -103,6 +103,14 @@ function hookSearch() { } }); + // Hide search results on Escape key on desktop too + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape' && search.isShown()) { + search.hide(); + input.blur(); + } + }); + // Check for ?q= URL parameter and trigger search automatically if (typeof URLSearchParams !== 'undefined') { const urlParams = new URLSearchParams(window.location.search); diff --git a/lib/rdoc/generator/template/aliki/js/search_controller.js b/lib/rdoc/generator/template/aliki/js/search_controller.js index e91bdbe93a..4431647c62 100644 --- a/lib/rdoc/generator/template/aliki/js/search_controller.js +++ b/lib/rdoc/generator/template/aliki/js/search_controller.js @@ -125,5 +125,9 @@ SearchController.prototype = Object.assign({}, SearchNavigation, new function() this.result.setAttribute('aria-expanded', 'true'); this.setNavigationActive(true); } + + this.isShown = function() { + return this.result.getAttribute('aria-expanded') == 'true'; + } }); From 8e7893e18913763f829c5123301aa15974cc59a9 Mon Sep 17 00:00:00 2001 From: Aoran Zeng Date: Wed, 28 Jan 2026 21:47:33 +0800 Subject: [PATCH 2/8] Type `s` or `/` to search for aliki --- lib/rdoc/generator/template/aliki/_header.rhtml | 2 +- lib/rdoc/generator/template/aliki/js/aliki.js | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/rdoc/generator/template/aliki/_header.rhtml b/lib/rdoc/generator/template/aliki/_header.rhtml index 39dde3f06c..e5efcce7fc 100644 --- a/lib/rdoc/generator/template/aliki/_header.rhtml +++ b/lib/rdoc/generator/template/aliki/_header.rhtml @@ -8,7 +8,7 @@
    { + if (e.key === 's' || e.key === '/' && !search.isShown()) { + e.preventDefault(); // Prevent the 's' '/' character from being typed + input.focus(); + } + }); + + // Check for ?q= URL parameter and trigger search automatically if (typeof URLSearchParams !== 'undefined') { const urlParams = new URLSearchParams(window.location.search); From 7ed2e3f794df8632bcf5a7427bc789c7463cd231 Mon Sep 17 00:00:00 2001 From: Aoran Zeng Date: Fri, 30 Jan 2026 17:15:12 +0800 Subject: [PATCH 3/8] Use `s` to search inside this class and `/` to search globally --- .../generator/template/aliki/_header.rhtml | 2 +- lib/rdoc/generator/template/aliki/js/aliki.js | 53 +++++++++++++------ 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/lib/rdoc/generator/template/aliki/_header.rhtml b/lib/rdoc/generator/template/aliki/_header.rhtml index e5efcce7fc..c7eb6b42b1 100644 --- a/lib/rdoc/generator/template/aliki/_header.rhtml +++ b/lib/rdoc/generator/template/aliki/_header.rhtml @@ -8,7 +8,7 @@
      { - if (e.key === 's' || e.key === '/' && !search.isShown()) { - e.preventDefault(); // Prevent the 's' '/' character from being typed + if (document.activeElement.tagName === 'INPUT') { + return; + } + + if (e.key === "/") { + e.preventDefault(); + input.value = ''; input.focus(); + } else if (e.key === "s" || e.key === "S") { + e.preventDefault(); + // Fill search field with current module name and trigger search + const moduleName = getCurrentModuleName(); + if (moduleName) { + input.value = moduleName + '::'; + input.focus(); + } else { + // Fallback to regular focus if not on a module page + input.focus(); + } } }); - // Check for ?q= URL parameter and trigger search automatically if (typeof URLSearchParams !== 'undefined') { const urlParams = new URLSearchParams(window.location.search); @@ -133,16 +150,23 @@ function hookSearch() { /* ===== Keyboard Shortcuts ===== */ -function hookFocus() { - document.addEventListener("keydown", (event) => { - if (document.activeElement.tagName === 'INPUT') { - return; - } - if (event.key === "/") { - event.preventDefault(); - document.querySelector('#search-field').focus(); - } - }); +/** + * Extract module/class name from URL path + * + * Examples: + * /RDoc/String.html -> String + * /RDoc/String/Example.html -> String::Example + */ +function getCurrentModuleName() { + + const path = window.location.pathname; + + // Match patterns like /RDoc/ClassName.html or /RDoc/ClassName/SubClass.html + const match = path.match(/\/([A-Z]\w*(?:\/[A-Z]\w*)*)\.(html|md|rdoc)$/); + + if (!match) return null; + + return match[1].replace(/\//g, '::'); } /* ===== Mobile Navigation ===== */ @@ -512,7 +536,6 @@ function showCopySuccess(button) { document.addEventListener('DOMContentLoaded', () => { hookSourceViews(); hookSearch(); - hookFocus(); hookSidebar(); generateToc(); setHeadingSelfLinkScrollHandlers(); From 440e430c7fffb852d7e21e2499178d0f75136c71 Mon Sep 17 00:00:00 2001 From: Aoran Zeng Date: Fri, 30 Jan 2026 17:19:38 +0800 Subject: [PATCH 4/8] Escape key forces to quit search status --- lib/rdoc/generator/template/aliki/js/aliki.js | 2 +- lib/rdoc/generator/template/aliki/js/search_controller.js | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/rdoc/generator/template/aliki/js/aliki.js b/lib/rdoc/generator/template/aliki/js/aliki.js index 6d637a4d1b..0963b6e7d0 100644 --- a/lib/rdoc/generator/template/aliki/js/aliki.js +++ b/lib/rdoc/generator/template/aliki/js/aliki.js @@ -105,7 +105,7 @@ function hookSearch() { // Hide search results on Escape key on desktop too document.addEventListener('keydown', (e) => { - if (e.key === 'Escape' && search.isShown()) { + if (e.key === 'Escape' && input.matches(":focus")) { search.hide(); input.blur(); } diff --git a/lib/rdoc/generator/template/aliki/js/search_controller.js b/lib/rdoc/generator/template/aliki/js/search_controller.js index 4431647c62..dfb75696ef 100644 --- a/lib/rdoc/generator/template/aliki/js/search_controller.js +++ b/lib/rdoc/generator/template/aliki/js/search_controller.js @@ -125,9 +125,4 @@ SearchController.prototype = Object.assign({}, SearchNavigation, new function() this.result.setAttribute('aria-expanded', 'true'); this.setNavigationActive(true); } - - this.isShown = function() { - return this.result.getAttribute('aria-expanded') == 'true'; - } }); - From 1b28edc99ee9996ffd0681538e5d0bdeb7c2b510 Mon Sep 17 00:00:00 2001 From: Aoran Zeng Date: Fri, 30 Jan 2026 17:41:28 +0800 Subject: [PATCH 5/8] Seach `s` for instance methods `Shift+s` for class methods --- lib/rdoc/generator/template/aliki/_header.rhtml | 2 +- lib/rdoc/generator/template/aliki/js/aliki.js | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/rdoc/generator/template/aliki/_header.rhtml b/lib/rdoc/generator/template/aliki/_header.rhtml index c7eb6b42b1..1be14d3bde 100644 --- a/lib/rdoc/generator/template/aliki/_header.rhtml +++ b/lib/rdoc/generator/template/aliki/_header.rhtml @@ -8,7 +8,7 @@
        { if (document.activeElement.tagName === 'INPUT') { return; @@ -125,10 +126,11 @@ function hookSearch() { input.focus(); } else if (e.key === "s" || e.key === "S") { e.preventDefault(); - // Fill search field with current module name and trigger search + // Fill search field with current module name and scope by method type const moduleName = getCurrentModuleName(); if (moduleName) { - input.value = moduleName + '::'; + const suffix = e.shiftKey ? '::' : '#'; + input.value = moduleName + suffix; input.focus(); } else { // Fallback to regular focus if not on a module page From 0205a6eebb7f433c50c481d25919e4473202f805 Mon Sep 17 00:00:00 2001 From: Aoran Zeng Date: Fri, 30 Jan 2026 17:51:10 +0800 Subject: [PATCH 6/8] Auto-complete `:` --- lib/rdoc/generator/template/aliki/js/aliki.js | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/lib/rdoc/generator/template/aliki/js/aliki.js b/lib/rdoc/generator/template/aliki/js/aliki.js index 85cddb3735..6f983ade34 100644 --- a/lib/rdoc/generator/template/aliki/js/aliki.js +++ b/lib/rdoc/generator/template/aliki/js/aliki.js @@ -96,6 +96,14 @@ function hookSearch() { } }); + // Hide search results on Escape key on desktop too + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape' && input.matches(":focus")) { + search.hide(); + input.blur(); + } + }); + // Show search results when focusing on input (if there's a query) input.addEventListener('focus', () => { if (input.value.trim()) { @@ -103,12 +111,25 @@ function hookSearch() { } }); - // Hide search results on Escape key on desktop too - document.addEventListener('keydown', (e) => { - if (e.key === 'Escape' && input.matches(":focus")) { - search.hide(); - input.blur(); + // Auto-complete namespace separator when user types ':' + input.addEventListener('keydown', (e) => { + if (e.key !== ':' || e.ctrlKey || e.metaKey || e.altKey) { + return; + } + + const start = input.selectionStart; + const end = input.selectionEnd; + if (start === null || end === null) { + return; } + + e.preventDefault(); + + const value = input.value; + input.value = value.slice(0, start) + '::' + value.slice(end); + + const caret = start + 2; + input.setSelectionRange(caret, caret); }); // Keyboard shortcuts for search @@ -150,8 +171,6 @@ function hookSearch() { } } -/* ===== Keyboard Shortcuts ===== */ - /** * Extract module/class name from URL path * From db02096ae2c7779d3f62e1a31bd77e52c2db127f Mon Sep 17 00:00:00 2001 From: Aoran Zeng Date: Sat, 31 Jan 2026 21:35:51 +0800 Subject: [PATCH 7/8] Allowing no filename suffix cases --- lib/rdoc/generator/template/aliki/js/aliki.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/rdoc/generator/template/aliki/js/aliki.js b/lib/rdoc/generator/template/aliki/js/aliki.js index 6f983ade34..4b413bfc5c 100644 --- a/lib/rdoc/generator/template/aliki/js/aliki.js +++ b/lib/rdoc/generator/template/aliki/js/aliki.js @@ -175,15 +175,15 @@ function hookSearch() { * Extract module/class name from URL path * * Examples: - * /RDoc/String.html -> String - * /RDoc/String/Example.html -> String::Example + * /_site/String.html -> String + * /_site/String/Example.html -> String::Example */ function getCurrentModuleName() { - const path = window.location.pathname; + const path = window.location.pathname; // Ignore the fragment - // Match patterns like /RDoc/ClassName.html or /RDoc/ClassName/SubClass.html - const match = path.match(/\/([A-Z]\w*(?:\/[A-Z]\w*)*)\.(html|md|rdoc)$/); + // In some cases, there may be no file extensions + const match = path.match(/\/([A-Z]\w*(?:\/[A-Z]\w*)*)(\.(html|md|rdoc))?$/); if (!match) return null; From 0f48d33361f004a5ea880492bbf48ba421c67828 Mon Sep 17 00:00:00 2001 From: Aoran Zeng Date: Mon, 2 Feb 2026 15:57:50 +0800 Subject: [PATCH 8/8] Auto-complete `:` on mobile too --- lib/rdoc/generator/template/aliki/js/aliki.js | 48 +++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/lib/rdoc/generator/template/aliki/js/aliki.js b/lib/rdoc/generator/template/aliki/js/aliki.js index 4b413bfc5c..286ba3ca94 100644 --- a/lib/rdoc/generator/template/aliki/js/aliki.js +++ b/lib/rdoc/generator/template/aliki/js/aliki.js @@ -75,6 +75,28 @@ function createSearchInstance(input, result) { return search; } +// Auto-complete namespace separator '::' when user types ':' +// For both desktop and mobile search inputs +function autoCompleteColons(input, e) { + if (e.key !== ':' || e.ctrlKey || e.metaKey || e.altKey) { + return; + } + + const start = input.selectionStart; + const end = input.selectionEnd; + if (start === null || end === null) { + return; + } + + e.preventDefault(); + + const value = input.value; + input.value = value.slice(0, start) + '::' + value.slice(end); + + const caret = start + 2; + input.setSelectionRange(caret, caret); +} + function hookSearch() { const input = document.querySelector('#search-field'); const result = document.querySelector('#search-results-desktop'); @@ -111,26 +133,8 @@ function hookSearch() { } }); - // Auto-complete namespace separator when user types ':' - input.addEventListener('keydown', (e) => { - if (e.key !== ':' || e.ctrlKey || e.metaKey || e.altKey) { - return; - } - - const start = input.selectionStart; - const end = input.selectionEnd; - if (start === null || end === null) { - return; - } - - e.preventDefault(); - - const value = input.value; - input.value = value.slice(0, start) + '::' + value.slice(end); - - const caret = start + 2; - input.setSelectionRange(caret, caret); - }); + // Auto-complete '::' when user types ':' + input.addEventListener('keydown', (e) => autoCompleteColons(input, e)); // Keyboard shortcuts for search // "/" for global search @@ -440,6 +444,10 @@ function hookSearchModal() { } }); + // Auto-complete '::' when user types ':' + searchInput.addEventListener('keydown', (e) => autoCompleteColons(searchInput, e)); + + // Check for ?q= URL parameter on mobile and open modal if (typeof URLSearchParams !== 'undefined') { const urlParams = new URLSearchParams(window.location.search);