From eb2eb510a85fb135e94404bbe67c2e534c7654b5 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Tue, 3 Feb 2026 13:09:13 -0800 Subject: [PATCH 1/3] fix(desktop): fallback to branch name when workspace name is empty When users provide an empty string as a custom workspace name, use the branch name as fallback and keep isUnnamed set to true. Changed from nullish coalescing (??) to logical OR (||) so empty strings trigger the fallback behavior. --- .../src/lib/trpc/routers/workspaces/procedures/create.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/lib/trpc/routers/workspaces/procedures/create.ts b/apps/desktop/src/lib/trpc/routers/workspaces/procedures/create.ts index 9c8dc623ad1..8bb7780d68e 100644 --- a/apps/desktop/src/lib/trpc/routers/workspaces/procedures/create.ts +++ b/apps/desktop/src/lib/trpc/routers/workspaces/procedures/create.ts @@ -383,7 +383,7 @@ export const createCreateProcedures = () => { worktreeId: worktree.id, type: "worktree", branch, - name: input.name ?? branch, + name: input.name || branch, isUnnamed: !input.name, tabOrder: maxTabOrder + 1, }) @@ -591,7 +591,7 @@ export const createCreateProcedures = () => { worktreeId: worktree.id, type: "worktree", branch: worktree.branch, - name: input.name ?? worktree.branch, + name: input.name || worktree.branch, isUnnamed: !input.name, tabOrder: maxTabOrder + 1, }) From c7d0404e1c1f622a6ef03afee9642039a8f1dff9 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Tue, 3 Feb 2026 13:54:48 -0800 Subject: [PATCH 2/3] Revert "fix(desktop): fallback to branch name when workspace name is empty" This reverts commit eb2eb510a85fb135e94404bbe67c2e534c7654b5. --- .../src/lib/trpc/routers/workspaces/procedures/create.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/lib/trpc/routers/workspaces/procedures/create.ts b/apps/desktop/src/lib/trpc/routers/workspaces/procedures/create.ts index 8bb7780d68e..9c8dc623ad1 100644 --- a/apps/desktop/src/lib/trpc/routers/workspaces/procedures/create.ts +++ b/apps/desktop/src/lib/trpc/routers/workspaces/procedures/create.ts @@ -383,7 +383,7 @@ export const createCreateProcedures = () => { worktreeId: worktree.id, type: "worktree", branch, - name: input.name || branch, + name: input.name ?? branch, isUnnamed: !input.name, tabOrder: maxTabOrder + 1, }) @@ -591,7 +591,7 @@ export const createCreateProcedures = () => { worktreeId: worktree.id, type: "worktree", branch: worktree.branch, - name: input.name || worktree.branch, + name: input.name ?? worktree.branch, isUnnamed: !input.name, tabOrder: maxTabOrder + 1, }) From b2e2db4ae3d96a747c2d4983d289a247ef2b2242 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Tue, 3 Feb 2026 13:56:50 -0800 Subject: [PATCH 3/3] fix(desktop): reset to branch name when workspace rename is cleared When users clear the workspace name (trim to empty), revert to the branch name and set isUnnamed=true so the auto-rename feature can still work. Changes: - Add isUnnamed field to workspace update patch schema - Update useWorkspaceRename hook to accept branch parameter - Handle empty name by setting name to branch and isUnnamed=true --- .../routers/workspaces/procedures/status.ts | 17 ++++++++++++----- .../settings/workspace/$workspaceId/page.tsx | 6 +++++- .../WorkspaceListItem/WorkspaceListItem.tsx | 2 +- .../useWorkspaceRename/useWorkspaceRename.ts | 18 ++++++++++++++---- 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/apps/desktop/src/lib/trpc/routers/workspaces/procedures/status.ts b/apps/desktop/src/lib/trpc/routers/workspaces/procedures/status.ts index c34ee8e8029..d1eebab62d2 100644 --- a/apps/desktop/src/lib/trpc/routers/workspaces/procedures/status.ts +++ b/apps/desktop/src/lib/trpc/routers/workspaces/procedures/status.ts @@ -64,6 +64,7 @@ export const createStatusProcedures = () => { patch: z.object({ name: z.string().optional(), preserveUnnamedStatus: z.boolean().optional(), + isUnnamed: z.boolean().optional(), }), }), ) @@ -75,12 +76,18 @@ export const createStatusProcedures = () => { ); } + const resolveIsUnnamed = () => { + if (input.patch.isUnnamed !== undefined) return input.patch.isUnnamed; + if (input.patch.name !== undefined && !input.patch.preserveUnnamedStatus) + return false; + return undefined; + }; + + const isUnnamed = resolveIsUnnamed(); + touchWorkspace(input.id, { - ...(input.patch.name !== undefined && { - name: input.patch.name, - // Only mark as named if not preserving unnamed status - ...(input.patch.preserveUnnamedStatus ? {} : { isUnnamed: false }), - }), + ...(input.patch.name !== undefined && { name: input.patch.name }), + ...(isUnnamed !== undefined && { isUnnamed }), }); return { success: true }; diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/workspace/$workspaceId/page.tsx b/apps/desktop/src/renderer/routes/_authenticated/settings/workspace/$workspaceId/page.tsx index 2e5b30f8fa0..55c0d79407e 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/settings/workspace/$workspaceId/page.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/settings/workspace/$workspaceId/page.tsx @@ -43,7 +43,11 @@ function WorkspaceSettingsPage() { id: workspaceId, }); - const rename = useWorkspaceRename(workspace?.id ?? "", workspace?.name ?? ""); + const rename = useWorkspaceRename( + workspace?.id ?? "", + workspace?.name ?? "", + workspace?.branch ?? "", + ); // Workspace is guaranteed to exist here because loader handles 404s if (!workspace) { diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx index b9f559e3acb..5cda546e352 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx @@ -88,7 +88,7 @@ export function WorkspaceListItem({ const matchRoute = useMatchRoute(); const reorderWorkspaces = useReorderWorkspaces(); const [hasHovered, setHasHovered] = useState(false); - const rename = useWorkspaceRename(id, name); + const rename = useWorkspaceRename(id, name, branch); const tabs = useTabsStore((s) => s.tabs); const panes = useTabsStore((s) => s.panes); const clearWorkspaceAttentionStatus = useTabsStore( diff --git a/apps/desktop/src/renderer/screens/main/hooks/useWorkspaceRename/useWorkspaceRename.ts b/apps/desktop/src/renderer/screens/main/hooks/useWorkspaceRename/useWorkspaceRename.ts index dc2e0b1c43d..17efd8b1ba3 100644 --- a/apps/desktop/src/renderer/screens/main/hooks/useWorkspaceRename/useWorkspaceRename.ts +++ b/apps/desktop/src/renderer/screens/main/hooks/useWorkspaceRename/useWorkspaceRename.ts @@ -1,20 +1,22 @@ import { useEffect, useRef, useState } from "react"; import { useUpdateWorkspace } from "renderer/react-query/workspaces/useUpdateWorkspace"; -export function useWorkspaceRename(workspaceId: string, workspaceName: string) { +export function useWorkspaceRename( + workspaceId: string, + workspaceName: string, + branch: 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]); @@ -25,7 +27,15 @@ export function useWorkspaceRename(workspaceId: string, workspaceName: string) { const submitRename = () => { const trimmedValue = renameValue.trim(); - if (trimmedValue && trimmedValue !== workspaceName) { + const isCleared = !trimmedValue; + + if (isCleared) { + updateWorkspace.mutate({ + id: workspaceId, + patch: { name: branch, isUnnamed: true }, + }); + setRenameValue(branch); + } else if (trimmedValue !== workspaceName) { updateWorkspace.mutate({ id: workspaceId, patch: { name: trimmedValue },