import SwiftUI // 引擎日志独立界面(#3):展示 EngineController.logs 环形缓冲里最近 N 条 console.warn/error,分级配色、 // 可选中复制、可一键清空。从设置页「查看日志」进入。便于在真机上(无 Web Inspector)定位 SSE / 鉴权 / // 传输失败原因——此前只能在设置页看到孤零零一行 lastEngineLog。 struct LogView: View { @Environment(EngineController.self) private var engine var body: some View { Group { if engine.logs.isEmpty { ContentUnavailableView(t("ios.logs.empty"), systemImage: "doc.text") } else { List { ForEach(engine.logs) { entry in logRow(entry) } } .listStyle(.plain) } } .navigationTitle(t("ios.logs.title")) .navigationBarTitleDisplayMode(.inline) .toolbar { if !engine.logs.isEmpty { ToolbarItem(placement: .topBarTrailing) { Button(role: .destructive) { engine.clearLogs() } label: { Label(t("ios.logs.clear"), systemImage: "trash") } } } } } private func logRow(_ entry: LogEntry) -> some View { VStack(alignment: .leading, spacing: 4) { HStack(spacing: 6) { Image(systemName: levelSymbol(entry.level)) .font(.caption) .foregroundStyle(levelTint(entry.level)) Text(timeString(entry.at)) .font(.caption2) .foregroundStyle(.secondary) .monospacedDigit() } Text(entry.message) .font(.caption.monospaced()) .foregroundStyle(.primary) .textSelection(.enabled) .fixedSize(horizontal: false, vertical: true) } .padding(.vertical, 2) } private func levelSymbol(_ level: String) -> String { switch level { case "error": return "xmark.octagon.fill" case "warn": return "exclamationmark.triangle.fill" default: return "info.circle" } } private func levelTint(_ level: String) -> Color { switch level { case "error": return .red case "warn": return .orange default: return .secondary } } private func timeString(_ date: Date) -> String { let f = DateFormatter() f.dateFormat = "HH:mm:ss" return f.string(from: date) } }