Skip to content

Terminal Improvement And GPU acceleration#79

Open
gajendraxdev wants to merge 12 commits into
zync-sh:mainfrom
gajendraxdev:main
Open

Terminal Improvement And GPU acceleration#79
gajendraxdev wants to merge 12 commits into
zync-sh:mainfrom
gajendraxdev:main

Conversation

@gajendraxdev

@gajendraxdev gajendraxdev commented Jun 17, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features

    • Added a GPU Acceleration (WebGL) toggle and Terminal Renderer Status diagnostics (including WebGL availability).
    • Improved terminal session performance with lazy WebGL activation and renderer status updates.
  • Bug Fixes

    • Hardened terminal input ordering/queueing and batching, including safer reconnect and session-ready handling.
    • Improved resize handling via a unified scheduler and reduced renderer transition artifacts.
    • Strengthened PTY lifecycle cleanup and connection/reconnect reliability.
  • Documentation

    • Updated Terminal roadmap and terminal-related documentation for the latest terminal stack behavior.
  • Tests

    • Added/expanded terminal renderer and reconnect/input test coverage, plus new terminal renderer test scripts.

Keep the xterm canvas in sync during layout transitions by always running visual fit while deferring PTY resize IPC until layout settles. Remove legacy width pinning, add refresh after fit, window-resize refit, and a layout-transition safety timeout. Ensure the terminal container fills its host via CSS.

Disable single-instance in debug builds so tauri dev no longer focuses the installed production app that shares the same identifier.
Introduce a modular terminal renderer stack with WebGL2 GPU rendering, explicit canvas fallback when GPU is disabled or WebGL is disposed, and compatible GPU + ligature activation. Settings include a GPU toggle, live renderer status for the active tab, and agent tests for policy, capability probing, session lifecycle, controller sync, and diagnostics.
Move Terminal.tsx under components/terminal/. Extract terminalCache, ligatures, renderer setup, and instance API into src/lib/terminal/. Update terminalSlice and aiContext to import from lib/terminal. Refresh TERMINAL_ROADMAP and related docs with current paths and GPU status.
…on guard

- Explicitly call child.kill() in PtyManager close paths (roadmap item 5.7) instead of relying on Drop
- Add reconnect_lock: Arc<Mutex<()>> to ConnectionHandle and take lock in reconnect_stored_connection
- Improve error handling on tunnel stop (only remove handles on full success)
- Remove .clone() on flush_deadline Instant in select branches
- Closes races on rapid disconnect/reconnect and ensures child processes are cleaned

Refs P1 5.7 + CodeRabbit reconnect / handle / clone comments.
…dingRestore on disconnect

- Add pendingInputBytes to TerminalCache and update incrementally instead of re-encoding full pendingInput on every keystroke (5.8)
- clearTerminalPendingInput also resets the byte counter
- clearTerminals accepts { preservePendingRestore?: boolean } and connectionSlice sets pendingRestore=true on SSH disconnect instead of wiping tabs (5.9)
- Remove sensitive console.log of fullConfig on connect (CodeRabbit)
- Improves keystroke perf and keeps terminal tabs alive across reconnect for better UX

Refs P1 5.8/5.9 + CodeRabbit zync-sh#1.
…sync and wakeup modules

- New focused modules under src/lib/terminal/: ptyLifecycle, terminalSpawn, terminalLazyPty, terminalConnectionWakeup, terminalLifecycleListeners, terminalResizeSync, terminalFit, inputPipeline, inputQueue, spawnContext, suspendAllTerminals, terminalSpawnErrors, connectionBackend, connectionOpQueue
- Centralize exports in index.ts; update Terminal.tsx / TerminalManager to delegate to lib
- Update connectionConfig, MainLayout, ConnectionItem for new wakeup / op queue behavior
- Enables Phase 1/2 exit criteria (lazy PTY, serialized input, gated flush, generation handling, reconnect wakeup)
- Big reduction in Terminal.tsx size and coupling

Part of terminal module reorganization.
…connect, spawn and renderer runner

