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:
parent
159e40f02c
commit
9c54fb3386
6 changed files with 2687 additions and 2 deletions
16
src/App.tsx
16
src/App.tsx
|
|
@ -36,6 +36,7 @@ const AvatarToolkit = lazy(() => import('./components/AvatarToolkit'));
|
||||||
const VisualScriptingCanvas = lazy(() => import('./components/visual-scripting/VisualScriptingCanvas'));
|
const VisualScriptingCanvas = lazy(() => import('./components/visual-scripting/VisualScriptingCanvas'));
|
||||||
const AssetLibrary = lazy(() => import('./components/assets/AssetLibrary'));
|
const AssetLibrary = lazy(() => import('./components/assets/AssetLibrary'));
|
||||||
const LivePreview = lazy(() => import('./components/preview/LivePreview'));
|
const LivePreview = lazy(() => import('./components/preview/LivePreview'));
|
||||||
|
const AIGenerationPanel = lazy(() => import('./components/ai-generation/AIGenerationPanel'));
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [currentCode, setCurrentCode] = useState('');
|
const [currentCode, setCurrentCode] = useState('');
|
||||||
|
|
@ -50,6 +51,7 @@ function App() {
|
||||||
const [showVisualScripting, setShowVisualScripting] = useState(false);
|
const [showVisualScripting, setShowVisualScripting] = useState(false);
|
||||||
const [showAssetLibrary, setShowAssetLibrary] = useState(false);
|
const [showAssetLibrary, setShowAssetLibrary] = useState(false);
|
||||||
const [showLivePreview, setShowLivePreview] = useState(false);
|
const [showLivePreview, setShowLivePreview] = useState(false);
|
||||||
|
const [showAIGeneration, setShowAIGeneration] = useState(false);
|
||||||
const [code, setCode] = useState('');
|
const [code, setCode] = useState('');
|
||||||
const [currentPlatform, setCurrentPlatform] = useState<PlatformId>('roblox');
|
const [currentPlatform, setCurrentPlatform] = useState<PlatformId>('roblox');
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
|
@ -489,6 +491,7 @@ end)`,
|
||||||
onVisualScriptingClick={() => setShowVisualScripting(true)}
|
onVisualScriptingClick={() => setShowVisualScripting(true)}
|
||||||
onAssetLibraryClick={() => setShowAssetLibrary(true)}
|
onAssetLibraryClick={() => setShowAssetLibrary(true)}
|
||||||
onLivePreviewClick={() => setShowLivePreview(true)}
|
onLivePreviewClick={() => setShowLivePreview(true)}
|
||||||
|
onAIGenerationClick={() => setShowAIGeneration(true)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -654,6 +657,19 @@ end)`,
|
||||||
</Dialog>
|
</Dialog>
|
||||||
)}
|
)}
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
<Suspense fallback={null}>
|
||||||
|
{showAIGeneration && (
|
||||||
|
<AIGenerationPanel
|
||||||
|
isOpen={showAIGeneration}
|
||||||
|
onClose={() => setShowAIGeneration(false)}
|
||||||
|
currentPlatform={currentPlatform}
|
||||||
|
onCodeGenerated={(code) => {
|
||||||
|
setCurrentCode(code);
|
||||||
|
handleCodeChange(code);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Suspense>
|
||||||
<Suspense fallback={null}>
|
<Suspense fallback={null}>
|
||||||
<WelcomeDialog />
|
<WelcomeDialog />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import {
|
||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '@/components/ui/dropdown-menu';
|
} 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 { toast } from 'sonner';
|
||||||
import { useState, useEffect, useCallback, memo } from 'react';
|
import { useState, useEffect, useCallback, memo } from 'react';
|
||||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from './ui/dialog';
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from './ui/dialog';
|
||||||
|
|
@ -28,9 +28,10 @@ interface ToolbarProps {
|
||||||
onVisualScriptingClick?: () => void;
|
onVisualScriptingClick?: () => void;
|
||||||
onAssetLibraryClick?: () => void;
|
onAssetLibraryClick?: () => void;
|
||||||
onLivePreviewClick?: () => 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 [showInfo, setShowInfo] = useState(false);
|
||||||
const [user, setUser] = useState<{ login: string; avatarUrl: string; email: string } | null>(null);
|
const [user, setUser] = useState<{ login: string; avatarUrl: string; email: string } | null>(null);
|
||||||
|
|
||||||
|
|
@ -178,6 +179,25 @@ export function Toolbar({ code, onTemplatesClick, onPreviewClick, onNewProjectCl
|
||||||
</Tooltip>
|
</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" />
|
<div className="h-6 w-px bg-border mx-1" />
|
||||||
|
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
|
|
@ -316,6 +336,12 @@ export function Toolbar({ code, onTemplatesClick, onPreviewClick, onNewProjectCl
|
||||||
<span>3D Preview</span>
|
<span>3D Preview</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
)}
|
)}
|
||||||
|
{onAIGenerationClick && (
|
||||||
|
<DropdownMenuItem onClick={onAIGenerationClick}>
|
||||||
|
<MagicWand className="mr-2" size={16} />
|
||||||
|
<span>AI Generate</span>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
<DropdownMenuItem onClick={handleCopy}>
|
<DropdownMenuItem onClick={handleCopy}>
|
||||||
<Copy className="mr-2" size={16} />
|
<Copy className="mr-2" size={16} />
|
||||||
<span>Copy Code</span>
|
<span>Copy Code</span>
|
||||||
|
|
|
||||||
560
src/components/ai-generation/AIGenerationPanel.tsx
Normal file
560
src/components/ai-generation/AIGenerationPanel.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
1
src/components/ai-generation/index.ts
Normal file
1
src/components/ai-generation/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export { default as AIGenerationPanel } from './AIGenerationPanel';
|
||||||
433
src/lib/ai-generation/generation-prompts.ts
Normal file
433
src/lib/ai-generation/generation-prompts.ts
Normal 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 || {};
|
||||||
|
}
|
||||||
1649
src/lib/ai-generation/system-templates.ts
Normal file
1649
src/lib/ai-generation/system-templates.ts
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue