Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

73 lines
2.1 KiB
TypeScript

import {
useCallback,
useRef,
useState,
type CSSProperties,
type JSX,
type ReactNode
} from "react";
import {
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuTrigger
} from "@tutti-os/ui-system";
import { useOptionalAgentHostApi } from "../../../agentActivityHost";
import { copyImageToClipboard } from "../lib/copyImageToClipboard";
import { translate } from "../../../i18n/index";
export function ConversationImageContextMenu({
src,
children,
asChild = false,
contentStyle
}: {
src: string;
children: ReactNode;
/**
* Attach the right-click listener directly to the child element instead of a
* wrapper span. Used for the zoomed image, whose positioning the zoom library
* manages and must not be disturbed by an extra wrapper element.
*/
asChild?: boolean;
/**
* Override the menu content style. Used by the zoomed image to raise the
* menu above the zoom modal (which sits above the default popover z-index).
*/
contentStyle?: CSSProperties;
}): JSX.Element {
const agentHostApi = useOptionalAgentHostApi();
const copyStartedRef = useRef(false);
const [menuResetKey, setMenuResetKey] = useState(0);
const copyAndClose = useCallback(() => {
if (copyStartedRef.current) {
return;
}
copyStartedRef.current = true;
setMenuResetKey((key) => key + 1);
void copyImageToClipboard(src, agentHostApi?.clipboard).finally(() => {
copyStartedRef.current = false;
});
}, [agentHostApi?.clipboard, src]);
return (
<ContextMenu key={menuResetKey}>
<ContextMenuTrigger asChild={asChild}>{children}</ContextMenuTrigger>
<ContextMenuContent style={contentStyle}>
<ContextMenuItem
onClick={copyAndClose}
onPointerDown={(event) => {
if (event.button !== 0) {
return;
}
event.preventDefault();
copyAndClose();
}}
onSelect={copyAndClose}
>
{translate("agentHost.agentGui.copyImage")}
</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>
);
}