feat(frontend): improve conversation card context menu (#9917)

This commit is contained in:
llamantino 2025-07-30 17:09:56 +02:00 committed by GitHub
parent e348634dbd
commit fdf9a49e28
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 95 additions and 23 deletions

View File

@ -0,0 +1,22 @@
import { cn } from "#/utils/utils";
interface ContextMenuIconTextProps {
icon: React.ComponentType<{ className?: string }>;
text: string;
className?: string;
iconClassName?: string;
}
export function ContextMenuIconText({
icon: Icon,
text,
className,
iconClassName,
}: ContextMenuIconTextProps) {
return (
<div className={cn("flex items-center gap-3 px-1", className)}>
<Icon className={cn("w-4 h-4 shrink-0", iconClassName)} />
{text}
</div>
);
}

View File

@ -19,7 +19,7 @@ export function ContextMenuListItem({
onClick={onClick}
disabled={isDisabled}
className={cn(
"text-sm px-4 py-2 w-full text-start hover:bg-white/10 first-of-type:rounded-t-md last-of-type:rounded-b-md",
"text-sm px-4 h-10 w-full text-start hover:bg-white/10 cursor-pointer",
"disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent text-nowrap",
)}
>

View File

@ -18,7 +18,7 @@ export function ContextMenu({
<ul
data-testid={testId}
ref={ref}
className={cn("bg-tertiary rounded-md", className)}
className={cn("bg-tertiary rounded-md overflow-hidden", className)}
>
{children}
</ul>

View File

@ -1,9 +1,20 @@
import {
Trash,
Power,
Pencil,
Download,
Wallet,
Wrench,
Bot,
} from "lucide-react";
import { useTranslation } from "react-i18next";
import { useClickOutsideElement } from "#/hooks/use-click-outside-element";
import { cn } from "#/utils/utils";
import { ContextMenu } from "../context-menu/context-menu";
import { ContextMenuListItem } from "../context-menu/context-menu-list-item";
import { ContextMenuSeparator } from "../context-menu/context-menu-separator";
import { I18nKey } from "#/i18n/declaration";
import { ContextMenuIconText } from "../context-menu/context-menu-icon-text";
interface ConversationCardContextMenuProps {
onClose: () => void;
@ -31,6 +42,12 @@ export function ConversationCardContextMenu({
const { t } = useTranslation();
const ref = useClickOutsideElement<HTMLUListElement>(onClose);
const hasEdit = Boolean(onEdit);
const hasDownload = Boolean(onDownloadViaVSCode);
const hasTools = Boolean(onShowAgentTools || onShowMicroagents);
const hasInfo = Boolean(onDisplayCost);
const hasControl = Boolean(onStop || onDelete);
return (
<ContextMenu
ref={ref}
@ -41,51 +58,84 @@ export function ConversationCardContextMenu({
position === "bottom" && "top-full",
)}
>
{onDelete && (
<ContextMenuListItem testId="delete-button" onClick={onDelete}>
{t(I18nKey.BUTTON$DELETE)}
</ContextMenuListItem>
)}
{onStop && (
<ContextMenuListItem testId="stop-button" onClick={onStop}>
{t(I18nKey.BUTTON$STOP)}
</ContextMenuListItem>
)}
{onEdit && (
<ContextMenuListItem testId="edit-button" onClick={onEdit}>
{t(I18nKey.BUTTON$EDIT_TITLE)}
<ContextMenuIconText
icon={Pencil}
text={t(I18nKey.BUTTON$EDIT_TITLE)}
/>
</ContextMenuListItem>
)}
{hasEdit && (hasDownload || hasTools || hasInfo || hasControl) && (
<ContextMenuSeparator />
)}
{onDownloadViaVSCode && (
<ContextMenuListItem
testId="download-vscode-button"
onClick={onDownloadViaVSCode}
>
{t(I18nKey.BUTTON$DOWNLOAD_VIA_VSCODE)}
<ContextMenuIconText
icon={Download}
text={t(I18nKey.BUTTON$DOWNLOAD_VIA_VSCODE)}
/>
</ContextMenuListItem>
)}
{onDisplayCost && (
<ContextMenuListItem
testId="display-cost-button"
onClick={onDisplayCost}
>
{t(I18nKey.BUTTON$DISPLAY_COST)}
</ContextMenuListItem>
{hasDownload && (hasTools || hasInfo || hasControl) && (
<ContextMenuSeparator />
)}
{onShowAgentTools && (
<ContextMenuListItem
testId="show-agent-tools-button"
onClick={onShowAgentTools}
>
{t(I18nKey.BUTTON$SHOW_AGENT_TOOLS_AND_METADATA)}
<ContextMenuIconText
icon={Wrench}
text={t(I18nKey.BUTTON$SHOW_AGENT_TOOLS_AND_METADATA)}
/>
</ContextMenuListItem>
)}
{onShowMicroagents && (
<ContextMenuListItem
testId="show-microagents-button"
onClick={onShowMicroagents}
>
{t(I18nKey.CONVERSATION$SHOW_MICROAGENTS)}
<ContextMenuIconText
icon={Bot}
text={t(I18nKey.CONVERSATION$SHOW_MICROAGENTS)}
/>
</ContextMenuListItem>
)}
{hasTools && (hasInfo || hasControl) && <ContextMenuSeparator />}
{onDisplayCost && (
<ContextMenuListItem
testId="display-cost-button"
onClick={onDisplayCost}
>
<ContextMenuIconText
icon={Wallet}
text={t(I18nKey.BUTTON$DISPLAY_COST)}
/>
</ContextMenuListItem>
)}
{hasInfo && hasControl && <ContextMenuSeparator />}
{onStop && (
<ContextMenuListItem testId="stop-button" onClick={onStop}>
<ContextMenuIconText icon={Power} text={t(I18nKey.BUTTON$STOP)} />
</ContextMenuListItem>
)}
{onDelete && (
<ContextMenuListItem testId="delete-button" onClick={onDelete}>
<ContextMenuIconText icon={Trash} text={t(I18nKey.BUTTON$DELETE)} />
</ContextMenuListItem>
)}
</ContextMenu>