Skip to content

terminal#12

Merged
anirudhisonline merged 3 commits into
mainfrom
terminal
May 18, 2026
Merged

terminal#12
anirudhisonline merged 3 commits into
mainfrom
terminal

Conversation

@anirudhisonline
Copy link
Copy Markdown
Contributor

@anirudhisonline anirudhisonline commented May 18, 2026

Summary by CodeRabbit

  • New Features

    • Integrated terminal with multiple concurrent tabs, rename and kill workflows
    • Automatic shell detection and picker; create terminals with chosen shell and args
    • Real-time PTY-backed terminal I/O with resize, write, and clean shutdown
    • Clickable links, optional GPU acceleration, auto-resize and live theme sync
  • Chores

    • Added terminal UI libraries and native PTY support for cross-platform sessions

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 18, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ceb28305-ea7c-4d85-955e-c1a2b164e941

📥 Commits

Reviewing files that changed from the base of the PR and between 727eaea and 8958856.

📒 Files selected for processing (2)
  • src/lib/components/panels/TerminalPanel.svelte
  • src/lib/stores/workspace.svelte.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/lib/stores/workspace.svelte.js
  • src/lib/components/panels/TerminalPanel.svelte

📝 Walkthrough

Walkthrough

Adds end-to-end terminal support: OS shell detection and PTY sessions in the Rust backend (portable-pty), Tauri invoke handlers and JS wrappers, xterm.js-based TerminalTab component, TerminalPanel UI for shell selection/rename/kill, and app layout integration.

Changes

Terminal/PTY Feature

Layer / File(s) Summary
Dependencies and configuration
package.json, src-tauri/Cargo.toml, .claude/settings.json
Adds xterm frontend packages, portable-pty Rust crate, and an allowed CLI command entry for PowerShell.
Backend module exports and handler registration
src-tauri/src/commands/mod.rs, src-tauri/src/lib.rs
Exports terminal module, registers TerminalState in app state, and wires terminal invoke handlers.
Shell detection and PTY session state
src-tauri/src/commands/terminal.rs (lines 1–176)
Implements TerminalState and OS-specific terminal_list_shells() returning ShellInfo entries.
PTY session lifecycle commands
src-tauri/src/commands/terminal.rs (lines 180–311)
Implements terminal_create (opens PTY, spawns shell, reader thread emitting base64 terminal:data:{sid} and terminal:exit:{sid}), terminal_write, terminal_resize, and terminal_close.
Frontend JavaScript bridge to Tauri
src/lib/commands/terminal.js
Exports terminalListShells, terminalCreate, terminalWrite, terminalResize, and terminalClose wrappers that invoke Tauri commands.
Terminal panel for shell selection and tab management
src/lib/components/panels/TerminalPanel.svelte
Loads shells on mount, New Terminal split button with shell picker, per-tab inline rename and kill confirmation, and renders terminal tab list or empty state.
xterm.js terminal emulator component
src/lib/components/workspace/TerminalTab.svelte
Creates xterm with CSS-var theming, loads Fit/WebLinks/WebGL addons, subscribes to Tauri output/exit events, forwards input to backend, syncs size via ResizeObserver and reactive effects, and performs cleanup on destroy.
App layout tool, panel, and tab integration
src/routes/app/+layout.svelte
Adds Terminal icon, integrates TerminalPanel into sidebar when active, mounts TerminalTab for tab.type === 'terminal', and updates tool/tab icon mappings.
Workspace store update
src/lib/stores/workspace.svelte.js
Adds renameTab(id, title) to update tab titles.

Sequence Diagram

