SwiftTerm Documentation

repository·main·Indexed 23 days ago

https://github.com/migueldeicaza/swiftterm

A 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.

Tokens
10.6K
Snippets
22
Records
66
Agent score
78%

What's inside SwiftTerm

  1. Key Features of SwiftTerm

    main

    SwiftTerm 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.
  2. How the Terminal class works

    main

    The Terminal class 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: Terminal instances are thread-safe. You can call feed(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.
  3. Wire a terminal view to a remote host over SSH

    main

    SwiftTerm 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 TerminalView and your chosen SSH library using the following pattern:

    1. User input to SSH: Implement TerminalViewDelegate/send(source:data:) to capture keystrokes and bytes from the terminal and write them to your SSH channel.
    2. 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.
    3. 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:) └──────────────┘
  4. Configure a Terminal with TerminalOptions

    main
    Use TerminalOptions to control the initial state and behavior of a Terminal or HeadlessTerminal instance. You can pass an options struct during construction to define dimensions, scrollback capacity, cursor appearance, and feature flags. For standard usage, use TerminalOptions.default to obtain sensible defaults (80x25 dimensions, 500-line scrollback, and a blinking block cursor).
  5. Use HeadlessTerminal for programmatic terminal automation

    main

    A HeadlessTerminal is a terminal emulator that runs a local process without a UI. It combines a Terminal engine with a LocalProcess, 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 terminal property to read buffer contents. To control the subprocess, use the process property.

  6. Connect TerminalView to a custom data source (SSH, Sockets, etc.)

    main

    To connect a terminal to a custom backend like SSH or a network socket, use TerminalView directly and implement the TerminalViewDelegate.

    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 (via UIScrollView).

    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) {}
    }
  7. How Kitty graphics protocol works

    main

    The 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 icat or timg:

    kitten icat image.png
    timg -pk image.png
  8. Configure Link Reporting and Activation

    main

    SwiftTerm can resolve links from two sources: Explicit links (OSC 8 hyperlinks) and Implicit links (URL-like text detected in content).

    Use the linkReporting property 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 is governed by linkHighlightMode. When a link is activated, TerminalView calls TerminalViewDelegate/requestOpenLink(source:link:params:).

    • Explicit links: link is the target; params contains parsed OSC 8 key/value pairs.
    • Implicit links: link is the detected URL text; params is 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 by UIPointerInteraction and UIHoverGestureRecognizer. Activation requires the Command key from a hardware keyboard for modifier-based modes.
    terminalView.linkReporting = .implicit // Default: explicit first, then implicit fallback
  9. How Sixel graphics support works

    main

    Sixel is a legacy inline graphics protocol that encodes images as six-pixel-tall rows. SwiftTerm parses Sixel data using the SixelDcsHandler and delivers the decoded raw RGBA pixel data to the front-end via the TerminalDelegate/createImageFromBitmap(source:bytes:width:height:) callback.

    To test Sixel support, you can use the img2sixel utility from the libsixel package:

    img2sixel image.png
  10. Integrate SSH with SwiftTerm on iOS

    main

    The 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 TerminalApp sample code in the repository for implementation patterns:

    • UIKitSshTerminalView.swift: Shows how to wire a TerminalView to an SSH connection.
    • SSHLoginView.swift: Provides a login UI for configuring connections.