Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions components/AppShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -250,16 +250,19 @@ export function AppShell() {
// read tool resolves it the same way (it strips the @ prefix).
const handleAtMention = useCallback((relativePath: string, isDir: boolean) => {
chatInputRef.current?.insertText(buildAtMentionText(relativePath, isDir));
}, []);
if (isMobile) { setRightPanelOpen(false); setSidebarOpen(false); }
}, [isMobile]);

const handleAtMentions = useCallback((relativePaths: string[]) => {
const mentions = buildFileAtMentionsText(relativePaths);
if (mentions) chatInputRef.current?.insertText(mentions);
}, []);
if (isMobile) { setRightPanelOpen(false); setSidebarOpen(false); }
}, [isMobile]);

const handleFileLineMention = useCallback((relativePath: string, startLine: number, endLine: number) => {
chatInputRef.current?.insertText(buildFileLineMentionText(relativePath, startLine, endLine));
}, []);
if (isMobile) { setRightPanelOpen(false); setSidebarOpen(false); }
}, [isMobile]);

const initialSessionId = initialNavigation.sessionId;
const [activeCwd, setActiveCwd] = useState<string | null>(null);
Expand Down Expand Up @@ -1598,6 +1601,7 @@ export function AppShell() {
gitRefreshKey={explorerRefreshKey}
initialDisplayMode={activeFileTab.initialDisplayMode}
onMentionLines={rightPanelOpen ? handleFileLineMention : undefined}
onAtMention={handleAtMention}
onOpenFile={(filePath) => handleOpenFile(
filePath,
getFileName(filePath),
Expand Down
49 changes: 31 additions & 18 deletions components/FileViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ interface Props {
sourceSessionId?: string | null;
onOpenFile?: (filePath: string) => void;
onMentionLines?: (relativePath: string, startLine: number, endLine: number) => void;
/** Insert this file's relative path into the chat input (@ mention). */
onAtMention?: (relativePath: string, isDir: boolean) => void;
gitRefreshKey?: number;
initialDisplayMode?: DisplayMode;
}
Expand Down Expand Up @@ -783,7 +785,7 @@ function DocumentViewer({ filePath, cwd, sourceSessionId }: Props) {
);
}

export function FileViewer({ filePath, cwd, sourceSessionId, onOpenFile, onMentionLines, gitRefreshKey, initialDisplayMode }: Props) {
export function FileViewer({ filePath, cwd, sourceSessionId, onOpenFile, onMentionLines, onAtMention, gitRefreshKey, initialDisplayMode }: Props) {
if (isImagePath(filePath)) {
return <ImageViewer filePath={filePath} cwd={cwd} sourceSessionId={sourceSessionId} />;
}
Expand All @@ -793,10 +795,10 @@ export function FileViewer({ filePath, cwd, sourceSessionId, onOpenFile, onMenti
if (isDocumentPreviewPath(filePath)) {
return <DocumentViewer filePath={filePath} cwd={cwd} sourceSessionId={sourceSessionId} />;
}
return <TextFileViewer filePath={filePath} cwd={cwd} sourceSessionId={sourceSessionId} onOpenFile={onOpenFile} onMentionLines={onMentionLines} gitRefreshKey={gitRefreshKey} initialDisplayMode={initialDisplayMode} />;
return <TextFileViewer filePath={filePath} cwd={cwd} sourceSessionId={sourceSessionId} onOpenFile={onOpenFile} onMentionLines={onMentionLines} onAtMention={onAtMention} gitRefreshKey={gitRefreshKey} initialDisplayMode={initialDisplayMode} />;
}

function TextFileViewer({ filePath, cwd, sourceSessionId, onOpenFile, onMentionLines, gitRefreshKey, initialDisplayMode }: Props) {
function TextFileViewer({ filePath, cwd, sourceSessionId, onOpenFile, onMentionLines, onAtMention, gitRefreshKey, initialDisplayMode }: Props) {
const { isDark } = useTheme();
const { t } = useI18n();
const [data, setData] = useState<FileData | null>(null);
Expand Down Expand Up @@ -965,10 +967,6 @@ function TextFileViewer({ filePath, cwd, sourceSessionId, onOpenFile, onMentionL
);
}, [cwd, filePath, onMentionLines]);

const handleMentionSelectedLines = useCallback(() => {
mentionLineRange(selectedLineRange);
}, [mentionLineRange, selectedLineRange]);

useEffect(() => {
if (!onMentionLines || displayMode !== "source") return;

Expand Down Expand Up @@ -1086,19 +1084,34 @@ function TextFileViewer({ filePath, cwd, sourceSessionId, onOpenFile, onMentionL
)}

<div className="file-viewer-actions">
{(onAtMention || onMentionLines) && (
<button
type="button"
onPointerDown={(event) => event.preventDefault()}
onClick={() => {
// Mention selected lines when a range is active (and line
// mention is wired up); otherwise fall back to a whole-file
// @mention. Same button, behavior follows the selection.
if (selectedLineRange && onMentionLines) {
mentionLineRange(selectedLineRange);
} else {
onAtMention?.(getRelativeFilePath(filePath, cwd), false);
}
}}
title={
selectedLineRange && onMentionLines
? `${t("i18n.mentionSelectedLines")} (L${selectedLineRange.startLine}${selectedLineRange.startLine !== selectedLineRange.endLine ? `-L${selectedLineRange.endLine}` : ""})`
: t("files.insertPath")
}
aria-label={t("files.mention")}
disabled={!onAtMention && !onMentionLines}
className="file-viewer-icon-button"
>
<MentionIcon />
</button>
)}
{effectiveDisplayMode === "source" && (
<>
<button
type="button"
onMouseDown={(event) => event.preventDefault()}
onClick={handleMentionSelectedLines}
title={t("i18n.mentionSelectedLines")}
aria-label={t("i18n.mentionSelectedLines")}
disabled={!selectedLineRange}
className="file-viewer-icon-button"
>
<MentionIcon />
</button>
<button
type="button"
onClick={() => setWrapLines((value) => !value)}
Expand Down