Major UI refresh: sidebar redesign, theme expansion, and chat performance optimizations (#706)

## Summary
Complete sidebar redesign and comprehensive UI polish pass with performance optimizations, theme system refinements, and desktop integration improvements.

## Key Changes

**Sidebar & Navigation Redesign**
- Redesigned sessions sidebar layout with unified button primitives
- Added activity sections with project grouping and improved session organization
- Refined sidebar corners, spacing, and visual hierarchy
- Removed NavRail component in favor of streamlined sidebar
- Stabilized sessions bar toggle position in fullscreen mode

**Performance Optimizations**
- Reduced chat streaming CPU usage and storage churn
- Optimized task tool polling and live timers with debouncing
- Prevented chat state races and reduced background request load
- Debounced draft writes and coalesced session reloads
- Optimized message store updates and turn tracking

**Theme & Visual System**
- Added theme-aware window corners (desktop) and border radius tokens
- Introduced glassmorphism effects on desktop sidebar
- Added backdrop blur to UI elements

**Chat Experience**
- Added session-based permission auto-accept toggle in chat input
- Polished permission shield UX with improved icon sizing and spacing
- Fixed chat scroll-to-bottom behavior and timeline tracking
- Enhanced tool output display with better path label detection
- Removed duplicate draft context details in chat header
- Added text selection menu to chat messages

**Git Improvements**
- Refreshed git history visual design with cleaner dividers
- Added remote removal action in sync selector
- Stabilized git polling to prevent excessive requests
- Improved tool output rendering for git operations

**Settings & Panels**
- Fixed mobile scrolling on settings pages
- Made outside-click settings close instantly
- Reduced settings load churn and CPU spikes
- Improved services dropdown layout and spacing
- Softened panel resize handles

**Desktop Integration**
- Synced macOS window theme with app theme
- Restored window dragging in sidebar header zones
- Fixed system window corners on macOS
- Improved header session metadata and action controls

**Button & Component Standardization**
- Unified button primitives across all components
- Standardized destructive action patterns
- Removed unused button variants (button-large, button-small)
- Aligned context tab close hit areas

---------

Co-authored-by: Iuliia Ivashko <yulia.ivashko@gmail.com>
This commit is contained in:
Bohdan Triapitsyn
2026-03-20 01:01:03 +02:00
committed by GitHub
co-authored by Iuliia Ivashko
parent 359879153a
commit 321cc7252a
222 changed files with 8575 additions and 5456 deletions
@@ -1,5 +1,5 @@
import React from 'react';
import { ButtonSmall } from '@/components/ui/button-small';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import { toast } from '@/components/ui';
@@ -20,7 +20,6 @@ import {
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { ButtonLarge } from '@/components/ui/button-large';
import { SkillsCatalogPage } from './catalog/SkillsCatalogPage';
import {
SKILL_LOCATION_OPTIONS,
@@ -459,9 +458,9 @@ const SkillsInstalledPage: React.FC = () => {
<h3 className="typography-ui-header font-medium text-foreground">
Supporting Files
</h3>
<ButtonSmall variant="outline" size="xs" className="!font-normal gap-1" onClick={handleAddFile}>
<Button variant="outline" size="xs" className="!font-normal gap-1" onClick={handleAddFile}>
<RiAddLine className="h-3.5 w-3.5" /> Add File
</ButtonSmall>
</Button>
</div>
<section className="px-2 pb-2 pt-0">
@@ -491,7 +490,7 @@ const SkillsInstalledPage: React.FC = () => {
pending
</span>
)}
<ButtonSmall
<Button size="sm"
variant="ghost"
className="h-5 w-5 px-0 flex-shrink-0 text-muted-foreground hover:text-[var(--status-error)] opacity-0 group-hover:opacity-100 transition-opacity"
onClick={(e) => {
@@ -500,7 +499,7 @@ const SkillsInstalledPage: React.FC = () => {
}}
>
<RiDeleteBinLine className="h-3 w-3" />
</ButtonSmall>
</Button>
</div>
))}
</div>
@@ -511,14 +510,14 @@ const SkillsInstalledPage: React.FC = () => {
{/* Save action */}
<div className="px-2 py-1">
<ButtonSmall
<Button
onClick={handleSave}
disabled={isSaving || !hasSkillChanges}
size="xs"
className="!font-normal"
>
{isSaving ? 'Saving...' : isNewSkill ? 'Create Skill' : 'Save Changes'}
</ButtonSmall>
</Button>
</div>
</div>
@@ -540,16 +539,17 @@ const SkillsInstalledPage: React.FC = () => {
</DialogDescription>
</DialogHeader>
<DialogFooter>
<ButtonLarge
<Button
size="sm"
variant="ghost"
onClick={() => setDeleteFilePath(null)}
disabled={isDeletingFile}
>
Cancel
</ButtonLarge>
<ButtonLarge onClick={handleConfirmDeleteFile} disabled={isDeletingFile} className="bg-[var(--status-error)] hover:bg-[var(--status-error)]/90 text-white border-0">
</Button>
<Button size="sm" variant="destructive" onClick={handleConfirmDeleteFile} disabled={isDeletingFile}>
Delete
</ButtonLarge>
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
@@ -603,7 +603,8 @@ const SkillsInstalledPage: React.FC = () => {
</div>
)}
<DialogFooter className="mt-4">
<ButtonLarge
<Button
size="sm"
variant="ghost"
onClick={() => {
setIsFileDialogOpen(false);
@@ -611,10 +612,10 @@ const SkillsInstalledPage: React.FC = () => {
}}
>
Cancel
</ButtonLarge>
<ButtonLarge onClick={handleSaveFile} disabled={isLoadingFile || !hasFileChanges}>
</Button>
<Button size="sm" onClick={handleSaveFile} disabled={isLoadingFile || !hasFileChanges}>
{editingFilePath ? 'Save Changes' : 'Create File'}
</ButtonLarge>
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
@@ -1,6 +1,5 @@
import React, { useMemo } from 'react';
import { ButtonSmall } from '@/components/ui/button-small';
import { ButtonLarge } from '@/components/ui/button-large';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { toast } from '@/components/ui';
import { isMobileDeviceViaCSS } from '@/lib/device';
@@ -207,13 +206,13 @@ export const SkillsSidebar: React.FC<SkillsSidebarProps> = ({ onItemSelect }) =>
<SettingsProjectSelector className="mb-3" />
<div className="flex items-center justify-between gap-2">
<span className="typography-meta text-muted-foreground">Total {skills.length}</span>
<ButtonSmall
<Button size="sm"
variant="ghost"
className="h-7 w-7 px-0 -my-1 text-muted-foreground"
onClick={handleCreateNew}
>
<RiAddLine className="h-3.5 w-3.5" />
</ButtonSmall>
</Button>
</div>
</div>
@@ -347,17 +346,18 @@ export const SkillsSidebar: React.FC<SkillsSidebarProps> = ({ onItemSelect }) =>
</DialogDescription>
</DialogHeader>
<DialogFooter>
<ButtonLarge
<Button
size="sm"
className="text-foreground hover:bg-interactive-hover hover:text-foreground"
variant="ghost"
onClick={() => setDeleteDialogSkill(null)}
disabled={isDeletePending}
className="text-foreground hover:bg-interactive-hover hover:text-foreground"
>
Cancel
</ButtonLarge>
<ButtonLarge onClick={handleConfirmDeleteSkill} disabled={isDeletePending}>
</Button>
<Button size="sm" onClick={handleConfirmDeleteSkill} disabled={isDeletePending}>
Delete
</ButtonLarge>
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
@@ -383,16 +383,17 @@ export const SkillsSidebar: React.FC<SkillsSidebarProps> = ({ onItemSelect }) =>
}}
/>
<DialogFooter>
<ButtonLarge
<Button
size="sm"
className="text-foreground hover:bg-interactive-hover hover:text-foreground"
variant="ghost"
onClick={() => setRenameDialogSkill(null)}
className="text-foreground hover:bg-interactive-hover hover:text-foreground"
>
Cancel
</ButtonLarge>
<ButtonLarge onClick={handleRenameSkill}>
</Button>
<Button size="sm" onClick={handleRenameSkill}>
Rename
</ButtonLarge>
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
@@ -461,12 +462,12 @@ const SkillListItem: React.FC<SkillListItemProps> = ({
<DropdownMenu open={isMenuOpen} onOpenChange={onMenuOpenChange}>
<DropdownMenuTrigger asChild>
<ButtonSmall
<Button size="sm"
variant="ghost"
className="h-6 w-6 px-0 flex-shrink-0 -mr-1 opacity-100 transition-opacity md:opacity-0 md:group-hover:opacity-100"
>
<RiMore2Line className="h-3.5 w-3.5" />
</ButtonSmall>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-fit min-w-20">
<DropdownMenuItem
@@ -9,7 +9,7 @@ import {
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { ButtonLarge } from '@/components/ui/button-large';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import {
Select,
@@ -311,24 +311,26 @@ export const AddCatalogDialog: React.FC<AddCatalogDialogProps> = ({ open, onOpen
</div>
<DialogFooter>
<ButtonLarge variant="ghost" onClick={() => onOpenChange(false)}>
<Button size="sm" variant="ghost" onClick={() => onOpenChange(false)}>
Cancel
</ButtonLarge>
<ButtonLarge
</Button>
<Button
size="sm"
className="gap-2"
variant="ghost"
onClick={() => void handleScan()}
disabled={isScanning || !source.trim()}
className="gap-2"
>
<RiGitRepositoryLine className="h-4 w-4" />
{isScanning ? 'Scanning...' : 'Scan'}
</ButtonLarge>
<ButtonLarge
</Button>
<Button
size="sm"
onClick={() => void handleAdd()}
disabled={!scanOk || isDuplicate || !label.trim() || !source.trim()}
>
Add catalog
</ButtonLarge>
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
@@ -8,8 +8,7 @@ import {
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { ButtonLarge } from '@/components/ui/button-large';
import { ButtonSmall } from '@/components/ui/button-small';
import { Button } from '@/components/ui/button';
import {
Select,
SelectContent,
@@ -73,8 +72,8 @@ export const InstallConflictsDialog: React.FC<InstallConflictsDialogProps> = ({
<div className="flex items-center justify-between gap-2">
<span className="typography-meta text-muted-foreground">{conflicts.length} conflict(s)</span>
<div className="flex items-center gap-2">
<ButtonSmall variant="outline" size="xs" className="!font-normal" onClick={() => setAll('skip')}>Skip all</ButtonSmall>
<ButtonSmall variant="outline" size="xs" className="!font-normal" onClick={() => setAll('overwrite')}>Overwrite all</ButtonSmall>
<Button variant="outline" size="xs" className="!font-normal" onClick={() => setAll('skip')}>Skip all</Button>
<Button variant="outline" size="xs" className="!font-normal" onClick={() => setAll('overwrite')}>Overwrite all</Button>
</div>
</div>
@@ -113,15 +112,16 @@ export const InstallConflictsDialog: React.FC<InstallConflictsDialogProps> = ({
</div>
<DialogFooter>
<ButtonLarge variant="ghost" onClick={() => onOpenChange(false)}>
<Button size="sm" variant="ghost" onClick={() => onOpenChange(false)}>
Cancel
</ButtonLarge>
<ButtonLarge
</Button>
<Button
size="sm"
onClick={() => onConfirm(decisions)}
disabled={!canConfirm}
>
Continue
</ButtonLarge>
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
@@ -10,7 +10,6 @@ import {
DialogTitle,
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { ButtonLarge } from '@/components/ui/button-large';
import { Input } from '@/components/ui/input';
import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay';
import { Checkbox } from '@/components/ui/checkbox';
@@ -487,15 +486,16 @@ export const InstallFromRepoDialog: React.FC<InstallFromRepoDialogProps> = ({ op
</div>
<DialogFooter className="flex-shrink-0">
<ButtonLarge variant="ghost" onClick={() => onOpenChange(false)}>
<Button size="sm" variant="ghost" onClick={() => onOpenChange(false)}>
Cancel
</ButtonLarge>
<ButtonLarge
</Button>
<Button
size="sm"
disabled={isInstalling || selectedDirs.length === 0 || !source.trim() || (scope === 'project' && !directoryOverride)}
onClick={() => void doInstall({})}
>
{isInstalling ? 'Installing…' : 'Install selected'}
</ButtonLarge>
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
@@ -9,7 +9,7 @@ import {
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { ButtonLarge } from '@/components/ui/button-large';
import { Button } from '@/components/ui/button';
import {
Select,
SelectContent,
@@ -231,13 +231,15 @@ export const InstallSkillDialog: React.FC<InstallSkillDialogProps> = ({ open, on
</div>
<DialogFooter>
<ButtonLarge
<Button
size="sm"
variant="ghost"
onClick={() => onOpenChange(false)}
>
Cancel
</ButtonLarge>
<ButtonLarge
</Button>
<Button
size="sm"
disabled={isInstalling || !item.installable || (scope === 'project' && !directoryOverride)}
onClick={() =>
void doInstall({
@@ -251,7 +253,7 @@ export const InstallSkillDialog: React.FC<InstallSkillDialogProps> = ({ open, on
}
>
{isInstalling ? 'Installing...' : 'Install'}
</ButtonLarge>
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
@@ -1,6 +1,6 @@
import React from 'react';
import { ButtonSmall } from '@/components/ui/button-small';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay';
import { SortableTabsStrip } from '@/components/ui/sortable-tabs-strip';
@@ -12,7 +12,6 @@ import {
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { ButtonLarge } from '@/components/ui/button-large';
import {
Select,
SelectContent,
@@ -195,7 +194,7 @@ export const SkillsCatalogPage: React.FC<SkillsCatalogPageProps> = ({ mode, onMo
</SelectContent>
</Select>
<ButtonSmall
<Button
variant="outline"
size="xs"
className="!font-normal h-6 w-6 px-0"
@@ -210,10 +209,10 @@ export const SkillsCatalogPage: React.FC<SkillsCatalogPageProps> = ({ mode, onMo
title="Refresh"
>
<RiRefreshLine className={cn("h-3.5 w-3.5", (isLoadingCatalog || isLoadingSource) && "animate-spin")} />
</ButtonSmall>
</Button>
{isCustomSource && (
<ButtonSmall
<Button
variant="ghost"
size="xs"
className="!font-normal h-6 w-6 px-0 text-[var(--status-error)] hover:text-[var(--status-error)]"
@@ -222,16 +221,16 @@ export const SkillsCatalogPage: React.FC<SkillsCatalogPageProps> = ({ mode, onMo
title="Remove Catalog"
>
<RiDeleteBinLine className="h-3.5 w-3.5" />
</ButtonSmall>
</Button>
)}
<ButtonSmall
<Button
size="xs"
className="!font-normal gap-1"
onClick={() => setAddCatalogOpen(true)}
>
<RiAddLine className="h-3.5 w-3.5" /> Add Catalog
</ButtonSmall>
</Button>
</div>
<div className="py-1.5">
@@ -328,7 +327,7 @@ export const SkillsCatalogPage: React.FC<SkillsCatalogPageProps> = ({ mode, onMo
) : null}
</div>
<ButtonSmall
<Button
variant="outline"
size="xs"
className="!font-normal shrink-0"
@@ -339,7 +338,7 @@ export const SkillsCatalogPage: React.FC<SkillsCatalogPageProps> = ({ mode, onMo
}}
>
Install
</ButtonSmall>
</Button>
</div>
</div>
);
@@ -350,7 +349,7 @@ export const SkillsCatalogPage: React.FC<SkillsCatalogPageProps> = ({ mode, onMo
{isClawdHubSource && hasMoreClawdHub && !isLoadingSource && filtered.length > 0 && (
<div className="flex justify-center mt-2 px-2">
<ButtonSmall
<Button
variant="outline"
size="xs"
className="!font-normal"
@@ -358,7 +357,7 @@ export const SkillsCatalogPage: React.FC<SkillsCatalogPageProps> = ({ mode, onMo
disabled={isLoadingMore}
>
{isLoadingMore ? 'Loading...' : 'Load More Skills'}
</ButtonSmall>
</Button>
</div>
)}
</div>
@@ -381,16 +380,17 @@ export const SkillsCatalogPage: React.FC<SkillsCatalogPageProps> = ({ mode, onMo
<DialogDescription>Are you sure you want to remove this catalog?</DialogDescription>
</DialogHeader>
<DialogFooter>
<ButtonLarge
<Button
size="sm"
variant="ghost"
onClick={() => setIsRemoveCatalogDialogOpen(false)}
disabled={isRemovingCatalog}
>
Cancel
</ButtonLarge>
<ButtonLarge className="bg-[var(--status-error)] hover:bg-[var(--status-error)]/90 text-white" onClick={() => void removeSelectedCatalog()} disabled={isRemovingCatalog}>
</Button>
<Button size="sm" variant="destructive" onClick={() => void removeSelectedCatalog()} disabled={isRemovingCatalog}>
Remove Catalog
</ButtonLarge>
</Button>
</DialogFooter>
</DialogContent>
</Dialog>