import { useEffect, useMemo } from 'react'; import { useChat } from '../context/ChatProvider'; import { INSUFFICIENT_BALANCE_NOTICE } from '../hooks/useTKMindChat'; import { useGoalRunBanner } from '../hooks/useGoalRunBanner'; import type { PageEditSubChatBridge } from '../hooks/usePageEditSubChat'; import type { MindSpaceChatContext, MindSpaceSaveCategory, PortalUser, Message } from '../types'; import { formatContextChip } from '../utils/mindspaceChatContext'; import { shouldShowChatMessage } from '../utils/message'; import { ChatPanel } from './ChatPanel'; import { TKMindAvatar } from './TKMindAvatar'; import { GoalRunAwaitingBanner } from './GoalRunAwaitingBanner'; export function SpaceChatPanel({ open, context, user, onClose, onOpenFullChat, onPageSaved, chatBridge, hideOpenFullChat = false, title = 'TKMind', prefillMessages = [], }: { open: boolean; context: MindSpaceChatContext; user: PortalUser; onClose: () => void; onOpenFullChat: () => void; onPageSaved?: (result: { kind: 'page' | 'category'; pageId?: string; categoryCode?: MindSpaceSaveCategory; }) => void; chatBridge?: PageEditSubChatBridge; hideOpenFullChat?: boolean; title?: string; prefillMessages?: Message[]; }) { const mainChat = useChat(); const chat = chatBridge ?? mainChat; const { session, messages, chatState, pendingTool, submit, stop, approveTool, error, notice, dismissNotice, openRecharge, uploadChatImage, uploadChatAttachment, retryConnect, } = chat; const { capabilities, grantedSkills, followAgentRun, goalRunEnabled } = mainChat; const goalRunBanner = useGoalRunBanner({ userId: user.id, sessionId: session?.id, chatState, submit: (text, options) => void submit(text, options), followAgentRun, featureEnabled: goalRunEnabled, }); useEffect(() => { if (!open) return; const handleKeyDown = (event: KeyboardEvent) => { if (event.key === 'Escape') onClose(); }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [open, onClose]); const displayMessages = useMemo(() => { if (!prefillMessages.length) return messages; if (messages.some((message) => message.role === 'user' && shouldShowChatMessage(message))) return messages; const extras = prefillMessages.filter( (item) => !messages.some((message) => message.id && message.id === item.id), ); return extras.length ? [...messages, ...extras] : messages; }, [messages, prefillMessages]); if (!open) return null; const contextLabel = formatContextChip(context); return (
{title} {contextLabel}
{!hideOpenFullChat ? ( ) : null}
{notice && (
{notice} {notice === INSUFFICIENT_BALANCE_NOTICE ? ( <> ) : ( )}
)} {error && (
{error}
)} {goalRunBanner.enabled && ( )} chatBridge ? void submit( text, { ...context, messageId: options?.messageId, forceDeepReasoning: options?.forceDeepReasoning, pgRequired: options?.pgRequired, imageGenerationMode: options?.imageGenerationMode, fileAttachments: options?.fileAttachments, }, imageUrls, previewImageUrls, ) : void submit( text, { mindspaceContext: context, messageId: options?.messageId, forceDeepReasoning: options?.forceDeepReasoning, pgRequired: options?.pgRequired, imageGenerationMode: options?.imageGenerationMode, fileAttachments: options?.fileAttachments, }, imageUrls, previewImageUrls, ) } onUploadImage={(file, onProgress, options) => uploadChatImage(file, onProgress, { messageId: options?.messageId }) } onUploadFile={(file, onProgress, options) => uploadChatAttachment(file, onProgress, { messageId: options?.messageId }) } onStop={stop} onApproveTool={approveTool} onPageSaved={onPageSaved} />
); }