Remove useless code

This commit is contained in:
Kaede Akino committed 2026-09-12 23:57:33 +08:00
1 parent b8a72cc23d
commit 83b54be813
59 files changed
+65 -299

No files matched your search

+2 -1
View File
@@ -1,5 +1,6 @@
project: Termind.xcodeproj
retain_public: true
schemes:
- Termind
- TFM
- TFI
- TermindMCPBridge
@@ -107,11 +107,6 @@ public final class CommandGeneratorController: CommandGenerating {
self.service = CommandGeneratorService()
}
init(ai: AIConfigurationModel, service: CommandGeneratorService) {
self.ai = ai
self.service = service
}
/// A local readiness check for contextual UI. It never contacts a model;
/// it only resolves the configured role and its already local credential.
public var isLocallyAvailable: Bool {
@@ -15,7 +15,7 @@ public nonisolated struct PortForwardingEditorPolicy {
public var isWaitingForSave: Bool { isEnabled && !hasSavedConfiguration && !isRuntimeActive }
public init(
rule: PortForwardingRule?, host: Host,
rule: PortForwardingRule?,
savedRule: PortForwardingRule?, savedHost: Host?,
isEnabled: Bool, isRuntimeActive: Bool
) {
@@ -1,5 +1,4 @@
import Foundation
import TermindDomain
public nonisolated enum RepositoryChangeReason: String, Hashable, Sendable {
case initial
@@ -1,5 +1,4 @@
import Foundation
import TermindDomain
public nonisolated struct ReconnectPolicy: Sendable, Equatable {
public static let sessionDefault = ReconnectPolicy()
@@ -197,13 +197,13 @@ public final class SpeechTranscriptionCoordinator {
case .partial(let transcript):
latestPartial = transcript
case .final(let transcript):
finish(with: transcript, ownerID: ownerID)
finish(with: transcript)
case .failure(let failure):
fail(failure, for: ownerID)
}
}
private func finish(with transcript: String, ownerID: UUID) {
private func finish(with transcript: String) {
let cleaned = transcript.trimmingCharacters(in: .whitespacesAndNewlines)
guard !cleaned.isEmpty else {
resetSession(cancelService: true)
@@ -1,5 +1,4 @@
@preconcurrency import AVFoundation
import TermindApplication
public typealias SpeechAudioBufferHandler = @Sendable (AVAudioPCMBuffer) -> Void
public typealias SpeechAudioLevelHandler = @Sendable (Float) -> Void
@@ -1,6 +1,5 @@
import Foundation
import SwiftData
import TermindDomain
@Model
public final class PersistentAISecretLocation {
@@ -1,7 +1,6 @@
import AgentKit
import Foundation
import SwiftData
import TermindDomain
@Model
public final class PersistentAgentConversation {
@@ -1,6 +1,5 @@
import Foundation
import SwiftData
import TermindApplication
import TermindDomain
public nonisolated struct SettingsRemoteMerge: Sendable {
@@ -1,7 +1,6 @@
import AgentKit
import Foundation
import SwiftData
import TermindDomain
@Model
public final class PersistentAgentSkill {
@@ -1,6 +1,5 @@
import AgentKit
import SwiftUI
import TermindApplication
/// How full the model's context window is, as a ring.
///
@@ -1,5 +1,4 @@
import Foundation
import TermindApplication
nonisolated public enum ChatMessageTimestampFormatter {
public static func string(
@@ -1,5 +1,4 @@
import SwiftUI
import TermindApplication
import TermindDomain
/// A fixed label gutter followed by four equal-width, leading-aligned data columns.
@@ -1,6 +1,5 @@
import Foundation
import TermindApplication
import TermindInfrastructure
/// What a command looks like to whoever asked about it.
///
@@ -75,8 +74,6 @@ public actor AgentBackgroundCommands {
public static let defaultIdleTimeout = Duration.seconds(600)
private struct Entry {
let hostID: UUID
let command: String
let stream: AgentCommandOutputStream
let task: Task<SSHExecResult, any Error>
let session: any SSHExecSessionProtocol
@@ -103,15 +100,12 @@ public actor AgentBackgroundCommands {
/// Takes ownership of a command still in flight, and of the session it is
/// running on.
public func adopt(
hostID: UUID, command: String, stream: AgentCommandOutputStream,
stream: AgentCommandOutputStream,
task: Task<SSHExecResult, any Error>, session: any SSHExecSessionProtocol,
now: ContinuousClock.Instant = .now
) -> UUID {
let handle = UUID()
entries[handle] = Entry(
hostID: hostID, command: command, stream: stream, task: task,
session: session, lastAskedAt: now
)
entries[handle] = Entry(stream: stream, task: task, session: session, lastAskedAt: now)
startSweeping()
return handle
}
@@ -2,7 +2,6 @@ import AgentKit
import Foundation
import TermindApplication
import TermindDomain
import TermindInfrastructure
import struct TermindDomain.Host
@@ -2,7 +2,6 @@ import AgentKit
import Foundation
import TermindApplication
import TermindDomain
import TermindInfrastructure
nonisolated public struct ListHostsTool: AgentToolDefinition, AgentToolSchemaBuilding {
public static let presenter = AgentToolDetailPresenter(
@@ -440,8 +439,7 @@ nonisolated public struct SSHExecTool: AgentToolDefinition, AgentToolSchemaBuild
// fresh one.
await pool.detachExec(hostID: hostID, session: session)
let handle = await background.adopt(
hostID: hostID, command: command, stream: stream,
task: running, session: session
stream: stream, task: running, session: session
)
let captured = stream.captured
return Self.result(
@@ -1,6 +1,5 @@
import Foundation
import TermindApplication
import TermindInfrastructure
/// Turns the chunks a running command emits into progress reports.
///
@@ -2,7 +2,6 @@ import AgentKit
import Foundation
import TermindApplication
import TermindDomain
import TermindInfrastructure
import struct TermindDomain.Host
@@ -306,7 +305,6 @@ public final class AgentHostResolver: Sendable {
}
private struct AuthenticationRequirement {
var host: Host
var mode: AgentSSHAuthenticationRequest.Mode
}
@@ -321,32 +319,32 @@ public final class AgentHostResolver: Sendable {
store.key(keyID)?.requiresPassphraseEntry == true,
submission.passphrase?.isEmpty != false
{
return AuthenticationRequirement(host: host, mode: .passphrase(keyID: keyID))
return AuthenticationRequirement(mode: .passphrase(keyID: keyID))
}
if submission.username.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
|| (submission.keyID == nil && submission.password == nil)
{
return AuthenticationRequirement(host: host, mode: .identity)
return AuthenticationRequirement(mode: .identity)
}
if forcedHostID == host.id {
return AuthenticationRequirement(host: host, mode: .identity)
return AuthenticationRequirement(mode: .identity)
}
return nil
}
guard let identity = store.identity(for: host) else {
return AuthenticationRequirement(host: host, mode: .identity)
return AuthenticationRequirement(mode: .identity)
}
let resolution = store.sshCredentials(for: identity)
if let keyID = resolution.requiredPassphraseKeyID {
return AuthenticationRequirement(host: host, mode: .passphrase(keyID: keyID))
return AuthenticationRequirement(mode: .passphrase(keyID: keyID))
}
if identity.username.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
|| resolution.credentials.isEmpty
{
return AuthenticationRequirement(host: host, mode: .identity)
return AuthenticationRequirement(mode: .identity)
}
if forcedHostID == host.id {
return AuthenticationRequirement(host: host, mode: .identity)
return AuthenticationRequirement(mode: .identity)
}
return nil
}
@@ -1,7 +1,6 @@
import AgentKit
import Foundation
import TermindApplication
import TermindInfrastructure
// The two scratch tools that touch a host.
//
@@ -2,7 +2,6 @@ import AgentKit
import Foundation
import TermindApplication
import TermindDomain
import TermindInfrastructure
import struct TermindDomain.Host
@@ -2,7 +2,6 @@ import AgentKit
import Foundation
import TermindApplication
import TermindDomain
import TermindInfrastructure
/// The wire projection stays with the tool; monitoring has no dependency on AgentKit.
nonisolated public enum MetricsToolOutput {
@@ -1,7 +1,6 @@
import AgentKit
import Foundation
import TermindApplication
import TermindInfrastructure
/// What Termind's own agent tools are built from.
///
@@ -2,7 +2,6 @@ import CSSH2
import CryptoKit
import Foundation
import OpenSSL
import TermindApplication
import TermindDomain
public nonisolated enum SSHKeyMaterialError: LocalizedError {
+39
View File
@@ -1325,3 +1325,42 @@ remains behind the context menu and confirmation dialog.
**Action:** Move platform-neutral presentation into TermindPresentation, inject tool presenters from the existing registry, and retain native platform rendering and viewport ownership. Replace mobile disclosure and detail branches with shared policy consumers. Estimated duplicate-policy reduction: 150–250 lines, with desktop presentation code moved rather than copied.
**Verification:** Pending shared, desktop, mobile and bilingual rendering checks.
## 2026-09-12 — Remove Periphery-confirmed dead code
**Status:** Completed on 2026-09-12; both app builds, focused tests, formatting, and localization checks pass.
**Evidence:** Periphery 3.8.0 reported 461 diagnostics while indexing the TFM scheme and 73 while indexing TFI.
After excluding test-only diagnostics and reconciling the two platform scans, source-reference checks confirm 34
unused production imports, several uncalled private/internal helpers and compatibility accessors, and stored fields
that are written but never read. SwiftUI/AppKit lifecycle entry points, Codable payload fields, C callback parameters,
protocol witnesses, and fields whose synthesized conformance reads are intentionally excluded from this pass.
**Action:** Repair the stale Periphery scheme configuration, remove only declarations confirmed by both index data
and `rg`, then rerun both app builds, focused tests for changed behavior, strict formatting, localization validation,
and Periphery to measure the reduced result set.
**Estimated reduction:** 70–110 production lines without adding an abstraction.
**Result:** Removed 34 unused production imports, three empty source files, and confirmed dead initializers,
accessors, view helpers, state storage, and background-command metadata. Production code changed by net -150 lines.
The TFM production diagnostic count fell from 114 to 69 (461 to 416 including test diagnostics), while the TFI
production count fell from 66 to 41. Remaining reports are retained protocol/C callback parameters, synthesized
conformance or Codable fields, dynamic UI entry points, and separate candidates that require their own behavioral
review. The macOS and iOS builds pass, as do 49 focused Agent background command, port forwarding, and speech tests,
strict Swift formatting, Simplified Chinese localization validation, and `git diff --check`.
## 2026-09-12 — Remove UI implementation-type assertions
**Status:** Completed.
**Evidence:** `TranscriptListViewBridgeTests` directly asserts dedicated concrete row classes, the mobile Terminal
list suite reflects private SwiftUI/ListViewKit view types, and the macOS Host chooser render test additionally
requires an `NSTableView`. Existing behavior tests already cover rendering, row identity and reuse, dynamic type,
virtualization, interaction, and state isolation without requiring those implementation choices.
**Action:** Remove the two tests whose only result is a concrete UI type, drop their test-only helper, and keep the
Host chooser environment/rendering coverage without asserting its underlying AppKit table class. Preserve UI type
access that is required to exercise or observe actual interaction behavior.
**Estimated reduction:** 30–40 test lines with no production or public API changes.
-1
View File
@@ -1,5 +1,4 @@
import GhosttyKit
import TermindInfrastructure
import TermindRemoteInfrastructure
nonisolated enum MobileRuntimeVersions {
@@ -125,8 +125,6 @@ final class MobileAssistantModel {
}
}
func stop() { for conversation in store.conversations { conversation.stop() } }
func suspendRecording() { speech.cancelAll() }
func expireBackground() {
@@ -2,7 +2,6 @@ import AgentKit
import Foundation
import Observation
import TermindApplication
import TermindDomain
@MainActor @Observable
final class MobileMCPEditorModel {
@@ -36,7 +35,6 @@ final class MobileMCPEditorModel {
self.discover = discover
key.begin(in: ai, reference: server.credentialRef)
}
var isDirty: Bool { draft != original || key.isDirty }
var hasValidURL: Bool {
guard let url = URL(string: draft.url.trimmingCharacters(in: .whitespacesAndNewlines)),
let scheme = url.scheme?.lowercased(), let host = url.host, !host.isEmpty
@@ -4,7 +4,6 @@ import SwiftUI
import TermindApplication
import TermindDomain
import TermindPresentation
import TermindRemoteInfrastructure
import UniformTypeIdentifiers
private struct MobileConversationAttachmentDropModifier: ViewModifier {
@@ -1,7 +1,6 @@
import AgentKit
import SwiftUI
import TermindApplication
import TermindDomain
enum MobileAssistantLibraryRoute: String, Identifiable, Hashable {
case conversations
@@ -133,7 +133,7 @@ struct MobileHostEditor: View {
for rule: PortForwardingRule, runtimeIsActive: Bool
) -> PortForwardingEditorPolicy {
PortForwardingEditorPolicy(
rule: rule, host: draft,
rule: rule,
savedRule: app.forwardingLibrary.portForwarding(rule.id), savedHost: library.host(draft.id),
isEnabled: app.forwardings.isEnabled(rule.id), isRuntimeActive: runtimeIsActive)
}
@@ -57,7 +57,7 @@ struct MobileForwardingEditor: View {
private var state: PortForwardingEditorPolicy {
PortForwardingEditorPolicy(
rule: draft, host: host,
rule: draft,
savedRule: app.forwardingLibrary.portForwarding(draft.id), savedHost: app.library.host(host.id),
isEnabled: app.forwardings.isEnabled(draft.id), isRuntimeActive: runtime.isActive)
}
@@ -1,7 +1,6 @@
import SwiftUI
import TermindDomain
import TermindPresentation
import TermindRemoteInfrastructure
struct MobileRemoteFileEditor: View {
enum Mode { case rename, permissions }
@@ -4,7 +4,6 @@ import GhosttyTerminal
import Observation
import TermindApplication
import TermindDomain
import TermindInfrastructure
import TermindPresentation
import TermindRemoteInfrastructure
import UIKit
@@ -129,7 +128,6 @@ final class MobileWorkspace: Identifiable {
private let connection: RemoteSessionConnectionController
private var viewport = InMemoryTerminalViewport(columns: 80, rows: 24)
var uploadConflictCount: Int { pendingUpload?.batch.conflictCount ?? 0 }
var transferConflictCount: Int {
pendingRemoteCopy?.batch.conflictCount
?? pendingDownload?.batch.conflictCount
@@ -3,7 +3,6 @@ import Foundation
nonisolated struct MobileWorkspaceDescriptor: Hashable, Identifiable, Sendable {
enum Kind: String, CaseIterable, Sendable {
case ssh, sftp
var title: String { self == .ssh ? "SSH" : "SFTP" }
var symbol: String { self == .ssh ? "terminal" : "folder" }
}
-29
View File
@@ -8,26 +8,6 @@ import UIKit
@Suite(.serialized) @MainActor
struct MobileTerminalListTests {
@Test func snippetsAndHistoryMountNativeSwiftUILists() async throws {
let app = try MobileAppModel(inMemory: true)
let host = TermindDomain.Host(name: "Test", host: "example.invalid")
app.library.upsert(host)
app.library.upsert(Snippet(title: "List files", body: "ls -la\npwd"))
app.commandHistory.record("pwd", for: host.id)
let workspace = MobileWorkspace(
descriptor: MobileWorkspaceDescriptor(hostID: host.id, title: host.name, kind: .ssh),
hosts: app.library,
commandHistory: app.commandHistory)
try await withHostedView(MobileTerminalSnippetsView(library: app.library, workspace: workspace)) { root in
expectNativeList(in: root.view)
}
try await withHostedView(MobileCommandHistoryView(workspace: workspace)) { root in
expectNativeList(in: root.view)
}
}
@Test func deletingAnEndpointUpdatesTheNativeHistoryList() async throws {
let app = try MobileAppModel(inMemory: true)
let host = TermindDomain.Host(name: "Test", host: "example.invalid")
@@ -152,15 +132,6 @@ struct MobileTerminalListTests {
try await check(controller)
}
private func expectNativeList(in root: UIView) {
let legacyList = descendants(of: UIScrollView.self, in: root).first {
String(reflecting: type(of: $0)).contains("ListViewKit.ListView")
}
#expect(legacyList == nil)
#expect(descendants(of: MobileHostingListRow.self, in: root).isEmpty)
#expect(!nativeVisibleCells(in: root).isEmpty)
}
private func nativeVisibleCells(in root: UIView) -> [UIView] {
let collectionCells = descendants(of: UICollectionViewCell.self, in: root)
let tableCells = descendants(of: UITableViewCell.self, in: root)
-19
View File
@@ -60,23 +60,4 @@ nonisolated enum DesktopSettingsKey {
}
/// The last approval posture selected in the Assistant composer.
static let agentPermissionMode = "assistant.permissionMode"
static var isWebSearchEnabled: Bool {
UserDefaults.standard.object(forKey: SettingsKey.webSearchEnabled) as? Bool
?? SettingsKey.defaultWebSearchEnabled
}
/// Let a provider run the search on its own servers where it can.
///
/// On by default, because where it works it beats a snippet API: the
/// provider reads whole pages and the model simply knows things. Off is the
/// compatibility position — every model then goes through the backend
/// below, which behaves identically on every endpoint.
///
/// The switch exists because whether a given endpoint really runs the
/// search is a *guess*: `ModelProvider.supportsNativeWebSearch(model:)`
/// infers it from the model id, and a gateway can make that inference wrong
/// in either direction. This is the escape hatch for when it is.
static var isBuiltInWebSearchEnabled: Bool {
UserDefaults.standard.object(forKey: SettingsKey.webSearchBuiltIn) as? Bool
?? SettingsKey.defaultWebSearchBuiltIn
}
}
-1
View File
@@ -1,5 +1,4 @@
import GhosttyKit
import TermindInfrastructure
import TermindRemoteInfrastructure
nonisolated enum RuntimeVersions {
@@ -1 +0,0 @@
import TermindApplication
-1
View File
@@ -1,5 +1,4 @@
import SwiftUI
import TermindApplication
import TermindDomain
extension CommandRisk {
@@ -1,7 +1,6 @@
import AgentKit
import AppKit
import Foundation
import TermindApplication
import UniformTypeIdentifiers
nonisolated enum ContextAttachmentOpener {
@@ -3,7 +3,6 @@ import Combine
import MarkdownParser
import MarkdownView
import SwiftUI
import TermindApplication
import TermindPresentation
/// The Markdown entry point used by the transcript.
@@ -1,5 +1,4 @@
import SwiftUI
import TermindApplication
import TermindDomain
/// The one fenced block Termind owns instead of MarkdownView: a shell command
@@ -22,7 +22,7 @@ struct PortForwardingEditorState {
isEnabled: Bool, runtime: PortForwardingRuntimeState
) {
let shared = PortForwardingEditorPolicy(
rule: rule, host: host, savedRule: savedRule, savedHost: savedHost,
rule: rule, savedRule: savedRule, savedHost: savedHost,
isEnabled: isEnabled, isRuntimeActive: runtime.isActive)
hasSavedConfiguration = shared.hasSavedConfiguration
self.isEnabled = shared.isEnabled
@@ -158,27 +158,10 @@ struct ModelProviderEditorPage: View {
app.aiConfiguration.upsert(provider)
}
// MARK: - Binding
private var endpoint: ProviderEndpoint {
app.aiConfiguration.endpoint(for: modelProviderID)
}
/// A two-way binding into the endpoint, written through the store's one
/// derivation so the composed URL and the API format are updated together.
private func endpointBinding<Value>(
_ keyPath: WritableKeyPath<ProviderEndpoint, Value>
) -> Binding<Value> {
Binding(
get: { endpoint[keyPath: keyPath] },
set: { newValue in
var updated = endpoint
updated[keyPath: keyPath] = newValue
app.aiConfiguration.setEndpoint(updated, for: modelProviderID)
}
)
}
/// A two-way binding into the stored provider.
///
/// Reads and writes go through `AIConfigurationModel` rather than through a local copy,
@@ -638,8 +638,6 @@ private struct TerminalSettings: View {
@Environment(AppModel.self) private var app
private var fontSize: Double { app.terminalSettings.preferences.fontSize }
private var fontFollowTheme: Bool { app.terminalSettings.preferences.fontFollowTheme }
private var shellIntegration: Bool { app.terminalSettings.preferences.shellIntegration }
private var remoteWindowTitleInMenuBar: Bool { app.terminalSettings.preferences.remoteWindowTitleInMenuBar }
private var darkTheme: String { app.terminalSettings.preferences.darkTheme }
private var lightTheme: String { app.terminalSettings.preferences.lightTheme }
private var darkCustomEnabled: Bool { app.terminalSettings.preferences.darkCustomEnabled }
@@ -9,13 +9,6 @@ nonisolated enum TerminalCustomConfigurationScheme: String, Identifiable, Hashab
var id: String { rawValue }
var settingsKey: String {
switch self {
case .light: SettingsKey.terminalCustomConfigurationLight
case .dark: SettingsKey.terminalCustomConfigurationDark
}
}
var preferenceKeyPath: WritableKeyPath<TerminalPreferences, String> {
switch self {
case .light: \.lightCustomConfiguration
@@ -242,10 +242,6 @@ struct StatusBarView: View {
private var host: Host? { pane?.hostID.flatMap { app.hostLibrary.host($0) } }
private func item(_ symbol: String, _ text: String) -> some View {
Label(text, systemImage: symbol).truncationMode(.head)
}
private func workingDirectoryButton(_ path: String, displayed: String) -> some View {
Button {
app.onboarding.recordUse(of: .statusBarPath)
@@ -2,10 +2,6 @@ import SwiftData
@MainActor
extension ModelContext {
func savePendingChanges() throws {
try commit {}
}
func commit(_ operation: () throws -> Void) throws {
do {
try operation()
@@ -64,7 +64,6 @@ actor LoopbackHTTPServer {
private var handler: Handler?
private var stateHandler: StateHandler?
private var readyListeners = 0
private(set) var state: State = .stopped
func start(
port: UInt16, token: String,
@@ -78,7 +77,6 @@ actor LoopbackHTTPServer {
self.token = token
self.handler = handler
self.stateHandler = stateHandler
state = .starting
stateHandler(.starting)
do {
@@ -125,7 +123,6 @@ actor LoopbackHTTPServer {
connections.removeAll()
handler = nil
readyListeners = 0
state = .stopped
stateHandler?(.stopped)
stateHandler = nil
}
@@ -135,11 +132,9 @@ actor LoopbackHTTPServer {
case .ready:
readyListeners += 1
if readyListeners == listeners.count {
state = .ready(port: port)
stateHandler?(.ready(port: port))
}
case .failed(let error):
state = .failed(error.localizedDescription)
stateHandler?(.failed(error.localizedDescription))
for listener in listeners { listener.cancel() }
default:
@@ -31,15 +31,3 @@ extension AIConfigurationModel {
)
}
}
extension SkillLibraryModel {
convenience init(
modelContext: ModelContext,
seedDefaults: UserDefaults = .standard
) {
self.init(
persistence: SkillPersistence(context: modelContext),
seedDefaults: seedDefaults
)
}
}
@@ -1,22 +0,0 @@
import Foundation
extension DispatchQueue {
func perform<T: Sendable>(
_ operation: @escaping @Sendable () throws -> T
) async throws -> T {
try await withCheckedThrowingContinuation { continuation in
async {
do { continuation.resume(returning: try operation()) } catch { continuation.resume(throwing: error) }
}
}
}
func perform(_ operation: @escaping @Sendable () -> Void) async {
await withCheckedContinuation { continuation in
async {
operation()
continuation.resume()
}
}
}
}
@@ -1,7 +0,0 @@
import SwiftData
import TermindPersistence
import TermindRemoteInfrastructure
extension AISecretStore {
convenience init(context: ModelContext) { self.init(context: context, keychain: KeychainSecretStore()) }
}
@@ -1,4 +1,3 @@
import TermindInfrastructure
import TermindRemoteInfrastructure
extension KeychainSecretStore {
+7 -7
View File
@@ -103,7 +103,7 @@ struct AgentBackgroundCommandTests {
let commands = AgentBackgroundCommands(sweepInterval: nil)
let session = StubSession()
let handle = await commands.adopt(
hostID: UUID(), command: "make", stream: AgentCommandOutputStream { _, _ in },
stream: AgentCommandOutputStream { _, _ in },
task: Task { exited(0, stdout: "built") }, session: session
)
@@ -126,7 +126,7 @@ struct AgentBackgroundCommandTests {
let released = AsyncRelease()
let stream = AgentCommandOutputStream(interval: .zero) { _, _ in }
let handle = await commands.adopt(
hostID: UUID(), command: "deploy", stream: stream,
stream: stream,
task: Task {
await released.wait()
return exited(0)
@@ -160,7 +160,7 @@ struct AgentBackgroundCommandTests {
let released = AsyncRelease()
let stream = AgentCommandOutputStream(interval: .zero) { _, _ in }
let handle = await commands.adopt(
hostID: UUID(), command: "deploy", stream: stream,
stream: stream,
task: Task {
await released.wait()
return exited(0)
@@ -184,7 +184,7 @@ struct AgentBackgroundCommandTests {
let released = AsyncRelease()
let start = ContinuousClock.now
let stale = await commands.adopt(
hostID: UUID(), command: "tail -f", stream: AgentCommandOutputStream { _, _ in },
stream: AgentCommandOutputStream { _, _ in },
task: Task {
await released.wait()
return exited(0)
@@ -192,7 +192,7 @@ struct AgentBackgroundCommandTests {
now: start
)
_ = await commands.adopt(
hostID: UUID(), command: "deploy", stream: AgentCommandOutputStream { _, _ in },
stream: AgentCommandOutputStream { _, _ in },
task: Task {
await released.wait()
return exited(0)
@@ -221,7 +221,7 @@ struct AgentBackgroundCommandTests {
let session = StubSession()
let released = AsyncRelease()
_ = await commands.adopt(
hostID: UUID(), command: "tail -f", stream: AgentCommandOutputStream { _, _ in },
stream: AgentCommandOutputStream { _, _ in },
task: Task {
await released.wait()
return exited(0)
@@ -253,7 +253,7 @@ struct AgentBackgroundCommandTests {
return exited(0)
}
let handle = await commands.adopt(
hostID: UUID(), command: "deploy", stream: AgentCommandOutputStream { _, _ in },
stream: AgentCommandOutputStream { _, _ in },
task: task, session: StubSession()
)
-79
View File
@@ -511,85 +511,6 @@ struct ArchitectureBoundaryTests {
}
}
@Test
func mobileNavigationAndMetricsShareTheDesktopDesign() throws {
let root = try String(
contentsOf: sourceRoot.appending(path: "../TFI/App/MobileRootView.swift"), encoding: .utf8)
#expect(!root.contains("Welcome"))
#expect(root.contains("Label(\"Transfers\", systemImage: \"arrow.up.arrow.down\")"))
let vault = try String(
contentsOf: sourceRoot.appending(path: "../TFI/Features/Vault/MobileVaultView.swift"), encoding: .utf8)
let navigation = try String(
contentsOf: sourceRoot.appending(path: "../TFI/Features/Shell/MobileNavigation.swift"), encoding: .utf8)
#expect(navigation.contains("case vault, metrics, workspaces, assistant, settings"))
#expect(!vault.contains("Label(\"Port Forwarding\", systemImage:"))
#expect(vault.contains("open(host.id, .ssh)"))
let cards = try String(
contentsOf: sourceRoot.appending(path: "../TFI/UI/Managed/MobileCards.swift"), encoding: .utf8)
#expect(vault.contains("MobileActionCard {"))
#expect(cards.contains(".contextMenu {"))
#expect(cards.contains("actions"))
#expect(!vault.contains("Import SSH Files"))
let keys = try String(
contentsOf: sourceRoot.appending(path: "../TFI/Features/Keychain/MobileCredentialsView.swift"),
encoding: .utf8)
#expect(!keys.contains("draft.notes"))
let settings = try String(
contentsOf: sourceRoot.appending(path: "../TFI/Features/Settings/MobileSettingsView.swift"), encoding: .utf8
)
#expect(settings.contains("Label(\"App\", systemImage: \"app.badge\")"))
#expect(settings.contains("Label(\"Sync\", systemImage: \"icloud\")"))
#expect(!settings.contains("Live Activities"))
let mobile = try String(
contentsOf: sourceRoot.appending(path: "../TFI/Application/Metrics/MobileMetricsModel.swift"),
encoding: .utf8)
#expect(mobile.contains("ServerMetricsModel("))
#expect(!mobile.contains("while !Task.isCancelled"))
let workspace = try String(
contentsOf: sourceRoot.appending(path: "../TFI/Features/Workspaces/MobileWorkspaceView.swift"),
encoding: .utf8)
#expect(workspace.contains(".toolbarTitleMenu"))
#expect(!workspace.contains("connectionMenuAction"))
#expect(workspace.contains("let closeWorkspace: (UUID) -> Void"))
#expect(workspace.contains("closeWorkspace(workspace.id)"))
#expect(workspace.contains("MobileOverflowMenuLabel(\"More\")"))
#expect(workspace.contains("Button(\"Generate Command\", systemImage: \"sparkles\")"))
let snippets = try #require(workspace.range(of: "Button(\"Snippets\", systemImage: \"curlybraces\")"))
let history = try #require(workspace.range(of: "Button(\"History\", systemImage: \"clock\")"))
let historyDivider = try #require(workspace[history.upperBound...].range(of: "Divider()"))
let nextMatch = try #require(workspace.range(of: "Button(\"Next Match\", systemImage: \"chevron.down\")"))
#expect(snippets.lowerBound < history.lowerBound)
#expect(history.lowerBound < historyDivider.lowerBound)
#expect(historyDivider.lowerBound < nextMatch.lowerBound)
#expect(workspace.contains(".dropDestination(for: URL.self)"))
#expect(workspace.contains(".toolbar(.hidden, for: .tabBar)"))
#expect(workspace.contains("guard let text = UIPasteboard.general.string else { return }"))
#expect(workspace.contains("workspace.insertTerminalText(text)"))
let mobileScene = try String(
contentsOf: sourceRoot.appending(path: "../TFI/App/MobileSceneModel.swift"), encoding: .utf8)
let mobileCommands = try String(
contentsOf: sourceRoot.appending(path: "../TFI/App/MobileCommands.swift"), encoding: .utf8)
#expect(root.contains(".focusedSceneValue(\\.mobileScene, scene)"))
#expect(mobileScene.contains("func closeWorkspace(_ id: MobileWorkspace.ID)"))
#expect(mobileCommands.contains("@FocusedValue(\\.mobileScene)"))
#expect(!workspace.contains("Circle().fill(workspace.connected"))
let keyboard = try String(
contentsOf: sourceRoot.appending(path: "../TFI/Features/Terminal/MobileTerminalKeyboard.swift"),
encoding: .utf8)
#expect(keyboard.contains("ScrollView(.horizontal"))
let pasteButton = try #require(keyboard.range(of: "pasteButton"))
let keyboardToggle = try #require(keyboard.range(of: "keyboardToggle"))
#expect(pasteButton.lowerBound < keyboardToggle.lowerBound)
#expect(keyboard.contains("Button(action: paste)"))
#expect(keyboard.contains("Label(\"Paste\", systemImage: \"doc.on.clipboard\")"))
#expect(keyboard.contains(".padding(.horizontal, 8)"))
#expect(keyboard.contains("static let barHeight: CGFloat = 44"))
#expect(keyboard.contains(".buttonStyle(.plain)"))
#expect(!keyboard.contains(".buttonStyle(.bordered)"))
#expect(keyboard.contains(".frame(width: 44, height: 44)"))
#expect(keyboard.contains(".frame(height: Self.barHeight)"))
}
@Test
func mobilePortUsesSharedStoresAndSeparateSigningFiles() throws {
let mobile = try String(
+1 -2
View File
@@ -47,7 +47,7 @@ struct MetricsInteractionTests {
}
@Test(arguments: [HostChooserPurpose.connect, .batchStartup, .metrics])
func hostChoosersMountNativeListsWithTheirRequiredEnvironment(
func hostChoosersRenderWithTheirRequiredEnvironment(
purpose: HostChooserPurpose
) async throws {
let host = Host(name: "Fixture", host: "fixture.invalid")
@@ -68,7 +68,6 @@ struct MetricsInteractionTests {
try await Task.sleep(for: .milliseconds(100))
hosting.layoutSubtreeIfNeeded()
#expect(hosting.fittingSize.height > 0)
#expect(hasTable(hosting))
#expect(windowModel.workspaces.isEmpty)
#expect(app.serverMetrics.servers.isEmpty)
}
-15
View File
@@ -352,21 +352,6 @@ struct TranscriptListViewBridgeTests {
#expect(!presentation.disclosures.hasPendingAutoCollapse(id))
}
@Test
func entryKindsUseDedicatedReusableRowTypes() {
let coordinator = TranscriptListView.Coordinator()
let listView = coordinator.makeListView()
let empty = TranscriptEntry(id: UUID(), content: .empty)
let assistant = assistant(id: UUID(), text: "Hello")
listView.frame = CGRect(x: 0, y: 0, width: 420, height: 800)
update(coordinator, listView, entries: [empty, assistant])
listView.layoutSubtreeIfNeeded()
#expect(listView.rowView(for: empty.id) is TranscriptEmptyRow)
#expect(listView.rowView(for: assistant.id) is TranscriptAssistantRow)
}
@Test
func streamingUpdateKeepsTheMountedAssistantRowIdentity() throws {
let coordinator = TranscriptListView.Coordinator()
@@ -10,7 +10,6 @@ nonisolated struct WorkspaceActivityAttributes: ActivityAttributes {
var connected: Bool
var iconAsset: String? = nil
var symbol: String { "server.rack" }
var destinationURL: URL { WorkspaceActivityAttributes.destinationURL(for: id) }
}