62 lines
3.1 KiB
Swift
62 lines
3.1 KiB
Swift
import SwiftUI
|
|
|
|
struct DaemonLogWindowView: View {
|
|
@Bindable var controller: DaemonController
|
|
@State private var filterLevel: DaemonLogLine.Level? = nil
|
|
@State private var searchText = ""
|
|
@State private var autoscroll = true
|
|
|
|
private var filtered: [DaemonLogLine] {
|
|
var lines = controller.logLines
|
|
if let lv = filterLevel { lines = lines.filter { $0.level == lv } }
|
|
if !searchText.isEmpty { lines = lines.filter { $0.message.localizedCaseInsensitiveContains(searchText) } }
|
|
return lines
|
|
}
|
|
|
|
var body: some View {
|
|
VStack(spacing:0) {
|
|
HStack(spacing:10) {
|
|
HStack(spacing:4) {
|
|
levelBtn("All", nil)
|
|
levelBtn("Info", .info)
|
|
levelBtn("OK", .success)
|
|
levelBtn("Warn", .warning)
|
|
levelBtn("Err", .error)
|
|
}
|
|
Spacer()
|
|
HStack(spacing:4) {
|
|
Image(systemName:"magnifyingglass").foregroundStyle(.secondary).font(.caption)
|
|
TextField("Search…", text:$searchText).textFieldStyle(.roundedBorder).frame(width:160)
|
|
}
|
|
Toggle(isOn:$autoscroll) { Image(systemName:"arrow.down.to.line") }.toggleStyle(.button).help("Auto-scroll")
|
|
Button { controller.clearLog() } label: { Image(systemName:"trash") }.help("Clear")
|
|
}
|
|
.padding(.horizontal,12).padding(.vertical,8).background(Color(NSColor.windowBackgroundColor))
|
|
Divider()
|
|
ScrollViewReader { proxy in
|
|
ScrollView {
|
|
LazyVStack(alignment:.leading,spacing:0) {
|
|
ForEach(filtered) { line in
|
|
HStack(alignment:.top,spacing:8) {
|
|
Text(line.formattedTime).font(.system(.caption2,design:.monospaced)).foregroundStyle(.tertiary).frame(width:60,alignment:.leading)
|
|
Text(line.level.prefix).font(.caption).frame(width:16)
|
|
Text(line.message).font(.system(.caption,design:.monospaced)).textSelection(.enabled).frame(maxWidth:.infinity,alignment:.leading)
|
|
}
|
|
.padding(.vertical,1).padding(.horizontal,4)
|
|
.background(line.level == .error ? Color.red.opacity(0.06) : line.level == .warning ? Color.orange.opacity(0.06) : .clear)
|
|
.id(line.id)
|
|
}
|
|
}.padding(.horizontal,8).padding(.vertical,4)
|
|
}
|
|
.onChange(of:filtered.count) { _,_ in if autoscroll, let last = filtered.last { proxy.scrollTo(last.id,anchor:.bottom) } }
|
|
}
|
|
}
|
|
.background(Color(NSColor.textBackgroundColor))
|
|
}
|
|
|
|
private func levelBtn(_ label: String, _ level: DaemonLogLine.Level?) -> some View {
|
|
Button(label) { filterLevel = level }
|
|
.buttonStyle(.bordered).controlSize(.mini)
|
|
.tint(filterLevel == level ? .accentColor : .secondary)
|
|
}
|
|
}
|