feat(terminal): v3.1.0 — WebGL, tab types, agent launch, New AI task + PTY fixes#84
feat(terminal): v3.1.0 — WebGL, tab types, agent launch, New AI task + PTY fixes#84devlint wants to merge 65 commits into
Conversation
|
C'est nice de voir un début avec le terminal! Il y a déjà quelques idées qui commecent a bouillonner dans ma tête. 🤯 |
|
@t1gu1 Tu peux déjà récup la branch et tester, voire itérer 🚀 ;-) |
|
Je vais avoir un temps pour regarder ça dans 5h/6h (Vers l'heure du souper, au Canada) |
|
@devlint À moins que tu me dit que ce soit un skill issue haha Enfin, je peux quand même cloner ta brancher et l'essayer! De mon côté, il faut que j'autorise les autres à pouvoir modifier ma branche/PR.
|
|
@t1gu1 Je t'ai ajouté en tant que collaborateur, tu devrais pouvoir contribuer directement sur le repo 👍 |
…ocking - Wrap PtyHandle.writer in Arc<Mutex> so terminal_write clones the Arc, drops the registry guard, then performs write_all/flush without holding the global sessions lock (fixes stall when kernel PTY buffer is full). - Add lock_sessions() helper that recovers from Mutex poisoning via unwrap_or_else(|e| e.into_inner()) instead of panicking; applied to all 5 lock sites including the new writer Arc lock. - enriched_path() now returns None when no extra paths were prepended. - Replace #[allow(unreachable_code)] with #[cfg(not(target_os = "macos"))].
… + dev-server write guard
…, alive flag + coalesce test - openTab: skip onChunk delivery while sessionId is still -1 (early chunk misroute fix) - disposeRepo: clear pending debounce timer for repo before closing sessions - closeTab: set tab.alive = false on spliced tab so callers see it as closed - tests: add debounce coalescing test (2 calls 400ms apart → handler fires exactly once)
…rag listener cleanup - Watch source changed from `tabs` (computed returning same array instance) to a derived string `id:sessionId|...` so the watch fires on every push/splice - onBeforeUnmount now removes mousemove/mouseup drag listeners to prevent leaks
Add pure `resolveTerminalShortcut(e, focused)` to useTerminalSessions.ts and wire it in App.vue onKeyDown before the repo-tab handlers, so that Cmd+T/W/1-9 act on terminal tabs when the terminal panel has focus.
…tate cursor/windsurf still launch externally; claude/other emit launch-agent to App.vue which opens a PTY tab and types the tool command into it. setTimeout fake-active removed; replaced with loadSessions() re-poll.
…it directly (no index race)
Add two user settings for the integrated PTY terminal: - terminalFontSize (default 13): replaces the hardcoded fontSize in TerminalPanel.vue - terminalShell (default ""): passed as shell override to terminalOpen; empty = auto Both fields added to AppSettings interface + defaults in useSettings.ts AND to the local Settings interface + defaults in SettingsPanel.vue (AGENTS.md sync rule). UI controls added in the Editor tab (range slider for font size, text input for shell). Shell is threaded: settings → openTerminalTab (App.vue) → openTab opts (useTerminalSessions) → terminalOpen shell param. i18n keys added to all 5 locale files (en, fr, es, pt-BR, zh-CN): settings.terminalFontSize, settings.terminalShell, settings.terminalShellHint
…t, complete i18n parity - I1: add closeRepoTab() wrapper in App.vue that calls disposeRepo before removing a repo tab; wire all 3 close-tab call sites (⌘W, menu closeWindow, @close-tab template binding) - I2: route PTY output by stable tab.id instead of sessionId; remove the tab.sessionId>=0 guard that dropped early chunks; add pendingChunks buffer in TerminalPanel so output arriving before xterm mounts is flushed on mountTab - M6: add settings.terminalFontSize/terminalShell/terminalShellHint to pt-BR and zh-CN locale files (keys were missing from committed versions)
…click Replace the Agents button in AppHeader with a Terminal button that emits openTerminal event. Wire AppHeader to call openTerminalTab() when clicked, which opens a shell PTY session in the active repository. Consumes terminal i18n keys (headerLabel, headerTooltip) added in Task 1. disabled
…SidebarListeners The "Agents" header button was renamed to "Terminal". Two leftover references removed: - RepoSidebar.vue defineEmits: openAgents event (never emitted) - App.vue repoSidebarListeners: openAgents handler (dead code) disabled
…po_path, shell validation, PTY orphan/zombie, EventSource leak, keystroke buffer)
…(transient blip fix)
a3015a6 to
167cadb
Compare
Header terminal button now toggles the panel instead of always spawning a new tab: hide if open, spawn first tab only when none exist. Restyle tab strip with rounded top corners and opacity states for active/hover. 🪄 Commit via GitWand
Bump new-tab font size and add black padded background to the terminal host for better visual separation. 🪄 Commit via GitWand
Only pass `-l` to shells that accept it — nushell/powershell reject it and the PTY dies on boot. Also force a SIGWINCH after first child output so TUIs that latch 80×24 at boot snap to the real size, and guard mountTab against concurrent runs spawning duplicate terminals. 🪄 Commit via GitWand
Wrap TerminalPanel in KeepAlive so toggling the dock deactivates rather than unmounts the xterm instances, preserving buffers and PTY view. On re-activation, refit each tab once its host is sized and kick a held two-step resize so running TUI children (claude) get a real SIGWINCH and redraw instead of staying blank until a manual resize. 🪄 Commit via GitWand
Replace the header terminal pill with a standalone tile on the AppDock that rides along with the dock and reflects the panel's open state, freeing header space and grouping navigation in one place. 🪄 Commit via GitWand
Fullscreen fills the app-body area while keeping the project list and header visible; state persists via localStorage. Adds enter/exit fullscreen labels across all locales. 🪄 Commit via GitWand
Let users resize the terminal panel from the left edge and both top corners, not just the right edge — corners adjust width and height together while pinning the bottom edge. 🪄 Commit via GitWand
Render a dark placeholder surface prompting the user to open a tab, instead of leaving a blank area when all tabs are closed. 🪄 Commit via GitWand
PTY spawn fails with a cryptic "spawn shell failed" when the Claude Code or Codex CLI isn't installed or on PATH. Surface a clear, localized message instead. 🪄 Commit via GitWand
Drive terminal layout (floating/fullscreen/bottom) from settings instead of a localStorage fullscreen flag, with a dock context menu to switch modes and an opt-out for hiding the terminal on view navigation. 🪄 Commit via GitWand
Track a persistent top offset so the panel floats anywhere instead of being pinned to the bottom edge. Add tab-bar drag-to-move on both axes, all four corner resize handles, and a bottom-edge resize handle. 🪄 Commit via GitWand
ea718f6 to
cb36313
Compare
Security: - Strip CLAUDE_AUTH_OVERRIDE_ENV vars from PTY env before spawn (terminal.rs) - Add first-class `agent` param so Claude/Codex never route through shell path, preserving the strip_claude_auth_env / claudeSpawnEnv infrastructure - Replace hand-rolled CWD canonicalization with safe_repo_path(cwd, ".") to honor AGENTS.md's single-audited-guard rule Concurrency / correctness (Rust): - Wrap master PTY in Arc<Mutex> so terminal_resize releases the global sessions lock before the resize ioctl — prevents blocking terminal_write across all sessions during a resize - Kill orphaned child process when try_clone_reader/take_writer fails post-spawn - Extract macos_enriched_path() shared helper; PTY now gets the same PATH prefixes as hidden_cmd (adds /usr/local/sbin and /opt/local/bin) Correctness (TypeScript / Vue): - devTerminalOpen: reject Promise on initial connection failure (was hanging) - openTerminalTab: add 10s timeout on terminalPanelRef watch (was hanging on async component load failure) - notifyOutput: cap debounce at 4s so continuous output can't starve git refresh - closeTab: document Fix-3 coverage of the sessionId=-1 / disposeRepo race - ResizeObserver: guard resize(-1) before session opens - refitWhenSized: re-trigger on sessionId -1→valid transition so slow spawns don't leave TUI at 80×24 - Keyboard tab-switch (Cmd+1-9) now calls markRead(), matching mouse-click - Capture repoPath at tab-open time so late output refreshes the correct repo - AgentSessionsPanel: forward session.tool instead of hardcoding "claude" - TerminalPanel: replace 3-path dropdown listener with single watchEffect - TerminalPanel: extract getOrCreateBuf() helper (pendingChunks + pendingInput) - dev-server: validate PTY id + string payload in terminal-write Other: - Add impeccable ignore for src/App.vue (semantic warning indicators) - Set worktree.bgIsolation=none in .claude/settings.json - Regression test for notifyOutput max-wait cap
Snapshot container/panel sizes at drag start instead of reading offset* on every mousemove, which forced a layout reflow per event. Also close PTY sessions concurrently via Promise.all on cleanup. 🪄 Commit via GitWand
Wire the clipboard-manager plugin so the terminal can copy, paste, select-all, clear and search via a right-click menu. Add opt-in copy-on-select and paste-on-right-click settings, with localized strings across all locales. 🪄 Commit via GitWand
Switch terminal default from floating to bottom and disable hide-on-nav so the dock is visible out of the box. Surface layout mode and hide-on-nav controls in the terminal settings tab. 🪄 Commit via GitWand
Provide a discoverable affordance to open the terminal search bar without relying on the keyboard shortcut or context menu. 🪄 Commit via GitWand
|
Hey hey @devlint! J'ai fait pas mal d'ajustement et d'itération de mon côté. |
|
@devlint Here is what I propose:
WDYT? EDIT: also, i just found that there is an error when i try to delete them from the GitTree: |
…ment Each project tab gets a caret submenu to switch its checkout in place across main and every worktree. AI-task scratch worktrees can be merged back or deleted from the menu, and closing a project now asks to confirm. 🪄 Commit via GitWand
Right-clicking a branch checked out in a worktree now offers "Delete worktree" (and "Merge & delete" for scratch worktrees) instead of a plain branch delete that git rejects. Reuses AiTaskCloseModal via a new mode prop and reloads the branch list after removal. 🪄 Commit via GitWand
A branch checked out in a worktree can't be plain-checked-out, so switch to its worktree instead, and return to the main worktree before checking out non-worktree branches. Probe worktrees only when relevant. 🪄 Commit via GitWand
Prompt for a task name before creating the AI-task scratch worktree and slugify it backend-side into `gitwand-scratch-<slug>`, so tasks are recognisable on disk and in repo tabs instead of an opaque timestamp. Collisions append the timestamp; blank names fall back to it. 🪄 Commit via GitWand
Stream non-default locales on demand to keep ~9k lines of translation data out of the initial bundle (P1.2). Templates re-render reactively once a locale chunk resolves. 🪄 Commit via GitWand







