Complete Phase 2: Full integration of multi-platform system

This completes the integration of the cross-platform translation engine
into the main application, making it fully functional.

App.tsx Changes:
- Added platform state management (currentPlatform: PlatformId)
- Added translation panel state (showTranslation)
- Integrated TranslationPanel component with lazy loading
- Updated Toolbar with platform selector and translate button
- Passed platform prop to TemplatesDrawer and CodeEditor
- Connected all state management for seamless platform switching

TemplatesDrawer Changes:
- Now accepts currentPlatform prop
- Filters templates by selected platform using getTemplatesForPlatform()
- Shows platform icon and name in header
- Displays template count for current platform
- Ready for UEFN/Spatial/Core templates

CodeEditor Changes:
- Accepts optional platform prop (defaults to 'roblox')
- Dynamically sets Monaco editor language based on platform:
  * roblox → lua
  * uefn → plaintext (Verse not yet in Monaco)
  * spatial → typescript
  * core → lua
- Maintains backward compatibility with existing code

Functional Features:
 Platform switching in toolbar
 Templates filter by platform automatically
 Editor language changes with platform
 Translation panel opens with "Translate" button
 Side-by-side code comparison
 Mock translation working
 All state connected and reactive

Next: Phase 3 (Create UEFN templates) or Phase 4 (Claude API integration)
This commit is contained in:
Claude 2026-01-17 22:58:04 +00:00
parent 0029ed685f
commit f4e6651724
No known key found for this signature in database
3 changed files with 51 additions and 13 deletions

View file

