Edit the drafted merge message before confirming

This commit is contained in:
Ian Gulliver
2026-10-15 18:55:20 -07:00
parent de394b8c35
commit 5a07658523
16 changed files with 324 additions and 211 deletions
+20 -5
View File
@@ -59,9 +59,18 @@ export function ago(iso) {
export function errorText(err) {
return err instanceof Error ? err.message : String(err);
}
function modal(text, buttons, anchor = null) {
const actions = buttons.map((b) => h("button", { type: "submit", value: b.value, class: b.class ?? "" }, b.label));
const dialog = h("dialog", { class: "modal" }, h("form", { method: "dialog" }, h("p", { class: "modal-text" }, text), h("div", { class: "modal-actions" }, ...actions)));
function modal(content, buttons, anchor = null) {
const actions = buttons.map((b) => h("button", { type: "submit", value: b.value, class: b.class ?? "", formNoValidate: b.value === "cancel" }, b.label));
const body = typeof content === "string" ? [h("p", { class: "modal-text" }, content)] : content;
const dialog = h("dialog", { class: "modal" }, h("form", { method: "dialog" }, ...body, h("div", { class: "modal-actions" }, ...actions)));
for (const input of dialog.querySelectorAll("input")) {
input.onkeydown = (e) => {
if (e.key === "Enter") {
e.preventDefault();
actions.at(-1)?.click();
}
};
}
if (anchor) {
const rect = anchor.getBoundingClientRect();
dialog.classList.add("anchored");
@@ -75,6 +84,12 @@ function modal(text, buttons, anchor = null) {
resolve(dialog.returnValue);
};
dialog.showModal();
const input = dialog.querySelector("input");
if (input) {
input.focus();
input.select();
return;
}
actions.at(-1)?.focus();
});
}
@@ -102,8 +117,8 @@ export function openPanel(title, mount) {
export async function showAlert(text) {
await modal(text, [{ label: "OK", value: "ok", class: "primary" }]);
}
export async function showConfirm(text, action, danger = false, anchor = null) {
const value = await modal(text, [
export async function showConfirm(content, action, danger = false, anchor = null) {
const value = await modal(content, [
{ label: "Cancel", value: "cancel" },
{ label: action, value: "ok", class: danger ? "danger-primary" : "primary" },
], anchor);
+1 -1
View File
File diff suppressed because one or more lines are too long
+15 -6
View File
@@ -953,19 +953,28 @@ export async function confirmMerge(taskId) {
void showAlert(errorText(err));
return;
}
await confirmThen(mergeQuestion(task.merge_title, target, coAuthors), "Merge", false, document.querySelector(".merge-button"), async () => {
await post("/api/tasks/{taskId}/merge", { path: { taskId } });
const title = h("input", { type: "text", value: task.merge_title, required: true, maxLength: 72, pattern: ".*\\S.*", class: "modal-field", ariaLabel: "Commit title" });
const question = [h("p", { class: "modal-text" }, `Merge into ${target} as one commit titled:`), title];
const trailers = mergeTrailers(coAuthors);
if (trailers) {
question.push(h("p", { class: "modal-text" }, trailers));
}
await confirmThen(question, "Merge", false, document.querySelector(".merge-button"), async () => {
await post("/api/tasks/{taskId}/merge", { path: { taskId }, body: { merge_title: title.value } });
});
}
function mergeQuestion(title, target, coAuthors) {
const lines = [`Merge "${title}" into ${target}?`];
function mergeTrailers(coAuthors) {
const lines = [];
const credited = coAuthors.filter((p) => p.git_name && p.git_email);
const missing = coAuthors.filter((p) => !p.git_name || !p.git_email);
if (credited.length > 0) {
lines.push("", "Co-authored-by:", ...credited.map((p) => ` ${p.git_name} <${p.git_email}>`));
lines.push("Co-authored-by:", ...credited.map((p) => ` ${p.git_name} <${p.git_email}>`));
}
if (missing.length > 0) {
lines.push("", "No git identity, so no trailer:", ...missing.map((p) => ` ${p.display_name} (${p.login})`));
if (lines.length > 0) {
lines.push("");
}
lines.push("No git identity, so no trailer:", ...missing.map((p) => ` ${p.display_name} (${p.login})`));
}
return lines.join("\n");
}
+1 -1
View File
File diff suppressed because one or more lines are too long