Skip to content

shadcn.io is not affiliated with official shadcn/ui

Components

Free

Terminal for Vue

A command the agent runs, with its output as it streams. For Vue with Ark UI, a Trace line over a Code Block that opens while the command runs.

UI library: Ark UI

A shell command the agent runs shows up as one line, a lint, a test run, a build, and its output streams in underneath while it runs.

Use it when the output arrives line by line and the reader wants to watch it. The line is a Trace line that opens on a Code Block, so there is no Terminal component to install in Vue. Open the line once the first output arrives, say the exit in the title, "pnpm lint exited with code 1", and don't put buttons inside the line: a copy or clear button sits next to it, since the line itself is the button that opens it.

Preview width

Installation

Terminal is a pattern. The command adds the parts it's built from, Trace and Code Block, and the code to copy is the demo's.

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.

Anatomy

Terminal anatomy: trace, title, icon, trigger, content, code block
Trace
└── TraceCollapsible (one per command)
    ├── TraceCollapsibleTrigger
    │   └── TraceLine (status from the exit code)
    │       ├── TraceIcon
    │       └── TraceTitle (the command)
    └── TraceCollapsibleContent
        └── CodeBlock (size="sm")
            └── CodeBlockContent (wrap, the output)

Usage

<script setup lang="ts">
import { TerminalIcon } from "lucide-vue-next";
import {
  CodeBlock, CodeBlockContent,
} from "@/components/ui/code-block";
import {
  Trace, TraceCollapsible, TraceCollapsibleContent,
  TraceCollapsibleTrigger, TraceIcon, TraceLine, TraceTitle,
} from "@/components/ui/trace";
</script>
export function TerminalCommand(props: { output: string }) {
  return (
    <Trace>
      <TraceCollapsible>
        <TraceCollapsibleTrigger>
          <TraceLine>
            <TraceIcon>
              <TerminalIcon aria-hidden="true" />
            </TraceIcon>
            <TraceTitle>Ran pnpm build</TraceTitle>
          </TraceLine>
        </TraceCollapsibleTrigger>
        <TraceCollapsibleContent>
          <CodeBlock size="sm">
            <CodeBlockContent className="max-h-64" wrap>
              {props.output}
            </CodeBlockContent>
          </CodeBlock>
        </TraceCollapsibleContent>
      </TraceCollapsible>
    </Trace>
  );
}

Demos

Exit Code

An install that ran and a lint that exited with code 1, open on its errors. A command that exits with any code other than 0 failed: set status="error" and say the code in the title.

Preview width
CommandstatusTitle
Running"running"Running pnpm build
Exited with 0noneRan pnpm build
Exited with another code"error"pnpm lint exited with code 1

The command is a tool call, so its states map the same way as in Tool.

Open While Running

Open the line once the first output arrives, so the output is visible while the command runs, and let it close when the command exits. Hold the open state as undefined until the reader opens or closes the line; after that, their choice wins, as in Trace. The demo passes :open="open ?? (running && output.length > 0)", and sets its state from @open-change. CodeBlockContent with wrap and a max-h-64 keeps a long run inside the line, scrolling, so the transcript below it doesn't jump with each line.

AI SDK

A tool result arrives only once the command exits. To stream the output while it runs, write it from the tool as a data part with the tool call's id. The AI SDK replaces a data part that has the same id, so the output grows in place.

writer.write({
  type: "data-terminal",
  id: toolCallId,
  data: { output },
});

On the client, find the data part that matches each tool part.

function getOutput(message: ChatMessage, toolCallId: string) {
  for (const part of message.parts) {
    if (part.type === "data-terminal" && part.id === toolCallId) {
      return part.data.output;
    }
  }

  return "";
}

The messages come from Chat in @ai-sdk/vue, with the same parts.

Migrating from AI SDK Elements

AI SDK Elements is for React. In Vue, start from the demos above: a Trace line per command, over a Code Block that fills as the output streams, is all there is to Terminal.

FAQ

What does installing Terminal add in Vue?

Trace and Code Block, the two parts a command line is built from, with the Collapsible it opens on. Terminal is a pattern, so no terminal file lands in your ui folder: copy the demo's code into your own component and feed it your command's output. The command uses the shadcn-vue CLI.

How does the output stream into the line?

Keep the output so far as one string and pass it to CodeBlockContent; each new line re-renders it. With the AI SDK, write it from the tool as a data part under the tool call's id, which replaces the last one, as the AI SDK section shows. The line opens once the first output arrives.

How do I show that a command failed?

By its exit code: anything but 0 sets status="error", which turns the line destructive, and the title says the code, "pnpm lint exited with code 1". Open the failed line on its output, as the Exit Code demo does, so the errors are the first thing the reader sees.

Does it show ANSI colors?

Code Block can: useHighlightedCode(output, "ansi") reads SGR colors, bold, dim, italic and underline into the theme's classes, as its Terminal Output demo shows. Most tools print plain text when they aren't writing to a terminal, as the demos here do; highlight the output as ansi when yours keeps its escape codes.

Where do a copy or clear button go?

Next to the line, not inside it. The line's trigger is a button, and a button inside a button is invalid and can't be reached with the keyboard. Put them in a row beside the TraceCollapsibleTrigger, or in a Code Block header inside the content.

Can the reader open a command's output with the keyboard?

Yes. Each command is a button: Tab reaches it, and Enter or Space opens or closes its output. The output scrolls inside its own box, which takes focus so the arrow keys can scroll it.

Is this the same as AI SDK Elements' Terminal?

It does the same job without a wrapper: Terminal was a dark box with its own header, ANSI renderer and buttons, and here a command is a Trace line over a Code Block in the theme's colors. The Migrating from AI SDK Elements section has a prompt that rebuilds the old file on these parts and keeps its exports.

API Reference

Terminal has no props of its own. Each command is a Trace collapsible, and its output a Code Block with size="sm" and CodeBlockContent's wrap.