feat: titlebar improvements#2147
Conversation
Greptile SummaryThis PR makes three related titlebar improvements: it adds an animated diff-stat badge (lines added/deleted) to the Changes toggle button, reorders the Files and Conversations sidebar buttons (swapping their icons and hotkeys to match), and introduces inline tab-rename via a right-click context menu on conversation tabs.
Confidence Score: 3/5The titlebar visual changes and shortcut reorder are safe to ship, but the inline rename input has two event-ordering bugs that need fixing before the rename feature works correctly. The diff-stat badge and button reorder are straightforward UI changes. The rename flow has an issue where pressing Escape still saves the edit (blur fires on unmount after setIsEditing(false)) and pressing Enter causes the rename RPC to fire twice (onKeyDown commit + blur-on-unmount). Both affect the primary user action of the new feature. src/renderer/features/tasks/view/tab-bar/conversation-tab-item.tsx — the onBlur/onKeyDown interaction in the rename input needs a guard to prevent double-saves and Escape from saving.
|
| Filename | Overview |
|---|---|
| src/renderer/features/tasks/view/tab-bar/conversation-tab-item.tsx | Adds inline rename editing via context menu; two logic bugs: Escape bypasses commitRename but blur still fires and saves the edit, and pressing Enter double-fires onRenameSubmit via keydown + blur-on-unmount. |
| src/renderer/features/tasks/view/tab-bar.tsx | Wires useConversations into makeTabRenderers to pass onRenameSubmit down to ConversationTabItem; straightforward and correct. |
| src/renderer/features/tasks/task-titlebar.tsx | Adds animated diff-stat badge to the Changes toggle button and reorders/re-icons the Files/Conversations sidebar buttons; unused MessageSquare import left behind. |
| src/shared/shortcuts.ts | Swaps the default hotkeys for sidebarConversations (Mod+Shift+3) and sidebarFiles (Mod+Shift+2) to match the new visual order of buttons in the titlebar. |
Sequence Diagram
sequenceDiagram
participant User
participant ConversationTabItem
participant TabBar
participant ConversationManagerStore
User->>ConversationTabItem: Right-click tab
ConversationTabItem->>ConversationTabItem: Open ContextMenu
User->>ConversationTabItem: Click "Rename"
ConversationTabItem->>ConversationTabItem: "handleRename() — set pendingRenameRef=true"
ConversationTabItem->>ConversationTabItem: onOpenChangeComplete(false) — setIsEditing(true)
ConversationTabItem->>ConversationTabItem: Render inline input
User->>ConversationTabItem: Type new name + press Enter
ConversationTabItem->>ConversationTabItem: onKeyDown → commitRename(value)
ConversationTabItem->>TabBar: onRenameSubmit(name)
TabBar->>ConversationManagerStore: renameConversation(id, name)
ConversationManagerStore->>ConversationManagerStore: Optimistic update (runInAction)
ConversationManagerStore-->>TabBar: await rpc.conversations.renameConversation
ConversationTabItem->>ConversationTabItem: setIsEditing(false) → unmounts input
Note over ConversationTabItem: onBlur fires from unmount → commitRename called again
ConversationTabItem->>TabBar: onRenameSubmit(name) [duplicate]
TabBar->>ConversationManagerStore: renameConversation(id, name) [duplicate]
Comments Outside Diff (1)
-
src/renderer/features/tasks/task-titlebar.tsx, line 2-13 (link)MessageSquareis imported but no longer used — the Conversations toggle was switched to theClockicon in this PR.Prompt To Fix With AI
This is a comment left during a code review. Path: src/renderer/features/tasks/task-titlebar.tsx Line: 2-13 Comment: `MessageSquare` is imported but no longer used — the Conversations toggle was switched to the `Clock` icon in this PR. How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 3
src/renderer/features/tasks/view/tab-bar/conversation-tab-item.tsx:79-82
**Escape cancels but blur still commits the rename**
When the user presses Escape, `setIsEditing(false)` is called directly — not through `commitRename`. React then unmounts the focused `<input>`, which fires `onBlur`. Because `onBlur` unconditionally calls `commitRename(e.target.value)`, any edits the user typed before pressing Escape will still be saved. A simple fix is to track whether Escape was used with a ref and bail out in `commitRename`.
### Issue 2 of 3
src/renderer/features/tasks/view/tab-bar/conversation-tab-item.tsx:78-80
**Double-fire of `onRenameSubmit` on Enter**
Pressing Enter calls `commitRename` (via `onKeyDown`), which calls `onRenameSubmit` and schedules `setIsEditing(false)`. When React re-renders and unmounts the focused input, `onBlur` fires and calls `commitRename` a second time. Because `rawTitle` in the closure still holds the old title (the render that created this `commitRename`), the `trimmed !== rawTitle` check passes and `renameConversation` is called twice on the network. A guard ref set to `true` in `commitRename` and checked at the top of the function prevents the second invocation.
### Issue 3 of 3
src/renderer/features/tasks/task-titlebar.tsx:2-13
`MessageSquare` is imported but no longer used — the Conversations toggle was switched to the `Clock` icon in this PR.
```suggestion
ArrowDown,
ArrowUp,
ChevronDown,
Clock,
FileDiff,
FolderOpen,
GitBranch,
Pin,
RefreshCcw,
Terminal,
} from 'lucide-react';
```
Reviews (1): Last reviewed commit: "fix: changes gap" | Re-trigger Greptile
| onKeyDown={(e) => { | ||
| if (e.key === 'Enter') commitRename(e.currentTarget.value); | ||
| else if (e.key === 'Escape') setIsEditing(false); | ||
| }} |
There was a problem hiding this comment.
Escape cancels but blur still commits the rename
When the user presses Escape, setIsEditing(false) is called directly — not through commitRename. React then unmounts the focused <input>, which fires onBlur. Because onBlur unconditionally calls commitRename(e.target.value), any edits the user typed before pressing Escape will still be saved. A simple fix is to track whether Escape was used with a ref and bail out in commitRename.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/renderer/features/tasks/view/tab-bar/conversation-tab-item.tsx
Line: 79-82
Comment:
**Escape cancels but blur still commits the rename**
When the user presses Escape, `setIsEditing(false)` is called directly — not through `commitRename`. React then unmounts the focused `<input>`, which fires `onBlur`. Because `onBlur` unconditionally calls `commitRename(e.target.value)`, any edits the user typed before pressing Escape will still be saved. A simple fix is to track whether Escape was used with a ref and bail out in `commitRename`.
How can I resolve this? If you propose a fix, please make it concise.| onBlur={(e) => commitRename(e.target.value)} | ||
| onKeyDown={(e) => { | ||
| if (e.key === 'Enter') commitRename(e.currentTarget.value); |
There was a problem hiding this comment.
Double-fire of
onRenameSubmit on Enter
Pressing Enter calls commitRename (via onKeyDown), which calls onRenameSubmit and schedules setIsEditing(false). When React re-renders and unmounts the focused input, onBlur fires and calls commitRename a second time. Because rawTitle in the closure still holds the old title (the render that created this commitRename), the trimmed !== rawTitle check passes and renameConversation is called twice on the network. A guard ref set to true in commitRename and checked at the top of the function prevents the second invocation.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/renderer/features/tasks/view/tab-bar/conversation-tab-item.tsx
Line: 78-80
Comment:
**Double-fire of `onRenameSubmit` on Enter**
Pressing Enter calls `commitRename` (via `onKeyDown`), which calls `onRenameSubmit` and schedules `setIsEditing(false)`. When React re-renders and unmounts the focused input, `onBlur` fires and calls `commitRename` a second time. Because `rawTitle` in the closure still holds the old title (the render that created this `commitRename`), the `trimmed !== rawTitle` check passes and `renameConversation` is called twice on the network. A guard ref set to `true` in `commitRename` and checked at the top of the function prevents the second invocation.
How can I resolve this? If you propose a fix, please make it concise.
No description provided.