mirror of
https://github.com/stackblitz/bolt.new
synced 2025-06-26 18:17:50 +00:00
feat: chat restore
added a basic chat restore
This commit is contained in:
commit
adcd6978d7
@ -12,6 +12,7 @@ import { Menu } from '~/components/sidebar/Menu.client';
|
||||
import { IconButton } from '~/components/ui/IconButton';
|
||||
import { Workbench } from '~/components/workbench/Workbench.client';
|
||||
import { classNames } from '~/utils/classNames';
|
||||
import { ChatHistory } from './ChatHistory.client';
|
||||
|
||||
interface BaseChatProps {
|
||||
textareaRef?: React.RefObject<HTMLTextAreaElement | null> | undefined;
|
||||
@ -198,6 +199,7 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
|
||||
>
|
||||
<div className="i-ph:image text-xl" />
|
||||
</IconButton>
|
||||
<ClientOnly>{() => <ChatHistory />}</ClientOnly>
|
||||
</div>
|
||||
{input.length > 3 ? (
|
||||
<div className="text-xs text-bolt-elements-textTertiary">
|
||||
|
||||
81
app/components/chat/ChatHistory.client.tsx
Normal file
81
app/components/chat/ChatHistory.client.tsx
Normal file
@ -0,0 +1,81 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import type { ChatHistoryItem } from '~/lib/persistence/useChatHistory';
|
||||
import { db } from '~/lib/persistence/useChatHistory';
|
||||
import { getAll } from '~/lib/persistence/db';
|
||||
import { useNavigate, useLoaderData } from '@remix-run/react';
|
||||
import { IconButton } from '~/components/ui/IconButton';
|
||||
import { classNames } from '~/utils/classNames';
|
||||
import { createScopedLogger } from '~/utils/logger';
|
||||
|
||||
const logger = createScopedLogger('ChatHistory');
|
||||
|
||||
export function ChatHistory() {
|
||||
const [history, setHistory] = useState<ChatHistoryItem[]>([]);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
const { id: currentId } = useLoaderData<{ id?: string }>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!db) return;
|
||||
|
||||
const loadHistory = async () => {
|
||||
try {
|
||||
const items = await getAll(db!);
|
||||
// Filter items to only show history for the current chat
|
||||
const filteredItems = items
|
||||
.filter(item => item.urlId === currentId || item.id === currentId)
|
||||
.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
||||
setHistory(filteredItems);
|
||||
} catch (error) {
|
||||
logger.error('Failed to load chat history', error);
|
||||
}
|
||||
};
|
||||
|
||||
loadHistory();
|
||||
}, [currentId]);
|
||||
|
||||
const handleRestore = (item: ChatHistoryItem) => {
|
||||
navigate(`/chat/${item.id}`);
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
if (!db || history.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<IconButton
|
||||
title="Chat History"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className={classNames({
|
||||
'text-bolt-elements-item-contentAccent!': isOpen,
|
||||
})}
|
||||
>
|
||||
<div className="i-ph:clock-counter-clockwise text-xl" />
|
||||
</IconButton>
|
||||
|
||||
{isOpen && (
|
||||
<div className="absolute bottom-full left-0 mb-2 w-[300px] max-h-[400px] overflow-y-auto bg-bolt-elements-background-depth-2 border border-bolt-elements-borderColor rounded-lg shadow-lg">
|
||||
<div className="p-2">
|
||||
<h3 className="text-sm font-medium text-bolt-elements-textPrimary px-2 py-1">Chat History</h3>
|
||||
<div className="mt-1">
|
||||
{history.map((item) => (
|
||||
<button
|
||||
key={item.id}
|
||||
onClick={() => handleRestore(item)}
|
||||
className="w-full text-left px-2 py-1.5 hover:bg-bolt-elements-item-backgroundHover rounded transition-colors"
|
||||
>
|
||||
<div className="text-sm text-bolt-elements-textPrimary truncate">
|
||||
{item.description || `Version ${new Date(item.timestamp).toLocaleTimeString()}`}
|
||||
</div>
|
||||
<div className="text-xs text-bolt-elements-textTertiary">
|
||||
{new Date(item.timestamp).toLocaleDateString()} {new Date(item.timestamp).toLocaleTimeString()}
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user