mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
feat(frontend): improve conversation card context menu (#9917)
This commit is contained in:
parent
e348634dbd
commit
fdf9a49e28
@ -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>
|
||||
);
|
||||
}
|
||||
@ -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",
|
||||
)}
|
||||
>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user