mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-05-10 23:10:52 +00:00
feat: make user made changes persistent after reload (#1387)
* feat: save user made changes persistent * fix: remove artifact from user message on the UI * fix: message Id generation fix
This commit is contained in:
parent
a33a1268c3
commit
b98485d99f
@ -25,6 +25,7 @@ import { createSampler } from '~/utils/sampler';
|
|||||||
import { getTemplates, selectStarterTemplate } from '~/utils/selectStarterTemplate';
|
import { getTemplates, selectStarterTemplate } from '~/utils/selectStarterTemplate';
|
||||||
import { logStore } from '~/lib/stores/logs';
|
import { logStore } from '~/lib/stores/logs';
|
||||||
import { streamingState } from '~/lib/stores/streaming';
|
import { streamingState } from '~/lib/stores/streaming';
|
||||||
|
import { filesToArtifacts } from '~/utils/fileUtils';
|
||||||
|
|
||||||
const toastAnimation = cssTransition({
|
const toastAnimation = cssTransition({
|
||||||
enter: 'animated fadeInRight',
|
enter: 'animated fadeInRight',
|
||||||
@ -320,17 +321,17 @@ export const ChatImpl = memo(
|
|||||||
const { assistantMessage, userMessage } = temResp;
|
const { assistantMessage, userMessage } = temResp;
|
||||||
setMessages([
|
setMessages([
|
||||||
{
|
{
|
||||||
id: `${new Date().getTime()}`,
|
id: `1-${new Date().getTime()}`,
|
||||||
role: 'user',
|
role: 'user',
|
||||||
content: messageContent,
|
content: messageContent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: `${new Date().getTime()}`,
|
id: `2-${new Date().getTime()}`,
|
||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
content: assistantMessage,
|
content: assistantMessage,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: `${new Date().getTime()}`,
|
id: `3-${new Date().getTime()}`,
|
||||||
role: 'user',
|
role: 'user',
|
||||||
content: `[Model: ${model}]\n\n[Provider: ${provider.name}]\n\n${userMessage}`,
|
content: `[Model: ${model}]\n\n[Provider: ${provider.name}]\n\n${userMessage}`,
|
||||||
annotations: ['hidden'],
|
annotations: ['hidden'],
|
||||||
@ -371,17 +372,18 @@ export const ChatImpl = memo(
|
|||||||
setMessages(messages.slice(0, -1));
|
setMessages(messages.slice(0, -1));
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileModifications = workbenchStore.getFileModifcations();
|
const modifiedFiles = workbenchStore.getModifiedFiles();
|
||||||
|
|
||||||
chatStore.setKey('aborted', false);
|
chatStore.setKey('aborted', false);
|
||||||
|
|
||||||
if (fileModifications !== undefined) {
|
if (modifiedFiles !== undefined) {
|
||||||
|
const userUpdateArtifact = filesToArtifacts(modifiedFiles, `${Date.now()}`);
|
||||||
append({
|
append({
|
||||||
role: 'user',
|
role: 'user',
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
text: `[Model: ${model}]\n\n[Provider: ${provider.name}]\n\n${messageContent}`,
|
text: `[Model: ${model}]\n\n[Provider: ${provider.name}]\n\n${userUpdateArtifact}${messageContent}`,
|
||||||
},
|
},
|
||||||
...imageDataList.map((imageData) => ({
|
...imageDataList.map((imageData) => ({
|
||||||
type: 'image',
|
type: 'image',
|
||||||
|
@ -43,5 +43,6 @@ export function UserMessage({ content }: UserMessageProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function stripMetadata(content: string) {
|
function stripMetadata(content: string) {
|
||||||
return content.replace(MODEL_REGEX, '').replace(PROVIDER_REGEX, '');
|
const artifactRegex = /<boltArtifact\s+[^>]*>[\s\S]*?<\/boltArtifact>/gm;
|
||||||
|
return content.replace(MODEL_REGEX, '').replace(PROVIDER_REGEX, '').replace(artifactRegex, '');
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,10 @@ const messageParser = new StreamingMessageParser({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const extractTextContent = (message: Message) =>
|
||||||
|
Array.isArray(message.content)
|
||||||
|
? (message.content.find((item) => item.type === 'text')?.text as string) || ''
|
||||||
|
: message.content;
|
||||||
|
|
||||||
export function useMessageParser() {
|
export function useMessageParser() {
|
||||||
const [parsedMessages, setParsedMessages] = useState<{ [key: number]: string }>({});
|
const [parsedMessages, setParsedMessages] = useState<{ [key: number]: string }>({});
|
||||||
@ -55,9 +59,8 @@ export function useMessageParser() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const [index, message] of messages.entries()) {
|
for (const [index, message] of messages.entries()) {
|
||||||
if (message.role === 'assistant') {
|
if (message.role === 'assistant' || message.role === 'user') {
|
||||||
const newParsedContent = messageParser.parse(message.id, message.content);
|
const newParsedContent = messageParser.parse(message.id, extractTextContent(message));
|
||||||
|
|
||||||
setParsedMessages((prevParsed) => ({
|
setParsedMessages((prevParsed) => ({
|
||||||
...prevParsed,
|
...prevParsed,
|
||||||
[index]: !reset ? (prevParsed[index] || '') + newParsedContent : newParsedContent,
|
[index]: !reset ? (prevParsed[index] || '') + newParsedContent : newParsedContent,
|
||||||
|
@ -75,6 +75,29 @@ export class FilesStore {
|
|||||||
getFileModifications() {
|
getFileModifications() {
|
||||||
return computeFileModifications(this.files.get(), this.#modifiedFiles);
|
return computeFileModifications(this.files.get(), this.#modifiedFiles);
|
||||||
}
|
}
|
||||||
|
getModifiedFiles() {
|
||||||
|
let modifiedFiles: { [path: string]: File } | undefined = undefined;
|
||||||
|
|
||||||
|
for (const [filePath, originalContent] of this.#modifiedFiles) {
|
||||||
|
const file = this.files.get()[filePath];
|
||||||
|
|
||||||
|
if (file?.type !== 'file') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.content === originalContent) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!modifiedFiles) {
|
||||||
|
modifiedFiles = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
modifiedFiles[filePath] = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
return modifiedFiles;
|
||||||
|
}
|
||||||
|
|
||||||
resetFileModifications() {
|
resetFileModifications() {
|
||||||
this.#modifiedFiles.clear();
|
this.#modifiedFiles.clear();
|
||||||
|
@ -238,6 +238,9 @@ export class WorkbenchStore {
|
|||||||
getFileModifcations() {
|
getFileModifcations() {
|
||||||
return this.#filesStore.getFileModifications();
|
return this.#filesStore.getFileModifications();
|
||||||
}
|
}
|
||||||
|
getModifiedFiles() {
|
||||||
|
return this.#filesStore.getModifiedFiles();
|
||||||
|
}
|
||||||
|
|
||||||
resetAllFileModifications() {
|
resetAllFileModifications() {
|
||||||
this.#filesStore.resetFileModifications();
|
this.#filesStore.resetFileModifications();
|
||||||
|
@ -103,3 +103,19 @@ export const detectProjectType = async (
|
|||||||
|
|
||||||
return { type: '', setupCommand: '', followupMessage: '' };
|
return { type: '', setupCommand: '', followupMessage: '' };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const filesToArtifacts = (files: { [path: string]: { content: string } }, id: string): string => {
|
||||||
|
return `
|
||||||
|
<boltArtifact id="${id}" title="User Updated Files">
|
||||||
|
${Object.keys(files)
|
||||||
|
.map(
|
||||||
|
(filePath) => `
|
||||||
|
<boltAction type="file" filePath="${filePath}">
|
||||||
|
${files[filePath].content}
|
||||||
|
</boltAction>
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
.join('\n')}
|
||||||
|
</boltArtifact>
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user