fix: convert dragged file paths to @mentions for attachment
When non-image files are dragged into the terminal, the file path was inserted as plain text and never attached. Now detected absolute paths are converted to @mentions so they get picked up by the attachment system.
This commit is contained in:
@@ -67,6 +67,7 @@ import { isBilledAsExtraUsage } from '../../utils/extraUsage.js';
|
||||
import { getFastModeUnavailableReason, isFastModeAvailable, isFastModeCooldown, isFastModeEnabled, isFastModeSupportedByModel } from '../../utils/fastMode.js';
|
||||
import { isFullscreenEnvEnabled } from '../../utils/fullscreen.js';
|
||||
import type { PromptInputHelpers } from '../../utils/handlePromptSubmit.js';
|
||||
import { extractDraggedFilePaths } from '../../utils/dragDropPaths.js';
|
||||
import { getImageFromClipboard, PASTE_THRESHOLD } from '../../utils/imagePaste.js';
|
||||
import type { ImageDimensions } from '../../utils/imageResizer.js';
|
||||
import { cacheImagePath, storeImage } from '../../utils/imageStore.js';
|
||||
@@ -1204,6 +1205,22 @@ function PromptInput({
|
||||
// Clean up pasted text - strip ANSI escape codes and normalize line endings and tabs
|
||||
let text = stripAnsi(rawText).replace(/\r/g, '\n').replaceAll('\t', ' ');
|
||||
|
||||
// Detect file paths from drag-and-drop and convert to @mentions.
|
||||
// When files are dragged into the terminal, the terminal sends their
|
||||
// absolute paths via bracketed paste. Image files are handled by the
|
||||
// image paste handler upstream; here we handle non-image files by
|
||||
// converting them to @mentions so they get attached on submit.
|
||||
const draggedPaths = extractDraggedFilePaths(text);
|
||||
if (draggedPaths.length > 0) {
|
||||
const mentions = draggedPaths
|
||||
.map(p => (p.includes(' ') ? `@"${p}"` : `@${p}`))
|
||||
.join(' ');
|
||||
// Ensure spacing around the mention(s) relative to existing input
|
||||
const charBefore = input[cursorOffset - 1];
|
||||
const prefix = charBefore && !/\s/.test(charBefore) ? ' ' : '';
|
||||
text = prefix + mentions + ' ';
|
||||
}
|
||||
|
||||
// Match typed/auto-suggest: `!cmd` pasted into empty input enters bash mode.
|
||||
if (input.length === 0) {
|
||||
const pastedMode = getModeFromInput(text);
|
||||
|
||||
47
src/utils/dragDropPaths.ts
Normal file
47
src/utils/dragDropPaths.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { isAbsolute } from 'path'
|
||||
import { getFsImplementation } from './fsOperations.js'
|
||||
import { isImageFilePath } from './imagePaste.js'
|
||||
|
||||
/**
|
||||
* Detect absolute file paths in pasted text (typically from drag-and-drop).
|
||||
* Returns the cleaned paths if ALL segments are existing non-image files,
|
||||
* or an empty array otherwise.
|
||||
*
|
||||
* Splitting logic mirrors usePasteHandler: space preceding `/` or a Windows
|
||||
* drive letter, plus newline separators.
|
||||
*/
|
||||
export function extractDraggedFilePaths(text: string): string[] {
|
||||
const segments = text
|
||||
.split(/ (?=\/|[A-Za-z]:\\)/)
|
||||
.flatMap(part => part.split('\n'))
|
||||
.map(s => s.trim())
|
||||
.filter(Boolean)
|
||||
|
||||
if (segments.length === 0) return []
|
||||
|
||||
const fs = getFsImplementation()
|
||||
const cleaned: string[] = []
|
||||
|
||||
for (const raw of segments) {
|
||||
// Strip outer quotes and shell-escape backslashes
|
||||
let p = raw
|
||||
if (
|
||||
(p.startsWith('"') && p.endsWith('"')) ||
|
||||
(p.startsWith("'") && p.endsWith("'"))
|
||||
) {
|
||||
p = p.slice(1, -1)
|
||||
}
|
||||
if (process.platform !== 'win32') {
|
||||
p = p.replace(/\\(.)/g, '$1')
|
||||
}
|
||||
|
||||
if (!isAbsolute(p)) return []
|
||||
// Image files are handled by the upstream image paste handler
|
||||
if (isImageFilePath(raw)) return []
|
||||
// Verify the path actually exists on disk
|
||||
if (!fs.existsSync(p)) return []
|
||||
cleaned.push(p)
|
||||
}
|
||||
|
||||
return cleaned
|
||||
}
|
||||
Reference in New Issue
Block a user