ink 2.4.0 → 2.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,10 +9,6 @@ var _yogaLayoutPrebuilt = _interopRequireDefault(require("yoga-layout-prebuilt")
9
9
 
10
10
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
11
 
12
- const hasOwnProperty = (obj, prop) => {
13
- return {}.hasOwnProperty.call(obj, prop);
14
- };
15
-
16
12
  const applyMarginStyles = (node, style) => {
17
13
  if (style.margin) {
18
14
  node.setMargin(_yogaLayoutPrebuilt.default.EDGE_TOP, style.margin);
@@ -110,7 +106,7 @@ const applyFlexStyles = (node, style) => {
110
106
  }
111
107
  }
112
108
 
113
- if (hasOwnProperty(style, 'flexBasis')) {
109
+ if (style.flexBasis !== undefined) {
114
110
  node.setFlexBasis(style.flexBasis);
115
111
  }
116
112
 
@@ -152,19 +148,19 @@ const applyFlexStyles = (node, style) => {
152
148
  };
153
149
 
154
150
  const applyDimensionStyles = (node, style) => {
155
- if (hasOwnProperty(style, 'width')) {
151
+ if (style.width !== undefined) {
156
152
  node.setWidth(style.width);
157
153
  }
158
154
 
159
- if (hasOwnProperty(style, 'height')) {
155
+ if (style.height !== undefined) {
160
156
  node.setHeight(style.height);
161
157
  }
162
158
 
163
- if (hasOwnProperty(style, 'minWidth')) {
159
+ if (style.minWidth !== undefined) {
164
160
  node.setMinWidth(style.minWidth);
165
161
  }
166
162
 
167
- if (hasOwnProperty(style, 'minHeight')) {
163
+ if (style.minHeight !== undefined) {
168
164
  node.setMinHeight(style.minHeight);
169
165
  }
170
166
  };
@@ -21,7 +21,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
21
21
 
22
22
  function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
23
23
 
24
- function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; if (obj != null) { var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
24
+ function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
25
25
 
26
26
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
27
27
 
@@ -136,5 +136,6 @@ _defineProperty(App, "propTypes", {
136
136
  stdin: _propTypes.default.object.isRequired,
137
137
  stdout: _propTypes.default.object.isRequired,
138
138
  exitOnCtrlC: _propTypes.default.bool.isRequired,
139
+ // eslint-disable-line react/boolean-prop-naming
139
140
  onExit: _propTypes.default.func.isRequired
140
141
  });
@@ -13,7 +13,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
13
13
 
14
14
  function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
15
15
 
16
- function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; if (obj != null) { var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
16
+ function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
17
17
 
18
18
  function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
19
19
 
@@ -13,11 +13,11 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
13
13
 
14
14
  function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
15
15
 
16
- function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; if (obj != null) { var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
16
+ function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
17
17
 
18
18
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
19
19
 
20
- function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
20
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
21
21
 
22
22
  function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
23
23
 
@@ -53,6 +53,8 @@ const Text = ({
53
53
  unstable__transformChildren: transformChildren
54
54
  }, children);
55
55
  };
56
+ /* eslint-disable react/boolean-prop-naming */
57
+
56
58
 
57
59
  Text.propTypes = {
58
60
  bold: _propTypes.default.bool,
@@ -62,6 +64,8 @@ Text.propTypes = {
62
64
  children: _propTypes.default.node.isRequired,
63
65
  unstable__transformChildren: _propTypes.default.func
64
66
  };
67
+ /* eslint-enable react/boolean-prop-naming */
68
+
65
69
  Text.defaultProps = {
66
70
  bold: false,
67
71
  italic: false,
@@ -79,7 +79,11 @@ class Output {
79
79
  }
80
80
  }
81
81
 
82
- return output.map(line => line.trimRight()).join('\n');
82
+ const generatedOutput = output.map(line => line.trimRight()).join('\n');
83
+ return {
84
+ output: generatedOutput,
85
+ height: output.length
86
+ };
83
87
  }
84
88
 
85
89
  }
@@ -64,11 +64,16 @@ var _default = ({
64
64
  });
65
65
  }
66
66
 
67
+ const {
68
+ output: generatedOutput,
69
+ height: outputHeight
70
+ } = output.get();
67
71
  return {
68
- output: output.get(),
72
+ output: generatedOutput,
73
+ outputHeight,
69
74
  // Newline at the end is needed, because static output doesn't have one, so
70
75
  // interactive output will override last line of static output
71
- staticOutput: staticOutput ? `${staticOutput.get()}\n` : undefined
76
+ staticOutput: staticOutput ? `${staticOutput.get().output}\n` : undefined
72
77
  };
73
78
  };
74
79
  };
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+
8
+ var _react = require("react");
9
+
10
+ var _AppContext = _interopRequireDefault(require("../components/AppContext"));
11
+
12
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
+
14
+ var _default = () => (0, _react.useContext)(_AppContext.default);
15
+
16
+ exports.default = _default;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+
8
+ var _react = require("react");
9
+
10
+ var _StdinContext = _interopRequireDefault(require("../components/StdinContext"));
11
+
12
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
+
14
+ var _default = () => (0, _react.useContext)(_StdinContext.default);
15
+
16
+ exports.default = _default;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+
8
+ var _react = require("react");
9
+
10
+ var _StdoutContext = _interopRequireDefault(require("../components/StdoutContext"));
11
+
12
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
+
14
+ var _default = () => (0, _react.useContext)(_StdoutContext.default);
15
+
16
+ exports.default = _default;
package/build/index.js CHANGED
@@ -57,6 +57,24 @@ Object.defineProperty(exports, "useInput", {
57
57
  return _useInput.default;
58
58
  }
59
59
  });
60
+ Object.defineProperty(exports, "useApp", {
61
+ enumerable: true,
62
+ get: function () {
63
+ return _useApp.default;
64
+ }
65
+ });
66
+ Object.defineProperty(exports, "useStdin", {
67
+ enumerable: true,
68
+ get: function () {
69
+ return _useStdin.default;
70
+ }
71
+ });
72
+ Object.defineProperty(exports, "useStdout", {
73
+ enumerable: true,
74
+ get: function () {
75
+ return _useStdout.default;
76
+ }
77
+ });
60
78
 
61
79
  var _render = _interopRequireDefault(require("./render"));
62
80
 
@@ -76,4 +94,10 @@ var _Static = _interopRequireDefault(require("./components/Static"));
76
94
 
77
95
  var _useInput = _interopRequireDefault(require("./hooks/use-input"));
78
96
 
97
+ var _useApp = _interopRequireDefault(require("./hooks/use-app"));
98
+
99
+ var _useStdin = _interopRequireDefault(require("./hooks/use-stdin"));
100
+
101
+ var _useStdout = _interopRequireDefault(require("./hooks/use-stdout"));
102
+
79
103
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
package/build/instance.js CHANGED
@@ -17,6 +17,8 @@ var _isCi = _interopRequireDefault(require("is-ci"));
17
17
 
18
18
  var _signalExit = _interopRequireDefault(require("signal-exit"));
19
19
 
20
+ var _ansiEscapes = _interopRequireDefault(require("ansi-escapes"));
21
+
20
22
  var _reconciler = _interopRequireDefault(require("./reconciler"));
21
23
 
22
24
  var _reconciler2 = _interopRequireDefault(require("./experimental/reconciler"));
@@ -35,7 +37,7 @@ var _App = _interopRequireDefault(require("./components/App"));
35
37
 
36
38
  function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
37
39
 
38
- function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; if (obj != null) { var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
40
+ function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
39
41
 
40
42
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
41
43
 
@@ -98,6 +100,7 @@ class Instance {
98
100
 
99
101
  const {
100
102
  output,
103
+ outputHeight,
101
104
  staticOutput
102
105
  } = this.renderer(this.rootNode); // If <Static> output isn't empty, it means new children have been added to it
103
106
 
@@ -110,31 +113,39 @@ class Instance {
110
113
 
111
114
  this.options.stdout.write(this.fullStaticOutput + output);
112
115
  return;
113
- } // To ensure static output is cleanly rendered before main output, clear main output first
116
+ }
114
117
 
118
+ if (_isCi.default) {
119
+ if (hasStaticOutput) {
120
+ this.options.stdout.write(staticOutput);
121
+ }
122
+
123
+ this.lastOutput = output;
124
+ return;
125
+ }
115
126
 
116
127
  if (hasStaticOutput) {
117
- if (!_isCi.default) {
118
- this.log.clear();
119
- }
128
+ this.fullStaticOutput += staticOutput;
129
+ }
120
130
 
121
- this.options.stdout.write(staticOutput);
131
+ if (this.options.experimental && outputHeight >= this.options.stdout.rows) {
132
+ this.options.stdout.write(_ansiEscapes.default.clearTerminal + this.fullStaticOutput + output);
133
+ this.lastOutput = output;
134
+ return;
135
+ } // To ensure static output is cleanly rendered before main output, clear main output first
122
136
 
123
- if (!_isCi.default) {
124
- if (this.options.experimental) {
125
- this.throttledLog(output);
126
- } else {
127
- this.log(output);
128
- }
129
- }
137
+
138
+ if (hasStaticOutput) {
139
+ this.log.clear();
140
+ this.options.stdout.write(staticOutput);
130
141
  }
131
142
 
132
143
  if (output !== this.lastOutput) {
133
- if (!_isCi.default) {
144
+ if (this.options.experimental) {
145
+ this.throttledLog(output);
146
+ } else {
134
147
  this.log(output);
135
148
  }
136
-
137
- this.lastOutput = output;
138
149
  }
139
150
  }
140
151
 
package/build/output.js CHANGED
@@ -65,6 +65,10 @@ class Output {
65
65
  return this.output.map(line => line.trimRight()).join('\n');
66
66
  }
67
67
 
68
+ getHeight() {
69
+ return this.output.length;
70
+ }
71
+
68
72
  }
69
73
 
70
74
  exports.default = Output;
@@ -45,7 +45,7 @@ const hostConfig = {
45
45
  }
46
46
  }
47
47
  } else if (key === 'style') {
48
- Object.assign(node.style, value);
48
+ node.style = value;
49
49
  } else if (key === 'unstable__transformChildren') {
50
50
  node.unstable__transformChildren = value; // eslint-disable-line camelcase
51
51
  } else if (key === 'unstable__static') {
@@ -101,7 +101,7 @@ const hostConfig = {
101
101
  }
102
102
  }
103
103
  } else if (key === 'style') {
104
- Object.assign(node.style, value);
104
+ node.style = value;
105
105
  } else if (key === 'unstable__transformChildren') {
106
106
  node.unstable__transformChildren = value; // eslint-disable-line camelcase
107
107
  } else if (key === 'unstable__static') {
@@ -38,7 +38,19 @@ const isAllTextNodes = node => {
38
38
 
39
39
 
40
40
  const squashTextNodes = node => {
41
- let text = '';
41
+ if (node.childNodes.length === 0) {
42
+ return '';
43
+ } // If parent container is `<Box>`, text nodes will be treated as separate nodes in
44
+ // the tree and will have their own coordinates in the layout.
45
+ // To ensure text nodes are aligned correctly, take X and Y of the first text node
46
+ // and use them as offset for the rest of the nodes
47
+ // Only first node is taken into account, because other text nodes can't have margin or padding,
48
+ // so their coordinates will be relative to the first node anyway
49
+
50
+
51
+ const offsetX = node.childNodes[0].yogaNode.getComputedLeft();
52
+ const offsetY = node.childNodes[0].yogaNode.getComputedTop();
53
+ let text = '\n'.repeat(offsetY) + ' '.repeat(offsetX);
42
54
 
43
55
  for (const childNode of node.childNodes) {
44
56
  let nodeText;
package/build/render.js CHANGED
@@ -13,7 +13,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
13
13
 
14
14
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
15
15
 
16
- function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
16
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
17
17
 
18
18
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
19
19
 
@@ -30,7 +30,8 @@ var _default = (node, options = {}) => {
30
30
  stdout: process.stdout,
31
31
  stdin: process.stdin,
32
32
  debug: false,
33
- exitOnCtrlC: true
33
+ exitOnCtrlC: true,
34
+ experimental: false
34
35
  }, options);
35
36
  let instance;
36
37
 
package/build/renderer.js CHANGED
@@ -110,6 +110,7 @@ var _default = ({
110
110
  });
111
111
  return {
112
112
  output: output.get(),
113
+ outputHeight: output.getHeight(),
113
114
  staticOutput: staticOutput ? `${staticOutput.get()}\n` : undefined
114
115
  };
115
116
  };
package/index.d.ts CHANGED
@@ -228,20 +228,41 @@ export const Text: React.FC<TextProps>;
228
228
  */
229
229
  export const Static: React.FC<{children: React.ReactNodeArray}>;
230
230
 
231
- /**
232
- * `<AppContext>` is a React context, which exposes a method to manually exit the app (unmount).
233
- */
234
- export const AppContext: React.Context<{
231
+ interface AppProps {
235
232
  /**
236
233
  * Exit (unmount) the whole Ink app.
237
234
  */
238
235
  readonly exit: (error?: Error) => void;
239
- }>;
236
+ }
240
237
 
241
238
  /**
242
- * <StdinContext> is a React context, which exposes input stream.
239
+ * `AppContext` is a React context, which exposes a method to manually exit the app (unmount).
243
240
  */
244
- export const StdinContext: React.Context<{
241
+ export const AppContext: React.Context<AppProps>;
242
+
243
+ /**
244
+ * `useApp` is a React hook, which exposes props of `AppContext`.
245
+ * ```js
246
+ * import {useApp} from 'ink';
247
+ *
248
+ * const MyApp = () => {
249
+ * const {exit} = useApp();
250
+ * };
251
+ * ```
252
+ *
253
+ * It's equivalent to consuming `AppContext` props via `AppContext.Consumer`:
254
+ *
255
+ * ```jsx
256
+ * <AppContext.Consumer>
257
+ * {({exit}) => {
258
+ * // …
259
+ * }}
260
+ * </AppContext.Consumer>
261
+ * ```
262
+ */
263
+ export function useApp(): AppProps;
264
+
265
+ interface StdinProps {
245
266
  /**
246
267
  * Stdin stream passed to `render()` in `options.stdin` or `process.stdin` by default. Useful if your app needs to handle user input.
247
268
  */
@@ -257,14 +278,33 @@ export const StdinContext: React.Context<{
257
278
  * If the `stdin` stream passed to Ink does not support setRawMode, this function does nothing.
258
279
  */
259
280
  readonly setRawMode: NodeJS.ReadStream['setRawMode'];
260
- }>;
281
+ }
282
+
283
+ /**
284
+ * `StdinContext` is a React context, which exposes input stream.
285
+ */
286
+ export const StdinContext: React.Context<StdinProps>;
261
287
 
262
288
  /**
263
- * `<StdoutContext>` is a React context, which exposes stdout stream, where Ink renders your app.
289
+ * `useStdin` is a React hook, which exposes props of `StdinContext`.
290
+ * Similar to `useApp`, it's equivalent to consuming `StdinContext` directly.
264
291
  */
265
- export const StdoutContext: React.Context<{
292
+ export function useStdin(): StdinProps;
293
+
294
+ interface StdoutProps {
266
295
  /**
267
296
  * Stdout stream passed to `render()` in `options.stdout` or `process.stdout` by default.
268
297
  */
269
298
  readonly stdout: NodeJS.WriteStream;
270
- }>;
299
+ }
300
+
301
+ /**
302
+ * `StdoutContext` is a React context, which exposes stdout stream, where Ink renders your app.
303
+ */
304
+ export const StdoutContext: React.Context<StdoutProps>;
305
+
306
+ /**
307
+ * `useStdout` is a React hook, which exposes props of `StdoutContext`.
308
+ * Similar to `useStdout`, it's equivalent to consuming `StdoutContext` directly.
309
+ */
310
+ export function useStdout(): StdoutProps;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ink",
3
- "version": "2.4.0",
3
+ "version": "2.7.1",
4
4
  "description": "React for CLI",
5
5
  "license": "MIT",
6
6
  "repository": "vadimdemedes/ink",
@@ -40,51 +40,58 @@
40
40
  "text"
41
41
  ],
42
42
  "dependencies": {
43
- "@types/react": "^16.8.6",
44
- "arrify": "^1.0.1",
45
- "auto-bind": "^2.0.0",
46
- "chalk": "^2.4.1",
47
- "cli-cursor": "^2.1.0",
48
- "cli-truncate": "^1.1.0",
43
+ "ansi-escapes": "^4.2.1",
44
+ "arrify": "^2.0.1",
45
+ "auto-bind": "^4.0.0",
46
+ "chalk": "^3.0.0",
47
+ "cli-cursor": "^3.1.0",
48
+ "cli-truncate": "^2.1.0",
49
49
  "is-ci": "^2.0.0",
50
50
  "lodash.throttle": "^4.1.1",
51
51
  "log-update": "^3.0.0",
52
52
  "prop-types": "^15.6.2",
53
- "react-reconciler": "^0.21.0",
54
- "scheduler": "^0.15.0",
53
+ "react-reconciler": "^0.24.0",
54
+ "scheduler": "^0.18.0",
55
55
  "signal-exit": "^3.0.2",
56
- "slice-ansi": "^1.0.0",
57
- "string-length": "^2.0.0",
58
- "widest-line": "^2.0.0",
59
- "wrap-ansi": "^5.0.0",
56
+ "slice-ansi": "^3.0.0",
57
+ "string-length": "^3.1.0",
58
+ "widest-line": "^3.1.0",
59
+ "wrap-ansi": "^6.2.0",
60
60
  "yoga-layout-prebuilt": "^1.9.3"
61
61
  },
62
62
  "devDependencies": {
63
- "@babel/cli": "^7.1.2",
64
- "@babel/core": "^7.1.2",
65
- "@babel/plugin-proposal-class-properties": "^7.1.0",
66
- "@babel/plugin-proposal-object-rest-spread": "^7.0.0",
67
- "@babel/preset-react": "^7.0.0",
68
- "ava": "^1.3.1",
63
+ "@babel/cli": "^7.8.3",
64
+ "@babel/core": "^7.8.3",
65
+ "@babel/plugin-proposal-class-properties": "^7.8.3",
66
+ "@babel/plugin-proposal-object-rest-spread": "^7.8.3",
67
+ "@babel/preset-react": "^7.8.3",
68
+ "@types/react": "^16.9.19",
69
+ "ava": "^2.4.0",
69
70
  "babel-eslint": "^10.0.1",
70
71
  "delay": "^4.1.0",
71
- "eslint-config-xo-react": "^0.19.0",
72
- "eslint-plugin-react": "^7.11.1",
73
- "eslint-plugin-react-hooks": "^1.4.0",
74
- "import-jsx": "^1.3.0",
72
+ "eslint-config-xo-react": "^0.22.0",
73
+ "eslint-plugin-react": "^7.18.0",
74
+ "eslint-plugin-react-hooks": "^2.3.0",
75
+ "import-jsx": "^3.1.0",
75
76
  "ms": "^2.1.1",
76
- "node-pty": "^0.8.1",
77
- "p-queue": "^3.0.0",
77
+ "node-pty": "^0.9.0",
78
+ "p-queue": "^6.2.1",
78
79
  "react": "^16.9.0",
79
- "sinon": "^7.2.7",
80
- "strip-ansi": "^5.2.0",
80
+ "sinon": "^8.1.1",
81
+ "strip-ansi": "^6.0.0",
81
82
  "svg-term-cli": "^2.1.1",
82
- "tsd": "^0.8.0",
83
- "xo": "^0.24.0"
83
+ "tsd": "^0.11.0",
84
+ "xo": "^0.25.3"
84
85
  },
85
86
  "peerDependencies": {
87
+ "@types/react": ">=16.8.0",
86
88
  "react": ">=16.8.0"
87
89
  },
90
+ "peerDependenciesMeta": {
91
+ "@types/react": {
92
+ "optional": true
93
+ }
94
+ },
88
95
  "babel": {
89
96
  "plugins": [
90
97
  "@babel/plugin-proposal-class-properties",
@@ -107,7 +114,13 @@
107
114
  "@babel/preset-react"
108
115
  ]
109
116
  }
110
- }
117
+ },
118
+ "files": [
119
+ "test/*.js"
120
+ ],
121
+ "helpers": [
122
+ "test/helpers/*.js"
123
+ ]
111
124
  },
