Skip to content

Commit cec983b

Browse files
maskarjohannesjo
andauthored
feat(terminal): paste clipboard images as temp file paths (#42)
* feat(terminal): paste clipboard images as temp file paths When Cmd+V is pressed and the clipboard contains an image instead of text, save it to a temp PNG file and paste the file path into the terminal. Useful for CLI tools that accept image paths. * fix(clipboard): use async write, add error handling, prevent temp file leak - Use async fs.promises.writeFile instead of writeFileSync to avoid blocking the main process on large clipboard images - Add try/catch returning null on write failure for graceful degradation - Use a fixed filename to prevent unbounded temp file accumulation - Add .catch() on paste IIFE to prevent unhandled rejections --------- Co-authored-by: maskar <Maskar@users.noreply.github.com> Co-authored-by: Johannes Millan <johannes.millan@gmail.com>
1 parent 774ffe2 commit cec983b

4 files changed

Lines changed: 31 additions & 4 deletions

File tree

electron/ipc/channels.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ export enum IPC {
102102
OpenPath = 'open_path',
103103
ReadFileText = 'read_file_text',
104104

105+
// Clipboard
106+
SaveClipboardImage = 'save_clipboard_image',
107+
105108
// Notifications
106109
ShowNotification = 'show_notification',
107110
NotificationClicked = 'notification_clicked',

electron/ipc/register.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { ipcMain, dialog, shell, app, BrowserWindow, Notification } from 'electron';
1+
import { ipcMain, dialog, shell, app, clipboard, BrowserWindow, Notification } from 'electron';
22
import fs from 'fs';
3+
import os from 'os';
34
import { fileURLToPath } from 'url';
45
import { IPC } from './channels.js';
56
import {
@@ -463,6 +464,20 @@ export function registerAllHandlers(win: BrowserWindow): void {
463464
return fs.readFileSync(args.filePath, 'utf8');
464465
});
465466

467+
// --- Clipboard ---
468+
const clipboardImagePath = path.join(os.tmpdir(), 'parallel-code-clipboard.png');
469+
ipcMain.handle(IPC.SaveClipboardImage, async () => {
470+
try {
471+
const img = clipboard.readImage();
472+
if (img.isEmpty()) return null;
473+
const buf = img.toPNG();
474+
await fs.promises.writeFile(clipboardImagePath, buf);
475+
return clipboardImagePath;
476+
} catch {
477+
return null;
478+
}
479+
});
480+
466481
// --- System ---
467482
ipcMain.handle(IPC.GetSystemFonts, () => getSystemMonospaceFonts());
468483

electron/preload.cjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ const ALLOWED_CHANNELS = new Set([
9393
// File links
9494
'open_path',
9595
'read_file_text',
96+
// Clipboard
97+
'save_clipboard_image',
9698
// Notifications
9799
'show_notification',
98100
'notification_clicked',

src/components/TerminalView.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,16 @@ export function TerminalView(props: TerminalViewProps) {
195195

196196
if (isPaste) {
197197
e.preventDefault();
198-
navigator.clipboard.readText().then((text) => {
199-
if (text) enqueueInput(text);
200-
});
198+
(async () => {
199+
const text = await navigator.clipboard.readText().catch(() => '');
200+
if (text) {
201+
enqueueInput(text);
202+
return;
203+
}
204+
// Fall back to clipboard image → save to temp file and paste path
205+
const filePath = await invoke<string | null>(IPC.SaveClipboardImage);
206+
if (filePath) enqueueInput(filePath);
207+
})().catch(() => {});
201208
return false;
202209
}
203210

0 commit comments

Comments
 (0)