diff --git a/frontend/src/components/panels/TerminalPanel.tsx b/frontend/src/components/panels/TerminalPanel.tsx index 34ea82fc..7f1b96bc 100644 --- a/frontend/src/components/panels/TerminalPanel.tsx +++ b/frontend/src/components/panels/TerminalPanel.tsx @@ -93,6 +93,7 @@ export const TerminalPanel: React.FC = React.memo(({ panel, const terminalState = panel.state?.customState as TerminalPanelState | undefined; const isCliPanel = !!terminalState?.isCliPanel; const [isCliReady, setIsCliReady] = useState(!!terminalState?.isCliReady); + const isCliPanelRef = useRef(isCliPanel); // ptyId for the current PTY behind this panel, delivered via // `terminal:ptyReady` when spawned through the ptyHost UtilityProcess. @@ -119,6 +120,10 @@ export const TerminalPanel: React.FC = React.memo(({ panel, // Sync isCliReady from panel prop when it changes (e.g. backend persisted isCliReady // before this component subscribed to the IPC event, or panel state was updated externally) + useEffect(() => { + isCliPanelRef.current = isCliPanel; + }, [isCliPanel]); + useEffect(() => { if (terminalState?.isCliReady && !isCliReady) { setIsCliReady(true); @@ -822,6 +827,21 @@ export const TerminalPanel: React.FC = React.memo(({ panel, // The "[Image] " prefix we used to add actually broke the parser's // path-detection — on Windows+WSL it caused Claude to cache the file but // never attach it to the API call (see commit 7b76ee5). + const pasteText = (text: string) => { + if (!terminal) return; + const shouldProtectMultilinePaste = isCliPanelRef.current && !tuiActiveRef.current && /[\r\n]/.test(text); + if (shouldProtectMultilinePaste) { + window.electronAPI.invoke( + 'terminal:input', + panel.id, + text.replace(/\r\n|\r|\n/g, '\x1b\r'), + ); + return; + } + + terminal.paste(text); + }; + const handlePaste = (e: ClipboardEvent) => { // Step 1: Check for images in browser clipboard (works on native Windows/macOS) const items = e.clipboardData?.items; @@ -895,7 +915,7 @@ export const TerminalPanel: React.FC = React.memo(({ panel, // No image found — forward the text content xterm would have pasted. if (text && !disposed && terminal) { - terminal.paste(text); + pasteText(text); } })(); };