Shantanupathak94 commited on
Commit
0e118b0
·
1 Parent(s): 951f367

fixing broken things.. ;)

Browse files
Files changed (2) hide show
  1. client/src/App.jsx +52 -102
  2. client/src/apiService.js +8 -4
client/src/App.jsx CHANGED
@@ -8,13 +8,16 @@ const getFileIcon = (filename) => {
8
  if (filename.endsWith('.js') || filename.endsWith('.jsx')) return <i className="fab fa-js" style={{ color: '#f7df1e' }}></i>;
9
  if (filename.endsWith('.json')) return <i className="fas fa-brackets-curly" style={{ color: '#cb3837' }}></i>;
10
  if (filename.endsWith('.py')) return <i className="fab fa-python" style={{ color: '#306998' }}></i>;
 
11
  return <i className="fas fa-file-code" style={{ color: '#8b949e' }}></i>;
12
  };
13
 
14
  const getLanguage = (filename) => {
15
  if (filename.endsWith('.html')) return 'html';
16
  if (filename.endsWith('.css')) return 'css';
17
- if (filename.endsWith('.js')) return 'javascript';
 
 
18
  return 'plaintext';
19
  };
20
 
@@ -22,30 +25,25 @@ function App() {
22
  const [files, setFiles] = useState({
23
  'index.html': { name: 'index.html', language: 'html', value: '\n<h1>Hello Shantanu ✨</h1>' },
24
  'style.css': { name: 'style.css', language: 'css', value: '/* Type your CSS here */\nbody {\n background-color: #1e1e1e;\n color: white;\n}' },
25
- 'script.js': { name: 'script.js', language: 'javascript', value: 'console.log("Ethrix Cloud Terminal Active!");' }
26
  });
27
 
28
  const [activeTab, setActiveTab] = useState('index.html');
29
  const [saveStatus, setSaveStatus] = useState('☁️ Synced');
30
 
31
- // UI Toggles
32
- const [showTerminal, setShowTerminal] = useState(true);
33
- const [isTerminalMinimized, setIsTerminalMinimized] = useState(false);
34
  const [showExplorer, setShowExplorer] = useState(true);
35
  const [websiteTheme, setWebsiteTheme] = useState('Auto Theme');
36
- const [activeMode, setActiveMode] = useState('Copilot Mode');
37
 
38
- // File Creation & Rename States
39
  const [isCreatingFile, setIsCreatingFile] = useState(false);
40
  const [newFileName, setNewFileName] = useState('');
41
  const [renamingFile, setRenamingFile] = useState(null);
42
  const [renameInput, setRenameInput] = useState('');
43
 
44
- // AI Chat Workflow States
45
- const [chatMessages, setChatMessages] = useState([{ role: 'ai', text: 'Hi Shantanu! 🖤 Main Shanvika hu. Aaj kis theme ki website banani hai?' }]);
46
  const [aiInput, setAiInput] = useState('');
47
  const [aiWorkflowStatus, setAiWorkflowStatus] = useState('');
48
- const [isTyping, setIsTyping] = useState(false);
49
 
50
  const filesRef = useRef(files);
51
  const terminalRef = useRef(null);
@@ -121,7 +119,6 @@ function App() {
121
  if (activeTab === fileName) setActiveTab(Object.keys(newFiles)[0] || '');
122
  };
123
 
124
- // 🚀 THE NEW AGENTIC WORKFLOW SUBMIT LOGIC 🚀
125
  const handleAISubmit = async (e) => {
126
  if (e.key === 'Enter' && aiInput.trim() !== '') {
127
  const prompt = aiInput.trim();
@@ -130,35 +127,29 @@ function App() {
130
  setAiWorkflowStatus('🧠 Reading context & planning architecture...');
131
 
132
  try {
133
- // ✨ Context Builder: Saari current files ko array mein convert kar rahe hain
134
  const existingFilesArray = Object.values(filesRef.current).map(f => ({
135
  filename: f.name,
136
- language: f.language,
137
  code: f.value
138
  }));
139
 
140
- const agenticPrompt = `Theme: ${websiteTheme}. Mode: ${activeMode}. User Request: ${prompt}`;
141
-
142
- // apiService mein naya update bhej rahe hain
143
- const generatedFiles = await api.generateCode(agenticPrompt, existingFilesArray, "gemini");
144
 
145
  if (generatedFiles && generatedFiles.length > 0) {
146
- setAiWorkflowStatus(`📄 Writing code in ${generatedFiles.length} files...`);
147
 
148
  const updatedFiles = { ...filesRef.current };
149
  generatedFiles.forEach(f => {
150
- updatedFiles[f.filename] = { name: f.filename, language: f.language || 'plaintext', value: f.code };
151
  });
152
 
153
  setTimeout(() => {
154
  setFiles(updatedFiles);
155
- // Open HTML or the first changed file
156
  const fileToOpen = generatedFiles.find(f => f.filename.endsWith('.html'))?.filename || generatedFiles[0].filename;
157
  setActiveTab(fileToOpen);
158
-
159
  if (!showExplorer) setShowExplorer(true);
160
  setAiWorkflowStatus('');
161
- setChatMessages(prev => [...prev, { role: 'ai', text: `✅ Maine ${generatedFiles.length} file(s) mein code update kar diya hai! ✨ Press Run in New Tab to check.` }]);
162
  }, 1000);
163
  }
164
  } catch (err) {
@@ -168,37 +159,23 @@ function App() {
168
  }
169
  };
170
 
171
- // ✨ FIX 1: Terminal Expand/Minimize Logic
172
- const toggleTerminalSize = () => {
173
- setIsTerminalMinimized(!isTerminalMinimized);
174
- };
175
-
176
- // Jab terminal minimize se wapas bada ho, usko resize karo taaki UI na fate
177
- useEffect(() => {
178
- if (!isTerminalMinimized && fitAddonRef.current) {
179
- setTimeout(() => {
180
- fitAddonRef.current.fit();
181
- }, 350); // CSS transition ke baad resize
182
- }
183
- }, [isTerminalMinimized]);
184
-
185
  const clearTerminal = () => {
186
  if (xtermInstance.current) { xtermInstance.current.clear(); xtermInstance.current.write('user@ethrix:~$ '); }
187
  };
188
 
189
- // FIX 2: Terminal ko wapas laane ka function
190
- const reopenTerminal = () => {
191
- setShowTerminal(true);
192
- setIsTerminalMinimized(false);
193
- };
194
 
195
- // Real Terminal Lifecycle
196
  useEffect(() => {
197
  if (!showTerminal || !terminalRef.current) return;
 
 
198
 
199
- // Agar terminal already nahi bana hai toh naya banao
200
  if (!xtermInstance.current) {
201
- const term = new window.Terminal({ theme: { background: '#0d1117', foreground: '#c9d1d9' }, fontFamily: '"Fira Code", monospace', fontSize: 13 });
202
  const fitAddon = new window.FitAddon.FitAddon();
203
  fitAddonRef.current = fitAddon;
204
 
@@ -207,7 +184,7 @@ function App() {
207
  fitAddon.fit();
208
  xtermInstance.current = term;
209
 
210
- term.writeln('\x1b[1;32mEthrix Local Terminal Started.\x1b[0m');
211
  term.write('user@ethrix:~$ ');
212
 
213
  let inputBuffer = '';
@@ -223,16 +200,12 @@ function App() {
223
  } else { inputBuffer += key; term.write(key); }
224
  });
225
 
226
- const resizeObserver = new ResizeObserver(() => {
227
- if (fitAddonRef.current && !isTerminalMinimized) fitAddonRef.current.fit();
228
- });
229
  resizeObserver.observe(terminalRef.current);
230
-
231
  return () => resizeObserver.disconnect();
232
  }
233
  }, [showTerminal]);
234
 
235
- // Cleanup jab terminal close (X) dabaya jaye
236
  useEffect(() => {
237
  if (!showTerminal && xtermInstance.current) {
238
  xtermInstance.current.dispose();
@@ -244,20 +217,17 @@ function App() {
244
  return (
245
  <div style={{ display: 'flex', flexDirection: 'column', height: '100vh', backgroundColor: '#0d1117', color: '#c9d1d9', fontFamily: 'sans-serif', overflow: 'hidden' }}>
246
 
247
- {/* 🚀 MAIN CONTENT AREA (3 Panels) */}
248
  <div style={{ display: 'flex', flex: 1, overflow: 'hidden' }}>
249
 
250
- {/* 📁 LEFT PANE: Smart File Explorer */}
251
  {showExplorer && (
252
- <div style={{ width: '250px', backgroundColor: '#010409', borderRight: '1px solid #30363d', display: 'flex', flexDirection: 'column' }}>
253
  <div style={{ padding: '12px 15px', display: 'flex', justifyContent: 'space-between', alignItems: 'center', borderBottom: '1px solid #30363d', backgroundColor: '#161b22' }}>
254
  <span style={{ fontSize: '11px', fontWeight: 'bold', color: '#8b949e', letterSpacing: '1px' }}>EXPLORER</span>
255
  <div style={{ display: 'flex', gap: '12px', color: '#c9d1d9', fontSize: '13px' }}>
256
- <i className="fas fa-file-plus" onClick={() => setIsCreatingFile(true)} style={{ cursor: 'pointer' }} title="New File"></i>
257
- <i className="fas fa-folder-plus" onClick={() => alert("Folder logic connecting soon!")} style={{ cursor: 'pointer' }} title="New Folder"></i>
258
  <i className="fas fa-upload" onClick={() => alert("Upload connected soon!")} style={{ cursor: 'pointer' }} title="Upload"></i>
259
  <i className="fas fa-download" onClick={() => alert("Download connected soon!")} style={{ cursor: 'pointer' }} title="Download"></i>
260
- {/* ✨ FIX 3: GitHub Icon Wapas Aa Gaya! ✨ */}
261
  <i className="fab fa-github" onClick={() => alert("GitHub Sync soon!")} style={{ cursor: 'pointer' }} title="GitHub"></i>
262
  </div>
263
  </div>
@@ -266,7 +236,7 @@ function App() {
266
  {isCreatingFile && (
267
  <div style={{ padding: '5px 10px', display: 'flex', alignItems: 'center', gap: '8px', backgroundColor: '#161b22', borderRadius: '4px', marginBottom: '5px' }}>
268
  <i className="fas fa-file" style={{ color: '#8b949e' }}></i>
269
- <input autoFocus type="text" value={newFileName} onChange={(e) => setNewFileName(e.target.value)} onKeyDown={handleCreateFile} onBlur={() => setIsCreatingFile(false)} placeholder="name.js" style={{ background: 'transparent', border: 'none', color: '#fff', outline: 'none', width: '100%', fontSize: '13px' }} />
270
  </div>
271
  )}
272
 
@@ -277,7 +247,7 @@ function App() {
277
  {renamingFile === fileName ? (
278
  <input autoFocus type="text" value={renameInput} onChange={(e) => setRenameInput(e.target.value)} onKeyDown={(e) => handleRenameFile(e, fileName)} onBlur={() => setRenamingFile(null)} style={{ background: '#161b22', border: '1px solid #58a6ff', color: '#fff', outline: 'none', width: '100%', fontSize: '13px', padding: '2px 4px' }} />
279
  ) : (
280
- <span style={{ fontSize: '13px', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{fileName}</span>
281
  )}
282
  </div>
283
  {renamingFile !== fileName && (
@@ -292,26 +262,25 @@ function App() {
292
  </div>
293
  )}
294
 
295
- {/* 💻 MIDDLE PANE: Editor & Terminal */}
296
  <div style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0 }}>
297
 
298
- <div style={{ height: '45px', backgroundColor: '#0d1117', borderBottom: '1px solid #30363d', display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '0 15px' }}>
299
- <div style={{ display: 'flex', alignItems: 'center' }}>
300
- <div onClick={() => setShowExplorer(!showExplorer)} style={{ padding: '0 15px 0 0', cursor: 'pointer', color: showExplorer ? '#58a6ff' : '#8b949e', borderRight: '1px solid #30363d', marginRight: '10px' }}>
301
  <i className="fas fa-bars" style={{ fontSize: '15px' }}></i>
302
  </div>
303
  {Object.keys(files).map(fileName => (
304
- <div key={fileName} onClick={() => setActiveTab(fileName)} style={{ padding: '10px 20px', fontSize: '13px', cursor: 'pointer', backgroundColor: activeTab === fileName ? '#161b22' : 'transparent', borderTop: activeTab === fileName ? '2px solid #58a6ff' : '2px solid transparent', color: activeTab === fileName ? '#c9d1d9' : '#8b949e', display: 'flex', alignItems: 'center', gap: '8px' }}>
305
- {getFileIcon(fileName)} {fileName}
306
  </div>
307
  ))}
308
  </div>
309
- <button onClick={runLivePreview} style={{ backgroundColor: '#238636', color: '#fff', border: 'none', padding: '6px 16px', borderRadius: '6px', cursor: 'pointer', fontSize: '13px', fontWeight: 'bold' }}>
310
- <i className="fas fa-external-link-alt" style={{ marginRight: '6px' }}></i> Run in New Tab
311
  </button>
312
  </div>
313
 
314
- {/* ✨ FIXED EDITOR PANE (Scroll Bug Fixed) ✨ */}
315
  <div style={{ flex: 1, backgroundColor: '#0d1117', position: 'relative', overflow: 'hidden' }}>
316
  <Editor
317
  height="100%"
@@ -319,56 +288,42 @@ function App() {
319
  theme="vs-dark"
320
  value={files[activeTab]?.value || ''}
321
  onChange={(val) => setFiles({ ...files, [activeTab]: { ...files[activeTab], value: val } })}
322
- options={{
323
- minimap: { enabled: false },
324
- fontSize: 14,
325
- automaticLayout: true, // 🚀 YEH HAI ASLI JADOO (Fixes Canvas Bug)
326
- scrollBeyondLastLine: false, // Extra khali space hatane ke liye
327
- wordWrap: 'on' // Code screen ke bahar na jaye
328
- }}
329
  />
330
  </div>
331
 
332
- {/* ✨ FIXED TERMINAL PANE ✨ */}
333
  {showTerminal && (
334
- <div style={{ height: isTerminalMinimized ? '35px' : '220px', transition: 'height 0.3s ease', backgroundColor: '#010409', borderTop: '1px solid #30363d', display: 'flex', flexDirection: 'column' }}>
335
- <div style={{ padding: '8px 15px', fontSize: '11px', color: '#8b949e', borderBottom: '1px solid #30363d', display: 'flex', justifyContent: 'space-between', alignItems: 'center', backgroundColor: '#161b22', userSelect: 'none' }}>
336
  <div style={{ display: 'flex', gap: '15px' }}>
337
- <span style={{ borderBottom: isTerminalMinimized ? 'none' : '1px solid #58a6ff', color: '#c9d1d9', fontWeight: 'bold' }}>TERMINAL</span>
338
- {!isTerminalMinimized && <span style={{ cursor: 'pointer' }}>OUTPUT</span>}
339
  </div>
340
-
341
  <div style={{ display: 'flex', gap: '15px', fontSize: '14px' }}>
342
- <i className="fas fa-trash-alt" onClick={clearTerminal} style={{ cursor: 'pointer', color: '#c9d1d9' }} title="Clear Terminal"></i>
343
- <i className={`fas ${isTerminalMinimized ? 'fa-chevron-up' : 'fa-chevron-down'}`} onClick={toggleTerminalSize} style={{ cursor: 'pointer', color: '#c9d1d9' }} title={isTerminalMinimized ? "Expand" : "Minimize"}></i>
344
  <i className="fas fa-times" onClick={() => setShowTerminal(false)} style={{ cursor: 'pointer', color: '#c9d1d9' }} title="Close"></i>
345
  </div>
346
  </div>
347
-
348
- {/* CSS Hack: Height 0 instead of display:none preserves Canvas memory! */}
349
- <div style={{ flex: 1, position: 'relative', overflow: 'hidden' }}>
350
- <div style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, opacity: isTerminalMinimized ? 0 : 1, pointerEvents: isTerminalMinimized ? 'none' : 'auto', transition: 'opacity 0.2s' }}>
351
- <div ref={terminalRef} style={{ width: '100%', height: '100%', padding: '10px' }}></div>
352
- </div>
353
  </div>
354
  </div>
355
  )}
356
  </div>
357
 
358
- {/* 🤖 RIGHT PANE: AI Chat Panel */}
359
- <div style={{ width: '320px', backgroundColor: '#010409', borderLeft: '1px solid #30363d', display: 'flex', flexDirection: 'column' }}>
360
  <div style={{ padding: '15px', borderBottom: '1px solid #30363d', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
361
  <div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
362
  <div style={{ width: '30px', height: '30px', borderRadius: '50%', background: 'linear-gradient(45deg, #ff00cc, #3333ff)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'white', fontWeight: 'bold' }}>S</div>
363
  <span style={{ fontSize: '14px', fontWeight: 'bold', color: '#c9d1d9' }}>Shanvika AI</span>
364
  </div>
365
-
366
  <select value={websiteTheme} onChange={(e) => setWebsiteTheme(e.target.value)} style={{ backgroundColor: '#161b22', color: '#8b949e', border: '1px solid #30363d', padding: '4px 8px', borderRadius: '4px', outline: 'none', fontSize: '11px', cursor: 'pointer' }}>
367
  <option>Auto Theme</option>
368
  <option>Professional</option>
369
  <option>Sci-Fi</option>
370
  <option>Fantasy</option>
371
- <option>Minimalist</option>
372
  </select>
373
  </div>
374
 
@@ -383,7 +338,6 @@ function App() {
383
  </div>
384
  </div>
385
  ))}
386
-
387
  {aiWorkflowStatus && (
388
  <div style={{ alignSelf: 'flex-start', maxWidth: '85%' }}>
389
  <div style={{ padding: '8px 12px', borderRadius: '8px', fontSize: '12px', backgroundColor: 'rgba(88, 166, 255, 0.1)', color: '#58a6ff', border: '1px solid rgba(88, 166, 255, 0.2)', display: 'flex', alignItems: 'center', gap: '8px' }}>
@@ -396,7 +350,7 @@ function App() {
396
 
397
  <div style={{ padding: '15px', borderTop: '1px solid #30363d', backgroundColor: '#0d1117' }}>
398
  <div style={{ position: 'relative' }}>
399
- <textarea value={aiInput} onChange={(e) => setAiInput(e.target.value)} onKeyDown={handleAISubmit} disabled={!!aiWorkflowStatus} placeholder="E.g., Make a portfolio website..." style={{ width: '100%', height: '80px', backgroundColor: '#010409', border: '1px solid #30363d', borderRadius: '8px', color: '#c9d1d9', padding: '10px', fontSize: '13px', resize: 'none', outline: 'none', opacity: aiWorkflowStatus ? 0.5 : 1 }} />
400
  <button onClick={() => handleAISubmit({ key: 'Enter' })} disabled={!!aiWorkflowStatus || !aiInput.trim()} style={{ position: 'absolute', bottom: '10px', right: '10px', background: 'transparent', border: 'none', color: '#58a6ff', cursor: 'pointer', fontSize: '16px', opacity: (aiWorkflowStatus || !aiInput.trim()) ? 0.5 : 1 }}>
401
  <i className="fas fa-paper-plane"></i>
402
  </button>
@@ -406,15 +360,12 @@ function App() {
406
 
407
  </div>
408
 
409
- {/* 🚀 VS CODE STYLE BOTTOM STATUS BAR */}
410
- <div style={{ height: '24px', backgroundColor: '#010409', borderTop: '1px solid #30363d', display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '0 15px', fontSize: '11px', color: '#c9d1d9', zIndex: 10 }}>
411
  <div style={{ display: 'flex', gap: '20px', alignItems: 'center' }}>
412
-
413
- {/* ✨ Yahan Se Terminal Wapas Aayega ✨ */}
414
- <div onClick={reopenTerminal} style={{ cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '5px', color: showTerminal ? '#58a6ff' : '#8b949e', transition: 'color 0.2s' }} title="Reopen Terminal">
415
  <i className="fas fa-terminal"></i> Terminal
416
  </div>
417
-
418
  <div style={{ display: 'flex', alignItems: 'center', gap: '5px', color: saveStatus.includes('Error') ? '#f85149' : '#3fb950' }}>
419
  <i className="fas fa-cloud"></i> {saveStatus}
420
  </div>
@@ -423,8 +374,7 @@ function App() {
423
  <div style={{ display: 'flex', gap: '15px', alignItems: 'center' }}>
424
  <select value={activeMode} onChange={(e) => setActiveMode(e.target.value)} style={{ background: 'transparent', border: 'none', color: '#8b949e', outline: 'none', fontSize: '11px', cursor: 'pointer' }}>
425
  <option>Copilot Mode</option>
426
- <option>God Mode (Auto)</option>
427
- <option>Debug Mode</option>
428
  </select>
429
  <div><i className="fas fa-code-branch" style={{ marginRight: '5px' }}></i> main</div>
430
  <div><i className="fas fa-check-double" style={{ marginRight: '5px', color: '#3fb950' }}></i> Ethrix Prettier</div>
 
8
  if (filename.endsWith('.js') || filename.endsWith('.jsx')) return <i className="fab fa-js" style={{ color: '#f7df1e' }}></i>;
9
  if (filename.endsWith('.json')) return <i className="fas fa-brackets-curly" style={{ color: '#cb3837' }}></i>;
10
  if (filename.endsWith('.py')) return <i className="fab fa-python" style={{ color: '#306998' }}></i>;
11
+ if (filename.includes('/')) return <i className="fas fa-folder-open" style={{ color: '#e3b341' }}></i>;
12
  return <i className="fas fa-file-code" style={{ color: '#8b949e' }}></i>;
13
  };
14
 
15
  const getLanguage = (filename) => {
16
  if (filename.endsWith('.html')) return 'html';
17
  if (filename.endsWith('.css')) return 'css';
18
+ if (filename.endsWith('.js') || filename.endsWith('.jsx')) return 'javascript';
19
+ if (filename.endsWith('.json')) return 'json';
20
+ if (filename.endsWith('.py')) return 'python';
21
  return 'plaintext';
22
  };
23
 
 
25
  const [files, setFiles] = useState({
26
  'index.html': { name: 'index.html', language: 'html', value: '\n<h1>Hello Shantanu ✨</h1>' },
27
  'style.css': { name: 'style.css', language: 'css', value: '/* Type your CSS here */\nbody {\n background-color: #1e1e1e;\n color: white;\n}' },
28
+ 'script.js': { name: 'script.js', language: 'javascript', value: 'console.log("Ethrix God Mode Active!");' }
29
  });
30
 
31
  const [activeTab, setActiveTab] = useState('index.html');
32
  const [saveStatus, setSaveStatus] = useState('☁️ Synced');
33
 
34
+ const [showTerminal, setShowTerminal] = useState(false);
 
 
35
  const [showExplorer, setShowExplorer] = useState(true);
36
  const [websiteTheme, setWebsiteTheme] = useState('Auto Theme');
37
+ const [activeMode, setActiveMode] = useState('Agentic Mode');
38
 
 
39
  const [isCreatingFile, setIsCreatingFile] = useState(false);
40
  const [newFileName, setNewFileName] = useState('');
41
  const [renamingFile, setRenamingFile] = useState(null);
42
  const [renameInput, setRenameInput] = useState('');
43
 
44
+ const [chatMessages, setChatMessages] = useState([{ role: 'ai', text: 'Hi Shantanu! 🖤 Main Shanvika hu. Agentic Workflow is 100% online. Kya banana hai aaj?' }]);
 
45
  const [aiInput, setAiInput] = useState('');
46
  const [aiWorkflowStatus, setAiWorkflowStatus] = useState('');
 
47
 
48
  const filesRef = useRef(files);
49
  const terminalRef = useRef(null);
 
119
  if (activeTab === fileName) setActiveTab(Object.keys(newFiles)[0] || '');
120
  };
121
 
 
122
  const handleAISubmit = async (e) => {
123
  if (e.key === 'Enter' && aiInput.trim() !== '') {
124
  const prompt = aiInput.trim();
 
127
  setAiWorkflowStatus('🧠 Reading context & planning architecture...');
128
 
129
  try {
 
130
  const existingFilesArray = Object.values(filesRef.current).map(f => ({
131
  filename: f.name,
132
+ language: f.language || 'plaintext',
133
  code: f.value
134
  }));
135
 
136
+ const generatedFiles = await api.generateCode(prompt, existingFilesArray, "gemini");
 
 
 
137
 
138
  if (generatedFiles && generatedFiles.length > 0) {
139
+ setAiWorkflowStatus(`📄 Updating ${generatedFiles.length} files...`);
140
 
141
  const updatedFiles = { ...filesRef.current };
142
  generatedFiles.forEach(f => {
143
+ updatedFiles[f.filename] = { name: f.filename, language: getLanguage(f.filename), value: f.code };
144
  });
145
 
146
  setTimeout(() => {
147
  setFiles(updatedFiles);
 
148
  const fileToOpen = generatedFiles.find(f => f.filename.endsWith('.html'))?.filename || generatedFiles[0].filename;
149
  setActiveTab(fileToOpen);
 
150
  if (!showExplorer) setShowExplorer(true);
151
  setAiWorkflowStatus('');
152
+ setChatMessages(prev => [...prev, { role: 'ai', text: `✅ Code generated beautifully! ✨ Press Run in New Tab to see it. Pata hai, main changes sirf unhi files mein karti hu jinki zarurat hoti hai!` }]);
153
  }, 1000);
154
  }
155
  } catch (err) {
 
159
  }
160
  };
161
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  const clearTerminal = () => {
163
  if (xtermInstance.current) { xtermInstance.current.clear(); xtermInstance.current.write('user@ethrix:~$ '); }
164
  };
165
 
166
+ useEffect(() => {
167
+ if (showTerminal && fitAddonRef.current) {
168
+ setTimeout(() => { fitAddonRef.current.fit(); }, 50);
169
+ }
170
+ }, [showTerminal]);
171
 
 
172
  useEffect(() => {
173
  if (!showTerminal || !terminalRef.current) return;
174
+ // Check if CDN loaded
175
+ if (!window.Terminal || !window.FitAddon) return;
176
 
 
177
  if (!xtermInstance.current) {
178
+ const term = new window.Terminal({ theme: { background: '#0d1117', foreground: '#c9d1d9', cursor: '#58a6ff' }, fontFamily: '"Fira Code", monospace', fontSize: 13, cursorBlink: true });
179
  const fitAddon = new window.FitAddon.FitAddon();
180
  fitAddonRef.current = fitAddon;
181
 
 
184
  fitAddon.fit();
185
  xtermInstance.current = term;
186
 
187
+ term.writeln('\x1b[1;36mEthrix Secure Cloud Terminal 1.0\x1b[0m');
188
  term.write('user@ethrix:~$ ');
189
 
190
  let inputBuffer = '';
 
200
  } else { inputBuffer += key; term.write(key); }
201
  });
202
 
203
+ const resizeObserver = new ResizeObserver(() => { if (fitAddonRef.current) fitAddonRef.current.fit(); });
 
 
204
  resizeObserver.observe(terminalRef.current);
 
205
  return () => resizeObserver.disconnect();
206
  }
207
  }, [showTerminal]);
208
 
 
209
  useEffect(() => {
210
  if (!showTerminal && xtermInstance.current) {
211
  xtermInstance.current.dispose();
 
217
  return (
218
  <div style={{ display: 'flex', flexDirection: 'column', height: '100vh', backgroundColor: '#0d1117', color: '#c9d1d9', fontFamily: 'sans-serif', overflow: 'hidden' }}>
219
 
 
220
  <div style={{ display: 'flex', flex: 1, overflow: 'hidden' }}>
221
 
222
+ {/* EXPLORER */}
223
  {showExplorer && (
224
+ <div style={{ width: '250px', backgroundColor: '#010409', borderRight: '1px solid #30363d', display: 'flex', flexDirection: 'column', flexShrink: 0 }}>
225
  <div style={{ padding: '12px 15px', display: 'flex', justifyContent: 'space-between', alignItems: 'center', borderBottom: '1px solid #30363d', backgroundColor: '#161b22' }}>
226
  <span style={{ fontSize: '11px', fontWeight: 'bold', color: '#8b949e', letterSpacing: '1px' }}>EXPLORER</span>
227
  <div style={{ display: 'flex', gap: '12px', color: '#c9d1d9', fontSize: '13px' }}>
228
+ <i className="fas fa-file-plus" onClick={() => setIsCreatingFile(true)} style={{ cursor: 'pointer' }} title="New File/Folder"></i>
 
229
  <i className="fas fa-upload" onClick={() => alert("Upload connected soon!")} style={{ cursor: 'pointer' }} title="Upload"></i>
230
  <i className="fas fa-download" onClick={() => alert("Download connected soon!")} style={{ cursor: 'pointer' }} title="Download"></i>
 
231
  <i className="fab fa-github" onClick={() => alert("GitHub Sync soon!")} style={{ cursor: 'pointer' }} title="GitHub"></i>
232
  </div>
233
  </div>
 
236
  {isCreatingFile && (
237
  <div style={{ padding: '5px 10px', display: 'flex', alignItems: 'center', gap: '8px', backgroundColor: '#161b22', borderRadius: '4px', marginBottom: '5px' }}>
238
  <i className="fas fa-file" style={{ color: '#8b949e' }}></i>
239
+ <input autoFocus type="text" value={newFileName} onChange={(e) => setNewFileName(e.target.value)} onKeyDown={handleCreateFile} onBlur={() => setIsCreatingFile(false)} placeholder="components/nav.js" style={{ background: 'transparent', border: 'none', color: '#fff', outline: 'none', width: '100%', fontSize: '13px' }} />
240
  </div>
241
  )}
242
 
 
247
  {renamingFile === fileName ? (
248
  <input autoFocus type="text" value={renameInput} onChange={(e) => setRenameInput(e.target.value)} onKeyDown={(e) => handleRenameFile(e, fileName)} onBlur={() => setRenamingFile(null)} style={{ background: '#161b22', border: '1px solid #58a6ff', color: '#fff', outline: 'none', width: '100%', fontSize: '13px', padding: '2px 4px' }} />
249
  ) : (
250
+ <span style={{ fontSize: '13px', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }} title={fileName}>{fileName}</span>
251
  )}
252
  </div>
253
  {renamingFile !== fileName && (
 
262
  </div>
263
  )}
264
 
265
+ {/* MIDDLE PANE */}
266
  <div style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0 }}>
267
 
268
+ <div style={{ height: '45px', backgroundColor: '#0d1117', borderBottom: '1px solid #30363d', display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '0 15px', flexShrink: 0 }}>
269
+ <div style={{ display: 'flex', alignItems: 'center', overflowX: 'auto' }}>
270
+ <div onClick={() => setShowExplorer(!showExplorer)} style={{ padding: '0 15px 0 0', cursor: 'pointer', color: showExplorer ? '#58a6ff' : '#8b949e', borderRight: '1px solid #30363d', marginRight: '10px', flexShrink: 0 }}>
271
  <i className="fas fa-bars" style={{ fontSize: '15px' }}></i>
272
  </div>
273
  {Object.keys(files).map(fileName => (
274
+ <div key={fileName} onClick={() => setActiveTab(fileName)} style={{ padding: '10px 15px', fontSize: '13px', cursor: 'pointer', backgroundColor: activeTab === fileName ? '#161b22' : 'transparent', borderTop: activeTab === fileName ? '2px solid #58a6ff' : '2px solid transparent', color: activeTab === fileName ? '#c9d1d9' : '#8b949e', display: 'flex', alignItems: 'center', gap: '8px', whiteSpace: 'nowrap' }}>
275
+ {getFileIcon(fileName)} {fileName.split('/').pop()}
276
  </div>
277
  ))}
278
  </div>
279
+ <button onClick={runLivePreview} style={{ backgroundColor: '#238636', color: '#fff', border: 'none', padding: '6px 16px', borderRadius: '6px', cursor: 'pointer', fontSize: '13px', fontWeight: 'bold', flexShrink: 0, marginLeft: '10px' }}>
280
+ <i className="fas fa-external-link-alt" style={{ marginRight: '6px' }}></i> Run
281
  </button>
282
  </div>
283
 
 
284
  <div style={{ flex: 1, backgroundColor: '#0d1117', position: 'relative', overflow: 'hidden' }}>
285
  <Editor
286
  height="100%"
 
288
  theme="vs-dark"
289
  value={files[activeTab]?.value || ''}
290
  onChange={(val) => setFiles({ ...files, [activeTab]: { ...files[activeTab], value: val } })}
291
+ options={{ minimap: { enabled: false }, fontSize: 14, automaticLayout: true, wordWrap: 'on' }}
 
 
 
 
 
 
292
  />
293
  </div>
294
 
295
+ {/* TERMINAL */}
296
  {showTerminal && (
297
+ <div style={{ height: '250px', backgroundColor: '#010409', borderTop: '1px solid #30363d', display: 'flex', flexDirection: 'column', flexShrink: 0 }}>
298
+ <div style={{ height: '35px', padding: '0 15px', fontSize: '11px', color: '#8b949e', borderBottom: '1px solid #30363d', display: 'flex', justifyContent: 'space-between', alignItems: 'center', backgroundColor: '#161b22', userSelect: 'none' }}>
299
  <div style={{ display: 'flex', gap: '15px' }}>
300
+ <span style={{ color: '#c9d1d9', fontWeight: 'bold' }}>TERMINAL</span>
301
+ <span style={{ cursor: 'pointer' }}>OUTPUT</span>
302
  </div>
 
303
  <div style={{ display: 'flex', gap: '15px', fontSize: '14px' }}>
304
+ <i className="fas fa-trash-alt" onClick={clearTerminal} style={{ cursor: 'pointer', color: '#c9d1d9' }} title="Clear"></i>
 
305
  <i className="fas fa-times" onClick={() => setShowTerminal(false)} style={{ cursor: 'pointer', color: '#c9d1d9' }} title="Close"></i>
306
  </div>
307
  </div>
308
+ <div style={{ flex: 1, minHeight: 0 }}>
309
+ <div ref={terminalRef} style={{ width: '100%', height: '100%', padding: '10px' }}></div>
 
 
 
 
310
  </div>
311
  </div>
312
  )}
313
  </div>
314
 
315
+ {/* AI PANEL */}
316
+ <div style={{ width: '320px', backgroundColor: '#010409', borderLeft: '1px solid #30363d', display: 'flex', flexDirection: 'column', flexShrink: 0 }}>
317
  <div style={{ padding: '15px', borderBottom: '1px solid #30363d', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
318
  <div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
319
  <div style={{ width: '30px', height: '30px', borderRadius: '50%', background: 'linear-gradient(45deg, #ff00cc, #3333ff)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'white', fontWeight: 'bold' }}>S</div>
320
  <span style={{ fontSize: '14px', fontWeight: 'bold', color: '#c9d1d9' }}>Shanvika AI</span>
321
  </div>
 
322
  <select value={websiteTheme} onChange={(e) => setWebsiteTheme(e.target.value)} style={{ backgroundColor: '#161b22', color: '#8b949e', border: '1px solid #30363d', padding: '4px 8px', borderRadius: '4px', outline: 'none', fontSize: '11px', cursor: 'pointer' }}>
323
  <option>Auto Theme</option>
324
  <option>Professional</option>
325
  <option>Sci-Fi</option>
326
  <option>Fantasy</option>
 
327
  </select>
328
  </div>
329
 
 
338
  </div>
339
  </div>
340
  ))}
 
341
  {aiWorkflowStatus && (
342
  <div style={{ alignSelf: 'flex-start', maxWidth: '85%' }}>
343
  <div style={{ padding: '8px 12px', borderRadius: '8px', fontSize: '12px', backgroundColor: 'rgba(88, 166, 255, 0.1)', color: '#58a6ff', border: '1px solid rgba(88, 166, 255, 0.2)', display: 'flex', alignItems: 'center', gap: '8px' }}>
 
350
 
351
  <div style={{ padding: '15px', borderTop: '1px solid #30363d', backgroundColor: '#0d1117' }}>
352
  <div style={{ position: 'relative' }}>
353
+ <textarea value={aiInput} onChange={(e) => setAiInput(e.target.value)} onKeyDown={handleAISubmit} disabled={!!aiWorkflowStatus} placeholder="Request a new site or changes..." style={{ width: '100%', height: '80px', backgroundColor: '#010409', border: '1px solid #30363d', borderRadius: '8px', color: '#c9d1d9', padding: '10px', fontSize: '13px', resize: 'none', outline: 'none', opacity: aiWorkflowStatus ? 0.5 : 1 }} />
354
  <button onClick={() => handleAISubmit({ key: 'Enter' })} disabled={!!aiWorkflowStatus || !aiInput.trim()} style={{ position: 'absolute', bottom: '10px', right: '10px', background: 'transparent', border: 'none', color: '#58a6ff', cursor: 'pointer', fontSize: '16px', opacity: (aiWorkflowStatus || !aiInput.trim()) ? 0.5 : 1 }}>
355
  <i className="fas fa-paper-plane"></i>
356
  </button>
 
360
 
361
  </div>
362
 
363
+ {/* STATUS BAR */}
364
+ <div style={{ height: '24px', backgroundColor: '#010409', borderTop: '1px solid #30363d', display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '0 15px', fontSize: '11px', color: '#c9d1d9', zIndex: 10, flexShrink: 0 }}>
365
  <div style={{ display: 'flex', gap: '20px', alignItems: 'center' }}>
366
+ <div onClick={() => setShowTerminal(!showTerminal)} style={{ cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '5px', color: showTerminal ? '#58a6ff' : '#8b949e', transition: 'color 0.2s' }} title="Toggle Terminal">
 
 
367
  <i className="fas fa-terminal"></i> Terminal
368
  </div>
 
369
  <div style={{ display: 'flex', alignItems: 'center', gap: '5px', color: saveStatus.includes('Error') ? '#f85149' : '#3fb950' }}>
370
  <i className="fas fa-cloud"></i> {saveStatus}
371
  </div>
 
374
  <div style={{ display: 'flex', gap: '15px', alignItems: 'center' }}>
375
  <select value={activeMode} onChange={(e) => setActiveMode(e.target.value)} style={{ background: 'transparent', border: 'none', color: '#8b949e', outline: 'none', fontSize: '11px', cursor: 'pointer' }}>
376
  <option>Copilot Mode</option>
377
+ <option>Agentic Mode</option>
 
378
  </select>
379
  <div><i className="fas fa-code-branch" style={{ marginRight: '5px' }}></i> main</div>
380
  <div><i className="fas fa-check-double" style={{ marginRight: '5px', color: '#3fb950' }}></i> Ethrix Prettier</div>
client/src/apiService.js CHANGED
@@ -5,28 +5,32 @@ export const api = {
5
  // 1. AI Generation (Secure Gateway)
6
  // apiService.js ke andar bas yeh function update karna hai
7
  generateCode: async (prompt, existingFiles, provider) => {
8
- // Apne Hugging Face space ka link daalna yahan
9
  const API_URL = "https://YOUR-HUGGINGFACE-SPACE-URL.hf.space";
 
10
  try {
 
11
  const response = await fetch(`${API_URL}/api/agent/generate`, {
12
  method: 'POST',
13
  headers: { 'Content-Type': 'application/json' },
14
  body: JSON.stringify({
15
  prompt: prompt,
16
- existing_files: existingFiles, // Asli Jadoo: Ab saari files context mein jayengi!
17
  model_preference: provider || "gemini"
18
  })
19
  });
 
20
  if (!response.ok) {
21
  const err = await response.json();
22
  throw new Error(err.detail || "AI Gateway Error");
23
  }
 
24
  const data = await response.json();
25
- return data.files; // Backend ab direct 'files' array bhejega
26
  } catch (error) {
27
  throw error;
28
  }
29
- }
30
 
31
  // 2. MongoDB Workspace Sync
32
  saveWorkspace: async (name, filesObject) => {
 
5
  // 1. AI Generation (Secure Gateway)
6
  // apiService.js ke andar bas yeh function update karna hai
7
  generateCode: async (prompt, existingFiles, provider) => {
8
+ // Yahan tumne jo apna Hugging Face space url dala hai, wahi rehne dena
9
  const API_URL = "https://YOUR-HUGGINGFACE-SPACE-URL.hf.space";
10
+
11
  try {
12
+ // DHYAAN DO: Naya endpoint aur naya body format
13
  const response = await fetch(`${API_URL}/api/agent/generate`, {
14
  method: 'POST',
15
  headers: { 'Content-Type': 'application/json' },
16
  body: JSON.stringify({
17
  prompt: prompt,
18
+ existing_files: existingFiles, // Ab AI ko tumhari files dikhengi!
19
  model_preference: provider || "gemini"
20
  })
21
  });
22
+
23
  if (!response.ok) {
24
  const err = await response.json();
25
  throw new Error(err.detail || "AI Gateway Error");
26
  }
27
+
28
  const data = await response.json();
29
+ return data.files; // Naya return format
30
  } catch (error) {
31
  throw error;
32
  }
33
+ },
34
 
35
  // 2. MongoDB Workspace Sync
36
  saveWorkspace: async (name, filesObject) => {