初始提交:terminalX 可运行态(M0/M1/M1.5 已真机验证)

- M0: libghostty SSH 终端(渲染/输入/连接)+ 白屏修复(OutputGate) + 会话状态机/自动重连
- M1: tsnet 用户态组网 + SSH-over-tsnet(fd 桥),shell 级真机验证;R5(Go+gvisor+C+++Swift 同进程) retire
- M1.5: tmux -CC 原生 tab(MVP)
- 结构: packages/(TXCore·TXTransport), apps/TerminalX, vendor/(libghostty-spm/libssh2/mbedtls/tsnet-bridge), artifacts/
- 文档: CLAUDE.md + docs/HANDOFF.md(新会话入口)
- 环境: 认证代理→依赖 vendor 本地化;Go 在 ~/.local/go;仅模拟器/未签名
- 待续: M2 mosh, tmux 多 pane, M4 安全(host key/SE)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
kid
2026-07-24 10:20:46 +08:00
commit aa92d0e676
2761 changed files with 803505 additions and 0 deletions

1
vendor/libghostty-spm/docs/.nojekyll vendored Normal file
View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,158 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>GhosttyKit · API · Configuration</title>
<link rel="stylesheet" href="../styles.css">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github.min.css"
integrity="sha384-eFTL69TLRZTkNfYZOLM+G04821K1qZao/4QLJbet1pP4tcF+fdXq/9CdqAbWRl/L"
crossorigin="anonymous"
media="(prefers-color-scheme: light)">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github-dark.min.css"
integrity="sha384-wH75j6z1lH97ZOpMOInqhgKzFkAInZPPSPlZpYKYTOqsaizPvhQZmAtLcPKXpLyH"
crossorigin="anonymous"
media="(prefers-color-scheme: dark)">
</head>
<body>
<nav class="topbar" aria-label="Primary">
<a class="home" href="../index.html">GhosttyKit</a>
<a href="../index.html">Overview</a>
<a href="../guide/index.html">Guide</a>
<a href="index.html" aria-current="page">API</a>
<a href="../architecture.html">Architecture</a>
<a href="../build.html">Build</a>
</nav>
<main>
<h1>Configuration</h1>
<nav class="on-this-page" aria-label="On this page">
<strong>On this page:</strong>
<a href="#TerminalSurfaceOptions">Surface options</a>
<a href="#TerminalConfiguration">Terminal config</a>
<a href="#TerminalTheme">Theme</a>
<a href="#Metrics">Metrics</a>
</nav>
<h2 id="TerminalSurfaceOptions">TerminalSurfaceOptions</h2>
<pre><code class="language-swift">public struct TerminalSurfaceOptions: Sendable {
public var backend: TerminalSessionBackend
public var fontSize: Float?
public var workingDirectory: String?
public var context: TerminalSurfaceContext
public init(
backend: TerminalSessionBackend = .exec,
fontSize: Float? = nil,
workingDirectory: String? = nil,
context: TerminalSurfaceContext = .window
)
}
public enum TerminalSessionBackend: Sendable {
case exec
case inMemory(InMemoryTerminalSession)
}
public enum TerminalSurfaceContext: Sendable, Equatable {
case window
case split
}
</code></pre>
<h2 id="TerminalConfiguration">TerminalConfiguration</h2>
<pre><code class="language-swift">public struct TerminalConfiguration: Sendable, Hashable {
public init()
public init(configure: (inout Builder) -&gt; Void)
public init(
startingFrom base: TerminalConfiguration,
configure: (inout Builder) -&gt; Void
)
public func appending(_ command: TerminalConfigCommand) -&gt; TerminalConfiguration
public func fontFamily(_ value: String) -&gt; TerminalConfiguration
public func fontSize(_ value: Float) -&gt; TerminalConfiguration
public func cursorStyle(_ style: TerminalCursorStyle) -&gt; TerminalConfiguration
public func background(_ value: String) -&gt; TerminalConfiguration
public func foreground(_ value: String) -&gt; TerminalConfiguration
public func palette(_ index: Int, color: String) -&gt; TerminalConfiguration
public func backgroundOpacity(_ value: Double) -&gt; TerminalConfiguration
public func windowPaddingX(_ value: Int) -&gt; TerminalConfiguration
public func windowPaddingY(_ value: Int) -&gt; TerminalConfiguration
public func custom(_ key: String, _ value: String) -&gt; TerminalConfiguration
public static let `default`: TerminalConfiguration
public var rendered: String { get }
}
</code></pre>
<p>The builder variant has matching <code>with*</code> mutating methods, which are useful when composing a default config with many overrides.</p>
<h2 id="TerminalTheme">TerminalTheme</h2>
<pre><code class="language-swift">public enum TerminalColorScheme: Sendable {
case light
case dark
}
public struct TerminalTheme: Sendable, Hashable {
public var light: TerminalConfiguration
public var dark: TerminalConfiguration
public init(
light: TerminalConfiguration,
dark: TerminalConfiguration
)
}
</code></pre>
<p><code>TerminalController</code> selects the light or dark configuration when the active color scheme changes.</p>
<h2 id="Metrics">Metrics</h2>
<pre><code class="language-swift">public struct TerminalGridMetrics: Sendable, Equatable {
public var columns: UInt16
public var rows: UInt16
public var widthPixels: UInt32
public var heightPixels: UInt32
public var cellWidthPixels: UInt32
public var cellHeightPixels: UInt32
}
public struct InMemoryTerminalViewport: Sendable, Equatable {
public var columns: UInt16
public var rows: UInt16
public var widthPixels: UInt32
public var heightPixels: UInt32
public var cellWidthPixels: UInt32
public var cellHeightPixels: UInt32
}
public struct TerminalInputModifiers: OptionSet, Sendable {
public static let shift: TerminalInputModifiers
public static let ctrl: TerminalInputModifiers
public static let alt: TerminalInputModifiers
public static let super_: TerminalInputModifiers
}
</code></pre>
<nav class="pager" aria-label="Sibling pages">
<a class="prev" href="terminal.html">Terminal surface</a>
<a class="parent" href="index.html">↑ API</a>
<a class="next" href="themes.html">Theme catalog</a>
</nav>
<footer>
GhosttyKit · <a href="https://github.com/Lakr233/libghostty-spm">github.com/Lakr233/libghostty-spm</a>
</footer>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/highlight.min.js"
integrity="sha384-GdEWAbCjn+ghjX0gLx7/N1hyTVmPAjdC2OvoAA0RyNcAOhqwtT8qnbCxWle2+uJX"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/swift.min.js"
integrity="sha384-TfALNLT6HJzZieazgsVvFM0DzFWQsgl0d7mdwPLyg1yg7XE4QwLY4jqmJRNnI1S4"
crossorigin="anonymous"></script>
<script>hljs.highlightAll();</script>
</body>
</html>

View File

@@ -0,0 +1,58 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>GhosttyKit · API</title>
<link rel="stylesheet" href="../styles.css">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github.min.css"
integrity="sha384-eFTL69TLRZTkNfYZOLM+G04821K1qZao/4QLJbet1pP4tcF+fdXq/9CdqAbWRl/L"
crossorigin="anonymous"
media="(prefers-color-scheme: light)">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github-dark.min.css"
integrity="sha384-wH75j6z1lH97ZOpMOInqhgKzFkAInZPPSPlZpYKYTOqsaizPvhQZmAtLcPKXpLyH"
crossorigin="anonymous"
media="(prefers-color-scheme: dark)">
</head>
<body>
<nav class="topbar" aria-label="Primary">
<a class="home" href="../index.html">GhosttyKit</a>
<a href="../index.html">Overview</a>
<a href="../guide/index.html">Guide</a>
<a href="index.html" aria-current="page">API</a>
<a href="../architecture.html">Architecture</a>
<a href="../build.html">Build</a>
</nav>
<main>
<h1>API reference</h1>
<p class="lede">Public Swift surface grouped by product. Signatures are trimmed to the integration points most hosts need first.</p>
<ul class="hub-list">
<li>
<a href="terminal.html">Terminal surface</a>
<span class="summary"><code>TerminalViewState</code>, <code>TerminalSurfaceView</code>, <code>TerminalView</code>, <code>TerminalController</code>, <code>InMemoryTerminalSession</code>, delegates.</span>
</li>
<li>
<a href="configuration.html">Configuration</a>
<span class="summary"><code>TerminalConfiguration</code>, <code>TerminalConfigCommand</code>, <code>TerminalTheme</code>, <code>TerminalColorScheme</code>, metrics and modifiers.</span>
</li>
<li>
<a href="themes.html">Theme catalog</a>
<span class="summary"><code>GhosttyThemeCatalog</code>, <code>GhosttyThemeDefinition</code>, conversion helpers, generated theme constants.</span>
</li>
<li>
<a href="shellcraft.html">ShellCraftKit</a>
<span class="summary"><code>ShellDefinition</code>, <code>ShellCommand</code>, <code>ShellSession</code>, <code>CommandResult</code>.</span>
</li>
</ul>
<footer>
GhosttyKit · <a href="https://github.com/Lakr233/libghostty-spm">github.com/Lakr233/libghostty-spm</a>
</footer>
</main>
</body>
</html>

