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.