From 17e39d3f3d49c88e626243d1e9b61b313fd91b6b Mon Sep 17 00:00:00 2001 From: ZHANGFEI23 Date: Thu, 4 Jun 2026 16:42:19 +0800 Subject: [PATCH] feat: session name can be edited now --- src/session.ts | 21 +++++++ src/ui/views/App.tsx | 8 +++ src/ui/views/SessionList.tsx | 109 ++++++++++++++++++++++++++++++++--- 3 files changed, 131 insertions(+), 7 deletions(-) diff --git a/src/session.ts b/src/session.ts index f12b91f2..d37b6ef6 100644 --- a/src/session.ts +++ b/src/session.ts @@ -1671,6 +1671,27 @@ ${skillMd} return true; } + /** + * Rename a session by updating its summary (display title). + * Returns true if the session was found and renamed, false otherwise. + */ + renameSession(sessionId: string, summary: string): boolean { + const trimmed = summary.trim(); + if (!trimmed) { + return false; + } + const entry = this.getSession(sessionId); + if (!entry) { + return false; + } + this.updateSessionEntry(sessionId, (existing) => ({ + ...existing, + summary: trimmed, + updateTime: new Date().toISOString(), + })); + return true; + } + listSessionMessages(sessionId: string): SessionMessage[] { const messagePath = this.getSessionMessagesPath(sessionId); if (!fs.existsSync(messagePath)) { diff --git a/src/ui/views/App.tsx b/src/ui/views/App.tsx index f07da6f5..1579848f 100644 --- a/src/ui/views/App.tsx +++ b/src/ui/views/App.tsx @@ -751,6 +751,14 @@ function App({ projectRoot, initialPrompt, onRestart }: AppProps): React.ReactEl onDelete={(id) => { void handleDeleteSession(id); }} + onRename={(id, newName) => { + if (sessionManager.renameSession(id, newName)) { + refreshSessionsList(); + setStatusLine(`Session renamed to "${newName}".`); + } else { + setErrorLine("Failed to rename session."); + } + }} /> ) : view === "undo" ? ( void; onCancel: () => void; onDelete?: (sessionId: string) => void; + onRename?: (sessionId: string, newName: string) => void; }; /** @@ -38,10 +39,13 @@ export function filterSessions(sessions: SessionEntry[], query: string): Session }); } -export function SessionList({ sessions, onSelect, onCancel, onDelete }: Props): React.ReactElement { +export function SessionList({ sessions, onSelect, onCancel, onDelete, onRename }: Props): React.ReactElement { const [index, setIndex] = useState(0); const [searchQuery, setSearchQuery] = useState(""); const [confirmDeleteSessionId, setConfirmDeleteSessionId] = useState(null); + const [renameSessionId, setRenameSessionId] = useState(null); + const [renameValue, setRenameValue] = useState(""); + const [renameCursor, setRenameCursor] = useState(0); const { columns, rows } = useWindowSize(); // Filter sessions by search query @@ -83,6 +87,65 @@ export function SessionList({ sessions, onSelect, onCancel, onDelete }: Props): const selectedSession = filteredSessions[safeIndex]; useInput((input, key) => { + // If in rename mode, handle rename editing + if (renameSessionId) { + if (key.return) { + if (renameValue.trim()) { + onRename?.(renameSessionId, renameValue.trim()); + } + setRenameSessionId(null); + setRenameValue(""); + setRenameCursor(0); + return; + } + if (key.escape) { + setRenameSessionId(null); + setRenameValue(""); + setRenameCursor(0); + return; + } + if (key.leftArrow) { + setRenameCursor((c) => Math.max(0, c - 1)); + return; + } + if (key.rightArrow) { + setRenameCursor((c) => Math.min(renameValue.length, c + 1)); + return; + } + if (key.home) { + setRenameCursor(0); + return; + } + if (key.end) { + setRenameCursor(renameValue.length); + return; + } + if (key.delete) { + if (renameCursor < renameValue.length) { + setRenameValue((prev) => prev.slice(0, renameCursor) + prev.slice(renameCursor + 1)); + // cursor stays at same position (next char shifts left) + } + return; + } + if (key.backspace) { + if (renameCursor > 0) { + setRenameValue((prev) => prev.slice(0, renameCursor - 1) + prev.slice(renameCursor)); + setRenameCursor((c) => c - 1); + } + return; + } + // Printable character: insert at cursor position + if (input && input.length > 0 && !key.meta && !key.ctrl && !key.tab) { + if (key.upArrow || key.downArrow) { + return; + } + setRenameValue((prev) => prev.slice(0, renameCursor) + input + prev.slice(renameCursor)); + setRenameCursor((c) => c + input.length); + return; + } + return; + } + // If in delete confirmation mode, handle confirm/cancel if (confirmDeleteSessionId) { if (key.return) { @@ -114,6 +177,17 @@ export function SessionList({ sessions, onSelect, onCancel, onDelete }: Props): return; } + // Ctrl+R: start rename on selected session + if (key.ctrl && (input === "r" || input === "R")) { + if (selectedSession && onRename) { + const name = selectedSession.summary || ""; + setRenameSessionId(selectedSession.id); + setRenameValue(name); + setRenameCursor(name.length); + return; + } + } + // Delete key: remove search character, or start delete confirmation if (key.delete || key.backspace) { if (searchQuery) { @@ -237,6 +311,7 @@ export function SessionList({ sessions, onSelect, onCancel, onDelete }: Props): const actualIndex = scrollOffset + i; const isSelected = actualIndex === safeIndex; const isConfirming = confirmDeleteSessionId === session.id; + const isRenaming = renameSessionId === session.id; return ( @@ -244,12 +319,20 @@ export function SessionList({ sessions, onSelect, onCancel, onDelete }: Props): - - {formatSessionTitle(session.summary || "Untitled")} - + {isRenaming ? ( + + Rename: {renameValue.slice(0, renameCursor)} + | + {renameValue.slice(renameCursor)} + + ) : ( + + {formatSessionTitle(session.summary || "Untitled")} + + )} {isConfirming ? ( [Delete? Enter=yes, Esc=no] - ) : ( + ) : isRenaming ? null : ( ({formatSessionStatus(session.status)}) )} @@ -272,7 +355,19 @@ export function SessionList({ sessions, onSelect, onCancel, onDelete }: Props): {/* Footer */} - {confirmDeleteSessionId ? ( + {renameSessionId ? ( + + Input new session name, + + Enter + + to save · + + Esc + + to cancel + + ) : confirmDeleteSessionId ? ( Delete this session? @@ -292,7 +387,7 @@ export function SessionList({ sessions, onSelect, onCancel, onDelete }: Props): ) : ( - Type to search · ↑/↓ navigate · PgUp/PgDn page · Enter select · Esc cancel · Del delete + Type to search · ↑/↓ navigate · PgUp/PgDn page · Enter select · Esc cancel · Del delete · Ctrl+r rename )}