View File

@@ -0,0 +1,111 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>GhosttyKit · API · ShellCraftKit</title>
<link rel="stylesheet" href="../styles.css">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github.min.css"
integrity="sha384-eFTL69TLRZTkNfYZOLM+G04821K1qZao/4QLJbet1pP4tcF+fdXq/9CdqAbWRl/L"
crossorigin="anonymous"
media="(prefers-color-scheme: light)">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github-dark.min.css"
integrity="sha384-wH75j6z1lH97ZOpMOInqhgKzFkAInZPPSPlZpYKYTOqsaizPvhQZmAtLcPKXpLyH"
crossorigin="anonymous"
media="(prefers-color-scheme: dark)">
</head>
<body>
<nav class="topbar" aria-label="Primary">
<a class="home" href="../index.html">GhosttyKit</a>
<a href="../index.html">Overview</a>
<a href="../guide/index.html">Guide</a>
<a href="index.html" aria-current="page">API</a>
<a href="../architecture.html">Architecture</a>
<a href="../build.html">Build</a>
</nav>
<main>
<h1>ShellCraftKit</h1>
<nav class="on-this-page" aria-label="On this page">
<strong>On this page:</strong>
<a href="#ShellDefinition">ShellDefinition</a>
<a href="#ShellCommand">ShellCommand</a>
<a href="#ShellSession">ShellSession</a>
</nav>
<h2 id="ShellDefinition">ShellDefinition</h2>
<pre><code class="language-swift">public struct ShellDefinition: Sendable {
public let prompt: String
public let welcomeMessage: String
public let fallbackMessage: @Sendable (String) -&gt; String
public init(
prompt: String = "$ ",
welcomeMessage: String? = nil,
fallback: (@Sendable (String) -&gt; String)? = nil,
@ShellCommandBuilder _ build: () -&gt; [ShellCommand]
)
}
</code></pre>
<h2 id="ShellCommand">ShellCommand</h2>
<pre><code class="language-swift">public enum CommandResult: Sendable {
case output(String)
case clear
case exit
}
public struct CommandContext: Sendable {
public let command: String
public let arguments: String
public let username: String
public let terminalSize: InMemoryTerminalViewport
}
public struct ShellCommand: Sendable {
public let name: String
public let summary: String
public init(
_ name: String,
summary: String = "",
handler: @escaping @Sendable (CommandContext) -&gt; CommandResult
)
}
</code></pre>
<h2 id="ShellSession">ShellSession</h2>
<pre><code class="language-swift">public final class ShellSession {
public let terminalSession: InMemoryTerminalSession
public init(shell: ShellDefinition)
public func start()
}
public let defaultSandboxShell: ShellDefinition
public let sandboxShell: ShellDefinition
</code></pre>
<nav class="pager" aria-label="Sibling pages">
<a class="prev" href="themes.html">Theme catalog</a>
<a class="parent" href="index.html">↑ API</a>
<a class="next" href="../architecture.html">Architecture</a>
</nav>
<footer>
GhosttyKit · <a href="https://github.com/Lakr233/libghostty-spm">github.com/Lakr233/libghostty-spm</a>
</footer>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/highlight.min.js"
integrity="sha384-GdEWAbCjn+ghjX0gLx7/N1hyTVmPAjdC2OvoAA0RyNcAOhqwtT8qnbCxWle2+uJX"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/swift.min.js"
integrity="sha384-TfALNLT6HJzZieazgsVvFM0DzFWQsgl0d7mdwPLyg1yg7XE4QwLY4jqmJRNnI1S4"
crossorigin="anonymous"></script>
<script>hljs.highlightAll();</script>
</body>
</html>

View File

@@ -0,0 +1,181 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>GhosttyKit · API · Terminal surface</title>
<link rel="stylesheet" href="../styles.css">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github.min.css"
integrity="sha384-eFTL69TLRZTkNfYZOLM+G04821K1qZao/4QLJbet1pP4tcF+fdXq/9CdqAbWRl/L"
crossorigin="anonymous"
media="(prefers-color-scheme: light)">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github-dark.min.css"
integrity="sha384-wH75j6z1lH97ZOpMOInqhgKzFkAInZPPSPlZpYKYTOqsaizPvhQZmAtLcPKXpLyH"
crossorigin="anonymous"
media="(prefers-color-scheme: dark)">
</head>
<body>
<nav class="topbar" aria-label="Primary">
<a class="home" href="../index.html">GhosttyKit</a>
<a href="../index.html">Overview</a>
<a href="../guide/index.html">Guide</a>
<a href="index.html" aria-current="page">API</a>
<a href="../architecture.html">Architecture</a>
<a href="../build.html">Build</a>
</nav>
<main>
<h1>Terminal surface</h1>
<nav class="on-this-page" aria-label="On this page">
<strong>On this page:</strong>
<a href="#TerminalViewState">TerminalViewState</a>
<a href="#TerminalSurfaceView">TerminalSurfaceView</a>
<a href="#TerminalView">TerminalView</a>
<a href="#TerminalController">TerminalController</a>
<a href="#InMemoryTerminalSession">InMemoryTerminalSession</a>
<a href="#Delegates">Delegates</a>
</nav>
<h2 id="TerminalViewState">TerminalViewState</h2>
<p>Main SwiftUI state container. Available on iOS 15, macOS 13, and Mac Catalyst 15.</p>
<pre><code class="language-swift">@MainActor
public final class TerminalViewState: ObservableObject {
@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 }
public internal(set) weak var surface: TerminalSurface? { get }
@Published public var configuration: TerminalSurfaceOptions
public var onClose: ((Bool) -&gt; Void)?
@Published public internal(set) var controller: TerminalController { get }
public var renderedConfig: String { get }
public var effectiveColorScheme: TerminalColorScheme { get }
public var theme: TerminalTheme { get }
public var terminalConfiguration: TerminalConfiguration { get }
public func send(_ text: String) -&gt; Bool
public func adopt(colorScheme: ColorScheme)
public func adopt(terminalColorScheme colorScheme: TerminalColorScheme)
public func setTheme(_ theme: TerminalTheme) -&gt; Bool
public func setTerminalConfiguration(_ configuration: TerminalConfiguration) -&gt; Bool
}
</code></pre>
<p class="muted"><code>controller</code> exposes a projected publisher for source compatibility, but controller-owned configuration changes are observable through <code>TerminalViewState</code> mutation methods. SwiftUI integrations should mutate through the view state rather than the controller directly.</p>
<h2 id="TerminalSurfaceView">TerminalSurfaceView</h2>
<pre><code class="language-swift">public struct TerminalSurfaceView: View {
public init(context: TerminalViewState)
}
</code></pre>
<h2 id="TerminalView">TerminalView</h2>
<pre><code class="language-swift">#if canImport(UIKit)
public typealias TerminalView = UITerminalView
#elseif canImport(AppKit)
public typealias TerminalView = AppTerminalView
#endif
</code></pre>
<h2 id="TerminalController">TerminalController</h2>
<pre><code class="language-swift">@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 init(
configSource: ConfigSource = .none,
theme: TerminalTheme = .default,
terminalConfiguration: TerminalConfiguration = .init()
)
public func setColorScheme(_ scheme: TerminalColorScheme)
public func setTheme(_ theme: TerminalTheme) -&gt; Bool
public func setTerminalConfiguration(_ configuration: TerminalConfiguration) -&gt; Bool
public func tick()
}
</code></pre>
<h2 id="InMemoryTerminalSession">InMemoryTerminalSession</h2>
<pre><code class="language-swift">public final class InMemoryTerminalSession: @unchecked Sendable {
public init(
write: @escaping @Sendable (Data) -&gt; Void,
resize: @escaping @Sendable (InMemoryTerminalViewport) -&gt; Void
)
public func readViewportText() -&gt; String?
public func receive(_ data: Data)
public func receive(_ string: String)
public func sendInput(_ data: Data)
public func finish(exitCode: UInt32, runtimeMilliseconds: UInt64)
}
</code></pre>
<h2 id="Delegates">Delegates</h2>
<pre><code class="language-swift">@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()
}
</code></pre>
<nav class="pager" aria-label="Sibling pages">
<a class="prev" href="index.html">API hub</a>
<a class="parent" href="index.html">↑ API</a>
<a class="next" href="configuration.html">Configuration</a>
</nav>
<footer>
GhosttyKit · <a href="https://github.com/Lakr233/libghostty-spm">github.com/Lakr233/libghostty-spm</a>
</footer>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/highlight.min.js"
integrity="sha384-GdEWAbCjn+ghjX0gLx7/N1hyTVmPAjdC2OvoAA0RyNcAOhqwtT8qnbCxWle2+uJX"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/swift.min.js"
integrity="sha384-TfALNLT6HJzZieazgsVvFM0DzFWQsgl0d7mdwPLyg1yg7XE4QwLY4jqmJRNnI1S4"
crossorigin="anonymous"></script>
<script>hljs.highlightAll();</script>
</body>
</html>

