ui: changes for context menu plus alignment + padding (#1179)

This commit is contained in:
josh 2025-09-09 23:58:40 +01:00 committed by GitHub
parent eae41c635d
commit 2f9ba09606
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 65 additions and 36 deletions

View file

@ -1,10 +1,10 @@
'use client';
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from '@/components/ui/hover-card';
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { cn } from '@/lib/utils';
import type { ComponentProps } from 'react';
import type { LanguageModelUsage } from 'ai';
@ -97,11 +97,11 @@ export const ContextIcon = ({ percent }: ContextIconProps) => {
return (
<svg
aria-label={`${formatPercent(percent)} of model context used`}
height="20"
height="28"
role="img"
style={{ color: 'currentcolor' }}
viewBox={`0 0 ${ICON_VIEWBOX} ${ICON_VIEWBOX}`}
width="20"
width="28"
>
<circle
cx={ICON_CENTER}
@ -271,8 +271,8 @@ export const Context = ({
n === undefined ? '—' : formatTokens(n);
return (
<HoverCard closeDelay={100} openDelay={100}>
<HoverCardTrigger asChild>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
className={cn(
'inline-flex select-none items-center gap-1 rounded-md px-1.5 py-1 text-sm',
@ -282,17 +282,18 @@ export const Context = ({
type="button"
{...props}
>
<span className="font-medium text-muted-foreground">
<span className="font-medium text-muted-foreground hidden">
{displayPct}
</span>
<ContextIcon percent={usedPercent} />
</button>
</HoverCardTrigger>
<HoverCardContent align="end" side="top" className="w-fit p-3">
</DropdownMenuTrigger>
<DropdownMenuContent align="end" side="top" className="w-fit p-3">
<div className="min-w-[240px] space-y-2">
<p className="text-start text-sm">
{displayPct} {used} / {total} tokens
</p>
<div className="flex justify-between items-start text-sm">
<span>{displayPct}</span>
<span className="text-muted-foreground">{used} / {total} tokens</span>
</div>
<div className="space-y-2">
<Progress className="h-2 bg-muted" value={usedPercent} />
</div>
@ -339,7 +340,7 @@ export const Context = ({
)}
</div>
</div>
</HoverCardContent>
</HoverCard>
</DropdownMenuContent>
</DropdownMenu>
);
};

View file

@ -698,6 +698,30 @@ export const DeltaIcon = ({ size = 16 }: { size?: number }) => (
</svg>
);
export const CpuIcon = ({ size = 16 }: { size?: number }) => (
<svg
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
style={{ color: 'currentcolor' }}
>
<path d="M4 12C4 8.22876 4 6.34315 5.17157 5.17157C6.34315 4 8.22876 4 12 4C15.7712 4 17.6569 4 18.8284 5.17157C20 6.34315 20 8.22876 20 12C20 15.7712 20 17.6569 18.8284 18.8284C17.6569 20 15.7712 20 12 20C8.22876 20 6.34315 20 5.17157 18.8284C4 17.6569 4 15.7712 4 12Z" strokeLinejoin="round" />
<path d="M9.5 2V4" strokeLinecap="round" strokeLinejoin="round" />
<path d="M14.5 2V4" strokeLinecap="round" strokeLinejoin="round" />
<path d="M9.5 20V22" strokeLinecap="round" strokeLinejoin="round" />
<path d="M14.5 20V22" strokeLinecap="round" strokeLinejoin="round" />
<path d="M13 9L9 13" strokeLinecap="round" strokeLinejoin="round" />
<path d="M15 13L13 15" strokeLinecap="round" strokeLinejoin="round" />
<path d="M22 14.5L20 14.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M4 9.5L2 9.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M4 14.5L2 14.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M22 9.5L20 9.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
);
export const PenIcon = ({ size = 16 }: { size?: number }) => (
<svg
height={size}

View file

@ -15,7 +15,7 @@ import {
import { toast } from 'sonner';
import { useLocalStorage, useWindowSize } from 'usehooks-ts';
import { ArrowUpIcon, PaperclipIcon, StopIcon } from './icons';
import { ArrowUpIcon, PaperclipIcon, CpuIcon, StopIcon, ChevronDownIcon } from './icons';
import { PreviewAttachment } from './preview-attachment';
import { Button } from './ui/button';
import { SuggestedActions } from './suggested-actions';
@ -26,10 +26,10 @@ import {
PromptInputTools,
PromptInputSubmit,
PromptInputModelSelect,
PromptInputModelSelectTrigger,
PromptInputModelSelectContent,
} from './elements/prompt-input';
import { SelectItem } from '@/components/ui/select';
import * as SelectPrimitive from '@radix-ui/react-select';
import equal from 'fast-deep-equal';
import type { UseChatHelpers } from '@ai-sdk/react';
import { AnimatePresence, motion } from 'framer-motion';
@ -352,14 +352,14 @@ function PureMultimodalInput({
minHeight={44}
maxHeight={200}
disableAutoResize={true}
className='grow resize-none border-0! border-none! bg-transparent px-2 py-3 text-sm outline-none ring-0 [-ms-overflow-style:none] [scrollbar-width:none] placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 sm:px-3 [&::-webkit-scrollbar]:hidden'
className='grow resize-none border-0! border-none! bg-transparent px-2 pt-1 pb-3 ml-1 text-sm outline-none ring-0 [-ms-overflow-style:none] [scrollbar-width:none] placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 [&::-webkit-scrollbar]:hidden'
rows={1}
autoFocus
/>{' '}
<Context {...contextProps} className="mr-1.5" />
<Context {...contextProps} className="mr-0.5 mt-1" />
</div>
<PromptInputToolbar className='!border-top-0 border-t-0! px-2 py-2 shadow-none dark:border-0 dark:border-transparent!'>
<PromptInputTools className="gap-1 sm:gap-2">
<PromptInputTools className="gap-0 sm:gap-0.5">
<AttachmentsButton
fileInputRef={fileInputRef}
status={status}
@ -374,7 +374,7 @@ function PureMultimodalInput({
<PromptInputSubmit
status={status}
disabled={!input.trim() || uploadQueue.length > 0}
className="size-7 rounded-full bg-primary p-1 text-primary-foreground transition-colors duration-200 hover:bg-primary/90 disabled:bg-muted disabled:text-muted-foreground"
className="size-7 rounded-full bg-primary p-1 text-primary-foreground transition-colors duration-200 hover:bg-primary/90 disabled:bg-muted disabled:text-muted-foreground -mt-2"
>
<ArrowUpIcon size={14} />
</PromptInputSubmit>
@ -413,7 +413,7 @@ function PureAttachmentsButton({
return (
<Button
data-testid="attachments-button"
className='h-fit rounded-md p-1 transition-colors duration-200 hover:bg-muted'
className='h-8 rounded-lg p-1 transition-colors hover:bg-accent'
onClick={(event) => {
event.preventDefault();
fileInputRef.current?.click();
@ -452,18 +452,22 @@ function PureModelSelectorCompact({
}
}}
>
<PromptInputModelSelectTrigger
<SelectPrimitive.Trigger
type="button"
className='max-w-[120px] truncate text-xs focus:outline-hidden focus:ring-0 focus:ring-offset-0 focus-visible:ring-0 focus-visible:ring-offset-0 data-[state=closed]:ring-0 data-[state=open]:ring-0 sm:max-w-none'
className='flex items-center gap-2 px-2 h-8 rounded-lg border-0 bg-background text-foreground hover:bg-accent transition-colors focus:outline-none focus:ring-0 focus-visible:ring-0 focus-visible:ring-offset-0 shadow-none'
>
{selectedModel?.name || 'Select model'}
</PromptInputModelSelectTrigger>
<PromptInputModelSelectContent className="max-w-[90vw] sm:max-w-[400px]">
<CpuIcon size={16} />
<span className="text-xs font-medium sm:block hidden">{selectedModel?.name}</span>
<ChevronDownIcon size={16} />
</SelectPrimitive.Trigger>
<PromptInputModelSelectContent className="min-w-[260px] p-0">
{chatModels.map((model) => (
<SelectItem key={model.id} value={model.name}>
<div className='flex flex-col items-start gap-1 py-1'>
<div className='font-medium text-sm'>{model.name}</div>
<div className='line-clamp-2 text-muted-foreground text-xs'>
<SelectItem key={model.id} value={model.name} className="px-3 py-2 text-xs">
<div className="flex flex-col min-w-0 flex-1">
<div className="font-medium truncate text-xs">
{model.name}
</div>
<div className="text-[10px] text-muted-foreground truncate leading-tight">
{model.description}
</div>
</div>

View file

@ -86,7 +86,7 @@ const SelectContent = React.forwardRef<
<SelectScrollUpButton />
<SelectPrimitive.Viewport
className={cn(
'p-1',
'p-2',
position === 'popper' &&
'h-(--radix-select-trigger-height) w-full min-w-(--radix-select-trigger-width)',
)}
@ -118,14 +118,14 @@ const SelectItem = React.forwardRef<
<SelectPrimitive.Item
ref={ref}
className={cn(
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50',
'relative flex w-full cursor-default select-none items-center rounded-md py-1.5 pr-8 pl-3 text-sm outline-hidden hover:bg-muted/50 focus:bg-muted data-[state=checked]:bg-muted data-disabled:pointer-events-none data-disabled:opacity-50 transition-colors',
className,
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<span className="absolute right-3 flex h-4 w-4 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
<Check className="h-4 w-4 text-foreground" />
</SelectPrimitive.ItemIndicator>
</span>