Using Cmd

The Cmd module provides terminal control commands — cursor positioning, screen clearing, and visibility toggling. Commands are zero-size boxes that emit ANSI escape sequences when rendered with renderPrettySync.

How Commands Work

Each command is a Box<AnsiStyle> with no visible dimensions. When composed with other boxes using Box.combine or Box.combineAll, commands inject escape sequences into the output stream without affecting layout.

import * as Box from "effect-boxes/Box";
import * as Cmd from "effect-boxes/Cmd";
 
// A command is just a box — compose it like any other
const output = Box.combineAll([Cmd.clearScreen, Box.text("Hello, terminal!")]);
 
console.log(Box.renderPrettySync(output));

Cursor Movement

Absolute Positioning

Move the cursor to a specific column and row (0-indexed):

import * as Cmd from "effect-boxes/Cmd";
 
// Move to column 10, row 5
Cmd.cursorTo(10, 5);
 
// Move to home position (top-left)
Cmd.home;

Relative Movement

Move the cursor relative to its current position:

// Move 5 columns right and 2 rows up
Cmd.cursorMove(5, -2);
 
// Single-direction helpers
Cmd.cursorUp(3);
Cmd.cursorDown(2);
Cmd.cursorForward(10);
Cmd.cursorBackward(4);
 
// Move to start of next/previous line
Cmd.cursorNextLine(1);
Cmd.cursorPrevLine(1);

Save and Restore

Save the cursor position and restore it later:

import { pipe } from "effect";
import * as Box from "effect-boxes/Box";
import * as Cmd from "effect-boxes/Cmd";
 
const withSavedPosition = Box.combineAll([
  Cmd.cursorSavePosition,
  Cmd.cursorTo(0, 0),
  Box.text("Status: OK"),
  Cmd.cursorRestorePosition,
]);

Screen Clearing

Full Screen

// Clear everything and move cursor to home
Cmd.clearScreen;
 
// Clear from cursor to end of screen
Cmd.eraseDown;
 
// Clear from cursor to start of screen
Cmd.eraseUp;
 
// Clear entire screen (without moving cursor)
Cmd.eraseScreen;

Line Clearing

// Clear the entire current line
Cmd.eraseLine;
 
// Clear from cursor to end of line
Cmd.eraseEndLine;
 
// Clear from cursor to start of line
Cmd.eraseStartLine;

Multi-line Clearing

Two approaches for clearing multiple lines:

// eraseLines: deletes N lines from the scroll region (shifts content up)
Cmd.eraseLines(5);
 
// clearLines: blanks N lines in place (no scrolling), cursor moves up
Cmd.clearLines(3);

clearLines is the better choice for rewriting output in place (spinners, progress bars) since it doesn't affect surrounding content.

Cursor Visibility

Hide the cursor during animations or full-screen rendering to avoid flicker:

import * as Box from "effect-boxes/Box";
import * as Cmd from "effect-boxes/Cmd";
 
// Hide during render, show after
const frame = Box.combineAll([
  Cmd.cursorHide,
  Box.text("Rendering..."),
  Cmd.cursorShow,
]);

Alternate Screen

Switch to the alternate screen buffer (used by full-screen TUI apps like vim, htop). Content on the main screen is preserved and restored on exit:

import * as Box from "effect-boxes/Box";
import * as Cmd from "effect-boxes/Cmd";
 
// Enter alternate screen at start
const enter = Box.combineAll([
  Cmd.altScreenEnter,
  Cmd.cursorHide,
  Cmd.clearScreen,
]);
 
// Leave alternate screen on exit (restores previous content)
const leave = Box.combineAll([Cmd.altScreenLeave, Cmd.cursorShow]);

Combining with Boxes

Use Box.combine (binary) or Box.combineAll (array) to interleave commands with visible content:

import { pipe } from "effect";
import * as Box from "effect-boxes/Box";
import * as Cmd from "effect-boxes/Cmd";
import * as Ansi from "effect-boxes/Ansi";
 
// Position text at specific coordinates
const positioned = Box.combineAll([
  Cmd.cursorTo(10, 5),
  pipe(Box.text("Hello!"), Box.annotate(Ansi.bold)),
  Cmd.cursorTo(10, 7),
  pipe(Box.text("World!"), Box.annotate(Ansi.green)),
]);
 
console.log(Box.renderPrettySync(positioned));

Commands compose with pipe too:

const output = pipe(
  Cmd.clearScreen,
  Box.combine(Cmd.cursorTo(0, 2)),
  Box.combine(Box.text("Centered title").pipe(Box.alignHoriz(Box.center1, 65)))
);

Practical Example: Rewriting Output

The clearLines pattern is common for progress displays and spinners — clear the previous frame and write the new one in its place:

import * as Box from "effect-boxes/Box";
import * as Cmd from "effect-boxes/Cmd";
 
const renderFrame = (progress: number) =>
  Box.combineAll([
    Cmd.clearLines(3),
    Box.vcat(
      [
        Box.text("Downloading..."),
        Box.text(`Progress: ${progress}%`),
        Box.text(progress === 100 ? "Done!" : `ETA: ${100 - progress}s`),
      ],
      Box.left
    ),
  ]);
 
// Each call to renderFrame clears the previous 3 lines
// and writes the updated content in place

Practical Example: Full-Screen App

A typical full-screen TUI lifecycle:

import { Effect, Console } from "effect";
import * as Box from "effect-boxes/Box";
import * as Cmd from "effect-boxes/Cmd";
import * as Ansi from "effect-boxes/Ansi";
 
const setup = Box.combineAll([
  Cmd.altScreenEnter,
  Cmd.cursorHide,
  Cmd.clearScreen,
]);
 
const render = (state: AppState) =>
  Box.combineAll([
    Cmd.home,
    buildUI(state), // your layout function
  ]);
 
const teardown = Box.combineAll([Cmd.altScreenLeave, Cmd.cursorShow]);
 
const app = Effect.gen(function* () {
  yield* Console.log(Box.renderPrettySync(setup));
  // ... event loop ...
  yield* Console.log(Box.renderPrettySync(teardown));
});

For the full list of available commands, see the Cmd API reference.