- New dedicated tests: terminalPtyLifecycle, terminalInput*, terminalReconnect*, terminalSpawn*, terminalLifecycleIntegration, terminalConnectionWakeup, terminalResizeSync, connectionOpQueue, terminalSpawnContext/Errors
- Add and wire runTerminalRendererTests.mjs (addresses long chain in test:terminal-renderer)
- Update runAllAgentTests.mjs and tsconfig.agent-tests.json for new coverage
- Package.json now points test:terminal-renderer at the aggregator script

Covers P1 exit criteria and CodeRabbit test runner feedback.
- 8d17335: child kill (5.7) + reconnect lock + CR fixes in Rust
- 1fd77d5: pendingInputBytes (5.8) + preservePendingRestore (5.9) + remove console.log
- d15f536: terminal module extraction / lifecycle refactor
- a684bb5: tests + dedicated renderer test runner

Also marks roadmap items and includes .code-review-graph ignore (from prior graph tooling).
@gajendraxdev

Copy link
Copy Markdown
Member Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 14ec2a7f-4add-4108-8a68-8bc5676eeb54

📥 Commits

Reviewing files that changed from the base of the PR and between 3565cee and e14c8c2.

📒 Files selected for processing (16)
  • CHANGELOG.md
  • src-tauri/src/commands.rs
  • src-tauri/src/pty.rs
  • src/features/connections/infrastructure/connectionOpQueue.ts
  • src/lib/terminal/inputPipeline.ts
  • src/lib/terminal/inputQueue.ts
  • src/lib/terminal/ligatures.ts
  • src/lib/terminal/rendererController.ts
  • src/lib/terminal/terminalLifecycleListeners.ts
  • src/lib/terminal/terminalResizeSync.ts
  • src/store/connectionSlice.ts
  • src/store/terminalSlice.ts
  • tests/connectionOpQueue.test.mjs
  • tests/runTerminalRendererTests.mjs
  • tests/terminalReconnectReset.test.mjs
  • tests/terminalResizeSync.test.mjs
✅ Files skipped from review due to trivial changes (2)
  • tests/runTerminalRendererTests.mjs
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (12)
  • tests/terminalResizeSync.test.mjs
  • src/store/terminalSlice.ts
  • src/lib/terminal/terminalResizeSync.ts
  • src/features/connections/infrastructure/connectionOpQueue.ts
  • tests/connectionOpQueue.test.mjs
  • src/lib/terminal/terminalLifecycleListeners.ts
  • src/lib/terminal/inputPipeline.ts
  • tests/terminalReconnectReset.test.mjs
  • src-tauri/src/pty.rs
  • src-tauri/src/commands.rs
  • src/store/connectionSlice.ts
  • src/lib/terminal/rendererController.ts

📝 Walkthrough

Walkthrough

