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)
This commit is contained in:
Claude 2026-01-23 23:12:28 +00:00
parent 159e40f02c
commit 9c54fb3386
No known key found for this signature in database
6 changed files with 2687 additions and 2 deletions

View file

@ -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<PlatformId>('roblox');
const isMobile = useIsMobile();
@ -489,6 +491,7 @@ end)`,
onVisualScriptingClick={() => setShowVisualScripting(true)}
onAssetLibraryClick={() => setShowAssetLibrary(true)}
onLivePreviewClick={() => setShowLivePreview(true)}
onAIGenerationClick={() => setShowAIGeneration(true)}
/>
</div>
@ -654,6 +657,19 @@ end)`,
</Dialog>
)}
</Suspense>
<Suspense fallback={null}>
{showAIGeneration && (
<AIGenerationPanel
isOpen={showAIGeneration}
onClose={() => setShowAIGeneration(false)}
currentPlatform={currentPlatform}
onCodeGenerated={(code) => {
setCurrentCode(code);
handleCodeChange(code);
}}
/>
)}
</Suspense>
<Suspense fallback={null}>
<WelcomeDialog />
</Suspense>

View file

@ -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
</Tooltip>
)}
{/* AI Generation Button */}
{onAIGenerationClick && (
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="outline"
size="sm"
onClick={onAIGenerationClick}
className="h-8 px-3 text-xs gap-1 bg-accent/10 border-accent/30 hover:bg-accent/20"
aria-label="AI Code Generator"
>
<MagicWand size={14} />
<span>AI Generate</span>
</Button>
</TooltipTrigger>
<TooltipContent>AI-Powered Code & System Generation</TooltipContent>
</Tooltip>
)}
<div className="h-6 w-px bg-border mx-1" />
<Tooltip>
@ -316,6 +336,12 @@ export function Toolbar({ code, onTemplatesClick, onPreviewClick, onNewProjectCl
<span>3D Preview</span>
</DropdownMenuItem>
)}
{onAIGenerationClick && (
<DropdownMenuItem onClick={onAIGenerationClick}>
<MagicWand className="mr-2" size={16} />
<span>AI Generate</span>
</DropdownMenuItem>
)}
<DropdownMenuItem onClick={handleCopy}>
<Copy className="mr-2" size={16} />
<span>Copy Code</span>

View file

