fix: add from __future__ import annotations to workspace_manager.py

Fixes NameError: ToolInstance not defined at runtime because
type annotations are evaluated at class definition time.
Deferring annotation evaluation with __future__ annotations
keeps TYPE_CHECKING imports from causing runtime crashes.

Also includes ruff formatting cleanup on workspace-related files.
This commit is contained in:
2026-05-31 23:41:45 +02:00
parent 5bba2bbd92
commit a5d64d1859
15 changed files with 831 additions and 699 deletions
+110 -94
View File
@@ -11,109 +11,125 @@ import { createInstance, startInstance } from "../api/sessions";
import type { Workspace } from "../types/workspace";
export function WorkspacesPage() {
const [showCreate, setShowCreate] = useState(false);
const [startWorkspace, setStartWorkspace] = useState<Workspace | null>(null);
const [showCreate, setShowCreate] = useState(false);
const [startWorkspace, setStartWorkspace] = useState<Workspace | null>(null);
// TODO: Get projectId and repoId from URL params or context
const projectId = "default-project";
const repoId = "default-repo";
// TODO: Get projectId and repoId from URL params or context
const projectId = "default-project";
const repoId = "default-repo";
const { workspaces, loading, error, refresh } = useWorkspaces(projectId, repoId);
const actions = useWorkspaceActions();
const { workspaces, loading, error, refresh } = useWorkspaces(
projectId,
repoId,
);
const actions = useWorkspaceActions();
const handleCreate = async (data: { name: string; branch: string }) => {
await actions.create(projectId, repoId, data);
setShowCreate(false);
await refresh();
};
const handleCreate = async (data: { name: string; branch: string }) => {
await actions.create(projectId, repoId, data);
setShowCreate(false);
await refresh();
};
const handleDelete = async (workspace: Workspace) => {
await actions.delete(projectId, repoId, workspace, refresh);
};
const handleDelete = async (workspace: Workspace) => {
await actions.delete(projectId, repoId, workspace, refresh);
};
const handleSync = async (workspace: Workspace) => {
await actions.sync(projectId, repoId, workspace, refresh);
};
const handleSync = async (workspace: Workspace) => {
await actions.sync(projectId, repoId, workspace, refresh);
};
const handleStartTool = async (toolTypeId: string, configProfileId?: string) => {
if (!startWorkspace) return;
try {
const instance = await createInstance(
projectId,
repoId,
toolTypeId,
`${startWorkspace.name} - ${toolTypeId}`,
undefined,
undefined,
undefined,
configProfileId,
[],
startWorkspace.id
);
await startInstance(projectId, repoId, instance.id, configProfileId);
setStartWorkspace(null);
await refresh();
} catch (err) {
alert(err instanceof Error ? err.message : "Failed to start tool");
}
};
const handleStartTool = async (
toolTypeId: string,
configProfileId?: string,
) => {
if (!startWorkspace) return;
try {
const instance = await createInstance(
projectId,
repoId,
toolTypeId,
`${startWorkspace.name} - ${toolTypeId}`,
undefined,
undefined,
undefined,
configProfileId,
[],
startWorkspace.id,
);
await startInstance(projectId, repoId, instance.id, configProfileId);
setStartWorkspace(null);
await refresh();
} catch (err) {
alert(err instanceof Error ? err.message : "Failed to start tool");
}
};
return (
<div className="page workspaces-page">
<header className="page-header">
<h1>Workspaces</h1>
<div className="header-actions">
<button className="btn btn-secondary" onClick={refresh} disabled={loading}>
<Icon name="refresh" size="sm" />
</button>
<button className="btn btn-primary" onClick={() => setShowCreate(true)}>
<Icon name="add" size="sm" /> New Workspace
</button>
</div>
</header>
return (
<div className="page workspaces-page">
<header className="page-header">
<h1>Workspaces</h1>
<div className="header-actions">
<button
className="btn btn-secondary"
onClick={refresh}
disabled={loading}
>
<Icon name="refresh" size="sm" />
</button>
<button
className="btn btn-primary"
onClick={() => setShowCreate(true)}
>
<Icon name="add" size="sm" /> New Workspace
</button>
</div>
</header>
{error && <div className="alert alert-error">{error}</div>}
{error && <div className="alert alert-error">{error}</div>}
{showCreate && (
<WorkspaceCreateForm
projectId={projectId}
repoId={repoId}
onSubmit={handleCreate}
onCancel={() => setShowCreate(false)}
/>
)}
{showCreate && (
<WorkspaceCreateForm
projectId={projectId}
repoId={repoId}
onSubmit={handleCreate}
onCancel={() => setShowCreate(false)}
/>
)}
{loading && workspaces.length === 0 ? (
<div className="loading-state">Loading workspaces...</div>
) : workspaces.length === 0 ? (
<div className="empty-state">
<p>No workspaces yet.</p>
<button className="btn btn-primary" onClick={() => setShowCreate(true)}>
<Icon name="add" size="sm" /> Create your first workspace
</button>
</div>
) : (
<div className="workspaces-grid">
{workspaces.map((ws) => (
<WorkspaceCard
key={ws.id}
workspace={ws}
loading={actions.loadingId === ws.id}
onStartTool={setStartWorkspace}
onSync={handleSync}
onDelete={handleDelete}
/>
))}
</div>
)}
{loading && workspaces.length === 0 ? (
<div className="loading-state">Loading workspaces...</div>
) : workspaces.length === 0 ? (
<div className="empty-state">
<p>No workspaces yet.</p>
<button
className="btn btn-primary"
onClick={() => setShowCreate(true)}
>
<Icon name="add" size="sm" /> Create your first workspace
</button>
</div>
) : (
<div className="workspaces-grid">
{workspaces.map((ws) => (
<WorkspaceCard
key={ws.id}
workspace={ws}
loading={actions.loadingId === ws.id}
onStartTool={setStartWorkspace}
onSync={handleSync}
onDelete={handleDelete}
/>
))}
</div>
)}
{startWorkspace && (
<StartToolModal
workspace={startWorkspace}
onClose={() => setStartWorkspace(null)}
onStart={handleStartTool}
/>
)}
</div>
);
{startWorkspace && (
<StartToolModal
workspace={startWorkspace}
onClose={() => setStartWorkspace(null)}
onStart={handleStartTool}
/>
)}
</div>
);
}