A customizable terminal/CLI interface for browser-side JavaScript
npm install browser-terminal-cli
bash
npm install browser-terminal-cli
`
$3
`bash
yarn add browser-terminal-cli
`
$3
`bash
pnpm add browser-terminal-cli
`
$3
`html
`
🚀 Quick Start
$3
`javascript
import { Terminal } from 'browser-terminal-cli';
const terminal = new Terminal({
container: '#terminal',
prompt: '❯ ',
welcomeMessage: 'Welcome! Type "help" for available commands.',
theme: 'dracula',
});
// Add a simple command
terminal.addCommand('hello', () => {
return 'Hello, World!';
}, 'Say hello');
// Add a command with arguments
terminal.addCommand('greet', ({ args }) => {
const name = args[0] || 'stranger';
return Hello, ${name}!;
}, 'Greet someone by name');
`
$3
`html
`
$3
`html
`
📖 API Reference
$3
`typescript
const terminal = new Terminal(options: TerminalOptions);
`
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| container | HTMLElement \| string | required | Container element or CSS selector |
| prompt | string | '$ ' | Command prompt string |
| welcomeMessage | string \| string[] | '' | Welcome message displayed on init |
| theme | string \| TerminalTheme | 'default' | Theme name or custom theme object |
| fontSize | number | 14 | Font size in pixels |
| fontFamily | string | 'Cascadia Code', monospace | Font family |
| cursorStyle | 'block' \| 'underline' \| 'bar' | 'block' | Cursor style |
| cursorBlink | boolean | true | Enable cursor blinking |
| history | boolean | true | Enable command history |
| historySize | number | 100 | Maximum history entries |
| autoFocus | boolean | true | Auto-focus terminal on init |
| scrollback | number | 1000 | Maximum output lines to keep |
| tabSize | number | 4 | Tab character width |
$3
#### write(text, options?)
Write text without a newline.
`javascript
terminal.write('Hello ');
terminal.write('World!');
// Output: Hello World!
`
#### writeln(text, options?)
Write text with a newline.
`javascript
terminal.writeln('Line 1');
terminal.writeln('Line 2');
`
#### writeError(text)
Write error message (red color).
`javascript
terminal.writeError('Something went wrong!');
`
#### writeSuccess(text)
Write success message (green color).
`javascript
terminal.writeSuccess('Operation completed!');
`
#### writeWarning(text)
Write warning message (yellow color).
`javascript
terminal.writeWarning('This is deprecated');
`
#### writeInfo(text)
Write info message (blue color).
`javascript
terminal.writeInfo('Tip: Use help for more commands');
`
#### writeTable(data, columns?)
Write formatted table.
`javascript
terminal.writeTable([
{ name: 'John', age: 30, city: 'NYC' },
{ name: 'Jane', age: 25, city: 'LA' },
{ name: 'Bob', age: 35, city: 'Chicago' },
]);
// Output:
// name | age | city
// -----+-----+--------
// John | 30 | NYC
// Jane | 25 | LA
// Bob | 35 | Chicago
`
#### clear()
Clear all terminal output.
`javascript
terminal.clear();
`
$3
`typescript
interface OutputOptions {
color?: string; // Text color (CSS color)
backgroundColor?: string; // Background color
bold?: boolean; // Bold text
italic?: boolean; // Italic text
underline?: boolean; // Underlined text
className?: string; // Custom CSS class
html?: boolean; // Parse as HTML
}
`
Examples:
`javascript
// Colored text
terminal.writeln('Error!', { color: '#ff5555' });
terminal.writeln('Success!', { color: 'rgb(80, 250, 123)' });
// Styled text
terminal.writeln('Important', { bold: true });
terminal.writeln('Emphasis', { italic: true });
terminal.writeln('Link', { underline: true, color: '#8be9fd' });
// Combined styles
terminal.writeln('Critical Error', {
color: '#ff5555',
bold: true,
backgroundColor: '#1a1a1a'
});
// HTML content
terminal.writeln('Bold and italic', { html: true });
terminal.writeln('Red text', { html: true });
// Custom class
terminal.writeln('Custom styled', { className: 'my-custom-class' });
`
---
$3
#### addCommand(name, handler, description?)
Simple command registration.
`javascript
terminal.addCommand('time', () => {
return new Date().toLocaleTimeString();
}, 'Display current time');
terminal.addCommand('add', ({ args }) => {
const sum = args.reduce((a, b) => a + parseFloat(b), 0);
return Sum: ${sum};
}, 'Add numbers together');
`
#### registerCommand(definition)
Advanced command registration.
`typescript
interface CommandDefinition {
name: string; // Command name
handler: CommandHandler; // Handler function
description?: string; // Help description
usage?: string; // Usage example
aliases?: string[]; // Alternative names
}
`
`javascript
terminal.registerCommand({
name: 'fetch',
description: 'Fetch data from a URL',
usage: 'fetch [--json]',
aliases: ['get', 'request'],
handler: async ({ args, flags, terminal }) => {
const url = args[0];
if (!url) {
terminal.writeError('Usage: fetch ');
return;
}
try {
terminal.writeln( Fetching ${url}...);
const response = await fetch(url);
const data = flags.json
? await response.json()
: await response.text();
return typeof data === 'object'
? JSON.stringify(data, null, 2)
: data;
} catch (error) {
terminal.writeError(Failed: ${error.message});
}
},
});
`
#### registerCommands(definitions[])
Register multiple commands at once.
`javascript
terminal.registerCommands([
{
name: 'start',
handler: () => 'Starting...',
description: 'Start the application',
},
{
name: 'stop',
handler: () => 'Stopping...',
description: 'Stop the application',
},
]);
`
#### removeCommand(name)
Remove a registered command.
`javascript
terminal.removeCommand('hello');
`
#### hasCommand(name)
Check if command exists.
`javascript
if (terminal.hasCommand('deploy')) {
terminal.exec('deploy');
}
`
#### getCommands()
Get all registered commands.
`javascript
const commands = terminal.getCommands();
commands.forEach(cmd => {
console.log(cmd.name, cmd.description);
});
`
$3
`typescript
interface CommandContext {
terminal: TerminalInterface; // Terminal instance
args: string[]; // Positional arguments
flags: Record; // Parsed flags
rawInput: string; // Original input string
}
`
Example:
`javascript
terminal.addCommand('example', (context) => {
const { terminal, args, flags, rawInput } = context;
console.log('Raw input:', rawInput);
console.log('Arguments:', args);
console.log('Flags:', flags);
// Use terminal methods
terminal.writeln('Processing...');
if (flags.verbose) {
terminal.writeln('Verbose mode enabled');
}
return Processed ${args.length} arguments;
});
// Usage: example arg1 arg2 --verbose --output=file.txt
// args: ['arg1', 'arg2']
// flags: { verbose: true, output: 'file.txt' }
`
---
$3
#### focus()
Focus the terminal input.
`javascript
terminal.focus();
`
#### blur()
Remove focus from terminal.
`javascript
terminal.blur();
`
#### disable()
Disable terminal input.
`javascript
terminal.disable();
`
#### enable()
Enable terminal input.
`javascript
terminal.enable();
`
#### setPrompt(prompt)
Change the command prompt.
`javascript
terminal.setPrompt('>>> ');
terminal.setPrompt('user@host:~$ ');
terminal.setPrompt('🚀 ');
`
#### getPrompt()
Get current prompt string.
`javascript
const prompt = terminal.getPrompt(); // '$ '
`
#### setValue(value)
Set the input field value.
`javascript
terminal.setValue('echo hello');
`
#### getValue()
Get current input value.
`javascript
const input = terminal.getValue();
`
#### exec(command)
Execute a command programmatically.
`javascript
await terminal.exec('help');
await terminal.exec('clear');
await terminal.exec('echo Hello World');
`
#### destroy()
Clean up and remove terminal.
`javascript
terminal.destroy();
`
---
$3
#### setTheme(theme)
Change terminal theme.
`javascript
// Use built-in theme
terminal.setTheme('dracula');
terminal.setTheme('matrix');
// Use custom theme
terminal.setTheme({
background: '#1a1a2e',
foreground: '#eaeaea',
cursor: '#00ff88',
selection: 'rgba(0, 255, 136, 0.3)',
black: '#000000',
red: '#ff5555',
green: '#00ff88',
yellow: '#ffff55',
blue: '#5555ff',
magenta: '#ff55ff',
cyan: '#55ffff',
white: '#ffffff',
});
`
#### getTheme()
Get current theme object.
`javascript
const theme = terminal.getTheme();
console.log(theme.background); // '#282a36'
`
$3
| Theme | Description |
|-------|-------------|
| default | Default dark theme |
| dark | GitHub dark inspired |
| light | Light theme |
| matrix | Matrix green on black |
| ubuntu | Ubuntu terminal colors |
| monokai | Monokai color scheme |
| dracula | Dracula theme |
| solarized-dark | Solarized dark |
| solarized-light | Solarized light |
---
$3
#### getHistory()
Get command history array.
`javascript
const history = terminal.getHistory();
// ['ls', 'cd documents', 'cat file.txt']
`
#### clearHistory()
Clear command history.
`javascript
terminal.clearHistory();
`
#### setHistory(history)
Set command history (for persistence).
`javascript
// Restore from localStorage
const saved = localStorage.getItem('terminal-history');
if (saved) {
terminal.setHistory(JSON.parse(saved));
}
// Save on command
terminal.on('command', () => {
localStorage.setItem('terminal-history',
JSON.stringify(terminal.getHistory())
);
});
`
---
$3
#### on(event, callback)
Subscribe to terminal events.
`javascript
terminal.on('command', (command, args) => {
console.log('Executed:', command, args);
analytics.track('command', { command, args });
});
terminal.on('input', (value) => {
console.log('Input changed:', value);
});
terminal.on('clear', () => {
console.log('Terminal cleared');
});
terminal.on('ready', () => {
console.log('Terminal initialized');
});
terminal.on('key', (event) => {
if (event.key === 'Escape') {
terminal.blur();
}
});
`
#### off(event, callback)
Unsubscribe from events.
`javascript
const handler = (cmd) => console.log(cmd);
terminal.on('command', handler);
terminal.off('command', handler);
`
$3
| Event | Callback Signature | Description |
|-------|-------------------|-------------|
| command | (command: string, args: string[]) => void | Command executed |
| input | (value: string) => void | Input value changed |
| clear | () => void | Terminal cleared |
| ready | () => void | Terminal initialized |
| key | (event: KeyboardEvent) => void | Key pressed |
---
$3
| Command | Description |
|---------|-------------|
| help | Show all available commands |
| help | Show help for specific command |
| clear | Clear terminal screen |
| history | Show command history |
| echo | Print text to terminal |
---
$3
| Shortcut | Action |
|----------|--------|
| ↑ Arrow Up | Previous command in history |
| ↓ Arrow Down | Next command in history |
| Tab | Auto-complete command name |
| Ctrl + C | Cancel current input |
| Ctrl + L | Clear terminal |
| Ctrl + U | Clear current line |
| Enter | Execute command |
---
🎨 Theming
$3
`typescript
interface TerminalTheme {
background: string; // Background color
foreground: string; // Default text color
cursor: string; // Cursor color
cursorAccent?: string; // Cursor text color
selection?: string; // Selection background
// ANSI Colors
black?: string;
red?: string;
green?: string;
yellow?: string;
blue?: string;
magenta?: string;
cyan?: string;
white?: string;
// Bright ANSI Colors
brightBlack?: string;
brightRed?: string;
brightGreen?: string;
brightYellow?: string;
brightBlue?: string;
brightMagenta?: string;
brightCyan?: string;
brightWhite?: string;
}
`
$3
`javascript
// Cyberpunk theme
terminal.setTheme({
background: '#0a0a0f',
foreground: '#0ff',
cursor: '#f0f',
green: '#0f0',
red: '#f00',
yellow: '#ff0',
blue: '#00f',
magenta: '#f0f',
cyan: '#0ff',
});
// Nord theme
terminal.setTheme({
background: '#2e3440',
foreground: '#d8dee9',
cursor: '#d8dee9',
black: '#3b4252',
red: '#bf616a',
green: '#a3be8c',
yellow: '#ebcb8b',
blue: '#81a1c1',
magenta: '#b48ead',
cyan: '#88c0d0',
white: '#e5e9f0',
});
`
---
💡 Examples
$3
`javascript
const fs = {
'/': { type: 'dir', children: ['home', 'usr', 'var'] },
'/home': { type: 'dir', children: ['user'] },
'/home/user': { type: 'dir', children: ['documents', 'readme.txt'] },
'/home/user/readme.txt': { type: 'file', content: 'Hello World!' },
};
let cwd = '/home/user';
terminal.addCommand('pwd', () => cwd);
terminal.addCommand('ls', ({ args }) => {
const path = args[0] ? resolvePath(args[0]) : cwd;
const node = fs[path];
if (!node) return ls: ${path}: No such file or directory;
if (node.type === 'file') return path.split('/').pop();
return node.children.join(' ');
});
terminal.addCommand('cd', ({ args }) => {
const path = args[0] ? resolvePath(args[0]) : '/home/user';
if (!fs[path] || fs[path].type !== 'dir') {
return cd: ${args[0]}: Not a directory;
}
cwd = path;
terminal.setPrompt(${cwd} $ );
});
terminal.addCommand('cat', ({ args }) => {
const path = resolvePath(args[0]);
const node = fs[path];
if (!node) return cat: ${args[0]}: No such file;
if (node.type !== 'file') return cat: ${args[0]}: Is a directory;
return node.content;
});
function resolvePath(path) {
if (path.startsWith('/')) return path;
if (path === '..') return cwd.split('/').slice(0, -1).join('/') || '/';
return ${cwd}/${path}.replace(/\/+/g, '/');
}
`
$3
`javascript
let gameState = {
score: 0,
level: 1,
target: randomTarget(),
};
function randomTarget() {
return Math.floor(Math.random() * 100) + 1;
}
terminal.registerCommand({
name: 'play',
description: 'Start the number guessing game',
handler: ({ terminal }) => {
gameState = { score: 0, level: 1, target: randomTarget() };
terminal.writeln('🎮 Number Guessing Game Started!');
terminal.writeln('Guess a number between 1-100');
terminal.writeln('Use: guess ');
},
});
terminal.registerCommand({
name: 'guess',
description: 'Guess the number',
usage: 'guess ',
handler: ({ args, terminal }) => {
const num = parseInt(args[0]);
if (isNaN(num)) {
return 'Please enter a valid number';
}
if (num === gameState.target) {
gameState.score += gameState.level * 10;
gameState.level++;
gameState.target = randomTarget();
terminal.writeSuccess( 🎉 Correct! Score: ${gameState.score});
terminal.writeln(Level ${gameState.level} - New number generated);
} else if (num < gameState.target) {
terminal.writeWarning('📈 Go higher!');
} else {
terminal.writeWarning('📉 Go lower!');
}
},
});
terminal.addCommand('score', () => {
return Score: ${gameState.score} | Level: ${gameState.level};
});
`
$3
`javascript
const API_BASE = 'https://jsonplaceholder.typicode.com';
terminal.registerCommand({
name: 'api',
description: 'Make API requests',
usage: 'api [--method=GET] [--body={}]',
handler: async ({ args, flags, terminal }) => {
const endpoint = args[0];
if (!endpoint) {
terminal.writeError('Usage: api ');
return;
}
const method = flags.method || 'GET';
const url = ${API_BASE}${endpoint};
terminal.writeln(${method} ${url}, { color: '#8be9fd' });
try {
const options = { method };
if (flags.body) {
options.body = flags.body;
options.headers = { 'Content-Type': 'application/json' };
}
const response = await fetch(url, options);
const data = await response.json();
terminal.writeSuccess(Status: ${response.status});
terminal.writeln(JSON.stringify(data, null, 2));
} catch (error) {
terminal.writeError(Error: ${error.message});
}
},
});
// Usage:
// api /posts/1
// api /posts --method=POST --body={"title":"Hello"}
`
$3
`javascript
const tasks = [];
let taskId = 1;
terminal.addCommand('add', ({ args }) => {
const text = args.join(' ');
if (!text) return 'Usage: add ';
tasks.push({ id: taskId++, text, done: false });
return ✅ Added task #${taskId - 1};
});
terminal.addCommand('list', ({ terminal }) => {
if (tasks.length === 0) {
return '📭 No tasks';
}
tasks.forEach(task => {
const status = task.done ? '✅' : '⬜';
const style = task.done ? { color: '#6b7280' } : {};
terminal.writeln(${status} #${task.id}: ${task.text}, style);
});
});
terminal.addCommand('done', ({ args }) => {
const id = parseInt(args[0]);
const task = tasks.find(t => t.id === id);
if (!task) return Task #${id} not found;
task.done = true;
return ✅ Completed: ${task.text};
});
terminal.addCommand('remove', ({ args }) => {
const id = parseInt(args[0]);
const index = tasks.findIndex(t => t.id === id);
if (index === -1) return Task #${id} not found;
const [task] = tasks.splice(index, 1);
return 🗑️ Removed: ${task.text};
});
`
$3
`javascript
terminal.registerCommand({
name: 'download',
description: 'Simulate file download with progress',
handler: async ({ args, terminal }) => {
const filename = args[0] || 'file.zip';
const size = parseInt(args[1]) || 100;
terminal.writeln(Downloading ${filename}...);
for (let i = 0; i <= 100; i += 5) {
const filled = Math.floor(i / 5);
const empty = 20 - filled;
const bar = '█'.repeat(filled) + '░'.repeat(empty);
const mb = ((i / 100) * size).toFixed(1);
// Create/update progress line
process.stdout.write(\r[${bar}] ${i}% (${mb}/${size} MB));
await new Promise(r => setTimeout(r, 100));
}
terminal.writeln('');
terminal.writeSuccess(✅ Downloaded ${filename});
},
});
`
---
🌐 Browser Support
| Browser | Version |
|---------|---------|
| Chrome | 60+ |
| Firefox | 55+ |
| Safari | 12+ |
| Edge | 79+ |
| Opera | 47+ |
---
📄 TypeScript
Full TypeScript support with exported types:
`typescript
import {
Terminal,
TerminalOptions,
TerminalTheme,
CommandDefinition,
CommandHandler,
CommandContext,
OutputOptions,
ThemeName,
} from 'browser-terminal-cli';
const options: TerminalOptions = {
container: '#terminal',
theme: 'dracula',
};
const terminal = new Terminal(options);
const myCommand: CommandDefinition = {
name: 'test',
description: 'A test command',
handler: (ctx: CommandContext): string => {
return Args: ${ctx.args.join(', ')};
},
};
terminal.registerCommand(myCommand);
`
---
🔧 Advanced Usage
$3
`javascript
const HISTORY_KEY = 'terminal-history';
// Load history on init
const terminal = new Terminal({
container: '#terminal',
});
const savedHistory = localStorage.getItem(HISTORY_KEY);
if (savedHistory) {
terminal.setHistory(JSON.parse(savedHistory));
}
// Save history on each command
terminal.on('command', () => {
localStorage.setItem(HISTORY_KEY, JSON.stringify(terminal.getHistory()));
});
`
$3
`javascript
terminal.on('key', (e) => {
if (e.key === 'Tab') {
e.preventDefault();
const input = terminal.getValue();
const suggestions = getAutocompleteSuggestions(input);
if (suggestions.length === 1) {
terminal.setValue(suggestions[0] + ' ');
} else if (suggestions.length > 1) {
terminal.writeln('');
terminal.writeln(suggestions.join(' '));
}
}
});
function getAutocompleteSuggestions(input) {
const files = ['readme.md', 'package.json', 'index.js'];
return files.filter(f => f.startsWith(input.split(' ').pop()));
}
`
$3
`javascript
const term1 = new Terminal({
container: '#terminal-1',
prompt: 'server1$ ',
theme: 'dracula',
});
const term2 = new Terminal({
container: '#terminal-2',
prompt: 'server2$ ',
theme: 'monokai',
});
// Sync commands between terminals
term1.on('command', (cmd) => {
term2.writeln([server1]: ${cmd}, { color: '#888' });
});
`
---
📝 License
MIT © Adhi
---
🤝 Contributing
Contributions are welcome! Please read our Contributing Guide for details.
1. Fork the repository
2. Create your feature branch (git checkout -b feature/amazing-feature)
3. Commit your changes (git commit -m 'Add amazing feature')
4. Push to the branch (git push origin feature/amazing-feature`)
Made with ❤️ for the web developer community