View File

@@ -0,0 +1,88 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>GhosttyKit · API · Theme catalog</title>
<link rel="stylesheet" href="../styles.css">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github.min.css"
integrity="sha384-eFTL69TLRZTkNfYZOLM+G04821K1qZao/4QLJbet1pP4tcF+fdXq/9CdqAbWRl/L"
crossorigin="anonymous"
media="(prefers-color-scheme: light)">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github-dark.min.css"
integrity="sha384-wH75j6z1lH97ZOpMOInqhgKzFkAInZPPSPlZpYKYTOqsaizPvhQZmAtLcPKXpLyH"
crossorigin="anonymous"
media="(prefers-color-scheme: dark)">
</head>
<body>
<nav class="topbar" aria-label="Primary">
<a class="home" href="../index.html">GhosttyKit</a>
<a href="../index.html">Overview</a>
<a href="../guide/index.html">Guide</a>
<a href="index.html" aria-current="page">API</a>
<a href="../architecture.html">Architecture</a>
<a href="../build.html">Build</a>
</nav>
<main>
<h1>Theme catalog</h1>
<nav class="on-this-page" aria-label="On this page">
<strong>On this page:</strong>
<a href="#GhosttyThemeCatalog">Catalog</a>
<a href="#GhosttyThemeDefinition">Definition</a>
<a href="#Conversion">Conversion</a>
</nav>
<h2 id="GhosttyThemeCatalog">GhosttyThemeCatalog</h2>
<pre><code class="language-swift">public enum GhosttyThemeCatalog {
public static let allThemes: [GhosttyThemeDefinition]
public static func theme(named name: String) -&gt; GhosttyThemeDefinition?
public static func search(_ query: String) -&gt; [GhosttyThemeDefinition]
}
</code></pre>
<h2 id="GhosttyThemeDefinition">GhosttyThemeDefinition</h2>
<pre><code class="language-swift">public struct GhosttyThemeDefinition: Sendable, Hashable, Identifiable {
public var id: String { get }
public let name: String
public let background: String
public let foreground: String
public let cursorColor: String?
public let cursorText: String?
public let selectionBackground: String?
public let selectionForeground: String?
public let palette: [Int: String]
}
</code></pre>
<h2 id="Conversion">Conversion helpers</h2>
<pre><code class="language-swift">public extension GhosttyThemeDefinition {
func toTerminalConfiguration() -&gt; TerminalConfiguration
func toTerminalTheme() -&gt; TerminalTheme
var isDark: Bool { get }
}
</code></pre>
<nav class="pager" aria-label="Sibling pages">
<a class="prev" href="configuration.html">Configuration</a>
<a class="parent" href="index.html">↑ API</a>
<a class="next" href="shellcraft.html">ShellCraftKit</a>
</nav>
<footer>
GhosttyKit · <a href="https://github.com/Lakr233/libghostty-spm">github.com/Lakr233/libghostty-spm</a>
</footer>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/highlight.min.js"
integrity="sha384-GdEWAbCjn+ghjX0gLx7/N1hyTVmPAjdC2OvoAA0RyNcAOhqwtT8qnbCxWle2+uJX"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/swift.min.js"
integrity="sha384-TfALNLT6HJzZieazgsVvFM0DzFWQsgl0d7mdwPLyg1yg7XE4QwLY4jqmJRNnI1S4"
crossorigin="anonymous"></script>
<script>hljs.highlightAll();</script>
</body>
</html>

View File

@@ -0,0 +1,105 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>GhosttyKit · Architecture</title>
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github.min.css"
integrity="sha384-eFTL69TLRZTkNfYZOLM+G04821K1qZao/4QLJbet1pP4tcF+fdXq/9CdqAbWRl/L"
crossorigin="anonymous"
media="(prefers-color-scheme: light)">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github-dark.min.css"
integrity="sha384-wH75j6z1lH97ZOpMOInqhgKzFkAInZPPSPlZpYKYTOqsaizPvhQZmAtLcPKXpLyH"
crossorigin="anonymous"
media="(prefers-color-scheme: dark)">
</head>
<body>
<nav class="topbar" aria-label="Primary">
<a class="home" href="index.html">GhosttyKit</a>
<a href="index.html">Overview</a>
<a href="guide/index.html">Guide</a>
<a href="api/index.html">API</a>
<a href="architecture.html" aria-current="page">Architecture</a>
<a href="build.html">Build</a>
</nav>
<main>
<h1>Architecture</h1>
<p class="lede">How the package wraps Ghostty, owns terminal state, bridges native input, and keeps sandboxed apps in control of I/O.</p>
<h2 id="layers">Layers</h2>
<pre><code>libghostty Zig/C Static library inside GhosttyKit.xcframework.
|
GhosttyKit Swift Re-export of ghostty.h for direct C API access.
|
GhosttyTerminal Swift Controller, configuration, native views, input, surface lifecycle.
|
GhosttyTheme Swift Theme catalog and TerminalConfiguration conversion helpers.
|
ShellCraftKit Swift Sandboxed command engine backed by InMemoryTerminalSession.</code></pre>
<h2 id="controller">Controller and surface</h2>
<p><code>TerminalController</code> is the source of truth for Ghostty app lifecycle and resolved configuration. It composes the base config source, per-session <code>TerminalConfiguration</code>, active <code>TerminalTheme</code>, and current color scheme into one rendered Ghostty config.</p>
<p>A platform view asks the controller to create a <code>TerminalSurface</code>. The surface owns the bridge to Ghostty&rsquo;s renderer, callback table, display link, and host-managed I/O hooks.</p>
<h2 id="platforms">Platform views</h2>
<table>
<thead><tr><th>Platform</th><th>Type</th><th>Input model</th></tr></thead>
<tbody>
<tr><td>iOS / Catalyst</td><td><code>UITerminalView</code></td><td><code>UITextInput</code>, hardware presses, touch scrolling, input accessory bar.</td></tr>
<tr><td>macOS</td><td><code>AppTerminalView</code></td><td><code>NSTextInputClient</code>, key events, mouse and scroll events.</td></tr>
<tr><td>SwiftUI</td><td><code>TerminalSurfaceView</code></td><td>Wraps the platform view and syncs SwiftUI color scheme.</td></tr>
</tbody>
</table>
<h2 id="io">Host-managed I/O</h2>
<p>Example apps use <code>GHOSTTY_SURFACE_IO_BACKEND_HOST_MANAGED</code>. Ghostty produces user input bytes through callbacks, and the host feeds output bytes back through <code>ghostty_surface_write_buffer</code>. <code>InMemoryTerminalSession</code> owns this bridge for Swift callers.</p>
<h2 id="ios-input">iOS input chain</h2>
<ol>
<li>Hardware keys arrive through <code>pressesBegan</code> and are translated into <code>ghostty_input_key_s</code>.</li>
<li>Software keyboard text arrives through <code>insertText(_:)</code> and deletion through <code>deleteBackward()</code>.</li>
<li>Marked text flows through <code>TerminalTextInputHandler</code> and calls Ghostty preedit APIs.</li>
<li>The accessory bar dispatches terminal keys and sticky Ctrl/Alt/Cmd modifiers.</li>
<li>Long-press selection requests are delegated to the host with a viewport snapshot.</li>
</ol>
<h2 id="trimmed-build">Trimmed build</h2>
<table>
<thead><tr><th>Component</th><th>Bundled state</th><th>Reason</th></tr></thead>
<tbody>
<tr><td>Terminal core</td><td>Included</td><td>VT parser, state machine, grid.</td></tr>
<tr><td>Metal renderer</td><td>Included</td><td>CAMetalLayer / IOSurface rendering.</td></tr>
<tr><td>CoreText font backend</td><td>Included</td><td>Apple text shaping and rasterization.</td></tr>
<tr><td>Custom shaders</td><td>Disabled</td><td>Embedded apps get a smaller dependency graph.</td></tr>
<tr><td>Inspector UI</td><td>Disabled</td><td>Host apps own diagnostics UI.</td></tr>
<tr><td>Native app runtime</td><td>Disabled</td><td>UIKit/AppKit/SwiftUI hosts provide the runtime.</td></tr>
<tr><td>Host-managed I/O backend</td><td>Added</td><td>Sandbox-friendly terminal transport.</td></tr>
<tr><td>iOS platform fixes</td><td>Added</td><td>Metal row alignment, simulator fixes, deployment-target adjustments.</td></tr>
</tbody>
</table>
<h2 id="rules">Architecture rules</h2>
<ul>
<li><code>TerminalController</code> owns the Ghostty app object and config resolution.</li>
<li>Platform views own native input and layer sizing.</li>
<li><code>TerminalSurface</code> owns Ghostty surface lifetime and display-link ticks.</li>
<li>Host apps own process execution and byte transport for sandboxed demos.</li>
<li>Catalyst follows the UIKit branch in platform conditionals.</li>
</ul>
<footer>
GhosttyKit · <a href="https://github.com/Lakr233/libghostty-spm">github.com/Lakr233/libghostty-spm</a>
</footer>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/highlight.min.js"
integrity="sha384-GdEWAbCjn+ghjX0gLx7/N1hyTVmPAjdC2OvoAA0RyNcAOhqwtT8qnbCxWle2+uJX"
crossorigin="anonymous"></script>
<script>hljs.highlightAll();</script>
</body>
</html>

