Compact run stage cards
This commit is contained in:
@@ -54,6 +54,15 @@ private func stageLabel(_ s: RunStage) -> String {
|
||||
return "waiting to start"
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private func taskLink(_ taskId: String, _ label: String, bold: Bool = false) -> AttributedString {
|
||||
var a = AttributedString(label)
|
||||
a.link = AppLink.task(taskId)
|
||||
a.foregroundColor = Palette.accent
|
||||
a.font = Fonts.font(13, weight: bold ? 700 : 400)
|
||||
return a
|
||||
}
|
||||
|
||||
private func stateColor(_ state: String) -> Color {
|
||||
switch state {
|
||||
case "running": Palette.accent
|
||||
@@ -195,20 +204,37 @@ private struct StageCard: View {
|
||||
let stage: RunStage
|
||||
|
||||
var body: some View {
|
||||
let only = stage.attempts.count == 1 ? stage.attempts[0] : nil
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
HStack(alignment: .firstTextBaseline, spacing: 10) {
|
||||
Text(stage.name).font(Fonts.font(13, weight: 700))
|
||||
if let only {
|
||||
Text(taskLink(only.taskId, stage.name, bold: true))
|
||||
} else {
|
||||
Text(stage.name).font(Fonts.font(13, weight: 700))
|
||||
}
|
||||
Text(stageLabel(stage))
|
||||
.font(Fonts.font(11))
|
||||
.foregroundStyle(stateColor(stage.state.rawValue))
|
||||
if only?.discarding == true {
|
||||
Text("discarding…").font(Fonts.font(11)).foregroundStyle(Palette.muted)
|
||||
}
|
||||
}
|
||||
Text(stage.after.isEmpty ? "starts from the brief" : "after \(stage.after.map { "“\($0)”" }.joined(separator: ", "))")
|
||||
.font(Fonts.font(11))
|
||||
.foregroundStyle(Palette.muted)
|
||||
if !stage.attempts.isEmpty {
|
||||
if let only, only.title.caseInsensitiveCompare(stage.name) != .orderedSame {
|
||||
Text("task “\(only.title)”")
|
||||
.font(Fonts.font(11))
|
||||
.foregroundStyle(Palette.muted)
|
||||
}
|
||||
if !stage.after.isEmpty {
|
||||
Text("after \(stage.after.map { "“\($0)”" }.joined(separator: ", "))")
|
||||
.font(Fonts.font(11))
|
||||
.foregroundStyle(Palette.muted)
|
||||
}
|
||||
if let only {
|
||||
AttemptRow(run: run, stage: stage, attempt: only, index: 0, solo: true)
|
||||
} else if !stage.attempts.isEmpty {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
ForEach(Array(stage.attempts.enumerated()), id: \.offset) { i, a in
|
||||
AttemptRow(run: run, stage: stage, attempt: a, index: i)
|
||||
AttemptRow(run: run, stage: stage, attempt: a, index: i, solo: false)
|
||||
}
|
||||
}
|
||||
.padding(.top, 8)
|
||||
@@ -227,22 +253,23 @@ private struct AttemptRow: View {
|
||||
let stage: RunStage
|
||||
let attempt: StageAttempt
|
||||
let index: Int
|
||||
let solo: Bool
|
||||
@Environment(Store.self) private var store
|
||||
|
||||
var body: some View {
|
||||
let a = attempt
|
||||
let fed = run.stages.flatMap { s in s.attempts.filter { d in d.inputs.contains { $0.taskId == a.taskId } }.map(\.taskId) }
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
HStack(alignment: .firstTextBaseline, spacing: 10) {
|
||||
if stage.attempts.count > 1 {
|
||||
if !solo {
|
||||
HStack(alignment: .firstTextBaseline, spacing: 10) {
|
||||
Text("#\(index + 1)").foregroundStyle(Palette.muted)
|
||||
}
|
||||
Text(link(a.taskId, a.title, bold: a.taskId == store.selectedTaskId))
|
||||
Text(attemptLabel(a))
|
||||
.font(Fonts.font(11))
|
||||
.foregroundStyle(stateColor(a.state.rawValue))
|
||||
if a.discarding {
|
||||
Text("discarding…").font(Fonts.font(11)).foregroundStyle(Palette.muted)
|
||||
Text(taskLink(a.taskId, a.title, bold: a.taskId == store.selectedTaskId))
|
||||
Text(attemptLabel(a))
|
||||
.font(Fonts.font(11))
|
||||
.foregroundStyle(stateColor(a.state.rawValue))
|
||||
if a.discarding {
|
||||
Text("discarding…").font(Fonts.font(11)).foregroundStyle(Palette.muted)
|
||||
}
|
||||
}
|
||||
}
|
||||
if let from = a.forkedFrom {
|
||||
@@ -273,9 +300,11 @@ private struct AttemptRow: View {
|
||||
.padding(.top, 6)
|
||||
}
|
||||
}
|
||||
.padding(.top, 8)
|
||||
.padding(.top, solo ? 0 : 8)
|
||||
.overlay(alignment: .top) {
|
||||
Rectangle().fill(Palette.border).frame(height: 1)
|
||||
if !solo {
|
||||
Rectangle().fill(Palette.border).frame(height: 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,19 +328,11 @@ private struct AttemptRow: View {
|
||||
return store.findTask(taskId)?.title ?? taskId
|
||||
}
|
||||
|
||||
private func link(_ taskId: String, _ label: String, bold: Bool = false) -> AttributedString {
|
||||
var a = AttributedString(label)
|
||||
a.link = AppLink.task(taskId)
|
||||
a.foregroundColor = Palette.accent
|
||||
a.font = Fonts.font(13, weight: bold ? 700 : 400)
|
||||
return a
|
||||
}
|
||||
|
||||
private func line(_ parts: [(String, String?)]) -> some View {
|
||||
var out = AttributedString()
|
||||
for (text, taskId) in parts {
|
||||
if let taskId {
|
||||
var a = link(taskId, text)
|
||||
var a = taskLink(taskId, text)
|
||||
a.font = Fonts.font(11)
|
||||
out += a
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user