Improve auto-probe UX: auto-connect after finding device

- Auto-probe now automatically connects after finding a VU meter
- Add connection status indicator (green/red dot) on each dial
- Add animation to dial value changes
- Show minimum arc value for better visibility at low values
- Display error messages in hardware panel
- Show "No USB serial devices" message when none found
- Improved status display with port name in green when connected
This commit is contained in:
Claude
2025-12-14 15:29:45 +00:00
parent 0ecf2c7940
commit 5e0cc74aaf
2 changed files with 50 additions and 12 deletions
+42 -8
View File
@@ -91,7 +91,7 @@ struct HardwarePanelView: View {
.disabled(serialManager.isProbing)
}
// Stats / Device info
// Stats / Device info / Errors
if serialManager.isConnected {
HStack {
Text("TX: \(formatBytes(serialManager.bytesSent))")
@@ -102,7 +102,20 @@ struct HardwarePanelView: View {
Text(serialManager.selectedPortPath.components(separatedBy: "/").last ?? "")
.font(.system(size: 9, design: .monospaced))
.foregroundColor(.gray)
.foregroundColor(.green)
}
} else if let error = serialManager.lastError {
HStack {
Image(systemName: "exclamationmark.triangle.fill")
.foregroundColor(.red)
.font(.system(size: 10))
Text(error)
.font(.system(size: 9, design: .monospaced))
.foregroundColor(.red)
.lineLimit(2)
Spacer()
}
} else if let detected = serialManager.detectedDevice {
HStack {
@@ -122,6 +135,18 @@ struct HardwarePanelView: View {
.foregroundColor(.gray)
}
}
} else if serialManager.availablePorts.isEmpty {
HStack {
Image(systemName: "usb")
.foregroundColor(.orange)
.font(.system(size: 10))
Text("No USB serial devices detected")
.font(.system(size: 9, design: .monospaced))
.foregroundColor(.orange)
Spacer()
}
}
}
.padding()
@@ -183,29 +208,35 @@ struct DialIndicatorView: View {
var body: some View {
VStack(spacing: 4) {
// Dial number
// Dial number with connection indicator
HStack(spacing: 2) {
Circle()
.fill(isConnected ? Color.green : Color.red)
.frame(width: 5, height: 5)
Text("D\(dialNumber)")
.font(.system(size: 10, weight: .bold, design: .monospaced))
.foregroundColor(.white.opacity(0.7))
}
// Value arc
ZStack {
// Background arc
Circle()
.trim(from: 0.25, to: 0.75)
.stroke(Color.gray.opacity(0.2), lineWidth: 4)
.stroke(Color.gray.opacity(0.3), lineWidth: 4)
.frame(width: 50, height: 50)
.rotationEffect(.degrees(180))
// Value arc
// Value arc - always show with minimum visibility
Circle()
.trim(from: 0.25, to: 0.25 + (Double(value) / 255.0) * 0.5)
.trim(from: 0.25, to: 0.25 + max(0.02, (Double(value) / 255.0) * 0.5))
.stroke(
isConnected ? dialColor(for: value) : Color.gray,
dialColor(for: value, connected: isConnected),
style: StrokeStyle(lineWidth: 4, lineCap: .round)
)
.frame(width: 50, height: 50)
.rotationEffect(.degrees(180))
.animation(.easeOut(duration: 0.1), value: value)
// Value text
VStack(spacing: 0) {
@@ -222,7 +253,10 @@ struct DialIndicatorView: View {
}
}
private func dialColor(for value: Int) -> Color {
private func dialColor(for value: Int, connected: Bool) -> Color {
if !connected {
return Color.gray.opacity(0.5)
}
let ratio = Double(value) / 255.0
if ratio > 0.9 { return .red }
if ratio > 0.75 { return .orange }
@@ -424,13 +424,17 @@ class SerialManager: ObservableObject {
self.selectedProtocol = best.protocol_
self.baudRate = best.baudRate
self.probeStatus = "Found: \(best.port.name)"
// Auto-connect after successful probe
self.connect()
} else if let firstWorking = workingPorts.first {
// No response but port works - use it anyway
self.detectedDevice = firstWorking.port
self.selectedPortPath = firstWorking.port.path
self.selectedProtocol = .vuServer
self.baudRate = firstWorking.baudRate
self.probeStatus = "Using: \(firstWorking.port.name) (no response)"
self.probeStatus = "Using: \(firstWorking.port.name)"
// Auto-connect
self.connect()
} else {
self.probeStatus = "No serial devices found"
}