sequenceDiagram
  participant User
  participant TerminalPanel as TerminalPanel UI
  participant JS as JS Bridge (src/lib/commands/terminal.js)
  participant Rust as Rust Backend (src-tauri)
  participant PTY as PTY/Shell
  participant TerminalTab as TerminalTab UI

  User->>TerminalPanel: Click "New Terminal"
  TerminalPanel->>JS: terminalListShells()
  JS->>Rust: invoke terminal_list_shells
  Rust->>JS: return ShellInfo[]
  JS->>TerminalPanel: shells array
  TerminalPanel->>TerminalPanel: render shell picker

  User->>TerminalPanel: select shell
  TerminalPanel->>JS: terminalCreate(sessionId, cwd, cols, rows, shell, shellArgs)
  JS->>Rust: invoke terminal_create
  Rust->>PTY: open PTY and spawn shell
  Rust->>Rust: start reader thread
  Rust->>JS: emit terminal:data:{sid} (base64)
  JS->>TerminalTab: deliver event
  TerminalTab->>TerminalTab: render output in xterm

  User->>TerminalTab: type command
  TerminalTab->>JS: terminalWrite(sessionId, data)
  JS->>Rust: invoke terminal_write
  Rust->>PTY: write to PTY writer
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • anide-app/anide#6: Related layout/tab UI changes that interact with terminal panel and tab rendering.

Poem

🐰 I tunneled in code to build a shell,
Threads hum, base64 blossoms tell,
xterm paints the prompt so bright,
Tabs renamed, killed with gentle might,
Now terminals purr into the night.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The pull request title 'terminal' is vague and does not clearly convey the main changes—adding terminal/PTY functionality, xterm.js integration, and related UI components. Use a more descriptive title that captures the primary change, such as 'Add terminal/PTY support with xterm.js integration' or 'Implement in-app terminal with shell enumeration'.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch terminal

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
src/lib/components/panels/TerminalPanel.svelte (1)

56-63: ⚡ Quick win

Expose dropdown state to assistive tech.

The shell-picker toggle should include aria-expanded and aria-controls so screen readers can track open/closed state.

♿ Suggested patch
       <button
         type="button"
         aria-label="Pick shell"
+        aria-expanded={dropdownOpen}
+        aria-controls="terminal-shell-picker"
         onclick={() => (dropdownOpen = !dropdownOpen)}
         class="flex items-center justify-center px-1.5 rounded-r-md border border-dashed border-border/60
           text-muted-foreground hover:text-foreground hover:border-border hover:bg-muted/50 transition-colors
           {dropdownOpen ? 'bg-muted/50 text-foreground border-border' : ''}"
       >
         <ChevronDown size={12} class="transition-transform {dropdownOpen ? 'rotate-180' : ''}" />
       </button>
@@
-      <div class="absolute left-2 right-2 top-full mt-1 z-50 rounded-md border bg-popover shadow-md overflow-hidden">
+      <div id="terminal-shell-picker" class="absolute left-2 right-2 top-full mt-1 z-50 rounded-md border bg-popover shadow-md overflow-hidden">

