Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions docs/getting-started/create-rezi.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,33 @@ The fastest way to start a new Rezi app is the scaffolding tool:
```bash
npm create rezi my-app
cd my-app
npm start
npm run start
```

This generates a TypeScript project with a multi-panel layout, list, status bar, and keybindings.

If you prefer Bun:

```bash
npm create rezi my-app -- --pm bun
cd my-app
bun run start
```

## Templates

Choose a template interactively or pass `--template`:
Canonical template names for `--template`:

- `dashboard`: Live ops dashboard with deterministic table updates.
Highlights: live-updating table with stable row keys, filter/sort/pin controls + incident telemetry.
- `form-app`: Multi-step form with validation and command modes.
Highlights: insert/command key modes with chords, modal help and toast notifications.
- `file-browser`: Explorer with async command palette search.
Highlights: async palette results with cancellation, table browser with details and preview.
- `streaming-viewer`: High-volume stream monitor with virtualized index.
Highlights: virtual list over 15k streams, live ingest feed with follow/pause controls.

Choose a template interactively or pass a canonical name:

```bash
npm create rezi my-app -- --template dashboard
Expand All @@ -21,12 +40,18 @@ npm create rezi my-app -- --template file-browser
npm create rezi my-app -- --template streaming-viewer
```

To inspect templates and highlights from the CLI:

```bash
npm create rezi -- --list-templates
```

## Options

- `--template <name>`: Select a template.
- `--template <dashboard|form-app|file-browser|streaming-viewer>`: Select a template.
- `--no-install`: Skip dependency installation.
- `--pm <npm|pnpm|yarn|bun>`: Choose a package manager.
- `--list-templates`: Print available templates.
- `--list-templates`: Print available templates and highlights.
- `--help`: Show help.

## Next Steps
Expand Down
4 changes: 4 additions & 0 deletions docs/getting-started/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ No. While Rezi uses a declarative widget tree similar to React's component model

Rezi is designed specifically for terminal UIs, not as a React port.

### I'm using Ink. How do I migrate?

Use the [Ink to Rezi migration guide](../migration/ink-to-rezi.md) for a direct mental-model map and practical migration recipes.

### What platforms does Rezi support?

Rezi supports:
Expand Down
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ User feedback: `spinner`, `skeleton`, `callout`, `errorDisplay`, `empty`
## Learn More

- [Concepts](guide/concepts.md) - Understanding Rezi's architecture
- [Ink to Rezi Migration](migration/ink-to-rezi.md) - Mental model mapping and migration recipes
- [Lifecycle & Updates](guide/lifecycle-and-updates.md) - State management patterns
- [Layout](guide/layout.md) - Spacing, alignment, and constraints
- [Input & Focus](guide/input-and-focus.md) - Keyboard navigation and focus management
Expand Down
238 changes: 238 additions & 0 deletions docs/migration/ink-to-rezi.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
# Ink to Rezi Migration

This guide maps common Ink mental models to Rezi and gives practical migration recipes.

## Read this first

Rezi is not a drop-in replacement for Ink.

- Rezi does **not** promise API or behavior compatibility with Ink.
- Migration is intentional: move patterns, not component names.

This is by design. Rezi prioritizes deterministic rendering, deterministic input routing, and deterministic layout in terminal cells.

## Mental model map

| Ink mental model | Rezi mental model | Why this is different |
|---|---|---|
| `render(<App />)` starts a React tree | `createNodeApp(...)` + `app.view((state) => VNode)` + `app.start()` | Rezi runs a runtime-owned state/render pipeline instead of React reconciliation |
| Re-render timing follows React scheduling | Re-renders happen at deterministic commit points after `app.update(...)`/event batches | Same input + state transitions produce the same frame sequence |
| Hooks live in React components | Stateful reusable widgets use `defineWidget((props, ctx) => ...)` with `ctx.useState/useRef/useEffect/useAppState` | Keeps root view pure while still allowing local widget state |
| Input usually handled with `useInput` and component focus assumptions | Input routes through stable widget `id`s, focus order, `app.keys()`, `app.modes()`, `focusZone`, `focusTrap` | Event routing and focus are explicit and deterministic |
| Yoga/flexbox layout expectations | Cell-based layout via `ui.row`, `ui.column`, `ui.box`, `width/height/flex`, `p/m/gap` | Terminal layout is measured in character cells, not pixels |

## What's different (on purpose)

- **No compatibility promise**: Rezi does not emulate Ink internals or component semantics.
- **Deterministic scheduling**: queued updates are coalesced at commit points; render-time updates throw.
- **Deterministic focus/input**: stable `id` + documented routing order avoids timing-dependent behavior.
- **Cell-based layout**: widths/heights/overflow are resolved in terminal cells for predictable output.

## Recipes

