fix: 显示下发智能体的云端图标
This commit is contained in:
17
src/pages/Chat/TeacherAvatar.tsx
Normal file
17
src/pages/Chat/TeacherAvatar.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { useState } from 'react';
|
||||
import { getAgentAvatarSrc } from '@/lib/agent-avatars';
|
||||
import type { TeacherDefinition } from '../../../shared/coding-teacher';
|
||||
|
||||
/** 展示发布版本的图标,缺图或加载失败时使用原有内置头像。 */
|
||||
export function TeacherAvatar({ definition, className }: {
|
||||
definition?: Pick<TeacherDefinition, 'avatar_id' | 'icon_url'> | null;
|
||||
className?: string;
|
||||
}) {
|
||||
const [failedIcon, setFailedIcon] = useState<string | null>(null);
|
||||
const icon = definition?.icon_url;
|
||||
const custom = Boolean(icon && icon !== failedIcon);
|
||||
return <img className={className} alt=""
|
||||
src={custom ? icon! : getAgentAvatarSrc(definition?.avatar_id)}
|
||||
style={{ objectFit: 'contain', imageRendering: custom ? 'auto' : 'pixelated' }}
|
||||
onError={custom ? () => setFailedIcon(icon!) : undefined} />;
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import type {
|
||||
} from '../../../shared/coding-teacher';
|
||||
import { TeacherDiscussionPanel, DiscussionFocus } from './TeacherDiscussionPanel';
|
||||
import type { TeacherDiscussionAction } from '../../../shared/coding-teacher';
|
||||
import { getAgentAvatarSrc } from '@/lib/agent-avatars';
|
||||
import { TeacherAvatar } from './TeacherAvatar';
|
||||
import { TeacherReply } from './TeacherReply';
|
||||
|
||||
export function TeacherChatPanel({
|
||||
@@ -67,7 +67,6 @@ export function TeacherChatPanel({
|
||||
const [historyOpen, setHistoryOpen] = useState(false);
|
||||
const [focus, setFocus] = useState<{ topicId: string; id: string; label: string } | null>(null);
|
||||
const [definition, setDefinition] = useState<TeacherDefinition | null>(null);
|
||||
const avatar = getAgentAvatarSrc(definition?.avatar_id);
|
||||
const externalTopicRef = useRef(externalTopic);
|
||||
externalTopicRef.current = externalTopic;
|
||||
const selectedAgentId = selectedAgent?.teacher_id;
|
||||
@@ -383,7 +382,7 @@ export function TeacherChatPanel({
|
||||
<aside className="consultation-pane flex h-full min-h-0 w-full flex-col"
|
||||
data-testid="teacher-chat-panel" aria-label={`${label}聊天`}>
|
||||
<header className="consultation-header flex h-[68px] shrink-0 items-center gap-3 px-5">
|
||||
<img className="consultation-avatar h-9 w-9 rounded-xl [image-rendering:pixelated]" src={avatar} alt="" />
|
||||
<TeacherAvatar className="consultation-avatar h-9 w-9 rounded-xl" definition={definition} />
|
||||
<div className="min-w-0 flex-1"><h2 className="truncate text-sm font-semibold">{definition?.name ?? '智能体'}</h2>
|
||||
<p className="mt-1 text-[11px] text-muted-foreground">{draftRevision ? '运营草稿试聊' : definition?.description}</p></div>
|
||||
<button aria-label={`${label}新话题`} title="新话题" disabled={busy || !enabled} onClick={() => void newTopic()} className="consult-icon"><Plus className="h-4 w-4" /></button>
|
||||
@@ -407,7 +406,7 @@ export function TeacherChatPanel({
|
||||
}} />}
|
||||
<div ref={scrollRef} className="consultation-messages min-h-0 flex-1 overflow-y-auto px-5 py-6">
|
||||
{!topic?.requests.length && <div className="flex min-h-full flex-col items-center justify-center pb-5 text-center">
|
||||
<img className="mb-6 h-[68px] w-[68px] rounded-3xl [image-rendering:pixelated]" src={avatar} alt="" />
|
||||
<TeacherAvatar className="mb-6 h-[68px] w-[68px] rounded-3xl" definition={definition} />
|
||||
<h3 className="text-lg font-medium tracking-tight">{definition?.welcome_message || '开始对话'}</h3>
|
||||
<div className="mt-6 flex w-full max-w-64 flex-col gap-2">{questions.map((question) => <button key={question} onClick={() => changeText(question)} className="consultation-suggestion flex items-center justify-between gap-2 rounded-lg border bg-white px-3 py-3 text-left text-xs">{question}<ChevronRight className="h-3 w-3 shrink-0 opacity-50" /></button>)}</div>
|
||||
</div>}
|
||||
@@ -416,7 +415,7 @@ export function TeacherChatPanel({
|
||||
? <p className="text-[11px] text-muted-foreground">智能体来看看你的进展</p>
|
||||
: <div className="flex justify-end"><p className="consultation-user max-w-[92%] whitespace-pre-wrap break-words rounded-2xl rounded-br-sm px-4 py-3 text-[13px] leading-7">{request.text}</p></div>}
|
||||
{request.references.length > 0 && <details className="text-xs text-muted-foreground"><summary>本轮引用 {request.references.length} 项</summary>{request.references.map((ref, index) => <pre className="whitespace-pre-wrap" key={index}>{ref.text}</pre>)}</details>}
|
||||
<div className="flex items-center gap-2 text-[11px] text-muted-foreground"><img src={avatar} alt="" className="h-6 w-6 rounded-lg [image-rendering:pixelated]" />{definition?.name ?? label}</div>
|
||||
<div className="flex items-center gap-2 text-[11px] text-muted-foreground"><TeacherAvatar definition={definition} className="h-6 w-6 rounded-lg" />{definition?.name ?? label}</div>
|
||||
{!!request.toolActivity?.length && <details className="text-xs text-muted-foreground">
|
||||
<summary className="cursor-pointer">使用了 {request.toolActivity.length} 项工具</summary>
|
||||
<ul className="mt-2 space-y-2" aria-label="智能体工具活动">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ArrowUpRight, Loader2, X } from 'lucide-react';
|
||||
import type { Ref } from 'react';
|
||||
import { getAgentAvatarSrc } from '@/lib/agent-avatars';
|
||||
import { TeacherAvatar } from './TeacherAvatar';
|
||||
import type { useTeacherCompanion } from './use-teacher-companion';
|
||||
|
||||
type Companion = ReturnType<typeof useTeacherCompanion>;
|
||||
@@ -12,7 +12,6 @@ export function TeacherCompanion({ companion, open, onOpen, bubbleRef }: {
|
||||
}) {
|
||||
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;
|
||||
@@ -28,13 +27,13 @@ export function TeacherCompanion({ companion, open, onOpen, bubbleRef }: {
|
||||
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="" />
|
||||
<span className="teacher-companion-avatar"><TeacherAvatar definition={agent.definition} />
|
||||
{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-avatar"><TeacherAvatar definition={definition} />{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>}
|
||||
@@ -52,7 +51,7 @@ export function TeacherCompanion({ companion, open, onOpen, bubbleRef }: {
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" className="teacher-companion-speaker" aria-label="打开这条智能体消息" onMouseDown={(event) => event.preventDefault()} onClick={viewConversation}>
|
||||
<img src={avatar} alt="" />
|
||||
<TeacherAvatar definition={definition} />
|
||||
</button>
|
||||
</div>}
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user