Skip to content

Commit 52e29ae

Browse files
committed
refactor: cleanup and simplify pending workspace code
- Extract sanitizeBranchName() to deduplicate validation logic - Remove redundant Map.set() after in-place sort - Add isDisabled flag to consolidate isCreating || isDeleting checks - Improve aria-label for deleting state
1 parent 1c414a1 commit 52e29ae

File tree

3 files changed

+31
-28
lines changed

3 files changed

+31
-28
lines changed

src/browser/components/WorkspaceListItem.tsx

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ const WorkspaceListItemInner: React.FC<WorkspaceListItemProps> = ({
4545
// Destructure metadata for convenience
4646
const { id: workspaceId, name: workspaceName, namedWorkspacePath, status } = metadata;
4747
const isCreating = status === "creating";
48+
const isDisabled = isCreating || isDeleting;
4849
const gitStatus = useGitStatus(workspaceId);
4950

5051
// Get rename context
@@ -107,14 +108,14 @@ const WorkspaceListItemInner: React.FC<WorkspaceListItemProps> = ({
107108
<div
108109
className={cn(
109110
"py-1.5 pl-4 pr-2 border-l-[3px] border-transparent transition-all duration-150 text-[13px] relative flex gap-2",
110-
isCreating || isDeleting
111+
isDisabled
111112
? "cursor-default opacity-70"
112113
: "cursor-pointer hover:bg-hover [&:hover_button]:opacity-100",
113-
isSelected && !isCreating && "bg-hover border-l-blue-400",
114+
isSelected && !isDisabled && "bg-hover border-l-blue-400",
114115
isDeleting && "pointer-events-none"
115116
)}
116117
onClick={() => {
117-
if (isCreating) return; // Disable click while creating
118+
if (isDisabled) return;
118119
onSelectWorkspace({
119120
projectPath,
120121
projectName,
@@ -123,7 +124,7 @@ const WorkspaceListItemInner: React.FC<WorkspaceListItemProps> = ({
123124
});
124125
}}
125126
onKeyDown={(e) => {
126-
if (isCreating) return; // Disable keyboard while creating
127+
if (isDisabled) return;
127128
if (e.key === "Enter" || e.key === " ") {
128129
e.preventDefault();
129130
onSelectWorkspace({
@@ -135,12 +136,16 @@ const WorkspaceListItemInner: React.FC<WorkspaceListItemProps> = ({
135136
}
136137
}}
137138
role="button"
138-
tabIndex={isCreating ? -1 : 0}
139+
tabIndex={isDisabled ? -1 : 0}
139140
aria-current={isSelected ? "true" : undefined}
140141
aria-label={
141-
isCreating ? `Creating workspace ${displayName}` : `Select workspace ${displayName}`
142+
isCreating
143+
? `Creating workspace ${displayName}`
144+
: isDeleting
145+
? `Deleting workspace ${displayName}`
146+
: `Select workspace ${displayName}`
142147
}
143-
aria-disabled={isCreating}
148+
aria-disabled={isDisabled}
144149
data-workspace-path={namedWorkspacePath}
145150
data-workspace-id={workspaceId}
146151
>
@@ -170,14 +175,14 @@ const WorkspaceListItemInner: React.FC<WorkspaceListItemProps> = ({
170175
<span
171176
className={cn(
172177
"text-foreground -mx-1 min-w-0 flex-1 truncate rounded-sm px-1 text-left text-[14px] transition-colors duration-200",
173-
!isCreating && "cursor-pointer hover:bg-white/5"
178+
!isDisabled && "cursor-pointer hover:bg-white/5"
174179
)}
175180
onDoubleClick={(e) => {
176-
if (isCreating) return; // Disable rename while creating
181+
if (isDisabled) return;
177182
e.stopPropagation();
178183
startRenaming();
179184
}}
180-
title={isCreating ? "Creating workspace..." : "Double-click to rename"}
185+
title={isDisabled ? undefined : "Double-click to rename"}
181186
>
182187
{canInterrupt || isCreating ? (
183188
<Shimmer className="w-full truncate" colorClass="var(--color-foreground)">

src/browser/utils/ui/workspaceFiltering.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,13 @@ export function buildSortedWorkspacesByProject(
4848
}
4949
}
5050

51-
// Sort each project's workspaces by recency
52-
for (const [projectPath, metadataList] of result) {
51+
// Sort each project's workspaces by recency (sort mutates in place)
52+
for (const metadataList of result.values()) {
5353
metadataList.sort((a, b) => {
5454
const aTimestamp = workspaceRecency[a.id] ?? 0;
5555
const bTimestamp = workspaceRecency[b.id] ?? 0;
5656
return bTimestamp - aTimestamp;
5757
});
58-
result.set(projectPath, metadataList);
5958
}
6059

6160
return result;

src/node/services/workspaceTitleGenerator.ts

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,30 +46,29 @@ export async function generateWorkspaceName(
4646
}
4747

4848
/**
49-
* Validate and sanitize branch name to be git-safe
49+
* Sanitize a string to be git-safe: lowercase, hyphens only, no leading/trailing hyphens.
5050
*/
51-
function validateBranchName(name: string): string {
52-
// Ensure git-safe
53-
const cleaned = name.toLowerCase().replace(/[^a-z0-9-]/g, "-");
54-
// Remove leading/trailing hyphens and collapse multiple hyphens
55-
return cleaned
51+
function sanitizeBranchName(name: string, maxLength: number): string {
52+
return name
53+
.toLowerCase()
54+
.replace(/[^a-z0-9]+/g, "-")
5655
.replace(/^-+|-+$/g, "")
5756
.replace(/-+/g, "-")
58-
.substring(0, 50);
57+
.substring(0, maxLength);
58+
}
59+
60+
/**
61+
* Validate and sanitize branch name to be git-safe
62+
*/
63+
function validateBranchName(name: string): string {
64+
return sanitizeBranchName(name, 50);
5965
}
6066

6167
/**
6268
* Generate a placeholder name from the user's message for immediate display
6369
* while the AI generates the real title. This is git-safe and human-readable.
6470
*/
6571
export function generatePlaceholderName(message: string): string {
66-
// Take first ~40 chars, sanitize for git branch name
6772
const truncated = message.slice(0, 40).trim();
68-
const sanitized = truncated
69-
.toLowerCase()
70-
.replace(/[^a-z0-9]+/g, "-")
71-
.replace(/^-+|-+$/g, "")
72-
.replace(/-+/g, "-")
73-
.substring(0, 30);
74-
return sanitized || "new-workspace";
73+
return sanitizeBranchName(truncated, 30) || "new-workspace";
7574
}

0 commit comments

Comments
 (0)