mirror of
https://github.com/SagerNet/sing-box-for-apple.git
synced 2026-06-03 11:37:44 +00:00
111 lines
4.3 KiB
Swift
111 lines
4.3 KiB
Swift
#if canImport(GhosttyTerminal)
|
|
import GhosttyTerminal
|
|
import Library
|
|
import SwiftUI
|
|
#if os(iOS) && !targetEnvironment(macCatalyst)
|
|
import UIKit
|
|
#endif
|
|
|
|
@MainActor
|
|
public struct TerminalWrapperView: View {
|
|
@StateObject private var viewModel = TerminalWrapperViewModel()
|
|
private let presentedSession: TailscaleSSHPresentedSession
|
|
@Environment(\.dismiss) private var dismiss
|
|
@Environment(\.openURL) private var openURL
|
|
|
|
public init(_ presentedSession: TailscaleSSHPresentedSession) {
|
|
self.presentedSession = presentedSession
|
|
}
|
|
|
|
public var body: some View {
|
|
TerminalSessionContentView(
|
|
viewModel: viewModel,
|
|
presentedSession: presentedSession
|
|
)
|
|
.navigationTitle(displayedTitle)
|
|
#if os(iOS)
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.toolbar {
|
|
ToolbarItem(placement: .cancellationAction) {
|
|
Button {
|
|
dismiss()
|
|
} label: {
|
|
Image(systemName: "chevron.backward")
|
|
}
|
|
}
|
|
}
|
|
.background(
|
|
Button(action: { dismiss() }) {}
|
|
.keyboardShortcut("w", modifiers: .command)
|
|
.opacity(0)
|
|
.frame(width: 0, height: 0)
|
|
.accessibilityHidden(true)
|
|
)
|
|
#endif
|
|
.onAppear {
|
|
viewModel.onWindowClose = { dismiss() }
|
|
viewModel.extras.onOpenURL = { urlString, _ in
|
|
guard let url = URL(string: urlString) else { return }
|
|
openURL(url)
|
|
}
|
|
#if os(iOS) && !targetEnvironment(macCatalyst)
|
|
viewModel.extras.onRequestTextSelection = { request in
|
|
presentTerminalSelectionSheet(request: request)
|
|
}
|
|
#endif
|
|
viewModel.start(presentedSession)
|
|
}
|
|
.onDisappear {
|
|
viewModel.disconnect()
|
|
}
|
|
}
|
|
|
|
private var displayedTitle: String {
|
|
TerminalSessionContentView.displayTitle(
|
|
phase: viewModel.phase,
|
|
extrasTitle: viewModel.extras.title,
|
|
peerHostName: presentedSession.peerHostName
|
|
)
|
|
}
|
|
|
|
#if os(iOS) && !targetEnvironment(macCatalyst)
|
|
@MainActor
|
|
private func presentTerminalSelectionSheet(request: TerminalTextSelectionRequest) {
|
|
guard let presenter = topmostViewController() else { return }
|
|
let selectionVC = TailsshTerminalSelectionViewController(
|
|
text: request.text,
|
|
anchorRange: request.anchorRange
|
|
)
|
|
selectionVC.onOpenURL = { url in
|
|
openURL(url)
|
|
}
|
|
let nav = UINavigationController(rootViewController: selectionVC)
|
|
nav.modalPresentationStyle = .pageSheet
|
|
if let sheet = nav.sheetPresentationController {
|
|
sheet.detents = [.medium(), .large()]
|
|
sheet.prefersGrabberVisible = true
|
|
}
|
|
presenter.present(nav, animated: true)
|
|
}
|
|
|
|
@MainActor
|
|
private func topmostViewController() -> UIViewController? {
|
|
let scene = UIApplication.shared.connectedScenes
|
|
.compactMap { $0 as? UIWindowScene }
|
|
.first { $0.activationState == .foregroundActive }
|
|
?? UIApplication.shared.connectedScenes
|
|
.compactMap { $0 as? UIWindowScene }
|
|
.first
|
|
guard let root = scene?.windows.first(where: { $0.isKeyWindow })?.rootViewController
|
|
?? scene?.windows.first?.rootViewController
|
|
else { return nil }
|
|
var top = root
|
|
while let presented = top.presentedViewController {
|
|
top = presented
|
|
}
|
|
return top
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|