Skip to content

Commit 6322911

Browse files
committed
Copy fix for tmux (only prioritize osc52 method for remote sessions)
1 parent 0bbe35c commit 6322911

File tree

4 files changed

+53
-16
lines changed

4 files changed

+53
-16
lines changed

cli/src/testing/env.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ export const createTestCliEnv = (overrides: Partial<CliEnv> = {}): CliEnv => ({
99
...createTestBaseEnv(),
1010

1111
// CLI-specific defaults
12+
SSH_CLIENT: undefined,
13+
SSH_TTY: undefined,
14+
SSH_CONNECTION: undefined,
1215
KITTY_WINDOW_ID: undefined,
1316
SIXEL_SUPPORT: undefined,
1417
ZED_NODE_ENV: undefined,

cli/src/types/env.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ export type CliEnv = BaseEnv & {
2121
TMUX?: string
2222
STY?: string
2323

24+
// SSH/remote session detection
25+
SSH_CLIENT?: string
26+
SSH_TTY?: string
27+
SSH_CONNECTION?: string
28+
2429
// Terminal-specific
2530
KITTY_WINDOW_ID?: string
2631
SIXEL_SUPPORT?: string

cli/src/utils/clipboard.ts

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -83,22 +83,17 @@ export async function copyTextToClipboard(
8383
}
8484

8585
try {
86-
// Try OSC52 first (works over SSH/headless), then fallback to platform tools
87-
if (!tryCopyViaOsc52(text)) {
88-
const { execSync } = require('child_process') as typeof import('child_process')
89-
const opts = { input: text, stdio: ['pipe', 'ignore', 'ignore'] as ('pipe' | 'ignore')[] }
90-
91-
if (process.platform === 'darwin') {
92-
execSync('pbcopy', opts)
93-
} else if (process.platform === 'linux') {
94-
try {
95-
execSync('xclip -selection clipboard', opts)
96-
} catch {
97-
execSync('xsel --clipboard --input', opts)
98-
}
99-
} else if (process.platform === 'win32') {
100-
execSync('clip', opts)
101-
}
86+
let copied: boolean
87+
if (isRemoteSession()) {
88+
// Remote/SSH: prefer OSC 52 (copies to client terminal's clipboard)
89+
copied = tryCopyViaOsc52(text) || tryCopyViaPlatformTool(text)
90+
} else {
91+
// Local: prefer platform tools (reliable with tmux), OSC 52 as fallback
92+
copied = tryCopyViaPlatformTool(text) || tryCopyViaOsc52(text)
93+
}
94+
95+
if (!copied) {
96+
throw new Error('No clipboard method available')
10297
}
10398

10499
if (!suppressGlobalMessage) {
@@ -137,6 +132,35 @@ export function clearClipboardMessage() {
137132
// because the client terminal handles clipboard. Format: ESC ] 52 ; c ; <base64> BEL
138133
// tmux/screen require passthrough wrapping to forward the sequence.
139134

135+
function isRemoteSession(): boolean {
136+
const env = getCliEnv()
137+
return !!(env.SSH_CLIENT || env.SSH_TTY || env.SSH_CONNECTION)
138+
}
139+
140+
function tryCopyViaPlatformTool(text: string): boolean {
141+
const { execSync } = require('child_process') as typeof import('child_process')
142+
const opts = { input: text, stdio: ['pipe', 'ignore', 'ignore'] as ('pipe' | 'ignore')[] }
143+
144+
try {
145+
if (process.platform === 'darwin') {
146+
execSync('pbcopy', opts)
147+
} else if (process.platform === 'linux') {
148+
try {
149+
execSync('xclip -selection clipboard', opts)
150+
} catch {
151+
execSync('xsel --clipboard --input', opts)
152+
}
153+
} else if (process.platform === 'win32') {
154+
execSync('clip', opts)
155+
} else {
156+
return false
157+
}
158+
return true
159+
} catch {
160+
return false
161+
}
162+
}
163+
140164
// 32KB is safe for all environments (tmux is the strictest)
141165
const OSC52_MAX_PAYLOAD = 32_000
142166

cli/src/utils/env.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ export const getCliEnv = (): CliEnv => ({
2121
TMUX: process.env.TMUX,
2222
STY: process.env.STY,
2323

24+
// SSH/remote session detection
25+
SSH_CLIENT: process.env.SSH_CLIENT,
26+
SSH_TTY: process.env.SSH_TTY,
27+
SSH_CONNECTION: process.env.SSH_CONNECTION,
28+
2429
// Terminal detection
2530
KITTY_WINDOW_ID: process.env.KITTY_WINDOW_ID,
2631
SIXEL_SUPPORT: process.env.SIXEL_SUPPORT,

0 commit comments

Comments
 (0)