@boba-cli/machine
v0.1.0-alpha.2
Published
Platform abstraction layer for Boba terminal UIs
Readme
@boba-cli/machine
Platform abstraction layer for Boba terminal UIs. This package enables Boba applications to run in both Node.js and browser environments (with xterm.js) by providing platform-agnostic interfaces for terminal I/O, clipboard access, environment detection, and signal handling.
Installation
npm install @boba-cli/machineOptional Peer Dependencies
For Node.js usage:
npm install clipboardy supports-color archiver unzipperFor browser usage:
npm install @xterm/xtermUsage
Node.js
import { createNodePlatform } from '@boba-cli/machine/node'
const platform = createNodePlatform()
// Subscribe to terminal input
const inputDisposable = platform.terminal.onInput((data) => {
console.log('Received:', data)
})
// Write to terminal
platform.terminal.write('Hello, World!\n')
// Get terminal size
const { columns, rows } = platform.terminal.getSize()
// Enable raw mode for key-by-key input
platform.terminal.enableRawMode()
// Handle signals
platform.signals.onInterrupt(() => {
console.log('Interrupted!')
platform.dispose()
})
// Read from clipboard
const text = await platform.clipboard.read()
// Detect color support
const colorSupport = platform.environment.getColorSupport()
if (colorSupport.has16m) {
// Use true color
}
// Create zip archive (requires archiver package)
if (platform.archive.isAvailable()) {
await platform.archive.zip('/path/to/source', '/path/to/output.zip')
}
// Extract zip archive (requires unzipper package)
if (platform.archive.isAvailable()) {
await platform.archive.unzip('/path/to/archive.zip', '/path/to/output')
}
// File system operations
const content = await platform.filesystem.readFile('/path/to/file.txt')
await platform.filesystem.writeFile('/path/to/output.txt', 'Hello!')
const exists = await platform.filesystem.exists('/path/to/file.txt')
// Path operations
const fullPath = platform.path.join('dir', 'subdir', 'file.txt')
const baseName = platform.path.basename('/path/to/file.txt') // 'file.txt'
const dirName = platform.path.dirname('/path/to/file.txt') // '/path/to'
// Terminal styling
const { style } = platform.style
console.log(style.red.bold('Error!'))
console.log(style.green('Success'))
console.log(style.blue.underline('Link'))
// Clean up when done
platform.dispose()Browser (with xterm.js)
import { Terminal } from '@xterm/xterm'
import { createBrowserPlatform } from '@boba-cli/machine/browser'
// Create xterm.js terminal
const terminal = new Terminal()
terminal.open(document.getElementById('terminal')!)
// Create platform adapter
const platform = createBrowserPlatform({ terminal })
// Use the same API as Node.js
platform.terminal.onInput((data) => {
// Handle input
})
platform.terminal.write('Hello from the browser!')
// Clean up
platform.dispose()Platform-Agnostic Code
Write code that works on both platforms:
import type { PlatformAdapter } from '@boba-cli/machine'
function runApp(platform: PlatformAdapter) {
const { columns, rows } = platform.terminal.getSize()
platform.terminal.onInput((data) => {
// Handle input bytes
})
platform.terminal.onResize((size) => {
// Handle resize
})
platform.terminal.write(`Terminal size: ${columns}x${rows}\n`)
}Byte Utilities
The package provides cross-platform byte utilities as a replacement for Node.js Buffer:
import {
encodeString,
decodeString,
byteLength,
concatBytes,
decodeFirstRune,
} from '@boba-cli/machine'
// Encode string to UTF-8 bytes
const bytes = encodeString('Hello, 世界!')
// Decode bytes to string
const text = decodeString(bytes)
// Get byte length of a string
const len = byteLength('Hello') // 5
// Concatenate byte arrays
const combined = concatBytes(bytes1, bytes2, bytes3)
// Decode first UTF-8 character
const [char, byteLen] = decodeFirstRune(bytes)ANSI Escape Sequences
Platform-agnostic ANSI escape sequence constants and utilities:
import {
CURSOR_SHOW,
CURSOR_HIDE,
CLEAR_SCREEN,
ALT_SCREEN_ON,
ALT_SCREEN_OFF,
cursorTo,
fgRGB,
bgRGB,
setWindowTitle,
} from '@boba-cli/machine'
// Use constants directly
terminal.write(CURSOR_HIDE)
terminal.write(CLEAR_SCREEN)
terminal.write(ALT_SCREEN_ON)
// Use utility functions
terminal.write(cursorTo(10, 5))
terminal.write(fgRGB(255, 128, 64))
terminal.write(setWindowTitle('My App'))API Reference
Interfaces
PlatformAdapter
Complete platform adapter combining all platform-specific functionality:
terminal: TerminalAdapter- Terminal I/O adaptersignals: SignalAdapter- Signal handling adapterclipboard: ClipboardAdapter- Clipboard operations adapterenvironment: EnvironmentAdapter- Environment access adapterfilesystem: FileSystemAdapter- File system operations adapterpath: PathAdapter- Path operations adapterarchive: ArchiveAdapter- Archive (zip/unzip) operations adapterstyle: StyleAdapter- Terminal text styling adapterdispose(): void- Clean up all resources
TerminalAdapter
Terminal I/O interface:
onInput(handler): Disposable- Subscribe to input dataonResize(handler): Disposable- Subscribe to resize eventswrite(data: string): void- Write to terminal outputgetSize(): TerminalSize- Get current terminal dimensionsenableRawMode(): void- Enable raw input modedisableRawMode(): void- Disable raw input modeisTTY(): boolean- Check if terminal is a TTY
SignalAdapter
Signal handling interface:
onInterrupt(handler): Disposable- Handle SIGINT/beforeunloadonTerminate(handler): Disposable- Handle SIGTERM/pagehide
ClipboardAdapter
Clipboard operations interface:
read(): Promise<string>- Read text from clipboardwrite(text: string): Promise<void>- Write text to clipboardisAvailable(): boolean- Check if clipboard is available
EnvironmentAdapter
Environment access interface:
get(name: string): string | undefined- Get environment variablegetColorSupport(): ColorSupport- Detect color support levelgetTerminalBackground(): TerminalBackground- Detect dark/light mode
FileSystemAdapter
File system operations interface:
readdir(path, options?): Promise<DirectoryEntry[] | string[]>- Read directory contentsstat(path): Promise<FileStat>- Get file/directory statsreadFile(path, encoding?): Promise<string>- Read file contents as textwriteFile(path, content): Promise<void>- Write text to a fileexists(path): Promise<boolean>- Check if path existsmkdir(path, options?): Promise<void>- Create directoryunlink(path): Promise<void>- Delete filermdir(path, options?): Promise<void>- Remove directoryrename(src, dst): Promise<void>- Rename/move file or directorycopyFile(src, dst): Promise<void>- Copy filecwd(): string- Get current working directoryhomedir(): string- Get user's home directory
PathAdapter
Path operations interface:
join(...segments): string- Join path segmentsdirname(path): string- Get directory namebasename(path, ext?): string- Get base nameextname(path): string- Get file extensionresolve(...segments): string- Resolve to absolute pathisAbsolute(path): boolean- Check if path is absolutenormalize(path): string- Normalize pathsep: string- Platform-specific path separator
ArchiveAdapter
Archive operations interface:
zip(sourceDir, destPath): Promise<void>- Create zip archive from directoryunzip(archivePath, destDir): Promise<void>- Extract zip archiveisAvailable(): boolean- Check if archiving is supported
Note: Archive operations require the archiver and unzipper packages to be installed in Node.js environments. Browser environments do not support archive operations.
StyleAdapter
Terminal text styling interface:
style: StyleFn- Chainable style functionenabled: boolean- Whether styling is enabledlevel: number- Color support level (0-3)
Types
TerminalSize
interface TerminalSize {
readonly columns: number
readonly rows: number
}ColorSupport
interface ColorSupport {
readonly level: number // 0-3
readonly hasBasic: boolean // 16 colors
readonly has256: boolean // 256 colors
readonly has16m: boolean // True color (16 million)
}TerminalBackground
type TerminalBackground = 'dark' | 'light' | 'unknown'Exports
The package has three entry points:
@boba-cli/machine- Core interfaces, byte utilities, and ANSI sequences@boba-cli/machine/node- Node.js adapter implementations@boba-cli/machine/browser- Browser/xterm.js adapter implementations
License
MIT
