Skip to content

fix(windows): address clipboard copy failure in TUI#8997

Open
zerafachris wants to merge 1 commit into
stablyai:mainfrom
zerafachris:fix-windows-clipboard-8977
Open

fix(windows): address clipboard copy failure in TUI#8997
zerafachris wants to merge 1 commit into
stablyai:mainfrom
zerafachris:fix-windows-clipboard-8977

Conversation

@zerafachris

Copy link
Copy Markdown

Fixes #8977

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The clipboard IPC handler now validates text before applying platform-specific write behavior. Windows uses PowerShell with Electron clipboard fallback, while other platforms continue using Electron directly. The terminal pane selection handler now separates clipboard write success and failure handling, with TODO placeholders for toast notifications.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description only says 'Fixes #8977' and omits the required summary, testing, review, security, and notes sections. Add the repository's template sections: Summary, Screenshots, Testing, AI Review Report, Security Audit, and Notes.
Linked Issues check ⚠️ Warning The PR improves Windows clipboard writing, but it does not implement the required failure-aware 'Copied' toast behavior for #8977. Wire the terminal copy flow to show success only after clipboard write succeeds and suppress or replace the toast on failure.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately reflects the Windows clipboard copy fix in the TUI.
Out of Scope Changes check ✅ Passed The changes stay focused on Windows clipboard handling and terminal selection copy behavior.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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: 2

🧹 Nitpick comments (1)
src/renderer/src/components/terminal-pane/use-terminal-pane-lifecycle.ts (1)

1136-1140: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Address the TODO comments for toast notifications.

Would you like me to generate a solution that implements these toast notifications using your project's toast dispatcher (or open a new issue to track this)?

If you already have a toast function in scope (e.g., from sonner or shadcn), you can replace these placeholders with it.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 2383da48-ae5c-44e4-9e92-80f3a526c6cd

📥 Commits

Reviewing files that changed from the base of the PR and between cf02cc4 and a85d801.

📒 Files selected for processing (2)
  • src/main/window/clipboard-ipc-handlers.ts
  • src/renderer/src/components/terminal-pane/use-terminal-pane-lifecycle.ts

Comment on lines +145 to +156
const newText = await assertClipboardTextWriteWithinLimitWithYield(text)
if (process.platform === 'win32') {
try {
// Use a PowerShell fallback for Windows to improve reliability
await runCommand('powershell', ['-Command', `Set-Clipboard -Value $input`], newText)
} catch (error) {
console.error('PowerShell clipboard write failed, falling back to Electron API', error)
clipboard.writeText(newText)
}
} else {
clipboard.writeText(newText)
}

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.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Test PowerShell Set-Clipboard with stdin

cat << 'EOF' > test-ps.js
const { spawn } = require('child_process');

function runCommand(command, args, stdin) {
  return new Promise((resolve, reject) => {
    const child = spawn(command, args, { stdio: ['pipe', 'ignore', 'ignore'] });
    child.on('error', reject);
    child.on('exit', (code) => code === 0 ? resolve() : reject(new Error('exit ' + code)));
    child.stdin.end(stdin);
  });
}

async function test() {
  const text = "Hello\nWorld 🌍";
  await runCommand('powershell', ['-Command', 'Set-Clipboard -Value $input'], text);
  console.log("Done");
}
test();
EOF

# Note: The sandbox runs Linux, so powershell.exe is not available to test directly.
# However, you can review PowerShell documentation regarding `$input` and InputEncoding.

Repository: stablyai/orca

Length of output: 151


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant file and helper locations first.
git ls-files src/main/window/clipboard-ipc-handlers.ts src/main | sed -n '1,120p'

echo
echo '--- outline: clipboard-ipc-handlers.ts ---'
ast-grep outline src/main/window/clipboard-ipc-handlers.ts --view expanded || true

echo
echo '--- search runCommand / Set-Clipboard / powershell ---'
rg -n "runCommand\\(|Set-Clipboard|powershell|InputEncoding|clipboard.writeText|assertClipboardTextWriteWithinLimitWithYield" src/main -S

Repository: stablyai/orca

Length of output: 50369


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- clipboard-ipc-handlers.ts slice ---'
sed -n '130,170p' src/main/window/clipboard-ipc-handlers.ts | cat -n

echo
echo '--- clipboard-ipc-handlers.test.ts relevant slice ---'
sed -n '200,280p' src/main/window/clipboard-ipc-handlers.test.ts | cat -n

echo
echo '--- runCommand implementation in clipboard-ipc-handlers.ts ---'
sed -n '55,80p' src/main/window/clipboard-ipc-handlers.ts | cat -n

Repository: stablyai/orca

Length of output: 7438


🌐 Web query:

PowerShell $input stdin Set-Clipboard -Value $input encoding newline handling powershell.exe documentation

💡 Result:

