import { BotDashboardView } from './components/BotDashboardView'; import { useBotDashboardModule } from './hooks/useBotDashboardModule'; import type { BotDashboardModuleProps } from './types'; import { formatBytes, formatWorkspaceTime, } from './utils'; import './BotDashboardModule.css'; import './components/DashboardShared.css'; export function BotDashboardModule({ onOpenImageFactory, forcedBotId, compactMode = false, compactPanelTab: compactPanelTabProp, onCompactPanelTabChange, }: BotDashboardModuleProps) { const dashboard = useBotDashboardModule({ forcedBotId, compactMode, compactPanelTab: compactPanelTabProp, onCompactPanelTabChange, }); const runtimeMoreLabel = dashboard.isZh ? '更多' : 'More'; const botListPanelProps = { bots: dashboard.bots, filteredBots: dashboard.filteredBots, pagedBots: dashboard.pagedBots, selectedBotId: dashboard.selectedBotId, normalizedBotListQuery: dashboard.normalizedBotListQuery, botListQuery: dashboard.botListQuery, botListPageSizeReady: dashboard.botListPageSizeReady, botListPage: dashboard.botListPage, botListTotalPages: dashboard.botListTotalPages, botListMenuOpen: dashboard.botListMenuOpen, controlStateByBot: dashboard.controlStateByBot, operatingBotId: dashboard.operatingBotId, compactMode: dashboard.compactMode, isZh: dashboard.isZh, isLoadingTemplates: dashboard.isLoadingTemplates, isBatchOperating: dashboard.isBatchOperating, labels: { batchStart: dashboard.t.batchStart, batchStop: dashboard.t.batchStop, botSearchNoResult: dashboard.t.botSearchNoResult, botSearchPlaceholder: dashboard.t.botSearchPlaceholder, clearSearch: dashboard.t.clearSearch, delete: dashboard.t.delete, disable: dashboard.t.disable, disabled: dashboard.t.disabled, enable: dashboard.t.enable, extensions: dashboard.t.extensions, image: dashboard.t.image, manageImages: dashboard.t.manageImages, newBot: dashboard.t.newBot, paginationNext: dashboard.t.paginationNext, paginationPage: dashboard.t.paginationPage, paginationPrev: dashboard.t.paginationPrev, searchAction: dashboard.t.searchAction, start: dashboard.t.start, stop: dashboard.t.stop, syncingPageSize: dashboard.t.syncingPageSize, templateManager: dashboard.t.templateManager, titleBots: dashboard.t.titleBots, }, botSearchInputName: dashboard.botSearchInputName, botListMenuRef: dashboard.botListMenuRef, onOpenCreateWizard: () => dashboard.setShowCreateBotModal(true), onOpenImageFactory, onToggleMenu: () => dashboard.setBotListMenuOpen((prev) => !prev), onCloseMenu: () => dashboard.setBotListMenuOpen(false), onOpenTemplateManager: dashboard.openTemplateManager, onBatchStartBots: dashboard.batchStartBots, onBatchStopBots: dashboard.batchStopBots, onBotListQueryChange: dashboard.setBotListQuery, onBotListPageChange: dashboard.setBotListPage, onSelectBot: dashboard.setSelectedBotId, onSetCompactPanelTab: (tab: 'chat' | 'runtime') => dashboard.setCompactPanelTab(tab), onSetBotEnabled: dashboard.setBotEnabled, onStartBot: dashboard.startBot, onStopBot: dashboard.stopBot, onOpenResourceMonitor: dashboard.openResourceMonitor, onRemoveBot: dashboard.removeBot, }; const topicFeedPanelProps = { isZh: dashboard.isZh, topicKey: dashboard.topicFeedTopicKey, topicOptions: dashboard.activeTopicOptions, topicState: dashboard.topicPanelState, items: dashboard.topicFeedItems, loading: dashboard.topicFeedLoading, loadingMore: dashboard.topicFeedLoadingMore, nextCursor: dashboard.topicFeedNextCursor, error: dashboard.topicFeedError, readSavingById: dashboard.topicFeedReadSavingById, deleteSavingById: dashboard.topicFeedDeleteSavingById, onTopicChange: dashboard.setTopicFeedTopicKey, onRefresh: () => void dashboard.loadTopicFeed({ append: false, topicKey: dashboard.topicFeedTopicKey }), onMarkRead: (itemId: number) => void dashboard.markTopicFeedItemRead(itemId), onDeleteItem: (item: (typeof dashboard.topicFeedItems)[number]) => void dashboard.deleteTopicFeedItem(item), onLoadMore: () => void dashboard.loadTopicFeed({ append: true, cursor: dashboard.topicFeedNextCursor, topicKey: dashboard.topicFeedTopicKey }), onOpenWorkspacePath: (path: string) => void dashboard.openWorkspacePathFromChat(path), resolveWorkspaceMediaSrc: dashboard.resolveWorkspaceMediaSrc, onOpenTopicSettings: dashboard.openTopicConfigModal, onDetailOpenChange: dashboard.setTopicDetailOpen, layout: 'panel' as const, }; const dashboardChatPanelProps = { conversation: dashboard.conversation, isZh: dashboard.isZh, labels: { badReply: dashboard.t.badReply, botDisabledHint: dashboard.t.botDisabledHint, botStarting: dashboard.t.botStarting, botStopping: dashboard.t.botStopping, chatDisabled: dashboard.t.chatDisabled, close: dashboard.t.close, controlCommandsHide: dashboard.t.controlCommandsHide, controlCommandsShow: dashboard.t.controlCommandsShow, copyPrompt: dashboard.t.copyPrompt, copyReply: dashboard.t.copyReply, deleteMessage: dashboard.t.deleteMessage, disabledPlaceholder: dashboard.t.disabledPlaceholder, download: dashboard.t.download, editPrompt: dashboard.t.editPrompt, fileNotPreviewable: dashboard.t.fileNotPreviewable, goodReply: dashboard.t.goodReply, inputPlaceholder: dashboard.t.inputPlaceholder, interrupt: dashboard.t.interrupt, noConversation: dashboard.t.noConversation, previewTitle: dashboard.t.previewTitle, quoteReply: dashboard.t.quoteReply, quotedReplyLabel: dashboard.t.quotedReplyLabel, send: dashboard.t.send, thinking: dashboard.t.thinking, uploadFile: dashboard.t.uploadFile, uploadingFile: dashboard.t.uploadingFile, user: dashboard.t.user, voiceStart: dashboard.t.voiceStart, voiceStop: dashboard.t.voiceStop, voiceTranscribing: dashboard.t.voiceTranscribing, you: dashboard.t.you, }, chatScrollRef: dashboard.chatScrollRef, onChatScroll: dashboard.onChatScroll, expandedProgressByKey: dashboard.expandedProgressByKey, expandedUserByKey: dashboard.expandedUserByKey, deletingMessageIdMap: dashboard.deletingMessageIdMap, feedbackSavingByMessageId: dashboard.feedbackSavingByMessageId, markdownComponents: dashboard.markdownComponents, workspaceDownloadExtensionSet: dashboard.workspaceDownloadExtensionSet, onToggleProgressExpand: dashboard.toggleProgressExpanded, onToggleUserExpand: dashboard.toggleUserExpanded, onEditUserPrompt: dashboard.editUserPrompt, onCopyUserPrompt: dashboard.copyUserPrompt, onDeleteConversationMessage: dashboard.deleteConversationMessage, onOpenWorkspacePath: dashboard.openWorkspacePathFromChat, onSubmitAssistantFeedback: dashboard.submitAssistantFeedback, onQuoteAssistantReply: dashboard.quoteAssistantReply, onCopyAssistantReply: dashboard.copyAssistantReply, isThinking: dashboard.isThinking, canChat: dashboard.canChat, isChatEnabled: dashboard.isChatEnabled, speechEnabled: dashboard.speechEnabled, selectedBotEnabled: dashboard.selectedBotEnabled, selectedBotControlState: dashboard.selectedBotControlState, quotedReply: dashboard.quotedReply, onClearQuotedReply: () => dashboard.setQuotedReply(null), pendingAttachments: dashboard.pendingAttachments, onRemovePendingAttachment: (path: string) => dashboard.setPendingAttachments((prev) => prev.filter((value) => value !== path)), attachmentUploadPercent: dashboard.attachmentUploadPercent, isUploadingAttachments: dashboard.isUploadingAttachments, filePickerRef: dashboard.filePickerRef, allowedAttachmentExtensions: dashboard.allowedAttachmentExtensions, onPickAttachments: dashboard.onPickAttachments, controlCommandPanelOpen: dashboard.controlCommandPanelOpen, controlCommandPanelRef: dashboard.controlCommandPanelRef, onToggleControlCommandPanel: () => { dashboard.setChatDatePickerOpen(false); dashboard.setControlCommandPanelOpen((prev) => !prev); }, activeControlCommand: dashboard.activeControlCommand, canSendControlCommand: dashboard.canSendControlCommand, isInterrupting: dashboard.isInterrupting, onSendControlCommand: dashboard.sendControlCommand, onInterruptExecution: dashboard.interruptExecution, chatDateTriggerRef: dashboard.chatDateTriggerRef, hasSelectedBot: Boolean(dashboard.selectedBotId), chatDateJumping: dashboard.chatDateJumping, onToggleChatDatePicker: dashboard.toggleChatDatePicker, chatDatePickerOpen: dashboard.chatDatePickerOpen, chatDatePanelPosition: dashboard.chatDatePanelPosition, chatDateValue: dashboard.chatDateValue, onChatDateValueChange: dashboard.setChatDateValue, onCloseChatDatePicker: () => dashboard.setChatDatePickerOpen(false), onJumpConversationToDate: dashboard.jumpConversationToDate, command: dashboard.command, onCommandChange: dashboard.setCommand, composerTextareaRef: dashboard.composerTextareaRef, onComposerKeyDown: dashboard.onComposerKeyDown, isVoiceRecording: dashboard.isVoiceRecording, isVoiceTranscribing: dashboard.isVoiceTranscribing, isCompactMobile: dashboard.isCompactMobile, voiceCountdown: dashboard.voiceCountdown, onVoiceInput: dashboard.onVoiceInput, onTriggerPickAttachments: dashboard.triggerPickAttachments, showInterruptSubmitAction: dashboard.showInterruptSubmitAction, onSubmitAction: () => (dashboard.showInterruptSubmitAction ? dashboard.interruptExecution() : dashboard.send()), }; const runtimePanelProps = { selectedBot: dashboard.selectedBot, selectedBotEnabled: dashboard.selectedBotEnabled, operatingBotId: dashboard.operatingBotId, runtimeMenuOpen: dashboard.runtimeMenuOpen, runtimeMenuRef: dashboard.runtimeMenuRef, displayState: dashboard.displayState, workspaceError: dashboard.workspaceError, workspacePathDisplay: dashboard.workspacePathDisplay, workspaceLoading: dashboard.workspaceLoading, workspaceQuery: dashboard.workspaceQuery, workspaceSearchInputName: dashboard.workspaceSearchInputName, workspaceSearchLoading: dashboard.workspaceSearchLoading, filteredWorkspaceEntries: dashboard.filteredWorkspaceEntries, workspaceParentPath: dashboard.workspaceParentPath, workspaceFileLoading: dashboard.workspaceFileLoading, workspaceDownloadExtensionSet: dashboard.workspaceDownloadExtensionSet, workspaceAutoRefresh: dashboard.workspaceAutoRefresh, hasPreviewFiles: dashboard.workspaceFiles.length > 0, isCompactHidden: dashboard.compactMode && (dashboard.isCompactListPage || dashboard.compactPanelTab !== 'runtime'), showCompactSurface: dashboard.showCompactBotPageClose, emptyStateText: dashboard.forcedBotMissing ? `${dashboard.t.noTelemetry}: ${String(dashboard.forcedBotId).trim()}` : dashboard.t.noTelemetry, labels: { agent: dashboard.t.agent, autoRefresh: dashboard.lc.autoRefresh, base: dashboard.t.base, channels: dashboard.t.channels, clearHistory: dashboard.t.clearHistory, clearSearch: dashboard.t.clearSearch, cronViewer: dashboard.t.cronViewer, download: dashboard.t.download, emptyDir: dashboard.t.emptyDir, envParams: dashboard.t.envParams, exportHistory: dashboard.t.exportHistory, fileNotPreviewable: dashboard.t.fileNotPreviewable, folder: dashboard.t.folder, goUp: dashboard.t.goUp, goUpTitle: dashboard.t.goUpTitle, loadingDir: dashboard.t.loadingDir, mcp: dashboard.t.mcp, more: runtimeMoreLabel, noPreviewFile: dashboard.t.noPreviewFile, openingPreview: dashboard.t.openingPreview, openFolderTitle: dashboard.t.openFolderTitle, params: dashboard.t.params, previewTitle: dashboard.t.previewTitle, refreshHint: dashboard.lc.refreshHint, restart: dashboard.t.restart, runtime: dashboard.t.runtime, searchAction: dashboard.t.searchAction, skills: dashboard.t.skills, topic: dashboard.t.topic, workspaceHint: dashboard.t.workspaceHint, workspaceOutputs: dashboard.t.workspaceOutputs, workspaceSearchNoResult: dashboard.t.workspaceSearchNoResult, workspaceSearchPlaceholder: dashboard.t.workspaceSearchPlaceholder, }, onRestartBot: dashboard.restartBot, onToggleRuntimeMenu: () => dashboard.setRuntimeMenuOpen((prev) => !prev), onOpenBaseConfig: dashboard.openBaseConfigModal, onOpenParamConfig: dashboard.openParamConfigModal, onOpenChannelConfig: dashboard.openChannelConfigModal, onOpenTopicConfig: dashboard.openTopicConfigModal, onOpenEnvParams: dashboard.openEnvParamsConfigModal, onOpenSkills: dashboard.openSkillsConfigModal, onOpenMcpConfig: dashboard.openMcpConfigModal, onOpenCronJobs: dashboard.openCronJobsModal, onOpenAgentFiles: dashboard.openAgentFilesModal, onExportHistory: () => { dashboard.setRuntimeMenuOpen(false); dashboard.exportConversationJson(); }, onClearHistory: async () => { dashboard.setRuntimeMenuOpen(false); await dashboard.clearConversationHistory(); }, onRefreshWorkspace: () => dashboard.selectedBot ? dashboard.loadWorkspaceTree(dashboard.selectedBot.id, dashboard.workspaceCurrentPath) : undefined, onWorkspaceQueryChange: dashboard.setWorkspaceQuery, onWorkspaceQueryClear: () => dashboard.setWorkspaceQuery(''), onWorkspaceQuerySearch: () => dashboard.setWorkspaceQuery((value) => value.trim()), onToggleWorkspaceAutoRefresh: () => dashboard.setWorkspaceAutoRefresh((prev) => !prev), onLoadWorkspaceTree: dashboard.loadWorkspaceTree, onOpenWorkspaceFilePreview: dashboard.openWorkspaceFilePreview, onShowWorkspaceHoverCard: dashboard.showWorkspaceHoverCard, onHideWorkspaceHoverCard: dashboard.hideWorkspaceHoverCard, }; const dashboardModalStackProps = { resourceMonitorModal: { open: dashboard.showResourceModal, botId: dashboard.resourceBotId, resourceBot: dashboard.resourceBot, resourceSnapshot: dashboard.resourceSnapshot, resourceLoading: dashboard.resourceLoading, resourceError: dashboard.resourceError, isZh: dashboard.isZh, closeLabel: dashboard.t.close, onClose: () => dashboard.setShowResourceModal(false), onRefresh: dashboard.loadResourceSnapshot, }, baseConfigModal: { open: dashboard.showBaseModal, selectedBotId: dashboard.selectedBot?.id || '', editForm: dashboard.editForm, paramDraft: dashboard.paramDraft, baseImageOptions: dashboard.baseImageOptions, systemTimezoneOptions: dashboard.systemTimezoneOptions, defaultSystemTimezone: dashboard.defaultSystemTimezone, passwordToggleLabels: dashboard.passwordToggleLabels, isSaving: dashboard.isSaving, isZh: dashboard.isZh, labels: { accessPassword: dashboard.t.accessPassword, accessPasswordPlaceholder: dashboard.t.accessPasswordPlaceholder, baseConfig: dashboard.t.baseConfig, baseImageReadonly: dashboard.t.baseImageReadonly, botIdReadonly: dashboard.t.botIdReadonly, botName: dashboard.t.botName, botNamePlaceholder: dashboard.t.botNamePlaceholder, cancel: dashboard.t.cancel, close: dashboard.t.close, save: dashboard.t.save, }, onClose: () => dashboard.setShowBaseModal(false), onEditFormChange: dashboard.updateEditForm, onParamDraftChange: dashboard.updateParamDraft, onSave: () => dashboard.saveBot('base'), }, paramConfigModal: { open: dashboard.showParamModal, editForm: dashboard.editForm, paramDraft: dashboard.paramDraft, passwordToggleLabels: dashboard.passwordToggleLabels, isZh: dashboard.isZh, isTestingProvider: dashboard.isTestingProvider, providerTestResult: dashboard.providerTestResult, isSaving: dashboard.isSaving, labels: { cancel: dashboard.t.cancel, close: dashboard.t.close, modelName: dashboard.t.modelName, modelNamePlaceholder: dashboard.t.modelNamePlaceholder, modelParams: dashboard.t.modelParams, newApiKey: dashboard.t.newApiKey, newApiKeyPlaceholder: dashboard.t.newApiKeyPlaceholder, saveParams: dashboard.t.saveParams, testModelConnection: dashboard.t.testModelConnection, testing: dashboard.t.testing, }, onClose: () => dashboard.setShowParamModal(false), onEditFormChange: dashboard.updateEditForm, onParamDraftChange: dashboard.updateParamDraft, onProviderChange: dashboard.onBaseProviderChange, onTestProviderConnection: dashboard.testProviderConnection, onSave: () => dashboard.saveBot('params'), }, channelConfigModal: dashboard.channelConfigModalProps, topicConfigModal: dashboard.topicConfigModalProps, skillsModal: dashboard.skillsModalProps, skillMarketInstallModal: dashboard.skillMarketInstallModalProps, mcpConfigModal: dashboard.mcpConfigModalProps, envParamsModal: dashboard.envParamsModalProps, cronJobsModal: dashboard.cronJobsModalProps, templateManagerModal: { open: dashboard.showTemplateModal, templateTab: dashboard.templateTab, templateAgentCount: dashboard.templateAgentCount, templateTopicCount: dashboard.templateTopicCount, templateAgentText: dashboard.templateAgentText, templateTopicText: dashboard.templateTopicText, isSavingTemplates: dashboard.isSavingTemplates, labels: { cancel: dashboard.t.cancel, close: dashboard.t.close, processing: dashboard.t.processing, save: dashboard.t.save, templateManagerTitle: dashboard.t.templateManagerTitle, templateTabAgent: dashboard.t.templateTabAgent, templateTabTopic: dashboard.t.templateTabTopic, }, onClose: () => dashboard.setShowTemplateModal(false), onTemplateTabChange: dashboard.setTemplateTab, onTemplateAgentTextChange: dashboard.setTemplateAgentText, onTemplateTopicTextChange: dashboard.setTemplateTopicText, onSave: dashboard.saveTemplateManager, }, agentFilesModal: { open: dashboard.showAgentModal, agentTab: dashboard.agentTab, tabValue: String(dashboard.editForm[dashboard.agentFieldByTab[dashboard.agentTab]]), isSaving: dashboard.isSaving, labels: { agentFiles: dashboard.t.agentFiles, cancel: dashboard.t.cancel, close: dashboard.t.close, saveFiles: dashboard.t.saveFiles, }, onClose: () => dashboard.setShowAgentModal(false), onAgentTabChange: dashboard.setAgentTab, onTabValueChange: (nextValue: string) => dashboard.updateAgentTabValue(dashboard.agentTab, nextValue), onSave: () => dashboard.saveBot('agent'), }, runtimeActionModal: { open: dashboard.showRuntimeActionModal, runtimeAction: dashboard.runtimeAction, labels: { close: dashboard.t.close, lastAction: dashboard.t.lastAction, }, onClose: () => dashboard.setShowRuntimeActionModal(false), }, workspacePreviewModal: { isZh: dashboard.isZh, labels: { cancel: dashboard.t.cancel, close: dashboard.t.close, copyAddress: dashboard.t.copyAddress, download: dashboard.t.download, editFile: dashboard.t.editFile, filePreview: dashboard.t.filePreview, fileTruncated: dashboard.t.fileTruncated, save: dashboard.t.save, }, preview: dashboard.workspacePreview, previewFullscreen: dashboard.workspacePreviewFullscreen, previewEditorEnabled: dashboard.workspacePreviewEditorEnabled, previewCanEdit: dashboard.workspacePreviewCanEdit, previewDraft: dashboard.workspacePreviewDraft, previewSaving: dashboard.workspacePreviewSaving, markdownComponents: dashboard.workspacePreviewMarkdownComponents, onClose: dashboard.closeWorkspacePreview, onToggleFullscreen: () => dashboard.setWorkspacePreviewFullscreen((prev) => !prev), onCopyPreviewPath: dashboard.copyWorkspacePreviewPath, onCopyPreviewUrl: dashboard.copyWorkspacePreviewUrl, onPreviewDraftChange: dashboard.setWorkspacePreviewDraft, onSavePreviewMarkdown: dashboard.saveWorkspacePreviewMarkdown, onEnterEditMode: () => dashboard.setWorkspacePreviewMode('edit'), onExitEditMode: () => { dashboard.setWorkspacePreviewDraft(dashboard.workspacePreview?.content || ''); dashboard.setWorkspacePreviewMode('preview'); }, getWorkspaceDownloadHref: dashboard.getWorkspaceDownloadHref, getWorkspaceRawHref: dashboard.getWorkspaceRawHref, }, workspaceHoverCard: { state: dashboard.workspaceHoverCard, isZh: dashboard.isZh, formatWorkspaceTime, formatBytes, }, }; const createBotModalProps = { open: dashboard.showCreateBotModal, onClose: () => dashboard.setShowCreateBotModal(false), onCreated: () => { void dashboard.refresh(); }, }; return ( { dashboard.setSelectedBotId(''); dashboard.setCompactPanelTab('chat'); }} dashboardModalStackProps={dashboardModalStackProps} createBotModalProps={createBotModalProps} /> ); }