feat: multi-session terminal frontend UI + tests (PR 3)
- Add TerminalSessionTabs component with status dots, rename, close, max-5 limit - Add 7 component tests for tab rendering, selection, close, rename - TerminalComponent: sessionId prop, forwardRef with fit() method - TerminalPage: multi-session orchestration, tab switching, auto-create default - Fullscreen mode: Alt+Shift+F toggle, auto-hide tabs, Esc exit - Keyboard shortcuts: Alt+Shift+N/W/ArrowLeft/ArrowRight/R - Add CSS for tabs, fullscreen, mobile responsive - Update useTerminalSessions hook for session CRUD - terminal_manager.py: lookup by internal session_id fallback Quality gates: tsc --noEmit clean, vitest (7/7 new tests passed), pytest (182 passed)
This commit is contained in:
@@ -1,87 +1,87 @@
|
||||
import { apiClient } from "./client";
|
||||
|
||||
export interface TerminalSession {
|
||||
id: string;
|
||||
name: string;
|
||||
status: string;
|
||||
has_websockets: boolean;
|
||||
created_at: string;
|
||||
last_activity_at: string | null;
|
||||
id: string;
|
||||
name: string;
|
||||
status: string;
|
||||
has_websockets: boolean;
|
||||
created_at: string;
|
||||
last_activity_at: string | null;
|
||||
}
|
||||
|
||||
export interface TerminalSessionListResponse {
|
||||
sessions: TerminalSession[];
|
||||
sessions: TerminalSession[];
|
||||
}
|
||||
|
||||
export interface TerminalSessionCreateRequest {
|
||||
name?: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export interface TerminalSessionCreateResponse {
|
||||
id: string;
|
||||
name: string;
|
||||
status: string;
|
||||
created_at: string;
|
||||
id: string;
|
||||
name: string;
|
||||
status: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export async function listTerminalSessions(
|
||||
projectId: string,
|
||||
repoId: string,
|
||||
instanceId: string
|
||||
projectId: string,
|
||||
repoId: string,
|
||||
instanceId: string,
|
||||
): Promise<TerminalSession[]> {
|
||||
const response = await apiClient.get(
|
||||
`/projects/${projectId}/repositories/${repoId}/instances/${instanceId}/terminal/sessions`
|
||||
);
|
||||
return response.data.sessions;
|
||||
const response = await apiClient.get(
|
||||
`/projects/${projectId}/repositories/${repoId}/instances/${instanceId}/terminal/sessions`,
|
||||
);
|
||||
return response.data.sessions;
|
||||
}
|
||||
|
||||
export async function createTerminalSession(
|
||||
projectId: string,
|
||||
repoId: string,
|
||||
instanceId: string,
|
||||
name?: string
|
||||
projectId: string,
|
||||
repoId: string,
|
||||
instanceId: string,
|
||||
name?: string,
|
||||
): Promise<TerminalSessionCreateResponse> {
|
||||
const response = await apiClient.post(
|
||||
`/projects/${projectId}/repositories/${repoId}/instances/${instanceId}/terminal/sessions`,
|
||||
{ name }
|
||||
);
|
||||
return response.data;
|
||||
const response = await apiClient.post(
|
||||
`/projects/${projectId}/repositories/${repoId}/instances/${instanceId}/terminal/sessions`,
|
||||
{ name },
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export async function closeTerminalSession(
|
||||
projectId: string,
|
||||
repoId: string,
|
||||
instanceId: string,
|
||||
sessionId: string
|
||||
projectId: string,
|
||||
repoId: string,
|
||||
instanceId: string,
|
||||
sessionId: string,
|
||||
): Promise<{ status: string; session_id: string }> {
|
||||
const response = await apiClient.delete(
|
||||
`/projects/${projectId}/repositories/${repoId}/instances/${instanceId}/terminal/sessions/${sessionId}`
|
||||
);
|
||||
return response.data;
|
||||
const response = await apiClient.delete(
|
||||
`/projects/${projectId}/repositories/${repoId}/instances/${instanceId}/terminal/sessions/${sessionId}`,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export async function resetTerminalSession(
|
||||
projectId: string,
|
||||
repoId: string,
|
||||
instanceId: string,
|
||||
sessionId: string
|
||||
projectId: string,
|
||||
repoId: string,
|
||||
instanceId: string,
|
||||
sessionId: string,
|
||||
): Promise<{ id: string; name: string; status: string }> {
|
||||
const response = await apiClient.post(
|
||||
`/projects/${projectId}/repositories/${repoId}/instances/${instanceId}/terminal/sessions/${sessionId}/reset`
|
||||
);
|
||||
return response.data;
|
||||
const response = await apiClient.post(
|
||||
`/projects/${projectId}/repositories/${repoId}/instances/${instanceId}/terminal/sessions/${sessionId}/reset`,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export async function renameTerminalSession(
|
||||
projectId: string,
|
||||
repoId: string,
|
||||
instanceId: string,
|
||||
sessionId: string,
|
||||
name: string
|
||||
projectId: string,
|
||||
repoId: string,
|
||||
instanceId: string,
|
||||
sessionId: string,
|
||||
name: string,
|
||||
): Promise<{ id: string; name: string }> {
|
||||
const response = await apiClient.post(
|
||||
`/projects/${projectId}/repositories/${repoId}/instances/${instanceId}/terminal/sessions/${sessionId}/rename`,
|
||||
{ name }
|
||||
);
|
||||
return response.data;
|
||||
const response = await apiClient.post(
|
||||
`/projects/${projectId}/repositories/${repoId}/instances/${instanceId}/terminal/sessions/${sessionId}/rename`,
|
||||
{ name },
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user