11import { settings , TextPrompt } from '@clack/core' ;
22import color from 'picocolors' ;
3- import { type CommonOptions , S_BAR , S_BAR_END , symbol } from './common.js' ;
3+ import {
4+ type ColorFormatter ,
5+ type CommonOptions ,
6+ defaultGlobalTheme ,
7+ type GlobalTheme ,
8+ resolveTheme ,
9+ S_BAR ,
10+ S_BAR_END ,
11+ S_STEP_ACTIVE ,
12+ S_STEP_CANCEL ,
13+ S_STEP_ERROR ,
14+ S_STEP_SUBMIT ,
15+ type ThemeOptions ,
16+ } from './common.js' ;
417
5- export interface TextOptions extends CommonOptions {
18+ /**
19+ * Theme options specific to the text prompt.
20+ * All formatters are optional - defaults will be used if not provided.
21+ */
22+ export interface TextTheme {
23+ /** Format the prompt symbol in active/initial state (default: cyan) */
24+ formatSymbolActive ?: ColorFormatter ;
25+ /** Format the prompt symbol on submit (default: green) */
26+ formatSymbolSubmit ?: ColorFormatter ;
27+ /** Format the prompt symbol on cancel (default: red) */
28+ formatSymbolCancel ?: ColorFormatter ;
29+ /** Format the prompt symbol on error (default: yellow) */
30+ formatSymbolError ?: ColorFormatter ;
31+ /** Format error messages (default: yellow) */
32+ formatErrorMessage ?: ColorFormatter ;
33+ }
34+
35+ /** Default theme values for the text prompt */
36+ const defaultTextTheme : Required < TextTheme & GlobalTheme > = {
37+ ...defaultGlobalTheme ,
38+ formatSymbolActive : color . cyan ,
39+ formatSymbolSubmit : color . green ,
40+ formatSymbolCancel : color . red ,
41+ formatSymbolError : color . yellow ,
42+ formatErrorMessage : color . yellow ,
43+ } ;
44+
45+ export interface TextOptions extends CommonOptions , ThemeOptions < TextTheme > {
646 message : string ;
747 placeholder ?: string ;
848 defaultValue ?: string ;
@@ -11,6 +51,8 @@ export interface TextOptions extends CommonOptions {
1151}
1252
1353export const text = ( opts : TextOptions ) => {
54+ const theme = resolveTheme < Required < TextTheme & GlobalTheme > > ( opts . theme , defaultTextTheme ) ;
55+
1456 return new TextPrompt ( {
1557 validate : opts . validate ,
1658 placeholder : opts . placeholder ,
@@ -21,7 +63,23 @@ export const text = (opts: TextOptions) => {
2163 input : opts . input ,
2264 render ( ) {
2365 const hasGuide = ( opts ?. withGuide ?? settings . withGuide ) !== false ;
24- const titlePrefix = `${ hasGuide ? `${ color . gray ( S_BAR ) } \n` : '' } ${ symbol ( this . state ) } ` ;
66+
67+ // Resolve symbol based on state
68+ const symbolText = ( ( ) => {
69+ switch ( this . state ) {
70+ case 'initial' :
71+ case 'active' :
72+ return theme . formatSymbolActive ( S_STEP_ACTIVE ) ;
73+ case 'cancel' :
74+ return theme . formatSymbolCancel ( S_STEP_CANCEL ) ;
75+ case 'error' :
76+ return theme . formatSymbolError ( S_STEP_ERROR ) ;
77+ case 'submit' :
78+ return theme . formatSymbolSubmit ( S_STEP_SUBMIT ) ;
79+ }
80+ } ) ( ) ;
81+
82+ const titlePrefix = `${ hasGuide ? `${ color . gray ( S_BAR ) } \n` : '' } ${ symbolText } ` ;
2583 const title = `${ titlePrefix } ${ opts . message } \n` ;
2684 const placeholder = opts . placeholder
2785 ? color . inverse ( opts . placeholder [ 0 ] ) + color . dim ( opts . placeholder . slice ( 1 ) )
@@ -31,24 +89,24 @@ export const text = (opts: TextOptions) => {
3189
3290 switch ( this . state ) {
3391 case 'error' : {
34- const errorText = this . error ? ` ${ color . yellow ( this . error ) } ` : '' ;
35- const errorPrefix = hasGuide ? `${ color . yellow ( S_BAR ) } ` : '' ;
36- const errorPrefixEnd = hasGuide ? color . yellow ( S_BAR_END ) : '' ;
92+ const errorText = this . error ? ` ${ theme . formatErrorMessage ( this . error ) } ` : '' ;
93+ const errorPrefix = hasGuide ? `${ theme . formatGuideError ( S_BAR ) } ` : '' ;
94+ const errorPrefixEnd = hasGuide ? theme . formatGuideError ( S_BAR_END ) : '' ;
3795 return `${ title . trim ( ) } \n${ errorPrefix } ${ userInput } \n${ errorPrefixEnd } ${ errorText } \n` ;
3896 }
3997 case 'submit' : {
4098 const valueText = value ? ` ${ color . dim ( value ) } ` : '' ;
41- const submitPrefix = hasGuide ? color . gray ( S_BAR ) : '' ;
99+ const submitPrefix = hasGuide ? theme . formatGuideSubmit ( S_BAR ) : '' ;
42100 return `${ title } ${ submitPrefix } ${ valueText } ` ;
43101 }
44102 case 'cancel' : {
45103 const valueText = value ? ` ${ color . strikethrough ( color . dim ( value ) ) } ` : '' ;
46- const cancelPrefix = hasGuide ? color . gray ( S_BAR ) : '' ;
104+ const cancelPrefix = hasGuide ? theme . formatGuideCancel ( S_BAR ) : '' ;
47105 return `${ title } ${ cancelPrefix } ${ valueText } ${ value . trim ( ) ? `\n${ cancelPrefix } ` : '' } ` ;
48106 }
49107 default : {
50- const defaultPrefix = hasGuide ? `${ color . cyan ( S_BAR ) } ` : '' ;
51- const defaultPrefixEnd = hasGuide ? color . cyan ( S_BAR_END ) : '' ;
108+ const defaultPrefix = hasGuide ? `${ theme . formatGuide ( S_BAR ) } ` : '' ;
109+ const defaultPrefixEnd = hasGuide ? theme . formatGuide ( S_BAR_END ) : '' ;
52110 return `${ title } ${ defaultPrefix } ${ userInput } \n${ defaultPrefixEnd } \n` ;
53111 }
54112 }
0 commit comments