Skip to content
ShineUI
Install

Terminal

The admin console.

<sui-terminal>MiscStable

About

Mono console with a pluggable command map, for support and admin tooling. help lists the map, clear wipes the log and unknown input prints "Unknown command". A handler may return a promise; its output lands when it settles. print() and clear() let a host drive the log. It is dark in either site mode unless data-sui-code="light".

Usage

<sui-terminal height="180"></sui-terminal> <script type="module"> import { defineCustomElements } from "@shineui/core/loader"; import "@shineui/core/shineui.css"; defineCustomElements(); const el = document.querySelector("sui-terminal"); el.commands = { whoami: () => { /* ... */ }, invoice: (arg) => { /* ... */ }, period: () => { /* ... */ } }; /* ((arg: string) => string | Promise<string>) | string | undefined */ el.addEventListener("suiCommand", (e) => console.log(e.detail)); </script>
import { SuiTerminal } from "@shineui/react"; import "@shineui/core/shineui.css"; export function Example() { const commands = { whoami: () => { /* ... */ }, invoice: (arg) => { /* ... */ }, period: () => { /* ... */ } }; /* ((arg: string) => string | Promise<string>) | string | undefined */ return <SuiTerminal height="180" commands={commands} onSuiCommand={(e) => console.log(e.detail)} />; }
<script setup> import { SuiTerminal } from "@shineui/vue"; import "@shineui/core/shineui.css"; const commands = { whoami: () => { /* ... */ }, invoice: (arg) => { /* ... */ }, period: () => { /* ... */ } }; // ((arg: string) => string | Promise<string>) | string | undefined const onChange = (e) => console.log(e.detail); </script> <template> <SuiTerminal height="180" :commands="commands" @sui-command="onChange" /> </template>
import { Component } from "@angular/core"; import { SuiTerminal } from "@shineui/angular"; @Component({ standalone: true, imports: [SuiTerminal], template: `<sui-terminal height="180" [commands]="commands" (suiCommand)="onChange($event)"></sui-terminal>`, }) export class Example { commands = { whoami: () => { /* ... */ }, invoice: (arg) => { /* ... */ }, period: () => { /* ... */ } }; // ((arg: string) => string | Promise<string>) | string | undefined onChange(e: CustomEvent) { console.log(e.detail); } }

Basic

Type help to list the map, then invoice 1043. The log is role="log" with aria-live="polite", so each printed line is announced; the field is named "Console input" and a click anywhere in the log focuses it.

<sui-terminal height="180"></sui-terminal>
// setup(root): properties and events setup(root) { root.querySelector('sui-terminal').commands = supportCommands(); }

Async handlers

A handler may return a promise. The input echoes at once and the output lands when it settles; a rejected handler prints its message, and output from a command still in flight when clear runs is dropped.

<sui-terminal id="tm-async" height="180" welcome="Type `status` or `sync`. Both take a moment."></sui-terminal>
// setup(root): properties and events setup(root) { const wait = (ms, value) => new Promise((resolve) => setTimeout(() => resolve(value), ms)); root.querySelector('#tm-async').commands = { status: () => wait(700, 'Bank feed healthy. Last sync 9:14 AM.'), sync: () => wait(900, 'Pulled 14 transactions from the August feed.'), post: () => Promise.reject(new Error('The August period is locked. Reopen it first.')), }; }

Prompt, welcome and height

prompt is the string before the input and every echoed line. welcome is the first printed line, and height caps the scrollable log in px.

<sui-terminal id="tm-shape" prompt="admin$" welcome="Falls City Supply · support console" height="120"></sui-terminal>
// setup(root): properties and events setup(root) { root.querySelector('#tm-shape').commands = supportCommands(); }

Writing into the log

print() writes lines into the log as output, one per string, and clear() empties it, as the clear command does. Neither runs a command or fires suiCommand, so a host can stream status or events into the console beside what the operator types.

<div style="display:grid;gap:10px"> <sui-terminal id="tm-print" height="150" welcome="Payment run status"></sui-terminal> <div style="display:flex;gap:8px"> <sui-button id="tm-print-step" variant="outline" size="sm" label="Print the next step"></sui-button> <sui-button id="tm-print-clear" variant="outline" size="sm" label="Clear"></sui-button> </div> </div>
// setup(root): properties and events setup(root) { const el = root.querySelector('#tm-print'); const steps = ['Matched 42 bills to the August run.', 'Approved by Dana Reyes.', 'Sent 42 payments to the bank.', 'Bank confirmed the batch.']; let at = 0; root.querySelector('#tm-print-step').addEventListener('click', () => { el.print(steps[at % steps.length]); at += 1; }); root.querySelector('#tm-print-clear').addEventListener('click', () => el.clear()); }

Events

suiCommand fires for every submitted line before it runs, with the raw line as the detail. Built-ins and unknown input are included, so a host can audit-log the session or watch for input its map does not cover.

Nothing submitted yet.
<div style="display:grid;gap:10px"> <sui-terminal id="tm-events" height="150"></sui-terminal> <span id="tm-log" style="font:460 13px/1.6 var(--font-sans);color:var(--ink-muted)">Nothing submitted yet.</span> </div>
// setup(root): properties and events setup(root) { const el = root.querySelector('#tm-events'); const log = root.querySelector('#tm-log'); const seen = []; el.commands = supportCommands(); el.addEventListener('suiCommand', (e) => { seen.push(e.detail); log.textContent = `Audit log: ${seen.join(' · ')}`; }); }

Keyboard

KeyAction
EnterRuns the line, clears the field and adds the line to the session history.
ArrowUpRecalls the previous line from the session history.
ArrowDownSteps forward through the history; past the newest line it clears the field.

API

<sui-terminal>

Properties

PropertyAttributeTypeDefaultDescription
commandsproperty only((arg: string) => string | Promise<string>) | string | undefinedCommand map: name to handler. The handler receives the rest of the line and returns the output text or a promise of it. With a promise the input echoes at once and the output is appended when it settles; a rejected handler prints its message, and output from a command still in flight when clear runs is dropped. Property, not attribute. Without it only help and clear exist.
heightheightnumber200Scrollable log height in px.
promptpromptstring'>'Prompt string shown before the input and before every echoed line.
welcomewelcomestringstring | undefinedFirst line printed in the log.

Events

EventDetailReactVueDescription
suiCommandstringonSuiCommand@sui-commandFires for every line the operator submits, before it runs; detail is the raw line. Built-ins and unknown input included, so a host can audit-log the session or watch for input its map does not cover.

Methods

MethodSignatureDescription
clear()clear() => Promise<void>Empties the log, the welcome line included, as the clear command does. Output from a command still in flight is dropped when it settles. suiCommand does not fire.
print()print(text: string | string[]) => Promise<void>Prints lines to the log as output, the way a command's output prints: one line per string, in order, scrolled into view. For a host that writes status or an event stream into the console. Nothing runs and suiCommand does not fire.
setFocus()setFocus() => Promise<void>Moves focus to the command line, as a click anywhere on the log does. The host's own focus() does the same.

Theming

Every design token this element can render with, read from its own stylesheets and the ones it imports. Set a token on :root or on any ancestor and every use below follows it. The values shown are the ones in the theme you are viewing. The list is read per stylesheet, so an element that imports a shared drawing module lists that module's tokens too, whether or not it paints with all of them.

Every name above is listed with its value and its role in Foundations.

See also