84
vendor/libghostty-spm/docs/build.html vendored Normal file
View File

@@ -0,0 +1,84 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>GhosttyKit · Build</title>
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github.min.css"
integrity="sha384-eFTL69TLRZTkNfYZOLM+G04821K1qZao/4QLJbet1pP4tcF+fdXq/9CdqAbWRl/L"
crossorigin="anonymous"
media="(prefers-color-scheme: light)">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github-dark.min.css"
integrity="sha384-wH75j6z1lH97ZOpMOInqhgKzFkAInZPPSPlZpYKYTOqsaizPvhQZmAtLcPKXpLyH"
crossorigin="anonymous"
media="(prefers-color-scheme: dark)">
</head>
<body>
<nav class="topbar" aria-label="Primary">
<a class="home" href="index.html">GhosttyKit</a>
<a href="index.html">Overview</a>
<a href="guide/index.html">Guide</a>
<a href="api/index.html">API</a>
<a href="architecture.html">Architecture</a>
<a href="build.html" aria-current="page">Build</a>
</nav>
<main>
<h1>Build</h1>
<p class="lede">Local package checks, binary rebuild flow, theme generation, and GitHub Pages publishing setup.</p>
<h2 id="package">Package checks</h2>
<pre><code class="language-bash">swift build
swift test
./Script/test.sh
</code></pre>
<p><code>./Script/test.sh</code> verifies the package across macOS, iOS, iOS Simulator, and Mac Catalyst destinations.</p>
<h2 id="xcframework">Rebuild libghostty</h2>
<pre><code class="language-bash">./Script/build.sh
./Script/build.sh \
--platforms macos,ios \
--source /path/to/ghostty \
--skip-tests
</code></pre>
<p>The build scripts apply patches from <code>Patches/ghostty</code>, build target libraries with Zig, merge them into <code>GhosttyKit.xcframework</code>, and update release artifacts.</p>
<h2 id="manifest">Release manifest</h2>
<pre><code class="language-bash">./Script/build-manifest.sh
</code></pre>
<p>When products, targets, or binary metadata change, keep <code>Package.swift</code>, <code>Package.local.swift</code>, and <code>Package.swift.template</code> aligned.</p>
<h2 id="themes">Theme generation</h2>
<pre><code class="language-bash">./Script/generate-themes.sh
</code></pre>
<p>This regenerates <code>Sources/GhosttyTheme/Themes</code> from iTerm2-Color-Schemes.</p>
<h2 id="pages">GitHub Pages</h2>
<p>The documentation site is a static site under <code>docs/</code>, matching the SSHKit Pages layout. <code>.github/workflows/pages.yml</code> publishes it with GitHub Actions on every <code>main</code> push that changes docs. <code>docs/.nojekyll</code> keeps GitHub Pages serving files exactly as written.</p>
<h2 id="local">Local preview</h2>
<pre><code class="language-bash">cd docs
python3 -m http.server 8000
</code></pre>
<footer>
GhosttyKit · <a href="https://github.com/Lakr233/libghostty-spm">github.com/Lakr233/libghostty-spm</a>
</footer>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/highlight.min.js"
integrity="sha384-GdEWAbCjn+ghjX0gLx7/N1hyTVmPAjdC2OvoAA0RyNcAOhqwtT8qnbCxWle2+uJX"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/bash.min.js"
integrity="sha384-83HvQQdGTWqnRLmgm19NjCb1+/awKJGytUX9sm3HJb2ddZ9Fs1Bst2bZogFjt9rr"
crossorigin="anonymous"></script>
<script>hljs.highlightAll();</script>
</body>
</html>

View File

@@ -0,0 +1,94 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>GhosttyKit · Host-managed I/O</title>
<link rel="stylesheet" href="../styles.css">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github.min.css"
integrity="sha384-eFTL69TLRZTkNfYZOLM+G04821K1qZao/4QLJbet1pP4tcF+fdXq/9CdqAbWRl/L"
crossorigin="anonymous"
media="(prefers-color-scheme: light)">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github-dark.min.css"
integrity="sha384-wH75j6z1lH97ZOpMOInqhgKzFkAInZPPSPlZpYKYTOqsaizPvhQZmAtLcPKXpLyH"
crossorigin="anonymous"
media="(prefers-color-scheme: dark)">
</head>
<body>
<nav class="topbar" aria-label="Primary">
<a class="home" href="../index.html">GhosttyKit</a>
<a href="../index.html">Overview</a>
<a href="index.html" aria-current="page">Guide</a>
<a href="../api/index.html">API</a>
<a href="../architecture.html">Architecture</a>
<a href="../build.html">Build</a>
</nav>
<main>
<h1>Host-managed I/O</h1>
<nav class="on-this-page" aria-label="On this page">
<strong>On this page:</strong>
<a href="#session">Session</a>
<a href="#output">Output</a>
<a href="#viewport">Viewport</a>
<a href="#exit">Exit</a>
</nav>
<p><code>InMemoryTerminalSession</code> connects Ghostty&rsquo;s host-managed backend to your own process, emulator, or command engine. The host supplies output with <code>receive</code>, and Ghostty sends user input through the <code>write</code> closure.</p>
<h2 id="session">Create a session</h2>
<pre><code class="language-swift">let session = InMemoryTerminalSession(
write: { data in
backend.handleInput(data)
},
resize: { viewport in
backend.resize(columns: viewport.columns, rows: viewport.rows)
}
)
terminal.configuration = TerminalSurfaceOptions(
backend: .inMemory(session)
)
</code></pre>
<h2 id="output">Send host output</h2>
<p>Use UTF-8 strings for simple demos and <code>Data</code> for exact byte streams.</p>
<pre><code class="language-swift">session.receive("hello from host\r\n")
session.receive(Data([0x1B, 0x5B, 0x32, 0x4A])) // ESC [ 2 J
</code></pre>
<h2 id="viewport">Read viewport text</h2>
<p><code>readViewportText()</code> returns the visible grid as a UTF-8 string. It reads viewport rows and separates them with <code>\n</code>.</p>
<pre><code class="language-swift">if let text = session.readViewportText() {
print(text)
}
</code></pre>
<h2 id="exit">Finish a process</h2>
<p>Tell Ghostty that the host-managed process has exited so lifecycle delegates and shell integration receive the close event.</p>
<pre><code class="language-swift">session.finish(exitCode: 0, runtimeMilliseconds: 1234)
</code></pre>
<nav class="pager" aria-label="Sibling pages">
<a class="prev" href="uikit-appkit.html">UIKit &amp; AppKit</a>
<a class="parent" href="index.html">↑ Guide</a>
<a class="next" href="themes.html">Themes</a>
</nav>
<footer>
GhosttyKit · <a href="https://github.com/Lakr233/libghostty-spm">github.com/Lakr233/libghostty-spm</a>
</footer>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/highlight.min.js"
integrity="sha384-GdEWAbCjn+ghjX0gLx7/N1hyTVmPAjdC2OvoAA0RyNcAOhqwtT8qnbCxWle2+uJX"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/swift.min.js"
integrity="sha384-TfALNLT6HJzZieazgsVvFM0DzFWQsgl0d7mdwPLyg1yg7XE4QwLY4jqmJRNnI1S4"
crossorigin="anonymous"></script>
<script>hljs.highlightAll();</script>
</body>
</html>

