Files
KArtSell.Aegis/docs/Design/kbx-foundation-v60-status-canonical-contract-hardening/packages/kbx-ui/src/shell/navigationSearch.ts
T
kjh2064 3f293d8aa8
deploy / deploy (push) Successful in 1m52s
deploy / notify (push) Successful in 1s
V13-FE-006: consolidate approved UI and contract hardening
2026-08-13 02:41:00 +09:00

13 lines
1.1 KiB
TypeScript

import type { KbxMenuSearchResult, KbxNavigationResolvedEntry } from '@kbx/contracts'
function normalize(value:string){return value.toLocaleLowerCase().replace(/\s+/g,'')}
export function searchNavigationEntries(entries:KbxNavigationResolvedEntry[], query:string, limit=12, priorityScreenIds:string[]=[]):KbxMenuSearchResult[]{
const q=normalize(query);const priority=new Map(priorityScreenIds.map((id,index)=>[id,index]))
return entries.map(entry=>{
const values=[entry.title,entry.screenId,entry.module,entry.section,...(entry.keywords??[])].map(normalize)
let score=q?0:(priority.has(entry.screenId)?1000-(priority.get(entry.screenId)??0):1)
for(const value of values){if(!q)continue;if(value===q)score=Math.max(score,100);else if(value.startsWith(q))score=Math.max(score,70);else if(value.includes(q))score=Math.max(score,40)}
if(q&&priority.has(entry.screenId)&&score>0)score+=5
return {screenId:entry.screenId,title:entry.title,module:entry.module,section:entry.section,path:entry.path,keywords:entry.keywords??[],score}
}).filter(x=>x.score>0).sort((a,b)=>b.score-a.score||a.title.localeCompare(b.title)).slice(0,limit)
}