Spaces:
Paused
Paused
File size: 6,030 Bytes
a5784e9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | /**
* Layout Component
* Main application layout with tabbed sidebars and error boundaries
*/
import { useState } from 'react';
import {
PanelLeft,
PanelRight,
Moon,
Sun,
Layers,
Settings,
MessageSquare,
Languages
} from 'lucide-react';
import { useTheme, useI18n, type Language } from '@/contexts';
import { ErrorBoundary } from '@/components/common/ErrorBoundary';
import { ChatPanel } from '@/components/chat/ChatPanel';
import { SettingsPanel } from '@/components/settings/SettingsPanel';
import { SettingsPage } from '@/components/settings/SettingsPage';
import { LogViewer } from '@/components/logs/LogViewer';
import styles from './Layout.module.css';
type MainView = 'chat' | 'settings';
export function Layout() {
const { theme, toggleTheme } = useTheme();
const { language, setLanguage, t } = useI18n();
const [leftSidebarOpen, setLeftSidebarOpen] = useState(true);
const [rightSidebarOpen, setRightSidebarOpen] = useState(true);
const [mainView, setMainView] = useState<MainView>('chat');
const toggleLanguage = () => {
const newLang: Language = language === 'en' ? 'zh' : 'en';
setLanguage(newLang);
};
return (
<div className={styles.layout}>
{/* Left Sidebar - Model Settings (only visible in chat mode) */}
{mainView === 'chat' && (
<aside
className={`${styles.sidebar} ${!leftSidebarOpen ? styles.collapsed : ''}`}
role="complementary"
aria-label={t.layout.modelSettings}
>
<div className={styles.sidebarHeader}>
<span className={styles.sidebarTitle}>{t.layout.modelSettings}</span>
</div>
<div className={styles.sidebarContent}>
<ErrorBoundary name={t.layout.modelSettings}>
<SettingsPanel />
</ErrorBoundary>
</div>
</aside>
)}
{/* Main Content */}
<main className={styles.main} role="main">
{/* Header */}
<header className={styles.header} role="banner">
<div className={styles.headerLeft}>
{mainView === 'chat' && (
<button
className={styles.toggleButton}
onClick={() => setLeftSidebarOpen(!leftSidebarOpen)}
aria-label={leftSidebarOpen ? t.layout.hideSettingsPanel : t.layout.showSettingsPanel}
aria-expanded={leftSidebarOpen}
>
<PanelLeft size={20} aria-hidden="true" />
</button>
)}
<div className={styles.logo}>
<Layers className={styles.logoIcon} size={24} aria-hidden="true" />
<span className={styles.logoText}>{t.layout.appTitle}</span>
</div>
</div>
<div className={styles.headerCenter}>
<div className={styles.mainTabs}>
<button
className={`${styles.mainTab} ${mainView === 'chat' ? styles.active : ''}`}
onClick={() => setMainView('chat')}
>
<MessageSquare size={16} />
{t.layout.chat}
</button>
<button
className={`${styles.mainTab} ${mainView === 'settings' ? styles.active : ''}`}
onClick={() => setMainView('settings')}
>
<Settings size={16} />
{t.layout.settings}
</button>
</div>
</div>
<div className={styles.headerRight}>
{/* Language Toggle Button */}
<button
className={styles.langToggle}
onClick={toggleLanguage}
aria-label={language === 'en' ? '切换到中文' : 'Switch to English'}
title={language === 'en' ? '切换到中文' : 'Switch to English'}
>
<Languages size={18} aria-hidden="true" />
<span className={styles.langLabel}>{language === 'en' ? 'EN' : '中'}</span>
</button>
<button
className={styles.toggleButton}
onClick={toggleTheme}
aria-label={theme === 'dark' ? t.layout.switchToLight : t.layout.switchToDark}
>
{theme === 'dark' ? <Sun size={20} aria-hidden="true" /> : <Moon size={20} aria-hidden="true" />}
</button>
{mainView === 'chat' && (
<button
className={styles.toggleButton}
onClick={() => setRightSidebarOpen(!rightSidebarOpen)}
aria-label={rightSidebarOpen ? t.layout.hideLogsPanel : t.layout.showLogsPanel}
aria-expanded={rightSidebarOpen}
>
<PanelRight size={20} aria-hidden="true" />
</button>
)}
</div>
</header>
{/* Content Area */}
<div className={styles.content}>
{mainView === 'chat' ? (
<div className={styles.chatArea}>
<ErrorBoundary name={t.layout.chat}>
<ChatPanel />
</ErrorBoundary>
</div>
) : (
<div className={styles.settingsArea}>
<ErrorBoundary name={t.layout.settings}>
<SettingsPage />
</ErrorBoundary>
</div>
)}
</div>
</main>
{/* Right Sidebar - Logs (only visible in chat mode) */}
{mainView === 'chat' && (
<aside
className={`${styles.rightSidebar} ${!rightSidebarOpen ? styles.collapsed : ''}`}
role="complementary"
aria-label={t.layout.logs}
>
<div className={styles.sidebarHeader}>
<span className={styles.sidebarTitle}>
{t.layout.logs}
</span>
</div>
<div className={styles.sidebarContent}>
<ErrorBoundary name={t.layout.logs}>
{rightSidebarOpen && <LogViewer />}
</ErrorBoundary>
</div>
</aside>
)}
</div>
);
}
|