View File

@@ -0,0 +1,66 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>GhosttyKit · Guide</title>
<link rel="stylesheet" href="../styles.css">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github.min.css"
integrity="sha384-eFTL69TLRZTkNfYZOLM+G04821K1qZao/4QLJbet1pP4tcF+fdXq/9CdqAbWRl/L"
crossorigin="anonymous"
media="(prefers-color-scheme: light)">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github-dark.min.css"
integrity="sha384-wH75j6z1lH97ZOpMOInqhgKzFkAInZPPSPlZpYKYTOqsaizPvhQZmAtLcPKXpLyH"
crossorigin="anonymous"
media="(prefers-color-scheme: dark)">
</head>
<body>
<nav class="topbar" aria-label="Primary">
<a class="home" href="../index.html">GhosttyKit</a>
<a href="../index.html">Overview</a>
<a href="index.html" aria-current="page">Guide</a>
<a href="../api/index.html">API</a>
<a href="../architecture.html">Architecture</a>
<a href="../build.html">Build</a>
</nav>
<main>
<h1>Guide</h1>
<p class="lede">Task-oriented integration notes for embedding GhosttyKit in Apple apps. Each page maps directly to a product or workflow.</p>
<ul class="hub-list">
<li>
<a href="swiftui.html">SwiftUI</a>
<span class="summary"><code>TerminalViewState</code>, <code>TerminalSurfaceView</code>, lifecycle state, and sending input.</span>
</li>
<li>
<a href="uikit-appkit.html">UIKit &amp; AppKit</a>
<span class="summary">Create <code>TerminalView</code>, assign a controller, attach delegates, and configure native input.</span>
</li>
<li>
<a href="in-memory.html">Host-managed I/O</a>
<span class="summary"><code>InMemoryTerminalSession</code> bridges terminal bytes to a sandbox-safe host backend.</span>
</li>
<li>
<a href="themes.html">Themes</a>
<span class="summary">Use <code>GhosttyThemeCatalog</code>, convert themes to terminal config, and switch light/dark variants.</span>
</li>
<li>
<a href="text-selection.html">Text selection</a>
<span class="summary">Long-press requests, viewport snapshots, UTF-16 ranges, and host presentation.</span>
</li>
<li>
<a href="shellcraft.html">ShellCraftKit</a>
<span class="summary">Build a sandboxed command surface backed by <code>ShellDefinition</code> and <code>ShellSession</code>.</span>
</li>
</ul>
<footer>
GhosttyKit · <a href="https://github.com/Lakr233/libghostty-spm">github.com/Lakr233/libghostty-spm</a>
</footer>
</main>
</body>
</html>

View File

@@ -0,0 +1,93 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>GhosttyKit · ShellCraftKit</title>
<link rel="stylesheet" href="../styles.css">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github.min.css"
integrity="sha384-eFTL69TLRZTkNfYZOLM+G04821K1qZao/4QLJbet1pP4tcF+fdXq/9CdqAbWRl/L"
crossorigin="anonymous"
media="(prefers-color-scheme: light)">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github-dark.min.css"
integrity="sha384-wH75j6z1lH97ZOpMOInqhgKzFkAInZPPSPlZpYKYTOqsaizPvhQZmAtLcPKXpLyH"
crossorigin="anonymous"
media="(prefers-color-scheme: dark)">
</head>
<body>
<nav class="topbar" aria-label="Primary">
<a class="home" href="../index.html">GhosttyKit</a>
<a href="../index.html">Overview</a>
<a href="index.html" aria-current="page">Guide</a>
<a href="../api/index.html">API</a>
<a href="../architecture.html">Architecture</a>
<a href="../build.html">Build</a>
</nav>
<main>
<h1>ShellCraftKit</h1>
<nav class="on-this-page" aria-label="On this page">
<strong>On this page:</strong>
<a href="#shell">Shell</a>
<a href="#commands">Commands</a>
<a href="#attach">Attach</a>
</nav>
<p><code>ShellCraftKit</code> is a small command engine for demos and sandboxed tools. It feeds a Ghostty terminal through <code>InMemoryTerminalSession</code> and executes Swift closures for commands.</p>
<h2 id="shell">Define a shell</h2>
<pre><code class="language-swift">import ShellCraftKit
let shell = ShellDefinition(
prompt: "demo$ ",
welcomeMessage: "\r\nGhosttyKit demo\r\n\r\n"
) {
ShellCommand("date", summary: "Print the current date") { _ in
.output(Date().description + "\r\n")
}
ShellCommand("clear", summary: "Clear the screen") { _ in
.clear
}
}
</code></pre>
<h2 id="commands">Command result</h2>
<p>A command returns one of three results: output, clear, or exit. <code>CommandContext</code> gives the parsed command name, arguments, username, and current terminal size.</p>
<pre><code class="language-swift">ShellCommand("size", summary: "Print terminal size") { context in
.output("\(context.terminalSize.columns)x\(context.terminalSize.rows)\r\n")
}
</code></pre>
<h2 id="attach">Attach to Ghostty</h2>
<pre><code class="language-swift">let shellSession = ShellSession(shell: shell)
shellSession.start()
terminal.configuration = TerminalSurfaceOptions(
backend: .inMemory(shellSession.terminalSession)
)
</code></pre>
<nav class="pager" aria-label="Sibling pages">
<a class="prev" href="text-selection.html">Text selection</a>
<a class="parent" href="index.html">↑ Guide</a>
<a class="next" href="../api/index.html">API</a>
</nav>
<footer>
GhosttyKit · <a href="https://github.com/Lakr233/libghostty-spm">github.com/Lakr233/libghostty-spm</a>
</footer>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/highlight.min.js"
integrity="sha384-GdEWAbCjn+ghjX0gLx7/N1hyTVmPAjdC2OvoAA0RyNcAOhqwtT8qnbCxWle2+uJX"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/swift.min.js"
integrity="sha384-TfALNLT6HJzZieazgsVvFM0DzFWQsgl0d7mdwPLyg1yg7XE4QwLY4jqmJRNnI1S4"
crossorigin="anonymous"></script>
<script>hljs.highlightAll();</script>
</body>
</html>

View File

