---
title: "Ops"
description: "UI directive builders for @bomb.sh/tty"
canonical: https://bomb.sh/docs/tty/api/ops/
---

# Ops

The ops module provides builders for describing terminal UI as a flat array of directives. Each frame is a complete snapshot. Build ops, pass them to [`term.render()`](/docs/tty/api/term), and write the output bytes.

## Building a frame

```ts
import { close, open, rgba, text, grow } from "@bomb.sh/tty";

let ops = [
  open("root", {
    layout: { width: grow(), height: grow(), direction: "ttb" },
  }),
  open("panel", {
    layout: { padding: { left: 1, right: 1, top: 1, bottom: 1 } },
    border: { color: rgba(0, 255, 0), left: 1, right: 1, top: 1, bottom: 1 },
    cornerRadius: { tl: 1, tr: 1, bl: 1, br: 1 },
  }),
  text("Hello, World!"),
  close(),
  close(),
];
```

## open(id, props?)

Opens a layout element. Every element needs a unique string `id` for pointer hit-testing and layout queries.

### Layout

| Property    | Type                                | Description                                           |
| ----------- | ----------------------------------- | ----------------------------------------------------- |
| `width`     | `SizingAxis`                        | Horizontal sizing (`fit`, `grow`, `percent`, `fixed`) |
| `height`    | `SizingAxis`                        | Vertical sizing                                       |
| `padding`   | `{ left?, right?, top?, bottom? }`  | Inner padding in cells                                |
| `gap`       | `number`                            | Gap between children                                  |
| `direction` | `"ltr"` \| `"ttb"`                  | Child layout direction                                |
| `alignX`    | `"left"` \| `"center"` \| `"right"` | Horizontal alignment                                  |
| `alignY`    | `"top"` \| `"center"` \| `"bottom"` | Vertical alignment                                    |

### Style

| Property       | Type                                           | Description                                       |
| -------------- | ---------------------------------------------- | ------------------------------------------------- |
| `bg`           | `number`                                       | Background color (packed RGBA via `rgba()`)       |
| `cornerRadius` | `{ tl?, tr?, bl?, br? }`                       | Rounded corners                                   |
| `border`       | `{ color, bg?, left?, right?, top?, bottom? }` | Border sides (number or `{ width, color?, bg? }`) |
| `clip`         | `{ horizontal?, vertical? }`                   | Clip overflowing content                          |

### Floating

| Property                      | Type                                              | Description             |
| ----------------------------- | ------------------------------------------------- | ----------------------- |
| `floating.x`, `floating.y`    | `number`                                          | Position offset         |
| `floating.expand`             | `{ width?, height? }`                             | Expand floating bounds  |
| `floating.attachTo`           | `"none"` \| `"parent"` \| `"element"` \| `"root"` | Attachment target       |
| `floating.attachPoints`       | `{ element?, parent? }`                           | Anchor points           |
| `floating.pointerCaptureMode` | `"capture"` \| `"passthrough"`                    | Pointer event capture   |
| `floating.clipTo`             | `"none"` \| `"attached-parent"`                   | Clip to attached parent |
| `floating.zIndex`             | `number`                                          | Stacking order          |

### Transitions

```ts
import { open, rgba } from "@bomb.sh/tty";

open("sidebar", {
  layout: { width: { type: "fixed", value: 20 } },
  bg: rgba(30, 30, 40),
  transition: {
    duration: 0.3,
    easing: "easeInOut",
    properties: ["width", "bg"],
  },
});
```

| Property      | Type                                                     | Description                         |
| ------------- | -------------------------------------------------------- | ----------------------------------- |
| `duration`    | `number`                                                 | Duration in seconds                 |
| `easing`      | `"linear"` \| `"easeIn"` \| `"easeOut"` \| `"easeInOut"` | Easing function                     |
| `properties`  | `TransitionProperty[]`                                   | Properties to animate               |
| `interactive` | `boolean`                                                | Allow interaction during transition |

Transition properties: `"x"`, `"y"`, `"position"`, `"width"`, `"height"`, `"size"`, `"bg"`, `"overlay"`, `"borderColor"`, `"borderWidth"`, `"all"`.

## text(content, props?)

Renders a text node inside the current open element.

| Property   | Type     | Description                          |
| ---------- | -------- | ------------------------------------ |
| `color`    | `number` | Foreground color                     |
| `bg`       | `number` | Background color                     |
| `fontSize` | `number` | Font size                            |
| `fontId`   | `number` | Font identifier                      |
| `wrap`     | `number` | Wrap width                           |
| `attrs`    | `number` | Text attributes (bold, italic, etc.) |

## close()

Closes the current open element. Ops must be properly nested. Every `open` needs a matching `close`.

## snapshot(ops)

Pre-packs an op array into a reusable snapshot op. Useful when part of the UI is static across frames.

```ts
import { close, open, snapshot, text, grow } from "@bomb.sh/tty";

let chrome = snapshot([
  open("header", { layout: { width: grow(), height: { type: "fixed", value: 1 } } }),
  text("My App"),
  close(),
]);

// Each frame: [chrome, ...dynamicOps]
```

## rgba(r, g, b, a?)

Packs an RGBA color into a 32-bit integer. Alpha defaults to 255.

```ts
import { rgba } from "@bomb.sh/tty";

rgba(255, 0, 0);       // red
rgba(0, 255, 0, 128);  // semi-transparent green
```

## Sizing helpers

```ts
import { fit, grow, percent, fixed } from "@bomb.sh/tty";

fit();              // shrink to content
fit(10, 40);        // fit with min/max
grow();             // expand to fill available space
grow(5, 50);        // grow with min/max
percent(0.5);       // 50% of parent
fixed(20);          // exactly 20 cells
```

## Types

| Type           | Description                                              |
| -------------- | -------------------------------------------------------- |
| `Op`           | Union of all directive types                             |
| `OpenElement`  | Element opened by `open()`                               |
| `Text`         | Text node                                                |
| `CloseElement` | Element closed by `close()`                              |
| `SizingAxis`   | `{ type: "fit" \| "grow" \| "percent" \| "fixed", ... }` |
| `Transition`   | Transition configuration                                 |
| `BorderSide`   | `number` or `{ width, color?, bg? }`                     |

> **Note:**
>
> `pack()` is also exported for low-level wire-format packing. Most applications use `open`/`text`/`close` and let `term.render()` handle packing internally.

## Next steps

* [Term API](/docs/tty/api/term) (rendering ops to the terminal)
* [Examples](/docs/tty/guides/examples) (transitions and layout demos)
