From c8dd14be235b0f881695a5cb7e7ffd4e78c65007 Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Wed, 22 Apr 2026 15:04:05 -0400 Subject: [PATCH] fix(ui): enable scroll-wheel in CommandList inside Dialog (#948) --- src/renderer/src/components/ui/command.tsx | 43 +++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/components/ui/command.tsx b/src/renderer/src/components/ui/command.tsx index 48802ef24bb..b7de6a92d0c 100644 --- a/src/renderer/src/components/ui/command.tsx +++ b/src/renderer/src/components/ui/command.tsx @@ -107,9 +107,50 @@ function CommandInput({ ) } -function CommandList({ className, ...props }: React.ComponentProps) { +function CommandList({ + className, + ref, + ...props +}: React.ComponentProps) { + const internalRef = React.useRef(null) + + // Why: Radix Dialog applies react-remove-scroll which calls preventDefault() + // on wheel events for portaled elements (e.g. Popover) outside the Dialog's + // DOM tree. The scrollbar renders (CSS overflow works) but the browser never + // scrolls because the native event is cancelled. A non-passive wheel listener + // directly on the list takes over scrolling manually so it works regardless + // of whether a scroll-lock is active. + React.useEffect(() => { + const el = internalRef.current + if (!el) { + return + } + const onWheel = (e: WheelEvent): void => { + if (el.scrollHeight <= el.clientHeight) { + return + } + e.preventDefault() + el.scrollTop += e.deltaY + } + el.addEventListener('wheel', onWheel, { passive: false }) + return () => el.removeEventListener('wheel', onWheel) + }, []) + + const mergedRef = React.useCallback( + (node: HTMLDivElement | null) => { + internalRef.current = node + if (typeof ref === 'function') { + ref(node) + } else if (ref) { + ref.current = node + } + }, + [ref] + ) + return (