@@ -0,0 +1,109 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>GhosttyKit · SwiftUI</title>
<link rel="stylesheet" href="../styles.css">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github.min.css"
integrity="sha384-eFTL69TLRZTkNfYZOLM+G04821K1qZao/4QLJbet1pP4tcF+fdXq/9CdqAbWRl/L"
crossorigin="anonymous"
media="(prefers-color-scheme: light)">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github-dark.min.css"
integrity="sha384-wH75j6z1lH97ZOpMOInqhgKzFkAInZPPSPlZpYKYTOqsaizPvhQZmAtLcPKXpLyH"
crossorigin="anonymous"
media="(prefers-color-scheme: dark)">
</head>
<body>
<nav class="topbar" aria-label="Primary">
<a class="home" href="../index.html">GhosttyKit</a>
<a href="../index.html">Overview</a>
<a href="index.html" aria-current="page">Guide</a>
<a href="../api/index.html">API</a>
<a href="../architecture.html">Architecture</a>
<a href="../build.html">Build</a>
</nav>
<main>
<h1>SwiftUI</h1>
<nav class="on-this-page" aria-label="On this page">
<strong>On this page:</strong>
<a href="#state">State</a>
<a href="#view">View</a>
<a href="#input">Input</a>
<a href="#configuration">Configuration</a>
</nav>
<p>SwiftUI integration starts with <code>TerminalViewState</code>. It owns the controller, exposes observable terminal state, and tracks the current <code>TerminalSurface</code> after a platform view attaches.</p>
<h2 id="state">State</h2>
<pre><code class="language-swift">import SwiftUI
import GhosttyTerminal
struct TerminalScreen: View {
@StateObject private var terminal = TerminalViewState()
var body: some View {
TerminalSurfaceView(context: terminal)
.navigationTitle(terminal.title)
}
}
</code></pre>
<p>Observable properties include <code>title</code>, <code>surfaceSize</code>, <code>isFocused</code>, <code>bellCount</code>, <code>lastBellAt</code>, desktop notification metadata, <code>workingDirectory</code>, and command-finished metadata.</p>
<h2 id="view">View</h2>
<p><code>TerminalSurfaceView</code> embeds the platform terminal view and adopts the current SwiftUI color scheme. iOS 15, macOS 13, and Mac Catalyst 15 are the SwiftUI floor for this wrapper.</p>
<pre><code class="language-swift">TerminalSurfaceView(context: terminal)
.onAppear {
terminal.configuration = TerminalSurfaceOptions(
backend: .inMemory(session),
fontSize: 13,
workingDirectory: "/"
)
}
</code></pre>
<h2 id="input">Input</h2>
<p>Call <code>send(_:)</code> after the surface attaches. The method returns <code>true</code> when Ghostty accepts the text.</p>
<pre><code class="language-swift">@discardableResult
func sendCommand(_ command: String) -> Bool {
terminal.send(command + "\r")
}
</code></pre>
<h2 id="configuration">Configuration</h2>
<p>Use fluent configuration for common Ghostty settings, and <code>custom(_:_:)</code> for additional Ghostty config keys.</p>
<pre><code class="language-swift">let config = TerminalConfiguration.default
.fontFamily("SF Mono")
.fontSize(13)
.windowPaddingX(8)
.windowPaddingY(6)
.custom("shell-integration", "detect")
terminal.setTerminalConfiguration(config)
</code></pre>
<nav class="pager" aria-label="Sibling pages">
<a class="prev" href="index.html">Guide</a>
<a class="parent" href="index.html">↑ Guide</a>
<a class="next" href="uikit-appkit.html">UIKit &amp; AppKit</a>
</nav>
<footer>
GhosttyKit · <a href="https://github.com/Lakr233/libghostty-spm">github.com/Lakr233/libghostty-spm</a>
</footer>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/highlight.min.js"
integrity="sha384-GdEWAbCjn+ghjX0gLx7/N1hyTVmPAjdC2OvoAA0RyNcAOhqwtT8qnbCxWle2+uJX"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/swift.min.js"
integrity="sha384-TfALNLT6HJzZieazgsVvFM0DzFWQsgl0d7mdwPLyg1yg7XE4QwLY4jqmJRNnI1S4"
crossorigin="anonymous"></script>
<script>hljs.highlightAll();</script>
</body>
</html>

View File

@@ -0,0 +1,89 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>GhosttyKit · Text selection</title>
<link rel="stylesheet" href="../styles.css">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github.min.css"
integrity="sha384-eFTL69TLRZTkNfYZOLM+G04821K1qZao/4QLJbet1pP4tcF+fdXq/9CdqAbWRl/L"
crossorigin="anonymous"
media="(prefers-color-scheme: light)">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github-dark.min.css"
integrity="sha384-wH75j6z1lH97ZOpMOInqhgKzFkAInZPPSPlZpYKYTOqsaizPvhQZmAtLcPKXpLyH"
crossorigin="anonymous"
media="(prefers-color-scheme: dark)">
</head>
<body>
<nav class="topbar" aria-label="Primary">
<a class="home" href="../index.html">GhosttyKit</a>
<a href="../index.html">Overview</a>
<a href="index.html" aria-current="page">Guide</a>
<a href="../api/index.html">API</a>
<a href="../architecture.html">Architecture</a>
<a href="../build.html">Build</a>
</nav>
<main>
<h1>Text selection</h1>
<nav class="on-this-page" aria-label="On this page">
<strong>On this page:</strong>
<a href="#flow">Flow</a>
<a href="#delegate">Delegate</a>
<a href="#range">Ranges</a>
</nav>
<p>On iOS, a single-finger long press requests host text selection. GhosttyKit sends a viewport snapshot, a suggested UTF-16 range, and the source point for anchoring UI.</p>
<h2 id="flow">Flow</h2>
<ol>
<li>The user long-presses the terminal view for 0.5 seconds.</li>
<li>The view asks Ghostty for a viewport text snapshot and a quicklook word anchor.</li>
<li><code>TerminalTextSelectionRequest</code> arrives at the delegate.</li>
<li>The host presents its own UI, commonly a <code>UITextView</code> sheet or popover.</li>
</ol>
<h2 id="delegate">Delegate</h2>
<pre><code class="language-swift">final class SelectionCoordinator: TerminalSurfaceTextSelectionRequestDelegate {
func terminalDidRequestTextSelection(_ request: TerminalTextSelectionRequest) {
let textView = UITextView()
textView.text = request.text
if let range = request.anchorRange {
textView.selectedRange = range
} else {
textView.selectAll(nil)
}
presentSelectionUI(textView, sourcePoint: request.sourcePoint)
}
}
</code></pre>
<h2 id="range">Ranges</h2>
<p><code>anchorRange</code> uses UTF-16 units so it can be assigned directly to <code>UITextView.selectedRange</code>. Duplicate words on the same row are disambiguated with the long-press x coordinate and Ghostty cell metrics.</p>
<p class="muted">The MVP targets the in-memory backend and viewport text. Full scrollback selection belongs in a host selection surface.</p>
<nav class="pager" aria-label="Sibling pages">
<a class="prev" href="themes.html">Themes</a>
<a class="parent" href="index.html">↑ Guide</a>
<a class="next" href="shellcraft.html">ShellCraftKit</a>
</nav>
<footer>
GhosttyKit · <a href="https://github.com/Lakr233/libghostty-spm">github.com/Lakr233/libghostty-spm</a>
</footer>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/highlight.min.js"
integrity="sha384-GdEWAbCjn+ghjX0gLx7/N1hyTVmPAjdC2OvoAA0RyNcAOhqwtT8qnbCxWle2+uJX"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/swift.min.js"
integrity="sha384-TfALNLT6HJzZieazgsVvFM0DzFWQsgl0d7mdwPLyg1yg7XE4QwLY4jqmJRNnI1S4"
crossorigin="anonymous"></script>
<script>hljs.highlightAll();</script>
</body>
</html>

View File

@@ -0,0 +1,98 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>GhosttyKit · Themes</title>
<link rel="stylesheet" href="../styles.css">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github.min.css"
integrity="sha384-eFTL69TLRZTkNfYZOLM+G04821K1qZao/4QLJbet1pP4tcF+fdXq/9CdqAbWRl/L"
crossorigin="anonymous"
media="(prefers-color-scheme: light)">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github-dark.min.css"
integrity="sha384-wH75j6z1lH97ZOpMOInqhgKzFkAInZPPSPlZpYKYTOqsaizPvhQZmAtLcPKXpLyH"
crossorigin="anonymous"
media="(prefers-color-scheme: dark)">
</head>
<body>
<nav class="topbar" aria-label="Primary">
<a class="home" href="../index.html">GhosttyKit</a>
<a href="../index.html">Overview</a>
<a href="index.html" aria-current="page">Guide</a>
<a href="../api/index.html">API</a>
<a href="../architecture.html">Architecture</a>
<a href="../build.html">Build</a>
</nav>
<main>
<h1>Themes</h1>
<nav class="on-this-page" aria-label="On this page">
<strong>On this page:</strong>
<a href="#catalog">Catalog</a>
<a href="#apply">Apply</a>
<a href="#custom">Custom</a>
</nav>
<p><code>GhosttyTheme</code> ships 485 terminal color themes from iTerm2-Color-Schemes. Each theme can render directly into <code>TerminalConfiguration</code> and participate in light/dark switching.</p>
<h2 id="catalog">Catalog lookup</h2>
<pre><code class="language-swift">import GhosttyTheme
let theme = GhosttyThemeCatalog.theme(named: "Dracula")
let matches = GhosttyThemeCatalog.search("solarized")
let all = GhosttyThemeCatalog.allThemes
</code></pre>
<h2 id="apply">Apply to a terminal</h2>
<p>Wrap one or two definitions in <code>TerminalTheme</code>, then assign it to the controller or state container.</p>
<pre><code class="language-swift">import GhosttyTerminal
import GhosttyTheme
let dark = GhosttyThemeCatalog.theme(named: "Dracula")!
let light = GhosttyThemeCatalog.theme(named: "Solarized Light")!
let theme = TerminalTheme(
light: light.toTerminalConfiguration(),
dark: dark.toTerminalConfiguration()
)
terminal.setTheme(theme)
</code></pre>
<p><code>TerminalSurfaceView</code> follows the SwiftUI color scheme and asks <code>TerminalController</code> to resolve the effective config for the active appearance.</p>
<h2 id="custom">Custom colors</h2>
<pre><code class="language-swift">let config = TerminalConfiguration.default
.background("#101010")
.foreground("#f0f0f0")
.cursorColor("#ffd866")
.selectionBackground("#334455")
.palette(0, color: "#1b1d23")
.palette(1, color: "#ef6155")
terminal.setTerminalConfiguration(config)
</code></pre>
<nav class="pager" aria-label="Sibling pages">
<a class="prev" href="in-memory.html">Host-managed I/O</a>
<a class="parent" href="index.html">↑ Guide</a>
<a class="next" href="text-selection.html">Text selection</a>
</nav>
<footer>
GhosttyKit · <a href="https://github.com/Lakr233/libghostty-spm">github.com/Lakr233/libghostty-spm</a>
</footer>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/highlight.min.js"
integrity="sha384-GdEWAbCjn+ghjX0gLx7/N1hyTVmPAjdC2OvoAA0RyNcAOhqwtT8qnbCxWle2+uJX"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/swift.min.js"
integrity="sha384-TfALNLT6HJzZieazgsVvFM0DzFWQsgl0d7mdwPLyg1yg7XE4QwLY4jqmJRNnI1S4"
crossorigin="anonymous"></script>
<script>hljs.highlightAll();</script>
</body>
</html>

