- 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
99 lines
2.7 KiB
Swift
99 lines
2.7 KiB
Swift
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)
|
|
}
|
|
}
|