Files
terminalX/vendor/libghostty-spm/Example/MobileGhosttyApp/TerminalSelectionViewController.swift
kid aa92d0e676 初始提交: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>
2026-07-24 10:20:46 +08:00

87 lines
3.0 KiB
Swift

import UIKit
final class TerminalSelectionViewController: UIViewController {
private let pendingText: String
private let pendingAnchorRange: NSRange?
var onDone: (() -> Void)?
private lazy var textView: UITextView = {
let view = UITextView()
view.isEditable = false
view.isSelectable = true
view.accessibilityIdentifier = "terminal.selectionTextView"
view.alwaysBounceVertical = true
view.font = .monospacedSystemFont(ofSize: 14, weight: .regular)
view.backgroundColor = .clear
view.textColor = .label
view.textContainerInset = .init(top: 12, left: 12, bottom: 12, right: 12)
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
init(text: String, anchorRange: NSRange?) {
pendingText = text
pendingAnchorRange = anchorRange
super.init(nibName: nil, bundle: nil)
}
@available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .clear
textView.text = pendingText
view.addSubview(textView)
// Pin to view edges (not safe area) so scrolled content extends
// under the transparent navigation bar / home indicator the way
// iOS sheets expect. UIKit's contentInsetAdjustmentBehavior keeps
// the initial scroll position below the nav bar via
// adjustedContentInset.
NSLayoutConstraint.activate([
textView.topAnchor.constraint(equalTo: view.topAnchor),
textView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
textView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
textView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
navigationItem.rightBarButtonItem = UIBarButtonItem(
barButtonSystemItem: .done,
target: self,
action: #selector(handleDone)
)
navigationItem.rightBarButtonItem?.accessibilityIdentifier = "terminal.selectionDoneButton"
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.view.backgroundColor = .clear
navigationController?.navigationBar.standardAppearance.configureWithTransparentBackground()
navigationController?.navigationBar.scrollEdgeAppearance = navigationController?.navigationBar.standardAppearance
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
textView.becomeFirstResponder()
let nsText = textView.text as NSString
if let range = pendingAnchorRange, NSMaxRange(range) <= nsText.length {
textView.selectedRange = range
textView.scrollRangeToVisible(range)
} else {
textView.selectAll(nil)
}
}
@objc private func handleDone() {
dismiss(animated: true) { [weak self] in
self?.onDone?()
}
}
}