@@ -62,6 +62,11 @@ npm install inquirerer
6262 - [ Custom Resolvers] ( #custom-resolvers )
6363 - [ Resolver Examples] ( #resolver-examples )
6464- [ CLI Helper] ( #cli-helper )
65+ - [ UI Components] ( #ui-components )
66+ - [ Spinner] ( #spinner )
67+ - [ Progress Bar] ( #progress-bar )
68+ - [ Streaming Text] ( #streaming-text )
69+ - [ Custom UI with UIEngine] ( #custom-ui-with-uiengine )
6570- [ Developing] ( #developing )
6671
6772## Quick Start
@@ -1241,4 +1246,204 @@ const handler: CommandHandler = async (argv, prompter) => {
12411246
12421247const cli = new CLI (handler , options );
12431248await cli .run ();
1244- ```
1249+ ```
1250+
1251+ ## UI Components
1252+
1253+ inquirerer includes a set of UI components for building rich terminal interfaces beyond simple prompts. These are useful for showing progress, loading states, and streaming output.
1254+
1255+ ### Spinner
1256+
1257+ Show an animated spinner while performing async operations:
1258+
1259+ ``` typescript
1260+ import { createSpinner } from ' inquirerer' ;
1261+
1262+ const spinner = createSpinner (' Loading packages...' );
1263+ spinner .start ();
1264+
1265+ const data = await fetchPackages ();
1266+
1267+ spinner .succeed (' Loaded 42 packages' );
1268+ // Or: spinner.fail('Failed to load'), spinner.warn('Warning'), spinner.info('Info')
1269+ ```
1270+
1271+ ** Spinner styles:**
1272+
1273+ ``` typescript
1274+ import { createSpinner , SPINNER_STYLES } from ' inquirerer' ;
1275+
1276+ // Use different spinner styles
1277+ const spinner = createSpinner (' Processing...' , {
1278+ frames: SPINNER_STYLES .dots , // ⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏
1279+ // frames: SPINNER_STYLES.line, // - \ | /
1280+ // frames: SPINNER_STYLES.arc, // ◜ ◠ ◝ ◞ ◡ ◟
1281+ // frames: SPINNER_STYLES.circle // ◐ ◓ ◑ ◒
1282+ });
1283+ ```
1284+
1285+ ** Update text while spinning:**
1286+
1287+ ``` typescript
1288+ spinner .start ();
1289+ spinner .text (' Step 1: Downloading...' );
1290+ await download ();
1291+ spinner .text (' Step 2: Installing...' );
1292+ await install ();
1293+ spinner .succeed (' Done!' );
1294+ ```
1295+
1296+ ### Progress Bar
1297+
1298+ Show progress for operations with known completion:
1299+
1300+ ``` typescript
1301+ import { createProgress } from ' inquirerer' ;
1302+
1303+ const progress = createProgress (' Installing dependencies' );
1304+ progress .start ();
1305+
1306+ for (let i = 0 ; i < packages .length ; i ++ ) {
1307+ await installPackage (packages [i ]);
1308+ progress .update ((i + 1 ) / packages .length );
1309+ }
1310+
1311+ progress .complete (' All packages installed' );
1312+ // Or: progress.error('Installation failed')
1313+ ```
1314+
1315+ ** Increment progress:**
1316+
1317+ ``` typescript
1318+ const progress = createProgress (' Processing files' );
1319+ progress .start ();
1320+
1321+ for (const file of files ) {
1322+ await processFile (file );
1323+ progress .increment (1 / files .length );
1324+ }
1325+
1326+ progress .complete ();
1327+ ```
1328+
1329+ ### Streaming Text
1330+
1331+ Display streaming output like AI chat responses:
1332+
1333+ ``` typescript
1334+ import { createStream } from ' inquirerer' ;
1335+
1336+ const stream = createStream ({ showCursor: true });
1337+ stream .start ();
1338+
1339+ for await (const token of llmResponse ) {
1340+ stream .append (token );
1341+ }
1342+
1343+ stream .done ();
1344+ ```
1345+
1346+ ** With line prefix:**
1347+
1348+ ``` typescript
1349+ const stream = createStream ({ prefix: ' > ' });
1350+ stream .start ();
1351+ stream .appendLine (' First line of response' );
1352+ stream .appendLine (' Second line of response' );
1353+ stream .done ();
1354+ ```
1355+
1356+ ### Custom UI with UIEngine
1357+
1358+ For fully custom interactive UIs, use the ` UIEngine ` directly:
1359+
1360+ ``` typescript
1361+ import { UIEngine , Key } from ' inquirerer' ;
1362+
1363+ interface MyState {
1364+ items: string [];
1365+ selectedIndex: number ;
1366+ }
1367+
1368+ const engine = new UIEngine ();
1369+
1370+ const result = await engine .run <MyState , string >({
1371+ initialState: {
1372+ items: [' Option A' , ' Option B' , ' Option C' ],
1373+ selectedIndex: 0
1374+ },
1375+
1376+ render : (state ) => [
1377+ ' Select an option:' ,
1378+ ... state .items .map ((item , i ) =>
1379+ i === state .selectedIndex ? ` > ${item } ` : ` ${item } `
1380+ )
1381+ ],
1382+
1383+ onEvent : (event , state ) => {
1384+ if (event .type === ' key' ) {
1385+ switch (event .key ) {
1386+ case Key .UP :
1387+ return {
1388+ state: {
1389+ ... state ,
1390+ selectedIndex: Math .max (0 , state .selectedIndex - 1 )
1391+ }
1392+ };
1393+ case Key .DOWN :
1394+ return {
1395+ state: {
1396+ ... state ,
1397+ selectedIndex: Math .min (state .items .length - 1 , state .selectedIndex + 1 )
1398+ }
1399+ };
1400+ case Key .ENTER :
1401+ return {
1402+ state ,
1403+ done: true ,
1404+ value: state .items [state .selectedIndex ]
1405+ };
1406+ }
1407+ }
1408+ return { state };
1409+ }
1410+ });
1411+
1412+ console .log (' You selected:' , result );
1413+ ```
1414+
1415+ ** With animations (tick events):**
1416+
1417+ ``` typescript
1418+ const engine = new UIEngine ();
1419+
1420+ await engine .run ({
1421+ initialState: { frame: 0 , message: ' Loading' },
1422+ tickInterval: 100 , // Trigger tick event every 100ms
1423+
1424+ render : (state ) => {
1425+ const frames = [' ⠋' , ' ⠙' , ' ⠹' , ' ⠸' , ' ⠼' , ' ⠴' , ' ⠦' , ' ⠧' , ' ⠇' , ' ⠏' ];
1426+ return [` ${frames [state .frame % frames .length ]} ${state .message }... ` ];
1427+ },
1428+
1429+ onEvent : (event , state ) => {
1430+ if (event .type === ' tick' ) {
1431+ return { state: { ... state , frame: state .frame + 1 } };
1432+ }
1433+ if (event .type === ' key' && event .key === Key .ENTER ) {
1434+ return { state , done: true };
1435+ }
1436+ return { state };
1437+ }
1438+ });
1439+ ```
1440+
1441+ ** Run the demos:**
1442+
1443+ ``` bash
1444+ cd packages/inquirerer
1445+ pnpm dev:spinner # Spinner styles demo
1446+ pnpm dev:chat # Streaming text demo
1447+ pnpm dev:upgrade # Interactive upgrade UI demo
1448+ pnpm dev:prompts # All prompt types demo
1449+ ```
0 commit comments