import SwiftUI struct FileView: View { @Environment(\.colorScheme) private var colorScheme @State private var model: FileViewModel @State private var editing = false @State private var draft = "" 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) .toolbar { ToolbarItem(placement: .topBarTrailing) { if model.state.value?.binary == false { Menu { NavigationLink(value: RepoRoute.log( repo: model.repoPath, ref: model.ref, path: model.filePath )) { Label("History", systemImage: "clock") } NavigationLink(value: RepoRoute.blame( repo: model.repoPath, path: model.filePath, ref: model.ref )) { Label("Blame", systemImage: "person.crop.rectangle.stack") } if let branch = model.editableBranch { Button { draft = model.state.value?.content ?? "" editing = true } label: { Label("Edit on \(branch)", systemImage: "pencil") } } } label: { Image(systemName: "ellipsis.circle") } .accessibilityIdentifier("file-actions-menu") } } } .sheet(isPresented: $editing) { if let branch = model.editableBranch { FileEditSheet(model: model, branch: branch, content: $draft) { editing = false } } } .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 { GBNotice( "Truncated — showing the first \(Int64(file.size).formatted(.byteCount(style: .file))).", .gbWarn ) .padding(.horizontal, 12) .padding(.vertical, 6) } 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(.gbMono(.caption)) } private var code: some View { let size = UIFont.preferredFont(forTextStyle: .caption1).pointSize let font = UIFont(name: "Atkinson Hyperlegible Mono", size: size) ?? .monospacedSystemFont(ofSize: size, weight: .regular) let highlighter = SyntaxHighlighter(colorScheme: colorScheme) if let highlighted = highlighter.highlight(text, fileName: fileName, font: font) { return Text(AttributedString(highlighted)) } return Text(text) } }