https://github.com/ydah/rterm
Headless terminal emulator library for Ruby.
https://github.com/ydah/rterm
emulator headless library ruby terminal terminal-emulator
Last synced: 25 days ago
JSON representation
Headless terminal emulator library for Ruby.
- Host: GitHub
- URL: https://github.com/ydah/rterm
- Owner: ydah
- License: mit
- Created: 2026-03-01T10:21:57.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2026-05-13T05:46:07.000Z (about 2 months ago)
- Last Synced: 2026-05-13T07:26:04.168Z (about 2 months ago)
- Topics: emulator, headless, library, ruby, terminal, terminal-emulator
- Language: Ruby
- Homepage:
- Size: 239 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.txt
Awesome Lists containing this project
README
# RTerm
RTerm is a headless terminal emulator library for Ruby. It parses ANSI/VT
streams, owns terminal state, and exposes PTY, rendering, image, and browser
bridge APIs without forcing a native UI.
## Current Scope
- Terminal core: buffer state, scrollback, reflow, selection, mouse reporting,
modes, OSC, DCS, colors, hyperlinks, clipboard policy, and window operations.
- Process integration: Unix PTY support and a Windows ConPTY boundary.
- Rendering: screen trees, HTML/ARIA output, RGBA raster frames, renderer
lifecycle state, and browser-side DOM/WebGL assets.
- Images: Sixel and iTerm2 image payload tracking with PNG, GIF, and JPEG
decoding paths.
- BrowserBridge: WebSocket session transport with resume, attach policies,
rate limits, heartbeats, origin checks, and binary frames.
- Addons: search, serialization, clipboard, links, fonts, Unicode widths,
graphemes, ligatures, progress, images, and host integration.
## Requirements
- Ruby 3.2 or later
- `faye-websocket` for BrowserBridge WebSocket deployments
- npm 9 or later when packaging browser adapter assets
## Installation
```ruby
# Gemfile
gem "rterm"
gem "faye-websocket" # only for BrowserBridge WebSocket servers
```
```bash
gem install rterm
```
## Quick Start
```ruby
require "rterm"
term = RTerm::Terminal.new(cols: 80, rows: 24, scrollback: 1_000)
term.write("Hello \e[1;31mWorld\e[0m!\r\n")
line = term.buffer.active.get_line(0)
puts line.to_string # => "Hello World!"
```
## Common Usage
### Terminal State
```ruby
term.write("open https://example.com\r\n")
term.select_url(8, 0)
puts term.selection
cell = term.buffer.active.get_line(0).get_cell(0)
puts term.cell_colors(cell)
puts term.cursor_info(active: false)
```
Useful options can be passed in snake_case or camelCase:
```ruby
term = RTerm::Terminal.new(
reflowCursorLine: true,
scrollOnEraseInDisplay: true,
ignoreBracketedPasteMode: true
)
```
### Addons
```ruby
search = RTerm::Addon::Search.new
term.load_addon(search)
term.write("error: failed\r\nerror: retrying")
puts search.find_all("error").length # => 2
serializer = RTerm::Addon::Serialize.new
term.load_addon(serializer)
snapshot = serializer.snapshot(scrollback: 100)
links = RTerm::Addon::WebLinks.new
term.load_addon(links)
term.write("open https://example.com")
links.open_link(links.find_links.first)
```
### Rendering
```ruby
screen = RTerm::Addon::ScreenRenderer.new
term.load_addon(screen)
puts screen.text
puts screen.accessibility_tree[:children].first[:text]
html = RTerm::Addon::HtmlRenderer.new
term.load_addon(html)
puts html.to_html
raster = RTerm::Addon::RasterRenderer.new(cell_width: 8, cell_height: 16)
term.load_addon(raster)
File.write("terminal.ppm", raster.to_ppm)
```
Renderer lifecycle addons keep host renderer state close to terminal state:
```ruby
renderer = RTerm::Addon::WebGL.new
term.load_addon(renderer)
renderer.attach_host(:canvas, viewport: { cellWidth: 9, cellHeight: 18 })
renderer.update_scrollbar(visible: true, width: 12)
renderer.on_context_loss { |event| warn event[:reason] }
```
### Images
```ruby
images = RTerm::Addon::Image.new
term.load_addon(images)
# After terminal output emits an image payload:
term.write("\ePqABCDEF\e\\")
image = images.by_protocol(:sixel).first
decoded = images.decode(image) if image
puts decoded[:result][:format] if decoded
```
### PTY
```ruby
pty = RTerm::Pty.new(
command: ENV["SHELL"] || "/bin/sh",
env: { "TERM" => "xterm-256color" },
cwd: Dir.pwd,
process_group: true
)
pty.on_data { |data| term.write(data) }
pty.write("printf 'hello\\n'\r")
pty.close
```
Windows hosts can provide or configure a ConPTY backend:
```ruby
conpty = RTerm::ConPTY.new(command: "cmd.exe", cols: 80, rows: 24)
conpty.on_data { |data| term.write(data) }
conpty.kill(:TERM, group: true) if conpty.process_group_enabled?
RTerm::ConPTY.configure_backend(lambda { |**options| MyConPTYBackend.new(**options) })
```
### BrowserBridge
```ruby
manager = RTerm::BrowserBridge::SessionManager.new(
max_sessions: 10,
idle_timeout: 600,
attach_policy: :single
)
message = RTerm::BrowserBridge::ProtocolHandler.decode(
'{"type":"create_session","payload":{"cols":80,"rows":24}}'
)
response = manager.process_message(message)
```
BrowserBridge production servers should start from secure defaults:
```ruby
RTerm::BrowserBridge::WebSocketServer.configure_secure_defaults do |config|
config.allowed_origins = ["https://your-app.example"]
config.authenticator = lambda do |message|
payload = message[:payload] || message["payload"] || {}
token = payload[:token] || payload["token"]
token == ENV.fetch("RTERM_BRIDGE_TOKEN")
end
end
```
The bundled browser adapter can mount a BrowserBridge session in a page:
```ruby
[
RTerm::BrowserAdapter.style_tag,
%(
),
RTerm::BrowserAdapter.script_tag,
%(new RTermBrowserAdapter("#terminal", { url: "wss://your-app.example/terminal", renderer: "webgl", raster: true });)
]
```
The same browser adapter assets can be consumed from JavaScript tooling:
```js
import RTermBrowserAdapter, { RTermWebGLRenderer } from "rterm-browser-adapter";
import "rterm-browser-adapter/style.css";
new RTermBrowserAdapter("#terminal", {
url: "wss://your-app.example/terminal",
renderer: "webgl",
raster: true
});
```
## Addon Summary
| Addon | Purpose |
| --- | --- |
| `Attach` | Forward terminal input and remote output through socket-like objects. |
| `Search` | Find text, regex matches, captures, and optional decorations. |
| `Serialize` | Export ANSI replay, HTML, and structured snapshots. |
| `Fit` | Compute terminal dimensions from available space. |
| `Clipboard` | Handle text copy/paste and OSC 52 policy flows. |
| `Progress` | Track OSC 9 progress state. |
| `Image` | Track, decode, filter, and dispatch Sixel, PNG, GIF, and JPEG payloads. |
| `Ligatures` | Compute character join ranges. |
| `Unicode11`, `UnicodeGraphemes` | Switch width providers and measure grapheme clusters. |
| `WebFonts` | Register font faces, expose CSS, estimate cells, and trigger relayout events. |
| `WebLinks` | Detect and activate links with provider hooks. |
| `HostIntegration` | Bridge host mount, input, clipboard, font, renderer, and accessibility events. |
| `ScreenRenderer`, `HtmlRenderer`, `RasterRenderer` | Produce screen trees, HTML/ARIA output, and RGBA frames. |
| `Canvas`, `WebGL` | Track renderer lifecycle and cache state. |
## Operational Notes
- `RTerm::Pty` is available on Unix-like systems.
- `RTerm::ConPTY` defines the Windows process boundary, uses the bundled native
backend on Windows, and reports group termination support when available.
- Keep BrowserBridge origins, message size limits, rate limits, and heartbeats
enabled in production.
- Disable clipboard handling for untrusted remote output with
`clipboard_enabled: false`.
- Do not connect untrusted browser input directly to a privileged shell.
- Validate URI schemes before activating OSC 8 hyperlinks.
## Examples
- `examples/basic_usage.rb`
- `examples/addons.rb`
- `examples/browser_adapter.html`
- `examples/websocket_server.rb`
- `examples/browser_bridge_production.rb`
## Documentation
- Security policy: `docs/security_policy.md`
- Renderer and font measurement scope: `docs/renderer_scope.md`
- Release checklist: `docs/release_checklist.md`
## Development
```bash
git clone https://github.com/ydah/rterm.git
cd rterm
bundle install
bundle exec rspec
bundle exec rake package:verify_contents
npm pack --dry-run
```
Strict integration checks require external terminal tools and browser automation:
```bash
bundle exec rake e2e:strict
```
Set `RTERM_BROWSER_E2E=1` to require the Playwright browser smoke in local runs.
Benchmarks live under `spec/benchmarks/`.
## License
MIT. See [LICENSE.txt](LICENSE.txt).