fix: isolate canvas mode switches
This commit is contained in:
parent
3f8219f415
commit
d4936a8ca4
@ -311,6 +311,14 @@ interface CanvasChatPanelProps {
|
||||
|
||||
type CanvasComposerMode = "agent" | "image" | "video" | "detail" | "clawd";
|
||||
|
||||
function getCanvasComposerModeSessionLabel(mode: CanvasComposerMode) {
|
||||
if (mode === "clawd") return "Clawd";
|
||||
if (mode === "video") return "视频";
|
||||
if (mode === "detail") return "AI详情";
|
||||
if (mode === "image") return "图像";
|
||||
return "Agent";
|
||||
}
|
||||
|
||||
type VideoStoryboardStreamData =
|
||||
| {
|
||||
stage: "detecting";
|
||||
@ -1321,6 +1329,7 @@ export function CanvasChatPanel({
|
||||
() => requestedChatSessionId || chatId
|
||||
);
|
||||
const activeChatIdRef = useRef<string>(requestedChatSessionId || chatId);
|
||||
const activeMessagesRef = useRef<ChatMessage[]>([]);
|
||||
const inFlightChatIdRef = useRef<string | null>(null);
|
||||
const inFlightRawChatIdRef = useRef<string | null>(null);
|
||||
const [chatSessions, setChatSessions] = useState<CanvasChatSession[]>([]);
|
||||
@ -1528,6 +1537,29 @@ export function CanvasChatPanel({
|
||||
);
|
||||
}, [getSendGuard]);
|
||||
|
||||
const startNewChatSession = useCallback(
|
||||
(options?: { title?: string; toastMessage?: string }) => {
|
||||
const now = Date.now();
|
||||
const newChatSessionId = `canvas-${crypto.randomUUID()}`;
|
||||
const fallbackTitle = `对话 ${chatSessions.length + 1}`;
|
||||
const newSession: CanvasChatSession = {
|
||||
id: newChatSessionId,
|
||||
title: options?.title || fallbackTitle,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
forceHistoryHydrationRef.current = null;
|
||||
releaseSendGuard(activeChatId);
|
||||
setChatSessions((prevSessions) => [newSession, ...prevSessions]);
|
||||
setActiveChatId(newChatSessionId);
|
||||
replaceChatSessionQuery(newChatSessionId);
|
||||
toast.success(options?.toastMessage || "已新建聊天窗口");
|
||||
return newChatSessionId;
|
||||
},
|
||||
[activeChatId, chatSessions.length, releaseSendGuard, replaceChatSessionQuery]
|
||||
);
|
||||
|
||||
const handleTypewriterActiveChange = useCallback((typingKey: string, active: boolean) => {
|
||||
setActiveTypingSections((prev) => {
|
||||
if (active) {
|
||||
@ -3479,6 +3511,21 @@ export function CanvasChatPanel({
|
||||
: isPresetAgentSelection
|
||||
? getToolbarItem("chatModePresetAgent").label
|
||||
: "Agent";
|
||||
const hasActiveChatContext = useCallback(() => {
|
||||
const editorText = (textareaRef.current?.textContent || "")
|
||||
.replace(/\u200b/g, " ")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
const hasDetailSlotContent = detailSlots.some((slot) => Boolean(slot?.url));
|
||||
return (
|
||||
activeMessagesRef.current.length > 0 ||
|
||||
Boolean(input.trim()) ||
|
||||
Boolean(editorText) ||
|
||||
attachments.length > 0 ||
|
||||
Boolean(selectedSkillTagId) ||
|
||||
hasDetailSlotContent
|
||||
);
|
||||
}, [attachments.length, detailSlots, input, selectedSkillTagId]);
|
||||
const getComposerModeMenuItemClass = useCallback(
|
||||
(mode: CanvasComposerMode) =>
|
||||
cn(
|
||||
@ -3801,6 +3848,58 @@ export function CanvasChatPanel({
|
||||
toast.error("Clawd 未开启");
|
||||
return;
|
||||
}
|
||||
if (mode === "agent") {
|
||||
if (!effectiveAgentEnabled) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (mode === "detail" && !canUseDetailGeneration) {
|
||||
toast.error("AI详情生成功能未开启");
|
||||
return;
|
||||
}
|
||||
|
||||
const preferredImageModelId =
|
||||
mode === "image"
|
||||
? allModels.find(
|
||||
(model) =>
|
||||
model.id === lastImageModelId &&
|
||||
model.id !== "auto" &&
|
||||
isCanvasImageModel(model) &&
|
||||
!model.isMultimodal
|
||||
)?.id || firstImageModelId
|
||||
: null;
|
||||
if (mode === "image" && !preferredImageModelId) {
|
||||
toast.error("当前没有可用的图像模型");
|
||||
return;
|
||||
}
|
||||
|
||||
const preferredVideoModelId =
|
||||
mode === "video"
|
||||
? allModels.find((model) => model.id === lastVideoModelId && isCanvasVideoModel(model))?.id ||
|
||||
firstVideoModelId
|
||||
: null;
|
||||
if (mode === "video" && !preferredVideoModelId) {
|
||||
toast.error("当前没有可用的视频模型");
|
||||
return;
|
||||
}
|
||||
|
||||
const isSwitchingMode = mode !== activeComposerMode;
|
||||
if (
|
||||
isSwitchingMode &&
|
||||
(getSendGuard(activeChatId).inFlight || isClawdSubmitting || isDetailGenerating)
|
||||
) {
|
||||
toast.message("当前对话正在处理中,完成后再切换模式");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSwitchingMode && hasActiveChatContext()) {
|
||||
const modeLabel = getCanvasComposerModeSessionLabel(mode);
|
||||
startNewChatSession({
|
||||
title: `${modeLabel} 对话`,
|
||||
toastMessage: `已为 ${modeLabel} 新建聊天窗口`,
|
||||
});
|
||||
}
|
||||
|
||||
activeComposerModeRef.current = mode;
|
||||
setComposerModeOverride(mode);
|
||||
|
||||
@ -3810,9 +3909,6 @@ export function CanvasChatPanel({
|
||||
}
|
||||
|
||||
if (mode === "agent") {
|
||||
if (!effectiveAgentEnabled) {
|
||||
return;
|
||||
}
|
||||
const preferredAgentModelId =
|
||||
allModels.find(
|
||||
(model) =>
|
||||
@ -3826,10 +3922,6 @@ export function CanvasChatPanel({
|
||||
}
|
||||
|
||||
if (mode === "detail") {
|
||||
if (!canUseDetailGeneration) {
|
||||
toast.error("AI详情生成功能未开启");
|
||||
return;
|
||||
}
|
||||
const detailModelId = effectiveAgentEnabled ? "auto" : "";
|
||||
currentModelIdRef.current = detailModelId;
|
||||
setSelectedModelId(detailModelId);
|
||||
@ -3839,35 +3931,16 @@ export function CanvasChatPanel({
|
||||
}
|
||||
|
||||
if (mode === "image") {
|
||||
const preferredImageModelId =
|
||||
allModels.find(
|
||||
(model) =>
|
||||
model.id === lastImageModelId &&
|
||||
model.id !== "auto" &&
|
||||
isCanvasImageModel(model) &&
|
||||
!model.isMultimodal
|
||||
)?.id || firstImageModelId;
|
||||
|
||||
if (!preferredImageModelId) {
|
||||
toast.error("当前没有可用的图像模型");
|
||||
return;
|
||||
}
|
||||
currentModelIdRef.current = preferredImageModelId;
|
||||
setSelectedModelId(preferredImageModelId);
|
||||
const nextImageModelId = preferredImageModelId || "";
|
||||
currentModelIdRef.current = nextImageModelId;
|
||||
setSelectedModelId(nextImageModelId);
|
||||
setIsDetailGenerationMode(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const preferredVideoModelId =
|
||||
allModels.find((model) => model.id === lastVideoModelId && isCanvasVideoModel(model))?.id ||
|
||||
firstVideoModelId;
|
||||
|
||||
if (!preferredVideoModelId) {
|
||||
toast.error("当前没有可用的视频模型");
|
||||
return;
|
||||
}
|
||||
currentModelIdRef.current = preferredVideoModelId;
|
||||
setSelectedModelId(preferredVideoModelId);
|
||||
const nextVideoModelId = preferredVideoModelId || "";
|
||||
currentModelIdRef.current = nextVideoModelId;
|
||||
setSelectedModelId(nextVideoModelId);
|
||||
setIsDetailGenerationMode(false);
|
||||
},
|
||||
[
|
||||
@ -3875,16 +3948,21 @@ export function CanvasChatPanel({
|
||||
effectiveAgentEnabled,
|
||||
isClawdModeEnabled,
|
||||
allModels,
|
||||
computedComposerMode,
|
||||
activeChatId,
|
||||
activeComposerMode,
|
||||
firstImageModelId,
|
||||
firstVideoModelId,
|
||||
hasSelectedAgentPreset,
|
||||
getSendGuard,
|
||||
hasActiveChatContext,
|
||||
isClawdSubmitting,
|
||||
isDetailGenerating,
|
||||
lastConversationModelId,
|
||||
lastImageModelId,
|
||||
isCanvasConversationOnlyModel,
|
||||
isCanvasImageModel,
|
||||
isCanvasVideoModel,
|
||||
lastVideoModelId,
|
||||
startNewChatSession,
|
||||
]
|
||||
);
|
||||
|
||||
@ -5262,6 +5340,10 @@ export function CanvasChatPanel({
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
activeMessagesRef.current = messages;
|
||||
}, [messages]);
|
||||
|
||||
useEffect(() => {
|
||||
if (status === "ready") {
|
||||
releaseSendGuard(activeChatId);
|
||||
@ -10126,22 +10208,8 @@ export function CanvasChatPanel({
|
||||
);
|
||||
|
||||
const createNewChatSession = useCallback(() => {
|
||||
const now = Date.now();
|
||||
const newChatSessionId = `canvas-${crypto.randomUUID()}`;
|
||||
const fallbackTitle = `对话 ${chatSessions.length + 1}`;
|
||||
const newSession: CanvasChatSession = {
|
||||
id: newChatSessionId,
|
||||
title: fallbackTitle,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
releaseSendGuard(activeChatId);
|
||||
setChatSessions((prevSessions) => [newSession, ...prevSessions]);
|
||||
setActiveChatId(newChatSessionId);
|
||||
replaceChatSessionQuery(newChatSessionId);
|
||||
toast.success("已新建聊天窗口");
|
||||
}, [activeChatId, chatSessions.length, releaseSendGuard, replaceChatSessionQuery]);
|
||||
startNewChatSession();
|
||||
}, [startNewChatSession]);
|
||||
|
||||
const switchToSession = useCallback((sessionId: string) => {
|
||||
if (!sessionId || sessionId === activeChatId) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user