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 { getFastModeUnavailableReason, isFastModeAvailable, isFastModeCooldown, isFastModeEnabled, isFastModeSupportedByModel } from '../../utils/fastMode.js';
|
||||||
import { isFullscreenEnvEnabled } from '../../utils/fullscreen.js';
|
import { isFullscreenEnvEnabled } from '../../utils/fullscreen.js';
|
||||||
import type { PromptInputHelpers } from '../../utils/handlePromptSubmit.js';
|
import type { PromptInputHelpers } from '../../utils/handlePromptSubmit.js';
|
||||||
|
import { extractDraggedFilePaths } from '../../utils/dragDropPaths.js';
|
||||||
import { getImageFromClipboard, PASTE_THRESHOLD } from '../../utils/imagePaste.js';
|
import { getImageFromClipboard, PASTE_THRESHOLD } from '../../utils/imagePaste.js';
|
||||||
import type { ImageDimensions } from '../../utils/imageResizer.js';
|
import type { ImageDimensions } from '../../utils/imageResizer.js';
|
||||||
import { cacheImagePath, storeImage } from '../../utils/imageStore.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
|
// Clean up pasted text - strip ANSI escape codes and normalize line endings and tabs
|
||||||
let text = stripAnsi(rawText).replace(/\r/g, '\n').replaceAll('\t', ' ');
|
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.
|
// Match typed/auto-suggest: `!cmd` pasted into empty input enters bash mode.
|
||||||
if (input.length === 0) {
|
if (input.length === 0) {
|
||||||
const pastedMode = getModeFromInput(text);
|
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