SwiftTerm Documentation
repository·main·Indexed 23 days ago
https://github.com/migueldeicaza/swifttermA VT100/Xterm terminal emulator library for Swift applications. It provides a UI-agnostic engine with platform-specific front-ends for macOS (AppKit) and iOS (UIKit), suitable for SSH clients, IDEs, or headless applications. Features include GPU-accelerated Metal rendering, customizable ANSI palettes, link reporting (OSC 8), and a termcast tool for recording and playing back sessions in asciinema format.
What's inside SwiftTerm
- SwiftTerm is a VT100/Xterm terminal emulator library for Swift applications. It provides a pluggable terminal emulation engine capable of handling escape sequence parsing, buffer management, Unicode rendering, and terminal state. It is designed to be embedded into various environments including macOS (AppKit), iOS/visionOS (UIKit), or headless environments.
Key Features of SwiftTerm
mainSwiftTerm supports a wide range of terminal capabilities:
- Rendering: Unicode (Emoji, combining characters, grapheme clusters), ANSI, 256-color, and TrueColor. Optional GPU-accelerated rendering via Metal.
- Text Attributes: Bold, italic, underline, strikethrough, dim/faint, blink, and inverse.
- Input/Interaction: Mouse event reporting (X10, SGR, UTF-8, URxvt), terminal resizing, and hyperlink support (OSC 8).
- Graphics: Sixel, iTerm2-style inline images, and Kitty graphics protocol.
- Search/Selection: Built-in macOS find bar and programmable search APIs.
- Session Management: Terminal session recording and playback with
termcast.
How the Terminal class works
mainThe
Terminalclass is the core VT100/Xterm terminal emulation engine in SwiftTerm. It is UI-agnostic, meaning it can be used with bundled AppKit/UIKit views, a headless backend, or a custom renderer.Key Concepts:
- Input: All input is provided to the terminal via the
feed(buffer:)family of methods. - Output: The terminal communicates back to the host/application through the
TerminalDelegate/send(source:data:)callback. - Thread Safety:
Terminalinstances are thread-safe. You can callfeed(byteArray:)from a background queue, and the terminal will handle internal synchronization. - State Management: It manages the terminal buffer, processes escape sequences, and tracks cursor state.
- Input: All input is provided to the terminal via the
Wire a terminal view to a remote host over SSH
mainSwiftTerm does not include a built-in SSH library. To connect a terminal to a remote host, you must implement a bidirectional data flow between the
TerminalViewand your chosen SSH library using the following pattern:- User input to SSH: Implement
TerminalViewDelegate/send(source:data:)to capture keystrokes and bytes from the terminal and write them to your SSH channel. - SSH output to terminal: When your SSH library receives data from the remote host, call
TerminalView/feed(byteArray:)to display that data in the terminal. - Terminal Resizing: When the terminal dimensions change, use
TerminalViewDelegate/sizeChanged(source:newCols:newRows:)to notify the SSH server to resize the PTY (Pseudo-Terminal).
┌──────────────┐ send(data:) ┌──────────────┐ │ │ ───────────────▶ │ │ │ TerminalView │ │ SSH Channel │ │ │ ◀────────────── │ │ └──────────────┘ feed(byteArray:) └──────────────┘- User input to SSH: Implement
Configure a Terminal with TerminalOptions
mainUseTerminalOptionsto control the initial state and behavior of aTerminalorHeadlessTerminalinstance. You can pass an options struct during construction to define dimensions, scrollback capacity, cursor appearance, and feature flags. For standard usage, useTerminalOptions.defaultto obtain sensible defaults (80x25 dimensions, 500-line scrollback, and a blinking block cursor).How iTerm2 inline images work
mainThe iTerm2 inline image protocol uses OSC 1337 to transmit Base64-encoded image data (such as PNG, JPEG, or GIF). SwiftTerm handles this through the
TerminalDelegate/createImage(source:data:width:height:preserveAspectRatio:)callback, which provides the encodedDataand sizing instructions.To test iTerm2 image support, use the
imgcatscript:imgcat image.pngUse HeadlessTerminal for programmatic terminal automation
mainA
HeadlessTerminalis a terminal emulator that runs a local process without a UI. It combines aTerminalengine with aLocalProcess, allowing you to run commands and inspect terminal output (including colors, cursor position, and escape sequences) programmatically. This is ideal for scripting, testing, and automation.To interact with the terminal, use the
terminalproperty to read buffer contents. To control the subprocess, use theprocessproperty.Connect TerminalView to a custom data source (SSH, Sockets, etc.)
mainTo connect a terminal to a custom backend like SSH or a network socket, use
TerminalViewdirectly and implement theTerminalViewDelegate.The core pattern is:
- Outgoing: Implement
TerminalViewDelegate.send(source:data:)to forward user input from the terminal to your backend. - Incoming: Call
TerminalView.feed(byteArray:)when your backend sends data to the terminal.
This pattern is used for both macOS (via
NSView) and iOS (viaUIScrollView).class MyTerminalController: NSViewController, TerminalViewDelegate { var terminalView: TerminalView! override func viewDidLoad() { super.viewDidLoad() terminalView = TerminalView(frame: view.bounds) terminalView.terminalDelegate = self view.addSubview(terminalView) } func send(source: TerminalView, data: ArraySlice<UInt8>) { // Send data to your backend (SSH channel, socket, etc.) } // Feed incoming data from the backend into the terminal: func onDataReceived(_ data: ArraySlice<UInt8>) { terminalView.feed(byteArray: data) } func sizeChanged(source: TerminalView, newCols: Int, newRows: Int) {} func setTerminalTitle(source: TerminalView, title: String) {} func hostCurrentDirectoryUpdate(source: TerminalView, directory: String?) {} func scrolled(source: TerminalView, position: Double) {} func requestOpenLink(source: TerminalView, link: String, params: [String: String]) {} func clipboardCopy(source: TerminalView, content: Data) {} func rangeChanged(source: TerminalView, startY: Int, endY: Int) {} }- Outgoing: Implement
How Kitty graphics protocol works
mainThe Kitty graphics protocol is a highly capable protocol that supports chunked transmission, image referencing by ID, arbitrary positioning with z-ordering, virtual placements with Unicode placeholders, and sub-image cropping. SwiftTerm manages image storage, placement, and Unicode placeholder rendering for this protocol.
To test Kitty graphics, use
kitten icatortimg:kitten icat image.pngtimg -pk image.pngConfigure Link Reporting and Activation
mainSwiftTerm can resolve links from two sources: Explicit links (OSC 8 hyperlinks) and Implicit links (URL-like text detected in content).
Link Reporting Modes
Use the
linkReportingproperty to control discovery:.none: Disable link tracking..explicit: Track OSC 8 links only..implicit: Track OSC 8 links first, then fallback to implicit URL detection (Default).
Link Activation
Link activation is governed by
linkHighlightMode. When a link is activated,TerminalViewcallsTerminalViewDelegate/requestOpenLink(source:link:params:).- Explicit links:
linkis the target;paramscontains parsed OSC 8 key/value pairs. - Implicit links:
linkis the detected URL text;paramsis empty.
Platform Specifics
- macOS: Default mode is
.hoverWithModifier(Command-click to open). Tracking is driven by AppKit mouse movement. - iOS/visionOS: Default mode is
.hover. Tracking is driven byUIPointerInteractionandUIHoverGestureRecognizer. Activation requires the Command key from a hardware keyboard for modifier-based modes.
terminalView.linkReporting = .implicit // Default: explicit first, then implicit fallbackHow Sixel graphics support works
mainSixel is a legacy inline graphics protocol that encodes images as six-pixel-tall rows. SwiftTerm parses Sixel data using the
SixelDcsHandlerand delivers the decoded raw RGBA pixel data to the front-end via theTerminalDelegate/createImageFromBitmap(source:bytes:width:height:)callback.To test Sixel support, you can use the
img2sixelutility from thelibsixelpackage:img2sixel image.pngIntegrate SSH with SwiftTerm on iOS
mainThe core SwiftTerm library does not include an SSH client to avoid extra dependencies. To connect to a remote system on iOS, you should integrate a modern SSH stack such as
swift-nio-ssh.Refer to the
TerminalAppsample code in the repository for implementation patterns:UIKitSshTerminalView.swift: Shows how to wire aTerminalViewto an SSH connection.SSHLoginView.swift: Provides a login UI for configuring connections.