View File

@@ -0,0 +1,124 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>GhosttyKit · UIKit &amp; AppKit</title>
<link rel="stylesheet" href="../styles.css">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github.min.css"
integrity="sha384-eFTL69TLRZTkNfYZOLM+G04821K1qZao/4QLJbet1pP4tcF+fdXq/9CdqAbWRl/L"
crossorigin="anonymous"
media="(prefers-color-scheme: light)">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github-dark.min.css"
integrity="sha384-wH75j6z1lH97ZOpMOInqhgKzFkAInZPPSPlZpYKYTOqsaizPvhQZmAtLcPKXpLyH"
crossorigin="anonymous"
media="(prefers-color-scheme: dark)">
</head>
<body>
<nav class="topbar" aria-label="Primary">
<a class="home" href="../index.html">GhosttyKit</a>
<a href="../index.html">Overview</a>
<a href="index.html" aria-current="page">Guide</a>
<a href="../api/index.html">API</a>
<a href="../architecture.html">Architecture</a>
<a href="../build.html">Build</a>
</nav>
<main>
<h1>UIKit &amp; AppKit</h1>
<nav class="on-this-page" aria-label="On this page">
<strong>On this page:</strong>
<a href="#view">View</a>
<a href="#delegate">Delegate</a>
<a href="#ios-input">iOS input</a>
</nav>
<p><code>TerminalView</code> resolves to <code>UITerminalView</code> on UIKit and <code>AppTerminalView</code> on AppKit. Catalyst follows UIKit.</p>
<h2 id="view">View setup</h2>
<pre><code class="language-swift">import GhosttyTerminal
let terminalView = TerminalView(frame: .zero)
let controller = TerminalController(configuration: .default)
terminalView.controller = controller
terminalView.configuration = TerminalSurfaceOptions(
backend: .inMemory(session)
)
terminalView.delegate = coordinator
</code></pre>
<p>The view owns the native input layer and the Metal-backed surface. <code>TerminalController</code> owns Ghostty app lifecycle, configuration resolution, themes, and surface creation.</p>
<h2 id="delegate">Delegate callbacks</h2>
<p>Adopt the delegate protocols that match your host UI. A single coordinator object can implement multiple protocols.</p>
<pre><code class="language-swift">final class Coordinator:
TerminalSurfaceTitleDelegate,
TerminalSurfaceGridResizeDelegate,
TerminalSurfaceBellDelegate,
TerminalSurfaceLifecycleDelegate
{
func terminalDidChangeTitle(_ title: String) {}
func terminalDidResize(_ size: TerminalGridMetrics) {}
func terminalDidRingBell() {}
func terminalDidAttachSurface(_ surface: TerminalSurface) {}
func terminalDidDetachSurface() {}
}
</code></pre>
<h2 id="ios-input">iOS input</h2>
<p><code>UITerminalView</code> conforms to <code>UITextInput</code>. Hardware keys enter through <code>pressesBegan</code>, software keyboard text enters through <code>insertText</code>, and marked text flows through the shared IME handler.</p>
<p>On iOS, the input accessory bar provides Esc, Tab, arrows, symbols, Paste, and sticky Ctrl/Alt/Cmd modifiers. Configure colors through <code>inputAccessoryStyle</code>.</p>
<pre><code class="language-swift">#if canImport(UIKit)
terminalView.inputAccessoryStyle = .init(
regularBackground: .secondarySystemBackground,
regularForeground: .label,
activeBackground: .label,
activeForeground: .systemBackground
)
#endif
</code></pre>
<p>Hosts with a custom keyboard bar can hide the bundled accessory and drive sticky modifiers through the public sticky APIs.</p>
<pre><code class="language-swift">#if canImport(UIKit)
terminalView.inputAccessoryItems = []
#endif
</code></pre>
<p>The default button list is available as <code>TerminalInputAccessoryItem.defaultItems</code>. Hosts can provide a smaller list while keeping the bundled bar styling and key dispatch behavior.</p>
<pre><code class="language-swift">#if canImport(UIKit)
terminalView.inputAccessoryItems = [
.esc,
.ctrl,
.alt,
.command,
.divider,
.tab,
.arrowLeft,
.arrowRight,
.paste,
]
#endif
</code></pre>
<nav class="pager" aria-label="Sibling pages">
<a class="prev" href="swiftui.html">SwiftUI</a>
<a class="parent" href="index.html">↑ Guide</a>
<a class="next" href="in-memory.html">Host-managed I/O</a>
</nav>
<footer>
GhosttyKit · <a href="https://github.com/Lakr233/libghostty-spm">github.com/Lakr233/libghostty-spm</a>
</footer>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/highlight.min.js"
integrity="sha384-GdEWAbCjn+ghjX0gLx7/N1hyTVmPAjdC2OvoAA0RyNcAOhqwtT8qnbCxWle2+uJX"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/swift.min.js"
integrity="sha384-TfALNLT6HJzZieazgsVvFM0DzFWQsgl0d7mdwPLyg1yg7XE4QwLY4jqmJRNnI1S4"
crossorigin="anonymous"></script>
<script>hljs.highlightAll();</script>
</body>
</html>

130
vendor/libghostty-spm/docs/index.html vendored Normal file
View File

