初始提交: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

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>