feat: 添加 :new 系统命令创建新会话

- Core: 新增 :new/:n 命令返回 new_session action
- Server: 处理 new_session action 创建新会话
- UI: useChat 添加 onSessionSwitch 回调
- Web/Desktop: ChatPage 和 App 实现会话切换逻辑
This commit is contained in:
2025-12-17 19:36:47 +08:00
parent e0444a966f
commit 48a11ff077
7 changed files with 63 additions and 4 deletions
@@ -4,12 +4,14 @@
import type { SystemCommand } from '../types.js';
import { clearCommand } from './clear.js';
import { newCommand } from './new.js';
/**
* 所有内置系统命令
*/
export const builtinSystemCommands: SystemCommand[] = [
clearCommand,
newCommand,
];
export { clearCommand };
export { clearCommand, newCommand };
@@ -0,0 +1,22 @@
/**
* :new 系统命令
*
* 创建新会话并切换到新会话
*/
import type { SystemCommand } from '../types.js';
export const newCommand: SystemCommand = {
name: 'new',
description: '创建新会话并切换',
aliases: ['n'],
execute: async (_context) => {
// 实际的创建操作由 Server 层处理
// 这里只返回操作指令
return {
success: true,
message: '正在创建新会话...',
action: { type: 'new_session' },
};
},
};
+4
View File
@@ -16,6 +16,8 @@ import {
interface ChatPageProps {
sessionId: string;
onSessionUpdated?: (sessionId: string, name: string) => void;
/** 切换会话回调(如 :new 命令创建新会话) */
onSessionSwitch?: (newSessionId: string) => void;
// 工具栏按钮
showFileBrowser?: boolean;
onToggleFileBrowser?: () => void;
@@ -31,6 +33,7 @@ interface ChatPageProps {
export function ChatPage({
sessionId,
onSessionUpdated,
onSessionSwitch,
showFileBrowser,
onToggleFileBrowser,
onOpenConfig,
@@ -55,6 +58,7 @@ export function ChatPage({
console.error('Chat error:', error);
},
onSessionUpdated,
onSessionSwitch,
onConfigError: (error) => {
toast.error(error.message, {
duration: 10000,
+13 -1
View File
@@ -322,7 +322,19 @@ async function handleSystemCommand(sessionId: string, content: string): Promise<
sessionManager.updateStatus(sessionId, 'idle');
break;
}
// 可以在这里添加其他操作类型的处理
case 'new_session': {
// 获取当前会话信息以复用配置
const currentSession = sessionManager.get(sessionId);
const workdir = currentSession?.workdir || process.cwd();
// 创建新会话
const newSession = await sessionManager.create({ workdir });
// 更新 action 中的 sessionId
result.action = { type: 'new_session', sessionId: newSession.id };
break;
}
}
}
+11 -2
View File
@@ -33,6 +33,8 @@ interface UseChatOptions {
onSessionUpdated?: (sessionId: string, name: string) => void;
/** 配置错误回调(如 API Key 未配置) */
onConfigError?: (error: ConfigErrorPayload) => void;
/** 切换会话回调(如 :new 命令创建新会话) */
onSessionSwitch?: (newSessionId: string) => void;
}
interface ChatState {
@@ -52,7 +54,7 @@ interface ChatState {
currentSubagent: SubagentState | null;
}
export function useChat({ sessionId, onError, onSessionNotFound, onSessionUpdated, onConfigError }: UseChatOptions) {
export function useChat({ sessionId, onError, onSessionNotFound, onSessionUpdated, onConfigError, onSessionSwitch }: UseChatOptions) {
const [state, setState] = useState<ChatState>({
messages: [],
isConnected: false,
@@ -77,10 +79,12 @@ export function useChat({ sessionId, onError, onSessionNotFound, onSessionUpdate
const onSessionNotFoundRef = useRef(onSessionNotFound);
const onSessionUpdatedRef = useRef(onSessionUpdated);
const onConfigErrorRef = useRef(onConfigError);
const onSessionSwitchRef = useRef(onSessionSwitch);
onErrorRef.current = onError;
onSessionNotFoundRef.current = onSessionNotFound;
onSessionUpdatedRef.current = onSessionUpdated;
onConfigErrorRef.current = onConfigError;
onSessionSwitchRef.current = onSessionSwitch;
// 加载历史消息
const loadMessages = useCallback(async () => {
@@ -572,7 +576,7 @@ export function useChat({ sessionId, onError, onSessionNotFound, onSessionUpdate
success: boolean;
message?: string;
error?: string;
action?: { type: string };
action?: { type: string; sessionId?: string };
};
// 处理清空消息操作
@@ -585,6 +589,11 @@ export function useChat({ sessionId, onError, onSessionNotFound, onSessionUpdate
}));
}
// 处理新建会话操作
if (payload.success && payload.action?.type === 'new_session' && payload.action.sessionId) {
onSessionSwitchRef.current?.(payload.action.sessionId);
}
// 如果有错误,通过 onError 回调通知
if (!payload.success && payload.error) {
onErrorRef.current?.(new Error(payload.error));
+6
View File
@@ -103,6 +103,11 @@ export function App() {
setSessionTitleUpdate({ sessionId, name });
}, []);
// 会话切换回调(:new 命令创建新会话后切换)
const handleSessionSwitch = useCallback((newSessionId: string) => {
setCurrentSessionId(newSessionId);
}, []);
// 处理面板宽度调整
const handleResize = useCallback((delta: number) => {
setIdePanelWidth((prev) => {
@@ -159,6 +164,7 @@ export function App() {
sessionId={currentSessionId}
onSessionNotFound={handleSessionNotFound}
onSessionUpdated={handleSessionUpdated}
onSessionSwitch={handleSessionSwitch}
responsive
onOpenCommands={() => setShowCommands(true)}
onOpenMCP={() => setShowMCP(true)}
+4
View File
@@ -22,6 +22,8 @@ interface ChatPageProps {
sessionId: string;
onSessionNotFound?: () => void;
onSessionUpdated?: (sessionId: string, name: string) => void;
/** 切换会话回调(如 :new 命令创建新会话) */
onSessionSwitch?: (newSessionId: string) => void;
responsive?: boolean;
// 工具栏按钮
onOpenCommands?: () => void;
@@ -39,6 +41,7 @@ export function ChatPage({
sessionId,
onSessionNotFound,
onSessionUpdated,
onSessionSwitch,
responsive = false,
onOpenCommands,
onOpenMCP,
@@ -74,6 +77,7 @@ export function ChatPage({
},
onSessionNotFound,
onSessionUpdated,
onSessionSwitch,
onConfigError: (error) => {
toast.error(error.message, {
duration: 10000,