Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 80 additions & 4 deletions src/utils/clipboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,34 @@ class Clipboard {
}
} else if (process.platform === 'linux') {
try {
// Use file:// URI format for Linux clipboard compatibility
const fileUri = url.pathToFileURL(filePath).toString();
await this.copyText(fileUri);
const method = Clipboard._detectLinuxClipboardMethod(fileUri);

if (!method) {
// Neither xclip nor wl-copy available; fall back to plain-text URI
return this.copyText(fileUri);
}

const result = spawnSync(method.tool, method.args, {
input: method.payload,
stdio: ['pipe', 'pipe', 'pipe'],
timeout: 2000,
});

if (result.error || result.status !== 0) {
logger.debug(
`Failed to copy file reference using ${method.tool}, falling back to text:`,
result.error?.message || `exit code ${result.status}`,
);
return this.copyText(fileUri);
}
} catch (error) {
logger.debug('Failed to copy file URI, falling back to text path:', error.message);
return this.copyText(filePath);
logger.debug(
'Failed to copy file reference on Linux, falling back to text:',
error.message,
);
const fileUri = url.pathToFileURL(filePath).toString();
return this.copyText(fileUri);
}
} else if (process.platform === 'darwin') {
try {
Expand All @@ -78,6 +100,60 @@ tell app "Finder" to set the clipboard to aFile`;
}
}

/**
* Detect the correct Linux clipboard tool, MIME type, and payload for file references.
*
* Desktop environment detection:
* - GTK-based (GNOME, XFCE, Cinnamon, MATE, Budgie): uses `x-special/gnome-copied-files`
* - KDE/Qt: uses `text/uri-list`
* - Unknown: defaults to GTK format (more widely supported)
*
* Display protocol detection:
* - Wayland ($WAYLAND_DISPLAY set): uses `wl-copy`
* - X11 ($DISPLAY set): uses `xclip`
* - Neither: returns null (no clipboard tool available)
*
* @param {string} fileUri - The file:// URI to place on the clipboard
* @returns {{ tool: string, args: string[], payload: string } | null}
* @private
*/
static _detectLinuxClipboardMethod(fileUri) {
const desktop = (process.env.XDG_CURRENT_DESKTOP || '').toUpperCase();
const isKDE = desktop.includes('KDE');

// Determine MIME type and payload based on desktop environment
let mimeType;
let payload;
if (isKDE) {
mimeType = 'text/uri-list';
payload = `${fileUri}\r\n`;
} else {
// GTK-based desktops (GNOME, XFCE, Cinnamon, MATE, Budgie) and unknown DEs
mimeType = 'x-special/gnome-copied-files';
payload = `copy\n${fileUri}`;
}

// Determine clipboard tool based on display protocol
if ((process.env.WAYLAND_DISPLAY || '').trim()) {
return {
tool: 'wl-copy',
args: ['--type', mimeType],
payload,
};
}

if ((process.env.DISPLAY || '').trim()) {
return {
tool: 'xclip',
args: ['-selection', 'clipboard', '-t', mimeType],
payload,
};
}

// No display server detected
return null;
}

/**
* Reveal file in file manager (cross-platform)
* @param {string} filePath - The file path to reveal
Expand Down
Loading
Loading