Skip to content
File

Blob: src/client/components/editor/controllers/ai-menu.tsx

typescript64 lines
1import { FloatingPortal } from "@floating-ui/react";
2import { preserveEditorSelectionOnMouseDown, useEditorRectPopover } from "./menu/popover";
3import type { AiRewriteAction } from "@/shared/types";
4import "../styles/ai-menu.css";
5 
6interface AiRewriteOption {
7 action: AiRewriteAction;
8 label: string;
9 description: string;
10}
11 
12const AI_REWRITE_OPTIONS: AiRewriteOption[] = [
13 { action: "proofread", label: "Proofread", description: "Fix grammar and spelling" },
14 { action: "formal", label: "Formal", description: "More professional tone" },
15 { action: "casual", label: "Casual", description: "More conversational tone" },
16 { action: "simplify", label: "Simplify", description: "Make it easier to read" },
17 { action: "expand", label: "Expand", description: "Add more detail" },
18];
19 
20interface AiMenuPanelProps {
21 triggerRef: React.RefObject<HTMLButtonElement | null>;
22 onSelect: (action: AiRewriteAction) => void;
23 onClose: () => void;
24}
25 
26export function AiMenuPanel({ triggerRef, onSelect, onClose }: AiMenuPanelProps) {
27 const { floatingStyles, setFloating } = useEditorRectPopover({
28 open: true,
29 onClose,
30 contextElement: () => triggerRef.current,
31 getAnchorRect: () => triggerRef.current?.getBoundingClientRect() ?? null,
32 offset: 6,
33 });
34 
35 return (
36 <FloatingPortal>
37 <div
38 ref={setFloating}
39 className="tiptap-menu-surface tiptap-ai-menu"
40 style={{ ...floatingStyles, zIndex: 60, animation: "var(--animate-menu)" }}
41 onMouseDownCapture={(e) => preserveEditorSelectionOnMouseDown(e)}
42 role="menu"
43 >
44 {AI_REWRITE_OPTIONS.map((option) => (
45 <button
46 key={option.action}
47 type="button"
48 role="menuitem"
49 className="tiptap-ai-menu-item"
50 aria-label={option.label}
51 onMouseDown={(e) => {
52 e.preventDefault();
53 onSelect(option.action);
54 }}
55 >
56 <span className="tiptap-ai-menu-label">{option.label}</span>
57 <span className="tiptap-ai-menu-desc">{option.description}</span>
58 </button>
59 ))}
60 </div>
61 </FloatingPortal>
62 );
63}