fix: tighten collapsed sidebar layout (#834)
This commit is contained in:
@@ -22,6 +22,12 @@ const logoPath = '/logo.png';
|
||||
|
||||
const collapsedGroups = reactive<Record<string, boolean>>({});
|
||||
|
||||
type SidebarGroupKey = "Conversation" | "Agent" | "Monitoring" | "System";
|
||||
|
||||
function groupLabel(key: SidebarGroupKey) {
|
||||
return t(`sidebar.group${key}${appStore.sidebarCollapsed ? "Short" : ""}`);
|
||||
}
|
||||
|
||||
function toggleGroup(key: string) {
|
||||
collapsedGroups[key] = !collapsedGroups[key];
|
||||
}
|
||||
@@ -79,12 +85,12 @@ function openChangelog() {
|
||||
<!-- Conversation -->
|
||||
<div class="nav-group">
|
||||
<div class="nav-group-label" @click="toggleGroup('conversation')">
|
||||
<span>{{ t("sidebar.groupConversation") }}</span>
|
||||
<span>{{ groupLabel("Conversation") }}</span>
|
||||
<svg class="nav-group-arrow" :class="{ collapsed: isGroupCollapsed('conversation') }" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="6 9 12 15 18 9" />
|
||||
</svg>
|
||||
</div>
|
||||
<div v-show="!isGroupCollapsed('conversation')">
|
||||
<div v-show="!isGroupCollapsed('conversation')" class="nav-group-items">
|
||||
<button class="nav-item" :class="{ active: selectedKey === 'hermes.chat' }" @click="handleNav('hermes.chat')">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
|
||||
@@ -124,12 +130,12 @@ function openChangelog() {
|
||||
<!-- Agent -->
|
||||
<div class="nav-group">
|
||||
<div class="nav-group-label" @click="toggleGroup('agent')">
|
||||
<span>{{ t("sidebar.groupAgent") }}</span>
|
||||
<span>{{ groupLabel("Agent") }}</span>
|
||||
<svg class="nav-group-arrow" :class="{ collapsed: isGroupCollapsed('agent') }" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="6 9 12 15 18 9" />
|
||||
</svg>
|
||||
</div>
|
||||
<div v-show="!isGroupCollapsed('agent')">
|
||||
<div v-show="!isGroupCollapsed('agent')" class="nav-group-items">
|
||||
<button class="nav-item" :class="{ active: selectedKey === 'hermes.jobs' }" @click="handleNav('hermes.jobs')">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="4" width="18" height="18" rx="2" ry="2" />
|
||||
@@ -196,12 +202,12 @@ function openChangelog() {
|
||||
<!-- Monitoring -->
|
||||
<div class="nav-group">
|
||||
<div class="nav-group-label" @click="toggleGroup('monitoring')">
|
||||
<span>{{ t("sidebar.groupMonitoring") }}</span>
|
||||
<span>{{ groupLabel("Monitoring") }}</span>
|
||||
<svg class="nav-group-arrow" :class="{ collapsed: isGroupCollapsed('monitoring') }" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="6 9 12 15 18 9" />
|
||||
</svg>
|
||||
</div>
|
||||
<div v-show="!isGroupCollapsed('monitoring')">
|
||||
<div v-show="!isGroupCollapsed('monitoring')" class="nav-group-items">
|
||||
<button class="nav-item" :class="{ active: selectedKey === 'hermes.logs' }" @click="handleNav('hermes.logs')">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
||||
@@ -232,12 +238,12 @@ function openChangelog() {
|
||||
<!-- System -->
|
||||
<div class="nav-group">
|
||||
<div class="nav-group-label" @click="toggleGroup('system')">
|
||||
<span>{{ t("sidebar.groupSystem") }}</span>
|
||||
<span>{{ groupLabel("System") }}</span>
|
||||
<svg class="nav-group-arrow" :class="{ collapsed: isGroupCollapsed('system') }" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="6 9 12 15 18 9" />
|
||||
</svg>
|
||||
</div>
|
||||
<div v-show="!isGroupCollapsed('system')">
|
||||
<div v-show="!isGroupCollapsed('system')" class="nav-group-items">
|
||||
<button class="nav-item" :class="{ active: selectedKey === 'hermes.gateways' }" @click="handleNav('hermes.gateways')">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="2" y="2" width="20" height="8" rx="2" ry="2" />
|
||||
@@ -423,6 +429,12 @@ function openChangelog() {
|
||||
}
|
||||
}
|
||||
|
||||
.nav-group-items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.nav-group-label {
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
@@ -549,14 +561,20 @@ function openChangelog() {
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.version-links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
:deep(.theme-switch-container) {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.github-link,
|
||||
.website-link {
|
||||
color: $text-muted;
|
||||
@@ -576,6 +594,9 @@ function openChangelog() {
|
||||
|
||||
.version-text {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
transition: color 0.2s;
|
||||
@@ -660,7 +681,18 @@ function openChangelog() {
|
||||
}
|
||||
|
||||
.nav-group-label {
|
||||
display: none;
|
||||
justify-content: center;
|
||||
gap: 2px;
|
||||
padding: 8px 0 4px;
|
||||
letter-spacing: 0;
|
||||
|
||||
span {
|
||||
max-width: 36px;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
@@ -677,13 +709,6 @@ function openChangelog() {
|
||||
}
|
||||
}
|
||||
|
||||
// Keep group children visible — user can still see icons
|
||||
.nav-group > div {
|
||||
display: flex !important;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
// Hide selectors and footer text, keep theme switch
|
||||
:deep(.profile-selector),
|
||||
:deep(.model-selector) {
|
||||
@@ -691,6 +716,12 @@ function openChangelog() {
|
||||
}
|
||||
|
||||
.sidebar-footer {
|
||||
.logout-item {
|
||||
margin: 0;
|
||||
padding: 10px 4px;
|
||||
border-radius: $radius-sm;
|
||||
}
|
||||
|
||||
.logout-item span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -85,6 +85,13 @@ export default {
|
||||
files: 'Dateien',
|
||||
groupChat: 'Gruppenchat',
|
||||
groupConversation: 'Konversation',
|
||||
groupConversationShort: 'Konv',
|
||||
groupAgent: 'Agent',
|
||||
groupAgentShort: 'Agent',
|
||||
groupSystem: 'System',
|
||||
groupSystemShort: 'Sys',
|
||||
groupMonitoring: 'Monitoring',
|
||||
groupMonitoringShort: 'Mon',
|
||||
settings: 'Einstellungen',
|
||||
connected: 'Verbunden',
|
||||
disconnected: 'Getrennt',
|
||||
|
||||
@@ -90,10 +90,14 @@ export default {
|
||||
groupChat: 'Group Chat',
|
||||
files: 'Files',
|
||||
groupConversation: 'Conversation',
|
||||
groupConversationShort: 'Conv',
|
||||
groupPlatform: 'Platform',
|
||||
groupAgent: 'Agent',
|
||||
groupAgentShort: 'Agent',
|
||||
groupSystem: 'System',
|
||||
groupSystemShort: 'Sys',
|
||||
groupMonitoring: 'Monitoring',
|
||||
groupMonitoringShort: 'Mon',
|
||||
groupTools: 'Tools',
|
||||
settings: 'Settings',
|
||||
connected: 'Connected',
|
||||
|
||||
@@ -85,6 +85,13 @@ export default {
|
||||
files: 'Archivos',
|
||||
groupChat: 'Chat grupal',
|
||||
groupConversation: 'Conversación',
|
||||
groupConversationShort: 'Conv',
|
||||
groupAgent: 'Agente',
|
||||
groupAgentShort: 'Ag.',
|
||||
groupSystem: 'Sistema',
|
||||
groupSystemShort: 'Sist',
|
||||
groupMonitoring: 'Monitoreo',
|
||||
groupMonitoringShort: 'Mon',
|
||||
settings: 'Configuracion',
|
||||
connected: 'Conectado',
|
||||
disconnected: 'Desconectado',
|
||||
|
||||
@@ -85,6 +85,13 @@ export default {
|
||||
files: 'Fichiers',
|
||||
groupChat: 'Chat de groupe',
|
||||
groupConversation: 'Conversation',
|
||||
groupConversationShort: 'Conv',
|
||||
groupAgent: 'Agent',
|
||||
groupAgentShort: 'Agent',
|
||||
groupSystem: 'Systeme',
|
||||
groupSystemShort: 'Sys',
|
||||
groupMonitoring: 'Suivi',
|
||||
groupMonitoringShort: 'Suivi',
|
||||
settings: 'Parametres',
|
||||
connected: 'Connecte',
|
||||
disconnected: 'Deconnecte',
|
||||
|
||||
@@ -85,6 +85,13 @@ export default {
|
||||
files: 'ファイル',
|
||||
groupChat: 'グループチャット',
|
||||
groupConversation: '会話',
|
||||
groupConversationShort: '会話',
|
||||
groupAgent: 'エージェント',
|
||||
groupAgentShort: '代理',
|
||||
groupSystem: 'システム',
|
||||
groupSystemShort: 'シス',
|
||||
groupMonitoring: '監視',
|
||||
groupMonitoringShort: '監視',
|
||||
settings: '設定',
|
||||
connected: '接続済み',
|
||||
disconnected: '未接続',
|
||||
|
||||
@@ -85,6 +85,13 @@ export default {
|
||||
files: '파일',
|
||||
groupChat: '그룹 채팅',
|
||||
groupConversation: '대화',
|
||||
groupConversationShort: '대화',
|
||||
groupAgent: '에이전트',
|
||||
groupAgentShort: '에전',
|
||||
groupSystem: '시스템',
|
||||
groupSystemShort: '시스템',
|
||||
groupMonitoring: '모니터링',
|
||||
groupMonitoringShort: '모니터',
|
||||
settings: '설정',
|
||||
connected: '연결됨',
|
||||
disconnected: '연결 끊김',
|
||||
|
||||
@@ -85,6 +85,13 @@ export default {
|
||||
files: 'Arquivos',
|
||||
groupChat: 'Chat em grupo',
|
||||
groupConversation: 'Conversa',
|
||||
groupConversationShort: 'Conv',
|
||||
groupAgent: 'Agente',
|
||||
groupAgentShort: 'Ag.',
|
||||
groupSystem: 'Sistema',
|
||||
groupSystemShort: 'Sist',
|
||||
groupMonitoring: 'Monitoramento',
|
||||
groupMonitoringShort: 'Mon',
|
||||
settings: 'Configuracoes',
|
||||
connected: 'Conectado',
|
||||
disconnected: 'Desconectado',
|
||||
|
||||
@@ -90,10 +90,14 @@ export default {
|
||||
groupChat: '群聊',
|
||||
files: '檔案',
|
||||
groupConversation: '對話',
|
||||
groupConversationShort: '對話',
|
||||
groupPlatform: '平台',
|
||||
groupAgent: '代理',
|
||||
groupAgentShort: '代理',
|
||||
groupSystem: '系統',
|
||||
groupSystemShort: '系統',
|
||||
groupMonitoring: '監控',
|
||||
groupMonitoringShort: '監控',
|
||||
groupTools: '工具',
|
||||
settings: '設定',
|
||||
connected: '已連線',
|
||||
|
||||
@@ -90,10 +90,14 @@ export default {
|
||||
groupChat: '群聊',
|
||||
files: '文件',
|
||||
groupConversation: '对话',
|
||||
groupConversationShort: '对话',
|
||||
groupPlatform: '平台',
|
||||
groupAgent: '代理',
|
||||
groupAgentShort: '代理',
|
||||
groupSystem: '系统',
|
||||
groupSystemShort: '系统',
|
||||
groupMonitoring: '监控',
|
||||
groupMonitoringShort: '监控',
|
||||
groupTools: '工具',
|
||||
settings: '设置',
|
||||
connected: '已连接',
|
||||
|
||||
@@ -82,6 +82,7 @@ describe('AppSidebar search entry', () => {
|
||||
mockAppStore.updateAvailable = false
|
||||
mockAppStore.clientOutdated = false
|
||||
mockAppStore.updating = false
|
||||
mockAppStore.sidebarCollapsed = false
|
||||
mockAppStore.reloadClient.mockClear()
|
||||
})
|
||||
|
||||
@@ -127,4 +128,33 @@ describe('AppSidebar search entry', () => {
|
||||
await reloadButton!.trigger('click')
|
||||
expect(mockAppStore.reloadClient).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('uses short group labels and keeps group folding active when collapsed', async () => {
|
||||
mockAppStore.sidebarCollapsed = true
|
||||
const wrapper = mount(AppSidebar, {
|
||||
global: {
|
||||
stubs: {
|
||||
ProfileSelector: true,
|
||||
ModelSelector: true,
|
||||
LanguageSwitch: true,
|
||||
ThemeSwitch: true,
|
||||
NButton: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
expect(wrapper.classes()).toContain('collapsed')
|
||||
expect(wrapper.findAll('.nav-group-label span').map(node => node.text())).toEqual([
|
||||
'sidebar.groupConversationShort',
|
||||
'sidebar.groupAgentShort',
|
||||
'sidebar.groupMonitoringShort',
|
||||
'sidebar.groupSystemShort',
|
||||
])
|
||||
|
||||
const agentGroup = wrapper.findAll('.nav-group')[1]
|
||||
expect(agentGroup.find('.nav-group-items').attributes('style')).toBeUndefined()
|
||||
|
||||
await agentGroup.find('.nav-group-label').trigger('click')
|
||||
expect(agentGroup.find('.nav-group-items').attributes('style')).toContain('display: none')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user