### 1) Layout: Yoga-style intent to Rezi cell layout

Use stacks (`row`/`column`) plus explicit cell constraints.

```typescript
app.view((state) =>
ui.row({ gap: 1 }, [
ui.box({ width: 24, border: "rounded", p: 1, title: "Sidebar" }, [
ui.column({ gap: 1 }, state.menu.map((item) => ui.text(item, { key: item }))),
]),
ui.box({ flex: 1, border: "single", p: 1, title: "Content" }, [
ui.text(state.title, { style: { bold: true } }),
ui.text(state.body, { textOverflow: "ellipsis", maxWidth: 60 }),
]),
])
);
```

Migration notes:
- `width`/`height` numbers are terminal cells.
- `flex` distributes remaining space.
- `p`, `m`, and `gap` are cell-based spacing.

### 2) Lists and virtualization

Small lists: map into `ui.column` with stable `key`.

```typescript
ui.column({ gap: 0 }, state.items.map((it) => ui.text(it.label, { key: it.id })));
```

Large lists: switch to `ui.virtualList`.

```typescript
ui.virtualList({
id: "results",
items: state.rows,
itemHeight: 1,
overscan: 3,
renderItem: (row, _i, focused) =>
ui.text(`${focused ? ">" : " "} ${row.name}`, {
key: row.id,
style: focused ? { bold: true } : undefined,
}),
});
```

### 3) Tables

Use `ui.table` for sorting, selection, and built-in virtualization.

```typescript
ui.table({
id: "users",
columns: [
{ key: "name", header: "Name", flex: 1, sortable: true, overflow: "middle" },
{ key: "role", header: "Role", width: 12 },
],
data: state.users,
getRowKey: (u) => u.id,
selection: state.selection,
selectionMode: "multi",
onSelectionChange: (keys) => app.update((s) => ({ ...s, selection: keys })),
onSort: (column, direction) => app.update((s) => ({ ...s, sortColumn: column, sortDirection: direction })),
virtualized: true,
});
```

### 4) Text styling and truncation

`ui.text` handles style + deterministic cell-aware truncation.

```typescript
import { rgb } from "@rezi-ui/core";

ui.column({ gap: 1 }, [
ui.text("Build failed", { style: { fg: rgb(255, 110, 110), bold: true } }),
ui.text(state.path, { textOverflow: "middle", maxWidth: 40 }),
]);
```

### 5) Forms

Use controlled `input` widgets and validate in update handlers, not during render.

```typescript
ui.field({
label: "Email",
required: true,
error: state.touched.email ? state.errors.email : undefined,
children: ui.input({
id: "email",
value: state.email,
onInput: (value) =>
app.update((s) => {
const next = { ...s, email: value };
return { ...next, errors: validate(next) };
}),
onBlur: () => app.update((s) => ({ ...s, touched: { ...s.touched, email: true } })),
}),
});
```

### 6) Keybindings: global, modes, and chords

Use `app.keys()` for global bindings and chord sequences; use `app.modes()` for contextual maps.

```typescript
app.keys({
"ctrl+s": () => save(),
"g g": () => jumpTop(),
"ctrl+x ctrl+s": () => save(),
});

app.modes({
normal: {
i: () => app.setMode("insert"),
"d d": () => deleteLine(),
},
insert: {
escape: () => app.setMode("normal"),
},
});

app.setMode("normal");
```

### 7) Overlays: modals and toasts

Render overlays in `ui.layers(...)`; later children are on top.

```typescript
ui.layers([
MainScreen(state),
state.showConfirm &&
ui.modal({
id: "confirm",
title: "Delete file?",
content: ui.text("This action cannot be undone."),
actions: [
ui.button({ id: "cancel", label: "Cancel" }),
ui.button({ id: "delete", label: "Delete" }),
],
onClose: () => app.update((s) => ({ ...s, showConfirm: false })),
}),
ui.toastContainer({
toasts: state.toasts,
position: "bottom-right",
onDismiss: (id) => app.update((s) => ({ ...s, toasts: s.toasts.filter((t) => t.id !== id) })),
}),
]);
```

### 8) Debugging and inspector

Use the debug controller for traces and the inspector overlay for live frame/focus inspection.

```typescript
import { createAppWithInspectorOverlay, createDebugController } from "@rezi-ui/core";
import { createNodeBackend } from "@rezi-ui/node";

const backend = createNodeBackend();
const debug = createDebugController({
backend: backend.debug,
terminalCapsProvider: () => backend.getCaps(),
});

await debug.enable({ minSeverity: "info" });

const app = createAppWithInspectorOverlay({
backend,
initialState,
inspector: {
hotkey: "ctrl+shift+i",
debug,
},
});
```

### 9) Performance

Use Rezi's deterministic performance model directly:

