ce8b5dc86d
- Extract tool instance lifecycle endpoints (start/stop/restart/delete) from api/tool/tool_instances.py into new api/tool/tool_lifecycle.py. - Register tool_lifecycle_router in main.py and api/tool/__init__.py. - Extract inline WorkspaceDetailPage components into components/features/workspace/: detail header, tab bars, file/git/tools/settings panels. Slim page from ~446 to ~62 lines. - Update OpenSpec reorganize-long-files tasks to reflect completed work and current source state; mark change completed. - Regenerate project maps. Quality gates: python3 -m py_compile (backend clean), npm run typecheck, npm run lint, npm test -- --run (87 passed), pytest workspace integration and unit tests (27 passed, 1 skipped).
69 lines
1.6 KiB
TypeScript
69 lines
1.6 KiB
TypeScript
/** Tab bar for the workspace detail page. */
|
|
|
|
import { Icon } from "../../icon";
|
|
|
|
export type WorkspaceTab = "files" | "git" | "tools" | "settings";
|
|
|
|
interface Tab {
|
|
id: WorkspaceTab;
|
|
label: string;
|
|
icon: string;
|
|
}
|
|
|
|
const TABS: Tab[] = [
|
|
{ id: "files", label: "Files", icon: "folder" },
|
|
{ id: "git", label: "Git", icon: "branch" },
|
|
{ id: "tools", label: "Tools", icon: "terminal" },
|
|
{ id: "settings", label: "Settings", icon: "settings" },
|
|
];
|
|
|
|
interface WorkspaceTabBarProps {
|
|
active: WorkspaceTab;
|
|
onChange: (tab: WorkspaceTab) => void;
|
|
}
|
|
|
|
export function WorkspaceTabBar({ active, onChange }: WorkspaceTabBarProps) {
|
|
return (
|
|
<nav className="tab-bar" role="tablist">
|
|
{TABS.map((tab) => (
|
|
<button
|
|
key={tab.id}
|
|
className={`tab ${active === tab.id ? "active" : ""}`}
|
|
onClick={() => onChange(tab.id)}
|
|
role="tab"
|
|
aria-selected={active === tab.id}
|
|
type="button"
|
|
>
|
|
<Icon
|
|
name={tab.icon as "folder" | "branch" | "terminal" | "settings"}
|
|
size="sm"
|
|
/>
|
|
{tab.label}
|
|
</button>
|
|
))}
|
|
</nav>
|
|
);
|
|
}
|
|
|
|
export function WorkspaceMobileTabBar({ active, onChange }: WorkspaceTabBarProps) {
|
|
return (
|
|
<nav className="mobile-tab-bar" role="tablist">
|
|
{TABS.map((tab) => (
|
|
<button
|
|
key={tab.id}
|
|
className={`mobile-tab ${active === tab.id ? "active" : ""}`}
|
|
onClick={() => onChange(tab.id)}
|
|
role="tab"
|
|
aria-selected={active === tab.id}
|
|
type="button"
|
|
>
|
|
<Icon
|
|
name={tab.icon as "folder" | "branch" | "terminal" | "settings"}
|
|
/>
|
|
<span>{tab.label}</span>
|
|
</button>
|
|
))}
|
|
</nav>
|
|
);
|
|
}
|