-
- {isUser ? 'You' : 'Claude'}
-
+ {isUser ? (
+
+
+ You
+
+ ) : (
+
+
+ Claude
+
+ )}
{hasContent && }
setCollapsed(c => !c)} />
@@ -236,9 +270,10 @@ function TurnBlock({ turn }: { turn: Turn }) {
{!collapsed && (
<>
+ {attachments.length > 0 &&
}
{hasContent && (
-
+
)}
{regularCalls.length > 0 && (
diff --git a/webview-ui/src/components/__tests__/MarkdownView.test.tsx b/webview-ui/src/components/__tests__/MarkdownView.test.tsx
index 65ab6a4..3ba63f2 100644
--- a/webview-ui/src/components/__tests__/MarkdownView.test.tsx
+++ b/webview-ui/src/components/__tests__/MarkdownView.test.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import { describe, expect, it, vi } from 'vitest';
import { fireEvent, render, screen } from '../../__tests__/helpers/render-helpers';
-import { CommandBlock, MarkdownView } from '../MarkdownView';
+import { CommandBlock, MarkdownView, MentionText } from '../MarkdownView';
describe('MarkdownView', () => {
it('renders headings, emphasis, code blocks, and lists', () => {
@@ -23,6 +23,21 @@ describe('MarkdownView', () => {
expect(screen.getByText('now')).toBeInTheDocument();
});
+ it('highlights @file mentions only when enabled, leaving emails alone', () => {
+ const { rerender } = render(
);
+ expect(screen.getByText('@docs/plan.md')).toHaveClass('font-mono');
+ expect(screen.queryByText('@example.com')).not.toBeInTheDocument();
+
+ rerender(
);
+ expect(screen.queryByText('@docs/plan.md')).not.toBeInTheDocument();
+ });
+
+ it('renders mention chips in plain text via MentionText', () => {
+ render(
);
+ expect(screen.getByText('@src/index.ts')).toHaveClass('font-mono');
+ expect(screen.getByTestId('summary')).toHaveTextContent('Fix @src/index.ts now');
+ });
+
it('renders markdown links and forwards link clicks', () => {
const onLinkClick = vi.fn();
render(
);
diff --git a/webview-ui/src/components/__tests__/SessionDetail.test.tsx b/webview-ui/src/components/__tests__/SessionDetail.test.tsx
index fc1a3dd..dd3fc32 100644
--- a/webview-ui/src/components/__tests__/SessionDetail.test.tsx
+++ b/webview-ui/src/components/__tests__/SessionDetail.test.tsx
@@ -72,6 +72,24 @@ describe('SessionDetail', () => {
expect(modelBadgeColor(null)).toBe('');
});
+ it('renders @-tagged file chips on user turns', () => {
+ const session = makeSession({
+ turns: [
+ makeTurn({
+ role: 'user',
+ content: 'Review @docs/plan.md please',
+ attachments: [{ path: '/home/user/project/docs/plan.md', displayPath: 'docs/plan.md' }],
+ timestamp: 1,
+ }),
+ ],
+ });
+
+ render(
);
+ const chip = screen.getByText('docs/plan.md');
+ expect(chip).toBeInTheDocument();
+ expect(chip.closest('span[title]')).toHaveAttribute('title', '/home/user/project/docs/plan.md');
+ });
+
it('renders cache, thinking, subagent cost, collapsed previews, and tool-only assistant turns', () => {
const longContent = 'A'.repeat(140);
const session = makeSession({
diff --git a/webview-ui/src/types.ts b/webview-ui/src/types.ts
index 1475c6c..ab1a735 100644
--- a/webview-ui/src/types.ts
+++ b/webview-ui/src/types.ts
@@ -49,6 +49,12 @@ export interface Turn {
outputTokens: number;
toolCalls: ToolCall[];
timestamp: number;
+ attachments?: TurnAttachment[]; // files the user @-tagged in this prompt
+}
+
+export interface TurnAttachment {
+ path: string; // absolute file path
+ displayPath: string; // project-relative path as shown in the prompt
}
export interface ToolCall {
diff --git a/webview-ui/src/views/ProjectDetail.tsx b/webview-ui/src/views/ProjectDetail.tsx
index d86d36a..02bdc6d 100644
--- a/webview-ui/src/views/ProjectDetail.tsx
+++ b/webview-ui/src/views/ProjectDetail.tsx
@@ -3,7 +3,7 @@ import { Project, Session, Turn, ProjectConfig, McpServer, ProjectStats, Project
import { vscode } from '../vscode';
import { formatTokens, formatDuration, timeAgo } from '../utils/format';
import { toolColor } from '../utils/toolColor';
-import { MarkdownView, CommandBlock } from '../components/MarkdownView';
+import { MarkdownView, CommandBlock, MentionText } from '../components/MarkdownView';
import SessionDetail from '../components/SessionDetail';
import WeeklyStatsTab from '../components/WeeklyStatsTab';
import ToolUsageBar from '../components/ToolUsageBar';
@@ -73,6 +73,7 @@ export default function ProjectDetail({ project, sessions, subagentSessions, con
const [selectedMemoryFileName, setSelectedMemoryFileName] = useState
(null);
const [selectedPlanFileName, setSelectedPlanFileName] = useState(null);
const memoryPreviewRef = useRef(null);
+ const sessionDetailRef = useRef(null);
const sorted = [...(sessions ?? [])].sort((a, b) => b.startTime - a.startTime);
useEffect(() => {
@@ -92,6 +93,9 @@ export default function ProjectDetail({ project, sessions, subagentSessions, con
setTurns([]);
setTurnsLoading(true);
vscode.postMessage({ type: 'getSessionTurns', sessionId: session.id });
+ requestAnimationFrame(() => {
+ sessionDetailRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' });
+ });
}, []);
if (!project) {
@@ -287,7 +291,7 @@ export default function ProjectDetail({ project, sessions, subagentSessions, con
{/* ── Sessions tab ── */}
{activeTab === 'sessions' && (
-
+
Sessions
{sorted.map(s => (
@@ -310,16 +314,27 @@ export default function ProjectDetail({ project, sessions, subagentSessions, con
{(s.subagentCostUsd ?? 0) > 0 && +sub}
{s.sessionSummary && (
- {s.sessionSummary}
+
)}
))}
-
+
{selectedSession ? (
-
+ <>
+
+
+ >
) : (
Select a session to view details