diff --git a/src/components/PlatformSelector.tsx b/src/components/PlatformSelector.tsx new file mode 100644 index 0000000..a4a06dd --- /dev/null +++ b/src/components/PlatformSelector.tsx @@ -0,0 +1,84 @@ +import { memo } from 'react'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { Badge } from '@/components/ui/badge'; +import { PlatformId, platforms, activePlatforms } from '@/lib/platforms'; + +interface PlatformSelectorProps { + value: PlatformId; + onChange: (platform: PlatformId) => void; + disabled?: boolean; +} + +export const PlatformSelector = memo(function PlatformSelector({ + value, + onChange, + disabled = false, +}: PlatformSelectorProps) { + const currentPlatform = platforms[value]; + + return ( + + ); +}); diff --git a/src/components/Toolbar.tsx b/src/components/Toolbar.tsx index a2f77dd..7c1c2f9 100644 --- a/src/components/Toolbar.tsx +++ b/src/components/Toolbar.tsx @@ -8,20 +8,25 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; -import { Copy, FileCode, Download, Info, Play, FolderPlus, User, SignOut, List } from '@phosphor-icons/react'; +import { Copy, FileCode, Download, Info, Play, FolderPlus, User, SignOut, List, ArrowsLeftRight } from '@phosphor-icons/react'; import { toast } from 'sonner'; import { useState, useEffect, useCallback, memo } from 'react'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { ThemeSwitcher } from './ThemeSwitcher'; +import { PlatformSelector } from './PlatformSelector'; +import { PlatformId } from '@/lib/platforms'; interface ToolbarProps { code: string; onTemplatesClick: () => void; onPreviewClick: () => void; onNewProjectClick: () => void; + currentPlatform: PlatformId; + onPlatformChange: (platform: PlatformId) => void; + onTranslateClick?: () => void; } -export function Toolbar({ code, onTemplatesClick, onPreviewClick, onNewProjectClick }: ToolbarProps) { +export function Toolbar({ code, onTemplatesClick, onPreviewClick, onNewProjectClick, currentPlatform, onPlatformChange, onTranslateClick }: ToolbarProps) { const [showInfo, setShowInfo] = useState(false); const [user, setUser] = useState<{ login: string; avatarUrl: string; email: string } | null>(null); @@ -67,7 +72,34 @@ export function Toolbar({ code, onTemplatesClick, onPreviewClick, onNewProjectCl {/* Desktop: Show all buttons */} -
+
+ {/* Platform Selector */} + + + {/* Translation Button */} + {onTranslateClick && ( + + + + + Cross-Platform Translation + + )} + +
+ +
+ + {/* Platform Selection */} +
+
+ Source: + + {sourcePlatform.icon} + {sourcePlatform.displayName} + +
+ +
+ Target: + +
+ +
+ + {/* Side-by-Side Code View */} +
+ {/* Source Code */} +
+
+

+ {sourcePlatform.icon} + {sourcePlatform.language} (Original) +

+
+ +
+                {currentCode || '// No code to translate'}
+              
+
+
+ + {/* Translated Code */} +
+
+

+ {targetPlatformInfo.icon} + {targetPlatformInfo.language} (Translated) +

+ {result?.translatedCode && ( + + )} +
+ + {isTranslating ? ( +
+
+ +

+ Translating code... +

+
+
+ ) : result ? ( +
+ {result.success && result.translatedCode ? ( + <> +
+                        {result.translatedCode}
+                      
+ + {result.explanation && ( +
+

+ + Explanation +

+

+ {result.explanation} +

+
+ )} + + {result.warnings && result.warnings.length > 0 && ( +
+

+ + Warnings +

+
    + {result.warnings.map((warning, i) => ( +
  • • {warning}
  • + ))} +
+
+ )} + + ) : ( +
+

+ {result.error || 'Translation failed'} +

+
+ )} +
+ ) : ( +
+
+ +

+ Click "Translate" to convert your code +

+

+ {sourcePlatform.displayName} → {targetPlatformInfo.displayName} +

+
+
+ )} +
+
+
+ + {/* Footer */} +
+

+ 🚀 Cross-platform translation powered by AeThex Studio AI • Support + for {sourcePlatform.displayName}, {targetPlatformInfo.displayName}, + and more coming soon +

+
+
+
+ ); +}); diff --git a/src/lib/platforms.ts b/src/lib/platforms.ts new file mode 100644 index 0000000..23f3a31 --- /dev/null +++ b/src/lib/platforms.ts @@ -0,0 +1,90 @@ +/** + * Platform Abstraction Layer + * Supports: Roblox, UEFN, Spatial, Core + */ + +export type PlatformId = 'roblox' | 'uefn' | 'spatial' | 'core'; + +export interface Platform { + id: PlatformId; + name: string; + displayName: string; + language: string; + fileExtension: string; + description: string; + color: string; + icon: string; + apiDocs: string; + status: 'active' | 'beta' | 'coming-soon'; +} + +export const platforms: Record = { + roblox: { + id: 'roblox', + name: 'Roblox', + displayName: 'Roblox Studio', + language: 'Lua 5.1', + fileExtension: '.lua', + description: 'Build immersive 3D experiences on Roblox', + color: '#00A2FF', + icon: '🎮', + apiDocs: 'https://create.roblox.com/docs', + status: 'active', + }, + uefn: { + id: 'uefn', + name: 'UEFN', + displayName: 'Unreal Editor for Fortnite', + language: 'Verse', + fileExtension: '.verse', + description: 'Create Fortnite experiences with Verse', + color: '#0E86D4', + icon: '⚡', + apiDocs: 'https://dev.epicgames.com/documentation/en-us/uefn', + status: 'beta', + }, + spatial: { + id: 'spatial', + name: 'Spatial', + displayName: 'Spatial Creator Toolkit', + language: 'TypeScript', + fileExtension: '.ts', + description: 'Build VR/AR experiences for Spatial', + color: '#FF6B6B', + icon: '🌐', + apiDocs: 'https://toolkit.spatial.io/docs', + status: 'coming-soon', + }, + core: { + id: 'core', + name: 'Core', + displayName: 'Core Games', + language: 'Lua 5.3', + fileExtension: '.lua', + description: 'Develop multiplayer games on Core', + color: '#FF4655', + icon: '🎯', + apiDocs: 'https://docs.coregames.com', + status: 'coming-soon', + }, +}; + +export const activePlatforms = Object.values(platforms).filter( + (p) => p.status === 'active' || p.status === 'beta' +); + +export function getPlatform(id: PlatformId): Platform { + return platforms[id]; +} + +export function isPlatformActive(id: PlatformId): boolean { + return platforms[id].status === 'active'; +} + +export function getLanguageForPlatform(id: PlatformId): string { + return platforms[id].language; +} + +export function getFileExtensionForPlatform(id: PlatformId): string { + return platforms[id].fileExtension; +} diff --git a/src/lib/templates.ts b/src/lib/templates.ts index 6a1aace..40f70df 100644 --- a/src/lib/templates.ts +++ b/src/lib/templates.ts @@ -1,9 +1,16 @@ +import { PlatformId } from './platforms'; + export interface ScriptTemplate { id: string; name: string; description: string; code: string; category: 'beginner' | 'gameplay' | 'ui' | 'tools' | 'advanced'; + platform: PlatformId; +} + +export function getTemplatesForPlatform(platform: PlatformId): ScriptTemplate[] { + return templates.filter(t => t.platform === platform); } export const templates: ScriptTemplate[] = [ @@ -12,6 +19,7 @@ export const templates: ScriptTemplate[] = [ name: 'Hello World', description: 'Basic print statement to test scripts', category: 'beginner', + platform: 'roblox', code: `-- Hello World Script print("Hello from Roblox!") @@ -23,6 +31,7 @@ print(message)`, name: 'Player Join Handler', description: 'Detect when players join and leave the game', category: 'beginner', + platform: 'roblox', code: `local Players = game:GetService("Players") Players.PlayerAdded:Connect(function(player) @@ -49,6 +58,7 @@ end)`, name: 'Part Touch Detector', description: 'Detect when a player touches a part', category: 'gameplay', + platform: 'roblox', code: `local part = script.Parent part.Touched:Connect(function(hit) @@ -69,6 +79,7 @@ end)`, name: 'Teleport Part', description: 'Teleport players when they touch a part', category: 'gameplay', + platform: 'roblox', code: `local part = script.Parent local destination = Vector3.new(0, 10, 0) -- Change this to your destination @@ -90,6 +101,7 @@ end)`, name: 'GUI Button Click', description: 'Handle button clicks in a GUI', category: 'ui', + platform: 'roblox', code: `local button = script.Parent button.MouseButton1Click:Connect(function() @@ -106,6 +118,7 @@ end)`, name: 'Give Tool to Player', description: 'Give a tool to players when they join', category: 'tools', + platform: 'roblox', code: `local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") @@ -130,6 +143,7 @@ end)`, name: 'Tween Part Animation', description: 'Smoothly animate a part using TweenService', category: 'gameplay', + platform: 'roblox', code: `local TweenService = game:GetService("TweenService") local part = script.Parent @@ -154,6 +168,7 @@ tween:Play()`, name: 'DataStore Save/Load', description: 'Save and load player data using DataStore', category: 'gameplay', + platform: 'roblox', code: `local DataStoreService = game:GetService("DataStoreService") local Players = game:GetService("Players") @@ -202,6 +217,7 @@ end)`, name: 'Proximity Prompt Interaction', description: 'Interactive prompt that appears when player is near', category: 'ui', + platform: 'roblox', code: `local part = script.Parent local ProximityPromptService = game:GetService("ProximityPromptService") @@ -230,6 +246,7 @@ end)`, name: 'Round System', description: 'Complete round-based game loop system', category: 'advanced', + platform: 'roblox', code: `local ReplicatedStorage = game:GetService("ReplicatedStorage") local Players = game:GetService("Players") @@ -268,6 +285,7 @@ end`, name: 'Advanced Leaderstats with XP', description: 'Complete leaderstats system with coins, XP, and level', category: 'gameplay', + platform: 'roblox', code: `local Players = game:GetService("Players") local function calculateLevel(xp) @@ -314,6 +332,7 @@ end)`, name: 'Shop System', description: 'Basic shop system with purchase handling', category: 'gameplay', + platform: 'roblox', code: `local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") @@ -377,6 +396,7 @@ end name: 'Camera Manipulation', description: 'Custom camera angles and cinematic views', category: 'advanced', + platform: 'roblox', code: `local Players = game:GetService("Players") local RunService = game:GetService("RunService") @@ -419,6 +439,7 @@ end)`, name: 'Basic Combat System', description: 'Simple damage and health system', category: 'gameplay', + platform: 'roblox', code: `local tool = script.Parent local damage = 10 local cooldown = 1 @@ -470,6 +491,7 @@ end)`, name: 'Basic Admin Commands', description: 'Simple admin command system with permissions', category: 'tools', + platform: 'roblox', code: `local Players = game:GetService("Players") local admins = { @@ -533,6 +555,7 @@ end)`, name: 'Badge Award System', description: 'Award badges to players for achievements', category: 'gameplay', + platform: 'roblox', code: `local BadgeService = game:GetService("BadgeService") local Players = game:GetService("Players") @@ -580,6 +603,7 @@ return { name: 'Inventory System', description: 'Complete player inventory with items and storage', category: 'advanced', + platform: 'roblox', code: `local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") @@ -663,6 +687,7 @@ return { name: 'Countdown Timer UI', description: 'Visual countdown timer with events', category: 'ui', + platform: 'roblox', code: `local Players = game:GetService("Players") local player = Players.LocalPlayer local playerGui = player:WaitForChild("PlayerGui") @@ -720,6 +745,7 @@ end)`, name: 'Sound/Music Manager', description: 'Manage background music and sound effects', category: 'tools', + platform: 'roblox', code: `local SoundService = game:GetService("SoundService") local SoundManager = {} @@ -797,6 +823,7 @@ return SoundManager`, name: 'Pathfinding NPC', description: 'NPC that follows player using PathfindingService', category: 'advanced', + platform: 'roblox', code: `local PathfindingService = game:GetService("PathfindingService") local RunService = game:GetService("RunService") @@ -862,6 +889,7 @@ spawn(followPath)`, name: 'Checkpoint System', description: 'Save player checkpoints and respawn at last checkpoint', category: 'gameplay', + platform: 'roblox', code: `local Players = game:GetService("Players") local checkpoints = workspace:WaitForChild("Checkpoints"):GetChildren() @@ -925,6 +953,7 @@ setupCheckpoints()`, name: 'Team System', description: 'Auto-assign players to balanced teams', category: 'gameplay', + platform: 'roblox', code: `local Teams = game:GetService("Teams") local Players = game:GetService("Players") @@ -991,6 +1020,7 @@ end)`, name: 'Custom Chat Commands', description: 'Player chat commands for emotes and actions', category: 'tools', + platform: 'roblox', code: `local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") @@ -1067,6 +1097,7 @@ end)`, name: 'Character Morphing', description: 'Change player appearance/morph into different models', category: 'gameplay', + platform: 'roblox', code: `local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") @@ -1153,6 +1184,7 @@ end)`, name: 'Kill Brick', description: 'Instantly kill player on touch (lava, void, etc.)', category: 'beginner', + platform: 'roblox', code: `local killBrick = script.Parent -- Make it look dangerous diff --git a/src/lib/translation-engine.ts b/src/lib/translation-engine.ts new file mode 100644 index 0000000..78e7696 --- /dev/null +++ b/src/lib/translation-engine.ts @@ -0,0 +1,273 @@ +/** + * Cross-Platform Code Translation Engine + * Core competitive differentiator for AeThex Studio + */ + +import { PlatformId, getPlatform } from './platforms'; +import { toast } from 'sonner'; +import { captureEvent, captureError } from './analytics'; + +export interface TranslationRequest { + sourceCode: string; + sourcePlatform: PlatformId; + targetPlatform: PlatformId; + context?: string; +} + +export interface TranslationResult { + success: boolean; + translatedCode?: string; + explanation?: string; + warnings?: string[]; + error?: string; +} + +/** + * Platform-specific translation prompts + */ +const getTranslationPrompt = ( + sourceCode: string, + sourcePlatform: PlatformId, + targetPlatform: PlatformId, + context?: string +): string => { + const sourcePlat = getPlatform(sourcePlatform); + const targetPlat = getPlatform(targetPlatform); + + return `You are an expert game developer specializing in cross-platform game development. + +**Task**: Translate the following ${sourcePlat.language} code (${sourcePlat.displayName}) to ${targetPlat.language} (${targetPlat.displayName}). + +**Source Platform**: ${sourcePlat.displayName} +- Language: ${sourcePlat.language} +- API Documentation: ${sourcePlat.apiDocs} + +**Target Platform**: ${targetPlat.displayName} +- Language: ${targetPlat.language} +- API Documentation: ${targetPlat.apiDocs} + +**Source Code**: +\`\`\`${sourcePlat.language.toLowerCase()} +${sourceCode} +\`\`\` + +${context ? `**Additional Context**: ${context}\n` : ''} + +**Instructions**: +1. Translate the code to ${targetPlat.language} while preserving the logic and functionality +2. Use ${targetPlat.displayName}-native APIs and best practices +3. Add comments explaining platform-specific differences +4. Ensure the code follows ${targetPlat.language} conventions and style +5. If certain features don't have direct equivalents, provide the closest alternative and explain + +**Output Format**: +Return ONLY the translated code wrapped in triple backticks with the language identifier. +Then provide a brief explanation of key changes and any warnings. + +Example: +\`\`\`${targetPlat.fileExtension.replace('.', '')} +// Translated code here +\`\`\` + +**Explanation**: [Brief explanation of translation] + +**Warnings**: [Any caveats or limitations, if applicable]`; +}; + +/** + * Platform-specific translation rules + */ +const platformTranslationRules: Record> = { + 'roblox-to-uefn': [ + 'game:GetService() → Use Verse imports', + 'Instance.new() → object{} syntax in Verse', + 'Connect() → Subscribe() in Verse', + 'wait() → Sleep() in Verse', + 'print() → Print() in Verse', + ], + 'uefn-to-roblox': [ + 'Verse imports → game:GetService()', + 'object{} → Instance.new()', + 'Subscribe() → Connect()', + 'Sleep() → wait()', + 'Print() → print()', + ], + 'roblox-to-spatial': [ + 'Lua → TypeScript syntax', + 'game:GetService() → Spatial SDK imports', + 'Instance.new() → new SpatialObject()', + 'Connect() → addEventListener()', + 'wait() → await setTimeout()', + ], + 'spatial-to-roblox': [ + 'TypeScript → Lua syntax', + 'Spatial SDK → game:GetService()', + 'new SpatialObject() → Instance.new()', + 'addEventListener() → Connect()', + 'await setTimeout() → wait()', + ], +}; + +/** + * Mock translation service (for development without API key) + * Replace with actual Claude API call in production + */ +async function translateWithMockService( + request: TranslationRequest +): Promise { + const ruleKey = `${request.sourcePlatform}-to-${request.targetPlatform}`; + const rules = platformTranslationRules[ruleKey] || []; + + return { + success: true, + translatedCode: `-- Translated from ${request.sourcePlatform} to ${request.targetPlatform} +-- Translation Rules Applied: +${rules.map(r => `-- ${r}`).join('\n')} + +-- Original Code (needs actual translation): +${request.sourceCode} + +-- TODO: Replace with actual ${request.targetPlatform} implementation`, + explanation: `This is a mock translation. The actual translation engine will use Claude API to intelligently convert ${request.sourcePlatform} code to ${request.targetPlatform}.`, + warnings: [ + 'Mock translation active - integrate Claude API for production', + `Translation rules: ${rules.join(', ')}`, + ], + }; +} + +/** + * Translate code using Claude API + * This is the production implementation + */ +async function translateWithClaudeAPI( + request: TranslationRequest +): Promise { + try { + const prompt = getTranslationPrompt( + request.sourceCode, + request.sourcePlatform, + request.targetPlatform, + request.context + ); + + // TODO: Replace with actual Claude API call + // For now, using mock service + // In production, use: + // const response = await fetch('https://api.anthropic.com/v1/messages', { + // method: 'POST', + // headers: { + // 'Content-Type': 'application/json', + // 'x-api-key': process.env.CLAUDE_API_KEY, + // 'anthropic-version': '2023-06-01', + // }, + // body: JSON.stringify({ + // model: 'claude-3-5-sonnet-20241022', + // max_tokens: 4096, + // messages: [{ role: 'user', content: prompt }], + // }), + // }); + + return await translateWithMockService(request); + } catch (error) { + captureError(error as Error, { + context: 'translation_api', + sourcePlatform: request.sourcePlatform, + targetPlatform: request.targetPlatform, + }); + + return { + success: false, + error: `Translation failed: ${(error as Error).message}`, + }; + } +} + +/** + * Main translation function + */ +export async function translateCode( + request: TranslationRequest +): Promise { + try { + // Validate platforms + if (request.sourcePlatform === request.targetPlatform) { + return { + success: false, + error: 'Source and target platforms must be different', + }; + } + + if (!request.sourceCode || request.sourceCode.trim() === '') { + return { + success: false, + error: 'Source code cannot be empty', + }; + } + + // Log translation attempt + captureEvent('translation_started', { + sourcePlatform: request.sourcePlatform, + targetPlatform: request.targetPlatform, + codeLength: request.sourceCode.length, + }); + + // Perform translation + const result = await translateWithClaudeAPI(request); + + // Log result + if (result.success) { + captureEvent('translation_success', { + sourcePlatform: request.sourcePlatform, + targetPlatform: request.targetPlatform, + }); + toast.success( + `Translated ${request.sourcePlatform} → ${request.targetPlatform}` + ); + } else { + captureEvent('translation_failed', { + sourcePlatform: request.sourcePlatform, + targetPlatform: request.targetPlatform, + error: result.error, + }); + toast.error(`Translation failed: ${result.error}`); + } + + return result; + } catch (error) { + captureError(error as Error, { context: 'translate_code' }); + return { + success: false, + error: `Unexpected error: ${(error as Error).message}`, + }; + } +} + +/** + * Get supported translation pairs + */ +export function getSupportedTranslations(): Array<{ + source: PlatformId; + target: PlatformId; +}> { + return [ + { source: 'roblox', target: 'uefn' }, + { source: 'uefn', target: 'roblox' }, + { source: 'roblox', target: 'spatial' }, + { source: 'spatial', target: 'roblox' }, + { source: 'uefn', target: 'spatial' }, + { source: 'spatial', target: 'uefn' }, + ]; +} + +/** + * Check if translation is supported + */ +export function isTranslationSupported( + source: PlatformId, + target: PlatformId +): boolean { + return getSupportedTranslations().some( + (pair) => pair.source === source && pair.target === target + ); +}