diff --git a/WorkbenchReplacement/MainView.swift b/WorkbenchReplacement/MainView.swift index fa54828..70be6d0 100644 --- a/WorkbenchReplacement/MainView.swift +++ b/WorkbenchReplacement/MainView.swift @@ -25,6 +25,8 @@ struct MainView: View { @EnvironmentObject private var model: DatabaseModel @State private var showingEditor = false + @State private var showingDatabaseTransfer = false + @State private var editing = ConnectionProfile() @State private var editingPassword = "" @@ -66,6 +68,9 @@ struct MainView: View { ) == "dark" ? .dark : .light ) .background(Color(nsColor: .windowBackgroundColor)) + .sheet(isPresented: $showingDatabaseTransfer) { + WorkbenchDatabaseTransferView(model: model) + } .sheet(isPresented: $showingEditor) { ConnectionEditor( profile: $editing, @@ -102,6 +107,13 @@ struct MainView: View { showingEditor = true } + toolbarButton( + "Import / Export", + icon: "arrow.up.arrow.down.square" + ) { + showingDatabaseTransfer = true + } + toolbarButton( "Refresh", icon: "arrow.clockwise" @@ -1539,3 +1551,503 @@ struct WorkbenchAdministrationView: View { } } } + + +// MARK: - Built-in Database Import / Export + +struct WorkbenchDatabaseTransferView: View { + @ObservedObject var model: DatabaseModel + @Environment(\.dismiss) private var dismiss + + @State private var selectedMode = 0 + @State private var database = "" + @State private var isWorking = false + @State private var progress = 0.0 + @State private var status = "Ready" + @State private var operationTask: Task? + @State private var showingImportConfirmation = false + @State private var pendingImportURL: URL? + + private var selectedProfile: ConnectionProfile? { + model.selected + } + + var body: some View { + VStack(spacing: 0) { + header + + Divider() + + Picker("Operation", selection: $selectedMode) { + Text("Data Export").tag(0) + Text("Data Import / Restore").tag(1) + } + .pickerStyle(.segmented) + .padding(20) + + Form { + Section("MySQL Connection") { + LabeledContent("Connection") { + Text(selectedProfile?.name ?? "Not selected") + } + + Picker("Database", selection: $database) { + Text("Select Database").tag("") + + ForEach(model.databases, id: \.self) { name in + Text(name).tag(name) + } + } + } + + if selectedMode == 0 { + Section("Export Options") { + Text("Export the SQL definitions of all base tables in the selected database.") + .foregroundStyle(.secondary) + + Text("This version exports table structure. Table data, views, triggers and stored routines will be added in the next stage.") + .font(.caption) + .foregroundStyle(.secondary) + } + } else { + Section("Import Options") { + Text("Import SQL statements into the selected database.") + .foregroundStyle(.secondary) + + Text("Supports ordinary SQL statements. Dumps using custom DELIMITER directives or stored routines are not supported yet.") + .font(.caption) + .foregroundStyle(.secondary) + } + } + + Section("Progress") { + ProgressView(value: progress) + .frame(maxWidth: .infinity) + + Text(status) + .font(.caption) + .foregroundStyle(.secondary) + .textSelection(.enabled) + } + } + .formStyle(.grouped) + + Divider() + + HStack { + Button("Close") { + operationTask?.cancel() + dismiss() + } + + Spacer() + + if isWorking { + Button("Cancel") { + operationTask?.cancel() + status = "Cancelling after current statement…" + } + } else { + Button( + selectedMode == 0 + ? "Export SQL…" + : "Import SQL…" + ) { + if selectedMode == 0 { + chooseExportDestination() + } else { + chooseImportSource() + } + } + .buttonStyle(.borderedProminent) + .disabled( + selectedProfile == nil || + database.isEmpty + ) + } + } + .padding(16) + } + .frame(minWidth: 650, idealWidth: 780, minHeight: 500) + .onAppear { + if database.isEmpty { + database = model.selectedDatabase.isEmpty + ? (model.databases.first ?? "") + : model.selectedDatabase + } + } + .confirmationDialog( + "Import SQL into \(database)?", + isPresented: $showingImportConfirmation + ) { + Button("Import SQL") { + guard let url = pendingImportURL else { return } + + operationTask = Task { + await importSQL(from: url) + } + } + + Button("Cancel", role: .cancel) { + pendingImportURL = nil + } + } message: { + Text( + "SQL files can modify or delete existing database objects and data. Ensure you have a backup before continuing." + ) + } + } + + private var header: some View { + HStack(spacing: 12) { + Image(systemName: "externaldrive.badge.timemachine") + .font(.system(size: 28)) + .foregroundStyle(.blue) + + VStack(alignment: .leading, spacing: 4) { + Text("Database Import / Export") + .font(.system(size: 22, weight: .semibold)) + + Text("Built-in native MySQL database tools") + .font(.caption) + .foregroundStyle(.secondary) + } + + Spacer() + } + .padding(20) + } + + private func chooseExportDestination() { + let panel = NSSavePanel() + panel.title = "Export MySQL Database" + panel.nameFieldStringValue = "\(database).sql" + panel.canCreateDirectories = true + + guard panel.runModal() == .OK, + let url = panel.url + else { return } + + operationTask = Task { + await exportSQL(to: url) + } + } + + private func chooseImportSource() { + let panel = NSOpenPanel() + panel.title = "Import MySQL SQL File" + panel.allowedContentTypes = [.plainText] + panel.allowsOtherFileTypes = true + panel.canChooseDirectories = false + panel.canChooseFiles = true + + guard panel.runModal() == .OK, + let url = panel.url + else { return } + + pendingImportURL = url + showingImportConfirmation = true + } + + @MainActor + private func execute(_ statement: String) async throws -> QueryResult { + guard let profile = selectedProfile else { + throw WorkbenchTransferError.message( + "No MySQL connection selected" + ) + } + + return try await NativeMySQLRunner.run( + profile: profile, + database: database, + password: PasswordStore.read(profile.id), + sql: statement + ) + } + + private func quoteIdentifier(_ identifier: String) -> String { + "`" + identifier.replacingOccurrences( + of: "`", + with: "``" + ) + "`" + } + + @MainActor + private func exportSQL(to url: URL) async { + isWorking = true + progress = 0 + status = "Reading database structure…" + + defer { + isWorking = false + operationTask = nil + } + + do { + let quotedDatabase = quoteIdentifier(database) + + let tableResult = try await execute( + "SHOW FULL TABLES FROM \(quotedDatabase)" + ) + + let tables = tableResult.rows.compactMap { row -> String? in + guard row.count >= 2, + row[1].uppercased() == "BASE TABLE" + else { return nil } + + return row[0] + } + + var output = """ + -- WorkbenchReplacement SQL Export + -- Database: \(database) + -- Generated: \(Date()) + + SET NAMES utf8mb4; + + """ + + for (index, table) in tables.enumerated() { + try Task.checkCancellation() + + status = "Exporting structure: \(table)" + + let result = try await execute( + "SHOW CREATE TABLE \(quotedDatabase).\(quoteIdentifier(table))" + ) + + guard let row = result.rows.first, + row.count >= 2 + else { + throw WorkbenchTransferError.message( + "Cannot retrieve CREATE TABLE for \(table)" + ) + } + + output += "\n-- Table: \(table)\n" + output += row[1] + output += ";\n\n" + + progress = Double(index + 1) / + Double(max(tables.count, 1)) + } + + try Task.checkCancellation() + + try output.write( + to: url, + atomically: true, + encoding: .utf8 + ) + + progress = 1 + status = "Export complete: \(tables.count) table definitions" + + } catch is CancellationError { + status = "Export cancelled" + } catch { + status = "Export failed: \(error.localizedDescription)" + } + } + + @MainActor + private func importSQL(from url: URL) async { + isWorking = true + progress = 0 + status = "Reading SQL file…" + + defer { + isWorking = false + operationTask = nil + } + + do { + let sql = try String( + contentsOf: url, + encoding: .utf8 + ) + + let statements = try WorkbenchSQLImportParser.parse(sql) + + guard !statements.isEmpty else { + status = "No SQL statements found" + return + } + + for (index, statement) in statements.enumerated() { + try Task.checkCancellation() + + status = "Executing statement \(index + 1) of \(statements.count)" + + _ = try await execute(statement) + + progress = Double(index + 1) / + Double(statements.count) + } + + status = "Import complete: \(statements.count) statements" + + model.loadDatabases() + + } catch is CancellationError { + status = "Import cancelled; previously executed statements may remain applied" + } catch { + status = "Import failed: \(error.localizedDescription)" + } + } +} + +enum WorkbenchTransferError: LocalizedError { + case message(String) + + var errorDescription: String? { + switch self { + case .message(let message): + return message + } + } +} + +// MARK: - SQL Import Parser +// +// Handles ordinary SQL statements with quoted strings, +// quoted identifiers, line comments and block comments. +// Stored routines and custom DELIMITER directives are +// deliberately rejected rather than executed incorrectly. + +enum WorkbenchSQLImportParser { + static func parse(_ sql: String) throws -> [String] { + var statements: [String] = [] + var current = "" + var quote: Character? + var escaped = false + var lineComment = false + var blockComment = false + + let characters = Array(sql) + var index = 0 + + while index < characters.count { + let char = characters[index] + let next: Character? = index + 1 < characters.count + ? characters[index + 1] + : nil + + if lineComment { + if char == "\n" { + lineComment = false + current.append("\n") + } + index += 1 + continue + } + + if blockComment { + if char == "*", next == "/" { + blockComment = false + index += 2 + } else { + index += 1 + } + continue + } + + if let activeQuote = quote { + current.append(char) + + if escaped { + escaped = false + } else if char == "\\" && activeQuote != "`" { + escaped = true + } else if char == activeQuote { + if next == activeQuote { + current.append(next!) + index += 1 + } else { + quote = nil + } + } + + index += 1 + continue + } + + if char == "'" || char == "\"" || char == "`" { + quote = char + current.append(char) + index += 1 + continue + } + + if char == "#" + || (char == "-" && next == "-" + && (index + 2 >= characters.count + || characters[index + 2].isWhitespace)) { + lineComment = true + index += char == "#" ? 1 : 2 + continue + } + + if char == "/", next == "*" { + // Executable MySQL comments are not safe to discard. + if index + 2 < characters.count, + characters[index + 2] == "!" { + throw WorkbenchTransferError.message( + "This SQL file contains MySQL executable comments, which are not supported by this importer yet." + ) + } + + blockComment = true + index += 2 + continue + } + + if char == ";" { + let statement = current.trimmingCharacters( + in: .whitespacesAndNewlines + ) + + if !statement.isEmpty { + statements.append(statement) + } + + current = "" + index += 1 + continue + } + + current.append(char) + index += 1 + } + + if quote != nil || blockComment { + throw WorkbenchTransferError.message( + "SQL file contains an unterminated quote or comment." + ) + } + + let remaining = current.trimmingCharacters( + in: .whitespacesAndNewlines + ) + + if !remaining.isEmpty { + statements.append(remaining) + } + + for statement in statements { + let upper = statement.uppercased() + + if upper.hasPrefix("DELIMITER ") + || upper.hasPrefix("CREATE PROCEDURE") + || upper.hasPrefix("CREATE FUNCTION") + || upper.hasPrefix("CREATE TRIGGER") + || upper.hasPrefix("CREATE EVENT") { + throw WorkbenchTransferError.message( + "Stored routines, triggers, events and DELIMITER directives require the advanced SQL importer." + ) + } + } + + return statements + } +} diff --git a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.o b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.o index 8d2c075..ef3e420 100644 Binary files a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.o and b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.o differ diff --git a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.swiftdeps b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.swiftdeps index 6d6cb18..53267af 100644 Binary files a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.swiftdeps and b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.swiftdeps differ diff --git a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.o b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.o index 1072684..98b54f9 100644 Binary files a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.o and b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.o differ diff --git a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.stringsdata b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.stringsdata index 2081851..a56a8ce 100644 --- a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.stringsdata +++ b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.stringsdata @@ -7,7 +7,7 @@ "key": "MySQL Workbench", "location": { "startingColumn": 18, - "startingLine": 88 + "startingLine": 93 } }, { @@ -15,7 +15,7 @@ "key": "Navigator", "location": { "startingColumn": 20, - "startingLine": 170 + "startingLine": 182 } }, { @@ -23,7 +23,7 @@ "key": "Administration", "location": { "startingColumn": 22, - "startingLine": 171 + "startingLine": 183 } }, { @@ -31,7 +31,7 @@ "key": "Schemas", "location": { "startingColumn": 22, - "startingLine": 172 + "startingLine": 184 } }, { @@ -39,7 +39,7 @@ "key": "SCHEMAS", "location": { "startingColumn": 22, - "startingLine": 192 + "startingLine": 204 } }, { @@ -47,7 +47,7 @@ "key": "Edit Connection", "location": { "startingColumn": 36, - "startingLine": 246 + "startingLine": 258 } }, { @@ -55,7 +55,7 @@ "key": "Set as Default Schema", "location": { "startingColumn": 24, - "startingLine": 363 + "startingLine": 375 } }, { @@ -63,7 +63,7 @@ "key": "Refresh Tables", "location": { "startingColumn": 24, - "startingLine": 367 + "startingLine": 379 } }, { @@ -71,7 +71,7 @@ "key": "Load Tables", "location": { "startingColumn": 28, - "startingLine": 437 + "startingLine": 449 } }, { @@ -79,7 +79,7 @@ "key": "Tables", "location": { "startingColumn": 22, - "startingLine": 410 + "startingLine": 422 } }, { @@ -87,7 +87,7 @@ "key": "Select First 100 Rows", "location": { "startingColumn": 20, - "startingLine": 493 + "startingLine": 505 } }, { @@ -95,7 +95,7 @@ "key": "New Query Tab", "location": { "startingColumn": 19, - "startingLine": 614 + "startingLine": 626 } }, { @@ -103,7 +103,7 @@ "key": "Default Schema:", "location": { "startingColumn": 18, - "startingLine": 639 + "startingLine": 651 } }, { @@ -111,7 +111,7 @@ "key": "SQL Editor", "location": { "startingColumn": 18, - "startingLine": 649 + "startingLine": 661 } }, { @@ -119,7 +119,7 @@ "key": "Execute SQL", "location": { "startingColumn": 19, - "startingLine": 627 + "startingLine": 639 } }, { @@ -127,7 +127,7 @@ "key": "Format SQL", "location": { "startingColumn": 19, - "startingLine": 634 + "startingLine": 646 } }, { @@ -135,7 +135,7 @@ "key": "%lld", "location": { "startingColumn": 26, - "startingLine": 689 + "startingLine": 701 } }, { @@ -143,7 +143,7 @@ "key": "Result Grid", "location": { "startingColumn": 22, - "startingLine": 721 + "startingLine": 733 } }, { @@ -151,7 +151,7 @@ "key": "No query results", "location": { "startingColumn": 26, - "startingLine": 739 + "startingLine": 751 } }, { @@ -159,7 +159,7 @@ "key": "Action Output", "location": { "startingColumn": 22, - "startingLine": 761 + "startingLine": 773 } }, { @@ -167,7 +167,7 @@ "key": "UTF-8", "location": { "startingColumn": 18, - "startingLine": 836 + "startingLine": 848 } }, { @@ -175,7 +175,7 @@ "key": "Connection Settings", "location": { "startingColumn": 18, - "startingLine": 1011 + "startingLine": 1023 } }, { @@ -183,7 +183,7 @@ "key": "Connection Name", "location": { "startingColumn": 21, - "startingLine": 1016 + "startingLine": 1028 } }, { @@ -191,7 +191,7 @@ "key": "Hostname", "location": { "startingColumn": 21, - "startingLine": 1021 + "startingLine": 1033 } }, { @@ -199,7 +199,7 @@ "key": "Port", "location": { "startingColumn": 21, - "startingLine": 1026 + "startingLine": 1038 } }, { @@ -207,7 +207,7 @@ "key": "Username", "location": { "startingColumn": 21, - "startingLine": 1032 + "startingLine": 1044 } }, { @@ -215,7 +215,7 @@ "key": "Password (macOS Keychain)", "location": { "startingColumn": 21, - "startingLine": 1037 + "startingLine": 1049 } }, { @@ -223,7 +223,7 @@ "key": "Default Schema", "location": { "startingColumn": 21, - "startingLine": 1042 + "startingLine": 1054 } }, { @@ -231,7 +231,7 @@ "key": "Cancel", "location": { "startingColumn": 24, - "startingLine": 1050 + "startingLine": 1062 } }, { @@ -239,7 +239,7 @@ "key": "Save", "location": { "startingColumn": 24, - "startingLine": 1054 + "startingLine": 1066 } }, { @@ -247,7 +247,7 @@ "key": "The server did not return any columns.", "location": { "startingColumn": 25, - "startingLine": 1376 + "startingLine": 1388 } }, { @@ -255,7 +255,7 @@ "key": "Loading %@…", "location": { "startingColumn": 30, - "startingLine": 1354 + "startingLine": 1366 } }, { @@ -263,7 +263,7 @@ "key": "No Data", "location": { "startingColumn": 21, - "startingLine": 1373 + "startingLine": 1385 } }, { @@ -271,7 +271,7 @@ "key": "Unable to Load %@", "location": { "startingColumn": 25, - "startingLine": 1360 + "startingLine": 1372 } }, { @@ -279,7 +279,7 @@ "key": "Retry", "location": { "startingColumn": 28, - "startingLine": 1366 + "startingLine": 1378 } }, { @@ -287,7 +287,7 @@ "key": "Refresh", "location": { "startingColumn": 23, - "startingLine": 1415 + "startingLine": 1427 } }, { @@ -295,7 +295,7 @@ "key": "SQL Editor", "location": { "startingColumn": 23, - "startingLine": 1422 + "startingLine": 1434 } }, { @@ -303,7 +303,7 @@ "key": "Not loaded", "location": { "startingColumn": 22, - "startingLine": 1500 + "startingLine": 1512 } }, { @@ -311,7 +311,7 @@ "key": "Updated %@", "location": { "startingColumn": 21, - "startingLine": 1497 + "startingLine": 1509 } }, { @@ -319,7 +319,199 @@ "key": "%lld rows", "location": { "startingColumn": 18, - "startingLine": 1505 + "startingLine": 1517 + } + }, + { + "comment": "", + "key": "Import SQL into %@?", + "location": { + "startingColumn": 13, + "startingLine": 1680 + } + }, + { + "comment": "", + "key": "Operation", + "location": { + "startingColumn": 20, + "startingLine": 1581 + } + }, + { + "comment": "", + "key": "Data Export", + "location": { + "startingColumn": 22, + "startingLine": 1582 + } + }, + { + "comment": "", + "key": "Data Import / Restore", + "location": { + "startingColumn": 22, + "startingLine": 1583 + } + }, + { + "comment": "", + "key": "MySQL Connection", + "location": { + "startingColumn": 25, + "startingLine": 1589 + } + }, + { + "comment": "", + "key": "Export Options", + "location": { + "startingColumn": 29, + "startingLine": 1604 + } + }, + { + "comment": "", + "key": "Import Options", + "location": { + "startingColumn": 29, + "startingLine": 1613 + } + }, + { + "comment": "", + "key": "Progress", + "location": { + "startingColumn": 25, + "startingLine": 1623 + } + }, + { + "comment": "", + "key": "Connection", + "location": { + "startingColumn": 36, + "startingLine": 1590 + } + }, + { + "comment": "", + "key": "Database", + "location": { + "startingColumn": 28, + "startingLine": 1594 + } + }, + { + "comment": "", + "key": "Select Database", + "location": { + "startingColumn": 30, + "startingLine": 1595 + } + }, + { + "comment": "", + "key": "Export the SQL definitions of all base tables in the selected database.", + "location": { + "startingColumn": 30, + "startingLine": 1605 + } + }, + { + "comment": "", + "key": "This version exports table structure. Table data, views, triggers and stored routines will be added in the next stage.", + "location": { + "startingColumn": 30, + "startingLine": 1608 + } + }, + { + "comment": "", + "key": "Import SQL statements into the selected database.", + "location": { + "startingColumn": 30, + "startingLine": 1614 + } + }, + { + "comment": "", + "key": "Supports ordinary SQL statements. Dumps using custom DELIMITER directives or stored routines are not supported yet.", + "location": { + "startingColumn": 30, + "startingLine": 1617 + } + }, + { + "comment": "", + "key": "Close", + "location": { + "startingColumn": 24, + "startingLine": 1638 + } + }, + { + "comment": "", + "key": "Cancel", + "location": { + "startingColumn": 28, + "startingLine": 1646 + } + }, + { + "comment": "", + "key": "Export SQL…", + "location": { + "startingColumn": 31, + "startingLine": 1653 + } + }, + { + "comment": "", + "key": "Import SQL…", + "location": { + "startingColumn": 31, + "startingLine": 1654 + } + }, + { + "comment": "", + "key": "Import SQL", + "location": { + "startingColumn": 20, + "startingLine": 1683 + } + }, + { + "comment": "", + "key": "Cancel", + "location": { + "startingColumn": 20, + "startingLine": 1691 + } + }, + { + "comment": "", + "key": "SQL files can modify or delete existing database objects and data. Ensure you have a backup before continuing.", + "location": { + "startingColumn": 17, + "startingLine": 1696 + } + }, + { + "comment": "", + "key": "Database Import / Export", + "location": { + "startingColumn": 22, + "startingLine": 1708 + } + }, + { + "comment": "", + "key": "Built-in native MySQL database tools", + "location": { + "startingColumn": 22, + "startingLine": 1711 } } ] diff --git a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.swiftdeps b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.swiftdeps index 75224dc..9db6421 100644 Binary files a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.swiftdeps and b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.swiftdeps differ diff --git a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement-primary.priors b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement-primary.priors index e0c7568..a0f8f7a 100644 Binary files a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement-primary.priors and b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement-primary.priors differ diff --git a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement.swiftmodule b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement.swiftmodule index b2804d4..dd0cf2c 100644 Binary files a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement.swiftmodule and b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement.swiftmodule differ diff --git a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement.swiftsourceinfo b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement.swiftsourceinfo index 6731660..4d913f3 100644 Binary files a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement.swiftsourceinfo and b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement.swiftsourceinfo differ diff --git a/build/Build/Intermediates.noindex/XCBuildData/build.db b/build/Build/Intermediates.noindex/XCBuildData/build.db index 2195bf4..fc2d94d 100644 Binary files a/build/Build/Intermediates.noindex/XCBuildData/build.db and b/build/Build/Intermediates.noindex/XCBuildData/build.db differ diff --git a/build/Build/Intermediates.noindex/XCBuildData/registered-launchservices.txt b/build/Build/Intermediates.noindex/XCBuildData/registered-launchservices.txt index 4e9b961..240b2b0 100644 --- a/build/Build/Intermediates.noindex/XCBuildData/registered-launchservices.txt +++ b/build/Build/Intermediates.noindex/XCBuildData/registered-launchservices.txt @@ -4,3 +4,4 @@ /Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Products/Debug/WorkbenchReplacement.app /Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Products/Debug/WorkbenchReplacement.app /Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Products/Debug/WorkbenchReplacement.app +/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Products/Debug/WorkbenchReplacement.app diff --git a/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/MacOS/WorkbenchReplacement b/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/MacOS/WorkbenchReplacement index f278b84..75494ed 100755 Binary files a/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/MacOS/WorkbenchReplacement and b/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/MacOS/WorkbenchReplacement differ diff --git a/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/MacOS/WorkbenchReplacement.debug.dylib b/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/MacOS/WorkbenchReplacement.debug.dylib index bd0b336..94aada3 100755 Binary files a/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/MacOS/WorkbenchReplacement.debug.dylib and b/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/MacOS/WorkbenchReplacement.debug.dylib differ diff --git a/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/MacOS/__preview.dylib b/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/MacOS/__preview.dylib index e192b46..25359c9 100755 Binary files a/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/MacOS/__preview.dylib and b/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/MacOS/__preview.dylib differ diff --git a/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/_CodeSignature/CodeResources b/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/_CodeSignature/CodeResources index ce832c6..ee2f58b 100644 --- a/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/_CodeSignature/CodeResources +++ b/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/_CodeSignature/CodeResources @@ -19,7 +19,7 @@ cdhash - 5Wek/oPAL5JjNIHDxN/dkqjd0UI= + aPFwAkm/guZ4EeZintWn1SNqigs= requirement identifier "WorkbenchReplacement.debug" and anchor apple generic and certificate leaf[subject.CN] = "Apple Development: Mark Gregory (FLBRJ3BYDD)" and certificate 1[field.1.2.840.113635.100.6.2.1] /* exists */ diff --git a/build/Build/Products/Debug/WorkbenchReplacement.swiftmodule/Project/arm64-apple-macos.swiftsourceinfo b/build/Build/Products/Debug/WorkbenchReplacement.swiftmodule/Project/arm64-apple-macos.swiftsourceinfo index 6731660..4d913f3 100644 Binary files a/build/Build/Products/Debug/WorkbenchReplacement.swiftmodule/Project/arm64-apple-macos.swiftsourceinfo and b/build/Build/Products/Debug/WorkbenchReplacement.swiftmodule/Project/arm64-apple-macos.swiftsourceinfo differ diff --git a/build/Build/Products/Debug/WorkbenchReplacement.swiftmodule/arm64-apple-macos.swiftmodule b/build/Build/Products/Debug/WorkbenchReplacement.swiftmodule/arm64-apple-macos.swiftmodule index b2804d4..dd0cf2c 100644 Binary files a/build/Build/Products/Debug/WorkbenchReplacement.swiftmodule/arm64-apple-macos.swiftmodule and b/build/Build/Products/Debug/WorkbenchReplacement.swiftmodule/arm64-apple-macos.swiftmodule differ diff --git a/build/Index.noindex/DataStore/v5/units/MainView.o-CNVK7PE7JUX3 b/build/Index.noindex/DataStore/v5/units/MainView.o-CNVK7PE7JUX3 index d3ff8c9..c59df78 100644 Binary files a/build/Index.noindex/DataStore/v5/units/MainView.o-CNVK7PE7JUX3 and b/build/Index.noindex/DataStore/v5/units/MainView.o-CNVK7PE7JUX3 differ diff --git a/build/Logs/Build/LogStoreManifest.plist b/build/Logs/Build/LogStoreManifest.plist index ee1836a..0462630 100644 --- a/build/Logs/Build/LogStoreManifest.plist +++ b/build/Logs/Build/LogStoreManifest.plist @@ -132,6 +132,48 @@ uniqueIdentifier AD44F407-7FE5-46AE-A82F-8FAC1CA2720B + BA4E5983-8DAA-49CA-A677-11E73E67F0B0 + + className + IDECommandLineBuildLog + documentTypeString + <nil> + domainType + Xcode.IDEActivityLogDomainType.BuildLog + fileName + BA4E5983-8DAA-49CA-A677-11E73E67F0B0.xcactivitylog + hasPrimaryLog + + primaryObservable + + highLevelStatus + S + totalNumberOfAnalyzerIssues + 0 + totalNumberOfErrors + 0 + totalNumberOfTestFailures + 0 + totalNumberOfWarnings + 0 + + schemeIdentifier-containerName + WorkbenchReplacement project + schemeIdentifier-schemeName + WorkbenchReplacement + schemeIdentifier-sharedScheme + 1 + signature + Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug + timeStartedRecording + 813875867.553792 + timeStoppedRecording + 813875879.94755697 + title + Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug + uniqueIdentifier + BA4E5983-8DAA-49CA-A677-11E73E67F0B0 + diff --git a/build/ModuleCache.noindex/Session.modulevalidation b/build/ModuleCache.noindex/Session.modulevalidation index 3d7bb35..2a981dd 100644 --- a/build/ModuleCache.noindex/Session.modulevalidation +++ b/build/ModuleCache.noindex/Session.modulevalidation @@ -1 +1 @@ -1792182904.277697: Module build session file for module cache at Path(str: "/Volumes/Developer/Applications/WorkbenchReplacement/build/ModuleCache.noindex") +1792183067.957757: Module build session file for module cache at Path(str: "/Volumes/Developer/Applications/WorkbenchReplacement/build/ModuleCache.noindex") diff --git a/build/SDKStatCaches.noindex/macosx27.0-26A425-e829b21a6f8c9b70876ba0858053c0447231f6df4def33e022ae2eb8f782df1a.sdkstatcache b/build/SDKStatCaches.noindex/macosx27.0-26A425-e829b21a6f8c9b70876ba0858053c0447231f6df4def33e022ae2eb8f782df1a.sdkstatcache index 78fd62c..647692d 100644 Binary files a/build/SDKStatCaches.noindex/macosx27.0-26A425-e829b21a6f8c9b70876ba0858053c0447231f6df4def33e022ae2eb8f782df1a.sdkstatcache and b/build/SDKStatCaches.noindex/macosx27.0-26A425-e829b21a6f8c9b70876ba0858053c0447231f6df4def33e022ae2eb8f782df1a.sdkstatcache differ diff --git a/build/info.plist b/build/info.plist index ce4b1e8..04f8146 100644 --- a/build/info.plist +++ b/build/info.plist @@ -3,7 +3,7 @@ LastAccessedDate - 2026-10-16T20:35:04Z + 2026-10-16T20:37:47Z WorkspacePath /Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement.xcodeproj