diff --git a/components/SessionSidebar.test.mjs b/components/SessionSidebar.test.mjs index 58856318a..381bc9c55 100644 --- a/components/SessionSidebar.test.mjs +++ b/components/SessionSidebar.test.mjs @@ -24,3 +24,10 @@ test("polls running sessions only while the tab is visible", () => { assert.match(source, /document\.visibilityState !== "visible"/); assert.match(source, /document\.addEventListener\("visibilitychange", onVisibilityChange\)/); }); + +test("does not persist an unchanged fallback title ending in whitespace", () => { + assert.match( + sessionItemSource, + /const name = renameValue\.trim\(\);[\s\S]*?if \(renameValue === title \|\| name === \(session\.name \?\? ""\)\) return;/, + ); +}); diff --git a/components/SessionSidebar.tsx b/components/SessionSidebar.tsx index 0514b55e5..fb0cbdf7f 100644 --- a/components/SessionSidebar.tsx +++ b/components/SessionSidebar.tsx @@ -1830,19 +1830,29 @@ function SessionItem({ const [deleting, setDeleting] = useState(false); const inputRef = useRef(null); + // Select the whole name once the rename input is mounted (startRename's + // immediate setTimeout can fire before the input exists). + useEffect(() => { + if (renaming) { + const id = requestAnimationFrame(() => inputRef.current?.select()); + return () => cancelAnimationFrame(id); + } + }, [renaming]); + const title = session.name || session.firstMessage.slice(0, 50) || session.id.slice(0, 12); const startRename = useCallback((e: React.MouseEvent) => { e.stopPropagation(); - setRenameValue(session.name ?? ""); + setRenameValue(session.name || session.firstMessage.slice(0, 50) || session.id.slice(0, 12)); setRenaming(true); - setTimeout(() => inputRef.current?.select(), 0); - }, [session.name]); + }, [session.name, session.firstMessage, session.id]); const commitRename = useCallback(async () => { const name = renameValue.trim(); setRenaming(false); - if (name === (session.name ?? "")) return; + // No-op when unchanged: the fallback title (first message / id) isn't a + // real stored name, so don't persist it as one. + if (renameValue === title || name === (session.name ?? "")) return; try { await fetch(`/api/sessions/${encodeURIComponent(session.id)}`, { method: "PATCH", @@ -1853,7 +1863,7 @@ function SessionItem({ } catch { // ignore } - }, [renameValue, session.id, session.name, onRenamed]); + }, [renameValue, session.id, session.name, onRenamed, title]); const performDelete = useCallback(async () => { setConfirmDelete(false);