## 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>
126 lines
3.9 KiB
TypeScript
126 lines
3.9 KiB
TypeScript
import React from 'react';
|
|
|
|
const WIPE_MASK =
|
|
'linear-gradient(to right, rgba(0,0,0,1) 0%, rgba(0,0,0,1) 45%, rgba(0,0,0,0) 60%, rgba(0,0,0,0) 100%)';
|
|
|
|
interface ToolRevealOnMountProps {
|
|
children: React.ReactNode;
|
|
animate: boolean;
|
|
wipe?: boolean;
|
|
delayMs?: number;
|
|
className?: string;
|
|
}
|
|
|
|
export const ToolRevealOnMount: React.FC<ToolRevealOnMountProps> = ({
|
|
children,
|
|
animate,
|
|
wipe = true,
|
|
delayMs = 0,
|
|
className,
|
|
}) => {
|
|
const rootRef = React.useRef<HTMLDivElement | null>(null);
|
|
|
|
const clearRevealStyles = React.useCallback((target: HTMLElement | null) => {
|
|
if (!target) {
|
|
return;
|
|
}
|
|
target.style.opacity = '';
|
|
// target.style.filter = '';
|
|
target.style.transform = '';
|
|
target.style.maskImage = '';
|
|
target.style.webkitMaskImage = '';
|
|
target.style.maskSize = '';
|
|
target.style.webkitMaskSize = '';
|
|
target.style.maskRepeat = '';
|
|
target.style.webkitMaskRepeat = '';
|
|
target.style.maskPosition = '';
|
|
target.style.webkitMaskPosition = '';
|
|
}, []);
|
|
|
|
React.useLayoutEffect(() => {
|
|
const el = rootRef.current;
|
|
|
|
if (!animate) {
|
|
clearRevealStyles(el);
|
|
return;
|
|
}
|
|
|
|
if (!el || typeof window === 'undefined') {
|
|
return;
|
|
}
|
|
|
|
if (window.matchMedia?.('(prefers-reduced-motion: reduce)').matches) {
|
|
clearRevealStyles(el);
|
|
return;
|
|
}
|
|
|
|
const maskSupported =
|
|
wipe &&
|
|
typeof CSS !== 'undefined' &&
|
|
(CSS.supports('mask-image', 'linear-gradient(to right, black, transparent)') ||
|
|
CSS.supports('-webkit-mask-image', 'linear-gradient(to right, black, transparent)'));
|
|
|
|
el.style.opacity = '0';
|
|
// el.style.filter = wipe ? 'blur(3px)' : 'blur(2px)';
|
|
el.style.transform = wipe ? 'translateX(-0.06em)' : 'translateY(0.04em)';
|
|
|
|
if (maskSupported) {
|
|
el.style.maskImage = WIPE_MASK;
|
|
el.style.webkitMaskImage = WIPE_MASK;
|
|
el.style.maskSize = '240% 100%';
|
|
el.style.webkitMaskSize = '240% 100%';
|
|
el.style.maskRepeat = 'no-repeat';
|
|
el.style.webkitMaskRepeat = 'no-repeat';
|
|
el.style.maskPosition = '100% 0%';
|
|
el.style.webkitMaskPosition = '100% 0%';
|
|
}
|
|
|
|
let animation: Animation | null = null;
|
|
const frame = window.requestAnimationFrame(() => {
|
|
const node = rootRef.current;
|
|
if (!node) {
|
|
return;
|
|
}
|
|
|
|
const keyframes: Keyframe[] = maskSupported
|
|
? [
|
|
{ opacity: 0, transform: 'translateX(-0.06em)', maskPosition: '100% 0%' },
|
|
{ opacity: 1, transform: 'translateX(0)', maskPosition: '0% 0%' },
|
|
]
|
|
: [
|
|
{
|
|
opacity: 0,
|
|
transform: wipe ? 'translateX(-0.06em)' : 'translateY(0.04em)',
|
|
},
|
|
{ opacity: 1, transform: wipe ? 'translateX(0)' : 'translateY(0)' },
|
|
];
|
|
|
|
animation = node.animate(keyframes, {
|
|
duration: 500,
|
|
easing: 'cubic-bezier(0.22, 1, 0.36, 1)',
|
|
delay: delayMs,
|
|
fill: 'forwards',
|
|
});
|
|
|
|
animation.finished
|
|
.catch(() => undefined)
|
|
.finally(() => {
|
|
const target = rootRef.current;
|
|
clearRevealStyles(target);
|
|
});
|
|
});
|
|
|
|
return () => {
|
|
window.cancelAnimationFrame(frame);
|
|
animation?.cancel();
|
|
clearRevealStyles(el);
|
|
};
|
|
}, [animate, clearRevealStyles, delayMs, wipe]);
|
|
|
|
return (
|
|
<div ref={rootRef} className={className}>
|
|
{children}
|
|
</div>
|
|
);
|
|
};
|