diff --git a/src/renderer/src/components/feature-wall/AiCommitPrSettingsCard.tsx b/src/renderer/src/components/feature-wall/AiCommitPrSettingsCard.tsx index b2696ec71..1cb5e1b8f 100644 --- a/src/renderer/src/components/feature-wall/AiCommitPrSettingsCard.tsx +++ b/src/renderer/src/components/feature-wall/AiCommitPrSettingsCard.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState, type JSX } from 'react' +import { useCallback, useState, type JSX } from 'react' import { Terminal } from 'lucide-react' import type { CommitMessageAiSettings, GlobalSettings, TuiAgent } from '../../../../shared/types' import { @@ -97,11 +97,12 @@ export function AiCommitPrSettingsCard(): JSX.Element | null { // onboarding that puts menus behind the z-[100] fullscreen tour layer, so // portal into the active tour/dialog surface instead. const [selectPortalRoot, setSelectPortalRoot] = useState(null) - useEffect(() => { - const el = document.querySelector( - '[data-onboarding-overlay], [data-slot="dialog-content"]' + const setSelectPortalHost = useCallback((node: HTMLDivElement | null) => { + // Why: select menus must portal into the active tour/dialog surface so + // body-level portals do not render behind the fullscreen onboarding layer. + setSelectPortalRoot( + node?.closest('[data-onboarding-overlay], [data-slot="dialog-content"]') ?? node ) - setSelectPortalRoot(el) }, []) if (!settings) { return null @@ -254,7 +255,7 @@ export function AiCommitPrSettingsCard(): JSX.Element | null { } return ( -
+
diff --git a/src/renderer/src/components/onboarding/NotificationStep.tsx b/src/renderer/src/components/onboarding/NotificationStep.tsx index fd640ead6..e94c6b183 100644 --- a/src/renderer/src/components/onboarding/NotificationStep.tsx +++ b/src/renderer/src/components/onboarding/NotificationStep.tsx @@ -1,5 +1,5 @@ /* eslint-disable max-lines -- Why: this onboarding step owns the full notification setup surface, including macOS guidance, sound choices, and upload controls. */ -import { useEffect, useRef, useState } from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import { BellRing, FileAudio, Settings, Upload, X } from 'lucide-react' import { toast } from 'sonner' import type { GlobalSettings, NotificationPermissionStatusResult } from '../../../../shared/types' @@ -59,10 +59,10 @@ export function NotificationStep({ notificationSettingsRef.current = notificationSettings } - useEffect(() => { + const setSelectPortalHost = useCallback((node: HTMLDivElement | null) => { // Why: onboarding sits above body-level portals, so the select menu must // portal into the overlay to stay clickable. - setSelectPortalRoot(document.querySelector('[data-onboarding-overlay]')) + setSelectPortalRoot(node?.closest('[data-onboarding-overlay]') ?? node) }, []) useEffect(() => { @@ -163,7 +163,7 @@ export function NotificationStep({ const isMac = permissionStatus?.platform === 'darwin' return ( -
+
{isMac ? (