feat: add frontend support for locally sharing chats

This commit is contained in:
Jun Siang Cheah 2024-03-31 22:03:28 +01:00 committed by Timothy J. Baek
parent 94976e5ed3
commit 7978adbf45
9 changed files with 433 additions and 106 deletions

View file

@ -16,6 +16,7 @@
const i18n = getContext('i18n');
export let chatId = '';
export let readOnly = false;
export let sendPrompt: Function;
export let continueGeneration: Function;
export let regenerateResponse: Function;
@ -317,6 +318,7 @@
<UserMessage
on:delete={() => messageDeleteHandler(message.id)}
user={$user}
{readOnly}
{message}
isFirstMessage={messageIdx === 0}
siblings={message.parentId !== null
@ -335,6 +337,7 @@
modelfiles={selectedModelfiles}
siblings={history.messages[message.parentId]?.childrenIds ?? []}
isLastMessage={messageIdx + 1 === messages.length}
{readOnly}
{confirmEditResponseMessage}
{showPreviousMessage}
{showNextMessage}

View file

@ -33,6 +33,8 @@
export let isLastMessage = true;
export let readOnly = false;
export let confirmEditResponseMessage: Function;
export let showPreviousMessage: Function;
export let showNextMessage: Function;
@ -469,31 +471,33 @@
</div>
{/if}
<Tooltip content="Edit" placement="bottom">
<button
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition"
on:click={() => {
editMessageHandler();
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
class="w-4 h-4"
{#if !readOnly}
<Tooltip content="Edit" placement="bottom">
<button
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition"
on:click={() => {
editMessageHandler();
}}
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
/>
</svg>
</button>
</Tooltip>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
/>
</svg>
</button>
</Tooltip>
{/if}
<Tooltip content="Copy" placement="bottom">
<button
@ -521,59 +525,61 @@
</button>
</Tooltip>
<Tooltip content="Good Response" placement="bottom">
<button
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1 rounded {message.rating === 1
? 'bg-gray-100 dark:bg-gray-800'
: ''} dark:hover:text-white hover:text-black transition"
on:click={() => {
rateMessage(message.id, 1);
}}
>
<svg
stroke="currentColor"
fill="none"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
class="w-4 h-4"
xmlns="http://www.w3.org/2000/svg"
><path
d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"
/></svg
{#if !readOnly}
<Tooltip content="Good Response" placement="bottom">
<button
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1 rounded {message.rating === 1
? 'bg-gray-100 dark:bg-gray-800'
: ''} dark:hover:text-white hover:text-black transition"
on:click={() => {
rateMessage(message.id, 1);
}}
>
</button>
</Tooltip>
<svg
stroke="currentColor"
fill="none"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
class="w-4 h-4"
xmlns="http://www.w3.org/2000/svg"
><path
d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"
/></svg
>
</button>
</Tooltip>
<Tooltip content="Bad Response" placement="bottom">
<button
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1 rounded {message.rating === -1
? 'bg-gray-100 dark:bg-gray-800'
: ''} dark:hover:text-white hover:text-black transition"
on:click={() => {
rateMessage(message.id, -1);
}}
>
<svg
stroke="currentColor"
fill="none"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
class="w-4 h-4"
xmlns="http://www.w3.org/2000/svg"
><path
d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"
/></svg
<Tooltip content="Bad Response" placement="bottom">
<button
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1 rounded {message.rating === -1
? 'bg-gray-100 dark:bg-gray-800'
: ''} dark:hover:text-white hover:text-black transition"
on:click={() => {
rateMessage(message.id, -1);
}}
>
</button>
</Tooltip>
<svg
stroke="currentColor"
fill="none"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
class="w-4 h-4"
xmlns="http://www.w3.org/2000/svg"
><path
d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"
/></svg
>
</button>
</Tooltip>
{/if}
<Tooltip content="Read Aloud" placement="bottom">
<button
@ -656,7 +662,7 @@
</button>
</Tooltip>
{#if $config.images}
{#if $config.images && !readOnly}
<Tooltip content="Generate Image" placement="bottom">
<button
class="{isLastMessage
@ -752,7 +758,7 @@
</Tooltip>
{/if}
{#if isLastMessage}
{#if isLastMessage && !readOnly}
<Tooltip content="Continue Response" placement="bottom">
<button
type="button"

View file

@ -15,6 +15,7 @@
export let message;
export let siblings;
export let isFirstMessage: boolean;
export let readOnly: boolean;
export let confirmEditMessage: Function;
export let showPreviousMessage: Function;
@ -250,29 +251,31 @@
</div>
{/if}
<Tooltip content="Edit" placement="bottom">
<button
class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition edit-user-message-button"
on:click={() => {
editMessageHandler();
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
class="w-4 h-4"
{#if !readOnly}
<Tooltip content="Edit" placement="bottom">
<button
class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition edit-user-message-button"
on:click={() => {
editMessageHandler();
}}
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
/>
</svg>
</button>
</Tooltip>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
/>
</svg>
</button>
</Tooltip>
{/if}
<Tooltip content="Copy" placement="bottom">
<button
@ -298,7 +301,7 @@
</button>
</Tooltip>
{#if !isFirstMessage}
{#if !isFirstMessage && !readOnly}
<Tooltip content="Delete" placement="bottom">
<button
class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition"

View file

@ -6,6 +6,7 @@
export let downloadChat: Function;
export let shareChat: Function;
export let shareLocalChat: Function;
export let show = false;
</script>
@ -23,6 +24,17 @@
{$i18n.t('Share to OpenWebUI Community')}
</button>
<button
class=" self-center px-8 py-1.5 w-full rounded-full text-sm font-medium bg-blue-600 hover:bg-blue-500 text-white mt-1.5"
type="button"
on:click={() => {
shareLocalChat();
show = false;
}}
>
{$i18n.t('Create local share link')}
</button>
<div class="flex justify-center space-x-1 mt-1.5">
<div class=" self-center text-gray-400 text-xs font-medium">{$i18n.t('or')}</div>

View file

@ -5,7 +5,7 @@
const { saveAs } = fileSaver;
import { Separator } from 'bits-ui';
import { getChatById } from '$lib/apis/chats';
import { getChatById, shareChatById } from '$lib/apis/chats';
import { WEBUI_NAME, chatId, modelfiles, settings, showSettings } from '$lib/stores';
import { slide } from 'svelte/transition';
@ -19,6 +19,7 @@
import ChevronUpDown from '../icons/ChevronUpDown.svelte';
import Menu from './Navbar/Menu.svelte';
import TagChatModal from '../chat/TagChatModal.svelte';
import { copyToClipboard } from '$lib/utils';
const i18n = getContext('i18n');
@ -32,7 +33,7 @@
export let addTag: Function;
export let deleteTag: Function;
export let showModelSelector = false;
export let showModelSelector = true;
let showShareChatModal = false;
let showTagChatModal = false;
@ -64,6 +65,23 @@
);
};
const shareLocalChat = async () => {
const chat = await getChatById(localStorage.token, $chatId);
console.log('shareLocal', chat);
if (chat.share_id) {
const shareUrl = `${window.location.origin}/s/${chat.share_id}`;
toast.info(
$i18n.t('Chat is already shared at {{shareUrl}}, copied to clipboard', { shareUrl })
);
copyToClipboard(shareUrl);
} else {
const sharedChat = await shareChatById(localStorage.token, $chatId);
const shareUrl = `${window.location.origin}/s/${sharedChat.id}`;
toast.info($i18n.t('Chat is now shared at {{shareUrl}}, copied to clipboard', { shareUrl }));
copyToClipboard(shareUrl);
}
};
const downloadChat = async () => {
const chat = (await getChatById(localStorage.token, $chatId)).chat;
console.log('download', chat);
@ -80,7 +98,7 @@
};
</script>
<ShareChatModal bind:show={showShareChatModal} {downloadChat} {shareChat} />
<ShareChatModal bind:show={showShareChatModal} {downloadChat} {shareChat} {shareLocalChat} />
<!-- <TagChatModal bind:show={showTagChatModal} {tags} {deleteTag} {addTag} /> -->
<nav id="nav" class=" sticky py-2.5 top-0 flex flex-row justify-center z-30">
<div
@ -135,8 +153,10 @@
</div> -->
<div class="flex items-center w-full max-w-full">
<div class="w-full flex-1 overflow-hidden max-w-full">
<ModelSelector bind:selectedModels />
<div class="flex-1 overflow-hidden max-w-full">
{#if showModelSelector}
<ModelSelector bind:selectedModels />
{/if}
</div>
<div class="self-start flex flex-none items-center">