初始提交: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,40 @@
//
// GhosttyConfigRenderer.swift
// libghostty-spm
//
// Created by Lakr233 on 2026/3/17.
//
import Foundation
enum GhosttyConfigRenderer {
static func render(
baseContents: String,
configuration: TerminalConfiguration,
theme: TerminalConfiguration
) -> String {
var sections: [String] = []
let normalizedBase = normalize(baseContents)
if !normalizedBase.isEmpty {
sections.append(normalizedBase)
}
let configurationLines = configuration.commands.map(\.renderedLine)
if !configurationLines.isEmpty {
sections.append(configurationLines.joined(separator: "\n"))
}
let themeLines = theme.commands.map(\.renderedLine)
if !themeLines.isEmpty {
sections.append(themeLines.joined(separator: "\n"))
}
guard !sections.isEmpty else { return "" }
return sections.joined(separator: "\n") + "\n"
}
private static func normalize(_ contents: String) -> String {
contents.trimmingCharacters(in: .whitespacesAndNewlines)
}
}

View File

@@ -0,0 +1,21 @@
//
// TerminalColorScheme+SwiftUI.swift
// libghostty-spm
//
// Created by Lakr233 on 2026/3/17.
//
#if canImport(SwiftUI)
import SwiftUI
extension TerminalColorScheme {
init(_ colorScheme: ColorScheme) {
switch colorScheme {
case .dark:
self = .dark
default:
self = .light
}
}
}
#endif

View File

@@ -0,0 +1,18 @@
//
// TerminalColorScheme.swift
// libghostty-spm
//
import GhosttyKit
public enum TerminalColorScheme: Sendable {
case light
case dark
var ghosttyValue: ghostty_color_scheme_e {
switch self {
case .light: GHOSTTY_COLOR_SCHEME_LIGHT
case .dark: GHOSTTY_COLOR_SCHEME_DARK
}
}
}

View File

