or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

components.mdhooks.mdindex.mdrendering.md
tile.json

hooks.mddocs/

0

# User Input Handling

1

2

Comprehensive input handling system for creating interactive CLI applications with keyboard navigation, focus management, and stream access.

3

4

## Types

5

6

These context Props types are available for import and use with the corresponding hooks:

7

8

```typescript { .api }

9

/**

10

* Props type for AppContext - used with useApp hook

11

*/

12

interface AppProps {

13

/**

14

* Exit (unmount) the whole Ink app

15

* @param error - Optional error to exit with

16

*/

17

readonly exit: (error?: Error) => void;

18

}

19

20

/**

21

* Props type for StdinContext - used with useStdin hook

22

*/

23

interface StdinProps {

24

/**

25

* Stdin stream passed to render() or process.stdin by default

26

*/

27

readonly stdin: NodeJS.ReadStream;

28

29

/**

30

* Ink's version of setRawMode that handles Ctrl+C properly

31

* Use instead of process.stdin.setRawMode

32

*/

33

readonly setRawMode: (value: boolean) => void;

34

35

/**

36

* Whether the current stdin supports setRawMode

37

*/

38

readonly isRawModeSupported: boolean;

39

40

/**

41

* Internal flag for exit on Ctrl+C behavior

42

*/

43

readonly internal_exitOnCtrlC: boolean;

44

45

/**

46

* Internal event emitter for input events

47

*/

48

readonly internal_eventEmitter: EventEmitter;

49

}

50

51

/**

52

* Props type for StdoutContext - used with useStdout hook

53

*/

54

interface StdoutProps {

55

/**

56

* Stdout stream passed to render() or process.stdout by default

57

*/

58

readonly stdout: NodeJS.WriteStream;

59

60

/**

61

* Write string to stdout while preserving Ink's output

62

* Useful for displaying external information without conflicts

63

*/

64

readonly write: (data: string) => void;

65

}

66

67

/**

68

* Props type for StderrContext - used with useStderr hook

69

*/

70

interface StderrProps {

71

/**

72

* Stderr stream passed to render() or process.stderr by default

73

*/

74

readonly stderr: NodeJS.WriteStream;

75

76

/**

77

* Write string to stderr while preserving Ink's output

78

* Useful for displaying external information without conflicts

79

*/

80

readonly write: (data: string) => void;

81

}

82

```

83

84

**Usage Examples:**

85

86

```typescript

87

import React from "react";

88

import {

89

render,

90

Text,

91

useApp,

92

useStdin,

93

useStdout,

94

useStderr,

95

type AppProps,

96

type StdinProps,

97

type StdoutProps,

98

type StderrProps

99

} from "ink";

100

101

// Using Props types for custom contexts or components

102

function CustomAppProvider({ children }: { children: React.ReactNode }) {

103

const appContext: AppProps = {

104

exit: (error?: Error) => {

105

console.log("Custom exit handler");

106

process.exit(error ? 1 : 0);

107

}

108

};

109

110

return (

111

<AppContext.Provider value={appContext}>

112

{children}

113

</AppContext.Provider>

114

);

115

}

116

```

117

118

## Capabilities

119

120

### Input Handling

121

122

Hook for handling user keyboard input with detailed key information parsing.

123

124

```typescript { .api }

125

/**

126

* Handle user keyboard input with parsed key information

127

* @param inputHandler - Function called for each input event

128

* @param options - Input handling configuration

129

*/

130

function useInput(

131

inputHandler: (input: string, key: Key) => void,

132

options?: InputOptions

133

): void;

134

135

interface InputOptions {

136

/**

137

* Enable or disable input capturing

138

* @default true

139

*/

140

isActive?: boolean;

141

}

142

143

interface Key {

144

upArrow: boolean; // Up arrow key pressed

145

downArrow: boolean; // Down arrow key pressed

146

leftArrow: boolean; // Left arrow key pressed

147

rightArrow: boolean; // Right arrow key pressed

148

pageDown: boolean; // Page Down key pressed

149

pageUp: boolean; // Page Up key pressed

150

return: boolean; // Return (Enter) key pressed

151

escape: boolean; // Escape key pressed

152

ctrl: boolean; // Ctrl key pressed

153

shift: boolean; // Shift key pressed

154

tab: boolean; // Tab key pressed

155

backspace: boolean; // Backspace key pressed

156

delete: boolean; // Delete key pressed

157

meta: boolean; // Meta key pressed

158

}

159

```

