feat: enhance timeline UI with slide-in animation and improved layout

- Add slide-in animation for timeline panel using framer-motion
- Implement responsive layout adjustments when timeline is open
- Add close button (X icon) to timeline header for better UX
- Wrap timeline in AnimatePresence for smooth enter/exit transitions
- Adjust main content and floating prompt input positioning when timeline is visible
- Improve timeline visual hierarchy with proper header and content sections
- Add spring animation with damping for smooth timeline panel transitions
This commit is contained in:
Vivek R
2025-07-02 19:35:38 +05:30
parent 2dfdf31b83
commit 443e8e157a

View File

@@ -9,7 +9,8 @@ import {
ChevronDown, ChevronDown,
GitBranch, GitBranch,
Settings, Settings,
Globe Globe,
X
} from "lucide-react"; } from "lucide-react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
@@ -971,7 +972,10 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
</motion.div> </motion.div>
{/* Main Content Area */} {/* Main Content Area */}
<div className="flex-1 overflow-hidden"> <div className={cn(
"flex-1 overflow-hidden transition-all duration-300",
showTimeline && "sm:mr-96"
)}>
{showPreview ? ( {showPreview ? (
// Split pane layout when preview is active // Split pane layout when preview is active
<SplitPane <SplitPane
@@ -1018,29 +1022,62 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
</div> </div>
{/* Floating Prompt Input - Always visible */} {/* Floating Prompt Input - Always visible */}
<ErrorBoundary> <div className={cn(
<FloatingPromptInput "fixed bottom-0 left-0 right-0 transition-all duration-300 z-50",
ref={floatingPromptRef} showTimeline && "sm:right-96"
onSend={handleSendPrompt} )}>
onCancel={handleCancelExecution} <ErrorBoundary>
isLoading={isLoading} <FloatingPromptInput
disabled={!projectPath} ref={floatingPromptRef}
projectPath={projectPath} onSend={handleSendPrompt}
/> onCancel={handleCancelExecution}
</ErrorBoundary> isLoading={isLoading}
disabled={!projectPath}
projectPath={projectPath}
/>
</ErrorBoundary>
</div>
{/* Timeline */} {/* Timeline */}
{showTimeline && effectiveSession && ( <AnimatePresence>
<TimelineNavigator {showTimeline && effectiveSession && (
sessionId={effectiveSession.id} <motion.div
projectId={effectiveSession.project_id} initial={{ x: "100%" }}
projectPath={projectPath} animate={{ x: 0 }}
currentMessageIndex={messages.length - 1} exit={{ x: "100%" }}
onCheckpointSelect={handleCheckpointSelect} transition={{ type: "spring", damping: 20, stiffness: 300 }}
onFork={handleFork} className="fixed right-0 top-0 h-full w-full sm:w-96 bg-background border-l border-border shadow-xl z-30 overflow-hidden"
refreshVersion={timelineVersion} >
/> <div className="h-full flex flex-col">
)} {/* Timeline Header */}
<div className="flex items-center justify-between p-4 border-b border-border">
<h3 className="text-lg font-semibold">Session Timeline</h3>
<Button
variant="ghost"
size="icon"
onClick={() => setShowTimeline(false)}
className="h-8 w-8"
>
<X className="h-4 w-4" />
</Button>
</div>
{/* Timeline Content */}
<div className="flex-1 overflow-y-auto p-4">
<TimelineNavigator
sessionId={effectiveSession.id}
projectId={effectiveSession.project_id}
projectPath={projectPath}
currentMessageIndex={messages.length - 1}
onCheckpointSelect={handleCheckpointSelect}
onFork={handleFork}
refreshVersion={timelineVersion}
/>
</div>
</div>
</motion.div>
)}
</AnimatePresence>
</div> </div>
{/* Fork Dialog */} {/* Fork Dialog */}