mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 04:11:10 -05:00
feat: opened tabs and selection in the @ menu
This commit is contained in:
@@ -50,7 +50,8 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const { filePaths, currentApiConfigName, listApiConfigMeta, customModes } = useExtensionState()
|
||||
const { filePaths, openedTabs, activeSelection, currentApiConfigName, listApiConfigMeta, customModes } =
|
||||
useExtensionState()
|
||||
const [gitCommits, setGitCommits] = useState<any[]>([])
|
||||
const [showDropdown, setShowDropdown] = useState(false)
|
||||
|
||||
@@ -89,6 +90,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
return () => window.removeEventListener("message", messageHandler)
|
||||
}, [setInputValue])
|
||||
|
||||
const [isTextAreaFocused, setIsTextAreaFocused] = useState(false)
|
||||
const [thumbnailsHeight, setThumbnailsHeight] = useState(0)
|
||||
const [textAreaBaseHeight, setTextAreaBaseHeight] = useState<number | undefined>(undefined)
|
||||
const [showContextMenu, setShowContextMenu] = useState(false)
|
||||
@@ -135,17 +137,36 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
}, [inputValue, textAreaDisabled, setInputValue])
|
||||
|
||||
const queryItems = useMemo(() => {
|
||||
return [
|
||||
const items = [
|
||||
{ type: ContextMenuOptionType.Problems, value: "problems" },
|
||||
...gitCommits,
|
||||
// Add opened tabs
|
||||
...openedTabs
|
||||
.filter((tab) => tab.path)
|
||||
.map((tab) => ({
|
||||
type: ContextMenuOptionType.OpenedFile,
|
||||
value: "/" + tab.path,
|
||||
})),
|
||||
|
||||
// Add regular file paths
|
||||
...filePaths
|
||||
.map((file) => "/" + file)
|
||||
.filter((path) => !openedTabs.some((tab) => tab.path && "/" + tab.path === path)) // Filter out paths that are already in openedTabs
|
||||
.map((path) => ({
|
||||
type: path.endsWith("/") ? ContextMenuOptionType.Folder : ContextMenuOptionType.File,
|
||||
value: path,
|
||||
})),
|
||||
]
|
||||
}, [filePaths, gitCommits])
|
||||
|
||||
if (activeSelection) {
|
||||
items.unshift({
|
||||
type: ContextMenuOptionType.OpenedFile,
|
||||
value: `/${activeSelection.file}:${activeSelection.selection.startLine + 1}-${activeSelection.selection.endLine + 1}`,
|
||||
})
|
||||
}
|
||||
|
||||
return items
|
||||
}, [filePaths, openedTabs, activeSelection])
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
|
||||
@@ -74,6 +74,7 @@ const ContextMenu: React.FC<ContextMenuProps> = ({
|
||||
return <span>Git Commits</span>
|
||||
}
|
||||
case ContextMenuOptionType.File:
|
||||
case ContextMenuOptionType.OpenedFile:
|
||||
case ContextMenuOptionType.Folder:
|
||||
if (option.value) {
|
||||
return (
|
||||
@@ -100,6 +101,8 @@ const ContextMenu: React.FC<ContextMenuProps> = ({
|
||||
|
||||
const getIconForOption = (option: ContextMenuQueryItem): string => {
|
||||
switch (option.type) {
|
||||
case ContextMenuOptionType.OpenedFile:
|
||||
return "star-full"
|
||||
case ContextMenuOptionType.File:
|
||||
return "file"
|
||||
case ContextMenuOptionType.Folder:
|
||||
@@ -194,6 +197,7 @@ const ContextMenu: React.FC<ContextMenuProps> = ({
|
||||
{(option.type === ContextMenuOptionType.Problems ||
|
||||
((option.type === ContextMenuOptionType.File ||
|
||||
option.type === ContextMenuOptionType.Folder ||
|
||||
option.type === ContextMenuOptionType.OpenedFile ||
|
||||
option.type === ContextMenuOptionType.Git) &&
|
||||
option.value)) && (
|
||||
<i
|
||||
|
||||
@@ -27,6 +27,11 @@ export interface ExtensionStateContextType extends ExtensionState {
|
||||
openAiModels: string[]
|
||||
mcpServers: McpServer[]
|
||||
filePaths: string[]
|
||||
openedTabs: Array<{ label: string; isActive: boolean; path?: string }>
|
||||
activeSelection: {
|
||||
file: string
|
||||
selection: { startLine: number; endLine: number }
|
||||
} | null
|
||||
setApiConfiguration: (config: ApiConfiguration) => void
|
||||
setCustomInstructions: (value?: string) => void
|
||||
setAlwaysAllowReadOnly: (value: boolean) => void
|
||||
@@ -116,6 +121,11 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
|
||||
const [glamaModels, setGlamaModels] = useState<Record<string, ModelInfo>>({
|
||||
[glamaDefaultModelId]: glamaDefaultModelInfo,
|
||||
})
|
||||
const [openedTabs, setOpenedTabs] = useState<Array<{ label: string; isActive: boolean; path?: string }>>([])
|
||||
const [activeSelection, setActiveSelection] = useState<{
|
||||
file: string
|
||||
selection: { startLine: number; endLine: number }
|
||||
} | null>(null)
|
||||
const [openRouterModels, setOpenRouterModels] = useState<Record<string, ModelInfo>>({
|
||||
[openRouterDefaultModelId]: openRouterDefaultModelInfo,
|
||||
})
|
||||
@@ -176,7 +186,13 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
|
||||
break
|
||||
}
|
||||
case "workspaceUpdated": {
|
||||
setFilePaths(message.filePaths ?? [])
|
||||
const paths = message.filePaths ?? []
|
||||
const tabs = message.openedTabs ?? []
|
||||
const selection = message.activeSelection ?? null
|
||||
|
||||
setFilePaths(paths)
|
||||
setOpenedTabs(tabs)
|
||||
setActiveSelection(selection)
|
||||
break
|
||||
}
|
||||
case "partialMessage": {
|
||||
@@ -243,6 +259,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
|
||||
openAiModels,
|
||||
mcpServers,
|
||||
filePaths,
|
||||
openedTabs,
|
||||
activeSelection,
|
||||
soundVolume: state.soundVolume,
|
||||
fuzzyMatchThreshold: state.fuzzyMatchThreshold,
|
||||
writeDelayMs: state.writeDelayMs,
|
||||
|
||||
@@ -48,6 +48,7 @@ export function removeMention(text: string, position: number): { newText: string
|
||||
}
|
||||
|
||||
export enum ContextMenuOptionType {
|
||||
OpenedFile = "openedFile",
|
||||
File = "file",
|
||||
Folder = "folder",
|
||||
Problems = "problems",
|
||||
@@ -80,8 +81,14 @@ export function getContextMenuOptions(
|
||||
if (query === "") {
|
||||
if (selectedType === ContextMenuOptionType.File) {
|
||||
const files = queryItems
|
||||
.filter((item) => item.type === ContextMenuOptionType.File)
|
||||
.map((item) => ({ type: ContextMenuOptionType.File, value: item.value }))
|
||||
.filter(
|
||||
(item) =>
|
||||
item.type === ContextMenuOptionType.File || item.type === ContextMenuOptionType.OpenedFile,
|
||||
)
|
||||
.map((item) => ({
|
||||
type: item.type,
|
||||
value: item.value,
|
||||
}))
|
||||
return files.length > 0 ? files : [{ type: ContextMenuOptionType.NoResults }]
|
||||
}
|
||||
|
||||
@@ -125,6 +132,12 @@ export function getContextMenuOptions(
|
||||
}
|
||||
if (query.startsWith("http")) {
|
||||
suggestions.push({ type: ContextMenuOptionType.URL, value: query })
|
||||
} else {
|
||||
suggestions.push(
|
||||
...queryItems
|
||||
.filter((item) => item.type !== ContextMenuOptionType.OpenedFile)
|
||||
.filter((item) => item.value?.toLowerCase().includes(lowerQuery)),
|
||||
)
|
||||
}
|
||||
|
||||
// Add exact SHA matches to suggestions
|
||||
|
||||
Reference in New Issue
Block a user