import SwiftUI import SaplingCore public struct SaplingInspectorView: View { private let project: Project? private let document: MarkdownDocument? private let statuses: [GitFileStatus] public init( project: Project?, document: MarkdownDocument?, statuses: [GitFileStatus] ) { self.project = project self.document = document self.statuses = statuses } public var body: some View { List { projectSection gitStatusSection outlineSection } .listStyle(.inset) .navigationTitle("Inspector") } @ViewBuilder private var projectSection: some View { Section("Project") { if let project { LabeledContent("Name", value: project.name) LabeledContent("Branch", value: project.gitRepository.currentBranch?.name ?? "Unknown") LabeledContent("Remotes", value: "\(project.remotes.count)") LabeledContent("LFS", value: project.usesGitLFS ? "Enabled" : "Disabled") LabeledContent("Subprojects", value: "\(project.subprojects.count)") } else { Text("No project selected") .foregroundStyle(.secondary) } } } private var gitStatusSection: some View { Section("Git Status") { if statuses.isEmpty { Label("Clean", systemImage: "checkmark.circle") .foregroundStyle(.green) } else { ForEach(statuses) { status in HStack { Image(systemName: iconName(for: status.state)) .foregroundStyle(color(for: status.state)) Text(status.path) .lineLimit(1) Spacer() Text(status.state.rawValue) .font(.caption) .foregroundStyle(.secondary) } } } } } private var outlineSection: some View { Section("Outline") { if let document { let headings = outlineHeadings(in: document.content) if headings.isEmpty { Text("No headings") .foregroundStyle(.secondary) } else { ForEach(headings, id: \.self) { heading in Text(heading) .lineLimit(1) } } } else { Text("No document selected") .foregroundStyle(.secondary) } } } private func outlineHeadings(in markdown: String) -> [String] { markdown .split(separator: "\n") .map(String.init) .compactMap { line in let trimmed = line.trimmingCharacters(in: .whitespaces) guard trimmed.hasPrefix("#") else { return nil } let hashes = trimmed.prefix { $0 == "#" }.count guard hashes <= 6, trimmed.dropFirst(hashes).first == " " else { return nil } return String(trimmed.dropFirst(hashes + 1)) } } private func iconName(for state: GitFileState) -> String { switch state { case .untracked: "questionmark.circle" case .added: "plus.circle" case .modified: "pencil.circle" case .deleted: "minus.circle" case .renamed: "arrow.triangle.2.circlepath" case .conflicted: "exclamationmark.triangle" } } private func color(for state: GitFileState) -> Color { switch state { case .untracked: .secondary case .added: .green case .modified: .orange case .deleted: .red case .renamed: .blue case .conflicted: .red } } }