diff --git a/.gitmodules b/.gitmodules index f0f5637..1f7b564 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "doxygen-awesome-css"] path = doxygen-awesome-css - url = https://github.com/jothepro/doxygen-awesome-css.git - branch = main + url = https://github.com/ReenigneArcher/doxygen-awesome-css.git + branch = lizardbyte diff --git a/CMakeLists.txt b/CMakeLists.txt index 11ff7ad..b357fdc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,7 +32,6 @@ file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/doxyconfig-header.html" DESTINATION "${SO file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/doxyconfig-Doxyfile" DESTINATION "${SOURCE_DOCS_DIR}") file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/doxyconfig-icons.js" DESTINATION "${SOURCE_DOCS_DIR}") file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/doxyconfig.css" DESTINATION "${SOURCE_DOCS_DIR}") -file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/doxyconfig-readthedocs-search.js" DESTINATION "${SOURCE_DOCS_DIR}") # append the "${SOURCE_DOCS_DIR}/Doxyfile to the Doxyfile-doxyconfig file(READ "${SOURCE_DOCS_DIR}/Doxyfile" DOXYFILE_CONTENTS) diff --git a/doxyconfig-Doxyfile b/doxyconfig-Doxyfile index f66dbbe..c917baa 100644 --- a/doxyconfig-Doxyfile +++ b/doxyconfig-Doxyfile @@ -44,12 +44,12 @@ HTML_EXTRA_FILES += ../third-party/doxyconfig/doxygen-awesome-css/doxygen-awesom HTML_EXTRA_FILES += ../third-party/doxyconfig/doxygen-awesome-css/doxygen-awesome-paragraph-link.js HTML_EXTRA_FILES += ../third-party/doxyconfig/doxygen-awesome-css/doxygen-awesome-interactive-toc.js HTML_EXTRA_FILES += ../third-party/doxyconfig/doxygen-awesome-css/doxygen-awesome-tabs.js +HTML_EXTRA_FILES += ../third-party/doxyconfig/doxygen-awesome-css/doxygen-awesome-readthedocs-search.js HTML_EXTRA_FILES += doxyconfig-icons.js HTML_EXTRA_STYLESHEET = ../third-party/doxyconfig/doxygen-awesome-css/doxygen-awesome.css +HTML_EXTRA_STYLESHEET += ../third-party/doxyconfig/doxygen-awesome-css/doxygen-awesome-readthedocs-search.css HTML_HEADER = doxyconfig-header.html -# readthedocs custom search -HTML_EXTRA_FILES += doxyconfig-readthedocs-search.js HTML_EXTRA_STYLESHEET += doxyconfig.css # custom aliases diff --git a/doxyconfig-header.html b/doxyconfig-header.html index 64cfb83..174eaf6 100644 --- a/doxyconfig-header.html +++ b/doxyconfig-header.html @@ -42,7 +42,7 @@ - + diff --git a/doxyconfig-readthedocs-search.js b/doxyconfig-readthedocs-search.js deleted file mode 100644 index 0d03899..0000000 --- a/doxyconfig-readthedocs-search.js +++ /dev/null @@ -1,392 +0,0 @@ -class ReadtheDocsSearch { - static searchResultsText=[ - "Sorry, no pages matching your query.", - "Search finished, found 1 page matching the search query.", - "Search finished, found $num pages matching the search query.", - ]; - - static get serverUrl() { - const serverUrlSuffix = '_/api/v3/'; - const domainName = globalThis.location.hostname; - console.log(`Domain name: ${domainName}`); - - if (domainName === 'localhost') { - let tmpServerUrl = serverUrl; - while (tmpServerUrl.endsWith('/')) { - tmpServerUrl = tmpServerUrl.slice(0, -1); - } - console.warn('Localhost detected, you probably need to bypass CORS'); - return `${tmpServerUrl}/${serverUrlSuffix}`; - } - return `https://${domainName}/${serverUrlSuffix}`; - } - - static init() { - const realSearchBox = globalThis.SearchBox; - globalThis.SearchBox = function(name, resultsPath, extension) { - if (realSearchBox) { - realSearchBox.call(this, name, resultsPath, extension); - } - - this.OnSearchFieldFocus = function() {}; - - const originalClose = this.CloseResultsWindow; - this.CloseResultsWindow = function() { - if (originalClose) { - originalClose.call(this); - } - const field = this.DOMSearchField ? this.DOMSearchField() : null; - if (field?.id === document.activeElement?.id) { - return; - } - }; - }; - - // Re-focus the search input if it is replaced in the DOM while focused. - // On mobile, viewport resize (triggered by the OSK opening) can cause - // menu.js resetState() to recreate the input element, losing focus. - const observer = new MutationObserver(function() { - const field = document.getElementById('MSearchField'); - if (field && ReadtheDocsSearch._searchFieldHadFocus) { - field.focus(); - } - }); - - if (document.body) { - observer.observe(document.body, { childList: true, subtree: true }); - ReadtheDocsSearch._attachLiveSearch(); - } else { - document.addEventListener('DOMContentLoaded', function() { - observer.observe(document.body, { childList: true, subtree: true }); - ReadtheDocsSearch._attachLiveSearch(); - }); - } - - document.addEventListener('focusin', function(e) { - if (e.target?.id === 'MSearchField') { - ReadtheDocsSearch._searchFieldHadFocus = true; - } - }); - document.addEventListener('focusout', function(e) { - if (e.target?.id === 'MSearchField') { - setTimeout(function() { - ReadtheDocsSearch._searchFieldHadFocus = false; - }, 0); - } - }); - - document.addEventListener('click', function(e) { - const dropdown = document.getElementById('RTDLiveResults'); - const field = document.getElementById('MSearchField'); - if (dropdown && !dropdown.contains(e.target) && e.target !== field) { - ReadtheDocsSearch._hideLiveResults(); - } - }); - - globalThis.searchFor = function(query, page, count) { - const results = document.getElementById('searchresults'); - - // Get the title - const pageTitle = document.querySelector('div.title'); - const originalTitle = pageTitle ? pageTitle.textContent : ''; - let pageTitleStates = ["Searching", "Searching .", "Searching ..", "Searching ..."]; - let pageTitleIndex = 0; - - // Function to update the page title - function updatePageTitle() { - if (pageTitle) pageTitle.textContent = pageTitleStates[pageTitleIndex]; - pageTitleIndex = (pageTitleIndex + 1) % pageTitleStates.length; - } - - // Start the interval to update the page title - let titleInterval = setInterval(updatePageTitle, 500); - - // The summary will be displayed at the top of the search results - let resultSummary = document.createElement('p'); - resultSummary.className = 'search-summary'; - results.appendChild(resultSummary); - - // Put all results into an unordered list - let resultList = document.createElement('ul'); - resultList.className = 'search'; - results.appendChild(resultList); - - // readthedocs metadata - let projectSlug = ReadtheDocsSearch.getMetaValue("readthedocs-project-slug") || "doxyconfig"; - let projectVersion = ReadtheDocsSearch.getMetaValue("readthedocs-version-slug") || "latest"; - - const ctx = { - query, resultSummary, resultList, titleInterval, pageTitle, originalTitle, firstUrl: true - }; - - // pull requests are not indexed, so use the default version - const versionReady = /^\d+$/.test(projectVersion) - ? ReadtheDocsSearch.getReadTheDocsDefaultVersion(projectSlug) - : Promise.resolve(projectVersion); - - versionReady.then(function(resolvedVersion) { - const url = `${ReadtheDocsSearch.serverUrl}search/?q=project:${projectSlug}/${resolvedVersion}+${query}&page=${page + 1}&page_size=${count}`; - console.log(url); - ReadtheDocsSearch.fetchResults(url, ctx); - }); - } - } - - static _attachLiveSearch() { - let debounceTimer = null; - - function onInput() { - clearTimeout(debounceTimer); - const field = document.getElementById('MSearchField'); - if (!field) return; - const query = field.value.trim(); - if (!query) { - ReadtheDocsSearch._hideLiveResults(); - return; - } - debounceTimer = setTimeout(function() { - ReadtheDocsSearch._runLiveSearch(query); - }, 300); - } - - function attachToField() { - const field = document.getElementById('MSearchField'); - if (!field || field._rtdLiveSearchAttached) return; - field._rtdLiveSearchAttached = true; - field.setAttribute('autocomplete', 'off'); - field.addEventListener('input', onInput); - field.addEventListener('keydown', function(e) { - if (e.key === 'Escape') { - ReadtheDocsSearch._hideLiveResults(); - } else if (e.key === 'ArrowDown') { - e.preventDefault(); - ReadtheDocsSearch._moveLiveResultFocus(1); - } else if (e.key === 'ArrowUp') { - e.preventDefault(); - ReadtheDocsSearch._moveLiveResultFocus(-1); - } else if (e.key === 'Enter') { - const focused = document.querySelector('#RTDLiveResults .rtd-live-item:focus'); - if (focused) { - e.preventDefault(); - focused.click(); - } - } - }); - } - - attachToField(); - - // Re-attach when the DOM changes (e.g. menu.js recreates the field on mobile) - const fieldObserver = new MutationObserver(attachToField); - fieldObserver.observe(document.body, { childList: true, subtree: true }); - } - - static _runLiveSearch(query) { - const projectSlug = ReadtheDocsSearch.getMetaValue("readthedocs-project-slug") || "doxyconfig"; - const projectVersion = ReadtheDocsSearch.getMetaValue("readthedocs-version-slug") || "latest"; - - const versionReady = /^\d+$/.test(projectVersion) - ? ReadtheDocsSearch.getReadTheDocsDefaultVersion(projectSlug) - : Promise.resolve(projectVersion); - - versionReady.then(function(resolvedVersion) { - const url = `${ReadtheDocsSearch.serverUrl}search/?q=project:${projectSlug}/${resolvedVersion}+${query}&page=1&page_size=5`; - fetch(url) - .then(function(response) { - if (!response.ok) throw new Error(response.statusText); - return response.json(); - }) - .then(function(data) { - ReadtheDocsSearch._showLiveResults(query, data.results || []); - }) - .catch(function() { - ReadtheDocsSearch._hideLiveResults(); - }); - }); - } - - static _showLiveResults(query, results) { - let dropdown = document.getElementById('RTDLiveResults'); - if (!dropdown) { - dropdown = document.createElement('div'); - dropdown.id = 'RTDLiveResults'; - document.body.appendChild(dropdown); - } - - dropdown.innerHTML = ''; - - if (results.length === 0) { - const empty = document.createElement('div'); - empty.className = 'rtd-live-empty'; - empty.textContent = 'No results found.'; - dropdown.appendChild(empty); - } else { - const list = document.createElement('ul'); - list.className = 'rtd-live-list'; - for (const item of results) { - const li = document.createElement('li'); - const a = document.createElement('a'); - a.className = 'rtd-live-item'; - a.href = `${item.domain}${item.path}`; - a.tabIndex = 0; - - const title = document.createElement('span'); - title.className = 'rtd-live-title'; - title.textContent = item.title; - a.appendChild(title); - - if (item.blocks && item.blocks.length > 0) { - const snippet = document.createElement('span'); - snippet.className = 'rtd-live-snippet'; - let snipHtml = item.blocks[0].highlights.content.join(' … '); - snipHtml = snipHtml.replaceAll(/(.*?)<\/span>/g, '$1'); - snippet.innerHTML = snipHtml; - a.appendChild(snippet); - } - - li.appendChild(a); - list.appendChild(li); - } - dropdown.appendChild(list); - } - - ReadtheDocsSearch._positionLiveResults(dropdown); - dropdown.style.display = 'block'; - } - - static _positionLiveResults(dropdown) { - const field = document.getElementById('MSearchField'); - const box = document.getElementById('MSearchBox'); - const anchor = box || field; - if (!anchor) return; - const rect = anchor.getBoundingClientRect(); - const scrollY = window.scrollY; - const viewportWidth = document.documentElement.clientWidth; - dropdown.style.top = `${rect.bottom + scrollY}px`; - dropdown.style.left = ''; - dropdown.style.right = `${viewportWidth - rect.right}px`; - dropdown.style.minWidth = `${Math.max(rect.width, 260)}px`; - } - - static _hideLiveResults() { - const dropdown = document.getElementById('RTDLiveResults'); - if (dropdown) { - dropdown.style.display = 'none'; - } - } - - static _moveLiveResultFocus(direction) { - const items = Array.from(document.querySelectorAll('#RTDLiveResults .rtd-live-item')); - if (items.length === 0) return; - const current = document.activeElement; - const idx = items.indexOf(current); - let next; - if (idx === -1) { - next = direction > 0 ? items[0] : items.at(-1); - } else { - next = items[idx + direction]; - } - if (next) next.focus(); - } - - static fetchResults(url, ctx) { - fetch(url) - .then(function(response) { - if (!response.ok) throw new Error(response.statusText); - return response.json(); - }) - .then(function(data) { - // Add the query to the search field - // This seems only be working if applied in the fetch success handler... - // maybe the field is not available before this point - const searchField = document.getElementById('MSearchField'); - if (searchField) searchField.value = ctx.query; - - if (ctx.firstUrl) { - if (data.count === 1) { - ctx.resultSummary.innerHTML = ReadtheDocsSearch.searchResultsText[1]; - } else if (data.count > 1) { - ctx.resultSummary.innerHTML = ReadtheDocsSearch.searchResultsText[2].replace(/\$num/, data.count); - } else { - ctx.resultSummary.innerHTML = ReadtheDocsSearch.searchResultsText[0]; - } - ctx.firstUrl = false; - } - - data.results.forEach(function(item) { - ReadtheDocsSearch.appendResultItem(ctx.resultList, item); - }); - - if (data.next) { - ReadtheDocsSearch.fetchResults(data.next, ctx); - } else { - // Clear the interval when the search is complete - clearInterval(ctx.titleInterval); - if (ctx.pageTitle) ctx.pageTitle.textContent = ctx.originalTitle; - } - }); - } - - static appendResultItem(resultList, item) { - const resultItem = document.createElement('li'); - resultItem.className = 'search-result'; - const resultItemUrl = `${item.domain}${item.path}`; - const resultItemTitleLink = document.createElement('a'); - const resultItemTitleHeading = document.createElement('h3'); - resultItemTitleHeading.appendChild(resultItemTitleLink); - resultItemTitleLink.href = resultItemUrl; - resultItemTitleLink.textContent = item.title; - resultItem.append(resultItemTitleHeading); - resultList.append(resultItem); - - const resultItemParagraph = document.createElement('p'); - resultItemParagraph.className = 'context'; - for (const block of item.blocks) { - let blockContent = block.highlights.content.join(', '); - blockContent = blockContent.replaceAll(/(.*?)<\/span>/g, '$1'); - resultItemParagraph.innerHTML += blockContent; - - const blockName = `#${block.title.toLowerCase().replaceAll(' ', '-')}`; - const blockUrl = resultItemUrl + blockName; - const blockLink = document.createElement('a'); - blockLink.href = blockUrl; - blockLink.textContent = "More..."; - resultItemParagraph.append(document.createTextNode(' ')); - resultItemParagraph.append(blockLink); - resultItemParagraph.append(document.createElement('br')); - } - resultItem.append(resultItemParagraph); - } - - // Function to extract the value of a specified Read the Docs meta property - static getMetaValue(propertyName) { - const metaTags = document.getElementsByTagName('meta'); - - for (let meta of metaTags) { - if (meta.name === propertyName) { - return meta.content; - } - } - - return null; - } - - static getReadTheDocsDefaultVersion(project) { - console.log('Pull request detected, getting default version from ReadTheDocs API'); - const url = `${ReadtheDocsSearch.serverUrl}projects/${project}/`; - return fetch(url) - .then(function(response) { - if (!response.ok) throw new Error(response.statusText); - return response.json(); - }) - .then(function(data) { - console.log(data); - return data.default_version || 'latest'; - }) - .catch(function(err) { - console.error('Error:', err); - console.log(`Cannot determine default version for ${project}, assuming "latest"`); - return 'latest'; - }); - } -} diff --git a/doxyconfig.css b/doxyconfig.css index 700ce61..ada60d2 100644 --- a/doxyconfig.css +++ b/doxyconfig.css @@ -1,24 +1,3 @@ -/* Highlight text in search results */ -span.highlighted { - background-color:var(--warning-color); - color: var(--page-foreground-color); - padding: 2px; - border-radius: 3px; -} - -/* Remove list bullets for search results */ -ul.search { - list-style-type: none; - padding: 0; -} - -/* Add horizontal divider for search results */ -ul.search li.search-result { - border-bottom: 1px solid var(--separator-color); - padding-bottom: 10px; - margin-bottom: 10px; -} - .lucide { display: inline-block; height: 1em; @@ -52,70 +31,3 @@ table.code-result-table td { vertical-align: top; width: 50%; } - -/* Live search dropdown */ -#RTDLiveResults { - display: none; - position: absolute; - z-index: 10000; - left: unset; - background: var(--page-background-color); - border: 1px solid var(--separator-color); - border-radius: var(--border-radius-large); - box-shadow: var(--box-shadow); - max-height: 420px; - overflow-y: auto; -} - -#RTDLiveResults .rtd-live-list { - list-style: none; - margin: 0; - padding: var(--spacing-small) 0; -} - -#RTDLiveResults .rtd-live-list li { - margin: 0; -} - -#RTDLiveResults .rtd-live-item { - display: block; - padding: var(--spacing-small) var(--spacing-medium); - color: var(--page-foreground-color); - text-decoration: none; - outline: none; -} - -#RTDLiveResults .rtd-live-item:hover, -#RTDLiveResults .rtd-live-item:focus { - background: var(--menu-focus-background); - color: var(--menu-focus-foreground); -} - -#RTDLiveResults .rtd-live-title { - display: block; - font-weight: bold; - font-size: var(--navigation-font-size); -} - -#RTDLiveResults .rtd-live-snippet { - display: block; - font-size: calc(var(--navigation-font-size) * 0.9); - color: var(--page-secondary-foreground-color); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 400px; -} - -#RTDLiveResults .rtd-live-snippet mark { - background-color: var(--warning-color); - color: var(--page-foreground-color); - padding: 0 2px; - border-radius: 2px; -} - -#RTDLiveResults .rtd-live-empty { - padding: var(--spacing-medium); - color: var(--page-secondary-foreground-color); - font-size: var(--navigation-font-size); -} diff --git a/doxygen-awesome-css b/doxygen-awesome-css index d52eafe..24c4f96 160000 --- a/doxygen-awesome-css +++ b/doxygen-awesome-css @@ -1 +1 @@ -Subproject commit d52eafe3e9303399fda15661f3d7bb8fe3d7eabc +Subproject commit 24c4f966e375ed92e9805e075177d34a9849a43e diff --git a/readthedocs_build.sh b/readthedocs_build.sh index 05185a9..b2092d8 100644 --- a/readthedocs_build.sh +++ b/readthedocs_build.sh @@ -57,7 +57,6 @@ function merge_doxyconfigs { cp "${DOXYCONFIG_DIR}/doxyconfig-header.html" "${docs_dir}" cp "${DOXYCONFIG_DIR}/doxyconfig-icons.js" "${docs_dir}" cp "${DOXYCONFIG_DIR}/doxyconfig.css" "${docs_dir}" - cp "${DOXYCONFIG_DIR}/doxyconfig-readthedocs-search.js" "${docs_dir}" cat "${docs_dir}Doxyfile" >> "${docs_dir}doxyconfig-Doxyfile" return 0 }