diff --git a/PHASE10-NOTES.md b/PHASE10-NOTES.md new file mode 100644 index 0000000..3abbe90 --- /dev/null +++ b/PHASE10-NOTES.md @@ -0,0 +1,8 @@ +# Phase 10 — Live export progress + +- Updates export progress while each streamed table is being written, rather than only after the table completes. +- Displays current-table row count, expected row count, and total exported rows. +- Throttles UI updates to every 500 rows (and at the last row) to reduce overhead. +- Retains Phase 9's single-result streaming, InnoDB snapshot, row count verification and SHA-256 sidecars. + +Limitations: Large individual values remain constrained by MySQL packet sizes. Progress updates are best effort and may be delayed by networking. Export consistency excludes concurrent DDL and non-InnoDB data. This source has not been built with Xcode or tested against a live server. diff --git a/PHASE11-NOTES.md b/PHASE11-NOTES.md new file mode 100644 index 0000000..3b8a1d3 --- /dev/null +++ b/PHASE11-NOTES.md @@ -0,0 +1,13 @@ +# Phase 11 — Streaming SQL import + +- Reads SQL dumps in 64 KiB file chunks, buffering only a line and the current SQL statement. +- Supports DELIMITER directives, SQL quoting, line/block comments and routine bodies. +- Reports statement number and starting line on SQL execution errors. +- Reports approximate byte progress and allows cancellation between statements. +- Verifies SHA-256 sidecar before import if present. + +## Limitations +- A single huge SQL statement or line is still buffered in memory. +- Import is nontransactional and cancellation can leave partial changes. +- Parser assumes UTF-8 input and standard MySQL SQL mode (not NO_BACKSLASH_ESCAPES). +- This has not been built with Xcode or integration-tested against MySQL. diff --git a/WorkbenchReplacement/MainView.swift b/WorkbenchReplacement/MainView.swift index 31d7590..e6e9583 100644 --- a/WorkbenchReplacement/MainView.swift +++ b/WorkbenchReplacement/MainView.swift @@ -1569,6 +1569,9 @@ struct WorkbenchDatabaseTransferView: View { @State private var database = "" @State private var isWorking = false @State private var progress = 0.0 + @State private var transferredRows: Int64 = 0 + @State private var currentTableRows: Int64 = 0 + @State private var currentTableExpected: Int64 = 0 @State private var status = "Ready" @State private var operationTask: Task? @State private var showingImportConfirmation = false @@ -1707,6 +1710,12 @@ struct WorkbenchDatabaseTransferView: View { .font(.caption) .foregroundStyle(.secondary) .textSelection(.enabled) + if isWorking && selectedMode == 0 && currentTableExpected > 0 { + ProgressView(value: Double(currentTableRows), total: Double(currentTableExpected)) + Text("Current table: \(currentTableRows.formatted()) / \(currentTableExpected.formatted()) rows · Total exported: \(transferredRows.formatted())") + .font(.caption.monospacedDigit()) + .foregroundStyle(.secondary) + } } } } @@ -1975,6 +1984,9 @@ struct WorkbenchDatabaseTransferView: View { exportSession = dedicatedProfile isWorking = true progress = 0 + transferredRows = 0 + currentTableRows = 0 + currentTableExpected = 0 let tables = availableTables.filter { selectedTables.contains($0) } let tempURL = url.deletingLastPathComponent().appendingPathComponent(".\(UUID().uuidString).sql.tmp") var handle: FileHandle? @@ -2090,12 +2102,18 @@ struct WorkbenchDatabaseTransferView: View { let expectedCount = Int64(countText) else { throw WorkbenchTransferError.message("Cannot count rows in \(table)") } + currentTableRows = 0 + currentTableExpected = expectedCount try write("-- Streamed snapshot: \(expectedCount) rows; order: \(usableIndex ?? "server scan")\n") let select = "SELECT CONCAT('(', CONCAT_WS(',', \(expressions)), ')') FROM \(qualified)" + (orderBy.map { " ORDER BY " + $0 } ?? "") let outputHandle = handle! let prefix = Data("INSERT INTO \(qualified) (\(names)) VALUES ".utf8) let suffix = Data(";\n".utf8) + let completedBeforeTable = transferredRows + let completedTables = index + let totalUnits = max(tables.count + views.count, 1) + var streamedInTable: Int64 = 0 let written = try await NativeMySQLRunner.stream( profile: dedicatedProfile, database: database, @@ -2105,7 +2123,21 @@ struct WorkbenchDatabaseTransferView: View { try outputHandle.write(contentsOf: prefix) try outputHandle.write(contentsOf: Data(literal.utf8)) try outputHandle.write(contentsOf: suffix) + streamedInTable += 1 + if streamedInTable % 500 == 0 || streamedInTable == expectedCount { + let completed = streamedInTable + DispatchQueue.main.async { + currentTableRows = completed + transferredRows = completedBeforeTable + completed + progress = (Double(completedTables) + + Double(completed) / Double(max(expectedCount, 1))) / + Double(totalUnits) + status = "Exporting \(table): \(completed.formatted()) / \(expectedCount.formatted()) rows" + } + } } + currentTableRows = written + transferredRows = completedBeforeTable + written guard written == expectedCount else { throw WorkbenchTransferError.message( "Table \(table): expected \(expectedCount) rows, streamed \(written). Backup discarded." @@ -2113,6 +2145,8 @@ struct WorkbenchDatabaseTransferView: View { } status = "Exported \(table): \(written) rows" } + currentTableExpected = 0 + currentTableRows = 0 progress = Double(index + 1) / Double(max(tables.count + views.count, 1)) } // Views are exported after base tables so dependencies exist on restore. @@ -2199,7 +2233,7 @@ struct WorkbenchDatabaseTransferView: View { do { try WorkbenchBackupIntegrity.createSidecar(for: url) progress = 1 - status = "Export complete and SHA-256 verified: \(tables.count) tables, \(views.count) views" + status = "Export complete and SHA-256 verified: \(tables.count) tables, \(views.count) views, \(transferredRows.formatted()) rows" } catch { progress = 1 status = "SQL export complete, but checksum creation failed: \(error.localizedDescription)" @@ -2237,30 +2271,30 @@ struct WorkbenchDatabaseTransferView: View { }.value } try Task.checkCancellation() - let sql = try String( - contentsOf: url, - encoding: .utf8 - ) + let fileSize = (try FileManager.default.attributesOfItem(atPath: url.path)[.size] as? NSNumber)?.int64Value ?? 0 + var statementNumber = 0 + var lastLine = 1 + let reader = try WorkbenchStreamingSQLReader(url: url) + defer { reader.close() } - let statements = try WorkbenchSQLImportParser.parse(sql) - - guard !statements.isEmpty else { - status = "No SQL statements found" - return - } - - for (index, statement) in statements.enumerated() { + while let entry = try reader.nextStatement() { try Task.checkCancellation() - - status = "Executing statement \(index + 1) of \(statements.count)" - - _ = try await execute(statement) - - progress = Double(index + 1) / - Double(statements.count) + statementNumber += 1 + lastLine = entry.line + status = "Importing statement \(statementNumber) · line \(lastLine)" + do { + _ = try await execute(entry.sql) + } catch { + throw WorkbenchTransferError.message( + "Statement \(statementNumber), line \(lastLine): \(error.localizedDescription)" + ) + } + if statementNumber % 10 == 0 || reader.bytesRead >= fileSize { + progress = fileSize > 0 ? min(1, Double(reader.bytesRead) / Double(fileSize)) : 1 + } } - - status = "Import complete: \(statements.count) statements" + progress = 1 + status = "Import complete: \(statementNumber) statements" model.loadDatabases() @@ -2467,3 +2501,129 @@ private enum WorkbenchBackupIntegrity { } } } + + +// MARK: - Phase 11: bounded-memory SQL dump reader +// Keeps only the current SQL statement in memory. A single very large +// INSERT still requires enough memory to hold that statement. +final class WorkbenchStreamingSQLReader { + struct Entry { let sql: String; let line: Int } + private let handle: FileHandle + private var bytes = Data() + private var reachedEOF = false + private(set) var bytesRead: Int64 = 0 + private var lineNumber = 0 + private var statementStart = 1 + private var statement = "" + private var delimiter = ";" + private var quote: Character? + private var escaped = false + private var blockComment = false + private var lineComment = false + private var pending: [Entry] = [] + + init(url: URL) throws { handle = try FileHandle(forReadingFrom: url) } + func close() { try? handle.close() } + + func nextStatement() throws -> Entry? { + while pending.isEmpty { + guard let line = try readLine() else { + if quote != nil || blockComment { + throw WorkbenchTransferError.message("Unterminated quote or comment near line \(lineNumber)") + } + let tail = statement.trimmingCharacters(in: .whitespacesAndNewlines) + statement = "" + if !tail.isEmpty { return Entry(sql: tail, line: statementStart) } + return nil + } + lineNumber += 1 + try consume(line) + } + return pending.removeFirst() + } + + private func readLine() throws -> String? { + while true { + if let end = bytes.firstIndex(of: 10) { + let data = bytes.prefix(through: end) + bytes.removeSubrange(...end) + guard let line = String(data: data, encoding: .utf8) else { + throw WorkbenchTransferError.message("Invalid UTF-8 at line \(lineNumber + 1)") + } + return line + } + if reachedEOF { + guard !bytes.isEmpty else { return nil } + let data = bytes + bytes.removeAll(keepingCapacity: true) + guard let line = String(data: data, encoding: .utf8) else { + throw WorkbenchTransferError.message("Invalid UTF-8 at end of SQL file") + } + return line + } + let chunk = try handle.read(upToCount: 64 * 1024) ?? Data() + bytesRead += Int64(chunk.count) + if chunk.isEmpty { reachedEOF = true } + else { bytes.append(chunk) } + } + } + + private func consume(_ line: String) throws { + let trimmed = line.trimmingCharacters(in: .whitespacesAndNewlines) + if quote == nil && !blockComment && statement.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && + trimmed.uppercased().hasPrefix("DELIMITER ") { + let token = String(trimmed.dropFirst(10)).trimmingCharacters(in: .whitespaces) + guard !token.isEmpty && !token.contains(where: { $0.isWhitespace }) else { + throw WorkbenchTransferError.message("Invalid DELIMITER at line \(lineNumber)") + } + delimiter = token + return + } + let chars = Array(line) + let delim = Array(delimiter) + var i = 0 + lineComment = false + while i < chars.count { + let ch = chars[i] + let next: Character? = i + 1 < chars.count ? chars[i + 1] : nil + if lineComment { break } + if blockComment { + statement.append(ch) + if ch == "*" && next == "/" { + statement.append("/"); i += 2; blockComment = false + } else { i += 1 } + continue + } + if let q = quote { + statement.append(ch) + if escaped { escaped = false } + else if ch == "\\" && q != "`" { escaped = true } + else if ch == q { + if next == q { statement.append(ch); i += 1 } + else { quote = nil } + } + i += 1 + continue + } + if ch == "'" || ch == "\"" || ch == "`" { + quote = ch; statement.append(ch); i += 1; continue + } + if ch == "#" || (ch == "-" && next == "-" && + (i + 2 >= chars.count || chars[i + 2].isWhitespace)) { + lineComment = true; break + } + if ch == "/" && next == "*" { + statement.append("/*"); blockComment = true; i += 2; continue + } + if i + delim.count <= chars.count && Array(chars[i..<(i + delim.count)]) == delim { + let sql = statement.trimmingCharacters(in: .whitespacesAndNewlines) + if !sql.isEmpty { pending.append(Entry(sql: sql, line: statementStart)) } + statement = ""; i += delim.count; statementStart = lineNumber; continue + } + if statement.isEmpty && !ch.isWhitespace { statementStart = lineNumber } + statement.append(ch) + i += 1 + } + if !statement.isEmpty { statement.append("\n") } + } +} 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 c02264a..d82069c 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 a8e037c..34f30c6 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 @@ -327,7 +327,7 @@ "key": "Import SQL into %@?", "location": { "startingColumn": 13, - "startingLine": 1780 + "startingLine": 1789 } }, { @@ -335,7 +335,7 @@ "key": "Operation", "location": { "startingColumn": 20, - "startingLine": 1600 + "startingLine": 1603 } }, { @@ -343,7 +343,7 @@ "key": "Data Export", "location": { "startingColumn": 22, - "startingLine": 1601 + "startingLine": 1604 } }, { @@ -351,7 +351,7 @@ "key": "Data Import / Restore", "location": { "startingColumn": 22, - "startingLine": 1602 + "startingLine": 1605 } }, { @@ -359,7 +359,7 @@ "key": "MySQL Connection", "location": { "startingColumn": 26, - "startingLine": 1609 + "startingLine": 1612 } }, { @@ -367,7 +367,7 @@ "key": "Export Options", "location": { "startingColumn": 30, - "startingLine": 1633 + "startingLine": 1636 } }, { @@ -375,7 +375,7 @@ "key": "Import Options", "location": { "startingColumn": 30, - "startingLine": 1689 + "startingLine": 1692 } }, { @@ -383,7 +383,7 @@ "key": "Progress", "location": { "startingColumn": 26, - "startingLine": 1701 + "startingLine": 1704 } }, { @@ -391,7 +391,7 @@ "key": "Connection", "location": { "startingColumn": 36, - "startingLine": 1611 + "startingLine": 1614 } }, { @@ -399,7 +399,7 @@ "key": "Database", "location": { "startingColumn": 28, - "startingLine": 1615 + "startingLine": 1618 } }, { @@ -407,7 +407,7 @@ "key": "Select Database", "location": { "startingColumn": 30, - "startingLine": 1616 + "startingLine": 1619 } }, { @@ -415,7 +415,7 @@ "key": "Refresh Databases", "location": { "startingColumn": 32, - "startingLine": 1624 + "startingLine": 1627 } }, { @@ -423,7 +423,7 @@ "key": "No tables found. Select a database or refresh the list.", "location": { "startingColumn": 34, - "startingLine": 1655 + "startingLine": 1658 } }, { @@ -431,7 +431,7 @@ "key": "InnoDB data is exported using a consistent snapshot and a single streamed query per table. Tables without unique indexes are supported.", "location": { "startingColumn": 30, - "startingLine": 1684 + "startingLine": 1687 } }, { @@ -439,7 +439,7 @@ "key": "Table structure", "location": { "startingColumn": 32, - "startingLine": 1635 + "startingLine": 1638 } }, { @@ -447,7 +447,7 @@ "key": "Table data", "location": { "startingColumn": 32, - "startingLine": 1636 + "startingLine": 1639 } }, { @@ -455,7 +455,7 @@ "key": "Add DROP TABLE IF EXISTS", "location": { "startingColumn": 32, - "startingLine": 1637 + "startingLine": 1640 } }, { @@ -463,7 +463,7 @@ "key": "Include views (definitions)", "location": { "startingColumn": 32, - "startingLine": 1639 + "startingLine": 1642 } }, { @@ -471,7 +471,7 @@ "key": "Include triggers", "location": { "startingColumn": 32, - "startingLine": 1641 + "startingLine": 1644 } }, { @@ -479,7 +479,7 @@ "key": "Include stored procedures and functions", "location": { "startingColumn": 32, - "startingLine": 1643 + "startingLine": 1646 } }, { @@ -487,7 +487,7 @@ "key": "Loading tables…", "location": { "startingColumn": 57, - "startingLine": 1653 + "startingLine": 1656 } }, { @@ -495,7 +495,7 @@ "key": "Tables (%lld selected)", "location": { "startingColumn": 34, - "startingLine": 1646 + "startingLine": 1649 } }, { @@ -503,7 +503,7 @@ "key": "All", "location": { "startingColumn": 36, - "startingLine": 1648 + "startingLine": 1651 } }, { @@ -511,7 +511,7 @@ "key": "None", "location": { "startingColumn": 36, - "startingLine": 1649 + "startingLine": 1652 } }, { @@ -519,7 +519,7 @@ "key": "Refresh", "location": { "startingColumn": 36, - "startingLine": 1650 + "startingLine": 1653 } }, { @@ -527,7 +527,7 @@ "key": "Views (%lld selected)", "location": { "startingColumn": 38, - "startingLine": 1669 + "startingLine": 1672 } }, { @@ -535,7 +535,7 @@ "key": "All", "location": { "startingColumn": 40, - "startingLine": 1671 + "startingLine": 1674 } }, { @@ -543,7 +543,7 @@ "key": "None", "location": { "startingColumn": 40, - "startingLine": 1672 + "startingLine": 1675 } }, { @@ -551,7 +551,7 @@ "key": "Import SQL statements into the selected database.", "location": { "startingColumn": 30, - "startingLine": 1691 + "startingLine": 1694 } }, { @@ -559,7 +559,15 @@ "key": "Supports MySQL DELIMITER directives, triggers and stored routines. Import is not transactional; use a verified backup before restoring.", "location": { "startingColumn": 30, - "startingLine": 1694 + "startingLine": 1697 + } + }, + { + "comment": "", + "key": "Current table: %@ / %@ rows · Total exported: %@", + "location": { + "startingColumn": 30, + "startingLine": 1715 } }, { @@ -567,7 +575,7 @@ "key": "Close", "location": { "startingColumn": 24, - "startingLine": 1720 + "startingLine": 1729 } }, { @@ -575,7 +583,7 @@ "key": "Verify Backup…", "location": { "startingColumn": 28, - "startingLine": 1728 + "startingLine": 1737 } }, { @@ -583,7 +591,7 @@ "key": "Verify an SQL file against its SHA-256 checksum sidecar", "location": { "startingColumn": 27, - "startingLine": 1731 + "startingLine": 1740 } }, { @@ -591,7 +599,7 @@ "key": "Cancel", "location": { "startingColumn": 28, - "startingLine": 1735 + "startingLine": 1744 } }, { @@ -599,7 +607,7 @@ "key": "Export SQL…", "location": { "startingColumn": 31, - "startingLine": 1742 + "startingLine": 1751 } }, { @@ -607,7 +615,7 @@ "key": "Import SQL…", "location": { "startingColumn": 31, - "startingLine": 1743 + "startingLine": 1752 } }, { @@ -615,7 +623,7 @@ "key": "Import SQL", "location": { "startingColumn": 20, - "startingLine": 1783 + "startingLine": 1792 } }, { @@ -623,7 +631,7 @@ "key": "Cancel", "location": { "startingColumn": 20, - "startingLine": 1791 + "startingLine": 1800 } }, { @@ -631,7 +639,7 @@ "key": "SQL files can modify or delete existing database objects and data. Ensure you have a backup before continuing.", "location": { "startingColumn": 17, - "startingLine": 1796 + "startingLine": 1805 } }, { @@ -639,7 +647,7 @@ "key": "Database Import / Export", "location": { "startingColumn": 22, - "startingLine": 1808 + "startingLine": 1817 } }, { @@ -647,7 +655,7 @@ "key": "Built-in native MySQL database tools", "location": { "startingColumn": 22, - "startingLine": 1811 + "startingLine": 1820 } } ] 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 3c50672..a54cf53 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 55aebe1..3efe3db 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 1941200..2b13412 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 1b9e01d..a81442a 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 0d43025..058ea56 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 fb687d1..135d826 100644 --- a/build/Build/Intermediates.noindex/XCBuildData/registered-launchservices.txt +++ b/build/Build/Intermediates.noindex/XCBuildData/registered-launchservices.txt @@ -15,3 +15,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 be546df..9a1d17d 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 d3f6007..5c65c1d 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 fd8c357..05f58e5 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 3cb8636..4d6f8f7 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 - lD7vQzFdLMorMrk09F1ArmxtEu4= + 6S/w6SxP3rEIpQZE9rYcyy4Jl84= 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 1b9e01d..a81442a 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 1941200..2b13412 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 556fb80..e0b1866 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 9844b94..05d847f 100644 --- a/build/Logs/Build/LogStoreManifest.plist +++ b/build/Logs/Build/LogStoreManifest.plist @@ -6,7 +6,7 @@ 13 logs - 32E9EC2D-B9B8-4CD2-8B3D-CD693A4CBD97 + 2FC9B29D-EE21-4701-8634-B8C65E3A79A5 className IDECommandLineBuildLog @@ -15,7 +15,7 @@ domainType Xcode.IDEActivityLogDomainType.BuildLog fileName - 32E9EC2D-B9B8-4CD2-8B3D-CD693A4CBD97.xcactivitylog + 2FC9B29D-EE21-4701-8634-B8C65E3A79A5.xcactivitylog hasPrimaryLog primaryObservable @@ -40,13 +40,13 @@ signature Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug timeStartedRecording - 813876968.59599304 + 813877859.46306098 timeStoppedRecording - 813876981.606776 + 813877874.00724101 title Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug uniqueIdentifier - 32E9EC2D-B9B8-4CD2-8B3D-CD693A4CBD97 + 2FC9B29D-EE21-4701-8634-B8C65E3A79A5 68955E3A-0B2E-4614-AC89-290DCD28B6D0 @@ -90,7 +90,7 @@ uniqueIdentifier 68955E3A-0B2E-4614-AC89-290DCD28B6D0 - 921D995A-6441-4831-9E4E-3283B2F38B89 + B2C949DC-0503-4157-BC15-F74641625B50 className IDECommandLineBuildLog @@ -99,7 +99,7 @@ domainType Xcode.IDEActivityLogDomainType.BuildLog fileName - 921D995A-6441-4831-9E4E-3283B2F38B89.xcactivitylog + B2C949DC-0503-4157-BC15-F74641625B50.xcactivitylog hasPrimaryLog primaryObservable @@ -124,13 +124,13 @@ signature Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug timeStartedRecording - 813877067.82482302 + 813877672.07052696 timeStoppedRecording - 813877081.68267095 + 813877685.02004802 title Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug uniqueIdentifier - 921D995A-6441-4831-9E4E-3283B2F38B89 + B2C949DC-0503-4157-BC15-F74641625B50 EB994874-BDA8-4BAC-BAF2-6EB6F20E2468 @@ -174,48 +174,6 @@ uniqueIdentifier EB994874-BDA8-4BAC-BAF2-6EB6F20E2468 - EDDF2132-184C-41A2-A915-0EC31843BAD5 - - className - IDECommandLineBuildLog - documentTypeString - <nil> - domainType - Xcode.IDEActivityLogDomainType.BuildLog - fileName - EDDF2132-184C-41A2-A915-0EC31843BAD5.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 - 813877252.80588198 - timeStoppedRecording - 813877266.32472801 - title - Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug - uniqueIdentifier - EDDF2132-184C-41A2-A915-0EC31843BAD5 - diff --git a/build/ModuleCache.noindex/Session.modulevalidation b/build/ModuleCache.noindex/Session.modulevalidation index e40ea50..a8e1ffc 100644 --- a/build/ModuleCache.noindex/Session.modulevalidation +++ b/build/ModuleCache.noindex/Session.modulevalidation @@ -1 +1 @@ -1792184713.510244: Module build session file for module cache at Path(str: "/Volumes/Developer/Applications/WorkbenchReplacement/build/ModuleCache.noindex") +1792185059.890316: 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 ff6bb57..d1d401a 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 750c42e..ba19501 100644 --- a/build/info.plist +++ b/build/info.plist @@ -3,7 +3,7 @@ LastAccessedDate - 2026-10-16T21:05:13Z + 2026-10-16T21:10:59Z WorkspacePath /Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement.xcodeproj