Spaces:
Paused
Paused
Update frontend/src/App.tsx
Browse files- frontend/src/App.tsx +45 -29
frontend/src/App.tsx
CHANGED
|
@@ -1,14 +1,16 @@
|
|
| 1 |
-
import React, { useState } from 'react';
|
| 2 |
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
|
| 3 |
import ChatPanel from './components/ChatPanel';
|
| 4 |
import AgentPanel from './components/AgentPanel';
|
| 5 |
import CodeEditor from './components/CodeEditor';
|
| 6 |
-
import
|
| 7 |
import FileTree from './components/FileTree';
|
| 8 |
import ModelSelector from './components/ModelSelector';
|
| 9 |
-
import StatusBar from './components/StatusBar';
|
| 10 |
import TopBar from './components/TopBar';
|
| 11 |
-
import
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
const App: React.FC = () => {
|
| 14 |
const [activeBottom, setActiveBottom] = useState('chat');
|
|
@@ -17,40 +19,59 @@ const App: React.FC = () => {
|
|
| 17 |
const [selectedModel, setSelectedModel] = useState('groq-llama-3.3-70b-versatile');
|
| 18 |
const [sidebarVisible, setSidebarVisible] = useState(true);
|
| 19 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
return (
|
| 21 |
<div style={{ display: 'flex', flexDirection: 'column', height: '100vh', background: 'var(--bg-primary)' }}>
|
| 22 |
-
{/* Top Bar */}
|
| 23 |
<TopBar />
|
| 24 |
-
|
| 25 |
<div style={{ display: 'flex', flex: 1, overflow: 'hidden' }}>
|
| 26 |
-
{/* Collapsible Sidebar */}
|
| 27 |
{sidebarVisible && (
|
| 28 |
<div style={{ width: 280, background: 'var(--bg-secondary)', borderRight: '1px solid var(--border)', display: 'flex', flexDirection: 'column' }}>
|
| 29 |
<div style={{ padding: '8px 12px', borderBottom: '1px solid var(--border)', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
| 30 |
<span style={{ fontWeight: 600, fontSize: 13 }}>EXPLORER</span>
|
| 31 |
-
<
|
| 32 |
</div>
|
| 33 |
<ModelSelector selectedModel={selectedModel} onModelChange={setSelectedModel} />
|
| 34 |
<FileTree projectPath={projectPath} onFileSelect={setActiveFile} />
|
| 35 |
</div>
|
| 36 |
)}
|
| 37 |
-
|
| 38 |
-
{/* Toggle sidebar button when collapsed */}
|
| 39 |
{!sidebarVisible && (
|
| 40 |
<div style={{ width: 40, background: 'var(--bg-secondary)', borderRight: '1px solid var(--border)', display: 'flex', alignItems: 'flex-start', paddingTop: 10, justifyContent: 'center' }}>
|
| 41 |
-
<
|
| 42 |
</div>
|
| 43 |
)}
|
| 44 |
-
|
| 45 |
-
{/* Main Editor Area */}
|
| 46 |
<PanelGroup direction="vertical" style={{ flex: 1 }}>
|
| 47 |
<Panel defaultSize={65} minSize={20}>
|
| 48 |
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
|
| 49 |
-
{/* Tab Bar */}
|
| 50 |
<div style={{ display: 'flex', background: 'var(--bg-tertiary)', borderBottom: '1px solid var(--border)', padding: '0 8px', overflowX: 'auto' }}>
|
| 51 |
{activeFile ? (
|
| 52 |
-
<div style={{ padding: '8px 12px', borderBottom: '2px solid var(--accent)', color: 'var(--accent)', fontSize: 13
|
| 53 |
-
|
| 54 |
</div>
|
| 55 |
) : (
|
| 56 |
<div style={{ padding: '8px 12px', color: 'var(--text-secondary)', fontSize: 13 }}>No file selected</div>
|
|
@@ -62,29 +83,24 @@ const App: React.FC = () => {
|
|
| 62 |
<PanelResizeHandle style={{ height: 4, background: 'var(--border)' }} />
|
| 63 |
<Panel defaultSize={35} minSize={15}>
|
| 64 |
<div style={{ height: '100%', display: 'flex', flexDirection: 'column', background: 'var(--bg-primary)' }}>
|
| 65 |
-
{/* Bottom Panel Tabs */}
|
| 66 |
<div style={{ display: 'flex', background: 'var(--bg-tertiary)', borderBottom: '1px solid var(--border)', padding: '0 8px' }}>
|
| 67 |
-
<button onClick={() => setActiveBottom('chat')} style={{ padding: '8px 16px', background: activeBottom === 'chat' ? 'var(--bg-primary)' : 'transparent', borderBottom: activeBottom === 'chat' ? '2px solid var(--accent)' : 'none',
|
| 68 |
-
|
| 69 |
-
</button>
|
| 70 |
-
<button onClick={() => setActiveBottom('
|
| 71 |
-
|
| 72 |
-
</button>
|
| 73 |
-
<button onClick={() => setActiveBottom('terminal')} style={{ padding: '8px 16px', background: activeBottom === 'terminal' ? 'var(--bg-primary)' : 'transparent', borderBottom: activeBottom === 'terminal' ? '2px solid var(--accent)' : 'none', display: 'flex', alignItems: 'center', gap: 6, color: activeBottom === 'terminal' ? 'var(--accent)' : 'var(--text-secondary)' }}>
|
| 74 |
-
<FaTerminal /> Terminal
|
| 75 |
-
</button>
|
| 76 |
</div>
|
| 77 |
<div style={{ flex: 1, overflow: 'hidden' }}>
|
| 78 |
{activeBottom === 'chat' && <ChatPanel projectPath={projectPath} model={selectedModel} />}
|
| 79 |
{activeBottom === 'agent' && <AgentPanel projectPath={projectPath} />}
|
| 80 |
-
{activeBottom === 'terminal' && <
|
|
|
|
|
|
|
| 81 |
</div>
|
| 82 |
</div>
|
| 83 |
</Panel>
|
| 84 |
</PanelGroup>
|
| 85 |
</div>
|
| 86 |
-
|
| 87 |
-
{/* Status Bar */}
|
| 88 |
<StatusBar model={selectedModel} />
|
| 89 |
</div>
|
| 90 |
);
|
|
|
|
| 1 |
+
import React, { useState, useEffect } from 'react';
|
| 2 |
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
|
| 3 |
import ChatPanel from './components/ChatPanel';
|
| 4 |
import AgentPanel from './components/AgentPanel';
|
| 5 |
import CodeEditor from './components/CodeEditor';
|
| 6 |
+
import TerminalTabs from './components/TerminalTabs';
|
| 7 |
import FileTree from './components/FileTree';
|
| 8 |
import ModelSelector from './components/ModelSelector';
|
|
|
|
| 9 |
import TopBar from './components/TopBar';
|
| 10 |
+
import StatusBar from './components/StatusBar';
|
| 11 |
+
import GitPanel from './components/GitPanel';
|
| 12 |
+
import BackgroundTasks from './components/BackgroundTasks';
|
| 13 |
+
import { FaComments, FaRobot, FaTerminal, FaCodeBranch, FaTasks } from 'react-icons/fa';
|
| 14 |
|
| 15 |
const App: React.FC = () => {
|
| 16 |
const [activeBottom, setActiveBottom] = useState('chat');
|
|
|
|
| 19 |
const [selectedModel, setSelectedModel] = useState('groq-llama-3.3-70b-versatile');
|
| 20 |
const [sidebarVisible, setSidebarVisible] = useState(true);
|
| 21 |
|
| 22 |
+
useEffect(() => {
|
| 23 |
+
const handleKeyDown = (e: KeyboardEvent) => {
|
| 24 |
+
const mod = e.ctrlKey || e.metaKey;
|
| 25 |
+
if (mod && e.key === 'b') {
|
| 26 |
+
e.preventDefault();
|
| 27 |
+
setSidebarVisible(prev => !prev);
|
| 28 |
+
} else if (mod && e.key === '1') {
|
| 29 |
+
e.preventDefault();
|
| 30 |
+
setActiveBottom('chat');
|
| 31 |
+
} else if (mod && e.key === '2') {
|
| 32 |
+
e.preventDefault();
|
| 33 |
+
setActiveBottom('agent');
|
| 34 |
+
} else if (mod && e.key === '3') {
|
| 35 |
+
e.preventDefault();
|
| 36 |
+
setActiveBottom('terminal');
|
| 37 |
+
} else if (mod && e.key === '4') {
|
| 38 |
+
e.preventDefault();
|
| 39 |
+
setActiveBottom('git');
|
| 40 |
+
} else if (mod && e.key === '5') {
|
| 41 |
+
e.preventDefault();
|
| 42 |
+
setActiveBottom('tasks');
|
| 43 |
+
}
|
| 44 |
+
};
|
| 45 |
+
window.addEventListener('keydown', handleKeyDown);
|
| 46 |
+
return () => window.removeEventListener('keydown', handleKeyDown);
|
| 47 |
+
}, []);
|
| 48 |
+
|
| 49 |
return (
|
| 50 |
<div style={{ display: 'flex', flexDirection: 'column', height: '100vh', background: 'var(--bg-primary)' }}>
|
|
|
|
| 51 |
<TopBar />
|
|
|
|
| 52 |
<div style={{ display: 'flex', flex: 1, overflow: 'hidden' }}>
|
|
|
|
| 53 |
{sidebarVisible && (
|
| 54 |
<div style={{ width: 280, background: 'var(--bg-secondary)', borderRight: '1px solid var(--border)', display: 'flex', flexDirection: 'column' }}>
|
| 55 |
<div style={{ padding: '8px 12px', borderBottom: '1px solid var(--border)', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
| 56 |
<span style={{ fontWeight: 600, fontSize: 13 }}>EXPLORER</span>
|
| 57 |
+
<span onClick={() => setSidebarVisible(false)} style={{ cursor: 'pointer', color: 'var(--text-secondary)' }}>×</span>
|
| 58 |
</div>
|
| 59 |
<ModelSelector selectedModel={selectedModel} onModelChange={setSelectedModel} />
|
| 60 |
<FileTree projectPath={projectPath} onFileSelect={setActiveFile} />
|
| 61 |
</div>
|
| 62 |
)}
|
|
|
|
|
|
|
| 63 |
{!sidebarVisible && (
|
| 64 |
<div style={{ width: 40, background: 'var(--bg-secondary)', borderRight: '1px solid var(--border)', display: 'flex', alignItems: 'flex-start', paddingTop: 10, justifyContent: 'center' }}>
|
| 65 |
+
<span onClick={() => setSidebarVisible(true)} style={{ cursor: 'pointer', color: 'var(--text-secondary)' }}>☰</span>
|
| 66 |
</div>
|
| 67 |
)}
|
|
|
|
|
|
|
| 68 |
<PanelGroup direction="vertical" style={{ flex: 1 }}>
|
| 69 |
<Panel defaultSize={65} minSize={20}>
|
| 70 |
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
|
|
|
|
| 71 |
<div style={{ display: 'flex', background: 'var(--bg-tertiary)', borderBottom: '1px solid var(--border)', padding: '0 8px', overflowX: 'auto' }}>
|
| 72 |
{activeFile ? (
|
| 73 |
+
<div style={{ padding: '8px 12px', borderBottom: '2px solid var(--accent)', color: 'var(--accent)', fontSize: 13 }}>
|
| 74 |
+
📄 {activeFile}
|
| 75 |
</div>
|
| 76 |
) : (
|
| 77 |
<div style={{ padding: '8px 12px', color: 'var(--text-secondary)', fontSize: 13 }}>No file selected</div>
|
|
|
|
| 83 |
<PanelResizeHandle style={{ height: 4, background: 'var(--border)' }} />
|
| 84 |
<Panel defaultSize={35} minSize={15}>
|
| 85 |
<div style={{ height: '100%', display: 'flex', flexDirection: 'column', background: 'var(--bg-primary)' }}>
|
|
|
|
| 86 |
<div style={{ display: 'flex', background: 'var(--bg-tertiary)', borderBottom: '1px solid var(--border)', padding: '0 8px' }}>
|
| 87 |
+
<button onClick={() => setActiveBottom('chat')} style={{ padding: '8px 16px', background: activeBottom === 'chat' ? 'var(--bg-primary)' : 'transparent', borderBottom: activeBottom === 'chat' ? '2px solid var(--accent)' : 'none', color: activeBottom === 'chat' ? 'var(--accent)' : 'var(--text-secondary)' }}><FaComments /> Chat (Ctrl+1)</button>
|
| 88 |
+
<button onClick={() => setActiveBottom('agent')} style={{ padding: '8px 16px', background: activeBottom === 'agent' ? 'var(--bg-primary)' : 'transparent', borderBottom: activeBottom === 'agent' ? '2px solid var(--accent)' : 'none', color: activeBottom === 'agent' ? 'var(--accent)' : 'var(--text-secondary)' }}><FaRobot /> Agent (Ctrl+2)</button>
|
| 89 |
+
<button onClick={() => setActiveBottom('terminal')} style={{ padding: '8px 16px', background: activeBottom === 'terminal' ? 'var(--bg-primary)' : 'transparent', borderBottom: activeBottom === 'terminal' ? '2px solid var(--accent)' : 'none', color: activeBottom === 'terminal' ? 'var(--accent)' : 'var(--text-secondary)' }}><FaTerminal /> Terminal (Ctrl+3)</button>
|
| 90 |
+
<button onClick={() => setActiveBottom('git')} style={{ padding: '8px 16px', background: activeBottom === 'git' ? 'var(--bg-primary)' : 'transparent', borderBottom: activeBottom === 'git' ? '2px solid var(--accent)' : 'none', color: activeBottom === 'git' ? 'var(--accent)' : 'var(--text-secondary)' }}><FaCodeBranch /> Git (Ctrl+4)</button>
|
| 91 |
+
<button onClick={() => setActiveBottom('tasks')} style={{ padding: '8px 16px', background: activeBottom === 'tasks' ? 'var(--bg-primary)' : 'transparent', borderBottom: activeBottom === 'tasks' ? '2px solid var(--accent)' : 'none', color: activeBottom === 'tasks' ? 'var(--accent)' : 'var(--text-secondary)' }}><FaTasks /> Tasks (Ctrl+5)</button>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
</div>
|
| 93 |
<div style={{ flex: 1, overflow: 'hidden' }}>
|
| 94 |
{activeBottom === 'chat' && <ChatPanel projectPath={projectPath} model={selectedModel} />}
|
| 95 |
{activeBottom === 'agent' && <AgentPanel projectPath={projectPath} />}
|
| 96 |
+
{activeBottom === 'terminal' && <TerminalTabs />}
|
| 97 |
+
{activeBottom === 'git' && <GitPanel projectPath={projectPath} />}
|
| 98 |
+
{activeBottom === 'tasks' && <BackgroundTasks />}
|
| 99 |
</div>
|
| 100 |
</div>
|
| 101 |
</Panel>
|
| 102 |
</PanelGroup>
|
| 103 |
</div>
|
|
|
|
|
|
|
| 104 |
<StatusBar model={selectedModel} />
|
| 105 |
</div>
|
| 106 |
);
|