diff --git a/packages/ui/src/api/client.ts b/packages/ui/src/api/client.ts
index 5a45cff..81aefdb 100644
--- a/packages/ui/src/api/client.ts
+++ b/packages/ui/src/api/client.ts
@@ -151,6 +151,8 @@ export type {
// System Commands types
SystemCommandInfo,
SystemCommandListResponse,
+ // Active file types
+ ActiveFileInfo,
} from './types.js';
// API Configuration
diff --git a/packages/ui/src/api/types.ts b/packages/ui/src/api/types.ts
index 3466a30..630be79 100644
--- a/packages/ui/src/api/types.ts
+++ b/packages/ui/src/api/types.ts
@@ -1144,3 +1144,17 @@ export interface AllFilesDiagnosticsResponse {
/** 诊断响应(联合类型) */
export type DiagnosticsResponse = SingleFileDiagnosticsResponse | AllFilesDiagnosticsResponse;
+// ============ 编辑器活动文件相关 ============
+
+/** 当前编辑器活动文件信息 */
+export interface ActiveFileInfo {
+ /** 文件路径 */
+ path: string;
+ /** 文件名 */
+ name: string;
+ /** 文件内容 */
+ content: string;
+ /** 语言类型 */
+ language: string;
+}
+
diff --git a/packages/ui/src/components/ChatInput.tsx b/packages/ui/src/components/ChatInput.tsx
index c55ec0d..8d44db8 100644
--- a/packages/ui/src/components/ChatInput.tsx
+++ b/packages/ui/src/components/ChatInput.tsx
@@ -8,7 +8,7 @@
*/
import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
-import { Square, Sparkles } from 'lucide-react';
+import { Square, Sparkles, FileCode, Link } from 'lucide-react';
import { motion } from 'framer-motion';
import clsx from 'clsx';
import { CommandMenu, type CommandMenuItem } from './CommandMenu.js';
@@ -19,6 +19,7 @@ import { AgentModeSelector, type AgentModeType } from './AgentModeSelector.js';
import { useCommands } from '../hooks/useCommands.js';
import { useSystemCommands } from '../hooks/useSystemCommands.js';
import { useFileMention } from '../hooks/useFileMention.js';
+import type { ActiveFileInfo } from '../api/types.js';
interface ChatInputProps {
onSend: (content: string) => void;
@@ -39,6 +40,12 @@ interface ChatInputProps {
autoApprove?: boolean;
/** 自动授权变更回调 */
onAutoApproveChange?: (enabled: boolean) => void;
+ /** 当前编辑器活动文件 */
+ activeFile?: ActiveFileInfo | null;
+ /** 是否自动附加当前编辑器文件 */
+ autoAttachActiveFile?: boolean;
+ /** 自动附加开关变更回调 */
+ onAutoAttachActiveFileToggle?: (enabled: boolean) => void;
}
export function ChatInput({
@@ -53,6 +60,9 @@ export function ChatInput({
onAgentModeChange,
autoApprove = false,
onAutoApproveChange,
+ activeFile,
+ autoAttachActiveFile = true,
+ onAutoAttachActiveFileToggle,
}: ChatInputProps) {
const [input, setInput] = useState('');
const [showCommandMenu, setShowCommandMenu] = useState(false);
@@ -249,7 +259,17 @@ export function ChatInput({
setShowSystemCommandMenu(false);
closeFileMenu();
- onSend(trimmed);
+ // 构建最终消息内容
+ let messageContent = trimmed;
+
+ // 自动附加当前编辑器文件(如果启用且文件未在 @提及中)
+ // 排除 / 斜杠命令和 : 系统命令
+ const isCommand = trimmed.startsWith('/') || trimmed.startsWith(':');
+ if (!isCommand && autoAttachActiveFile && activeFile && !mentionedFiles.includes(activeFile.path)) {
+ messageContent = `@${activeFile.path} ${messageContent}`;
+ }
+
+ onSend(messageContent);
setInput('');
// 重置高度
@@ -370,9 +390,47 @@ export function ChatInput({
: 'border-line hover:border-fg-subtle/30 focus-within:border-primary-500 focus-within:shadow-lg focus-within:shadow-primary-500/10'
)}
>
- {/* 已选文件标签 */}
- {mentionedFiles.length > 0 && (
-
+ {/* 文件标签区域:活动文件 + 已选文件 */}
+ {(activeFile || mentionedFiles.length > 0) && (
+
+ {/* 当前编辑器活动文件 */}
+ {activeFile && !mentionedFiles.includes(activeFile.path) && (
+
+ {/* 自动附加开关 */}
+ {onAutoAttachActiveFileToggle && (
+
+ )}
+ {/* 活动文件标签 */}
+
+
+ {activeFile.name}
+ {autoAttachActiveFile && (
+ (auto)
+ )}
+
+
+ )}
+ {/* @提及的文件 */}
{mentionedFiles.map((file, index) => (
void;
}
-export function IDE({ className, sidebarWidth = 256 }: IDEProps) {
+export function IDE({ className, sidebarWidth = 256, onActiveFileChange }: IDEProps) {
const [tabs, setTabs] = useState([]);
const [activeTabId, setActiveTabId] = useState(null);
const [workingDirectory, setWorkingDirectory] = useState('');
@@ -134,6 +137,23 @@ export function IDE({ className, sidebarWidth = 256 }: IDEProps) {
}
}, [activeTabId, tabs]);
+ // 通知父组件活动文件变化
+ useEffect(() => {
+ if (!onActiveFileChange) return;
+
+ const activeTab = tabs.find((tab) => tab.id === activeTabId);
+ if (activeTab) {
+ onActiveFileChange({
+ path: activeTab.path,
+ name: activeTab.name,
+ content: activeTab.content,
+ language: activeTab.language,
+ });
+ } else {
+ onActiveFileChange(null);
+ }
+ }, [activeTabId, tabs, onActiveFileChange]);
+
// 打开文件
const handleFileSelect = useCallback(async (path: string, name: string) => {
// 检查是否已打开
diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts
index 4164fc3..210968b 100644
--- a/packages/ui/src/index.ts
+++ b/packages/ui/src/index.ts
@@ -199,6 +199,8 @@ export type {
SingleFileDiagnosticsResponse,
AllFilesDiagnosticsResponse,
DiagnosticsResponse,
+ // Active file types
+ ActiveFileInfo,
} from './api/client.js';
// Primitives (shadcn/ui style)
diff --git a/packages/web/src/App.tsx b/packages/web/src/App.tsx
index a5b09c3..b1a269d 100644
--- a/packages/web/src/App.tsx
+++ b/packages/web/src/App.tsx
@@ -23,6 +23,7 @@ import {
listSessions,
createSession,
type Session,
+ type ActiveFileInfo,
} from '@ai-assistant/ui';
import { ChatPage } from './pages/Chat';
@@ -45,6 +46,18 @@ export function App() {
return saved ? parseFloat(saved) : 70;
});
+ // 编辑器联动状态
+ const [activeFile, setActiveFile] = useState(null);
+ const [autoAttachActiveFile, setAutoAttachActiveFile] = useState(() => {
+ const saved = localStorage.getItem('ai-assistant-auto-attach-file');
+ return saved !== 'false'; // 默认开启
+ });
+
+ // 持久化自动附加开关状态
+ useEffect(() => {
+ localStorage.setItem('ai-assistant-auto-attach-file', String(autoAttachActiveFile));
+ }, [autoAttachActiveFile]);
+
// 初始化:加载会话
useEffect(() => {
const HAS_SESSIONS_KEY = 'ai-assistant-has-sessions';
@@ -146,7 +159,7 @@ export function App() {
className="hidden md:flex flex-col"
style={{ width: `${idePanelWidth}%` }}
>
-
+
{/* 可拖拽分割线 */}
@@ -175,6 +188,9 @@ export function App() {
onOpenLSP={() => setShowLSP(true)}
onOpenDiagnostics={() => setShowDiagnostics(true)}
onOpenSessions={() => setShowSessions(true)}
+ activeFile={activeFile}
+ autoAttachActiveFile={autoAttachActiveFile}
+ onAutoAttachActiveFileToggle={setAutoAttachActiveFile}
/>
) : (
diff --git a/packages/web/src/pages/Chat.tsx b/packages/web/src/pages/Chat.tsx
index 06b7fa2..30dfaea 100644
--- a/packages/web/src/pages/Chat.tsx
+++ b/packages/web/src/pages/Chat.tsx
@@ -16,6 +16,7 @@ import {
SubagentProgress,
DiagnosticsIndicator,
ToolbarOverflowMenu,
+ type ActiveFileInfo,
} from '@ai-assistant/ui';
interface ChatPageProps {
@@ -35,6 +36,13 @@ interface ChatPageProps {
onOpenLSP?: () => void;
onOpenDiagnostics?: () => void;
onOpenSessions?: () => void;
+ // 编辑器联动
+ /** 当前编辑器活动文件 */
+ activeFile?: ActiveFileInfo | null;
+ /** 是否自动附加当前编辑器文件 */
+ autoAttachActiveFile?: boolean;
+ /** 自动附加开关变更回调 */
+ onAutoAttachActiveFileToggle?: (enabled: boolean) => void;
}
export function ChatPage({
@@ -52,6 +60,9 @@ export function ChatPage({
onOpenLSP,
onOpenDiagnostics,
onOpenSessions,
+ activeFile,
+ autoAttachActiveFile,
+ onAutoAttachActiveFileToggle,
}: ChatPageProps) {
const {
messages,
@@ -228,6 +239,9 @@ export function ChatPage({
onAgentModeChange={setAgentMode}
autoApprove={autoApprove}
onAutoApproveChange={setAutoApprove}
+ activeFile={activeFile}
+ autoAttachActiveFile={autoAttachActiveFile}
+ onAutoAttachActiveFileToggle={onAutoAttachActiveFileToggle}
/>
{/* Permission Dialog */}