Also applies to: 72-73

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/components/panels/TerminalPanel.svelte` around lines 56 - 63, The
shell-picker toggle button that flips the local dropdownOpen state should expose
that state to assistive tech by adding aria-expanded={dropdownOpen} and
aria-controls="shell-dropdown" (or another unique id) to the button, and ensure
the dropdown element rendered when dropdownOpen is used has id="shell-dropdown"
(and appropriate role like "menu" or "listbox") so screen readers can associate
the control with the popup; apply the same aria-expanded/aria-controls change to
the other toggle referenced (the second dropdown toggle) and ensure ids are
unique if both dropdowns exist.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.claude/settings.json:
- Line 11: Replace the Bash(...) wrapper around the PowerShell cmdlet with a
PowerShell(...) wrapper and remove the hardcoded Windows path: change the
"Bash(Get-ChildItem -Path \"d:\\\\takerest\\\\gits\\\\takerest\" -Force)" string
to use PowerShell(Get-ChildItem -Path $Env:TAKEREST_ROOT -Force) or
PowerShell(Get-ChildItem -Path (Join-Path $Env:USERPROFILE
'takerest\\gits\\takerest') -Force) so the command runs in PowerShell and the
path is environment-driven/portable; ensure proper quoting/escaping for the JSON
string and reference the original Bash(...), Get-ChildItem, and the settings
entry when making the edit.

In `@src-tauri/src/commands/terminal.rs`:
- Around line 278-286: The current code silently does nothing when a session id
is missing (the if let Some(session) = sessions.get_mut(&session_id) { ... }
branches), which hides state drift; update these code paths (where you access
sessions, e.g., the write path using session.writer.write_all(data.as_bytes()),
the resize path, and the close/send paths that use sessions.get/_get_mut) to
return an explicit error when the session is not found instead of succeeding.
Replace the if-let-no-op with a match or an early-return check that returns
Err(...) (use a clear message including the session_id, e.g., format!("session
not found: {}", session_id) converted to the function's error type) so callers
receive a failure when the session id is unknown.
- Around line 259-267: The current code uses
state.sessions.lock().unwrap().insert(session_id, PtySession { ... }) which will
silently replace an existing session and leak the previous PTY; change this to
first check for an existing entry for session_id and avoid replacing it (return
an error) or, if replacement is intended, first remove the existing session and
gracefully terminate it before inserting the new one. Concretely, use
state.sessions.lock().unwrap().entry(session_id) to detect Existing vs Vacant
and either return Err/early when the entry is Occupied, or call remove/take the
old PtySession and call its child.kill()/cleanup methods (and close its writer)
before inserting the new PtySession so you don’t leave the previous PTY running.

In `@src/lib/components/workspace/TerminalTab.svelte`:
- Around line 109-111: The fire-and-forget terminal RPC calls (e.g., the onData
handler where _term.onData calls terminalWrite, and the other async RPC
invocations around the same area) can produce unhandled promise rejections when
sessions close or races occur; update each fire-and-forget call (specifically
the terminalWrite invocation inside _term.onData and the other two async RPC
calls referenced in the review) to append .catch(() => {}) so rejections are
swallowed while preserving the current non-await, latency-sensitive behavior.
- Around line 42-122: The async onMount flow can complete after onDestroy and
leak listeners/PTYs; add a local "mounted" (or "alive") boolean/flag set true at
start and set false in onDestroy, then after each await (listen(...) and
terminalCreate(...)) check the flag: if false then immediately cleanup any
resources returned (call the obtained _unlistenData/_unlistenExit functions,
dispose _webgl/_fit/_term/_ro/_themeObserver as appropriate) and do not call
terminalCreate or _term.write; also guard any subsequent uses of _term (e.g. in
the terminalWrite onData handler and ResizeObserver callback) so they no-op if
not mounted. Ensure you reference and manage the symbols _unlistenData,
_unlistenExit, listen, terminalCreate, _term, _webgl, _fit, _ro, _themeObserver
and onDestroy when implementing this check-and-cleanup flow.

---

Nitpick comments:
In `@src/lib/components/panels/TerminalPanel.svelte`:
- Around line 56-63: The shell-picker toggle button that flips the local
dropdownOpen state should expose that state to assistive tech by adding
aria-expanded={dropdownOpen} and aria-controls="shell-dropdown" (or another
unique id) to the button, and ensure the dropdown element rendered when
dropdownOpen is used has id="shell-dropdown" (and appropriate role like "menu"
or "listbox") so screen readers can associate the control with the popup; apply
the same aria-expanded/aria-controls change to the other toggle referenced (the
second dropdown toggle) and ensure ids are unique if both dropdowns exist.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 308fa548-9227-45cb-986f-67ba6ff4dce9

📥 Commits

Reviewing files that changed from the base of the PR and between ba842b7 and f3973ba.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • src-tauri/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • .claude/settings.json
  • package.json
  • src-tauri/Cargo.toml
  • src-tauri/src/commands/mod.rs
  • src-tauri/src/commands/terminal.rs
  • src-tauri/src/lib.rs
  • src/lib/commands/terminal.js
  • src/lib/components/panels/TerminalPanel.svelte
  • src/lib/components/workspace/TerminalTab.svelte
  • src/routes/app/+layout.svelte

Comment thread .claude/settings.json
Comment thread src-tauri/src/commands/terminal.rs Outdated
Comment thread src-tauri/src/commands/terminal.rs
Comment thread src/lib/components/workspace/TerminalTab.svelte
Comment thread src/lib/components/workspace/TerminalTab.svelte
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/lib/components/panels/TerminalPanel.svelte (1)

31-33: ⚡ Quick win

Don’t silently swallow shell discovery failures.

At Line 32, catch {} hides actionable errors and makes “no shells found” indistinguishable from “failed to load shells.” Capture an error state so the UI can show a distinct failure message.

Proposed patch
   let shells = $state([]);
+  let shellsLoadError = $state('');
@@
   onMount(async () => {
-    try { shells = await terminalListShells(); } catch {}
+    try {
+      shells = await terminalListShells();
+      shellsLoadError = '';
+    } catch (err) {
+      shellsLoadError = 'Failed to load shells';
+      shells = [];
+    }
   });
@@
-        {`#if` shells.length === 0}
-          <p class="px-3 py-2 text-xs text-muted-foreground">No shells detected</p>
+        {`#if` shellsLoadError}
+          <p class="px-3 py-2 text-xs text-destructive">{shellsLoadError}</p>
+        {:else if shells.length === 0}
+          <p class="px-3 py-2 text-xs text-muted-foreground">No shells detected</p>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/components/panels/TerminalPanel.svelte` around lines 31 - 33, The
current onMount block swallows errors from terminalListShells() (onMount,
terminalListShells, shells) which prevents the UI from distinguishing "no
shells" from "failed to load"; update the catch to capture the thrown error and
set a dedicated error state (e.g., shellsError or shellsLoadError) and/or a
boolean (e.g., shellsLoadingFailed) so the component can render a distinct
failure message instead of silently doing nothing; ensure you still preserve any
existing fallback for shells (e.g., empty array) but populate the new error
state with the caught error (or a normalized message) for the template to read.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/components/panels/TerminalPanel.svelte`:
- Around line 113-123: The each block in TerminalPanel.svelte currently keys
shell rows by shell.program which may not be unique; update the key for the
{`#each` shells as shell (shell.program)} loop to a composite key combining
program, args and name (for example using shell.program + '-' +
shell.args.join(',') + '-' + shell.name) so each shell entry is unique; ensure
you reference the same shell variable used by openTerminal and the Terminal
component and handle cases where shell.args may be undefined (coerce to empty
array or string) when building the composite key.