160

161

**Usage Examples:**

162

163

```typescript

164

import React, { useState } from "react";

165

import { render, Box, Text, useInput } from "ink";

166

167

// Basic input handling

168

function InputDemo() {

169

const [input, setInput] = useState("");

170

const [lastKey, setLastKey] = useState("");

171

172

useInput((input, key) => {

173

if (key.return) {

174

setInput("");

175

return;

176

}

177

178

if (key.backspace) {

179

setInput(prev => prev.slice(0, -1));

180

return;

181

}

182

183

if (input) {

184

setInput(prev => prev + input);

185

}

186

187

// Track special keys

188

if (key.upArrow) setLastKey("Up Arrow");

189

if (key.downArrow) setLastKey("Down Arrow");

190

if (key.ctrl) setLastKey("Ctrl pressed");

191

if (key.escape) setLastKey("Escape");

192

});

193

194

return (

195

<Box flexDirection="column">

196

<Text>Input: {input}</Text>

197

<Text>Last key: {lastKey}</Text>

198

<Text dimColor>Type text, use arrows, press Enter to clear</Text>

199

</Box>

200

);

201

}

202

203

// Navigation with arrow keys

204

function Navigation() {

205

const [selectedIndex, setSelectedIndex] = useState(0);

206

const items = ["Option 1", "Option 2", "Option 3"];

207

208

useInput((input, key) => {

209

if (key.upArrow) {

210

setSelectedIndex(prev => Math.max(0, prev - 1));

211

}

212

213

if (key.downArrow) {

214

setSelectedIndex(prev => Math.min(items.length - 1, prev + 1));

215

}

216

});

217

218

return (

219

<Box flexDirection="column">

220

{items.map((item, index) => (

221

<Text key={index} color={index === selectedIndex ? "blue" : "white"}>

222

{index === selectedIndex ? "► " : " "}{item}

223

</Text>

224

))}

225

</Box>

226

);

227

}

228

229

// Conditional input handling

230

function ConditionalInput() {

231

const [isActive, setIsActive] = useState(true);

232

233

useInput((input, key) => {

234

if (input === "q") {

235

setIsActive(false);

236

}

237

}, { isActive });

238

239

return (

240

<Text color={isActive ? "green" : "red"}>

241

Input {isActive ? "active" : "inactive"}. Press 'q' to toggle.

242

</Text>

243

);

244

}

245

```

246

247

### Focus Management

248

249

Hook for making components focusable and managing focus navigation with Tab key.

250

251

```typescript { .api }

252

/**

253

* Make component focusable for Tab navigation

254

* @param options - Focus configuration

255

* @returns Focus state and control functions

256

*/

257

function useFocus(options?: FocusOptions): FocusResult;

258

259

interface FocusOptions {

260

/**

261

* Enable or disable focus for this component

262

* @default true

263

*/

264

isActive?: boolean;

265

266

/**

267

* Auto focus this component if no active component exists

268

* @default false

269

*/

270

autoFocus?: boolean;

271

272

/**

273

* Assign ID for programmatic focusing

274

*/

275

id?: string;

276

}

277

278

interface FocusResult {

279

/**

280

* Whether this component is currently focused

281

*/

282

isFocused: boolean;

283

284

/**

285

* Focus a specific component by ID

286

*/

287

focus: (id: string) => void;

288

}

289

```

290

291

**Usage Examples:**

292

293

