Slate mantém a composição declarativa e o JSX, mas separa a árvore da aplicação do ciclo de vida do terminal. Essa diferença é o ponto que mais importa ao migrar: criar uma view não inicia uma sessão física.
| Ink | Slate 2.3.0 |
|---|---|
render(<App />) inicia a aplicação |
render(<App />) cria um SlateApplication; use um controller para iniciar input/output |
Box |
Container |
Text |
Text ou texto filho |
useInput |
useInput(app, handler) ou onEvent |
useFocus |
useFocus(app, id) |
useFocusManager |
useFocusManager(app) |
useCursor |
useCursor(app) |
useWindowSize |
useWindowSize(app) |
useApp().exit() |
handler que retorna "exit" |
useStdin |
createTerminalSession().input ou fonte própria + createInputRouter |
useState |
signal ou app.setState |
| Yoga via Ink | createYogaLayoutEngine ou createFlexLayoutEngine |
@inkjs/ui |
Input, Select, Checkbox, Tabs, Table, Spinner, Progress, Modal, ScrollView, List e Form |
Para renderização ANSI e JSX sem React:
npm install @slate-terminal/core @slate-terminal/react
Para teclado e mouse do terminal, acrescente:
npm install @slate-terminal/native
A instalação de native é desnecessária para um frame. Rust só é necessário para
compilar esse addon a partir do checkout com npm run native:build.
O equivalente ao render(<App />) de Ink, quando a aplicação precisa de input
físico, é explícito:
import { createTerminalSession } from "@slate-terminal/core";
import { Container, Text, createSlateOutput, createTerminalController, render } from "@slate-terminal/react";
const app = render(Container({
id: "app",
children: Text({ id: "message", text: "Press Ctrl+C to exit" })
}), { viewport: { width: 40, height: 2 } });
const session = createTerminalSession();
const terminal = createTerminalController(
app,
session.input,
createSlateOutput(process.stdout),
{ onExit: () => session.close() }
);
const shutdown = () => {
terminal.close();
session.close();
};
process.once("SIGTERM", shutdown);
terminal.start();
terminal.start() inicia o polling. terminal.close() fecha o router e a
árvore; session.close() libera a fonte e restaura os modos nativos. Ctrl+C
já é tratado pelo controller. Se o addon estiver ausente, o caminho acima falha
em createTerminalSession() com uma mensagem explícita; renderAnsi() continua
disponível.
Antes:
import React from "react";
import { Box, Text } from "ink";
export function App() {
return <Box flexDirection="column"><Text>Slate</Text></Box>;
}
Depois:
import { Container, Text } from "@slate-terminal/react";
export function App() {
return <Container direction="column"><Text>Slate</Text></Container>;
}
No tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"jsx": "react-jsx",
"jsxImportSource": "@slate-terminal/react",
"strict": true
}
}
Esse caminho não instala React. createReactAdapter e
createReactTerminalRoot ficam disponíveis quando a aplicação realmente usa
React e o reconciler compatível.
Sinais substituem o estado local de um componente:
import { Text, signal } from "@slate-terminal/react";
const count = signal(0);
const view = Text({ id: "count", text: count });
count.update(value => value + 1);
Para testar input sem um terminal, use eventos diretamente na aplicação:
import { Button, Container, Input, render, signal } from "@slate-terminal/react";
const name = signal("");
const app = render(Container({
id: "app",
children: [
Input({ id: "name", value: name, onChange: value => name.set(value) }),
Button({ id: "save", label: "Salvar" })
]
}), { viewport: { width: 20, height: 2 } });
app.focus("name");
app.dispatch({ kind: "key", code: "A" });
app.dispatch({ kind: "paste", text: " Slate" });
app.dispatch({ kind: "key", code: "Tab" });
app.close();
Mouse e teclado chegam aos mesmos handlers. Para uma fonte nativa, prefira
createTerminalSession().input; para uma fonte de teste ou de outro processo,
implemente poll(timeoutMs) e conecte-a com createInputRouter.
@slate-terminal/core e @slate-terminal/react.Box por Container, preservando IDs que participam de foco e edição.useInput por onEvent ou por useInput(app, handler).useState por sinais controlados nos widgets.@slate-terminal/native somente quando a aplicação precisar de
input físico.O guia de produção cobre o ciclo completo. A referência para agentes resume contratos e erros para ferramentas que precisam gerar código sem adivinhar o lifecycle.