Skip to content

Commit 17b988a

Browse files
committed
use shadcn for deep research card
1 parent 9d5d243 commit 17b988a

File tree

4 files changed

+205
-72
lines changed

4 files changed

+205
-72
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"use client";
2+
3+
import * as AccordionPrimitive from "@radix-ui/react-accordion";
4+
import { ChevronDown } from "lucide-react";
5+
import * as React from "react";
6+
import { cn } from "./lib/utils";
7+
8+
const Accordion = AccordionPrimitive.Root;
9+
10+
const AccordionItem = React.forwardRef<
11+
React.ElementRef<typeof AccordionPrimitive.Item>,
12+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
13+
>(({ className, ...props }, ref) => (
14+
<AccordionPrimitive.Item
15+
ref={ref}
16+
className={cn("border-b", className)}
17+
{...props}
18+
/>
19+
));
20+
AccordionItem.displayName = "AccordionItem";
21+
22+
const AccordionTrigger = React.forwardRef<
23+
React.ElementRef<typeof AccordionPrimitive.Trigger>,
24+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
25+
>(({ className, children, ...props }, ref) => (
26+
<AccordionPrimitive.Header className="flex">
27+
<AccordionPrimitive.Trigger
28+
ref={ref}
29+
className={cn(
30+
"flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline text-left [&[data-state=open]>svg]:rotate-180",
31+
className,
32+
)}
33+
{...props}
34+
>
35+
{children}
36+
<ChevronDown className="h-4 w-4 shrink-0 text-neutral-500 transition-transform duration-200 dark:text-neutral-400" />
37+
</AccordionPrimitive.Trigger>
38+
</AccordionPrimitive.Header>
39+
));
40+
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
41+
42+
const AccordionContent = React.forwardRef<
43+
React.ElementRef<typeof AccordionPrimitive.Content>,
44+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
45+
>(({ className, children, ...props }, ref) => (
46+
<AccordionPrimitive.Content
47+
ref={ref}
48+
className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
49+
{...props}
50+
>
51+
<div className={cn("pb-4 pt-0", className)}>{children}</div>
52+
</AccordionPrimitive.Content>
53+
));
54+
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
55+
56+
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import * as React from "react";
2+
import { cn } from "./lib/utils";
3+
4+
const Card = React.forwardRef<
5+
HTMLDivElement,
6+
React.HTMLAttributes<HTMLDivElement>
7+
>(({ className, ...props }, ref) => (
8+
<div
9+
ref={ref}
10+
className={cn(
11+
"rounded-xl border border-neutral-200 bg-white text-neutral-950 shadow dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-50",
12+
className,
13+
)}
14+
{...props}
15+
/>
16+
));
17+
Card.displayName = "Card";
18+
19+
const CardHeader = React.forwardRef<
20+
HTMLDivElement,
21+
React.HTMLAttributes<HTMLDivElement>
22+
>(({ className, ...props }, ref) => (
23+
<div
24+
ref={ref}
25+
className={cn("flex flex-col space-y-1.5 p-6", className)}
26+
{...props}
27+
/>
28+
));
29+
CardHeader.displayName = "CardHeader";
30+
31+
const CardTitle = React.forwardRef<
32+
HTMLDivElement,
33+
React.HTMLAttributes<HTMLDivElement>
34+
>(({ className, ...props }, ref) => (
35+
<div
36+
ref={ref}
37+
className={cn("font-semibold leading-none tracking-tight", className)}
38+
{...props}
39+
/>
40+
));
41+
CardTitle.displayName = "CardTitle";
42+
43+
const CardDescription = React.forwardRef<
44+
HTMLDivElement,
45+
React.HTMLAttributes<HTMLDivElement>
46+
>(({ className, ...props }, ref) => (
47+
<div
48+
ref={ref}
49+
className={cn("text-sm text-neutral-500 dark:text-neutral-400", className)}
50+
{...props}
51+
/>
52+
));
53+
CardDescription.displayName = "CardDescription";
54+
55+
const CardContent = React.forwardRef<
56+
HTMLDivElement,
57+
React.HTMLAttributes<HTMLDivElement>
58+
>(({ className, ...props }, ref) => (
59+
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
60+
));
61+
CardContent.displayName = "CardContent";
62+
63+
const CardFooter = React.forwardRef<
64+
HTMLDivElement,
65+
React.HTMLAttributes<HTMLDivElement>
66+
>(({ className, ...props }, ref) => (
67+
<div
68+
ref={ref}
69+
className={cn("flex items-center p-6 pt-0", className)}
70+
{...props}
71+
/>
72+
));
73+
CardFooter.displayName = "CardFooter";
74+
75+
export {
76+
Card,
77+
CardContent,
78+
CardDescription,
79+
CardFooter,
80+
CardHeader,
81+
CardTitle,
82+
};

