Improved progress display
Previously, the progress bar updated only after completing an entire table. For large tables such as LIBARC, this could make the application appear to have stopped responding. Phase 10 updates the interface while rows are being exported.
This commit is contained in:
@@ -2307,6 +2307,7 @@ struct WorkbenchAdministrationView: View {
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
} else {
|
||||
resultContent
|
||||
.layoutPriority(1)
|
||||
}
|
||||
|
||||
if let guidance = diagnosticGuidance {
|
||||
@@ -2560,15 +2561,22 @@ struct WorkbenchAdministrationView: View {
|
||||
)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
case .dashboard:
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 18) {
|
||||
WorkbenchInlinePerformanceDashboard(samples: diagnosticSamples)
|
||||
// Dashboard must not show the legacy, unformatted Variable_name table.
|
||||
Text("Server overview").font(.title2.bold())
|
||||
// Keep the live charts visible even while scrolling the overview.
|
||||
// The old outer ScrollView moved all charts offscreen when scrolled.
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
WorkbenchInlinePerformanceDashboard(samples: diagnosticSamples)
|
||||
.frame(maxWidth: .infinity, alignment: .topLeading)
|
||||
.layoutPriority(2)
|
||||
Divider()
|
||||
Text("Server overview").font(.title2.bold())
|
||||
ScrollView(.vertical) {
|
||||
WorkbenchServerOverview(rows: filteredRows)
|
||||
.frame(maxWidth: .infinity, alignment: .topLeading)
|
||||
}
|
||||
.padding(18)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
}
|
||||
.padding(16)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||
default:
|
||||
// Status and System Variables is deliberately a distinct variable
|
||||
// inspector, never replaced by the monitoring screen.
|
||||
@@ -4134,9 +4142,9 @@ private struct WorkbenchInlinePerformanceDashboard: View {
|
||||
.lineStyle(StrokeStyle(lineWidth: 2.5))
|
||||
}
|
||||
.chartYAxis { AxisMarks(position: .leading) }
|
||||
.frame(height: 130)
|
||||
.frame(height: 92)
|
||||
}
|
||||
.padding(14)
|
||||
.padding(10)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.background(Color(nsColor: .controlBackgroundColor))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 12))
|
||||
@@ -4151,7 +4159,9 @@ private struct WorkbenchInlinePerformanceDashboard: View {
|
||||
Text("\(samples.count) samples · refreshes every 5 seconds")
|
||||
.font(.caption).foregroundStyle(.secondary)
|
||||
}
|
||||
LazyVGrid(columns: [GridItem(.adaptive(minimum: 285), spacing: 14)], spacing: 12) {
|
||||
LazyVGrid(columns: [GridItem(.flexible(minimum: 0), spacing: 12),
|
||||
GridItem(.flexible(minimum: 0), spacing: 12),
|
||||
GridItem(.flexible(minimum: 0), spacing: 12)], spacing: 10) {
|
||||
panel("Queries per second", metric: "Queries", rate: true, tint: .blue)
|
||||
panel("Active connections", metric: "Threads_connected", rate: false, tint: .teal)
|
||||
panel("Running threads", metric: "Threads_running", rate: false, tint: .orange)
|
||||
@@ -4320,83 +4330,76 @@ private struct WorkbenchLiveMonitorScreen: View {
|
||||
}
|
||||
}
|
||||
|
||||
// Charts and counters are sibling regions. Neither is wrapped in the
|
||||
// other region's ScrollView; the charts stay on-screen while rows scroll.
|
||||
// Fixed-height chart cards live OUTSIDE the independently scrolling table.
|
||||
// Avoid GeometryReader and nested graph ScrollViews: they previously allowed
|
||||
// the graph viewport to collapse when the parent administration view laid out.
|
||||
var body: some View {
|
||||
GeometryReader { geometry in
|
||||
let width = max(0, geometry.size.width - 32)
|
||||
let twoColumns = width >= 640
|
||||
let gridColumns = Array(repeating: GridItem(.flexible(minimum: 0), spacing: 12), count: twoColumns ? 2 : 1)
|
||||
let cardHeight: CGFloat = twoColumns ? 160 : 125
|
||||
let chartAreaHeight: CGFloat = twoColumns ? 334 : min(285, max(150, geometry.size.height * 0.40))
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
HStack(spacing: 10) {
|
||||
Label(pageTitle + " · LIVE GRAPHS", systemImage: "chart.xyaxis.line")
|
||||
.font(.title3.bold())
|
||||
Spacer(minLength: 4)
|
||||
Text("\(samples.count) samples · 5s refresh")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
HStack(spacing: 10) {
|
||||
Label(pageTitle, systemImage: "chart.xyaxis.line")
|
||||
.font(.title3.bold())
|
||||
Spacer(minLength: 4)
|
||||
Text("Live · \(samples.count) samples · 5s")
|
||||
.font(.caption).foregroundStyle(.secondary)
|
||||
if page == .serverStatus || page == .serverCounters {
|
||||
Picker("Graphs", selection: $chartGroup) {
|
||||
Text("Activity").tag("Activity")
|
||||
Text("Network").tag("Network")
|
||||
Text("InnoDB").tag("InnoDB")
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.pickerStyle(.segmented)
|
||||
.frame(maxWidth: 350)
|
||||
}
|
||||
|
||||
if page == .serverStatus || page == .serverCounters {
|
||||
Picker("Graphs", selection: $chartGroup) {
|
||||
Text("Activity").tag("Activity")
|
||||
Text("Network").tag("Network")
|
||||
Text("InnoDB").tag("InnoDB")
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
.frame(maxWidth: 350)
|
||||
}
|
||||
|
||||
// A bounded graph viewport, with two complete columns on
|
||||
// desktop. On narrow windows, only this area scrolls.
|
||||
ScrollView(.vertical) {
|
||||
LazyVGrid(columns: gridColumns, spacing: 12) {
|
||||
ForEach(metrics, id: \.self) { metric in
|
||||
chart(metric)
|
||||
.frame(height: cardHeight)
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.frame(height: min(chartAreaHeight, max(135, geometry.size.height * 0.54)))
|
||||
.accessibilityIdentifier("monitor-graphs-visible")
|
||||
|
||||
Divider()
|
||||
if page == .serverStatus {
|
||||
Text("Server overview")
|
||||
.font(.headline)
|
||||
ScrollView(.vertical) {
|
||||
WorkbenchServerOverview(rows: rows)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
} else {
|
||||
HStack {
|
||||
Text(page == .connectionStatistics ? "Connection statistics" : page == .innodbMetrics ? "InnoDB statistics" : "All MySQL server counters")
|
||||
.font(.headline)
|
||||
Spacer(minLength: 6)
|
||||
if page == .serverCounters {
|
||||
Picker("Category", selection: $categorySelection) {
|
||||
ForEach(categories, id: \.self) { Text($0).tag($0) }
|
||||
}
|
||||
.frame(width: 155)
|
||||
}
|
||||
Text("\(visibleRows.count) counters")
|
||||
.font(.caption).foregroundStyle(.secondary)
|
||||
}
|
||||
WorkbenchColouredCounterTable(columns: ["Metric", "Value"], rows: visibleRows)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
// Exactly two rows of two graphs. No graph-region scrolling and no
|
||||
// dependence on the size proposed by a surrounding GeometryReader.
|
||||
LazyVGrid(
|
||||
columns: [GridItem(.flexible(minimum: 0), spacing: 12),
|
||||
GridItem(.flexible(minimum: 0), spacing: 12)],
|
||||
spacing: 12
|
||||
) {
|
||||
ForEach(metrics, id: \.self) { metric in
|
||||
chart(metric)
|
||||
.frame(height: 148)
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
}
|
||||
.padding(16)
|
||||
.frame(width: geometry.size.width, height: geometry.size.height, alignment: .topLeading)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.accessibilityIdentifier("monitor-graphs-visible")
|
||||
|
||||
Divider()
|
||||
|
||||
if page == .serverStatus {
|
||||
Text("Server overview").font(.headline)
|
||||
ScrollView(.vertical) {
|
||||
WorkbenchServerOverview(rows: rows)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
} else {
|
||||
HStack {
|
||||
Text(page == .connectionStatistics ? "Connection statistics" : page == .innodbMetrics ? "InnoDB statistics" : "All MySQL server counters")
|
||||
.font(.headline)
|
||||
Spacer(minLength: 6)
|
||||
if page == .serverCounters {
|
||||
Picker("Category", selection: $categorySelection) {
|
||||
ForEach(categories, id: \.self) { Text($0).tag($0) }
|
||||
}
|
||||
.frame(width: 155)
|
||||
}
|
||||
Text("\(visibleRows.count) counters")
|
||||
.font(.caption).foregroundStyle(.secondary)
|
||||
}
|
||||
WorkbenchColouredCounterTable(columns: ["Metric", "Value"], rows: visibleRows)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
}
|
||||
}
|
||||
.padding(16)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||
}
|
||||
}
|
||||
|
||||
private struct WorkbenchServerCountersScreen: View {
|
||||
|
||||
Reference in New Issue
Block a user