Tools View & Crash Report & OOM Report
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
import SwiftUI
|
||||
|
||||
private struct OrderedStringMap {
|
||||
let entries: [(key: String, value: String)]
|
||||
|
||||
init?(data: Data) {
|
||||
guard let json = String(data: data, encoding: .utf8) else { return nil }
|
||||
var entries: [(key: String, value: String)] = []
|
||||
var rest = json[...]
|
||||
|
||||
func skip(_ ch: Character) -> Bool {
|
||||
rest = rest.drop(while: \.isWhitespace)
|
||||
guard rest.first == ch else { return false }
|
||||
rest = rest.dropFirst()
|
||||
return true
|
||||
}
|
||||
|
||||
func readString() -> String? {
|
||||
rest = rest.drop(while: \.isWhitespace)
|
||||
guard rest.first == "\"" else { return nil }
|
||||
rest = rest.dropFirst()
|
||||
var s = ""
|
||||
while let ch = rest.first, ch != "\"" {
|
||||
if ch == "\\" { rest = rest.dropFirst() }
|
||||
if let c = rest.first { s.append(c); rest = rest.dropFirst() }
|
||||
}
|
||||
if !rest.isEmpty { rest = rest.dropFirst() }
|
||||
return s
|
||||
}
|
||||
|
||||
guard skip("{") else { return nil }
|
||||
while true {
|
||||
guard let key = readString(), skip(":"), let value = readString() else { break }
|
||||
if !value.isEmpty { entries.append((key: key, value: value)) }
|
||||
if !skip(",") { break }
|
||||
}
|
||||
self.entries = entries
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
public struct MetadataFormView: View {
|
||||
@State private var entries: [(key: String, value: String)] = []
|
||||
@State private var isLoading = true
|
||||
|
||||
let url: URL
|
||||
let title: String
|
||||
|
||||
public init(url: URL, title: String) {
|
||||
self.url = url
|
||||
self.title = title
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
FormView {
|
||||
if !isLoading {
|
||||
Section {
|
||||
ForEach(entries, id: \.key) { entry in
|
||||
FormTextItem(LocalizedStringKey(entry.key), entry.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.overlay {
|
||||
if isLoading {
|
||||
ProgressView()
|
||||
} else if entries.isEmpty {
|
||||
Text("Empty")
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
Task.detached {
|
||||
let loaded = loadEntries()
|
||||
await MainActor.run {
|
||||
entries = loaded
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle(title)
|
||||
}
|
||||
|
||||
private nonisolated func loadEntries() -> [(key: String, value: String)] {
|
||||
guard let data = try? Data(contentsOf: url),
|
||||
let map = OrderedStringMap(data: data)
|
||||
else {
|
||||
return []
|
||||
}
|
||||
return map.entries
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
#if canImport(UIKit)
|
||||
import UIKit
|
||||
#elseif canImport(AppKit)
|
||||
import AppKit
|
||||
#endif
|
||||
|
||||
import SwiftUI
|
||||
|
||||
#if os(tvOS)
|
||||
struct PlainTextView: UIViewRepresentable {
|
||||
let content: String
|
||||
|
||||
private static let monoFont = UIFont.monospacedSystemFont(ofSize: 24, weight: .regular)
|
||||
|
||||
func makeUIView(context _: Context) -> UITextView {
|
||||
let textView = UITextView()
|
||||
// isSelectable must be true for UITextView to be focusable on tvOS.
|
||||
// Without focus, the Siri Remote cannot scroll the content.
|
||||
// SwiftUI ScrollView + Text / LazyVStack + .focusable() do NOT work
|
||||
// reliably inside navigation destinations on tvOS.
|
||||
textView.isSelectable = true
|
||||
textView.isUserInteractionEnabled = true
|
||||
textView.isScrollEnabled = true
|
||||
textView.backgroundColor = .clear
|
||||
textView.textContainerInset = UIEdgeInsets(top: 40, left: 40, bottom: 40, right: 40)
|
||||
textView.textContainer.lineFragmentPadding = 0
|
||||
textView.font = Self.monoFont
|
||||
textView.textColor = .label
|
||||
textView.text = content
|
||||
textView.panGestureRecognizer.allowedTouchTypes = [NSNumber(value: UITouch.TouchType.indirect.rawValue)]
|
||||
return textView
|
||||
}
|
||||
|
||||
func updateUIView(_: UITextView, context _: Context) {}
|
||||
}
|
||||
|
||||
#elseif os(iOS)
|
||||
struct PlainTextView: UIViewRepresentable {
|
||||
let content: String
|
||||
|
||||
private static let monoFont = UIFont.monospacedSystemFont(ofSize: 12, weight: .regular)
|
||||
|
||||
func makeUIView(context _: Context) -> UITextView {
|
||||
let textView = UITextView()
|
||||
textView.isEditable = false
|
||||
textView.isSelectable = true
|
||||
textView.isScrollEnabled = false
|
||||
textView.backgroundColor = .clear
|
||||
textView.textContainerInset = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)
|
||||
textView.textContainer.lineFragmentPadding = 0
|
||||
textView.font = Self.monoFont
|
||||
textView.textColor = .label
|
||||
textView.text = content
|
||||
textView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
||||
return textView
|
||||
}
|
||||
|
||||
func updateUIView(_: UITextView, context _: Context) {}
|
||||
}
|
||||
|
||||
#elseif os(macOS)
|
||||
struct PlainTextView: NSViewRepresentable {
|
||||
let content: String
|
||||
|
||||
private static let monoFont = NSFont.monospacedSystemFont(ofSize: 12, weight: .regular)
|
||||
|
||||
func makeNSView(context _: Context) -> NSScrollView {
|
||||
let scrollView = NSScrollView()
|
||||
scrollView.hasVerticalScroller = true
|
||||
scrollView.hasHorizontalScroller = false
|
||||
scrollView.autohidesScrollers = true
|
||||
|
||||
let textView = NSTextView()
|
||||
textView.isEditable = false
|
||||
textView.isSelectable = true
|
||||
textView.drawsBackground = false
|
||||
textView.textContainerInset = NSSize(width: 16, height: 16)
|
||||
textView.font = Self.monoFont
|
||||
textView.textColor = .labelColor
|
||||
textView.autoresizingMask = [.width]
|
||||
textView.string = content
|
||||
|
||||
if let textContainer = textView.textContainer {
|
||||
textContainer.widthTracksTextView = true
|
||||
textContainer.containerSize = NSSize(width: scrollView.contentSize.width, height: .greatestFiniteMagnitude)
|
||||
textContainer.lineFragmentPadding = 0
|
||||
}
|
||||
|
||||
scrollView.documentView = textView
|
||||
return scrollView
|
||||
}
|
||||
|
||||
func updateNSView(_: NSScrollView, context _: Context) {}
|
||||
}
|
||||
#endif
|
||||
@@ -82,7 +82,7 @@ public struct ShareButtonCompat<Label: View>: View {
|
||||
do {
|
||||
let shareItem = try await itemURL()
|
||||
await MainActor.run {
|
||||
presentShareController(shareItem)
|
||||
presentShareSheet(shareItem)
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
@@ -91,22 +91,6 @@ public struct ShareButtonCompat<Label: View>: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func presentShareController(_ item: URL) {
|
||||
guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
|
||||
let rootViewController = windowScene.keyWindow?.rootViewController
|
||||
else {
|
||||
return
|
||||
}
|
||||
var topViewController = rootViewController
|
||||
while let presented = topViewController.presentedViewController {
|
||||
topViewController = presented
|
||||
}
|
||||
topViewController.present(
|
||||
UIActivityViewController(activityItems: [item], applicationActivities: nil),
|
||||
animated: true
|
||||
)
|
||||
}
|
||||
|
||||
#elseif os(macOS)
|
||||
private nonisolated func shareItemAsync() async {
|
||||
do {
|
||||
@@ -125,7 +109,7 @@ public struct ShareButtonCompat<Label: View>: View {
|
||||
}
|
||||
|
||||
#if os(macOS)
|
||||
private struct SharingServicePicker: NSViewRepresentable {
|
||||
struct SharingServicePicker: NSViewRepresentable {
|
||||
@Binding private var isPresented: Bool
|
||||
@Binding private var alert: AlertState?
|
||||
@Binding private var item: URL?
|
||||
|
||||
Reference in New Issue
Block a user