|
1 | | -{{ define "left" }} |
2 | | - {{ partial "sidebar/mainnav.html" . }} |
3 | | -{{ end }} |
4 | | - |
5 | | -{{ define "main" }} |
6 | | - <article class="prose dark:prose-invert max-w-none"> |
7 | | - <h1 class="py-4">{{ .Title }}</h1> |
8 | | - {{ .Content }} |
9 | | - <div class="not-prose"> |
10 | | - <div class="flex justify-between gap-8 flex-row"> |
11 | | - <input type="search" id="search-page-input" |
12 | | - class="ring-3-gray-light-200 dark:ring-3-gray-dark-400 dark:bg-background-dark focus:ring-3-blue-light dark:focus:ring-3-blue-dark ring-3-[1.5px] w-full max-w-xl min-w-0 rounded-sm bg-white px-4 py-2 outline-hidden" |
13 | | - placeholder="Search…" tabindex="0" /> |
14 | | - <div class="admonition flex flex-row items-center gap-1"> |
15 | | - <p>Not finding what you're looking for? Try</p> |
16 | | - <button onclick="askAI('search-page-input')" class="topbar-button bg-blue-400/95 border-blue-300 hover:bg-blue-400/90"> |
17 | | - <span>Ask AI</span> |
18 | | - <span class="icon-svg"> |
19 | | - {{ partial "utils/svg.html" "/icons/sparkle.svg" }} |
20 | | - </span> |
21 | | - </button> |
22 | | - </div> |
23 | | - </div> |
24 | | - <hr class="border-divider-light dark:border-divider-dark" /> |
25 | | - <div id="search-page-results"> |
26 | | - <!-- results --> |
| 1 | +{{ define "left" }}{{ partial "sidebar/mainnav.html" . }}{{ end }} {{ define |
| 2 | +"main" }} |
| 3 | +<article class="prose dark:prose-invert max-w-none"> |
| 4 | + <h1 class="py-4">{{ .Title }}</h1> |
| 5 | + {{ .Content }} |
| 6 | + <div class="not-prose"> |
| 7 | + <div class="flex flex-col items-center justify-between gap-8 lg:flex-row"> |
| 8 | + <input |
| 9 | + type="search" |
| 10 | + id="search-page-input" |
| 11 | + class="ring-3-gray-light-200 dark:ring-3-gray-dark-400 dark:bg-background-dark focus:ring-3-blue-light dark:focus:ring-3-blue-dark ring-3-[1.5px] outline-hidden w-full min-w-0 rounded-sm bg-white px-4 py-2 lg:max-w-xl" |
| 12 | + placeholder="Search…" |
| 13 | + tabindex="0" |
| 14 | + /> |
| 15 | + <div class="flex items-center gap-2"> |
| 16 | + <p class="text-sm text-gray-600 dark:text-gray-400"> |
| 17 | + Not finding what you're looking for? Try |
| 18 | + </p> |
| 19 | + <button |
| 20 | + onclick="askAI('search-page-input')" |
| 21 | + class="topbar-button open-kapa-widget" |
| 22 | + > |
| 23 | + <span>Ask AI</span> |
| 24 | + <span class="icon-svg"> |
| 25 | + {{ partial "utils/svg.html" "/icons/sparkle.svg" }} |
| 26 | + </span> |
| 27 | + </button> |
27 | 28 | </div> |
28 | 29 | </div> |
29 | | - </article> |
30 | | - <script type="module"> |
31 | | - // Global variable to hold the pagefind module |
32 | | - let pagefind; |
33 | | - |
34 | | - // Initialize the pagefind module and fire a search if the query parameter exists |
35 | | - window.addEventListener("load", async function () { |
36 | | - // Hydrate pagefind |
37 | | - pagefind = await import("/pagefind/pagefind.js"); |
38 | | - await pagefind.options({ |
39 | | - ranking: { |
40 | | - termFrequency: 0.2, |
41 | | - pageLength: 0.75, |
42 | | - termSaturation: 1.4, |
43 | | - termSimilarity: 6.0, |
44 | | - }, |
45 | | - }); |
46 | | - |
47 | | - // Get the query parameter from the URL |
48 | | - const urlParams = new URLSearchParams(window.location.search); |
49 | | - const query = urlParams.get("q"); |
50 | | - |
51 | | - // If no query parameter is set, return |
52 | | - if (!query) { |
53 | | - return; |
54 | | - } |
55 | | - |
56 | | - const searchInput = document.getElementById("search-page-input"); |
57 | | - |
58 | | - // Set the value of the input field to the query parameter |
59 | | - searchInput.value = query; |
60 | | - |
61 | | - // Trigger the input event to simulate user typing |
62 | | - const event = new Event("input", { |
63 | | - bubbles: true, |
64 | | - cancelable: true, |
65 | | - }); |
66 | | - // Trigger the input event for the search input |
67 | | - searchInput.dispatchEvent(event); |
68 | | - searchInput.focus(); |
| 30 | + <hr class="border-divider-light dark:border-divider-dark" /> |
| 31 | + <div id="search-page-results"> |
| 32 | + <!-- results --> |
| 33 | + </div> |
| 34 | + </div> |
| 35 | +</article> |
| 36 | +<script type="module"> |
| 37 | + // Global variable to hold the pagefind module |
| 38 | + let pagefind; |
| 39 | + |
| 40 | + // Initialize the pagefind module and fire a search if the query parameter exists |
| 41 | + window.addEventListener("load", async function () { |
| 42 | + // Hydrate pagefind |
| 43 | + pagefind = await import("/pagefind/pagefind.js"); |
| 44 | + await pagefind.options({ |
| 45 | + ranking: { |
| 46 | + termFrequency: 0.2, |
| 47 | + pageLength: 0.75, |
| 48 | + termSaturation: 1.4, |
| 49 | + termSimilarity: 6.0, |
| 50 | + }, |
69 | 51 | }); |
70 | 52 |
|
71 | | - const searchPageInput = document.querySelector("#search-page-input"); |
72 | | - const searchPageResults = document.querySelector("#search-page-results"); |
| 53 | + // Get the query parameter from the URL |
| 54 | + const urlParams = new URLSearchParams(window.location.search); |
| 55 | + const query = urlParams.get("q"); |
73 | 56 |
|
74 | | - // onPageSearch returns 10 results per query |
75 | | - async function onPageSearch(e) { |
76 | | - pagefind.init(); |
77 | | - const query = e.target.value; |
78 | | - |
79 | | - // Set the query parameter in the URL |
80 | | - const params = new URLSearchParams(document.location.search); |
81 | | - params.set("q", query); |
| 57 | + // If no query parameter is set, return |
| 58 | + if (!query) { |
| 59 | + return; |
| 60 | + } |
82 | 61 |
|
83 | | - // Default the current page to 1 |
84 | | - let currentPage = 1; |
| 62 | + const searchInput = document.getElementById("search-page-input"); |
85 | 63 |
|
86 | | - // Check if the page parameter exists |
87 | | - const page = params.get("page"); |
88 | | - // Calculate the range start based on the page parameter |
89 | | - if (page) { |
90 | | - currentPage = parseInt(page); |
91 | | - } |
92 | | - const rangeStart = (currentPage - 1) * 10; |
93 | | - const rangeEnd = rangeStart + 10; |
| 64 | + // Set the value of the input field to the query parameter |
| 65 | + searchInput.value = query; |
94 | 66 |
|
95 | | - // Execute the search |
96 | | - const search = await pagefind.debouncedSearch(query); |
97 | | - // If no search results are found, exit |
98 | | - if (search === null) { |
| 67 | + // Trigger the input event to simulate user typing |
| 68 | + const event = new Event("input", { |
| 69 | + bubbles: true, |
| 70 | + cancelable: true, |
| 71 | + }); |
| 72 | + // Trigger the input event for the search input |
| 73 | + searchInput.dispatchEvent(event); |
| 74 | + searchInput.focus(); |
| 75 | + }); |
| 76 | + |
| 77 | + const searchPageInput = document.querySelector("#search-page-input"); |
| 78 | + const searchPageResults = document.querySelector("#search-page-results"); |
| 79 | + |
| 80 | + // onPageSearch returns 10 results per query |
| 81 | + async function onPageSearch(e) { |
| 82 | + pagefind.init(); |
| 83 | + const query = e.target.value; |
| 84 | + |
| 85 | + // Set the query parameter in the URL |
| 86 | + const params = new URLSearchParams(document.location.search); |
| 87 | + params.set("q", query); |
| 88 | + |
| 89 | + // Default the current page to 1 |
| 90 | + let currentPage = 1; |
| 91 | + |
| 92 | + // Check if the page parameter exists |
| 93 | + const page = params.get("page"); |
| 94 | + // Calculate the range start based on the page parameter |
| 95 | + if (page) { |
| 96 | + currentPage = parseInt(page); |
| 97 | + } |
| 98 | + const rangeStart = (currentPage - 1) * 10; |
| 99 | + const rangeEnd = rangeStart + 10; |
| 100 | + |
| 101 | + // Execute the search |
| 102 | + const search = await pagefind.debouncedSearch(query); |
| 103 | + // If no search results are found, exit |
| 104 | + if (search === null) { |
| 105 | + return; |
| 106 | + } else { |
| 107 | + // total number of results |
| 108 | + const resultsLength = search.results.length; |
| 109 | + // Get the data for the search results |
| 110 | + // Slice the results based on the range start + 10 |
| 111 | + const resultsData = await Promise.all( |
| 112 | + search.results.slice(rangeStart, rangeEnd).map((r) => r.data()), |
| 113 | + ); |
| 114 | + // If the range does not have any results, display a message |
| 115 | + if (resultsData.length === 0) { |
| 116 | + searchPageResults.innerHTML = `<div class="p-4">No results found</div>`; |
99 | 117 | return; |
| 118 | + } |
| 119 | + // Add an index to the results, for heap tracking |
| 120 | + const results = resultsData.map((item, index) => ({ |
| 121 | + ...item, |
| 122 | + index: index + 1, |
| 123 | + })); |
| 124 | + |
| 125 | + // If the query is not empty, display the search results container |
| 126 | + if (query) { |
| 127 | + searchPageResults.classList.remove("hidden"); |
100 | 128 | } else { |
101 | | - // total number of results |
102 | | - const resultsLength = search.results.length; |
103 | | - // Get the data for the search results |
104 | | - // Slice the results based on the range start + 10 |
105 | | - const resultsData = await Promise.all( |
106 | | - search.results.slice(rangeStart, rangeEnd).map((r) => r.data()), |
107 | | - ); |
108 | | - // If the range does not have any results, display a message |
109 | | - if (resultsData.length === 0) { |
110 | | - searchPageResults.innerHTML = `<div class="p-4">No results found</div>`; |
111 | | - return; |
112 | | - } |
113 | | - // Add an index to the results, for heap tracking |
114 | | - const results = resultsData.map((item, index) => ({ |
115 | | - ...item, |
116 | | - index: index + 1, |
117 | | - })); |
118 | | - |
119 | | - // If the query is not empty, display the search results container |
120 | | - if (query) { |
121 | | - searchPageResults.classList.remove("hidden"); |
122 | | - } else { |
123 | | - searchPageResults.classList.add("hidden"); |
124 | | - } |
| 129 | + searchPageResults.classList.add("hidden"); |
| 130 | + } |
125 | 131 |
|
126 | | - // Generate the search results HTML |
127 | | - let resultsHTML = `<div class="text-gray-400 dark:text-gray-500 p-2">${resultsLength} results</div>`; |
128 | | - |
129 | | - // Map results to HTML |
130 | | - resultsHTML += results |
131 | | - .map((item) => { |
132 | | - return `<div class="p-4"> |
133 | | - <div class="flex flex-col"> |
134 | | - <span class="text-gray-400 dark:texty-gray-dark text-sm">${item.meta.breadcrumbs}</span> |
135 | | - <a class="link" href="${item.url}" data-query="${query}" data-index="${item.index}">${item.meta.title}</a> |
136 | | - <p class="text-black dark:text-white overflow-hidden">…${item.excerpt}…</p> |
137 | | - </div> |
138 | | - </div>`; |
139 | | - }) |
140 | | - .join(""); |
141 | | - // If the results length is greater than 10, display links to show more results |
142 | | - if (resultsLength > 10) { |
143 | | - resultsHTML += `<hr class="border-divider-light dark:border-divider-dark">`; |
144 | | - resultsHTML += `<ul class="flex flex-wrap gap-1 pt-4 pb-8 justify-center text-sm">`; |
145 | | - for (let i = 1; i <= resultsLength / 10; i++) { |
146 | | - if (i == currentPage) { |
147 | | - resultsHTML += `<li class="flex items-center justify-center"> |
| 132 | + // Generate the search results HTML |
| 133 | + let resultsHTML = `<div class="text-gray-400 dark:text-gray-500 p-2">${resultsLength} results</div>`; |
| 134 | + |
| 135 | + // Map results to HTML |
| 136 | + resultsHTML += results |
| 137 | + .map((item) => { |
| 138 | + return `<div class="p-4"> |
| 139 | +<div class="flex flex-col"> |
| 140 | + <span class="text-gray-400 dark:texty-gray-dark text-sm">${item.meta.breadcrumbs}</span> |
| 141 | + <a class="link" style="word-break: break-word; overflow-wrap: anywhere;" href="${item.url}" data-query="${query}" data-index="${item.index}">${item.meta.title}</a> |
| 142 | + <p class="text-black dark:text-white overflow-hidden" style="word-break: break-word; overflow-wrap: anywhere;">…${item.excerpt}…</p> |
| 143 | +</div> |
| 144 | +</div>`; |
| 145 | + }) |
| 146 | + .join(""); |
| 147 | + // If the results length is greater than 10, display links to show more results |
| 148 | + if (resultsLength > 10) { |
| 149 | + resultsHTML += `<hr class="border-divider-light dark:border-divider-dark">`; |
| 150 | + resultsHTML += `<ul class="flex flex-wrap gap-1 pt-4 pb-8 justify-center text-sm">`; |
| 151 | + for (let i = 1; i <= resultsLength / 10; i++) { |
| 152 | + if (i == currentPage) { |
| 153 | + resultsHTML += `<li class="flex items-center justify-center"> |
148 | 154 | <a href="/search/?q=${query}&page=${i}" class="pagination-link bg-gray-200 dark:bg-gray-800 dark:text-gray-200">${i}</a> |
149 | 155 | </li>`; |
150 | | - } else { |
151 | | - resultsHTML += `<li class="flex items-center justify-center"> |
| 156 | + } else { |
| 157 | + resultsHTML += `<li class="flex items-center justify-center"> |
152 | 158 | <a href="/search/?q=${query}&page=${i}" class="pagination-link bg-gray-100 dark:bg-gray-900 dark:text-gray-400">${i}</a> |
153 | 159 | </li>`; |
154 | | - } |
155 | 160 | } |
156 | | - resultsHTML += `</ul>`; |
157 | 161 | } |
158 | | - |
159 | | - searchPageResults.innerHTML = resultsHTML; |
| 162 | + resultsHTML += `</ul>`; |
160 | 163 | } |
161 | | - } |
162 | 164 |
|
163 | | - searchPageInput.addEventListener("input", (e) => onPageSearch(e)); |
164 | | - |
165 | | - // Event delegation for tracking link clicks |
166 | | - if (window.heap !== undefined) { |
167 | | - searchPageResults.addEventListener("click", function (event) { |
168 | | - if (event.target.tagName === "A" && event.target.closest(".link")) { |
169 | | - const searchQuery = event.target.getAttribute("data-query"); |
170 | | - const resultIndex = event.target.getAttribute("data-index"); |
171 | | - const url = new URL(event.target.href); |
172 | | - const properties = { |
173 | | - docs_search_target_path: url.pathname, |
174 | | - docs_search_target_title: event.target.textContent, |
175 | | - docs_search_query_text: searchQuery, |
176 | | - docs_search_target_index: resultIndex, |
177 | | - docs_search_source_path: window.location.pathname, |
178 | | - docs_search_source_title: document.title, |
179 | | - }; |
180 | | - heap.track("Docs - Search - Click - Result Link", properties); |
181 | | - } |
182 | | - }); |
| 165 | + searchPageResults.innerHTML = resultsHTML; |
183 | 166 | } |
184 | | - </script> |
| 167 | + } |
| 168 | + |
| 169 | + searchPageInput.addEventListener("input", (e) => onPageSearch(e)); |
| 170 | + |
| 171 | + // Event delegation for tracking link clicks |
| 172 | + if (window.heap !== undefined) { |
| 173 | + searchPageResults.addEventListener("click", function (event) { |
| 174 | + if (event.target.tagName === "A" && event.target.closest(".link")) { |
| 175 | + const searchQuery = event.target.getAttribute("data-query"); |
| 176 | + const resultIndex = event.target.getAttribute("data-index"); |
| 177 | + const url = new URL(event.target.href); |
| 178 | + const properties = { |
| 179 | + docs_search_target_path: url.pathname, |
| 180 | + docs_search_target_title: event.target.textContent, |
| 181 | + docs_search_query_text: searchQuery, |
| 182 | + docs_search_target_index: resultIndex, |
| 183 | + docs_search_source_path: window.location.pathname, |
| 184 | + docs_search_source_title: document.title, |
| 185 | + }; |
| 186 | + heap.track("Docs - Search - Click - Result Link", properties); |
| 187 | + } |
| 188 | + }); |
| 189 | + } |
| 190 | +</script> |
185 | 191 | {{ end }} |
0 commit comments