@@ -152,7 +152,7 @@ function makeTeaser(body, terms) {
152152 * @return {string } - The escaped string safe for HTML rendering.
153153 */
154154function escapeHtml ( unsafe ) {
155- if ( typeof unsafe !== ' string' ) return '' ;
155+ if ( typeof unsafe !== " string" ) return "" ;
156156 return unsafe
157157 . replace ( / & / g, "&" )
158158 . replace ( / < / g, "<" )
@@ -170,16 +170,16 @@ function escapeHtml(unsafe) {
170170 */
171171function formatSearchResultItem ( item , terms ) {
172172 // Create article element
173- const article = document . createElement ( ' article' ) ;
174- article . className = ' search-results__item' ;
173+ const article = document . createElement ( " article" ) ;
174+ article . className = " search-results__item" ;
175175
176176 // Create link with title
177- const link = document . createElement ( 'a' ) ;
177+ const link = document . createElement ( "a" ) ;
178178 link . href = item . ref ;
179179 link . textContent = item . doc . title ;
180180
181181 // Create section with teaser (makeTeaser returns HTML string with <b> tags)
182- const section = document . createElement ( ' section' ) ;
182+ const section = document . createElement ( " section" ) ;
183183 section . innerHTML = makeTeaser ( item . doc . body , terms ) ;
184184
185185 article . appendChild ( link ) ;
@@ -197,6 +197,7 @@ function initSearch() {
197197 var $searchModalInput = document . getElementById ( "search-modal" ) ;
198198 var $searchResults = document . querySelector ( ".search-results" ) ;
199199 var $searchResultsItems = document . querySelector ( ".search-results__items" ) ;
200+ var $searchResultsCount = document . getElementById ( "search-results-count" ) ;
200201 var $searchBackdrop = document . querySelector ( ".search-backdrop" ) ;
201202 var $slashIcon = document . getElementById ( "slash-icon" ) ;
202203 var MAX_ITEMS = 10 ;
@@ -223,7 +224,8 @@ function initSearch() {
223224 scrollPosition = window . pageYOffset || document . documentElement . scrollTop ;
224225
225226 // Calculate scrollbar width to prevent layout shift
226- const scrollbarWidth = window . innerWidth - document . documentElement . clientWidth ;
227+ const scrollbarWidth =
228+ window . innerWidth - document . documentElement . clientWidth ;
227229
228230 document . body . classList . add ( "modal-open" ) ;
229231 document . body . style . top = `-${ scrollPosition } px` ;
@@ -243,8 +245,8 @@ function initSearch() {
243245 $searchResults . classList . remove ( "active" ) ;
244246 $searchBackdrop . classList . remove ( "active" ) ;
245247 document . body . classList . remove ( "modal-open" ) ;
246- document . body . style . top = '' ;
247- document . body . style . paddingRight = '' ;
248+ document . body . style . top = "" ;
249+ document . body . style . paddingRight = "" ;
248250
249251 // Restore scroll position
250252 window . scrollTo ( 0 , scrollPosition ) ;
@@ -253,16 +255,32 @@ function initSearch() {
253255 $searchInput . value = "" ;
254256 $searchModalInput . value = "" ;
255257 $searchResultsItems . innerHTML = "" ;
258+ $searchResultsCount . classList . remove ( "visible" ) ;
259+ $searchResultsCount . innerHTML = "" ;
256260 selectedIndex = - 1 ;
257261 }
258262
263+ // Function to update results count display
264+ function updateResultsCount ( count , query ) {
265+ if ( ! $searchResultsCount ) return ;
266+
267+ if ( count > 0 && query ) {
268+ const resultText = count === 1 ? "result" : "results" ;
269+ $searchResultsCount . innerHTML = `${ count } ${ resultText } for "<strong>${ escapeHtml ( query ) } </strong>"` ;
270+ $searchResultsCount . classList . add ( "visible" ) ;
271+ } else {
272+ $searchResultsCount . classList . remove ( "visible" ) ;
273+ $searchResultsCount . innerHTML = "" ;
274+ }
275+ }
276+
259277 // Function to update selected item
260278 function updateSelection ( newIndex ) {
261- const items = $searchResultsItems . querySelectorAll ( ' .search-results__item' ) ;
279+ const items = $searchResultsItems . querySelectorAll ( " .search-results__item" ) ;
262280 if ( items . length === 0 ) return ;
263281
264282 // Remove previous selection
265- items . forEach ( item => item . classList . remove ( ' selected' ) ) ;
283+ items . forEach ( ( item ) => item . classList . remove ( " selected" ) ) ;
266284
267285 // Clamp index
268286 if ( newIndex < 0 ) newIndex = 0 ;
@@ -272,8 +290,11 @@ function initSearch() {
272290
273291 // Add selection to new item
274292 if ( items [ selectedIndex ] ) {
275- items [ selectedIndex ] . classList . add ( 'selected' ) ;
276- items [ selectedIndex ] . scrollIntoView ( { block : 'nearest' , behavior : 'smooth' } ) ;
293+ items [ selectedIndex ] . classList . add ( "selected" ) ;
294+ items [ selectedIndex ] . scrollIntoView ( {
295+ block : "nearest" ,
296+ behavior : "smooth" ,
297+ } ) ;
277298 }
278299 }
279300
@@ -323,7 +344,10 @@ function initSearch() {
323344 function updateSlashIconVisibility ( ) {
324345 if ( $slashIcon ) {
325346 // Hide if field is focused OR has text, show only when unfocused AND empty
326- if ( document . activeElement === $searchInput || $searchInput . value . trim ( ) !== "" ) {
347+ if (
348+ document . activeElement === $searchInput ||
349+ $searchInput . value . trim ( ) !== ""
350+ ) {
327351 $slashIcon . classList . add ( "hidden" ) ;
328352 } else {
329353 $slashIcon . classList . remove ( "hidden" ) ;
@@ -335,7 +359,7 @@ function initSearch() {
335359 $searchInput . addEventListener ( "input" , updateSlashIconVisibility ) ;
336360
337361 // Hide slash icon when search field is focused
338- $searchInput . addEventListener ( "focus" , function ( ) {
362+ $searchInput . addEventListener ( "focus" , function ( ) {
339363 updateSlashIconVisibility ( ) ;
340364 // Open modal when navbar search is focused
341365 showSearchModal ( ) ;
@@ -345,12 +369,12 @@ function initSearch() {
345369 $searchInput . addEventListener ( "blur" , updateSlashIconVisibility ) ;
346370
347371 // When typing in navbar search, open modal and sync to modal input
348- $searchInput . addEventListener ( "input" , function ( ) {
372+ $searchInput . addEventListener ( "input" , function ( ) {
349373 if ( $searchInput . value . length > 0 ) {
350374 showSearchModal ( ) ;
351375 $searchModalInput . value = $searchInput . value ;
352376 // Trigger search on modal input
353- $searchModalInput . dispatchEvent ( new Event ( ' input' ) ) ;
377+ $searchModalInput . dispatchEvent ( new Event ( " input" ) ) ;
354378 }
355379 } ) ;
356380
@@ -369,37 +393,45 @@ function initSearch() {
369393
370394 if ( term === "" ) {
371395 // Show empty state but keep modal open
396+ updateResultsCount ( 0 , "" ) ;
372397 return ;
373398 }
374399
375400 var results = ( await initIndex ( ) ) . search ( term , options ) ;
376401 if ( results . length === 0 ) {
377402 // show "No results found"
378403 const noResultsItem = document . createElement ( "li" ) ;
379- noResultsItem . className = "search-results__item search-results__no-results" ;
404+ noResultsItem . className =
405+ "search-results__item search-results__no-results" ;
380406 noResultsItem . textContent = "No results found..." ;
381407 $searchResultsItems . appendChild ( noResultsItem ) ;
408+ updateResultsCount ( 0 , "" ) ;
382409 return ;
383410 }
384411
412+ // Update results count with total results (not just displayed items)
413+ updateResultsCount ( results . length , term ) ;
414+
385415 // Sanitize user input (search terms) before using
386- const sanitizedTerms = term . split ( " " ) . map ( t => escapeHtml ( t ) ) ;
416+ const sanitizedTerms = term . split ( " " ) . map ( ( t ) => escapeHtml ( t ) ) ;
387417
388418 for ( var i = 0 ; i < Math . min ( results . length , MAX_ITEMS ) ; i ++ ) {
389419 var item = document . createElement ( "li" ) ;
390420 item . appendChild ( formatSearchResultItem ( results [ i ] , sanitizedTerms ) ) ;
391421
392422 // Add click handler for each item
393- item . addEventListener ( ' click' , function ( ) {
394- const link = this . querySelector ( 'a' ) ;
423+ item . addEventListener ( " click" , function ( ) {
424+ const link = this . querySelector ( "a" ) ;
395425 if ( link ) {
396426 window . location . href = link . href ;
397427 }
398428 } ) ;
399429
400430 // Add hover handler to update selection
401- item . addEventListener ( 'mouseenter' , function ( ) {
402- const items = Array . from ( $searchResultsItems . querySelectorAll ( '.search-results__item' ) ) ;
431+ item . addEventListener ( "mouseenter" , function ( ) {
432+ const items = Array . from (
433+ $searchResultsItems . querySelectorAll ( ".search-results__item" ) ,
434+ ) ;
403435 const index = items . indexOf ( this ) ;
404436 if ( index >= 0 ) {
405437 updateSelection ( index ) ;
@@ -416,7 +448,7 @@ function initSearch() {
416448
417449 // Handle arrow key navigation and Enter key
418450 $searchModalInput . addEventListener ( "keydown" , function ( e ) {
419- const items = $searchResultsItems . querySelectorAll ( ' .search-results__item' ) ;
451+ const items = $searchResultsItems . querySelectorAll ( " .search-results__item" ) ;
420452 if ( items . length === 0 ) return ;
421453
422454 if ( e . key === "ArrowDown" ) {
@@ -427,7 +459,7 @@ function initSearch() {
427459 updateSelection ( selectedIndex - 1 ) ;
428460 } else if ( e . key === "Enter" && selectedIndex >= 0 ) {
429461 e . preventDefault ( ) ;
430- const link = items [ selectedIndex ] . querySelector ( 'a' ) ;
462+ const link = items [ selectedIndex ] . querySelector ( "a" ) ;
431463 if ( link ) {
432464 window . location . href = link . href ;
433465 }
@@ -447,7 +479,11 @@ function initSearch() {
447479 // event listener for `/` to open search modal
448480 document . addEventListener ( "keydown" , function ( e ) {
449481 // Only open modal if we're not already typing in an input
450- if ( e . key === "/" && document . activeElement . tagName !== "INPUT" && document . activeElement . tagName !== "TEXTAREA" ) {
482+ if (
483+ e . key === "/" &&
484+ document . activeElement . tagName !== "INPUT" &&
485+ document . activeElement . tagName !== "TEXTAREA"
486+ ) {
451487 // don't have input be `/`
452488 e . preventDefault ( ) ;
453489 showSearchModal ( ) ;
0 commit comments