Ink Target

Compile KERN to Ink (React for the terminal). Full React component model with hooks, input handling, and flexbox layout.

What it generates

KERN input:

screen name=Dashboard
  state name=count initial=0
  on event=key key=up
    handler <<<
      setCount(prev => prev + 1);
    >>>
  box {color:blue,borderStyle:round}
    text value="Count:" {fw:bold}
    text value={{count}} {color:green}

Compiled output:

import React, { useState } from 'react';
import { Box, Text, useInput } from 'ink';

export default function Dashboard() {
  const [count, setCount] = useState(0);

  useInput((input, key) => {
    if (!(key.upArrow)) return;
    setCount(prev => prev + 1);
  });

  return (
    <Box flexDirection="column">
      <Box borderStyle="round" borderColor="blue">
        <Text bold>{"Count:"}</Text>
        <Text color="green">{count}</Text>
      </Box>
    </Box>
  );
}

Supported nodes

The Ink transpiler maps KERN nodes to Ink components and React hooks:

UI nodes

KERN NodeInk OutputDescription
screenReact function componentRoot component (export default)
text<Text>Styled text with bold, color, dimColor, italic, backgroundColor
box<Box>Flexbox container with borderStyle, borderColor, padding, width, flexGrow
separator<Text dimColor>Horizontal line
table<Box flexDirection="column">Table with header and row components
scoreboard<Box flexDirection="column">Results display with metric rows
spinner<Spinner /> (ink-spinner)Animated loading indicator
progress<Box> with bar charactersProgress bar with percentage, supports expressions
gradient<Text> per characterPer-character color gradient
input-area<Box borderStyle="single">Persistent input region at bottom
output-area<Box flexGrow={1}>Scrollable output region
text-input<TextInput /> (ink-text-input)Text input with bind, placeholder, onSubmit, history
select-input<SelectInput /> (ink-select-input)Selection list with items and onSelect
each.map() iterationList rendering with key support
conditional{condition && (...)}Conditional rendering

Hook nodes

KERN NodeReact HookDescription
stateuseStateReactive state with auto-generated setter (setX)
refuseRefMutable ref (xRef)
on event=keyuseInputKeyboard input handler with key filtering
streamuseEffect + async generatorStreaming data with auto-cancellation
logicuseEffectSide-effect code with dependency array
callbackuseCallbackMemoized function with params and deps
machineuseReducerState machine with reducer pattern

Component props

Screen nodes accept a props attribute for typed component parameters:

screen name=StatusBar props="label:string, active:boolean"
  text value={{label}} {fw:bold}
  conditional if={{active}}
    text value="ON" {color:green}

Compiles to:

export default function StatusBar({ label, active }: { label: string; active: boolean }) {
  // ...
}

Key handling

The on event=key node maps to Ink's useInput hook. Supported key names: return, escape, tab, up, down, left, right, backspace, delete, or any single character.

New in v3.1.9

The Ink transpiler received a major upgrade to production-ready status:

  • @inkjs/ui components — Spinner, TextInput, SelectInput, MultiSelect, ConfirmInput, PasswordInput, StatusMessage, Alert, OrderedList, UnorderedList migrated from legacy ink-* packages
  • Safe setters — __inkSafe wrapper auto-bridges microtask to macrotask on all state setters, eliminating repaint bugs
  • Channel streams — stream mode=channel for AsyncGenerator patterns with AbortController cleanup
  • Multi-screen files — Named and default exports, cross-screen imports with screen-embed from=
  • Layout primitives — layout-row, layout-col, layout-stack, spacer
  • Entry-point artifact — Auto-generates index.tsx with render + waitUntilExit boilerplate
  • Lazy useState — IIFEs and constructors wrapped as useState(() => expr)

Dependencies

The Ink target requires these npm packages:

npm install react ink @inkjs/ui

@inkjs/ui components are imported automatically when their corresponding nodes are used.

Configuration

kern dev app.kern --target=ink