feat: rename sessions inline via double-click (#1320)
* feat: rename sessions inline via double-click Double-clicking a session name in the sidebar or the mobile session status bar now switches it into an inline editable input. Enter saves, Esc cancels, and clicking elsewhere blurs the input to save. This mirrors the VSCode/Finder rename pattern and removes the need to open the session menu for what is a very common action. * fix(rename): close sidebar input on empty title; drop duplicate mobile editor Two issues from PR review: 1. handleSaveEdit (sidebar) only closed the input when editTitle.trim() was non-empty. Clearing the title and pressing Enter or blurring left the input open with no exit path other than Escape. The save handler now always closes the editor; an empty title is treated as a silent cancel (no update call). 2. ExpandedView (mobile) renders the current session twice — once in the sticky header, once in the session list — so a single editingSessionId produced two simultaneous inputs for the current session. The list row for the current session now suppresses its rename input; the header remains the single editor in that case. * fix: refine inline session rename editing --------- Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
committed by
GitHub
co-authored by
Bohdan Triapitsyn
parent
49bf1dbff3
commit
0c60fd6222
@@ -62,7 +62,7 @@ type Props = {
|
||||
handleCancelEdit: () => void;
|
||||
toggleParent: (expansionKey: string) => void;
|
||||
handleSessionSelect: (sessionId: string, sessionDirectory: string | null, isMissingDirectory: boolean, projectId?: string | null) => void;
|
||||
handleSessionDoubleClick: () => void;
|
||||
handleSessionDoubleClick: (sessionId: string, sessionTitle: string) => void;
|
||||
togglePinnedSession: (sessionId: string) => void;
|
||||
handleShareSession: (session: Session) => void;
|
||||
copiedSessionId: string | null;
|
||||
@@ -284,6 +284,7 @@ function SessionNodeItemComponent(props: Props): React.ReactNode {
|
||||
: (showQuickArchiveAction ? 'group-hover:pr-12 group-focus-within:pr-12' : 'group-hover:pr-5 group-focus-within:pr-5'));
|
||||
const alwaysActionPaddingClass = showQuickArchiveAction ? 'pr-13' : 'pr-7';
|
||||
const suppressNextSelectRef = React.useRef(false);
|
||||
const editCancelledRef = React.useRef(false);
|
||||
const [isTouchPressed, setIsTouchPressed] = React.useState(false);
|
||||
|
||||
const session = node.session;
|
||||
@@ -470,6 +471,7 @@ function SessionNodeItemComponent(props: Props): React.ReactNode {
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Escape') {
|
||||
event.stopPropagation();
|
||||
editCancelledRef.current = true;
|
||||
handleCancelEdit();
|
||||
return;
|
||||
}
|
||||
@@ -477,6 +479,13 @@ function SessionNodeItemComponent(props: Props): React.ReactNode {
|
||||
event.stopPropagation();
|
||||
}
|
||||
}}
|
||||
onBlur={() => {
|
||||
if (editCancelledRef.current) {
|
||||
editCancelledRef.current = false;
|
||||
return;
|
||||
}
|
||||
handleSaveEdit();
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
@@ -824,7 +833,7 @@ function SessionNodeItemComponent(props: Props): React.ReactNode {
|
||||
onClick={(event) => handleRowSelect(event)}
|
||||
onDoubleClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleSessionDoubleClick();
|
||||
handleSessionDoubleClick(session.id, sessionTitle);
|
||||
}}
|
||||
className={cn(
|
||||
'flex min-w-0 flex-1 cursor-pointer flex-col gap-0 overflow-hidden rounded-sm text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50 text-foreground select-none disabled:cursor-not-allowed transition-[padding]',
|
||||
@@ -888,7 +897,7 @@ function SessionNodeItemComponent(props: Props): React.ReactNode {
|
||||
onClick={(event) => handleRowSelect(event)}
|
||||
onDoubleClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleSessionDoubleClick();
|
||||
handleSessionDoubleClick(session.id, sessionTitle);
|
||||
}}
|
||||
className={cn(
|
||||
'flex min-w-0 flex-1 cursor-pointer flex-col gap-0 overflow-hidden rounded-sm text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50 text-foreground select-none disabled:cursor-not-allowed transition-[padding]',
|
||||
|
||||
@@ -98,16 +98,19 @@ export const useSessionActions = (args: Args) => {
|
||||
[args],
|
||||
);
|
||||
|
||||
const handleSessionDoubleClick = React.useCallback(() => {
|
||||
args.setActiveMainTab('chat');
|
||||
const handleSessionDoubleClick = React.useCallback((sessionId: string, sessionTitle: string) => {
|
||||
args.setEditingId(sessionId);
|
||||
args.setEditTitle(sessionTitle);
|
||||
}, [args]);
|
||||
|
||||
const handleSaveEdit = React.useCallback(async () => {
|
||||
if (args.editingId && args.editTitle.trim()) {
|
||||
await args.updateSessionTitle(args.editingId, args.editTitle.trim());
|
||||
args.setEditingId(null);
|
||||
args.setEditTitle('');
|
||||
if (!args.editingId) return;
|
||||
const trimmed = args.editTitle.trim();
|
||||
if (trimmed) {
|
||||
await args.updateSessionTitle(args.editingId, trimmed);
|
||||
}
|
||||
args.setEditingId(null);
|
||||
args.setEditTitle('');
|
||||
}, [args]);
|
||||
|
||||
const handleCancelEdit = React.useCallback(() => {
|
||||
|
||||
Reference in New Issue
Block a user