源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
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
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
This commit is contained in:
+72
@@ -0,0 +1,72 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user