112
125
  "xo": {
113
126
  "parser": "babel-eslint",
@@ -118,7 +131,9 @@
118
131
  "react"
119
132
  ],
120
133
  "rules": {
121
- "react/no-unescaped-entities": "off"
134
+ "react/no-unescaped-entities": "off",
135
+ "react/state-in-constructor": "off",
136
+ "react/jsx-indent": "off"
122
137
  },
123
138
  "overrides": [
124
139
  {
@@ -128,7 +143,7 @@
128
143
  ],
129
144
  "rules": {
130
145
  "unicorn/filename-case": "off",
131
- "react/require-default-props": "warning"
146
+ "react/require-default-props": "warn"
132
147
  }
133
148
  }
134
149
  ]
package/readme.md CHANGED
@@ -11,19 +11,17 @@
11
11
 
12
12
  ![Build Status](https://github.com/vadimdemedes/ink/workflows/tests/badge.svg)
13
13
 
14
-
15
14
  ## Install
16
15
 
17
16
  ```
18
17
  $ npm install ink react
19
18
  ```
20
19
 
21
-
22
20
  ## Usage
23
21
 
24
22
  ```jsx
25
- import React, {Component} from 'react';
26
- import {render, Color} from 'ink';
23
+ import React, { Component } from "react";
24
+ import { render, Color } from "ink";
27
25
 
28
26
  class Counter extends Component {
29
27
  constructor() {
@@ -35,11 +33,7 @@ class Counter extends Component {
35
33
  }
36
34
 
37
35
  render() {
38
- return (
39
- <Color green>
40
- {this.state.i} tests passed
41
- </Color>
42
- );
36
+ return <Color green>{this.state.i} tests passed</Color>;
43
37
  }
44
38
 
45
39
  componentDidMount() {
@@ -55,7 +49,7 @@ class Counter extends Component {
55
49
  }
56
50
  }
57
51
 
58
- render(<Counter/>);
52
+ render(<Counter />);
59
53
  ```
60
54
 
61
55
  <img src="media/demo.svg" width="600">
@@ -63,33 +57,39 @@ render(<Counter/>);
63
57
  You can also check it out live on [repl.it sandbox](https://ink-counter-demo.vadimdemedes.repl.run/).
64
58
  Feel 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
59
 
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
- - [changelog-view](https://github.com/jdeniau/changelog-view) - Tool view changelog in console.
73
- - [gomoku-terminal](https://github.com/acrazing/gomoku-terminal) - Play online Gomoku in the terminal.
74
- - [cfpush](https://github.com/mamachanko/cfpush) - An interactive Cloud Foundry tutorial in your terminal.
75
- - [startd](https://github.com/mgrip/startd) - Turn your React component into a web app from the command-line.
76
- - [sindresorhus](https://github.com/sindresorhus/sindresorhus) - The Sindre Sorhus CLI.
77
- - [tap](https://node-tap.org) - A Test-Anything-Protocol library for JavaScript.
78
- - [gatsby-cli](https://www.gatsbyjs.org) - Gatsby is a modern web framework for blazing fast websites.
79
-
60
+ ## Who's Using Ink?
61
+
62
+ - [Gatsby](https://www.gatsbyjs.org) - Gatsby is a modern web framework for blazing fast websites.
63
+ - [Parcel](https://parceljs.org) - Blazing fast, zero configuration web application bundler.
64
+ - [tap](https://node-tap.org) - A Test-Anything-Protocol library for JavaScript.
65
+ - [Typewriter](https://github.com/segmentio/typewriter) - Generates strongly-typed [Segment](https://segment.com) analytics clients from arbitrary JSON Schema.
66
+ - [Prisma](https://www.prisma.io) - The unified data layer for modern applications.
67
+ - [Wallace](https://www.projectwallace.com) - Pretty CSS analytics on the CLI.
68
+ - [tink](https://github.com/npm/tink) - Next-generation runtime and package manager.
69
+ - [Splash](https://github.com/Shopify/polaris-react/tree/master/scripts/splash) - Observe the splash zone of a change across the Shopify's [Polaris](https://polaris.shopify.com) component library.
70
+ - [emoj](https://github.com/sindresorhus/emoj) - Find relevant emoji on the command-line.
71
+ - [emma](https://github.com/maticzav/emma-cli) - Terminal assistant to find and install npm packages.
72
+ - [sindresorhus](https://github.com/sindresorhus/sindresorhus) - The Sindre Sorhus CLI.
73
+ - [swiff](https://github.com/simple-integrated-marketing/swiff) - Multi-environment command line tools for time-saving web developers.
74
+ - [share](https://github.com/marionebl/share-cli) - Quickly share files from your command line.
75
+ - [Kubelive](https://github.com/ameerthehacker/kubelive) - CLI for Kubernetes to provide live data about the cluster and its resources.
76
+ - [changelog-view](https://github.com/jdeniau/changelog-view) - Tool view changelog in console.
77
+ - [gomoku-terminal](https://github.com/acrazing/gomoku-terminal) - Play online Gomoku in the terminal.
78
+ - [cfpush](https://github.com/mamachanko/cfpush) - An interactive Cloud Foundry tutorial in your terminal.
79
+ - [startd](https://github.com/mgrip/startd) - Turn your React component into a web app from the command-line.
80
+ - [wiki-cli](https://github.com/hexrcs/wiki-cli) - Search Wikipedia and read summaries directly in your terminal.
80
81
 
81
82
  ## Contents
82
83
 
83
- - [Getting Started](#getting-started)
84
- - [Examples](#examples)
85
- - [API](#api)
86
- - [Building Layouts](#building-layouts)
87
- - [Built-in Components](#built-in-components)
88
- - [Hooks](#hooks)
89
- - [Useful Components](#useful-components)
90
- - [Testing](#testing)
91
- - [Experimental mode](#experimental-mode)
92
-
84
+ - [Getting Started](#getting-started)
85
+ - [Examples](#examples)
86
+ - [API](#api)
87
+ - [Building Layouts](#building-layouts)
88
+ - [Built-in Components](#built-in-components)
89
+ - [Hooks](#hooks)
90
+ - [Useful Components](#useful-components)
91
+ - [Testing](#testing)
92
+ - [Experimental mode](#experimental-mode)
93
93
 
94
94
  ## Getting Started
95
95
 
@@ -120,25 +120,19 @@ To get started with Ink quickly, use [create-ink-app](https://github.com/vadimde
120
120
  Don't forget to import `React` into every file that contains JSX:
121
121
 
122
122
  ```jsx
123
- import React from 'react';
124
- import {render, Box} from 'ink';
123
+ import React from "react";
124
+ import { render, Box } from "ink";
125
125
 
126
- const Demo = () => (
127
- <Box>
128
- Hello World
129
- </Box>
130
- );
126
+ const Demo = () => <Box>Hello World</Box>;
131
127
 
132
- render(<Demo/>);
128
+ render(<Demo />);
133
129
  ```
134
130
 
135
-
136
131
  ## Examples
137
132
 
138
- - [Jest](examples/jest/jest.js) - Implementation of basic Jest UI [(live demo)](https://ink-jest-demo.vadimdemedes.repl.run/).
139
- - [Counter](examples/counter/counter.js) - Simple counter that increments every 100ms [(live demo)](https://ink-counter-demo.vadimdemedes.repl.run/).
140
- - [Form with Validation](https://github.com/final-form/rff-cli-example) - Using framework agnostic form library, [🏁 Final Form](https://github.com/final-form/final-form#-final-form) to manage input state.
141
-
133
+ - [Jest](examples/jest/jest.js) - Implementation of basic Jest UI [(live demo)](https://ink-jest-demo.vadimdemedes.repl.run/).
134
+ - [Counter](examples/counter/counter.js) - Simple counter that increments every 100ms [(live demo)](https://ink-counter-demo.vadimdemedes.repl.run/).
135
+ - [Form with Validation](https://github.com/final-form/rff-cli-example) - Using framework agnostic form library, [🏁 Final Form](https://github.com/final-form/final-form#-final-form) to manage input state.
142
136
 
143
137
  ## API
144
138
 
@@ -197,8 +191,8 @@ Default: `false`
197
191
  Enables [experimental mode](#experimental-mode).
198
192
 
199
193
  ```jsx
200
- import React, {Component} from 'react';
201
- import {render, Box} from 'ink';
194
+ import React, { Component } from "react";
195
+ import { render, Box } from "ink";
202
196
 
203
197
  class Counter extends Component {
204
198
  constructor() {
@@ -210,11 +204,7 @@ class Counter extends Component {
210
204
  }
211
205
 
212
206
  render() {
213
- return (
214
- <Box>
215
- Iteration #{this.state.i}
216
- </Box>
217
- );
207
+ return <Box>Iteration #{this.state.i}</Box>;
218
208
  }
219
209
 
220
210
  componentDidMount() {
@@ -230,7 +220,7 @@ class Counter extends Component {
230
220
  }
231
221
  }
232
222
 
233
- const app = render(<Counter/>);
223
+ const app = render(<Counter />);
234
224
 
235
225
  setTimeout(() => {
236
226
  // Enough counting
@@ -254,12 +244,12 @@ Replace previous root node with a new one or update props of the current root no
254
244
 
255
245
  ```jsx
256
246
  // Update props of the root node
257
- const {rerender} = render(<Counter count={1}/>);
258
- rerender(<Counter count={2}/>);
247
+ const { rerender } = render(<Counter count={1} />);
248
+ rerender(<Counter count={2} />);
259
249
 
260
250
  // Replace root node
261
- const {rerender} = render(<OldCounter/>);
262
- rerender(<NewCounter/>);
251
+ const { rerender } = render(<OldCounter />);
252
+ rerender(<NewCounter />);
263
253
  ```
264
254
 
265
255
  ##### unmount
@@ -267,7 +257,7 @@ rerender(<NewCounter/>);
267
257
  Manually unmount the whole Ink app.
268
258
 
269
259
  ```jsx
270
- const {unmount} = render(<MyApp/>);
260
+ const { unmount } = render(<MyApp />);
271
261
  unmount();
272
262
  ```
273
263
 
@@ -276,7 +266,7 @@ unmount();
276
266
  Returns a promise, which resolves when app is unmounted.
277
267
 
278
268
  ```jsx
279
- const {unmount, waitUntilExit} = render(<MyApp/>);
269
+ const { unmount, waitUntilExit } = render(<MyApp />);
280
270
 
281
271
  setTimeout(unmount, 1000);
282
272
 
@@ -290,7 +280,6 @@ It's important to remember that each element is a Flexbox container.
290
280
  Think of it as if each `<div>` in the browser had `display: flex`.
291
281
  See `<Box>` built-in component below for documentation on how to use Flexbox layouts in Ink.
292
282
 
293
-
294
283
  ### Built-in Components
295
284
 
296
285
  #### `<Box>`
@@ -300,7 +289,7 @@ See `<Box>` built-in component below for documentation on how to use Flexbox lay
300
289
  Import:
301
290
 
302
291
  ```js
303
- import {Box} from 'ink';
292
+ import { Box } from "ink";
304
293
  ```
305
294
 
306
295
  ##### Dimensions
@@ -317,8 +306,7 @@ Width of the element in spaces. You can also set it in percent, which will calcu
317
306
 
318
307
  ```jsx
319
308
  <Box width={10}>
320
- <Box width="50%">X</Box>
321
- Y
309
+ <Box width="50%">X</Box>Y
322
310
  </Box> //=> 'X Y'
323
311
  ```
324
312
 
@@ -334,8 +322,7 @@ Height of the element in lines (rows). You can also set it in percent, which wil
334
322
 
335
323
  ```jsx
336
324
  <Box height={6} flexDirection="column">
337
- <Box height="50%">X</Box>
338
- Y
325
+ <Box height="50%">X</Box>Y
339
326
  </Box> //=> 'X\n\n\nY\n\n'
340
327
  ```
341
328
 
@@ -360,7 +347,7 @@ Values: `wrap` `truncate` `truncate-start` `truncate-middle` `truncate-end`
360
347
 
361
348
  This property tells Ink to wrap or truncate text content of `<Box>` if its width is larger than container. If `wrap` is passed, Ink will wrap text and split it into multiple lines. If `truncate-*` is passed, Ink will truncate text instead, which will result in one line of text with the rest cut off.
362
349
 
363
- *Note:* Ink doesn't wrap text by default.
350
+ _Note:_ Ink doesn't wrap text by default.
364
351
 
365
352
  ```jsx
366
353
  <Box textWrap="wrap">Hello World</Box>
@@ -483,9 +470,7 @@ See [flex-grow](https://css-tricks.com/almanac/properties/f/flex-grow/).
483
470
  ```jsx
484
471
  <Box>
485
472
  Label:
486
- <Box flexGrow={1}>
487
- Fills all remaining space
488
- </Box>
473
+ <Box flexGrow={1}>Fills all remaining space</Box>
489
474
  </Box>
490
475
  ```
491
476
 
@@ -501,9 +486,7 @@ See [flex-shrink](https://css-tricks.com/almanac/properties/f/flex-shrink/).
501
486
  <Box flexShrink={2} width={10}>
502
487
  Will be 1/4
503
488
  </Box>
504
- <Box width={10}>
505
- Will be 3/4
506
- </Box>
489
+ <Box width={10}>Will be 3/4</Box>
507
490
  </Box>
508
491
  ```
509
492
 
@@ -515,15 +498,13 @@ See [flex-basis](https://css-tricks.com/almanac/properties/f/flex-basis/).
515
498
 
516
499
  ```jsx
517
500
  <Box width={6}>
518
- <Box flexBasis={3}>X</Box>
519
- Y
501
+ <Box flexBasis={3}>X</Box>Y
520
502
  </Box> //=> 'X Y'
521
503
  ```
522
504
 
523
505
  ```jsx
524
506
  <Box width={6}>
525
- <Box flexBasis="50%">X</Box>
526
- Y
507
+ <Box flexBasis="50%">X</Box>Y
527
508
  </Box> //=> 'X Y'
528
509
  ```
529
510
 
@@ -631,7 +612,6 @@ See [justify-content](https://css-tricks.com/almanac/properties/f/justify-conten
631
612
  // [ X Y ]
632
613
  ```
633
614
 
634
-
635
615
  #### `<Color>`
636
616
 
637
617
  The `<Color>` component is a simple wrapper around [the `chalk` API](https://github.com/chalk/chalk#api).
@@ -640,7 +620,7 @@ It supports all of the chalk's methods as `props`.
640
620
  Import:
641
621
 
642
622
  ```js
643
- import {Color} from 'ink';
623
+ import { Color } from "ink";
644
624
  ```
645
625
 
646
626
  Usage:
@@ -666,7 +646,7 @@ This component can change the style of the text, make it bold, underline, italic
666
646
  Import:
667
647
 
668
648
  ```js
669
- import {Text} from 'ink';
649
+ import { Text } from "ink";
670
650
  ```
671
651
 
672
652
  ##### bold
@@ -717,12 +697,12 @@ Jest continuously writes the list of completed tests to the output, while updati
717
697
  <>
718
698
  <Static>
719
699
  {tests.map(test => (
720
- <Test key={test.id} title={test.title}/>
700
+ <Test key={test.id} title={test.title} />
721
701
  ))}
722
702
  </Static>
723
703
 
724
704
  <Box marginTop={1}>
725
- <TestResults passed={results.passed} failed={results.failed}/>
705
+ <TestResults passed={results.passed} failed={results.failed} />
726
706
  </Box>
727
707
  </>
728
708
  ```
@@ -736,7 +716,7 @@ See [examples/jest](examples/jest/jest.js) for a basic implementation of Jest's
736
716
  Import:
737
717
 
738
718
  ```js
739
- import {AppContext} from 'ink';
719
+ import { AppContext } from "ink";
740
720
  ```
741
721
 
742
722
  ##### exit
@@ -765,7 +745,7 @@ If `exit` is called with an Error, `waitUntilExit` will reject with that error.
765
745
  Import:
766
746
 
767
747
  ```js
768
- import {StdinContext} from 'ink';
748
+ import { StdinContext } from "ink";
769
749
  ```
770
750
 
771
751
  ##### stdin
@@ -780,9 +760,7 @@ Usage:
780
760
 
781
761
  ```jsx
782
762
  <StdinContext.Consumer>
783
- {({ stdin }) => (
784
- <MyComponent stdin={stdin}/>
785
- )}
763
+ {({ stdin }) => <MyComponent stdin={stdin} />}
786
764
  </StdinContext.Consumer>
787
765
  ```
788
766
 
@@ -797,9 +775,13 @@ Usage:
797
775
 
798
776
  ```jsx
799
777
  <StdinContext.Consumer>
800
- {({ isRawModeSupported, setRawMode, stdin }) => (
801
- isRawModeSupported ? <MyInputComponent setRawMode={setRawMode} stdin={stdin}/> : <MyComponentThatDoesntUseInput />
802
- )}
778
+ {({ isRawModeSupported, setRawMode, stdin }) =>
779
+ isRawModeSupported ? (
780
+ <MyInputComponent setRawMode={setRawMode} stdin={stdin} />
781
+ ) : (
782
+ <MyComponentThatDoesntUseInput />
783
+ )
784
+ }
803
785
  </StdinContext.Consumer>
804
786
  ```
805
787
 
@@ -808,7 +790,7 @@ Usage:
808
790
  Type: `function`<br>
809
791
 
810
792
  See [`setRawMode`](https://nodejs.org/api/tty.html#tty_readstream_setrawmode_mode).
811
- Ink 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`. Ink also enables `keypress` events via [`readline.emitKeypressEvents()`](https://nodejs.org/api/readline.html#readline_readline_emitkeypressevents_stream_interface) when raw mode is enabled.
793
+ Ink 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`.
812
794
 
813
795
  **Warning:** This function will throw unless the current `stdin` supports `setRawMode`. Use [`isRawModeSupported`](#israwmodesupported) to detect `setRawMode` support.
814
796
 
@@ -816,9 +798,7 @@ Usage:
816
798
 
817
799
  ```jsx
818
800
  <StdinContext.Consumer>
819
- {({ setRawMode }) => (
820
- <MyComponent setRawMode={setRawMode}/>
821
- )}
801
+ {({ setRawMode }) => <MyComponent setRawMode={setRawMode} />}
822
802
  </StdinContext.Consumer>
823
803
  ```
824
804
 
@@ -829,7 +809,7 @@ Usage:
829
809
  Import:
830
810
 
831
811
  ```js
832
- import {StdoutContext} from 'ink';
812
+ import { StdoutContext } from "ink";
833
813
  ```
834
814
 
835
815
  ##### stdout
@@ -841,9 +821,7 @@ Usage:
841
821
 
842
822
  ```jsx
843
823
  <StdoutContext.Consumer>
844
- {({ stdout }) => (
845
- <MyComponent stdout={stdout}/>
846
- )}
824
+ {({ stdout }) => <MyComponent stdout={stdout} />}
847
825
  </StdoutContext.Consumer>
848
826
  ```
849
827
 
@@ -890,8 +868,11 @@ Type: `object`
890
868
  Handy information about a key that was pressed.
891
869
 
892
870
  ##### key.leftArrow
871
+
893
872
  ##### key.rightArrow
873
+
894
874
  ##### key.upArrow
875
+
895
876
  ##### key.downArrow
896
877
 
897
878
  Type: `boolean`<br>
@@ -928,40 +909,76 @@ Default: `false`
928
909
 
929
910
  [Meta key](https://en.wikipedia.org/wiki/Meta_key) was pressed.
930
911
 
912
+ ### useApp
913
+
914
+ `useApp` is a React hook, which exposes props of [`AppContext`](#appcontext).
915
+
916
+ ```js
917
+ import { useApp } from "ink";
918
+
919
+ const MyApp = () => {
920
+ const { exit } = useApp();
921
+ };
922
+ ```
923
+
924
+ It's equivalent to consuming `AppContext` props via `AppContext.Consumer`:
925
+
926
+ ```jsx
927
+ <AppContext.Consumer>
928
+ {({ exit }) => {
929
+ // …
930
+ }}
931
+ </AppContext.Consumer>
932
+ ```
933
+
934
+ ### useStdin
935
+
936
+ `useStdin` is a React hook, which exposes props of [`StdinContext`](#stdincontext).
937
+ Similar to `useApp`, it's equivalent to consuming `StdinContext` directly.
938
+
939
+ ### useStdout
940
+
941
+ `useStdout` is a React hook, which exposes props of [`StdoutContext`](#stdoutcontext).
942
+ Similar to `useApp`, it's equivalent to consuming `StdoutContext` directly.
943
+
944
+ ## Useful Hooks
945
+
946
+ - [ink-use-stdout-dimensions](https://github.com/cameronhunter/ink-monorepo/tree/master/packages/ink-use-stdout-dimensions) - Subscribe to stdout dimensions.
931
947
 
932
948
  ## Useful Components
933
949
 
934
- - [ink-text-input](https://github.com/vadimdemedes/ink-text-input) - Text input.
935
- - [ink-spinner](https://github.com/vadimdemedes/ink-spinner) - Spinner.
936
- - [ink-select-input](https://github.com/vadimdemedes/ink-select-input) - Select (dropdown) input.
937
- - [ink-link](https://github.com/sindresorhus/ink-link) - Link component.
938
- - [ink-box](https://github.com/sindresorhus/ink-box) - Styled box component.
939
- - [ink-gradient](https://github.com/sindresorhus/ink-gradient) - Gradient color component.
940
- - [ink-big-text](https://github.com/sindresorhus/ink-big-text) - Awesome text component.
941
- - [ink-image](https://github.com/kevva/ink-image) - Display images inside the terminal.
942
- - [ink-tab](https://github.com/jdeniau/ink-tab) - Tab component.
943
- - [ink-color-pipe](https://github.com/LitoMore/ink-color-pipe) - Create color text with simpler style strings in Ink.
944
- - [ink-multi-select](https://github.com/karaggeorge/ink-multi-select) - Select one or more values from a list
945
- - [ink-divider](https://github.com/JureSotosek/ink-divider) - A divider component.
946
- - [ink-progress-bar](https://github.com/brigand/ink-progress-bar) - Configurable component for rendering progress bars.
947
- - [ink-table](https://github.com/maticzav/ink-table) - Table component.
950
+ - [ink-text-input](https://github.com/vadimdemedes/ink-text-input) - Text input.
951
+ - [ink-spinner](https://github.com/vadimdemedes/ink-spinner) - Spinner.
952
+ - [ink-select-input](https://github.com/vadimdemedes/ink-select-input) - Select (dropdown) input.
953
+ - [ink-link](https://github.com/sindresorhus/ink-link) - Link component.
954
+ - [ink-box](https://github.com/sindresorhus/ink-box) - Styled box component.
955
+ - [ink-gradient](https://github.com/sindresorhus/ink-gradient) - Gradient color component.
956
+ - [ink-big-text](https://github.com/sindresorhus/ink-big-text) - Awesome text component.
957
+ - [ink-image](https://github.com/kevva/ink-image) - Display images inside the terminal.
958
+ - [ink-tab](https://github.com/jdeniau/ink-tab) - Tab component.
959
+ - [ink-color-pipe](https://github.com/LitoMore/ink-color-pipe) - Create color text with simpler style strings in Ink.
960
+ - [ink-multi-select](https://github.com/karaggeorge/ink-multi-select) - Select one or more values from a list
961
+ - [ink-divider](https://github.com/JureSotosek/ink-divider) - A divider component.
962
+ - [ink-progress-bar](https://github.com/brigand/ink-progress-bar) - Configurable component for rendering progress bars.
963
+ - [ink-table](https://github.com/maticzav/ink-table) - Table component.
964
+ - [ink-ascii](https://github.com/hexrcs/ink-ascii) - Awesome text component with more font choices, based on Figlet.
965
+ - [ink-markdown](https://github.com/cameronhunter/ink-markdown) - Render syntax highlighted Markdown.
948
966
 
949
967
  ### Incompatible components
950
968
 
951
969
  These are components that haven't migrated to Ink 2 yet:
952
970
 
953
- - [ink-console](https://github.com/ForbesLindesay/ink-console) - Render output from `console[method]` calls in a scrollable panel.
954
- - [ink-confirm-input](https://github.com/kevva/ink-confirm-input) - Yes/No confirmation input.
955
- - [ink-checkbox-list](https://github.com/MaxMEllon/ink-checkbox-list) - Checkbox.
956
- - [ink-quicksearch](https://github.com/aicioara/ink-quicksearch) - Select Component with fast quicksearch-like navigation
957
- - [ink-autocomplete](https://github.com/maticzav/ink-autocomplete) - Autocomplete.
958
- - [ink-broadcast](https://github.com/jimmed/ink-broadcast) - Implementation of react-broadcast for Ink.
959
- - [ink-router](https://github.com/jimmed/ink-router) - Implementation of react-router for Ink.
960
- - [ink-select](https://github.com/karaggeorge/ink-select) - Select component.
961
- - [ink-scrollbar](https://github.com/karaggeorge/ink-scrollbar) - Scrollbar component.
962
- - [ink-text-animation](https://github.com/yardnsm/ink-text-animation) - Text animation component.
963
- - [ink-figlet](https://github.com/KimotoYanke/ink-figlet) - Large text component with Figlet fonts.
964
-
971
+ - [ink-console](https://github.com/ForbesLindesay/ink-console) - Render output from `console[method]` calls in a scrollable panel.
972
+ - [ink-confirm-input](https://github.com/kevva/ink-confirm-input) - Yes/No confirmation input.
973
+ - [ink-checkbox-list](https://github.com/MaxMEllon/ink-checkbox-list) - Checkbox.
974
+ - [ink-quicksearch](https://github.com/aicioara/ink-quicksearch) - Select Component with fast quicksearch-like navigation
975
+ - [ink-autocomplete](https://github.com/maticzav/ink-autocomplete) - Autocomplete.
976
+ - [ink-broadcast](https://github.com/jimmed/ink-broadcast) - Implementation of react-broadcast for Ink.
977
+ - [ink-router](https://github.com/jimmed/ink-router) - Implementation of react-router for Ink.
978
+ - [ink-select](https://github.com/karaggeorge/ink-select) - Select component.
979
+ - [ink-scrollbar](https://github.com/karaggeorge/ink-scrollbar) - Scrollbar component.
980
+ - [ink-text-animation](https://github.com/yardnsm/ink-text-animation) - Text animation component.
981
+ - [ink-figlet](https://github.com/KimotoYanke/ink-figlet) - Large text component with Figlet fonts.
965
982
 
966
983
  ## Testing
967
984
 
@@ -969,34 +986,51 @@ Ink components are simple to test with [ink-testing-library](https://github.com/
969
986
  Here's a simple example that checks how component is rendered:
970
987
 
971
988
  ```jsx
972
- import React from 'react';
973
- import {Text} from 'ink';
974
- import {render} from 'ink-testing-library';
989
+ import React from "react";
990
+ import { Text } from "ink";
991
+ import { render } from "ink-testing-library";
975
992
 
976
993
  const Test = () => <Text>Hello World</Text>;
977
- const {lastFrame} = render(<Test/>);
994
+ const { lastFrame } = render(<Test />);
978
995
 
979
- lastFrame() === 'Hello World'; //=> true
996
+ lastFrame() === "Hello World"; //=> true
980
997
  ```
981
998
 
982
999
  Visit [ink-testing-library](https://github.com/vadimdemedes/ink-testing-library) for more examples and full documentation.
983
1000
 
984
1001
  ## Experimental Mode
985
1002
 
986
- Ink has experimental mode, which currently enables a more efficient and faster reconciler and renderer.
1003
+ Ink has experimental mode, which includes stable new features behind a flag.
1004
+ They're exposed behind a flag, because I want to be extra sure that it doesn't introduce regressions before shipping this new code for everyone and making it a default.
1005
+
987
1006
  Instead of shipping it under `next` tag or something similar, Ink ships it as part of a regular release.
988
1007
  It can be enabled simply by passing `experimental` parameter to `render()` function:
989
1008
 
990
1009
  ```jsx
991
- render(<App/>, {experimental: true});
1010
+ render(<App />, { experimental: true });
992
1011
  ```
993
1012
 
994
- Experimental mode actually includes stable features, but I want to be extra sure that it doesn't introduce regressions before shipping this new code for everyone and making it a default.
995
-
996
1013
  Feel free to use experimental mode in development and I would appreciate if you reported any regressions you might see.
997
1014
 
1015
+ ### More efficient reconciler and renderer
1016
+
1017
+ Experimental mode enables a new reconciler and renderer, which should significantly improve the rendering performance of your Ink apps.
1018
+ Ink rebuilds the entire layout and output on every update, which can be taxing if there's a high frequency of updates.
1019
+ Experimental mode ensures only necessary parts of the layout are updated and limits the number of renders to 60 frames per second.
1020
+
1021
+ ### Automatic handling of oversized output
1022
+
1023
+ Unfortunately, terminals can't rerender output that is taller than terminal window.
1024
+ So if your app output has a height of 60 rows, but user resized terminal window to 50 rows, first 10 rows won't be rerendered, because they're out of viewport.
1025
+
1026
+ Experimental mode adopts the same workaround that Jest does, it erases the entire terminal content if output is taller than terminal window. It comes with tradeoffs though:
1027
+
1028
+ - Output can become janky, since erasing terminal is not a "cheap" operation.
1029
+ - Entire scrollback history in that terminal session will be lost.
1030
+
1031
+ It is, however, the only way known now to handle this.
998
1032
 
999
1033
  ## Maintainers
1000
1034
 
1001
- - [Vadim Demedes](https://github.com/vadimdemedes)
1002
- - [Sindre Sorhus](https://github.com/sindresorhus)
1035
+ - [Vadim Demedes](https://github.com/vadimdemedes)
1036
+ - [Sindre Sorhus](https://github.com/sindresorhus)