mirror of
https://github.com/open-webui/open-webui
synced 2025-05-28 09:11:36 +00:00
feat: goto message
This commit is contained in:
parent
c700126c17
commit
3be626bef3
@ -107,6 +107,47 @@
|
||||
}
|
||||
};
|
||||
|
||||
const gotoMessage = async (message, idx) => {
|
||||
// Determine the correct sibling list (either parent's children or root messages)
|
||||
let siblings;
|
||||
if (message.parentId !== null) {
|
||||
siblings = history.messages[message.parentId].childrenIds;
|
||||
} else {
|
||||
siblings = Object.values(history.messages)
|
||||
.filter((msg) => msg.parentId === null)
|
||||
.map((msg) => msg.id);
|
||||
}
|
||||
|
||||
// Clamp index to a valid range
|
||||
idx = Math.max(0, Math.min(idx, siblings.length - 1));
|
||||
|
||||
let messageId = siblings[idx];
|
||||
|
||||
// If we're navigating to a different message
|
||||
if (message.id !== messageId) {
|
||||
// Drill down to the deepest child of that branch
|
||||
let messageChildrenIds = history.messages[messageId].childrenIds;
|
||||
while (messageChildrenIds.length !== 0) {
|
||||
messageId = messageChildrenIds.at(-1);
|
||||
messageChildrenIds = history.messages[messageId].childrenIds;
|
||||
}
|
||||
|
||||
history.currentId = messageId;
|
||||
}
|
||||
|
||||
await tick();
|
||||
|
||||
// Optional auto-scroll
|
||||
if ($settings?.scrollOnBranchChange ?? true) {
|
||||
const element = document.getElementById('messages-container');
|
||||
autoScroll = element.scrollHeight - element.scrollTop <= element.clientHeight + 50;
|
||||
|
||||
setTimeout(() => {
|
||||
scrollToBottom();
|
||||
}, 100);
|
||||
}
|
||||
};
|
||||
|
||||
const showPreviousMessage = async (message) => {
|
||||
if (message.parentId !== null) {
|
||||
let messageId =
|
||||
@ -408,6 +449,7 @@
|
||||
messageId={message.id}
|
||||
idx={messageIdx}
|
||||
{user}
|
||||
{gotoMessage}
|
||||
{showPreviousMessage}
|
||||
{showNextMessage}
|
||||
{updateChat}
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
export let user;
|
||||
|
||||
export let gotoMessage;
|
||||
export let showPreviousMessage;
|
||||
export let showNextMessage;
|
||||
export let updateChat;
|
||||
@ -57,6 +58,7 @@
|
||||
: (Object.values(history.messages)
|
||||
.filter((message) => message.parentId === null)
|
||||
.map((message) => message.id) ?? [])}
|
||||
{gotoMessage}
|
||||
{showPreviousMessage}
|
||||
{showNextMessage}
|
||||
{editMessage}
|
||||
@ -70,6 +72,7 @@
|
||||
{messageId}
|
||||
isLastMessage={messageId === history.currentId}
|
||||
siblings={history.messages[history.messages[messageId].parentId]?.childrenIds ?? []}
|
||||
{gotoMessage}
|
||||
{showPreviousMessage}
|
||||
{showNextMessage}
|
||||
{updateChat}
|
||||
|
@ -58,6 +58,35 @@
|
||||
}
|
||||
}
|
||||
|
||||
const gotoMessage = async (modelIdx, messageIdx) => {
|
||||
// Clamp messageIdx to ensure it's within valid range
|
||||
groupedMessageIdsIdx[modelIdx] = Math.max(
|
||||
0,
|
||||
Math.min(messageIdx, groupedMessageIds[modelIdx].messageIds.length - 1)
|
||||
);
|
||||
|
||||
// Get the messageId at the specified index
|
||||
let messageId = groupedMessageIds[modelIdx].messageIds[groupedMessageIdsIdx[modelIdx]];
|
||||
console.log(messageId);
|
||||
|
||||
// Traverse the branch to find the deepest child message
|
||||
let messageChildrenIds = history.messages[messageId].childrenIds;
|
||||
while (messageChildrenIds.length !== 0) {
|
||||
messageId = messageChildrenIds.at(-1);
|
||||
messageChildrenIds = history.messages[messageId].childrenIds;
|
||||
}
|
||||
|
||||
// Update the current message ID in history
|
||||
history.currentId = messageId;
|
||||
|
||||
// Await UI updates
|
||||
await tick();
|
||||
await updateChat();
|
||||
|
||||
// Trigger scrolling after navigation
|
||||
triggerScroll();
|
||||
};
|
||||
|
||||
const showPreviousMessage = async (modelIdx) => {
|
||||
groupedMessageIdsIdx[modelIdx] = Math.max(0, groupedMessageIdsIdx[modelIdx] - 1);
|
||||
|
||||
@ -224,6 +253,7 @@
|
||||
messageId={_messageId}
|
||||
isLastMessage={true}
|
||||
siblings={groupedMessageIds[modelIdx].messageIds}
|
||||
gotoMessage={(message, messageIdx) => gotoMessage(modelIdx, messageIdx)}
|
||||
showPreviousMessage={() => showPreviousMessage(modelIdx)}
|
||||
showNextMessage={() => showNextMessage(modelIdx)}
|
||||
{updateChat}
|
||||
|
@ -5,7 +5,7 @@
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { onMount, tick, getContext } from 'svelte';
|
||||
import type { Writable } from 'svelte/store';
|
||||
import type { i18n as i18nType } from 'i18next';
|
||||
import type { i18n as i18nType, t } from 'i18next';
|
||||
|
||||
const i18n = getContext<Writable<i18nType>>('i18n');
|
||||
|
||||
@ -110,6 +110,7 @@
|
||||
|
||||
export let siblings;
|
||||
|
||||
export let gotoMessage: Function = () => {};
|
||||
export let showPreviousMessage: Function;
|
||||
export let showNextMessage: Function;
|
||||
|
||||
@ -139,6 +140,8 @@
|
||||
let editedContent = '';
|
||||
let editTextAreaElement: HTMLTextAreaElement;
|
||||
|
||||
let messageIndexEdit = false;
|
||||
|
||||
let audioParts: Record<number, HTMLAudioElement | null> = {};
|
||||
let speaking = false;
|
||||
let speakingIdx: number | undefined;
|
||||
@ -846,11 +849,50 @@
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="text-sm tracking-widest font-semibold self-center dark:text-gray-100 min-w-fit"
|
||||
>
|
||||
{siblings.indexOf(message.id) + 1}/{siblings.length}
|
||||
</div>
|
||||
{#if messageIndexEdit}
|
||||
<div
|
||||
class="text-sm flex justify-center font-semibold self-center dark:text-gray-100 min-w-fit"
|
||||
>
|
||||
<input
|
||||
id="message-index-input-{message.id}"
|
||||
type="number"
|
||||
value={siblings.indexOf(message.id) + 1}
|
||||
min="1"
|
||||
max={siblings.length}
|
||||
on:focus={(e) => {
|
||||
e.target.select();
|
||||
}}
|
||||
on:blur={(e) => {
|
||||
gotoMessage(message, e.target.value - 1);
|
||||
messageIndexEdit = false;
|
||||
}}
|
||||
on:keydown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
gotoMessage(message, e.target.value - 1);
|
||||
messageIndexEdit = false;
|
||||
}
|
||||
}}
|
||||
class="bg-transparent font-semibold self-center dark:text-gray-100 min-w-fit outline-hidden"
|
||||
/>/{siblings.length}
|
||||
</div>
|
||||
{:else}
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
class="text-sm tracking-widest font-semibold self-center dark:text-gray-100 min-w-fit"
|
||||
on:dblclick={async () => {
|
||||
messageIndexEdit = true;
|
||||
|
||||
await tick();
|
||||
const input = document.getElementById(`message-index-input-${message.id}`);
|
||||
if (input) {
|
||||
input.focus();
|
||||
input.select();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{siblings.indexOf(message.id) + 1}/{siblings.length}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<button
|
||||
class="self-center p-1 hover:bg-black/5 dark:hover:bg-white/5 dark:hover:text-white hover:text-black rounded-md transition"
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
export let siblings;
|
||||
|
||||
export let gotoMessage: Function;
|
||||
export let showPreviousMessage: Function;
|
||||
export let showNextMessage: Function;
|
||||
|
||||
@ -38,6 +39,8 @@
|
||||
|
||||
let showDeleteConfirm = false;
|
||||
|
||||
let messageIndexEdit = false;
|
||||
|
||||
let edit = false;
|
||||
let editedContent = '';
|
||||
let messageEditTextAreaElement: HTMLTextAreaElement;
|
||||
@ -267,11 +270,52 @@
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="text-sm tracking-widest font-semibold self-center dark:text-gray-100"
|
||||
>
|
||||
{siblings.indexOf(message.id) + 1}/{siblings.length}
|
||||
</div>
|
||||
{#if messageIndexEdit}
|
||||
<div
|
||||
class="text-sm flex justify-center font-semibold self-center dark:text-gray-100 min-w-fit"
|
||||
>
|
||||
<input
|
||||
id="message-index-input-{message.id}"
|
||||
type="number"
|
||||
value={siblings.indexOf(message.id) + 1}
|
||||
min="1"
|
||||
max={siblings.length}
|
||||
on:focus={(e) => {
|
||||
e.target.select();
|
||||
}}
|
||||
on:blur={(e) => {
|
||||
gotoMessage(message, e.target.value - 1);
|
||||
messageIndexEdit = false;
|
||||
}}
|
||||
on:keydown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
gotoMessage(message, e.target.value - 1);
|
||||
messageIndexEdit = false;
|
||||
}
|
||||
}}
|
||||
class="bg-transparent font-semibold self-center dark:text-gray-100 min-w-fit outline-hidden"
|
||||
/>/{siblings.length}
|
||||
</div>
|
||||
{:else}
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
class="text-sm tracking-widest font-semibold self-center dark:text-gray-100 min-w-fit"
|
||||
on:dblclick={async () => {
|
||||
messageIndexEdit = true;
|
||||
|
||||
await tick();
|
||||
const input = document.getElementById(
|
||||
`message-index-input-${message.id}`
|
||||
);
|
||||
if (input) {
|
||||
input.focus();
|
||||
input.select();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{siblings.indexOf(message.id) + 1}/{siblings.length}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<button
|
||||
class="self-center p-1 hover:bg-black/5 dark:hover:bg-white/5 dark:hover:text-white hover:text-black rounded-md transition"
|
||||
@ -398,11 +442,52 @@
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="text-sm tracking-widest font-semibold self-center dark:text-gray-100"
|
||||
>
|
||||
{siblings.indexOf(message.id) + 1}/{siblings.length}
|
||||
</div>
|
||||
{#if messageIndexEdit}
|
||||
<div
|
||||
class="text-sm flex justify-center font-semibold self-center dark:text-gray-100 min-w-fit"
|
||||
>
|
||||
<input
|
||||
id="message-index-input-{message.id}"
|
||||
type="number"
|
||||
value={siblings.indexOf(message.id) + 1}
|
||||
min="1"
|
||||
max={siblings.length}
|
||||
on:focus={(e) => {
|
||||
e.target.select();
|
||||
}}
|
||||
on:blur={(e) => {
|
||||
gotoMessage(message, e.target.value - 1);
|
||||
messageIndexEdit = false;
|
||||
}}
|
||||
on:keydown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
gotoMessage(message, e.target.value - 1);
|
||||
messageIndexEdit = false;
|
||||
}
|
||||
}}
|
||||
class="bg-transparent font-semibold self-center dark:text-gray-100 min-w-fit outline-hidden"
|
||||
/>/{siblings.length}
|
||||
</div>
|
||||
{:else}
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
class="text-sm tracking-widest font-semibold self-center dark:text-gray-100 min-w-fit"
|
||||
on:dblclick={async () => {
|
||||
messageIndexEdit = true;
|
||||
|
||||
await tick();
|
||||
const input = document.getElementById(
|
||||
`message-index-input-${message.id}`
|
||||
);
|
||||
if (input) {
|
||||
input.focus();
|
||||
input.select();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{siblings.indexOf(message.id) + 1}/{siblings.length}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<button
|
||||
class="self-center p-1 hover:bg-black/5 dark:hover:bg-white/5 dark:hover:text-white hover:text-black rounded-md transition"
|
||||
|
Loading…
Reference in New Issue
Block a user