Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { cn } from '@/lib/utils';
import {
Popover,
PopoverContent,
PopoverPortal,
PopoverTrigger
} from '@radix-ui/react-popover';
import { every } from 'lodash';
Expand Down Expand Up @@ -191,51 +192,53 @@ export const CommandPopoverButton = ({
</Tooltip>
</TooltipProvider>

<PopoverContent
id="command-popover"
align="start"
sideOffset={12}
data-popover-content
tabIndex={0}
className={cn(
'p-2 rounded-lg border shadow-md bg-background',
'animate-in fade-in-0 zoom-in-95 duration-200',
'focus:outline-none'
)}
onKeyDown={handleKeyDown}
onMouseLeave={handleMouseLeave}
>
<Command className="overflow-hidden bg-transparent">
<CommandListScrollable maxItems={5} className="custom-scrollbar">
<CommandGroup className="p-0">
{nonButtonCommands.map((command, index) => (
<CommandItemAnimated
key={command.id}
index={index}
isSelected={index === selectedIndex}
onMouseMove={() => handleMouseMove(index)}
onSelect={() => handleCommandSelect(command)} // Direct call for mouse clicks
className="space-x-2"
>
<Icon
name={command.icon}
className={cn(
'!size-5 text-muted-foreground transition-transform duration-150',
index === selectedIndex && 'scale-110'
)}
/>
<div className="flex-1">
<div className="font-medium">{command.id}</div>
<div className="text-sm text-muted-foreground">
{command.description}
<PopoverPortal container={window.cl_shadowRootElement}>
<PopoverContent
id="command-popover"
align="start"
sideOffset={12}
data-popover-content
tabIndex={0}
className={cn(
'z-[51] p-2 rounded-lg border shadow-md bg-background',
'animate-in fade-in-0 zoom-in-95 duration-200',
'focus:outline-none'
)}
onKeyDown={handleKeyDown}
onMouseLeave={handleMouseLeave}
>
<Command className="overflow-hidden bg-transparent">
<CommandListScrollable maxItems={5} className="custom-scrollbar">
<CommandGroup className="p-0">
{nonButtonCommands.map((command, index) => (
<CommandItemAnimated
key={command.id}
index={index}
isSelected={index === selectedIndex}
onMouseMove={() => handleMouseMove(index)}
onSelect={() => handleCommandSelect(command)} // Direct call for mouse clicks
className="space-x-2"
>
<Icon
name={command.icon}
className={cn(
'!size-5 text-muted-foreground transition-transform duration-150',
index === selectedIndex && 'scale-110'
)}
/>
<div className="flex-1">
<div className="font-medium">{command.id}</div>
<div className="text-sm text-muted-foreground">
{command.description}
</div>
</div>
</div>
</CommandItemAnimated>
))}
</CommandGroup>
</CommandListScrollable>
</Command>
</PopoverContent>
</CommandItemAnimated>
))}
</CommandGroup>
</CommandListScrollable>
</Command>
</PopoverContent>
</PopoverPortal>
</Popover>
</div>
);
Expand Down
127 changes: 65 additions & 62 deletions frontend/src/components/chat/MessageComposer/FavoriteButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { cn } from '@/lib/utils';
import {
Popover,
PopoverContent,
PopoverPortal,
PopoverTrigger
} from '@radix-ui/react-popover';
import { Star, Trash } from 'lucide-react';
Expand Down Expand Up @@ -117,69 +118,71 @@ export const FavoriteButton = ({ disabled = false, onSelect }: Props) => {
</Tooltip>
</TooltipProvider>

<PopoverContent
align="start"
sideOffset={12}
className="p-2 w-[300px] rounded-lg border shadow-md bg-background"
>
<Command>
<CommandListScrollable className="max-h-[300px] custom-scrollbar">
{favorites.length === 0 ? (
<CommandEmpty className="py-6 px-4">
<div className="flex flex-col items-center gap-2 text-center">
<p className="text-sm font-medium text-foreground">
{t('chat.favorites.empty.title')}
</p>
<p className="text-xs text-muted-foreground">
{t('chat.favorites.empty.description')}
</p>
</div>
</CommandEmpty>
) : (
<CommandGroup heading={t('chat.favorites.headline')}>
{favorites.map((step) => (
<CommandItem
key={step.id}
value={step.id}
onSelect={() => {
onSelect(step.output);
setOpen(false);
cancelTooltipOpen();
}}
className="cursor-pointer group"
>
<div className="flex items-center justify-between gap-2 w-full overflow-hidden">
<div className="flex flex-col gap-1 overflow-hidden">
<span className="truncate text-sm">
{step.output}
</span>
<span className="text-xs text-muted-foreground">
{new Date(step.createdAt).toLocaleDateString()}
</span>
<PopoverPortal container={window.cl_shadowRootElement}>
<PopoverContent
align="start"
sideOffset={12}
className="z-[51] p-2 w-[300px] rounded-lg border shadow-md bg-background"
>
<Command>
<CommandListScrollable className="max-h-[300px] custom-scrollbar">
{favorites.length === 0 ? (
<CommandEmpty className="py-6 px-4">
<div className="flex flex-col items-center gap-2 text-center">
<p className="text-sm font-medium text-foreground">
{t('chat.favorites.empty.title')}
</p>
<p className="text-xs text-muted-foreground">
{t('chat.favorites.empty.description')}
</p>
</div>
</CommandEmpty>
) : (
<CommandGroup heading={t('chat.favorites.headline')}>
{favorites.map((step) => (
<CommandItem
key={step.id}
value={step.id}
onSelect={() => {
onSelect(step.output);
setOpen(false);
cancelTooltipOpen();
}}
className="cursor-pointer group"
>
<div className="flex items-center justify-between gap-2 w-full overflow-hidden">
<div className="flex flex-col gap-1 overflow-hidden">
<span className="truncate text-sm">
{step.output}
</span>
<span className="text-xs text-muted-foreground">
{new Date(step.createdAt).toLocaleDateString()}
</span>
</div>
<Button
type="button"
variant="ghost"
size="icon"
className="h-7 w-7 text-muted-foreground hover:text-foreground"
aria-label={t('chat.favorites.remove')}
disabled={disabled}
onPointerDown={(event) => event.stopPropagation()}
onClick={(event) => {
event.stopPropagation();
toggleMessageFavorite(step);
}}
>
<Trash className="h-3.5 w-3.5" />
</Button>
</div>
<Button
type="button"
variant="ghost"
size="icon"
className="h-7 w-7 text-muted-foreground hover:text-foreground"
aria-label={t('chat.favorites.remove')}
disabled={disabled}
onPointerDown={(event) => event.stopPropagation()}
onClick={(event) => {
event.stopPropagation();
toggleMessageFavorite(step);
}}
>
<Trash className="h-3.5 w-3.5" />
</Button>
</div>
</CommandItem>
))}
</CommandGroup>
)}
</CommandListScrollable>
</Command>
</PopoverContent>
</CommandItem>
))}
</CommandGroup>
)}
</CommandListScrollable>
</Command>
</PopoverContent>
</PopoverPortal>
</Popover>
</div>
);
Expand Down
103 changes: 53 additions & 50 deletions frontend/src/components/chat/MessageComposer/ModePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { cn } from '@/lib/utils';
import {
Popover,
PopoverContent,
PopoverPortal,
PopoverTrigger
} from '@radix-ui/react-popover';
import { ChevronDown, ChevronUp } from 'lucide-react';
Expand Down Expand Up @@ -155,57 +156,59 @@ export const ModePicker = ({
</Button>
</PopoverTrigger>

<PopoverContent
id={`mode-picker-popover-${mode.id}`}
align="start"
side="top"
sideOffset={4}
className={cn(
'p-1 rounded-md border shadow-lg bg-popover',
'animate-in fade-in-0 zoom-in-95 duration-150',
'w-[280px]'
)}
onKeyDown={handleKeyDown}
onMouseLeave={handleMouseLeave}
>
<Command className="overflow-hidden bg-transparent">
<CommandListScrollable maxItems={6} className="custom-scrollbar">
<CommandGroup className="p-0">
{options.map((option, index) => (
<CommandItemAnimated
key={option.id}
index={index}
isSelected={index === selectedIndex}
onMouseMove={() => handleMouseMove(index)}
onSelect={() => handleOptionSelect(option)}
className={cn(
'flex items-start gap-2 px-2 py-2 cursor-pointer',
selectedOptionId === option.id && 'bg-accent'
)}
>
{renderIcon(
option.icon,
cn(
'!size-5 mt-0.5 text-muted-foreground flex-shrink-0',
index === selectedIndex && 'text-foreground'
)
)}
<div className="flex-1 min-w-0">
<div className="font-medium text-sm leading-tight">
{option.name}
</div>
{option.description && (
<div className="text-xs text-muted-foreground mt-0.5 leading-tight">
{option.description}
</div>
<PopoverPortal container={window.cl_shadowRootElement}>
<PopoverContent
id={`mode-picker-popover-${mode.id}`}
align="start"
side="top"
sideOffset={4}
className={cn(
'z-[51] p-1 rounded-md border shadow-lg bg-popover',
'animate-in fade-in-0 zoom-in-95 duration-150',
'w-[280px]'
)}
onKeyDown={handleKeyDown}
onMouseLeave={handleMouseLeave}
>
<Command className="overflow-hidden bg-transparent">
<CommandListScrollable maxItems={6} className="custom-scrollbar">
<CommandGroup className="p-0">
{options.map((option, index) => (
<CommandItemAnimated
key={option.id}
index={index}
isSelected={index === selectedIndex}
onMouseMove={() => handleMouseMove(index)}
onSelect={() => handleOptionSelect(option)}
className={cn(
'flex items-start gap-2 px-2 py-2 cursor-pointer',
selectedOptionId === option.id && 'bg-accent'
)}
>
{renderIcon(
option.icon,
cn(
'!size-5 mt-0.5 text-muted-foreground flex-shrink-0',
index === selectedIndex && 'text-foreground'
)
)}
</div>
</CommandItemAnimated>
))}
</CommandGroup>
</CommandListScrollable>
</Command>
</PopoverContent>
<div className="flex-1 min-w-0">
<div className="font-medium text-sm leading-tight">
{option.name}
</div>
{option.description && (
<div className="text-xs text-muted-foreground mt-0.5 leading-tight">
{option.description}
</div>
)}
</div>
</CommandItemAnimated>
))}
</CommandGroup>
</CommandListScrollable>
</Command>
</PopoverContent>
</PopoverPortal>
</Popover>
</div>
);
Expand Down
Loading
Loading