feat: add vault switcher in status bar

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
lucaronin
2026-02-17 17:45:31 +01:00
parent 2f59d1d6c8
commit 829cd2ad95
4 changed files with 133 additions and 19 deletions

View File

@@ -1,6 +1,35 @@
import { Package, GitBranch, RefreshCw, Sparkles, FileText, Bell, Settings } from 'lucide-react'
import { useState, useRef, useEffect } from 'react'
import { Package, GitBranch, RefreshCw, Sparkles, FileText, Bell, Settings, FolderOpen, Check } from 'lucide-react'
export interface VaultOption {
label: string
path: string
}
interface StatusBarProps {
noteCount: number
vaultPath: string
vaults: VaultOption[]
onSwitchVault: (path: string) => void
}
export function StatusBar({ noteCount, vaultPath, vaults, onSwitchVault }: StatusBarProps) {
const [showVaultMenu, setShowVaultMenu] = useState(false)
const menuRef = useRef<HTMLDivElement>(null)
const activeVault = vaults.find((v) => v.path === vaultPath)
useEffect(() => {
if (!showVaultMenu) return
const handleClick = (e: MouseEvent) => {
if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
setShowVaultMenu(false)
}
}
document.addEventListener('mousedown', handleClick)
return () => document.removeEventListener('mousedown', handleClick)
}, [showVaultMenu])
export function StatusBar() {
return (
<footer
style={{
@@ -18,6 +47,74 @@ export function StatusBar() {
>
{/* Left section */}
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
<div ref={menuRef} style={{ position: 'relative' }}>
<span
role="button"
onClick={() => setShowVaultMenu((v) => !v)}
style={{
display: 'flex',
alignItems: 'center',
gap: 4,
cursor: 'pointer',
padding: '2px 4px',
borderRadius: 3,
background: showVaultMenu ? 'var(--hover)' : 'transparent',
}}
title="Switch vault"
>
<FolderOpen size={13} />
{activeVault?.label ?? 'Vault'}
</span>
{showVaultMenu && (
<div
style={{
position: 'absolute',
bottom: '100%',
left: 0,
marginBottom: 4,
background: 'var(--sidebar)',
border: '1px solid var(--border)',
borderRadius: 6,
padding: 4,
minWidth: 160,
boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
zIndex: 1000,
}}
>
{vaults.map((v) => (
<div
key={v.path}
role="button"
onClick={() => {
onSwitchVault(v.path)
setShowVaultMenu(false)
}}
style={{
display: 'flex',
alignItems: 'center',
gap: 6,
padding: '4px 8px',
borderRadius: 4,
cursor: 'pointer',
background: v.path === vaultPath ? 'var(--hover)' : 'transparent',
color: v.path === vaultPath ? 'var(--foreground)' : 'var(--muted-foreground)',
fontSize: 12,
}}
onMouseEnter={(e) => {
if (v.path !== vaultPath) e.currentTarget.style.background = 'var(--hover)'
}}
onMouseLeave={(e) => {
if (v.path !== vaultPath) e.currentTarget.style.background = 'transparent'
}}
>
{v.path === vaultPath ? <Check size={12} /> : <span style={{ width: 12 }} />}
{v.label}
</div>
))}
</div>
)}
</div>
<span style={{ color: 'var(--border)' }}>|</span>
<span style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
<Package size={13} />
v0.4.2
@@ -42,7 +139,7 @@ export function StatusBar() {
</span>
<span style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
<FileText size={13} />
1,247 notes
{noteCount.toLocaleString()} notes
</span>
<span
style={{ display: 'flex', alignItems: 'center', opacity: 0.4, cursor: 'not-allowed' }}