|
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