From 5b21f4ea7c9e56cc9504770ff640bfd58bc8f4b7 Mon Sep 17 00:00:00 2001 From: Rafael Audibert <32079912+rafaeelaudibert@users.noreply.github.com> Date: Fri, 22 May 2026 14:51:49 -0300 Subject: [PATCH 1/2] fix(message-editor): Close mode/model/effort dropdowns on selection Radix's DropdownMenuRadioItem doesn't auto-close the menu after a selection. Control the open state in ModeSelector, UnifiedModelSelector, and ReasoningLevelSelector so picking a value closes the dropdown. Generated-By: PostHog Code Task-Id: c5f82471-6e62-482b-8d08-118a62241516 --- .../message-editor/components/ModeSelector.tsx | 13 +++++++++++-- .../sessions/components/ReasoningLevelSelector.tsx | 10 ++++++++-- .../sessions/components/UnifiedModelSelector.tsx | 10 +++++++--- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/apps/code/src/renderer/features/message-editor/components/ModeSelector.tsx b/apps/code/src/renderer/features/message-editor/components/ModeSelector.tsx index 7167e65aa..6bc3b15bd 100644 --- a/apps/code/src/renderer/features/message-editor/components/ModeSelector.tsx +++ b/apps/code/src/renderer/features/message-editor/components/ModeSelector.tsx @@ -19,6 +19,7 @@ import { MenuLabel, } from "@posthog/quill"; import { flattenSelectOptions } from "@renderer/features/sessions/stores/sessionStore"; +import { useState } from "react"; interface ModeStyle { icon: React.ReactNode; @@ -78,6 +79,8 @@ export function ModeSelector({ allowBypassPermissions, disabled, }: ModeSelectorProps) { + const [open, setOpen] = useState(false); + if (!modeOption || modeOption.type !== "select") return null; const allOptions = flattenSelectOptions(modeOption.options); @@ -95,7 +98,7 @@ export function ModeSelector({ allOptions.find((opt) => opt.value === currentValue)?.name ?? currentValue; return ( - + Mode - + { + onChange(value); + setOpen(false); + }} + > {options.map((option) => { const style = getStyle(option.value); return ( diff --git a/apps/code/src/renderer/features/sessions/components/ReasoningLevelSelector.tsx b/apps/code/src/renderer/features/sessions/components/ReasoningLevelSelector.tsx index a8bcaec2b..cfbc4aab9 100644 --- a/apps/code/src/renderer/features/sessions/components/ReasoningLevelSelector.tsx +++ b/apps/code/src/renderer/features/sessions/components/ReasoningLevelSelector.tsx @@ -9,6 +9,7 @@ import { DropdownMenuTrigger, MenuLabel, } from "@posthog/quill"; +import { useState } from "react"; import { flattenSelectOptions } from "../stores/sessionStore"; interface ReasoningLevelSelectorProps { @@ -24,6 +25,8 @@ export function ReasoningLevelSelector({ onChange, disabled, }: ReasoningLevelSelectorProps) { + const [open, setOpen] = useState(false); + if (!thoughtOption || thoughtOption.type !== "select") { return null; } @@ -36,7 +39,7 @@ export function ReasoningLevelSelector({ const prefix = adapter === "codex" ? "Reasoning" : "Effort"; return ( - + {adapter === "codex" ? "Reasoning" : "Effort"} onChange?.(value)} + onValueChange={(value) => { + onChange?.(value); + setOpen(false); + }} > {options.map((level) => ( diff --git a/apps/code/src/renderer/features/sessions/components/UnifiedModelSelector.tsx b/apps/code/src/renderer/features/sessions/components/UnifiedModelSelector.tsx index 25b68370e..13134f1ba 100644 --- a/apps/code/src/renderer/features/sessions/components/UnifiedModelSelector.tsx +++ b/apps/code/src/renderer/features/sessions/components/UnifiedModelSelector.tsx @@ -21,7 +21,7 @@ import { DropdownMenuTrigger, MenuLabel, } from "@posthog/quill"; -import { Fragment, useMemo } from "react"; +import { Fragment, useMemo, useState } from "react"; import { flattenSelectOptions } from "../stores/sessionStore"; const ADAPTER_ICONS: Record = { @@ -55,6 +55,7 @@ export function UnifiedModelSelector({ disabled, isConnecting, }: UnifiedModelSelectorProps) { + const [open, setOpen] = useState(false); const selectOption = modelOption?.type === "select" ? modelOption : undefined; const options = selectOption ? flattenSelectOptions(selectOption.options) @@ -83,7 +84,7 @@ export function UnifiedModelSelector({ } return ( - + {ADAPTER_LABELS[adapter]} onModelChange?.(value)} + onValueChange={(value) => { + onModelChange?.(value); + setOpen(false); + }} > {groupedOptions.length > 0 ? groupedOptions.map((group, index) => ( From 5d82ae23d351db2f9fb77de665903e6e4228311b Mon Sep 17 00:00:00 2001 From: Adam Bowker Date: Fri, 22 May 2026 13:51:54 -0700 Subject: [PATCH 2/2] fix(message-editor): Avoid radio-fill flash during dropdown close The previous fix closed the dropdown but applied the selection synchronously, so the radio dot visibly filled during the 150ms close transition. Stash the selected value in a ref, close the menu first, and apply onChange in onOpenChangeComplete so the menu fades out with the prior selection and the trigger button updates as it finishes. Generated-By: PostHog Code Task-Id: a0c8afef-5408-4c6d-af0d-3a221e275b4c --- .../message-editor/components/ModeSelector.tsx | 16 +++++++++++++--- .../components/ReasoningLevelSelector.tsx | 16 +++++++++++++--- .../sessions/components/UnifiedModelSelector.tsx | 16 +++++++++++++--- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/apps/code/src/renderer/features/message-editor/components/ModeSelector.tsx b/apps/code/src/renderer/features/message-editor/components/ModeSelector.tsx index 6bc3b15bd..5d288fbbd 100644 --- a/apps/code/src/renderer/features/message-editor/components/ModeSelector.tsx +++ b/apps/code/src/renderer/features/message-editor/components/ModeSelector.tsx @@ -19,7 +19,7 @@ import { MenuLabel, } from "@posthog/quill"; import { flattenSelectOptions } from "@renderer/features/sessions/stores/sessionStore"; -import { useState } from "react"; +import { useRef, useState } from "react"; interface ModeStyle { icon: React.ReactNode; @@ -80,6 +80,7 @@ export function ModeSelector({ disabled, }: ModeSelectorProps) { const [open, setOpen] = useState(false); + const pendingValueRef = useRef(null); if (!modeOption || modeOption.type !== "select") return null; @@ -98,7 +99,16 @@ export function ModeSelector({ allOptions.find((opt) => opt.value === currentValue)?.name ?? currentValue; return ( - + { + if (!isOpen && pendingValueRef.current !== null) { + onChange(pendingValueRef.current); + pendingValueRef.current = null; + } + }} + > { - onChange(value); + pendingValueRef.current = value; setOpen(false); }} > diff --git a/apps/code/src/renderer/features/sessions/components/ReasoningLevelSelector.tsx b/apps/code/src/renderer/features/sessions/components/ReasoningLevelSelector.tsx index cfbc4aab9..c60408d8e 100644 --- a/apps/code/src/renderer/features/sessions/components/ReasoningLevelSelector.tsx +++ b/apps/code/src/renderer/features/sessions/components/ReasoningLevelSelector.tsx @@ -9,7 +9,7 @@ import { DropdownMenuTrigger, MenuLabel, } from "@posthog/quill"; -import { useState } from "react"; +import { useRef, useState } from "react"; import { flattenSelectOptions } from "../stores/sessionStore"; interface ReasoningLevelSelectorProps { @@ -26,6 +26,7 @@ export function ReasoningLevelSelector({ disabled, }: ReasoningLevelSelectorProps) { const [open, setOpen] = useState(false); + const pendingValueRef = useRef(null); if (!thoughtOption || thoughtOption.type !== "select") { return null; @@ -39,7 +40,16 @@ export function ReasoningLevelSelector({ const prefix = adapter === "codex" ? "Reasoning" : "Effort"; return ( - + { + if (!isOpen && pendingValueRef.current !== null) { + onChange?.(pendingValueRef.current); + pendingValueRef.current = null; + } + }} + > { - onChange?.(value); + pendingValueRef.current = value; setOpen(false); }} > diff --git a/apps/code/src/renderer/features/sessions/components/UnifiedModelSelector.tsx b/apps/code/src/renderer/features/sessions/components/UnifiedModelSelector.tsx index 13134f1ba..12ff6479f 100644 --- a/apps/code/src/renderer/features/sessions/components/UnifiedModelSelector.tsx +++ b/apps/code/src/renderer/features/sessions/components/UnifiedModelSelector.tsx @@ -21,7 +21,7 @@ import { DropdownMenuTrigger, MenuLabel, } from "@posthog/quill"; -import { Fragment, useMemo, useState } from "react"; +import { Fragment, useMemo, useRef, useState } from "react"; import { flattenSelectOptions } from "../stores/sessionStore"; const ADAPTER_ICONS: Record = { @@ -56,6 +56,7 @@ export function UnifiedModelSelector({ isConnecting, }: UnifiedModelSelectorProps) { const [open, setOpen] = useState(false); + const pendingValueRef = useRef(null); const selectOption = modelOption?.type === "select" ? modelOption : undefined; const options = selectOption ? flattenSelectOptions(selectOption.options) @@ -84,7 +85,16 @@ export function UnifiedModelSelector({ } return ( - + { + if (!isOpen && pendingValueRef.current !== null) { + onModelChange?.(pendingValueRef.current); + pendingValueRef.current = null; + } + }} + > { - onModelChange?.(value); + pendingValueRef.current = value; setOpen(false); }} >