feat: add multiple terminal tabs with rename capability

Add support for multiple terminal instances per project with tabbed
navigation in the debug panel. Each terminal maintains its own PTY
session and WebSocket connection.

Backend changes:
- Add terminal metadata storage (id, name, created_at) per project
- Update terminal_manager.py with create, list, rename, delete functions
- Extend WebSocket endpoint to /api/terminal/ws/{project}/{terminal_id}
- Add REST endpoints for terminal CRUD operations
- Implement deferred PTY start with initial resize message

Frontend changes:
- Create TerminalTabs component with neobrutalism styling
- Support double-click rename and right-click context menu
- Fix terminal switching issues with transform-based hiding
- Use isActiveRef to prevent stale closure bugs in connect()
- Add double requestAnimationFrame for reliable activation timing
- Implement proper dimension validation in fitTerminal()

Other updates:
- Add GLM model configuration documentation to README
- Simplify client.py by removing CLI_COMMAND support
- Update chat session services with consistent patterns

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Auto
2026-01-12 11:55:50 +02:00
parent c1985eb285
commit a7f8c3aa8d
16 changed files with 1032 additions and 194 deletions

View File

@@ -23,6 +23,7 @@ import type {
ModelsResponse,
DevServerStatusResponse,
DevServerConfig,
TerminalInfo,
} from './types'
const API_BASE = '/api'
@@ -333,3 +334,41 @@ export async function stopDevServer(
export async function getDevServerConfig(projectName: string): Promise<DevServerConfig> {
return fetchJSON(`/projects/${encodeURIComponent(projectName)}/devserver/config`)
}
// ============================================================================
// Terminal API
// ============================================================================
export async function listTerminals(projectName: string): Promise<TerminalInfo[]> {
return fetchJSON(`/terminal/${encodeURIComponent(projectName)}`)
}
export async function createTerminal(
projectName: string,
name?: string
): Promise<TerminalInfo> {
return fetchJSON(`/terminal/${encodeURIComponent(projectName)}`, {
method: 'POST',
body: JSON.stringify({ name: name ?? null }),
})
}
export async function renameTerminal(
projectName: string,
terminalId: string,
name: string
): Promise<TerminalInfo> {
return fetchJSON(`/terminal/${encodeURIComponent(projectName)}/${terminalId}`, {
method: 'PATCH',
body: JSON.stringify({ name }),
})
}
export async function deleteTerminal(
projectName: string,
terminalId: string
): Promise<void> {
await fetchJSON(`/terminal/${encodeURIComponent(projectName)}/${terminalId}`, {
method: 'DELETE',
})
}