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
@@ -37,7 +37,6 @@ import { Input } from '@/components/ui/input';
import { MobileOverlayPanel } from '@/components/ui/MobileOverlayPanel';
import { ProviderLogo } from '@/components/ui/ProviderLogo';
import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay';
import { Switch } from '@/components/ui/switch';
import { TextLoop } from '@/components/ui/TextLoop';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { useIsVSCodeRuntime } from '@/hooks/useRuntimeAPIs';
@@ -350,14 +349,6 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
? (sessionSavedAgentName || stickySessionAgentName || currentAgentName)
: currentAgentName;
const sessionIdForEditMode = currentSessionId ?? '__global__';
const sessionEditMode = useContextStore((state) => {
if (!uiAgentName) {
return undefined;
}
return state.getSessionAgentEditMode(sessionIdForEditMode, uiAgentName, 'ask');
});
const setSessionAgentEditMode = useContextStore((state) => state.setSessionAgentEditMode);
const {
toggleFavoriteModel,
isFavoriteModel,
@@ -506,21 +497,6 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
return getCurrentAgent?.();
}, [agents, getCurrentAgent, uiAgentName]);
const agentEditAction = React.useMemo<PermissionAction>(() => {
if (!currentAgent) {
return 'deny';
}
return resolveWildcardPermissionAction(currentAgent.permission, 'edit') ?? 'allow';
}, [currentAgent]);
const selectionContextReady = Boolean(uiAgentName);
const approveEditsAvailable = agentEditAction === 'ask';
const approveEditsChecked = approveEditsAvailable
? sessionEditMode === 'allow' || sessionEditMode === 'full'
: agentEditAction === 'allow';
const approveEditsDisabled = !selectionContextReady || !approveEditsAvailable;
const sizeVariant: 'mobile' | 'vscode' | 'default' = isMobile ? 'mobile' : isVSCodeRuntime ? 'vscode' : 'default';
const buttonHeight = sizeVariant === 'mobile' ? 'h-9' : sizeVariant === 'vscode' ? 'h-6' : 'h-8';
const editToggleIconClass = sizeVariant === 'mobile' ? 'h-5 w-5' : sizeVariant === 'vscode' ? 'h-4 w-4' : 'h-4 w-4';
@@ -545,13 +521,6 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
return <RiQuestionLine className={combinedClassName} style={iconStyle} />;
}, [editToggleIconClass]);
const handleApproveEditsToggle = React.useCallback((checked: boolean) => {
if (!selectionContextReady || !currentAgentName || !approveEditsAvailable) {
return;
}
setSessionAgentEditMode(sessionIdForEditMode, currentAgentName, checked ? 'allow' : 'ask', 'ask');
}, [approveEditsAvailable, currentAgentName, selectionContextReady, setSessionAgentEditMode, sessionIdForEditMode]);
const currentProvider = getCurrentProvider();
const models = Array.isArray(currentProvider?.models) ? currentProvider.models : [];
@@ -1819,23 +1788,6 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
onClose={closeMobilePanel}
title="Select agent"
contentMaxHeightClassName="max-h-[min(52dvh,360px)]"
footer={(
<div className="flex items-center justify-between">
<span
className={cn(
'typography-meta font-medium',
approveEditsDisabled ? 'text-muted-foreground' : 'text-foreground'
)}
>
Auto-approve edits
</span>
<Switch
checked={approveEditsChecked}
disabled={approveEditsDisabled}
onCheckedChange={handleApproveEditsToggle}
/>
</div>
)}
>
<div className="flex flex-col gap-2">
{selectableDesktopAgents.map((agent) => {
@@ -2302,10 +2254,9 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
{/* Favorites Section */}
{filteredFavorites.length > 0 && (
<>
<div>
<DropdownMenuLabel
style={{ backgroundColor: 'var(--surface-elevated)' }}
className="typography-micro font-semibold text-muted-foreground uppercase tracking-wider flex items-center gap-2 -mx-1 px-3 py-1.5 sticky top-0 z-10 border-b border-border/30"
className="typography-micro font-semibold text-muted-foreground uppercase tracking-wider flex items-center gap-2 -mx-1 px-3 py-1.5 border-b border-border/30"
>
<RiStarFill className="h-4 w-4 text-primary" />
Favorites
@@ -2314,16 +2265,15 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
const idx = currentFlatIndex++;
return renderModelRow(model, providerID, modelID, 'fav', idx, modelSelectedIndex === idx);
})}
</>
</div>
)}
{/* Recents Section */}
{filteredRecents.length > 0 && (
<>
<div>
{filteredFavorites.length > 0 && <DropdownMenuSeparator />}
<DropdownMenuLabel
style={{ backgroundColor: 'var(--surface-elevated)' }}
className="typography-micro font-semibold text-muted-foreground uppercase tracking-wider flex items-center gap-2 -mx-1 px-3 py-1.5 sticky top-0 z-10 border-b border-border/30"
className="typography-micro font-semibold text-muted-foreground uppercase tracking-wider flex items-center gap-2 -mx-1 px-3 py-1.5 border-b border-border/30"
>
<RiTimeLine className="h-4 w-4" />
Recent
@@ -2332,7 +2282,7 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
const idx = currentFlatIndex++;
return renderModelRow(model, providerID, modelID, 'recent', idx, modelSelectedIndex === idx);
})}
</>
</div>
)}
{/* Separator before providers */}
@@ -2342,7 +2292,7 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
{/* All Providers - Flat List */}
{providerSections.map(({ provider, isExpanded, visibleModels }, index) => (
<React.Fragment key={provider.id}>
<div key={provider.id}>
{index > 0 && <DropdownMenuSeparator />}
<div
role="button"
@@ -2366,8 +2316,8 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
}
}}
className={cn(
'typography-micro font-semibold text-muted-foreground uppercase tracking-wider flex w-full items-center gap-2 -mx-1 px-3 py-1.5 sticky top-0 z-10 border-b border-border/30',
'bg-[var(--surface-elevated)] text-left transition-colors',
'typography-micro font-semibold text-muted-foreground uppercase tracking-wider flex w-full items-center gap-2 -mx-1 px-3 py-1.5 border-b border-border/30',
'text-left transition-colors',
forceExpandProviders ? 'cursor-default' : 'cursor-pointer'
)}
aria-expanded={isExpanded}
@@ -2392,7 +2342,7 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
const idx = currentFlatIndex++;
return renderModelRow(model, provider.id as string, model.id as string, 'provider', idx, modelSelectedIndex === idx);
})}
</React.Fragment>
</div>
))}
</div>
</ScrollableOverlay>
@@ -2755,24 +2705,6 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
)}
</div>
</ScrollableOverlay>
<DropdownMenuSeparator />
<div className="flex flex-col gap-1 px-1 py-0.5">
<div className="rounded-xl bg-transparent">
<div className="flex items-center justify-between px-2 py-2">
<span className={cn(
'typography-meta font-medium',
approveEditsDisabled ? 'text-muted-foreground' : 'text-foreground'
)}>
Auto-approve edits
</span>
<Switch
checked={approveEditsChecked}
disabled={approveEditsDisabled}
onCheckedChange={handleApproveEditsToggle}
/>
</div>
</div>
</div>
</DropdownMenuContent>
</DropdownMenu>
{renderAgentTooltipContent()}