This commit is contained in:
Timothy Jaeryang Baek
2026-07-23 02:55:06 -04:00
parent cf887b68ea
commit 73421c5b42
8 changed files with 103 additions and 21 deletions
+7 -9
View File
@@ -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}
@@ -183,10 +183,10 @@
</button>
</Tooltip>
{:else if item.data.id === 'fork'}
<Tooltip content="Clone this chat into a new copy." placement="top">
<Tooltip content="Fork the current chat branch into a new chat." placement="top">
<button
type="button"
aria-label="Fork: clone this chat into a new copy."
aria-label="Fork: fork the current chat branch into a new chat."
class="slash-command-row flex items-center gap-2 w-full h-6 px-2 rounded-xl text-xs text-left transition-colors duration-75
{commandIdx === selectedIdx ? 'app-interactive-active' : ''} disabled:opacity-50"
disabled={forkDisabled}
@@ -222,7 +222,7 @@
</span>
<span class="flex-1 min-w-0 flex items-baseline gap-1.5 overflow-hidden">
<span class="truncate">Fork</span>
<span class="app-muted text-[0.625rem] truncate shrink-0">Clone chat</span>
<span class="app-muted text-[0.625rem] truncate shrink-0">Current branch</span>
</span>
</button>
</Tooltip>
+2
View File
@@ -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}
@@ -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}
@@ -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}
@@ -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 @@
</button>
</Tooltip>
{#if message.done && !readOnly && forkHandler}
<Tooltip content="Fork chat" placement="bottom">
<button
aria-label="Fork chat"
class="{isLastMessage || ($settings?.highContrastMode ?? false)
? 'visible'
: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition"
on:click={() => {
forkHandler?.(message.id);
}}
>
<svg
class="w-4 h-4"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.8"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="M4 12H9" />
<path d="M9 12C12.5 12 12.5 7 16 7H20" />
<path d="M17 4L20 7L17 10" />
<path d="M9 12C12.5 12 12.5 17 16 17H20" />
<path d="M17 14L20 17L17 20" />
</svg>
</button>
</Tooltip>
{/if}
{#if onInsertToNote && visibleResponseContent}
<Tooltip content={$i18n.t('Insert into note')} placement="bottom">
<button