Skip to content

Commit 341411d

Browse files
authored
Merge pull request #68 from CopilotKit/feat/demo-gallery
feat: demo gallery prompt collection
2 parents 90adfb6 + af6cd56 commit 341411d

File tree

6 files changed

+422
-2
lines changed

6 files changed

+422
-2
lines changed

apps/app/src/app/page.tsx

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,27 @@
11
"use client";
22

3-
import { useEffect } from "react";
3+
import { useEffect, useState } from "react";
44
import { ExampleLayout } from "@/components/example-layout";
55
import { useGenerativeUIExamples, useExampleSuggestions } from "@/hooks";
66
import { ExplainerCardsPortal } from "@/components/explainer-cards";
7-
import { CopilotChat } from "@copilotkit/react-core/v2";
7+
import { DemoGallery, type DemoItem } from "@/components/demo-gallery";
8+
import { GridIcon } from "@/components/demo-gallery/grid-icon";
9+
import { CopilotChat, useAgent, useCopilotKit } from "@copilotkit/react-core/v2";
810

911
export default function HomePage() {
1012
useGenerativeUIExamples();
1113
useExampleSuggestions();
1214

15+
const [demoDrawerOpen, setDemoDrawerOpen] = useState(false);
16+
const { agent } = useAgent();
17+
const { copilotkit } = useCopilotKit();
18+
19+
const handleTryDemo = (demo: DemoItem) => {
20+
setDemoDrawerOpen(false);
21+
agent.addMessage({ id: crypto.randomUUID(), content: demo.prompt, role: "user" });
22+
copilotkit.runAgent({ agent });
23+
};
24+
1325
// Widget bridge: handle messages from widget iframes
1426
useEffect(() => {
1527
const handler = (e: MessageEvent) => {
@@ -55,6 +67,20 @@ export default function HomePage() {
5567
</p>
5668
</div>
5769
<div className="flex items-center gap-2">
70+
<button
71+
onClick={() => setDemoDrawerOpen(true)}
72+
className="inline-flex items-center gap-1.5 px-3 py-2 rounded-full text-sm font-medium no-underline whitespace-nowrap transition-all duration-150 hover:-translate-y-px cursor-pointer"
73+
style={{
74+
color: "var(--text-secondary)",
75+
border: "1px solid var(--color-border-glass, rgba(0,0,0,0.1))",
76+
background: "var(--surface-primary, rgba(255,255,255,0.6))",
77+
fontFamily: "var(--font-family)",
78+
}}
79+
title="Open Demo Gallery"
80+
>
81+
<GridIcon size={15} />
82+
Demos
83+
</button>
5884
<a
5985
href="https://github.com/CopilotKit/OpenGenerativeUI"
6086
target="_blank"
@@ -84,6 +110,11 @@ export default function HomePage() {
84110
</div>
85111
</div>
86112

113+
<DemoGallery
114+
open={demoDrawerOpen}
115+
onClose={() => setDemoDrawerOpen(false)}
116+
onTryDemo={handleTryDemo}
117+
/>
87118
</>
88119
);
89120
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"use client";
2+
3+
import type { DemoCategory } from "./demo-data";
4+
import { DEMO_CATEGORIES } from "./demo-data";
5+
6+
interface CategoryFilterProps {
7+
selected: DemoCategory | null;
8+
onSelect: (category: DemoCategory | null) => void;
9+
}
10+
11+
export function CategoryFilter({ selected, onSelect }: CategoryFilterProps) {
12+
const categories: (DemoCategory | null)[] = [null, ...DEMO_CATEGORIES];
13+
14+
return (
15+
<div className="flex gap-2 overflow-x-auto pb-1 scrollbar-none">
16+
{categories.map((cat) => {
17+
const isActive = cat === selected;
18+
return (
19+
<button
20+
key={cat ?? "all"}
21+
onClick={() => onSelect(cat)}
22+
className="shrink-0 px-3 py-1.5 rounded-full text-xs font-medium transition-all duration-150 cursor-pointer"
23+
style={{
24+
background: isActive
25+
? "linear-gradient(135deg, var(--color-lilac-dark, #6366f1), var(--color-mint-dark, #10b981))"
26+
: "var(--surface-primary, rgba(255,255,255,0.6))",
27+
color: isActive ? "#fff" : "var(--text-secondary, #666)",
28+
border: isActive
29+
? "none"
30+
: "1px solid var(--color-border-glass, rgba(0,0,0,0.1))",
31+
}}
32+
>
33+
{cat ?? "All"}
34+
</button>
35+
);
36+
})}
37+
</div>
38+
);
39+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"use client";
2+
3+
import type { DemoItem } from "./demo-data";
4+
5+
const CATEGORY_COLORS: Record<string, { bg: string; text: string }> = {
6+
"3D / Animation": { bg: "rgba(139,92,246,0.12)", text: "rgba(139,92,246,1)" },
7+
"Data Visualization": { bg: "rgba(59,130,246,0.12)", text: "rgba(59,130,246,1)" },
8+
Diagrams: { bg: "rgba(16,185,129,0.12)", text: "rgba(16,185,129,1)" },
9+
Interactive: { bg: "rgba(245,158,11,0.12)", text: "rgba(245,158,11,1)" },
10+
"UI Components": { bg: "rgba(236,72,153,0.12)", text: "rgba(236,72,153,1)" },
11+
};
12+
13+
interface DemoCardProps {
14+
demo: DemoItem;
15+
onTry: (demo: DemoItem) => void;
16+
}
17+
18+
export function DemoCard({ demo, onTry }: DemoCardProps) {
19+
const categoryColor = CATEGORY_COLORS[demo.category] ?? {
20+
bg: "rgba(100,100,100,0.12)",
21+
text: "rgba(100,100,100,1)",
22+
};
23+
24+
return (
25+
<button
26+
onClick={() => onTry(demo)}
27+
className="rounded-xl overflow-hidden flex flex-col text-left transition-all duration-200 hover:shadow-lg hover:-translate-y-0.5 cursor-pointer w-full"
28+
style={{
29+
border: "1px solid var(--color-border-glass, rgba(0,0,0,0.1))",
30+
background: "var(--surface-primary, #fff)",
31+
}}
32+
>
33+
<div className="flex flex-col gap-1.5 p-4 flex-1">
34+
<div className="flex items-center justify-between">
35+
<div className="flex items-center gap-2">
36+
<span className="text-lg">{demo.emoji}</span>
37+
<h3
38+
className="text-sm font-semibold truncate"
39+
style={{ color: "var(--text-primary, #1a1a1a)" }}
40+
>
41+
{demo.title}
42+
</h3>
43+
</div>
44+
<span
45+
className="text-[10px] font-semibold px-2 py-0.5 rounded-full shrink-0"
46+
style={{
47+
background: categoryColor.bg,
48+
color: categoryColor.text,
49+
}}
50+
>
51+
{demo.category}
52+
</span>
53+
</div>
54+
<p
55+
className="text-xs line-clamp-2"
56+
style={{ color: "var(--text-secondary, #666)" }}
57+
>
58+
{demo.description}
59+
</p>
60+
</div>
61+
</button>
62+
);
63+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
export type DemoCategory =
2+
| "3D / Animation"
3+
| "Data Visualization"
4+
| "Diagrams"
5+
| "Interactive"
6+
| "UI Components";
7+
8+
export interface DemoItem {
9+
id: string;
10+
title: string;
11+
description: string;
12+
category: DemoCategory;
13+
emoji: string;
14+
prompt: string;
15+
}
16+
17+
export const DEMO_EXAMPLES: DemoItem[] = [
18+
{
19+
id: "demo-pitch-roll-yaw",
20+
title: "Pitch, Roll & Yaw",
21+
description:
22+
"Interactive 3D airplane explaining pitch, roll, and yaw with control buttons",
23+
category: "3D / Animation",
24+
emoji: "✈️",
25+
prompt:
26+
"Create a 3D plane in Three.js to explain how pitch, roll, and yaw work. Give me buttons to control each axis. Add labels showing which rotation is which.",
27+
},
28+
{
29+
id: "demo-weather",
30+
title: "Weather Card",
31+
description:
32+
"Current weather conditions with temperature, humidity, wind, and UV index",
33+
category: "UI Components",
34+
emoji: "🌤️",
35+
prompt:
36+
"Create a beautiful weather card showing current conditions for San Francisco with temperature, humidity, wind speed, UV index, and a 5-day mini forecast.",
37+
},
38+
{
39+
id: "demo-binary-search",
40+
title: "Binary Search",
41+
description:
42+
"Step-by-step visualization of binary search on a sorted array",
43+
category: "Diagrams",
44+
emoji: "🔍",
45+
prompt:
46+
"Visualize how binary search works on a sorted list. Step by step with animation. Show the high, low, and mid pointers moving.",
47+
},
48+
{
49+
id: "demo-solar-system",
50+
title: "Solar System",
51+
description:
52+
"3D solar system with orbiting planets you can click for facts",
53+
category: "3D / Animation",
54+
emoji: "🪐",
55+
prompt:
56+
"Build a 3D solar system with orbiting planets using Three.js. Let me click on each planet to see facts about it. Include realistic relative sizes and orbital speeds.",
57+
},
58+
{
59+
id: "demo-dashboard",
60+
title: "KPI Dashboard",
61+
description:
62+
"Quarterly performance dashboard with metrics cards and bar chart",
63+
category: "Data Visualization",
64+
emoji: "📊",
65+
prompt:
66+
"Create a KPI dashboard showing Q1 2026 performance with revenue, active users, and conversion rate. Include a monthly revenue bar chart and trend indicators.",
67+
},
68+
{
69+
id: "demo-sorting",
70+
title: "Sorting Comparison",
71+
description:
72+
"Animated side-by-side comparison of bubble sort vs quicksort",
73+
category: "Diagrams",
74+
emoji: "📶",
75+
prompt:
76+
"Create an animated comparison of bubble sort vs quicksort running side by side on the same random array. Add speed controls and a step counter.",
77+
},
78+
{
79+
id: "demo-pomodoro",
80+
title: "Pomodoro Timer",
81+
description:
82+
"Focus timer with circular progress ring, session counter, and controls",
83+
category: "Interactive",
84+
emoji: "🍅",
85+
prompt:
86+
"Build a Pomodoro timer with a circular progress ring, start/pause/reset buttons, and a session counter. Use 25 min work / 5 min break intervals. Make it look clean and minimal.",
87+
},
88+
{
89+
id: "demo-neural-network",
90+
title: "Neural Network",
91+
description:
92+
"Interactive neural network diagram with animated forward pass",
93+
category: "Diagrams",
94+
emoji: "🧠",
95+
prompt:
96+
"Visualize a simple neural network with input, hidden, and output layers. Animate the forward pass showing data flowing through the network. Let me adjust the number of neurons per layer.",
97+
},
98+
{
99+
id: "demo-invoice",
100+
title: "Invoice Card",
101+
description:
102+
"Compact invoice card with amount, client info, and action buttons",
103+
category: "UI Components",
104+
emoji: "🧾",
105+
prompt:
106+
"Create an invoice card showing a monthly billing summary with client name, amount due, invoice number, and send/expand action buttons.",
107+
},
108+
{
109+
id: "demo-music-visualizer",
110+
title: "Music Equalizer",
111+
description:
112+
"Audio equalizer visualization with animated frequency bars and controls",
113+
category: "3D / Animation",
114+
emoji: "🎵",
115+
prompt:
116+
"Create a music equalizer visualization with animated bars that respond to frequency sliders. Add controls for bass, mid, and treble. Use a gradient color scheme.",
117+
},
118+
];
119+
120+
export const DEMO_CATEGORIES: DemoCategory[] = [
121+
"3D / Animation",
122+
"Data Visualization",
123+
"Diagrams",
124+
"Interactive",
125+
"UI Components",
126+
];
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import type { CSSProperties } from "react";
2+
3+
interface GridIconProps {
4+
size?: number;
5+
style?: CSSProperties;
6+
}
7+
8+
export function GridIcon({ size = 18, style }: GridIconProps) {
9+
return (
10+
<svg
11+
width={size}
12+
height={size}
13+
viewBox="0 0 24 24"
14+
fill="none"
15+
stroke="currentColor"
16+
strokeWidth="2"
17+
strokeLinecap="round"
18+
strokeLinejoin="round"
19+
style={style}
20+
>
21+
<rect width="7" height="7" x="3" y="3" rx="1" />
22+
<rect width="7" height="7" x="14" y="3" rx="1" />
23+
<rect width="7" height="7" x="14" y="14" rx="1" />
24+
<rect width="7" height="7" x="3" y="14" rx="1" />
25+
</svg>
26+
);
27+
}

0 commit comments

Comments
 (0)