```typescript

294

import React, { useState } from "react";

295

import { render, Box, Text, useInput, useFocus } from "ink";

296

297

// Focusable button component

298

function FocusableButton({ children, onSelect, id }: {

299

children: string;

300

onSelect: () => void;

301

id?: string;

302

}) {

303

const { isFocused } = useFocus({ id });

304

305

useInput((input, key) => {

306

if (key.return && isFocused) {

307

onSelect();

308

}

309

});

310

311

return (

312

<Box>

313

<Text color={isFocused ? "blue" : "white"} inverse={isFocused}>

314

{isFocused ? "► " : " "}{children}

315

</Text>

316

</Box>

317

);

318

}

319

320

// Menu with focusable items

321

function FocusableMenu() {

322

const [selected, setSelected] = useState("");

323

324

return (

325

<Box flexDirection="column">

326

<Text>Use Tab to navigate, Enter to select:</Text>

327

328

<FocusableButton

329

id="option1"

330

onSelect={() => setSelected("Option 1")}

331

>

332

Option 1

333

</FocusableButton>

334

335

<FocusableButton

336

id="option2"

337

onSelect={() => setSelected("Option 2")}

338

>

339

Option 2

340

</FocusableButton>

341

342

<FocusableButton

343

id="option3"

344

onSelect={() => setSelected("Option 3")}

345

>

346

Option 3

347

</FocusableButton>

348

349

{selected && (

350

<Text color="green">Selected: {selected}</Text>

351

)}

352

</Box>

353

);

354

}

355

356

// Auto-focus example

357

function AutoFocusInput() {

358

const { isFocused } = useFocus({ autoFocus: true });

359

const [value, setValue] = useState("");

360

361

useInput((input, key) => {

362

if (!isFocused) return;

363

364

if (key.backspace) {

365

setValue(prev => prev.slice(0, -1));

366

} else if (input) {

367

setValue(prev => prev + input);

368

}

369

});

370

371

return (

372

<Box>

373

<Text>Input: </Text>

374

<Text color="blue" inverse={isFocused}>{value || " "}</Text>

375

</Box>

376

);

377

}

378

```

379

380

### Focus Manager

381

382

Hook for programmatically controlling focus across all components.

383

384

```typescript { .api }

385

/**

386

* Control focus management for all components

387

* @returns Focus management functions

388

*/

389

function useFocusManager(): FocusManagerResult;

390

391

interface FocusManagerResult {

392

/**

393

* Enable focus management for all components

394

*/

395

enableFocus: () => void;

396

397

/**

398

* Disable focus management, active component loses focus

399

*/

400

disableFocus: () => void;

401

402

/**

403

* Switch focus to next focusable component

404

*/

405

focusNext: () => void;

406

407

/**

408

* Switch focus to previous focusable component

409

*/

410

focusPrevious: () => void;

411

412

/**

413

* Focus specific component by ID

414

*/

415

focus: (id: string) => void;

416

}

417

```

418

419

**Usage Examples:**

420

421

```typescript

422

import React from "react";

423

import { render, Box, Text, useInput, useFocus, useFocusManager } from "ink";

424

425

function FocusController() {

426

const { enableFocus, disableFocus, focusNext, focusPrevious, focus } = useFocusManager();

427

428

useInput((input, key) => {

429

if (input === "e") enableFocus();

430

if (input === "d") disableFocus();

431

if (input === "n") focusNext();

432

if (input === "p") focusPrevious();

433

if (input === "1") focus("first");

434

if (input === "2") focus("second");

435

});

436

437

return (

438

<Box flexDirection="column">

439

<Text>Focus Control:</Text>

440

<Text dimColor>e: enable, d: disable, n: next, p: previous</Text>

441

<Text dimColor>1: focus first, 2: focus second</Text>

442

443

<FocusableItem id="first">First Item</FocusableItem>

444

<FocusableItem id="second">Second Item</FocusableItem>

445

</Box>

446

);

447

}

448

449

function FocusableItem({ id, children }: { id: string; children: string }) {

450

const { isFocused } = useFocus({ id });

451

return <Text color={isFocused ? "blue" : "white"}>{children}</Text>;

452

}

453

```

