Skip to content

Commit f6dc763

Browse files
committed
refactor: simplify CreationControls props by passing nameState object
Replace 6 individual name-related props with single WorkspaceNameState object. Reduces indirection and keeps component interface cleaner.
1 parent 02440b5 commit f6dc763

File tree

4 files changed

+31
-59
lines changed

4 files changed

+31
-59
lines changed

src/browser/components/ChatInput/CreationControls.tsx

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { RuntimeIconSelector } from "../RuntimeIconSelector";
55
import { Loader2, Wand2 } from "lucide-react";
66
import { cn } from "@/common/lib/utils";
77
import { Tooltip, TooltipWrapper } from "../Tooltip";
8+
import type { WorkspaceNameState } from "@/browser/hooks/useWorkspaceName";
89

910
interface CreationControlsProps {
1011
branches: string[];
@@ -13,25 +14,12 @@ interface CreationControlsProps {
1314
runtimeMode: RuntimeMode;
1415
defaultRuntimeMode: RuntimeMode;
1516
sshHost: string;
16-
/** Called when user clicks a runtime icon to select it (does not persist) */
1717
onRuntimeModeChange: (mode: RuntimeMode) => void;
18-
/** Called when user checks "Default for project" checkbox (persists) */
1918
onSetDefaultRuntime: (mode: RuntimeMode) => void;
20-
/** Called when user changes SSH host */
2119
onSshHostChange: (host: string) => void;
2220
disabled: boolean;
23-
/** Workspace name state */
24-
workspaceName: string;
25-
/** Whether name is being generated */
26-
isGeneratingName: boolean;
27-
/** Whether auto-generation is enabled */
28-
autoGenerateName: boolean;
29-
/** Name generation error */
30-
nameError: string | null;
31-
/** Called when auto-generate checkbox changes */
32-
onAutoGenerateChange: (enabled: boolean) => void;
33-
/** Called when user types in the name field */
34-
onNameChange: (name: string) => void;
21+
/** Workspace name generation state and actions */
22+
nameState: WorkspaceNameState;
3523
}
3624

3725
/**
@@ -45,26 +33,26 @@ export function CreationControls(props: CreationControlsProps) {
4533
const showTrunkBranchSelector =
4634
props.branches.length > 0 && props.runtimeMode !== RUNTIME_MODE.LOCAL;
4735

48-
const { onNameChange, onAutoGenerateChange } = props;
36+
const { nameState } = props;
4937

5038
const handleNameChange = useCallback(
5139
(e: React.ChangeEvent<HTMLInputElement>) => {
52-
onNameChange(e.target.value);
40+
nameState.setName(e.target.value);
5341
},
54-
[onNameChange]
42+
[nameState]
5543
);
5644

5745
// Clicking into the input disables auto-generation so user can edit
5846
const handleInputFocus = useCallback(() => {
59-
if (props.autoGenerateName) {
60-
onAutoGenerateChange(false);
47+
if (nameState.autoGenerate) {
48+
nameState.setAutoGenerate(false);
6149
}
62-
}, [props.autoGenerateName, onAutoGenerateChange]);
50+
}, [nameState]);
6351

6452
// Toggle auto-generation via wand button
6553
const handleWandClick = useCallback(() => {
66-
onAutoGenerateChange(!props.autoGenerateName);
67-
}, [props.autoGenerateName, onAutoGenerateChange]);
54+
nameState.setAutoGenerate(!nameState.autoGenerate);
55+
}, [nameState]);
6856

6957
return (
7058
<div className="flex flex-col gap-2">
@@ -77,19 +65,19 @@ export function CreationControls(props: CreationControlsProps) {
7765
<input
7866
id="workspace-name"
7967
type="text"
80-
value={props.workspaceName}
68+
value={nameState.name}
8169
onChange={handleNameChange}
8270
onFocus={handleInputFocus}
83-
placeholder={props.isGeneratingName ? "Generating..." : "workspace-name"}
71+
placeholder={nameState.isGenerating ? "Generating..." : "workspace-name"}
8472
disabled={props.disabled}
8573
className={cn(
8674
"bg-separator text-foreground border-border-medium focus:border-accent h-6 w-full rounded border px-2 pr-6 text-xs focus:outline-none disabled:opacity-50",
87-
props.nameError && "border-red-500"
75+
nameState.error && "border-red-500"
8876
)}
8977
/>
9078
{/* Magic wand / loading indicator - vertically centered */}
9179
<div className="absolute inset-y-0 right-0 flex items-center pr-1.5">
92-
{props.isGeneratingName ? (
80+
{nameState.isGenerating ? (
9381
<Loader2 className="text-accent h-3.5 w-3.5 animate-spin" />
9482
) : (
9583
<TooltipWrapper inline>
@@ -98,26 +86,26 @@ export function CreationControls(props: CreationControlsProps) {
9886
onClick={handleWandClick}
9987
disabled={props.disabled}
10088
className="flex h-full items-center disabled:opacity-50"
101-
aria-label={props.autoGenerateName ? "Disable auto-naming" : "Enable auto-naming"}
89+
aria-label={nameState.autoGenerate ? "Disable auto-naming" : "Enable auto-naming"}
10290
>
10391
<Wand2
10492
className={cn(
10593
"h-3.5 w-3.5 transition-colors",
106-
props.autoGenerateName
94+
nameState.autoGenerate
10795
? "text-accent"
10896
: "text-muted-foreground opacity-50 hover:opacity-75"
10997
)}
11098
/>
11199
</button>
112100
<Tooltip className="tooltip" align="center">
113-
{props.autoGenerateName ? "Auto-naming enabled" : "Click to enable auto-naming"}
101+
{nameState.autoGenerate ? "Auto-naming enabled" : "Click to enable auto-naming"}
114102
</Tooltip>
115103
</TooltipWrapper>
116104
)}
117105
</div>
118106
</div>
119107
{/* Error display - inline */}
120-
{props.nameError && <span className="text-xs text-red-500">{props.nameError}</span>}
108+
{nameState.error && <span className="text-xs text-red-500">{nameState.error}</span>}
121109
</div>
122110

123111
{/* Second row: Runtime, Branch, SSH */}

src/browser/components/ChatInput/index.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,7 +1193,7 @@ export const ChatInput: React.FC<ChatInputProps> = (props) => {
11931193
projectName={props.projectName}
11941194
isSending={creationState.isSending || isSending}
11951195
workspaceName={
1196-
creationState.isSending || isSending ? creationState.workspaceName : undefined
1196+
creationState.isSending || isSending ? creationState.nameState.name : undefined
11971197
}
11981198
/>
11991199
)}
@@ -1404,12 +1404,7 @@ export const ChatInput: React.FC<ChatInputProps> = (props) => {
14041404
onSetDefaultRuntime={creationState.setDefaultRuntimeMode}
14051405
onSshHostChange={creationState.setSshHost}
14061406
disabled={creationState.isSending || isSending}
1407-
workspaceName={creationState.workspaceName}
1408-
isGeneratingName={creationState.isGeneratingName}
1409-
autoGenerateName={creationState.autoGenerateName}
1410-
nameError={creationState.nameError}
1411-
onAutoGenerateChange={creationState.setAutoGenerateName}
1412-
onNameChange={creationState.setWorkspaceName}
1407+
nameState={creationState.nameState}
14131408
/>
14141409
)}
14151410
</div>

src/browser/components/ChatInput/useCreationWorkspace.ts

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import type { Toast } from "@/browser/components/ChatInputToast";
1919
import { createErrorToast } from "@/browser/components/ChatInputToasts";
2020
import { useAPI } from "@/browser/contexts/API";
2121
import type { ImagePart } from "@/common/orpc/types";
22-
import { useWorkspaceName } from "@/browser/hooks/useWorkspaceName";
22+
import { useWorkspaceName, type WorkspaceNameState } from "@/browser/hooks/useWorkspaceName";
2323

2424
interface UseCreationWorkspaceOptions {
2525
projectPath: string;
@@ -69,18 +69,8 @@ interface UseCreationWorkspaceReturn {
6969
setToast: (toast: Toast | null) => void;
7070
isSending: boolean;
7171
handleSend: (message: string, imageParts?: ImagePart[]) => Promise<boolean>;
72-
/** Workspace name state */
73-
workspaceName: string;
74-
/** Whether name is being generated */
75-
isGeneratingName: boolean;
76-
/** Whether auto-generation is enabled */
77-
autoGenerateName: boolean;
78-
/** Name generation error */
79-
nameError: string | null;
80-
/** Set auto-generation enabled */
81-
setAutoGenerateName: (enabled: boolean) => void;
82-
/** Set workspace name (for manual entry) */
83-
setWorkspaceName: (name: string) => void;
72+
/** Workspace name generation state and actions (for CreationControls) */
73+
nameState: WorkspaceNameState;
8474
}
8575

8676
/**
@@ -254,12 +244,7 @@ export function useCreationWorkspace({
254244
setToast,
255245
isSending,
256246
handleSend,
257-
// Workspace name state
258-
workspaceName: workspaceNameState.name,
259-
isGeneratingName: workspaceNameState.isGenerating,
260-
autoGenerateName: workspaceNameState.autoGenerate,
261-
nameError: workspaceNameState.error,
262-
setAutoGenerateName: workspaceNameState.setAutoGenerate,
263-
setWorkspaceName: workspaceNameState.setName,
247+
// Workspace name state (for CreationControls)
248+
nameState: workspaceNameState,
264249
};
265250
}

src/browser/hooks/useWorkspaceName.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ export interface UseWorkspaceNameOptions {
88
debounceMs?: number;
99
}
1010

11-
export interface UseWorkspaceNameReturn {
11+
/** State and actions for workspace name generation, suitable for passing to components */
12+
export interface WorkspaceNameState {
1213
/** The generated or manually entered name */
1314
name: string;
1415
/** Whether name generation is in progress */
@@ -21,6 +22,9 @@ export interface UseWorkspaceNameReturn {
2122
setAutoGenerate: (enabled: boolean) => void;
2223
/** Set manual name (for when auto-generate is off) */
2324
setName: (name: string) => void;
25+
}
26+
27+
export interface UseWorkspaceNameReturn extends WorkspaceNameState {
2428
/** Wait for any pending generation to complete */
2529
waitForGeneration: () => Promise<string>;
2630
}

0 commit comments

Comments
 (0)