@ -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<SystemCategory, React.ReactNode> = {
gameplay: <Gamepad2 className="h-4 w-4" />,
economy: <Coins className="h-4 w-4" />,
combat: <Swords className="h-4 w-4" />,
social: <Users className="h-4 w-4" />,
ui: <Layout className="h-4 w-4" />,
world: <Globe className="h-4 w-4" />,
multiplayer: <Network className="h-4 w-4" />,
monetization: <CreditCard className="h-4 w-4" />,
};
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<SystemCategory | null>(null);
const [selectedTemplate, setSelectedTemplate] = useState<SystemTemplate | null>(null);
const [parameters, setParameters] = useState<Record<string, any>>({});
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<string, any> = {};
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 (
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label className="text-xs">{param.name}</Label>
<span className="text-xs text-muted-foreground">{value}</span>
</div>
<Slider
value={[value]}
onValueChange={([v]) =>
setParameters((p) => ({ ...p, [param.id]: v }))
}
min={param.validation?.min || 0}
max={param.validation?.max || 100}
step={1}
className="w-full"
/>
<p className="text-[10px] text-muted-foreground">{param.description}</p>
</div>
);
case 'boolean':
return (
<div className="flex items-center justify-between">
<div>
<Label className="text-xs">{param.name}</Label>
<p className="text-[10px] text-muted-foreground">{param.description}</p>
</div>
<Switch
checked={value}
onCheckedChange={(checked) =>
setParameters((p) => ({ ...p, [param.id]: checked }))
}
/>
</div>
);
case 'select':
return (
<div className="space-y-1">
<Label className="text-xs">{param.name}</Label>
<Select
value={value}
onValueChange={(v) => setParameters((p) => ({ ...p, [param.id]: v }))}
>
<SelectTrigger className="h-8 text-xs">
<SelectValue />
</SelectTrigger>
<SelectContent>
{param.options?.map((opt) => (
<SelectItem key={opt.value} value={opt.value}>
{opt.label}
</SelectItem>
))}
</SelectContent>
</Select>
<p className="text-[10px] text-muted-foreground">{param.description}</p>
</div>
);
case 'array':
return (
<div className="space-y-1">
<Label className="text-xs">{param.name}</Label>
<Input
value={Array.isArray(value) ? value.join(', ') : value}
onChange={(e) =>
setParameters((p) => ({
...p,
[param.id]: e.target.value.split(',').map((s) => s.trim()),
}))
}
className="h-8 text-xs"
placeholder="item1, item2, item3"
/>
<p className="text-[10px] text-muted-foreground">{param.description}</p>
</div>
);
default:
return (
<div className="space-y-1">
<Label className="text-xs">{param.name}</Label>
<Input
value={value}
onChange={(e) =>
setParameters((p) => ({ ...p, [param.id]: e.target.value }))
}
className="h-8 text-xs"
/>
<p className="text-[10px] text-muted-foreground">{param.description}</p>
</div>
);
}
};
return (
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent className="max-w-5xl max-h-[85vh] p-0 overflow-hidden">
<DialogHeader className="px-6 py-4 border-b">
<DialogTitle className="flex items-center gap-2">
<Sparkles className="h-5 w-5 text-primary" />
AI Code Generator
<Badge variant="secondary" className="ml-2 text-xs">
{templatePlatform.toUpperCase()}
</Badge>
</DialogTitle>
</DialogHeader>
<div className="flex h-[70vh]">
{/* Left Panel - Categories/Templates */}
<div className="w-72 border-r flex flex-col">
<Tabs value={activeTab} onValueChange={(v) => setActiveTab(v as any)} className="flex-1 flex flex-col">
<TabsList className="grid grid-cols-3 mx-3 mt-3">
<TabsTrigger value="templates" className="text-xs">
<Layers className="h-3 w-3 mr-1" />
Systems
</TabsTrigger>
<TabsTrigger value="snippets" className="text-xs">
<Code className="h-3 w-3 mr-1" />
Snippets
</TabsTrigger>
<TabsTrigger value="custom" className="text-xs">
<Wand2 className="h-3 w-3 mr-1" />
Custom
</TabsTrigger>
</TabsList>
<div className="px-3 py-2">
<div className="relative">
<Search className="absolute left-2.5 top-2.5 h-3 w-3 text-muted-foreground" />
<Input
placeholder="Search..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-8 h-8 text-xs"
/>
</div>
</div>
<ScrollArea className="flex-1">
<TabsContent value="templates" className="mt-0 p-2">
{/* Category filters */}
<div className="flex flex-wrap gap-1 mb-3">
<Badge
variant={selectedCategory === null ? 'default' : 'outline'}
className="cursor-pointer text-[10px]"
onClick={() => setSelectedCategory(null)}
>
All
</Badge>
{(Object.keys(SYSTEM_CATEGORIES) as SystemCategory[]).map((cat) => (
<Badge
key={cat}
variant={selectedCategory === cat ? 'default' : 'outline'}
className="cursor-pointer text-[10px]"
onClick={() => setSelectedCategory(cat)}
>
{CATEGORY_ICONS[cat]}
<span className="ml-1">{SYSTEM_CATEGORIES[cat].label.split(' ')[0]}</span>
</Badge>
))}
</div>
{/* Template list */}
<div className="space-y-1">
{filteredTemplates.map((template) => (
<div
key={template.id}
className={`p-2 rounded-md border cursor-pointer transition-colors ${
selectedTemplate?.id === template.id
? 'bg-primary/10 border-primary'
: 'hover:bg-accent'
}`}
onClick={() => handleTemplateSelect(template)}
>
<div className="flex items-center justify-between">
<span className="font-medium text-sm">{template.name}</span>
<Badge
variant="secondary"
className="text-[10px]"
style={{
backgroundColor: `${SYSTEM_CATEGORIES[template.category].color}20`,
color: SYSTEM_CATEGORIES[template.category].color,
}}
>
{template.complexity}
</Badge>
</div>
<p className="text-xs text-muted-foreground mt-1 line-clamp-2">
{template.description}
</p>
<div className="flex items-center gap-2 mt-2 text-[10px] text-muted-foreground">
<span>~{template.estimatedLines} lines</span>
<span></span>
<span>{template.features.length} features</span>
</div>
</div>
))}
</div>
</TabsContent>
<TabsContent value="snippets" className="mt-0 p-2">
<Accordion type="multiple" className="w-full">
{Object.entries(snippets).map(([name, code]) => (
<AccordionItem key={name} value={name}>
<AccordionTrigger className="text-sm py-2">
<div className="flex items-center gap-2">
<FileCode className="h-4 w-4" />
{name.replace(/-/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase())}
</div>
</AccordionTrigger>
<AccordionContent>
<div className="space-y-2">
<pre className="text-[10px] bg-muted p-2 rounded overflow-x-auto max-h-32">
{code.slice(0, 200)}...
</pre>
<div className="flex gap-1">
<Button
variant="outline"
size="sm"
className="h-7 text-xs flex-1"
onClick={() => handleCopySnippet(code)}
>
<Copy className="h-3 w-3 mr-1" />
Copy
</Button>
<Button
variant="default"
size="sm"
className="h-7 text-xs flex-1"
onClick={() => handleInsertSnippet(code)}
>
<Zap className="h-3 w-3 mr-1" />
Insert
</Button>
</div>
</div>
</AccordionContent>
</AccordionItem>
))}
</Accordion>
</TabsContent>
<TabsContent value="custom" className="mt-0 p-3">
<div className="space-y-3">
<Label className="text-xs">Describe what you want to create:</Label>
<Textarea
placeholder="E.g., Create a system where players can collect coins that spawn randomly around the map. Include a leaderboard that shows top collectors."
value={customPrompt}
onChange={(e) => setCustomPrompt(e.target.value)}
className="min-h-[150px] text-xs"
/>
<Button
className="w-full"
disabled={!customPrompt.trim() || isGenerating}
onClick={() => {
// For now, just show a message
toast.info('Custom AI generation coming soon! Use system templates for now.');
}}
>
<Wand2 className="h-4 w-4 mr-2" />
Generate with AI
</Button>
<p className="text-[10px] text-muted-foreground text-center">
Tip: Be specific about mechanics, numbers, and behaviors you want.
</p>
</div>
</TabsContent>
</ScrollArea>
</Tabs>
</div>
{/* Right Panel - Configuration/Preview */}
<div className="flex-1 flex flex-col">
{selectedTemplate ? (
<>
{/* Template Info */}
<div className="p-4 border-b">
<div className="flex items-center justify-between">
<div>
<h3 className="font-semibold">{selectedTemplate.name}</h3>
<p className="text-sm text-muted-foreground">
{selectedTemplate.description}
</p>
</div>
<Button onClick={handleGenerate}>
<Sparkles className="h-4 w-4 mr-2" />
Generate
</Button>
</div>
<div className="flex flex-wrap gap-1 mt-3">
{selectedTemplate.features.map((feature) => (
<Badge key={feature} variant="outline" className="text-[10px]">
{feature}
</Badge>
))}
</div>
</div>
{/* Parameters */}
{selectedTemplate.parameters.length > 0 && (
<div className="p-4 border-b">
<h4 className="font-medium text-sm mb-3">Configuration</h4>
<div className="grid grid-cols-2 gap-4">
{selectedTemplate.parameters.map((param) => (
<div key={param.id}>{renderParameterInput(param)}</div>
))}
</div>
</div>
)}
{/* Generated Code */}
<div className="flex-1 flex flex-col min-h-0">
<div className="flex items-center justify-between px-4 py-2 border-b">
<span className="font-medium text-sm">Generated Code</span>
{generatedCode && (
<div className="flex gap-1">
<Button variant="outline" size="sm" onClick={handleCopy}>
{copied ? (
<Check className="h-3 w-3 mr-1" />
) : (
<Copy className="h-3 w-3 mr-1" />
)}
{copied ? 'Copied!' : 'Copy'}
</Button>
<Button size="sm" onClick={handleInsert}>
<ChevronRight className="h-3 w-3 mr-1" />
Insert
</Button>
</div>
)}
</div>
<ScrollArea className="flex-1">
{generatedCode ? (
<pre className="p-4 text-xs font-mono whitespace-pre-wrap">
{generatedCode}
</pre>
) : (
<div className="flex items-center justify-center h-full text-muted-foreground">
<div className="text-center">
<Sparkles className="h-8 w-8 mx-auto mb-2 opacity-50" />
<p className="text-sm">Configure parameters and click Generate</p>
</div>
</div>
)}
</ScrollArea>
</div>
</>
) : (
<div className="flex items-center justify-center h-full text-muted-foreground">
<div className="text-center">
<Layers className="h-12 w-12 mx-auto mb-4 opacity-50" />
<h3 className="font-medium text-lg mb-2">Select a System Template</h3>
<p className="text-sm max-w-xs">
Choose from pre-built game systems or create custom code with AI assistance.
</p>
</div>
</div>
)}
</div>
</div>
</DialogContent>
</Dialog>
);
}

