/** A sidebar link's inline name editor; the input sits beside the link, not inside it. */ export function conversationName( link: HTMLAnchorElement, label: HTMLElement, save: (title: string | null) => Promise, ) { const editor = document.createElement("input"); editor.className = "conversation-name"; editor.autocomplete = "off"; editor.setAttribute("aria-label", "Conversation name"); editor.hidden = true; let title: string | undefined; let fallback = ""; let initial = ""; let editing = false; let saving = false; let disposed = false; const finish = (focus: boolean) => { editing = false; editor.hidden = true; link.hidden = false; if (focus && !disposed) link.focus(); }; const start = (event?: Event) => { event?.preventDefault(); if (editing || disposed) return; editing = true; initial = title ?? fallback; editor.value = initial; editor.removeAttribute("aria-invalid"); editor.title = "save: Enter, cancel: Escape"; link.hidden = true; editor.hidden = false; editor.focus(); editor.select(); }; const submit = async (focus: boolean) => { if (!editing || saving || disposed) return; const next = editor.value.trim() || null; if (editor.value === initial || next === (title ?? null)) return finish(focus); const before = title; saving = true; editor.readOnly = true; try { await save(next); if (disposed) return; if (title === before) title = next ?? undefined; label.textContent = title ?? fallback; finish(focus); } catch (error) { if (disposed) return; editor.setAttribute("aria-invalid", "true"); editor.title = error instanceof Error ? error.message : String(error); if (focus) editor.focus(); } finally { saving = false; editor.readOnly = false; } }; let touch = false; link.addEventListener("pointerdown", (event) => { touch = event.pointerType === "touch"; }); link.addEventListener("dblclick", (event) => { if (!touch && !matchMedia("(pointer: coarse), (hover: none)").matches) start(event); }); link.addEventListener("keydown", (event) => { if (event.key === "F2") start(event); }); editor.addEventListener("keydown", (event) => { if (event.key !== "Enter" && event.key !== "Escape") return; if (event.isComposing) return; event.preventDefault(); if (saving) return; if (event.key === "Escape") finish(true); else void submit(true); }); editor.addEventListener("blur", () => void submit(false)); return { editor, start, update(next: string | undefined, preview: string) { title = next; fallback = preview; label.textContent = title ?? fallback; }, dispose() { disposed = true; }, }; }