405 lines
7.6 KiB
Markdown
405 lines
7.6 KiB
Markdown
# React API Reference
|
|
|
|
## Rendering
|
|
|
|
### createRoot(renderer)
|
|
|
|
Creates a React root for rendering.
|
|
|
|
```tsx
|
|
import { createCliRenderer } from "@opentui/core"
|
|
import { createRoot } from "@opentui/react"
|
|
|
|
const renderer = await createCliRenderer({
|
|
exitOnCtrlC: false, // Handle Ctrl+C yourself
|
|
})
|
|
|
|
const root = createRoot(renderer)
|
|
root.render(<App />)
|
|
```
|
|
|
|
## Hooks
|
|
|
|
### useRenderer()
|
|
|
|
Access the OpenTUI renderer instance.
|
|
|
|
```tsx
|
|
import { useRenderer } from "@opentui/react"
|
|
import { useEffect } from "react"
|
|
|
|
function App() {
|
|
const renderer = useRenderer()
|
|
|
|
useEffect(() => {
|
|
// Access renderer properties
|
|
console.log(`Terminal: ${renderer.width}x${renderer.height}`)
|
|
|
|
// Show debug console
|
|
renderer.console.show()
|
|
}, [renderer])
|
|
|
|
return <text>Hello</text>
|
|
}
|
|
```
|
|
|
|
### useKeyboard(handler, options?)
|
|
|
|
Handle keyboard events.
|
|
|
|
```tsx
|
|
import { useKeyboard, useRenderer } from "@opentui/react"
|
|
|
|
function App() {
|
|
const renderer = useRenderer()
|
|
|
|
useKeyboard((key) => {
|
|
if (key.name === "escape") {
|
|
renderer.destroy() // Never use process.exit() directly!
|
|
}
|
|
if (key.ctrl && key.name === "s") {
|
|
saveDocument()
|
|
}
|
|
})
|
|
|
|
return <text>Press ESC to exit</text>
|
|
}
|
|
|
|
// With release events
|
|
function GameControls() {
|
|
const [pressed, setPressed] = useState(new Set<string>())
|
|
|
|
useKeyboard(
|
|
(event) => {
|
|
setPressed(keys => {
|
|
const newKeys = new Set(keys)
|
|
if (event.eventType === "release") {
|
|
newKeys.delete(event.name)
|
|
} else {
|
|
newKeys.add(event.name)
|
|
}
|
|
return newKeys
|
|
})
|
|
},
|
|
{ release: true } // Include release events
|
|
)
|
|
|
|
return <text>Pressed: {Array.from(pressed).join(", ")}</text>
|
|
}
|
|
```
|
|
|
|
**Options:**
|
|
- `release?: boolean` - Include key release events (default: false)
|
|
|
|
**KeyEvent properties:**
|
|
- `name: string` - Key name ("a", "escape", "f1", etc.)
|
|
- `sequence: string` - Raw escape sequence
|
|
- `ctrl: boolean` - Ctrl modifier
|
|
- `shift: boolean` - Shift modifier
|
|
- `meta: boolean` - Alt modifier
|
|
- `option: boolean` - Option modifier (macOS)
|
|
- `eventType: "press" | "release" | "repeat"`
|
|
- `repeated: boolean` - Key is being held
|
|
|
|
### useOnResize(callback)
|
|
|
|
Handle terminal resize events.
|
|
|
|
```tsx
|
|
import { useOnResize } from "@opentui/react"
|
|
|
|
function App() {
|
|
useOnResize((width, height) => {
|
|
console.log(`Resized to ${width}x${height}`)
|
|
})
|
|
|
|
return <text>Resize the terminal</text>
|
|
}
|
|
```
|
|
|
|
### useTerminalDimensions()
|
|
|
|
Get reactive terminal dimensions.
|
|
|
|
```tsx
|
|
import { useTerminalDimensions } from "@opentui/react"
|
|
|
|
function ResponsiveLayout() {
|
|
const { width, height } = useTerminalDimensions()
|
|
|
|
return (
|
|
<box flexDirection={width > 80 ? "row" : "column"}>
|
|
<box flexGrow={1}>
|
|
<text>Width: {width}</text>
|
|
</box>
|
|
<box flexGrow={1}>
|
|
<text>Height: {height}</text>
|
|
</box>
|
|
</box>
|
|
)
|
|
}
|
|
```
|
|
|
|
### useTimeline(options?)
|
|
|
|
Create animations with the timeline system.
|
|
|
|
```tsx
|
|
import { useTimeline } from "@opentui/react"
|
|
import { useEffect, useState } from "react"
|
|
|
|
function AnimatedBox() {
|
|
const [width, setWidth] = useState(0)
|
|
|
|
const timeline = useTimeline({
|
|
duration: 2000,
|
|
loop: false,
|
|
})
|
|
|
|
useEffect(() => {
|
|
timeline.add(
|
|
{ width: 0 },
|
|
{
|
|
width: 50,
|
|
duration: 2000,
|
|
ease: "easeOutQuad",
|
|
onUpdate: (anim) => {
|
|
setWidth(Math.round(anim.targets[0].width))
|
|
},
|
|
}
|
|
)
|
|
}, [timeline])
|
|
|
|
return <box style={{ width, height: 3, backgroundColor: "#6a5acd" }} />
|
|
}
|
|
```
|
|
|
|
**Options:**
|
|
- `duration?: number` - Default duration (ms)
|
|
- `loop?: boolean` - Loop the timeline
|
|
- `autoplay?: boolean` - Auto-start (default: true)
|
|
- `onComplete?: () => void` - Completion callback
|
|
- `onPause?: () => void` - Pause callback
|
|
|
|
**Timeline methods:**
|
|
- `add(target, properties, startTime?)` - Add animation
|
|
- `play()` - Start playback
|
|
- `pause()` - Pause playback
|
|
- `restart()` - Restart from beginning
|
|
|
|
## Components
|
|
|
|
### Text Component
|
|
|
|
```tsx
|
|
<text
|
|
content="Hello" // Or use children
|
|
fg="#FFFFFF" // Foreground color
|
|
bg="#000000" // Background color
|
|
selectable={true} // Allow text selection
|
|
>
|
|
{/* Use nested modifier tags for styling */}
|
|
<span fg="red">Red</span>
|
|
<strong>Bold</strong>
|
|
<em>Italic</em>
|
|
<u>Underline</u>
|
|
<br />
|
|
<a href="https://...">Link</a>
|
|
</text>
|
|
```
|
|
|
|
> **Note**: Do NOT use `bold`, `italic`, `underline` as props on `<text>`. Use nested modifier tags like `<strong>`, `<em>`, `<u>` instead.
|
|
|
|
### Box Component
|
|
|
|
```tsx
|
|
<box
|
|
// Borders
|
|
border // Enable border
|
|
borderStyle="single" // single | double | rounded | bold
|
|
borderColor="#FFFFFF"
|
|
title="Title"
|
|
titleAlignment="center" // left | center | right
|
|
|
|
// Colors
|
|
backgroundColor="#1a1a2e"
|
|
|
|
// Layout (see layout/REFERENCE.md)
|
|
flexDirection="row"
|
|
justifyContent="center"
|
|
alignItems="center"
|
|
gap={2}
|
|
|
|
// Spacing
|
|
padding={2}
|
|
paddingTop={1}
|
|
margin={1}
|
|
|
|
// Dimensions
|
|
width={40}
|
|
height={10}
|
|
flexGrow={1}
|
|
|
|
// Events
|
|
onMouseDown={(e) => {}}
|
|
onMouseUp={(e) => {}}
|
|
onMouseMove={(e) => {}}
|
|
>
|
|
{children}
|
|
</box>
|
|
```
|
|
|
|
### Scrollbox Component
|
|
|
|
```tsx
|
|
<scrollbox
|
|
focused // Enable keyboard scrolling
|
|
style={{
|
|
rootOptions: { backgroundColor: "#24283b" },
|
|
wrapperOptions: { backgroundColor: "#1f2335" },
|
|
viewportOptions: { backgroundColor: "#1a1b26" },
|
|
contentOptions: { backgroundColor: "#16161e" },
|
|
scrollbarOptions: {
|
|
showArrows: true,
|
|
trackOptions: {
|
|
foregroundColor: "#7aa2f7",
|
|
backgroundColor: "#414868",
|
|
},
|
|
},
|
|
}}
|
|
>
|
|
{/* Scrollable content */}
|
|
{items.map((item, i) => (
|
|
<box key={i}>
|
|
<text>{item}</text>
|
|
</box>
|
|
))}
|
|
</scrollbox>
|
|
```
|
|
|
|
### Input Component
|
|
|
|
```tsx
|
|
<input
|
|
value={value}
|
|
onChange={(newValue) => setValue(newValue)}
|
|
placeholder="Enter text..."
|
|
focused // Start focused
|
|
width={30}
|
|
backgroundColor="#1a1a1a"
|
|
textColor="#FFFFFF"
|
|
cursorColor="#00FF00"
|
|
focusedBackgroundColor="#2a2a2a"
|
|
/>
|
|
```
|
|
|
|
### Textarea Component
|
|
|
|
```tsx
|
|
<textarea
|
|
value={text}
|
|
onChange={(newValue) => setText(newValue)}
|
|
placeholder="Enter multiple lines..."
|
|
focused
|
|
width={40}
|
|
height={10}
|
|
showLineNumbers
|
|
wrapText
|
|
/>
|
|
```
|
|
|
|
### Select Component
|
|
|
|
```tsx
|
|
<select
|
|
options={[
|
|
{ name: "Option 1", description: "First option", value: "1" },
|
|
{ name: "Option 2", description: "Second option", value: "2" },
|
|
]}
|
|
onChange={(index, option) => setSelected(option)}
|
|
selectedIndex={0}
|
|
focused
|
|
showScrollIndicator
|
|
height={8}
|
|
/>
|
|
```
|
|
|
|
### Tab Select Component
|
|
|
|
```tsx
|
|
<tab-select
|
|
options={[
|
|
{ name: "Home", description: "Dashboard" },
|
|
{ name: "Settings", description: "Configuration" },
|
|
]}
|
|
onChange={(index, option) => setTab(option)}
|
|
tabWidth={20}
|
|
focused
|
|
/>
|
|
```
|
|
|
|
### ASCII Font Component
|
|
|
|
```tsx
|
|
<ascii-font
|
|
text="TITLE"
|
|
font="tiny" // tiny | block | slick | shade
|
|
color="#FFFFFF"
|
|
/>
|
|
```
|
|
|
|
### Code Component
|
|
|
|
```tsx
|
|
<code
|
|
code={sourceCode}
|
|
language="typescript"
|
|
showLineNumbers
|
|
highlightLines={[1, 5, 10]}
|
|
/>
|
|
```
|
|
|
|
### Line Number Component
|
|
|
|
```tsx
|
|
<line-number
|
|
code={sourceCode}
|
|
language="typescript"
|
|
startLine={1}
|
|
highlightedLines={[5]}
|
|
diagnostics={[
|
|
{ line: 3, severity: "error", message: "Syntax error" }
|
|
]}
|
|
/>
|
|
```
|
|
|
|
### Diff Component
|
|
|
|
```tsx
|
|
<diff
|
|
oldCode={originalCode}
|
|
newCode={modifiedCode}
|
|
language="typescript"
|
|
mode="unified" // unified | split
|
|
showLineNumbers
|
|
/>
|
|
```
|
|
|
|
## Type Exports
|
|
|
|
```tsx
|
|
import type {
|
|
// Component props
|
|
TextProps,
|
|
BoxProps,
|
|
InputProps,
|
|
SelectProps,
|
|
|
|
// Hook types
|
|
KeyEvent,
|
|
|
|
// From core
|
|
CliRenderer,
|
|
} from "@opentui/react"
|
|
```
|