import { Bot, ChevronDown, Loader2, MessageCircle, Paperclip, Plus, Send, Trash2, X, } from 'lucide-react'; import { type FormEvent, useEffect, useState, type WheelEvent as ReactWheelEvent, } from 'react'; import { PlatformDangerConfirmDialog } from '@/src/components/common/PlatformDangerConfirmDialog.tsx'; import AttachmentChip from '@/src/components/image-editor/EditorAgentConversation/AttachmentChip.tsx'; import { AttachmentPicker } from '@/src/components/image-editor/EditorAgentConversation/AttachmentPicker.tsx'; import { attachmentKey } from '@/src/components/image-editor/EditorAgentConversation/common.ts'; import { EditorAgentDraftTextarea } from '@/src/components/image-editor/EditorAgentConversation/EditorAgentDraftTextarea.tsx'; import { MessageBubble, ThinkingBubble, } from '@/src/components/image-editor/EditorAgentConversation/MessageBubble.tsx'; import type { CanvasLayer, EditorAsset, } from '@/src/components/image-editor/ImageCanvasEditorTypes.ts'; import { useImageCanvasContextStore } from '../useImageCanvasContextStore.ts'; import { useConversationAttachments } from './useConversationAttachments.ts'; import { type EditorAgentConversationClient, useEditorAgentConversation, } from './useEditorAgentConversation'; type EditorAgentConversationPanelViewProps = { open: boolean; onToggleOpen: () => void; layers?: CanvasLayer[]; assets?: EditorAsset[]; // TODO refactor: move the task list update seperate onConfirmSent?: () => void; client?: EditorAgentConversationClient; }; function stopAgentPanelWheel(event: ReactWheelEvent) { event.stopPropagation(); } export function EditorAgentConversationPanelView({ open, onToggleOpen, layers = [], assets = [], onConfirmSent, client, }: EditorAgentConversationPanelViewProps) { const [hasConversationMounted, setHasConversationMounted] = useState(open); useEffect(() => { if (open) { setHasConversationMounted(true); } }, [open]); const projectId = useImageCanvasContextStore((state) => state.projectId); const effectiveProjectId = hasConversationMounted ? projectId : null; const { conversations, activeConversation, activeConversationId, messages, isLoadingConversations, isLoadingMessages, isCreatingConversation, isDeletingConversation, isWaiting, isPatienceNoticeVisible, toolCallAction, isToolCallActionPending, errorMessage, createConversation, selectConversation, refreshActiveConversation, sendMessage, confirmToolCall, cancelToolCall, deleteActiveConversation, } = useEditorAgentConversation({ projectId: effectiveProjectId, client, onConfirmSent, }); const [draftText, setDraftText] = useState(''); const { attachments, attachmentError, isPastingAttachment, attachmentPickerOpen, attachmentPickerTab, draftAttachmentKeys, canvasAttachmentOptions, libraryAttachmentOptions, setAttachmentPickerTab, openAttachmentPicker, closeAttachmentPicker, toggleAttachmentKey, applyAttachmentSelection, referenceContextAsset, handleInputPaste, removeAttachment, consumeAttachments, restoreAttachments, } = useConversationAttachments({ projectId, layers, assets }); const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false); const hasProject = Boolean(projectId?.trim()); const isConversationBusy = isWaiting || isToolCallActionPending; const hasCurrentConversationContent = messages.length > 0; const isConversationSelectDisabled = !conversations.length || isLoadingConversations || isCreatingConversation || isConversationBusy; const isMessageSubmissionBlocked = isCreatingConversation || isLoadingConversations || isLoadingMessages || isWaiting || isToolCallActionPending || isPastingAttachment || !draftText.trim() || !hasProject; const currentConversationTitle = activeConversation?.title ?? '新对话'; const handleCreateConversation = () => { void createConversation().catch(() => undefined); }; const submitMessage = (event: FormEvent) => { event.preventDefault(); if (isMessageSubmissionBlocked) { return; } const text = draftText.trim(); setDraftText(''); const nextAttachments = consumeAttachments(); void sendMessage(text, nextAttachments).catch(() => { setDraftText((currentText) => (currentText ? currentText : text)); restoreAttachments(nextAttachments); }); }; if (!open) { return ( ); } return ( <> setDeleteConfirmOpen(false)} onConfirm={() => { void deleteActiveConversation().then(() => setDeleteConfirmOpen(false), ); }} > 确认删除这个对话吗? ); }