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.homeRelative 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.eraseScreenLine 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.eraseStartLineMulti-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 placePractical 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.