diff --git a/packages/plugins/examples/plugin-file-browser-example/src/ui/index.tsx b/packages/plugins/examples/plugin-file-browser-example/src/ui/index.tsx index 0e12d903dc..d47acc53c8 100644 --- a/packages/plugins/examples/plugin-file-browser-example/src/ui/index.tsx +++ b/packages/plugins/examples/plugin-file-browser-example/src/ui/index.tsx @@ -767,9 +767,9 @@ export function CommentFileLinks({ context }: PluginCommentAnnotationProps) { // --------------------------------------------------------------------------- /** - * Per-comment context menu item that appears in the comment "more" (⋮) menu. - * Extracts file paths from the comment body and, if any are found, renders - * a button to open the first file in the project Files tab. + * Per-comment context menu item that appears in the comment header area. + * Renders a "Files" trigger button that opens a dropdown listing all + * file paths extracted from the comment body. * * Respects the `commentAnnotationMode` instance config — hidden when mode * is `"annotation"` or `"none"`. @@ -784,6 +784,20 @@ export function CommentOpenFiles({ context }: PluginCommentContextMenuItemProps) companyId: context.companyId, }); + const [open, setOpen] = useState(false); + const containerRef = useRef(null); + + useEffect(() => { + if (!open) return; + function handleClickOutside(e: globalThis.MouseEvent) { + if (containerRef.current && !containerRef.current.contains(e.target as Node)) { + setOpen(false); + } + } + document.addEventListener("mousedown", handleClickOutside); + return () => document.removeEventListener("mousedown", handleClickOutside); + }, [open]); + if (mode === "annotation" || mode === "none") return null; if (!data?.links?.length) return null; @@ -791,25 +805,99 @@ export function CommentOpenFiles({ context }: PluginCommentContextMenuItemProps) const projectId = context.projectId; return ( -
-
- Files -
- {data.links.map((link) => { - const href = buildFileBrowserHref(prefix, projectId, link); - const fileName = link.split("/").pop() ?? link; - return ( - navigateToFileBrowser(href, e)} - className="flex w-full items-center gap-2 rounded px-2 py-1 text-xs text-foreground hover:bg-accent transition-colors" - title={`Open ${link} in file browser`} - > - {fileName} - - ); - })} +
+ + + {open && ( +
+
+ Referenced files +
+ {data.links.map((link) => { + const href = buildFileBrowserHref(prefix, projectId, link); + const fileName = link.split("/").pop() ?? link; + return ( + { navigateToFileBrowser(href, e); setOpen(false); }} + title={link} + style={{ + display: "flex", + alignItems: "center", + gap: "0.5rem", + padding: "0.375rem 0.5rem", + borderRadius: "0.375rem", + fontSize: "0.75rem", + color: "var(--foreground, #0f172a)", + textDecoration: "none", + transition: "background 0.1s", + }} + onMouseEnter={(e) => { (e.currentTarget as HTMLElement).style.background = "var(--accent, #f1f5f9)"; }} + onMouseLeave={(e) => { (e.currentTarget as HTMLElement).style.background = "transparent"; }} + > + + {fileName} + {fileName !== link && ( + + {link} + + )} + + ); + })} +
+ )}
); }