migueldeicaza/swiftterm
SwiftTerm
Companion Apps
Tecolot is a MacOS Terminal.app replacement.
SwiftTermApp builds an actual iOS app that uses this library and is more complete than the testing apps in this module and provides a proper configuration UI. It is a proof of concept for what you would need to do.
Pane is a terminal multiplexor, similar to tmux.
Sample Code There are a couple of minimal sample apps for Mac and iOS showing how to use the library inside the TerminalApp directory.
- The sample Mac app has much of the functionality of MacOS' Terminal.app, but without the configuration UI.
- The sample iOS application uses an SSH library to connect to a remote system (as there is no native shell
on iOS to run) and includes a login UI to configure the connection.
History
This is a port of my original XtermSharp, which was itself based on xterm.js. At this point, I consider SwiftTerm to be a more advanced terminal emulator than both of those (modulo Selection/Accessibility) as it handles UTF, Unicode and grapheme clusters better than those and has a more complete coverage of terminal emulation. XtermSharp is generally attempting to keep up, but has lagged behind.
Plenty of test cases have been extracted from xterm.js and Ghostty and this also relies extensively on esctest to ensure compatibility.
Features ========
- Pretty decent terminal emulation, on or better than XtermSharp and xterm.js (and more comprehensive in many ways)
- Unicode rendering (including Emoji, and combining characters and emoji)
- Bidirectional text (Arabic, Hebrew) following the terminal-wg BiDi recommendation, with Arabic contextual shaping
- Reusable and pluggable engine allows multiple user interfaces to be built on top of it:
Bundled MacOS and iOS Bundled Headless terminal. TermKit contains a terminal-over-a-terminal Pane implements a terminal multiplexor
- Selection engine (with macOS support in the view)
- Search support with a built-in macOS find bar and programmable search APIs
- Supports colors (ANSI, 256, TrueColor)
- Supports text attributes including bold, italic, underline, strikethrough, and dim/faint (SGR 2)
- Supports mouse events
- Supports terminal resizing operations (controlled by remote host, or locally)
- Hyperlinks in terminal output
- Local process and SSH connection support (some assembly required for the last one)
- Proper CoreText rendering can munch through the hardened Unicode test suites.
- Graphics support:
Sixel (Use img2sixel to test) iTerm2-style graphic rendering (Use imgcat to test) * Kitty graphics (Use kittyimg to test)
- Terminal session recording and playback with termcast
- Thread-safe Terminal instances
- Fuzzed and abused
- Optional GPU-accelerated rendering via Metal (macOS, iOS, visionOS)
- Seems pretty fast to me
Image formats
SwiftTerm supports these image data formats:
- Sixel image streams.
- PNG images through the iTerm2 and Kitty graphics protocols.
- JPEG images through the iTerm2 graphics protocol.
- Raw RGB (24-bit) and RGBA (32-bit) pixel data through the Kitty
graphics protocol.
For iTerm2 images, the Apple views use the system image decoder. Other image formats that the target platform can decode can also work, but PNG and JPEG are the tested formats.
The SwiftTerm library itself contains the source code for both the engine and the front-ends. The front-ends are conditionally compiled based on the target platform.
Embedded Swift
SwiftTerm has a Foundation-free, headless core for Embedded Swift. Select it with the Embedded package trait. It omits the Apple views, local-process host, image decoders, and Kitty Clipboard. Embedded builds require Swift 6.3 or later. The portable host APIs are documented in the DocC guide. See the Embedded Swift build and integration guide for trait, compiler, and target settings.
WebAssembly
SwiftTerm supplies a browser and Node.js terminal engine with ABI version 1, copied render snapshots, generated reply bytes, and host-event queues. The TypeScript package loads either the Full or Embedded WASM reactor. The host supplies a renderer and PTY transport. The Full build supports PNG, Kitty, Sixel, iTerm graphics, and Kitty Clipboard; Embedded keeps terminal input, selection, scrollback, and bracketed paste while omitting graphics and clipboard support. A Canvas 2D example is included.
See the WebAssembly build and browser guide for the pinned compiler/SDK pair, build commands, API use, limits, and tests.
The Web terminal sample is a SwiftPM package with a Hummingbird server, WebSocket transport, and a local shell PTY. Run TerminalApp/WebTerminal/run.sh to start it on localhost.
The engine is in this directory, while code for macOS lives under Mac, and code for iOS, lives under iOS. Given that those two share a lot of common traits, the shared code is under Apple.
Using SwiftTerm
SwiftTerm uses the Swift Package Manager for its build, and you can add the library to your project by using the url for this project or a fork of it.
MacOS NSView
The macOS AppKit NSView implementation TerminalView is a reusable NSView control that can be connected to any source by implementing the TerminalViewDelegate. I anticipate that a common scenario will be to host a local Unix command, so I have included LocalProcessTerminalView which is an implementation that connects the TerminalView to a Unix pseudo-terminal and runs a command there.
iOS UIView
There is an equivalent UIKit UIView implementation for TerminalView which like its NSView companion is an embeddable and reusable view that can be connected to your application by implementing the same TerminalViewDelegate. Unlike the NSView case running on a Mac, where a common scenario will be to run local commands, given that iOS does not offer access to processes, the most common scenario will be to wire up this terminal to a remote host. And the safest way of connecting to a remote system is with SSH.
Using SSH
The core library currently does not provide a convenient way to connect to SSH, purely to avoid the additional dependency. The iOS sample app demonstrates how to integrate SSH using a modern SSH stack with swift-nio-ssh. See UIKitSshTerminalView and SSHLoginView for an example of connecting the TerminalView for iOS to an SSH connection.
Termcast - Terminal Recording and Playback
SwiftTerm includes a termcast command-line tool that can record and playback terminal sessions in the asciinema .cast format. This tool is built using SwiftTerm's LocalProcess functionality.
Recording Sessions
To record a terminal session:
swift run termcast record output.castOptions:
--command/-c: Specify a command to run (defaults to your shell)--timeout/-t: Set an automatic timeout in seconds
Examples:
# Record an interactive shell session
swift run termcast record my-session.cast
# Record a specific command
swift run termcast record -c "ls -la && echo 'Done'" command-demo.cast
# Record with a 30-second timeout
swift run termcast record --timeout 30 timed-session.castPlaying Back Sessions
To playback a recorded session:
swift run termcast playback my-session.castThe playback will show the recorded terminal session with proper timing, including both input and output as they occurred during recording.
Features
- Full input/output capture: Records both user input and program output with precise timing
- Raw terminal mode: Properly handles terminal control sequences and special keys
- asciinema compatibility: Uses the standard
.castformat for interoperability - Live display: Shows the session live while recording
- Proper terminal handling: Maintains correct line endings and terminal state
Working on SwiftTerm ====================
If you are using Xcode, there are two toplevel projects, one for Mac and one for iOS in the TerminalApp directory, one called "iOSTerminal.xcodeproj" and one called "MacTerminal.xcodeproj".
This is needed because Xcode does not provide code completion for iOS if you have a Mac project in the project. So I had to split them up. Both projects reference the same SwiftTerm package.
When working with these projects, if you choose the terminal application it will run this one. To run the test suite, select the 'SwiftTerm' target instead, and you can use 'SwiftTermFuzz' to run the fuzzer.
You can use swift build to build the package, and swift test to run the test suite. For better test coverage, fetch esctest, a conformance suite for terminal emulators:
make clone-esctest
swift testThis fetches a pinned commit of esctest2. testKnownGood then runs the esctest tests listed in Tests/SwiftTermTests/Fixtures/esctest-known-good.txt, and fails if one of them fails.
To run the whole suite and see every failure, run make esctest-report, which writes esctest.log. When a change makes more tests pass, make two logs and update the list with scripts/esctest-known-good.py; the script describes the steps.
If using Xcode, you can select the "SwiftTerm" project, and then use Command-U to run the test suite.
Bidirectional text (BiDi)
SwiftTerm implements the terminal-wg BiDi recommendation for right-to-left and mixed-direction text on the Apple views (both the CoreGraphics and Metal renderers):
- The buffer stays in logical order; each paragraph is reordered at render
time with the Unicode Bidirectional Algorithm, with Arabic contextual shaping, lam-alef ligatures, and bracket mirroring.
- All six presentation modes from the recommendation are supported:
implicit/explicit, fixed LTR/RTL, and autodetection from the first strong character. The default (implicit + autodetect + LTR fallback) renders RTL text correctly out of the box and leaves LTR output unchanged.
- Terminal applications control the behavior with the standard sequences:
BDSM (CSI 8 h/l), SCP (CSI Ps SP k), and DEC private modes 2501 (autodetection), 2500 (box-drawing mirroring), and 1243 (arrow-key swapping), including DECRQM queries and XTSAVE/XTRESTORE.
- Embedders can set the initial state through
TerminalOptions
(initialBidiState, initialBidiArrowKeySwap, maximumBidiParagraphRows), inspect it via Terminal.currentBidiState, and opt a view out entirely with TerminalView.bidiHostPolicy = .legacyLeftToRight.
The details are in the BiDi documentation.
BiDi visual test harness
The SwiftTerm BiDi harness is an AppKit app for visual BiDi tests. It shows SwiftTerm beside a WebKit reference. Its scenarios cover paragraph reflow, terminal modes, reset behavior, box mirroring, combining marks, selection, cursor movement, and scrollback.
Run it from the repository root:
Tools/BidiHarness/Scripts/run-harness.sh --artifacts /tmp/bidi-artifactsUse the controls in the app to select a scenario, move through its steps, resize the terminal, scroll, change the renderer, and save a capture. The app also has a local control socket for repeatable test runs. See the harness README for the control commands, Xcode instructions, artifact paths, and the macOS permission that Metal window capture needs.
Screenshots ===========
24 Bit Color
<img width="1246" alt="24 bit color" src="https://user-images.githubusercontent.com/36863/79060395-82181400-7c52-11ea-8f48-cd02323a8284.png">
Midnight Commander
<img width="969" alt="Screen Shot 2020-04-12 at 12 17 49 AM" src="https://user-images.githubusercontent.com/36863/79060466-49c50580-7c53-11ea-8514-bb4a31359662.png">
Solid UTF-8 support, excellent rendering: <img width="891" height="1699" alt="Image" src="https://github.com/user-attachments/assets/cb55eddb-ae96-417c-9fea-fb75faab2db2" /> <img width="891" height="1699" alt="Image" src="https://github.com/user-attachments/assets/ae468839-b8d2-402c-8174-d519a84647e1" />
Supports hyperlinks emitted by modern apps:
<img width="674" alt="image" src="https://user-images.githubusercontent.com/36863/80055972-0b500600-84f1-11ea-9c57-41cadce67162.png">
iOS support:
<img width="981" alt="image" src="https://user-images.githubusercontent.com/36863/80056069-54a05580-84f1-11ea-8597-5a227c9c64a7.png">
Sixel support:
<img width="770" alt="image" src="https://user-images.githubusercontent.com/36863/115647346-97a62c00-a2f1-11eb-929a-f9d942cc0c09.png">
<img width="568" alt="image" src="https://user-images.githubusercontent.com/36863/115647706-4e0a1100-a2f2-11eb-9bba-2a82503bca33.png">
Resources =========
- Digital's VT100 User Guide
- Terminal Guide - very nice and visual, but not normative
- Xterm Control Sequences
- VT510 Video Terminal Programmer Information
Additional and useful documents:
- VT330/VT340 Programmer Reference Manual Volume 2: Graphics Programming
- A parser for DEC’s ANSI-compatible video terminals
- Codes and Standards
- Linux Console Docs they are a subset of vt100, but often simple to follow.
- Sixel Graphics
Test suites:
- VTTest - old, but still good
- EscTest - fantastic: George Nachman, the author of iTerm, created this test suite, and it became a FreeDesktop standard. Since then, Thomas E. Dickey, the xterm maintainer and maintainer of many text apps has contributed to this effort.
Authors
- Thanks go to the xterm.js developers that originally wrote a terminal emulator
that was licensed under a license that allowed for maximum reuse.
- Marcin Krzyzanowski who masterfully improved and curated the rendering engine on AppKit/CoreText to be the glorious renderer that it is today - and for his contributions to the rendering engine
- Greg Munn that did a lot of work in XtermSharp to support the needs of Visual Studio for
Mac
- Anders Borum has contributed reliability fixes, the sixel parser and changes required to put SwiftTerm to use in production.
- Miguel de Icaza -me- who have been looking for an excuse to write some Swift code.
Package Metadata
Repository: migueldeicaza/swiftterm
Default branch: main
README: README.md