@nuvin/ink 6.6.7 → 7.0.0-alpha

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.
Files changed (149) hide show
  1. package/build/ansi-tokenizer.d.ts +38 -0
  2. package/build/ansi-tokenizer.js +316 -0
  3. package/build/ansi-tokenizer.js.map +1 -0
  4. package/build/components/AnimationContext.d.ts +9 -0
  5. package/build/components/AnimationContext.js +13 -0
  6. package/build/components/AnimationContext.js.map +1 -0
  7. package/build/components/App.d.ts +11 -49
  8. package/build/components/App.js +412 -227
  9. package/build/components/App.js.map +1 -1
  10. package/build/components/AppContext.d.ts +33 -3
  11. package/build/components/AppContext.js +2 -1
  12. package/build/components/AppContext.js.map +1 -1
  13. package/build/components/Box.d.ts +15 -8
  14. package/build/components/Box.js +12 -10
  15. package/build/components/Box.js.map +1 -1
  16. package/build/components/CursorContext.d.ts +11 -0
  17. package/build/components/CursorContext.js +8 -0
  18. package/build/components/CursorContext.js.map +1 -0
  19. package/build/components/ErrorBoundary.d.ts +18 -0
  20. package/build/components/ErrorBoundary.js +23 -0
  21. package/build/components/ErrorBoundary.js.map +1 -0
  22. package/build/components/ErrorOverview.js +6 -6
  23. package/build/components/ErrorOverview.js.map +1 -1
  24. package/build/components/Static.js.map +1 -1
  25. package/build/components/StdinContext.d.ts +7 -1
  26. package/build/components/StdinContext.js +1 -0
  27. package/build/components/StdinContext.js.map +1 -1
  28. package/build/components/Text.d.ts +1 -1
  29. package/build/components/Text.js +1 -1
  30. package/build/components/Text.js.map +1 -1
  31. package/build/components/Transform.d.ts +1 -1
  32. package/build/cursor-helpers.d.ts +38 -0
  33. package/build/cursor-helpers.js +56 -0
  34. package/build/cursor-helpers.js.map +1 -0
  35. package/build/devtools-window-polyfill.js +7 -4
  36. package/build/devtools-window-polyfill.js.map +1 -1
  37. package/build/devtools.js +31 -6
  38. package/build/devtools.js.map +1 -1
  39. package/build/dom.d.ts +5 -1
  40. package/build/dom.js +20 -1
  41. package/build/dom.js.map +1 -1
  42. package/build/hooks/use-animation.d.ts +49 -0
  43. package/build/hooks/use-animation.js +87 -0
  44. package/build/hooks/use-animation.js.map +1 -0
  45. package/build/hooks/use-app.d.ts +5 -2
  46. package/build/hooks/use-app.js +1 -1
  47. package/build/hooks/use-box-metrics.d.ts +59 -0
  48. package/build/hooks/use-box-metrics.js +88 -0
  49. package/build/hooks/use-box-metrics.js.map +1 -0
  50. package/build/hooks/use-cursor.d.ts +12 -0
  51. package/build/hooks/use-cursor.js +29 -0
  52. package/build/hooks/use-cursor.js.map +1 -0
  53. package/build/hooks/use-focus-manager.d.ts +17 -2
  54. package/build/hooks/use-focus-manager.js +2 -1
  55. package/build/hooks/use-focus-manager.js.map +1 -1
  56. package/build/hooks/use-focus.d.ts +2 -1
  57. package/build/hooks/use-focus.js +5 -4
  58. package/build/hooks/use-focus.js.map +1 -1
  59. package/build/hooks/use-input.d.ts +40 -1
  60. package/build/hooks/use-input.js +84 -51
  61. package/build/hooks/use-input.js.map +1 -1
  62. package/build/hooks/use-is-screen-reader-enabled.d.ts +2 -1
  63. package/build/hooks/use-is-screen-reader-enabled.js +2 -1
  64. package/build/hooks/use-is-screen-reader-enabled.js.map +1 -1
  65. package/build/hooks/use-paste.d.ts +35 -0
  66. package/build/hooks/use-paste.js +62 -0
  67. package/build/hooks/use-paste.js.map +1 -0
  68. package/build/hooks/use-stderr.d.ts +1 -1
  69. package/build/hooks/use-stderr.js +1 -1
  70. package/build/hooks/use-stdin.d.ts +4 -2
  71. package/build/hooks/use-stdin.js +2 -1
  72. package/build/hooks/use-stdin.js.map +1 -1
  73. package/build/hooks/use-stdout.d.ts +1 -1
  74. package/build/hooks/use-stdout.js +1 -1
  75. package/build/hooks/use-window-size.d.ts +18 -0
  76. package/build/hooks/use-window-size.js +22 -0
  77. package/build/hooks/use-window-size.js.map +1 -0
  78. package/build/index.d.ts +14 -1
  79. package/build/index.js +7 -0
  80. package/build/index.js.map +1 -1
  81. package/build/ink.d.ts +86 -5
  82. package/build/ink.js +581 -80
  83. package/build/ink.js.map +1 -1
  84. package/build/input-parser.d.ts +10 -0
  85. package/build/input-parser.js +194 -0
  86. package/build/input-parser.js.map +1 -0
  87. package/build/kitty-keyboard.d.ts +23 -0
  88. package/build/kitty-keyboard.js +32 -0
  89. package/build/kitty-keyboard.js.map +1 -0
  90. package/build/log-update.d.ts +7 -1
  91. package/build/log-update.js +181 -56
  92. package/build/log-update.js.map +1 -1
  93. package/build/measure-element.d.ts +4 -0
  94. package/build/measure-element.js +4 -0
  95. package/build/measure-element.js.map +1 -1
  96. package/build/measure-text.js +3 -11
  97. package/build/measure-text.js.map +1 -1
  98. package/build/output.d.ts +2 -1
  99. package/build/output.js +62 -58
  100. package/build/output.js.map +1 -1
  101. package/build/parse-keypress.d.ts +9 -3
  102. package/build/parse-keypress.js +283 -13
  103. package/build/parse-keypress.js.map +1 -1
  104. package/build/reconciler.js +57 -37
  105. package/build/reconciler.js.map +1 -1
  106. package/build/render-background.js +2 -0
  107. package/build/render-background.js.map +1 -1
  108. package/build/render-border.d.ts +1 -1
  109. package/build/render-border.js +35 -24
  110. package/build/render-border.js.map +1 -1
  111. package/build/render-node-to-output.d.ts +1 -1
  112. package/build/render-node-to-output.js +35 -18
  113. package/build/render-node-to-output.js.map +1 -1
  114. package/build/render-to-string.d.ts +38 -0
  115. package/build/render-to-string.js +116 -0
  116. package/build/render-to-string.js.map +1 -0
  117. package/build/render.d.ts +91 -3
  118. package/build/render.js +17 -5
  119. package/build/render.js.map +1 -1
  120. package/build/sanitize-ansi.d.ts +2 -0
  121. package/build/sanitize-ansi.js +27 -0
  122. package/build/sanitize-ansi.js.map +1 -0
  123. package/build/squash-text-nodes.js +2 -1
  124. package/build/squash-text-nodes.js.map +1 -1
  125. package/build/styles.d.ts +75 -37
  126. package/build/styles.js +87 -44
  127. package/build/styles.js.map +1 -1
  128. package/build/utils.d.ts +9 -2
  129. package/build/utils.js +18 -3
  130. package/build/utils.js.map +1 -1
  131. package/build/wrap-text.js +7 -14
  132. package/build/wrap-text.js.map +1 -1
  133. package/build/write-synchronized.d.ts +4 -0
  134. package/build/write-synchronized.js +9 -0
  135. package/build/write-synchronized.js.map +1 -0
  136. package/package.json +45 -105
  137. package/readme.md +850 -64
  138. package/build/components/FixedLayout.d.ts +0 -12
  139. package/build/components/FixedLayout.js +0 -19
  140. package/build/components/FixedLayout.js.map +0 -1
  141. package/build/components/VirtualizedList.d.ts +0 -21
  142. package/build/components/VirtualizedList.js +0 -55
  143. package/build/components/VirtualizedList.js.map +0 -1
  144. package/build/hooks/use-mouse.d.ts +0 -15
  145. package/build/hooks/use-mouse.js +0 -121
  146. package/build/hooks/use-mouse.js.map +0 -1
  147. package/build/hooks/use-virtualization.d.ts +0 -19
  148. package/build/hooks/use-virtualization.js +0 -56
  149. package/build/hooks/use-virtualization.js.map +0 -1
