From 9c54fb338699b510397bd1e19e89643c4db3f8ce Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 23 Jan 2026 23:12:28 +0000 Subject: [PATCH] feat: Add AI Code Generation v2 with system templates and snippets - Create comprehensive system templates (inventory, quests, currency, combat, friends) - Add platform-specific code patterns and snippets for Roblox/UEFN/Spatial - Build AIGenerationPanel component with template browser and parameter editor - Add configurable generation with sliders, switches, and dropdowns - Include code snippets library with copy/insert functionality - Integrate AI Generate button into Toolbar (desktop and mobile) - Support custom AI generation prompts (placeholder for future AI integration) --- src/App.tsx | 16 + src/components/Toolbar.tsx | 30 +- .../ai-generation/AIGenerationPanel.tsx | 560 ++++++ src/components/ai-generation/index.ts | 1 + src/lib/ai-generation/generation-prompts.ts | 433 +++++ src/lib/ai-generation/system-templates.ts | 1649 +++++++++++++++++ 6 files changed, 2687 insertions(+), 2 deletions(-) create mode 100644 src/components/ai-generation/AIGenerationPanel.tsx create mode 100644 src/components/ai-generation/index.ts create mode 100644 src/lib/ai-generation/generation-prompts.ts create mode 100644 src/lib/ai-generation/system-templates.ts diff --git a/src/App.tsx b/src/App.tsx index 9de25db..a80410f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -36,6 +36,7 @@ const AvatarToolkit = lazy(() => import('./components/AvatarToolkit')); const VisualScriptingCanvas = lazy(() => import('./components/visual-scripting/VisualScriptingCanvas')); const AssetLibrary = lazy(() => import('./components/assets/AssetLibrary')); const LivePreview = lazy(() => import('./components/preview/LivePreview')); +const AIGenerationPanel = lazy(() => import('./components/ai-generation/AIGenerationPanel')); function App() { const [currentCode, setCurrentCode] = useState(''); @@ -50,6 +51,7 @@ function App() { const [showVisualScripting, setShowVisualScripting] = useState(false); const [showAssetLibrary, setShowAssetLibrary] = useState(false); const [showLivePreview, setShowLivePreview] = useState(false); + const [showAIGeneration, setShowAIGeneration] = useState(false); const [code, setCode] = useState(''); const [currentPlatform, setCurrentPlatform] = useState('roblox'); const isMobile = useIsMobile(); @@ -489,6 +491,7 @@ end)`, onVisualScriptingClick={() => setShowVisualScripting(true)} onAssetLibraryClick={() => setShowAssetLibrary(true)} onLivePreviewClick={() => setShowLivePreview(true)} + onAIGenerationClick={() => setShowAIGeneration(true)} /> @@ -654,6 +657,19 @@ end)`, )} + + {showAIGeneration && ( + setShowAIGeneration(false)} + currentPlatform={currentPlatform} + onCodeGenerated={(code) => { + setCurrentCode(code); + handleCodeChange(code); + }} + /> + )} + diff --git a/src/components/Toolbar.tsx b/src/components/Toolbar.tsx index d16c8a7..bdc9bcc 100644 --- a/src/components/Toolbar.tsx +++ b/src/components/Toolbar.tsx @@ -7,7 +7,7 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; -import { Copy, FileCode, Download, Info, Play, FolderPlus, User, SignOut, List, ArrowsLeftRight, UserCircle, GitBranch, Package, Cube } from '@phosphor-icons/react'; +import { Copy, FileCode, Download, Info, Play, FolderPlus, User, SignOut, List, ArrowsLeftRight, UserCircle, GitBranch, Package, Cube, MagicWand } from '@phosphor-icons/react'; import { toast } from 'sonner'; import { useState, useEffect, useCallback, memo } from 'react'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from './ui/dialog'; @@ -28,9 +28,10 @@ interface ToolbarProps { onVisualScriptingClick?: () => void; onAssetLibraryClick?: () => void; onLivePreviewClick?: () => void; + onAIGenerationClick?: () => void; } -export function Toolbar({ code, onTemplatesClick, onPreviewClick, onNewProjectClick, currentPlatform, onPlatformChange, onTranslateClick, onAvatarToolkitClick, onVisualScriptingClick, onAssetLibraryClick, onLivePreviewClick }: ToolbarProps) { +export function Toolbar({ code, onTemplatesClick, onPreviewClick, onNewProjectClick, currentPlatform, onPlatformChange, onTranslateClick, onAvatarToolkitClick, onVisualScriptingClick, onAssetLibraryClick, onLivePreviewClick, onAIGenerationClick }: ToolbarProps) { const [showInfo, setShowInfo] = useState(false); const [user, setUser] = useState<{ login: string; avatarUrl: string; email: string } | null>(null); @@ -178,6 +179,25 @@ export function Toolbar({ code, onTemplatesClick, onPreviewClick, onNewProjectCl )} + {/* AI Generation Button */} + {onAIGenerationClick && ( + + + + + AI-Powered Code & System Generation + + )} +
@@ -316,6 +336,12 @@ export function Toolbar({ code, onTemplatesClick, onPreviewClick, onNewProjectCl 3D Preview )} + {onAIGenerationClick && ( + + + AI Generate + + )} Copy Code diff --git a/src/components/ai-generation/AIGenerationPanel.tsx b/src/components/ai-generation/AIGenerationPanel.tsx new file mode 100644 index 0000000..b9755fb --- /dev/null +++ b/src/components/ai-generation/AIGenerationPanel.tsx @@ -0,0 +1,560 @@ +'use client'; + +import { useState, useMemo } from 'react'; +import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { Textarea } from '@/components/ui/textarea'; +import { Badge } from '@/components/ui/badge'; +import { ScrollArea } from '@/components/ui/scroll-area'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { Switch } from '@/components/ui/switch'; +import { Slider } from '@/components/ui/slider'; +import { Separator } from '@/components/ui/separator'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from '@/components/ui/accordion'; +import { + Wand2, + Copy, + Check, + Sparkles, + Code, + Search, + Layers, + Gamepad2, + Coins, + Swords, + Users, + Layout, + Globe, + Network, + CreditCard, + ChevronRight, + FileCode, + Zap, +} from 'lucide-react'; +import { toast } from 'sonner'; + +import { + SYSTEM_TEMPLATES, + SYSTEM_CATEGORIES, + SystemCategory, + SystemTemplate, + TemplateParameter, + generateCode, + getTemplatesByCategory, + getTemplatesForPlatform, +} from '@/lib/ai-generation/system-templates'; +import { + CODE_SNIPPETS, + getSnippetsForPlatform, + getPatternsForPlatform, +} from '@/lib/ai-generation/generation-prompts'; +import { PlatformId } from '@/lib/platforms'; + +// Category icons +const CATEGORY_ICONS: Record = { + gameplay: , + economy: , + combat: , + social: , + ui: , + world: , + multiplayer: , + monetization: , +}; + +interface AIGenerationPanelProps { + isOpen: boolean; + onClose: () => void; + currentPlatform: PlatformId; + onCodeGenerated: (code: string) => void; +} + +export default function AIGenerationPanel({ + isOpen, + onClose, + currentPlatform, + onCodeGenerated, +}: AIGenerationPanelProps) { + const [activeTab, setActiveTab] = useState<'templates' | 'snippets' | 'custom'>('templates'); + const [selectedCategory, setSelectedCategory] = useState(null); + const [selectedTemplate, setSelectedTemplate] = useState(null); + const [parameters, setParameters] = useState>({}); + const [generatedCode, setGeneratedCode] = useState(''); + const [copied, setCopied] = useState(false); + const [searchQuery, setSearchQuery] = useState(''); + const [customPrompt, setCustomPrompt] = useState(''); + const [isGenerating, setIsGenerating] = useState(false); + + // Map PlatformId to template platform + const templatePlatform = useMemo(() => { + switch (currentPlatform) { + case 'roblox': + return 'roblox'; + case 'uefn': + return 'uefn'; + case 'spatial': + return 'spatial'; + default: + return 'roblox'; + } + }, [currentPlatform]); + + // Filter templates by platform and search + const filteredTemplates = useMemo(() => { + let templates = getTemplatesForPlatform(templatePlatform); + + if (selectedCategory) { + templates = templates.filter((t) => t.category === selectedCategory); + } + + if (searchQuery) { + const query = searchQuery.toLowerCase(); + templates = templates.filter( + (t) => + t.name.toLowerCase().includes(query) || + t.description.toLowerCase().includes(query) + ); + } + + return templates; + }, [templatePlatform, selectedCategory, searchQuery]); + + // Get snippets for current platform + const snippets = useMemo(() => { + return getSnippetsForPlatform(templatePlatform); + }, [templatePlatform]); + + // Initialize parameters when template is selected + const handleTemplateSelect = (template: SystemTemplate) => { + setSelectedTemplate(template); + const initialParams: Record = {}; + template.parameters.forEach((param) => { + initialParams[param.id] = param.default; + }); + setParameters(initialParams); + setGeneratedCode(''); + }; + + // Generate code from template + const handleGenerate = () => { + if (!selectedTemplate) return; + + const code = generateCode(selectedTemplate, templatePlatform, parameters); + setGeneratedCode(code); + toast.success('Code generated!'); + }; + + // Copy code to clipboard + const handleCopy = async () => { + await navigator.clipboard.writeText(generatedCode); + setCopied(true); + toast.success('Copied to clipboard!'); + setTimeout(() => setCopied(false), 2000); + }; + + // Insert code into editor + const handleInsert = () => { + onCodeGenerated(generatedCode); + toast.success('Code inserted into editor!'); + onClose(); + }; + + // Copy snippet + const handleCopySnippet = async (code: string) => { + await navigator.clipboard.writeText(code); + toast.success('Snippet copied!'); + }; + + // Insert snippet + const handleInsertSnippet = (code: string) => { + onCodeGenerated(code); + toast.success('Snippet inserted!'); + onClose(); + }; + + // Render parameter input + const renderParameterInput = (param: TemplateParameter) => { + const value = parameters[param.id]; + + switch (param.type) { + case 'number': + return ( +
+
+ + {value} +
+ + setParameters((p) => ({ ...p, [param.id]: v })) + } + min={param.validation?.min || 0} + max={param.validation?.max || 100} + step={1} + className="w-full" + /> +

{param.description}

+
+ ); + + case 'boolean': + return ( +
+
+ +

{param.description}

+
+ + setParameters((p) => ({ ...p, [param.id]: checked })) + } + /> +
+ ); + + case 'select': + return ( +
+ + +

{param.description}

+
+ ); + + case 'array': + return ( +
+ + + setParameters((p) => ({ + ...p, + [param.id]: e.target.value.split(',').map((s) => s.trim()), + })) + } + className="h-8 text-xs" + placeholder="item1, item2, item3" + /> +

