@unblessed/browser
Version:
Browser runtime adapter for @unblessed/core TUI library with xterm.js integration
454 lines (349 loc) • 10.1 kB
Markdown
# @unblessed/browser
Browser runtime adapter for [@unblessed/core](../core) - Run terminal UIs in the browser with xterm.js integration.
[](https://www.npmjs.com/package/@unblessed/browser)
[](../../LICENSE)
> ⚠️ **ALPHA SOFTWARE** - This package is part of the unblessed alpha release. API may change between alpha versions.
## Overview
`@unblessed/browser` enables terminal user interface (TUI) applications built with `@unblessed/core` to run in web browsers. It provides:
- **Browser polyfills** for Node.js APIs (fs, process, path, etc.)
- **xterm.js integration** for terminal rendering
- **Runtime adapter** that bridges @unblessed/core to the browser environment
- **Full widget support** - all @unblessed/core widgets work in the browser
## Installation
```bash
npm install @unblessed/browser@alpha xterm
# or
pnpm add @unblessed/browser@alpha xterm
# or
yarn add @unblessed/browser@alpha xterm
```
**Requirements:** Modern browser with ES2020 support (Chrome 90+, Firefox 88+, Safari 14+)
## Quick Start
### Basic Example
```typescript
import { Terminal } from "xterm";
import { Screen, Box } from "@unblessed/browser";
import "xterm/css/xterm.css";
// Create xterm.js terminal
const term = new Terminal({
cursorBlink: true,
fontSize: 14,
});
// Mount to DOM
term.open(document.getElementById("terminal")!);
// Create screen - automatically sets up xterm adapter
const screen = new Screen({ terminal: term });
// Build your TUI
const box = new Box({
parent: screen,
top: "center",
left: "center",
width: "50%",
height: "50%",
content: "{bold}Hello from the browser!{/bold}",
tags: true,
border: {
type: "line",
},
style: {
fg: "white",
bg: "blue",
border: {
fg: "#f0f0f0",
},
},
});
// Handle key events
screen.key(["escape", "q", "C-c"], () => {
process.exit(0);
});
// Render
box.focus();
screen.render();
```
### HTML Setup
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>unblessed Browser Example</title>
<link rel="stylesheet" href="node_modules/xterm/css/xterm.css" />
<style>
body {
margin: 0;
padding: 0;
background: #000;
}
#terminal {
width: 100vw;
height: 100vh;
}
</style>
</head>
<body>
<div id="terminal"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
```
## API Reference
### `Screen`
Browser-specific Screen class that automatically handles xterm.js integration.
```typescript
import { Screen } from "@unblessed/browser";
import { Terminal } from "xterm";
const term = new Terminal();
term.open(document.getElementById("terminal")!);
const screen = new Screen({
terminal: term, // xterm.js Terminal instance (required)
mouse: true, // Enable mouse support (default: true)
term: "xterm-256color", // Terminal type (default: 'xterm-256color')
});
```
**Options:**
- `terminal` (Terminal) - xterm.js Terminal instance - automatically creates XTermAdapter
- `mouse` (boolean) - Enable mouse events (default: true)
- `term` (string) - Terminal type string (default: 'xterm-256color')
- All standard ScreenOptions from @unblessed/core
**Returns:** Screen instance with xterm.js integration
### `XTermAdapter`
Low-level adapter for manual setup (rarely needed).
```typescript
import { XTermAdapter } from "@unblessed/browser";
const adapter = new XTermAdapter({
terminal: term,
mouse: true,
});
// Use as input/output for custom Screen setup
```
### Runtime Access
Access the browser runtime for advanced use cases:
```typescript
import { getRuntime } from "@unblessed/core";
const runtime = getRuntime();
// Provides: fs, path, process, Buffer, etc.
// Runtime is automatically initialized when you import @unblessed/browser
```
> **Note:** The runtime is automatically initialized when you import from `@unblessed/browser`. You don't need to call any initialization functions.
### Widget Re-exports
All @unblessed/core widgets are re-exported for convenience:
```typescript
import { Box, List, Input, Form, Button } from "@unblessed/browser";
const box = new Box({ parent: screen /* ... */ });
const list = new List({ parent: screen /* ... */ });
```
## Features
### ✅ Complete Node.js API Polyfills
- **File System** - Virtual FS with terminfo/font data
- **Process** - Browser-compatible process object
- **Path** - Cross-platform path utilities
- **Buffer** - Full Buffer support
- **Events** - EventEmitter implementation
- **Streams** - Readable/Writable streams
### ✅ Full Widget Support
All @unblessed/core widgets work in the browser:
- Layout: Box, Layout, Line
- Input: Input, Textarea, Button, Checkbox, RadioButton, Form
- Display: Text, Log, List, Table, FileManager
- Advanced: ProgressBar, Loading, Terminal (limited)
### ✅ Mouse & Keyboard Events
Full support for:
- Click, hover, drag events
- Keyboard shortcuts
- Focus management
- Mouse wheel scrolling
### ✅ xterm.js Integration
- Terminal rendering via xterm.js
- Automatic resize handling
- SGR mouse encoding
- 256 color support
## Examples
### Interactive Form
```typescript
import { Form, Input, Button } from "@unblessed/browser";
const form = new Form({
parent: screen,
keys: true,
left: "center",
top: "center",
width: 50,
height: 12,
border: { type: "line" },
label: " Login ",
});
const username = new Input({
parent: form,
name: "username",
top: 1,
left: 1,
height: 1,
width: 45,
label: " Username: ",
});
const password = new Input({
parent: form,
name: "password",
top: 3,
left: 1,
height: 1,
width: 45,
label: " Password: ",
censor: true,
});
const submit = new Button({
parent: form,
top: 6,
left: "center",
shrink: true,
padding: { left: 2, right: 2 },
content: "Submit",
style: {
bg: "blue",
focus: { bg: "red" },
},
});
submit.on("press", () => {
form.submit();
});
form.on("submit", (data) => {
console.log("Form submitted:", data);
});
screen.render();
```
### File Manager
```typescript
import { FileManager } from "@unblessed/browser";
const fm = new FileManager({
parent: screen,
border: { type: "line" },
style: {
border: { fg: "cyan" },
selected: { bg: "blue" },
},
height: "shrink",
width: "shrink",
top: "center",
left: "center",
label: " {blue-fg}%path{/blue-fg} ",
cwd: "/",
keys: true,
vi: true,
scrollbar: {
bg: "white",
},
});
fm.on("file", (file) => {
console.log("Selected file:", file);
});
screen.render();
```
### Real-time Data Display
```typescript
import { Log } from "@unblessed/browser";
const log = new Log({
parent: screen,
top: 0,
left: 0,
width: "100%",
height: "100%",
border: { type: "line" },
label: " Live Logs ",
tags: true,
scrollable: true,
mouse: true,
keys: true,
vi: true,
scrollbar: {
ch: " ",
inverse: true,
},
});
// Simulate streaming logs
setInterval(() => {
const timestamp = new Date().toISOString();
const level = ["INFO", "WARN", "ERROR"][Math.floor(Math.random() * 3)];
const color = { INFO: "green", WARN: "yellow", ERROR: "red" }[level];
log.log(
`{${color}-fg}[${timestamp}]{/} {bold}${level}{/}: Sample log message`,
);
screen.render();
}, 1000);
```
## Build Configuration
### Vite
```typescript
// vite.config.ts
import { defineConfig } from "vite";
export default defineConfig({
optimizeDeps: {
exclude: ["@unblessed/browser", "@unblessed/core"],
},
resolve: {
alias: {
// If needed for older bundlers
buffer: "buffer/",
process: "process/browser",
},
},
});
```
### Webpack
```javascript
// webpack.config.js
module.exports = {
resolve: {
fallback: {
buffer: require.resolve("buffer/"),
process: require.resolve("process/browser"),
path: require.resolve("path-browserify"),
stream: require.resolve("stream-browserify"),
},
},
};
```
## Architecture
### How It Works
1. **Runtime Abstraction**: @unblessed/core uses a runtime interface for all platform operations
2. **Browser Runtime**: This package provides browser-compatible implementations
3. **Automatic Initialization**: Runtime is set up when you import @unblessed/browser
4. **Widget Compatibility**: All widgets use the runtime interface, so they work unchanged
### Polyfills Provided
- `process` - Browser-compatible process object with env, platform, events
- `Buffer` - Full Buffer API via the buffer package
- `fs` - Virtual file system with bundled terminfo/font data
- `path` - Path manipulation via path-browserify
- `util` - Browser-compatible util.inspect and util.format
- Event system - Full EventEmitter support
## Browser Support
- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
- Any browser supporting ES2020
## Limitations
- No real file system access (virtual FS only)
- No child process support (Terminal widget limited)
- No native TTY detection (always reports TTY)
- Performance may vary on complex UIs
## Troubleshooting
### Module not found errors
If you see module resolution errors, ensure your bundler is configured to handle Node.js polyfills. See [Build Configuration](#build-configuration).
### Blank terminal
Make sure xterm.css is loaded:
```html
<link rel="stylesheet" href="node_modules/xterm/css/xterm.css" />
```
### Mouse events not working
Ensure mouse is enabled (it's enabled by default):
```typescript
new Screen({ terminal: term, mouse: true });
```
## Contributing
See the main [unblessed repository](https://github.com/vdeantoni/unblessed) for contribution guidelines.
## License
MIT © [Vinicius De Antoni](https://github.com/vdeantoni)
## Related
- [@unblessed/core](../core) - Core TUI library
- [@unblessed/node](../node) - Node.js runtime adapter
- [xterm.js](https://xtermjs.org/) - Terminal emulator for the web