diff --git a/apps/web/src/hooks/use-start-tool.ts b/apps/web/src/hooks/use-start-tool.ts new file mode 100644 index 0000000..b8f1854 --- /dev/null +++ b/apps/web/src/hooks/use-start-tool.ts @@ -0,0 +1,64 @@ +/** Shared hook for starting a tool on a workspace. */ + +import { useCallback, useState } from "react"; +import { createInstance, startInstance } from "../api/sessions"; +import type { Workspace } from "../types/workspace"; +import type { ToolInstance } from "../api/sessions"; + +export interface UseStartToolResult { + starting: boolean; + error: string | null; + startTool: ( + workspace: Workspace, + toolTypeId: string, + displayName?: string, + configProfileId?: string, + ) => Promise; +} + +export function useStartTool(): UseStartToolResult { + const [starting, setStarting] = useState(false); + const [error, setError] = useState(null); + + const startTool = useCallback( + async ( + workspace: Workspace, + toolTypeId: string, + displayName?: string, + configProfileId?: string, + ): Promise => { + setStarting(true); + setError(null); + try { + const instance = await createInstance( + workspace.project_id, + workspace.repo_id, + toolTypeId, + displayName || workspace.name, + undefined, + undefined, + undefined, + configProfileId, + [], + workspace.id, + ); + await startInstance( + workspace.project_id, + workspace.repo_id, + instance.id, + configProfileId, + ); + return instance; + } catch (err) { + const msg = err instanceof Error ? err.message : "Failed to start tool"; + setError(msg); + return null; + } finally { + setStarting(false); + } + }, + [], + ); + + return { starting, error, startTool }; +} diff --git a/apps/web/src/pages/workspace-detail.tsx b/apps/web/src/pages/workspace-detail.tsx index d83f487..43ed24b 100644 --- a/apps/web/src/pages/workspace-detail.tsx +++ b/apps/web/src/pages/workspace-detail.tsx @@ -7,6 +7,7 @@ import { useWorkspaces } from "../hooks/use-workspaces"; import { useWorkspaceFiles } from "../hooks/use-workspace-files"; import { useWorkspaceGit } from "../hooks/use-workspace-git"; import { useWorkspaceInstances } from "../hooks/use-workspace-instances"; +import { useStartTool } from "../hooks/use-start-tool"; import { useMobileViewport } from "../hooks/use-mobile-viewport"; import { StartToolModal } from "../components/start-tool-modal"; import type { FileEntry } from "../api/workspace-files"; @@ -312,7 +313,8 @@ function GitTab({ workspaceId }: { workspaceId: string }) { /* ─── Tools Tab ─── */ function ToolsTab({ workspace }: { workspace: Workspace }) { - const { instances, loading, create } = useWorkspaceInstances(workspace.id); + const { instances, loading, refresh } = useWorkspaceInstances(workspace.id); + const { startTool, starting } = useStartTool(); const [showModal, setShowModal] = useState(false); return ( @@ -326,8 +328,9 @@ function ToolsTab({ workspace }: { workspace: Workspace }) { ) : ( @@ -355,8 +358,9 @@ function ToolsTab({ workspace }: { workspace: Workspace }) { )} @@ -365,8 +369,16 @@ function ToolsTab({ workspace }: { workspace: Workspace }) { workspace={workspace} onClose={() => setShowModal(false)} onStart={async (toolTypeId, configProfileId) => { - await create(toolTypeId, undefined, configProfileId); - setShowModal(false); + const instance = await startTool( + workspace, + toolTypeId, + undefined, + configProfileId, + ); + if (instance) { + setShowModal(false); + await refresh(); + } }} /> )} @@ -376,11 +388,7 @@ function ToolsTab({ workspace }: { workspace: Workspace }) { /* ─── Settings Tab ─── */ -function SettingsTab({ - workspace, -}: { - workspace: Workspace; -}) { +function SettingsTab({ workspace }: { workspace: Workspace }) { return (
@@ -411,5 +419,3 @@ function SettingsTab({
); } - - diff --git a/apps/web/src/pages/workspaces.tsx b/apps/web/src/pages/workspaces.tsx index f688a2e..e86833e 100644 --- a/apps/web/src/pages/workspaces.tsx +++ b/apps/web/src/pages/workspaces.tsx @@ -4,10 +4,10 @@ import { useState } from "react"; import { Icon } from "../components/icon"; import { useWorkspaces } from "../hooks/use-workspaces"; import { useWorkspaceActions } from "../hooks/use-workspace-actions"; +import { useStartTool } from "../hooks/use-start-tool"; import { WorkspaceCard } from "../components/workspace-card"; import { WorkspaceCreateForm } from "../components/workspace-create-form"; import { StartToolModal } from "../components/start-tool-modal"; -import { createInstance, startInstance } from "../api/sessions"; import type { Workspace } from "../types/workspace"; export function WorkspacesPage() { @@ -16,6 +16,7 @@ export function WorkspacesPage() { const { workspaces, loading, error, refresh } = useWorkspaces(); const actions = useWorkspaceActions(); + const { startTool } = useStartTool(); const handleDelete = async (workspace: Workspace) => { await actions.delete(workspace, refresh); @@ -35,29 +36,15 @@ export function WorkspacesPage() { configProfileId?: string, ) => { if (!startWorkspace) return; - try { - const instance = await createInstance( - startWorkspace.project_id, - startWorkspace.repo_id, - toolTypeId, - `${startWorkspace.name} - ${toolTypeId}`, - undefined, - undefined, - undefined, - configProfileId, - [], - startWorkspace.id, - ); - await startInstance( - startWorkspace.project_id, - startWorkspace.repo_id, - instance.id, - configProfileId, - ); + const instance = await startTool( + startWorkspace, + toolTypeId, + undefined, + configProfileId, + ); + if (instance) { setStartWorkspace(null); await refresh(); - } catch (err) { - alert(err instanceof Error ? err.message : "Failed to start tool"); } };