diff --git a/PHASE25-26-NOTES.md b/PHASE25-26-NOTES.md new file mode 100644 index 0000000..aa54490 --- /dev/null +++ b/PHASE25-26-NOTES.md @@ -0,0 +1,14 @@ +# Phase 25–26 — Performance sampling and diagnostics + +- Samples the Slow Query Analysis, Connection Statistics, InnoDB Metrics, Dashboard, and Server Status screens on successful refresh. +- `View Trends…` compares current and previous counter values, reporting changes per second when values are monotonic. +- Tracks up to 60 snapshots per open administration screen in memory; `Clear Samples` removes them. +- Slow-query snapshots compare execution counts by Performance Schema SQL digest text. +- No new server privileges, schema modifications, third-party dependencies or external MySQL binaries. + +## Limitations +- Samples are not persisted, and are discarded when the screen is closed. +- Slow query analysis is limited to the top 100 digests returned by the existing SQL; digests dropping out of the top 100 will not be comparable. +- For cumulative counters, server restarts or Performance Schema resets can invalidate deltas; negative deltas are suppressed. +- A counter rate is not the same as query latency, and the SQL digest data may be truncated. +- Not compiled with Xcode or tested on a live MySQL server in this environment. diff --git a/WorkbenchReplacement/MainView.swift b/WorkbenchReplacement/MainView.swift index 82bd9e2..fb92b0d 100644 --- a/WorkbenchReplacement/MainView.swift +++ b/WorkbenchReplacement/MainView.swift @@ -2211,6 +2211,14 @@ struct WorkbenchAdministrationView: View { @State private var processActionError: String? = nil @State private var processActionStatus: String? = nil @State private var terminatingProcess = false + // Phase 25/26: bounded, in-session samples. No credentials or SQL persisted. + @State private var diagnosticSamples: [DiagnosticSample] = [] + @State private var showingTrends = false + + private var supportsTrends: Bool { + page == .slowQueries || page == .connectionStatistics || + page == .innodbMetrics || page == .dashboard || page == .serverStatus + } private var diagnosticGuidance: String? { switch page { @@ -2284,6 +2292,20 @@ struct WorkbenchAdministrationView: View { .padding(.vertical, 8) } + if supportsTrends && !diagnosticSamples.isEmpty { + HStack(spacing: 12) { + Label("\(diagnosticSamples.count) samples", systemImage: "chart.xyaxis.line") + .foregroundStyle(.secondary) + Button("View Trends…") { showingTrends = true } + Button("Clear Samples") { diagnosticSamples.removeAll() } + Spacer() + Text("Samples are kept in memory for this screen only") + .font(.caption).foregroundStyle(.secondary) + } + .padding(.horizontal, 16) + .padding(.vertical, 6) + } + if page == .clientConnections { processControls } @@ -2361,6 +2383,9 @@ struct WorkbenchAdministrationView: View { ) } } + .sheet(isPresented: $showingTrends) { + WorkbenchDiagnosticTrendsView(page: page, samples: diagnosticSamples) + } .sheet(isPresented: $showingGrants) { VStack(alignment: .leading, spacing: 12) { Text("Grants: \(selectedAccount ?? "")") @@ -2626,6 +2651,12 @@ struct WorkbenchAdministrationView: View { ) result = response + if supportsTrends { + diagnosticSamples.append(DiagnosticSample(at: Date(), columns: response.columns, rows: response.rows)) + if diagnosticSamples.count > 60 { + diagnosticSamples.removeFirst(diagnosticSamples.count - 60) + } + } if page == .clientConnections, let selectedProcessID, !processIDs.contains(selectedProcessID) { @@ -4055,3 +4086,93 @@ private struct WorkbenchAccountManagerView: View { } } } + + +// MARK: - Phase 25/26: Live performance samples and counter rates + +private struct DiagnosticSample: Identifiable { + let id = UUID() + let at: Date + let columns: [String] + let rows: [[String]] +} + +private struct WorkbenchDiagnosticTrendsView: View { + let page: WorkbenchAdminPage + let samples: [DiagnosticSample] + @Environment(\.dismiss) private var dismiss + + private func metricMap(_ sample: DiagnosticSample) -> [String: Double] { + var values: [String: Double] = [:] + if page == .slowQueries { + guard let name = sample.columns.firstIndex(of: "DIGEST_TEXT"), + let count = sample.columns.firstIndex(of: "COUNT_STAR") else { return values } + for row in sample.rows where row.indices.contains(name) && row.indices.contains(count) { + if let value = Double(row[count]) { values[row[name]] = value } + } + } else { + for row in sample.rows where row.count >= 2 { + if let value = Double(row[1]) { values[row[0]] = value } + } + } + return values + } + + private var comparisons: [(String, Double, Double, Double?)] { + guard let latest = samples.last else { return [] } + let current = metricMap(latest) + let prior = samples.count > 1 ? metricMap(samples[samples.count - 2]) : [:] + let seconds = samples.count > 1 + ? latest.at.timeIntervalSince(samples[samples.count - 2].at) : 0 + return current.keys.sorted().map { key in + let value = current[key] ?? 0 + let before = prior[key] ?? value + let rate: Double? = seconds > 0 && prior[key] != nil && value >= before + ? (value - before) / seconds : nil + return (key, value, before, rate) + } + } + + var body: some View { + VStack(alignment: .leading, spacing: 12) { + HStack { + Text("\(page.rawValue) — Trends").font(.title2.bold()) + Spacer() + Button("Close") { dismiss() } + } + Text("\(samples.count) samples. Rates compare the two latest successful refreshes; cumulative counters can reset when MySQL restarts or statistics are cleared.") + .font(.caption).foregroundStyle(.secondary) + if samples.count < 2 { + ContentUnavailableView("More samples needed", systemImage: "chart.xyaxis.line", + description: Text("Refresh this screen twice, or enable auto-refresh.")) + } else { + ScrollView { + LazyVStack(alignment: .leading, spacing: 8) { + HStack { + Text("Metric").frame(maxWidth: .infinity, alignment: .leading) + Text("Current").frame(width: 110, alignment: .trailing) + Text("Previous").frame(width: 110, alignment: .trailing) + Text("Change/sec").frame(width: 110, alignment: .trailing) + }.font(.headline) + Divider() + ForEach(comparisons.indices, id: \.self) { index in + let item = comparisons[index] + HStack { + Text(item.0).lineLimit(2).frame(maxWidth: .infinity, alignment: .leading) + Text(item.1.formatted()).frame(width: 110, alignment: .trailing) + Text(item.2.formatted()).frame(width: 110, alignment: .trailing) + Text(item.3.map { $0.formatted(.number.precision(.fractionLength(2))) } ?? "—") + .frame(width: 110, alignment: .trailing) + }.font(.system(.caption, design: .monospaced)) + Divider() + } + } + } + } + Text("Query digest counts are grouped by SQL fingerprint. This is a sampled comparison, not a durable monitoring database.") + .font(.caption).foregroundStyle(.secondary) + } + .padding(20) + .frame(minWidth: 820, minHeight: 480) + } +} 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 730c3b2..3ad2e63 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 fbc7baa..babd39b 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 @@ -719,7 +719,7 @@ "key": "Terminate MySQL session?", "location": { "startingColumn": 13, - "startingLine": 2337 + "startingLine": 2359 } }, { @@ -727,7 +727,7 @@ "key": "The server did not return any columns.", "location": { "startingColumn": 25, - "startingLine": 2270 + "startingLine": 2278 } }, { @@ -735,7 +735,7 @@ "key": "Loading %@…", "location": { "startingColumn": 30, - "startingLine": 2248 + "startingLine": 2256 } }, { @@ -743,7 +743,7 @@ "key": "No Data", "location": { "startingColumn": 21, - "startingLine": 2267 + "startingLine": 2275 } }, { @@ -751,7 +751,7 @@ "key": "Unable to Load %@", "location": { "startingColumn": 25, - "startingLine": 2254 + "startingLine": 2262 } }, { @@ -759,7 +759,39 @@ "key": "Retry", "location": { "startingColumn": 28, - "startingLine": 2260 + "startingLine": 2268 + } + }, + { + "comment": "", + "key": "Samples are kept in memory for this screen only", + "location": { + "startingColumn": 26, + "startingLine": 2302 + } + }, + { + "comment": "", + "key": "%lld samples", + "location": { + "startingColumn": 27, + "startingLine": 2297 + } + }, + { + "comment": "", + "key": "View Trends…", + "location": { + "startingColumn": 28, + "startingLine": 2299 + } + }, + { + "comment": "", + "key": "Clear Samples", + "location": { + "startingColumn": 28, + "startingLine": 2300 } }, { @@ -767,7 +799,7 @@ "key": "Changes require confirmation", "location": { "startingColumn": 26, - "startingLine": 2314 + "startingLine": 2336 } }, { @@ -775,7 +807,7 @@ "key": "Account", "location": { "startingColumn": 28, - "startingLine": 2293 + "startingLine": 2315 } }, { @@ -783,7 +815,7 @@ "key": "Show Grants", "location": { "startingColumn": 28, - "startingLine": 2304 + "startingLine": 2326 } }, { @@ -791,7 +823,7 @@ "key": "Manage Accounts…", "location": { "startingColumn": 28, - "startingLine": 2308 + "startingLine": 2330 } }, { @@ -799,7 +831,7 @@ "key": "Select account", "location": { "startingColumn": 30, - "startingLine": 2294 + "startingLine": 2316 } }, { @@ -807,7 +839,7 @@ "key": "%@@%@", "location": { "startingColumn": 38, - "startingLine": 2298 + "startingLine": 2320 } }, { @@ -815,7 +847,7 @@ "key": "%@ session %@", "location": { "startingColumn": 24, - "startingLine": 2345 + "startingLine": 2367 } }, { @@ -823,7 +855,7 @@ "key": "Cancel", "location": { "startingColumn": 20, - "startingLine": 2350 + "startingLine": 2372 } }, { @@ -831,7 +863,7 @@ "key": "This operation affects a live MySQL session. Terminating a connection can interrupt transactions and roll back uncommitted work. Verify the session ID before proceeding.", "location": { "startingColumn": 18, - "startingLine": 2352 + "startingLine": 2374 } }, { @@ -839,7 +871,7 @@ "key": "Grants: %@", "location": { "startingColumn": 22, - "startingLine": 2366 + "startingLine": 2391 } }, { @@ -847,7 +879,7 @@ "key": "Close", "location": { "startingColumn": 24, - "startingLine": 2378 + "startingLine": 2403 } }, { @@ -855,7 +887,7 @@ "key": "Requires PROCESS privilege to see other users' sessions and appropriate privileges to terminate them.", "location": { "startingColumn": 18, - "startingLine": 2408 + "startingLine": 2433 } }, { @@ -863,7 +895,7 @@ "key": "Session", "location": { "startingColumn": 24, - "startingLine": 2388 + "startingLine": 2413 } }, { @@ -871,7 +903,7 @@ "key": "Stop Query", "location": { "startingColumn": 24, - "startingLine": 2395 + "startingLine": 2420 } }, { @@ -879,7 +911,7 @@ "key": "Disconnect Session", "location": { "startingColumn": 24, - "startingLine": 2397 + "startingLine": 2422 } }, { @@ -887,7 +919,7 @@ "key": "Select a session", "location": { "startingColumn": 26, - "startingLine": 2389 + "startingLine": 2414 } }, { @@ -895,7 +927,7 @@ "key": "ID %@", "location": { "startingColumn": 30, - "startingLine": 2391 + "startingLine": 2416 } }, { @@ -903,7 +935,7 @@ "key": "Filter results", "location": { "startingColumn": 27, - "startingLine": 2473 + "startingLine": 2498 } }, { @@ -911,7 +943,7 @@ "key": "Auto-refresh", "location": { "startingColumn": 20, - "startingLine": 2478 + "startingLine": 2503 } }, { @@ -919,7 +951,7 @@ "key": "Refresh every five seconds", "location": { "startingColumn": 23, - "startingLine": 2480 + "startingLine": 2505 } }, { @@ -927,7 +959,7 @@ "key": "Refresh", "location": { "startingColumn": 23, - "startingLine": 2485 + "startingLine": 2510 } }, { @@ -935,7 +967,7 @@ "key": "SQL Editor", "location": { "startingColumn": 23, - "startingLine": 2492 + "startingLine": 2517 } }, { @@ -943,7 +975,7 @@ "key": "Not loaded", "location": { "startingColumn": 22, - "startingLine": 2570 + "startingLine": 2595 } }, { @@ -951,7 +983,7 @@ "key": "Updated %@", "location": { "startingColumn": 21, - "startingLine": 2567 + "startingLine": 2592 } }, { @@ -959,7 +991,7 @@ "key": "%lld rows", "location": { "startingColumn": 18, - "startingLine": 2575 + "startingLine": 2600 } }, { @@ -967,7 +999,7 @@ "key": "Import SQL into %@?", "location": { "startingColumn": 13, - "startingLine": 2882 + "startingLine": 2913 } }, { @@ -975,7 +1007,7 @@ "key": "Operation", "location": { "startingColumn": 20, - "startingLine": 2685 + "startingLine": 2716 } }, { @@ -983,7 +1015,7 @@ "key": "Data Export", "location": { "startingColumn": 22, - "startingLine": 2686 + "startingLine": 2717 } }, { @@ -991,7 +1023,7 @@ "key": "Data Import / Restore", "location": { "startingColumn": 22, - "startingLine": 2687 + "startingLine": 2718 } }, { @@ -999,7 +1031,7 @@ "key": "MySQL Connection", "location": { "startingColumn": 26, - "startingLine": 2694 + "startingLine": 2725 } }, { @@ -1007,7 +1039,7 @@ "key": "Export Options", "location": { "startingColumn": 30, - "startingLine": 2718 + "startingLine": 2749 } }, { @@ -1015,7 +1047,7 @@ "key": "Import Options", "location": { "startingColumn": 30, - "startingLine": 2774 + "startingLine": 2805 } }, { @@ -1023,7 +1055,7 @@ "key": "Progress", "location": { "startingColumn": 26, - "startingLine": 2786 + "startingLine": 2817 } }, { @@ -1031,7 +1063,7 @@ "key": "Connection", "location": { "startingColumn": 36, - "startingLine": 2696 + "startingLine": 2727 } }, { @@ -1039,7 +1071,7 @@ "key": "Database", "location": { "startingColumn": 28, - "startingLine": 2700 + "startingLine": 2731 } }, { @@ -1047,7 +1079,7 @@ "key": "Select Database", "location": { "startingColumn": 30, - "startingLine": 2701 + "startingLine": 2732 } }, { @@ -1055,7 +1087,7 @@ "key": "Refresh Databases", "location": { "startingColumn": 32, - "startingLine": 2709 + "startingLine": 2740 } }, { @@ -1063,7 +1095,7 @@ "key": "No tables found. Select a database or refresh the list.", "location": { "startingColumn": 34, - "startingLine": 2740 + "startingLine": 2771 } }, { @@ -1071,7 +1103,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": 2769 + "startingLine": 2800 } }, { @@ -1079,7 +1111,7 @@ "key": "Table structure", "location": { "startingColumn": 32, - "startingLine": 2720 + "startingLine": 2751 } }, { @@ -1087,7 +1119,7 @@ "key": "Table data", "location": { "startingColumn": 32, - "startingLine": 2721 + "startingLine": 2752 } }, { @@ -1095,7 +1127,7 @@ "key": "Add DROP TABLE IF EXISTS", "location": { "startingColumn": 32, - "startingLine": 2722 + "startingLine": 2753 } }, { @@ -1103,7 +1135,7 @@ "key": "Include views (definitions)", "location": { "startingColumn": 32, - "startingLine": 2724 + "startingLine": 2755 } }, { @@ -1111,7 +1143,7 @@ "key": "Include triggers", "location": { "startingColumn": 32, - "startingLine": 2726 + "startingLine": 2757 } }, { @@ -1119,7 +1151,7 @@ "key": "Include stored procedures and functions", "location": { "startingColumn": 32, - "startingLine": 2728 + "startingLine": 2759 } }, { @@ -1127,7 +1159,7 @@ "key": "Loading tables…", "location": { "startingColumn": 57, - "startingLine": 2738 + "startingLine": 2769 } }, { @@ -1135,7 +1167,7 @@ "key": "Tables (%lld selected)", "location": { "startingColumn": 34, - "startingLine": 2731 + "startingLine": 2762 } }, { @@ -1143,7 +1175,7 @@ "key": "All", "location": { "startingColumn": 36, - "startingLine": 2733 + "startingLine": 2764 } }, { @@ -1151,7 +1183,7 @@ "key": "None", "location": { "startingColumn": 36, - "startingLine": 2734 + "startingLine": 2765 } }, { @@ -1159,7 +1191,7 @@ "key": "Refresh", "location": { "startingColumn": 36, - "startingLine": 2735 + "startingLine": 2766 } }, { @@ -1167,7 +1199,7 @@ "key": "Views (%lld selected)", "location": { "startingColumn": 38, - "startingLine": 2754 + "startingLine": 2785 } }, { @@ -1175,7 +1207,7 @@ "key": "All", "location": { "startingColumn": 40, - "startingLine": 2756 + "startingLine": 2787 } }, { @@ -1183,7 +1215,7 @@ "key": "None", "location": { "startingColumn": 40, - "startingLine": 2757 + "startingLine": 2788 } }, { @@ -1191,7 +1223,7 @@ "key": "Import SQL statements into the selected database.", "location": { "startingColumn": 30, - "startingLine": 2776 + "startingLine": 2807 } }, { @@ -1199,7 +1231,7 @@ "key": "Supports MySQL DELIMITER directives, triggers and stored routines. Import is not transactional; use a verified backup before restoring.", "location": { "startingColumn": 30, - "startingLine": 2779 + "startingLine": 2810 } }, { @@ -1207,7 +1239,7 @@ "key": "Current table: %@ / %@ rows · Total exported: %@", "location": { "startingColumn": 30, - "startingLine": 2803 + "startingLine": 2834 } }, { @@ -1215,7 +1247,7 @@ "key": "Close", "location": { "startingColumn": 24, - "startingLine": 2817 + "startingLine": 2848 } }, { @@ -1223,7 +1255,7 @@ "key": "Verify Backup…", "location": { "startingColumn": 28, - "startingLine": 2825 + "startingLine": 2856 } }, { @@ -1231,7 +1263,7 @@ "key": "Verify an SQL file against its SHA-256 checksum sidecar", "location": { "startingColumn": 27, - "startingLine": 2828 + "startingLine": 2859 } }, { @@ -1239,7 +1271,7 @@ "key": "Verify Restored Database…", "location": { "startingColumn": 28, - "startingLine": 2829 + "startingLine": 2860 } }, { @@ -1247,7 +1279,7 @@ "key": "Compare restored table row counts and object names with the export manifest", "location": { "startingColumn": 27, - "startingLine": 2833 + "startingLine": 2864 } }, { @@ -1255,7 +1287,7 @@ "key": "Cancel", "location": { "startingColumn": 28, - "startingLine": 2837 + "startingLine": 2868 } }, { @@ -1263,7 +1295,7 @@ "key": "Export SQL…", "location": { "startingColumn": 31, - "startingLine": 2844 + "startingLine": 2875 } }, { @@ -1271,7 +1303,7 @@ "key": "Import SQL…", "location": { "startingColumn": 31, - "startingLine": 2845 + "startingLine": 2876 } }, { @@ -1279,7 +1311,7 @@ "key": "Import SQL", "location": { "startingColumn": 20, - "startingLine": 2885 + "startingLine": 2916 } }, { @@ -1287,7 +1319,7 @@ "key": "Cancel", "location": { "startingColumn": 20, - "startingLine": 2893 + "startingLine": 2924 } }, { @@ -1295,7 +1327,7 @@ "key": "SQL files can modify or delete existing database objects and data. Ensure you have a backup before continuing.", "location": { "startingColumn": 17, - "startingLine": 2898 + "startingLine": 2929 } }, { @@ -1303,7 +1335,7 @@ "key": "Database Import / Export", "location": { "startingColumn": 22, - "startingLine": 2910 + "startingLine": 2941 } }, { @@ -1311,7 +1343,7 @@ "key": "Built-in native MySQL database tools", "location": { "startingColumn": 22, - "startingLine": 2913 + "startingLine": 2944 } }, { @@ -1319,7 +1351,7 @@ "key": "Confirm %@", "location": { "startingColumn": 16, - "startingLine": 3964 + "startingLine": 3995 } }, { @@ -1327,7 +1359,7 @@ "key": "Manage MySQL Accounts", "location": { "startingColumn": 18, - "startingLine": 3914 + "startingLine": 3945 } }, { @@ -1335,7 +1367,7 @@ "key": "Account changes run on %@. Review the SQL before applying it.", "location": { "startingColumn": 18, - "startingLine": 3915 + "startingLine": 3946 } }, { @@ -1343,7 +1375,7 @@ "key": "Operation", "location": { "startingColumn": 24, - "startingLine": 3918 + "startingLine": 3949 } }, { @@ -1351,7 +1383,7 @@ "key": "Username", "location": { "startingColumn": 27, - "startingLine": 3923 + "startingLine": 3954 } }, { @@ -1359,7 +1391,7 @@ "key": "Host", "location": { "startingColumn": 27, - "startingLine": 3925 + "startingLine": 3956 } }, { @@ -1367,7 +1399,7 @@ "key": "Password", "location": { "startingColumn": 33, - "startingLine": 3927 + "startingLine": 3958 } }, { @@ -1375,7 +1407,7 @@ "key": "Database", "location": { "startingColumn": 31, - "startingLine": 3930 + "startingLine": 3961 } }, { @@ -1383,7 +1415,7 @@ "key": "Privilege", "location": { "startingColumn": 28, - "startingLine": 3931 + "startingLine": 3962 } }, { @@ -1391,7 +1423,7 @@ "key": "Close", "location": { "startingColumn": 24, - "startingLine": 3946 + "startingLine": 3977 } }, { @@ -1399,7 +1431,7 @@ "key": "Review SQL…", "location": { "startingColumn": 24, - "startingLine": 3948 + "startingLine": 3979 } }, { @@ -1407,7 +1439,7 @@ "key": "Cancel", "location": { "startingColumn": 20, - "startingLine": 3965 + "startingLine": 3996 } }, { @@ -1415,7 +1447,7 @@ "key": "Apply Change", "location": { "startingColumn": 20, - "startingLine": 3966 + "startingLine": 3997 } }, { @@ -1423,7 +1455,87 @@ "key": "Run this operation against %@?\n\n%@", "location": { "startingColumn": 18, - "startingLine": 3971 + "startingLine": 4002 + } + }, + { + "comment": "", + "key": "%lld samples. Rates compare the two latest successful refreshes; cumulative counters can reset when MySQL restarts or statistics are cleared.", + "location": { + "startingColumn": 18, + "startingLine": 4143 + } + }, + { + "comment": "", + "key": "Refresh this screen twice, or enable auto-refresh.", + "location": { + "startingColumn": 58, + "startingLine": 4147 + } + }, + { + "comment": "", + "key": "Query digest counts are grouped by SQL fingerprint. This is a sampled comparison, not a durable monitoring database.", + "location": { + "startingColumn": 18, + "startingLine": 4172 + } + }, + { + "comment": "", + "key": "More samples needed", + "location": { + "startingColumn": 40, + "startingLine": 4146 + } + }, + { + "comment": "", + "key": "%@ — Trends", + "location": { + "startingColumn": 22, + "startingLine": 4139 + } + }, + { + "comment": "", + "key": "Close", + "location": { + "startingColumn": 24, + "startingLine": 4141 + } + }, + { + "comment": "", + "key": "Metric", + "location": { + "startingColumn": 34, + "startingLine": 4152 + } + }, + { + "comment": "", + "key": "Current", + "location": { + "startingColumn": 34, + "startingLine": 4153 + } + }, + { + "comment": "", + "key": "Previous", + "location": { + "startingColumn": 34, + "startingLine": 4154 + } + }, + { + "comment": "", + "key": "Change/sec", + "location": { + "startingColumn": 34, + "startingLine": 4155 } } ] 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 7904cfd..2796245 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 4e23019..a4eab7b 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 5690d6b..34b656c 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 0b56cdc..e066dc1 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 ae994cc..4716024 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 1dd82f4..6addbb6 100644 --- a/build/Build/Intermediates.noindex/XCBuildData/registered-launchservices.txt +++ b/build/Build/Intermediates.noindex/XCBuildData/registered-launchservices.txt @@ -30,3 +30,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 cf38929..28cb370 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 e3f65ca..dbbdca6 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 8560fd8..738f882 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 9e990a2..68311f8 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 - s+RLc8/qmsHJTbP/vbm0kuQhxbA= + Fuz3gDgwkgMS+lEzJ6OSoxSzPqo= 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 0b56cdc..e066dc1 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 5690d6b..34b656c 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 c229536..277b5ce 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 facb46a..689f1f8 100644 --- a/build/Logs/Build/LogStoreManifest.plist +++ b/build/Logs/Build/LogStoreManifest.plist @@ -6,48 +6,6 @@ 13 logs - 0D84D8B7-4EBD-46B6-9E1E-5318CA005DB1 - - className - IDECommandLineBuildLog - documentTypeString - <nil> - domainType - Xcode.IDEActivityLogDomainType.BuildLog - fileName - 0D84D8B7-4EBD-46B6-9E1E-5318CA005DB1.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 - 813880069.98529804 - timeStoppedRecording - 813880086.21717203 - title - Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug - uniqueIdentifier - 0D84D8B7-4EBD-46B6-9E1E-5318CA005DB1 - 64DBE2F7-41EB-44D3-A0AB-CCC5468F4E72 className @@ -90,7 +48,7 @@ uniqueIdentifier 64DBE2F7-41EB-44D3-A0AB-CCC5468F4E72 - 9B404BEF-D5CC-4B4B-93CF-3A0C95C748D2 + F4C8C8BA-B9A7-46F3-8FF6-180215A4D5E4 className IDECommandLineBuildLog @@ -99,7 +57,7 @@ domainType Xcode.IDEActivityLogDomainType.BuildLog fileName - 9B404BEF-D5CC-4B4B-93CF-3A0C95C748D2.xcactivitylog + F4C8C8BA-B9A7-46F3-8FF6-180215A4D5E4.xcactivitylog hasPrimaryLog primaryObservable @@ -124,55 +82,13 @@ signature Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug timeStartedRecording - 813879702.54720795 + 813918930.91679394 timeStoppedRecording - 813879718.93544304 + 813918948.64702797 title Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug uniqueIdentifier - 9B404BEF-D5CC-4B4B-93CF-3A0C95C748D2 - - FE752EBA-0994-486D-9A0C-1DA5C49E3451 - - className - IDECommandLineBuildLog - documentTypeString - <nil> - domainType - Xcode.IDEActivityLogDomainType.BuildLog - fileName - FE752EBA-0994-486D-9A0C-1DA5C49E3451.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 - 813879926.65620303 - timeStoppedRecording - 813879942.82261503 - title - Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug - uniqueIdentifier - FE752EBA-0994-486D-9A0C-1DA5C49E3451 + F4C8C8BA-B9A7-46F3-8FF6-180215A4D5E4 diff --git a/build/ModuleCache.noindex/Session.modulevalidation b/build/ModuleCache.noindex/Session.modulevalidation index 529aeff..aa50651 100644 --- a/build/ModuleCache.noindex/Session.modulevalidation +++ b/build/ModuleCache.noindex/Session.modulevalidation @@ -1 +1 @@ -1792187454.770874: Module build session file for module cache at Path(str: "/Volumes/Developer/Applications/WorkbenchReplacement/build/ModuleCache.noindex") +1792226131.7427912: 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 9b8e1f6..e06d269 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 90f17fa..eebbe38 100644 --- a/build/info.plist +++ b/build/info.plist @@ -3,7 +3,7 @@ LastAccessedDate - 2026-10-16T21:50:54Z + 2026-10-17T08:35:31Z WorkspacePath /Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement.xcodeproj