Skip to content

feat: titlebar improvements#2147

Merged
Davidknp merged 6 commits into
mainfrom
ux-improvements
May 20, 2026
Merged

feat: titlebar improvements#2147
Davidknp merged 6 commits into
mainfrom
ux-improvements

Conversation

@Davidknp
Copy link
Copy Markdown
Collaborator

No description provided.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 20, 2026

Greptile Summary

This 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.

  • Diff-stat badge & button reorder (task-titlebar.tsx, shortcuts.ts): The changes toggle now expands to show +N / -N line counts with a CSS max-width transition. The Files and Conversations buttons swap positions and icons; their keyboard shortcuts are updated to match in shortcuts.ts.
  • Inline tab rename (conversation-tab-item.tsx, tab-bar.tsx): Right-clicking a conversation tab shows a "Rename" option that replaces the tab with an <input>. renameConversation is plumbed in via useConversations in tab-bar.tsx. There are two event-ordering bugs in the rename input that can cause the save to fire on Escape or fire twice on Enter (see inline comments).

Confidence Score: 3/5

The 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.

Important Files Changed

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]
Loading

Comments Outside Diff (1)

  1. src/renderer/features/tasks/task-titlebar.tsx, line 2-13 (link)

    P2 MessageSquare is imported but no longer used — the Conversations toggle was switched to the Clock icon 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

Comment on lines +79 to +82
onKeyDown={(e) => {
if (e.key === 'Enter') commitRename(e.currentTarget.value);
else if (e.key === 'Escape') setIsEditing(false);
}}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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.

Comment on lines +78 to +80
onBlur={(e) => commitRename(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') commitRename(e.currentTarget.value);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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.

@Davidknp Davidknp merged commit cffce6d into main May 20, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant