@@ -16,6 +16,14 @@ type SearchResp = {
1616 relatedSearches : string [ ] ;
1717} ;
1818
19+ const PAGE_SIZE = 10 ;
20+
21+ const parseSearchQuery = ( ) : string | undefined => {
22+ const argStart = process . argv [ 2 ] === "search" ? 3 : 2 ;
23+ const args = process . argv . slice ( argStart ) . filter ( ( a ) => ! a . startsWith ( "-" ) ) ;
24+ return args . length ? args . join ( " " ) : undefined ;
25+ } ;
26+
1927const openUrl = ( url : string ) => {
2028 const cmd = process . platform === "darwin" ? "open" : "xdg-open" ;
2129 try {
@@ -46,19 +54,28 @@ const wrap = (text: string, width: number): string[] => {
4654const truncate = ( s : string , max : number ) =>
4755 s . length > max ? s . slice ( 0 , max - 1 ) + "…" : s ;
4856
49- const renderResults = ( data : SearchResp ) => {
57+ const totalPages = ( count : number ) => Math . max ( 1 , Math . ceil ( count / PAGE_SIZE ) ) ;
58+
59+ const renderResults = (
60+ data : SearchResp ,
61+ page : number ,
62+ showRelated : boolean ,
63+ ) => {
5064 const cols = process . stdout . columns ?? 100 ;
5165 const maxWidth = Math . min ( cols - 8 , 90 ) ;
5266 const pad = " " ;
67+ const pages = totalPages ( data . results . length ) ;
68+ const start = ( page - 1 ) * PAGE_SIZE ;
69+ const pageResults = data . results . slice ( start , start + PAGE_SIZE ) ;
5370
5471 console . log ( ) ;
5572 console . log (
56- ` ${ t . muted ( `${ data . results . length } results · ${ data . totalTime } ms` ) } ` ,
73+ ` ${ t . muted ( `page ${ page } / ${ pages } · ${ data . results . length } results · ${ data . totalTime } ms` ) } ` ,
5774 ) ;
5875 console . log ( ) ;
5976
60- data . results . forEach ( ( r , i ) => {
61- const num = t . dim ( ` ${ String ( i + 1 ) . padStart ( 2 ) } ` ) ;
77+ pageResults . forEach ( ( r , i ) => {
78+ const num = t . dim ( ` ${ String ( start + i + 1 ) . padStart ( 2 ) } ` ) ;
6279 console . log ( `${ num } ${ t . bold ( t . brand ( truncate ( r . title , maxWidth ) ) ) } ` ) ;
6380 console . log ( `${ pad } ${ t . success ( truncate ( r . url , maxWidth ) ) } ` ) ;
6481
@@ -74,7 +91,7 @@ const renderResults = (data: SearchResp) => {
7491 console . log ( ) ;
7592 } ) ;
7693
77- if ( data . relatedSearches ?. length ) {
94+ if ( showRelated && data . relatedSearches ?. length ) {
7895 const related = data . relatedSearches
7996 . slice ( 0 , 5 )
8097 . map ( ( s ) => t . primary ( s ) )
@@ -105,49 +122,58 @@ const doSearch = async (
105122 return result . data ;
106123} ;
107124
108- export const searchCmd = async ( ) => {
109- const config = await loadConfig ( ) ;
125+ const promptQuery = async ( ) : Promise < string | null > => {
126+ const query = await p . text ( {
127+ message : t . brand ( "search" ) ,
128+ placeholder : "what are you looking for?" ,
129+ } ) ;
130+ if ( p . isCancel ( query ) || ! query ) return null ;
131+ return query ;
132+ } ;
110133
111- if ( ! config . instanceUrl ) {
112- p . log . warn ( t . warning ( "no instance configured - run Login / Setup first" ) ) ;
113- return ;
114- }
134+ const browseResults = async ( data : SearchResp ) : Promise < "again" | "back" > => {
135+ let page = 1 ;
136+ const pages = totalPages ( data . results . length ) ;
115137
116138 while ( true ) {
117- const query = await p . text ( {
118- message : t . brand ( "search" ) ,
119- placeholder : "what are you looking for?" ,
120- } ) ;
121- if ( p . isCancel ( query ) || ! query ) return ;
122-
123- const data = await doSearch ( query , config ) ;
124- if ( ! data ) return ;
125-
126- if ( ! data . results . length ) {
127- p . log . warn ( t . muted ( "no results found" ) ) ;
128- continue ;
129- }
130-
131- renderResults ( data ) ;
132-
133- const openOpts = data . results . slice ( 0 , 5 ) . map ( ( r , i ) => ( {
134- value : `open:${ i } ` ,
135- label : t . text ( `open #${ i + 1 } ` ) ,
136- hint : truncate ( r . title , 50 ) ,
137- } ) ) ;
139+ renderResults ( data , page , page === 1 ) ;
140+
141+ const openOpts = data . results
142+ . slice ( ( page - 1 ) * PAGE_SIZE , page * PAGE_SIZE )
143+ . map ( ( r , i ) => ( {
144+ value : `open:${ ( page - 1 ) * PAGE_SIZE + i } ` ,
145+ label : t . text ( truncate ( r . title , 60 ) ) ,
146+ } ) ) ;
147+
148+ const navOpts = [
149+ ...( page > 1
150+ ? [ { value : "prev" , label : t . muted ( "previous page" ) } ]
151+ : [ ] ) ,
152+ ...( page < pages
153+ ? [ { value : "next" , label : t . muted ( "next page" ) } ]
154+ : [ ] ) ,
155+ ] ;
138156
139157 const action = await p . select ( {
140158 message : t . muted ( "open a result, search again, or go back" ) ,
141159 options : [
142160 ...openOpts ,
161+ ...navOpts ,
143162 { value : "again" , label : t . muted ( "search again" ) } ,
144163 { value : "back" , label : t . muted ( "back to menu" ) } ,
145164 ] ,
146165 } ) ;
147166
148- if ( p . isCancel ( action ) || action === "back" ) return ;
149-
150- if ( action === "again" ) continue ;
167+ if ( p . isCancel ( action ) || action === "back" ) return "back" ;
168+ if ( action === "again" ) return "again" ;
169+ if ( action === "next" ) {
170+ page = Math . min ( page + 1 , pages ) ;
171+ continue ;
172+ }
173+ if ( action === "prev" ) {
174+ page = Math . max ( page - 1 , 1 ) ;
175+ continue ;
176+ }
151177
152178 if ( typeof action === "string" && action . startsWith ( "open:" ) ) {
153179 const idx = parseInt ( action . slice ( 5 ) , 10 ) ;
@@ -156,3 +182,32 @@ export const searchCmd = async () => {
156182 }
157183 }
158184} ;
185+
186+ export const searchCmd = async ( ) => {
187+ const config = await loadConfig ( ) ;
188+
189+ if ( ! config . instanceUrl ) {
190+ p . log . warn ( t . warning ( "no instance configured - run Login / Setup first" ) ) ;
191+ return ;
192+ }
193+
194+ let pendingQuery = parseSearchQuery ( ) ;
195+
196+ while ( true ) {
197+ const query = pendingQuery ?? ( await promptQuery ( ) ) ;
198+ pendingQuery = undefined ;
199+
200+ if ( ! query ) return ;
201+
202+ const data = await doSearch ( query , config ) ;
203+ if ( ! data ) return ;
204+
205+ if ( ! data . results . length ) {
206+ p . log . warn ( t . muted ( "no results found" ) ) ;
207+ continue ;
208+ }
209+
210+ const action = await browseResults ( data ) ;
211+ if ( action === "back" ) return ;
212+ }
213+ } ;
0 commit comments