UNPKG

15.5 kBMarkdownView Raw
1<h1 align="center">
2 <br>
3 <br>
4 <img width="300" alt="Ink" src="media/logo.png">
5 <br>
6 <br>
7 <br>
8</h1>
9
10> React for CLIs. Build and test your CLI output using components.
11
12[![Build Status](https://travis-ci.org/vadimdemedes/ink.svg?branch=next)](https://travis-ci.org/vadimdemedes/ink)
13
14
15## Install
16
17```
18$ npm install ink@next
19```
20
21
22## Usage
23
24```jsx
25import React, {Component} from 'react';
26import {render, Color} from 'ink';
27
28class Counter extends Component {
29 constructor() {
30 super();
31
32 this.state = {
33 i: 0
34 };
35 }
36
37 render() {
38 return (
39 <Color green>
40 {this.state.i} tests passed
41 </Color>
42 );
43 }
44
45 componentDidMount() {
46 this.timer = setInterval(() => {
47 this.setState({
48 i: this.state.i + 1
49 });
50 }, 100);
51 }
52
53 componentWillUnmount() {
54 clearInterval(this.timer);
55 }
56}
57
58render(<Counter/>);
59```
60
61<img src="media/demo.svg" width="600">
62
63You can also check it out live on [repl.it sandbox](https://ink-counter-demo.vadimdemedes.repl.run/).
64Feel free to play around with the code and fork this repl at [https://repl.it/@vadimdemedes/ink-counter-demo](https://repl.it/@vadimdemedes/ink-counter-demo).
65
66
67## Built with Ink
68
69- [emoj](https://github.com/sindresorhus/emoj) - Find relevant emoji on the command-line.
70- [emma](https://github.com/maticzav/emma-cli) - Terminal assistant to find and install npm packages.
71- [swiff](https://github.com/simple-integrated-marketing/swiff) - Multi-environment command line tools for time-saving web developers.
72
73
74## Table of Contents
75
76- [Getting Started](#getting-started)
77- [Examples](#examples)
78- [API](#api)
79- [Building Layouts](#building-layouts)
80- [Built-in Components](#built-in-components)
81- [Useful Components](#useful-components)
82
83
84## Getting Started
85
86Ink's goal is to provide the same component-based UI building experience that React provides, but for command-line apps. It uses [yoga-layout](https://github.com/facebook/yoga) to allow Flexbox layouts in the terminal. If you are already familiar with React, you already know Ink.
87
88The key difference you have to remember is that the rendering result isn't a DOM, but a string, which Ink writes to the output.
89
90To ensure all examples work and you can begin your adventure with Ink, make sure to set up Babel with a React preset. After [installing Babel](https://babeljs.io/docs/en/usage), configure it in `package.json`:
91
92```json
93{
94 "babel": {
95 "presets": [
96 "@babel/preset-react"
97 ]
98 }
99}
100```
101
102Don't forget to import `React` into every file that contains JSX:
103
104```jsx
105import React from 'react';
106import {render, Box} from 'ink';
107
108const Demo = () => (
109 <Box>
110 Hello World
111 </Box>
112);
113
114render(<Demo/>);
115```
116
117
118## Examples
119
120- [Jest](examples/jest/jest.js) - Implementation of basic Jest UI [(live demo)](https://ink-jest-demo.vadimdemedes.repl.run/).
121- [Counter](examples/counter/counter.js) - Simple counter that increments every 100ms [(live demo)](https://ink-counter-demo.vadimdemedes.repl.run/).
122
123
124## API
125
126Since Ink is a React renderer, it means that all features of React are supported.
127Head over to [React](https://reactjs.org/) website for documentation on how to use it.
128In this readme only Ink's methods will be documented.
129
130#### render(tree, options)
131
132Returns: `App`
133
134Mount a component and render the output.
135
136##### tree
137
138Type: `ReactElement`
139
140##### options
141
142###### stdout
143
144Type: `Stream`<br>
145Default: `process.stdout`
146
147Output stream where app will be rendered.
148
149###### stdin
150
151Type: `Stream`<br>
152Default: `process.stdin`
153
154Input stream where app will listen for input.
155
156###### exitOnCtrlC
157
158Type: `Boolean`<br>
159Default: `true`
160
161Configure whether Ink should listen to Ctrl+C keyboard input and exit the app.
162This is needed in case `process.stdin` is in [raw mode](https://nodejs.org/api/tty.html#tty_readstream_setrawmode_mode), because then Ctrl+C is ignored by default and process is expected to handle it manually.
163
164###### debug
165
166Type: `Boolean`<br>
167Default: `false`
168
169If `true`, each update will be rendered as a separate output, without replacing the previous one.
170
171```jsx
172import React, {Component} from 'react';
173import {render, Box} from 'ink';
174
175class Counter extends Component {
176 constructor() {
177 super();
178
179 this.state = {
180 i: 0
181 };
182 }
183
184 render() {
185 return (
186 <Box>
187 Iteration #{this.state.i}
188 </Box>
189 );
190 }
191
192 componentDidMount() {
193 this.timer = setInterval(() => {
194 this.setState(prevState => ({
195 i: prevState.i + 1
196 }));
197 }, 100);
198 }
199
200 componentWillUnmount() {
201 clearInterval(this.timer);
202 }
203}
204
205const app = render(<Counter/>);
206
207setTimeout(() => {
208 // Enough counting
209 app.unmount();
210}, 1000);
211```
212
213There's also a shortcut to avoid passing `options` object:
214
215```jsx
216render(<Counter>, process.stdout);
217```
218
219#### App
220
221This is the object that `render()` returns.
222
223##### unmount
224
225Manually unmount the whole Ink app.
226
227```jsx
228const app = render(<MyApp/>);
229app.unmount();
230```
231
232##### waitUntilExit
233
234Returns a promise, which resolves when app is unmounted.
235
236```jsx
237const app = render(<MyApp/>);
238
239setTimeout(() => {
240 app.unmount();
241}, 1000);
242
243await app.waitUntilExit(); // resolves after `app.unmount()` is called
244```
245
246## Building Layouts
247
248Ink uses [Yoga](https://github.com/facebook/yoga) - a Flexbox layout engine to build great user interfaces for your CLIs.
249It's important to remember that each element is a Flexbox container.
250Think of it as if each `<div>` in the browser had `display: flex`.
251See `<Box>` built-in component below for documentation on how to use Flexbox layouts in Ink.
252
253
254### Built-in Components
255
256#### &lt;Box&gt;
257
258`<Box>` it's an essential Ink component to build your layout. It's like a `<div style="display: flex">` in a browser.
259
260Import:
261
262```js
263import {Box} from 'ink';
264```
265
266##### Padding
267
268###### paddingTop
269
270Type: `number`<br>
271Default: `0`
272
273###### paddingBottom
274
275Type: `number`<br>
276Default: `0`
277
278###### paddingLeft
279
280Type: `number`<br>
281Default: `0`
282
283###### paddingRight
284
285Type: `number`<br>
286Default: `0`
287
288###### paddingX
289
290Type: `number`<br>
291Default: `0`
292
293###### paddingY
294
295Type: `number`<br>
296Default: `0`
297
298###### padding
299
300Type: `number`<br>
301Default: `0`
302
303```jsx
304<Box paddingTop={2}>Top</Box>
305<Box paddingBottom={2}>Bottom</Box>
306<Box paddingLeft={2}>Left</Box>
307<Box paddingRight={2}>Right</Box>
308<Box paddingX={2}>Left and right</Box>
309<Box paddingY={2}>Top and bottom</Box>
310<Box padding={2}>Top, bottom, left and right</Box>
311```
312
313##### Margin
314
315###### marginTop
316
317Type: `number`<br>
318Default: `0`
319
320###### marginBottom
321
322Type: `number`<br>
323Default: `0`
324
325###### marginLeft
326
327Type: `number`<br>
328Default: `0`
329
330###### marginRight
331
332Type: `number`<br>
333Default: `0`
334
335###### marginX
336
337Type: `number`<br>
338Default: `0`
339
340###### marginY
341
342Type: `number`<br>
343Default: `0`
344
345###### margin
346
347Type: `number`<br>
348Default: `0`
349
350```jsx
351<Box marginTop={2}>Top</Box>
352<Box marginBottom={2}>Bottom</Box>
353<Box marginLeft={2}>Left</Box>
354<Box marginRight={2}>Right</Box>
355<Box marginX={2}>Left and right</Box>
356<Box marginY={2}>Top and bottom</Box>
357<Box margin={2}>Top, bottom, left and right</Box>
358```
359
360##### Flex
361
362###### flexGrow
363
364Type: `number`<br>
365Default: `0`
366
367See [flex-grow](https://css-tricks.com/almanac/properties/f/flex-grow/).
368
369```jsx
370<Box>
371 Label:
372 <Box flexGrow={1}>
373 Fills all remaining space
374 </Box>
375</Box>
376```
377
378###### flexShrink
379
380Type: `number`<br>
381Default: `1`
382
383See [flex-shrink](https://css-tricks.com/almanac/properties/f/flex-shrink/).
384
385```jsx
386<Box width={20}>
387 <Box flexShrink={2} width={10}>
388 Will be 1/4
389 </Box>
390 <Box width={10}>
391 Will be 3/4
392 </Box>
393</Box>
394```
395
396###### flexDirection
397
398Type: `string`<br>
399Allowed values: `row`, `row-reverse`, `column` and `column-reverse`
400
401See [flex-direction](https://css-tricks.com/almanac/properties/f/flex-direction/).
402
403```jsx
404<Box>
405 <Box marginRight={1}>X</Box>
406 <Box>Y</Box>
407</Box>
408// X Y
409
410<Box flexDirection="row-reverse">
411 <Box>X</Box>
412 <Box marginRight={1}>Y</Box>
413</Box>
414// Y X
415
416<Box flexDirection="column">
417 <Box>X</Box>
418 <Box>Y</Box>
419</Box>
420// X
421// Y
422
423<Box flexDirection="column-reverse">
424 <Box>X</Box>
425 <Box>Y</Box>
426</Box>
427// Y
428// X
429```
430
431###### alignItems
432
433Type: `string`<br>
434Allowed values: `flex-start`, `center` and `flex-end`
435
436See [align-items](https://css-tricks.com/almanac/properties/f/align-items/).
437
438```jsx
439<Box alignItems="flex-start">
440 <Box marginRight={1}>X</Box>
441 <Box>{`A\nB\nC`}</Box>
442</Box>
443// X A
444// B
445// C
446
447<Box alignItems="center">
448 <Box marginRight={1}>X</Box>
449 <Box>{`A\nB\nC`}</Box>
450</Box>
451// A
452// X B
453// C
454
455<Box alignItems="flex-end">
456 <Box marginRight={1}>X</Box>
457 <Box>{`A\nB\nC`}</Box>
458</Box>
459// A
460// B
461// X C
462```
463
464###### justifyContent
465
466Type: `string`<br>
467Allowed values: `flex-start`, `center`, `flex-end`, `space-between` and `space-around`.
468
469See [justify-content](https://css-tricks.com/almanac/properties/f/justify-content/).
470
471```jsx
472<Box justifyContent="flex-start">
473 <Box>X</Box>
474</Box>
475// [X ]
476
477<Box justifyContent="center">
478 <Box>X</Box>
479</Box>
480// [ X ]
481
482<Box justifyContent="flex-end">
483 <Box>X</Box>
484</Box>
485// [ X]
486
487<Box justifyContent="space-between">
488 <Box>X</Box>
489 <Box>Y</Box>
490</Box>
491// [X Y]
492
493<Box justifyContent="space-around">
494 <Box>X</Box>
495 <Box>Y</Box>
496</Box>
497// [ X Y ]
498```
499
500
501#### &lt;Color&gt;
502
503The `<Color>` compoment is a simple wrapper around [the `chalk` API](https://github.com/chalk/chalk#api).
504It supports all of the chalk's methods as `props`.
505
506Import:
507
508```js
509import {Color} from 'ink';
510```
511
512Usage:
513
514```jsx
515<Color rgb={[255, 255, 255]} bgKeyword="magenta">
516 Hello!
517</Color>
518
519<Color hex="#000000" bgHex="#FFFFFF">
520 Hey there
521</Color>
522
523<Color blue>
524 I'm blue
525</Color>
526```
527
528#### &lt;Text&gt;
529
530This component can change the style of the text, make it bold, underline, italic or strikethrough.
531
532Import:
533
534```js
535import {Text} from 'ink';
536```
537
538##### bold
539
540Type: `boolean`<br>
541Default: `false`
542
543##### italic
544
545Type: `boolean`<br>
546Default: `false`
547
548##### underline
549
550Type: `boolean`<br>
551Default: `false`
552
553##### strikethrough
554
555Type: `boolean`<br>
556Default: `false`
557
558Usage:
559
560```jsx
561<Text bold>I am bold</Text>
562<Text italic>I am italic</Text>
563<Text underline>I am underline</Text>
564<Text strikethrough>I am strikethrough</Text>
565```
566
567#### &lt;Static&gt;
568
569`<Static>` component allows permanently rendering output to stdout and preserving it across renders.
570Components passed to `<Static>` as children will be written to stdout only once and will never be rerendered.
571`<Static>` output comes first, before any other output from your components, no matter where it is in the tree.
572In order for this mechanism to work properly, at most one `<Static>` component must be present in your node tree and components that were rendered must never update their output. Ink will detect new children appended to `<Static>` and render them to stdout.
573
574**Note:** `<Static>` accepts only an array of children and each of them must have a unique key.
575
576Example use case for this component is Jest's output:
577
578![](https://jestjs.io/img/content/feature-fast.png)
579
580Jest continuosuly writes the list of completed tests to the output, while updating test results at the bottom of the output in real-time. Here's how this user interface could be implemented with Ink:
581
582```jsx
583<Fragment>
584 <Static>
585 {tests.map(test => (
586 <Test key={test.id} title={test.title}/>
587 ))}
588 </Static>
589
590 <Box marginTop={1}>
591 <TestResults passed={results.passed} failed={results.failed}/>
592 </Box>
593</Fragment>
594```
595
596See [examples/jest](examples/jest/jest.js) for a basic implementation of Jest's UI.
597
598#### &lt;AppContext&gt;
599
600`<StdinContext>` is a [React context](https://reactjs.org/docs/context.html#reactcreatecontext), which exposes a method to manually exit the app (unmount).
601
602Import:
603
604```js
605import {AppContext} from 'ink';
606```
607
608##### exit
609
610Type: `Function`
611
612Exit (unmount) the whole Ink app.
613
614Usage:
615
616```jsx
617<AppContext.Consumer>
618 {({ exit }) => (
619 {/* Calling `onExit()` from within <MyApp> will unmount the app */}
620 <MyApp onExit={exit}/>
621 )}
622</AppContext.Consumer>
623```
624
625#### &lt;StdinContext&gt;
626
627`<StdinContext>` is a [React context](https://reactjs.org/docs/context.html#reactcreatecontext), which exposes input stream.
628
629Import:
630
631```js
632import {StdinContext} from 'ink';
633```
634
635##### stdin
636
637Type: `Stream`<br>
638Default: `process.stdin`
639
640Stdin stream passed to `render()` in `options.stdin` or `process.stdin` by default.
641Useful if your app needs to handle user input.
642
643Usage:
644
645```jsx
646<StdinContext.Consumer>
647 {({ stdin }) => (
648 <MyComponent stdin={stdin}/>
649 )}
650</StdinContext.Consumer>
651```
652
653##### setRawMode
654
655Type: `function`<br>
656
657See [setRawMode](https://nodejs.org/api/tty.html#tty_readstream_setrawmode_mode).
658Ink exposes this function via own `<StdinContext>` to be able to handle <kbd>Ctrl</kbd>+<kbd>C</kbd>, that's why you should use Ink's `setRawMode` instead of `process.stdin.setRawMode`.
659
660Usage:
661
662```jsx
663<StdinContext.Consumer>
664 {({ setRawMode }) => (
665 <MyComponent setRawMode={setRawMode}/>
666 )}
667</StdinContext.Consumer>
668```
669
670#### &lt;StdoutContext&gt;
671
672`<StdoutContext>` is a [React context](https://reactjs.org/docs/context.html#reactcreatecontext), which exposes stdout stream, where Ink renders your app.
673
674Import:
675
676```js
677import {StdoutContext} from 'ink';
678```
679
680##### stdout
681
682Type: `Stream`<br>
683Default: `process.stdout`
684
685Usage:
686
687```jsx
688<StdoutContext.Consumer>
689 {({ stdout }) => (
690 <MyComponent stdout={stdout}/>
691 )}
692</StdoutContext.Consumer>
693```
694
695
696## Useful Components
697
698- [ink-text-input](https://github.com/vadimdemedes/ink-text-input) - Text input.
699- [ink-spinner](https://github.com/vadimdemedes/ink-spinner) - Spinner.
700- [ink-select-input](https://github.com/vadimdemedes/ink-select-input) - Select (dropdown) input.
701- [ink-link](https://github.com/sindresorhus/ink-link) - Link component.
702- [ink-box](https://github.com/sindresorhus/ink-box) - Box component.
703- [ink-gradient](https://github.com/sindresorhus/ink-gradient) - Gradient color component.
704- [ink-big-text](https://github.com/sindresorhus/ink-big-text) - Awesome text component.
705- [ink-image](https://github.com/kevva/ink-image) - Display images inside the terminal.
706
707### Incompatible components
708
709These are components that haven't migrated to Ink 2 yet:
710
711- [ink-progress-bar](https://github.com/brigand/ink-progress-bar) - Configurable component for rendering progress bars.
712- [ink-console](https://github.com/ForbesLindesay/ink-console) - Render output from `console[method]` calls in a scrollable panel.
713- [ink-confirm-input](https://github.com/kevva/ink-confirm-input) - Yes/No confirmation input.
714- [ink-checkbox-list](https://github.com/MaxMEllon/ink-checkbox-list) - Checkbox.
715- [ink-quicksearch](https://github.com/aicioara/ink-quicksearch) - Select Component with fast quicksearch-like navigation
716- [ink-autocomplete](https://github.com/maticzav/ink-autocomplete) - Autocomplete.
717- [ink-table](https://github.com/maticzav/ink-table) - Table component.
718- [ink-broadcast](https://github.com/jimmed/ink-broadcast) - Implementation of react-broadcast for Ink.
719- [ink-router](https://github.com/jimmed/ink-router) - Implementation of react-router for Ink.
720- [ink-tab](https://github.com/jdeniau/ink-tab) - Tab component.
721- [ink-select](https://github.com/karaggeorge/ink-select) - Select component.
722- [ink-scrollbar](https://github.com/karaggeorge/ink-scrollbar) - Scrollbar component.
723- [ink-text-animation](https://github.com/yardnsm/ink-text-animation) - Text animation component.
724- [ink-figlet](https://github.com/KimotoYanke/ink-figlet) - Large text component with Figlet fonts.
725- [ink-divider](https://github.com/JureSotosek/ink-divider) - A divider component.
726
727
728## License
729
730MIT © [Vadim Demedes](https://github.com/vadimdemedes)