Files
openchamber/packages/ui/src/components/chat/message/parts/ToolRevealOnMount.tsx
T
Bohdan TriapitsynandIuliia Ivashko 321cc7252a 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>
2026-03-20 01:01:03 +02:00

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>
);
};