import { useCallback, useRef, type MouseEvent, type PointerEvent, type ReactNode } from "react"; import { FileText, MoreHorizontal, Pencil, AtSign } from "lucide-react"; import { Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from "@tutti-os/ui-system"; import type { AgentGuiWorkbenchSessionAction, AgentGuiWorkbenchSessionMenuCopy } from "./commands.ts"; const menuContentClassName = "w-max min-w-44 nodrag [-webkit-app-region:no-drag]"; export interface AgentGuiWorkbenchSessionMenuAdditionalAction { id: string; label: string; icon?: ReactNode; onSelect: () => void; } export interface AgentGuiWorkbenchSessionMenuProps { actions?: readonly AgentGuiWorkbenchSessionAction[]; additionalActions?: readonly AgentGuiWorkbenchSessionMenuAdditionalAction[]; copy: AgentGuiWorkbenchSessionMenuCopy; onAction: (action: AgentGuiWorkbenchSessionAction) => void; } export function AgentGuiWorkbenchSessionMenu({ actions = ["rename", "copy-markdown", "copy-reference"], additionalActions = [], copy, onAction }: AgentGuiWorkbenchSessionMenuProps): ReactNode { const showRename = actions.includes("rename"); const showCopyMarkdown = actions.includes("copy-markdown"); const showCopyReference = actions.includes("copy-reference"); const showCopyActions = showCopyMarkdown || showCopyReference; const showBuiltInActions = showRename || showCopyActions; const pendingActionRef = useRef(false); const select = useCallback((onSelect: () => void) => { if (pendingActionRef.current) { return; } pendingActionRef.current = true; // Radix renders the menu through a React portal. Portal events still // bubble through the React tree, so dispatching synchronously from // pointerup can update/unmount header chrome while its draggable region // is handling the same gesture. Let the menu gesture finish first. window.requestAnimationFrame(() => { pendingActionRef.current = false; onSelect(); }); }, []); const actionProps = useCallback( (onSelect: () => void) => ({ onClick: () => select(onSelect), onPointerUp: (event: PointerEvent) => { if (event.button === 0) { select(onSelect); } }, onSelect: () => select(onSelect) }), [select] ); return ( { if (open) { pendingActionRef.current = false; } }} > {additionalActions.map((action) => ( {action.icon} {action.label} ))} {additionalActions.length > 0 && showBuiltInActions ? ( ) : null} {showRename ? ( onAction("rename"))}> ) : null} {showRename && showCopyActions ? : null} {showCopyMarkdown ? ( onAction("copy-markdown"))}> ) : null} {showCopyReference ? ( onAction("copy-reference"))}> ) : null} ); } function stopMousePropagation(event: MouseEvent): void { event.stopPropagation(); } function stopPointerPropagation(event: PointerEvent): void { event.stopPropagation(); }