mirror of
https://github.com/yattee/yattee.git
synced 2026-05-12 18:35:05 +00:00
Polish Log Viewer for macOS
Convert detail and filter sheets to shared helpers, add inline Filter / Export / Clear buttons next to the search bar (toolbar items weren't surfacing in the settings detail pane), inline the Reset Filters button at the bottom of the filter sheet, use a 'Close' text button, and trim the macOS Share Sheet to just the scrollable log with a Copy button.
This commit is contained in:
@@ -22,16 +22,14 @@ struct ShareSheet: View {
|
|||||||
let items: [Any]
|
let items: [Any]
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
VStack(spacing: 12) {
|
||||||
Text(String(localized: "settings.advanced.logs.export.instructions"))
|
|
||||||
.padding()
|
|
||||||
|
|
||||||
if let text = items.first as? String {
|
if let text = items.first as? String {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
Text(text)
|
Text(text)
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.fontDesign(.monospaced)
|
.fontDesign(.monospaced)
|
||||||
.textSelection(.enabled)
|
.textSelection(.enabled)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
.frame(maxHeight: 400)
|
.frame(maxHeight: 400)
|
||||||
@@ -43,9 +41,10 @@ struct ShareSheet: View {
|
|||||||
NSPasteboard.general.setString(text, forType: .string)
|
NSPasteboard.general.setString(text, forType: .string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding()
|
.keyboardShortcut(.defaultAction)
|
||||||
}
|
}
|
||||||
.frame(minWidth: 400, minHeight: 300)
|
.padding()
|
||||||
|
.frame(minWidth: 420, minHeight: 320)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -19,8 +19,38 @@ struct LogViewerView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
// Search bar
|
#if os(macOS)
|
||||||
|
HStack(spacing: 8) {
|
||||||
|
searchBar
|
||||||
|
|
||||||
|
Button {
|
||||||
|
showingFilters = true
|
||||||
|
} label: {
|
||||||
|
Label(String(localized: "settings.advanced.logs.filter"), systemImage: "line.3.horizontal.decrease.circle")
|
||||||
|
.labelStyle(.iconOnly)
|
||||||
|
}
|
||||||
|
.help(String(localized: "settings.advanced.logs.filter"))
|
||||||
|
|
||||||
|
Button {
|
||||||
|
showingExportSheet = true
|
||||||
|
} label: {
|
||||||
|
Label(String(localized: "settings.advanced.logs.export"), systemImage: "square.and.arrow.up")
|
||||||
|
.labelStyle(.iconOnly)
|
||||||
|
}
|
||||||
|
.help(String(localized: "settings.advanced.logs.export"))
|
||||||
|
|
||||||
|
Button(role: .destructive) {
|
||||||
|
loggingService.clearLogs()
|
||||||
|
} label: {
|
||||||
|
Label(String(localized: "settings.advanced.logs.clear"), systemImage: "trash")
|
||||||
|
.labelStyle(.iconOnly)
|
||||||
|
}
|
||||||
|
.help(String(localized: "settings.advanced.logs.clear"))
|
||||||
|
}
|
||||||
|
.padding(.trailing)
|
||||||
|
#else
|
||||||
searchBar
|
searchBar
|
||||||
|
#endif
|
||||||
|
|
||||||
// Log list
|
// Log list
|
||||||
logList
|
logList
|
||||||
@@ -192,13 +222,18 @@ private struct LogEntryDetailView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
List {
|
SettingsFormContainer {
|
||||||
Section(String(localized: "settings.advanced.logs.detail.info")) {
|
SettingsFormSection("settings.advanced.logs.detail.info") {
|
||||||
LabeledContent(String(localized: "settings.advanced.logs.detail.timestamp")) {
|
HStack {
|
||||||
|
Text(String(localized: "settings.advanced.logs.detail.timestamp"))
|
||||||
|
Spacer()
|
||||||
Text(entry.timestamp.formatted(date: .abbreviated, time: .standard))
|
Text(entry.timestamp.formatted(date: .abbreviated, time: .standard))
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
}
|
}
|
||||||
|
|
||||||
LabeledContent(String(localized: "settings.advanced.logs.detail.level")) {
|
HStack {
|
||||||
|
Text(String(localized: "settings.advanced.logs.detail.level"))
|
||||||
|
Spacer()
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: entry.level.icon)
|
Image(systemName: entry.level.icon)
|
||||||
Text(entry.level.rawValue.capitalized)
|
Text(entry.level.rawValue.capitalized)
|
||||||
@@ -206,27 +241,32 @@ private struct LogEntryDetailView: View {
|
|||||||
.foregroundStyle(levelColor)
|
.foregroundStyle(levelColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
LabeledContent(String(localized: "settings.advanced.logs.detail.category")) {
|
HStack {
|
||||||
|
Text(String(localized: "settings.advanced.logs.detail.category"))
|
||||||
|
Spacer()
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: entry.category.icon)
|
Image(systemName: entry.category.icon)
|
||||||
Text(entry.category.rawValue)
|
Text(entry.category.rawValue)
|
||||||
}
|
}
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Section(String(localized: "settings.advanced.logs.detail.message")) {
|
SettingsFormSection("settings.advanced.logs.detail.message") {
|
||||||
Text(entry.message)
|
Text(entry.message)
|
||||||
.font(.body)
|
.font(.body)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
.textSelection(.enabled)
|
.textSelection(.enabled)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if let details = entry.details {
|
if let details = entry.details {
|
||||||
Section(String(localized: "settings.advanced.logs.detail.details")) {
|
SettingsFormSection("settings.advanced.logs.detail.details") {
|
||||||
Text(details)
|
Text(details)
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.fontDesign(.monospaced)
|
.fontDesign(.monospaced)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
.textSelection(.enabled)
|
.textSelection(.enabled)
|
||||||
#endif
|
#endif
|
||||||
@@ -249,6 +289,9 @@ private struct LogEntryDetailView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.presentationDetents([.medium, .large])
|
.presentationDetents([.medium, .large])
|
||||||
|
#if os(macOS)
|
||||||
|
.frame(minWidth: 500, minHeight: 400)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private var levelColor: Color {
|
private var levelColor: Color {
|
||||||
@@ -269,28 +312,38 @@ private struct LogFiltersSheet: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
List {
|
SettingsFormContainer {
|
||||||
Section(String(localized: "settings.advanced.logs.filter.categories")) {
|
SettingsFormSection("settings.advanced.logs.filter.categories") {
|
||||||
ForEach(LogCategory.allCases, id: \.self) { category in
|
ForEach(LogCategory.allCases, id: \.self) { category in
|
||||||
Toggle(isOn: binding(for: category)) {
|
Toggle(isOn: binding(for: category)) {
|
||||||
Label(category.rawValue, systemImage: category.icon)
|
Label(category.rawValue, systemImage: category.icon)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if os(macOS)
|
||||||
|
.labelStyle(FixedIconWidthLabelStyle())
|
||||||
|
#endif
|
||||||
|
|
||||||
Section(String(localized: "settings.advanced.logs.filter.levels")) {
|
SettingsFormSection("settings.advanced.logs.filter.levels") {
|
||||||
ForEach(LogLevel.allCases, id: \.self) { level in
|
ForEach(LogLevel.allCases, id: \.self) { level in
|
||||||
Toggle(isOn: binding(for: level)) {
|
Toggle(isOn: binding(for: level)) {
|
||||||
Label(level.rawValue.capitalized, systemImage: level.icon)
|
Label(level.rawValue.capitalized, systemImage: level.icon)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if os(macOS)
|
||||||
|
.labelStyle(FixedIconWidthLabelStyle())
|
||||||
|
#endif
|
||||||
|
|
||||||
Section {
|
HStack {
|
||||||
Button(String(localized: "settings.advanced.logs.filter.reset")) {
|
Button(String(localized: "settings.advanced.logs.filter.reset")) {
|
||||||
loggingService.resetFilters()
|
loggingService.resetFilters()
|
||||||
}
|
}
|
||||||
|
Spacer()
|
||||||
}
|
}
|
||||||
|
.padding(.horizontal, 16)
|
||||||
|
.padding(.top, 4)
|
||||||
|
.padding(.bottom, 16)
|
||||||
}
|
}
|
||||||
.navigationTitle(String(localized: "settings.advanced.logs.filter.title"))
|
.navigationTitle(String(localized: "settings.advanced.logs.filter.title"))
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@@ -301,8 +354,7 @@ private struct LogFiltersSheet: View {
|
|||||||
Button(role: .cancel) {
|
Button(role: .cancel) {
|
||||||
dismiss()
|
dismiss()
|
||||||
} label: {
|
} label: {
|
||||||
Label(String(localized: "common.close"), systemImage: "xmark")
|
Text(String(localized: "common.close"))
|
||||||
.labelStyle(.iconOnly)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user