Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/swift-ios/App/NativeFeatureClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6710,6 +6710,7 @@ final class NativeFeatureClient: FeatureClient, FeatureDeviceManaging,
mapped.machineIcon = serverConfigsByEnvironmentID[environment.id]?.settings?.environmentIcon
?? environment.descriptor?.platform.machine
mapped.newProjectsRoot = serverConfigsByEnvironmentID[environment.id]?.newProjectsRoot
mapped.addProjectBaseDirectory = serverConfigsByEnvironmentID[environment.id]?.settings?.addProjectBaseDirectory
mapped.supportsScratch = serverConfigsByEnvironmentID[environment.id]?.scratchWorkspaceRoot != nil
mapped.canCustomizeIcon = serverConfigsByEnvironmentID[environment.id]?.environment?.capabilities.environmentIcon
?? environment.descriptor?.capabilities.environmentIcon
Expand Down
3 changes: 3 additions & 0 deletions apps/swift-ios/Core/ServerConfigModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ public struct ServerSettingsSnapshot: Codable, Equatable, Sendable {
public var environmentIcon: String? = nil
public var sourceControlWritingStyle: JSONValue? = nil
public var worktreesDirectory: String? = nil
public var addProjectBaseDirectory: String? = nil
public var previousWorktreesDirectories: [String]? = nil
public var removeAgentCreditsOnMerge: Bool? = nil
public var sourceControlHosts: [String: [String: JSONValue]]? = nil
Expand Down Expand Up @@ -310,6 +311,7 @@ public struct ServerSettingsSnapshot: Codable, Equatable, Sendable {
case environmentIcon
case sourceControlWritingStyle
case worktreesDirectory, previousWorktreesDirectories, removeAgentCreditsOnMerge, sourceControlHosts
case addProjectBaseDirectory
case worktreeSubmodules, storageCleanup, worktreeCleanup
case defaultAutoPull, responseStreamingMode, projectSettingsOverrides, projectSettingsFolded, providerInstances
case favorites, providerModelPreferences
Expand All @@ -335,6 +337,7 @@ public struct ServerSettingsSnapshot: Codable, Equatable, Sendable {
environmentIcon = try container.decodeIfPresent(String.self, forKey: .environmentIcon)
sourceControlWritingStyle = try container.decodeIfPresent(JSONValue.self, forKey: .sourceControlWritingStyle)
worktreesDirectory = try container.decodeIfPresent(String.self, forKey: .worktreesDirectory)
addProjectBaseDirectory = try container.decodeIfPresent(String.self, forKey: .addProjectBaseDirectory)
previousWorktreesDirectories = try container.decodeIfPresent([String].self, forKey: .previousWorktreesDirectories)
removeAgentCreditsOnMerge = try container.decodeIfPresent(Bool.self, forKey: .removeAgentCreditsOnMerge)
sourceControlHosts = try container.decodeIfPresent([String: [String: JSONValue]].self, forKey: .sourceControlHosts)
Expand Down
4 changes: 3 additions & 1 deletion apps/swift-ios/Features/Shared/FeatureModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public struct FeatureEnvironment: Identifiable, Sendable, Equatable, Hashable, C
public var machineIcon: String? = nil
public var supportsScratch: Bool? = nil
public var newProjectsRoot: String? = nil
public var addProjectBaseDirectory: String? = nil
public var canCustomizeIcon: Bool? = nil
public var supportsWorktreesDirectory: Bool? = nil
public var supportsServerBrowser: Bool? = nil
Expand Down Expand Up @@ -97,7 +98,7 @@ public struct FeatureEnvironment: Identifiable, Sendable, Equatable, Hashable, C
case connectionState
case connectionDetail
case machineIcon
case supportsScratch, newProjectsRoot
case supportsScratch, newProjectsRoot, addProjectBaseDirectory
case canCustomizeIcon, supportsWorktreesDirectory, supportsServerBrowser, permissions
}

Expand All @@ -116,6 +117,7 @@ public struct FeatureEnvironment: Identifiable, Sendable, Equatable, Hashable, C
connectionDetail = try container.decodeIfPresent(String.self, forKey: .connectionDetail)
machineIcon = try container.decodeIfPresent(String.self, forKey: .machineIcon)
newProjectsRoot = try container.decodeIfPresent(String.self, forKey: .newProjectsRoot)
addProjectBaseDirectory = try container.decodeIfPresent(String.self, forKey: .addProjectBaseDirectory)
supportsScratch = try container.decodeIfPresent(Bool.self, forKey: .supportsScratch)
canCustomizeIcon = try container.decodeIfPresent(Bool.self, forKey: .canCustomizeIcon)
supportsWorktreesDirectory = try container.decodeIfPresent(Bool.self, forKey: .supportsWorktreesDirectory)
Expand Down
54 changes: 49 additions & 5 deletions apps/swift-ios/Features/Workspace/AddProjectView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ public struct AddProjectView: View {
@State private var isBrowsing = false
@State private var browseError: String?
@State private var browseRequestID: UUID?
/// The base directory seeded for an environment, kept until the user browses.
@State private var seededBaseDirectory: (environmentID: String, path: String)?

@State private var discovery: SourceControlDiscoveryResult?
@State private var isDiscovering = false
Expand Down Expand Up @@ -154,6 +156,9 @@ public struct AddProjectView: View {
updateSuggestedDestination()
errorMessage = nil
}
.onChange(of: addProjectBaseDirectory) {
reseedLateBaseDirectory()
}
.task(id: selectedEnvironmentID) {
guard selectedEnvironmentID != nil else { return }
resetEnvironmentState()
Expand Down Expand Up @@ -631,7 +636,9 @@ public struct AddProjectView: View {
private func resetEnvironmentState() {
if mode == .newProject && selectedEnvironment?.newProjectsRoot == nil { mode = .folder }
publishesToGitHub = false
browsePath = "~/"
let baseDirectory = addProjectBaseDirectory
seededBaseDirectory = selectedEnvironmentID.map { ($0, baseDirectory) }
browsePath = baseDirectory
browseResult = nil
browseError = nil
browseRequestID = nil
Expand All @@ -644,13 +651,45 @@ public struct AddProjectView: View {
pendingManagedClone = nil
cloneRequestID = nil
didEditDestination = false
localPath = "~/"
localPath = baseDirectory
destinationPath = repositoryInput.isEmpty
? "~/"
: ProjectCreationPath.appending(repositoryName, to: "~/")
? baseDirectory
: ProjectCreationPath.appending(repositoryName, to: baseDirectory)
errorMessage = nil
}

/// A connected environment can publish its shell before its config, so the
/// setting may arrive after seeding. Follow it only while the paths are untouched.
private func reseedLateBaseDirectory() {
let baseDirectory = addProjectBaseDirectory
guard selectedEnvironment != nil,
let seeded = seededBaseDirectory,
seeded.environmentID == selectedEnvironmentID,
seeded.path != baseDirectory,
localPath == seeded.path,
!didEditDestination,
!isSubmitting,
cloneRequestID == nil,
pendingCloneRegistration == nil,
pendingManagedClone == nil else {
return
}
seededBaseDirectory = (seeded.environmentID, baseDirectory)
browsePath = baseDirectory
localPath = baseDirectory
destinationPath = repositoryInput.isEmpty
? baseDirectory
: ProjectCreationPath.appending(repositoryName, to: baseDirectory)
Task { await loadDirectory(baseDirectory, updateSelection: false) }
}

/// Matches web: a blank per-environment setting falls back to the home directory.
private var addProjectBaseDirectory: String {
let configured = selectedEnvironment?.addProjectBaseDirectory?
.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
return configured.isEmpty ? "~/" : ProjectCreationPath.directoryBrowsePath(configured)
}

private func loadDiscovery() async {
guard let environmentID = selectedEnvironmentID,
let projectClient else {
Expand Down Expand Up @@ -695,6 +734,7 @@ public struct AddProjectView: View {
}
let requestedPath = path.trimmingCharacters(in: .whitespacesAndNewlines)
guard !requestedPath.isEmpty else { return }
if updateSelection { seededBaseDirectory = nil }
let requestID = UUID()
browseRequestID = requestID
isBrowsing = true
Expand Down Expand Up @@ -859,7 +899,11 @@ public struct AddProjectView: View {

errorMessage = nil
isSubmitting = true
defer { isSubmitting = false }
defer {
isSubmitting = false
// The base directory may have arrived while this lookup blocked reseeding.
reseedLateBaseDirectory()
}
do {
let result = try await projectClient.lookupProjectRepository(
environmentID: environment.id,
Expand Down
Loading