templates/types/streaming/nextjs/app/components/ui/chat/custom/deep-research-card.tsx

Lines changed: 51 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,19 @@ import { Message } from "@llamaindex/chat-ui";
44
import {
55
AlertCircle,
66
CheckCircle2,
7-
ChevronDown,
87
CircleDashed,
98
Clock,
109
NotebookPen,
1110
Search,
1211
} from "lucide-react";
1312
import { useMemo } from "react";
1413
import {
15-
Collapsible,
16-
CollapsibleContent,
17-
CollapsibleTrigger,
18-
} from "../../collapsible";
14+
Accordion,
15+
AccordionContent,
16+
AccordionItem,
17+
AccordionTrigger,
18+
} from "../../accordion";
19+
import { Card, CardContent, CardHeader, CardTitle } from "../../card";
1920
import { cn } from "../../lib/utils";
2021
import { Markdown } from "./markdown";
2122

@@ -163,63 +164,53 @@ export function DeepResearchCard({
163164
}
164165

165166
return (
166-
<div
167-
className={cn(
168-
"rounded-lg border bg-card text-card-foreground shadow-sm p-5 space-y-6 w-full",
169-
className,
170-
)}
171-
>
172-
{state.retrieve.state !== null && (
173-
<div className="border-t pt-4">
174-
<h3 className="text-lg font-semibold flex items-center gap-2">
167+
<Card className={cn("w-full", className)}>
168+
<CardHeader className="space-y-4">
169+
{state.retrieve.state !== null && (
170+
<CardTitle className="flex items-center gap-2">
175171
<Search className="h-5 w-5" />
176-
<span>
177-
{state.retrieve.state === "inprogress"
178-
? "Searching..."
179-
: "Search completed"}
180-
</span>
181-
</h3>
182-
</div>
183-
)}
184-
185-
{state.analyze.state !== null && (
186-
<div className="border-t pt-4">
187-
<h3 className="text-lg font-semibold flex items-center gap-2">
172+
{state.retrieve.state === "inprogress"
173+
? "Searching..."
174+
: "Search completed"}
175+
</CardTitle>
176+
)}
177+
{state.analyze.state !== null && (
178+
<CardTitle className="flex items-center gap-2 border-t pt-4">
188179
<NotebookPen className="h-5 w-5" />
189-
<span>
190-
{state.analyze.state === "inprogress"
191-
? "Analyzing..."
192-
: "Analysis"}
193-
</span>
194-
</h3>
195-
{state.analyze.questions.length > 0 && (
196-
<div className="space-y-2">
197-
{state.analyze.questions.map((question: QuestionState) => (
198-
<Collapsible key={question.id}>
199-
<CollapsibleTrigger className="w-full">
200-
<div className="flex items-center gap-2 p-3 hover:bg-accent transition-colors rounded-lg border">
201-
<div className="flex-shrink-0">
202-
{stateIcon[question.state]}
203-
</div>
204-
<span className="font-medium text-left flex-1">
205-
{question.question}
206-
</span>
207-
<ChevronDown className="h-5 w-5 transition-transform ui-expanded:rotate-180" />
180+
{state.analyze.state === "inprogress" ? "Analyzing..." : "Analysis"}
181+
</CardTitle>
182+
)}
183+
</CardHeader>
184+
185+
<CardContent>
186+
{state.analyze.questions.length > 0 && (
187+
<Accordion type="single" collapsible className="space-y-2">
188+
{state.analyze.questions.map((question: QuestionState) => (
189+
<AccordionItem
190+
key={question.id}
191+
value={question.id}
192+
className="border rounded-lg [&[data-state=open]>div]:rounded-b-none"
193+
>
194+
<AccordionTrigger className="hover:bg-accent hover:no-underline py-3 px-3 gap-2">
195+
<div className="flex items-center gap-2 w-full">
196+
<div className="flex-shrink-0">
197+
{stateIcon[question.state]}
208198
</div>
209-
</CollapsibleTrigger>
210-
{question.answer && (
211-
<CollapsibleContent>
212-
<div className="p-3 border border-t-0 rounded-b-lg">
213-
<Markdown content={question.answer} />
214-
</div>
215-
</CollapsibleContent>
216-
)}
217-
</Collapsible>
218-
))}
219-
</div>
220-
)}
221-
</div>
222-
)}
223-
</div>
199+
<span className="font-medium text-left flex-1">
200+
{question.question}
201+
</span>
202+
</div>
203+
</AccordionTrigger>
204+
{question.answer && (
205+
<AccordionContent className="border-t pt-3">
206+
<Markdown content={question.answer} />
207+
</AccordionContent>
208+
)}
209+
</AccordionItem>
210+
))}
211+
</Accordion>
212+
)}
213+
</CardContent>
214+
</Card>
224215
);
225216
}
Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,52 @@
11
{
2-
"name": "llama-index-nextjs-streaming",
3-
"version": "1.0.0",
2+
"name": "test-app",
3+
"version": "0.1.0",
44
"scripts": {
55
"format": "prettier --ignore-unknown --cache --check .",
66
"format:write": "prettier --ignore-unknown --write .",
77
"dev": "next dev",
88
"build": "next build",
99
"start": "next start",
10-
"lint": "next lint"
10+
"lint": "next lint",
11+
"generate": "tsx app/api/chat/engine/generate.ts"
1112
},
1213
"dependencies": {
1314
"@apidevtools/swagger-parser": "^10.1.0",
1415
"@e2b/code-interpreter": "^1.0.4",
16+
"@llamaindex/chat-ui": "0.0.14",
17+
"@radix-ui/react-accordion": "^1.2.2",
1518
"@radix-ui/react-collapsible": "^1.0.3",
1619
"@radix-ui/react-select": "^2.1.1",
1720
"@radix-ui/react-slot": "^1.0.2",
1821
"@radix-ui/react-tabs": "^1.1.0",
19-
"@llamaindex/chat-ui": "0.0.14",
2022
"ai": "^4.0.3",
2123
"ajv": "^8.12.0",
22-
"class-variance-authority": "^0.7.0",
24+
"class-variance-authority": "^0.7.1",
2325
"clsx": "^2.1.1",
2426
"dotenv": "^16.3.1",
2527
"duck-duck-scrape": "^2.2.5",
2628
"formdata-node": "^6.0.3",
2729
"got": "^14.4.1",
2830
"llamaindex": "0.8.2",
2931
"lucide-react": "^0.460.0",
32+
"marked": "^14.1.2",
3033
"next": "^15.1.3",
34+
"papaparse": "^5.4.1",
3135
"react": "^19.0.0",
3236
"react-dom": "^19.0.0",
33-
"papaparse": "^5.4.1",
3437
"supports-color": "^8.1.1",
35-
"tailwind-merge": "^2.1.0",
38+
"tailwind-merge": "^2.6.0",
39+
"tailwindcss-animate": "^1.0.7",
3640
"tiktoken": "^1.0.15",
37-
"uuid": "^9.0.1",
38-
"marked": "^14.1.2"
41+
"uuid": "^9.0.1"
3942
},
4043
"devDependencies": {
44+
"@llamaindex/workflow": "^0.0.3",
4145
"@types/node": "^20.10.3",
46+
"@types/papaparse": "^5.3.15",
4247
"@types/react": "^19.0.2",
4348
"@types/react-dom": "^19.0.2",
4449
"@types/uuid": "^9.0.8",
45-
"@llamaindex/workflow": "^0.0.3",
46-
"@types/papaparse": "^5.3.15",
4750
"autoprefixer": "^10.4.16",
4851
"cross-env": "^7.0.3",
4952
"eslint": "^9.14.0",
@@ -55,5 +58,6 @@
5558
"tailwindcss": "^3.3.6",
5659
"tsx": "^4.7.2",
5760
"typescript": "^5.3.2"
58-
}
61+
},
62+
"packageManager": "[email protected]+sha512.76e2379760a4328ec4415815bcd6628dee727af3779aaa4c914e3944156c4299921a89f976381ee107d41f12cfa4b66681ca9c718f0668fa0831ed4c6d8ba56c"
5963
}

0 commit comments

Comments
 (0)