mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-18 00:23:25 -06:00
0335317679
* fix(macos): reconcile external gateway config edits * refactor(macos): split gateway reconciliation helpers * chore(macos): refresh native i18n inventory * refactor(macos): remove obsolete gateway config helper * test(macos): seed watcher fixtures as canonical * fix(macos): add gateway config conflict recovery
137 lines
4.2 KiB
Swift
137 lines
4.2 KiB
Swift
import Foundation
|
|
import SwiftUI
|
|
|
|
extension AppState {
|
|
enum GatewayConfigField: String, CaseIterable {
|
|
case mode = "gateway.mode"
|
|
case remoteTransport = "gateway.remote.transport"
|
|
case remoteUrl = "gateway.remote.url"
|
|
case remoteTarget = "gateway.remote.sshTarget"
|
|
case remoteIdentity = "gateway.remote.sshIdentity"
|
|
case remoteHostKeyPolicy = "gateway.remote.sshHostKeyPolicy"
|
|
case remoteToken = "gateway.remote.token"
|
|
|
|
var remoteKey: String? {
|
|
switch self {
|
|
case .mode:
|
|
nil
|
|
case .remoteTransport:
|
|
"transport"
|
|
case .remoteUrl:
|
|
"url"
|
|
case .remoteTarget:
|
|
"sshTarget"
|
|
case .remoteIdentity:
|
|
"sshIdentity"
|
|
case .remoteHostKeyPolicy:
|
|
"sshHostKeyPolicy"
|
|
case .remoteToken:
|
|
"token"
|
|
}
|
|
}
|
|
|
|
var displayName: String {
|
|
switch self {
|
|
case .mode:
|
|
String(localized: "Gateway location")
|
|
case .remoteTransport:
|
|
String(localized: "Transport")
|
|
case .remoteUrl:
|
|
String(localized: "Gateway URL")
|
|
case .remoteTarget:
|
|
String(localized: "SSH target")
|
|
case .remoteIdentity:
|
|
String(localized: "Identity file")
|
|
case .remoteHostKeyPolicy:
|
|
String(localized: "SSH host key policy")
|
|
case .remoteToken:
|
|
String(localized: "Gateway token")
|
|
}
|
|
}
|
|
}
|
|
|
|
struct GatewayConfigConflict: Equatable {
|
|
let fields: [GatewayConfigField]
|
|
let fieldNames: [String]
|
|
let message: String
|
|
}
|
|
|
|
enum GatewayConfigValue: Equatable {
|
|
case missing
|
|
case json(Data)
|
|
}
|
|
|
|
struct GatewayConfigSnapshot {
|
|
static let empty = GatewayConfigSnapshot(values: [:])
|
|
let values: [GatewayConfigField: GatewayConfigValue]
|
|
|
|
subscript(field: GatewayConfigField) -> GatewayConfigValue {
|
|
self.values[field] ?? .missing
|
|
}
|
|
}
|
|
|
|
struct RemoteGatewayConfigDraft {
|
|
var transport: RemoteTransport
|
|
var remoteUrl: String
|
|
var remoteHost: String?
|
|
var remoteTarget: String
|
|
var remoteIdentity: String
|
|
var remoteToken: String
|
|
var dirtyFields: Set<GatewayConfigField>
|
|
}
|
|
|
|
struct GatewayConfigSyncDraft {
|
|
var connectionMode: ConnectionMode
|
|
var remoteTransport: RemoteTransport
|
|
var remoteTarget: String
|
|
var remoteIdentity: String
|
|
var remoteUrl: String
|
|
var remoteToken: String
|
|
var dirtyFields: Set<GatewayConfigField>
|
|
}
|
|
|
|
struct GatewaySelectionSnapshot: Equatable {
|
|
let connectionMode: ConnectionMode
|
|
let remoteTransport: RemoteTransport
|
|
let remoteUrl: String
|
|
let remoteTarget: String
|
|
}
|
|
}
|
|
|
|
struct GatewayConfigConflictRecoveryView: View {
|
|
@Bindable var state: AppState
|
|
|
|
var body: some View {
|
|
if let conflict = state.gatewayConfigConflict {
|
|
HStack(alignment: .top, spacing: 10) {
|
|
Image(systemName: "exclamationmark.triangle.fill")
|
|
.foregroundStyle(.orange)
|
|
.accessibilityHidden(true)
|
|
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
Text(verbatim: conflict.message)
|
|
.font(.footnote)
|
|
.fixedSize(horizontal: false, vertical: true)
|
|
|
|
HStack(spacing: 8) {
|
|
Button("Use file version") {
|
|
self.state.useFileGatewayConfigConflict()
|
|
}
|
|
.buttonStyle(.bordered)
|
|
|
|
Button("Keep my edits") {
|
|
self.state.keepGatewayConfigEdits()
|
|
}
|
|
.buttonStyle(.borderedProminent)
|
|
}
|
|
.controlSize(.small)
|
|
}
|
|
}
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.padding(.horizontal, 14)
|
|
.padding(.vertical, 11)
|
|
.background(.orange.opacity(0.08))
|
|
}
|
|
}
|
|
}
|