| ... | ... | @@ -2223,10 +2223,18 @@ export default function Sidebar() { |
| 2223 | 2223 | }, |
| 2224 | 2224 | [isMobile, router, setOpenMobile], |
| 2225 | 2225 | ); |
| 2226 | // Safari can send a click after Ctrl+click opens settings. Ignore that one |
| 2227 | // selection, then clear the guard when the picker opens again. |
| 2228 | const suppressNextScopeChangeRef = useRef(false); |
| 2229 | const highlightedProjectScopeKeyRef = useRef<string | null>(null); |
| 2226 | 2230 | const handleProjectSettings = useCallback( |
| 2227 | | (event: ReactMouseEvent<HTMLButtonElement>, projectGroup: SidebarProjectSnapshot) => { |
| 2231 | ( |
| 2232 | event: ReactMouseEvent<HTMLElement> | ReactKeyboardEvent<HTMLInputElement>, |
| 2233 | projectGroup: SidebarProjectSnapshot, |
| 2234 | ) => { |
| 2228 | 2235 | event.preventDefault(); |
| 2229 | 2236 | event.stopPropagation(); |
| 2237 | suppressNextScopeChangeRef.current = true; |
| 2230 | 2238 | dispatchProjectScopeMenu({ type: "project-settings-opened" }); |
| 2231 | 2239 | openProjectSettings(projectGroup); |
| 2232 | 2240 | }, |
| ... | ... | @@ -3724,10 +3732,18 @@ export default function Sidebar() { |
| 3724 | 3732 | isItemEqualToValue={(a, b) => a.value === b.value} |
| 3725 | 3733 | open={projectScopeMenuState.open} |
| 3726 | 3734 | onOpenChange={(open) => { |
| 3735 | if (open) suppressNextScopeChangeRef.current = false; |
| 3727 | 3736 | dispatchProjectScopeMenu({ type: "open-changed", open }); |
| 3728 | 3737 | }} |
| 3738 | onItemHighlighted={(item) => { |
| 3739 | highlightedProjectScopeKeyRef.current = item?.value ?? null; |
| 3740 | }} |
| 3729 | 3741 | value={selectedProjectScopeItem} |
| 3730 | 3742 | onValueChange={(item) => { |
| 3743 | if (suppressNextScopeChangeRef.current) { |
| 3744 | suppressNextScopeChangeRef.current = false; |
| 3745 | return; |
| 3746 | } |
| 3731 | 3747 | if (!item) return; |
| 3732 | 3748 | setProjectScopeKey(item.value === "all" ? null : item.value); |
| 3733 | 3749 | }} |
| ... | ... | @@ -3778,6 +3794,24 @@ export default function Sidebar() { |
| 3778 | 3794 | size="sm" |
| 3779 | 3795 | unstyled |
| 3780 | 3796 | value={projectScopeMenuState.query} |
| 3797 | onKeyDown={(event) => { |
| 3798 | if ( |
| 3799 | event.defaultPrevented || |
| 3800 | event.nativeEvent.isComposing || |
| 3801 | event.ctrlKey || |
| 3802 | event.altKey || |
| 3803 | event.metaKey || |
| 3804 | (event.key !== "ContextMenu" && |
| 3805 | !(event.shiftKey && event.key === "F10")) |
| 3806 | ) { |
| 3807 | return; |
| 3808 | } |
| 3809 | // Combobox items use virtual focus: keyboard events |
| 3810 | // stay on this input, not on the highlighted option. |
| 3811 | const scopeKey = highlightedProjectScopeKeyRef.current; |
| 3812 | const project = scopeKey ? projectGroupByScopeKey.get(scopeKey) : null; |
| 3813 | if (project) handleProjectSettings(event, project); |
| 3814 | }} |
| 3781 | 3815 | onChange={(event) => |
| 3782 | 3816 | dispatchProjectScopeMenu({ |
| 3783 | 3817 | type: "query-changed", |
| ... | ... | @@ -3798,6 +3832,9 @@ export default function Sidebar() { |
| 3798 | 3832 | value={item} |
| 3799 | 3833 | className="h-8 min-h-8 py-0 font-medium" |
| 3800 | 3834 | contentClassName="flex min-w-0 items-center gap-2" |
| 3835 | onContextMenu={(event) => { |
| 3836 | if (project) handleProjectSettings(event, project); |
| 3837 | }} |
| 3801 | 3838 | > |
| 3802 | 3839 | {project ? ( |
| 3803 | 3840 | <ProjectFavicon |
| ... | ... | @@ -3816,7 +3853,8 @@ export default function Sidebar() { |
| 3816 | 3853 | <Button |
| 3817 | 3854 | size="icon-xs" |
| 3818 | 3855 | variant="ghost-muted" |
| 3819 | | aria-label={`Project settings for ${project.displayName}`} |
| 3856 | tabIndex={-1} |
| 3857 | aria-hidden="true" |
| 3820 | 3858 | title={`Project settings for ${project.displayName}`} |
| 3821 | 3859 | className="ml-auto size-6 [--control-icon-color:currentColor] text-icon-muted focus-visible:bg-accent focus-visible:text-foreground" |
| 3822 | 3860 | onPointerDown={(event) => event.stopPropagation()} |