mirror of https://github.com/kortix-ai/suna.git
chore(ui): update input box
This commit is contained in:
parent
d6de70878a
commit
0ec8235d5c
|
@ -205,12 +205,12 @@ function DashboardContent() {
|
|||
</div>
|
||||
)}
|
||||
|
||||
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-[560px] max-w-[90%]">
|
||||
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-[650px] max-w-[90%]">
|
||||
<div className="flex flex-col items-center text-center mb-2 w-full">
|
||||
<h1 className={cn('tracking-tight text-4xl font-bold leading-tight')}>
|
||||
Welcome to <span className={cn(secondaryGradient)}>Suna</span>
|
||||
<h1 className={cn('tracking-tight text-4xl font-semibold leading-tight')}>
|
||||
Hey
|
||||
</h1>
|
||||
<p className="tracking-tight text-3xl font-semibold text-muted-foreground/80 mt-2 flex items-center gap-2">
|
||||
<p className="tracking-tight text-3xl font-normal text-muted-foreground/80 mt-2 flex items-center gap-2">
|
||||
What would you like Suna to do today?
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
@ -10,10 +10,9 @@ import React, {
|
|||
import { motion } from 'framer-motion';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { FileUploadHandler, handleFiles } from './file-upload-handler';
|
||||
import { handleFiles } from './file-upload-handler';
|
||||
import { MessageInput } from './message-input';
|
||||
import { UploadedFilesDisplay } from './uploaded-file-display';
|
||||
import { ModelSelector } from './model-selector';
|
||||
import { useModelSelection } from './_use-model-selection';
|
||||
|
||||
export interface ChatInputHandles {
|
||||
|
@ -167,9 +166,9 @@ export const ChatInput = forwardRef<ChatInputHandles, ChatInputProps>(
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="mx-auto w-full max-w-3xl px-4">
|
||||
<div className="mx-auto w-full max-w-4xl px-4">
|
||||
<Card
|
||||
className="shadow-none w-full max-w-3xl mx-auto bg-transparent border-none rounded-xl overflow-hidden"
|
||||
className="shadow-none w-full max-w-4xl mx-auto bg-transparent border-none rounded-xl overflow-hidden"
|
||||
onDragOver={handleDragOver}
|
||||
onDragLeave={handleDragLeave}
|
||||
onDrop={(e) => {
|
||||
|
@ -190,7 +189,7 @@ export const ChatInput = forwardRef<ChatInputHandles, ChatInputProps>(
|
|||
}}
|
||||
>
|
||||
<div className="w-full bg-muted/30 text-sm flex flex-col justify-between items-start rounded-lg border-b">
|
||||
<CardContent className="shadow w-full p-1.5 pb-2 pt-3 bg-background rounded-2xl border">
|
||||
<CardContent className="shadow w-full p-1.5 pb-2 pt-3 bg-sidebar rounded-2xl border">
|
||||
<UploadedFilesDisplay
|
||||
uploadedFiles={uploadedFiles}
|
||||
sandboxId={sandboxId}
|
||||
|
@ -209,33 +208,21 @@ export const ChatInput = forwardRef<ChatInputHandles, ChatInputProps>(
|
|||
onStopAgent={onStopAgent}
|
||||
isDraggingOver={isDraggingOver}
|
||||
uploadedFiles={uploadedFiles}
|
||||
|
||||
fileInputRef={fileInputRef}
|
||||
isUploading={isUploading}
|
||||
sandboxId={sandboxId}
|
||||
setPendingFiles={setPendingFiles}
|
||||
setUploadedFiles={setUploadedFiles}
|
||||
setIsUploading={setIsUploading}
|
||||
hideAttachments={hideAttachments}
|
||||
|
||||
selectedModel={selectedModel}
|
||||
onModelChange={handleModelChange}
|
||||
modelOptions={modelOptions}
|
||||
currentTier={subscriptionTier}
|
||||
canAccessModel={canAccessModel}
|
||||
/>
|
||||
|
||||
<div className="flex items-center justify-start mt-3 ml-3">
|
||||
<div className="flex items-center gap-3">
|
||||
{!hideAttachments && (
|
||||
<FileUploadHandler
|
||||
ref={fileInputRef}
|
||||
loading={loading}
|
||||
disabled={disabled}
|
||||
isAgentRunning={isAgentRunning}
|
||||
isUploading={isUploading}
|
||||
sandboxId={sandboxId}
|
||||
setPendingFiles={setPendingFiles}
|
||||
setUploadedFiles={setUploadedFiles}
|
||||
setIsUploading={setIsUploading}
|
||||
/>
|
||||
)}
|
||||
|
||||
<ModelSelector
|
||||
selectedModel={selectedModel}
|
||||
onModelChange={handleModelChange}
|
||||
modelOptions={modelOptions}
|
||||
currentTier={subscriptionTier}
|
||||
canAccessModel={canAccessModel}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</div>
|
||||
</Card>
|
||||
|
@ -257,4 +244,4 @@ export const ChatInput = forwardRef<ChatInputHandles, ChatInputProps>(
|
|||
},
|
||||
);
|
||||
|
||||
ChatInput.displayName = 'ChatInput';
|
||||
ChatInput.displayName = 'ChatInput';
|
|
@ -188,8 +188,8 @@ export const FileUploadHandler = forwardRef<
|
|||
type="button"
|
||||
onClick={handleFileUpload}
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-7 rounded-md text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
size="default"
|
||||
className="h-7 rounded-md text-muted-foreground"
|
||||
disabled={
|
||||
loading || (disabled && !isAgentRunning) || isUploading
|
||||
}
|
||||
|
@ -199,7 +199,7 @@ export const FileUploadHandler = forwardRef<
|
|||
) : (
|
||||
<Paperclip className="h-4 w-4" />
|
||||
)}
|
||||
<span className="text-xs">Attachments</span>
|
||||
<span className="text-sm">Attachments</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top">
|
||||
|
@ -220,8 +220,5 @@ export const FileUploadHandler = forwardRef<
|
|||
},
|
||||
);
|
||||
|
||||
// For easier access from other components
|
||||
FileUploadHandler.displayName = 'FileUploadHandler';
|
||||
|
||||
// Export the helper functions for use in other components
|
||||
export { handleFiles, handleLocalFiles, uploadFiles };
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
'use client';
|
||||
|
||||
import React, { forwardRef, useEffect } from 'react';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Square, Loader2, ArrowUp } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { UploadedFile } from './chat-input';
|
||||
import { FileUploadHandler } from './file-upload-handler';
|
||||
import { ModelSelector } from './model-selector';
|
||||
import { useModelSelection } from './_use-model-selection';
|
||||
|
||||
interface MessageInputProps {
|
||||
value: string;
|
||||
|
@ -18,6 +19,20 @@ interface MessageInputProps {
|
|||
onStopAgent?: () => void;
|
||||
isDraggingOver: boolean;
|
||||
uploadedFiles: UploadedFile[];
|
||||
|
||||
fileInputRef: React.RefObject<HTMLInputElement>;
|
||||
isUploading: boolean;
|
||||
sandboxId?: string;
|
||||
setPendingFiles: React.Dispatch<React.SetStateAction<File[]>>;
|
||||
setUploadedFiles: React.Dispatch<React.SetStateAction<UploadedFile[]>>;
|
||||
setIsUploading: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
hideAttachments?: boolean;
|
||||
|
||||
selectedModel: string;
|
||||
onModelChange: (model: string) => void;
|
||||
modelOptions: any[];
|
||||
currentTier: string;
|
||||
canAccessModel: (model: string) => boolean;
|
||||
}
|
||||
|
||||
export const MessageInput = forwardRef<HTMLTextAreaElement, MessageInputProps>(
|
||||
|
@ -33,6 +48,20 @@ export const MessageInput = forwardRef<HTMLTextAreaElement, MessageInputProps>(
|
|||
onStopAgent,
|
||||
isDraggingOver,
|
||||
uploadedFiles,
|
||||
|
||||
fileInputRef,
|
||||
isUploading,
|
||||
sandboxId,
|
||||
setPendingFiles,
|
||||
setUploadedFiles,
|
||||
setIsUploading,
|
||||
hideAttachments = false,
|
||||
|
||||
selectedModel,
|
||||
onModelChange,
|
||||
modelOptions,
|
||||
currentTier,
|
||||
canAccessModel,
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
|
@ -58,6 +87,10 @@ export const MessageInput = forwardRef<HTMLTextAreaElement, MessageInputProps>(
|
|||
return () => window.removeEventListener('resize', adjustHeight);
|
||||
}, [value, ref]);
|
||||
|
||||
const {
|
||||
subscriptionTier,
|
||||
} = useModelSelection();
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
|
@ -72,50 +105,81 @@ export const MessageInput = forwardRef<HTMLTextAreaElement, MessageInputProps>(
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="flex gap-2 px-2">
|
||||
<Textarea
|
||||
ref={ref}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder={placeholder}
|
||||
className={cn(
|
||||
'w-full bg-transparent dark:bg-transparent border-none shadow-none focus-visible:ring-0 px-2 py-1 text-base min-h-[40px] max-h-[200px] overflow-y-auto resize-none',
|
||||
isDraggingOver ? 'opacity-40' : '',
|
||||
)}
|
||||
disabled={loading || (disabled && !isAgentRunning)}
|
||||
rows={2}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
onClick={isAgentRunning && onStopAgent ? onStopAgent : onSubmit}
|
||||
size="icon"
|
||||
className={cn(
|
||||
'flex-shrink-0 self-end',
|
||||
isAgentRunning ? 'bg-red-500 hover:bg-red-600' : '',
|
||||
(!value.trim() && uploadedFiles.length === 0 && !isAgentRunning) ||
|
||||
loading ||
|
||||
(disabled && !isAgentRunning)
|
||||
? 'opacity-50'
|
||||
: '',
|
||||
)}
|
||||
disabled={
|
||||
(!value.trim() && uploadedFiles.length === 0 && !isAgentRunning) ||
|
||||
loading ||
|
||||
(disabled && !isAgentRunning)
|
||||
}
|
||||
>
|
||||
{loading ? (
|
||||
<Loader2 className="h-5 w-5 animate-spin" />
|
||||
) : isAgentRunning ? (
|
||||
<Square className="h-5 w-5" />
|
||||
) : (
|
||||
<ArrowUp className="h-5 w-5" />
|
||||
)}
|
||||
</Button>
|
||||
<div className="flex flex-col w-full h-auto gap-4 justify-between">
|
||||
<div className="flex gap-2 items-center px-2">
|
||||
<Textarea
|
||||
ref={ref}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder={placeholder}
|
||||
className={cn(
|
||||
'w-full bg-transparent dark:bg-transparent border-none shadow-none focus-visible:ring-0 px-2 py-1 text-base min-h-[40px] max-h-[200px] overflow-y-auto resize-none',
|
||||
isDraggingOver ? 'opacity-40' : '',
|
||||
)}
|
||||
disabled={loading || (disabled && !isAgentRunning)}
|
||||
rows={2}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between mt-1 ml-3 mb-1 pr-2">
|
||||
<div className="flex items-center gap-3">
|
||||
{!hideAttachments && (
|
||||
<FileUploadHandler
|
||||
ref={fileInputRef}
|
||||
loading={loading}
|
||||
disabled={disabled}
|
||||
isAgentRunning={isAgentRunning}
|
||||
isUploading={isUploading}
|
||||
sandboxId={sandboxId}
|
||||
setPendingFiles={setPendingFiles}
|
||||
setUploadedFiles={setUploadedFiles}
|
||||
setIsUploading={setIsUploading}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
</div>
|
||||
<div className='flex items-center gap-2'>
|
||||
<ModelSelector
|
||||
selectedModel={selectedModel}
|
||||
onModelChange={onModelChange}
|
||||
modelOptions={modelOptions}
|
||||
currentTier={subscriptionTier}
|
||||
canAccessModel={canAccessModel}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
onClick={isAgentRunning && onStopAgent ? onStopAgent : onSubmit}
|
||||
size="sm"
|
||||
className={cn(
|
||||
'w-7 h-7 flex-shrink-0 self-end',
|
||||
isAgentRunning ? 'bg-red-500 hover:bg-red-600' : '',
|
||||
(!value.trim() && uploadedFiles.length === 0 && !isAgentRunning) ||
|
||||
loading ||
|
||||
(disabled && !isAgentRunning)
|
||||
? 'opacity-50'
|
||||
: '',
|
||||
)}
|
||||
disabled={
|
||||
(!value.trim() && uploadedFiles.length === 0 && !isAgentRunning) ||
|
||||
loading ||
|
||||
(disabled && !isAgentRunning)
|
||||
}
|
||||
>
|
||||
{loading ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
) : isAgentRunning ? (
|
||||
<Square className="h-4 w-4" />
|
||||
) : (
|
||||
<ArrowUp className="h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
MessageInput.displayName = 'MessageInput';
|
||||
MessageInput.displayName = 'MessageInput';
|
|
@ -64,10 +64,10 @@ export const ModelSelector: React.FC<ModelSelectorProps> = ({
|
|||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
size='default'
|
||||
className="h-7 rounded-md text-muted-foreground shadow-none border-none focus:ring-0 w-auto px-2 py-0"
|
||||
>
|
||||
<div className="flex items-center gap-1 text-xs">
|
||||
<div className="flex items-center gap-1 text-sm">
|
||||
<span>{selectedModelLabel}</span>
|
||||
<ChevronDown className="h-3 w-3 opacity-50" />
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue