V5 Frontend Integration Plan
Date: 2025-10-08 Goal: Integrate V4 multi-session workspace + theia llm chat into V5 wrapper Timeline: 8-12 hours (1-2 days) Status: 🟡 Planning → Implementation
🎯 Integration Objective
Merge three separate frontend implementations into one cohesive V5 application:
- V5 Wrapper (current) - Modern Header/Footer/Auth connected to V5 API
- V4 Frontend-Original - Multi-session workspace with tabs
- theia llm Extension - 4-mode llm chat widget
Result: Professional IDE wrapper with multi-session support, VS Code-like layout, and integrated llm chat.
📊 Component Inventory
Source: V5 Current (/workspace/PROJECTS/t2/src/)
| Component | Size | Status | Action |
|---|---|---|---|
| app.tsx | 369 lines | ✅ Keep | Merge routes |
| header.tsx | 5.4KB | ✅ Keep | No changes |
| footer.tsx | 3.8KB | ✅ Keep | No changes |
| layout.tsx | 2.7KB | 🔄 Enhance | Add workspace area |
| side-panel.tsx | 5.9KB | 🔄 Adapt | Make collapsible |
| theia-embed.tsx | 4.0KB | ✅ Keep | For theia pods |
| login-page.tsx | 5.0KB | ✅ Keep | No changes |
| register-page.tsx | 10KB | ✅ Keep | No changes |
| auth-store.ts | 14KB | ✅ Keep | Connected to V5 API |
| use-theia-theme.ts | 1.8KB | ✅ Keep | Theme sync |
| [20+ doc pages] | ~50KB | ✅ Keep | No changes |
Source: V4 Frontend-Original (/workspace/PROJECTS/t2/src/frontend-original/)
| Component | Size | Status | Action |
|---|---|---|---|
| unified-workspace.tsx | 12KB | ✅ Copy | Adapt for V5 |
| workspace-store.ts | 12KB | ✅ Copy | Update API endpoints |
| resizable-panel.tsx | 3.5KB | ✅ Copy | Use as-is |
| editor.tsx | 2.2KB | ⏭️ Skip | theia provides |
| file-explorer.tsx | 12KB | ⏭️ Skip | theia provides |
| terminal.tsx | 10KB | ⏭️ Skip | theia provides |
Source: theia llm Extension (/workspace/PROJECTS/t2/src/browser/llm-integration/)
| Component | Size | Status | Action |
|---|---|---|---|
| llm-chat-widget.tsx | 12KB | ✅ Extract | Convert to React |
| llm-service.ts | ~8KB | ✅ Copy | Adapt for wrapper |
🏗️ Target Architecture
┌─────────────────────────────────────────────────────────────┐
│ V5 Frontend Wrapper │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Header (V5) │ │
│ │ Logo | Theme Toggle | User Menu | Docs │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Main Content Area │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ Multi-Session Tabs (V4) │ │ │
│ │ │ [Session 1] [Session 2] [Session 3] [+] │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ Unified workspace (V4 + theia llm) │ │ │
│ │ │ │ │ │
│ │ │ ┌──┬────────────────────────┬─────────────────┐ │ │ │
│ │ │ │🔍│ Explorer / Search │ editor Area │ │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ │🌳│ - Files │ [File tabs] │ │ │ │
│ │ │ │ │ - Projects │ - main.ts │ │ │ │
│ │ │ │📂│ - Tasks │ - app.tsx │ │ │ │
│ │ │ │ │ │ - README.md │ │ │ │
│ │ │ │🤖│ AI Agents │ │ │ │ │
│ │ │ │ │ - Code Gen │ [Code editor] │ │ │ │
│ │ │ │💬│ - llm Chat (theia) │ │ │ │ │
│ │ │ │ │ - Review │ │ │ │ │
│ │ │ │⚙️│ Settings │ │ │ │ │
│ │ │ └──┴────────────────────────┴─────────────────┘ │ │ │
│ │ │ │ │ │
│ │ │ ┌──────────────────────────────────────────────┐ │ │ │
│ │ │ │ Bottom Panel (Collapsible) │ │ │ │
│ │ │ │ [terminal] [llm Chat] [Logs] [Problems] │ │ │ │
│ │ │ │ │ │ │ │
│ │ │ │ > npm run dev │ │ │ │
│ │ │ │ Building... │ │ │ │
│ │ │ └──────────────────────────────────────────────┘ │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Footer (V5) │ │
│ │ © 2025 Coditect | Privacy | Terms | Status │ │
│ └────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
📋 Detailed Integration Steps
Phase 1: Setup & Preparation (30 minutes)
1.1 Create New Component Directories
mkdir -p src/components/workspace
mkdir -p src/components/llm
mkdir -p src/components/session-tabs
mkdir -p src/stores
mkdir -p src/services
1.2 Install Missing Dependencies
# Check if we need any additional packages
npm install react-resizable-panels # Already installed ✅
npm install react-icons # Already installed ✅
Phase 2: Copy V4 Components (1 hour)
2.1 Copy Unifiedworkspace Component
Source: src/frontend-original/src/components/workspace/unified-workspace.tsx
Destination: src/components/workspace/unified-workspace.tsx
Changes Needed:
// Remove theia-specific imports
// Change from: import { FiFileText, ... } from 'react-icons/fi'
// Change to: Keep react-icons (already have it)
// Update layout structure
- Remove theia widgets (editor, terminal, file-explorer)
- Keep Activity Bar
- Keep Panel system
- Add llm Chat integration point
2.2 Copy workspaceStore
Source: src/frontend-original/src/stores/workspace-store.ts
Destination: src/stores/workspace-store.ts
Changes Needed:
// Update API endpoints
- OLD: apiUrl('/workspaces') → NEW: apiUrl('/v5/sessions')
- OLD: apiUrl('/projects') → NEW: apiUrl('/v5/projects') (if exists)
- OLD: apiUrl('/tasks') → NEW: apiUrl('/v5/tasks') (if exists)
// Update data models to match V5 API
interface Session { // Was: workspace
id: string
tenantId: string // Match V5 API field names
userId: string
name: string
workspacePath?: string
createdAt: string
updatedAt: string
lastAccessedAt: string
}
2.3 Copy ResizablePanel
Source: src/frontend-original/src/components/resizable-panel.tsx
Destination: src/components/workspace/resizable-panel.tsx
Changes: None needed (utility component)
Phase 3: Extract theia llm Widget (2 hours)
3.1 Convert llmChatWidget to Standalone React Component
Source: src/browser/llm-integration/llm-chat-widget.tsx (theia widget)
Destination: src/components/llm/llmChat.tsx (React component)
Conversion Steps:
// BEFORE (theia widget):
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
@injectable()
export class llmChatWidget extends ReactWidget {
protected render(): React.ReactNode { ... }
}
// AFTER (React component):
import React, { useState, useEffect } from 'react';
export const llmChat: React.FC<llmChatProps> = () => {
const [messages, setMessages] = useState<llmMessage[]>([]);
const [mode, setMode] = useState<WorkflowMode>('single');
// ... rest of component
return (
<div className="llm-chat-container">
{/* Same UI as theia widget */}
</div>
);
}
Key Changes:
- Remove
@injectable()and@inject()decorators - Convert class properties → React state (
useState) - Convert methods → functions
- Keep all UI exactly the same
- Keep 4 workflow modes (single, parallel, sequential, consensus)
- Keep model selectors
3.2 Copy llmService
Source: src/browser/llm-integration/services/llm-service.ts
Destination: src/services/llm-service.ts
Changes Needed:
// Remove theia dependency injection
- Remove: @injectable()
- Remove: import from '@theia/core'
// Make it a plain TypeScript class
export class llmService {
private readonly LM_STUDIO_BASE_URL =
import.meta.env.VITE_LM_STUDIO_URL || 'http://localhost:1234/v1';
// Keep all methods as-is
async chatCompletion(...) { ... }
async getAvailableModels() { ... }
// etc.
}
// Export singleton instance
export const llmService = new llmService();
3.3 Create llm Store (Zustand)
New File: src/stores/llm-store.ts
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { llmService } from '../services/llm-service';
interface llmState {
// State
messages: llmMessage[];
mode: WorkflowMode;
primaryModel: string | null;
secondaryModel: string | null;
availableModels: Array<{ id: string; name: string; provider: string }>;
isLoading: boolean;
// Actions
sendMessage: (content: string) => Promise<void>;
setMode: (mode: WorkflowMode) => void;
setPrimaryModel: (model: string) => void;
setSecondaryModel: (model: string) => void;
clearMessages: () => void;
loadModels: () => Promise<void>;
}
export const usellmStore = create<llmState>()(
persist(
(set, get) => ({
messages: [],
mode: 'single',
primaryModel: null,
secondaryModel: null,
availableModels: [],
isLoading: false,
sendMessage: async (content: string) => {
// Implementation from llmChatWidget
},
setMode: (mode) => set({ mode }),
setPrimaryModel: (model) => set({ primaryModel: model }),
setSecondaryModel: (model) => set({ secondaryModel: model }),
clearMessages: () => set({ messages: [] }),
loadModels: async () => {
const models = await llmService.getAvailableModels();
set({ availableModels: models });
}
}),
{ name: 'llm-storage' }
)
);
Phase 4: Create Session Tab Manager (1.5 hours)
New File: src/components/session-tabs/session-tab-manager.tsx
import React from 'react';
import { Box, HStack, IconButton, Text, useColorModeValue } from '@chakra-ui/react';
import { FiPlus, FiX } from 'react-icons/fi';
import { useworkspaceStore } from '../../stores/workspaceStore';
interface SessionTab {
id: string;
name: string;
isDirty?: boolean;
}
export const SessionTabManager: React.FC = () => {
const { sessions, currentSession, setCurrentSession, createSession, deleteSession } = useworkspaceStore();
const bgColor = useColorModeValue('gray.100', 'gray.800');
const activeBg = useColorModeValue('white', 'gray.700');
const borderColor = useColorModeValue('gray.200', 'gray.600');
return (
<HStack
spacing={0}
borderBottom={`1px solid ${borderColor}`}
bg={bgColor}
px={2}
py={1}
overflowX="auto"
>
{sessions.map((session) => (
<Box
key={session.id}
px={4}
py={2}
bg={currentSession?.id === session.id ? activeBg : 'transparent'}
borderBottom={currentSession?.id === session.id ? '2px solid' : 'none'}
borderColor="blue.500"
cursor="pointer"
onClick={() => setCurrentSession(session)}
position="relative"
display="flex"
alignItems="center"
gap={2}
_hover={{ bg: useColorModeValue('gray.50', 'gray.700') }}
>
<Text fontSize="sm" fontWeight={currentSession?.id === session.id ? 'semibold' : 'normal'}>
{session.name}
</Text>
{session.isDirty && (
<Box w="6px" h="6px" borderRadius="full" bg="blue.500" />
)}
<IconButton
aria-label="Close session"
icon={<FiX />}
size="xs"
variant="ghost"
onClick={(e) => {
e.stopPropagation();
deleteSession(session.id);
}}
/>
</Box>
))}
<IconButton
aria-label="New session"
icon={<FiPlus />}
size="sm"
variant="ghost"
onClick={() => createSession('Untitled')}
/>
</HStack>
);
};
Phase 5: Integrate into V5 layout (2 hours)
5.1 Update layout.tsx
File: src/components/layout.tsx
import React from 'react';
import { Box, Flex } from '@chakra-ui/react';
import { Header } from './Header';
import { Footer } from './Footer';
import { SessionTabManager } from './session-tabs/SessionTabManager';
import { Unifiedworkspace } from './workspace/Unifiedworkspace';
interface layoutProps {
children?: React.ReactNode;
showIDE?: boolean;
sessionId?: string;
}
export const layout: React.FC<layoutProps> = ({ children, showIDE = false, sessionId }) => {
return (
<Flex direction="column" h="100vh">
<Header />
{showIDE ? (
<Box flex="1" overflow="hidden">
{/* Multi-session tabs */}
<SessionTabManager />
{/* Unified workspace (VS Code-like layout) */}
<Unifiedworkspace />
</Box>
) : (
<Box flex="1" overflow="auto">
{children}
</Box>
)}
<Footer />
</Flex>
);
};
5.2 Update app.tsx Routes
File: src/app.tsx
// Add new imports
import { Unifiedworkspace } from './components/workspace/Unifiedworkspace';
// Update /ide route
<Route
path="/ide"
element={
isAuthenticated ? (
<layout showIDE>
{/* Unifiedworkspace is rendered by layout */}
</layout>
) : (
<Navigate to="/login" />
)
}
/>
// Add /ide/:sessionId route for specific sessions
<Route
path="/ide/:sessionId"
element={
isAuthenticated ? (
<layout showIDE sessionId={undefined} />
) : (
<Navigate to="/login" />
)
}
/>
Phase 6: Update Stores for V5 API (1.5 hours)
6.1 Update workspace-store.ts
File: src/stores/workspace-store.ts
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
interface Session {
id: string;
tenantId: string;
userId: string;
name: string;
workspacePath?: string;
createdAt: string;
updatedAt: string;
lastAccessedAt: string;
isDirty?: boolean;
}
interface workspaceState {
// Data
sessions: Session[];
currentSession: Session | null;
isLoading: boolean;
error: string | null;
// Actions
fetchSessions: () => Promise<void>;
setCurrentSession: (session: Session) => void;
createSession: (name: string) => Promise<void>;
updateSession: (id: string, data: Partial<Session>) => Promise<void>;
deleteSession: (id: string) => Promise<void>;
}
const apiUrl = (path: string) => {
const base = import.meta.env.VITE_API_URL || 'http://34.46.212.40/api';
return `${base}${path}`;
};
export const useworkspaceStore = create<workspaceState>()(
persist(
(set, get) => ({
sessions: [],
currentSession: null,
isLoading: false,
error: null,
fetchSessions: async () => {
const token = localStorage.getItem('auth_token');
if (!token) return;
set({ isLoading: true, error: null });
try {
const response = await fetch(apiUrl('/v5/sessions'), {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error('Failed to fetch sessions');
}
const data = await response.json();
const sessions = data.data || [];
set({
sessions,
currentSession: get().currentSession || sessions[0] || null
});
} catch (error) {
set({ error: error instanceof Error ? error.message : 'Failed to fetch sessions' });
} finally {
set({ isLoading: false });
}
},
setCurrentSession: (session) => {
set({ currentSession: session });
},
createSession: async (name: string) => {
const token = localStorage.getItem('auth_token');
if (!token) return;
try {
const response = await fetch(apiUrl('/v5/sessions'), {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ name })
});
if (!response.ok) {
throw new Error('Failed to create session');
}
const data = await response.json();
const newSession = data.data;
set(state => ({
sessions: [...state.sessions, newSession],
currentSession: newSession
}));
} catch (error) {
set({ error: error instanceof Error ? error.message : 'Failed to create session' });
}
},
updateSession: async (id: string, updates: Partial<Session>) => {
const token = localStorage.getItem('auth_token');
if (!token) return;
try {
const response = await fetch(apiUrl(`/v5/sessions/${id}`), {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(updates)
});
if (!response.ok) {
throw new Error('Failed to update session');
}
const data = await response.json();
const updatedSession = data.data;
set(state => ({
sessions: state.sessions.map(s => s.id === id ? updatedSession : s),
currentSession: state.currentSession?.id === id ? updatedSession : state.currentSession
}));
} catch (error) {
set({ error: error instanceof Error ? error.message : 'Failed to update session' });
}
},
deleteSession: async (id: string) => {
const token = localStorage.getItem('auth_token');
if (!token) return;
try {
const response = await fetch(apiUrl(`/v5/sessions/${id}`), {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${token}`
}
});
if (!response.ok) {
throw new Error('Failed to delete session');
}
set(state => {
const remainingSessions = state.sessions.filter(s => s.id !== id);
return {
sessions: remainingSessions,
currentSession: state.currentSession?.id === id
? remainingSessions[0] || null
: state.currentSession
};
});
} catch (error) {
set({ error: error instanceof Error ? error.message : 'Failed to delete session' });
}
}
}),
{ name: 'workspace-storage' }
)
);
Phase 7: Styling & Polish (1 hour)
7.1 Create workspace Styles
New File: src/components/workspace/workspace.css
/* Unified workspace Styles */
.unified-workspace {
display: flex;
height: 100%;
overflow: hidden;
}
.activity-bar {
width: 48px;
background: var(--chakra-colors-gray-800);
border-right: 1px solid var(--chakra-colors-gray-700);
display: flex;
flex-direction: column;
}
.sidebar {
width: 300px;
background: var(--chakra-colors-gray-800);
border-right: 1px solid var(--chakra-colors-gray-700);
overflow-y: auto;
}
.editor-area {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
.editor-tabs {
display: flex;
background: var(--chakra-colors-gray-800);
border-bottom: 1px solid var(--chakra-colors-gray-700);
overflow-x: auto;
}
.editor-content {
flex: 1;
overflow: auto;
background: var(--chakra-colors-gray-900);
}
.bottom-panel {
height: 200px;
background: var(--chakra-colors-gray-800);
border-top: 1px solid var(--chakra-colors-gray-700);
display: flex;
flex-direction: column;
}
/* llm Chat Styles */
.llm-chat-container {
display: flex;
flex-direction: column;
height: 100%;
background: var(--chakra-colors-gray-900);
}
.llm-chat-messages {
flex: 1;
overflow-y: auto;
padding: 16px;
}
.llm-message {
padding: 12px;
margin-bottom: 12px;
border-radius: 8px;
background: var(--chakra-colors-gray-800);
border: 1px solid var(--chakra-colors-gray-700);
}
.llm-message.user {
background: var(--chakra-colors-blue-900);
border-color: var(--chakra-colors-blue-700);
}
7.2 Import Styles in Unifiedworkspace
import './workspace.css';
Phase 8: Testing & Integration (2 hours)
8.1 Local Development Test Checklist
# Start dev server
npm run prototype:dev
# Test sequence:
1. Register new user (test-integration@example.com)
2. Login successfully
3. Redirect to /ide
4. See session tabs appear
5. Click "+" to create new session
6. Switch between sessions
7. Open llm Chat in activity bar
8. Test all 4 workflow modes:
- Single mode with one model
- Parallel mode with two models
- Sequential mode (llm1 → llm2)
- Consensus mode
9. Verify session persistence (refresh page)
10. Logout and login again
11. Verify sessions restored
8.2 Known Issues & Fixes
Issue 1: CORS errors when calling V5 API
// Fix in backend: Add CORS headers
// Or use proxy in vite.config.ts:
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://34.46.212.40',
changeOrigin: true
}
}
}
})
Issue 2: Session not persisting after page reload
// Fix: Load sessions on app mount
useEffect(() => {
if (isAuthenticated) {
workspaceStore.fetchSessions();
}
}, [isAuthenticated]);
📁 File Structure (After Integration)
src/
├── app.tsx # ✅ Updated with new routes
├── main.tsx # ✅ No changes
├── index.css # ✅ No changes
│
├── components/
│ ├── header.tsx # ✅ No changes
│ ├── footer.tsx # ✅ No changes
│ ├── layout.tsx # 🔄 Enhanced with workspace
│ ├── side-panel.tsx # ✅ Keep as-is
│ ├── theia-embed.tsx # ✅ Keep for theia pods
│ │
│ ├── workspace/ # 🆕 NEW
│ │ ├── unified-workspace.tsx # ✅ From V4
│ │ ├── resizable-panel.tsx # ✅ From V4
│ │ └── workspace.css # 🆕 NEW
│ │
│ ├── session-tabs/ # 🆕 NEW
│ │ └── session-tab-manager.tsx # 🆕 NEW
│ │
│ ├── llm/ # 🆕 NEW
│ │ ├── llmChat.tsx # ✅ From theia widget
│ │ ├── model-selector.tsx # 🆕 Extracted
│ │ └── mode-selector.tsx # 🆕 Extracted
│ │
│ └── [Origami components] # ✅ No changes
│
├── pages/ # ✅ All existing pages
│ ├── login-page.tsx
│ ├── register-page.tsx
│ └── [20+ doc pages]
│
├── stores/ # 🔄 Enhanced
│ ├── auth-store.ts # ✅ No changes
│ ├── workspace-store.ts # 🆕 NEW (from V4, adapted)
│ └── llm-store.ts # 🆕 NEW
│
├── services/ # 🆕 NEW
│ └── llm-service.ts # ✅ From theia extension
│
├── hooks/
│ └── use-theia-theme.ts # ✅ No changes
│
├── theme/
│ └── index.ts # ✅ No changes
│
└── types/
└── index.ts # 🔄 Add WorkflowMode, etc.
🔄 API Endpoint Mapping
| Feature | V4 Endpoint | V5 Endpoint | Status |
|---|---|---|---|
| Sessions | /workspaces | /v5/sessions | ✅ EXISTS |
| Session Create | POST /workspaces | POST /v5/sessions | ✅ EXISTS |
| Session List | GET /workspaces | GET /v5/sessions | ✅ EXISTS |
| Session Get | GET /workspaces/{id} | GET /v5/sessions/{id} | ✅ EXISTS |
| Session Update | PATCH /workspaces/{id} | PATCH /v5/sessions/{id} | ❌ NEED TO ADD |
| Session Delete | DELETE /workspaces/{id} | DELETE /v5/sessions/{id} | ✅ EXISTS |
| Projects | /projects | /v5/projects | ❌ FUTURE |
| Tasks | /tasks | /v5/tasks | ❌ FUTURE |
Note: We'll use sessions instead of workspaces for V5. Projects and tasks are future features.
⏱️ Time Estimates
| Phase | Task | Time | Priority |
|---|---|---|---|
| 1 | Setup & Preparation | 30m | 🔴 Critical |
| 2 | Copy V4 Components | 1h | 🔴 Critical |
| 3 | Extract theia llm Widget | 2h | 🔴 Critical |
| 4 | Create Session Tab Manager | 1.5h | 🔴 Critical |
| 5 | Integrate into V5 layout | 2h | 🔴 Critical |
| 6 | Update Stores for V5 API | 1.5h | 🔴 Critical |
| 7 | Styling & Polish | 1h | 🟡 High |
| 8 | Testing & Integration | 2h | 🔴 Critical |
| Total | 11.5h | ~1.5 days |
🎯 Success Criteria
Functional Requirements
- User can register and login with V5 API
- User sees session tabs after login
- User can create new sessions (+ button)
- User can switch between sessions (tab click)
- User can close sessions (X button)
- Unifiedworkspace renders with activity bar
- llm Chat accessible from activity bar
- llm Chat has 4 workflow modes
- Model selection works (16+ models)
- Sessions persist across page reloads
- Sessions sync with V5 backend API
UI Requirements
- Header/Footer remain from V5
- Theme toggle works (dark/light)
- Responsive layout (no horizontal scroll)
- Activity bar icons visible
- editor area functional
- Bottom panel collapsible
- Professional VS Code-like appearance
Technical Requirements
- No TypeScript errors
- No console errors
- API calls use correct V5 endpoints
- JWT auth working
- Multi-tenant isolation
- Local storage persistence
- Production build succeeds
🚀 Deployment Plan
After successful local testing:
# 1. Create .env.production
VITE_API_URL=http://34.46.212.40/api
# 2. Build production bundle
npm run prototype:build
# 3. Create Dockerfile.frontend
# (see v5-frontend-integration-plan.md Phase 9)
# 4. Deploy to GKE
gcloud builds submit --config cloudbuild-frontend.yaml
# 5. Update LoadBalancer routing
# / → Frontend
# /api/v5/* → Backend API
# /theia/* → theia pods (future)
📋 Next Steps
Immediate (After plan approval):
- Execute Phase 1 (Setup)
- Execute Phase 2 (Copy components)
- Execute Phase 3 (Extract llm widget)
Short-term (Same session): 4. Execute Phase 4-6 (Integration) 5. Execute Phase 7-8 (Testing)
Deploy (Next session): 6. Production build 7. Deploy to GKE 8. End-to-end testing on production
Plan Status: ✅ READY FOR EXECUTION
Estimated Completion: 1.5 days (solo) | 1 day (with assistance)
Next Action: Proceed with Phase 1 implementation?