workspace file search (#2002)
This commit is contained in:
committed by
GitHub
parent
82a4e0fccf
commit
7bc8ece068
@@ -553,6 +553,7 @@ export function SessionChatBoxContainer({
|
||||
return (
|
||||
<SessionChatBox
|
||||
status={status}
|
||||
workspaceId={workspaceId}
|
||||
projectId={projectId}
|
||||
editor={{
|
||||
value: editorValue,
|
||||
|
||||
@@ -32,6 +32,7 @@ interface ChatBoxBaseProps {
|
||||
placeholder: string;
|
||||
onCmdEnter: () => void;
|
||||
disabled?: boolean;
|
||||
workspaceId?: string;
|
||||
projectId?: string;
|
||||
autoFocus?: boolean;
|
||||
|
||||
@@ -81,6 +82,7 @@ export function ChatBoxBase({
|
||||
placeholder,
|
||||
onCmdEnter,
|
||||
disabled,
|
||||
workspaceId,
|
||||
projectId,
|
||||
autoFocus,
|
||||
variant,
|
||||
@@ -142,6 +144,7 @@ export function ChatBoxBase({
|
||||
onCmdEnter={onCmdEnter}
|
||||
disabled={disabled}
|
||||
className="min-h-0 max-h-[50vh] overflow-y-auto"
|
||||
workspaceId={workspaceId}
|
||||
projectId={projectId}
|
||||
autoFocus={autoFocus}
|
||||
onPasteFiles={onPasteFiles}
|
||||
|
||||
@@ -129,6 +129,7 @@ interface SessionChatBoxProps {
|
||||
reviewComments?: ReviewCommentsProps;
|
||||
toolbarActions?: ToolbarActionsProps;
|
||||
error?: string | null;
|
||||
workspaceId?: string;
|
||||
projectId?: string;
|
||||
agent?: BaseCodingAgent | null;
|
||||
executor?: ExecutorProps;
|
||||
@@ -153,6 +154,7 @@ export function SessionChatBox({
|
||||
reviewComments,
|
||||
toolbarActions,
|
||||
error,
|
||||
workspaceId,
|
||||
projectId,
|
||||
agent,
|
||||
executor,
|
||||
@@ -488,6 +490,7 @@ export function SessionChatBox({
|
||||
placeholder={placeholder}
|
||||
onCmdEnter={handleCmdEnter}
|
||||
disabled={isDisabled}
|
||||
workspaceId={workspaceId}
|
||||
projectId={projectId}
|
||||
autoFocus={true}
|
||||
focusKey={focusKey}
|
||||
|
||||
@@ -56,7 +56,10 @@ type WysiwygProps = {
|
||||
disabled?: boolean;
|
||||
onPasteFiles?: (files: File[]) => void;
|
||||
className?: string;
|
||||
projectId?: string; // for file search in typeahead
|
||||
/** Workspace ID for workspace-scoped file search (preferred over projectId) */
|
||||
workspaceId?: string;
|
||||
/** Project ID for file search in typeahead (fallback if workspaceId not provided) */
|
||||
projectId?: string;
|
||||
onCmdEnter?: () => void;
|
||||
onShiftCmdEnter?: () => void;
|
||||
/** Task attempt ID for resolving .vibe-images paths (preferred over taskId) */
|
||||
@@ -85,6 +88,7 @@ function WYSIWYGEditor({
|
||||
disabled = false,
|
||||
onPasteFiles,
|
||||
className,
|
||||
workspaceId,
|
||||
projectId,
|
||||
onCmdEnter,
|
||||
onShiftCmdEnter,
|
||||
@@ -251,7 +255,10 @@ function WYSIWYGEditor({
|
||||
{autoFocus && <AutoFocusPlugin />}
|
||||
<HistoryPlugin />
|
||||
<MarkdownShortcutPlugin transformers={extendedTransformers} />
|
||||
<FileTagTypeaheadPlugin projectId={projectId} />
|
||||
<FileTagTypeaheadPlugin
|
||||
workspaceId={workspaceId}
|
||||
projectId={projectId}
|
||||
/>
|
||||
<KeyboardCommandsPlugin
|
||||
onCmdEnter={onCmdEnter}
|
||||
onShiftCmdEnter={onShiftCmdEnter}
|
||||
|
||||
@@ -62,7 +62,13 @@ function getMenuPosition(anchorEl: HTMLElement) {
|
||||
return { top, bottom, left };
|
||||
}
|
||||
|
||||
export function FileTagTypeaheadPlugin({ projectId }: { projectId?: string }) {
|
||||
export function FileTagTypeaheadPlugin({
|
||||
workspaceId,
|
||||
projectId,
|
||||
}: {
|
||||
workspaceId?: string;
|
||||
projectId?: string;
|
||||
}) {
|
||||
const [editor] = useLexicalComposerContext();
|
||||
const [options, setOptions] = useState<FileTagOption[]>([]);
|
||||
const lastMousePositionRef = useRef<{ x: number; y: number } | null>(null);
|
||||
@@ -77,7 +83,7 @@ export function FileTagTypeaheadPlugin({ projectId }: { projectId?: string }) {
|
||||
}
|
||||
|
||||
// Here query is a string, including possible empty string ''
|
||||
searchTagsAndFiles(query, projectId)
|
||||
searchTagsAndFiles(query, { workspaceId, projectId })
|
||||
.then((results) => {
|
||||
setOptions(results.map((r) => new FileTagOption(r)));
|
||||
})
|
||||
@@ -85,7 +91,7 @@ export function FileTagTypeaheadPlugin({ projectId }: { projectId?: string }) {
|
||||
console.error('Failed to search tags/files', err);
|
||||
});
|
||||
},
|
||||
[projectId]
|
||||
[workspaceId, projectId]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -565,6 +565,18 @@ export const attemptsApi = {
|
||||
return handleApiResponse<void>(response);
|
||||
},
|
||||
|
||||
searchFiles: async (
|
||||
workspaceId: string,
|
||||
query: string,
|
||||
mode?: string
|
||||
): Promise<SearchResult[]> => {
|
||||
const modeParam = mode ? `&mode=${encodeURIComponent(mode)}` : '';
|
||||
const response = await makeRequest(
|
||||
`/api/task-attempts/${workspaceId}/search?q=${encodeURIComponent(query)}${modeParam}`
|
||||
);
|
||||
return handleApiResponse<SearchResult[]>(response);
|
||||
},
|
||||
|
||||
runAgentSetup: async (
|
||||
attemptId: string,
|
||||
data: RunAgentSetupRequest
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { projectsApi, tagsApi } from '@/lib/api';
|
||||
import { attemptsApi, projectsApi, tagsApi } from '@/lib/api';
|
||||
import type { SearchResult, Tag } from 'shared/types';
|
||||
|
||||
interface FileSearchResult extends SearchResult {
|
||||
@@ -11,9 +11,14 @@ export interface SearchResultItem {
|
||||
file?: FileSearchResult;
|
||||
}
|
||||
|
||||
export interface SearchOptions {
|
||||
workspaceId?: string;
|
||||
projectId?: string;
|
||||
}
|
||||
|
||||
export async function searchTagsAndFiles(
|
||||
query: string,
|
||||
projectId?: string
|
||||
options?: SearchOptions
|
||||
): Promise<SearchResultItem[]> {
|
||||
const results: SearchResultItem[] = [];
|
||||
|
||||
@@ -24,16 +29,24 @@ export async function searchTagsAndFiles(
|
||||
);
|
||||
results.push(...filteredTags.map((tag) => ({ type: 'tag' as const, tag })));
|
||||
|
||||
// Fetch files (if projectId is available and query has content)
|
||||
if (projectId && query.length > 0) {
|
||||
const fileResults = await projectsApi.searchFiles(projectId, query);
|
||||
const fileSearchResults: FileSearchResult[] = fileResults.map((item) => ({
|
||||
...item,
|
||||
name: item.path.split('/').pop() || item.path,
|
||||
}));
|
||||
results.push(
|
||||
...fileSearchResults.map((file) => ({ type: 'file' as const, file }))
|
||||
);
|
||||
// Fetch files - prefer workspace-scoped if available
|
||||
if (query.length > 0) {
|
||||
let fileResults: SearchResult[] = [];
|
||||
if (options?.workspaceId) {
|
||||
fileResults = await attemptsApi.searchFiles(options.workspaceId, query);
|
||||
} else if (options?.projectId) {
|
||||
fileResults = await projectsApi.searchFiles(options.projectId, query);
|
||||
}
|
||||
|
||||
if (fileResults.length > 0) {
|
||||
const fileSearchResults: FileSearchResult[] = fileResults.map((item) => ({
|
||||
...item,
|
||||
name: item.path.split('/').pop() || item.path,
|
||||
}));
|
||||
results.push(
|
||||
...fileSearchResults.map((file) => ({ type: 'file' as const, file }))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
|
||||
Reference in New Issue
Block a user