Add Start and Discuss buttons to lead suggestions
This commit is contained in:
Vendored
+9
@@ -300,6 +300,15 @@ function link(href, children) {
|
||||
return h("span", {}, ...children);
|
||||
}
|
||||
}
|
||||
const suggest = /^#\/suggest\/(\w+)\/([^/]+)\/(.+)$/.exec(href);
|
||||
if (suggest) {
|
||||
try {
|
||||
return todoLink(suggest[1], decodeURIComponent(suggest[2]), children, decodeURIComponent(suggest[3]));
|
||||
}
|
||||
catch {
|
||||
return h("span", {}, ...children);
|
||||
}
|
||||
}
|
||||
if (href.startsWith("#/")) {
|
||||
return h("a", { href }, ...children);
|
||||
}
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+32
-14
@@ -1,10 +1,14 @@
|
||||
import { post } from "./api.js";
|
||||
import { errorText, h, replace, showAlert } from "./dom.js";
|
||||
import { currentSession, focusTask, state } from "./state.js";
|
||||
import { currentLead, currentSession, focusTask, state } from "./state.js";
|
||||
import { taskStyle } from "./taskcolor.js";
|
||||
const starting = new Set();
|
||||
export function todoLink(projectId, title, children) {
|
||||
return h("span", { class: "todo-link", data: { project: projectId, title } }, h("span", { class: "todo-title" }, ...children), " ", h("span", { class: "todo-action" }));
|
||||
const busy = new Map();
|
||||
export function todoLink(projectId, title, children, brief) {
|
||||
const data = { project: projectId, title };
|
||||
if (brief !== undefined) {
|
||||
data["brief"] = brief;
|
||||
}
|
||||
return h("span", { class: "todo-link", data }, h("span", { class: "todo-title" }, ...children), " ", h("span", { class: "todo-action" }));
|
||||
}
|
||||
export function fillTodoLinks(root, model) {
|
||||
for (const el of root.querySelectorAll(".todo-link")) {
|
||||
@@ -14,9 +18,10 @@ export function fillTodoLinks(root, model) {
|
||||
function fill(el, model) {
|
||||
const projectId = el.dataset["project"];
|
||||
const title = el.dataset["title"];
|
||||
const brief = el.dataset["brief"];
|
||||
const action = el.querySelector(".todo-action");
|
||||
const titleEl = el.querySelector(".todo-title");
|
||||
const task = (state.tasks.get(projectId) ?? []).find((t) => t.status === "active" && t.todo_item === title);
|
||||
const task = (state.tasks.get(projectId) ?? []).find((t) => t.status === "active" && (brief === undefined ? t.todo_item === title : t.title === title));
|
||||
if (task) {
|
||||
const sess = currentSession(task.id);
|
||||
el.setAttribute("style", taskStyle(task.id));
|
||||
@@ -34,23 +39,36 @@ function fill(el, model) {
|
||||
el.removeAttribute("style");
|
||||
titleEl.classList.remove("task-name");
|
||||
const key = `${projectId}\n${title}`;
|
||||
const busy = starting.has(key);
|
||||
const start = async (discuss) => {
|
||||
starting.add(key);
|
||||
const label = busy.get(key);
|
||||
const run = async (busyLabel, act) => {
|
||||
busy.set(key, busyLabel);
|
||||
fill(el, model);
|
||||
try {
|
||||
await post("/api/projects/{projectId}/todo/tasks", { path: { projectId }, body: { title, model, discuss } });
|
||||
await act();
|
||||
}
|
||||
catch (err) {
|
||||
void showAlert(errorText(err));
|
||||
}
|
||||
starting.delete(key);
|
||||
busy.delete(key);
|
||||
fill(el, model);
|
||||
};
|
||||
const startButton = h("button", { disabled: busy, title: "Start a task that gets straight to work" }, busy ? "starting…" : "start");
|
||||
startButton.onclick = () => void start(false);
|
||||
const discussButton = h("button", { disabled: busy, title: "Start a task that discusses it with you in chat before building anything" }, "discuss");
|
||||
discussButton.onclick = () => void start(true);
|
||||
if (brief === undefined) {
|
||||
const start = (discuss) => run(discuss ? "discuss" : "start", () => post("/api/projects/{projectId}/todo/tasks", { path: { projectId }, body: { title, model, discuss } }));
|
||||
const startButton = h("button", { disabled: !!label, title: "Start a task that gets straight to work" }, label === "start" ? "starting…" : "start");
|
||||
startButton.onclick = () => void start(false);
|
||||
const discussButton = h("button", { disabled: !!label, title: "Start a task that discusses it with you in chat before building anything" }, label === "discuss" ? "starting…" : "discuss");
|
||||
discussButton.onclick = () => void start(true);
|
||||
replace(action, startButton, discussButton);
|
||||
return;
|
||||
}
|
||||
const startButton = h("button", { disabled: !!label, title: "Start a task with the lead's brief" }, label === "start" ? "starting…" : "start");
|
||||
startButton.onclick = () => void run("start", () => post("/api/projects/{projectId}/tasks", { path: { projectId }, body: { title, prompt: brief, model } }));
|
||||
const discussButton = h("button", { disabled: !!label, title: "Ask your lead to talk it over in chat" }, label === "discuss" ? "sending…" : "discuss");
|
||||
discussButton.onclick = () => void run("discuss", () => {
|
||||
const lead = currentLead();
|
||||
const live = lead !== undefined && lead.state !== "completed";
|
||||
return post("/api/me/lead/messages", { body: { text: `Let's discuss your suggestion "${title}" before starting it:\n\n${brief}`, model: live ? undefined : model } });
|
||||
});
|
||||
replace(action, startButton, discussButton);
|
||||
}
|
||||
//# sourceMappingURL=todolink.js.map
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"todolink.js","sourceRoot":"","sources":["../src/todolink.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"todolink.js","sourceRoot":"","sources":["../src/todolink.ts"],"names":[],"mappings":"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"}
|
||||
+5
-1
@@ -218,7 +218,7 @@ export interface paths {
|
||||
};
|
||||
get: operations["listTasks"];
|
||||
put?: never;
|
||||
/** @description Creates the task's branch and worktree from the project's default branch, or with workflow, starts a run of that workflow from it. */
|
||||
/** @description Creates the task's branch and worktree from the project's default branch, and with prompt starts its agent, or with workflow, starts a run of that workflow from it. */
|
||||
post: operations["createTask"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
@@ -1097,6 +1097,10 @@ export interface components {
|
||||
};
|
||||
TaskCreate: {
|
||||
title: string;
|
||||
/** @description Starts the task's agent with this as its first message, in the same request. Not with workflow. */
|
||||
prompt?: string;
|
||||
/** @description The agent's model when prompt is given. */
|
||||
model?: string;
|
||||
/** @description The name of a saved workflow to run instead of creating one task with no agent: each stage with no after stages becomes its own task, titled "<title>: <stage>", with the title as the run's brief. The response is the first of those tasks. */
|
||||
workflow?: string;
|
||||
};
|
||||
|
||||
@@ -339,6 +339,14 @@ function link(href: string, children: Node[]): Node {
|
||||
return h("span", {}, ...children);
|
||||
}
|
||||
}
|
||||
const suggest = /^#\/suggest\/(\w+)\/([^/]+)\/(.+)$/.exec(href);
|
||||
if (suggest) {
|
||||
try {
|
||||
return todoLink(suggest[1]!, decodeURIComponent(suggest[2]!), children, decodeURIComponent(suggest[3]!));
|
||||
} catch {
|
||||
return h("span", {}, ...children);
|
||||
}
|
||||
}
|
||||
if (href.startsWith("#/")) {
|
||||
return h("a", { href }, ...children);
|
||||
}
|
||||
|
||||
+36
-14
@@ -1,12 +1,16 @@
|
||||
import { post } from "./api.js";
|
||||
import { errorText, h, replace, showAlert } from "./dom.js";
|
||||
import { currentSession, focusTask, state } from "./state.js";
|
||||
import { currentLead, currentSession, focusTask, state } from "./state.js";
|
||||
import { taskStyle } from "./taskcolor.js";
|
||||
|
||||
const starting = new Set<string>();
|
||||
const busy = new Map<string, string>();
|
||||
|
||||
export function todoLink(projectId: string, title: string, children: Node[]): HTMLElement {
|
||||
return h("span", { class: "todo-link", data: { project: projectId, title } }, h("span", { class: "todo-title" }, ...children), " ", h("span", { class: "todo-action" }));
|
||||
export function todoLink(projectId: string, title: string, children: Node[], brief?: string): HTMLElement {
|
||||
const data: Record<string, string> = { project: projectId, title };
|
||||
if (brief !== undefined) {
|
||||
data["brief"] = brief;
|
||||
}
|
||||
return h("span", { class: "todo-link", data }, h("span", { class: "todo-title" }, ...children), " ", h("span", { class: "todo-action" }));
|
||||
}
|
||||
|
||||
export function fillTodoLinks(root: ParentNode, model: string): void {
|
||||
@@ -18,9 +22,10 @@ export function fillTodoLinks(root: ParentNode, model: string): void {
|
||||
function fill(el: HTMLElement, model: string): void {
|
||||
const projectId = el.dataset["project"]!;
|
||||
const title = el.dataset["title"]!;
|
||||
const brief = el.dataset["brief"];
|
||||
const action = el.querySelector<HTMLElement>(".todo-action")!;
|
||||
const titleEl = el.querySelector<HTMLElement>(".todo-title")!;
|
||||
const task = (state.tasks.get(projectId) ?? []).find((t) => t.status === "active" && t.todo_item === title);
|
||||
const task = (state.tasks.get(projectId) ?? []).find((t) => t.status === "active" && (brief === undefined ? t.todo_item === title : t.title === title));
|
||||
|
||||
if (task) {
|
||||
const sess = currentSession(task.id);
|
||||
@@ -48,21 +53,38 @@ function fill(el: HTMLElement, model: string): void {
|
||||
el.removeAttribute("style");
|
||||
titleEl.classList.remove("task-name");
|
||||
const key = `${projectId}\n${title}`;
|
||||
const busy = starting.has(key);
|
||||
const start = async (discuss: boolean) => {
|
||||
starting.add(key);
|
||||
const label = busy.get(key);
|
||||
const run = async (busyLabel: string, act: () => Promise<unknown>) => {
|
||||
busy.set(key, busyLabel);
|
||||
fill(el, model);
|
||||
try {
|
||||
await post("/api/projects/{projectId}/todo/tasks", { path: { projectId }, body: { title, model, discuss } });
|
||||
await act();
|
||||
} catch (err) {
|
||||
void showAlert(errorText(err));
|
||||
}
|
||||
starting.delete(key);
|
||||
busy.delete(key);
|
||||
fill(el, model);
|
||||
};
|
||||
const startButton = h("button", { disabled: busy, title: "Start a task that gets straight to work" }, busy ? "starting…" : "start");
|
||||
startButton.onclick = () => void start(false);
|
||||
const discussButton = h("button", { disabled: busy, title: "Start a task that discusses it with you in chat before building anything" }, "discuss");
|
||||
discussButton.onclick = () => void start(true);
|
||||
|
||||
if (brief === undefined) {
|
||||
const start = (discuss: boolean) => run(discuss ? "discuss" : "start", () => post("/api/projects/{projectId}/todo/tasks", { path: { projectId }, body: { title, model, discuss } }));
|
||||
const startButton = h("button", { disabled: !!label, title: "Start a task that gets straight to work" }, label === "start" ? "starting…" : "start");
|
||||
startButton.onclick = () => void start(false);
|
||||
const discussButton = h("button", { disabled: !!label, title: "Start a task that discusses it with you in chat before building anything" }, label === "discuss" ? "starting…" : "discuss");
|
||||
discussButton.onclick = () => void start(true);
|
||||
replace(action, startButton, discussButton);
|
||||
return;
|
||||
}
|
||||
|
||||
const startButton = h("button", { disabled: !!label, title: "Start a task with the lead's brief" }, label === "start" ? "starting…" : "start");
|
||||
startButton.onclick = () =>
|
||||
void run("start", () => post("/api/projects/{projectId}/tasks", { path: { projectId }, body: { title, prompt: brief, model } }));
|
||||
const discussButton = h("button", { disabled: !!label, title: "Ask your lead to talk it over in chat" }, label === "discuss" ? "sending…" : "discuss");
|
||||
discussButton.onclick = () =>
|
||||
void run("discuss", () => {
|
||||
const lead = currentLead();
|
||||
const live = lead !== undefined && lead.state !== "completed";
|
||||
return post("/api/me/lead/messages", { body: { text: `Let's discuss your suggestion "${title}" before starting it:\n\n${brief}`, model: live ? undefined : model } });
|
||||
});
|
||||
replace(action, startButton, discussButton);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user