wuyiqunLu
feat: add switch to toggle self reflection (#44)
34afd2e unverified
raw
history blame
No virus
6.87 kB
'use client';
import * as React from 'react';
import { type UseChatHelpers } from 'ai/react';
import Textarea from 'react-textarea-autosize';
import { Button } from '@/components/ui/Button';
import { MessageBase } from '../../lib/types';
import { useEnterSubmit } from '@/lib/hooks/useEnterSubmit';
import Img from '../ui/Img';
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from '@/components/ui/Tooltip';
import {
IconArrowDown,
IconArrowElbow,
IconRefresh,
IconStop,
} from '@/components/ui/Icons';
import { cn } from '@/lib/utils';
import { generateInputImageMarkdown } from '@/lib/messageUtils';
import { Switch } from '../ui/Switch';
export interface ComposerProps
extends Pick<
UseChatHelpers,
'append' | 'isLoading' | 'reload' | 'stop' | 'input' | 'setInput'
> {
id?: string;
title?: string;
messages: MessageBase[];
url?: string;
isAtBottom: boolean;
scrollToBottom: () => void;
enableSelfReflection: boolean;
setEnableSelfReflection: (value: boolean) => void;
}
export function Composer({
id,
title,
isLoading,
stop,
append,
reload,
input,
setInput,
messages,
isAtBottom,
scrollToBottom,
enableSelfReflection,
setEnableSelfReflection,
url,
}: ComposerProps) {
const { formRef, onKeyDown } = useEnterSubmit();
const inputRef = React.useRef<HTMLTextAreaElement>(null);
React.useEffect(() => {
if (inputRef.current) {
inputRef.current.focus();
}
}, []);
return (
// <div className="fixed inset-x-0 bottom-0 w-full animate-in duration-300 ease-in-out peer-[[data-state=open]]:group-[]:lg:pl-[250px] peer-[[data-state=open]]:group-[]:xl:pl-[300px] h-[178px]">
<div className="mx-auto sm:max-w-3xl sm:px-4 h-full">
<div className="px-4 py-2 space-y-4 border-t shadow-lg bg-background sm:rounded-t-xl sm:border md:py-4 h-full">
<form
onSubmit={async e => {
e.preventDefault();
if (!input?.trim()) {
return;
}
setInput('');
await append({
id,
content:
input + (url ? '\n\n' + generateInputImageMarkdown(url) : ''),
role: 'user',
});
scrollToBottom();
}}
ref={formRef}
className="h-full"
>
<div className="relative flex px-8 pl-2 overflow-hidden size-full bg-background sm:rounded-md sm:border sm:px-12 sm:pl-2 items-start">
{url && (
<div className="w-1/5 p-2 h-full flex items-center justify-center relative">
<Tooltip>
<TooltipTrigger asChild>
<Img
src={url}
className="cursor-zoom-in"
alt="preview-image"
/>
</TooltipTrigger>
<TooltipContent>
<Img
src={url}
className="m-2"
quality={100}
width={500}
alt="zoomed-in-image"
/>
</TooltipContent>
</Tooltip>
</div>
)}
<div className="flex flex-col gap-2 w-4/5 px-4">
<Textarea
ref={inputRef}
tabIndex={0}
onKeyDown={onKeyDown}
rows={1}
value={input}
disabled={isLoading}
onChange={e => setInput(e.target.value)}
placeholder={
isLoading
? 'Vision Agent is thinking...'
: 'Ask question about the image.'
}
spellCheck={false}
className="min-h-[60px] resize-none bg-transparent py-[1.3em] focus-within:outline-none sm:text-sm"
/>
<div className="flex items-center gap-2 mt-4">
<Switch
checked={enableSelfReflection}
onCheckedChange={checked => {
setEnableSelfReflection(checked);
}}
/>
<p className="text-sm">Self Reflection</p>
</div>
</div>
{/* Scroll to bottom Icon */}
<div
className={cn(
'absolute top-3 right-4 transition-opacity duration-300',
isAtBottom ? 'opacity-0' : 'opacity-100',
)}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="outline"
size="icon"
className="bg-background"
onClick={() => scrollToBottom()}
>
<IconArrowDown />
</Button>
</TooltipTrigger>
<TooltipContent>Scroll to bottom</TooltipContent>
</Tooltip>
</div>
{/* Stop / Regenerate Icon */}
<div className="absolute bottom-14 right-4">
{isLoading ? (
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="outline"
size="icon"
className="bg-background"
onClick={() => stop()}
>
<IconStop />
</Button>
</TooltipTrigger>
<TooltipContent>Stop generating</TooltipContent>
</Tooltip>
) : (
messages?.length >= 2 && (
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="outline"
size="icon"
className="bg-background"
onClick={() => reload()}
>
<IconRefresh />
</Button>
</TooltipTrigger>
<TooltipContent>Regenerate response</TooltipContent>
</Tooltip>
)
)}
</div>
{/* Submit Icon */}
<div className="absolute bottom-3 right-4">
<Tooltip>
<TooltipTrigger asChild>
<Button
type="submit"
size="icon"
disabled={isLoading || input === ''}
>
<IconArrowElbow />
</Button>
</TooltipTrigger>
<TooltipContent>Send message</TooltipContent>
</Tooltip>
</div>
</div>
</form>
</div>
</div>
// </div>
);
}