View file

@ -0,0 +1 @@
export { default as AIGenerationPanel } from './AIGenerationPanel';

View file

@ -0,0 +1,433 @@
/**
* AI Code Generation v2 - Generation Prompts
* Prompts and utilities for AI-assisted code generation
*/
export interface GenerationPrompt {
id: string;
name: string;
description: string;
category: string;
template: string;
examples: string[];
}
export interface GenerationResult {
code: string;
explanation: string;
warnings: string[];
suggestions: string[];
}
// Platform-specific code patterns
export const PLATFORM_PATTERNS = {
roblox: {
services: [
'Players',
'ReplicatedStorage',
'ServerStorage',
'DataStoreService',
'TweenService',
'RunService',
'UserInputService',
'Workspace',
'Lighting',
'SoundService',
'MarketplaceService',
'TeleportService',
],
commonPatterns: {
playerJoin: `Players.PlayerAdded:Connect(function(player)
-- Code here
end)`,
dataStore: `local DataStore = DataStoreService:GetDataStore("StoreName")
local success, data = pcall(function()
return DataStore:GetAsync(key)
end)`,
tween: `local tweenInfo = TweenInfo.new(duration, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
local tween = TweenService:Create(object, tweenInfo, {Property = value})
tween:Play()`,
remoteEvent: `local RemoteEvent = ReplicatedStorage:WaitForChild("EventName")
-- Server:
RemoteEvent.OnServerEvent:Connect(function(player, ...)
end)
-- Client:
RemoteEvent:FireServer(...)`,
},
},
uefn: {
modules: [
'/Fortnite.com/Devices',
'/Fortnite.com/Characters',
'/Fortnite.com/Game',
'/Verse.org/Simulation',
'/Verse.org/Random',
],
commonPatterns: {
device: `device_name := class(creative_device):
OnBegin<override>()<suspends> : void =
# Code here`,
subscription: `if (Agent := agent[Player]):
Agent.JumpedEvent.Subscribe(OnPlayerJumped)`,
async: `spawn:
# Async code here
Sleep(1.0)`,
},
},
spatial: {
modules: ['spatial', 'three', 'physics', 'networking'],
commonPatterns: {
component: `export class MyComponent extends Component {
onStart(): void {
// Code here
}
}`,
networked: `@networked
class NetworkedState {
@networked health: number = 100;
}`,
},
},
};
// Generation prompt templates
export const GENERATION_PROMPTS: GenerationPrompt[] = [
{
id: 'game-mechanic',
name: 'Game Mechanic',
description: 'Generate a custom game mechanic',
category: 'gameplay',
template: `Generate a {{platform}} script for the following game mechanic:
**Mechanic Description:**
{{description}}
**Requirements:**
- Clean, well-commented code
- Error handling where appropriate
- Modular design for easy modification
- Performance optimized
{{additionalRequirements}}`,
examples: [
'Double jump ability with cooldown',
'Grappling hook that pulls player to surfaces',
'Time-slowing ability when in danger',
],
},
{
id: 'npc-behavior',
name: 'NPC Behavior',
description: 'Generate NPC AI behavior',
category: 'ai',
template: `Generate a {{platform}} NPC behavior script:
**NPC Type:** {{npcType}}
**Behavior:** {{behavior}}
Requirements:
- State machine pattern
- Patrol, chase, and return states
- Detection range: {{detectionRange}} studs
- Attack range: {{attackRange}} studs
{{additionalRequirements}}`,
examples: [
'Guard that patrols and chases intruders',
'Shopkeeper that interacts with players',
'Boss with multiple attack phases',
],
},
{
id: 'ui-system',
name: 'UI System',
description: 'Generate UI components and systems',
category: 'ui',
template: `Generate a {{platform}} UI system:
**UI Type:** {{uiType}}
**Features:** {{features}}
Requirements:
- Responsive design
- Smooth animations
- Accessibility considerations
- Mobile support
{{additionalRequirements}}`,
examples: ['Health bar with animations', 'Inventory grid UI', 'Dialog system with choices'],
},
{
id: 'multiplayer-feature',
name: 'Multiplayer Feature',
description: 'Generate multiplayer/networked features',
category: 'multiplayer',
template: `Generate a {{platform}} multiplayer feature:
**Feature:** {{feature}}
**Players:** {{playerCount}}
Requirements:
- Server-side validation
- Client prediction where needed
- Efficient networking
- Cheat prevention
{{additionalRequirements}}`,
examples: ['Team-based capture points', 'Trading system between players', 'Synchronized events'],
},
];
// Helper function to fill prompt template
export function fillPromptTemplate(
promptId: string,
variables: Record<string, string>
): string {
const prompt = GENERATION_PROMPTS.find((p) => p.id === promptId);
if (!prompt) return '';
let filled = prompt.template;
for (const [key, value] of Object.entries(variables)) {
filled = filled.split(`{{${key}}}`).join(value);
}
return filled;
}
// Code snippet library for common patterns
export const CODE_SNIPPETS = {
roblox: {
'player-data-template': `-- Player Data Management
local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local DataStore = DataStoreService:GetDataStore("PlayerData")
local PlayerData = {}
local DEFAULT_DATA = {
coins = 0,
level = 1,
inventory = {}
}
local function LoadData(player)
local success, data = pcall(function()
return DataStore:GetAsync("Player_" .. player.UserId)
end)
if success and data then
PlayerData[player] = data
else
PlayerData[player] = table.clone(DEFAULT_DATA)
end
end
local function SaveData(player)
if not PlayerData[player] then return end
pcall(function()
DataStore:SetAsync("Player_" .. player.UserId, PlayerData[player])
end)
end
Players.PlayerAdded:Connect(LoadData)
Players.PlayerRemoving:Connect(function(player)
SaveData(player)
PlayerData[player] = nil
end)
game:BindToClose(function()
for _, player in ipairs(Players:GetPlayers()) do
SaveData(player)
end
end)`,
'tween-utility': `-- Tween Utility Module
local TweenService = game:GetService("TweenService")
local TweenUtility = {}
function TweenUtility.Tween(object, properties, duration, easingStyle, easingDirection)
easingStyle = easingStyle or Enum.EasingStyle.Quad
easingDirection = easingDirection or Enum.EasingDirection.Out
local tweenInfo = TweenInfo.new(duration, easingStyle, easingDirection)
local tween = TweenService:Create(object, tweenInfo, properties)
tween:Play()
return tween
end
function TweenUtility.FadeIn(guiObject, duration)
guiObject.Visible = true
return TweenUtility.Tween(guiObject, {BackgroundTransparency = 0}, duration or 0.3)
end
function TweenUtility.FadeOut(guiObject, duration)
local tween = TweenUtility.Tween(guiObject, {BackgroundTransparency = 1}, duration or 0.3)
tween.Completed:Connect(function()
guiObject.Visible = false
end)
return tween
end
return TweenUtility`,
'signal-class': `-- Signal/Event Class
local Signal = {}
Signal.__index = Signal
function Signal.new()
return setmetatable({
_connections = {}
}, Signal)
end
function Signal:Connect(callback)
local connection = {
_callback = callback,
_signal = self
}
function connection:Disconnect()
for i, conn in ipairs(self._signal._connections) do
if conn == self then
table.remove(self._signal._connections, i)
break
end
end
end
table.insert(self._connections, connection)
return connection
end
function Signal:Fire(...)
for _, connection in ipairs(self._connections) do
task.spawn(connection._callback, ...)
end
end
function Signal:Wait()
local thread = coroutine.running()
local connection
connection = self:Connect(function(...)
connection:Disconnect()
task.spawn(thread, ...)
end)
return coroutine.yield()
end
return Signal`,
},
uefn: {
'device-template': `# Basic Device Template
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
device_name := class(creative_device):
# Device references
@editable TriggerDevice : trigger_device = trigger_device{}
# Variables
var IsActive : logic = false
OnBegin<override>()<suspends> : void =
TriggerDevice.TriggeredEvent.Subscribe(OnTriggered)
Print("Device initialized")
OnTriggered(Agent : ?agent) : void =
if (Player := player[Agent?]):
set IsActive = not IsActive
Print("Triggered by player")`,
'player-counter': `# Player Counter
using { /Fortnite.com/Devices }
using { /Fortnite.com/Game }
using { /Verse.org/Simulation }
player_counter := class(creative_device):
var PlayerCount : int = 0
OnBegin<override>()<suspends> : void =
GetPlayspace().PlayerAddedEvent().Subscribe(OnPlayerAdded)
GetPlayspace().PlayerRemovedEvent().Subscribe(OnPlayerRemoved)
OnPlayerAdded(Player : player) : void =
set PlayerCount += 1
Print("Players: {PlayerCount}")
OnPlayerRemoved(Player : player) : void =
set PlayerCount -= 1
Print("Players: {PlayerCount}")`,
},
spatial: {
'component-template': `// Basic Component Template
import { Component, NetworkedComponent } from 'spatial';
export class MyComponent extends Component {
// Properties
private health: number = 100;
private isActive: boolean = true;
onStart(): void {
console.log('Component started');
this.setupListeners();
}
onUpdate(deltaTime: number): void {
if (!this.isActive) return;
// Update logic here
}
onDestroy(): void {
console.log('Component destroyed');
}
private setupListeners(): void {
// Setup event listeners
}
}`,
'networked-state': `// Networked State Management
import { NetworkedComponent, networked, rpc, RpcMode } from 'spatial';
export class GameState extends NetworkedComponent {
@networked
private score: number = 0;
@networked
private gamePhase: string = 'waiting';
@rpc(RpcMode.Server)
addScore(amount: number): void {
this.score += amount;
this.onScoreChanged();
}
@rpc(RpcMode.AllClients)
onScoreChanged(): void {
console.log(\`Score updated: \${this.score}\`);
}
@rpc(RpcMode.Server)
setGamePhase(phase: string): void {
this.gamePhase = phase;
}
}`,
},
};
// Get snippets for a platform
export function getSnippetsForPlatform(platform: string): Record<string, string> {
return CODE_SNIPPETS[platform as keyof typeof CODE_SNIPPETS] || {};
}
// Get common patterns for a platform
export function getPatternsForPlatform(
platform: string
): Record<string, string> {
const patterns = PLATFORM_PATTERNS[platform as keyof typeof PLATFORM_PATTERNS];
return patterns?.commonPatterns || {};
}

File diff suppressed because it is too large Load diff