feat: 添加侧边栏收起/展开功能并优化样式
This commit is contained in:
parent
a40f57fb22
commit
63714b6c6e
119
web/src/App.tsx
119
web/src/App.tsx
@ -1,5 +1,5 @@
|
|||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { Zap, ArrowLeft, Bot, Clock, Sun, Moon, PanelRightOpen, X, Brain, Settings as SettingsIcon, ChevronRight } from 'lucide-react'
|
import { Zap, ArrowLeft, Bot, Clock, Sun, Moon, PanelRightOpen, PanelLeftClose, PanelLeftOpen, X, Brain, Settings as SettingsIcon, ChevronRight } from 'lucide-react'
|
||||||
import { ChatContainer } from './components/Chat/ChatContainer'
|
import { ChatContainer } from './components/Chat/ChatContainer'
|
||||||
import { TopicList } from './components/Sidebar/TopicList'
|
import { TopicList } from './components/Sidebar/TopicList'
|
||||||
import { SchedulerJobList } from './components/Sidebar/SchedulerJobList'
|
import { SchedulerJobList } from './components/Sidebar/SchedulerJobList'
|
||||||
@ -120,6 +120,22 @@ function App() {
|
|||||||
|
|
||||||
const [rightPanelTab, setRightPanelTab] = useState<'memory' | 'skill'>('memory')
|
const [rightPanelTab, setRightPanelTab] = useState<'memory' | 'skill'>('memory')
|
||||||
|
|
||||||
|
const [sidebarCollapsed, setSidebarCollapsed] = useState(() => {
|
||||||
|
try {
|
||||||
|
return localStorage.getItem('picobot-sidebar-collapsed') === 'true'
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const toggleSidebar = useCallback(() => {
|
||||||
|
setSidebarCollapsed(prev => {
|
||||||
|
const next = !prev
|
||||||
|
localStorage.setItem('picobot-sidebar-collapsed', String(next))
|
||||||
|
return next
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
const [theme, setTheme] = useState<'dark' | 'light'>(() => {
|
const [theme, setTheme] = useState<'dark' | 'light'>(() => {
|
||||||
const saved = localStorage.getItem('picobot-theme')
|
const saved = localStorage.getItem('picobot-theme')
|
||||||
return saved === 'light' ? 'light' : 'dark'
|
return saved === 'light' ? 'light' : 'dark'
|
||||||
@ -562,34 +578,83 @@ function App() {
|
|||||||
|
|
||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
<div className="flex flex-1 overflow-hidden relative">
|
<div className="flex flex-1 overflow-hidden relative">
|
||||||
{/* Left Sidebar */}
|
{/* Left Sidebar — smooth width animation via will-change + overflow-hidden */}
|
||||||
<div className={`w-72 shrink-0 border-r border-[var(--border-color)] bg-[var(--bg-secondary)]/50 flex flex-col ${subAgentView || schedulerView ? 'opacity-50 pointer-events-none' : ''}`}>
|
<div
|
||||||
{/* Tab 栏 */}
|
className={`shrink-0 border-r border-[var(--border-color)] bg-[var(--bg-secondary)]/50 flex flex-col overflow-hidden ${sidebarCollapsed ? 'w-11' : 'w-72'} ${subAgentView || schedulerView ? 'opacity-50 pointer-events-none' : ''}`}
|
||||||
<div className="flex border-b border-[var(--border-color)]">
|
style={{ transition: 'width 200ms ease-out', willChange: 'width' }}
|
||||||
<button
|
>
|
||||||
onClick={() => setSidebarTab('topics')}
|
{/* Tab 栏 + collapse toggle */}
|
||||||
className={`flex-1 py-2.5 text-sm font-medium text-center transition-colors ${
|
<div className="flex border-b border-[var(--border-color)]" style={{ minWidth: sidebarCollapsed ? 0 : '288px' }}>
|
||||||
sidebarTab === 'topics'
|
{sidebarCollapsed ? (
|
||||||
? 'text-[var(--accent-cyan)] border-b-2 border-[var(--accent-cyan)]'
|
/* 收起态:紧凑竖排 tab 按钮 */
|
||||||
: 'text-[var(--text-muted)] hover:text-[var(--text-secondary)]'
|
<div className="flex flex-col w-full">
|
||||||
}`}
|
<button
|
||||||
>
|
onClick={() => { setSidebarTab('topics'); setSidebarCollapsed(false); localStorage.setItem('picobot-sidebar-collapsed', 'false'); }}
|
||||||
话题
|
className={`py-2 text-xs font-medium text-center transition-colors ${
|
||||||
</button>
|
sidebarTab === 'topics'
|
||||||
<button
|
? 'text-[var(--accent-cyan)] bg-[var(--accent-cyan)]/10'
|
||||||
onClick={() => setSidebarTab('scheduler')}
|
: 'text-[var(--text-muted)] hover:text-[var(--text-secondary)] hover:bg-[var(--overlay-hover)]'
|
||||||
className={`flex-1 py-2.5 text-sm font-medium text-center transition-colors ${
|
}`}
|
||||||
sidebarTab === 'scheduler'
|
title="话题"
|
||||||
? 'text-[var(--accent-cyan)] border-b-2 border-[var(--accent-cyan)]'
|
>
|
||||||
: 'text-[var(--text-muted)] hover:text-[var(--text-secondary)]'
|
话<br/>题
|
||||||
}`}
|
</button>
|
||||||
>
|
<button
|
||||||
定时任务
|
onClick={() => { setSidebarTab('scheduler'); setSidebarCollapsed(false); localStorage.setItem('picobot-sidebar-collapsed', 'false'); }}
|
||||||
</button>
|
className={`py-2 text-xs font-medium text-center transition-colors ${
|
||||||
|
sidebarTab === 'scheduler'
|
||||||
|
? 'text-[var(--accent-cyan)] bg-[var(--accent-cyan)]/10'
|
||||||
|
: 'text-[var(--text-muted)] hover:text-[var(--text-secondary)] hover:bg-[var(--overlay-hover)]'
|
||||||
|
}`}
|
||||||
|
title="定时任务"
|
||||||
|
>
|
||||||
|
定<br/>时
|
||||||
|
</button>
|
||||||
|
{/* 展开按钮 */}
|
||||||
|
<button
|
||||||
|
onClick={toggleSidebar}
|
||||||
|
className="flex items-center justify-center py-2 text-[var(--text-muted)] hover:text-[var(--accent-cyan)] hover:bg-[var(--overlay-hover)] transition-colors"
|
||||||
|
title="展开侧栏"
|
||||||
|
>
|
||||||
|
<PanelLeftOpen className="h-3.5 w-3.5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<button
|
||||||
|
onClick={() => setSidebarTab('topics')}
|
||||||
|
className={`flex-1 py-2.5 text-sm font-medium text-center transition-colors whitespace-nowrap ${
|
||||||
|
sidebarTab === 'topics'
|
||||||
|
? 'text-[var(--accent-cyan)] border-b-2 border-[var(--accent-cyan)]'
|
||||||
|
: 'text-[var(--text-muted)] hover:text-[var(--text-secondary)]'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
话题
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setSidebarTab('scheduler')}
|
||||||
|
className={`flex-1 py-2.5 text-sm font-medium text-center transition-colors whitespace-nowrap ${
|
||||||
|
sidebarTab === 'scheduler'
|
||||||
|
? 'text-[var(--accent-cyan)] border-b-2 border-[var(--accent-cyan)]'
|
||||||
|
: 'text-[var(--text-muted)] hover:text-[var(--text-secondary)]'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
定时任务
|
||||||
|
</button>
|
||||||
|
{/* 收起按钮 */}
|
||||||
|
<button
|
||||||
|
onClick={toggleSidebar}
|
||||||
|
className="flex items-center justify-center px-2 text-[var(--text-muted)] hover:text-[var(--accent-cyan)] hover:bg-[var(--overlay-hover)] transition-colors shrink-0"
|
||||||
|
title="收起侧栏"
|
||||||
|
>
|
||||||
|
<PanelLeftClose className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-1 overflow-hidden">
|
<div className="flex-1 overflow-hidden" style={{ minWidth: sidebarCollapsed ? 0 : '288px' }}>
|
||||||
{sidebarTab === 'topics' ? (
|
{sidebarCollapsed ? null : sidebarTab === 'topics' ? (
|
||||||
<TopicList
|
<TopicList
|
||||||
sessionId={sessionId}
|
sessionId={sessionId}
|
||||||
topics={topics}
|
topics={topics}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user