task description auto

This commit is contained in:
Ian Gulliver
2026-10-20 08:57:37 -07:00
parent 8f3226350e
commit d530ec5edc
20 changed files with 434 additions and 342 deletions
+42 -27
View File
@@ -4,7 +4,7 @@ import SwiftUI
typealias WorkflowSummary = Components.Schemas.WorkflowSummary
let goKeys: Set<String> = ["k", "p", "l", "s", "/"]
let goKeys: Set<String> = ["k", "n", "p", "l", "s", "/"]
@MainActor
func runGo(_ key: String, _ store: Store, _ overlays: Overlays) {
@@ -12,6 +12,11 @@ func runGo(_ key: String, _ store: Store, _ overlays: Overlays) {
switch key {
case "k":
overlays.openSwitcher(store)
case "n":
overlays.switcher?.close()
if let project = store.focusedProject() ?? store.projects.first {
createTask(project.id, .init(), store, overlays)
}
case "p":
store.focusTab(.project)
case "l":
@@ -31,6 +36,19 @@ func runGo(_ key: String, _ store: Store, _ overlays: Overlays) {
}
}
@MainActor
func createTask(_ projectId: String, _ body: Components.Schemas.TaskCreate, _ store: Store, _ overlays: Overlays) {
Task {
do {
let task = try await store.api.client.createTask(path: .init(projectId: projectId), body: .json(body)).created.body.json
store.composerFocus = task.id
store.focusTask(task.id, .transcript)
} catch {
overlays.showAlert(errorText(error))
}
}
}
@MainActor
func ctrlGo(_ key: String, _ store: Store, _ overlays: Overlays) -> Bool {
if terminalFocused() {
@@ -43,6 +61,7 @@ func ctrlGo(_ key: String, _ store: Store, _ overlays: Overlays) -> Bool {
private let shortcuts: [(String, [(String, String)])] = [
("Anywhere", [
("⌘K", "Switch task, or create one"),
("⌘N", "New task in the current project"),
("⌘P", "Open the Project tab"),
("⌘L", "Open the Lead tab and its composer"),
("⌘S", "Merge the right pane's task"),
@@ -53,7 +72,7 @@ private let shortcuts: [(String, [(String, String)])] = [
("Task switcher", [
("↑ ↓", "Move the highlight"),
("Enter", "Pick the highlighted row"),
("name: title", "Create the task in the project whose name starts with name"),
("name: message", "Start the task in the project whose name starts with name"),
("Esc", "Back from the workflow list to the task list"),
]),
("Composer and answers", [
@@ -101,14 +120,14 @@ private func includes(_ s: String, _ q: String) -> Bool {
q.isEmpty || s.contains(q)
}
private let switcherPlaceholder = "Go to a task, or type a title to create one…"
private let switcherPlaceholder = "Go to a task, or type a first message to start one…"
private let projectPrefix = Re(#"^([^:\s]+):\s*(.*)$"#)
struct SwitcherRow: Identifiable {
enum Kind {
case task(Project, ByreTask, Int)
case project(Project)
case newTask(Project, String)
case newTask(Project)
case startTask(Project, String)
case startWorkflow(Project, String)
case workflow(Project, String, WorkflowSummary)
@@ -199,12 +218,15 @@ final class SwitcherModel {
rows.append(SwitcherRow(id: "project:\(p.id)", kind: .project(p)))
}
let fallback = store.focusedProject() ?? store.projects.first
for project in title.isEmpty ? [] : targets ?? (fallback.map { [$0] } ?? []) {
rows.append(SwitcherRow(id: "new:\(project.id)", kind: .newTask(project, title)))
for project in targets ?? (fallback.map { [$0] } ?? []) {
if title.isEmpty {
rows.append(SwitcherRow(id: "new:\(project.id)", kind: .newTask(project)))
continue
}
rows.append(SwitcherRow(id: "go:\(project.id)", kind: .startTask(project, title)))
rows.append(SwitcherRow(id: "start:\(project.id)", kind: .startWorkflow(project, title)))
}
return (rows, "No active tasks. Type a title to create one.")
return (rows, "No active tasks. Type a first message to start one.")
}
func setText(_ value: String) {
@@ -232,10 +254,10 @@ final class SwitcherModel {
case .project(let p):
close()
store.focusProject(p.id)
case .newTask(let p, let title):
create(p.id, .init(title: title))
case .startTask(let p, let title):
create(p.id, .init(title: title, prompt: title, model: store.defaultModel))
case .newTask(let p):
create(p.id, .init())
case .startTask(let p, let message):
create(p.id, .init(prompt: message, model: store.defaultModel))
case .startWorkflow(let p, let title):
pickWorkflow(p, title)
case .workflow(let p, let title, let w):
@@ -271,14 +293,7 @@ final class SwitcherModel {
private func create(_ projectId: String, _ body: Components.Schemas.TaskCreate) {
close()
Task {
do {
let task = try await store.api.client.createTask(path: .init(projectId: projectId), body: .json(body)).created.body.json
go(task.id)
} catch {
overlays.showAlert(errorText(error))
}
}
createTask(projectId, body, store, overlays)
}
private func pickWorkflow(_ project: Project, _ title: String) {
@@ -420,7 +435,7 @@ private struct SwitcherRowView: View {
.font(Fonts.font(11, weight: 700))
.foregroundStyle(Palette.accent)
.help("A turn summary you haven't read")
.accessibilityLabel("New summary from \(task.title), unread")
.accessibilityLabel("New summary from \(task.name), unread")
} else {
Text(ts.label)
.font(Fonts.font(11))
@@ -435,22 +450,22 @@ private struct SwitcherRowView: View {
.truncationMode(.tail)
.frame(maxWidth: .infinity, alignment: .leading)
state("project")
case .newTask(let p, let title):
Text(created([("New task in ", nil), (p.name, Palette.muted), (": \(title)", nil)]))
case .newTask(let p):
Text(created([("New task in ", nil), (p.name, Palette.muted)]))
.foregroundStyle(Palette.accent)
.lineLimit(1)
.truncationMode(.tail)
.frame(maxWidth: .infinity, alignment: .leading)
state(store.defaultModel)
.help("Model for the first message")
case .startTask(let p, let title):
Text(created([("Start in ", nil), (p.name, Palette.muted), (": \(title)", nil)]))
case .startTask(let p, let message):
Text(created([("Start in ", nil), (p.name, Palette.muted), (": \(message)", nil)]))
.foregroundStyle(Palette.accent)
.lineLimit(1)
.truncationMode(.tail)
.frame(maxWidth: .infinity, alignment: .leading)
state(store.defaultModel)
.help("The title is the agent's first message")
.help("The agent's first message; the agent names the task")
case .startWorkflow(let p, let title):
Text(created([("Start workflow… in ", nil), (p.name, Palette.muted), (": \(title)", nil)]))
.foregroundStyle(Palette.accent)
@@ -486,9 +501,9 @@ private struct SwitcherRowView: View {
private func taskTitle(_ project: Project, _ task: ByreTask, _ color: Color) -> AttributedString {
var parts: [(String, Color?)] = [(project.name, Palette.muted), (" ", nil)]
if let parent = task.parentTaskId {
parts.append(("\(store.findTask(parent)?.title ?? parent) ↳ ", Palette.muted))
parts.append(("\(store.findTask(parent)?.name ?? parent) ↳ ", Palette.muted))
}
parts.append((task.title, color))
parts.append((task.name, color))
return created(parts)
}