Display the attached images inside the chat input area on the web app

- Put the attached images display div inside the same parent div as
  the text area
- Keep the attachment, microphone/send message buttons aligned with
  the text area. So the attached images just show up at the top of the
  text area but everything else stays at the same horizontal height as
  before.

- This improves the UX by
  - Ensuring that the attached images do not obscure the agents pane
    above the chat input area
  - The attached images visually look like they are inside the actual
    input area, rather than floating above it. So the visual aligns
    with the semantics
This commit is contained in:
Debanjum Singh Solanky
2024-10-19 16:29:45 -07:00
parent 3e39fac455
commit 58a331227d

View File

@@ -420,32 +420,11 @@ export default function ChatInputArea(props: ChatInputProps) {
</div> </div>
)} )}
<div <div
className={`${styles.actualInputArea} items-center justify-between dark:bg-neutral-700 relative ${isDragAndDropping && "animate-pulse"}`} className={`${styles.actualInputArea} justify-between dark:bg-neutral-700 relative ${isDragAndDropping && "animate-pulse"}`}
onDragOver={handleDragOver} onDragOver={handleDragOver}
onDragLeave={handleDragLeave} onDragLeave={handleDragLeave}
onDrop={handleDragAndDropFiles} onDrop={handleDragAndDropFiles}
> >
{imageUploaded && (
<div className="absolute bottom-full left-0 right-0 px-12 py-2 dark:bg-neutral-700 bg-white w-full rounded-t-lg border dark:border-none flex items-center space-x-2 overflow-x-auto">
{imagePaths.map((path, index) => (
<div key={index} className="relative flex-shrink-0 group">
<img
src={path}
alt={`img-${index}`}
className="w-auto h-16 object-cover rounded-xl"
/>
<Button
variant="ghost"
size="icon"
className="absolute -top-2 -right-2 h-5 w-5 rounded-full bg-neutral-200 dark:bg-neutral-600 hover:bg-neutral-300 dark:hover:bg-neutral-500 opacity-0 group-hover:opacity-100 transition-opacity"
onClick={() => removeImageUpload(index)}
>
<X className="h-3 w-3" />
</Button>
</div>
))}
</div>
)}
<input <input
type="file" type="file"
multiple={true} multiple={true}
@@ -453,15 +432,37 @@ export default function ChatInputArea(props: ChatInputProps) {
onChange={handleFileChange} onChange={handleFileChange}
style={{ display: "none" }} style={{ display: "none" }}
/> />
<Button <div className="flex items-end pb-4">
variant={"ghost"} <Button
className="!bg-none p-0 m-2 h-auto text-3xl rounded-full text-gray-300 hover:text-gray-500" variant={"ghost"}
disabled={props.sendDisabled} className="!bg-none p-0 m-2 h-auto text-3xl rounded-full text-gray-300 hover:text-gray-500"
onClick={handleFileButtonClick} disabled={props.sendDisabled}
> onClick={handleFileButtonClick}
<Paperclip className="w-8 h-8" /> >
</Button> <Paperclip className="w-8 h-8" />
<div className="grid w-full gap-1.5 relative"> </Button>
</div>
<div className="flex-grow flex flex-col w-full gap-1.5 relative pb-2">
<div className="flex flex-wrap items-center gap-2">
{imageUploaded &&
imagePaths.map((path, index) => (
<div key={index} className="relative flex-shrink-0 group">
<img
src={path}
alt={`img-${index}`}
className="w-auto h-16 object-cover rounded-xl"
/>
<Button
variant="ghost"
size="icon"
className="absolute -top-2 -right-2 h-5 w-5 rounded-full bg-neutral-200 dark:bg-neutral-600 hover:bg-neutral-300 dark:hover:bg-neutral-500 opacity-0 group-hover:opacity-100 transition-opacity"
onClick={() => removeImageUpload(index)}
>
<X className="h-3 w-3" />
</Button>
</div>
))}
</div>
<Textarea <Textarea
ref={chatInputRef} ref={chatInputRef}
className={`border-none w-full h-16 min-h-16 max-h-[128px] md:py-4 rounded-lg resize-none dark:bg-neutral-700 ${props.isMobileWidth ? "text-md" : "text-lg"}`} className={`border-none w-full h-16 min-h-16 max-h-[128px] md:py-4 rounded-lg resize-none dark:bg-neutral-700 ${props.isMobileWidth ? "text-md" : "text-lg"}`}
@@ -481,57 +482,59 @@ export default function ChatInputArea(props: ChatInputProps) {
disabled={props.sendDisabled || recording} disabled={props.sendDisabled || recording}
/> />
</div> </div>
{recording ? ( <div className="flex items-end pb-4">
<TooltipProvider> {recording ? (
<Tooltip> <TooltipProvider>
<TooltipTrigger asChild> <Tooltip>
<Button <TooltipTrigger asChild>
variant="default" <Button
className={`${!recording && "hidden"} ${props.agentColor ? convertToBGClass(props.agentColor) : "bg-orange-300 hover:bg-orange-500"} rounded-full p-1 m-2 h-auto text-3xl transition transform md:hover:-translate-y-1`} variant="default"
onClick={() => { className={`${!recording && "hidden"} ${props.agentColor ? convertToBGClass(props.agentColor) : "bg-orange-300 hover:bg-orange-500"} rounded-full p-1 m-2 h-auto text-3xl transition transform md:hover:-translate-y-1`}
setRecording(!recording); onClick={() => {
}} setRecording(!recording);
disabled={props.sendDisabled} }}
> disabled={props.sendDisabled}
<Stop weight="fill" className="w-6 h-6" /> >
</Button> <Stop weight="fill" className="w-6 h-6" />
</TooltipTrigger> </Button>
<TooltipContent> </TooltipTrigger>
Click to stop recording and transcribe your voice. <TooltipContent>
</TooltipContent> Click to stop recording and transcribe your voice.
</Tooltip> </TooltipContent>
</TooltipProvider> </Tooltip>
) : mediaRecorder ? ( </TooltipProvider>
<InlineLoading /> ) : mediaRecorder ? (
) : ( <InlineLoading />
<TooltipProvider> ) : (
<Tooltip> <TooltipProvider>
<TooltipTrigger asChild> <Tooltip>
<Button <TooltipTrigger asChild>
variant="default" <Button
className={`${!message || recording || "hidden"} ${props.agentColor ? convertToBGClass(props.agentColor) : "bg-orange-300 hover:bg-orange-500"} rounded-full p-1 m-2 h-auto text-3xl transition transform md:hover:-translate-y-1`} variant="default"
onClick={() => { className={`${!message || recording || "hidden"} ${props.agentColor ? convertToBGClass(props.agentColor) : "bg-orange-300 hover:bg-orange-500"} rounded-full p-1 m-2 h-auto text-3xl transition transform md:hover:-translate-y-1`}
setMessage("Listening..."); onClick={() => {
setRecording(!recording); setMessage("Listening...");
}} setRecording(!recording);
disabled={props.sendDisabled} }}
> disabled={props.sendDisabled}
<Microphone weight="fill" className="w-6 h-6" /> >
</Button> <Microphone weight="fill" className="w-6 h-6" />
</TooltipTrigger> </Button>
<TooltipContent> </TooltipTrigger>
Click to transcribe your message with voice. <TooltipContent>
</TooltipContent> Click to transcribe your message with voice.
</Tooltip> </TooltipContent>
</TooltipProvider> </Tooltip>
)} </TooltipProvider>
<Button )}
className={`${(!message || recording) && "hidden"} ${props.agentColor ? convertToBGClass(props.agentColor) : "bg-orange-300 hover:bg-orange-500"} rounded-full p-1 m-2 h-auto text-3xl transition transform md:hover:-translate-y-1`} <Button
onClick={onSendMessage} className={`${(!message || recording) && "hidden"} ${props.agentColor ? convertToBGClass(props.agentColor) : "bg-orange-300 hover:bg-orange-500"} rounded-full p-1 m-2 h-auto text-3xl transition transform md:hover:-translate-y-1`}
disabled={props.sendDisabled} onClick={onSendMessage}
> disabled={props.sendDisabled}
<ArrowUp className="w-6 h-6" weight="bold" /> >
</Button> <ArrowUp className="w-6 h-6" weight="bold" />
</Button>
</div>
</div> </div>
</> </>
); );