diff --git a/src/components/layout/MainLayout.tsx b/src/components/layout/MainLayout.tsx
index 09c80eb7..1293a290 100644
--- a/src/components/layout/MainLayout.tsx
+++ b/src/components/layout/MainLayout.tsx
@@ -384,18 +384,85 @@ export function MainLayout() {
});
}, [fetchConfig]);
- const navItems = [
- { path: '/', label: t('nav.dashboard'), icon: sidebarIcons.dashboard },
- { path: '/config', label: t('nav.config_management'), icon: sidebarIcons.config },
- { path: '/ai-providers', label: t('nav.ai_providers'), icon: sidebarIcons.aiProviders },
- { path: '/auth-files', label: t('nav.auth_files'), icon: sidebarIcons.authFiles },
- { path: '/oauth', label: t('nav.oauth', { defaultValue: 'OAuth' }), icon: sidebarIcons.oauth },
- { path: '/quota', label: t('nav.quota_management'), icon: sidebarIcons.quota },
- ...(config?.loggingToFile
- ? [{ path: '/logs', label: t('nav.logs'), icon: sidebarIcons.logs }]
- : []),
- { path: '/system', label: t('nav.system_info'), icon: sidebarIcons.system },
+ const navGroups = [
+ {
+ id: 'operate',
+ labelKey: 'nav_groups.operate',
+ items: [
+ {
+ path: '/',
+ labelKey: 'nav.dashboard',
+ metaKey: 'nav_meta.dashboard',
+ icon: sidebarIcons.dashboard,
+ },
+ ],
+ },
+ {
+ id: 'gateway',
+ labelKey: 'nav_groups.gateway',
+ items: [
+ {
+ path: '/ai-providers',
+ labelKey: 'nav.ai_providers',
+ metaKey: 'nav_meta.ai_providers',
+ icon: sidebarIcons.aiProviders,
+ },
+ {
+ path: '/auth-files',
+ labelKey: 'nav.auth_files',
+ metaKey: 'nav_meta.auth_files',
+ icon: sidebarIcons.authFiles,
+ },
+ {
+ path: '/oauth',
+ labelKey: 'nav.oauth',
+ metaKey: 'nav_meta.oauth',
+ icon: sidebarIcons.oauth,
+ },
+ ],
+ },
+ {
+ id: 'observe',
+ labelKey: 'nav_groups.observe',
+ items: [
+ {
+ path: '/quota',
+ labelKey: 'nav.quota_management',
+ metaKey: 'nav_meta.quota_management',
+ icon: sidebarIcons.quota,
+ },
+ ...(config?.loggingToFile
+ ? [
+ {
+ path: '/logs',
+ labelKey: 'nav.logs',
+ metaKey: 'nav_meta.logs',
+ icon: sidebarIcons.logs,
+ },
+ ]
+ : []),
+ ],
+ },
+ {
+ id: 'control',
+ labelKey: 'nav_groups.control',
+ items: [
+ {
+ path: '/config',
+ labelKey: 'nav.config_management',
+ metaKey: 'nav_meta.config_management',
+ icon: sidebarIcons.config,
+ },
+ {
+ path: '/system',
+ labelKey: 'nav.system_info',
+ metaKey: 'nav_meta.system_info',
+ icon: sidebarIcons.system,
+ },
+ ],
+ },
];
+ const navItems = navGroups.flatMap((group) => group.items);
const navOrder = navItems.map((item) => item.path);
const getRouteOrder = (pathname: string) => {
const trimmedPath =
@@ -652,17 +719,32 @@ export function MainLayout() {
- {navItems.map((item) => (
-
`nav-item ${isActive ? 'active' : ''}`}
- onClick={() => setSidebarOpen(false)}
- title={showSidebarLabels ? undefined : item.label}
- >
- {item.icon}
- {showSidebarLabels && {item.label}}
-
+ {navGroups.map((group, idx) => (
+
+ {showSidebarLabels
+ ?
{t(group.labelKey)}
+ : idx > 0 &&
}
+ {group.items.map((item) => {
+ const itemLabel = t(item.labelKey);
+ return (
+
`nav-item ${isActive ? 'active' : ''}`}
+ onClick={() => setSidebarOpen(false)}
+ title={showSidebarLabels ? undefined : itemLabel}
+ >
+ {item.icon}
+ {showSidebarLabels && (
+
+ {itemLabel}
+ {t(item.metaKey)}
+
+ )}
+
+ );
+ })}
+
))}
diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json
index bb0df03b..9774c6a6 100644
--- a/src/i18n/locales/en.json
+++ b/src/i18n/locales/en.json
@@ -120,6 +120,22 @@
"logs": "Logs Viewer",
"system_info": "Management Center Info"
},
+ "nav_groups": {
+ "operate": "Operate",
+ "gateway": "Gateway",
+ "observe": "Observe",
+ "control": "Control"
+ },
+ "nav_meta": {
+ "dashboard": "Operations overview",
+ "ai_providers": "Upstream model routing",
+ "auth_files": "Auth files & credentials",
+ "oauth": "OAuth authorization",
+ "quota_management": "API keys & limits",
+ "logs": "Request tracing",
+ "config_management": "Gateway configuration",
+ "system_info": "Runtime & diagnostics"
+ },
"dashboard": {
"title": "Dashboard",
"subtitle": "Welcome to CLI Proxy API Management Center",
diff --git a/src/i18n/locales/ru.json b/src/i18n/locales/ru.json
index 2fcf340f..56390273 100644
--- a/src/i18n/locales/ru.json
+++ b/src/i18n/locales/ru.json
@@ -120,6 +120,22 @@
"logs": "Просмотр логов",
"system_info": "Информация системы"
},
+ "nav_groups": {
+ "operate": "Работа",
+ "gateway": "Шлюз",
+ "observe": "Наблюдение",
+ "control": "Управление"
+ },
+ "nav_meta": {
+ "dashboard": "Обзор работы",
+ "ai_providers": "Маршрутизация моделей",
+ "auth_files": "Файлы и учётные данные",
+ "oauth": "Авторизация OAuth",
+ "quota_management": "API ключи и лимиты",
+ "logs": "Трассировка запросов",
+ "config_management": "Базовая конфигурация шлюза",
+ "system_info": "Состояние и диагностика"
+ },
"dashboard": {
"title": "Панель управления",
"subtitle": "Добро пожаловать в Центр управления CLI Proxy API",
diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json
index 56a1331d..af945c89 100644
--- a/src/i18n/locales/zh-CN.json
+++ b/src/i18n/locales/zh-CN.json
@@ -120,6 +120,22 @@
"logs": "日志查看",
"system_info": "中心信息"
},
+ "nav_groups": {
+ "operate": "运行",
+ "gateway": "网关",
+ "observe": "观测",
+ "control": "控制"
+ },
+ "nav_meta": {
+ "dashboard": "运行总览",
+ "ai_providers": "上游模型路由",
+ "auth_files": "Auth 文件与凭证",
+ "oauth": "OAuth 授权登录",
+ "quota_management": "API Key 与限额",
+ "logs": "请求追踪与排查",
+ "config_management": "网关基础配置",
+ "system_info": "运行与诊断信息"
+ },
"dashboard": {
"title": "仪表盘",
"subtitle": "欢迎使用 CLI Proxy API 管理中心",
diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json
index dfcc07b7..2a96cccc 100644
--- a/src/i18n/locales/zh-TW.json
+++ b/src/i18n/locales/zh-TW.json
@@ -120,6 +120,22 @@
"logs": "記錄檢視",
"system_info": "中心資訊"
},
+ "nav_groups": {
+ "operate": "運行",
+ "gateway": "閘道",
+ "observe": "觀測",
+ "control": "控制"
+ },
+ "nav_meta": {
+ "dashboard": "運行總覽",
+ "ai_providers": "上游模型路由",
+ "auth_files": "Auth 檔案與憑證",
+ "oauth": "OAuth 授權登入",
+ "quota_management": "API Key 與限額",
+ "logs": "請求追蹤與排查",
+ "config_management": "閘道基礎設定",
+ "system_info": "運行與診斷資訊"
+ },
"dashboard": {
"title": "儀表板",
"subtitle": "歡迎使用 CLI Proxy API 管理中心",
diff --git a/src/styles/layout.scss b/src/styles/layout.scss
index b7c5a29f..5eba872e 100644
--- a/src/styles/layout.scss
+++ b/src/styles/layout.scss
@@ -3,7 +3,7 @@
:root {
--header-height: 80px;
--shell-gutter: 24px;
- --sidebar-panel-width: 240px;
+ --sidebar-panel-width: 256px;
--sidebar-collapsed-width: 68px;
--sidebar-active-width: var(--sidebar-panel-width);
--sidebar-gap: 32px;
@@ -428,6 +428,14 @@
justify-content: center;
padding: 10px;
}
+
+ .nav-group-label {
+ display: none;
+ }
+
+ .nav-group-divider {
+ display: block;
+ }
}
.sidebar-brand {
@@ -462,12 +470,36 @@
.nav-section {
display: flex;
flex-direction: column;
- gap: $spacing-sm;
+ gap: 14px;
flex: 1;
}
+ .nav-group {
+ display: flex;
+ flex-direction: column;
+ gap: $spacing-xs;
+ }
+
+ .nav-group-label {
+ padding: 0 12px 4px;
+ font-size: 10.5px;
+ font-weight: 700;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: color-mix(in srgb, var(--text-primary) 42%, transparent);
+ user-select: none;
+ }
+
+ .nav-group-divider {
+ display: none;
+ height: 1px;
+ width: 24px;
+ margin: 4px auto;
+ background: color-mix(in srgb, var(--border-color) 70%, transparent);
+ }
+
.nav-item {
- padding: 10px 12px;
+ padding: 9px 12px;
border-radius: 11px;
border: 1px solid transparent;
color: var(--text-primary);
@@ -509,6 +541,13 @@
}
}
+ .nav-text {
+ display: flex;
+ flex-direction: column;
+ min-width: 0;
+ line-height: 1.2;
+ }
+
.nav-label {
min-width: 0;
overflow: hidden;
@@ -516,6 +555,16 @@
white-space: nowrap;
}
+ .nav-meta {
+ margin-top: 2px;
+ font-size: 11.5px;
+ font-weight: 500;
+ color: color-mix(in srgb, var(--text-primary) 50%, transparent);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
&:hover {
background: color-mix(in srgb, var(--text-primary) 7%, transparent);
border-color: color-mix(in srgb, var(--border-color) 58%, transparent);
@@ -527,6 +576,10 @@
color-mix(in srgb, var(--bg-primary) 88%, transparent);
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--border-hover) 86%, transparent);
}
+
+ .nav-meta {
+ color: color-mix(in srgb, var(--text-primary) 62%, transparent);
+ }
}
&.active {
@@ -542,6 +595,10 @@
color-mix(in srgb, var(--primary-color) 14%, var(--bg-primary));
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--primary-color) 24%, transparent);
}
+
+ .nav-meta {
+ color: color-mix(in srgb, var(--text-primary) 68%, transparent);
+ }
}
}
}