@ -20,6 +20,8 @@ import { initPostHog, captureEvent } from './lib/posthog';
import { initSentry, captureError } from './lib/sentry'; import { initSentry, captureError } from './lib/sentry';
import { LoadingSpinner } from './components/ui/loading-spinner'; import { LoadingSpinner } from './components/ui/loading-spinner';
import { PlatformId } from './lib/platforms';
// Lazy load heavy/modal components for code splitting and better initial load // Lazy load heavy/modal components for code splitting and better initial load
const TemplatesDrawer = lazy(() => import('./components/TemplatesDrawer').then(m => ({ default: m.TemplatesDrawer }))); const TemplatesDrawer = lazy(() => import('./components/TemplatesDrawer').then(m => ({ default: m.TemplatesDrawer })));
const WelcomeDialog = lazy(() => import('./components/WelcomeDialog').then(m => ({ default: m.WelcomeDialog }))); const WelcomeDialog = lazy(() => import('./components/WelcomeDialog').then(m => ({ default: m.WelcomeDialog })));
@ -27,6 +29,7 @@ const PreviewModal = lazy(() => import('./components/PreviewModal').then(m => ({
const NewProjectModal = lazy(() => import('./components/NewProjectModal').then(m => ({ default: m.NewProjectModal }))); const NewProjectModal = lazy(() => import('./components/NewProjectModal').then(m => ({ default: m.NewProjectModal })));
const EducationPanel = lazy(() => import('./components/EducationPanel').then(m => ({ default: m.EducationPanel }))); const EducationPanel = lazy(() => import('./components/EducationPanel').then(m => ({ default: m.EducationPanel })));
const PassportLogin = lazy(() => import('./components/PassportLogin').then(m => ({ default: m.PassportLogin }))); const PassportLogin = lazy(() => import('./components/PassportLogin').then(m => ({ default: m.PassportLogin })));
const TranslationPanel = lazy(() => import('./components/TranslationPanel').then(m => ({ default: m.TranslationPanel })));
function App() { function App() {
const [currentCode, setCurrentCode] = useState(''); const [currentCode, setCurrentCode] = useState('');
@ -36,7 +39,9 @@ function App() {
const [showFileSearch, setShowFileSearch] = useState(false); const [showFileSearch, setShowFileSearch] = useState(false);
const [showCommandPalette, setShowCommandPalette] = useState(false); const [showCommandPalette, setShowCommandPalette] = useState(false);
const [showSearchInFiles, setShowSearchInFiles] = useState(false); const [showSearchInFiles, setShowSearchInFiles] = useState(false);
const [showTranslation, setShowTranslation] = useState(false);
const [code, setCode] = useState(''); const [code, setCode] = useState('');
const [currentPlatform, setCurrentPlatform] = useState<PlatformId>('roblox');
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const [showPassportLogin, setShowPassportLogin] = useState(false); const [showPassportLogin, setShowPassportLogin] = useState(false);
@ -457,6 +462,9 @@ end)`,
<div className="h-screen flex flex-col bg-background text-foreground"> <div className="h-screen flex flex-col bg-background text-foreground">
<Toolbar <Toolbar
code={currentCode} code={currentCode}
currentPlatform={currentPlatform}
onPlatformChange={setCurrentPlatform}
onTranslateClick={() => setShowTranslation(true)}
onTemplatesClick={() => setShowTemplates(true)} onTemplatesClick={() => setShowTemplates(true)}
onPreviewClick={() => setShowPreview(true)} onPreviewClick={() => setShowPreview(true)}
onNewProjectClick={() => setShowNewProject(true)} onNewProjectClick={() => setShowNewProject(true)}
@ -490,7 +498,7 @@ end)`,
onFileClose={handleFileClose} onFileClose={handleFileClose}
/> />
<div className="flex-1"> <div className="flex-1">
<CodeEditor onCodeChange={handleCodeChange} /> <CodeEditor onCodeChange={handleCodeChange} platform={currentPlatform} />
</div> </div>
</TabsContent> </TabsContent>
<TabsContent value="ai" className="flex-1 m-0"> <TabsContent value="ai" className="flex-1 m-0">
@ -529,7 +537,7 @@ end)`,
onFileClose={handleFileClose} onFileClose={handleFileClose}
/> />
<div className="flex-1"> <div className="flex-1">
<CodeEditor onCodeChange={setCurrentCode} /> <CodeEditor onCodeChange={setCurrentCode} platform={currentPlatform} />
</div> </div>
</div> </div>
</ResizablePanel> </ResizablePanel>
@ -572,6 +580,7 @@ end)`,
<TemplatesDrawer <TemplatesDrawer
onSelectTemplate={handleTemplateSelect} onSelectTemplate={handleTemplateSelect}
onClose={() => setShowTemplates(false)} onClose={() => setShowTemplates(false)}
currentPlatform={currentPlatform}
/> />
)} )}
</Suspense> </Suspense>
@ -592,6 +601,17 @@ end)`,
/> />
</Suspense> </Suspense>
<Suspense fallback={null}>
{showTranslation && (
<TranslationPanel
isOpen={showTranslation}
onClose={() => setShowTranslation(false)}
currentCode={currentCode}
currentPlatform={currentPlatform}
/>
)}
</Suspense>
<Suspense fallback={null}> <Suspense fallback={null}>
<WelcomeDialog /> <WelcomeDialog />
</Suspense> </Suspense>

View file

@ -1,14 +1,24 @@
import Editor from '@monaco-editor/react'; import Editor from '@monaco-editor/react';
import { useKV } from '@github/spark/hooks'; import { useKV } from '@github/spark/hooks';
import { useEffect } from 'react'; import { useEffect, useMemo } from 'react';
import { LoadingSpinner } from './ui/loading-spinner'; import { LoadingSpinner } from './ui/loading-spinner';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { PlatformId } from '@/lib/platforms';
interface CodeEditorProps { interface CodeEditorProps {
onCodeChange?: (code: string) => void; onCodeChange?: (code: string) => void;
platform?: PlatformId;
} }
export function CodeEditor({ onCodeChange }: CodeEditorProps) { export function CodeEditor({ onCodeChange, platform = 'roblox' }: CodeEditorProps) {
const languageMap: Record<PlatformId, string> = useMemo(() => ({
roblox: 'lua',
uefn: 'plaintext', // Verse not yet supported by Monaco, use plaintext
spatial: 'typescript',
core: 'lua',
}), []);
const editorLanguage = languageMap[platform];
const [code, setCode] = useKV('aethex-current-code', `-- Welcome to AeThex Studio! const [code, setCode] = useKV('aethex-current-code', `-- Welcome to AeThex Studio!
-- Write your Roblox Lua code here -- Write your Roblox Lua code here
@ -61,7 +71,7 @@ end)
<div className="flex-1 min-h-0"> <div className="flex-1 min-h-0">
<Editor <Editor
height="100%" height="100%"
defaultLanguage="lua" language={editorLanguage}
theme="vs-dark" theme="vs-dark"
value={code} value={code}
onChange={handleEditorChange} onChange={handleEditorChange}

View file

@ -4,19 +4,24 @@ import { ScrollArea } from '@/components/ui/scroll-area';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { X } from '@phosphor-icons/react'; import { X } from '@phosphor-icons/react';
import { templates, type ScriptTemplate } from '@/lib/templates'; import { templates, type ScriptTemplate, getTemplatesForPlatform } from '@/lib/templates';
import { PlatformId, getPlatform } from '@/lib/platforms';
interface TemplatesDrawerProps { interface TemplatesDrawerProps {
onSelectTemplate: (code: string) => void; onSelectTemplate: (code: string) => void;
onClose: () => void; onClose: () => void;
currentPlatform: PlatformId;
} }
export function TemplatesDrawer({ onSelectTemplate, onClose }: TemplatesDrawerProps) { export function TemplatesDrawer({ onSelectTemplate, onClose, currentPlatform }: TemplatesDrawerProps) {
const platform = getPlatform(currentPlatform);
const platformTemplates = getTemplatesForPlatform(currentPlatform);
const categories = { const categories = {
beginner: templates.filter(t => t.category === 'beginner'), beginner: platformTemplates.filter(t => t.category === 'beginner'),
gameplay: templates.filter(t => t.category === 'gameplay'), gameplay: platformTemplates.filter(t => t.category === 'gameplay'),
ui: templates.filter(t => t.category === 'ui'), ui: platformTemplates.filter(t => t.category === 'ui'),
tools: templates.filter(t => t.category === 'tools'), tools: platformTemplates.filter(t => t.category === 'tools'),
}; };
const handleTemplateClick = (template: ScriptTemplate) => { const handleTemplateClick = (template: ScriptTemplate) => {
@ -29,9 +34,12 @@ export function TemplatesDrawer({ onSelectTemplate, onClose }: TemplatesDrawerPr
<Card className="w-full max-w-4xl max-h-[80vh] flex flex-col bg-card border-border"> <Card className="w-full max-w-4xl max-h-[80vh] flex flex-col bg-card border-border">
<div className="flex items-center justify-between p-4 border-b border-border"> <div className="flex items-center justify-between p-4 border-b border-border">
<div> <div>
<h2 className="text-2xl font-bold">Script Templates</h2> <h2 className="text-2xl font-bold flex items-center gap-2">
<span>{platform.icon}</span>
{platform.displayName} Templates
</h2>
<p className="text-sm text-muted-foreground mt-1"> <p className="text-sm text-muted-foreground mt-1">
Choose a template to get started quickly {platformTemplates.length} templates available Choose one to get started
</p> </p>
</div> </div>
<Button variant="ghost" size="icon" onClick={onClose}> <Button variant="ghost" size="icon" onClick={onClose}>