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:
parent
0029ed685f
commit
f4e6651724
3 changed files with 51 additions and 13 deletions
24
src/App.tsx
24
src/App.tsx
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
|
|
@ -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}>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue