99 SelectKeyPrompt ,
1010 SelectPrompt ,
1111 State ,
12- TextPrompt ,
1312 strLength ,
13+ TextPrompt
1414} from '@clack/core' ;
1515import isUnicodeSupported from 'is-unicode-supported' ;
1616import color from 'picocolors' ;
@@ -61,43 +61,6 @@ const symbol = (state: State) => {
6161
6262const format = Prompt . prototype . format ;
6363
64- interface LimitOptionsParams < TOption > {
65- options : TOption [ ] ;
66- maxItems : number | undefined ;
67- cursor : number ;
68- style : ( option : TOption , active : boolean ) => string ;
69- }
70-
71- const limitOptions = < TOption > ( params : LimitOptionsParams < TOption > ) : string [ ] => {
72- const { cursor, options, style } = params ;
73-
74- const paramMaxItems = params . maxItems ?? Infinity ;
75- const outputMaxItems = Math . max ( process . stdout . rows - 4 , 0 ) ;
76- // We clamp to minimum 5 because anything less doesn't make sense UX wise
77- const maxItems = Math . min ( outputMaxItems , Math . max ( paramMaxItems , 5 ) ) ;
78- let slidingWindowLocation = 0 ;
79-
80- if ( cursor >= slidingWindowLocation + maxItems - 3 ) {
81- slidingWindowLocation = Math . max ( Math . min ( cursor - maxItems + 3 , options . length - maxItems ) , 0 ) ;
82- } else if ( cursor < slidingWindowLocation + 2 ) {
83- slidingWindowLocation = Math . max ( cursor - 2 , 0 ) ;
84- }
85-
86- const shouldRenderTopEllipsis = maxItems < options . length && slidingWindowLocation > 0 ;
87- const shouldRenderBottomEllipsis =
88- maxItems < options . length && slidingWindowLocation + maxItems < options . length ;
89-
90- return options
91- . slice ( slidingWindowLocation , slidingWindowLocation + maxItems )
92- . map ( ( option , i , arr ) => {
93- const isTopLimit = i === 0 && shouldRenderTopEllipsis ;
94- const isBottomLimit = i === arr . length - 1 && shouldRenderBottomEllipsis ;
95- return isTopLimit || isBottomLimit
96- ? color . dim ( '...' )
97- : style ( option , i + slidingWindowLocation === cursor ) ;
98- } ) ;
99- } ;
100-
10164interface ThemeParams {
10265 ctx : Omit < Prompt , 'prompt' > ;
10366 message : string ;
@@ -138,7 +101,10 @@ function applyTheme(data: ThemeParams): string {
138101 style : ( line ) => color . strikethrough ( color . dim ( line ) ) ,
139102 } ,
140103 } ) ,
141- ] . join ( '\n' ) ;
104+ value ? color . gray ( S_BAR ) : null ,
105+ ]
106+ . filter ( Boolean )
107+ . join ( '\n' ) ;
142108
143109 case 'error' :
144110 return [
@@ -192,6 +158,43 @@ function applyTheme(data: ThemeParams): string {
192158 }
193159}
194160
161+ interface LimitOptionsParams < TOption > {
162+ options : TOption [ ] ;
163+ maxItems : number | undefined ;
164+ cursor : number ;
165+ style : ( option : TOption , active : boolean ) => string ;
166+ }
167+
168+ const limitOptions = < TOption > ( params : LimitOptionsParams < TOption > ) : string [ ] => {
169+ const { cursor, options, style } = params ;
170+
171+ const paramMaxItems = params . maxItems ?? Infinity ;
172+ const outputMaxItems = Math . max ( process . stdout . rows - 4 , 0 ) ;
173+ // We clamp to minimum 5 because anything less doesn't make sense UX wise
174+ const maxItems = Math . min ( outputMaxItems , Math . max ( paramMaxItems , 5 ) ) ;
175+ let slidingWindowLocation = 0 ;
176+
177+ if ( cursor >= slidingWindowLocation + maxItems - 3 ) {
178+ slidingWindowLocation = Math . max ( Math . min ( cursor - maxItems + 3 , options . length - maxItems ) , 0 ) ;
179+ } else if ( cursor < slidingWindowLocation + 2 ) {
180+ slidingWindowLocation = Math . max ( cursor - 2 , 0 ) ;
181+ }
182+
183+ const shouldRenderTopEllipsis = maxItems < options . length && slidingWindowLocation > 0 ;
184+ const shouldRenderBottomEllipsis =
185+ maxItems < options . length && slidingWindowLocation + maxItems < options . length ;
186+
187+ return options
188+ . slice ( slidingWindowLocation , slidingWindowLocation + maxItems )
189+ . map ( ( option , i , arr ) => {
190+ const isTopLimit = i === 0 && shouldRenderTopEllipsis ;
191+ const isBottomLimit = i === arr . length - 1 && shouldRenderBottomEllipsis ;
192+ return isTopLimit || isBottomLimit
193+ ? color . dim ( '...' )
194+ : style ( option , i + slidingWindowLocation === cursor ) ;
195+ } ) ;
196+ } ;
197+
195198export interface TextOptions {
196199 message : string ;
197200 placeholder ?: string ;
@@ -285,7 +288,10 @@ export interface SelectOptions<Value> {
285288}
286289
287290export const select = < Value > ( opts : SelectOptions < Value > ) => {
288- const opt = ( option : Option < Value > , state : 'inactive' | 'active' | 'selected' | 'cancelled' ) => {
291+ const opt = (
292+ option : Option < Value > ,
293+ state : 'inactive' | 'active' | 'selected' | 'cancelled'
294+ ) : string => {
289295 const label = option . label ?? String ( option . value ) ;
290296 switch ( state ) {
291297 case 'selected' :
@@ -311,17 +317,16 @@ export const select = <Value>(opts: SelectOptions<Value>) => {
311317 value = opt ( this . options [ this . cursor ] , 'selected' ) ;
312318 break ;
313319 case 'cancel' :
314- return `${ title } ${ color . gray ( S_BAR ) } ${ opt (
315- this . options [ this . cursor ] ,
316- 'cancelled'
317- ) } \n${ color . gray ( S_BAR ) } `;
320+ value = opt ( this . options [ this . cursor ] , 'cancelled' ) ;
321+ break ;
318322 default : {
319- return ` ${ title } ${ color . cyan ( S_BAR ) } ${ limitOptions ( {
323+ value = limitOptions ( {
320324 cursor : this . cursor ,
321325 options : this . options ,
322326 maxItems : opts . maxItems ,
323327 style : ( item , active ) => opt ( item , active ? 'active' : 'inactive' ) ,
324- } ) . join ( `\n${ color . cyan ( S_BAR ) } ` ) } \n${ color . cyan ( S_BAR_END ) } \n`;
328+ } ) . join ( '\n' ) ;
329+ break ;
325330 }
326331 }
327332 return applyTheme ( {
@@ -362,16 +367,17 @@ export const selectKey = <Value extends string>(opts: SelectOptions<Value>) => {
362367
363368 switch ( this . state ) {
364369 case 'submit' :
365- return `${ title } ${ color . gray ( S_BAR ) } ${ opt (
370+ return `${ title } \n ${ color . gray ( S_BAR ) } ${ opt (
366371 this . options . find ( ( opt ) => opt . value === this . value ) ! ,
367372 'selected'
368373 ) } `;
369374 case 'cancel' :
370- return `${ title } ${ color . gray ( S_BAR ) } ${ opt ( this . options [ 0 ] , 'cancelled' ) } \n${ color . gray (
371- S_BAR
372- ) } `;
375+ return `${ title } \n${ color . gray ( S_BAR ) } ${ opt (
376+ this . options [ 0 ] ,
377+ 'cancelled'
378+ ) } \n${ color . gray ( S_BAR ) } `;
373379 default :
374- return `${ title } ${ color . cyan ( S_BAR ) } ${ this . options
380+ return `${ title } \n ${ color . cyan ( S_BAR ) } ${ this . options
375381 . map ( ( option , i ) => opt ( option , i === this . cursor ? 'active' : 'inactive' ) )
376382 . join ( `\n${ color . cyan ( S_BAR ) } ` ) } \n${ color . cyan ( S_BAR_END ) } \n`;
377383 }
@@ -442,51 +448,50 @@ export const multiselect = <Value>(opts: MultiSelectOptions<Value>) => {
442448 } ;
443449
444450 switch ( this . state ) {
445- case 'submit' : {
451+ case 'submit' :
446452 value =
447453 this . options
448454 . filter ( ( { value } ) => this . value . includes ( value ) )
449455 . map ( ( option ) => opt ( option , 'submitted' ) )
450456 . join ( color . dim ( ', ' ) ) || color . dim ( 'none' ) ;
451457 break ;
452- }
453- case 'cancel' : {
458+ case 'cancel' :
454459 value =
455460 this . options
456461 . filter ( ( { value } ) => this . value . includes ( value ) )
457462 . map ( ( option ) => opt ( option , 'cancelled' ) )
458463 . join ( color . dim ( ', ' ) ) ?? '' ;
459- }
460- case 'error' : {
461- const footer = this . error
462- . split ( '\n' )
463- . map ( ( ln , i ) =>
464- i === 0 ? `${ color . yellow ( S_BAR_END ) } ${ color . yellow ( ln ) } ` : ` ${ ln } `
465- )
466- . join ( '\n' ) ;
467- return (
468- title +
469- color . yellow ( S_BAR ) +
470- ' ' +
471- limitOptions ( {
472- options : this . options ,
473- cursor : this . cursor ,
474- maxItems : opts . maxItems ,
475- style : styleOption ,
476- } ) . join ( `\n${ color . yellow ( S_BAR ) } ` ) +
477- '\n' +
478- footer +
479- '\n'
464+ break ;
465+ case 'error' :
466+ error = format (
467+ this . error
468+ . split ( '\n' )
469+ . map ( ( ln , i ) => ( i === 0 ? color . yellow ( ln ) : ln ) )
470+ . join ( '\n' ) ,
471+ {
472+ firstLine : {
473+ start : color . yellow ( S_BAR_END ) ,
474+ } ,
475+ default : {
476+ start : color . hidden ( '-' ) ,
477+ } ,
478+ }
480479 ) ;
481- }
482- default : {
483- return ` ${ title } ${ color . cyan ( S_BAR ) } ${ limitOptions ( {
480+ value = limitOptions ( {
481+ cursor : this . cursor ,
482+ maxItems : opts . maxItems ,
484483 options : this . options ,
484+ style : styleOption ,
485+ } ) . join ( '\n' ) ;
486+ break ;
487+ default :
488+ value = limitOptions ( {
485489 cursor : this . cursor ,
486490 maxItems : opts . maxItems ,
491+ options : this . options ,
487492 style : styleOption ,
488- } ) . join ( `\n ${ color . cyan ( S_BAR ) } ` ) } \n ${ color . cyan ( S_BAR_END ) } \n` ;
489- }
493+ } ) . join ( '\n' ) ;
494+ break ;
490495 }
491496 return applyTheme ( {
492497 ctx : this ,
@@ -564,21 +569,7 @@ export const groupMultiselect = <Value>(opts: GroupMultiSelectOptions<Value>) =>
564569 ) } `;
565570 } ,
566571 render ( ) {
567- const symbol = ( state : State ) => {
568- switch ( state ) {
569- case 'initial' :
570- case 'active' :
571- return color . cyan ( S_STEP_ACTIVE ) ;
572- case 'cancel' :
573- return color . red ( S_STEP_CANCEL ) ;
574- case 'error' :
575- return color . yellow ( S_STEP_ERROR ) ;
576- case 'submit' :
577- return color . green ( S_STEP_SUBMIT ) ;
578- }
579- } ;
580-
581- let title = `${ color . gray ( S_BAR ) } \n${ symbol ( this . state ) } ${ opts . message } \n` ;
572+ let title = `${ color . gray ( S_BAR ) } \n${ symbol ( this . state ) } ${ opts . message } ` ;
582573
583574 switch ( this . state ) {
584575 case 'submit' : {
@@ -861,9 +852,8 @@ export const spinner = () => {
861852 : code === 1
862853 ? color . red ( S_STEP_CANCEL )
863854 : color . red ( S_STEP_ERROR ) ;
864- process . stdout . write ( cursor . move ( - 999 , 0 ) ) ;
865- process . stdout . write ( erase . down ( 1 ) ) ;
866- process . stdout . write ( `${ step } ${ _message } \n` ) ;
855+ _message = formatMessage ( step , msg || _message ) ;
856+ process . stdout . write ( _message + '\n' ) ;
867857 clearHooks ( ) ;
868858 unblock ( ) ;
869859 } ;
0 commit comments