Spaces:
Running
Running
Download src/components/CustomGptStoreModal.tsx from Aura-Studio/AI: direct link, hf CLI and curl.
- Browser
- Download file 22.6 kB
-
https://huggingface.co/spaces/Aura-Studio/AI/resolve/main/src/components/CustomGptStoreModal.tsx
- Command line
-
hf download hf://spaces/Aura-Studio/AI/src/components/CustomGptStoreModal.tsx
-
curl -L -o CustomGptStoreModal.tsx https://huggingface.co/spaces/Aura-Studio/AI/resolve/main/src/components/CustomGptStoreModal.tsx
22.6 kB
| import React, { useState } from "react"; | |
| import { | |
| Store, | |
| Sparkles, | |
| Plus, | |
| Search, | |
| Star, | |
| Users, | |
| BookOpen, | |
| Code, | |
| FileText, | |
| Image as ImageIcon, | |
| Database, | |
| Lock, | |
| Globe, | |
| ArrowRight, | |
| Check, | |
| Upload, | |
| X, | |
| Sliders, | |
| Share2, | |
| LogIn, | |
| Zap, | |
| ShieldCheck, | |
| Cpu, | |
| Layers, | |
| ExternalLink | |
| } from "lucide-react"; | |
| export interface CustomGpt { | |
| id: string; | |
| name: string; | |
| author: string; | |
| description: string; | |
| category: "coding" | "oxford" | "creative" | "productivity" | "analytics"; | |
| avatar: string; | |
| rating: number; | |
| useCount: number; | |
| systemPrompt: string; | |
| knowledgeFiles?: string[]; | |
| capabilities: { | |
| webSearch: boolean; | |
| codeExecution: boolean; | |
| artifacts: boolean; | |
| computerUse: boolean; | |
| }; | |
| customApiActions?: { | |
| endpointUrl: string; | |
| method: "GET" | "POST"; | |
| headers: string; | |
| }[]; | |
| isCustomCreated?: boolean; | |
| } | |
| interface CustomGptStoreModalProps { | |
| isOpen: boolean; | |
| onClose: () => void; | |
| currentUser: { email?: string; isGuest?: boolean } | null; | |
| onOpenAuthModal: () => void; | |
| onSelectGpt: (gpt: CustomGpt) => void; | |
| } | |
| export default function CustomGptStoreModal({ | |
| isOpen, | |
| onClose, | |
| currentUser, | |
| onOpenAuthModal, | |
| onSelectGpt | |
| }: CustomGptStoreModalProps) { | |
| const [activeTab, setActiveTab] = useState<"marketplace" | "create" | "myGpts">("marketplace"); | |
| const [selectedCategory, setSelectedCategory] = useState<string>("all"); | |
| const [searchQuery, setSearchQuery] = useState<string>(""); | |
| // New GPT Form State | |
| const [name, setName] = useState<string>(""); | |
| const [description, setDescription] = useState<string>(""); | |
| const [category, setCategory] = useState<CustomGpt["category"]>("coding"); | |
| const [avatar, setAvatar] = useState<string>("🤖"); | |
| const [systemPrompt, setSystemPrompt] = useState<string>(""); | |
| const [knowledgeFiles, setKnowledgeFiles] = useState<string[]>([]); | |
| const [webSearch, setWebSearch] = useState<boolean>(true); | |
| const [codeExecution, setCodeExecution] = useState<boolean>(true); | |
| const [artifacts, setArtifacts] = useState<boolean>(true); | |
| const [computerUse, setComputerUse] = useState<boolean>(false); | |
| const [apiEndpoint, setApiEndpoint] = useState<string>(""); | |
| const [apiMethod, setApiMethod] = useState<"GET" | "POST">("POST"); | |
| // Default Featured Custom GPT Marketplace Items | |
| const [customGpts, setCustomGpts] = useState<CustomGpt[]>([ | |
| { | |
| id: "gpt-oxford", | |
| name: "Oxford Textbook Master", | |
| author: "Oxford Education AI", | |
| description: "Expert tutor for Grade 6-8 Oxford English lessons, literature summaries, and Q&A analysis.", | |
| category: "oxford", | |
| avatar: "📚", | |
| rating: 4.9, | |
| useCount: 14200, | |
| systemPrompt: "You are an expert Oxford English literature & language tutor with full indexed knowledge of Grade 6, Grade 7, and Grade 8 textbooks.", | |
| capabilities: { webSearch: true, codeExecution: false, artifacts: true, computerUse: false } | |
| }, | |
| { | |
| id: "gpt-code-architect", | |
| name: "React & Vite Code Architect", | |
| author: "Aura Dev Labs", | |
| description: "Senior full-stack code architect creating production-ready React 19, TypeScript, and Express components with side-by-side Artifacts execution.", | |
| category: "coding", | |
| avatar: "⚡", | |
| rating: 4.95, | |
| useCount: 28900, | |
| systemPrompt: "You are a master React 19 & TypeScript software architect specializing in clean, modern UI components with Tailwind CSS.", | |
| capabilities: { webSearch: true, codeExecution: true, artifacts: true, computerUse: true } | |
| }, | |
| { | |
| id: "gpt-image-prompt", | |
| name: "FLUX & Gemini Visual Designer", | |
| author: "Aura Creative", | |
| description: "Crafts ultra-detailed image prompts, vector SVG graphic layouts, and typography palettes for AI Image Studio.", | |
| category: "creative", | |
| avatar: "🎨", | |
| rating: 4.88, | |
| useCount: 9800, | |
| systemPrompt: "You are a master creative director generating photorealistic FLUX image prompts and custom SVG vector art.", | |
| capabilities: { webSearch: false, codeExecution: false, artifacts: true, computerUse: false } | |
| }, | |
| { | |
| id: "gpt-data-analyst", | |
| name: "Spreadsheet & SQL Data Analyst", | |
| author: "Analytics Core", | |
| description: "Parses CSV, Excel, and JSON data to generate interactive charts, SQL queries, and financial trend breakdowns.", | |
| category: "analytics", | |
| avatar: "📊", | |
| rating: 4.85, | |
| useCount: 11200, | |
| systemPrompt: "You are an expert data analyst who transforms raw data files into clear visualizations, Recharts charts, and SQL queries.", | |
| capabilities: { webSearch: true, codeExecution: true, artifacts: true, computerUse: false } | |
| } | |
| ]); | |
| if (!isOpen) return null; | |
| const isSignedIn = currentUser && !currentUser.isGuest && Boolean(currentUser.email); | |
| const filteredGpts = customGpts.filter(gpt => { | |
| const matchesCategory = selectedCategory === "all" || gpt.category === selectedCategory; | |
| const matchesSearch = gpt.name.toLowerCase().includes(searchQuery.toLowerCase()) || | |
| gpt.description.toLowerCase().includes(searchQuery.toLowerCase()); | |
| return matchesCategory && matchesSearch; | |
| }); | |
| const handleCreateGpt = (e: React.FormEvent) => { | |
| e.preventDefault(); | |
| if (!isSignedIn) { | |
| onOpenAuthModal(); | |
| return; | |
| } | |
| if (!name.trim() || !systemPrompt.trim()) return; | |
| const newGpt: CustomGpt = { | |
| id: `custom-gpt-${Date.now()}`, | |
| name, | |
| author: currentUser?.email || "Signed-in Creator", | |
| description: description || "Custom user-built specialized AI micro-app.", | |
| category, | |
| avatar, | |
| rating: 5.0, | |
| useCount: 1, | |
| systemPrompt, | |
| knowledgeFiles, | |
| capabilities: { | |
| webSearch, | |
| codeExecution, | |
| artifacts, | |
| computerUse | |
| }, | |
| customApiActions: apiEndpoint ? [{ endpointUrl: apiEndpoint, method: apiMethod, headers: "Content-Type: application/json" }] : [], | |
| isCustomCreated: true | |
| }; | |
| setCustomGpts([newGpt, ...customGpts]); | |
| setActiveTab("marketplace"); | |
| onSelectGpt(newGpt); | |
| }; | |
| return ( | |
| <div className="fixed inset-0 z-50 flex items-center justify-center p-3 md:p-6 bg-slate-950/85 backdrop-blur-md animate-fade-in font-sans"> | |
| <div | |
| className="relative w-full max-w-5xl max-h-[92vh] bg-slate-900 text-slate-100 rounded-3xl shadow-2xl border border-slate-700/80 flex flex-col overflow-hidden" | |
| onClick={e => e.stopPropagation()} | |
| > | |
| {/* Header Bar */} | |
| <div className="bg-gradient-to-r from-slate-950 via-slate-900 to-indigo-950 p-5 border-b border-slate-800 shrink-0 flex items-center justify-between gap-4"> | |
| <div className="flex items-center gap-3"> | |
| <div className="h-11 w-11 rounded-2xl bg-indigo-500/20 border border-indigo-500/40 flex items-center justify-center text-indigo-400 shrink-0 shadow-inner"> | |
| <Store className="h-6 w-6" /> | |
| </div> | |
| <div> | |
| <div className="flex items-center gap-2"> | |
| <h3 className="text-lg font-black tracking-tight text-white">Custom GPT Store & Studio</h3> | |
| <span className="px-2.5 py-0.5 rounded-full bg-purple-500/20 text-purple-300 border border-purple-500/30 text-[10px] font-mono font-bold"> | |
| Micro-App Marketplace | |
| </span> | |
| </div> | |
| <p className="text-xs text-slate-400"> | |
| Package tailored system prompts, knowledge files, and custom API connections into shareable micro-apps | |
| </p> | |
| </div> | |
| </div> | |
| <button | |
| onClick={onClose} | |
| className="h-8 w-8 rounded-full bg-slate-800 hover:bg-slate-700 text-slate-300 flex items-center justify-center transition cursor-pointer shrink-0" | |
| > | |
| <X className="h-4.5 w-4.5" /> | |
| </button> | |
| </div> | |
| {/* Tab Navigation */} | |
| <div className="bg-slate-950 border-b border-slate-800 px-5 py-3 flex items-center justify-between gap-3 flex-wrap"> | |
| <div className="flex items-center gap-2 bg-slate-900 p-1 rounded-2xl border border-slate-800 text-xs font-semibold"> | |
| <button | |
| onClick={() => setActiveTab("marketplace")} | |
| className={`px-4 py-2 rounded-xl flex items-center gap-2 transition cursor-pointer ${ | |
| activeTab === "marketplace" ? "bg-indigo-600 text-white shadow-md" : "text-slate-400 hover:text-slate-200" | |
| }`} | |
| > | |
| <Store className="h-4 w-4" /> | |
| <span>Marketplace ({customGpts.length})</span> | |
| </button> | |
| <button | |
| onClick={() => { | |
| if (!isSignedIn) { | |
| onOpenAuthModal(); | |
| } else { | |
| setActiveTab("create"); | |
| } | |
| }} | |
| className={`px-4 py-2 rounded-xl flex items-center gap-2 transition cursor-pointer ${ | |
| activeTab === "create" ? "bg-indigo-600 text-white shadow-md" : "text-slate-400 hover:text-slate-200" | |
| }`} | |
| > | |
| <Plus className="h-4 w-4 text-amber-400" /> | |
| <span>Create Custom GPT</span> | |
| {!isSignedIn && <Lock className="h-3 w-3 text-amber-400 ml-1" />} | |
| </button> | |
| </div> | |
| {/* User Sign-In Badge */} | |
| <div> | |
| {isSignedIn ? ( | |
| <div className="px-3 py-1.5 rounded-xl bg-emerald-500/10 border border-emerald-500/30 text-emerald-300 text-xs font-mono font-bold flex items-center gap-1.5"> | |
| <ShieldCheck className="h-3.5 w-3.5 text-emerald-400" /> | |
| <span>Signed In ({currentUser?.email})</span> | |
| </div> | |
| ) : ( | |
| <button | |
| onClick={onOpenAuthModal} | |
| className="px-3.5 py-1.5 rounded-xl bg-amber-500/20 hover:bg-amber-500/30 text-amber-200 border border-amber-500/40 text-xs font-bold flex items-center gap-1.5 transition cursor-pointer" | |
| > | |
| <LogIn className="h-3.5 w-3.5 text-amber-400" /> | |
| <span>Sign In Required for Custom GPT Creation</span> | |
| </button> | |
| )} | |
| </div> | |
| </div> | |
| {/* TAB 1: Marketplace View */} | |
| {activeTab === "marketplace" && ( | |
| <div className="flex-1 overflow-y-auto p-5 flex flex-col gap-5 bg-slate-950/60"> | |
| {/* Search & Categories Bar */} | |
| <div className="flex flex-col sm:flex-row items-center justify-between gap-3"> | |
| <div className="relative w-full sm:w-80"> | |
| <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-slate-400" /> | |
| <input | |
| type="text" | |
| value={searchQuery} | |
| onChange={e => setSearchQuery(e.target.value)} | |
| placeholder="Search Custom GPTs by name or skill..." | |
| className="w-full pl-9 pr-4 py-2 rounded-xl bg-slate-900 border border-slate-800 text-xs text-white placeholder-slate-500 focus:outline-none focus:ring-1 focus:ring-indigo-500" | |
| /> | |
| </div> | |
| {/* Category Pills */} | |
| <div className="flex items-center gap-1.5 overflow-x-auto w-full sm:w-auto pb-1"> | |
| {[ | |
| { id: "all", label: "All GPTs" }, | |
| { id: "coding", label: "Coding & Dev" }, | |
| { id: "oxford", label: "Oxford Education" }, | |
| { id: "creative", label: "Creative & Design" }, | |
| { id: "analytics", label: "Data & Charts" } | |
| ].map(cat => ( | |
| <button | |
| key={cat.id} | |
| onClick={() => setSelectedCategory(cat.id)} | |
| className={`px-3 py-1.5 rounded-xl text-xs font-bold shrink-0 transition cursor-pointer ${ | |
| selectedCategory === cat.id | |
| ? "bg-indigo-600 text-white" | |
| : "bg-slate-900 text-slate-400 hover:text-slate-200 border border-slate-800" | |
| }`} | |
| > | |
| {cat.label} | |
| </button> | |
| ))} | |
| </div> | |
| </div> | |
| {/* Custom GPT Cards Grid */} | |
| <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| {filteredGpts.map(gpt => ( | |
| <div | |
| key={gpt.id} | |
| className="p-4 rounded-2xl bg-slate-900/90 border border-slate-800 hover:border-indigo-500/50 transition-all shadow-lg flex flex-col justify-between gap-4 group" | |
| > | |
| <div className="flex items-start gap-3"> | |
| <div className="h-12 w-12 rounded-2xl bg-gradient-to-br from-indigo-500/20 to-purple-500/20 border border-indigo-500/30 flex items-center justify-center text-2xl shrink-0 shadow-inner group-hover:scale-105 transition-transform"> | |
| {gpt.avatar} | |
| </div> | |
| <div className="min-w-0 flex-1"> | |
| <div className="flex items-center justify-between gap-2"> | |
| <h4 className="text-sm font-bold text-white truncate">{gpt.name}</h4> | |
| <span className="flex items-center gap-1 text-[11px] font-bold text-amber-400"> | |
| <Star className="h-3 w-3 fill-amber-400" /> | |
| {gpt.rating} | |
| </span> | |
| </div> | |
| <span className="text-[10px] font-mono text-indigo-400 block mb-1">By {gpt.author}</span> | |
| <p className="text-xs text-slate-300 line-clamp-2 leading-relaxed">{gpt.description}</p> | |
| </div> | |
| </div> | |
| {/* Capabilities Badges */} | |
| <div className="flex items-center justify-between pt-2 border-t border-slate-800 text-[10px] font-mono text-slate-400"> | |
| <div className="flex items-center gap-1.5 flex-wrap"> | |
| {gpt.capabilities.artifacts && <span className="px-2 py-0.5 rounded bg-indigo-500/10 text-indigo-300 border border-indigo-500/20">Artifacts</span>} | |
| {gpt.capabilities.codeExecution && <span className="px-2 py-0.5 rounded bg-emerald-500/10 text-emerald-300 border border-emerald-500/20">Code Run</span>} | |
| {gpt.capabilities.computerUse && <span className="px-2 py-0.5 rounded bg-purple-500/10 text-purple-300 border border-purple-500/20">Computer Use</span>} | |
| </div> | |
| <button | |
| onClick={() => { | |
| onSelectGpt(gpt); | |
| onClose(); | |
| }} | |
| className="px-3 py-1.5 rounded-xl bg-indigo-600 hover:bg-indigo-500 text-white font-bold text-xs flex items-center gap-1.5 shadow-md cursor-pointer transition active:scale-95 shrink-0" | |
| > | |
| <span>Load GPT</span> | |
| <ArrowRight className="h-3.5 w-3.5" /> | |
| </button> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| )} | |
| {/* TAB 2: Create Custom GPT Form */} | |
| {activeTab === "create" && ( | |
| <form onSubmit={handleCreateGpt} className="flex-1 overflow-y-auto p-5 flex flex-col gap-5 bg-slate-950/60"> | |
| {!isSignedIn && ( | |
| <div className="p-4 rounded-2xl bg-amber-500/10 border border-amber-500/30 text-amber-200 text-xs flex items-center justify-between gap-3"> | |
| <div className="flex items-center gap-2"> | |
| <Lock className="h-4 w-4 text-amber-400 shrink-0" /> | |
| <span>Sign In Required to publish Custom GPT micro-apps to the marketplace.</span> | |
| </div> | |
| <button | |
| type="button" | |
| onClick={onOpenAuthModal} | |
| className="px-3 py-1.5 rounded-xl bg-amber-600 hover:bg-amber-500 text-white font-bold text-xs shrink-0 cursor-pointer" | |
| > | |
| Sign In Now | |
| </button> | |
| </div> | |
| )} | |
| <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <div> | |
| <label className="text-xs font-bold text-slate-300 block mb-1">GPT Name</label> | |
| <input | |
| type="text" | |
| required | |
| value={name} | |
| onChange={e => setName(e.target.value)} | |
| placeholder="e.g. Oxford Grade 7 Math Tutor" | |
| className="w-full px-3.5 py-2.5 rounded-xl bg-slate-900 border border-slate-800 text-xs text-white focus:outline-none focus:border-indigo-500" | |
| /> | |
| </div> | |
| <div> | |
| <label className="text-xs font-bold text-slate-300 block mb-1">Category & Emoji Avatar</label> | |
| <div className="flex gap-2"> | |
| <select | |
| value={category} | |
| onChange={e => setCategory(e.target.value as any)} | |
| className="flex-1 px-3.5 py-2.5 rounded-xl bg-slate-900 border border-slate-800 text-xs text-white focus:outline-none focus:border-indigo-500 cursor-pointer" | |
| > | |
| <option value="coding">Coding & Dev</option> | |
| <option value="oxford">Oxford Education</option> | |
| <option value="creative">Creative & Design</option> | |
| <option value="productivity">Productivity</option> | |
| <option value="analytics">Data & Analytics</option> | |
| </select> | |
| <input | |
| type="text" | |
| value={avatar} | |
| onChange={e => setAvatar(e.target.value)} | |
| maxLength={2} | |
| className="w-14 text-center px-2 py-2.5 rounded-xl bg-slate-900 border border-slate-800 text-lg font-bold" | |
| /> | |
| </div> | |
| </div> | |
| </div> | |
| <div> | |
| <label className="text-xs font-bold text-slate-300 block mb-1">Short Description</label> | |
| <input | |
| type="text" | |
| value={description} | |
| onChange={e => setDescription(e.target.value)} | |
| placeholder="What does this specialized micro-app do?" | |
| className="w-full px-3.5 py-2.5 rounded-xl bg-slate-900 border border-slate-800 text-xs text-white focus:outline-none focus:border-indigo-500" | |
| /> | |
| </div> | |
| <div> | |
| <label className="text-xs font-bold text-slate-300 block mb-1">System Instructions & Custom Persona</label> | |
| <textarea | |
| required | |
| rows={4} | |
| value={systemPrompt} | |
| onChange={e => setSystemPrompt(e.target.value)} | |
| placeholder="Detailed system prompt guiding how the model thinks, acts, formats output, and uses tools..." | |
| className="w-full p-3.5 rounded-xl bg-slate-900 border border-slate-800 text-xs text-white focus:outline-none focus:border-indigo-500 font-mono resize-none leading-relaxed" | |
| /> | |
| </div> | |
| {/* Capability Toggles */} | |
| <div> | |
| <label className="text-xs font-bold text-slate-300 block mb-2">Enabled Capabilities</label> | |
| <div className="grid grid-cols-2 sm:grid-cols-4 gap-2"> | |
| {[ | |
| { label: "Web Search", state: webSearch, setter: setWebSearch }, | |
| { label: "Code Run", state: codeExecution, setter: setCodeExecution }, | |
| { label: "Artifacts", state: artifacts, setter: setArtifacts }, | |
| { label: "Computer Use", state: computerUse, setter: setComputerUse } | |
| ].map((cap, i) => ( | |
| <button | |
| key={i} | |
| type="button" | |
| onClick={() => cap.setter(!cap.state)} | |
| className={`p-2.5 rounded-xl border text-xs font-bold flex items-center justify-between cursor-pointer transition ${ | |
| cap.state ? "bg-indigo-600/20 border-indigo-500 text-indigo-300" : "bg-slate-900 border-slate-800 text-slate-500" | |
| }`} | |
| > | |
| <span>{cap.label}</span> | |
| {cap.state && <Check className="h-3.5 w-3.5 text-indigo-400" />} | |
| </button> | |
| ))} | |
| </div> | |
| </div> | |
| {/* Custom API Action Connection */} | |
| <div className="p-4 rounded-2xl bg-slate-900 border border-slate-800 flex flex-col gap-3"> | |
| <span className="text-xs font-bold text-white flex items-center gap-2"> | |
| <Zap className="h-4 w-4 text-amber-400" /> | |
| Custom API Action Connection (OpenAPI Spec) | |
| </span> | |
| <div className="flex gap-2"> | |
| <select | |
| value={apiMethod} | |
| onChange={e => setApiMethod(e.target.value as any)} | |
| className="px-3 py-2 rounded-xl bg-slate-950 border border-slate-800 text-xs font-mono font-bold text-white" | |
| > | |
| <option value="GET">GET</option> | |
| <option value="POST">POST</option> | |
| </select> | |
| <input | |
| type="text" | |
| value={apiEndpoint} | |
| onChange={e => setApiEndpoint(e.target.value)} | |
| placeholder="https://api.example.com/v1/action" | |
| className="flex-1 px-3 py-2 rounded-xl bg-slate-950 border border-slate-800 text-xs font-mono text-white focus:outline-none" | |
| /> | |
| </div> | |
| </div> | |
| {/* Form Submit */} | |
| <button | |
| type="submit" | |
| disabled={!isSignedIn} | |
| className="w-full py-3 px-6 rounded-2xl bg-gradient-to-r from-indigo-600 to-purple-600 hover:from-indigo-500 hover:to-purple-500 disabled:opacity-50 text-white font-bold text-xs flex items-center justify-center gap-2 shadow-xl cursor-pointer transition" | |
| > | |
| <Sparkles className="h-4 w-4 text-amber-400" /> | |
| <span>Publish Custom GPT to Marketplace</span> | |
| </button> | |
| </form> | |
| )} | |
| </div> | |
| </div> | |
| ); | |
| } | |