@@ -0,0 +1,360 @@
//
// TerminalConfiguration.swift
// libghostty-spm
//
// Created by Lakr233 on 2026/3/17.
//
public enum TerminalCursorStyle: String, Sendable, Hashable {
case block
case bar
case underline
}
public enum TerminalConfigCommand: Sendable, Hashable {
// Font
case fontFamily(String)
case fontSize(Float)
case fontThicken(Bool)
case fontThickenStrength(Int)
// Cursor
case cursorStyle(TerminalCursorStyle)
case cursorStyleBlink(Bool)
case cursorColor(String)
case cursorText(String)
case cursorOpacity(Double)
// Colors
case background(String)
case foreground(String)
case selectionBackground(String)
case selectionForeground(String)
case boldColor(String)
case palette(index: Int, color: String)
case minimumContrast(Double)
// Background
case backgroundOpacity(Double)
case backgroundBlur(Int)
// Layout
case windowPaddingX(Int)
case windowPaddingY(Int)
/// Escape hatch
case custom(key: String, value: String)
var renderedLine: String {
switch self {
case let .fontFamily(value):
"font-family = \(value)"
case let .fontSize(value):
"font-size = \(value.formatted(.number.precision(.fractionLength(0 ... 2))))"
case let .fontThicken(enabled):
"font-thicken = \(enabled)"
case let .fontThickenStrength(value):
"font-thicken-strength = \(value)"
case let .cursorStyle(style):
"cursor-style = \(style.rawValue)"
case let .cursorStyleBlink(enabled):
"cursor-style-blink = \(enabled)"
case let .cursorColor(value):
"cursor-color = \(value)"
case let .cursorText(value):
"cursor-text = \(value)"
case let .cursorOpacity(value):
"cursor-opacity = \(value.formatted(.number.precision(.fractionLength(0 ... 3))))"
case let .background(value):
"background = \(value)"
case let .foreground(value):
"foreground = \(value)"
case let .selectionBackground(value):
"selection-background = \(value)"
case let .selectionForeground(value):
"selection-foreground = \(value)"
case let .boldColor(value):
"bold-color = \(value)"
case let .palette(index, color):
"palette = \(index)=\(color)"
case let .minimumContrast(value):
"minimum-contrast = \(value.formatted(.number.precision(.fractionLength(0 ... 2))))"
case let .backgroundOpacity(value):
"background-opacity = \(value.formatted(.number.precision(.fractionLength(0 ... 3))))"
case let .backgroundBlur(value):
"background-blur = \(value)"
case let .windowPaddingX(value):
"window-padding-x = \(value)"
case let .windowPaddingY(value):
"window-padding-y = \(value)"
case let .custom(key, value):
"\(key) = \(value)"
}
}
}
public struct TerminalConfiguration: Sendable, Hashable {
public struct Builder {
var commands: [TerminalConfigCommand] = []
public init() {}
init(commands: [TerminalConfigCommand]) {
self.commands = commands
}
/// Font
public mutating func withFontFamily(_ value: String) {
commands.append(.fontFamily(value))
}
public mutating func withFontSize(_ value: Float) {
commands.append(.fontSize(value))
}
public mutating func withFontThicken(_ enabled: Bool) {
commands.append(.fontThicken(enabled))
}
public mutating func withFontThickenStrength(_ value: Int) {
commands.append(.fontThickenStrength(value))
}
/// Cursor
public mutating func withCursorStyle(_ style: TerminalCursorStyle) {
commands.append(.cursorStyle(style))
}
public mutating func withCursorStyleBlink(_ enabled: Bool) {
commands.append(.cursorStyleBlink(enabled))
}
public mutating func withCursorColor(_ value: String) {
commands.append(.cursorColor(value))
}
public mutating func withCursorText(_ value: String) {
commands.append(.cursorText(value))
}
public mutating func withCursorOpacity(_ value: Double) {
commands.append(.cursorOpacity(value))
}
/// Colors
public mutating func withBackground(_ value: String) {
commands.append(.background(value))
}
public mutating func withForeground(_ value: String) {
commands.append(.foreground(value))
}
public mutating func withSelectionBackground(_ value: String) {
commands.append(.selectionBackground(value))
}
public mutating func withSelectionForeground(_ value: String) {
commands.append(.selectionForeground(value))
}
public mutating func withBoldColor(_ value: String) {
commands.append(.boldColor(value))
}
public mutating func withPalette(_ index: Int, color: String) {
commands.append(.palette(index: index, color: color))
}
public mutating func withMinimumContrast(_ value: Double) {
commands.append(.minimumContrast(value))
}
/// Background
public mutating func withBackgroundOpacity(_ value: Double) {
commands.append(.backgroundOpacity(value))
}
public mutating func withBackgroundBlur(_ value: Int) {
commands.append(.backgroundBlur(value))
}
/// Layout
public mutating func withWindowPaddingX(_ value: Int) {
commands.append(.windowPaddingX(value))
}
public mutating func withWindowPaddingY(_ value: Int) {
commands.append(.windowPaddingY(value))
}
/// Escape hatch
public mutating func withCustom(_ key: String, _ value: String) {
commands.append(.custom(key: key, value: value))
}
}
let commands: [TerminalConfigCommand]
public init() {
commands = []
}
public init(configure: (inout Builder) -> Void) {
self.init(startingFrom: .init(), configure: configure)
}
public init(
startingFrom base: TerminalConfiguration,
configure: (inout Builder) -> Void
) {
var builder = Builder(commands: base.commands)
configure(&builder)
commands = builder.commands
}
public func appending(_ command: TerminalConfigCommand) -> TerminalConfiguration {
TerminalConfiguration(commands: commands + [command])
}
// MARK: - Font
public func fontFamily(_ value: String) -> TerminalConfiguration {
appending(.fontFamily(value))
}
public func fontSize(_ value: Float) -> TerminalConfiguration {
appending(.fontSize(value))
}
public func fontThicken(_ enabled: Bool) -> TerminalConfiguration {
appending(.fontThicken(enabled))
}
public func fontThickenStrength(_ value: Int) -> TerminalConfiguration {
appending(.fontThickenStrength(value))
}
// MARK: - Cursor
public func cursorStyle(_ style: TerminalCursorStyle) -> TerminalConfiguration {
appending(.cursorStyle(style))
}
public func cursorStyleBlink(_ enabled: Bool) -> TerminalConfiguration {
appending(.cursorStyleBlink(enabled))
}
public func cursorColor(_ value: String) -> TerminalConfiguration {
appending(.cursorColor(value))
}
public func cursorText(_ value: String) -> TerminalConfiguration {
appending(.cursorText(value))
}
public func cursorOpacity(_ value: Double) -> TerminalConfiguration {
appending(.cursorOpacity(value))
}
// MARK: - Colors
public func background(_ value: String) -> TerminalConfiguration {
appending(.background(value))
}
public func foreground(_ value: String) -> TerminalConfiguration {
appending(.foreground(value))
}
public func selectionBackground(_ value: String) -> TerminalConfiguration {
appending(.selectionBackground(value))
}
public func selectionForeground(_ value: String) -> TerminalConfiguration {
appending(.selectionForeground(value))
}
public func boldColor(_ value: String) -> TerminalConfiguration {
appending(.boldColor(value))
}
public func palette(_ index: Int, color: String) -> TerminalConfiguration {
appending(.palette(index: index, color: color))
}
public func minimumContrast(_ value: Double) -> TerminalConfiguration {
appending(.minimumContrast(value))
}
// MARK: - Background
public func backgroundOpacity(_ value: Double) -> TerminalConfiguration {
appending(.backgroundOpacity(value))
}
public func backgroundBlur(_ value: Int) -> TerminalConfiguration {
appending(.backgroundBlur(value))
}
// MARK: - Layout
public func windowPaddingX(_ value: Int) -> TerminalConfiguration {
appending(.windowPaddingX(value))
}
public func windowPaddingY(_ value: Int) -> TerminalConfiguration {
appending(.windowPaddingY(value))
}
// MARK: - Escape Hatch
public func custom(_ key: String, _ value: String) -> TerminalConfiguration {
appending(.custom(key: key, value: value))
}
// MARK: - Defaults
public static let `default` = TerminalConfiguration { builder in
builder.withCursorStyle(.block)
builder.withCursorStyleBlink(true)
#if os(iOS)
builder.withFontSize(10)
#else
builder.withFontSize(14)
#endif
builder.withFontThicken(true)
}
public var rendered: String {
commands.map(\.renderedLine).joined(separator: "\n")
}
var isEmpty: Bool {
commands.isEmpty
}
init(commands: [TerminalConfigCommand]) {
self.commands = commands
}
}

