feat: enhance UI components, update theme handling, and improve tool expansion logic for better performance
This commit is contained in:
@@ -26,6 +26,11 @@ import type { TurnGroupingContext } from './hooks/useTurnGrouping';
|
||||
|
||||
const ToolOutputDialog = React.lazy(() => import('./message/ToolOutputDialog'));
|
||||
|
||||
const DETAILED_DEFAULT_TOOLS = new Set(['task', 'edit', 'multiedit', 'write', 'bash']);
|
||||
|
||||
const isDetailedDefaultTool = (toolName: unknown): boolean =>
|
||||
typeof toolName === 'string' && DETAILED_DEFAULT_TOOLS.has(toolName.toLowerCase());
|
||||
|
||||
function useStickyDisplayValue<T>(value: T | null | undefined): T | null | undefined {
|
||||
const ref = React.useRef<{ hasValue: boolean; value: T | null | undefined }>({ hasValue: false, value: undefined as T | null | undefined });
|
||||
|
||||
@@ -124,6 +129,10 @@ const ChatMessage: React.FC<ChatMessageProps> = ({
|
||||
content: '',
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
setExpandedTools(new Set());
|
||||
}, [message.info.id, toolCallExpansion]);
|
||||
|
||||
const messageRole = React.useMemo(() => deriveMessageRole(message.info), [message.info]);
|
||||
const isUser = messageRole.isUser;
|
||||
|
||||
@@ -309,44 +318,48 @@ const ChatMessage: React.FC<ChatMessageProps> = ({
|
||||
|
||||
const effectiveExpandedTools = React.useMemo(() => {
|
||||
// 'collapsed': Activity and tools start collapsed
|
||||
// 'activity': Activity expanded, tools collapsed
|
||||
// 'detailed': Activity and tools expanded
|
||||
|
||||
// 'activity': Activity expanded, tools collapsed
|
||||
// 'detailed': Activity expanded, only key tools expanded
|
||||
|
||||
if (toolCallExpansion === 'collapsed' || toolCallExpansion === 'activity') {
|
||||
// Tools default collapsed: expandedTools contains IDs of tools that ARE expanded
|
||||
return expandedTools;
|
||||
}
|
||||
|
||||
// 'detailed': Tools default expanded
|
||||
// Collect all relevant tool IDs (from this message and the entire turn if we're rendering a progressive group)
|
||||
const allToolIds = new Set<string>();
|
||||
|
||||
// 1. Add tools from this message
|
||||
|
||||
// 'detailed': expand only allowlisted tools by default.
|
||||
// expandedTools acts as a "toggled" set (XOR with defaults).
|
||||
const defaultExpandedToolIds = new Set<string>();
|
||||
|
||||
for (const part of toolParts) {
|
||||
if (part.id) {
|
||||
allToolIds.add(part.id);
|
||||
const toolName = (part as { tool?: unknown }).tool;
|
||||
if (part.id && isDetailedDefaultTool(toolName)) {
|
||||
defaultExpandedToolIds.add(part.id);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. If we're rendering a progressive group for the turn, include all turn tools
|
||||
|
||||
if (turnGroupingContext?.isFirstAssistantInTurn) {
|
||||
for (const activity of turnGroupingContext.activityParts) {
|
||||
if (activity.kind === 'tool' && activity.part.id) {
|
||||
allToolIds.add(activity.part.id);
|
||||
if (activity.kind !== 'tool') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const toolPart = activity.part as unknown as { id?: string; tool?: unknown };
|
||||
if (toolPart.id && isDetailedDefaultTool(toolPart.tool)) {
|
||||
defaultExpandedToolIds.add(toolPart.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// expandedTools contains IDs of tools that ARE collapsed (inverted)
|
||||
// Return a set of all tool IDs EXCEPT those in expandedTools
|
||||
const effective = new Set<string>();
|
||||
for (const id of allToolIds) {
|
||||
if (!expandedTools.has(id)) {
|
||||
|
||||
const effective = new Set(defaultExpandedToolIds);
|
||||
for (const id of expandedTools) {
|
||||
if (effective.has(id)) {
|
||||
effective.delete(id);
|
||||
} else {
|
||||
effective.add(id);
|
||||
}
|
||||
}
|
||||
return effective;
|
||||
}, [toolCallExpansion, expandedTools, toolParts, turnGroupingContext]);
|
||||
}, [expandedTools, toolCallExpansion, toolParts, turnGroupingContext]);
|
||||
|
||||
const agentMention = React.useMemo(() => {
|
||||
if (!isUser) {
|
||||
|
||||
@@ -778,6 +778,7 @@ export const ModelControls: React.FC<ModelControlsProps> = ({ className }) => {
|
||||
}
|
||||
// Add to recent models on successful selection
|
||||
addRecentModel(providerId, modelId);
|
||||
setAgentMenuOpen(false);
|
||||
if (isCompact) {
|
||||
closeMobilePanel();
|
||||
}
|
||||
@@ -1160,7 +1161,7 @@ export const ModelControls: React.FC<ModelControlsProps> = ({ className }) => {
|
||||
{!mobileModelQuery && favoriteModelsList.length > 0 && (
|
||||
<div className="rounded-xl border border-border/40 bg-background/95">
|
||||
<div className="px-2 py-1.5 text-xs font-semibold text-muted-foreground uppercase tracking-wider">
|
||||
<RiStarFill className="h-3 w-3 inline-block mr-1.5 text-yellow-500" />
|
||||
<RiStarFill className="h-3 w-3 inline-block mr-1.5 text-primary" />
|
||||
Favorites
|
||||
</div>
|
||||
<div className="flex flex-col border-t border-border/30">
|
||||
@@ -1635,7 +1636,7 @@ export const ModelControls: React.FC<ModelControlsProps> = ({ className }) => {
|
||||
{favoriteModelsList.length > 0 && (
|
||||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger className="typography-meta">
|
||||
<RiStarFill className="h-3 w-3 flex-shrink-0 mr-2 text-yellow-500" />
|
||||
<RiStarFill className="h-3 w-3 flex-shrink-0 mr-2 text-primary" />
|
||||
Favorites
|
||||
</DropdownMenuSubTrigger>
|
||||
<DropdownMenuSubContent
|
||||
@@ -1668,8 +1669,7 @@ export const ModelControls: React.FC<ModelControlsProps> = ({ className }) => {
|
||||
<DropdownMenuItem
|
||||
key={`fav-${providerID}-${modelID}`}
|
||||
className="typography-meta"
|
||||
onSelect={(e) => {
|
||||
e.preventDefault();
|
||||
onSelect={() => {
|
||||
handleProviderAndModelChange(providerID, modelID);
|
||||
}}
|
||||
>
|
||||
@@ -1757,8 +1757,7 @@ export const ModelControls: React.FC<ModelControlsProps> = ({ className }) => {
|
||||
<DropdownMenuItem
|
||||
key={`recent-${providerID}-${modelID}`}
|
||||
className="typography-meta"
|
||||
onSelect={(e) => {
|
||||
e.preventDefault();
|
||||
onSelect={() => {
|
||||
handleProviderAndModelChange(providerID, modelID);
|
||||
}}
|
||||
>
|
||||
@@ -1873,8 +1872,7 @@ export const ModelControls: React.FC<ModelControlsProps> = ({ className }) => {
|
||||
<DropdownMenuItem
|
||||
key={model.id}
|
||||
className="typography-meta"
|
||||
onSelect={(e) => {
|
||||
e.preventDefault();
|
||||
onSelect={() => {
|
||||
handleProviderAndModelChange(provider.id as string, model.id as string);
|
||||
}}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user