mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
Fix portal stuff
This commit is contained in:
parent
5f20fbbf44
commit
9fdc546f01
10 changed files with 71 additions and 69 deletions
|
|
@ -132,7 +132,6 @@ const App = () => {
|
|||
const AppWithProviders = () => (
|
||||
<ExtensionStateContextProvider>
|
||||
<App />
|
||||
<div id="roo-portal" />
|
||||
</ExtensionStateContextProvider>
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +1,25 @@
|
|||
import React, { forwardRef, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"
|
||||
import DynamicTextArea from "react-textarea-autosize"
|
||||
|
||||
import { mentionRegex, mentionRegexGlobal } from "../../../../src/shared/context-mentions"
|
||||
import { useExtensionState } from "../../context/ExtensionStateContext"
|
||||
import { WebviewMessage } from "../../../../src/shared/WebviewMessage"
|
||||
import { Mode, getAllModes } from "../../../../src/shared/modes"
|
||||
|
||||
import { vscode } from "@/utils/vscode"
|
||||
import {
|
||||
ContextMenuOptionType,
|
||||
getContextMenuOptions,
|
||||
insertMention,
|
||||
removeMention,
|
||||
shouldShowContextMenu,
|
||||
} from "../../utils/context-mentions"
|
||||
} from "@/utils/context-mentions"
|
||||
import { SelectDropdown, DropdownOptionType } from "@/components/ui"
|
||||
|
||||
import { useExtensionState } from "../../context/ExtensionStateContext"
|
||||
import Thumbnails from "../common/Thumbnails"
|
||||
import { convertToMentionPath } from "../../utils/path-mentions"
|
||||
import { MAX_IMAGES_PER_MESSAGE } from "./ChatView"
|
||||
import ContextMenu from "./ContextMenu"
|
||||
import Thumbnails from "../common/Thumbnails"
|
||||
import { vscode } from "../../utils/vscode"
|
||||
import { WebviewMessage } from "../../../../src/shared/WebviewMessage"
|
||||
import { Mode, getAllModes } from "../../../../src/shared/modes"
|
||||
import { convertToMentionPath } from "../../utils/path-mentions"
|
||||
import { SelectDropdown, DropdownOptionType } from "../ui"
|
||||
|
||||
interface ChatTextAreaProps {
|
||||
inputValue: string
|
||||
|
|
|
|||
|
|
@ -1275,7 +1275,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
|
|||
modeShortcutText={modeShortcutText}
|
||||
/>
|
||||
|
||||
<div id="chat-view-portal" />
|
||||
<div id="roo-portal" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { useState, useCallback } from "react"
|
|||
import { CheckIcon, Cross2Icon } from "@radix-ui/react-icons"
|
||||
|
||||
import { Button, Popover, PopoverContent, PopoverTrigger } from "@/components/ui"
|
||||
import { useRooPortal } from "@/components/ui/hooks"
|
||||
|
||||
import { vscode } from "../../../utils/vscode"
|
||||
import { Checkpoint } from "./schema"
|
||||
|
|
@ -16,6 +17,7 @@ type CheckpointMenuProps = {
|
|||
export const CheckpointMenu = ({ ts, commitHash, currentHash, checkpoint }: CheckpointMenuProps) => {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const [isConfirming, setIsConfirming] = useState(false)
|
||||
const portalContainer = useRooPortal("roo-portal")
|
||||
|
||||
const isCurrent = currentHash === commitHash
|
||||
const isFirst = checkpoint.isFirst
|
||||
|
|
@ -60,7 +62,7 @@ export const CheckpointMenu = ({ ts, commitHash, currentHash, checkpoint }: Chec
|
|||
<span className="codicon codicon-history" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent align="end">
|
||||
<PopoverContent align="end" container={portalContainer}>
|
||||
<div className="flex flex-col gap-2">
|
||||
{!isCurrent && (
|
||||
<div className="flex flex-col gap-1 group hover:text-foreground">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import * as React from "react"
|
||||
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
|
||||
import { PortalProps } from "@radix-ui/react-portal"
|
||||
import { CheckIcon, ChevronRightIcon, DotFilledIcon } from "@radix-ui/react-icons"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
|
@ -53,25 +54,21 @@ DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayNam
|
|||
|
||||
const DropdownMenuContent = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
|
||||
>(({ className, sideOffset = 4, ...props }, ref) => {
|
||||
const container = React.useMemo(() => document.getElementById("roo-portal"), [])
|
||||
|
||||
return (
|
||||
<DropdownMenuPrimitive.Portal container={container}>
|
||||
<DropdownMenuPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</DropdownMenuPrimitive.Portal>
|
||||
)
|
||||
})
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> & Pick<PortalProps, "container">
|
||||
>(({ className, sideOffset = 4, container, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Portal container={container}>
|
||||
<DropdownMenuPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</DropdownMenuPrimitive.Portal>
|
||||
))
|
||||
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
|
||||
|
||||
const DropdownMenuItem = React.forwardRef<
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
export * from "./useClipboard"
|
||||
export * from "./useRooPortal"
|
||||
|
|
|
|||
10
webview-ui/src/components/ui/hooks/useRooPortal.ts
Normal file
10
webview-ui/src/components/ui/hooks/useRooPortal.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { useState } from "react"
|
||||
import { useMount } from "react-use"
|
||||
|
||||
export const useRooPortal = (id: string) => {
|
||||
const [container, setContainer] = useState<HTMLElement>()
|
||||
|
||||
useMount(() => setContainer(document.getElementById(id) ?? undefined))
|
||||
|
||||
return container
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import * as React from "react"
|
||||
import { PortalProps } from "@radix-ui/react-portal"
|
||||
import * as PopoverPrimitive from "@radix-ui/react-popover"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
|
@ -11,25 +12,21 @@ const PopoverAnchor = PopoverPrimitive.Anchor
|
|||
|
||||
const PopoverContent = React.forwardRef<
|
||||
React.ElementRef<typeof PopoverPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
|
||||
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => {
|
||||
const container = React.useMemo(() => document.getElementById("roo-portal"), [])
|
||||
|
||||
return (
|
||||
<PopoverPrimitive.Portal container={container}>
|
||||
<PopoverPrimitive.Content
|
||||
ref={ref}
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 w-72 rounded-xs border border-vscode-dropdown-border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</PopoverPrimitive.Portal>
|
||||
)
|
||||
})
|
||||
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> & Pick<PortalProps, "container">
|
||||
>(({ className, align = "center", sideOffset = 4, container, ...props }, ref) => (
|
||||
<PopoverPrimitive.Portal container={container}>
|
||||
<PopoverPrimitive.Content
|
||||
ref={ref}
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 w-72 rounded-xs border border-vscode-dropdown-border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</PopoverPrimitive.Portal>
|
||||
))
|
||||
PopoverContent.displayName = PopoverPrimitive.Content.displayName
|
||||
|
||||
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
import { useRooPortal } from "./hooks/useRooPortal"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
|
|
@ -6,9 +10,7 @@ import {
|
|||
DropdownMenuTrigger,
|
||||
DropdownMenuSeparator,
|
||||
} from "./dropdown-menu"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
// Constants for option types
|
||||
export enum DropdownOptionType {
|
||||
ITEM = "item",
|
||||
SEPARATOR = "separator",
|
||||
|
|
@ -19,7 +21,7 @@ export interface DropdownOption {
|
|||
value: string
|
||||
label: string
|
||||
disabled?: boolean
|
||||
type?: DropdownOptionType // Optional type to specify special behaviors
|
||||
type?: DropdownOptionType
|
||||
}
|
||||
|
||||
export interface SelectDropdownProps {
|
||||
|
|
@ -38,8 +40,6 @@ export interface SelectDropdownProps {
|
|||
shortcutText?: string
|
||||
}
|
||||
|
||||
// TODO: Get rid of this and use the native @shadcn/ui `Select` component.
|
||||
|
||||
export const SelectDropdown = React.forwardRef<React.ElementRef<typeof DropdownMenuTrigger>, SelectDropdownProps>(
|
||||
(
|
||||
{
|
||||
|
|
@ -59,24 +59,19 @@ export const SelectDropdown = React.forwardRef<React.ElementRef<typeof DropdownM
|
|||
},
|
||||
ref,
|
||||
) => {
|
||||
// Track open state
|
||||
const [open, setOpen] = React.useState(false)
|
||||
const portalContainer = useRooPortal("roo-portal")
|
||||
|
||||
// Find the selected option label
|
||||
const selectedOption = options.find((option) => option.value === value)
|
||||
const displayText = selectedOption?.label || placeholder || ""
|
||||
|
||||
// Handle menu item click
|
||||
const handleSelect = (option: DropdownOption) => {
|
||||
// Check if this is an action option by its explicit type
|
||||
if (option.type === DropdownOptionType.ACTION) {
|
||||
window.postMessage({
|
||||
type: "action",
|
||||
action: option.value,
|
||||
})
|
||||
window.postMessage({ type: "action", action: option.value })
|
||||
setOpen(false)
|
||||
return
|
||||
}
|
||||
|
||||
onChange(option.value)
|
||||
setOpen(false)
|
||||
}
|
||||
|
|
@ -94,7 +89,7 @@ export const SelectDropdown = React.forwardRef<React.ElementRef<typeof DropdownM
|
|||
triggerClassName,
|
||||
)}
|
||||
style={{
|
||||
width: "100%", // Take full width of parent
|
||||
width: "100%", // Take full width of parent.
|
||||
minWidth: "0",
|
||||
maxWidth: "100%",
|
||||
}}>
|
||||
|
|
@ -121,17 +116,16 @@ export const SelectDropdown = React.forwardRef<React.ElementRef<typeof DropdownM
|
|||
sideOffset={sideOffset}
|
||||
onEscapeKeyDown={() => setOpen(false)}
|
||||
onInteractOutside={() => setOpen(false)}
|
||||
container={portalContainer}
|
||||
className={cn(
|
||||
"bg-vscode-dropdown-background text-vscode-dropdown-foreground border border-vscode-dropdown-border z-50",
|
||||
contentClassName,
|
||||
)}>
|
||||
{options.map((option, index) => {
|
||||
// Handle separator type
|
||||
if (option.type === DropdownOptionType.SEPARATOR) {
|
||||
return <DropdownMenuSeparator key={`sep-${index}`} />
|
||||
}
|
||||
|
||||
// Handle shortcut text type (disabled label for keyboard shortcuts)
|
||||
if (
|
||||
option.type === DropdownOptionType.SHORTCUT ||
|
||||
(option.disabled && shortcutText && option.label.includes(shortcutText))
|
||||
|
|
@ -143,7 +137,6 @@ export const SelectDropdown = React.forwardRef<React.ElementRef<typeof DropdownM
|
|||
)
|
||||
}
|
||||
|
||||
// Regular menu items
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
key={`item-${option.value}`}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import * as React from "react"
|
||||
import { PortalProps } from "@radix-ui/react-portal"
|
||||
import * as SelectPrimitive from "@radix-ui/react-select"
|
||||
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
|
||||
|
||||
|
|
@ -37,10 +38,9 @@ function SelectContent({
|
|||
className,
|
||||
children,
|
||||
position = "popper",
|
||||
container,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Content>) {
|
||||
const container = React.useMemo(() => document.getElementById("roo-portal"), [])
|
||||
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Content> & Pick<PortalProps, "container">) {
|
||||
return (
|
||||
<SelectPrimitive.Portal container={container}>
|
||||
<SelectPrimitive.Content
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue