chore(vendor): import pi 1.3.0
Imported as a linear vendor commit so forge rebases do not replay the upstream root tree. git-subtree-dir: third_party/pi git-subtree-split: 71023aec1546d6d677a65669c5665c0927ce0dd0
This commit is contained in:
62
third_party/pi/packages/tui/README.md
vendored
62
third_party/pi/packages/tui/README.md
vendored
@@ -78,6 +78,44 @@ tui.requestRender(); // Request a re-render
|
||||
tui.onDebug = () => console.log("Debug triggered");
|
||||
```
|
||||
|
||||
### Colors and terminal styles
|
||||
|
||||
Colors are values that can be converted or mixed before terminal rendering:
|
||||
|
||||
```typescript
|
||||
import {
|
||||
colorToRgb,
|
||||
foregroundAnsi,
|
||||
getTerminalColorMode,
|
||||
mixColors,
|
||||
parseColor,
|
||||
rgbColor,
|
||||
styleText,
|
||||
} from "@earendil-works/pi-tui";
|
||||
|
||||
const accent = parseColor("oklch(70% 0.12 220)");
|
||||
const background = parseColor("#20242a");
|
||||
const foreground = mixColors(accent, background, 0.2);
|
||||
|
||||
const text = styleText(
|
||||
"Ready",
|
||||
{ fg: foreground, bg: background, bold: true },
|
||||
getTerminalColorMode(),
|
||||
);
|
||||
```
|
||||
|
||||
`Color` is an indexed ANSI color, an sRGB color, or an OKLCH color. Every color converts to sRGB, so color math such as `mixColors()` always works. Indices 0-15 follow the user's terminal palette, so their sRGB values are approximations. `styleText()` converts colors to truecolor or 256-color output based on the requested terminal mode.
|
||||
|
||||
`parseColor()` also accepts OKHSL, as in `okhsl(250 60% 55%)`; `okhslColor()` builds it in code and `colorToOkhsl()` reads any color's OKHSL channels. OKHSL saturation is relative to the most the sRGB gamut allows at the hue and lightness, so every value is in gamut and equal saturation looks equally colorful across hues. OKHSL colors are converted to sRGB when created.
|
||||
|
||||
Conversions are not cached. OKLCH colors, especially ones outside the sRGB gamut, are more expensive to convert than sRGB or indexed colors. For colors used on every render, convert once and reuse the result:
|
||||
|
||||
```typescript
|
||||
const { r, g, b } = colorToRgb(mixColors(accent, background, 0.2));
|
||||
const foreground = rgbColor(r, g, b); // cheap to render repeatedly
|
||||
const foregroundCode = foregroundAnsi(foreground, getTerminalColorMode());
|
||||
```
|
||||
|
||||
### Alternate-screen viewport layouts
|
||||
|
||||
`TuiAltScreen` can render an explicit terminal-height layout. `VStack` and `HStack` allocate constrained regions, while `ScrollView` owns scrolling for one region. These semantics are intentionally unavailable on `TuiMainScreen`, where the terminal owns scrollback.
|
||||
@@ -213,7 +251,7 @@ interface Component {
|
||||
render(width: number): string[];
|
||||
handleInput?(data: string): void;
|
||||
handleMouse?(event: TuiMouseEvent): TuiMouseEventResult | undefined;
|
||||
invalidate?(): void;
|
||||
invalidate(): void;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -222,7 +260,7 @@ interface Component {
|
||||
| `render(width)` | Returns an array of strings, one per line. Each line **must not exceed `width`** or the TUI will error. Use `truncateToWidth()` or manual wrapping to ensure this. |
|
||||
| `handleInput?(data)` | Called when the component has focus and receives keyboard input. The `data` string contains raw terminal input (may include ANSI escape sequences). |
|
||||
| `handleMouse?(event)` | Called by `TuiAltScreen` for normalized pointer input targeted at the component. |
|
||||
| `invalidate?()` | Called to clear any cached render state. Components should re-render from scratch on the next `render()` call. |
|
||||
| `invalidate()` | Required. Clear any cached render state so the next `render()` starts from scratch. Components without cached render state can use an empty implementation. |
|
||||
|
||||
The TUI appends a full SGR reset and OSC 8 reset at the end of each rendered line. Styles do not carry across lines. If you emit multi-line text with styling, reapply styles per line or use `wrapTextWithAnsi()` so styles are preserved for each wrapped line.
|
||||
|
||||
@@ -270,7 +308,7 @@ const collapsible = new MouseRegion(content, (event) => {
|
||||
Components that display a text cursor and need IME (Input Method Editor) support should implement the `Focusable` interface:
|
||||
|
||||
```typescript
|
||||
import { CURSOR_MARKER, type Component, type Focusable } from "@earendil-works/pi-tui";
|
||||
import { CURSOR_MARKER, type Component, type Focusable, renderFakeCursor } from "@earendil-works/pi-tui";
|
||||
|
||||
class MyInput implements Component, Focusable {
|
||||
focused: boolean = false; // Set by TUI when focus changes
|
||||
@@ -278,8 +316,10 @@ class MyInput implements Component, Focusable {
|
||||
render(width: number): string[] {
|
||||
const marker = this.focused ? CURSOR_MARKER : "";
|
||||
// Emit marker right before the fake cursor
|
||||
return [`> ${beforeCursor}${marker}\x1b[7m${atCursor}\x1b[27m${afterCursor}`];
|
||||
return [`> ${beforeCursor}${marker}${renderFakeCursor(atCursor)}${afterCursor}`];
|
||||
}
|
||||
|
||||
invalidate(): void {}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -289,7 +329,9 @@ When a `Focusable` component has focus, TUI:
|
||||
3. Positions the hardware terminal cursor at that location
|
||||
4. Shows the hardware cursor only when `showHardwareCursor` is enabled
|
||||
|
||||
The cursor remains hidden by default. This keeps the fake cursor rendering, while still positioning the hardware cursor for terminals that track IME candidate windows with hidden cursors. Some terminals require a visible hardware cursor for IME positioning; enable it with the renderer constructor's `showHardwareCursor` argument or `setShowHardwareCursor(true)`. The `Editor` and `Input` built-in components already implement this interface.
|
||||
`renderFakeCursor()` wraps the cursor cell in zero-width markers. TUI renders them as reverse video. When `showHardwareCursor` is enabled, TUI removes the fake cursor that directly follows `CURSOR_MARKER`, so the focused component shows only the terminal cursor. Fake cursors without a preceding `CURSOR_MARKER` stay visible.
|
||||
|
||||
The hardware cursor remains hidden by default. This keeps the fake cursor rendering, while still positioning the hardware cursor for terminals that track IME candidate windows with hidden cursors. Some terminals require a visible hardware cursor for IME positioning; enable it with the renderer constructor's `showHardwareCursor` argument or `setShowHardwareCursor(true)`. The `Editor` and `Input` built-in components already implement this interface.
|
||||
|
||||
**Container components with embedded inputs:** When a container component (dialog, selector, etc.) contains an `Input` or `Editor` child, the container must implement `Focusable` and propagate the focus state to the child:
|
||||
|
||||
@@ -790,6 +832,8 @@ class MyInteractiveComponent implements Component {
|
||||
return truncateToWidth(prefix + item, width);
|
||||
});
|
||||
}
|
||||
|
||||
invalidate(): void {}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -821,6 +865,8 @@ class MyComponent implements Component {
|
||||
// Pad to exact width (optional, for backgrounds)
|
||||
return [line + " ".repeat(width - visible)];
|
||||
}
|
||||
|
||||
invalidate(): void {}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -878,7 +924,7 @@ See `test/chat-simple.ts` for a complete chat interface example with:
|
||||
|
||||
Run it:
|
||||
```bash
|
||||
npx tsx test/chat-simple.ts
|
||||
node test/chat-simple.ts
|
||||
```
|
||||
|
||||
## Development
|
||||
@@ -891,7 +937,7 @@ npm install
|
||||
npm run check
|
||||
|
||||
# Run the demo
|
||||
npx tsx test/chat-simple.ts
|
||||
node test/chat-simple.ts
|
||||
```
|
||||
|
||||
### Debug logging
|
||||
@@ -899,5 +945,5 @@ npx tsx test/chat-simple.ts
|
||||
Set `PI_TUI_WRITE_LOG` to capture the raw ANSI stream written to stdout.
|
||||
|
||||
```bash
|
||||
PI_TUI_WRITE_LOG=/tmp/tui-ansi.log npx tsx test/chat-simple.ts
|
||||
PI_TUI_WRITE_LOG=/tmp/tui-ansi.log node test/chat-simple.ts
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user