This commit is contained in:
Ian Gulliver
2026-10-14 21:01:38 -07:00
parent 9e741ede32
commit b2bd894677
20 changed files with 585 additions and 343 deletions
+25 -9
View File
@@ -87,15 +87,32 @@ export function mountTranscript(root) {
}
const sess = currentSession(task.id);
if (task.status !== "active") {
replace(head, h("div", { class: "meta" }, h("code", {}, task.branch)), h("div", { class: "head-actions" }, task.status));
replace(head, h("div", { class: "head-actions" }, task.status));
return;
}
const target = state.projects.find((p) => p.id === task.project_id)?.default_branch ?? "the default branch";
const merge = h("button", { title: `Squash into one commit on ${target}; nothing is pushed` }, "Merge");
merge.onclick = () => closeTask(task, (u) => `Merge "${task.title}" into ${target}? ${changesText(u)} become one commit on ${target}. Nothing is pushed.`, "/api/tasks/{taskId}/merge");
const discard = h("button", { class: "danger", title: "Delete the worktree and branch without merging" }, "Discard");
discard.onclick = () => closeTask(task, (u) => `Discard "${task.title}"? ${changesText(u)} that aren't on ${target} will be deleted. This can't be undone.`, "/api/tasks/{taskId}/discard");
replace(head, h("div", { class: "meta" }, h("code", {}, task.branch)), h("div", { class: "head-actions" }, sess && contextMeter(context.get(sess.id)), sess && compactButton(sess.id, sess.state), merge, discard));
const busy = sess?.state === "running" || sess?.state === "queued";
const merge = h("button", { disabled: busy, title: `The agent merges ${target} in, then byre squashes into one commit on ${target}; nothing is pushed` }, "Merge");
merge.onclick = () => confirmChanges(task, (u) => `Merge "${task.title}" into ${target}? The agent first merges ${target} in and resolves any conflicts; when that turn ends, ${changesText(u)} become one commit on ${target} and the task closes. Nothing is pushed.`, async () => {
await post("/api/tasks/{taskId}/merge", { path: { taskId: task.id } });
});
const mergeIn = h("button", { disabled: busy, title: `Ask the agent to merge ${target} into this branch and resolve any conflicts` }, `Merge ${target} in`);
mergeIn.onclick = async () => {
mergeIn.disabled = true;
try {
await post("/api/tasks/{taskId}/merge-in", { path: { taskId: task.id } });
}
catch (err) {
alert(errorText(err));
mergeIn.disabled = false;
}
};
const discard = h("button", { class: "danger", disabled: busy, title: "Delete the worktree and branch without merging" }, "Discard");
discard.onclick = () => confirmChanges(task, (u) => `Discard "${task.title}"? ${changesText(u)} that aren't on ${target} will be deleted. This can't be undone.`, async () => {
await post("/api/tasks/{taskId}/discard", { path: { taskId: task.id } });
location.hash = "#/";
});
replace(head, h("div", { class: "head-actions session-actions" }, sess && contextMeter(context.get(sess.id)), sess && compactButton(sess.id, sess.state), mergeIn), h("div", { class: "head-actions" }, merge, discard));
};
let composerModel = "";
const renderComposer = () => {
@@ -610,14 +627,13 @@ function messageImages(ids) {
return h("a", { href: src, target: "_blank", rel: "noopener" }, h("img", { src, alt: "attached image", loading: "lazy" }));
}));
}
async function closeTask(task, prompt, path) {
async function confirmChanges(task, prompt, act) {
try {
const u = await get("/api/tasks/{taskId}/unmerged", { path: { taskId: task.id } });
if (!confirm(prompt(u))) {
return;
}
await post(path, { path: { taskId: task.id } });
location.hash = "#/";
await act();
}
catch (err) {
alert(errorText(err));