454

455

### Application Control

456

457

Hook for controlling the application lifecycle.

458

459

```typescript { .api }

460

/**

461

* Access application control functions

462

* @returns Application control interface

463

*/

464

function useApp(): AppResult;

465

466

interface AppResult {

467

/**

468

* Exit (unmount) the whole Ink app

469

* @param error - Optional error to exit with

470

*/

471

exit: (error?: Error) => void;

472

}

473

```

474

475

**Usage Examples:**

476

477

```typescript

478

import React, { useEffect } from "react";

479

import { render, Text, useInput, useApp } from "ink";

480

481

// Exit on specific key

482

function ExitableApp() {

483

const { exit } = useApp();

484

485

useInput((input, key) => {

486

if (input === "q" || key.escape) {

487

exit();

488

}

489

});

490

491

return <Text>Press 'q' or Escape to exit</Text>;

492

}

493

494

// Exit with error

495

function ErrorApp() {

496

const { exit } = useApp();

497

498

useEffect(() => {

499

const timer = setTimeout(() => {

500

exit(new Error("Something went wrong"));

501

}, 3000);

502

503

return () => clearTimeout(timer);

504

}, [exit]);

505

506

return <Text color="red">App will exit with error in 3 seconds...</Text>;

507

}

508

```

509

510

### Stream Access

511

512

Hooks for accessing stdin, stdout, and stderr streams directly.

513

514

```typescript { .api }

515

/**

516

* Access stdin stream and related utilities

517

*/

518

function useStdin(): StdinResult;

519

520

/**

521

* Access stdout stream and write function

522

*/

523

function useStdout(): StdoutResult;

524

525

/**

526

* Access stderr stream and write function

527

*/

528

function useStderr(): StderrResult;

529

530

interface StdinResult {

531

/**

532

* Input stream

533

*/

534

stdin: NodeJS.ReadStream;

535

536

/**

537

* Set raw mode for stdin (use Ink's version, not process.stdin.setRawMode)

538

*/

539

setRawMode: (value: boolean) => void;

540

541

/**

542

* Whether stdin supports raw mode

543

*/

544

isRawModeSupported: boolean;

545

546

/**

547

* Internal flag for exit on Ctrl+C behavior

548

*/

549

internal_exitOnCtrlC: boolean;

550

551

/**

552

* Internal event emitter for input events

553

*/

554

internal_eventEmitter: EventEmitter;

555

}

556

557

interface StdoutResult {

558

/**

559

* Output stream where app renders

560

*/

561

stdout: NodeJS.WriteStream;

562

563

/**

564

* Write string to stdout while preserving Ink's output

565

* Useful for displaying external information without conflicts

566

*/

567

write: (data: string) => void;

568

}

569

570

interface StderrResult {

571

/**

572

* Error stream for error output

573

*/

574

stderr: NodeJS.WriteStream;

575

576

/**

577

* Write string to stderr while preserving Ink's output

578

* Useful for displaying external information without conflicts

579

*/

580

write: (data: string) => void;

581

}

582

```

583

584

**Usage Examples:**

585

586

```typescript

587

import React, { useEffect, useState } from "react";

588

import { render, Text, useStdin, useStdout } from "ink";

589

590

// Direct stream access

591

function StreamAccess() {

592

const { stdin, setRawMode, isRawModeSupported } = useStdin();

593

const stdout = useStdout();

594

const [rawMode, setRawModeState] = useState(false);

595

596

useEffect(() => {

597

if (isRawModeSupported) {

598

setRawMode(true);

599

setRawModeState(true);

600

601

return () => {

602

setRawMode(false);

603

setRawModeState(false);

604

};

605

}

606

}, [setRawMode, isRawModeSupported]);

607

608

return (

609

<Text>

610

Raw mode: {rawMode ? "enabled" : "disabled"}

611

(supported: {isRawModeSupported ? "yes" : "no"})

612

</Text>

613

);

614

}

615

```