fix: show tablet actions without hover

Makes hover-only controls visible on tablets
Preserves existing mobile and desktop layouts
Covers chat, session sidebar, file, tab, and picker actions
This commit is contained in:
Bohdan Triapitsyn
2026-05-03 17:19:55 +03:00
parent 43f98e0344
commit 6595322f1a
20 changed files with 164 additions and 68 deletions
@@ -82,6 +82,7 @@ type ReasoningTimelineBlockProps = {
showDuration?: boolean;
isStreaming?: boolean;
actions?: React.ReactNode;
alwaysShowActions?: boolean;
};
export const ReasoningTimelineBlock: React.FC<ReasoningTimelineBlockProps> = ({
@@ -93,6 +94,7 @@ export const ReasoningTimelineBlock: React.FC<ReasoningTimelineBlockProps> = ({
showDuration = true,
isStreaming = false,
actions,
alwaysShowActions = false,
}) => {
const [isExpanded, setIsExpanded] = React.useState(false);
@@ -126,7 +128,7 @@ export const ReasoningTimelineBlock: React.FC<ReasoningTimelineBlockProps> = ({
className={cn(
'absolute inset-0 transition-opacity',
isExpanded && 'opacity-0',
!isExpanded && 'group-hover/tool:opacity-0'
!isExpanded && (alwaysShowActions ? 'opacity-0' : 'group-hover/tool:opacity-0')
)}
>
<Icon className="h-3.5 w-3.5" />
@@ -135,7 +137,7 @@ export const ReasoningTimelineBlock: React.FC<ReasoningTimelineBlockProps> = ({
className={cn(
'absolute inset-0 transition-opacity flex items-center justify-center',
isExpanded && 'opacity-100',
!isExpanded && 'opacity-0 group-hover/tool:opacity-100'
!isExpanded && (alwaysShowActions ? 'opacity-100' : 'opacity-0 group-hover/tool:opacity-100')
)}
>
{isExpanded ? <RiArrowDownSLine className="h-3.5 w-3.5" /> : <RiArrowRightSLine className="h-3.5 w-3.5" />}
@@ -200,12 +202,14 @@ type ReasoningPartProps = {
part: Part;
onContentChange?: (reason?: ContentChangeReason) => void;
messageId: string;
alwaysShowActions?: boolean;
};
const ReasoningPart = React.memo(({
part,
onContentChange,
messageId,
alwaysShowActions = false,
}: ReasoningPartProps) => {
const chatRenderMode = useUIStore((state) => state.chatRenderMode);
const partWithText = part as PartWithText;
@@ -234,6 +238,7 @@ const ReasoningPart = React.memo(({
time={time}
showDuration={chatRenderMode !== 'sorted'}
isStreaming={isStreaming}
alwaysShowActions={alwaysShowActions}
/>
);
});
@@ -51,6 +51,7 @@ interface ToolPartProps {
onToggle: (toolId: string) => void;
syntaxTheme: { [key: string]: React.CSSProperties };
isMobile: boolean;
alwaysShowActions?: boolean;
onContentChange?: (reason?: ContentChangeReason) => void;
onShowPopup?: (content: ToolPopupContent) => void;
animateTailText?: boolean;
@@ -1804,6 +1805,7 @@ const ToolPart: React.FC<ToolPartProps> = ({
onToggle,
syntaxTheme,
isMobile,
alwaysShowActions = isMobile,
onContentChange,
onShowPopup,
animateTailText = true,
@@ -2522,7 +2524,7 @@ const ToolPart: React.FC<ToolPartProps> = ({
className={cn(
'absolute inset-0 transition-opacity',
isExpanded && 'opacity-0',
!isExpanded && 'group-hover/tool:opacity-0'
!isExpanded && (alwaysShowActions ? 'opacity-0' : 'group-hover/tool:opacity-0')
)}
style={iconStyle}
>
@@ -2533,7 +2535,7 @@ const ToolPart: React.FC<ToolPartProps> = ({
className={cn(
'absolute inset-0 transition-opacity flex items-center justify-center',
isExpanded && 'opacity-100',
!isExpanded && 'opacity-0 group-hover/tool:opacity-100'
!isExpanded && (alwaysShowActions ? 'opacity-100' : 'opacity-0 group-hover/tool:opacity-100')
)}
>
{isExpanded ? <RiArrowDownSLine className="h-3.5 w-3.5" /> : <RiArrowRightSLine className="h-3.5 w-3.5" />}
@@ -2661,6 +2663,7 @@ export default React.memo(ToolPart, (prev, next) => {
&& prev.isExpanded === next.isExpanded
&& prev.syntaxTheme === next.syntaxTheme
&& prev.isMobile === next.isMobile
&& prev.alwaysShowActions === next.alwaysShowActions
&& prev.onContentChange === next.onContentChange
&& prev.onShowPopup === next.onShowPopup
&& prev.animateTailText === next.animateTailText;