Skip to content

Commit 835224f

Browse files
author
Erez Sharim
committed
feat: agent description
adds agent description in UI agent card
1 parent 9bee766 commit 835224f

File tree

1 file changed

+95
-48
lines changed

1 file changed

+95
-48
lines changed

chat/src/routes/_chat/_layout/index.tsx

Lines changed: 95 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { fetchClientWithThrow } from "@/lib/api/api";
33
import { cn } from "@/lib/utils";
44
import { ErrorComponent, Link, createFileRoute } from "@tanstack/react-router";
55
import { BotIcon } from "lucide-react";
6+
import { useState, useRef, useEffect } from "react";
67

78
export const Route = createFileRoute("/_chat/_layout/")({
89
loader: async () => {
@@ -26,60 +27,106 @@ function Index() {
2627
<div className="flex flex-1 flex-col gap-4 p-4 justify-center">
2728
<div className="divide-y divide-border overflow-hidden rounded-lg shadow sm:grid sm:grid-cols-2 sm:gap-1 sm:divide-y-0">
2829
{agents.map((agent, agentIdx) => (
29-
<div
30+
<AgentCard
3031
key={agent.id}
32+
agent={agent}
33+
agentIdx={agentIdx}
34+
agents={agents}
35+
/>
36+
))}
37+
</div>
38+
</div>
39+
</div>
40+
);
41+
}
42+
43+
function AgentCard({
44+
agent,
45+
agentIdx,
46+
agents,
47+
}: {
48+
agent: any;
49+
agentIdx: number;
50+
agents: any[];
51+
}) {
52+
const [isExpanded, setIsExpanded] = useState(false);
53+
const [shouldTruncate, setShouldTruncate] = useState(false);
54+
const textRef = useRef<HTMLParagraphElement>(null);
55+
56+
useEffect(() => {
57+
if (textRef.current && agent.description) {
58+
// Check if the text is actually being truncated by comparing scroll height vs client height
59+
const element = textRef.current;
60+
setShouldTruncate(element.scrollHeight > element.clientHeight);
61+
}
62+
}, [agent.description]);
63+
64+
return (
65+
<div
66+
className={cn(
67+
agentIdx === 0 ? "rounded-tl-lg rounded-tr-lg sm:rounded-tr-none" : "",
68+
agentIdx === 1 ? "sm:rounded-tr-lg" : "",
69+
agentIdx === agents.length - 2 ? "sm:rounded-bl-lg" : "",
70+
agentIdx === agents.length - 1
71+
? "rounded-bl-lg rounded-br-lg sm:rounded-bl-none"
72+
: "",
73+
"group relative bg-muted/40 p-6 focus-within:ring-2 focus-within:ring-inset focus-within:ring-ring"
74+
)}
75+
>
76+
<div>
77+
<span
78+
className={cn("inline-flex rounded-lg p-3 ring-4 ring-secondary")}
79+
>
80+
<BotIcon aria-hidden="true" className="size-6" />
81+
</span>
82+
</div>
83+
<div className="mt-8">
84+
<h3 className="text-base font-semibold">
85+
<Link
86+
to="/agent/$agentId"
87+
params={{ agentId: agent.id }}
88+
className="focus:outline-none"
89+
>
90+
{/* Extend touch target to entire panel */}
91+
<span aria-hidden="true" className="absolute inset-0" />
92+
{agent.name}
93+
</Link>
94+
</h3>
95+
{agent.description && (
96+
<div className="mt-2">
97+
<p
98+
ref={textRef}
3199
className={cn(
32-
agentIdx === 0
33-
? "rounded-tl-lg rounded-tr-lg sm:rounded-tr-none"
34-
: "",
35-
agentIdx === 1 ? "sm:rounded-tr-lg" : "",
36-
agentIdx === agents.length - 2 ? "sm:rounded-bl-lg" : "",
37-
agentIdx === agents.length - 1
38-
? "rounded-bl-lg rounded-br-lg sm:rounded-bl-none"
39-
: "",
40-
"group relative bg-muted/40 p-6 focus-within:ring-2 focus-within:ring-inset focus-within:ring-ring",
100+
"text-sm text-muted-foreground",
101+
!isExpanded && "line-clamp-2"
41102
)}
42103
>
43-
<div>
44-
<span
45-
className={cn(
46-
"inline-flex rounded-lg p-3 ring-4 ring-secondary",
47-
)}
48-
>
49-
<BotIcon aria-hidden="true" className="size-6" />
50-
</span>
51-
</div>
52-
<div className="mt-8">
53-
<h3 className="text-base font-semibold">
54-
<Link
55-
to="/agent/$agentId"
56-
params={{ agentId: agent.id }}
57-
className="focus:outline-none"
58-
>
59-
{/* Extend touch target to entire panel */}
60-
<span aria-hidden="true" className="absolute inset-0" />
61-
{agent.name}
62-
</Link>
63-
</h3>
64-
{/* may put here agent's description
65-
<p className="mt-2 text-sm text-gray-500">
66-
...
67-
</p>
68-
*/}
69-
</div>
70-
<span
71-
aria-hidden="true"
72-
className="pointer-events-none absolute right-6 top-6 text-muted-foreground/20 group-hover:text-muted-foreground/40"
104+
{agent.description}
105+
</p>
106+
{shouldTruncate && (
107+
<button
108+
onClick={(e) => {
109+
e.preventDefault();
110+
e.stopPropagation();
111+
setIsExpanded(!isExpanded);
112+
}}
113+
className="relative z-10 mt-1 text-xs text-primary hover:text-primary/80 focus:outline-none rounded pointer-events-auto"
73114
>
74-
<svg fill="currentColor" viewBox="0 0 24 24" className="size-6">
75-
<title>Open</title>
76-
<path d="M20 4h1a1 1 0 00-1-1v1zm-1 12a1 1 0 102 0h-2zM8 3a1 1 0 000 2V3zM3.293 19.293a1 1 0 101.414 1.414l-1.414-1.414zM19 4v12h2V4h-2zm1-1H8v2h12V3zm-.707.293l-16 16 1.414 1.414 16-16-1.414-1.414z" />
77-
</svg>
78-
</span>
79-
</div>
80-
))}
81-
</div>
115+
{isExpanded ? "Show less" : "Show more"}
116+
</button>
117+
)}
118+
</div>
119+
)}
82120
</div>
121+
<span
122+
aria-hidden="true"
123+
className="pointer-events-none absolute right-6 top-6 text-muted-foreground/20 group-hover:text-muted-foreground/40"
124+
>
125+
<svg fill="currentColor" viewBox="0 0 24 24" className="size-6">
126+
<title>Open</title>
127+
<path d="M20 4h1a1 1 0 00-1-1v1zm-1 12a1 1 0 102 0h-2zM8 3a1 1 0 000 2V3zM3.293 19.293a1 1 0 101.414 1.414l-1.414-1.414zM19 4v12h2V4h-2zm1-1H8v2h12V3zm-.707.293l-16 16 1.414 1.414 16-16-1.414-1.414z" />
128+
</svg>
129+
</span>
83130
</div>
84131
);
85132
}

0 commit comments

Comments
 (0)