Add iPad support, auto-pinning, and comprehensive logging

- Adaptive iPhone/iPad layout with NavigationSplitView sidebar
- Auto-detect SSL-pinned domains, fall back to passthrough
- Certificate install via local HTTP server (Safari profile flow)
- App Group-backed CA, per-domain leaf cert LRU cache
- DB-backed config repository, Darwin notification throttling
- Rules engine, breakpoint rules, pinned domain tracking
- os.Logger instrumentation across tunnel/proxy/mitm/capture/cert/rules/db/ipc/ui
- Fix dyld framework embed, race conditions, thread safety
This commit is contained in:
Trey t
2026-04-11 12:52:18 -05:00
parent c77e506db5
commit 148bc3887c
77 changed files with 6710 additions and 847 deletions

View File

@@ -0,0 +1,136 @@
import SwiftUI
struct JSONTreeView: View {
let data: Data
@State private var root: JSONNode?
var body: some View {
Group {
if let root {
JSONNodeExpandableView(
node: root,
collapseByDefault: false,
childCollapseThreshold: 50
)
} else {
Text("Invalid JSON")
.font(.system(.caption, design: .monospaced))
.foregroundStyle(.secondary)
}
}
.task {
root = parseJSON(data)
}
}
private func parseJSON(_ data: Data) -> JSONNode? {
guard let obj = try? JSONSerialization.jsonObject(with: data) else { return nil }
return buildNode(key: nil, value: obj)
}
private func buildNode(key: String?, value: Any) -> JSONNode {
if let dict = value as? [String: Any] {
let children = dict.sorted(by: { $0.key < $1.key }).map { buildNode(key: $0.key, value: $0.value) }
return JSONNode(key: key, kind: .object, displayValue: "{\(dict.count)}", children: children)
} else if let arr = value as? [Any] {
let children = arr.enumerated().map { buildNode(key: "[\($0.offset)]", value: $0.element) }
return JSONNode(key: key, kind: .array, displayValue: "[\(arr.count)]", children: children)
} else if let str = value as? String {
return JSONNode(key: key, kind: .string, displayValue: "\"\(str)\"", children: [])
} else if let num = value as? NSNumber {
if CFBooleanGetTypeID() == CFGetTypeID(num) {
let boolVal = num.boolValue
return JSONNode(key: key, kind: .bool, displayValue: boolVal ? "true" : "false", children: [])
}
return JSONNode(key: key, kind: .number, displayValue: "\(num)", children: [])
} else if value is NSNull {
return JSONNode(key: key, kind: .null, displayValue: "null", children: [])
} else {
return JSONNode(key: key, kind: .string, displayValue: "\(value)", children: [])
}
}
}
// MARK: - Model
private struct JSONNode: Identifiable {
let id = UUID()
let key: String?
let kind: JSONValueKind
let displayValue: String
let children: [JSONNode]
var childCount: Int {
children.count + children.reduce(0) { $0 + $1.childCount }
}
}
private enum JSONValueKind {
case object, array, string, number, bool, null
}
// MARK: - Expandable wrapper (allows per-node expand state)
private struct JSONNodeExpandableView: View {
let node: JSONNode
let collapseByDefault: Bool
let childCollapseThreshold: Int
@State private var isExpanded: Bool
init(node: JSONNode, collapseByDefault: Bool, childCollapseThreshold: Int) {
self.node = node
self.collapseByDefault = collapseByDefault
self.childCollapseThreshold = childCollapseThreshold
_isExpanded = State(initialValue: !collapseByDefault)
}
var body: some View {
if node.children.isEmpty {
leafRow
} else {
DisclosureGroup(isExpanded: $isExpanded) {
ForEach(node.children) { child in
JSONNodeExpandableView(
node: child,
collapseByDefault: child.childCount > childCollapseThreshold,
childCollapseThreshold: childCollapseThreshold
)
}
} label: {
labelRow(valueColor: .secondary)
}
}
}
private var leafRow: some View {
labelRow(valueColor: valueColor)
}
private func labelRow(valueColor: Color) -> some View {
HStack(alignment: .firstTextBaseline, spacing: 4) {
if let key = node.key {
Text(key + ":")
.font(.system(.caption, design: .monospaced))
.bold()
.foregroundStyle(.primary)
}
Text(node.displayValue)
.font(.system(.caption, design: .monospaced))
.foregroundStyle(valueColor)
.textSelection(.enabled)
}
}
private var valueColor: Color {
switch node.kind {
case .string: return .green
case .number: return .blue
case .bool: return .orange
case .null: return .gray
case .object, .array: return .secondary
}
}
}