mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-02-22 00:27:35 +00:00
Feat: Refine design language and use theme tokens across most pages.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { FolderPlus, MessageSquare, RefreshCw, Search, X } from 'lucide-react';
|
||||
import { FolderPlus, Plus, RefreshCw, Search, X, PanelLeftClose } from 'lucide-react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { Button } from '../../../ui/button';
|
||||
import { Input } from '../../../ui/input';
|
||||
@@ -33,155 +33,159 @@ export default function SidebarHeader({
|
||||
onCollapseSidebar,
|
||||
t,
|
||||
}: SidebarHeaderProps) {
|
||||
const LogoBlock = () => (
|
||||
<div className="flex items-center gap-2.5 min-w-0">
|
||||
<div className="w-7 h-7 bg-primary/90 rounded-lg flex items-center justify-center shadow-sm flex-shrink-0">
|
||||
<svg className="w-3.5 h-3.5 text-primary-foreground" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2.2} strokeLinecap="round" strokeLinejoin="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" />
|
||||
</svg>
|
||||
</div>
|
||||
<h1 className="text-sm font-semibold text-foreground tracking-tight truncate">{t('app.title')}</h1>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex-shrink-0">
|
||||
{/* Desktop header */}
|
||||
<div
|
||||
className="md:p-4 md:border-b md:border-border"
|
||||
className="hidden md:block px-3 pt-3 pb-2"
|
||||
style={isPWA && isMobile ? { paddingTop: '44px' } : {}}
|
||||
>
|
||||
<div className="hidden md:flex items-center justify-between">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
{IS_PLATFORM ? (
|
||||
<a
|
||||
href="https://cloudcli.ai/dashboard"
|
||||
className="flex items-center gap-3 hover:opacity-80 transition-opacity group"
|
||||
className="flex items-center gap-2.5 min-w-0 hover:opacity-80 transition-opacity"
|
||||
title={t('tooltips.viewEnvironments')}
|
||||
>
|
||||
<div className="w-8 h-8 bg-primary rounded-lg flex items-center justify-center shadow-sm group-hover:shadow-md transition-shadow">
|
||||
<MessageSquare className="w-4 h-4 text-primary-foreground" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-lg font-bold text-foreground">{t('app.title')}</h1>
|
||||
<p className="text-sm text-muted-foreground">{t('app.subtitle')}</p>
|
||||
</div>
|
||||
<LogoBlock />
|
||||
</a>
|
||||
) : (
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-8 h-8 bg-primary rounded-lg flex items-center justify-center shadow-sm">
|
||||
<MessageSquare className="w-4 h-4 text-primary-foreground" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-lg font-bold text-foreground">{t('app.title')}</h1>
|
||||
<p className="text-sm text-muted-foreground">{t('app.subtitle')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<LogoBlock />
|
||||
)}
|
||||
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-8 w-8 px-0 hover:bg-accent transition-colors duration-200"
|
||||
onClick={onCollapseSidebar}
|
||||
title={t('tooltips.hideSidebar')}
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="md:hidden p-3 border-b border-border"
|
||||
style={isPWA && isMobile ? { paddingTop: '16px' } : {}}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
{IS_PLATFORM ? (
|
||||
<a
|
||||
href="https://cloudcli.ai/dashboard"
|
||||
className="flex items-center gap-3 active:opacity-70 transition-opacity"
|
||||
title={t('tooltips.viewEnvironments')}
|
||||
>
|
||||
<div className="w-8 h-8 bg-primary rounded-lg flex items-center justify-center">
|
||||
<MessageSquare className="w-4 h-4 text-primary-foreground" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-lg font-semibold text-foreground">{t('app.title')}</h1>
|
||||
<p className="text-sm text-muted-foreground">{t('projects.title')}</p>
|
||||
</div>
|
||||
</a>
|
||||
) : (
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-8 h-8 bg-primary rounded-lg flex items-center justify-center">
|
||||
<MessageSquare className="w-4 h-4 text-primary-foreground" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-lg font-semibold text-foreground">{t('app.title')}</h1>
|
||||
<p className="text-sm text-muted-foreground">{t('projects.title')}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
className="w-8 h-8 rounded-md bg-background border border-border flex items-center justify-center active:scale-95 transition-all duration-150"
|
||||
onClick={onRefresh}
|
||||
disabled={isRefreshing}
|
||||
>
|
||||
<RefreshCw className={`w-4 h-4 text-foreground ${isRefreshing ? 'animate-spin' : ''}`} />
|
||||
</button>
|
||||
<button
|
||||
className="w-8 h-8 rounded-md bg-primary text-primary-foreground flex items-center justify-center active:scale-95 transition-all duration-150"
|
||||
onClick={onCreateProject}
|
||||
>
|
||||
<FolderPlus className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!isLoading && !isMobile && (
|
||||
<div className="px-3 md:px-4 py-2 border-b border-border">
|
||||
<div className="flex gap-2">
|
||||
<div className="flex items-center gap-0.5 flex-shrink-0">
|
||||
<Button
|
||||
variant="default"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="flex-1 h-8 text-xs bg-primary hover:bg-primary/90 transition-all duration-200"
|
||||
onClick={onCreateProject}
|
||||
title={t('tooltips.createProject')}
|
||||
>
|
||||
<FolderPlus className="w-3.5 h-3.5 mr-1.5" />
|
||||
{t('projects.newProject')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-8 w-8 px-0 hover:bg-accent transition-colors duration-200 group"
|
||||
className="h-7 w-7 p-0 text-muted-foreground hover:text-foreground hover:bg-accent/80 rounded-lg"
|
||||
onClick={onRefresh}
|
||||
disabled={isRefreshing}
|
||||
title={t('tooltips.refresh')}
|
||||
>
|
||||
<RefreshCw
|
||||
className={`w-3.5 h-3.5 ${
|
||||
isRefreshing ? 'animate-spin' : 'group-hover:rotate-180 transition-transform duration-300'
|
||||
isRefreshing ? 'animate-spin' : ''
|
||||
}`}
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-7 w-7 p-0 text-muted-foreground hover:text-foreground hover:bg-accent/80 rounded-lg"
|
||||
onClick={onCreateProject}
|
||||
title={t('tooltips.createProject')}
|
||||
>
|
||||
<Plus className="w-3.5 h-3.5" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-7 w-7 p-0 text-muted-foreground hover:text-foreground hover:bg-accent/80 rounded-lg"
|
||||
onClick={onCollapseSidebar}
|
||||
title={t('tooltips.hideSidebar')}
|
||||
>
|
||||
<PanelLeftClose className="w-3.5 h-3.5" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{projectsCount > 0 && !isLoading && (
|
||||
<div className="px-3 md:px-4 py-2 border-b border-border">
|
||||
<div className="relative">
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-muted-foreground" />
|
||||
{/* Search bar */}
|
||||
{projectsCount > 0 && !isLoading && (
|
||||
<div className="relative mt-2.5">
|
||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-muted-foreground/50 pointer-events-none" />
|
||||
<Input
|
||||
type="text"
|
||||
placeholder={t('projects.searchPlaceholder')}
|
||||
value={searchFilter}
|
||||
onChange={(event) => onSearchFilterChange(event.target.value)}
|
||||
className="pl-9 h-9 text-sm bg-muted/50 border-0 focus:bg-background focus:ring-1 focus:ring-primary/20"
|
||||
className="nav-search-input pl-9 pr-8 h-9 text-xs rounded-xl border-0 placeholder:text-muted-foreground/40 focus-visible:ring-0 focus-visible:ring-offset-0 transition-all duration-200"
|
||||
/>
|
||||
{searchFilter && (
|
||||
<button
|
||||
onClick={onClearSearchFilter}
|
||||
className="absolute right-2 top-1/2 transform -translate-y-1/2 p-1 hover:bg-accent rounded"
|
||||
className="absolute right-2.5 top-1/2 -translate-y-1/2 p-0.5 hover:bg-accent rounded-md"
|
||||
>
|
||||
<X className="w-3 h-3 text-muted-foreground" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Desktop divider */}
|
||||
<div className="hidden md:block nav-divider" />
|
||||
|
||||
{/* Mobile header */}
|
||||
<div
|
||||
className="md:hidden p-3 pb-2"
|
||||
style={isPWA && isMobile ? { paddingTop: '16px' } : {}}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
{IS_PLATFORM ? (
|
||||
<a
|
||||
href="https://cloudcli.ai/dashboard"
|
||||
className="flex items-center gap-2.5 active:opacity-70 transition-opacity min-w-0"
|
||||
title={t('tooltips.viewEnvironments')}
|
||||
>
|
||||
<LogoBlock />
|
||||
</a>
|
||||
) : (
|
||||
<LogoBlock />
|
||||
)}
|
||||
|
||||
<div className="flex gap-1.5 flex-shrink-0">
|
||||
<button
|
||||
className="w-8 h-8 rounded-lg bg-muted/50 flex items-center justify-center active:scale-95 transition-all"
|
||||
onClick={onRefresh}
|
||||
disabled={isRefreshing}
|
||||
>
|
||||
<RefreshCw className={`w-4 h-4 text-muted-foreground ${isRefreshing ? 'animate-spin' : ''}`} />
|
||||
</button>
|
||||
<button
|
||||
className="w-8 h-8 rounded-lg bg-primary/90 text-primary-foreground flex items-center justify-center active:scale-95 transition-all"
|
||||
onClick={onCreateProject}
|
||||
>
|
||||
<FolderPlus className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
||||
{/* Mobile search */}
|
||||
{projectsCount > 0 && !isLoading && (
|
||||
<div className="relative mt-2.5">
|
||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground/50 pointer-events-none" />
|
||||
<Input
|
||||
type="text"
|
||||
placeholder={t('projects.searchPlaceholder')}
|
||||
value={searchFilter}
|
||||
onChange={(event) => onSearchFilterChange(event.target.value)}
|
||||
className="nav-search-input pl-10 pr-9 h-10 text-sm rounded-xl border-0 placeholder:text-muted-foreground/40 focus-visible:ring-0 focus-visible:ring-offset-0 transition-all duration-200"
|
||||
/>
|
||||
{searchFilter && (
|
||||
<button
|
||||
onClick={onClearSearchFilter}
|
||||
className="absolute right-2.5 top-1/2 -translate-y-1/2 p-1 hover:bg-accent rounded-md"
|
||||
>
|
||||
<X className="w-3.5 h-3.5 text-muted-foreground" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Mobile divider */}
|
||||
<div className="md:hidden nav-divider" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user