feat: 在顶栏并排展示下发智能体并直接切换

This commit is contained in:
brother7 committed 2026-09-24 15:38:50 +08:00
1 parent 39b03a1628
commit a10cf00015
15 files changed
+291 -59

No files matched your search

+18 -3
View File
@@ -7,23 +7,38 @@ type Companion = ReturnType<typeof useTeacherCompanion>;
export function TeacherCompanion({ companion, open, onOpen, bubbleRef }: {
companion: Companion;
open: boolean;
onOpen(): void;
onOpen(agentId?: string): void;
bubbleRef?: Ref<HTMLDivElement>;
}) {
const { definition, invitation, welcome, loading, enabled, checking, error } = companion;
const name = definition?.name || '智能体';
const avatar = getAgentAvatarSrc(definition?.avatar_id);
const activeId = companion.selectedAgent?.teacher_id;
const agents = companion.selectedAgent && !companion.agents.some((item) => item.teacher_id === activeId)
? [...companion.agents, companion.selectedAgent] : companion.agents;
const viewConversation = () => { companion.dismiss(); onOpen(); };
const status = loading ? '正在连接智能体…'
: checking ? '智能体在看看你最近的进展…'
: error || (!enabled ? '智能体暂未开放,仍可查看以往讨论' : open ? '我们慢慢聊,想到什么都可以说' : '点我聊聊,想到什么都可以说');
return (
<section className="teacher-companion no-drag" aria-label="智能体陪伴" data-testid="teacher-companion">
<button type="button" className="teacher-companion-person" title={status} aria-label="智能体" aria-expanded={open} aria-controls="coding-consultation-dock" onMouseDown={(event) => event.preventDefault()} onClick={onOpen}>
<nav className="consultation-agent-tabs" aria-label="已下发智能体">
{agents.map((agent) => <button key={agent.teacher_id} type="button" className="teacher-companion-person"
title={agent.definition.description || agent.definition.name} aria-label={agents.length === 1 ? '智能体' : `与${agent.definition.name}聊天`}
aria-expanded={open && activeId === agent.teacher_id} aria-pressed={open && activeId === agent.teacher_id}
aria-controls="coding-consultation-dock" data-agent-id={agent.teacher_id}
onMouseDown={(event) => event.preventDefault()} onClick={() => onOpen(agent.teacher_id)}>
<span className="teacher-companion-avatar"><img src={getAgentAvatarSrc(agent.definition.avatar_id)} alt="" />
{activeId === agent.teacher_id && invitation && <span className="teacher-companion-unread" aria-hidden="true" />}</span>
<span className="teacher-companion-name">{agent.definition.name}</span>
{activeId === agent.teacher_id && checking && <Loader2 className="h-3 w-3 shrink-0 animate-spin text-muted-foreground motion-reduce:animate-none" aria-label="智能体正在思考" />}
</button>)}
{(!agents.length || (!activeId && companion.topic)) && <button type="button" className="teacher-companion-person" title={status} aria-label="智能体" aria-expanded={open} aria-controls="coding-consultation-dock" onMouseDown={(event) => event.preventDefault()} onClick={() => onOpen()}>
<span className="teacher-companion-avatar"><img src={avatar} alt="" />{invitation && <span className="teacher-companion-unread" aria-hidden="true" />}</span>
<span className="teacher-companion-name">{name}</span>
{checking && <Loader2 className="h-3 w-3 shrink-0 animate-spin text-muted-foreground motion-reduce:animate-none" aria-label="智能体正在思考" />}
</button>
</button>}
</nav>
{(invitation || welcome) && <div ref={bubbleRef} className="teacher-companion-presence" data-testid="teacher-presence">
<div className="teacher-companion-bubble" data-testid="teacher-invitation" data-bubble-kind={invitation ? 'check-in' : 'welcome'}>
<div className="teacher-companion-bubble-heading">