feat(terminal): add embedded PTY-backed terminal dock with per-tab sessions

Adds a built-in terminal dock that runs a real pseudo-terminal per repository tab.

- Backend: new src-tauri/src/terminal.rs implements TerminalState and Tauri commands
  terminal_open, terminal_write, terminal_resize and terminal_close using the
  portable-pty crate. Terminal output is emitted via "terminal:data" and exits
  via "terminal:exit". Includes unit tests for UTF-8 chunk handling. Cargo.toml
  updated to depend on portable-pty and main.rs registers the state/commands.
- Frontend: App.svelte/UI and app.css updated to show a resizable terminal dock,
  toggleable with Ctrl+` and persisted open/height state. TerminalPanel.svelte is
  lazy-loaded per repo tab (frontend provides session ids like repo:<path>).
  package.json adds @xterm/xterm and @xterm/addon-fit for the frontend terminal
  surface.
- Docs: CHANGELOG.md notes the new embedded terminal feature.

Behavior: each repository tab has its own PTY-backed shell started in the repo
cwd; the user's shell (SHELL/COMSPEC) is used so prompts, aliases and interactive
behavior work as in a normal terminal.
This commit is contained in:
2026-09-22 21:11:48 +02:00
parent 8bac03e3fc
commit 6ef5d3b677
13 changed files with 832 additions and 85 deletions
+19
View File
@@ -777,3 +777,22 @@ export function getIntegrationIssueLabels(provider: GitIntegrationProvider, base
export function setIntegrationIssueLabels(provider: GitIntegrationProvider, baseUrl: string, username: string, token: string, repository: string, number: number, labels: import("./types").IntegrationLabel[], expected: string[] | null = null): Promise<import("./types").IntegrationLabel[]> {
return invoke("set_integration_issue_labels", { provider, baseUrl, username, token, repository, number, labels, expected });
}
// ── Embedded terminal ────────────────────────────────────────────────────────
// Session ids are owned by the frontend: one per repository tab.
export function openTerminal(id: string, cwd: string, cols: number, rows: number): Promise<void> {
return invoke<void>("terminal_open", { id, cwd, cols, rows });
}
export function writeTerminal(id: string, data: string): Promise<void> {
return invoke<void>("terminal_write", { id, data });
}
export function resizeTerminal(id: string, cols: number, rows: number): Promise<void> {
return invoke<void>("terminal_resize", { id, cols, rows });
}
export function closeTerminal(id: string): Promise<void> {
return invoke<void>("terminal_close", { id });
}