View File

@@ -0,0 +1,70 @@
//
// TerminalTheme+Defaults.swift
// libghostty-spm
//
public extension TerminalTheme {
/// Afterglow (dark) + Alabaster (light) default theme.
static let `default` = TerminalTheme(
light: .alabaster,
dark: .afterglow
)
}
public extension TerminalConfiguration {
/// Alabaster light terminal theme.
///
/// Background: #F7F7F7, Foreground: #000000
static let alabaster = TerminalConfiguration { builder in
builder.withBackground("F7F7F7")
builder.withForeground("000000")
builder.withCursorColor("007ACC")
builder.withSelectionBackground("C9D0D9")
// Normal colors (0-7)
builder.withPalette(0, color: "#000000")
builder.withPalette(1, color: "#AA3731")
builder.withPalette(2, color: "#448C27")
builder.withPalette(3, color: "#CB8800")
builder.withPalette(4, color: "#325CC0")
builder.withPalette(5, color: "#7A3E9D")
builder.withPalette(6, color: "#0083B2")
builder.withPalette(7, color: "#F7F7F7")
// Bright colors (8-15)
builder.withPalette(8, color: "#777777")
builder.withPalette(9, color: "#F03E31")
builder.withPalette(10, color: "#60CB00")
builder.withPalette(11, color: "#FFBC5D")
builder.withPalette(12, color: "#007ACC")
builder.withPalette(13, color: "#E64CE6")
builder.withPalette(14, color: "#00AACB")
builder.withPalette(15, color: "#F7F7F7")
}
/// Afterglow dark terminal theme.
///
/// Background: #212121, Foreground: #D0D0D0
static let afterglow = TerminalConfiguration { builder in
builder.withBackground("212121")
builder.withForeground("D0D0D0")
builder.withCursorColor("D0D0D0")
builder.withSelectionBackground("303030")
// Normal colors (0-7)
builder.withPalette(0, color: "#151515")
builder.withPalette(1, color: "#AC4142")
builder.withPalette(2, color: "#7E8E50")
builder.withPalette(3, color: "#E4B567")
builder.withPalette(4, color: "#6C99BB")
builder.withPalette(5, color: "#9F4E86")
builder.withPalette(6, color: "#7DD5CF")
builder.withPalette(7, color: "#D0D0D0")
// Bright colors (8-15)
builder.withPalette(8, color: "#505050")
builder.withPalette(9, color: "#AC4142")
builder.withPalette(10, color: "#7E8E50")
builder.withPalette(11, color: "#E4B567")
builder.withPalette(12, color: "#6C99BB")
builder.withPalette(13, color: "#9F4E86")
builder.withPalette(14, color: "#7DD5CF")
builder.withPalette(15, color: "#F5F5F5")
}
}

View File

@@ -0,0 +1,32 @@
//
// TerminalTheme.swift
// libghostty-spm
//
// Created by Lakr233 on 2026/3/17.
//
public struct TerminalTheme: Sendable, Hashable {
public var light: TerminalConfiguration
public var dark: TerminalConfiguration
public init(
light: TerminalConfiguration = .init(),
dark: TerminalConfiguration = .init()
) {
self.light = light
self.dark = dark
}
var isEmpty: Bool {
light.isEmpty && dark.isEmpty
}
func configuration(for colorScheme: TerminalColorScheme) -> TerminalConfiguration {
switch colorScheme {
case .light:
light
case .dark:
dark
}
}
}