Terminal

A window that types its commands out and prints their output a beat later.

Loading preview

Installation

npx shadcn@latest add @loomui/terminal

Usage

import {
  Terminal,
  TerminalCommand,
  TerminalOutput,
} from "@/components/ui/terminal"
<Terminal title="~/acme-app">
  <TerminalCommand>npm install</TerminalCommand>
  <TerminalOutput delay={500}>added 214 packages in 3s</TerminalOutput>
  <TerminalCommand>npm run dev</TerminalCommand>
  <TerminalOutput>ready on http://localhost:3000</TerminalOutput>
</Terminal>

The session runs in order. A command types itself out, hands the session to the next line, and an output waits its delay before printing. Lines are rendered as the session reaches them rather than hidden and revealed, so a line that has not run yet is not in the document at all.

Order comes from where a line sits in the children, so writing a session is writing it down. There are no indexes to keep in step and no step prop to increment when you add a line in the middle.

Pace

<Terminal speed={20}>
  <TerminalCommand>pnpm build</TerminalCommand>
  <TerminalOutput delay={900}>Compiled successfully</TerminalOutput>
</Terminal>

speed is milliseconds per character, and it belongs to the terminal rather than to a line, because one hand is typing all of it. Around 34 reads as a person. Below about 15 it reads as a paste.

delay on an output is how long the command appears to take before it answers. This is the prop that sells the whole thing: an install that answers instantly looks fake, and one that answers after 400ms looks like an install.

Prompts

<TerminalCommand prompt="›">deploy --prod</TerminalCommand>

prompt is whatever sits in front of the command, and it is decoration. It is aria-hidden and not selectable, so copying the session gives you commands you can paste rather than commands with a $ welded on.

Holding and skipping

startOnView holds the session until the window is on screen, which is on by default. A terminal that has already finished typing by the time it is scrolled to has told its story to an empty room.

instant prints the whole session at once. Use it in tests, or anywhere the content matters more than the performance.

Props

Terminal

PropTypeDefaultDescription
titlestring"bash"Text in the window bar.
speednumber34Milliseconds per typed character.
startOnViewbooleantrueHold until the window scrolls into view.
instantbooleanfalsePrint the whole session with no typing.
classNamestringnoneMerged onto the window.

TerminalCommand

PropTypeDefaultDescription
childrenstringnoneThe command, as plain text.
promptstring"$"What sits in front of the command.

TerminalOutput

PropTypeDefaultDescription
childrenReactNodenoneAnything. Output is not typed, so markup is fine.
delaynumber260Milliseconds the command appears to run.

Accessibility

The session is not a live region, so a screen reader is not interrupted line by line by decoration. Under prefers-reduced-motion the whole session renders at once, which is also what a reader who cannot wait for the typing gets: the full text, immediately, in order.