diff --git a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx b/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx
index b14e662b5b2..6d97698391a 100644
--- a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx
+++ b/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceItem.tsx
@@ -7,8 +7,9 @@ import {
useReorderWorkspaces,
useSetActiveWorkspace,
} from "renderer/react-query/workspaces";
-import { DeleteWorkspaceDialog } from "./DeleteWorkspaceDialog";
import { useTabs } from "renderer/stores";
+import { DeleteWorkspaceDialog } from "./DeleteWorkspaceDialog";
+import { useWorkspaceRename } from "./useWorkspaceRename";
const WORKSPACE_TYPE = "WORKSPACE";
@@ -37,6 +38,7 @@ export function WorkspaceItem({
const reorderWorkspaces = useReorderWorkspaces();
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
const tabs = useTabs();
+ const rename = useWorkspaceRename(id, title);
const needsAttention = tabs
.filter((t) => t.workspaceId === id)
@@ -80,7 +82,8 @@ export function WorkspaceItem({
ref={(node) => {
drag(drop(node));
}}
- onMouseDown={() => setActive.mutate({ id })}
+ onMouseDown={() => !rename.isRenaming && setActive.mutate({ id })}
+ onDoubleClick={rename.startRename}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
className={`
@@ -94,14 +97,30 @@ export function WorkspaceItem({
`}
style={{ cursor: isDragging ? "grabbing" : "pointer" }}
>
-
- {title}
-
- {needsAttention && (
-
-
-
-
+ {rename.isRenaming ? (
+ rename.setRenameValue(e.target.value)}
+ onBlur={rename.submitRename}
+ onKeyDown={rename.handleKeyDown}
+ onClick={(e) => e.stopPropagation()}
+ onMouseDown={(e) => e.stopPropagation()}
+ className="flex-1 min-w-0 bg-muted border border-primary rounded px-1 py-0.5 text-sm outline-none"
+ />
+ ) : (
+ <>
+
+ {title}
+
+ {needsAttention && (
+
+
+
+
+ )}
+ >
)}
diff --git a/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/useWorkspaceRename.ts b/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/useWorkspaceRename.ts
new file mode 100644
index 00000000000..dc2e0b1c43d
--- /dev/null
+++ b/apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/useWorkspaceRename.ts
@@ -0,0 +1,64 @@
+import { useEffect, useRef, useState } from "react";
+import { useUpdateWorkspace } from "renderer/react-query/workspaces/useUpdateWorkspace";
+
+export function useWorkspaceRename(workspaceId: string, workspaceName: string) {
+ const [isRenaming, setIsRenaming] = useState(false);
+ const [renameValue, setRenameValue] = useState(workspaceName);
+ const inputRef = useRef(null);
+ const updateWorkspace = useUpdateWorkspace();
+
+ // Select input text when rename mode is activated
+ useEffect(() => {
+ if (isRenaming && inputRef.current) {
+ inputRef.current.select();
+ }
+ }, [isRenaming]);
+
+ // Sync rename value when workspace name changes
+ useEffect(() => {
+ setRenameValue(workspaceName);
+ }, [workspaceName]);
+
+ const startRename = () => {
+ setIsRenaming(true);
+ };
+
+ const submitRename = () => {
+ const trimmedValue = renameValue.trim();
+ if (trimmedValue && trimmedValue !== workspaceName) {
+ updateWorkspace.mutate({
+ id: workspaceId,
+ patch: { name: trimmedValue },
+ });
+ } else {
+ setRenameValue(workspaceName);
+ }
+ setIsRenaming(false);
+ };
+
+ const cancelRename = () => {
+ setRenameValue(workspaceName);
+ setIsRenaming(false);
+ };
+
+ const handleKeyDown = (e: React.KeyboardEvent) => {
+ if (e.key === "Enter") {
+ e.preventDefault();
+ submitRename();
+ } else if (e.key === "Escape") {
+ e.preventDefault();
+ cancelRename();
+ }
+ };
+
+ return {
+ isRenaming,
+ renameValue,
+ inputRef,
+ setRenameValue,
+ startRename,
+ submitRename,
+ cancelRename,
+ handleKeyDown,
+ };
+}