feat: 添加 :new 系统命令创建新会话
- Core: 新增 :new/:n 命令返回 new_session action - Server: 处理 new_session action 创建新会话 - UI: useChat 添加 onSessionSwitch 回调 - Web/Desktop: ChatPage 和 App 实现会话切换逻辑
This commit is contained in:
@@ -4,12 +4,14 @@
|
|||||||
|
|
||||||
import type { SystemCommand } from '../types.js';
|
import type { SystemCommand } from '../types.js';
|
||||||
import { clearCommand } from './clear.js';
|
import { clearCommand } from './clear.js';
|
||||||
|
import { newCommand } from './new.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 所有内置系统命令
|
* 所有内置系统命令
|
||||||
*/
|
*/
|
||||||
export const builtinSystemCommands: SystemCommand[] = [
|
export const builtinSystemCommands: SystemCommand[] = [
|
||||||
clearCommand,
|
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' },
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -16,6 +16,8 @@ import {
|
|||||||
interface ChatPageProps {
|
interface ChatPageProps {
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
onSessionUpdated?: (sessionId: string, name: string) => void;
|
onSessionUpdated?: (sessionId: string, name: string) => void;
|
||||||
|
/** 切换会话回调(如 :new 命令创建新会话) */
|
||||||
|
onSessionSwitch?: (newSessionId: string) => void;
|
||||||
// 工具栏按钮
|
// 工具栏按钮
|
||||||
showFileBrowser?: boolean;
|
showFileBrowser?: boolean;
|
||||||
onToggleFileBrowser?: () => void;
|
onToggleFileBrowser?: () => void;
|
||||||
@@ -31,6 +33,7 @@ interface ChatPageProps {
|
|||||||
export function ChatPage({
|
export function ChatPage({
|
||||||
sessionId,
|
sessionId,
|
||||||
onSessionUpdated,
|
onSessionUpdated,
|
||||||
|
onSessionSwitch,
|
||||||
showFileBrowser,
|
showFileBrowser,
|
||||||
onToggleFileBrowser,
|
onToggleFileBrowser,
|
||||||
onOpenConfig,
|
onOpenConfig,
|
||||||
@@ -55,6 +58,7 @@ export function ChatPage({
|
|||||||
console.error('Chat error:', error);
|
console.error('Chat error:', error);
|
||||||
},
|
},
|
||||||
onSessionUpdated,
|
onSessionUpdated,
|
||||||
|
onSessionSwitch,
|
||||||
onConfigError: (error) => {
|
onConfigError: (error) => {
|
||||||
toast.error(error.message, {
|
toast.error(error.message, {
|
||||||
duration: 10000,
|
duration: 10000,
|
||||||
|
|||||||
@@ -322,7 +322,19 @@ async function handleSystemCommand(sessionId: string, content: string): Promise<
|
|||||||
sessionManager.updateStatus(sessionId, 'idle');
|
sessionManager.updateStatus(sessionId, 'idle');
|
||||||
break;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ interface UseChatOptions {
|
|||||||
onSessionUpdated?: (sessionId: string, name: string) => void;
|
onSessionUpdated?: (sessionId: string, name: string) => void;
|
||||||
/** 配置错误回调(如 API Key 未配置) */
|
/** 配置错误回调(如 API Key 未配置) */
|
||||||
onConfigError?: (error: ConfigErrorPayload) => void;
|
onConfigError?: (error: ConfigErrorPayload) => void;
|
||||||
|
/** 切换会话回调(如 :new 命令创建新会话) */
|
||||||
|
onSessionSwitch?: (newSessionId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ChatState {
|
interface ChatState {
|
||||||
@@ -52,7 +54,7 @@ interface ChatState {
|
|||||||
currentSubagent: SubagentState | null;
|
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>({
|
const [state, setState] = useState<ChatState>({
|
||||||
messages: [],
|
messages: [],
|
||||||
isConnected: false,
|
isConnected: false,
|
||||||
@@ -77,10 +79,12 @@ export function useChat({ sessionId, onError, onSessionNotFound, onSessionUpdate
|
|||||||
const onSessionNotFoundRef = useRef(onSessionNotFound);
|
const onSessionNotFoundRef = useRef(onSessionNotFound);
|
||||||
const onSessionUpdatedRef = useRef(onSessionUpdated);
|
const onSessionUpdatedRef = useRef(onSessionUpdated);
|
||||||
const onConfigErrorRef = useRef(onConfigError);
|
const onConfigErrorRef = useRef(onConfigError);
|
||||||
|
const onSessionSwitchRef = useRef(onSessionSwitch);
|
||||||
onErrorRef.current = onError;
|
onErrorRef.current = onError;
|
||||||
onSessionNotFoundRef.current = onSessionNotFound;
|
onSessionNotFoundRef.current = onSessionNotFound;
|
||||||
onSessionUpdatedRef.current = onSessionUpdated;
|
onSessionUpdatedRef.current = onSessionUpdated;
|
||||||
onConfigErrorRef.current = onConfigError;
|
onConfigErrorRef.current = onConfigError;
|
||||||
|
onSessionSwitchRef.current = onSessionSwitch;
|
||||||
|
|
||||||
// 加载历史消息
|
// 加载历史消息
|
||||||
const loadMessages = useCallback(async () => {
|
const loadMessages = useCallback(async () => {
|
||||||
@@ -572,7 +576,7 @@ export function useChat({ sessionId, onError, onSessionNotFound, onSessionUpdate
|
|||||||
success: boolean;
|
success: boolean;
|
||||||
message?: string;
|
message?: string;
|
||||||
error?: 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 回调通知
|
// 如果有错误,通过 onError 回调通知
|
||||||
if (!payload.success && payload.error) {
|
if (!payload.success && payload.error) {
|
||||||
onErrorRef.current?.(new Error(payload.error));
|
onErrorRef.current?.(new Error(payload.error));
|
||||||
|
|||||||
@@ -103,6 +103,11 @@ export function App() {
|
|||||||
setSessionTitleUpdate({ sessionId, name });
|
setSessionTitleUpdate({ sessionId, name });
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// 会话切换回调(:new 命令创建新会话后切换)
|
||||||
|
const handleSessionSwitch = useCallback((newSessionId: string) => {
|
||||||
|
setCurrentSessionId(newSessionId);
|
||||||
|
}, []);
|
||||||
|
|
||||||
// 处理面板宽度调整
|
// 处理面板宽度调整
|
||||||
const handleResize = useCallback((delta: number) => {
|
const handleResize = useCallback((delta: number) => {
|
||||||
setIdePanelWidth((prev) => {
|
setIdePanelWidth((prev) => {
|
||||||
@@ -159,6 +164,7 @@ export function App() {
|
|||||||
sessionId={currentSessionId}
|
sessionId={currentSessionId}
|
||||||
onSessionNotFound={handleSessionNotFound}
|
onSessionNotFound={handleSessionNotFound}
|
||||||
onSessionUpdated={handleSessionUpdated}
|
onSessionUpdated={handleSessionUpdated}
|
||||||
|
onSessionSwitch={handleSessionSwitch}
|
||||||
responsive
|
responsive
|
||||||
onOpenCommands={() => setShowCommands(true)}
|
onOpenCommands={() => setShowCommands(true)}
|
||||||
onOpenMCP={() => setShowMCP(true)}
|
onOpenMCP={() => setShowMCP(true)}
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ interface ChatPageProps {
|
|||||||
sessionId: string;
|
sessionId: string;
|
||||||
onSessionNotFound?: () => void;
|
onSessionNotFound?: () => void;
|
||||||
onSessionUpdated?: (sessionId: string, name: string) => void;
|
onSessionUpdated?: (sessionId: string, name: string) => void;
|
||||||
|
/** 切换会话回调(如 :new 命令创建新会话) */
|
||||||
|
onSessionSwitch?: (newSessionId: string) => void;
|
||||||
responsive?: boolean;
|
responsive?: boolean;
|
||||||
// 工具栏按钮
|
// 工具栏按钮
|
||||||
onOpenCommands?: () => void;
|
onOpenCommands?: () => void;
|
||||||
@@ -39,6 +41,7 @@ export function ChatPage({
|
|||||||
sessionId,
|
sessionId,
|
||||||
onSessionNotFound,
|
onSessionNotFound,
|
||||||
onSessionUpdated,
|
onSessionUpdated,
|
||||||
|
onSessionSwitch,
|
||||||
responsive = false,
|
responsive = false,
|
||||||
onOpenCommands,
|
onOpenCommands,
|
||||||
onOpenMCP,
|
onOpenMCP,
|
||||||
@@ -74,6 +77,7 @@ export function ChatPage({
|
|||||||
},
|
},
|
||||||
onSessionNotFound,
|
onSessionNotFound,
|
||||||
onSessionUpdated,
|
onSessionUpdated,
|
||||||
|
onSessionSwitch,
|
||||||
onConfigError: (error) => {
|
onConfigError: (error) => {
|
||||||
toast.error(error.message, {
|
toast.error(error.message, {
|
||||||
duration: 10000,
|
duration: 10000,
|
||||||
|
|||||||
Reference in New Issue
Block a user