import SwiftUI struct FileView: View { @Environment(\.colorScheme) private var colorScheme @State private var model: FileViewModel init(client: GitbayClient, repo: String, path: String, ref: String?) { _model = State(initialValue: FileViewModel( client: client, repoPath: repo, filePath: path, ref: ref )) } var body: some View { Group { if let file = model.state.value { content(file) } else { Color.clear } } .overlay { LoadStateOverlay(state: model.state) } .navigationTitle(model.fileName) .navigationBarTitleDisplayMode(.inline) .task { await model.load() } } @ViewBuilder private func content(_ file: FileContent) -> some View { if file.binary { ContentUnavailableView { Label("Binary file", systemImage: "doc.zipper") } description: { Text("\(file.file) · \(Int64(file.size).formatted(.byteCount(style: .file)))") } } else if let text = file.content { VStack(spacing: 0) { if file.isTruncated { Label( "Truncated — showing the first \(Int64(file.size).formatted(.byteCount(style: .file))).", systemImage: "scissors" ) .font(.caption) .frame(maxWidth: .infinity, alignment: .leading) .padding(8) .background(.yellow.opacity(0.15)) } CodeScrollView(text: text, fileName: model.fileName, colorScheme: colorScheme) } } } } /// Both-axis scrolling for code, highlighted when the language is known. private struct CodeScrollView: View { let text: String let fileName: String let colorScheme: ColorScheme var body: some View { ScrollView([.horizontal, .vertical]) { code .padding(12) .frame(maxWidth: .infinity, alignment: .leading) } .font(.caption.monospaced()) } private var code: some View { let font = UIFont.monospacedSystemFont( ofSize: UIFont.preferredFont(forTextStyle: .caption1).pointSize, weight: .regular ) let highlighter = SyntaxHighlighter(colorScheme: colorScheme) if let highlighted = highlighter.highlight(text, fileName: fileName, font: font) { return Text(AttributedString(highlighted)) } return Text(text) } }