webui: Conversation forking + branching improvements (#21021)
* refactor: Make `DialogConfirmation` extensible with children slot * feat: Add conversation forking logic * feat: Conversation forking UI * feat: Update delete/edit dialogs and logic for forks * refactor: Improve Chat Sidebar UX and add MCP Servers entry * refactor: Cleanup * feat: Update message in place when editing leaf nodes * chore: Cleanup * chore: Cleanup * chore: Cleanup * chore: Cleanup * chore: Cleanup * chore: Cleanup * refactor: Post-review improvements * chore: update webui build output * test: Update Storybook test * chore: update webui build output * chore: update webui build output
This commit is contained in:
@@ -1265,35 +1265,53 @@ class ChatStore {
|
||||
let result = this.getMessageByIdWithRole(messageId, MessageRole.USER);
|
||||
if (!result) result = this.getMessageByIdWithRole(messageId, MessageRole.SYSTEM);
|
||||
if (!result) return;
|
||||
const { message: msg } = result;
|
||||
const { message: msg, index: idx } = result;
|
||||
try {
|
||||
const allMessages = await conversationsStore.getConversationMessages(activeConv.id);
|
||||
const rootMessage = allMessages.find((m) => m.type === 'root' && m.parent === null);
|
||||
const isFirstUserMessage =
|
||||
msg.role === MessageRole.USER && rootMessage && msg.parent === rootMessage.id;
|
||||
const parentId = msg.parent || rootMessage?.id;
|
||||
if (!parentId) return;
|
||||
const extrasToUse =
|
||||
newExtras !== undefined
|
||||
? JSON.parse(JSON.stringify(newExtras))
|
||||
: msg.extra
|
||||
? JSON.parse(JSON.stringify(msg.extra))
|
||||
: undefined;
|
||||
const newMessage = await DatabaseService.createMessageBranch(
|
||||
{
|
||||
convId: msg.convId,
|
||||
type: msg.type,
|
||||
timestamp: Date.now(),
|
||||
role: msg.role,
|
||||
|
||||
let messageIdForResponse: string;
|
||||
|
||||
if (msg.children.length === 0) {
|
||||
// No responses after this message — update in place instead of branching
|
||||
const updates: Partial<DatabaseMessage> = {
|
||||
content: newContent,
|
||||
toolCalls: msg.toolCalls || '',
|
||||
children: [],
|
||||
extra: extrasToUse,
|
||||
model: msg.model
|
||||
},
|
||||
parentId
|
||||
);
|
||||
await conversationsStore.updateCurrentNode(newMessage.id);
|
||||
timestamp: Date.now(),
|
||||
extra: extrasToUse
|
||||
};
|
||||
await DatabaseService.updateMessage(msg.id, updates);
|
||||
conversationsStore.updateMessageAtIndex(idx, updates);
|
||||
messageIdForResponse = msg.id;
|
||||
} else {
|
||||
// Has children — create a new branch as sibling
|
||||
const parentId = msg.parent || rootMessage?.id;
|
||||
if (!parentId) return;
|
||||
const newMessage = await DatabaseService.createMessageBranch(
|
||||
{
|
||||
convId: msg.convId,
|
||||
type: msg.type,
|
||||
timestamp: Date.now(),
|
||||
role: msg.role,
|
||||
content: newContent,
|
||||
toolCalls: msg.toolCalls || '',
|
||||
children: [],
|
||||
extra: extrasToUse,
|
||||
model: msg.model
|
||||
},
|
||||
parentId
|
||||
);
|
||||
await conversationsStore.updateCurrentNode(newMessage.id);
|
||||
messageIdForResponse = newMessage.id;
|
||||
}
|
||||
|
||||
conversationsStore.updateConversationTimestamp();
|
||||
if (isFirstUserMessage && newContent.trim())
|
||||
await conversationsStore.updateConversationTitleWithConfirmation(
|
||||
@@ -1301,7 +1319,8 @@ class ChatStore {
|
||||
newContent.trim()
|
||||
);
|
||||
await conversationsStore.refreshActiveMessages();
|
||||
if (msg.role === MessageRole.USER) await this.generateResponseForMessage(newMessage.id);
|
||||
if (msg.role === MessageRole.USER)
|
||||
await this.generateResponseForMessage(messageIdForResponse);
|
||||
} catch (error) {
|
||||
console.error('Failed to edit message with branching:', error);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user