new release script

This commit is contained in:
Mark Gregory
2026-10-18 15:41:16 +01:00
parent 55c4254b25
commit 73e9a3efe2
4 changed files with 157 additions and 30 deletions
+109 -24
View File
@@ -8,18 +8,10 @@ import UniformTypeIdentifiers
// MARK: - Classic Workbench Colours
private enum WB {
static let toolbar = Color(nsColor: NSColor(
calibratedWhite: 0.88, alpha: 1
))
static let sidebar = Color(nsColor: NSColor(
calibratedWhite: 0.94, alpha: 1
))
static let border = Color(nsColor: NSColor(
calibratedWhite: 0.76, alpha: 1
))
static let header = Color(nsColor: NSColor(
calibratedWhite: 0.91, alpha: 1
))
static let toolbar = Color(nsColor: .controlBackgroundColor)
static let sidebar = Color(nsColor: .underPageBackgroundColor)
static let border = Color(nsColor: .separatorColor)
static let header = Color(nsColor: .controlBackgroundColor)
}
// MARK: - Main Window
@@ -85,11 +77,6 @@ struct MainView: View {
statusBar
}
.frame(minWidth: 800, minHeight: 520)
.preferredColorScheme(
UserDefaults.standard.string(
forKey: "preferences.appearance"
) == "dark" ? .dark : .light
)
.background(Color(nsColor: .windowBackgroundColor))
.onReceive(NotificationCenter.default.publisher(for: .workbenchRevealSQLEditor)) { _ in
administrationPage = nil
@@ -1376,6 +1363,7 @@ struct SQLTextView: NSViewRepresentable {
@Binding var selectedSQL: String
var focusRequest: Int = 0
var completions: [String] = []
@Environment(\.colorScheme) private var colorScheme
func makeCoordinator() -> Coordinator {
Coordinator(self)
@@ -1385,11 +1373,12 @@ struct SQLTextView: NSViewRepresentable {
let scroll = NSTextView.scrollableTextView()
let editor = scroll.documentView as! NSTextView
scroll.appearance = NSAppearance(named: .aqua)
editor.appearance = NSAppearance(named: .aqua)
editor.backgroundColor = .white
editor.textColor = .black
scroll.backgroundColor = .white
let nativeAppearance = NSAppearance(named: colorScheme == .dark ? .darkAqua : .aqua)
scroll.appearance = nativeAppearance
editor.appearance = nativeAppearance
editor.backgroundColor = .textBackgroundColor
editor.textColor = .textColor
scroll.backgroundColor = .textBackgroundColor
editor.isEditable = true
editor.isRichText = false
@@ -1429,6 +1418,13 @@ struct SQLTextView: NSViewRepresentable {
return
}
context.coordinator.parent = self
let nativeAppearance = NSAppearance(named: colorScheme == .dark ? .darkAqua : .aqua)
scroll.appearance = nativeAppearance
editor.appearance = nativeAppearance
editor.backgroundColor = .textBackgroundColor
editor.textColor = .textColor
scroll.backgroundColor = .textBackgroundColor
context.coordinator.highlight(editor)
if context.coordinator.lastFocusRequest != focusRequest {
context.coordinator.lastFocusRequest = focusRequest
DispatchQueue.main.async { [weak editor] in
@@ -2073,6 +2069,7 @@ struct NativeResultGrid: NSViewRepresentable {
let columns: [String]
let rows: [[String]]
var hiddenColumnIndices: Set<Int> = []
@Environment(\.colorScheme) private var colorScheme
func makeCoordinator() -> Coordinator {
Coordinator()
@@ -2084,10 +2081,13 @@ struct NativeResultGrid: NSViewRepresentable {
scroll.hasHorizontalScroller = true
scroll.autohidesScrollers = true
scroll.drawsBackground = true
scroll.backgroundColor = .white
scroll.backgroundColor = .textBackgroundColor
let table = NSTableView()
table.headerView = NSTableHeaderView()
let nativeAppearance = NSAppearance(named: colorScheme == .dark ? .darkAqua : .aqua)
scroll.appearance = nativeAppearance
table.appearance = nativeAppearance
table.usesAlternatingRowBackgroundColors =
UserDefaults.standard.object(
forKey: "preferences.alternatingRows"
@@ -2102,7 +2102,7 @@ struct NativeResultGrid: NSViewRepresentable {
table.allowsColumnResizing = true
table.allowsColumnReordering = true
table.columnAutoresizingStyle = .noColumnAutoresizing
table.backgroundColor = .white
table.backgroundColor = .textBackgroundColor
context.coordinator.columns = columns
context.coordinator.rows = rows
@@ -2126,6 +2126,12 @@ struct NativeResultGrid: NSViewRepresentable {
return
}
let nativeAppearance = NSAppearance(named: colorScheme == .dark ? .darkAqua : .aqua)
scroll.appearance = nativeAppearance
table.appearance = nativeAppearance
scroll.backgroundColor = .textBackgroundColor
table.backgroundColor = .textBackgroundColor
let oldColumns = context.coordinator.columns
context.coordinator.columns = columns
@@ -2963,6 +2969,42 @@ struct WorkbenchDatabaseTransferView: View {
@State private var loadingDatabases = false
@State private var lastBackupURL: URL?
@State private var lastVerificationReport = ""
// Phase 61: Recent backups are kept locally, never uploaded or copied.
@State private var recentBackups: [String] = UserDefaults.standard.stringArray(forKey: "DBSyn.recentBackups.v1") ?? []
@State private var selectedBackupPath = ""
private func rememberBackup(_ url: URL) {
let path = url.standardizedFileURL.path
recentBackups.removeAll { $0 == path }
recentBackups.insert(path, at: 0)
recentBackups = Array(recentBackups.prefix(20))
selectedBackupPath = path
UserDefaults.standard.set(recentBackups, forKey: "DBSyn.recentBackups.v1")
}
private var selectedBackupURL: URL? {
guard !selectedBackupPath.isEmpty,
FileManager.default.fileExists(atPath: selectedBackupPath) else { return nil }
return URL(fileURLWithPath: selectedBackupPath)
}
private func verifyRecentBackup() {
guard let url = selectedBackupURL else { return }
operationTask = Task {
isWorking = true
defer { isWorking = false; operationTask = nil }
do {
try await Task.detached(priority: .utility) {
try WorkbenchBackupIntegrity.verify(url)
}.value
status = "Verified SHA-256: \(url.lastPathComponent)"
progress = 1
} catch {
status = "Verification failed: \(error.localizedDescription)"
}
}
}
private var selectedProfile: ConnectionProfile? {
model.selected
@@ -3075,6 +3117,48 @@ struct WorkbenchDatabaseTransferView: View {
}
}
GroupBox("Recent Backups") {
VStack(alignment: .leading, spacing: 10) {
Text("Previously exported SQL backups. Files stay in their original locations; missing files cannot be verified or restored.")
.font(.caption).foregroundStyle(.secondary)
if recentBackups.isEmpty {
Text("No DBSyn backups recorded yet.").foregroundStyle(.secondary)
} else {
Picker("Backup", selection: $selectedBackupPath) {
Text("Select a backup").tag("")
ForEach(recentBackups, id: \.self) { path in
Text(URL(fileURLWithPath: path).lastPathComponent +
(FileManager.default.fileExists(atPath: path) ? "" : " (missing)"))
.tag(path)
}
}
.labelsHidden()
HStack {
Button("Reveal in Finder") {
if let url = selectedBackupURL {
NSWorkspace.shared.activateFileViewerSelecting([url])
}
}.disabled(selectedBackupURL == nil)
Button("Verify SHA-256") { verifyRecentBackup() }
.disabled(selectedBackupURL == nil || isWorking)
Button("Restore Selected…") {
if let url = selectedBackupURL {
selectedMode = 1
pendingImportURL = url
showingImportConfirmation = true
}
}.disabled(selectedBackupURL == nil || isWorking || database.isEmpty || selectedProfile == nil)
Spacer()
Button("Clear History") {
recentBackups = []
selectedBackupPath = ""
UserDefaults.standard.removeObject(forKey: "DBSyn.recentBackups.v1")
}
}
}
}
}
GroupBox("Progress") {
VStack(alignment: .leading, spacing: 10) {
ProgressView(value: progress)
@@ -3721,6 +3805,7 @@ struct WorkbenchDatabaseTransferView: View {
)
try manifest.write(for: url)
lastBackupURL = url
rememberBackup(url)
progress = 1
status = "Export complete and SHA-256 verified: \(tables.count) tables, \(views.count) views, \(transferredRows.formatted()) rows"
} catch {