Skip to content

Commit be4db6f

Browse files
authored
fix: Show parent directory in file mention chips (#1472)
## Problem File mention chips only show the filename, making it hard to distinguish between files with the same name in different directories. Absolute file paths pasted into @ mentions aren't recognized at all. <!-- Who is this for and what problem does it solve? --> <!-- Closes #ISSUE_ID --> ## Changes 1. Show parent directory prefix in file mention chip labels (e.g. components/Button.tsx) 2. Add tooltip on file chips showing the full path 3. Recognize and chipify absolute file paths in @ mention suggestions 4. Add fileExists tRPC endpoint to validate absolute paths before suggesting them <!-- What did you change and why? --> <!-- If there are frontend changes, include screenshots. --> ## How did you test this? Manually <!-- Describe what you tested -- manual steps, automated tests, or both. --> <!-- If you're an agent, only list tests you actually ran. -->
1 parent 5f5c438 commit be4db6f

3 files changed

Lines changed: 51 additions & 7 deletions

File tree

apps/code/src/renderer/features/message-editor/suggestions/getSuggestions.ts

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import type { AvailableCommand } from "@agentclientprotocol/sdk";
22
import { CODE_COMMANDS } from "@features/message-editor/commands";
33
import { getAvailableCommandsForTask } from "@features/sessions/stores/sessionStore";
4-
import { fetchRepoFiles, searchFiles } from "@hooks/useRepoFiles";
4+
import {
5+
fetchRepoFiles,
6+
pathToFileItem,
7+
searchFiles,
8+
} from "@hooks/useRepoFiles";
9+
import { isAbsolutePath } from "@utils/path";
510
import Fuse, { type IFuseOptions } from "fuse.js";
611
import { useDraftStore } from "../stores/draftStore";
712
import type { CommandSuggestionItem, FileSuggestionItem } from "../types";
@@ -39,26 +44,55 @@ function searchCommands(
3944
return results.map((result) => result.item);
4045
}
4146

47+
function parentDirLabel(dir: string, name: string): string {
48+
const parent = dir.split("/").filter(Boolean).pop();
49+
return parent ? `${parent}/${name}` : name;
50+
}
51+
52+
function getAbsolutePathSuggestion(query: string): FileSuggestionItem | null {
53+
if (!isAbsolutePath(query)) return null;
54+
if (!/\.\w+$/.test(query)) return null;
55+
56+
const fileItem = pathToFileItem(query);
57+
return {
58+
id: query,
59+
label: parentDirLabel(fileItem.dir, fileItem.name),
60+
description: fileItem.dir || undefined,
61+
filename: fileItem.name,
62+
path: query,
63+
};
64+
}
65+
4266
export async function getFileSuggestions(
4367
sessionId: string,
4468
query: string,
4569
): Promise<FileSuggestionItem[]> {
4670
const repoPath = useDraftStore.getState().contexts[sessionId]?.repoPath;
71+
const absoluteMatch = getAbsolutePathSuggestion(query);
4772

4873
if (!repoPath) {
49-
return [];
74+
return absoluteMatch ? [absoluteMatch] : [];
5075
}
5176

5277
const { files, fzf } = await fetchRepoFiles(repoPath);
5378
const matched = searchFiles(fzf, files, query);
5479

55-
return matched.map((file) => ({
80+
const results: FileSuggestionItem[] = matched.map((file) => ({
5681
id: file.path,
57-
label: file.name,
82+
label: parentDirLabel(file.dir, file.name),
5883
description: file.dir || undefined,
5984
filename: file.name,
6085
path: file.path,
6186
}));
87+
88+
if (
89+
absoluteMatch &&
90+
!results.some((r) => `${repoPath}/${r.id}` === absoluteMatch.id)
91+
) {
92+
results.unshift(absoluteMatch);
93+
}
94+
95+
return results;
6296
}
6397

6498
export function getCommandSuggestions(

apps/code/src/renderer/features/message-editor/tiptap/MentionChipView.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ function DefaultChip({
3535

3636
const isCommand = type === "command";
3737
const prefix = isCommand ? "/" : "@";
38+
const isFile = type === "file";
3839

39-
return (
40+
const chip = (
4041
<span
4142
className={`${isCommand ? "cli-slash-command" : "cli-file-mention"} ${chipClass}`}
4243
contentEditable={false}
@@ -45,6 +46,12 @@ function DefaultChip({
4546
{label}
4647
</span>
4748
);
49+
50+
if (isFile) {
51+
return <Tooltip content={id}>{chip}</Tooltip>;
52+
}
53+
54+
return chip;
4855
}
4956

5057
function PastedTextChip({

apps/code/src/renderer/features/sessions/components/session-update/parseFileMentions.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,15 @@ export function parseMentionTags(content: string): ReactNode[] {
9191

9292
if (match[1]) {
9393
const filePath = match[1];
94-
const fileName = filePath.split("/").pop() ?? filePath;
94+
const segments = filePath.split("/").filter(Boolean);
95+
const fileName = segments.pop() ?? filePath;
96+
const parentDir = segments.pop();
97+
const label = parentDir ? `${parentDir}/${fileName}` : fileName;
9598
parts.push(
9699
<MentionChip
97100
key={`file-${matchIndex}`}
98101
icon={<File size={12} />}
99-
label={fileName}
102+
label={label}
100103
/>,
101104
);
102105
} else if (match[2]) {

0 commit comments

Comments
 (0)