Export wrapTextWithAnsi utility and add handleInput docs

- Export wrapTextWithAnsi from tui package
- Add 'Handling Input' section to README with key detection example
- Document wrapTextWithAnsi in utilities section
This commit is contained in:
Mario Zechner 2025-12-30 23:28:38 +01:00
parent 1d3203fd3d
commit 71cbae6371
3 changed files with 46 additions and 2 deletions

View file

@ -7,6 +7,7 @@
- `isShiftCtrlO()` key detection function for Shift+Ctrl+O (Kitty protocol)
- `isShiftCtrlD()` key detection function for Shift+Ctrl+D (Kitty protocol)
- `TUI.onDebug` callback for global debug key handling (Shift+Ctrl+D)
- `wrapTextWithAnsi()` utility now exported (wraps text to width, preserving ANSI codes)
### Changed

View file

@ -445,7 +445,7 @@ interface Terminal {
## Utilities
```typescript
import { visibleWidth, truncateToWidth } from "@mariozechner/pi-tui";
import { visibleWidth, truncateToWidth, wrapTextWithAnsi } from "@mariozechner/pi-tui";
// Get visible width of string (ignoring ANSI codes)
const width = visibleWidth("\x1b[31mHello\x1b[0m"); // 5
@ -455,12 +455,55 @@ const truncated = truncateToWidth("Hello World", 8); // "Hello..."
// Truncate without ellipsis
const truncatedNoEllipsis = truncateToWidth("Hello World", 8, ""); // "Hello Wo"
// Wrap text to width (preserving ANSI codes across line breaks)
const lines = wrapTextWithAnsi("This is a long line that needs wrapping", 20);
// ["This is a long line", "that needs wrapping"]
```
## Creating Custom Components
When creating custom components, **each line returned by `render()` must not exceed the `width` parameter**. The TUI will error if any line is wider than the terminal.
### Handling Input
Use the key detection utilities to handle keyboard input:
```typescript
import {
isEnter, isEscape, isArrowUp, isArrowDown,
isCtrlC, isTab, isBackspace
} from "@mariozechner/pi-tui";
import type { Component } from "@mariozechner/pi-tui";
class MyInteractiveComponent implements Component {
private selectedIndex = 0;
private items = ["Option 1", "Option 2", "Option 3"];
public onSelect?: (index: number) => void;
public onCancel?: () => void;
handleInput(data: string): void {
if (isArrowUp(data)) {
this.selectedIndex = Math.max(0, this.selectedIndex - 1);
} else if (isArrowDown(data)) {
this.selectedIndex = Math.min(this.items.length - 1, this.selectedIndex + 1);
} else if (isEnter(data)) {
this.onSelect?.(this.selectedIndex);
} else if (isEscape(data) || isCtrlC(data)) {
this.onCancel?.();
}
}
render(width: number): string[] {
return this.items.map((item, i) => {
const prefix = i === this.selectedIndex ? "> " : " ";
return truncateToWidth(prefix + item, width);
});
}
}
```
### Handling Line Width
Use the provided utilities to ensure lines fit:

View file

@ -85,4 +85,4 @@ export {
} from "./terminal-image.js";
export { type Component, Container, TUI } from "./tui.js";
// Utilities
export { truncateToWidth, visibleWidth } from "./utils.js";
export { truncateToWidth, visibleWidth, wrapTextWithAnsi } from "./utils.js";