11<script setup lang="ts">
2- import { ref , computed , watch , onMounted , onUnmounted } from ' vue'
3- import { useData } from ' vitepress'
2+ import { ref , watch , onMounted } from ' vue'
43import { onKeyStroke } from ' @vueuse/core'
54
6- const { isDark } = useData ()
7-
85interface SearchResult {
96 id: string
107 title: string
@@ -57,12 +54,27 @@ defineExpose({
5754 openSearch
5855})
5956
60- const closeSearch = () => {
61- isOpen .value = false
57+ const clearSearch = () => {
58+ // Cancel any ongoing search operations
59+ if (searchTimeout ) {
60+ clearTimeout (searchTimeout )
61+ searchTimeout = null
62+ }
63+ if (abortController ) {
64+ abortController .abort ()
65+ abortController = null
66+ }
67+
6268 searchQuery .value = ' '
6369 searchResults .value = []
6470 selectedIndex .value = 0
6571 searchError .value = null
72+ isLoading .value = false
73+ }
74+
75+ const closeSearch = () => {
76+ clearSearch ()
77+ isOpen .value = false
6678}
6779
6880// Keyboard shortcuts (only for modal interactions)
@@ -94,15 +106,23 @@ onKeyStroke('Enter', () => {
94106
95107// Debounced search
96108let searchTimeout: NodeJS .Timeout | null = null
109+ let abortController: AbortController | null = null
97110
98111watch (searchQuery , async (newQuery ) => {
112+ // Cancel previous timeout and abort any ongoing request
99113 if (searchTimeout ) {
100114 clearTimeout (searchTimeout )
115+ searchTimeout = null
116+ }
117+ if (abortController ) {
118+ abortController .abort ()
119+ abortController = null
101120 }
102121
103122 if (! newQuery .trim ()) {
104123 searchResults .value = []
105124 searchError .value = null
125+ isLoading .value = false
106126 return
107127 }
108128
@@ -118,6 +138,16 @@ const performSearch = async (query: string) => {
118138 return
119139 }
120140
141+ if (! korrektlyConfig .datasetId || ! korrektlyConfig .apiToken ) {
142+ console .error (' Korrektly dataset ID not configured' )
143+ searchError .value = ' Search is not properly configured. Please check the environment variables.'
144+ return
145+ }
146+
147+ // Create new abort controller for this search
148+ abortController = new AbortController ()
149+ const currentAbortController = abortController
150+
121151 isLoading .value = true
122152 searchError .value = null
123153 try {
@@ -127,6 +157,11 @@ const performSearch = async (query: string) => {
127157 search_type: ' hybrid' ,
128158 })
129159
160+ // Check if this request was aborted
161+ if (currentAbortController .signal .aborted ) {
162+ return
163+ }
164+
130165 // Check if response contains an error
131166 if (response ?.error || response ?.message ) {
132167 const errorMessage = response .message || response .error || ' An unknown error occurred'
@@ -148,7 +183,10 @@ const performSearch = async (query: string) => {
148183
149184 const title = getMetadata (' title' ) || getMetadata (' heading' ) || extractTitle (chunk .content_html ) || ' Untitled'
150185 const description = getMetadata (' description' ) || ' '
151- const hierarchy = getMetadata (' hierarchy' ) || ' '
186+
187+ // Handle hierarchy as array
188+ const hierarchyMeta = chunk .metadata ?.find ((m : any ) => m .key === ' hierarchy' )
189+ const hierarchy = hierarchyMeta ?.value || []
152190
153191 // Build URL from source_url or group tracking_id
154192 let url = chunk .source_url || ' '
@@ -161,17 +199,15 @@ const performSearch = async (query: string) => {
161199 url = url .replace (' .md' , ' ' )
162200 }
163201
164- // Create breadcrumb from hierarchy or URL (excluding 'docs' prefix)
202+ // Create breadcrumb from hierarchy array or URL (excluding 'docs' prefix)
165203 let breadcrumb = ' '
166- if (hierarchy ) {
167- // Convert "home > aditya > workspace > coollabs > coolify-docs > docs > services > n8n"
168- // to "services / n8n"
169- const parts = hierarchy .split (' > ' )
170- const docsIndex = parts .indexOf (' docs' )
171- if (docsIndex !== - 1 && docsIndex < parts .length - 1 ) {
172- breadcrumb = parts .slice (docsIndex + 1 ).join (' / ' )
204+ if (Array .isArray (hierarchy ) && hierarchy .length > 0 ) {
205+ // Find 'docs' in the hierarchy array and take everything after it
206+ const docsIndex = hierarchy .indexOf (' docs' )
207+ if (docsIndex !== - 1 && docsIndex < hierarchy .length - 1 ) {
208+ breadcrumb = hierarchy .slice (docsIndex + 1 ).join (' / ' )
173209 } else {
174- breadcrumb = hierarchy .replace ( / > / g , ' / ' )
210+ breadcrumb = hierarchy .join ( ' / ' )
175211 }
176212 } else if (url ) {
177213 // Extract from URL: /docs/services/n8n -> services / n8n
@@ -191,6 +227,11 @@ const performSearch = async (query: string) => {
191227
192228 selectedIndex .value = 0
193229 } catch (error : any ) {
230+ // Don't show error if the request was aborted
231+ if (currentAbortController .signal .aborted ) {
232+ return
233+ }
234+
194235 console .error (' Search error:' , error )
195236
196237 // Try to extract error message from the error object
@@ -207,7 +248,10 @@ const performSearch = async (query: string) => {
207248 searchError .value = errorMessage
208249 searchResults .value = []
209250 } finally {
210- isLoading .value = false
251+ // Only reset loading if not aborted
252+ if (! currentAbortController .signal .aborted ) {
253+ isLoading .value = false
254+ }
211255 }
212256}
213257
@@ -283,7 +327,8 @@ const truncate = (text: string, length: number) => {
283327 <button
284328 v-if =" searchQuery"
285329 class =" absolute right-3 w-6 h-6 flex items-center justify-center rounded bg-[var(--vp-c-bg-soft)] text-[var(--vp-c-text-2)] hover:bg-[var(--vp-c-bg-elv)] hover:text-[var(--vp-c-text-1)] transition-all text-xl leading-none"
286- @click =" searchQuery = ''"
330+ @click =" clearSearch"
331+ title =" Clear search"
287332 >
288333 ×
289334 </button >
@@ -353,7 +398,6 @@ const truncate = (text: string, length: number) => {
353398
354399 <!-- Initial State -->
355400 <div v-else class =" py-12 px-6 text-center text-[var(--vp-c-text-2)]" >
356- <p class =" text-sm mb-6" >Start typing to search...</p >
357401 <div class =" mt-6" >
358402 <p class =" text-xs font-semibold text-[var(--vp-c-text-2)] mb-3 uppercase tracking-wide" >Popular searches:</p >
359403 <div class =" flex flex-wrap gap-2 justify-center" >
0 commit comments