In `@src/lib/stores/workspace.svelte.js`:
- Around line 87-90: renameTab currently accepts any input and can set a tab
title to blank/whitespace; update the store-level renameTab(tabId, title) to
validate and normalize the title before mutating tabs: trim the incoming title,
reject or fallback to a safe default (e.g., keep the existing tab.title or use
"Untitled") when the trimmed string is empty, and only assign the normalized
value to the matching tab found via tabs.find(t => t.id === id); ensure you
reference the renameTab function and the tabs collection when making this
change.

---

Nitpick comments:
In `@src/lib/components/panels/TerminalPanel.svelte`:
- Around line 31-33: The current onMount block swallows errors from
terminalListShells() (onMount, terminalListShells, shells) which prevents the UI
from distinguishing "no shells" from "failed to load"; update the catch to
capture the thrown error and set a dedicated error state (e.g., shellsError or
shellsLoadError) and/or a boolean (e.g., shellsLoadingFailed) so the component
can render a distinct failure message instead of silently doing nothing; ensure
you still preserve any existing fallback for shells (e.g., empty array) but
populate the new error state with the caught error (or a normalized message) for
the template to read.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0c3aed7d-b3b7-487a-b125-2e92a56348cb

📥 Commits

Reviewing files that changed from the base of the PR and between f3973ba and 727eaea.

📒 Files selected for processing (4)
  • src-tauri/src/commands/terminal.rs
  • src/lib/components/panels/TerminalPanel.svelte
  • src/lib/components/workspace/TerminalTab.svelte
  • src/lib/stores/workspace.svelte.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/lib/components/workspace/TerminalTab.svelte
  • src-tauri/src/commands/terminal.rs

Comment thread src/lib/components/panels/TerminalPanel.svelte Outdated
Comment thread src/lib/stores/workspace.svelte.js
@anirudhisonline anirudhisonline merged commit 31a754d into main May 18, 2026
1 check passed
@anirudhisonline anirudhisonline deleted the terminal branch May 18, 2026 16:42
@coderabbitai coderabbitai Bot mentioned this pull request May 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant