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 {
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# Phase 44 — Monitoring graphs layout
|
||||
|
||||
- Removed the GeometryReader and nested graph ScrollView from the active `WorkbenchLiveMonitorScreen`.
|
||||
- Four live charts now render in a fixed two-column, two-row grid at the top of Server Status, Server Counters, Connection Statistics and InnoDB Metrics.
|
||||
- The counters/overview region below the charts scrolls independently.
|
||||
- Gave administration result content higher layout priority than the footer and auxiliary controls.
|
||||
- Kept the existing five-second sample collection and graph calculation logic.
|
||||
|
||||
Validation: `swiftc -frontend -parse` passed for the Swift source files on Linux. macOS SDK compilation and on-screen appearance have not been verified.
|
||||
Reference in New Issue
Block a user