npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

browser-terminal-cli

v1.0.2

Published

A customizable terminal/CLI interface for browser-side JavaScript

Readme

Browser Terminal CLI

npm version npm downloads License: MIT TypeScript Zero Dependencies

A powerful, customizable, and lightweight terminal/CLI interface for browser-side JavaScript applications. Create beautiful command-line interfaces in your web apps with ease.

✨ Features

  • 🎨 9 Built-in Themes - Dracula, Monokai, Matrix, Ubuntu, Solarized, and more
  • ⌨️ Command History - Navigate with arrow keys, persistent storage ready
  • 📝 Tab Completion - Auto-complete command names
  • 🔌 Easy Command Registration - Simple API for adding custom commands
  • 🎯 Full TypeScript Support - Complete type definitions included
  • 📦 Zero Dependencies - Lightweight (~15KB minified)
  • Keyboard Shortcuts - Ctrl+C, Ctrl+L, Ctrl+U support
  • 📊 Rich Output - Tables, colors, styled text, HTML support
  • 🎭 Customizable - Prompts, themes, fonts, cursor styles
  • 🔄 Async Commands - Full Promise/async-await support
  • 📱 Responsive - Works on desktop and mobile browsers

📦 Installation

NPM

npm install browser-terminal-cli

Yarn

yarn add browser-terminal-cli

PNPM

pnpm add browser-terminal-cli

CDN

<!-- UMD Build -->
<script src="https://unpkg.com/browser-terminal-cli/dist/index.umd.js"></script>

<!-- ESM Build -->
<script type="module">
  import { Terminal } from 'https://unpkg.com/browser-terminal-cli/dist/index.esm.js';
</script>

🚀 Quick Start

ES Modules

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');

HTML Setup

<!DOCTYPE html>
<html>
<head>
  <style>
    #terminal {
      width: 800px;
      height: 400px;
      border-radius: 8px;
      overflow: hidden;
    }
  </style>
</head>
<body>
  <div id="terminal"></div>
  
  <script type="module">
    import { Terminal } from 'browser-terminal-cli';
    
    const terminal = new Terminal({
      container: '#terminal',
      theme: 'monokai',
    });
  </script>
</body>
</html>

UMD (Script Tag)

<script src="https://unpkg.com/browser-terminal-cli/dist/index.umd.js"></script>
<script>
  const terminal = new BrowserTerminal.Terminal({
    container: '#terminal',
  });
</script>

📖 API Reference

Constructor Options

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 |

Output Methods

write(text, options?)

Write text without a newline.

terminal.write('Hello ');
terminal.write('World!');
// Output: Hello World!

writeln(text, options?)

Write text with a newline.

terminal.writeln('Line 1');
terminal.writeln('Line 2');

writeError(text)

Write error message (red color).

terminal.writeError('Something went wrong!');

writeSuccess(text)

Write success message (green color).

terminal.writeSuccess('Operation completed!');

writeWarning(text)

Write warning message (yellow color).

terminal.writeWarning('This is deprecated');

writeInfo(text)

Write info message (blue color).

terminal.writeInfo('Tip: Use help for more commands');

writeTable(data, columns?)

Write formatted table.

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.

terminal.clear();

Output Options

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:

// 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('<strong>Bold</strong> and <em>italic</em>', { html: true });
terminal.writeln('<span style="color: red">Red text</span>', { html: true });

// Custom class
terminal.writeln('Custom styled', { className: 'my-custom-class' });

Command Registration

addCommand(name, handler, description?)

Simple command registration.

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.

