fix(router): route casual chat to DeepSeek and reserve Cursor for page tasks
Default unmatched conversation to direct_chat, stop blanket Agent session continuation and generic Agent→Cursor promotion; fix WeChat page retry userPublishDir scope and simplify ChatPanel controls. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+25
-103
@@ -1,5 +1,5 @@
|
||||
import { ChangeEvent, useCallback, useEffect, useLayoutEffect, useRef, useState, type ClipboardEvent, type MutableRefObject } from 'react';
|
||||
import { BrainCircuit, Database, Image, ImageOff, ImagePlus } from 'lucide-react';
|
||||
import { BrainCircuit } from 'lucide-react';
|
||||
import { useNetworkStatus } from '../hooks/useNetworkStatus';
|
||||
import { openAvatarPicker } from '../utils/userAvatar';
|
||||
import {
|
||||
@@ -11,11 +11,7 @@ import {
|
||||
} from '../utils/chatSkills';
|
||||
import { getMessageSaveActions } from '../utils/messageSave';
|
||||
import { getDisplayText } from '../utils/message';
|
||||
import {
|
||||
IMAGE_GENERATION_MODE_LABELS,
|
||||
nextImageGenerationMode,
|
||||
type ImageGenerationMode,
|
||||
} from '../utils/imageGeneration';
|
||||
import type { ImageGenerationMode } from '../utils/imageGeneration';
|
||||
import {
|
||||
downloadChatMessageDocx,
|
||||
} from '../api/client';
|
||||
@@ -39,6 +35,7 @@ import { ChatSkillPicker } from './ChatSkillPicker';
|
||||
import { ChatSkillIcon } from './ChatSkillIcons';
|
||||
import { PageTemplateShopModal } from './PageTemplateShopModal';
|
||||
import { ChatLoadingSpinner } from './ChatLoadingSpinner';
|
||||
import { useTaskLoadingTip } from '../hooks/useTaskLoadingTip';
|
||||
import { ChatSharePreviewModal } from './ChatSharePreviewModal';
|
||||
import { ChatPlazaPublishModal } from './ChatPlazaPublishModal';
|
||||
import { MessageList } from './MessageList';
|
||||
@@ -183,6 +180,7 @@ export function ChatPanel({
|
||||
onBalanceUpdate,
|
||||
onGrantedSkillsUpdate,
|
||||
onOpenRecharge,
|
||||
taskLoadingTip = null,
|
||||
}: {
|
||||
variant: 'full' | 'compact';
|
||||
user?: PortalUser | null;
|
||||
@@ -231,6 +229,7 @@ export function ChatPanel({
|
||||
onBalanceUpdate?: (balanceCents: number) => void;
|
||||
onGrantedSkillsUpdate?: (skills: string[]) => void;
|
||||
onOpenRecharge?: () => void;
|
||||
taskLoadingTip?: string | null;
|
||||
}) {
|
||||
const online = useNetworkStatus();
|
||||
const [input, setInput] = useState('');
|
||||
@@ -240,12 +239,8 @@ export function ChatPanel({
|
||||
const [plazaPublishSource, setPlazaPublishSource] = useState<Message | null>(null);
|
||||
const [voiceNotice, setVoiceNotice] = useState<string | null>(null);
|
||||
const [forceDeepReasoning, setForceDeepReasoning] = useState(false);
|
||||
const [pgRequired, setPgRequired] = useState(false);
|
||||
const [imageGenerationMode, setImageGenerationMode] = useState<ImageGenerationMode>('auto');
|
||||
const [imageGenerationTipVisible, setImageGenerationTipVisible] = useState(false);
|
||||
const [pgTipVisible, setPgTipVisible] = useState(false);
|
||||
const [deepReasoningTipVisible, setDeepReasoningTipVisible] = useState(false);
|
||||
const [chatControlOnboardingStep, setChatControlOnboardingStep] = useState<0 | 1 | 2 | null>(null);
|
||||
const [chatControlOnboardingStep, setChatControlOnboardingStep] = useState<0 | 1 | null>(null);
|
||||
const [templateShopOpen, setTemplateShopOpen] = useState(false);
|
||||
const [activeTemplatePrefill, setActiveTemplatePrefill] = useState<{
|
||||
skillId: string;
|
||||
@@ -261,8 +256,6 @@ export function ChatPanel({
|
||||
const [voiceStopSignal, setVoiceStopSignal] = useState(0);
|
||||
const pendingSkillRef = useRef<string | null>(null);
|
||||
const chatInputRef = useRef<HTMLTextAreaElement>(null);
|
||||
const imageGenerationTipTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const pgTipTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const deepReasoningTipTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const [randomPrompt] = useState(
|
||||
() => CHAT_PLACEHOLDER_PROMPTS[Math.floor(Math.random() * CHAT_PLACEHOLDER_PROMPTS.length)],
|
||||
@@ -282,6 +275,7 @@ export function ChatPanel({
|
||||
}>(null);
|
||||
const prevMessageCountRef = useRef(messages.length);
|
||||
const showAssistantTyping = chatState === 'waiting' || chatState === 'streaming';
|
||||
const displayLoadingTip = useTaskLoadingTip(showAssistantTyping, taskLoadingTip);
|
||||
|
||||
inputRef.current = input;
|
||||
pendingImagesRef.current = pendingImages;
|
||||
@@ -337,19 +331,12 @@ export function ChatPanel({
|
||||
setChatControlOnboardingStep(0);
|
||||
const timers = [
|
||||
window.setTimeout(() => setChatControlOnboardingStep(1), 2_200),
|
||||
window.setTimeout(() => setChatControlOnboardingStep(2), 4_400),
|
||||
window.setTimeout(() => setChatControlOnboardingStep(null), 6_600),
|
||||
window.setTimeout(() => setChatControlOnboardingStep(null), 4_400),
|
||||
];
|
||||
return () => timers.forEach((timer) => window.clearTimeout(timer));
|
||||
}, [session?.id]);
|
||||
|
||||
useEffect(() => () => {
|
||||
if (imageGenerationTipTimerRef.current) {
|
||||
clearTimeout(imageGenerationTipTimerRef.current);
|
||||
}
|
||||
if (pgTipTimerRef.current) {
|
||||
clearTimeout(pgTipTimerRef.current);
|
||||
}
|
||||
if (deepReasoningTipTimerRef.current) {
|
||||
clearTimeout(deepReasoningTipTimerRef.current);
|
||||
}
|
||||
@@ -372,26 +359,6 @@ export function ChatPanel({
|
||||
[],
|
||||
);
|
||||
|
||||
const showImageGenerationSlowTip = useCallback(() => {
|
||||
showTransientControlTip(setImageGenerationTipVisible, imageGenerationTipTimerRef);
|
||||
}, [showTransientControlTip]);
|
||||
|
||||
const handlePgToggle = useCallback(() => {
|
||||
setPgRequired((current) => {
|
||||
const next = !current;
|
||||
if (next) {
|
||||
showTransientControlTip(setPgTipVisible, pgTipTimerRef);
|
||||
} else {
|
||||
setPgTipVisible(false);
|
||||
if (pgTipTimerRef.current) {
|
||||
clearTimeout(pgTipTimerRef.current);
|
||||
pgTipTimerRef.current = null;
|
||||
}
|
||||
}
|
||||
return next;
|
||||
});
|
||||
}, [showTransientControlTip]);
|
||||
|
||||
const handleDeepReasoningToggle = useCallback(() => {
|
||||
setForceDeepReasoning((current) => {
|
||||
const next = !current;
|
||||
@@ -408,11 +375,6 @@ export function ChatPanel({
|
||||
});
|
||||
}, [showTransientControlTip]);
|
||||
|
||||
const handleImageGenerationClick = useCallback(() => {
|
||||
setImageGenerationMode((current) => nextImageGenerationMode(current));
|
||||
showImageGenerationSlowTip();
|
||||
}, [showImageGenerationSlowTip]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const container = mainRef.current;
|
||||
if (!container) return;
|
||||
@@ -876,8 +838,8 @@ export function ChatPanel({
|
||||
await onSubmit(trimmed, imagesToSend, previewImagesToSend, {
|
||||
messageId: outgoingMessageId,
|
||||
forceDeepReasoning,
|
||||
pgRequired,
|
||||
imageGenerationMode,
|
||||
pgRequired: false,
|
||||
imageGenerationMode: 'auto',
|
||||
selectedChatSkill,
|
||||
fileAttachments: fileAttachmentsToSend,
|
||||
});
|
||||
@@ -907,8 +869,6 @@ export function ChatPanel({
|
||||
}, [
|
||||
activeTemplatePrefill,
|
||||
forceDeepReasoning,
|
||||
pgRequired,
|
||||
imageGenerationMode,
|
||||
input,
|
||||
onSubmit,
|
||||
onUploadFile,
|
||||
@@ -1118,6 +1078,7 @@ export function ChatPanel({
|
||||
<MessageList
|
||||
messages={messages}
|
||||
streaming={showAssistantTyping}
|
||||
typingTip={displayLoadingTip}
|
||||
onAvatarClick={compact ? undefined : openAvatarPicker}
|
||||
onSaveAsPage={openSaveActions}
|
||||
onShareToPlaza={openPlazaPublish}
|
||||
@@ -1323,14 +1284,19 @@ export function ChatPanel({
|
||||
className={`input chat-input-field chat-input-task-status${uploadFieldClass}`}
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
aria-label={taskInputStatusText}
|
||||
aria-label={[taskInputStatusText, displayLoadingTip].filter(Boolean).join(' ')}
|
||||
>
|
||||
<span className="chat-input-task-status-text">{taskInputStatusText}</span>
|
||||
<span className="typing-dots chat-input-task-status-dots" aria-hidden="true">
|
||||
<span />
|
||||
<span />
|
||||
<span />
|
||||
</span>
|
||||
<div className="chat-input-task-status-main">
|
||||
<span className="chat-input-task-status-text">{taskInputStatusText}</span>
|
||||
<span className="typing-dots chat-input-task-status-dots" aria-hidden="true">
|
||||
<span />
|
||||
<span />
|
||||
<span />
|
||||
</span>
|
||||
</div>
|
||||
{displayLoadingTip ? (
|
||||
<span className="chat-input-task-status-tip">{displayLoadingTip}</span>
|
||||
) : null}
|
||||
</div>
|
||||
) : (
|
||||
<textarea
|
||||
@@ -1396,51 +1362,7 @@ export function ChatPanel({
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
className={`chat-deep-reasoning-toggle chat-icon-toggle${pgRequired ? ' is-active' : ''}${pgTipVisible ? ' chat-icon-toggle-tip-active' : ''}${chatControlOnboardingStep === 1 ? ' chat-control-onboarding-active' : ''}`}
|
||||
disabled={voiceDisabled}
|
||||
role="checkbox"
|
||||
aria-checked={pgRequired}
|
||||
aria-label="使用 PostgreSQL 数据空间"
|
||||
title="点击开启后,本轮生成的可保存数据必须使用你的专属 PostgreSQL 数据空间"
|
||||
onClick={handlePgToggle}
|
||||
>
|
||||
<Database className="chat-deep-reasoning-toggle-icon" aria-hidden="true" />
|
||||
<span className="chat-icon-toggle-label">db</span>
|
||||
{pgTipVisible && (
|
||||
<span className="chat-control-onboarding-tip chat-icon-toggle-tip" role="status">
|
||||
数据将写入你的 PostgreSQL 专属空间
|
||||
</span>
|
||||
)}
|
||||
{chatControlOnboardingStep === 1 && (
|
||||
<span className="chat-control-onboarding-tip" role="status">需要长期使用的数据,放进专属空间</span>
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`chat-deep-reasoning-toggle chat-icon-toggle chat-image-generation-toggle is-${imageGenerationMode}${imageGenerationMode === 'required' ? ' is-active' : ''}${imageGenerationTipVisible ? ' chat-icon-toggle-tip-active' : ''}`}
|
||||
disabled={voiceDisabled}
|
||||
role="checkbox"
|
||||
aria-checked={imageGenerationMode === 'auto' ? 'mixed' : imageGenerationMode === 'required'}
|
||||
aria-label={`AI 配图:${IMAGE_GENERATION_MODE_LABELS[imageGenerationMode]}`}
|
||||
title={`AI 配图:${IMAGE_GENERATION_MODE_LABELS[imageGenerationMode]}。启用后会加载智能生图 skill,自动补全场景、构图、光线和负面约束。点击切换为${IMAGE_GENERATION_MODE_LABELS[nextImageGenerationMode(imageGenerationMode)]}`}
|
||||
onClick={handleImageGenerationClick}
|
||||
>
|
||||
{imageGenerationMode === 'required' ? (
|
||||
<ImagePlus className="chat-deep-reasoning-toggle-icon" aria-hidden="true" />
|
||||
) : imageGenerationMode === 'disabled' ? (
|
||||
<ImageOff className="chat-deep-reasoning-toggle-icon" aria-hidden="true" />
|
||||
) : (
|
||||
<Image className="chat-deep-reasoning-toggle-icon" aria-hidden="true" />
|
||||
)}
|
||||
{imageGenerationTipVisible && (
|
||||
<span className="chat-control-onboarding-tip chat-icon-toggle-tip" role="status">
|
||||
图片生成较慢,请耐心等待
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`chat-deep-reasoning-toggle chat-icon-toggle${forceDeepReasoning ? ' is-active' : ''}${deepReasoningTipVisible ? ' chat-icon-toggle-tip-active' : ''}${chatControlOnboardingStep === 2 ? ' chat-control-onboarding-active' : ''}`}
|
||||
className={`chat-deep-reasoning-toggle chat-icon-toggle${forceDeepReasoning ? ' is-active' : ''}${deepReasoningTipVisible ? ' chat-icon-toggle-tip-active' : ''}${chatControlOnboardingStep === 1 ? ' chat-control-onboarding-active' : ''}`}
|
||||
disabled={voiceDisabled}
|
||||
role="checkbox"
|
||||
aria-checked={forceDeepReasoning}
|
||||
@@ -1455,7 +1377,7 @@ export function ChatPanel({
|
||||
深度推理耗时较长,请耐心等待
|
||||
</span>
|
||||
)}
|
||||
{chatControlOnboardingStep === 2 && (
|
||||
{chatControlOnboardingStep === 1 && (
|
||||
<span className="chat-control-onboarding-tip" role="status">复杂问题,开启深度推理</span>
|
||||
)}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user