import cls from 'classnames'; import { debounce } from 'lodash'; import React, { memo, useCallback, useEffect, useRef, useState } from 'react'; import { ClickOutside } from '@opensumi/ide-components/lib/click-outside'; import { AppConfig, LabelService } from '@opensumi/ide-core-browser'; import { Icon, Input, Scrollbars } from '@opensumi/ide-core-browser/lib/components'; import { RecentFilesManager } from '@opensumi/ide-core-browser/lib/quick-open/recent-files'; import { useInjectable } from '@opensumi/ide-core-browser/lib/react-hooks/injectable-hooks'; import { FileSearchServicePath, IFileSearchService } from '@opensumi/ide-file-search/lib/common/file-search'; import { URI } from '@opensumi/ide-utils'; import { FileContext } from '../../../common/llm-context'; import styles from './style.module.less'; interface CandidateFileProps { uri: URI; active: boolean; selected: boolean; onDidSelect: (val: URI) => void; onDidDeselect: (val: URI) => void; } const CandidateFile = memo(({ uri, active, selected, onDidSelect, onDidDeselect }: CandidateFileProps) => { const labelService = useInjectable(LabelService); const appConfig = useInjectable(AppConfig); const itemsRef = useRef(null); useEffect(() => { if (active && itemsRef.current) { const scrollBehavior: ScrollIntoViewOptions = { behavior: 'instant', block: 'end', }; itemsRef.current.scrollIntoView(scrollBehavior); } }, [active, itemsRef.current]); return (
(itemsRef.current = ele)} onClick={() => (selected ? onDidDeselect(uri) : onDidSelect(uri))} > {uri.path.base} {URI.file(appConfig.workspaceDir).relative(uri.parent)?.toString()} {selected && }
); }); interface ContextSelectorProps { addedFiles: FileContext[]; onDidSelect: (val: URI) => void; onDidDeselect: (val: URI) => void; onDidClose: () => void; } export const ContextSelector = memo(({ addedFiles, onDidDeselect, onDidSelect, onDidClose }: ContextSelectorProps) => { const [candidateFiles, updateCandidateFiles] = useState([]); const [activeFile, setActiveFile] = useState(null); const [searching, toggleSearching] = useState(false); const [searchResults, updateSearchResults] = useState([]); const recentFilesManager: RecentFilesManager = useInjectable(RecentFilesManager); const appConfig = useInjectable(AppConfig); const searchService = useInjectable(FileSearchServicePath); const container = useRef(); useEffect(() => { if (candidateFiles.length === 0) { recentFilesManager.getMostRecentlyOpenedFiles().then((files) => { const addedUris = addedFiles.map((val) => val.uri); const recentFiles = files.filter((file) => !addedUris.includes(new URI(file))).map((file) => new URI(file)); updateCandidateFiles(recentFiles); setActiveFile(recentFiles[0] || null); }); } }, [addedFiles]); const onDidInput = useCallback( debounce((ev) => { if (ev.target.value.trim() === '') { updateSearchResults([]); setActiveFile(candidateFiles[0]); return; } toggleSearching(true); searchService .find(ev.target.value, { rootUris: [appConfig.workspaceDir], limit: 200, useGitIgnore: true, noIgnoreParent: true, fuzzyMatch: true, }) .then((res) => { const results = res.map((val) => new URI(val)); updateSearchResults(results); setActiveFile(results[0]); }) .finally(() => { toggleSearching(false); }); }, 500), [], ); const onDidKeyDown = useCallback( (event) => { const { key } = event; if (key === 'Escape') { onDidClose(); return; } if (key === 'Enter' && activeFile) { onDidSelect(activeFile); return; } const validKeys = ['ArrowUp', 'ArrowDown']; if (!validKeys.includes(key)) { return; } const files = searchResults.length > 0 ? searchResults : candidateFiles; if (files.length === 0) { return; } const currentIndex = files.indexOf(activeFile!); const safeIndex = currentIndex === -1 ? 0 : currentIndex; const lastIndex = files.length - 1; const nextIndex = key === 'ArrowUp' ? (safeIndex > 0 ? safeIndex - 1 : lastIndex) : safeIndex < lastIndex ? safeIndex + 1 : 0; setActiveFile(files[nextIndex]); }, [activeFile, searchResults, candidateFiles], ); return ( onDidClose()}>
(el ? (container.current = el.ref) : null)}>
{searching &&
} {searchResults.length > 0 ? 'Search Results' : 'Recent Opened Files'} {(searchResults.length > 0 ? searchResults : candidateFiles).map((file) => ( val.uri.isEqual(file))} /> ))}
); });