@@ -55,16 +55,7 @@ defineExpose({
5555})
5656
5757const 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-
58+ cancelOngoingSearch ()
6859 searchQuery .value = ' '
6960 searchResults .value = []
7061 selectedIndex .value = 0
@@ -108,8 +99,7 @@ onKeyStroke('Enter', () => {
10899let searchTimeout: NodeJS .Timeout | null = null
109100let abortController: AbortController | null = null
110101
111- watch (searchQuery , async (newQuery ) => {
112- // Cancel previous timeout and abort any ongoing request
102+ const cancelOngoingSearch = () => {
113103 if (searchTimeout ) {
114104 clearTimeout (searchTimeout )
115105 searchTimeout = null
@@ -118,6 +108,10 @@ watch(searchQuery, async (newQuery) => {
118108 abortController .abort ()
119109 abortController = null
120110 }
111+ }
112+
113+ watch (searchQuery , async (newQuery ) => {
114+ cancelOngoingSearch ()
121115
122116 if (! newQuery .trim ()) {
123117 searchResults .value = []
@@ -126,9 +120,7 @@ watch(searchQuery, async (newQuery) => {
126120 return
127121 }
128122
129- searchTimeout = setTimeout (async () => {
130- await performSearch (newQuery )
131- }, 300 )
123+ searchTimeout = setTimeout (() => performSearch (newQuery ), 300 )
132124})
133125
134126const performSearch = async (query : string ) => {
@@ -157,15 +149,10 @@ const performSearch = async (query: string) => {
157149 search_type: ' hybrid' ,
158150 })
159151
160- // Check if this request was aborted
161- if (currentAbortController .signal .aborted ) {
162- return
163- }
152+ if (currentAbortController .signal .aborted ) return
164153
165- // Check if response contains an error
166154 if (response ?.error || response ?.message ) {
167- const errorMessage = response .message || response .error || ' An unknown error occurred'
168- searchError .value = errorMessage
155+ searchError .value = response .message || response .error || ' An unknown error occurred'
169156 searchResults .value = []
170157 console .error (' Search API error:' , response )
171158 return
@@ -174,87 +161,65 @@ const performSearch = async (query: string) => {
174161 // The API returns { success, data: { results: [...] } }
175162 const results = response ?.data ?.results || response ?.results || response ?.chunks || []
176163
177- searchResults .value = results .map ((chunk : any ) => {
178- // Extract metadata from array format
179- const getMetadata = (key : string ) => {
180- const meta = chunk .metadata ?.find ((m : any ) => m .key === key )
181- return meta ?.value || ' '
182- }
183-
184- const title = getMetadata (' title' ) || getMetadata (' heading' ) || extractTitle (chunk .content_html ) || ' Untitled'
185- const description = getMetadata (' description' ) || ' '
186-
187- // Handle hierarchy as array
188- const hierarchyMeta = chunk .metadata ?.find ((m : any ) => m .key === ' hierarchy' )
189- const hierarchy = hierarchyMeta ?.value || []
190-
191- // Build URL from source_url or group tracking_id
192- let url = chunk .source_url || ' '
193- if (url .includes (' /home/aditya/workspace/coollabs/coolify-docs/docs' )) {
194- // Convert file path to URL path
195- url = url .replace (' /home/aditya/workspace/coollabs/coolify-docs/docs' , ' /docs' )
196- url = url .replace (' .md' , ' ' )
197- } else if (chunk .group ?.tracking_id ) {
198- url = chunk .group .tracking_id .replace (' /home/aditya/workspace/coollabs/coolify-docs/docs' , ' /docs' )
199- url = url .replace (' .md' , ' ' )
200- }
201-
202- // Create breadcrumb from hierarchy array or URL (excluding 'docs' prefix)
203- let breadcrumb = ' '
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 (' / ' )
209- } else {
210- breadcrumb = hierarchy .join (' / ' )
211- }
212- } else if (url ) {
213- // Extract from URL: /docs/services/n8n -> services / n8n
214- breadcrumb = url .replace (/ ^ \/ docs\/ / , ' ' ).replace (/ \/ / g , ' / ' )
215- }
216-
217- return {
218- id: chunk .id ,
219- title ,
220- content: description || chunk .content_html || chunk .content || ' ' ,
221- url ,
222- highlight: chunk .content_html ,
223- hierarchy ,
224- breadcrumb ,
225- }
226- })
164+ searchResults .value = results .map ((chunk : any ) => transformSearchResult (chunk ))
227165
228166 selectedIndex .value = 0
229167 } catch (error : any ) {
230- // Don't show error if the request was aborted
231- if (currentAbortController .signal .aborted ) {
232- return
233- }
168+ if (currentAbortController .signal .aborted ) return
234169
235170 console .error (' Search error:' , error )
236-
237- // Try to extract error message from the error object
238- let errorMessage = ' An unexpected error occurred while searching.'
239-
240- if (error ?.response ?.data ?.message ) {
241- errorMessage = error .response .data .message
242- } else if (error ?.message ) {
243- errorMessage = error .message
244- } else if (typeof error === ' string' ) {
245- errorMessage = error
246- }
247-
248- searchError .value = errorMessage
171+ searchError .value = extractErrorMessage (error )
249172 searchResults .value = []
250173 } finally {
251- // Only reset loading if not aborted
252174 if (! currentAbortController .signal .aborted ) {
253175 isLoading .value = false
254176 }
255177 }
256178}
257179
180+ const getMetadata = (chunk : any , key : string ): any => {
181+ const meta = chunk .metadata ?.find ((m : any ) => m .key === key )
182+ return meta ?.value || ' '
183+ }
184+
185+ const transformSearchResult = (chunk : any ): SearchResult => {
186+ const title = getMetadata (chunk , ' title' ) || getMetadata (chunk , ' heading' ) || extractTitle (chunk .content_html ) || ' Untitled'
187+ const description = getMetadata (chunk , ' description' ) || ' '
188+ const hierarchy = getMetadata (chunk , ' hierarchy' ) || []
189+ const url = normalizeUrl (chunk .source_url || chunk .group ?.tracking_id || ' ' )
190+ const breadcrumb = createBreadcrumb (hierarchy , url )
191+
192+ return {
193+ id: chunk .id ,
194+ title ,
195+ content: description || chunk .content_html || chunk .content || ' ' ,
196+ url ,
197+ highlight: chunk .content_html ,
198+ hierarchy ,
199+ breadcrumb ,
200+ }
201+ }
202+
203+ const normalizeUrl = (path : string ): string => {
204+ if (! path ) return ' '
205+
206+ // Find 'docs' in the path and extract everything from that point
207+ const docsIndex = path .indexOf (' /docs' )
208+ if (docsIndex !== - 1 ) {
209+ path = path .substring (docsIndex )
210+ }
211+
212+ // Remove .md extension
213+ return path .replace (/ \. md$ / , ' ' )
214+ }
215+
216+ const extractErrorMessage = (error : any ): string => {
217+ if (error ?.response ?.data ?.message ) return error .response .data .message
218+ if (error ?.message ) return error .message
219+ if (typeof error === ' string' ) return error
220+ return ' An unexpected error occurred while searching.'
221+ }
222+
258223const extractTitle = (html : string ): string => {
259224 if (! html ) return ' '
260225 const temp = document .createElement (' div' )
@@ -263,6 +228,26 @@ const extractTitle = (html: string): string => {
263228 return heading ?.textContent ?.trim () || ' '
264229}
265230
231+ const createBreadcrumb = (hierarchy : string [], url : string ): string => {
232+ // Try hierarchy first
233+ if (Array .isArray (hierarchy ) && hierarchy .length > 0 ) {
234+ const docsIndex = hierarchy .findIndex (item => item === ' docs' )
235+ return docsIndex !== - 1
236+ ? hierarchy .slice (docsIndex ).join (' / ' )
237+ : hierarchy .join (' / ' )
238+ }
239+
240+ // Fall back to URL
241+ if (! url ) return ' '
242+
243+ const urlParts = url .replace (/ ^ \/ + / , ' ' ).split (' /' )
244+ const docsIndex = urlParts .indexOf (' docs' )
245+
246+ return docsIndex !== - 1
247+ ? urlParts .slice (docsIndex ).join (' / ' )
248+ : urlParts .join (' / ' )
249+ }
250+
266251const navigateToResult = (result : SearchResult ) => {
267252 window .location .href = result .url
268253 closeSearch ()
0 commit comments