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:
98
UI/SharedComponents/HexView.swift
Normal file
98
UI/SharedComponents/HexView.swift
Normal file
@@ -0,0 +1,98 @@
|
||||
import SwiftUI
|
||||
|
||||
struct HexView: View {
|
||||
let data: Data
|
||||
|
||||
@State private var showAll = false
|
||||
|
||||
private var displayData: Data {
|
||||
if showAll || data.count <= 1024 {
|
||||
return data
|
||||
}
|
||||
return data.prefix(1024)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
// Header
|
||||
Text("Offset 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ASCII")
|
||||
.font(.system(.caption2, design: .monospaced))
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom, 4)
|
||||
|
||||
// Hex rows
|
||||
let bytes = [UInt8](displayData)
|
||||
let rowCount = (bytes.count + 15) / 16
|
||||
|
||||
ForEach(0..<rowCount, id: \.self) { row in
|
||||
hexRow(bytes: bytes, row: row)
|
||||
}
|
||||
|
||||
// Show more
|
||||
if !showAll && data.count > 1024 {
|
||||
Button {
|
||||
showAll = true
|
||||
} label: {
|
||||
Text("Show All (\(formatBytes(data.count)) total)")
|
||||
.font(.system(.caption, design: .monospaced))
|
||||
}
|
||||
.padding(.top, 8)
|
||||
}
|
||||
}
|
||||
.textSelection(.enabled)
|
||||
}
|
||||
|
||||
private func hexRow(bytes: [UInt8], row: Int) -> some View {
|
||||
let offset = row * 16
|
||||
let end = min(offset + 16, bytes.count)
|
||||
let rowBytes = Array(bytes[offset..<end])
|
||||
|
||||
return HStack(spacing: 0) {
|
||||
// Offset
|
||||
Text(String(format: "%08X ", offset))
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
// Hex bytes
|
||||
Text(hexString(rowBytes))
|
||||
|
||||
// ASCII
|
||||
Text(" ")
|
||||
Text(asciiString(rowBytes))
|
||||
.foregroundStyle(.orange)
|
||||
}
|
||||
.font(.system(.caption2, design: .monospaced))
|
||||
}
|
||||
|
||||
private func hexString(_ bytes: [UInt8]) -> String {
|
||||
var result = ""
|
||||
for i in 0..<16 {
|
||||
if i < bytes.count {
|
||||
result += String(format: "%02X ", bytes[i])
|
||||
} else {
|
||||
result += " "
|
||||
}
|
||||
if i == 7 {
|
||||
result += " "
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private func asciiString(_ bytes: [UInt8]) -> String {
|
||||
var result = ""
|
||||
for byte in bytes {
|
||||
if byte >= 0x20, byte <= 0x7E {
|
||||
result += String(UnicodeScalar(byte))
|
||||
} else {
|
||||
result += "."
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private func formatBytes(_ count: Int) -> String {
|
||||
if count < 1024 { return "\(count) B" }
|
||||
if count < 1_048_576 { return String(format: "%.1f KB", Double(count) / 1024) }
|
||||
return String(format: "%.1f MB", Double(count) / 1_048_576)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user