In PowerShell, the $input variable is an automatic, enumerator-based variable that captures all data passed to a script, function, or script block via the pipeline [1][2]. When you pipe data to Set-Clipboard, the cmdlet does not directly read from a standard input stream (stdin) in the traditional shell sense; rather, it receives objects directly from the PowerShell pipeline [3]. Regarding the behavior of Set-Clipboard: 1. Newline Handling: By design, Set-Clipboard, when receiving multiple objects or string input through the pipeline, may append newlines between items depending on how the input is passed and formatted [4][5]. It is known to perform a ToString conversion on objects by default, which can lead to unexpected formatting if not explicitly controlled (e.g., using Out-String -Stream before piping to Set-Clipboard) [6]. 2. Encoding: Set-Clipboard is a high-level PowerShell cmdlet designed to abstract clipboard access across platforms (PowerShell 7.x+) or leverage Windows APIs [3][7]. It generally manages text encoding automatically to ensure compatibility with the clipboard's requirements, avoiding the manual character encoding complexities associated with legacy external utilities like clip.exe [8][9]. 3. Stdin vs. Pipeline: It is important to distinguish that $input is for handling pipeline-bound data within PowerShell code, not for reading stdin from external processes or redirection in the way that native Linux or Windows CMD shells operate [10]. If you need to relay pipeline data to an external application that expects actual stdin, you must explicitly pipe $input to that application [10][11]. 4. Documentation: Official documentation for Set-Clipboard is available via the Microsoft Learn platform [4]. For specific nuances regarding $input, refer to the about_Automatic_Variables help topic in PowerShell [1]. Because Set-Clipboard is an internal cmdlet, it does not share the same stdin encoding limitations that external binaries (like clip.exe) often encounter when they interpret input based on the active console's code page [12][9].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- src/main/window/clipboard-ipc-handlers.ts lines 138-158 ---'
sed -n '138,158p' src/main/window/clipboard-ipc-handlers.ts | cat -n

echo
echo '--- src/main/window/clipboard-ipc-handlers.test.ts lines 210-260 ---'
sed -n '210,260p' src/main/window/clipboard-ipc-handlers.test.ts | cat -n

echo
echo '--- src/main/window/clipboard-file-copy.ts lines 44-82 ---'
sed -n '44,82p' src/main/window/clipboard-file-copy.ts | cat -n

echo
echo '--- src/main/ipc/pty.test.ts lines 6440-6455 ---'
sed -n '6440,6455p' src/main/ipc/pty.test.ts | cat -n

Repository: stablyai/orca

Length of output: 6363


🌐 Web query:

PowerShell $input Set-Clipboard stdin UTF-8 InputEncoding multiline string behavior official documentation

💡 Result:

When piping multiline string input to the Set-Clipboard cmdlet in PowerShell, the encoding and handling of that input depend on how it is read into the system. Official documentation states that Set-Clipboard accepts string values [1][2], and when you pipe data to it, PowerShell processes that input as strings [2]. However, direct piping (e.g., echo "multiline" | Set-Clipboard) does not always implicitly read the entire stdin buffer using UTF-8; instead, it relies on the host's current input encoding settings [3][4]. On Windows, the primary issue is that [Console]::InputEncoding frequently defaults to the system's legacy OEM/ANSI code page (e.g., CP1252) rather than UTF-8 [5]. Consequently, if your input contains non-ASCII characters, reading that input via [Console]::In.ReadToEnd or similar methods may result in character corruption (mojibake) because the bytes are decoded using the legacy code page [5]. To ensure correct UTF-8 handling for multiline input when using Set-Clipboard, you must explicitly set the input encoding before reading the stream [5]. The recommended approach is to include the following configuration in your script [5]: [Console]::InputEncoding = [System.Text.Encoding]::UTF8 $input | Set-Clipboard Alternatively, to avoid encoding issues entirely, you can encode your input as Base64 before passing it to PowerShell, then decode it within the session [5]: $bytes = [Convert]::FromBase64String([Console]::In.ReadToEnd) [System.Text.Encoding]::UTF8.GetString($bytes) | Set-Clipboard Regarding multiline string behavior, Set-Clipboard preserves line breaks correctly as part of the string object [1][2]. Unlike the legacy clip.exe utility, which often appends a trailing newline to the clipboard contents [6][7], Set-Clipboard handles the piped string content as provided [7]. If you encounter unexpected trailing newlines or empty lines, ensure your input generation command does not add them (e.g., use Out-String -NoNewLine) [7].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- src/main/window/clipboard-ipc-handlers.ts 60-75 ---'
sed -n '60,75p' src/main/window/clipboard-ipc-handlers.ts | cat -n

echo
echo '--- src/main/window/clipboard-ipc-handlers.ts 140-156 ---'
sed -n '140,156p' src/main/window/clipboard-ipc-handlers.ts | cat -n

echo
echo '--- src/main/window/clipboard-ipc-handlers.test.ts 220-255 ---'
sed -n '220,255p' src/main/window/clipboard-ipc-handlers.test.ts | cat -n

Repository: stablyai/orca

Length of output: 3856


🌐 Web query:

site:learn.microsoft.com PowerShell about_Automatic_Variables input stdin pipeline $input

💡 Result:

