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:
committed by
GitHub
co-authored by
Iuliia Ivashko
parent
359879153a
commit
321cc7252a
@@ -318,6 +318,33 @@ const withShellBridgeDetails = (message: ChatMessageEntry, details: ShellBridgeD
|
||||
};
|
||||
};
|
||||
|
||||
const normalizedMessageBySource = new WeakMap<ChatMessageEntry, ChatMessageEntry>();
|
||||
|
||||
const getNormalizedMessageForDisplay = (message: ChatMessageEntry): ChatMessageEntry => {
|
||||
const cached = normalizedMessageBySource.get(message);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const filteredParts = filterSyntheticParts(message.parts);
|
||||
const normalized = filteredParts === message.parts
|
||||
? message
|
||||
: {
|
||||
...message,
|
||||
parts: filteredParts,
|
||||
};
|
||||
|
||||
normalizedMessageBySource.set(message, normalized);
|
||||
return normalized;
|
||||
};
|
||||
|
||||
const isAssistantTextOnlyMessage = (message: ChatMessageEntry): boolean => {
|
||||
if (resolveMessageRole(message) !== 'assistant') {
|
||||
return false;
|
||||
}
|
||||
return message.parts.length > 0 && message.parts.every((part) => part?.type === 'text');
|
||||
};
|
||||
|
||||
interface MessageListProps {
|
||||
sessionKey: string;
|
||||
turnStart: number;
|
||||
@@ -813,6 +840,11 @@ const MessageList = React.forwardRef<MessageListHandle, MessageListProps>(({
|
||||
previousOrder: string[];
|
||||
animatedIds: Set<string>;
|
||||
}>({ sessionKey: undefined, previousOrder: [], animatedIds: new Set() });
|
||||
const baseDisplayCacheRef = React.useRef<{
|
||||
input: ChatMessageEntry[];
|
||||
output: ChatMessageEntry[];
|
||||
outputIndexById: Map<string, number>;
|
||||
} | null>(null);
|
||||
|
||||
const stableOnMessageContentChange = useStableEvent(onMessageContentChange);
|
||||
const stableGetAnimationHandlers = useStableEvent(getAnimationHandlers);
|
||||
@@ -843,8 +875,51 @@ const MessageList = React.forwardRef<MessageListHandle, MessageListProps>(({
|
||||
|
||||
|
||||
const baseDisplayMessages = React.useMemo(() => {
|
||||
const seenIdsFromTail = new Set<string>();
|
||||
const cached = baseDisplayCacheRef.current;
|
||||
const lastMessage = messages.length > 0 ? messages[messages.length - 1] : undefined;
|
||||
const canUseTailFastPath = Boolean(lastMessage && isAssistantTextOnlyMessage(lastMessage));
|
||||
|
||||
if (cached && canUseTailFastPath && cached.input.length === messages.length && messages.length > 0) {
|
||||
let changedCount = 0;
|
||||
let changedIndex = -1;
|
||||
let idsStable = true;
|
||||
|
||||
for (let index = 0; index < messages.length; index += 1) {
|
||||
if (messages[index]?.info?.id !== cached.input[index]?.info?.id) {
|
||||
idsStable = false;
|
||||
break;
|
||||
}
|
||||
if (messages[index] !== cached.input[index]) {
|
||||
changedCount += 1;
|
||||
changedIndex = index;
|
||||
if (changedCount > 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (idsStable && changedCount === 1 && changedIndex === messages.length - 1) {
|
||||
const changedMessage = messages[changedIndex];
|
||||
const previousMessage = changedIndex > 0 ? messages[changedIndex - 1] : undefined;
|
||||
const bridgeSensitive = isUserSubtaskMessage(previousMessage) || isUserShellMarkerMessage(previousMessage);
|
||||
|
||||
if (changedMessage && isAssistantTextOnlyMessage(changedMessage) && !bridgeSensitive) {
|
||||
const outputIndex = cached.outputIndexById.get(changedMessage.info.id);
|
||||
if (outputIndex !== undefined) {
|
||||
const nextOutput = [...cached.output];
|
||||
nextOutput[outputIndex] = getNormalizedMessageForDisplay(changedMessage);
|
||||
baseDisplayCacheRef.current = {
|
||||
input: messages,
|
||||
output: nextOutput,
|
||||
outputIndexById: cached.outputIndexById,
|
||||
};
|
||||
return nextOutput;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const seenIdsFromTail = new Set<string>();
|
||||
const dedupedMessages: ChatMessageEntry[] = [];
|
||||
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
||||
const message = messages[index];
|
||||
@@ -855,26 +930,13 @@ const MessageList = React.forwardRef<MessageListHandle, MessageListProps>(({
|
||||
}
|
||||
seenIdsFromTail.add(messageId);
|
||||
}
|
||||
dedupedMessages.push(message);
|
||||
dedupedMessages.push(getNormalizedMessageForDisplay(message));
|
||||
}
|
||||
dedupedMessages.reverse();
|
||||
|
||||
const normalizedMessages = dedupedMessages
|
||||
.map((message) => {
|
||||
const filteredParts = filterSyntheticParts(message.parts);
|
||||
const normalized = filteredParts === message.parts
|
||||
? message
|
||||
: {
|
||||
...message,
|
||||
parts: filteredParts,
|
||||
};
|
||||
return normalized;
|
||||
});
|
||||
|
||||
const output: ChatMessageEntry[] = [];
|
||||
|
||||
for (let index = 0; index < normalizedMessages.length; index += 1) {
|
||||
const current = normalizedMessages[index];
|
||||
for (let index = 0; index < dedupedMessages.length; index += 1) {
|
||||
const current = dedupedMessages[index];
|
||||
const previous = output.length > 0 ? output[output.length - 1] : undefined;
|
||||
|
||||
if (isUserSubtaskMessage(previous)) {
|
||||
@@ -896,6 +958,19 @@ const MessageList = React.forwardRef<MessageListHandle, MessageListProps>(({
|
||||
output.push(current);
|
||||
}
|
||||
|
||||
const outputIndexById = new Map<string, number>();
|
||||
output.forEach((message, index) => {
|
||||
const id = message.info?.id;
|
||||
if (typeof id === 'string' && id.length > 0) {
|
||||
outputIndexById.set(id, index);
|
||||
}
|
||||
});
|
||||
baseDisplayCacheRef.current = {
|
||||
input: messages,
|
||||
output,
|
||||
outputIndexById,
|
||||
};
|
||||
|
||||
return output;
|
||||
}, [messages]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user