@wolf-tui/core
v1.3.3
Published
Framework-agnostic TUI core library
Readme
@wolf-tui/core
Framework-agnostic TUI core engine for wolf-tui. Provides DOM abstraction, layout engine, and rendering primitives.
Overview
This package is the foundation of wolf-tui. It provides:
- DOM abstraction — Virtual DOM nodes for TUI elements
- Taffy layout engine — Flexbox and CSS Grid via Rust bindings
- Renderer — Converts DOM tree to ANSI terminal output
- Utilities — Text measurement, keypress parsing, color handling
Note: This is an internal package. Most users should use
@wolf-tui/react,@wolf-tui/vue, or@wolf-tui/angularinstead.
Taffy Layout Engine
wolf-tui v2.0 uses Taffy, a high-performance Rust-based layout engine.
Why Taffy?
| Feature | Yoga (old) | Taffy (current) |
| ----------- | -------------- | --------------- |
| Flexbox | Yes | Yes |
| CSS Grid | No | Yes |
| calc() | No | Yes |
| Framework | React-specific | Agnostic |
| Performance | Good | Better |
Integration
Taffy is integrated via napi-rs native Node.js bindings:
internal/core/
├── rust/
│ ├── lib.rs # napi-rs entry point
│ ├── layout.rs # LayoutTree wrapper
│ ├── style.rs # Style conversion
│ └── error.rs # Error handling
├── Cargo.toml
└── layout.js # Generated JS bindingsPlatform Support
| Platform | Architecture | Binary |
| -------- | ------------ | ------------------------------------ |
| Linux | x86_64 | wolfie-layout.linux-x64-gnu.node |
| Linux | aarch64 | wolfie-layout.linux-arm64-gnu.node |
| macOS | x86_64 | wolfie-layout.darwin-x64.node |
| macOS | aarch64 | wolfie-layout.darwin-arm64.node |
| Windows | x86_64 | wolfie-layout.win32-x64-msvc.node |
Building Native Bindings
cd internal/core
pnpm build:rust # Build native bindings only
pnpm build:full # Build Rust + TypeScriptRequires Rust toolchain installed.
API Reference
DOM Functions
createNode(name, layoutTree)
Create a new DOM element.
import { createNode, LayoutTree } from '@wolf-tui/core'
import { LayoutTree as TaffyLayoutTree } from '@wolf-tui/core/layout'
const layoutTree = new TaffyLayoutTree()
const node = createNode('wolfie-box', layoutTree)appendChildNode(parent, child)
Append a child node to a parent.
appendChildNode(parent, child)insertBeforeNode(parent, child, before)
Insert a child before another node.
insertBeforeNode(parent, newChild, referenceChild)removeChildNode(parent, child)
Remove a child from parent.
removeChildNode(parent, child)setAttribute(node, key, value)
Set an attribute on a node.
setAttribute(node, 'className', 'my-class')setStyle(node, style)
Set style properties on a node.
setStyle(node, {
flexDirection: 'column',
padding: 1,
borderStyle: 'round',
})createTextNode(value, layoutTree)
Create a text node.
const textNode = createTextNode('Hello', layoutTree)setTextNodeValue(node, value)
Update text node content.
setTextNodeValue(textNode, 'Updated text')Renderer
renderer(rootNode, isScreenReaderEnabled, layoutTree)
Render DOM tree to terminal output.
import { renderer } from '@wolf-tui/core'
const { output } = renderer(rootNode, false, layoutTree)
process.stdout.write(output)Returns:
output— ANSI-styled string for terminaloutputHeight— Number of lines
Layout
getComputedLayout(node, layoutTree)
Get computed layout for a node.
import { getComputedLayout } from '@wolf-tui/core'
const layout = getComputedLayout(node, layoutTree)
// { left: 0, top: 0, width: 80, height: 24 }Returns ComputedLayout:
left— X positiontop— Y positionwidth— Computed widthheight— Computed height
isDisplayNone(node, layoutTree)
Check if node is hidden.
if (isDisplayNone(node, layoutTree)) {
// Node not rendered
}Styles
applyLayoutStyle(layoutTree, nodeId, style)
Apply style to layout node.
import { applyLayoutStyle } from '@wolf-tui/core'
applyLayoutStyle(layoutTree, node.layoutNodeId, {
flexDirection: 'column',
padding: 2,
})resolveViewportUnits(style, terminalWidth, terminalHeight)
Resolve vw/vh units to absolute values.
import { resolveViewportUnits } from '@wolf-tui/core'
const resolved = resolveViewportUnits(
{ width: '50vw', height: '100vh' },
80, // terminal columns
24 // terminal rows
)
// { width: 40, height: 24 }parseNumericValue(value)
Parse numeric style value.
parseNumericValue('1rem') // 4
parseNumericValue('8px') // 2
parseNumericValue(10) // 10Utilities
measureText(text)
Measure text dimensions.
import { measureText } from '@wolf-tui/core'
const { width, height } = measureText('Hello\nWorld')
// { width: 5, height: 2 }wrapText(text, maxWidth, wrapMode)
Wrap text to fit width.
import { wrapText } from '@wolf-tui/core'
const wrapped = wrapText('Long text here', 10, 'wrap')Wrap modes:
'wrap'— Wrap at word boundaries'truncate'/'truncate-end'— Truncate with ellipsis at end'truncate-start'— Truncate with ellipsis at start'truncate-middle'— Truncate with ellipsis in middle
parseKeypress(input)
Parse terminal keypress data.
import { parseKeypress } from '@wolf-tui/core'
const key = parseKeypress('\x1b[A') // Up arrow
// { name: 'up', ctrl: false, shift: false, meta: false, ... }colorize(text, color, backgroundColor)
Apply ANSI colors to text.
import { colorize } from '@wolf-tui/core'
const colored = colorize('Hello', 'green', 'black')measureElement(node)
Measure a rendered element.
import { measureElement } from '@wolf-tui/core'
const { width, height } = measureElement(node)Log Update
logUpdate.create(stream)
Create a log updater for a stream.
import { logUpdate } from '@wolf-tui/core'
const log = logUpdate.create(process.stdout)
log('First output')
log('Updated output') // Replaces previous
log.done() // FinalizeMethods:
log(text)— Update output (replaces previous)log.clear()— Clear outputlog.done()— Finalize and move cursor down
Types
DOMElement
Virtual DOM element.
interface DOMElement {
nodeName: ElementNames
style?: Styles
attributes: Record<string, DOMNodeAttribute>
childNodes: DOMNode[]
parentNode?: DOMElement
layoutNodeId?: number
onRender?: () => void
}Styles
Style properties.
interface Styles {
// Layout
display?: 'flex' | 'none'
position?: 'relative' | 'absolute'
flexDirection?: 'row' | 'column' | 'row-reverse' | 'column-reverse'
flexWrap?: 'wrap' | 'nowrap' | 'wrap-reverse'
flexGrow?: number
flexShrink?: number
flexBasis?: number | string
alignItems?: 'flex-start' | 'center' | 'flex-end' | 'stretch'
alignSelf?: 'flex-start' | 'center' | 'flex-end' | 'auto'
justifyContent?:
| 'flex-start'
| 'center'
| 'flex-end'
| 'space-between'
| 'space-around'
| 'space-evenly'
gap?: number
rowGap?: number
columnGap?: number
// Sizing
width?: number | string
height?: number | string
minWidth?: number
minHeight?: number
maxWidth?: number
maxHeight?: number
// Spacing
padding?: number
paddingX?: number
paddingY?: number
paddingTop?: number
paddingRight?: number
paddingBottom?: number
paddingLeft?: number
margin?: number
marginX?: number
marginY?: number
marginTop?: number
marginRight?: number
marginBottom?: number
marginLeft?: number
// Border
borderStyle?: 'single' | 'double' | 'round' | 'classic' | 'bold'
borderColor?: string
borderTopColor?: string
borderRightColor?: string
borderBottomColor?: string
borderLeftColor?: string
borderTop?: boolean
borderRight?: boolean
borderBottom?: boolean
borderLeft?: boolean
borderDimColor?: boolean
// Text
color?: string
backgroundColor?: string
fontWeight?: 'bold' | 'normal' | number
fontStyle?: 'italic' | 'normal'
textDecoration?: 'underline' | 'line-through' | 'none'
textWrap?:
| 'wrap'
| 'truncate'
| 'truncate-end'
| 'truncate-middle'
| 'truncate-start'
dimColor?: boolean
inverse?: boolean
// Overflow
overflow?: 'visible' | 'hidden'
overflowX?: 'visible' | 'hidden'
overflowY?: 'visible' | 'hidden'
}LayoutTree
Layout tree interface (implemented by Taffy bindings).
interface LayoutTree {
createNode(): number
removeNode(nodeId: number): void
setStyle(nodeId: number, style: Record<string, unknown>): void
setTextDimensions(nodeId: number, width: number, height: number): void
appendChild(parentId: number, childId: number): void
insertChild(parentId: number, childId: number, index: number): void
removeChild(parentId: number, childId: number): void
computeLayout(rootId: number, availableWidth: number): void
getLayout(nodeId: number): {
x: number
y: number
width: number
height: number
}
}Architecture
┌─────────────────────────────────────────────────────────────┐
│ Framework Adapters │
│ (React, Vue, Angular) │
└────────────────────────┬────────────────────────────────────┘
│
┌──────────▼──────────┐
│ @wolf-tui/core │
│ │
│ ┌─────────────────┐ │
│ │ DOM Abstraction │ │
│ │ (dom.ts) │ │
│ └────────┬────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ Style Handler │ │
│ │ (styles.ts) │ │
│ └────────┬────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ Taffy Layout │◀┼──── Rust Bindings
│ │ (layout.rs) │ │ (napi-rs)
│ └────────┬────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ Renderer │ │
│ │ (renderer.ts) │ │
│ └────────┬────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ ANSI Output │ │
│ │ (output.ts) │ │
│ └─────────────────┘ │
└─────────────────────┘
│
▼
TerminalLicense
MIT