@@ -0,0 +1,130 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>GhosttyKit · Overview</title>
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github.min.css"
integrity="sha384-eFTL69TLRZTkNfYZOLM+G04821K1qZao/4QLJbet1pP4tcF+fdXq/9CdqAbWRl/L"
crossorigin="anonymous"
media="(prefers-color-scheme: light)">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github-dark.min.css"
integrity="sha384-wH75j6z1lH97ZOpMOInqhgKzFkAInZPPSPlZpYKYTOqsaizPvhQZmAtLcPKXpLyH"
crossorigin="anonymous"
media="(prefers-color-scheme: dark)">
</head>
<body>
<nav class="topbar" aria-label="Primary">
<a class="home" href="index.html">GhosttyKit</a>
<a href="index.html" aria-current="page">Overview</a>
<a href="guide/index.html">Guide</a>
<a href="api/index.html">API</a>
<a href="architecture.html">Architecture</a>
<a href="build.html">Build</a>
</nav>
<main>
<h1>GhosttyKit</h1>
<p class="lede">Swift package for embedding Ghostty&rsquo;s terminal emulator library in Apple apps. Native SwiftUI, UIKit, and AppKit views wrap a pre-built <code>libghostty</code> XCFramework.</p>
<h2 id="capabilities">Capabilities</h2>
<ul>
<li>Terminal rendering: Ghostty&rsquo;s VT parser, grid, CoreText font backend, and Metal renderer.</li>
<li>SwiftUI: <code>TerminalSurfaceView</code> with an <code>ObservableObject</code> <code>TerminalViewState</code>.</li>
<li>UIKit and AppKit: platform terminal views through the shared <code>TerminalView</code> typealias.</li>
<li>Input: hardware keyboard, software keyboard, IME, mouse, touch scrolling, iOS accessory keys, sticky modifiers.</li>
<li>Host-managed I/O: sandbox-friendly byte transport through <code>InMemoryTerminalSession</code>.</li>
<li>Themes: 485 bundled iTerm2 color schemes exposed through <code>GhosttyThemeCatalog</code>.</li>
<li>ShellCraftKit: a sandboxed shell emulation framework for demos and embedded command surfaces.</li>
</ul>
<h2 id="install">Install</h2>
<p>Add the package to <code>Package.swift</code>:</p>
<pre><code class="language-swift">dependencies: [
.package(url: "https://github.com/Lakr233/libghostty-spm.git", from: "1.2.0"),
],
targets: [
.target(name: "App", dependencies: [
.product(name: "GhosttyTerminal", package: "libghostty-spm"),
]),
]
</code></pre>
<p>Choose the product that matches the integration layer you need.</p>
<table>
<thead><tr><th>Product</th><th>Use</th></tr></thead>
<tbody>
<tr><td><code>GhosttyKit</code></td><td>Direct access to the <code>ghostty.h</code> C API.</td></tr>
<tr><td><code>GhosttyTerminal</code></td><td>Swift wrapper, native views, configuration, input, display link, host-managed I/O.</td></tr>
<tr><td><code>GhosttyTheme</code></td><td>Bundled terminal color schemes and bridges to <code>TerminalConfiguration</code>.</td></tr>
<tr><td><code>ShellCraftKit</code></td><td>Sandboxed shell emulation on top of <code>GhosttyTerminal</code>.</td></tr>
</tbody>
</table>
<h2 id="platforms">Platforms</h2>
<table>
<thead><tr><th>Platform</th><th>Floor</th></tr></thead>
<tbody>
<tr><td>iOS</td><td>15.0</td></tr>
<tr><td>macOS</td><td>13.0</td></tr>
<tr><td>Mac Catalyst</td><td>15.0</td></tr>
</tbody>
</table>
<h2 id="quick-start">Quick start</h2>
<pre><code class="language-swift">import SwiftUI
import GhosttyTerminal
struct ContentView: View {
@StateObject private var terminal = TerminalViewState()
private let session = InMemoryTerminalSession(
write: { data in
// Bytes from Ghostty to your host process or emulator.
},
resize: { viewport in
// Keep your backend grid in sync with Ghostty.
}
)
var body: some View {
TerminalSurfaceView(context: terminal)
.navigationTitle(terminal.title)
.onAppear {
terminal.configuration = TerminalSurfaceOptions(
backend: .inMemory(session)
)
}
}
}
</code></pre>
<p><code>InMemoryTerminalSession.receive(_:)</code> writes host output into the terminal. The <code>write</code> closure receives terminal input bytes for your backend.</p>
<h2 id="examples">Examples</h2>
<ul>
<li><code>Example/GhosttyTerminalApp</code> is the macOS AppKit demo with delegate callbacks.</li>
<li><code>Example/MobileGhosttyApp</code> is the iOS UIKit demo with keyboard, safe area, themes, and text selection.</li>
</ul>
<footer>
GhosttyKit · <a href="https://github.com/Lakr233/libghostty-spm">github.com/Lakr233/libghostty-spm</a>
</footer>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/highlight.min.js"
integrity="sha384-GdEWAbCjn+ghjX0gLx7/N1hyTVmPAjdC2OvoAA0RyNcAOhqwtT8qnbCxWle2+uJX"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/swift.min.js"
integrity="sha384-TfALNLT6HJzZieazgsVvFM0DzFWQsgl0d7mdwPLyg1yg7XE4QwLY4jqmJRNnI1S4"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/bash.min.js"
integrity="sha384-83HvQQdGTWqnRLmgm19NjCb1+/awKJGytUX9sm3HJb2ddZ9Fs1Bst2bZogFjt9rr"
crossorigin="anonymous"></script>
<script>hljs.highlightAll();</script>
</body>
</html>

221
vendor/libghostty-spm/docs/styles.css vendored Normal file
View File

@@ -0,0 +1,221 @@
:root {
--bg: #ffffff;
--fg: #1d1d1f;
--muted: #6e6e73;
--link: #0a66c2;
--border: #e5e5e7;
--code-bg: #f6f6f7;
--table-stripe: #fafafa;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #0e0e10;
--fg: #e8e8ea;
--muted: #9a9aa1;
--link: #7aa2f7;
--border: #25252a;
--code-bg: #161618;
--table-stripe: #131316;
}
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
background: var(--bg);
color: var(--fg);
font: 15px/1.6 -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
nav.topbar {
border-bottom: 1px solid var(--border);
padding: 14px 24px;
font-size: 14px;
display: flex;
flex-wrap: wrap;
gap: 18px;
align-items: baseline;
}
nav.topbar a {
color: var(--fg);
text-decoration: none;
}
nav.topbar a.home {
font-weight: 600;
margin-right: 8px;
}
nav.topbar a:hover,
nav.topbar a[aria-current="page"] {
color: var(--link);
}
main {
max-width: 760px;
margin: 0 auto;
padding: 32px 24px 96px;
}
h1, h2, h3, h4 {
line-height: 1.25;
font-weight: 600;
}
h1 { font-size: 26px; margin: 0 0 8px; }
h2 {
font-size: 19px;
margin: 40px 0 12px;
padding-top: 24px;
border-top: 1px solid var(--border);
}
h3 { font-size: 16px; margin: 28px 0 8px; }
h4 { font-size: 14px; margin: 20px 0 6px; }
p, ul, ol { margin: 0 0 12px; }
ul, ol { padding-left: 20px; }
li { margin-bottom: 4px; }
a { color: var(--link); }
a:hover { text-decoration: underline; }
code {
font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
font-size: 12.5px;
background: var(--code-bg);
padding: 1px 5px;
border-radius: 3px;
}
pre {
background: var(--code-bg);
padding: 14px 16px;
border-radius: 6px;
overflow-x: auto;
font-size: 12.5px;
line-height: 1.5;
margin: 0 0 14px;
}
pre code {
background: transparent;
padding: 0;
font-size: inherit;
}
table {
width: 100%;
border-collapse: collapse;
margin: 0 0 14px;
font-size: 14px;
}
th, td {
text-align: left;
padding: 8px 12px;
border-bottom: 1px solid var(--border);
vertical-align: top;
}
th { font-weight: 600; }
tbody tr:nth-child(even) { background: var(--table-stripe); }
hr {
border: 0;
border-top: 1px solid var(--border);
margin: 32px 0;
}
.muted { color: var(--muted); }
.lede { font-size: 16px; color: var(--muted); margin-bottom: 24px; }
.anchor-list {
list-style: none;
padding: 0;
margin: 0 0 24px;
column-count: 2;
column-gap: 24px;
}
.anchor-list li {
break-inside: avoid;
margin-bottom: 4px;
}
footer {
margin-top: 64px;
padding-top: 16px;
border-top: 1px solid var(--border);
color: var(--muted);
font-size: 13px;
}
nav.pager {
margin-top: 48px;
padding-top: 16px;
border-top: 1px solid var(--border);
display: flex;
justify-content: space-between;
gap: 16px;
font-size: 14px;
}
nav.pager .prev::before { content: "← "; }
nav.pager .next::after { content: " →"; }
nav.pager .parent { color: var(--muted); }
nav.pager a { text-decoration: none; }
nav.pager a:hover { text-decoration: underline; }
nav.on-this-page {
font-size: 13px;
color: var(--muted);
margin: 0 0 24px;
padding: 8px 0 12px;
border-bottom: 1px solid var(--border);
}
nav.on-this-page strong {
color: var(--fg);
font-weight: 600;
margin-right: 6px;
}
nav.on-this-page a { margin-right: 12px; }
ul.hub-list {
list-style: none;
padding: 0;
margin: 0 0 24px;
}
ul.hub-list li {
padding: 12px 0;
border-bottom: 1px solid var(--border);
}
ul.hub-list li a {
font-weight: 600;
font-size: 15px;
text-decoration: none;
}
ul.hub-list li a:hover { text-decoration: underline; }
ul.hub-list .summary {
display: block;
color: var(--muted);
font-weight: 400;
margin-top: 2px;
font-size: 14px;
}
@media (max-width: 560px) {
.anchor-list { column-count: 1; }
nav.topbar { gap: 12px; }
main { padding: 24px 18px 64px; }
}