From 73421c5b42ac5c2ebc5faa520b7ed3fa0e39f10d Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Thu, 23 Jul 2026 02:55:06 -0400 Subject: [PATCH] refac --- src/lib/apis/chats/index.ts | 41 +++++++++++++++++++ src/lib/components/chat/Chat.svelte | 16 ++++---- .../Commands/SlashCommands.svelte | 6 +-- src/lib/components/chat/Messages.svelte | 2 + .../components/chat/Messages/Message.svelte | 3 ++ .../Messages/MultiResponseMessages.svelte | 3 ++ .../chat/Messages/ResponseMessage.svelte | 32 +++++++++++++++ src/lib/utils/index.ts | 21 ++++++---- 8 files changed, 103 insertions(+), 21 deletions(-) diff --git a/src/lib/apis/chats/index.ts b/src/lib/apis/chats/index.ts index b4bc2e002c..64d2960dbf 100644 --- a/src/lib/apis/chats/index.ts +++ b/src/lib/apis/chats/index.ts @@ -920,6 +920,47 @@ export const cloneChatById = async (token: string, id: string, title?: string) = return res; }; +export const forkChatById = async (token: string, id: string, messageId?: string | null) => { + let error = null; + + const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}/fork`, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + ...(token && { authorization: `Bearer ${token}` }) + }, + body: JSON.stringify({ + message_id: messageId ?? null + }) + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .then((json) => { + return json; + }) + .catch((err) => { + error = err; + + if ('detail' in err) { + error = err.detail; + } else { + error = err; + } + + console.error(err); + return null; + }); + + if (error) { + throw error; + } + + return res; +}; + export const cloneSharedChatById = async (token: string, id: string) => { let error = null; diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index 08bde0bc9f..d059846625 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -70,10 +70,10 @@ import { archiveChatById, - cloneChatById, compactChatById, createNewChat, deleteChatById, + forkChatById, getAllTags, getChatById, getTagsById, @@ -1884,6 +1884,9 @@ (chatContent?.history ?? undefined) !== undefined ? chatContent.history : convertMessagesToHistory(chatContent.messages); + if (chat?.current_message_id && history?.messages?.[chat.current_message_id]) { + history.currentId = chat.current_message_id; + } // Sanitize history: repair orphaned references and structurally-malformed // nodes from failed regenerations (#24424, #24157, #20474) @@ -2466,7 +2469,7 @@ document.getElementById('chat-input')?.focus(); }; - const handleForkChat = async () => { + const handleForkChat = async (messageId: string | null = null) => { if (!$chatId || !history?.currentId) { toast.message($i18n.t('No chat to fork')); return; @@ -2489,13 +2492,7 @@ const toastId = toast.loading($i18n.t('Forking chat...')); try { - const result = await cloneChatById( - localStorage.token, - $chatId, - $i18n.t('Clone of {{TITLE}}', { - TITLE: $chatTitle || 'Chat' - }) - ); + const result = await forkChatById(localStorage.token, $chatId, messageId ?? history.currentId); if (result?.id) { if (!embedded) { @@ -3725,6 +3722,7 @@ {mergeResponses} {chatActionHandler} {addMessages} + forkHandler={generating || taskIds?.length ? null : handleForkChat} topPadding={!embedded} bottomPadding={files.length > 0} {onSelect} diff --git a/src/lib/components/chat/MessageInput/Commands/SlashCommands.svelte b/src/lib/components/chat/MessageInput/Commands/SlashCommands.svelte index 084379ac90..75ec04ee1a 100644 --- a/src/lib/components/chat/MessageInput/Commands/SlashCommands.svelte +++ b/src/lib/components/chat/MessageInput/Commands/SlashCommands.svelte @@ -183,10 +183,10 @@ {:else if item.data.id === 'fork'} - + diff --git a/src/lib/components/chat/Messages.svelte b/src/lib/components/chat/Messages.svelte index 9322175b7c..0adb4a027a 100644 --- a/src/lib/components/chat/Messages.svelte +++ b/src/lib/components/chat/Messages.svelte @@ -40,6 +40,7 @@ export let showMessage: Function = () => {}; export let submitMessage: Function = () => {}; export let addMessages: Function = () => {}; + export let forkHandler: Function | null = null; export let readOnly = false; export let editCodeBlock = true; @@ -539,6 +540,7 @@ {continueResponse} {mergeResponses} {addMessages} + {forkHandler} {triggerScroll} {readOnly} {editCodeBlock} diff --git a/src/lib/components/chat/Messages/Message.svelte b/src/lib/components/chat/Messages/Message.svelte index 5fb9a399da..0b18ca2d9f 100644 --- a/src/lib/components/chat/Messages/Message.svelte +++ b/src/lib/components/chat/Messages/Message.svelte @@ -39,6 +39,7 @@ export let mergeResponses; export let addMessages; + export let forkHandler: Function | null = null; export let triggerScroll; export let readOnly = false; export let editCodeBlock = true; @@ -97,6 +98,7 @@ {continueResponse} {regenerateResponse} {addMessages} + {forkHandler} {readOnly} {editCodeBlock} {topPadding} @@ -122,6 +124,7 @@ {mergeResponses} {triggerScroll} {addMessages} + {forkHandler} {readOnly} {editCodeBlock} {topPadding} diff --git a/src/lib/components/chat/Messages/MultiResponseMessages.svelte b/src/lib/components/chat/Messages/MultiResponseMessages.svelte index a58f6bd9a9..6223cf8338 100644 --- a/src/lib/components/chat/Messages/MultiResponseMessages.svelte +++ b/src/lib/components/chat/Messages/MultiResponseMessages.svelte @@ -45,6 +45,7 @@ export let mergeResponses: Function; export let addMessages: Function; + export let forkHandler: Function | null = null; export let triggerScroll: Function; @@ -319,6 +320,7 @@ groupedMessageIds[selectedModelIdx].messageIds.length - 1; }} {addMessages} + {forkHandler} {readOnly} {topPadding} {onInsertToNote} @@ -376,6 +378,7 @@ groupedMessageIds[modelIdx].messageIds.length - 1; }} {addMessages} + {forkHandler} {readOnly} {editCodeBlock} {topPadding} diff --git a/src/lib/components/chat/Messages/ResponseMessage.svelte b/src/lib/components/chat/Messages/ResponseMessage.svelte index 11b71eaae0..36d05bc37b 100644 --- a/src/lib/components/chat/Messages/ResponseMessage.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage.svelte @@ -159,6 +159,7 @@ export let submitMessage: Function; export let continueResponse: Function; export let regenerateResponse: Function; + export let forkHandler: Function | null = null; export let addMessages: Function; @@ -1054,6 +1055,37 @@ + {#if message.done && !readOnly && forkHandler} + + + + {/if} + {#if onInsertToNote && visibleResponseContent}