This PR extracts terminal lifecycle, input, renderer, sizing, and reconnect logic into src/lib/terminal/*, adds GPU renderer settings and diagnostics, hardens Rust PTY/reconnect handling, and rewires connection and terminal store flows to serialize operations and preserve terminal state.

Changes

Terminal Stack Modularization and GPU Renderer

Layer / File(s) Summary
Renderer contracts and session state
src/lib/terminal/types.ts, src/lib/terminal/rendererPolicy.ts, src/lib/terminal/webglCapability.ts, src/lib/terminal/rendererSession.ts, src/lib/terminal/terminalCache.ts, src/store/settingsSlice.ts, src/components/settings/constants/defaults.ts, tests/terminalRendererPolicy.test.mjs, tests/terminalRendererSession.test.mjs, tests/terminalWebglCapability.test.mjs, tests/terminalRendererDiagnostics.test.mjs, tests/terminalSpawnErrors.test.mjs
TerminalRendererKind/TerminalRendererState, renderer policy and diagnostics helpers, cached WebGL probing, renderer session registry, terminal cache, and GPU acceleration defaults are added and tested.
Renderer runtime and settings UI
src/lib/terminal/rendererLifecycle.ts, src/lib/terminal/rendererController.ts, src/lib/terminal/rendererSetup.ts, src/lib/terminal/ligatures.ts, src/components/settings/tabs/TerminalRendererStatus.tsx, src/components/settings/tabs/TerminalTab.tsx, tests/terminalRendererController.test.mjs, tests/terminalRendererPolicy.test.mjs, tests/terminalRendererDiagnostics.test.mjs
Canvas/WebGL switching, renderer teardown, reload/reactivation, ligature loading, renderer status display, and the GPU acceleration toggle are wired together.
Terminal input, PTY lifecycle, resize, and wakeup
src/lib/terminal/inputPipeline.ts, src/lib/terminal/inputQueue.ts, src/lib/terminal/ptyLifecycle.ts, src/lib/terminal/terminalSpawnErrors.ts, src/lib/terminal/spawnContext.ts, src/lib/terminal/terminalSpawn.ts, src/lib/terminal/suspendAllTerminals.ts, src/lib/terminal/connectionBackend.ts, src/lib/terminal/terminalFit.ts, src/lib/terminal/terminalLazyPty.ts, src/lib/terminal/terminalLifecycleListeners.ts, src/lib/terminal/terminalResizeSync.ts, src/lib/terminal/terminalConnectionWakeup.ts, src/lib/terminal/instanceApi.ts, tests/terminalInputPipeline.test.mjs, tests/terminalInputQueue.test.mjs, tests/terminalLazyPty.test.mjs, tests/terminalLifecycleIntegration.test.mjs, tests/terminalPtyLifecycle.test.mjs, tests/terminalReconnectFlow.test.mjs, tests/terminalReconnectReset.test.mjs, tests/terminalResizeSync.test.mjs, tests/terminalSpawnContext.test.mjs, tests/terminalSpawnErrors.test.mjs, tests/terminalConnectionWakeup.test.mjs
Input buffering and serialization, PTY spawn/suspend/reset, lazy PTY policy, lifecycle listener attachment, resize syncing, reconnect wakeup, and terminal instance cleanup are extracted into shared modules and covered by tests.
Connection and store wiring
src/features/connections/infrastructure/connectionOpQueue.ts, src/features/connections/domain/connectionConfig.ts, src/store/connectionSlice.ts, src/store/sessionSlice.ts, src/store/terminalSlice.ts, tests/connectionOpQueue.test.mjs, tests/connectionDomain.test.mjs
Connection operations are serialized, auto-connect behavior changes, backend liveness is tracked, terminal reconnect wakeup is triggered after connect, disconnect preserves pending restore state, and session restore now reconnects the active connection.
Terminal component refactor and UI wiring
src/components/terminal/Terminal.tsx, src/components/terminal/TerminalManager.tsx, src/components/layout/MainLayout.tsx, src/components/layout/sidebar/ConnectionItem.tsx, src/index.css, src/lib/aiContext.ts, src/lib/terminal/index.ts, src/components/settings/tabs/TerminalTab.tsx, src/components/settings/tabs/TerminalRendererStatus.tsx
Terminal rendering, layout opacity, click handling, terminal input dispatch, shared exports, CSS, and settings UI are updated to use the new terminal modules.
Rust PTY and reconnect backend
src-tauri/src/pty.rs, src-tauri/src/commands.rs, src-tauri/src/lib.rs
Local and remote PTY output batching is unified, local PTYs are explicitly killed on close, reconnects are serialized with a per-connection mutex, and connection-not-ready errors use a stable prefix.
Tests, docs, config, and scripts
tests/runAllAgentTests.mjs, tests/runTerminalRendererTests.mjs, package.json, tsconfig.agent-tests.json, CHANGELOG.md, docs/SESSION_PERSISTENCE.md, docs/TERMINAL_GHOST_SUGGESTIONS.md, docs/TERMINAL_ROADMAP.md, .gitignore
Adds terminal-focused test runners and compilation entries, updates changelog/roadmap/terminal docs, and ignores the generated review-graph directory.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • zync-sh/zync#38: Directly related to PTY output batching and local/remote terminal event flow in src-tauri/src/pty.rs.
  • zync-sh/zync#43: Related to terminal reconnect and wakeup behavior; this PR refactors that flow into shared terminal modules and store wiring.
  • zync-sh/zync#65: Related to terminal settings and the GPU/WebGL toggle area in src/components/settings/tabs/TerminalTab.tsx.

Poem

🐇 I hopped through queues and renderer glow,
WebGL and canvas now both know the flow.
PTYs wake, then rest, then start anew,
with terminal state kept tidy and true.
A carrot for caches, a wiggle for speed —
this bunny approves of every byte indeed.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Note

Unit test generation is a public access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

❌ Failed to create PR with unit tests: AGENT_CHAT: Failed to open pull request

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/store/terminalSlice.ts (1)

224-239: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

preservePendingRestore still tears down terminal instances before preserving tab metadata.

In preserve mode, the function currently executes terminal:kill and destroyTerminalInstance(...) first, which defeats the reconnect-preserve behavior and can drop in-memory terminal state before tabs are marked pending restore.

Suggested fix
     clearTerminals: (connectionId, options = {}) => {
         set(state => {
-            // Kill all known terminals for this connection and destroy cached instances
             const tabs = state.terminals[connectionId] || [];
-            tabs.forEach(t => {
-                ipc.send('terminal:kill', { termId: t.id });
-                destroyTerminalInstance(t.id);
-            });
 
             if (options.preservePendingRestore) {
                 // Preserve tab list with pendingRestore metadata for SSH reconnect/restore flow (roadmap 5.9)
                 // Do not delete active/synced so reconnect can wake the tabs.
                 return {
                     terminals: {
                         ...state.terminals,
                         [connectionId]: tabs.map(t => ({ ...t, pendingRestore: true }))
                     },
                 };
             }
 
+            // Kill all known terminals for this connection and destroy cached instances
+            tabs.forEach(t => {
+                ipc.send('terminal:kill', { termId: t.id });
+                destroyTerminalInstance(t.id);
+            });
+
             const newTerminals = { ...state.terminals };
             delete newTerminals[connectionId];
🤖 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/store/terminalSlice.ts` around lines 224 - 239, The terminal kill and
destroy operations in the forEach loop (containing `ipc.send('terminal:kill')`
and `destroyTerminalInstance(t.id)`) are executing unconditionally before
checking the `preservePendingRestore` flag, which means terminals are being
destroyed even when preservation is intended. Move the entire forEach loop that
kills and destroys terminals inside a conditional block so that it only executes
when `preservePendingRestore` is false or not set, allowing terminals to be
preserved intact when the flag is true.
🧹 Nitpick comments (2)
tests/terminalResizeSync.test.mjs (1)

20-26: ⚡ Quick win

Restore the previous globalThis.window after this test module finishes.

This test mutates a process-global and never restores it, which can leak into subsequent test modules when run in a single Node process.

Suggested fix
+const previousWindow = globalThis.window;
-globalThis.window = {
+globalThis.window = {
   ipcRenderer: {
     send: (_channel, payload) => {
       ipcResizes.push(payload);
     },
   },
 };
 
-runTest('syncTerminalResize skips IPC when cache entry is missing', () => {
+try {
+runTest('syncTerminalResize skips IPC when cache entry is missing', () => {
   terminalCache.clear();
   ipcResizes.length = 0;
   syncTerminalResize(SESSION, term);
   assert.equal(ipcResizes.length, 0);
 });
@@
 runTest('syncTerminalResize sends IPC when PTY is live', () => {
@@
 });
 
 console.log('Terminal resize sync tests passed.');
+} finally {
+  terminalCache.clear();
+  if (previousWindow === undefined) {
+    delete globalThis.window;
+  } else {
+    globalThis.window = previousWindow;
+  }
+}
🤖 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 `@tests/terminalResizeSync.test.mjs` around lines 20 - 26, The test module is
modifying globalThis.window without restoring it after the tests complete, which
can cause side effects for other tests run in the same process. Save the
original value of globalThis.window before the test setup (where ipcRenderer is
being mocked), and add a cleanup function using an appropriate test framework
hook (such as afterEach or after) to restore the original globalThis.window
value after the tests finish running.
src/features/connections/infrastructure/connectionOpQueue.ts (1)

3-24: ⚡ Quick win

Clean up settled queue tails from opChains to avoid unbounded key retention.

opChains never deletes completed entries, so removed/ephemeral connection IDs accumulate over uptime.

Suggested fix
   const previous = opChains.get(connectionId) ?? Promise.resolve();
   const next = previous
     .catch(() => {})
     .then(() => op());
 
-  opChains.set(
-    connectionId,
-    next.then(
-      () => undefined,
-      () => undefined,
-    ),
-  );
+  const tail = next.then(
+    () => undefined,
+    () => undefined,
+  );
+  opChains.set(connectionId, tail);
+  tail.finally(() => {
+    if (opChains.get(connectionId) === tail) {
+      opChains.delete(connectionId);
+    }
+  });
 
   return next;
 }
🤖 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/features/connections/infrastructure/connectionOpQueue.ts` around lines 3
- 24, The opChains Map accumulates entries indefinitely without removing
completed operations, causing memory bloat over time. In the
runSerializedConnectionOp function, after setting the next operation in the map,
add cleanup logic to delete the connectionId entry from opChains once the
promise settles (regardless of success or failure). This can be done by chaining
an additional handler after the existing .then() calls that resolves to
undefined, which removes the entry from the map using
opChains.delete(connectionId).
🤖 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 `@CHANGELOG.md`:
- Around line 7-37: The CHANGELOG.md [Unreleased] section contains duplicate
"### Added" headings (appearing twice) and duplicate "### Fixed" headings
(appearing twice), which violates markdown linting rules (MD024). Consolidate
all entries by merging the first "### Added" block (containing Terminal Phase 2
Lifecycle Extraction, Phase 1 Hardening, Lazy PTY Spawn, etc.) with the second
"### Added" block (containing Terminal GPU Acceleration, GPU + Font Ligatures,
etc.) into a single unified "### Added" section. Similarly, merge the first "###
Fixed" block (Connecting Screen Transparency, Terminal External Writes, Input
Queue Drain, etc.) with the second "### Fixed" block (Terminal Resize Hardening,
Dev Single-Instance Focus Steal, GPU Off Blank Terminal, etc.) into a single
"### Fixed" section. Delete all duplicate heading lines so only one "### Added"
and one "### Fixed" heading remain under [Unreleased].

In `@src-tauri/src/commands.rs`:
- Around line 1840-1849: The code holds the state.connections lock while
awaiting the reconnect_lock, which can deadlock if a concurrent reconnect needs
state.connections while holding reconnect_lock. Fix this by acquiring the
reconnect_lock reference and releasing the state.connections lock before
awaiting the reconnect_lock - restructure the code to extract the clone of
h.reconnect_lock inside the lock scope, then drop the connections lock before
calling lock_owned().await. Additionally, ensure the same reconnect_lock
instance is preserved when replacing the handle (also applies to the code around
line 1899-1901) so that all waiters continue using the same lock instead of
splitting across old and new locks.

In `@src-tauri/src/pty.rs`:
- Around line 779-788: The close() method now explicitly kills the child process
for TerminalHandle::Local using child.kill(), but the close_by_connection()
method (lines 813-820) only aborts tasks without killing the child process. This
can leave the local shell process alive when closing by connection. Apply the
same explicit child process cleanup pattern used in close() to the
TerminalHandle::Local variant in close_by_connection() by adding the let _ =
child.kill(); call after handling the task aborts.

In `@src/lib/terminal/inputPipeline.ts`:
- Around line 35-38: The code in the section where cached.pendingInput is being
cleared (setting it to empty string and cached.pendingInputBytes to 0) happens
before the window.ipcRenderer.send() call completes. Move the clearing of
cached.pendingInput and cached.pendingInputBytes to occur after the
window.ipcRenderer.send() call succeeds, or wrap the send call in error handling
to restore the data if the send fails. This ensures that if the IPC write throws
an exception, the buffered input data is not lost.

In `@src/lib/terminal/inputQueue.ts`:
- Around line 22-40: The clearTerminalInputQueue function deletes the queue
entry immediately after bumping the epoch, which allows new tasks to start
executing before the current pre-clear task has finished executing. This breaks
per-session serialization and can cause PTY writes to interleave. Instead of
deleting the sessionQueues entry, you should either keep it as-is (the epoch
bump will prevent stale tasks from executing anyway) or ensure the deletion only
happens after the current promise in the queue has settled by waiting for it to
complete first before removing the entry from sessionQueues.

In `@src/lib/terminal/ligatures.ts`:
- Around line 41-47: The `cached.ligaturesEnabled` flag becomes stale because it
is not explicitly set to false when `disposeTerminalLigatures` is called,
leaving the flag in an inconsistent state with the actual addon lifecycle.
Ensure that whenever `disposeTerminalLigatures(cached)` is executed, the
`cached.ligaturesEnabled` property is explicitly set to false either within the
`disposeTerminalLigatures` function itself or immediately before the function
returns, so that the cached state accurately reflects that the ligatures addon
has been disabled.

In `@src/lib/terminal/rendererController.ts`:
- Around line 104-106: The issue is that when desiredKind changes while a
loadPromise is still in-flight, the method returns the old loadPromise without
reconciling to the new desiredKind. Fix this in the loadSync method by adding a
check after confirming rendererState.loadPromise exists: verify that the current
kind matches the desiredKind, and if they differ, do not return the stale
loadPromise but instead proceed to create a new transition to the updated
desiredKind. This same pattern needs to be applied at both locations where
loadPromise is returned (around lines 104-106 and 132-134) to ensure the
renderer state is reconciled whenever a transition completes and the desired
mode has changed in the interim.

In `@src/lib/terminal/terminalLifecycleListeners.ts`:
- Around line 27-37: Add proper error handling to all three listen() calls by
attaching .catch() handlers to each promise chain to handle registration
failures. Refactor the listener registration logic into a helper function that
properly manages the async flow, ensuring that cached.listenerAttached is only
set to true after all three listen() registrations complete successfully. In the
.then() callbacks for each listen() call, ensure the returned unlistenFn is
always stored in the terminalCache entry's unlisten array (initialize the array
if it doesn't exist), and handle the edge case where the cache entry is
unexpectedly absent by logging a warning and still returning the cleanup
function to prevent listener leaks. This prevents unhandled promise rejections,
ensures the listenerAttached flag accurately reflects registration state, and
guarantees cleanup functions are stored for proper teardown.

In `@src/lib/terminal/terminalResizeSync.ts`:
- Around line 21-22: The cached.lastResize state is being updated before the
window.ipcRenderer.send call for the terminal:resize event, which means if the
send operation fails, the cache is already marked as updated and subsequent
retries for the same size will be suppressed. Move the cached.lastResize =
nextSize assignment to execute only after confirming that
window.ipcRenderer.send successfully completes, or wrap the cache update in
error handling to ensure the state is only cached when the resize dispatch
actually succeeds.

In `@src/store/connectionSlice.ts`:
- Around line 531-542: The return statement at line 541 inside the finally block
is unsafe because it can suppress exceptions thrown from earlier code paths.
Remove the return statement from this finally block in the disconnect flow.
Instead, restructure the code to handle the early exit logic in the main
conditional block before the set call completes, so that the return happens in
the normal control flow rather than in the finally block. Ensure the logic that
maps terminals with pendingRestore: true and calls markConnectionStatus still
executes correctly without needing the finally block return.

In `@tests/connectionOpQueue.test.mjs`:
- Around line 4-12: The `runTest` function needs to properly handle async test
functions. Make the `runTest` function itself async and await the call to `fn()`
so that async test bodies complete before logging success. This ensures the
try-catch block properly catches any errors thrown during async test execution
and prevents false passes when tests are still pending.

In `@tests/runTerminalRendererTests.mjs`:
- Around line 3-18: The tests array is missing two terminal regression test
files that are part of the terminal behavior coverage. Add the missing test
files 'tests/terminalSpawnErrors.test.mjs' and
'tests/terminalReconnectFlow.test.mjs' to the tests array in the appropriate
positions to ensure complete regression test coverage when running the terminal
renderer test suite.

In `@tests/terminalReconnectReset.test.mjs`:
- Around line 20-22: The mock send function in the test is only capturing the
payload parameter into ipcKills but ignoring the _channel parameter, which means
the test cannot verify that the correct IPC channel name is being sent. Modify
the send mock function to capture both the _channel and payload (perhaps as an
object with channel and payload properties) into ipcKills so that assertions can
verify the correct IPC event name is being used in addition to the payload
content. Apply this fix to both occurrences of the send function mock.

---

Outside diff comments:
In `@src/store/terminalSlice.ts`:
- Around line 224-239: The terminal kill and destroy operations in the forEach
loop (containing `ipc.send('terminal:kill')` and
`destroyTerminalInstance(t.id)`) are executing unconditionally before checking
the `preservePendingRestore` flag, which means terminals are being destroyed
even when preservation is intended. Move the entire forEach loop that kills and
destroys terminals inside a conditional block so that it only executes when
`preservePendingRestore` is false or not set, allowing terminals to be preserved
intact when the flag is true.

---

Nitpick comments:
In `@src/features/connections/infrastructure/connectionOpQueue.ts`:
- Around line 3-24: The opChains Map accumulates entries indefinitely without
removing completed operations, causing memory bloat over time. In the
runSerializedConnectionOp function, after setting the next operation in the map,
add cleanup logic to delete the connectionId entry from opChains once the
promise settles (regardless of success or failure). This can be done by chaining
an additional handler after the existing .then() calls that resolves to
undefined, which removes the entry from the map using
opChains.delete(connectionId).

In `@tests/terminalResizeSync.test.mjs`:
- Around line 20-26: The test module is modifying globalThis.window without
restoring it after the tests complete, which can cause side effects for other
tests run in the same process. Save the original value of globalThis.window
before the test setup (where ipcRenderer is being mocked), and add a cleanup
function using an appropriate test framework hook (such as afterEach or after)
to restore the original globalThis.window value after the tests finish running.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 52af6cd4-cc39-437e-b74c-9eda1adaf05b

📥 Commits

Reviewing files that changed from the base of the PR and between 09648dc and 3565cee.

📒 Files selected for processing (70)
  • .gitignore
  • CHANGELOG.md
  • docs/SESSION_PERSISTENCE.md
  • docs/TERMINAL_GHOST_SUGGESTIONS.md
  • docs/TERMINAL_ROADMAP.md
  • package.json
  • src-tauri/src/commands.rs
  • src-tauri/src/lib.rs
  • src-tauri/src/pty.rs
  • src/components/layout/MainLayout.tsx
  • src/components/layout/sidebar/ConnectionItem.tsx
  • src/components/settings/constants/defaults.ts
  • src/components/settings/tabs/TerminalRendererStatus.tsx
  • src/components/settings/tabs/TerminalTab.tsx
  • src/components/terminal/Terminal.tsx
  • src/components/terminal/TerminalManager.tsx
  • src/features/connections/domain/connectionConfig.ts
  • src/features/connections/infrastructure/connectionOpQueue.ts
  • src/index.css
  • src/lib/aiContext.ts
  • src/lib/terminal/connectionBackend.ts
  • src/lib/terminal/index.ts
  • src/lib/terminal/inputPipeline.ts
  • src/lib/terminal/inputQueue.ts
  • src/lib/terminal/instanceApi.ts
  • src/lib/terminal/ligatures.ts
  • src/lib/terminal/ptyLifecycle.ts
  • src/lib/terminal/rendererController.ts
  • src/lib/terminal/rendererDiagnostics.ts
  • src/lib/terminal/rendererLifecycle.ts
  • src/lib/terminal/rendererPolicy.ts
  • src/lib/terminal/rendererSession.ts
  • src/lib/terminal/rendererSetup.ts
  • src/lib/terminal/spawnContext.ts
  • src/lib/terminal/suspendAllTerminals.ts
  • src/lib/terminal/terminalCache.ts
  • src/lib/terminal/terminalConnectionWakeup.ts
  • src/lib/terminal/terminalFit.ts
  • src/lib/terminal/terminalLazyPty.ts
  • src/lib/terminal/terminalLifecycleListeners.ts
  • src/lib/terminal/terminalResizeSync.ts
  • src/lib/terminal/terminalSpawn.ts
  • src/lib/terminal/terminalSpawnErrors.ts
  • src/lib/terminal/types.ts
  • src/lib/terminal/webglCapability.ts
  • src/store/connectionSlice.ts
  • src/store/sessionSlice.ts
  • src/store/settingsSlice.ts
  • src/store/terminalSlice.ts
  • tests/connectionDomain.test.mjs
  • tests/connectionOpQueue.test.mjs
  • tests/runAllAgentTests.mjs
  • tests/runTerminalRendererTests.mjs
  • tests/terminalConnectionWakeup.test.mjs
  • tests/terminalInputPipeline.test.mjs
  • tests/terminalInputQueue.test.mjs
  • tests/terminalLazyPty.test.mjs
  • tests/terminalLifecycleIntegration.test.mjs
  • tests/terminalPtyLifecycle.test.mjs
  • tests/terminalReconnectFlow.test.mjs
  • tests/terminalReconnectReset.test.mjs
  • tests/terminalRendererController.test.mjs
  • tests/terminalRendererDiagnostics.test.mjs
  • tests/terminalRendererPolicy.test.mjs
  • tests/terminalRendererSession.test.mjs
  • tests/terminalResizeSync.test.mjs
  • tests/terminalSpawnContext.test.mjs
  • tests/terminalSpawnErrors.test.mjs
  • tests/terminalWebglCapability.test.mjs
  • tsconfig.agent-tests.json

Comment thread CHANGELOG.md
Comment thread src-tauri/src/commands.rs Outdated
Comment thread src-tauri/src/pty.rs
Comment thread src/lib/terminal/inputPipeline.ts Outdated
Comment thread src/lib/terminal/inputQueue.ts
Comment thread src/lib/terminal/terminalResizeSync.ts Outdated
Comment thread src/store/connectionSlice.ts Outdated
Comment thread tests/connectionOpQueue.test.mjs Outdated
Comment thread tests/runTerminalRendererTests.mjs
Comment thread tests/terminalReconnectReset.test.mjs Outdated
- CHANGELOG: deduplicate ### Added/### Fixed headings under Unreleased
- commands.rs: fix reconnect_lock acquisition (no lock held across await) + preserve same lock instance on handle replace
- pty.rs: add explicit child.kill() in close_by_connection for Local
- inputPipeline.ts: clear pendingInput after send
- inputQueue.ts: avoid deleting queue entry on clear (epoch suffices)
- ligatures.ts: set ligaturesEnabled=false on dispose
- rendererController.ts: reconcile desiredKind before returning stale loadPromise
- terminalLifecycleListeners.ts: add .catch handlers + set listenerAttached correctly
- terminalResizeSync.ts: update lastResize after successful send
- connectionSlice.ts: remove return from finally block
- terminalSlice.ts: only kill/destroy when not preserving pendingRestore
- connectionOpQueue.ts: add cleanup for opChains entries
- Tests: make runTest async, capture channel in mocks, restore globalThis.window, add missing test files to runner

All items verified against current code and fixed minimally.
- Added reference to 579efb4 for the review fixes.
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