feat: complete reorganize-long-files cleanup
- 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).
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
/** 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user