@awarebydefault/carte
v0.22.0
Published
A concurrent React renderer for ultra-low-latency terminal applications.
Maintainers
Readme
Carte
A concurrent React renderer for terminal applications, full-screen on the alternate screen or inline in the main one. Yoga flexbox layout, a terminal-native host tree, focus and input management, and cell-level updates into a packed, double-buffered screen.
bun add @awarebydefault/carte reactimport {Box, Input, render, Text} from '@awarebydefault/carte'
function App() {
return (
<Box
borderLabel="Profile"
style={{flexDirection: 'column', borderStyle: 'round', padding: 1}}
>
<Text>Name</Text>
<Input placeholder="Ada Lovelace" autoFocus />
</Box>
)
}
const {waitUntilExit} = render(<App />)
await waitUntilExit()Bun >=1.3.14, React ^19.2.0. Pre-release: the API is still being reshaped.
Documentation
| | | | --- | --- | | Getting started | Install to a tested application, and the traps on the way | | Reference | Every component, prop, style, hook, event, binding, and option | | Writing tests | The three tiers, and why waiting is about the screen rather than the clock | | How it works and why | The architecture, the constraints, and what is deliberately out of scope |
package.json declares repository.directory, so npm resolves these against
the repository — they work from the package page and from a checkout alike.
What it is
Not a browser. There is no DOM, no CSS, and no HTML. div, span, and
input are rejected at reconciliation, because a terminal has nothing to render
them onto. Applications compose Box, Text, Input, Textarea, ScrollBox,
and the rest of Carte's primitives, and styles are a bounded Yoga subset plus
terminal SGR attributes passed as an object.
Real React. Function and class components, hooks, context, refs, keyed
reconciliation, fragments, controlled components, external stores, Suspense,
transitions, error boundaries, and portals all work. render() returns a root
with rerender(), asynchronous unmount(), flushSync(), and
batchedUpdates(). Await unmount() when shutting down programmatically, so
React effects are cleaned up before terminal modes are restored.
Either screen. By default Carte takes the alternate screen and gives it back
untouched on exit. Where that screen is not available — output redirected to a
pipe, a terminal calling itself dumb, or one that answers that it does not
know mode 1049 — it draws an inline block at the cursor instead, leaving
scrollback intact. Pass screen: 'alternate' or screen: 'inline' to decide
for yourself; an application that names one is never overridden.
The keyboard through actions. A binding maps a chord to an action name and
the application handles the action, so a user can remap it and a help overlay can
list it. KeybindingScope defaults to focus-tree scoping — bindings apply only
while focus is below the scope — and global covers application-wide navigation
and modal UI.
<KeybindingScope context="Navigation" global>
<App />
</KeybindingScope>Three chords are reserved and cannot be rebound: Ctrl+C, Ctrl+D, and
Ctrl+M, the last because it is Enter at the byte level. What Carte binds by
default is smaller and yours to take back — Ctrl+C to interrupt, Ctrl+D to
exit, Ctrl+L to redraw. Every other command is the application's.
Subpaths
| Import | Holds |
| --- | --- |
| @awarebydefault/carte | Components, hooks, render, and the types an application needs |
| @awarebydefault/carte/testing | The test harness; /testing/e2e for a real terminal |
| @awarebydefault/carte/highlight | Syntax grammars for CodeBlock and Markdown, in their own bundle |
| @awarebydefault/carte/devtools | Opening a running application to an inspector |
| @awarebydefault/carte/adapters/* | Bridges to packages that expect a browser |
| @awarebydefault/carte/core | The renderer's internals, for integrations that reach past the components |
Each is its own bundle, so importing the root carries none of the others.
Licence
MIT.
