aboutsummaryrefslogtreecommitdiff
path: root/Hutch/Views/Repositories/DiffView.swift
diff options
context:
space:
mode:
Diffstat (limited to 'Hutch/Views/Repositories/DiffView.swift')
-rw-r--r--Hutch/Views/Repositories/DiffView.swift79
1 files changed, 79 insertions, 0 deletions
diff --git a/Hutch/Views/Repositories/DiffView.swift b/Hutch/Views/Repositories/DiffView.swift
new file mode 100644
index 0000000..b1db464
--- /dev/null
+++ b/Hutch/Views/Repositories/DiffView.swift
@@ -0,0 +1,79 @@
+import SwiftUI
+
+/// Renders a unified diff string with syntax highlighting:
+/// - Green background for added lines (+)
+/// - Red background for removed lines (-)
+/// - Gray for hunk headers (@@)
+/// - File headers (--- / +++ / diff) in bold
+struct DiffView: View {
+ let diff: String
+
+ var body: some View {
+ let lines = diff.components(separatedBy: "\n")
+
+ LazyVStack(alignment: .leading, spacing: 0) {
+ ForEach(Array(lines.enumerated()), id: \.offset) { _, line in
+ DiffLineView(line: line)
+ }
+ }
+ .font(.caption.monospaced())
+ }
+}
+
+private struct DiffLineView: View {
+ let line: String
+
+ var body: some View {
+ Text(line.isEmpty ? " " : line)
+ .frame(maxWidth: .infinity, alignment: .leading)
+ .padding(.horizontal, 8)
+ .padding(.vertical, 1)
+ .background(backgroundColor)
+ .foregroundStyle(foregroundColor)
+ .fontWeight(isHeader ? .semibold : .regular)
+ }
+
+ private var kind: DiffLineKind {
+ if line.hasPrefix("@@") { return .hunk }
+ if line.hasPrefix("+++") || line.hasPrefix("---") { return .fileHeader }
+ if line.hasPrefix("diff ") { return .fileHeader }
+ if line.hasPrefix("index ") { return .meta }
+ if line.hasPrefix("+") { return .added }
+ if line.hasPrefix("-") { return .removed }
+ return .context
+ }
+
+ private var backgroundColor: Color {
+ switch kind {
+ case .added: .green.opacity(0.15)
+ case .removed: .red.opacity(0.15)
+ case .hunk: .gray.opacity(0.12)
+ case .fileHeader: .gray.opacity(0.08)
+ case .meta: .gray.opacity(0.05)
+ case .context: .clear
+ }
+ }
+
+ private var foregroundColor: Color {
+ switch kind {
+ case .added: .green
+ case .removed: .red
+ case .hunk: .secondary
+ case .meta: .secondary
+ default: .primary
+ }
+ }
+
+ private var isHeader: Bool {
+ kind == .fileHeader
+ }
+}
+
+private enum DiffLineKind {
+ case added
+ case removed
+ case hunk
+ case fileHeader
+ case meta
+ case context
+}