后端: - split_frontmatter 兼容 CRLF/LF 行尾符,修复 Windows 下 EXPERT.md 解析失败 - Config::load_from 移除 fallback 到 cwd/config.json 的逻辑,避免静默加载项目目录配置导致 experts 字段缺失 - ExpertRuntime 新增 update_config 方法,save_config 时同步更新 ExpertRuntime 内部 config,sources 变更即时生效 - source_order 空数组不再兜底返回 [User, Project],尊重用户关闭所有源的意图 - from_config_with_cwd 增加诊断日志,输出 enabled/sources/cwd/discovered 前端: - ExpertSelector 弹窗改为按钮上方居中展开 - 专家卡片 name 完整显示(break-all),路径显示在卡片底部 - "管理专家"入口仅在无专家时显示 - 每次打开下拉都重新拉取列表和选中状态 - 监听设置弹窗关闭事件刷新已选专家(处理已选专家被禁用的情况)
68 lines
2.1 KiB
TypeScript
68 lines
2.1 KiB
TypeScript
import { useState } from 'react'
|
||
import { MessageList } from './MessageList'
|
||
import { MessageInput } from './MessageInput'
|
||
import { ExpertSelector } from './ExpertSelector'
|
||
import type { ChatMessage, Attachment } from '../../types/protocol'
|
||
|
||
interface ChatContainerProps {
|
||
messages: ChatMessage[]
|
||
isLoading: boolean
|
||
isReadOnly?: boolean
|
||
channelName?: string
|
||
onSendMessage: (content: string, attachments: Attachment[]) => void
|
||
onNavigateToSubAgent?: (taskId: string, description: string, subagentType?: string) => void
|
||
onStop?: () => void
|
||
showThinking?: boolean
|
||
/** 视图标识,用于保存/恢复滚动位置 */
|
||
viewKey?: string
|
||
/** 高亮的消息 ID */
|
||
highlightedMessageId?: string | null
|
||
/** 当前 session ID,用于专家选择 */
|
||
sessionId?: string | null
|
||
/** 打开设置页(用于专家管理入口) */
|
||
onOpenSettings?: () => void
|
||
/** 设置弹窗关闭信号(每次关闭递增,用于触发 ExpertSelector 刷新) */
|
||
settingsClosedTick?: number
|
||
}
|
||
|
||
export function ChatContainer({
|
||
messages,
|
||
isLoading,
|
||
isReadOnly = false,
|
||
channelName,
|
||
onSendMessage,
|
||
onNavigateToSubAgent,
|
||
onStop,
|
||
showThinking = true,
|
||
viewKey,
|
||
highlightedMessageId,
|
||
sessionId,
|
||
onOpenSettings,
|
||
settingsClosedTick,
|
||
}: ChatContainerProps) {
|
||
const [selectedExpert, setSelectedExpert] = useState<{ name: string; description: string } | null>(null)
|
||
|
||
return (
|
||
<div className="flex h-full flex-col relative">
|
||
<div className="flex-1 overflow-hidden relative">
|
||
<MessageList messages={messages} onNavigateToSubAgent={onNavigateToSubAgent} showThinking={showThinking} viewKey={viewKey} highlightedMessageId={highlightedMessageId} />
|
||
</div>
|
||
<ExpertSelector
|
||
sessionId={sessionId ?? null}
|
||
onManageExperts={onOpenSettings}
|
||
onSelectionChange={setSelectedExpert}
|
||
settingsClosedTick={settingsClosedTick}
|
||
/>
|
||
<MessageInput
|
||
onSend={onSendMessage}
|
||
onStop={onStop}
|
||
disabled={isLoading}
|
||
isLoading={isLoading}
|
||
isReadOnly={isReadOnly}
|
||
channelName={channelName}
|
||
selectedExpert={selectedExpert}
|
||
/>
|
||
</div>
|
||
)
|
||
}
|