import SwiftUI import UIKit /// Diagnostic log viewer. Shows the in-memory/persisted log entries with a /// Share button (so the user can send logs to Hermes) and a Clear button. struct LogsView: View { @ObservedObject private var store = LogStore.shared @State private var showShare = false var body: some View { Group { if store.entries.isEmpty { ContentUnavailableView( "No logs yet", systemImage: "doc.text", description: Text("Run the app and reproduce the issue, then come back here.")) } else { List { ForEach(store.entries.reversed()) { entry in VStack(alignment: .leading, spacing: 2) { HStack { Text(entry.level.rawValue) .font(.caption2.weight(.bold)) .foregroundColor(color(for: entry.level)) Text(entry.timestamp, style: .time) .font(.caption2) .foregroundColor(.secondary) } Text(entry.message) .font(.caption.monospaced()) .textSelection(.enabled) } .padding(.vertical, 2) } } } } .navigationTitle("Logs") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .primaryAction) { Button { showShare = true } label: { Image(systemName: "square.and.arrow.up") } .disabled(store.entries.isEmpty) } ToolbarItem(placement: .cancellationAction) { Button("Clear") { store.clear() } .disabled(store.entries.isEmpty) } } .sheet(isPresented: $showShare) { ShareSheet(text: store.text) } } private func color(for level: LogStore.Entry.Level) -> Color { switch level { case .info: return .secondary case .warn: return .orange case .error: return .red } } } /// Minimal share sheet wrapper (ShareLink is iOS 16+; this is explicit). struct ShareSheet: UIViewControllerRepresentable { let text: String func makeUIViewController(context: Context) -> UIActivityViewController { UIActivityViewController(activityItems: [text], applicationActivities: nil) } func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {} }