Link worktree file paths in Markdown to the Files tab
This commit is contained in:
@@ -5,13 +5,26 @@ typealias DirListing = Components.Schemas.DirListing
|
||||
typealias DirEntry = Components.Schemas.DirEntry
|
||||
typealias FileContent = Components.Schemas.FileContent
|
||||
|
||||
private struct Place: Equatable {
|
||||
struct FilePlace: Equatable {
|
||||
var dir = ""
|
||||
var file: String?
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private var places: [String: Place] = [:]
|
||||
private struct FilesKey: Equatable {
|
||||
let taskId: String?
|
||||
let place: FilePlace
|
||||
}
|
||||
|
||||
extension Store {
|
||||
var filePlace: FilePlace {
|
||||
selectedTaskId.flatMap { filePlaces[$0] } ?? FilePlace()
|
||||
}
|
||||
|
||||
func openFile(_ taskId: String, _ path: String) {
|
||||
filePlaces[taskId] = path.hasSuffix("/") ? FilePlace(dir: String(path.dropLast())) : FilePlace(dir: parent(path), file: path)
|
||||
route("#/task/\(taskId)/files")
|
||||
}
|
||||
}
|
||||
|
||||
private func join(_ dir: String, _ name: String) -> String {
|
||||
dir.isEmpty ? name : "\(dir)/\(name)"
|
||||
@@ -50,22 +63,15 @@ private final class FilesModel {
|
||||
self.store = store
|
||||
}
|
||||
|
||||
var place: Place {
|
||||
store.selectedTaskId.flatMap { places[$0] } ?? Place()
|
||||
var place: FilePlace {
|
||||
store.filePlace
|
||||
}
|
||||
|
||||
func go(_ p: Place) {
|
||||
func go(_ p: FilePlace) {
|
||||
guard let taskId = store.selectedTaskId else {
|
||||
return
|
||||
}
|
||||
places[taskId] = p
|
||||
listing = nil
|
||||
content = nil
|
||||
error = ""
|
||||
generation += 1
|
||||
Task {
|
||||
await load()
|
||||
}
|
||||
store.filePlaces[taskId] = p
|
||||
}
|
||||
|
||||
func reset() {
|
||||
@@ -74,6 +80,7 @@ private final class FilesModel {
|
||||
listing = nil
|
||||
content = nil
|
||||
error = ""
|
||||
generation += 1
|
||||
Task {
|
||||
await load()
|
||||
}
|
||||
@@ -159,7 +166,7 @@ struct FilesView: View {
|
||||
}
|
||||
}
|
||||
.frame(maxHeight: .infinity, alignment: .top)
|
||||
.task(id: store.selectedTaskId) {
|
||||
.task(id: FilesKey(taskId: store.selectedTaskId, place: store.filePlace)) {
|
||||
let m = model ?? FilesModel(store)
|
||||
model = m
|
||||
m.reset()
|
||||
@@ -204,7 +211,7 @@ private struct FilesHead: View {
|
||||
Text(label).foregroundStyle(Palette.text)
|
||||
} else {
|
||||
Button(label) {
|
||||
model.go(Place(dir: dir))
|
||||
model.go(FilePlace(dir: dir))
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.foregroundStyle(Palette.accent)
|
||||
@@ -237,16 +244,16 @@ private struct FilesBody: View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
if !l.path.isEmpty {
|
||||
EntryRow(label: "../", kind: .dir, ignored: false, meta: "", symlink: false) {
|
||||
model.go(Place(dir: parent(l.path)))
|
||||
model.go(FilePlace(dir: parent(l.path)))
|
||||
}
|
||||
}
|
||||
ForEach(l.entries, id: \.name) { e in
|
||||
let meta = [e.ignored ? "ignored" : nil, e._type == .file ? bytes(e.size) : nil].compactMap { $0 }.joined(separator: " · ")
|
||||
EntryRow(label: e._type == .dir ? "\(e.name)/" : e.name, kind: e._type, ignored: e.ignored, meta: meta, symlink: e.symlink) {
|
||||
if e._type == .dir {
|
||||
model.go(Place(dir: join(l.path, e.name)))
|
||||
model.go(FilePlace(dir: join(l.path, e.name)))
|
||||
} else if e._type == .file {
|
||||
model.go(Place(dir: l.path, file: join(l.path, e.name)))
|
||||
model.go(FilePlace(dir: l.path, file: join(l.path, e.name)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user