| 211 | * - Message sending with workspace creation |
| 212 | */ |
| 213 | export function useCreationWorkspace({ |
| 214 | projectPath, |
| 215 | onWorkspaceCreated, |
| 216 | message, |
| 217 | subProjectPath, |
| 218 | draftId, |
| 219 | dynamicWorkflowsEnabled = false, |
| 220 | userModel, |
| 221 | }: UseCreationWorkspaceOptions): UseCreationWorkspaceReturn { |
| 222 | const workspaceContext = useOptionalWorkspaceContext(); |
| 223 | const promoteWorkspaceDraft = workspaceContext?.promoteWorkspaceDraft; |
| 224 | const deleteWorkspaceDraft = workspaceContext?.deleteWorkspaceDraft; |
| 225 | const { currentWorkspaceId, currentProjectId, pendingDraftId } = useRouter(); |
| 226 | const isMountedRef = useRef(true); |
| 227 | const latestRouteRef = useRef({ currentWorkspaceId, currentProjectId, pendingDraftId }); |
| 228 | |
| 229 | useEffect(() => { |
| 230 | isMountedRef.current = true; |
| 231 | return () => { |
| 232 | isMountedRef.current = false; |
| 233 | }; |
| 234 | }, []); |
| 235 | |
| 236 | // Keep router state fresh synchronously so auto-navigation checks don't lag behind route changes. |
| 237 | latestRouteRef.current = { currentWorkspaceId, currentProjectId, pendingDraftId }; |
| 238 | const { api } = useAPI(); |
| 239 | const { getProjectConfig, refreshProjects, loading: projectsLoading } = useProjectContext(); |
| 240 | const { config: providersConfig } = useProvidersConfig(); |
| 241 | const [branches, setBranches] = useState<string[]>([]); |
| 242 | const [branchesLoaded, setBranchesLoaded] = useState(false); |
| 243 | const [recommendedTrunk, setRecommendedTrunk] = useState<string | null>(null); |
| 244 | const [toast, setToast] = useState<Toast | null>(null); |
| 245 | const [isSending, setIsSending] = useState(false); |
| 246 | const [trustPrompt, setTrustPrompt] = useState<{ |
| 247 | resolve: (trusted: boolean) => void; |
| 248 | /** Snapshot of the project path that triggered the trust prompt, |
| 249 | * so onConfirm trusts the correct project even if the user navigates. */ |
| 250 | projectPath: string; |
| 251 | } | null>(null); |
| 252 | // The confirmed identity being used for workspace creation (set after waitForGeneration resolves) |
| 253 | const [creatingWithIdentity, setCreatingWithIdentity] = useState<WorkspaceIdentity | null>(null); |
| 254 | const [runtimeAvailabilityState, setRuntimeAvailabilityState] = |
| 255 | useState<RuntimeAvailabilityState>({ status: "loading" }); |
| 256 | |
| 257 | // Centralized draft workspace settings with automatic persistence |
| 258 | const { |
| 259 | settings, |
| 260 | coderConfigFallback, |
| 261 | sshHostFallback, |
| 262 | setSelectedRuntime, |
| 263 | setDefaultRuntimeChoice, |
| 264 | setTrunkBranch, |
| 265 | } = useDraftWorkspaceSettings(projectPath, branches, recommendedTrunk); |
| 266 | |
| 267 | // Persist draft workspace name generation state per draft (so multiple drafts don't share a |
| 268 | // single auto-naming/manual-name state). |
| 269 | const workspaceNameScopeId = |
| 270 | projectPath.trim().length > 0 |