{param.description}

+
+ ); + + default: + return ( +
+ + + setParameters((p) => ({ ...p, [param.id]: e.target.value })) + } + className="h-8 text-xs" + /> +

{param.description}

+
+ ); + } + }; + + return ( + + + + + + AI Code Generator + + {templatePlatform.toUpperCase()} + + + + +
+ {/* Left Panel - Categories/Templates */} +
+ setActiveTab(v as any)} className="flex-1 flex flex-col"> + + + + Systems + + + + Snippets + + + + Custom + + + +
+
+ + setSearchQuery(e.target.value)} + className="pl-8 h-8 text-xs" + /> +
+
+ + + + {/* Category filters */} +
+ setSelectedCategory(null)} + > + All + + {(Object.keys(SYSTEM_CATEGORIES) as SystemCategory[]).map((cat) => ( + setSelectedCategory(cat)} + > + {CATEGORY_ICONS[cat]} + {SYSTEM_CATEGORIES[cat].label.split(' ')[0]} + + ))} +
+ + {/* Template list */} +
+ {filteredTemplates.map((template) => ( +
handleTemplateSelect(template)} + > +
+ {template.name} + + {template.complexity} + +
+

+ {template.description} +

+
+ ~{template.estimatedLines} lines + + {template.features.length} features +
+
+ ))} +
+
+ + + + {Object.entries(snippets).map(([name, code]) => ( + + +
+ + {name.replace(/-/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase())} +
+
+ +
+
+                              {code.slice(0, 200)}...
+                            
+
+ + +
+
+
+
+ ))} +
+
+ + +
+ +