Add a Files tab to the project page
This commit is contained in:
@@ -18,11 +18,14 @@ enum AppLink {
|
||||
return c.url!
|
||||
}
|
||||
|
||||
static func file(_ taskId: String, _ path: String) -> URL {
|
||||
static func file(_ root: FileRoot, _ path: String) -> URL {
|
||||
var c = URLComponents()
|
||||
c.scheme = "byre-app"
|
||||
c.host = "file"
|
||||
c.queryItems = [URLQueryItem(name: "task", value: taskId), URLQueryItem(name: "path", value: path)]
|
||||
c.queryItems = switch root {
|
||||
case .task(let id): [URLQueryItem(name: "task", value: id), URLQueryItem(name: "path", value: path)]
|
||||
case .project(let id): [URLQueryItem(name: "project", value: id), URLQueryItem(name: "path", value: path)]
|
||||
}
|
||||
return c.url!
|
||||
}
|
||||
|
||||
@@ -61,7 +64,11 @@ func openAppLink(_ url: URL, _ store: Store, _ overlays: Overlays) -> OpenURLAct
|
||||
case "route":
|
||||
store.route(q["hash"] ?? "")
|
||||
case "file":
|
||||
store.openFile(q["task"] ?? "", q["path"] ?? "")
|
||||
if let project = q["project"] {
|
||||
store.showInProjectFiles(project, q["path"] ?? "", overlays)
|
||||
} else {
|
||||
store.openFile(q["task"] ?? "", q["path"] ?? "")
|
||||
}
|
||||
case "preview":
|
||||
let taskId = q["task"] ?? ""
|
||||
store.route("#/task/\(taskId)")
|
||||
@@ -169,17 +176,23 @@ struct InlineRenderer {
|
||||
let store: Store
|
||||
let size: CGFloat
|
||||
let taskId: String?
|
||||
let project: String?
|
||||
let weight: CGFloat
|
||||
let dir: String
|
||||
private let root: String
|
||||
|
||||
init(store: Store, size: CGFloat, taskId: String?, weight: CGFloat = 400, dir: String = "") {
|
||||
init(store: Store, size: CGFloat, taskId: String?, project: String? = nil, weight: CGFloat = 400, dir: String = "") {
|
||||
self.store = store
|
||||
self.size = size
|
||||
self.taskId = taskId
|
||||
self.project = project
|
||||
self.weight = weight
|
||||
self.dir = dir
|
||||
root = taskId.flatMap { store.findTask($0)?.worktreePath } ?? ""
|
||||
if let taskId {
|
||||
root = store.findTask(taskId)?.worktreePath ?? ""
|
||||
} else {
|
||||
root = project.flatMap { id in store.projects.first { $0.id == id }?.repoPath } ?? ""
|
||||
}
|
||||
}
|
||||
|
||||
private struct Style {
|
||||
@@ -275,7 +288,7 @@ struct InlineRenderer {
|
||||
s.italic = true
|
||||
render(inner, s, &out)
|
||||
case .image(let src, let alt):
|
||||
if let source = worktreeImageSource(src, taskId, dir: dir) {
|
||||
if let source = worktreeImageSource(src, files, dir: dir) {
|
||||
out.append(.image(source, src: src))
|
||||
} else {
|
||||
link(src, [.text(alt.isEmpty ? src : alt)], style, &out)
|
||||
@@ -327,11 +340,15 @@ struct InlineRenderer {
|
||||
render(inner, style, &out)
|
||||
}
|
||||
|
||||
private var files: FileRoot? {
|
||||
taskId.map(FileRoot.task) ?? project.map(FileRoot.project)
|
||||
}
|
||||
|
||||
private func fileLink(_ path: String) -> URL? {
|
||||
guard let taskId, !root.isEmpty, let p = normalPath(path) else {
|
||||
guard let files, !root.isEmpty, let p = normalPath(path) else {
|
||||
return nil
|
||||
}
|
||||
return AppLink.file(taskId, p)
|
||||
return AppLink.file(files, p)
|
||||
}
|
||||
|
||||
private func codeFile(_ code: String) -> URL? {
|
||||
@@ -451,6 +468,7 @@ struct MDContext {
|
||||
var size: CGFloat = 13
|
||||
var color: Color = Palette.text
|
||||
var taskId: String?
|
||||
var project: String?
|
||||
var choice: ((MDItem, Bool) -> AnyView)?
|
||||
var discuss: ((MDItem) -> AnyView)?
|
||||
var gridColumns = 4
|
||||
@@ -590,7 +608,7 @@ private struct FlowBuilder {
|
||||
}
|
||||
|
||||
private func inline(_ inlines: [MDInline], _ size: CGFloat) -> NSAttributedString {
|
||||
let a = InlineRenderer(store: store, size: size, taskId: ctx.taskId, weight: ctx.weight, dir: ctx.dir).text(inlines)
|
||||
let a = InlineRenderer(store: store, size: size, taskId: ctx.taskId, project: ctx.project, weight: ctx.weight, dir: ctx.dir).text(inlines)
|
||||
return nativeString(a)
|
||||
}
|
||||
|
||||
@@ -902,7 +920,7 @@ struct InlineView: View {
|
||||
@Environment(\.centerImages) private var centerImages
|
||||
|
||||
var body: some View {
|
||||
let segs = InlineRenderer(store: store, size: ctx.size, taskId: ctx.taskId, weight: ctx.weight, dir: ctx.dir).segments(inlines)
|
||||
let segs = InlineRenderer(store: store, size: ctx.size, taskId: ctx.taskId, project: ctx.project, weight: ctx.weight, dir: ctx.dir).segments(inlines)
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
ForEach(Array(segs.enumerated()), id: \.offset) { _, seg in
|
||||
switch seg {
|
||||
@@ -948,7 +966,7 @@ private struct BlockView: View {
|
||||
case .paragraph(let inlines):
|
||||
InlineView(inlines: inlines, ctx: ctx)
|
||||
case .heading(let level, let inlines):
|
||||
InlineView(inlines: [.strong(inlines)], ctx: MDContext(size: level == 3 ? 14 : 13, color: ctx.color, taskId: ctx.taskId, dir: ctx.dir))
|
||||
InlineView(inlines: [.strong(inlines)], ctx: MDContext(size: level == 3 ? 14 : 13, color: ctx.color, taskId: ctx.taskId, project: ctx.project, dir: ctx.dir))
|
||||
.padding(.top, 4)
|
||||
case .rule:
|
||||
Rectangle()
|
||||
@@ -956,7 +974,7 @@ private struct BlockView: View {
|
||||
.frame(height: 1)
|
||||
.padding(.vertical, 2)
|
||||
case .quote(let blocks):
|
||||
MarkdownView(blocks: blocks, ctx: MDContext(size: ctx.size, color: Palette.muted, taskId: ctx.taskId, dir: ctx.dir))
|
||||
MarkdownView(blocks: blocks, ctx: MDContext(size: ctx.size, color: Palette.muted, taskId: ctx.taskId, project: ctx.project, dir: ctx.dir))
|
||||
.padding(.leading, 13)
|
||||
.overlay(alignment: .leading) {
|
||||
Rectangle().fill(Palette.border).frame(width: 3)
|
||||
@@ -1107,7 +1125,7 @@ private struct DetailsBlock: View {
|
||||
HStack(alignment: .firstTextBaseline, spacing: 4) {
|
||||
Text(open ? "▾" : "▸")
|
||||
.foregroundStyle(Palette.accent)
|
||||
InlineView(inlines: summary, ctx: MDContext(size: ctx.size, color: Palette.accent, taskId: ctx.taskId, dir: ctx.dir))
|
||||
InlineView(inlines: summary, ctx: MDContext(size: ctx.size, color: Palette.accent, taskId: ctx.taskId, project: ctx.project, dir: ctx.dir))
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user