Summary
Four terminal features for v3.1.0, plus a round of bug fixes and security hardening.
1. WebGL renderer + inline search + clickable links
@xterm/addon-search— Ctrl+F or toolbar, prev/next navigation@xterm/addon-web-links2. Tab types + unread indicator
shell,claude,codex— each with a distinct icon3. Fix agent launch — real PTY tab
4. "New AI task" — scratch worktree + Claude Code tab
POST /api/scratch-worktree-createusesrealpathto canonicalize the cwdBug fixes & hardening
terminal.rs): multibyte chars straddling 8 KB PTY read chunks were corrupted byfrom_utf8_lossy; fixed with avalid_up_to()carry-buffer loopApp.vue):TerminalPanelis lazy-loaded viadefineAsyncComponent; a singlenextTick()wasn't enough — fixed with awatch(terminalPanelRef)promise so the PTY starts only when the element is mountedEventSourcewas blocked; addedAccess-Control-Allow-Originto/api/terminal-openSSE headerspackage.jsonrootpostinstall): pnpm doesn't preserve +x on node-pty'sspawn-helperbinary, causingposix_spawnpfailures; fixed withfind node_modules/.pnpm -name 'spawn-helper' -exec chmod +x {} +safe_repo_path()on all FS ops, shell executable whitelist, PTY orphan/zombie prevention on tab close, EventSource closed on shell EOF to prevent browser auto-reconnect spawning untracked shells, keystroke input buffer boundedterminal.searchPrev/terminal.searchNext) added to all 5 locales (en, fr, es, pt-BR, zh-CN)pnpm dev:webTest plan
pnpm test— 320/320 passingcargo buildclean (no warnings)pnpm dev:web) — shell tab works, can type commandsecho "héllo 日本語") renders correctlyVue du Terminal dans GitWand

Zoom sur Codex
