Conversation history
Persist a conversation with its design and resume it later.
A conversation with the assistant has a name, its messages, and the design it worked on. You can keep it in your own database and open it again later, with the design as the conversation left it.
Storage
Pass a name for the conversation as chatSessionId, and two functions: onSaveChatSession stores the record, and onGetChatSession returns it:
import { VideoEditor, type ChatSession } from '@layermetry/media-editor/react';
// Your backend stores one record per conversation. This example keeps them in localStorage.
async function saveChatSession(session: ChatSession) {
localStorage.setItem(`chat:${session.id}`, JSON.stringify(session));
console.log(`Saved ${session.id}: ${session.messages.length} messages, design ${session.draft ? 'included' : 'none'}`);
}
async function getChatSession(sessionId: string): Promise<ChatSession | null> {
const stored = localStorage.getItem(`chat:${sessionId}`);
console.log(`Loaded ${sessionId}: ${stored ? 'found' : 'new conversation'}`);
return stored ? JSON.parse(stored) : null;
}
export default function App() {
return (
<div style={{ position: 'fixed', inset: 0 }}>
<VideoEditor
licenseKey="YOUR_LICENSE_KEY"
onClose={() => console.log('The editor was closed')}
mediaApiUrl="https://media.example.com"
chatSessionId="campaign-42"
onSaveChatSession={saveChatSession}
onGetChatSession={getChatSession}
/>
</div>
);
}The editor calls onSaveChatSession:
- when the user opens a conversation that your database does not have yet, so it has a record before the first message;
- at the end of every request;
- for both conversations when the user presses New chat;
- once more, without waiting, when the editor closes.
The record has these fields:
| Field | Type | Description |
|---|---|---|
id | string | The name of the conversation, the same as chatSessionId. |
schemaVersion | number | The version of the record format. It is 1. |
messages | array | The messages, in the format that the chat service reads. |
draft | object | The design, as a template record. See Saving and loading. |
lastTurnId | string or null | The last request that the editor's panel ran. |
createdAt, updatedAt | string | Dates in ISO 8601 format. |
The editor waits for the Promise of onSaveChatSession. If it rejects, or takes longer than 30 seconds, the panel shows "Not saved — retry" with a button.
When you pass chatSessionId or onSaveChatSession, the editor stops keeping the conversation in the browser. Your database then holds the only copy.
Pass onUploadAsset too. The design in each record is built like a saved template, so without it every picture from the user's computer is stored inside the record, on every save.
Default: without these props, the editor names each conversation itself and keeps it in the browser only. The console says so once.
Resuming a conversation
Pass the same chatSessionId again. When the assistant panel opens, the editor calls onGetChatSession. The record you return brings back the messages and the design:
What your function returns decides what happens:
| Return value | What happens |
|---|---|
A record with draft | The messages and the design open. The design wins over defaultTemplate. |
A record without draft | The messages open. The canvas keeps defaultTemplate, or stays empty. |
A record with an empty messages list | The conversation exists and is new. |
null | Your database does not know the name. The editor starts an empty conversation with that name, and saves it. |
Without onGetChatSession, pass the stored messages as initialMessages instead. That restores the messages only, not the design.
A design whose pictures are no longer in your storage still opens. The console names each missing item, and the rest is drawn.
To follow the conversation as it happens, use onChatEvent. See Custom chat UI.