ui: changes for context menu plus alignment + padding (#1179)
This commit is contained in:
parent
eae41c635d
commit
2f9ba09606
4 changed files with 65 additions and 36 deletions
|
|
@ -1,10 +1,10 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
HoverCard,
|
DropdownMenu,
|
||||||
HoverCardContent,
|
DropdownMenuContent,
|
||||||
HoverCardTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '@/components/ui/hover-card';
|
} from '@/components/ui/dropdown-menu';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import type { ComponentProps } from 'react';
|
import type { ComponentProps } from 'react';
|
||||||
import type { LanguageModelUsage } from 'ai';
|
import type { LanguageModelUsage } from 'ai';
|
||||||
|
|
@ -97,11 +97,11 @@ export const ContextIcon = ({ percent }: ContextIconProps) => {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
aria-label={`${formatPercent(percent)} of model context used`}
|
aria-label={`${formatPercent(percent)} of model context used`}
|
||||||
height="20"
|
height="28"
|
||||||
role="img"
|
role="img"
|
||||||
style={{ color: 'currentcolor' }}
|
style={{ color: 'currentcolor' }}
|
||||||
viewBox={`0 0 ${ICON_VIEWBOX} ${ICON_VIEWBOX}`}
|
viewBox={`0 0 ${ICON_VIEWBOX} ${ICON_VIEWBOX}`}
|
||||||
width="20"
|
width="28"
|
||||||
>
|
>
|
||||||
<circle
|
<circle
|
||||||
cx={ICON_CENTER}
|
cx={ICON_CENTER}
|
||||||
|
|
@ -271,8 +271,8 @@ export const Context = ({
|
||||||
n === undefined ? '—' : formatTokens(n);
|
n === undefined ? '—' : formatTokens(n);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HoverCard closeDelay={100} openDelay={100}>
|
<DropdownMenu>
|
||||||
<HoverCardTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<button
|
<button
|
||||||
className={cn(
|
className={cn(
|
||||||
'inline-flex select-none items-center gap-1 rounded-md px-1.5 py-1 text-sm',
|
'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"
|
type="button"
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<span className="font-medium text-muted-foreground">
|
<span className="font-medium text-muted-foreground hidden">
|
||||||
{displayPct}
|
{displayPct}
|
||||||
</span>
|
</span>
|
||||||
<ContextIcon percent={usedPercent} />
|
<ContextIcon percent={usedPercent} />
|
||||||
</button>
|
</button>
|
||||||
</HoverCardTrigger>
|
</DropdownMenuTrigger>
|
||||||
<HoverCardContent align="end" side="top" className="w-fit p-3">
|
<DropdownMenuContent align="end" side="top" className="w-fit p-3">
|
||||||
<div className="min-w-[240px] space-y-2">
|
<div className="min-w-[240px] space-y-2">
|
||||||
<p className="text-start text-sm">
|
<div className="flex justify-between items-start text-sm">
|
||||||
{displayPct} • {used} / {total} tokens
|
<span>{displayPct}</span>
|
||||||
</p>
|
<span className="text-muted-foreground">{used} / {total} tokens</span>
|
||||||
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Progress className="h-2 bg-muted" value={usedPercent} />
|
<Progress className="h-2 bg-muted" value={usedPercent} />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -339,7 +340,7 @@ export const Context = ({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</HoverCardContent>
|
</DropdownMenuContent>
|
||||||
</HoverCard>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -698,6 +698,30 @@ export const DeltaIcon = ({ size = 16 }: { size?: number }) => (
|
||||||
</svg>
|
</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 }) => (
|
export const PenIcon = ({ size = 16 }: { size?: number }) => (
|
||||||
<svg
|
<svg
|
||||||
height={size}
|
height={size}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import {
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { useLocalStorage, useWindowSize } from 'usehooks-ts';
|
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 { PreviewAttachment } from './preview-attachment';
|
||||||
import { Button } from './ui/button';
|
import { Button } from './ui/button';
|
||||||
import { SuggestedActions } from './suggested-actions';
|
import { SuggestedActions } from './suggested-actions';
|
||||||
|
|
@ -26,10 +26,10 @@ import {
|
||||||
PromptInputTools,
|
PromptInputTools,
|
||||||
PromptInputSubmit,
|
PromptInputSubmit,
|
||||||
PromptInputModelSelect,
|
PromptInputModelSelect,
|
||||||
PromptInputModelSelectTrigger,
|
|
||||||
PromptInputModelSelectContent,
|
PromptInputModelSelectContent,
|
||||||
} from './elements/prompt-input';
|
} from './elements/prompt-input';
|
||||||
import { SelectItem } from '@/components/ui/select';
|
import { SelectItem } from '@/components/ui/select';
|
||||||
|
import * as SelectPrimitive from '@radix-ui/react-select';
|
||||||
import equal from 'fast-deep-equal';
|
import equal from 'fast-deep-equal';
|
||||||
import type { UseChatHelpers } from '@ai-sdk/react';
|
import type { UseChatHelpers } from '@ai-sdk/react';
|
||||||
import { AnimatePresence, motion } from 'framer-motion';
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
|
|
@ -352,14 +352,14 @@ function PureMultimodalInput({
|
||||||
minHeight={44}
|
minHeight={44}
|
||||||
maxHeight={200}
|
maxHeight={200}
|
||||||
disableAutoResize={true}
|
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}
|
rows={1}
|
||||||
autoFocus
|
autoFocus
|
||||||
/>{' '}
|
/>{' '}
|
||||||
<Context {...contextProps} className="mr-1.5" />
|
<Context {...contextProps} className="mr-0.5 mt-1" />
|
||||||
</div>
|
</div>
|
||||||
<PromptInputToolbar className='!border-top-0 border-t-0! px-2 py-2 shadow-none dark:border-0 dark:border-transparent!'>
|
<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
|
<AttachmentsButton
|
||||||
fileInputRef={fileInputRef}
|
fileInputRef={fileInputRef}
|
||||||
status={status}
|
status={status}
|
||||||
|
|
@ -374,7 +374,7 @@ function PureMultimodalInput({
|
||||||
<PromptInputSubmit
|
<PromptInputSubmit
|
||||||
status={status}
|
status={status}
|
||||||
disabled={!input.trim() || uploadQueue.length > 0}
|
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} />
|
<ArrowUpIcon size={14} />
|
||||||
</PromptInputSubmit>
|
</PromptInputSubmit>
|
||||||
|
|
@ -413,7 +413,7 @@ function PureAttachmentsButton({
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
data-testid="attachments-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) => {
|
onClick={(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
fileInputRef.current?.click();
|
fileInputRef.current?.click();
|
||||||
|
|
@ -452,18 +452,22 @@ function PureModelSelectorCompact({
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<PromptInputModelSelectTrigger
|
<SelectPrimitive.Trigger
|
||||||
type="button"
|
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'}
|
<CpuIcon size={16} />
|
||||||
</PromptInputModelSelectTrigger>
|
<span className="text-xs font-medium sm:block hidden">{selectedModel?.name}</span>
|
||||||
<PromptInputModelSelectContent className="max-w-[90vw] sm:max-w-[400px]">
|
<ChevronDownIcon size={16} />
|
||||||
|
</SelectPrimitive.Trigger>
|
||||||
|
<PromptInputModelSelectContent className="min-w-[260px] p-0">
|
||||||
{chatModels.map((model) => (
|
{chatModels.map((model) => (
|
||||||
<SelectItem key={model.id} value={model.name}>
|
<SelectItem key={model.id} value={model.name} className="px-3 py-2 text-xs">
|
||||||
<div className='flex flex-col items-start gap-1 py-1'>
|
<div className="flex flex-col min-w-0 flex-1">
|
||||||
<div className='font-medium text-sm'>{model.name}</div>
|
<div className="font-medium truncate text-xs">
|
||||||
<div className='line-clamp-2 text-muted-foreground text-xs'>
|
{model.name}
|
||||||
|
</div>
|
||||||
|
<div className="text-[10px] text-muted-foreground truncate leading-tight">
|
||||||
{model.description}
|
{model.description}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ const SelectContent = React.forwardRef<
|
||||||
<SelectScrollUpButton />
|
<SelectScrollUpButton />
|
||||||
<SelectPrimitive.Viewport
|
<SelectPrimitive.Viewport
|
||||||
className={cn(
|
className={cn(
|
||||||
'p-1',
|
'p-2',
|
||||||
position === 'popper' &&
|
position === 'popper' &&
|
||||||
'h-(--radix-select-trigger-height) w-full min-w-(--radix-select-trigger-width)',
|
'h-(--radix-select-trigger-height) w-full min-w-(--radix-select-trigger-width)',
|
||||||
)}
|
)}
|
||||||
|
|
@ -118,14 +118,14 @@ const SelectItem = React.forwardRef<
|
||||||
<SelectPrimitive.Item
|
<SelectPrimitive.Item
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
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,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...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>
|
<SelectPrimitive.ItemIndicator>
|
||||||
<Check className="h-4 w-4" />
|
<Check className="h-4 w-4 text-foreground" />
|
||||||
</SelectPrimitive.ItemIndicator>
|
</SelectPrimitive.ItemIndicator>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue