Add ability to return to older chat message state
Browse files
app/components/chat/Messages.client.tsx
CHANGED
@@ -3,6 +3,7 @@ import React from 'react';
|
|
3 |
import { classNames } from '~/utils/classNames';
|
4 |
import { AssistantMessage } from './AssistantMessage';
|
5 |
import { UserMessage } from './UserMessage';
|
|
|
6 |
|
7 |
interface MessagesProps {
|
8 |
id?: string;
|
@@ -13,12 +14,21 @@ interface MessagesProps {
|
|
13 |
|
14 |
export const Messages = React.forwardRef<HTMLDivElement, MessagesProps>((props: MessagesProps, ref) => {
|
15 |
const { id, isStreaming = false, messages = [] } = props;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
|
17 |
return (
|
18 |
<div id={id} ref={ref} className={props.className}>
|
19 |
{messages.length > 0
|
20 |
? messages.map((message, index) => {
|
21 |
-
const { role, content } = message;
|
22 |
const isUserMessage = role === 'user';
|
23 |
const isFirst = index === 0;
|
24 |
const isLast = index === messages.length - 1;
|
@@ -41,6 +51,15 @@ export const Messages = React.forwardRef<HTMLDivElement, MessagesProps>((props:
|
|
41 |
<div className="grid grid-col-1 w-full">
|
42 |
{isUserMessage ? <UserMessage content={content} /> : <AssistantMessage content={content} />}
|
43 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
</div>
|
45 |
);
|
46 |
})
|
|
|
3 |
import { classNames } from '~/utils/classNames';
|
4 |
import { AssistantMessage } from './AssistantMessage';
|
5 |
import { UserMessage } from './UserMessage';
|
6 |
+
import { useLocation, useNavigate } from '@remix-run/react';
|
7 |
|
8 |
interface MessagesProps {
|
9 |
id?: string;
|
|
|
14 |
|
15 |
export const Messages = React.forwardRef<HTMLDivElement, MessagesProps>((props: MessagesProps, ref) => {
|
16 |
const { id, isStreaming = false, messages = [] } = props;
|
17 |
+
const location = useLocation();
|
18 |
+
const navigate = useNavigate();
|
19 |
+
|
20 |
+
const handleRewind = (messageId: string) => {
|
21 |
+
const searchParams = new URLSearchParams(location.search);
|
22 |
+
searchParams.set('rewindId', messageId);
|
23 |
+
window.location.search = searchParams.toString();
|
24 |
+
//navigate(`${location.pathname}?${searchParams.toString()}`);
|
25 |
+
};
|
26 |
|
27 |
return (
|
28 |
<div id={id} ref={ref} className={props.className}>
|
29 |
{messages.length > 0
|
30 |
? messages.map((message, index) => {
|
31 |
+
const { role, content, id: messageId } = message;
|
32 |
const isUserMessage = role === 'user';
|
33 |
const isFirst = index === 0;
|
34 |
const isLast = index === messages.length - 1;
|
|
|
51 |
<div className="grid grid-col-1 w-full">
|
52 |
{isUserMessage ? <UserMessage content={content} /> : <AssistantMessage content={content} />}
|
53 |
</div>
|
54 |
+
{messageId && (
|
55 |
+
<button
|
56 |
+
onClick={() => handleRewind(messageId)}
|
57 |
+
className="self-start p-2 text-bolt-elements-textSecondary hover:text-bolt-elements-textPrimary"
|
58 |
+
title="Rewind to this message"
|
59 |
+
>
|
60 |
+
<div className="i-ph:arrow-counter-clockwise text-xl"></div>
|
61 |
+
</button>
|
62 |
+
)}
|
63 |
</div>
|
64 |
);
|
65 |
})
|
app/lib/persistence/useChatHistory.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
import { useLoaderData, useNavigate } from '@remix-run/react';
|
2 |
import { useState, useEffect } from 'react';
|
3 |
import { atom } from 'nanostores';
|
4 |
import type { Message } from 'ai';
|
@@ -24,6 +24,7 @@ export const description = atom<string | undefined>(undefined);
|
|
24 |
export function useChatHistory() {
|
25 |
const navigate = useNavigate();
|
26 |
const { id: mixedId } = useLoaderData<{ id?: string }>();
|
|
|
27 |
|
28 |
const [initialMessages, setInitialMessages] = useState<Message[]>([]);
|
29 |
const [ready, setReady] = useState<boolean>(false);
|
@@ -44,7 +45,13 @@ export function useChatHistory() {
|
|
44 |
getMessages(db, mixedId)
|
45 |
.then((storedMessages) => {
|
46 |
if (storedMessages && storedMessages.messages.length > 0) {
|
47 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
setUrlId(storedMessages.urlId);
|
49 |
description.set(storedMessages.description);
|
50 |
chatId.set(storedMessages.id);
|
|
|
1 |
+
import { useLoaderData, useNavigate, useSearchParams } from '@remix-run/react';
|
2 |
import { useState, useEffect } from 'react';
|
3 |
import { atom } from 'nanostores';
|
4 |
import type { Message } from 'ai';
|
|
|
24 |
export function useChatHistory() {
|
25 |
const navigate = useNavigate();
|
26 |
const { id: mixedId } = useLoaderData<{ id?: string }>();
|
27 |
+
const [searchParams] = useSearchParams();
|
28 |
|
29 |
const [initialMessages, setInitialMessages] = useState<Message[]>([]);
|
30 |
const [ready, setReady] = useState<boolean>(false);
|
|
|
45 |
getMessages(db, mixedId)
|
46 |
.then((storedMessages) => {
|
47 |
if (storedMessages && storedMessages.messages.length > 0) {
|
48 |
+
const rewindId = searchParams.get('rewindId');
|
49 |
+
const filteredMessages = rewindId
|
50 |
+
? storedMessages.messages.slice(0,
|
51 |
+
storedMessages.messages.findIndex(m => m.id === rewindId) + 1)
|
52 |
+
: storedMessages.messages;
|
53 |
+
|
54 |
+
setInitialMessages(filteredMessages);
|
55 |
setUrlId(storedMessages.urlId);
|
56 |
description.set(storedMessages.description);
|
57 |
chatId.set(storedMessages.id);
|