@@ -11,9 +11,7 @@ console.log('[MossSearch] Config:', config);
1111import InferEdgeLogo from ' ./InferEdgeLogo_Dark_Icon.png' ;
1212console .log (' [MossSearch] Logo imported:' , !! InferEdgeLogo );
1313
14- // ---------------------------------------------------------------------------
15- // State
16- // ---------------------------------------------------------------------------
14+
1715const isOpen = ref (false );
1816const query = ref (' ' );
1917const results = ref <MossResult []>([]);
@@ -44,22 +42,33 @@ interface MossResult {
4442// Moss client
4543// ---------------------------------------------------------------------------
4644let mossClient: any = null ;
47- let indexLoaded = false ;
45+ const isClientReady = ref (false );
46+ const indexLoaded = ref (false );
4847let debounceTimer: ReturnType <typeof setTimeout > | null = null ;
4948
50- const loadIndex = async () => {
51- if ( indexLoaded || isIndexLoading . value ) return ;
52- isIndexLoading . value = true ;
49+ // Phase 1: import SDK and create client — fast, enables cloud (hot-path) queries immediately
50+ const initClient = async () => {
51+ if ( mossClient ) return ;
5352 errorMsg .value = ' ' ;
5453 try {
5554 const { MossClient } = await import (' @inferedge/moss' );
56- console .log (' [Moss] Client imported successfully' );
5755 mossClient = new MossClient (config .projectId , config .projectKey );
56+ isClientReady .value = true ;
57+ } catch (err : any ) {
58+ console .error (' [Moss] Failed to initialize client:' , err );
59+ errorMsg .value = ' Could not initialize search. Check your Moss configuration.' ;
60+ }
61+ };
62+
63+ // Phase 2: download model + index locally — runs in background, enables sub-10ms on-device queries
64+ const loadLocalIndex = async () => {
65+ if (indexLoaded .value || isIndexLoading .value || ! mossClient ) return ;
66+ isIndexLoading .value = true ;
67+ try {
5868 await mossClient .loadIndex (config .indexName );
59- indexLoaded = true ;
69+ indexLoaded . value = true ;
6070 } catch (err : any ) {
61- console .error (' [Moss] Failed to load search index:' , err );
62- errorMsg .value = ' Could not load search index. Check your Moss configuration.' ;
71+ console .error (' [Moss] Failed to load local index, staying on cloud search:' , err );
6372 } finally {
6473 isIndexLoading .value = false ;
6574 }
@@ -70,7 +79,8 @@ const loadIndex = async () => {
7079// ---------------------------------------------------------------------------
7180const open = async () => {
7281 isOpen .value = true ;
73- loadIndex (); // fire & forget — UI shows loading state
82+ await initClient (); // fast — just SDK import + constructor; enables cloud queries
83+ loadLocalIndex (); // fire & forget — background model + index download
7484 await nextTick ();
7585 searchInput .value ?.focus ();
7686};
@@ -87,7 +97,7 @@ const close = () => {
8797// Search
8898// ---------------------------------------------------------------------------
8999const performSearch = async (q : string ) => {
90- if (! q .trim () || ! mossClient || ! indexLoaded ) {
100+ if (! q .trim () || ! mossClient ) {
91101 results .value = [];
92102 return ;
93103 }
@@ -115,6 +125,16 @@ watch(query, (q) => {
115125 debounceTimer = setTimeout (() => performSearch (q ), 200 );
116126});
117127
128+ // Re-run search when client first becomes available (edge case: user typed before import finished)
129+ watch (isClientReady , (ready ) => {
130+ if (ready && query .value .trim ()) performSearch (query .value );
131+ });
132+
133+ // Seamless handoff: re-run active query with local index once it finishes downloading
134+ watch (indexLoaded , (loaded ) => {
135+ if (loaded && query .value .trim ()) performSearch (query .value );
136+ });
137+
118138// ---------------------------------------------------------------------------
119139// Navigation
120140// ---------------------------------------------------------------------------
@@ -286,16 +306,8 @@ const getTypeIcon = (r: MossResult): string => {
286306
287307 <!-- Body: status / results -->
288308 <div class =" moss-body" >
289- <!-- Index loading -->
290- <div v-if =" isIndexLoading" class =" moss-status" >
291- <svg class =" moss-spinner" xmlns =" http://www.w3.org/2000/svg" width =" 20" height =" 20" viewBox =" 0 0 24 24" fill =" none" stroke =" currentColor" stroke-width =" 2" aria-hidden =" true" >
292- <path d =" M21 12a9 9 0 1 1-6.219-8.56" />
293- </svg >
294- <span >Loading search index…</span >
295- </div >
296-
297- <!-- Error -->
298- <div v-else-if =" errorMsg" class =" moss-status moss-status--error" >
309+ <!-- Error (e.g. bad credentials — client could not be initialized) -->
310+ <div v-if =" errorMsg" class =" moss-status moss-status--error" >
299311 <span >{{ errorMsg }}</span >
300312 </div >
301313
@@ -304,8 +316,8 @@ const getTypeIcon = (r: MossResult): string => {
304316 <span >Type to search the docs</span >
305317 </div >
306318
307- <!-- Searching -->
308- <div v-else-if =" isSearching" class =" moss-status" >
319+ <!-- Searching (only shown when no prior results to display) -->
320+ <div v-else-if =" isSearching && results.length === 0 " class =" moss-status" >
309321 <svg class =" moss-spinner" xmlns =" http://www.w3.org/2000/svg" width =" 20" height =" 20" viewBox =" 0 0 24 24" fill =" none" stroke =" currentColor" stroke-width =" 2" aria-hidden =" true" >
310322 <path d =" M21 12a9 9 0 1 1-6.219-8.56" />
311323 </svg >
@@ -353,8 +365,14 @@ const getTypeIcon = (r: MossResult): string => {
353365 <kbd >↵</kbd > select   ;
354366 <kbd >Esc</kbd > close
355367 </span >
368+ <span v-if =" isIndexLoading" class =" moss-footer-syncing" title =" Downloading local index for sub-10ms search" >
369+ <svg class =" moss-spinner-sm" xmlns =" http://www.w3.org/2000/svg" width =" 11" height =" 11" viewBox =" 0 0 24 24" fill =" none" stroke =" currentColor" stroke-width =" 2.5" aria-hidden =" true" >
370+ <path d =" M21 12a9 9 0 1 1-6.219-8.56" />
371+ </svg >
372+ Syncing locally…
373+ </span >
356374 <span class =" moss-footer-brand" >
357- Search by
375+ Powered by
358376 <a href =" https://moss.dev" target =" _blank" rel =" noopener noreferrer" >
359377 <img :src =" InferEdgeLogo" alt =" Moss" class =" moss-footer-logo" />
360378 Moss
@@ -619,6 +637,20 @@ const getTypeIcon = (r: MossResult): string => {
619637 border-radius : 3px ;
620638}
621639
640+ .moss-footer-syncing {
641+ display : flex ;
642+ align-items : center ;
643+ gap : 4px ;
644+ font-size : 11px ;
645+ color : var (--vp-c-text-3 );
646+ opacity : 0.8 ;
647+ }
648+
649+ .moss-spinner-sm {
650+ flex-shrink : 0 ;
651+ animation : moss-spin 0.8s linear infinite ;
652+ }
653+
622654.moss-footer-brand a {
623655 color : var (--vp-c-brand-1 );
624656 text-decoration : none ;
0 commit comments