Terminal UI library built with Effect
npm install @effect-tui/coreLow-level terminal UI primitives built with Effect.
``bash`
bun add @effect-tui/core effect
- CellBuffer - Frame storage with glyph, style, and width-aware cells
- Palette - Style deduplication for ANSI output
- Colors - Color helpers (indexed, RGB, hex, grayscale)
- Keys - Raw TTY key decoding
- Spring animations - Analytical spring physics
A 2D grid of cells, each storing a codepoint, style ID, and width. Handles wide characters (CJK, emoji) correctly.
`typescript`
const buffer = new CellBuffer(80, 24)
buffer.put(0, 0, "Hello", styleId) // Put string
buffer.drawCP(5, 0, 0x1F600, styleId) // Draw codepoint (emoji)
buffer.fillRect(0, 1, 10, 3, 32, styleId) // Fill rectangle
Maps style objects to numeric IDs and generates ANSI escape sequences:
`typescript`
const palette = new Palette()
const style = palette.id({ fg: Colors.green, bg: Colors.black })
const ansi = palette.sgr(style) // "\x1b32;40m"
`typescript`
Colors.red // Named color (indexed)
Colors.rgb(255,0,0) // RGB
Colors.hex("#ff0000") // Hex
Colors.gray(12) // Grayscale (0-23)
Colors.idx(196) // 256-color index
`typescript
import { CellBuffer, Surface, Colors } from "@effect-tui/core"
// Create a buffer
const buffer = new CellBuffer(80, 24)
buffer.put(0, 0, "Hello", { fg: Colors.green })
// Render to terminal
const surface = new Surface(process.stdout)
surface.render(buffer)
`
Observable values with spring physics (matches framer-motion):
`typescript
import { motionValue } from "@effect-tui/core"
// Create motion value
const mv = motionValue(0)
// Subscribe to changes
mv.onChange((value) => console.log(value))
// Animate to target with spring physics
mv.animate(100, { duration: 0.35, bounce: 0.2 })
// Read current value
const value = mv.get()
`
Parameters:
- duration - Time to reach ~99% of target (seconds)bounce
- - Overshoot amount (0 = no bounce, 0.5 = bouncy)
Frame-based discrete animation (used by ShinyText):
`typescript
import { Step } from "@effect-tui/core"
// Create stepper cycling through 10 frames at 80ms/frame
let stepper = Step.init(10, 80)
// Tick with current timestamp
stepper = Step.tick(stepper, Date.now())
console.log(stepper.index) // Current frame index
``
- [@effect-tui/react - React renderer for terminal UIs
MIT