feat(image): complete reviewed generation delivery
Memind CI / Test, build, and release guards (pull_request) Successful in 2m47s

This commit is contained in:
john
2026-07-20 20:04:44 +08:00
parent fc3d27aae0
commit 4b15610aa8
42 changed files with 1764 additions and 97 deletions
+32 -3
View File
@@ -226,6 +226,7 @@ export function ChatPanel({
const [forceDeepReasoning, setForceDeepReasoning] = useState(false);
const [pgRequired, setPgRequired] = useState(false);
const [imageGenerationMode, setImageGenerationMode] = useState<ImageGenerationMode>('auto');
const [imageGenerationTipVisible, setImageGenerationTipVisible] = useState(false);
const [chatControlOnboardingStep, setChatControlOnboardingStep] = useState<0 | 1 | 2 | null>(null);
const [pendingImages, setPendingImages] = useState<PendingChatImage[]>([]);
const [pendingFiles, setPendingFiles] = useState<PendingChatFile[]>([]);
@@ -235,6 +236,7 @@ export function ChatPanel({
const [fileError, setFileError] = useState<string | null>(null);
const [voiceStopSignal, setVoiceStopSignal] = useState(0);
const pendingSkillRef = useRef<string | null>(null);
const imageGenerationTipTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const [randomPrompt] = useState(
() => CHAT_PLACEHOLDER_PROMPTS[Math.floor(Math.random() * CHAT_PLACEHOLDER_PROMPTS.length)],
);
@@ -314,6 +316,28 @@ export function ChatPanel({
return () => timers.forEach((timer) => window.clearTimeout(timer));
}, [session?.id]);
useEffect(() => () => {
if (imageGenerationTipTimerRef.current) {
clearTimeout(imageGenerationTipTimerRef.current);
}
}, []);
const showImageGenerationSlowTip = useCallback(() => {
setImageGenerationTipVisible(true);
if (imageGenerationTipTimerRef.current) {
clearTimeout(imageGenerationTipTimerRef.current);
}
imageGenerationTipTimerRef.current = setTimeout(() => {
setImageGenerationTipVisible(false);
imageGenerationTipTimerRef.current = null;
}, 5_000);
}, []);
const handleImageGenerationClick = useCallback(() => {
setImageGenerationMode((current) => nextImageGenerationMode(current));
showImageGenerationSlowTip();
}, [showImageGenerationSlowTip]);
useLayoutEffect(() => {
const container = mainRef.current;
if (!container) return;
@@ -1240,13 +1264,13 @@ export function ChatPanel({
</label>
<button
type="button"
className={`chat-deep-reasoning-toggle chat-image-generation-toggle is-${imageGenerationMode}${imageGenerationMode === 'required' ? ' is-active' : ''}`}
className={`chat-deep-reasoning-toggle chat-image-generation-toggle is-${imageGenerationMode}${imageGenerationMode === 'required' ? ' is-active' : ''}${imageGenerationTipVisible ? ' chat-image-generation-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]}。点击切换为${IMAGE_GENERATION_MODE_LABELS[nextImageGenerationMode(imageGenerationMode)]}`}
onClick={() => setImageGenerationMode((current) => nextImageGenerationMode(current))}
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" />
@@ -1258,6 +1282,11 @@ export function ChatPanel({
<span className="chat-image-generation-state" aria-hidden="true">
{imageGenerationMode === 'auto' ? 'A' : imageGenerationMode === 'required' ? '✓' : '×'}
</span>
{imageGenerationTipVisible && (
<span className="chat-control-onboarding-tip chat-image-generation-tip" role="status">
</span>
)}
</button>
<label
className={`chat-deep-reasoning-toggle${forceDeepReasoning ? ' is-active' : ''}${chatControlOnboardingStep === 2 ? ' chat-control-onboarding-active' : ''}`}