package/readme.md CHANGED
@@ -24,6 +24,8 @@ Since Ink is a React renderer, all features of React are supported.
24
24
  Head over to the [React](https://reactjs.org) website for documentation on how to use it.
25
25
  Only Ink's methods are documented in this readme.
26
26
 
27
+ **Fully AI-generated pull requests are not accepted. You can use AI, but should be verified and cleaned up by a human. Only Opus 4.6+ (high-effort) and Codex 5.4+ (extra high) are accepted models. Preferably created with Opus and verified by Codex.**
28
+
27
29
  ---
28
30
 
29
31
  <div align="center">
@@ -42,6 +44,9 @@ Only Ink's methods are documented in this readme.
42
44
  npm install ink react
43
45
  ```
44
46
 
47
+ > [!NOTE]
48
+ > This readme documents the upcoming version of Ink. For the latest stable release, see [Ink on npm](https://www.npmjs.com/package/ink).
49
+
45
50
  ## Usage
46
51
 
47
52
  ```jsx
@@ -69,13 +74,11 @@ render(<Counter />);
69
74
 
70
75
  <img src="media/demo.svg" width="600">
71
76
 
72
- 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).
73
-
74
77
  ## Who's Using Ink?
75
78
 
76
79
  - [Claude Code](https://github.com/anthropics/claude-code) - An agentic coding tool made by Anthropic.
77
80
  - [Gemini CLI](https://github.com/google-gemini/gemini-cli) - An agentic coding tool made by Google.
78
- - [GitHub Copilot for CLI](https://githubnext.com/projects/copilot-cli) - Just say what you want the shell to do.
81
+ - [GitHub Copilot CLI](https://github.com/features/copilot/cli) - Just say what you want the shell to do.
79
82
  - [Canva CLI](https://www.canva.dev/docs/apps/canva-cli/) - CLI for creating and managing Canva Apps.
80
83
  - [Cloudflare's Wrangler](https://github.com/cloudflare/wrangler2) - The CLI for Cloudflare Workers.
81
84
  - [Linear](https://linear.app) - Linear built an internal CLI for managing deployments, configs, and other housekeeping tasks.
@@ -123,15 +126,18 @@ Feel free to play around with the code and fork this Repl at [https://repl.it/@v
123
126
  - [argonaut](https://github.com/darksworm/argonaut) - Manage Argo CD resources.
124
127
  - [Qodo Command](https://github.com/qodo-ai/command) - Build, run, and manage AI agents.
125
128
  - [Nanocoder](https://github.com/nano-collective/nanocoder) - A community-built, local-first AI coding agent with multi-provider support.
129
+ - [dev3000](https://github.com/vercel-labs/dev3000) - An AI agent MCP orchestrator and developer browser.
126
130
  - [Neovate Code](https://github.com/neovateai/neovate-code) - An agentic coding tool made by AntGroup.
127
131
  - [instagram-cli](https://github.com/supreme-gg-gg/instagram-cli) - Instagram client.
128
132
  - [ElevenLabs CLI](https://github.com/elevenlabs/cli) - ElevenLabs agents client.
133
+ - [SSH AI Chat](https://github.com/miantiao-me/ssh-ai-chat) - Chat with AI over SSH.
129
134
 
130
- *(PRs welcome. Append new entries at the end. Repos must have 100+ stars and showcase Ink beyond a basic list picker.)*
135
+ _(PRs welcome. Append new entries at the end. Repos must have 100+ stars and showcase Ink beyond a basic list picker.)_
131
136
 
132
137
  ## Contents
133
138
 
134
139
  - [Getting Started](#getting-started)
140
+ - [App Lifecycle](#app-lifecycle)
135
141
  - [Components](#components)
136
142
  - [`<Text>`](#text)
137
143
  - [`<Box>`](#box)
@@ -141,19 +147,26 @@ Feel free to play around with the code and fork this Repl at [https://repl.it/@v
141
147
  - [`<Transform>`](#transform)
142
148
  - [Hooks](#hooks)
143
149
  - [`useInput`](#useinputinputhandler-options)
150
+ - [`usePaste`](#usepastehandler-options)
144
151
  - [`useApp`](#useapp)
145
152
  - [`useStdin`](#usestdin)
146
153
  - [`useStdout`](#usestdout)
154
+ - [`useBoxMetrics`](#useboxmetricsref)
147
155
  - [`useStderr`](#usestderr)
156
+ - [`useWindowSize`](#usewindowsize)
148
157
  - [`useFocus`](#usefocusoptions)
149
158
  - [`useFocusManager`](#usefocusmanager)
159
+ - [`useCursor`](#usecursor)
160
+ - [`useAnimation`](#useanimationoptions)
150
161
  - [API](#api)
151
162
  - [Testing](#testing)
152
163
  - [Using React Devtools](#using-react-devtools)
153
164
  - [Screen Reader Support](#screen-reader-support)
154
165
  - [Useful Components](#useful-components)
155
166
  - [Useful Hooks](#useful-hooks)
167
+ - [Recipes](#recipes)
156
168
  - [Examples](#examples)
169
+ - [Continuous Integration](#continuous-integration)
157
170
 
158
171
  ## Getting Started
159
172
 
@@ -220,6 +233,22 @@ Think of it as if every `<div>` in the browser had `display: flex`.
220
233
  See [`<Box>`](#box) built-in component below for documentation on how to use Flexbox layouts in Ink.
221
234
  Note that all text must be wrapped in a [`<Text>`](#text) component.
222
235
 
236
+ ## App Lifecycle
237
+
238
+ An Ink app is a Node.js process, so it stays alive only while there is active work in the event loop (timers, pending promises, [`useInput`](#useinputinputhandler-options) listening on `stdin`, etc.). If your component tree has no async work, the app will render once and exit immediately.
239
+
240
+ To exit the app, press **Ctrl+C** (enabled by default via [`exitOnCtrlC`](#exitonctrlc)), call [`exit()`](#exiterrororresult) from [`useApp`](#useapp) inside a component, or call [`unmount()`](#unmount) on the object returned by [`render()`](#rendertree-options).
241
+
242
+ Use [`waitUntilExit()`](#waituntilexit) to run code after the app is unmounted:
243
+
244
+ ```jsx
245
+ const {waitUntilExit} = render(<MyApp />);
246
+
247
+ await waitUntilExit();
248
+
249
+ console.log('App exited');
250
+ ```
251
+
223
252
  ## Components
224
253
 
225
254
  ### `<Text>`
@@ -247,7 +276,8 @@ const Example = () => (
247
276
  render(<Example />);
248
277
  ```
249
278
 
250
- **Note:** `<Text>` allows only text nodes and nested `<Text>` components inside of it. For example, `<Box>` component can't be used inside `<Text>`.
279
+ > [!NOTE]
280
+ > `<Text>` allows only text nodes and nested `<Text>` components inside of it. For example, `<Box>` component can't be used inside `<Text>`.
251
281
 
252
282
  #### color
253
283
 
@@ -339,11 +369,12 @@ Invert background and foreground colors.
339
369
  #### wrap
340
370
 
341
371
  Type: `string`\
342
- Allowed values: `wrap` `truncate` `truncate-start` `truncate-middle` `truncate-end`\
372
+ Allowed values: `wrap` `hard` `truncate` `truncate-start` `truncate-middle` `truncate-end`\
343
373
  Default: `wrap`
344
374
 
345
375
  This property tells Ink to wrap or truncate text if its width is larger than the container.
346
376
  If `wrap` is passed (the default), Ink will wrap text and split it into multiple lines.
377
+ If `hard` is passed, Ink will fill each line to the full column width, breaking words as necessary.
347
378
  If `truncate-*` is passed, Ink will truncate text instead, resulting in one line of text with the rest cut off.
348
379
 
349
380
  ```jsx
@@ -352,6 +383,11 @@ If `truncate-*` is passed, Ink will truncate text instead, resulting in one line
352
383
  </Box>
353
384
  //=> 'Hello\nWorld'
354
385
 
386
+ <Box width={7}>
387
+ <Text wrap="hard">Hello World</Text>
388
+ </Box>
389
+ //=> 'Hello W\norld'
390
+
355
391
  // `truncate` is an alias to `truncate-end`
356
392
  <Box width={7}>
357
393
  <Text wrap="truncate">Hello World</Text>
@@ -445,11 +481,33 @@ Percentages aren't supported yet; see https://github.com/facebook/yoga/issues/87
445
481
 
446
482
  ##### minHeight
447
483
 
484
+ Type: `number` `string`
485
+
486
+ Sets a minimum height of the element in lines (rows).
487
+ You can also set it as a percentage, which will calculate the minimum height based on the height of the parent element.
488
+
489
+ ##### maxWidth
490
+
448
491
  Type: `number`
449
492
 
450
- Sets a minimum height of the element.
493
+ Sets a maximum width of the element.
451
494
  Percentages aren't supported yet; see https://github.com/facebook/yoga/issues/872.
452
495
 
496
+ ##### maxHeight
497
+
498
+ Type: `number` `string`
499
+
500
+ Sets a maximum height of the element in lines (rows).
501
+ You can also set it as a percentage, which will calculate the maximum height based on the height of the parent element.
502
+
503
+ ##### aspectRatio
504
+
505
+ Type: `number`
506
+
507
+ Defines the aspect ratio (width/height) for the element.
508
+
509
+ Use it with at least one size constraint (`width`, `height`, `minHeight`, or `maxHeight`) so Ink can derive the missing dimension.
510
+
453
511
  #### Padding
454
512
 
455
513
  ##### paddingTop
@@ -754,7 +812,7 @@ See [flex-wrap](https://css-tricks.com/almanac/properties/f/flex-wrap/).
754
812
  ##### alignItems
755
813
 
756
814
  Type: `string`\
757
- Allowed values: `flex-start` `center` `flex-end`
815
+ Allowed values: `flex-start` `center` `flex-end` `stretch` `baseline`
758
816
 
759
817
  See [align-items](https://css-tricks.com/almanac/properties/a/align-items/).
760
818
 
@@ -812,7 +870,7 @@ See [align-items](https://css-tricks.com/almanac/properties/a/align-items/).
812
870
 
813
871
  Type: `string`\
814
872
  Default: `auto`\
815
- Allowed values: `auto` `flex-start` `center` `flex-end`
873
+ Allowed values: `auto` `flex-start` `center` `flex-end` `stretch` `baseline`
816
874
 
817
875
  See [align-self](https://css-tricks.com/almanac/properties/a/align-self/).
818
876
 
@@ -845,6 +903,16 @@ See [align-self](https://css-tricks.com/almanac/properties/a/align-self/).
845
903
  // X
846
904
  ```
847
905
 
906
+ ##### alignContent
907
+
908
+ Type: `string`\
909
+ Default: `flex-start`\
910
+ Allowed values: `flex-start` `flex-end` `center` `stretch` `space-between` `space-around` `space-evenly`
911
+
912
+ Defines alignment between flex lines on the cross axis when `flexWrap` creates multiple lines.
913
+ See [align-content](https://css-tricks.com/almanac/properties/a/align-content/).
914
+ Unlike CSS (`stretch`), Ink defaults to `flex-start` so wrapped lines stay compact and fixed-height boxes don't gain unexpected empty rows unless you opt in to stretching.
915
+
848
916
  ##### justifyContent
849
917
 
850
918
  Type: `string`\
@@ -887,6 +955,46 @@ See [justify-content](https://css-tricks.com/almanac/properties/j/justify-conten
887
955
  // [ X Y ]
888
956
  ```
889
957
 
958
+ #### Position
959
+
960
+ ##### position
961
+
962
+ Type: `string`\
963
+ Allowed values: `relative` `absolute` `static`\
964
+ Default: `relative`
965
+
966
+ Controls how the element is positioned.
967
+
968
+ When `position` is `static`, `top`, `right`, `bottom`, and `left` are ignored.
969
+
970
+ ##### top
971
+
972
+ Type: `number` `string`
973
+
974
+ Top offset for positioned elements.
975
+ You can also set it as a percentage of the parent size.
976
+
977
+ ##### right
978
+
979
+ Type: `number` `string`
980
+
981
+ Right offset for positioned elements.
982
+ You can also set it as a percentage of the parent size.
983
+
984
+ ##### bottom
985
+
986
+ Type: `number` `string`
987
+
988
+ Bottom offset for positioned elements.
989
+ You can also set it as a percentage of the parent size.
990
+
991
+ ##### left
992
+
993
+ Type: `number` `string`
994
+
995
+ Left offset for positioned elements.
996
+ You can also set it as a percentage of the parent size.
997
+
890
998
  #### Visibility
891
999
 
892
1000
  ##### display
@@ -982,7 +1090,7 @@ Alternatively, pass a custom border style like so:
982
1090
  bottomLeft: '↗',
983
1091
  bottom: '↑',
984
1092
  bottomRight: '↖',
985
- right: '←'
1093
+ right: '←',
986
1094
  }}
987
1095
  >
988
1096
  <Text>Custom</Text>
@@ -1023,7 +1131,7 @@ Accepts the same values as [`color`](#color) in `<Text>` component.
1023
1131
 
1024
1132
  Type: `string`
1025
1133
 
1026
- Change right border color.
1134
+ Change the right border color.
1027
1135
  Accepts the same values as [`color`](#color) in `<Text>` component.
1028
1136
 
1029
1137
  ```jsx
@@ -1036,7 +1144,7 @@ Accepts the same values as [`color`](#color) in `<Text>` component.
1036
1144
 
1037
1145
  Type: `string`
1038
1146
 
1039
- Change bottom border color.
1147
+ Change the bottom border color.
1040
1148
  Accepts the same values as [`color`](#color) in `<Text>` component.
1041
1149
 
1042
1150
  ```jsx
@@ -1049,7 +1157,7 @@ Accepts the same values as [`color`](#color) in `<Text>` component.
1049
1157
 
1050
1158
  Type: `string`
1051
1159
 
1052
- Change left border color.
1160
+ Change the left border color.
1053
1161
  Accepts the same values as [`color`](#color) in `<Text>` component.
1054
1162
 
1055
1163
  ```jsx
@@ -1124,33 +1232,103 @@ Dim the right border color.
1124
1232
  </Box>
1125
1233
  ```
1126
1234
 
1235
+ ##### borderBackgroundColor
1236
+
1237
+ Type: `string`
1238
+
1239
+ Change border background color.
1240
+ Accepts the same values as [`backgroundColor`](#backgroundcolor) in `<Text>` component.
1241
+ A shorthand for setting `borderTopBackgroundColor`, `borderRightBackgroundColor`, `borderBottomBackgroundColor`, and `borderLeftBackgroundColor`.
1242
+
1243
+ ```jsx
1244
+ <Box borderStyle="round" borderColor="white" borderBackgroundColor="green">
1245
+ <Text>Hello world</Text>
1246
+ </Box>
1247
+ ```
1248
+
1249
+ ##### borderTopBackgroundColor
1250
+
1251
+ Type: `string`
1252
+
1253
+ Change top border background color.
1254
+ Accepts the same values as [`backgroundColor`](#backgroundcolor) in `<Text>` component.
1255
+ Falls back to `borderBackgroundColor` if not specified.
1256
+
1257
+ ```jsx
1258
+ <Box borderStyle="round" borderColor="white" borderTopBackgroundColor="green">
1259
+ <Text>Hello world</Text>
1260
+ </Box>
1261
+ ```
1262
+
1263
+ ##### borderBottomBackgroundColor
1264
+
1265
+ Type: `string`
1266
+
1267
+ Change bottom border background color.
1268
+ Accepts the same values as [`backgroundColor`](#backgroundcolor) in `<Text>` component.
1269
+ Falls back to `borderBackgroundColor` if not specified.
1270
+
1271
+ ```jsx
1272
+ <Box borderStyle="round" borderColor="white" borderBottomBackgroundColor="green">
1273
+ <Text>Hello world</Text>
1274
+ </Box>
1275
+ ```
1276
+
1277
+ ##### borderRightBackgroundColor
1278
+
1279
+ Type: `string`
1280
+
1281
+ Change right border background color.
1282
+ Accepts the same values as [`backgroundColor`](#backgroundcolor) in `<Text>` component.
1283
+ Falls back to `borderBackgroundColor` if not specified.
1284
+
1285
+ ```jsx
1286
+ <Box borderStyle="round" borderColor="white" borderRightBackgroundColor="green">
1287
+ <Text>Hello world</Text>
1288
+ </Box>
1289
+ ```
1290
+
1291
+ ##### borderLeftBackgroundColor
1292
+
1293
+ Type: `string`
1294
+
1295
+ Change left border background color.
1296
+ Accepts the same values as [`backgroundColor`](#backgroundcolor) in `<Text>` component.
1297
+ Falls back to `borderBackgroundColor` if not specified.
1298
+
1299
+ ```jsx
1300
+ <Box borderStyle="round" borderColor="white" borderLeftBackgroundColor="green">
1301
+ <Text>Hello world</Text>
1302
+ </Box>
1303
+ ```
1304
+
1127
1305
  ##### borderTop
1128
1306
 
1129
1307
  Type: `boolean`\
1130
1308
  Default: `true`
1131
1309
 
1132
- Determines whether top border is visible.
1310
+ Determines whether the top border is visible.
1133
1311
 
1134
1312
  ##### borderRight
1135
1313
 
1136
1314
  Type: `boolean`\
1137
1315
  Default: `true`
1138
1316
 
1139
- Determines whether right border is visible.
1317
+ Determines whether the right border is visible.
1140
1318
 
1141
1319
  ##### borderBottom
1142
1320
 
1143
1321
  Type: `boolean`\
1144
1322
  Default: `true`
1145
1323
 
1146
- Determines whether bottom border is visible.
1324
+ Determines whether the bottom border is visible.
1147
1325
 
1148
1326
  ##### borderLeft
1149
1327
 
1150
1328
  Type: `boolean`\
1151
1329
  Default: `true`
1152
1330
 
1153
- Determines whether left border is visible.
1331
+ Determines whether the left border is visible.
1154
1332
 
1155
1333
  #### Background
1156
1334
 
@@ -1168,11 +1346,23 @@ Accepts the same values as [`color`](#color) in the `<Text>` component.
1168
1346
  <Text>Red background</Text>
1169
1347
  </Box>
1170
1348
 
1171
- <Box backgroundColor="#FF8800" width={20} height={3} marginTop={1} alignSelf="flex-start">
1349
+ <Box
1350
+ backgroundColor="#FF8800"
1351
+ width={20}
1352
+ height={3}
1353
+ marginTop={1}
1354
+ alignSelf="flex-start"
1355
+ >
1172
1356
  <Text>Orange background</Text>
1173
1357
  </Box>
1174
1358
 
1175
- <Box backgroundColor="rgb(0, 255, 0)" width={20} height={3} marginTop={1} alignSelf="flex-start">
1359
+ <Box
1360
+ backgroundColor="rgb(0, 255, 0)"
1361
+ width={20}
1362
+ height={3}
1363
+ marginTop={1}
1364
+ alignSelf="flex-start"
1365
+ >
1176
1366
  <Text>Green background</Text>
1177
1367
  </Box>
1178
1368
  </Box>
@@ -1191,7 +1381,12 @@ The background color fills the entire `<Box>` area and is inherited by child `<T
1191
1381
  Background colors work with borders and padding:
1192
1382
 
1193
1383
  ```jsx
1194
- <Box backgroundColor="cyan" borderStyle="round" padding={1} alignSelf="flex-start">
1384
+ <Box
1385
+ backgroundColor="cyan"
1386
+ borderStyle="round"
1387
+ padding={1}
1388
+ alignSelf="flex-start"
1389
+ >
1195
1390
  <Text>Background with border and padding</Text>
1196
1391
  </Box>
1197
1392
  ```
@@ -1300,8 +1495,8 @@ const Example = () => {
1300
1495
  ...previousTests,
1301
1496
  {
1302
1497
  id: previousTests.length,
1303
- title: `Test #${previousTests.length + 1}`
1304
- }
1498
+ title: `Test #${previousTests.length + 1}`,
1499
+ },
1305
1500
  ]);
1306
1501
 
1307
1502
  timer = setTimeout(run, 100);
@@ -1337,9 +1532,8 @@ const Example = () => {
1337
1532
  render(<Example />);
1338
1533
  ```
1339
1534
 
1340
- **Note:** `<Static>` only renders new items in the `items` prop and ignores items
1341
- that were previously rendered. This means that when you add new items to the `items`
1342
- array, changes you make to previous items will not trigger a rerender.
1535
+ > [!NOTE]
1536
+ > `<Static>` only renders new items in the `items` prop and ignores items that were previously rendered. This means that when you add new items to the `items` array, changes you make to previous items will not trigger a rerender.
1343
1537
 
1344
1538
  See [examples/static](examples/static/static.tsx) for an example usage of `<Static>` component.
1345
1539
 
@@ -1394,7 +1588,11 @@ For example, you might want to apply a [gradient to text](https://github.com/sin
1394
1588
  These use cases can't accept React nodes as input; they expect a string.
1395
1589
  That's what the `<Transform>` component does: it gives you an output string of its child components and lets you transform it in any way.
1396
1590
 
1397
- **Note:** `<Transform>` must be applied only to `<Text>` children components and shouldn't change the dimensions of the output; otherwise, the layout will be incorrect.
1591
+ > [!NOTE]
1592
+ > `<Transform>` must be applied only to `<Text>` children components and shouldn't change the dimensions of the output; otherwise, the layout will be incorrect.
1593
+
1594
+ > [!IMPORTANT]
1595
+ > When children use `<Text>` styling props (e.g. `color`, `bold`), the string passed to `transform` will contain [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code). If your transform manipulates whitespace or does string operations like `.trim()`, you may need to use ANSI-aware methods (e.g. from [`slice-ansi`](https://github.com/chalk/slice-ansi) or [`strip-ansi`](https://github.com/chalk/strip-ansi)).
1398
1596
 
1399
1597
  ```jsx
1400
1598
  import {render, Transform} from 'ink';
@@ -1417,12 +1615,11 @@ For example, to implement a hanging indent component, you can indent all the lin
1417
1615
  ```jsx
1418
1616
  import {render, Transform} from 'ink';
1419
1617
 
1420
- const HangingIndent = ({content, indent = 4, children, ...props}) => (
1618
+ const HangingIndent = ({indent = 4, children}) => (
1421
1619
  <Transform
1422
1620
  transform={(line, index) =>
1423
1621
  index === 0 ? line : ' '.repeat(indent) + line
1424
1622
  }
1425
- {...props}
1426
1623
  >
1427
1624
  {children}
1428
1625
  </Transform>
@@ -1436,12 +1633,7 @@ const text =
1436
1633
  'of my hands only. I lived there two years and two months. At ' +
1437
1634
  'present I am a sojourner in civilized life again.';
1438
1635
 
1439
- // Other text properties are allowed as well
1440
- render(
1441
- <HangingIndent bold dimColor indent={4}>
1442
- {text}
1443
- </HangingIndent>
1444
- );
1636
+ render(<HangingIndent indent={4}>{text}</HangingIndent>);
1445
1637
  ```
1446
1638
 
1447
1639
  #### transform(outputLine, index)
@@ -1467,7 +1659,7 @@ The zero-indexed line number of the line that's currently being transformed.
1467
1659
 
1468
1660
  ### useInput(inputHandler, options?)
1469
1661
 
1470
- This hook is used for handling user input.
1662
+ A React hook that returns `void` and handles user input.
1471
1663
  It's a more convenient alternative to using `useStdin` and listening for `data` events.
1472
1664
  The callback you pass to `useInput` is called for each character when the user enters any input.
1473
1665
  However, if the user pastes text and it's more than one character, the callback will be called only once, and the whole string will be passed as `input`.
@@ -1582,6 +1774,16 @@ Default: `false`
1582
1774
  If the Page Up or Page Down key was pressed, the corresponding property will be `true`.
1583
1775
  For example, if the user presses Page Down, `key.pageDown` equals `true`.
1584
1776
 
1777
+ ###### key.home
1778
+
1779
+ ###### key.end
1780
+
1781
+ Type: `boolean`\
1782
+ Default: `false`
1783
+
1784
+ If the Home or End key was pressed, the corresponding property will be `true`.
1785
+ For example, if the user presses End, `key.end` equals `true`.
1786
+
1585
1787
  ###### key.meta
1586
1788
 
1587
1789
  Type: `boolean`\
@@ -1589,6 +1791,41 @@ Default: `false`
1589
1791
 
1590
1792
  [Meta key](https://en.wikipedia.org/wiki/Meta_key) was pressed.
1591
1793
 
1794
+ ###### key.super
1795
+
1796
+ Type: `boolean`\
1797
+ Default: `false`
1798
+
1799
+ Super key (Cmd on macOS, Win on Windows) was pressed. Requires [kitty keyboard protocol](#kittykeyboard).
1800
+
1801
+ ###### key.hyper
1802
+
1803
+ Type: `boolean`\
1804
+ Default: `false`
1805
+
1806
+ Hyper key was pressed. Requires [kitty keyboard protocol](#kittykeyboard).
1807
+
1808
+ ###### key.capsLock
1809
+
1810
+ Type: `boolean`\
1811
+ Default: `false`
1812
+
1813
+ Caps Lock was active. Requires [kitty keyboard protocol](#kittykeyboard).
1814
+
1815
+ ###### key.numLock
1816
+
1817
+ Type: `boolean`\
1818
+ Default: `false`
1819
+
1820
+ Num Lock was active. Requires [kitty keyboard protocol](#kittykeyboard).
1821
+
1822
+ ###### key.eventType
1823
+
1824
+ Type: `'press' | 'repeat' | 'release'`\
1825
+ Default: `undefined`
1826
+
1827
+ The type of key event. Only available with [kitty keyboard protocol](#kittykeyboard). Without the protocol, this property is `undefined`.
1828
+
1592
1829
  #### options
1593
1830
 
1594
1831
  Type: `object`
@@ -1601,23 +1838,77 @@ Default: `true`
1601
1838
  Enable or disable capturing of user input.
1602
1839
  Useful when there are multiple `useInput` hooks used at once to avoid handling the same input several times.
1603
1840
 
1841
+ ### usePaste(handler, options?)
1842
+
1843
+ A React hook that calls `handler` whenever the user pastes text. Bracketed paste mode (`\x1b[?2004h`) is automatically enabled while the hook is active, so pasted text arrives as a single string rather than being misinterpreted as individual key presses.
1844
+
1845
+ `usePaste` and `useInput` can be used together in the same component. They operate on separate event channels, so paste content is never forwarded to `useInput` handlers when `usePaste` is active.
1846
+
1847
+ ```jsx
1848
+ import {useInput, usePaste} from 'ink';
1849
+
1850
+ const MyInput = () => {
1851
+ useInput((input, key) => {
1852
+ // Only receives typed characters and key events, not pasted text.
1853
+ if (key.return) {
1854
+ // Submit
1855
+ }
1856
+ });
1857
+
1858
+ usePaste((text) => {
1859
+ // Receives the full pasted string, including newlines.
1860
+ console.log('Pasted:', text);
1861
+ });
1862
+
1863
+ return …
1864
+ };
1865
+ ```
1866
+
1867
+ #### handler(text)
1868
+
1869
+ Type: `Function`
1870
+
1871
+ Called with the full pasted string whenever the user pastes text. The string is delivered verbatim — newlines, escape sequences, and other special characters are preserved exactly as pasted.
1872
+
1873
+ ##### text
1874
+
1875
+ Type: `string`
1876
+
1877
+ The pasted text.
1878
+
1879
+ #### options
1880
+
1881
+ Type: `object`
1882
+
1883
+ ##### isActive
1884
+
1885
+ Type: `boolean`\
1886
+ Default: `true`
1887
+
1888
+ Enable or disable the paste handler. Useful when multiple components use `usePaste` and only one should be active at a time.
1889
+
1604
1890
  ### useApp()
1605
1891
 
1606
- `useApp` is a React hook that exposes a method to manually exit the app (unmount).
1892
+ A React hook that returns app lifecycle methods.
1607
1893
 
1608
- #### exit(error?)
1894
+ #### exit(errorOrResult?)
1609
1895
 
1610
1896
  Type: `Function`
1611
1897
 
1612
1898
  Exit (unmount) the whole Ink app.
1613
1899
 
1614
- ##### error
1900
+ ##### errorOrResult
1901
+
1902
+ Type: `Error | unknown`
1615
1903
 
1616
- Type: `Error`
1904
+ Optional value that controls how [`waitUntilExit`](#waituntilexit) settles:
1617
1905
 
1618
- Optional error. If passed, [`waitUntilExit`](waituntilexit) will reject with that error.
1906
+ - `exit()` resolves with `undefined`.
1907
+ - `exit(error)` rejects when `error` is an `Error`.
1908
+ - `exit(value)` resolves with `value`.
1619
1909
 
1620
1910
  ```js
1911
+ import {useEffect} from 'react';
1621
1912
  import {useApp} from 'ink';
1622
1913
 
1623
1914
  const Example = () => {
@@ -1628,15 +1919,39 @@ const Example = () => {
1628
1919
  setTimeout(() => {
1629
1920
  exit();
1630
1921
  }, 5000);
1631
- }, []);
1922
+ }, [exit]);
1632
1923
 
1633
1924
  return …
1634
1925
  };
1635
1926
  ```
1636
1927
 
1928
+ #### waitUntilRenderFlush()
1929
+
1930
+ Type: `Function`
1931
+
1932
+ Returns a promise that settles after pending render output is flushed to stdout.
1933
+
1934
+ ```js
1935
+ import {useEffect} from 'react';
1936
+ import {useApp} from 'ink';
1937
+
1938
+ const Example = () => {
1939
+ const {waitUntilRenderFlush} = useApp();
1940
+
1941
+ useEffect(() => {
1942
+ void (async () => {
1943
+ await waitUntilRenderFlush();
1944
+ runNextCommand();
1945
+ })();
1946
+ }, [waitUntilRenderFlush]);
1947
+
1948
+ return …;
1949
+ };
1950
+ ```
1951
+
1637
1952
  ### useStdin()
1638
1953
 
1639
- `useStdin` is a React hook that exposes the stdin stream.
1954
+ A React hook that returns the stdin stream and stdin-related utilities.
1640
1955
 
1641
1956
  #### stdin
1642
1957
 
@@ -1710,7 +2025,7 @@ const Example = () => {
1710
2025
 
1711
2026
  ### useStdout()
1712
2027
 
1713
- `useStdout` is a React hook that exposes the stdout stream where Ink renders your app.
2028
+ A React hook that returns the stdout stream where Ink renders your app and stdout-related utilities.
1714
2029
 
1715
2030
  #### stdout
1716
2031
 
@@ -1756,9 +2071,73 @@ const Example = () => {
1756
2071
 
1757
2072
  See additional usage example in [examples/use-stdout](examples/use-stdout/use-stdout.tsx).
1758
2073
 
2074
+ ### useBoxMetrics(ref)
2075
+
2076
+ A React hook that returns the current layout metrics for a tracked box element.
2077
+ It updates when layout changes (for example terminal resize, sibling/content changes, or position changes).
2078
+
2079
+ Use `hasMeasured` to detect when the currently tracked element has been measured.
2080
+
2081
+ #### ref
2082
+
2083
+ Type: `React.RefObject<DOMElement>`
2084
+
2085
+ A ref to the `<Box>` element to track.
2086
+
2087
+ ```jsx
2088
+ import {useRef} from 'react';
2089
+ import {Box, Text, useBoxMetrics} from 'ink';
2090
+
2091
+ const Example = () => {
2092
+ const ref = useRef(null);
2093
+ const {width, height, left, top, hasMeasured} = useBoxMetrics(ref);
2094
+
2095
+ return (
2096
+ <Box ref={ref}>
2097
+ <Text>
2098
+ {hasMeasured ? `${width}x${height} at ${left},${top}` : 'Measuring...'}
2099
+ </Text>
2100
+ </Box>
2101
+ );
2102
+ };
2103
+ ```
2104
+
2105
+ #### width
2106
+
2107
+ Type: `number`
2108
+
2109
+ Element width.
2110
+
2111
+ #### height
2112
+
2113
+ Type: `number`
2114
+
2115
+ Element height.
2116
+
2117
+ #### left
2118
+
2119
+ Type: `number`
2120
+
2121
+ Distance from the left edge of the parent.
2122
+
2123
+ #### top
2124
+
2125
+ Type: `number`
2126
+
2127
+ Distance from the top edge of the parent.
2128
+
2129
+ #### hasMeasured
2130
+
2131
+ Type: `boolean`
2132
+
2133
+ Whether the currently tracked element has been measured.
2134
+
2135
+ > [!NOTE]
2136
+ > The hook returns `{width: 0, height: 0, left: 0, top: 0}` until the first layout pass completes. It also returns zeros when the tracked ref is detached.
2137
+
1759
2138
  ### useStderr()
1760
2139
 
1761
- `useStderr` is a React hook that exposes the stderr stream.
2140
+ A React hook that returns the stderr stream and stderr-related utilities.
1762
2141
 
1763
2142
  #### stderr
1764
2143
 
@@ -1805,8 +2184,42 @@ const Example = () => {
1805
2184
  };
1806
2185
  ```
1807
2186
 
2187
+ ### useWindowSize()
2188
+
2189
+ A React hook that returns the current terminal dimensions and re-renders the component whenever the terminal is resized.
2190
+
2191
+ ```js
2192
+ import {Text, useWindowSize} from 'ink';
2193
+
2194
+ const Example = () => {
2195
+ const {columns, rows} = useWindowSize();
2196
+
2197
+ return (
2198
+ <Text>
2199
+ {columns}x{rows}
2200
+ </Text>
2201
+ );
2202
+ };
2203
+ ```
2204
+
2205
+ #### columns
2206
+
2207
+ Type: `number`
2208
+
2209
+ Number of columns (horizontal character cells).
2210
+
2211
+ #### rows
2212
+
2213
+ Type: `number`
2214
+
2215
+ Number of rows (vertical character cells).
2216
+
2217
+ > [!NOTE]
2218
+ > When the terminal is resized narrower, ghost lines may briefly appear depending on the terminal emulator's reflow behavior.
2219
+
1808
2220
  ### useFocus(options?)
1809
2221
 
2222
+ A React hook that returns focus state and focus controls for the current component.
1810
2223
  A component that uses the `useFocus` hook becomes "focusable" to Ink, so when the user presses <kbd>Tab</kbd>, Ink will switch focus to this component.
1811
2224
  If there are multiple components that execute the `useFocus` hook, focus will be given to them in the order in which these components are rendered.
1812
2225
  This hook returns an object with an `isFocused` boolean property, which determines whether this component is focused.
@@ -1851,13 +2264,14 @@ See example in [examples/use-focus](examples/use-focus/use-focus.tsx) and [examp
1851
2264
 
1852
2265
  ### useFocusManager()
1853
2266
 
1854
- This hook exposes methods to enable or disable focus management for all components or manually switch focus to next or previous components.
2267
+ A React hook that returns methods to manage focus across focusable components.
1855
2268
 
1856
2269
  #### enableFocus()
1857
2270
 
1858
2271
  Enable focus management for all components.
1859
2272
 
1860
- **Note:** You don't need to call this method manually unless you've disabled focus management. Focus management is enabled by default.
2273
+ > [!NOTE]
2274
+ > You don't need to call this method manually unless you've disabled focus management. Focus management is enabled by default.
1861
2275
 
1862
2276
  ```js
1863
2277
  import {useFocusManager} from 'ink';
@@ -1898,7 +2312,8 @@ Switch focus to the next focusable component.
1898
2312
  If there's no active component right now, focus will be given to the first focusable component.
1899
2313
  If the active component is the last in the list of focusable components, focus will be switched to the first focusable component.
1900
2314
 
1901
- **Note:** Ink calls this method when user presses <kbd>Tab</kbd>.
2315
+ > [!NOTE]
2316
+ > Ink calls this method when user presses <kbd>Tab</kbd>.
1902
2317
 
1903
2318
  ```js
1904
2319
  import {useFocusManager} from 'ink';
@@ -1920,7 +2335,8 @@ Switch focus to the previous focusable component.
1920
2335
  If there's no active component right now, focus will be given to the first focusable component.
1921
2336
  If the active component is the first in the list of focusable components, focus will be switched to the last focusable component.
1922
2337
 
1923
- **Note:** Ink calls this method when user presses <kbd>Shift</kbd>+<kbd>Tab</kbd>.
2338
+ > [!NOTE]
2339
+ > Ink calls this method when user presses <kbd>Shift</kbd>+<kbd>Tab</kbd>.
1924
2340
 
1925
2341
  ```js
1926
2342
  import {useFocusManager} from 'ink';
@@ -1943,7 +2359,7 @@ const Example = () => {
1943
2359
  Type: `string`
1944
2360
 
1945
2361
  Switch focus to the component with the given [`id`](#id).
1946
- If there's no component with that ID, focus will be given to the next focusable component.
2362
+ If there's no component with that ID, focus is not changed.
1947
2363
 
1948
2364
  ```js
1949
2365
  import {useFocusManager, useInput} from 'ink';
@@ -1962,9 +2378,79 @@ const Example = () => {
1962
2378
  };
1963
2379
  ```
1964
2380
 
2381
+ #### activeId
2382
+
2383
+ Type: `string | undefined`
2384
+
2385
+ The ID of the currently focused component, or `undefined` if no component is focused.
2386
+
2387
+ ```js
2388
+ import {Text, useFocusManager} from 'ink';
2389
+
2390
+ const Example = () => {
2391
+ const {activeId} = useFocusManager();
2392
+
2393
+ return <Text>Focused: {activeId ?? 'none'}</Text>;
2394
+ };
2395
+ ```
2396
+
2397
+ ### useCursor()
2398
+
2399
+ A React hook that returns methods to control the terminal cursor position after each render.
2400
+ This is essential for IME (Input Method Editor) support, where the composing character is displayed at the cursor location.
2401
+
2402
+ ```jsx
2403
+ import {useState} from 'react';
2404
+ import {Box, Text, useCursor} from 'ink';
2405
+ import stringWidth from 'string-width';
2406
+
2407
+ const TextInput = () => {
2408
+ const [text, setText] = useState('');
2409
+ const {setCursorPosition} = useCursor();
2410
+
2411
+ const prompt = '> ';
2412
+ setCursorPosition({x: stringWidth(prompt + text), y: 1});
2413
+
2414
+ return (
2415
+ <Box flexDirection="column">
2416
+ <Text>Type here:</Text>
2417
+ <Text>
2418
+ {prompt}
2419
+ {text}
2420
+ </Text>
2421
+ </Box>
2422
+ );
2423
+ };
2424
+ ```
2425
+
2426
+ #### setCursorPosition(position)
2427
+
2428
+ Set the cursor position relative to the Ink output. Pass `undefined` to hide the cursor.
2429
+
2430
+ ##### position
2431
+
2432
+ Type: `object | undefined`
2433
+
2434
+ Use [`string-width`](https://github.com/sindresorhus/string-width) to calculate `x` for strings containing wide characters (CJK, emoji).
2435
+
2436
+ See a full example at [examples/cursor-ime](examples/cursor-ime/cursor-ime.tsx).
2437
+
2438
+ ###### x
2439
+
2440
+ Type: `number`
2441
+
2442
+ Column position (0-based).
2443
+
2444
+ ###### y
2445
+
2446
+ Type: `number`
2447
+
2448
+ Row position from the top of the Ink output (0 = first line).
2449
+
1965
2450
  ### useIsScreenReaderEnabled()
1966
2451
 
1967
- Returns whether a screen reader is enabled. This is useful when you want to render different output for screen readers.
2452
+ A React hook that returns whether a screen reader is enabled.
2453
+ This is useful when you want to render different output for screen readers.
1968
2454
 
1969
2455
  ```jsx
1970
2456
  import {useIsScreenReaderEnabled, Text} from 'ink';
@@ -1982,6 +2468,65 @@ const Example = () => {
1982
2468
  };
1983
2469
  ```
1984
2470
 
2471
+ ### useAnimation(options?)
2472
+
2473
+ A React hook that drives animations. Returns a frame counter, elapsed time, frame delta, and a reset function. All animations share a single timer internally, so multiple animated components consolidate into one render cycle.
2474
+
2475
+ ```jsx
2476
+ import {Text, useAnimation} from 'ink';
2477
+
2478
+ const Spinner = () => {
2479
+ const {frame} = useAnimation({interval: 80});
2480
+ const characters = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
2481
+
2482
+ return <Text>{characters[frame % characters.length]}</Text>;
2483
+ };
2484
+ ```
2485
+
2486
+ #### options
2487
+
2488
+ Type: `object`
2489
+
2490
+ ##### interval
2491
+
2492
+ Type: `number`\
2493
+ Default: `100`
2494
+
2495
+ Time between ticks in milliseconds.
2496
+
2497
+ ##### isActive
2498
+
2499
+ Type: `boolean`\
2500
+ Default: `true`
2501
+
2502
+ Whether the animation is running. When set to `false`, the animation stops. When toggled back to `true`, all values reset to `0`.
2503
+
2504
+ #### Return value
2505
+
2506
+ ##### frame
2507
+
2508
+ Type: `number`
2509
+
2510
+ Discrete counter that increments by 1 each interval. Useful for indexed sequences like spinner frames.
2511
+
2512
+ ##### time
2513
+
2514
+ Type: `number`
2515
+
2516
+ Total elapsed time in milliseconds since the animation started or was last reset. Useful for continuous math-based animations like sine waves: `Math.sin(time / 1000 * Math.PI * 2)`.
2517
+
2518
+ ##### delta
2519
+
2520
+ Type: `number`
2521
+
2522
+ Time in milliseconds since the previous rendered tick. Accounts for throttled renders. Useful for physics-based or velocity-driven motion: `position += speed * delta`.
2523
+
2524
+ ##### reset
2525
+
2526
+ Type: `() => void`
2527
+
2528
+ Resets `frame`, `time`, and `delta` to `0` and restarts timing from the current moment. Useful for one-shot animations triggered by events.
2529
+
1985
2530
  ## API
1986
2531
 
1987
2532
  #### render(tree, options?)
@@ -1992,7 +2537,7 @@ Mount a component and render the output.
1992
2537
 
1993
2538
  ##### tree
1994
2539
 
1995
- Type: `ReactElement`
2540
+ Type: `ReactNode`
1996
2541
 
1997
2542
  ##### options
1998
2543
 
@@ -2003,7 +2548,7 @@ Type: `object`
2003
2548
  Type: `stream.Writable`\
2004
2549
  Default: `process.stdout`
2005
2550
 
2006
- Output stream where app will be rendered.
2551
+ Output stream where the app will be rendered.
2007
2552
 
2008
2553
  ###### stdin
2009
2554
 
@@ -2036,6 +2581,8 @@ Patch console methods to ensure console output doesn't mix with Ink's output.
2036
2581
  When any of the `console.*` methods are called (like `console.log()`), Ink intercepts their output, clears the main output, renders output from the console method, and then rerenders the main output again.
2037
2582
  That way, both are visible and don't overlap each other.
2038
2583
 
2584
+ Once unmount starts, Ink restores the native console before React cleanup runs. Teardown-time `console.*` output then follows the normal console behavior instead of being rerouted through Ink.
2585
+
2039
2586
  This functionality is powered by [patch-console](https://github.com/vadimdemedes/patch-console), so if you need to disable Ink's interception of output but want to build something custom, you can use that.
2040
2587
 
2041
2588
  ###### onRender
@@ -2043,7 +2590,16 @@ This functionality is powered by [patch-console](https://github.com/vadimdemedes
2043
2590
  Type: `({renderTime: number}) => void`\
2044
2591
  Default: `undefined`
2045
2592
 
2046
- Runs the given callback after each render and re-render with a metrics object.
2593
+ Runs the given callback after each render and re-render with render metrics.
2594
+ This callback runs after Ink commits a frame, but it does not wait for `stdout`/`stderr` stream callbacks.
2595
+ To run code after output is flushed, use [`waitUntilRenderFlush()`](#waituntilrenderflush).
2596
+
2597
+ ###### isScreenReaderEnabled
2598
+
2599
+ Type: `boolean`\
2600
+ Default: `process.env['INK_SCREEN_READER'] === 'true'`
2601
+
2602
+ Enable screen reader support. See [Screen Reader Support](#screen-reader-support).
2047
2603
 
2048
2604
  ###### debug
2049
2605
 
@@ -2070,17 +2626,187 @@ Default: `false`
2070
2626
  Enable incremental rendering mode which only updates changed lines instead of redrawing the entire output.
2071
2627
  This can reduce flickering and improve performance for frequently updating UIs.
2072
2628
 
2629
+ ###### concurrent
2630
+
2631
+ Type: `boolean`\
2632
+ Default: `false`
2633
+
2634
+ Enable React Concurrent Rendering mode.
2635
+
2636
+ When enabled:
2637
+
2638
+ - Suspense boundaries work correctly with async data fetching
2639
+ - `useTransition` and `useDeferredValue` hooks are fully functional
2640
+ - Updates can be interrupted for higher priority work
2641
+
2642
+ ```jsx
2643
+ render(<MyApp />, {concurrent: true});
2644
+ ```
2645
+
2646
+ > [!NOTE]
2647
+ > Concurrent mode changes the timing of renders. Some tests may need to use `act()` to properly await updates. Reusing the same stdout across multiple `render()` calls without unmounting is unsupported. Call `unmount()` first if you need to change the rendering mode or create a fresh instance.
2648
+
2649
+ ###### interactive
2650
+
2651
+ Type: `boolean`\
2652
+ Default: `true` (`false` if in CI (detected via [`is-in-ci`](https://github.com/sindresorhus/is-in-ci)) or `stdout.isTTY` is falsy)
2653
+
2654
+ Override automatic interactive mode detection.
2655
+
2656
+ By default, Ink detects whether the environment is interactive based on CI detection and `stdout.isTTY`. When non-interactive, Ink skips terminal-specific features like ANSI erase sequences, cursor manipulation, synchronized output, resize handling, and kitty keyboard auto-detection. Only the final frame of non-static output is written at unmount.
2657
+
2658
+ Most users should not need to set this option. Use it when you have your own "interactive" detection logic that differs from the built-in behavior.
2659
+
2660
+ > [!NOTE]
2661
+ > Reusing the same stdout across multiple `render()` calls without unmounting is unsupported. Call `unmount()` first if you need to change this option or create a fresh instance.
2662
+
2663
+ ```jsx
2664
+ // Use your own detection logic
2665
+ const isInteractive = myCustomDetection();
2666
+ render(<MyApp />, {interactive: isInteractive});
2667
+ ```
2668
+
2669
+ ###### alternateScreen
2670
+
2671
+ Type: `boolean`\
2672
+ Default: `false`
2673
+
2674
+ Render the app in the terminal's alternate screen buffer. When enabled, the app renders on a separate screen, and the original terminal content is restored when the app exits. This is the same mechanism used by programs like vim, htop, and less.
2675
+
2676
+ Note: The terminal's scrollback buffer is not available while in the alternate screen. This is standard terminal behavior; programs like vim use the alternate screen specifically to avoid polluting the user's scrollback history.
2677
+
2678
+ Ink intentionally treats alternate-screen teardown output as disposable. It does not preserve or replay teardown-time frames, hook writes, or `console.*` output after restoring the primary screen.
2679
+
2680
+ Only works in interactive mode. Ignored when `interactive` is `false` or in a non-interactive environment (CI, piped stdout).
2681
+
2682
+ > [!NOTE]
2683
+ > Reusing the same stdout across multiple `render()` calls without unmounting is unsupported. Call `unmount()` first if you need to change this option or create a fresh instance.
2684
+
2685
+ ```jsx
2686
+ render(<MyApp />, {alternateScreen: true});
2687
+ ```
2688
+
2689
+ ###### kittyKeyboard
2690
+
2691
+ Type: `object`\
2692
+ Default: `undefined`
2693
+
2694
+ Enable the [kitty keyboard protocol](https://sw.kovidgoyal.net/kitty/keyboard-protocol/) for enhanced keyboard input handling. When enabled, terminals that support the protocol will report additional key information including `super`, `hyper`, `capsLock`, `numLock` modifiers and `eventType` (press/repeat/release).
2695
+
2696
+ ```jsx
2697
+ import {render} from 'ink';
2698
+
2699
+ render(<MyApp />, {kittyKeyboard: {mode: 'auto'}});
2700
+ ```
2701
+
2702
+ ```jsx
2703
+ import {render} from 'ink';
2704
+
2705
+ render(<MyApp />, {
2706
+ kittyKeyboard: {
2707
+ mode: 'enabled',
2708
+ flags: ['disambiguateEscapeCodes', 'reportEventTypes'],
2709
+ },
2710
+ });
2711
+ ```
2712
+
2713
+ **kittyKeyboard.mode**
2714
+
2715
+ Type: `'auto' | 'enabled' | 'disabled'`\
2716
+ Default: `'auto'`
2717
+
2718
+ - `'auto'`: Detect terminal support using a heuristic precheck (known terminals like kitty, WezTerm, Ghostty) followed by a protocol query confirmation (`CSI ? u`). The protocol is only enabled if the terminal responds to the query within a short timeout.
2719
+ - `'enabled'`: Force enable the protocol. Both stdin and stdout must be TTYs.
2720
+ - `'disabled'`: Never enable the protocol.
2721
+
2722
+ **kittyKeyboard.flags**
2723
+
2724
+ Type: `string[]`\
2725
+ Default: `['disambiguateEscapeCodes']`
2726
+
2727
+ Protocol flags to request from the terminal. Pass an array of flag name strings.
2728
+
2729
+ Available flags:
2730
+
2731
+ - `'disambiguateEscapeCodes'` - Disambiguate escape codes
2732
+ - `'reportEventTypes'` - Report key press, repeat, and release events
2733
+ - `'reportAlternateKeys'` - Report alternate key encodings
2734
+ - `'reportAllKeysAsEscapeCodes'` - Report all keys as escape codes
2735
+ - `'reportAssociatedText'` - Report associated text with key events
2736
+
2737
+ **Behavior notes**
2738
+
2739
+ When the kitty keyboard protocol is enabled, input handling changes in several ways:
2740
+
2741
+ - **Non-printable keys produce empty input.** Keys like function keys (F1-F35), modifier-only keys (Shift, Control, Super), media keys, Caps Lock, Print Screen, and similar keys will not produce any text in the `input` parameter of `useInput`. They can still be detected via the `key` object properties.
2742
+ - **Ctrl+letter shortcuts work as expected.** When the terminal sends `Ctrl+letter` as codepoint 1-26 (the kitty CSI-u alternate form), `input` is set to the letter name (e.g. `'c'` for `Ctrl+C`) and `key.ctrl` is `true`. This ensures `exitOnCtrlC` and custom `Ctrl+letter` handlers continue to work regardless of which codepoint form the terminal uses.
2743
+ - **Key disambiguation.** The protocol allows the terminal to distinguish between keys that normally produce the same escape sequence. For example:
2744
+ - `Ctrl+I` vs `Tab` - without the protocol, both produce the same byte (`\x09`). With the protocol, they are reported as distinct keys.
2745
+ - `Shift+Enter` vs `Enter` - the shift modifier is correctly reported.
2746
+ - `Escape` key vs `Ctrl+[` - these are disambiguated.
2747
+ - **Event types.** With the `reportEventTypes` flag, key press, repeat, and release events are distinguished via `key.eventType`.
2748
+
2749
+ #### renderToString(tree, options?)
2750
+
2751
+ Returns: `string`
2752
+
2753
+ Render a React element to a string synchronously. Unlike `render()`, this function does not write to stdout, does not set up any terminal event listeners, and returns the rendered output as a string.
2754
+
2755
+ Useful for generating documentation, writing output to files, testing, or any scenario where you need the rendered output as a string without starting a persistent terminal application.
2756
+
2757
+ ```jsx
2758
+ import {renderToString, Text, Box} from 'ink';
2759
+
2760
+ const output = renderToString(
2761
+ <Box padding={1}>
2762
+ <Text color="green">Hello World</Text>
2763
+ </Box>,
2764
+ );
2765
+
2766
+ console.log(output);
2767
+ ```
2768
+
2769
+ **Notes:**
2770
+
2771
+ - Terminal-specific hooks (`useInput`, `useStdin`, `useStdout`, `useStderr`, `useWindowSize`, `useApp`, `useFocus`, `useFocusManager`) return default no-op values since there is no terminal session. They will not throw, but they will not function as in a live terminal.
2772
+ - `useEffect` callbacks will execute during rendering (due to synchronous rendering mode), but state updates they trigger will not affect the returned output, which reflects the initial render.
2773
+ - `useLayoutEffect` callbacks fire synchronously during commit, so state updates they trigger **will** be reflected in the output.
2774
+ - The `<Static>` component is supported — its output is prepended to the dynamic output.
2775
+ - If a component throws during rendering, the error is propagated to the caller after cleanup.
2776
+
2777
+ ##### tree
2778
+
2779
+ Type: `ReactNode`
2780
+
2781
+ ##### options
2782
+
2783
+ Type: `object`
2784
+
2785
+ ###### columns
2786
+
2787
+ Type: `number`\
2788
+ Default: `80`
2789
+
2790
+ Width of the virtual terminal in columns. Controls where text wrapping occurs.
2791
+
2792
+ ```jsx
2793
+ const output = renderToString(<Text>{'A'.repeat(100)}</Text>, {
2794
+ columns: 40,
2795
+ });
2796
+ // Text wraps at 40 columns
2797
+ ```
2798
+
2073
2799
  #### Instance
2074
2800
 
2075
2801
  This is the object that `render()` returns.
2076
2802
 
2077
2803
  ##### rerender(tree)
2078
2804
 
2079
- Replace the previous root node with a new one or update props of the current root node.
2805
+ Replace the previous root node with a new one or update the props of the current root node.
2080
2806
 
2081
2807
  ###### tree
2082
2808
 
2083
- Type: `ReactElement`
2809
+ Type: `ReactNode`
2084
2810
 
2085
2811
  ```jsx
2086
2812
  // Update props of the root node
@@ -2103,7 +2829,10 @@ unmount();
2103
2829
 
2104
2830
  ##### waitUntilExit()
2105
2831
 
2106
- Returns a promise that resolves when the app is unmounted.
2832
+ Returns a promise that settles when the app is unmounted.
2833
+
2834
+ It resolves with the value passed to `exit(value)` and rejects with the error passed to `exit(error)`.
2835
+ When `unmount()` is called manually, it settles after unmount-related stdout writes complete.
2107
2836
 
2108
2837
  ```jsx
2109
2838
  const {unmount, waitUntilExit} = render(<MyApp />);
@@ -2113,6 +2842,27 @@ setTimeout(unmount, 1000);
2113
2842
  await waitUntilExit(); // resolves after `unmount()` is called
2114
2843
  ```
2115
2844
 
2845
+ ##### waitUntilRenderFlush()
2846
+
2847
+ Returns a promise that settles after pending render output is flushed to stdout.
2848
+
2849
+ Useful when you need to run code only after a frame is written:
2850
+
2851
+ ```jsx
2852
+ const {rerender, waitUntilRenderFlush} = render(<MyApp step="loading" />);
2853
+
2854
+ rerender(<MyApp step="ready" />);
2855
+ await waitUntilRenderFlush(); // output for "ready" is flushed
2856
+
2857
+ runNextCommand();
2858
+ ```
2859
+
2860
+ ##### cleanup()
2861
+
2862
+ Unmount the current app and delete the internal Ink instance associated with the current `stdout`.
2863
+ This is mostly useful for advanced cases (for example, tests) where you need `render()` to create a fresh instance for the same stream.
2864
+ Unlike deleting the internal instance directly, this also tears down terminal state such as the alternate screen.
2865
+
2116
2866
  ##### clear()
2117
2867
 
2118
2868
  Clear output.
@@ -2128,7 +2878,8 @@ Measure the dimensions of a particular `<Box>` element.
2128
2878
  Returns an object with `width` and `height` properties.
2129
2879
  This function is useful when your component needs to know the amount of available space it has. You can use it when you need to change the layout based on the length of its content.
2130
2880
 
2131
- **Note:** `measureElement()` returns correct results only after the initial render, when the layout has been calculated. Until then, `width` and `height` equal zero. It's recommended to call `measureElement()` in a `useEffect` hook, which fires after the component has rendered.
2881
+ > [!NOTE]
2882
+ > `measureElement()` returns `{width: 0, height: 0}` when called during render (before layout is calculated). Call it from post-render code, such as `useEffect`, `useLayoutEffect`, input handlers, or timer callbacks. When content changes, pass the relevant dependency to your effect so it re-measures after each update.
2132
2883
 
2133
2884
  ##### ref
2134
2885
 
@@ -2163,7 +2914,7 @@ render(<Example />);
2163
2914
  ## Testing
2164
2915
 
2165
2916
  Ink components are simple to test with [ink-testing-library](https://github.com/vadimdemedes/ink-testing-library).
2166
- Here's a simple example that checks how component is rendered:
2917
+ Here's a simple example that checks how the component is rendered:
2167
2918
 
2168
2919
  ```jsx
2169
2920
  import React from 'react';
@@ -2197,7 +2948,8 @@ npx react-devtools
2197
2948
  After it starts, you should see the component tree of your CLI.
2198
2949
  You can even inspect and change the props of components, and see the results immediately in the CLI, without restarting it.
2199
2950
 
2200
- **Note**: You must manually quit your CLI via <kbd>Ctrl</kbd>+<kbd>C</kbd> after you're done testing.
2951
+ > [!NOTE]
2952
+ > You must manually quit your CLI via <kbd>Ctrl</kbd>+<kbd>C</kbd> after you're done testing.
2201
2953
 
2202
2954
  ## Screen Reader Support
2203
2955
 
@@ -2253,38 +3005,49 @@ Default: `false`
2253
3005
 
2254
3006
  Hide the element from screen readers.
2255
3007
 
2256
- ##### aria-role
3008
+ ### `aria-role`
2257
3009
 
2258
3010
  Type: `string`
2259
3011
 
2260
3012
  The role of the element.
2261
3013
 
2262
3014
  Supported values:
3015
+
2263
3016
  - `button`
2264
3017
  - `checkbox`
2265
- - `radio`
2266
- - `radiogroup`
3018
+ - `combobox`
2267
3019
  - `list`
3020
+ - `listbox`
2268
3021
  - `listitem`
2269
3022
  - `menu`
2270
3023
  - `menuitem`
3024
+ - `option`
2271
3025
  - `progressbar`
3026
+ - `radio`
3027
+ - `radiogroup`
2272
3028
  - `tab`
2273
3029
  - `tablist`
3030
+ - `table`
3031
+ - `textbox`
2274
3032
  - `timer`
2275
3033
  - `toolbar`
2276
- - `table`
2277
3034
 
2278
- ##### aria-state
3035
+ ### `aria-state`
2279
3036
 
2280
3037
  Type: `object`
2281
3038
 
2282
3039
  The state of the element.
2283
3040
 
2284
3041
  Supported values:
3042
+
3043
+ - `busy` (boolean)
2285
3044
  - `checked` (boolean)
2286
3045
  - `disabled` (boolean)
2287
3046
  - `expanded` (boolean)
3047
+ - `multiline` (boolean)
3048
+ - `multiselectable` (boolean)
3049
+ - `readonly` (boolean)
3050
+ - `required` (boolean)
2288
3051
  - `selected` (boolean)
2289
3052
 
2290
3053
  ## Creating Components
@@ -2323,11 +3086,20 @@ For a practical example of building an accessible component, see the [ARIA examp
2323
3086
  - [ink-spawn](https://github.com/kraenhansen/ink-spawn) - Spawn child processes.
2324
3087
  - [ink-titled-box](https://github.com/mishieck/ink-titled-box) - Box with a title.
2325
3088
  - [ink-chart](https://github.com/pppp606/ink-chart) - Sparkline and bar chart.
3089
+ - [ink-scroll-view](https://github.com/ByteLandTechnology/ink-scroll-view) - Scroll container.
3090
+ - [ink-scroll-list](https://github.com/ByteLandTechnology/ink-scroll-list) - Scrollable list.
3091
+ - [ink-stepper](https://github.com/archcorsair/ink-stepper) - Step-by-step wizard.
3092
+ - [ink-virtual-list](https://github.com/archcorsair/ink-virtual-list) - Virtualized list that renders only visible items for performance.
3093
+ - [ink-color-picker](https://github.com/sina-byn/ink-color-picker) - Color picker.
2326
3094
 
2327
3095
  ## Useful Hooks
2328
3096
 
2329
3097
  - [ink-use-stdout-dimensions](https://github.com/cameronhunter/ink-monorepo/tree/master/packages/ink-use-stdout-dimensions) - Subscribe to stdout dimensions.
2330
3098
 
3099
+ ## Recipes
3100
+
3101
+ - [Routing with React Router](recipes/routing.md) - Navigate between routes using `MemoryRouter`.
3102
+
2331
3103
  ## Examples
2332
3104
 
2333
3105
  The [`examples`](/examples) directory contains a set of real examples. You can run them with:
@@ -2349,6 +3121,20 @@ npm run example examples/[example name]
2349
3121
  - [Write to stderr](examples/use-stderr/use-stderr.tsx) - Write to stderr, bypassing main Ink output.
2350
3122
  - [Static](examples/static/static.tsx) - Use the `<Static>` component to render permanent output.
2351
3123
  - [Child process](examples/subprocess-output) - Renders output from a child process.
3124
+ - [Router](examples/router/router.tsx) - Navigate between routes using React Router's `MemoryRouter`.
3125
+
3126
+ ## Continuous Integration
3127
+
3128
+ When running on CI (detected via the `CI` environment variable), Ink adapts its rendering:
3129
+
3130
+ - Only the last frame is rendered on exit, instead of continuously updating the terminal. This is because most CI environments don't support the ANSI escape sequences used to overwrite previous output.
3131
+ - Terminal resize events are not listened to.
3132
+
3133
+ If your CI environment supports full terminal rendering and you want to opt out of this behavior, set `CI=false`:
3134
+
3135
+ ```sh
3136
+ CI=false node my-cli.js
3137
+ ```
2352
3138
 
2353
3139
  ## Maintainers
2354
3140