PadXcode-iPad/Config/LocalGitConfiguration.swift

114 lines
4.6 KiB
Swift

import Foundation
import SwiftUI
import UIKit
struct LocalGitServer: Codable, Identifiable {
let id: UUID
var displayName: String
var tailscaleIP: String
var sshUser: String
var reposBasePath: String
var sshPort: Int
var sshBaseURL: String {
let p = sshPort == 22 ? "" : ":\(sshPort)"
return "ssh://\(sshUser)@\(tailscaleIP)\(p)"
}
func repoURL(for name: String) -> String { "\(sshBaseURL)\(reposBasePath)/\(name).git" }
init(id: UUID = UUID(), displayName: String, tailscaleIP: String,
sshUser: String, reposBasePath: String, sshPort: Int = 22) {
self.id = id; self.displayName = displayName; self.tailscaleIP = tailscaleIP
self.sshUser = sshUser; self.reposBasePath = reposBasePath; self.sshPort = sshPort
}
}
final class LocalGitServerStore: ObservableObject {
@Published var servers: [LocalGitServer] = [] { didSet { persist() } }
private let key = "padxcode.localGitServers"
init() { load() }
func add(_ s: LocalGitServer) { servers.append(s) }
func remove(id: UUID) { servers.removeAll { $0.id == id } }
private func persist() {
if let d = try? JSONEncoder().encode(servers) { UserDefaults.standard.set(d, forKey: key) }
}
private func load() {
guard let d = UserDefaults.standard.data(forKey: key),
let v = try? JSONDecoder().decode([LocalGitServer].self, from: d) else { return }
servers = v
}
}
// MARK: - SSH Setup View
struct SSHKeySetupView: View {
@Environment(\.dismiss) private var dismiss
var body: some View {
NavigationStack {
ScrollView {
VStack(alignment: .leading, spacing: 20) {
SetupStep("1", title: "Copy Working Copy SSH key",
body: "Working Copy → Settings → SSH Keys → copy public key.")
SetupStep("2", title: "Enable Remote Login on Mac",
body: "System Settings → General → Sharing → Remote Login → On.")
SetupStep("3", title: "Add key to authorized_keys", body: "Run on your Mac:")
CodeBlock("""
echo "PASTE_YOUR_WC_PUBLIC_KEY" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
""")
SetupStep("4", title: "Create a bare repository", body: "Run on your Mac:")
CodeBlock("""
mkdir -p ~/GitRemotes/NavidromePlayer.git
cd ~/GitRemotes/NavidromePlayer.git && git init --bare
cd ~/Dev/NavidromePlayer
git remote add origin ~/GitRemotes/NavidromePlayer.git
git push -u origin main
""")
SetupStep("5", title: "Clone in Working Copy",
body: "Working Copy → + → Clone → enter your SSH URL:")
CodeBlock("ssh://dallas@100.x.x.x/Users/dallas/GitRemotes/NavidromePlayer.git")
}
.padding(20)
}
.navigationTitle("SSH + Local Git Setup")
.navigationBarTitleDisplayMode(.inline)
.toolbar { ToolbarItem(placement: .confirmationAction) { Button("Done") { dismiss() } } }
}
}
}
private struct SetupStep: View {
let step: String; let title: String; let detail: String
init(_ step: String, title: String, body: String) {
self.step = step; self.title = title; self.detail = body
}
var body: some View {
HStack(alignment: .top, spacing: 12) {
Text(step).font(.system(.title3, design: .rounded).bold())
.foregroundStyle(.white).frame(width: 28, height: 28)
.background(Color.accentColor, in: Circle())
VStack(alignment: .leading, spacing: 4) {
Text(title).font(.headline)
Text(detail).font(.subheadline).foregroundStyle(.secondary)
}
}
}
}
private struct CodeBlock: View {
let code: String
init(_ code: String) { self.code = code }
var body: some View {
HStack(alignment: .top) {
ScrollView(.horizontal, showsIndicators: false) {
Text(code).font(.system(.caption, design: .monospaced))
.textSelection(.enabled).padding(12)
}
Button { UIPasteboard.general.string = code }
label: { Image(systemName: "doc.on.doc").font(.caption).padding(10) }
.buttonStyle(.plain).foregroundStyle(.secondary)
}
.background(Color(.systemBackground), in: RoundedRectangle(cornerRadius: 8))
.overlay(RoundedRectangle(cornerRadius: 8).stroke(Color(.separator), lineWidth: 0.5))
}
}