From 3aa4907307ccd9fb65785f3157a7025df362aebc Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Sun, 15 Mar 2026 11:15:33 -0700 Subject: [PATCH 1/2] refactor: update comment file links to use dropdown for file paths --- .../src/ui/index.tsx | 132 +++++++++++++++--- 1 file changed, 110 insertions(+), 22 deletions(-) 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} + + )} + + ); + })} +
+ )}
); } From 643d869994a595627f4eed4a9c97a832db5ffcf1 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Sun, 15 Mar 2026 15:24:14 -0700 Subject: [PATCH 2/2] feat: enhance file browser component with keyboard navigation and improved styling --- .../src/ui/index.tsx | 72 +++++-------------- 1 file changed, 17 insertions(+), 55 deletions(-) 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 d47acc53c8..e75435771e 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 @@ -794,8 +794,15 @@ export function CommentOpenFiles({ context }: PluginCommentContextMenuItemProps) setOpen(false); } } + function handleKeyDown(e: globalThis.KeyboardEvent) { + if (e.key === "Escape") setOpen(false); + } document.addEventListener("mousedown", handleClickOutside); - return () => document.removeEventListener("mousedown", handleClickOutside); + document.addEventListener("keydown", handleKeyDown); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + document.removeEventListener("keydown", handleKeyDown); + }; }, [open]); if (mode === "annotation" || mode === "none") return null; @@ -805,24 +812,11 @@ export function CommentOpenFiles({ context }: PluginCommentContextMenuItemProps) const projectId = context.projectId; return ( -
+
{open && ( -
-
+
+
Referenced files
{data.links.map((link) => { @@ -869,27 +843,15 @@ export function CommentOpenFiles({ context }: PluginCommentContextMenuItemProps) href={href} onClick={(e) => { 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"; }} + className="flex items-center gap-2 px-2 py-1.5 rounded-md text-xs text-foreground no-underline transition-colors hover:bg-accent" > - {fileName} + {fileName} {fileName !== link && ( - + {link} )}