Skip to content

Commit 193c46a

Browse files
committed
refactor(cli): extract readClipboardText utility and add processing feedback
- Extract clipboard text reading logic into readClipboardText() in clipboard-image.ts - Simplify onPasteImage handler in chat.tsx using the new utility - Add user feedback message when images are still processing in router.ts
1 parent 64247ca commit 193c46a

File tree

3 files changed

+55
-33
lines changed

3 files changed

+55
-33
lines changed

cli/src/chat.tsx

Lines changed: 13 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { routeUserPrompt, addBashMessageToHistory } from './commands/router'
1414
import { addClipboardPlaceholder, addPendingImageFromFile } from './utils/add-pending-image'
1515
import { getProjectRoot } from './project-files'
1616
import { AnnouncementBanner } from './components/announcement-banner'
17-
import { hasClipboardImage, readClipboardImage } from './utils/clipboard-image'
17+
import { hasClipboardImage, readClipboardImage, readClipboardText } from './utils/clipboard-image'
1818
import { showClipboardMessage } from './utils/clipboard'
1919
import { ChatInputBar } from './components/chat-input-bar'
2020
import { MessageWithAgents } from './components/message-with-agents'
@@ -1015,39 +1015,22 @@ export const Chat = ({
10151015
onBashHistoryUp: navigateUp,
10161016
onBashHistoryDown: navigateDown,
10171017
onPasteImage: () => {
1018-
// Show placeholder immediately for instant feedback
10191018
const placeholderPath = addClipboardPlaceholder()
10201019

1021-
// Check and process clipboard in background
10221020
setTimeout(() => {
10231021
if (!hasClipboardImage()) {
1024-
// No image - remove placeholder and simulate text paste
10251022
useChatStore.getState().removePendingImage(placeholderPath)
1026-
1027-
// Read text from clipboard and insert it
1028-
try {
1029-
const { spawnSync } = require('child_process')
1030-
const textResult = spawnSync(
1031-
process.platform === 'darwin' ? 'pbpaste' :
1032-
process.platform === 'win32' ? 'powershell' : 'xclip',
1033-
process.platform === 'win32' ? ['-Command', 'Get-Clipboard'] :
1034-
process.platform === 'linux' ? ['-selection', 'clipboard', '-o'] : [],
1035-
{ encoding: 'utf-8', timeout: 1000 }
1036-
)
1037-
if (textResult.status === 0 && textResult.stdout) {
1038-
const text = textResult.stdout
1039-
setInputValue((prev) => {
1040-
const before = prev.text.slice(0, prev.cursorPosition)
1041-
const after = prev.text.slice(prev.cursorPosition)
1042-
return {
1043-
text: before + text + after,
1044-
cursorPosition: before.length + text.length,
1045-
lastEditDueToNav: false,
1046-
}
1047-
})
1048-
}
1049-
} catch {
1050-
// Ignore errors - text paste just won't work
1023+
const text = readClipboardText()
1024+
if (text) {
1025+
setInputValue((prev) => {
1026+
const before = prev.text.slice(0, prev.cursorPosition)
1027+
const after = prev.text.slice(prev.cursorPosition)
1028+
return {
1029+
text: before + text + after,
1030+
cursorPosition: before.length + text.length,
1031+
lastEditDueToNav: false,
1032+
}
1033+
})
10511034
}
10521035
return
10531036
}
@@ -1065,7 +1048,7 @@ export const Chat = ({
10651048
void addPendingImageFromFile(result.imagePath, cwd, placeholderPath)
10661049
}, 0)
10671050

1068-
return true // Always consume - we handle text paste ourselves if needed
1051+
return true
10691052
},
10701053
}),
10711054
[

cli/src/commands/router.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,16 @@ import {
1616
import { getProjectRoot } from '../project-files'
1717
import { useChatStore } from '../state/chat-store'
1818
import { getSystemMessage, getUserMessage } from '../utils/message-history'
19-
import { capturePendingImages, hasProcessingImages, validateAndAddImage } from '../utils/add-pending-image'
19+
import {
20+
capturePendingImages,
21+
hasProcessingImages,
22+
validateAndAddImage,
23+
} from '../utils/add-pending-image'
2024
import {
2125
buildBashHistoryMessages,
2226
createRunTerminalToolResult,
2327
} from '../utils/bash-messages'
28+
import { showClipboardMessage } from '../utils/clipboard'
2429

2530
import type { PendingBashMessage } from '../state/chat-store'
2631

@@ -355,10 +360,12 @@ export async function routeUserPrompt(
355360
}
356361

357362
// Regular message or unknown slash command - send to agent
358-
363+
359364
// Block sending if images are still processing
360365
if (hasProcessingImages()) {
361-
// Don't send - images are still processing
366+
showClipboardMessage('processing images...', {
367+
durationMs: 2000,
368+
})
362369
return
363370
}
364371

cli/src/utils/clipboard-image.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,3 +301,35 @@ export function readClipboardImage(): ClipboardImageResult {
301301
}
302302
}
303303
}
304+
305+
/**
306+
* Read text from clipboard. Returns null if reading fails.
307+
*/
308+
export function readClipboardText(): string | null {
309+
try {
310+
const platform = process.platform
311+
let result: ReturnType<typeof spawnSync>
312+
313+
switch (platform) {
314+
case 'darwin':
315+
result = spawnSync('pbpaste', [], { encoding: 'utf-8', timeout: 1000 })
316+
break
317+
case 'win32':
318+
result = spawnSync('powershell', ['-Command', 'Get-Clipboard'], { encoding: 'utf-8', timeout: 1000 })
319+
break
320+
case 'linux':
321+
result = spawnSync('xclip', ['-selection', 'clipboard', '-o'], { encoding: 'utf-8', timeout: 1000 })
322+
break
323+
default:
324+
return null
325+
}
326+
327+
if (result.status === 0 && result.stdout) {
328+
const output = typeof result.stdout === 'string' ? result.stdout : result.stdout.toString('utf-8')
329+
return output.replace(/\n+$/, '')
330+
}
331+
return null
332+
} catch {
333+
return null
334+
}
335+
}

0 commit comments

Comments
 (0)