The $input automatic variable in PowerShell is an enumerator that provides access to input objects passed to a function, scriptblock, or script file via the pipeline [1][2][3]. It is only available within these specific contexts [1][2]. Key characteristics and behaviors of $input include: Availability and Scope: $input is available in functions, scriptblocks (including unnamed ones), and script files [1][2]. It is also available to commands executed via the -Command parameter of the pwsh executable [1][2]. Enumeration: Because $input is an enumerator, it can be used to iterate over the collection of all input objects [1][2]. Once you access its properties, the enumerator may be consumed; to reuse the data, you should store the contents of $input in another variable [1][2]. Behavior in Function Blocks: - Without begin, process, or end blocks: $input enumerates all input passed to the function [1][2]. - In a process block: $input contains the current object in the pipeline [1][2]. Note that the process block automatically advances the enumerator [1][2]. - In an end block: $input enumerates all input passed to the function [1][2]. - You cannot use $input in both the process block and the end block within the same function or scriptblock [1][2]. Important Note on Pipelines: While $input relates to PowerShell pipeline objects, it is distinct from the stdin stream used in other shells [4]. PowerShell does not connect native stdin directly to the PowerShell pipeline [4]. For standard advanced functions, it is generally recommended to use defined parameters with ValueFromPipeline support instead of relying on $input [5]. The automatic variables $_ or $PSItem are typically used to access the current pipeline object within a process block, rather than $input [5].

Citations:


🌐 Web query:

Microsoft Learn PowerShell Console InputEncoding native stdin about_Character_Encoding $input automatic variable

💡 Result:

In PowerShell, character encoding for stdin and interaction with native programs is managed through specific configuration variables and.NET properties. Character Encoding and Stdin PowerShell handles character encoding for stdin and native program communication via the automatic variable $OutputEncoding and the.NET property [Console]::InputEncoding [1][2]. 1. $OutputEncoding: This automatic variable determines the encoding PowerShell uses when piping data to external (native) programs via stdin [1][3]. It does not affect redirection operators (>, >>) or cmdlet file output, which are controlled by other mechanisms like $PSDefaultParameterValues [1]. 2. [Console]::InputEncoding: This.NET property controls the encoding the console uses to read input [4][5]. It maps to the Windows SetConsoleCP function [2]. For consistent behavior with native applications, it is widely recommended to keep $OutputEncoding and [Console]::InputEncoding synchronized [2]. Mismatches between these settings often lead to character corruption when passing non-ASCII data to native commands [2][6]. The common practice to ensure alignment is: [Console]::InputEncoding = [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 $input Automatic Variable The $input automatic variable is used to access input passed to a function, scriptblock, or script file [7][8]. - Nature of $input: It is an enumerator, not a standard collection [8][9]. Because it is an enumerator, it can only be consumed once [9]. Accessing it—or using methods like MoveNext—advances the enumerator, and once the data has been read or the enumerator is fully consumed, it will appear empty [8][9]. - Scope and Behavior: - In a function without a process block, $input enumerates all pipeline input provided to that function [8]. - In a process block, $input typically enumerates the single object currently in the pipeline (though developers generally prefer using $_ or $PSItem in this context to avoid consuming the enumerator) [8][10]. - Once accessed, the data is removed from the enumerator [8][9]. If you need to reuse the input, you should store the contents of $input in another variable (e.g., $data = $input) before accessing it [8]. References: [11] about_Character_Encoding - PowerShell | Microsoft Learn [1] about_Automatic_Variables - PowerShell | Microsoft Learn [4] Console.InputEncoding Property (System) - Microsoft Learn [5] Details about console encoding in PowerShell (GitHub Gist) [2] PowerShell/PowerShell Issue #8584 (GitHub)

Citations:


Set the Windows PowerShell fallback to UTF-8 powershell.exe can decode stdin with the legacy console code page here, so non-ASCII or multiline clipboard text can be mangled, and $input is an enumerator rather than a raw stdin string. Set [Console]::InputEncoding to UTF-8 and pipe the text into Set-Clipboard explicitly.

Comment on lines +1133 to +1140
void window.api.ui
.writeClipboardText(selection)
.then(() => {
// TODO(orca): Show a "Copied" toast notification
})
.catch(() => {
// TODO(orca): Show a "Copy failed" toast notification
})

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.

🩺 Stability & Availability | 🔴 Critical | 🏗️ Heavy lift

Debounce the clipboard write to prevent a powershell.exe fork bomb.

Because xterm fires onSelectionChange continuously while the user drags their mouse to select text, this writeClipboardText call executes dozens of times per second. Previously, this only resulted in redundant fast Electron API calls. However, with the Windows platform changes in this PR, each call will now spawn a powershell.exe process.

This will cause severe CPU spikes, UI freezing, and clipboard write failures on Windows when "copy on select" (terminalClipboardOnSelect) is enabled.

Move this call inside the existing window.setTimeout block (which already debounces the primary-selection write for this exact reason) or add a new debounce timer for it.

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.

[Bug]: Windows: TUI (e.g. OpenCode) copy shows "Copied" but clipboard is empty (works on macOS)

2 participants