Skip to content

Commit 501f83c

Browse files
committed
feat: add demo gallery as a prompt collection drawer
Restore and simplify the demo gallery components. Cards are now compact clickable prompts in a single-column list instead of a grid with large preview areas. Clicking a card sends its prompt to the chat.
1 parent 90adfb6 commit 501f83c

File tree

3 files changed

+328
-0
lines changed

3 files changed

+328
-0
lines changed
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: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
"use client";
2+
3+
import { useState } from "react";
4+
import { DEMO_EXAMPLES, type DemoCategory, type DemoItem } from "./demo-data";
5+
import { DemoCard } from "./demo-card";
6+
import { CategoryFilter } from "./category-filter";
7+
8+
export type { DemoItem } from "./demo-data";
9+
10+
interface DemoGalleryProps {
11+
open: boolean;
12+
onClose: () => void;
13+
onTryDemo: (demo: DemoItem) => void;
14+
}
15+
16+
export function DemoGallery({ open, onClose, onTryDemo }: DemoGalleryProps) {
17+
const [selectedCategory, setSelectedCategory] =
18+
useState<DemoCategory | null>(null);
19+
20+
const filtered = selectedCategory
21+
? DEMO_EXAMPLES.filter((d) => d.category === selectedCategory)
22+
: DEMO_EXAMPLES;
23+
24+
return (
25+
<>
26+
{/* Backdrop */}
27+
{open && (
28+
<div
29+
className="fixed inset-0 z-40"
30+
style={{ background: "rgba(0,0,0,0.3)", backdropFilter: "blur(2px)" }}
31+
onClick={onClose}
32+
/>
33+
)}
34+
35+
{/* Drawer panel */}
36+
<div
37+
className="fixed top-0 right-0 h-full z-50 flex flex-col transition-transform duration-300 ease-in-out"
38+
style={{
39+
width: 480,
40+
maxWidth: "90vw",
41+
transform: open ? "translateX(0)" : "translateX(100%)",
42+
background: "var(--surface-primary, #fff)",
43+
borderLeft: "1px solid var(--color-border-glass, rgba(0,0,0,0.1))",
44+
boxShadow: open ? "-8px 0 30px rgba(0,0,0,0.1)" : "none",
45+
}}
46+
>
47+
{/* Header */}
48+
<div
49+
className="flex items-center justify-between px-5 py-4 shrink-0"
50+
style={{
51+
borderBottom:
52+
"1px solid var(--color-border-glass, rgba(0,0,0,0.1))",
53+
}}
54+
>
55+
<div className="flex items-center gap-2">
56+
<svg
57+
width="18"
58+
height="18"
59+
viewBox="0 0 24 24"
60+
fill="none"
61+
stroke="currentColor"
62+
strokeWidth="2"
63+
strokeLinecap="round"
64+
strokeLinejoin="round"
65+
style={{ color: "var(--text-secondary, #666)" }}
66+
>
67+
<rect width="7" height="7" x="3" y="3" rx="1" />
68+
<rect width="7" height="7" x="14" y="3" rx="1" />
69+
<rect width="7" height="7" x="14" y="14" rx="1" />
70+
<rect width="7" height="7" x="3" y="14" rx="1" />
71+
</svg>
72+
<h2
73+
className="text-base font-semibold"
74+
style={{ color: "var(--text-primary, #1a1a1a)" }}
75+
>
76+
Demo Gallery
77+
</h2>
78+
<span
79+
className="text-xs font-medium px-2 py-0.5 rounded-full"
80+
style={{
81+
background: "var(--color-background-secondary, #f5f5f5)",
82+
color: "var(--text-secondary, #666)",
83+
}}
84+
>
85+
{DEMO_EXAMPLES.length}
86+
</span>
87+
</div>
88+
<button
89+
onClick={onClose}
90+
className="p-1.5 rounded-lg transition-colors duration-150 cursor-pointer"
91+
style={{ color: "var(--text-secondary, #666)" }}
92+
>
93+
<svg
94+
width="18"
95+
height="18"
96+
viewBox="0 0 24 24"
97+
fill="none"
98+
stroke="currentColor"
99+
strokeWidth="2"
100+
strokeLinecap="round"
101+
strokeLinejoin="round"
102+
>
103+
<path d="M18 6 6 18" />
104+
<path d="m6 6 12 12" />
105+
</svg>
106+
</button>
107+
</div>
108+
109+
{/* Category filter */}
110+
<div className="px-5 pt-4 pb-2 shrink-0">
111+
<CategoryFilter
112+
selected={selectedCategory}
113+
onSelect={setSelectedCategory}
114+
/>
115+
</div>
116+
117+
{/* Card list */}
118+
<div className="flex-1 overflow-y-auto px-5 pb-5">
119+
<div className="flex flex-col gap-3 pt-2">
120+
{filtered.map((demo) => (
121+
<DemoCard key={demo.id} demo={demo} onTry={onTryDemo} />
122+
))}
123+
</div>
124+
125+
{filtered.length === 0 && (
126+
<div className="flex flex-col items-center justify-center py-16 gap-2">
127+
<p
128+
className="text-sm font-medium"
129+
style={{ color: "var(--text-secondary, #666)" }}
130+
>
131+
No demos in this category
132+
</p>
133+
</div>
134+
)}
135+
</div>
136+
</div>
137+
</>
138+
);
139+
}

0 commit comments

Comments
 (0)