interface CommandDefinition {
  name: string;              // Command name
  handler: CommandHandler;   // Handler function
  description?: string;      // Help description
  usage?: string;           // Usage example
  aliases?: string[];       // Alternative names
}
terminal.registerCommand({
  name: 'fetch',
  description: 'Fetch data from a URL',
  usage: 'fetch <url> [--json]',
  aliases: ['get', 'request'],
  handler: async ({ args, flags, terminal }) => {
    const url = args[0];
    if (!url) {
      terminal.writeError('Usage: fetch <url>');
      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.

terminal.registerCommands([
  {
    name: 'start',
    handler: () => 'Starting...',
    description: 'Start the application',
  },
  {
    name: 'stop',
    handler: () => 'Stopping...',
    description: 'Stop the application',
  },
]);

removeCommand(name)

Remove a registered command.

terminal.removeCommand('hello');

hasCommand(name)

Check if command exists.

if (terminal.hasCommand('deploy')) {
  terminal.exec('deploy');
}

getCommands()

Get all registered commands.

const commands = terminal.getCommands();
commands.forEach(cmd => {
  console.log(cmd.name, cmd.description);
});

Command Handler Context

interface CommandContext {
  terminal: TerminalInterface;  // Terminal instance
  args: string[];               // Positional arguments
  flags: Record<string, string | boolean>; // Parsed flags
  rawInput: string;             // Original input string
}

Example:

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' }

Control Methods

focus()

Focus the terminal input.

terminal.focus();

blur()

Remove focus from terminal.

terminal.blur();

disable()

Disable terminal input.

terminal.disable();

enable()

Enable terminal input.

terminal.enable();

setPrompt(prompt)

Change the command prompt.

terminal.setPrompt('>>> ');
terminal.setPrompt('user@host:~$ ');
terminal.setPrompt('🚀 ');

getPrompt()

Get current prompt string.

const prompt = terminal.getPrompt(); // '$ '

setValue(value)

Set the input field value.

terminal.setValue('echo hello');

getValue()

Get current input value.

const input = terminal.getValue();

exec(command)

Execute a command programmatically.

await terminal.exec('help');
await terminal.exec('clear');
await terminal.exec('echo Hello World');

destroy()

Clean up and remove terminal.

terminal.destroy();

Theme Methods

setTheme(theme)

Change terminal theme.

// 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.

const theme = terminal.getTheme();
console.log(theme.background); // '#282a36'

Available Themes

| 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 |


History Methods

getHistory()

Get command history array.

const history = terminal.getHistory();
// ['ls', 'cd documents', 'cat file.txt']

clearHistory()

Clear command history.

terminal.clearHistory();

setHistory(history)

Set command history (for persistence).

// 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())
  );
});

Events

on(event, callback)

Subscribe to terminal events.

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.

const handler = (cmd) => console.log(cmd);
terminal.on('command', handler);
terminal.off('command', handler);

Event Types

| 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 |


Built-in Commands

| Command | Description | |---------|-------------| | help | Show all available commands | | help <command> | Show help for specific command | | clear | Clear terminal screen | | history | Show command history | | echo <text> | Print text to terminal |


Keyboard Shortcuts

| 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

Custom Theme Object

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;
}

Theme Examples

// 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

File System Simulation

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, '/');
}

Interactive Game

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 <number>');
  },
});

terminal.registerCommand({
  name: 'guess',
  description: 'Guess the number',
  usage: 'guess <number>',
  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}`;
});

API Client

const API_BASE = 'https://jsonplaceholder.typicode.com';

terminal.registerCommand({
  name: 'api',
  description: 'Make API requests',
  usage: 'api <endpoint> [--method=GET] [--body={}]',
  handler: async ({ args, flags, terminal }) => {
    const endpoint = args[0];
    if (!endpoint) {
      terminal.writeError('Usage: api <endpoint>');
      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"}

Task Manager

const tasks = [];
let taskId = 1;

terminal.addCommand('add', ({ args }) => {
  const text = args.join(' ');
  if (!text) return 'Usage: add <task description>';
  
  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}`;
});

Progress Animation

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:

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

Persisting History

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()));
});

Custom Autocomplete

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()));
}

Multiple Terminals

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)
  5. Open a Pull Request

🙏 Acknowledgments

  • Inspired by Xterm.js
  • Themes inspired by popular color schemes
  • Built with TypeScript and Rollup

📬 Support