fix(vscode): prevent selector popover clipping in new worktree dialog

The New Worktree dialog renders model, reasoning-variant, and mode pickers
inline (portal=false) so clicks aren't swallowed by the modal overlay. The
CSS overflow escape hatch that lets an open popover break out of the dialog's
scroll containers keyed only on model-selector-popover, so the variant and
mode dropdowns added later were clipped by .am-nv-dialog-content and the
prompt container.

Generalize the :has() rules to any popover-content inside the dialog. Popover
content unmounts on close, so the override only applies while a dropdown is open.

Add a visual-regression story (NewWorktreeDialog with the variant dropdown
open) that reproduces the clipping without the fix and covers all inline
pickers going forward.
This commit is contained in:
marius-kilocode
2026-07-07 14:20:30 +02:00
parent 08f0bf6457
commit a5df5bc8e4
3 changed files with 49 additions and 4 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Fix the reasoning-variant and mode dropdowns being clipped inside the New Worktree dialog. The dialog's scroll-container overflow escape now covers all inline selector popovers, not just the model picker, so dropdowns render fully above the prompt input.
@@ -2709,10 +2709,11 @@ body.am-wt-dragging-active * {
overflow-y: auto;
}
.am-nv-dialog .am-prompt-input-container:has([class~="model-selector-popover"][data-component="popover-content"]),
.am-nv-dialog-content:has([class~="model-selector-popover"][data-component="popover-content"]),
[data-component="dialog"]:has(.am-nv-dialog [class~="model-selector-popover"][data-component="popover-content"])
[data-slot="dialog-body"] {
/* While any inline (non-portaled) selector popover is open, let it escape the
dialog's scroll containers. Covers model, thinking-variant, and mode pickers. */
.am-nv-dialog .am-prompt-input-container:has([data-component="popover-content"]),
.am-nv-dialog-content:has([data-component="popover-content"]),
[data-component="dialog"]:has(.am-nv-dialog [data-component="popover-content"]) [data-slot="dialog-body"] {
overflow: visible;
}
@@ -10,6 +10,8 @@ import { FileTree } from "../../diff-viewer/FileTree"
import { DiffPanel } from "../../agent-manager/DiffPanel"
import { FullScreenDiffView } from "../../diff-viewer/FullScreenDiffView"
import { WorktreeItem } from "../../agent-manager/WorktreeItem"
import { NewWorktreeDialog } from "../../agent-manager/NewWorktreeDialog"
import { useDialog } from "@kilocode/kilo-ui/context/dialog"
import { ChatView } from "../components/chat/ChatView"
import { registerVscodeToolOverrides } from "../components/chat/VscodeToolOverrides"
import { SessionContext } from "../context/session"
@@ -929,6 +931,43 @@ export const TabBarSingleTab: Story = {
),
}
// ---------------------------------------------------------------------------
// NewWorktreeDialog — variant dropdown must escape the dialog scroll containers
// (regression: inline popovers were clipped by .am-nv-dialog-content overflow)
// ---------------------------------------------------------------------------
const NewWorktreeVariantOpener = () => {
const dialog = useDialog()
const open = () => {
if (document.querySelector("[data-component='popover-content']")) return
window.dispatchEvent(new CustomEvent("openVariantPicker"))
requestAnimationFrame(open)
}
onMount(() => {
dialog.show(() => <NewWorktreeDialog onClose={() => {}} />)
requestAnimationFrame(open)
})
return null
}
export const NewWorktreeVariantDropdown1280: Story = {
name: "NewWorktreeDialog — variant dropdown open",
parameters: { layout: "fullscreen" },
render: () => {
const session = {
...mockSessionValue(),
configModel: () => ({ providerID: "kilo", modelID: "anthropic/claude-sonnet-4-6" }),
}
return (
<StoryProviders noPadding>
<SessionContext.Provider value={session as any}>
<NewWorktreeVariantOpener />
</SessionContext.Provider>
</StoryProviders>
)
},
}
const searchSection = { id: "polish", name: "Polish", color: "Blue", order: 0, collapsed: false }
const slackedSection = { id: "slacked", name: "SLACKED", color: "Yellow", order: 1, collapsed: false }
const sidebarSearchItems: SidebarSearchItem[] = [