open-index/open-npm huggingface dataset:
npm package: @matthesketh/ink-pipeline
Description: A multi-step pipeline visualisation component for Ink 5
Latest version: 0.1.0

# @matthesketh/ink-pipeline

A multi-step process/pipeline visualisation component for [Ink 5](https://github.com/vadimdemedes/ink). Shows sequential steps with individual status, progress, timing, and rollback indication. Perfect for deploy workflows, CI pipelines, and setup wizards.

## Install

```sh
npm install @matthesketh/ink-pipeline
```

Peer dependencies: `ink 5.2.1` and `react 18.3.1`.

## Usage

```tsx
import React from 'react';
import { render } from 'ink';
import { Pipeline } from '@matthesketh/ink-pipeline';

function App() {
  return (
    <Pipeline
      title="Deploy Pipeline"
      steps={[
        { label: 'Install deps', status: 'success', duration: 1200 },
        { label: 'Build', status: 'running', progress: 0.45 },
        { label: 'Deploy', status: 'pending' },
        { label: 'Health check', status: 'pending' },
      ]}
    />
  );
}

render(<App />);
```

Output:

```
Deploy Pipeline
[ok] Install deps (1.2s)
 |
[..] Build [====    ] 45%
 |
[ ] Deploy
 |
[ ] Health check
```

## API

### `<Pipeline>`

| Prop           | Type             | Default | Description                                  |
| -------------- | ---------------- | ------- | -------------------------------------------- |
| `steps`        | `PipelineStep[]` | -       | Array of steps to display                    |
| `title`        | `string`         | -       | Optional title shown above the pipeline      |
| `showDuration` | `boolean`        | `true`  | Show elapsed time next to completed steps    |
| `showProgress` | `boolean`        | `true`  | Show progress bar for running steps          |
| `compact`      | `boolean`        | `false` | Compact mode: one line per step, no connectors |

### `PipelineStep`

| Field      | Type         | Description                                |
| ---------- | ------------ | ------------------------------------------ |
| `label`    | `string`     | Step name                                  |
| `status`   | `StepStatus` | Current step status                        |
| `progress` | `number`     | 0-1, shown as percentage when running      |
| `duration` | `number`     | Milliseconds, shown as elapsed time        |
| `output`   | `string`     | Detail text shown below the step           |

### `StepStatus`

`'pending' | 'running' | 'success' | 'error' | 'skipped' | 'rolling-back'`

Status indicators: `(pending)` pending, spinner running, `(success)` success, `(error)` error, `(skipped)` skipped, `(rolling-back)` rolling-back.

## License

MIT
npm package: @matthesketh/ink-radio
Description: A radio button group for single-select in Ink 5
Latest version: 0.1.0

# @matthesketh/ink-radio

A radio button group for single-select in Ink 5.

## Install

```bash
npm install @matthesketh/ink-radio
```

## Usage

```tsx
import React, { useState } from 'react';
import { render } from 'ink';
import { RadioGroup } from '@matthesketh/ink-radio';

const options = [
  { label: 'Small', value: 'sm' },
  { label: 'Medium', value: 'md' },
  { label: 'Large', value: 'lg' },
];

function App() {
  const [value, setValue] = useState('md');
  return <RadioGroup options={options} value={value} onChange={setValue} />;
}

render(<App />);
```

## Props

| Prop        | Type                              | Default      | Description                    |
| ----------- | --------------------------------- | ------------ | ------------------------------ |
| `options`   | `RadioOption[]`                   | **required** | List of options to display     |
| `value`     | `string`                          | `undefined`  | Currently selected value       |
| `onChange`   | `(value: string) => void`        | `undefined`  | Called when selection changes   |
| `direction` | `'vertical' \| 'horizontal'`     | `'vertical'` | Layout direction               |
| `color`     | `string`                          | `'cyan'`     | Colour of the selected option  |

### RadioOption

```ts
interface RadioOption {
  label: string;
  value: string;
  disabled?: boolean;
}
```

## Appearance

Selected options render in bold with the configured colour. Disabled options render dim.

## License

MIT
npm package: @matthesketh/ink-rule
Description: A horizontal divider/separator for Ink 5 with optional centred label
Latest version: 0.1.0

# @matthesketh/ink-rule

A horizontal divider/separator for [Ink 5](https://github.com/vadimdemedes/ink) with optional centred label.

## Install

```sh
npm install @matthesketh/ink-rule
```

## Usage

```tsx
import { Rule } from '@matthesketh/ink-rule';

// simple horizontal rule
<Rule />

// with a centred title
<Rule title="Section" />

// custom character and colour
<Rule char="=" color="cyan" />

// fixed width
<Rule width={40} title="Info" />
```

## Props

| Prop    | Type     | Default                    | Description                      |
| ------- | -------- | -------------------------- | -------------------------------- |
| `title` | `string` |                            | centred label in the rule        |
| `char`  | `string` | `'\u2500'`                 | character used to draw the line  |
| `color` | `string` | `'grey'`                   | colour of the line characters    |
| `width` | `number` | `process.stdout.columns`   | width of the rule in columns     |

## License

MIT
npm package: @matthesketh/ink-scrollable-list
Description: Windowed scrollable list component for Ink 5 with follow-cursor scrolling
Latest version: 0.1.0

# @matthesketh/ink-scrollable-list

Windowed scrollable list component for Ink 5. Renders only visible items, follows the cursor, shows scroll indicators.

## Install

```bash
npm install @matthesketh/ink-scrollable-list
```

## Usage

```tsx
import { ScrollableList } from '@matthesketh/ink-scrollable-list';

function MyList() {
  const [selectedIndex, setSelectedIndex] = useState(0);

  return (
    <ScrollableList
      items={items}
      selectedIndex={selectedIndex}
      maxVisible={15}
      renderItem={(item, selected) => (
        <Text bold={selected} color={selected ? 'cyan' : 'white'}>
          {selected ? '> ' : '  '}{item.name}
        </Text>
      )}
      emptyText="No items found"
    />
  );
}
```

## Props

| Prop | Type | Description |
|------|------|-------------|
| `items` | `T[]` | Array of items to render |
| `selectedIndex` | `number` | Currently selected index |
| `maxVisible` | `number` | Max items visible at once |
| `renderItem` | `(item, selected, index) => ReactNode` | Render function per item |
| `emptyText` | `string?` | Text shown when items is empty |

## Requirements

- Ink >= 5.0.0
- React >= 18.0.0
npm package: @matthesketh/ink-split-pane
Description: Resizable side-by-side or top-bottom panel layout for Ink 5 terminal dashboards
Latest version: 0.1.0

# @matthesketh/ink-split-pane

Resizable side-by-side or top-bottom panel layout for [Ink 5](https://github.com/vadimdemedes/ink).

## Install

```sh
npm install @matthesketh/ink-split-pane
```

## Usage

```tsx
import { SplitPane } from '@matthesketh/ink-split-pane';
import { Text } from 'ink';

// Horizontal (side-by-side)
<SplitPane sizes={[60, 40]}>
  <Text>Left panel</Text>
  <Text>Right panel</Text>
</SplitPane>

// Vertical (top-bottom)
<SplitPane direction="vertical" sizes={[70, 30]}>
  <Text>Top panel</Text>
  <Text>Bottom panel</Text>
</SplitPane>
```

## Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `direction` | `'horizontal' \| 'vertical'` | `'horizontal'` | Layout direction. Horizontal = side by side, vertical = stacked. |
| `sizes` | `[number, number]` | `[50, 50]` | Percentage split between the two panes. |
| `minSize` | `number` | `5` | Minimum columns (horizontal) or rows (vertical) per pane. |
| `showDivider` | `boolean` | `true` | Whether to render the divider between panes. |
| `dividerChar` | `string` | `'\u2502'` / `'\u2500'` | Character used to draw the divider. |
| `dividerColor` | `string` | `'gray'` | Ink color for the divider. |
| `children` | `[ReactNode, ReactNode]` | required | Exactly two children. |

## License

MIT
npm package: @matthesketh/ink-status-bar
Description: Fixed-position status bar component for Ink 5 with key hints, mode indicators, and slot layout
Latest version: 0.1.0

# @matthesketh/ink-status-bar

A fixed-position status bar component for [Ink 5](https://github.com/vadimdemedes/ink). Renders a single-row bar at the bottom of your terminal UI with key hints, mode indicators, and context info -- the kind you see in vim, htop, or nano.

## Install

```bash
npm install @matthesketh/ink-status-bar
```

## Usage

```tsx
import React from 'react';
import { render, Text } from 'ink';
import { StatusBar } from '@matthesketh/ink-status-bar';

function App() {
  return (
    <>
      {/* your app content */}
      <StatusBar
        items={[
          { key: 'q', label: 'quit' },
          { key: 'Tab', label: 'switch view' },
          { key: '?', label: 'help' },
        ]}
      />
    </>
  );
}

render(<App />);
```

### With slots

```tsx
<StatusBar
  left={<Text bold>INSERT</Text>}
  center={<Text>index.ts</Text>}
  right={<Text>3/10</Text>}
  backgroundColor="blue"
  color="white"
/>
```

### Key hints with a mode indicator

```tsx
<StatusBar
  left={<Text bold>NORMAL</Text>}
  items={[
    { key: 'i', label: 'insert' },
    { key: ':', label: 'command' },
  ]}
  right={<Text>Ln 42, Col 8</Text>}
/>
```

## Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `items` | `KeyHint[]` | `undefined` | Key hints displayed as `[key] label` pairs. Placed in left slot if no `left` prop, otherwise in center. |
| `left` | `React.ReactNode` | `undefined` | Content aligned to the left (e.g. mode indicator). |
| `center` | `React.ReactNode` | `undefined` | Content aligned to the center (e.g. filename). |
| `right` | `React.ReactNode` | `undefined` | Content aligned to the right (e.g. position info). |
| `backgroundColor` | `string` | `'gray'` | Background colour for the entire bar. |
| `color` | `string` | `'white'` | Default text colour. |

### KeyHint

```ts
interface KeyHint {
  key: string;    // e.g. "q", "Tab", "↑↓"
  label: string;  // e.g. "quit", "switch view"
}
```

## Requirements

- Node.js >= 18
- React >= 18.0.0
- Ink >= 5.0.0
npm package: @matthesketh/ink-switch
Description: An on/off toggle switch control for Ink 5 terminal UIs
Latest version: 0.1.0

# @matthesketh/ink-switch

An on/off toggle switch control for Ink 5 terminal UIs.

## Install

```
npm install @matthesketh/ink-switch
```

## Usage

```tsx
import { Switch } from '@matthesketh/ink-switch';

<Switch value={isOn} onChange={setIsOn} label="Wi-Fi" />
```

## Props

| Prop       | Type                      | Default   | Description                  |
|------------|---------------------------|-----------|------------------------------|
| `value`    | `boolean`                 | required  | Whether the switch is on     |
| `onChange`  | `(value: boolean) => void` | ---      | Called when toggled           |
| `label`    | `string`                  | ---       | Label shown after the toggle |
| `onLabel`  | `string`                  | `'ON'`    | Text shown when on           |
| `offLabel` | `string`                  | `'OFF'`   | Text shown when off          |
| `onColor`  | `string`                  | `'green'` | Ink color when on            |
| `offColor` | `string`                  | `'red'`   | Ink color when off           |
| `disabled` | `boolean`                 | `false`   | Dims the switch              |

## Visual

```
(*)--  ON   (green, on)
--(*)  OFF  (red, off)
```
npm package: @matthesketh/ink-table
Description: Modern table component for Ink 5 with keyboard navigation, alignment, sorting indicators, and truncation
Latest version: 0.1.0

# @matthesketh/ink-table

Modern table component for [Ink 5](https://github.com/vadimdemedes/ink) with column alignment, truncation, row selection highlighting, and windowed scrolling.

The existing `ink-table` on npm targets Ink 3.x and is no longer maintained. This package is a ground-up replacement for Ink 5.

## Install

```bash
npm install @matthesketh/ink-table
```

## Usage

```tsx
import React, { useState } from 'react';
import { render } from 'ink';
import { Table, type Column } from '@matthesketh/ink-table';

interface Service {
  name: string;
  status: string;
  cpu: number;
}

const columns: Column<Service>[] = [
  { key: 'name', header: 'Service', width: 20 },
  { key: 'status', header: 'Status' },
  { key: 'cpu', header: 'CPU %', align: 'right', width: 8 },
];

const data: Service[] = [
  { name: 'api-gateway', status: 'running', cpu: 12.4 },
  { name: 'auth-service', status: 'running', cpu: 3.1 },
  { name: 'worker', status: 'stopped', cpu: 0 },
];

function App() {
  const [selected, setSelected] = useState(0);

  return (
    <Table
      data={data}
      columns={columns}
      selectedIndex={selected}
      maxVisible={10}
      borderStyle="single"
    />
  );
}

render(<App />);
```

## Props

### `TableProps<T>`

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `data` | `T[]` | *required* | Array of row objects |
| `columns` | `Column<T>[]` | *required* | Column definitions |
| `selectedIndex` | `number` | `undefined` | Index of highlighted row (bold + cyan). Omit for no selection. |
| `maxVisible` | `number` | `undefined` | Maximum visible rows. Enables windowed scrolling with indicators. |
| `emptyText` | `string` | `'No data'` | Text shown when `data` is empty |
| `borderStyle` | `'single' \| 'none'` | `'single'` | `'single'` draws box-drawing separators; `'none'` uses spaces |

### `Column<T>`

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `key` | `keyof T & string` | *required* | Property name to read from each row |
| `header` | `string` | *required* | Column header text |
| `width` | `number` | auto | Fixed column width. Auto-calculated from content if omitted (capped at 30). |
| `align` | `'left' \| 'right' \| 'center'` | `'left'` | Text alignment within the column |
| `render` | `(value, row) => ReactNode` | `undefined` | Custom cell renderer |

## Features

- **Auto column widths** -- calculated from header and data content, capped at 30 characters
- **Truncation** -- cells exceeding column width are truncated with an ellipsis character
- **Alignment** -- left, right, or center alignment per column
- **Row selection** -- highlighted row rendered in bold cyan
- **Windowed scrolling** -- follow-cursor algorithm with scroll indicators when `maxVisible` is set
- **Custom renderers** -- per-column `render` function for custom cell content
- **Border styles** -- box-drawing characters or plain spaces

## Requirements

- Node.js >= 20
- Ink >= 5.0.0
- React >= 18.0.0
npm package: @matthesketh/ink-tabs
Description: A tab bar component for Ink 5 with active indicator, badges, and keyboard navigation
Latest version: 0.1.0

# @matthesketh/ink-tabs

A tab bar component for [Ink 5](https://github.com/vadimdemedes/ink). Shows labelled tabs with an active indicator, optional badges, and keyboard navigation support via parent `onChange`.

## Install

```bash
npm install @matthesketh/ink-tabs
```

## Usage

```tsx
import { Tabs } from '@matthesketh/ink-tabs';

const tabs = [
  { id: 'home', label: 'Home' },
  { id: 'logs', label: 'Logs', badge: 3 },
  { id: 'settings', label: 'Settings' },
];

function App() {
  const [activeId, setActiveId] = useState('home');

  return <Tabs tabs={tabs} activeId={activeId} onChange={setActiveId} />;
}
```

## Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `tabs` | `Tab[]` | required | Array of tab objects |
| `activeId` | `string` | required | ID of the currently active tab |
| `onChange` | `(id: string) => void` | — | Called when a tab should change (parent handles input) |
| `accentColor` | `string` | `'cyan'` | Colour for the active tab |
| `separator` | `string` | `' \| '` | String rendered between tabs |

### Tab

| Field | Type | Description |
|-------|------|-------------|
| `id` | `string` | Unique identifier |
| `label` | `string` | Display text |
| `badge` | `string \| number` | Optional badge shown as `(N)` after label |

## License

MIT
npm package: @matthesketh/ink-task-list
Description: A step/task progress display component for Ink 5
Latest version: 0.1.0

# @matthesketh/ink-task-list

A step/task progress display component for [Ink 5](https://github.com/vadimdemedes/ink). Shows a list of tasks with status indicators (spinner, checkmark, error, pending).

## Install

```sh
npm install @matthesketh/ink-task-list
```

## Usage

```tsx
import React from 'react';
import { render } from 'ink';
import { TaskList } from '@matthesketh/ink-task-list';

function App() {
  return (
    <TaskList
      tasks={[
        { label: 'Install dependencies', status: 'success' },
        { label: 'Build project', status: 'running' },
        { label: 'Run tests', status: 'pending' },
      ]}
    />
  );
}

render(<App />);
```

## API

### `<TaskList>`

| Prop         | Type     | Default | Description                        |
|--------------|----------|---------|------------------------------------|
| `tasks`      | `Task[]` | —       | Array of tasks to display          |
| `showOutput` | `boolean`| `true`  | Whether to show task output lines  |

### `Task`

| Property | Type         | Description                            |
|----------|--------------|----------------------------------------|
| `label`  | `string`     | Task label text                        |
| `status` | `TaskStatus` | One of: `pending`, `running`, `success`, `error`, `skipped` |
| `output` | `string?`    | Optional output shown below the task   |

### Status indicators

| Status    | Indicator | Style      |
|-----------|-----------|------------|
| `pending` | `○`       | dim        |
| `running` | spinner   | cyan, bold label |
| `success` | `✓`       | green      |
| `error`   | `✗`       | red        |
| `skipped` | `–`       | dim        |

## License

MIT
npm package: @matthesketh/ink-textarea
Description: Multi-line text input component for Ink 5 with cursor movement and scrolling
Latest version: 0.1.0

# @matthesketh/ink-textarea

A multi-line text input component for [Ink 5](https://github.com/vadimdemedes/ink) with cursor movement and scrolling.

`ink-text-input` is single-line only -- this fills the gap.

## Install

```sh
npm install @matthesketh/ink-textarea
```

## Usage

```tsx
import React, { useState } from 'react';
import { render } from 'ink';
import { TextArea } from '@matthesketh/ink-textarea';

function App() {
  const [value, setValue] = useState('');

  return (
    <TextArea
      value={value}
      onChange={setValue}
      onSubmit={(text) => console.log('Submitted:', text)}
      height={10}
      placeholder="Type your message..."
    />
  );
}

render(<App />);
```

## Props

| Prop         | Type                        | Default | Description                                |
| ------------ | --------------------------- | ------- | ------------------------------------------ |
| `value`      | `string`                    |         | Current text value (controlled)            |
| `onChange`    | `(value: string) => void`   |         | Called when text changes                   |
| `onSubmit`   | `(value: string) => void`   |         | Called on Ctrl+Enter or Ctrl+S             |
| `height`     | `number`                    | `5`     | Visible rows                               |
| `placeholder`| `string`                    |         | Shown when value is empty                  |
| `focus`      | `boolean`                   | `true`  | Whether the component accepts input        |
| `showCursor` | `boolean`                   | `true`  | Show cursor position as inverted character |

## Key Bindings

- **Arrow keys** -- move cursor up/down/left/right
- **Enter** -- insert newline
- **Ctrl+Enter** or **Ctrl+S** -- trigger `onSubmit`
- **Backspace** -- delete character before cursor
- **Tab** -- insert two spaces

## License

MIT
npm package: @matthesketh/ink-timeline
Description: Chronological event timeline display for Ink 5
Latest version: 0.1.0

# @matthesketh/ink-timeline

Chronological event timeline display for Ink 5. Shows events with timestamps, type badges, and descriptions -- ideal for deploy history, incident logs, audit trails.

## Install

```bash
npm install @matthesketh/ink-timeline
```

## Usage

```tsx
import React from 'react';
import { render } from 'ink';
import { Timeline } from '@matthesketh/ink-timeline';

const events = [
  { time: '10:00', title: 'Deployed v1.2.3', type: 'deploy' },
  { time: '10:15', title: 'Service restarted', type: 'restart' },
  { time: '10:30', title: 'CPU spike detected', type: 'alert', description: 'CPU usage above 90%' },
];

render(<Timeline events={events} />);
```

## Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `events` | `TimelineEvent[]` | required | Array of events to display |
| `maxVisible` | `number` | `undefined` | Limit visible events with scroll indicators |
| `showRelativeTime` | `boolean` | `false` | Show "2m ago" style timestamps |

### TimelineEvent

| Field | Type | Description |
|-------|------|-------------|
| `time` | `string \| Date` | Displayed timestamp |
| `type` | `string` | Badge label (e.g. deploy, restart, alert) |
| `typeColor` | `string` | Override badge colour |
| `title` | `string` | Event title |
| `description` | `string` | Detail shown below title |

### Auto-colours

| Type | Colour |
|------|--------|
| deploy | green |
| restart | yellow |
| alert | red |
| error | red |
| info | blue |

## License

MIT
npm package: @matthesketh/ink-toast
Description: Timed ephemeral toast notifications for Ink 5 apps
Latest version: 0.1.0

# @matthesketh/ink-toast

Timed ephemeral toast notifications for Ink 5 apps. Like toast notifications in web apps, but for the terminal.

## Install

```bash
npm install @matthesketh/ink-toast
```

## Usage

```tsx
import React from 'react';
import { render, Text } from 'ink';
import { ToastProvider, ToastContainer, useToast } from '@matthesketh/ink-toast';

function App() {
  const { toast } = useToast();

  React.useEffect(() => {
    toast('Deployment successful!', 'success');
    toast('Check your config', 'warning', 5000);
  }, []);

  return (
    <>
      <Text>My App</Text>
      <ToastContainer />
    </>
  );
}

render(
  <ToastProvider maxToasts={3}>
    <App />
  </ToastProvider>
);
```

## API

### `<ToastProvider>`

Wraps your app and provides toast context to all children.

| Prop       | Type     | Default | Description                        |
|------------|----------|---------|------------------------------------|
| children   | ReactNode | -      | Child components                   |
| maxToasts  | number   | 3       | Maximum visible toasts at once     |

### `useToast()`

Hook that returns a `toast()` function for creating toasts from any component inside `<ToastProvider>`.

```ts
const { toast } = useToast();
toast(message: string, type?: 'success' | 'error' | 'info' | 'warning', duration?: number): void
```

- `message` — text to display
- `type` — determines colour and icon (default: `'info'`)
- `duration` — auto-dismiss delay in ms (default: `3000`)

### `<ToastContainer />`

Renders the visible toasts. Place at the bottom of your app layout.

### Toast types

| Type    | Colour | Icon           |
|---------|--------|----------------|
| success | green  | U+2713 (check) |
| error   | red    | U+2717 (cross) |
| info    | blue   | U+2139 (info)  |
| warning | yellow | U+26A0 (warn)  |

### `Toast` interface

```ts
interface Toast {
  id: string;
  message: string;
  type: 'success' | 'error' | 'info' | 'warning';
  duration: number;
}
```

## Requirements

- Node.js >= 18
- Ink >= 5.0.0
- React >= 18.0.0

## Licence

MIT
npm package: @matthesketh/ink-tree
Description: Collapsible tree view component for Ink 5 with keyboard navigation and custom rendering
Latest version: 0.1.0

# @matthesketh/ink-tree

A collapsible tree view component for [Ink 5](https://github.com/vadimdemedes/ink). Displays hierarchical data with expand/collapse, keyboard navigation, and custom rendering.

## Install

```bash
npm install @matthesketh/ink-tree
```

## Usage

```tsx
import React, { useState } from 'react';
import { render } from 'ink';
import { Tree, type TreeNode } from '@matthesketh/ink-tree';

const data: TreeNode[] = [
  {
    id: 'src',
    label: 'src',
    children: [
      { id: 'index', label: 'index.ts' },
      {
        id: 'components',
        label: 'components',
        children: [
          { id: 'app', label: 'App.tsx' },
          { id: 'header', label: 'Header.tsx' },
        ],
      },
    ],
  },
];

function App() {
  const [expanded, setExpanded] = useState(new Set(['src', 'components']));
  const [selected, setSelected] = useState('index');

  return (
    <Tree
      nodes={data}
      expandedIds={expanded}
      selectedId={selected}
      maxVisible={10}
    />
  );
}

render(<App />);
```

## API

### `TreeNode`

```ts
interface TreeNode {
  id: string;
  label: string;
  children?: TreeNode[];
  data?: unknown;
}
```

### `TreeProps`

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `nodes` | `TreeNode[]` | required | Tree data |
| `selectedId` | `string` | - | Highlighted node id |
| `expandedIds` | `Set<string>` | `new Set()` | Which nodes are expanded |
| `maxVisible` | `number` | - | Windowed display limit |
| `renderNode` | `(node, depth, selected, expanded) => ReactNode` | default renderer | Custom node renderer |
| `indent` | `number` | `2` | Spaces per indentation level |

### `flattenTree`

```ts
function flattenTree(nodes: TreeNode[], expandedIds: Set<string>): FlatNode[];
```

Utility to flatten a tree into a visible list based on expanded state. Useful for building custom tree implementations.

## Visual

Collapsed tree shows indicator arrows for expandable nodes and dot for leaves, with tree connector lines for hierarchy.

## License

MIT
npm package: @matthesketh/ink-viewport
Description: Terminal size hook and viewport-aware layout for Ink 5
Latest version: 0.1.0

# @matthesketh/ink-viewport

Terminal size hook and viewport-aware layout for Ink 5 apps.

## Install

```bash
npm install @matthesketh/ink-viewport
```

## Usage

### useTerminalSize()

```tsx
import { useTerminalSize } from '@matthesketh/ink-viewport';

function MyComponent() {
  const { rows, columns } = useTerminalSize();
  return <Text>{columns}x{rows}</Text>;
}
```

### Viewport + useAvailableHeight()

```tsx
import { Viewport, useAvailableHeight } from '@matthesketh/ink-viewport';

function App() {
  return (
    <Viewport chrome={4}>
      <ScrollableContent />
    </Viewport>
  );
}

function ScrollableContent() {
  const height = useAvailableHeight();
  return <Box height={height}>...</Box>;
}
```

## Requirements

- Ink >= 5.0.0
- React >= 18.0.0
npm package: @matthesketh/react-guidetour
Description: React 19 guided tour component. Modernised fork of react-joyride by Gil Barbara.
Author: Matt Hesketh
Latest version: 1.1.1

# @matthesketh/react-guidetour

React 19 guided tour component. A modernised fork of [react-joyride](https://github.com/gilbarbara/react-joyride) by Gil Barbara.

## Why this fork?

react-joyride v2.9.3 uses `ReactDOM.unmountComponentAtNode()` and `ReactDOM.unstable_renderSubtreeIntoContainer()`, both removed in React 19. This fork:

- Converts all class components to functional components with hooks
- Uses `createPortal` directly (no legacy React 15/16 branching)
- Removes legacy browser detection (IE/Edge legacy)
- Targets React 19+ only

## Install

```bash
npm install @matthesketh/react-guidetour
```

## Usage

The API is the same as react-joyride. See the [react-joyride documentation](https://docs.react-joyride.com/) for full usage details.

```tsx
import Joyride from '@matthesketh/react-guidetour';

const steps = [
  {
    target: '.my-first-step',
    content: 'This is my first step!',
  },
];

function App() {
  return <Joyride steps={steps} />;
}
```

## Attribution

Original work by [Gil Barbara](https://github.com/gilbarbara) under the MIT License.
npm package: @matthesketh/utopia-ai
Description: AI adapters and MCP support for UtopiaJS
Author: Matt
Latest version: 0.7.1

# @matthesketh/utopia-ai

AI adapters and MCP support for UtopiaJS. Unified interface for chat completions, streaming, embeddings, and agentic tool loops. Built-in support for OpenAI, Anthropic, Google Gemini, and Ollama. Includes a full MCP (Model Context Protocol) server and client implementation.

## Install

```bash
pnpm add @matthesketh/utopia-ai
```

Install a provider SDK as needed:

```bash
pnpm add openai              # for OpenAI
pnpm add @anthropic-ai/sdk   # for Anthropic
pnpm add @google/generative-ai  # for Google Gemini
# Ollama requires no extra dependency
```

## Usage

```ts
import { createAI } from '@matthesketh/utopia-ai';
import { openaiAdapter } from '@matthesketh/utopia-ai/openai';

const ai = createAI(openaiAdapter({
  apiKey: process.env.OPENAI_API_KEY!,
}));

// Chat
const res = await ai.chat({
  messages: [{ role: 'user', content: 'Hello!' }],
});

// Streaming
for await (const chunk of ai.stream({ messages })) {
  process.stdout.write(chunk.delta);
}

// Agentic tool loop
const result = await ai.run({
  messages: [{ role: 'user', content: 'What is the weather?' }],
  tools: [{
    definition: { name: 'get_weather', description: '...', parameters: { type: 'object', properties: {} } },
    handler: async ({ city }) => ({ temp: 72 }),
  }],
});
```

## API

| Export | Description |
|--------|-------------|
| `createAI(adapter, options?)` | Create an AI instance with hooks and retry |
| `streamSSE(res, stream)` | Stream chat chunks as Server-Sent Events |
| `parseSSEStream(response)` | Parse SSE events from a fetch Response (browser) |
| `collectStream(stream)` | Collect a stream into a single string |

**Adapters:** `@matthesketh/utopia-ai/openai`, `@matthesketh/utopia-ai/anthropic`, `@matthesketh/utopia-ai/google`, `@matthesketh/utopia-ai/ollama`.

**MCP (`@matthesketh/utopia-ai/mcp`):** `createMCPServer`, `createMCPClient`, `createMCPHandler`.

See [docs/ai.md](../../docs/ai.md) for full documentation on adapters, streaming, tool calling, MCP, and type reference.

## License

MIT
npm package: @matthesketh/utopia-cli
Description: CLI for the UtopiaJS framework
Author: Matt
Latest version: 0.7.4

# @matthesketh/utopia-cli

CLI for the UtopiaJS framework. Wraps Vite and auto-injects the UtopiaJS plugin when no `vite.config` exists.

## Install

```bash
pnpm add -D @matthesketh/utopia-cli
```

Or scaffold a new project (includes the CLI automatically):

```bash
npx create-utopia my-app
```

## Commands

```bash
utopia dev        # Start development server
utopia build      # Build for production
utopia preview    # Preview production build
utopia test       # Run component tests (vitest + <test> block extraction)
utopia create     # Prints instructions for npx create-utopia
```

## Options

| Flag | Description |
|------|-------------|
| `--port <port>` | Specify port (dev/preview) |
| `--host [host]` | Expose to network |
| `--open` | Open browser on startup |
| `--outDir <dir>` | Output directory (build) |
| `-c, --config <file>` | Use a specific Vite config file |
| `-h, --help` | Show help |
| `-v, --version` | Show version |

## How It Works

- If no `vite.config.{ts,js,mjs,mts}` exists, the CLI auto-injects `@matthesketh/utopia-vite-plugin`
- `utopia test` auto-injects the `utopiaTestPlugin` from `@matthesketh/utopia-test/plugin`, which extracts `<test>` blocks from `.utopia` files into vitest-discoverable companion files
- All commands pass through to Vite under the hood

## License

MIT
npm package: @matthesketh/utopia-compiler
Description: Compiler for .utopia single-file components
Author: Matt
Latest version: 0.7.3

# @matthesketh/utopia-compiler

Compiler for `.utopia` single-file components. Parses SFC blocks (template, script, style), compiles templates to direct DOM operations, and applies scoped CSS.

## Install

```bash
pnpm add @matthesketh/utopia-compiler
```

## Usage

```ts
import { compile } from '@matthesketh/utopia-compiler';

const source = `
<template>
  <div>{{ count() }}</div>
</template>

<script>
import { signal } from '@matthesketh/utopia-core'
const count = signal(0)
</script>

<style scoped>
div { color: blue; }
</style>
`;

const { code, css } = compile(source, { filename: 'Counter.utopia' });
// code: compiled JS module with render function
// css:  scoped CSS with data attributes
```

## API

| Export | Description |
|--------|-------------|
| `compile(source, options?)` | Compile a `.utopia` SFC source string to `{ code, css }`. |
| `parse(source, filename?)` | Parse SFC into a descriptor with template, script, and style blocks. |
| `compileTemplate(source, options?)` | Compile a template string to a render function module. |
| `compileStyle(options)` | Compile and scope CSS with data attribute selectors. |
| `generateScopeId(filename)` | Generate a deterministic scope ID from a filename. |

See [docs/architecture.md](../../docs/architecture.md) for the full compilation pipeline.

## License

MIT
npm package: @matthesketh/utopia-core
Description: Fine-grained signals reactivity system for UtopiaJS
Author: Matt
Latest version: 0.7.1

# @matthesketh/utopia-core

Fine-grained signals reactivity system for UtopiaJS.

## Install

```bash
pnpm add @matthesketh/utopia-core
```

## Usage

```ts
import { signal, computed, effect, batch, untrack } from '@matthesketh/utopia-core';

// Writable signal
const count = signal(0);
count();          // read (tracked)
count.value;      // read (tracked)
count.peek();     // read (untracked)
count.set(1);     // write
count.update(n => n + 1); // write via callback

// Lazy computed
const double = computed(() => count() * 2);
double();         // 4

// Reactive effect
const dispose = effect(() => {
  console.log('count is', count());
  return () => console.log('cleaning up');
});
dispose(); // stop watching

// Batch multiple writes
batch(() => {
  count.set(10);
  count.set(20);
}); // effects run once after batch

// Untracked reads
effect(() => {
  const tracked = count();
  const untracked_val = untrack(() => count());
});
```

## API

| Export | Description |
|--------|-------------|
| `signal(value)` | Writable reactive cell. Read via `()` or `.value`, write via `.set()` or `.update()`. |
| `computed(fn)` | Lazy derived value. Recomputes only when dependencies change and the value is read. |
| `effect(fn)` | Eager side-effect. Re-runs when dependencies change. Returns a dispose function. |
| `batch(fn)` | Groups multiple writes -- effects only run once after the batch completes. |
| `untrack(fn)` | Reads signals inside `fn` without creating dependency subscriptions. |

See [docs/architecture.md](../../docs/architecture.md) for full details on the reactivity system.

## License

MIT
npm package: @matthesketh/utopia-email
Description: Template-based email rendering for UtopiaJS
Author: Matt
Latest version: 0.7.0

# @matthesketh/utopia-email

Template-based email rendering for UtopiaJS. Render `.utopia` components to email-safe HTML with CSS inlining and plain text fallback. Adapter pattern for sending via SMTP, Resend, or SendGrid.

## Install

```bash
pnpm add @matthesketh/utopia-email
```

Install a provider SDK as needed:

```bash
pnpm add nodemailer     # for SMTP
pnpm add resend         # for Resend
pnpm add @sendgrid/mail # for SendGrid
```

## Usage

```ts
import { createMailer } from '@matthesketh/utopia-email';
import { smtpAdapter } from '@matthesketh/utopia-email/smtp';
import WelcomeEmail from './emails/Welcome.utopia';

const mailer = createMailer(smtpAdapter({
  host: 'smtp.example.com',
  port: 587,
  auth: { user: 'user', pass: 'pass' },
}));

await mailer.send({
  to: 'user@example.com',
  from: 'noreply@example.com',
  subject: 'Welcome!',
  component: WelcomeEmail,
  props: { name: 'Alice' },
});
```

## API

| Export | Description |
|--------|-------------|
| `createMailer(adapter)` | Create a mailer instance |
| `renderEmail(component, props?, options?)` | Render to `{ html, text, subject }` without sending |
| `inlineCSS(html, css)` | Inline CSS into `style=""` attributes |
| `htmlToText(html)` | Convert HTML to plain text |

**Built-in components:** `EmailLayout`, `EmailButton`, `EmailCard`, `EmailDivider`, `EmailHeading`, `EmailText`, `EmailImage`, `EmailColumns`, `EmailSpacer`.

**Adapters:** `@matthesketh/utopia-email/smtp`, `@matthesketh/utopia-email/resend`, `@matthesketh/utopia-email/sendgrid`.

See [docs/email.md](../../docs/email.md) for the full rendering pipeline, component reference, and type documentation.

## License

MIT
npm package: @matthesketh/utopia-router
Description: File-based routing for UtopiaJS
Author: Matt
Latest version: 0.7.7

# @matthesketh/utopia-router

File-based routing for UtopiaJS with History API, navigation guards, and reactive route state. SvelteKit-style file conventions (`+page.utopia`, `+layout.utopia`, `[param]`, `[...rest]`, `(group)`).

## Install

```bash
pnpm add @matthesketh/utopia-router
```

## Usage

```ts
import { createRouter, currentRoute, navigate } from '@matthesketh/utopia-router';
import { buildRouteTable } from '@matthesketh/utopia-router';

const routes = buildRouteTable(routeManifest);
createRouter(routes);

// Reactive route state
console.log(currentRoute().pathname);

// Programmatic navigation
navigate('/about');
```

## File Conventions

| File Path | URL Pattern |
|-----------|-------------|
| `src/routes/+page.utopia` | `/` |
| `src/routes/about/+page.utopia` | `/about` |
| `src/routes/blog/[slug]/+page.utopia` | `/blog/:slug` |
| `src/routes/[...rest]/+page.utopia` | `/*rest` (catch-all) |
| `src/routes/(auth)/login/+page.utopia` | `/login` (route group) |
| `src/routes/+layout.utopia` | Layout wrapper |
| `src/routes/+error.utopia` | Error boundary |

## API

**Route matching:**

| Export | Description |
|--------|-------------|
| `filePathToRoute(path)` | Convert a file path to a route pattern |
| `compilePattern(pattern)` | Compile a route pattern to a regex matcher |
| `matchRoute(routes, url)` | Match a URL against a route table |
| `buildRouteTable(manifest)` | Build a route table from a file manifest |

**Client-side router:**

| Export | Description |
|--------|-------------|
| `createRouter(routes)` | Initialize the client router |
| `navigate(url)` | Programmatic navigation |
| `back()` | Go back in history |
| `forward()` | Go forward in history |
| `beforeNavigate(hook)` | Register a navigation guard |
| `destroy()` | Tear down the router |
| `currentRoute` | Reactive signal with the current route |
| `isNavigating` | Reactive signal indicating navigation in progress |

**Components:**

| Export | Description |
|--------|-------------|
| `createRouterView()` | Render the matched route component |
| `createLink(props)` | Create a client-side navigation link |

See [docs/architecture.md](../../docs/architecture.md) for full routing details.

## License

MIT
npm package: @matthesketh/utopia-runtime
Description: DOM renderer and component lifecycle for UtopiaJS
Author: Matt
Latest version: 0.7.3

# @matthesketh/utopia-runtime

DOM renderer, directives, component lifecycle, scheduler, and hydration for UtopiaJS. This is the client-side runtime that compiled `.utopia` components import from.

## Install

```bash
pnpm add @matthesketh/utopia-runtime
```

## Usage

```ts
import { mount } from '@matthesketh/utopia-runtime';
import App from './App.utopia';

mount(App, '#app');
```

For hydrating server-rendered HTML:

```ts
import { hydrate } from '@matthesketh/utopia-runtime';
import App from './App.utopia';

hydrate(App, '#app');
```

## API

**DOM helpers** (used by compiled template output):

| Export | Description |
|--------|-------------|
| `createElement(tag)` | Create a DOM element |
| `createTextNode(text)` | Create a text node |
| `createComment(text)` | Create a comment node |
| `setText(node, text)` | Set text content |
| `setAttr(el, name, value)` | Set an attribute |
| `addEventListener(el, event, handler)` | Attach an event listener |
| `appendChild(parent, child)` | Append a child node |
| `insertBefore(parent, node, ref)` | Insert before a reference node |
| `removeNode(node)` | Remove a node from the DOM |

**Directives** (used by compiled control-flow):

| Export | Description |
|--------|-------------|
| `createIf(anchor, cond, trueBranch, falseBranch?)` | Conditional rendering |
| `createFor(anchor, list, renderFn)` | List rendering |
| `createComponent(def, props?)` | Component instantiation |

**Lifecycle:**

| Export | Description |
|--------|-------------|
| `mount(component, target)` | Mount a component to the DOM |
| `createComponentInstance(def, props?)` | Create a component instance |
| `hydrate(component, target)` | Hydrate server-rendered HTML |

**Scheduler:**

| Export | Description |
|--------|-------------|
| `queueJob(fn)` | Queue a microtask job |
| `nextTick()` | Wait for the next flush |

**Re-exports from `@matthesketh/utopia-core`:** `signal`, `computed`, `effect`, `batch`, `untrack`, `createEffect`.

See [docs/architecture.md](../../docs/architecture.md) and [docs/ssr.md](../../docs/ssr.md) for full details.

## License

MIT
npm package: @matthesketh/utopia-server
Description: Server-side rendering for UtopiaJS
Author: Matt
Latest version: 0.7.1

# @matthesketh/utopia-server

Server-side rendering for UtopiaJS. Provides `renderToString`, `renderToStream`, server-side routing, and an HTTP request handler. The SSR runtime is a drop-in replacement for `@matthesketh/utopia-runtime` that builds VNode trees instead of real DOM.

## Install

```bash
pnpm add @matthesketh/utopia-server
```

## Usage

```ts
import { renderToString } from '@matthesketh/utopia-server';
import App from './App.utopia';

const { html, css } = renderToString(App, { title: 'Hello' });
```

Streaming:

```ts
import { renderToStream } from '@matthesketh/utopia-server';
import App from './App.utopia';

const stream = renderToStream(App);
stream.pipe(res);
```

## API

| Export | Description |
|--------|-------------|
| `renderToString(component, props?)` | Render a component to `{ html, css }` synchronously. |
| `renderToStream(component, props?)` | Render to a Node.js `Readable` stream. |
| `createServerRouter(routes, url)` | Match a URL against routes on the server. |
| `createHandler(options)` | Create a Node.js HTTP request handler for SSR. |

**Subpath export:** `@matthesketh/utopia-server/ssr-runtime` -- drop-in replacement for `@matthesketh/utopia-runtime` used during SSR builds. The Vite plugin handles this swap automatically.

**VNode types:** `VElement`, `VText`, `VComment`, `VNode`.

See [docs/ssr.md](../../docs/ssr.md) for the full SSR architecture, hydration details, and setup guide.

## License

MIT
npm package: @matthesketh/utopia-test
Description: Testing utilities for UtopiaJS components
Author: Matt
Latest version: 0.7.0

# @matthesketh/utopia-test

Testing utilities for UtopiaJS components. Mount components in jsdom, query the DOM, and simulate events.

## Install

```bash
pnpm add -D @matthesketh/utopia-test vitest
```

## Usage

Write tests inside `<test>` blocks in your `.utopia` files:

```html
<template>
  <button @click="increment">Count: {{ count() }}</button>
</template>

<script>
import { signal } from '@matthesketh/utopia-core';
const count = signal(0);
const increment = () => count.set(count() + 1);
</script>

<test>
import { describe, it, expect } from 'vitest';
import { render, fireEvent, nextTick } from '@matthesketh/utopia-test';

describe('Counter', () => {
  it('increments on click', async () => {
    const { getByText } = render(self);
    const btn = getByText('Count: 0');
    fireEvent.click(btn);
    await nextTick();
    expect(btn.textContent).toBe('Count: 1');
  });
});
</test>
```

Run with:

```bash
utopia test
```

The `self` import is auto-generated — it refers to the component defined in the same file.

## API

### `mount(definition, options?)`

Mount a component into the DOM.

```ts
const { container, component, unmount } = mount(MyComponent, {
  props: { title: 'Hello' },
});
```

**Returns:** `{ container, component, unmount }`

### `render(definition, options?)`

Mount a component with query helpers.

```ts
const { getBySelector, getAllBySelector, getByText, unmount } = render(MyComponent);
```

**Returns:** Everything from `mount()` plus:

| Helper | Description |
|--------|-------------|
| `getBySelector(css)` | Query one element by CSS selector (throws if not found) |
| `getAllBySelector(css)` | Query all matching elements |
| `getByText(text \| RegExp)` | Find element by text content (throws if not found) |

### `fireEvent`

Simulate DOM events:

```ts
fireEvent.click(element);
fireEvent.input(element);
fireEvent.change(element);
fireEvent.submit(element);
fireEvent.keydown(element, { key: 'Enter' });
fireEvent.keyup(element);
fireEvent.focus(element);
fireEvent.blur(element);
fireEvent.custom(element, 'my-event', { detail: 42 });
```

### `nextTick()`

Wait for pending signal effects to flush. Re-exported from `@matthesketh/utopia-runtime`.

```ts
count.set(5);
await nextTick();
expect(el.textContent).toBe('5');
```

## Vitest Plugin

The package also exports a Vite/Vitest plugin at `@matthesketh/utopia-test/plugin`:

```ts
import { utopiaTestPlugin } from '@matthesketh/utopia-test/plugin';
```

This plugin extracts `<test>` blocks from `.utopia` files and generates companion `.utopia.test.ts` files that vitest discovers automatically. Generated files are cleaned up after the test run.

The `utopia test` CLI command injects this plugin automatically — you only need the manual import if configuring vitest yourself.

## License

MIT
npm package: @matthesketh/utopia-vite-plugin
Description: Vite plugin for UtopiaJS .utopia files
Author: Matt
Latest version: 0.7.2

# @matthesketh/utopia-vite-plugin

Vite plugin for UtopiaJS. Transforms `.utopia` single-file components, extracts and injects CSS through Vite's virtual module pipeline, provides granular HMR support (style-only hot updates), and handles the SSR runtime swap.

## Install

```bash
pnpm add -D @matthesketh/utopia-vite-plugin
```

Requires `vite` ^6.0.0 or ^7.0.0 as a peer dependency.

## Usage

```ts
// vite.config.ts
import { defineConfig } from '@matthesketh/utopia-vite-plugin';

export default defineConfig();
```

Or with the plugin directly:

```ts
// vite.config.ts
import { defineConfig } from 'vite';
import utopia from '@matthesketh/utopia-vite-plugin';

export default defineConfig({
  plugins: [utopia()],
});
```

## API

| Export | Description |
|--------|-------------|
| `default (utopiaPlugin)` | The Vite plugin factory. Accepts `UtopiaPluginOptions`. |
| `defineConfig(userConfig?)` | Create a Vite config pre-configured for UtopiaJS (plugin, extensions, SSR settings). |

**UtopiaPluginOptions:**

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `include` | `FilterPattern` | `'**/*.utopia'` | Glob patterns to include |
| `exclude` | `FilterPattern` | -- | Glob patterns to exclude |

**Features:**

- Compiles `.utopia` files via `@matthesketh/utopia-compiler`
- Extracts CSS to virtual modules processed by Vite's CSS pipeline
- Granular HMR: style-only changes skip component re-render
- SSR alias: swaps `@matthesketh/utopia-runtime` to `@matthesketh/utopia-server/ssr-runtime` in SSR builds and dev SSR
- Resolves `.utopia` as a file extension for bare imports

See [docs/architecture.md](../../docs/architecture.md) and [docs/ssr.md](../../docs/ssr.md) for details.

## License

MIT
npm package: @matthew.b/makr-css
Description: Modular css base files for a lightweight and quick start to your project.
Author: Mattew Boyles
Latest version: 1.0.0

# makr.css
Light and Modular CSS files that give you a fast and consistent start on your projects. 

The aim of makr.css is to give foundational stability to the core of your projects by keeping the structure and base component css the same across your work, while allowing you to build on top of it exactly what you need so each project has a unique look and bespoke feel. 

makr.css is there to provide your project balance and stability and stay out of the way while you design and style your work.