Terminal Surface

TerminalViewState

Main SwiftUI state container. Available on iOS 15, macOS 13, Mac Catalyst 15, and visionOS 1.

@MainActor
public final class TerminalViewState: ObservableObject {
    public convenience init()
    public convenience init(configFilePath: String?)
    public init(
        configSource: TerminalController.ConfigSource = .none,
        theme: TerminalTheme = .default,
        terminalConfiguration: TerminalConfiguration = .init()
    )
    public init(controller: TerminalController)

    @Published public internal(set) var title: String { get }
    @Published public internal(set) var surfaceSize: TerminalGridMetrics? { get }
    @Published public internal(set) var isFocused: Bool { get }
    @Published public internal(set) var bellCount: Int { get }
    @Published public internal(set) var lastBellAt: Date? { get }
    @Published public internal(set) var lastDesktopNotificationTitle: String? { get }
    @Published public internal(set) var lastDesktopNotificationBody: String? { get }
    @Published public internal(set) var lastDesktopNotificationAt: Date? { get }
    @Published public internal(set) var workingDirectory: String? { get }
    @Published public internal(set) var lastCommandExitCode: Int? { get }
    @Published public internal(set) var lastCommandDurationNanos: UInt64? { get }
    @Published public internal(set) var scrollbar: TerminalScrollbar? { get }
    public internal(set) weak var surface: TerminalSurface? { get }

    @Published public var configuration: TerminalSurfaceOptions
    /// False stops rendering for a surface kept mounted but hidden; the
    /// grid, scrollback, and session are kept.
    @Published public var isSurfaceVisible: Bool
    public var onClose: ((Bool) -> Void)?
    @Published public internal(set) var controller: TerminalController { get }

    /// The platform view presenting this state; nil while none does.
    public var attachedPlatformView: TerminalView? { get }
    /// Factory for a TerminalView subclass; read once, when the surface
    /// view is made.
    public var makePlatformView: (@MainActor () -> TerminalView)?
    #if canImport(UIKit)
    #if !targetEnvironment(macCatalyst)
    /// nil shows TerminalInputAccessoryItem.defaultItems; [] hides the bar.
    @Published public var inputAccessoryItems: [TerminalInputAccessoryItem]?
    #endif
    #endif

    /// Opt-in for the iOS long-press text-selection flow.
    public var onTextSelectionRequest: ((TerminalTextSelectionRequest) -> Void)?
    /// Presents OSC 52 reads/writes and flagged pastes; nil denies a
    /// program's request silently and allows a user paste.
    public var onClipboardConfirmationRequest: ((TerminalClipboardConfirmationRequest) -> Void)?

    public var renderedConfig: String { get }
    public var effectiveColorScheme: TerminalColorScheme { get }
    public var theme: TerminalTheme { get }
    public var terminalConfiguration: TerminalConfiguration { get }

    public func paste(text: String) -> Bool
    public func sendKey(_ press: TerminalKeyPress) -> Bool
    public func sendKey(_ key: TerminalKey, modifiers: TerminalInputModifiers = []) -> Bool
    @available(*, deprecated, renamed: "paste(text:)")
    public func send(_ text: String) -> Bool
    public func requestFocus()
    public func performBindingAction(_ action: String) -> Bool
    public func jumpToPrompt(by offset: Int16) -> Bool
    public func scrollToRow(_ row: UInt) -> Bool
    public var isMouseCaptured: Bool { get }
    public func sendMousePos(x: Double, y: Double, modifiers: TerminalInputModifiers = [])
    public func sendMouseButton(
        state: ghostty_input_mouse_state_e,
        button: ghostty_input_mouse_button_e,
        modifiers: TerminalInputModifiers = []
    ) -> Bool
    public func sendMouseScroll(
        x: Double,
        y: Double,
        mods: TerminalScrollModifiers = TerminalScrollModifiers(precision: true)
    )
    public func adopt(colorScheme: ColorScheme)
    public func adopt(terminalColorScheme colorScheme: TerminalColorScheme)
    public func setTheme(_ theme: TerminalTheme) -> Bool
    public func setTerminalConfiguration(_ configuration: TerminalConfiguration) -> Bool
}

controller exposes a projected publisher for source compatibility. SwiftUI integrations should observe configuration changes through TerminalViewState and mutate the view state rather than the controller.

TerminalSurfaceView

public struct TerminalSurfaceView: View {
    public init(context: TerminalViewState)
}

TerminalView

#if canImport(UIKit)
public typealias TerminalView = UITerminalView
#elseif canImport(AppKit)
public typealias TerminalView = AppTerminalView
#endif

TerminalController

@MainActor
public final class TerminalController {
    public enum ConfigSource: Sendable, Hashable {
        case none
        case file(String)
        case generated(String)
    }

    public var currentConfigSource: ConfigSource { get }
    public var renderedConfig: String { get }
    public private(set) var terminalConfiguration: TerminalConfiguration { get }
    public private(set) var theme: TerminalTheme { get }
    public private(set) var effectiveColorScheme: TerminalColorScheme { get }
    public internal(set) var lastConfigurationIssue: String? { get }

    public static let shared: TerminalController

    /// The default configuration.
    public convenience init()
    /// A fully custom configuration, rendered as the config source.
    public convenience init(
        configuration: TerminalConfiguration,
        theme: TerminalTheme = .default
    )
    /// Commands composed on top of the default configuration.
    public convenience init(
        theme: TerminalTheme = .default,
        configure: (inout TerminalConfiguration.Builder) -> Void
    )
    /// Loads the configuration from a file; nil means no file.
    public convenience init(
        configFilePath: String?,
        theme: TerminalTheme = .default
    )
    /// Low-level initialiser for full control over the config source.
    public init(
        configSource: ConfigSource = .none,
        theme: TerminalTheme = .default,
        terminalConfiguration: TerminalConfiguration = .init()
    )

    public func setColorScheme(_ scheme: TerminalColorScheme)
    public func setTheme(_ theme: TerminalTheme) -> Bool
    public func setTerminalConfiguration(_ configuration: TerminalConfiguration) -> Bool
    public func tick()
}

InMemoryTerminalSession

public final class InMemoryTerminalSession: @unchecked Sendable {
    public init(
        write: @escaping @Sendable (Data) -> Void,
        resize: @escaping @Sendable (InMemoryTerminalViewport) -> Void
    )

    public func readViewportText() -> String?
    public func receive(_ data: Data)
    public func receive(_ string: String)
    public func sendInput(_ data: Data)
    public func finish(exitCode: UInt32, runtimeMilliseconds: UInt64)
}

Delegates

@MainActor public protocol TerminalSurfaceTitleDelegate {
    func terminalDidChangeTitle(_ title: String)
}

@MainActor public protocol TerminalSurfaceGridResizeDelegate {
    func terminalDidResize(_ size: TerminalGridMetrics)
}

@MainActor public protocol TerminalSurfaceBellDelegate {
    func terminalDidRingBell()
}

@MainActor public protocol TerminalSurfaceCloseDelegate {
    func terminalDidClose(processAlive: Bool)
}

@MainActor public protocol TerminalSurfaceTextSelectionRequestDelegate {
    func terminalDidRequestTextSelection(_ request: TerminalTextSelectionRequest)
}

@MainActor public protocol TerminalSurfaceLifecycleDelegate {
    func terminalDidAttachSurface(_ surface: TerminalSurface)
    func terminalDidDetachSurface()
}