mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-05 08:10:14 +00:00
Merge pull request #1190 from RooVetGit/file_drag_drop
File drag and drop
This commit is contained in:
commit
199b6eaec1
8 changed files with 105 additions and 6 deletions
5
.changeset/shaggy-spies-kneel.md
Normal file
5
.changeset/shaggy-spies-kneel.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"roo-cline": patch
|
||||
---
|
||||
|
||||
Add drag-and-drop for files
|
||||
|
|
@ -2421,6 +2421,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
|
||||
const allowedCommands = vscode.workspace.getConfiguration("roo-cline").get<string[]>("allowedCommands") || []
|
||||
|
||||
const cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0) || ""
|
||||
|
||||
return {
|
||||
version: this.context.extension?.packageJSON?.version ?? "",
|
||||
apiConfiguration,
|
||||
|
|
@ -2467,6 +2469,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
experiments: experiments ?? experimentDefault,
|
||||
mcpServers: this.mcpHub?.getAllServers() ?? [],
|
||||
maxOpenTabsContext: maxOpenTabsContext ?? 20,
|
||||
cwd: cwd,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@ export interface ExtensionState {
|
|||
customModes: ModeConfig[]
|
||||
toolRequirements?: Record<string, boolean> // Map of tool names to their requirements (e.g. {"apply_diff": true} if diffEnabled)
|
||||
maxOpenTabsContext: number // Maximum number of VSCode open tabs to include in context (0-500)
|
||||
cwd?: string // Current working directory
|
||||
}
|
||||
|
||||
export interface ClineMessage {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import { vscode } from "../../utils/vscode"
|
|||
import { WebviewMessage } from "../../../../src/shared/WebviewMessage"
|
||||
import { Mode, getAllModes } from "../../../../src/shared/modes"
|
||||
import { CaretIcon } from "../common/CaretIcon"
|
||||
import { convertToMentionPath } from "../../utils/path-mentions"
|
||||
|
||||
interface ChatTextAreaProps {
|
||||
inputValue: string
|
||||
|
|
@ -50,7 +51,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
|||
},
|
||||
ref,
|
||||
) => {
|
||||
const { filePaths, openedTabs, currentApiConfigName, listApiConfigMeta, customModes } = useExtensionState()
|
||||
const { filePaths, openedTabs, currentApiConfigName, listApiConfigMeta, customModes, cwd } = useExtensionState()
|
||||
const [gitCommits, setGitCommits] = useState<any[]>([])
|
||||
const [showDropdown, setShowDropdown] = useState(false)
|
||||
|
||||
|
|
@ -589,18 +590,24 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
|||
const files = Array.from(e.dataTransfer.files)
|
||||
const text = e.dataTransfer.getData("text")
|
||||
if (text) {
|
||||
const newValue = inputValue.slice(0, cursorPosition) + text + inputValue.slice(cursorPosition)
|
||||
// Convert the path to a mention-friendly format
|
||||
const mentionText = convertToMentionPath(text, cwd)
|
||||
|
||||
const newValue =
|
||||
inputValue.slice(0, cursorPosition) + mentionText + " " + inputValue.slice(cursorPosition)
|
||||
setInputValue(newValue)
|
||||
const newCursorPosition = cursorPosition + text.length
|
||||
const newCursorPosition = cursorPosition + mentionText.length + 1
|
||||
setCursorPosition(newCursorPosition)
|
||||
setIntendedCursorPosition(newCursorPosition)
|
||||
return
|
||||
}
|
||||
|
||||
const acceptedTypes = ["png", "jpeg", "webp"]
|
||||
const imageFiles = files.filter((file) => {
|
||||
const [type, subtype] = file.type.split("/")
|
||||
return type === "image" && acceptedTypes.includes(subtype)
|
||||
})
|
||||
|
||||
if (!shouldDisableImages && imageFiles.length > 0) {
|
||||
const imagePromises = imageFiles.map((file) => {
|
||||
return new Promise<string | null>((resolve) => {
|
||||
|
|
|
|||
|
|
@ -880,9 +880,8 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
|
|||
const placeholderText = useMemo(() => {
|
||||
const baseText = task ? "Type a message..." : "Type your task here..."
|
||||
const contextText = "(@ to add context, / to switch modes"
|
||||
const imageText = shouldDisableImages ? "" : ", hold shift to drag in images"
|
||||
const helpText = imageText ? `\n${contextText}${imageText})` : `\n${contextText})`
|
||||
return baseText + helpText
|
||||
const imageText = shouldDisableImages ? "hold shift to drag in files" : ", hold shift to drag in files/images"
|
||||
return baseText + `\n${contextText}${imageText})`
|
||||
}, [task, shouldDisableImages])
|
||||
|
||||
const itemContent = useCallback(
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
|
|||
autoApprovalEnabled: false,
|
||||
customModes: [],
|
||||
maxOpenTabsContext: 20,
|
||||
cwd: "",
|
||||
})
|
||||
|
||||
const [didHydrateState, setDidHydrateState] = useState(false)
|
||||
|
|
|
|||
45
webview-ui/src/utils/__tests__/path-mentions.test.ts
Normal file
45
webview-ui/src/utils/__tests__/path-mentions.test.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { convertToMentionPath } from "../path-mentions"
|
||||
|
||||
describe("path-mentions", () => {
|
||||
describe("convertToMentionPath", () => {
|
||||
it("should convert an absolute path to a mention path when it starts with cwd", () => {
|
||||
// Windows-style paths
|
||||
expect(convertToMentionPath("C:\\Users\\user\\project\\file.txt", "C:\\Users\\user\\project")).toBe(
|
||||
"@/file.txt",
|
||||
)
|
||||
|
||||
// Unix-style paths
|
||||
expect(convertToMentionPath("/Users/user/project/file.txt", "/Users/user/project")).toBe("@/file.txt")
|
||||
})
|
||||
|
||||
it("should handle paths with trailing slashes in cwd", () => {
|
||||
expect(convertToMentionPath("/Users/user/project/file.txt", "/Users/user/project/")).toBe("@/file.txt")
|
||||
})
|
||||
|
||||
it("should be case-insensitive when matching paths", () => {
|
||||
expect(convertToMentionPath("/Users/User/Project/file.txt", "/users/user/project")).toBe("@/file.txt")
|
||||
})
|
||||
|
||||
it("should return the original path when cwd is not provided", () => {
|
||||
expect(convertToMentionPath("/Users/user/project/file.txt")).toBe("/Users/user/project/file.txt")
|
||||
})
|
||||
|
||||
it("should return the original path when it does not start with cwd", () => {
|
||||
expect(convertToMentionPath("/Users/other/project/file.txt", "/Users/user/project")).toBe(
|
||||
"/Users/other/project/file.txt",
|
||||
)
|
||||
})
|
||||
|
||||
it("should normalize backslashes to forward slashes", () => {
|
||||
expect(convertToMentionPath("C:\\Users\\user\\project\\subdir\\file.txt", "C:\\Users\\user\\project")).toBe(
|
||||
"@/subdir/file.txt",
|
||||
)
|
||||
})
|
||||
|
||||
it("should handle nested paths correctly", () => {
|
||||
expect(convertToMentionPath("/Users/user/project/nested/deeply/file.txt", "/Users/user/project")).toBe(
|
||||
"@/nested/deeply/file.txt",
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
38
webview-ui/src/utils/path-mentions.ts
Normal file
38
webview-ui/src/utils/path-mentions.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* Utilities for handling path-related operations in mentions
|
||||
*/
|
||||
|
||||
/**
|
||||
* Converts an absolute path to a mention-friendly path
|
||||
* If the provided path starts with the current working directory,
|
||||
* it's converted to a relative path prefixed with @
|
||||
*
|
||||
* @param path The path to convert
|
||||
* @param cwd The current working directory
|
||||
* @returns A mention-friendly path
|
||||
*/
|
||||
export function convertToMentionPath(path: string, cwd?: string): string {
|
||||
const normalizedPath = path.replace(/\\/g, "/")
|
||||
let normalizedCwd = cwd ? cwd.replace(/\\/g, "/") : ""
|
||||
|
||||
if (!normalizedCwd) {
|
||||
return path
|
||||
}
|
||||
|
||||
// Remove trailing slash from cwd if it exists
|
||||
if (normalizedCwd.endsWith("/")) {
|
||||
normalizedCwd = normalizedCwd.slice(0, -1)
|
||||
}
|
||||
|
||||
// Always use case-insensitive comparison for path matching
|
||||
const lowerPath = normalizedPath.toLowerCase()
|
||||
const lowerCwd = normalizedCwd.toLowerCase()
|
||||
|
||||
if (lowerPath.startsWith(lowerCwd)) {
|
||||
const relativePath = normalizedPath.substring(normalizedCwd.length)
|
||||
// Ensure there's a slash after the @ symbol when we create the mention path
|
||||
return "@" + (relativePath.startsWith("/") ? relativePath : "/" + relativePath)
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue