mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-29 21:07:40 +01:00
Refine selected-agent chat header layout
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
e913431cf2
commit
bc43558c58
2 files changed
+98
-45
No files matched your search
@@ -91,6 +91,10 @@ describe("SelectedAgentChatView", () => {
|
||||
flushSync(() => root.render(node));
|
||||
}
|
||||
|
||||
function countText(text: string, needle: string): number {
|
||||
return text.split(needle).length - 1;
|
||||
}
|
||||
|
||||
it("renders the real selected-agent identity in the header", () => {
|
||||
render(
|
||||
<SelectedAgentChatView
|
||||
@@ -106,6 +110,46 @@ describe("SelectedAgentChatView", () => {
|
||||
expect(container.textContent?.toLowerCase()).not.toContain("concierge");
|
||||
});
|
||||
|
||||
it("uses the left-side selector as the selected-agent identity without repeating the role", () => {
|
||||
render(
|
||||
<SelectedAgentChatView
|
||||
agents={[ceo, eng]}
|
||||
targetAgentId={ceo.id}
|
||||
comments={[]}
|
||||
onSend={async () => {}}
|
||||
onTargetAgentChange={() => {}}
|
||||
/>,
|
||||
);
|
||||
|
||||
const header = container.querySelector(
|
||||
'[data-testid="selected-agent-chat-header"]',
|
||||
) as HTMLDivElement | null;
|
||||
const switcher = header?.querySelector('[aria-label="Choose chat agent"]');
|
||||
expect(switcher).not.toBeNull();
|
||||
expect(switcher?.textContent).toContain("Sarah");
|
||||
expect(countText(switcher?.textContent ?? "", "CEO")).toBe(1);
|
||||
expect(header?.textContent).toBe(switcher?.textContent);
|
||||
});
|
||||
|
||||
it("pads the chat body to align with the selected-agent header", () => {
|
||||
render(
|
||||
<SelectedAgentChatView
|
||||
agents={[ceo]}
|
||||
targetAgentId={ceo.id}
|
||||
comments={[]}
|
||||
onSend={async () => {}}
|
||||
/>,
|
||||
);
|
||||
|
||||
const body = container.querySelector(
|
||||
'[data-testid="selected-agent-chat-body"]',
|
||||
) as HTMLDivElement | null;
|
||||
expect(body).not.toBeNull();
|
||||
expect(body?.className).toContain("px-4");
|
||||
expect(body?.className).toContain("pt-3");
|
||||
expect(body?.className).toContain("pb-4");
|
||||
});
|
||||
|
||||
it("shows a loading indicator while the first fetch is in flight", () => {
|
||||
render(
|
||||
<SelectedAgentChatView agents={[ceo]} targetAgentId={ceo.id} comments={[]} loading onSend={async () => {}} />,
|
||||
|
||||
@@ -184,28 +184,14 @@ export function SelectedAgentChatView({
|
||||
return (
|
||||
<div className={cn("flex min-h-0 min-w-0 flex-1 flex-col", className)}>
|
||||
{/* Identity header — real selected agent (no board-concierge persona). */}
|
||||
<div className="relative flex shrink-0 items-center justify-between gap-2 px-4 py-3">
|
||||
<div
|
||||
data-testid="selected-agent-chat-header"
|
||||
className="relative flex shrink-0 items-center gap-2 px-4 py-3"
|
||||
>
|
||||
<div
|
||||
className="pointer-events-none absolute bottom-0 left-0 right-0 h-px bg-border"
|
||||
aria-hidden
|
||||
/>
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
<Avatar size="sm" className="shrink-0">
|
||||
<AvatarFallback>
|
||||
{targetAgent?.icon ? (
|
||||
<AgentIcon icon={targetAgent.icon} className="h-3.5 w-3.5" />
|
||||
) : (
|
||||
agentInitials(targetName)
|
||||
)}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="min-w-0">
|
||||
<div className="truncate text-sm font-semibold text-foreground">{targetName}</div>
|
||||
{targetRole ? (
|
||||
<div className="truncate text-xs text-muted-foreground">{targetRole}</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
{canSwitch ? (
|
||||
<Select
|
||||
value={targetAgentId ?? undefined}
|
||||
@@ -213,12 +199,12 @@ export function SelectedAgentChatView({
|
||||
>
|
||||
<SelectTrigger
|
||||
size="sm"
|
||||
className="w-auto min-w-[9rem] gap-2"
|
||||
className="h-auto w-auto min-w-0 max-w-full gap-2 px-2 py-1.5"
|
||||
aria-label="Choose chat agent"
|
||||
>
|
||||
<SelectValue placeholder="Choose agent" />
|
||||
</SelectTrigger>
|
||||
<SelectContent align="end">
|
||||
<SelectContent align="start">
|
||||
{invokableAgents.map((a) => (
|
||||
<SelectItem key={a.id} value={a.id}>
|
||||
<span className="flex items-center gap-2">
|
||||
@@ -232,7 +218,25 @@ export function SelectedAgentChatView({
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
) : null}
|
||||
) : (
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
<Avatar size="sm" className="shrink-0">
|
||||
<AvatarFallback>
|
||||
{targetAgent?.icon ? (
|
||||
<AgentIcon icon={targetAgent.icon} className="h-3.5 w-3.5" />
|
||||
) : (
|
||||
agentInitials(targetName)
|
||||
)}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="min-w-0">
|
||||
<div className="truncate text-sm font-semibold text-foreground">{targetName}</div>
|
||||
{targetRole ? (
|
||||
<div className="truncate text-xs text-muted-foreground">{targetRole}</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{errorText ? (
|
||||
@@ -266,30 +270,35 @@ export function SelectedAgentChatView({
|
||||
<Loader2 className="h-5 w-5 animate-spin" aria-label="Loading conversation" />
|
||||
</div>
|
||||
) : (
|
||||
<IssueChatThread
|
||||
variant="full"
|
||||
comments={comments}
|
||||
interactions={interactions}
|
||||
liveRuns={liveRuns}
|
||||
activeRun={activeRun}
|
||||
feedbackVotes={feedbackVotes}
|
||||
issueId={issueId}
|
||||
companyId={companyId}
|
||||
projectId={projectId}
|
||||
agentMap={agentMap}
|
||||
currentUserId={currentUserId}
|
||||
mentions={mentions}
|
||||
emptyMessage={
|
||||
emptyMessage ?? `Send ${targetName} a message to start the conversation.`
|
||||
}
|
||||
onAdd={handleAdd}
|
||||
onStopRun={onStopRun}
|
||||
onVote={onVote}
|
||||
onAcceptInteraction={onAcceptInteraction}
|
||||
onRejectInteraction={onRejectInteraction}
|
||||
onSubmitInteractionAnswers={onSubmitInteractionAnswers}
|
||||
onCancelInteraction={onCancelInteraction}
|
||||
/>
|
||||
<div
|
||||
data-testid="selected-agent-chat-body"
|
||||
className="min-w-0 px-4 pb-4 pt-3"
|
||||
>
|
||||
<IssueChatThread
|
||||
variant="full"
|
||||
comments={comments}
|
||||
interactions={interactions}
|
||||
liveRuns={liveRuns}
|
||||
activeRun={activeRun}
|
||||
feedbackVotes={feedbackVotes}
|
||||
issueId={issueId}
|
||||
companyId={companyId}
|
||||
projectId={projectId}
|
||||
agentMap={agentMap}
|
||||
currentUserId={currentUserId}
|
||||
mentions={mentions}
|
||||
emptyMessage={
|
||||
emptyMessage ?? `Send ${targetName} a message to start the conversation.`
|
||||
}
|
||||
onAdd={handleAdd}
|
||||
onStopRun={onStopRun}
|
||||
onVote={onVote}
|
||||
onAcceptInteraction={onAcceptInteraction}
|
||||
onRejectInteraction={onRejectInteraction}
|
||||
onSubmitInteractionAnswers={onSubmitInteractionAnswers}
|
||||
onCancelInteraction={onCancelInteraction}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user