From d4fc4bef19e5e8cdcf384503dfe9795d64be1881 Mon Sep 17 00:00:00 2001
From: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Date: Sat, 28 Mar 2026 13:37:52 -0700
Subject: [PATCH] Support dragging source control files into terminals (#172)
---
.../right-sidebar/SourceControl.tsx | 6 ++++
.../terminal-pane/pane-helpers.test.ts | 30 +++++++++++++++++++
.../components/terminal-pane/pane-helpers.ts | 14 ++++++++-
3 files changed, 49 insertions(+), 1 deletion(-)
create mode 100644 src/renderer/src/components/terminal-pane/pane-helpers.test.ts
diff --git a/src/renderer/src/components/right-sidebar/SourceControl.tsx b/src/renderer/src/components/right-sidebar/SourceControl.tsx
index 4ad11dd99..ad82f8c95 100644
--- a/src/renderer/src/components/right-sidebar/SourceControl.tsx
+++ b/src/renderer/src/components/right-sidebar/SourceControl.tsx
@@ -238,6 +238,12 @@ export default function SourceControl(): React.JSX.Element {
{
+ const absolutePath = joinPath(worktreePath, entry.path)
+ e.dataTransfer.setData('text/x-orca-file-path', absolutePath)
+ e.dataTransfer.effectAllowed = 'copy'
+ }}
onClick={() => handleOpenDiff(entry)}
>
{
+ it('keeps safe POSIX paths unquoted', () => {
+ expect(shellEscapePath('/tmp/file.txt', 'Macintosh')).toBe('/tmp/file.txt')
+ })
+
+ it('single-quotes POSIX paths with shell-special characters', () => {
+ expect(shellEscapePath("/tmp/it's here.txt", 'Linux')).toBe("'/tmp/it'\\''s here.txt'")
+ })
+
+ it('keeps safe Windows paths unquoted', () => {
+ expect(shellEscapePath('C:\\Users\\orca\\file.txt', 'Windows')).toBe(
+ 'C:\\Users\\orca\\file.txt'
+ )
+ })
+
+ it('double-quotes Windows paths with spaces', () => {
+ expect(shellEscapePath('C:\\Users\\orca\\my file.txt', 'Windows')).toBe(
+ '"C:\\Users\\orca\\my file.txt"'
+ )
+ })
+
+ it('double-quotes Windows paths with cmd separators', () => {
+ expect(shellEscapePath('C:\\Users\\orca\\a&b.txt', 'Windows')).toBe(
+ '"C:\\Users\\orca\\a&b.txt"'
+ )
+ })
+})
diff --git a/src/renderer/src/components/terminal-pane/pane-helpers.ts b/src/renderer/src/components/terminal-pane/pane-helpers.ts
index 614e6e3e8..5a78c18e4 100644
--- a/src/renderer/src/components/terminal-pane/pane-helpers.ts
+++ b/src/renderer/src/components/terminal-pane/pane-helpers.ts
@@ -21,9 +21,21 @@ export function fitAndFocusPanes(manager: PaneManager): void {
focusActivePane(manager)
}
-export function shellEscapePath(path: string): string {
+function isWindowsUserAgent(userAgent: string): boolean {
+ return userAgent.includes('Windows')
+}
+
+export function shellEscapePath(
+ path: string,
+ userAgent: string = typeof navigator === 'undefined' ? '' : navigator.userAgent
+): string {
+ if (isWindowsUserAgent(userAgent)) {
+ return /^[a-zA-Z0-9_./@:\\-]+$/.test(path) ? path : `"${path}"`
+ }
+
if (/^[a-zA-Z0-9_./@:-]+$/.test(path)) {
return path
}
+
return `'${path.replace(/'/g, "'\\''")}'`
}