- Keep `app.view(state)` pure and cheap.
- Keep `key` stable in dynamic lists.
- Precompute expensive derived data during `app.update(...)`.
- Use `ui.virtualList`/`ui.table` for large datasets.
- Turn on debug `perf`/`frame` categories to inspect hotspots.

## Quick migration checklist

1. Replace `render(...)` entrypoint with `createNodeApp` + `app.view` + `app.start`.
2. Move React component local state to app state or `defineWidget` context hooks.
3. Replace `useInput`-style handlers with `app.keys`, `app.modes`, and widget callbacks.
4. Convert Yoga assumptions to cell-based layout (`row`/`column`/`box`, `flex`, cell spacing).
5. Add stable `id` (focus/routing) and stable `key` (list reconciliation).
6. Verify behavior with debug traces and inspector overlay before parity signoff.

## Related docs

- [Composition](../guide/composition.md)
- [Lifecycle & Updates](../guide/lifecycle-and-updates.md)
- [Layout](../guide/layout.md)
- [Input & Focus](../guide/input-and-focus.md)
- [Performance](../guide/performance.md)
- [Debugging](../guide/debugging.md)
39 changes: 31 additions & 8 deletions docs/packages/create-rezi.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,26 @@ Scaffold a new Rezi terminal UI app in seconds.
```bash
npm create rezi my-app
cd my-app
npm start
npm run start

# or bun
# npm create rezi my-app -- --pm bun
# cd my-app
# bun run start
```

## Templates

- `dashboard`: Multi-panel ops dashboard with a status list and activity feed
- `form-app`: Data entry flow with a live preview panel
- `file-browser`: Split view file browser with details + preview
- `streaming-viewer`: Live viewer with chat and stream metrics
- `dashboard`: Live ops dashboard with deterministic table updates.
Highlights: live-updating table with stable row keys, filter/sort/pin controls + incident telemetry.
- `form-app`: Multi-step form with validation and command modes.
Highlights: insert/command key modes with chords, modal help and toast notifications.
- `file-browser`: Explorer with async command palette search.
Highlights: async palette results with cancellation, table browser with details and preview.
- `streaming-viewer`: High-volume stream monitor with virtualized index.
Highlights: virtual list over 15k streams, live ingest feed with follow/pause controls.

Choose a template interactively or pass `--template`:
Choose a template interactively or pass a canonical template name:

```bash
npm create rezi my-app -- --template dashboard
Expand All @@ -26,10 +35,24 @@ npm create rezi my-app -- --template file-browser
npm create rezi my-app -- --template streaming-viewer
```

List templates and highlights from the CLI:

```bash
npm create rezi -- --list-templates
```

## Options

- `--template <name>`: Select a template.
- `--template <dashboard|form-app|file-browser|streaming-viewer>`: Select a template.
- `--no-install`: Skip dependency installation.
- `--pm <npm|pnpm|yarn|bun>`: Choose a package manager.
- `--list-templates`: Print available templates.
- `--list-templates`: Print available templates and highlights.
- `--help`: Show help.

## Template Smoke Check

Run deterministic template smoke checks (metadata consistency + build/typecheck expectations):

```bash
npm run check:create-rezi-templates
```
2 changes: 2 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ nav:
- JSX: getting-started/jsx.md
- Examples: getting-started/examples.md
- FAQ: getting-started/faq.md
- Migration:
- Ink to Rezi: migration/ink-to-rezi.md
- Guides:
- Concepts: guide/concepts.md
- Composition: guide/composition.md
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@
"docs:build": "bash scripts/docs.sh build",
"build:native": "npm -w @rezi-ui/native run build:native",
"test:native:smoke": "npm -w @rezi-ui/native run test:native:smoke",
"check:create-rezi-templates": "node scripts/check-create-rezi-templates.mjs",
"check:core-portability": "node scripts/check-core-portability.mjs",
"check:docs": "npm run docs:build",
"check:unicode": "node scripts/check-unicode-sync.mjs",
"check": "npm run check:core-portability && npm run check:docs && npm run check:unicode",
"check": "npm run check:create-rezi-templates && npm run check:core-portability && npm run check:docs && npm run check:unicode",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Make check runnable without a prior build

Adding check:create-rezi-templates to the top-level check command makes npm run check fail on a fresh checkout, because that script hard-fails unless packages/create-rezi/dist/scaffold.js and packages/{core,node}/dist/index.d.ts already exist (see scripts/check-create-rezi-templates.mjs guards around lines 73-95). In the common local/CI flow where contributors run checks right after install, check now exits before running any real validations unless they manually run a full build first.

Useful? React with 👍 / 👎.

"test": "node scripts/run-tests.mjs",
"test:e2e": "node scripts/run-e2e.mjs",
"test:e2e:reduced": "node scripts/run-e2e.mjs --profile reduced",
Expand Down
Loading
Loading