mirror of https://github.com/kortix-ai/suna.git
Merge pull request #1083 from kubet/feat/visual-improvements-and-fixes
Feat/visual improvements and fixes
This commit is contained in:
commit
8f2ea13fa2
|
@ -52,6 +52,7 @@ export default function ThreadPage({
|
|||
const [debugMode, setDebugMode] = useState(false);
|
||||
const [initialPanelOpenAttempted, setInitialPanelOpenAttempted] = useState(false);
|
||||
const [selectedAgentId, setSelectedAgentId] = useState<string | undefined>(undefined);
|
||||
const [isSidePanelAnimating, setIsSidePanelAnimating] = useState(false);
|
||||
|
||||
// Refs
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
|
@ -533,6 +534,12 @@ export default function ThreadPage({
|
|||
}
|
||||
}, [streamingToolCall, handleStreamingToolCall]);
|
||||
|
||||
useEffect(() => {
|
||||
setIsSidePanelAnimating(true);
|
||||
const timer = setTimeout(() => setIsSidePanelAnimating(false), 200); // Match transition duration
|
||||
return () => clearTimeout(timer);
|
||||
}, [isSidePanelOpen]);
|
||||
|
||||
if (!initialLoadCompleted || isLoading) {
|
||||
return <ThreadSkeleton isSidePanelOpen={isSidePanelOpen} />;
|
||||
}
|
||||
|
@ -616,6 +623,7 @@ export default function ThreadPage({
|
|||
isMobile={isMobile}
|
||||
initialLoadCompleted={initialLoadCompleted}
|
||||
agentName={agent && agent.name}
|
||||
disableInitialAnimation={!initialLoadCompleted && toolCalls.length > 0}
|
||||
>
|
||||
{/* {workflowId && (
|
||||
<div className="px-4 pt-4">
|
||||
|
@ -641,7 +649,8 @@ export default function ThreadPage({
|
|||
|
||||
<div
|
||||
className={cn(
|
||||
"fixed bottom-0 z-10 bg-gradient-to-t from-background via-background/90 to-transparent px-4 pt-8 transition-all duration-200 ease-in-out",
|
||||
"fixed bottom-0 z-10 bg-gradient-to-t from-background via-background/90 to-transparent px-4 pt-8",
|
||||
isSidePanelAnimating ? "" : "transition-all duration-200 ease-in-out",
|
||||
leftSidebarState === 'expanded' ? 'left-[72px] md:left-[256px]' : 'left-[72px]',
|
||||
isSidePanelOpen ? 'right-[90%] sm:right-[450px] md:right-[500px] lg:right-[550px] xl:right-[650px]' : 'right-0',
|
||||
isMobile ? 'left-0 right-0' : ''
|
||||
|
|
|
@ -39,6 +39,7 @@ interface ThreadLayoutProps {
|
|||
isMobile: boolean;
|
||||
initialLoadCompleted: boolean;
|
||||
agentName?: string;
|
||||
disableInitialAnimation?: boolean;
|
||||
}
|
||||
|
||||
export function ThreadLayout({
|
||||
|
@ -72,7 +73,8 @@ export function ThreadLayout({
|
|||
debugMode,
|
||||
isMobile,
|
||||
initialLoadCompleted,
|
||||
agentName
|
||||
agentName,
|
||||
disableInitialAnimation = false
|
||||
}: ThreadLayoutProps) {
|
||||
return (
|
||||
<div className="flex h-screen">
|
||||
|
@ -117,6 +119,7 @@ export function ThreadLayout({
|
|||
isLoading={!initialLoadCompleted || isLoading}
|
||||
onFileClick={onViewFiles}
|
||||
agentName={agentName}
|
||||
disableInitialAnimation={disableInitialAnimation}
|
||||
/>
|
||||
|
||||
{sandboxId && (
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import React from 'react';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { ChatInput } from '@/components/thread/chat-input/chat-input';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
interface ThreadSkeletonProps {
|
||||
isSidePanelOpen?: boolean;
|
||||
|
@ -12,6 +14,17 @@ export function ThreadSkeleton({
|
|||
showHeader = true,
|
||||
messageCount = 3,
|
||||
}: ThreadSkeletonProps) {
|
||||
// Mock handlers for the ChatInput component
|
||||
const handleSubmit = (message: string) => {
|
||||
// No-op for skeleton
|
||||
console.log('Skeleton submit:', message);
|
||||
};
|
||||
|
||||
const handleChange = (value: string) => {
|
||||
// No-op for skeleton
|
||||
console.log('Skeleton change:', value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-screen">
|
||||
<div
|
||||
|
@ -36,7 +49,7 @@ export function ThreadSkeleton({
|
|||
)}
|
||||
|
||||
{/* Skeleton Chat Messages */}
|
||||
<div className="flex-1 overflow-y-auto px-6 py-4 pb-[5.5rem]">
|
||||
<div className="flex-1 overflow-y-auto px-6 py-4 pb-72">
|
||||
<div className="mx-auto max-w-3xl space-y-6">
|
||||
{/* Generate multiple message skeletons based on messageCount */}
|
||||
{Array.from({ length: messageCount }).map((_, index) => (
|
||||
|
@ -102,20 +115,44 @@ export function ThreadSkeleton({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Skeleton Side Panel (closed state) */}
|
||||
{isSidePanelOpen && (
|
||||
<div className="hidden sm:block">
|
||||
<div className="h-screen w-[450px] border-l">
|
||||
<div className="p-4">
|
||||
<Skeleton className="h-8 w-32 mb-4" />
|
||||
<Skeleton className="h-20 w-full rounded-md mb-4" />
|
||||
<Skeleton className="h-40 w-full rounded-md" />
|
||||
</div>
|
||||
{/* ChatInput - Inside the left div, positioned at bottom with exact same styling */}
|
||||
<div
|
||||
className={cn(
|
||||
"bg-gradient-to-t from-background via-background/90 to-transparent px-0 pt-8 transition-all duration-200 ease-in-out"
|
||||
)}
|
||||
>
|
||||
<div className={cn(
|
||||
"mx-auto",
|
||||
"max-w-3xl"
|
||||
)}>
|
||||
<ChatInput
|
||||
onSubmit={handleSubmit}
|
||||
onChange={handleChange}
|
||||
placeholder="Describe what you need help with..."
|
||||
loading={false}
|
||||
disabled={true}
|
||||
isAgentRunning={false}
|
||||
value=""
|
||||
hideAttachments={false}
|
||||
isLoggedIn={true}
|
||||
hideAgentSelection={true}
|
||||
defaultShowSnackbar={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Side Panel - Always visible in skeleton with exact responsive widths */}
|
||||
<div className="hidden sm:block">
|
||||
<div className="h-screen w-[90%] sm:w-[450px] md:w-[500px] lg:w-[550px] xl:w-[650px] border-l">
|
||||
<div className="p-4">
|
||||
<Skeleton className="h-8 w-32 mb-4" />
|
||||
<Skeleton className="h-20 w-full rounded-md mb-4" />
|
||||
<Skeleton className="h-40 w-full rounded-md" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -48,6 +48,7 @@ interface ToolCallSidePanelProps {
|
|||
isLoading?: boolean;
|
||||
agentName?: string;
|
||||
onFileClick?: (filePath: string) => void;
|
||||
disableInitialAnimation?: boolean;
|
||||
}
|
||||
|
||||
interface ToolCallSnapshot {
|
||||
|
@ -73,6 +74,7 @@ export function ToolCallSidePanel({
|
|||
externalNavigateToIndex,
|
||||
agentName,
|
||||
onFileClick,
|
||||
disableInitialAnimation,
|
||||
}: ToolCallSidePanelProps) {
|
||||
const [dots, setDots] = React.useState('');
|
||||
const [internalIndex, setInternalIndex] = React.useState(0);
|
||||
|
@ -678,11 +680,11 @@ export function ToolCallSidePanel({
|
|||
<motion.div
|
||||
key="sidepanel"
|
||||
layoutId={FLOATING_LAYOUT_ID}
|
||||
initial={{ opacity: 0 }}
|
||||
initial={disableInitialAnimation ? { opacity: 1 } : { opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{
|
||||
opacity: { duration: 0.15 },
|
||||
opacity: { duration: disableInitialAnimation ? 0 : 0.15 },
|
||||
layout: {
|
||||
type: "spring",
|
||||
stiffness: 400,
|
||||
|
|
|
@ -285,7 +285,7 @@ export function FileOperationToolView({
|
|||
</div>
|
||||
<div className='flex items-center gap-2'>
|
||||
{isHtml && htmlPreviewUrl && !isStreaming && (
|
||||
<Button variant="outline" size="sm" className="h-8 text-xs bg-white dark:bg-zinc-900 hover:bg-zinc-100 dark:hover:bg-zinc-800" asChild>
|
||||
<Button variant="outline" size="sm" className="h-8 text-xs bg-white dark:bg-muted/50 hover:bg-zinc-100 dark:hover:bg-zinc-800 shadow-none" asChild>
|
||||
<a href={htmlPreviewUrl} target="_blank" rel="noopener noreferrer">
|
||||
<ExternalLink className="h-3.5 w-3.5 mr-1.5" />
|
||||
Open in Browser
|
||||
|
@ -295,14 +295,14 @@ export function FileOperationToolView({
|
|||
<TabsList className="h-8 bg-muted/50 border border-border/50 p-0.5 gap-1">
|
||||
<TabsTrigger
|
||||
value="code"
|
||||
className="flex items-center gap-1.5 px-4 py-2 text-xs font-medium transition-all [&[data-state=active]]:bg-primary/10 [&[data-state=active]]:text-foreground hover:bg-background/50 text-muted-foreground"
|
||||
className="flex items-center gap-1.5 px-4 py-2 text-xs font-medium transition-all [&[data-state=active]]:bg-white [&[data-state=active]]:dark:bg-primary/10 [&[data-state=active]]:text-foreground hover:bg-background/50 text-muted-foreground shadow-none"
|
||||
>
|
||||
<Code className="h-3.5 w-3.5" />
|
||||
Source
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="preview"
|
||||
className="flex items-center gap-1.5 px-4 py-2 text-xs font-medium transition-all [&[data-state=active]]:bg-primary/10 [&[data-state=active]]:text-foreground hover:bg-background/50 text-muted-foreground"
|
||||
className="flex items-center gap-1.5 px-4 py-2 text-xs font-medium transition-all [&[data-state=active]]:bg-white [&[data-state=active]]:dark:bg-primary/10 [&[data-state=active]]:text-foreground hover:bg-background/50 text-muted-foreground shadow-none"
|
||||
>
|
||||
<Eye className="h-3.5 w-3.5" />
|
||||
Preview
|
||||
|
|
Loading…
Reference in New Issue