/** * Floating action menu shown near text selection in the editor. * * Positioned absolutely relative to the editor content container. * Uses onMouseDown preventDefault to avoid deselecting text in CM6. */ import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip'; import { ListTodo, MessageSquare } from 'lucide-react'; import type { EditorSelectionInfo } from '@shared/types/editor'; // ============================================================================= // Types // ============================================================================= interface EditorSelectionMenuProps { info: EditorSelectionInfo; /** Bounding rect of the editor content container (for viewport → container conversion) */ containerRect: DOMRect; onSendMessage: () => void; onCreateTask: () => void; } // ============================================================================= // Constants // ============================================================================= const MENU_GAP = 8; // px gap between selection end and menu const MENU_WIDTH = 68; // approximate menu width for clamping const MENU_HEIGHT = 32; // approximate menu height for clamping // ============================================================================= // Component // ============================================================================= export const EditorSelectionMenu = ({ info, containerRect, onSendMessage, onCreateTask, }: EditorSelectionMenuProps): React.ReactElement | null => { if (!info.text.trim()) return null; // Convert viewport coords → container-relative const rawTop = info.screenRect.top - containerRect.top; const rawLeft = info.screenRect.right - containerRect.left + MENU_GAP; // Check if selection is within visible container bounds const selTopInContainer = info.screenRect.top - containerRect.top; const selBottomInContainer = info.screenRect.bottom - containerRect.top; if (selBottomInContainer < 0 || selTopInContainer > containerRect.height) { return null; // selection is scrolled out of view } // Clamp to container bounds const top = Math.max(0, Math.min(rawTop, containerRect.height - MENU_HEIGHT)); const left = rawLeft + MENU_WIDTH > containerRect.width ? info.screenRect.right - containerRect.left - MENU_WIDTH - MENU_GAP // flip to left side : rawLeft; return (