johnandersen777.bsky.social/knotless
git that lives in your Atmosphere account
Forked from chadtmiller.com/knotless
0
0
import SwiftUI
struct TreeView: View {
let repo: RepoRef
let ref: String?
let path: String
var body: some View {
Loading(load: { [repo, ref, path] in
let tree: Tree = try await Client.shared.query(
"getTree", ["repo": repo.uri, "ref": ref, "path": path.isEmpty ? nil : path])
return tree.entries ?? []
}) { entries in
if entries.isEmpty {
ContentUnavailableView("No files here", systemImage: "folder")
} else {
List { ForEach(entries.sorted { ($0.kind == "tree" ? 0 : 1, $0.name) < ($1.kind == "tree" ? 0 : 1, $1.name) }) { entry in
let full = path.isEmpty ? entry.name : path + "/" + entry.name
NavigationLink(value: entry.kind == "tree"
? Route.tree(repo, ref: ref, path: full)
: Route.file(repo, ref: ref, path: full)) {
SwiftUI.Label {
Text(entry.name)
} icon: {
Oct(name: entry.kind == "tree" ? "file-directory-fill" : "file")
.foregroundStyle(entry.kind == "tree" ? Color.accent : Color.neutral)
}
}
}.listRowBackground(Color.page) }
.listStyle(.plain)
}
}
.navigationTitle(path.isEmpty ? repo.name : (path.split(separator: "/").last.map(String.init) ?? path))
.navigationBarTitleDisplayMode(.inline)
}
}
struct FileView: View {
let repo: RepoRef
let ref: String?
let path: String
var body: some View {
Loading(load: { [repo, ref, path] in
let file: FileAnswer = try await Client.shared.query(
"getFile", ["repo": repo.uri, "ref": ref, "path": path])
return file
}) { file in
if let text = file.text {
let lines = text.hasSuffix("\n") ? String(text.dropLast()).components(separatedBy: "\n") : text.components(separatedBy: "\n")
CodeLines(lines: lines, painted: Highlight.paint(lines: lines, ranges: file.ranges))
} else {
ContentUnavailableView("This file can't be shown here", systemImage: "doc")
}
}
.navigationTitle(path.split(separator: "/").last.map(String.init) ?? path)
.navigationBarTitleDisplayMode(.inline)
}
}
/// A file's lines, each on one row whatever its length. The code scrolls sideways
/// under numbers that stay put.
private struct CodeLines: View {
let lines: [String]
let painted: [AttributedString]
/// Every row the same height, so the two columns line up whatever a line holds.
@ScaledMetric(relativeTo: .caption) private var rowHeight = 17
var body: some View {
ScrollView {
HStack(alignment: .top, spacing: Space.s2) {
column(widest: "\(painted.count)", alignment: .trailing) { i in
Text("\(i + 1)").foregroundStyle(.secondary)
}
.padding(.leading, Space.s2)
ScrollView(.horizontal) {
column(widest: lines.max { $0.count < $1.count } ?? "", alignment: .leading) { i in
Text(painted[i])
}
.padding(.trailing, Space.s4)
}
}
.padding(.vertical, Space.s2)
}
}
/// Rows made as they come into view. The widest, drawn with no height, holds
/// the column at its full width from the start, so it doesn't jump on scrolling.
private func column(widest: String, alignment: HorizontalAlignment, @ViewBuilder row: @escaping (Int) -> Text) -> some View {
VStack(alignment: alignment, spacing: 0) {
Text(widest).frame(height: 0).hidden().accessibilityHidden(true)
LazyVStack(alignment: alignment, spacing: 0) {
ForEach(painted.indices, id: \.self) { i in
row(i).lineLimit(1).fixedSize().frame(height: rowHeight)
}
}
}
.font(.caption.monospaced())
.fixedSize(horizontal: true, vertical: false)
}
}